Coverage Report

Created: 2026-08-14 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdk-pixbuf/gdk-pixbuf/io-gif.c
Line
Count
Source
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2
/* GdkPixbuf library - GIF image loader
3
 *
4
 * Copyright (C) 1999 Mark Crichton
5
 * Copyright (C) 1999 The Free Software Foundation
6
 *
7
 * Authors: Jonathan Blandford <jrb@redhat.com>
8
 *          Adapted from the gimp gif filter written by Adam Moss <adam@gimp.org>
9
 *          Gimp work based on earlier work.
10
 *          Permission to relicense under the LGPL obtained.
11
 *
12
 * This library is free software; you can redistribute it and/or
13
 * modify it under the terms of the GNU Lesser General Public
14
 * License as published by the Free Software Foundation; either
15
 * version 2 of the License, or (at your option) any later version.
16
 *
17
 * This library is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20
 * Lesser General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Lesser General Public
23
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
24
 */
25
26
/* This loader is very hairy code.
27
 *
28
 * The main loop was not designed for incremental loading, so when it was hacked
29
 * in it got a bit messy.  Basically, every function is written to expect a failed
30
 * read_gif, and lets you call it again assuming that the bytes are there.
31
 *
32
 * Return vals.
33
 * Unless otherwise specified, these are the return vals for most functions:
34
 *
35
 *  0 -> success
36
 * -1 -> more bytes needed.
37
 * -2 -> failure; abort the load
38
 * -3 -> control needs to be passed back to the main loop
39
 *        \_ (most of the time returning 0 will get this, but not always)
40
 *
41
 * >1 -> for functions that get a guchar, the char will be returned.
42
 *
43
 * -jrb (11/03/1999)
44
 */
45
46
/*
47
 * If you have any images that crash this code, please, please let me know and
48
 * send them to me.
49
 *                                            <jrb@redhat.com>
50
 */
51
52

53
54
#include "config.h"
55
#include <stdio.h>
56
#include <string.h>
57
#include <errno.h>
58
#include <glib/gi18n-lib.h>
59
#include "gdk-pixbuf-io.h"
60
#include "io-gif-animation.h"
61
62

63
64
#undef DUMP_IMAGE_DETAILS 
65
66
#define MAXCOLORMAPSIZE  256
67
68
#define INTERLACE          0x40
69
#define LOCALCOLORMAP      0x80
70
103k
#define BitSet(byte, bit)  (((byte) & (bit)) == (bit))
71
218k
#define LM_to_uint(a,b)         (((b)<<8)|(a))
72
73
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
74
75

76
77
typedef unsigned char CMap[3][MAXCOLORMAPSIZE];
78
79
/* Possible states we can be in. */
80
enum {
81
  GIF_START,
82
  GIF_GET_COLORMAP,
83
  GIF_GET_NEXT_STEP,
84
  GIF_GET_FRAME_INFO,
85
  GIF_GET_EXTENSION,
86
  GIF_GET_COLORMAP2,
87
  GIF_PREPARE_LZW,
88
  GIF_GET_LZW,
89
  GIF_DONE
90
};
91
92
typedef struct _GifContext GifContext;
93
struct _GifContext
94
{
95
  int state; /* really only relevant for progressive loading */
96
  unsigned int width;
97
  unsigned int height;
98
99
        gboolean has_global_cmap;
100
101
        gint global_colormap_size;
102
        unsigned int global_bit_pixel;
103
104
        CMap frame_color_map;
105
        gint frame_colormap_size;
106
        unsigned int frame_bit_pixel;
107
108
  GdkPixbufGifAnim *animation;
109
  GdkPixbufFrame *frame;
110
  int transparent_index;
111
  int delay_time;
112
  int disposal;
113
114
  /* stuff per frame. */
115
  int frame_len;
116
  int frame_height;
117
  int frame_interlace;
118
  int x_offset;
119
  int y_offset;
120
121
  /* Static read only */
122
  FILE *file;
123
124
  /* progressive read, only. */
125
  GdkPixbufModuleSizeFunc size_func;
126
  GdkPixbufModulePreparedFunc prepared_func;
127
  GdkPixbufModuleUpdatedFunc updated_func;
128
  gpointer user_data;
129
  GByteArray *buf;
130
131
  /* extension context */
132
  guchar extension_label;
133
  guchar extension_flag;
134
        gboolean in_loop_extension;
135
136
  /* get block context */
137
  guchar block_count;
138
  guchar block_buf[280];
139
140
  guchar lzw_set_code_size;
141
142
        /* error pointer */
143
        GError **error;
144
};
145
146
/* Returns TRUE if read is OK,
147
 * FALSE if more memory is needed. */
