Coverage Report

Created: 2026-10-10 06:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwebp/examples/gifdec.c
Line
Count
Source
1
// Copyright 2012 Google Inc. All Rights Reserved.
2
//
3
// Use of this source code is governed by a BSD-style license
4
// that can be found in the COPYING file in the root of the source
5
// tree. An additional intellectual property rights grant can be found
6
// in the file PATENTS. All contributing project authors may
7
// be found in the AUTHORS file in the root of the source tree.
8
// -----------------------------------------------------------------------------
9
//
10
// GIF decode.
11
12
#include "./gifdec.h"
13
14
#include <stdio.h>
15
16
#ifdef WEBP_HAVE_GIF
17
#include <assert.h>
18
#include <stdlib.h>
19
#include <string.h>
20
21
#include "webp/encode.h"
22
#include "webp/mux_types.h"
23
#include "webp/types.h"
24
25
0
#define GIF_TRANSPARENT_COLOR 0x00000000u
26
0
#define GIF_WHITE_COLOR 0xffffffffu
27
0
#define GIF_TRANSPARENT_MASK 0x01
28
0
#define GIF_DISPOSE_MASK 0x07
29
0
#define GIF_DISPOSE_SHIFT 2
30
31
// from utils/utils.h
32
#ifdef __cplusplus
33
extern "C" {
34
#endif
35
extern void WebPCopyPlane(const uint8_t* src, int src_stride, uint8_t* dst,
36
                          int dst_stride, int width, int height);
37
extern void WebPCopyPixels(const WebPPicture* const src,
38
                           WebPPicture* const dst);
39
#ifdef __cplusplus
40
}
41
#endif
42
43
void GIFGetBackgroundColor(const ColorMapObject* const color_map,
44
                           int bgcolor_index, int transparent_index,
45
0
                           uint32_t* const bgcolor) {
46
0
  if (transparent_index != GIF_INDEX_INVALID &&
47
0
      bgcolor_index == transparent_index) {
48
0
    *bgcolor = GIF_TRANSPARENT_COLOR;  // Special case.
49
0
  } else if (color_map == NULL || color_map->Colors == NULL ||
50
0
             bgcolor_index >= color_map->ColorCount) {
51
0
    *bgcolor = GIF_WHITE_COLOR;
52
0
    fprintf(stderr,
53
0
            "GIF decode warning: invalid background color index. Assuming "
54
0
            "white background.\n");
55
0
  } else {
56
0
    const GifColorType color = color_map->Colors[bgcolor_index];
57
0
    *bgcolor = (0xffu << 24) | (color.Red << 16) | (color.Green << 8) |
58
0
               (color.Blue << 0);
59
0
  }
60
0
}
61
62
int GIFReadGraphicsExtension(const GifByteType* const buf, int* const duration,
63
                             GIFDisposeMethod* const dispose,
64
0
                             int* const transparent_index) {
65
0
  const int flags = buf[1];
66
0
  const int dispose_raw = (flags >> GIF_DISPOSE_SHIFT) & GIF_DISPOSE_MASK;
67
0
  const int duration_raw = buf[2] | (buf[3] << 8);  // In 10 ms units.
68
0
  if (buf[0] != 4) return 0;
69
0
  *duration = duration_raw * 10;  // Duration is in 1 ms units.
70
0
  switch (dispose_raw) {
71
0
    case 3:
72
0
      *dispose = GIF_DISPOSE_RESTORE_PREVIOUS;
73
0
      break;
74
0
    case 2:
75
0
      *dispose = GIF_DISPOSE_BACKGROUND;
76
0
      break;
77
0
    case 1:
78
0
    case 0:
79
0
    default:
80
0
      *dispose = GIF_DISPOSE_NONE;
81
0
      break;
82
0
  }
83
0
  *transparent_index =
84
0
      (flags & GIF_TRANSPARENT_MASK) ? buf[4] : GIF_INDEX_INVALID;
85
0
  return 1;
86
0
}
87
88
static int Remap(const GifFileType* const gif, const uint8_t* const src,
89
0
                 int len, int transparent_index, uint32_t* dst) {
90
0
  int i;
91
0
  const GifColorType* colors;
92
0
  const ColorMapObject* const cmap =
93
0
      gif->Image.ColorMap ? gif->Image.ColorMap : gif->SColorMap;
94
0
  if (cmap == NULL) return 1;
95
0
  if (cmap->Colors == NULL || cmap->ColorCount <= 0) return 0;
96
0
  colors = cmap->Colors;
97
98
0
  for (i = 0; i < len; ++i) {
99
0
    if (src[i] == transparent_index) {
100
0
      dst[i] = GIF_TRANSPARENT_COLOR;
101
0
    } else if (src[i] < cmap->ColorCount) {
102
0
      const GifColorType c = colors[src[i]];
103
0
      dst[i] = c.Blue | (c.Green << 8) | (c.Red << 16) | (0xffu << 24);
104
0
    } else {
105
0
      return 0;
106
0
    }
107
0
  }
108
0
  return 1;
109
0
}
110
111
int GIFReadFrame(GifFileType* const gif, int transparent_index,
112
0
                 GIFFrameRect* const gif_rect, WebPPicture* const picture) {
113
0
  WebPPicture sub_image;
114
0
  const GifImageDesc* const image_desc = &gif->Image;
115
0
  uint32_t* dst = NULL;
116
0
  uint8_t* tmp = NULL;
117
0
  const GIFFrameRect rect = {image_desc->Left, image_desc->Top,
118
0
                             image_desc->Width, image_desc->Height};
119
0
  const uint64_t memory_needed = 4 * rect.width * (uint64_t)rect.height;
120
0
  int ok = 0;
121
0
  *gif_rect = rect;
122
123
0
  if (memory_needed != (size_t)memory_needed || memory_needed > (4ULL << 32)) {
124
0
    fprintf(stderr, "Image is too large (%d x %d).", rect.width, rect.height);
125
0
    return 0;
126
0
  }
127
128
  // Use a view for the sub-picture:
129
0
  if (!WebPPictureView(picture, rect.x_offset, rect.y_offset, rect.width,
130
0
                       rect.height, &sub_image)) {
131
0
    fprintf(stderr, "Sub-image %dx%d at position %d,%d is invalid!\n",
132
0
            rect.width, rect.height, rect.x_offset, rect.y_offset);
133
0
    return 0;
134
0
  }
135
0
  dst = sub_image.argb;
136
137
0
  tmp = (uint8_t*)WebPMalloc(rect.width * sizeof(*tmp));
138
0
  if (tmp == NULL) goto End;
139
140
0
  if (image_desc->Interlace) {  // Interlaced image.
141
    // We need 4 passes, with the following offsets and jumps.
142
0
    const int interlace_offsets[] = {0, 4, 2, 1};
143
0
    const int interlace_jumps[] = {8, 8, 4, 2};
144
0
    int pass;
145
0
    for (pass = 0; pass < 4; ++pass) {
146
0
      const size_t stride = (size_t)sub_image.argb_stride;
147
0
      int y = interlace_offsets[pass];
148
0
      uint32_t* row = dst + y * stride;
149
0
      const size_t jump = interlace_jumps[pass] * stride;
150
0
      for (; y < rect.height; y += interlace_jumps[pass], row += jump) {
151
0
        if (DGifGetLine(gif, tmp, rect.width) == GIF_ERROR) goto End;
152
0
        if (!Remap(gif, tmp, rect.width, transparent_index, row)) goto End;
153
0
      }
154
0
    }
155
0
  } else {  // Non-interlaced image.
156
0
    int y;
157
0
    uint32_t* ptr = dst;
158
0
    for (y = 0; y < rect.height; ++y, ptr += sub_image.argb_stride) {
159
0
      if (DGifGetLine(gif, tmp, rect.width) == GIF_ERROR) goto End;
160
0
      if (!Remap(gif, tmp, rect.width, transparent_index, ptr)) goto End;
161
0
    }
162
0
  }
163
0
  ok = 1;
164
165
0
End:
166
0
  if (!ok) picture->error_code = sub_image.error_code;
167
0
  WebPPictureFree(&sub_image);
168
0
  WebPFree(tmp);
169
0
  return ok;
170
0
}
171
172
int GIFReadLoopCount(GifFileType* const gif, GifByteType** const buf,
173
0
                     int* const loop_count) {
174
0
  assert(!memcmp(*buf + 1, "NETSCAPE2.0", 11) ||
175
0
         !memcmp(*buf + 1, "ANIMEXTS1.0", 11));
176
0
  if (DGifGetExtensionNext(gif, buf) == GIF_ERROR) {
177
0
    return 0;
178
0
  }
179
0
  if (*buf == NULL) {
180
0
    return 0;  // Loop count sub-block missing.
181
0
  }
182
0
  if ((*buf)[0] < 3 || (*buf)[1] != 1) {
183
0
    return 0;  // wrong size/marker
184
0
  }
185
0
  *loop_count = (*buf)[2] | ((*buf)[3] << 8);
186
0
  return 1;
187
0
}
188
189
int GIFReadMetadata(GifFileType* const gif, GifByteType** const buf,
190
0
                    WebPData* const metadata) {
191
0
  const int is_xmp = !memcmp(*buf + 1, "XMP DataXMP", 11);
192
0
  const int is_icc = !memcmp(*buf + 1, "ICCRGBG1012", 11);
193
0
  assert(is_xmp || is_icc);
194
0
  (void)is_icc;  // silence unused warning.
195
  // Construct metadata from sub-blocks.
196
  // Usual case (including ICC profile): In each sub-block, the
197
  // first byte specifies its size in bytes (0 to 255) and the
198
  // rest of the bytes contain the data.
199
  // Special case for XMP data: In each sub-block, the first byte
200
  // is also part of the XMP payload. XMP in GIF also has a 257
201
  // byte padding data. See the XMP specification for details.
202
0
  while (1) {
203
0
    WebPData subblock;
204
0
    const uint8_t* tmp;
205
0
    if (DGifGetExtensionNext(gif, buf) == GIF_ERROR) {
206
0
      return 0;
207
0
    }
208
0
    if (*buf == NULL) break;  // Finished.
209
0
    subblock.size = is_xmp ? (*buf)[0] + 1 : (*buf)[0];
210
0
    assert(subblock.size > 0);
211
0
    subblock.bytes = is_xmp ? *buf : *buf + 1;
212
    // Note: We store returned value in 'tmp' first, to avoid
213
    // leaking old memory in metadata->bytes on error.
214
0
    tmp = (uint8_t*)realloc((void*)metadata->bytes,
215
0
                            metadata->size + subblock.size);
216
0
    if (tmp == NULL) {
217
0
      return 0;
218
0
    }
219
0
    memcpy((void*)(tmp + metadata->size), subblock.bytes, subblock.size);
220
0
    metadata->bytes = tmp;
221
0
    metadata->size += subblock.size;
222
0
  }
223
0
  if (is_xmp) {
224
    // XMP padding data is 0x01, 0xff, 0xfe ... 0x01, 0x00.
225
0
    const size_t xmp_pading_size = 257;
226
0
    if (metadata->size > xmp_pading_size) {
227
0
      metadata->size -= xmp_pading_size;
228
0
    }
229
0
  }
230
0
  return 1;
231
0
}
232
233
static void ClearRectangle(WebPPicture* const picture, int left, int top,
234
0
                           int width, int height) {
235
0
  int i, j;
236
0
  const size_t stride = picture->argb_stride;
237
0
  uint32_t* dst = picture->argb + top * stride + left;
238
0
  for (j = 0; j < height; ++j, dst += stride) {
239
0
    for (i = 0; i < width; ++i) dst[i] = GIF_TRANSPARENT_COLOR;
240
0
  }
241
0
}
242
243
0
void GIFClearPic(WebPPicture* const pic, const GIFFrameRect* const rect) {
244
0
  if (rect != NULL) {
245
0
    ClearRectangle(pic, rect->x_offset, rect->y_offset, rect->width,
246
0
                   rect->height);
247
0
  } else {
248
0
    ClearRectangle(pic, 0, 0, pic->width, pic->height);
249
0
  }
250
0
}
251
252
0
void GIFCopyPixels(const WebPPicture* const src, WebPPicture* const dst) {
253
0
  WebPCopyPixels(src, dst);
254
0
}
255
256
void GIFDisposeFrame(GIFDisposeMethod dispose, const GIFFrameRect* const rect,
257
                     const WebPPicture* const prev_canvas,
258
0
                     WebPPicture* const curr_canvas) {
259
0
  assert(rect != NULL);
260
0
  if (dispose == GIF_DISPOSE_BACKGROUND) {
261
0
    GIFClearPic(curr_canvas, rect);
262
0
  } else if (dispose == GIF_DISPOSE_RESTORE_PREVIOUS) {
263
0
    const size_t src_stride = prev_canvas->argb_stride;
264
0
    const uint32_t* const src =
265
0
        prev_canvas->argb + rect->x_offset + rect->y_offset * src_stride;
266
0
    const size_t dst_stride = curr_canvas->argb_stride;
267
0
    uint32_t* const dst =
268
0
        curr_canvas->argb + rect->x_offset + rect->y_offset * dst_stride;
269
0
    assert(prev_canvas != NULL);
270
0
    WebPCopyPlane((uint8_t*)src, (int)(4 * src_stride), (uint8_t*)dst,
271
0
                  (int)(4 * dst_stride), 4 * rect->width, rect->height);
272
0
  }
273
0
}
274
275
void GIFBlendFrames(const WebPPicture* const src,
276
0
                    const GIFFrameRect* const rect, WebPPicture* const dst) {
277
0
  int i, j;
278
0
  const size_t src_stride = src->argb_stride;
279
0
  const size_t dst_stride = dst->argb_stride;
280
0
  assert(src->width == dst->width && src->height == dst->height);
281
0
  for (j = rect->y_offset; j < rect->y_offset + rect->height; ++j) {
282
0
    for (i = rect->x_offset; i < rect->x_offset + rect->width; ++i) {
283
0
      const uint32_t src_pixel = src->argb[j * src_stride + i];
284
0
      const int src_alpha = src_pixel >> 24;
285
0
      if (src_alpha != 0) {
286
0
        dst->argb[j * dst_stride + i] = src_pixel;
287
0
      }
288
0
    }
289
0
  }
290
0
}
291
292
29
void GIFDisplayError(const GifFileType* const gif, int gif_error) {
293
  // libgif 4.2.0 has retired PrintGifError() and added GifErrorString().
294
29
#if LOCAL_GIF_PREREQ(4, 2)
295
29
#if LOCAL_GIF_PREREQ(5, 0)
296
  // Static string actually, hence the const char* cast.
297
29
  const char* error_str =
298
29
      (const char*)GifErrorString((gif == NULL) ? gif_error : gif->Error);
299
#else
300
  const char* error_str = (const char*)GifErrorString();
301
  (void)gif;
302
#endif
303
29
  if (error_str == NULL) error_str = "Unknown error";
304
29
  fprintf(stderr, "GIFLib Error %d: %s\n", gif_error, error_str);
305
#else
306
  (void)gif;
307
  fprintf(stderr, "GIFLib Error %d: ", gif_error);
308
  PrintGifError();
309
  fprintf(stderr, "\n");
310
#endif
311
29
}
312
313
#else  // !WEBP_HAVE_GIF
314
315
static void ErrorGIFNotAvailable(void) {
316
  fprintf(stderr,
317
          "GIF support not compiled. Please install the libgif-dev "
318
          "package before building.\n");
319
}
320
321
void GIFGetBackgroundColor(const struct ColorMapObject* const color_map,
322
                           int bgcolor_index, int transparent_index,
323
                           uint32_t* const bgcolor) {
324
  (void)color_map;
325
  (void)bgcolor_index;
326
  (void)transparent_index;
327
  (void)bgcolor;
328
  ErrorGIFNotAvailable();
329
}
330
331
int GIFReadGraphicsExtension(const GifByteType* const data, int* const duration,
332
                             GIFDisposeMethod* const dispose,
333
                             int* const transparent_index) {
334
  (void)data;
335
  (void)duration;
336
  (void)dispose;
337
  (void)transparent_index;
338
  ErrorGIFNotAvailable();
339
  return 0;
340
}
341
342
int GIFReadFrame(struct GifFileType* const gif, int transparent_index,
343
                 GIFFrameRect* const gif_rect,
344
                 struct WebPPicture* const picture) {
345
  (void)gif;
346
  (void)transparent_index;
347
  (void)gif_rect;
348
  (void)picture;
349
  ErrorGIFNotAvailable();
350
  return 0;
351
}
352
353
int GIFReadLoopCount(struct GifFileType* const gif, GifByteType** const buf,
354
                     int* const loop_count) {
355
  (void)gif;
356
  (void)buf;
357
  (void)loop_count;
358
  ErrorGIFNotAvailable();
359
  return 0;
360
}
361
362
int GIFReadMetadata(struct GifFileType* const gif, GifByteType** const buf,
363
                    struct WebPData* const metadata) {
364
  (void)gif;
365
  (void)buf;
366
  (void)metadata;
367
  ErrorGIFNotAvailable();
368
  return 0;
369
}
370
371
void GIFDisposeFrame(GIFDisposeMethod dispose, const GIFFrameRect* const rect,
372
                     const struct WebPPicture* const prev_canvas,
373
                     struct WebPPicture* const curr_canvas) {
374
  (void)dispose;
375
  (void)rect;
376
  (void)prev_canvas;
377
  (void)curr_canvas;
378
  ErrorGIFNotAvailable();
379
}
380
381
void GIFBlendFrames(const struct WebPPicture* const src,
382
                    const GIFFrameRect* const rect,
383
                    struct WebPPicture* const dst) {
384
  (void)src;
385
  (void)rect;
386
  (void)dst;
387
  ErrorGIFNotAvailable();
388
}
389
390
void GIFDisplayError(const struct GifFileType* const gif, int gif_error) {
391
  (void)gif;
392
  (void)gif_error;
393
  ErrorGIFNotAvailable();
394
}
395
396
void GIFClearPic(struct WebPPicture* const pic,
397
                 const GIFFrameRect* const rect) {
398
  (void)pic;
399
  (void)rect;
400
  ErrorGIFNotAvailable();
401
}
402
403
void GIFCopyPixels(const struct WebPPicture* const src,
404
                   struct WebPPicture* const dst) {
405
  (void)src;
406
  (void)dst;
407
  ErrorGIFNotAvailable();
408
}
409
410
#endif  // WEBP_HAVE_GIF
411
412
// -----------------------------------------------------------------------------