2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Compuserv Graphics Interchange Format %
20 % Copyright 1999-2018 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 % https://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/resource_.h"
70 #include "MagickCore/static.h"
71 #include "MagickCore/string_.h"
72 #include "MagickCore/string-private.h"
73 #include "MagickCore/module.h"
78 #define MaximumLZWBits 12
79 #define MaximumLZWCode (1UL << MaximumLZWBits)
84 typedef struct _LZWCodeInfo
97 typedef struct _LZWStack
105 typedef struct _LZWInfo
133 Forward declarations.
136 GetNextLZWCode(LZWInfo *,const size_t);
138 static MagickBooleanType
139 WriteGIFImage(const ImageInfo *,Image *,ExceptionInfo *);
142 ReadBlobBlock(Image *,unsigned char *);
145 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
149 % D e c o d e I m a g e %
153 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
155 % DecodeImage uncompresses an image via GIF-coding.
157 % The format of the DecodeImage method is:
159 % MagickBooleanType DecodeImage(Image *image,const ssize_t opacity)
161 % A description of each parameter follows:
163 % o image: the address of a structure of type Image.
165 % o opacity: The colormap index associated with the transparent color.
169 static LZWInfo *RelinquishLZWInfo(LZWInfo *lzw_info)
171 if (lzw_info->table[0] != (size_t *) NULL)
172 lzw_info->table[0]=(size_t *) RelinquishMagickMemory(
174 if (lzw_info->table[1] != (size_t *) NULL)
175 lzw_info->table[1]=(size_t *) RelinquishMagickMemory(
177 if (lzw_info->stack != (LZWStack *) NULL)
179 if (lzw_info->stack->codes != (size_t *) NULL)
180 lzw_info->stack->codes=(size_t *) RelinquishMagickMemory(
181 lzw_info->stack->codes);
182 lzw_info->stack=(LZWStack *) RelinquishMagickMemory(lzw_info->stack);
184 lzw_info=(LZWInfo *) RelinquishMagickMemory(lzw_info);
185 return((LZWInfo *) NULL);
188 static inline void ResetLZWInfo(LZWInfo *lzw_info)
193 lzw_info->bits=lzw_info->data_size+1;
195 lzw_info->maximum_code=one << lzw_info->bits;
196 lzw_info->slot=lzw_info->maximum_data_value+3;
197 lzw_info->genesis=MagickTrue;
200 static LZWInfo *AcquireLZWInfo(Image *image,const size_t data_size)
211 lzw_info=(LZWInfo *) AcquireMagickMemory(sizeof(*lzw_info));
212 if (lzw_info == (LZWInfo *) NULL)
213 return((LZWInfo *) NULL);
214 (void) memset(lzw_info,0,sizeof(*lzw_info));
215 lzw_info->image=image;
216 lzw_info->data_size=data_size;
218 lzw_info->maximum_data_value=(one << data_size)-1;
219 lzw_info->clear_code=lzw_info->maximum_data_value+1;
220 lzw_info->end_code=lzw_info->maximum_data_value+2;
221 lzw_info->table[0]=(size_t *) AcquireQuantumMemory(MaximumLZWCode,
222 sizeof(**lzw_info->table));
223 lzw_info->table[1]=(size_t *) AcquireQuantumMemory(MaximumLZWCode,
224 sizeof(**lzw_info->table));
225 if ((lzw_info->table[0] == (size_t *) NULL) ||
226 (lzw_info->table[1] == (size_t *) NULL))
228 lzw_info=RelinquishLZWInfo(lzw_info);
229 return((LZWInfo *) NULL);
231 (void) memset(lzw_info->table[0],0,MaximumLZWCode*
232 sizeof(**lzw_info->table));
233 (void) memset(lzw_info->table[1],0,MaximumLZWCode*
234 sizeof(**lzw_info->table));
235 for (i=0; i <= (ssize_t) lzw_info->maximum_data_value; i++)
237 lzw_info->table[0][i]=0;
238 lzw_info->table[1][i]=(size_t) i;
240 ResetLZWInfo(lzw_info);
241 lzw_info->code_info.buffer[0]='\0';
242 lzw_info->code_info.buffer[1]='\0';
243 lzw_info->code_info.count=2;
244 lzw_info->code_info.bit=8*lzw_info->code_info.count;
245 lzw_info->code_info.eof=MagickFalse;
246 lzw_info->genesis=MagickTrue;
247 lzw_info->stack=(LZWStack *) AcquireMagickMemory(sizeof(*lzw_info->stack));
248 if (lzw_info->stack == (LZWStack *) NULL)
250 lzw_info=RelinquishLZWInfo(lzw_info);
251 return((LZWInfo *) NULL);
253 lzw_info->stack->codes=(size_t *) AcquireQuantumMemory(2UL*
254 MaximumLZWCode,sizeof(*lzw_info->stack->codes));
255 if (lzw_info->stack->codes == (size_t *) NULL)
257 lzw_info=RelinquishLZWInfo(lzw_info);
258 return((LZWInfo *) NULL);
260 lzw_info->stack->index=lzw_info->stack->codes;
261 lzw_info->stack->top=lzw_info->stack->codes+2*MaximumLZWCode;
265 static inline int GetNextLZWCode(LZWInfo *lzw_info,const size_t bits)
276 while (((lzw_info->code_info.bit+bits) > (8*lzw_info->code_info.count)) &&
277 (lzw_info->code_info.eof == MagickFalse))
282 lzw_info->code_info.buffer[0]=lzw_info->code_info.buffer[
283 lzw_info->code_info.count-2];
284 lzw_info->code_info.buffer[1]=lzw_info->code_info.buffer[
285 lzw_info->code_info.count-1];
286 lzw_info->code_info.bit-=8*(lzw_info->code_info.count-2);
287 lzw_info->code_info.count=2;
288 count=ReadBlobBlock(lzw_info->image,&lzw_info->code_info.buffer[
289 lzw_info->code_info.count]);
291 lzw_info->code_info.count+=count;
293 lzw_info->code_info.eof=MagickTrue;
295 if ((lzw_info->code_info.bit+bits) > (8*lzw_info->code_info.count))
299 for (i=0; i < (ssize_t) bits; i++)
301 code|=((lzw_info->code_info.buffer[lzw_info->code_info.bit/8] &
302 (one << (lzw_info->code_info.bit % 8))) != 0) << i;
303 lzw_info->code_info.bit++;
308 static inline int PopLZWStack(LZWStack *stack_info)
310 if (stack_info->index <= stack_info->codes)
313 return((int) *stack_info->index);
316 static inline void PushLZWStack(LZWStack *stack_info,const size_t value)
318 if (stack_info->index >= stack_info->top)
320 *stack_info->index=value;
324 static int ReadBlobLZWByte(LZWInfo *lzw_info)
336 if (lzw_info->stack->index != lzw_info->stack->codes)
337 return(PopLZWStack(lzw_info->stack));
338 if (lzw_info->genesis != MagickFalse)
340 lzw_info->genesis=MagickFalse;
343 lzw_info->first_code=(size_t) GetNextLZWCode(lzw_info,lzw_info->bits);
344 lzw_info->last_code=lzw_info->first_code;
345 } while (lzw_info->first_code == lzw_info->clear_code);
346 return((int) lzw_info->first_code);
348 code=GetNextLZWCode(lzw_info,lzw_info->bits);
351 if ((size_t) code == lzw_info->clear_code)
353 ResetLZWInfo(lzw_info);
354 return(ReadBlobLZWByte(lzw_info));
356 if ((size_t) code == lzw_info->end_code)
358 if ((size_t) code < lzw_info->slot)
362 PushLZWStack(lzw_info->stack,lzw_info->first_code);
363 value=lzw_info->last_code;
366 while (value > lzw_info->maximum_data_value)
368 if ((size_t) count > MaximumLZWCode)
371 if ((size_t) value > MaximumLZWCode)
373 PushLZWStack(lzw_info->stack,lzw_info->table[1][value]);
374 value=lzw_info->table[0][value];
376 lzw_info->first_code=lzw_info->table[1][value];
377 PushLZWStack(lzw_info->stack,lzw_info->first_code);
379 if (lzw_info->slot < MaximumLZWCode)
381 lzw_info->table[0][lzw_info->slot]=lzw_info->last_code;
382 lzw_info->table[1][lzw_info->slot]=lzw_info->first_code;
384 if ((lzw_info->slot >= lzw_info->maximum_code) &&
385 (lzw_info->bits < MaximumLZWBits))
388 lzw_info->maximum_code=one << lzw_info->bits;
391 lzw_info->last_code=(size_t) code;
392 return(PopLZWStack(lzw_info->stack));
395 static MagickBooleanType DecodeImage(Image *image,const ssize_t opacity,
396 ExceptionInfo *exception)
416 Allocate decoder tables.
418 assert(image != (Image *) NULL);
419 assert(image->signature == MagickCoreSignature);
420 if (image->debug != MagickFalse)
421 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
422 data_size=(unsigned char) ReadBlobByte(image);
423 if (data_size > MaximumLZWBits)
424 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
425 lzw_info=AcquireLZWInfo(image,data_size);
426 if (lzw_info == (LZWInfo *) NULL)
427 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
431 for (y=0; y < (ssize_t) image->rows; y++)
439 q=QueueAuthenticPixels(image,0,offset,image->columns,1,exception);
440 if (q == (Quantum *) NULL)
442 for (x=0; x < (ssize_t) image->columns; )
444 c=ReadBlobLZWByte(lzw_info);
447 index=ConstrainColormapIndex(image,(ssize_t) c,exception);
448 SetPixelIndex(image,(Quantum) index,q);
449 SetPixelViaPixelInfo(image,image->colormap+index,q);
450 SetPixelAlpha(image,index == opacity ? TransparentAlpha : OpaqueAlpha,q);
452 q+=GetPixelChannels(image);
454 if (SyncAuthenticPixels(image,exception) == MagickFalse)
456 if (x < (ssize_t) image->columns)
458 if (image->interlace == NoInterlace)
486 if ((pass == 0) && (offset >= (ssize_t) image->rows))
491 if ((pass == 1) && (offset >= (ssize_t) image->rows))
496 if ((pass == 2) && (offset >= (ssize_t) image->rows))
503 lzw_info=RelinquishLZWInfo(lzw_info);
504 if (y < (ssize_t) image->rows)
505 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
510 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
514 % E n c o d e I m a g e %
518 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
520 % EncodeImage compresses an image via GIF-coding.
522 % The format of the EncodeImage method is:
524 % MagickBooleanType EncodeImage(const ImageInfo *image_info,Image *image,
525 % const size_t data_size)
527 % A description of each parameter follows:
529 % o image_info: the image info.
531 % o image: the address of a structure of type Image.
533 % o data_size: The number of bits in the compressed packet.
536 static MagickBooleanType EncodeImage(const ImageInfo *image_info,Image *image,
537 const size_t data_size,ExceptionInfo *exception)
539 #define MaxCode(number_bits) ((one << (number_bits))-1)
540 #define MaxHashTable 5003
541 #define MaxGIFBits 12UL
542 #define MaxGIFTable (1UL << MaxGIFBits)
543 #define GIFOutputCode(code) \
549 datum|=(size_t) (code) << bits; \
551 datum=(size_t) (code); \
556 Add a character to current packet. \
558 packet[length++]=(unsigned char) (datum & 0xff); \
561 (void) WriteBlobByte(image,(unsigned char) length); \
562 (void) WriteBlob(image,length,packet); \
568 if (free_code > max_code) \
571 if (number_bits == MaxGIFBits) \
572 max_code=MaxGIFTable; \
574 max_code=MaxCode(number_bits); \
590 end_of_information_code,
610 Allocate encoder tables.
612 assert(image != (Image *) NULL);
614 packet=(unsigned char *) AcquireQuantumMemory(256,sizeof(*packet));
615 hash_code=(short *) AcquireQuantumMemory(MaxHashTable,sizeof(*hash_code));
616 hash_prefix=(short *) AcquireQuantumMemory(MaxHashTable,sizeof(*hash_prefix));
617 hash_suffix=(unsigned char *) AcquireQuantumMemory(MaxHashTable,
618 sizeof(*hash_suffix));
619 if ((packet == (unsigned char *) NULL) || (hash_code == (short *) NULL) ||
620 (hash_prefix == (short *) NULL) ||
621 (hash_suffix == (unsigned char *) NULL))
623 if (packet != (unsigned char *) NULL)
624 packet=(unsigned char *) RelinquishMagickMemory(packet);
625 if (hash_code != (short *) NULL)
626 hash_code=(short *) RelinquishMagickMemory(hash_code);
627 if (hash_prefix != (short *) NULL)
628 hash_prefix=(short *) RelinquishMagickMemory(hash_prefix);
629 if (hash_suffix != (unsigned char *) NULL)
630 hash_suffix=(unsigned char *) RelinquishMagickMemory(hash_suffix);
634 Initialize GIF encoder.
636 (void) memset(packet,0,256*sizeof(*packet));
637 (void) memset(hash_code,0,MaxHashTable*sizeof(*hash_code));
638 (void) memset(hash_prefix,0,MaxHashTable*sizeof(*hash_prefix));
639 (void) memset(hash_suffix,0,MaxHashTable*sizeof(*hash_suffix));
640 number_bits=data_size;
641 max_code=MaxCode(number_bits);
642 clear_code=((short) one << (data_size-1));
643 end_of_information_code=clear_code+1;
644 free_code=clear_code+2;
648 GIFOutputCode(clear_code);
655 for (y=0; y < (ssize_t) image->rows; y++)
657 register const Quantum
663 p=GetVirtualPixels(image,0,offset,image->columns,1,exception);
664 if (p == (const Quantum *) NULL)
668 waiting_code=(short) GetPixelIndex(image,p);
669 p+=GetPixelChannels(image);
671 for (x=(ssize_t) (y == 0 ? 1 : 0); x < (ssize_t) image->columns; x++)
676 index=(Quantum) ((size_t) GetPixelIndex(image,p) & 0xff);
677 p+=GetPixelChannels(image);
678 k=(ssize_t) (((size_t) index << (MaxGIFBits-8))+waiting_code);
679 if (k >= MaxHashTable)
681 next_pixel=MagickFalse;
683 if (hash_code[k] > 0)
685 if ((hash_prefix[k] == waiting_code) &&
686 (hash_suffix[k] == (unsigned char) index))
688 waiting_code=hash_code[k];
692 displacement=MaxHashTable-k;
698 if (hash_code[k] == 0)
700 if ((hash_prefix[k] == waiting_code) &&
701 (hash_suffix[k] == (unsigned char) index))
703 waiting_code=hash_code[k];
704 next_pixel=MagickTrue;
708 if (next_pixel != MagickFalse)
711 GIFOutputCode(waiting_code);
712 if (free_code < MaxGIFTable)
714 hash_code[k]=(short) free_code++;
715 hash_prefix[k]=waiting_code;
716 hash_suffix[k]=(unsigned char) index;
721 Fill the hash table with empty entries.
723 for (k=0; k < MaxHashTable; k++)
726 Reset compressor and issue a clear code.
728 free_code=clear_code+2;
729 GIFOutputCode(clear_code);
730 number_bits=data_size;
731 max_code=MaxCode(number_bits);
733 waiting_code=(short) index;
735 if (image_info->interlace == NoInterlace)
744 if (offset >= (ssize_t) image->rows)
754 if (offset >= (ssize_t) image->rows)
764 if (offset >= (ssize_t) image->rows)
779 Flush out the buffered code.
781 GIFOutputCode(waiting_code);
782 GIFOutputCode(end_of_information_code);
786 Add a character to current packet.
788 packet[length++]=(unsigned char) (datum & 0xff);
791 (void) WriteBlobByte(image,(unsigned char) length);
792 (void) WriteBlob(image,length,packet);
797 Flush accumulated data.
801 (void) WriteBlobByte(image,(unsigned char) length);
802 (void) WriteBlob(image,length,packet);
807 hash_suffix=(unsigned char *) RelinquishMagickMemory(hash_suffix);
808 hash_prefix=(short *) RelinquishMagickMemory(hash_prefix);
809 hash_code=(short *) RelinquishMagickMemory(hash_code);
810 packet=(unsigned char *) RelinquishMagickMemory(packet);
815 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
823 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
825 % IsGIF() returns MagickTrue if the image format type, identified by the
826 % magick string, is GIF.
828 % The format of the IsGIF method is:
830 % MagickBooleanType IsGIF(const unsigned char *magick,const size_t length)
832 % A description of each parameter follows:
834 % o magick: compare image format pattern against these bytes.
836 % o length: Specifies the length of the magick string.
839 static MagickBooleanType IsGIF(const unsigned char *magick,const size_t length)
843 if (LocaleNCompare((char *) magick,"GIF8",4) == 0)
849 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
853 + R e a d B l o b B l o c k %
857 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
859 % ReadBlobBlock() reads data from the image file and returns it. The
860 % amount of data is determined by first reading a count byte. The number
861 % of bytes read is returned.
863 % The format of the ReadBlobBlock method is:
865 % ssize_t ReadBlobBlock(Image *image,unsigned char *data)
867 % A description of each parameter follows:
869 % o image: the image.
871 % o data: Specifies an area to place the information requested from
875 static ssize_t ReadBlobBlock(Image *image,unsigned char *data)
883 assert(image != (Image *) NULL);
884 assert(image->signature == MagickCoreSignature);
885 assert(data != (unsigned char *) NULL);
886 count=ReadBlob(image,1,&block_count);
889 count=ReadBlob(image,(size_t) block_count,data);
890 if (count != (ssize_t) block_count)
896 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
900 % R e a d G I F I m a g e %
904 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
906 % ReadGIFImage() reads a Compuserve Graphics image file and returns it.
907 % It allocates the memory necessary for the new Image structure and returns a
908 % pointer to the new image.
910 % The format of the ReadGIFImage method is:
912 % Image *ReadGIFImage(const ImageInfo *image_info,ExceptionInfo *exception)
914 % A description of each parameter follows:
916 % o image_info: the image info.
918 % o exception: return any errors or warnings in this structure.
922 static void *DestroyGIFProfile(void *profile)
924 return((void *) DestroyStringInfo((StringInfo *) profile));
927 static MagickBooleanType PingGIFImage(Image *image,ExceptionInfo *exception)
934 assert(image != (Image *) NULL);
935 assert(image->signature == MagickCoreSignature);
936 if (image->debug != MagickFalse)
937 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
938 if (ReadBlob(image,1,&data_size) != 1)
939 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
940 if (data_size > MaximumLZWBits)
941 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
942 if (ReadBlob(image,1,&length) != 1)
943 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
946 if (ReadBlob(image,length,buffer) != (ssize_t) length)
947 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
948 if (ReadBlob(image,1,&length) != 1)
949 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
954 static Image *ReadGIFImage(const ImageInfo *image_info,ExceptionInfo *exception)
956 #define BitSet(byte,bit) (((byte) & (bit)) == (bit))
957 #define LSBFirstOrder(x,y) (((y) << 8) | (x))
958 #define ThrowGIFException(exception,message) \
960 if (profiles != (LinkedListInfo *) NULL) \
961 profiles=DestroyLinkedList(profiles,DestroyGIFProfile); \
962 if (global_colormap != (unsigned char *) NULL) \
963 global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap); \
964 if (meta_image != (Image *) NULL) \
965 meta_image=DestroyImage(meta_image); \
966 ThrowReaderException((exception),(message)); \
982 register unsigned char
1006 assert(image_info != (const ImageInfo *) NULL);
1007 assert(image_info->signature == MagickCoreSignature);
1008 if (image_info->debug != MagickFalse)
1009 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1010 image_info->filename);
1011 assert(exception != (ExceptionInfo *) NULL);
1012 assert(exception->signature == MagickCoreSignature);
1013 image=AcquireImage(image_info,exception);
1014 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1015 if (status == MagickFalse)
1017 image=DestroyImageList(image);
1018 return((Image *) NULL);
1021 Determine if this a GIF file.
1023 count=ReadBlob(image,6,buffer);
1024 if ((count != 6) || ((LocaleNCompare((char *) buffer,"GIF87",5) != 0) &&
1025 (LocaleNCompare((char *) buffer,"GIF89",5) != 0)))
1026 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1027 (void) memset(buffer,0,sizeof(buffer));
1028 meta_image=AcquireImage(image_info,exception); /* metadata container */
1029 meta_image->page.width=ReadBlobLSBShort(image);
1030 meta_image->page.height=ReadBlobLSBShort(image);
1031 flag=(unsigned char) ReadBlobByte(image);
1032 profiles=(LinkedListInfo *) NULL;
1033 background=(unsigned char) ReadBlobByte(image);
1034 c=(unsigned char) ReadBlobByte(image); /* reserved */
1036 global_colors=one << (((size_t) flag & 0x07)+1);
1037 global_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
1038 MagickMax(global_colors,256),3UL*sizeof(*global_colormap));
1039 if (global_colormap == (unsigned char *) NULL)
1040 ThrowGIFException(ResourceLimitError,"MemoryAllocationFailed");
1041 (void) memset(global_colormap,0,3*MagickMax(global_colors,256)*
1042 sizeof(*global_colormap));
1043 if (BitSet((int) flag,0x80) != 0)
1045 count=ReadBlob(image,(size_t) (3*global_colors),global_colormap);
1046 if (count != (ssize_t) (3*global_colors))
1047 ThrowGIFException(CorruptImageError,"InsufficientImageDataInFile");
1054 count=ReadBlob(image,1,&c);
1057 if (c == (unsigned char) ';')
1058 break; /* terminator */
1059 if (c == (unsigned char) '!')
1062 GIF Extension block.
1064 (void) memset(buffer,0,sizeof(buffer));
1065 count=ReadBlob(image,1,&c);
1067 ThrowGIFException(CorruptImageError,"UnableToReadExtensionBlock");
1073 Read graphics control extension.
1075 while (ReadBlobBlock(image,buffer) != 0) ;
1076 meta_image->dispose=(DisposeType) ((buffer[0] >> 2) & 0x07);
1077 meta_image->delay=((size_t) buffer[2] << 8) | buffer[1];
1078 if ((ssize_t) (buffer[0] & 0x01) == 0x01)
1079 opacity=(ssize_t) buffer[3];
1091 Read comment extension.
1093 comments=AcquireString((char *) NULL);
1094 for (length=0; ; length+=count)
1096 count=(ssize_t) ReadBlobBlock(image,buffer);
1100 (void) ConcatenateString(&comments,(const char *) buffer);
1102 (void) SetImageProperty(meta_image,"comment",comments,exception);
1103 comments=DestroyString(comments);
1112 Read Netscape Loop extension.
1115 if (ReadBlobBlock(image,buffer) != 0)
1116 loop=LocaleNCompare((char *) buffer,"NETSCAPE2.0",11) == 0 ?
1117 MagickTrue : MagickFalse;
1118 if (loop != MagickFalse)
1119 while (ReadBlobBlock(image,buffer) != 0)
1121 meta_image->iterations=((size_t) buffer[2] << 8) | buffer[1];
1122 if (meta_image->iterations != 0)
1123 meta_image->iterations++;
1128 name[MagickPathExtent];
1148 Store GIF application extension as a generic profile.
1150 icc=LocaleNCompare((char *) buffer,"ICCRGBG1012",11) == 0 ?
1151 MagickTrue : MagickFalse;
1152 magick=LocaleNCompare((char *) buffer,"ImageMagick",11) == 0 ?
1153 MagickTrue : MagickFalse;
1154 i8bim=LocaleNCompare((char *) buffer,"MGK8BIM0000",11) == 0 ?
1155 MagickTrue : MagickFalse;
1156 iptc=LocaleNCompare((char *) buffer,"MGKIPTC0000",11) == 0 ?
1157 MagickTrue : MagickFalse;
1158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1159 " Reading GIF application extension");
1160 info=(unsigned char *) AcquireQuantumMemory(255UL,
1162 if (info == (unsigned char *) NULL)
1163 ThrowGIFException(ResourceLimitError,
1164 "MemoryAllocationFailed");
1165 (void) memset(info,0,255UL*sizeof(*info));
1166 reserved_length=255;
1167 for (info_length=0; ; )
1169 block_length=(int) ReadBlobBlock(image,&info[info_length]);
1170 if (block_length == 0)
1172 info_length+=block_length;
1173 if (info_length > (reserved_length-255))
1175 reserved_length+=4096;
1176 info=(unsigned char *) ResizeQuantumMemory(info,(size_t)
1177 reserved_length,sizeof(*info));
1178 if (info == (unsigned char *) NULL)
1180 info=(unsigned char *) RelinquishMagickMemory(info);
1181 ThrowGIFException(ResourceLimitError,
1182 "MemoryAllocationFailed");
1186 profile=BlobToStringInfo(info,(size_t) info_length);
1187 if (profile == (StringInfo *) NULL)
1189 info=(unsigned char *) RelinquishMagickMemory(info);
1190 ThrowGIFException(ResourceLimitError,
1191 "MemoryAllocationFailed");
1193 if (i8bim != MagickFalse)
1194 (void) CopyMagickString(name,"8bim",sizeof(name));
1195 else if (icc != MagickFalse)
1196 (void) CopyMagickString(name,"icc",sizeof(name));
1197 else if (iptc != MagickFalse)
1198 (void) CopyMagickString(name,"iptc",sizeof(name));
1199 else if (magick != MagickFalse)
1201 (void) CopyMagickString(name,"magick",sizeof(name));
1202 meta_image->gamma=StringToDouble((char *) info+6,
1206 (void) FormatLocaleString(name,sizeof(name),"gif:%.11s",
1208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1209 " profile name=%s",name);
1210 info=(unsigned char *) RelinquishMagickMemory(info);
1211 if (magick != MagickFalse)
1212 profile=DestroyStringInfo(profile);
1215 if (profiles == (LinkedListInfo *) NULL)
1216 profiles=NewLinkedList(0);
1217 SetStringInfoName(profile,name);
1218 (void) AppendValueToLinkedList(profiles,profile);
1225 while (ReadBlobBlock(image,buffer) != 0) ;
1230 if (c != (unsigned char) ',')
1233 if (image_count != 1)
1236 Allocate next image structure.
1238 AcquireNextImage(image_info,image,exception);
1239 if (GetNextImageInList(image) == (Image *) NULL)
1244 image=SyncNextImageInList(image);
1247 Read image attributes.
1249 meta_image->page.x=(ssize_t) ReadBlobLSBShort(image);
1250 meta_image->page.y=(ssize_t) ReadBlobLSBShort(image);
1251 meta_image->scene=image->scene;
1252 (void) CloneImageProperties(image,meta_image);
1253 DestroyImageProperties(meta_image);
1254 image->storage_class=PseudoClass;
1255 image->compression=LZWCompression;
1256 image->columns=ReadBlobLSBShort(image);
1257 image->rows=ReadBlobLSBShort(image);
1259 flag=(unsigned char) ReadBlobByte(image);
1260 image->interlace=BitSet((int) flag,0x40) != 0 ? GIFInterlace : NoInterlace;
1261 local_colors=BitSet((int) flag,0x80) == 0 ? global_colors : one <<
1262 ((size_t) (flag & 0x07)+1);
1263 image->colors=local_colors;
1264 if (opacity >= (ssize_t) image->colors)
1265 image->colors=(size_t) (opacity+1);
1266 image->ticks_per_second=100;
1267 image->alpha_trait=opacity >= 0 ? BlendPixelTrait : UndefinedPixelTrait;
1268 if ((image->columns == 0) || (image->rows == 0))
1269 ThrowGIFException(CorruptImageError,"NegativeOrZeroImageSize");
1271 Inititialize colormap.
1273 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
1274 ThrowGIFException(ResourceLimitError,"MemoryAllocationFailed");
1275 if (BitSet((int) flag,0x80) == 0)
1278 Use global colormap.
1281 for (i=0; i < (ssize_t) image->colors; i++)
1283 image->colormap[i].red=(double) ScaleCharToQuantum(*p++);
1284 image->colormap[i].green=(double) ScaleCharToQuantum(*p++);
1285 image->colormap[i].blue=(double) ScaleCharToQuantum(*p++);
1288 image->colormap[i].alpha=(double) TransparentAlpha;
1289 image->transparent_color=image->colormap[opacity];
1292 image->background_color=image->colormap[MagickMin((ssize_t) background,
1293 (ssize_t) image->colors-1)];
1301 Read local colormap.
1303 colormap=(unsigned char *) AcquireQuantumMemory((size_t)
1304 MagickMax(local_colors,256),3UL*sizeof(*colormap));
1305 if (colormap == (unsigned char *) NULL)
1306 ThrowGIFException(ResourceLimitError,"MemoryAllocationFailed");
1307 (void) memset(colormap,0,3*MagickMax(local_colors,256)*
1309 count=ReadBlob(image,(3*local_colors)*sizeof(*colormap),colormap);
1310 if (count != (ssize_t) (3*local_colors))
1312 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1313 ThrowGIFException(CorruptImageError,"InsufficientImageDataInFile");
1316 for (i=0; i < (ssize_t) image->colors; i++)
1318 image->colormap[i].red=(double) ScaleCharToQuantum(*p++);
1319 image->colormap[i].green=(double) ScaleCharToQuantum(*p++);
1320 image->colormap[i].blue=(double) ScaleCharToQuantum(*p++);
1322 image->colormap[i].alpha=(double) TransparentAlpha;
1324 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1326 if (image->gamma == 1.0)
1328 for (i=0; i < (ssize_t) image->colors; i++)
1329 if (IsPixelInfoGray(image->colormap+i) == MagickFalse)
1331 (void) SetImageColorspace(image,i == (ssize_t) image->colors ?
1332 GRAYColorspace : RGBColorspace,exception);
1334 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
1335 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
1337 status=SetImageExtent(image,image->columns,image->rows,exception);
1338 if (status == MagickFalse)
1340 if (profiles != (LinkedListInfo *) NULL)
1341 profiles=DestroyLinkedList(profiles,DestroyGIFProfile);
1342 global_colormap=(unsigned char *) RelinquishMagickMemory(
1344 meta_image=DestroyImage(meta_image);
1345 return(DestroyImageList(image));
1350 if (image_info->ping != MagickFalse)
1351 status=PingGIFImage(image,exception);
1353 status=DecodeImage(image,opacity,exception);
1354 if ((image_info->ping == MagickFalse) && (status == MagickFalse))
1355 ThrowGIFException(CorruptImageError,"CorruptImage");
1356 if (profiles != (LinkedListInfo *) NULL)
1364 ResetLinkedListIterator(profiles);
1365 profile=(StringInfo *) GetNextValueInLinkedList(profiles);
1366 while (profile != (StringInfo *) NULL)
1368 (void) SetImageProfile(image,GetStringInfoName(profile),profile,
1370 profile=(StringInfo *) GetNextValueInLinkedList(profiles);
1372 profiles=DestroyLinkedList(profiles,DestroyGIFProfile);
1374 duration+=image->delay*image->iterations;
1375 if (image_info->number_scenes != 0)
1376 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
1379 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
1380 image->scene-1,image->scene);
1381 if (status == MagickFalse)
1384 image->duration=duration;
1385 if (profiles != (LinkedListInfo *) NULL)
1386 profiles=DestroyLinkedList(profiles,DestroyGIFProfile);
1387 meta_image=DestroyImage(meta_image);
1388 global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap);
1389 if ((image->columns == 0) || (image->rows == 0))
1390 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
1391 (void) CloseBlob(image);
1392 if (status == MagickFalse)
1393 return(DestroyImageList(image));
1394 return(GetFirstImageInList(image));
1398 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1402 % R e g i s t e r G I F I m a g e %
1406 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1408 % RegisterGIFImage() adds properties for the GIF image format to
1409 % the list of supported formats. The properties include the image format
1410 % tag, a method to read and/or write the format, whether the format
1411 % supports the saving of more than one frame to the same file or blob,
1412 % whether the format supports native in-memory I/O, and a brief
1413 % description of the format.
1415 % The format of the RegisterGIFImage method is:
1417 % size_t RegisterGIFImage(void)
1420 ModuleExport size_t RegisterGIFImage(void)
1425 entry=AcquireMagickInfo("GIF","GIF",
1426 "CompuServe graphics interchange format");
1427 entry->decoder=(DecodeImageHandler *) ReadGIFImage;
1428 entry->encoder=(EncodeImageHandler *) WriteGIFImage;
1429 entry->magick=(IsImageFormatHandler *) IsGIF;
1430 entry->mime_type=ConstantString("image/gif");
1431 (void) RegisterMagickInfo(entry);
1432 entry=AcquireMagickInfo("GIF","GIF87",
1433 "CompuServe graphics interchange format");
1434 entry->decoder=(DecodeImageHandler *) ReadGIFImage;
1435 entry->encoder=(EncodeImageHandler *) WriteGIFImage;
1436 entry->magick=(IsImageFormatHandler *) IsGIF;
1437 entry->flags^=CoderAdjoinFlag;
1438 entry->version=ConstantString("version 87a");
1439 entry->mime_type=ConstantString("image/gif");
1440 (void) RegisterMagickInfo(entry);
1441 return(MagickImageCoderSignature);
1445 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1449 % U n r e g i s t e r G I F I m a g e %
1453 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1455 % UnregisterGIFImage() removes format registrations made by the
1456 % GIF module from the list of supported formats.
1458 % The format of the UnregisterGIFImage method is:
1460 % UnregisterGIFImage(void)
1463 ModuleExport void UnregisterGIFImage(void)
1465 (void) UnregisterMagickInfo("GIF");
1466 (void) UnregisterMagickInfo("GIF87");
1470 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1474 % W r i t e G I F I m a g e %
1478 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1480 % WriteGIFImage() writes an image to a file in the Compuserve Graphics
1483 % The format of the WriteGIFImage method is:
1485 % MagickBooleanType WriteGIFImage(const ImageInfo *image_info,
1486 % Image *image,ExceptionInfo *exception)
1488 % A description of each parameter follows.
1490 % o image_info: the image info.
1492 % o image: The image.
1494 % o exception: return any errors or warnings in this structure.
1497 static MagickBooleanType WriteGIFImage(const ImageInfo *image_info,Image *image,
1498 ExceptionInfo *exception)
1518 register unsigned char
1537 Open output image file.
1539 assert(image_info != (const ImageInfo *) NULL);
1540 assert(image_info->signature == MagickCoreSignature);
1541 assert(image != (Image *) NULL);
1542 assert(image->signature == MagickCoreSignature);
1543 if (image->debug != MagickFalse)
1544 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1545 assert(exception != (ExceptionInfo *) NULL);
1546 assert(exception->signature == MagickCoreSignature);
1547 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
1548 if (status == MagickFalse)
1553 global_colormap=(unsigned char *) AcquireQuantumMemory(768UL,
1554 sizeof(*global_colormap));
1555 colormap=(unsigned char *) AcquireQuantumMemory(768UL,sizeof(*colormap));
1556 if ((global_colormap == (unsigned char *) NULL) ||
1557 (colormap == (unsigned char *) NULL))
1559 if (global_colormap != (unsigned char *) NULL)
1560 global_colormap=(unsigned char *) RelinquishMagickMemory(
1562 if (colormap != (unsigned char *) NULL)
1563 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1564 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1566 for (i=0; i < 768; i++)
1567 colormap[i]=(unsigned char) 0;
1571 write_info=CloneImageInfo(image_info);
1572 if (LocaleCompare(write_info->magick,"GIF87") != 0)
1573 (void) WriteBlob(image,6,(unsigned char *) "GIF89a");
1576 (void) WriteBlob(image,6,(unsigned char *) "GIF87a");
1577 write_info->adjoin=MagickFalse;
1580 Determine image bounding box.
1582 page.width=image->columns;
1583 if (image->page.width > page.width)
1584 page.width=image->page.width;
1585 page.height=image->rows;
1586 if (image->page.height > page.height)
1587 page.height=image->page.height;
1588 page.x=image->page.x;
1589 page.y=image->page.y;
1590 (void) WriteBlobLSBShort(image,(unsigned short) page.width);
1591 (void) WriteBlobLSBShort(image,(unsigned short) page.height);
1593 Write images to file.
1595 if ((write_info->adjoin != MagickFalse) &&
1596 (GetNextImageInList(image) != (Image *) NULL))
1597 write_info->interlace=NoInterlace;
1600 imageListLength=GetImageListLength(image);
1603 (void) TransformImageColorspace(image,sRGBColorspace,exception);
1605 if (IsImageOpaque(image,exception) != MagickFalse)
1607 if ((image->storage_class == DirectClass) || (image->colors > 256))
1608 (void) SetImageType(image,PaletteType,exception);
1617 Identify transparent colormap index.
1619 if ((image->storage_class == DirectClass) || (image->colors > 256))
1620 (void) SetImageType(image,PaletteBilevelAlphaType,exception);
1621 for (i=0; i < (ssize_t) image->colors; i++)
1622 if (image->colormap[i].alpha != OpaqueAlpha)
1629 alpha=fabs(image->colormap[i].alpha-TransparentAlpha);
1630 beta=fabs(image->colormap[opacity].alpha-TransparentAlpha);
1636 (void) SetImageType(image,PaletteBilevelAlphaType,exception);
1637 for (i=0; i < (ssize_t) image->colors; i++)
1638 if (image->colormap[i].alpha != OpaqueAlpha)
1645 alpha=fabs(image->colormap[i].alpha-TransparentAlpha);
1646 beta=fabs(image->colormap[opacity].alpha-TransparentAlpha);
1653 image->colormap[opacity].red=image->transparent_color.red;
1654 image->colormap[opacity].green=image->transparent_color.green;
1655 image->colormap[opacity].blue=image->transparent_color.blue;
1658 if ((image->storage_class == DirectClass) || (image->colors > 256))
1659 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1660 for (bits_per_pixel=1; bits_per_pixel < 8; bits_per_pixel++)
1661 if ((one << bits_per_pixel) >= image->colors)
1664 for (i=0; i < (ssize_t) image->colors; i++)
1666 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].red));
1667 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].green));
1668 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].blue));
1670 for ( ; i < (ssize_t) (one << bits_per_pixel); i++)
1672 *q++=(unsigned char) 0x0;
1673 *q++=(unsigned char) 0x0;
1674 *q++=(unsigned char) 0x0;
1676 if ((GetPreviousImageInList(image) == (Image *) NULL) ||
1677 (write_info->adjoin == MagickFalse))
1680 Write global colormap.
1683 c|=(8-1) << 4; /* color resolution */
1684 c|=(bits_per_pixel-1); /* size of global colormap */
1685 (void) WriteBlobByte(image,(unsigned char) c);
1686 for (j=0; j < (ssize_t) image->colors; j++)
1687 if (IsPixelInfoEquivalent(&image->background_color,image->colormap+j))
1689 (void) WriteBlobByte(image,(unsigned char)
1690 (j == (ssize_t) image->colors ? 0 : j)); /* background color */
1691 (void) WriteBlobByte(image,(unsigned char) 0x00); /* reserved */
1692 length=(size_t) (3*(one << bits_per_pixel));
1693 (void) WriteBlob(image,length,colormap);
1694 for (j=0; j < 768; j++)
1695 global_colormap[j]=colormap[j];
1697 if (LocaleCompare(write_info->magick,"GIF87") != 0)
1703 Write graphics control extension.
1705 (void) WriteBlobByte(image,(unsigned char) 0x21);
1706 (void) WriteBlobByte(image,(unsigned char) 0xf9);
1707 (void) WriteBlobByte(image,(unsigned char) 0x04);
1708 c=image->dispose << 2;
1711 (void) WriteBlobByte(image,(unsigned char) c);
1712 delay=(size_t) (100*image->delay/MagickMax((size_t)
1713 image->ticks_per_second,1));
1714 (void) WriteBlobLSBShort(image,(unsigned short) delay);
1715 (void) WriteBlobByte(image,(unsigned char) (opacity >= 0 ? opacity :
1717 (void) WriteBlobByte(image,(unsigned char) 0x00);
1718 value=GetImageProperty(image,"comment",exception);
1719 if ((LocaleCompare(write_info->magick,"GIF87") != 0) &&
1720 (value != (const char *) NULL))
1729 Write comment extension.
1731 (void) WriteBlobByte(image,(unsigned char) 0x21);
1732 (void) WriteBlobByte(image,(unsigned char) 0xfe);
1733 for (p=value; *p != '\0'; )
1735 count=MagickMin(strlen(p),255);
1736 (void) WriteBlobByte(image,(unsigned char) count);
1737 for (i=0; i < (ssize_t) count; i++)
1738 (void) WriteBlobByte(image,(unsigned char) *p++);
1740 (void) WriteBlobByte(image,(unsigned char) 0x00);
1742 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
1743 (GetNextImageInList(image) != (Image *) NULL) &&
1744 (image->iterations != 1))
1747 Write Netscape Loop extension.
1749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1750 " Writing GIF Extension %s","NETSCAPE2.0");
1751 (void) WriteBlobByte(image,(unsigned char) 0x21);
1752 (void) WriteBlobByte(image,(unsigned char) 0xff);
1753 (void) WriteBlobByte(image,(unsigned char) 0x0b);
1754 (void) WriteBlob(image,11,(unsigned char *) "NETSCAPE2.0");
1755 (void) WriteBlobByte(image,(unsigned char) 0x03);
1756 (void) WriteBlobByte(image,(unsigned char) 0x01);
1757 (void) WriteBlobLSBShort(image,(unsigned short) (image->iterations ?
1758 image->iterations-1 : 0));
1759 (void) WriteBlobByte(image,(unsigned char) 0x00);
1761 if ((image->gamma != 1.0f/2.2f))
1764 attributes[MagickPathExtent];
1770 Write ImageMagick extension.
1772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1773 " Writing GIF Extension %s","ImageMagick");
1774 (void) WriteBlobByte(image,(unsigned char) 0x21);
1775 (void) WriteBlobByte(image,(unsigned char) 0xff);
1776 (void) WriteBlobByte(image,(unsigned char) 0x0b);
1777 (void) WriteBlob(image,11,(unsigned char *) "ImageMagick");
1778 count=FormatLocaleString(attributes,MagickPathExtent,"gamma=%g",
1780 (void) WriteBlobByte(image,(unsigned char) count);
1781 (void) WriteBlob(image,(size_t) count,(unsigned char *) attributes);
1782 (void) WriteBlobByte(image,(unsigned char) 0x00);
1784 ResetImageProfileIterator(image);
1793 name=GetNextImageProfile(image);
1794 if (name == (const char *) NULL)
1796 profile=GetImageProfile(image,name);
1797 if (profile != (StringInfo *) NULL)
1799 if ((LocaleCompare(name,"ICC") == 0) ||
1800 (LocaleCompare(name,"ICM") == 0) ||
1801 (LocaleCompare(name,"IPTC") == 0) ||
1802 (LocaleCompare(name,"8BIM") == 0) ||
1803 (LocaleNCompare(name,"gif:",4) == 0))
1811 datum=GetStringInfoDatum(profile);
1812 length=GetStringInfoLength(profile);
1813 (void) WriteBlobByte(image,(unsigned char) 0x21);
1814 (void) WriteBlobByte(image,(unsigned char) 0xff);
1815 (void) WriteBlobByte(image,(unsigned char) 0x0b);
1816 if ((LocaleCompare(name,"ICC") == 0) ||
1817 (LocaleCompare(name,"ICM") == 0))
1820 Write ICC extension.
1822 (void) WriteBlob(image,11,(unsigned char *) "ICCRGBG1012");
1823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1824 " Writing GIF Extension %s","ICCRGBG1012");
1827 if ((LocaleCompare(name,"IPTC") == 0))
1830 Write IPTC extension.
1832 (void) WriteBlob(image,11,(unsigned char *) "MGKIPTC0000");
1833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1834 " Writing GIF Extension %s","MGKIPTC0000");
1837 if ((LocaleCompare(name,"8BIM") == 0))
1840 Write 8BIM extension.
1842 (void) WriteBlob(image,11,(unsigned char *)
1844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1845 " Writing GIF Extension %s","MGK8BIM0000");
1850 extension[MagickPathExtent];
1853 Write generic extension.
1855 (void) CopyMagickString(extension,name+4,
1857 (void) WriteBlob(image,11,(unsigned char *) extension);
1858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1859 " Writing GIF Extension %s",name);
1862 while ((ssize_t) length > offset)
1867 if ((length-offset) < 255)
1868 block_length=length-offset;
1871 (void) WriteBlobByte(image,(unsigned char) block_length);
1872 (void) WriteBlob(image,(size_t) block_length,datum+offset);
1873 offset+=(ssize_t) block_length;
1875 (void) WriteBlobByte(image,(unsigned char) 0x00);
1880 (void) WriteBlobByte(image,','); /* image separator */
1882 Write the image header.
1884 page.x=image->page.x;
1885 page.y=image->page.y;
1886 if ((image->page.width != 0) && (image->page.height != 0))
1888 (void) WriteBlobLSBShort(image,(unsigned short) (page.x < 0 ? 0 : page.x));
1889 (void) WriteBlobLSBShort(image,(unsigned short) (page.y < 0 ? 0 : page.y));
1890 (void) WriteBlobLSBShort(image,(unsigned short) image->columns);
1891 (void) WriteBlobLSBShort(image,(unsigned short) image->rows);
1893 if (write_info->interlace != NoInterlace)
1894 c|=0x40; /* pixel data is interlaced */
1895 for (j=0; j < (ssize_t) (3*image->colors); j++)
1896 if (colormap[j] != global_colormap[j])
1898 if (j == (ssize_t) (3*image->colors))
1899 (void) WriteBlobByte(image,(unsigned char) c);
1903 c|=(bits_per_pixel-1); /* size of local colormap */
1904 (void) WriteBlobByte(image,(unsigned char) c);
1905 length=(size_t) (3*(one << bits_per_pixel));
1906 (void) WriteBlob(image,length,colormap);
1909 Write the image data.
1911 c=(int) MagickMax(bits_per_pixel,2);
1912 (void) WriteBlobByte(image,(unsigned char) c);
1913 status=EncodeImage(write_info,image,(size_t) MagickMax(bits_per_pixel,2)+1,
1915 if (status == MagickFalse)
1917 global_colormap=(unsigned char *) RelinquishMagickMemory(
1919 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1920 write_info=DestroyImageInfo(write_info);
1921 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1923 (void) WriteBlobByte(image,(unsigned char) 0x00);
1924 if (GetNextImageInList(image) == (Image *) NULL)
1926 image=SyncNextImageInList(image);
1928 status=SetImageProgress(image,SaveImagesTag,scene,imageListLength);
1929 if (status == MagickFalse)
1931 } while (write_info->adjoin != MagickFalse);
1932 (void) WriteBlobByte(image,';'); /* terminator */
1933 global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap);
1934 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1935 write_info=DestroyImageInfo(write_info);
1936 (void) CloseBlob(image);