]> granicus.if.org Git - imagemagick/blob - coders/icon.c
db7b5ce2b48481df3698be19c16382c323fdf6aa
[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-2010 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         if ((long) image->columns > icon_info.width)
318           image->columns=(unsigned long) icon_info.width;
319         image->rows=(unsigned long) icon_file.directory[i].height;
320         if ((long) image->rows > icon_info.height)
321           image->rows=(unsigned long) icon_info.height;
322         image->depth=8;
323         if (image->debug != MagickFalse)
324           {
325             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
326               " scene    = %ld",i);
327             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
328               "   size   = %ld",icon_info.size);
329             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
330               "   width  = %d",icon_file.directory[i].width);
331             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
332               "   height = %d",icon_file.directory[i].height);
333             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
334               "   colors = %ld",icon_info.number_colors);
335             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
336               "   planes = %d",icon_info.planes);
337             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
338               "   bpp    = %d",icon_info.bits_per_pixel);
339           }
340       if ((icon_info.number_colors != 0) || (icon_info.bits_per_pixel <= 16))
341         {
342           image->storage_class=PseudoClass;
343           image->colors=icon_info.number_colors;
344           if (image->colors == 0)
345             image->colors=1 << icon_info.bits_per_pixel;
346         }
347       if (image->storage_class == PseudoClass)
348         {
349           register long
350             i;
351
352           unsigned char
353             *icon_colormap;
354
355           unsigned long
356             number_colors;
357
358           /*
359             Read Icon raster colormap.
360           */
361           number_colors=1UL << icon_info.bits_per_pixel;
362           if (AcquireImageColormap(image,number_colors) == MagickFalse)
363             ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
364           icon_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
365             image->colors,4UL*sizeof(*icon_colormap));
366           if (icon_colormap == (unsigned char *) NULL)
367             ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
368           count=ReadBlob(image,(size_t) (4*image->colors),icon_colormap);
369           if (count != (ssize_t) (4*image->colors))
370             ThrowReaderException(CorruptImageError,
371               "InsufficientImageDataInFile");
372           p=icon_colormap;
373           for (i=0; i < (long) image->colors; i++)
374           {
375             image->colormap[i].blue=(Quantum) ScaleCharToQuantum(*p++);
376             image->colormap[i].green=(Quantum) ScaleCharToQuantum(*p++);
377             image->colormap[i].red=(Quantum) ScaleCharToQuantum(*p++);
378             p++;
379           }
380           icon_colormap=(unsigned char *) RelinquishMagickMemory(icon_colormap);
381         }
382         /*
383           Convert Icon raster image to pixel packets.
384         */
385         if ((image_info->ping != MagickFalse) &&
386             (image_info->number_scenes != 0))
387           if (image->scene >= (image_info->scene+image_info->number_scenes-1))
388             break;
389         bytes_per_line=(((image->columns*icon_info.bits_per_pixel)+31) &
390           ~31) >> 3;
391         scanline_pad=((((image->columns*icon_info.bits_per_pixel)+31) & ~31)-
392           (image->columns*icon_info.bits_per_pixel)) >> 3;
393         switch (icon_info.bits_per_pixel)
394         {
395           case 1:
396           {
397             /*
398               Convert bitmap scanline.
399             */
400             for (y=(long) image->rows-1; y >= 0; y--)
401             {
402               q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
403               if (q == (PixelPacket *) NULL)
404                 break;
405               indexes=GetAuthenticIndexQueue(image);
406               for (x=0; x < (long) (image->columns-7); x+=8)
407               {
408                 byte=(unsigned long) ReadBlobByte(image);
409                 for (bit=0; bit < 8; bit++)
410                   indexes[x+bit]=(IndexPacket)
411                     ((byte & (0x80 >> bit)) != 0 ? 0x01 : 0x00);
412               }
413               if ((image->columns % 8) != 0)
414                 {
415                   byte=(unsigned long) ReadBlobByte(image);
416                   for (bit=0; bit < (image->columns % 8); bit++)
417                     indexes[x+bit]=(IndexPacket)
418                       ((byte & (0x80 >> bit)) != 0 ? 0x01 : 0x00);
419                 }
420               for (x=0; x < (long) scanline_pad; x++)
421                 (void) ReadBlobByte(image);
422               if (SyncAuthenticPixels(image,exception) == MagickFalse)
423                 break;
424               if (image->previous == (Image *) NULL)
425                 {
426                   status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
427                     image->rows);
428                   if (status == MagickFalse)
429                     break;
430                 }
431             }
432             break;
433           }
434           case 4:
435           {
436             /*
437               Read 4-bit Icon scanline.
438             */
439             for (y=(long) image->rows-1; y >= 0; y--)
440             {
441               q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
442               if (q == (PixelPacket *) NULL)
443                 break;
444               indexes=GetAuthenticIndexQueue(image);
445               for (x=0; x < ((long) image->columns-1); x+=2)
446               {
447                 byte=(unsigned long) ReadBlobByte(image);
448                 indexes[x]=(IndexPacket) ((byte >> 4) & 0xf);
449                 indexes[x+1]=(IndexPacket) ((byte) & 0xf);
450               }
451               if ((image->columns % 2) != 0)
452                 {
453                   byte=(unsigned long) ReadBlobByte(image);
454                   indexes[x]=(IndexPacket) ((byte >> 4) & 0xf);
455                 }
456               for (x=0; x < (long) scanline_pad; x++)
457                 (void) ReadBlobByte(image);
458               if (SyncAuthenticPixels(image,exception) == MagickFalse)
459                 break;
460               if (image->previous == (Image *) NULL)
461                 {
462                   status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
463                     image->rows);
464                   if (status == MagickFalse)
465                     break;
466                 }
467             }
468             break;
469           }
470           case 8:
471           {
472             /*
473               Convert PseudoColor scanline.
474             */
475             for (y=(long) image->rows-1; y >= 0; y--)
476             {
477               q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
478               if (q == (PixelPacket *) NULL)
479                 break;
480               indexes=GetAuthenticIndexQueue(image);
481               for (x=0; x < (long) image->columns; x++)
482               {
483                 byte=(unsigned long) ReadBlobByte(image);
484                 indexes[x]=(IndexPacket) byte;
485               }
486               for (x=0; x < (long) scanline_pad; x++)
487                 (void) ReadBlobByte(image);
488               if (SyncAuthenticPixels(image,exception) == MagickFalse)
489                 break;
490               if (image->previous == (Image *) NULL)
491                 {
492                   status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
493                     image->rows);
494                   if (status == MagickFalse)
495                     break;
496                 }
497             }
498             break;
499           }
500           case 16:
501           {
502             /*
503               Convert PseudoColor scanline.
504             */
505             for (y=(long) image->rows-1; y >= 0; y--)
506             {
507               q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
508               if (q == (PixelPacket *) NULL)
509                 break;
510               indexes=GetAuthenticIndexQueue(image);
511               for (x=0; x < (long) image->columns; x++)
512               {
513                 byte=(unsigned long) ReadBlobByte(image);
514                 byte|=(unsigned long) (ReadBlobByte(image) << 8);
515                 indexes[x]=(IndexPacket) byte;
516               }
517               for (x=0; x < (long) scanline_pad; x++)
518                 (void) ReadBlobByte(image);
519               if (SyncAuthenticPixels(image,exception) == MagickFalse)
520                 break;
521               if (image->previous == (Image *) NULL)
522                 {
523                   status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
524                     image->rows);
525                   if (status == MagickFalse)
526                     break;
527                 }
528             }
529             break;
530           }
531           case 24:
532           case 32:
533           {
534             /*
535               Convert DirectColor scanline.
536             */
537             for (y=(long) image->rows-1; y >= 0; y--)
538             {
539               q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
540               if (q == (PixelPacket *) NULL)
541                 break;
542               for (x=0; x < (long) image->columns; x++)
543               {
544                 q->blue=ScaleCharToQuantum((unsigned char) ReadBlobByte(image));
545                 q->green=ScaleCharToQuantum((unsigned char)
546                   ReadBlobByte(image));
547                 q->red=ScaleCharToQuantum((unsigned char) ReadBlobByte(image));
548                 if (icon_info.bits_per_pixel == 32)
549                   q->opacity=(Quantum) (QuantumRange-ScaleCharToQuantum(
550                     (unsigned char) ReadBlobByte(image)));
551                 q++;
552               }
553               if (icon_info.bits_per_pixel == 24)
554                 for (x=0; x < (long) scanline_pad; x++)
555                   (void) ReadBlobByte(image);
556               if (SyncAuthenticPixels(image,exception) == MagickFalse)
557                 break;
558               if (image->previous == (Image *) NULL)
559                 {
560                   status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
561                     image->rows);
562                   if (status == MagickFalse)
563                     break;
564                 }
565             }
566             break;
567           }
568           default:
569             ThrowReaderException(CorruptImageError,"ImproperImageHeader");
570         }
571         (void) SyncImage(image);
572         if (icon_info.bits_per_pixel != 32)
573           {
574             /*
575               Read the ICON alpha mask.
576             */
577             image->storage_class=DirectClass;
578             for (y=(long) image->rows-1; y >= 0; y--)
579             {
580               q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
581               if (q == (PixelPacket *) NULL)
582                 break;
583               for (x=0; x < ((long) image->columns-7); x+=8)
584               {
585                 byte=(unsigned long) ReadBlobByte(image);
586                 for (bit=0; bit < 8; bit++)
587                   q[x+bit].opacity=(Quantum) (((byte & (0x80 >> bit)) != 0) ?
588                     TransparentOpacity : OpaqueOpacity);
589               }
590               if ((image->columns % 8) != 0)
591                 {
592                   byte=(unsigned long) ReadBlobByte(image);
593                   for (bit=0; bit < (image->columns % 8); bit++)
594                     q[x+bit].opacity=(Quantum) (((byte & (0x80 >> bit)) != 0) ?
595                       TransparentOpacity : OpaqueOpacity);
596                 }
597               if ((image->columns % 32) != 0)
598                 for (x=0; x < (long) ((32-(image->columns % 32))/8); x++)
599                   (void) ReadBlobByte(image);
600               if (SyncAuthenticPixels(image,exception) == MagickFalse)
601                 break;
602             }
603           }
604         if (EOFBlob(image) != MagickFalse)
605           {
606             ThrowFileException(exception,CorruptImageError,
607               "UnexpectedEndOfFile",image->filename);
608             break;
609           }
610       }
611     /*
612       Proceed to next image.
613     */
614     if (image_info->number_scenes != 0)
615       if (image->scene >= (image_info->scene+image_info->number_scenes-1))
616         break;
617     if (i < (long) (icon_file.count-1))
618       {
619         /*
620           Allocate next image structure.
621         */
622         AcquireNextImage(image_info,image);
623         if (GetNextImageInList(image) == (Image *) NULL)
624           {
625             image=DestroyImageList(image);
626             return((Image *) NULL);
627           }
628         image=SyncNextImageInList(image);
629         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
630           GetBlobSize(image));
631         if (status == MagickFalse)
632           break;
633       }
634   }
635   (void) CloseBlob(image);
636   return(GetFirstImageInList(image));
637 }
638 \f
639 /*
640 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
641 %                                                                             %
642 %                                                                             %
643 %                                                                             %
644 %   R e g i s t e r I C O N I m a g e                                         %
645 %                                                                             %
646 %                                                                             %
647 %                                                                             %
648 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
649 %
650 %  RegisterICONImage() adds attributes for the Icon image format to
651 %  the list of supported formats.  The attributes include the image format
652 %  tag, a method to read and/or write the format, whether the format
653 %  supports the saving of more than one frame to the same file or blob,
654 %  whether the format supports native in-memory I/O, and a brief
655 %  description of the format.
656 %
657 %  The format of the RegisterICONImage method is:
658 %
659 %      unsigned long RegisterICONImage(void)
660 %
661 */
662 ModuleExport unsigned long RegisterICONImage(void)
663 {
664   MagickInfo
665     *entry;
666
667   entry=SetMagickInfo("CUR");
668   entry->decoder=(DecodeImageHandler *) ReadICONImage;
669   entry->encoder=(EncodeImageHandler *) WriteICONImage;
670   entry->adjoin=MagickFalse;
671   entry->seekable_stream=MagickTrue;
672   entry->description=ConstantString("Microsoft icon");
673   entry->module=ConstantString("CUR");
674   (void) RegisterMagickInfo(entry);
675   entry=SetMagickInfo("ICO");
676   entry->decoder=(DecodeImageHandler *) ReadICONImage;
677   entry->encoder=(EncodeImageHandler *) WriteICONImage;
678   entry->adjoin=MagickTrue;
679   entry->seekable_stream=MagickTrue;
680   entry->description=ConstantString("Microsoft icon");
681   entry->module=ConstantString("ICON");
682   (void) RegisterMagickInfo(entry);
683   entry=SetMagickInfo("ICON");
684   entry->decoder=(DecodeImageHandler *) ReadICONImage;
685   entry->encoder=(EncodeImageHandler *) WriteICONImage;
686   entry->adjoin=MagickFalse;
687   entry->seekable_stream=MagickTrue;
688   entry->description=ConstantString("Microsoft icon");
689   entry->module=ConstantString("ICON");
690   (void) RegisterMagickInfo(entry);
691   return(MagickImageCoderSignature);
692 }
693 \f
694 /*
695 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
696 %                                                                             %
697 %                                                                             %
698 %                                                                             %
699 %   U n r e g i s t e r I C O N I m a g e                                     %
700 %                                                                             %
701 %                                                                             %
702 %                                                                             %
703 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
704 %
705 %  UnregisterICONImage() removes format registrations made by the
706 %  ICON module from the list of supported formats.
707 %
708 %  The format of the UnregisterICONImage method is:
709 %
710 %      UnregisterICONImage(void)
711 %
712 */
713 ModuleExport void UnregisterICONImage(void)
714 {
715   (void) UnregisterMagickInfo("CUR");
716   (void) UnregisterMagickInfo("ICO");
717   (void) UnregisterMagickInfo("ICON");
718 }
719 \f
720 /*
721 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
722 %                                                                             %
723 %                                                                             %
724 %                                                                             %
725 %   W r i t e I C O N I m a g e                                               %
726 %                                                                             %
727 %                                                                             %
728 %                                                                             %
729 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
730 %
731 %  WriteICONImage() writes an image in Microsoft Windows bitmap encoded
732 %  image format, version 3 for Windows or (if the image has a matte channel)
733 %  version 4.
734 %
735 %  The format of the WriteICONImage method is:
736 %
737 %      MagickBooleanType WriteICONImage(const ImageInfo *image_info,
738 %        Image *image)
739 %
740 %  A description of each parameter follows.
741 %
742 %    o image_info: the image info.
743 %
744 %    o image:  The image.
745 %
746 */
747 static MagickBooleanType WriteICONImage(const ImageInfo *image_info,
748   Image *image)
749 {
750   IconFile
751     icon_file;
752
753   IconInfo
754     icon_info;
755
756   Image
757     *next;
758   
759   long
760     y;
761
762   MagickBooleanType
763     status;
764
765   MagickOffsetType
766     offset,
767     scene;
768
769   register const IndexPacket
770     *indexes;
771
772   register const PixelPacket
773     *p;
774
775   register long
776     i,
777     x;
778
779   register unsigned char
780     *q;
781
782   unsigned char
783     bit,
784     byte,
785     *pixels;
786
787   unsigned long
788     bytes_per_line,
789     scanline_pad;
790
791   /*
792     Open output image file.
793   */
794   assert(image_info != (const ImageInfo *) NULL);
795   assert(image_info->signature == MagickSignature);
796   assert(image != (Image *) NULL);
797   assert(image->signature == MagickSignature);
798     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"%s",image->filename);
799   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
800   if (status == MagickFalse)
801     return(status);
802   scene=0;
803   next=image;
804   do
805   {
806     if ((image->columns > 256L) || (image->rows > 256L))
807       ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
808     scene++;
809     next=SyncNextImageInList(next);
810   } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
811   /*
812     Dump out a ICON header template to be properly initialized later.
813   */
814   (void) WriteBlobLSBShort(image,0);
815   (void) WriteBlobLSBShort(image,1);
816   (void) WriteBlobLSBShort(image,(unsigned char) scene);
817   (void) ResetMagickMemory(&icon_file,0,sizeof(icon_file));
818   (void) ResetMagickMemory(&icon_info,0,sizeof(icon_info));
819   scene=0;
820   next=image;
821   do
822   {
823     (void) WriteBlobByte(image,icon_file.directory[scene].width);
824     (void) WriteBlobByte(image,icon_file.directory[scene].height);
825     (void) WriteBlobByte(image,icon_file.directory[scene].colors);
826     (void) WriteBlobByte(image,icon_file.directory[scene].reserved);
827     (void) WriteBlobLSBShort(image,icon_file.directory[scene].planes);
828     (void) WriteBlobLSBShort(image,icon_file.directory[scene].bits_per_pixel);
829     (void) WriteBlobLSBLong(image,icon_file.directory[scene].size);
830     (void) WriteBlobLSBLong(image,icon_file.directory[scene].offset);
831     scene++;
832     next=SyncNextImageInList(next);
833   } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
834   scene=0;
835   next=image;
836   do
837   {
838     if ((next->columns == 256) && (next->rows == 256))
839       {
840         Image
841           *write_image;
842
843         ImageInfo
844           *write_info;
845
846         size_t
847           length;
848
849         unsigned char
850           *png;
851
852         /*
853           Icon image encoded as a compressed PNG image.
854         */
855         write_image=CloneImage(next,0,0,MagickTrue,&image->exception);
856         if (write_image == (Image *) NULL)
857           return(MagickFalse);
858         write_info=CloneImageInfo(image_info);
859         (void) CopyMagickString(write_info->filename,"PNG:",MaxTextExtent);
860         png=(unsigned char *) ImageToBlob(write_info,write_image,&length,
861           &image->exception);
862         write_image=DestroyImage(write_image);
863         write_info=DestroyImageInfo(write_info);
864         if (png == (unsigned char *) NULL)
865           return(MagickFalse);
866         icon_file.directory[scene].width=0;
867         icon_file.directory[scene].height=0;
868         icon_file.directory[scene].colors=0;
869         icon_file.directory[scene].reserved=0;
870         icon_file.directory[scene].planes=1;
871         icon_file.directory[scene].bits_per_pixel=32;
872         icon_file.directory[scene].size=(unsigned long) length;
873         icon_file.directory[scene].offset=(unsigned long) TellBlob(image);
874         (void) WriteBlob(image,(size_t) length,png);
875         png=(unsigned char *) RelinquishMagickMemory(png);
876       }
877     else
878       {
879         /*
880           Initialize ICON raster file header.
881         */
882         if (next->colorspace != RGBColorspace)
883           (void) TransformImageColorspace(next,RGBColorspace);
884         icon_info.file_size=14+12+28;
885         icon_info.offset_bits=icon_info.file_size;
886         icon_info.compression=BI_RGB;
887         if ((next->storage_class != DirectClass) && (next->colors > 256))
888           (void) SetImageStorageClass(next,DirectClass);
889         if (next->storage_class == DirectClass)
890           {
891             /*
892               Full color ICON raster.
893             */
894             icon_info.number_colors=0;
895             icon_info.bits_per_pixel=32;
896             icon_info.compression=(unsigned long) BI_RGB;
897           }
898         else
899           {
900             /*
901               Colormapped ICON raster.
902             */
903             icon_info.bits_per_pixel=8;
904             if (next->colors <= 256)
905               icon_info.bits_per_pixel=8;
906             if (next->colors <= 16)
907               icon_info.bits_per_pixel=4;
908             if (next->colors <= 2)
909               icon_info.bits_per_pixel=1;
910             icon_info.number_colors=1 << icon_info.bits_per_pixel;
911             if (icon_info.number_colors < next->colors)
912               {
913                 (void) SetImageStorageClass(next,DirectClass);
914                 icon_info.number_colors=0;
915                 icon_info.bits_per_pixel=(unsigned short) 24;
916                 icon_info.compression=(unsigned long) BI_RGB;
917               }
918             else
919               {
920                 icon_info.file_size+=3*(1UL << icon_info.bits_per_pixel);
921                 icon_info.offset_bits+=3*(1UL << icon_info.bits_per_pixel);
922                 icon_info.file_size+=(1UL << icon_info.bits_per_pixel);
923                 icon_info.offset_bits+=(1UL << icon_info.bits_per_pixel);
924               }
925           }
926         bytes_per_line=(((next->columns*icon_info.bits_per_pixel)+31) &
927           ~31) >> 3;
928         icon_info.ba_offset=0;
929         icon_info.width=(long) next->columns;
930         icon_info.height=(long) next->rows;
931         icon_info.planes=1;
932         icon_info.image_size=bytes_per_line*next->rows;
933         icon_info.size=40;
934         icon_info.size+=(4*icon_info.number_colors);
935         icon_info.size+=icon_info.image_size;
936         icon_info.size+=(((icon_info.width+31) & ~31) >> 3)*icon_info.height;
937         icon_info.file_size+=icon_info.image_size;
938         icon_info.x_pixels=0;
939         icon_info.y_pixels=0;
940         switch (next->units)
941         {
942           case UndefinedResolution:
943           case PixelsPerInchResolution:
944           {
945             icon_info.x_pixels=(unsigned long) (100.0*next->x_resolution/2.54);
946             icon_info.y_pixels=(unsigned long) (100.0*next->y_resolution/2.54);
947             break;
948           }
949           case PixelsPerCentimeterResolution:
950           {
951             icon_info.x_pixels=(unsigned long) (100.0*next->x_resolution);
952             icon_info.y_pixels=(unsigned long) (100.0*next->y_resolution);
953             break;
954           }
955         }
956         icon_info.colors_important=icon_info.number_colors;
957         /*
958           Convert MIFF to ICON raster pixels.
959         */
960         pixels=(unsigned char *) AcquireQuantumMemory((size_t)
961           icon_info.image_size,sizeof(*pixels));
962         if (pixels == (unsigned char *) NULL)
963           ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
964         (void) ResetMagickMemory(pixels,0,(size_t) icon_info.image_size);
965         switch (icon_info.bits_per_pixel)
966         {
967           case 1:
968           {
969             unsigned long
970               bit,
971               byte;
972
973             /*
974               Convert PseudoClass image to a ICON monochrome image.
975             */
976             for (y=0; y < (long) next->rows; y++)
977             {
978               p=GetVirtualPixels(next,0,y,next->columns,1,&next->exception);
979               if (p == (const PixelPacket *) NULL)
980                 break;
981               indexes=GetVirtualIndexQueue(next);
982               q=pixels+(next->rows-y-1)*bytes_per_line;
983               bit=0;
984               byte=0;
985               for (x=0; x < (long) next->columns; x++)
986               {
987                 byte<<=1;
988                 byte|=indexes[x] != 0 ? 0x01 : 0x00;
989                 bit++;
990                 if (bit == 8)
991                   {
992                     *q++=(unsigned char) byte;
993                     bit=0;
994                     byte=0;
995                   }
996                }
997               if (bit != 0)
998                 *q++=(unsigned char) (byte << (8-bit));
999               if (next->previous == (Image *) NULL)
1000                 {
1001                   status=SetImageProgress(next,SaveImageTag,y,next->rows);
1002                   if (status == MagickFalse)
1003                     break;
1004                 }
1005             }
1006             break;
1007           }
1008           case 4:
1009           {
1010             unsigned long
1011               nibble,
1012               byte;
1013
1014             /*
1015               Convert PseudoClass image to a ICON monochrome image.
1016             */
1017             for (y=0; y < (long) next->rows; y++)
1018             {
1019               p=GetVirtualPixels(next,0,y,next->columns,1,&next->exception);
1020               if (p == (const PixelPacket *) NULL)
1021                 break;
1022               indexes=GetVirtualIndexQueue(next);
1023               q=pixels+(next->rows-y-1)*bytes_per_line;
1024               nibble=0;
1025               byte=0;
1026               for (x=0; x < (long) next->columns; x++)
1027               {
1028                 byte<<=4;
1029                 byte|=((unsigned long) indexes[x] & 0x0f);
1030                 nibble++;
1031                 if (nibble == 2)
1032                   {
1033                     *q++=(unsigned char) byte;
1034                     nibble=0;
1035                     byte=0;
1036                   }
1037                }
1038               if (nibble != 0)
1039                 *q++=(unsigned char) (byte << 4);
1040               if (next->previous == (Image *) NULL)
1041                 {
1042                   status=SetImageProgress(next,SaveImageTag,y,next->rows);
1043                   if (status == MagickFalse)
1044                     break;
1045                 }
1046             }
1047             break;
1048           }
1049           case 8:
1050           {
1051             /*
1052               Convert PseudoClass packet to ICON pixel.
1053             */
1054             for (y=0; y < (long) next->rows; y++)
1055             {
1056               p=GetVirtualPixels(next,0,y,next->columns,1,&next->exception);
1057               if (p == (const PixelPacket *) NULL)
1058                 break;
1059               indexes=GetVirtualIndexQueue(next);
1060               q=pixels+(next->rows-y-1)*bytes_per_line;
1061               for (x=0; x < (long) next->columns; x++)
1062                 *q++=(unsigned char) indexes[x];
1063               if (next->previous == (Image *) NULL)
1064                 {
1065                   status=SetImageProgress(next,SaveImageTag,y,next->rows);
1066                   if (status == MagickFalse)
1067                     break;
1068                 }
1069             }
1070             break;
1071           }
1072           case 24:
1073           case 32:
1074           {
1075             /*
1076               Convert DirectClass packet to ICON BGR888 or BGRA8888 pixel.
1077             */
1078             for (y=0; y < (long) next->rows; y++)
1079             {
1080               p=GetVirtualPixels(next,0,y,next->columns,1,&next->exception);
1081               if (p == (const PixelPacket *) NULL)
1082                 break;
1083               q=pixels+(next->rows-y-1)*bytes_per_line;
1084               for (x=0; x < (long) next->columns; x++)
1085               {
1086                 *q++=ScaleQuantumToChar(GetBluePixelComponent(p));
1087                 *q++=ScaleQuantumToChar(GetGreenPixelComponent(p));
1088                 *q++=ScaleQuantumToChar(GetRedPixelComponent(p));
1089                 if (next->matte == MagickFalse)
1090                   *q++=ScaleQuantumToChar(QuantumRange);
1091                 else
1092                   *q++=ScaleQuantumToChar(GetAlphaPixelComponent(p));
1093                 p++;
1094               }
1095               if (icon_info.bits_per_pixel == 24)
1096                 for (x=3L*(long) next->columns; x < (long) bytes_per_line; x++)
1097                   *q++=0x00;
1098               if (next->previous == (Image *) NULL)
1099                 {
1100                   status=SetImageProgress(next,SaveImageTag,y,next->rows);
1101                   if (status == MagickFalse)
1102                     break;
1103                 }
1104             }
1105             break;
1106           }
1107         }
1108         /*
1109           Write 40-byte version 3+ bitmap header.
1110         */
1111         icon_file.directory[scene].width=(unsigned char) icon_info.width;
1112         icon_file.directory[scene].height=(unsigned char) icon_info.height;
1113         icon_file.directory[scene].colors=(unsigned char)
1114           icon_info.number_colors;
1115         icon_file.directory[scene].reserved=0;
1116         icon_file.directory[scene].planes=icon_info.planes;
1117         icon_file.directory[scene].bits_per_pixel=icon_info.bits_per_pixel;
1118         icon_file.directory[scene].size=icon_info.size;
1119         icon_file.directory[scene].offset=(unsigned long) TellBlob(image);
1120         (void) WriteBlobLSBLong(image,(unsigned long) 40);
1121         (void) WriteBlobLSBLong(image,(unsigned long) icon_info.width);
1122         (void) WriteBlobLSBLong(image,(unsigned long) icon_info.height*2);
1123         (void) WriteBlobLSBShort(image,icon_info.planes);
1124         (void) WriteBlobLSBShort(image,icon_info.bits_per_pixel);
1125         (void) WriteBlobLSBLong(image,icon_info.compression);
1126         (void) WriteBlobLSBLong(image,icon_info.image_size);
1127         (void) WriteBlobLSBLong(image,icon_info.x_pixels);
1128         (void) WriteBlobLSBLong(image,icon_info.y_pixels);
1129         (void) WriteBlobLSBLong(image,icon_info.number_colors);
1130         (void) WriteBlobLSBLong(image,icon_info.colors_important);
1131         if (next->storage_class == PseudoClass)
1132           {
1133             unsigned char
1134               *icon_colormap;
1135
1136             /*
1137               Dump colormap to file.
1138             */
1139             icon_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
1140               (1UL << icon_info.bits_per_pixel),4UL*sizeof(*icon_colormap));
1141             if (icon_colormap == (unsigned char *) NULL)
1142               ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1143             q=icon_colormap;
1144             for (i=0; i < (long) next->colors; i++)
1145             {
1146               *q++=ScaleQuantumToChar(next->colormap[i].blue);
1147               *q++=ScaleQuantumToChar(next->colormap[i].green);
1148               *q++=ScaleQuantumToChar(next->colormap[i].red);
1149               *q++=(unsigned char) 0x0;
1150             }
1151             for ( ; i < (long) (1UL << icon_info.bits_per_pixel); i++)
1152             {
1153               *q++=(unsigned char) 0x00;
1154               *q++=(unsigned char) 0x00;
1155               *q++=(unsigned char) 0x00;
1156               *q++=(unsigned char) 0x00;
1157             }
1158             (void) WriteBlob(image,(size_t) (4UL*(1UL <<
1159               icon_info.bits_per_pixel)),icon_colormap);
1160             icon_colormap=(unsigned char *) RelinquishMagickMemory(
1161               icon_colormap);
1162           }
1163         (void) WriteBlob(image,(size_t) icon_info.image_size,pixels);
1164         pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1165         /*
1166           Write matte mask.
1167         */
1168         scanline_pad=(((next->columns+31) & ~31)-next->columns) >> 3;
1169         for (y=((long) next->rows - 1); y >= 0; y--)
1170         {
1171           p=GetVirtualPixels(next,0,y,next->columns,1,&next->exception);
1172           if (p == (const PixelPacket *) NULL)
1173             break;
1174           bit=0;
1175           byte=0;
1176           for (x=0; x < (long) next->columns; x++)
1177           {
1178             byte<<=1;
1179             if ((next->matte == MagickTrue) &&
1180                 (p->opacity == (Quantum) TransparentOpacity))
1181               byte|=0x01;
1182             bit++;
1183             if (bit == 8)
1184               {
1185                 (void) WriteBlobByte(image,(unsigned char) byte);
1186                 bit=0;
1187                 byte=0;
1188               }
1189             p++;
1190           }
1191           if (bit != 0)
1192             (void) WriteBlobByte(image,(unsigned char) (byte << (8-bit)));
1193           for (i=0; i < (long) scanline_pad; i++)
1194             (void) WriteBlobByte(image,(unsigned char) 0);
1195         }
1196       }
1197     if (GetNextImageInList(next) == (Image *) NULL)
1198       break;
1199     next=SyncNextImageInList(next);
1200     status=SetImageProgress(next,SaveImagesTag,scene++,
1201       GetImageListLength(next));
1202     if (status == MagickFalse)
1203       break;
1204   } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
1205   offset=SeekBlob(image,0,SEEK_SET);
1206   (void) WriteBlobLSBShort(image,0);
1207   (void) WriteBlobLSBShort(image,1);
1208   (void) WriteBlobLSBShort(image,(unsigned short) (scene+1));
1209   scene=0;
1210   next=image;
1211   do
1212   {
1213     (void) WriteBlobByte(image,icon_file.directory[scene].width);
1214     (void) WriteBlobByte(image,icon_file.directory[scene].height);
1215     (void) WriteBlobByte(image,icon_file.directory[scene].colors);
1216     (void) WriteBlobByte(image,icon_file.directory[scene].reserved);
1217     (void) WriteBlobLSBShort(image,icon_file.directory[scene].planes);
1218     (void) WriteBlobLSBShort(image,icon_file.directory[scene].bits_per_pixel);
1219     (void) WriteBlobLSBLong(image,icon_file.directory[scene].size);
1220     (void) WriteBlobLSBLong(image,icon_file.directory[scene].offset);
1221     scene++;
1222     next=SyncNextImageInList(next);
1223   } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
1224   (void) CloseBlob(image);
1225   return(MagickTrue);
1226 }