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