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