]> granicus.if.org Git - imagemagick/blob - coders/icon.c
(no commit message)
[imagemagick] / coders / icon.c
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 %                                John Cristy                                  %
17 %                                 July 1992                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2011 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 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/blob.h"
44 #include "MagickCore/blob-private.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/colormap.h"
47 #include "MagickCore/colorspace.h"
48 #include "MagickCore/exception.h"
49 #include "MagickCore/exception-private.h"
50 #include "MagickCore/image.h"
51 #include "MagickCore/image-private.h"
52 #include "MagickCore/list.h"
53 #include "MagickCore/log.h"
54 #include "MagickCore/magick.h"
55 #include "MagickCore/memory_.h"
56 #include "MagickCore/monitor.h"
57 #include "MagickCore/monitor-private.h"
58 #include "MagickCore/nt-feature.h"
59 #include "MagickCore/pixel-accessor.h"
60 #include "MagickCore/quantize.h"
61 #include "MagickCore/quantum-private.h"
62 #include "MagickCore/static.h"
63 #include "MagickCore/string_.h"
64 #include "MagickCore/module.h"
65 \f
66 /*
67   Define declarations.
68 */
69 #if !defined(MAGICKCORE_WINDOWS_SUPPORT) || defined(__MINGW32__)
70 #define BI_RGB  0
71 #define BI_RLE8  1
72 #define BI_BITFIELDS  3
73 #endif
74 #define MaxIcons  1024
75 \f
76 /*
77   Typedef declarations.
78 */
79 typedef struct _IconEntry
80 {
81   unsigned char
82     width,
83     height,
84     colors,
85     reserved;
86
87   unsigned short int
88     planes,
89     bits_per_pixel;
90
91   size_t
92     size,
93     offset;
94 } IconEntry;
95
96 typedef struct _IconFile
97 {
98   short
99     reserved,
100     resource_type,
101     count;
102
103   IconEntry
104     directory[MaxIcons];
105 } IconFile;
106
107 typedef struct _IconInfo
108 {
109   size_t
110     file_size,
111     ba_offset,
112     offset_bits,
113     size;
114
115   ssize_t
116     width,
117     height;
118
119   unsigned short
120     planes,
121     bits_per_pixel;
122
123   size_t
124     compression,
125     image_size,
126     x_pixels,
127     y_pixels,
128     number_colors,
129     red_mask,
130     green_mask,
131     blue_mask,
132     alpha_mask,
133     colors_important;
134
135   ssize_t
136     colorspace;
137 } IconInfo;
138 \f
139 /*
140   Forward declaractions.
141 */
142 static MagickBooleanType
143   WriteICONImage(const ImageInfo *,Image *,ExceptionInfo *);
144 \f
145 /*
146 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
147 %                                                                             %
148 %                                                                             %
149 %                                                                             %
150 %   R e a d I C O N I m a g e                                                 %
151 %                                                                             %
152 %                                                                             %
153 %                                                                             %
154 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
155 %
156 %  ReadICONImage() reads a Microsoft icon image file and returns it.  It
157 %  allocates the memory necessary for the new Image structure and returns a
158 %  pointer to the new image.
159 %
160 %  The format of the ReadICONImage method is:
161 %
162 %      Image *ReadICONImage(const ImageInfo *image_info,
163 %        ExceptionInfo *exception)
164 %
165 %  A description of each parameter follows:
166 %
167 %    o image_info: the image info.
168 %
169 %    o exception: return any errors or warnings in this structure.
170 %
171 */
172 static Image *ReadICONImage(const ImageInfo *image_info,
173   ExceptionInfo *exception)
174 {
175   IconFile
176     icon_file;
177
178   IconInfo
179     icon_info;
180
181   Image
182     *image;
183
184   MagickBooleanType
185     status;
186
187   register ssize_t
188     i,
189     x;
190
191   register Quantum
192     *q;
193
194   register unsigned char
195     *p;
196
197   size_t
198     bit,
199     byte,
200     bytes_per_line,
201     length,
202     one,
203     scanline_pad;
204
205   ssize_t
206     count,
207     offset,
208     y;
209
210   unsigned char
211     *pixels;
212
213   /*
214     Open image file.
215   */
216   assert(image_info != (const ImageInfo *) NULL);
217   assert(image_info->signature == MagickSignature);
218   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"%s",image_info->filename);
219   assert(exception != (ExceptionInfo *) NULL);
220   assert(exception->signature == MagickSignature);
221   image=AcquireImage(image_info,exception);
222   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
223   if (status == MagickFalse)
224     {
225       image=DestroyImageList(image);
226       return((Image *) NULL);
227     }
228   icon_file.reserved=(short) ReadBlobLSBShort(image);
229   icon_file.resource_type=(short) ReadBlobLSBShort(image);
230   icon_file.count=(short) ReadBlobLSBShort(image);
231   if ((icon_file.reserved != 0) ||
232       ((icon_file.resource_type != 1) && (icon_file.resource_type != 2)) ||
233       (icon_file.count > MaxIcons))
234     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
235   for (i=0; i < icon_file.count; i++)
236   {
237     icon_file.directory[i].width=(unsigned char) ReadBlobByte(image);
238     icon_file.directory[i].height=(unsigned char) ReadBlobByte(image);
239     icon_file.directory[i].colors=(unsigned char) ReadBlobByte(image);
240     icon_file.directory[i].reserved=(unsigned char) ReadBlobByte(image);
241     icon_file.directory[i].planes=(unsigned short) ReadBlobLSBShort(image);
242     icon_file.directory[i].bits_per_pixel=(unsigned short)
243       ReadBlobLSBShort(image);
244     icon_file.directory[i].size=ReadBlobLSBLong(image);
245     icon_file.directory[i].offset=ReadBlobLSBLong(image);
246   }
247   one=1;
248   for (i=0; i < icon_file.count; i++)
249   {
250     /*
251       Verify Icon identifier.
252     */
253     offset=(ssize_t) SeekBlob(image,(MagickOffsetType)
254       icon_file.directory[i].offset,SEEK_SET);
255     if (offset < 0)
256       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
257     icon_info.size=ReadBlobLSBLong(image);
258     icon_info.width=(unsigned char) ((int) ReadBlobLSBLong(image));
259     icon_info.height=(unsigned char) ((int) ReadBlobLSBLong(image)/2);
260     icon_info.planes=ReadBlobLSBShort(image);
261     icon_info.bits_per_pixel=ReadBlobLSBShort(image);
262     if (icon_info.bits_per_pixel > 32)
263       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
264     icon_info.compression=ReadBlobLSBLong(image);
265     icon_info.image_size=ReadBlobLSBLong(image);
266     icon_info.x_pixels=ReadBlobLSBLong(image);
267     icon_info.y_pixels=ReadBlobLSBLong(image);
268     icon_info.number_colors=ReadBlobLSBLong(image);
269     icon_info.colors_important=ReadBlobLSBLong(image);
270     image->matte=MagickTrue;
271     image->columns=(size_t) icon_file.directory[i].width;
272     if ((ssize_t) image->columns > icon_info.width)
273       image->columns=(size_t) icon_info.width;
274     if (image->columns == 0)
275       image->columns=256;
276     image->rows=(size_t) icon_file.directory[i].height;
277     if ((ssize_t) image->rows > icon_info.height)
278       image->rows=(size_t) icon_info.height;
279     if (image->rows == 0)
280       image->rows=256;
281     image->depth=icon_info.bits_per_pixel;
282     if (image->debug != MagickFalse)
283       {
284         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
285           " scene    = %.20g",(double) i);
286         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
287           "   size   = %.20g",(double) icon_info.size);
288         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
289           "   width  = %.20g",(double) icon_file.directory[i].width);
290         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
291           "   height = %.20g",(double) icon_file.directory[i].height);
292         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
293           "   colors = %.20g",(double ) icon_info.number_colors);
294         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
295           "   planes = %.20g",(double) icon_info.planes);
296         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
297           "   bpp    = %.20g",(double) icon_info.bits_per_pixel);
298       }
299   if ((icon_info.number_colors != 0) || (icon_info.bits_per_pixel <= 16))
300     {
301       image->storage_class=PseudoClass;
302       image->colors=icon_info.number_colors;
303       if (image->colors == 0)
304         image->colors=one << icon_info.bits_per_pixel;
305     }
306   if (image->storage_class == PseudoClass)
307     {
308       register ssize_t
309         i;
310
311       size_t
312         number_colors,
313         one;
314
315       unsigned char
316         *icon_colormap;
317
318       /*
319         Read Icon raster colormap.
320       */
321       one=1;
322       number_colors=one << icon_info.bits_per_pixel;
323       if (AcquireImageColormap(image,number_colors,exception) == MagickFalse)
324         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
325       icon_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
326         image->colors,4UL*sizeof(*icon_colormap));
327       if (icon_colormap == (unsigned char *) NULL)
328         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
329       count=ReadBlob(image,(size_t) (4*image->colors),icon_colormap);
330       if (count != (ssize_t) (4*image->colors))
331         ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
332       p=icon_colormap;
333       for (i=0; i < (ssize_t) image->colors; i++)
334       {
335         image->colormap[i].blue=(Quantum) ScaleCharToQuantum(*p++);
336         image->colormap[i].green=(Quantum) ScaleCharToQuantum(*p++);
337         image->colormap[i].red=(Quantum) ScaleCharToQuantum(*p++);
338         p++;
339       }
340       icon_colormap=(unsigned char *) RelinquishMagickMemory(icon_colormap);
341     }
342     if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
343       if (image->scene >= (image_info->scene+image_info->number_scenes-1))
344         break;
345     length=icon_file.directory[i].size-40;
346     pixels=(unsigned char *) AcquireQuantumMemory(length,sizeof(*pixels));
347     if (pixels == (unsigned char *) NULL)
348       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
349     count=ReadBlob(image,length,pixels);
350     if (count != (ssize_t) length)
351       {
352         pixels=(unsigned char *) RelinquishMagickMemory(pixels);
353         ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
354       }
355     if (memcmp(pixels,"\211PNG\r\n\032\n",8) == 0)
356       {
357         Image
358           *icon_image;
359
360         ImageInfo
361           *read_info;
362
363         /*
364           Icon image encoded as a compressed PNG image.
365         */
366         read_info=CloneImageInfo(image_info);
367         (void) CopyMagickString(read_info->magick,"PNG",MaxTextExtent);
368         icon_image=BlobToImage(read_info,pixels,length,exception);
369         read_info=DestroyImageInfo(read_info);
370         pixels=(unsigned char *) RelinquishMagickMemory(pixels);
371         if (icon_image == (Image *) NULL)
372           {
373             image=DestroyImageList(image);
374             return((Image *) NULL);
375           }
376         DestroyBlob(icon_image);
377         icon_image->blob=ReferenceBlob(image->blob);
378         ReplaceImageInList(&image,icon_image);
379       }
380     else
381       {
382         register unsigned char
383           *p;
384
385         /*
386           Convert Icon raster image to pixel packets.
387         */
388         bytes_per_line=(((image->columns*icon_info.bits_per_pixel)+31) &
389           ~31) >> 3;
390         (void) bytes_per_line;
391         scanline_pad=((((image->columns*icon_info.bits_per_pixel)+31) & ~31)-
392           (image->columns*icon_info.bits_per_pixel)) >> 3;
393         p=pixels;
394         switch (icon_info.bits_per_pixel)
395         {
396           case 1:
397           {
398             /*
399               Convert bitmap scanline.
400             */
401             for (y=(ssize_t) image->rows-1; y >= 0; y--)
402             {
403               q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
404               if (q == (Quantum *) NULL)
405                 break;
406               for (x=0; x < (ssize_t) (image->columns-7); x+=8)
407               {
408                 byte=(size_t) *p++;
409                 for (bit=0; bit < 8; bit++)
410                 {
411                   SetPixelIndex(image,((byte & (0x80 >> bit)) != 0 ? 0x01 :
412                     0x00),q);
413                   q+=GetPixelChannels(image);
414                 }
415               }
416               if ((image->columns % 8) != 0)
417                 {
418                   byte=(size_t) *p++;
419                   for (bit=0; bit < (image->columns % 8); bit++)
420                   {
421                     SetPixelIndex(image,((byte & (0x80 >> bit)) != 0 ? 0x01 :
422                       0x00),q);
423                     q+=GetPixelChannels(image);
424                   }
425                 }
426               for (x=0; x < (ssize_t) scanline_pad; x++)
427                 (void) *p++;
428               if (SyncAuthenticPixels(image,exception) == MagickFalse)
429                 break;
430               if (image->previous == (Image *) NULL)
431                 {
432                   status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
433                     image->rows);
434                   if (status == MagickFalse)
435                     break;
436                 }
437             }
438             break;
439           }
440           case 4:
441           {
442             /*
443               Read 4-bit Icon scanline.
444             */
445             for (y=(ssize_t) image->rows-1; y >= 0; y--)
446             {
447               q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
448               if (q == (Quantum *) NULL)
449                 break;
450               for (x=0; x < ((ssize_t) image->columns-1); x+=2)
451               {
452                 byte=(size_t) *p++;
453                 SetPixelIndex(image,((byte >> 4) & 0xf),q);
454                 q+=GetPixelChannels(image);
455                 SetPixelIndex(image,((byte) & 0xf),q);
456                 q+=GetPixelChannels(image);
457               }
458               if ((image->columns % 2) != 0)
459                 {
460                   byte=(size_t) *p++;
461                   SetPixelIndex(image,((byte >> 4) & 0xf),q);
462                   q+=GetPixelChannels(image);
463                 }
464               for (x=0; x < (ssize_t) scanline_pad; x++)
465                 (void) *p++;
466               if (SyncAuthenticPixels(image,exception) == MagickFalse)
467                 break;
468               if (image->previous == (Image *) NULL)
469                 {
470                   status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
471                     image->rows);
472                   if (status == MagickFalse)
473                     break;
474                 }
475             }
476             break;
477           }
478           case 8:
479           {
480             /*
481               Convert PseudoColor scanline.
482             */
483             for (y=(ssize_t) image->rows-1; y >= 0; y--)
484             {
485               q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
486               if (q == (Quantum *) NULL)
487                 break;
488               for (x=0; x < (ssize_t) image->columns; x++)
489               {
490                 byte=(size_t) *p++;
491                 SetPixelIndex(image,byte,q);
492                 q+=GetPixelChannels(image);
493               }
494               for (x=0; x < (ssize_t) scanline_pad; x++)
495                 (void) *p++;
496               if (SyncAuthenticPixels(image,exception) == MagickFalse)
497                 break;
498               if (image->previous == (Image *) NULL)
499                 {
500                   status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
501                     image->rows);
502                   if (status == MagickFalse)
503                     break;
504                 }
505             }
506             break;
507           }
508           case 16:
509           {
510             /*
511               Convert PseudoColor scanline.
512             */
513             for (y=(ssize_t) image->rows-1; y >= 0; y--)
514             {
515               q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
516               if (q == (Quantum *) NULL)
517                 break;
518               for (x=0; x < (ssize_t) image->columns; x++)
519               {
520                 byte=(size_t) *p++;
521                 byte|=(size_t) (*p++ << 8);
522                 SetPixelIndex(image,byte,q);
523                 q+=GetPixelChannels(image);
524               }
525               for (x=0; x < (ssize_t) scanline_pad; x++)
526                 (void) *p++;
527               if (SyncAuthenticPixels(image,exception) == MagickFalse)
528                 break;
529               if (image->previous == (Image *) NULL)
530                 {
531                   status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
532                     image->rows);
533                   if (status == MagickFalse)
534                     break;
535                 }
536             }
537             break;
538           }
539           case 24:
540           case 32:
541           {
542             /*
543               Convert DirectColor scanline.
544             */
545             for (y=(ssize_t) image->rows-1; y >= 0; y--)
546             {
547               q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
548               if (q == (Quantum *) NULL)
549                 break;
550               for (x=0; x < (ssize_t) image->columns; x++)
551               {
552                 SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
553                 SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
554                 SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
555                 if (icon_info.bits_per_pixel == 32)
556                   SetPixelAlpha(image,ScaleCharToQuantum(*p++),q);
557                 q+=GetPixelChannels(image);
558               }
559               if (icon_info.bits_per_pixel == 24)
560                 for (x=0; x < (ssize_t) scanline_pad; x++)
561                   (void) *p++;
562               if (SyncAuthenticPixels(image,exception) == MagickFalse)
563                 break;
564               if (image->previous == (Image *) NULL)
565                 {
566                   status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
567                     image->rows);
568                   if (status == MagickFalse)
569                     break;
570                 }
571             }
572             break;
573           }
574           default:
575             ThrowReaderException(CorruptImageError,"ImproperImageHeader");
576         }
577         if (image_info->ping == MagickFalse)
578           (void) SyncImage(image);
579         if (icon_info.bits_per_pixel != 32)
580           {
581             /*
582               Read the ICON alpha mask.
583             */
584             image->storage_class=DirectClass;
585             for (y=(ssize_t) image->rows-1; y >= 0; y--)
586             {
587               q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
588               if (q == (Quantum *) NULL)
589                 break;
590               for (x=0; x < ((ssize_t) image->columns-7); x+=8)
591               {
592                 byte=(size_t) *p++;
593                 for (bit=0; bit < 8; bit++)
594                 {
595                   SetPixelAlpha(image,(((byte & (0x80 >> bit)) != 0) ?
596                     TransparentAlpha : OpaqueAlpha),q);
597                   q+=GetPixelChannels(image);
598                 }
599               }
600               if ((image->columns % 8) != 0)
601                 {
602                   byte=(size_t) *p++;
603                   for (bit=0; bit < (image->columns % 8); bit++)
604                   {
605                     SetPixelAlpha(image,(((byte & (0x80 >> bit)) != 0) ?
606                       TransparentAlpha : OpaqueAlpha),q);
607                     q+=GetPixelChannels(image);
608                   }
609                 }
610               if ((image->columns % 32) != 0)
611                 for (x=0; x < (ssize_t) ((32-(image->columns % 32))/8); x++)
612                   (void) *p++;
613               if (SyncAuthenticPixels(image,exception) == MagickFalse)
614                 break;
615             }
616           }
617         if (EOFBlob(image) != MagickFalse)
618           {
619             ThrowFileException(exception,CorruptImageError,
620               "UnexpectedEndOfFile",image->filename);
621             break;
622           }
623       }
624     pixels=(unsigned char *) RelinquishMagickMemory(pixels);
625     /*
626       Proceed to next image.
627     */
628     if (image_info->number_scenes != 0)
629       if (image->scene >= (image_info->scene+image_info->number_scenes-1))
630         break;
631     if (i < (ssize_t) (icon_file.count-1))
632       {
633         /*
634           Allocate next image structure.
635         */
636         AcquireNextImage(image_info,image,exception);
637         if (GetNextImageInList(image) == (Image *) NULL)
638           {
639             image=DestroyImageList(image);
640             return((Image *) NULL);
641           }
642         image=SyncNextImageInList(image);
643         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
644           GetBlobSize(image));
645         if (status == MagickFalse)
646           break;
647       }
648   }
649   (void) CloseBlob(image);
650   return(GetFirstImageInList(image));
651 }
652 \f
653 /*
654 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
655 %                                                                             %
656 %                                                                             %
657 %                                                                             %
658 %   R e g i s t e r I C O N I m a g e                                         %
659 %                                                                             %
660 %                                                                             %
661 %                                                                             %
662 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
663 %
664 %  RegisterICONImage() adds attributes for the Icon image format to
665 %  the list of supported formats.  The attributes include the image format
666 %  tag, a method to read and/or write the format, whether the format
667 %  supports the saving of more than one frame to the same file or blob,
668 %  whether the format supports native in-memory I/O, and a brief
669 %  description of the format.
670 %
671 %  The format of the RegisterICONImage method is:
672 %
673 %      size_t RegisterICONImage(void)
674 %
675 */
676 ModuleExport size_t RegisterICONImage(void)
677 {
678   MagickInfo
679     *entry;
680
681   entry=SetMagickInfo("CUR");
682   entry->decoder=(DecodeImageHandler *) ReadICONImage;
683   entry->encoder=(EncodeImageHandler *) WriteICONImage;
684   entry->adjoin=MagickFalse;
685   entry->seekable_stream=MagickTrue;
686   entry->description=ConstantString("Microsoft icon");
687   entry->module=ConstantString("CUR");
688   (void) RegisterMagickInfo(entry);
689   entry=SetMagickInfo("ICO");
690   entry->decoder=(DecodeImageHandler *) ReadICONImage;
691   entry->encoder=(EncodeImageHandler *) WriteICONImage;
692   entry->adjoin=MagickTrue;
693   entry->seekable_stream=MagickTrue;
694   entry->description=ConstantString("Microsoft icon");
695   entry->module=ConstantString("ICON");
696   (void) RegisterMagickInfo(entry);
697   entry=SetMagickInfo("ICON");
698   entry->decoder=(DecodeImageHandler *) ReadICONImage;
699   entry->encoder=(EncodeImageHandler *) WriteICONImage;
700   entry->adjoin=MagickFalse;
701   entry->seekable_stream=MagickTrue;
702   entry->description=ConstantString("Microsoft icon");
703   entry->module=ConstantString("ICON");
704   (void) RegisterMagickInfo(entry);
705   return(MagickImageCoderSignature);
706 }
707 \f
708 /*
709 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
710 %                                                                             %
711 %                                                                             %
712 %                                                                             %
713 %   U n r e g i s t e r I C O N I m a g e                                     %
714 %                                                                             %
715 %                                                                             %
716 %                                                                             %
717 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
718 %
719 %  UnregisterICONImage() removes format registrations made by the
720 %  ICON module from the list of supported formats.
721 %
722 %  The format of the UnregisterICONImage method is:
723 %
724 %      UnregisterICONImage(void)
725 %
726 */
727 ModuleExport void UnregisterICONImage(void)
728 {
729   (void) UnregisterMagickInfo("CUR");
730   (void) UnregisterMagickInfo("ICO");
731   (void) UnregisterMagickInfo("ICON");
732 }
733 \f
734 /*
735 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
736 %                                                                             %
737 %                                                                             %
738 %                                                                             %
739 %   W r i t e I C O N I m a g e                                               %
740 %                                                                             %
741 %                                                                             %
742 %                                                                             %
743 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
744 %
745 %  WriteICONImage() writes an image in Microsoft Windows bitmap encoded
746 %  image format, version 3 for Windows or (if the image has a matte channel)
747 %  version 4.
748 %
749 %  The format of the WriteICONImage method is:
750 %
751 %      MagickBooleanType WriteICONImage(const ImageInfo *image_info,
752 %        Image *image,ExceptionInfo *exception)
753 %
754 %  A description of each parameter follows.
755 %
756 %    o image_info: the image info.
757 %
758 %    o image:  The image.
759 %
760 %    o exception: return any errors or warnings in this structure.
761 %
762 */
763 static MagickBooleanType WriteICONImage(const ImageInfo *image_info,
764   Image *image,ExceptionInfo *exception)
765 {
766   IconFile
767     icon_file;
768
769   IconInfo
770     icon_info;
771
772   Image
773     *next;
774   
775   MagickBooleanType
776     status;
777
778   MagickOffsetType
779     offset,
780     scene;
781
782   register const Quantum
783     *p;
784
785   register ssize_t
786     i,
787     x;
788
789   register unsigned char
790     *q;
791
792   size_t
793     bytes_per_line,
794     scanline_pad;
795
796   ssize_t
797     y;
798
799   unsigned char
800     bit,
801     byte,
802     *pixels;
803
804   /*
805     Open output image file.
806   */
807   assert(image_info != (const ImageInfo *) NULL);
808   assert(image_info->signature == MagickSignature);
809   assert(image != (Image *) NULL);
810   assert(image->signature == MagickSignature);
811     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"%s",image->filename);
812   assert(exception != (ExceptionInfo *) NULL);
813   assert(exception->signature == MagickSignature);
814   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
815   if (status == MagickFalse)
816     return(status);
817   scene=0;
818   next=image;
819   do
820   {
821     if ((image->columns > 256L) || (image->rows > 256L))
822       ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
823     scene++;
824     next=SyncNextImageInList(next);
825   } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
826   /*
827     Dump out a ICON header template to be properly initialized later.
828   */
829   (void) WriteBlobLSBShort(image,0);
830   (void) WriteBlobLSBShort(image,1);
831   (void) WriteBlobLSBShort(image,(unsigned char) scene);
832   (void) ResetMagickMemory(&icon_file,0,sizeof(icon_file));
833   (void) ResetMagickMemory(&icon_info,0,sizeof(icon_info));
834   scene=0;
835   next=image;
836   do
837   {
838     (void) WriteBlobByte(image,icon_file.directory[scene].width);
839     (void) WriteBlobByte(image,icon_file.directory[scene].height);
840     (void) WriteBlobByte(image,icon_file.directory[scene].colors);
841     (void) WriteBlobByte(image,icon_file.directory[scene].reserved);
842     (void) WriteBlobLSBShort(image,icon_file.directory[scene].planes);
843     (void) WriteBlobLSBShort(image,icon_file.directory[scene].bits_per_pixel);
844     (void) WriteBlobLSBLong(image,(unsigned int)
845       icon_file.directory[scene].size);
846     (void) WriteBlobLSBLong(image,(unsigned int)
847       icon_file.directory[scene].offset);
848     scene++;
849     next=SyncNextImageInList(next);
850   } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
851   scene=0;
852   next=image;
853   do
854   {
855     if ((next->columns == 256) && (next->rows == 256))
856       {
857         Image
858           *write_image;
859
860         ImageInfo
861           *write_info;
862
863         size_t
864           length;
865
866         unsigned char
867           *png;
868
869         /*
870           Icon image encoded as a compressed PNG image.
871         */
872         write_image=CloneImage(next,0,0,MagickTrue,exception);
873         if (write_image == (Image *) NULL)
874           return(MagickFalse);
875         write_info=CloneImageInfo(image_info);
876         (void) CopyMagickString(write_info->filename,"PNG:",MaxTextExtent);
877         png=(unsigned char *) ImageToBlob(write_info,write_image,&length,
878           exception);
879         write_image=DestroyImage(write_image);
880         write_info=DestroyImageInfo(write_info);
881         if (png == (unsigned char *) NULL)
882           return(MagickFalse);
883         icon_file.directory[scene].width=0;
884         icon_file.directory[scene].height=0;
885         icon_file.directory[scene].colors=0;
886         icon_file.directory[scene].reserved=0;
887         icon_file.directory[scene].planes=1;
888         icon_file.directory[scene].bits_per_pixel=32;
889         icon_file.directory[scene].size=(size_t) length;
890         icon_file.directory[scene].offset=(size_t) TellBlob(image);
891         (void) WriteBlob(image,(size_t) length,png);
892         png=(unsigned char *) RelinquishMagickMemory(png);
893       }
894     else
895       {
896         /*
897           Initialize ICON raster file header.
898         */
899         if (next->colorspace != RGBColorspace)
900           (void) TransformImageColorspace(next,RGBColorspace);
901         icon_info.file_size=14+12+28;
902         icon_info.offset_bits=icon_info.file_size;
903         icon_info.compression=BI_RGB;
904         if ((next->storage_class != DirectClass) && (next->colors > 256))
905           (void) SetImageStorageClass(next,DirectClass,exception);
906         if (next->storage_class == DirectClass)
907           {
908             /*
909               Full color ICON raster.
910             */
911             icon_info.number_colors=0;
912             icon_info.bits_per_pixel=32;
913             icon_info.compression=(size_t) BI_RGB;
914           }
915         else
916           {
917             size_t
918               one;
919
920             /*
921               Colormapped ICON raster.
922             */
923             icon_info.bits_per_pixel=8;
924             if (next->colors <= 256)
925               icon_info.bits_per_pixel=8;
926             if (next->colors <= 16)
927               icon_info.bits_per_pixel=4;
928             if (next->colors <= 2)
929               icon_info.bits_per_pixel=1;
930             one=1;
931             icon_info.number_colors=one << icon_info.bits_per_pixel;
932             if (icon_info.number_colors < next->colors)
933               {
934                 (void) SetImageStorageClass(next,DirectClass,exception);
935                 icon_info.number_colors=0;
936                 icon_info.bits_per_pixel=(unsigned short) 24;
937                 icon_info.compression=(size_t) BI_RGB;
938               }
939             else
940               {
941                 size_t
942                   one;
943
944                 one=1;
945                 icon_info.file_size+=3*(one << icon_info.bits_per_pixel);
946                 icon_info.offset_bits+=3*(one << icon_info.bits_per_pixel);
947                 icon_info.file_size+=(one << icon_info.bits_per_pixel);
948                 icon_info.offset_bits+=(one << icon_info.bits_per_pixel);
949               }
950           }
951         bytes_per_line=(((next->columns*icon_info.bits_per_pixel)+31) &
952           ~31) >> 3;
953         icon_info.ba_offset=0;
954         icon_info.width=(ssize_t) next->columns;
955         icon_info.height=(ssize_t) next->rows;
956         icon_info.planes=1;
957         icon_info.image_size=bytes_per_line*next->rows;
958         icon_info.size=40;
959         icon_info.size+=(4*icon_info.number_colors);
960         icon_info.size+=icon_info.image_size;
961         icon_info.size+=(((icon_info.width+31) & ~31) >> 3)*icon_info.height;
962         icon_info.file_size+=icon_info.image_size;
963         icon_info.x_pixels=0;
964         icon_info.y_pixels=0;
965         switch (next->units)
966         {
967           case UndefinedResolution:
968           case PixelsPerInchResolution:
969           {
970             icon_info.x_pixels=(size_t) (100.0*next->x_resolution/2.54);
971             icon_info.y_pixels=(size_t) (100.0*next->y_resolution/2.54);
972             break;
973           }
974           case PixelsPerCentimeterResolution:
975           {
976             icon_info.x_pixels=(size_t) (100.0*next->x_resolution);
977             icon_info.y_pixels=(size_t) (100.0*next->y_resolution);
978             break;
979           }
980         }
981         icon_info.colors_important=icon_info.number_colors;
982         /*
983           Convert MIFF to ICON raster pixels.
984         */
985         pixels=(unsigned char *) AcquireQuantumMemory((size_t)
986           icon_info.image_size,sizeof(*pixels));
987         if (pixels == (unsigned char *) NULL)
988           ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
989         (void) ResetMagickMemory(pixels,0,(size_t) icon_info.image_size);
990         switch (icon_info.bits_per_pixel)
991         {
992           case 1:
993           {
994             size_t
995               bit,
996               byte;
997
998             /*
999               Convert PseudoClass image to a ICON monochrome image.
1000             */
1001             for (y=0; y < (ssize_t) next->rows; y++)
1002             {
1003               p=GetVirtualPixels(next,0,y,next->columns,1,exception);
1004               if (p == (const Quantum *) NULL)
1005                 break;
1006               q=pixels+(next->rows-y-1)*bytes_per_line;
1007               bit=0;
1008               byte=0;
1009               for (x=0; x < (ssize_t) next->columns; x++)
1010               {
1011                 byte<<=1;
1012                 byte|=GetPixelIndex(next,p) != 0 ? 0x01 : 0x00;
1013                 bit++;
1014                 if (bit == 8)
1015                   {
1016                     *q++=(unsigned char) byte;
1017                     bit=0;
1018                     byte=0;
1019                   }
1020                 p+=GetPixelChannels(image);
1021               }
1022               if (bit != 0)
1023                 *q++=(unsigned char) (byte << (8-bit));
1024               if (next->previous == (Image *) NULL)
1025                 {
1026                   status=SetImageProgress(next,SaveImageTag,y,next->rows);
1027                   if (status == MagickFalse)
1028                     break;
1029                 }
1030             }
1031             break;
1032           }
1033           case 4:
1034           {
1035             size_t
1036               nibble,
1037               byte;
1038
1039             /*
1040               Convert PseudoClass image to a ICON monochrome image.
1041             */
1042             for (y=0; y < (ssize_t) next->rows; y++)
1043             {
1044               p=GetVirtualPixels(next,0,y,next->columns,1,exception);
1045               if (p == (const Quantum *) NULL)
1046                 break;
1047               q=pixels+(next->rows-y-1)*bytes_per_line;
1048               nibble=0;
1049               byte=0;
1050               for (x=0; x < (ssize_t) next->columns; x++)
1051               {
1052                 byte<<=4;
1053                 byte|=((size_t) GetPixelIndex(next,p) & 0x0f);
1054                 nibble++;
1055                 if (nibble == 2)
1056                   {
1057                     *q++=(unsigned char) byte;
1058                     nibble=0;
1059                     byte=0;
1060                   }
1061                 p+=GetPixelChannels(image);
1062               }
1063               if (nibble != 0)
1064                 *q++=(unsigned char) (byte << 4);
1065               if (next->previous == (Image *) NULL)
1066                 {
1067                   status=SetImageProgress(next,SaveImageTag,y,next->rows);
1068                   if (status == MagickFalse)
1069                     break;
1070                 }
1071             }
1072             break;
1073           }
1074           case 8:
1075           {
1076             /*
1077               Convert PseudoClass packet to ICON pixel.
1078             */
1079             for (y=0; y < (ssize_t) next->rows; y++)
1080             {
1081               p=GetVirtualPixels(next,0,y,next->columns,1,exception);
1082               if (p == (const Quantum *) NULL)
1083                 break;
1084               q=pixels+(next->rows-y-1)*bytes_per_line;
1085               for (x=0; x < (ssize_t) next->columns; x++)
1086               {
1087                 *q++=(unsigned char) GetPixelIndex(next,p);
1088                 p+=GetPixelChannels(image);
1089               }
1090               if (next->previous == (Image *) NULL)
1091                 {
1092                   status=SetImageProgress(next,SaveImageTag,y,next->rows);
1093                   if (status == MagickFalse)
1094                     break;
1095                 }
1096             }
1097             break;
1098           }
1099           case 24:
1100           case 32:
1101           {
1102             /*
1103               Convert DirectClass packet to ICON BGR888 or BGRA8888 pixel.
1104             */
1105             for (y=0; y < (ssize_t) next->rows; y++)
1106             {
1107               p=GetVirtualPixels(next,0,y,next->columns,1,exception);
1108               if (p == (const Quantum *) NULL)
1109                 break;
1110               q=pixels+(next->rows-y-1)*bytes_per_line;
1111               for (x=0; x < (ssize_t) next->columns; x++)
1112               {
1113                 *q++=ScaleQuantumToChar(GetPixelBlue(next,p));
1114                 *q++=ScaleQuantumToChar(GetPixelGreen(next,p));
1115                 *q++=ScaleQuantumToChar(GetPixelRed(next,p));
1116                 if (next->matte == MagickFalse)
1117                   *q++=ScaleQuantumToChar(QuantumRange);
1118                 else
1119                   *q++=ScaleQuantumToChar(GetPixelAlpha(next,p));
1120                 p+=GetPixelChannels(next);
1121               }
1122               if (icon_info.bits_per_pixel == 24)
1123                 for (x=3L*(ssize_t) next->columns; x < (ssize_t) bytes_per_line; x++)
1124                   *q++=0x00;
1125               if (next->previous == (Image *) NULL)
1126                 {
1127                   status=SetImageProgress(next,SaveImageTag,y,next->rows);
1128                   if (status == MagickFalse)
1129                     break;
1130                 }
1131             }
1132             break;
1133           }
1134         }
1135         /*
1136           Write 40-byte version 3+ bitmap header.
1137         */
1138         icon_file.directory[scene].width=(unsigned char) icon_info.width;
1139         icon_file.directory[scene].height=(unsigned char) icon_info.height;
1140         icon_file.directory[scene].colors=(unsigned char)
1141           icon_info.number_colors;
1142         icon_file.directory[scene].reserved=0;
1143         icon_file.directory[scene].planes=icon_info.planes;
1144         icon_file.directory[scene].bits_per_pixel=icon_info.bits_per_pixel;
1145         icon_file.directory[scene].size=icon_info.size;
1146         icon_file.directory[scene].offset=(size_t) TellBlob(image);
1147         (void) WriteBlobLSBLong(image,(unsigned int) 40);
1148         (void) WriteBlobLSBLong(image,(unsigned int) icon_info.width);
1149         (void) WriteBlobLSBLong(image,(unsigned int) icon_info.height*2);
1150         (void) WriteBlobLSBShort(image,icon_info.planes);
1151         (void) WriteBlobLSBShort(image,icon_info.bits_per_pixel);
1152         (void) WriteBlobLSBLong(image,(unsigned int) icon_info.compression);
1153         (void) WriteBlobLSBLong(image,(unsigned int) icon_info.image_size);
1154         (void) WriteBlobLSBLong(image,(unsigned int) icon_info.x_pixels);
1155         (void) WriteBlobLSBLong(image,(unsigned int) icon_info.y_pixels);
1156         (void) WriteBlobLSBLong(image,(unsigned int) icon_info.number_colors);
1157         (void) WriteBlobLSBLong(image,(unsigned int) icon_info.colors_important);
1158         if (next->storage_class == PseudoClass)
1159           {
1160             unsigned char
1161               *icon_colormap;
1162
1163             /*
1164               Dump colormap to file.
1165             */
1166             icon_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
1167               (1UL << icon_info.bits_per_pixel),4UL*sizeof(*icon_colormap));
1168             if (icon_colormap == (unsigned char *) NULL)
1169               ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1170             q=icon_colormap;
1171             for (i=0; i < (ssize_t) next->colors; i++)
1172             {
1173               *q++=ScaleQuantumToChar(next->colormap[i].blue);
1174               *q++=ScaleQuantumToChar(next->colormap[i].green);
1175               *q++=ScaleQuantumToChar(next->colormap[i].red);
1176               *q++=(unsigned char) 0x0;
1177             }
1178             for ( ; i < (ssize_t) (1UL << icon_info.bits_per_pixel); i++)
1179             {
1180               *q++=(unsigned char) 0x00;
1181               *q++=(unsigned char) 0x00;
1182               *q++=(unsigned char) 0x00;
1183               *q++=(unsigned char) 0x00;
1184             }
1185             (void) WriteBlob(image,(size_t) (4UL*(1UL <<
1186               icon_info.bits_per_pixel)),icon_colormap);
1187             icon_colormap=(unsigned char *) RelinquishMagickMemory(
1188               icon_colormap);
1189           }
1190         (void) WriteBlob(image,(size_t) icon_info.image_size,pixels);
1191         pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1192         /*
1193           Write matte mask.
1194         */
1195         scanline_pad=(((next->columns+31) & ~31)-next->columns) >> 3;
1196         for (y=((ssize_t) next->rows - 1); y >= 0; y--)
1197         {
1198           p=GetVirtualPixels(next,0,y,next->columns,1,exception);
1199           if (p == (const Quantum *) NULL)
1200             break;
1201           bit=0;
1202           byte=0;
1203           for (x=0; x < (ssize_t) next->columns; x++)
1204           {
1205             byte<<=1;
1206             if ((next->matte == MagickTrue) &&
1207                 (GetPixelAlpha(next,p) == (Quantum) TransparentAlpha))
1208               byte|=0x01;
1209             bit++;
1210             if (bit == 8)
1211               {
1212                 (void) WriteBlobByte(image,(unsigned char) byte);
1213                 bit=0;
1214                 byte=0;
1215               }
1216             p+=GetPixelChannels(next);
1217           }
1218           if (bit != 0)
1219             (void) WriteBlobByte(image,(unsigned char) (byte << (8-bit)));
1220           for (i=0; i < (ssize_t) scanline_pad; i++)
1221             (void) WriteBlobByte(image,(unsigned char) 0);
1222         }
1223       }
1224     if (GetNextImageInList(next) == (Image *) NULL)
1225       break;
1226     next=SyncNextImageInList(next);
1227     status=SetImageProgress(next,SaveImagesTag,scene++,
1228       GetImageListLength(next));
1229     if (status == MagickFalse)
1230       break;
1231   } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
1232   offset=SeekBlob(image,0,SEEK_SET);
1233   (void) offset;
1234   (void) WriteBlobLSBShort(image,0);
1235   (void) WriteBlobLSBShort(image,1);
1236   (void) WriteBlobLSBShort(image,(unsigned short) (scene+1));
1237   scene=0;
1238   next=image;
1239   do
1240   {
1241     (void) WriteBlobByte(image,icon_file.directory[scene].width);
1242     (void) WriteBlobByte(image,icon_file.directory[scene].height);
1243     (void) WriteBlobByte(image,icon_file.directory[scene].colors);
1244     (void) WriteBlobByte(image,icon_file.directory[scene].reserved);
1245     (void) WriteBlobLSBShort(image,icon_file.directory[scene].planes);
1246     (void) WriteBlobLSBShort(image,icon_file.directory[scene].bits_per_pixel);
1247     (void) WriteBlobLSBLong(image,(unsigned int)
1248       icon_file.directory[scene].size);
1249     (void) WriteBlobLSBLong(image,(unsigned int)
1250       icon_file.directory[scene].offset);
1251     scene++;
1252     next=SyncNextImageInList(next);
1253   } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
1254   (void) CloseBlob(image);
1255   return(MagickTrue);
1256 }