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/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,
340 lzw_info->last_code=lzw_info->first_code;
341 } while (lzw_info->first_code == lzw_info->clear_code);
342 return((int) lzw_info->first_code);
344 code=GetNextLZWCode(lzw_info,lzw_info->bits);
347 if ((size_t) code == lzw_info->clear_code)
349 ResetLZWInfo(lzw_info);
350 return(ReadBlobLZWByte(lzw_info));
352 if ((size_t) code == lzw_info->end_code)
354 if ((size_t) code < lzw_info->slot)
358 PushLZWStack(lzw_info->stack,lzw_info->first_code);
359 value=lzw_info->last_code;
362 while (value > lzw_info->maximum_data_value)
364 if ((size_t) count > MaximumLZWCode)
367 if ((size_t) value > MaximumLZWCode)
369 PushLZWStack(lzw_info->stack,lzw_info->table[1][value]);
370 value=lzw_info->table[0][value];
372 lzw_info->first_code=lzw_info->table[1][value];
373 PushLZWStack(lzw_info->stack,lzw_info->first_code);
375 if (lzw_info->slot < MaximumLZWCode)
377 lzw_info->table[0][lzw_info->slot]=lzw_info->last_code;
378 lzw_info->table[1][lzw_info->slot]=lzw_info->first_code;
380 if ((lzw_info->slot >= lzw_info->maximum_code) &&
381 (lzw_info->bits < MaximumLZWBits))
384 lzw_info->maximum_code=one << lzw_info->bits;
387 lzw_info->last_code=(size_t) code;
388 return(PopLZWStack(lzw_info->stack));
391 static MagickBooleanType DecodeImage(Image *image,const ssize_t opacity,
392 ExceptionInfo *exception)
414 Allocate decoder tables.
416 assert(image != (Image *) NULL);
417 assert(image->signature == MagickSignature);
418 if (image->debug != MagickFalse)
419 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
420 data_size=(unsigned char) ReadBlobByte(image);
421 if (data_size > MaximumLZWBits)
422 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
423 lzw_info=AcquireLZWInfo(image,data_size);
424 if (lzw_info == (LZWInfo *) NULL)
425 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
429 for (y=0; y < (ssize_t) image->rows; y++)
437 q=GetAuthenticPixels(image,0,offset,image->columns,1,exception);
438 if (q == (Quantum *) NULL)
440 for (x=0; x < (ssize_t) image->columns; )
442 c=ReadBlobLZWByte(lzw_info);
445 index=ConstrainColormapIndex(image,(size_t) c,exception);
446 SetPixelIndex(image,index,q);
447 SetPixelInfoPixel(image,image->colormap+(ssize_t) index,q);
448 SetPixelAlpha(image,(ssize_t) index == opacity ? TransparentAlpha :
451 q+=GetPixelChannels(image);
453 if (x < (ssize_t) image->columns)
455 if (image->interlace == NoInterlace)
464 if (offset >= (ssize_t) image->rows)
474 if (offset >= (ssize_t) image->rows)
484 if (offset >= (ssize_t) image->rows)
497 if (SyncAuthenticPixels(image,exception) == MagickFalse)
500 lzw_info=RelinquishLZWInfo(lzw_info);
501 if (y < (ssize_t) image->rows)
502 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
507 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
511 % E n c o d e I m a g e %
515 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
517 % EncodeImage compresses an image via GIF-coding.
519 % The format of the EncodeImage method is:
521 % MagickBooleanType EncodeImage(const ImageInfo *image_info,Image *image,
522 % const size_t data_size)
524 % A description of each parameter follows:
526 % o image_info: the image info.
528 % o image: the address of a structure of type Image.
530 % o data_size: The number of bits in the compressed packet.
533 static MagickBooleanType EncodeImage(const ImageInfo *image_info,Image *image,
534 const size_t data_size,ExceptionInfo *exception)
536 #define MaxCode(number_bits) ((one << (number_bits))-1)
537 #define MaxHashTable 5003
538 #define MaxGIFBits 12UL
539 #define MaxGIFTable (1UL << MaxGIFBits)
540 #define GIFOutputCode(code) \
546 datum|=(code) << bits; \
553 Add a character to current packet. \
555 packet[length++]=(unsigned char) (datum & 0xff); \
558 (void) WriteBlobByte(image,(unsigned char) length); \
559 (void) WriteBlob(image,length,packet); \
565 if (free_code > max_code) \
568 if (number_bits == MaxGIFBits) \
569 max_code=MaxGIFTable; \
571 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 number_bits=data_size;
637 max_code=MaxCode(number_bits);
638 clear_code=((short) one << (data_size-1));
639 end_of_information_code=clear_code+1;
640 free_code=clear_code+2;
644 for (i=0; i < MaxHashTable; i++)
646 GIFOutputCode(clear_code);
653 for (y=0; y < (ssize_t) image->rows; y++)
655 register const Quantum
661 p=GetVirtualPixels(image,0,offset,image->columns,1,exception);
662 if (p == (const Quantum *) NULL)
666 waiting_code=(short) GetPixelIndex(image,p);
667 p+=GetPixelChannels(image);
669 for (x=(ssize_t) (y == 0 ? 1 : 0); x < (ssize_t) image->columns; x++)
674 index=(Quantum) ((size_t) GetPixelIndex(image,p) & 0xff);
675 p+=GetPixelChannels(image);
676 k=(ssize_t) (((size_t) index << (MaxGIFBits-8))+waiting_code);
677 if (k >= MaxHashTable)
679 next_pixel=MagickFalse;
681 if (hash_code[k] > 0)
683 if ((hash_prefix[k] == waiting_code) &&
684 (hash_suffix[k] == (unsigned char) index))
686 waiting_code=hash_code[k];
690 displacement=MaxHashTable-k;
696 if (hash_code[k] == 0)
698 if ((hash_prefix[k] == waiting_code) &&
699 (hash_suffix[k] == (unsigned char) index))
701 waiting_code=hash_code[k];
702 next_pixel=MagickTrue;
706 if (next_pixel == MagickTrue)
709 GIFOutputCode((size_t) waiting_code);
710 if (free_code < MaxGIFTable)
712 hash_code[k]=(short) free_code++;
713 hash_prefix[k]=waiting_code;
714 hash_suffix[k]=(unsigned char) index;
719 Fill the hash table with empty entries.
721 for (k=0; k < MaxHashTable; k++)
724 Reset compressor and issue a clear code.
726 free_code=clear_code+2;
727 GIFOutputCode(clear_code);
728 number_bits=data_size;
729 max_code=MaxCode(number_bits);
731 waiting_code=(short) index;
733 if (image_info->interlace == NoInterlace)
742 if (offset >= (ssize_t) image->rows)
752 if (offset >= (ssize_t) image->rows)
762 if (offset >= (ssize_t) image->rows)
777 Flush out the buffered code.
779 GIFOutputCode((size_t) waiting_code);
780 GIFOutputCode(end_of_information_code);
784 Add a character to current packet.
786 packet[length++]=(unsigned char) (datum & 0xff);
789 (void) WriteBlobByte(image,(unsigned char) length);
790 (void) WriteBlob(image,length,packet);
795 Flush accumulated data.
799 (void) WriteBlobByte(image,(unsigned char) length);
800 (void) WriteBlob(image,length,packet);
805 hash_suffix=(unsigned char *) RelinquishMagickMemory(hash_suffix);
806 hash_prefix=(short *) RelinquishMagickMemory(hash_prefix);
807 hash_code=(short *) RelinquishMagickMemory(hash_code);
808 packet=(unsigned char *) RelinquishMagickMemory(packet);
813 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
821 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
823 % IsGIF() returns MagickTrue if the image format type, identified by the
824 % magick string, is GIF.
826 % The format of the IsGIF method is:
828 % MagickBooleanType IsGIF(const unsigned char *magick,const size_t length)
830 % A description of each parameter follows:
832 % o magick: compare image format pattern against these bytes.
834 % o length: Specifies the length of the magick string.
837 static MagickBooleanType IsGIF(const unsigned char *magick,const size_t length)
841 if (LocaleNCompare((char *) magick,"GIF8",4) == 0)
847 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
851 + R e a d B l o b B l o c k %
855 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
857 % ReadBlobBlock() reads data from the image file and returns it. The
858 % amount of data is determined by first reading a count byte. The number
859 % of bytes read is returned.
861 % The format of the ReadBlobBlock method is:
863 % size_t ReadBlobBlock(Image *image,unsigned char *data)
865 % A description of each parameter follows:
867 % o image: the image.
869 % o data: Specifies an area to place the information requested from
873 static ssize_t ReadBlobBlock(Image *image,unsigned char *data)
881 assert(image != (Image *) NULL);
882 assert(image->signature == MagickSignature);
883 assert(data != (unsigned char *) NULL);
884 count=ReadBlob(image,1,&block_count);
887 return(ReadBlob(image,(size_t) block_count,data));
891 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
895 % R e a d G I F I m a g e %
899 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
901 % ReadGIFImage() reads a Compuserve Graphics image file and returns it.
902 % It allocates the memory necessary for the new Image structure and returns a
903 % pointer to the new image.
905 % The format of the ReadGIFImage method is:
907 % Image *ReadGIFImage(const ImageInfo *image_info,ExceptionInfo *exception)
909 % A description of each parameter follows:
911 % o image_info: the image info.
913 % o exception: return any errors or warnings in this structure.
917 static inline size_t MagickMax(const size_t x,const size_t y)
924 static inline size_t MagickMin(const size_t x,const size_t y)
931 static MagickBooleanType PingGIFImage(Image *image,ExceptionInfo *exception)
938 assert(image != (Image *) NULL);
939 assert(image->signature == MagickSignature);
940 if (image->debug != MagickFalse)
941 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
942 if (ReadBlob(image,1,&data_size) != 1)
943 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
944 if (data_size > MaximumLZWBits)
945 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
946 if (ReadBlob(image,1,&length) != 1)
947 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
950 if (ReadBlob(image,length,buffer) != (ssize_t) length)
951 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
952 if (ReadBlob(image,1,&length) != 1)
953 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
958 static Image *ReadGIFImage(const ImageInfo *image_info,ExceptionInfo *exception)
960 #define BitSet(byte,bit) (((byte) & (bit)) == (bit))
961 #define LSBFirstOrder(x,y) (((y) << 8) | (x))
967 number_extensionss=0;
978 register unsigned char
998 header[MaxTextExtent],
1004 assert(image_info != (const ImageInfo *) NULL);
1005 assert(image_info->signature == MagickSignature);
1006 if (image_info->debug != MagickFalse)
1007 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1008 image_info->filename);
1009 assert(exception != (ExceptionInfo *) NULL);
1010 assert(exception->signature == MagickSignature);
1011 image=AcquireImage(image_info,exception);
1012 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1013 if (status == MagickFalse)
1015 image=DestroyImageList(image);
1016 return((Image *) NULL);
1019 Determine if this a GIF file.
1021 count=ReadBlob(image,6,magick);
1022 if ((count != 6) || ((LocaleNCompare((char *) magick,"GIF87",5) != 0) &&
1023 (LocaleNCompare((char *) magick,"GIF89",5) != 0)))
1024 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1025 page.width=ReadBlobLSBShort(image);
1026 page.height=ReadBlobLSBShort(image);
1027 flag=(unsigned char) ReadBlobByte(image);
1028 background=(unsigned char) ReadBlobByte(image);
1029 c=(unsigned char) ReadBlobByte(image); /* reserved */
1031 global_colors=one << (((size_t) flag & 0x07)+1);
1032 global_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
1033 MagickMax(global_colors,256),3UL*sizeof(*global_colormap));
1034 if (global_colormap == (unsigned char *) NULL)
1035 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1036 if (BitSet((int) flag,0x80) != 0)
1037 count=ReadBlob(image,(size_t) (3*global_colors),global_colormap);
1045 count=ReadBlob(image,1,&c);
1048 if (c == (unsigned char) ';')
1049 break; /* terminator */
1050 if (c == (unsigned char) '!')
1053 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];
1086 Read comment extension.
1088 comments=AcquireString((char *) NULL);
1089 for (length=0; ; length+=count)
1091 count=(ssize_t) ReadBlobBlock(image,header);
1095 comments=(char *) ResizeQuantumMemory(comments,length+count,
1097 if (comments == (char *) NULL)
1098 ThrowReaderException(ResourceLimitError,
1099 "MemoryAllocationFailed");
1100 (void) CopyMagickMemory(comments+length,header,(size_t) count);
1102 comments[length+count]='\0';
1103 (void) SetImageProperty(image,"comment",comments,exception);
1104 comments=DestroyString(comments);
1113 Read Netscape Loop extension.
1116 if (ReadBlobBlock(image,header) != 0)
1117 loop=LocaleNCompare((char *) header,"NETSCAPE2.0",11) == 0 ?
1118 MagickTrue : MagickFalse;
1119 if (loop != MagickFalse)
1121 while (ReadBlobBlock(image,header) != 0)
1122 iterations=(size_t) ((header[2] << 8) | header[1]);
1128 name[MaxTextExtent];
1148 Store GIF application extension as a generic profile.
1150 icc=LocaleNCompare((char *) header,"ICCRGBG1012",11) == 0 ?
1151 MagickTrue : MagickFalse;
1152 magick=LocaleNCompare((char *) header,"ImageMagick",11) == 0 ?
1153 MagickTrue : MagickFalse;
1154 i8bim=LocaleNCompare((char *) header,"MGK8BIM0000",11) == 0 ?
1155 MagickTrue : MagickFalse;
1156 iptc=LocaleNCompare((char *) header,"MGKIPTC0000",11) == 0 ?
1157 MagickTrue : MagickFalse;
1158 number_extensionss++;
1159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1160 " Reading GIF application extension");
1161 info=(unsigned char *) AcquireQuantumMemory(255UL,
1163 if (info == (unsigned char *) NULL)
1164 ThrowReaderException(ResourceLimitError,
1165 "MemoryAllocationFailed");
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,
1177 (size_t) reserved_length,sizeof(*info));
1178 if (info == (unsigned char *) NULL)
1179 ThrowReaderException(ResourceLimitError,
1180 "MemoryAllocationFailed");
1183 profile=BlobToStringInfo(info,(size_t) info_length);
1184 if (profile == (StringInfo *) NULL)
1185 ThrowReaderException(ResourceLimitError,
1186 "MemoryAllocationFailed");
1187 if (i8bim == MagickTrue)
1188 (void) CopyMagickString(name,"8bim",sizeof(name));
1189 else if (icc == MagickTrue)
1190 (void) CopyMagickString(name,"icc",sizeof(name));
1191 else if (iptc == MagickTrue)
1192 (void) CopyMagickString(name,"iptc",sizeof(name));
1193 else if (magick == MagickTrue)
1195 (void) CopyMagickString(name,"magick",sizeof(name));
1196 image->gamma=StringToDouble((char *) info+6,(char **) NULL);
1199 (void) FormatLocaleString(name,sizeof(name),"gif:%.11s",
1201 info=(unsigned char *) RelinquishMagickMemory(info);
1202 if (magick == MagickFalse)
1203 (void) SetImageProfile(image,name,profile,exception);
1204 profile=DestroyStringInfo(profile);
1205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1206 " profile name=%s",name);
1212 while (ReadBlobBlock(image,header) != 0) ;
1217 if (c != (unsigned char) ',')
1219 if (image_count != 0)
1222 Allocate next image structure.
1224 AcquireNextImage(image_info,image,exception);
1225 if (GetNextImageInList(image) == (Image *) NULL)
1227 image=DestroyImageList(image);
1228 global_colormap=(unsigned char *) RelinquishMagickMemory(
1230 return((Image *) NULL);
1232 image=SyncNextImageInList(image);
1236 Read image attributes.
1238 image->storage_class=PseudoClass;
1239 image->compression=LZWCompression;
1240 page.x=(ssize_t) ReadBlobLSBShort(image);
1241 page.y=(ssize_t) ReadBlobLSBShort(image);
1242 image->columns=ReadBlobLSBShort(image);
1243 image->rows=ReadBlobLSBShort(image);
1245 flag=(unsigned char) ReadBlobByte(image);
1246 image->interlace=BitSet((int) flag,0x40) != 0 ? GIFInterlace : NoInterlace;
1247 image->colors=BitSet((int) flag,0x80) == 0 ? global_colors : one <<
1248 ((size_t) (flag & 0x07)+1);
1249 if (opacity >= (ssize_t) image->colors)
1251 image->page.width=page.width;
1252 image->page.height=page.height;
1253 image->page.y=page.y;
1254 image->page.x=page.x;
1256 image->ticks_per_second=100;
1257 image->dispose=(DisposeType) dispose;
1258 image->iterations=iterations;
1259 image->matte=opacity >= 0 ? MagickTrue : MagickFalse;
1263 if ((image->columns == 0) || (image->rows == 0))
1265 global_colormap=(unsigned char *) RelinquishMagickMemory(
1267 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
1270 Inititialize colormap.
1272 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
1274 global_colormap=(unsigned char *) RelinquishMagickMemory(
1276 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1278 if (BitSet((int) flag,0x80) == 0)
1281 Use global colormap.
1284 for (i=0; i < (ssize_t) image->colors; i++)
1286 image->colormap[i].red=(double) ScaleCharToQuantum(*p++);
1287 image->colormap[i].green=(double) ScaleCharToQuantum(*p++);
1288 image->colormap[i].blue=(double) ScaleCharToQuantum(*p++);
1291 image->colormap[i].alpha=(double) TransparentAlpha;
1292 image->transparent_color=image->colormap[opacity];
1295 image->background_color=image->colormap[MagickMin(background,
1304 Read local colormap.
1306 colormap=(unsigned char *) AcquireQuantumMemory(image->colors,3*
1308 if (colormap == (unsigned char *) NULL)
1310 global_colormap=(unsigned char *) RelinquishMagickMemory(
1312 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1314 count=ReadBlob(image,(3*image->colors)*sizeof(*colormap),colormap);
1315 if (count != (ssize_t) (3*image->colors))
1317 global_colormap=(unsigned char *) RelinquishMagickMemory(
1319 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1320 ThrowReaderException(CorruptImageError,
1321 "InsufficientImageDataInFile");
1324 for (i=0; i < (ssize_t) image->colors; i++)
1326 image->colormap[i].red=(double) ScaleCharToQuantum(*p++);
1327 image->colormap[i].green=(double) ScaleCharToQuantum(*p++);
1328 image->colormap[i].blue=(double) ScaleCharToQuantum(*p++);
1330 image->colormap[i].alpha=(double) TransparentAlpha;
1332 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1334 for (i=0; i < (ssize_t) image->colors; i++)
1335 if ((image->colormap[i].red != image->colormap[i].green) ||
1336 (image->colormap[i].green != image->colormap[i].blue))
1338 if ((i == (ssize_t) image->colors) && (image->gamma == 1.0))
1339 SetImageColorspace(image,GRAYColorspace,exception);
1340 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
1341 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
1346 if (image_info->ping != MagickFalse)
1347 status=PingGIFImage(image,exception);
1349 status=DecodeImage(image,opacity,exception);
1350 if ((image_info->ping == MagickFalse) && (status == MagickFalse))
1352 global_colormap=(unsigned char *) RelinquishMagickMemory(
1354 ThrowReaderException(CorruptImageError,"CorruptImage");
1356 if (image_info->number_scenes != 0)
1357 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
1360 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) image->scene-
1362 if (status == MagickFalse)
1365 global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap);
1366 if ((image->columns == 0) || (image->rows == 0))
1367 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
1368 (void) CloseBlob(image);
1369 return(GetFirstImageInList(image));
1373 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1377 % R e g i s t e r G I F I m a g e %
1381 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1383 % RegisterGIFImage() adds properties for the GIF image format to
1384 % the list of supported formats. The properties include the image format
1385 % tag, a method to read and/or write the format, whether the format
1386 % supports the saving of more than one frame to the same file or blob,
1387 % whether the format supports native in-memory I/O, and a brief
1388 % description of the format.
1390 % The format of the RegisterGIFImage method is:
1392 % size_t RegisterGIFImage(void)
1395 ModuleExport size_t RegisterGIFImage(void)
1400 entry=SetMagickInfo("GIF");
1401 entry->decoder=(DecodeImageHandler *) ReadGIFImage;
1402 entry->encoder=(EncodeImageHandler *) WriteGIFImage;
1403 entry->magick=(IsImageFormatHandler *) IsGIF;
1404 entry->description=ConstantString("CompuServe graphics interchange format");
1405 entry->module=ConstantString("GIF");
1406 (void) RegisterMagickInfo(entry);
1407 entry=SetMagickInfo("GIF87");
1408 entry->decoder=(DecodeImageHandler *) ReadGIFImage;
1409 entry->encoder=(EncodeImageHandler *) WriteGIFImage;
1410 entry->magick=(IsImageFormatHandler *) IsGIF;
1411 entry->adjoin=MagickFalse;
1412 entry->description=ConstantString("CompuServe graphics interchange format");
1413 entry->version=ConstantString("version 87a");
1414 entry->module=ConstantString("GIF");
1415 (void) RegisterMagickInfo(entry);
1416 return(MagickImageCoderSignature);
1420 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1424 % U n r e g i s t e r G I F I m a g e %
1428 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1430 % UnregisterGIFImage() removes format registrations made by the
1431 % GIF module from the list of supported formats.
1433 % The format of the UnregisterGIFImage method is:
1435 % UnregisterGIFImage(void)
1438 ModuleExport void UnregisterGIFImage(void)
1440 (void) UnregisterMagickInfo("GIF");
1441 (void) UnregisterMagickInfo("GIF87");
1445 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1449 % W r i t e G I F I m a g e %
1453 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1455 % WriteGIFImage() writes an image to a file in the Compuserve Graphics
1458 % The format of the WriteGIFImage method is:
1460 % MagickBooleanType WriteGIFImage(const ImageInfo *image_info,
1461 % Image *image,ExceptionInfo *exception)
1463 % A description of each parameter follows.
1465 % o image_info: the image info.
1467 % o image: The image.
1469 % o exception: return any errors or warnings in this structure.
1472 static MagickBooleanType WriteGIFImage(const ImageInfo *image_info,Image *image,
1473 ExceptionInfo *exception)
1496 register unsigned char
1514 Open output image file.
1516 assert(image_info != (const ImageInfo *) NULL);
1517 assert(image_info->signature == MagickSignature);
1518 assert(image != (Image *) NULL);
1519 assert(image->signature == MagickSignature);
1520 if (image->debug != MagickFalse)
1521 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1522 assert(exception != (ExceptionInfo *) NULL);
1523 assert(exception->signature == MagickSignature);
1524 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
1525 if (status == MagickFalse)
1530 global_colormap=(unsigned char *) AcquireQuantumMemory(768UL,
1531 sizeof(*global_colormap));
1532 colormap=(unsigned char *) AcquireQuantumMemory(768UL,sizeof(*colormap));
1533 if ((global_colormap == (unsigned char *) NULL) ||
1534 (colormap == (unsigned char *) NULL))
1535 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1536 for (i=0; i < 768; i++)
1537 colormap[i]=(unsigned char) 0;
1541 write_info=CloneImageInfo(image_info);
1542 if (LocaleCompare(write_info->magick,"GIF87") != 0)
1543 (void) WriteBlob(image,6,(unsigned char *) "GIF89a");
1546 (void) WriteBlob(image,6,(unsigned char *) "GIF87a");
1547 write_info->adjoin=MagickFalse;
1550 Determine image bounding box.
1552 page.width=image->columns;
1553 if (image->page.width > page.width)
1554 page.width=image->page.width;
1555 page.height=image->rows;
1556 if (image->page.height > page.height)
1557 page.height=image->page.height;
1558 page.x=image->page.x;
1559 page.y=image->page.y;
1560 (void) WriteBlobLSBShort(image,(unsigned short) page.width);
1561 (void) WriteBlobLSBShort(image,(unsigned short) page.height);
1563 Write images to file.
1565 interlace=write_info->interlace;
1566 if ((write_info->adjoin != MagickFalse) &&
1567 (GetNextImageInList(image) != (Image *) NULL))
1568 interlace=NoInterlace;
1573 if ((IssRGBColorspace(image->colorspace) == MagickFalse) &&
1574 (IsImageGray(image,exception) == MagickFalse))
1575 (void) TransformImageColorspace(image,sRGBColorspace,exception);
1577 if (IsImageOpaque(image,exception) != MagickFalse)
1579 if ((image->storage_class == DirectClass) || (image->colors > 256))
1580 (void) SetImageType(image,PaletteType,exception);
1589 Identify transparent colormap index.
1591 if ((image->storage_class == DirectClass) || (image->colors > 256))
1592 (void) SetImageType(image,PaletteBilevelMatteType,exception);
1593 for (i=0; i < (ssize_t) image->colors; i++)
1594 if (image->colormap[i].alpha != OpaqueAlpha)
1601 alpha=(MagickRealType) TransparentAlpha-(MagickRealType)
1602 image->colormap[i].alpha;
1603 beta=(MagickRealType) TransparentAlpha-(MagickRealType)
1604 image->colormap[opacity].alpha;
1610 (void) SetImageType(image,PaletteBilevelMatteType,exception);
1611 for (i=0; i < (ssize_t) image->colors; i++)
1612 if (image->colormap[i].alpha != OpaqueAlpha)
1619 alpha=(Quantum) TransparentAlpha-(MagickRealType)
1620 image->colormap[i].alpha;
1621 beta=(Quantum) TransparentAlpha-(MagickRealType)
1622 image->colormap[opacity].alpha;
1629 image->colormap[opacity].red=image->transparent_color.red;
1630 image->colormap[opacity].green=image->transparent_color.green;
1631 image->colormap[opacity].blue=image->transparent_color.blue;
1634 if ((image->storage_class == DirectClass) || (image->colors > 256))
1635 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1636 for (bits_per_pixel=1; bits_per_pixel < 8; bits_per_pixel++)
1637 if ((one << bits_per_pixel) >= image->colors)
1640 for (i=0; i < (ssize_t) image->colors; i++)
1642 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].red));
1643 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].green));
1644 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].blue));
1646 for ( ; i < (ssize_t) (one << bits_per_pixel); i++)
1648 *q++=(unsigned char) 0x0;
1649 *q++=(unsigned char) 0x0;
1650 *q++=(unsigned char) 0x0;
1652 if ((GetPreviousImageInList(image) == (Image *) NULL) ||
1653 (write_info->adjoin == MagickFalse))
1656 Write global colormap.
1659 c|=(8-1) << 4; /* color resolution */
1660 c|=(bits_per_pixel-1); /* size of global colormap */
1661 (void) WriteBlobByte(image,(unsigned char) c);
1662 for (j=0; j < (ssize_t) image->colors; j++)
1663 if (IsPixelInfoEquivalent(&image->background_color,image->colormap+j))
1665 (void) WriteBlobByte(image,(unsigned char)
1666 (j == (ssize_t) image->colors ? 0 : j)); /* background color */
1667 (void) WriteBlobByte(image,(unsigned char) 0x00); /* reserved */
1668 length=(size_t) (3*(one << bits_per_pixel));
1669 (void) WriteBlob(image,length,colormap);
1670 for (j=0; j < 768; j++)
1671 global_colormap[j]=colormap[j];
1673 if (LocaleCompare(write_info->magick,"GIF87") != 0)
1676 Write graphics control extension.
1678 (void) WriteBlobByte(image,(unsigned char) 0x21);
1679 (void) WriteBlobByte(image,(unsigned char) 0xf9);
1680 (void) WriteBlobByte(image,(unsigned char) 0x04);
1681 c=image->dispose << 2;
1684 (void) WriteBlobByte(image,(unsigned char) c);
1685 delay=(size_t) (100*image->delay/MagickMax((size_t)
1686 image->ticks_per_second,1));
1687 (void) WriteBlobLSBShort(image,(unsigned short) delay);
1688 (void) WriteBlobByte(image,(unsigned char) (opacity >= 0 ? opacity :
1690 (void) WriteBlobByte(image,(unsigned char) 0x00);
1691 if ((LocaleCompare(write_info->magick,"GIF87") != 0) &&
1692 (GetImageProperty(image,"comment",exception) != (const char *) NULL))
1704 Write comment extension.
1706 (void) WriteBlobByte(image,(unsigned char) 0x21);
1707 (void) WriteBlobByte(image,(unsigned char) 0xfe);
1708 value=GetImageProperty(image,"comment",exception);
1709 for (p=value; *p != '\0'; )
1711 count=MagickMin(strlen(p),255);
1712 (void) WriteBlobByte(image,(unsigned char) count);
1713 for (i=0; i < (ssize_t) count; i++)
1714 (void) WriteBlobByte(image,(unsigned char) *p++);
1716 (void) WriteBlobByte(image,(unsigned char) 0x00);
1718 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
1719 (GetNextImageInList(image) != (Image *) NULL) &&
1720 (image->iterations != 1))
1723 Write Netscape Loop extension.
1725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1726 " Writing GIF Extension %s","NETSCAPE2.0");
1727 (void) WriteBlobByte(image,(unsigned char) 0x21);
1728 (void) WriteBlobByte(image,(unsigned char) 0xff);
1729 (void) WriteBlobByte(image,(unsigned char) 0x0b);
1730 (void) WriteBlob(image,11,(unsigned char *) "NETSCAPE2.0");
1731 (void) WriteBlobByte(image,(unsigned char) 0x03);
1732 (void) WriteBlobByte(image,(unsigned char) 0x01);
1733 (void) WriteBlobLSBShort(image,(unsigned short) image->iterations);
1734 (void) WriteBlobByte(image,(unsigned char) 0x00);
1736 if ((image->gamma != 1.0f/2.2f))
1739 attributes[MaxTextExtent];
1745 Write ImageMagick extension.
1747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1748 " Writing GIF Extension %s","ImageMagick");
1749 (void) WriteBlobByte(image,(unsigned char) 0x21);
1750 (void) WriteBlobByte(image,(unsigned char) 0xff);
1751 (void) WriteBlobByte(image,(unsigned char) 0x0b);
1752 (void) WriteBlob(image,11,(unsigned char *) "ImageMagick");
1753 length=FormatLocaleString(attributes,MaxTextExtent,"gamma=%g",
1755 (void) WriteBlobByte(image,(unsigned char) length);
1756 (void) WriteBlob(image,length,(unsigned char *) attributes);
1757 (void) WriteBlobByte(image,(unsigned char) 0x00);
1759 ResetImageProfileIterator(image);
1768 name=GetNextImageProfile(image);
1769 if (name == (const char *) NULL)
1771 profile=GetImageProfile(image,name);
1772 if (profile != (StringInfo *) NULL)
1774 if ((LocaleCompare(name,"ICC") == 0) ||
1775 (LocaleCompare(name,"ICM") == 0) ||
1776 (LocaleCompare(name,"IPTC") == 0) ||
1777 (LocaleCompare(name,"8BIM") == 0) ||
1778 (LocaleNCompare(name,"gif:",4) == 0))
1789 datum=GetStringInfoDatum(profile);
1790 length=GetStringInfoLength(profile);
1791 (void) WriteBlobByte(image,(unsigned char) 0x21);
1792 (void) WriteBlobByte(image,(unsigned char) 0xff);
1793 (void) WriteBlobByte(image,(unsigned char) 0x0b);
1794 if ((LocaleCompare(name,"ICC") == 0) ||
1795 (LocaleCompare(name,"ICM") == 0))
1798 Write ICC extension.
1800 (void) WriteBlob(image,11,(unsigned char *) "ICCRGBG1012");
1801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1802 " Writing GIF Extension %s","ICCRGBG1012");
1805 if ((LocaleCompare(name,"IPTC") == 0))
1808 Write IPTC extension.
1810 (void) WriteBlob(image,11,(unsigned char *) "MGKIPTC0000");
1811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1812 " Writing GIF Extension %s","MGKIPTC0000");
1815 if ((LocaleCompare(name,"8BIM") == 0))
1818 Write 8BIM extension.
1820 (void) WriteBlob(image,11,(unsigned char *)
1822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1823 " Writing GIF Extension %s","MGK8BIM0000");
1828 extension[MaxTextExtent];
1831 Write generic extension.
1833 (void) CopyMagickString(extension,name+4,
1835 (void) WriteBlob(image,11,(unsigned char *) extension);
1836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1837 " Writing GIF Extension %s",name);
1840 while ((ssize_t) length > offset)
1845 if ((length-offset) < 255)
1846 block_length=length-offset;
1849 (void) WriteBlobByte(image,(unsigned char) block_length);
1850 (void) WriteBlob(image,(size_t) block_length,datum+offset);
1851 offset+=(ssize_t) block_length;
1853 (void) WriteBlobByte(image,(unsigned char) 0x00);
1858 (void) WriteBlobByte(image,','); /* image separator */
1860 Write the image header.
1862 page.x=image->page.x;
1863 page.y=image->page.y;
1864 if ((image->page.width != 0) && (image->page.height != 0))
1866 (void) WriteBlobLSBShort(image,(unsigned short) (page.x < 0 ? 0 : page.x));
1867 (void) WriteBlobLSBShort(image,(unsigned short) (page.y < 0 ? 0 : page.y));
1868 (void) WriteBlobLSBShort(image,(unsigned short) image->columns);
1869 (void) WriteBlobLSBShort(image,(unsigned short) image->rows);
1871 if (interlace != NoInterlace)
1872 c|=0x40; /* pixel data is interlaced */
1873 for (j=0; j < (ssize_t) (3*image->colors); j++)
1874 if (colormap[j] != global_colormap[j])
1876 if (j == (ssize_t) (3*image->colors))
1877 (void) WriteBlobByte(image,(unsigned char) c);
1881 c|=(bits_per_pixel-1); /* size of local colormap */
1882 (void) WriteBlobByte(image,(unsigned char) c);
1883 length=(size_t) (3*(one << bits_per_pixel));
1884 (void) WriteBlob(image,length,colormap);
1887 Write the image data.
1889 c=(int) MagickMax(bits_per_pixel,2);
1890 (void) WriteBlobByte(image,(unsigned char) c);
1891 status=EncodeImage(write_info,image,(size_t) MagickMax(bits_per_pixel,2)+1,
1893 if (status == MagickFalse)
1895 global_colormap=(unsigned char *) RelinquishMagickMemory(
1897 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1898 write_info=DestroyImageInfo(write_info);
1899 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1901 (void) WriteBlobByte(image,(unsigned char) 0x00);
1902 if (GetNextImageInList(image) == (Image *) NULL)
1904 image=SyncNextImageInList(image);
1906 status=SetImageProgress(image,SaveImagesTag,scene,
1907 GetImageListLength(image));
1908 if (status == MagickFalse)
1910 } while (write_info->adjoin != MagickFalse);
1911 (void) WriteBlobByte(image,';'); /* terminator */
1912 global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap);
1913 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1914 write_info=DestroyImageInfo(write_info);
1915 (void) CloseBlob(image);