/src/graphicsmagick/coders/bmp.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | % Copyright (C) 2003-2024 GraphicsMagick Group |
3 | | % Copyright (C) 2002 ImageMagick Studio |
4 | | % Copyright 1991-1999 E. I. du Pont de Nemours and Company |
5 | | % |
6 | | % This program is covered by multiple licenses, which are described in |
7 | | % Copyright.txt. You should have received a copy of Copyright.txt with this |
8 | | % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html. |
9 | | % |
10 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
11 | | % % |
12 | | % % |
13 | | % % |
14 | | % BBBB M M PPPP % |
15 | | % B B MM MM P P % |
16 | | % BBBB M M M PPPP % |
17 | | % B B M M P % |
18 | | % BBBB M M P % |
19 | | % % |
20 | | % % |
21 | | % Read/Write Microsoft Windows Bitmap Image Format. % |
22 | | % % |
23 | | % % |
24 | | % Software Design % |
25 | | % John Cristy % |
26 | | % Glenn Randers-Pehrson % |
27 | | % December 2001 % |
28 | | % % |
29 | | % % |
30 | | % % |
31 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
32 | | % |
33 | | % |
34 | | */ |
35 | | |
36 | | /* |
37 | | Include declarations. |
38 | | */ |
39 | | #include "magick/studio.h" |
40 | | #include "magick/blob.h" |
41 | | #include "magick/colormap.h" |
42 | | #include "magick/constitute.h" |
43 | | #include "magick/enum_strings.h" |
44 | | #include "magick/log.h" |
45 | | #include "magick/magick.h" |
46 | | #include "magick/monitor.h" |
47 | | #include "magick/pixel_cache.h" |
48 | | #include "magick/profile.h" |
49 | | #include "magick/transform.h" |
50 | | #include "magick/utility.h" |
51 | | |
52 | | /* |
53 | | Macro definitions (from Windows wingdi.h). |
54 | | */ |
55 | | |
56 | | #undef BI_JPEG |
57 | 17.1k | #define BI_JPEG 4 |
58 | | #undef BI_PNG |
59 | 5.35k | #define BI_PNG 5 |
60 | | #ifndef BI_ALPHABITFIELDS |
61 | 2.05k | #define BI_ALPHABITFIELDS 6 |
62 | | #endif |
63 | | #if !defined(MSWINDOWS) || defined(__MINGW32__) |
64 | | #undef BI_RGB |
65 | 8.90k | #define BI_RGB 0 |
66 | | #undef BI_RLE8 |
67 | 522k | #define BI_RLE8 1 |
68 | | #undef BI_RLE4 |
69 | 3.15k | #define BI_RLE4 2 |
70 | | #undef BI_BITFIELDS |
71 | 2.59k | #define BI_BITFIELDS 3 |
72 | | |
73 | | #undef LCS_CALIBRATED_RGB |
74 | 2.03k | #define LCS_CALIBRATED_RGB 0 |
75 | | #undef LCS_sRGB |
76 | | #define LCS_sRGB 1 |
77 | | #undef LCS_WINDOWS_COLOR_SPACE |
78 | | #define LCS_WINDOWS_COLOR_SPACE 2 |
79 | | #undef PROFILE_LINKED |
80 | | #define PROFILE_LINKED 3 |
81 | | #undef PROFILE_EMBEDDED |
82 | | #define PROFILE_EMBEDDED 4 |
83 | | |
84 | | #undef LCS_GM_BUSINESS |
85 | 9 | #define LCS_GM_BUSINESS 1 /* Saturation */ |
86 | | #undef LCS_GM_GRAPHICS |
87 | 3 | #define LCS_GM_GRAPHICS 2 /* Relative */ |
88 | | #undef LCS_GM_IMAGES |
89 | 3 | #define LCS_GM_IMAGES 4 /* Perceptual */ |
90 | | #undef LCS_GM_ABS_COLORIMETRIC |
91 | 71 | #define LCS_GM_ABS_COLORIMETRIC 8 /* Absolute */ |
92 | | #endif /* !defined(MSWINDOWS) || defined(__MINGW32__) */ |
93 | | |
94 | | #if (QuantumDepth == 8) |
95 | | #define MS_VAL16_TO_QUANTUM(_value) ((_value>=8192)?255:(_value>>5)) |
96 | | #elif (QuantumDepth == 16) |
97 | 55.8k | #define MS_VAL16_TO_QUANTUM(_value) ((_value>=8192)?65535:(_value*8)) |
98 | | #elif (QuantumDepth == 32) |
99 | | #define MS_VAL16_TO_QUANTUM(_value) ((_value>=8192)?4294443007:(_value*524288)) |
100 | | #else |
101 | | # error Unsupported quantum depth. |
102 | | #endif |
103 | | |
104 | | |
105 | | #ifdef WORDS_BIGENDIAN |
106 | | #define LD_UINT16_LSB(_pixel, _ptr) _pixel=(magick_uint16_t)*_ptr++; _pixel|=(magick_uint16_t)*_ptr++ << 8 |
107 | | #else |
108 | 55.8k | #define LD_UINT16_LSB(_pixel, _ptr) _pixel=*(magick_uint16_t*)_ptr; _ptr+=2 |
109 | | #endif |
110 | | |
111 | | |
112 | | /* |
113 | | Typedef declarations. |
114 | | */ |
115 | | typedef struct _BMPInfo |
116 | | { |
117 | | size_t |
118 | | file_size, /* 0 or size of file in bytes */ |
119 | | image_size; /* bytes_per_line*image->rows or uint32_t from file */ |
120 | | |
121 | | magick_uint32_t |
122 | | ba_offset, |
123 | | offset_bits,/* Starting position of image data in bytes */ |
124 | | size; /* Header size 12 = v2, 12-64 OS/2 v2, 40 = v3, 108 = v4, 124 = v5 */ |
125 | | |
126 | | magick_int32_t |
127 | | width, /* BMP width */ |
128 | | height; /* BMP height (negative means bottom-up) */ |
129 | | |
130 | | magick_uint16_t |
131 | | planes, |
132 | | bits_per_pixel; |
133 | | |
134 | | magick_uint32_t |
135 | | compression, |
136 | | x_pixels, |
137 | | y_pixels, |
138 | | number_colors, |
139 | | colors_important; |
140 | | |
141 | | magick_uint32_t |
142 | | red_mask, |
143 | | green_mask, |
144 | | blue_mask, |
145 | | alpha_mask; |
146 | | |
147 | | magick_int32_t |
148 | | colorspace; |
149 | | |
150 | | PrimaryInfo |
151 | | red_primary, |
152 | | green_primary, |
153 | | blue_primary, |
154 | | gamma_scale; |
155 | | } BMPInfo; |
156 | | |
157 | | /* |
158 | | Forward declarations. |
159 | | */ |
160 | | static unsigned int |
161 | | WriteBMPImage(const ImageInfo *,Image *); |
162 | | |
163 | | /* |
164 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
165 | | % % |
166 | | % % |
167 | | % % |
168 | | % D e c o d e I m a g e % |
169 | | % % |
170 | | % % |
171 | | % % |
172 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
173 | | % |
174 | | % Method DecodeImage unpacks the packed image pixels into runlength-encoded |
175 | | % pixel packets. |
176 | | % |
177 | | % The format of the DecodeImage method is: |
178 | | % |
179 | | % MagickPassFail DecodeImage(Image *image,const unsigned long compression, |
180 | | % unsigned char *pixels) |
181 | | % |
182 | | % A description of each parameter follows: |
183 | | % |
184 | | % o status: Method DecodeImage returns MagickPass if all the pixels are |
185 | | % uncompressed without error, otherwise MagickFail. |
186 | | % |
187 | | % o image: The address of a structure of type Image. |
188 | | % |
189 | | % o compression: Zero means uncompressed. A value of 1 means the |
190 | | % compressed pixels are runlength encoded for a 256-color bitmap. |
191 | | % A value of 2 means a 16-color bitmap. A value of 3 means bitfields |
192 | | % encoding. |
193 | | % |
194 | | % o pixels: The address of a byte (8 bits) array of pixel data created by |
195 | | % the decoding process. |
196 | | % |
197 | | % o pixels_size: The size of the allocated buffer array. |
198 | | % |
199 | | */ |
200 | | static MagickPassFail DecodeImage(Image *image,const unsigned long compression, |
201 | | unsigned char *pixels, const size_t pixels_size) |
202 | 419 | { |
203 | 419 | unsigned long |
204 | 419 | x, |
205 | 419 | y; |
206 | | |
207 | 419 | unsigned int |
208 | 419 | i; |
209 | | |
210 | 419 | int |
211 | 419 | byte, |
212 | 419 | count; |
213 | | |
214 | 419 | register unsigned char |
215 | 419 | *q; |
216 | | |
217 | 419 | unsigned char |
218 | 419 | *end; |
219 | | |
220 | 419 | assert(image != (Image *) NULL); |
221 | 419 | assert(pixels != (unsigned char *) NULL); |
222 | 419 | if (image->logging) |
223 | 419 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
224 | 419 | " Decoding RLE compressed pixels to" |
225 | 419 | " %" MAGICK_SIZE_T_F "u bytes", |
226 | 419 | (MAGICK_SIZE_T ) image->rows*image->columns); |
227 | | |
228 | 419 | (void) memset(pixels,0,pixels_size); |
229 | 419 | byte=0; |
230 | 419 | x=0; |
231 | 419 | q=pixels; |
232 | 419 | end=pixels + pixels_size; |
233 | | /* |
234 | | Decompress sufficient data to support the number of pixels (or |
235 | | rows) in the image and then return. |
236 | | |
237 | | Do not wait to read the final EOL and EOI markers (if not yet |
238 | | encountered) since we always read this marker just before we |
239 | | return. |
240 | | */ |
241 | 516k | for (y=0; y < image->rows; ) |
242 | 516k | { |
243 | 516k | if (q < pixels || q >= end) |
244 | 139 | { |
245 | 139 | if (image->logging) |
246 | 139 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
247 | 139 | " Decode buffer full (y=%lu, " |
248 | 139 | "pixels_size=%" MAGICK_SIZE_T_F "u, " |
249 | 139 | "pixels=%p, q=%p, end=%p)", |
250 | 139 | y, (MAGICK_SIZE_T) pixels_size, |
251 | 139 | pixels, q, end); |
252 | 139 | break; |
253 | 139 | } |
254 | 516k | count=ReadBlobByte(image); |
255 | 516k | if (count == EOF) |
256 | 46 | return MagickFail; |
257 | 516k | if (count > 0) |
258 | 487k | { |
259 | 487k | count=Min(count, end - q); |
260 | | /* |
261 | | Encoded mode. |
262 | | */ |
263 | 487k | byte=ReadBlobByte(image); |
264 | 487k | if (byte == EOF) |
265 | 14 | return MagickFail; |
266 | 487k | if (compression == BI_RLE8) |
267 | 15.7k | { |
268 | 2.60M | for ( i=count; i != 0; --i ) |
269 | 2.58M | { |
270 | 2.58M | *q++=(unsigned char) byte; |
271 | 2.58M | } |
272 | 15.7k | } |
273 | 471k | else |
274 | 471k | { |
275 | 52.6M | for ( i=0; i < (unsigned int) count; i++ ) |
276 | 52.1M | { |
277 | 52.1M | *q++=(unsigned char) |
278 | 52.1M | ((i & 0x01) ? (byte & 0x0f) : ((byte >> 4) & 0x0f)); |
279 | 52.1M | } |
280 | 471k | } |
281 | 487k | x+=count; |
282 | 487k | } |
283 | 28.9k | else |
284 | 28.9k | { |
285 | | /* |
286 | | Escape mode. |
287 | | */ |
288 | 28.9k | count=ReadBlobByte(image); |
289 | 28.9k | if (count == EOF) |
290 | 5 | return MagickFail; |
291 | 28.9k | if (count == 0x01) |
292 | 3 | { |
293 | 3 | if (image->logging) |
294 | 3 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
295 | 3 | " RLE Escape code encountered"); |
296 | 3 | goto rle_decode_done; |
297 | 3 | } |
298 | 28.9k | switch (count) |
299 | 28.9k | { |
300 | 11.6k | case 0x00: |
301 | 11.6k | { |
302 | | /* |
303 | | End of line. |
304 | | */ |
305 | 11.6k | x=0; |
306 | 11.6k | y++; |
307 | 11.6k | q=pixels+y*(size_t) image->columns; |
308 | 11.6k | break; |
309 | 0 | } |
310 | 421 | case 0x02: |
311 | 421 | { |
312 | | /* |
313 | | Delta mode. |
314 | | */ |
315 | 421 | byte=ReadBlobByte(image); |
316 | 421 | if (byte == EOF) |
317 | 1 | return MagickFail; |
318 | 420 | x+=byte; |
319 | 420 | byte=ReadBlobByte(image); |
320 | 420 | if (byte == EOF) |
321 | 1 | return MagickFail; |
322 | 419 | y+=byte; |
323 | 419 | q=pixels+y*(size_t) image->columns+x; |
324 | 419 | break; |
325 | 420 | } |
326 | 16.8k | default: |
327 | 16.8k | { |
328 | | /* |
329 | | Absolute mode. |
330 | | */ |
331 | 16.8k | count=Min(count, end - q); |
332 | 16.8k | if (count < 0) |
333 | 0 | return MagickFail; |
334 | 16.8k | if (compression == BI_RLE8) |
335 | 48.6k | for (i=count; i != 0; --i) |
336 | 47.7k | { |
337 | 47.7k | byte=ReadBlobByte(image); |
338 | 47.7k | if (byte == EOF) |
339 | 107 | return MagickFail; |
340 | 47.6k | *q++=byte; |
341 | 47.6k | } |
342 | 15.8k | else |
343 | 1.04M | for (i=0; i < (unsigned int) count; i++) |
344 | 1.02M | { |
345 | 1.02M | if ((i & 0x01) == 0) |
346 | 516k | { |
347 | 516k | byte=ReadBlobByte(image); |
348 | 516k | if (byte == EOF) |
349 | 14 | return MagickFail; |
350 | 516k | } |
351 | 1.02M | *q++=(unsigned char) |
352 | 1.02M | ((i & 0x01) ? (byte & 0x0f) : ((byte >> 4) & 0x0f)); |
353 | 1.02M | } |
354 | 16.7k | x+=count; |
355 | | /* |
356 | | Read pad byte. |
357 | | */ |
358 | 16.7k | if (compression == BI_RLE8) |
359 | 926 | { |
360 | 926 | if (count & 0x01) |
361 | 429 | if (ReadBlobByte(image) == EOF) |
362 | 2 | return MagickFail; |
363 | 926 | } |
364 | 15.8k | else |
365 | 15.8k | if (((count & 0x03) == 1) || ((count & 0x03) == 2)) |
366 | 1.09k | if (ReadBlobByte(image) == EOF) |
367 | 5 | return MagickFail; |
368 | 16.7k | break; |
369 | 16.7k | } |
370 | 28.9k | } |
371 | 28.9k | } |
372 | 516k | if (QuantumTick(y,image->rows)) |
373 | 19.7k | if (!MagickMonitorFormatted(y,image->rows,&image->exception, |
374 | 19.7k | LoadImageText,image->filename, |
375 | 19.7k | image->columns,image->rows)) |
376 | 0 | break; |
377 | 516k | } |
378 | 221 | (void) ReadBlobByte(image); /* end of line */ |
379 | 221 | (void) ReadBlobByte(image); |
380 | 224 | rle_decode_done: |
381 | 224 | if (image->logging) |
382 | 224 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
383 | 224 | " Decoded %" MAGICK_SIZE_T_F "u bytes", |
384 | 224 | (MAGICK_SIZE_T) (q-pixels)); |
385 | 224 | if ((MAGICK_SIZE_T) (q-pixels) < pixels_size) |
386 | 3 | { |
387 | 3 | if (image->logging) |
388 | 3 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
389 | 3 | " RLE decoded output is truncated"); |
390 | 3 | return MagickFail; |
391 | 3 | } |
392 | 221 | return(MagickPass); |
393 | 224 | } |
394 | | |
395 | | /* |
396 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
397 | | % % |
398 | | % % |
399 | | % % |
400 | | % E n c o d e I m a g e % |
401 | | % % |
402 | | % % |
403 | | % % |
404 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
405 | | % |
406 | | % Method EncodeImage compresses pixels using a runlength encoded format. |
407 | | % |
408 | | % The format of the EncodeImage method is: |
409 | | % |
410 | | % static unsigned int EncodeImage(Image *image, |
411 | | % const unsigned long bytes_per_line,const unsigned char *pixels, |
412 | | % unsigned char *compressed_pixels) |
413 | | % |
414 | | % A description of each parameter follows: |
415 | | % |
416 | | % o status: Method EncodeImage returns the number of bytes in the |
417 | | % runlength encoded compress_pixels array. |
418 | | % |
419 | | % o image: A pointer to an Image structure. |
420 | | % |
421 | | % o bytes_per_line: The number of bytes in a scanline of compressed pixels |
422 | | % |
423 | | % o pixels: The address of a byte (8 bits) array of pixel data created by |
424 | | % the compression process. |
425 | | % |
426 | | % o compressed_pixels: The address of a byte (8 bits) array of compressed |
427 | | % pixel data. |
428 | | % |
429 | | % |
430 | | */ |
431 | | static size_t EncodeImage(Image *image,const size_t bytes_per_line, |
432 | | const unsigned char *pixels,unsigned char *compressed_pixels) |
433 | 97 | { |
434 | 97 | unsigned long |
435 | 97 | y; |
436 | | |
437 | 97 | register const unsigned char |
438 | 97 | *p; |
439 | | |
440 | 97 | register unsigned long |
441 | 97 | i, |
442 | 97 | x; |
443 | | |
444 | 97 | register unsigned char |
445 | 97 | *q; |
446 | | |
447 | | /* |
448 | | Runlength encode pixels. |
449 | | */ |
450 | 97 | assert(image != (Image *) NULL); |
451 | 97 | assert(pixels != (const unsigned char *) NULL); |
452 | 97 | assert(compressed_pixels != (unsigned char *) NULL); |
453 | 97 | p=pixels; |
454 | 97 | q=compressed_pixels; |
455 | 97 | i=0; |
456 | 40.6k | for (y=0; y < image->rows; y++) |
457 | 40.5k | { |
458 | 20.6M | for (x=0; x < bytes_per_line; x+=i) |
459 | 20.6M | { |
460 | | /* |
461 | | Determine runlength. |
462 | | */ |
463 | 47.8M | for (i=1; (((size_t) x+i) < bytes_per_line); i++) |
464 | 47.8M | if ((i == 255) || (*(p+i) != *p)) |
465 | 20.5M | break; |
466 | 20.6M | *q++=(unsigned char) i; |
467 | 20.6M | *q++=(*p); |
468 | 20.6M | p+=i; |
469 | 20.6M | } |
470 | | /* |
471 | | End of line. |
472 | | */ |
473 | 40.5k | *q++=0x00; |
474 | 40.5k | *q++=0x00; |
475 | 40.5k | if (QuantumTick(y,image->rows)) |
476 | 8.38k | if (!MagickMonitorFormatted(y,image->rows,&image->exception, |
477 | 8.38k | SaveImageText,image->filename, |
478 | 8.38k | image->columns,image->rows)) |
479 | 0 | break; |
480 | 40.5k | } |
481 | | /* |
482 | | End of bitmap. |
483 | | */ |
484 | 97 | *q++=0; |
485 | 97 | *q++=0x01; |
486 | 97 | return((size_t) (q-compressed_pixels)); |
487 | 97 | } |
488 | | |
489 | | /* |
490 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
491 | | % % |
492 | | % % |
493 | | % % |
494 | | % I s B M P % |
495 | | % % |
496 | | % % |
497 | | % % |
498 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
499 | | % |
500 | | % Method IsBMP returns MagickTrue if the image format type, identified by the |
501 | | % magick string, is BMP. |
502 | | % |
503 | | % The format of the IsBMP method is: |
504 | | % |
505 | | % unsigned int IsBMP(const unsigned char *magick,const size_t length) |
506 | | % |
507 | | % A description of each parameter follows: |
508 | | % |
509 | | % o status: Method IsBMP returns MagickTrue if the image format type is BMP. |
510 | | % |
511 | | % o magick: This string is generally the first few bytes of an image file |
512 | | % or blob. |
513 | | % |
514 | | % o length: Specifies the length of the magick string. |
515 | | % |
516 | | % |
517 | | */ |
518 | | static unsigned int IsBMP(const unsigned char *magick,const size_t length) |
519 | 0 | { |
520 | 0 | if (length < 2) |
521 | 0 | return(MagickFalse); |
522 | 0 | if ((LocaleNCompare((char *) magick,"BA",2) == 0) || |
523 | 0 | (LocaleNCompare((char *) magick,"BM",2) == 0) || |
524 | 0 | (LocaleNCompare((char *) magick,"IC",2) == 0) || |
525 | 0 | (LocaleNCompare((char *) magick,"PI",2) == 0) || |
526 | 0 | (LocaleNCompare((char *) magick,"CI",2) == 0) || |
527 | 0 | (LocaleNCompare((char *) magick,"CP",2) == 0)) |
528 | 0 | return(MagickTrue); |
529 | 0 | return(MagickFalse); |
530 | 0 | } |
531 | | |
532 | | |
533 | | static const char *DecodeBiCompression(const magick_uint32_t BiCompression, const magick_uint32_t BiSize) |
534 | 0 | { |
535 | 0 | switch(BiCompression) |
536 | 0 | { |
537 | 0 | case BI_RGB: return "BI_RGB"; /* uncompressed */ |
538 | 0 | case BI_RLE4: return "BI_RLE4"; /* 4 bit RLE */ |
539 | 0 | case BI_RLE8: return "BI_RLE8"; /* 8 bit RLE */ |
540 | 0 | case BI_BITFIELDS: |
541 | 0 | if(BiSize==64) return "OS/2 Huffman 1D"; |
542 | 0 | else return "BI_BITFIELDS"; |
543 | 0 | case BI_JPEG: if(BiSize==64) return "OS/2 RLE-24"; |
544 | 0 | else return "BI_JPEG"; |
545 | 0 | case BI_PNG: return "BI_PNG"; |
546 | 0 | case BI_ALPHABITFIELDS: return "BI_ALPHABITFIELDS"; |
547 | 0 | } |
548 | 0 | return "UNKNOWN"; |
549 | 0 | } |
550 | | |
551 | | |
552 | | static Image *ExtractNestedBlob(Image ** image, const ImageInfo * image_info, int ImgType, ExceptionInfo * exception) |
553 | 7.07k | { |
554 | 7.07k | size_t |
555 | 7.07k | alloc_size; |
556 | | |
557 | 7.07k | unsigned char |
558 | 7.07k | *blob; |
559 | | |
560 | 7.07k | alloc_size = GetBlobSize(*image) - TellBlob(*image); |
561 | | |
562 | 7.07k | if (alloc_size > 0 && |
563 | 7.07k | (blob = MagickAllocateResourceLimitedMemory(unsigned char *,alloc_size)) != NULL) |
564 | 6.98k | { |
565 | | /* Copy JPG to memory blob */ |
566 | 6.98k | if (ReadBlob(*image,alloc_size,blob) == alloc_size) |
567 | 6.98k | { |
568 | 6.98k | Image *image2; |
569 | 6.98k | ImageInfo *clone_info; |
570 | | |
571 | 6.98k | clone_info = CloneImageInfo(image_info); |
572 | 6.98k | (void) strlcpy(clone_info->magick, (ImgType==BI_JPEG)?"JPEG":"PNG", sizeof(clone_info->magick)); |
573 | | |
574 | | /* BlobToFile("/tmp/jnx-tile.jpg", blob,alloc_size,exception); */ |
575 | | |
576 | | /* (void) strlcpy(clone_info->filename, (ImgType==BI_JPEG)?"JPEG:":"PNG:", sizeof(clone_info->filename)); */ |
577 | 6.98k | FormatString(clone_info->filename,"%sblob-%px", ImgType==BI_JPEG?"JPEG:":"PNG:", blob); |
578 | 6.98k | if ((image2 = BlobToImage(clone_info,blob,alloc_size,exception)) |
579 | 6.98k | != NULL) |
580 | 1.26k | { |
581 | 1.26k | if ((*image)->logging) |
582 | 1.26k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
583 | 1.26k | "Read embedded %s blob with dimensions %lux%lu", |
584 | 1.26k | image2->magick, image2->rows, image2->columns); |
585 | | /* |
586 | | Replace current image with new image while copying |
587 | | base image attributes. |
588 | | */ |
589 | 1.26k | (void) strlcpy(image2->filename, (*image)->filename, |
590 | 1.26k | sizeof(image2->filename)); |
591 | 1.26k | (void) strlcpy(image2->magick_filename, (*image)->magick_filename, |
592 | 1.26k | sizeof(image2->magick_filename)); |
593 | 1.26k | (void) strlcpy(image2->magick, (*image)->magick, |
594 | 1.26k | sizeof(image2->magick)); |
595 | 1.26k | DestroyBlob(image2); |
596 | 1.26k | image2->blob = ReferenceBlob((*image)->blob); |
597 | 1.26k | if(((*image)->rows == 0) || ((*image)->columns == 0)) |
598 | 1.26k | DeleteImageFromList(image); |
599 | 1.26k | AppendImageToList(image, image2); |
600 | 1.26k | } |
601 | 6.98k | DestroyImageInfo(clone_info); |
602 | 6.98k | clone_info = (ImageInfo *) NULL; |
603 | 6.98k | MagickFreeResourceLimitedMemory(blob); |
604 | 6.98k | } |
605 | 2 | else |
606 | 2 | { |
607 | 2 | MagickFreeResourceLimitedMemory(blob); |
608 | | /* Failed to read enough data from input */ |
609 | 2 | ThrowException(exception,CorruptImageError,UnexpectedEndOfFile, (*image)->filename); |
610 | 2 | } |
611 | 6.98k | } |
612 | 84 | else |
613 | 84 | { |
614 | | /* Failed to allocate memory */ |
615 | 84 | ThrowException(exception,ResourceLimitError,MemoryAllocationFailed, |
616 | 84 | (*image)->filename); |
617 | 84 | } |
618 | 7.07k | return(*image); |
619 | 7.07k | } |
620 | | |
621 | | |
622 | | /* |
623 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
624 | | % % |
625 | | % % |
626 | | % % |
627 | | % R e a d B M P I m a g e % |
628 | | % % |
629 | | % % |
630 | | % % |
631 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
632 | | % |
633 | | % Method ReadBMPImage reads a Microsoft Windows bitmap image file, Version |
634 | | % 2, 3 (for Windows or NT), or 4, and returns it. It allocates the memory |
635 | | % necessary for the new Image structure and returns a pointer to the new |
636 | | % image. |
637 | | % |
638 | | % The format of the ReadBMPImage method is: |
639 | | % |
640 | | % image=ReadBMPImage(image_info) |
641 | | % |
642 | | % A description of each parameter follows: |
643 | | % |
644 | | % o image: Method ReadBMPImage returns a pointer to the image after |
645 | | % reading. A null image is returned if there is a memory shortage or |
646 | | % if the image cannot be read. |
647 | | % |
648 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
649 | | % |
650 | | % o exception: return any errors or warnings in this structure. |
651 | | % |
652 | | % |
653 | | */ |
654 | 11.7k | #define ThrowBMPReaderException(code_,reason_,image_) \ |
655 | 11.7k | do { \ |
656 | 11.7k | MagickFreeResourceLimitedMemory(bmp_colormap); \ |
657 | 11.7k | MagickFreeResourceLimitedMemory(pixels); \ |
658 | 11.7k | ThrowReaderException(code_,reason_,image_); \ |
659 | 0 | } while (0); |
660 | | |
661 | | static Image *ReadBMPImage(const ImageInfo *image_info,ExceptionInfo *exception) |
662 | 14.0k | { |
663 | 14.0k | BMPInfo |
664 | 14.0k | bmp_info; |
665 | | |
666 | 14.0k | Image |
667 | 14.0k | *image; |
668 | | |
669 | 14.0k | int |
670 | 14.0k | logging; |
671 | | |
672 | 14.0k | long |
673 | 14.0k | y; |
674 | | |
675 | 14.0k | magick_uint32_t |
676 | 14.0k | blue, |
677 | 14.0k | green, |
678 | 14.0k | opacity, |
679 | 14.0k | red; |
680 | | |
681 | 14.0k | ExtendedSignedIntegralType |
682 | 14.0k | start_position; |
683 | | |
684 | 14.0k | register long |
685 | 14.0k | x; |
686 | | |
687 | 14.0k | register PixelPacket |
688 | 14.0k | *q; |
689 | | |
690 | 14.0k | register long |
691 | 14.0k | i; |
692 | | |
693 | 14.0k | register unsigned char |
694 | 14.0k | *p; |
695 | | |
696 | 14.0k | size_t |
697 | 14.0k | bytes_per_line, |
698 | 14.0k | count, |
699 | 14.0k | length, |
700 | 14.0k | pixels_size; |
701 | | |
702 | 14.0k | unsigned char |
703 | 14.0k | *bmp_colormap, |
704 | 14.0k | magick[12], |
705 | 14.0k | *pixels; |
706 | | |
707 | 14.0k | unsigned int |
708 | 14.0k | status; |
709 | | |
710 | 14.0k | magick_off_t |
711 | 14.0k | file_remaining, |
712 | 14.0k | file_size, |
713 | 14.0k | offset; |
714 | | |
715 | | /* |
716 | | Open image file. |
717 | | */ |
718 | 14.0k | assert(image_info != (const ImageInfo *) NULL); |
719 | 14.0k | assert(image_info->signature == MagickSignature); |
720 | 14.0k | assert(exception != (ExceptionInfo *) NULL); |
721 | 14.0k | assert(exception->signature == MagickSignature); |
722 | 14.0k | bmp_colormap=(unsigned char *) NULL; |
723 | 14.0k | pixels=(unsigned char *) NULL; |
724 | 14.0k | logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter"); |
725 | 14.0k | image=AllocateImage(image_info); |
726 | 14.0k | image->rows=0; |
727 | 14.0k | image->columns=0; |
728 | 14.0k | status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); |
729 | 14.0k | if (status == MagickFalse) |
730 | 14.0k | ThrowBMPReaderException(FileOpenError,UnableToOpenFile,image); |
731 | 14.0k | file_size=GetBlobSize(image); |
732 | | /* |
733 | | Determine if this is a BMP file. |
734 | | */ |
735 | 14.0k | (void) memset(&bmp_info,0,sizeof(BMPInfo)); |
736 | 14.0k | bmp_info.ba_offset=0; |
737 | 14.0k | start_position=0; |
738 | 14.0k | magick[0]=magick[1]=0; |
739 | 14.0k | count=ReadBlob(image,2,(char *) magick); |
740 | 14.0k | do |
741 | 14.0k | { |
742 | 14.0k | PixelPacket |
743 | 14.0k | quantum_bits, |
744 | 14.0k | shift; |
745 | | |
746 | 14.0k | magick_uint32_t |
747 | 14.0k | profile_data, |
748 | 14.0k | profile_size; |
749 | | |
750 | | /* |
751 | | Verify BMP identifier. |
752 | | */ |
753 | | /* if (bmp_info.ba_offset == 0) */ /* FIXME: Investigate. Start position needs to always advance! */ |
754 | 14.0k | start_position=TellBlob(image)-2; |
755 | 14.0k | bmp_info.ba_offset=0; |
756 | | /* "BA" is OS/2 bitmap array file */ |
757 | 180k | while (LocaleNCompare((char *) magick,"BA",2) == 0) |
758 | 166k | { |
759 | 166k | bmp_info.file_size=ReadBlobLSBLong(image); |
760 | 166k | bmp_info.ba_offset=ReadBlobLSBLong(image); |
761 | 166k | bmp_info.offset_bits=ReadBlobLSBLong(image); |
762 | 166k | if ((count=ReadBlob(image,2,(char *) magick)) != 2) |
763 | 27 | break; |
764 | 166k | } |
765 | | |
766 | 14.0k | if (count != 2) /* Found "BA" header from above above */ |
767 | 14.0k | ThrowBMPReaderException(CorruptImageError,ImproperImageHeader,image); |
768 | | |
769 | 14.0k | if (logging ) |
770 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule()," Magick: %c%c", |
771 | 0 | magick[0],magick[1]); |
772 | | |
773 | 14.0k | bmp_info.file_size=ReadBlobLSBLong(image); /* File size in bytes */ |
774 | 14.0k | if (logging) |
775 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
776 | 0 | " File size: Claimed=%" MAGICK_SIZE_T_F "u, Actual=%" |
777 | 0 | MAGICK_OFF_F "d", |
778 | 0 | (MAGICK_SIZE_T) bmp_info.file_size, file_size); |
779 | 14.0k | (void) ReadBlobLSBLong(image); /* Reserved */ |
780 | 14.0k | bmp_info.offset_bits=ReadBlobLSBLong(image); /* Bit map offset from start of file */ |
781 | 14.0k | bmp_info.size=ReadBlobLSBLong(image); /* BMP Header size */ |
782 | 14.0k | if (logging) |
783 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
784 | 0 | " Header size: %u\n" |
785 | 0 | " Offset bits: %u\n" |
786 | 0 | " Image data offset: %u", |
787 | 0 | bmp_info.size, |
788 | 0 | bmp_info.offset_bits, |
789 | 0 | bmp_info.ba_offset); |
790 | | |
791 | 14.0k | if (LocaleNCompare((char *) magick,"BM",2) != 0) /* "BM" is Windows or OS/2 file. */ |
792 | 9.14k | { |
793 | 9.14k | if ((LocaleNCompare((char *) magick,"CI",2) != 0) || /* "CI" is OS/2 Color Icon */ |
794 | 9.14k | (bmp_info.size!=12 && bmp_info.size!=40 && bmp_info.size!=64)) /* CI chunk must have biSize only 12 or 40 or 64 */ |
795 | 7.78k | ThrowBMPReaderException(CorruptImageError,ImproperImageHeader,image); |
796 | 7.78k | } |
797 | | |
798 | 12.6k | if ((bmp_info.file_size != 0) && ((magick_off_t) bmp_info.file_size > file_size)) |
799 | 12.2k | ThrowBMPReaderException(CorruptImageError,ImproperImageHeader,image); |
800 | 12.2k | if (bmp_info.offset_bits < bmp_info.size) |
801 | 11.8k | ThrowBMPReaderException(CorruptImageError,ImproperImageHeader,image); |
802 | | |
803 | 11.8k | if(bmp_info.size == 12) |
804 | 584 | { |
805 | | /* |
806 | | Windows 2.X or OS/2 BMP image file. |
807 | | */ |
808 | 584 | bmp_info.width=(magick_int16_t) ReadBlobLSBShort(image); /* Width */ |
809 | 584 | bmp_info.height=(magick_int16_t) ReadBlobLSBShort(image); /* Height */ |
810 | 584 | bmp_info.planes=ReadBlobLSBShort(image); /* # of color planes */ |
811 | 584 | bmp_info.bits_per_pixel=ReadBlobLSBShort(image); /* Bits per pixel */ |
812 | 584 | bmp_info.x_pixels=0; |
813 | 584 | bmp_info.y_pixels=0; |
814 | 584 | bmp_info.number_colors=0; |
815 | 584 | bmp_info.compression=BI_RGB; |
816 | 584 | bmp_info.image_size=0; |
817 | 584 | bmp_info.alpha_mask=0; |
818 | 584 | if (logging) |
819 | 0 | { |
820 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
821 | 0 | " Format: Windows 2.X or OS/2 Bitmap"); |
822 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
823 | 0 | " Geometry: %dx%d",bmp_info.width,bmp_info.height); |
824 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
825 | 0 | " Planes: %u",bmp_info.planes); |
826 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
827 | 0 | " Bits per pixel: %u",bmp_info.bits_per_pixel); |
828 | 0 | } |
829 | 584 | } |
830 | 11.2k | else |
831 | 11.2k | { |
832 | | /* |
833 | | Microsoft Windows 3.X or later BMP image file. |
834 | | */ |
835 | 11.2k | switch(bmp_info.size) |
836 | 11.2k | { |
837 | 7.35k | case 40: if(logging) |
838 | 0 | (void) LogMagickEvent(CoderEvent, GetMagickModule(), "Format: MS Windows bitmap 3.X"); |
839 | 7.35k | break; |
840 | 6 | case 52: if(logging) |
841 | 0 | (void) LogMagickEvent(CoderEvent, GetMagickModule(), "Format: MS Windows bitmap 3.X V2"); |
842 | 6 | break; |
843 | 2 | case 56:if(logging) |
844 | 0 | (void) LogMagickEvent(CoderEvent, GetMagickModule(), "Format: MS Windows bitmap 3.X V3"); |
845 | 2 | break; |
846 | 1.08k | case 64: if(logging) |
847 | 0 | (void) LogMagickEvent(CoderEvent, GetMagickModule(), "Format: OS22XBITMAPHEADER"); |
848 | 1.08k | break; |
849 | 11 | case 78: |
850 | 13 | case 108: if (logging) |
851 | 0 | (void) LogMagickEvent(CoderEvent, GetMagickModule(), "Format: MS Windows bitmap 3.X V4"); |
852 | 13 | break; |
853 | 774 | case 124: if(logging) |
854 | 0 | (void) LogMagickEvent(CoderEvent, GetMagickModule(), "Format: MS Windows bitmap 3.X V5"); |
855 | 774 | break; |
856 | 1.99k | default: if (bmp_info.size < 64) |
857 | 1.53k | ThrowBMPReaderException(CorruptImageError, NonOS2HeaderSizeError, image); |
858 | | /* A value larger than 64 indicates a later version of the OS/2 BMP format. |
859 | | .... as far as OS/2 development caesed we could consider to |
860 | | close this Trojan's horse window in future. */ |
861 | 1.53k | if(logging) |
862 | 0 | (void) LogMagickEvent(CoderEvent, GetMagickModule(), "Format: MS Windows bitmap 3.X ?"); |
863 | 1.53k | break; |
864 | 11.2k | } |
865 | | |
866 | | /* |
867 | | BMP v3 defines width and height as signed LONG (32 bit) values. If |
868 | | height is a positive number, then the image is a "bottom-up" |
869 | | bitmap with origin in the lower-left corner. If height is a |
870 | | negative number, then the image is a "top-down" bitmap with the |
871 | | origin in the upper-left corner. The meaning of negative values |
872 | | is not defined for width. |
873 | | */ |
874 | 10.7k | bmp_info.width=(magick_int32_t) ReadBlobLSBLong(image); /* Width */ |
875 | 10.7k | bmp_info.height=(magick_int32_t) ReadBlobLSBLong(image); /* Height */ |
876 | 10.7k | bmp_info.planes=ReadBlobLSBShort(image); /* # of color planes */ |
877 | 10.7k | bmp_info.bits_per_pixel=ReadBlobLSBShort(image); /* Bits per pixel (1/4/8/16/24/32) */ |
878 | 10.7k | bmp_info.compression=ReadBlobLSBLong(image); /* Compression method */ |
879 | 10.7k | bmp_info.image_size=ReadBlobLSBLong(image); /* Bitmap size (bytes) */ |
880 | 10.7k | bmp_info.x_pixels=ReadBlobLSBLong(image); /* Horizontal resolution (pixels/meter) */ |
881 | 10.7k | bmp_info.y_pixels=ReadBlobLSBLong(image); /* Vertical resolution (pixels/meter) */ |
882 | 10.7k | bmp_info.number_colors=ReadBlobLSBLong(image); /* Number of colors */ |
883 | 10.7k | bmp_info.colors_important=ReadBlobLSBLong(image); /* Minimum important colors */ |
884 | 10.7k | profile_data=0; |
885 | 10.7k | profile_size=0; |
886 | 10.7k | if (logging) |
887 | 0 | { |
888 | 0 | (void) LogMagickEvent(CoderEvent, GetMagickModule(), |
889 | 0 | " Geometry: %dx%d\n" |
890 | 0 | " Planes: %u\n" |
891 | 0 | " Bits per pixel: %u", |
892 | 0 | bmp_info.width, bmp_info.height, |
893 | 0 | bmp_info.planes, |
894 | 0 | bmp_info.bits_per_pixel); |
895 | 0 | if(bmp_info.compression <= BI_ALPHABITFIELDS) |
896 | 0 | (void) LogMagickEvent(CoderEvent, GetMagickModule(), |
897 | 0 | " Compression: %s", DecodeBiCompression(bmp_info.compression,bmp_info.size)); |
898 | 0 | else |
899 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
900 | 0 | " Compression: UNKNOWN (%u)",bmp_info.compression); |
901 | 0 | (void) LogMagickEvent(CoderEvent, GetMagickModule(), |
902 | 0 | " Number of colors: %u\n" |
903 | 0 | " Important colors: %u", |
904 | 0 | bmp_info.number_colors, bmp_info.colors_important); |
905 | 0 | } |
906 | | |
907 | 10.7k | if(bmp_info.size==64) |
908 | 1.08k | { /* OS22XBITMAPHEADER */ |
909 | 1.08k | magick_uint16_t Units; /* Type of units used to measure resolution */ |
910 | 1.08k | magick_uint16_t Reserved; /* Pad structure to 4-byte boundary */ |
911 | 1.08k | magick_uint16_t Recording; /* Recording algorithm */ |
912 | 1.08k | magick_uint16_t Rendering; /* Halftoning algorithm used */ |
913 | 1.08k | magick_uint32_t Size1; /* Reserved for halftoning algorithm use */ |
914 | 1.08k | magick_uint32_t Size2; /* Reserved for halftoning algorithm use */ |
915 | 1.08k | magick_uint32_t ColorEncoding; /* Color model used in bitmap */ |
916 | 1.08k | magick_uint32_t Identifier; /* Reserved for application use */ |
917 | 1.08k | Units = ReadBlobLSBShort(image); |
918 | 1.08k | Reserved = ReadBlobLSBShort(image); |
919 | 1.08k | Recording = ReadBlobLSBShort(image); |
920 | 1.08k | Rendering = ReadBlobLSBShort(image); |
921 | 1.08k | Size1 = ReadBlobLSBLong(image); |
922 | 1.08k | Size2 = ReadBlobLSBLong(image); |
923 | 1.08k | ColorEncoding = ReadBlobLSBLong(image); |
924 | 1.08k | Identifier = ReadBlobLSBLong(image); |
925 | | |
926 | 1.08k | if(logging) |
927 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
928 | 0 | " Units: %u\n" |
929 | 0 | " Reserved: %u\n" |
930 | 0 | " Recording: %u\n" |
931 | 0 | " Rendering: %u\n" |
932 | 0 | " Size1: %u\n" |
933 | 0 | " Size2: %u\n" |
934 | 0 | " ColorEncoding: %u\n" |
935 | 0 | " Identifier: %u", |
936 | 0 | Units, Reserved, Recording, Rendering, |
937 | 0 | Size1, Size2, ColorEncoding, Identifier); |
938 | | /* OS/2 does not recognise JPEG nor PNG. */ |
939 | 1.08k | if(bmp_info.compression==BI_JPEG || bmp_info.compression==BI_PNG) |
940 | 3 | ThrowBMPReaderException(CoderError,CompressionNotValid,image) |
941 | 1.08k | } |
942 | | |
943 | 10.7k | if (bmp_info.size>=52 && bmp_info.size!=64) |
944 | 2.33k | { |
945 | 2.33k | bmp_info.red_mask=ReadBlobLSBLong(image); |
946 | 2.33k | bmp_info.green_mask=ReadBlobLSBLong(image); |
947 | 2.33k | bmp_info.blue_mask=ReadBlobLSBLong(image); |
948 | 2.33k | if (bmp_info.size >= 56) |
949 | 2.32k | { |
950 | | /* |
951 | | Read color management information. |
952 | | */ |
953 | 2.32k | bmp_info.alpha_mask=ReadBlobLSBLong(image); |
954 | 2.32k | if (image->logging) |
955 | 2.32k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
956 | 2.32k | "Alpha Mask: 0x%04x", |
957 | 2.32k | bmp_info.alpha_mask); |
958 | | |
959 | 2.32k | if (bmp_info.size > 120) |
960 | 2.03k | { |
961 | | /* |
962 | | https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapv4header |
963 | | */ |
964 | 2.03k | magick_uint32_t |
965 | 2.03k | v4_red_primary_x, v4_red_primary_y, v4_red_primary_z, |
966 | 2.03k | v4_green_primary_x, v4_green_primary_y, v4_green_primary_z, |
967 | 2.03k | v4_blue_primary_x, v4_blue_primary_y, v4_blue_primary_z, |
968 | 2.03k | v4_gamma_x, v4_gamma_y, v4_gamma_z; |
969 | | |
970 | 2.03k | double |
971 | 2.03k | bmp_gamma, |
972 | 2.03k | sum; |
973 | | |
974 | 2.03k | bmp_info.colorspace=(magick_int32_t) ReadBlobLSBLong(image); |
975 | 2.03k | if (image->logging) |
976 | 2.03k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
977 | 2.03k | "BMP Colorspace: 0x%04x", |
978 | 2.03k | bmp_info.colorspace); |
979 | | |
980 | 2.03k | v4_red_primary_x=ReadBlobLSBLong(image); |
981 | 2.03k | v4_red_primary_y=ReadBlobLSBLong(image); |
982 | 2.03k | v4_red_primary_z=ReadBlobLSBLong(image); |
983 | 2.03k | v4_green_primary_x=ReadBlobLSBLong(image); |
984 | 2.03k | v4_green_primary_y=ReadBlobLSBLong(image); |
985 | 2.03k | v4_green_primary_z=ReadBlobLSBLong(image); |
986 | 2.03k | v4_blue_primary_x=ReadBlobLSBLong(image); |
987 | 2.03k | v4_blue_primary_y=ReadBlobLSBLong(image); |
988 | 2.03k | v4_blue_primary_z=ReadBlobLSBLong(image); |
989 | 2.03k | v4_gamma_x = ReadBlobLSBLong(image); |
990 | 2.03k | v4_gamma_y = ReadBlobLSBLong(image); |
991 | 2.03k | v4_gamma_z = ReadBlobLSBLong(image); |
992 | | |
993 | 2.03k | if (LCS_CALIBRATED_RGB == bmp_info.colorspace) |
994 | 584 | { |
995 | | /* |
996 | | Decode 2^30 fixed point formatted CIE primaries. |
997 | | https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-ciexyztriple |
998 | | https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-ciexyz |
999 | | */ |
1000 | 584 | bmp_info.red_primary.x=(double) v4_red_primary_x/0x3ffffff; |
1001 | 584 | bmp_info.red_primary.y=(double) v4_red_primary_y/0x3ffffff; |
1002 | 584 | bmp_info.red_primary.z=(double) v4_red_primary_z/0x3ffffff; |
1003 | | |
1004 | 584 | bmp_info.green_primary.x=(double) v4_green_primary_x/0x3ffffff; |
1005 | 584 | bmp_info.green_primary.y=(double) v4_green_primary_y/0x3ffffff; |
1006 | 584 | bmp_info.green_primary.z=(double) v4_green_primary_z/0x3ffffff; |
1007 | | |
1008 | 584 | bmp_info.blue_primary.x=(double) v4_blue_primary_x/0x3ffffff; |
1009 | 584 | bmp_info.blue_primary.y=(double) v4_blue_primary_y/0x3ffffff; |
1010 | 584 | bmp_info.blue_primary.z=(double) v4_blue_primary_z/0x3ffffff; |
1011 | | |
1012 | 584 | if (image->logging) |
1013 | 584 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1014 | 584 | "BMP Primaries: red(%g,%g,%g)," |
1015 | 584 | " green(%g,%g,%g)," |
1016 | 584 | " blue(%g,%g,%g)", |
1017 | 584 | bmp_info.red_primary.x, |
1018 | 584 | bmp_info.red_primary.y, |
1019 | 584 | bmp_info.red_primary.z, |
1020 | 584 | bmp_info.green_primary.x, |
1021 | 584 | bmp_info.green_primary.y, |
1022 | 584 | bmp_info.green_primary.z, |
1023 | 584 | bmp_info.blue_primary.x, |
1024 | 584 | bmp_info.blue_primary.y, |
1025 | 584 | bmp_info.blue_primary.z); |
1026 | | |
1027 | 584 | sum=bmp_info.red_primary.x+bmp_info.red_primary.y+bmp_info.red_primary.z; |
1028 | 584 | sum=Max(MagickEpsilon,sum); |
1029 | 584 | bmp_info.red_primary.x/=sum; |
1030 | 584 | bmp_info.red_primary.y/=sum; |
1031 | 584 | image->chromaticity.red_primary.x=bmp_info.red_primary.x; |
1032 | 584 | image->chromaticity.red_primary.y=bmp_info.red_primary.y; |
1033 | | |
1034 | 584 | sum=bmp_info.green_primary.x+bmp_info.green_primary.y+bmp_info.green_primary.z; |
1035 | 584 | sum=Max(MagickEpsilon,sum); |
1036 | 584 | bmp_info.green_primary.x/=sum; |
1037 | 584 | bmp_info.green_primary.y/=sum; |
1038 | 584 | image->chromaticity.green_primary.x=bmp_info.green_primary.x; |
1039 | 584 | image->chromaticity.green_primary.y=bmp_info.green_primary.y; |
1040 | | |
1041 | 584 | sum=bmp_info.blue_primary.x+bmp_info.blue_primary.y+bmp_info.blue_primary.z; |
1042 | 584 | sum=Max(MagickEpsilon,sum); |
1043 | 584 | bmp_info.blue_primary.x/=sum; |
1044 | 584 | bmp_info.blue_primary.y/=sum; |
1045 | 584 | image->chromaticity.blue_primary.x=bmp_info.blue_primary.x; |
1046 | 584 | image->chromaticity.blue_primary.y=bmp_info.blue_primary.y; |
1047 | | |
1048 | | /* |
1049 | | Decode 16^16 fixed point formatted gamma_scales. |
1050 | | Gamma encoded in unsigned fixed 16.16 format. The |
1051 | | upper 16 bits are the unsigned integer value. The |
1052 | | lower 16 bits are the fractional part. |
1053 | | */ |
1054 | 584 | bmp_info.gamma_scale.x=v4_gamma_x/0xffff; |
1055 | 584 | bmp_info.gamma_scale.y=v4_gamma_y/0xffff; |
1056 | 584 | bmp_info.gamma_scale.z=v4_gamma_z/0xffff; |
1057 | | |
1058 | | /* |
1059 | | Compute a single averaged gamma from the BMP 3-channel gamma. |
1060 | | */ |
1061 | 584 | bmp_gamma = (bmp_info.gamma_scale.x+bmp_info.gamma_scale.y+ |
1062 | 584 | bmp_info.gamma_scale.z)/3.0; |
1063 | 584 | if (image->logging) |
1064 | 584 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1065 | 584 | "BMP Gamma: %g", bmp_gamma); |
1066 | | /* This range is based on what libpng is willing to accept */ |
1067 | 584 | if (bmp_gamma > 0.00016 && bmp_gamma < 6250.0) |
1068 | 79 | { |
1069 | 79 | image->gamma=bmp_gamma; |
1070 | 79 | } |
1071 | 505 | else |
1072 | 505 | { |
1073 | 505 | if (image->logging) |
1074 | 505 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1075 | 505 | "Ignoring illegal BMP gamma value %g " |
1076 | 505 | "(gamma scale xyz %g,%g,%g)", |
1077 | 505 | bmp_gamma, bmp_info.gamma_scale.x, |
1078 | 505 | bmp_info.gamma_scale.y, bmp_info.gamma_scale.z); |
1079 | 505 | } |
1080 | 584 | } |
1081 | 2.03k | } |
1082 | 2.32k | } |
1083 | 2.33k | } |
1084 | 10.7k | if (bmp_info.size > 108) |
1085 | 2.25k | { |
1086 | | /* |
1087 | | https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapv5header |
1088 | | https://www.loc.gov/preservation/digital/formats/fdd/fdd000189.shtml |
1089 | | */ |
1090 | 2.25k | magick_uint32_t |
1091 | 2.25k | intent; |
1092 | | |
1093 | | /* |
1094 | | Read BMP Version 5 color management information. |
1095 | | */ |
1096 | 2.25k | intent=ReadBlobLSBLong(image); |
1097 | 2.25k | switch ((int) intent) |
1098 | 2.25k | { |
1099 | 8 | case LCS_GM_BUSINESS: |
1100 | 8 | { |
1101 | 8 | image->rendering_intent=SaturationIntent; |
1102 | 8 | break; |
1103 | 0 | } |
1104 | 2 | case LCS_GM_GRAPHICS: |
1105 | 2 | { |
1106 | 2 | image->rendering_intent=RelativeIntent; |
1107 | 2 | break; |
1108 | 0 | } |
1109 | 2 | case LCS_GM_IMAGES: |
1110 | 2 | { |
1111 | 2 | image->rendering_intent=PerceptualIntent; |
1112 | 2 | break; |
1113 | 0 | } |
1114 | 70 | case LCS_GM_ABS_COLORIMETRIC: |
1115 | 70 | { |
1116 | 70 | image->rendering_intent=AbsoluteIntent; |
1117 | 70 | break; |
1118 | 0 | } |
1119 | 2.25k | } |
1120 | 2.25k | profile_data=ReadBlobLSBLong(image); |
1121 | 2.25k | profile_size=ReadBlobLSBLong(image); |
1122 | 2.25k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1123 | 2.25k | " Profile: size %u data %u", |
1124 | 2.25k | profile_size,profile_data); |
1125 | 2.25k | (void) ReadBlobLSBLong(image); /* Reserved byte */ |
1126 | 2.25k | } |
1127 | 10.7k | } |
1128 | | |
1129 | 11.3k | if (EOFBlob(image)) |
1130 | 10.3k | ThrowBMPReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
1131 | | |
1132 | | /* |
1133 | | It seems that some BMPs claim a file size two bytes larger than |
1134 | | they actually are so allow some slop before warning about file |
1135 | | size. |
1136 | | */ |
1137 | 10.3k | if ((magick_off_t) bmp_info.file_size > file_size+2) |
1138 | 0 | { |
1139 | 0 | ThrowException(exception,CorruptImageWarning, |
1140 | 0 | LengthAndFilesizeDoNotMatch,image->filename); |
1141 | 0 | } |
1142 | 10.3k | if (logging && (magick_off_t) bmp_info.file_size < file_size) |
1143 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1144 | 0 | " Discarding all data beyond bmp_info.file_size"); |
1145 | 10.3k | if (bmp_info.width <= 0) |
1146 | 9.78k | ThrowBMPReaderException(CorruptImageError,NegativeOrZeroImageSize,image); |
1147 | 9.78k | if ((bmp_info.height) == 0 || (bmp_info.height < -2147483647)) |
1148 | 9.77k | ThrowBMPReaderException(CorruptImageError,NegativeOrZeroImageSize,image); |
1149 | 9.77k | if ((bmp_info.height < 0) && (bmp_info.compression !=0)) |
1150 | 9.28k | ThrowBMPReaderException(CorruptImageError,CompressionNotValid,image); |
1151 | 9.28k | switch ((unsigned int) bmp_info.compression) |
1152 | 9.28k | { |
1153 | 198 | case BI_BITFIELDS: |
1154 | 198 | if(bmp_info.size==40) |
1155 | 188 | { |
1156 | 188 | if(bmp_info.ba_offset==0) bmp_info.ba_offset=52; |
1157 | 188 | if(bmp_info.ba_offset<52) /* check for gap size >=12*/ |
1158 | 184 | ThrowBMPReaderException(CorruptImageError,CorruptImage,image); |
1159 | 184 | bmp_info.red_mask=ReadBlobLSBLong(image); |
1160 | 184 | bmp_info.green_mask=ReadBlobLSBLong(image); |
1161 | 184 | bmp_info.blue_mask=ReadBlobLSBLong(image); |
1162 | 184 | goto CheckBitSize; |
1163 | 188 | } |
1164 | 10 | goto CheckAlphaBitSize; |
1165 | 325 | case BI_ALPHABITFIELDS: |
1166 | 325 | if(bmp_info.size==40) |
1167 | 289 | { |
1168 | 289 | if(bmp_info.ba_offset==0) bmp_info.ba_offset=56; |
1169 | 289 | if(bmp_info.ba_offset<56) /* check for gap size >=16*/ |
1170 | 288 | ThrowBMPReaderException(CorruptImageError,CorruptImage,image); |
1171 | 288 | bmp_info.red_mask=ReadBlobLSBLong(image); |
1172 | 288 | bmp_info.green_mask=ReadBlobLSBLong(image); |
1173 | 288 | bmp_info.blue_mask=ReadBlobLSBLong(image); |
1174 | 288 | bmp_info.alpha_mask=ReadBlobLSBLong(image); |
1175 | 288 | } |
1176 | 334 | CheckAlphaBitSize: |
1177 | 334 | if (image->logging) |
1178 | 334 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1179 | 334 | "Alpha Mask: 0x%04x", |
1180 | 334 | bmp_info.alpha_mask); |
1181 | 518 | CheckBitSize: |
1182 | 518 | if (image->logging) |
1183 | 518 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1184 | 518 | "Red Mask: 0x%04x\n" |
1185 | 518 | "Green Mask: 0x%04x\n" |
1186 | 518 | "Blue Mask: 0x%04x", |
1187 | 518 | bmp_info.red_mask, bmp_info.green_mask, bmp_info.blue_mask); |
1188 | 518 | if(!(bmp_info.bits_per_pixel==16 || bmp_info.bits_per_pixel==32)) |
1189 | 360 | ThrowBMPReaderException(CorruptImageError,CorruptImage,image); |
1190 | 360 | break; |
1191 | | |
1192 | 709 | case BI_RGB: |
1193 | 1.08k | case BI_RLE8: |
1194 | 1.26k | case BI_RLE4: |
1195 | 1.26k | break; |
1196 | | |
1197 | 4.54k | case BI_JPEG: |
1198 | 4.54k | offset = start_position + 14 + bmp_info.size; |
1199 | 4.54k | if(logging) |
1200 | 0 | (void)LogMagickEvent(CoderEvent,GetMagickModule(), |
1201 | 0 | "Seek offset %" MAGICK_OFF_F "d", |
1202 | 0 | (magick_off_t) offset); |
1203 | 4.54k | if((offset < start_position) || |
1204 | 4.54k | (SeekBlob(image,offset,SEEK_SET) != (magick_off_t) offset)) |
1205 | 4.54k | ThrowBMPReaderException(CorruptImageError,ImproperImageHeader,image); |
1206 | 4.54k | { |
1207 | 4.54k | MonitorHandler previous_handler; |
1208 | 4.54k | previous_handler = SetMonitorHandler(0); |
1209 | 4.54k | image = ExtractNestedBlob(&image, image_info, bmp_info.compression, exception); |
1210 | 4.54k | (void) SetMonitorHandler(previous_handler); |
1211 | 4.54k | if (exception->severity >= ErrorException) |
1212 | 3.27k | ThrowBMPReaderException(CoderError,JPEGCompressionNotSupported,image) |
1213 | 4.54k | } |
1214 | 1.26k | goto ExitLoop; /* I need to break a loop. Other BMPs in a chain are ignored. */ |
1215 | | |
1216 | 2.52k | case BI_PNG: |
1217 | 2.52k | offset = start_position + 14 + bmp_info.size; |
1218 | 2.52k | if(logging) |
1219 | 0 | (void)LogMagickEvent(CoderEvent,GetMagickModule(), |
1220 | 0 | "Seek offset %" MAGICK_OFF_F "d", |
1221 | 0 | (magick_off_t) offset); |
1222 | 2.52k | if((offset < start_position) || |
1223 | 2.52k | (SeekBlob(image,offset,SEEK_SET) != (magick_off_t) offset)) |
1224 | 2.52k | ThrowBMPReaderException(CorruptImageError,ImproperImageHeader,image); |
1225 | 2.52k | { |
1226 | 2.52k | MonitorHandler previous_handler; |
1227 | 2.52k | previous_handler = SetMonitorHandler(0); |
1228 | 2.52k | image = ExtractNestedBlob(&image, image_info, bmp_info.compression, exception); |
1229 | 2.52k | (void) SetMonitorHandler(previous_handler); |
1230 | 2.52k | if (exception->severity >= ErrorException) |
1231 | 2.52k | ThrowBMPReaderException(CoderError,PNGCompressionNotSupported,image) |
1232 | 2.52k | } |
1233 | 0 | goto ExitLoop; /* I need to break a loop. Other BMPs in a chain are ignored. */ |
1234 | | |
1235 | 423 | default: |
1236 | 423 | ThrowBMPReaderException(CorruptImageError,UnrecognizedImageCompression,image) |
1237 | 9.28k | } |
1238 | | |
1239 | 1.62k | if (bmp_info.planes != 1) |
1240 | 24 | ThrowBMPReaderException(CorruptImageError,StaticPlanesValueNotEqualToOne, |
1241 | 1.62k | image); |
1242 | 1.60k | if ((bmp_info.bits_per_pixel != 1) && (bmp_info.bits_per_pixel != 2) && (bmp_info.bits_per_pixel != 4) && |
1243 | 1.60k | (bmp_info.bits_per_pixel != 8) && (bmp_info.bits_per_pixel != 16) && |
1244 | 1.60k | (bmp_info.bits_per_pixel != 24) && (bmp_info.bits_per_pixel != 32) && |
1245 | 1.60k | (bmp_info.bits_per_pixel != 48) && (bmp_info.bits_per_pixel != 64)) |
1246 | 1.56k | ThrowBMPReaderException(CorruptImageError,UnrecognizedBitsPerPixel,image); |
1247 | 1.56k | if (bmp_info.bits_per_pixel < 16) |
1248 | 794 | { |
1249 | 794 | if (bmp_info.number_colors > (1UL << bmp_info.bits_per_pixel)) |
1250 | 768 | ThrowBMPReaderException(CorruptImageError,UnrecognizedNumberOfColors,image); |
1251 | 768 | } |
1252 | 1.53k | if ((bmp_info.compression == 1) && (bmp_info.bits_per_pixel != 8)) |
1253 | 1.53k | ThrowBMPReaderException(CorruptImageError,UnrecognizedBitsPerPixel,image); |
1254 | 1.53k | if ((bmp_info.compression == 2) && (bmp_info.bits_per_pixel != 4)) |
1255 | 1.52k | ThrowBMPReaderException(CorruptImageError,UnrecognizedBitsPerPixel,image); |
1256 | 1.52k | if ((bmp_info.compression == 3) && (bmp_info.bits_per_pixel < 16)) |
1257 | 1.52k | ThrowBMPReaderException(CorruptImageError,UnrecognizedBitsPerPixel,image); |
1258 | | |
1259 | 1.52k | image->columns=bmp_info.width; |
1260 | 1.52k | image->rows=AbsoluteValue(bmp_info.height); |
1261 | | #if (QuantumDepth == 8) |
1262 | | image->depth=8; |
1263 | | #else |
1264 | 1.52k | if(bmp_info.bits_per_pixel==48 || bmp_info.bits_per_pixel==64) |
1265 | 213 | image->depth=16; |
1266 | 1.31k | else |
1267 | 1.31k | image->depth=8; |
1268 | 1.52k | #endif |
1269 | | /* |
1270 | | Image has alpha channel if alpha mask is specified, or is |
1271 | | uncompressed and 32-bits per pixel |
1272 | | */ |
1273 | 1.52k | image->matte=((bmp_info.alpha_mask != 0) |
1274 | 1.52k | || ((bmp_info.compression == BI_RGB) |
1275 | 1.38k | && (bmp_info.bits_per_pixel == 32))); |
1276 | 1.52k | if (bmp_info.bits_per_pixel < 16) |
1277 | 765 | { |
1278 | 765 | if (bmp_info.number_colors == 0) |
1279 | 227 | image->colors=1L << bmp_info.bits_per_pixel; |
1280 | 538 | else |
1281 | 538 | image->colors=bmp_info.number_colors; |
1282 | 765 | image->storage_class=PseudoClass; |
1283 | 765 | } |
1284 | 1.52k | if (image->storage_class == PseudoClass) |
1285 | 765 | { |
1286 | 765 | unsigned int |
1287 | 765 | packet_size; |
1288 | | |
1289 | | /* |
1290 | | Read BMP raster colormap. |
1291 | | */ |
1292 | 765 | if (logging) |
1293 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1294 | 0 | " Reading colormap of %u colors",image->colors); |
1295 | 765 | if (!AllocateImageColormap(image,image->colors)) |
1296 | 0 | ThrowBMPReaderException(ResourceLimitError,MemoryAllocationFailed, |
1297 | 765 | image); |
1298 | 765 | bmp_colormap=MagickAllocateResourceLimitedArray(unsigned char *,4,image->colors); |
1299 | 765 | if (bmp_colormap == (unsigned char *) NULL) |
1300 | 0 | ThrowBMPReaderException(ResourceLimitError,MemoryAllocationFailed, |
1301 | 765 | image); |
1302 | 765 | if ((bmp_info.size == 12) || (bmp_info.size == 64)) |
1303 | 152 | packet_size=3; |
1304 | 613 | else |
1305 | 613 | packet_size=4; |
1306 | 765 | offset=start_position+14+bmp_info.size; |
1307 | 765 | if (logging) |
1308 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1309 | 0 | "Seek offset %" MAGICK_OFF_F "d", |
1310 | 0 | (magick_off_t) offset); |
1311 | 765 | if ((offset < start_position) || |
1312 | 765 | (SeekBlob(image,offset,SEEK_SET) != (magick_off_t) offset)) |
1313 | 765 | ThrowBMPReaderException(CorruptImageError,ImproperImageHeader,image); |
1314 | 765 | if (ReadBlob(image,(size_t) packet_size*image->colors,(char *) bmp_colormap) |
1315 | 765 | != (size_t) packet_size*image->colors) |
1316 | 629 | ThrowBMPReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
1317 | 629 | p=bmp_colormap; |
1318 | 9.01k | for (i=0; i < (long) image->colors; i++) |
1319 | 8.38k | { |
1320 | 8.38k | image->colormap[i].blue=ScaleCharToQuantum(*p++); |
1321 | 8.38k | image->colormap[i].green=ScaleCharToQuantum(*p++); |
1322 | 8.38k | image->colormap[i].red=ScaleCharToQuantum(*p++); |
1323 | 8.38k | if (packet_size == 4) |
1324 | 4.40k | p++; |
1325 | 8.38k | } |
1326 | 629 | MagickFreeResourceLimitedMemory(bmp_colormap); |
1327 | 629 | } |
1328 | | |
1329 | 1.39k | if (image_info->ping && (image_info->subrange != 0)) |
1330 | 0 | if (image->scene >= (image_info->subimage+image_info->subrange-1)) |
1331 | 0 | break; |
1332 | | |
1333 | 1.39k | if (CheckImagePixelLimits(image, exception) != MagickPass) |
1334 | 1.33k | ThrowBMPReaderException(ResourceLimitError,ImagePixelLimitExceeded,image); |
1335 | | |
1336 | | /* |
1337 | | Read image data. |
1338 | | */ |
1339 | 1.33k | if (logging) |
1340 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1341 | 0 | "start_position %" MAGICK_OFF_F "d," |
1342 | 0 | " bmp_info.offset_bits %" MAGICK_OFF_F "d," |
1343 | 0 | " bmp_info.ba_offset %" MAGICK_OFF_F "d" , |
1344 | 0 | (magick_off_t) start_position, |
1345 | 0 | (magick_off_t) bmp_info.offset_bits, |
1346 | 0 | (magick_off_t) bmp_info.ba_offset); |
1347 | 1.33k | offset=start_position+bmp_info.offset_bits; |
1348 | 1.33k | if (logging) |
1349 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1350 | 0 | "Seek offset %" MAGICK_OFF_F "d", |
1351 | 0 | (magick_off_t) offset); |
1352 | 1.33k | if ((offset < start_position) || |
1353 | 1.33k | (SeekBlob(image,offset,SEEK_SET) != (magick_off_t) offset)) |
1354 | 1.33k | ThrowBMPReaderException(CorruptImageError,ImproperImageHeader,image); |
1355 | 1.33k | if (bmp_info.compression == BI_RLE4) |
1356 | 154 | bmp_info.bits_per_pixel<<=1; |
1357 | 1.33k | if (logging) |
1358 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1359 | 0 | "image->columns: %lu, bmp_info.bits_per_pixel %u", |
1360 | 0 | image->columns, bmp_info.bits_per_pixel); |
1361 | | /* |
1362 | | Below emulates: |
1363 | | bytes_per_line=4*((image->columns*bmp_info.bits_per_pixel+31)/32); |
1364 | | */ |
1365 | 1.33k | bytes_per_line=MagickArraySize(image->columns,bmp_info.bits_per_pixel); |
1366 | 1.33k | if ((bytes_per_line > 0) && (~((size_t) 0) - bytes_per_line) > 31) |
1367 | 1.33k | bytes_per_line = MagickArraySize(4,(bytes_per_line+31)/32); |
1368 | 1.33k | if (bytes_per_line == 0) |
1369 | 1.33k | ThrowBMPReaderException(CoderError,ArithmeticOverflow,image); |
1370 | | |
1371 | 1.33k | if (logging) |
1372 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1373 | 0 | " Bytes per line: %" MAGICK_SIZE_T_F "u", |
1374 | 0 | (MAGICK_SIZE_T) bytes_per_line); |
1375 | | |
1376 | 1.33k | length=MagickArraySize(bytes_per_line,image->rows); |
1377 | 1.33k | if (logging) |
1378 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1379 | 0 | " Expected total raster length: %" MAGICK_SIZE_T_F "u", |
1380 | 0 | (MAGICK_SIZE_T) length); |
1381 | 1.33k | if (length == 0) |
1382 | 1.33k | ThrowBMPReaderException(CoderError,ArithmeticOverflow,image); |
1383 | | |
1384 | | /* |
1385 | | Check that file data is reasonable given claims by file header. |
1386 | | We do this before allocating raster memory to avoid DOS. |
1387 | | */ |
1388 | 1.33k | if ((bmp_info.compression == BI_RGB) || |
1389 | 1.33k | (bmp_info.compression == BI_BITFIELDS) || |
1390 | 1.33k | (bmp_info.compression == BI_ALPHABITFIELDS)) |
1391 | 877 | { |
1392 | | /* |
1393 | | Not compressed. |
1394 | | */ |
1395 | 877 | file_remaining=file_size-TellBlob(image); |
1396 | 877 | if (file_remaining < (magick_off_t) length) |
1397 | 76 | ThrowBMPReaderException(CorruptImageError,InsufficientImageDataInFile, |
1398 | 877 | image); |
1399 | 801 | } |
1400 | 459 | else if ((bmp_info.compression == BI_RLE4) || |
1401 | 459 | (bmp_info.compression == BI_RLE8)) |
1402 | 459 | { |
1403 | | /* RLE Compressed. Assume a maximum compression ratio. */ |
1404 | 459 | file_remaining=file_size-TellBlob(image); |
1405 | 459 | if ((file_remaining <= 0) || (((double) length/file_remaining) > 254.0)) |
1406 | 40 | ThrowBMPReaderException(CorruptImageError,InsufficientImageDataInFile, |
1407 | 459 | image); |
1408 | 419 | } |
1409 | | |
1410 | 1.22k | if ((image->columns+1UL) < image->columns) |
1411 | 1.22k | ThrowBMPReaderException(CoderError,ArithmeticOverflow,image); |
1412 | 1.22k | pixels_size=MagickArraySize(Max(bytes_per_line,(size_t) image->columns+1), |
1413 | 1.22k | image->rows); |
1414 | 1.22k | if (logging) |
1415 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1416 | 0 | " Pixels size %" MAGICK_SIZE_T_F "u", |
1417 | 0 | (MAGICK_SIZE_T) pixels_size); |
1418 | 1.22k | if (pixels_size == 0) |
1419 | 1.22k | ThrowBMPReaderException(CoderError,ArithmeticOverflow,image); |
1420 | 1.22k | pixels=MagickAllocateResourceLimitedMemory(unsigned char *, pixels_size); |
1421 | 1.22k | if (pixels == (unsigned char *) NULL) |
1422 | 1.22k | ThrowBMPReaderException(ResourceLimitError,MemoryAllocationFailed,image); |
1423 | 1.22k | if ((bmp_info.compression == BI_RGB) || |
1424 | 1.22k | (bmp_info.compression == BI_BITFIELDS) || |
1425 | 1.22k | (bmp_info.compression == BI_ALPHABITFIELDS)) |
1426 | 801 | { |
1427 | 801 | if (logging) |
1428 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1429 | 0 | " Reading pixels (%" MAGICK_SIZE_T_F "u bytes)", |
1430 | 0 | (MAGICK_SIZE_T) length); |
1431 | 801 | if (ReadBlob(image,length,(char *) pixels) != (size_t) length) |
1432 | 800 | ThrowBMPReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
1433 | 800 | } |
1434 | 419 | else |
1435 | 419 | { |
1436 | | /* |
1437 | | Convert run-length encoded raster pixels. |
1438 | | |
1439 | | DecodeImage() normally decompresses to rows*columns bytes of data. |
1440 | | */ |
1441 | 419 | status=DecodeImage(image,bmp_info.compression,pixels, |
1442 | 419 | (size_t) image->rows*image->columns); |
1443 | 419 | if (status == MagickFail) |
1444 | 198 | ThrowBMPReaderException(CorruptImageError,UnableToRunlengthDecodeImage, |
1445 | 419 | image); |
1446 | 221 | } |
1447 | | /* |
1448 | | Initialize image structure. |
1449 | | */ |
1450 | 1.02k | image->units=PixelsPerCentimeterResolution; |
1451 | 1.02k | image->x_resolution=bmp_info.x_pixels/100.0; |
1452 | 1.02k | image->y_resolution=bmp_info.y_pixels/100.0; |
1453 | 1.02k | (void) memset(&quantum_bits,0,sizeof(PixelPacket)); |
1454 | 1.02k | (void) memset(&shift,0,sizeof(PixelPacket)); |
1455 | | /* |
1456 | | Convert BMP raster image to pixel packets. |
1457 | | */ |
1458 | | #if 0 |
1459 | | if (bmp_info.compression == BI_RGB) |
1460 | | { |
1461 | | bmp_info.alpha_mask=(image->matte ? 0xff000000U : 0U); |
1462 | | bmp_info.red_mask=0x00ff0000U; |
1463 | | bmp_info.green_mask=0x0000ff00U; |
1464 | | bmp_info.blue_mask=0x000000ffU; |
1465 | | if (bmp_info.bits_per_pixel == 16) |
1466 | | { |
1467 | | /* RGB555. JFO: Please consider whether this is correct ?? I guess RGB 565! */ |
1468 | | bmp_info.red_mask=0x00007c00U; |
1469 | | bmp_info.green_mask=0x000003e0U; |
1470 | | bmp_info.blue_mask=0x0000001fU; |
1471 | | } |
1472 | | } |
1473 | | #endif |
1474 | 1.02k | if ((bmp_info.bits_per_pixel == 16) || (bmp_info.bits_per_pixel == 32)) |
1475 | 406 | { |
1476 | 406 | register magick_uint32_t |
1477 | 406 | sample; |
1478 | | |
1479 | | /* Use defaults for 40 bytes header and also a reminder of a culture of sloth. */ |
1480 | 406 | if(bmp_info.compression == BI_RGB || |
1481 | 406 | (bmp_info.red_mask==0 && bmp_info.green_mask==0 && bmp_info.blue_mask==0 && bmp_info.alpha_mask==0)) |
1482 | 147 | { |
1483 | 147 | if (bmp_info.bits_per_pixel == 16) |
1484 | 57 | { |
1485 | 57 | if(bmp_info.compression==BI_ALPHABITFIELDS) |
1486 | 7 | { /* USE ARGB 1555 */ |
1487 | 7 | image->matte = MagickTrue; |
1488 | 7 | bmp_info.alpha_mask=0x00008000U; |
1489 | 7 | bmp_info.red_mask=0x00007c00U; |
1490 | 7 | bmp_info.green_mask=0x000003e0U; |
1491 | 7 | bmp_info.blue_mask=0x0000001fU; |
1492 | 7 | } |
1493 | 50 | else /* USE RGB 565 */ |
1494 | 50 | { |
1495 | 50 | bmp_info.red_mask=0x0000F800U; |
1496 | 50 | bmp_info.green_mask=0x000007e0U; |
1497 | 50 | bmp_info.blue_mask=0x0000001fU; |
1498 | 50 | } |
1499 | 57 | } |
1500 | 147 | if ( bmp_info.bits_per_pixel == 32) |
1501 | 90 | { |
1502 | 90 | if(bmp_info.compression==BI_RGB || bmp_info.compression==BI_ALPHABITFIELDS) |
1503 | 85 | { |
1504 | 85 | image->matte = MagickTrue; |
1505 | 85 | bmp_info.alpha_mask=0xff000000U; |
1506 | 85 | } |
1507 | 90 | bmp_info.red_mask=0x00ff0000U; |
1508 | 90 | bmp_info.green_mask=0x0000ff00U; |
1509 | 90 | bmp_info.blue_mask=0x000000ffU; |
1510 | 90 | } |
1511 | 147 | } |
1512 | | |
1513 | | /* |
1514 | | Get shift and quantum bits info from bitfield masks. |
1515 | | */ |
1516 | 406 | if (bmp_info.red_mask != 0U) |
1517 | 3.53k | while ((shift.red < 32U) && (((bmp_info.red_mask << shift.red) & 0x80000000U) == 0)) |
1518 | 3.23k | shift.red++; |
1519 | 406 | if (bmp_info.green_mask != 0) |
1520 | 3.63k | while ((shift.green < 32U) && (((bmp_info.green_mask << shift.green) & 0x80000000U) == 0)) |
1521 | 3.33k | shift.green++; |
1522 | 406 | if (bmp_info.blue_mask != 0) |
1523 | 5.46k | while ((shift.blue < 32U) && (((bmp_info.blue_mask << shift.blue) & 0x80000000U) == 0)) |
1524 | 5.17k | shift.blue++; |
1525 | 406 | if (bmp_info.alpha_mask != 0) |
1526 | 504 | while ((shift.opacity < 32U) && (((bmp_info.alpha_mask << shift.opacity) & 0x80000000U) == 0)) |
1527 | 341 | shift.opacity++; |
1528 | 406 | sample=shift.red; |
1529 | 2.53k | while ((sample < 32U) && (((bmp_info.red_mask << sample) & 0x80000000U) != 0)) |
1530 | 2.13k | sample++; |
1531 | 406 | quantum_bits.red=(Quantum) (sample-shift.red); |
1532 | 406 | sample=shift.green; |
1533 | 2.74k | while ((sample < 32U) && (((bmp_info.green_mask << sample) & 0x80000000U) != 0)) |
1534 | 2.33k | sample++; |
1535 | 406 | quantum_bits.green=(Quantum) (sample-shift.green); |
1536 | 406 | sample=shift.blue; |
1537 | 2.76k | while ((sample < 32U) && (((bmp_info.blue_mask << sample) & 0x80000000U) != 0)) |
1538 | 2.35k | sample++; |
1539 | 406 | quantum_bits.blue=(Quantum) (sample-shift.blue); |
1540 | 406 | sample=shift.opacity; |
1541 | 1.97k | while ((sample < 32U) && (((bmp_info.alpha_mask << sample) & 0x80000000U) != 0)) |
1542 | 1.56k | sample++; |
1543 | 406 | quantum_bits.opacity=(Quantum) (sample-shift.opacity); |
1544 | 406 | } |
1545 | 1.02k | switch (bmp_info.bits_per_pixel) |
1546 | 1.02k | { |
1547 | 76 | case 1: |
1548 | 110 | case 2: |
1549 | 140 | case 4: |
1550 | 140 | { |
1551 | | /* |
1552 | | Convert PseudoColor scanline. |
1553 | | */ |
1554 | 6.07k | for (y=(long) image->rows-1; y >= 0; y--) |
1555 | 5.93k | { |
1556 | 5.93k | p=pixels+((size_t)image->rows-y-1)*bytes_per_line; |
1557 | 5.93k | q=SetImagePixels(image,0,y,image->columns,1); |
1558 | 5.93k | if (q == (PixelPacket *) NULL) |
1559 | 0 | break; |
1560 | 5.93k | if (ImportImagePixelArea(image,IndexQuantum,bmp_info.bits_per_pixel,p,0,0) |
1561 | 5.93k | == MagickFail) |
1562 | 0 | break; |
1563 | 5.93k | if (!SyncImagePixels(image)) |
1564 | 0 | break; |
1565 | 5.93k | if (image->previous == (Image *) NULL) |
1566 | 5.93k | if (QuantumTick(y,image->rows)) |
1567 | 3.00k | { |
1568 | 3.00k | status=MagickMonitorFormatted((size_t)image->rows-y-1,image->rows, |
1569 | 3.00k | exception,LoadImageText, |
1570 | 3.00k | image->filename, |
1571 | 3.00k | image->columns,image->rows); |
1572 | 3.00k | if (status == MagickFalse) |
1573 | 0 | break; |
1574 | 3.00k | } |
1575 | 5.93k | } |
1576 | 140 | break; |
1577 | 110 | } |
1578 | 240 | case 8: |
1579 | 240 | { |
1580 | | /* |
1581 | | Convert PseudoColor scanline. |
1582 | | */ |
1583 | 240 | if ((bmp_info.compression == BI_RLE8) || |
1584 | 240 | (bmp_info.compression == BI_RLE4)) |
1585 | 221 | bytes_per_line=image->columns; |
1586 | 79.9k | for (y=(long) image->rows-1; y >= 0; y--) |
1587 | 79.7k | { |
1588 | 79.7k | p=pixels+((size_t)image->rows-y-1)*bytes_per_line; |
1589 | 79.7k | q=SetImagePixels(image,0,y,image->columns,1); |
1590 | 79.7k | if (q == (PixelPacket *) NULL) |
1591 | 0 | break; |
1592 | 79.7k | if (ImportImagePixelArea(image,IndexQuantum,bmp_info.bits_per_pixel,p,0,0) |
1593 | 79.7k | == MagickFail) |
1594 | 0 | break; |
1595 | 79.7k | if (!SyncImagePixels(image)) |
1596 | 0 | break; |
1597 | 79.7k | if (image->previous == (Image *) NULL) |
1598 | 79.7k | if (QuantumTick(y,image->rows)) |
1599 | 16.1k | { |
1600 | 16.1k | status=MagickMonitorFormatted((size_t)image->rows-y-1,image->rows, |
1601 | 16.1k | exception,LoadImageText, |
1602 | 16.1k | image->filename, |
1603 | 16.1k | image->columns,image->rows); |
1604 | 16.1k | if (status == MagickFalse) |
1605 | 0 | break; |
1606 | 16.1k | } |
1607 | 79.7k | } |
1608 | 240 | break; |
1609 | 110 | } |
1610 | 215 | case 16: |
1611 | 215 | { |
1612 | 215 | magick_uint32_t |
1613 | 215 | pixel; |
1614 | | |
1615 | | /* |
1616 | | Convert bitfield encoded 16-bit PseudoColor scanline. |
1617 | | */ |
1618 | 215 | if (bmp_info.compression != BI_RGB && |
1619 | 215 | bmp_info.compression != BI_BITFIELDS && |
1620 | 215 | bmp_info.compression != BI_ALPHABITFIELDS) |
1621 | 0 | ThrowBMPReaderException(CorruptImageError,UnrecognizedImageCompression,image) |
1622 | 215 | bytes_per_line=2*((size_t)image->columns+image->columns%2); |
1623 | 215 | image->storage_class=DirectClass; |
1624 | 6.63k | for (y=(long) image->rows-1; y >= 0; y--) |
1625 | 6.42k | { |
1626 | 6.42k | p=pixels+((size_t)image->rows-y-1)*bytes_per_line; |
1627 | 6.42k | q=SetImagePixels(image,0,y,image->columns,1); |
1628 | 6.42k | if (q == (PixelPacket *) NULL) |
1629 | 0 | break; |
1630 | 146k | for (x=0; x < (long) image->columns; x++) |
1631 | 140k | { |
1632 | | #ifdef WORDS_BIGENDIAN |
1633 | | pixel=(*p++); |
1634 | | pixel|=(*p++) << 8; |
1635 | | #else |
1636 | 140k | pixel = *(magick_uint16_t*)p; |
1637 | 140k | p += 2; |
1638 | 140k | #endif |
1639 | 140k | red=((pixel & bmp_info.red_mask) << shift.red) >> 16; |
1640 | 140k | if (quantum_bits.red <= 8) /* TODO: this is ugly, but better than nothing. Should be reworked. */ |
1641 | 139k | red|=(red >> 8); |
1642 | 140k | green=((pixel & bmp_info.green_mask) << shift.green) >> 16; |
1643 | 140k | if (quantum_bits.green <= 8) |
1644 | 139k | green|=(green >> 8); |
1645 | 140k | blue=((pixel & bmp_info.blue_mask) << shift.blue) >> 16; |
1646 | 140k | if (quantum_bits.blue <= 8) |
1647 | 139k | blue|=(blue >> 8); |
1648 | 140k | if (image->matte != MagickFalse) |
1649 | 518 | { |
1650 | 518 | opacity=((pixel & bmp_info.alpha_mask) << shift.opacity) >> 16; |
1651 | 518 | if (quantum_bits.opacity <= 8) |
1652 | 260 | opacity|=(opacity >> 8); |
1653 | 518 | q->opacity=MaxRGB-ScaleShortToQuantum(opacity); |
1654 | 518 | } |
1655 | 140k | q->red=ScaleShortToQuantum(red); |
1656 | 140k | q->green=ScaleShortToQuantum(green); |
1657 | 140k | q->blue=ScaleShortToQuantum(blue); |
1658 | 140k | q->opacity=OpaqueOpacity; |
1659 | 140k | q++; |
1660 | 140k | } |
1661 | 6.42k | if (!SyncImagePixels(image)) |
1662 | 0 | break; |
1663 | 6.42k | if (image->previous == (Image *) NULL) |
1664 | 6.42k | if (QuantumTick(y,image->rows)) |
1665 | 2.94k | { |
1666 | 2.94k | status=MagickMonitorFormatted((size_t)image->rows-y-1,image->rows, |
1667 | 2.94k | exception,LoadImageText, |
1668 | 2.94k | image->filename, |
1669 | 2.94k | image->columns,image->rows); |
1670 | 2.94k | if (status == MagickFalse) |
1671 | 0 | break; |
1672 | 2.94k | } |
1673 | 6.42k | } |
1674 | 215 | break; |
1675 | 215 | } |
1676 | 37 | case 24: |
1677 | 37 | { |
1678 | | /* |
1679 | | Convert DirectColor scanline. |
1680 | | */ |
1681 | 37 | bytes_per_line=4*(((size_t)image->columns*24+31)/32); |
1682 | 5.09k | for (y=(long) image->rows-1; y >= 0; y--) |
1683 | 5.05k | { |
1684 | 5.05k | p=pixels+((size_t)image->rows-y-1)*bytes_per_line; |
1685 | 5.05k | q=SetImagePixels(image,0,y,image->columns,1); |
1686 | 5.05k | if (q == (PixelPacket *) NULL) |
1687 | 0 | break; |
1688 | 10.3k | for (x=0; x < (long) image->columns; x++) |
1689 | 5.25k | { |
1690 | 5.25k | q->blue=ScaleCharToQuantum(*p++); |
1691 | 5.25k | q->green=ScaleCharToQuantum(*p++); |
1692 | 5.25k | q->red=ScaleCharToQuantum(*p++); |
1693 | 5.25k | q->opacity=OpaqueOpacity; |
1694 | 5.25k | q++; |
1695 | 5.25k | } |
1696 | 5.05k | if (!SyncImagePixels(image)) |
1697 | 0 | break; |
1698 | 5.05k | if (image->previous == (Image *) NULL) |
1699 | 5.05k | if (QuantumTick(y,image->rows)) |
1700 | 2.25k | { |
1701 | 2.25k | status=MagickMonitorFormatted((size_t)image->rows-y-1,image->rows, |
1702 | 2.25k | exception,LoadImageText, |
1703 | 2.25k | image->filename, |
1704 | 2.25k | image->columns,image->rows); |
1705 | 2.25k | if (status == MagickFalse) |
1706 | 0 | break; |
1707 | 2.25k | } |
1708 | 5.05k | } |
1709 | 37 | break; |
1710 | 215 | } |
1711 | 191 | case 32: |
1712 | 191 | { |
1713 | | /* char ZeroOpacity = 1; */ |
1714 | | /* |
1715 | | Convert bitfield encoded DirectColor scanline. |
1716 | | */ |
1717 | 191 | if(bmp_info.compression != BI_RGB && |
1718 | 191 | bmp_info.compression != BI_BITFIELDS && |
1719 | 191 | bmp_info.compression != BI_ALPHABITFIELDS) |
1720 | 0 | ThrowBMPReaderException(CorruptImageError,UnrecognizedImageCompression,image) |
1721 | 191 | bytes_per_line=4*((size_t) image->columns); |
1722 | 5.65k | for (y=(long) image->rows-1; y >= 0; y--) |
1723 | 5.46k | { |
1724 | 5.46k | magick_uint32_t |
1725 | 5.46k | pixel; |
1726 | | |
1727 | 5.46k | p=pixels+((size_t) image->rows-y-1)*bytes_per_line; |
1728 | 5.46k | q=SetImagePixels(image,0,y,image->columns,1); |
1729 | 5.46k | if (q == (PixelPacket *) NULL) |
1730 | 0 | break; |
1731 | 11.3k | for (x=0; x < (long) image->columns; x++) |
1732 | 5.88k | { |
1733 | | #ifdef WORDS_BIGENDIAN |
1734 | | pixel =((magick_uint32_t) *p++); |
1735 | | pixel|=((magick_uint32_t) *p++ << 8); |
1736 | | pixel|=((magick_uint32_t) *p++ << 16); |
1737 | | pixel|=((magick_uint32_t) *p++ << 24); |
1738 | | #else |
1739 | 5.88k | pixel = *(magick_uint32_t*)p; |
1740 | 5.88k | p += 4; |
1741 | 5.88k | #endif |
1742 | 5.88k | red=((pixel & bmp_info.red_mask) << shift.red) >> 16; |
1743 | 5.88k | if (quantum_bits.red <= 8) |
1744 | 5.65k | red|=(red >> 8); |
1745 | 5.88k | green=((pixel & bmp_info.green_mask) << shift.green) >> 16; |
1746 | 5.88k | if (quantum_bits.green <= 8) |
1747 | 5.64k | green|=(green >> 8); |
1748 | 5.88k | blue=((pixel & bmp_info.blue_mask) << shift.blue) >> 16; |
1749 | 5.88k | if (quantum_bits.blue <= 8) |
1750 | 5.62k | blue|=(blue >> 8); |
1751 | 5.88k | if (image->matte != MagickFalse) |
1752 | 5.39k | { |
1753 | 5.39k | opacity=((pixel & bmp_info.alpha_mask) << shift.opacity) >> 16; |
1754 | | /* if(opacity!=0) ZeroOpacity=0; */ |
1755 | 5.39k | if (quantum_bits.opacity <= 8) |
1756 | 5.18k | opacity|=(opacity >> 8); |
1757 | 5.39k | q->opacity=MaxRGB-ScaleShortToQuantum(opacity); |
1758 | 5.39k | } |
1759 | 488 | else |
1760 | 488 | { |
1761 | 488 | q->opacity = OpaqueOpacity; |
1762 | 488 | } |
1763 | 5.88k | q->red=ScaleShortToQuantum(red); |
1764 | 5.88k | q->green=ScaleShortToQuantum(green); |
1765 | 5.88k | q->blue=ScaleShortToQuantum(blue); |
1766 | 5.88k | q++; |
1767 | 5.88k | } |
1768 | 5.46k | if (!SyncImagePixels(image)) |
1769 | 0 | break; |
1770 | 5.46k | if (image->previous == (Image *) NULL) |
1771 | 5.46k | if (QuantumTick(y,image->rows)) |
1772 | 2.65k | { |
1773 | 2.65k | status=MagickMonitorFormatted((size_t)image->rows-y-1,image->rows, |
1774 | 2.65k | exception,LoadImageText, |
1775 | 2.65k | image->filename, |
1776 | 2.65k | image->columns,image->rows); |
1777 | 2.65k | if (status == MagickFalse) |
1778 | 0 | break; |
1779 | 2.65k | } |
1780 | 5.46k | } |
1781 | | /* if(ZeroOpacity) image->matte = MagickFalse; */ |
1782 | 191 | break; |
1783 | 191 | } |
1784 | | |
1785 | 97 | case 48: |
1786 | 97 | { |
1787 | 97 | magick_uint16_t val_16; |
1788 | | /* |
1789 | | Convert DirectColor scanline. |
1790 | | */ |
1791 | 5.12k | for(y=(long) image->rows-1; y >= 0; y--) |
1792 | 5.03k | { |
1793 | 5.03k | p = pixels+((size_t) image->rows-y-1)*bytes_per_line; |
1794 | 5.03k | q = SetImagePixels(image,0,y,image->columns,1); |
1795 | 5.03k | if(q == (PixelPacket *) NULL) |
1796 | 0 | break; |
1797 | 11.6k | for(x=0; x < (long) image->columns; x++) |
1798 | 6.57k | { |
1799 | 6.57k | LD_UINT16_LSB(val_16,p); |
1800 | 6.57k | q->blue = MS_VAL16_TO_QUANTUM(val_16); |
1801 | 6.57k | LD_UINT16_LSB(val_16,p); |
1802 | 6.57k | q->green = MS_VAL16_TO_QUANTUM(val_16); |
1803 | 6.57k | LD_UINT16_LSB(val_16,p); |
1804 | 6.57k | q->red = MS_VAL16_TO_QUANTUM(val_16); |
1805 | 6.57k | q->opacity = OpaqueOpacity; |
1806 | 6.57k | q++; |
1807 | 6.57k | } |
1808 | 5.03k | if(!SyncImagePixels(image)) |
1809 | 0 | break; |
1810 | 5.03k | if(image->previous == (Image *) NULL) |
1811 | 5.03k | if(QuantumTick(y,image->rows)) |
1812 | 2.29k | { |
1813 | 2.29k | status=MagickMonitorFormatted((size_t)image->rows-y-1,image->rows, |
1814 | 2.29k | exception,LoadImageText, |
1815 | 2.29k | image->filename, |
1816 | 2.29k | image->columns,image->rows); |
1817 | 2.29k | if(status == MagickFalse) |
1818 | 0 | break; |
1819 | 2.29k | } |
1820 | 5.03k | } |
1821 | 97 | break; |
1822 | 191 | } |
1823 | | |
1824 | 101 | case 64: |
1825 | 101 | { |
1826 | 101 | magick_uint16_t val_16; |
1827 | | /* |
1828 | | Convert DirectColor scanline. |
1829 | | */ |
1830 | 5.87k | for(y=(long) image->rows-1; y >= 0; y--) |
1831 | 5.77k | { |
1832 | 5.77k | p = pixels+((size_t) image->rows-y-1)*bytes_per_line; |
1833 | 5.77k | q = SetImagePixels(image,0,y,image->columns,1); |
1834 | 5.77k | if(q == (PixelPacket *) NULL) |
1835 | 0 | break; |
1836 | 17.8k | for(x=0; x < (long) image->columns; x++) |
1837 | 12.0k | { |
1838 | 12.0k | LD_UINT16_LSB(val_16,p); |
1839 | 12.0k | q->blue = MS_VAL16_TO_QUANTUM(val_16); |
1840 | 12.0k | LD_UINT16_LSB(val_16,p); |
1841 | 12.0k | q->green = MS_VAL16_TO_QUANTUM(val_16); |
1842 | 12.0k | LD_UINT16_LSB(val_16,p); |
1843 | 12.0k | q->red = MS_VAL16_TO_QUANTUM(val_16); |
1844 | | /* FIXME: support alpha */ |
1845 | 12.0k | q->opacity = OpaqueOpacity; |
1846 | 12.0k | p+=2; |
1847 | 12.0k | q++; |
1848 | 12.0k | } |
1849 | 5.77k | if(!SyncImagePixels(image)) |
1850 | 0 | break; |
1851 | 5.77k | if(image->previous == (Image *) NULL) |
1852 | 5.77k | if(QuantumTick(y,image->rows)) |
1853 | 2.35k | { |
1854 | 2.35k | status=MagickMonitorFormatted((size_t) image->rows-y-1,image->rows, |
1855 | 2.35k | exception,LoadImageText, |
1856 | 2.35k | image->filename, |
1857 | 2.35k | image->columns,image->rows); |
1858 | 2.35k | if(status == MagickFalse) |
1859 | 0 | break; |
1860 | 2.35k | } |
1861 | 5.77k | } |
1862 | 101 | break; |
1863 | 191 | } |
1864 | 0 | default: |
1865 | 0 | ThrowBMPReaderException(CorruptImageError,ImproperImageHeader,image); |
1866 | 1.02k | } |
1867 | 1.02k | MagickFreeResourceLimitedMemory(pixels); |
1868 | 1.02k | if (EOFBlob(image)) |
1869 | 24 | { |
1870 | 24 | ThrowException(exception,CorruptImageError,UnexpectedEndOfFile, |
1871 | 24 | image->filename); |
1872 | 24 | break; |
1873 | 24 | } |
1874 | 997 | if (bmp_info.height < 0) |
1875 | 244 | { |
1876 | 244 | Image |
1877 | 244 | *flipped_image; |
1878 | | |
1879 | | /* |
1880 | | Correct image orientation. |
1881 | | */ |
1882 | 244 | flipped_image=FlipImage(image,exception); |
1883 | 244 | if (flipped_image == (Image *) NULL) |
1884 | 0 | { |
1885 | 0 | DestroyImageList(image); |
1886 | 0 | return((Image *) NULL); |
1887 | 0 | } |
1888 | 244 | DestroyBlob(flipped_image); |
1889 | 244 | flipped_image->blob=ReferenceBlob(image->blob); |
1890 | 244 | ReplaceImageInList(&image,flipped_image); |
1891 | 244 | } |
1892 | 997 | StopTimer(&image->timer); |
1893 | | /* |
1894 | | Proceed to next image. |
1895 | | */ |
1896 | 997 | if (image_info->subrange != 0) |
1897 | 997 | if (image->scene >= (image_info->subimage+image_info->subrange-1)) |
1898 | 997 | break; |
1899 | 0 | *magick='\0'; |
1900 | 0 | file_remaining=file_size-TellBlob(image); |
1901 | 0 | if (file_remaining == 0) |
1902 | 0 | break; |
1903 | 0 | offset=bmp_info.ba_offset; |
1904 | 0 | if (logging) |
1905 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1906 | 0 | "Seek offset %" MAGICK_OFF_F "d", |
1907 | 0 | (magick_off_t) offset); |
1908 | 0 | if (offset > 0) |
1909 | 0 | if ((offset < TellBlob(image)) || |
1910 | 0 | (SeekBlob(image,offset,SEEK_SET) != (magick_off_t) offset)) |
1911 | 0 | ThrowBMPReaderException(CorruptImageError,ImproperImageHeader,image); |
1912 | 0 | if (ReadBlob(image,2,(char *) magick) != (size_t) 2) |
1913 | 0 | break; |
1914 | 0 | if (IsBMP(magick,2)) |
1915 | 0 | { |
1916 | | /* |
1917 | | Acquire next image structure. |
1918 | | */ |
1919 | 0 | AllocateNextImage(image_info,image); |
1920 | 0 | if (image->next == (Image *) NULL) |
1921 | 0 | { |
1922 | 0 | DestroyImageList(image); |
1923 | 0 | return((Image *) NULL); |
1924 | 0 | } |
1925 | 0 | image=SyncNextImageInList(image); |
1926 | 0 | status=MagickMonitorFormatted(TellBlob(image),GetBlobSize(image), |
1927 | 0 | exception,LoadImagesText, |
1928 | 0 | image->filename); |
1929 | 0 | if (status == MagickFalse) |
1930 | 0 | break; |
1931 | 0 | } |
1932 | 0 | } while (IsBMP(magick,2)); |
1933 | 2.28k | ExitLoop: |
1934 | | |
1935 | 2.28k | { |
1936 | 2.28k | Image *p; |
1937 | 2.28k | long scene=0; |
1938 | | |
1939 | | /* |
1940 | | Rewind list, removing any empty images while rewinding. |
1941 | | */ |
1942 | 2.28k | p=image; |
1943 | 2.28k | image=NULL; |
1944 | 4.57k | while (p != (Image *)NULL) |
1945 | 2.28k | { |
1946 | 2.28k | Image *tmp=p; |
1947 | 2.28k | if ((p->rows == 0) || (p->columns == 0)) { |
1948 | 0 | p=p->previous; |
1949 | 0 | DeleteImageFromList(&tmp); |
1950 | 2.28k | } else { |
1951 | 2.28k | image=p; |
1952 | 2.28k | p=p->previous; |
1953 | 2.28k | } |
1954 | 2.28k | } |
1955 | | |
1956 | | /* |
1957 | | Fix scene numbers |
1958 | | */ |
1959 | 4.57k | for (p=image; p != (Image *) NULL; p=p->next) |
1960 | 2.28k | p->scene=scene++; |
1961 | 2.28k | } |
1962 | | |
1963 | | /* |
1964 | | while (image->previous != (Image *) NULL) |
1965 | | image=image->previous; |
1966 | | */ |
1967 | 2.28k | CloseBlob(image); |
1968 | | #if 0 |
1969 | | if (logging) |
1970 | | { |
1971 | | const size_t list_length = GetImageListLength(image); |
1972 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1973 | | "%lu image%s in list", list_length, list_length > 1 ? "s" : ""); |
1974 | | } |
1975 | | #endif |
1976 | | |
1977 | 2.28k | if (logging) |
1978 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"return"); |
1979 | 2.28k | return(image); |
1980 | 14.0k | } |
1981 | | |
1982 | | /* |
1983 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1984 | | % % |
1985 | | % % |
1986 | | % % |
1987 | | % R e g i s t e r B M P I m a g e % |
1988 | | % % |
1989 | | % % |
1990 | | % % |
1991 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1992 | | % |
1993 | | % Method RegisterBMPImage adds attributes for the BMP image format to |
1994 | | % the list of supported formats. The attributes include the image format |
1995 | | % tag, a method to read and/or write the format, whether the format |
1996 | | % supports the saving of more than one frame to the same file or blob, |
1997 | | % whether the format supports native in-memory I/O, and a brief |
1998 | | % description of the format. |
1999 | | % |
2000 | | % The format of the RegisterBMPImage method is: |
2001 | | % |
2002 | | % RegisterBMPImage(void) |
2003 | | % |
2004 | | */ |
2005 | | ModuleExport void RegisterBMPImage(void) |
2006 | 5 | { |
2007 | 5 | MagickInfo |
2008 | 5 | *entry; |
2009 | | |
2010 | 5 | entry=SetMagickInfo("BMP"); |
2011 | 5 | entry->decoder=(DecoderHandler) ReadBMPImage; |
2012 | 5 | entry->encoder=(EncoderHandler) WriteBMPImage; |
2013 | 5 | entry->magick=(MagickHandler) IsBMP; |
2014 | 5 | entry->description="Microsoft Windows bitmap image"; |
2015 | 5 | entry->module="BMP"; |
2016 | 5 | entry->adjoin=MagickFalse; |
2017 | 5 | entry->seekable_stream=MagickTrue; |
2018 | 5 | entry->coder_class=PrimaryCoderClass; |
2019 | 5 | (void) RegisterMagickInfo(entry); |
2020 | | |
2021 | 5 | entry=SetMagickInfo("BMP2"); |
2022 | 5 | entry->encoder=(EncoderHandler) WriteBMPImage; |
2023 | 5 | entry->magick=(MagickHandler) IsBMP; |
2024 | 5 | entry->description="Microsoft Windows bitmap image v2"; |
2025 | 5 | entry->module="BMP"; |
2026 | 5 | entry->adjoin=MagickFalse; |
2027 | 5 | entry->coder_class=PrimaryCoderClass; |
2028 | 5 | entry->seekable_stream=MagickTrue; |
2029 | 5 | (void) RegisterMagickInfo(entry); |
2030 | | |
2031 | 5 | entry=SetMagickInfo("BMP3"); |
2032 | 5 | entry->encoder=(EncoderHandler) WriteBMPImage; |
2033 | 5 | entry->magick=(MagickHandler) IsBMP; |
2034 | 5 | entry->description="Microsoft Windows bitmap image v3"; |
2035 | 5 | entry->module="BMP"; |
2036 | 5 | entry->adjoin=MagickFalse; |
2037 | 5 | entry->seekable_stream=MagickTrue; |
2038 | 5 | entry->coder_class=PrimaryCoderClass; |
2039 | 5 | (void) RegisterMagickInfo(entry); |
2040 | 5 | } |
2041 | | |
2042 | | /* |
2043 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2044 | | % % |
2045 | | % % |
2046 | | % % |
2047 | | % U n r e g i s t e r B M P I m a g e % |
2048 | | % % |
2049 | | % % |
2050 | | % % |
2051 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2052 | | % |
2053 | | % Method UnregisterBMPImage removes format registrations made by the |
2054 | | % BMP module from the list of supported formats. |
2055 | | % |
2056 | | % The format of the UnregisterBMPImage method is: |
2057 | | % |
2058 | | % UnregisterBMPImage(void) |
2059 | | % |
2060 | | */ |
2061 | | ModuleExport void UnregisterBMPImage(void) |
2062 | 0 | { |
2063 | 0 | (void) UnregisterMagickInfo("BMP"); |
2064 | 0 | (void) UnregisterMagickInfo("BMP2"); |
2065 | 0 | (void) UnregisterMagickInfo("BMP3"); |
2066 | 0 | } |
2067 | | |
2068 | | |
2069 | | typedef struct |
2070 | | { |
2071 | | const char FormatName[5]; |
2072 | | const char FormatNameDDot[6]; |
2073 | | const char Desc[24]; |
2074 | | } TForeignFormatDesc; |
2075 | | |
2076 | | static const TForeignFormatDesc StoreDescJPG = {"JPEG", "JPEG:", " Creating jpeg_image."}; |
2077 | | static const TForeignFormatDesc StoreDescPNG = {"PNG", "PNG:", " Creating png_image."}; |
2078 | | |
2079 | | |
2080 | | static int StoreAlienBlob(Image * image, const ImageInfo * image_info, const TForeignFormatDesc *pFormatDesc) |
2081 | 0 | { |
2082 | 0 | ImageInfo *jpeg_image_info; |
2083 | 0 | Image *jpeg_image; |
2084 | 0 | void *Data; |
2085 | 0 | size_t DataSize = 0; |
2086 | |
|
2087 | 0 | jpeg_image_info = (ImageInfo *)CloneImageInfo(image_info); |
2088 | 0 | if(jpeg_image_info == (ImageInfo *) NULL) |
2089 | 0 | ThrowWriterException(ResourceLimitError,MemoryAllocationFailed, image); |
2090 | 0 | if(image->logging) |
2091 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), "%s", pFormatDesc->Desc); |
2092 | |
|
2093 | 0 | jpeg_image = CloneImage(image,0,0,MagickTrue,&image->exception); |
2094 | |
|
2095 | 0 | (void)strlcpy(jpeg_image_info->magick, pFormatDesc->FormatName, MaxTextExtent); |
2096 | 0 | (void)strlcpy(jpeg_image_info->filename,"JPEG:",MaxTextExtent); |
2097 | 0 | (void)strlcpy(jpeg_image->magick, pFormatDesc->FormatName, MaxTextExtent); |
2098 | 0 | (void)strlcpy(jpeg_image->filename, pFormatDesc->FormatNameDDot, MaxTextExtent); |
2099 | |
|
2100 | 0 | Data = ImageToBlob(jpeg_image_info,jpeg_image,&DataSize,&image->exception); |
2101 | 0 | if(Data!=NULL) |
2102 | 0 | { |
2103 | 0 | WriteBlob(image,DataSize,Data); |
2104 | 0 | MagickFreeMemory(Data); |
2105 | 0 | } |
2106 | | |
2107 | | /* Destroy JPEG image and image_info */ |
2108 | 0 | DestroyImage(jpeg_image); |
2109 | 0 | DestroyImageInfo(jpeg_image_info); |
2110 | |
|
2111 | 0 | return 0; |
2112 | 0 | } |
2113 | | |
2114 | | |
2115 | | /* |
2116 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2117 | | % % |
2118 | | % % |
2119 | | % % |
2120 | | % W r i t e B M P I m a g e % |
2121 | | % % |
2122 | | % % |
2123 | | % % |
2124 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2125 | | % |
2126 | | % Method WriteBMPImage writes an image in Microsoft Windows bitmap encoded |
2127 | | % image format, version 3 for Windows or (if the image has a matte channel) |
2128 | | % version 4. |
2129 | | % |
2130 | | % The format of the WriteBMPImage method is: |
2131 | | % |
2132 | | % unsigned int WriteBMPImage(const ImageInfo *image_info,Image *image) |
2133 | | % |
2134 | | % A description of each parameter follows. |
2135 | | % |
2136 | | % o status: Method WriteBMPImage return MagickTrue if the image is written. |
2137 | | % MagickFalse is returned is there is a memory shortage or if the image file |
2138 | | % fails to write. |
2139 | | % |
2140 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
2141 | | % |
2142 | | % o image: A pointer to an Image structure. |
2143 | | % |
2144 | | % |
2145 | | */ |
2146 | | static unsigned int WriteBMPImage(const ImageInfo *image_info,Image *image) |
2147 | 870 | { |
2148 | 870 | BMPInfo |
2149 | 870 | bmp_info; |
2150 | | |
2151 | 870 | int |
2152 | 870 | logging; |
2153 | | |
2154 | 870 | unsigned long |
2155 | 870 | y; |
2156 | | |
2157 | 870 | register const PixelPacket |
2158 | 870 | *p; |
2159 | | |
2160 | 870 | register unsigned long |
2161 | 870 | i, |
2162 | 870 | x; |
2163 | | |
2164 | 870 | register unsigned char |
2165 | 870 | *q; |
2166 | | |
2167 | 870 | unsigned char |
2168 | 870 | *bmp_data, |
2169 | 870 | *pixels; |
2170 | | |
2171 | 870 | size_t |
2172 | 870 | bytes_per_line, |
2173 | 870 | image_size; |
2174 | | |
2175 | 870 | MagickBool |
2176 | 870 | adjoin, |
2177 | 870 | have_color_info; |
2178 | | |
2179 | 870 | MagickPassFail |
2180 | 870 | status; |
2181 | | |
2182 | 870 | unsigned long |
2183 | 870 | scene, |
2184 | 870 | type; |
2185 | | |
2186 | | /* const unsigned char */ |
2187 | | /* *color_profile=0; */ |
2188 | | |
2189 | 870 | size_t |
2190 | 870 | color_profile_length=0; |
2191 | | |
2192 | 870 | size_t |
2193 | 870 | image_list_length; |
2194 | | |
2195 | | /* |
2196 | | Open output image file. |
2197 | | */ |
2198 | 870 | assert(image_info != (const ImageInfo *) NULL); |
2199 | 870 | assert(image_info->signature == MagickSignature); |
2200 | 870 | assert(image != (Image *) NULL); |
2201 | 870 | assert(image->signature == MagickSignature); |
2202 | 870 | image_list_length=GetImageListLength(image); |
2203 | 870 | logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter"); |
2204 | 870 | if (logging) |
2205 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2206 | 0 | "%" MAGICK_SIZE_T_F "u image frames in list", |
2207 | 0 | (MAGICK_SIZE_T) image_list_length); |
2208 | 870 | status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception); |
2209 | 870 | if (status == MagickFail) |
2210 | 870 | ThrowWriterException(FileOpenError,UnableToOpenFile,image); |
2211 | 870 | type=4; |
2212 | 870 | if (LocaleCompare(image_info->magick,"BMP2") == 0) |
2213 | 0 | type=2; |
2214 | 870 | else |
2215 | 870 | if (LocaleCompare(image_info->magick,"BMP3") == 0) |
2216 | 0 | type=3; |
2217 | 870 | scene=0; |
2218 | 870 | adjoin=image_info->adjoin; |
2219 | | |
2220 | | /* |
2221 | | Retrieve color profile from Image (if any) |
2222 | | FIXME: is color profile support writing not properly implemented? |
2223 | | */ |
2224 | | /* color_profile= */ (void) GetImageProfile(image,"ICM",&color_profile_length); |
2225 | | |
2226 | 870 | do |
2227 | 870 | { |
2228 | | /* |
2229 | | Initialize BMP raster file header. |
2230 | | */ |
2231 | 870 | if (logging) |
2232 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2233 | 0 | "Original: Scene %lu, storage_class %s, colors %u", |
2234 | 0 | scene, |
2235 | 0 | ClassTypeToString(image->storage_class), |
2236 | 0 | image->colors); |
2237 | 870 | (void) TransformColorspace(image,RGBColorspace); |
2238 | 870 | (void) memset(&bmp_info,0,sizeof(BMPInfo)); |
2239 | 870 | bmp_info.file_size=14+12; |
2240 | 870 | if (type > 2) |
2241 | 870 | bmp_info.file_size+=28; |
2242 | 870 | bmp_info.offset_bits=(magick_uint32_t) bmp_info.file_size; |
2243 | 870 | bmp_info.compression=BI_RGB; |
2244 | 870 | if ((image->storage_class != DirectClass) && (image->colors > 256)) |
2245 | 0 | (void) SetImageType(image,TrueColorType); |
2246 | | |
2247 | 870 | if(type>2 && AccessDefinition(image_info,"bmp","allow-jpeg")) |
2248 | 0 | { |
2249 | 0 | image->compression = JPEGCompression; |
2250 | 0 | bmp_info.number_colors = 0; |
2251 | 0 | bmp_info.bits_per_pixel = 0; |
2252 | 0 | bmp_info.compression = BI_JPEG; |
2253 | 0 | } |
2254 | 870 | else if(type>2 && AccessDefinition(image_info,"bmp","allow-png")) |
2255 | 0 | { |
2256 | 0 | image->compression = ZipCompression; |
2257 | 0 | bmp_info.number_colors = 0; |
2258 | 0 | bmp_info.bits_per_pixel = 0; |
2259 | 0 | bmp_info.compression = BI_PNG; |
2260 | 0 | } |
2261 | 870 | else |
2262 | 870 | { |
2263 | 870 | if(image->storage_class != DirectClass) |
2264 | 325 | { |
2265 | | /* |
2266 | | Colormapped BMP raster. |
2267 | | */ |
2268 | 325 | bmp_info.bits_per_pixel=8; |
2269 | 325 | if (image->colors <= 2) |
2270 | 123 | bmp_info.bits_per_pixel=1; |
2271 | 202 | else if (image->colors <= 16) |
2272 | 102 | bmp_info.bits_per_pixel=4; |
2273 | 100 | else if (image->colors <= 256) |
2274 | 100 | bmp_info.bits_per_pixel=8; |
2275 | 325 | bmp_info.number_colors=1 << bmp_info.bits_per_pixel; |
2276 | 325 | if (image->matte) |
2277 | 14 | (void) SetImageType(image,TrueColorMatteType); |
2278 | 311 | else |
2279 | 311 | if (bmp_info.number_colors < image->colors) |
2280 | 0 | (void) SetImageType(image,TrueColorType); |
2281 | 311 | else |
2282 | 311 | { |
2283 | 311 | bmp_info.file_size+=3U*(((size_t)1U) << bmp_info.bits_per_pixel); |
2284 | 311 | bmp_info.offset_bits+=3U*(1U << bmp_info.bits_per_pixel); |
2285 | 311 | if (type > 2) |
2286 | 311 | { |
2287 | 311 | bmp_info.file_size+=((size_t)1U << bmp_info.bits_per_pixel); |
2288 | 311 | bmp_info.offset_bits+=(1U << bmp_info.bits_per_pixel); |
2289 | 311 | } |
2290 | 311 | } |
2291 | 325 | } |
2292 | | /* Note: Image class could be changed in a code above. */ |
2293 | 870 | if (image->storage_class==DirectClass) |
2294 | 559 | { |
2295 | | /* |
2296 | | Full color BMP raster. |
2297 | | */ |
2298 | 559 | bmp_info.number_colors=0; |
2299 | 559 | bmp_info.bits_per_pixel=((type > 3) && image->matte) ? 32 : 24; |
2300 | 559 | bmp_info.compression= |
2301 | 559 | (type > 3) && image->matte ? BI_BITFIELDS : BI_RGB; |
2302 | 559 | } |
2303 | 870 | } |
2304 | | |
2305 | 870 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2306 | 870 | "Final: Scene %lu, storage_class %s, colors %u", |
2307 | 870 | scene, |
2308 | 870 | ClassTypeToString(image->storage_class), |
2309 | 870 | image->colors); |
2310 | 870 | if(bmp_info.compression==BI_JPEG || bmp_info.compression==BI_PNG) |
2311 | 0 | { |
2312 | 0 | bytes_per_line = 0; |
2313 | 0 | image_size = 0; |
2314 | 0 | have_color_info = 0; |
2315 | 0 | } |
2316 | 870 | else |
2317 | 870 | { |
2318 | | /* |
2319 | | Below emulates: |
2320 | | bytes_per_line=4*((image->columns*bmp_info.bits_per_pixel+31)/32); |
2321 | | */ |
2322 | 870 | bytes_per_line=MagickArraySize(image->columns,bmp_info.bits_per_pixel); |
2323 | 870 | if ((bytes_per_line > 0) && (~((size_t) 0) - bytes_per_line) > 31) |
2324 | 870 | bytes_per_line = MagickArraySize(4,(bytes_per_line+31)/32); |
2325 | 870 | if (bytes_per_line==0) |
2326 | 870 | ThrowWriterException(CoderError,ArithmeticOverflow,image); |
2327 | 870 | image_size=MagickArraySize(bytes_per_line,image->rows); |
2328 | 870 | if ((image_size == 0) || ((image_size & 0xffffffff) != image_size)) |
2329 | 870 | ThrowWriterException(CoderError,ArithmeticOverflow,image); |
2330 | 870 | have_color_info=(int) ((image->rendering_intent != UndefinedIntent) || |
2331 | 870 | (color_profile_length != 0) || (image->gamma != 0.0)); |
2332 | 870 | } |
2333 | 870 | bmp_info.ba_offset=0; |
2334 | 870 | if (type == 2) |
2335 | 0 | bmp_info.size=12; |
2336 | 870 | else |
2337 | 870 | if ((type == 3) || (!image->matte && !have_color_info)) |
2338 | 743 | { |
2339 | 743 | type=3; |
2340 | 743 | bmp_info.size=40; |
2341 | 743 | } |
2342 | 127 | else |
2343 | 127 | { |
2344 | 127 | int |
2345 | 127 | extra_size; |
2346 | | |
2347 | 127 | bmp_info.size=108; |
2348 | 127 | extra_size=68; |
2349 | 127 | if ((image->rendering_intent != UndefinedIntent) || |
2350 | 127 | (color_profile_length != 0)) |
2351 | 4 | { |
2352 | 4 | bmp_info.size=124; |
2353 | 4 | extra_size+=16; |
2354 | 4 | } |
2355 | 127 | bmp_info.file_size+=extra_size; |
2356 | 127 | bmp_info.offset_bits+=extra_size; |
2357 | 127 | } |
2358 | | /* |
2359 | | Verify and enforce that image dimensions do not exceed limit |
2360 | | imposed by file format. |
2361 | | */ |
2362 | 870 | if (type == 2) |
2363 | 0 | { |
2364 | 0 | bmp_info.width=(magick_int16_t) image->columns; |
2365 | 0 | bmp_info.height=(magick_int16_t) image->rows; |
2366 | 0 | } |
2367 | 870 | else |
2368 | 870 | { |
2369 | 870 | bmp_info.width=(magick_int32_t) image->columns; |
2370 | 870 | bmp_info.height=(magick_int32_t) image->rows; |
2371 | 870 | } |
2372 | 870 | if (((unsigned long) bmp_info.width != image->columns) || |
2373 | 870 | ((unsigned long) bmp_info.height != image->rows)) |
2374 | 0 | { |
2375 | 0 | ThrowWriterException(CoderError,ImageColumnOrRowSizeIsNotSupported,image); |
2376 | 0 | } |
2377 | | |
2378 | 870 | bmp_info.planes=1; |
2379 | 870 | bmp_info.image_size=image_size; |
2380 | 870 | bmp_info.file_size+=bmp_info.image_size; |
2381 | 870 | bmp_info.x_pixels=75*39; |
2382 | 870 | bmp_info.y_pixels=75*39; |
2383 | 870 | if (image->units == PixelsPerInchResolution) |
2384 | 0 | { |
2385 | 0 | bmp_info.x_pixels=(unsigned long) (100.0*image->x_resolution/2.54); |
2386 | 0 | bmp_info.y_pixels=(unsigned long) (100.0*image->y_resolution/2.54); |
2387 | 0 | } |
2388 | 870 | if (image->units == PixelsPerCentimeterResolution) |
2389 | 822 | { |
2390 | 822 | bmp_info.x_pixels=(unsigned long) (100.0*image->x_resolution); |
2391 | 822 | bmp_info.y_pixels=(unsigned long) (100.0*image->y_resolution); |
2392 | 822 | } |
2393 | 870 | bmp_info.colors_important=bmp_info.number_colors; |
2394 | | /* |
2395 | | Convert MIFF to BMP raster pixels. |
2396 | | */ |
2397 | 870 | if(bmp_info.compression==BI_JPEG || bmp_info.compression==BI_PNG) |
2398 | 0 | pixels=NULL; |
2399 | 870 | else |
2400 | 870 | { |
2401 | 870 | pixels=MagickAllocateResourceLimitedMemory(unsigned char *,bmp_info.image_size); |
2402 | 870 | if (pixels == (unsigned char *) NULL) |
2403 | 870 | ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,image); |
2404 | 870 | } |
2405 | 870 | switch (bmp_info.bits_per_pixel) |
2406 | 870 | { |
2407 | 120 | case 1: |
2408 | 120 | { |
2409 | 120 | ExportPixelAreaOptions |
2410 | 120 | export_options; |
2411 | | |
2412 | | /* |
2413 | | Convert PseudoClass image to a BMP monochrome image. |
2414 | | */ |
2415 | 120 | if (logging) |
2416 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2417 | 0 | " Output %u-bit PseudoClass pixels", |
2418 | 0 | bmp_info.bits_per_pixel); |
2419 | 120 | ExportPixelAreaOptionsInit(&export_options); |
2420 | 120 | export_options.pad_bytes=(unsigned long) (bytes_per_line - (((size_t) image->columns+7)/8)); |
2421 | 120 | export_options.pad_value=0x00; |
2422 | 18.3k | for (y=0; y < image->rows; y++) |
2423 | 18.2k | { |
2424 | 18.2k | p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); |
2425 | 18.2k | if (p == (const PixelPacket *) NULL) |
2426 | 0 | break; |
2427 | 18.2k | q=pixels+((size_t) image->rows-y-1)*bytes_per_line; |
2428 | 18.2k | if (ExportImagePixelArea(image,IndexQuantum,1,q,&export_options,0) |
2429 | 18.2k | == MagickFail) |
2430 | 0 | { |
2431 | 0 | break; |
2432 | 0 | } |
2433 | 18.2k | if (image->previous == (Image *) NULL) |
2434 | 18.2k | if (QuantumTick(y,image->rows)) |
2435 | 6.09k | if (!MagickMonitorFormatted(y,image->rows,&image->exception, |
2436 | 6.09k | SaveImageText,image->filename, |
2437 | 6.09k | image->columns,image->rows)) |
2438 | 0 | break; |
2439 | 18.2k | } |
2440 | 120 | break; |
2441 | 0 | } |
2442 | 94 | case 4: |
2443 | 94 | { |
2444 | 94 | ExportPixelAreaOptions |
2445 | 94 | export_options; |
2446 | | |
2447 | | /* |
2448 | | Convert PseudoClass image to a BMP monochrome image. |
2449 | | */ |
2450 | 94 | if (logging) |
2451 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2452 | 0 | " Output %u-bit PseudoClass pixels", |
2453 | 0 | bmp_info.bits_per_pixel); |
2454 | 94 | ExportPixelAreaOptionsInit(&export_options); |
2455 | 94 | export_options.pad_bytes=(unsigned long) (bytes_per_line - ((image->columns+1)/2)); |
2456 | 94 | export_options.pad_value=0x00; |
2457 | 26.6k | for (y=0; y < image->rows; y++) |
2458 | 26.6k | { |
2459 | 26.6k | p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); |
2460 | 26.6k | if (p == (const PixelPacket *) NULL) |
2461 | 0 | break; |
2462 | 26.6k | q=pixels+((size_t) image->rows-y-1)*bytes_per_line; |
2463 | 26.6k | if (ExportImagePixelArea(image,IndexQuantum,4,q,&export_options,0) |
2464 | 26.6k | == MagickFail) |
2465 | 0 | { |
2466 | 0 | break; |
2467 | 0 | } |
2468 | 26.6k | if (image->previous == (Image *) NULL) |
2469 | 26.6k | if (QuantumTick(y,image->rows)) |
2470 | 5.09k | if (!MagickMonitorFormatted(y,image->rows,&image->exception, |
2471 | 5.09k | SaveImageText,image->filename, |
2472 | 5.09k | image->columns,image->rows)) |
2473 | 0 | break; |
2474 | 26.6k | } |
2475 | 94 | break; |
2476 | 0 | } |
2477 | 97 | case 8: |
2478 | 97 | { |
2479 | 97 | ExportPixelAreaOptions |
2480 | 97 | export_options; |
2481 | | |
2482 | | /* |
2483 | | Convert PseudoClass packet to BMP pixel. |
2484 | | */ |
2485 | 97 | if (logging) |
2486 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2487 | 0 | " Output %u-bit PseudoClass pixels", |
2488 | 0 | bmp_info.bits_per_pixel); |
2489 | 97 | ExportPixelAreaOptionsInit(&export_options); |
2490 | 97 | export_options.pad_bytes=(unsigned long) (bytes_per_line - image->columns); |
2491 | 40.6k | for (y=0; y < image->rows; y++) |
2492 | 40.5k | { |
2493 | 40.5k | p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); |
2494 | 40.5k | if (p == (const PixelPacket *) NULL) |
2495 | 0 | break; |
2496 | 40.5k | q=pixels+((size_t) image->rows-y-1)*bytes_per_line; |
2497 | 40.5k | if (ExportImagePixelArea(image,IndexQuantum,8,q,&export_options,0) |
2498 | 40.5k | == MagickFail) |
2499 | 0 | { |
2500 | | /* Please note that pixels array has uninitialised elements when this fails. */ |
2501 | 0 | if(logging) |
2502 | 0 | (void)LogMagickEvent(CoderEvent,GetMagickModule(), |
2503 | 0 | " ExportImagePixelArea failed at row %lu", y); |
2504 | 0 | ThrowWriterException(CoderError,DataEncodingSchemeIsNotSupported,image); |
2505 | 0 | break; |
2506 | 0 | } |
2507 | 40.5k | if (image->previous == (Image *) NULL) |
2508 | 40.5k | if (QuantumTick(y,image->rows)) |
2509 | 8.38k | if (!MagickMonitorFormatted(y,image->rows,&image->exception, |
2510 | 8.38k | SaveImageText,image->filename, |
2511 | 8.38k | image->columns,image->rows)) |
2512 | 0 | break; |
2513 | 40.5k | } |
2514 | 97 | break; |
2515 | 97 | } |
2516 | 434 | case 24: |
2517 | 559 | case 32: |
2518 | 559 | { |
2519 | | /* |
2520 | | Convert DirectClass packet to BMP BGR888 or BGRA8888 pixel. |
2521 | | */ |
2522 | 559 | if (logging) |
2523 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2524 | 0 | " Output %u-bit DirectClass pixels", |
2525 | 0 | bmp_info.bits_per_pixel); |
2526 | 37.3k | for (y=0; y < image->rows; y++) |
2527 | 36.8k | { |
2528 | 36.8k | p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); |
2529 | 36.8k | if (p == (const PixelPacket *) NULL) |
2530 | 0 | break; |
2531 | 36.8k | q=pixels+((size_t) image->rows-y-1)*bytes_per_line; |
2532 | 615k | for (x=0; x < image->columns; x++) |
2533 | 578k | { |
2534 | 578k | *q++=ScaleQuantumToChar(p->blue); |
2535 | 578k | *q++=ScaleQuantumToChar(p->green); |
2536 | 578k | *q++=ScaleQuantumToChar(p->red); |
2537 | 578k | if (bmp_info.bits_per_pixel == 32) |
2538 | 414k | *q++=ScaleQuantumToChar(MaxRGB-p->opacity); |
2539 | 578k | p++; |
2540 | 578k | } |
2541 | 36.8k | if (bmp_info.bits_per_pixel == 24) |
2542 | 22.1k | { |
2543 | | /* initialize padding bytes */ |
2544 | 46.8k | for (x=3*image->columns; x < bytes_per_line; x++) |
2545 | 24.7k | *q++=0x00; |
2546 | 22.1k | } |
2547 | 36.8k | if (image->previous == (Image *) NULL) |
2548 | 36.8k | if (QuantumTick(y,image->rows)) |
2549 | 13.6k | if (!MagickMonitorFormatted(y,image->rows,&image->exception, |
2550 | 13.6k | SaveImageText,image->filename, |
2551 | 13.6k | image->columns,image->rows)) |
2552 | 0 | break; |
2553 | 36.8k | } |
2554 | 559 | break; |
2555 | 434 | } |
2556 | 0 | default: |
2557 | 0 | { |
2558 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2559 | 0 | "Unsupported bits-per-pixel %u!", |
2560 | 0 | bmp_info.bits_per_pixel); |
2561 | |
|
2562 | 0 | break; |
2563 | 434 | } |
2564 | 870 | } |
2565 | 870 | if ((type > 2) && (bmp_info.bits_per_pixel == 8)) |
2566 | 97 | if (image_info->compression != NoCompression) |
2567 | 97 | { |
2568 | 97 | size_t |
2569 | 97 | length; |
2570 | | |
2571 | | /* |
2572 | | Convert run-length encoded raster pixels. |
2573 | | */ |
2574 | 97 | length=2*(bytes_per_line+2)*((size_t)image->rows+2)+2; |
2575 | 97 | bmp_data=MagickAllocateResourceLimitedMemory(unsigned char *,length); |
2576 | 97 | if (bmp_data == (unsigned char *) NULL) |
2577 | 0 | { |
2578 | 0 | MagickFreeResourceLimitedMemory(pixels); |
2579 | 0 | ThrowWriterException(ResourceLimitError,MemoryAllocationFailed, |
2580 | 0 | image); |
2581 | 0 | } |
2582 | 97 | bmp_info.file_size-=bmp_info.image_size; |
2583 | 97 | bmp_info.image_size=EncodeImage(image,bytes_per_line,pixels, |
2584 | 97 | bmp_data); |
2585 | 97 | bmp_info.file_size+=bmp_info.image_size; |
2586 | 97 | MagickFreeResourceLimitedMemory(pixels); |
2587 | 97 | pixels=bmp_data; |
2588 | 97 | bmp_info.compression=BI_RLE8; |
2589 | 97 | } |
2590 | | /* |
2591 | | Write BMP for Windows, all versions, 14-byte header. |
2592 | | */ |
2593 | 870 | if (logging) |
2594 | 0 | { |
2595 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2596 | 0 | " Writing BMP version %ld datastream",type); |
2597 | 0 | if (image->storage_class == DirectClass) |
2598 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2599 | 0 | " Storage class=DirectClass"); |
2600 | 0 | else |
2601 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2602 | 0 | " Storage class=PseudoClass"); |
2603 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2604 | 0 | " Image depth=%u",image->depth); |
2605 | 0 | if (image->matte) |
2606 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2607 | 0 | " Matte=True"); |
2608 | 0 | else |
2609 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2610 | 0 | " Matte=False"); |
2611 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2612 | 0 | " BMP bits_per_pixel=%d",bmp_info.bits_per_pixel); |
2613 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2614 | 0 | " BMP file_size=%" MAGICK_SIZE_T_F "u bytes", |
2615 | 0 | (MAGICK_SIZE_T) bmp_info.file_size); |
2616 | 0 | if(bmp_info.compression <=BI_ALPHABITFIELDS) |
2617 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2618 | 0 | " Compression=%s", DecodeBiCompression(bmp_info.compression,40)); |
2619 | 0 | else |
2620 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2621 | 0 | " Compression=UNKNOWN (%u)",bmp_info.compression); |
2622 | 0 | if (bmp_info.number_colors == 0) |
2623 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2624 | 0 | " Number_colors=unspecified"); |
2625 | 0 | else |
2626 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2627 | 0 | " Number_colors=%u",bmp_info.number_colors); |
2628 | 0 | } |
2629 | 870 | (void) WriteBlob(image,2,"BM"); |
2630 | 870 | (void) WriteBlobLSBLong(image,(magick_uint32_t) bmp_info.file_size); |
2631 | 870 | (void) WriteBlobLSBLong(image,bmp_info.ba_offset); /* always 0 */ |
2632 | 870 | (void) WriteBlobLSBLong(image,bmp_info.offset_bits); |
2633 | 870 | if (type == 2) |
2634 | 0 | { |
2635 | | /* |
2636 | | Write 12-byte version 2 bitmap header. |
2637 | | */ |
2638 | 0 | (void) WriteBlobLSBLong(image,bmp_info.size); |
2639 | 0 | (void) WriteBlobLSBShort(image,bmp_info.width); |
2640 | 0 | (void) WriteBlobLSBShort(image,bmp_info.height); |
2641 | 0 | (void) WriteBlobLSBShort(image,bmp_info.planes); |
2642 | 0 | (void) WriteBlobLSBShort(image,bmp_info.bits_per_pixel); |
2643 | 0 | } |
2644 | 870 | else |
2645 | 870 | { |
2646 | | /* |
2647 | | Write 40-byte version 3+ bitmap header. |
2648 | | */ |
2649 | 870 | (void) WriteBlobLSBLong(image,bmp_info.size); |
2650 | 870 | (void) WriteBlobLSBLong(image,bmp_info.width); |
2651 | 870 | (void) WriteBlobLSBLong(image,bmp_info.height); |
2652 | 870 | (void) WriteBlobLSBShort(image,bmp_info.planes); |
2653 | 870 | (void) WriteBlobLSBShort(image,bmp_info.bits_per_pixel); |
2654 | 870 | (void) WriteBlobLSBLong(image,bmp_info.compression); |
2655 | 870 | (void) WriteBlobLSBLong(image,(magick_uint32_t) bmp_info.image_size); |
2656 | 870 | (void) WriteBlobLSBLong(image,bmp_info.x_pixels); |
2657 | 870 | (void) WriteBlobLSBLong(image,bmp_info.y_pixels); |
2658 | 870 | (void) WriteBlobLSBLong(image,bmp_info.number_colors); |
2659 | 870 | (void) WriteBlobLSBLong(image,bmp_info.colors_important); |
2660 | 870 | } |
2661 | 870 | if ((type > 3) && (image->matte || have_color_info)) |
2662 | 127 | { |
2663 | | /* |
2664 | | Write the rest of the 108-byte BMP Version 4 header. |
2665 | | */ |
2666 | 127 | (void) WriteBlobLSBLong(image,0x00ff0000L); /* Red mask */ |
2667 | 127 | (void) WriteBlobLSBLong(image,0x0000ff00L); /* Green mask */ |
2668 | 127 | (void) WriteBlobLSBLong(image,0x000000ffL); /* Blue mask */ |
2669 | 127 | (void) WriteBlobLSBLong(image,0xff000000UL); /* Alpha mask */ |
2670 | 127 | (void) WriteBlobLSBLong(image,0x00000001L); /* CSType==Calib. RGB */ |
2671 | 127 | (void) WriteBlobLSBLong(image, |
2672 | 127 | (magick_uint32_t) (image->chromaticity.red_primary.x*0x3ffffff)); |
2673 | 127 | (void) WriteBlobLSBLong(image, |
2674 | 127 | (magick_uint32_t) (image->chromaticity.red_primary.y*0x3ffffff)); |
2675 | 127 | (void) WriteBlobLSBLong(image, |
2676 | 127 | (magick_uint32_t) (1.000f-(image->chromaticity.red_primary.x |
2677 | 127 | +image->chromaticity.red_primary.y)*0x3ffffff)); |
2678 | 127 | (void) WriteBlobLSBLong(image, |
2679 | 127 | (magick_uint32_t) (image->chromaticity.green_primary.x*0x3ffffff)); |
2680 | 127 | (void) WriteBlobLSBLong(image, |
2681 | 127 | (magick_uint32_t) (image->chromaticity.green_primary.y*0x3ffffff)); |
2682 | 127 | (void) WriteBlobLSBLong(image, |
2683 | 127 | (magick_uint32_t) (1.000f-(image->chromaticity.green_primary.x |
2684 | 127 | +image->chromaticity.green_primary.y)*0x3ffffff)); |
2685 | 127 | (void) WriteBlobLSBLong(image, |
2686 | 127 | (magick_uint32_t) (image->chromaticity.blue_primary.x*0x3ffffff)); |
2687 | 127 | (void) WriteBlobLSBLong(image, |
2688 | 127 | (magick_uint32_t) (image->chromaticity.blue_primary.y*0x3ffffff)); |
2689 | 127 | (void) WriteBlobLSBLong(image, |
2690 | 127 | (magick_uint32_t) (1.000f-(image->chromaticity.blue_primary.x |
2691 | 127 | +image->chromaticity.blue_primary.y)*0x3ffffff)); |
2692 | | |
2693 | 127 | (void) WriteBlobLSBLong(image,(magick_uint32_t) (bmp_info.gamma_scale.x*0xffff)); |
2694 | 127 | (void) WriteBlobLSBLong(image,(magick_uint32_t) (bmp_info.gamma_scale.y*0xffff)); |
2695 | 127 | (void) WriteBlobLSBLong(image,(magick_uint32_t) (bmp_info.gamma_scale.z*0xffff)); |
2696 | 127 | if ((image->rendering_intent != UndefinedIntent) || |
2697 | 127 | (color_profile_length != 0)) |
2698 | 4 | { |
2699 | 4 | long |
2700 | 4 | intent; |
2701 | | |
2702 | 4 | switch ((int) image->rendering_intent) |
2703 | 4 | { |
2704 | 1 | case SaturationIntent: |
2705 | 1 | { |
2706 | 1 | intent=LCS_GM_BUSINESS; |
2707 | 1 | break; |
2708 | 0 | } |
2709 | 1 | case RelativeIntent: |
2710 | 1 | { |
2711 | 1 | intent=LCS_GM_GRAPHICS; |
2712 | 1 | break; |
2713 | 0 | } |
2714 | 1 | case PerceptualIntent: |
2715 | 1 | { |
2716 | 1 | intent=LCS_GM_IMAGES; |
2717 | 1 | break; |
2718 | 0 | } |
2719 | 1 | case AbsoluteIntent: |
2720 | 1 | { |
2721 | 1 | intent=LCS_GM_ABS_COLORIMETRIC; |
2722 | 1 | break; |
2723 | 0 | } |
2724 | 0 | default: |
2725 | 0 | { |
2726 | 0 | intent=0; |
2727 | 0 | break; |
2728 | 0 | } |
2729 | 4 | } |
2730 | 4 | (void) WriteBlobLSBLong(image,intent); |
2731 | 4 | (void) WriteBlobLSBLong(image,0x0); /* dummy profile data */ |
2732 | 4 | (void) WriteBlobLSBLong(image,0x0); /* dummy profile length */ |
2733 | 4 | (void) WriteBlobLSBLong(image,0x0); /* reserved */ |
2734 | 4 | } |
2735 | 127 | } |
2736 | 870 | if(pixels==NULL) |
2737 | 0 | { |
2738 | 0 | StoreAlienBlob(image,image_info, (bmp_info.compression==BI_JPEG)? &StoreDescJPG : &StoreDescPNG); |
2739 | 0 | } |
2740 | 870 | else |
2741 | 870 | { |
2742 | 870 | if (image->storage_class==PseudoClass) |
2743 | 311 | { |
2744 | 311 | unsigned char |
2745 | 311 | *bmp_colormap; |
2746 | | |
2747 | | /* |
2748 | | Dump colormap to file. |
2749 | | */ |
2750 | 311 | if (logging) |
2751 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2752 | 0 | " Colormap: %u entries",image->colors); |
2753 | 311 | bmp_colormap=MagickAllocateResourceLimitedArray(unsigned char *,4, |
2754 | 311 | ((size_t)1L << bmp_info.bits_per_pixel)); |
2755 | 311 | if (bmp_colormap == (unsigned char *) NULL) |
2756 | 0 | { |
2757 | 0 | MagickFreeResourceLimitedMemory(pixels); |
2758 | 0 | ThrowWriterException(ResourceLimitError,MemoryAllocationFailed, image); |
2759 | 0 | } |
2760 | 311 | q=bmp_colormap; |
2761 | 18.5k | for (i=0; i < Min(image->colors,bmp_info.number_colors); i++) |
2762 | 18.2k | { |
2763 | 18.2k | *q++=ScaleQuantumToChar(image->colormap[i].blue); |
2764 | 18.2k | *q++=ScaleQuantumToChar(image->colormap[i].green); |
2765 | 18.2k | *q++=ScaleQuantumToChar(image->colormap[i].red); |
2766 | 18.2k | if (type > 2) |
2767 | 18.2k | *q++=(Quantum) 0x0; |
2768 | 18.2k | } |
2769 | 8.64k | for ( ; i < (1UL << bmp_info.bits_per_pixel); i++) |
2770 | 8.33k | { |
2771 | 8.33k | *q++=(Quantum) 0x0; |
2772 | 8.33k | *q++=(Quantum) 0x0; |
2773 | 8.33k | *q++=(Quantum) 0x0; |
2774 | 8.33k | if (type > 2) |
2775 | 8.33k | *q++=(Quantum) 0x0; |
2776 | 8.33k | } |
2777 | 311 | if (type <= 2) |
2778 | 0 | (void) WriteBlob(image,3*((size_t)1UL << bmp_info.bits_per_pixel), |
2779 | 0 | (char *) bmp_colormap); |
2780 | 311 | else |
2781 | 311 | (void) WriteBlob(image,4*((size_t)1UL << bmp_info.bits_per_pixel), |
2782 | 311 | (char *) bmp_colormap); |
2783 | 311 | MagickFreeResourceLimitedMemory(bmp_colormap); |
2784 | 311 | } |
2785 | 870 | if (logging) |
2786 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2787 | 0 | " Pixels: %" MAGICK_SIZE_T_F "u bytes", |
2788 | 0 | (MAGICK_SIZE_T) bmp_info.image_size); |
2789 | 870 | (void) WriteBlob(image,bmp_info.image_size,(char *) pixels); |
2790 | 870 | MagickFreeResourceLimitedMemory(pixels); |
2791 | 870 | } |
2792 | 870 | if (image->next == (Image *) NULL) |
2793 | 870 | { |
2794 | 870 | if (logging) |
2795 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2796 | 0 | "No more image frames in list (scene=%lu)", |
2797 | 0 | scene); |
2798 | 870 | break; |
2799 | 870 | } |
2800 | 0 | image=SyncNextImageInList(image); |
2801 | 0 | status&=MagickMonitorFormatted(scene++,image_list_length, |
2802 | 0 | &image->exception,SaveImagesText, |
2803 | 0 | image->filename); |
2804 | 0 | if (status != MagickPass) |
2805 | 0 | break; |
2806 | 0 | if (logging) |
2807 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2808 | 0 | "At end of image adjoin loop (adjoin=%u, scene=%lu)", |
2809 | 0 | image_info->adjoin, scene); |
2810 | 0 | } while (adjoin); |
2811 | 870 | if (adjoin) |
2812 | 870 | while (image->previous != (Image *) NULL) |
2813 | 0 | image=image->previous; |
2814 | 870 | status &= CloseBlob(image); |
2815 | 870 | if (logging) |
2816 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"return"); |
2817 | 870 | return(status); |
2818 | 870 | } |