Coverage Report

Created: 2026-06-30 07:12

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
307
#define IconRgbCompression (size_t) 0
75
7.41k
#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.71k
{
114
7.71k
  ssize_t
115
7.71k
    i;
116
117
7.71k
  assert(directory != (IconDirectory *) NULL);
118
119
7.71k
  if (directory->icons != (IconEntry **) NULL)
120
7.70k
    {
121
44.7k
      for (i=0; i < (ssize_t) directory->count; i++)
122
37.0k
      {
123
37.0k
        if (directory->icons[i] != (IconEntry *) NULL)
124
37.0k
          directory->icons[i]=(IconEntry *) RelinquishMagickMemory(
125
37.0k
            directory->icons[i]);
126
37.0k
      }
127
7.70k
      directory->icons=(IconEntry **) RelinquishMagickMemory(directory->icons);
128
7.70k
    }
129
7.71k
  directory=(IconDirectory *) RelinquishMagickMemory(directory);
130
7.71k
  return(directory);
131
7.71k
}
132
133
static IconDirectory *AcquireIconDirectory(size_t count)
134
7.71k
{
135
7.71k
  IconDirectory
136
7.71k
    *directory;
137
138
7.71k
  ssize_t
139
7.71k
    i;
140
141
7.71k
  directory=(IconDirectory*) AcquireMagickMemory(sizeof(*directory));
142
7.71k
  if (directory == (IconDirectory*) NULL)
143
0
    return(directory);
144
7.71k
  directory->icons=(IconEntry **) AcquireQuantumMemory(count,
145
7.71k
    sizeof(*directory->icons));
146
7.71k
  if (directory->icons == (IconEntry **) NULL)
147
3
    return(RelinquishIconDirectory(directory));
148
7.70k
  memset(directory->icons,0,count*sizeof(*directory->icons));
149
7.70k
  directory->count=0;
150
44.7k
  for (i=0; i < (ssize_t) count; i++)
151
37.0k
  {
152
37.0k
    directory->icons[i]=(IconEntry *) AcquireMagickMemory(
153
37.0k
      sizeof(**directory->icons));
154
37.0k
    if (directory->icons[i] == (IconEntry *) NULL)
155
0
      return(RelinquishIconDirectory(directory));
156
37.0k
    directory->count++;
157
37.0k
  }
158
7.70k
  return(directory);
159
7.70k
}
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
118
{
191
118
  size_t
192
118
    columns,
193
118
    rows;
194
195
118
  ssize_t
196
118
    i;
197
198
  /*
199
    Read Windows 1.0 Icon.
200
  */
201
118
  (void) ReadBlobLSBLong(image);  /* hot spot X/Y */
202
118
  columns=(size_t) ReadBlobLSBShort(image);
203
118
  rows=(size_t) (ReadBlobLSBShort(image));
204
118
  (void) ReadBlobLSBShort(image);  /* width of bitmap in bytes */
205
118
  (void) ReadBlobLSBShort(image);  /* cursor color */
206
118
  if (((rows != 32) && (rows != 64)) || ((columns != 32) && (columns != 64)))
207
58
    {
208
58
      (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
209
58
        "ImproperImageHeader","`%s'",image->filename);
210
58
      return(DestroyImageList(image));
211
58
    }
212
  /*
213
    Convert bitmap scanline.
214
  */
215
60
  if (SetImageExtent(image,columns,rows,exception) == MagickFalse)
216
0
    return(DestroyImageList(image));
217
60
  image->alpha_trait=BlendPixelTrait;
218
60
  if (AcquireImageColormap(image,3,exception) == MagickFalse)
219
0
    return(DestroyImageList(image));
220
60
  image->colormap[1].alpha=TransparentAlpha;
221
180
  for (i=0; i < 2; i++)
222
120
  {
223
120
    ssize_t
224
120
      y;
225
226
931
    for (y=0; y < (ssize_t) image->rows; y++)
227
920
    {
228
920
      Quantum
229
920
        *q;
230
231
920
      ssize_t
232
920
        x;
233
234
920
      q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
235
920
      if (q == (Quantum *) NULL)
236
0
        break;
237
4.94k
      for (x=0; x < (ssize_t) (image->columns-7); x+=8)
238
4.02k
      {
239
4.02k
        size_t
240
4.02k
          bit,
241
4.02k
          byte;
242
 
243
4.02k
        byte=(size_t) ReadBlobByte(image);
244
36.2k
        for (bit=0; bit < 8; bit++)
245
32.2k
        {
246
32.2k
          Quantum
247
32.2k
            index;
248
249
32.2k
          index=(Quantum) ((byte & (0x80 >> bit)) != 0 ? (i == 0 ? 0x01 : 0x02) : 0x00);
250
32.2k
          if (i == 0)
251
25.7k
            SetPixelIndex(image,index,q);
252
6.46k
          else
253
6.46k
            if (GetPixelIndex(image,q) != 0x01)
254
4.07k
              SetPixelIndex(image,index,q);
255
32.2k
          q+=(ptrdiff_t) GetPixelChannels(image);
256
32.2k
        }
257
4.02k
      }
258
920
      if (SyncAuthenticPixels(image,exception) == MagickFalse)
259
0
        break;
260
920
      if (EOFBlob(image) != MagickFalse)
261
109
        break;
262
920
    }
263
120
  }
264
60
  if (SyncImage(image,exception) == MagickFalse)
265
0
    return(DestroyImageList(image));
266
60
  if (CloseBlob(image) == MagickFalse)
267
0
    return(DestroyImageList(image));
268
60
  return(image);
269
60
}
270
271
static inline size_t GetICONSize(size_t directory_size,size_t image_size)
272
3.75k
{
273
3.75k
  if (image_size != 0)
274
3.27k
    return(image_size);
275
479
  if (directory_size != 0)
276
391
    return(directory_size);
277
88
  return(256);
278
479
}
279
280
static Image *ReadICONImage(const ImageInfo *image_info,
281
  ExceptionInfo *exception)
282
7.58k
{
283
7.58k
#define ThrowICONReaderException(exception,message) \
284
24
{ \
285
24
  directory=RelinquishIconDirectory(directory); \
286
751
  ThrowReaderException(exception,message) \
287
751
}
288
289
7.58k
  IconDirectory
290
7.58k
    *directory;
291
292
7.58k
  Image
293
7.58k
    *image;
294
295
7.58k
  MagickBooleanType
296
7.58k
    status;
297
298
7.58k
  MagickSizeType
299
7.58k
    extent;
300
301
7.58k
  Quantum
302
7.58k
    *q;
303
304
7.58k
  short
305
7.58k
    reserved,
306
7.58k
    resource_type;
307
308
7.58k
  size_t
309
7.58k
    bit,
310
7.58k
    byte,
311
7.58k
    bytes_per_line,
312
7.58k
    one,
313
7.58k
    scanline_pad;
314
315
7.58k
  ssize_t
316
7.58k
    i,
317
7.58k
    offset,
318
7.58k
    x,
319
7.58k
    y;
320
321
7.58k
  unsigned char
322
7.58k
    *p;
323
324
7.58k
  unsigned short
325
7.58k
    icon_count;
326
327
  /*
328
    Open image file.
329
  */
330
7.58k
  assert(image_info != (const ImageInfo *) NULL);
331
7.58k
  assert(image_info->signature == MagickCoreSignature);
332
7.58k
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),"%s",image_info->filename);
333
7.58k
  assert(exception != (ExceptionInfo *) NULL);
