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