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/static.h"
70 #include "MagickCore/string_.h"
71 #include "MagickCore/string-private.h"
72 #include "MagickCore/module.h"
77 #define MaximumLZWBits 12
78 #define MaximumLZWCode (1UL << MaximumLZWBits)
83 typedef struct _LZWCodeInfo
96 typedef struct _LZWStack
104 typedef struct _LZWInfo
132 Forward declarations.
135 GetNextLZWCode(LZWInfo *,const size_t);
137 static MagickBooleanType
138 WriteGIFImage(const ImageInfo *,Image *,ExceptionInfo *);
141 ReadBlobBlock(Image *,unsigned char *);
144 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
148 % D e c o d e I m a g e %
152 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
154 % DecodeImage uncompresses an image via GIF-coding.
156 % The format of the DecodeImage method is:
158 % MagickBooleanType DecodeImage(Image *image,const ssize_t opacity)
160 % A description of each parameter follows:
162 % o image: the address of a structure of type Image.
164 % o opacity: The colormap index associated with the transparent color.
168 static LZWInfo *RelinquishLZWInfo(LZWInfo *lzw_info)
170 if (lzw_info->table[0] != (size_t *) NULL)
171 lzw_info->table[0]=(size_t *) RelinquishMagickMemory(
173 if (lzw_info->table[1] != (size_t *) NULL)
174 lzw_info->table[1]=(size_t *) RelinquishMagickMemory(
176 if (lzw_info->stack != (LZWStack *) NULL)
178 if (lzw_info->stack->codes != (size_t *) NULL)
179 lzw_info->stack->codes=(size_t *) RelinquishMagickMemory(
180 lzw_info->stack->codes);
181 lzw_info->stack=(LZWStack *) RelinquishMagickMemory(lzw_info->stack);
183 lzw_info=(LZWInfo *) RelinquishMagickMemory(lzw_info);
184 return((LZWInfo *) NULL);
187 static inline void ResetLZWInfo(LZWInfo *lzw_info)
192 lzw_info->bits=lzw_info->data_size+1;
194 lzw_info->maximum_code=one << lzw_info->bits;
195 lzw_info->slot=lzw_info->maximum_data_value+3;
196 lzw_info->genesis=MagickTrue;
199 static LZWInfo *AcquireLZWInfo(Image *image,const size_t data_size)
210 lzw_info=(LZWInfo *) AcquireMagickMemory(sizeof(*lzw_info));
211 if (lzw_info == (LZWInfo *) NULL)
212 return((LZWInfo *) NULL);
213 (void) ResetMagickMemory(lzw_info,0,sizeof(*lzw_info));
214 lzw_info->image=image;
215 lzw_info->data_size=data_size;
217 lzw_info->maximum_data_value=(one << data_size)-1;
218 lzw_info->clear_code=lzw_info->maximum_data_value+1;
219 lzw_info->end_code=lzw_info->maximum_data_value+2;
220 lzw_info->table[0]=(size_t *) AcquireQuantumMemory(MaximumLZWCode,
221 sizeof(**lzw_info->table));
222 lzw_info->table[1]=(size_t *) AcquireQuantumMemory(MaximumLZWCode,
223 sizeof(**lzw_info->table));
224 if ((lzw_info->table[0] == (size_t *) NULL) ||
225 (lzw_info->table[1] == (size_t *) NULL))
227 lzw_info=RelinquishLZWInfo(lzw_info);
228 return((LZWInfo *) NULL);
230 for (i=0; i <= (ssize_t) lzw_info->maximum_data_value; i++)
232 lzw_info->table[0][i]=0;
233 lzw_info->table[1][i]=(size_t) i;
235 ResetLZWInfo(lzw_info);
236 lzw_info->code_info.buffer[0]='\0';
237 lzw_info->code_info.buffer[1]='\0';
238 lzw_info->code_info.count=2;
239 lzw_info->code_info.bit=8*lzw_info->code_info.count;
240 lzw_info->code_info.eof=MagickFalse;
241 lzw_info->genesis=MagickTrue;
242 lzw_info->stack=(LZWStack *) AcquireMagickMemory(sizeof(*lzw_info->stack));
243 if (lzw_info->stack == (LZWStack *) NULL)
245 lzw_info=RelinquishLZWInfo(lzw_info);
246 return((LZWInfo *) NULL);
248 lzw_info->stack->codes=(size_t *) AcquireQuantumMemory(2UL*
249 MaximumLZWCode,sizeof(*lzw_info->stack->codes));
250 if (lzw_info->stack->codes == (size_t *) NULL)
252 lzw_info=RelinquishLZWInfo(lzw_info);
253 return((LZWInfo *) NULL);
255 lzw_info->stack->index=lzw_info->stack->codes;
256 lzw_info->stack->top=lzw_info->stack->codes+2*MaximumLZWCode;
260 static inline int GetNextLZWCode(LZWInfo *lzw_info,const size_t bits)
271 while (((lzw_info->code_info.bit+bits) > (8*lzw_info->code_info.count)) &&
272 (lzw_info->code_info.eof == MagickFalse))
277 lzw_info->code_info.buffer[0]=lzw_info->code_info.buffer[
278 lzw_info->code_info.count-2];
279 lzw_info->code_info.buffer[1]=lzw_info->code_info.buffer[
280 lzw_info->code_info.count-1];
281 lzw_info->code_info.bit-=8*(lzw_info->code_info.count-2);
282 lzw_info->code_info.count=2;
283 count=ReadBlobBlock(lzw_info->image,&lzw_info->code_info.buffer[
284 lzw_info->code_info.count]);
286 lzw_info->code_info.count+=count;
288 lzw_info->code_info.eof=MagickTrue;
290 if ((lzw_info->code_info.bit+bits) > (8*lzw_info->code_info.count))
294 for (i=0; i < (ssize_t) bits; i++)
296 code|=((lzw_info->code_info.buffer[lzw_info->code_info.bit/8] &
297 (one << (lzw_info->code_info.bit % 8))) != 0) << i;
298 lzw_info->code_info.bit++;
303 static inline int PopLZWStack(LZWStack *stack_info)
305 if (stack_info->index <= stack_info->codes)
308 return((int) *stack_info->index);
311 static inline void PushLZWStack(LZWStack *stack_info,const size_t value)
313 if (stack_info->index >= stack_info->top)
315 *stack_info->index=value;
319 static int ReadBlobLZWByte(LZWInfo *lzw_info)
331 if (lzw_info->stack->index != lzw_info->stack->codes)
332 return(PopLZWStack(lzw_info->stack));
333 if (lzw_info->genesis != MagickFalse)
335 lzw_info->genesis=MagickFalse;
338 lzw_info->first_code=(size_t) GetNextLZWCode(lzw_info,lzw_info->bits);
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)
411 Allocate decoder tables.
413 assert(image != (Image *) NULL);
414 assert(image->signature == MagickCoreSignature);
415 if (image->debug != MagickFalse)
416 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
417 data_size=(unsigned char) ReadBlobByte(image);
418 if (data_size > MaximumLZWBits)
419 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
420 lzw_info=AcquireLZWInfo(image,data_size);
421 if (lzw_info == (LZWInfo *) NULL)
422 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
426 for (y=0; y < (ssize_t) image->rows; y++)
434 q=QueueAuthenticPixels(image,0,offset,image->columns,1,exception);
435 if (q == (Quantum *) NULL)
437 for (x=0; x < (ssize_t) image->columns; )
439 c=ReadBlobLZWByte(lzw_info);
442 index=ConstrainColormapIndex(image,(ssize_t) c,exception);
443 SetPixelIndex(image,(Quantum) index,q);
444 SetPixelViaPixelInfo(image,image->colormap+index,q);
445 SetPixelAlpha(image,index == opacity ? TransparentAlpha : OpaqueAlpha,q);
447 q+=GetPixelChannels(image);
449 if (SyncAuthenticPixels(image,exception) == MagickFalse)
451 if (x < (ssize_t) image->columns)
453 if (image->interlace == NoInterlace)
481 if ((pass == 0) && (offset >= (ssize_t) image->rows))
486 if ((pass == 1) && (offset >= (ssize_t) image->rows))
491 if ((pass == 2) && (offset >= (ssize_t) image->rows))
498 lzw_info=RelinquishLZWInfo(lzw_info);
499 if (y < (ssize_t) image->rows)
500 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
505 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
509 % E n c o d e I m a g e %
513 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
515 % EncodeImage compresses an image via GIF-coding.
517 % The format of the EncodeImage method is:
519 % MagickBooleanType EncodeImage(const ImageInfo *image_info,Image *image,
520 % const size_t data_size)
522 % A description of each parameter follows:
524 % o image_info: the image info.
526 % o image: the address of a structure of type Image.
528 % o data_size: The number of bits in the compressed packet.
531 static MagickBooleanType EncodeImage(const ImageInfo *image_info,Image *image,
532 const size_t data_size,ExceptionInfo *exception)
534 #define MaxCode(number_bits) ((one << (number_bits))-1)
535 #define MaxHashTable 5003
536 #define MaxGIFBits 12UL
537 #define MaxGIFTable (1UL << MaxGIFBits)
538 #define GIFOutputCode(code) \
544 datum|=(size_t) (code) << bits; \
546 datum=(size_t) (code); \
551 Add a character to current packet. \
553 packet[length++]=(unsigned char) (datum & 0xff); \
556 (void) WriteBlobByte(image,(unsigned char) length); \
557 (void) WriteBlob(image,length,packet); \
563 if (free_code > max_code) \
566 if (number_bits == MaxGIFBits) \
567 max_code=MaxGIFTable; \
569 max_code=MaxCode(number_bits); \
585 end_of_information_code,
605 Allocate encoder tables.
607 assert(image != (Image *) NULL);
609 packet=(unsigned char *) AcquireQuantumMemory(256,sizeof(*packet));
610 hash_code=(short *) AcquireQuantumMemory(MaxHashTable,sizeof(*hash_code));
611 hash_prefix=(short *) AcquireQuantumMemory(MaxHashTable,sizeof(*hash_prefix));
612 hash_suffix=(unsigned char *) AcquireQuantumMemory(MaxHashTable,
613 sizeof(*hash_suffix));
614 if ((packet == (unsigned char *) NULL) || (hash_code == (short *) NULL) ||
615 (hash_prefix == (short *) NULL) ||
616 (hash_suffix == (unsigned char *) NULL))
618 if (packet != (unsigned char *) NULL)
619 packet=(unsigned char *) RelinquishMagickMemory(packet);
620 if (hash_code != (short *) NULL)
621 hash_code=(short *) RelinquishMagickMemory(hash_code);
622 if (hash_prefix != (short *) NULL)
623 hash_prefix=(short *) RelinquishMagickMemory(hash_prefix);
624 if (hash_suffix != (unsigned char *) NULL)
625 hash_suffix=(unsigned char *) RelinquishMagickMemory(hash_suffix);
629 Initialize GIF encoder.
631 (void) ResetMagickMemory(hash_code,0,MaxHashTable*sizeof(*hash_code));
632 (void) ResetMagickMemory(hash_prefix,0,MaxHashTable*sizeof(*hash_prefix));
633 (void) ResetMagickMemory(hash_suffix,0,MaxHashTable*sizeof(*hash_suffix));
634 number_bits=data_size;
635 max_code=MaxCode(number_bits);
636 clear_code=((short) one << (data_size-1));
637 end_of_information_code=clear_code+1;
638 free_code=clear_code+2;
642 GIFOutputCode(clear_code);
649 for (y=0; y < (ssize_t) image->rows; y++)
651 register const Quantum
657 p=GetVirtualPixels(image,0,offset,image->columns,1,exception);
658 if (p == (const Quantum *) NULL)
662 waiting_code=(short) GetPixelIndex(image,p);
663 p+=GetPixelChannels(image);
665 for (x=(ssize_t) (y == 0 ? 1 : 0); x < (ssize_t) image->columns; x++)
670 index=(Quantum) ((size_t) GetPixelIndex(image,p) & 0xff);
671 p+=GetPixelChannels(image);
672 k=(ssize_t) (((size_t) index << (MaxGIFBits-8))+waiting_code);
673 if (k >= MaxHashTable)
675 next_pixel=MagickFalse;
677 if (hash_code[k] > 0)
679 if ((hash_prefix[k] == waiting_code) &&
680 (hash_suffix[k] == (unsigned char) index))
682 waiting_code=hash_code[k];
686 displacement=MaxHashTable-k;
692 if (hash_code[k] == 0)
694 if ((hash_prefix[k] == waiting_code) &&
695 (hash_suffix[k] == (unsigned char) index))
697 waiting_code=hash_code[k];
698 next_pixel=MagickTrue;
702 if (next_pixel != MagickFalse)
705 GIFOutputCode(waiting_code);
706 if (free_code < MaxGIFTable)
708 hash_code[k]=(short) free_code++;
709 hash_prefix[k]=waiting_code;
710 hash_suffix[k]=(unsigned char) index;
715 Fill the hash table with empty entries.
717 for (k=0; k < MaxHashTable; k++)
720 Reset compressor and issue a clear code.
722 free_code=clear_code+2;
723 GIFOutputCode(clear_code);
724 number_bits=data_size;
725 max_code=MaxCode(number_bits);
727 waiting_code=(short) index;
729 if (image_info->interlace == NoInterlace)
738 if (offset >= (ssize_t) image->rows)
748 if (offset >= (ssize_t) image->rows)
758 if (offset >= (ssize_t) image->rows)
773 Flush out the buffered code.
775 GIFOutputCode(waiting_code);
776 GIFOutputCode(end_of_information_code);
780 Add a character to current packet.
782 packet[length++]=(unsigned char) (datum & 0xff);
785 (void) WriteBlobByte(image,(unsigned char) length);
786 (void) WriteBlob(image,length,packet);
791 Flush accumulated data.
795 (void) WriteBlobByte(image,(unsigned char) length);
796 (void) WriteBlob(image,length,packet);
801 hash_suffix=(unsigned char *) RelinquishMagickMemory(hash_suffix);
802 hash_prefix=(short *) RelinquishMagickMemory(hash_prefix);
803 hash_code=(short *) RelinquishMagickMemory(hash_code);
804 packet=(unsigned char *) RelinquishMagickMemory(packet);
809 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
817 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
819 % IsGIF() returns MagickTrue if the image format type, identified by the
820 % magick string, is GIF.
822 % The format of the IsGIF method is:
824 % MagickBooleanType IsGIF(const unsigned char *magick,const size_t length)
826 % A description of each parameter follows:
828 % o magick: compare image format pattern against these bytes.
830 % o length: Specifies the length of the magick string.
833 static MagickBooleanType IsGIF(const unsigned char *magick,const size_t length)
837 if (LocaleNCompare((char *) magick,"GIF8",4) == 0)
843 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
847 + R e a d B l o b B l o c k %
851 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
853 % ReadBlobBlock() reads data from the image file and returns it. The
854 % amount of data is determined by first reading a count byte. The number
855 % of bytes read is returned.
857 % The format of the ReadBlobBlock method is:
859 % ssize_t ReadBlobBlock(Image *image,unsigned char *data)
861 % A description of each parameter follows:
863 % o image: the image.
865 % o data: Specifies an area to place the information requested from
869 static ssize_t ReadBlobBlock(Image *image,unsigned char *data)
877 assert(image != (Image *) NULL);
878 assert(image->signature == MagickCoreSignature);
879 assert(data != (unsigned char *) NULL);
880 count=ReadBlob(image,1,&block_count);
883 count=ReadBlob(image,(size_t) block_count,data);
884 if (count != (ssize_t) block_count)
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.
915 static MagickBooleanType PingGIFImage(Image *image,ExceptionInfo *exception)
922 assert(image != (Image *) NULL);
923 assert(image->signature == MagickCoreSignature);
924 if (image->debug != MagickFalse)
925 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
926 if (ReadBlob(image,1,&data_size) != 1)
927 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
928 if (data_size > MaximumLZWBits)
929 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
930 if (ReadBlob(image,1,&length) != 1)
931 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
934 if (ReadBlob(image,length,buffer) != (ssize_t) length)
935 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
936 if (ReadBlob(image,1,&length) != 1)
937 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
942 static Image *ReadGIFImage(const ImageInfo *image_info,ExceptionInfo *exception)
944 #define BitSet(byte,bit) (((byte) & (bit)) == (bit))
945 #define LSBFirstOrder(x,y) (((y) << 8) | (x))
952 number_extensionss=0;
960 register unsigned char
984 assert(image_info != (const ImageInfo *) NULL);
985 assert(image_info->signature == MagickCoreSignature);
986 if (image_info->debug != MagickFalse)
987 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
988 image_info->filename);
989 assert(exception != (ExceptionInfo *) NULL);
990 assert(exception->signature == MagickCoreSignature);
991 image=AcquireImage(image_info,exception);
992 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
993 if (status == MagickFalse)
995 image=DestroyImageList(image);
996 return((Image *) NULL);
999 Determine if this a GIF file.
1001 count=ReadBlob(image,6,buffer);
1002 if ((count != 6) || ((LocaleNCompare((char *) buffer,"GIF87",5) != 0) &&
1003 (LocaleNCompare((char *) buffer,"GIF89",5) != 0)))
1004 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1005 (void) ResetMagickMemory(buffer,0,sizeof(buffer));
1006 meta_image=AcquireImage(image_info,exception); /* metadata container */
1007 meta_image->page.width=ReadBlobLSBShort(image);
1008 meta_image->page.height=ReadBlobLSBShort(image);
1009 flag=(unsigned char) ReadBlobByte(image);
1010 background=(unsigned char) ReadBlobByte(image);
1011 c=(unsigned char) ReadBlobByte(image); /* reserved */
1013 global_colors=one << (((size_t) flag & 0x07)+1);
1014 global_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
1015 MagickMax(global_colors,256),3UL*sizeof(*global_colormap));
1016 if (global_colormap == (unsigned char *) NULL)
1018 meta_image=DestroyImage(meta_image);
1019 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1021 (void) ResetMagickMemory(global_colormap,0,3*MagickMax(global_colors,256)*
1022 sizeof(*global_colormap));
1023 if (BitSet((int) flag,0x80) != 0)
1025 count=ReadBlob(image,(size_t) (3*global_colors),global_colormap);
1026 if (count != (ssize_t) (3*global_colors))
1028 global_colormap=(unsigned char *) RelinquishMagickMemory(
1030 meta_image=DestroyImage(meta_image);
1031 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
1039 count=ReadBlob(image,1,&c);
1042 if (c == (unsigned char) ';')
1043 break; /* terminator */
1044 if (c == (unsigned char) '!')
1047 GIF Extension block.
1049 count=ReadBlob(image,1,&c);
1052 global_colormap=(unsigned char *) RelinquishMagickMemory(
1054 meta_image=DestroyImage(meta_image);
1055 ThrowReaderException(CorruptImageError,
1056 "UnableToReadExtensionBlock");
1063 Read graphics control extension.
1065 while (ReadBlobBlock(image,buffer) != 0) ;
1066 meta_image->dispose=(DisposeType) ((buffer[0] >> 2) & 0x07);
1067 meta_image->delay=((size_t) buffer[2] << 8) | buffer[1];
1068 if ((ssize_t) (buffer[0] & 0x01) == 0x01)
1069 opacity=(ssize_t) buffer[3];
1081 Read comment extension.
1083 comments=AcquireString((char *) NULL);
1084 for (length=0; ; length+=count)
1086 count=(ssize_t) ReadBlobBlock(image,buffer);
1090 (void) ConcatenateString(&comments,(const char *) buffer);
1092 (void) SetImageProperty(meta_image,"comment",comments,exception);
1093 comments=DestroyString(comments);
1102 Read Netscape Loop extension.
1105 if (ReadBlobBlock(image,buffer) != 0)
1106 loop=LocaleNCompare((char *) buffer,"NETSCAPE2.0",11) == 0 ?
1107 MagickTrue : MagickFalse;
1108 if (loop != MagickFalse)
1109 while (ReadBlobBlock(image,buffer) != 0)
1111 meta_image->iterations=((size_t) buffer[2] << 8) | buffer[1];
1112 if (meta_image->iterations != 0)
1113 meta_image->iterations++;
1118 name[MagickPathExtent];
1138 Store GIF application extension as a generic profile.
1140 icc=LocaleNCompare((char *) buffer,"ICCRGBG1012",11) == 0 ?
1141 MagickTrue : MagickFalse;
1142 magick=LocaleNCompare((char *) buffer,"ImageMagick",11) == 0 ?
1143 MagickTrue : MagickFalse;
1144 i8bim=LocaleNCompare((char *) buffer,"MGK8BIM0000",11) == 0 ?
1145 MagickTrue : MagickFalse;
1146 iptc=LocaleNCompare((char *) buffer,"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)
1155 meta_image=DestroyImage(meta_image);
1156 global_colormap=(unsigned char *) RelinquishMagickMemory(
1158 ThrowReaderException(ResourceLimitError,
1159 "MemoryAllocationFailed");
1161 (void) ResetMagickMemory(info,0,255UL*sizeof(*info));
1162 reserved_length=255;
1163 for (info_length=0; ; )
1165 block_length=(int) ReadBlobBlock(image,&info[info_length]);
1166 if (block_length == 0)
1168 info_length+=block_length;
1169 if (info_length > (reserved_length-255))
1171 reserved_length+=4096;
1172 info=(unsigned char *) ResizeQuantumMemory(info,(size_t)
1173 reserved_length,sizeof(*info));
1174 if (info == (unsigned char *) NULL)
1176 meta_image=DestroyImage(meta_image);
1177 global_colormap=(unsigned char *)
1178 RelinquishMagickMemory(global_colormap);
1179 ThrowReaderException(ResourceLimitError,
1180 "MemoryAllocationFailed");
1184 profile=BlobToStringInfo(info,(size_t) info_length);
1185 if (profile == (StringInfo *) NULL)
1187 meta_image=DestroyImage(meta_image);
1188 global_colormap=(unsigned char *) RelinquishMagickMemory(
1190 info=(unsigned char *) RelinquishMagickMemory(info);
1191 ThrowReaderException(ResourceLimitError,
1192 "MemoryAllocationFailed");
1194 if (i8bim != MagickFalse)
1195 (void) CopyMagickString(name,"8bim",sizeof(name));
1196 else if (icc != MagickFalse)
1197 (void) CopyMagickString(name,"icc",sizeof(name));
1198 else if (iptc != MagickFalse)
1199 (void) CopyMagickString(name,"iptc",sizeof(name));
1200 else if (magick != MagickFalse)
1202 (void) CopyMagickString(name,"magick",sizeof(name));
1203 meta_image->gamma=StringToDouble((char *) info+6,
1207 (void) FormatLocaleString(name,sizeof(name),"gif:%.11s",
1209 info=(unsigned char *) RelinquishMagickMemory(info);
1210 if (magick == MagickFalse)
1211 (void) SetImageProfile(meta_image,name,profile,exception);
1212 profile=DestroyStringInfo(profile);
1213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1214 " profile name=%s",name);
1220 while (ReadBlobBlock(image,buffer) != 0) ;
1225 if (c != (unsigned char) ',')
1227 if (image_count != 0)
1230 Allocate next image structure.
1232 AcquireNextImage(image_info,image,exception);
1233 if (GetNextImageInList(image) == (Image *) NULL)
1235 image=DestroyImageList(image);
1236 global_colormap=(unsigned char *) RelinquishMagickMemory(
1238 return((Image *) NULL);
1240 image=SyncNextImageInList(image);
1244 Read image attributes.
1246 meta_image->page.x=(ssize_t) ReadBlobLSBShort(image);
1247 meta_image->page.y=(ssize_t) ReadBlobLSBShort(image);
1248 meta_image->scene=image->scene;
1249 (void) CloneImageProperties(image,meta_image);
1250 DestroyImageProperties(meta_image);
1251 (void) CloneImageProfiles(image,meta_image);
1252 DestroyImageProfiles(meta_image);
1253 image->storage_class=PseudoClass;
1254 image->compression=LZWCompression;
1255 image->columns=ReadBlobLSBShort(image);
1256 image->rows=ReadBlobLSBShort(image);
1258 flag=(unsigned char) ReadBlobByte(image);
1259 image->interlace=BitSet((int) flag,0x40) != 0 ? GIFInterlace : NoInterlace;
1260 local_colors=BitSet((int) flag,0x80) == 0 ? global_colors : one <<
1261 ((size_t) (flag & 0x07)+1);
1262 image->colors=local_colors;
1263 if (opacity >= (ssize_t) image->colors)
1264 image->colors=(size_t) (opacity+1);
1265 image->ticks_per_second=100;
1266 image->alpha_trait=opacity >= 0 ? BlendPixelTrait : UndefinedPixelTrait;
1267 if ((image->columns == 0) || (image->rows == 0))
1269 global_colormap=(unsigned char *) RelinquishMagickMemory(
1271 meta_image=DestroyImage(meta_image);
1272 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
1275 Inititialize colormap.
1277 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
1279 global_colormap=(unsigned char *) RelinquishMagickMemory(
1281 meta_image=DestroyImage(meta_image);
1282 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1284 if (BitSet((int) flag,0x80) == 0)
1287 Use global colormap.
1290 for (i=0; i < (ssize_t) image->colors; i++)
1292 image->colormap[i].red=(double) ScaleCharToQuantum(*p++);
1293 image->colormap[i].green=(double) ScaleCharToQuantum(*p++);
1294 image->colormap[i].blue=(double) ScaleCharToQuantum(*p++);
1297 image->colormap[i].alpha=(double) TransparentAlpha;
1298 image->transparent_color=image->colormap[opacity];
1301 image->background_color=image->colormap[MagickMin((ssize_t) background,
1302 (ssize_t) image->colors-1)];
1310 Read local colormap.
1312 colormap=(unsigned char *) AcquireQuantumMemory((size_t)
1313 MagickMax(local_colors,256),3UL*sizeof(*colormap));
1314 if (colormap == (unsigned char *) NULL)
1316 global_colormap=(unsigned char *) RelinquishMagickMemory(
1318 meta_image=DestroyImage(meta_image);
1319 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1321 (void) ResetMagickMemory(colormap,0,3*MagickMax(local_colors,256)*
1323 count=ReadBlob(image,(3*local_colors)*sizeof(*colormap),colormap);
1324 if (count != (ssize_t) (3*local_colors))
1326 global_colormap=(unsigned char *) RelinquishMagickMemory(
1328 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1329 meta_image=DestroyImage(meta_image);
1330 ThrowReaderException(CorruptImageError,
1331 "InsufficientImageDataInFile");
1334 for (i=0; i < (ssize_t) image->colors; i++)
1336 image->colormap[i].red=(double) ScaleCharToQuantum(*p++);
1337 image->colormap[i].green=(double) ScaleCharToQuantum(*p++);
1338 image->colormap[i].blue=(double) ScaleCharToQuantum(*p++);
1340 image->colormap[i].alpha=(double) TransparentAlpha;
1342 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1344 if (image->gamma == 1.0)
1346 for (i=0; i < (ssize_t) image->colors; i++)
1347 if (IsPixelInfoGray(image->colormap+i) == MagickFalse)
1349 (void) SetImageColorspace(image,i == (ssize_t) image->colors ?
1350 GRAYColorspace : RGBColorspace,exception);
1352 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
1353 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
1355 status=SetImageExtent(image,image->columns,image->rows,exception);
1356 if (status == MagickFalse)
1358 global_colormap=(unsigned char *) RelinquishMagickMemory(
1360 meta_image=DestroyImage(meta_image);
1361 return(DestroyImageList(image));
1366 if (image_info->ping != MagickFalse)
1367 status=PingGIFImage(image,exception);
1369 status=DecodeImage(image,opacity,exception);
1370 if ((image_info->ping == MagickFalse) && (status == MagickFalse))
1372 global_colormap=(unsigned char *) RelinquishMagickMemory(
1374 meta_image=DestroyImage(meta_image);
1375 ThrowReaderException(CorruptImageError,"CorruptImage");
1377 duration+=image->delay*image->iterations;
1378 if (image_info->number_scenes != 0)
1379 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
1382 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) image->scene-
1384 if (status == MagickFalse)
1387 image->duration=duration;
1388 meta_image=DestroyImage(meta_image);
1389 global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap);
1390 if ((image->columns == 0) || (image->rows == 0))
1391 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
1392 (void) CloseBlob(image);
1393 return(GetFirstImageInList(image));
1397 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1401 % R e g i s t e r G I F I m a g e %
1405 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1407 % RegisterGIFImage() adds properties for the GIF image format to
1408 % the list of supported formats. The properties include the image format
1409 % tag, a method to read and/or write the format, whether the format
1410 % supports the saving of more than one frame to the same file or blob,
1411 % whether the format supports native in-memory I/O, and a brief
1412 % description of the format.
1414 % The format of the RegisterGIFImage method is:
1416 % size_t RegisterGIFImage(void)
1419 ModuleExport size_t RegisterGIFImage(void)
1424 entry=AcquireMagickInfo("GIF","GIF",
1425 "CompuServe graphics interchange format");
1426 entry->decoder=(DecodeImageHandler *) ReadGIFImage;
1427 entry->encoder=(EncodeImageHandler *) WriteGIFImage;
1428 entry->magick=(IsImageFormatHandler *) IsGIF;
1429 entry->mime_type=ConstantString("image/gif");
1430 (void) RegisterMagickInfo(entry);
1431 entry=AcquireMagickInfo("GIF","GIF87",
1432 "CompuServe graphics interchange format");
1433 entry->decoder=(DecodeImageHandler *) ReadGIFImage;
1434 entry->encoder=(EncodeImageHandler *) WriteGIFImage;
1435 entry->magick=(IsImageFormatHandler *) IsGIF;
1436 entry->flags^=CoderAdjoinFlag;
1437 entry->version=ConstantString("version 87a");
1438 entry->mime_type=ConstantString("image/gif");
1439 (void) RegisterMagickInfo(entry);
1440 return(MagickImageCoderSignature);
1444 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1448 % U n r e g i s t e r G I F I m a g e %
1452 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1454 % UnregisterGIFImage() removes format registrations made by the
1455 % GIF module from the list of supported formats.
1457 % The format of the UnregisterGIFImage method is:
1459 % UnregisterGIFImage(void)
1462 ModuleExport void UnregisterGIFImage(void)
1464 (void) UnregisterMagickInfo("GIF");
1465 (void) UnregisterMagickInfo("GIF87");
1469 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1473 % W r i t e G I F I m a g e %
1477 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1479 % WriteGIFImage() writes an image to a file in the Compuserve Graphics
1482 % The format of the WriteGIFImage method is:
1484 % MagickBooleanType WriteGIFImage(const ImageInfo *image_info,
1485 % Image *image,ExceptionInfo *exception)
1487 % A description of each parameter follows.
1489 % o image_info: the image info.
1491 % o image: The image.
1493 % o exception: return any errors or warnings in this structure.
1496 static MagickBooleanType WriteGIFImage(const ImageInfo *image_info,Image *image,
1497 ExceptionInfo *exception)
1517 register unsigned char
1535 Open output image file.
1537 assert(image_info != (const ImageInfo *) NULL);
1538 assert(image_info->signature == MagickCoreSignature);
1539 assert(image != (Image *) NULL);
1540 assert(image->signature == MagickCoreSignature);
1541 if (image->debug != MagickFalse)
1542 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1543 assert(exception != (ExceptionInfo *) NULL);
1544 assert(exception->signature == MagickCoreSignature);
1545 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
1546 if (status == MagickFalse)
1551 global_colormap=(unsigned char *) AcquireQuantumMemory(768UL,
1552 sizeof(*global_colormap));
1553 colormap=(unsigned char *) AcquireQuantumMemory(768UL,sizeof(*colormap));
1554 if ((global_colormap == (unsigned char *) NULL) ||
1555 (colormap == (unsigned char *) NULL))
1557 if (global_colormap != (unsigned char *) NULL)
1558 global_colormap=(unsigned char *) RelinquishMagickMemory(
1560 if (colormap != (unsigned char *) NULL)
1561 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1562 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1564 for (i=0; i < 768; i++)
1565 colormap[i]=(unsigned char) 0;
1569 write_info=CloneImageInfo(image_info);
1570 if (LocaleCompare(write_info->magick,"GIF87") != 0)
1571 (void) WriteBlob(image,6,(unsigned char *) "GIF89a");
1574 (void) WriteBlob(image,6,(unsigned char *) "GIF87a");
1575 write_info->adjoin=MagickFalse;
1578 Determine image bounding box.
1580 page.width=image->columns;
1581 if (image->page.width > page.width)
1582 page.width=image->page.width;
1583 page.height=image->rows;
1584 if (image->page.height > page.height)
1585 page.height=image->page.height;
1586 page.x=image->page.x;
1587 page.y=image->page.y;
1588 (void) WriteBlobLSBShort(image,(unsigned short) page.width);
1589 (void) WriteBlobLSBShort(image,(unsigned short) page.height);
1591 Write images to file.
1593 if ((write_info->adjoin != MagickFalse) &&
1594 (GetNextImageInList(image) != (Image *) NULL))
1595 write_info->interlace=NoInterlace;
1600 (void) TransformImageColorspace(image,sRGBColorspace,exception);
1602 if (IsImageOpaque(image,exception) != MagickFalse)
1604 if ((image->storage_class == DirectClass) || (image->colors > 256))
1605 (void) SetImageType(image,PaletteType,exception);
1614 Identify transparent colormap index.
1616 if ((image->storage_class == DirectClass) || (image->colors > 256))
1617 (void) SetImageType(image,PaletteBilevelAlphaType,exception);
1618 for (i=0; i < (ssize_t) image->colors; i++)
1619 if (image->colormap[i].alpha != OpaqueAlpha)
1626 alpha=fabs(image->colormap[i].alpha-TransparentAlpha);
1627 beta=fabs(image->colormap[opacity].alpha-TransparentAlpha);
1633 (void) SetImageType(image,PaletteBilevelAlphaType,exception);
1634 for (i=0; i < (ssize_t) image->colors; i++)
1635 if (image->colormap[i].alpha != OpaqueAlpha)
1642 alpha=fabs(image->colormap[i].alpha-TransparentAlpha);
1643 beta=fabs(image->colormap[opacity].alpha-TransparentAlpha);
1650 image->colormap[opacity].red=image->transparent_color.red;
1651 image->colormap[opacity].green=image->transparent_color.green;
1652 image->colormap[opacity].blue=image->transparent_color.blue;
1655 if ((image->storage_class == DirectClass) || (image->colors > 256))
1656 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1657 for (bits_per_pixel=1; bits_per_pixel < 8; bits_per_pixel++)
1658 if ((one << bits_per_pixel) >= image->colors)
1661 for (i=0; i < (ssize_t) image->colors; i++)
1663 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].red));
1664 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].green));
1665 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].blue));
1667 for ( ; i < (ssize_t) (one << bits_per_pixel); i++)
1669 *q++=(unsigned char) 0x0;
1670 *q++=(unsigned char) 0x0;
1671 *q++=(unsigned char) 0x0;
1673 if ((GetPreviousImageInList(image) == (Image *) NULL) ||
1674 (write_info->adjoin == MagickFalse))
1677 Write global colormap.
1680 c|=(8-1) << 4; /* color resolution */
1681 c|=(bits_per_pixel-1); /* size of global colormap */
1682 (void) WriteBlobByte(image,(unsigned char) c);
1683 for (j=0; j < (ssize_t) image->colors; j++)
1684 if (IsPixelInfoEquivalent(&image->background_color,image->colormap+j))
1686 (void) WriteBlobByte(image,(unsigned char)
1687 (j == (ssize_t) image->colors ? 0 : j)); /* background color */
1688 (void) WriteBlobByte(image,(unsigned char) 0x00); /* reserved */
1689 length=(size_t) (3*(one << bits_per_pixel));
1690 (void) WriteBlob(image,length,colormap);
1691 for (j=0; j < 768; j++)
1692 global_colormap[j]=colormap[j];
1694 if (LocaleCompare(write_info->magick,"GIF87") != 0)
1700 Write graphics control extension.
1702 (void) WriteBlobByte(image,(unsigned char) 0x21);
1703 (void) WriteBlobByte(image,(unsigned char) 0xf9);
1704 (void) WriteBlobByte(image,(unsigned char) 0x04);
1705 c=image->dispose << 2;
1708 (void) WriteBlobByte(image,(unsigned char) c);
1709 delay=(size_t) (100*image->delay/MagickMax((size_t)
1710 image->ticks_per_second,1));
1711 (void) WriteBlobLSBShort(image,(unsigned short) delay);
1712 (void) WriteBlobByte(image,(unsigned char) (opacity >= 0 ? opacity :
1714 (void) WriteBlobByte(image,(unsigned char) 0x00);
1715 value=GetImageProperty(image,"comment",exception);
1716 if ((LocaleCompare(write_info->magick,"GIF87") != 0) &&
1717 (value != (const char *) NULL))
1726 Write comment extension.
1728 (void) WriteBlobByte(image,(unsigned char) 0x21);
1729 (void) WriteBlobByte(image,(unsigned char) 0xfe);
1730 for (p=value; *p != '\0'; )
1732 count=MagickMin(strlen(p),255);
1733 (void) WriteBlobByte(image,(unsigned char) count);
1734 for (i=0; i < (ssize_t) count; i++)
1735 (void) WriteBlobByte(image,(unsigned char) *p++);
1737 (void) WriteBlobByte(image,(unsigned char) 0x00);
1739 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
1740 (GetNextImageInList(image) != (Image *) NULL) &&
1741 (image->iterations != 1))
1744 Write Netscape Loop extension.
1746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1747 " Writing GIF Extension %s","NETSCAPE2.0");
1748 (void) WriteBlobByte(image,(unsigned char) 0x21);
1749 (void) WriteBlobByte(image,(unsigned char) 0xff);
1750 (void) WriteBlobByte(image,(unsigned char) 0x0b);
1751 (void) WriteBlob(image,11,(unsigned char *) "NETSCAPE2.0");
1752 (void) WriteBlobByte(image,(unsigned char) 0x03);
1753 (void) WriteBlobByte(image,(unsigned char) 0x01);
1754 (void) WriteBlobLSBShort(image,(unsigned short) (image->iterations ?
1755 image->iterations-1 : 0));
1756 (void) WriteBlobByte(image,(unsigned char) 0x00);
1758 if ((image->gamma != 1.0f/2.2f))
1761 attributes[MagickPathExtent];
1767 Write ImageMagick extension.
1769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1770 " Writing GIF Extension %s","ImageMagick");
1771 (void) WriteBlobByte(image,(unsigned char) 0x21);
1772 (void) WriteBlobByte(image,(unsigned char) 0xff);
1773 (void) WriteBlobByte(image,(unsigned char) 0x0b);
1774 (void) WriteBlob(image,11,(unsigned char *) "ImageMagick");
1775 count=FormatLocaleString(attributes,MagickPathExtent,"gamma=%g",
1777 (void) WriteBlobByte(image,(unsigned char) count);
1778 (void) WriteBlob(image,(size_t) count,(unsigned char *) attributes);
1779 (void) WriteBlobByte(image,(unsigned char) 0x00);
1781 ResetImageProfileIterator(image);
1790 name=GetNextImageProfile(image);
1791 if (name == (const char *) NULL)
1793 profile=GetImageProfile(image,name);
1794 if (profile != (StringInfo *) NULL)
1796 if ((LocaleCompare(name,"ICC") == 0) ||
1797 (LocaleCompare(name,"ICM") == 0) ||
1798 (LocaleCompare(name,"IPTC") == 0) ||
1799 (LocaleCompare(name,"8BIM") == 0) ||
1800 (LocaleNCompare(name,"gif:",4) == 0))
1808 datum=GetStringInfoDatum(profile);
1809 length=GetStringInfoLength(profile);
1810 (void) WriteBlobByte(image,(unsigned char) 0x21);
1811 (void) WriteBlobByte(image,(unsigned char) 0xff);
1812 (void) WriteBlobByte(image,(unsigned char) 0x0b);
1813 if ((LocaleCompare(name,"ICC") == 0) ||
1814 (LocaleCompare(name,"ICM") == 0))
1817 Write ICC extension.
1819 (void) WriteBlob(image,11,(unsigned char *) "ICCRGBG1012");
1820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1821 " Writing GIF Extension %s","ICCRGBG1012");
1824 if ((LocaleCompare(name,"IPTC") == 0))
1827 Write IPTC extension.
1829 (void) WriteBlob(image,11,(unsigned char *) "MGKIPTC0000");
1830 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1831 " Writing GIF Extension %s","MGKIPTC0000");
1834 if ((LocaleCompare(name,"8BIM") == 0))
1837 Write 8BIM extension.
1839 (void) WriteBlob(image,11,(unsigned char *)
1841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1842 " Writing GIF Extension %s","MGK8BIM0000");
1847 extension[MagickPathExtent];
1850 Write generic extension.
1852 (void) CopyMagickString(extension,name+4,
1854 (void) WriteBlob(image,11,(unsigned char *) extension);
1855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1856 " Writing GIF Extension %s",name);
1859 while ((ssize_t) length > offset)
1864 if ((length-offset) < 255)
1865 block_length=length-offset;
1868 (void) WriteBlobByte(image,(unsigned char) block_length);
1869 (void) WriteBlob(image,(size_t) block_length,datum+offset);
1870 offset+=(ssize_t) block_length;
1872 (void) WriteBlobByte(image,(unsigned char) 0x00);
1877 (void) WriteBlobByte(image,','); /* image separator */
1879 Write the image header.
1881 page.x=image->page.x;
1882 page.y=image->page.y;
1883 if ((image->page.width != 0) && (image->page.height != 0))
1885 (void) WriteBlobLSBShort(image,(unsigned short) (page.x < 0 ? 0 : page.x));
1886 (void) WriteBlobLSBShort(image,(unsigned short) (page.y < 0 ? 0 : page.y));
1887 (void) WriteBlobLSBShort(image,(unsigned short) image->columns);
1888 (void) WriteBlobLSBShort(image,(unsigned short) image->rows);
1890 if (write_info->interlace != NoInterlace)
1891 c|=0x40; /* pixel data is interlaced */
1892 for (j=0; j < (ssize_t) (3*image->colors); j++)
1893 if (colormap[j] != global_colormap[j])
1895 if (j == (ssize_t) (3*image->colors))
1896 (void) WriteBlobByte(image,(unsigned char) c);
1900 c|=(bits_per_pixel-1); /* size of local colormap */
1901 (void) WriteBlobByte(image,(unsigned char) c);
1902 length=(size_t) (3*(one << bits_per_pixel));
1903 (void) WriteBlob(image,length,colormap);
1906 Write the image data.
1908 c=(int) MagickMax(bits_per_pixel,2);
1909 (void) WriteBlobByte(image,(unsigned char) c);
1910 status=EncodeImage(write_info,image,(size_t) MagickMax(bits_per_pixel,2)+1,
1912 if (status == MagickFalse)
1914 global_colormap=(unsigned char *) RelinquishMagickMemory(
1916 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1917 write_info=DestroyImageInfo(write_info);
1918 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1920 (void) WriteBlobByte(image,(unsigned char) 0x00);
1921 if (GetNextImageInList(image) == (Image *) NULL)
1923 image=SyncNextImageInList(image);
1925 status=SetImageProgress(image,SaveImagesTag,scene,
1926 GetImageListLength(image));
1927 if (status == MagickFalse)
1929 } while (write_info->adjoin != MagickFalse);
1930 (void) WriteBlobByte(image,';'); /* terminator */
1931 global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap);
1932 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1933 write_info=DestroyImageInfo(write_info);
1934 (void) CloseBlob(image);