334
7.58k
  assert(exception->signature == MagickCoreSignature);
335
7.58k
  image=AcquireImage(image_info,exception);
336
7.58k
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
337
7.58k
  if (status == MagickFalse)
338
0
    {
339
0
      image=DestroyImageList(image);
340
0
      return((Image *) NULL);
341
0
    }
342
7.58k
  reserved=(short) ReadBlobLSBShort(image);
343
7.58k
  if ((reserved == 0x0001) || (reserved == 0x0101) || (reserved == 0x0201))
344
118
    return(Read1XImage(image,exception));
345
7.46k
  resource_type=(short) ReadBlobLSBShort(image);
346
7.46k
  icon_count=ReadBlobLSBShort(image);
347
7.46k
  if ((reserved != 0) || ((resource_type != 1) && (resource_type != 2)) ||
348
7.41k
      (icon_count > MaxIcons))
349
7.40k
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
350
7.40k
  directory=AcquireIconDirectory((size_t) icon_count);
351
7.40k
  if (directory == (IconDirectory *) NULL)
352
7.40k
    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
353
7.40k
  extent=0;
354
21.5k
  for (i=0; i < (ssize_t) directory->count; i++)
355
14.2k
  {
356
14.2k
    directory->icons[i]->width=(unsigned char) ReadBlobByte(image);
357
14.2k
    directory->icons[i]->height=(unsigned char) ReadBlobByte(image);
358
14.2k
    directory->icons[i]->colors=(unsigned char) ReadBlobByte(image);
359
14.2k
    directory->icons[i]->reserved=(unsigned char) ReadBlobByte(image);
360
14.2k
    directory->icons[i]->planes=(unsigned short) ReadBlobLSBShort(image);
361
14.2k
    directory->icons[i]->bits_per_pixel=(unsigned short)
362
14.2k
      ReadBlobLSBShort(image);
363
14.2k
    directory->icons[i]->size=ReadBlobLSBLong(image);
364
14.2k
    directory->icons[i]->offset=ReadBlobLSBLong(image);
365
14.2k
    if (EOFBlob(image) != MagickFalse)
366
97
      break;
367
14.1k
    extent=MagickMax(extent,directory->icons[i]->size);
368
14.1k
  }
369
7.40k
  if ((EOFBlob(image) != MagickFalse) || (extent > GetBlobSize(image)))
370
7.25k
    ThrowICONReaderException(CorruptImageError,"UnexpectedEndOfFile");
371
7.25k
  one=1;
372
12.6k
  for (i=0; i < (ssize_t) directory->count; i++)
373
11.1k
  {
374
11.1k
    size_t
375
11.1k
      size;
376
377
11.1k
    ssize_t
378
11.1k
      count,
379
11.1k
      height,
380
11.1k
      width;
381
382
11.1k
    unsigned short
383
11.1k
      bits_per_pixel,
384
11.1k
      planes;
385
386
    /*
387
      Verify Icon identifier.
388
    */
389
11.1k
    offset=(ssize_t) SeekBlob(image,(MagickOffsetType)
390
11.1k
      directory->icons[i]->offset,SEEK_SET);
391
11.1k
    if (offset < 0)
392
11.1k
      ThrowICONReaderException(CorruptImageError,"ImproperImageHeader");
393
11.1k
    size=ReadBlobLSBLong(image);
394
11.1k
    width=ReadBlobLSBSignedLong(image);
395
11.1k
    height=(ReadBlobLSBSignedLong(image)/2);
396
11.1k
    planes=ReadBlobLSBShort(image);
397
11.1k
    bits_per_pixel=ReadBlobLSBShort(image);
398
11.1k
    if (EOFBlob(image) != MagickFalse)
399
415
      {
400
415
        ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
401
415
          image->filename);
402
415
        break;
403
415
      }
404
10.7k
    if (((planes == 18505) && (bits_per_pixel == 21060)) || 
405
2.91k
        (size == 0x474e5089))
406
8.50k
      {
407
8.50k
        Image
408
8.50k
          *icon_image;
409
410
8.50k
        ImageInfo
411
8.50k
          *read_info;
412
413
8.50k
        size_t
414
8.50k
          length;
415
416
8.50k
        unsigned char
417
8.50k
          *png;
418
419
        /*
420
          Icon image encoded as a compressed PNG image.
421
        */
422
8.50k
        length=directory->icons[i]->size;
423
8.50k
        if ((length < 16) || (~length < 16))
424
8.47k
          ThrowICONReaderException(ResourceLimitError,"MemoryAllocationFailed");
425
8.47k
        png=(unsigned char *) AcquireQuantumMemory(length,sizeof(*png));
426
8.47k
        if (png == (unsigned char *) NULL)
427
8.47k
          ThrowICONReaderException(ResourceLimitError,"MemoryAllocationFailed");
428
8.47k
        (void) memcpy(png,"\211PNG\r\n\032\n\000\000\000\015",12);
429
8.47k
        png[12]=(unsigned char) planes;
430
8.47k
        png[13]=(unsigned char) (planes >> 8);
431
8.47k
        png[14]=(unsigned char) bits_per_pixel;
432
8.47k
        png[15]=(unsigned char) (bits_per_pixel >> 8);
433
8.47k
        count=ReadBlob(image,length-16,png+16);
434
8.47k
        if (count != (ssize_t) (length-16))
435
79
          {
436
79
            png=(unsigned char *) RelinquishMagickMemory(png);
437
79
            ThrowICONReaderException(CorruptImageError,
438
79
              "InsufficientImageDataInFile");
439
0
          }
440
8.39k
        read_info=CloneImageInfo(image_info);
441
8.39k
        (void) CopyMagickString(read_info->magick,"PNG",MagickPathExtent);
442
8.39k
        icon_image=BlobToImage(read_info,png,length,exception);
443
8.39k
        read_info=DestroyImageInfo(read_info);
444
8.39k
        png=(unsigned char *) RelinquishMagickMemory(png);
445
8.39k
        if (icon_image == (Image *) NULL)
446
4.23k
          {
447
4.23k
            directory=RelinquishIconDirectory(directory);
448
4.23k
            return(DestroyImageList(image));
449
4.23k
          }
450
4.16k
        DestroyBlob(icon_image);
451
4.16k
        icon_image->blob=ReferenceBlob(image->blob);
452
4.16k
        ReplaceImageInList(&image,icon_image);
453
4.16k
        icon_image->scene=(size_t) i;
454
4.16k
      }
455
2.23k
    else
