Coverage Report

Created: 2025-11-16 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/graphicsmagick/coders/icon.c
Line
Count
Source
1
/*
2
% Copyright (C) 2003-2022 GraphicsMagick Group
3
% Copyright (C) 2002 ImageMagick Studio
4
% Copyright 1991-1999 E. I. du Pont de Nemours and Company
5
%
6
% This program is covered by multiple licenses, which are described in
7
% Copyright.txt. You should have received a copy of Copyright.txt with this
8
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
9
%
10
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11
%                                                                             %
12
%                                                                             %
13
%                                                                             %
14
%                        IIIII   CCCC   OOO   N   N                           %
15
%                          I    C      O   O  NN  N                           %
16
%                          I    C      O   O  N N N                           %
17
%                          I    C      O   O  N  NN                           %
18
%                        IIIII   CCCC   OOO   N   N                           %
19
%                                                                             %
20
%                                                                             %
21
%                   Read Microsoft Windows Icon Format.                       %
22
%                                                                             %
23
%                                                                             %
24
%                              Software Design                                %
25
%                                John Cristy                                  %
26
%                                 July 1992                                   %
27
%                                                                             %
28
%                                Re-written                                   %
29
%                              Bob Friesenhahn                                %
30
%                                January 2015                                 %
31
%                                                                             %
32
%                                                                             %
33
%                                                                             %
34
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35
%
36
%
37
*/
38

39
/*
40
  Include declarations.
41
*/
42
#include "magick/studio.h"
43
#include "magick/blob.h"
44
#include "magick/colormap.h"
45
#include "magick/log.h"
46
#include "magick/magick.h"
47
#include "magick/monitor.h"
48
#include "magick/pixel_cache.h"
49
#include "magick/utility.h"
50
51
/* http://msdn.microsoft.com/en-us/library/ms997538.aspx */
52

53
9.92k
#define MaxIcons  256
54
/*
55
  Icon Entry
56
*/
57
typedef struct _IconDirEntry
58
{
59
  magick_uint8_t
60
    width,
61
    height,
62
    colors,
63
    reserved;
64
65
  magick_uint16_t
66
    planes,
67
    bits_per_pixel;
68
69
  magick_uint32_t
70
    size,
71
    offset;
72
} IconDirEntry;
73
74
/*
75
  Icon Directory
76
*/
77
typedef struct _IconFile
78
{
79
  magick_uint16_t
80
    reserved,
81
    resource_type, /* 1 = ICON, 2 = CURSOR */
82
    count;
83
84
  IconDirEntry
85
    directory[MaxIcons];
86
} IconFile;
87

88
static void LogICONDirEntry(const unsigned int i, const IconDirEntry *icon_entry)
89
13.3k
{
90
13.3k
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
91
13.3k
                        "IconDirEntry[%u]:\n"
92
13.3k
                        "    Width:  %u\n"
93
13.3k
                        "    Height: %u\n"
94
13.3k
                        "    Colors: %u\n"
95
13.3k
                        "    Reserved: %u\n"
96
13.3k
                        "    Planes: %u\n"
97
13.3k
                        "    BPP:    %u\n"
98
13.3k
                        "    Size:   %u\n"
99
13.3k
                        "    Offset: %u",
100
13.3k
                        i,
101
13.3k
                        (unsigned int) icon_entry->width,
102
13.3k
                        (unsigned int) icon_entry->height,
103
13.3k
                        (unsigned int) icon_entry->colors,
104
13.3k
                        (unsigned int) icon_entry->reserved,
105
13.3k
                        (unsigned int) icon_entry->planes,
106
13.3k
                        (unsigned int) icon_entry->bits_per_pixel,
107
13.3k
                        (unsigned int) icon_entry->size,
108
13.3k
                        (unsigned int) icon_entry->offset
109
13.3k
                        );
110
13.3k
}
111
static void LogICONFile(const IconFile *icon_file)
112
9.99k
{
113
9.99k
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
114
9.99k
                        "IconFile:\n"
115
9.99k
                        "    Reserved:     %u\n"
116
9.99k
                        "    ResourceType: %u\n"
117
9.99k
                        "    Count:        %u",
118
9.99k
                        (unsigned int) icon_file->reserved,
119
9.99k
                        (unsigned int) icon_file->resource_type,
120
9.99k
                        (unsigned int) icon_file->count
121
9.99k
                        );
122
9.99k
}
123

