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