/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 | } |