Coverage Report

Created: 2025-08-11 08:01

/src/graphicsmagick/coders/gif.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
% Copyright (C) 2003-2021 GraphicsMagick Group
3
% Copyright (C) 2002 ImageMagick Studio
4
% Copyright 1991-1999 E. I. du Pont de Nemours and Company
5
%
6
% This program is covered by multiple licenses, which are described in
7
% Copyright.txt. You should have received a copy of Copyright.txt with this
8
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
9
%
10
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11
%                                                                             %
12
%                                                                             %
13
%                                                                             %
14
%                             GGGG  IIIII  FFFFF                              %
15
%                            G        I    F                                  %
16
%                            G  GG    I    FFF                                %
17
%                            G   G    I    F                                  %
18
%                             GGG   IIIII  F                                  %
19
%                                                                             %
20
%                                                                             %
21
%            Read/Write Compuserv Graphics Interchange Format.                %
22
%                                                                             %
23
%                                                                             %
24
%                              Software Design                                %
25
%                                John Cristy                                  %
26
%                                 July 1992                                   %
27
%                                                                             %
28
%                                                                             %
29
%                                                                             %
30
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
31
%
32
%
33
*/
34

35
/*
36
  Include declarations.
37
*/
38
#include "magick/studio.h"
39
#include "magick/attribute.h"
40
#include "magick/blob.h"
41
#include "magick/color.h"
42
#include "magick/colormap.h"
43
#include "magick/enum_strings.h"
44
#include "magick/log.h"
45
#include "magick/magick.h"
46
#include "magick/monitor.h"
47
#include "magick/pixel_cache.h"
48
#include "magick/quantize.h"
49
#include "magick/utility.h"
50

51
/*
52
  Forward declarations.
53
*/
54
static size_t
55
  ReadBlobBlock(Image *,unsigned char *);
56
57
static unsigned int
58
  WriteGIFImage(const ImageInfo *,Image *);
59

60
/*
61
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
62
%                                                                             %
63
%                                                                             %
64
%                                                                             %
65
%   D e c o d e I m a g e                                                     %
66
%                                                                             %
67
%                                                                             %
68
%                                                                             %
69
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
70
%
71
%  Method DecodeImage uncompresses an image via GIF-coding.
72
%
73
%  The format of the DecodeImage method is:
74
%
75
%      MagickPassFail DecodeImage(Image *image,const long opacity)
76
%
77
%  A description of each parameter follows:
78
%
79
%    o status:  Method DecodeImage returns MagickPass if all the pixels are
80
%      uncompressed without error, otherwise MagickFail.
81
%
82
%    o image: The address of a structure of type Image.
83
%
84
%    o opacity:  The colormap index associated with the transparent
85
%      color.
86
%
87
%
88
*/
89
53.3M
#define MaxStackSize  4096
90
8.29M
#define NullCode (~0U)
91
static MagickPassFail DecodeImage(Image *image,const long opacity)
92
3.13k
{
93
3.13k
  int
94
3.13k
    bits,
95
3.13k
    code_size;
96
97
3.13k
  unsigned int
98
3.13k
    available,
99
3.13k
    clear,
100
3.13k
    code,
101
3.13k
    code_mask,
102
3.13k
    end_of_information,
103
3.13k
    in_code,
104
3.13k
    old_code,
105
3.13k
    pass;
106
107
3.13k
  unsigned long
108
3.13k
    y,
109
3.13k
    offset;
110
111
3.13k
  register IndexPacket
112
3.13k
    *indexes;
113
114
3.13k
  register unsigned long
115
3.13k
    x;
116
117
3.13k
  register PixelPacket
118
3.13k
    *q;
119
120
3.13k
  register unsigned char
121
3.13k
    *c;
122
123
3.13k
  register unsigned int
124
3.13k
    datum;
125
126
3.13k
  size_t
127
3.13k
    count;
128
129
3.13k
  unsigned short
130
3.13k
    *prefix;
131
132
3.13k
  unsigned char
133
3.13k
    data_size,
134
3.13k
    first,
135
3.13k
    index,
136
3.13k
    *packet,
137
3.13k
    *pixel_stack,
138
3.13k
    *suffix,
139
3.13k
    *top_stack;
140
141
3.13k
  MagickPassFail
142
3.13k
    status=MagickPass;
143
144
3.13k
  assert(image != (Image *) NULL);
145
146
3.13k
  data_size=ReadBlobByte(image);
147
3.13k
  if (data_size > 8U) /* 256 */
148
2.92k
    ThrowBinaryException(CorruptImageError,CorruptImage,image->filename);
149
  /*
150
    Allocate decoder tables.
151
  */
152
2.92k
  packet=MagickAllocateClearedMemory(unsigned char *,256);
153
2.92k
  prefix=MagickAllocateClearedArray(unsigned short *,MaxStackSize,sizeof(short));
154
2.92k
  suffix=MagickAllocateClearedMemory(unsigned char *,MaxStackSize);
155
2.92k
  pixel_stack=MagickAllocateClearedMemory(unsigned char *,MaxStackSize+1);
156
2.92k
  if ((packet == (unsigned char *) NULL) ||
157
2.92k
      (prefix == (unsigned short *) NULL) ||
158
2.92k
      (suffix == (unsigned char *) NULL) ||
159
2.92k
      (pixel_stack == (unsigned char *) NULL))
160
0
    {
161
0
      MagickFreeMemory(pixel_stack);
162
0
      MagickFreeMemory(suffix);
163
0
      MagickFreeMemory(prefix);
164
0
      MagickFreeMemory(packet);
165
0
      ThrowBinaryException(ResourceLimitError,MemoryAllocationFailed,
166
0
                           image->filename);
167
0
    }
168
  /*
169
    Initialize GIF data stream decoder.
170
  */
171
2.92k
  clear=1U << data_size;
172
2.92k
  end_of_information=clear+1;
173
2.92k
  available=clear+2;
174
2.92k
  old_code=NullCode;
175
2.92k
  code_size=data_size+1;
176
2.92k
  code_mask=(1U << code_size)-1;
177
138k
  for (code=0; code < clear; code++)
178
135k
    {
179
135k
      prefix[code]=0;
180
135k
      suffix[code]=(unsigned char) code;
181
135k
    }
182
  /*
183
    Decode GIF pixel stream.
184
  */
185
2.92k
  datum=0;
186
2.92k
  bits=0;
187
2.92k
  c=0;
188
2.92k
  count=0;
189
2.92k
  first=0;
190
2.92k
  offset=0;
191
2.92k
  pass=0;
192
2.92k
  top_stack=pixel_stack;
193
542k
  for (y=0; y < image->rows; y++)
194
540k
    {
195
540k
      q=SetImagePixels(image,0,offset,image->columns,1);
196
540k
      if (q == (PixelPacket *) NULL)
197
95
        {
198
95
          status=MagickFail;
199
95
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
200
95
                                "Breaking");
201
95
          break;
202
95
        }
203
540k
      indexes=AccessMutableIndexes(image);
204
43.3M
      for (x=0; x < image->columns; )
205
42.8M
        {
206
42.8M
          if (top_stack == pixel_stack)
207
19.9M
            {
208
19.9M
              if (bits < code_size)
209
11.6M
                {
210
                  /*
211
                    Load bytes until there is enough bits for a code.
212
                  */
213
11.6M
                  if (count == 0)
214
64.7k
                    {
215
                      /*
216
                        Read a new data block.
217
                      */
218
64.7k
                      count=ReadBlobBlock(image,packet);
219
64.7k
                      if (count == 0)
220
469
                        break;
221
64.2k
                      c=packet;
222
64.2k
                    }
223
11.6M
                  datum+=((unsigned int) *c) << bits;
224
11.6M
                  bits+=8;
225
11.6M
                  c++;
226
11.6M
                  count--;
227
11.6M
                  continue;
228
11.6M
                }
229
              /*
230
                Get the next code.
231
              */
232
8.28M
              code=datum & code_mask;
233
8.28M
              datum>>=code_size;
234
8.28M
              bits-=code_size;
235
              /*
236
                Interpret the code
237
              */
238
8.28M
              if (code == end_of_information)
239
9
                break;
240
8.28M
              if (code == clear)
241
7.04k
                {
242
                  /*
243
                    Reset decoder.
244
                  */
245
7.04k
                  code_size=data_size+1;
246
7.04k
                  code_mask=(1U << code_size)-1;
247
7.04k
                  available=clear+2;
248
7.04k
                  old_code=NullCode;
249
7.04k
                  continue;
250
7.04k
                }
251
8.28M
              if (old_code == NullCode)
252
6.77k
                {
253
6.77k
                  *top_stack++=suffix[code];
254
6.77k
                  old_code=code;
255
6.77k
                  first=(unsigned char) code;
256
6.77k
                  continue;
257
6.77k
                }
258
8.27M
              in_code=code;
259
8.27M
              if (code >= available)
260
383k
                {
261
383k
                  *top_stack++=first;
262
383k
                  code=old_code;
263
383k
                }
264
30.7M
              while (code >= clear)
265
22.5M
                {
266
22.5M
                  if (code >= MaxStackSize)
267
6
                    {
268
6
                      if (image->logging)
269
6
                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
270
6
                                              "Suffix index (%u) is outside of"
271
6
                                              " bounds (0-%u)", code, MaxStackSize);
272
6
                      status=MagickFail;
273
6
                      break;
274
6
                    }
275
22.5M
                  if ((top_stack-pixel_stack) >= MaxStackSize)
276
9
                    {
277
9
                      if (image->logging)
278
9
                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
279
9
                                              "Attempted overrun of pixel stack bounds");
280
9
                      status=MagickFail;
281
9
                      break;
282
9
                    }
283
22.5M
                  *top_stack++=suffix[code];
284
22.5M
                  code=prefix[code];
285
22.5M
                }
286
8.27M
              if (status == MagickFail)
287
15
                break;
288
8.27M
              first=suffix[code];
289
              /*
290
                Add a new string to the string table,
291
              */
292
8.27M
              if (available < MaxStackSize)
293
6.99M
                {
294
6.99M
                  prefix[available]=(short) old_code;
295
6.99M
                  suffix[available]=first;
296
6.99M
                  available++;
297
6.99M
                  if (available >= (code_mask + 1) && code_size < 12)
298
10.7k
                    {
299
10.7k
                      code_size++;
300
10.7k
                      code_mask+=available;
301
10.7k
                    }
302
6.99M
                }
303
8.27M
              *top_stack++=first;
304
8.27M
              old_code=in_code;
305
8.27M
            }
