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