2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Compuserv Graphics Interchange Format %
20 % Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
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. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
42 #include "MagickCore/studio.h"
43 #include "MagickCore/attribute.h"
44 #include "MagickCore/blob.h"
45 #include "MagickCore/blob-private.h"
46 #include "MagickCore/cache.h"
47 #include "MagickCore/color.h"
48 #include "MagickCore/color-private.h"
49 #include "MagickCore/colormap.h"
50 #include "MagickCore/colormap-private.h"
51 #include "MagickCore/colorspace.h"
52 #include "MagickCore/colorspace-private.h"
53 #include "MagickCore/exception.h"
54 #include "MagickCore/exception-private.h"
55 #include "MagickCore/image.h"
56 #include "MagickCore/image-private.h"
57 #include "MagickCore/list.h"
58 #include "MagickCore/profile.h"
59 #include "MagickCore/magick.h"
60 #include "MagickCore/memory_.h"
61 #include "MagickCore/monitor.h"
62 #include "MagickCore/monitor-private.h"
63 #include "MagickCore/option.h"
64 #include "MagickCore/pixel.h"
65 #include "MagickCore/pixel-accessor.h"
66 #include "MagickCore/property.h"
67 #include "MagickCore/quantize.h"
68 #include "MagickCore/quantum-private.h"
69 #include "MagickCore/static.h"
70 #include "MagickCore/string_.h"
71 #include "MagickCore/module.h"
76 #define MaximumLZWBits 12
77 #define MaximumLZWCode (1UL << MaximumLZWBits)
82 typedef struct _LZWCodeInfo
95 typedef struct _LZWStack
103 typedef struct _LZWInfo
131 Forward declarations.
134 GetNextLZWCode(LZWInfo *,const size_t);
136 static MagickBooleanType
137 WriteGIFImage(const ImageInfo *,Image *,ExceptionInfo *);
140 ReadBlobBlock(Image *,unsigned char *);
143 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
147 % D e c o d e I m a g e %
151 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
153 % DecodeImage uncompresses an image via GIF-coding.
155 % The format of the DecodeImage method is:
157 % MagickBooleanType DecodeImage(Image *image,const ssize_t opacity)
159 % A description of each parameter follows:
161 % o image: the address of a structure of type Image.
163 % o opacity: The colormap index associated with the transparent color.
167 static LZWInfo *RelinquishLZWInfo(LZWInfo *lzw_info)
169 if (lzw_info->table[0] != (size_t *) NULL)
170 lzw_info->table[0]=(size_t *) RelinquishMagickMemory(
172 if (lzw_info->table[1] != (size_t *) NULL)
173 lzw_info->table[1]=(size_t *) RelinquishMagickMemory(
175 if (lzw_info->stack != (LZWStack *) NULL)
177 if (lzw_info->stack->codes != (size_t *) NULL)
178 lzw_info->stack->codes=(size_t *) RelinquishMagickMemory(
179 lzw_info->stack->codes);
180 lzw_info->stack=(LZWStack *) RelinquishMagickMemory(lzw_info->stack);
182 lzw_info=(LZWInfo *) RelinquishMagickMemory(lzw_info);
183 return((LZWInfo *) NULL);
186 static inline void ResetLZWInfo(LZWInfo *lzw_info)
191 lzw_info->bits=lzw_info->data_size+1;
193 lzw_info->maximum_code=one << lzw_info->bits;
194 lzw_info->slot=lzw_info->maximum_data_value+3;
195 lzw_info->genesis=MagickTrue;
198 static LZWInfo *AcquireLZWInfo(Image *image,const size_t data_size)
209 lzw_info=(LZWInfo *) AcquireMagickMemory(sizeof(*lzw_info));
210 if (lzw_info == (LZWInfo *) NULL)
211 return((LZWInfo *) NULL);
212 (void) ResetMagickMemory(lzw_info,0,sizeof(*lzw_info));
213 lzw_info->image=image;
214 lzw_info->data_size=data_size;
216 lzw_info->maximum_data_value=(one << data_size)-1;
217 lzw_info->clear_code=lzw_info->maximum_data_value+1;
218 lzw_info->end_code=lzw_info->maximum_data_value+2;
219 lzw_info->table[0]=(size_t *) AcquireQuantumMemory(MaximumLZWCode,
220 sizeof(*lzw_info->table));
221 lzw_info->table[1]=(size_t *) AcquireQuantumMemory(MaximumLZWCode,
222 sizeof(*lzw_info->table));
223 if ((lzw_info->table[0] == (size_t *) NULL) ||
224 (lzw_info->table[1] == (size_t *) NULL))
226 lzw_info=RelinquishLZWInfo(lzw_info);
227 return((LZWInfo *) NULL);
229 for (i=0; i <= (ssize_t) lzw_info->maximum_data_value; i++)
231 lzw_info->table[0][i]=0;
232 lzw_info->table[1][i]=(size_t) i;
234 ResetLZWInfo(lzw_info);
235 lzw_info->code_info.buffer[0]='\0';
236 lzw_info->code_info.buffer[1]='\0';
237 lzw_info->code_info.count=2;
238 lzw_info->code_info.bit=8*lzw_info->code_info.count;
239 lzw_info->code_info.eof=MagickFalse;
240 lzw_info->genesis=MagickTrue;
241 lzw_info->stack=(LZWStack *) AcquireMagickMemory(sizeof(*lzw_info->stack));
242 if (lzw_info->stack == (LZWStack *) NULL)
244 lzw_info=RelinquishLZWInfo(lzw_info);
245 return((LZWInfo *) NULL);
247 lzw_info->stack->codes=(size_t *) AcquireQuantumMemory(2UL*
248 MaximumLZWCode,sizeof(*lzw_info->stack->codes));
249 if (lzw_info->stack->codes == (size_t *) NULL)
251 lzw_info=RelinquishLZWInfo(lzw_info);
252 return((LZWInfo *) NULL);
254 lzw_info->stack->index=lzw_info->stack->codes;
255 lzw_info->stack->top=lzw_info->stack->codes+2*MaximumLZWCode;
259 static inline int GetNextLZWCode(LZWInfo *lzw_info,const size_t bits)
270 while (((lzw_info->code_info.bit+bits) > (8*lzw_info->code_info.count)) &&
271 (lzw_info->code_info.eof == MagickFalse))
276 lzw_info->code_info.buffer[0]=lzw_info->code_info.buffer[
277 lzw_info->code_info.count-2];
278 lzw_info->code_info.buffer[1]=lzw_info->code_info.buffer[
279 lzw_info->code_info.count-1];
280 lzw_info->code_info.bit-=8*(lzw_info->code_info.count-2);
281 lzw_info->code_info.count=2;
282 count=ReadBlobBlock(lzw_info->image,&lzw_info->code_info.buffer[
283 lzw_info->code_info.count]);
285 lzw_info->code_info.count+=count;
287 lzw_info->code_info.eof=MagickTrue;
289 if ((lzw_info->code_info.bit+bits) > (8*lzw_info->code_info.count))
293 for (i=0; i < (ssize_t) bits; i++)
295 code|=((lzw_info->code_info.buffer[lzw_info->code_info.bit/8] &
296 (one << (lzw_info->code_info.bit % 8))) != 0) << i;
297 lzw_info->code_info.bit++;
302 static inline int PopLZWStack(LZWStack *stack_info)
304 if (stack_info->index <= stack_info->codes)
307 return((int) *stack_info->index);
310 static inline void PushLZWStack(LZWStack *stack_info,const size_t value)
312 if (stack_info->index >= stack_info->top)
314 *stack_info->index=value;
318 static int ReadBlobLZWByte(LZWInfo *lzw_info)
330 if (lzw_info->stack->index != lzw_info->stack->codes)
331 return(PopLZWStack(lzw_info->stack));
332 if (lzw_info->genesis != MagickFalse)
334 lzw_info->genesis=MagickFalse;
337 lzw_info->first_code=(size_t) GetNextLZWCode(lzw_info,
339 lzw_info->last_code=lzw_info->first_code;
340 } while (lzw_info->first_code == lzw_info->clear_code);
341 return((int) lzw_info->first_code);
343 code=GetNextLZWCode(lzw_info,lzw_info->bits);
346 if ((size_t) code == lzw_info->clear_code)
348 ResetLZWInfo(lzw_info);
349 return(ReadBlobLZWByte(lzw_info));
351 if ((size_t) code == lzw_info->end_code)
353 if ((size_t) code < lzw_info->slot)
357 PushLZWStack(lzw_info->stack,lzw_info->first_code);
358 value=lzw_info->last_code;
361 while (value > lzw_info->maximum_data_value)
363 if ((size_t) count > MaximumLZWCode)
366 if ((size_t) value > MaximumLZWCode)
368 PushLZWStack(lzw_info->stack,lzw_info->table[1][value]);
369 value=lzw_info->table[0][value];
371 lzw_info->first_code=lzw_info->table[1][value];
372 PushLZWStack(lzw_info->stack,lzw_info->first_code);
374 if (lzw_info->slot < MaximumLZWCode)
376 lzw_info->table[0][lzw_info->slot]=lzw_info->last_code;
377 lzw_info->table[1][lzw_info->slot]=lzw_info->first_code;
379 if ((lzw_info->slot >= lzw_info->maximum_code) &&
380 (lzw_info->bits < MaximumLZWBits))
383 lzw_info->maximum_code=one << lzw_info->bits;
386 lzw_info->last_code=(size_t) code;
387 return(PopLZWStack(lzw_info->stack));
390 static MagickBooleanType DecodeImage(Image *image,const ssize_t opacity,
391 ExceptionInfo *exception)
413 Allocate decoder tables.
415 assert(image != (Image *) NULL);
416 assert(image->signature == MagickSignature);
417 if (image->debug != MagickFalse)
418 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
419 data_size=(unsigned char) ReadBlobByte(image);
420 if (data_size > MaximumLZWBits)
421 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
422 lzw_info=AcquireLZWInfo(image,data_size);
423 if (lzw_info == (LZWInfo *) NULL)
424 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
428 for (y=0; y < (ssize_t) image->rows; y++)
436 q=GetAuthenticPixels(image,0,offset,image->columns,1,exception);
437 if (q == (Quantum *) NULL)
439 for (x=0; x < (ssize_t) image->columns; )
441 c=ReadBlobLZWByte(lzw_info);
444 index=ConstrainColormapIndex(image,(size_t) c,exception);
445 SetPixelIndex(image,index,q);
446 SetPixelInfoPixel(image,image->colormap+(ssize_t) index,q);
447 SetPixelAlpha(image,(ssize_t) index == opacity ? TransparentAlpha :
450 q+=GetPixelChannels(image);
452 if (x < (ssize_t) image->columns)
454 if (image->interlace == NoInterlace)
463 if (offset >= (ssize_t) image->rows)
473 if (offset >= (ssize_t) image->rows)
483 if (offset >= (ssize_t) image->rows)
496 if (SyncAuthenticPixels(image,exception) == MagickFalse)
499 lzw_info=RelinquishLZWInfo(lzw_info);
500 if (y < (ssize_t) image->rows)
501 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
506 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
510 % E n c o d e I m a g e %
514 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
516 % EncodeImage compresses an image via GIF-coding.
518 % The format of the EncodeImage method is:
520 % MagickBooleanType EncodeImage(const ImageInfo *image_info,Image *image,
521 % const size_t data_size)
523 % A description of each parameter follows:
525 % o image_info: the image info.
527 % o image: the address of a structure of type Image.
529 % o data_size: The number of bits in the compressed packet.
532 static MagickBooleanType EncodeImage(const ImageInfo *image_info,Image *image,
533 const size_t data_size,ExceptionInfo *exception)
535 #define MaxCode(number_bits) ((one << (number_bits))-1)
536 #define MaxHashTable 5003
537 #define MaxGIFBits 12UL
538 #define MaxGIFTable (1UL << MaxGIFBits)
539 #define GIFOutputCode(code) \
545 datum|=(code) << bits; \
552 Add a character to current packet. \
554 packet[length++]=(unsigned char) (datum & 0xff); \
557 (void) WriteBlobByte(image,(unsigned char) length); \
558 (void) WriteBlob(image,length,packet); \
564 if (free_code > max_code) \
567 if (number_bits == MaxGIFBits) \
568 max_code=MaxGIFTable; \
570 max_code=MaxCode(number_bits); \
589 end_of_information_code,
609 Allocate encoder tables.
611 assert(image != (Image *) NULL);
613 packet=(unsigned char *) AcquireQuantumMemory(256,sizeof(*packet));
614 hash_code=(short *) AcquireQuantumMemory(MaxHashTable,sizeof(*hash_code));
615 hash_prefix=(short *) AcquireQuantumMemory(MaxHashTable,sizeof(*hash_prefix));
616 hash_suffix=(unsigned char *) AcquireQuantumMemory(MaxHashTable,
617 sizeof(*hash_suffix));
618 if ((packet == (unsigned char *) NULL) || (hash_code == (short *) NULL) ||
619 (hash_prefix == (short *) NULL) ||
620 (hash_suffix == (unsigned char *) NULL))
622 if (packet != (unsigned char *) NULL)
623 packet=(unsigned char *) RelinquishMagickMemory(packet);
624 if (hash_code != (short *) NULL)
625 hash_code=(short *) RelinquishMagickMemory(hash_code);
626 if (hash_prefix != (short *) NULL)
627 hash_prefix=(short *) RelinquishMagickMemory(hash_prefix);
628 if (hash_suffix != (unsigned char *) NULL)
629 hash_suffix=(unsigned char *) RelinquishMagickMemory(hash_suffix);
633 Initialize GIF encoder.
635 number_bits=data_size;
636 max_code=MaxCode(number_bits);
637 clear_code=((short) one << (data_size-1));
638 end_of_information_code=clear_code+1;
639 free_code=clear_code+2;
643 for (i=0; i < MaxHashTable; i++)
645 GIFOutputCode(clear_code);
652 for (y=0; y < (ssize_t) image->rows; y++)
654 register const Quantum
660 p=GetVirtualPixels(image,0,offset,image->columns,1,exception);
661 if (p == (const Quantum *) NULL)
665 waiting_code=(short) GetPixelIndex(image,p);
666 p+=GetPixelChannels(image);
668 for (x=(ssize_t) (y == 0 ? 1 : 0); x < (ssize_t) image->columns; x++)
673 index=(Quantum) ((size_t) GetPixelIndex(image,p) & 0xff);
674 p+=GetPixelChannels(image);
675 k=(ssize_t) (((size_t) index << (MaxGIFBits-8))+waiting_code);
676 if (k >= MaxHashTable)
678 next_pixel=MagickFalse;
680 if (hash_code[k] > 0)
682 if ((hash_prefix[k] == waiting_code) &&
683 (hash_suffix[k] == (unsigned char) index))
685 waiting_code=hash_code[k];
689 displacement=MaxHashTable-k;
695 if (hash_code[k] == 0)
697 if ((hash_prefix[k] == waiting_code) &&
698 (hash_suffix[k] == (unsigned char) index))
700 waiting_code=hash_code[k];
701 next_pixel=MagickTrue;
705 if (next_pixel == MagickTrue)
708 GIFOutputCode((size_t) waiting_code);
709 if (free_code < MaxGIFTable)
711 hash_code[k]=(short) free_code++;
712 hash_prefix[k]=waiting_code;
713 hash_suffix[k]=(unsigned char) index;
718 Fill the hash table with empty entries.
720 for (k=0; k < MaxHashTable; k++)
723 Reset compressor and issue a clear code.
725 free_code=clear_code+2;
726 GIFOutputCode(clear_code);
727 number_bits=data_size;
728 max_code=MaxCode(number_bits);
730 waiting_code=(short) index;
732 if (image_info->interlace == NoInterlace)
741 if (offset >= (ssize_t) image->rows)
751 if (offset >= (ssize_t) image->rows)
761 if (offset >= (ssize_t) image->rows)
776 Flush out the buffered code.
778 GIFOutputCode((size_t) waiting_code);
779 GIFOutputCode(end_of_information_code);
783 Add a character to current packet.
785 packet[length++]=(unsigned char) (datum & 0xff);
788 (void) WriteBlobByte(image,(unsigned char) length);
789 (void) WriteBlob(image,length,packet);
794 Flush accumulated data.
798 (void) WriteBlobByte(image,(unsigned char) length);
799 (void) WriteBlob(image,length,packet);
804 hash_suffix=(unsigned char *) RelinquishMagickMemory(hash_suffix);
805 hash_prefix=(short *) RelinquishMagickMemory(hash_prefix);
806 hash_code=(short *) RelinquishMagickMemory(hash_code);
807 packet=(unsigned char *) RelinquishMagickMemory(packet);
812 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
820 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
822 % IsGIF() returns MagickTrue if the image format type, identified by the
823 % magick string, is GIF.
825 % The format of the IsGIF method is:
827 % MagickBooleanType IsGIF(const unsigned char *magick,const size_t length)
829 % A description of each parameter follows:
831 % o magick: compare image format pattern against these bytes.
833 % o length: Specifies the length of the magick string.
836 static MagickBooleanType IsGIF(const unsigned char *magick,const size_t length)
840 if (LocaleNCompare((char *) magick,"GIF8",4) == 0)
846 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
850 + R e a d B l o b B l o c k %
854 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
856 % ReadBlobBlock() reads data from the image file and returns it. The
857 % amount of data is determined by first reading a count byte. The number
858 % of bytes read is returned.
860 % The format of the ReadBlobBlock method is:
862 % size_t ReadBlobBlock(Image *image,unsigned char *data)
864 % A description of each parameter follows:
866 % o image: the image.
868 % o data: Specifies an area to place the information requested from
872 static ssize_t ReadBlobBlock(Image *image,unsigned char *data)
880 assert(image != (Image *) NULL);
881 assert(image->signature == MagickSignature);
882 assert(data != (unsigned char *) NULL);
883 count=ReadBlob(image,1,&block_count);
886 return(ReadBlob(image,(size_t) block_count,data));
890 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
894 % R e a d G I F I m a g e %
898 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
900 % ReadGIFImage() reads a Compuserve Graphics image file and returns it.
901 % It allocates the memory necessary for the new Image structure and returns a
902 % pointer to the new image.
904 % The format of the ReadGIFImage method is:
906 % Image *ReadGIFImage(const ImageInfo *image_info,ExceptionInfo *exception)
908 % A description of each parameter follows:
910 % o image_info: the image info.
912 % o exception: return any errors or warnings in this structure.
916 static inline size_t MagickMax(const size_t x,const size_t y)
923 static inline size_t MagickMin(const size_t x,const size_t y)
930 static MagickBooleanType PingGIFImage(Image *image,ExceptionInfo *exception)
937 assert(image != (Image *) NULL);
938 assert(image->signature == MagickSignature);
939 if (image->debug != MagickFalse)
940 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
941 if (ReadBlob(image,1,&data_size) != 1)
942 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
943 if (data_size > MaximumLZWBits)
944 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
945 if (ReadBlob(image,1,&length) != 1)
946 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
949 if (ReadBlob(image,length,buffer) != (ssize_t) length)
950 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
951 if (ReadBlob(image,1,&length) != 1)
952 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
957 static Image *ReadGIFImage(const ImageInfo *image_info,ExceptionInfo *exception)
959 #define BitSet(byte,bit) (((byte) & (bit)) == (bit))
960 #define LSBFirstOrder(x,y) (((y) << 8) | (x))
966 number_extensionss=0;
977 register unsigned char
997 header[MaxTextExtent],
1003 assert(image_info != (const ImageInfo *) NULL);
1004 assert(image_info->signature == MagickSignature);
1005 if (image_info->debug != MagickFalse)
1006 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1007 image_info->filename);
1008 assert(exception != (ExceptionInfo *) NULL);
1009 assert(exception->signature == MagickSignature);
1010 image=AcquireImage(image_info,exception);
1011 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1012 if (status == MagickFalse)
1014 image=DestroyImageList(image);
1015 return((Image *) NULL);
1018 Determine if this a GIF file.
1020 count=ReadBlob(image,6,magick);
1021 if ((count != 6) || ((LocaleNCompare((char *) magick,"GIF87",5) != 0) &&
1022 (LocaleNCompare((char *) magick,"GIF89",5) != 0)))
1023 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1024 page.width=ReadBlobLSBShort(image);
1025 page.height=ReadBlobLSBShort(image);
1026 flag=(unsigned char) ReadBlobByte(image);
1027 background=(unsigned char) ReadBlobByte(image);
1028 c=(unsigned char) ReadBlobByte(image); /* reserved */
1030 global_colors=one << (((size_t) flag & 0x07)+1);
1031 global_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
1032 MagickMax(global_colors,256),3UL*sizeof(*global_colormap));
1033 if (global_colormap == (unsigned char *) NULL)
1034 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1035 if (BitSet((int) flag,0x80) != 0)
1036 count=ReadBlob(image,(size_t) (3*global_colors),global_colormap);
1044 count=ReadBlob(image,1,&c);
1047 if (c == (unsigned char) ';')
1048 break; /* terminator */
1049 if (c == (unsigned char) '!')
1052 GIF Extension block.
1055 count=ReadBlob(image,1,&c);
1058 global_colormap=(unsigned char *) RelinquishMagickMemory(
1060 ThrowReaderException(CorruptImageError,
1061 "UnableToReadExtensionBlock");
1068 Read graphics control extension.
1070 while (ReadBlobBlock(image,header) != 0) ;
1071 dispose=(size_t) (header[0] >> 2);
1072 delay=(size_t) ((header[2] << 8) | header[1]);
1073 if ((ssize_t) (header[0] & 0x01) == 0x01)
1074 opacity=(ssize_t) header[3];
1083 Read comment extension.
1085 comments=AcquireString((char *) NULL);
1088 count=(ssize_t) ReadBlobBlock(image,header);
1092 (void) ConcatenateString(&comments,(const char *) header);
1094 (void) SetImageProperty(image,"comment",comments,exception);
1095 comments=DestroyString(comments);
1100 /* Read GIF application extension */
1106 Read Netscape Loop extension.
1109 if (ReadBlobBlock(image,header) != 0)
1110 loop=LocaleNCompare((char *) header,"NETSCAPE2.0",11) == 0 ?
1111 MagickTrue : MagickFalse;
1112 if (loop != MagickFalse)
1114 while (ReadBlobBlock(image,header) != 0)
1115 iterations=(size_t) ((header[2] << 8) | header[1]);
1121 name[MaxTextExtent];
1140 Store GIF application extension as a generic profile.
1142 i8bim=LocaleNCompare((char *) header,"MGK8BIM0000",11) == 0 ?
1143 MagickTrue : MagickFalse;
1144 icc=LocaleNCompare((char *) header,"ICCRGBG1012",11) == 0 ?
1145 MagickTrue : MagickFalse;
1146 iptc=LocaleNCompare((char *) header,"MGKIPTC0000",11) == 0 ?
1147 MagickTrue : MagickFalse;
1148 number_extensionss++;
1149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1150 " Reading GIF application extension");
1151 info=(unsigned char *) AcquireQuantumMemory(255UL,
1153 if (info == (unsigned char *) NULL)
1154 ThrowReaderException(ResourceLimitError,
1155 "MemoryAllocationFailed");
1156 reserved_length=255;
1157 for (info_length=0; ; )
1159 block_length=(int) ReadBlobBlock(image,&info[info_length]);
1160 if (block_length == 0)
1162 info_length+=block_length;
1163 if (info_length > (reserved_length-255))
1165 reserved_length+=4096;
1166 info=(unsigned char *) ResizeQuantumMemory(info,
1167 (size_t) reserved_length,sizeof(*info));
1168 if (info == (unsigned char *) NULL)
1169 ThrowReaderException(ResourceLimitError,
1170 "MemoryAllocationFailed");
1173 profile=BlobToStringInfo(info,(size_t) info_length);
1174 if (profile == (StringInfo *) NULL)
1175 ThrowReaderException(ResourceLimitError,
1176 "MemoryAllocationFailed");
1177 info=(unsigned char *) RelinquishMagickMemory(info);
1178 if (i8bim == MagickTrue)
1179 (void) CopyMagickString(name,"8bim",sizeof(name));
1180 else if (icc == MagickTrue)
1181 (void) CopyMagickString(name,"icc",sizeof(name));
1182 else if (iptc == MagickTrue)
1183 (void) CopyMagickString(name,"iptc",sizeof(name));
1185 (void) FormatLocaleString(name,sizeof(name),"gif:%.11s",
1187 (void) SetImageProfile(image,name,profile,exception);
1188 profile=DestroyStringInfo(profile);
1189 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1190 " profile name=%s",name);
1196 while (ReadBlobBlock(image,header) != 0) ;
1201 if (c != (unsigned char) ',')
1203 if (image_count != 0)
1206 Allocate next image structure.
1208 AcquireNextImage(image_info,image,exception);
1209 if (GetNextImageInList(image) == (Image *) NULL)
1211 image=DestroyImageList(image);
1212 global_colormap=(unsigned char *) RelinquishMagickMemory(
1214 return((Image *) NULL);
1216 image=SyncNextImageInList(image);
1220 Read image attributes.
1222 image->storage_class=PseudoClass;
1223 image->compression=LZWCompression;
1224 page.x=(ssize_t) ReadBlobLSBShort(image);
1225 page.y=(ssize_t) ReadBlobLSBShort(image);
1226 image->columns=ReadBlobLSBShort(image);
1227 image->rows=ReadBlobLSBShort(image);
1229 flag=(unsigned char) ReadBlobByte(image);
1230 image->interlace=BitSet((int) flag,0x40) != 0 ? GIFInterlace : NoInterlace;
1231 image->colors=BitSet((int) flag,0x80) == 0 ? global_colors : one <<
1232 ((size_t) (flag & 0x07)+1);
1233 if (opacity >= (ssize_t) image->colors)
1235 image->page.width=page.width;
1236 image->page.height=page.height;
1237 image->page.y=page.y;
1238 image->page.x=page.x;
1240 image->ticks_per_second=100;
1241 image->dispose=(DisposeType) dispose;
1242 image->iterations=iterations;
1243 image->matte=opacity >= 0 ? MagickTrue : MagickFalse;
1247 if ((image->columns == 0) || (image->rows == 0))
1249 global_colormap=(unsigned char *) RelinquishMagickMemory(
1251 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
1254 Inititialize colormap.
1256 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
1258 global_colormap=(unsigned char *) RelinquishMagickMemory(
1260 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1262 if (BitSet((int) flag,0x80) == 0)
1265 Use global colormap.
1268 for (i=0; i < (ssize_t) image->colors; i++)
1270 image->colormap[i].red=ScaleCharToQuantum(*p++);
1271 image->colormap[i].green=ScaleCharToQuantum(*p++);
1272 image->colormap[i].blue=ScaleCharToQuantum(*p++);
1275 image->colormap[i].alpha=(Quantum) TransparentAlpha;
1276 image->transparent_color=image->colormap[opacity];
1279 image->background_color=image->colormap[MagickMin(background,
1288 Read local colormap.
1290 colormap=(unsigned char *) AcquireQuantumMemory(image->colors,3*
1292 if (colormap == (unsigned char *) NULL)
1294 global_colormap=(unsigned char *) RelinquishMagickMemory(
1296 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1298 count=ReadBlob(image,(3*image->colors)*sizeof(*colormap),colormap);
1299 if (count != (ssize_t) (3*image->colors))
1301 global_colormap=(unsigned char *) RelinquishMagickMemory(
1303 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1304 ThrowReaderException(CorruptImageError,
1305 "InsufficientImageDataInFile");
1308 for (i=0; i < (ssize_t) image->colors; i++)
1310 image->colormap[i].red=ScaleCharToQuantum(*p++);
1311 image->colormap[i].green=ScaleCharToQuantum(*p++);
1312 image->colormap[i].blue=ScaleCharToQuantum(*p++);
1314 image->colormap[i].alpha=(Quantum) TransparentAlpha;
1316 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1318 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
1319 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
1324 if (image_info->ping != MagickFalse)
1325 status=PingGIFImage(image,exception);
1327 status=DecodeImage(image,opacity,exception);
1328 if ((image_info->ping == MagickFalse) && (status == MagickFalse))
1330 global_colormap=(unsigned char *) RelinquishMagickMemory(
1332 ThrowReaderException(CorruptImageError,"CorruptImage");
1334 if (image_info->number_scenes != 0)
1335 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
1338 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) image->scene-
1340 if (status == MagickFalse)
1343 global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap);
1344 if ((image->columns == 0) || (image->rows == 0))
1345 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
1346 (void) CloseBlob(image);
1347 return(GetFirstImageInList(image));
1351 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1355 % R e g i s t e r G I F I m a g e %
1359 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1361 % RegisterGIFImage() adds properties for the GIF image format to
1362 % the list of supported formats. The properties include the image format
1363 % tag, a method to read and/or write the format, whether the format
1364 % supports the saving of more than one frame to the same file or blob,
1365 % whether the format supports native in-memory I/O, and a brief
1366 % description of the format.
1368 % The format of the RegisterGIFImage method is:
1370 % size_t RegisterGIFImage(void)
1373 ModuleExport size_t RegisterGIFImage(void)
1378 entry=SetMagickInfo("GIF");
1379 entry->decoder=(DecodeImageHandler *) ReadGIFImage;
1380 entry->encoder=(EncodeImageHandler *) WriteGIFImage;
1381 entry->magick=(IsImageFormatHandler *) IsGIF;
1382 entry->description=ConstantString("CompuServe graphics interchange format");
1383 entry->module=ConstantString("GIF");
1384 (void) RegisterMagickInfo(entry);
1385 entry=SetMagickInfo("GIF87");
1386 entry->decoder=(DecodeImageHandler *) ReadGIFImage;
1387 entry->encoder=(EncodeImageHandler *) WriteGIFImage;
1388 entry->magick=(IsImageFormatHandler *) IsGIF;
1389 entry->adjoin=MagickFalse;
1390 entry->description=ConstantString("CompuServe graphics interchange format");
1391 entry->version=ConstantString("version 87a");
1392 entry->module=ConstantString("GIF");
1393 (void) RegisterMagickInfo(entry);
1394 return(MagickImageCoderSignature);
1398 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1402 % U n r e g i s t e r G I F I m a g e %
1406 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1408 % UnregisterGIFImage() removes format registrations made by the
1409 % GIF module from the list of supported formats.
1411 % The format of the UnregisterGIFImage method is:
1413 % UnregisterGIFImage(void)
1416 ModuleExport void UnregisterGIFImage(void)
1418 (void) UnregisterMagickInfo("GIF");
1419 (void) UnregisterMagickInfo("GIF87");
1423 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1427 % W r i t e G I F I m a g e %
1431 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1433 % WriteGIFImage() writes an image to a file in the Compuserve Graphics
1436 % The format of the WriteGIFImage method is:
1438 % MagickBooleanType WriteGIFImage(const ImageInfo *image_info,
1439 % Image *image,ExceptionInfo *exception)
1441 % A description of each parameter follows.
1443 % o image_info: the image info.
1445 % o image: The image.
1447 % o exception: return any errors or warnings in this structure.
1450 static MagickBooleanType WriteGIFImage(const ImageInfo *image_info,Image *image,
1451 ExceptionInfo *exception)
1474 register unsigned char
1492 Open output image file.
1494 assert(image_info != (const ImageInfo *) NULL);
1495 assert(image_info->signature == MagickSignature);
1496 assert(image != (Image *) NULL);
1497 assert(image->signature == MagickSignature);
1498 if (image->debug != MagickFalse)
1499 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1500 assert(exception != (ExceptionInfo *) NULL);
1501 assert(exception->signature == MagickSignature);
1502 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
1503 if (status == MagickFalse)
1508 global_colormap=(unsigned char *) AcquireQuantumMemory(768UL,
1509 sizeof(*global_colormap));
1510 colormap=(unsigned char *) AcquireQuantumMemory(768UL,sizeof(*colormap));
1511 if ((global_colormap == (unsigned char *) NULL) ||
1512 (colormap == (unsigned char *) NULL))
1513 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1514 for (i=0; i < 768; i++)
1515 colormap[i]=(unsigned char) 0;
1519 write_info=CloneImageInfo(image_info);
1520 if (LocaleCompare(write_info->magick,"GIF87") != 0)
1521 (void) WriteBlob(image,6,(unsigned char *) "GIF89a");
1524 (void) WriteBlob(image,6,(unsigned char *) "GIF87a");
1525 write_info->adjoin=MagickFalse;
1528 Determine image bounding box.
1530 page.width=image->columns;
1531 if (image->page.width > page.width)
1532 page.width=image->page.width;
1533 page.height=image->rows;
1534 if (image->page.height > page.height)
1535 page.height=image->page.height;
1536 page.x=image->page.x;
1537 page.y=image->page.y;
1538 (void) WriteBlobLSBShort(image,(unsigned short) page.width);
1539 (void) WriteBlobLSBShort(image,(unsigned short) page.height);
1541 Write images to file.
1543 interlace=write_info->interlace;
1544 if ((write_info->adjoin != MagickFalse) &&
1545 (GetNextImageInList(image) != (Image *) NULL))
1546 interlace=NoInterlace;
1551 if (IssRGBColorspace(image->colorspace) == MagickFalse)
1552 (void) TransformImageColorspace(image,sRGBColorspace,exception);
1554 if (IsImageOpaque(image,exception) != MagickFalse)
1556 if ((image->storage_class == DirectClass) || (image->colors > 256))
1557 (void) SetImageType(image,PaletteType,exception);
1566 Identify transparent colormap index.
1568 if ((image->storage_class == DirectClass) || (image->colors > 256))
1569 (void) SetImageType(image,PaletteBilevelMatteType,exception);
1570 for (i=0; i < (ssize_t) image->colors; i++)
1571 if (image->colormap[i].alpha != OpaqueAlpha)
1578 alpha=(MagickRealType) TransparentAlpha-(MagickRealType)
1579 image->colormap[i].alpha;
1580 beta=(MagickRealType) TransparentAlpha-(MagickRealType)
1581 image->colormap[opacity].alpha;
1587 (void) SetImageType(image,PaletteBilevelMatteType,exception);
1588 for (i=0; i < (ssize_t) image->colors; i++)
1589 if (image->colormap[i].alpha != OpaqueAlpha)
1596 alpha=(Quantum) TransparentAlpha-(MagickRealType)
1597 image->colormap[i].alpha;
1598 beta=(Quantum) TransparentAlpha-(MagickRealType)
1599 image->colormap[opacity].alpha;
1606 image->colormap[opacity].red=image->transparent_color.red;
1607 image->colormap[opacity].green=image->transparent_color.green;
1608 image->colormap[opacity].blue=image->transparent_color.blue;
1611 if ((image->storage_class == DirectClass) || (image->colors > 256))
1612 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1613 for (bits_per_pixel=1; bits_per_pixel < 8; bits_per_pixel++)
1614 if ((one << bits_per_pixel) >= image->colors)
1617 for (i=0; i < (ssize_t) image->colors; i++)
1619 *q++=ScaleQuantumToChar(image->colormap[i].red);
1620 *q++=ScaleQuantumToChar(image->colormap[i].green);
1621 *q++=ScaleQuantumToChar(image->colormap[i].blue);
1623 for ( ; i < (ssize_t) (one << bits_per_pixel); i++)
1625 *q++=(unsigned char) 0x0;
1626 *q++=(unsigned char) 0x0;
1627 *q++=(unsigned char) 0x0;
1629 if ((GetPreviousImageInList(image) == (Image *) NULL) ||
1630 (write_info->adjoin == MagickFalse))
1633 Write global colormap.
1636 c|=(8-1) << 4; /* color resolution */
1637 c|=(bits_per_pixel-1); /* size of global colormap */
1638 (void) WriteBlobByte(image,(unsigned char) c);
1639 for (j=0; j < (ssize_t) image->colors; j++)
1640 if (IsPixelInfoEquivalent(&image->background_color,image->colormap+j))
1642 (void) WriteBlobByte(image,(unsigned char)
1643 (j == (ssize_t) image->colors ? 0 : j)); /* background color */
1644 (void) WriteBlobByte(image,(unsigned char) 0x00); /* reserved */
1645 length=(size_t) (3*(one << bits_per_pixel));
1646 (void) WriteBlob(image,length,colormap);
1647 for (j=0; j < 768; j++)
1648 global_colormap[j]=colormap[j];
1650 if (LocaleCompare(write_info->magick,"GIF87") != 0)
1653 Write graphics control extension.
1655 (void) WriteBlobByte(image,(unsigned char) 0x21);
1656 (void) WriteBlobByte(image,(unsigned char) 0xf9);
1657 (void) WriteBlobByte(image,(unsigned char) 0x04);
1658 c=image->dispose << 2;
1661 (void) WriteBlobByte(image,(unsigned char) c);
1662 delay=(size_t) (100*image->delay/MagickMax((size_t)
1663 image->ticks_per_second,1));
1664 (void) WriteBlobLSBShort(image,(unsigned short) delay);
1665 (void) WriteBlobByte(image,(unsigned char) (opacity >= 0 ? opacity :
1667 (void) WriteBlobByte(image,(unsigned char) 0x00);
1668 if ((LocaleCompare(write_info->magick,"GIF87") != 0) &&
1669 (GetImageProperty(image,"comment",exception) != (const char *) NULL))
1681 Write Comment extension.
1683 (void) WriteBlobByte(image,(unsigned char) 0x21);
1684 (void) WriteBlobByte(image,(unsigned char) 0xfe);
1685 value=GetImageProperty(image,"comment",exception);
1687 while (strlen(p) != 0)
1689 count=MagickMin(strlen(p),255);
1690 (void) WriteBlobByte(image,(unsigned char) count);
1691 for (i=0; i < (ssize_t) count; i++)
1692 (void) WriteBlobByte(image,(unsigned char) *p++);
1694 (void) WriteBlobByte(image,(unsigned char) 0x00);
1696 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
1697 (GetNextImageInList(image) != (Image *) NULL) &&
1698 (image->iterations != 1))
1701 Write Netscape Loop extension.
1703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1704 " Writing GIF Extension %s","NETSCAPE2.0");
1705 (void) WriteBlobByte(image,(unsigned char) 0x21);
1706 (void) WriteBlobByte(image,(unsigned char) 0xff);
1707 (void) WriteBlobByte(image,(unsigned char) 0x0b);
1708 (void) WriteBlob(image,11,(unsigned char *) "NETSCAPE2.0");
1709 (void) WriteBlobByte(image,(unsigned char) 0x03);
1710 (void) WriteBlobByte(image,(unsigned char) 0x01);
1711 (void) WriteBlobLSBShort(image,(unsigned short) image->iterations);
1712 (void) WriteBlobByte(image,(unsigned char) 0x00);
1714 ResetImageProfileIterator(image);
1723 name=GetNextImageProfile(image);
1724 if (name == (const char *) NULL)
1726 profile=GetImageProfile(image,name);
1727 if (profile != (StringInfo *) NULL)
1729 if ((LocaleCompare(name,"ICC") == 0) ||
1730 (LocaleCompare(name,"ICM") == 0) ||
1731 (LocaleCompare(name,"IPTC") == 0) ||
1732 (LocaleCompare(name,"8BIM") == 0) ||
1733 (LocaleNCompare(name,"gif:",4) == 0))
1744 datum=GetStringInfoDatum(profile);
1745 length=GetStringInfoLength(profile);
1746 (void) WriteBlobByte(image,(unsigned char) 0x21);
1747 (void) WriteBlobByte(image,(unsigned char) 0xff);
1748 (void) WriteBlobByte(image,(unsigned char) 0x0b);
1749 if ((LocaleCompare(name,"ICC") == 0) ||
1750 (LocaleCompare(name,"ICM") == 0))
1753 Write ICC extension.
1755 (void) WriteBlob(image,11,(unsigned char *)"ICCRGBG1012");
1756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1757 " Writing GIF Extension %s","ICCRGBG1012");
1760 if ((LocaleCompare(name,"IPTC") == 0))
1763 write IPTC extension.
1765 (void) WriteBlob(image,11,(unsigned char *)"MGKIPTC0000");
1766 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1767 " Writing GIF Extension %s","MGKIPTC0000");
1770 if ((LocaleCompare(name,"8BIM") == 0))
1773 Write 8BIM extension>
1775 (void) WriteBlob(image,11,(unsigned char *)
1777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1778 " Writing GIF Extension %s","MGK8BIM0000");
1783 extension[MaxTextExtent];
1785 /* write generic extension */
1786 (void) CopyMagickString(extension,name+4,
1788 (void) WriteBlob(image,11,(unsigned char *) extension);
1789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1790 " Writing GIF Extension %s",name);
1793 while ((ssize_t) length > offset)
1798 if ((length-offset) < 255)
1799 block_length=length-offset;
1802 (void) WriteBlobByte(image,(unsigned char) block_length);
1803 (void) WriteBlob(image,(size_t) block_length,datum+offset);
1804 offset+=(ssize_t) block_length;
1806 (void) WriteBlobByte(image,(unsigned char) 0x00);
1811 (void) WriteBlobByte(image,','); /* image separator */
1813 Write the image header.
1815 page.x=image->page.x;
1816 page.y=image->page.y;
1817 if ((image->page.width != 0) && (image->page.height != 0))
1819 (void) WriteBlobLSBShort(image,(unsigned short) (page.x < 0 ? 0 : page.x));
1820 (void) WriteBlobLSBShort(image,(unsigned short) (page.y < 0 ? 0 : page.y));
1821 (void) WriteBlobLSBShort(image,(unsigned short) image->columns);
1822 (void) WriteBlobLSBShort(image,(unsigned short) image->rows);
1824 if (interlace != NoInterlace)
1825 c|=0x40; /* pixel data is interlaced */
1826 for (j=0; j < (ssize_t) (3*image->colors); j++)
1827 if (colormap[j] != global_colormap[j])
1829 if (j == (ssize_t) (3*image->colors))
1830 (void) WriteBlobByte(image,(unsigned char) c);
1834 c|=(bits_per_pixel-1); /* size of local colormap */
1835 (void) WriteBlobByte(image,(unsigned char) c);
1836 length=(size_t) (3*(one << bits_per_pixel));
1837 (void) WriteBlob(image,length,colormap);
1840 Write the image data.
1842 c=(int) MagickMax(bits_per_pixel,2);
1843 (void) WriteBlobByte(image,(unsigned char) c);
1844 status=EncodeImage(write_info,image,(size_t) MagickMax(bits_per_pixel,2)+1,
1846 if (status == MagickFalse)
1848 global_colormap=(unsigned char *) RelinquishMagickMemory(
1850 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1851 write_info=DestroyImageInfo(write_info);
1852 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1854 (void) WriteBlobByte(image,(unsigned char) 0x00);
1855 if (GetNextImageInList(image) == (Image *) NULL)
1857 image=SyncNextImageInList(image);
1859 status=SetImageProgress(image,SaveImagesTag,scene,
1860 GetImageListLength(image));
1861 if (status == MagickFalse)
1863 } while (write_info->adjoin != MagickFalse);
1864 (void) WriteBlobByte(image,';'); /* terminator */
1865 global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap);
1866 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1867 write_info=DestroyImageInfo(write_info);
1868 (void) CloseBlob(image);