blob: 509738c5a7537f29383c33f4f4b49d6372bf5e83 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% BBBB M M PPPP %
7% B B MM MM P P %
8% BBBB M M M PPPP %
9% B B M M P %
10% BBBB M M P %
11% %
12% %
13% Read/Write Microsoft Windows Bitmap Image Format %
14% %
15% Software Design %
16% John Cristy %
17% Glenn Randers-Pehrson %
18% December 2001 %
19% %
20% %
cristy7e41fe82010-12-04 23:12:08 +000021% Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000022% dedicated to making software imaging solutions freely available. %
23% %
24% You may not use this file except in compliance with the License. You may %
25% obtain a copy of the License at %
26% %
27% http://www.imagemagick.org/script/license.php %
28% %
29% Unless required by applicable law or agreed to in writing, software %
30% distributed under the License is distributed on an "AS IS" BASIS, %
31% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32% See the License for the specific language governing permissions and %
33% limitations under the License. %
34% %
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
cristy4c08aed2011-07-01 19:47:50 +000043#include "MagickCore/studio.h"
44#include "MagickCore/blob.h"
45#include "MagickCore/blob-private.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/colormap-private.h"
48#include "MagickCore/color-private.h"
49#include "MagickCore/colormap.h"
50#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000051#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000052#include "MagickCore/exception.h"
53#include "MagickCore/exception-private.h"
54#include "MagickCore/image.h"
55#include "MagickCore/image-private.h"
56#include "MagickCore/list.h"
57#include "MagickCore/log.h"
58#include "MagickCore/magick.h"
59#include "MagickCore/memory_.h"
60#include "MagickCore/monitor.h"
61#include "MagickCore/monitor-private.h"
62#include "MagickCore/pixel-accessor.h"
63#include "MagickCore/profile.h"
64#include "MagickCore/quantum-private.h"
65#include "MagickCore/static.h"
66#include "MagickCore/string_.h"
67#include "MagickCore/module.h"
68#include "MagickCore/transform.h"
cristy3ed852e2009-09-05 21:47:34 +000069
70/*
71 Macro definitions (from Windows wingdi.h).
72*/
73#undef BI_JPEG
74#define BI_JPEG 4
75#undef BI_PNG
76#define BI_PNG 5
cristy0157aea2010-04-24 21:12:18 +000077#if !defined(MAGICKCORE_WINDOWS_SUPPORT) || defined(__MINGW32__)
cristy3ed852e2009-09-05 21:47:34 +000078#define BI_RGB 0
79#define BI_RLE8 1
80#define BI_RLE4 2
81#define BI_BITFIELDS 3
82
83#define LCS_CALIBRATED_RBG 0
84#define LCS_sRGB 1
85#define LCS_WINDOWS_COLOR_SPACE 2
86#define PROFILE_LINKED 3
87#define PROFILE_EMBEDDED 4
88
89#define LCS_GM_BUSINESS 1 /* Saturation */
90#define LCS_GM_GRAPHICS 2 /* Relative */
91#define LCS_GM_IMAGES 4 /* Perceptual */
92#define LCS_GM_ABS_COLORIMETRIC 8 /* Absolute */
93#endif
94
95/*
96 Typedef declarations.
97*/
98typedef struct _BMPInfo
99{
100 unsigned int
101 file_size,
102 ba_offset,
103 offset_bits,
104 size;
105
cristybb503372010-05-27 20:51:26 +0000106 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000107 width,
108 height;
109
110 unsigned short
111 planes,
112 bits_per_pixel;
113
114 unsigned int
115 compression,
116 image_size,
117 x_pixels,
118 y_pixels,
119 number_colors,
120 red_mask,
121 green_mask,
122 blue_mask,
123 alpha_mask,
124 colors_important;
125
126 int
127 colorspace;
128
129 PrimaryInfo
130 red_primary,
131 green_primary,
132 blue_primary,
133 gamma_scale;
134} BMPInfo;
135
136/*
137 Forward declarations.
138*/
139static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000140 WriteBMPImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000141
142/*
143%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
144% %
145% %
146% %
147% D e c o d e I m a g e %
148% %
149% %
150% %
151%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
152%
153% DecodeImage unpacks the packed image pixels into runlength-encoded
154% pixel packets.
155%
156% The format of the DecodeImage method is:
157%
158% MagickBooleanType DecodeImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000159% const size_t compression,unsigned char *pixels)
cristy3ed852e2009-09-05 21:47:34 +0000160%
161% A description of each parameter follows:
162%
163% o image: the address of a structure of type Image.
164%
165% o compression: Zero means uncompressed. A value of 1 means the
166% compressed pixels are runlength encoded for a 256-color bitmap.
167% A value of 2 means a 16-color bitmap. A value of 3 means bitfields
168% encoding.
169%
170% o pixels: The address of a byte (8 bits) array of pixel data created by
171% the decoding process.
172%
173*/
174
cristybb503372010-05-27 20:51:26 +0000175static inline ssize_t MagickAbsoluteValue(const ssize_t x)
cristy3ed852e2009-09-05 21:47:34 +0000176{
177 if (x < 0)
178 return(-x);
179 return(x);
180}
181
182static inline size_t MagickMax(const size_t x,const size_t y)
183{
184 if (x > y)
185 return(x);
186 return(y);
187}
188
cristybb503372010-05-27 20:51:26 +0000189static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000190{
191 if (x < y)
192 return(x);
193 return(y);
194}
195
cristy4e82e512011-04-24 01:33:42 +0000196static MagickBooleanType DecodeImage(Image *image,const size_t compression,
197 unsigned char *pixels)
cristy3ed852e2009-09-05 21:47:34 +0000198{
199 int
200 count;
201
cristybb503372010-05-27 20:51:26 +0000202 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000203 i,
204 x;
205
206 register unsigned char
207 *p,
208 *q;
209
cristy4e82e512011-04-24 01:33:42 +0000210 ssize_t
211 y;
212
cristy3ed852e2009-09-05 21:47:34 +0000213 unsigned char
214 byte;
215
216 assert(image != (Image *) NULL);
217 assert(image->signature == MagickSignature);
218 if (image->debug != MagickFalse)
219 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
220 assert(pixels != (unsigned char *) NULL);
221 (void) ResetMagickMemory(pixels,0,(size_t) image->columns*image->rows*
222 sizeof(*pixels));
223 byte=0;
224 x=0;
225 p=pixels;
226 q=pixels+(size_t) image->columns*image->rows;
cristybb503372010-05-27 20:51:26 +0000227 for (y=0; y < (ssize_t) image->rows; )
cristy3ed852e2009-09-05 21:47:34 +0000228 {
229 if ((p < pixels) || (p >= q))
230 break;
231 count=ReadBlobByte(image);
232 if (count == EOF)
233 break;
234 if (count != 0)
235 {
236 /*
237 Encoded mode.
238 */
239 count=MagickMin(count,(int) (q-p));
240 byte=(unsigned char) ReadBlobByte(image);
241 if (compression == BI_RLE8)
242 {
243 for (i=0; i < count; i++)
244 *p++=(unsigned char) byte;
245 }
246 else
247 {
248 for (i=0; i < count; i++)
249 *p++=(unsigned char)
250 ((i & 0x01) != 0 ? (byte & 0x0f) : ((byte >> 4) & 0x0f));
251 }
252 x+=count;
253 }
254 else
255 {
256 /*
257 Escape mode.
258 */
259 count=ReadBlobByte(image);
260 if (count == 0x01)
261 return(MagickTrue);
262 switch (count)
263 {
264 case 0x00:
265 {
266 /*
267 End of line.
268 */
269 x=0;
270 y++;
271 p=pixels+y*image->columns;
272 break;
273 }
274 case 0x02:
275 {
276 /*
277 Delta mode.
278 */
279 x+=ReadBlobByte(image);
280 y+=ReadBlobByte(image);
281 p=pixels+y*image->columns+x;
282 break;
283 }
284 default:
285 {
286 /*
287 Absolute mode.
288 */
289 count=MagickMin(count,(int) (q-p));
290 if (compression == BI_RLE8)
291 for (i=0; i < count; i++)
292 *p++=(unsigned char) ReadBlobByte(image);
293 else
294 for (i=0; i < count; i++)
295 {
296 if ((i & 0x01) == 0)
297 byte=(unsigned char) ReadBlobByte(image);
298 *p++=(unsigned char)
299 ((i & 0x01) != 0 ? (byte & 0x0f) : ((byte >> 4) & 0x0f));
300 }
301 x+=count;
302 /*
303 Read pad byte.
304 */
305 if (compression == BI_RLE8)
306 {
307 if ((count & 0x01) != 0)
308 (void) ReadBlobByte(image);
309 }
310 else
311 if (((count & 0x03) == 1) || ((count & 0x03) == 2))
312 (void) ReadBlobByte(image);
313 break;
314 }
315 }
316 }
317 if (SetImageProgress(image,LoadImageTag,y,image->rows) == MagickFalse)
318 break;
319 }
320 (void) ReadBlobByte(image); /* end of line */
321 (void) ReadBlobByte(image);
322 return(MagickTrue);
323}
324
325/*
326%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
327% %
328% %
329% %
330% E n c o d e I m a g e %
331% %
332% %
333% %
334%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
335%
336% EncodeImage compresses pixels using a runlength encoded format.
337%
338% The format of the EncodeImage method is:
339%
340% static MagickBooleanType EncodeImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000341% const size_t bytes_per_line,const unsigned char *pixels,
cristy3ed852e2009-09-05 21:47:34 +0000342% unsigned char *compressed_pixels)
343%
344% A description of each parameter follows:
345%
346% o image: The image.
347%
348% o bytes_per_line: the number of bytes in a scanline of compressed pixels
349%
350% o pixels: The address of a byte (8 bits) array of pixel data created by
351% the compression process.
352%
353% o compressed_pixels: The address of a byte (8 bits) array of compressed
354% pixel data.
355%
356*/
cristybb503372010-05-27 20:51:26 +0000357static size_t EncodeImage(Image *image,const size_t bytes_per_line,
cristy3ed852e2009-09-05 21:47:34 +0000358 const unsigned char *pixels,unsigned char *compressed_pixels)
359{
cristy3ed852e2009-09-05 21:47:34 +0000360 MagickBooleanType
361 status;
362
363 register const unsigned char
364 *p;
365
cristybb503372010-05-27 20:51:26 +0000366 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000367 i,
368 x;
369
370 register unsigned char
371 *q;
372
cristy4e82e512011-04-24 01:33:42 +0000373 ssize_t
374 y;
375
cristy3ed852e2009-09-05 21:47:34 +0000376 /*
377 Runlength encode pixels.
378 */
379 assert(image != (Image *) NULL);
380 assert(image->signature == MagickSignature);
381 if (image->debug != MagickFalse)
382 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
383 assert(pixels != (const unsigned char *) NULL);
384 assert(compressed_pixels != (unsigned char *) NULL);
385 p=pixels;
386 q=compressed_pixels;
387 i=0;
cristybb503372010-05-27 20:51:26 +0000388 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000389 {
cristybb503372010-05-27 20:51:26 +0000390 for (x=0; x < (ssize_t) bytes_per_line; x+=i)
cristy3ed852e2009-09-05 21:47:34 +0000391 {
392 /*
393 Determine runlength.
394 */
cristybb503372010-05-27 20:51:26 +0000395 for (i=1; ((x+i) < (ssize_t) bytes_per_line); i++)
cristy3ed852e2009-09-05 21:47:34 +0000396 if ((i == 255) || (*(p+i) != *p))
397 break;
398 *q++=(unsigned char) i;
399 *q++=(*p);
400 p+=i;
401 }
402 /*
403 End of line.
404 */
405 *q++=(unsigned char) 0x00;
406 *q++=(unsigned char) 0x00;
cristycee97112010-05-28 00:44:52 +0000407 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy4e82e512011-04-24 01:33:42 +0000408 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000409 if (status == MagickFalse)
410 break;
411 }
412 /*
413 End of bitmap.
414 */
415 *q++=(unsigned char) 0x00;
416 *q++=(unsigned char) 0x01;
417 return((size_t) (q-compressed_pixels));
418}
419
420/*
421%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
422% %
423% %
424% %
425% I s B M P %
426% %
427% %
428% %
429%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
430%
431% IsBMP() returns MagickTrue if the image format type, identified by the
432% magick string, is BMP.
433%
434% The format of the IsBMP method is:
435%
436% MagickBooleanType IsBMP(const unsigned char *magick,const size_t length)
437%
438% A description of each parameter follows:
439%
440% o magick: compare image format pattern against these bytes.
441%
442% o length: Specifies the length of the magick string.
443%
444*/
445static MagickBooleanType IsBMP(const unsigned char *magick,const size_t length)
446{
447 if (length < 2)
448 return(MagickFalse);
449 if ((LocaleNCompare((char *) magick,"BA",2) == 0) ||
450 (LocaleNCompare((char *) magick,"BM",2) == 0) ||
451 (LocaleNCompare((char *) magick,"IC",2) == 0) ||
452 (LocaleNCompare((char *) magick,"PI",2) == 0) ||
453 (LocaleNCompare((char *) magick,"CI",2) == 0) ||
454 (LocaleNCompare((char *) magick,"CP",2) == 0))
455 return(MagickTrue);
456 return(MagickFalse);
457}
458
459/*
460%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
461% %
462% %
463% %
464% R e a d B M P I m a g e %
465% %
466% %
467% %
468%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
469%
470% ReadBMPImage() reads a Microsoft Windows bitmap image file, Version
471% 2, 3 (for Windows or NT), or 4, and returns it. It allocates the memory
472% necessary for the new Image structure and returns a pointer to the new
473% image.
474%
475% The format of the ReadBMPImage method is:
476%
477% image=ReadBMPImage(image_info)
478%
479% A description of each parameter follows:
480%
481% o image_info: the image info.
482%
483% o exception: return any errors or warnings in this structure.
484%
485*/
486
487static Image *ReadBMPImage(const ImageInfo *image_info,ExceptionInfo *exception)
488{
489 BMPInfo
490 bmp_info;
491
492 Image
493 *image;
494
cristy3ed852e2009-09-05 21:47:34 +0000495 MagickBooleanType
496 status;
497
498 MagickOffsetType
499 offset,
500 start_position;
501
cristy4c08aed2011-07-01 19:47:50 +0000502 Quantum
503 index;
cristy3ed852e2009-09-05 21:47:34 +0000504
cristy4c08aed2011-07-01 19:47:50 +0000505 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000506 *q;
507
cristybb503372010-05-27 20:51:26 +0000508 register ssize_t
cristy0da03562011-04-23 23:23:21 +0000509 i,
510 x;
cristy3ed852e2009-09-05 21:47:34 +0000511
512 register unsigned char
513 *p;
514
cristybb503372010-05-27 20:51:26 +0000515 size_t
cristy3ed852e2009-09-05 21:47:34 +0000516 bit,
517 blue,
518 bytes_per_line,
519 green,
cristy0da03562011-04-23 23:23:21 +0000520 length,
cristy3ed852e2009-09-05 21:47:34 +0000521 opacity,
522 red;
523
cristy0da03562011-04-23 23:23:21 +0000524 ssize_t
cristy4e82e512011-04-24 01:33:42 +0000525 count,
526 y;
cristy0da03562011-04-23 23:23:21 +0000527
528 unsigned char
529 magick[12],
530 *pixels;
531
cristy3ed852e2009-09-05 21:47:34 +0000532 /*
533 Open image file.
534 */
535 assert(image_info != (const ImageInfo *) NULL);
536 assert(image_info->signature == MagickSignature);
537 if (image_info->debug != MagickFalse)
538 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
539 image_info->filename);
540 assert(exception != (ExceptionInfo *) NULL);
541 assert(exception->signature == MagickSignature);
cristy9950d572011-10-01 18:22:35 +0000542 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000543 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
544 if (status == MagickFalse)
545 {
546 image=DestroyImageList(image);
547 return((Image *) NULL);
548 }
549 /*
550 Determine if this a BMP file.
551 */
552 (void) ResetMagickMemory(&bmp_info,0,sizeof(bmp_info));
553 bmp_info.ba_offset=0;
554 start_position=0;
555 count=ReadBlob(image,2,magick);
556 do
557 {
cristy101ab702011-10-13 13:06:32 +0000558 PixelInfo
559 quantum_bits;
cristy3ed852e2009-09-05 21:47:34 +0000560
561 PixelPacket
cristy101ab702011-10-13 13:06:32 +0000562 shift;
cristy3ed852e2009-09-05 21:47:34 +0000563
cristybb503372010-05-27 20:51:26 +0000564 size_t
cristy3ed852e2009-09-05 21:47:34 +0000565 profile_data,
566 profile_size;
567
568 /*
569 Verify BMP identifier.
570 */
571 if (bmp_info.ba_offset == 0)
572 start_position=TellBlob(image)-2;
573 bmp_info.ba_offset=0;
574 while (LocaleNCompare((char *) magick,"BA",2) == 0)
575 {
576 bmp_info.file_size=ReadBlobLSBLong(image);
577 bmp_info.ba_offset=ReadBlobLSBLong(image);
578 bmp_info.offset_bits=ReadBlobLSBLong(image);
579 count=ReadBlob(image,2,magick);
580 if (count != 2)
581 break;
582 }
583 if (image->debug != MagickFalse)
584 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Magick: %c%c",
585 magick[0],magick[1]);
586 if ((count == 0) || ((LocaleNCompare((char *) magick,"BM",2) != 0) &&
587 (LocaleNCompare((char *) magick,"CI",2) != 0)))
588 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
589 bmp_info.file_size=ReadBlobLSBLong(image);
590 (void) ReadBlobLSBLong(image);
591 bmp_info.offset_bits=ReadBlobLSBLong(image);
592 bmp_info.size=ReadBlobLSBLong(image);
593 if (image->debug != MagickFalse)
594 (void) LogMagickEvent(CoderEvent,GetMagickModule()," BMP size: %u",
595 bmp_info.size);
596 if (bmp_info.size == 12)
597 {
598 /*
599 OS/2 BMP image file.
600 */
601 bmp_info.width=(short) ReadBlobLSBShort(image);
602 bmp_info.height=(short) ReadBlobLSBShort(image);
603 bmp_info.planes=ReadBlobLSBShort(image);
604 bmp_info.bits_per_pixel=ReadBlobLSBShort(image);
605 bmp_info.x_pixels=0;
606 bmp_info.y_pixels=0;
607 bmp_info.number_colors=0;
608 bmp_info.compression=BI_RGB;
609 bmp_info.image_size=0;
610 bmp_info.alpha_mask=0;
611 if (image->debug != MagickFalse)
612 {
613 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
614 " Format: OS/2 Bitmap");
615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000616 " Geometry: %.20gx%.20g",(double) bmp_info.width,(double)
617 bmp_info.height);
cristy3ed852e2009-09-05 21:47:34 +0000618 }
619 }
620 else
621 {
622 /*
623 Microsoft Windows BMP image file.
624 */
625 if (bmp_info.size < 40)
626 ThrowReaderException(CorruptImageError,"NonOS2HeaderSizeError");
627 bmp_info.width=(int) ReadBlobLSBLong(image);
628 bmp_info.height=(int) ReadBlobLSBLong(image);
629 bmp_info.planes=ReadBlobLSBShort(image);
630 bmp_info.bits_per_pixel=ReadBlobLSBShort(image);
631 bmp_info.compression=ReadBlobLSBLong(image);
632 bmp_info.image_size=ReadBlobLSBLong(image);
633 bmp_info.x_pixels=ReadBlobLSBLong(image);
634 bmp_info.y_pixels=ReadBlobLSBLong(image);
635 bmp_info.number_colors=ReadBlobLSBLong(image);
636 bmp_info.colors_important=ReadBlobLSBLong(image);
637 profile_data=0;
638 profile_size=0;
639 if (image->debug != MagickFalse)
640 {
641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
642 " Format: MS Windows bitmap");
643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000644 " Geometry: %.20gx%.20g",(double) bmp_info.width,(double)
645 bmp_info.height);
cristy3ed852e2009-09-05 21:47:34 +0000646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000647 " Bits per pixel: %.20g",(double) bmp_info.bits_per_pixel);
cristy3ed852e2009-09-05 21:47:34 +0000648 switch ((int) bmp_info.compression)
649 {
650 case BI_RGB:
651 {
652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
653 " Compression: BI_RGB");
654 break;
655 }
656 case BI_RLE4:
657 {
658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
659 " Compression: BI_RLE4");
660 break;
661 }
662 case BI_RLE8:
663 {
664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
665 " Compression: BI_RLE8");
666 break;
667 }
668 case BI_BITFIELDS:
669 {
670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
671 " Compression: BI_BITFIELDS");
672 break;
673 }
674 case BI_PNG:
675 {
676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
677 " Compression: BI_PNG");
678 break;
679 }
680 case BI_JPEG:
681 {
682 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
683 " Compression: BI_JPEG");
684 break;
685 }
686 default:
687 {
688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
689 " Compression: UNKNOWN (%u)",bmp_info.compression);
690 }
691 }
692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
693 " Number of colors: %u",bmp_info.number_colors);
694 }
695 bmp_info.red_mask=ReadBlobLSBLong(image);
696 bmp_info.green_mask=ReadBlobLSBLong(image);
697 bmp_info.blue_mask=ReadBlobLSBLong(image);
698 if (bmp_info.size > 40)
699 {
700 double
701 sum;
702
703 /*
704 Read color management information.
705 */
706 bmp_info.alpha_mask=ReadBlobLSBLong(image);
cristy6cff05d2010-09-02 11:22:46 +0000707 bmp_info.colorspace=(int) ReadBlobLSBLong(image);
cristy3ed852e2009-09-05 21:47:34 +0000708 /*
709 Decode 2^30 fixed point formatted CIE primaries.
710 */
711 bmp_info.red_primary.x=(double) ReadBlobLSBLong(image)/0x3ffffff;
712 bmp_info.red_primary.y=(double) ReadBlobLSBLong(image)/0x3ffffff;
713 bmp_info.red_primary.z=(double) ReadBlobLSBLong(image)/0x3ffffff;
714 bmp_info.green_primary.x=(double) ReadBlobLSBLong(image)/0x3ffffff;
715 bmp_info.green_primary.y=(double) ReadBlobLSBLong(image)/0x3ffffff;
716 bmp_info.green_primary.z=(double) ReadBlobLSBLong(image)/0x3ffffff;
717 bmp_info.blue_primary.x=(double) ReadBlobLSBLong(image)/0x3ffffff;
718 bmp_info.blue_primary.y=(double) ReadBlobLSBLong(image)/0x3ffffff;
719 bmp_info.blue_primary.z=(double) ReadBlobLSBLong(image)/0x3ffffff;
cristybd4ab272010-08-13 17:57:55 +0000720 sum=bmp_info.red_primary.x+bmp_info.red_primary.y+
cristy3ed852e2009-09-05 21:47:34 +0000721 bmp_info.red_primary.z;
722 image->chromaticity.red_primary.x/=sum;
723 image->chromaticity.red_primary.y/=sum;
cristybd4ab272010-08-13 17:57:55 +0000724 sum=bmp_info.green_primary.x+bmp_info.green_primary.y+
cristy3ed852e2009-09-05 21:47:34 +0000725 bmp_info.green_primary.z;
726 image->chromaticity.green_primary.x/=sum;
727 image->chromaticity.green_primary.y/=sum;
cristybd4ab272010-08-13 17:57:55 +0000728 sum=bmp_info.blue_primary.x+bmp_info.blue_primary.y+
cristy3ed852e2009-09-05 21:47:34 +0000729 bmp_info.blue_primary.z;
730 image->chromaticity.blue_primary.x/=sum;
731 image->chromaticity.blue_primary.y/=sum;
732 /*
733 Decode 16^16 fixed point formatted gamma_scales.
734 */
735 bmp_info.gamma_scale.x=(double) ReadBlobLSBLong(image)/0xffff;
736 bmp_info.gamma_scale.y=(double) ReadBlobLSBLong(image)/0xffff;
737 bmp_info.gamma_scale.z=(double) ReadBlobLSBLong(image)/0xffff;
738 /*
739 Compute a single gamma from the BMP 3-channel gamma.
740 */
741 image->gamma=(bmp_info.gamma_scale.x+bmp_info.gamma_scale.y+
742 bmp_info.gamma_scale.z)/3.0;
743 }
744 if (bmp_info.size > 108)
745 {
cristybb503372010-05-27 20:51:26 +0000746 size_t
cristy3ed852e2009-09-05 21:47:34 +0000747 intent;
748
749 /*
750 Read BMP Version 5 color management information.
751 */
752 intent=ReadBlobLSBLong(image);
753 switch ((int) intent)
754 {
755 case LCS_GM_BUSINESS:
756 {
757 image->rendering_intent=SaturationIntent;
758 break;
759 }
760 case LCS_GM_GRAPHICS:
761 {
762 image->rendering_intent=RelativeIntent;
763 break;
764 }
765 case LCS_GM_IMAGES:
766 {
767 image->rendering_intent=PerceptualIntent;
768 break;
769 }
770 case LCS_GM_ABS_COLORIMETRIC:
771 {
772 image->rendering_intent=AbsoluteIntent;
773 break;
774 }
775 }
776 profile_data=ReadBlobLSBLong(image);
777 profile_size=ReadBlobLSBLong(image);
cristyda16f162011-02-19 23:52:17 +0000778 (void) profile_data;
779 (void) profile_size;
cristy3ed852e2009-09-05 21:47:34 +0000780 (void) ReadBlobLSBLong(image); /* Reserved byte */
781 }
782 }
cristy59eefcf2011-02-01 15:54:38 +0000783 if ((MagickSizeType) bmp_info.file_size > GetBlobSize(image))
cristy3ed852e2009-09-05 21:47:34 +0000784 (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
785 "LengthAndFilesizeDoNotMatch","`%s'",image->filename);
cristy59eefcf2011-02-01 15:54:38 +0000786 else
787 if ((MagickSizeType) bmp_info.file_size < GetBlobSize(image))
788 (void) ThrowMagickException(exception,GetMagickModule(),
789 CorruptImageWarning,"LengthAndFilesizeDoNotMatch","`%s'",
790 image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000791 if (bmp_info.width <= 0)
792 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
793 if (bmp_info.height == 0)
794 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
795 if (bmp_info.planes != 1)
796 ThrowReaderException(CorruptImageError,"StaticPlanesValueNotEqualToOne");
797 if ((bmp_info.bits_per_pixel != 1) && (bmp_info.bits_per_pixel != 4) &&
798 (bmp_info.bits_per_pixel != 8) && (bmp_info.bits_per_pixel != 16) &&
799 (bmp_info.bits_per_pixel != 24) && (bmp_info.bits_per_pixel != 32))
800 ThrowReaderException(CorruptImageError,"UnrecognizedBitsPerPixel");
801 if (bmp_info.number_colors > (1U << bmp_info.bits_per_pixel))
802 {
803 if (bmp_info.bits_per_pixel < 24)
804 ThrowReaderException(CorruptImageError,"UnrecognizedNumberOfColors");
805 bmp_info.number_colors=0;
806 }
cristy3ed852e2009-09-05 21:47:34 +0000807 if ((bmp_info.compression == 1) && (bmp_info.bits_per_pixel != 8))
808 ThrowReaderException(CorruptImageError,"UnrecognizedBitsPerPixel");
809 if ((bmp_info.compression == 2) && (bmp_info.bits_per_pixel != 4))
810 ThrowReaderException(CorruptImageError,"UnrecognizedBitsPerPixel");
811 if ((bmp_info.compression == 3) && (bmp_info.bits_per_pixel < 16))
812 ThrowReaderException(CorruptImageError,"UnrecognizedBitsPerPixel");
813 switch (bmp_info.compression)
814 {
815 case BI_RGB:
816 case BI_RLE8:
817 case BI_RLE4:
818 case BI_BITFIELDS:
819 break;
820 case BI_JPEG:
821 ThrowReaderException(CoderError,"JPEGCompressNotSupported");
822 case BI_PNG:
823 ThrowReaderException(CoderError,"PNGCompressNotSupported");
824 default:
825 ThrowReaderException(CorruptImageError,"UnrecognizedImageCompression");
826 }
cristybb503372010-05-27 20:51:26 +0000827 image->columns=(size_t) MagickAbsoluteValue(bmp_info.width);
828 image->rows=(size_t) MagickAbsoluteValue(bmp_info.height);
cristy3ed852e2009-09-05 21:47:34 +0000829 image->depth=bmp_info.bits_per_pixel <= 8 ? bmp_info.bits_per_pixel : 8;
cristyaeb2cbc2010-05-07 13:28:58 +0000830 if ((bmp_info.bits_per_pixel == 16) ||
831 (bmp_info.bits_per_pixel == 32))
832 image->matte=bmp_info.alpha_mask != 0 ? MagickTrue : MagickFalse;
cristy6a4e1002010-05-26 12:48:31 +0000833 if ((bmp_info.number_colors != 0) || (bmp_info.bits_per_pixel < 16))
cristy3ed852e2009-09-05 21:47:34 +0000834 {
cristy0b29b252010-05-30 01:59:46 +0000835 size_t
836 one;
837
cristy3ed852e2009-09-05 21:47:34 +0000838 image->storage_class=PseudoClass;
839 image->colors=bmp_info.number_colors;
cristy0b29b252010-05-30 01:59:46 +0000840 one=1;
cristy3ed852e2009-09-05 21:47:34 +0000841 if (image->colors == 0)
cristy0b29b252010-05-30 01:59:46 +0000842 image->colors=one << bmp_info.bits_per_pixel;
cristy3ed852e2009-09-05 21:47:34 +0000843 }
844 if (image->storage_class == PseudoClass)
845 {
846 unsigned char
847 *bmp_colormap;
848
849 size_t
850 packet_size;
851
852 /*
853 Read BMP raster colormap.
854 */
855 if (image->debug != MagickFalse)
856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000857 " Reading colormap of %.20g colors",(double) image->colors);
cristy018f07f2011-09-04 21:15:19 +0000858 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000859 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
860 bmp_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
861 image->colors,4*sizeof(*bmp_colormap));
862 if (bmp_colormap == (unsigned char *) NULL)
863 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
864 if ((bmp_info.size == 12) || (bmp_info.size == 64))
865 packet_size=3;
866 else
867 packet_size=4;
868 offset=SeekBlob(image,start_position+14+bmp_info.size,SEEK_SET);
869 if (offset < 0)
870 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
871 count=ReadBlob(image,packet_size*image->colors,bmp_colormap);
872 if (count != (ssize_t) (packet_size*image->colors))
873 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
874 p=bmp_colormap;
cristybb503372010-05-27 20:51:26 +0000875 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000876 {
877 image->colormap[i].blue=ScaleCharToQuantum(*p++);
878 image->colormap[i].green=ScaleCharToQuantum(*p++);
879 image->colormap[i].red=ScaleCharToQuantum(*p++);
880 if (packet_size == 4)
881 p++;
882 }
883 bmp_colormap=(unsigned char *) RelinquishMagickMemory(bmp_colormap);
884 }
885 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
886 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
887 break;
888 /*
889 Read image data.
890 */
891 offset=SeekBlob(image,start_position+bmp_info.offset_bits,SEEK_SET);
892 if (offset < 0)
893 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
894 if (bmp_info.compression == BI_RLE4)
895 bmp_info.bits_per_pixel<<=1;
896 bytes_per_line=4*((image->columns*bmp_info.bits_per_pixel+31)/32);
897 length=(size_t) bytes_per_line*image->rows;
898 pixels=(unsigned char *) AcquireQuantumMemory((size_t) image->rows,
899 MagickMax(bytes_per_line,image->columns+256UL)*sizeof(*pixels));
900 if (pixels == (unsigned char *) NULL)
901 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
902 if ((bmp_info.compression == BI_RGB) ||
903 (bmp_info.compression == BI_BITFIELDS))
904 {
905 if (image->debug != MagickFalse)
906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000907 " Reading pixels (%.20g bytes)",(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000908 count=ReadBlob(image,length,pixels);
909 if (count != (ssize_t) length)
cristyde60ea72010-07-09 23:12:09 +0000910 {
911 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
912 ThrowReaderException(CorruptImageError,
913 "InsufficientImageDataInFile");
914 }
cristy3ed852e2009-09-05 21:47:34 +0000915 }
916 else
917 {
918 /*
919 Convert run-length encoded raster pixels.
920 */
921 status=DecodeImage(image,bmp_info.compression,pixels);
922 if (status == MagickFalse)
cristyde60ea72010-07-09 23:12:09 +0000923 {
924 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
925 ThrowReaderException(CorruptImageError,
926 "UnableToRunlengthDecodeImage");
927 }
cristy3ed852e2009-09-05 21:47:34 +0000928 }
929 /*
930 Initialize image structure.
931 */
932 image->x_resolution=(double) bmp_info.x_pixels/100.0;
933 image->y_resolution=(double) bmp_info.y_pixels/100.0;
934 image->units=PixelsPerCentimeterResolution;
935 /*
936 Convert BMP raster image to pixel packets.
937 */
938 if (bmp_info.compression == BI_RGB)
939 {
940 bmp_info.alpha_mask=0;
941 bmp_info.red_mask=0x00ff0000U;
942 bmp_info.green_mask=0x0000ff00U;
943 bmp_info.blue_mask=0x000000ffU;
944 if (bmp_info.bits_per_pixel == 16)
945 {
946 /*
947 RGB555.
948 */
949 bmp_info.red_mask=0x00007c00U;
950 bmp_info.green_mask=0x000003e0U;
951 bmp_info.blue_mask=0x0000001fU;
952 }
953 }
954 if ((bmp_info.bits_per_pixel == 16) || (bmp_info.bits_per_pixel == 32))
955 {
cristybb503372010-05-27 20:51:26 +0000956 register size_t
cristy3ed852e2009-09-05 21:47:34 +0000957 sample;
958
959 /*
960 Get shift and quantum bits info from bitfield masks.
961 */
962 (void) ResetMagickMemory(&shift,0,sizeof(shift));
963 (void) ResetMagickMemory(&quantum_bits,0,sizeof(quantum_bits));
964 if (bmp_info.red_mask != 0)
965 while (((bmp_info.red_mask << shift.red) & 0x80000000UL) == 0)
966 shift.red++;
967 if (bmp_info.green_mask != 0)
968 while (((bmp_info.green_mask << shift.green) & 0x80000000UL) == 0)
969 shift.green++;
970 if (bmp_info.blue_mask != 0)
971 while (((bmp_info.blue_mask << shift.blue) & 0x80000000UL) == 0)
972 shift.blue++;
973 if (bmp_info.alpha_mask != 0)
cristy4c08aed2011-07-01 19:47:50 +0000974 while (((bmp_info.alpha_mask << shift.alpha) & 0x80000000UL) == 0)
975 shift.alpha++;
cristy3ed852e2009-09-05 21:47:34 +0000976 sample=shift.red;
977 while (((bmp_info.red_mask << sample) & 0x80000000UL) != 0)
978 sample++;
979 quantum_bits.red=(Quantum) (sample-shift.red);
980 sample=shift.green;
981 while (((bmp_info.green_mask << sample) & 0x80000000UL) != 0)
982 sample++;
983 quantum_bits.green=(Quantum) (sample-shift.green);
984 sample=shift.blue;
985 while (((bmp_info.blue_mask << sample) & 0x80000000UL) != 0)
986 sample++;
987 quantum_bits.blue=(Quantum) (sample-shift.blue);
cristy4c08aed2011-07-01 19:47:50 +0000988 sample=shift.alpha;
cristy3ed852e2009-09-05 21:47:34 +0000989 while (((bmp_info.alpha_mask << sample) & 0x80000000UL) != 0)
990 sample++;
cristy4c08aed2011-07-01 19:47:50 +0000991 quantum_bits.alpha=(Quantum) (sample-shift.alpha);
cristy3ed852e2009-09-05 21:47:34 +0000992 }
993 switch (bmp_info.bits_per_pixel)
994 {
995 case 1:
996 {
997 /*
998 Convert bitmap scanline.
999 */
cristybb503372010-05-27 20:51:26 +00001000 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +00001001 {
1002 p=pixels+(image->rows-y-1)*bytes_per_line;
1003 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001004 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001005 break;
cristybb503372010-05-27 20:51:26 +00001006 for (x=0; x < ((ssize_t) image->columns-7); x+=8)
cristy3ed852e2009-09-05 21:47:34 +00001007 {
1008 for (bit=0; bit < 8; bit++)
1009 {
cristy4c08aed2011-07-01 19:47:50 +00001010 index=(Quantum) (((*p) & (0x80 >> bit)) != 0 ? 0x01 : 0x00);
1011 SetPixelIndex(image,index,q);
cristyed231572011-07-14 02:18:59 +00001012 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001013 }
1014 p++;
1015 }
1016 if ((image->columns % 8) != 0)
1017 {
1018 for (bit=0; bit < (image->columns % 8); bit++)
1019 {
cristy4c08aed2011-07-01 19:47:50 +00001020 index=(Quantum) (((*p) & (0x80 >> bit)) != 0 ? 0x01 : 0x00);
1021 SetPixelIndex(image,index,q);
cristyed231572011-07-14 02:18:59 +00001022 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001023 }
1024 p++;
1025 }
1026 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1027 break;
1028 if (image->previous == (Image *) NULL)
1029 {
cristycee97112010-05-28 00:44:52 +00001030 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
1031 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001032 if (status == MagickFalse)
1033 break;
1034 }
1035 }
cristyea1a8aa2011-10-20 13:24:06 +00001036 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001037 break;
1038 }
1039 case 4:
1040 {
1041 /*
1042 Convert PseudoColor scanline.
1043 */
cristybb503372010-05-27 20:51:26 +00001044 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +00001045 {
1046 p=pixels+(image->rows-y-1)*bytes_per_line;
1047 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001048 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001049 break;
cristybb503372010-05-27 20:51:26 +00001050 for (x=0; x < ((ssize_t) image->columns-1); x+=2)
cristy3ed852e2009-09-05 21:47:34 +00001051 {
cristyc82a27b2011-10-21 01:07:16 +00001052 index=ConstrainColormapIndex(image,(*p >> 4) & 0x0f,exception);
cristy4c08aed2011-07-01 19:47:50 +00001053 SetPixelIndex(image,index,q);
cristyed231572011-07-14 02:18:59 +00001054 q+=GetPixelChannels(image);
cristyc82a27b2011-10-21 01:07:16 +00001055 index=ConstrainColormapIndex(image,*p & 0x0f,exception);
cristy4c08aed2011-07-01 19:47:50 +00001056 SetPixelIndex(image,index,q);
cristyed231572011-07-14 02:18:59 +00001057 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001058 p++;
1059 }
1060 if ((image->columns % 2) != 0)
1061 {
cristyc82a27b2011-10-21 01:07:16 +00001062 index=ConstrainColormapIndex(image,(*p >> 4) & 0xf,exception);
cristy4c08aed2011-07-01 19:47:50 +00001063 SetPixelIndex(image,index,q);
cristyed231572011-07-14 02:18:59 +00001064 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001065 p++;
1066 }
1067 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1068 break;
1069 if (image->previous == (Image *) NULL)
1070 {
cristycee97112010-05-28 00:44:52 +00001071 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
1072 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001073 if (status == MagickFalse)
1074 break;
1075 }
1076 }
cristyea1a8aa2011-10-20 13:24:06 +00001077 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001078 break;
1079 }
1080 case 8:
1081 {
1082 /*
1083 Convert PseudoColor scanline.
1084 */
1085 if ((bmp_info.compression == BI_RLE8) ||
1086 (bmp_info.compression == BI_RLE4))
1087 bytes_per_line=image->columns;
cristybb503372010-05-27 20:51:26 +00001088 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +00001089 {
1090 p=pixels+(image->rows-y-1)*bytes_per_line;
1091 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001092 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001093 break;
cristybb503372010-05-27 20:51:26 +00001094 for (x = (ssize_t)image->columns; x != 0; --x)
cristy3ed852e2009-09-05 21:47:34 +00001095 {
cristyc82a27b2011-10-21 01:07:16 +00001096 index=ConstrainColormapIndex(image,*p++,exception);
cristy4c08aed2011-07-01 19:47:50 +00001097 SetPixelIndex(image,index,q);
cristyed231572011-07-14 02:18:59 +00001098 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001099 }
1100 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1101 break;
1102 offset=(MagickOffsetType) (image->rows-y-1);
1103 if (image->previous == (Image *) NULL)
1104 {
cristycee97112010-05-28 00:44:52 +00001105 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
1106 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001107 if (status == MagickFalse)
1108 break;
1109 }
1110 }
cristyea1a8aa2011-10-20 13:24:06 +00001111 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001112 break;
1113 }
1114 case 16:
1115 {
cristybb503372010-05-27 20:51:26 +00001116 size_t
cristy3ed852e2009-09-05 21:47:34 +00001117 pixel;
1118
1119 /*
1120 Convert bitfield encoded 16-bit PseudoColor scanline.
1121 */
1122 if (bmp_info.compression != BI_RGB &&
1123 bmp_info.compression != BI_BITFIELDS)
cristyde60ea72010-07-09 23:12:09 +00001124 {
1125 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1126 ThrowReaderException(CorruptImageError,
1127 "UnrecognizedImageCompression");
1128 }
cristy3ed852e2009-09-05 21:47:34 +00001129 bytes_per_line=2*(image->columns+image->columns % 2);
1130 image->storage_class=DirectClass;
cristybb503372010-05-27 20:51:26 +00001131 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +00001132 {
1133 p=pixels+(image->rows-y-1)*bytes_per_line;
1134 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001135 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001136 break;
cristybb503372010-05-27 20:51:26 +00001137 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001138 {
cristybb503372010-05-27 20:51:26 +00001139 pixel=(size_t) (*p++);
cristy3ed852e2009-09-05 21:47:34 +00001140 pixel|=(*p++) << 8;
1141 red=((pixel & bmp_info.red_mask) << shift.red) >> 16;
1142 if (quantum_bits.red == 5)
1143 red|=((red & 0xe000) >> 5);
1144 if (quantum_bits.red <= 8)
1145 red|=((red & 0xff00) >> 8);
1146 green=((pixel & bmp_info.green_mask) << shift.green) >> 16;
1147 if (quantum_bits.green == 5)
1148 green|=((green & 0xe000) >> 5);
1149 if (quantum_bits.green == 6)
1150 green|=((green & 0xc000) >> 6);
1151 if (quantum_bits.green <= 8)
1152 green|=((green & 0xff00) >> 8);
1153 blue=((pixel & bmp_info.blue_mask) << shift.blue) >> 16;
1154 if (quantum_bits.blue == 5)
1155 blue|=((blue & 0xe000) >> 5);
1156 if (quantum_bits.blue <= 8)
1157 blue|=((blue & 0xff00) >> 8);
cristy4c08aed2011-07-01 19:47:50 +00001158 opacity=((pixel & bmp_info.alpha_mask) << shift.alpha) >> 16;
1159 if (quantum_bits.alpha <= 8)
cristy3ed852e2009-09-05 21:47:34 +00001160 opacity|=((opacity & 0xff00) >> 8);
cristy4c08aed2011-07-01 19:47:50 +00001161 SetPixelRed(image,ScaleShortToQuantum((unsigned short) red),q);
1162 SetPixelGreen(image,ScaleShortToQuantum((unsigned short) green),q);
1163 SetPixelBlue(image,ScaleShortToQuantum((unsigned short) blue),q);
1164 SetPixelAlpha(image,OpaqueAlpha,q);
cristy3ed852e2009-09-05 21:47:34 +00001165 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001166 SetPixelAlpha(image,
1167 ScaleShortToQuantum((unsigned short) opacity),q);
cristyed231572011-07-14 02:18:59 +00001168 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001169 }
1170 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1171 break;
1172 offset=(MagickOffsetType) (image->rows-y-1);
1173 if (image->previous == (Image *) NULL)
1174 {
cristycee97112010-05-28 00:44:52 +00001175 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
1176 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001177 if (status == MagickFalse)
1178 break;
1179 }
1180 }
1181 break;
1182 }
1183 case 24:
1184 {
1185 /*
1186 Convert DirectColor scanline.
1187 */
1188 bytes_per_line=4*((image->columns*24+31)/32);
cristybb503372010-05-27 20:51:26 +00001189 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +00001190 {
1191 p=pixels+(image->rows-y-1)*bytes_per_line;
1192 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001193 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001194 break;
cristybb503372010-05-27 20:51:26 +00001195 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001196 {
cristy4c08aed2011-07-01 19:47:50 +00001197 SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
1198 SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
1199 SetPixelRed(image,ScaleCharToQuantum(*p++),q);
1200 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001201 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001202 }
1203 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1204 break;
1205 offset=(MagickOffsetType) (image->rows-y-1);
1206 if (image->previous == (Image *) NULL)
1207 {
cristycee97112010-05-28 00:44:52 +00001208 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
1209 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001210 if (status == MagickFalse)
1211 break;
1212 }
1213 }
1214 break;
1215 }
1216 case 32:
1217 {
1218 /*
1219 Convert bitfield encoded DirectColor scanline.
1220 */
1221 if ((bmp_info.compression != BI_RGB) &&
1222 (bmp_info.compression != BI_BITFIELDS))
cristyde60ea72010-07-09 23:12:09 +00001223 {
1224 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1225 ThrowReaderException(CorruptImageError,
1226 "UnrecognizedImageCompression");
1227 }
cristy3ed852e2009-09-05 21:47:34 +00001228 bytes_per_line=4*(image->columns);
cristybb503372010-05-27 20:51:26 +00001229 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +00001230 {
cristybb503372010-05-27 20:51:26 +00001231 size_t
cristy3ed852e2009-09-05 21:47:34 +00001232 pixel;
1233
1234 p=pixels+(image->rows-y-1)*bytes_per_line;
1235 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001236 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001237 break;
cristybb503372010-05-27 20:51:26 +00001238 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001239 {
cristybb503372010-05-27 20:51:26 +00001240 pixel=(size_t) (*p++);
cristy3ed852e2009-09-05 21:47:34 +00001241 pixel|=(*p++ << 8);
1242 pixel|=(*p++ << 16);
1243 pixel|=(*p++ << 24);
1244 red=((pixel & bmp_info.red_mask) << shift.red) >> 16;
1245 if (quantum_bits.red == 8)
1246 red|=(red >> 8);
1247 green=((pixel & bmp_info.green_mask) << shift.green) >> 16;
1248 if (quantum_bits.green == 8)
1249 green|=(green >> 8);
1250 blue=((pixel & bmp_info.blue_mask) << shift.blue) >> 16;
1251 if (quantum_bits.blue == 8)
1252 blue|=(blue >> 8);
cristy4c08aed2011-07-01 19:47:50 +00001253 opacity=((pixel & bmp_info.alpha_mask) << shift.alpha) >> 16;
1254 if (quantum_bits.alpha == 8)
cristy3ed852e2009-09-05 21:47:34 +00001255 opacity|=(opacity >> 8);
cristy4c08aed2011-07-01 19:47:50 +00001256 SetPixelRed(image,ScaleShortToQuantum((unsigned short) red),q);
1257 SetPixelGreen(image,ScaleShortToQuantum((unsigned short) green),q);
1258 SetPixelBlue(image,ScaleShortToQuantum((unsigned short) blue),q);
1259 SetPixelAlpha(image,OpaqueAlpha,q);
cristy3ed852e2009-09-05 21:47:34 +00001260 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001261 SetPixelAlpha(image,
1262 ScaleShortToQuantum((unsigned short) opacity),q);
cristyed231572011-07-14 02:18:59 +00001263 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001264 }
1265 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1266 break;
1267 offset=(MagickOffsetType) (image->rows-y-1);
1268 if (image->previous == (Image *) NULL)
1269 {
cristycee97112010-05-28 00:44:52 +00001270 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
1271 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001272 if (status == MagickFalse)
1273 break;
1274 }
1275 }
1276 break;
1277 }
1278 default:
cristyde60ea72010-07-09 23:12:09 +00001279 {
1280 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
cristy3ed852e2009-09-05 21:47:34 +00001281 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristyde60ea72010-07-09 23:12:09 +00001282 }
cristy3ed852e2009-09-05 21:47:34 +00001283 }
1284 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1285 if (EOFBlob(image) != MagickFalse)
1286 {
1287 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
1288 image->filename);
1289 break;
1290 }
1291 if (bmp_info.height < 0)
1292 {
1293 Image
1294 *flipped_image;
1295
1296 /*
1297 Correct image orientation.
1298 */
1299 flipped_image=FlipImage(image,exception);
cristy65e3b002010-04-13 21:20:56 +00001300 if (flipped_image != (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001301 {
cristy65e3b002010-04-13 21:20:56 +00001302 DuplicateBlob(flipped_image,image);
1303 image=DestroyImage(image);
1304 image=flipped_image;
cristy3ed852e2009-09-05 21:47:34 +00001305 }
cristy3ed852e2009-09-05 21:47:34 +00001306 }
1307 /*
1308 Proceed to next image.
1309 */
1310 if (image_info->number_scenes != 0)
1311 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
1312 break;
1313 *magick='\0';
1314 if (bmp_info.ba_offset != 0)
1315 {
1316 offset=SeekBlob(image,(MagickOffsetType) bmp_info.ba_offset,SEEK_SET);
1317 if (offset < 0)
1318 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1319 }
1320 count=ReadBlob(image,2,magick);
1321 if ((count == 2) && (IsBMP(magick,2) != MagickFalse))
1322 {
1323 /*
1324 Acquire next image structure.
1325 */
cristy9950d572011-10-01 18:22:35 +00001326 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001327 if (GetNextImageInList(image) == (Image *) NULL)
1328 {
1329 image=DestroyImageList(image);
1330 return((Image *) NULL);
1331 }
1332 image=SyncNextImageInList(image);
1333 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
1334 GetBlobSize(image));
1335 if (status == MagickFalse)
1336 break;
1337 }
1338 } while (IsBMP(magick,2) != MagickFalse);
1339 (void) CloseBlob(image);
1340 return(GetFirstImageInList(image));
1341}
1342
1343/*
1344%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1345% %
1346% %
1347% %
1348% R e g i s t e r B M P I m a g e %
1349% %
1350% %
1351% %
1352%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1353%
1354% RegisterBMPImage() adds attributes for the BMP image format to
1355% the list of supported formats. The attributes include the image format
1356% tag, a method to read and/or write the format, whether the format
1357% supports the saving of more than one frame to the same file or blob,
1358% whether the format supports native in-memory I/O, and a brief
1359% description of the format.
1360%
1361% The format of the RegisterBMPImage method is:
1362%
cristybb503372010-05-27 20:51:26 +00001363% size_t RegisterBMPImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001364%
1365*/
cristybb503372010-05-27 20:51:26 +00001366ModuleExport size_t RegisterBMPImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001367{
1368 MagickInfo
1369 *entry;
1370
1371 entry=SetMagickInfo("BMP");
1372 entry->decoder=(DecodeImageHandler *) ReadBMPImage;
1373 entry->encoder=(EncodeImageHandler *) WriteBMPImage;
1374 entry->magick=(IsImageFormatHandler *) IsBMP;
cristy2e3d5242010-04-16 23:44:55 +00001375 entry->description=ConstantString("Microsoft Windows bitmap image");
cristy3ed852e2009-09-05 21:47:34 +00001376 entry->module=ConstantString("BMP");
1377 entry->adjoin=MagickFalse;
1378 entry->seekable_stream=MagickTrue;
1379 (void) RegisterMagickInfo(entry);
1380 entry=SetMagickInfo("BMP2");
1381 entry->encoder=(EncodeImageHandler *) WriteBMPImage;
1382 entry->magick=(IsImageFormatHandler *) IsBMP;
cristy2dd92ae2010-04-04 16:10:59 +00001383 entry->description=ConstantString("Microsoft Windows bitmap image (V2)");
cristy3ed852e2009-09-05 21:47:34 +00001384 entry->module=ConstantString("BMP");
1385 entry->adjoin=MagickFalse;
1386 entry->seekable_stream=MagickTrue;
1387 (void) RegisterMagickInfo(entry);
1388 entry=SetMagickInfo("BMP3");
1389 entry->encoder=(EncodeImageHandler *) WriteBMPImage;
1390 entry->magick=(IsImageFormatHandler *) IsBMP;
cristy2dd92ae2010-04-04 16:10:59 +00001391 entry->description=ConstantString("Microsoft Windows bitmap image (V3)");
cristy3ed852e2009-09-05 21:47:34 +00001392 entry->module=ConstantString("BMP");
1393 entry->adjoin=MagickFalse;
1394 entry->seekable_stream=MagickTrue;
1395 (void) RegisterMagickInfo(entry);
1396 return(MagickImageCoderSignature);
1397}
1398
1399/*
1400%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1401% %
1402% %
1403% %
1404% U n r e g i s t e r B M P I m a g e %
1405% %
1406% %
1407% %
1408%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1409%
1410% UnregisterBMPImage() removes format registrations made by the
1411% BMP module from the list of supported formats.
1412%
1413% The format of the UnregisterBMPImage method is:
1414%
1415% UnregisterBMPImage(void)
1416%
1417*/
1418ModuleExport void UnregisterBMPImage(void)
1419{
1420 (void) UnregisterMagickInfo("BMP");
1421 (void) UnregisterMagickInfo("BMP2");
1422 (void) UnregisterMagickInfo("BMP3");
1423}
1424
1425/*
1426%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1427% %
1428% %
1429% %
1430% W r i t e B M P I m a g e %
1431% %
1432% %
1433% %
1434%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1435%
1436% WriteBMPImage() writes an image in Microsoft Windows bitmap encoded
1437% image format, version 3 for Windows or (if the image has a matte channel)
1438% version 4.
1439%
1440% The format of the WriteBMPImage method is:
1441%
cristy1e178e72011-08-28 19:44:34 +00001442% MagickBooleanType WriteBMPImage(const ImageInfo *image_info,
1443% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001444%
1445% A description of each parameter follows.
1446%
1447% o image_info: the image info.
1448%
1449% o image: The image.
1450%
cristy1e178e72011-08-28 19:44:34 +00001451% o exception: return any errors or warnings in this structure.
1452%
cristy3ed852e2009-09-05 21:47:34 +00001453*/
cristy1e178e72011-08-28 19:44:34 +00001454static MagickBooleanType WriteBMPImage(const ImageInfo *image_info,Image *image,
1455 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001456{
1457 BMPInfo
1458 bmp_info;
1459
1460 const StringInfo
1461 *profile;
1462
cristy3ed852e2009-09-05 21:47:34 +00001463 MagickBooleanType
1464 have_color_info,
1465 status;
1466
1467 MagickOffsetType
1468 scene;
1469
cristy4c08aed2011-07-01 19:47:50 +00001470 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001471 *p;
1472
cristybb503372010-05-27 20:51:26 +00001473 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001474 i,
1475 x;
1476
1477 register unsigned char
1478 *q;
1479
cristybb503372010-05-27 20:51:26 +00001480 size_t
cristy3ed852e2009-09-05 21:47:34 +00001481 bytes_per_line,
1482 type;
1483
cristy4e82e512011-04-24 01:33:42 +00001484 ssize_t
1485 y;
1486
1487 unsigned char
1488 *bmp_data,
1489 *pixels;
1490
cristy3ed852e2009-09-05 21:47:34 +00001491 /*
1492 Open output image file.
1493 */
1494 assert(image_info != (const ImageInfo *) NULL);
1495 assert(image_info->signature == MagickSignature);
1496 assert(image != (Image *) NULL);
1497 assert(image->signature == MagickSignature);
1498 if (image->debug != MagickFalse)
1499 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00001500 assert(exception != (ExceptionInfo *) NULL);
1501 assert(exception->signature == MagickSignature);
cristy1e178e72011-08-28 19:44:34 +00001502 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00001503 if (status == MagickFalse)
1504 return(status);
1505 type=4;
1506 if (LocaleCompare(image_info->magick,"BMP2") == 0)
1507 type=2;
1508 else
1509 if (LocaleCompare(image_info->magick,"BMP3") == 0)
1510 type=3;
1511 scene=0;
1512 do
1513 {
1514 /*
1515 Initialize BMP raster file header.
1516 */
cristy510d06a2011-07-06 23:43:54 +00001517 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristye941a752011-10-15 01:52:48 +00001518 (void) TransformImageColorspace(image,RGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001519 (void) ResetMagickMemory(&bmp_info,0,sizeof(bmp_info));
1520 bmp_info.file_size=14+12;
1521 if (type > 2)
1522 bmp_info.file_size+=28;
1523 bmp_info.offset_bits=bmp_info.file_size;
1524 bmp_info.compression=BI_RGB;
1525 if ((image->storage_class == PseudoClass) && (image->colors > 256))
cristy1e178e72011-08-28 19:44:34 +00001526 (void) SetImageStorageClass(image,DirectClass,exception);
cristy3ed852e2009-09-05 21:47:34 +00001527 if (image->storage_class != DirectClass)
1528 {
1529 /*
1530 Colormapped BMP raster.
1531 */
1532 bmp_info.bits_per_pixel=8;
1533 if (image->colors <= 2)
1534 bmp_info.bits_per_pixel=1;
1535 else
1536 if (image->colors <= 16)
1537 bmp_info.bits_per_pixel=4;
1538 else
1539 if (image->colors <= 256)
1540 bmp_info.bits_per_pixel=8;
1541 if (image_info->compression == RLECompression)
1542 bmp_info.bits_per_pixel=8;
1543 bmp_info.number_colors=1U << bmp_info.bits_per_pixel;
1544 if (image->matte != MagickFalse)
cristy1e178e72011-08-28 19:44:34 +00001545 (void) SetImageStorageClass(image,DirectClass,exception);
cristy3ed852e2009-09-05 21:47:34 +00001546 else
cristybb503372010-05-27 20:51:26 +00001547 if ((size_t) bmp_info.number_colors < image->colors)
cristy1e178e72011-08-28 19:44:34 +00001548 (void) SetImageStorageClass(image,DirectClass,exception);
cristy3ed852e2009-09-05 21:47:34 +00001549 else
1550 {
1551 bmp_info.file_size+=3*(1UL << bmp_info.bits_per_pixel);
1552 bmp_info.offset_bits+=3*(1UL << bmp_info.bits_per_pixel);
1553 if (type > 2)
1554 {
1555 bmp_info.file_size+=(1UL << bmp_info.bits_per_pixel);
1556 bmp_info.offset_bits+=(1UL << bmp_info.bits_per_pixel);
1557 }
1558 }
1559 }
1560 if (image->storage_class == DirectClass)
1561 {
1562 /*
1563 Full color BMP raster.
1564 */
1565 bmp_info.number_colors=0;
1566 bmp_info.bits_per_pixel=(unsigned short)
1567 ((type > 3) && (image->matte != MagickFalse) ? 32 : 24);
1568 bmp_info.compression=(unsigned int) ((type > 3) &&
1569 (image->matte != MagickFalse) ? BI_BITFIELDS : BI_RGB);
1570 }
1571 bytes_per_line=4*((image->columns*bmp_info.bits_per_pixel+31)/32);
1572 bmp_info.ba_offset=0;
1573 profile=GetImageProfile(image,"icc");
1574 have_color_info=(image->rendering_intent != UndefinedIntent) ||
1575 (profile != (StringInfo *) NULL) || (image->gamma != 0.0) ? MagickTrue :
1576 MagickFalse;
1577 if (type == 2)
1578 bmp_info.size=12;
1579 else
1580 if ((type == 3) || ((image->matte == MagickFalse) &&
1581 (have_color_info == MagickFalse)))
1582 {
1583 type=3;
1584 bmp_info.size=40;
1585 }
1586 else
1587 {
1588 int
1589 extra_size;
1590
1591 bmp_info.size=108;
1592 extra_size=68;
1593 if ((image->rendering_intent != UndefinedIntent) ||
1594 (profile != (StringInfo *) NULL))
1595 {
1596 bmp_info.size=124;
1597 extra_size+=16;
1598 }
1599 bmp_info.file_size+=extra_size;
1600 bmp_info.offset_bits+=extra_size;
1601 }
cristybb503372010-05-27 20:51:26 +00001602 bmp_info.width=(ssize_t) image->columns;
1603 bmp_info.height=(ssize_t) image->rows;
cristy3ed852e2009-09-05 21:47:34 +00001604 bmp_info.planes=1;
1605 bmp_info.image_size=(unsigned int) (bytes_per_line*image->rows);
1606 bmp_info.file_size+=bmp_info.image_size;
1607 bmp_info.x_pixels=75*39;
1608 bmp_info.y_pixels=75*39;
1609 switch (image->units)
1610 {
1611 case UndefinedResolution:
1612 case PixelsPerInchResolution:
1613 {
1614 bmp_info.x_pixels=(unsigned int) (100.0*image->x_resolution/2.54);
1615 bmp_info.y_pixels=(unsigned int) (100.0*image->y_resolution/2.54);
1616 break;
1617 }
1618 case PixelsPerCentimeterResolution:
1619 {
1620 bmp_info.x_pixels=(unsigned int) (100.0*image->x_resolution);
1621 bmp_info.y_pixels=(unsigned int) (100.0*image->y_resolution);
1622 break;
1623 }
1624 }
1625 bmp_info.colors_important=bmp_info.number_colors;
1626 /*
1627 Convert MIFF to BMP raster pixels.
1628 */
1629 pixels=(unsigned char *) AcquireQuantumMemory((size_t) bmp_info.image_size,
1630 sizeof(*pixels));
1631 if (pixels == (unsigned char *) NULL)
1632 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1633 (void) ResetMagickMemory(pixels,0,(size_t) bmp_info.image_size);
1634 switch (bmp_info.bits_per_pixel)
1635 {
1636 case 1:
1637 {
cristybb503372010-05-27 20:51:26 +00001638 size_t
cristy3ed852e2009-09-05 21:47:34 +00001639 bit,
1640 byte;
1641
1642 /*
1643 Convert PseudoClass image to a BMP monochrome image.
1644 */
cristybb503372010-05-27 20:51:26 +00001645 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001646 {
cristy4c08aed2011-07-01 19:47:50 +00001647 ssize_t
1648 offset;
1649
cristy1e178e72011-08-28 19:44:34 +00001650 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001651 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001652 break;
cristy3ed852e2009-09-05 21:47:34 +00001653 q=pixels+(image->rows-y-1)*bytes_per_line;
1654 bit=0;
1655 byte=0;
cristybb503372010-05-27 20:51:26 +00001656 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001657 {
1658 byte<<=1;
cristy4c08aed2011-07-01 19:47:50 +00001659 byte|=GetPixelIndex(image,p) != 0 ? 0x01 : 0x00;
cristy3ed852e2009-09-05 21:47:34 +00001660 bit++;
1661 if (bit == 8)
1662 {
1663 *q++=(unsigned char) byte;
1664 bit=0;
1665 byte=0;
1666 }
cristyed231572011-07-14 02:18:59 +00001667 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001668 }
1669 if (bit != 0)
1670 {
1671 *q++=(unsigned char) (byte << (8-bit));
1672 x++;
1673 }
cristy4c08aed2011-07-01 19:47:50 +00001674 offset=(ssize_t) (image->columns+7)/8;
1675 for (x=offset; x < (ssize_t) bytes_per_line; x++)
cristy3ed852e2009-09-05 21:47:34 +00001676 *q++=0x00;
1677 if (image->previous == (Image *) NULL)
1678 {
cristycee97112010-05-28 00:44:52 +00001679 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1680 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001681 if (status == MagickFalse)
1682 break;
1683 }
1684 }
1685 break;
1686 }
1687 case 4:
1688 {
cristybb503372010-05-27 20:51:26 +00001689 size_t
cristy4c08aed2011-07-01 19:47:50 +00001690 byte,
cristy3ed852e2009-09-05 21:47:34 +00001691 nibble,
cristy4c08aed2011-07-01 19:47:50 +00001692 offset;
cristy3ed852e2009-09-05 21:47:34 +00001693
1694 /*
1695 Convert PseudoClass image to a BMP monochrome image.
1696 */
cristybb503372010-05-27 20:51:26 +00001697 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001698 {
cristy1e178e72011-08-28 19:44:34 +00001699 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001700 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001701 break;
cristy3ed852e2009-09-05 21:47:34 +00001702 q=pixels+(image->rows-y-1)*bytes_per_line;
1703 nibble=0;
1704 byte=0;
cristybb503372010-05-27 20:51:26 +00001705 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001706 {
1707 byte<<=4;
cristy4c08aed2011-07-01 19:47:50 +00001708 byte|=((size_t) GetPixelIndex(image,p) & 0x0f);
cristy3ed852e2009-09-05 21:47:34 +00001709 nibble++;
1710 if (nibble == 2)
1711 {
1712 *q++=(unsigned char) byte;
1713 nibble=0;
1714 byte=0;
1715 }
cristyed231572011-07-14 02:18:59 +00001716 p+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001717 }
1718 if (nibble != 0)
1719 {
1720 *q++=(unsigned char) (byte << 4);
1721 x++;
1722 }
1723 offset=(ssize_t) (image->columns+1)/2;
1724 for (x=offset; x < (ssize_t) bytes_per_line; x++)
cristy3ed852e2009-09-05 21:47:34 +00001725 *q++=0x00;
1726 if (image->previous == (Image *) NULL)
1727 {
cristycee97112010-05-28 00:44:52 +00001728 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1729 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001730 if (status == MagickFalse)
1731 break;
1732 }
1733 }
1734 break;
1735 }
1736 case 8:
1737 {
1738 /*
1739 Convert PseudoClass packet to BMP pixel.
1740 */
cristybb503372010-05-27 20:51:26 +00001741 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001742 {
cristy1e178e72011-08-28 19:44:34 +00001743 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001744 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001745 break;
cristy3ed852e2009-09-05 21:47:34 +00001746 q=pixels+(image->rows-y-1)*bytes_per_line;
cristybb503372010-05-27 20:51:26 +00001747 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00001748 {
1749 *q++=(unsigned char) GetPixelIndex(image,p);
cristyed231572011-07-14 02:18:59 +00001750 p+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001751 }
cristybb503372010-05-27 20:51:26 +00001752 for ( ; x < (ssize_t) bytes_per_line; x++)
cristy3ed852e2009-09-05 21:47:34 +00001753 *q++=0x00;
1754 if (image->previous == (Image *) NULL)
1755 {
cristycee97112010-05-28 00:44:52 +00001756 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1757 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001758 if (status == MagickFalse)
1759 break;
1760 }
1761 }
1762 break;
1763 }
1764 case 24:
1765 {
1766 /*
1767 Convert DirectClass packet to BMP BGR888.
1768 */
cristybb503372010-05-27 20:51:26 +00001769 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001770 {
cristy1e178e72011-08-28 19:44:34 +00001771 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001772 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001773 break;
1774 q=pixels+(image->rows-y-1)*bytes_per_line;
cristybb503372010-05-27 20:51:26 +00001775 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001776 {
cristy4c08aed2011-07-01 19:47:50 +00001777 *q++=ScaleQuantumToChar(GetPixelBlue(image,p));
1778 *q++=ScaleQuantumToChar(GetPixelGreen(image,p));
1779 *q++=ScaleQuantumToChar(GetPixelRed(image,p));
cristyed231572011-07-14 02:18:59 +00001780 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001781 }
cristybb503372010-05-27 20:51:26 +00001782 for (x=3L*(ssize_t) image->columns; x < (ssize_t) bytes_per_line; x++)
cristy3ed852e2009-09-05 21:47:34 +00001783 *q++=0x00;
1784 if (image->previous == (Image *) NULL)
1785 {
cristycee97112010-05-28 00:44:52 +00001786 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1787 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001788 if (status == MagickFalse)
1789 break;
1790 }
1791 }
1792 break;
1793 }
1794 case 32:
1795 {
1796 /*
1797 Convert DirectClass packet to ARGB8888 pixel.
1798 */
cristybb503372010-05-27 20:51:26 +00001799 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001800 {
cristy1e178e72011-08-28 19:44:34 +00001801 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001802 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001803 break;
1804 q=pixels+(image->rows-y-1)*bytes_per_line;
cristybb503372010-05-27 20:51:26 +00001805 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001806 {
cristy4c08aed2011-07-01 19:47:50 +00001807 *q++=ScaleQuantumToChar(GetPixelBlue(image,p));
1808 *q++=ScaleQuantumToChar(GetPixelGreen(image,p));
1809 *q++=ScaleQuantumToChar(GetPixelRed(image,p));
1810 *q++=ScaleQuantumToChar(GetPixelAlpha(image,p));
cristyed231572011-07-14 02:18:59 +00001811 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001812 }
1813 if (image->previous == (Image *) NULL)
1814 {
cristycee97112010-05-28 00:44:52 +00001815 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1816 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001817 if (status == MagickFalse)
1818 break;
1819 }
1820 }
1821 break;
1822 }
1823 }
1824 if ((type > 2) && (bmp_info.bits_per_pixel == 8))
1825 if (image_info->compression != NoCompression)
1826 {
1827 size_t
1828 length;
1829
1830 /*
1831 Convert run-length encoded raster pixels.
1832 */
1833 length=(size_t) (2*(bytes_per_line+2)*(image->rows+2)+2);
1834 bmp_data=(unsigned char *) NULL;
1835 if (~length >= bytes_per_line)
1836 bmp_data=(unsigned char *) AcquireQuantumMemory(length+
1837 bytes_per_line,sizeof(*bmp_data));
1838 if (bmp_data == (unsigned char *) NULL)
1839 {
1840 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1841 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1842 }
1843 bmp_info.file_size-=bmp_info.image_size;
1844 bmp_info.image_size=(unsigned int) EncodeImage(image,bytes_per_line,
1845 pixels,bmp_data);
1846 bmp_info.file_size+=bmp_info.image_size;
1847 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1848 pixels=bmp_data;
1849 bmp_info.compression=BI_RLE8;
1850 }
1851 /*
1852 Write BMP for Windows, all versions, 14-byte header.
1853 */
1854 if (image->debug != MagickFalse)
1855 {
1856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001857 " Writing BMP version %.20g datastream",(double) type);
cristy3ed852e2009-09-05 21:47:34 +00001858 if (image->storage_class == DirectClass)
1859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1860 " Storage class=DirectClass");
1861 else
1862 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1863 " Storage class=PseudoClass");
1864 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001865 " Image depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00001866 if (image->matte != MagickFalse)
1867 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1868 " Matte=True");
1869 else
1870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1871 " Matte=MagickFalse");
1872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001873 " BMP bits_per_pixel=%.20g",(double) bmp_info.bits_per_pixel);
cristy3ed852e2009-09-05 21:47:34 +00001874 switch ((int) bmp_info.compression)
1875 {
1876 case BI_RGB:
1877 {
1878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1879 " Compression=BI_RGB");
1880 break;
1881 }
1882 case BI_RLE8:
1883 {
1884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1885 " Compression=BI_RLE8");
1886 break;
1887 }
1888 case BI_BITFIELDS:
1889 {
1890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1891 " Compression=BI_BITFIELDS");
1892 break;
1893 }
1894 default:
1895 {
1896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1897 " Compression=UNKNOWN (%u)",bmp_info.compression);
1898 break;
1899 }
1900 }
1901 if (bmp_info.number_colors == 0)
1902 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1903 " Number_colors=unspecified");
1904 else
1905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1906 " Number_colors=%u",bmp_info.number_colors);
1907 }
1908 (void) WriteBlob(image,2,(unsigned char *) "BM");
1909 (void) WriteBlobLSBLong(image,bmp_info.file_size);
1910 (void) WriteBlobLSBLong(image,bmp_info.ba_offset); /* always 0 */
1911 (void) WriteBlobLSBLong(image,bmp_info.offset_bits);
1912 if (type == 2)
1913 {
1914 /*
1915 Write 12-byte version 2 bitmap header.
1916 */
1917 (void) WriteBlobLSBLong(image,bmp_info.size);
1918 (void) WriteBlobLSBShort(image,(unsigned short) bmp_info.width);
1919 (void) WriteBlobLSBShort(image,(unsigned short) bmp_info.height);
1920 (void) WriteBlobLSBShort(image,bmp_info.planes);
1921 (void) WriteBlobLSBShort(image,bmp_info.bits_per_pixel);
1922 }
1923 else
1924 {
1925 /*
1926 Write 40-byte version 3+ bitmap header.
1927 */
1928 (void) WriteBlobLSBLong(image,bmp_info.size);
1929 (void) WriteBlobLSBLong(image,(unsigned int) bmp_info.width);
1930 (void) WriteBlobLSBLong(image,(unsigned int) bmp_info.height);
1931 (void) WriteBlobLSBShort(image,bmp_info.planes);
1932 (void) WriteBlobLSBShort(image,bmp_info.bits_per_pixel);
1933 (void) WriteBlobLSBLong(image,bmp_info.compression);
1934 (void) WriteBlobLSBLong(image,bmp_info.image_size);
1935 (void) WriteBlobLSBLong(image,bmp_info.x_pixels);
1936 (void) WriteBlobLSBLong(image,bmp_info.y_pixels);
1937 (void) WriteBlobLSBLong(image,bmp_info.number_colors);
1938 (void) WriteBlobLSBLong(image,bmp_info.colors_important);
1939 }
1940 if ((type > 3) && ((image->matte != MagickFalse) ||
1941 (have_color_info != MagickFalse)))
1942 {
1943 /*
1944 Write the rest of the 108-byte BMP Version 4 header.
1945 */
1946 (void) WriteBlobLSBLong(image,0x00ff0000U); /* Red mask */
1947 (void) WriteBlobLSBLong(image,0x0000ff00U); /* Green mask */
1948 (void) WriteBlobLSBLong(image,0x000000ffU); /* Blue mask */
1949 (void) WriteBlobLSBLong(image,0xff000000U); /* Alpha mask */
1950 (void) WriteBlobLSBLong(image,0x00000001U); /* CSType==Calib. RGB */
1951 (void) WriteBlobLSBLong(image,(unsigned int)
1952 image->chromaticity.red_primary.x*0x3ffffff);
1953 (void) WriteBlobLSBLong(image,(unsigned int)
1954 image->chromaticity.red_primary.y*0x3ffffff);
1955 (void) WriteBlobLSBLong(image,(unsigned int)
1956 (1.000f-(image->chromaticity.red_primary.x+
1957 image->chromaticity.red_primary.y)*0x3ffffff));
1958 (void) WriteBlobLSBLong(image,(unsigned int)
1959 image->chromaticity.green_primary.x*0x3ffffff);
1960 (void) WriteBlobLSBLong(image,(unsigned int)
1961 image->chromaticity.green_primary.y*0x3ffffff);
1962 (void) WriteBlobLSBLong(image,(unsigned int)
1963 (1.000f-(image->chromaticity.green_primary.x+
1964 image->chromaticity.green_primary.y)*0x3ffffff));
1965 (void) WriteBlobLSBLong(image,(unsigned int)
1966 image->chromaticity.blue_primary.x*0x3ffffff);
1967 (void) WriteBlobLSBLong(image,(unsigned int)
1968 image->chromaticity.blue_primary.y*0x3ffffff);
1969 (void) WriteBlobLSBLong(image,(unsigned int)
1970 (1.000f-(image->chromaticity.blue_primary.x+
1971 image->chromaticity.blue_primary.y)*0x3ffffff));
1972 (void) WriteBlobLSBLong(image,(unsigned int)
1973 bmp_info.gamma_scale.x*0xffff);
1974 (void) WriteBlobLSBLong(image,(unsigned int)
1975 bmp_info.gamma_scale.y*0xffff);
1976 (void) WriteBlobLSBLong(image,(unsigned int)
1977 bmp_info.gamma_scale.z*0xffff);
1978 if ((image->rendering_intent != UndefinedIntent) ||
1979 (profile != (StringInfo *) NULL))
1980 {
cristybb503372010-05-27 20:51:26 +00001981 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001982 intent;
1983
1984 switch ((int) image->rendering_intent)
1985 {
1986 case SaturationIntent:
1987 {
1988 intent=LCS_GM_BUSINESS;
1989 break;
1990 }
1991 case RelativeIntent:
1992 {
1993 intent=LCS_GM_GRAPHICS;
1994 break;
1995 }
1996 case PerceptualIntent:
1997 {
1998 intent=LCS_GM_IMAGES;
1999 break;
2000 }
2001 case AbsoluteIntent:
2002 {
2003 intent=LCS_GM_ABS_COLORIMETRIC;
2004 break;
2005 }
2006 default:
2007 {
2008 intent=0;
2009 break;
2010 }
2011 }
2012 (void) WriteBlobLSBLong(image,(unsigned int) intent);
2013 (void) WriteBlobLSBLong(image,0x00); /* dummy profile data */
2014 (void) WriteBlobLSBLong(image,0x00); /* dummy profile length */
2015 (void) WriteBlobLSBLong(image,0x00); /* reserved */
2016 }
2017 }
2018 if (image->storage_class == PseudoClass)
2019 {
2020 unsigned char
2021 *bmp_colormap;
2022
2023 /*
2024 Dump colormap to file.
2025 */
2026 if (image->debug != MagickFalse)
2027 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002028 " Colormap: %.20g entries",(double) image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002029 bmp_colormap=(unsigned char *) AcquireQuantumMemory((size_t) (1UL <<
2030 bmp_info.bits_per_pixel),4*sizeof(*bmp_colormap));
2031 if (bmp_colormap == (unsigned char *) NULL)
2032 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2033 q=bmp_colormap;
cristybb503372010-05-27 20:51:26 +00002034 for (i=0; i < (ssize_t) MagickMin((ssize_t) image->colors,(ssize_t) bmp_info.number_colors); i++)
cristy3ed852e2009-09-05 21:47:34 +00002035 {
2036 *q++=ScaleQuantumToChar(image->colormap[i].blue);
2037 *q++=ScaleQuantumToChar(image->colormap[i].green);
2038 *q++=ScaleQuantumToChar(image->colormap[i].red);
2039 if (type > 2)
2040 *q++=(unsigned char) 0x0;
2041 }
cristybb503372010-05-27 20:51:26 +00002042 for ( ; i < (ssize_t) (1UL << bmp_info.bits_per_pixel); i++)
cristy3ed852e2009-09-05 21:47:34 +00002043 {
2044 *q++=(unsigned char) 0x00;
2045 *q++=(unsigned char) 0x00;
2046 *q++=(unsigned char) 0x00;
2047 if (type > 2)
2048 *q++=(unsigned char) 0x00;
2049 }
2050 if (type <= 2)
2051 (void) WriteBlob(image,(size_t) (3*(1L << bmp_info.bits_per_pixel)),
2052 bmp_colormap);
2053 else
2054 (void) WriteBlob(image,(size_t) (4*(1L << bmp_info.bits_per_pixel)),
2055 bmp_colormap);
2056 bmp_colormap=(unsigned char *) RelinquishMagickMemory(bmp_colormap);
2057 }
2058 if (image->debug != MagickFalse)
2059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2060 " Pixels: %u bytes",bmp_info.image_size);
2061 (void) WriteBlob(image,(size_t) bmp_info.image_size,pixels);
2062 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
2063 if (GetNextImageInList(image) == (Image *) NULL)
2064 break;
2065 image=SyncNextImageInList(image);
2066 status=SetImageProgress(image,SaveImagesTag,scene++,
2067 GetImageListLength(image));
2068 if (status == MagickFalse)
2069 break;
2070 } while (image_info->adjoin != MagickFalse);
2071 (void) CloseBlob(image);
2072 return(MagickTrue);
2073}