148
static gboolean
149
gif_read (GifContext *context, guchar *buffer, size_t len)
150
12.4M
{
151
12.4M
  gboolean retval;
152
12.4M
  if (context->file) {
153
138k
    retval = (fread (buffer, 1, len, context->file) == len);
154
155
138k
                if (!retval && ferror (context->file)) {
156
0
                        gint save_errno = errno;
157
0
                        g_set_error (context->error,
158
0
                                     G_FILE_ERROR,
159
0
                                     g_file_error_from_errno (save_errno),
160
0
                                     _("Failure reading GIF: %s"), 
161
0
                                     g_strerror (save_errno));
162
0
                }
163
164
138k
    return retval;
165
12.3M
  } else {
166
12.3M
    if (context->buf->len >= len) {
167
12.2M
      memcpy (buffer, context->buf->data, len);
168
12.2M
      g_byte_array_remove_range (context->buf, 0, len);
169
12.2M
      return TRUE;
170
12.2M
    }
171
12.3M
  }
172
53.7k
  return FALSE;
173
12.4M
}
174
175
/* Changes the stage to be GIF_GET_COLORMAP */
176
static void
177
gif_set_get_colormap (GifContext *context)
178
804
{
179
804
  context->global_colormap_size = 0;
180
804
  context->state = GIF_GET_COLORMAP;
181
804
}
182
183
static void
184
gif_set_get_colormap2 (GifContext *context)
185
2.77k
{
186
2.77k
  context->state = GIF_GET_COLORMAP2;
187
2.77k
}
188
189
static gint
190
gif_get_colormap (GifContext *context)
191
804
{
192
804
  unsigned char rgb[3];
193
194
54.2k
  while (context->global_colormap_size < context->global_bit_pixel) {
195
53.4k
    if (!gif_read (context, rgb, sizeof (rgb))) {
196
0
      return -1;
197
0
    }
198
199
53.4k
    context->animation->color_map[context->global_colormap_size * 3 + 0] = rgb[0];
200
53.4k
    context->animation->color_map[context->global_colormap_size * 3 + 1] = rgb[1];
201
53.4k
    context->animation->color_map[context->global_colormap_size * 3 + 2] = rgb[2];
202
203
53.4k
    context->global_colormap_size ++;
204
53.4k
  }
205
206
804
  return 0;
207
804
}
208
209
210
static gint
211
gif_get_colormap2 (GifContext *context)
212
2.89k
{
213
2.89k
  unsigned char rgb[3];
214
215
110k
  while (context->frame_colormap_size < context->frame_bit_pixel) {
216
107k
    if (!gif_read (context, rgb, sizeof (rgb))) {
217
128
      return -1;
218
128
    }
219
220
107k
    context->frame_color_map[0][context->frame_colormap_size] = rgb[0];
221
107k
    context->frame_color_map[1][context->frame_colormap_size] = rgb[1];
222
107k
    context->frame_color_map[2][context->frame_colormap_size] = rgb[2];
223
224
107k
    context->frame_colormap_size ++;
225
107k
  }
226
227
2.76k
  return 0;
228
2.89k
}
229
230
/*
231
 * in order for this function to work, we need to perform some black magic.
232
 * We want to return -1 to let the calling function know, as before, that it needs
233
 * more bytes.  If we return 0, we were able to successfully read all block->count bytes.
234
 * Problem is, we don't want to reread block_count every time, so we check to see if
235
 * context->block_count is 0 before we read in the function.
236
 *
237
 * As a result, context->block_count MUST be 0 the first time the get_data_block is called
238
 * within a context, and cannot be 0 the second time it's called.
239
 */
240
241
static int
242
get_data_block (GifContext *context,
243
    unsigned char *buf,
244
    gint *empty_block)