456
2.23k
      {
457
2.23k
        size_t
458
2.23k
          number_colors;
459
460
2.23k
        if (bits_per_pixel > 32)
461
1.92k
          ThrowICONReaderException(CorruptImageError,"ImproperImageHeader");
462
1.92k
        (void) ReadBlobLSBLong(image); /* compression */
463
1.92k
        (void) ReadBlobLSBLong(image); /* image_size */
464
1.92k
        (void) ReadBlobLSBLong(image); /* x_pixels */
465
1.92k
        (void) ReadBlobLSBLong(image); /* y_pixels */
466
1.92k
        number_colors=ReadBlobLSBLong(image);
467
1.92k
        if (number_colors > GetBlobSize(image))
468
50
          ThrowICONReaderException(CorruptImageError,
469
1.92k
            "InsufficientImageDataInFile");
470
1.87k
        (void) ReadBlobLSBLong(image); /* colors_important */
471
1.87k
        image->alpha_trait=BlendPixelTrait;
472
1.87k
        image->columns=(size_t) GetICONSize(directory->icons[i]->width,
473
1.87k
          (size_t) width);
474
1.87k
        image->rows=(size_t) GetICONSize(directory->icons[i]->height,
475
1.87k
          (size_t) height);
476
1.87k
        image->depth=bits_per_pixel;
477
1.87k
        if (image->depth > 16)
478
651
          image->depth=8;
479
1.87k
        if (image->debug != MagickFalse)
480
0
          {
481
0
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
482
0
              " scene    = %.20g",(double) i);
483
0
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
484
0
              "   size   = %.20g",(double) size);
485
0
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
486
0
              "   width  = %.20g",(double) image->columns);
487
0
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
488
0
              "   height = %.20g",(double) image->rows);
489
0
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
490
0
              "   colors = %.20g",(double ) number_colors);
491
0
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
492
0
              "   planes = %.20g",(double) planes);
493
0
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
494
0
              "   bpp    = %.20g",(double) bits_per_pixel);
495
0
          }
496
1.87k
      if ((number_colors != 0) || (bits_per_pixel <= 16U))
497
1.39k
        {
498
1.39k
          image->storage_class=PseudoClass;
499
1.39k
          image->colors=number_colors;
500
1.39k
          if ((image->colors == 0) || (image->colors > 256))
501
735
            image->colors=one << bits_per_pixel;
502
1.39k
        }
503
1.87k
      if (image->storage_class == PseudoClass)
504
1.39k
        {
505
1.39k
          ssize_t
506
1.39k
            j;
507
508
1.39k
          unsigned char
509
1.39k
            *icon_colormap;
510
511
          /*
512
            Read Icon raster colormap.
513
          */
514
1.39k
          if (image->colors > GetBlobSize(image))
515
29
            ThrowICONReaderException(CorruptImageError,
516
1.39k
              "InsufficientImageDataInFile");
517
1.36k
          if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
518
0
            ThrowICONReaderException(ResourceLimitError,
519
1.36k
              "MemoryAllocationFailed");
520
1.36k
          icon_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
521
1.36k
            image->colors,4UL*sizeof(*icon_colormap));
522
1.36k
          if (icon_colormap == (unsigned char *) NULL)
523
0
            ThrowICONReaderException(ResourceLimitError,
524
1.36k
              "MemoryAllocationFailed");
525
1.36k
          count=ReadBlob(image,(size_t) (4*image->colors),icon_colormap);
526
1.36k
          if (count != (ssize_t) (4*image->colors))
527
80
            {
528
80
              icon_colormap=(unsigned char *) RelinquishMagickMemory(
529
80
                icon_colormap);
530
80
              ThrowICONReaderException(CorruptImageError,
531
80
                "InsufficientImageDataInFile");
532
0
            }
533
1.28k
          p=icon_colormap;
534
8.46k
          for (j=0; j < (ssize_t) image->colors; j++)
535
7.17k
          {
536
7.17k
            image->colormap[j].blue=(Quantum) ScaleCharToQuantum(*p++);
537
7.17k
            image->colormap[j].green=(Quantum) ScaleCharToQuantum(*p++);
538
7.17k
            image->colormap[j].red=(Quantum) ScaleCharToQuantum(*p++);
539
7.17k
            p++;
540
7.17k
          }
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.76k
        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.76k
        status=SetImageExtent(image,image->columns,image->rows,exception);
551
1.76k
        if (status == MagickFalse)
552
201
          {
553
201
            directory=RelinquishIconDirectory(directory);
554
201
            return(DestroyImageList(image));
555
201
          }
556
1.56k
        bytes_per_line=(((image->columns*bits_per_pixel)+31U) & ~31U) >> 3;
557
1.56k
        (void) bytes_per_line;
558
1.56k
        scanline_pad=((((image->columns*bits_per_pixel)+31U) & ~31U)-
559
1.56k
          (image->columns*bits_per_pixel)) >> 3;
560
1.56k
        switch (bits_per_pixel)
561
1.56k
        {
562
632
          case 1:
563
632
          {
564
            /*
565
              Convert bitmap scanline.
566
            */
567
16.2k
            for (y=(ssize_t) image->rows-1; y >= 0; y--)
568
15.7k
            {
569
15.7k
              q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
570
15.7k
              if (q == (Quantum *) NULL)
571
0
                break;
572
65.5k
              for (x=0; x < (ssize_t) (image->columns-7); x+=8)
573
49.8k
              {
574
49.8k
                byte=(size_t) ReadBlobByte(image);
575
448k
                for (bit=0; bit < 8; bit++)
576
398k
                {
577
398k
                  SetPixelIndex(image,(Quantum) ((byte & (0x80 >> bit)) != 0 ?
578
289k
                    0x01 : 0x00),q);
579
398k
                  q+=(ptrdiff_t) GetPixelChannels(image);
580
398k
                }
581
49.8k
              }
582
15.7k
              if ((image->columns % 8) != 0)
583
13.2k
                {
584
13.2k
                  byte=(size_t) ReadBlobByte(image);
585
54.4k
                  for (bit=0; bit < (image->columns % 8); bit++)
586
41.2k
                  {
587
41.2k
                    SetPixelIndex(image,(Quantum) ((byte & (0x80 >> bit)) != 0 ?
588
35.1k
                      0x01 : 0x00),q);
589
41.2k
                    q+=(ptrdiff_t) GetPixelChannels(image);
590
41.2k
                  }
591
13.2k
                }
592
39.5k
              for (x=0; x < (ssize_t) scanline_pad; x++)
593
23.8k
                (void) ReadBlobByte(image);
594
15.7k
              if (SyncAuthenticPixels(image,exception) == MagickFalse)
595
0
                break;
596
15.7k
              if (EOFBlob(image) != MagickFalse)
597
67
                break;
598
15.6k
              if (image->previous == (Image *) NULL)
599
3.32k
                {
600
3.32k
                  status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
601
3.32k
                    image->rows-y-1,(MagickSizeType) image->rows);
602
3.32k
                  if (status == MagickFalse)
603
0
                    break;
604
3.32k
                }
605
15.6k
            }
606
632
            break;
607
0
          }
608
243
          case 4:
