Coverage Report

Created: 2026-11-12 07:35

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