1 /*
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                                %
16 %                                   Cristy                                    %
17 %                                 July 1992                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization      %
21 %  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 %    https://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 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #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"
49 #include "MagickCore/colorspace-private.h"
50 #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"
60 #include "MagickCore/nt-base-private.h"
61 #include "MagickCore/option.h"
62 #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"
68 
69 /*
70   Define declarations.
71 */
72 #if !defined(MAGICKCORE_WINDOWS_SUPPORT) || defined(__MINGW32__)
73 #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 */
82 typedef 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 
94   size_t
95     size,
96     offset;
97 } IconEntry;
98 
99 typedef struct _IconFile
100 {
101   short
102     reserved,
103     resource_type,
104     count;
105 
106   IconEntry
107     directory[MaxIcons];
108 } IconFile;
109 
110 typedef struct _IconInfo
111 {
112   size_t
113     file_size,
114     ba_offset,
115     offset_bits,
116     size;
117 
118   ssize_t
119     width,
120     height;
121 
122   unsigned short
123     planes,
124     bits_per_pixel;
125 
126   size_t
127     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 
138   ssize_t
139     colorspace;
140 } IconInfo;
141 
142 /*
143   Forward declaractions.
144 */
145 static MagickBooleanType
146   WriteICONImage(const ImageInfo *,Image *,ExceptionInfo *);
147 
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 */
ReadICONImage(const ImageInfo * image_info,ExceptionInfo * exception)175 static 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 
187   MagickBooleanType
188     status;
189 
190   MagickSizeType
191     extent;
192 
193   ssize_t
194     i,
195     x;
196 
197   Quantum
198     *q;
199 
200   unsigned char
201     *p;
202 
203   size_t
204     bit,
205     byte,
206     bytes_per_line,
207     one,
208     scanline_pad;
209 
210   ssize_t
211     count,
212     offset,
213     y;
214 
215   /*
216     Open image file.
217   */
218   assert(image_info != (const ImageInfo *) NULL);
219   assert(image_info->signature == MagickCoreSignature);
220   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"%s",image_info->filename);
221   assert(exception != (ExceptionInfo *) NULL);
222   assert(exception->signature == MagickCoreSignature);
223   image=AcquireImage(image_info,exception);
224   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
225   if (status == MagickFalse)
226     {
227       image=DestroyImageList(image);
228       return((Image *) NULL);
229     }
230   icon_file.reserved=(short) ReadBlobLSBShort(image);
231   icon_file.resource_type=(short) ReadBlobLSBShort(image);
232   icon_file.count=(short) ReadBlobLSBShort(image);
233   if ((icon_file.reserved != 0) ||
234       ((icon_file.resource_type != 1) && (icon_file.resource_type != 2)) ||
235       (icon_file.count > MaxIcons))
236     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
237   extent=0;
238   for (i=0; i < icon_file.count; i++)
239   {
240     icon_file.directory[i].width=(unsigned char) ReadBlobByte(image);
241     icon_file.directory[i].height=(unsigned char) ReadBlobByte(image);
242     icon_file.directory[i].colors=(unsigned char) ReadBlobByte(image);
243     icon_file.directory[i].reserved=(unsigned char) ReadBlobByte(image);
244     icon_file.directory[i].planes=(unsigned short) ReadBlobLSBShort(image);
245     icon_file.directory[i].bits_per_pixel=(unsigned short)
246       ReadBlobLSBShort(image);
247     icon_file.directory[i].size=ReadBlobLSBLong(image);
248     icon_file.directory[i].offset=ReadBlobLSBLong(image);
249     if (EOFBlob(image) != MagickFalse)
250       break;
251     extent=MagickMax(extent,icon_file.directory[i].size);
252   }
253   if ((EOFBlob(image) != MagickFalse) || (extent > GetBlobSize(image)))
254     ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
255   one=1;
256   for (i=0; i < icon_file.count; i++)
257   {
258     /*
259       Verify Icon identifier.
260     */
261     offset=(ssize_t) SeekBlob(image,(MagickOffsetType)
262       icon_file.directory[i].offset,SEEK_SET);
263     if (offset < 0)
264       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
265     icon_info.size=ReadBlobLSBLong(image);
266     icon_info.width=(unsigned char) ReadBlobLSBSignedLong(image);
267     icon_info.height=(unsigned char) (ReadBlobLSBSignedLong(image)/2);
268     icon_info.planes=ReadBlobLSBShort(image);
269     icon_info.bits_per_pixel=ReadBlobLSBShort(image);
270     if (EOFBlob(image) != MagickFalse)
271       {
272         ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
273           image->filename);
274         break;
275       }
276     if (((icon_info.planes == 18505) && (icon_info.bits_per_pixel == 21060)) ||
277         (icon_info.size == 0x474e5089))
278       {
279         Image
280           *icon_image;
281 
282         ImageInfo
283           *read_info;
284 
285         size_t
286           length;
287 
288         unsigned char
289           *png;
290 
291         /*
292           Icon image encoded as a compressed PNG image.
293         */
294         length=icon_file.directory[i].size;
295         if ((length < 16) || (~length < 16))
296           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
297         png=(unsigned char *) AcquireQuantumMemory(length,sizeof(*png));
298         if (png == (unsigned char *) NULL)
299           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
300         (void) memcpy(png,"\211PNG\r\n\032\n\000\000\000\015",12);
301         png[12]=(unsigned char) icon_info.planes;
302         png[13]=(unsigned char) (icon_info.planes >> 8);
303         png[14]=(unsigned char) icon_info.bits_per_pixel;
304         png[15]=(unsigned char) (icon_info.bits_per_pixel >> 8);
305         count=ReadBlob(image,length-16,png+16);
306         if (count != (ssize_t) (length-16))
307           {
308             png=(unsigned char *) RelinquishMagickMemory(png);
309             ThrowReaderException(CorruptImageError,
310                 "InsufficientImageDataInFile");
311           }
312         read_info=CloneImageInfo(image_info);
313         (void) CopyMagickString(read_info->magick,"PNG",MagickPathExtent);
314         icon_image=BlobToImage(read_info,png,length,exception);
315         read_info=DestroyImageInfo(read_info);
316         png=(unsigned char *) RelinquishMagickMemory(png);
317         if (icon_image == (Image *) NULL)
318           return(DestroyImageList(image));
319         DestroyBlob(icon_image);
320         icon_image->blob=ReferenceBlob(image->blob);
321         ReplaceImageInList(&image,icon_image);
322       }
323     else
324       {
325         if (icon_info.bits_per_pixel > 32)
326           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
327         icon_info.compression=ReadBlobLSBLong(image);
328         icon_info.image_size=ReadBlobLSBLong(image);
329         icon_info.x_pixels=ReadBlobLSBLong(image);
330         icon_info.y_pixels=ReadBlobLSBLong(image);
331         icon_info.number_colors=ReadBlobLSBLong(image);
332         if (icon_info.number_colors > GetBlobSize(image))
333           ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
334         icon_info.colors_important=ReadBlobLSBLong(image);
335         image->alpha_trait=BlendPixelTrait;
336         image->columns=(size_t) icon_file.directory[i].width;
337         if ((ssize_t) image->columns > icon_info.width)
338           image->columns=(size_t) icon_info.width;
339         if (image->columns == 0)
340           image->columns=256;
341         image->rows=(size_t) icon_file.directory[i].height;
342         if ((ssize_t) image->rows > icon_info.height)
343           image->rows=(size_t) icon_info.height;
344         if (image->rows == 0)
345           image->rows=256;
346         image->depth=icon_info.bits_per_pixel;
347         if (image->depth > 16)
348           image->depth=8;
349         if (image->debug != MagickFalse)
350           {
351             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
352               " scene    = %.20g",(double) i);
353             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
354               "   size   = %.20g",(double) icon_info.size);
355             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
356               "   width  = %.20g",(double) icon_file.directory[i].width);
357             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
358               "   height = %.20g",(double) icon_file.directory[i].height);
359             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
360               "   colors = %.20g",(double ) icon_info.number_colors);
361             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
362               "   planes = %.20g",(double) icon_info.planes);
363             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
364               "   bpp    = %.20g",(double) icon_info.bits_per_pixel);
365           }
366       if ((icon_info.number_colors != 0) || (icon_info.bits_per_pixel <= 16U))
367         {
368           image->storage_class=PseudoClass;
369           image->colors=icon_info.number_colors;
370           if ((image->colors == 0) || (image->colors > 256))
371             image->colors=one << icon_info.bits_per_pixel;
372         }
373       if (image->storage_class == PseudoClass)
374         {
375           ssize_t
376             j;
377 
378           unsigned char
379             *icon_colormap;
380 
381           /*
382             Read Icon raster colormap.
383           */
384           if (image->colors > GetBlobSize(image))
385             ThrowReaderException(CorruptImageError,
386               "InsufficientImageDataInFile");
387           if (AcquireImageColormap(image,image->colors,exception) ==
388               MagickFalse)
389             ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
390           icon_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
391             image->colors,4UL*sizeof(*icon_colormap));
392           if (icon_colormap == (unsigned char *) NULL)
393             ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
394           count=ReadBlob(image,(size_t) (4*image->colors),icon_colormap);
395           if (count != (ssize_t) (4*image->colors))
396             {
397               icon_colormap=(unsigned char *) RelinquishMagickMemory(
398                 icon_colormap);
399               ThrowReaderException(CorruptImageError,
400                 "InsufficientImageDataInFile");
401             }
402           p=icon_colormap;
403           for (j=0; j < (ssize_t) image->colors; j++)
404           {
405             image->colormap[j].blue=(Quantum) ScaleCharToQuantum(*p++);
406             image->colormap[j].green=(Quantum) ScaleCharToQuantum(*p++);
407             image->colormap[j].red=(Quantum) ScaleCharToQuantum(*p++);
408             p++;
409           }
410           icon_colormap=(unsigned char *) RelinquishMagickMemory(icon_colormap);
411         }
412         /*
413           Convert Icon raster image to pixel packets.
414         */
415         if ((image_info->ping != MagickFalse) &&
416             (image_info->number_scenes != 0))
417           if (image->scene >= (image_info->scene+image_info->number_scenes-1))
418             break;
419         status=SetImageExtent(image,image->columns,image->rows,exception);
420         if (status == MagickFalse)
421           return(DestroyImageList(image));
422         bytes_per_line=(((image->columns*icon_info.bits_per_pixel)+31) &
423           ~31) >> 3;
424         (void) bytes_per_line;
425         scanline_pad=((((image->columns*icon_info.bits_per_pixel)+31) & ~31)-
426           (image->columns*icon_info.bits_per_pixel)) >> 3;
427         switch (icon_info.bits_per_pixel)
428         {
429           case 1:
430           {
431             /*
432               Convert bitmap scanline.
433             */
434             for (y=(ssize_t) image->rows-1; y >= 0; y--)
435             {
436               q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
437               if (q == (Quantum *) NULL)
438                 break;
439               for (x=0; x < (ssize_t) (image->columns-7); x+=8)
440               {
441                 byte=(size_t) ReadBlobByte(image);
442                 for (bit=0; bit < 8; bit++)
443                 {
444                   SetPixelIndex(image,((byte & (0x80 >> bit)) != 0 ? 0x01 :
445                     0x00),q);
446                   q+=GetPixelChannels(image);
447                 }
448               }
449               if ((image->columns % 8) != 0)
450                 {
451                   byte=(size_t) ReadBlobByte(image);
452                   for (bit=0; bit < (image->columns % 8); bit++)
453                   {
454                     SetPixelIndex(image,((byte & (0x80 >> bit)) != 0 ? 0x01 :
455                       0x00),q);
456                     q+=GetPixelChannels(image);
457                   }
458                 }
459               for (x=0; x < (ssize_t) scanline_pad; x++)
460                 (void) ReadBlobByte(image);
461               if (SyncAuthenticPixels(image,exception) == MagickFalse)
462                 break;
463               if (image->previous == (Image *) NULL)
464                 {
465                   status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
466                     image->rows);
467                   if (status == MagickFalse)
468                     break;
469                 }
470             }
471             break;
472           }
473           case 4:
474           {
475             /*
476               Read 4-bit Icon scanline.
477             */
478             for (y=(ssize_t) image->rows-1; y >= 0; y--)
479             {
480               q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
481               if (q == (Quantum *) NULL)
482                 break;
483               for (x=0; x < ((ssize_t) image->columns-1); x+=2)
484               {
485                 byte=(size_t) ReadBlobByte(image);
486                 SetPixelIndex(image,((byte >> 4) & 0xf),q);
487                 q+=GetPixelChannels(image);
488                 SetPixelIndex(image,((byte) & 0xf),q);
489                 q+=GetPixelChannels(image);
490               }
491               if ((image->columns % 2) != 0)
492                 {
493                   byte=(size_t) ReadBlobByte(image);
494                   SetPixelIndex(image,((byte >> 4) & 0xf),q);
495                   q+=GetPixelChannels(image);
496                 }
497               for (x=0; x < (ssize_t) scanline_pad; x++)
498                 (void) ReadBlobByte(image);
499               if (SyncAuthenticPixels(image,exception) == MagickFalse)
500                 break;
501               if (image->previous == (Image *) NULL)
502                 {
503                   status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
504                     image->rows);
505                   if (status == MagickFalse)
506                     break;
507                 }
508             }
509             break;
510           }
511           case 8:
512           {
513             /*
514               Convert PseudoColor scanline.
515             */
516             for (y=(ssize_t) image->rows-1; y >= 0; y--)
517             {
518               q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
519               if (q == (Quantum *) NULL)
520                 break;
521               for (x=0; x < (ssize_t) image->columns; x++)
522               {
523                 byte=(size_t) ReadBlobByte(image);
524                 SetPixelIndex(image,(Quantum) byte,q);
525                 q+=GetPixelChannels(image);
526               }
527               for (x=0; x < (ssize_t) scanline_pad; x++)
528                 (void) ReadBlobByte(image);
529               if (SyncAuthenticPixels(image,exception) == MagickFalse)
530                 break;
531               if (image->previous == (Image *) NULL)
532                 {
533                   status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
534                     image->rows);
535                   if (status == MagickFalse)
536                     break;
537                 }
538             }
539             break;
540           }
541           case 16:
542           {
543             /*
544               Convert PseudoColor scanline.
545             */
546             for (y=(ssize_t) image->rows-1; y >= 0; y--)
547             {
548               q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
549               if (q == (Quantum *) NULL)
550                 break;
551               for (x=0; x < (ssize_t) image->columns; x++)
552               {
553                 byte=(size_t) ReadBlobByte(image);
554                 byte|=((size_t) ReadBlobByte(image) << 8);
555                 SetPixelIndex(image,(Quantum) byte,q);
556                 q+=GetPixelChannels(image);
557               }
558               for (x=0; x < (ssize_t) scanline_pad; x++)
559                 (void) ReadBlobByte(image);
560               if (SyncAuthenticPixels(image,exception) == MagickFalse)
561                 break;
562               if (image->previous == (Image *) NULL)
563                 {
564                   status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
565                     image->rows);
566                   if (status == MagickFalse)
567                     break;
568                 }
569             }
570             break;
571           }
572           case 24:
573           case 32:
574           {
575             /*
576               Convert DirectColor scanline.
577             */
578             for (y=(ssize_t) image->rows-1; y >= 0; y--)
579             {
580               q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
581               if (q == (Quantum *) NULL)
582                 break;
583               for (x=0; x < (ssize_t) image->columns; x++)
584               {
585                 SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
586                   ReadBlobByte(image)),q);
587                 SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
588                   ReadBlobByte(image)),q);
589                 SetPixelRed(image,ScaleCharToQuantum((unsigned char)
590                   ReadBlobByte(image)),q);
591                 if (icon_info.bits_per_pixel == 32)
592                   SetPixelAlpha(image,ScaleCharToQuantum((unsigned char)
593                     ReadBlobByte(image)),q);
594                 q+=GetPixelChannels(image);
595               }
596               if (icon_info.bits_per_pixel == 24)
597                 for (x=0; x < (ssize_t) scanline_pad; x++)
598                   (void) ReadBlobByte(image);
599               if (SyncAuthenticPixels(image,exception) == MagickFalse)
600                 break;
601               if (image->previous == (Image *) NULL)
602                 {
603                   status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
604                     image->rows);
605                   if (status == MagickFalse)
606                     break;
607                 }
608             }
609             break;
610           }
611           default:
612             ThrowReaderException(CorruptImageError,"ImproperImageHeader");
613         }
614         if ((image_info->ping == MagickFalse) &&
615             (icon_info.bits_per_pixel <= 16))
616           (void) SyncImage(image,exception);
617         if (icon_info.bits_per_pixel != 32)
618           {
619             /*
620               Read the ICON alpha mask.
621             */
622             image->storage_class=DirectClass;
623             for (y=(ssize_t) image->rows-1; y >= 0; y--)
624             {
625               q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
626               if (q == (Quantum *) NULL)
627                 break;
628               for (x=0; x < ((ssize_t) image->columns-7); x+=8)
629               {
630                 byte=(size_t) ReadBlobByte(image);
631                 for (bit=0; bit < 8; bit++)
632                 {
633                   SetPixelAlpha(image,(((byte & (0x80 >> bit)) != 0) ?
634                     TransparentAlpha : OpaqueAlpha),q);
635                   q+=GetPixelChannels(image);
636                 }
637               }
638               if ((image->columns % 8) != 0)
639                 {
640                   byte=(size_t) ReadBlobByte(image);
641                   for (bit=0; bit < (image->columns % 8); bit++)
642                   {
643                     SetPixelAlpha(image,(((byte & (0x80 >> bit)) != 0) ?
644                       TransparentAlpha : OpaqueAlpha),q);
645                     q+=GetPixelChannels(image);
646                   }
647                 }
648               if ((image->columns % 32) != 0)
649                 for (x=0; x < (ssize_t) ((32-(image->columns % 32))/8); x++)
650                   (void) ReadBlobByte(image);
651               if (SyncAuthenticPixels(image,exception) == MagickFalse)
652                 break;
653             }
654           }
655         if (EOFBlob(image) != MagickFalse)
656           {
657             ThrowFileException(exception,CorruptImageError,
658               "UnexpectedEndOfFile",image->filename);
659             break;
660           }
661       }
662     /*
663       Proceed to next image.
664     */
665     if (image_info->number_scenes != 0)
666       if (image->scene >= (image_info->scene+image_info->number_scenes-1))
667         break;
668     if (i < (ssize_t) (icon_file.count-1))
669       {
670         /*
671           Allocate next image structure.
672         */
673         AcquireNextImage(image_info,image,exception);
674         if (GetNextImageInList(image) == (Image *) NULL)
675           {
676             status=MagickFalse;
677             break;
678           }
679         image=SyncNextImageInList(image);
680         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
681           GetBlobSize(image));
682         if (status == MagickFalse)
683           break;
684       }
685   }
686   (void) CloseBlob(image);
687   if (status == MagickFalse)
688     return(DestroyImageList(image));
689   return(GetFirstImageInList(image));
690 }
691 
692 /*
693 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
694 %                                                                             %
695 %                                                                             %
696 %                                                                             %
697 %   R e g i s t e r I C O N I m a g e                                         %
698 %                                                                             %
699 %                                                                             %
700 %                                                                             %
701 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
702 %
703 %  RegisterICONImage() adds attributes for the Icon image format to
704 %  the list of supported formats.  The attributes include the image format
705 %  tag, a method to read and/or write the format, whether the format
706 %  supports the saving of more than one frame to the same file or blob,
707 %  whether the format supports native in-memory I/O, and a brief
708 %  description of the format.
709 %
710 %  The format of the RegisterICONImage method is:
711 %
712 %      size_t RegisterICONImage(void)
713 %
714 */
RegisterICONImage(void)715 ModuleExport size_t RegisterICONImage(void)
716 {
717   MagickInfo
718     *entry;
719 
720   entry=AcquireMagickInfo("ICON","CUR","Microsoft icon");
721   entry->decoder=(DecodeImageHandler *) ReadICONImage;
722   entry->encoder=(EncodeImageHandler *) WriteICONImage;
723   entry->flags^=CoderAdjoinFlag;
724   entry->flags|=CoderDecoderSeekableStreamFlag;
725   entry->flags|=CoderEncoderSeekableStreamFlag;
726   (void) RegisterMagickInfo(entry);
727   entry=AcquireMagickInfo("ICON","ICO","Microsoft icon");
728   entry->decoder=(DecodeImageHandler *) ReadICONImage;
729   entry->encoder=(EncodeImageHandler *) WriteICONImage;
730   entry->flags|=CoderDecoderSeekableStreamFlag;
731   entry->flags|=CoderEncoderSeekableStreamFlag;
732   (void) RegisterMagickInfo(entry);
733   entry=AcquireMagickInfo("ICON","ICON","Microsoft icon");
734   entry->decoder=(DecodeImageHandler *) ReadICONImage;
735   entry->encoder=(EncodeImageHandler *) WriteICONImage;
736   entry->flags^=CoderAdjoinFlag;
737   entry->flags|=CoderDecoderSeekableStreamFlag;
738   entry->flags|=CoderEncoderSeekableStreamFlag;
739   (void) RegisterMagickInfo(entry);
740   return(MagickImageCoderSignature);
741 }
742 
743 /*
744 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
745 %                                                                             %
746 %                                                                             %
747 %                                                                             %
748 %   U n r e g i s t e r I C O N I m a g e                                     %
749 %                                                                             %
750 %                                                                             %
751 %                                                                             %
752 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
753 %
754 %  UnregisterICONImage() removes format registrations made by the
755 %  ICON module from the list of supported formats.
756 %
757 %  The format of the UnregisterICONImage method is:
758 %
759 %      UnregisterICONImage(void)
760 %
761 */
UnregisterICONImage(void)762 ModuleExport void UnregisterICONImage(void)
763 {
764   (void) UnregisterMagickInfo("CUR");
765   (void) UnregisterMagickInfo("ICO");
766   (void) UnregisterMagickInfo("ICON");
767 }
768 
769 /*
770 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
771 %                                                                             %
772 %                                                                             %
773 %                                                                             %
774 %   W r i t e I C O N I m a g e                                               %
775 %                                                                             %
776 %                                                                             %
777 %                                                                             %
778 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
779 %
780 %  WriteICONImage() writes an image in Microsoft Windows bitmap encoded
781 %  image format, version 3 for Windows or (if the image has a matte channel)
782 %  version 4.
783 %
784 %  It encodes any subimage as a compressed PNG image ("BI_PNG)", only when its
785 %  dimensions are 256x256 and image->compression is undefined or is defined as
786 %  ZipCompression.
787 %
788 %  The format of the WriteICONImage method is:
789 %
790 %      MagickBooleanType WriteICONImage(const ImageInfo *image_info,
791 %        Image *image,ExceptionInfo *exception)
792 %
793 %  A description of each parameter follows.
794 %
795 %    o image_info: the image info.
796 %
797 %    o image:  The image.
798 %
799 %    o exception: return any errors or warnings in this structure.
800 %
801 */
802 
AutoResizeImage(const Image * image,const char * option,MagickOffsetType * count,ExceptionInfo * exception)803 static Image *AutoResizeImage(const Image *image,const char *option,
804   MagickOffsetType *count,ExceptionInfo *exception)
805 {
806 #define MAX_SIZES 16
807 
808   char
809     *q;
810 
811   const char
812     *p;
813 
814   Image
815     *images,
816     *resized;
817 
818   ssize_t
819     i;
820 
821   size_t
822     sizes[MAX_SIZES] ={ 256, 192, 128, 96, 64, 48, 40, 32, 24, 16};
823 
824   images=NULL;
825   *count=0;
826   i=0;
827   p=option;
828   while ((*p != '\0') && (i < MAX_SIZES))
829   {
830     size_t
831       size;
832 
833     while ((isspace((int) ((unsigned char) *p)) != 0))
834       p++;
835     size=(size_t) strtol(p,&q,10);
836     if ((p == q) || (size < 16) || (size > 256))
837       return((Image *) NULL);
838     p=q;
839     sizes[i++]=size;
840     while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == ','))
841       p++;
842   }
843   if (i == 0)
844     i=10;
845   *count=i;
846   for (i=0; i < *count; i++)
847   {
848     resized=ResizeImage(image,sizes[i],sizes[i],image->filter,exception);
849     if (resized == (Image *) NULL)
850       return(DestroyImageList(images));
851     if (images == (Image *) NULL)
852       images=resized;
853     else
854       AppendImageToList(&images,resized);
855   }
856   return(images);
857 }
858 
WriteICONImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)859 static MagickBooleanType WriteICONImage(const ImageInfo *image_info,
860   Image *image,ExceptionInfo *exception)
861 {
862   const char
863     *option;
864 
865   IconFile
866     icon_file;
867 
868   IconInfo
869     icon_info;
870 
871   Image
872     *images,
873     *next;
874 
875   MagickBooleanType
876     status;
877 
878   MagickOffsetType
879     offset,
880     scene;
881 
882   const Quantum
883     *p;
884 
885   ssize_t
886     i,
887     x;
888 
889   unsigned char
890     *q;
891 
892   size_t
893     bytes_per_line,
894     imageListLength,
895     scanline_pad;
896 
897   ssize_t
898     y;
899 
900   unsigned char
901     *pixels;
902 
903   /*
904     Open output image file.
905   */
906   assert(image_info != (const ImageInfo *) NULL);
907   assert(image_info->signature == MagickCoreSignature);
908   assert(image != (Image *) NULL);
909   assert(image->signature == MagickCoreSignature);
910     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"%s",image->filename);
911   assert(exception != (ExceptionInfo *) NULL);
912   assert(exception->signature == MagickCoreSignature);
913   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
914   if (status == MagickFalse)
915     return(status);
916   images=(Image *) NULL;
917   option=GetImageOption(image_info,"icon:auto-resize");
918   if (option != (const char *) NULL)
919     {
920       images=AutoResizeImage(image,option,&scene,exception);
921       if (images == (Image *) NULL)
922         ThrowWriterException(ImageError,"InvalidDimensions");
923     }
924   else
925     {
926       scene=0;
927       next=image;
928       do
929       {
930         if ((image->columns > 256L) || (image->rows > 256L))
931           ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
932         scene++;
933         next=SyncNextImageInList(next);
934       } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
935     }
936   /*
937     Dump out a ICON header template to be properly initialized later.
938   */
939   (void) WriteBlobLSBShort(image,0);
940   (void) WriteBlobLSBShort(image,1);
941   (void) WriteBlobLSBShort(image,(unsigned char) scene);
942   (void) memset(&icon_file,0,sizeof(icon_file));
943   (void) memset(&icon_info,0,sizeof(icon_info));
944   scene=0;
945   next=(images != (Image *) NULL) ? images : image;
946   do
947   {
948     (void) WriteBlobByte(image,icon_file.directory[scene].width);
949     (void) WriteBlobByte(image,icon_file.directory[scene].height);
950     (void) WriteBlobByte(image,icon_file.directory[scene].colors);
951     (void) WriteBlobByte(image,icon_file.directory[scene].reserved);
952     (void) WriteBlobLSBShort(image,icon_file.directory[scene].planes);
953     (void) WriteBlobLSBShort(image,icon_file.directory[scene].bits_per_pixel);
954     (void) WriteBlobLSBLong(image,(unsigned int)
955       icon_file.directory[scene].size);
956     (void) WriteBlobLSBLong(image,(unsigned int)
957       icon_file.directory[scene].offset);
958     scene++;
959     next=SyncNextImageInList(next);
960   } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
961   scene=0;
962   next=(images != (Image *) NULL) ? images : image;
963   imageListLength=GetImageListLength(image);
964   do
965   {
966     if ((next->columns > 255L) && (next->rows > 255L) &&
967         ((next->compression == UndefinedCompression) ||
968         (next->compression == ZipCompression)))
969       {
970         Image
971           *write_image;
972 
973         ImageInfo
974           *write_info;
975 
976         size_t
977           length;
978 
979         unsigned char
980           *png;
981 
982         write_image=CloneImage(next,0,0,MagickTrue,exception);
983         if (write_image == (Image *) NULL)
984           {
985             images=DestroyImageList(images);
986             return(MagickFalse);
987           }
988         write_info=CloneImageInfo(image_info);
989         (void) CopyMagickString(write_info->magick,"PNG",MagickPathExtent);
990         length=0;
991         /*
992           Don't write any ancillary chunks except for gAMA.
993         */
994         (void) SetImageArtifact(write_image,"png:include-chunk","none,gama");
995         /*
996           Only write PNG32 formatted PNG (32-bit RGBA), 8 bits per channel.
997         */
998         (void) SetImageArtifact(write_image,"png:format","png32");
999         (void) SetImageArtifact(write_image,"png:color-type","6");
1000         png=(unsigned char *) ImageToBlob(write_info,write_image,&length,
1001           exception);
1002         write_image=DestroyImageList(write_image);
1003         write_info=DestroyImageInfo(write_info);
1004         if (png == (unsigned char *) NULL)
1005           {
1006             images=DestroyImageList(images);
1007             return(MagickFalse);
1008           }
1009         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;
1015         icon_file.directory[scene].size=(size_t) length;
1016         icon_file.directory[scene].offset=(size_t) TellBlob(image);
1017         (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         */
1025         (void) TransformImageColorspace(next,sRGBColorspace,exception);
1026         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))
1030           (void) SetImageStorageClass(next,DirectClass,exception);
1031         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;
1038             icon_info.compression=(size_t) BI_RGB;
1039           }
1040         else
1041           {
1042             size_t
1043               one;
1044 
1045             /*
1046               Colormapped ICON raster.
1047             */
1048             icon_info.bits_per_pixel=8;
1049             if (next->colors <= 16)
1050               icon_info.bits_per_pixel=4;
1051             if (next->colors <= 2)
1052               icon_info.bits_per_pixel=1;
1053             one=1;
1054             icon_info.number_colors=one << icon_info.bits_per_pixel;
1055             if (icon_info.number_colors < next->colors)
1056               {
1057                 (void) SetImageStorageClass(next,DirectClass,exception);
1058                 icon_info.number_colors=0;
1059                 icon_info.bits_per_pixel=(unsigned short) 24;
1060                 icon_info.compression=(size_t) BI_RGB;
1061               }
1062             else
1063               {
1064                 one=1;
1065                 icon_info.file_size+=3*(one << icon_info.bits_per_pixel);
1066                 icon_info.offset_bits+=3*(one << icon_info.bits_per_pixel);
1067                 icon_info.file_size+=(one << icon_info.bits_per_pixel);
1068                 icon_info.offset_bits+=(one << icon_info.bits_per_pixel);
1069               }
1070           }
1071         bytes_per_line=(((next->columns*icon_info.bits_per_pixel)+31) & ~31) >>
1072           3;
1073         icon_info.ba_offset=0;
1074         icon_info.width=(ssize_t) next->columns;
1075         icon_info.height=(ssize_t) next->rows;
1076         icon_info.planes=1;
1077         icon_info.image_size=bytes_per_line*next->rows;
1078         icon_info.size=40;
1079         icon_info.size+=(4*icon_info.number_colors);
1080         icon_info.size+=icon_info.image_size;
1081         icon_info.size+=(((icon_info.width+31) & ~31) >> 3)*icon_info.height;
1082         icon_info.file_size+=icon_info.image_size;
1083         icon_info.x_pixels=0;
1084         icon_info.y_pixels=0;
1085         switch (next->units)
1086         {
1087           case UndefinedResolution:
1088           case PixelsPerInchResolution:
1089           {
1090             icon_info.x_pixels=(size_t) (100.0*next->resolution.x/2.54);
1091             icon_info.y_pixels=(size_t) (100.0*next->resolution.y/2.54);
1092             break;
1093           }
1094           case PixelsPerCentimeterResolution:
1095           {
1096             icon_info.x_pixels=(size_t) (100.0*next->resolution.x);
1097             icon_info.y_pixels=(size_t) (100.0*next->resolution.y);
1098             break;
1099           }
1100         }
1101         icon_info.colors_important=icon_info.number_colors;
1102         /*
1103           Convert MIFF to ICON raster pixels.
1104         */
1105         pixels=(unsigned char *) AcquireQuantumMemory((size_t)
1106           icon_info.image_size,sizeof(*pixels));
1107         if (pixels == (unsigned char *) NULL)
1108           {
1109             images=DestroyImageList(images);
1110             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1111           }
1112         (void) memset(pixels,0,(size_t) icon_info.image_size);
1113         switch (icon_info.bits_per_pixel)
1114         {
1115           case 1:
1116           {
1117             size_t
1118               bit,
1119               byte;
1120 
1121             /*
1122               Convert PseudoClass image to a ICON monochrome image.
1123             */
1124             for (y=0; y < (ssize_t) next->rows; y++)
1125             {
1126               p=GetVirtualPixels(next,0,y,next->columns,1,exception);
1127               if (p == (const Quantum *) NULL)
1128                 break;
1129               q=pixels+(next->rows-y-1)*bytes_per_line;
1130               bit=0;
1131               byte=0;
1132               for (x=0; x < (ssize_t) next->columns; x++)
1133               {
1134                 byte<<=1;
1135                 byte|=GetPixelIndex(next,p) != 0 ? 0x01 : 0x00;
1136                 bit++;
1137                 if (bit == 8)
1138                   {
1139                     *q++=(unsigned char) byte;
1140                     bit=0;
1141                     byte=0;
1142                   }
1143                 p+=GetPixelChannels(next);
1144               }
1145               if (bit != 0)
1146                 *q++=(unsigned char) (byte << (8-bit));
1147               if (next->previous == (Image *) NULL)
1148                 {
1149                   status=SetImageProgress(next,SaveImageTag,y,next->rows);
1150                   if (status == MagickFalse)
1151                     break;
1152                 }
1153             }
1154             break;
1155           }
1156           case 4:
1157           {
1158             size_t
1159               nibble,
1160               byte;
1161 
1162             /*
1163               Convert PseudoClass image to a ICON monochrome image.
1164             */
1165             for (y=0; y < (ssize_t) next->rows; y++)
1166             {
1167               p=GetVirtualPixels(next,0,y,next->columns,1,exception);
1168               if (p == (const Quantum *) NULL)
1169                 break;
1170               q=pixels+(next->rows-y-1)*bytes_per_line;
1171               nibble=0;
1172               byte=0;
1173               for (x=0; x < (ssize_t) next->columns; x++)
1174               {
1175                 byte<<=4;
1176                 byte|=((size_t) GetPixelIndex(next,p) & 0x0f);
1177                 nibble++;
1178                 if (nibble == 2)
1179                   {
1180                     *q++=(unsigned char) byte;
1181                     nibble=0;
1182                     byte=0;
1183                   }
1184                 p+=GetPixelChannels(next);
1185               }
1186               if (nibble != 0)
1187                 *q++=(unsigned char) (byte << 4);
1188               if (next->previous == (Image *) NULL)
1189                 {
1190                   status=SetImageProgress(next,SaveImageTag,y,next->rows);
1191                   if (status == MagickFalse)
1192                     break;
1193                 }
1194             }
1195             break;
1196           }
1197           case 8:
1198           {
1199             /*
1200               Convert PseudoClass packet to ICON pixel.
1201             */
1202             for (y=0; y < (ssize_t) next->rows; y++)
1203             {
1204               p=GetVirtualPixels(next,0,y,next->columns,1,exception);
1205               if (p == (const Quantum *) NULL)
1206                 break;
1207               q=pixels+(next->rows-y-1)*bytes_per_line;
1208               for (x=0; x < (ssize_t) next->columns; x++)
1209               {
1210                 *q++=(unsigned char) GetPixelIndex(next,p);
1211                 p+=GetPixelChannels(next);
1212               }
1213               if (next->previous == (Image *) NULL)
1214                 {
1215                   status=SetImageProgress(next,SaveImageTag,y,next->rows);
1216                   if (status == MagickFalse)
1217                     break;
1218                 }
1219             }
1220             break;
1221           }
1222           case 24:
1223           case 32:
1224           {
1225             /*
1226               Convert DirectClass packet to ICON BGR888 or BGRA8888 pixel.
1227             */
1228             for (y=0; y < (ssize_t) next->rows; y++)
1229             {
1230               p=GetVirtualPixels(next,0,y,next->columns,1,exception);
1231               if (p == (const Quantum *) NULL)
1232                 break;
1233               q=pixels+(next->rows-y-1)*bytes_per_line;
1234               for (x=0; x < (ssize_t) next->columns; x++)
1235               {
1236                 *q++=ScaleQuantumToChar(GetPixelBlue(next,p));
1237                 *q++=ScaleQuantumToChar(GetPixelGreen(next,p));
1238                 *q++=ScaleQuantumToChar(GetPixelRed(next,p));
1239                 if (next->alpha_trait == UndefinedPixelTrait)
1240                   *q++=ScaleQuantumToChar(QuantumRange);
1241                 else
1242                   *q++=ScaleQuantumToChar(GetPixelAlpha(next,p));
1243                 p+=GetPixelChannels(next);
1244               }
1245               if (icon_info.bits_per_pixel == 24)
1246                 for (x=3L*(ssize_t) next->columns; x < (ssize_t) bytes_per_line; x++)
1247                   *q++=0x00;
1248               if (next->previous == (Image *) NULL)
1249                 {
1250                   status=SetImageProgress(next,SaveImageTag,y,next->rows);
1251                   if (status == MagickFalse)
1252                     break;
1253                 }
1254             }
1255             break;
1256           }
1257         }
1258         /*
1259           Write 40-byte version 3+ bitmap header.
1260         */
1261         icon_file.directory[scene].width=(unsigned char) icon_info.width;
1262         icon_file.directory[scene].height=(unsigned char) icon_info.height;
1263         icon_file.directory[scene].colors=(unsigned char)
1264           icon_info.number_colors;
1265         icon_file.directory[scene].reserved=0;
1266         icon_file.directory[scene].planes=icon_info.planes;
1267         icon_file.directory[scene].bits_per_pixel=icon_info.bits_per_pixel;
1268         icon_file.directory[scene].size=icon_info.size;
1269         icon_file.directory[scene].offset=(size_t) TellBlob(image);
1270         (void) WriteBlobLSBLong(image,(unsigned int) 40);
1271         (void) WriteBlobLSBLong(image,(unsigned int) icon_info.width);
1272         (void) WriteBlobLSBLong(image,(unsigned int) icon_info.height*2);
1273         (void) WriteBlobLSBShort(image,icon_info.planes);
1274         (void) WriteBlobLSBShort(image,icon_info.bits_per_pixel);
1275         (void) WriteBlobLSBLong(image,(unsigned int) icon_info.compression);
1276         (void) WriteBlobLSBLong(image,(unsigned int) icon_info.image_size);
1277         (void) WriteBlobLSBLong(image,(unsigned int) icon_info.x_pixels);
1278         (void) WriteBlobLSBLong(image,(unsigned int) icon_info.y_pixels);
1279         (void) WriteBlobLSBLong(image,(unsigned int) icon_info.number_colors);
1280         (void) WriteBlobLSBLong(image,(unsigned int)
1281           icon_info.colors_important);
1282         if (next->storage_class == PseudoClass)
1283           {
1284             unsigned char
1285               *icon_colormap;
1286 
1287             /*
1288               Dump colormap to file.
1289             */
1290             icon_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
1291               (1UL << icon_info.bits_per_pixel),4UL*sizeof(*icon_colormap));
1292             if (icon_colormap == (unsigned char *) NULL)
1293               {
1294                 images=DestroyImageList(images);
1295                 ThrowWriterException(ResourceLimitError,
1296                   "MemoryAllocationFailed");
1297               }
1298             q=icon_colormap;
1299             for (i=0; i < (ssize_t) next->colors; i++)
1300             {
1301               *q++=ScaleQuantumToChar(next->colormap[i].blue);
1302               *q++=ScaleQuantumToChar(next->colormap[i].green);
1303               *q++=ScaleQuantumToChar(next->colormap[i].red);
1304               *q++=(unsigned char) 0x0;
1305             }
1306             for ( ; i < (ssize_t) (1UL << icon_info.bits_per_pixel); i++)
1307             {
1308               *q++=(unsigned char) 0x00;
1309               *q++=(unsigned char) 0x00;
1310               *q++=(unsigned char) 0x00;
1311               *q++=(unsigned char) 0x00;
1312             }
1313             (void) WriteBlob(image,(size_t) (4UL*(1UL <<
1314               icon_info.bits_per_pixel)),icon_colormap);
1315             icon_colormap=(unsigned char *) RelinquishMagickMemory(
1316               icon_colormap);
1317           }
1318         (void) WriteBlob(image,(size_t) icon_info.image_size,pixels);
1319         pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1320         /*
1321           Write matte mask.
1322         */
1323         scanline_pad=(((next->columns+31) & ~31)-next->columns) >> 3;
1324         for (y=((ssize_t) next->rows - 1); y >= 0; y--)
1325         {
1326           unsigned char
1327             bit,
1328             byte;
1329 
1330           p=GetVirtualPixels(next,0,y,next->columns,1,exception);
1331           if (p == (const Quantum *) NULL)
1332             break;
1333           bit=0;
1334           byte=0;
1335           for (x=0; x < (ssize_t) next->columns; x++)
1336           {
1337             byte<<=1;
1338             if ((next->alpha_trait != UndefinedPixelTrait) &&
1339                 (GetPixelAlpha(next,p) == (Quantum) TransparentAlpha))
1340               byte|=0x01;
1341             bit++;
1342             if (bit == 8)
1343               {
1344                 (void) WriteBlobByte(image,(unsigned char) byte);
1345                 bit=0;
1346                 byte=0;
1347               }
1348             p+=GetPixelChannels(next);
1349           }
1350           if (bit != 0)
1351             (void) WriteBlobByte(image,(unsigned char) (byte << (8-bit)));
1352           for (i=0; i < (ssize_t) scanline_pad; i++)
1353             (void) WriteBlobByte(image,(unsigned char) 0);
1354         }
1355       }
1356     if (GetNextImageInList(next) == (Image *) NULL)
1357       break;
1358     status=SetImageProgress(next,SaveImagesTag,scene++,imageListLength);
1359     if (status == MagickFalse)
1360       break;
1361     next=SyncNextImageInList(next);
1362   } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
1363   offset=SeekBlob(image,0,SEEK_SET);
1364   (void) offset;
1365   (void) WriteBlobLSBShort(image,0);
1366   (void) WriteBlobLSBShort(image,1);
1367   (void) WriteBlobLSBShort(image,(unsigned short) (scene+1));
1368   scene=0;
1369   next=(images != (Image *) NULL) ? images : image;
1370   do
1371   {
1372     (void) WriteBlobByte(image,icon_file.directory[scene].width);
1373     (void) WriteBlobByte(image,icon_file.directory[scene].height);
1374     (void) WriteBlobByte(image,icon_file.directory[scene].colors);
1375     (void) WriteBlobByte(image,icon_file.directory[scene].reserved);
1376     (void) WriteBlobLSBShort(image,icon_file.directory[scene].planes);
1377     (void) WriteBlobLSBShort(image,icon_file.directory[scene].bits_per_pixel);
1378     (void) WriteBlobLSBLong(image,(unsigned int)
1379       icon_file.directory[scene].size);
1380     (void) WriteBlobLSBLong(image,(unsigned int)
1381       icon_file.directory[scene].offset);
1382     scene++;
1383     next=SyncNextImageInList(next);
1384   } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
1385   (void) CloseBlob(image);
1386   images=DestroyImageList(images);
1387   return(MagickTrue);
1388 }
1389