609
243
          {
610
            /*
611
              Read 4-bit Icon scanline.
612
            */
613
2.03k
            for (y=(ssize_t) image->rows-1; y >= 0; y--)
614
1.85k
            {
615
1.85k
              q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
616
1.85k
              if (q == (Quantum *) NULL)
617
0
                break;
618
29.4k
              for (x=0; x < ((ssize_t) image->columns-1); x+=2)
619
27.6k
              {
620
27.6k
                byte=(size_t) ReadBlobByte(image);
621
27.6k
                SetPixelIndex(image,(Quantum) ((byte >> 4) & 0xf),q);
622
27.6k
                q+=(ptrdiff_t) GetPixelChannels(image);
623
27.6k
                SetPixelIndex(image,(Quantum) ((byte) & 0xf),q);
624
27.6k
                q+=(ptrdiff_t) GetPixelChannels(image);
625
27.6k
              }
626
1.85k
              if ((image->columns % 2) != 0)
627
1.36k
                {
628
1.36k
                  byte=(size_t) ReadBlobByte(image);
629
1.36k
                  SetPixelIndex(image,(Quantum) ((byte >> 4) & 0xf),q);
630
1.36k
                  q+=(ptrdiff_t) GetPixelChannels(image);
631
1.36k
                }
632
3.93k
              for (x=0; x < (ssize_t) scanline_pad; x++)
633
2.07k
                (void) ReadBlobByte(image);
634
1.85k
              if (SyncAuthenticPixels(image,exception) == MagickFalse)
635
0
                break;
636
1.85k
              if (EOFBlob(image) != MagickFalse)
637
66
                break;
638
1.79k
              if (image->previous == (Image *) NULL)
639
718
                {
640
718
                  status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
641
718
                    image->rows-y-1,(MagickSizeType) image->rows);
642
718
                  if (status == MagickFalse)
643
0
                    break;
644
718
                }
645
1.79k
            }
646
243
            break;
647
0
          }
648
94
          case 8:
649
94
          {
650
            /*
651
              Convert PseudoColor scanline.
652
            */
653
821
            for (y=(ssize_t) image->rows-1; y >= 0; y--)
654
771
            {
655
771
              q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
656
771
              if (q == (Quantum *) NULL)
657
0
                break;
658
11.0k
              for (x=0; x < (ssize_t) image->columns; x++)
659
10.2k
              {
660
10.2k
                byte=(size_t) ReadBlobByte(image);
661
10.2k
                SetPixelIndex(image,(Quantum) byte,q);
662
10.2k
                q+=(ptrdiff_t) GetPixelChannels(image);
663
10.2k
              }
664
2.23k
              for (x=0; x < (ssize_t) scanline_pad; x++)
665
1.46k
                (void) ReadBlobByte(image);
666
771
              if (SyncAuthenticPixels(image,exception) == MagickFalse)
667
0
                break;
668
771
              if (EOFBlob(image) != MagickFalse)
669
44
                break;
670
727
              if (image->previous == (Image *) NULL)
671
474
                {
672
474
                  status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
673
474
                    image->rows-y-1,(MagickSizeType) image->rows);
674
474
                  if (status == MagickFalse)
675
0
                    break;
676
474
                }
677
727
            }
678
94
            break;
679
0
          }
680
95
          case 16:
681
95
          {
682
            /*
683
              Convert PseudoColor scanline.
684
            */
685
855
            for (y=(ssize_t) image->rows-1; y >= 0; y--)
686
802
            {
687
802
              q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
688
802
              if (q == (Quantum *) NULL)
689
0
                break;
690
7.17k
              for (x=0; x < (ssize_t) image->columns; x++)
691
6.36k
              {
692
6.36k
                byte=(size_t) ReadBlobByte(image);
693
6.36k
                byte|=((size_t) ReadBlobByte(image) << 8);
694
6.36k
                SetPixelIndex(image,(Quantum) byte,q);
695
6.36k
                q+=(ptrdiff_t) GetPixelChannels(image);
696
6.36k
              }
697
1.73k
              for (x=0; x < (ssize_t) scanline_pad; x++)
698
934
                (void) ReadBlobByte(image);
699
802
              if (SyncAuthenticPixels(image,exception) == MagickFalse)
700
0
                break;
701
802
              if (EOFBlob(image) != MagickFalse)
702
42
                break;
703
760
              if (image->previous == (Image *) NULL)
704
447
                {
705
447
                  status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
706
447
                    image->rows-y-1,(MagickSizeType) image->rows);
707
447
                  if (status == MagickFalse)
708
0
                    break;
709
447
                }
710
760
            }
711
95
            break;
712
0
          }
713
196
          case 24:
714
480
          case 32:
715
480
          {
716
            /*
717
              Convert DirectColor scanline.
718
            */
719
9.11k
            for (y=(ssize_t) image->rows-1; y >= 0; y--)
720
8.74k
            {
721
8.74k
              q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
722
8.74k
              if (q == (Quantum *) NULL)
723
0
                break;
724
69.3k
              for (x=0; x < (ssize_t) image->columns; x++)
725
60.5k
              {
726
60.5k
                SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
727
60.5k
                  ReadBlobByte(image)),q);
728
60.5k
                SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
729
60.5k
                  ReadBlobByte(image)),q);
730
60.5k
                SetPixelRed(image,ScaleCharToQuantum((unsigned char)
731
60.5k
                  ReadBlobByte(image)),q);
732
60.5k
                if (bits_per_pixel == 32)
733
21.2k
                  SetPixelAlpha(image,ScaleCharToQuantum((unsigned char)
734
21.2k
                    ReadBlobByte(image)),q);
735
60.5k
                q+=(ptrdiff_t) GetPixelChannels(image);
736
60.5k
              }
737
8.74k
              if (bits_per_pixel == 24)
738
2.28k
                for (x=0; x < (ssize_t) scanline_pad; x++)
739
1.24k
                  (void) ReadBlobByte(image);
740
8.74k
              if (SyncAuthenticPixels(image,exception) == MagickFalse)
741
0
                break;
742
8.74k
              if (EOFBlob(image) != MagickFalse)
743
115
                break;
744
8.63k
              if (image->previous == (Image *) NULL)
745
7.72k
                {
746
7.72k
                  status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
747
7.72k
                    image->rows-y-1,(MagickSizeType) image->rows);
748
7.72k
                  if (status == MagickFalse)
749
0
                    break;
750
7.72k
                }
751
8.63k
            }
752
480
            break;
753
196
          }
754
24
          default:
755
24
            ThrowICONReaderException(CorruptImageError,"ImproperImageHeader");
756
1.56k
        }
757
1.54k
        if ((image_info->ping == MagickFalse) && (bits_per_pixel <= 16))
758
1.06k
          (void) SyncImage(image,exception);
759
1.54k
        if (bits_per_pixel != 32)
760
1.26k
          {
761
            /*
762
              Read the ICON alpha mask.
763
            */
764
1.26k
            image->storage_class=DirectClass;
765
17.9k
            for (y=(ssize_t) image->rows-1; y >= 0; y--)
766
16.9k
            {
767
16.9k
              q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
768
16.9k
              if (q == (Quantum *) NULL)
769
0
                break;
770
74.7k
              for (x=0; x < ((ssize_t) image->columns-7); x+=8)
771
57.7k
              {
772
57.7k
                byte=(size_t) ReadBlobByte(image);
773
519k
                for (bit=0; bit < 8; bit++)
774
461k
                {
775
461k
                  SetPixelAlpha(image,(((byte & (0x80 >> bit)) != 0) ?
776
278k
                    TransparentAlpha : OpaqueAlpha),q);
777
461k
                  q+=(ptrdiff_t) GetPixelChannels(image);
778
461k
                }
779
57.7k
              }
780
16.9k
              if ((image->columns % 8) != 0)
781
14.6k
                {
782
14.6k
                  byte=(size_t) ReadBlobByte(image);
783
62.4k
                  for (bit=0; bit < (image->columns % 8); bit++)
784
47.8k
                  {
785
47.8k
                    SetPixelAlpha(image,(((byte & (0x80 >> bit)) != 0) ?
786
39.6k
                      TransparentAlpha : OpaqueAlpha),q);
787
47.8k
                    q+=(ptrdiff_t) GetPixelChannels(image);
788
47.8k
                  }
789
14.6k
                }
790
16.9k
              if ((image->columns % 32) != 0)
791
43.2k
                for (x=0; x < (ssize_t) ((32-(image->columns % 32))/8); x++)
792
27.2k
                  (void) ReadBlobByte(image);
793
16.9k
              if (SyncAuthenticPixels(image,exception) == MagickFalse)
794
0
                break;
795
16.9k
              if (EOFBlob(image) != MagickFalse)
796
323
                break;
797
16.9k
            }
798
1.26k
          }