124
/*
125
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
126
%                                                                             %
127
%                                                                             %
128
%                                                                             %
129
%   R e a d I C O N I m a g e                                                 %
130
%                                                                             %
131
%                                                                             %
132
%                                                                             %
133
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
134
%
135
%  Method ReadIconImage reads a Microsoft icon image file and returns it.  It
136
%  allocates the memory necessary for the new Image structure and returns a
137
%  pointer to the new image.
138
%
139
%  The format of the ReadIconImage method is:
140
%
141
%      Image *ReadIconImage(const ImageInfo *image_info,
142
%        ExceptionInfo *exception)
143
%
144
%  A description of each parameter follows:
145
%
146
%    o image:  Method ReadIconImage returns a pointer to the image after
147
%      reading.  A null image is returned if there is a memory shortage or
148
%      if the image cannot be read.
149
%
150
%    o image_info: Specifies a pointer to a ImageInfo structure.
151
%
152
%    o exception: return any errors or warnings in this structure.
153
%
154
%
155
*/
156
static Image *ReadIconImage(const ImageInfo *image_info,
157
  ExceptionInfo *exception)
158
10.0k
{
159
10.0k
  IconFile
160
10.0k
    icon_file;
161
162
10.0k
  Image
163
10.0k
    *image;
164
165
10.0k
  unsigned char
166
10.0k
    *data;
167
168
10.0k
  size_t
169
10.0k
    data_alloc_size;
170
171
10.0k
  register long
172
10.0k
    i;
173
174
10.0k
  unsigned int
175
10.0k
    status;
176
177
  /*
178
    Open image file.
179
  */
180
10.0k
  assert(image_info != (const ImageInfo *) NULL);
181
10.0k
  assert(image_info->signature == MagickSignature);
182
10.0k
  assert(exception != (ExceptionInfo *) NULL);
183
10.0k
  assert(exception->signature == MagickSignature);
184
10.0k
  image=AllocateImage(image_info);
185
10.0k
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
186
10.0k
  if (status == False)
187
10.0k
    ThrowReaderException(FileOpenError,UnableToOpenFile,image);
188
  /*
189
    Read and validate icon header
190
  */
191
10.0k
  icon_file.reserved=ReadBlobLSBShort(image);
192
10.0k
  icon_file.resource_type=ReadBlobLSBShort(image);
193
10.0k
  icon_file.count=ReadBlobLSBShort(image);
194
10.0k
  if (EOFBlob(image))
195
9.99k
    ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
196
9.99k
  if (image->logging)
197
9.99k
    LogICONFile(&icon_file);
198
  /*
199
    Windows ICO and CUR formats are essentially the same except that
200
    .CUR uses resource_type==1 while .ICO uses resource_type=2.
201
  */
202
9.99k
  if ((icon_file.reserved != 0) ||
203
9.96k
      ((icon_file.resource_type != 1) &&
204
5.18k
       (icon_file.resource_type != 2)) ||
205
9.92k
      (icon_file.count > MaxIcons))
206
9.91k
    ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
207
  /*
208
    Read and validate icon directory.
209
  */
210
9.91k
  data_alloc_size=0;
211
9.91k
  data=(unsigned char *) NULL;
212
23.1k
  for (i=0; i < icon_file.count; i++)
213
13.4k
  {
214
    /* 0 - 255, 0 means 256! */
215
13.4k
    icon_file.directory[i].width=ReadBlobByte(image);
216
    /* 0 - 255, 0 means 256! */
217
13.4k
    icon_file.directory[i].height=ReadBlobByte(image);
218
13.4k
    icon_file.directory[i].colors=ReadBlobByte(image);
219
13.4k
    icon_file.directory[i].reserved=ReadBlobByte(image);
220
13.4k
    icon_file.directory[i].planes=ReadBlobLSBShort(image);
221
13.4k
    icon_file.directory[i].bits_per_pixel=ReadBlobLSBShort(image);
222
13.4k
    icon_file.directory[i].size=ReadBlobLSBLong(image);
223
13.4k
    icon_file.directory[i].offset=ReadBlobLSBLong(image);
224
13.4k
    if (EOFBlob(image))
225
13.3k
      ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
226
13.3k
    if (image->logging)
227
13.3k
      LogICONDirEntry(i,&icon_file.directory[i]);
228
13.3k
    if (
229
        /*
230
          Restrict allocation size
231
        */
232
13.3k
        (icon_file.directory[i].size < 20) ||
233
13.3k
        (icon_file.directory[i].size > 256+256*256*2*4) ||
234
235
        /*
236
          planes
237
238
          In ICO format (1): Specifies color planes. Should be 0 or 1.
239
240
          In CUR format (2): Specifies the horizontal coordinates of
241
          the hotspot in number of pixels from the left.
242
        */
243
13.2k
        ((icon_file.resource_type == 1) &&
244
6.76k
         (icon_file.directory[i].planes != 0) &&
245
1.52k
         (icon_file.directory[i].planes != 1)) ||
246
        /*
247
          bits_per_pixel
248
249
          In ICO format (1): Specifies bits per pixel.
250
251
          In CUR format (2): Specifies the vertical coordinates of the
252
          hotspot in number of pixels from the top.
253
         */
254
13.2k
        ((icon_file.resource_type == 1) &&
255
6.73k
         ((icon_file.directory[i].bits_per_pixel >= 8) &&
256
3.85k
          (icon_file.directory[i].colors != 0))) ||
257
258
13.2k
        (icon_file.directory[i].size == 0) ||
259
13.2k
        (icon_file.directory[i].offset == 0))
260
13.2k
      ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
261
13.2k
    data_alloc_size=Max(data_alloc_size,icon_file.directory[i].size);
262
13.2k
  }
263
9.63k
  data=MagickAllocateResourceLimitedMemory(unsigned char *,data_alloc_size);
264
9.63k
  if (data == (unsigned char *) NULL)
265
9.63k
    ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image);