245
3.67M
{
246
247
3.67M
  if (context->block_count == 0) {
248
3.63M
    if (!gif_read (context, &context->block_count, 1)) {
249
3.85k
      return -1;
250
3.85k
    }
251
3.63M
  }
252
253
3.67M
  if (context->block_count == 0)
254
350k
    if (empty_block) {
255
314k
      *empty_block = TRUE;
256
314k
      return 0;
257
314k
    }
258
259
3.35M
  if (!gif_read (context, buf, context->block_count)) {
260
43.5k
    return -1;
261
43.5k
  }
262
263
3.31M
  return 0;
264
3.35M
}
265
266
static void
267
gif_set_get_extension (GifContext *context)
268
263k
{
269
263k
  context->state = GIF_GET_EXTENSION;
270
263k
  context->extension_flag = TRUE;
271
263k
  context->extension_label = 0;
272
263k
  context->block_count = 0;
273
263k
}
274
275
static int
276
gif_get_extension (GifContext *context)
277
298k
{
278
298k
  gint retval;
279
298k
  gint empty_block = FALSE;
280
281
298k
  if (context->extension_flag) {
282
298k
    if (context->extension_label == 0) {
283
      /* I guess bad things can happen if we have an extension of 0 )-: */
284
      /* I should look into this sometime */
285
263k
      if (!gif_read (context, & context->extension_label , 1)) {
286
627
        return -1;
287
627
      }
288
263k
    }
289
290
297k
    switch (context->extension_label) {
291
9.67k
                case 0xf9:      /* Graphic Control Extension */
292
9.67k
                        retval = get_data_block (context, (unsigned char *) context->block_buf, NULL);
293
9.67k
      if (retval != 0)
294
426
        return retval;
295
296
9.24k
      if (context->frame == NULL) {
297
        /* I only want to set the transparency if I haven't
298
         * created the frame yet.
299
                                 */
300
9.24k
        context->disposal = (context->block_buf[0] >> 2) & 0x7;
301
9.24k
        context->delay_time = LM_to_uint (context->block_buf[1], context->block_buf[2]);
302
        
303
9.24k
        if ((context->block_buf[0] & 0x1) != 0) {
304
2.18k
          context->transparent_index = context->block_buf[3];
305
7.06k
        } else {
306
7.06k
          context->transparent_index = -1;
307
7.06k
        }
308
9.24k
      }
309
310
      /* Now we've successfully loaded this one, we continue on our way */
311
9.24k
      context->block_count = 0;
312
9.24k
      context->extension_flag = FALSE;
313
9.24k
      break;
314
283k
                case 0xff: /* application extension */
315
283k
                        if (!context->in_loop_extension) { 
316
280k
                                retval = get_data_block (context, (unsigned char *) context->block_buf, NULL);
317
280k
                                if (retval != 0)
318
25.0k
                                        return retval;
319
255k
                                if (!strncmp ((gchar *)context->block_buf, "NETSCAPE2.0", 11) ||
320
185k
                                    !strncmp ((gchar *)context->block_buf, "ANIMEXTS1.0", 11)) {
321
185k
                                        context->in_loop_extension = TRUE;
322
185k
                                }
323
255k
                                context->block_count = 0;
324
255k
                        }
325
258k
                        if (context->in_loop_extension) {
326
447k
                                do {
327
447k
                                        retval = get_data_block (context, (unsigned char *) context->block_buf, &empty_block);
328
447k
                                        if (retval != 0)
329
3.17k
                                                return retval;
330
444k
                                        if (context->block_buf[0] == 0x01) {
331
321k
                                                context->animation->loop = context->block_buf[1] + (context->block_buf[2] << 8);
332
321k
                                                if (context->animation->loop != 0) 
333
42.8k
                                                        context->animation->loop++;
334
321k
                                        }
335
444k
                                        context->block_count = 0;
336
444k
                                }
337
444k
                                while (!empty_block);
338
185k
                                context->in_loop_extension = FALSE;
339
185k
                                context->extension_flag = FALSE;
340
185k
                                return 0;
341
188k
                        }
342
70.2k
      break;                          
343
70.2k
    default:
344
      /* Unhandled extension */
345
3.97k
      break;
346
297k
    }
347
297k
  }
348
  /* read all blocks, until I get an empty block, in case there was an extension I didn't know about. */
349
605k
  do {
350
605k
    retval = get_data_block (context, (unsigned char *) context->block_buf, &empty_block);
351
605k
    if (retval != 0)
352
6.26k
      return retval;
353
598k
    context->block_count = 0;
354
598k
  } while (!empty_block);
355
356
77.8k
  return 0;
357
84.1k
}
358
359
static void
360
gif_set_get_lzw (GifContext *context)
361
51.9k
{
362
51.9k
  context->state = GIF_GET_LZW;
363
51.9k
}
364
365
static int
366
gif_get_lzw (GifContext *context)
367
64.1k
{
368
64.1k
  if (context->frame == NULL) {
369
51.9k
                int rowstride;
370
51.9k
                guint64 len;
371
372
51.9k
                rowstride = gdk_pixbuf_calculate_rowstride (GDK_COLORSPACE_RGB,
373
51.9k
                                                            TRUE,
374
51.9k
                                                            8,
375
51.9k
                                                            context->frame_len,
376
51.9k
                                                            context->frame_height);
377
51.9k
                if (rowstride < 0 ||
378
51.9k
                    !g_uint64_checked_mul (&len, rowstride, context->frame_height) ||
379
51.9k
                    len >= G_MAXINT) {
380
17
                        g_set_error_literal (context->error,
381
17
                                             GDK_PIXBUF_ERROR,
382
17
                                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
383
17
                                             _("Not enough memory to load GIF file"));
384
17
                        return -2;
385
17
                }
386
387
51.8k
                context->frame = g_new0 (GdkPixbufFrame, 1);
388
389
51.8k
                context->frame->lzw_data = g_byte_array_new ();
390
51.8k
                context->frame->lzw_code_size = context->lzw_set_code_size;
391
392
51.8k
                context->frame->width = context->frame_len;
393
51.8k
                context->frame->height = context->frame_height;
394
51.8k
                context->frame->x_offset = context->x_offset;
395
51.8k
                context->frame->y_offset = context->y_offset;
396
51.8k
                context->frame->interlace = context->frame_interlace;
397
398
51.8k
                if (context->frame_colormap_size > 0) {
399
2.75k
                        int i;
400
401
2.75k
                        context->frame->color_map = g_malloc (256 * 3);
402
2.75k
                        context->frame->color_map_allocated = TRUE;
403
707k
                        for (i = 0; i < 256; i++) {
404
704k
                                context->frame->color_map[i * 3 + 0] = context->frame_color_map[0][i];
405
704k
                                context->frame->color_map[i * 3 + 1] = context->frame_color_map[1][i];
406
704k
                                context->frame->color_map[i * 3 + 2] = context->frame_color_map[2][i];
407
704k
                        }
408
2.75k
                }
409
49.1k
                else {
410
49.1k
                        context->frame->color_map = context->animation->color_map;
411
49.1k
                }
412
413
51.8k
                context->frame->transparent_index = context->transparent_index;
414
415
                /* GIF delay is in hundredths, we want thousandths */
416
51.8k
                context->frame->delay_time = context->delay_time * 10;
417
418
                /* GIFs with delay time 0 are mostly broken, but they
419
                 * just want a default, "not that fast" delay.
420
                 */
421
51.8k
                if (context->frame->delay_time == 0)
422
13.2k
                        context->frame->delay_time = 100;
423
424
                /* No GIFs gets to play faster than 50 fps. They just
425
                 * lock up poor gtk.
426
                 */
427
51.8k
                if (context->frame->delay_time < 20)
428
32.8k
                        context->frame->delay_time = 20; /* 20 = "fast" */
429
430
51.8k
                context->frame->elapsed = context->animation->total_time;
431
51.8k
                context->animation->total_time += context->frame->delay_time;                
432
                
433
51.8k
                switch (context->disposal) {
434
13.1k
                case 0:
435
15.7k
                case 1:
436
15.7k
                        context->frame->action = GDK_PIXBUF_FRAME_RETAIN;
437
15.7k
                        break;
438
394
                case 2:
439
394
                        context->frame->action = GDK_PIXBUF_FRAME_DISPOSE;
440
394
                        break;
441
34.6k
                case 3:
442
34.6k
                        context->frame->action = GDK_PIXBUF_FRAME_REVERT;
443
34.6k
                        break;
444
1.15k
                default:
445
1.15k
                        context->frame->action = GDK_PIXBUF_FRAME_RETAIN;
446
1.15k
                        break;
447
51.8k
                }
448
449
51.8k
                context->animation->frames = g_list_append (context->animation->frames, context->frame);
450
451
    /* Notify when have first frame */
452
51.8k
    if (context->animation->frames->next == NULL) {
453
709
      GdkPixbuf *pixbuf = gdk_pixbuf_animation_get_static_image (GDK_PIXBUF_ANIMATION (context->animation));
454
709
      if (pixbuf != NULL)
455
709
        (* context->prepared_func) (pixbuf,
456
709
                                                            GDK_PIXBUF_ANIMATION (context->animation),
457
709
                                                            context->user_data);
458
709
    }
459
51.8k
        }
460
461
  /* read all blocks */
462
2.33M
  while (TRUE) {
463
2.33M
    gint retval, empty_block = FALSE;
464
465
2.33M
    retval = get_data_block (context, (unsigned char *) context->block_buf, &empty_block);
466
467
    /* Notify frame update */
468
2.33M
    if ((retval != 0 || empty_block) && context->animation->frames->next == NULL) {
469
11.3k
      GdkPixbuf *pixbuf = gdk_pixbuf_animation_get_static_image (GDK_PIXBUF_ANIMATION (context->animation));
470
11.3k
      if (pixbuf)
471
11.3k
        (* context->updated_func) (pixbuf,
472
11.3k
                                                           0, 0, context->frame->width, context->frame->height,
473
11.3k
                                                           context->user_data);
474
11.3k
    }
475
476
2.33M
    if (retval != 0)
477
12.5k
      return retval;
478
479
2.32M
    if (empty_block) {
480
51.6k
      context->frame = NULL;
481
51.6k
      context->state = GIF_GET_NEXT_STEP;
482
51.6k
      return 0;
483
51.6k
    }
484
485
2.26M
    g_byte_array_append (context->frame->lzw_data, context->block_buf, context->block_count);
486
2.26M
    if (context->animation->last_frame == context->frame)
487
9.12k
      context->animation->last_frame = NULL;
488
2.26M
    context->block_count = 0;
489
2.26M
  }
490
64.1k
}
491
492
static void
493
gif_set_prepare_lzw (GifContext *context)
494
51.9k
{
495
51.9k
  context->state = GIF_PREPARE_LZW;
496
51.9k
}
497
static int
498
gif_prepare_lzw (GifContext *context)
499
52.0k
{
500
52.0k
  if (!gif_read (context, &(context->lzw_set_code_size), 1)) {
501
    /*g_message (_("GIF: EOF / read error on image data\n"));*/
502
108
    return -1;
503
108
  }
504
505
51.9k
        if (context->lzw_set_code_size >= 12) {
506
18
                g_set_error_literal (context->error,
507
18
                                     GDK_PIXBUF_ERROR,
508
18
                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
509
18
                                     _("GIF image is corrupt (incorrect LZW compression)"));
510
18
                return -2;
511
18
        }
512
513
51.9k
  gif_set_get_lzw (context);
514
515
51.9k
  return 0;
516
51.9k
}
517
518
/*
519
 * Read the GIF signature and screen descriptor.
520
 *
521
 * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9  | 10     | 11 | 12 |
522
 * |-----------|-----------|-----------------------------------|
523
 * | magic     | version   | screen descriptor                 |
524
 * | G | I | F | 8 | 9 | a | width | height | colors | ignored |
525
 */