799
1.54k
        if (EOFBlob(image) != MagickFalse)
800
351
          {
801
351
            ThrowFileException(exception,CorruptImageError,
802
351
              "UnexpectedEndOfFile",image->filename);
803
351
            break;
804
351
          }
805
1.54k
      }
806
    /*
807
      Proceed to next image.
808
    */
809
5.35k
    if (image_info->number_scenes != 0)
810
0
      if (image->scene >= (image_info->scene+image_info->number_scenes-1))
811
0
        break;
812
5.35k
    if (i < ((ssize_t) directory->count-1))
813
3.90k
      {
814
        /*
815
          Allocate next image structure.
816
        */
817
3.90k
        AcquireNextImage(image_info,image,exception);
818
3.90k
        if (GetNextImageInList(image) == (Image *) NULL)
819
0
          {
820
0
            status=MagickFalse;
821
0
            break;
822
0
          }
823
3.90k
        image=SyncNextImageInList(image);
824
3.90k
        status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
825
3.90k
          GetBlobSize(image));
826
3.90k
        if (status == MagickFalse)
827
0
          break;
828
3.90k
      }
829
5.35k
  }
830
2.21k
  directory=RelinquishIconDirectory(directory);
831
2.21k
  (void) CloseBlob(image);
832
2.21k
  if (status == MagickFalse)
833
0
    return(DestroyImageList(image));
834
2.21k
  return(GetFirstImageInList(image));
835
2.21k
}
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
9
{
862
9
  MagickInfo
863
9
    *entry;
864
865
9
  entry=AcquireMagickInfo("ICON","CUR","Microsoft icon");
866
9
  entry->decoder=(DecodeImageHandler *) ReadICONImage;
867
9
  entry->encoder=(EncodeImageHandler *) WriteICONImage;
868
9
  entry->flags^=CoderAdjoinFlag;
869
9
  entry->flags|=CoderDecoderSeekableStreamFlag;
870
9
  entry->flags|=CoderEncoderSeekableStreamFlag;
871
9
  (void) RegisterMagickInfo(entry);
872
9
  entry=AcquireMagickInfo("ICON","ICO","Microsoft icon");
873
9
  entry->decoder=(DecodeImageHandler *) ReadICONImage;
874
9
  entry->encoder=(EncodeImageHandler *) WriteICONImage;
875
9
  entry->flags|=CoderDecoderSeekableStreamFlag;
876
9
  entry->flags|=CoderEncoderSeekableStreamFlag;
877
9
  (void) RegisterMagickInfo(entry);
878
9
  entry=AcquireMagickInfo("ICON","ICN","Microsoft icon");
879
9
  entry->decoder=(DecodeImageHandler *) ReadICONImage;
880
9
  entry->encoder=(EncodeImageHandler *) WriteICONImage;
881
9
  entry->flags ^= CoderAdjoinFlag;
882
9
  entry->flags|=CoderDecoderSeekableStreamFlag;
883
9
  entry->flags|=CoderEncoderSeekableStreamFlag;
884
9
  (void) RegisterMagickInfo(entry);
885
9
  entry=AcquireMagickInfo("ICON","ICON","Microsoft icon");
886
9
  entry->decoder=(DecodeImageHandler *) ReadICONImage;
887
9
  entry->encoder=(EncodeImageHandler *) WriteICONImage;
888
9
  entry->flags^=CoderAdjoinFlag;
889
9
  entry->flags|=CoderDecoderSeekableStreamFlag;
890
9
  entry->flags|=CoderEncoderSeekableStreamFlag;
891
9
  (void) RegisterMagickInfo(entry);
892
9
  return(MagickImageCoderSignature);
893
9
}
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
307
{
1014
307
  if ((png_size != 0) && (image->columns >= png_size) &&
1015
0
      (image->rows >= png_size))
1016
0
    return(MagickTrue);
1017
307
  if ((image->columns > 256L) && (image->rows > 256L) &&
1018
0
      ((image->compression == UndefinedCompression) ||
1019
0
       (image->compression == ZipCompression)))
1020
0
    return(MagickTrue);
1021
307
  return(MagickFalse);
1022
307
}
1023
1024
static MagickBooleanType WriteICONImage(const ImageInfo *image_info,
1025
  Image *image,ExceptionInfo *exception)
1026
309
{
1027
309
#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
309
  const char
1036
309
    *option;
1037
1038
309
  const Quantum
1039
309
    *p;
1040
1041
309
  IconDirectory
1042
309
    *directory;
1043
1044
309
  Image
1045
309
    *images,
1046
309
    *frame;
1047
  
1048
309
  MagickBooleanType
1049
309
    adjoin,
1050
309
    status;
1051
1052
309
  MagickOffsetType
1053
309
    offset,
1054
309
    scene;
1055
1056
309
  size_t
1057
309
    bytes_per_line,
1058
309
    number_scenes,
1059
309
    png_size,
1060
309
    scanline_pad;
1061
1062
309
  ssize_t
1063
309
    i,
1064
309
    x,
1065
309
    y;
1066
1067
309
  unsigned char
1068
309
    *pixels,
1069
309
    *q;
1070
1071
  /*
1072
    Open output image file.
1073
  */
1074
309
  assert(image_info != (const ImageInfo *) NULL);
1075
309
  assert(image_info->signature == MagickCoreSignature);
1076
309
  assert(image != (Image *) NULL);
1077
309
  assert(image->signature == MagickCoreSignature);
1078
309
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),"%s",image->filename);
1079
309
  assert(exception != (ExceptionInfo *) NULL);
1080
309
  assert(exception->signature == MagickCoreSignature);
1081
309
  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
1082
309
  if (status == MagickFalse)
1083
0
    return(status);
1084
309
  images=(Image *) NULL;
1085
309
  adjoin=image_info->adjoin;
1086
309
  option=GetImageOption(image_info,"icon:auto-resize");
1087
309
  if (option != (const char *) NULL)
1088
0
    {
1089
0
      images=AutoResizeImage(image,option,&scene,exception);
1090
0
      if (images == (Image *) NULL)
1091
0
        ThrowWriterException(ImageError,"InvalidDimensions");
1092
0
      adjoin=MagickTrue;
1093
0
    }
1094
309
  else
1095
309
    {
1096
309
      scene=0;
1097
309
      frame=image;
1098
309
      do
1099
309
      {
1100
309
        if ((image->columns > 512L) || (image->rows > 512L))
1101
307
          ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
1102
307
        scene++;
1103
307
        frame=SyncNextImageInList(frame);
1104
307
      } while ((frame != (Image *) NULL) && (adjoin != MagickFalse));
1105
309
    }
1106
  /*
1107
    Dump out a ICON header template to be properly initialized later.
1108
  */
1109
307
  (void) WriteBlobLSBShort(image,0);