306
31.1M
          if (status == MagickFail)
307
0
            break;
308
          /*
309
            Pop a pixel off the pixel stack.
310
          */
311
31.1M
          top_stack--;
312
31.1M
          index=(*top_stack);
313
31.1M
          VerifyColormapIndex(image,index);
314
31.1M
          indexes[x]=index;
315
31.1M
          *q=image->colormap[index];
316
31.1M
          q->opacity=(Quantum)
317
31.1M
            (index == opacity ? TransparentOpacity : OpaqueOpacity);
318
31.1M
          x++;
319
31.1M
          q++;
320
31.1M
        }
321
540k
      if (image->interlace == NoInterlace)
322
40.4k
        {
323
40.4k
          offset++;
324
40.4k
        }
325
499k
      else
326
499k
        switch (pass)
327
499k
          {
328
67.2k
          case 0:
329
67.2k
          default:
330
67.2k
            {
331
67.2k
              offset+=8;
332
67.2k
              if (offset >= image->rows)
333
830
                {
334
830
                  pass++;
335
830
                  offset=4;
336
830
                }
337
67.2k
              break;
338
67.2k
            }
339
64.1k
          case 1:
340
64.1k
            {
341
64.1k
              offset+=8;
342
64.1k
              if (offset >= image->rows)
343
601
                {
344
601
                  pass++;
345
601
                  offset=2;
346
601
                }
347
64.1k
              break;
348
67.2k
            }
349
125k
          case 2:
350
125k
            {
351
125k
              offset+=4;
352
125k
              if (offset >= image->rows)
353
573
                {
354
573
                  pass++;
355
573
                  offset=1;
356
573
                }
357
125k
              break;
358
67.2k
            }
359
243k
          case 3:
360
243k
            {
361
243k
              offset+=2;
362
243k
              break;
363
67.2k
            }
364
499k
          }
365
540k
      if (!SyncImagePixels(image))
366
0
        {
367
0
          status=MagickFail;
368
0
          if (image->logging)
369
0
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
370
0
                                  "Failed to sync image pixels");
371
0
          break;
372
0
        }
373
540k
      if (x < image->columns)
374
493
        {
375
493
          status=MagickFail;
376
493
          if (image->logging)
377
493
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
378
493
                                  "Quitting, LZW decode."
379
493
                                  " Decoded only %lu columns out of %lu.",
380
493
                                  x, image->columns);
381
493
          break;
382
493
        }
383
539k
      if (image->previous == (Image *) NULL)
384
539k
        if (QuantumTick(y,image->rows))
385
71.5k
          if (!MagickMonitorFormatted(y,image->rows,&image->exception,
386
71.5k
                                      LoadImageText,image->filename,
387
71.5k
                                      image->columns,image->rows))
388
0
            {
389
0
              status=MagickFail;
390
0
              break;
391
0
            }
392
539k
    }
393
2.92k
  MagickFreeMemory(pixel_stack);
394
2.92k
  MagickFreeMemory(suffix);
395
2.92k
  MagickFreeMemory(prefix);
396
2.92k
  MagickFreeMemory(packet);
397
2.92k
  if ((status == MagickFail) || (y < image->rows))
398
588
    {
399
588
      if ((image->logging) && (y < image->rows))
400
588
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
401
588
                              "Decoded only %lu rows out of %lu.",
402
588
                              y, image->rows);
403
588
      if (image->exception.severity < ErrorException)
404
472
        ThrowException(&image->exception,CorruptImageError,CorruptImage,image->filename);
405
588
      return MagickFail;
406
588
    }
407
2.33k
  return(MagickPass);
408
2.92k
}
409

410
/*
411
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
412
%                                                                             %
413
%                                                                             %
414
%                                                                             %
415
%   E n c o d e I m a g e                                                     %
416
%                                                                             %
417
%                                                                             %
418
%                                                                             %
419
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
420
%
421
%  Method EncodeImage compresses an image via GIF-coding.
422
%
423
%  The format of the EncodeImage method is:
424
%
425
%      MagickPassFail EncodeImage(const ImageInfo *image_info,Image *image,
426
%        const unsigned int data_size)
427
%
428
%  A description of each parameter follows:
429
%
430
%    o status:  Method EncodeImage returns MagickPass if all the pixels are
431
%      compressed without error, otherwise MagickFail.
432
%
433
%    o image_info: Specifies a pointer to a ImageInfo structure.
434
%
435
%    o image: The address of a structure of type Image.
436
%
437
%    o data_size:  The number of bits in the compressed packet.
438
%
439
%
440
*/
441
9.59k
#define MaxCode(number_bits)  ((short) ((1U << (number_bits))-1))
442
70.0M
#define MaxHashTable  5003
443
36.2M
#define MaxGIFBits  12
444
8.92M
#define MaxGIFTable  ((short) (1U << MaxGIFBits))
445
446
8.92M
#define GIFOutputCode(code) \
447
8.92M
{ \
448
8.92M
  /*  \
449
8.92M
    Emit a code. \
450
8.92M
  */ \
451
8.92M
  if (bits > 0) \
452
8.92M
    datum|=((unsigned int) code << bits); \
453
8.92M
  else \
454
8.92M
    datum=(unsigned int) code; \
455
8.92M
  bits+=number_bits; \
456
21.4M
  while (bits >= 8) \
457
12.5M
  { \
458
12.5M
    /*  \
459
12.5M
      Add a character to current packet. \
460
12.5M
    */ \
461
12.5M
    packet[byte_count++]=(unsigned char) (datum & 0xff); \
462
12.5M
    if (byte_count >= 254) \
463
12.5M
      { \
464
48.9k
        (void) WriteBlobByte(image,byte_count); \
465
48.9k
        (void) WriteBlob(image,byte_count,(char *) packet); \
466
48.9k
        byte_count=0; \
467
48.9k
      } \
468
12.5M
    datum>>=8; \
469
12.5M
    bits-=8; \
470
12.5M
  } \
471
8.92M
  if (free_code > max_code)  \
472
8.92M
    { \
473
8.67k
      number_bits++; \
474
8.67k
      if (number_bits == MaxGIFBits) \
475
8.67k
        max_code=MaxGIFTable; \
476
8.67k
      else \
477
8.67k
        max_code=MaxCode(number_bits); \
478
8.67k
    } \
479
8.92M
}
480
static MagickPassFail EncodeImage(const ImageInfo *image_info,Image *image,
481
  const unsigned int data_size)
