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