1110
307
  (void) WriteBlobLSBShort(image,1);
1111
307
  (void) WriteBlobLSBShort(image,(unsigned char) scene);
1112
307
  frame=(images != (Image *) NULL) ? images : image;
1113
307
  number_scenes=0;
1114
307
  do
1115
307
  {
1116
307
    number_scenes++;
1117
307
    (void) WriteBlobByte(image,0); /* width */
1118
307
    (void) WriteBlobByte(image,0); /* height */
1119
307
    (void) WriteBlobByte(image,0); /* colors */
1120
307
    (void) WriteBlobByte(image,0); /* reserved */
1121
307
    (void) WriteBlobLSBShort(image,0); /* planes */
1122
307
    (void) WriteBlobLSBShort(image,0); /* bits_per_pixel */
1123
307
    (void) WriteBlobLSBLong(image,0); /* size */
1124
307
    (void) WriteBlobLSBLong(image,0); /* offset */
1125
307
    frame=SyncNextImageInList(frame);
1126
307
  } while ((frame != (Image *) NULL) && (adjoin != MagickFalse));
1127
307
  scene=0;
1128
307
  frame=(images != (Image *) NULL) ? images : image;
1129
307
  directory=AcquireIconDirectory(number_scenes);
1130
307
  if (directory == (IconDirectory *) NULL)
1131
0
    {
1132
0
      if (images != (Image *) NULL) 
1133
0
        images=DestroyImageList(images);
1134
0
      ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1135
0
    }
1136
307
  png_size=0;
1137
307
  option=GetImageOption(image_info,"icon:png-compression-size");
1138
307
  if (option != (const char*)NULL)
1139
0
    png_size=(size_t) StringToDouble(option,(char**) NULL);
1140
307
  do