526
static gint
527
gif_init (GifContext *context)
528
846
{
529
846
  unsigned char buf[13];
530
846
  char version[4];
531
846
        gint width, height;
532
533
846
  if (!gif_read (context, buf, 13)) {
534
    /* Unable to read magic number,
535
                 * gif_read() should have set error
536
                 */
537
0
    return -1;
538
0
  }
539
540
846
  if (strncmp ((char *) buf, "GIF", 3) != 0) {
541
    /* Not a GIF file */
542
0
                g_set_error_literal (context->error,
543
0
                                     GDK_PIXBUF_ERROR,
544
0
                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
545
0
                                     _("File does not appear to be a GIF file"));
546
0
    return -2;
547
0
  }
548
549
846
  strncpy (version, (char *) buf + 3, 3);
550
846
  version[3] = '\0';
551
552
846
  if ((strcmp (version, "87a") != 0) && (strcmp (version, "89a") != 0)) {
553
0
    gchar *escaped_version;
554
555
    /* bad version number, not '87a' or '89a' */
556
0
    escaped_version = g_strescape (version, NULL);
557
0
                g_set_error (context->error,
558
0
                             GDK_PIXBUF_ERROR,
559
0
                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
560
0
                             _("Version %s of the GIF file format is not supported"),
561
0
                             escaped_version);
562
0
    g_free (escaped_version);
563
0
    return -2;
564
0
  }
565
566
846
  context->width = LM_to_uint (buf[6], buf[7]);
567
846
  context->height = LM_to_uint (buf[8], buf[9]);
568
        /* The 4th byte is
569
         * high bit: whether to use the background index
570
         * next 3:   color resolution
571
         * next:     whether colormap is sorted by priority of allocation
572
         * last 3:   size of colormap
573
         */
574
846
  context->global_bit_pixel = 2 << (buf[10] & 0x07);
575
846
        context->has_global_cmap = (buf[10] & 0x80) != 0;
576
577
846
        context->animation->width = context->width;
578
846
        context->animation->height = context->height;
579
580
846
        width = context->width;
581
846
        height = context->height;
582
583
846
        (*context->size_func) (&width, &height, context->user_data);
584
585
846
        if (width == 0 || height == 0) {
586
0
                g_set_error_literal (context->error,
587
0
                                     GDK_PIXBUF_ERROR,
588
0
                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
589
0
                                     _("Resulting GIF image has zero size"));
590
0
                return -2;
591
0
        }
592
593
846
  if (context->has_global_cmap) {
594
804
    gif_set_get_colormap (context);
595
804
  } else {
596
42
    context->state = GIF_GET_NEXT_STEP;
597
42
  }
598
599
#ifdef DUMP_IMAGE_DETAILS
600
        g_print (">Image width: %d height: %d global_cmap: %d\n",
601
                 context->width, context->height, context->has_global_cmap);
602
#endif
603
        
604
846
  return 0;
605
846
}
606
607
static void
608
gif_set_get_frame_info (GifContext *context)
609
51.9k
{
610
51.9k
  context->state = GIF_GET_FRAME_INFO;
611
51.9k
}
612
613
static gint
614
gif_get_frame_info (GifContext *context)
615
52.6k
{
616
52.6k
  unsigned char buf[9];
617
        
618
52.6k
  if (!gif_read (context, buf, 9)) {
619
656
    return -1;
620
656
  }
621
622
  /* Okay, we got all the info we need.  Lets record it */
623
51.9k
  context->frame_len = LM_to_uint (buf[4], buf[5]);
624
51.9k
  context->frame_height = LM_to_uint (buf[6], buf[7]);
625
51.9k
  context->x_offset = LM_to_uint (buf[0], buf[1]);
626
51.9k
  context->y_offset = LM_to_uint (buf[2], buf[3]);
627
628
51.9k
  context->frame_interlace = BitSet (buf[8], INTERLACE);
629
630
#ifdef DUMP_IMAGE_DETAILS
631
        g_print (">width: %d height: %d xoffset: %d yoffset: %d disposal: %d delay: %d transparent: %d interlace: %d\n",
632
                 context->frame_len, context->frame_height, context->x_offset, context->y_offset,
633
                 context->disposal, context->delay_time, context->transparent_index, context->frame_interlace);
634
#endif
635
        
636
51.9k
  context->frame_colormap_size = 0;
637
51.9k
  if (BitSet (buf[8], LOCALCOLORMAP)) {
638
639
#ifdef DUMP_IMAGE_DETAILS
640
                g_print (">has local colormap\n");
641
#endif
642
                
643
    /* Does this frame have it's own colormap. */
644
    /* really only relevant when looking at the first frame
645
     * of an animated gif. */
646
    /* if it does, we need to re-read in the colormap,
647
     * the gray_scale, and the bit_pixel */
648
2.77k
    context->frame_bit_pixel = 1 << ((buf[8] & 0x07) + 1);
649
2.77k
    gif_set_get_colormap2 (context);
650
2.77k
    return 0;
651
2.77k
  }
652
653
49.1k
        if (!context->has_global_cmap) {
654
2
                context->state = GIF_DONE;
655
                
656
2
                g_set_error_literal (context->error,
657
2
                                     GDK_PIXBUF_ERROR,
658
2
                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
659
2
                                     _("GIF image has no global colormap, and a frame inside it has no local colormap."));
660
                
661
2
    return -2;
662
2
        }
663
664
49.1k
  gif_set_prepare_lzw (context);
665
49.1k
  return 0;
666
667
49.1k
}
668
669
static gint
670
gif_get_next_step (GifContext *context)
671
320k
{
672
320k
  unsigned char c;
673
4.95M
  while (TRUE) {
674
4.95M
    if (!gif_read (context, &c, 1)) {
675
4.85k
      return -1;
676
4.85k
    }
677
4.95M
    if (c == ';') {
678
      /* GIF terminator */
679
      /* hmm.  Not 100% sure what to do about this.  Should
680
       * i try to return a blank image instead? */
681
267
      context->state = GIF_DONE;
682
267
      return 0;
683
267
    }
684
685
4.95M
    if (c == '!') {
686
      /* Check the extension */
687
263k
      gif_set_get_extension (context);
688
263k
      return 0;
689
263k
    }
690
691
    /* look for frame */
692
4.68M
    if (c != ',') {
693
      /* Not a valid start character */
694
4.63M
      continue;
695
4.63M
    }
696
    /* load the frame */
697
51.9k
    gif_set_get_frame_info (context);
698
51.9k
    return 0;
699
4.68M
  }
700
320k
}
701
702
703
#define LOG(x) /* g_print ("%s: %s\n", G_STRLOC, x); */
704
705
static gint
706
gif_main_loop (GifContext *context)
707
57.6k
{
708
57.6k
  gint retval = 0;
709
710
796k
  do {
711
796k
    switch (context->state) {
712
846
    case GIF_START:
713
846
                        LOG("start\n");
714
846
      retval = gif_init (context);
715
846
      break;
716
717
804
    case GIF_GET_COLORMAP:
718
804
                        LOG("get_colormap\n");
719
804
      retval = gif_get_colormap (context);
720
804
      if (retval == 0)
721
804
        context->state = GIF_GET_NEXT_STEP;
722
804
      break;
723
724
320k
    case GIF_GET_NEXT_STEP:
725
320k
                        LOG("next_step\n");
726
320k
      retval = gif_get_next_step (context);
727
320k
      break;
728
729
52.6k
    case GIF_GET_FRAME_INFO:
730
52.6k
                        LOG("frame_info\n");
731
52.6k
      retval = gif_get_frame_info (context);
732
52.6k
      break;
733
734
298k
    case GIF_GET_EXTENSION:
735
298k
                        LOG("get_extension\n");
736
298k
      retval = gif_get_extension (context);
737
298k
      if (retval == 0)
738
263k
        context->state = GIF_GET_NEXT_STEP;
739
298k
      break;
740
741
2.89k
    case GIF_GET_COLORMAP2:
742
2.89k
                        LOG("get_colormap2\n");
743
2.89k
      retval = gif_get_colormap2 (context);
744
2.89k
      if (retval == 0)
745
2.76k
        gif_set_prepare_lzw (context);
746
2.89k
      break;
747
748
52.0k
    case GIF_PREPARE_LZW:
749
52.0k
                        LOG("prepare_lzw\n");
750
52.0k
      retval = gif_prepare_lzw (context);
751
52.0k
      break;
752
753
64.1k
    case GIF_GET_LZW:
754
64.1k
                        LOG("get_lzw\n");
755
64.1k
      retval = gif_get_lzw (context);
756
64.1k
      break;
757
758
3.78k
    case GIF_DONE:
759
3.78k
                        LOG("done\n");
760
                        /* fall through */
761
3.78k
    default:
762
3.78k
      retval = 0;
763
3.78k
      goto done;
764
796k
    };
765
792k
  } while ((retval == 0) || (retval == -3));
766
57.6k
 done:
767
57.6k
  return retval;
768
57.6k
}
769
770
static GifContext *
771
new_context (GdkPixbufModuleSizeFunc size_func,
772
             GdkPixbufModulePreparedFunc prepared_func,
773
             GdkPixbufModuleUpdatedFunc updated_func,
774
             gpointer user_data)
