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