blob: 5c75926afe1237a3ee3967adb1d4134a28bafd21 [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 %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% Glenn Randers-Pehrson %
18% December 2001 %
19% %
20% %
Cristy7ce65e72015-12-12 18:03:16 -050021% Copyright 1999-2016 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"
cristye462ed72013-08-13 12:20:40 +000062#include "MagickCore/option.h"
cristy4c08aed2011-07-01 19:47:50 +000063#include "MagickCore/pixel-accessor.h"
64#include "MagickCore/profile.h"
65#include "MagickCore/quantum-private.h"
66#include "MagickCore/static.h"
67#include "MagickCore/string_.h"
68#include "MagickCore/module.h"
69#include "MagickCore/transform.h"
cristy3ed852e2009-09-05 21:47:34 +000070
71/*
72 Macro definitions (from Windows wingdi.h).
73*/
74#undef BI_JPEG
75#define BI_JPEG 4
76#undef BI_PNG
77#define BI_PNG 5
cristy07a3cca2012-12-10 13:09:10 +000078#if !defined(MAGICKCORE_WINDOWS_SUPPORT) || defined(__MINGW32__) || defined(__MINGW64__)
cristy3a99dcf2011-12-17 01:29:40 +000079#undef BI_RGB
cristy3ed852e2009-09-05 21:47:34 +000080#define BI_RGB 0
cristy3a99dcf2011-12-17 01:29:40 +000081#undef BI_RLE8
cristy3ed852e2009-09-05 21:47:34 +000082#define BI_RLE8 1
cristy3a99dcf2011-12-17 01:29:40 +000083#undef BI_RLE4
cristy3ed852e2009-09-05 21:47:34 +000084#define BI_RLE4 2
cristy3a99dcf2011-12-17 01:29:40 +000085#undef BI_BITFIELDS
cristy3ed852e2009-09-05 21:47:34 +000086#define BI_BITFIELDS 3
87
cristy3a99dcf2011-12-17 01:29:40 +000088#undef LCS_CALIBRATED_RBG
cristy3ed852e2009-09-05 21:47:34 +000089#define LCS_CALIBRATED_RBG 0
cristy3a99dcf2011-12-17 01:29:40 +000090#undef LCS_sRGB
cristy3ed852e2009-09-05 21:47:34 +000091#define LCS_sRGB 1
cristy3a99dcf2011-12-17 01:29:40 +000092#undef LCS_WINDOWS_COLOR_SPACE
cristy3ed852e2009-09-05 21:47:34 +000093#define LCS_WINDOWS_COLOR_SPACE 2
cristy3a99dcf2011-12-17 01:29:40 +000094#undef PROFILE_LINKED
cristy3ed852e2009-09-05 21:47:34 +000095#define PROFILE_LINKED 3
cristy3a99dcf2011-12-17 01:29:40 +000096#undef PROFILE_EMBEDDED
cristy3ed852e2009-09-05 21:47:34 +000097#define PROFILE_EMBEDDED 4
98
cristy3a99dcf2011-12-17 01:29:40 +000099#undef LCS_GM_BUSINESS
cristy3ed852e2009-09-05 21:47:34 +0000100#define LCS_GM_BUSINESS 1 /* Saturation */
cristy3a99dcf2011-12-17 01:29:40 +0000101#undef LCS_GM_GRAPHICS
cristy3ed852e2009-09-05 21:47:34 +0000102#define LCS_GM_GRAPHICS 2 /* Relative */
cristy3a99dcf2011-12-17 01:29:40 +0000103#undef LCS_GM_IMAGES
cristy3ed852e2009-09-05 21:47:34 +0000104#define LCS_GM_IMAGES 4 /* Perceptual */
cristy3a99dcf2011-12-17 01:29:40 +0000105#undef LCS_GM_ABS_COLORIMETRIC
cristy3ed852e2009-09-05 21:47:34 +0000106#define LCS_GM_ABS_COLORIMETRIC 8 /* Absolute */
107#endif
108
109/*
110 Typedef declarations.
111*/
112typedef struct _BMPInfo
113{
cristydf409582015-06-12 16:35:04 +0000114 unsigned long
cristy3ed852e2009-09-05 21:47:34 +0000115 file_size,
116 ba_offset,
117 offset_bits,
118 size;
119
cristy7c242ea2013-06-21 17:19:53 +0000120 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000121 width,
122 height;
123
124 unsigned short
125 planes,
126 bits_per_pixel;
127
cristydf409582015-06-12 16:35:04 +0000128 unsigned long
cristy3ed852e2009-09-05 21:47:34 +0000129 compression,
130 image_size,
131 x_pixels,
132 y_pixels,
133 number_colors,
134 red_mask,
135 green_mask,
136 blue_mask,
137 alpha_mask,
138 colors_important;
139
cristydf409582015-06-12 16:35:04 +0000140 long
cristy3ed852e2009-09-05 21:47:34 +0000141 colorspace;
142
143 PrimaryInfo
144 red_primary,
145 green_primary,
146 blue_primary,
147 gamma_scale;
148} BMPInfo;
149
150/*
151 Forward declarations.
152*/
153static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000154 WriteBMPImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000155
156/*
157%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
158% %
159% %
160% %
161% D e c o d e I m a g e %
162% %
163% %
164% %
165%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
166%
167% DecodeImage unpacks the packed image pixels into runlength-encoded
168% pixel packets.
169%
170% The format of the DecodeImage method is:
171%
172% MagickBooleanType DecodeImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000173% const size_t compression,unsigned char *pixels)
cristy3ed852e2009-09-05 21:47:34 +0000174%
175% A description of each parameter follows:
176%
177% o image: the address of a structure of type Image.
178%
179% o compression: Zero means uncompressed. A value of 1 means the
180% compressed pixels are runlength encoded for a 256-color bitmap.
181% A value of 2 means a 16-color bitmap. A value of 3 means bitfields
182% encoding.
183%
184% o pixels: The address of a byte (8 bits) array of pixel data created by
185% the decoding process.
186%
187*/
cristy4e82e512011-04-24 01:33:42 +0000188static MagickBooleanType DecodeImage(Image *image,const size_t compression,
189 unsigned char *pixels)
cristy3ed852e2009-09-05 21:47:34 +0000190{
cristy32e04a32013-06-16 00:48:49 +0000191 int
192 count;
193
cristybb503372010-05-27 20:51:26 +0000194 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000195 i,
196 x;
197
198 register unsigned char
199 *p,
200 *q;
201
cristy4e82e512011-04-24 01:33:42 +0000202 ssize_t
203 y;
204
cristy3ed852e2009-09-05 21:47:34 +0000205 unsigned char
206 byte;
207
208 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000209 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000210 if (image->debug != MagickFalse)
211 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
212 assert(pixels != (unsigned char *) NULL);
213 (void) ResetMagickMemory(pixels,0,(size_t) image->columns*image->rows*
214 sizeof(*pixels));
215 byte=0;
216 x=0;
217 p=pixels;
218 q=pixels+(size_t) image->columns*image->rows;
cristybb503372010-05-27 20:51:26 +0000219 for (y=0; y < (ssize_t) image->rows; )
cristy3ed852e2009-09-05 21:47:34 +0000220 {
cristy311dbd72015-02-26 23:05:17 +0000221 MagickBooleanType
222 status;
223
224 if ((p < pixels) || (p > q))
cristy3ed852e2009-09-05 21:47:34 +0000225 break;
cristy32e04a32013-06-16 00:48:49 +0000226 count=ReadBlobByte(image);
227 if (count == EOF)
cristy3ed852e2009-09-05 21:47:34 +0000228 break;
229 if (count != 0)
230 {
231 /*
232 Encoded mode.
233 */
cristya321eb72013-06-23 10:42:37 +0000234 count=(int) MagickMin((ssize_t) count,(ssize_t) (q-p));
cristy3ed852e2009-09-05 21:47:34 +0000235 byte=(unsigned char) ReadBlobByte(image);
236 if (compression == BI_RLE8)
237 {
cristya321eb72013-06-23 10:42:37 +0000238 for (i=0; i < (ssize_t) count; i++)
cristy3ed852e2009-09-05 21:47:34 +0000239 *p++=(unsigned char) byte;
240 }
241 else
242 {
cristya321eb72013-06-23 10:42:37 +0000243 for (i=0; i < (ssize_t) count; i++)
cristy3ed852e2009-09-05 21:47:34 +0000244 *p++=(unsigned char)
245 ((i & 0x01) != 0 ? (byte & 0x0f) : ((byte >> 4) & 0x0f));
246 }
247 x+=count;
248 }
249 else
250 {
251 /*
252 Escape mode.
253 */
cristy32e04a32013-06-16 00:48:49 +0000254 count=ReadBlobByte(image);
cristy311dbd72015-02-26 23:05:17 +0000255 if (count == EOF)
256 break;
cristy3ed852e2009-09-05 21:47:34 +0000257 if (count == 0x01)
258 return(MagickTrue);
259 switch (count)
260 {
261 case 0x00:
262 {
263 /*
264 End of line.
265 */
266 x=0;
267 y++;
268 p=pixels+y*image->columns;
269 break;
270 }
271 case 0x02:
272 {
273 /*
274 Delta mode.
275 */
276 x+=ReadBlobByte(image);
277 y+=ReadBlobByte(image);
278 p=pixels+y*image->columns+x;
279 break;
280 }
281 default:
282 {
283 /*
284 Absolute mode.
285 */
cristya321eb72013-06-23 10:42:37 +0000286 count=(int) MagickMin((ssize_t) count,(ssize_t) (q-p));
cristy3ed852e2009-09-05 21:47:34 +0000287 if (compression == BI_RLE8)
cristya321eb72013-06-23 10:42:37 +0000288 for (i=0; i < (ssize_t) count; i++)
cristy3ed852e2009-09-05 21:47:34 +0000289 *p++=(unsigned char) ReadBlobByte(image);
290 else
cristya321eb72013-06-23 10:42:37 +0000291 for (i=0; i < (ssize_t) count; i++)
cristy3ed852e2009-09-05 21:47:34 +0000292 {
293 if ((i & 0x01) == 0)
294 byte=(unsigned char) ReadBlobByte(image);
295 *p++=(unsigned char)
296 ((i & 0x01) != 0 ? (byte & 0x0f) : ((byte >> 4) & 0x0f));
297 }
298 x+=count;
299 /*
300 Read pad byte.
301 */
302 if (compression == BI_RLE8)
303 {
304 if ((count & 0x01) != 0)
305 (void) ReadBlobByte(image);
306 }
307 else
308 if (((count & 0x03) == 1) || ((count & 0x03) == 2))
309 (void) ReadBlobByte(image);
310 break;
311 }
312 }
313 }
cristy311dbd72015-02-26 23:05:17 +0000314 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
315 image->rows);
316 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000317 break;
318 }
319 (void) ReadBlobByte(image); /* end of line */
320 (void) ReadBlobByte(image);
cristy311dbd72015-02-26 23:05:17 +0000321 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +0000322}
323
324/*
325%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
326% %
327% %
328% %
329% E n c o d e I m a g e %
330% %
331% %
332% %
333%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
334%
335% EncodeImage compresses pixels using a runlength encoded format.
336%
337% The format of the EncodeImage method is:
338%
339% static MagickBooleanType EncodeImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000340% const size_t bytes_per_line,const unsigned char *pixels,
cristy3ed852e2009-09-05 21:47:34 +0000341% unsigned char *compressed_pixels)
342%
343% A description of each parameter follows:
344%
345% o image: The image.
346%
347% o bytes_per_line: the number of bytes in a scanline of compressed pixels
348%
349% o pixels: The address of a byte (8 bits) array of pixel data created by
350% the compression process.
351%
352% o compressed_pixels: The address of a byte (8 bits) array of compressed
353% pixel data.
354%
355*/
cristybb503372010-05-27 20:51:26 +0000356static size_t EncodeImage(Image *image,const size_t bytes_per_line,
cristy3ed852e2009-09-05 21:47:34 +0000357 const unsigned char *pixels,unsigned char *compressed_pixels)
358{
cristy3ed852e2009-09-05 21:47:34 +0000359 MagickBooleanType
360 status;
361
362 register const unsigned char
363 *p;
364
cristybb503372010-05-27 20:51:26 +0000365 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000366 i,
367 x;
368
369 register unsigned char
370 *q;
371
cristy4e82e512011-04-24 01:33:42 +0000372 ssize_t
373 y;
374
cristy3ed852e2009-09-05 21:47:34 +0000375 /*
376 Runlength encode pixels.
377 */
378 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000379 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000380 if (image->debug != MagickFalse)
381 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
382 assert(pixels != (const unsigned char *) NULL);
383 assert(compressed_pixels != (unsigned char *) NULL);
384 p=pixels;
385 q=compressed_pixels;
386 i=0;
cristybb503372010-05-27 20:51:26 +0000387 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000388 {
cristybb503372010-05-27 20:51:26 +0000389 for (x=0; x < (ssize_t) bytes_per_line; x+=i)
cristy3ed852e2009-09-05 21:47:34 +0000390 {
391 /*
392 Determine runlength.
393 */
cristybb503372010-05-27 20:51:26 +0000394 for (i=1; ((x+i) < (ssize_t) bytes_per_line); i++)
cristy3ed852e2009-09-05 21:47:34 +0000395 if ((i == 255) || (*(p+i) != *p))
396 break;
397 *q++=(unsigned char) i;
398 *q++=(*p);
399 p+=i;
400 }
401 /*
402 End of line.
403 */
404 *q++=(unsigned char) 0x00;
405 *q++=(unsigned char) 0x00;
cristycee97112010-05-28 00:44:52 +0000406 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy4e82e512011-04-24 01:33:42 +0000407 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000408 if (status == MagickFalse)
409 break;
410 }
411 /*
412 End of bitmap.
413 */
414 *q++=(unsigned char) 0x00;
415 *q++=(unsigned char) 0x01;
416 return((size_t) (q-compressed_pixels));
417}
418
419/*
420%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
421% %
422% %
423% %
424% I s B M P %
425% %
426% %
427% %
428%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
429%
430% IsBMP() returns MagickTrue if the image format type, identified by the
431% magick string, is BMP.
432%
433% The format of the IsBMP method is:
434%
435% MagickBooleanType IsBMP(const unsigned char *magick,const size_t length)
436%
437% A description of each parameter follows:
438%
439% o magick: compare image format pattern against these bytes.
440%
441% o length: Specifies the length of the magick string.
442%
443*/
444static MagickBooleanType IsBMP(const unsigned char *magick,const size_t length)
445{
446 if (length < 2)
447 return(MagickFalse);
448 if ((LocaleNCompare((char *) magick,"BA",2) == 0) ||
449 (LocaleNCompare((char *) magick,"BM",2) == 0) ||
450 (LocaleNCompare((char *) magick,"IC",2) == 0) ||
451 (LocaleNCompare((char *) magick,"PI",2) == 0) ||
452 (LocaleNCompare((char *) magick,"CI",2) == 0) ||
453 (LocaleNCompare((char *) magick,"CP",2) == 0))
454 return(MagickTrue);
455 return(MagickFalse);
456}
457
458/*
459%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
460% %
461% %
462% %
463% R e a d B M P I m a g e %
464% %
465% %
466% %
467%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
468%
469% ReadBMPImage() reads a Microsoft Windows bitmap image file, Version
470% 2, 3 (for Windows or NT), or 4, and returns it. It allocates the memory
471% necessary for the new Image structure and returns a pointer to the new
472% image.
473%
474% The format of the ReadBMPImage method is:
475%
476% image=ReadBMPImage(image_info)
477%
478% A description of each parameter follows:
479%
480% o image_info: the image info.
481%
482% o exception: return any errors or warnings in this structure.
483%
484*/
485
486static Image *ReadBMPImage(const ImageInfo *image_info,ExceptionInfo *exception)
487{
488 BMPInfo
489 bmp_info;
490
491 Image
492 *image;
493
cristy3ed852e2009-09-05 21:47:34 +0000494 MagickBooleanType
495 status;
496
497 MagickOffsetType
498 offset,
499 start_position;
500
cristya321eb72013-06-23 10:42:37 +0000501 MemoryInfo
cristy0553bd52013-06-30 15:53:50 +0000502 *pixel_info;
cristya321eb72013-06-23 10:42:37 +0000503
cristy4c08aed2011-07-01 19:47:50 +0000504 Quantum
505 index;
cristy3ed852e2009-09-05 21:47:34 +0000506
cristy4c08aed2011-07-01 19:47:50 +0000507 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000508 *q;
509
cristybb503372010-05-27 20:51:26 +0000510 register ssize_t
cristy0da03562011-04-23 23:23:21 +0000511 i,
512 x;
cristy3ed852e2009-09-05 21:47:34 +0000513
514 register unsigned char
515 *p;
516
cristybb503372010-05-27 20:51:26 +0000517 size_t
cristy3ed852e2009-09-05 21:47:34 +0000518 bit,
519 blue,
520 bytes_per_line,
521 green,
cristy0da03562011-04-23 23:23:21 +0000522 length,
cristy3ed852e2009-09-05 21:47:34 +0000523 red;
524
cristy0da03562011-04-23 23:23:21 +0000525 ssize_t
cristy4e82e512011-04-24 01:33:42 +0000526 count,
527 y;
cristy0da03562011-04-23 23:23:21 +0000528
529 unsigned char
530 magick[12],
531 *pixels;
532
cristy3ed852e2009-09-05 21:47:34 +0000533 /*
534 Open image file.
535 */
536 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000537 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000538 if (image_info->debug != MagickFalse)
539 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
540 image_info->filename);
541 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000542 assert(exception->signature == MagickCoreSignature);
cristy9950d572011-10-01 18:22:35 +0000543 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000544 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
545 if (status == MagickFalse)
546 {
547 image=DestroyImageList(image);
548 return((Image *) NULL);
549 }
550 /*
551 Determine if this a BMP file.
552 */
553 (void) ResetMagickMemory(&bmp_info,0,sizeof(bmp_info));
554 bmp_info.ba_offset=0;
555 start_position=0;
556 count=ReadBlob(image,2,magick);
cristy771c8842015-01-09 12:13:22 +0000557 if (count != 2)
558 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristy3ed852e2009-09-05 21:47:34 +0000559 do
560 {
cristy101ab702011-10-13 13:06:32 +0000561 PixelInfo
562 quantum_bits;
cristy3ed852e2009-09-05 21:47:34 +0000563
564 PixelPacket
cristy101ab702011-10-13 13:06:32 +0000565 shift;
cristy3ed852e2009-09-05 21:47:34 +0000566
cristybb503372010-05-27 20:51:26 +0000567 size_t
cristy3ed852e2009-09-05 21:47:34 +0000568 profile_data,
569 profile_size;
570
571 /*
572 Verify BMP identifier.
573 */
574 if (bmp_info.ba_offset == 0)
575 start_position=TellBlob(image)-2;
576 bmp_info.ba_offset=0;
577 while (LocaleNCompare((char *) magick,"BA",2) == 0)
578 {
579 bmp_info.file_size=ReadBlobLSBLong(image);
580 bmp_info.ba_offset=ReadBlobLSBLong(image);
581 bmp_info.offset_bits=ReadBlobLSBLong(image);
582 count=ReadBlob(image,2,magick);
583 if (count != 2)
584 break;
585 }
586 if (image->debug != MagickFalse)
587 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Magick: %c%c",
588 magick[0],magick[1]);
cristy6af1e972015-01-06 12:46:27 +0000589 if ((count != 2) || ((LocaleNCompare((char *) magick,"BM",2) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +0000590 (LocaleNCompare((char *) magick,"CI",2) != 0)))
591 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
592 bmp_info.file_size=ReadBlobLSBLong(image);
593 (void) ReadBlobLSBLong(image);
594 bmp_info.offset_bits=ReadBlobLSBLong(image);
595 bmp_info.size=ReadBlobLSBLong(image);
596 if (image->debug != MagickFalse)
cristydf409582015-06-12 16:35:04 +0000597 (void) LogMagickEvent(CoderEvent,GetMagickModule()," BMP size: %lu",
cristy3ed852e2009-09-05 21:47:34 +0000598 bmp_info.size);
599 if (bmp_info.size == 12)
600 {
601 /*
602 OS/2 BMP image file.
603 */
cristy151b66d2015-04-15 10:50:31 +0000604 (void) CopyMagickString(image->magick,"BMP2",MagickPathExtent);
cristya321eb72013-06-23 10:42:37 +0000605 bmp_info.width=(ssize_t) ((short) ReadBlobLSBShort(image));
606 bmp_info.height=(ssize_t) ((short) ReadBlobLSBShort(image));
cristy3ed852e2009-09-05 21:47:34 +0000607 bmp_info.planes=ReadBlobLSBShort(image);
608 bmp_info.bits_per_pixel=ReadBlobLSBShort(image);
609 bmp_info.x_pixels=0;
610 bmp_info.y_pixels=0;
611 bmp_info.number_colors=0;
612 bmp_info.compression=BI_RGB;
613 bmp_info.image_size=0;
614 bmp_info.alpha_mask=0;
615 if (image->debug != MagickFalse)
616 {
617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
618 " Format: OS/2 Bitmap");
619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000620 " Geometry: %.20gx%.20g",(double) bmp_info.width,(double)
621 bmp_info.height);
cristy3ed852e2009-09-05 21:47:34 +0000622 }
623 }
624 else
625 {
626 /*
627 Microsoft Windows BMP image file.
628 */
629 if (bmp_info.size < 40)
630 ThrowReaderException(CorruptImageError,"NonOS2HeaderSizeError");
cristya321eb72013-06-23 10:42:37 +0000631 bmp_info.width=(ssize_t) ((int) ReadBlobLSBLong(image));
632 bmp_info.height=(ssize_t) ((int) ReadBlobLSBLong(image));
cristy3ed852e2009-09-05 21:47:34 +0000633 bmp_info.planes=ReadBlobLSBShort(image);
634 bmp_info.bits_per_pixel=ReadBlobLSBShort(image);
635 bmp_info.compression=ReadBlobLSBLong(image);
636 bmp_info.image_size=ReadBlobLSBLong(image);
637 bmp_info.x_pixels=ReadBlobLSBLong(image);
638 bmp_info.y_pixels=ReadBlobLSBLong(image);
639 bmp_info.number_colors=ReadBlobLSBLong(image);
640 bmp_info.colors_important=ReadBlobLSBLong(image);
641 profile_data=0;
642 profile_size=0;
643 if (image->debug != MagickFalse)
644 {
645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
646 " Format: MS Windows bitmap");
647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000648 " Geometry: %.20gx%.20g",(double) bmp_info.width,(double)
649 bmp_info.height);
cristy3ed852e2009-09-05 21:47:34 +0000650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000651 " Bits per pixel: %.20g",(double) bmp_info.bits_per_pixel);
cristy3ed852e2009-09-05 21:47:34 +0000652 switch ((int) bmp_info.compression)
653 {
654 case BI_RGB:
655 {
656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
657 " Compression: BI_RGB");
658 break;
659 }
660 case BI_RLE4:
661 {
662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
663 " Compression: BI_RLE4");
664 break;
665 }
666 case BI_RLE8:
667 {
668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
669 " Compression: BI_RLE8");
670 break;
671 }
672 case BI_BITFIELDS:
673 {
674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
675 " Compression: BI_BITFIELDS");
676 break;
677 }
678 case BI_PNG:
679 {
680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
681 " Compression: BI_PNG");
682 break;
683 }
684 case BI_JPEG:
685 {
686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
687 " Compression: BI_JPEG");
688 break;
689 }
690 default:
691 {
692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristydf409582015-06-12 16:35:04 +0000693 " Compression: UNKNOWN (%lu)",bmp_info.compression);
cristy3ed852e2009-09-05 21:47:34 +0000694 }
695 }
696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristydf409582015-06-12 16:35:04 +0000697 " Number of colors: %lu",bmp_info.number_colors);
cristy3ed852e2009-09-05 21:47:34 +0000698 }
699 bmp_info.red_mask=ReadBlobLSBLong(image);
700 bmp_info.green_mask=ReadBlobLSBLong(image);
701 bmp_info.blue_mask=ReadBlobLSBLong(image);
702 if (bmp_info.size > 40)
703 {
704 double
cristydf409582015-06-12 16:35:04 +0000705 gamma;
cristy3ed852e2009-09-05 21:47:34 +0000706
707 /*
708 Read color management information.
709 */
710 bmp_info.alpha_mask=ReadBlobLSBLong(image);
cristy6cff05d2010-09-02 11:22:46 +0000711 bmp_info.colorspace=(int) ReadBlobLSBLong(image);
cristy3ed852e2009-09-05 21:47:34 +0000712 /*
713 Decode 2^30 fixed point formatted CIE primaries.
714 */
glennrp34ea3d32012-11-22 03:57:24 +0000715# define BMP_DENOM ((double) 0x40000000)
716 bmp_info.red_primary.x=(double) ReadBlobLSBLong(image)/BMP_DENOM;
717 bmp_info.red_primary.y=(double) ReadBlobLSBLong(image)/BMP_DENOM;
718 bmp_info.red_primary.z=(double) ReadBlobLSBLong(image)/BMP_DENOM;
719 bmp_info.green_primary.x=(double) ReadBlobLSBLong(image)/BMP_DENOM;
720 bmp_info.green_primary.y=(double) ReadBlobLSBLong(image)/BMP_DENOM;
721 bmp_info.green_primary.z=(double) ReadBlobLSBLong(image)/BMP_DENOM;
722 bmp_info.blue_primary.x=(double) ReadBlobLSBLong(image)/BMP_DENOM;
723 bmp_info.blue_primary.y=(double) ReadBlobLSBLong(image)/BMP_DENOM;
724 bmp_info.blue_primary.z=(double) ReadBlobLSBLong(image)/BMP_DENOM;
725
cristydf409582015-06-12 16:35:04 +0000726 gamma=bmp_info.red_primary.x+bmp_info.red_primary.y+
cristy3ed852e2009-09-05 21:47:34 +0000727 bmp_info.red_primary.z;
cristydf409582015-06-12 16:35:04 +0000728 gamma=PerceptibleReciprocal(gamma);
729 bmp_info.red_primary.x*=gamma;
730 bmp_info.red_primary.y*=gamma;
cristy4629d112012-11-22 02:25:54 +0000731 image->chromaticity.red_primary.x=bmp_info.red_primary.x;
732 image->chromaticity.red_primary.y=bmp_info.red_primary.y;
glennrp34ea3d32012-11-22 03:57:24 +0000733
cristydf409582015-06-12 16:35:04 +0000734 gamma=bmp_info.green_primary.x+bmp_info.green_primary.y+
cristy3ed852e2009-09-05 21:47:34 +0000735 bmp_info.green_primary.z;
cristydf409582015-06-12 16:35:04 +0000736 gamma=PerceptibleReciprocal(gamma);
737 bmp_info.green_primary.x*=gamma;
738 bmp_info.green_primary.y*=gamma;
cristy4629d112012-11-22 02:25:54 +0000739 image->chromaticity.green_primary.x=bmp_info.green_primary.x;
740 image->chromaticity.green_primary.y=bmp_info.green_primary.y;
glennrp34ea3d32012-11-22 03:57:24 +0000741
cristydf409582015-06-12 16:35:04 +0000742 gamma=bmp_info.blue_primary.x+bmp_info.blue_primary.y+
cristy3ed852e2009-09-05 21:47:34 +0000743 bmp_info.blue_primary.z;
cristydf409582015-06-12 16:35:04 +0000744 gamma=PerceptibleReciprocal(gamma);
745 bmp_info.blue_primary.x*=gamma;
746 bmp_info.blue_primary.y*=gamma;
cristy4629d112012-11-22 02:25:54 +0000747 image->chromaticity.blue_primary.x=bmp_info.blue_primary.x;
748 image->chromaticity.blue_primary.y=bmp_info.blue_primary.y;
glennrp34ea3d32012-11-22 03:57:24 +0000749
cristy3ed852e2009-09-05 21:47:34 +0000750 /*
751 Decode 16^16 fixed point formatted gamma_scales.
752 */
cristyc554cc82012-08-12 19:55:34 +0000753 bmp_info.gamma_scale.x=(double) ReadBlobLSBLong(image)/0x10000;
754 bmp_info.gamma_scale.y=(double) ReadBlobLSBLong(image)/0x10000;
755 bmp_info.gamma_scale.z=(double) ReadBlobLSBLong(image)/0x10000;
cristy3ed852e2009-09-05 21:47:34 +0000756 /*
757 Compute a single gamma from the BMP 3-channel gamma.
758 */
759 image->gamma=(bmp_info.gamma_scale.x+bmp_info.gamma_scale.y+
760 bmp_info.gamma_scale.z)/3.0;
761 }
dirk5816af42014-09-14 08:53:21 +0000762 else
cristy151b66d2015-04-15 10:50:31 +0000763 (void) CopyMagickString(image->magick,"BMP3",MagickPathExtent);
glennrp34ea3d32012-11-22 03:57:24 +0000764
cristy3ed852e2009-09-05 21:47:34 +0000765 if (bmp_info.size > 108)
766 {
cristybb503372010-05-27 20:51:26 +0000767 size_t
cristy3ed852e2009-09-05 21:47:34 +0000768 intent;
769
770 /*
771 Read BMP Version 5 color management information.
772 */
773 intent=ReadBlobLSBLong(image);
774 switch ((int) intent)
775 {
776 case LCS_GM_BUSINESS:
777 {
778 image->rendering_intent=SaturationIntent;
779 break;
780 }
781 case LCS_GM_GRAPHICS:
782 {
783 image->rendering_intent=RelativeIntent;
784 break;
785 }
786 case LCS_GM_IMAGES:
787 {
788 image->rendering_intent=PerceptualIntent;
789 break;
790 }
791 case LCS_GM_ABS_COLORIMETRIC:
792 {
793 image->rendering_intent=AbsoluteIntent;
794 break;
795 }
796 }
797 profile_data=ReadBlobLSBLong(image);
798 profile_size=ReadBlobLSBLong(image);
cristyda16f162011-02-19 23:52:17 +0000799 (void) profile_data;
800 (void) profile_size;
cristy3ed852e2009-09-05 21:47:34 +0000801 (void) ReadBlobLSBLong(image); /* Reserved byte */
802 }
803 }
cristy59eefcf2011-02-01 15:54:38 +0000804 if ((MagickSizeType) bmp_info.file_size > GetBlobSize(image))
cristy3ed852e2009-09-05 21:47:34 +0000805 (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
806 "LengthAndFilesizeDoNotMatch","`%s'",image->filename);
cristy59eefcf2011-02-01 15:54:38 +0000807 else
808 if ((MagickSizeType) bmp_info.file_size < GetBlobSize(image))
809 (void) ThrowMagickException(exception,GetMagickModule(),
810 CorruptImageWarning,"LengthAndFilesizeDoNotMatch","`%s'",
811 image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000812 if (bmp_info.width <= 0)
813 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
814 if (bmp_info.height == 0)
815 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
816 if (bmp_info.planes != 1)
817 ThrowReaderException(CorruptImageError,"StaticPlanesValueNotEqualToOne");
818 if ((bmp_info.bits_per_pixel != 1) && (bmp_info.bits_per_pixel != 4) &&
819 (bmp_info.bits_per_pixel != 8) && (bmp_info.bits_per_pixel != 16) &&
820 (bmp_info.bits_per_pixel != 24) && (bmp_info.bits_per_pixel != 32))
821 ThrowReaderException(CorruptImageError,"UnrecognizedBitsPerPixel");
glennrpf5d7c9e2012-11-23 17:43:18 +0000822 if (bmp_info.bits_per_pixel < 16 &&
823 bmp_info.number_colors > (1U << bmp_info.bits_per_pixel))
cristyf8dcd772014-12-19 12:11:44 +0000824 ThrowReaderException(CorruptImageError,"UnrecognizedNumberOfColors");
cristy3ed852e2009-09-05 21:47:34 +0000825 if ((bmp_info.compression == 1) && (bmp_info.bits_per_pixel != 8))
826 ThrowReaderException(CorruptImageError,"UnrecognizedBitsPerPixel");
827 if ((bmp_info.compression == 2) && (bmp_info.bits_per_pixel != 4))
828 ThrowReaderException(CorruptImageError,"UnrecognizedBitsPerPixel");
829 if ((bmp_info.compression == 3) && (bmp_info.bits_per_pixel < 16))
830 ThrowReaderException(CorruptImageError,"UnrecognizedBitsPerPixel");
831 switch (bmp_info.compression)
832 {
833 case BI_RGB:
834 case BI_RLE8:
835 case BI_RLE4:
836 case BI_BITFIELDS:
837 break;
838 case BI_JPEG:
839 ThrowReaderException(CoderError,"JPEGCompressNotSupported");
840 case BI_PNG:
841 ThrowReaderException(CoderError,"PNGCompressNotSupported");
842 default:
843 ThrowReaderException(CorruptImageError,"UnrecognizedImageCompression");
844 }
cristy7c242ea2013-06-21 17:19:53 +0000845 image->columns=(size_t) MagickAbsoluteValue(bmp_info.width);
846 image->rows=(size_t) MagickAbsoluteValue(bmp_info.height);
cristy3ed852e2009-09-05 21:47:34 +0000847 image->depth=bmp_info.bits_per_pixel <= 8 ? bmp_info.bits_per_pixel : 8;
dirkc2dce492013-11-30 21:16:04 +0000848 image->alpha_trait=((bmp_info.alpha_mask != 0) &&
dirkf161fa42014-12-15 15:46:43 +0000849 (bmp_info.compression == BI_BITFIELDS)) ? BlendPixelTrait :
850 UndefinedPixelTrait;
glennrpf5d7c9e2012-11-23 17:43:18 +0000851 if (bmp_info.bits_per_pixel < 16)
cristy3ed852e2009-09-05 21:47:34 +0000852 {
cristy0b29b252010-05-30 01:59:46 +0000853 size_t
854 one;
855
cristy3ed852e2009-09-05 21:47:34 +0000856 image->storage_class=PseudoClass;
857 image->colors=bmp_info.number_colors;
cristy0b29b252010-05-30 01:59:46 +0000858 one=1;
cristy3ed852e2009-09-05 21:47:34 +0000859 if (image->colors == 0)
cristy0b29b252010-05-30 01:59:46 +0000860 image->colors=one << bmp_info.bits_per_pixel;
cristy3ed852e2009-09-05 21:47:34 +0000861 }
862 if (image->storage_class == PseudoClass)
863 {
864 unsigned char
865 *bmp_colormap;
866
867 size_t
868 packet_size;
869
870 /*
871 Read BMP raster colormap.
872 */
873 if (image->debug != MagickFalse)
874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000875 " Reading colormap of %.20g colors",(double) image->colors);
cristy018f07f2011-09-04 21:15:19 +0000876 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000877 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
878 bmp_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
879 image->colors,4*sizeof(*bmp_colormap));
880 if (bmp_colormap == (unsigned char *) NULL)
881 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
882 if ((bmp_info.size == 12) || (bmp_info.size == 64))
883 packet_size=3;
884 else
885 packet_size=4;
886 offset=SeekBlob(image,start_position+14+bmp_info.size,SEEK_SET);
887 if (offset < 0)
888 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
889 count=ReadBlob(image,packet_size*image->colors,bmp_colormap);
890 if (count != (ssize_t) (packet_size*image->colors))
891 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
892 p=bmp_colormap;
cristybb503372010-05-27 20:51:26 +0000893 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000894 {
cristya321eb72013-06-23 10:42:37 +0000895 image->colormap[i].blue=(MagickRealType) ScaleCharToQuantum(*p++);
896 image->colormap[i].green=(MagickRealType) ScaleCharToQuantum(*p++);
897 image->colormap[i].red=(MagickRealType) ScaleCharToQuantum(*p++);
cristy3ed852e2009-09-05 21:47:34 +0000898 if (packet_size == 4)
899 p++;
900 }
901 bmp_colormap=(unsigned char *) RelinquishMagickMemory(bmp_colormap);
902 }
cristy47b81c22012-03-29 14:19:09 +0000903 image->resolution.x=(double) bmp_info.x_pixels/100.0;
904 image->resolution.y=(double) bmp_info.y_pixels/100.0;
905 image->units=PixelsPerCentimeterResolution;
cristy3ed852e2009-09-05 21:47:34 +0000906 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
907 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
908 break;
cristyacabb842014-12-14 23:36:33 +0000909 status=SetImageExtent(image,image->columns,image->rows,exception);
910 if (status == MagickFalse)
911 return(DestroyImageList(image));
cristy3ed852e2009-09-05 21:47:34 +0000912 /*
913 Read image data.
914 */
cristycd20c152012-04-09 18:57:54 +0000915 offset=SeekBlob(image,start_position+bmp_info.offset_bits,SEEK_SET);
916 if (offset < 0)
917 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristy3ed852e2009-09-05 21:47:34 +0000918 if (bmp_info.compression == BI_RLE4)
919 bmp_info.bits_per_pixel<<=1;
920 bytes_per_line=4*((image->columns*bmp_info.bits_per_pixel+31)/32);
921 length=(size_t) bytes_per_line*image->rows;
cristy0553bd52013-06-30 15:53:50 +0000922 pixel_info=AcquireVirtualMemory((size_t) image->rows,
cristy7c242ea2013-06-21 17:19:53 +0000923 MagickMax(bytes_per_line,image->columns+256UL)*sizeof(*pixels));
cristy0553bd52013-06-30 15:53:50 +0000924 if (pixel_info == (MemoryInfo *) NULL)
cristy7c242ea2013-06-21 17:19:53 +0000925 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristy0553bd52013-06-30 15:53:50 +0000926 pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +0000927 if ((bmp_info.compression == BI_RGB) ||
928 (bmp_info.compression == BI_BITFIELDS))
929 {
930 if (image->debug != MagickFalse)
931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000932 " Reading pixels (%.20g bytes)",(double) length);
cristy7c242ea2013-06-21 17:19:53 +0000933 count=ReadBlob(image,length,pixels);
934 if (count != (ssize_t) length)
cristyde60ea72010-07-09 23:12:09 +0000935 {
cristy0553bd52013-06-30 15:53:50 +0000936 pixel_info=RelinquishVirtualMemory(pixel_info);
cristy7c242ea2013-06-21 17:19:53 +0000937 ThrowReaderException(CorruptImageError,
938 "InsufficientImageDataInFile");
cristyde60ea72010-07-09 23:12:09 +0000939 }
cristy3ed852e2009-09-05 21:47:34 +0000940 }
941 else
942 {
943 /*
944 Convert run-length encoded raster pixels.
945 */
946 status=DecodeImage(image,bmp_info.compression,pixels);
947 if (status == MagickFalse)
cristyde60ea72010-07-09 23:12:09 +0000948 {
cristy0553bd52013-06-30 15:53:50 +0000949 pixel_info=RelinquishVirtualMemory(pixel_info);
cristyde60ea72010-07-09 23:12:09 +0000950 ThrowReaderException(CorruptImageError,
951 "UnableToRunlengthDecodeImage");
952 }
cristy3ed852e2009-09-05 21:47:34 +0000953 }
954 /*
cristy3ed852e2009-09-05 21:47:34 +0000955 Convert BMP raster image to pixel packets.
956 */
957 if (bmp_info.compression == BI_RGB)
958 {
dirkf161fa42014-12-15 15:46:43 +0000959 /*
960 We should ignore the alpha value in BMP3 files but there have been
961 reports about 32 bit files with alpha. We do a quick check to see if
962 the alpha channel contains a value that is not zero (default value).
963 If we find a non zero value we asume the program that wrote the file
964 wants to use the alpha channel.
965 */
cristy17f11b02014-12-20 19:37:04 +0000966 if ((image->alpha_trait == UndefinedPixelTrait) && (bmp_info.size == 40) &&
dirkf161fa42014-12-15 15:46:43 +0000967 (bmp_info.bits_per_pixel == 32))
968 {
969 bytes_per_line=4*(image->columns);
970 for (y=(ssize_t) image->rows-1; y >= 0; y--)
971 {
972 p=pixels+(image->rows-y-1)*bytes_per_line;
973 for (x=0; x < (ssize_t) image->columns; x++)
974 {
975 if (*(p+3) != 0)
976 {
977 image->alpha_trait=BlendPixelTrait;
978 y=-1;
979 break;
980 }
981 p+=4;
982 }
983 }
984 }
cristy17f11b02014-12-20 19:37:04 +0000985 bmp_info.alpha_mask=image->alpha_trait != UndefinedPixelTrait ?
cristya321eb72013-06-23 10:42:37 +0000986 0xff000000U : 0U;
cristy3ed852e2009-09-05 21:47:34 +0000987 bmp_info.red_mask=0x00ff0000U;
988 bmp_info.green_mask=0x0000ff00U;
989 bmp_info.blue_mask=0x000000ffU;
990 if (bmp_info.bits_per_pixel == 16)
991 {
992 /*
993 RGB555.
994 */
995 bmp_info.red_mask=0x00007c00U;
996 bmp_info.green_mask=0x000003e0U;
997 bmp_info.blue_mask=0x0000001fU;
998 }
999 }
cristy58d4fe12015-02-07 11:53:05 +00001000 (void) ResetMagickMemory(&shift,0,sizeof(shift));
1001 (void) ResetMagickMemory(&quantum_bits,0,sizeof(quantum_bits));
cristy3ed852e2009-09-05 21:47:34 +00001002 if ((bmp_info.bits_per_pixel == 16) || (bmp_info.bits_per_pixel == 32))
1003 {
cristybb503372010-05-27 20:51:26 +00001004 register size_t
cristy3ed852e2009-09-05 21:47:34 +00001005 sample;
1006
1007 /*
1008 Get shift and quantum bits info from bitfield masks.
1009 */
cristy3ed852e2009-09-05 21:47:34 +00001010 if (bmp_info.red_mask != 0)
1011 while (((bmp_info.red_mask << shift.red) & 0x80000000UL) == 0)
1012 shift.red++;
1013 if (bmp_info.green_mask != 0)
1014 while (((bmp_info.green_mask << shift.green) & 0x80000000UL) == 0)
1015 shift.green++;
1016 if (bmp_info.blue_mask != 0)
1017 while (((bmp_info.blue_mask << shift.blue) & 0x80000000UL) == 0)
1018 shift.blue++;
1019 if (bmp_info.alpha_mask != 0)
cristy4c08aed2011-07-01 19:47:50 +00001020 while (((bmp_info.alpha_mask << shift.alpha) & 0x80000000UL) == 0)
1021 shift.alpha++;
cristy3ed852e2009-09-05 21:47:34 +00001022 sample=shift.red;
1023 while (((bmp_info.red_mask << sample) & 0x80000000UL) != 0)
1024 sample++;
cristya321eb72013-06-23 10:42:37 +00001025 quantum_bits.red=(MagickRealType) (sample-shift.red);
cristy3ed852e2009-09-05 21:47:34 +00001026 sample=shift.green;
1027 while (((bmp_info.green_mask << sample) & 0x80000000UL) != 0)
1028 sample++;
cristya321eb72013-06-23 10:42:37 +00001029 quantum_bits.green=(MagickRealType) (sample-shift.green);
cristy3ed852e2009-09-05 21:47:34 +00001030 sample=shift.blue;
1031 while (((bmp_info.blue_mask << sample) & 0x80000000UL) != 0)
1032 sample++;
cristya321eb72013-06-23 10:42:37 +00001033 quantum_bits.blue=(MagickRealType) (sample-shift.blue);
cristy4c08aed2011-07-01 19:47:50 +00001034 sample=shift.alpha;
cristy3ed852e2009-09-05 21:47:34 +00001035 while (((bmp_info.alpha_mask << sample) & 0x80000000UL) != 0)
1036 sample++;
cristya321eb72013-06-23 10:42:37 +00001037 quantum_bits.alpha=(MagickRealType) (sample-shift.alpha);
cristy3ed852e2009-09-05 21:47:34 +00001038 }
1039 switch (bmp_info.bits_per_pixel)
1040 {
1041 case 1:
1042 {
1043 /*
1044 Convert bitmap scanline.
1045 */
cristybb503372010-05-27 20:51:26 +00001046 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +00001047 {
1048 p=pixels+(image->rows-y-1)*bytes_per_line;
1049 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001050 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001051 break;
cristybb503372010-05-27 20:51:26 +00001052 for (x=0; x < ((ssize_t) image->columns-7); x+=8)
cristy3ed852e2009-09-05 21:47:34 +00001053 {
1054 for (bit=0; bit < 8; bit++)
1055 {
cristy4c08aed2011-07-01 19:47:50 +00001056 index=(Quantum) (((*p) & (0x80 >> bit)) != 0 ? 0x01 : 0x00);
1057 SetPixelIndex(image,index,q);
cristyed231572011-07-14 02:18:59 +00001058 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001059 }
1060 p++;
1061 }
1062 if ((image->columns % 8) != 0)
1063 {
1064 for (bit=0; bit < (image->columns % 8); bit++)
1065 {
cristy4c08aed2011-07-01 19:47:50 +00001066 index=(Quantum) (((*p) & (0x80 >> bit)) != 0 ? 0x01 : 0x00);
1067 SetPixelIndex(image,index,q);
cristyed231572011-07-14 02:18:59 +00001068 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001069 }
1070 p++;
1071 }
1072 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1073 break;
1074 if (image->previous == (Image *) NULL)
1075 {
cristy8314a302014-12-08 16:05:32 +00001076 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
1077 (image->rows-y),image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001078 if (status == MagickFalse)
1079 break;
1080 }
1081 }
cristyea1a8aa2011-10-20 13:24:06 +00001082 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001083 break;
1084 }
1085 case 4:
1086 {
1087 /*
1088 Convert PseudoColor scanline.
1089 */
cristybb503372010-05-27 20:51:26 +00001090 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +00001091 {
1092 p=pixels+(image->rows-y-1)*bytes_per_line;
1093 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001094 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001095 break;
cristybb503372010-05-27 20:51:26 +00001096 for (x=0; x < ((ssize_t) image->columns-1); x+=2)
cristy3ed852e2009-09-05 21:47:34 +00001097 {
dirkfcef1752014-12-29 21:43:36 +00001098 if (IsValidColormapIndex(image,(*p >> 4) & 0x0f,&index,exception)
1099 == MagickFalse)
1100 break;
cristy4c08aed2011-07-01 19:47:50 +00001101 SetPixelIndex(image,index,q);
cristyed231572011-07-14 02:18:59 +00001102 q+=GetPixelChannels(image);
dirkfcef1752014-12-29 21:43:36 +00001103 if (IsValidColormapIndex(image,*p & 0x0f,&index,exception) ==
1104 MagickFalse)
1105 break;
cristy4c08aed2011-07-01 19:47:50 +00001106 SetPixelIndex(image,index,q);
cristyed231572011-07-14 02:18:59 +00001107 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001108 p++;
1109 }
1110 if ((image->columns % 2) != 0)
1111 {
dirkfcef1752014-12-29 21:43:36 +00001112 if (IsValidColormapIndex(image,(*p >> 4) & 0xf,&index,exception)
1113 == MagickFalse)
1114 break;
cristy4c08aed2011-07-01 19:47:50 +00001115 SetPixelIndex(image,index,q);
cristyed231572011-07-14 02:18:59 +00001116 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001117 p++;
dirkc8e4bcc2015-06-27 21:33:57 +00001118 x++;
cristy3ed852e2009-09-05 21:47:34 +00001119 }
cristyb3f97ae2015-05-18 12:29:32 +00001120 if (x < (ssize_t) image->columns)
dirkfcef1752014-12-29 21:43:36 +00001121 break;
cristy3ed852e2009-09-05 21:47:34 +00001122 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1123 break;
1124 if (image->previous == (Image *) NULL)
1125 {
cristy8314a302014-12-08 16:05:32 +00001126 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
1127 (image->rows-y),image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001128 if (status == MagickFalse)
1129 break;
1130 }
1131 }
cristyea1a8aa2011-10-20 13:24:06 +00001132 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001133 break;
1134 }
1135 case 8:
1136 {
1137 /*
1138 Convert PseudoColor scanline.
1139 */
1140 if ((bmp_info.compression == BI_RLE8) ||
1141 (bmp_info.compression == BI_RLE4))
1142 bytes_per_line=image->columns;
cristybb503372010-05-27 20:51:26 +00001143 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +00001144 {
1145 p=pixels+(image->rows-y-1)*bytes_per_line;
1146 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001147 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001148 break;
cristycaf45802012-06-16 18:28:54 +00001149 for (x=(ssize_t) image->columns; x != 0; --x)
cristy3ed852e2009-09-05 21:47:34 +00001150 {
dirkfcef1752014-12-29 21:43:36 +00001151 if (IsValidColormapIndex(image,*p++,&index,exception) ==
1152 MagickFalse)
1153 break;
cristy4c08aed2011-07-01 19:47:50 +00001154 SetPixelIndex(image,index,q);
cristyed231572011-07-14 02:18:59 +00001155 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001156 }
dirkfcef1752014-12-29 21:43:36 +00001157 if (x > 0)
1158 break;
cristy3ed852e2009-09-05 21:47:34 +00001159 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1160 break;
1161 offset=(MagickOffsetType) (image->rows-y-1);
1162 if (image->previous == (Image *) NULL)
1163 {
cristy8314a302014-12-08 16:05:32 +00001164 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
1165 (image->rows-y),image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001166 if (status == MagickFalse)
1167 break;
1168 }
1169 }
cristyea1a8aa2011-10-20 13:24:06 +00001170 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001171 break;
1172 }
1173 case 16:
1174 {
cristybb503372010-05-27 20:51:26 +00001175 size_t
cristy3e03ba72014-12-09 12:19:30 +00001176 alpha,
cristy3ed852e2009-09-05 21:47:34 +00001177 pixel;
1178
1179 /*
1180 Convert bitfield encoded 16-bit PseudoColor scanline.
1181 */
1182 if (bmp_info.compression != BI_RGB &&
1183 bmp_info.compression != BI_BITFIELDS)
cristyde60ea72010-07-09 23:12:09 +00001184 {
cristy0553bd52013-06-30 15:53:50 +00001185 pixel_info=RelinquishVirtualMemory(pixel_info);
cristyde60ea72010-07-09 23:12:09 +00001186 ThrowReaderException(CorruptImageError,
1187 "UnrecognizedImageCompression");
1188 }
cristy3ed852e2009-09-05 21:47:34 +00001189 bytes_per_line=2*(image->columns+image->columns % 2);
1190 image->storage_class=DirectClass;
cristybb503372010-05-27 20:51:26 +00001191 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +00001192 {
1193 p=pixels+(image->rows-y-1)*bytes_per_line;
1194 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001195 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001196 break;
cristybb503372010-05-27 20:51:26 +00001197 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001198 {
cristybb503372010-05-27 20:51:26 +00001199 pixel=(size_t) (*p++);
cristy3ed852e2009-09-05 21:47:34 +00001200 pixel|=(*p++) << 8;
1201 red=((pixel & bmp_info.red_mask) << shift.red) >> 16;
1202 if (quantum_bits.red == 5)
1203 red|=((red & 0xe000) >> 5);
1204 if (quantum_bits.red <= 8)
1205 red|=((red & 0xff00) >> 8);
1206 green=((pixel & bmp_info.green_mask) << shift.green) >> 16;
1207 if (quantum_bits.green == 5)
1208 green|=((green & 0xe000) >> 5);
1209 if (quantum_bits.green == 6)
1210 green|=((green & 0xc000) >> 6);
1211 if (quantum_bits.green <= 8)
1212 green|=((green & 0xff00) >> 8);
1213 blue=((pixel & bmp_info.blue_mask) << shift.blue) >> 16;
1214 if (quantum_bits.blue == 5)
1215 blue|=((blue & 0xe000) >> 5);
1216 if (quantum_bits.blue <= 8)
1217 blue|=((blue & 0xff00) >> 8);
cristy4c08aed2011-07-01 19:47:50 +00001218 SetPixelRed(image,ScaleShortToQuantum((unsigned short) red),q);
1219 SetPixelGreen(image,ScaleShortToQuantum((unsigned short) green),q);
1220 SetPixelBlue(image,ScaleShortToQuantum((unsigned short) blue),q);
1221 SetPixelAlpha(image,OpaqueAlpha,q);
cristy17f11b02014-12-20 19:37:04 +00001222 if (image->alpha_trait != UndefinedPixelTrait)
dirkf161fa42014-12-15 15:46:43 +00001223 {
1224 alpha=((pixel & bmp_info.alpha_mask) << shift.alpha) >> 16;
1225 if (quantum_bits.alpha <= 8)
1226 alpha|=((alpha & 0xff00) >> 8);
1227 SetPixelAlpha(image,ScaleShortToQuantum(
1228 (unsigned short) alpha),q);
1229 }
cristyed231572011-07-14 02:18:59 +00001230 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001231 }
1232 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1233 break;
1234 offset=(MagickOffsetType) (image->rows-y-1);
1235 if (image->previous == (Image *) NULL)
1236 {
cristy8314a302014-12-08 16:05:32 +00001237 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
1238 (image->rows-y),image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001239 if (status == MagickFalse)
1240 break;
1241 }
1242 }
1243 break;
1244 }
1245 case 24:
1246 {
1247 /*
1248 Convert DirectColor scanline.
1249 */
1250 bytes_per_line=4*((image->columns*24+31)/32);
cristybb503372010-05-27 20:51:26 +00001251 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +00001252 {
1253 p=pixels+(image->rows-y-1)*bytes_per_line;
1254 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001255 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001256 break;
cristybb503372010-05-27 20:51:26 +00001257 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001258 {
cristy4c08aed2011-07-01 19:47:50 +00001259 SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
1260 SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
1261 SetPixelRed(image,ScaleCharToQuantum(*p++),q);
1262 SetPixelAlpha(image,OpaqueAlpha,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 {
cristy8314a302014-12-08 16:05:32 +00001270 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
1271 (image->rows-y),image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001272 if (status == MagickFalse)
1273 break;
1274 }
1275 }
1276 break;
1277 }
1278 case 32:
1279 {
1280 /*
1281 Convert bitfield encoded DirectColor scanline.
1282 */
1283 if ((bmp_info.compression != BI_RGB) &&
1284 (bmp_info.compression != BI_BITFIELDS))
cristyde60ea72010-07-09 23:12:09 +00001285 {
cristy0553bd52013-06-30 15:53:50 +00001286 pixel_info=RelinquishVirtualMemory(pixel_info);
cristyde60ea72010-07-09 23:12:09 +00001287 ThrowReaderException(CorruptImageError,
1288 "UnrecognizedImageCompression");
1289 }
cristy3ed852e2009-09-05 21:47:34 +00001290 bytes_per_line=4*(image->columns);
cristybb503372010-05-27 20:51:26 +00001291 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +00001292 {
cristybb503372010-05-27 20:51:26 +00001293 size_t
cristy3e03ba72014-12-09 12:19:30 +00001294 alpha,
cristy3ed852e2009-09-05 21:47:34 +00001295 pixel;
1296
1297 p=pixels+(image->rows-y-1)*bytes_per_line;
1298 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001299 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001300 break;
cristybb503372010-05-27 20:51:26 +00001301 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001302 {
cristybb503372010-05-27 20:51:26 +00001303 pixel=(size_t) (*p++);
cristyfa6de8c2014-05-18 16:34:44 +00001304 pixel|=((size_t) *p++ << 8);
1305 pixel|=((size_t) *p++ << 16);
1306 pixel|=((size_t) *p++ << 24);
cristy3ed852e2009-09-05 21:47:34 +00001307 red=((pixel & bmp_info.red_mask) << shift.red) >> 16;
1308 if (quantum_bits.red == 8)
1309 red|=(red >> 8);
1310 green=((pixel & bmp_info.green_mask) << shift.green) >> 16;
1311 if (quantum_bits.green == 8)
1312 green|=(green >> 8);
1313 blue=((pixel & bmp_info.blue_mask) << shift.blue) >> 16;
1314 if (quantum_bits.blue == 8)
1315 blue|=(blue >> 8);
cristy4c08aed2011-07-01 19:47:50 +00001316 SetPixelRed(image,ScaleShortToQuantum((unsigned short) red),q);
1317 SetPixelGreen(image,ScaleShortToQuantum((unsigned short) green),q);
1318 SetPixelBlue(image,ScaleShortToQuantum((unsigned short) blue),q);
dirk271dce62014-12-14 20:28:38 +00001319 SetPixelAlpha(image,OpaqueAlpha,q);
cristy17f11b02014-12-20 19:37:04 +00001320 if (image->alpha_trait != UndefinedPixelTrait)
dirkf161fa42014-12-15 15:46:43 +00001321 {
1322 alpha=((pixel & bmp_info.alpha_mask) << shift.alpha) >> 16;
1323 if (quantum_bits.alpha == 8)
1324 alpha|=(alpha >> 8);
1325 SetPixelAlpha(image,ScaleShortToQuantum(
1326 (unsigned short) alpha),q);
1327 }
cristyed231572011-07-14 02:18:59 +00001328 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001329 }
1330 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1331 break;
1332 offset=(MagickOffsetType) (image->rows-y-1);
1333 if (image->previous == (Image *) NULL)
1334 {
cristy8314a302014-12-08 16:05:32 +00001335 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
1336 (image->rows-y),image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001337 if (status == MagickFalse)
1338 break;
1339 }
1340 }
1341 break;
1342 }
1343 default:
cristyde60ea72010-07-09 23:12:09 +00001344 {
cristy0553bd52013-06-30 15:53:50 +00001345 pixel_info=RelinquishVirtualMemory(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +00001346 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristyde60ea72010-07-09 23:12:09 +00001347 }
cristy3ed852e2009-09-05 21:47:34 +00001348 }
cristy0553bd52013-06-30 15:53:50 +00001349 pixel_info=RelinquishVirtualMemory(pixel_info);
dirkfcef1752014-12-29 21:43:36 +00001350 if (y > 0)
1351 break;
cristy3ed852e2009-09-05 21:47:34 +00001352 if (EOFBlob(image) != MagickFalse)
1353 {
1354 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
1355 image->filename);
1356 break;
1357 }
1358 if (bmp_info.height < 0)
1359 {
1360 Image
1361 *flipped_image;
1362
1363 /*
1364 Correct image orientation.
1365 */
1366 flipped_image=FlipImage(image,exception);
cristy65e3b002010-04-13 21:20:56 +00001367 if (flipped_image != (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001368 {
cristy65e3b002010-04-13 21:20:56 +00001369 DuplicateBlob(flipped_image,image);
1370 image=DestroyImage(image);
1371 image=flipped_image;
cristy3ed852e2009-09-05 21:47:34 +00001372 }
cristy3ed852e2009-09-05 21:47:34 +00001373 }
1374 /*
1375 Proceed to next image.
1376 */
1377 if (image_info->number_scenes != 0)
1378 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
1379 break;
1380 *magick='\0';
1381 if (bmp_info.ba_offset != 0)
1382 {
1383 offset=SeekBlob(image,(MagickOffsetType) bmp_info.ba_offset,SEEK_SET);
1384 if (offset < 0)
1385 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1386 }
1387 count=ReadBlob(image,2,magick);
1388 if ((count == 2) && (IsBMP(magick,2) != MagickFalse))
1389 {
1390 /*
1391 Acquire next image structure.
1392 */
cristy9950d572011-10-01 18:22:35 +00001393 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001394 if (GetNextImageInList(image) == (Image *) NULL)
1395 {
1396 image=DestroyImageList(image);
1397 return((Image *) NULL);
1398 }
1399 image=SyncNextImageInList(image);
1400 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
1401 GetBlobSize(image));
1402 if (status == MagickFalse)
1403 break;
1404 }
1405 } while (IsBMP(magick,2) != MagickFalse);
1406 (void) CloseBlob(image);
1407 return(GetFirstImageInList(image));
1408}
1409
1410/*
1411%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1412% %
1413% %
1414% %
1415% R e g i s t e r B M P I m a g e %
1416% %
1417% %
1418% %
1419%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1420%
1421% RegisterBMPImage() adds attributes for the BMP image format to
1422% the list of supported formats. The attributes include the image format
1423% tag, a method to read and/or write the format, whether the format
1424% supports the saving of more than one frame to the same file or blob,
1425% whether the format supports native in-memory I/O, and a brief
1426% description of the format.
1427%
1428% The format of the RegisterBMPImage method is:
1429%
cristybb503372010-05-27 20:51:26 +00001430% size_t RegisterBMPImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001431%
1432*/
cristybb503372010-05-27 20:51:26 +00001433ModuleExport size_t RegisterBMPImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001434{
1435 MagickInfo
1436 *entry;
1437
dirk06b627a2015-04-06 18:59:17 +00001438 entry=AcquireMagickInfo("BMP","BMP","Microsoft Windows bitmap image");
cristy3ed852e2009-09-05 21:47:34 +00001439 entry->decoder=(DecodeImageHandler *) ReadBMPImage;
1440 entry->encoder=(EncodeImageHandler *) WriteBMPImage;
1441 entry->magick=(IsImageFormatHandler *) IsBMP;
dirk08e9a112015-02-22 01:51:41 +00001442 entry->flags^=CoderAdjoinFlag;
1443 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001444 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001445 entry=AcquireMagickInfo("BMP","BMP2","Microsoft Windows bitmap image (V2)");
cristy3ed852e2009-09-05 21:47:34 +00001446 entry->encoder=(EncodeImageHandler *) WriteBMPImage;
1447 entry->magick=(IsImageFormatHandler *) IsBMP;
dirk08e9a112015-02-22 01:51:41 +00001448 entry->flags^=CoderAdjoinFlag;
1449 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001450 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001451 entry=AcquireMagickInfo("BMP","BMP3","Microsoft Windows bitmap image (V3)");
cristy3ed852e2009-09-05 21:47:34 +00001452 entry->encoder=(EncodeImageHandler *) WriteBMPImage;
1453 entry->magick=(IsImageFormatHandler *) IsBMP;
dirk08e9a112015-02-22 01:51:41 +00001454 entry->flags^=CoderAdjoinFlag;
1455 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001456 (void) RegisterMagickInfo(entry);
1457 return(MagickImageCoderSignature);
1458}
1459
1460/*
1461%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1462% %
1463% %
1464% %
1465% U n r e g i s t e r B M P I m a g e %
1466% %
1467% %
1468% %
1469%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1470%
1471% UnregisterBMPImage() removes format registrations made by the
1472% BMP module from the list of supported formats.
1473%
1474% The format of the UnregisterBMPImage method is:
1475%
1476% UnregisterBMPImage(void)
1477%
1478*/
1479ModuleExport void UnregisterBMPImage(void)
1480{
1481 (void) UnregisterMagickInfo("BMP");
1482 (void) UnregisterMagickInfo("BMP2");
1483 (void) UnregisterMagickInfo("BMP3");
1484}
1485
1486/*
1487%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1488% %
1489% %
1490% %
1491% W r i t e B M P I m a g e %
1492% %
1493% %
1494% %
1495%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1496%
1497% WriteBMPImage() writes an image in Microsoft Windows bitmap encoded
1498% image format, version 3 for Windows or (if the image has a matte channel)
1499% version 4.
1500%
1501% The format of the WriteBMPImage method is:
1502%
cristy1e178e72011-08-28 19:44:34 +00001503% MagickBooleanType WriteBMPImage(const ImageInfo *image_info,
1504% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001505%
1506% A description of each parameter follows.
1507%
1508% o image_info: the image info.
1509%
1510% o image: The image.
1511%
cristy1e178e72011-08-28 19:44:34 +00001512% o exception: return any errors or warnings in this structure.
1513%
cristy3ed852e2009-09-05 21:47:34 +00001514*/
cristy1e178e72011-08-28 19:44:34 +00001515static MagickBooleanType WriteBMPImage(const ImageInfo *image_info,Image *image,
1516 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001517{
1518 BMPInfo
1519 bmp_info;
glennrpd0a3f8b2013-08-10 16:11:30 +00001520
dirkde652d92015-11-26 13:19:13 +01001521 const char
1522 *option;
cristy3ed852e2009-09-05 21:47:34 +00001523
1524 const StringInfo
1525 *profile;
1526
cristy3ed852e2009-09-05 21:47:34 +00001527 MagickBooleanType
1528 have_color_info,
1529 status;
1530
1531 MagickOffsetType
1532 scene;
1533
cristya321eb72013-06-23 10:42:37 +00001534 MemoryInfo
cristy0553bd52013-06-30 15:53:50 +00001535 *pixel_info;
cristya321eb72013-06-23 10:42:37 +00001536
cristy4c08aed2011-07-01 19:47:50 +00001537 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001538 *p;
1539
cristybb503372010-05-27 20:51:26 +00001540 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001541 i,
1542 x;
1543
1544 register unsigned char
1545 *q;
1546
cristybb503372010-05-27 20:51:26 +00001547 size_t
cristy3ed852e2009-09-05 21:47:34 +00001548 bytes_per_line,
1549 type;
1550
cristy4e82e512011-04-24 01:33:42 +00001551 ssize_t
1552 y;
1553
1554 unsigned char
1555 *bmp_data,
1556 *pixels;
1557
cristy3ed852e2009-09-05 21:47:34 +00001558 /*
1559 Open output image file.
1560 */
1561 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001562 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001563 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001564 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001565 if (image->debug != MagickFalse)
1566 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00001567 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001568 assert(exception->signature == MagickCoreSignature);
cristy1e178e72011-08-28 19:44:34 +00001569 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00001570 if (status == MagickFalse)
1571 return(status);
1572 type=4;
1573 if (LocaleCompare(image_info->magick,"BMP2") == 0)
1574 type=2;
1575 else
1576 if (LocaleCompare(image_info->magick,"BMP3") == 0)
1577 type=3;
glennrpd0a3f8b2013-08-10 16:11:30 +00001578
dirkde652d92015-11-26 13:19:13 +01001579 option=GetImageOption(image_info,"bmp:format");
1580 if (option != (char *) NULL)
glennrpd0a3f8b2013-08-10 16:11:30 +00001581 {
1582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
dirkde652d92015-11-26 13:19:13 +01001583 " Format=%s",option);
glennrpd0a3f8b2013-08-10 16:11:30 +00001584
dirkde652d92015-11-26 13:19:13 +01001585 if (LocaleCompare(option,"bmp2") == 0)
glennrpd0a3f8b2013-08-10 16:11:30 +00001586 type=2;
dirkde652d92015-11-26 13:19:13 +01001587 if (LocaleCompare(option,"bmp3") == 0)
glennrpd0a3f8b2013-08-10 16:11:30 +00001588 type=3;
dirkde652d92015-11-26 13:19:13 +01001589 if (LocaleCompare(option,"bmp4") == 0)
glennrpd0a3f8b2013-08-10 16:11:30 +00001590 type=4;
1591 }
1592
cristy3ed852e2009-09-05 21:47:34 +00001593 scene=0;
1594 do
1595 {
1596 /*
1597 Initialize BMP raster file header.
1598 */
cristyaf8d3912014-02-21 14:50:33 +00001599 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001600 (void) ResetMagickMemory(&bmp_info,0,sizeof(bmp_info));
1601 bmp_info.file_size=14+12;
1602 if (type > 2)
1603 bmp_info.file_size+=28;
1604 bmp_info.offset_bits=bmp_info.file_size;
1605 bmp_info.compression=BI_RGB;
1606 if ((image->storage_class == PseudoClass) && (image->colors > 256))
cristy1e178e72011-08-28 19:44:34 +00001607 (void) SetImageStorageClass(image,DirectClass,exception);
cristy3ed852e2009-09-05 21:47:34 +00001608 if (image->storage_class != DirectClass)
1609 {
1610 /*
1611 Colormapped BMP raster.
1612 */
1613 bmp_info.bits_per_pixel=8;
1614 if (image->colors <= 2)
1615 bmp_info.bits_per_pixel=1;
1616 else
1617 if (image->colors <= 16)
1618 bmp_info.bits_per_pixel=4;
1619 else
1620 if (image->colors <= 256)
1621 bmp_info.bits_per_pixel=8;
1622 if (image_info->compression == RLECompression)
1623 bmp_info.bits_per_pixel=8;
1624 bmp_info.number_colors=1U << bmp_info.bits_per_pixel;
cristy17f11b02014-12-20 19:37:04 +00001625 if (image->alpha_trait != UndefinedPixelTrait)
cristy1e178e72011-08-28 19:44:34 +00001626 (void) SetImageStorageClass(image,DirectClass,exception);
cristy3ed852e2009-09-05 21:47:34 +00001627 else
cristybb503372010-05-27 20:51:26 +00001628 if ((size_t) bmp_info.number_colors < image->colors)
cristy1e178e72011-08-28 19:44:34 +00001629 (void) SetImageStorageClass(image,DirectClass,exception);
cristy3ed852e2009-09-05 21:47:34 +00001630 else
1631 {
1632 bmp_info.file_size+=3*(1UL << bmp_info.bits_per_pixel);
1633 bmp_info.offset_bits+=3*(1UL << bmp_info.bits_per_pixel);
1634 if (type > 2)
1635 {
1636 bmp_info.file_size+=(1UL << bmp_info.bits_per_pixel);
1637 bmp_info.offset_bits+=(1UL << bmp_info.bits_per_pixel);
1638 }
1639 }
1640 }
1641 if (image->storage_class == DirectClass)
1642 {
1643 /*
1644 Full color BMP raster.
1645 */
1646 bmp_info.number_colors=0;
1647 bmp_info.bits_per_pixel=(unsigned short)
cristy17f11b02014-12-20 19:37:04 +00001648 ((type > 3) && (image->alpha_trait != UndefinedPixelTrait) ? 32 : 24);
cristy3ed852e2009-09-05 21:47:34 +00001649 bmp_info.compression=(unsigned int) ((type > 3) &&
cristy17f11b02014-12-20 19:37:04 +00001650 (image->alpha_trait != UndefinedPixelTrait) ? BI_BITFIELDS : BI_RGB);
dirkde652d92015-11-26 13:19:13 +01001651 if ((type == 3) && (image->alpha_trait != UndefinedPixelTrait))
1652 {
1653 option=GetImageOption(image_info,"bmp3:alpha");
1654 if (IsStringTrue(option))
1655 bmp_info.bits_per_pixel=32;
1656 }
cristy3ed852e2009-09-05 21:47:34 +00001657 }
1658 bytes_per_line=4*((image->columns*bmp_info.bits_per_pixel+31)/32);
1659 bmp_info.ba_offset=0;
1660 profile=GetImageProfile(image,"icc");
1661 have_color_info=(image->rendering_intent != UndefinedIntent) ||
1662 (profile != (StringInfo *) NULL) || (image->gamma != 0.0) ? MagickTrue :
1663 MagickFalse;
1664 if (type == 2)
1665 bmp_info.size=12;
1666 else
cristy17f11b02014-12-20 19:37:04 +00001667 if ((type == 3) || ((image->alpha_trait == UndefinedPixelTrait) &&
cristy3ed852e2009-09-05 21:47:34 +00001668 (have_color_info == MagickFalse)))
1669 {
1670 type=3;
1671 bmp_info.size=40;
1672 }
1673 else
1674 {
1675 int
1676 extra_size;
1677
1678 bmp_info.size=108;
1679 extra_size=68;
1680 if ((image->rendering_intent != UndefinedIntent) ||
1681 (profile != (StringInfo *) NULL))
1682 {
1683 bmp_info.size=124;
1684 extra_size+=16;
1685 }
1686 bmp_info.file_size+=extra_size;
1687 bmp_info.offset_bits+=extra_size;
1688 }
cristy7c242ea2013-06-21 17:19:53 +00001689 bmp_info.width=(ssize_t) image->columns;
1690 bmp_info.height=(ssize_t) image->rows;
cristy3ed852e2009-09-05 21:47:34 +00001691 bmp_info.planes=1;
1692 bmp_info.image_size=(unsigned int) (bytes_per_line*image->rows);
1693 bmp_info.file_size+=bmp_info.image_size;
1694 bmp_info.x_pixels=75*39;
1695 bmp_info.y_pixels=75*39;
1696 switch (image->units)
1697 {
1698 case UndefinedResolution:
1699 case PixelsPerInchResolution:
1700 {
cristy2a11bef2011-10-28 18:33:11 +00001701 bmp_info.x_pixels=(unsigned int) (100.0*image->resolution.x/2.54);
1702 bmp_info.y_pixels=(unsigned int) (100.0*image->resolution.y/2.54);
cristy3ed852e2009-09-05 21:47:34 +00001703 break;
1704 }
1705 case PixelsPerCentimeterResolution:
1706 {
cristy2a11bef2011-10-28 18:33:11 +00001707 bmp_info.x_pixels=(unsigned int) (100.0*image->resolution.x);
1708 bmp_info.y_pixels=(unsigned int) (100.0*image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001709 break;
1710 }
1711 }
1712 bmp_info.colors_important=bmp_info.number_colors;
1713 /*
1714 Convert MIFF to BMP raster pixels.
1715 */
cristy0553bd52013-06-30 15:53:50 +00001716 pixel_info=AcquireVirtualMemory((size_t) bmp_info.image_size,
cristy3ed852e2009-09-05 21:47:34 +00001717 sizeof(*pixels));
cristy0553bd52013-06-30 15:53:50 +00001718 if (pixel_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001719 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy0553bd52013-06-30 15:53:50 +00001720 pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +00001721 (void) ResetMagickMemory(pixels,0,(size_t) bmp_info.image_size);
1722 switch (bmp_info.bits_per_pixel)
1723 {
1724 case 1:
1725 {
cristybb503372010-05-27 20:51:26 +00001726 size_t
cristy3ed852e2009-09-05 21:47:34 +00001727 bit,
1728 byte;
1729
1730 /*
1731 Convert PseudoClass image to a BMP monochrome image.
1732 */
cristybb503372010-05-27 20:51:26 +00001733 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001734 {
cristy4c08aed2011-07-01 19:47:50 +00001735 ssize_t
1736 offset;
1737
cristy1e178e72011-08-28 19:44:34 +00001738 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001739 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001740 break;
cristy3ed852e2009-09-05 21:47:34 +00001741 q=pixels+(image->rows-y-1)*bytes_per_line;
1742 bit=0;
1743 byte=0;
cristybb503372010-05-27 20:51:26 +00001744 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001745 {
1746 byte<<=1;
cristy4c08aed2011-07-01 19:47:50 +00001747 byte|=GetPixelIndex(image,p) != 0 ? 0x01 : 0x00;
cristy3ed852e2009-09-05 21:47:34 +00001748 bit++;
1749 if (bit == 8)
1750 {
1751 *q++=(unsigned char) byte;
1752 bit=0;
1753 byte=0;
1754 }
cristyed231572011-07-14 02:18:59 +00001755 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001756 }
1757 if (bit != 0)
1758 {
1759 *q++=(unsigned char) (byte << (8-bit));
1760 x++;
1761 }
cristy4c08aed2011-07-01 19:47:50 +00001762 offset=(ssize_t) (image->columns+7)/8;
1763 for (x=offset; x < (ssize_t) bytes_per_line; x++)
cristy3ed852e2009-09-05 21:47:34 +00001764 *q++=0x00;
1765 if (image->previous == (Image *) NULL)
1766 {
cristycee97112010-05-28 00:44:52 +00001767 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1768 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001769 if (status == MagickFalse)
1770 break;
1771 }
1772 }
1773 break;
1774 }
1775 case 4:
1776 {
cristybb503372010-05-27 20:51:26 +00001777 size_t
cristy4c08aed2011-07-01 19:47:50 +00001778 byte,
cristya321eb72013-06-23 10:42:37 +00001779 nibble;
1780
1781 ssize_t
cristy7c242ea2013-06-21 17:19:53 +00001782 offset;
cristy3ed852e2009-09-05 21:47:34 +00001783
1784 /*
1785 Convert PseudoClass image to a BMP monochrome image.
1786 */
cristybb503372010-05-27 20:51:26 +00001787 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001788 {
cristy1e178e72011-08-28 19:44:34 +00001789 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001790 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001791 break;
cristy3ed852e2009-09-05 21:47:34 +00001792 q=pixels+(image->rows-y-1)*bytes_per_line;
1793 nibble=0;
1794 byte=0;
cristybb503372010-05-27 20:51:26 +00001795 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001796 {
1797 byte<<=4;
cristy4c08aed2011-07-01 19:47:50 +00001798 byte|=((size_t) GetPixelIndex(image,p) & 0x0f);
cristy3ed852e2009-09-05 21:47:34 +00001799 nibble++;
1800 if (nibble == 2)
1801 {
1802 *q++=(unsigned char) byte;
1803 nibble=0;
1804 byte=0;
1805 }
cristyed231572011-07-14 02:18:59 +00001806 p+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001807 }
1808 if (nibble != 0)
1809 {
1810 *q++=(unsigned char) (byte << 4);
1811 x++;
1812 }
1813 offset=(ssize_t) (image->columns+1)/2;
1814 for (x=offset; x < (ssize_t) bytes_per_line; x++)
cristy3ed852e2009-09-05 21:47:34 +00001815 *q++=0x00;
1816 if (image->previous == (Image *) NULL)
1817 {
cristycee97112010-05-28 00:44:52 +00001818 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1819 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001820 if (status == MagickFalse)
1821 break;
1822 }
1823 }
1824 break;
1825 }
1826 case 8:
1827 {
1828 /*
1829 Convert PseudoClass packet to BMP pixel.
1830 */
cristybb503372010-05-27 20:51:26 +00001831 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001832 {
cristy1e178e72011-08-28 19:44:34 +00001833 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001834 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001835 break;
cristy3ed852e2009-09-05 21:47:34 +00001836 q=pixels+(image->rows-y-1)*bytes_per_line;
cristybb503372010-05-27 20:51:26 +00001837 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00001838 {
1839 *q++=(unsigned char) GetPixelIndex(image,p);
cristyed231572011-07-14 02:18:59 +00001840 p+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001841 }
cristybb503372010-05-27 20:51:26 +00001842 for ( ; x < (ssize_t) bytes_per_line; x++)
cristy3ed852e2009-09-05 21:47:34 +00001843 *q++=0x00;
1844 if (image->previous == (Image *) NULL)
1845 {
cristycee97112010-05-28 00:44:52 +00001846 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1847 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001848 if (status == MagickFalse)
1849 break;
1850 }
1851 }
1852 break;
1853 }
1854 case 24:
1855 {
1856 /*
1857 Convert DirectClass packet to BMP BGR888.
1858 */
cristybb503372010-05-27 20:51:26 +00001859 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001860 {
cristy1e178e72011-08-28 19:44:34 +00001861 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001862 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001863 break;
1864 q=pixels+(image->rows-y-1)*bytes_per_line;
cristybb503372010-05-27 20:51:26 +00001865 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001866 {
cristy4c08aed2011-07-01 19:47:50 +00001867 *q++=ScaleQuantumToChar(GetPixelBlue(image,p));
1868 *q++=ScaleQuantumToChar(GetPixelGreen(image,p));
1869 *q++=ScaleQuantumToChar(GetPixelRed(image,p));
cristyed231572011-07-14 02:18:59 +00001870 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001871 }
cristybb503372010-05-27 20:51:26 +00001872 for (x=3L*(ssize_t) image->columns; x < (ssize_t) bytes_per_line; x++)
cristy3ed852e2009-09-05 21:47:34 +00001873 *q++=0x00;
1874 if (image->previous == (Image *) NULL)
1875 {
cristycee97112010-05-28 00:44:52 +00001876 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1877 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001878 if (status == MagickFalse)
1879 break;
1880 }
1881 }
1882 break;
1883 }
1884 case 32:
1885 {
1886 /*
1887 Convert DirectClass packet to ARGB8888 pixel.
1888 */
cristybb503372010-05-27 20:51:26 +00001889 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001890 {
cristy1e178e72011-08-28 19:44:34 +00001891 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001892 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001893 break;
1894 q=pixels+(image->rows-y-1)*bytes_per_line;
cristybb503372010-05-27 20:51:26 +00001895 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001896 {
cristy4c08aed2011-07-01 19:47:50 +00001897 *q++=ScaleQuantumToChar(GetPixelBlue(image,p));
1898 *q++=ScaleQuantumToChar(GetPixelGreen(image,p));
1899 *q++=ScaleQuantumToChar(GetPixelRed(image,p));
1900 *q++=ScaleQuantumToChar(GetPixelAlpha(image,p));
cristyed231572011-07-14 02:18:59 +00001901 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001902 }
1903 if (image->previous == (Image *) NULL)
1904 {
cristycee97112010-05-28 00:44:52 +00001905 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1906 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001907 if (status == MagickFalse)
1908 break;
1909 }
1910 }
1911 break;
1912 }
1913 }
1914 if ((type > 2) && (bmp_info.bits_per_pixel == 8))
1915 if (image_info->compression != NoCompression)
1916 {
cristya321eb72013-06-23 10:42:37 +00001917 MemoryInfo
1918 *rle_info;
cristy3ed852e2009-09-05 21:47:34 +00001919
1920 /*
1921 Convert run-length encoded raster pixels.
1922 */
cristya321eb72013-06-23 10:42:37 +00001923 rle_info=AcquireVirtualMemory((size_t) (2*(bytes_per_line+2)+2),
1924 (image->rows+2)*sizeof(*pixels));
1925 if (rle_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001926 {
cristy0553bd52013-06-30 15:53:50 +00001927 pixel_info=RelinquishVirtualMemory(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +00001928 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1929 }
cristya321eb72013-06-23 10:42:37 +00001930 bmp_data=(unsigned char *) GetVirtualMemoryBlob(rle_info);
cristy3ed852e2009-09-05 21:47:34 +00001931 bmp_info.file_size-=bmp_info.image_size;
1932 bmp_info.image_size=(unsigned int) EncodeImage(image,bytes_per_line,
1933 pixels,bmp_data);
1934 bmp_info.file_size+=bmp_info.image_size;
cristy0553bd52013-06-30 15:53:50 +00001935 pixel_info=RelinquishVirtualMemory(pixel_info);
1936 pixel_info=rle_info;
cristy3ed852e2009-09-05 21:47:34 +00001937 pixels=bmp_data;
1938 bmp_info.compression=BI_RLE8;
1939 }
1940 /*
1941 Write BMP for Windows, all versions, 14-byte header.
1942 */
1943 if (image->debug != MagickFalse)
1944 {
1945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001946 " Writing BMP version %.20g datastream",(double) type);
cristy3ed852e2009-09-05 21:47:34 +00001947 if (image->storage_class == DirectClass)
1948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1949 " Storage class=DirectClass");
1950 else
1951 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1952 " Storage class=PseudoClass");
1953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001954 " Image depth=%.20g",(double) image->depth);
cristy17f11b02014-12-20 19:37:04 +00001955 if (image->alpha_trait != UndefinedPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +00001956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1957 " Matte=True");
1958 else
1959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1960 " Matte=MagickFalse");
1961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001962 " BMP bits_per_pixel=%.20g",(double) bmp_info.bits_per_pixel);
cristy3ed852e2009-09-05 21:47:34 +00001963 switch ((int) bmp_info.compression)
1964 {
1965 case BI_RGB:
1966 {
1967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1968 " Compression=BI_RGB");
1969 break;
1970 }
1971 case BI_RLE8:
1972 {
1973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1974 " Compression=BI_RLE8");
1975 break;
1976 }
1977 case BI_BITFIELDS:
1978 {
1979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1980 " Compression=BI_BITFIELDS");
1981 break;
1982 }
1983 default:
1984 {
1985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristydf409582015-06-12 16:35:04 +00001986 " Compression=UNKNOWN (%lu)",bmp_info.compression);
cristy3ed852e2009-09-05 21:47:34 +00001987 break;
1988 }
1989 }
1990 if (bmp_info.number_colors == 0)
1991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1992 " Number_colors=unspecified");
1993 else
1994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristydf409582015-06-12 16:35:04 +00001995 " Number_colors=%lu",bmp_info.number_colors);
cristy3ed852e2009-09-05 21:47:34 +00001996 }
1997 (void) WriteBlob(image,2,(unsigned char *) "BM");
1998 (void) WriteBlobLSBLong(image,bmp_info.file_size);
1999 (void) WriteBlobLSBLong(image,bmp_info.ba_offset); /* always 0 */
2000 (void) WriteBlobLSBLong(image,bmp_info.offset_bits);
2001 if (type == 2)
2002 {
2003 /*
2004 Write 12-byte version 2 bitmap header.
2005 */
2006 (void) WriteBlobLSBLong(image,bmp_info.size);
2007 (void) WriteBlobLSBShort(image,(unsigned short) bmp_info.width);
2008 (void) WriteBlobLSBShort(image,(unsigned short) bmp_info.height);
2009 (void) WriteBlobLSBShort(image,bmp_info.planes);
2010 (void) WriteBlobLSBShort(image,bmp_info.bits_per_pixel);
2011 }
2012 else
2013 {
2014 /*
2015 Write 40-byte version 3+ bitmap header.
2016 */
2017 (void) WriteBlobLSBLong(image,bmp_info.size);
2018 (void) WriteBlobLSBLong(image,(unsigned int) bmp_info.width);
2019 (void) WriteBlobLSBLong(image,(unsigned int) bmp_info.height);
2020 (void) WriteBlobLSBShort(image,bmp_info.planes);
2021 (void) WriteBlobLSBShort(image,bmp_info.bits_per_pixel);
2022 (void) WriteBlobLSBLong(image,bmp_info.compression);
2023 (void) WriteBlobLSBLong(image,bmp_info.image_size);
2024 (void) WriteBlobLSBLong(image,bmp_info.x_pixels);
2025 (void) WriteBlobLSBLong(image,bmp_info.y_pixels);
2026 (void) WriteBlobLSBLong(image,bmp_info.number_colors);
2027 (void) WriteBlobLSBLong(image,bmp_info.colors_important);
2028 }
cristy17f11b02014-12-20 19:37:04 +00002029 if ((type > 3) && ((image->alpha_trait != UndefinedPixelTrait) ||
cristy3ed852e2009-09-05 21:47:34 +00002030 (have_color_info != MagickFalse)))
2031 {
2032 /*
2033 Write the rest of the 108-byte BMP Version 4 header.
2034 */
2035 (void) WriteBlobLSBLong(image,0x00ff0000U); /* Red mask */
2036 (void) WriteBlobLSBLong(image,0x0000ff00U); /* Green mask */
2037 (void) WriteBlobLSBLong(image,0x000000ffU); /* Blue mask */
2038 (void) WriteBlobLSBLong(image,0xff000000U); /* Alpha mask */
cristy55388ab2012-05-01 00:01:47 +00002039 (void) WriteBlobLSBLong(image,0x73524742U); /* sRGB */
cristy3ed852e2009-09-05 21:47:34 +00002040 (void) WriteBlobLSBLong(image,(unsigned int)
cristyc554cc82012-08-12 19:55:34 +00002041 (image->chromaticity.red_primary.x*0x40000000));
cristy3ed852e2009-09-05 21:47:34 +00002042 (void) WriteBlobLSBLong(image,(unsigned int)
cristyc554cc82012-08-12 19:55:34 +00002043 (image->chromaticity.red_primary.y*0x40000000));
cristy3ed852e2009-09-05 21:47:34 +00002044 (void) WriteBlobLSBLong(image,(unsigned int)
cristyc554cc82012-08-12 19:55:34 +00002045 ((1.000f-(image->chromaticity.red_primary.x+
2046 image->chromaticity.red_primary.y))*0x40000000));
cristy3ed852e2009-09-05 21:47:34 +00002047 (void) WriteBlobLSBLong(image,(unsigned int)
cristyc554cc82012-08-12 19:55:34 +00002048 (image->chromaticity.green_primary.x*0x40000000));
cristy3ed852e2009-09-05 21:47:34 +00002049 (void) WriteBlobLSBLong(image,(unsigned int)
cristyc554cc82012-08-12 19:55:34 +00002050 (image->chromaticity.green_primary.y*0x40000000));
cristy3ed852e2009-09-05 21:47:34 +00002051 (void) WriteBlobLSBLong(image,(unsigned int)
cristyc554cc82012-08-12 19:55:34 +00002052 ((1.000f-(image->chromaticity.green_primary.x+
2053 image->chromaticity.green_primary.y))*0x40000000));
cristy3ed852e2009-09-05 21:47:34 +00002054 (void) WriteBlobLSBLong(image,(unsigned int)
cristyc554cc82012-08-12 19:55:34 +00002055 (image->chromaticity.blue_primary.x*0x40000000));
cristy3ed852e2009-09-05 21:47:34 +00002056 (void) WriteBlobLSBLong(image,(unsigned int)
cristyc554cc82012-08-12 19:55:34 +00002057 (image->chromaticity.blue_primary.y*0x40000000));
cristy3ed852e2009-09-05 21:47:34 +00002058 (void) WriteBlobLSBLong(image,(unsigned int)
cristyc554cc82012-08-12 19:55:34 +00002059 ((1.000f-(image->chromaticity.blue_primary.x+
2060 image->chromaticity.blue_primary.y))*0x40000000));
cristy3ed852e2009-09-05 21:47:34 +00002061 (void) WriteBlobLSBLong(image,(unsigned int)
cristyc554cc82012-08-12 19:55:34 +00002062 (bmp_info.gamma_scale.x*0x10000));
cristy3ed852e2009-09-05 21:47:34 +00002063 (void) WriteBlobLSBLong(image,(unsigned int)
cristyc554cc82012-08-12 19:55:34 +00002064 (bmp_info.gamma_scale.y*0x10000));
cristy3ed852e2009-09-05 21:47:34 +00002065 (void) WriteBlobLSBLong(image,(unsigned int)
cristyc554cc82012-08-12 19:55:34 +00002066 (bmp_info.gamma_scale.z*0x10000));
cristy3ed852e2009-09-05 21:47:34 +00002067 if ((image->rendering_intent != UndefinedIntent) ||
2068 (profile != (StringInfo *) NULL))
2069 {
cristybb503372010-05-27 20:51:26 +00002070 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002071 intent;
2072
2073 switch ((int) image->rendering_intent)
2074 {
2075 case SaturationIntent:
2076 {
2077 intent=LCS_GM_BUSINESS;
2078 break;
2079 }
2080 case RelativeIntent:
2081 {
2082 intent=LCS_GM_GRAPHICS;
2083 break;
2084 }
2085 case PerceptualIntent:
2086 {
2087 intent=LCS_GM_IMAGES;
2088 break;
2089 }
2090 case AbsoluteIntent:
2091 {
2092 intent=LCS_GM_ABS_COLORIMETRIC;
2093 break;
2094 }
2095 default:
2096 {
2097 intent=0;
2098 break;
2099 }
2100 }
2101 (void) WriteBlobLSBLong(image,(unsigned int) intent);
2102 (void) WriteBlobLSBLong(image,0x00); /* dummy profile data */
2103 (void) WriteBlobLSBLong(image,0x00); /* dummy profile length */
2104 (void) WriteBlobLSBLong(image,0x00); /* reserved */
2105 }
2106 }
2107 if (image->storage_class == PseudoClass)
2108 {
2109 unsigned char
2110 *bmp_colormap;
2111
2112 /*
2113 Dump colormap to file.
2114 */
2115 if (image->debug != MagickFalse)
2116 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002117 " Colormap: %.20g entries",(double) image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002118 bmp_colormap=(unsigned char *) AcquireQuantumMemory((size_t) (1UL <<
2119 bmp_info.bits_per_pixel),4*sizeof(*bmp_colormap));
2120 if (bmp_colormap == (unsigned char *) NULL)
2121 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2122 q=bmp_colormap;
cristybb503372010-05-27 20:51:26 +00002123 for (i=0; i < (ssize_t) MagickMin((ssize_t) image->colors,(ssize_t) bmp_info.number_colors); i++)
cristy3ed852e2009-09-05 21:47:34 +00002124 {
cristya321eb72013-06-23 10:42:37 +00002125 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].blue));
2126 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].green));
2127 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].red));
cristy3ed852e2009-09-05 21:47:34 +00002128 if (type > 2)
2129 *q++=(unsigned char) 0x0;
2130 }
cristybb503372010-05-27 20:51:26 +00002131 for ( ; i < (ssize_t) (1UL << bmp_info.bits_per_pixel); i++)
cristy3ed852e2009-09-05 21:47:34 +00002132 {
2133 *q++=(unsigned char) 0x00;
2134 *q++=(unsigned char) 0x00;
2135 *q++=(unsigned char) 0x00;
2136 if (type > 2)
2137 *q++=(unsigned char) 0x00;
2138 }
2139 if (type <= 2)
2140 (void) WriteBlob(image,(size_t) (3*(1L << bmp_info.bits_per_pixel)),
2141 bmp_colormap);
2142 else
2143 (void) WriteBlob(image,(size_t) (4*(1L << bmp_info.bits_per_pixel)),
2144 bmp_colormap);
2145 bmp_colormap=(unsigned char *) RelinquishMagickMemory(bmp_colormap);
2146 }
2147 if (image->debug != MagickFalse)
2148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristydf409582015-06-12 16:35:04 +00002149 " Pixels: %lu bytes",bmp_info.image_size);
cristy3ed852e2009-09-05 21:47:34 +00002150 (void) WriteBlob(image,(size_t) bmp_info.image_size,pixels);
cristy0553bd52013-06-30 15:53:50 +00002151 pixel_info=RelinquishVirtualMemory(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +00002152 if (GetNextImageInList(image) == (Image *) NULL)
2153 break;
2154 image=SyncNextImageInList(image);
2155 status=SetImageProgress(image,SaveImagesTag,scene++,
2156 GetImageListLength(image));
2157 if (status == MagickFalse)
2158 break;
2159 } while (image_info->adjoin != MagickFalse);
2160 (void) CloseBlob(image);
2161 return(MagickTrue);
cristy84af0c32013-06-23 18:08:28 +00002162}