482
1.11k
{
483
484
1.11k
  int
485
1.11k
    displacement,
486
1.11k
    next_pixel,
487
1.11k
    bits,
488
1.11k
    byte_count,
489
1.11k
    k,
490
1.11k
    number_bits,
491
1.11k
    offset,
492
1.11k
    pass;
493
494
1.11k
  long
495
1.11k
    datum,
496
1.11k
    y;
497
498
1.11k
  register const PixelPacket
499
1.11k
    *p;
500
501
1.11k
  register const IndexPacket
502
1.11k
    *indexes;
503
504
1.11k
  register long
505
1.11k
    i,
506
1.11k
    x;
507
508
1.11k
  short
509
1.11k
    clear_code,
510
1.11k
    end_of_information_code,
511
1.11k
    free_code,
512
1.11k
    *hash_code,
513
1.11k
    *hash_prefix,
514
1.11k
    index,
515
1.11k
    max_code,
516
1.11k
    waiting_code;
517
518
1.11k
  unsigned char
519
1.11k
    *packet,
520
1.11k
    *hash_suffix;
521
522
  /*
523
    Allocate encoder tables.
524
  */
525
1.11k
  assert(image != (Image *) NULL);
526
1.11k
  packet=MagickAllocateMemory(unsigned char *,256);
527
1.11k
  hash_code=MagickAllocateArray(short *,MaxHashTable,sizeof(short));
528
1.11k
  hash_prefix=MagickAllocateArray(short *,MaxHashTable,sizeof(short));
529
1.11k
  hash_suffix=MagickAllocateMemory(unsigned char *,MaxHashTable);
530
1.11k
  if ((packet == (unsigned char *) NULL) || (hash_code == (short *) NULL) ||
531
1.11k
      (hash_prefix == (short *) NULL) ||
532
1.11k
      (hash_suffix == (unsigned char *) NULL))
533
0
    {
534
0
      MagickFreeMemory(packet);
535
0
      MagickFreeMemory(hash_code);
536
0
      MagickFreeMemory(hash_prefix);
537
0
      MagickFreeMemory(hash_suffix);
538
0
      return(MagickFail);
539
0
    }
540
  /*
541
    Initialize GIF encoder.
542
  */
543
1.11k
  number_bits=data_size;
544
1.11k
  max_code=MaxCode(number_bits);
545
1.11k
  clear_code=((short) 1U << (data_size-1));
546
1.11k
  end_of_information_code=clear_code+1;
547
1.11k
  free_code=clear_code+2;
548
1.11k
  byte_count=0;
549
1.11k
  datum=0;
550
1.11k
  bits=0;
551
5.57M
  for (i=0; i < MaxHashTable; i++)
552
5.57M
    hash_code[i]=0;
553
1.11k
  GIFOutputCode(clear_code);
554
  /*
555
    Encode pixels.
556
  */
557
1.11k
  offset=0;
558
1.11k
  pass=0;
559
1.11k
  waiting_code=0;
560
511k
  for (y=0; y < (long) image->rows; y++)
561
510k
  {
562
510k
    p=AcquireImagePixels(image,0,offset,image->columns,1,&image->exception);
563
510k
    if (p == (const PixelPacket *) NULL)
564
0
      break;
565
510k
    indexes=AccessImmutableIndexes(image);
566
510k
    if (y == 0)
567
1.11k
      waiting_code=(*indexes);
568
27.8M
    for (x=(y == 0) ? 1 : 0; x < (long) image->columns; x++)
569
27.3M
    {
570
      /*
571
        Probe hash table.
572
      */
573
27.3M
      index=indexes[x] & 0xff;
574
27.3M
      p++;
575
27.3M
      k=(int) ((unsigned int) index << (MaxGIFBits-8))+waiting_code;
576
27.3M
      if (k >= MaxHashTable)
577
1.32M
        k-=MaxHashTable;
578
27.3M
      next_pixel=False;
579
27.3M
      displacement=1;
580
27.3M
      if ((image_info->compression != NoCompression) && (hash_code[k] > 0))
581
22.2M
        {
582
22.2M
          if ((hash_prefix[k] == waiting_code) && (hash_suffix[k] == index))
583
13.0M
            {
584
13.0M
              waiting_code=hash_code[k];
585
13.0M
              continue;
586
13.0M
            }
587
9.15M
          if (k != 0)
588
9.15M
            displacement=MaxHashTable-k;
589
9.15M
          for ( ; ; )
590
21.9M
          {
591
21.9M
            k-=displacement;
592
21.9M
            if (k < 0)
593
15.9M
              k+=MaxHashTable;
594
21.9M
            if (hash_code[k] == 0)
595
3.84M
              break;
596
18.0M
            if ((hash_prefix[k] == waiting_code) && (hash_suffix[k] == index))
597
5.30M
              {
598
5.30M
                waiting_code=hash_code[k];
599
5.30M
                next_pixel=True;
600
5.30M
                break;
601
5.30M
              }
602
18.0M
          }
603
9.15M
          if (next_pixel == True)
604
5.30M
            continue;
605
9.15M
        }
606
8.92M
      GIFOutputCode(waiting_code);
607
8.92M
      if (free_code < MaxGIFTable)
608
8.92M
        {
609
8.92M
          hash_code[k]=free_code++;
610
8.92M
          hash_prefix[k]=waiting_code;
611
8.92M
          hash_suffix[k]=(unsigned char) index;
612
8.92M
        }
613
2.14k
      else
614
2.14k
        {
615
          /*
616
            Fill the hash table with empty entries.
617
          */
618
10.7M
          for (k=0; k < MaxHashTable; k++)
619
10.7M
            hash_code[k]=0;
620
          /*
621
            Reset compressor and issue a clear code.
622
          */
623
2.14k
          free_code=clear_code+2;
624
2.14k
          GIFOutputCode(clear_code);
625
2.14k
          number_bits=data_size;
626
2.14k
          max_code=MaxCode(number_bits);
627
2.14k
        }
628
8.92M
      waiting_code=index;
629
8.92M
    }
630
510k
    if ((image_info->interlace == NoInterlace) ||
631
510k
        (image_info->interlace == UndefinedInterlace))
632
510k
      offset++;
633
0
    else
634
0
      switch (pass)
635
0
      {
636
0
        case 0:
637
0
        default:
638
0
        {
639
0
          offset+=8;
640
0
          if (offset >= (long) image->rows)
641
0
            {
642
0
              pass++;
643
0
              offset=4;
644
0
            }
645
0
          break;
646
0
        }
647
0
        case 1:
648
0
        {
649
0
          offset+=8;
650
0
          if (offset >= (long) image->rows)
651
0
            {
652
0
              pass++;
653
0
              offset=2;
654
0
            }
655
0
          break;
656
0
        }
657
0
        case 2:
658
0
        {
659
0
          offset+=4;
660
0
          if (offset >= (long) image->rows)
661
0
            {
662
0
              pass++;
663
0
              offset=1;
664
0
            }
665
0
          break;
666
0
        }
667
0
        case 3:
668
0
        {
669
0
          offset+=2;
670
0
          break;
671
0
        }
672
0
      }
673
510k
    if (image->previous == (Image *) NULL)
674
510k
      if (QuantumTick(y,image->rows))
675
61.7k
        if (!MagickMonitorFormatted(y,image->rows,&image->exception,
676
61.7k
                                    SaveImageText,image->filename,
677
61.7k
                                    image->columns,image->rows))
678
0
          break;
679
510k
  }
680
  /*
681
    Flush out the buffered code.
682
  */
683
1.11k
  GIFOutputCode(waiting_code);
684
1.11k
  GIFOutputCode(end_of_information_code);
685
1.11k
  if (bits > 0)
686
790
    {
687
      /*
688
        Add a character to current packet.
689
      */
690
790
      packet[byte_count++]=(unsigned char) (datum & 0xff);
691
790
      if (byte_count >= 254)
692
6
        {
693
6
          (void) WriteBlobByte(image,byte_count);
694
6
          (void) WriteBlob(image,byte_count,(char *) packet);
695
6
          byte_count=0;
696
6
        }
697
790
    }
698
  /*
699
    Flush accumulated data.
700
  */
701
1.11k
  if (byte_count > 0)
702
1.10k
    {
703
1.10k
      (void) WriteBlobByte(image,byte_count);
704
1.10k
      (void) WriteBlob(image,byte_count,(char *) packet);
705
1.10k
    }
706
  /*
707
    Free encoder memory.
708
  */
709
1.11k
  MagickFreeMemory(hash_suffix);
710
1.11k
  MagickFreeMemory(hash_prefix);
711
1.11k
  MagickFreeMemory(hash_code);
712
1.11k
  MagickFreeMemory(packet);
713
1.11k
  return(MagickPass);
714
1.11k
}
715

