blob: 67883b166f962862c917951a957dc650aa60de39 [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% %
Cristyf6ff9ea2016-12-05 09:53:35 -050020% Copyright 1999-2017 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% %
Cristyf19d4142017-04-24 11:34:30 -040026% https://www.imagemagick.org/script/license.php %
cristy3ed852e2009-09-05 21:47:34 +000027% %
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*/
dirk501a11e2014-01-19 16:22:18 +0000145static Image
146 *AutoResizeImage(const Image *,const char *,MagickOffsetType *,
147 ExceptionInfo *);
148
cristy3ed852e2009-09-05 21:47:34 +0000149static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000150 WriteICONImage(const ImageInfo *,Image *,ExceptionInfo *);
dirk501a11e2014-01-19 16:22:18 +0000151
152Image *AutoResizeImage(const Image *image,const char *option,
153 MagickOffsetType *count,ExceptionInfo *exception)
154{
155 #define MAX_SIZES 16
156
157 char
158 *q;
159
160 const char
161 *p;
162
163 Image
164 *resized,
165 *images;
166
167 register ssize_t
168 i;
169
170 size_t
171 sizes[MAX_SIZES]={256,192,128,96,64,48,40,32,24,16};
172
173 images=NULL;
174 *count=0;
175 i=0;
176 p=option;
177 while (*p != '\0' && i < MAX_SIZES)
178 {
179 size_t
180 size;
181
182 while ((isspace((int) ((unsigned char) *p)) != 0))
183 p++;
184
185 size=(size_t)strtol(p,&q,10);
cristy0f6fc2d2015-05-30 00:49:11 +0000186 if ((p == q) || (size < 16) || (size > 256))
187 return((Image *) NULL);
dirk501a11e2014-01-19 16:22:18 +0000188
189 p=q;
190 sizes[i++]=size;
191
192 while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == ','))
193 p++;
194 }
195
196 if (i==0)
197 i=10;
198 *count=i;
199 for (i=0; i < *count; i++)
200 {
201 resized=ResizeImage(image,sizes[i],sizes[i],image->filter,exception);
202 if (resized == (Image *) NULL)
203 return(DestroyImageList(images));
204
205 if (images == (Image *) NULL)
206 images=resized;
207 else
208 AppendImageToList(&images,resized);
209 }
210 return(images);
211}
cristy3ed852e2009-09-05 21:47:34 +0000212
213/*
214%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
215% %
216% %
217% %
218% R e a d I C O N I m a g e %
219% %
220% %
221% %
222%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
223%
224% ReadICONImage() reads a Microsoft icon image file and returns it. It
225% allocates the memory necessary for the new Image structure and returns a
226% pointer to the new image.
227%
228% The format of the ReadICONImage method is:
229%
230% Image *ReadICONImage(const ImageInfo *image_info,
231% ExceptionInfo *exception)
232%
233% A description of each parameter follows:
234%
235% o image_info: the image info.
236%
237% o exception: return any errors or warnings in this structure.
238%
239*/
240static Image *ReadICONImage(const ImageInfo *image_info,
241 ExceptionInfo *exception)
242{
243 IconFile
244 icon_file;
245
246 IconInfo
247 icon_info;
248
249 Image
250 *image;
251
cristy3ed852e2009-09-05 21:47:34 +0000252 MagickBooleanType
253 status;
254
Cristyc2c1ece2017-04-23 10:39:00 -0400255 MagickSizeType
256 extent;
257
cristybb503372010-05-27 20:51:26 +0000258 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000259 i,
260 x;
261
cristy4c08aed2011-07-01 19:47:50 +0000262 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000263 *q;
264
265 register unsigned char
266 *p;
267
cristybb503372010-05-27 20:51:26 +0000268 size_t
cristy3ed852e2009-09-05 21:47:34 +0000269 bit,
cristyeaedf062010-05-29 22:36:02 +0000270 byte,
cristy3ed852e2009-09-05 21:47:34 +0000271 bytes_per_line,
cristyeaedf062010-05-29 22:36:02 +0000272 one,
cristy3ed852e2009-09-05 21:47:34 +0000273 scanline_pad;
274
cristyebc891a2011-04-24 23:04:16 +0000275 ssize_t
276 count,
277 offset,
278 y;
279
cristy3ed852e2009-09-05 21:47:34 +0000280 /*
281 Open image file.
282 */
283 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000284 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"%s",image_info->filename);
286 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000287 assert(exception->signature == MagickCoreSignature);
cristy1f1de182011-10-05 18:41:34 +0000288 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000289 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
290 if (status == MagickFalse)
291 {
292 image=DestroyImageList(image);
293 return((Image *) NULL);
294 }
295 icon_file.reserved=(short) ReadBlobLSBShort(image);
296 icon_file.resource_type=(short) ReadBlobLSBShort(image);
297 icon_file.count=(short) ReadBlobLSBShort(image);
298 if ((icon_file.reserved != 0) ||
299 ((icon_file.resource_type != 1) && (icon_file.resource_type != 2)) ||
300 (icon_file.count > MaxIcons))
301 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
Cristyc2c1ece2017-04-23 10:39:00 -0400302 extent=0;
cristy3ed852e2009-09-05 21:47:34 +0000303 for (i=0; i < icon_file.count; i++)
304 {
305 icon_file.directory[i].width=(unsigned char) ReadBlobByte(image);
306 icon_file.directory[i].height=(unsigned char) ReadBlobByte(image);
307 icon_file.directory[i].colors=(unsigned char) ReadBlobByte(image);
308 icon_file.directory[i].reserved=(unsigned char) ReadBlobByte(image);
309 icon_file.directory[i].planes=(unsigned short) ReadBlobLSBShort(image);
310 icon_file.directory[i].bits_per_pixel=(unsigned short)
311 ReadBlobLSBShort(image);
312 icon_file.directory[i].size=ReadBlobLSBLong(image);
313 icon_file.directory[i].offset=ReadBlobLSBLong(image);
cristy9c4177b2015-01-06 12:53:14 +0000314 if (EOFBlob(image) != MagickFalse)
Cristyc6c70bb2016-04-04 16:22:30 -0400315 break;
Cristyc2c1ece2017-04-23 10:39:00 -0400316 extent=MagickMax(extent,icon_file.directory[i].size);
cristy3ed852e2009-09-05 21:47:34 +0000317 }
Cristyc2c1ece2017-04-23 10:39:00 -0400318 if ((EOFBlob(image) != MagickFalse) || (extent > GetBlobSize(image)))
Cristyc6c70bb2016-04-04 16:22:30 -0400319 ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
cristyeaedf062010-05-29 22:36:02 +0000320 one=1;
cristy3ed852e2009-09-05 21:47:34 +0000321 for (i=0; i < icon_file.count; i++)
322 {
323 /*
324 Verify Icon identifier.
325 */
326 offset=(ssize_t) SeekBlob(image,(MagickOffsetType)
327 icon_file.directory[i].offset,SEEK_SET);
328 if (offset < 0)
329 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
330 icon_info.size=ReadBlobLSBLong(image);
Cristy1fe0b872016-04-23 10:48:31 -0400331 icon_info.width=(unsigned char) ReadBlobLSBSignedLong(image);
332 icon_info.height=(unsigned char) (ReadBlobLSBSignedLong(image)/2);
cristy4e09e032011-10-05 19:42:10 +0000333 icon_info.planes=ReadBlobLSBShort(image);
334 icon_info.bits_per_pixel=ReadBlobLSBShort(image);
cristy995156f2015-01-05 22:42:52 +0000335 if (EOFBlob(image) != MagickFalse)
336 {
337 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
338 image->filename);
339 break;
340 }
cristy43602772014-01-29 14:07:29 +0000341 if (((icon_info.planes == 18505) && (icon_info.bits_per_pixel == 21060)) ||
342 (icon_info.size == 0x474e5089))
cristy3ed852e2009-09-05 21:47:34 +0000343 {
344 Image
345 *icon_image;
346
347 ImageInfo
348 *read_info;
349
cristyf054ee72011-10-05 17:04:08 +0000350 size_t
351 length;
352
353 unsigned char
354 *png;
355
cristy3ed852e2009-09-05 21:47:34 +0000356 /*
357 Icon image encoded as a compressed PNG image.
358 */
cristyf054ee72011-10-05 17:04:08 +0000359 length=icon_file.directory[i].size;
dirk0ee195d2016-04-07 23:27:27 +0200360 if ((length < 16) || (~length < 16))
cristy0f6fc2d2015-05-30 00:49:11 +0000361 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristy4e09e032011-10-05 19:42:10 +0000362 png=(unsigned char *) AcquireQuantumMemory(length+16,sizeof(*png));
cristyf054ee72011-10-05 17:04:08 +0000363 if (png == (unsigned char *) NULL)
364 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
365 (void) CopyMagickMemory(png,"\211PNG\r\n\032\n\000\000\000\015",12);
cristy4e09e032011-10-05 19:42:10 +0000366 png[12]=(unsigned char) icon_info.planes;
367 png[13]=(unsigned char) (icon_info.planes >> 8);
368 png[14]=(unsigned char) icon_info.bits_per_pixel;
369 png[15]=(unsigned char) (icon_info.bits_per_pixel >> 8);
370 count=ReadBlob(image,length-16,png+16);
dirk833908d2014-01-28 19:26:32 +0000371 icon_image=(Image *) NULL;
372 if (count > 0)
cristyf054ee72011-10-05 17:04:08 +0000373 {
dirk833908d2014-01-28 19:26:32 +0000374 read_info=CloneImageInfo(image_info);
cristy151b66d2015-04-15 10:50:31 +0000375 (void) CopyMagickString(read_info->magick,"PNG",MagickPathExtent);
dirk833908d2014-01-28 19:26:32 +0000376 icon_image=BlobToImage(read_info,png,length+16,exception);
377 read_info=DestroyImageInfo(read_info);
cristyf054ee72011-10-05 17:04:08 +0000378 }
cristyf054ee72011-10-05 17:04:08 +0000379 png=(unsigned char *) RelinquishMagickMemory(png);
cristy47de5a92011-10-05 01:47:42 +0000380 if (icon_image == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000381 {
dirk833908d2014-01-28 19:26:32 +0000382 if (count != (ssize_t) (length-16))
383 ThrowReaderException(CorruptImageError,
384 "InsufficientImageDataInFile");
cristy47de5a92011-10-05 01:47:42 +0000385 image=DestroyImageList(image);
386 return((Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000387 }
cristy47de5a92011-10-05 01:47:42 +0000388 DestroyBlob(icon_image);
389 icon_image->blob=ReferenceBlob(image->blob);
390 ReplaceImageInList(&image,icon_image);
cristy3ed852e2009-09-05 21:47:34 +0000391 }
392 else
393 {
cristyf054ee72011-10-05 17:04:08 +0000394 if (icon_info.bits_per_pixel > 32)
395 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
396 icon_info.compression=ReadBlobLSBLong(image);
397 icon_info.image_size=ReadBlobLSBLong(image);
398 icon_info.x_pixels=ReadBlobLSBLong(image);
399 icon_info.y_pixels=ReadBlobLSBLong(image);
400 icon_info.number_colors=ReadBlobLSBLong(image);
401 icon_info.colors_important=ReadBlobLSBLong(image);
cristy8a46d822012-08-28 23:32:39 +0000402 image->alpha_trait=BlendPixelTrait;
cristyf054ee72011-10-05 17:04:08 +0000403 image->columns=(size_t) icon_file.directory[i].width;
404 if ((ssize_t) image->columns > icon_info.width)
405 image->columns=(size_t) icon_info.width;
cristy4e09e032011-10-05 19:42:10 +0000406 if (image->columns == 0)
407 image->columns=256;
cristyf054ee72011-10-05 17:04:08 +0000408 image->rows=(size_t) icon_file.directory[i].height;
409 if ((ssize_t) image->rows > icon_info.height)
410 image->rows=(size_t) icon_info.height;
cristy4e09e032011-10-05 19:42:10 +0000411 if (image->rows == 0)
412 image->rows=256;
cristyf054ee72011-10-05 17:04:08 +0000413 image->depth=icon_info.bits_per_pixel;
414 if (image->debug != MagickFalse)
415 {
416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
417 " scene = %.20g",(double) i);
418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
419 " size = %.20g",(double) icon_info.size);
420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
421 " width = %.20g",(double) icon_file.directory[i].width);
422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
423 " height = %.20g",(double) icon_file.directory[i].height);
424 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
425 " colors = %.20g",(double ) icon_info.number_colors);
426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
427 " planes = %.20g",(double) icon_info.planes);
428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
429 " bpp = %.20g",(double) icon_info.bits_per_pixel);
430 }
cristya2fb29a2014-09-21 22:04:52 +0000431 if ((icon_info.number_colors != 0) || (icon_info.bits_per_pixel <= 16U))
cristyf054ee72011-10-05 17:04:08 +0000432 {
433 image->storage_class=PseudoClass;
434 image->colors=icon_info.number_colors;
Dirk Lemstrab3299a32017-05-03 23:58:25 +0200435 if ((image->colors == 0) || (image->colors > 256))
cristyf054ee72011-10-05 17:04:08 +0000436 image->colors=one << icon_info.bits_per_pixel;
437 }
438 if (image->storage_class == PseudoClass)
439 {
440 register ssize_t
441 i;
cristy3ed852e2009-09-05 21:47:34 +0000442
cristyf054ee72011-10-05 17:04:08 +0000443 unsigned char
444 *icon_colormap;
445
446 /*
447 Read Icon raster colormap.
448 */
dirk833908d2014-01-28 19:26:32 +0000449 if (AcquireImageColormap(image,image->colors,exception) ==
450 MagickFalse)
cristyf054ee72011-10-05 17:04:08 +0000451 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
452 icon_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
453 image->colors,4UL*sizeof(*icon_colormap));
454 if (icon_colormap == (unsigned char *) NULL)
455 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
456 count=ReadBlob(image,(size_t) (4*image->colors),icon_colormap);
457 if (count != (ssize_t) (4*image->colors))
Dirk Lemstrafbb14282017-05-02 08:34:04 +0200458 {
459 icon_colormap=(unsigned char *) RelinquishMagickMemory(
460 icon_colormap);
461 ThrowReaderException(CorruptImageError,
462 "InsufficientImageDataInFile");
463 }
cristyf054ee72011-10-05 17:04:08 +0000464 p=icon_colormap;
465 for (i=0; i < (ssize_t) image->colors; i++)
466 {
467 image->colormap[i].blue=(Quantum) ScaleCharToQuantum(*p++);
468 image->colormap[i].green=(Quantum) ScaleCharToQuantum(*p++);
469 image->colormap[i].red=(Quantum) ScaleCharToQuantum(*p++);
470 p++;
471 }
472 icon_colormap=(unsigned char *) RelinquishMagickMemory(icon_colormap);
473 }
cristy3ed852e2009-09-05 21:47:34 +0000474 /*
475 Convert Icon raster image to pixel packets.
476 */
cristyf054ee72011-10-05 17:04:08 +0000477 if ((image_info->ping != MagickFalse) &&
478 (image_info->number_scenes != 0))
479 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
480 break;
cristyacabb842014-12-14 23:36:33 +0000481 status=SetImageExtent(image,image->columns,image->rows,exception);
482 if (status == MagickFalse)
483 return(DestroyImageList(image));
cristy3ed852e2009-09-05 21:47:34 +0000484 bytes_per_line=(((image->columns*icon_info.bits_per_pixel)+31) &
485 ~31) >> 3;
cristyda16f162011-02-19 23:52:17 +0000486 (void) bytes_per_line;
cristy3ed852e2009-09-05 21:47:34 +0000487 scanline_pad=((((image->columns*icon_info.bits_per_pixel)+31) & ~31)-
488 (image->columns*icon_info.bits_per_pixel)) >> 3;
489 switch (icon_info.bits_per_pixel)
490 {
491 case 1:
492 {
493 /*
494 Convert bitmap scanline.
495 */
cristybb503372010-05-27 20:51:26 +0000496 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000497 {
498 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000499 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000500 break;
cristybb503372010-05-27 20:51:26 +0000501 for (x=0; x < (ssize_t) (image->columns-7); x+=8)
cristy3ed852e2009-09-05 21:47:34 +0000502 {
cristyf054ee72011-10-05 17:04:08 +0000503 byte=(size_t) ReadBlobByte(image);
cristy3ed852e2009-09-05 21:47:34 +0000504 for (bit=0; bit < 8; bit++)
cristy4c08aed2011-07-01 19:47:50 +0000505 {
506 SetPixelIndex(image,((byte & (0x80 >> bit)) != 0 ? 0x01 :
507 0x00),q);
cristyed231572011-07-14 02:18:59 +0000508 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000509 }
cristy3ed852e2009-09-05 21:47:34 +0000510 }
511 if ((image->columns % 8) != 0)
512 {
cristyf054ee72011-10-05 17:04:08 +0000513 byte=(size_t) ReadBlobByte(image);
cristy3ed852e2009-09-05 21:47:34 +0000514 for (bit=0; bit < (image->columns % 8); bit++)
cristy4c08aed2011-07-01 19:47:50 +0000515 {
516 SetPixelIndex(image,((byte & (0x80 >> bit)) != 0 ? 0x01 :
517 0x00),q);
cristyed231572011-07-14 02:18:59 +0000518 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000519 }
cristy3ed852e2009-09-05 21:47:34 +0000520 }
cristybb503372010-05-27 20:51:26 +0000521 for (x=0; x < (ssize_t) scanline_pad; x++)
cristyf054ee72011-10-05 17:04:08 +0000522 (void) ReadBlobByte(image);
cristy3ed852e2009-09-05 21:47:34 +0000523 if (SyncAuthenticPixels(image,exception) == MagickFalse)
524 break;
525 if (image->previous == (Image *) NULL)
526 {
527 status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
528 image->rows);
529 if (status == MagickFalse)
530 break;
531 }
532 }
533 break;
534 }
535 case 4:
536 {
537 /*
538 Read 4-bit Icon scanline.
539 */
cristybb503372010-05-27 20:51:26 +0000540 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000541 {
542 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000543 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000544 break;
cristybb503372010-05-27 20:51:26 +0000545 for (x=0; x < ((ssize_t) image->columns-1); x+=2)
cristy3ed852e2009-09-05 21:47:34 +0000546 {
cristyf054ee72011-10-05 17:04:08 +0000547 byte=(size_t) ReadBlobByte(image);
cristy4c08aed2011-07-01 19:47:50 +0000548 SetPixelIndex(image,((byte >> 4) & 0xf),q);
cristyed231572011-07-14 02:18:59 +0000549 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000550 SetPixelIndex(image,((byte) & 0xf),q);
cristyed231572011-07-14 02:18:59 +0000551 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000552 }
553 if ((image->columns % 2) != 0)
554 {
cristyf054ee72011-10-05 17:04:08 +0000555 byte=(size_t) ReadBlobByte(image);
cristy4c08aed2011-07-01 19:47:50 +0000556 SetPixelIndex(image,((byte >> 4) & 0xf),q);
cristyed231572011-07-14 02:18:59 +0000557 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000558 }
cristybb503372010-05-27 20:51:26 +0000559 for (x=0; x < (ssize_t) scanline_pad; x++)
cristyf054ee72011-10-05 17:04:08 +0000560 (void) ReadBlobByte(image);
cristy3ed852e2009-09-05 21:47:34 +0000561 if (SyncAuthenticPixels(image,exception) == MagickFalse)
562 break;
563 if (image->previous == (Image *) NULL)
564 {
565 status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
566 image->rows);
567 if (status == MagickFalse)
568 break;
569 }
570 }
571 break;
572 }
573 case 8:
574 {
575 /*
576 Convert PseudoColor scanline.
577 */
cristybb503372010-05-27 20:51:26 +0000578 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000579 {
580 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000581 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000582 break;
cristybb503372010-05-27 20:51:26 +0000583 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000584 {
cristyf054ee72011-10-05 17:04:08 +0000585 byte=(size_t) ReadBlobByte(image);
dirk1678aec2015-08-30 12:07:29 +0200586 SetPixelIndex(image,(Quantum) byte,q);
cristyed231572011-07-14 02:18:59 +0000587 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000588 }
cristybb503372010-05-27 20:51:26 +0000589 for (x=0; x < (ssize_t) scanline_pad; x++)
cristyf054ee72011-10-05 17:04:08 +0000590 (void) ReadBlobByte(image);
cristy3ed852e2009-09-05 21:47:34 +0000591 if (SyncAuthenticPixels(image,exception) == MagickFalse)
592 break;
593 if (image->previous == (Image *) NULL)
594 {
595 status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
596 image->rows);
597 if (status == MagickFalse)
598 break;
599 }
600 }
601 break;
602 }
603 case 16:
604 {
605 /*
606 Convert PseudoColor scanline.
607 */
cristybb503372010-05-27 20:51:26 +0000608 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000609 {
610 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000611 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000612 break;
cristybb503372010-05-27 20:51:26 +0000613 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000614 {
cristyf054ee72011-10-05 17:04:08 +0000615 byte=(size_t) ReadBlobByte(image);
616 byte|=(size_t) (ReadBlobByte(image) << 8);
dirk1678aec2015-08-30 12:07:29 +0200617 SetPixelIndex(image,(Quantum) byte,q);
cristyed231572011-07-14 02:18:59 +0000618 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000619 }
cristybb503372010-05-27 20:51:26 +0000620 for (x=0; x < (ssize_t) scanline_pad; x++)
cristyf054ee72011-10-05 17:04:08 +0000621 (void) ReadBlobByte(image);
cristy3ed852e2009-09-05 21:47:34 +0000622 if (SyncAuthenticPixels(image,exception) == MagickFalse)
623 break;
624 if (image->previous == (Image *) NULL)
625 {
626 status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
627 image->rows);
628 if (status == MagickFalse)
629 break;
630 }
631 }
632 break;
633 }
634 case 24:
635 case 32:
636 {
637 /*
638 Convert DirectColor scanline.
639 */
cristybb503372010-05-27 20:51:26 +0000640 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000641 {
642 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000643 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000644 break;
cristybb503372010-05-27 20:51:26 +0000645 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000646 {
cristyf054ee72011-10-05 17:04:08 +0000647 SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
648 ReadBlobByte(image)),q);
649 SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
650 ReadBlobByte(image)),q);
651 SetPixelRed(image,ScaleCharToQuantum((unsigned char)
652 ReadBlobByte(image)),q);
cristy3ed852e2009-09-05 21:47:34 +0000653 if (icon_info.bits_per_pixel == 32)
cristyf054ee72011-10-05 17:04:08 +0000654 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char)
655 ReadBlobByte(image)),q);
cristyed231572011-07-14 02:18:59 +0000656 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000657 }
658 if (icon_info.bits_per_pixel == 24)
cristybb503372010-05-27 20:51:26 +0000659 for (x=0; x < (ssize_t) scanline_pad; x++)
cristyf054ee72011-10-05 17:04:08 +0000660 (void) ReadBlobByte(image);
cristy3ed852e2009-09-05 21:47:34 +0000661 if (SyncAuthenticPixels(image,exception) == MagickFalse)
662 break;
663 if (image->previous == (Image *) NULL)
664 {
665 status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
666 image->rows);
667 if (status == MagickFalse)
668 break;
669 }
670 }
671 break;
672 }
673 default:
674 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
675 }
Cristye0fe9892016-04-05 09:55:07 -0400676 if ((image_info->ping == MagickFalse) &&
677 (icon_info.bits_per_pixel <= 16))
cristyea1a8aa2011-10-20 13:24:06 +0000678 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000679 if (icon_info.bits_per_pixel != 32)
680 {
681 /*
682 Read the ICON alpha mask.
683 */
684 image->storage_class=DirectClass;
cristybb503372010-05-27 20:51:26 +0000685 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000686 {
687 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000688 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000689 break;
cristybb503372010-05-27 20:51:26 +0000690 for (x=0; x < ((ssize_t) image->columns-7); x+=8)
cristy3ed852e2009-09-05 21:47:34 +0000691 {
cristyf054ee72011-10-05 17:04:08 +0000692 byte=(size_t) ReadBlobByte(image);
cristy3ed852e2009-09-05 21:47:34 +0000693 for (bit=0; bit < 8; bit++)
cristy4c08aed2011-07-01 19:47:50 +0000694 {
695 SetPixelAlpha(image,(((byte & (0x80 >> bit)) != 0) ?
696 TransparentAlpha : OpaqueAlpha),q);
cristyed231572011-07-14 02:18:59 +0000697 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000698 }
cristy3ed852e2009-09-05 21:47:34 +0000699 }
700 if ((image->columns % 8) != 0)
701 {
cristyf054ee72011-10-05 17:04:08 +0000702 byte=(size_t) ReadBlobByte(image);
cristy3ed852e2009-09-05 21:47:34 +0000703 for (bit=0; bit < (image->columns % 8); bit++)
cristy4c08aed2011-07-01 19:47:50 +0000704 {
705 SetPixelAlpha(image,(((byte & (0x80 >> bit)) != 0) ?
706 TransparentAlpha : OpaqueAlpha),q);
cristyed231572011-07-14 02:18:59 +0000707 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000708 }
cristy3ed852e2009-09-05 21:47:34 +0000709 }
cristy0e8200f2009-09-13 21:10:06 +0000710 if ((image->columns % 32) != 0)
cristybb503372010-05-27 20:51:26 +0000711 for (x=0; x < (ssize_t) ((32-(image->columns % 32))/8); x++)
cristyf054ee72011-10-05 17:04:08 +0000712 (void) ReadBlobByte(image);
cristy3ed852e2009-09-05 21:47:34 +0000713 if (SyncAuthenticPixels(image,exception) == MagickFalse)
714 break;
715 }
716 }
717 if (EOFBlob(image) != MagickFalse)
718 {
719 ThrowFileException(exception,CorruptImageError,
720 "UnexpectedEndOfFile",image->filename);
721 break;
722 }
723 }
724 /*
725 Proceed to next image.
726 */
727 if (image_info->number_scenes != 0)
728 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
729 break;
cristybb503372010-05-27 20:51:26 +0000730 if (i < (ssize_t) (icon_file.count-1))
cristy3ed852e2009-09-05 21:47:34 +0000731 {
732 /*
733 Allocate next image structure.
734 */
cristy1f1de182011-10-05 18:41:34 +0000735 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000736 if (GetNextImageInList(image) == (Image *) NULL)
737 {
738 image=DestroyImageList(image);
739 return((Image *) NULL);
740 }
741 image=SyncNextImageInList(image);
742 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
743 GetBlobSize(image));
744 if (status == MagickFalse)
745 break;
746 }
747 }
748 (void) CloseBlob(image);
749 return(GetFirstImageInList(image));
750}
751
752/*
753%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
754% %
755% %
756% %
757% R e g i s t e r I C O N I m a g e %
758% %
759% %
760% %
761%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
762%
763% RegisterICONImage() adds attributes for the Icon image format to
764% the list of supported formats. The attributes include the image format
765% tag, a method to read and/or write the format, whether the format
766% supports the saving of more than one frame to the same file or blob,
767% whether the format supports native in-memory I/O, and a brief
768% description of the format.
769%
770% The format of the RegisterICONImage method is:
771%
cristybb503372010-05-27 20:51:26 +0000772% size_t RegisterICONImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000773%
774*/
cristybb503372010-05-27 20:51:26 +0000775ModuleExport size_t RegisterICONImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000776{
777 MagickInfo
778 *entry;
779
dirk06b627a2015-04-06 18:59:17 +0000780 entry=AcquireMagickInfo("ICON","CUR","Microsoft icon");
cristy3ed852e2009-09-05 21:47:34 +0000781 entry->decoder=(DecodeImageHandler *) ReadICONImage;
782 entry->encoder=(EncodeImageHandler *) WriteICONImage;
dirk08e9a112015-02-22 01:51:41 +0000783 entry->flags^=CoderAdjoinFlag;
Dirk Lemstra8974d772017-01-22 03:19:27 +0100784 entry->flags|=CoderDecoderSeekableStreamFlag;
785 entry->flags|=CoderEncoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +0000786 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +0000787 entry=AcquireMagickInfo("ICON","ICO","Microsoft icon");
cristy3ed852e2009-09-05 21:47:34 +0000788 entry->decoder=(DecodeImageHandler *) ReadICONImage;
789 entry->encoder=(EncodeImageHandler *) WriteICONImage;
Dirk Lemstra8974d772017-01-22 03:19:27 +0100790 entry->flags|=CoderDecoderSeekableStreamFlag;
791 entry->flags|=CoderEncoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +0000792 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +0000793 entry=AcquireMagickInfo("ICON","ICON","Microsoft icon");
cristy3ed852e2009-09-05 21:47:34 +0000794 entry->decoder=(DecodeImageHandler *) ReadICONImage;
795 entry->encoder=(EncodeImageHandler *) WriteICONImage;
dirk08e9a112015-02-22 01:51:41 +0000796 entry->flags^=CoderAdjoinFlag;
Dirk Lemstra8974d772017-01-22 03:19:27 +0100797 entry->flags|=CoderDecoderSeekableStreamFlag;
798 entry->flags|=CoderEncoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +0000799 (void) RegisterMagickInfo(entry);
800 return(MagickImageCoderSignature);
801}
802
803/*
804%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
805% %
806% %
807% %
808% U n r e g i s t e r I C O N I m a g e %
809% %
810% %
811% %
812%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
813%
814% UnregisterICONImage() removes format registrations made by the
815% ICON module from the list of supported formats.
816%
817% The format of the UnregisterICONImage method is:
818%
819% UnregisterICONImage(void)
820%
821*/
822ModuleExport void UnregisterICONImage(void)
823{
824 (void) UnregisterMagickInfo("CUR");
825 (void) UnregisterMagickInfo("ICO");
826 (void) UnregisterMagickInfo("ICON");
827}
828
829/*
830%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
831% %
832% %
833% %
834% W r i t e I C O N I m a g e %
835% %
836% %
837% %
838%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
839%
840% WriteICONImage() writes an image in Microsoft Windows bitmap encoded
841% image format, version 3 for Windows or (if the image has a matte channel)
842% version 4.
843%
cristy83cb91e2012-10-31 00:58:22 +0000844% It encodes any subimage as a compressed PNG image ("BI_PNG)", only when its
845% dimensions are 256x256 and image->compression is undefined or is defined as
846% ZipCompression.
glennrp4b708b82012-07-17 19:15:58 +0000847%
cristy3ed852e2009-09-05 21:47:34 +0000848% The format of the WriteICONImage method is:
849%
850% MagickBooleanType WriteICONImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +0000851% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000852%
853% A description of each parameter follows.
854%
855% o image_info: the image info.
856%
857% o image: The image.
858%
cristy1e178e72011-08-28 19:44:34 +0000859% o exception: return any errors or warnings in this structure.
860%
cristy3ed852e2009-09-05 21:47:34 +0000861*/
862static MagickBooleanType WriteICONImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +0000863 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000864{
dirk501a11e2014-01-19 16:22:18 +0000865 const char
866 *option;
867
cristy3ed852e2009-09-05 21:47:34 +0000868 IconFile
869 icon_file;
870
871 IconInfo
872 icon_info;
873
874 Image
dirk9e137702014-01-09 19:29:59 +0000875 *images,
cristy3ed852e2009-09-05 21:47:34 +0000876 *next;
877
cristy3ed852e2009-09-05 21:47:34 +0000878 MagickBooleanType
879 status;
880
881 MagickOffsetType
882 offset,
883 scene;
884
cristy4c08aed2011-07-01 19:47:50 +0000885 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000886 *p;
887
cristybb503372010-05-27 20:51:26 +0000888 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000889 i,
890 x;
891
892 register unsigned char
893 *q;
894
cristyebc891a2011-04-24 23:04:16 +0000895 size_t
896 bytes_per_line,
897 scanline_pad;
898
899 ssize_t
900 y;
901
cristy3ed852e2009-09-05 21:47:34 +0000902 unsigned char
903 bit,
904 byte,
905 *pixels;
906
cristy3ed852e2009-09-05 21:47:34 +0000907 /*
908 Open output image file.
909 */
910 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000911 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000912 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000913 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000914 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +0000915 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000916 assert(exception->signature == MagickCoreSignature);
cristy1e178e72011-08-28 19:44:34 +0000917 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +0000918 if (status == MagickFalse)
919 return(status);
dirk9e137702014-01-09 19:29:59 +0000920 images=(Image *) NULL;
dirk501a11e2014-01-19 16:22:18 +0000921 option=GetImageOption(image_info,"icon:auto-resize");
922 if (option != (const char *) NULL)
dirk9e137702014-01-09 19:29:59 +0000923 {
dirk501a11e2014-01-19 16:22:18 +0000924 images=AutoResizeImage(image,option,&scene,exception);
dirk9e137702014-01-09 19:29:59 +0000925 if (images == (Image *) NULL)
dirk501a11e2014-01-19 16:22:18 +0000926 ThrowWriterException(ImageError,"InvalidDimensions");
dirk9e137702014-01-09 19:29:59 +0000927 }
928 else
929 {
930 scene=0;
931 next=image;
932 do
933 {
934 if ((image->columns > 256L) || (image->rows > 256L))
935 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
936 scene++;
937 next=SyncNextImageInList(next);
938 } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
939 }
cristy3ed852e2009-09-05 21:47:34 +0000940 /*
941 Dump out a ICON header template to be properly initialized later.
942 */
943 (void) WriteBlobLSBShort(image,0);
944 (void) WriteBlobLSBShort(image,1);
945 (void) WriteBlobLSBShort(image,(unsigned char) scene);
946 (void) ResetMagickMemory(&icon_file,0,sizeof(icon_file));
947 (void) ResetMagickMemory(&icon_info,0,sizeof(icon_info));
948 scene=0;
dirk9e137702014-01-09 19:29:59 +0000949 next=(images != (Image *) NULL) ? images : image;
cristy3ed852e2009-09-05 21:47:34 +0000950 do
951 {
952 (void) WriteBlobByte(image,icon_file.directory[scene].width);
953 (void) WriteBlobByte(image,icon_file.directory[scene].height);
954 (void) WriteBlobByte(image,icon_file.directory[scene].colors);
955 (void) WriteBlobByte(image,icon_file.directory[scene].reserved);
956 (void) WriteBlobLSBShort(image,icon_file.directory[scene].planes);
957 (void) WriteBlobLSBShort(image,icon_file.directory[scene].bits_per_pixel);
cristy0b29b252010-05-30 01:59:46 +0000958 (void) WriteBlobLSBLong(image,(unsigned int)
959 icon_file.directory[scene].size);
960 (void) WriteBlobLSBLong(image,(unsigned int)
961 icon_file.directory[scene].offset);
cristy3ed852e2009-09-05 21:47:34 +0000962 scene++;
963 next=SyncNextImageInList(next);
964 } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
965 scene=0;
dirk9e137702014-01-09 19:29:59 +0000966 next=(images != (Image *) NULL) ? images : image;
cristy3ed852e2009-09-05 21:47:34 +0000967 do
968 {
glennrp4b708b82012-07-17 19:15:58 +0000969 if ((next->columns > 255L) && (next->rows > 255L) &&
970 ((next->compression == UndefinedCompression) ||
971 (next->compression == ZipCompression)))
cristy3ed852e2009-09-05 21:47:34 +0000972 {
973 Image
974 *write_image;
975
976 ImageInfo
977 *write_info;
978
979 size_t
980 length;
981
982 unsigned char
983 *png;
984
cristy1e178e72011-08-28 19:44:34 +0000985 write_image=CloneImage(next,0,0,MagickTrue,exception);
dirkc1e52002014-01-09 20:22:42 +0000986 if (write_image == (Image *) NULL)
987 {
988 images=DestroyImageList(images);
989 return(MagickFalse);
dirk9e137702014-01-09 19:29:59 +0000990 }
cristy3ed852e2009-09-05 21:47:34 +0000991 write_info=CloneImageInfo(image_info);
cristy151b66d2015-04-15 10:50:31 +0000992 (void) CopyMagickString(write_info->filename,"PNG:",MagickPathExtent);
glennrp4b708b82012-07-17 19:15:58 +0000993
994 /* Don't write any ancillary chunks except for gAMA */
995 (void) SetImageArtifact(write_image,"png:include-chunk","none,gama");
996
997 /* Only write PNG32 formatted PNG (32-bit RGBA), 8 bits per channel */
998 (void) SetImageArtifact(write_image,"png:format","png32");
999
cristy3ed852e2009-09-05 21:47:34 +00001000 png=(unsigned char *) ImageToBlob(write_info,write_image,&length,
cristy1e178e72011-08-28 19:44:34 +00001001 exception);
Cristy15bc0d62015-12-17 12:36:54 -05001002 write_image=DestroyImageList(write_image);
cristy3ed852e2009-09-05 21:47:34 +00001003 write_info=DestroyImageInfo(write_info);
dirkc1e52002014-01-09 20:22:42 +00001004 if (png == (unsigned char *) NULL)
1005 {
1006 images=DestroyImageList(images);
1007 return(MagickFalse);
dirk9e137702014-01-09 19:29:59 +00001008 }
cristy3ed852e2009-09-05 21:47:34 +00001009 icon_file.directory[scene].width=0;
1010 icon_file.directory[scene].height=0;
1011 icon_file.directory[scene].colors=0;
1012 icon_file.directory[scene].reserved=0;
1013 icon_file.directory[scene].planes=1;
1014 icon_file.directory[scene].bits_per_pixel=32;
cristybb503372010-05-27 20:51:26 +00001015 icon_file.directory[scene].size=(size_t) length;
1016 icon_file.directory[scene].offset=(size_t) TellBlob(image);
cristy3ed852e2009-09-05 21:47:34 +00001017 (void) WriteBlob(image,(size_t) length,png);
1018 png=(unsigned char *) RelinquishMagickMemory(png);
1019 }
1020 else
1021 {
1022 /*
1023 Initialize ICON raster file header.
1024 */
cristyaf8d3912014-02-21 14:50:33 +00001025 (void) TransformImageColorspace(next,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001026 icon_info.file_size=14+12+28;
1027 icon_info.offset_bits=icon_info.file_size;
1028 icon_info.compression=BI_RGB;
1029 if ((next->storage_class != DirectClass) && (next->colors > 256))
cristy1e178e72011-08-28 19:44:34 +00001030 (void) SetImageStorageClass(next,DirectClass,exception);
cristy3ed852e2009-09-05 21:47:34 +00001031 if (next->storage_class == DirectClass)
1032 {
1033 /*
1034 Full color ICON raster.
1035 */
1036 icon_info.number_colors=0;
1037 icon_info.bits_per_pixel=32;
cristybb503372010-05-27 20:51:26 +00001038 icon_info.compression=(size_t) BI_RGB;
cristy3ed852e2009-09-05 21:47:34 +00001039 }
1040 else
1041 {
cristy35ef8242010-06-03 16:24:13 +00001042 size_t
1043 one;
1044
cristy3ed852e2009-09-05 21:47:34 +00001045 /*
1046 Colormapped ICON raster.
1047 */
1048 icon_info.bits_per_pixel=8;
1049 if (next->colors <= 256)
1050 icon_info.bits_per_pixel=8;
1051 if (next->colors <= 16)
1052 icon_info.bits_per_pixel=4;
1053 if (next->colors <= 2)
1054 icon_info.bits_per_pixel=1;
cristy35ef8242010-06-03 16:24:13 +00001055 one=1;
1056 icon_info.number_colors=one << icon_info.bits_per_pixel;
cristy3ed852e2009-09-05 21:47:34 +00001057 if (icon_info.number_colors < next->colors)
1058 {
cristy1e178e72011-08-28 19:44:34 +00001059 (void) SetImageStorageClass(next,DirectClass,exception);
cristy3ed852e2009-09-05 21:47:34 +00001060 icon_info.number_colors=0;
1061 icon_info.bits_per_pixel=(unsigned short) 24;
cristybb503372010-05-27 20:51:26 +00001062 icon_info.compression=(size_t) BI_RGB;
cristy3ed852e2009-09-05 21:47:34 +00001063 }
1064 else
1065 {
cristy0b29b252010-05-30 01:59:46 +00001066 size_t
1067 one;
1068
1069 one=1;
1070 icon_info.file_size+=3*(one << icon_info.bits_per_pixel);
1071 icon_info.offset_bits+=3*(one << icon_info.bits_per_pixel);
1072 icon_info.file_size+=(one << icon_info.bits_per_pixel);
1073 icon_info.offset_bits+=(one << icon_info.bits_per_pixel);
cristy3ed852e2009-09-05 21:47:34 +00001074 }
1075 }
1076 bytes_per_line=(((next->columns*icon_info.bits_per_pixel)+31) &
1077 ~31) >> 3;
1078 icon_info.ba_offset=0;
cristybb503372010-05-27 20:51:26 +00001079 icon_info.width=(ssize_t) next->columns;
1080 icon_info.height=(ssize_t) next->rows;
cristy3ed852e2009-09-05 21:47:34 +00001081 icon_info.planes=1;
1082 icon_info.image_size=bytes_per_line*next->rows;
1083 icon_info.size=40;
1084 icon_info.size+=(4*icon_info.number_colors);
1085 icon_info.size+=icon_info.image_size;
1086 icon_info.size+=(((icon_info.width+31) & ~31) >> 3)*icon_info.height;
1087 icon_info.file_size+=icon_info.image_size;
1088 icon_info.x_pixels=0;
1089 icon_info.y_pixels=0;
1090 switch (next->units)
1091 {
1092 case UndefinedResolution:
1093 case PixelsPerInchResolution:
1094 {
cristy2a11bef2011-10-28 18:33:11 +00001095 icon_info.x_pixels=(size_t) (100.0*next->resolution.x/2.54);
1096 icon_info.y_pixels=(size_t) (100.0*next->resolution.y/2.54);
cristy3ed852e2009-09-05 21:47:34 +00001097 break;
1098 }
1099 case PixelsPerCentimeterResolution:
1100 {
cristy2a11bef2011-10-28 18:33:11 +00001101 icon_info.x_pixels=(size_t) (100.0*next->resolution.x);
1102 icon_info.y_pixels=(size_t) (100.0*next->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001103 break;
1104 }
1105 }
1106 icon_info.colors_important=icon_info.number_colors;
1107 /*
1108 Convert MIFF to ICON raster pixels.
1109 */
1110 pixels=(unsigned char *) AcquireQuantumMemory((size_t)
1111 icon_info.image_size,sizeof(*pixels));
dirkc1e52002014-01-09 20:22:42 +00001112 if (pixels == (unsigned char *) NULL)
1113 {
dirk9e137702014-01-09 19:29:59 +00001114 images=DestroyImageList(images);
1115 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
dirkc1e52002014-01-09 20:22:42 +00001116 }
cristy3ed852e2009-09-05 21:47:34 +00001117 (void) ResetMagickMemory(pixels,0,(size_t) icon_info.image_size);
1118 switch (icon_info.bits_per_pixel)
1119 {
1120 case 1:
1121 {
cristybb503372010-05-27 20:51:26 +00001122 size_t
cristy3ed852e2009-09-05 21:47:34 +00001123 bit,
1124 byte;
1125
1126 /*
1127 Convert PseudoClass image to a ICON monochrome image.
1128 */
cristybb503372010-05-27 20:51:26 +00001129 for (y=0; y < (ssize_t) next->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001130 {
cristy1e178e72011-08-28 19:44:34 +00001131 p=GetVirtualPixels(next,0,y,next->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001132 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001133 break;
cristy3ed852e2009-09-05 21:47:34 +00001134 q=pixels+(next->rows-y-1)*bytes_per_line;
1135 bit=0;
1136 byte=0;
cristybb503372010-05-27 20:51:26 +00001137 for (x=0; x < (ssize_t) next->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001138 {
1139 byte<<=1;
cristy4c08aed2011-07-01 19:47:50 +00001140 byte|=GetPixelIndex(next,p) != 0 ? 0x01 : 0x00;
cristy3ed852e2009-09-05 21:47:34 +00001141 bit++;
1142 if (bit == 8)
1143 {
1144 *q++=(unsigned char) byte;
1145 bit=0;
1146 byte=0;
1147 }
cristyed231572011-07-14 02:18:59 +00001148 p+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001149 }
cristy3ed852e2009-09-05 21:47:34 +00001150 if (bit != 0)
1151 *q++=(unsigned char) (byte << (8-bit));
1152 if (next->previous == (Image *) NULL)
1153 {
1154 status=SetImageProgress(next,SaveImageTag,y,next->rows);
1155 if (status == MagickFalse)
1156 break;
1157 }
1158 }
1159 break;
1160 }
1161 case 4:
1162 {
cristybb503372010-05-27 20:51:26 +00001163 size_t
cristy3ed852e2009-09-05 21:47:34 +00001164 nibble,
1165 byte;
1166
1167 /*
1168 Convert PseudoClass image to a ICON monochrome image.
1169 */
cristybb503372010-05-27 20:51:26 +00001170 for (y=0; y < (ssize_t) next->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001171 {
cristy1e178e72011-08-28 19:44:34 +00001172 p=GetVirtualPixels(next,0,y,next->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001173 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001174 break;
cristy3ed852e2009-09-05 21:47:34 +00001175 q=pixels+(next->rows-y-1)*bytes_per_line;
1176 nibble=0;
1177 byte=0;
cristybb503372010-05-27 20:51:26 +00001178 for (x=0; x < (ssize_t) next->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001179 {
1180 byte<<=4;
cristy4c08aed2011-07-01 19:47:50 +00001181 byte|=((size_t) GetPixelIndex(next,p) & 0x0f);
cristy3ed852e2009-09-05 21:47:34 +00001182 nibble++;
1183 if (nibble == 2)
1184 {
1185 *q++=(unsigned char) byte;
1186 nibble=0;
1187 byte=0;
1188 }
cristyed231572011-07-14 02:18:59 +00001189 p+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001190 }
cristy3ed852e2009-09-05 21:47:34 +00001191 if (nibble != 0)
1192 *q++=(unsigned char) (byte << 4);
1193 if (next->previous == (Image *) NULL)
1194 {
1195 status=SetImageProgress(next,SaveImageTag,y,next->rows);
1196 if (status == MagickFalse)
1197 break;
1198 }
1199 }
1200 break;
1201 }
1202 case 8:
1203 {
1204 /*
1205 Convert PseudoClass packet to ICON pixel.
1206 */
cristybb503372010-05-27 20:51:26 +00001207 for (y=0; y < (ssize_t) next->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001208 {
cristy1e178e72011-08-28 19:44:34 +00001209 p=GetVirtualPixels(next,0,y,next->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001210 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001211 break;
cristy3ed852e2009-09-05 21:47:34 +00001212 q=pixels+(next->rows-y-1)*bytes_per_line;
cristybb503372010-05-27 20:51:26 +00001213 for (x=0; x < (ssize_t) next->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00001214 {
1215 *q++=(unsigned char) GetPixelIndex(next,p);
cristyed231572011-07-14 02:18:59 +00001216 p+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001217 }
cristy3ed852e2009-09-05 21:47:34 +00001218 if (next->previous == (Image *) NULL)
1219 {
1220 status=SetImageProgress(next,SaveImageTag,y,next->rows);
1221 if (status == MagickFalse)
1222 break;
1223 }
1224 }
1225 break;
1226 }
1227 case 24:
1228 case 32:
1229 {
1230 /*
1231 Convert DirectClass packet to ICON BGR888 or BGRA8888 pixel.
1232 */
cristybb503372010-05-27 20:51:26 +00001233 for (y=0; y < (ssize_t) next->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001234 {
cristy1e178e72011-08-28 19:44:34 +00001235 p=GetVirtualPixels(next,0,y,next->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001236 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001237 break;
1238 q=pixels+(next->rows-y-1)*bytes_per_line;
cristybb503372010-05-27 20:51:26 +00001239 for (x=0; x < (ssize_t) next->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001240 {
cristy4c08aed2011-07-01 19:47:50 +00001241 *q++=ScaleQuantumToChar(GetPixelBlue(next,p));
1242 *q++=ScaleQuantumToChar(GetPixelGreen(next,p));
1243 *q++=ScaleQuantumToChar(GetPixelRed(next,p));
cristy17f11b02014-12-20 19:37:04 +00001244 if (next->alpha_trait == UndefinedPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +00001245 *q++=ScaleQuantumToChar(QuantumRange);
1246 else
cristy4c08aed2011-07-01 19:47:50 +00001247 *q++=ScaleQuantumToChar(GetPixelAlpha(next,p));
cristyed231572011-07-14 02:18:59 +00001248 p+=GetPixelChannels(next);
cristy3ed852e2009-09-05 21:47:34 +00001249 }
1250 if (icon_info.bits_per_pixel == 24)
cristybb503372010-05-27 20:51:26 +00001251 for (x=3L*(ssize_t) next->columns; x < (ssize_t) bytes_per_line; x++)
cristy3ed852e2009-09-05 21:47:34 +00001252 *q++=0x00;
1253 if (next->previous == (Image *) NULL)
1254 {
1255 status=SetImageProgress(next,SaveImageTag,y,next->rows);
1256 if (status == MagickFalse)
1257 break;
1258 }
1259 }
1260 break;
1261 }
1262 }
1263 /*
1264 Write 40-byte version 3+ bitmap header.
1265 */
1266 icon_file.directory[scene].width=(unsigned char) icon_info.width;
1267 icon_file.directory[scene].height=(unsigned char) icon_info.height;
1268 icon_file.directory[scene].colors=(unsigned char)
1269 icon_info.number_colors;
1270 icon_file.directory[scene].reserved=0;
1271 icon_file.directory[scene].planes=icon_info.planes;
1272 icon_file.directory[scene].bits_per_pixel=icon_info.bits_per_pixel;
1273 icon_file.directory[scene].size=icon_info.size;
cristybb503372010-05-27 20:51:26 +00001274 icon_file.directory[scene].offset=(size_t) TellBlob(image);
cristy35ef8242010-06-03 16:24:13 +00001275 (void) WriteBlobLSBLong(image,(unsigned int) 40);
1276 (void) WriteBlobLSBLong(image,(unsigned int) icon_info.width);
1277 (void) WriteBlobLSBLong(image,(unsigned int) icon_info.height*2);
cristy3ed852e2009-09-05 21:47:34 +00001278 (void) WriteBlobLSBShort(image,icon_info.planes);
1279 (void) WriteBlobLSBShort(image,icon_info.bits_per_pixel);
cristy35ef8242010-06-03 16:24:13 +00001280 (void) WriteBlobLSBLong(image,(unsigned int) icon_info.compression);
1281 (void) WriteBlobLSBLong(image,(unsigned int) icon_info.image_size);
1282 (void) WriteBlobLSBLong(image,(unsigned int) icon_info.x_pixels);
1283 (void) WriteBlobLSBLong(image,(unsigned int) icon_info.y_pixels);
1284 (void) WriteBlobLSBLong(image,(unsigned int) icon_info.number_colors);
1285 (void) WriteBlobLSBLong(image,(unsigned int) icon_info.colors_important);
cristy3ed852e2009-09-05 21:47:34 +00001286 if (next->storage_class == PseudoClass)
1287 {
1288 unsigned char
1289 *icon_colormap;
1290
1291 /*
1292 Dump colormap to file.
1293 */
1294 icon_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
1295 (1UL << icon_info.bits_per_pixel),4UL*sizeof(*icon_colormap));
dirkc1e52002014-01-09 20:22:42 +00001296 if (icon_colormap == (unsigned char *) NULL)
1297 {
1298 images=DestroyImageList(images);
1299 ThrowWriterException(ResourceLimitError,
1300 "MemoryAllocationFailed");
dirk9e137702014-01-09 19:29:59 +00001301 }
cristy3ed852e2009-09-05 21:47:34 +00001302 q=icon_colormap;
cristybb503372010-05-27 20:51:26 +00001303 for (i=0; i < (ssize_t) next->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001304 {
1305 *q++=ScaleQuantumToChar(next->colormap[i].blue);
1306 *q++=ScaleQuantumToChar(next->colormap[i].green);
1307 *q++=ScaleQuantumToChar(next->colormap[i].red);
1308 *q++=(unsigned char) 0x0;
1309 }
cristybb503372010-05-27 20:51:26 +00001310 for ( ; i < (ssize_t) (1UL << icon_info.bits_per_pixel); i++)
cristy3ed852e2009-09-05 21:47:34 +00001311 {
1312 *q++=(unsigned char) 0x00;
1313 *q++=(unsigned char) 0x00;
1314 *q++=(unsigned char) 0x00;
1315 *q++=(unsigned char) 0x00;
1316 }
1317 (void) WriteBlob(image,(size_t) (4UL*(1UL <<
1318 icon_info.bits_per_pixel)),icon_colormap);
1319 icon_colormap=(unsigned char *) RelinquishMagickMemory(
1320 icon_colormap);
1321 }
1322 (void) WriteBlob(image,(size_t) icon_info.image_size,pixels);
1323 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1324 /*
1325 Write matte mask.
1326 */
1327 scanline_pad=(((next->columns+31) & ~31)-next->columns) >> 3;
cristybb503372010-05-27 20:51:26 +00001328 for (y=((ssize_t) next->rows - 1); y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +00001329 {
cristy1e178e72011-08-28 19:44:34 +00001330 p=GetVirtualPixels(next,0,y,next->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001331 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001332 break;
1333 bit=0;
1334 byte=0;
cristybb503372010-05-27 20:51:26 +00001335 for (x=0; x < (ssize_t) next->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001336 {
1337 byte<<=1;
cristy17f11b02014-12-20 19:37:04 +00001338 if ((next->alpha_trait != UndefinedPixelTrait) &&
cristy4c08aed2011-07-01 19:47:50 +00001339 (GetPixelAlpha(next,p) == (Quantum) TransparentAlpha))
cristy3ed852e2009-09-05 21:47:34 +00001340 byte|=0x01;
1341 bit++;
1342 if (bit == 8)
1343 {
1344 (void) WriteBlobByte(image,(unsigned char) byte);
1345 bit=0;
1346 byte=0;
1347 }
cristyed231572011-07-14 02:18:59 +00001348 p+=GetPixelChannels(next);
cristy3ed852e2009-09-05 21:47:34 +00001349 }
1350 if (bit != 0)
1351 (void) WriteBlobByte(image,(unsigned char) (byte << (8-bit)));
cristybb503372010-05-27 20:51:26 +00001352 for (i=0; i < (ssize_t) scanline_pad; i++)
cristy3ed852e2009-09-05 21:47:34 +00001353 (void) WriteBlobByte(image,(unsigned char) 0);
1354 }
1355 }
1356 if (GetNextImageInList(next) == (Image *) NULL)
1357 break;
cristy3ed852e2009-09-05 21:47:34 +00001358 status=SetImageProgress(next,SaveImagesTag,scene++,
1359 GetImageListLength(next));
1360 if (status == MagickFalse)
1361 break;
cristy5467fcf2014-05-18 17:20:55 +00001362 next=SyncNextImageInList(next);
cristy3ed852e2009-09-05 21:47:34 +00001363 } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
1364 offset=SeekBlob(image,0,SEEK_SET);
cristyda16f162011-02-19 23:52:17 +00001365 (void) offset;
cristy3ed852e2009-09-05 21:47:34 +00001366 (void) WriteBlobLSBShort(image,0);
1367 (void) WriteBlobLSBShort(image,1);
1368 (void) WriteBlobLSBShort(image,(unsigned short) (scene+1));
1369 scene=0;
dirk9e137702014-01-09 19:29:59 +00001370 next=(images != (Image *) NULL) ? images : image;
cristy3ed852e2009-09-05 21:47:34 +00001371 do
1372 {
1373 (void) WriteBlobByte(image,icon_file.directory[scene].width);
1374 (void) WriteBlobByte(image,icon_file.directory[scene].height);
1375 (void) WriteBlobByte(image,icon_file.directory[scene].colors);
1376 (void) WriteBlobByte(image,icon_file.directory[scene].reserved);
1377 (void) WriteBlobLSBShort(image,icon_file.directory[scene].planes);
1378 (void) WriteBlobLSBShort(image,icon_file.directory[scene].bits_per_pixel);
cristy0b29b252010-05-30 01:59:46 +00001379 (void) WriteBlobLSBLong(image,(unsigned int)
1380 icon_file.directory[scene].size);
1381 (void) WriteBlobLSBLong(image,(unsigned int)
1382 icon_file.directory[scene].offset);
cristy3ed852e2009-09-05 21:47:34 +00001383 scene++;
1384 next=SyncNextImageInList(next);
1385 } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
1386 (void) CloseBlob(image);
dirk9e137702014-01-09 19:29:59 +00001387 images=DestroyImageList(images);
cristy3ed852e2009-09-05 21:47:34 +00001388 return(MagickTrue);
1389}