775
846
{
776
846
  GifContext *context;
777
778
846
        g_assert (size_func != NULL);
779
846
        g_assert (prepared_func != NULL);
780
846
        g_assert (updated_func != NULL);
781
782
846
  context = g_try_malloc (sizeof (GifContext));
783
846
        if (context == NULL)
784
0
                return NULL;
785
786
846
        memset (context, 0, sizeof (GifContext));
787
        
788
846
        context->animation = g_object_new (GDK_TYPE_PIXBUF_GIF_ANIM, NULL);
789
846
  context->frame = NULL;
790
846
  context->transparent_index = -1;
791
846
  context->file = NULL;
792
846
  context->state = GIF_START;
793
846
  context->size_func = size_func;
794
846
  context->prepared_func = prepared_func;
795
846
  context->updated_func = updated_func;
796
846
  context->user_data = user_data;
797
846
  context->buf = g_byte_array_new ();
798
846
        context->animation->loop = 1;
799
846
        context->in_loop_extension = FALSE;
800
801
846
  return context;
802
846
}
803
804
static void
805
noop_size_notify (gint     *width,
806
      gint     *height,
807
      gpointer  data)
808
49
{
809
49
}
810
811
static void
812
noop_prepared_notify (GdkPixbuf *pixbuf,
813
                      GdkPixbufAnimation *anim,
814
                      gpointer user_data)