266
9.63k
  for (i=0; i < icon_file.count; i++)
267
9.63k
  {
268
    /*
269
      Verify and read icons
270
    */
271
9.63k
    Image
272
9.63k
      *icon_image;
273
274
9.63k
    ImageInfo
275
9.63k
      *read_info;
276
277
9.63k
    char
278
9.63k
      dib_size[MaxTextExtent],
279
9.63k
      format[MaxTextExtent];
280
281
9.63k
    size_t
282
9.63k
      count;
283
284
9.63k
    if (SeekBlob(image,icon_file.directory[i].offset,SEEK_SET) !=
285
9.63k
        (magick_off_t) icon_file.directory[i].offset)
286
0
      {
287
0
        if (image->logging)
288
0
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
289
0
                                "Failed to seek to offset %u",
290
0
                                icon_file.directory[i].offset);
291
0
        MagickFreeResourceLimitedMemory(data);
292
0
        ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
293
0
      }
294
9.63k
    if ((count=ReadBlob(image,icon_file.directory[i].size,data)) != icon_file.directory[i].size)
295
217
      {
296
217
        if (image->logging)
297
217
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
298
217
                                  "Read %" MAGICK_SIZE_T_F  "u bytes from blob"
299
217
                                  " (expected %" MAGICK_SIZE_T_F  "u bytes)",
300
217
                                  (MAGICK_SIZE_T) count,
301
217
                                  (MAGICK_SIZE_T) icon_file.directory[i].size);
302
217
        MagickFreeResourceLimitedMemory(data);
303
217
        ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
304
0
      }
305
9.41k
    format[0]='\0';
306
9.41k
    if (memcmp(data,"\050\000\000\000",4) == 0)
307
1.77k
      (void) strlcpy(format,"ICODIB",sizeof(format));
308
7.64k
    else if (memcmp(data,"\211PNG\r\n\032\n",8) == 0)
309
7.51k
      (void) strlcpy(format,"PNG",sizeof(format));
310
9.41k
    if (format[0] == '\0')
311
127
      {
312
127
        MagickFreeResourceLimitedMemory(data);
313
127
        ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
314
0
      }
315
9.29k
    if (image->logging)
316
9.29k
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
317
9.29k
                            "Reading icon using %s format",
318
9.29k
                            format);
319
9.29k
    icon_image=(Image *) NULL;
320
9.29k
    read_info=CloneImageInfo(image_info);
321
9.29k
    if (read_info != (const ImageInfo *) NULL)
322
9.29k
      {
323
9.29k
        (void) strlcpy(read_info->magick,format,MaxTextExtent);
324
9.29k
        (void) strlcpy(read_info->filename,format,MaxTextExtent);
325
9.29k
        (void) strlcat(read_info->filename,":",MaxTextExtent);
326
9.29k
        FormatString(dib_size,"%ux%u",
327
9.29k
                     icon_file.directory[i].width == 0 ? 256 :
328
9.29k
                     icon_file.directory[i].width,
329
9.29k
                     icon_file.directory[i].height == 0 ? 256 :
330
9.29k
                     icon_file.directory[i].height);
331
9.29k
        (void) CloneString(&read_info->size,dib_size);
332
9.29k
        icon_image=BlobToImage(read_info,data,icon_file.directory[i].size,exception);
333
9.29k
        DestroyImageInfo(read_info);
334
9.29k
        read_info=(ImageInfo *) NULL;
335
9.29k
      }
336
9.29k
    if (icon_image == (Image *) NULL)
