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