716
/*
717
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
718
%                                                                             %
719
%                                                                             %
720
%                                                                             %
721
%   I s G I F                                                                 %
722
%                                                                             %
723
%                                                                             %
724
%                                                                             %
725
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
726
%
727
%  Method IsGIF returns MagickTrue if the image format type, identified by
728
%  the magick string, is GIF.
729
%
730
%  The format of the IsGIF method is:
731
%
732
%      MagickBool IsGIF(const unsigned char *magick,const size_t length)
733
%
734
%  A description of each parameter follows:
735
%
736
%    o status:  Method IsGIF returns True if the image format type is GIF.
737
%
738
%    o magick: This string is generally the first few bytes of an image file
739
%      or blob.
740
%
741
%    o length: Specifies the length of the magick string.
742
%
743
%
744
*/
745
static MagickBool IsGIF(const unsigned char *magick,const size_t length)
746
0
{
747
0
  if (length < 4)
748
0
    return(MagickFalse);
749
0
  if (LocaleNCompare((char *) magick,"GIF8",4) == 0)
750
0
    return(MagickTrue);
751
0
  return(MagickFalse);
752
0
}
753

754
/*
755
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
756
%                                                                             %
757
%                                                                             %
758
%                                                                             %
759
+  R e a d B l o b B l o c k                                                  %
760
%                                                                             %
761
%                                                                             %
762
%                                                                             %
763
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
764
%
765
%  Method ReadBlobBlock reads data from the image file and returns it.  The
766
%  amount of data is determined by first reading a count byte.  The number
767
%  of bytes read is returned.
768
%
769
%  The format of the ReadBlobBlock method is:
770
%
771
%      size_t ReadBlobBlock(Image *image,unsigned char *data)
772
%
773
%  A description of each parameter follows:
774
%
775
%    o count:  Method ReadBlobBlock returns the number of bytes read.
776
%
777
%    o image: The image.
778
%
779
%    o data:  Specifies an area to place the information requested from
780
%      the file.
781
%
782
%
783
*/
784
static size_t ReadBlobBlock(Image *image,unsigned char *data)
785
6.02M
{
786
6.02M
  size_t
787
6.02M
    count=0;
788
789
6.02M
  unsigned char
790
6.02M
    block_count;
791
792
6.02M
  assert(image != (Image *) NULL);
793
6.02M
  assert(image->signature == MagickSignature);
794
6.02M
  assert(data != (unsigned char *) NULL);
795
6.02M
  if (ReadBlob(image,1,&block_count) == 1)
796
6.01M
    {
797
6.01M
      if ((count=ReadBlob(image,(size_t) block_count,data)) != (size_t) block_count)
798
618
        count=0;
799
6.01M
    }
800
6.02M
  return count;
801
6.02M
}
802

803
/*
804
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
805
%                                                                             %
806
%                                                                             %
807
%                                                                             %
808
%   R e a d G I F I m a g e                                                   %
809
%                                                                             %
810
%                                                                             %
811
%                                                                             %
812
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
813
%
814
%  Method ReadGIFImage reads a Compuserve Graphics image file and returns it.
815
%  It allocates the memory necessary for the new Image structure and returns a
816
%  pointer to the new image.
817
%
818
%  The format of the ReadGIFImage method is:
819
%
820
%      Image *ReadGIFImage(const ImageInfo *image_info,ExceptionInfo *exception)
821
%
822
%  A description of each parameter follows:
823
%
824
%    o image:  Method ReadGIFImage returns a pointer to the image after
825
%      reading.  A null image is returned if there is a memory shortage or
826
%      an error occurs.
827
%
828
%    o image_info: Specifies a pointer to a ImageInfo structure.
829
%
830
%    o exception: return any errors or warnings in this structure.
831
%
832
%
833
*/
834
16.2k
#define BitSet(byte,bit)  (((byte) & (bit)) == (bit))
835
#define LSBFirstOrder(x,y)  (((y) << 8) | (x))
836
static Image *ReadGIFImage(const ImageInfo *image_info,ExceptionInfo *exception)
837
5.76k
{
838
5.76k
  Image
839
5.76k
    *image;
840
841
5.76k
  int
842
5.76k
    status;
843
844
5.76k
  long
845
5.76k
    opacity;
846
847
5.76k
  RectangleInfo
848
5.76k
    page;
849
850
5.76k
  register unsigned int
851
5.76k
    i;
852
853
5.76k
  register unsigned char
854
5.76k
    *p;
855
856
5.76k
  size_t
857
5.76k
    count;
858
859
5.76k
  unsigned char
860
5.76k
    background,
861
5.76k
    c,
862
5.76k
    flag,
863
5.76k
    *global_colormap,
864
5.76k
    header[MaxTextExtent],
865
5.76k
    magick[12];
866
867
5.76k
  unsigned int
868
5.76k
    global_colors=0,
869
5.76k
    local_colors=0;
870
871
5.76k
  unsigned long
872
5.76k
    delay,
873
5.76k
    dispose,
874
5.76k
    image_count,
875
5.76k
    iterations;
876
877
  /*
878
    Open image file.
879
  */
880
5.76k
  assert(image_info != (const ImageInfo *) NULL);
881
5.76k
  assert(image_info->signature == MagickSignature);
882
5.76k
  assert(exception != (ExceptionInfo *) NULL);
883
5.76k
  assert(exception->signature == MagickSignature);
884
5.76k
  image=AllocateImage(image_info);
885
5.76k
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
886
5.76k
  if (status == MagickFail)
887
5.76k
    ThrowReaderException(FileOpenError,UnableToOpenFile,image);
888
  /*
889
    Determine if this is a GIF file.
890
  */
891
5.76k
  (void) memset(magick,0,sizeof(magick));
892
5.76k
  count=ReadBlob(image,6,(char *) magick);
893
5.76k
  if ((count != 6) || ((LocaleNCompare((char *) magick,"GIF87",5) != 0) &&
894
5.75k
      (LocaleNCompare((char *) magick,"GIF89",5) != 0)))
895
5.63k
    ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
896
5.63k
  if (image->logging)
897
5.63k
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
898
5.63k
                          "Magick: %.6s",magick);
899
5.63k
  global_colormap=(unsigned char *) NULL;
900
5.63k
  page.width=ReadBlobLSBShort(image);
901
5.63k
  page.height=ReadBlobLSBShort(image);
902
5.63k
  if (image->logging)
903
5.63k
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
904
5.63k
                          "Canvas Page: %lux%lu", page.width, page.height);
905
5.63k
  flag=ReadBlobByte(image);
906
5.63k
  background=ReadBlobByte(image);
907
5.63k
  c=ReadBlobByte(image);  /* reserved */
908
5.63k
  global_colors=1 << ((flag & 0x07)+1);
909
5.63k
  global_colormap=MagickAllocateClearedArray(unsigned char *,3U,Max(global_colors,256U));
