blob: 0ea42cdf5c43ec8cd4545539505f1b627d9cf0fa [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% IIIII CCCC OOO N N %
7% I C O O NN N %
8% I C O O N N N %
9% I C O O N NN %
10% IIIII CCCC OOO N N %
11% %
12% %
13% Read Microsoft Windows Icon Format %
14% %
15% Software Design %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% July 1992 %
18% %
19% %
cristyfe676ee2013-11-18 13:03:38 +000020% Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
cristy4c08aed2011-07-01 19:47:50 +000042#include "MagickCore/studio.h"
glennrpaa192b12012-01-17 21:35:21 +000043#include "MagickCore/artifact.h"
cristy4c08aed2011-07-01 19:47:50 +000044#include "MagickCore/blob.h"
45#include "MagickCore/blob-private.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/colormap.h"
48#include "MagickCore/colorspace.h"
cristy3d9f5ba2012-06-26 13:37:31 +000049#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000050#include "MagickCore/exception.h"
51#include "MagickCore/exception-private.h"
52#include "MagickCore/image.h"
53#include "MagickCore/image-private.h"
54#include "MagickCore/list.h"
55#include "MagickCore/log.h"
56#include "MagickCore/magick.h"
57#include "MagickCore/memory_.h"
58#include "MagickCore/monitor.h"
59#include "MagickCore/monitor-private.h"
cristy2c5fc272012-02-22 01:27:46 +000060#include "MagickCore/nt-base-private.h"
dirk9e137702014-01-09 19:29:59 +000061#include "MagickCore/option.h"
cristy4c08aed2011-07-01 19:47:50 +000062#include "MagickCore/pixel-accessor.h"
63#include "MagickCore/quantize.h"
64#include "MagickCore/quantum-private.h"
65#include "MagickCore/static.h"
66#include "MagickCore/string_.h"
67#include "MagickCore/module.h"
cristy3ed852e2009-09-05 21:47:34 +000068
69/*
70 Define declarations.
71*/
cristy07a3cca2012-12-10 13:09:10 +000072#if !defined(MAGICKCORE_WINDOWS_SUPPORT) || defined(__MINGW32__) || defined(__MINGW64__)
cristy3ed852e2009-09-05 21:47:34 +000073#define BI_RGB 0
74#define BI_RLE8 1
75#define BI_BITFIELDS 3
76#endif
77#define MaxIcons 1024
78
79/*
80 Typedef declarations.
81*/
82typedef struct _IconEntry
83{
84 unsigned char
85 width,
86 height,
87 colors,
88 reserved;
89
90 unsigned short int
91 planes,
92 bits_per_pixel;
93
cristybb503372010-05-27 20:51:26 +000094 size_t
cristy3ed852e2009-09-05 21:47:34 +000095 size,
96 offset;
97} IconEntry;
98
99typedef struct _IconFile
100{
101 short
102 reserved,
103 resource_type,
104 count;
105
106 IconEntry
107 directory[MaxIcons];
108} IconFile;
109
110typedef struct _IconInfo
111{
cristybb503372010-05-27 20:51:26 +0000112 size_t
cristy3ed852e2009-09-05 21:47:34 +0000113 file_size,
114 ba_offset,
115 offset_bits,
116 size;
117
cristybb503372010-05-27 20:51:26 +0000118 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000119 width,
120 height;
121
122 unsigned short
123 planes,
124 bits_per_pixel;
125
cristybb503372010-05-27 20:51:26 +0000126 size_t
cristy3ed852e2009-09-05 21:47:34 +0000127 compression,
128 image_size,
129 x_pixels,
130 y_pixels,
131 number_colors,
132 red_mask,
133 green_mask,
134 blue_mask,
135 alpha_mask,
136 colors_important;
137
cristybb503372010-05-27 20:51:26 +0000138 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000139 colorspace;
140} IconInfo;
141
142/*
143 Forward declaractions.
144*/
145static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000146 WriteICONImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000147
148/*
149%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
150% %
151% %
152% %
153% R e a d I C O N I m a g e %
154% %
155% %
156% %
157%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
158%
159% ReadICONImage() reads a Microsoft icon image file and returns it. It
160% allocates the memory necessary for the new Image structure and returns a
161% pointer to the new image.
162%
163% The format of the ReadICONImage method is:
164%
165% Image *ReadICONImage(const ImageInfo *image_info,
166% ExceptionInfo *exception)
167%
168% A description of each parameter follows:
169%
170% o image_info: the image info.
171%
172% o exception: return any errors or warnings in this structure.
173%
174*/
175static Image *ReadICONImage(const ImageInfo *image_info,
176 ExceptionInfo *exception)
177{
178 IconFile
179 icon_file;
180
181 IconInfo
182 icon_info;
183
184 Image
185 *image;
186
cristy3ed852e2009-09-05 21:47:34 +0000187 MagickBooleanType
188 status;
189
cristybb503372010-05-27 20:51:26 +0000190 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000191 i,
192 x;
193
cristy4c08aed2011-07-01 19:47:50 +0000194 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000195 *q;
196
197 register unsigned char
198 *p;
199
cristybb503372010-05-27 20:51:26 +0000200 size_t
cristy3ed852e2009-09-05 21:47:34 +0000201 bit,
cristyeaedf062010-05-29 22:36:02 +0000202 byte,
cristy3ed852e2009-09-05 21:47:34 +0000203 bytes_per_line,
cristyeaedf062010-05-29 22:36:02 +0000204 one,
cristy3ed852e2009-09-05 21:47:34 +0000205 scanline_pad;
206
cristyebc891a2011-04-24 23:04:16 +0000207 ssize_t
208 count,
209 offset,
210 y;
211
cristy3ed852e2009-09-05 21:47:34 +0000212 /*
213 Open image file.
214 */
215 assert(image_info != (const ImageInfo *) NULL);
216 assert(image_info->signature == MagickSignature);
217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"%s",image_info->filename);
218 assert(exception != (ExceptionInfo *) NULL);
219 assert(exception->signature == MagickSignature);
cristy1f1de182011-10-05 18:41:34 +0000220 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000221 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
222 if (status == MagickFalse)
223 {
224 image=DestroyImageList(image);
225 return((Image *) NULL);
226 }
227 icon_file.reserved=(short) ReadBlobLSBShort(image);
228 icon_file.resource_type=(short) ReadBlobLSBShort(image);
229 icon_file.count=(short) ReadBlobLSBShort(image);
230 if ((icon_file.reserved != 0) ||
231 ((icon_file.resource_type != 1) && (icon_file.resource_type != 2)) ||
232 (icon_file.count > MaxIcons))
233 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
234 for (i=0; i < icon_file.count; i++)
235 {
236 icon_file.directory[i].width=(unsigned char) ReadBlobByte(image);
237 icon_file.directory[i].height=(unsigned char) ReadBlobByte(image);
238 icon_file.directory[i].colors=(unsigned char) ReadBlobByte(image);
239 icon_file.directory[i].reserved=(unsigned char) ReadBlobByte(image);
240 icon_file.directory[i].planes=(unsigned short) ReadBlobLSBShort(image);
241 icon_file.directory[i].bits_per_pixel=(unsigned short)
242 ReadBlobLSBShort(image);
243 icon_file.directory[i].size=ReadBlobLSBLong(image);
244 icon_file.directory[i].offset=ReadBlobLSBLong(image);
245 }
cristyeaedf062010-05-29 22:36:02 +0000246 one=1;
cristy3ed852e2009-09-05 21:47:34 +0000247 for (i=0; i < icon_file.count; i++)
248 {
249 /*
250 Verify Icon identifier.
251 */
252 offset=(ssize_t) SeekBlob(image,(MagickOffsetType)
253 icon_file.directory[i].offset,SEEK_SET);
254 if (offset < 0)
255 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
256 icon_info.size=ReadBlobLSBLong(image);
cristyd63c0502011-01-03 15:45:11 +0000257 icon_info.width=(unsigned char) ((int) ReadBlobLSBLong(image));
258 icon_info.height=(unsigned char) ((int) ReadBlobLSBLong(image)/2);
cristy4e09e032011-10-05 19:42:10 +0000259 icon_info.planes=ReadBlobLSBShort(image);
260 icon_info.bits_per_pixel=ReadBlobLSBShort(image);
261 if ((icon_info.planes == 18505) && (icon_info.bits_per_pixel == 21060))
cristy3ed852e2009-09-05 21:47:34 +0000262 {
263 Image
264 *icon_image;
265
266 ImageInfo
267 *read_info;
268
cristyf054ee72011-10-05 17:04:08 +0000269 size_t
270 length;
271
272 unsigned char
273 *png;
274
cristy3ed852e2009-09-05 21:47:34 +0000275 /*
276 Icon image encoded as a compressed PNG image.
277 */
cristyf054ee72011-10-05 17:04:08 +0000278 length=icon_file.directory[i].size;
cristy4e09e032011-10-05 19:42:10 +0000279 png=(unsigned char *) AcquireQuantumMemory(length+16,sizeof(*png));
cristyf054ee72011-10-05 17:04:08 +0000280 if (png == (unsigned char *) NULL)
281 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
282 (void) CopyMagickMemory(png,"\211PNG\r\n\032\n\000\000\000\015",12);
cristy4e09e032011-10-05 19:42:10 +0000283 png[12]=(unsigned char) icon_info.planes;
284 png[13]=(unsigned char) (icon_info.planes >> 8);
285 png[14]=(unsigned char) icon_info.bits_per_pixel;
286 png[15]=(unsigned char) (icon_info.bits_per_pixel >> 8);
287 count=ReadBlob(image,length-16,png+16);
288 if (count != (ssize_t) (length-16))
cristyf054ee72011-10-05 17:04:08 +0000289 {
290 png=(unsigned char *) RelinquishMagickMemory(png);
291 ThrowReaderException(CorruptImageError,
292 "InsufficientImageDataInFile");
293 }
cristy3ed852e2009-09-05 21:47:34 +0000294 read_info=CloneImageInfo(image_info);
295 (void) CopyMagickString(read_info->magick,"PNG",MaxTextExtent);
cristy4e09e032011-10-05 19:42:10 +0000296 icon_image=BlobToImage(read_info,png,length+16,exception);
cristy3ed852e2009-09-05 21:47:34 +0000297 read_info=DestroyImageInfo(read_info);
cristyf054ee72011-10-05 17:04:08 +0000298 png=(unsigned char *) RelinquishMagickMemory(png);
cristy47de5a92011-10-05 01:47:42 +0000299 if (icon_image == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000300 {
cristy47de5a92011-10-05 01:47:42 +0000301 image=DestroyImageList(image);
302 return((Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000303 }
cristy47de5a92011-10-05 01:47:42 +0000304 DestroyBlob(icon_image);
305 icon_image->blob=ReferenceBlob(image->blob);
306 ReplaceImageInList(&image,icon_image);
cristy3ed852e2009-09-05 21:47:34 +0000307 }
308 else
309 {
cristyf054ee72011-10-05 17:04:08 +0000310 if (icon_info.bits_per_pixel > 32)
311 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
312 icon_info.compression=ReadBlobLSBLong(image);
313 icon_info.image_size=ReadBlobLSBLong(image);
314 icon_info.x_pixels=ReadBlobLSBLong(image);
315 icon_info.y_pixels=ReadBlobLSBLong(image);
316 icon_info.number_colors=ReadBlobLSBLong(image);
317 icon_info.colors_important=ReadBlobLSBLong(image);
cristy8a46d822012-08-28 23:32:39 +0000318 image->alpha_trait=BlendPixelTrait;
cristyf054ee72011-10-05 17:04:08 +0000319 image->columns=(size_t) icon_file.directory[i].width;
320 if ((ssize_t) image->columns > icon_info.width)
321 image->columns=(size_t) icon_info.width;
cristy4e09e032011-10-05 19:42:10 +0000322 if (image->columns == 0)
323 image->columns=256;
cristyf054ee72011-10-05 17:04:08 +0000324 image->rows=(size_t) icon_file.directory[i].height;
325 if ((ssize_t) image->rows > icon_info.height)
326 image->rows=(size_t) icon_info.height;
cristy4e09e032011-10-05 19:42:10 +0000327 if (image->rows == 0)
328 image->rows=256;
cristyf054ee72011-10-05 17:04:08 +0000329 image->depth=icon_info.bits_per_pixel;
330 if (image->debug != MagickFalse)
331 {
332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
333 " scene = %.20g",(double) i);
334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
335 " size = %.20g",(double) icon_info.size);
336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
337 " width = %.20g",(double) icon_file.directory[i].width);
338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
339 " height = %.20g",(double) icon_file.directory[i].height);
340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
341 " colors = %.20g",(double ) icon_info.number_colors);
342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
343 " planes = %.20g",(double) icon_info.planes);
344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
345 " bpp = %.20g",(double) icon_info.bits_per_pixel);
346 }
347 if ((icon_info.number_colors != 0) || (icon_info.bits_per_pixel <= 16))
348 {
349 image->storage_class=PseudoClass;
350 image->colors=icon_info.number_colors;
351 if (image->colors == 0)
352 image->colors=one << icon_info.bits_per_pixel;
353 }
354 if (image->storage_class == PseudoClass)
355 {
356 register ssize_t
357 i;
cristy3ed852e2009-09-05 21:47:34 +0000358
cristyf054ee72011-10-05 17:04:08 +0000359 size_t
360 number_colors,
361 one;
362
363 unsigned char
364 *icon_colormap;
365
366 /*
367 Read Icon raster colormap.
368 */
369 one=1;
370 number_colors=one << icon_info.bits_per_pixel;
371 if (AcquireImageColormap(image,number_colors,exception) == MagickFalse)
372 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
373 icon_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
374 image->colors,4UL*sizeof(*icon_colormap));
375 if (icon_colormap == (unsigned char *) NULL)
376 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
377 count=ReadBlob(image,(size_t) (4*image->colors),icon_colormap);
378 if (count != (ssize_t) (4*image->colors))
379 ThrowReaderException(CorruptImageError,
380 "InsufficientImageDataInFile");
381 p=icon_colormap;
382 for (i=0; i < (ssize_t) image->colors; i++)
383 {
384 image->colormap[i].blue=(Quantum) ScaleCharToQuantum(*p++);
385 image->colormap[i].green=(Quantum) ScaleCharToQuantum(*p++);
386 image->colormap[i].red=(Quantum) ScaleCharToQuantum(*p++);
387 p++;
388 }
389 icon_colormap=(unsigned char *) RelinquishMagickMemory(icon_colormap);
390 }
cristy3ed852e2009-09-05 21:47:34 +0000391 /*
392 Convert Icon raster image to pixel packets.
393 */
cristyf054ee72011-10-05 17:04:08 +0000394 if ((image_info->ping != MagickFalse) &&
395 (image_info->number_scenes != 0))
396 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
397 break;
cristy3ed852e2009-09-05 21:47:34 +0000398 bytes_per_line=(((image->columns*icon_info.bits_per_pixel)+31) &
399 ~31) >> 3;
cristyda16f162011-02-19 23:52:17 +0000400 (void) bytes_per_line;
cristy3ed852e2009-09-05 21:47:34 +0000401 scanline_pad=((((image->columns*icon_info.bits_per_pixel)+31) & ~31)-
402 (image->columns*icon_info.bits_per_pixel)) >> 3;
403 switch (icon_info.bits_per_pixel)
404 {
405 case 1:
406 {
407 /*
408 Convert bitmap scanline.
409 */
cristybb503372010-05-27 20:51:26 +0000410 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000411 {
412 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000413 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000414 break;
cristybb503372010-05-27 20:51:26 +0000415 for (x=0; x < (ssize_t) (image->columns-7); x+=8)
cristy3ed852e2009-09-05 21:47:34 +0000416 {
cristyf054ee72011-10-05 17:04:08 +0000417 byte=(size_t) ReadBlobByte(image);
cristy3ed852e2009-09-05 21:47:34 +0000418 for (bit=0; bit < 8; bit++)
cristy4c08aed2011-07-01 19:47:50 +0000419 {
420 SetPixelIndex(image,((byte & (0x80 >> bit)) != 0 ? 0x01 :
421 0x00),q);
cristyed231572011-07-14 02:18:59 +0000422 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000423 }
cristy3ed852e2009-09-05 21:47:34 +0000424 }
425 if ((image->columns % 8) != 0)
426 {
cristyf054ee72011-10-05 17:04:08 +0000427 byte=(size_t) ReadBlobByte(image);
cristy3ed852e2009-09-05 21:47:34 +0000428 for (bit=0; bit < (image->columns % 8); bit++)
cristy4c08aed2011-07-01 19:47:50 +0000429 {
430 SetPixelIndex(image,((byte & (0x80 >> bit)) != 0 ? 0x01 :
431 0x00),q);
cristyed231572011-07-14 02:18:59 +0000432 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000433 }
cristy3ed852e2009-09-05 21:47:34 +0000434 }
cristybb503372010-05-27 20:51:26 +0000435 for (x=0; x < (ssize_t) scanline_pad; x++)
cristyf054ee72011-10-05 17:04:08 +0000436 (void) ReadBlobByte(image);
cristy3ed852e2009-09-05 21:47:34 +0000437 if (SyncAuthenticPixels(image,exception) == MagickFalse)
438 break;
439 if (image->previous == (Image *) NULL)
440 {
441 status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
442 image->rows);
443 if (status == MagickFalse)
444 break;
445 }
446 }
447 break;
448 }
449 case 4:
450 {
451 /*
452 Read 4-bit Icon scanline.
453 */
cristybb503372010-05-27 20:51:26 +0000454 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000455 {
456 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000457 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000458 break;
cristybb503372010-05-27 20:51:26 +0000459 for (x=0; x < ((ssize_t) image->columns-1); x+=2)
cristy3ed852e2009-09-05 21:47:34 +0000460 {
cristyf054ee72011-10-05 17:04:08 +0000461 byte=(size_t) ReadBlobByte(image);
cristy4c08aed2011-07-01 19:47:50 +0000462 SetPixelIndex(image,((byte >> 4) & 0xf),q);
cristyed231572011-07-14 02:18:59 +0000463 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000464 SetPixelIndex(image,((byte) & 0xf),q);
cristyed231572011-07-14 02:18:59 +0000465 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000466 }
467 if ((image->columns % 2) != 0)
468 {
cristyf054ee72011-10-05 17:04:08 +0000469 byte=(size_t) ReadBlobByte(image);
cristy4c08aed2011-07-01 19:47:50 +0000470 SetPixelIndex(image,((byte >> 4) & 0xf),q);
cristyed231572011-07-14 02:18:59 +0000471 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000472 }
cristybb503372010-05-27 20:51:26 +0000473 for (x=0; x < (ssize_t) scanline_pad; x++)
cristyf054ee72011-10-05 17:04:08 +0000474 (void) ReadBlobByte(image);
cristy3ed852e2009-09-05 21:47:34 +0000475 if (SyncAuthenticPixels(image,exception) == MagickFalse)
476 break;
477 if (image->previous == (Image *) NULL)
478 {
479 status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
480 image->rows);
481 if (status == MagickFalse)
482 break;
483 }
484 }
485 break;
486 }
487 case 8:
488 {
489 /*
490 Convert PseudoColor scanline.
491 */
cristybb503372010-05-27 20:51:26 +0000492 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000493 {
494 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000495 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000496 break;
cristybb503372010-05-27 20:51:26 +0000497 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000498 {
cristyf054ee72011-10-05 17:04:08 +0000499 byte=(size_t) ReadBlobByte(image);
cristy4c08aed2011-07-01 19:47:50 +0000500 SetPixelIndex(image,byte,q);
cristyed231572011-07-14 02:18:59 +0000501 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000502 }
cristybb503372010-05-27 20:51:26 +0000503 for (x=0; x < (ssize_t) scanline_pad; x++)
cristyf054ee72011-10-05 17:04:08 +0000504 (void) ReadBlobByte(image);
cristy3ed852e2009-09-05 21:47:34 +0000505 if (SyncAuthenticPixels(image,exception) == MagickFalse)
506 break;
507 if (image->previous == (Image *) NULL)
508 {
509 status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
510 image->rows);
511 if (status == MagickFalse)
512 break;
513 }
514 }
515 break;
516 }
517 case 16:
518 {
519 /*
520 Convert PseudoColor scanline.
521 */
cristybb503372010-05-27 20:51:26 +0000522 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000523 {
524 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000525 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000526 break;
cristybb503372010-05-27 20:51:26 +0000527 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000528 {
cristyf054ee72011-10-05 17:04:08 +0000529 byte=(size_t) ReadBlobByte(image);
530 byte|=(size_t) (ReadBlobByte(image) << 8);
cristy4c08aed2011-07-01 19:47:50 +0000531 SetPixelIndex(image,byte,q);
cristyed231572011-07-14 02:18:59 +0000532 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000533 }
cristybb503372010-05-27 20:51:26 +0000534 for (x=0; x < (ssize_t) scanline_pad; x++)
cristyf054ee72011-10-05 17:04:08 +0000535 (void) ReadBlobByte(image);
cristy3ed852e2009-09-05 21:47:34 +0000536 if (SyncAuthenticPixels(image,exception) == MagickFalse)
537 break;
538 if (image->previous == (Image *) NULL)
539 {
540 status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
541 image->rows);
542 if (status == MagickFalse)
543 break;
544 }
545 }
546 break;
547 }
548 case 24:
549 case 32:
550 {
551 /*
552 Convert DirectColor scanline.
553 */
cristybb503372010-05-27 20:51:26 +0000554 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000555 {
556 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000557 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000558 break;
cristybb503372010-05-27 20:51:26 +0000559 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000560 {
cristyf054ee72011-10-05 17:04:08 +0000561 SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
562 ReadBlobByte(image)),q);
563 SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
564 ReadBlobByte(image)),q);
565 SetPixelRed(image,ScaleCharToQuantum((unsigned char)
566 ReadBlobByte(image)),q);
cristy3ed852e2009-09-05 21:47:34 +0000567 if (icon_info.bits_per_pixel == 32)
cristyf054ee72011-10-05 17:04:08 +0000568 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char)
569 ReadBlobByte(image)),q);
cristyed231572011-07-14 02:18:59 +0000570 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000571 }
572 if (icon_info.bits_per_pixel == 24)
cristybb503372010-05-27 20:51:26 +0000573 for (x=0; x < (ssize_t) scanline_pad; x++)
cristyf054ee72011-10-05 17:04:08 +0000574 (void) ReadBlobByte(image);
cristy3ed852e2009-09-05 21:47:34 +0000575 if (SyncAuthenticPixels(image,exception) == MagickFalse)
576 break;
577 if (image->previous == (Image *) NULL)
578 {
579 status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
580 image->rows);
581 if (status == MagickFalse)
582 break;
583 }
584 }
585 break;
586 }
587 default:
588 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
589 }
cristy5dd71882010-08-01 20:53:13 +0000590 if (image_info->ping == MagickFalse)
cristyea1a8aa2011-10-20 13:24:06 +0000591 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000592 if (icon_info.bits_per_pixel != 32)
593 {
594 /*
595 Read the ICON alpha mask.
596 */
597 image->storage_class=DirectClass;
cristybb503372010-05-27 20:51:26 +0000598 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000599 {
600 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000601 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000602 break;
cristybb503372010-05-27 20:51:26 +0000603 for (x=0; x < ((ssize_t) image->columns-7); x+=8)
cristy3ed852e2009-09-05 21:47:34 +0000604 {
cristyf054ee72011-10-05 17:04:08 +0000605 byte=(size_t) ReadBlobByte(image);
cristy3ed852e2009-09-05 21:47:34 +0000606 for (bit=0; bit < 8; bit++)
cristy4c08aed2011-07-01 19:47:50 +0000607 {
608 SetPixelAlpha(image,(((byte & (0x80 >> bit)) != 0) ?
609 TransparentAlpha : OpaqueAlpha),q);
cristyed231572011-07-14 02:18:59 +0000610 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000611 }
cristy3ed852e2009-09-05 21:47:34 +0000612 }
613 if ((image->columns % 8) != 0)
614 {
cristyf054ee72011-10-05 17:04:08 +0000615 byte=(size_t) ReadBlobByte(image);
cristy3ed852e2009-09-05 21:47:34 +0000616 for (bit=0; bit < (image->columns % 8); bit++)
cristy4c08aed2011-07-01 19:47:50 +0000617 {
618 SetPixelAlpha(image,(((byte & (0x80 >> bit)) != 0) ?
619 TransparentAlpha : OpaqueAlpha),q);
cristyed231572011-07-14 02:18:59 +0000620 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000621 }
cristy3ed852e2009-09-05 21:47:34 +0000622 }
cristy0e8200f2009-09-13 21:10:06 +0000623 if ((image->columns % 32) != 0)
cristybb503372010-05-27 20:51:26 +0000624 for (x=0; x < (ssize_t) ((32-(image->columns % 32))/8); x++)
cristyf054ee72011-10-05 17:04:08 +0000625 (void) ReadBlobByte(image);
cristy3ed852e2009-09-05 21:47:34 +0000626 if (SyncAuthenticPixels(image,exception) == MagickFalse)
627 break;
628 }
629 }
630 if (EOFBlob(image) != MagickFalse)
631 {
632 ThrowFileException(exception,CorruptImageError,
633 "UnexpectedEndOfFile",image->filename);
634 break;
635 }
636 }
637 /*
638 Proceed to next image.
639 */
640 if (image_info->number_scenes != 0)
641 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
642 break;
cristybb503372010-05-27 20:51:26 +0000643 if (i < (ssize_t) (icon_file.count-1))
cristy3ed852e2009-09-05 21:47:34 +0000644 {
645 /*
646 Allocate next image structure.
647 */
cristy1f1de182011-10-05 18:41:34 +0000648 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000649 if (GetNextImageInList(image) == (Image *) NULL)
650 {
651 image=DestroyImageList(image);
652 return((Image *) NULL);
653 }
654 image=SyncNextImageInList(image);
655 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
656 GetBlobSize(image));
657 if (status == MagickFalse)
658 break;
659 }
660 }
661 (void) CloseBlob(image);
662 return(GetFirstImageInList(image));
663}
664
665/*
666%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
667% %
668% %
669% %
670% R e g i s t e r I C O N I m a g e %
671% %
672% %
673% %
674%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
675%
676% RegisterICONImage() adds attributes for the Icon image format to
677% the list of supported formats. The attributes include the image format
678% tag, a method to read and/or write the format, whether the format
679% supports the saving of more than one frame to the same file or blob,
680% whether the format supports native in-memory I/O, and a brief
681% description of the format.
682%
683% The format of the RegisterICONImage method is:
684%
cristybb503372010-05-27 20:51:26 +0000685% size_t RegisterICONImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000686%
687*/
cristybb503372010-05-27 20:51:26 +0000688ModuleExport size_t RegisterICONImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000689{
690 MagickInfo
691 *entry;
692
693 entry=SetMagickInfo("CUR");
694 entry->decoder=(DecodeImageHandler *) ReadICONImage;
695 entry->encoder=(EncodeImageHandler *) WriteICONImage;
696 entry->adjoin=MagickFalse;
697 entry->seekable_stream=MagickTrue;
698 entry->description=ConstantString("Microsoft icon");
699 entry->module=ConstantString("CUR");
700 (void) RegisterMagickInfo(entry);
701 entry=SetMagickInfo("ICO");
702 entry->decoder=(DecodeImageHandler *) ReadICONImage;
703 entry->encoder=(EncodeImageHandler *) WriteICONImage;
704 entry->adjoin=MagickTrue;
705 entry->seekable_stream=MagickTrue;
706 entry->description=ConstantString("Microsoft icon");
707 entry->module=ConstantString("ICON");
708 (void) RegisterMagickInfo(entry);
709 entry=SetMagickInfo("ICON");
710 entry->decoder=(DecodeImageHandler *) ReadICONImage;
711 entry->encoder=(EncodeImageHandler *) WriteICONImage;
712 entry->adjoin=MagickFalse;
713 entry->seekable_stream=MagickTrue;
714 entry->description=ConstantString("Microsoft icon");
715 entry->module=ConstantString("ICON");
716 (void) RegisterMagickInfo(entry);
717 return(MagickImageCoderSignature);
718}
719
720/*
721%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
722% %
723% %
724% %
725% U n r e g i s t e r I C O N I m a g e %
726% %
727% %
728% %
729%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
730%
731% UnregisterICONImage() removes format registrations made by the
732% ICON module from the list of supported formats.
733%
734% The format of the UnregisterICONImage method is:
735%
736% UnregisterICONImage(void)
737%
738*/
739ModuleExport void UnregisterICONImage(void)
740{
741 (void) UnregisterMagickInfo("CUR");
742 (void) UnregisterMagickInfo("ICO");
743 (void) UnregisterMagickInfo("ICON");
744}
745
746/*
747%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
748% %
749% %
750% %
751% W r i t e I C O N I m a g e %
752% %
753% %
754% %
755%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
756%
757% WriteICONImage() writes an image in Microsoft Windows bitmap encoded
758% image format, version 3 for Windows or (if the image has a matte channel)
759% version 4.
760%
cristy83cb91e2012-10-31 00:58:22 +0000761% It encodes any subimage as a compressed PNG image ("BI_PNG)", only when its
762% dimensions are 256x256 and image->compression is undefined or is defined as
763% ZipCompression.
glennrp4b708b82012-07-17 19:15:58 +0000764%
cristy3ed852e2009-09-05 21:47:34 +0000765% The format of the WriteICONImage method is:
766%
767% MagickBooleanType WriteICONImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +0000768% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000769%
770% A description of each parameter follows.
771%
772% o image_info: the image info.
773%
774% o image: The image.
775%
cristy1e178e72011-08-28 19:44:34 +0000776% o exception: return any errors or warnings in this structure.
777%
cristy3ed852e2009-09-05 21:47:34 +0000778*/
779static MagickBooleanType WriteICONImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +0000780 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000781{
782 IconFile
783 icon_file;
784
785 IconInfo
786 icon_info;
787
788 Image
dirk9e137702014-01-09 19:29:59 +0000789 *images,
cristy3ed852e2009-09-05 21:47:34 +0000790 *next;
791
cristy3ed852e2009-09-05 21:47:34 +0000792 MagickBooleanType
793 status;
794
795 MagickOffsetType
796 offset,
797 scene;
798
cristy4c08aed2011-07-01 19:47:50 +0000799 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000800 *p;
801
cristybb503372010-05-27 20:51:26 +0000802 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000803 i,
804 x;
805
806 register unsigned char
807 *q;
808
cristyebc891a2011-04-24 23:04:16 +0000809 size_t
810 bytes_per_line,
811 scanline_pad;
812
813 ssize_t
814 y;
815
cristy3ed852e2009-09-05 21:47:34 +0000816 unsigned char
817 bit,
818 byte,
819 *pixels;
820
cristy3ed852e2009-09-05 21:47:34 +0000821 /*
822 Open output image file.
823 */
824 assert(image_info != (const ImageInfo *) NULL);
825 assert(image_info->signature == MagickSignature);
826 assert(image != (Image *) NULL);
827 assert(image->signature == MagickSignature);
828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +0000829 assert(exception != (ExceptionInfo *) NULL);
830 assert(exception->signature == MagickSignature);
cristy1e178e72011-08-28 19:44:34 +0000831 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +0000832 if (status == MagickFalse)
833 return(status);
dirk9e137702014-01-09 19:29:59 +0000834 images=(Image *) NULL;
835 if (GetImageOption(image_info,"icon:auto-resize") != (const char *) NULL)
836 {
837 size_t
838 sizes[]={192,128,96,64,48,40,32,24,16};
839
840 if ((image->columns != 256L) && (image->rows != 256L))
841 ThrowWriterException(ImageError,"SizeMustBe256x256");
842 if (image->next != (Image *) NULL)
843 ThrowWriterException(ImageError,"OnlyOneImageAllowed");
844 images=CloneImage(image,0,0,MagickTrue,exception);
845 if (images == (Image *) NULL)
846 return(MagickFalse);
847 scene=sizeof(sizes)/sizeof(sizes[0]);
848 for (i=0; i < scene; i++)
849 {
dirkc1e52002014-01-09 20:22:42 +0000850 next=ResizeImage(image,sizes[i],sizes[i],image->filter,exception);
851 if (next == (Image *) NULL)
852 {
853 images=DestroyImageList(images);
854 return(MagickFalse);
dirk9e137702014-01-09 19:29:59 +0000855 }
856 AppendImageToList(&images,next);
857 }
858 scene++;
859 }
860 else
861 {
862 scene=0;
863 next=image;
864 do
865 {
866 if ((image->columns > 256L) || (image->rows > 256L))
867 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
868 scene++;
869 next=SyncNextImageInList(next);
870 } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
871 }
cristy3ed852e2009-09-05 21:47:34 +0000872 /*
873 Dump out a ICON header template to be properly initialized later.
874 */
875 (void) WriteBlobLSBShort(image,0);
876 (void) WriteBlobLSBShort(image,1);
877 (void) WriteBlobLSBShort(image,(unsigned char) scene);
878 (void) ResetMagickMemory(&icon_file,0,sizeof(icon_file));
879 (void) ResetMagickMemory(&icon_info,0,sizeof(icon_info));
880 scene=0;
dirk9e137702014-01-09 19:29:59 +0000881 next=(images != (Image *) NULL) ? images : image;
cristy3ed852e2009-09-05 21:47:34 +0000882 do
883 {
884 (void) WriteBlobByte(image,icon_file.directory[scene].width);
885 (void) WriteBlobByte(image,icon_file.directory[scene].height);
886 (void) WriteBlobByte(image,icon_file.directory[scene].colors);
887 (void) WriteBlobByte(image,icon_file.directory[scene].reserved);
888 (void) WriteBlobLSBShort(image,icon_file.directory[scene].planes);
889 (void) WriteBlobLSBShort(image,icon_file.directory[scene].bits_per_pixel);
cristy0b29b252010-05-30 01:59:46 +0000890 (void) WriteBlobLSBLong(image,(unsigned int)
891 icon_file.directory[scene].size);
892 (void) WriteBlobLSBLong(image,(unsigned int)
893 icon_file.directory[scene].offset);
cristy3ed852e2009-09-05 21:47:34 +0000894 scene++;
895 next=SyncNextImageInList(next);
896 } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
897 scene=0;
dirk9e137702014-01-09 19:29:59 +0000898 next=(images != (Image *) NULL) ? images : image;
cristy3ed852e2009-09-05 21:47:34 +0000899 do
900 {
glennrp4b708b82012-07-17 19:15:58 +0000901 if ((next->columns > 255L) && (next->rows > 255L) &&
902 ((next->compression == UndefinedCompression) ||
903 (next->compression == ZipCompression)))
cristy3ed852e2009-09-05 21:47:34 +0000904 {
905 Image
906 *write_image;
907
908 ImageInfo
909 *write_info;
910
911 size_t
912 length;
913
914 unsigned char
915 *png;
916
cristy1e178e72011-08-28 19:44:34 +0000917 write_image=CloneImage(next,0,0,MagickTrue,exception);
dirkc1e52002014-01-09 20:22:42 +0000918 if (write_image == (Image *) NULL)
919 {
920 images=DestroyImageList(images);
921 return(MagickFalse);
dirk9e137702014-01-09 19:29:59 +0000922 }
cristy3ed852e2009-09-05 21:47:34 +0000923 write_info=CloneImageInfo(image_info);
924 (void) CopyMagickString(write_info->filename,"PNG:",MaxTextExtent);
glennrp4b708b82012-07-17 19:15:58 +0000925
926 /* Don't write any ancillary chunks except for gAMA */
927 (void) SetImageArtifact(write_image,"png:include-chunk","none,gama");
928
929 /* Only write PNG32 formatted PNG (32-bit RGBA), 8 bits per channel */
930 (void) SetImageArtifact(write_image,"png:format","png32");
931
cristy3ed852e2009-09-05 21:47:34 +0000932 png=(unsigned char *) ImageToBlob(write_info,write_image,&length,
cristy1e178e72011-08-28 19:44:34 +0000933 exception);
cristy3ed852e2009-09-05 21:47:34 +0000934 write_image=DestroyImage(write_image);
935 write_info=DestroyImageInfo(write_info);
dirkc1e52002014-01-09 20:22:42 +0000936 if (png == (unsigned char *) NULL)
937 {
938 images=DestroyImageList(images);
939 return(MagickFalse);
dirk9e137702014-01-09 19:29:59 +0000940 }
cristy3ed852e2009-09-05 21:47:34 +0000941 icon_file.directory[scene].width=0;
942 icon_file.directory[scene].height=0;
943 icon_file.directory[scene].colors=0;
944 icon_file.directory[scene].reserved=0;
945 icon_file.directory[scene].planes=1;
946 icon_file.directory[scene].bits_per_pixel=32;
cristybb503372010-05-27 20:51:26 +0000947 icon_file.directory[scene].size=(size_t) length;
948 icon_file.directory[scene].offset=(size_t) TellBlob(image);
cristy3ed852e2009-09-05 21:47:34 +0000949 (void) WriteBlob(image,(size_t) length,png);
950 png=(unsigned char *) RelinquishMagickMemory(png);
951 }
952 else
953 {
954 /*
955 Initialize ICON raster file header.
956 */
cristy3d9f5ba2012-06-26 13:37:31 +0000957 if (IssRGBCompatibleColorspace(next->colorspace) == MagickFalse)
cristyc511e882012-04-16 21:11:14 +0000958 (void) TransformImageColorspace(next,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +0000959 icon_info.file_size=14+12+28;
960 icon_info.offset_bits=icon_info.file_size;
961 icon_info.compression=BI_RGB;
962 if ((next->storage_class != DirectClass) && (next->colors > 256))
cristy1e178e72011-08-28 19:44:34 +0000963 (void) SetImageStorageClass(next,DirectClass,exception);
cristy3ed852e2009-09-05 21:47:34 +0000964 if (next->storage_class == DirectClass)
965 {
966 /*
967 Full color ICON raster.
968 */
969 icon_info.number_colors=0;
970 icon_info.bits_per_pixel=32;
cristybb503372010-05-27 20:51:26 +0000971 icon_info.compression=(size_t) BI_RGB;
cristy3ed852e2009-09-05 21:47:34 +0000972 }
973 else
974 {
cristy35ef8242010-06-03 16:24:13 +0000975 size_t
976 one;
977
cristy3ed852e2009-09-05 21:47:34 +0000978 /*
979 Colormapped ICON raster.
980 */
981 icon_info.bits_per_pixel=8;
982 if (next->colors <= 256)
983 icon_info.bits_per_pixel=8;
984 if (next->colors <= 16)
985 icon_info.bits_per_pixel=4;
986 if (next->colors <= 2)
987 icon_info.bits_per_pixel=1;
cristy35ef8242010-06-03 16:24:13 +0000988 one=1;
989 icon_info.number_colors=one << icon_info.bits_per_pixel;
cristy3ed852e2009-09-05 21:47:34 +0000990 if (icon_info.number_colors < next->colors)
991 {
cristy1e178e72011-08-28 19:44:34 +0000992 (void) SetImageStorageClass(next,DirectClass,exception);
cristy3ed852e2009-09-05 21:47:34 +0000993 icon_info.number_colors=0;
994 icon_info.bits_per_pixel=(unsigned short) 24;
cristybb503372010-05-27 20:51:26 +0000995 icon_info.compression=(size_t) BI_RGB;
cristy3ed852e2009-09-05 21:47:34 +0000996 }
997 else
998 {
cristy0b29b252010-05-30 01:59:46 +0000999 size_t
1000 one;
1001
1002 one=1;
1003 icon_info.file_size+=3*(one << icon_info.bits_per_pixel);
1004 icon_info.offset_bits+=3*(one << icon_info.bits_per_pixel);
1005 icon_info.file_size+=(one << icon_info.bits_per_pixel);
1006 icon_info.offset_bits+=(one << icon_info.bits_per_pixel);
cristy3ed852e2009-09-05 21:47:34 +00001007 }
1008 }
1009 bytes_per_line=(((next->columns*icon_info.bits_per_pixel)+31) &
1010 ~31) >> 3;
1011 icon_info.ba_offset=0;
cristybb503372010-05-27 20:51:26 +00001012 icon_info.width=(ssize_t) next->columns;
1013 icon_info.height=(ssize_t) next->rows;
cristy3ed852e2009-09-05 21:47:34 +00001014 icon_info.planes=1;
1015 icon_info.image_size=bytes_per_line*next->rows;
1016 icon_info.size=40;
1017 icon_info.size+=(4*icon_info.number_colors);
1018 icon_info.size+=icon_info.image_size;
1019 icon_info.size+=(((icon_info.width+31) & ~31) >> 3)*icon_info.height;
1020 icon_info.file_size+=icon_info.image_size;
1021 icon_info.x_pixels=0;
1022 icon_info.y_pixels=0;
1023 switch (next->units)
1024 {
1025 case UndefinedResolution:
1026 case PixelsPerInchResolution:
1027 {
cristy2a11bef2011-10-28 18:33:11 +00001028 icon_info.x_pixels=(size_t) (100.0*next->resolution.x/2.54);
1029 icon_info.y_pixels=(size_t) (100.0*next->resolution.y/2.54);
cristy3ed852e2009-09-05 21:47:34 +00001030 break;
1031 }
1032 case PixelsPerCentimeterResolution:
1033 {
cristy2a11bef2011-10-28 18:33:11 +00001034 icon_info.x_pixels=(size_t) (100.0*next->resolution.x);
1035 icon_info.y_pixels=(size_t) (100.0*next->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001036 break;
1037 }
1038 }
1039 icon_info.colors_important=icon_info.number_colors;
1040 /*
1041 Convert MIFF to ICON raster pixels.
1042 */
1043 pixels=(unsigned char *) AcquireQuantumMemory((size_t)
1044 icon_info.image_size,sizeof(*pixels));
dirkc1e52002014-01-09 20:22:42 +00001045 if (pixels == (unsigned char *) NULL)
1046 {
dirk9e137702014-01-09 19:29:59 +00001047 images=DestroyImageList(images);
1048 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
dirkc1e52002014-01-09 20:22:42 +00001049 }
cristy3ed852e2009-09-05 21:47:34 +00001050 (void) ResetMagickMemory(pixels,0,(size_t) icon_info.image_size);
1051 switch (icon_info.bits_per_pixel)
1052 {
1053 case 1:
1054 {
cristybb503372010-05-27 20:51:26 +00001055 size_t
cristy3ed852e2009-09-05 21:47:34 +00001056 bit,
1057 byte;
1058
1059 /*
1060 Convert PseudoClass image to a ICON monochrome image.
1061 */
cristybb503372010-05-27 20:51:26 +00001062 for (y=0; y < (ssize_t) next->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001063 {
cristy1e178e72011-08-28 19:44:34 +00001064 p=GetVirtualPixels(next,0,y,next->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001065 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001066 break;
cristy3ed852e2009-09-05 21:47:34 +00001067 q=pixels+(next->rows-y-1)*bytes_per_line;
1068 bit=0;
1069 byte=0;
cristybb503372010-05-27 20:51:26 +00001070 for (x=0; x < (ssize_t) next->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001071 {
1072 byte<<=1;
cristy4c08aed2011-07-01 19:47:50 +00001073 byte|=GetPixelIndex(next,p) != 0 ? 0x01 : 0x00;
cristy3ed852e2009-09-05 21:47:34 +00001074 bit++;
1075 if (bit == 8)
1076 {
1077 *q++=(unsigned char) byte;
1078 bit=0;
1079 byte=0;
1080 }
cristyed231572011-07-14 02:18:59 +00001081 p+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001082 }
cristy3ed852e2009-09-05 21:47:34 +00001083 if (bit != 0)
1084 *q++=(unsigned char) (byte << (8-bit));
1085 if (next->previous == (Image *) NULL)
1086 {
1087 status=SetImageProgress(next,SaveImageTag,y,next->rows);
1088 if (status == MagickFalse)
1089 break;
1090 }
1091 }
1092 break;
1093 }
1094 case 4:
1095 {
cristybb503372010-05-27 20:51:26 +00001096 size_t
cristy3ed852e2009-09-05 21:47:34 +00001097 nibble,
1098 byte;
1099
1100 /*
1101 Convert PseudoClass image to a ICON monochrome image.
1102 */
cristybb503372010-05-27 20:51:26 +00001103 for (y=0; y < (ssize_t) next->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001104 {
cristy1e178e72011-08-28 19:44:34 +00001105 p=GetVirtualPixels(next,0,y,next->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001106 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001107 break;
cristy3ed852e2009-09-05 21:47:34 +00001108 q=pixels+(next->rows-y-1)*bytes_per_line;
1109 nibble=0;
1110 byte=0;
cristybb503372010-05-27 20:51:26 +00001111 for (x=0; x < (ssize_t) next->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001112 {
1113 byte<<=4;
cristy4c08aed2011-07-01 19:47:50 +00001114 byte|=((size_t) GetPixelIndex(next,p) & 0x0f);
cristy3ed852e2009-09-05 21:47:34 +00001115 nibble++;
1116 if (nibble == 2)
1117 {
1118 *q++=(unsigned char) byte;
1119 nibble=0;
1120 byte=0;
1121 }
cristyed231572011-07-14 02:18:59 +00001122 p+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001123 }
cristy3ed852e2009-09-05 21:47:34 +00001124 if (nibble != 0)
1125 *q++=(unsigned char) (byte << 4);
1126 if (next->previous == (Image *) NULL)
1127 {
1128 status=SetImageProgress(next,SaveImageTag,y,next->rows);
1129 if (status == MagickFalse)
1130 break;
1131 }
1132 }
1133 break;
1134 }
1135 case 8:
1136 {
1137 /*
1138 Convert PseudoClass packet to ICON pixel.
1139 */
cristybb503372010-05-27 20:51:26 +00001140 for (y=0; y < (ssize_t) next->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001141 {
cristy1e178e72011-08-28 19:44:34 +00001142 p=GetVirtualPixels(next,0,y,next->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001143 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001144 break;
cristy3ed852e2009-09-05 21:47:34 +00001145 q=pixels+(next->rows-y-1)*bytes_per_line;
cristybb503372010-05-27 20:51:26 +00001146 for (x=0; x < (ssize_t) next->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00001147 {
1148 *q++=(unsigned char) GetPixelIndex(next,p);
cristyed231572011-07-14 02:18:59 +00001149 p+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001150 }
cristy3ed852e2009-09-05 21:47:34 +00001151 if (next->previous == (Image *) NULL)
1152 {
1153 status=SetImageProgress(next,SaveImageTag,y,next->rows);
1154 if (status == MagickFalse)
1155 break;
1156 }
1157 }
1158 break;
1159 }
1160 case 24:
1161 case 32:
1162 {
1163 /*
1164 Convert DirectClass packet to ICON BGR888 or BGRA8888 pixel.
1165 */
cristybb503372010-05-27 20:51:26 +00001166 for (y=0; y < (ssize_t) next->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001167 {
cristy1e178e72011-08-28 19:44:34 +00001168 p=GetVirtualPixels(next,0,y,next->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001169 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001170 break;
1171 q=pixels+(next->rows-y-1)*bytes_per_line;
cristybb503372010-05-27 20:51:26 +00001172 for (x=0; x < (ssize_t) next->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001173 {
cristy4c08aed2011-07-01 19:47:50 +00001174 *q++=ScaleQuantumToChar(GetPixelBlue(next,p));
1175 *q++=ScaleQuantumToChar(GetPixelGreen(next,p));
1176 *q++=ScaleQuantumToChar(GetPixelRed(next,p));
cristy8a46d822012-08-28 23:32:39 +00001177 if (next->alpha_trait != BlendPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +00001178 *q++=ScaleQuantumToChar(QuantumRange);
1179 else
cristy4c08aed2011-07-01 19:47:50 +00001180 *q++=ScaleQuantumToChar(GetPixelAlpha(next,p));
cristyed231572011-07-14 02:18:59 +00001181 p+=GetPixelChannels(next);
cristy3ed852e2009-09-05 21:47:34 +00001182 }
1183 if (icon_info.bits_per_pixel == 24)
cristybb503372010-05-27 20:51:26 +00001184 for (x=3L*(ssize_t) next->columns; x < (ssize_t) bytes_per_line; x++)
cristy3ed852e2009-09-05 21:47:34 +00001185 *q++=0x00;
1186 if (next->previous == (Image *) NULL)
1187 {
1188 status=SetImageProgress(next,SaveImageTag,y,next->rows);
1189 if (status == MagickFalse)
1190 break;
1191 }
1192 }
1193 break;
1194 }
1195 }
1196 /*
1197 Write 40-byte version 3+ bitmap header.
1198 */
1199 icon_file.directory[scene].width=(unsigned char) icon_info.width;
1200 icon_file.directory[scene].height=(unsigned char) icon_info.height;
1201 icon_file.directory[scene].colors=(unsigned char)
1202 icon_info.number_colors;
1203 icon_file.directory[scene].reserved=0;
1204 icon_file.directory[scene].planes=icon_info.planes;
1205 icon_file.directory[scene].bits_per_pixel=icon_info.bits_per_pixel;
1206 icon_file.directory[scene].size=icon_info.size;
cristybb503372010-05-27 20:51:26 +00001207 icon_file.directory[scene].offset=(size_t) TellBlob(image);
cristy35ef8242010-06-03 16:24:13 +00001208 (void) WriteBlobLSBLong(image,(unsigned int) 40);
1209 (void) WriteBlobLSBLong(image,(unsigned int) icon_info.width);
1210 (void) WriteBlobLSBLong(image,(unsigned int) icon_info.height*2);
cristy3ed852e2009-09-05 21:47:34 +00001211 (void) WriteBlobLSBShort(image,icon_info.planes);
1212 (void) WriteBlobLSBShort(image,icon_info.bits_per_pixel);
cristy35ef8242010-06-03 16:24:13 +00001213 (void) WriteBlobLSBLong(image,(unsigned int) icon_info.compression);
1214 (void) WriteBlobLSBLong(image,(unsigned int) icon_info.image_size);
1215 (void) WriteBlobLSBLong(image,(unsigned int) icon_info.x_pixels);
1216 (void) WriteBlobLSBLong(image,(unsigned int) icon_info.y_pixels);
1217 (void) WriteBlobLSBLong(image,(unsigned int) icon_info.number_colors);
1218 (void) WriteBlobLSBLong(image,(unsigned int) icon_info.colors_important);
cristy3ed852e2009-09-05 21:47:34 +00001219 if (next->storage_class == PseudoClass)
1220 {
1221 unsigned char
1222 *icon_colormap;
1223
1224 /*
1225 Dump colormap to file.
1226 */
1227 icon_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
1228 (1UL << icon_info.bits_per_pixel),4UL*sizeof(*icon_colormap));
dirkc1e52002014-01-09 20:22:42 +00001229 if (icon_colormap == (unsigned char *) NULL)
1230 {
1231 images=DestroyImageList(images);
1232 ThrowWriterException(ResourceLimitError,
1233 "MemoryAllocationFailed");
dirk9e137702014-01-09 19:29:59 +00001234 }
cristy3ed852e2009-09-05 21:47:34 +00001235 q=icon_colormap;
cristybb503372010-05-27 20:51:26 +00001236 for (i=0; i < (ssize_t) next->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001237 {
1238 *q++=ScaleQuantumToChar(next->colormap[i].blue);
1239 *q++=ScaleQuantumToChar(next->colormap[i].green);
1240 *q++=ScaleQuantumToChar(next->colormap[i].red);
1241 *q++=(unsigned char) 0x0;
1242 }
cristybb503372010-05-27 20:51:26 +00001243 for ( ; i < (ssize_t) (1UL << icon_info.bits_per_pixel); i++)
cristy3ed852e2009-09-05 21:47:34 +00001244 {
1245 *q++=(unsigned char) 0x00;
1246 *q++=(unsigned char) 0x00;
1247 *q++=(unsigned char) 0x00;
1248 *q++=(unsigned char) 0x00;
1249 }
1250 (void) WriteBlob(image,(size_t) (4UL*(1UL <<
1251 icon_info.bits_per_pixel)),icon_colormap);
1252 icon_colormap=(unsigned char *) RelinquishMagickMemory(
1253 icon_colormap);
1254 }
1255 (void) WriteBlob(image,(size_t) icon_info.image_size,pixels);
1256 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1257 /*
1258 Write matte mask.
1259 */
1260 scanline_pad=(((next->columns+31) & ~31)-next->columns) >> 3;
cristybb503372010-05-27 20:51:26 +00001261 for (y=((ssize_t) next->rows - 1); y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +00001262 {
cristy1e178e72011-08-28 19:44:34 +00001263 p=GetVirtualPixels(next,0,y,next->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001264 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001265 break;
1266 bit=0;
1267 byte=0;
cristybb503372010-05-27 20:51:26 +00001268 for (x=0; x < (ssize_t) next->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001269 {
1270 byte<<=1;
cristy8a46d822012-08-28 23:32:39 +00001271 if ((next->alpha_trait == BlendPixelTrait) &&
cristy4c08aed2011-07-01 19:47:50 +00001272 (GetPixelAlpha(next,p) == (Quantum) TransparentAlpha))
cristy3ed852e2009-09-05 21:47:34 +00001273 byte|=0x01;
1274 bit++;
1275 if (bit == 8)
1276 {
1277 (void) WriteBlobByte(image,(unsigned char) byte);
1278 bit=0;
1279 byte=0;
1280 }
cristyed231572011-07-14 02:18:59 +00001281 p+=GetPixelChannels(next);
cristy3ed852e2009-09-05 21:47:34 +00001282 }
1283 if (bit != 0)
1284 (void) WriteBlobByte(image,(unsigned char) (byte << (8-bit)));
cristybb503372010-05-27 20:51:26 +00001285 for (i=0; i < (ssize_t) scanline_pad; i++)
cristy3ed852e2009-09-05 21:47:34 +00001286 (void) WriteBlobByte(image,(unsigned char) 0);
1287 }
1288 }
1289 if (GetNextImageInList(next) == (Image *) NULL)
1290 break;
1291 next=SyncNextImageInList(next);
1292 status=SetImageProgress(next,SaveImagesTag,scene++,
1293 GetImageListLength(next));
1294 if (status == MagickFalse)
1295 break;
1296 } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
1297 offset=SeekBlob(image,0,SEEK_SET);
cristyda16f162011-02-19 23:52:17 +00001298 (void) offset;
cristy3ed852e2009-09-05 21:47:34 +00001299 (void) WriteBlobLSBShort(image,0);
1300 (void) WriteBlobLSBShort(image,1);
1301 (void) WriteBlobLSBShort(image,(unsigned short) (scene+1));
1302 scene=0;
dirk9e137702014-01-09 19:29:59 +00001303 next=(images != (Image *) NULL) ? images : image;
cristy3ed852e2009-09-05 21:47:34 +00001304 do
1305 {
1306 (void) WriteBlobByte(image,icon_file.directory[scene].width);
1307 (void) WriteBlobByte(image,icon_file.directory[scene].height);
1308 (void) WriteBlobByte(image,icon_file.directory[scene].colors);
1309 (void) WriteBlobByte(image,icon_file.directory[scene].reserved);
1310 (void) WriteBlobLSBShort(image,icon_file.directory[scene].planes);
1311 (void) WriteBlobLSBShort(image,icon_file.directory[scene].bits_per_pixel);
cristy0b29b252010-05-30 01:59:46 +00001312 (void) WriteBlobLSBLong(image,(unsigned int)
1313 icon_file.directory[scene].size);
1314 (void) WriteBlobLSBLong(image,(unsigned int)
1315 icon_file.directory[scene].offset);
cristy3ed852e2009-09-05 21:47:34 +00001316 scene++;
1317 next=SyncNextImageInList(next);
1318 } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
1319 (void) CloseBlob(image);
dirk9e137702014-01-09 19:29:59 +00001320 images=DestroyImageList(images);
cristy3ed852e2009-09-05 21:47:34 +00001321 return(MagickTrue);
1322}