Coverage Report

Created: 2026-05-16 07:22

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