Bug Summary

File:gif.c
Warning:line 1287, column 29
Use of zero-allocated memory

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name gif.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-eagerly-assume -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -mrelocation-model static -mthread-model posix -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -munwind-tables -fuse-init-array -target-cpu x86-64 -dwarf-column-info -debugger-tuning=gdb -resource-dir /usr/lib/llvm-7/lib/clang/7.0.1 -D NSGIF_NAME=nsgif -D NSGIF_VERSION=1.0.0 -I /var/lib/jenkins/workspace/scan-build-libnsgif/include/ -I /var/lib/jenkins/workspace/scan-build-libnsgif/src -D _ALIGNED=__attribute__((aligned)) -D STMTEXPR=1 -D DEBUG -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-7/lib/clang/7.0.1/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -Wwrite-strings -Wno-error -std=c99 -fconst-strings -fdebug-compilation-dir /var/lib/jenkins/workspace/scan-build-libnsgif -ferror-limit 19 -fmessage-length 0 -fobjc-runtime=gcc -fdiagnostics-show-option -analyzer-display-progress -analyzer-output=html -o /var/lib/jenkins/workspace/scan-build-libnsgif/clangScanBuildReports/2024-03-06-092755-12848-1 -x c src/gif.c -faddrsig
1/*
2 * Copyright 2004 Richard Wilson <richard.wilson@netsurf-browser.org>
3 * Copyright 2008 Sean Fox <dyntryx@gmail.com>
4 * Copyright 2013-2022 Michael Drake <tlsa@netsurf-browser.org>
5 *
6 * This file is part of NetSurf's libnsgif, http://www.netsurf-browser.org/
7 * Licenced under the MIT License,
8 * http://www.opensource.org/licenses/mit-license.php
9 */
10
11#include <assert.h>
12#include <stdint.h>
13#include <stdlib.h>
14#include <string.h>
15#include <stdbool.h>
16
17#include "lzw.h"
18#include "nsgif.h"
19
20/** Default minimum allowable frame delay in cs. */
21#define NSGIF_FRAME_DELAY_MIN2 2
22
23/**
24 * Default frame delay to apply.
25 *
26 * Used when a frame delay lower than the minimum is requested.
27 */
28#define NSGIF_FRAME_DELAY_DEFAULT10 10
29
30/** GIF frame data */
31typedef struct nsgif_frame {
32 struct nsgif_frame_info info;
33
34 /** offset (in bytes) to the GIF frame data */
35 size_t frame_offset;
36 /** whether the frame has previously been decoded. */
37 bool_Bool decoded;
38 /** whether the frame is totally opaque */
39 bool_Bool opaque;
40 /** whether a full image redraw is required */
41 bool_Bool redraw_required;
42
43 /** Amount of LZW data found in scan */
44 uint32_t lzw_data_length;
45
46 /** the index designating a transparent pixel */
47 uint32_t transparency_index;
48
49 /** offset to frame colour table */
50 uint32_t colour_table_offset;
51
52 /* Frame flags */
53 uint32_t flags;
54} nsgif_frame;
55
56/** Pixel format: colour component order. */
57struct nsgif_colour_layout {
58 uint8_t r; /**< Byte offset within pixel to red component. */
59 uint8_t g; /**< Byte offset within pixel to green component. */
60 uint8_t b; /**< Byte offset within pixel to blue component. */
61 uint8_t a; /**< Byte offset within pixel to alpha component. */
62};
63
64/** GIF animation data */
65struct nsgif {
66 struct nsgif_info info;
67
68 /** LZW decode context */
69 void *lzw_ctx;
70 /** callbacks for bitmap functions */
71 nsgif_bitmap_cb_vt bitmap;
72 /** decoded frames */
73 nsgif_frame *frames;
74 /** current frame */
75 uint32_t frame;
76 /** current frame decoded to bitmap */
77 uint32_t decoded_frame;
78
79 /** currently decoded image; stored as bitmap from bitmap_create callback */
80 nsgif_bitmap_t *frame_image;
81 /** Row span of frame_image in pixels. */
82 uint32_t rowspan;
83
84 /** Minimum allowable frame delay. */
85 uint16_t delay_min;
86
87 /** Frame delay to apply when delay is less than \ref delay_min. */
88 uint16_t delay_default;
89
90 /** number of animation loops so far */
91 int loop_count;
92
93 /** number of frames partially decoded */
94 uint32_t frame_count_partial;
95
96 /**
97 * Whether all the GIF data has been supplied, or if there may be
98 * more to come.
99 */
100 bool_Bool data_complete;
101
102 /** pointer to GIF data */
103 const uint8_t *buf;
104 /** current index into GIF data */
105 size_t buf_pos;
106 /** total number of bytes of GIF data available */
107 size_t buf_len;
108
109 /** current number of frame holders */
110 uint32_t frame_holders;
111 /** background index */
112 uint32_t bg_index;
113 /** image aspect ratio (ignored) */
114 uint32_t aspect_ratio;
115 /** size of global colour table (in entries) */
116 uint32_t colour_table_size;
117
118 /** current colour table */
119 uint32_t *colour_table;
120 /** Client's colour component order. */
121 struct nsgif_colour_layout colour_layout;
122 /** global colour table */
123 uint32_t global_colour_table[NSGIF_MAX_COLOURS256];
124 /** local colour table */
125 uint32_t local_colour_table[NSGIF_MAX_COLOURS256];
126
127 /** previous frame for NSGIF_FRAME_RESTORE */
128 void *prev_frame;
129 /** previous frame index */
130 uint32_t prev_index;
131};
132
133/**
134 * Helper macro to get number of elements in an array.
135 *
136 * \param[in] _a Array to count elements of.
137 * \return NUlber of elements in array.
138 */
139#define NSGIF_ARRAY_LEN(_a)((sizeof(_a)) / (sizeof(*_a))) ((sizeof(_a)) / (sizeof(*_a)))
140
141/**
142 *
143 * \file
144 * \brief GIF image decoder
145 *
146 * The GIF format is thoroughly documented; a full description can be found at
147 * http://www.w3.org/Graphics/GIF/spec-gif89a.txt
148 *
149 * \todo Plain text and comment extensions should be implemented.
150 */
151
152/** Internal flag that the colour table needs to be processed */
153#define NSGIF_PROCESS_COLOURS0xaa000000 0xaa000000
154
155/** Internal flag that a frame is invalid/unprocessed */
156#define NSGIF_FRAME_INVALID(4294967295U) UINT32_MAX(4294967295U)
157
158/** Transparent colour */
159#define NSGIF_TRANSPARENT_COLOUR0x00 0x00
160
161/** No transparency */
162#define NSGIF_NO_TRANSPARENCY(0xFFFFFFFFu) (0xFFFFFFFFu)
163
164/* GIF Flags */
165#define NSGIF_COLOUR_TABLE_MASK0x80 0x80
166#define NSGIF_COLOUR_TABLE_SIZE_MASK0x07 0x07
167#define NSGIF_BLOCK_TERMINATOR0x00 0x00
168#define NSGIF_TRAILER0x3b 0x3b
169
170/**
171 * Convert an LZW result code to equivalent GIF result code.
172 *
173 * \param[in] l_res LZW response code.
174 * \return GIF result code.
175 */
176static nsgif_error nsgif__error_from_lzw(lzw_result l_res)
177{
178 static const nsgif_error g_res[] = {
179 [LZW_OK] = NSGIF_OK,
180 [LZW_NO_MEM] = NSGIF_ERR_OOM,
181 [LZW_OK_EOD] = NSGIF_ERR_END_OF_DATA,
182 [LZW_NO_DATA] = NSGIF_ERR_END_OF_DATA,
183 [LZW_EOI_CODE] = NSGIF_ERR_DATA_FRAME,
184 [LZW_BAD_ICODE] = NSGIF_ERR_DATA_FRAME,
185 [LZW_BAD_CODE] = NSGIF_ERR_DATA_FRAME,
186 };
187 assert(l_res != LZW_BAD_PARAM)((l_res != LZW_BAD_PARAM) ? (void) (0) : __assert_fail ("l_res != LZW_BAD_PARAM"
, "src/gif.c", 187, __extension__ __PRETTY_FUNCTION__))
;
188 assert(l_res != LZW_NO_COLOUR)((l_res != LZW_NO_COLOUR) ? (void) (0) : __assert_fail ("l_res != LZW_NO_COLOUR"
, "src/gif.c", 188, __extension__ __PRETTY_FUNCTION__))
;
189 return g_res[l_res];
190}
191
192/**
193 * Updates the sprite memory size
194 *
195 * \param gif The animation context
196 * \param width The width of the sprite
197 * \param height The height of the sprite
198 * \return NSGIF_ERR_OOM for a memory error NSGIF_OK for success
199 */
200static nsgif_error nsgif__initialise_sprite(
201 struct nsgif *gif,
202 uint32_t width,
203 uint32_t height)
204{
205 /* Already allocated? */
206 if (gif->frame_image) {
207 return NSGIF_OK;
208 }
209
210 assert(gif->bitmap.create)((gif->bitmap.create) ? (void) (0) : __assert_fail ("gif->bitmap.create"
, "src/gif.c", 210, __extension__ __PRETTY_FUNCTION__))
;
211 gif->frame_image = gif->bitmap.create(width, height);
212 if (gif->frame_image == NULL((void*)0)) {
213 return NSGIF_ERR_OOM;
214 }
215
216 return NSGIF_OK;
217}
218
219/**
220 * Helper to get the rendering bitmap for a gif.
221 *
222 * \param[in] gif The gif object we're decoding.
223 * \return Client pixel buffer for rendering into.
224 */
225static inline uint32_t* nsgif__bitmap_get(
226 struct nsgif *gif)
227{
228 nsgif_error ret;
229
230 /* Make sure we have a buffer to decode to. */
231 ret = nsgif__initialise_sprite(gif, gif->info.width, gif->info.height);
232 if (ret != NSGIF_OK) {
233 return NULL((void*)0);
234 }
235
236 gif->rowspan = gif->info.width;
237 if (gif->bitmap.get_rowspan) {
238 gif->rowspan = gif->bitmap.get_rowspan(gif->frame_image);
239 }
240
241 /* Get the frame data */
242 assert(gif->bitmap.get_buffer)((gif->bitmap.get_buffer) ? (void) (0) : __assert_fail ("gif->bitmap.get_buffer"
, "src/gif.c", 242, __extension__ __PRETTY_FUNCTION__))
;
243 return (void *)gif->bitmap.get_buffer(gif->frame_image);
244}
245
246/**
247 * Helper to tell the client that their bitmap was modified.
248 *
249 * \param[in] gif The gif object we're decoding.
250 */
251static inline void nsgif__bitmap_modified(
252 const struct nsgif *gif)
253{
254 if (gif->bitmap.modified) {
255 gif->bitmap.modified(gif->frame_image);
256 }
257}
258
259/**
260 * Helper to tell the client that whether the bitmap is opaque.
261 *
262 * \param[in] gif The gif object we're decoding.
263 * \param[in] frame The frame that has been decoded.
264 */
265static inline void nsgif__bitmap_set_opaque(
266 const struct nsgif *gif,
267 const struct nsgif_frame *frame)
268{
269 if (gif->bitmap.set_opaque) {
270 gif->bitmap.set_opaque(
271 gif->frame_image, frame->opaque);
272 }
273}
274
275/**
276 * Helper to get the client to determine if the bitmap is opaque.
277 *
278 * \todo: We don't really need to get the client to do this for us.
279 *
280 * \param[in] gif The gif object we're decoding.
281 * \return true if the bitmap is opaque, false otherwise.
282 */
283static inline bool_Bool nsgif__bitmap_get_opaque(
284 const struct nsgif *gif)
285{
286 if (gif->bitmap.test_opaque) {
287 return gif->bitmap.test_opaque(
288 gif->frame_image);
289 }
290
291 return false0;
292}
293
294static void nsgif__record_frame(
295 struct nsgif *gif,
296 const uint32_t *bitmap)
297{
298 size_t pixel_bytes = sizeof(*bitmap);
299 size_t height = gif->info.height;
300 size_t width = gif->info.width;
301 uint32_t *prev_frame;
302
303 if (gif->decoded_frame == NSGIF_FRAME_INVALID(4294967295U) ||
304 gif->decoded_frame == gif->prev_index) {
305 /* No frame to copy, or already have this frame recorded. */
306 return;
307 }
308
309 bitmap = nsgif__bitmap_get(gif);
310 if (bitmap == NULL((void*)0)) {
311 return;
312 }
313
314 if (gif->prev_frame == NULL((void*)0)) {
315 prev_frame = realloc(gif->prev_frame,
316 width * height * pixel_bytes);
317 if (prev_frame == NULL((void*)0)) {
318 return;
319 }
320 } else {
321 prev_frame = gif->prev_frame;
322 }
323
324 memcpy(prev_frame, bitmap, width * height * pixel_bytes);
325
326 gif->prev_frame = prev_frame;
327 gif->prev_index = gif->decoded_frame;
328}
329
330static nsgif_error nsgif__recover_frame(
331 const struct nsgif *gif,
332 uint32_t *bitmap)
333{
334 const uint32_t *prev_frame = gif->prev_frame;
335 size_t pixel_bytes = sizeof(*bitmap);
336 size_t height = gif->info.height;
337 size_t width = gif->info.width;
338
339 memcpy(bitmap, prev_frame, height * width * pixel_bytes);
340
341 return NSGIF_OK;
342}
343
344/**
345 * Get the next line for GIF decode.
346 *
347 * Note that the step size must be initialised to 24 at the start of the frame
348 * (when y == 0). This is because of the first two passes of the frame have
349 * the same step size of 8, and the step size is used to determine the current
350 * pass.
351 *
352 * \param[in] height Frame height in pixels.
353 * \param[in,out] y Current row, starting from 0, updated on exit.
354 * \param[in,out] step Current step starting with 24, updated on exit.
355 * \return true if there is a row to process, false at the end of the frame.
356 */
357static inline bool_Bool nsgif__deinterlace(uint32_t height, uint32_t *y, uint8_t *step)
358{
359 *y += *step & 0xf;
360
361 if (*y < height) return true1;
362
363 switch (*step) {
364 case 24: *y = 4; *step = 8; if (*y < height) return true1;
365 /* Fall through. */
366 case 8: *y = 2; *step = 4; if (*y < height) return true1;
367 /* Fall through. */
368 case 4: *y = 1; *step = 2; if (*y < height) return true1;
369 /* Fall through. */
370 default:
371 break;
372 }
373
374 return false0;
375}
376
377/**
378 * Get the next line for GIF decode.
379 *
380 * \param[in] interlace Non-zero if the frame is not interlaced.
381 * \param[in] height Frame height in pixels.
382 * \param[in,out] y Current row, starting from 0, updated on exit.
383 * \param[in,out] step Current step starting with 24, updated on exit.
384 * \return true if there is a row to process, false at the end of the frame.
385 */
386static inline bool_Bool nsgif__next_row(uint32_t interlace,
387 uint32_t height, uint32_t *y, uint8_t *step)
388{
389 if (!interlace) {
390 return (++*y != height);
391 } else {
392 return nsgif__deinterlace(height, y, step);
393 }
394}
395
396/**
397 * Get any frame clip adjustment for the image extent.
398 *
399 * \param[in] frame_off Frame's X or Y offset.
400 * \param[in] frame_dim Frame width or height.
401 * \param[in] image_ext Image width or height constraint.
402 * \return the amount the frame needs to be clipped to fit the image in given
403 * dimension.
404 */
405static inline uint32_t gif__clip(
406 uint32_t frame_off,
407 uint32_t frame_dim,
408 uint32_t image_ext)
409{
410 uint32_t frame_ext = frame_off + frame_dim;
411
412 if (frame_ext <= image_ext) {
413 return 0;
414 }
415
416 return frame_ext - image_ext;
417}
418
419/**
420 * Perform any jump over decoded data, to accommodate clipped portion of frame.
421 *
422 * \param[in,out] skip Number of pixels of data to jump.
423 * \param[in,out] available Number of pixels of data currently available.
424 * \param[in,out] pos Position in decoded pixel value data.
425 */
426static inline void gif__jump_data(
427 uint32_t *skip,
428 uint32_t *available,
429 const uint8_t **pos)
430{
431 uint32_t jump = (*skip < *available) ? *skip : *available;
432
433 *skip -= jump;
434 *available -= jump;
435 *pos += jump;
436}
437
438static nsgif_error nsgif__decode_complex(
439 struct nsgif *gif,
440 uint32_t width,
441 uint32_t height,
442 uint32_t offset_x,
443 uint32_t offset_y,
444 uint32_t interlace,
445 const uint8_t *data,
446 uint32_t transparency_index,
447 uint32_t *restrict frame_data,
448 uint32_t *restrict colour_table)
449{
450 lzw_result res;
451 nsgif_error ret = NSGIF_OK;
452 uint32_t clip_x = gif__clip(offset_x, width, gif->info.width);
453 uint32_t clip_y = gif__clip(offset_y, height, gif->info.height);
454 const uint8_t *uncompressed;
455 uint32_t available = 0;
456 uint8_t step = 24;
457 uint32_t skip = 0;
458 uint32_t y = 0;
459
460 if (offset_x >= gif->info.width ||
461 offset_y >= gif->info.height) {
462 return NSGIF_OK;
463 }
464
465 width -= clip_x;
466 height -= clip_y;
467
468 if (width == 0 || height == 0) {
469 return NSGIF_OK;
470 }
471
472 /* Initialise the LZW decoding */
473 res = lzw_decode_init(gif->lzw_ctx, data[0],
474 gif->buf, gif->buf_len,
475 data + 1 - gif->buf);
476 if (res != LZW_OK) {
477 return nsgif__error_from_lzw(res);
478 }
479
480 do {
481 uint32_t x;
482 uint32_t *frame_scanline;
483
484 frame_scanline = frame_data + offset_x +
485 (y + offset_y) * gif->rowspan;
486
487 x = width;
488 while (x > 0) {
489 unsigned row_available;
490 while (available == 0) {
491 if (res != LZW_OK) {
492 /* Unexpected end of frame, try to recover */
493 if (res == LZW_OK_EOD ||
494 res == LZW_EOI_CODE) {
495 ret = NSGIF_OK;
496 } else {
497 ret = nsgif__error_from_lzw(res);
498 }
499 return ret;
500 }
501 res = lzw_decode(gif->lzw_ctx,
502 &uncompressed, &available);
503
504 if (available == 0) {
505 return NSGIF_OK;
506 }
507 gif__jump_data(&skip, &available, &uncompressed);
508 }
509
510 row_available = x < available ? x : available;
511 x -= row_available;
512 available -= row_available;
513 if (transparency_index > 0xFF) {
514 while (row_available-- > 0) {
515 *frame_scanline++ =
516 colour_table[*uncompressed++];
517 }
518 } else {
519 while (row_available-- > 0) {
520 register uint32_t colour;
521 colour = *uncompressed++;
522 if (colour != transparency_index) {
523 *frame_scanline =
524 colour_table[colour];
525 }
526 frame_scanline++;
527 }
528 }
529 }
530
531 skip = clip_x;
532 gif__jump_data(&skip, &available, &uncompressed);
533 } while (nsgif__next_row(interlace, height, &y, &step));
534
535 return ret;
536}
537
538static nsgif_error nsgif__decode_simple(
539 struct nsgif *gif,
540 uint32_t height,
541 uint32_t offset_y,
542 const uint8_t *data,
543 uint32_t transparency_index,
544 uint32_t *restrict frame_data,
545 uint32_t *restrict colour_table)
546{
547 uint32_t pixels;
548 uint32_t written = 0;
549 nsgif_error ret = NSGIF_OK;
550 lzw_result res;
551
552 if (offset_y >= gif->info.height) {
553 return NSGIF_OK;
554 }
555
556 height -= gif__clip(offset_y, height, gif->info.height);
557
558 if (height == 0) {
559 return NSGIF_OK;
560 }
561
562 /* Initialise the LZW decoding */
563 res = lzw_decode_init_map(gif->lzw_ctx, data[0],
564 transparency_index, colour_table,
565 gif->buf, gif->buf_len,
566 data + 1 - gif->buf);
567 if (res != LZW_OK) {
568 return nsgif__error_from_lzw(res);
569 }
570
571 frame_data += (offset_y * gif->info.width);
572 pixels = gif->info.width * height;
573
574 while (pixels > 0) {
575 res = lzw_decode_map(gif->lzw_ctx,
576 frame_data, pixels, &written);
577 pixels -= written;
578 frame_data += written;
579 if (res != LZW_OK) {
580 /* Unexpected end of frame, try to recover */
581 if (res == LZW_OK_EOD || res == LZW_EOI_CODE) {
582 ret = NSGIF_OK;
583 } else {
584 ret = nsgif__error_from_lzw(res);
585 }
586 break;
587 }
588 }
589
590 if (pixels == 0) {
591 ret = NSGIF_OK;
592 }
593
594 return ret;
595}
596
597static inline nsgif_error nsgif__decode(
598 struct nsgif *gif,
599 struct nsgif_frame *frame,
600 const uint8_t *data,
601 uint32_t *restrict frame_data)
602{
603 nsgif_error ret;
604 uint32_t width = frame->info.rect.x1 - frame->info.rect.x0;
605 uint32_t height = frame->info.rect.y1 - frame->info.rect.y0;
606 uint32_t offset_x = frame->info.rect.x0;
607 uint32_t offset_y = frame->info.rect.y0;
608 uint32_t transparency_index = frame->transparency_index;
609 uint32_t *restrict colour_table = gif->colour_table;
610
611 if (frame->info.interlaced == false0 && offset_x == 0 &&
612 width == gif->info.width &&
613 width == gif->rowspan) {
614 ret = nsgif__decode_simple(gif, height, offset_y,
615 data, transparency_index,
616 frame_data, colour_table);
617 } else {
618 ret = nsgif__decode_complex(gif, width, height,
619 offset_x, offset_y, frame->info.interlaced,
620 data, transparency_index,
621 frame_data, colour_table);
622 }
623
624 if (gif->data_complete && ret == NSGIF_ERR_END_OF_DATA) {
625 /* This is all the data there is, so make do. */
626 ret = NSGIF_OK;
627 }
628
629 return ret;
630}
631
632/**
633 * Restore a GIF to the background colour.
634 *
635 * \param[in] gif The gif object we're decoding.
636 * \param[in] frame The frame to clear, or NULL.
637 * \param[in] bitmap The bitmap to clear the frame in.
638 */
639static void nsgif__restore_bg(
640 struct nsgif *gif,
641 struct nsgif_frame *frame,
642 uint32_t *bitmap)
643{
644 size_t pixel_bytes = sizeof(*bitmap);
645
646 if (frame == NULL((void*)0)) {
647 size_t width = gif->info.width;
648 size_t height = gif->info.height;
649
650 memset(bitmap, NSGIF_TRANSPARENT_COLOUR0x00,
651 width * height * pixel_bytes);
652 } else {
653 uint32_t width = frame->info.rect.x1 - frame->info.rect.x0;
654 uint32_t height = frame->info.rect.y1 - frame->info.rect.y0;
655 uint32_t offset_x = frame->info.rect.x0;
656 uint32_t offset_y = frame->info.rect.y0;
657
658 if (frame->info.display == false0 ||
659 frame->info.rect.x0 >= gif->info.width ||
660 frame->info.rect.y0 >= gif->info.height) {
661 return;
662 }
663
664 width -= gif__clip(offset_x, width, gif->info.width);
665 height -= gif__clip(offset_y, height, gif->info.height);
666
667 if (frame->info.transparency) {
668 for (uint32_t y = 0; y < height; y++) {
669 uint32_t *scanline = bitmap + offset_x +
670 (offset_y + y) * gif->info.width;
671 memset(scanline, NSGIF_TRANSPARENT_COLOUR0x00,
672 width * pixel_bytes);
673 }
674 } else {
675 for (uint32_t y = 0; y < height; y++) {
676 uint32_t *scanline = bitmap + offset_x +
677 (offset_y + y) * gif->info.width;
678 for (uint32_t x = 0; x < width; x++) {
679 scanline[x] = gif->info.background;
680 }
681 }
682 }
683 }
684}
685
686static nsgif_error nsgif__update_bitmap(
687 struct nsgif *gif,
688 struct nsgif_frame *frame,
689 const uint8_t *data,
690 uint32_t frame_idx)
691{
692 nsgif_error ret;
693 uint32_t *bitmap;
694
695 gif->decoded_frame = frame_idx;
696
697 bitmap = nsgif__bitmap_get(gif);
698 if (bitmap == NULL((void*)0)) {
699 return NSGIF_ERR_OOM;
700 }
701
702 /* Handle any bitmap clearing/restoration required before decoding this
703 * frame. */
704 if (frame_idx == 0 || gif->decoded_frame == NSGIF_FRAME_INVALID(4294967295U)) {
705 nsgif__restore_bg(gif, NULL((void*)0), bitmap);
706
707 } else {
708 struct nsgif_frame *prev = &gif->frames[frame_idx - 1];
709
710 if (prev->info.disposal == NSGIF_DISPOSAL_RESTORE_BG) {
711 nsgif__restore_bg(gif, prev, bitmap);
712
713 } else if (prev->info.disposal == NSGIF_DISPOSAL_RESTORE_PREV) {
714 ret = nsgif__recover_frame(gif, bitmap);
715 if (ret != NSGIF_OK) {
716 nsgif__restore_bg(gif, prev, bitmap);
717 }
718 }
719 }
720
721 if (frame->info.disposal == NSGIF_DISPOSAL_RESTORE_PREV) {
722 /* Store the previous frame for later restoration */
723 nsgif__record_frame(gif, bitmap);
724 }
725
726 ret = nsgif__decode(gif, frame, data, bitmap);
727
728 nsgif__bitmap_modified(gif);
729
730 if (!frame->decoded) {
731 frame->opaque = nsgif__bitmap_get_opaque(gif);
732 frame->decoded = true1;
733 }
734 nsgif__bitmap_set_opaque(gif, frame);
735
736 return ret;
737}
738
739/**
740 * Parse the graphic control extension
741 *
742 * \param[in] frame The gif frame object we're decoding.
743 * \param[in] data The data to decode.
744 * \param[in] len Byte length of data.
745 * \return NSGIF_ERR_END_OF_DATA if more data is needed,
746 * NSGIF_OK for success.
747 */
748static nsgif_error nsgif__parse_extension_graphic_control(
749 struct nsgif_frame *frame,
750 const uint8_t *data,
751 size_t len)
752{
753 enum {
754 GIF_MASK_TRANSPARENCY = 0x01,
755 GIF_MASK_DISPOSAL = 0x1c,
756 };
757
758 /* 6-byte Graphic Control Extension is:
759 *
760 * +0 CHAR Graphic Control Label
761 * +1 CHAR Block Size
762 * +2 CHAR __Packed Fields__
763 * 3BITS Reserved
764 * 3BITS Disposal Method
765 * 1BIT User Input Flag
766 * 1BIT Transparent Color Flag
767 * +3 SHORT Delay Time
768 * +5 CHAR Transparent Color Index
769 */
770 if (len < 6) {
771 return NSGIF_ERR_END_OF_DATA;
772 }
773
774 frame->info.delay = data[3] | (data[4] << 8);
775
776 if (data[2] & GIF_MASK_TRANSPARENCY) {
777 frame->info.transparency = true1;
778 frame->transparency_index = data[5];
779 }
780
781 frame->info.disposal = ((data[2] & GIF_MASK_DISPOSAL) >> 2);
782 /* I have encountered documentation and GIFs in the
783 * wild that use 0x04 to restore the previous frame,
784 * rather than the officially documented 0x03. I
785 * believe some (older?) software may even actually
786 * export this way. We handle this as a type of
787 * "quirks" mode. */
788 if (frame->info.disposal == NSGIF_DISPOSAL_RESTORE_QUIRK) {
789 frame->info.disposal = NSGIF_DISPOSAL_RESTORE_PREV;
790 }
791
792 /* if we are clearing the background then we need to
793 * redraw enough to cover the previous frame too. */
794 frame->redraw_required =
795 frame->info.disposal == NSGIF_DISPOSAL_RESTORE_BG ||
796 frame->info.disposal == NSGIF_DISPOSAL_RESTORE_PREV;
797
798 return NSGIF_OK;
799}
800
801/**
802 * Check an app ext identifier and authentication code for loop count extension.
803 *
804 * \param[in] data The data to decode.
805 * \param[in] len Byte length of data.
806 * \return true if extension is a loop count extension.
807 */
808static bool_Bool nsgif__app_ext_is_loop_count(
809 const uint8_t *data,
810 size_t len)
811{
812 enum {
813 EXT_LOOP_COUNT_BLOCK_SIZE = 0x0b,
814 };
815
816 assert(len > 13)((len > 13) ? (void) (0) : __assert_fail ("len > 13", "src/gif.c"
, 816, __extension__ __PRETTY_FUNCTION__))
;
817 (void)(len);
818
819 if (data[1] == EXT_LOOP_COUNT_BLOCK_SIZE) {
820 if (strncmp((const char *)data + 2, "NETSCAPE2.0", 11) == 0 ||
821 strncmp((const char *)data + 2, "ANIMEXTS1.0", 11) == 0) {
822 return true1;
823 }
824 }
825
826 return false0;
827}
828
829/**
830 * Parse the application extension
831 *
832 * \param[in] gif The gif object we're decoding.
833 * \param[in] data The data to decode.
834 * \param[in] len Byte length of data.
835 * \return NSGIF_ERR_END_OF_DATA if more data is needed,
836 * NSGIF_OK for success.
837 */
838static nsgif_error nsgif__parse_extension_application(
839 struct nsgif *gif,
840 const uint8_t *data,
841 size_t len)
842{
843 /* 14-byte+ Application Extension is:
844 *
845 * +0 CHAR Application Extension Label
846 * +1 CHAR Block Size
847 * +2 8CHARS Application Identifier
848 * +10 3CHARS Appl. Authentication Code
849 * +13 1-256 Application Data (Data sub-blocks)
850 */
851 if (len < 17) {
852 return NSGIF_ERR_END_OF_DATA;
853 }
854
855 if (nsgif__app_ext_is_loop_count(data, len)) {
856 enum {
857 EXT_LOOP_COUNT_SUB_BLOCK_SIZE = 0x03,
858 EXT_LOOP_COUNT_SUB_BLOCK_ID = 0x01,
859 };
860 if ((data[13] == EXT_LOOP_COUNT_SUB_BLOCK_SIZE) &&
861 (data[14] == EXT_LOOP_COUNT_SUB_BLOCK_ID)) {
862 gif->info.loop_max = data[15] | (data[16] << 8);
863
864 /* The value in the source data means repeat N times
865 * after the first implied play. A value of zero has
866 * the special meaning of loop forever. (The only way
867 * to play the animation once is not to have this
868 * extension at all. */
869 if (gif->info.loop_max > 0) {
870 gif->info.loop_max++;
871 }
872 }
873 }
874
875 return NSGIF_OK;
876}
877
878/**
879 * Parse the frame's extensions
880 *
881 * \param[in] gif The gif object we're decoding.
882 * \param[in] frame The frame to parse extensions for.
883 * \param[in] pos Current position in data, updated on exit.
884 * \param[in] decode Whether to decode or skip over the extension.
885 * \return NSGIF_ERR_END_OF_DATA if more data is needed,
886 * NSGIF_OK for success.
887 */
888static nsgif_error nsgif__parse_frame_extensions(
889 struct nsgif *gif,
890 struct nsgif_frame *frame,
891 const uint8_t **pos,
892 bool_Bool decode)
893{
894 enum {
895 GIF_EXT_INTRODUCER = 0x21,
896 GIF_EXT_GRAPHIC_CONTROL = 0xf9,
897 GIF_EXT_COMMENT = 0xfe,
898 GIF_EXT_PLAIN_TEXT = 0x01,
899 GIF_EXT_APPLICATION = 0xff,
900 };
901 const uint8_t *nsgif_data = *pos;
902 const uint8_t *nsgif_end = gif->buf + gif->buf_len;
903 int nsgif_bytes = nsgif_end - nsgif_data;
904
905 /* Initialise the extensions */
906 while (nsgif_bytes > 0 && nsgif_data[0] == GIF_EXT_INTRODUCER) {
907 bool_Bool block_step = true1;
908 nsgif_error ret;
909
910 nsgif_data++;
911 nsgif_bytes--;
912
913 if (nsgif_bytes == 0) {
914 return NSGIF_ERR_END_OF_DATA;
915 }
916
917 /* Switch on extension label */
918 switch (nsgif_data[0]) {
919 case GIF_EXT_GRAPHIC_CONTROL:
920 if (decode) {
921 ret = nsgif__parse_extension_graphic_control(
922 frame,
923 nsgif_data,
924 nsgif_bytes);
925 if (ret != NSGIF_OK) {
926 return ret;
927 }
928 }
929 break;
930
931 case GIF_EXT_APPLICATION:
932 if (decode) {
933 ret = nsgif__parse_extension_application(
934 gif, nsgif_data, nsgif_bytes);
935 if (ret != NSGIF_OK) {
936 return ret;
937 }
938 }
939 break;
940
941 case GIF_EXT_COMMENT:
942 /* Move the pointer to the first data sub-block Skip 1
943 * byte for the extension label. */
944 ++nsgif_data;
945 block_step = false0;
946 break;
947
948 default:
949 break;
950 }
951
952 if (block_step) {
953 /* Move the pointer to the first data sub-block Skip 2
954 * bytes for the extension label and size fields Skip
955 * the extension size itself
956 */
957 if (nsgif_bytes < 2) {
958 return NSGIF_ERR_END_OF_DATA;
959 }
960 nsgif_data += 2 + nsgif_data[1];
961 }
962
963 /* Repeatedly skip blocks until we get a zero block or run out
964 * of data. This data is ignored by this gif decoder. */
965 while (nsgif_data < nsgif_end && nsgif_data[0] != NSGIF_BLOCK_TERMINATOR0x00) {
966 nsgif_data += nsgif_data[0] + 1;
967 if (nsgif_data >= nsgif_end) {
968 return NSGIF_ERR_END_OF_DATA;
969 }
970 }
971 nsgif_data++;
972 nsgif_bytes = nsgif_end - nsgif_data;
973 }
974
975 if (nsgif_data > nsgif_end) {
976 nsgif_data = nsgif_end;
977 }
978
979 /* Set buffer position and return */
980 *pos = nsgif_data;
981 return NSGIF_OK;
982}
983
984/**
985 * Parse a GIF Image Descriptor.
986 *
987 * The format is:
988 *
989 * +0 CHAR Image Separator (0x2c)
990 * +1 SHORT Image Left Position
991 * +3 SHORT Image Top Position
992 * +5 SHORT Width
993 * +7 SHORT Height
994 * +9 CHAR __Packed Fields__
995 * 1BIT Local Colour Table Flag
996 * 1BIT Interlace Flag
997 * 1BIT Sort Flag
998 * 2BITS Reserved
999 * 3BITS Size of Local Colour Table
1000 *
1001 * \param[in] gif The gif object we're decoding.
1002 * \param[in] frame The frame to parse an image descriptor for.
1003 * \param[in] pos Current position in data, updated on exit.
1004 * \param[in] decode Whether to decode the image descriptor.
1005 * \return NSGIF_OK on success, appropriate error otherwise.
1006 */
1007static nsgif_error nsgif__parse_image_descriptor(
1008 struct nsgif *gif,
1009 struct nsgif_frame *frame,
1010 const uint8_t **pos,
1011 bool_Bool decode)
1012{
1013 const uint8_t *data = *pos;
1014 size_t len = gif->buf + gif->buf_len - data;
1015 enum {
1016 NSGIF_IMAGE_DESCRIPTOR_LEN = 10u,
1017 NSGIF_IMAGE_SEPARATOR = 0x2Cu,
1018 NSGIF_MASK_INTERLACE = 0x40u,
1019 };
1020
1021 assert(gif != NULL)((gif != ((void*)0)) ? (void) (0) : __assert_fail ("gif != NULL"
, "src/gif.c", 1021, __extension__ __PRETTY_FUNCTION__))
;
1022 assert(frame != NULL)((frame != ((void*)0)) ? (void) (0) : __assert_fail ("frame != NULL"
, "src/gif.c", 1022, __extension__ __PRETTY_FUNCTION__))
;
1023
1024 if (len < NSGIF_IMAGE_DESCRIPTOR_LEN) {
1025 return NSGIF_ERR_END_OF_DATA;
1026 }
1027
1028 if (decode) {
1029 uint32_t x, y, w, h;
1030
1031 if (data[0] != NSGIF_IMAGE_SEPARATOR) {
1032 return NSGIF_ERR_DATA_FRAME;
1033 }
1034
1035 x = data[1] | (data[2] << 8);
1036 y = data[3] | (data[4] << 8);
1037 w = data[5] | (data[6] << 8);
1038 h = data[7] | (data[8] << 8);
1039 frame->flags = data[9];
1040
1041 frame->info.rect.x0 = x;
1042 frame->info.rect.y0 = y;
1043 frame->info.rect.x1 = x + w;
1044 frame->info.rect.y1 = y + h;
1045
1046 frame->info.interlaced = frame->flags & NSGIF_MASK_INTERLACE;
1047
1048 /* Allow first frame to grow image dimensions. */
1049 if (gif->info.frame_count == 0) {
1050 if (x + w > gif->info.width) {
1051 gif->info.width = x + w;
1052 }
1053 if (y + h > gif->info.height) {
1054 gif->info.height = y + h;
1055 }
1056 }
1057 }
1058
1059 *pos += NSGIF_IMAGE_DESCRIPTOR_LEN;
1060 return NSGIF_OK;
1061}
1062
1063/**
1064 * Extract a GIF colour table into a LibNSGIF colour table buffer.
1065 *
1066 * \param[in] colour_table The colour table to populate.
1067 * \param[in] layout la.
1068 * \param[in] colour_table_entries The number of colour table entries.
1069 * \param[in] data Raw colour table data.
1070 */
1071static void nsgif__colour_table_decode(
1072 uint32_t colour_table[NSGIF_MAX_COLOURS256],
1073 const struct nsgif_colour_layout *layout,
1074 size_t colour_table_entries,
1075 const uint8_t *data)
1076{
1077 uint8_t *entry = (uint8_t *)colour_table;
1078
1079 while (colour_table_entries--) {
1080 /* Gif colour map contents are r,g,b.
1081 *
1082 * We want to pack them bytewise into the colour table,
1083 * according to the client colour layout.
1084 */
1085
1086 entry[layout->r] = *data++;
1087 entry[layout->g] = *data++;
1088 entry[layout->b] = *data++;
1089 entry[layout->a] = 0xff;
1090
1091 entry += sizeof(uint32_t);
1092 }
1093}
1094
1095/**
1096 * Extract a GIF colour table into a LibNSGIF colour table buffer.
1097 *
1098 * \param[in] colour_table The colour table to populate.
1099 * \param[in] layout The target pixel format to decode to.
1100 * \param[in] colour_table_entries The number of colour table entries.
1101 * \param[in] data Current position in data.
1102 * \param[in] data_len The available length of `data`.
1103 * \param[out] used Number of colour table bytes read.
1104 * \param[in] decode Whether to decode the colour table.
1105 * \return NSGIF_OK on success, appropriate error otherwise.
1106 */
1107static inline nsgif_error nsgif__colour_table_extract(
1108 uint32_t colour_table[NSGIF_MAX_COLOURS256],
1109 const struct nsgif_colour_layout *layout,
1110 size_t colour_table_entries,
1111 const uint8_t *data,
1112 size_t data_len,
1113 size_t *used,
1114 bool_Bool decode)
1115{
1116 if (data_len < colour_table_entries * 3) {
1117 return NSGIF_ERR_END_OF_DATA;
1118 }
1119
1120 if (decode) {
1121 nsgif__colour_table_decode(colour_table, layout,
1122 colour_table_entries, data);
1123 }
1124
1125 *used = colour_table_entries * 3;
1126 return NSGIF_OK;
1127}
1128
1129/**
1130 * Get a frame's colour table.
1131 *
1132 * Sets up gif->colour_table for the frame.
1133 *
1134 * \param[in] gif The gif object we're decoding.
1135 * \param[in] frame The frame to get the colour table for.
1136 * \param[in] pos Current position in data, updated on exit.
1137 * \param[in] decode Whether to decode the colour table.
1138 * \return NSGIF_OK on success, appropriate error otherwise.
1139 */
1140static nsgif_error nsgif__parse_colour_table(
1141 struct nsgif *gif,
1142 struct nsgif_frame *frame,
1143 const uint8_t **pos,
1144 bool_Bool decode)
1145{
1146 nsgif_error ret;
1147 const uint8_t *data = *pos;
1148 size_t len = gif->buf + gif->buf_len - data;
1149 size_t used_bytes;
1150
1151 assert(gif != NULL)((gif != ((void*)0)) ? (void) (0) : __assert_fail ("gif != NULL"
, "src/gif.c", 1151, __extension__ __PRETTY_FUNCTION__))
;
1152 assert(frame != NULL)((frame != ((void*)0)) ? (void) (0) : __assert_fail ("frame != NULL"
, "src/gif.c", 1152, __extension__ __PRETTY_FUNCTION__))
;
1153
1154 if ((frame->flags & NSGIF_COLOUR_TABLE_MASK0x80) == 0) {
1155 gif->colour_table = gif->global_colour_table;
1156 return NSGIF_OK;
1157 }
1158
1159 if (decode == false0) {
1160 frame->colour_table_offset = *pos - gif->buf;
1161 }
1162
1163 ret = nsgif__colour_table_extract(
1164 gif->local_colour_table, &gif->colour_layout,
1165 2 << (frame->flags & NSGIF_COLOUR_TABLE_SIZE_MASK0x07),
1166 data, len, &used_bytes, decode);
1167 if (ret != NSGIF_OK) {
1168 return ret;
1169 }
1170 *pos += used_bytes;
1171
1172 if (decode) {
1173 gif->colour_table = gif->local_colour_table;
1174 } else {
1175 frame->info.local_palette = true1;
1176 }
1177
1178 return NSGIF_OK;
1179}
1180
1181/**
1182 * Parse the image data for a gif frame.
1183 *
1184 * Sets up gif->colour_table for the frame.
1185 *
1186 * \param[in] gif The gif object we're decoding.
1187 * \param[in] frame The frame to parse image data for.
1188 * \param[in] pos Current position in data, updated on exit.
1189 * \param[in] decode Whether to decode the image data.
1190 * \return NSGIF_OK on success, appropriate error otherwise.
1191 */
1192static nsgif_error nsgif__parse_image_data(
1193 struct nsgif *gif,
1194 struct nsgif_frame *frame,
1195 const uint8_t **pos,
1196 bool_Bool decode)
1197{
1198 const uint8_t *data = *pos;
1199 size_t len = gif->buf + gif->buf_len - data;
1200 uint32_t frame_idx = frame - gif->frames;
1201 uint8_t minimum_code_size;
1202 nsgif_error ret;
1203
1204 assert(gif != NULL)((gif != ((void*)0)) ? (void) (0) : __assert_fail ("gif != NULL"
, "src/gif.c", 1204, __extension__ __PRETTY_FUNCTION__))
;
1205 assert(frame != NULL)((frame != ((void*)0)) ? (void) (0) : __assert_fail ("frame != NULL"
, "src/gif.c", 1205, __extension__ __PRETTY_FUNCTION__))
;
1206
1207 if (!decode) {
1208 gif->frame_count_partial = frame_idx + 1;
1209 }
1210
1211 /* Ensure sufficient data remains. A gif trailer or a minimum lzw code
1212 * followed by a gif trailer is treated as OK, although without any
1213 * image data. */
1214 switch (len) {
1215 default: if (data[0] == NSGIF_TRAILER0x3b) return NSGIF_OK;
1216 break;
1217 case 2: if (data[1] == NSGIF_TRAILER0x3b) return NSGIF_OK;
1218 /* Fall through. */
1219 case 1: if (data[0] == NSGIF_TRAILER0x3b) return NSGIF_OK;
1220 /* Fall through. */
1221 case 0: return NSGIF_ERR_END_OF_DATA;
1222 }
1223
1224 minimum_code_size = data[0];
1225 if (minimum_code_size >= LZW_CODE_MAX12) {
1226 return NSGIF_ERR_DATA_FRAME;
1227 }
1228
1229 if (decode) {
1230 ret = nsgif__update_bitmap(gif, frame, data, frame_idx);
1231 } else {
1232 uint32_t block_size = 0;
1233
1234 /* Skip the minimum code size. */
1235 data++;
1236 len--;
1237
1238 while (block_size != 1) {
1239 if (len < 1) {
1240 return NSGIF_ERR_END_OF_DATA;
1241 }
1242 block_size = data[0] + 1;
1243 /* Check if the frame data runs off the end of the file */
1244 if (block_size > len) {
1245 frame->lzw_data_length += len;
1246 return NSGIF_ERR_END_OF_DATA;
1247 }
1248
1249 len -= block_size;
1250 data += block_size;
1251 frame->lzw_data_length += block_size;
1252 }
1253
1254 *pos = data;
1255
1256 gif->info.frame_count = frame_idx + 1;
1257 gif->frames[frame_idx].info.display = true1;
1258
1259 return NSGIF_OK;
1260 }
1261
1262 return ret;
1263}
1264
1265static struct nsgif_frame *nsgif__get_frame(
1266 struct nsgif *gif,
1267 uint32_t frame_idx)
1268{
1269 struct nsgif_frame *frame;
1270
1271 if (gif->frame_holders > frame_idx) {
12
Taking false branch
1272 frame = &gif->frames[frame_idx];
1273 } else {
1274 /* Allocate more memory */
1275 size_t count = frame_idx + 1;
1276 struct nsgif_frame *temp;
1277
1278 temp = realloc(gif->frames, count * sizeof(*frame));
1279 if (temp == NULL((void*)0)) {
13
Assuming 'temp' is not equal to NULL
14
Taking false branch
1280 return NULL((void*)0);
1281 }
1282 gif->frames = temp;
1283 gif->frame_holders = count;
1284
1285 frame = &gif->frames[frame_idx];
1286
1287 frame->info.local_palette = false0;
15
Use of zero-allocated memory
1288 frame->info.transparency = false0;
1289 frame->info.display = false0;
1290 frame->info.disposal = 0;
1291 frame->info.delay = 10;
1292
1293 frame->transparency_index = NSGIF_NO_TRANSPARENCY(0xFFFFFFFFu);
1294 frame->frame_offset = gif->buf_pos;
1295 frame->redraw_required = false0;
1296 frame->lzw_data_length = 0;
1297 frame->decoded = false0;
1298 }
1299
1300 return frame;
1301}
1302
1303/**
1304 * Attempts to initialise the next frame
1305 *
1306 * \param[in] gif The animation context
1307 * \param[in] frame_idx The frame number to decode.
1308 * \param[in] decode Whether to decode the graphical image data.
1309 * \return NSGIF_OK on success, appropriate error otherwise.
1310*/
1311static nsgif_error nsgif__process_frame(
1312 struct nsgif *gif,
1313 uint32_t frame_idx,
1314 bool_Bool decode)
1315{
1316 nsgif_error ret;
1317 const uint8_t *pos;
1318 const uint8_t *end;
1319 struct nsgif_frame *frame;
1320
1321 frame = nsgif__get_frame(gif, frame_idx);
11
Calling 'nsgif__get_frame'
1322 if (frame == NULL((void*)0)) {
1323 return NSGIF_ERR_OOM;
1324 }
1325
1326 end = gif->buf + gif->buf_len;
1327
1328 if (decode) {
1329 pos = gif->buf + frame->frame_offset;
1330
1331 /* Ensure this frame is supposed to be decoded */
1332 if (frame->info.display == false0) {
1333 return NSGIF_OK;
1334 }
1335
1336 /* Ensure the frame is in range to decode */
1337 if (frame_idx > gif->frame_count_partial) {
1338 return NSGIF_ERR_END_OF_DATA;
1339 }
1340
1341 /* Done if frame is already decoded */
1342 if (frame_idx == gif->decoded_frame) {
1343 return NSGIF_OK;
1344 }
1345 } else {
1346 pos = gif->buf + gif->buf_pos;
1347
1348 /* Check if we've finished */
1349 if (pos < end && pos[0] == NSGIF_TRAILER0x3b) {
1350 return NSGIF_OK;
1351 }
1352 }
1353
1354 ret = nsgif__parse_frame_extensions(gif, frame, &pos, !decode);
1355 if (ret != NSGIF_OK) {
1356 goto cleanup;
1357 }
1358
1359 ret = nsgif__parse_image_descriptor(gif, frame, &pos, !decode);
1360 if (ret != NSGIF_OK) {
1361 goto cleanup;
1362 }
1363
1364 ret = nsgif__parse_colour_table(gif, frame, &pos, decode);
1365 if (ret != NSGIF_OK) {
1366 goto cleanup;
1367 }
1368
1369 ret = nsgif__parse_image_data(gif, frame, &pos, decode);
1370 if (ret != NSGIF_OK) {
1371 goto cleanup;
1372 }
1373
1374cleanup:
1375 if (!decode) {
1376 gif->buf_pos = pos - gif->buf;
1377 }
1378
1379 return ret;
1380}
1381
1382/* exported function documented in nsgif.h */
1383void nsgif_destroy(nsgif_t *gif)
1384{
1385 if (gif == NULL((void*)0)) {
1386 return;
1387 }
1388
1389 /* Release all our memory blocks */
1390 if (gif->frame_image) {
1391 assert(gif->bitmap.destroy)((gif->bitmap.destroy) ? (void) (0) : __assert_fail ("gif->bitmap.destroy"
, "src/gif.c", 1391, __extension__ __PRETTY_FUNCTION__))
;
1392 gif->bitmap.destroy(gif->frame_image);
1393 gif->frame_image = NULL((void*)0);
1394 }
1395
1396 free(gif->frames);
1397 gif->frames = NULL((void*)0);
1398
1399 free(gif->prev_frame);
1400 gif->prev_frame = NULL((void*)0);
1401
1402 lzw_context_destroy(gif->lzw_ctx);
1403 gif->lzw_ctx = NULL((void*)0);
1404
1405 free(gif);
1406}
1407
1408/**
1409 * Check whether the host is little endian.
1410 *
1411 * Checks whether least significant bit is in the first byte of a `uint16_t`.
1412 *
1413 * \return true if host is little endian.
1414 */
1415static inline bool_Bool nsgif__host_is_little_endian(void)
1416{
1417 const uint16_t test = 1;
1418
1419 return ((const uint8_t *) &test)[0];
1420}
1421
1422static struct nsgif_colour_layout nsgif__bitmap_fmt_to_colour_layout(
1423 nsgif_bitmap_fmt_t bitmap_fmt)
1424{
1425 bool_Bool le = nsgif__host_is_little_endian();
1426
1427 /* Map endian-dependant formats to byte-wise format for the host. */
1428 switch (bitmap_fmt) {
1429 case NSGIF_BITMAP_FMT_RGBA8888:
1430 bitmap_fmt = (le) ? NSGIF_BITMAP_FMT_A8B8G8R8
1431 : NSGIF_BITMAP_FMT_R8G8B8A8;
1432 break;
1433 case NSGIF_BITMAP_FMT_BGRA8888:
1434 bitmap_fmt = (le) ? NSGIF_BITMAP_FMT_A8R8G8B8
1435 : NSGIF_BITMAP_FMT_B8G8R8A8;
1436 break;
1437 case NSGIF_BITMAP_FMT_ARGB8888:
1438 bitmap_fmt = (le) ? NSGIF_BITMAP_FMT_B8G8R8A8
1439 : NSGIF_BITMAP_FMT_A8R8G8B8;
1440 break;
1441 case NSGIF_BITMAP_FMT_ABGR8888:
1442 bitmap_fmt = (le) ? NSGIF_BITMAP_FMT_R8G8B8A8
1443 : NSGIF_BITMAP_FMT_A8B8G8R8;
1444 break;
1445 default:
1446 break;
1447 }
1448
1449 /* Set up colour component order for bitmap format. */
1450 switch (bitmap_fmt) {
1451 default:
1452 /* Fall through. */
1453 case NSGIF_BITMAP_FMT_R8G8B8A8:
1454 return (struct nsgif_colour_layout) {
1455 .r = 0,
1456 .g = 1,
1457 .b = 2,
1458 .a = 3,
1459 };
1460
1461 case NSGIF_BITMAP_FMT_B8G8R8A8:
1462 return (struct nsgif_colour_layout) {
1463 .b = 0,
1464 .g = 1,
1465 .r = 2,
1466 .a = 3,
1467 };
1468
1469 case NSGIF_BITMAP_FMT_A8R8G8B8:
1470 return (struct nsgif_colour_layout) {
1471 .a = 0,
1472 .r = 1,
1473 .g = 2,
1474 .b = 3,
1475 };
1476
1477 case NSGIF_BITMAP_FMT_A8B8G8R8:
1478 return (struct nsgif_colour_layout) {
1479 .a = 0,
1480 .b = 1,
1481 .g = 2,
1482 .r = 3,
1483 };
1484 }
1485}
1486
1487/* exported function documented in nsgif.h */
1488nsgif_error nsgif_create(
1489 const nsgif_bitmap_cb_vt *bitmap_vt,
1490 nsgif_bitmap_fmt_t bitmap_fmt,
1491 nsgif_t **gif_out)
1492{
1493 nsgif_t *gif;
1494
1495 gif = calloc(1, sizeof(*gif));
1496 if (gif == NULL((void*)0)) {
1497 return NSGIF_ERR_OOM;
1498 }
1499
1500 gif->bitmap = *bitmap_vt;
1501 gif->decoded_frame = NSGIF_FRAME_INVALID(4294967295U);
1502 gif->prev_index = NSGIF_FRAME_INVALID(4294967295U);
1503
1504 gif->delay_min = NSGIF_FRAME_DELAY_MIN2;
1505 gif->delay_default = NSGIF_FRAME_DELAY_DEFAULT10;
1506
1507 gif->colour_layout = nsgif__bitmap_fmt_to_colour_layout(bitmap_fmt);
1508
1509 *gif_out = gif;
1510 return NSGIF_OK;
1511}
1512
1513/* exported function documented in nsgif.h */
1514void nsgif_set_frame_delay_behaviour(
1515 nsgif_t *gif,
1516 uint16_t delay_min,
1517 uint16_t delay_default)
1518{
1519 gif->delay_min = delay_min;
1520 gif->delay_default = delay_default;
1521}
1522
1523/**
1524 * Read GIF header.
1525 *
1526 * 6-byte GIF file header is:
1527 *
1528 * +0 3CHARS Signature ('GIF')
1529 * +3 3CHARS Version ('87a' or '89a')
1530 *
1531 * \param[in] gif The GIF object we're decoding.
1532 * \param[in,out] pos The current buffer position, updated on success.
1533 * \param[in] strict Whether to require a known GIF version.
1534 * \return NSGIF_OK on success, appropriate error otherwise.
1535 */
1536static nsgif_error nsgif__parse_header(
1537 struct nsgif *gif,
1538 const uint8_t **pos,
1539 bool_Bool strict)
1540{
1541 const uint8_t *data = *pos;
1542 size_t len = gif->buf + gif->buf_len - data;
1543
1544 if (len < 6) {
1545 return NSGIF_ERR_END_OF_DATA;
1546 }
1547
1548 if (strncmp((const char *) data, "GIF", 3) != 0) {
1549 return NSGIF_ERR_DATA;
1550 }
1551 data += 3;
1552
1553 if (strict == true1) {
1554 if ((strncmp((const char *) data, "87a", 3) != 0) &&
1555 (strncmp((const char *) data, "89a", 3) != 0)) {
1556 return NSGIF_ERR_DATA;
1557 }
1558 }
1559 data += 3;
1560
1561 *pos = data;
1562 return NSGIF_OK;
1563}
1564
1565/**
1566 * Read Logical Screen Descriptor.
1567 *
1568 * 7-byte Logical Screen Descriptor is:
1569 *
1570 * +0 SHORT Logical Screen Width
1571 * +2 SHORT Logical Screen Height
1572 * +4 CHAR __Packed Fields__
1573 * 1BIT Global Colour Table Flag
1574 * 3BITS Colour Resolution
1575 * 1BIT Sort Flag
1576 * 3BITS Size of Global Colour Table
1577 * +5 CHAR Background Colour Index
1578 * +6 CHAR Pixel Aspect Ratio
1579 *
1580 * \param[in] gif The GIF object we're decoding.
1581 * \param[in,out] pos The current buffer position, updated on success.
1582 * \return NSGIF_OK on success, appropriate error otherwise.
1583 */
1584static nsgif_error nsgif__parse_logical_screen_descriptor(
1585 struct nsgif *gif,
1586 const uint8_t **pos)
1587{
1588 const uint8_t *data = *pos;
1589 size_t len = gif->buf + gif->buf_len - data;
1590
1591 if (len < 7) {
1592 return NSGIF_ERR_END_OF_DATA;
1593 }
1594
1595 gif->info.width = data[0] | (data[1] << 8);
1596 gif->info.height = data[2] | (data[3] << 8);
1597 gif->info.global_palette = data[4] & NSGIF_COLOUR_TABLE_MASK0x80;
1598 gif->colour_table_size = 2 << (data[4] & NSGIF_COLOUR_TABLE_SIZE_MASK0x07);
1599 gif->bg_index = data[5];
1600 gif->aspect_ratio = data[6];
1601 gif->info.loop_max = 1;
1602
1603 *pos += 7;
1604 return NSGIF_OK;
1605}
1606
1607/* exported function documented in nsgif.h */
1608nsgif_error nsgif_data_scan(
1609 nsgif_t *gif,
1610 size_t size,
1611 const uint8_t *data)
1612{
1613 const uint8_t *nsgif_data;
1614 nsgif_error ret;
1615 uint32_t frames;
1616
1617 if (gif->data_complete) {
1618 return NSGIF_ERR_DATA_COMPLETE;
1619 }
1620
1621 /* Initialize values */
1622 gif->buf_len = size;
1623 gif->buf = data;
1624
1625 /* Get our current processing position */
1626 nsgif_data = gif->buf + gif->buf_pos;
1627
1628 /* See if we should initialise the GIF */
1629 if (gif->buf_pos == 0) {
1630 /* We want everything to be NULL before we start so we've no
1631 * chance of freeing bad pointers (paranoia)
1632 */
1633 gif->frame_image = NULL((void*)0);
1634 gif->frames = NULL((void*)0);
1635 gif->frame_holders = 0;
1636
1637 /* The caller may have been lazy and not reset any values */
1638 gif->info.frame_count = 0;
1639 gif->frame_count_partial = 0;
1640 gif->decoded_frame = NSGIF_FRAME_INVALID(4294967295U);
1641 gif->frame = NSGIF_FRAME_INVALID(4294967295U);
1642
1643 ret = nsgif__parse_header(gif, &nsgif_data, false0);
1644 if (ret != NSGIF_OK) {
1645 return ret;
1646 }
1647
1648 ret = nsgif__parse_logical_screen_descriptor(gif, &nsgif_data);
1649 if (ret != NSGIF_OK) {
1650 return ret;
1651 }
1652
1653 /* Remember we've done this now */
1654 gif->buf_pos = nsgif_data - gif->buf;
1655
1656 /* Some broken GIFs report the size as the screen size they
1657 * were created in. As such, we detect for the common cases and
1658 * set the sizes as 0 if they are found which results in the
1659 * GIF being the maximum size of the frames.
1660 */
1661 if (((gif->info.width == 640) && (gif->info.height == 480)) ||
1662 ((gif->info.width == 640) && (gif->info.height == 512)) ||
1663 ((gif->info.width == 800) && (gif->info.height == 600)) ||
1664 ((gif->info.width == 1024) && (gif->info.height == 768)) ||
1665 ((gif->info.width == 1280) && (gif->info.height == 1024)) ||
1666 ((gif->info.width == 1600) && (gif->info.height == 1200)) ||
1667 ((gif->info.width == 0) || (gif->info.height == 0)) ||
1668 ((gif->info.width > 2048) || (gif->info.height > 2048))) {
1669 gif->info.width = 1;
1670 gif->info.height = 1;
1671 }
1672
1673 /* Set the first colour to a value that will never occur in
1674 * reality so we know if we've processed it
1675 */
1676 gif->global_colour_table[0] = NSGIF_PROCESS_COLOURS0xaa000000;
1677
1678 /* Check if the GIF has no frame data (13-byte header + 1-byte
1679 * termination block) Although generally useless, the GIF
1680 * specification does not expressly prohibit this
1681 */
1682 if (gif->buf_len == gif->buf_pos + 1) {
1683 if (nsgif_data[0] == NSGIF_TRAILER0x3b) {
1684 return NSGIF_OK;
1685 }
1686 }
1687 }
1688
1689 /* Do the colour map if we haven't already. As the top byte is always
1690 * 0xff or 0x00 depending on the transparency we know if it's been
1691 * filled in.
1692 */
1693 if (gif->global_colour_table[0] == NSGIF_PROCESS_COLOURS0xaa000000) {
1694 /* Check for a global colour map signified by bit 7 */
1695 if (gif->info.global_palette) {
1696 size_t remaining = gif->buf + gif->buf_len - nsgif_data;
1697 size_t used;
1698
1699 ret = nsgif__colour_table_extract(
1700 gif->global_colour_table,
1701 &gif->colour_layout,
1702 gif->colour_table_size,
1703 nsgif_data, remaining, &used, true1);
1704 if (ret != NSGIF_OK) {
1705 return ret;
1706 }
1707
1708 nsgif_data += used;
1709 gif->buf_pos = (nsgif_data - gif->buf);
1710 } else {
1711 /* Create a default colour table with the first two
1712 * colours as black and white. */
1713 uint8_t *entry = (uint8_t *)gif->global_colour_table;
1714
1715 /* Black */
1716 entry[gif->colour_layout.r] = 0x00;
1717 entry[gif->colour_layout.g] = 0x00;
1718 entry[gif->colour_layout.b] = 0x00;
1719 entry[gif->colour_layout.a] = 0xFF;
1720
1721 entry += sizeof(uint32_t);
1722
1723 /* White */
1724 entry[gif->colour_layout.r] = 0xFF;
1725 entry[gif->colour_layout.g] = 0xFF;
1726 entry[gif->colour_layout.b] = 0xFF;
1727 entry[gif->colour_layout.a] = 0xFF;
1728
1729 gif->colour_table_size = 2;
1730 }
1731
1732 if (gif->info.global_palette &&
1733 gif->bg_index < gif->colour_table_size) {
1734 size_t bg_idx = gif->bg_index;
1735 gif->info.background = gif->global_colour_table[bg_idx];
1736 } else {
1737 gif->info.background = gif->global_colour_table[0];
1738 }
1739 }
1740
1741 if (gif->lzw_ctx == NULL((void*)0)) {
1742 lzw_result res = lzw_context_create(
1743 (struct lzw_ctx **)&gif->lzw_ctx);
1744 if (res != LZW_OK) {
1745 return nsgif__error_from_lzw(res);
1746 }
1747 }
1748
1749 /* Try to initialise all frames. */
1750 do {
1751 frames = gif->info.frame_count;
1752 ret = nsgif__process_frame(gif, frames, false0);
1753 } while (gif->info.frame_count > frames);
1754
1755 if (ret == NSGIF_ERR_END_OF_DATA && gif->info.frame_count > 0) {
1756 ret = NSGIF_OK;
1757 }
1758
1759 return ret;
1760}
1761
1762/* exported function documented in nsgif.h */
1763void nsgif_data_complete(
1764 nsgif_t *gif)
1765{
1766 if (gif->data_complete == false0) {
1767 uint32_t start = gif->info.frame_count;
1768 uint32_t end = gif->frame_count_partial;
1769
1770 for (uint32_t f = start; f < end; f++) {
1771 nsgif_frame *frame = &gif->frames[f];
1772
1773 if (frame->lzw_data_length > 0) {
1774 frame->info.display = true1;
1775 gif->info.frame_count = f + 1;
1776
1777 if (f == 0) {
1778 frame->info.transparency = true1;
1779 }
1780 break;
1781 }
1782 }
1783 }
1784
1785 gif->data_complete = true1;
1786}
1787
1788static void nsgif__redraw_rect_extend(
1789 const nsgif_rect_t *frame,
1790 nsgif_rect_t *redraw)
1791{
1792 if (redraw->x1 == 0 || redraw->y1 == 0) {
1793 *redraw = *frame;
1794 } else {
1795 if (redraw->x0 > frame->x0) {
1796 redraw->x0 = frame->x0;
1797 }
1798 if (redraw->x1 < frame->x1) {
1799 redraw->x1 = frame->x1;
1800 }
1801 if (redraw->y0 > frame->y0) {
1802 redraw->y0 = frame->y0;
1803 }
1804 if (redraw->y1 < frame->y1) {
1805 redraw->y1 = frame->y1;
1806 }
1807 }
1808}
1809
1810static uint32_t nsgif__frame_next(
1811 const nsgif_t *gif,
1812 bool_Bool partial,
1813 uint32_t frame)
1814{
1815 uint32_t frames = partial ?
1816 gif->frame_count_partial :
1817 gif->info.frame_count;
1818
1819 if (frames == 0) {
1820 return NSGIF_FRAME_INVALID(4294967295U);
1821 }
1822
1823 frame++;
1824 return (frame >= frames) ? 0 : frame;
1825}
1826
1827static nsgif_error nsgif__next_displayable_frame(
1828 const nsgif_t *gif,
1829 uint32_t *frame,
1830 uint32_t *delay)
1831{
1832 uint32_t next = *frame;
1833
1834 do {
1835 next = nsgif__frame_next(gif, false0, next);
1836 if (next <= *frame && *frame != NSGIF_FRAME_INVALID(4294967295U) &&
1837 gif->data_complete == false0) {
1838 return NSGIF_ERR_END_OF_DATA;
1839
1840 } else if (next == *frame || next == NSGIF_FRAME_INVALID(4294967295U)) {
1841 return NSGIF_ERR_FRAME_DISPLAY;
1842 }
1843
1844 if (delay != NULL((void*)0)) {
1845 *delay += gif->frames[next].info.delay;
1846 }
1847
1848 } while (gif->frames[next].info.display == false0);
1849
1850 *frame = next;
1851 return NSGIF_OK;
1852}
1853
1854static inline bool_Bool nsgif__animation_complete(int count, int max)
1855{
1856 if (max == 0) {
1857 return false0;
1858 }
1859
1860 return (count >= max);
1861}
1862
1863nsgif_error nsgif_reset(
1864 nsgif_t *gif)
1865{
1866 gif->loop_count = 0;
1867 gif->frame = NSGIF_FRAME_INVALID(4294967295U);
1868
1869 return NSGIF_OK;
1870}
1871
1872/* exported function documented in nsgif.h */
1873nsgif_error nsgif_frame_prepare(
1874 nsgif_t *gif,
1875 nsgif_rect_t *area,
1876 uint32_t *delay_cs,
1877 uint32_t *frame_new)
1878{
1879 nsgif_error ret;
1880 nsgif_rect_t rect = {
1881 .x1 = 0,
1882 .y1 = 0,
1883 };
1884 uint32_t delay = 0;
1885 uint32_t frame = gif->frame;
1886
1887 if (gif->frame != NSGIF_FRAME_INVALID(4294967295U) &&
1888 gif->frame < gif->info.frame_count &&
1889 gif->frames[gif->frame].info.display) {
1890 rect = gif->frames[gif->frame].info.rect;
1891 }
1892
1893 if (nsgif__animation_complete(
1894 gif->loop_count,
1895 gif->info.loop_max)) {
1896 return NSGIF_ERR_ANIMATION_END;
1897 }
1898
1899 ret = nsgif__next_displayable_frame(gif, &frame, &delay);
1900 if (ret != NSGIF_OK) {
1901 return ret;
1902 }
1903
1904 if (gif->frame != NSGIF_FRAME_INVALID(4294967295U) && frame < gif->frame) {
1905 gif->loop_count++;
1906 }
1907
1908 if (gif->data_complete) {
1909 /* Check for last frame, which has infinite delay. */
1910
1911 if (gif->info.frame_count == 1) {
1912 delay = NSGIF_INFINITE((4294967295U));
1913 } else if (gif->info.loop_max != 0) {
1914 uint32_t frame_next = frame;
1915
1916 ret = nsgif__next_displayable_frame(gif,
1917 &frame_next, NULL((void*)0));
1918 if (ret != NSGIF_OK) {
1919 return ret;
1920 }
1921
1922 if (gif->data_complete && frame_next < frame) {
1923 if (nsgif__animation_complete(
1924 gif->loop_count + 1,
1925 gif->info.loop_max)) {
1926 delay = NSGIF_INFINITE((4294967295U));
1927 }
1928 }
1929 }
1930 }
1931
1932 gif->frame = frame;
1933 nsgif__redraw_rect_extend(&gif->frames[frame].info.rect, &rect);
1934
1935 if (delay < gif->delay_min) {
1936 delay = gif->delay_default;
1937 }
1938
1939 *frame_new = gif->frame;
1940 *delay_cs = delay;
1941 *area = rect;
1942
1943 return NSGIF_OK;
1944}
1945
1946/* exported function documented in nsgif.h */
1947nsgif_error nsgif_frame_decode(
1948 nsgif_t *gif,
1949 uint32_t frame,
1950 nsgif_bitmap_t **bitmap)
1951{
1952 uint32_t start_frame;
1953 nsgif_error ret = NSGIF_OK;
1954
1955 if (frame >= gif->info.frame_count) {
1
Assuming the condition is false
2
Taking false branch
1956 return NSGIF_ERR_BAD_FRAME;
1957 }
1958
1959 if (gif->decoded_frame == frame) {
3
Assuming the condition is false
4
Taking false branch
1960 *bitmap = gif->frame_image;
1961 return NSGIF_OK;
1962
1963 } else if (gif->decoded_frame >= frame ||
5
Assuming the condition is false
7
Taking false branch
1964 gif->decoded_frame == NSGIF_FRAME_INVALID(4294967295U)) {
6
Assuming the condition is false
1965 /* Can skip to first frame or restart. */
1966 start_frame = 0;
1967 } else {
1968 start_frame = nsgif__frame_next(
1969 gif, false0, gif->decoded_frame);
1970 }
1971
1972 for (uint32_t f = start_frame; f <= frame; f++) {
8
Assuming 'f' is <= 'frame'
9
Loop condition is true. Entering loop body
1973 ret = nsgif__process_frame(gif, f, true1);
10
Calling 'nsgif__process_frame'
1974 if (ret != NSGIF_OK) {
1975 return ret;
1976 }
1977 }
1978
1979 *bitmap = gif->frame_image;
1980 return ret;
1981}
1982
1983/* exported function documented in nsgif.h */
1984const nsgif_info_t *nsgif_get_info(const nsgif_t *gif)
1985{
1986 return &gif->info;
1987}
1988
1989/* exported function documented in nsgif.h */
1990const nsgif_frame_info_t *nsgif_get_frame_info(
1991 const nsgif_t *gif,
1992 uint32_t frame)
1993{
1994 if (frame >= gif->info.frame_count) {
1995 return NULL((void*)0);
1996 }
1997
1998 return &gif->frames[frame].info;
1999}
2000
2001/* exported function documented in nsgif.h */
2002void nsgif_global_palette(
2003 const nsgif_t *gif,
2004 uint32_t table[NSGIF_MAX_COLOURS256],
2005 size_t *entries)
2006{
2007 size_t len = sizeof(*table) * NSGIF_MAX_COLOURS256;
2008
2009 memcpy(table, gif->global_colour_table, len);
2010 *entries = gif->colour_table_size;
2011}
2012
2013/* exported function documented in nsgif.h */
2014bool_Bool nsgif_local_palette(
2015 const nsgif_t *gif,
2016 uint32_t frame,
2017 uint32_t table[NSGIF_MAX_COLOURS256],
2018 size_t *entries)
2019{
2020 const nsgif_frame *f;
2021
2022 if (frame >= gif->frame_count_partial) {
2023 return false0;
2024 }
2025
2026 f = &gif->frames[frame];
2027 if (f->info.local_palette == false0) {
2028 return false0;
2029 }
2030
2031 *entries = 2 << (f->flags & NSGIF_COLOUR_TABLE_SIZE_MASK0x07);
2032 nsgif__colour_table_decode(table, &gif->colour_layout,
2033 *entries, gif->buf + f->colour_table_offset);
2034
2035 return true1;
2036}
2037
2038/* exported function documented in nsgif.h */
2039const char *nsgif_strerror(nsgif_error err)
2040{
2041 static const char *const str[] = {
2042 [NSGIF_OK] = "Success",
2043 [NSGIF_ERR_OOM] = "Out of memory",
2044 [NSGIF_ERR_DATA] = "Invalid source data",
2045 [NSGIF_ERR_BAD_FRAME] = "Requested frame does not exist",
2046 [NSGIF_ERR_DATA_FRAME] = "Invalid frame data",
2047 [NSGIF_ERR_END_OF_DATA] = "Unexpected end of GIF source data",
2048 [NSGIF_ERR_DATA_COMPLETE] = "Can't add data to completed GIF",
2049 [NSGIF_ERR_FRAME_DISPLAY] = "Frame can't be displayed",
2050 [NSGIF_ERR_ANIMATION_END] = "Animation complete",
2051 };
2052
2053 if (err >= NSGIF_ARRAY_LEN(str)((sizeof(str)) / (sizeof(*str))) || str[err] == NULL((void*)0)) {
2054 return "Unknown error";
2055 }
2056
2057 return str[err];
2058}
2059
2060/* exported function documented in nsgif.h */
2061const char *nsgif_str_disposal(enum nsgif_disposal disposal)
2062{
2063 static const char *const str[] = {
2064 [NSGIF_DISPOSAL_UNSPECIFIED] = "Unspecified",
2065 [NSGIF_DISPOSAL_NONE] = "None",
2066 [NSGIF_DISPOSAL_RESTORE_BG] = "Restore background",
2067 [NSGIF_DISPOSAL_RESTORE_PREV] = "Restore previous",
2068 [NSGIF_DISPOSAL_RESTORE_QUIRK] = "Restore quirk",
2069 };
2070
2071 if (disposal >= NSGIF_ARRAY_LEN(str)((sizeof(str)) / (sizeof(*str))) || str[disposal] == NULL((void*)0)) {
2072 return "Unspecified";
2073 }
2074
2075 return str[disposal];
2076}