337
7.92k
      {
338
7.92k
        MagickFreeResourceLimitedMemory(data);
339
7.92k
        DestroyImageList(image);
340
7.92k
        return((Image *) NULL);
341
7.92k
      }
342
1.36k
    DestroyBlob(icon_image);
343
1.36k
    icon_image->blob=ReferenceBlob(image->blob);
344
1.36k
    icon_image->scene=i;
345
1.36k
    (void) strlcpy(icon_image->magick,image_info->magick,
346
1.36k
                   sizeof(icon_image->magick));
347
1.36k
    ReplaceImageInList(&image,icon_image);
348
1.36k
    StopTimer(&image->timer);
349
350
    /*
351
      Proceed to next image.
352
    */
353
1.36k
    if (image_info->subrange != 0)
354
1.36k
      if (image->scene >= (image_info->subimage+image_info->subrange-1))
355
1.36k
        break;
356
0
    if (i < (long) (icon_file.count-1))
357
0
      {
358
        /*
359
          Allocate next image structure.
360
        */
361
0
        AllocateNextImage(image_info,image);
362
0
        if (image->next == (Image *) NULL)
363
0
          {
364
0
            DestroyImageList(image);
365
0
            return((Image *) NULL);
366
0
          }
367
0
        image=SyncNextImageInList(image);
368
0
        if (!MagickMonitorFormatted(TellBlob(image),GetBlobSize(image),exception,
369
0
                                    LoadImagesText,image->filename))
370
0
          break;
371
0
      }
372
0
  } /* for (i=0; i < icon_file.count; i++) */
373
1.36k
  while (image->previous != (Image *) NULL)
374
0
    image=image->previous;
375
1.36k
  CloseBlob(image);
376
1.36k
  MagickFreeResourceLimitedMemory(data);
377
1.36k
  return(image);
378
9.63k
}
379

380
/*
381
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
382
%                                                                             %
383
%                                                                             %
384
%                                                                             %
385
%   R e g i s t e r I C O N I m a g e                                         %
386
%                                                                             %
387
%                                                                             %
388
%                                                                             %
389
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
390
%
391
%  Method RegisterICONImage adds attributes for the Icon image format to
392
%  the list of supported formats.  The attributes include the image format
393
%  tag, a method to read and/or write the format, whether the format
394
%  supports the saving of more than one frame to the same file or blob,
395
%  whether the format supports native in-memory I/O, and a brief
396
%  description of the format.
397
%
398
%  The format of the RegisterICONImage method is:
399
%
400
%      RegisterICONImage(void)
401
%
402
*/
403
ModuleExport void RegisterICONImage(void)
404
2
{
405
2
  MagickInfo
406
2
    *entry;
407
408
2
  entry=SetMagickInfo("CUR");
409
2
  entry->decoder=(DecoderHandler) ReadIconImage;
410
2
  entry->adjoin=False;
411
2
  entry->seekable_stream=True;
412
2
  entry->description="Microsoft Cursor Icon";
413
2
  entry->module="ICON";
414
2
  (void) RegisterMagickInfo(entry);
415
416
2
  entry=SetMagickInfo("ICO");
417
2
  entry->decoder=(DecoderHandler) ReadIconImage;
418
2
  entry->adjoin=False;
419
2
  entry->seekable_stream=True;
420
2
  entry->description="Microsoft Icon";
421
2
  entry->module="ICON";
422
2
  (void) RegisterMagickInfo(entry);
423
424
2
  entry=SetMagickInfo("ICON");
425
2
  entry->decoder=(DecoderHandler) ReadIconImage;
426
2
  entry->adjoin=False;
427
2
  entry->seekable_stream=True;
428
2
  entry->description="Microsoft Icon";
429
2
  entry->module="ICON";
430
2
  (void) RegisterMagickInfo(entry);
431
2
}
432

433
/*
434
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
435
%                                                                             %
436
%                                                                             %
437
%                                                                             %
438
%   U n r e g i s t e r I C O N I m a g e                                     %
439
%                                                                             %
440
%                                                                             %
441
%                                                                             %
442
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
443
%
444
%  Method UnregisterICONImage removes format registrations made by the
445
%  ICON module from the list of supported formats.
446
%
447
%  The format of the UnregisterICONImage method is:
448
%
449
%      UnregisterICONImage(void)
450
%
451
*/
452
ModuleExport void UnregisterICONImage(void)
453
0
{
454
0
  (void) UnregisterMagickInfo("CUR");
455
0
  (void) UnregisterMagickInfo("ICO");
456
0
  (void) UnregisterMagickInfo("ICON");
457
0
}