icon.c revision acd2ed254c18c254a0ab5aafa06d1645e5d079d8
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
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
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
76/*
77  Typedef declarations.
78*/
79typedef 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
96typedef struct _IconFile
97{
98  short
99    reserved,
100    resource_type,
101    count;
102
103  IconEntry
104    directory[MaxIcons];
105} IconFile;
106
107typedef 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
139/*
140  Forward declaractions.
141*/
142static MagickBooleanType
143  WriteICONImage(const ImageInfo *,Image *,ExceptionInfo *);
144
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*/
172static 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);
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) == 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);
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
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*/
678ModuleExport 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
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*/
729ModuleExport void UnregisterICONImage(void)
730{
731  (void) UnregisterMagickInfo("CUR");
732  (void) UnregisterMagickInfo("ICO");
733  (void) UnregisterMagickInfo("ICON");
734}
735
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*/
765static 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}
1259