1141
307
  {
1142
307
    size_t
1143
307
      size;
1144
1145
307
    unsigned short
1146
307
      bits_per_pixel,
1147
307
      planes;
1148
1149
307
    if (ShouldCompressAsPng(frame,png_size) != MagickFalse)
1150
0
      {
1151
0
        Image
1152
0
          *write_image;
1153
1154
0
        ImageInfo
1155
0
          *write_info;
1156
1157
0
        size_t
1158
0
          length;
1159
1160
0
        unsigned char
1161
0
          *png;
1162
1163
0
        write_image=CloneImage(frame,0,0,MagickTrue,exception);
1164
0
        if (write_image == (Image *) NULL)
1165
0
          {
1166
0
            directory=RelinquishIconDirectory(directory);
1167
0
            images=DestroyImageList(images);
1168
0
            return(MagickFalse);
1169
0
          }
1170
0
        write_info=CloneImageInfo(image_info);
1171
0
        length=0;
1172
        /*
1173
          Don't write any ancillary chunks except for gAMA,tRNS.
1174
        */
1175
0
        (void) SetImageArtifact(write_image,"png:include-chunk",
1176
0
          "none,gama,tRNS");
1177
        /*
1178
          Only write PNG32 formatted PNG (32-bit RGBA), 8 bits per channel.
1179
        */
1180
0
        (void) CopyMagickString(write_info->magick,"PNG32",MagickPathExtent);
1181
0
        png=(unsigned char *) ImageToBlob(write_info,write_image,&length,
1182
0
          exception);
1183
0
        write_image=DestroyImageList(write_image);
1184
0
        write_info=DestroyImageInfo(write_info);
1185
0
        if (png == (unsigned char *) NULL)
1186
0
          {
1187
0
            directory=RelinquishIconDirectory(directory);
1188
0
            images=DestroyImageList(images);
1189
0
            return(MagickFalse);
1190
0
          }
1191
0
        directory->icons[scene]->width=0;
1192
0
        directory->icons[scene]->height=0;
1193
0
        directory->icons[scene]->colors=0;
1194
0
        directory->icons[scene]->reserved=0;
1195
0
        directory->icons[scene]->planes=1;
1196
0
        directory->icons[scene]->bits_per_pixel=32;
1197
0
        directory->icons[scene]->size=(size_t) length;
1198
0
        directory->icons[scene]->offset=(size_t) TellBlob(image);
1199
0
        (void) WriteBlob(image,(size_t) length,png);
1200
0
        png=(unsigned char *) RelinquishMagickMemory(png);
1201
0
      }
1202
307
    else
1203
307
      {
1204
307
        size_t
1205
307
          image_size,
1206
307
          number_colors,
1207
307
          x_pixels,
1208
307
          y_pixels;
1209
1210
307
        ssize_t
1211
307
          width,
1212
307
          height;
1213
1214
        /*
1215
          Initialize ICON raster file header.
1216
        */
1217
307
        (void) TransformImageColorspace(frame,sRGBColorspace,exception);
1218
307
        if ((frame->storage_class != DirectClass) && (frame->colors > 256))
1219
0
          (void) SetImageStorageClass(frame,DirectClass,exception);
1220
307
        if (frame->storage_class == DirectClass)
1221
167
          {
1222
            /*
1223
              Full color ICON raster.
1224
            */
1225
167
            number_colors=0;
1226
167
            bits_per_pixel=32;
1227
167
          }
1228
140
        else
1229
140
          {
1230
            /*
1231
              Colormapped ICON raster.
1232
            */
1233
140
            bits_per_pixel=8;
1234
140
            if (frame->colors <= 16)
1235
99
              bits_per_pixel=4;
1236
140
            if (frame->colors <= 2)
1237
39
              bits_per_pixel=1;
1238
140
            number_colors=(size_t) 1 << bits_per_pixel;
1239
140
            if (number_colors < frame->colors)
1240
0
              {
1241
0
                (void) SetImageStorageClass(frame,DirectClass,exception);
1242
0
                number_colors=0;
1243
0
                bits_per_pixel=(unsigned short) 24;
1244
0
              }
1245
140
          }
1246
307
        bytes_per_line=(((frame->columns*bits_per_pixel)+31U) &
1247
307
          ~31U) >> 3;
1248
307
        width=(ssize_t) frame->columns;
1249
307
        height=(ssize_t) frame->rows;
1250
307
        planes=1;
1251
307
        image_size=bytes_per_line*frame->rows;
1252
307
        size=40;
1253
307
        size+=(4*number_colors);
1254
307
        size+=image_size;
1255
307
        size+=(size_t) ((((width+31U) & ~31U) >> 3)*height);
1256
307
        x_pixels=0;
1257
307
        y_pixels=0;
1258
307
        switch (frame->units)
1259
307
        {
1260
307
          case UndefinedResolution:
1261
307
          case PixelsPerInchResolution:
1262
307
          {
1263
307
            x_pixels=(size_t) (100.0*frame->resolution.x/2.54);
1264
307
            y_pixels=(size_t) (100.0*frame->resolution.y/2.54);
1265
307
            break;
1266
307
          }
1267
0
          case PixelsPerCentimeterResolution:
1268
0
          {
1269
0
            x_pixels=(size_t) (100.0*frame->resolution.x);
1270
0
            y_pixels=(size_t) (100.0*frame->resolution.y);
1271
0
            break;
1272
307
          }
1273
307
        }
1274
        /*
1275
          Convert MIFF to ICON raster pixels.
1276
        */
1277
307
        pixels=(unsigned char *) AcquireQuantumMemory(image_size,
1278
307
          sizeof(*pixels));
1279
307
        if (pixels == (unsigned char *) NULL)
1280
307
          ThrowICONWriterException(ResourceLimitError,"MemoryAllocationFailed");
1281
307
        (void) memset(pixels,0,image_size);
1282
307
        switch (bits_per_pixel)
1283
307
        {
1284
39
          case 1:
1285
39
          {
1286
39
            size_t
1287
39
              bit,
1288
39
              byte;
1289
1290
            /*
1291
              Convert PseudoClass image to a ICON monochrome image.
1292
            */
1293
1.20k
            for (y=0; y < (ssize_t) frame->rows; y++)
1294
1.16k
            {
1295
1.16k
              p=GetVirtualPixels(frame,0,y,frame->columns,1,exception);
1296
1.16k
              if (p == (const Quantum *) NULL)
1297
0
                break;
1298
1.16k
              q=pixels+((ssize_t) frame->rows-y-1)*(ssize_t) bytes_per_line;
1299
1.16k
              bit=0;
1300
1.16k
              byte=0;
1301
5.36k
              for (x=0; x < (ssize_t) frame->columns; x++)
1302
4.19k
              {
1303
4.19k
                byte<<=1;
1304
4.19k
                byte|=(size_t) (GetPixelIndex(frame,p) != 0 ? 0x01 : 0x00);
1305
4.19k
                bit++;
1306
4.19k
                if (bit == 8)
1307
358
                  {
1308
358
                    *q++=(unsigned char) byte;
1309
358
                    bit=0;
1310
358
                    byte=0;
1311
358
                  }
1312
4.19k
                p+=(ptrdiff_t) GetPixelChannels(frame);
1313
4.19k
              }
1314
1.16k
              if (bit != 0)
1315
945
                *q++=(unsigned char) (byte << (8-bit));
1316
1.16k
              if (frame->previous == (Image *) NULL)
1317
1.16k
                {
1318
1.16k
                  status=SetImageProgress(frame,SaveImageTag,y,frame->rows);
1319
1.16k
                  if (status == MagickFalse)
1320
0
                    break;
1321
1.16k
                }
1322
1.16k
            }
1323
39
            break;
1324
0
          }
1325
60
          case 4:
1326
60
          {
1327
60
            size_t
1328
60
              nibble,
1329
60
              byte;
1330
1331
            /*
1332
              Convert PseudoClass image to a ICON monochrome image.
1333
            */
1334
1.37k
            for (y=0; y < (ssize_t) frame->rows; y++)
1335
1.31k
            {
1336
1.31k
              p=GetVirtualPixels(frame,0,y,frame->columns,1,exception);
1337
1.31k
              if (p == (const Quantum *) NULL)
1338
0
                break;
1339
1.31k
              q=pixels+((ssize_t) frame->rows-y-1)*(ssize_t) bytes_per_line;
1340
1.31k
              nibble=0;
1341
1.31k
              byte=0;
1342
25.1k
              for (x=0; x < (ssize_t) frame->columns; x++)
1343
23.7k
              {
1344
23.7k
                byte<<=4;
1345
23.7k
                byte|=((size_t) GetPixelIndex(frame,p) & 0x0f);
1346
23.7k
                nibble++;
1347
23.7k
                if (nibble == 2)
1348
11.7k
                  {
1349
11.7k
                    *q++=(unsigned char) byte;
1350
11.7k
                    nibble=0;
1351
11.7k
                    byte=0;
1352
11.7k
                  }
1353
23.7k
                p+=(ptrdiff_t) GetPixelChannels(frame);
1354
23.7k
              }
1355
1.31k
              if (nibble != 0)
1356
355
                *q++=(unsigned char) (byte << 4);
1357
1.31k
              if (frame->previous == (Image *) NULL)
1358
1.31k
                {
1359
1.31k
                  status=SetImageProgress(frame,SaveImageTag,y,frame->rows);
1360
1.31k
                  if (status == MagickFalse)
1361
0
                    break;
1362
1.31k
                }
1363
1.31k
            }
1364
60
            break;
1365
0
          }
1366
41
          case 8:
1367
41
          {
1368
            /*
1369
              Convert PseudoClass packet to ICON pixel.
1370
            */
1371
1.48k
            for (y=0; y < (ssize_t) frame->rows; y++)
1372
1.44k
            {
1373
1.44k
              p=GetVirtualPixels(frame,0,y,frame->columns,1,exception);
1374
1.44k
              if (p == (const Quantum *) NULL)
1375
0
                break;
1376
1.44k
              q=pixels+((ssize_t) frame->rows-y-1)*(ssize_t) bytes_per_line;
1377
4.17k
              for (x=0; x < (ssize_t) frame->columns; x++)
1378
2.72k
              {
1379
2.72k
                *q++=(unsigned char) GetPixelIndex(frame,p);
1380
2.72k
                p+=(ptrdiff_t) GetPixelChannels(frame);
1381
2.72k
              }
1382
1.44k
              if (frame->previous == (Image *) NULL)
1383
1.44k
                {
1384
1.44k
                  status=SetImageProgress(frame,SaveImageTag,y,frame->rows);
1385
1.44k
                  if (status == MagickFalse)
1386
0
                    break;
1387
1.44k
                }
1388
1.44k
            }
1389
41
            break;
1390
0
          }
1391
0
          case 24:
1392
167
          case 32:
1393
167
          {
1394
            /*
1395
              Convert DirectClass packet to ICON BGR888 or BGRA8888 pixel.
1396
            */
1397
5.22k
            for (y=0; y < (ssize_t) frame->rows; y++)
1398
5.05k
            {
1399
5.05k
              p=GetVirtualPixels(frame,0,y,frame->columns,1,exception);
1400
5.05k
              if (p == (const Quantum *) NULL)
1401
0
                break;
1402
5.05k
              q=pixels+((ssize_t) frame->rows-y-1)*(ssize_t) bytes_per_line;
1403
200k
              for (x=0; x < (ssize_t) frame->columns; x++)
1404
195k
              {
1405
195k
                *q++=ScaleQuantumToChar(GetPixelBlue(frame,p));
1406
195k
                *q++=ScaleQuantumToChar(GetPixelGreen(frame,p));
1407
195k
                *q++=ScaleQuantumToChar(GetPixelRed(frame,p));
1408
195k
                if (frame->alpha_trait == UndefinedPixelTrait)
1409
273
                  *q++=ScaleQuantumToChar(QuantumRange);
1410
194k
                else
1411
194k
                  *q++=ScaleQuantumToChar(GetPixelAlpha(frame,p));
1412
195k
                p+=(ptrdiff_t) GetPixelChannels(frame);
1413
195k
              }
1414
5.05k
              if (bits_per_pixel == 24)
1415
0
                for (x=3L*(ssize_t) frame->columns; x < (ssize_t) bytes_per_line; x++)
1416
0
                  *q++=0x00;
1417
5.05k
              if (frame->previous == (Image *) NULL)
1418
5.05k
                {
1419
5.05k
                  status=SetImageProgress(frame,SaveImageTag,y,frame->rows);
1420
5.05k
                  if (status == MagickFalse)
1421
0
                    break;
1422
5.05k
                }
1423
5.05k
            }
1424
167
            break;
1425
0
          }
1426
307
        }
1427
        /*
1428
          Write 40-byte version 3+ bitmap header.
1429
        */
1430
        /* The value 0 is accepted as representing a width of 256 */
1431
307
        directory->icons[scene]->width=(unsigned char) width % 256;
1432
307
        directory->icons[scene]->height=(unsigned char) height % 256;
1433
307
        directory->icons[scene]->colors=(unsigned char) number_colors;
1434
307
        directory->icons[scene]->reserved=0;
1435
307
        directory->icons[scene]->planes=planes;
1436
307
        directory->icons[scene]->bits_per_pixel=bits_per_pixel;
1437
307
        directory->icons[scene]->size=size;
1438
307
        directory->icons[scene]->offset=(size_t) TellBlob(image);
1439
307
        (void) WriteBlobLSBLong(image,(unsigned int) 40);
1440
307
        (void) WriteBlobLSBLong(image,(unsigned int) width);
1441
307
        (void) WriteBlobLSBLong(image,(unsigned int) height*2);
1442
307
        (void) WriteBlobLSBShort(image,planes);
1443
307
        (void) WriteBlobLSBShort(image,bits_per_pixel);
1444
307
        (void) WriteBlobLSBLong(image,(unsigned int) IconRgbCompression);
1445
307
        (void) WriteBlobLSBLong(image,(unsigned int) image_size);
1446
307
        (void) WriteBlobLSBLong(image,(unsigned int) x_pixels);
1447
307
        (void) WriteBlobLSBLong(image,(unsigned int) y_pixels);
1448
307
        (void) WriteBlobLSBLong(image,(unsigned int) number_colors);
1449
307
        (void) WriteBlobLSBLong(image,(unsigned int) number_colors);
1450
307
        if (frame->storage_class == PseudoClass)
1451
140
          {
1452
140
            unsigned char
1453
140
              *icon_colormap;
1454
1455
            /*
1456
              Dump colormap to file.
1457
            */
1458
140
            icon_colormap=(unsigned char *) AcquireQuantumMemory((size_t) 1UL
1459
140
              << bits_per_pixel,4UL*sizeof(*icon_colormap));
1460
140
            if (icon_colormap == (unsigned char *) NULL)
1461
0
              ThrowICONWriterException(ResourceLimitError,
1462
140
                "MemoryAllocationFailed");
1463
140
            q=icon_colormap;
1464
2.66k
            for (i=0; i < (ssize_t) frame->colors; i++)
1465
2.52k
            {
1466
2.52k
              *q++=ScaleQuantumToChar((Quantum) frame->colormap[i].blue);
1467
2.52k
              *q++=ScaleQuantumToChar((Quantum) frame->colormap[i].green);
1468
2.52k
              *q++=ScaleQuantumToChar((Quantum) frame->colormap[i].red);
1469
2.52k
              *q++=(unsigned char) 0x00;
1470
2.52k
            }
1471
9.14k
            for ( ; i < (ssize_t) 1UL << bits_per_pixel; i++)
1472
9.00k
            {
1473
9.00k
              *q++=(unsigned char) 0x00;
1474
9.00k
              *q++=(unsigned char) 0x00;
1475
9.00k
              *q++=(unsigned char) 0x00;
1476
9.00k
              *q++=(unsigned char) 0x00;
1477
9.00k
            }
1478
140
            (void) WriteBlob(image,(size_t) (4UL*(1UL << bits_per_pixel)),
1479
140
              icon_colormap);
1480
140
            icon_colormap=(unsigned char *) RelinquishMagickMemory(
1481
140
              icon_colormap);
1482
140
          }
1483
307
        (void) WriteBlob(image,image_size,pixels);
1484
307
        pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1485
        /*
1486
          Write matte mask.
1487
        */
1488
307
        scanline_pad=(((frame->columns+31U) & ~31U)-frame->columns) >> 3;
1489
9.29k
        for (y=((ssize_t) frame->rows - 1); y >= 0; y--)
1490
8.98k
        {
1491
8.98k
          unsigned char
1492
8.98k
            bit,
1493
8.98k
            byte;
1494
1495
8.98k
          p=GetVirtualPixels(frame,0,y,frame->columns,1,exception);
1496
8.98k
          if (p == (const Quantum *) NULL)
1497
0
            break;
1498
8.98k
          bit=0;
1499
8.98k
          byte=0;
1500
234k
          for (x=0; x < (ssize_t) frame->columns; x++)
1501
225k
          {
1502
225k
            byte<<=1;
1503
225k
            if ((frame->alpha_trait != UndefinedPixelTrait) &&
1504
225k
                (GetPixelAlpha(frame,p) == (Quantum) TransparentAlpha))
1505
81.7k
              byte|=0x01;
1506
225k
            bit++;
1507
225k
            if (bit == 8)
1508
26.8k
              {
1509
26.8k
                (void) WriteBlobByte(image,(unsigned char) byte);
1510
26.8k
                bit=0;
1511
26.8k
                byte=0;
1512
26.8k
              }
1513
225k
            p+=(ptrdiff_t) GetPixelChannels(frame);
1514
225k
          }
1515
8.98k
          if (bit != 0)
1516
7.06k
            (void) WriteBlobByte(image,(unsigned char) (byte << (8-bit)));
1517
29.8k
          for (i=0; i < (ssize_t) scanline_pad; i++)
1518
20.8k
            (void) WriteBlobByte(image,(unsigned char) 0);
1519
8.98k
        }
1520
307
      }
1521
307
    if (GetNextImageInList(frame) == (Image *) NULL)
1522
307
      break;
1523
0
    status=SetImageProgress(frame,SaveImagesTag,scene++,number_scenes);
1524
0
    if (status == MagickFalse)
1525
0
      break;
1526
0
    frame=SyncNextImageInList(frame);
1527
0
  } while ((frame != (Image *) NULL) && (adjoin != MagickFalse));