910
5.63k
  if (global_colormap == (unsigned char *) NULL)
911
5.63k
    ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image);
912
5.63k
  if (image->logging)
913
5.63k
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
914
5.63k
                          "Global Colors: %u", global_colors);
915
5.63k
  if (BitSet(flag,0x80))
916
1.58k
    {
917
1.58k
      if (ReadBlob(image,(size_t) 3*global_colors,(char *) global_colormap) != (size_t) 3U*global_colors)
918
65
        {
919
65
          MagickFreeMemory(global_colormap);
920
65
          ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
921
0
        }
922
1.58k
    }
923
5.57k
  delay=0;
924
5.57k
  dispose=0;
925
5.57k
  iterations=1;
926
5.57k
  opacity=(-1);
927
5.57k
  image_count=0;
928
5.57k
  for ( ; ; )
929
15.9M
  {
930
15.9M
    count=ReadBlob(image,1,(char *) &c);
931
15.9M
    if (count != 1)
932
1.84k
      break;
933
15.9M
    if (c == ';')
934
115
      break;  /* terminator */
935
15.9M
    if (c == '!')
936
2.52M
      {
937
        /*
938
          GIF Extension block.
939
        */
940
2.52M
        count=ReadBlob(image,1,(char *) &c);
941
2.52M
        if (count != 1) {
942
36
          MagickFreeMemory(global_colormap);
943
36
          ThrowReaderException(CorruptImageError,UnableToReadExtensionBlock,
944
36
            image);
945
0
        }
946
2.52M
        switch (c)
947
2.52M
        {
948
13.4k
          case 0xf9:
949
13.4k
          {
950
            /*
951
              Read Graphics Control extension.
952
            */
953
13.4k
            size_t
954
13.4k
              ncount;
955
956
13.4k
            count=0;
957
85.7k
            while ((ncount = ReadBlobBlock(image,header)) != 0)
958
72.2k
              count = ncount;
959
13.4k
            if (count < 4)
960
2.76k
              break;
961
10.6k
            dispose=header[0] >> 2;
962
10.6k
            delay=((unsigned int) header[2] << 8) | header[1];
963
10.6k
            if ((header[0] & 0x01) == 1)
964
7.25k
              opacity=(header[3] & 0xff);
965
10.6k
            break;
966
13.4k
          }
967
2.28M
          case 0xfe:
968
2.28M
          {
969
            /*
970
              Read Comment extension.
971
            */
972
2.28M
            char
973
2.28M
              *comments = (char *) NULL;
974
975
2.28M
            size_t
976
2.28M
              allocation_length=0,
977
2.28M
              offset=0;
978
979
2.28M
            for ( ; ; )
980
5.05M
              {
981
5.05M
                count=ReadBlobBlock(image,header);
982
5.05M
                if (count == 0)
983
2.28M
                  break;
984
2.77M
                header[count]='\0';
985
2.77M
                if (count+offset+1 >= allocation_length)
986
2.41M
                  {
987
2.41M
                    char *comments_new;
988
2.41M
                    allocation_length=allocation_length+count+1;
989
2.41M
                    comments_new=MagickReallocateResourceLimitedMemory(char *,
990
2.41M
                                                                       comments,
991
2.41M
                                                                       allocation_length);
992
2.41M
                    if (comments_new == (char *) NULL)
993
0
                      {
994
0
                        MagickFreeMemory(global_colormap);
995
0
                        MagickFreeResourceLimitedMemory(comments);
996
0
                        ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image);
997
0
                      }
998
2.41M
                    comments=comments_new;
999
2.41M
                    (void) strlcpy(&comments[offset],(char *)header,allocation_length-offset);
1000
2.41M
                  }
1001
2.77M
              }
1002
2.28M
            (void) SetImageAttribute(image,"comment",NULL);
1003
2.28M
            (void) SetImageAttribute(image,"comment",comments);
1004
2.28M
            MagickFreeResourceLimitedMemory(comments);
1005
2.28M
            break;
1006
2.28M
          }
1007
154k
          case 0xff:
1008
154k
          {
1009
154k
            size_t
1010
154k
              ncount;
1011
1012
154k
            int
1013
154k
              loop;
1014
1015
            /*
1016
              Read Netscape Loop extension.
1017
            */
1018
154k
            loop=False;
1019
154k
            count=ReadBlobBlock(image,header);
1020
154k
            if (count >= 11)
1021
145k
              loop=!LocaleNCompare((char *) header,"NETSCAPE2.0",11);
1022
428k
            while ((ncount=ReadBlobBlock(image,header)) != 0)
1023
273k
              {
1024
273k
                count=ncount;
1025
273k
                if (loop && (count >= 3))
1026
35.2k
                  {
1027
35.2k
                    iterations=((unsigned int) header[2] << 8) | header[1];
1028
35.2k
                    if (image->logging)
1029
35.2k
                      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1030
35.2k
                                            "Loop extension with iterations %lu",
1031
35.2k
                                            iterations);
1032
1033
35.2k
                  }
1034
273k
              }
1035
154k
            break;
1036
2.28M
          }
1037
72.5k
          default:
1038
72.5k
          {
1039
230k
            while (ReadBlobBlock(image,header) != 0);
1040
72.5k
            break;
1041
2.28M
          }
1042
2.52M
        }
1043
2.52M
      }
1044
15.9M
    if (c != ',')
1045
15.9M
      continue;
1046
3.57k
    if (image_count != 0)
1047
0
      {
1048
        /*
1049
          Allocate next image structure.
1050
        */
1051
0
        AllocateNextImage(image_info,image);
1052
0
        if (image->next == (Image *) NULL)
1053
0
          {
1054
0
            DestroyImageList(image);
1055
0
            MagickFreeMemory(global_colormap);
1056
0
            return((Image *) NULL);
1057
0
          }
1058
0
        image=SyncNextImageInList(image);
1059
0
        if (!MagickMonitorFormatted(TellBlob(image),GetBlobSize(image),exception,
1060
0
                                    LoadImagesText,image->filename))
1061
0
          break;
1062
0
      }
1063
3.57k
    image_count++;
1064
    /*
1065
      Read image attributes.
1066
    */
1067
3.57k
    image->storage_class=PseudoClass;
1068
3.57k
    image->compression=LZWCompression;
1069
3.57k
    page.x=ReadBlobLSBShort(image);
1070
3.57k
    page.y=ReadBlobLSBShort(image);
1071
3.57k
    image->columns=ReadBlobLSBShort(image);
1072
3.57k
    image->rows=ReadBlobLSBShort(image);
1073
3.57k
    image->depth=8;
1074
3.57k
    flag=ReadBlobByte(image);
1075
3.57k
    image->interlace=BitSet(flag,0x40) ? LineInterlace : NoInterlace;
1076
3.57k
    local_colors=!BitSet(flag,0x80) ? global_colors : 0x01U << ((flag & 0x07)+1);
1077
3.57k
    image->colors=local_colors;
1078
3.57k
    if (opacity == (long) image->colors) /* Add an extra color for transparent black */
1079
61
      image->colors++;
1080
3.51k
    else if (opacity >= (long) image->colors)
1081
42
      opacity=(-1);
1082
3.57k
    image->page.width=page.width;
1083
3.57k
    image->page.height=page.height;
1084
3.57k
    image->page.y=page.y;
1085
3.57k
    image->page.x=page.x;
1086
3.57k
    image->delay=delay;
1087
3.57k
    image->dispose=(DisposeType) dispose;
1088
3.57k
    image->iterations=iterations;
1089
3.57k
    image->matte=opacity >= 0;
1090
3.57k
    delay=0;
1091
3.57k
    dispose=0;
1092
3.57k
    iterations=1;
1093
3.57k
    if ((image->columns == 0) || (image->rows == 0)) {
1094
95
      MagickFreeMemory(global_colormap);
1095
95
      ThrowReaderException(CorruptImageError,NegativeOrZeroImageSize,image);
1096
0
    }
1097
3.48k
    if (image->logging)
1098
3.48k
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1099
3.48k
                            "Image[%lu]: %lux%lu, Page: %lux%lu+%ld+%ld, Colors=%u, "