815
37
{
816
37
}
817
818
static void
819
noop_updated_notify (GdkPixbuf *pixbuf,
820
         int        x,
821
         int        y,
822
         int        width,
823
         int        height,
824
         gpointer   user_data)
825
37
{
826
37
}
827
828
static GifContext *
829
new_context_without_callbacks (void)
830
49
{
831
49
        return new_context (noop_size_notify, noop_prepared_notify, noop_updated_notify, NULL);
832
49
}
833
834
/* Shared library entry point */
835
static GdkPixbuf *
836
gdk_pixbuf__gif_image_load (FILE *file, GError **error)
837
32
{
838
32
  GifContext *context;
839
32
  GdkPixbuf *pixbuf;
840
32
        gint retval;
841
842
32
  g_return_val_if_fail (file != NULL, NULL);
843
844
32
  context = new_context_without_callbacks ();
845
846
32
        if (context == NULL) {
847
0
                g_set_error_literal (error,
848
0
                                     GDK_PIXBUF_ERROR,
849
0
                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
850
0
                                     _("Not enough memory to load GIF file"));
851
0
                return NULL;
852
0
        }
853
        
854
32
  context->file = file;
855
32
        context->error = error;
856
857
32
        retval = gif_main_loop (context);
858
32
  if (retval == -1 || context->animation->frames == NULL) {
859
23
                if (context->error && *(context->error) == NULL)
860
23
                        g_set_error_literal (context->error,
861
23
                                             GDK_PIXBUF_ERROR,
862
23
                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
863
23
                                             _("GIF file was missing some data (perhaps it was truncated somehow?)"));
864
23
        }
865
9
        else if (retval == -2) {
866
1
                pixbuf = NULL;
867
1
                goto out;
868
1
        }
869
        
870
31
        pixbuf = gdk_pixbuf_animation_get_static_image (GDK_PIXBUF_ANIMATION (context->animation));
871
872
31
        if (pixbuf)
873
24
                g_object_ref (pixbuf);
874
875
32
out:
876
32
        g_object_unref (context->animation);
877
        
878
32
  g_byte_array_unref (context->buf);
879
32
  g_free (context);
880
 
881
32
  return pixbuf;
882
31
}
883
884
static gpointer
885
gdk_pixbuf__gif_image_begin_load (GdkPixbufModuleSizeFunc size_func,
886
                                  GdkPixbufModulePreparedFunc prepared_func,
887
          GdkPixbufModuleUpdatedFunc updated_func,
888
          gpointer user_data,
889
                                  GError **error)
