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