1100
3.48k
                            " Interlace: %s, Matte: %s, Delay: %lu, Dispose: %s, Iterations: %lu",
1101
3.48k
                            image->scene,
1102
3.48k
                            image->columns, image->rows,
1103
3.48k
                            image->page.width, image->page.height, image->page.x, image->page.y,
1104
3.48k
                            image->colors,
1105
3.48k
                            InterlaceTypeToString(image->interlace),
1106
3.48k
                            image->matte ? "True" : "False",
1107
3.48k
                            image->delay,
1108
3.48k
                            DisposeTypeToString(image->dispose),
1109
3.48k
                            image->iterations);
1110
    /*
1111
      Inititialize colormap.
1112
    */
1113
3.48k
    if (!AllocateImageColormap(image,image->colors)) {
1114
0
      MagickFreeMemory(global_colormap);
1115
0
      ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image);
1116
0
    }
1117
3.48k
    if (!BitSet(flag,0x80))
1118
3.37k
      {
1119
        /*
1120
          Use global colormap.
1121
        */
1122
3.37k
        p=global_colormap;
1123
230k
        for (i=0; i < image->colors; i++)
1124
226k
        {
1125
226k
          VerifyColormapIndex(image, i);
1126
226k
          image->colormap[i].red=ScaleCharToQuantum(*p++);
1127
226k
          image->colormap[i].green=ScaleCharToQuantum(*p++);
1128
226k
          image->colormap[i].blue=ScaleCharToQuantum(*p++);
1129
226k
          if ((long) i == opacity)
1130
291
            image->colormap[i].opacity=(Quantum) TransparentOpacity;
1131
226k
        }
1132
1133
3.37k
        if (image->colors > 0)
1134
3.37k
          image->background_color=
1135
3.37k
            image->colormap[Min(background,image->colors-1)];
1136
3.37k
      }
1137
109
    else
1138
109
      {
1139
109
        unsigned char
1140
109
          *colormap;
1141
1142
        /*
1143
          Read local colormap.
1144
        */
1145
109
        colormap=MagickAllocateClearedArray(unsigned char *,3,image->colors);
1146
109
        if (colormap == (unsigned char *) NULL)
1147
0
          {
1148
0
            MagickFreeMemory(global_colormap);
1149
0
            ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,
1150
0
                                 image);
1151
0
          }
1152
109
        if (ReadBlob(image, (size_t) 3*local_colors,(char *) colormap) != (size_t) 3*local_colors)
1153
69
          {
1154
69
            MagickFreeMemory(global_colormap);
1155
69
            MagickFreeMemory(colormap);
1156
69
            ThrowReaderException(CorruptImageError,InsufficientImageDataInFile,image);
1157
0
          }
1158
40
        p=colormap;
1159
1.98k
        for (i=0; i < image->colors; i++)
1160
1.94k
        {
1161
1.94k
          image->colormap[i].red=ScaleCharToQuantum(*p++);
1162
1.94k
          image->colormap[i].green=ScaleCharToQuantum(*p++);
1163
1.94k
          image->colormap[i].blue=ScaleCharToQuantum(*p++);
1164
1.94k
          if ((long) i == opacity)
1165
19
            image->colormap[i].opacity=(Quantum) TransparentOpacity;
1166
1.94k
        }
1167
40
        MagickFreeMemory(colormap);
1168
40
      }
1169
3.41k
    if (image_info->ping && (image_info->subrange != 0))
1170
0
      if (image->scene >= (image_info->subimage+image_info->subrange-1))
1171
0
        break;
1172
1173
3.41k
    if (CheckImagePixelLimits(image, exception) != MagickPass)
1174
275
      {
1175
275
        MagickFreeMemory(global_colormap);
1176
275
        ThrowReaderException(ResourceLimitError,ImagePixelLimitExceeded,image);
1177
0
      }
1178
1179
    /*
1180
      Decode image.
1181
    */
1182
3.13k
    status=DecodeImage(image,opacity);
1183
3.13k
    if (!image_info->ping && (status == False)) {
1184
802
      MagickFreeMemory(global_colormap);
1185
802
      GetImageException(image,exception);
1186
802
      ThrowReaderException(CorruptImageError,CorruptImage,image);
1187
0
    }
1188
2.33k
    StopTimer(&image->timer);
1189
2.33k
    if (image_info->subrange != 0)
1190
2.33k
      if (image->scene >= (image_info->subimage+image_info->subrange-1))
1191
2.33k
        break;
1192
2.33k
  }
1193
4.29k
  MagickFreeMemory(global_colormap);
1194
4.29k
  if ((image->columns == 0) || (image->rows == 0))
1195
2.33k
    ThrowReaderException(CorruptImageError,NegativeOrZeroImageSize,image);
1196
2.33k
  while (image->previous != (Image *) NULL)
1197
0
    image=image->previous;
1198
2.33k
  CloseBlob(image);
1199
2.33k
  return(image);
1200
4.29k
}
1201

1202
/*
1203
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1204
%                                                                             %
1205
%                                                                             %
1206
%                                                                             %
1207
%   R e g i s t e r G I F I m a g e                                           %
1208
%                                                                             %
1209
%                                                                             %
1210
%                                                                             %
1211
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1212
%
1213
%  Method RegisterGIFImage adds attributes for the GIF image format to
1214
%  the list of supported formats.  The attributes include the image format
1215
%  tag, a method to read and/or write the format, whether the format
1216
%  supports the saving of more than one frame to the same file or blob,
1217
%  whether the format supports native in-memory I/O, and a brief
1218
%  description of the format.
1219
%
1220
%  The format of the RegisterGIFImage method is:
1221
%
1222
%      RegisterGIFImage(void)
1223
%
1224
*/
1225
ModuleExport void RegisterGIFImage(void)
1226
6
{
1227
6
  MagickInfo
1228
6
    *entry;
1229
1230
6
  entry=SetMagickInfo("GIF");
1231
6
  entry->decoder=(DecoderHandler) ReadGIFImage;
1232
6
  entry->encoder=(EncoderHandler) WriteGIFImage;
1233
6
  entry->magick=(MagickHandler) IsGIF;
1234
6
  entry->description="CompuServe graphics interchange format";
1235
6
  entry->version="version 89a";
1236
6
  entry->module="GIF";
1237
6
  entry->coder_class=PrimaryCoderClass;
1238
6
  (void) RegisterMagickInfo(entry);
1239
1240
6
  entry=SetMagickInfo("GIF87");
1241
6
  entry->decoder=(DecoderHandler) ReadGIFImage;
1242
6
  entry->encoder=(EncoderHandler) WriteGIFImage;
1243
6
  entry->magick=(MagickHandler) IsGIF;
1244
6
  entry->adjoin=False;
1245
6
  entry->description="CompuServe graphics interchange format";
1246
6
  entry->version="version 87a";
1247
6
  entry->module="GIF";
1248
6
  entry->coder_class=PrimaryCoderClass;
1249
6
  (void) RegisterMagickInfo(entry);
1250
6
}
1251

1252
/*
1253
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1254
%                                                                             %
1255
%                                                                             %
1256
%                                                                             %
1257
%   U n r e g i s t e r G I F I m a g e                                       %
1258
%                                                                             %
1259
%                                                                             %
1260
%                                                                             %
1261
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1262
%
1263
%  Method UnregisterGIFImage removes format registrations made by the
1264
%  GIF module from the list of supported formats.
1265
%
1266
%  The format of the UnregisterGIFImage method is:
1267
%
1268
%      UnregisterGIFImage(void)
1269
%
1270
*/
1271
ModuleExport void UnregisterGIFImage(void)
1272
0
{
1273
0
  (void) UnregisterMagickInfo("GIF");
1274
0
  (void) UnregisterMagickInfo("GIF87");
1275
0
}
1276

