root / ImageMagick / trunk / coders / icon.c

Revision 12035, 37.4 kB (checked in by cristy, 2 weeks ago)
Line 
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-2008 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 "magick/studio.h"
43#include "magick/blob.h"
44#include "magick/blob-private.h"
45#include "magick/colorspace.h"
46#include "magick/exception.h"
47#include "magick/exception-private.h"
48#include "magick/image.h"
49#include "magick/image-private.h"
50#include "magick/list.h"
51#include "magick/log.h"
52#include "magick/magick.h"
53#include "magick/memory_.h"
54#include "magick/monitor.h"
55#include "magick/monitor-private.h"
56#include "magick/nt-feature.h"
57#include "magick/quantize.h"
58#include "magick/quantum-private.h"
59#include "magick/static.h"
60#include "magick/string_.h"
61#include "magick/module.h"
62
63/*
64  Define declarations.
65*/
66#if !defined(__WINDOWS__) || defined(__MINGW32__)
67#define BI_RGB  0
68#define BI_RLE8  1
69#define BI_BITFIELDS  3
70#endif
71#define MaxIcons  1024
72
73/*
74  Typedef declarations.
75*/
76typedef struct _IconEntry
77{
78  unsigned char
79    width,
80    height,
81    colors,
82    reserved;
83
84  unsigned short int
85    planes,
86    bits_per_pixel;
87
88  unsigned long
89    size,
90    offset;
91} IconEntry;
92
93typedef struct _IconFile
94{
95  short
96    reserved,
97    resource_type,
98    count;
99
100  IconEntry
101    directory[MaxIcons];
102} IconFile;
103
104typedef struct _IconInfo
105{
106  unsigned long
107    file_size,
108    ba_offset,
109    offset_bits,
110    size;
111
112  long
113    width,
114    height;
115
116  unsigned short
117    planes,
118    bits_per_pixel;
119
120  unsigned long
121    compression,
122    image_size,
123    x_pixels,
124    y_pixels,
125    number_colors,
126    red_mask,
127    green_mask,
128    blue_mask,
129    alpha_mask,
130    colors_important;
131
132  long
133    colorspace;
134} IconInfo;
135
136/*
137  Forward declaractions.
138*/
139static MagickBooleanType
140  WriteICONImage(const ImageInfo *,Image *);
141
142/*
143%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
144%                                                                             %
145%                                                                             %
146%                                                                             %
147%   R e a d I C O N I m a g e                                                 %
148%                                                                             %
149%                                                                             %
150%                                                                             %
151%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
152%
153%  ReadICONImage() reads a Microsoft icon image file and returns it.  It
154%  allocates the memory necessary for the new Image structure and returns a
155%  pointer to the new image.
156%
157%  The format of the ReadICONImage method is:
158%
159%      Image *ReadICONImage(const ImageInfo *image_info,
160%        ExceptionInfo *exception)
161%
162%  A description of each parameter follows:
163%
164%    o image_info: the image info.
165%
166%    o exception: return any errors or warnings in this structure.
167%
168*/
169static Image *ReadICONImage(const ImageInfo *image_info,
170  ExceptionInfo *exception)
171{
172  IconFile
173    icon_file;
174
175  IconInfo
176    icon_info;
177
178  Image
179    *image;
180
181  long
182    y;
183
184  MagickBooleanType
185    status;
186
187  register IndexPacket
188    *indexes;
189
190  register long
191    i,
192    x;
193
194  register PixelPacket
195    *q;
196
197  register unsigned char
198    *p;
199
200  ssize_t
201    count,
202    offset;
203
204  unsigned long
205    bit,
206    byte;
207
208  unsigned long
209    bytes_per_line,
210    scanline_pad;
211
212  /*
213    Open image file.
214  */
215  assert(image_info != (const ImageInfo *) NULL);
216  assert(image_info->signature == MagickSignature);
217  (void) LogMagickEvent(CoderEvent,GetMagickModule(),"%s",image_info->filename);
218  assert(exception != (ExceptionInfo *) NULL);
219  assert(exception->signature == MagickSignature);
220  image=AcquireImage(image_info);
221  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
222  if (status == MagickFalse)
223    {
224      image=DestroyImageList(image);
225      return((Image *) NULL);
226    }
227  icon_file.reserved=(short) ReadBlobLSBShort(image);
228  icon_file.resource_type=(short) ReadBlobLSBShort(image);
229  icon_file.count=(short) ReadBlobLSBShort(image);
230  if ((icon_file.reserved != 0) ||
231      ((icon_file.resource_type != 1) && (icon_file.resource_type != 2)) ||
232      (icon_file.count > MaxIcons))
233    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
234  for (i=0; i < icon_file.count; i++)
235  {
236    icon_file.directory[i].width=(unsigned char) ReadBlobByte(image);
237    icon_file.directory[i].height=(unsigned char) ReadBlobByte(image);
238    icon_file.directory[i].colors=(unsigned char) ReadBlobByte(image);
239    icon_file.directory[i].reserved=(unsigned char) ReadBlobByte(image);
240    icon_file.directory[i].planes=(unsigned short) ReadBlobLSBShort(image);
241    icon_file.directory[i].bits_per_pixel=(unsigned short)
242      ReadBlobLSBShort(image);
243    icon_file.directory[i].size=ReadBlobLSBLong(image);
244    icon_file.directory[i].offset=ReadBlobLSBLong(image);
245  }
246  for (i=0; i < icon_file.count; i++)
247  {
248    /*
249      Verify Icon identifier.
250    */
251    offset=(ssize_t) SeekBlob(image,(MagickOffsetType)
252      icon_file.directory[i].offset,SEEK_SET);
253    if (offset < 0)
254      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
255    icon_info.size=ReadBlobLSBLong(image);
256    icon_info.width=(unsigned char) ReadBlobLSBLong(image);
257    icon_info.height=(unsigned char) ReadBlobLSBLong(image)/2;
258    icon_info.planes=ReadBlobLSBShort(image);
259    icon_info.bits_per_pixel=ReadBlobLSBShort(image);
260    if (icon_info.bits_per_pixel > 32)
261      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
262    icon_info.compression=ReadBlobLSBLong(image);
263    icon_info.image_size=ReadBlobLSBLong(image);
264    icon_info.x_pixels=ReadBlobLSBLong(image);
265    icon_info.y_pixels=ReadBlobLSBLong(image);
266    icon_info.number_colors=ReadBlobLSBLong(image);
267    icon_info.colors_important=ReadBlobLSBLong(image);
268    image->matte=MagickTrue;
269    image->columns=(unsigned long) icon_file.directory[i].width;
270    if (image->columns == 0)
271      image->columns=256;
272    image->rows=(unsigned long) icon_file.directory[i].height;
273    if (image->rows == 0)
274      image->rows=256;
275    image->depth=8;
276    if (image->debug != MagickFalse)
277      {
278        (void) LogMagickEvent(CoderEvent,GetMagickModule()," scene    = %ld",i);
279        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"   size   = %ld",
280          icon_info.size);
281        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"   width  = %d",
282          icon_file.directory[i].width);
283        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"   height = %d",
284          icon_file.directory[i].height);
285        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"   colors = %ld",
286          icon_info.number_colors);
287        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"   planes = %d",
288          icon_info.planes);
289        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"   bpp    = %d",
290          icon_info.bits_per_pixel);
291      }
292  if ((icon_info.number_colors != 0) || (icon_info.bits_per_pixel <= 16))
293    {
294      image->storage_class=PseudoClass;
295      image->colors=icon_info.number_colors;
296      if (image->colors == 0)
297        image->colors=1 << icon_info.bits_per_pixel;
298    }
299  if (image->storage_class == PseudoClass)
300    {
301      register long
302        i;
303
304      unsigned char
305        *icon_colormap;
306
307      unsigned long
308        number_colors;
309
310      /*
311        Read Icon raster colormap.
312      */
313      number_colors=1UL << icon_info.bits_per_pixel;
314      if (AcquireImageColormap(image,number_colors) == MagickFalse)
315        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
316      icon_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
317        image->colors,4UL*sizeof(*icon_colormap));
318      if (icon_colormap == (unsigned char *) NULL)
319        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
320      count=ReadBlob(image,(size_t) (4*image->colors),icon_colormap);
321      if (count != (ssize_t) (4*image->colors))
322        ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
323      p=icon_colormap;
324      for (i=0; i < (long) image->colors; i++)
325      {
326        image->colormap[i].blue=(Quantum) ScaleCharToQuantum(*p++);
327        image->colormap[i].green=(Quantum) ScaleCharToQuantum(*p++);
328        image->colormap[i].red=(Quantum) ScaleCharToQuantum(*p++);
329        p++;
330      }
331      icon_colormap=(unsigned char *) RelinquishMagickMemory(icon_colormap);
332    }
333    /*
334      Convert Icon raster image to pixel packets.
335    */
336    if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
337      if (image->scene >= (image_info->scene+image_info->number_scenes-1))
338        break;
339    if (SetImageExtent(image,0,0) == MagickFalse)
340      {
341        InheritException(exception,&image->exception);
342        return(DestroyImageList(image));
343      }
344    bytes_per_line=(((image->columns*icon_info.bits_per_pixel)+31) & ~31) >> 3;
345    scanline_pad=((((image->columns*icon_info.bits_per_pixel)+31) & ~31)-
346      (image->columns*icon_info.bits_per_pixel)) >> 3;
347    switch (icon_info.bits_per_pixel)
348    {
349      case 1:
350      {
351        /*
352          Convert bitmap scanline.
353        */
354        for (y=(long) image->rows-1; y >= 0; y--)
355        {
356          q=SetImagePixels(image,0,y,image->columns,1);
357          if (q == (PixelPacket *) NULL)
358            break;
359          indexes=GetIndexes(image);
360          for (x=0; x < (long) (image->columns-7); x+=8)
361          {
362            byte=(unsigned long) ReadBlobByte(image);
363            for (bit=0; bit < 8; bit++)
364              indexes[x+bit]=(IndexPacket)
365                ((byte & (0x80 >> bit)) != 0 ? 0x01 : 0x00);
366          }
367          if ((image->columns % 8) != 0)
368            {
369              byte=(unsigned long) ReadBlobByte(image);
370              for (bit=0; bit < (image->columns % 8); bit++)
371                indexes[x+bit]=(IndexPacket)
372                  ((byte & (0x80 >> bit)) != 0 ? 0x01 : 0x00);
373            }
374          for (x=0; x < (long) scanline_pad; x++)
375            (void) ReadBlobByte(image);
376          if (SyncImagePixels(image) == MagickFalse)
377            break;
378          if (image->previous == (Image *) NULL)
379            {
380              status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
381                image->rows);
382              if (status == MagickFalse)
383                break;
384            }
385        }
386        break;
387      }
388      case 4:
389      {
390        /*
391          Read 4-bit Icon scanline.
392        */
393        for (y=(long) image->rows-1; y >= 0; y--)
394        {
395          q=SetImagePixels(image,0,y,image->columns,1);
396          if (q == (PixelPacket *) NULL)
397            break;
398          indexes=GetIndexes(image);
399          for (x=0; x < ((long) image->columns-1); x+=2)
400          {
401            byte=(unsigned long) ReadBlobByte(image);
402            indexes[x]=(IndexPacket) ((byte >> 4) & 0xf);
403            indexes[x+1]=(IndexPacket) ((byte) & 0xf);
404          }
405          if ((image->columns % 2) != 0)
406            {
407              byte=(unsigned long) ReadBlobByte(image);
408              indexes[x]=(IndexPacket) ((byte >> 4) & 0xf);
409            }
410          for (x=0; x < (long) scanline_pad; x++)
411            (void) ReadBlobByte(image);
412          if (SyncImagePixels(image) == MagickFalse)
413            break;
414          if (image->previous == (Image *) NULL)
415            {
416              status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
417                image->rows);
418              if (status == MagickFalse)
419                break;
420            }
421        }
422        break;
423      }
424      case 8:
425      {
426        /*
427          Convert PseudoColor scanline.
428        */
429        for (y=(long) image->rows-1; y >= 0; y--)
430        {
431          q=SetImagePixels(image,0,y,image->columns,1);
432          if (q == (PixelPacket *) NULL)
433            break;
434          indexes=GetIndexes(image);
435          for (x=0; x < (long) image->columns; x++)
436          {
437            byte=(unsigned long) ReadBlobByte(image);
438            indexes[x]=(IndexPacket) byte;
439          }
440          for (x=0; x < (long) scanline_pad; x++)
441            (void) ReadBlobByte(image);
442          if (SyncImagePixels(image) == MagickFalse)
443            break;
444          if (image->previous == (Image *) NULL)
445            {
446              status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
447                image->rows);
448              if (status == MagickFalse)
449                break;
450            }
451        }
452        break;
453      }
454      case 16:
455      {
456        /*
457          Convert PseudoColor scanline.
458        */
459        for (y=(long) image->rows-1; y >= 0; y--)
460        {
461          q=SetImagePixels(image,0,y,image->columns,1);
462          if (q == (PixelPacket *) NULL)
463            break;
464          indexes=GetIndexes(image);
465          for (x=0; x < (long) image->columns; x++)
466          {
467            byte=(unsigned long) ReadBlobByte(image);
468            byte|=(unsigned long) (ReadBlobByte(image) << 8);
469            indexes[x]=(IndexPacket) byte;
470          }
471          for (x=0; x < (long) scanline_pad; x++)
472            (void) ReadBlobByte(image);
473          if (SyncImagePixels(image) == MagickFalse)
474            break;
475          if (image->previous == (Image *) NULL)
476            {
477              status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
478                image->rows);
479              if (status == MagickFalse)
480                break;
481            }
482        }
483        break;
484      }
485      case 24:
486      case 32:
487      {
488        /*
489          Convert DirectColor scanline.
490        */
491        for (y=(long) image->rows-1; y >= 0; y--)
492        {
493          q=SetImagePixels(image,0,y,image->columns,1);
494          if (q == (PixelPacket *) NULL)
495            break;
496          for (x=0; x < (long) image->columns; x++)
497          {
498            q->blue=ScaleCharToQuantum((unsigned char) ReadBlobByte(image));
499            q->green=ScaleCharToQuantum((unsigned char) ReadBlobByte(image));
500            q->red=ScaleCharToQuantum((unsigned char) ReadBlobByte(image));
501            if (icon_info.bits_per_pixel == 32)
502              q->opacity=(Quantum) QuantumRange-ScaleCharToQuantum(
503                (unsigned char) ReadBlobByte(image));
504            q++;
505          }
506          if (icon_info.bits_per_pixel == 24)
507            for (x=0; x < (long) scanline_pad; x++)
508              (void) ReadBlobByte(image);
509          if (SyncImagePixels(image) == MagickFalse)
510            break;
511          if (image->previous == (Image *) NULL)
512            {
513              status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
514                image->rows);
515              if (status == MagickFalse)
516                break;
517            }
518        }
519        break;
520      }
521      default:
522        ThrowReaderException(CorruptImageError,"ImproperImageHeader");
523    }
524    (void) SyncImage(image);
525    if (icon_info.bits_per_pixel != 32)
526      {
527        /*
528          Read the ICON alpha mask.
529        */
530        image->storage_class=DirectClass;
531        for (y=(long) image->rows-1; y >= 0; y--)
532        {
533          q=GetImagePixels(image,0,y,image->columns,1);
534          if (q == (PixelPacket *) NULL)
535            break;
536          for (x=0; x < ((long) image->columns-7); x+=8)
537          {
538            byte=(unsigned long) ReadBlobByte(image);
539            for (bit=0; bit < 8; bit++)
540              q[x+bit].opacity=(Quantum) (((byte & (0x80 >> bit)) != 0) ?
541                TransparentOpacity : OpaqueOpacity);
542          }
543          if ((image->columns % 8) != 0)
544            {
545              byte=(unsigned long) ReadBlobByte(image);
546              for (bit=0; bit < (image->columns % 8); bit++)
547                q[x+bit].opacity=(Quantum) (((byte & (0x80 >> bit)) != 0) ?
548                  TransparentOpacity : OpaqueOpacity);
549            }
550         if ((image->columns % 32)  != 0)
551           for (x=0; x < (long) ((32-(image->columns % 32))/8); x++)
552             (void) ReadBlobByte(image);
553          if (SyncImagePixels(image) == MagickFalse)
554            break;
555        }
556      }
557    if (EOFBlob(image) != MagickFalse)
558      {
559        ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
560          image->filename);
561        break;
562      }
563    /*
564      Proceed to next image.
565    */
566    if (image_info->number_scenes != 0)
567      if (image->scene >= (image_info->scene+image_info->number_scenes-1))
568        break;
569    if (i < (long) (icon_file.count-1))
570      {
571        /*
572          Allocate next image structure.
573        */
574        AcquireNextImage(image_info,image);
575        if (GetNextImageInList(image) == (Image *) NULL)
576          {
577            image=DestroyImageList(image);
578            return((Image *) NULL);
579          }
580        image=SyncNextImageInList(image);
581        status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
582          GetBlobSize(image));
583        if (status == MagickFalse)
584          break;
585      }
586  }
587  (void) CloseBlob(image);
588  return(GetFirstImageInList(image));
589}
590
591/*
592%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
593%                                                                             %
594%                                                                             %
595%                                                                             %
596%   R e g i s t e r I C O N I m a g e                                         %
597%                                                                             %
598%                                                                             %
599%                                                                             %
600%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
601%
602%  RegisterICONImage() adds attributes for the Icon image format to
603%  the list of supported formats.  The attributes include the image format
604%  tag, a method to read and/or write the format, whether the format
605%  supports the saving of more than one frame to the same file or blob,
606%  whether the format supports native in-memory I/O, and a brief
607%  description of the format.
608%
609%  The format of the RegisterICONImage method is:
610%
611%      unsigned long RegisterICONImage(void)
612%
613*/
614ModuleExport unsigned long RegisterICONImage(void)
615{
616  MagickInfo
617    *entry;
618
619  entry=SetMagickInfo("CUR");
620  entry->decoder=(DecodeImageHandler *) ReadICONImage;
621  entry->encoder=(EncodeImageHandler *) WriteICONImage;
622  entry->adjoin=MagickFalse;
623  entry->seekable_stream=MagickTrue;
624  entry->description=ConstantString("Microsoft icon");
625  entry->module=ConstantString("CUR");
626  (void) RegisterMagickInfo(entry);
627  entry=SetMagickInfo("ICO");
628  entry->decoder=(DecodeImageHandler *) ReadICONImage;
629  entry->encoder=(EncodeImageHandler *) WriteICONImage;
630  entry->adjoin=MagickTrue;
631  entry->seekable_stream=MagickTrue;
632  entry->description=ConstantString("Microsoft icon");
633  entry->module=ConstantString("ICON");
634  (void) RegisterMagickInfo(entry);
635  entry=SetMagickInfo("ICON");
636  entry->decoder=(DecodeImageHandler *) ReadICONImage;
637  entry->encoder=(EncodeImageHandler *) WriteICONImage;
638  entry->adjoin=MagickFalse;
639  entry->seekable_stream=MagickTrue;
640  entry->description=ConstantString("Microsoft icon");
641  entry->module=ConstantString("ICON");
642  (void) RegisterMagickInfo(entry);
643  return(MagickImageCoderSignature);
644}
645
646/*
647%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
648%                                                                             %
649%                                                                             %
650%                                                                             %
651%   U n r e g i s t e r I C O N I m a g e                                     %
652%                                                                             %
653%                                                                             %
654%                                                                             %
655%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
656%
657%  UnregisterICONImage() removes format registrations made by the
658%  ICON module from the list of supported formats.
659%
660%  The format of the UnregisterICONImage method is:
661%
662%      UnregisterICONImage(void)
663%
664*/
665ModuleExport void UnregisterICONImage(void)
666{
667  (void) UnregisterMagickInfo("CUR");
668  (void) UnregisterMagickInfo("ICO");
669  (void) UnregisterMagickInfo("ICON");
670}
671
672/*
673%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
674%                                                                             %
675%                                                                             %
676%                                                                             %
677%   W r i t e I C O N I m a g e                                               %
678%                                                                             %
679%                                                                             %
680%                                                                             %
681%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
682%
683%  WriteICONImage() writes an image in Microsoft Windows bitmap encoded
684%  image format, version 3 for Windows or (if the image has a matte channel)
685%  version 4.
686%
687%  The format of the WriteICONImage method is:
688%
689%      MagickBooleanType WriteICONImage(const ImageInfo *image_info,
690%        Image *image)
691%
692%  A description of each parameter follows.
693%
694%    o image_info: the image info.
695%
696%    o image:  The image.
697%
698*/
699static MagickBooleanType WriteICONImage(const ImageInfo *image_info,
700  Image *image)
701{
702  IconFile
703    icon_file;
704
705  IconInfo
706    icon_info;
707
708  Image
709    *next;
710 
711  long
712    y;
713
714  MagickBooleanType
715    status;
716
717  MagickOffsetType
718    offset,
719    scene;
720
721  register const PixelPacket
722    *p;
723
724  register IndexPacket
725    *indexes;
726
727  register long
728    i,
729    x;
730
731  register unsigned char
732    *q;
733
734  unsigned char
735    bit,
736    byte,
737    *pixels;
738
739  unsigned long
740    bytes_per_line,
741    scanline_pad;
742
743  /*
744    Open output image file.
745  */
746  assert(image_info != (const ImageInfo *) NULL);
747  assert(image_info->signature == MagickSignature);
748  assert(image != (Image *) NULL);
749  assert(image->signature == MagickSignature);
750    (void) LogMagickEvent(CoderEvent,GetMagickModule(),"%s",image->filename);
751  status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
752  if (status == MagickFalse)
753    return(status);
754  scene=0;
755  next=image;
756  do
757  {
758    if ((image->columns > 256L) || (image->rows > 256L))
759      ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
760    scene++;
761    next=SyncNextImageInList(next);
762  } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
763  /*
764    Dump out a ICON header template to be properly initialized later.
765  */
766  (void) WriteBlobLSBShort(image,0);
767  (void) WriteBlobLSBShort(image,1);
768  (void) WriteBlobLSBShort(image,(unsigned char) scene);
769  (void) ResetMagickMemory(&icon_info,0,sizeof(icon_info));
770  scene=0;
771  next=image;
772  do
773  {
774    (void) WriteBlobByte(image,icon_file.directory[scene].width);
775    (void) WriteBlobByte(image,icon_file.directory[scene].height);
776    (void) WriteBlobByte(image,icon_file.directory[scene].colors);
777    (void) WriteBlobByte(image,icon_file.directory[scene].reserved);
778    (void) WriteBlobLSBShort(image,icon_file.directory[scene].planes);
779    (void) WriteBlobLSBShort(image,icon_file.directory[scene].bits_per_pixel);
780    (void) WriteBlobLSBLong(image,icon_file.directory[scene].size);
781    (void) WriteBlobLSBLong(image,icon_file.directory[scene].offset);
782    scene++;
783    next=SyncNextImageInList(next);
784  } while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
785  scene=0;
786  next=image;
787  do
788  {
789    /*
790      Initialize ICON raster file header.
791    */
792    if (image->colorspace != RGBColorspace)
793      (void) SetImageColorspace(next,RGBColorspace);
794    icon_info.file_size=14+12+28;
795    icon_info.offset_bits=icon_info.file_size;
796    icon_info.compression=BI_RGB;
797    if ((next->storage_class != DirectClass) && (next->colors > 256))
798      (void) SetImageStorageClass(next,DirectClass);
799    if (next->storage_class == DirectClass)
800      {
801        /*
802          Full color ICON raster.
803        */
804        icon_info.number_colors=0;
805        icon_info.bits_per_pixel=32;
806        icon_info.compression=(unsigned long) BI_RGB;
807      }
808    else
809      {
810        /*
811          Colormapped ICON raster.
812        */
813        icon_info.bits_per_pixel=8;
814        if (next->colors <= 256)
815          icon_info.bits_per_pixel=8;
816        if (next->colors <= 16)
817          icon_info.bits_per_pixel=4;
818        if (next->colors <= 2)
819          icon_info.bits_per_pixel=1;
820        icon_info.number_colors=1 << icon_info.bits_per_pixel;
821        if (icon_info.number_colors < next->colors)
822          {
823            (void) SetImageStorageClass(next,DirectClass);
824            icon_info.number_colors=0;
825            icon_info.bits_per_pixel=(unsigned short) 24;
826            icon_info.compression=(unsigned long) BI_RGB;
827          }
828        else
829          {
830            icon_info.file_size+=3*(1UL << icon_info.bits_per_pixel);
831            icon_info.offset_bits+=3*(1UL << icon_info.bits_per_pixel);
832            icon_info.file_size+=(1UL << icon_info.bits_per_pixel);
833            icon_info.offset_bits+=(1UL << icon_info.bits_per_pixel);
834          }
835      }
836    bytes_per_line=(((next->columns*icon_info.bits_per_pixel)+31) & ~31) >> 3;
837    icon_info.ba_offset=0;
838    icon_info.width=(long) next->columns;
839    icon_info.height=(long) next->rows;
840    icon_info.planes=1;
841    icon_info.image_size=bytes_per_line*next->rows;
842    icon_info.size=40;
843    icon_info.size+=(4*icon_info.number_colors);
844    icon_info.size+=icon_info.image_size;
845    icon_info.size+=(((icon_info.width+31) & ~31) >> 3)*icon_info.height;
846    icon_info.file_size+=icon_info.image_size;
847    icon_info.x_pixels=0;
848    icon_info.y_pixels=0;
849    switch (next->units)
850    {
851      case UndefinedResolution:
852      case PixelsPerInchResolution:
853      {
854        icon_info.x_pixels=(unsigned long) (100.0*next->x_resolution/2.54);
855        icon_info.y_pixels=(unsigned long) (100.0*next->y_resolution/2.54);
856        break;
857      }
858      case PixelsPerCentimeterResolution:
859      {
860        icon_info.x_pixels=(unsigned long) (100.0*next->x_resolution);
861        icon_info.y_pixels=(unsigned long) (100.0*next->y_resolution);
862        break;
863      }
864    }
865    icon_info.colors_important=icon_info.number_colors;
866    /*
867      Convert MIFF to ICON raster pixels.
868    */
869    pixels=(unsigned char *) AcquireQuantumMemory((size_t) icon_info.image_size,
870      sizeof(*pixels));
871    if (pixels == (unsigned char *) NULL)
872      ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
873    (void) ResetMagickMemory(pixels,0,(size_t) icon_info.image_size);
874    switch (icon_info.bits_per_pixel)
875    {
876      case 1:
877      {
878        unsigned long
879          bit,
880          byte;
881
882        /*
883          Convert PseudoClass image to a ICON monochrome image.
884        */
885        for (y=0; y < (long) next->rows; y++)
886        {
887          p=AcquireImagePixels(next,0,y,next->columns,1,&next->exception);
888          if (p == (const PixelPacket *) NULL)
889            break;
890          indexes=GetIndexes(next);
891          q=pixels+(next->rows-y-1)*bytes_per_line;
892          bit=0;
893          byte=0;
894          for (x=0; x < (long) next->columns; x++)
895          {
896            byte<<=1;
897            byte|=indexes[x] != 0 ? 0x01 : 0x00;
898            bit++;
899            if (bit == 8)
900              {
901                *q++=(unsigned char) byte;
902                bit=0;
903                byte=0;
904              }
905           }
906          if (bit != 0)
907            *q++=(unsigned char) (byte << (8-bit));
908          if (next->previous == (Image *) NULL)
909            {
910              status=SetImageProgress(next,SaveImageTag,y,next->rows);
911              if (status == MagickFalse)
912                break;
913            }
914        }
915        break;
916      }
917      case 4:
918      {
919        unsigned long
920          nibble,
921          byte;
922
923        /*
924          Convert PseudoClass image to a ICON monochrome image.
925        */
926        for (y=0; y < (long) next->rows; y++)
927        {
928          p=AcquireImagePixels(next,0,y,next->columns,1,&next->exception);
929          if (p == (const PixelPacket *) NULL)
930            break;
931          indexes=GetIndexes(next);
932          q=pixels+(next->rows-y-1)*bytes_per_line;
933          nibble=0;
934          byte=0;
935          for (x=0; x < (long) next->columns; x++)
936          {
937            byte<<=4;
938            byte|=((unsigned long) indexes[x] & 0x0f);
939            nibble++;
940            if (nibble == 2)
941              {
942                *q++=(unsigned char) byte;
943                nibble=0;
944                byte=0;
945              }
946           }
947          if (nibble != 0)
948            *q++=(unsigned char) (byte << 4);
949          if (next->previous == (Image *) NULL)
950            {
951              status=SetImageProgress(next,SaveImageTag,y,next->rows);
952              if (status == MagickFalse)
953                break;
954            }
955        }
956        break;
957      }
958      case 8:
959      {
960        /*
961          Convert PseudoClass packet to ICON pixel.
962        */
963        for (y=0; y < (long) next->rows; y++)
964        {
965          p=AcquireImagePixels(next,0,y,next->columns,1,&next->exception);
966          if (p == (const PixelPacket *) NULL)
967            break;
968          indexes=GetIndexes(next);
969          q=pixels+(next->rows-y-1)*bytes_per_line;
970          for (x=0; x < (long) next->columns; x++)
971            *q++=(unsigned char) indexes[x];
972          if (next->previous == (Image *) NULL)
973            {
974              status=SetImageProgress(next,SaveImageTag,y,next->rows);
975              if (status == MagickFalse)
976                break;
977            }
978        }
979        break;
980      }
981      case 24:
982      case 32:
983      {
984        /*
985          Convert DirectClass packet to ICON BGR888 or BGRA8888 pixel.
986        */
987        for (y=0; y < (long) next->rows; y++)
988        {
989          p=AcquireImagePixels(next,0,y,next->columns,1,&next->exception);
990          if (p == (const PixelPacket *) NULL)
991            break;
992          q=pixels+(next->rows-y-1)*bytes_per_line;
993          for (x=0; x < (long) next->columns; x++)
994          {
995            *q++=ScaleQuantumToChar(p->blue);
996            *q++=ScaleQuantumToChar(p->green);