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