1277
/*
1278
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1279
%                                                                             %
1280
%                                                                             %
1281
%                                                                             %
1282
%   W r i t e G I F I m a g e                                                 %
1283
%                                                                             %
1284
%                                                                             %
1285
%                                                                             %
1286
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1287
%
1288
%  Method WriteGIFImage writes an image to a file in the Compuserve Graphics
1289
%  image format.
1290
%
1291
%  The format of the WriteGIFImage method is:
1292
%
1293
%      MagickPassFail WriteGIFImage(const ImageInfo *image_info,Image *image)
1294
%
1295
%  A description of each parameter follows.
1296
%
1297
%    o status: Method WriteGIFImage return MagickPass if the image is written.
1298
%      MagickFail is returned is there is a memory shortage or if the image file
1299
%      fails to write.
1300
%
1301
%    o image_info: Specifies a pointer to a ImageInfo structure.
1302
%
1303
%    o image:  A pointer to an Image structure.
1304
%
1305
%
1306
*/
1307
static MagickPassFail WriteGIFImage(const ImageInfo *image_info,Image *image)
1308
1.11k
{
1309
1.11k
  Image
1310
1.11k
    *next_image;
1311
1312
1.11k
  int
1313
1.11k
    y;
1314
1315
1.11k
  long
1316
1.11k
    opacity;
1317
1318
1.11k
  QuantizeInfo
1319
1.11k
    quantize_info;
1320
1321
1.11k
  RectangleInfo
1322
1.11k
    page;
1323
1324
1.11k
  register const PixelPacket
1325
1.11k
    *p;
1326
1327
1.11k
  register long
1328
1.11k
    x;
1329
1330
1.11k
  register unsigned int
1331
1.11k
    i;
1332
1333
1.11k
  register unsigned char
1334
1.11k
    *q;
1335
1336
1.11k
  size_t
1337
1.11k
    j;
1338
1339
1.11k
  unsigned char
1340
1.11k
    bits_per_pixel,
1341
1.11k
    c,
1342
1.11k
    *colormap,
1343
1.11k
    *global_colormap;
1344
1345
1.11k
  unsigned int
1346
1.11k
    interlace,
1347
1.11k
    status;
1348
1349
1.11k
  unsigned long
1350
1.11k
    scene;
1351
1352
1.11k
  size_t
1353
1.11k
    image_list_length;
1354
1355
  /*
1356
    Open output image file.
1357
  */
1358
1.11k
  assert(image_info != (const ImageInfo *) NULL);
1359
1.11k
  assert(image_info->signature == MagickSignature);
1360
1.11k
  assert(image != (Image *) NULL);
1361
1.11k
  assert(image->signature == MagickSignature);
1362
1.11k
  image_list_length=GetImageListLength(image);
1363
1.11k
  status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
1364
1.11k
  if (status == MagickFail)
1365
1.11k
    ThrowWriterException(FileOpenError,UnableToOpenFile,image);
1366
  /*
1367
    Determine image bounding box.
1368
  */
1369
1.11k
  page.width=image->columns;
1370
1.11k
  page.height=image->rows;
1371
1.11k
  page.x=0;
1372
1.11k
  page.y=0;
1373
2.22k
  for (next_image=image; next_image != (Image *) NULL; )
1374
1.11k
  {
1375
1.11k
    page.x=next_image->page.x;
1376
1.11k
    page.y=next_image->page.y;
1377
1.11k
    if ((next_image->columns+page.x) > page.width)
1378
1.03k
      page.width=next_image->columns+page.x;
1379
1.11k
    if ((next_image->rows+page.y) > page.height)
1380
1.00k
      page.height=next_image->rows+page.y;
1381
1.11k
    next_image=next_image->next;
1382
1.11k
  }
1383
  /*
1384
    Allocate colormaps.
1385
  */
1386
1.11k
  global_colormap=MagickAllocateMemory(unsigned char *,768);
1387
1.11k
  if (global_colormap == (unsigned char *) NULL)
1388
1.11k
    ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,image);
1389
1.11k
  colormap=MagickAllocateMemory(unsigned char *,768);
1390
1.11k
  if (colormap == (unsigned char *) NULL)
1391
0
    {
1392
0
      MagickFreeMemory(global_colormap);
1393
0
      ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,image);
1394
0
    }
1395
856k
  for (i=0; i < 768; i++)
1396
855k
    colormap[i]=0;
1397
  /*
1398
    Write GIF header.
1399
  */
1400
1.11k
  if ((GetImageAttribute(image,"comment") == (ImageAttribute *) NULL) &&
1401
1.11k
      !image_info->adjoin && !image->matte)
1402
0
    (void) WriteBlob(image,6,"GIF87a");
1403
1.11k
  else
1404
1.11k
    if (LocaleCompare(image_info->magick,"GIF87") == 0)
1405
543
      (void) WriteBlob(image,6,"GIF87a");
1406
571
    else
1407
571
      (void) WriteBlob(image,6,"GIF89a");
1408
1.11k
  page.x=image->page.x;
1409
1.11k
  page.y=image->page.y;
1410
1.11k
  if ((image->page.width != 0) && (image->page.height != 0))
1411
1.11k
    page=image->page;
1412
1.11k
  (void) WriteBlobLSBShort(image,page.width);
1413
1.11k
  (void) WriteBlobLSBShort(image,page.height);
1414
  /*
1415
    Write images to file.
1416
  */
1417
1.11k
  interlace=(image_info->interlace == UndefinedInterlace ? NoInterlace :
1418
1.11k
             image_info->interlace);
1419
1.11k
  if (image_info->adjoin && (image->next != (Image *) NULL))
1420
0
    interlace=NoInterlace;
1421
1.11k
  opacity=(-1);
1422
1.11k
  scene=0;
1423
1.11k
  do
1424
1.11k
  {
1425
1.11k
    (void) TransformColorspace(image,RGBColorspace);
1426
1.11k
    if ((image->storage_class == DirectClass) || (image->colors > 256))
1427
0
      {
1428
        /*
1429
          GIF requires that the image is colormapped.
1430
        */
1431
0
        GetQuantizeInfo(&quantize_info);
1432
0
        quantize_info.dither=image_info->dither;
1433
0
        quantize_info.number_colors=image->matte ? 255 : 256;
1434
0
        (void) QuantizeImage(&quantize_info,image);
1435
0
        if (image->matte)
1436
0
          {
1437
            /*
1438
              Set transparent pixel.
1439
            */
1440
0
            opacity=(long) image->colors++;
1441
0
            MagickReallocMemory(PixelPacket *,image->colormap,
1442
0
              image->colors*sizeof(PixelPacket));
1443
0
            if (image->colormap == (PixelPacket *) NULL)
1444
0
              {
1445
0
                MagickFreeMemory(global_colormap);
1446
0
                MagickFreeMemory(colormap);
1447
0
                ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,image)
1448
0
              }
1449
0
            image->colormap[opacity]=image->background_color;
1450
0
            for (y=0; y < (long) image->rows; y++)
1451
0
            {
1452
0
              register IndexPacket
1453
0
                *indexes;
1454
1455
0
              p=GetImagePixelsEx(image,0,y,image->columns,1,
1456
0
                &image->exception);
1457
0
              if (p == (const PixelPacket *) NULL)
1458
0
                break;
1459
0
              indexes=AccessMutableIndexes(image);
1460
0
              for (x=0; x < (long) image->columns; x++)
1461
0
              {
1462
0
                if (p->opacity == TransparentOpacity)
1463
0
                  indexes[x]=(IndexPacket) opacity;
1464
0
                p++;
1465
0
              }
1466
0
              if (!SyncImagePixels(image))
1467
0
                break;
1468
0
            }
1469
0
          }
1470
0
      }
1471
1.11k
    else
1472
1.11k
      if (image->matte)
1473
132
        {
1474
          /*
1475
            Identify transparent pixel index.
1476
          */
1477
4.50k
          for (y=0; y < (long) image->rows; y++)
1478
4.45k
          {
1479
4.45k
            register const IndexPacket
1480
4.45k
              *indexes;
1481
1482
4.45k
            p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
1483
4.45k
            if (p == (const PixelPacket *) NULL)
1484
0
              break;
1485
4.45k
            indexes=AccessImmutableIndexes(image);
1486
586k
            for (x=0; x < (long) image->columns; x++)
1487
581k
            {
1488
581k
              if (p->opacity == TransparentOpacity)
1489
76
                {
1490
76
                  opacity=(long) indexes[x];
1491
76
                  break;
1492
76
                }
1493
581k
              p++;
1494
581k
            }
1495
4.45k
            if (x < (long) image->columns)
1496
76
              break;
1497
4.45k
          }
1498
132
        }