890
797
{
891
797
  GifContext *context;
892
893
797
        g_assert (size_func != NULL);
894
797
        g_assert (prepared_func != NULL);
895
797
        g_assert (updated_func != NULL);
896
897
797
  context = new_context (size_func, prepared_func, updated_func, user_data);
898
899
797
        if (context == NULL) {
900
0
                g_set_error_literal (error,
901
0
                                     GDK_PIXBUF_ERROR,
902
0
                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
903
0
                                     _("Not enough memory to load GIF file"));
904
0
                return NULL;
905
0
        }
906
        
907
797
        context->error = error;
908
909
797
  return (gpointer) context;
910
797
}
911
912
static gboolean
913
gdk_pixbuf__gif_image_stop_load (gpointer data, GError **error)
914
797
{
915
797
  GifContext *context = (GifContext *) data;
916
797
        gboolean retval = TRUE;
917
        
918
797
        if (context->animation->frames == NULL) {
919
125
                g_set_error_literal (error,
920
125
                                     GDK_PIXBUF_ERROR,
921
125
                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
922
125
                                     _("GIF image was truncated or incomplete."));
923
924
125
                retval = FALSE;
925
672
        } else if (context->state != GIF_DONE) {
926
423
                g_set_error_literal (error,
927
423
                                     GDK_PIXBUF_ERROR,
928
423
                                     GDK_PIXBUF_ERROR_INCOMPLETE_ANIMATION,
929
423
                                     _("Not all frames of the GIF image were loaded."));
930
931
423
                retval = FALSE;
932
423
        }
933
934
797
        g_object_unref (context->animation);
935
936
797
  g_byte_array_unref (context->buf);
937
797
  g_free (context);
938
939
797
        return retval;
940
797
}
941
942
static gboolean
943
gdk_pixbuf__gif_image_load_increment (gpointer data,
944
                                      const guchar *buf, guint size,
945
                                      GError **error)
