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