]> granicus.if.org Git - imagemagick/blob - coders/icon.c
IM7 enhance.c: functional macro names the way Cristy apparently likes them
[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 %  It encodes any subimage as a compressed PNG image ("BI_PNG)",
761 %  only when its dimensions are 256x256 and image->compression is
762 %  undefined or is defined as ZipCompression.
763 %
764 %  The format of the WriteICONImage method is:
765 %
766 %      MagickBooleanType WriteICONImage(const ImageInfo *image_info,
767 %        Image *image,ExceptionInfo *exception)
768 %
769 %  A description of each parameter follows.
770 %
771 %    o image_info: the image info.
772 %
773 %    o image:  The image.
774 %
775 %    o exception: return any errors or warnings in this structure.
776 %
777 */
778 static MagickBooleanType WriteICONImage(const ImageInfo *image_info,
779   Image *image,ExceptionInfo *exception)
780 {
781   IconFile
782     icon_file;
783
784   IconInfo
785     icon_info;
786
787   Image
788     *next;
789   
790   MagickBooleanType
791     status;
792
793   MagickOffsetType
794     offset,
795     scene;
796
797   register const Quantum
798     *p;
799
800   register ssize_t
801     i,
802     x;
803
804   register unsigned char
805     *q;
806
807   size_t
808     bytes_per_line,
809     scanline_pad;
810
811   ssize_t
812     y;
813
814   unsigned char
815     bit,
816     byte,
817     *pixels;
818
819   /*
820     Open output image file.
821   */
822   assert(image_info != (const ImageInfo *) NULL);
823   assert(image_info->signature == MagickSignature);
824   assert(image != (Image *) NULL);
825   assert(image->signature == MagickSignature);
826     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"%s",image->filename);
827   assert(exception != (ExceptionInfo *) NULL);
828   assert(exception->signature == MagickSignature);
829   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
830   if (status == MagickFalse)
831     return(status);
832   scene=0;
833   next=image;
834   do
835   {
836     if ((image->columns > 256L) || (image->rows > 256L))
837       ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
838     scene++;
839     next=SyncNextImageInList(next);
840   } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
841   /*
842     Dump out a ICON header template to be properly initialized later.
843   */
844   (void) WriteBlobLSBShort(image,0);
845   (void) WriteBlobLSBShort(image,1);
846   (void) WriteBlobLSBShort(image,(unsigned char) scene);
847   (void) ResetMagickMemory(&icon_file,0,sizeof(icon_file));
848   (void) ResetMagickMemory(&icon_info,0,sizeof(icon_info));
849   scene=0;
850   next=image;
851   do
852   {
853     (void) WriteBlobByte(image,icon_file.directory[scene].width);
854     (void) WriteBlobByte(image,icon_file.directory[scene].height);
855     (void) WriteBlobByte(image,icon_file.directory[scene].colors);
856     (void) WriteBlobByte(image,icon_file.directory[scene].reserved);
857     (void) WriteBlobLSBShort(image,icon_file.directory[scene].planes);
858     (void) WriteBlobLSBShort(image,icon_file.directory[scene].bits_per_pixel);
859     (void) WriteBlobLSBLong(image,(unsigned int)
860       icon_file.directory[scene].size);
861     (void) WriteBlobLSBLong(image,(unsigned int)
862       icon_file.directory[scene].offset);
863     scene++;
864     next=SyncNextImageInList(next);
865   } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
866   scene=0;
867   next=image;
868   do
869   {
870     if ((next->columns > 255L) && (next->rows > 255L) &&
871         ((next->compression == UndefinedCompression) ||
872         (next->compression == ZipCompression)))
873       {
874         Image
875           *write_image;
876
877         ImageInfo
878           *write_info;
879
880         size_t
881           length;
882
883         unsigned char
884           *png;
885
886         write_image=CloneImage(next,0,0,MagickTrue,exception);
887         if (write_image == (Image *) NULL)
888           return(MagickFalse);
889         write_info=CloneImageInfo(image_info);
890         (void) CopyMagickString(write_info->filename,"PNG:",MaxTextExtent);
891
892         /* Don't write any ancillary chunks except for gAMA */
893         (void) SetImageArtifact(write_image,"png:include-chunk","none,gama");
894
895         /* Only write PNG32 formatted PNG (32-bit RGBA), 8 bits per channel */
896         (void) SetImageArtifact(write_image,"png:format","png32");
897
898         png=(unsigned char *) ImageToBlob(write_info,write_image,&length,
899           exception);
900         write_image=DestroyImage(write_image);
901         write_info=DestroyImageInfo(write_info);
902         if (png == (unsigned char *) NULL)
903           return(MagickFalse);
904         icon_file.directory[scene].width=0;
905         icon_file.directory[scene].height=0;
906         icon_file.directory[scene].colors=0;
907         icon_file.directory[scene].reserved=0;
908         icon_file.directory[scene].planes=1;
909         icon_file.directory[scene].bits_per_pixel=32;
910         icon_file.directory[scene].size=(size_t) length;
911         icon_file.directory[scene].offset=(size_t) TellBlob(image);
912         (void) WriteBlob(image,(size_t) length,png);
913         png=(unsigned char *) RelinquishMagickMemory(png);
914       }
915     else
916       {
917         /*
918           Initialize ICON raster file header.
919         */
920         if (IssRGBCompatibleColorspace(next->colorspace) == MagickFalse)
921           (void) TransformImageColorspace(next,sRGBColorspace,exception);
922         icon_info.file_size=14+12+28;
923         icon_info.offset_bits=icon_info.file_size;
924         icon_info.compression=BI_RGB;
925         if ((next->storage_class != DirectClass) && (next->colors > 256))
926           (void) SetImageStorageClass(next,DirectClass,exception);
927         if (next->storage_class == DirectClass)
928           {
929             /*
930               Full color ICON raster.
931             */
932             icon_info.number_colors=0;
933             icon_info.bits_per_pixel=32;
934             icon_info.compression=(size_t) BI_RGB;
935           }
936         else
937           {
938             size_t
939               one;
940
941             /*
942               Colormapped ICON raster.
943             */
944             icon_info.bits_per_pixel=8;
945             if (next->colors <= 256)
946               icon_info.bits_per_pixel=8;
947             if (next->colors <= 16)
948               icon_info.bits_per_pixel=4;
949             if (next->colors <= 2)
950               icon_info.bits_per_pixel=1;
951             one=1;
952             icon_info.number_colors=one << icon_info.bits_per_pixel;
953             if (icon_info.number_colors < next->colors)
954               {
955                 (void) SetImageStorageClass(next,DirectClass,exception);
956                 icon_info.number_colors=0;
957                 icon_info.bits_per_pixel=(unsigned short) 24;
958                 icon_info.compression=(size_t) BI_RGB;
959               }
960             else
961               {
962                 size_t
963                   one;
964
965                 one=1;
966                 icon_info.file_size+=3*(one << icon_info.bits_per_pixel);
967                 icon_info.offset_bits+=3*(one << icon_info.bits_per_pixel);
968                 icon_info.file_size+=(one << icon_info.bits_per_pixel);
969                 icon_info.offset_bits+=(one << icon_info.bits_per_pixel);
970               }
971           }
972         bytes_per_line=(((next->columns*icon_info.bits_per_pixel)+31) &
973           ~31) >> 3;
974         icon_info.ba_offset=0;
975         icon_info.width=(ssize_t) next->columns;
976         icon_info.height=(ssize_t) next->rows;
977         icon_info.planes=1;
978         icon_info.image_size=bytes_per_line*next->rows;
979         icon_info.size=40;
980         icon_info.size+=(4*icon_info.number_colors);
981         icon_info.size+=icon_info.image_size;
982         icon_info.size+=(((icon_info.width+31) & ~31) >> 3)*icon_info.height;
983         icon_info.file_size+=icon_info.image_size;
984         icon_info.x_pixels=0;
985         icon_info.y_pixels=0;
986         switch (next->units)
987         {
988           case UndefinedResolution:
989           case PixelsPerInchResolution:
990           {
991             icon_info.x_pixels=(size_t) (100.0*next->resolution.x/2.54);
992             icon_info.y_pixels=(size_t) (100.0*next->resolution.y/2.54);
993             break;
994           }
995           case PixelsPerCentimeterResolution:
996           {
997             icon_info.x_pixels=(size_t) (100.0*next->resolution.x);
998             icon_info.y_pixels=(size_t) (100.0*next->resolution.y);
999             break;
1000           }
1001         }
1002         icon_info.colors_important=icon_info.number_colors;
1003         /*
1004           Convert MIFF to ICON raster pixels.
1005         */
1006         pixels=(unsigned char *) AcquireQuantumMemory((size_t)
1007           icon_info.image_size,sizeof(*pixels));
1008         if (pixels == (unsigned char *) NULL)
1009           ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1010         (void) ResetMagickMemory(pixels,0,(size_t) icon_info.image_size);
1011         switch (icon_info.bits_per_pixel)
1012         {
1013           case 1:
1014           {
1015             size_t
1016               bit,
1017               byte;
1018
1019             /*
1020               Convert PseudoClass image to a ICON monochrome image.
1021             */
1022             for (y=0; y < (ssize_t) next->rows; y++)
1023             {
1024               p=GetVirtualPixels(next,0,y,next->columns,1,exception);
1025               if (p == (const Quantum *) NULL)
1026                 break;
1027               q=pixels+(next->rows-y-1)*bytes_per_line;
1028               bit=0;
1029               byte=0;
1030               for (x=0; x < (ssize_t) next->columns; x++)
1031               {
1032                 byte<<=1;
1033                 byte|=GetPixelIndex(next,p) != 0 ? 0x01 : 0x00;
1034                 bit++;
1035                 if (bit == 8)
1036                   {
1037                     *q++=(unsigned char) byte;
1038                     bit=0;
1039                     byte=0;
1040                   }
1041                 p+=GetPixelChannels(image);
1042               }
1043               if (bit != 0)
1044                 *q++=(unsigned char) (byte << (8-bit));
1045               if (next->previous == (Image *) NULL)
1046                 {
1047                   status=SetImageProgress(next,SaveImageTag,y,next->rows);
1048                   if (status == MagickFalse)
1049                     break;
1050                 }
1051             }
1052             break;
1053           }
1054           case 4:
1055           {
1056             size_t
1057               nibble,
1058               byte;
1059
1060             /*
1061               Convert PseudoClass image to a ICON monochrome image.
1062             */
1063             for (y=0; y < (ssize_t) next->rows; y++)
1064             {
1065               p=GetVirtualPixels(next,0,y,next->columns,1,exception);
1066               if (p == (const Quantum *) NULL)
1067                 break;
1068               q=pixels+(next->rows-y-1)*bytes_per_line;
1069               nibble=0;
1070               byte=0;
1071               for (x=0; x < (ssize_t) next->columns; x++)
1072               {
1073                 byte<<=4;
1074                 byte|=((size_t) GetPixelIndex(next,p) & 0x0f);
1075                 nibble++;
1076                 if (nibble == 2)
1077                   {
1078                     *q++=(unsigned char) byte;
1079                     nibble=0;
1080                     byte=0;
1081                   }
1082                 p+=GetPixelChannels(image);
1083               }
1084               if (nibble != 0)
1085                 *q++=(unsigned char) (byte << 4);
1086               if (next->previous == (Image *) NULL)
1087                 {
1088                   status=SetImageProgress(next,SaveImageTag,y,next->rows);
1089                   if (status == MagickFalse)
1090                     break;
1091                 }
1092             }
1093             break;
1094           }
1095           case 8:
1096           {
1097             /*
1098               Convert PseudoClass packet to ICON pixel.
1099             */
1100             for (y=0; y < (ssize_t) next->rows; y++)
1101             {
1102               p=GetVirtualPixels(next,0,y,next->columns,1,exception);
1103               if (p == (const Quantum *) NULL)
1104                 break;
1105               q=pixels+(next->rows-y-1)*bytes_per_line;
1106               for (x=0; x < (ssize_t) next->columns; x++)
1107               {
1108                 *q++=(unsigned char) GetPixelIndex(next,p);
1109                 p+=GetPixelChannels(image);
1110               }
1111               if (next->previous == (Image *) NULL)
1112                 {
1113                   status=SetImageProgress(next,SaveImageTag,y,next->rows);
1114                   if (status == MagickFalse)
1115                     break;
1116                 }
1117             }
1118             break;
1119           }
1120           case 24:
1121           case 32:
1122           {
1123             /*
1124               Convert DirectClass packet to ICON BGR888 or BGRA8888 pixel.
1125             */
1126             for (y=0; y < (ssize_t) next->rows; y++)
1127             {
1128               p=GetVirtualPixels(next,0,y,next->columns,1,exception);
1129               if (p == (const Quantum *) NULL)
1130                 break;
1131               q=pixels+(next->rows-y-1)*bytes_per_line;
1132               for (x=0; x < (ssize_t) next->columns; x++)
1133               {
1134                 *q++=ScaleQuantumToChar(GetPixelBlue(next,p));
1135                 *q++=ScaleQuantumToChar(GetPixelGreen(next,p));
1136                 *q++=ScaleQuantumToChar(GetPixelRed(next,p));
1137                 if (next->matte == MagickFalse)
1138                   *q++=ScaleQuantumToChar(QuantumRange);
1139                 else
1140                   *q++=ScaleQuantumToChar(GetPixelAlpha(next,p));
1141                 p+=GetPixelChannels(next);
1142               }
1143               if (icon_info.bits_per_pixel == 24)
1144                 for (x=3L*(ssize_t) next->columns; x < (ssize_t) bytes_per_line; x++)
1145                   *q++=0x00;
1146               if (next->previous == (Image *) NULL)
1147                 {
1148                   status=SetImageProgress(next,SaveImageTag,y,next->rows);
1149                   if (status == MagickFalse)
1150                     break;
1151                 }
1152             }
1153             break;
1154           }
1155         }
1156         /*
1157           Write 40-byte version 3+ bitmap header.
1158         */
1159         icon_file.directory[scene].width=(unsigned char) icon_info.width;
1160         icon_file.directory[scene].height=(unsigned char) icon_info.height;
1161         icon_file.directory[scene].colors=(unsigned char)
1162           icon_info.number_colors;
1163         icon_file.directory[scene].reserved=0;
1164         icon_file.directory[scene].planes=icon_info.planes;
1165         icon_file.directory[scene].bits_per_pixel=icon_info.bits_per_pixel;
1166         icon_file.directory[scene].size=icon_info.size;
1167         icon_file.directory[scene].offset=(size_t) TellBlob(image);
1168         (void) WriteBlobLSBLong(image,(unsigned int) 40);
1169         (void) WriteBlobLSBLong(image,(unsigned int) icon_info.width);
1170         (void) WriteBlobLSBLong(image,(unsigned int) icon_info.height*2);
1171         (void) WriteBlobLSBShort(image,icon_info.planes);
1172         (void) WriteBlobLSBShort(image,icon_info.bits_per_pixel);
1173         (void) WriteBlobLSBLong(image,(unsigned int) icon_info.compression);
1174         (void) WriteBlobLSBLong(image,(unsigned int) icon_info.image_size);
1175         (void) WriteBlobLSBLong(image,(unsigned int) icon_info.x_pixels);
1176         (void) WriteBlobLSBLong(image,(unsigned int) icon_info.y_pixels);
1177         (void) WriteBlobLSBLong(image,(unsigned int) icon_info.number_colors);
1178         (void) WriteBlobLSBLong(image,(unsigned int) icon_info.colors_important);
1179         if (next->storage_class == PseudoClass)
1180           {
1181             unsigned char
1182               *icon_colormap;
1183
1184             /*
1185               Dump colormap to file.
1186             */
1187             icon_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
1188               (1UL << icon_info.bits_per_pixel),4UL*sizeof(*icon_colormap));
1189             if (icon_colormap == (unsigned char *) NULL)
1190               ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1191             q=icon_colormap;
1192             for (i=0; i < (ssize_t) next->colors; i++)
1193             {
1194               *q++=ScaleQuantumToChar(next->colormap[i].blue);
1195               *q++=ScaleQuantumToChar(next->colormap[i].green);
1196               *q++=ScaleQuantumToChar(next->colormap[i].red);
1197               *q++=(unsigned char) 0x0;
1198             }
1199             for ( ; i < (ssize_t) (1UL << icon_info.bits_per_pixel); i++)
1200             {
1201               *q++=(unsigned char) 0x00;
1202               *q++=(unsigned char) 0x00;
1203               *q++=(unsigned char) 0x00;
1204               *q++=(unsigned char) 0x00;
1205             }
1206             (void) WriteBlob(image,(size_t) (4UL*(1UL <<
1207               icon_info.bits_per_pixel)),icon_colormap);
1208             icon_colormap=(unsigned char *) RelinquishMagickMemory(
1209               icon_colormap);
1210           }
1211         (void) WriteBlob(image,(size_t) icon_info.image_size,pixels);
1212         pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1213         /*
1214           Write matte mask.
1215         */
1216         scanline_pad=(((next->columns+31) & ~31)-next->columns) >> 3;
1217         for (y=((ssize_t) next->rows - 1); y >= 0; y--)
1218         {
1219           p=GetVirtualPixels(next,0,y,next->columns,1,exception);
1220           if (p == (const Quantum *) NULL)
1221             break;
1222           bit=0;
1223           byte=0;
1224           for (x=0; x < (ssize_t) next->columns; x++)
1225           {
1226             byte<<=1;
1227             if ((next->matte == MagickTrue) &&
1228                 (GetPixelAlpha(next,p) == (Quantum) TransparentAlpha))
1229               byte|=0x01;
1230             bit++;
1231             if (bit == 8)
1232               {
1233                 (void) WriteBlobByte(image,(unsigned char) byte);
1234                 bit=0;
1235                 byte=0;
1236               }
1237             p+=GetPixelChannels(next);
1238           }
1239           if (bit != 0)
1240             (void) WriteBlobByte(image,(unsigned char) (byte << (8-bit)));
1241           for (i=0; i < (ssize_t) scanline_pad; i++)
1242             (void) WriteBlobByte(image,(unsigned char) 0);
1243         }
1244       }
1245     if (GetNextImageInList(next) == (Image *) NULL)
1246       break;
1247     next=SyncNextImageInList(next);
1248     status=SetImageProgress(next,SaveImagesTag,scene++,
1249       GetImageListLength(next));
1250     if (status == MagickFalse)
1251       break;
1252   } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
1253   offset=SeekBlob(image,0,SEEK_SET);
1254   (void) offset;
1255   (void) WriteBlobLSBShort(image,0);
1256   (void) WriteBlobLSBShort(image,1);
1257   (void) WriteBlobLSBShort(image,(unsigned short) (scene+1));
1258   scene=0;
1259   next=image;
1260   do
1261   {
1262     (void) WriteBlobByte(image,icon_file.directory[scene].width);
1263     (void) WriteBlobByte(image,icon_file.directory[scene].height);
1264     (void) WriteBlobByte(image,icon_file.directory[scene].colors);
1265     (void) WriteBlobByte(image,icon_file.directory[scene].reserved);
1266     (void) WriteBlobLSBShort(image,icon_file.directory[scene].planes);
1267     (void) WriteBlobLSBShort(image,icon_file.directory[scene].bits_per_pixel);
1268     (void) WriteBlobLSBLong(image,(unsigned int)
1269       icon_file.directory[scene].size);
1270     (void) WriteBlobLSBLong(image,(unsigned int)
1271       icon_file.directory[scene].offset);
1272     scene++;
1273     next=SyncNextImageInList(next);
1274   } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
1275   (void) CloseBlob(image);
1276   return(MagickTrue);
1277 }