946
57.5k
{
947
57.5k
  gint retval;
948
57.5k
  GifContext *context = (GifContext *) data;
949
950
57.5k
        context->error = error;
951
952
57.5k
  g_byte_array_append (context->buf, buf, size);
953
954
57.5k
  retval = gif_main_loop (context);
955
57.5k
  if (retval == -2)
956
33
    return FALSE;
957
958
57.5k
  return TRUE;
959
57.5k
}
960
961
static GdkPixbufAnimation *
962
gdk_pixbuf__gif_image_load_animation (FILE *file,
963
                                      GError **error)
964
17
{
965
17
  GifContext *context;
966
17
  GdkPixbufAnimation *animation;
967
968
17
  g_return_val_if_fail (file != NULL, NULL);
969
970
17
  context = new_context_without_callbacks ();
971
972
17
        if (context == NULL) {
973
0
                g_set_error_literal (error,
974
0
                                     GDK_PIXBUF_ERROR,
975
0
                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
976
0
                                     _("Not enough memory to load GIF file"));
977
0
                return NULL;
978
0
        }
979
        
980
17
        context->error = error;
981
17
  context->file = file;
982
983
17
  if (gif_main_loop (context) == -1 || context->animation->frames == NULL) {
984
12
                if (context->error && *(context->error) == NULL)
985
11
                        g_set_error_literal (context->error,
986
11
                                             GDK_PIXBUF_ERROR,
987
11
                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
988
11
                                             _("GIF file was missing some data (perhaps it was truncated somehow?)"));
989
990
12
                g_object_unref (context->animation);
991
12
                context->animation = NULL;
992
12
        }
993
994
17
        if (context->animation)
995
5
                animation = GDK_PIXBUF_ANIMATION (context->animation);
996
12
        else
997
12
                animation = NULL;
998
999
17
        if (context->error && *(context->error))
1000
14
                g_print ("%s\n", (*(context->error))->message);
1001
        
1002
17
  g_byte_array_unref (context->buf);
1003
17
  g_free (context);
1004
17
  return animation;
1005
17
}
1006
1007
#ifndef INCLUDE_gif
1008
#define MODULE_ENTRY(function) G_MODULE_EXPORT void function
1009
#else
1010
#define MODULE_ENTRY(function) void _gdk_pixbuf__gif_ ## function
1011
#endif
1012
1013
MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
1014
3
{
1015
3
        module->load = gdk_pixbuf__gif_image_load;
1016
3
        module->begin_load = gdk_pixbuf__gif_image_begin_load;
1017
3
        module->stop_load = gdk_pixbuf__gif_image_stop_load;
1018
3
        module->load_increment = gdk_pixbuf__gif_image_load_increment;
1019
3
        module->load_animation = gdk_pixbuf__gif_image_load_animation;
1020
3
}
1021
1022
MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
1023
3
{
1024
3
        static const GdkPixbufModulePattern signature[] = {
1025
3
                { "GIF8", NULL, 100 },
1026
3
                { NULL, NULL, 0 }
1027
3
        };
1028
3
  static const gchar *mime_types[] = {
1029
3
    "image/gif",
1030
3
    NULL
1031
3
  };
1032
3
  static const gchar *extensions[] = {
1033
3
    "gif",
1034
3
    NULL
1035
3
  };
1036
1037
3
  info->name = "gif";
1038
3
        info->signature = (GdkPixbufModulePattern *) signature;
1039
3
  info->description = NC_("image format", "GIF");
1040
3
  info->mime_types = (gchar **) mime_types;
1041
3
  info->extensions = (gchar **) extensions;
1042
3
  info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
1043
3
  info->license = "LGPL";
1044
3
}