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