1499
1.11k
    if (image->colormap == (PixelPacket *) NULL)
1500
0
      break;
1501
5.96k
    for (bits_per_pixel=1; bits_per_pixel < 8; bits_per_pixel++)
1502
5.48k
      if ((1UL << bits_per_pixel) >= image->colors)
1503
633
        break;
1504
1.11k
    q=colormap;
1505
140k
    for (i=0; i < image->colors; i++)
1506
139k
    {
1507
139k
      *q++=ScaleQuantumToChar(image->colormap[i].red);
1508
139k
      *q++=ScaleQuantumToChar(image->colormap[i].green);
1509
139k
      *q++=ScaleQuantumToChar(image->colormap[i].blue);
1510
139k
    }
1511
2.08k
    for ( ; i < (1U << bits_per_pixel); i++)
1512
974
    {
1513
974
      *q++=(Quantum) 0x0;
1514
974
      *q++=(Quantum) 0x0;
1515
974
      *q++=(Quantum) 0x0;
1516
974
    }
1517
1.11k
    if ((image->previous == (Image *) NULL) || !image_info->adjoin)
1518
1.11k
      {
1519
        /*
1520
          Write global colormap.
1521
        */
1522
1.11k
        c=0x80;
1523
1.11k
        c|=(8-1) << 4;  /* color resolution */
1524
1.11k
        c|=(bits_per_pixel-1);   /* size of global colormap */
1525
1.11k
        (void) WriteBlobByte(image,c);
1526
6.63k
        for (p=image->colormap, j=0; j < Max(image->colors-1,1); j++, p++)
1527
6.50k
          if (ColorMatch(&image->background_color,p))
1528
985
            break;
1529
1.11k
        (void) WriteBlobByte(image,(magick_uint8_t) j);  /* background color */
1530
1.11k
        (void) WriteBlobByte(image,0x0);  /* reserved */
1531
1.11k
        (void) WriteBlob(image,3*(((size_t) 1) << bits_per_pixel),(char *) colormap);
1532
856k
        for (j=0; j < 768; j++)
1533
855k
          global_colormap[j]=colormap[j];
1534
1.11k
      }
1535
1.11k
    if (LocaleCompare(image_info->magick,"GIF87") != 0)
1536
571
      {
1537
        /*
1538
          Write Graphics Control extension.
1539
        */
1540
571
        (void) WriteBlobByte(image,0x21);
1541
571
        (void) WriteBlobByte(image,0xf9);
1542
571
        (void) WriteBlobByte(image,0x04);
1543
571
        c=(unsigned char) ((unsigned int) image->dispose << 2);
1544
571
        if (opacity >= 0)
1545
42
          c|=0x01;
1546
571
        (void) WriteBlobByte(image,c);
1547
571
        (void) WriteBlobLSBShort(image,image->delay);
1548
571
        (void) WriteBlobByte(image,opacity >= 0 ? opacity : 0);
1549
571
        (void) WriteBlobByte(image,0x00);
1550
571
        {
1551
571
          const ImageAttribute
1552
571
            *attribute;
1553
1554
571
          if (((attribute=GetImageAttribute(image,"comment")) !=
1555
571
               (ImageAttribute *) NULL) &&
1556
571
              (attribute->value != (const char *) NULL))
1557
78
            {
1558
78
              register char
1559
78
                *p;
1560
1561
78
              size_t
1562
78
                count;
1563
1564
              /*
1565
                Write Comment extension.
1566
              */
1567
78
              (void) WriteBlobByte(image,0x21);
1568
78
              (void) WriteBlobByte(image,0xfe);
1569
78
              p=attribute->value;
1570
152
              while (strlen(p) != 0)
1571
74
                {
1572
74
                  count=Min(strlen(p),255);
1573
74
                  (void) WriteBlobByte(image, (magick_uint8_t)count);
1574
4.71k
                  for (i=0; i < count; i++)
1575
4.63k
                    (void) WriteBlobByte(image,*p++);
1576
74
                }
1577
78
              (void) WriteBlobByte(image,0x0);
1578
78
            }
1579
571
        }
1580
571
        if ((image->previous == (Image *) NULL) &&
1581
571
            (image->next != (Image *) NULL) && (image->iterations != 1))
1582
0
          {
1583
            /*
1584
              Write Netscape Loop extension.
1585
            */
1586
0
            if (image->logging)
1587
0
              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1588
0
                                    "Loop extension with iterations %lu",
1589
0
                                    image->iterations);
1590
0
            (void) WriteBlobByte(image,0x21);
1591
0
            (void) WriteBlobByte(image,0xff);
1592
0
            (void) WriteBlobByte(image,0x0b);
1593
0
            (void) WriteBlob(image,11,"NETSCAPE2.0");
1594
0
            (void) WriteBlobByte(image,0x03);
1595
0
            (void) WriteBlobByte(image,0x01);
1596
0
            (void) WriteBlobLSBShort(image,image->iterations);
1597
0
            (void) WriteBlobByte(image,0x00);
1598
0
          }
1599
571
      }
1600
1.11k
    (void) WriteBlobByte(image,',');  /* image separator */
1601
    /*
1602
      Write the image header.
1603
    */
1604
1.11k
    page.x=image->page.x;
1605
1.11k
    page.y=image->page.y;
1606
1.11k
    if ((image->page.width != 0) && (image->page.height != 0))
1607
1.11k
      page=image->page;
1608
1.11k
    (void) WriteBlobLSBShort(image,page.x);
1609
1.11k
    (void) WriteBlobLSBShort(image,page.y);
1610
1.11k
    (void) WriteBlobLSBShort(image,image->columns);
1611
1.11k
    (void) WriteBlobLSBShort(image,image->rows);
1612
1.11k
    c=0x00;
1613
1.11k
    if (interlace != NoInterlace)
1614
0
      c|=0x40;  /* pixel data is interlaced */
1615
419k
    for (j=0; j < ((size_t) 3*image->colors); j++)
1616
417k
      if (colormap[j] != global_colormap[j])
1617
0
        break;
1618
1.11k
    if (j == ((size_t) 3*image->colors))
1619
1.11k
      (void) WriteBlobByte(image,c);
1620
0
    else
1621
0
      {
1622
0
        c|=0x80;
1623
0
        c|=(bits_per_pixel-1);   /* size of local colormap */
1624
0
        (void) WriteBlobByte(image,c);
1625
0
        (void) WriteBlob(image,3*(((size_t) 1U) << bits_per_pixel),(char *) colormap);
1626
0
      }
1627
    /*
1628
      Write the image data.
1629
    */
1630
1.11k
    c=Max(bits_per_pixel,2);
1631
1.11k
    (void) WriteBlobByte(image,c);
1632
1.11k
    status=EncodeImage(image_info,image,Max(bits_per_pixel,2)+1);
1633
1.11k
    if (status == MagickFail)
1634
0
      {
1635
0
        MagickFreeMemory(global_colormap);
1636
0
        MagickFreeMemory(colormap);
1637
0
        ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,image)
1638
0
      }
1639
1.11k
    (void) WriteBlobByte(image,0x0);
1640
1.11k
    if (image->next == (Image *) NULL)
1641
1.11k
      break;
1642
0
    image=SyncNextImageInList(image);
1643
0
    status=MagickMonitorFormatted(scene++,image_list_length,
1644
0
                                  &image->exception,SaveImagesText,
1645
0
                                  image->filename);
1646
0
    if (status == MagickFail)
1647
0
      break;
1648
0
  } while (image_info->adjoin);
1649
1.11k
  (void) WriteBlobByte(image,';'); /* terminator */
1650
1.11k
  MagickFreeMemory(global_colormap);
1651
1.11k
  MagickFreeMemory(colormap);
1652
1.11k
  if (image_info->adjoin)
1653
1.11k
    while (image->previous != (Image *) NULL)
1654
0
      image=image->previous;
1655
1.11k
  status &= CloseBlob(image);
1656
1.11k
  return(status);
1657
1.11k
}