]> granicus.if.org Git - imagemagick/blob - coders/gif.c
(no commit message)
[imagemagick] / coders / gif.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                             GGGG  IIIII  FFFFF                              %
7 %                            G        I    F                                  %
8 %                            G  GG    I    FFF                                %
9 %                            G   G    I    F                                  %
10 %                             GGG   IIIII  F                                  %
11 %                                                                             %
12 %                                                                             %
13 %            Read/Write Compuserv Graphics Interchange Format                 %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                John Cristy                                  %
17 %                                 July 1992                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "magick/studio.h"
43 #include "magick/blob.h"
44 #include "magick/blob-private.h"
45 #include "magick/cache.h"
46 #include "magick/color.h"
47 #include "magick/color-private.h"
48 #include "magick/colormap.h"
49 #include "magick/colormap-private.h"
50 #include "magick/colorspace.h"
51 #include "magick/exception.h"
52 #include "magick/exception-private.h"
53 #include "magick/image.h"
54 #include "magick/image-private.h"
55 #include "magick/list.h"
56 #include "magick/profile.h"
57 #include "magick/magick.h"
58 #include "magick/memory_.h"
59 #include "magick/monitor.h"
60 #include "magick/monitor-private.h"
61 #include "magick/option.h"
62 #include "magick/property.h"
63 #include "magick/quantize.h"
64 #include "magick/quantum-private.h"
65 #include "magick/static.h"
66 #include "magick/string_.h"
67 #include "magick/module.h"
68 \f
69 /*
70   Define declarations.
71 */
72 #define MaximumLZWBits  12
73 #define MaximumLZWCode  (1UL << MaximumLZWBits)
74 \f
75 /*
76   Typdef declarations.
77 */
78 typedef struct _LZWCodeInfo
79 {
80   unsigned char
81     buffer[280];
82
83   size_t
84     count,
85     bit;
86
87   MagickBooleanType
88     eof;
89 } LZWCodeInfo;
90
91 typedef struct _LZWStack
92 {
93   size_t
94     *codes,
95     *index,
96     *top;
97 } LZWStack;
98
99 typedef struct _LZWInfo
100 {
101   Image
102     *image;
103
104   LZWStack
105     *stack;
106
107   MagickBooleanType
108     genesis;
109
110   size_t
111     data_size,
112     maximum_data_value,
113     clear_code,
114     end_code,
115     bits,
116     first_code,
117     last_code,
118     maximum_code,
119     slot,
120     *table[2];
121
122   LZWCodeInfo
123     code_info;
124 } LZWInfo;
125 \f
126 /*
127   Forward declarations.
128 */
129 static inline int
130   GetNextLZWCode(LZWInfo *,const size_t);
131
132 static MagickBooleanType
133   WriteGIFImage(const ImageInfo *,Image *);
134
135 static ssize_t
136   ReadBlobBlock(Image *,unsigned char *);
137 \f
138 /*
139 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
140 %                                                                             %
141 %                                                                             %
142 %                                                                             %
143 %   D e c o d e I m a g e                                                     %
144 %                                                                             %
145 %                                                                             %
146 %                                                                             %
147 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
148 %
149 %  DecodeImage uncompresses an image via GIF-coding.
150 %
151 %  The format of the DecodeImage method is:
152 %
153 %      MagickBooleanType DecodeImage(Image *image,const ssize_t opacity)
154 %
155 %  A description of each parameter follows:
156 %
157 %    o image: the address of a structure of type Image.
158 %
159 %    o opacity:  The colormap index associated with the transparent color.
160 %
161 */
162
163 static LZWInfo *RelinquishLZWInfo(LZWInfo *lzw_info)
164 {
165   if (lzw_info->table[0] != (size_t *) NULL)
166     lzw_info->table[0]=(size_t *) RelinquishMagickMemory(
167       lzw_info->table[0]);
168   if (lzw_info->table[1] != (size_t *) NULL)
169     lzw_info->table[1]=(size_t *) RelinquishMagickMemory(
170       lzw_info->table[1]);
171   if (lzw_info->stack != (LZWStack *) NULL)
172     {
173       if (lzw_info->stack->codes != (size_t *) NULL)
174         lzw_info->stack->codes=(size_t *) RelinquishMagickMemory(
175           lzw_info->stack->codes);
176       lzw_info->stack=(LZWStack *) RelinquishMagickMemory(lzw_info->stack);
177     }
178   lzw_info=(LZWInfo *) RelinquishMagickMemory(lzw_info);
179   return((LZWInfo *) NULL);
180 }
181
182 static inline void ResetLZWInfo(LZWInfo *lzw_info)
183 {
184   lzw_info->bits=lzw_info->data_size+1;
185   lzw_info->maximum_code=1UL << lzw_info->bits;
186   lzw_info->slot=lzw_info->maximum_data_value+3;
187   lzw_info->genesis=MagickTrue;
188 }
189
190 static LZWInfo *AcquireLZWInfo(Image *image,const size_t data_size)
191 {
192   LZWInfo
193     *lzw_info;
194
195   register ssize_t
196     i;
197
198   lzw_info=(LZWInfo *) AcquireAlignedMemory(1,sizeof(*lzw_info));
199   if (lzw_info == (LZWInfo *) NULL)
200     return((LZWInfo *) NULL);
201   (void) ResetMagickMemory(lzw_info,0,sizeof(*lzw_info));
202   lzw_info->image=image;
203   lzw_info->data_size=data_size;
204   lzw_info->maximum_data_value=(1UL << data_size)-1;
205   lzw_info->clear_code=lzw_info->maximum_data_value+1;
206   lzw_info->end_code=lzw_info->maximum_data_value+2;
207   lzw_info->table[0]=(size_t *) AcquireQuantumMemory(MaximumLZWCode,
208     sizeof(*lzw_info->table));
209   lzw_info->table[1]=(size_t *) AcquireQuantumMemory(MaximumLZWCode,
210     sizeof(*lzw_info->table));
211   if ((lzw_info->table[0] == (size_t *) NULL) ||
212       (lzw_info->table[1] == (size_t *) NULL))
213     {
214       lzw_info=RelinquishLZWInfo(lzw_info);
215       return((LZWInfo *) NULL);
216     }
217   for (i=0; i <= (ssize_t) lzw_info->maximum_data_value; i++)
218   {
219     lzw_info->table[0][i]=0;
220     lzw_info->table[1][i]=(size_t) i;
221   }
222   ResetLZWInfo(lzw_info);
223   lzw_info->code_info.buffer[0]='\0';
224   lzw_info->code_info.buffer[1]='\0';
225   lzw_info->code_info.count=2;
226   lzw_info->code_info.bit=8*lzw_info->code_info.count;
227   lzw_info->code_info.eof=MagickFalse;
228   lzw_info->genesis=MagickTrue;
229   lzw_info->stack=(LZWStack *) AcquireAlignedMemory(1,sizeof(*lzw_info->stack));
230   if (lzw_info->stack == (LZWStack *) NULL)
231     {
232       lzw_info=RelinquishLZWInfo(lzw_info);
233       return((LZWInfo *) NULL);
234     }
235   lzw_info->stack->codes=(size_t *) AcquireQuantumMemory(2UL*
236     MaximumLZWCode,sizeof(*lzw_info->stack->codes));
237   if (lzw_info->stack->codes == (size_t *) NULL)
238     {
239       lzw_info=RelinquishLZWInfo(lzw_info);
240       return((LZWInfo *) NULL);
241     }
242   lzw_info->stack->index=lzw_info->stack->codes;
243   lzw_info->stack->top=lzw_info->stack->codes+2*MaximumLZWCode;
244   return(lzw_info);
245 }
246
247 static inline int GetNextLZWCode(LZWInfo *lzw_info,const size_t bits)
248 {
249   int
250     code;
251
252   register ssize_t
253     i;
254
255   while (((lzw_info->code_info.bit+bits) > (8*lzw_info->code_info.count)) &&
256          (lzw_info->code_info.eof == MagickFalse))
257   {
258     ssize_t
259       count;
260
261     lzw_info->code_info.buffer[0]=lzw_info->code_info.buffer[
262       lzw_info->code_info.count-2];
263     lzw_info->code_info.buffer[1]=lzw_info->code_info.buffer[
264       lzw_info->code_info.count-1];
265     lzw_info->code_info.bit-=8*(lzw_info->code_info.count-2);
266     lzw_info->code_info.count=2;
267     count=ReadBlobBlock(lzw_info->image,&lzw_info->code_info.buffer[
268       lzw_info->code_info.count]);
269     if (count > 0)
270       lzw_info->code_info.count+=count;
271     else
272       lzw_info->code_info.eof=MagickTrue;
273   }
274   if ((lzw_info->code_info.bit+bits) > (8*lzw_info->code_info.count))
275     return(-1);
276   code=0;
277   for (i=0; i < (ssize_t) bits; i++)
278   {
279     code|=((lzw_info->code_info.buffer[lzw_info->code_info.bit/8] &
280       (1UL << (lzw_info->code_info.bit % 8))) != 0) << i;
281     lzw_info->code_info.bit++;
282   }
283   return(code);
284 }
285
286 static inline ssize_t PopLZWStack(LZWStack *stack_info)
287 {
288   if (stack_info->index <= stack_info->codes)
289     return(-1);
290   stack_info->index--;
291   return((ssize_t) *stack_info->index);
292 }
293
294 static inline void PushLZWStack(LZWStack *stack_info,const size_t value)
295 {
296   if (stack_info->index >= stack_info->top)
297     return;
298   *stack_info->index=value;
299   stack_info->index++;
300 }
301
302 static int ReadBlobLZWByte(LZWInfo *lzw_info)
303 {
304   int
305     code;
306
307   ssize_t
308     count;
309
310   size_t
311     value;
312
313   if (lzw_info->stack->index != lzw_info->stack->codes)
314     return(PopLZWStack(lzw_info->stack));
315   if (lzw_info->genesis != MagickFalse)
316     {
317       lzw_info->genesis=MagickFalse;
318       do
319       {
320         lzw_info->first_code=(size_t) GetNextLZWCode(lzw_info,
321           lzw_info->bits);
322         lzw_info->last_code=lzw_info->first_code;
323       } while (lzw_info->first_code == lzw_info->clear_code);
324       return((int) lzw_info->first_code);
325     }
326   code=GetNextLZWCode(lzw_info,lzw_info->bits);
327   if (code < 0)
328     return(code);
329   if ((size_t) code == lzw_info->clear_code)
330     {
331       ResetLZWInfo(lzw_info);
332       return(ReadBlobLZWByte(lzw_info));
333     }
334   if ((size_t) code == lzw_info->end_code)
335     return(-1);
336   if ((size_t) code < lzw_info->slot)
337     value=(size_t) code;
338   else
339     {
340       PushLZWStack(lzw_info->stack,lzw_info->first_code);
341       value=lzw_info->last_code;
342     }
343   count=0;
344   while (value > lzw_info->maximum_data_value)
345   {
346     if ((size_t) count > MaximumLZWCode)
347       return(-1);
348     count++;
349     if ((size_t) value > MaximumLZWCode)
350       return(-1);
351     PushLZWStack(lzw_info->stack,lzw_info->table[1][value]);
352     value=lzw_info->table[0][value];
353   }
354   lzw_info->first_code=lzw_info->table[1][value];
355   PushLZWStack(lzw_info->stack,lzw_info->first_code);
356   if (lzw_info->slot < MaximumLZWCode)
357     {
358       lzw_info->table[0][lzw_info->slot]=lzw_info->last_code;
359       lzw_info->table[1][lzw_info->slot]=lzw_info->first_code;
360       lzw_info->slot++;
361       if ((lzw_info->slot >= lzw_info->maximum_code) &&
362           (lzw_info->bits < MaximumLZWBits))
363         {
364           lzw_info->bits++;
365           lzw_info->maximum_code=1UL << lzw_info->bits;
366         }
367     }
368   lzw_info->last_code=(size_t) code;
369   return(PopLZWStack(lzw_info->stack));
370 }
371
372 static MagickBooleanType DecodeImage(Image *image,const ssize_t opacity)
373 {
374   ExceptionInfo
375     *exception;
376
377   IndexPacket
378     index;
379
380   int
381     c;
382
383   ssize_t
384     offset,
385     y;
386
387   LZWInfo
388     *lzw_info;
389
390   unsigned char
391     data_size;
392
393   size_t
394     pass;
395
396   /*
397     Allocate decoder tables.
398   */
399   assert(image != (Image *) NULL);
400   assert(image->signature == MagickSignature);
401   if (image->debug != MagickFalse)
402     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
403   data_size=(unsigned char) ReadBlobByte(image);
404   if (data_size > MaximumLZWBits)
405     ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
406   lzw_info=AcquireLZWInfo(image,data_size);
407   if (lzw_info == (LZWInfo *) NULL)
408     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
409       image->filename);
410   exception=(&image->exception);
411   pass=0;
412   offset=0;
413   for (y=0; y < (ssize_t) image->rows; y++)
414   {
415     register IndexPacket
416       *restrict indexes;
417
418     register ssize_t
419       x;
420
421     register PixelPacket
422       *restrict q;
423
424     q=GetAuthenticPixels(image,0,offset,image->columns,1,exception);
425     if (q == (PixelPacket *) NULL)
426       break;
427     indexes=GetAuthenticIndexQueue(image);
428     for (x=0; x < (ssize_t) image->columns; )
429     {
430       c=ReadBlobLZWByte(lzw_info);
431       if (c < 0)
432         break;
433       index=ConstrainColormapIndex(image,(size_t) c);
434       q->red=image->colormap[(ssize_t) index].red;
435       q->green=image->colormap[(ssize_t) index].green;
436       q->blue=image->colormap[(ssize_t) index].blue;
437       q->opacity=(ssize_t) index == opacity ? (Quantum) TransparentOpacity :
438         (Quantum) OpaqueOpacity;
439       indexes[x]=index;
440       x++;
441       q++;
442     }
443     if (x < (ssize_t) image->columns)
444       break;
445     if (image->interlace == NoInterlace)
446       offset++;
447     else
448       switch (pass)
449       {
450         case 0:
451         default:
452         {
453           offset+=8;
454           if (offset >= (ssize_t) image->rows)
455             {
456               pass++;
457               offset=4;
458             }
459           break;
460         }
461         case 1:
462         {
463           offset+=8;
464           if (offset >= (ssize_t) image->rows)
465             {
466               pass++;
467               offset=2;
468             }
469           break;
470         }
471         case 2:
472         {
473           offset+=4;
474           if (offset >= (ssize_t) image->rows)
475             {
476               pass++;
477               offset=1;
478             }
479           break;
480         }
481         case 3:
482         {
483           offset+=2;
484           break;
485         }
486       }
487     if (SyncAuthenticPixels(image,exception) == MagickFalse)
488       break;
489   }
490   lzw_info=RelinquishLZWInfo(lzw_info);
491   if (y < (ssize_t) image->rows)
492     ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
493   return(MagickTrue);
494 }
495 \f
496 /*
497 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
498 %                                                                             %
499 %                                                                             %
500 %                                                                             %
501 %   E n c o d e I m a g e                                                     %
502 %                                                                             %
503 %                                                                             %
504 %                                                                             %
505 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
506 %
507 %  EncodeImage compresses an image via GIF-coding.
508 %
509 %  The format of the EncodeImage method is:
510 %
511 %      MagickBooleanType EncodeImage(const ImageInfo *image_info,Image *image,
512 %        const size_t data_size)
513 %
514 %  A description of each parameter follows:
515 %
516 %    o image_info: the image info.
517 %
518 %    o image: the address of a structure of type Image.
519 %
520 %    o data_size:  The number of bits in the compressed packet.
521 %
522 */
523 static MagickBooleanType EncodeImage(const ImageInfo *image_info,Image *image,
524   const size_t data_size)
525 {
526 #define MaxCode(number_bits)  ((1UL << (number_bits))-1)
527 #define MaxHashTable  5003
528 #define MaxGIFBits  12UL
529 #define MaxGIFTable  (1UL << MaxGIFBits)
530 #define GIFOutputCode(code) \
531 { \
532   /*  \
533     Emit a code. \
534   */ \
535   if (bits > 0) \
536     datum|=(code) << bits; \
537   else \
538     datum=code; \
539   bits+=number_bits; \
540   while (bits >= 8) \
541   { \
542     /*  \
543       Add a character to current packet. \
544     */ \
545     packet[length++]=(unsigned char) (datum & 0xff); \
546     if (length >= 254) \
547       { \
548         (void) WriteBlobByte(image,(unsigned char) length); \
549         (void) WriteBlob(image,length,packet); \
550         length=0; \
551       } \
552     datum>>=8; \
553     bits-=8; \
554   } \
555   if (free_code > max_code)  \
556     { \
557       number_bits++; \
558       if (number_bits == MaxGIFBits) \
559         max_code=MaxGIFTable; \
560       else \
561         max_code=MaxCode(number_bits); \
562     } \
563 }
564
565   IndexPacket
566     index;
567
568   ssize_t
569     displacement,
570     offset,
571     k,
572     y;
573
574   register ssize_t
575     i;
576
577   size_t
578     length;
579
580   short
581     *hash_code,
582     *hash_prefix,
583     waiting_code;
584
585   unsigned char
586     *packet,
587     *hash_suffix;
588
589   size_t
590     bits,
591     clear_code,
592     datum,
593     end_of_information_code,
594     free_code,
595     max_code,
596     next_pixel,
597     number_bits,
598     pass;
599
600   /*
601     Allocate encoder tables.
602   */
603   assert(image != (Image *) NULL);
604   packet=(unsigned char *) AcquireQuantumMemory(256,sizeof(*packet));
605   hash_code=(short *) AcquireQuantumMemory(MaxHashTable,sizeof(*hash_code));
606   hash_prefix=(short *) AcquireQuantumMemory(MaxHashTable,sizeof(*hash_prefix));
607   hash_suffix=(unsigned char *) AcquireQuantumMemory(MaxHashTable,
608     sizeof(*hash_suffix));
609   if ((packet == (unsigned char *) NULL) || (hash_code == (short *) NULL) ||
610       (hash_prefix == (short *) NULL) ||
611       (hash_suffix == (unsigned char *) NULL))
612     {
613       if (packet != (unsigned char *) NULL)
614         packet=(unsigned char *) RelinquishMagickMemory(packet);
615       if (hash_code != (short *) NULL)
616         hash_code=(short *) RelinquishMagickMemory(hash_code);
617       if (hash_prefix != (short *) NULL)
618         hash_prefix=(short *) RelinquishMagickMemory(hash_prefix);
619       if (hash_suffix != (unsigned char *) NULL)
620         hash_suffix=(unsigned char *) RelinquishMagickMemory(hash_suffix);
621       return(MagickFalse);
622     }
623   /*
624     Initialize GIF encoder.
625   */
626   number_bits=data_size;
627   max_code=MaxCode(number_bits);
628   clear_code=((short) 1UL << (data_size-1));
629   end_of_information_code=clear_code+1;
630   free_code=clear_code+2;
631   length=0;
632   datum=0;
633   bits=0;
634   for (i=0; i < MaxHashTable; i++)
635     hash_code[i]=0;
636   GIFOutputCode(clear_code);
637   /*
638     Encode pixels.
639   */
640   offset=0;
641   pass=0;
642   waiting_code=0;
643   for (y=0; y < (ssize_t) image->rows; y++)
644   {
645     register const IndexPacket
646       *restrict indexes;
647
648     register const PixelPacket
649       *restrict p;
650
651     register ssize_t
652       x;
653
654     p=GetVirtualPixels(image,0,offset,image->columns,1,&image->exception);
655     if (p == (const PixelPacket *) NULL)
656       break;
657     indexes=GetVirtualIndexQueue(image);
658     if (y == 0)
659       waiting_code=(short) (*indexes);
660     for (x=(y == 0) ? 1 : 0; x < (ssize_t) image->columns; x++)
661     {
662       /*
663         Probe hash table.
664       */
665       index=(IndexPacket) ((size_t) indexes[x] & 0xff);
666       p++;
667       k=(ssize_t) (((size_t) index << (MaxGIFBits-8))+waiting_code);
668       if (k >= MaxHashTable)
669         k-=MaxHashTable;
670       next_pixel=MagickFalse;
671       displacement=1;
672       if (hash_code[k] > 0)
673         {
674           if ((hash_prefix[k] == waiting_code) &&
675               (hash_suffix[k] == (unsigned char) index))
676             {
677               waiting_code=hash_code[k];
678               continue;
679             }
680           if (k != 0)
681             displacement=MaxHashTable-k;
682           for ( ; ; )
683           {
684             k-=displacement;
685             if (k < 0)
686               k+=MaxHashTable;
687             if (hash_code[k] == 0)
688               break;
689             if ((hash_prefix[k] == waiting_code) &&
690                 (hash_suffix[k] == (unsigned char) index))
691               {
692                 waiting_code=hash_code[k];
693                 next_pixel=MagickTrue;
694                 break;
695               }
696           }
697           if (next_pixel == MagickTrue)
698             continue;
699         }
700       GIFOutputCode((size_t) waiting_code);
701       if (free_code < MaxGIFTable)
702         {
703           hash_code[k]=(short) free_code++;
704           hash_prefix[k]=waiting_code;
705           hash_suffix[k]=(unsigned char) index;
706         }
707       else
708         {
709           /*
710             Fill the hash table with empty entries.
711           */
712           for (k=0; k < MaxHashTable; k++)
713             hash_code[k]=0;
714           /*
715             Reset compressor and issue a clear code.
716           */
717           free_code=clear_code+2;
718           GIFOutputCode(clear_code);
719           number_bits=data_size;
720           max_code=MaxCode(number_bits);
721         }
722       waiting_code=(short) index;
723     }
724     if (image_info->interlace == NoInterlace)
725       offset++;
726     else
727       switch (pass)
728       {
729         case 0:
730         default:
731         {
732           offset+=8;
733           if (offset >= (ssize_t) image->rows)
734             {
735               pass++;
736               offset=4;
737             }
738           break;
739         }
740         case 1:
741         {
742           offset+=8;
743           if (offset >= (ssize_t) image->rows)
744             {
745               pass++;
746               offset=2;
747             }
748           break;
749         }
750         case 2:
751         {
752           offset+=4;
753           if (offset >= (ssize_t) image->rows)
754             {
755               pass++;
756               offset=1;
757             }
758           break;
759         }
760         case 3:
761         {
762           offset+=2;
763           break;
764         }
765       }
766   }
767   /*
768     Flush out the buffered code.
769   */
770   GIFOutputCode((size_t) waiting_code);
771   GIFOutputCode(end_of_information_code);
772   if (bits > 0)
773     {
774       /*
775         Add a character to current packet.
776       */
777       packet[length++]=(unsigned char) (datum & 0xff);
778       if (length >= 254)
779         {
780           (void) WriteBlobByte(image,(unsigned char) length);
781           (void) WriteBlob(image,length,packet);
782           length=0;
783         }
784     }
785   /*
786     Flush accumulated data.
787   */
788   if (length > 0)
789     {
790       (void) WriteBlobByte(image,(unsigned char) length);
791       (void) WriteBlob(image,length,packet);
792     }
793   /*
794     Free encoder memory.
795   */
796   hash_suffix=(unsigned char *) RelinquishMagickMemory(hash_suffix);
797   hash_prefix=(short *) RelinquishMagickMemory(hash_prefix);
798   hash_code=(short *) RelinquishMagickMemory(hash_code);
799   packet=(unsigned char *) RelinquishMagickMemory(packet);
800   return(MagickTrue);
801 }
802 \f
803 /*
804 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
805 %                                                                             %
806 %                                                                             %
807 %                                                                             %
808 %   I s G I F                                                                 %
809 %                                                                             %
810 %                                                                             %
811 %                                                                             %
812 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
813 %
814 %  IsGIF() returns MagickTrue if the image format type, identified by the
815 %  magick string, is GIF.
816 %
817 %  The format of the IsGIF method is:
818 %
819 %      MagickBooleanType IsGIF(const unsigned char *magick,const size_t length)
820 %
821 %  A description of each parameter follows:
822 %
823 %    o magick: compare image format pattern against these bytes.
824 %
825 %    o length: Specifies the length of the magick string.
826 %
827 */
828 static MagickBooleanType IsGIF(const unsigned char *magick,const size_t length)
829 {
830   if (length < 4)
831     return(MagickFalse);
832   if (LocaleNCompare((char *) magick,"GIF8",4) == 0)
833     return(MagickTrue);
834   return(MagickFalse);
835 }
836 \f
837 /*
838 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
839 %                                                                             %
840 %                                                                             %
841 %                                                                             %
842 +  R e a d B l o b B l o c k                                                  %
843 %                                                                             %
844 %                                                                             %
845 %                                                                             %
846 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
847 %
848 %  ReadBlobBlock() reads data from the image file and returns it.  The
849 %  amount of data is determined by first reading a count byte.  The number
850 %  of bytes read is returned.
851 %
852 %  The format of the ReadBlobBlock method is:
853 %
854 %      size_t ReadBlobBlock(Image *image,unsigned char *data)
855 %
856 %  A description of each parameter follows:
857 %
858 %    o image: the image.
859 %
860 %    o data:  Specifies an area to place the information requested from
861 %      the file.
862 %
863 */
864 static ssize_t ReadBlobBlock(Image *image,unsigned char *data)
865 {
866   ssize_t
867     count;
868
869   unsigned char
870     block_count;
871
872   assert(image != (Image *) NULL);
873   assert(image->signature == MagickSignature);
874   assert(data != (unsigned char *) NULL);
875   count=ReadBlob(image,1,&block_count);
876   if (count != 1)
877     return(0);
878   return(ReadBlob(image,(size_t) block_count,data));
879 }
880 \f
881 /*
882 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
883 %                                                                             %
884 %                                                                             %
885 %                                                                             %
886 %   R e a d G I F I m a g e                                                   %
887 %                                                                             %
888 %                                                                             %
889 %                                                                             %
890 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
891 %
892 %  ReadGIFImage() reads a Compuserve Graphics image file and returns it.
893 %  It allocates the memory necessary for the new Image structure and returns a
894 %  pointer to the new image.
895 %
896 %  The format of the ReadGIFImage method is:
897 %
898 %      Image *ReadGIFImage(const ImageInfo *image_info,ExceptionInfo *exception)
899 %
900 %  A description of each parameter follows:
901 %
902 %    o image_info: the image info.
903 %
904 %    o exception: return any errors or warnings in this structure.
905 %
906 */
907
908 static inline size_t MagickMax(const size_t x,const size_t y)
909 {
910   if (x > y)
911     return(x);
912   return(y);
913 }
914
915 static inline size_t MagickMin(const size_t x,const size_t y)
916 {
917   if (x < y)
918     return(x);
919   return(y);
920 }
921
922 static MagickBooleanType PingGIFImage(Image *image)
923 {
924   unsigned char
925     buffer[256],
926     length,
927     data_size;
928
929   assert(image != (Image *) NULL);
930   assert(image->signature == MagickSignature);
931   if (image->debug != MagickFalse)
932     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
933   if (ReadBlob(image,1,&data_size) != 1)
934     ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
935   if (data_size > MaximumLZWBits)
936     ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
937   if (ReadBlob(image,1,&length) != 1)
938     ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
939   while (length != 0)
940   {
941     if (ReadBlob(image,length,buffer) != (ssize_t) length)
942       ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
943     if (ReadBlob(image,1,&length) != 1)
944       ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
945   }
946   return(MagickTrue);
947 }
948
949 static Image *ReadGIFImage(const ImageInfo *image_info,ExceptionInfo *exception)
950 {
951 #define BitSet(byte,bit)  (((byte) & (bit)) == (bit))
952 #define LSBFirstOrder(x,y)  (((y) << 8) | (x))
953
954   Image
955     *image;
956
957   int
958     number_extensionss=0;
959
960   ssize_t
961     opacity;
962
963   MagickBooleanType
964     status;
965
966   RectangleInfo
967     page;
968
969   register ssize_t
970     i;
971
972   register unsigned char
973     *p;
974
975   ssize_t
976     count;
977
978   unsigned char
979     background,
980     c,
981     flag,
982     *global_colormap,
983     header[MaxTextExtent],
984     magick[12];
985
986   size_t
987     delay,
988     dispose,
989     global_colors,
990     image_count,
991     iterations;
992
993   /*
994     Open image file.
995   */
996   assert(image_info != (const ImageInfo *) NULL);
997   assert(image_info->signature == MagickSignature);
998   if (image_info->debug != MagickFalse)
999     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1000       image_info->filename);
1001   assert(exception != (ExceptionInfo *) NULL);
1002   assert(exception->signature == MagickSignature);
1003   image=AcquireImage(image_info);
1004   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1005   if (status == MagickFalse)
1006     {
1007       image=DestroyImageList(image);
1008       return((Image *) NULL);
1009     }
1010   /*
1011     Determine if this a GIF file.
1012   */
1013   count=ReadBlob(image,6,magick);
1014   if ((count != 6) || ((LocaleNCompare((char *) magick,"GIF87",5) != 0) &&
1015       (LocaleNCompare((char *) magick,"GIF89",5) != 0)))
1016     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1017   page.width=ReadBlobLSBShort(image);
1018   page.height=ReadBlobLSBShort(image);
1019   flag=(unsigned char) ReadBlobByte(image);
1020   background=(unsigned char) ReadBlobByte(image);
1021   c=(unsigned char) ReadBlobByte(image);  /* reserved */
1022   global_colors=1UL << (((size_t) flag & 0x07)+1);
1023   global_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
1024     MagickMax(global_colors,256),3UL*sizeof(*global_colormap));
1025   if (global_colormap == (unsigned char *) NULL)
1026     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1027   if (BitSet((int) flag,0x80) != 0)
1028     count=ReadBlob(image,(size_t) (3*global_colors),global_colormap);
1029   delay=0;
1030   dispose=0;
1031   iterations=1;
1032   opacity=(-1);
1033   image_count=0;
1034   for ( ; ; )
1035   {
1036     count=ReadBlob(image,1,&c);
1037     if (count != 1)
1038       break;
1039     if (c == (unsigned char) ';')
1040       break;  /* terminator */
1041     if (c == (unsigned char) '!')
1042       {
1043         /*
1044           GIF Extension block.
1045         */
1046
1047         count=ReadBlob(image,1,&c);
1048         if (count != 1)
1049           {
1050             global_colormap=(unsigned char *) RelinquishMagickMemory(
1051               global_colormap);
1052             ThrowReaderException(CorruptImageError,
1053               "UnableToReadExtensionBlock");
1054           }
1055         switch (c)
1056         {
1057           case 0xf9:
1058           {
1059             /*
1060               Read graphics control extension.
1061             */
1062             while (ReadBlobBlock(image,header) != 0) ;
1063             dispose=(size_t) (header[0] >> 2);
1064             delay=(size_t) ((header[2] << 8) | header[1]);
1065             if ((ssize_t) (header[0] & 0x01) == 0x01)
1066               opacity=(ssize_t) header[3];
1067             break;
1068           }
1069           case 0xfe:
1070           {
1071             char
1072               *comments;
1073
1074             /*
1075               Read comment extension.
1076             */
1077             comments=AcquireString((char *) NULL);
1078             for ( ; ; )
1079             {
1080               count=(ssize_t) ReadBlobBlock(image,header);
1081               if (count == 0)
1082                 break;
1083               header[count]='\0';
1084               (void) ConcatenateString(&comments,(const char *) header);
1085             }
1086             (void) SetImageProperty(image,"comment",comments);
1087             comments=DestroyString(comments);
1088             break;
1089           }
1090           case 0xff:
1091           {
1092             /* Read GIF application extension */
1093
1094             MagickBooleanType
1095               loop;
1096
1097             /*
1098               Read Netscape Loop extension.
1099             */
1100             loop=MagickFalse;
1101             if (ReadBlobBlock(image,header) != 0)
1102               loop=LocaleNCompare((char *) header,"NETSCAPE2.0",11) == 0 ?
1103                 MagickTrue : MagickFalse;
1104             if (loop != MagickFalse)
1105               {
1106                 while (ReadBlobBlock(image,header) != 0)
1107                   iterations=(size_t) ((header[2] << 8) | header[1]);
1108                 break;
1109               }
1110             else
1111               {
1112                 char
1113                   name[MaxTextExtent];
1114
1115                 int
1116                   block_length,
1117                   info_length,
1118                   reserved_length;
1119
1120                 MagickBooleanType
1121                   i8bim,
1122                   icc,
1123                   iptc;
1124
1125                 StringInfo
1126                   *profile;
1127
1128                 unsigned char
1129                   *info;
1130
1131                 /*
1132                   Store GIF application extension as a generic profile.
1133                 */
1134                 i8bim=LocaleNCompare((char *) header,"MGK8BIM0000",11) == 0 ?
1135                   MagickTrue : MagickFalse;
1136                 icc=LocaleNCompare((char *) header,"ICCRGBG1012",11) == 0 ?
1137                   MagickTrue : MagickFalse;
1138                 iptc=LocaleNCompare((char *) header,"MGKIPTC0000",11) == 0 ?
1139                   MagickTrue : MagickFalse;
1140                 number_extensionss++;
1141                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1142                   "    Reading GIF application extension");
1143                 info=(unsigned char *) AcquireQuantumMemory(255UL,
1144                   sizeof(*info));
1145                 reserved_length=255;
1146                 for (info_length=0; ; )
1147                 {
1148                   block_length=(int) ReadBlobBlock(image,&info[info_length]);
1149                   if (block_length == 0)
1150                     break;
1151                   info_length+=block_length;
1152                   if (info_length > (reserved_length-255))
1153                     {
1154                        reserved_length+=4096;
1155                        info=(unsigned char *) ResizeQuantumMemory(info,
1156                          (size_t) reserved_length,sizeof(*info));
1157                     }
1158                 }
1159                 info=(unsigned char *) ResizeQuantumMemory(info,(size_t)
1160                   (info_length+1),sizeof(*info));
1161                 profile=AcquireStringInfo((size_t) info_length);
1162                 SetStringInfoDatum(profile,(const unsigned char *) info);
1163                 if (i8bim == MagickTrue)
1164                   (void) CopyMagickString(name,"8bim",sizeof(name));
1165                 else if (icc == MagickTrue)
1166                   (void) CopyMagickString(name,"icc",sizeof(name));
1167                 else if (iptc == MagickTrue)
1168                   (void) CopyMagickString(name,"iptc",sizeof(name));
1169                 else
1170                   (void) FormatMagickString(name,sizeof(name),"gif:%.11s",
1171                     header);
1172                 (void) SetImageProfile(image,name,profile);
1173                 info=(unsigned char *) RelinquishMagickMemory(info);
1174                 profile=DestroyStringInfo(profile);
1175                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1176                   "      profile name=%s",name);
1177               }
1178             break;
1179           }
1180           default:
1181           {
1182             while (ReadBlobBlock(image,header) != 0) ;
1183             break;
1184           }
1185         }
1186       }
1187     if (c != (unsigned char) ',')
1188       continue;
1189     if (image_count != 0)
1190       {
1191         /*
1192           Allocate next image structure.
1193         */
1194         AcquireNextImage(image_info,image);
1195         if (GetNextImageInList(image) == (Image *) NULL)
1196           {
1197             image=DestroyImageList(image);
1198             global_colormap=(unsigned char *) RelinquishMagickMemory(
1199               global_colormap);
1200             return((Image *) NULL);
1201           }
1202         image=SyncNextImageInList(image);
1203       }
1204     image_count++;
1205     /*
1206       Read image attributes.
1207     */
1208     image->storage_class=PseudoClass;
1209     image->compression=LZWCompression;
1210     page.x=(ssize_t) ReadBlobLSBShort(image);
1211     page.y=(ssize_t) ReadBlobLSBShort(image);
1212     image->columns=ReadBlobLSBShort(image);
1213     image->rows=ReadBlobLSBShort(image);
1214     image->depth=8;
1215     flag=(unsigned char) ReadBlobByte(image);
1216     image->interlace=BitSet((int) flag,0x40) != 0 ? GIFInterlace :
1217       NoInterlace;
1218     image->colors=BitSet((int) flag,0x80) == 0 ? global_colors :
1219       1UL << ((size_t) (flag & 0x07)+1);
1220     if (opacity >= (ssize_t) image->colors)
1221       opacity=(-1);
1222     image->page.width=page.width;
1223     image->page.height=page.height;
1224     image->page.y=page.y;
1225     image->page.x=page.x;
1226     image->delay=delay;
1227     image->ticks_per_second=100;
1228     image->dispose=(DisposeType) dispose;
1229     image->iterations=iterations;
1230     image->matte=opacity >= 0 ? MagickTrue : MagickFalse;
1231     delay=0;
1232     dispose=0;
1233     iterations=1;
1234     if ((image->columns == 0) || (image->rows == 0))
1235       {
1236         global_colormap=(unsigned char *) RelinquishMagickMemory(
1237           global_colormap);
1238         ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
1239       }
1240     /*
1241       Inititialize colormap.
1242     */
1243     if (AcquireImageColormap(image,image->colors) == MagickFalse)
1244       {
1245         global_colormap=(unsigned char *) RelinquishMagickMemory(
1246           global_colormap);
1247         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1248       }
1249     if (BitSet((int) flag,0x80) == 0)
1250       {
1251         /*
1252           Use global colormap.
1253         */
1254         p=global_colormap;
1255         for (i=0; i < (ssize_t) image->colors; i++)
1256         {
1257           image->colormap[i].red=ScaleCharToQuantum(*p++);
1258           image->colormap[i].green=ScaleCharToQuantum(*p++);
1259           image->colormap[i].blue=ScaleCharToQuantum(*p++);
1260           if (i == opacity)
1261             {
1262               image->colormap[i].opacity=(Quantum) TransparentOpacity;
1263               image->transparent_color=image->colormap[opacity];
1264             }
1265         }
1266         image->background_color=image->colormap[MagickMin(background,
1267           image->colors-1)];
1268       }
1269     else
1270       {
1271         unsigned char
1272           *colormap;
1273
1274         /*
1275           Read local colormap.
1276         */
1277         colormap=(unsigned char *) AcquireQuantumMemory(image->colors,
1278           3*sizeof(*colormap));
1279         if (colormap == (unsigned char *) NULL)
1280           {
1281             global_colormap=(unsigned char *) RelinquishMagickMemory(
1282               global_colormap);
1283             ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1284           }
1285         count=ReadBlob(image,(3*image->colors)*sizeof(*colormap),colormap);
1286         if (count != (ssize_t) (3*image->colors))
1287           {
1288             global_colormap=(unsigned char *) RelinquishMagickMemory(
1289               global_colormap);
1290             colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1291             ThrowReaderException(CorruptImageError,
1292               "InsufficientImageDataInFile");
1293           }
1294         p=colormap;
1295         for (i=0; i < (ssize_t) image->colors; i++)
1296         {
1297           image->colormap[i].red=ScaleCharToQuantum(*p++);
1298           image->colormap[i].green=ScaleCharToQuantum(*p++);
1299           image->colormap[i].blue=ScaleCharToQuantum(*p++);
1300           if (i == opacity)
1301             image->colormap[i].opacity=(Quantum) TransparentOpacity;
1302         }
1303         colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1304       }
1305     if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
1306       if (image->scene >= (image_info->scene+image_info->number_scenes-1))
1307         break;
1308     /*
1309       Decode image.
1310     */
1311     if (image_info->ping != MagickFalse)
1312       status=PingGIFImage(image);
1313     else
1314       status=DecodeImage(image,opacity);
1315     if ((image_info->ping == MagickFalse) && (status == MagickFalse))
1316       {
1317         global_colormap=(unsigned char *) RelinquishMagickMemory(
1318           global_colormap);
1319         ThrowReaderException(CorruptImageError,"CorruptImage");
1320       }
1321     if (image_info->number_scenes != 0)
1322       if (image->scene >= (image_info->scene+image_info->number_scenes-1))
1323         break;
1324     opacity=(-1);
1325     status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) image->scene-
1326       1,image->scene);
1327     if (status == MagickFalse)
1328       break;
1329   }
1330   global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap);
1331   if ((image->columns == 0) || (image->rows == 0))
1332     ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
1333   (void) CloseBlob(image);
1334   return(GetFirstImageInList(image));
1335 }
1336 \f
1337 /*
1338 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1339 %                                                                             %
1340 %                                                                             %
1341 %                                                                             %
1342 %   R e g i s t e r G I F I m a g e                                           %
1343 %                                                                             %
1344 %                                                                             %
1345 %                                                                             %
1346 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1347 %
1348 %  RegisterGIFImage() adds properties for the GIF image format to
1349 %  the list of supported formats.  The properties include the image format
1350 %  tag, a method to read and/or write the format, whether the format
1351 %  supports the saving of more than one frame to the same file or blob,
1352 %  whether the format supports native in-memory I/O, and a brief
1353 %  description of the format.
1354 %
1355 %  The format of the RegisterGIFImage method is:
1356 %
1357 %      size_t RegisterGIFImage(void)
1358 %
1359 */
1360 ModuleExport size_t RegisterGIFImage(void)
1361 {
1362   MagickInfo
1363     *entry;
1364
1365   entry=SetMagickInfo("GIF");
1366   entry->decoder=(DecodeImageHandler *) ReadGIFImage;
1367   entry->encoder=(EncodeImageHandler *) WriteGIFImage;
1368   entry->magick=(IsImageFormatHandler *) IsGIF;
1369   entry->description=ConstantString("CompuServe graphics interchange format");
1370   entry->module=ConstantString("GIF");
1371   (void) RegisterMagickInfo(entry);
1372   entry=SetMagickInfo("GIF87");
1373   entry->decoder=(DecodeImageHandler *) ReadGIFImage;
1374   entry->encoder=(EncodeImageHandler *) WriteGIFImage;
1375   entry->magick=(IsImageFormatHandler *) IsGIF;
1376   entry->adjoin=MagickFalse;
1377   entry->description=ConstantString("CompuServe graphics interchange format");
1378   entry->version=ConstantString("version 87a");
1379   entry->module=ConstantString("GIF");
1380   (void) RegisterMagickInfo(entry);
1381   return(MagickImageCoderSignature);
1382 }
1383 \f
1384 /*
1385 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1386 %                                                                             %
1387 %                                                                             %
1388 %                                                                             %
1389 %   U n r e g i s t e r G I F I m a g e                                       %
1390 %                                                                             %
1391 %                                                                             %
1392 %                                                                             %
1393 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1394 %
1395 %  UnregisterGIFImage() removes format registrations made by the
1396 %  GIF module from the list of supported formats.
1397 %
1398 %  The format of the UnregisterGIFImage method is:
1399 %
1400 %      UnregisterGIFImage(void)
1401 %
1402 */
1403 ModuleExport void UnregisterGIFImage(void)
1404 {
1405   (void) UnregisterMagickInfo("GIF");
1406   (void) UnregisterMagickInfo("GIF87");
1407 }
1408 \f
1409 /*
1410 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1411 %                                                                             %
1412 %                                                                             %
1413 %                                                                             %
1414 %   W r i t e G I F I m a g e                                                 %
1415 %                                                                             %
1416 %                                                                             %
1417 %                                                                             %
1418 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1419 %
1420 %  WriteGIFImage() writes an image to a file in the Compuserve Graphics
1421 %  image format.
1422 %
1423 %  The format of the WriteGIFImage method is:
1424 %
1425 %      MagickBooleanType WriteGIFImage(const ImageInfo *image_info,Image *image)
1426 %
1427 %  A description of each parameter follows.
1428 %
1429 %    o image_info: the image info.
1430 %
1431 %    o image:  The image.
1432 %
1433 */
1434 static MagickBooleanType WriteGIFImage(const ImageInfo *image_info,Image *image)
1435 {
1436   Image
1437     *next_image;
1438
1439   int
1440     c;
1441
1442   ssize_t
1443     j,
1444     opacity;
1445
1446   ImageInfo
1447     *write_info;
1448
1449   InterlaceType
1450     interlace;
1451
1452   MagickBooleanType
1453     status;
1454
1455   MagickOffsetType
1456     scene;
1457
1458   RectangleInfo
1459     page;
1460
1461   register ssize_t
1462     i;
1463
1464   register unsigned char
1465     *q;
1466
1467   size_t
1468     length;
1469
1470   unsigned char
1471     *colormap,
1472     *global_colormap;
1473
1474   size_t
1475     bits_per_pixel,
1476     delay;
1477
1478   /*
1479     Open output image file.
1480   */
1481   assert(image_info != (const ImageInfo *) NULL);
1482   assert(image_info->signature == MagickSignature);
1483   assert(image != (Image *) NULL);
1484   assert(image->signature == MagickSignature);
1485   if (image->debug != MagickFalse)
1486     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1487   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
1488   if (status == MagickFalse)
1489     return(status);
1490   /*
1491     Allocate colormap.
1492   */
1493   global_colormap=(unsigned char *) AcquireQuantumMemory(768UL,
1494     sizeof(*global_colormap));
1495   colormap=(unsigned char *) AcquireQuantumMemory(768UL,sizeof(*colormap));
1496   if ((global_colormap == (unsigned char *) NULL) ||
1497       (colormap == (unsigned char *) NULL))
1498     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1499   for (i=0; i < 768; i++)
1500     colormap[i]=(unsigned char) 0;
1501   /*
1502     Write GIF header.
1503   */
1504   write_info=CloneImageInfo(image_info);
1505   if (LocaleCompare(write_info->magick,"GIF87") != 0)
1506     (void) WriteBlob(image,6,(unsigned char *) "GIF89a");
1507   else
1508     {
1509       (void) WriteBlob(image,6,(unsigned char *) "GIF87a");
1510       write_info->adjoin=MagickFalse;
1511     }
1512   /*
1513     Determine image bounding box.
1514   */
1515   page.width=image->columns;
1516   page.height=image->rows;
1517   page.x=0;
1518   page.y=0;
1519   if (write_info->adjoin != MagickFalse)
1520     for (next_image=image; next_image != (Image *) NULL; )
1521     {
1522       page.x=next_image->page.x;
1523       page.y=next_image->page.y;
1524       if ((next_image->page.width+page.x) > page.width)
1525         page.width=next_image->page.width+page.x;
1526       if ((next_image->page.height+page.y) > page.height)
1527         page.height=next_image->page.height+page.y;
1528       next_image=GetNextImageInList(next_image);
1529     }
1530   page.x=image->page.x;
1531   page.y=image->page.y;
1532   if ((image->page.width != 0) && (image->page.height != 0))
1533     page=image->page;
1534   (void) WriteBlobLSBShort(image,(unsigned short) page.width);
1535   (void) WriteBlobLSBShort(image,(unsigned short) page.height);
1536   /*
1537     Write images to file.
1538   */
1539   interlace=write_info->interlace;
1540   if ((write_info->adjoin != MagickFalse) &&
1541       (GetNextImageInList(image) != (Image *) NULL))
1542     interlace=NoInterlace;
1543   scene=0;
1544   do
1545   {
1546     if (image->colorspace != RGBColorspace)
1547       (void) TransformImageColorspace(image,RGBColorspace);
1548     opacity=(-1);
1549     if (IsOpaqueImage(image,&image->exception) != MagickFalse)
1550       {
1551         if ((image->storage_class == DirectClass) || (image->colors > 256))
1552           (void) SetImageType(image,PaletteType);
1553       }
1554     else
1555       {
1556         MagickRealType
1557           alpha,
1558           beta;
1559
1560         /*
1561           Identify transparent colormap index.
1562         */
1563         if ((image->storage_class == DirectClass) || (image->colors > 256))
1564           (void) SetImageType(image,PaletteBilevelMatteType);
1565         for (i=0; i < (ssize_t) image->colors; i++)
1566           if (image->colormap[i].opacity != OpaqueOpacity)
1567             {
1568               if (opacity < 0)
1569                 {
1570                   opacity=i;
1571                   continue;
1572                 }
1573               alpha=(MagickRealType) TransparentOpacity-(MagickRealType)
1574                 image->colormap[i].opacity;
1575               beta=(MagickRealType) TransparentOpacity-(MagickRealType)
1576                 image->colormap[opacity].opacity;
1577               if (alpha < beta)
1578                 opacity=i;
1579             }
1580         if (opacity == -1)
1581           {
1582             (void) SetImageType(image,PaletteBilevelMatteType);
1583             for (i=0; i < (ssize_t) image->colors; i++)
1584               if (image->colormap[i].opacity != OpaqueOpacity)
1585                 {
1586                   if (opacity < 0)
1587                     {
1588                       opacity=i;
1589                       continue;
1590                     }
1591                   alpha=(Quantum) TransparentOpacity-(MagickRealType)
1592                     image->colormap[i].opacity;
1593                   beta=(Quantum) TransparentOpacity-(MagickRealType)
1594                     image->colormap[opacity].opacity;
1595                   if (alpha < beta)
1596                     opacity=i;
1597                 }
1598           }
1599         if (opacity >= 0)
1600           {
1601             image->colormap[opacity].red=image->transparent_color.red;
1602             image->colormap[opacity].green=image->transparent_color.green;
1603             image->colormap[opacity].blue=image->transparent_color.blue;
1604           }
1605       }
1606     if ((image->storage_class == DirectClass) || (image->colors > 256))
1607       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1608     for (bits_per_pixel=1; bits_per_pixel < 8; bits_per_pixel++)
1609       if ((1UL << bits_per_pixel) >= image->colors)
1610         break;
1611     q=colormap;
1612     for (i=0; i < (ssize_t) image->colors; i++)
1613     {
1614       *q++=ScaleQuantumToChar(image->colormap[i].red);
1615       *q++=ScaleQuantumToChar(image->colormap[i].green);
1616       *q++=ScaleQuantumToChar(image->colormap[i].blue);
1617     }
1618     for ( ; i < (ssize_t) (1UL << bits_per_pixel); i++)
1619     {
1620       *q++=(unsigned char) 0x0;
1621       *q++=(unsigned char) 0x0;
1622       *q++=(unsigned char) 0x0;
1623     }
1624     if ((GetPreviousImageInList(image) == (Image *) NULL) ||
1625         (write_info->adjoin == MagickFalse))
1626       {
1627         /*
1628           Write global colormap.
1629         */
1630         c=0x80;
1631         c|=(8-1) << 4;  /* color resolution */
1632         c|=(bits_per_pixel-1);   /* size of global colormap */
1633         (void) WriteBlobByte(image,(unsigned char) c);
1634         for (j=0; j < (ssize_t) image->colors; j++)
1635           if (IsColorEqual(&image->background_color,image->colormap+j))
1636             break;
1637         (void) WriteBlobByte(image,(unsigned char)
1638           (j == (ssize_t) image->colors ? 0 : j));  /* background color */
1639         (void) WriteBlobByte(image,(unsigned char) 0x00);  /* reserved */
1640         length=(size_t) (3*(1UL << bits_per_pixel));
1641         (void) WriteBlob(image,length,colormap);
1642         for (j=0; j < 768; j++)
1643           global_colormap[j]=colormap[j];
1644       }
1645     if (LocaleCompare(write_info->magick,"GIF87") != 0)
1646       {
1647         /*
1648           Write graphics control extension.
1649         */
1650         (void) WriteBlobByte(image,(unsigned char) 0x21);
1651         (void) WriteBlobByte(image,(unsigned char) 0xf9);
1652         (void) WriteBlobByte(image,(unsigned char) 0x04);
1653         c=image->dispose << 2;
1654         if (opacity >= 0)
1655           c|=0x01;
1656         (void) WriteBlobByte(image,(unsigned char) c);
1657         delay=(size_t) (100*image->delay/MagickMax((size_t)
1658           image->ticks_per_second,1));
1659         (void) WriteBlobLSBShort(image,(unsigned short) delay);
1660         (void) WriteBlobByte(image,(unsigned char) (opacity >= 0 ? opacity :
1661           0));
1662         (void) WriteBlobByte(image,(unsigned char) 0x00);
1663         if ((LocaleCompare(write_info->magick,"GIF87") != 0) &&
1664             (GetImageProperty(image,"comment") != (const char *) NULL))
1665           {
1666             const char
1667               *value;
1668
1669             register const char
1670               *p;
1671
1672             size_t
1673               count;
1674
1675             /*
1676               Write Comment extension.
1677             */
1678             (void) WriteBlobByte(image,(unsigned char) 0x21);
1679             (void) WriteBlobByte(image,(unsigned char) 0xfe);
1680             value=GetImageProperty(image,"comment");
1681             p=value;
1682             while (strlen(p) != 0)
1683             {
1684               count=MagickMin(strlen(p),255);
1685               (void) WriteBlobByte(image,(unsigned char) count);
1686               for (i=0; i < (ssize_t) count; i++)
1687                 (void) WriteBlobByte(image,(unsigned char) *p++);
1688             }
1689             (void) WriteBlobByte(image,(unsigned char) 0x00);
1690           }
1691         if ((GetPreviousImageInList(image) == (Image *) NULL) &&
1692             (GetNextImageInList(image) != (Image *) NULL) &&
1693             (image->iterations != 1))
1694           {
1695             /*
1696               Write Netscape Loop extension.
1697             */
1698             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1699                "  Writing GIF Extension %s","NETSCAPE2.0");
1700             (void) WriteBlobByte(image,(unsigned char) 0x21);
1701             (void) WriteBlobByte(image,(unsigned char) 0xff);
1702             (void) WriteBlobByte(image,(unsigned char) 0x0b);
1703             (void) WriteBlob(image,11,(unsigned char *) "NETSCAPE2.0");
1704             (void) WriteBlobByte(image,(unsigned char) 0x03);
1705             (void) WriteBlobByte(image,(unsigned char) 0x01);
1706             (void) WriteBlobLSBShort(image,(unsigned short) image->iterations);
1707             (void) WriteBlobByte(image,(unsigned char) 0x00);
1708           }
1709         ResetImageProfileIterator(image);
1710         for ( ; ; )
1711         {
1712           char
1713             *name;
1714
1715           const StringInfo
1716             *profile;
1717
1718           name=GetNextImageProfile(image);
1719           if (name == (const char *) NULL)
1720             break;
1721           profile=GetImageProfile(image,name);
1722           if (profile != (StringInfo *) NULL)
1723           {
1724             if ((LocaleCompare(name,"ICC") == 0) ||
1725                 (LocaleCompare(name,"ICM") == 0) ||
1726                 (LocaleCompare(name,"IPTC") == 0) ||
1727                 (LocaleCompare(name,"8BIM") == 0) ||
1728                 (LocaleNCompare(name,"gif:",4) == 0))
1729             {
1730                size_t
1731                  length;
1732
1733                ssize_t
1734                  offset;
1735
1736                unsigned char
1737                  *datum;
1738
1739                datum=GetStringInfoDatum(profile);
1740                length=GetStringInfoLength(profile);
1741                (void) WriteBlobByte(image,(unsigned char) 0x21);
1742                (void) WriteBlobByte(image,(unsigned char) 0xff);
1743                (void) WriteBlobByte(image,(unsigned char) 0x0b);
1744                if ((LocaleCompare(name,"ICC") == 0) ||
1745                    (LocaleCompare(name,"ICM") == 0))
1746                  {
1747                    /*
1748                      Write ICC extension.
1749                    */
1750                    (void) WriteBlob(image,11,(unsigned char *)"ICCRGBG1012");
1751                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1752                        "  Writing GIF Extension %s","ICCRGBG1012");
1753                  }
1754                else
1755                  if ((LocaleCompare(name,"IPTC") == 0))
1756                    {
1757                      /*
1758                        write IPTC extension.
1759                      */
1760                      (void) WriteBlob(image,11,(unsigned char *)"MGKIPTC0000");
1761                      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1762                          "  Writing GIF Extension %s","MGKIPTC0000");
1763                    }
1764                  else
1765                    if ((LocaleCompare(name,"8BIM") == 0))
1766                      {
1767                        /*
1768                          Write 8BIM extension>
1769                        */
1770                         (void) WriteBlob(image,11,(unsigned char *)
1771                           "MGK8BIM0000");
1772                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1773                           "  Writing GIF Extension %s","MGK8BIM0000");
1774                      }
1775                    else
1776                      {
1777                        char
1778                          extension[MaxTextExtent];
1779
1780                        /* write generic extension */
1781                        (void) CopyMagickString(extension,name+4,
1782                          sizeof(extension));
1783                        (void) WriteBlob(image,11,(unsigned char *) extension);
1784                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1785                           "  Writing GIF Extension %s",name);
1786                      }
1787                offset=0;
1788                while ((ssize_t) length > offset)
1789                {
1790                  size_t
1791                    block_length;
1792
1793                  if ((length-offset) < 255)
1794                    block_length=length-offset;
1795                  else
1796                    block_length=255;
1797                  (void) WriteBlobByte(image,(unsigned char) block_length);
1798                  (void) WriteBlob(image,(size_t) block_length,datum+offset);
1799                  offset+=(ssize_t) block_length;
1800                }
1801                (void) WriteBlobByte(image,(unsigned char) 0x00);
1802             }
1803           }
1804         }
1805       }
1806     (void) WriteBlobByte(image,',');  /* image separator */
1807     /*
1808       Write the image header.
1809     */
1810     page.x=image->page.x;
1811     page.y=image->page.y;
1812     if ((image->page.width != 0) && (image->page.height != 0))
1813       page=image->page;
1814     (void) WriteBlobLSBShort(image,(unsigned short) (page.x < 0 ? 0 : page.x));
1815     (void) WriteBlobLSBShort(image,(unsigned short) (page.y < 0 ? 0 : page.y));
1816     (void) WriteBlobLSBShort(image,(unsigned short) image->columns);
1817     (void) WriteBlobLSBShort(image,(unsigned short) image->rows);
1818     c=0x00;
1819     if (interlace != NoInterlace)
1820       c|=0x40;  /* pixel data is interlaced */
1821     for (j=0; j < (ssize_t) (3*image->colors); j++)
1822       if (colormap[j] != global_colormap[j])
1823         break;
1824     if (j == (ssize_t) (3*image->colors))
1825       (void) WriteBlobByte(image,(unsigned char) c);
1826     else
1827       {
1828         c|=0x80;
1829         c|=(bits_per_pixel-1);   /* size of local colormap */
1830         (void) WriteBlobByte(image,(unsigned char) c);
1831         length=(size_t) (3*(1UL << bits_per_pixel));
1832         (void) WriteBlob(image,length,colormap);
1833       }
1834     /*
1835       Write the image data.
1836     */
1837     c=(int) MagickMax(bits_per_pixel,2);
1838     (void) WriteBlobByte(image,(unsigned char) c);
1839     status=EncodeImage(write_info,image,(size_t)
1840       MagickMax(bits_per_pixel,2)+1);
1841     if (status == MagickFalse)
1842       {
1843         global_colormap=(unsigned char *) RelinquishMagickMemory(
1844           global_colormap);
1845         colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1846         write_info=DestroyImageInfo(write_info);
1847         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1848       }
1849     (void) WriteBlobByte(image,(unsigned char) 0x00);
1850     if (GetNextImageInList(image) == (Image *) NULL)
1851       break;
1852     image=SyncNextImageInList(image);
1853     scene++;
1854     status=SetImageProgress(image,SaveImagesTag,scene,
1855       GetImageListLength(image));
1856     if (status == MagickFalse)
1857       break;
1858   } while (write_info->adjoin != MagickFalse);
1859   (void) WriteBlobByte(image,';'); /* terminator */
1860   global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap);
1861   colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1862   write_info=DestroyImageInfo(write_info);
1863   (void) CloseBlob(image);
1864   return(MagickTrue);
1865 }