1528
307
  offset=SeekBlob(image,0,SEEK_SET);
1529
307
  (void) offset;
1530
307
  (void) WriteBlobLSBShort(image,0);
1531
307
  (void) WriteBlobLSBShort(image,1);
1532
307
  (void) WriteBlobLSBShort(image,(unsigned short) (scene+1));
1533
307
  scene=0;
1534
307
  frame=(images != (Image *) NULL) ? images : image;
1535
307
  do
1536
307
  {
1537
307
    (void) WriteBlobByte(image,directory->icons[scene]->width);
1538
307
    (void) WriteBlobByte(image,directory->icons[scene]->height);
1539
307
    (void) WriteBlobByte(image,directory->icons[scene]->colors);
1540
307
    (void) WriteBlobByte(image,directory->icons[scene]->reserved);
1541
307
    (void) WriteBlobLSBShort(image,directory->icons[scene]->planes);
1542
307
    (void) WriteBlobLSBShort(image,directory->icons[scene]->bits_per_pixel);
1543
307
    (void) WriteBlobLSBLong(image,(unsigned int)
1544
307
      directory->icons[scene]->size);
1545
307
    (void) WriteBlobLSBLong(image,(unsigned int)
1546
307
      directory->icons[scene]->offset);
1547
307
    scene++;
1548
307
    frame=SyncNextImageInList(frame);
1549
307
  } while ((frame != (Image *) NULL) && (adjoin != MagickFalse));
1550
307
  directory=RelinquishIconDirectory(directory);
1551
307
  (void) CloseBlob(image);
1552
307
  images=DestroyImageList(images);
1553
307
  return(MagickTrue);
1554
307
}