libnsgif: branch master updated. release/0.2.1-117-g3832f7e
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/libnsgif.git/shortlog/3832f7edc4cd117642ad...
...commit http://git.netsurf-browser.org/libnsgif.git/commit/3832f7edc4cd117642ad78...
...tree http://git.netsurf-browser.org/libnsgif.git/tree/3832f7edc4cd117642ad7890...
The branch, master has been updated
via 3832f7edc4cd117642ad7890eb1476d1f9d8e918 (commit)
via a83ed529003818e3f4ce70a68752a34c66461de2 (commit)
via fe39d4611ccce7cdb7be11edf7a819888b6319a4 (commit)
via e6be9591cfa6f901c6f4c421a9aeaa08da986eaf (commit)
via de3510f1215dd16b215bd34ffe2cc81a34f67985 (commit)
via 125cd0409ab9d2d13fc41fcfae2fe001e4241878 (commit)
from 032e62c80df60b5fcedcd75f3043e0a18e114b6e (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=3832f7edc4cd117642...
commit 3832f7edc4cd117642ad7890eb1476d1f9d8e918
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Reorder #includes.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index dbc066a..ecddf78 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -8,14 +8,14 @@
* http://www.opensource.org/licenses/mit-license.php
*/
-#include <stdbool.h>
+#include <assert.h>
#include <stdint.h>
-#include <string.h>
#include <stdlib.h>
-#include <assert.h>
+#include <string.h>
+#include <stdbool.h>
-#include "libnsgif.h"
#include "lzw.h"
+#include "libnsgif.h"
/**
*
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=a83ed529003818e3f4...
commit a83ed529003818e3f4ce70a68752a34c66461de2
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
LOG: Remove unused logging code.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 38816f5..dbc066a 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -10,13 +10,11 @@
#include <stdbool.h>
#include <stdint.h>
-#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
-#include "libnsgif.h"
-#include "utils/log.h"
+#include "libnsgif.h"
#include "lzw.h"
/**
diff --git a/src/utils/log.h b/src/utils/log.h
deleted file mode 100644
index 1413374..0000000
--- a/src/utils/log.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright 2003 James Bursa <bursa(a)users.sourceforge.net>
- * Copyright 2004 John Tytgat <John.Tytgat(a)aaug.net>
- *
- * This file is part of NetSurf, http://www.netsurf-browser.org/
- * Licenced under the MIT License,
- * http://www.opensource.org/licenses/mit-license.php
- */
-
-#include <stdio.h>
-
-#ifndef _LIBNSGIF_LOG_H_
-#define _LIBNSGIF_LOG_H_
-
-#ifdef NDEBUG
-# define LOG(x) ((void) 0)
-#else
-# define LOG(x) do { fprintf(stderr, x), fputc('\n', stderr); } while (0)
-#endif /* NDEBUG */
-
-#endif /* _LIBNSGIF_LOG_H_ */
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=fe39d4611ccce7cdb7...
commit fe39d4611ccce7cdb7be11edf7a819888b6319a4
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
API: Use stdint types.
diff --git a/include/libnsgif.h b/include/libnsgif.h
index 309e2f0..b281034 100644
--- a/include/libnsgif.h
+++ b/include/libnsgif.h
@@ -37,12 +37,12 @@ typedef struct gif_frame {
/** whether the frame should be displayed/animated */
bool display;
/** delay (in cs) before animating the frame */
- unsigned int frame_delay;
+ uint32_t frame_delay;
/* Internal members are listed below */
/** offset (in bytes) to the GIF frame data */
- unsigned int frame_pointer;
+ uint32_t frame_pointer;
/** whether the frame has previously been used */
bool virgin;
/** whether the frame is totally opaque */
@@ -50,27 +50,27 @@ typedef struct gif_frame {
/** whether a full image redraw is required */
bool redraw_required;
/** how the previous frame should be disposed; affects plotting */
- unsigned char disposal_method;
+ uint8_t disposal_method;
/** whether we acknowledge transparency */
bool transparency;
/** the index designating a transparent pixel */
- unsigned int transparency_index;
+ uint32_t transparency_index;
/** x co-ordinate of redraw rectangle */
- unsigned int redraw_x;
+ uint32_t redraw_x;
/** y co-ordinate of redraw rectangle */
- unsigned int redraw_y;
+ uint32_t redraw_y;
/** width of redraw rectangle */
- unsigned int redraw_width;
+ uint32_t redraw_width;
/** height of redraw rectangle */
- unsigned int redraw_height;
+ uint32_t redraw_height;
/* Frame flags */
- unsigned int flags;
+ uint32_t flags;
} gif_frame;
/* API for Bitmap callbacks */
typedef void* (*gif_bitmap_cb_create)(int width, int height);
typedef void (*gif_bitmap_cb_destroy)(void *bitmap);
-typedef unsigned char* (*gif_bitmap_cb_get_buffer)(void *bitmap);
+typedef uint8_t* (*gif_bitmap_cb_get_buffer)(void *bitmap);
typedef void (*gif_bitmap_cb_set_opaque)(void *bitmap, bool opaque);
typedef bool (*gif_bitmap_cb_test_opaque)(void *bitmap);
typedef void (*gif_bitmap_cb_modified)(void *bitmap);
@@ -103,13 +103,13 @@ typedef struct gif_animation {
/** pointer to GIF data */
const uint8_t *gif_data;
/** width of GIF (may increase during decoding) */
- unsigned int width;
+ uint32_t width;
/** height of GIF (may increase during decoding) */
- unsigned int height;
+ uint32_t height;
/** number of frames decoded */
- unsigned int frame_count;
+ uint32_t frame_count;
/** number of frames partially decoded */
- unsigned int frame_count_partial;
+ uint32_t frame_count_partial;
/** decoded frames */
gif_frame *frames;
/** current frame decoded to bitmap */
@@ -122,27 +122,27 @@ typedef struct gif_animation {
/* Internal members are listed below */
/** current index into GIF data */
- unsigned int buffer_position;
+ uint32_t buffer_position;
/** total number of bytes of GIF data available */
- unsigned int buffer_size;
+ uint32_t buffer_size;
/** current number of frame holders */
- unsigned int frame_holders;
+ uint32_t frame_holders;
/** background index */
- unsigned int bg_index;
+ uint32_t bg_index;
/** background colour */
- unsigned int bg_colour;
+ uint32_t bg_colour;
/** image aspect ratio (ignored) */
- unsigned int aspect_ratio;
+ uint32_t aspect_ratio;
/** size of colour table (in entries) */
- unsigned int colour_table_size;
+ uint32_t colour_table_size;
/** whether the GIF has a global colour table */
bool global_colours;
/** global colour table */
- unsigned int *global_colour_table;
+ uint32_t *global_colour_table;
/** local colour table */
- unsigned int *local_colour_table;
+ uint32_t *local_colour_table;
/** current colour table */
- unsigned int *colour_table;
+ uint32_t *colour_table;
/** previous frame for GIF_FRAME_RESTORE */
void *prev_frame;
@@ -184,7 +184,7 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data);
* - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
* - GIF_OK for successful decoding
*/
-gif_result gif_decode_frame(gif_animation *gif, unsigned int frame);
+gif_result gif_decode_frame(gif_animation *gif, uint32_t frame);
/**
* Releases any workspace held by a gif
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 5011683..38816f5 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -1353,7 +1353,7 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
}
/* exported function documented in libnsgif.h */
-gif_result gif_decode_frame(gif_animation *gif, unsigned int frame)
+gif_result gif_decode_frame(gif_animation *gif, uint32_t frame)
{
return gif__process_frame(gif, frame, true);
}
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=e6be9591cfa6f901c6...
commit e6be9591cfa6f901c6f4c421a9aeaa08da986eaf
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
TEST: Style: Fix whitespace.
diff --git a/test/decode_gif.c b/test/decode_gif.c
index 52d73b6..dfc2b84 100644
--- a/test/decode_gif.c
+++ b/test/decode_gif.c
@@ -20,232 +20,224 @@
#define BYTES_PER_PIXEL 4
#define MAX_IMAGE_BYTES (48 * 1024 * 1024)
-
static void *bitmap_create(int width, int height)
{
- /* ensure a stupidly large bitmap is not created */
- if (((long long)width * (long long)height) > (MAX_IMAGE_BYTES/BYTES_PER_PIXEL)) {
- return NULL;
- }
- return calloc(width * height, BYTES_PER_PIXEL);
+ /* ensure a stupidly large bitmap is not created */
+ if (((long long)width * (long long)height) > (MAX_IMAGE_BYTES/BYTES_PER_PIXEL)) {
+ return NULL;
+ }
+ return calloc(width * height, BYTES_PER_PIXEL);
}
-
static void bitmap_set_opaque(void *bitmap, bool opaque)
{
- (void) opaque; /* unused */
- (void) bitmap; /* unused */
- assert(bitmap);
+ (void) opaque; /* unused */
+ (void) bitmap; /* unused */
+ assert(bitmap);
}
-
static bool bitmap_test_opaque(void *bitmap)
{
- (void) bitmap; /* unused */
- assert(bitmap);
- return false;
+ (void) bitmap; /* unused */
+ assert(bitmap);
+ return false;
}
-
static unsigned char *bitmap_get_buffer(void *bitmap)
{
- assert(bitmap);
- return bitmap;
+ assert(bitmap);
+ return bitmap;
}
-
static void bitmap_destroy(void *bitmap)
{
- assert(bitmap);
- free(bitmap);
+ assert(bitmap);
+ free(bitmap);
}
-
static void bitmap_modified(void *bitmap)
{
- (void) bitmap; /* unused */
- assert(bitmap);
- return;
+ (void) bitmap; /* unused */
+ assert(bitmap);
+ return;
}
static unsigned char *load_file(const char *path, size_t *data_size)
{
- FILE *fd;
- struct stat sb;
- unsigned char *buffer;
- size_t size;
- size_t n;
-
- fd = fopen(path, "rb");
- if (!fd) {
- perror(path);
- exit(EXIT_FAILURE);
- }
-
- if (stat(path, &sb)) {
- perror(path);
- exit(EXIT_FAILURE);
- }
- size = sb.st_size;
-
- buffer = malloc(size);
- if (!buffer) {
- fprintf(stderr, "Unable to allocate %lld bytes\n",
- (long long) size);
- exit(EXIT_FAILURE);
- }
-
- n = fread(buffer, 1, size, fd);
- if (n != size) {
- perror(path);
- exit(EXIT_FAILURE);
- }
-
- fclose(fd);
-
- *data_size = size;
- return buffer;
+ FILE *fd;
+ struct stat sb;
+ unsigned char *buffer;
+ size_t size;
+ size_t n;
+
+ fd = fopen(path, "rb");
+ if (!fd) {
+ perror(path);
+ exit(EXIT_FAILURE);
+ }
+
+ if (stat(path, &sb)) {
+ perror(path);
+ exit(EXIT_FAILURE);
+ }
+ size = sb.st_size;
+
+ buffer = malloc(size);
+ if (!buffer) {
+ fprintf(stderr, "Unable to allocate %lld bytes\n",
+ (long long) size);
+ exit(EXIT_FAILURE);
+ }
+
+ n = fread(buffer, 1, size, fd);
+ if (n != size) {
+ perror(path);
+ exit(EXIT_FAILURE);
+ }
+
+ fclose(fd);
+
+ *data_size = size;
+ return buffer;
}
-
static void warning(const char *context, gif_result code)
{
- fprintf(stderr, "%s failed: ", context);
- switch (code)
- {
- case GIF_FRAME_DATA_ERROR:
- fprintf(stderr, "GIF_FRAME_DATA_ERROR");
- break;
- case GIF_INSUFFICIENT_DATA:
- fprintf(stderr, "GIF_INSUFFICIENT_DATA");
- break;
- case GIF_DATA_ERROR:
- fprintf(stderr, "GIF_DATA_ERROR");
- break;
- case GIF_INSUFFICIENT_MEMORY:
- fprintf(stderr, "GIF_INSUFFICIENT_MEMORY");
- break;
- default:
- fprintf(stderr, "unknown code %i", code);
- break;
- }
- fprintf(stderr, "\n");
+ fprintf(stderr, "%s failed: ", context);
+ switch (code)
+ {
+ case GIF_FRAME_DATA_ERROR:
+ fprintf(stderr, "GIF_FRAME_DATA_ERROR");
+ break;
+ case GIF_INSUFFICIENT_DATA:
+ fprintf(stderr, "GIF_INSUFFICIENT_DATA");
+ break;
+ case GIF_DATA_ERROR:
+ fprintf(stderr, "GIF_DATA_ERROR");
+ break;
+ case GIF_INSUFFICIENT_MEMORY:
+ fprintf(stderr, "GIF_INSUFFICIENT_MEMORY");
+ break;
+ default:
+ fprintf(stderr, "unknown code %i", code);
+ break;
+ }
+ fprintf(stderr, "\n");
}
static void write_ppm(FILE* fh, const char *name, gif_animation *gif,
- bool no_write)
+ bool no_write)
{
- unsigned int i;
- gif_result code;
-
- if (!no_write) {
- fprintf(fh, "P3\n");
- fprintf(fh, "# %s\n", name);
- fprintf(fh, "# width %u \n", gif->width);
- fprintf(fh, "# height %u \n", gif->height);
- fprintf(fh, "# frame_count %u \n", gif->frame_count);
- fprintf(fh, "# frame_count_partial %u \n", gif->frame_count_partial);
- fprintf(fh, "# loop_count %u \n", gif->loop_count);
- fprintf(fh, "%u %u 256\n", gif->width, gif->height * gif->frame_count);
- }
-
- /* decode the frames */
- for (i = 0; i != gif->frame_count; i++) {
- unsigned int row, col;
- unsigned char *image;
-
- code = gif_decode_frame(gif, i);
- if (code != GIF_OK)
- warning("gif_decode_frame", code);
-
- if (!gif->frames[i].display) {
- continue;
- }
-
- if (!no_write) {
- fprintf(fh, "# frame %u:\n", i);
- image = (unsigned char *) gif->frame_image;
- for (row = 0; row != gif->height; row++) {
- for (col = 0; col != gif->width; col++) {
- size_t z = (row * gif->width + col) * 4;
- fprintf(fh, "%u %u %u ",
- (unsigned char) image[z],
- (unsigned char) image[z + 1],
- (unsigned char) image[z + 2]);
- }
- fprintf(fh, "\n");
- }
- }
- }
-
+ unsigned int i;
+ gif_result code;
+
+ if (!no_write) {
+ fprintf(fh, "P3\n");
+ fprintf(fh, "# %s\n", name);
+ fprintf(fh, "# width %u \n", gif->width);
+ fprintf(fh, "# height %u \n", gif->height);
+ fprintf(fh, "# frame_count %u \n", gif->frame_count);
+ fprintf(fh, "# frame_count_partial %u \n", gif->frame_count_partial);
+ fprintf(fh, "# loop_count %u \n", gif->loop_count);
+ fprintf(fh, "%u %u 256\n", gif->width, gif->height * gif->frame_count);
+ }
+
+ /* decode the frames */
+ for (i = 0; i != gif->frame_count; i++) {
+ unsigned int row, col;
+ unsigned char *image;
+
+ code = gif_decode_frame(gif, i);
+ if (code != GIF_OK)
+ warning("gif_decode_frame", code);
+
+ if (!gif->frames[i].display) {
+ continue;
+ }
+
+ if (!no_write) {
+ fprintf(fh, "# frame %u:\n", i);
+ image = (unsigned char *) gif->frame_image;
+ for (row = 0; row != gif->height; row++) {
+ for (col = 0; col != gif->width; col++) {
+ size_t z = (row * gif->width + col) * 4;
+ fprintf(fh, "%u %u %u ",
+ (unsigned char) image[z],
+ (unsigned char) image[z + 1],
+ (unsigned char) image[z + 2]);
+ }
+ fprintf(fh, "\n");
+ }
+ }
+ }
}
int main(int argc, char *argv[])
{
- gif_bitmap_callback_vt bitmap_callbacks = {
- bitmap_create,
- bitmap_destroy,
- bitmap_get_buffer,
- bitmap_set_opaque,
- bitmap_test_opaque,
- bitmap_modified
- };
- gif_animation gif;
- size_t size;
- gif_result code;
- unsigned char *data;
- FILE *outf = stdout;
- bool no_write = false;
-
- if (argc < 2) {
- fprintf(stderr, "Usage: %s image.gif [out]\n", argv[0]);
- fprintf(stderr, "\n");
- fprintf(stderr, "If [out] is NOWRITE, the gif will be docoded "
- "but not output.\n");
- fprintf(stderr, "Otherwise [out] is an output filename.\n");
- fprintf(stderr, "When [out] is unset, output is to stdout.\n");
-
- return 1;
- }
-
- if (argc > 2) {
- if (strcmp(argv[2], "NOWRITE") == 0) {
- no_write = true;
- } else {
- outf = fopen(argv[2], "w+");
- if (outf == NULL) {
- fprintf(stderr, "Unable to open %s for writing\n", argv[2]);
- return 2;
- }
- }
- }
-
- /* create our gif animation */
- gif_create(&gif, &bitmap_callbacks);
-
- /* load file into memory */
- data = load_file(argv[1], &size);
-
- /* begin decoding */
- do {
- code = gif_initialise(&gif, size, data);
- if (code != GIF_OK && code != GIF_WORKING) {
- warning("gif_initialise", code);
- gif_finalise(&gif);
- free(data);
- return 1;
- }
- } while (code != GIF_OK);
-
- write_ppm(outf, argv[1], &gif, no_write);
-
- if (argc > 2 && !no_write) {
- fclose(outf);
- }
-
- /* clean up */
- gif_finalise(&gif);
- free(data);
-
- return 0;
+ gif_bitmap_callback_vt bitmap_callbacks = {
+ bitmap_create,
+ bitmap_destroy,
+ bitmap_get_buffer,
+ bitmap_set_opaque,
+ bitmap_test_opaque,
+ bitmap_modified
+ };
+ gif_animation gif;
+ size_t size;
+ gif_result code;
+ unsigned char *data;
+ FILE *outf = stdout;
+ bool no_write = false;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s image.gif [out]\n", argv[0]);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "If [out] is NOWRITE, the gif will be docoded "
+ "but not output.\n");
+ fprintf(stderr, "Otherwise [out] is an output filename.\n");
+ fprintf(stderr, "When [out] is unset, output is to stdout.\n");
+
+ return 1;
+ }
+
+ if (argc > 2) {
+ if (strcmp(argv[2], "NOWRITE") == 0) {
+ no_write = true;
+ } else {
+ outf = fopen(argv[2], "w+");
+ if (outf == NULL) {
+ fprintf(stderr, "Unable to open %s for writing\n", argv[2]);
+ return 2;
+ }
+ }
+ }
+
+ /* create our gif animation */
+ gif_create(&gif, &bitmap_callbacks);
+
+ /* load file into memory */
+ data = load_file(argv[1], &size);
+
+ /* begin decoding */
+ do {
+ code = gif_initialise(&gif, size, data);
+ if (code != GIF_OK && code != GIF_WORKING) {
+ warning("gif_initialise", code);
+ gif_finalise(&gif);
+ free(data);
+ return 1;
+ }
+ } while (code != GIF_OK);
+
+ write_ppm(outf, argv[1], &gif, no_write);
+
+ if (argc > 2 && !no_write) {
+ fclose(outf);
+ }
+
+ /* clean up */
+ gif_finalise(&gif);
+ free(data);
+
+ return 0;
}
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=de3510f1215dd16b21...
commit de3510f1215dd16b215bd34ffe2cc81a34f67985
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Style: Use double underscore for remaining static functions.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 6f59b3a..5011683 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -67,7 +67,13 @@ enum gif_disposal {
#define GIF_BLOCK_TERMINATOR 0x00
#define GIF_TRAILER 0x3b
-static gif_result gif_error_from_lzw(lzw_result l_res)
+/**
+ * Convert an LZW result code to equivalent GIF result code.
+ *
+ * \param[in] l_res LZW response code.
+ * \return GIF result code.
+ */
+static gif_result gif__error_from_lzw(lzw_result l_res)
{
static const gif_result g_res[] = {
[LZW_OK] = GIF_OK,
@@ -91,7 +97,7 @@ static gif_result gif_error_from_lzw(lzw_result l_res)
* \param height The height of the sprite
* \return GIF_INSUFFICIENT_MEMORY for a memory error GIF_OK for success
*/
-static gif_result gif_initialise_sprite(
+static gif_result gif__initialise_sprite(
struct gif_animation *gif,
uint32_t width,
uint32_t height)
@@ -122,7 +128,7 @@ static inline uint32_t* gif__bitmap_get(
gif_result ret;
/* Make sure we have a buffer to decode to. */
- ret = gif_initialise_sprite(gif, gif->width, gif->height);
+ ret = gif__initialise_sprite(gif, gif->width, gif->height);
if (ret != GIF_OK) {
return NULL;
}
@@ -323,7 +329,7 @@ static gif_result gif__decode_complex(
gif->gif_data, gif->buffer_size,
data + 1 - gif->gif_data);
if (res != LZW_OK) {
- return gif_error_from_lzw(res);
+ return gif__error_from_lzw(res);
}
do {
@@ -343,7 +349,7 @@ static gif_result gif__decode_complex(
if (res == LZW_OK_EOD) {
ret = GIF_OK;
} else {
- ret = gif_error_from_lzw(res);
+ ret = gif__error_from_lzw(res);
}
break;
}
@@ -396,7 +402,7 @@ static gif_result gif__decode_simple(
gif->gif_data, gif->buffer_size,
data + 1 - gif->gif_data);
if (res != LZW_OK) {
- return gif_error_from_lzw(res);
+ return gif__error_from_lzw(res);
}
frame_data += (offset_y * gif->width);
@@ -411,7 +417,7 @@ static gif_result gif__decode_simple(
if (res == LZW_OK_EOD) {
ret = GIF_OK;
} else {
- ret = gif_error_from_lzw(res);
+ ret = gif__error_from_lzw(res);
}
break;
}
@@ -1334,7 +1340,7 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
lzw_result res = lzw_context_create(
(struct lzw_ctx **)&gif->lzw_ctx);
if (res != LZW_OK) {
- return gif_error_from_lzw(res);
+ return gif__error_from_lzw(res);
}
}
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=125cd0409ab9d2d13f...
commit 125cd0409ab9d2d13fc41fcfae2fe001e4241878
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Update code documentation.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 6c21a7c..6f59b3a 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -150,7 +150,7 @@ static inline void gif__bitmap_modified(
* Helper to tell the client that whether the bitmap is opaque.
*
* \param[in] gif The gif object we're decoding.
- * \param[in] frmae The frame that has been decoded.
+ * \param[in] frame The frame that has been decoded.
*/
static inline void gif__bitmap_set_opaque(
const struct gif_animation *gif,
@@ -646,6 +646,7 @@ static gif_result gif__parse_extension_application(
*
* \param[in] gif The gif object we're decoding.
* \param[in] frame The frame to parse extensions for.
+ * \param[in] pos Current position in data, updated on exit.
* \param[in] decode Whether to decode or skip over the extension.
* \return GIF_INSUFFICIENT_DATA if more data is needed,
* GIF_OK for success.
@@ -754,8 +755,10 @@ static gif_result gif__parse_frame_extensions(
* 2BITS Reserved
* 3BITS Size of Local Colour Table
*
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to parse an image descriptor for.
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to parse an image descriptor for.
+ * \param[in] pos Current position in data, updated on exit.
+ * \param[in] decode Whether to decode the image descriptor.
* \return GIF_OK on success, appropriate error otherwise.
*/
static gif_result gif__parse_image_descriptor(
@@ -858,8 +861,10 @@ static gif_result gif__colour_table_extract(
*
* Sets up gif->colour_table for the frame.
*
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to get the colour table for.
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to get the colour table for.
+ * \param[in] pos Current position in data, updated on exit.
+ * \param[in] decode Whether to decode the colour table.
* \return GIF_OK on success, appropriate error otherwise.
*/
static gif_result gif__parse_colour_table(
@@ -894,8 +899,10 @@ static gif_result gif__parse_colour_table(
*
* Sets up gif->colour_table for the frame.
*
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to get the colour table for.
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to parse image data for.
+ * \param[in] pos Current position in data, updated on exit.
+ * \param[in] decode Whether to decode the image data.
* \return GIF_OK on success, appropriate error otherwise.
*/
static gif_result gif__parse_image_data(
-----------------------------------------------------------------------
Summary of changes:
include/libnsgif.h | 50 +++----
src/libnsgif.c | 55 ++++----
src/utils/log.h | 21 ---
test/decode_gif.c | 372 +++++++++++++++++++++++++---------------------------
4 files changed, 240 insertions(+), 258 deletions(-)
delete mode 100644 src/utils/log.h
diff --git a/include/libnsgif.h b/include/libnsgif.h
index 309e2f0..b281034 100644
--- a/include/libnsgif.h
+++ b/include/libnsgif.h
@@ -37,12 +37,12 @@ typedef struct gif_frame {
/** whether the frame should be displayed/animated */
bool display;
/** delay (in cs) before animating the frame */
- unsigned int frame_delay;
+ uint32_t frame_delay;
/* Internal members are listed below */
/** offset (in bytes) to the GIF frame data */
- unsigned int frame_pointer;
+ uint32_t frame_pointer;
/** whether the frame has previously been used */
bool virgin;
/** whether the frame is totally opaque */
@@ -50,27 +50,27 @@ typedef struct gif_frame {
/** whether a full image redraw is required */
bool redraw_required;
/** how the previous frame should be disposed; affects plotting */
- unsigned char disposal_method;
+ uint8_t disposal_method;
/** whether we acknowledge transparency */
bool transparency;
/** the index designating a transparent pixel */
- unsigned int transparency_index;
+ uint32_t transparency_index;
/** x co-ordinate of redraw rectangle */
- unsigned int redraw_x;
+ uint32_t redraw_x;
/** y co-ordinate of redraw rectangle */
- unsigned int redraw_y;
+ uint32_t redraw_y;
/** width of redraw rectangle */
- unsigned int redraw_width;
+ uint32_t redraw_width;
/** height of redraw rectangle */
- unsigned int redraw_height;
+ uint32_t redraw_height;
/* Frame flags */
- unsigned int flags;
+ uint32_t flags;
} gif_frame;
/* API for Bitmap callbacks */
typedef void* (*gif_bitmap_cb_create)(int width, int height);
typedef void (*gif_bitmap_cb_destroy)(void *bitmap);
-typedef unsigned char* (*gif_bitmap_cb_get_buffer)(void *bitmap);
+typedef uint8_t* (*gif_bitmap_cb_get_buffer)(void *bitmap);
typedef void (*gif_bitmap_cb_set_opaque)(void *bitmap, bool opaque);
typedef bool (*gif_bitmap_cb_test_opaque)(void *bitmap);
typedef void (*gif_bitmap_cb_modified)(void *bitmap);
@@ -103,13 +103,13 @@ typedef struct gif_animation {
/** pointer to GIF data */
const uint8_t *gif_data;
/** width of GIF (may increase during decoding) */
- unsigned int width;
+ uint32_t width;
/** height of GIF (may increase during decoding) */
- unsigned int height;
+ uint32_t height;
/** number of frames decoded */
- unsigned int frame_count;
+ uint32_t frame_count;
/** number of frames partially decoded */
- unsigned int frame_count_partial;
+ uint32_t frame_count_partial;
/** decoded frames */
gif_frame *frames;
/** current frame decoded to bitmap */
@@ -122,27 +122,27 @@ typedef struct gif_animation {
/* Internal members are listed below */
/** current index into GIF data */
- unsigned int buffer_position;
+ uint32_t buffer_position;
/** total number of bytes of GIF data available */
- unsigned int buffer_size;
+ uint32_t buffer_size;
/** current number of frame holders */
- unsigned int frame_holders;
+ uint32_t frame_holders;
/** background index */
- unsigned int bg_index;
+ uint32_t bg_index;
/** background colour */
- unsigned int bg_colour;
+ uint32_t bg_colour;
/** image aspect ratio (ignored) */
- unsigned int aspect_ratio;
+ uint32_t aspect_ratio;
/** size of colour table (in entries) */
- unsigned int colour_table_size;
+ uint32_t colour_table_size;
/** whether the GIF has a global colour table */
bool global_colours;
/** global colour table */
- unsigned int *global_colour_table;
+ uint32_t *global_colour_table;
/** local colour table */
- unsigned int *local_colour_table;
+ uint32_t *local_colour_table;
/** current colour table */
- unsigned int *colour_table;
+ uint32_t *colour_table;
/** previous frame for GIF_FRAME_RESTORE */
void *prev_frame;
@@ -184,7 +184,7 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data);
* - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
* - GIF_OK for successful decoding
*/
-gif_result gif_decode_frame(gif_animation *gif, unsigned int frame);
+gif_result gif_decode_frame(gif_animation *gif, uint32_t frame);
/**
* Releases any workspace held by a gif
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 6c21a7c..ecddf78 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -8,16 +8,14 @@
* http://www.opensource.org/licenses/mit-license.php
*/
-#include <stdbool.h>
+#include <assert.h>
#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
#include <stdlib.h>
-#include <assert.h>
-#include "libnsgif.h"
-#include "utils/log.h"
+#include <string.h>
+#include <stdbool.h>
#include "lzw.h"
+#include "libnsgif.h"
/**
*
@@ -67,7 +65,13 @@ enum gif_disposal {
#define GIF_BLOCK_TERMINATOR 0x00
#define GIF_TRAILER 0x3b
-static gif_result gif_error_from_lzw(lzw_result l_res)
+/**
+ * Convert an LZW result code to equivalent GIF result code.
+ *
+ * \param[in] l_res LZW response code.
+ * \return GIF result code.
+ */
+static gif_result gif__error_from_lzw(lzw_result l_res)
{
static const gif_result g_res[] = {
[LZW_OK] = GIF_OK,
@@ -91,7 +95,7 @@ static gif_result gif_error_from_lzw(lzw_result l_res)
* \param height The height of the sprite
* \return GIF_INSUFFICIENT_MEMORY for a memory error GIF_OK for success
*/
-static gif_result gif_initialise_sprite(
+static gif_result gif__initialise_sprite(
struct gif_animation *gif,
uint32_t width,
uint32_t height)
@@ -122,7 +126,7 @@ static inline uint32_t* gif__bitmap_get(
gif_result ret;
/* Make sure we have a buffer to decode to. */
- ret = gif_initialise_sprite(gif, gif->width, gif->height);
+ ret = gif__initialise_sprite(gif, gif->width, gif->height);
if (ret != GIF_OK) {
return NULL;
}
@@ -150,7 +154,7 @@ static inline void gif__bitmap_modified(
* Helper to tell the client that whether the bitmap is opaque.
*
* \param[in] gif The gif object we're decoding.
- * \param[in] frmae The frame that has been decoded.
+ * \param[in] frame The frame that has been decoded.
*/
static inline void gif__bitmap_set_opaque(
const struct gif_animation *gif,
@@ -323,7 +327,7 @@ static gif_result gif__decode_complex(
gif->gif_data, gif->buffer_size,
data + 1 - gif->gif_data);
if (res != LZW_OK) {
- return gif_error_from_lzw(res);
+ return gif__error_from_lzw(res);
}
do {
@@ -343,7 +347,7 @@ static gif_result gif__decode_complex(
if (res == LZW_OK_EOD) {
ret = GIF_OK;
} else {
- ret = gif_error_from_lzw(res);
+ ret = gif__error_from_lzw(res);
}
break;
}
@@ -396,7 +400,7 @@ static gif_result gif__decode_simple(
gif->gif_data, gif->buffer_size,
data + 1 - gif->gif_data);
if (res != LZW_OK) {
- return gif_error_from_lzw(res);
+ return gif__error_from_lzw(res);
}
frame_data += (offset_y * gif->width);
@@ -411,7 +415,7 @@ static gif_result gif__decode_simple(
if (res == LZW_OK_EOD) {
ret = GIF_OK;
} else {
- ret = gif_error_from_lzw(res);
+ ret = gif__error_from_lzw(res);
}
break;
}
@@ -646,6 +650,7 @@ static gif_result gif__parse_extension_application(
*
* \param[in] gif The gif object we're decoding.
* \param[in] frame The frame to parse extensions for.
+ * \param[in] pos Current position in data, updated on exit.
* \param[in] decode Whether to decode or skip over the extension.
* \return GIF_INSUFFICIENT_DATA if more data is needed,
* GIF_OK for success.
@@ -754,8 +759,10 @@ static gif_result gif__parse_frame_extensions(
* 2BITS Reserved
* 3BITS Size of Local Colour Table
*
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to parse an image descriptor for.
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to parse an image descriptor for.
+ * \param[in] pos Current position in data, updated on exit.
+ * \param[in] decode Whether to decode the image descriptor.
* \return GIF_OK on success, appropriate error otherwise.
*/
static gif_result gif__parse_image_descriptor(
@@ -858,8 +865,10 @@ static gif_result gif__colour_table_extract(
*
* Sets up gif->colour_table for the frame.
*
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to get the colour table for.
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to get the colour table for.
+ * \param[in] pos Current position in data, updated on exit.
+ * \param[in] decode Whether to decode the colour table.
* \return GIF_OK on success, appropriate error otherwise.
*/
static gif_result gif__parse_colour_table(
@@ -894,8 +903,10 @@ static gif_result gif__parse_colour_table(
*
* Sets up gif->colour_table for the frame.
*
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to get the colour table for.
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to parse image data for.
+ * \param[in] pos Current position in data, updated on exit.
+ * \param[in] decode Whether to decode the image data.
* \return GIF_OK on success, appropriate error otherwise.
*/
static gif_result gif__parse_image_data(
@@ -1327,7 +1338,7 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
lzw_result res = lzw_context_create(
(struct lzw_ctx **)&gif->lzw_ctx);
if (res != LZW_OK) {
- return gif_error_from_lzw(res);
+ return gif__error_from_lzw(res);
}
}
@@ -1340,7 +1351,7 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
}
/* exported function documented in libnsgif.h */
-gif_result gif_decode_frame(gif_animation *gif, unsigned int frame)
+gif_result gif_decode_frame(gif_animation *gif, uint32_t frame)
{
return gif__process_frame(gif, frame, true);
}
diff --git a/src/utils/log.h b/src/utils/log.h
deleted file mode 100644
index 1413374..0000000
--- a/src/utils/log.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright 2003 James Bursa <bursa(a)users.sourceforge.net>
- * Copyright 2004 John Tytgat <John.Tytgat(a)aaug.net>
- *
- * This file is part of NetSurf, http://www.netsurf-browser.org/
- * Licenced under the MIT License,
- * http://www.opensource.org/licenses/mit-license.php
- */
-
-#include <stdio.h>
-
-#ifndef _LIBNSGIF_LOG_H_
-#define _LIBNSGIF_LOG_H_
-
-#ifdef NDEBUG
-# define LOG(x) ((void) 0)
-#else
-# define LOG(x) do { fprintf(stderr, x), fputc('\n', stderr); } while (0)
-#endif /* NDEBUG */
-
-#endif /* _LIBNSGIF_LOG_H_ */
diff --git a/test/decode_gif.c b/test/decode_gif.c
index 52d73b6..dfc2b84 100644
--- a/test/decode_gif.c
+++ b/test/decode_gif.c
@@ -20,232 +20,224 @@
#define BYTES_PER_PIXEL 4
#define MAX_IMAGE_BYTES (48 * 1024 * 1024)
-
static void *bitmap_create(int width, int height)
{
- /* ensure a stupidly large bitmap is not created */
- if (((long long)width * (long long)height) > (MAX_IMAGE_BYTES/BYTES_PER_PIXEL)) {
- return NULL;
- }
- return calloc(width * height, BYTES_PER_PIXEL);
+ /* ensure a stupidly large bitmap is not created */
+ if (((long long)width * (long long)height) > (MAX_IMAGE_BYTES/BYTES_PER_PIXEL)) {
+ return NULL;
+ }
+ return calloc(width * height, BYTES_PER_PIXEL);
}
-
static void bitmap_set_opaque(void *bitmap, bool opaque)
{
- (void) opaque; /* unused */
- (void) bitmap; /* unused */
- assert(bitmap);
+ (void) opaque; /* unused */
+ (void) bitmap; /* unused */
+ assert(bitmap);
}
-
static bool bitmap_test_opaque(void *bitmap)
{
- (void) bitmap; /* unused */
- assert(bitmap);
- return false;
+ (void) bitmap; /* unused */
+ assert(bitmap);
+ return false;
}
-
static unsigned char *bitmap_get_buffer(void *bitmap)
{
- assert(bitmap);
- return bitmap;
+ assert(bitmap);
+ return bitmap;
}
-
static void bitmap_destroy(void *bitmap)
{
- assert(bitmap);
- free(bitmap);
+ assert(bitmap);
+ free(bitmap);
}
-
static void bitmap_modified(void *bitmap)
{
- (void) bitmap; /* unused */
- assert(bitmap);
- return;
+ (void) bitmap; /* unused */
+ assert(bitmap);
+ return;
}
static unsigned char *load_file(const char *path, size_t *data_size)
{
- FILE *fd;
- struct stat sb;
- unsigned char *buffer;
- size_t size;
- size_t n;
-
- fd = fopen(path, "rb");
- if (!fd) {
- perror(path);
- exit(EXIT_FAILURE);
- }
-
- if (stat(path, &sb)) {
- perror(path);
- exit(EXIT_FAILURE);
- }
- size = sb.st_size;
-
- buffer = malloc(size);
- if (!buffer) {
- fprintf(stderr, "Unable to allocate %lld bytes\n",
- (long long) size);
- exit(EXIT_FAILURE);
- }
-
- n = fread(buffer, 1, size, fd);
- if (n != size) {
- perror(path);
- exit(EXIT_FAILURE);
- }
-
- fclose(fd);
-
- *data_size = size;
- return buffer;
+ FILE *fd;
+ struct stat sb;
+ unsigned char *buffer;
+ size_t size;
+ size_t n;
+
+ fd = fopen(path, "rb");
+ if (!fd) {
+ perror(path);
+ exit(EXIT_FAILURE);
+ }
+
+ if (stat(path, &sb)) {
+ perror(path);
+ exit(EXIT_FAILURE);
+ }
+ size = sb.st_size;
+
+ buffer = malloc(size);
+ if (!buffer) {
+ fprintf(stderr, "Unable to allocate %lld bytes\n",
+ (long long) size);
+ exit(EXIT_FAILURE);
+ }
+
+ n = fread(buffer, 1, size, fd);
+ if (n != size) {
+ perror(path);
+ exit(EXIT_FAILURE);
+ }
+
+ fclose(fd);
+
+ *data_size = size;
+ return buffer;
}
-
static void warning(const char *context, gif_result code)
{
- fprintf(stderr, "%s failed: ", context);
- switch (code)
- {
- case GIF_FRAME_DATA_ERROR:
- fprintf(stderr, "GIF_FRAME_DATA_ERROR");
- break;
- case GIF_INSUFFICIENT_DATA:
- fprintf(stderr, "GIF_INSUFFICIENT_DATA");
- break;
- case GIF_DATA_ERROR:
- fprintf(stderr, "GIF_DATA_ERROR");
- break;
- case GIF_INSUFFICIENT_MEMORY:
- fprintf(stderr, "GIF_INSUFFICIENT_MEMORY");
- break;
- default:
- fprintf(stderr, "unknown code %i", code);
- break;
- }
- fprintf(stderr, "\n");
+ fprintf(stderr, "%s failed: ", context);
+ switch (code)
+ {
+ case GIF_FRAME_DATA_ERROR:
+ fprintf(stderr, "GIF_FRAME_DATA_ERROR");
+ break;
+ case GIF_INSUFFICIENT_DATA:
+ fprintf(stderr, "GIF_INSUFFICIENT_DATA");
+ break;
+ case GIF_DATA_ERROR:
+ fprintf(stderr, "GIF_DATA_ERROR");
+ break;
+ case GIF_INSUFFICIENT_MEMORY:
+ fprintf(stderr, "GIF_INSUFFICIENT_MEMORY");
+ break;
+ default:
+ fprintf(stderr, "unknown code %i", code);
+ break;
+ }
+ fprintf(stderr, "\n");
}
static void write_ppm(FILE* fh, const char *name, gif_animation *gif,
- bool no_write)
+ bool no_write)
{
- unsigned int i;
- gif_result code;
-
- if (!no_write) {
- fprintf(fh, "P3\n");
- fprintf(fh, "# %s\n", name);
- fprintf(fh, "# width %u \n", gif->width);
- fprintf(fh, "# height %u \n", gif->height);
- fprintf(fh, "# frame_count %u \n", gif->frame_count);
- fprintf(fh, "# frame_count_partial %u \n", gif->frame_count_partial);
- fprintf(fh, "# loop_count %u \n", gif->loop_count);
- fprintf(fh, "%u %u 256\n", gif->width, gif->height * gif->frame_count);
- }
-
- /* decode the frames */
- for (i = 0; i != gif->frame_count; i++) {
- unsigned int row, col;
- unsigned char *image;
-
- code = gif_decode_frame(gif, i);
- if (code != GIF_OK)
- warning("gif_decode_frame", code);
-
- if (!gif->frames[i].display) {
- continue;
- }
-
- if (!no_write) {
- fprintf(fh, "# frame %u:\n", i);
- image = (unsigned char *) gif->frame_image;
- for (row = 0; row != gif->height; row++) {
- for (col = 0; col != gif->width; col++) {
- size_t z = (row * gif->width + col) * 4;
- fprintf(fh, "%u %u %u ",
- (unsigned char) image[z],
- (unsigned char) image[z + 1],
- (unsigned char) image[z + 2]);
- }
- fprintf(fh, "\n");
- }
- }
- }
-
+ unsigned int i;
+ gif_result code;
+
+ if (!no_write) {
+ fprintf(fh, "P3\n");
+ fprintf(fh, "# %s\n", name);
+ fprintf(fh, "# width %u \n", gif->width);
+ fprintf(fh, "# height %u \n", gif->height);
+ fprintf(fh, "# frame_count %u \n", gif->frame_count);
+ fprintf(fh, "# frame_count_partial %u \n", gif->frame_count_partial);
+ fprintf(fh, "# loop_count %u \n", gif->loop_count);
+ fprintf(fh, "%u %u 256\n", gif->width, gif->height * gif->frame_count);
+ }
+
+ /* decode the frames */
+ for (i = 0; i != gif->frame_count; i++) {
+ unsigned int row, col;
+ unsigned char *image;
+
+ code = gif_decode_frame(gif, i);
+ if (code != GIF_OK)
+ warning("gif_decode_frame", code);
+
+ if (!gif->frames[i].display) {
+ continue;
+ }
+
+ if (!no_write) {
+ fprintf(fh, "# frame %u:\n", i);
+ image = (unsigned char *) gif->frame_image;
+ for (row = 0; row != gif->height; row++) {
+ for (col = 0; col != gif->width; col++) {
+ size_t z = (row * gif->width + col) * 4;
+ fprintf(fh, "%u %u %u ",
+ (unsigned char) image[z],
+ (unsigned char) image[z + 1],
+ (unsigned char) image[z + 2]);
+ }
+ fprintf(fh, "\n");
+ }
+ }
+ }
}
int main(int argc, char *argv[])
{
- gif_bitmap_callback_vt bitmap_callbacks = {
- bitmap_create,
- bitmap_destroy,
- bitmap_get_buffer,
- bitmap_set_opaque,
- bitmap_test_opaque,
- bitmap_modified
- };
- gif_animation gif;
- size_t size;
- gif_result code;
- unsigned char *data;
- FILE *outf = stdout;
- bool no_write = false;
-
- if (argc < 2) {
- fprintf(stderr, "Usage: %s image.gif [out]\n", argv[0]);
- fprintf(stderr, "\n");
- fprintf(stderr, "If [out] is NOWRITE, the gif will be docoded "
- "but not output.\n");
- fprintf(stderr, "Otherwise [out] is an output filename.\n");
- fprintf(stderr, "When [out] is unset, output is to stdout.\n");
-
- return 1;
- }
-
- if (argc > 2) {
- if (strcmp(argv[2], "NOWRITE") == 0) {
- no_write = true;
- } else {
- outf = fopen(argv[2], "w+");
- if (outf == NULL) {
- fprintf(stderr, "Unable to open %s for writing\n", argv[2]);
- return 2;
- }
- }
- }
-
- /* create our gif animation */
- gif_create(&gif, &bitmap_callbacks);
-
- /* load file into memory */
- data = load_file(argv[1], &size);
-
- /* begin decoding */
- do {
- code = gif_initialise(&gif, size, data);
- if (code != GIF_OK && code != GIF_WORKING) {
- warning("gif_initialise", code);
- gif_finalise(&gif);
- free(data);
- return 1;
- }
- } while (code != GIF_OK);
-
- write_ppm(outf, argv[1], &gif, no_write);
-
- if (argc > 2 && !no_write) {
- fclose(outf);
- }
-
- /* clean up */
- gif_finalise(&gif);
- free(data);
-
- return 0;
+ gif_bitmap_callback_vt bitmap_callbacks = {
+ bitmap_create,
+ bitmap_destroy,
+ bitmap_get_buffer,
+ bitmap_set_opaque,
+ bitmap_test_opaque,
+ bitmap_modified
+ };
+ gif_animation gif;
+ size_t size;
+ gif_result code;
+ unsigned char *data;
+ FILE *outf = stdout;
+ bool no_write = false;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s image.gif [out]\n", argv[0]);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "If [out] is NOWRITE, the gif will be docoded "
+ "but not output.\n");
+ fprintf(stderr, "Otherwise [out] is an output filename.\n");
+ fprintf(stderr, "When [out] is unset, output is to stdout.\n");
+
+ return 1;
+ }
+
+ if (argc > 2) {
+ if (strcmp(argv[2], "NOWRITE") == 0) {
+ no_write = true;
+ } else {
+ outf = fopen(argv[2], "w+");
+ if (outf == NULL) {
+ fprintf(stderr, "Unable to open %s for writing\n", argv[2]);
+ return 2;
+ }
+ }
+ }
+
+ /* create our gif animation */
+ gif_create(&gif, &bitmap_callbacks);
+
+ /* load file into memory */
+ data = load_file(argv[1], &size);
+
+ /* begin decoding */
+ do {
+ code = gif_initialise(&gif, size, data);
+ if (code != GIF_OK && code != GIF_WORKING) {
+ warning("gif_initialise", code);
+ gif_finalise(&gif);
+ free(data);
+ return 1;
+ }
+ } while (code != GIF_OK);
+
+ write_ppm(outf, argv[1], &gif, no_write);
+
+ if (argc > 2 && !no_write) {
+ fclose(outf);
+ }
+
+ /* clean up */
+ gif_finalise(&gif);
+ free(data);
+
+ return 0;
}
--
NetSurf GIF Decoder
1 year, 10 months
libnsgif: branch master updated. release/0.2.1-111-g032e62c
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/libnsgif.git/shortlog/032e62c80df60b5fcedc...
...commit http://git.netsurf-browser.org/libnsgif.git/commit/032e62c80df60b5fcedcd7...
...tree http://git.netsurf-browser.org/libnsgif.git/tree/032e62c80df60b5fcedcd75f...
The branch, master has been updated
via 032e62c80df60b5fcedcd75f3043e0a18e114b6e (commit)
via 4f20f753689e93c8448c58fd9c4245e0817298fa (commit)
via 2487a3b82a88ce5d2c44028ebb1e86e22a2ed179 (commit)
via 16b5148bb734d9b7bb39419d81294c681b04b556 (commit)
via 2b2fa970ff094ae4ffc219603a793cb54a960480 (commit)
via 893c7c14c9ff58ecfd2d370e20e15eac6da779db (commit)
via c240b835fe6e3774237b6d968ddb405c8085690f (commit)
via 15670d3d0e3dcf9db168244428cab8e43460251a (commit)
via e2458d5e968323735a2a836da5f5838e62caf6b6 (commit)
via 470c70dac58c146ac280f6c7920764e59a885a42 (commit)
via 9f28b76c419889ef76832bcb65912acd42f9e685 (commit)
via b7952d1c53dffeced0feb9d75e66da972d68318a (commit)
via 69581f88b3dd5be49b4de539e3a836006a988235 (commit)
via 1b0c8759f5d96e96ded4cb50bb2772103780e2eb (commit)
via d1762afcee8b6541a974eeb37edea2a828096b8a (commit)
via 947586eb1b14c267ee996ba655af13899488c0f3 (commit)
via 24c503f95e1f9ef726e546e616649120b666d15d (commit)
via 5e0642811e2edce8d8845fdf0bcb8f9eefc71f80 (commit)
via 9f5e2f1af86d35247895dd57bf03f32c5539a04d (commit)
via 5e93ea19730e0df42e9dc3a777cde466f5a8f73d (commit)
via 920ba6c423181ee0eec932c5daeda75fb13fc140 (commit)
via 1b8341cb075731cffbf69141c24d8954fef4ee50 (commit)
via 8a5130c5559989c3b9ea6f74469427abe08c7193 (commit)
via 82d8eff773bbb56cd2021187949e39fcc28899c7 (commit)
via 677ba295b37cc1671f40383f92ad133fe6db26d9 (commit)
via 1e4a781b695992fbe09459baa3518637fba96223 (commit)
via c17ab37852fadbc6fe7ae80a969aa408f3d809b1 (commit)
via 0f7bcedeedff2cdee82644213741b5b96d655d92 (commit)
via 22b5b8f627a770172fbcc9e37656dcf40836f5df (commit)
via 1587b6a1a936ba11dfc951d07766562dbb4e8cf9 (commit)
via d96f028f305b5286618dcfc682e0de2515b3b7bd (commit)
via 2d440af6eed4d0ff82b8ef5143c7d2aee3856cc8 (commit)
via ea368d768fa12bffd51b0c8f38c374c5d398c859 (commit)
via 999dd692409595601648495543e16e70bc946e45 (commit)
via 1fbc97ea763c3f76eea43695637fe283ce831746 (commit)
via 41f33c7495e7b83b40a93e4a76481d770f0c5f7b (commit)
via 42cfb0d9a5b4e6dbf0981262916e160be438157b (commit)
via 6765615f800be363c88f085f7b2ebf1dd3d68b26 (commit)
via 58efffc9ec484899333458746bc169b03d69293b (commit)
via b8ae276840d1ad997d2400b001d2ca47b94ed4a7 (commit)
via a13205c215f879c0e63d736fca2f56028b243ffa (commit)
via 1f87439ecacaecbf70fdd740708745b1b90cad89 (commit)
via 827903b2cc1222b476c7f47bb462f526cb14a52c (commit)
via eb855e5e40417b0c3c54c937df7b35940288fd00 (commit)
via 496870618e9977cfcac4b0433bf17b6e8e4d2434 (commit)
via 8b30f0709564df422c549cccc1a7868588c30a67 (commit)
via eb0aa2326fd1da0bf4c74d083f889eec1632e1dd (commit)
via 811922e7a5c7de1a83a016ce76d0ea009b26d9fa (commit)
via e1c7bbf7fa9e762ac406310c62c3e30cde40f998 (commit)
via d78b6d12479e2567da7e078d52d9bc8fd0ad587c (commit)
via 838a69e759309db257c2ece5102ad732590847db (commit)
via bca8f74754f2316f40374368edad191a6803a1aa (commit)
via 6661c46dc8f836052785ef5db1d363b4a0bfcf25 (commit)
via b6d35c01d3955c4c247c710187f14c1ca2e4cd94 (commit)
via 623953b935cc9910e3cb7df178773a6092723913 (commit)
via 6a5c2a60d82fa39d1d653ced3c25eb15b3a0007f (commit)
via 2aa9bae406866de8dfeea142e4e02c619a20ae23 (commit)
via 323d6e3827258c8db1d0e49be9ac154d253efece (commit)
via cc492166b9988972b87492fb37b92c759de6b4de (commit)
via e60c1f6c8de7e154c12019140f92bd05b8966194 (commit)
via 4e957db96569b680d7db86ac1a072f0805ee512b (commit)
via e9a47faf8062f9b7db406c273fcbafa2aa6388e1 (commit)
via 46d7a0bb13a36dabaa5ede2158d22e3e702f5ad2 (commit)
via 8eac839eed55432446077659c052b6459509af2a (commit)
via 12580f6d6737c7a549f465cd76183798cb6539bc (commit)
via 6344f78d4faac7f141edd2ecb21223f95988f582 (commit)
via 5af2a3dddfebff63742bfd83618d037b4d95bca2 (commit)
via bd01b79660e5c41e04180ad291bf82619d420c79 (commit)
via 3d3dd3d63af6aa0c7ee126978c4ee39d2dd7e817 (commit)
from dabc6307eaa9e93958fd7d6084bb154e0ae03c54 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=032e62c80df60b5fce...
commit 032e62c80df60b5fcedcd75f3043e0a18e114b6e
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Delay LZW creation until frame proccessing.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 5ba092a..6c21a7c 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -1207,14 +1207,6 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
gif->buffer_size = size;
gif->gif_data = data;
- if (gif->lzw_ctx == NULL) {
- lzw_result res = lzw_context_create(
- (struct lzw_ctx **)&gif->lzw_ctx);
- if (res != LZW_OK) {
- return gif_error_from_lzw(res);
- }
- }
-
/* Get our current processing position */
gif_data = gif->gif_data + gif->buffer_position;
@@ -1331,6 +1323,14 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
}
}
+ if (gif->lzw_ctx == NULL) {
+ lzw_result res = lzw_context_create(
+ (struct lzw_ctx **)&gif->lzw_ctx);
+ if (res != LZW_OK) {
+ return gif_error_from_lzw(res);
+ }
+ }
+
/* Repeatedly try to initialise frames */
do {
ret = gif__process_frame(gif, gif->frame_count, false);
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=4f20f753689e93c844...
commit 4f20f753689e93c8448c58fd9c4245e0817298fa
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Simplify check for no frame data.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 6a93dc8..5ba092a 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -67,9 +67,6 @@ enum gif_disposal {
#define GIF_BLOCK_TERMINATOR 0x00
#define GIF_TRAILER 0x3b
-/** standard GIF header size */
-#define GIF_STANDARD_HEADER_SIZE 13
-
static gif_result gif_error_from_lzw(lzw_result l_res)
{
static const gif_result g_res[] = {
@@ -1247,6 +1244,9 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
return ret;
}
+ /* Remember we've done this now */
+ gif->buffer_position = gif_data - gif->gif_data;
+
/* Some broken GIFs report the size as the screen size they
* were created in. As such, we detect for the common cases and
* set the sizes as 0 if they are found which results in the
@@ -1286,16 +1286,11 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
* termination block) Although generally useless, the GIF
* specification does not expressly prohibit this
*/
- if (gif->buffer_size == (GIF_STANDARD_HEADER_SIZE + 1)) {
+ if (gif->buffer_size == gif->buffer_position + 1) {
if (gif_data[0] == GIF_TRAILER) {
return GIF_OK;
- } else {
- return GIF_INSUFFICIENT_DATA;
}
}
-
- /* Remember we've done this now */
- gif->buffer_position = gif_data - gif->gif_data;
}
/* Do the colour map if we haven't already. As the top byte is always
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=2487a3b82a88ce5d2c...
commit 2487a3b82a88ce5d2c44028ebb1e86e22a2ed179
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Split out logical screen descriptor extraction.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 4e419a2..6a93dc8 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -1158,6 +1158,48 @@ static gif_result gif__parse_header(
return GIF_OK;
}
+/**
+ * Read Logical Screen Descriptor.
+ *
+ * 7-byte Logical Screen Descriptor is:
+ *
+ * +0 SHORT Logical Screen Width
+ * +2 SHORT Logical Screen Height
+ * +4 CHAR __Packed Fields__
+ * 1BIT Global Colour Table Flag
+ * 3BITS Colour Resolution
+ * 1BIT Sort Flag
+ * 3BITS Size of Global Colour Table
+ * +5 CHAR Background Colour Index
+ * +6 CHAR Pixel Aspect Ratio
+ *
+ * \param[in] gif The GIF object we're decoding.
+ * \param[in,out] pos The current buffer position, updated on success.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_logical_screen_descriptor(
+ struct gif_animation *gif,
+ const uint8_t **pos)
+{
+ const uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
+
+ if (len < 7) {
+ return GIF_INSUFFICIENT_DATA;
+ }
+
+ gif->width = data[0] | (data[1] << 8);
+ gif->height = data[2] | (data[3] << 8);
+ gif->global_colours = data[4] & GIF_COLOUR_TABLE_MASK;
+ gif->colour_table_size = 2 << (data[4] & GIF_COLOUR_TABLE_SIZE_MASK);
+ gif->bg_index = data[5];
+ gif->aspect_ratio = data[6];
+ gif->loop_count = 1;
+
+ *pos += 7;
+ return GIF_OK;
+}
+
/* exported function documented in libnsgif.h */
gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
{
@@ -1176,13 +1218,6 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
}
}
- /* Check for sufficient data to be a GIF (6-byte header + 7-byte
- * logical screen descriptor)
- */
- if (gif->buffer_size < GIF_STANDARD_HEADER_SIZE) {
- return GIF_INSUFFICIENT_DATA;
- }
-
/* Get our current processing position */
gif_data = gif->gif_data + gif->buffer_position;
@@ -1207,26 +1242,10 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
return ret;
}
- /* 7-byte Logical Screen Descriptor is:
- *
- * +0 SHORT Logical Screen Width
- * +2 SHORT Logical Screen Height
- * +4 CHAR __Packed Fields__
- * 1BIT Global Colour Table Flag
- * 3BITS Colour Resolution
- * 1BIT Sort Flag
- * 3BITS Size of Global Colour Table
- * +5 CHAR Background Colour Index
- * +6 CHAR Pixel Aspect Ratio
- */
- gif->width = gif_data[0] | (gif_data[1] << 8);
- gif->height = gif_data[2] | (gif_data[3] << 8);
- gif->global_colours = (gif_data[4] & GIF_COLOUR_TABLE_MASK);
- gif->colour_table_size = (2 << (gif_data[4] & GIF_COLOUR_TABLE_SIZE_MASK));
- gif->bg_index = gif_data[5];
- gif->aspect_ratio = gif_data[6];
- gif->loop_count = 1;
- gif_data += 7;
+ ret = gif__parse_logical_screen_descriptor(gif, &gif_data);
+ if (ret != GIF_OK) {
+ return ret;
+ }
/* Some broken GIFs report the size as the screen size they
* were created in. As such, we detect for the common cases and
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=16b5148bb734d9b7bb...
commit 16b5148bb734d9b7bb39419d81294c681b04b556
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
LZW: Remove double blank lines.
diff --git a/src/lzw.c b/src/lzw.c
index 710895e..6f85caa 100644
--- a/src/lzw.c
+++ b/src/lzw.c
@@ -96,7 +96,6 @@ struct lzw_ctx {
uint8_t stack_base[LZW_TABLE_ENTRY_MAX];
};
-
/* Exported function, documented in lzw.h */
lzw_result lzw_context_create(struct lzw_ctx **ctx)
{
@@ -109,14 +108,12 @@ lzw_result lzw_context_create(struct lzw_ctx **ctx)
return LZW_OK;
}
-
/* Exported function, documented in lzw.h */
void lzw_context_destroy(struct lzw_ctx *ctx)
{
free(ctx);
}
-
/**
* Advance the context to the next sub-block in the input data.
*
@@ -153,7 +150,6 @@ static lzw_result lzw__block_advance(struct lzw_read_ctx *restrict ctx)
return LZW_OK;
}
-
/**
* Get the next LZW code of given size from the raw input data.
*
@@ -223,7 +219,6 @@ static inline lzw_result lzw__read_code(
return LZW_OK;
}
-
/**
* Handle clear code.
*
diff --git a/src/lzw.h b/src/lzw.h
index 4568298..c68753a 100644
--- a/src/lzw.h
+++ b/src/lzw.h
@@ -20,11 +20,9 @@
/** Maximum LZW code size in bits */
#define LZW_CODE_MAX 12
-
/* Declare lzw internal context structure */
struct lzw_ctx;
-
/** LZW decoding response codes */
typedef enum lzw_result {
LZW_OK, /**< Success */
@@ -38,7 +36,6 @@ typedef enum lzw_result {
LZW_BAD_CODE, /**< Error: Bad LZW code */
} lzw_result;
-
/**
* Create an LZW decompression context.
*
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=2b2fa970ff094ae4ff...
commit 2b2fa970ff094ae4ffc219603a793cb54a960480
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Remove double blank lines.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 3e11dde..4e419a2 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -30,7 +30,6 @@
* \todo Plain text and comment extensions should be implemented.
*/
-
/** Maximum colour table size */
#define GIF_MAX_COLOURS 256
@@ -1326,14 +1325,12 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
return ret;
}
-
/* exported function documented in libnsgif.h */
gif_result gif_decode_frame(gif_animation *gif, unsigned int frame)
{
return gif__process_frame(gif, frame, true);
}
-
/* exported function documented in libnsgif.h */
void gif_finalise(gif_animation *gif)
{
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=893c7c14c9ff58ecfd...
commit 893c7c14c9ff58ecfd2d370e20e15eac6da779db
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Initilisation: Make frame initialisation loop more readable.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 457ce00..3e11dde 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -1319,7 +1319,9 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
}
/* Repeatedly try to initialise frames */
- while ((ret = gif__process_frame(gif, gif->frame_count, false)) == GIF_WORKING);
+ do {
+ ret = gif__process_frame(gif, gif->frame_count, false);
+ } while (ret == GIF_WORKING);
return ret;
}
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=c240b835fe6e377423...
commit c240b835fe6e3774237b6d968ddb405c8085690f
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Unify insufficient data error codes.
There is no difference in what the client needs to do.
If there are displayable frames, they can display them.
Otherwise more data is needed.
Internally only `GIF_INSUFFICIENT_DATA` is used now.
To remove the `GIF_INSUFFICIENT_FRAME_DATA` could make existing
client applications fail to compile, so it is left as an alias
to the same value.
At some point the API will be changed drastically, but for now
I want existing applications to still build.
diff --git a/include/libnsgif.h b/include/libnsgif.h
index 5bd26b4..309e2f0 100644
--- a/include/libnsgif.h
+++ b/include/libnsgif.h
@@ -23,9 +23,9 @@
typedef enum {
GIF_WORKING = 1,
GIF_OK = 0,
- GIF_INSUFFICIENT_FRAME_DATA = -1,
+ GIF_INSUFFICIENT_DATA = -1,
+ GIF_INSUFFICIENT_FRAME_DATA = GIF_INSUFFICIENT_DATA,
GIF_FRAME_DATA_ERROR = -2,
- GIF_INSUFFICIENT_DATA = -3,
GIF_DATA_ERROR = -4,
GIF_INSUFFICIENT_MEMORY = -5,
GIF_FRAME_NO_DISPLAY = -6,
@@ -166,11 +166,9 @@ void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks);
*
* \return Error return value.
* - GIF_FRAME_DATA_ERROR for GIF frame data error
- * - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to process
- * any more frames
+ * - GIF_INSUFFICIENT_DATA reached unexpected end of source data
* - GIF_INSUFFICIENT_MEMORY for memory error
* - GIF_DATA_ERROR for GIF error
- * - GIF_INSUFFICIENT_DATA for insufficient data to do anything
* - GIF_OK for successful decoding
* - GIF_WORKING for successful decoding if more frames are expected
*/
@@ -181,9 +179,8 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data);
*
* \return Error return value.
* - GIF_FRAME_DATA_ERROR for GIF frame data error
- * - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
* - GIF_DATA_ERROR for GIF error (invalid frame header)
- * - GIF_INSUFFICIENT_DATA for insufficient data to do anything
+ * - GIF_INSUFFICIENT_DATA reached unexpected end of source data
* - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
* - GIF_OK for successful decoding
*/
diff --git a/src/libnsgif.c b/src/libnsgif.c
index f0fab44..457ce00 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -77,7 +77,7 @@ static gif_result gif_error_from_lzw(lzw_result l_res)
[LZW_OK] = GIF_OK,
[LZW_OK_EOD] = GIF_END_OF_FRAME,
[LZW_NO_MEM] = GIF_INSUFFICIENT_MEMORY,
- [LZW_NO_DATA] = GIF_INSUFFICIENT_FRAME_DATA,
+ [LZW_NO_DATA] = GIF_INSUFFICIENT_DATA,
[LZW_EOI_CODE] = GIF_FRAME_DATA_ERROR,
[LZW_BAD_ICODE] = GIF_FRAME_DATA_ERROR,
[LZW_BAD_CODE] = GIF_FRAME_DATA_ERROR,
@@ -560,7 +560,7 @@ static gif_result gif__update_bitmap(
* \param[in] frame The gif object we're decoding.
* \param[in] data The data to decode.
* \param[in] len Byte length of data.
- * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
+ * \return GIF_INSUFFICIENT_DATA if more data is needed,
* GIF_OK for success.
*/
static gif_result gif__parse_extension_graphic_control(
@@ -581,7 +581,7 @@ static gif_result gif__parse_extension_graphic_control(
* +5 CHAR Transparent Color Index
*/
if (len < 6) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ return GIF_INSUFFICIENT_DATA;
}
frame->frame_delay = data[3] | (data[4] << 8);
@@ -616,7 +616,7 @@ static gif_result gif__parse_extension_graphic_control(
* \param[in] gif The gif object we're decoding.
* \param[in] data The data to decode.
* \param[in] len Byte length of data.
- * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
+ * \return GIF_INSUFFICIENT_DATA if more data is needed,
* GIF_OK for success.
*/
static gif_result gif__parse_extension_application(
@@ -633,7 +633,7 @@ static gif_result gif__parse_extension_application(
* +13 1-256 Application Data (Data sub-blocks)
*/
if (len < 17) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ return GIF_INSUFFICIENT_DATA;
}
if ((data[1] == 0x0b) &&
@@ -651,7 +651,7 @@ static gif_result gif__parse_extension_application(
* \param[in] gif The gif object we're decoding.
* \param[in] frame The frame to parse extensions for.
* \param[in] decode Whether to decode or skip over the extension.
- * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
+ * \return GIF_INSUFFICIENT_DATA if more data is needed,
* GIF_OK for success.
*/
static gif_result gif__parse_frame_extensions(
@@ -673,7 +673,7 @@ static gif_result gif__parse_frame_extensions(
gif_bytes--;
if (gif_bytes == 0) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ return GIF_INSUFFICIENT_DATA;
}
/* Switch on extension label */
@@ -715,7 +715,7 @@ static gif_result gif__parse_frame_extensions(
* the extension size itself
*/
if (gif_bytes < 2) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ return GIF_INSUFFICIENT_DATA;
}
gif_data += 2 + gif_data[1];
}
@@ -725,7 +725,7 @@ static gif_result gif__parse_frame_extensions(
while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) {
gif_data += gif_data[0] + 1;
if (gif_data >= gif_end) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ return GIF_INSUFFICIENT_DATA;
}
}
gif_data++;
@@ -779,7 +779,7 @@ static gif_result gif__parse_image_descriptor(
assert(frame != NULL);
if (len < GIF_IMAGE_DESCRIPTOR_LEN) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ return GIF_INSUFFICIENT_DATA;
}
if (decode) {
@@ -830,11 +830,7 @@ static gif_result gif__colour_table_extract(
size_t len = gif->gif_data + gif->buffer_size - data;
if (len < colour_table_entries * 3) {
- if (colour_table == gif->local_colour_table) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- } else {
- return GIF_INSUFFICIENT_DATA;
- }
+ return GIF_INSUFFICIENT_DATA;
}
if (decode) {
@@ -935,7 +931,7 @@ static gif_result gif__parse_image_data(
/* Fall through. */
case 1: if (data[0] == GIF_TRAILER) return GIF_OK;
/* Fall through. */
- case 0: return GIF_INSUFFICIENT_FRAME_DATA;
+ case 0: return GIF_INSUFFICIENT_DATA;
}
minimum_code_size = data[0];
@@ -953,7 +949,7 @@ static gif_result gif__parse_image_data(
len--;
while (block_size != 1) {
- if (len < 1) return GIF_INSUFFICIENT_FRAME_DATA;
+ if (len < 1) return GIF_INSUFFICIENT_DATA;
block_size = data[0] + 1;
/* Check if the frame data runs off the end of the file */
if (block_size > len) {
@@ -971,7 +967,7 @@ static gif_result gif__parse_image_data(
/* Check if we've finished */
if (len < 1) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ return GIF_INSUFFICIENT_DATA;
} else {
if (data[0] == GIF_TRAILER) {
return GIF_OK;
@@ -1026,10 +1022,9 @@ static struct gif_frame *gif__get_frame(
* \param[in] frame_idx The frame number to decode.
* \param[in] decode Whether to decode the graphical image data.
* \return error code
- * - GIF_INSUFFICIENT_DATA for insufficient data to do anything
+ * - GIF_INSUFFICIENT_DATA reached unexpected end of source data.
* - GIF_FRAME_DATA_ERROR for GIF frame data error
* - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
- * - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
* - GIF_DATA_ERROR for GIF error (invalid frame header)
* - GIF_OK for successful decoding
* - GIF_WORKING for successful decoding if more frames are expected
@@ -1326,19 +1321,6 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
/* Repeatedly try to initialise frames */
while ((ret = gif__process_frame(gif, gif->frame_count, false)) == GIF_WORKING);
- /* If there was a memory error tell the caller */
- if ((ret == GIF_INSUFFICIENT_MEMORY) ||
- (ret == GIF_DATA_ERROR)) {
- return ret;
- }
-
- /* If we didn't have some frames then a GIF_INSUFFICIENT_DATA becomes a
- * GIF_INSUFFICIENT_FRAME_DATA
- */
- if (ret == GIF_INSUFFICIENT_DATA && gif->frame_count_partial > 0) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
-
return ret;
}
diff --git a/test/decode_gif.c b/test/decode_gif.c
index 64387ef..52d73b6 100644
--- a/test/decode_gif.c
+++ b/test/decode_gif.c
@@ -113,9 +113,6 @@ static void warning(const char *context, gif_result code)
fprintf(stderr, "%s failed: ", context);
switch (code)
{
- case GIF_INSUFFICIENT_FRAME_DATA:
- fprintf(stderr, "GIF_INSUFFICIENT_FRAME_DATA");
- break;
case GIF_FRAME_DATA_ERROR:
fprintf(stderr, "GIF_FRAME_DATA_ERROR");
break;
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=15670d3d0e3dcf9db1...
commit 15670d3d0e3dcf9db168244428cab8e43460251a
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Use common colour table code for global colour table.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index b7b007b..f0fab44 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -830,7 +830,11 @@ static gif_result gif__colour_table_extract(
size_t len = gif->gif_data + gif->buffer_size - data;
if (len < colour_table_entries * 3) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ if (colour_table == gif->local_colour_table) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ } else {
+ return GIF_INSUFFICIENT_DATA;
+ }
}
if (decode) {
@@ -1164,7 +1168,6 @@ static gif_result gif__parse_header(
gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
{
const uint8_t *gif_data;
- uint32_t index;
gif_result ret;
/* Initialize values */
@@ -1289,27 +1292,14 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
if (gif->global_colour_table[0] == GIF_PROCESS_COLOURS) {
/* Check for a global colour map signified by bit 7 */
if (gif->global_colours) {
- if (gif->buffer_size < (gif->colour_table_size * 3 + GIF_STANDARD_HEADER_SIZE)) {
- return GIF_INSUFFICIENT_DATA;
- }
- for (index = 0; index < gif->colour_table_size; index++) {
- /* Gif colour map contents are r,g,b.
- *
- * We want to pack them bytewise into the
- * colour table, such that the red component
- * is in byte 0 and the alpha component is in
- * byte 3.
- */
- uint8_t *entry = (uint8_t *) &gif->
- global_colour_table[index];
-
- entry[0] = gif_data[0]; /* r */
- entry[1] = gif_data[1]; /* g */
- entry[2] = gif_data[2]; /* b */
- entry[3] = 0xff; /* a */
-
- gif_data += 3;
+ ret = gif__colour_table_extract(gif,
+ gif->global_colour_table,
+ gif->colour_table_size,
+ &gif_data, true);
+ if (ret != GIF_OK) {
+ return ret;
}
+
gif->buffer_position = (gif_data - gif->gif_data);
} else {
/* Create a default colour table with the first two
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=e2458d5e968323735a...
commit e2458d5e968323735a2a836da5f5838e62caf6b6
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Split out colour table extraction.
This will allow the local and global colour table extraction to
share code.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 6de26a0..b7b007b 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -810,40 +810,32 @@ static gif_result gif__parse_image_descriptor(
}
/**
- * Get a frame's colour table.
- *
- * Sets up gif->colour_table for the frame.
+ * Extract a GIF colour table into a LibNSGIF colour table buffer.
*
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to get the colour table for.
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] colour_table The colour table to populate.
+ * \param[in] colour_table_entries The number of colour table entries.
+ * \param[in] pos Current position in data, updated on exit.
+ * \param[in] decode Whether to decode the colour table.
* \return GIF_OK on success, appropriate error otherwise.
*/
-static gif_result gif__parse_colour_table(
+static gif_result gif__colour_table_extract(
struct gif_animation *gif,
- struct gif_frame *frame,
+ uint32_t *colour_table,
+ size_t colour_table_entries,
const uint8_t **pos,
bool decode)
{
- unsigned colour_table_size;
const uint8_t *data = *pos;
size_t len = gif->gif_data + gif->buffer_size - data;
- assert(gif != NULL);
- assert(frame != NULL);
-
- if ((frame->flags & GIF_COLOUR_TABLE_MASK) == 0) {
- gif->colour_table = gif->global_colour_table;
- return GIF_OK;
- }
-
- colour_table_size = 2 << (frame->flags & GIF_COLOUR_TABLE_SIZE_MASK);
- if (len < colour_table_size * 3) {
+ if (len < colour_table_entries * 3) {
return GIF_INSUFFICIENT_FRAME_DATA;
}
if (decode) {
- int count = colour_table_size;
- uint8_t *entry = (uint8_t *)gif->local_colour_table;
+ int count = colour_table_entries;
+ uint8_t *entry = (uint8_t *)colour_table;
while (count--) {
/* Gif colour map contents are r,g,b.
@@ -861,8 +853,43 @@ static gif_result gif__parse_colour_table(
}
}
+ *pos += colour_table_entries * 3;
+ return GIF_OK;
+}
+
+/**
+ * Get a frame's colour table.
+ *
+ * Sets up gif->colour_table for the frame.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to get the colour table for.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_colour_table(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ const uint8_t **pos,
+ bool decode)
+{
+ gif_result ret;
+
+ assert(gif != NULL);
+ assert(frame != NULL);
+
+ if ((frame->flags & GIF_COLOUR_TABLE_MASK) == 0) {
+ gif->colour_table = gif->global_colour_table;
+ return GIF_OK;
+ }
+
+ ret = gif__colour_table_extract(gif, gif->local_colour_table,
+ 2 << (frame->flags & GIF_COLOUR_TABLE_SIZE_MASK),
+ pos, decode);
+ if (ret != GIF_OK) {
+ return ret;
+ }
+
gif->colour_table = gif->local_colour_table;
- *pos += colour_table_size * 3;
return GIF_OK;
}
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=470c70dac58c146ac2...
commit 470c70dac58c146ac280f6c7920764e59a885a42
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Split header reader out into helper function.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 5cfa020..6de26a0 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -1091,6 +1091,47 @@ void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks)
gif->prev_index = GIF_INVALID_FRAME;
}
+/**
+ * Read GIF header.
+ *
+ * 6-byte GIF file header is:
+ *
+ * +0 3CHARS Signature ('GIF')
+ * +3 3CHARS Version ('87a' or '89a')
+ *
+ * \param[in] gif The GIF object we're decoding.
+ * \param[in,out] pos The current buffer position, updated on success.
+ * \param[in] strict Whether to require a known GIF version.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_header(
+ struct gif_animation *gif,
+ const uint8_t **pos,
+ bool strict)
+{
+ const uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
+
+ if (len < 6) {
+ return GIF_INSUFFICIENT_DATA;
+ }
+
+ if (strncmp((const char *) data, "GIF", 3) != 0) {
+ return GIF_DATA_ERROR;
+ }
+ data += 3;
+
+ if (strict == true) {
+ if ((strncmp((const char *) data, "87a", 3) != 0) &&
+ (strncmp((const char *) data, "89a", 3) != 0)) {
+ return GIF_DATA_ERROR;
+ }
+ }
+ data += 3;
+
+ *pos = data;
+ return GIF_OK;
+}
/* exported function documented in libnsgif.h */
gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
@@ -1137,23 +1178,10 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
gif->frame_count_partial = 0;
gif->decoded_frame = GIF_INVALID_FRAME;
- /* 6-byte GIF file header is:
- *
- * +0 3CHARS Signature ('GIF')
- * +3 3CHARS Version ('87a' or '89a')
- */
- if (strncmp((const char *) gif_data, "GIF", 3) != 0) {
- return GIF_DATA_ERROR;
+ ret = gif__parse_header(gif, &gif_data, false);
+ if (ret != GIF_OK) {
+ return ret;
}
- gif_data += 3;
-
- /* Ensure GIF reports version 87a or 89a */
- /*
- if ((strncmp(gif_data, "87a", 3) != 0) &&
- (strncmp(gif_data, "89a", 3) != 0))
- LOG(("Unknown GIF format - proceeding anyway"));
- */
- gif_data += 3;
/* 7-byte Logical Screen Descriptor is:
*
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=9f28b76c419889ef76...
commit 9f28b76c419889ef76832bcb65912acd42f9e685
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
Add myself to the copyright headers.
diff --git a/COPYING b/COPYING
index c6e1688..9334b89 100644
--- a/COPYING
+++ b/COPYING
@@ -1,5 +1,6 @@
Copyright (C) 2004 Richard Wilson
Copyright (C) 2008 Sean Fox
+Copyright (C) 2013-2021 Michael Drake
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/include/libnsgif.h b/include/libnsgif.h
index 3087a4d..5bd26b4 100644
--- a/include/libnsgif.h
+++ b/include/libnsgif.h
@@ -1,6 +1,7 @@
/*
* Copyright 2004 Richard Wilson <richard.wilson(a)netsurf-browser.org>
* Copyright 2008 Sean Fox <dyntryx(a)gmail.com>
+ * Copyright 2013-2021 Michael Drake <tlsa(a)netsurf-browser.org>
*
* This file is part of NetSurf's libnsgif, http://www.netsurf-browser.org/
* Licenced under the MIT License,
diff --git a/src/libnsgif.c b/src/libnsgif.c
index a87c136..5cfa020 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -1,6 +1,7 @@
/*
* Copyright 2004 Richard Wilson <richard.wilson(a)netsurf-browser.org>
* Copyright 2008 Sean Fox <dyntryx(a)gmail.com>
+ * Copyright 2013-2021 Michael Drake <tlsa(a)netsurf-browser.org>
*
* This file is part of NetSurf's libnsgif, http://www.netsurf-browser.org/
* Licenced under the MIT License,
diff --git a/src/lzw.h b/src/lzw.h
index 4549c60..4568298 100644
--- a/src/lzw.h
+++ b/src/lzw.h
@@ -4,6 +4,7 @@
* http://www.opensource.org/licenses/mit-license.php
*
* Copyright 2017 Michael Drake <michael.drake(a)codethink.co.uk>
+ * Copyright 2021 Michael Drake <tlsa(a)netsurf-browser.org>
*/
#ifndef LZW_H_
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=b7952d1c53dffeced0...
commit b7952d1c53dffeced0feb9d75e66da972d68318a
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Initialisation: Remove misleading comment.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 6046769..a87c136 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -1293,7 +1293,6 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
return GIF_INSUFFICIENT_FRAME_DATA;
}
- /* Return how many we got */
return ret;
}
-----------------------------------------------------------------------
Summary of changes:
COPYING | 1 +
include/libnsgif.h | 234 +++----
src/libnsgif.c | 1784 +++++++++++++++++++++++++++-------------------------
src/lzw.c | 5 -
src/lzw.h | 4 +-
test/decode_gif.c | 3 -
6 files changed, 1035 insertions(+), 996 deletions(-)
diff --git a/COPYING b/COPYING
index c6e1688..9334b89 100644
--- a/COPYING
+++ b/COPYING
@@ -1,5 +1,6 @@
Copyright (C) 2004 Richard Wilson
Copyright (C) 2008 Sean Fox
+Copyright (C) 2013-2021 Michael Drake
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/include/libnsgif.h b/include/libnsgif.h
index 50dc688..309e2f0 100644
--- a/include/libnsgif.h
+++ b/include/libnsgif.h
@@ -1,6 +1,7 @@
/*
* Copyright 2004 Richard Wilson <richard.wilson(a)netsurf-browser.org>
* Copyright 2008 Sean Fox <dyntryx(a)gmail.com>
+ * Copyright 2013-2021 Michael Drake <tlsa(a)netsurf-browser.org>
*
* This file is part of NetSurf's libnsgif, http://www.netsurf-browser.org/
* Licenced under the MIT License,
@@ -15,53 +16,55 @@
#ifndef _LIBNSGIF_H_
#define _LIBNSGIF_H_
+#include <stdint.h>
#include <stdbool.h>
-#include <inttypes.h>
/* Error return values */
typedef enum {
- GIF_WORKING = 1,
- GIF_OK = 0,
- GIF_INSUFFICIENT_FRAME_DATA = -1,
- GIF_FRAME_DATA_ERROR = -2,
- GIF_INSUFFICIENT_DATA = -3,
- GIF_DATA_ERROR = -4,
- GIF_INSUFFICIENT_MEMORY = -5,
- GIF_FRAME_NO_DISPLAY = -6,
- GIF_END_OF_FRAME = -7
+ GIF_WORKING = 1,
+ GIF_OK = 0,
+ GIF_INSUFFICIENT_DATA = -1,
+ GIF_INSUFFICIENT_FRAME_DATA = GIF_INSUFFICIENT_DATA,
+ GIF_FRAME_DATA_ERROR = -2,
+ GIF_DATA_ERROR = -4,
+ GIF_INSUFFICIENT_MEMORY = -5,
+ GIF_FRAME_NO_DISPLAY = -6,
+ GIF_END_OF_FRAME = -7
} gif_result;
/** GIF frame data */
typedef struct gif_frame {
- /** whether the frame should be displayed/animated */
- bool display;
- /** delay (in cs) before animating the frame */
- unsigned int frame_delay;
-
- /* Internal members are listed below */
-
- /** offset (in bytes) to the GIF frame data */
- unsigned int frame_pointer;
- /** whether the frame has previously been used */
- bool virgin;
- /** whether the frame is totally opaque */
- bool opaque;
- /** whether a forcable screen redraw is required */
- bool redraw_required;
- /** how the previous frame should be disposed; affects plotting */
- unsigned char disposal_method;
- /** whether we acknoledge transparency */
- bool transparency;
- /** the index designating a transparent pixel */
- unsigned char transparency_index;
- /** x co-ordinate of redraw rectangle */
- unsigned int redraw_x;
- /** y co-ordinate of redraw rectangle */
- unsigned int redraw_y;
- /** width of redraw rectangle */
- unsigned int redraw_width;
- /** height of redraw rectangle */
- unsigned int redraw_height;
+ /** whether the frame should be displayed/animated */
+ bool display;
+ /** delay (in cs) before animating the frame */
+ unsigned int frame_delay;
+
+ /* Internal members are listed below */
+
+ /** offset (in bytes) to the GIF frame data */
+ unsigned int frame_pointer;
+ /** whether the frame has previously been used */
+ bool virgin;
+ /** whether the frame is totally opaque */
+ bool opaque;
+ /** whether a full image redraw is required */
+ bool redraw_required;
+ /** how the previous frame should be disposed; affects plotting */
+ unsigned char disposal_method;
+ /** whether we acknowledge transparency */
+ bool transparency;
+ /** the index designating a transparent pixel */
+ unsigned int transparency_index;
+ /** x co-ordinate of redraw rectangle */
+ unsigned int redraw_x;
+ /** y co-ordinate of redraw rectangle */
+ unsigned int redraw_y;
+ /** width of redraw rectangle */
+ unsigned int redraw_width;
+ /** height of redraw rectangle */
+ unsigned int redraw_height;
+ /* Frame flags */
+ unsigned int flags;
} gif_frame;
/* API for Bitmap callbacks */
@@ -74,77 +77,81 @@ typedef void (*gif_bitmap_cb_modified)(void *bitmap);
/** Bitmap callbacks function table */
typedef struct gif_bitmap_callback_vt {
- /** Create a bitmap. */
- gif_bitmap_cb_create bitmap_create;
- /** Free a bitmap. */
- gif_bitmap_cb_destroy bitmap_destroy;
- /** Return a pointer to the pixel data in a bitmap. */
- gif_bitmap_cb_get_buffer bitmap_get_buffer;
-
- /* Members below are optional */
-
- /** Sets whether a bitmap should be plotted opaque. */
- gif_bitmap_cb_set_opaque bitmap_set_opaque;
- /** Tests whether a bitmap has an opaque alpha channel. */
- gif_bitmap_cb_test_opaque bitmap_test_opaque;
- /** The bitmap image has changed, so flush any persistant cache. */
- gif_bitmap_cb_modified bitmap_modified;
+ /** Create a bitmap. */
+ gif_bitmap_cb_create bitmap_create;
+ /** Free a bitmap. */
+ gif_bitmap_cb_destroy bitmap_destroy;
+ /** Return a pointer to the pixel data in a bitmap. */
+ gif_bitmap_cb_get_buffer bitmap_get_buffer;
+
+ /* Members below are optional */
+
+ /** Sets whether a bitmap should be plotted opaque. */
+ gif_bitmap_cb_set_opaque bitmap_set_opaque;
+ /** Tests whether a bitmap has an opaque alpha channel. */
+ gif_bitmap_cb_test_opaque bitmap_test_opaque;
+ /** The bitmap image has changed, so flush any persistent cache. */
+ gif_bitmap_cb_modified bitmap_modified;
} gif_bitmap_callback_vt;
/** GIF animation data */
typedef struct gif_animation {
- /** LZW decode context */
- void *lzw_ctx;
- /** callbacks for bitmap functions */
- gif_bitmap_callback_vt bitmap_callbacks;
- /** pointer to GIF data */
- unsigned char *gif_data;
- /** width of GIF (may increase during decoding) */
- unsigned int width;
- /** heigth of GIF (may increase during decoding) */
- unsigned int height;
- /** number of frames decoded */
- unsigned int frame_count;
- /** number of frames partially decoded */
- unsigned int frame_count_partial;
- /** decoded frames */
- gif_frame *frames;
- /** current frame decoded to bitmap */
- int decoded_frame;
- /** currently decoded image; stored as bitmap from bitmap_create callback */
- void *frame_image;
- /** number of times to loop animation */
- int loop_count;
-
- /* Internal members are listed below */
-
- /** current index into GIF data */
- unsigned int buffer_position;
- /** total number of bytes of GIF data available */
- unsigned int buffer_size;
- /** current number of frame holders */
- unsigned int frame_holders;
- /** index in the colour table for the background colour */
- unsigned int background_index;
- /** image aspect ratio (ignored) */
- unsigned int aspect_ratio;
- /** size of colour table (in entries) */
- unsigned int colour_table_size;
- /** whether the GIF has a global colour table */
- bool global_colours;
- /** global colour table */
- unsigned int *global_colour_table;
- /** local colour table */
- unsigned int *local_colour_table;
-
- /** previous frame for GIF_FRAME_RESTORE */
- void *prev_frame;
- /** previous frame index */
- int prev_index;
- /** previous frame width */
- unsigned prev_width;
- /** previous frame height */
- unsigned prev_height;
+ /** LZW decode context */
+ void *lzw_ctx;
+ /** callbacks for bitmap functions */
+ gif_bitmap_callback_vt bitmap_callbacks;
+ /** pointer to GIF data */
+ const uint8_t *gif_data;
+ /** width of GIF (may increase during decoding) */
+ unsigned int width;
+ /** height of GIF (may increase during decoding) */
+ unsigned int height;
+ /** number of frames decoded */
+ unsigned int frame_count;
+ /** number of frames partially decoded */
+ unsigned int frame_count_partial;
+ /** decoded frames */
+ gif_frame *frames;
+ /** current frame decoded to bitmap */
+ int decoded_frame;
+ /** currently decoded image; stored as bitmap from bitmap_create callback */
+ void *frame_image;
+ /** number of times to loop animation */
+ int loop_count;
+
+ /* Internal members are listed below */
+
+ /** current index into GIF data */
+ unsigned int buffer_position;
+ /** total number of bytes of GIF data available */
+ unsigned int buffer_size;
+ /** current number of frame holders */
+ unsigned int frame_holders;
+ /** background index */
+ unsigned int bg_index;
+ /** background colour */
+ unsigned int bg_colour;
+ /** image aspect ratio (ignored) */
+ unsigned int aspect_ratio;
+ /** size of colour table (in entries) */
+ unsigned int colour_table_size;
+ /** whether the GIF has a global colour table */
+ bool global_colours;
+ /** global colour table */
+ unsigned int *global_colour_table;
+ /** local colour table */
+ unsigned int *local_colour_table;
+ /** current colour table */
+ unsigned int *colour_table;
+
+ /** previous frame for GIF_FRAME_RESTORE */
+ void *prev_frame;
+ /** previous frame index */
+ int prev_index;
+ /** previous frame width */
+ unsigned prev_width;
+ /** previous frame height */
+ unsigned prev_height;
} gif_animation;
/**
@@ -157,28 +164,23 @@ void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks);
* any information that hasn't already been decoded.
* If an error occurs, all previously decoded frames are retained.
*
- * @return Error return value.
+ * \return Error return value.
* - GIF_FRAME_DATA_ERROR for GIF frame data error
- * - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to process
- * any more frames
+ * - GIF_INSUFFICIENT_DATA reached unexpected end of source data
* - GIF_INSUFFICIENT_MEMORY for memory error
* - GIF_DATA_ERROR for GIF error
- * - GIF_INSUFFICIENT_DATA for insufficient data to do anything
* - GIF_OK for successful decoding
* - GIF_WORKING for successful decoding if more frames are expected
*/
-gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data);
+gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data);
/**
* Decodes a GIF frame.
*
- * @return Error return value. If a frame does not contain any image data,
- * GIF_OK is returned and gif->current_error is set to
- * GIF_FRAME_NO_DISPLAY
+ * \return Error return value.
* - GIF_FRAME_DATA_ERROR for GIF frame data error
- * - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
* - GIF_DATA_ERROR for GIF error (invalid frame header)
- * - GIF_INSUFFICIENT_DATA for insufficient data to do anything
+ * - GIF_INSUFFICIENT_DATA reached unexpected end of source data
* - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
* - GIF_OK for successful decoding
*/
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 16bb78a..6c21a7c 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -1,6 +1,7 @@
/*
* Copyright 2004 Richard Wilson <richard.wilson(a)netsurf-browser.org>
* Copyright 2008 Sean Fox <dyntryx(a)gmail.com>
+ * Copyright 2013-2021 Michael Drake <tlsa(a)netsurf-browser.org>
*
* This file is part of NetSurf's libnsgif, http://www.netsurf-browser.org/
* Licenced under the MIT License,
@@ -29,7 +30,6 @@
* \todo Plain text and comment extensions should be implemented.
*/
-
/** Maximum colour table size */
#define GIF_MAX_COLOURS 256
@@ -45,13 +45,15 @@
/** No transparency */
#define GIF_NO_TRANSPARENCY (0xFFFFFFFFu)
-/* GIF Flags */
-#define GIF_FRAME_COMBINE 1
-#define GIF_FRAME_CLEAR 2
-#define GIF_FRAME_RESTORE 3
-#define GIF_FRAME_QUIRKS_RESTORE 4
+enum gif_disposal {
+ GIF_DISPOSAL_UNSPECIFIED,
+ GIF_DISPOSAL_NONE,
+ GIF_DISPOSAL_RESTORE_BG,
+ GIF_DISPOSAL_RESTORE_PREV,
+ GIF_DISPOSAL_RESTORE_QUIRK, /**< Alias for GIF_DISPOSAL_RESTORE_PREV. */
+};
-#define GIF_IMAGE_SEPARATOR 0x2c
+/* GIF Flags */
#define GIF_INTERLACE_MASK 0x40
#define GIF_COLOUR_TABLE_MASK 0x80
#define GIF_COLOUR_TABLE_SIZE_MASK 0x07
@@ -65,9 +67,21 @@
#define GIF_BLOCK_TERMINATOR 0x00
#define GIF_TRAILER 0x3b
-/** standard GIF header size */
-#define GIF_STANDARD_HEADER_SIZE 13
-
+static gif_result gif_error_from_lzw(lzw_result l_res)
+{
+ static const gif_result g_res[] = {
+ [LZW_OK] = GIF_OK,
+ [LZW_OK_EOD] = GIF_END_OF_FRAME,
+ [LZW_NO_MEM] = GIF_INSUFFICIENT_MEMORY,
+ [LZW_NO_DATA] = GIF_INSUFFICIENT_DATA,
+ [LZW_EOI_CODE] = GIF_FRAME_DATA_ERROR,
+ [LZW_BAD_ICODE] = GIF_FRAME_DATA_ERROR,
+ [LZW_BAD_CODE] = GIF_FRAME_DATA_ERROR,
+ };
+ assert(l_res != LZW_BAD_PARAM);
+ assert(l_res != LZW_NO_COLOUR);
+ return g_res[l_res];
+}
/**
* Updates the sprite memory size
@@ -77,10 +91,10 @@
* \param height The height of the sprite
* \return GIF_INSUFFICIENT_MEMORY for a memory error GIF_OK for success
*/
-static gif_result
-gif_initialise_sprite(gif_animation *gif,
- unsigned int width,
- unsigned int height)
+static gif_result gif_initialise_sprite(
+ struct gif_animation *gif,
+ uint32_t width,
+ uint32_t height)
{
/* Already allocated? */
if (gif->frame_image) {
@@ -96,473 +110,82 @@ gif_initialise_sprite(gif_animation *gif,
return GIF_OK;
}
-
/**
- * Attempts to initialise the frame's extensions
+ * Helper to get the rendering bitmap for a gif.
*
- * \param gif The animation context
- * \param frame The frame number
- * @return GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the
- * frame GIF_OK for successful initialisation.
+ * \param[in] gif The gif object we're decoding.
+ * \return Client pixel buffer for rendering into.
*/
-static gif_result
-gif_initialise_frame_extensions(gif_animation *gif, const int frame)
+static inline uint32_t* gif__bitmap_get(
+ struct gif_animation *gif)
{
- unsigned char *gif_data, *gif_end;
- int gif_bytes;
- unsigned int block_size;
-
- /* Get our buffer position etc. */
- gif_data = (unsigned char *)(gif->gif_data + gif->buffer_position);
- gif_end = (unsigned char *)(gif->gif_data + gif->buffer_size);
-
- /* Initialise the extensions */
- while (gif_data < gif_end && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
- ++gif_data;
- if ((gif_bytes = (gif_end - gif_data)) < 1) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
-
- /* Switch on extension label */
- switch (gif_data[0]) {
- case GIF_EXTENSION_GRAPHIC_CONTROL:
- /* 6-byte Graphic Control Extension is:
- *
- * +0 CHAR Graphic Control Label
- * +1 CHAR Block Size
- * +2 CHAR __Packed Fields__
- * 3BITS Reserved
- * 3BITS Disposal Method
- * 1BIT User Input Flag
- * 1BIT Transparent Color Flag
- * +3 SHORT Delay Time
- * +5 CHAR Transparent Color Index
- */
- if (gif_bytes < 6) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
-
- gif->frames[frame].frame_delay = gif_data[3] | (gif_data[4] << 8);
- if (gif_data[2] & GIF_TRANSPARENCY_MASK) {
- gif->frames[frame].transparency = true;
- gif->frames[frame].transparency_index = gif_data[5];
- }
- gif->frames[frame].disposal_method = ((gif_data[2] & GIF_DISPOSAL_MASK) >> 2);
- /* I have encountered documentation and GIFs in the
- * wild that use 0x04 to restore the previous frame,
- * rather than the officially documented 0x03. I
- * believe some (older?) software may even actually
- * export this way. We handle this as a type of
- * "quirks" mode.
- */
- if (gif->frames[frame].disposal_method == GIF_FRAME_QUIRKS_RESTORE) {
- gif->frames[frame].disposal_method = GIF_FRAME_RESTORE;
- }
- gif_data += (2 + gif_data[1]);
- break;
-
- case GIF_EXTENSION_APPLICATION:
- /* 14-byte+ Application Extension is:
- *
- * +0 CHAR Application Extension Label
- * +1 CHAR Block Size
- * +2 8CHARS Application Identifier
- * +10 3CHARS Appl. Authentication Code
- * +13 1-256 Application Data (Data sub-blocks)
- */
- if (gif_bytes < 17) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- if ((gif_data[1] == 0x0b) &&
- (strncmp((const char *) gif_data + 2,
- "NETSCAPE2.0", 11) == 0) &&
- (gif_data[13] == 0x03) &&
- (gif_data[14] == 0x01)) {
- gif->loop_count = gif_data[15] | (gif_data[16] << 8);
- }
- gif_data += (2 + gif_data[1]);
- break;
-
- case GIF_EXTENSION_COMMENT:
- /* Move the pointer to the first data sub-block Skip 1
- * byte for the extension label
- */
- ++gif_data;
- break;
-
- default:
- /* Move the pointer to the first data sub-block Skip 2
- * bytes for the extension label and size fields Skip
- * the extension size itself
- */
- if (gif_bytes < 2) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- gif_data += (2 + gif_data[1]);
- }
+ gif_result ret;
- /* Repeatedly skip blocks until we get a zero block or run out
- * of data This data is ignored by this gif decoder
- */
- gif_bytes = (gif_end - gif_data);
- block_size = 0;
- while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) {
- block_size = gif_data[0] + 1;
- if ((gif_bytes -= block_size) < 0) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- gif_data += block_size;
- }
- ++gif_data;
+ /* Make sure we have a buffer to decode to. */
+ ret = gif_initialise_sprite(gif, gif->width, gif->height);
+ if (ret != GIF_OK) {
+ return NULL;
}
- /* Set buffer position and return */
- gif->buffer_position = (gif_data - gif->gif_data);
- return GIF_OK;
+ /* Get the frame data */
+ assert(gif->bitmap_callbacks.bitmap_get_buffer);
+ return (uint32_t *)gif->bitmap_callbacks.bitmap_get_buffer(
+ gif->frame_image);
}
-
/**
- * Attempts to initialise the next frame
+ * Helper to tell the client that their bitmap was modified.
*
- * \param gif The animation context
- * \return error code
- * - GIF_INSUFFICIENT_DATA for insufficient data to do anything
- * - GIF_FRAME_DATA_ERROR for GIF frame data error
- * - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
- * - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
- * - GIF_DATA_ERROR for GIF error (invalid frame header)
- * - GIF_OK for successful decoding
- * - GIF_WORKING for successful decoding if more frames are expected
-*/
-static gif_result gif_initialise_frame(gif_animation *gif)
+ * \param[in] gif The gif object we're decoding.
+ */
+static inline void gif__bitmap_modified(
+ const struct gif_animation *gif)
{
- int frame;
- gif_frame *temp_buf;
-
- unsigned char *gif_data, *gif_end;
- int gif_bytes;
- unsigned int flags = 0;
- unsigned int width, height, offset_x, offset_y;
- unsigned int block_size, colour_table_size;
- bool first_image = true;
- gif_result return_value;
-
- /* Get the frame to decode and our data position */
- frame = gif->frame_count;
-
- /* Get our buffer position etc. */
- gif_data = (unsigned char *)(gif->gif_data + gif->buffer_position);
- gif_end = (unsigned char *)(gif->gif_data + gif->buffer_size);
- gif_bytes = (gif_end - gif_data);
-
- /* Check if we've finished */
- if ((gif_bytes > 0) && (gif_data[0] == GIF_TRAILER)) {
- return GIF_OK;
- }
-
- /* Check if there is enough data remaining. The shortest block of data
- * is a 4-byte comment extension + 1-byte block terminator + 1-byte gif
- * trailer
- */
- if (gif_bytes < 6) {
- return GIF_INSUFFICIENT_DATA;
- }
-
- /* We could theoretically get some junk data that gives us millions of
- * frames, so we ensure that we don't have a silly number
- */
- if (frame > 4096) {
- return GIF_FRAME_DATA_ERROR;
- }
-
- /* Get some memory to store our pointers in etc. */
- if ((int)gif->frame_holders <= frame) {
- /* Allocate more memory */
- temp_buf = (gif_frame *)realloc(gif->frames, (frame + 1) * sizeof(gif_frame));
- if (temp_buf == NULL) {
- return GIF_INSUFFICIENT_MEMORY;
- }
- gif->frames = temp_buf;
- gif->frame_holders = frame + 1;
- }
-
- /* Store our frame pointer. We would do it when allocating except we
- * start off with one frame allocated so we can always use realloc.
- */
- gif->frames[frame].frame_pointer = gif->buffer_position;
- gif->frames[frame].display = false;
- gif->frames[frame].virgin = true;
- gif->frames[frame].disposal_method = 0;
- gif->frames[frame].transparency = false;
- gif->frames[frame].frame_delay = 100;
- gif->frames[frame].redraw_required = false;
-
- /* Invalidate any previous decoding we have of this frame */
- if (gif->decoded_frame == frame) {
- gif->decoded_frame = GIF_INVALID_FRAME;
- }
-
- /* We pretend to initialise the frames, but really we just skip over
- * all the data contained within. This is all basically a cut down
- * version of gif_decode_frame that doesn't have any of the LZW bits in
- * it.
- */
-
- /* Initialise any extensions */
- gif->buffer_position = gif_data - gif->gif_data;
- return_value = gif_initialise_frame_extensions(gif, frame);
- if (return_value != GIF_OK) {
- return return_value;
- }
- gif_data = (gif->gif_data + gif->buffer_position);
- gif_bytes = (gif_end - gif_data);
-
- /* Check if we've finished */
- if ((gif_bytes = (gif_end - gif_data)) < 1) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
-
- if (gif_data[0] == GIF_TRAILER) {
- gif->buffer_position = (gif_data - gif->gif_data);
- gif->frame_count = frame + 1;
- return GIF_OK;
- }
-
- /* If we're not done, there should be an image descriptor */
- if (gif_data[0] != GIF_IMAGE_SEPARATOR) {
- return GIF_FRAME_DATA_ERROR;
- }
-
- /* Do some simple boundary checking */
- if (gif_bytes < 10) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- offset_x = gif_data[1] | (gif_data[2] << 8);
- offset_y = gif_data[3] | (gif_data[4] << 8);
- width = gif_data[5] | (gif_data[6] << 8);
- height = gif_data[7] | (gif_data[8] << 8);
-
- /* Set up the redraw characteristics. We have to check for extending
- * the area due to multi-image frames.
- */
- if (!first_image) {
- if (gif->frames[frame].redraw_x > offset_x) {
- gif->frames[frame].redraw_width += (gif->frames[frame].redraw_x - offset_x);
- gif->frames[frame].redraw_x = offset_x;
- }
-
- if (gif->frames[frame].redraw_y > offset_y) {
- gif->frames[frame].redraw_height += (gif->frames[frame].redraw_y - offset_y);
- gif->frames[frame].redraw_y = offset_y;
- }
-
- if ((offset_x + width) > (gif->frames[frame].redraw_x + gif->frames[frame].redraw_width)) {
- gif->frames[frame].redraw_width = (offset_x + width) - gif->frames[frame].redraw_x;
- }
-
- if ((offset_y + height) > (gif->frames[frame].redraw_y + gif->frames[frame].redraw_height)) {
- gif->frames[frame].redraw_height = (offset_y + height) - gif->frames[frame].redraw_y;
- }
- } else {
- first_image = false;
- gif->frames[frame].redraw_x = offset_x;
- gif->frames[frame].redraw_y = offset_y;
- gif->frames[frame].redraw_width = width;
- gif->frames[frame].redraw_height = height;
- }
-
- /* if we are clearing the background then we need to redraw enough to
- * cover the previous frame too
- */
- gif->frames[frame].redraw_required =
- ((gif->frames[frame].disposal_method == GIF_FRAME_CLEAR) ||
- (gif->frames[frame].disposal_method == GIF_FRAME_RESTORE));
-
- /* Frame size may have grown.
- */
- gif->width = (offset_x + width > gif->width) ?
- offset_x + width : gif->width;
- gif->height = (offset_y + height > gif->height) ?
- offset_y + height : gif->height;
-
- /* Decode the flags */
- flags = gif_data[9];
- colour_table_size = 2 << (flags & GIF_COLOUR_TABLE_SIZE_MASK);
-
- /* Move our data onwards and remember we've got a bit of this frame */
- gif_data += 10;
- gif_bytes = (gif_end - gif_data);
- gif->frame_count_partial = frame + 1;
-
- /* Skip the local colour table */
- if (flags & GIF_COLOUR_TABLE_MASK) {
- gif_data += 3 * colour_table_size;
- if ((gif_bytes = (gif_end - gif_data)) < 0) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- }
-
- /* Ensure we have a correct code size */
- if (gif_bytes < 1) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- if (gif_data[0] >= LZW_CODE_MAX) {
- return GIF_DATA_ERROR;
- }
-
- /* Move our pointer to the actual image data */
- gif_data++;
- --gif_bytes;
-
- /* Repeatedly skip blocks until we get a zero block or run out of data
- * These blocks of image data are processed later by gif_decode_frame()
- */
- block_size = 0;
- while (block_size != 1) {
- if (gif_bytes < 1) return GIF_INSUFFICIENT_FRAME_DATA;
- block_size = gif_data[0] + 1;
- /* Check if the frame data runs off the end of the file */
- if ((int)(gif_bytes - block_size) < 0) {
- /* Try to recover by signaling the end of the gif.
- * Once we get garbage data, there is no logical way to
- * determine where the next frame is. It's probably
- * better to partially load the gif than not at all.
- */
- if (gif_bytes >= 2) {
- gif_data[0] = 0;
- gif_data[1] = GIF_TRAILER;
- gif_bytes = 1;
- ++gif_data;
- break;
- } else {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- } else {
- gif_bytes -= block_size;
- gif_data += block_size;
- }
- }
-
- /* Add the frame and set the display flag */
- gif->buffer_position = gif_data - gif->gif_data;
- gif->frame_count = frame + 1;
- gif->frames[frame].display = true;
-
- /* Check if we've finished */
- if (gif_bytes < 1) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- } else {
- if (gif_data[0] == GIF_TRAILER) {
- return GIF_OK;
- }
+ if (gif->bitmap_callbacks.bitmap_modified) {
+ gif->bitmap_callbacks.bitmap_modified(gif->frame_image);
}
- return GIF_WORKING;
}
-
/**
- * Skips the frame's extensions (which have been previously initialised)
+ * Helper to tell the client that whether the bitmap is opaque.
*
- * \param gif The animation context
- * \return GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the
- * frame GIF_OK for successful decoding
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frmae The frame that has been decoded.
*/
-static gif_result gif_skip_frame_extensions(gif_animation *gif)
+static inline void gif__bitmap_set_opaque(
+ const struct gif_animation *gif,
+ const struct gif_frame *frame)
{
- unsigned char *gif_data, *gif_end;
- int gif_bytes;
- unsigned int block_size;
-
- /* Get our buffer position etc. */
- gif_data = (unsigned char *)(gif->gif_data + gif->buffer_position);
- gif_end = (unsigned char *)(gif->gif_data + gif->buffer_size);
- gif_bytes = (gif_end - gif_data);
-
- /* Skip the extensions */
- while (gif_data < gif_end && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
- ++gif_data;
- if (gif_data >= gif_end) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
-
- /* Switch on extension label */
- switch(gif_data[0]) {
- case GIF_EXTENSION_COMMENT:
- /* Move the pointer to the first data sub-block
- * 1 byte for the extension label
- */
- ++gif_data;
- break;
-
- default:
- /* Move the pointer to the first data sub-block 2 bytes
- * for the extension label and size fields Skip the
- * extension size itself
- */
- if (gif_data + 1 >= gif_end) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- gif_data += (2 + gif_data[1]);
- }
-
- /* Repeatedly skip blocks until we get a zero block or run out
- * of data This data is ignored by this gif decoder
- */
- gif_bytes = (gif_end - gif_data);
- block_size = 0;
- while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) {
- block_size = gif_data[0] + 1;
- if ((gif_bytes -= block_size) < 0) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- gif_data += block_size;
- }
- ++gif_data;
+ if (gif->bitmap_callbacks.bitmap_set_opaque) {
+ gif->bitmap_callbacks.bitmap_set_opaque(
+ gif->frame_image, frame->opaque);
}
-
- /* Set buffer position and return */
- gif->buffer_position = (gif_data - gif->gif_data);
- return GIF_OK;
}
-static unsigned int gif_interlaced_line(int height, int y) {
- if ((y << 3) < height) {
- return (y << 3);
- }
- y -= ((height + 7) >> 3);
- if ((y << 3) < (height - 4)) {
- return (y << 3) + 4;
- }
- y -= ((height + 3) >> 3);
- if ((y << 2) < (height - 2)) {
- return (y << 2) + 2;
+/**
+ * Helper to get the client to determine if the bitmap is opaque.
+ *
+ * \todo: We don't really need to get the client to do this for us.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \return true if the bitmap is opaque, false otherwise.
+ */
+static inline bool gif__bitmap_get_opaque(
+ const struct gif_animation *gif)
+{
+ if (gif->bitmap_callbacks.bitmap_test_opaque) {
+ return gif->bitmap_callbacks.bitmap_test_opaque(
+ gif->frame_image);
}
- y -= ((height + 1) >> 2);
- return (y << 1) + 1;
-}
-
-static gif_result gif_error_from_lzw(lzw_result l_res)
-{
- static const gif_result g_res[] = {
- [LZW_OK] = GIF_OK,
- [LZW_OK_EOD] = GIF_END_OF_FRAME,
- [LZW_NO_MEM] = GIF_INSUFFICIENT_MEMORY,
- [LZW_NO_DATA] = GIF_INSUFFICIENT_FRAME_DATA,
- [LZW_EOI_CODE] = GIF_FRAME_DATA_ERROR,
- [LZW_BAD_ICODE] = GIF_FRAME_DATA_ERROR,
- [LZW_BAD_CODE] = GIF_FRAME_DATA_ERROR,
- };
- assert(l_res != LZW_BAD_PARAM);
- assert(l_res != LZW_NO_COLOUR);
- return g_res[l_res];
+ return false;
}
-static void gif__record_previous_frame(gif_animation *gif)
+static void gif__record_frame(
+ struct gif_animation *gif,
+ const uint32_t *bitmap)
{
bool need_alloc = gif->prev_frame == NULL;
- const uint32_t *frame_data;
uint32_t *prev_frame;
if (gif->decoded_frame == GIF_INVALID_FRAME ||
@@ -571,9 +194,8 @@ static void gif__record_previous_frame(gif_animation *gif)
return;
}
- assert(gif->bitmap_callbacks.bitmap_get_buffer);
- frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
- if (!frame_data) {
+ bitmap = gif__bitmap_get(gif);
+ if (bitmap == NULL) {
return;
}
@@ -592,7 +214,7 @@ static void gif__record_previous_frame(gif_animation *gif)
prev_frame = gif->prev_frame;
}
- memcpy(prev_frame, frame_data, gif->width * gif->height * 4);
+ memcpy(prev_frame, bitmap, gif->width * gif->height * 4);
gif->prev_frame = prev_frame;
gif->prev_width = gif->width;
@@ -600,72 +222,116 @@ static void gif__record_previous_frame(gif_animation *gif)
gif->prev_index = gif->decoded_frame;
}
-static gif_result gif__recover_previous_frame(const gif_animation *gif)
+static gif_result gif__recover_frame(
+ const struct gif_animation *gif,
+ uint32_t *bitmap)
{
const uint32_t *prev_frame = gif->prev_frame;
unsigned height = gif->height < gif->prev_height ? gif->height : gif->prev_height;
unsigned width = gif->width < gif->prev_width ? gif->width : gif->prev_width;
- uint32_t *frame_data;
if (prev_frame == NULL) {
return GIF_FRAME_DATA_ERROR;
}
- assert(gif->bitmap_callbacks.bitmap_get_buffer);
- frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
- if (!frame_data) {
- return GIF_INSUFFICIENT_MEMORY;
- }
-
for (unsigned y = 0; y < height; y++) {
- memcpy(frame_data, prev_frame, width * 4);
+ memcpy(bitmap, prev_frame, width * 4);
- frame_data += gif->width;
+ bitmap += gif->width;
prev_frame += gif->prev_width;
}
return GIF_OK;
}
-static gif_result
-gif__decode_complex(gif_animation *gif,
- unsigned int frame,
- unsigned int width,
- unsigned int height,
- unsigned int offset_x,
- unsigned int offset_y,
- unsigned int interlace,
- uint8_t minimum_code_size,
- unsigned int *restrict frame_data,
- unsigned int *restrict colour_table)
+/**
+ * Get the next line for GIF decode.
+ *
+ * Note that the step size must be initialised to 24 at the start of the frame
+ * (when y == 0). This is because of the first two passes of the frame have
+ * the same step size of 8, and the step size is used to determine the current
+ * pass.
+ *
+ * \param[in] height Frame height in pixels.
+ * \param[in,out] y Current row, starting from 0, updated on exit.
+ * \param[in,out] step Current step starting with 24, updated on exit.
+ * \return true if there is a row to process, false at the end of the frame.
+ */
+static inline bool gif__deinterlace(uint32_t height, uint32_t *y, uint8_t *step)
+{
+ *y += *step & 0xf;
+
+ if (*y < height) return true;
+
+ switch (*step) {
+ case 24: *y = 4; *step = 8; if (*y < height) return true;
+ /* Fall through. */
+ case 8: *y = 2; *step = 4; if (*y < height) return true;
+ /* Fall through. */
+ case 4: *y = 1; *step = 2; if (*y < height) return true;
+ /* Fall through. */
+ default:
+ break;
+ }
+
+ return false;
+}
+
+/**
+ * Get the next line for GIF decode.
+ *
+ * \param[in] interlace Non-zero if the frame is not interlaced.
+ * \param[in] height Frame height in pixels.
+ * \param[in,out] y Current row, starting from 0, updated on exit.
+ * \param[in,out] step Current step starting with 24, updated on exit.
+ * \return true if there is a row to process, false at the end of the frame.
+ */
+static inline bool gif__next_row(uint32_t interlace,
+ uint32_t height, uint32_t *y, uint8_t *step)
+{
+ if (!interlace) {
+ return (++*y != height);
+ } else {
+ return gif__deinterlace(height, y, step);
+ }
+}
+
+static gif_result gif__decode_complex(
+ struct gif_animation *gif,
+ uint32_t width,
+ uint32_t height,
+ uint32_t offset_x,
+ uint32_t offset_y,
+ uint32_t interlace,
+ const uint8_t *data,
+ uint32_t transparency_index,
+ uint32_t *restrict frame_data,
+ uint32_t *restrict colour_table)
{
- unsigned int transparency_index;
- uint32_t available = 0;
- gif_result ret = GIF_OK;
lzw_result res;
+ gif_result ret = GIF_OK;
+ uint32_t available = 0;
+ uint8_t step = 24;
+ uint32_t y = 0;
+
+ if (height == 0) {
+ return GIF_OK;
+ }
/* Initialise the LZW decoding */
- res = lzw_decode_init(gif->lzw_ctx, minimum_code_size,
- gif->gif_data, gif->buffer_size, gif->buffer_position);
+ res = lzw_decode_init(gif->lzw_ctx, data[0],
+ gif->gif_data, gif->buffer_size,
+ data + 1 - gif->gif_data);
if (res != LZW_OK) {
return gif_error_from_lzw(res);
}
- transparency_index = gif->frames[frame].transparency ?
- gif->frames[frame].transparency_index :
- GIF_NO_TRANSPARENCY;
+ do {
+ uint32_t x;
+ uint32_t *frame_scanline;
- for (unsigned int y = 0; y < height; y++) {
- unsigned int x;
- unsigned int decode_y;
- unsigned int *frame_scanline;
-
- if (interlace) {
- decode_y = gif_interlaced_line(height, y) + offset_y;
- } else {
- decode_y = y + offset_y;
- }
- frame_scanline = frame_data + offset_x + (decode_y * gif->width);
+ frame_scanline = frame_data + offset_x +
+ (y + offset_y) * gif->width;
x = width;
while (x > 0) {
@@ -695,7 +361,7 @@ gif__decode_complex(gif_animation *gif,
}
} else {
while (row_available-- > 0) {
- register unsigned int colour;
+ register uint32_t colour;
colour = *uncompressed++;
if (colour != transparency_index) {
*frame_scanline =
@@ -705,33 +371,30 @@ gif__decode_complex(gif_animation *gif,
}
}
}
- }
+ } while (gif__next_row(interlace, height, &y, &step));
+
return ret;
}
-static gif_result
-gif__decode_simple(gif_animation *gif,
- unsigned int frame,
- unsigned int height,
- unsigned int offset_y,
- uint8_t minimum_code_size,
- unsigned int *restrict frame_data,
- unsigned int *restrict colour_table)
+static gif_result gif__decode_simple(
+ struct gif_animation *gif,
+ uint32_t height,
+ uint32_t offset_y,
+ const uint8_t *data,
+ uint32_t transparency_index,
+ uint32_t *restrict frame_data,
+ uint32_t *restrict colour_table)
{
- unsigned int transparency_index;
uint32_t pixels = gif->width * height;
uint32_t written = 0;
gif_result ret = GIF_OK;
lzw_result res;
- transparency_index = gif->frames[frame].transparency ?
- gif->frames[frame].transparency_index :
- GIF_NO_TRANSPARENCY;
-
/* Initialise the LZW decoding */
- res = lzw_decode_init_map(gif->lzw_ctx,
- minimum_code_size, transparency_index, colour_table,
- gif->gif_data, gif->buffer_size, gif->buffer_position);
+ res = lzw_decode_init_map(gif->lzw_ctx, data[0],
+ transparency_index, colour_table,
+ gif->gif_data, gif->buffer_size,
+ data + 1 - gif->gif_data);
if (res != LZW_OK) {
return gif_error_from_lzw(res);
}
@@ -761,320 +424,685 @@ gif__decode_simple(gif_animation *gif,
return ret;
}
-static inline gif_result
-gif__decode(gif_animation *gif,
- unsigned int frame,
- unsigned int width,
- unsigned int height,
- unsigned int offset_x,
- unsigned int offset_y,
- unsigned int interlace,
- uint8_t minimum_code_size,
- unsigned int *restrict frame_data,
- unsigned int *restrict colour_table)
+static inline gif_result gif__decode(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ const uint8_t *data,
+ uint32_t *restrict frame_data)
{
gif_result ret;
+ uint32_t offset_x = frame->redraw_x;
+ uint32_t offset_y = frame->redraw_y;
+ uint32_t width = frame->redraw_width;
+ uint32_t height = frame->redraw_height;
+ uint32_t interlace = frame->flags & GIF_INTERLACE_MASK;
+ uint32_t transparency_index = frame->transparency_index;
+ uint32_t *restrict colour_table = gif->colour_table;
if (interlace == false && width == gif->width && offset_x == 0) {
- ret = gif__decode_simple(gif, frame, height, offset_y,
- minimum_code_size, frame_data, colour_table);
+ ret = gif__decode_simple(gif, height, offset_y,
+ data, transparency_index,
+ frame_data, colour_table);
} else {
- ret = gif__decode_complex(gif, frame, width, height,
+ ret = gif__decode_complex(gif, width, height,
offset_x, offset_y, interlace,
- minimum_code_size, frame_data, colour_table);
+ data, transparency_index,
+ frame_data, colour_table);
}
return ret;
}
/**
- * decode a gif frame
+ * Restore a GIF to the background colour.
*
- * \param gif gif animation context.
- * \param frame The frame number to decode.
- * \param clear_image flag for image data being cleared instead of plotted.
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to clear, or NULL.
+ * \param[in] bitmap The bitmap to clear the frame in.
*/
-static gif_result
-gif_internal_decode_frame(gif_animation *gif,
- unsigned int frame,
- bool clear_image)
+static void gif__restore_bg(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ uint32_t *bitmap)
{
- gif_result err;
- unsigned int index = 0;
- unsigned char *gif_data, *gif_end;
- int gif_bytes;
- unsigned int width, height, offset_x, offset_y;
- unsigned int flags, colour_table_size, interlace;
- unsigned int *colour_table;
- unsigned int *frame_data = 0; // Set to 0 for no warnings
- unsigned int save_buffer_position;
- unsigned int return_value = 0;
-
- /* Ensure this frame is supposed to be decoded */
- if (gif->frames[frame].display == false) {
- return GIF_OK;
- }
+ if (frame == NULL) {
+ memset(bitmap, GIF_TRANSPARENT_COLOUR,
+ gif->width * gif->height * sizeof(*bitmap));
+ } else {
+ uint32_t offset_x = frame->redraw_x;
+ uint32_t offset_y = frame->redraw_y;
+ uint32_t width = frame->redraw_width;
+ uint32_t height = frame->redraw_height;
- /* Ensure the frame is in range to decode */
- if (frame > gif->frame_count_partial) {
- return GIF_INSUFFICIENT_DATA;
- }
+ if (frame->display == false) {
+ return;
+ }
- /* done if frame is already decoded */
- if ((!clear_image) &&
- ((int)frame == gif->decoded_frame)) {
- return GIF_OK;
+ if (frame->transparency) {
+ for (uint32_t y = 0; y < height; y++) {
+ uint32_t *scanline = bitmap + offset_x +
+ (offset_y + y) * gif->width;
+ memset(scanline, GIF_TRANSPARENT_COLOUR,
+ width * sizeof(*bitmap));
+ }
+ } else {
+ for (uint32_t y = 0; y < height; y++) {
+ uint32_t *scanline = bitmap + offset_x +
+ (offset_y + y) * gif->width;
+ for (uint32_t x = 0; x < width; x++) {
+ scanline[x] = gif->bg_colour;
+ }
+ }
+ }
}
+}
- /* Get the start of our frame data and the end of the GIF data */
- gif_data = gif->gif_data + gif->frames[frame].frame_pointer;
- gif_end = gif->gif_data + gif->buffer_size;
- gif_bytes = (gif_end - gif_data);
+static gif_result gif__update_bitmap(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ const uint8_t *data,
+ uint32_t frame_idx)
+{
+ gif_result ret;
+ uint32_t *bitmap;
- /*
- * Ensure there is a minimal amount of data to proceed. The shortest
- * block of data is a 10-byte image descriptor + 1-byte gif trailer
- */
- if (gif_bytes < 12) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ gif->decoded_frame = frame_idx;
+
+ bitmap = gif__bitmap_get(gif);
+ if (bitmap == NULL) {
+ return GIF_INSUFFICIENT_MEMORY;
}
- /* Save the buffer position */
- save_buffer_position = gif->buffer_position;
- gif->buffer_position = gif_data - gif->gif_data;
+ /* Handle any bitmap clearing/restoration required before decoding this
+ * frame. */
+ if (frame_idx == 0 || gif->decoded_frame == GIF_INVALID_FRAME) {
+ gif__restore_bg(gif, NULL, bitmap);
+
+ } else {
+ struct gif_frame *prev = &gif->frames[frame_idx - 1];
+
+ if (prev->disposal_method == GIF_DISPOSAL_RESTORE_BG) {
+ gif__restore_bg(gif, prev, bitmap);
- /* Skip any extensions because they have already been processed */
- if ((return_value = gif_skip_frame_extensions(gif)) != GIF_OK) {
- goto gif_decode_frame_exit;
+ } else if (prev->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
+ ret = gif__recover_frame(gif, bitmap);
+ if (ret != GIF_OK) {
+ gif__restore_bg(gif, prev, bitmap);
+ }
+ }
}
- gif_data = (gif->gif_data + gif->buffer_position);
- gif_bytes = (gif_end - gif_data);
- /* Ensure we have enough data for the 10-byte image descriptor + 1-byte
- * gif trailer
- */
- if (gif_bytes < 12) {
- return_value = GIF_INSUFFICIENT_FRAME_DATA;
- goto gif_decode_frame_exit;
+ if (frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
+ /* Store the previous frame for later restoration */
+ gif__record_frame(gif, bitmap);
}
- /* 10-byte Image Descriptor is:
- *
- * +0 CHAR Image Separator (0x2c)
- * +1 SHORT Image Left Position
- * +3 SHORT Image Top Position
- * +5 SHORT Width
- * +7 SHORT Height
- * +9 CHAR __Packed Fields__
- * 1BIT Local Colour Table Flag
- * 1BIT Interlace Flag
- * 1BIT Sort Flag
- * 2BITS Reserved
- * 3BITS Size of Local Colour Table
- */
- if (gif_data[0] != GIF_IMAGE_SEPARATOR) {
- return_value = GIF_DATA_ERROR;
- goto gif_decode_frame_exit;
+ ret = gif__decode(gif, frame, data, bitmap);
+
+ gif__bitmap_modified(gif);
+
+ if (frame->virgin) {
+ frame->opaque = gif__bitmap_get_opaque(gif);
+ frame->virgin = false;
}
- offset_x = gif_data[1] | (gif_data[2] << 8);
- offset_y = gif_data[3] | (gif_data[4] << 8);
- width = gif_data[5] | (gif_data[6] << 8);
- height = gif_data[7] | (gif_data[8] << 8);
+ gif__bitmap_set_opaque(gif, frame);
+
+ return ret;
+}
- /* Boundary checking - shouldn't ever happen except unless the data has
- * been modified since initialisation.
+/**
+ * Parse the application extension
+ *
+ * \param[in] frame The gif object we're decoding.
+ * \param[in] data The data to decode.
+ * \param[in] len Byte length of data.
+ * \return GIF_INSUFFICIENT_DATA if more data is needed,
+ * GIF_OK for success.
+ */
+static gif_result gif__parse_extension_graphic_control(
+ struct gif_frame *frame,
+ const uint8_t *data,
+ size_t len)
+{
+ /* 6-byte Graphic Control Extension is:
+ *
+ * +0 CHAR Graphic Control Label
+ * +1 CHAR Block Size
+ * +2 CHAR __Packed Fields__
+ * 3BITS Reserved
+ * 3BITS Disposal Method
+ * 1BIT User Input Flag
+ * 1BIT Transparent Color Flag
+ * +3 SHORT Delay Time
+ * +5 CHAR Transparent Color Index
*/
- if ((offset_x + width > gif->width) ||
- (offset_y + height > gif->height)) {
- return_value = GIF_DATA_ERROR;
- goto gif_decode_frame_exit;
+ if (len < 6) {
+ return GIF_INSUFFICIENT_DATA;
}
- /* Make sure we have a buffer to decode to.
- */
- if (gif_initialise_sprite(gif, gif->width, gif->height)) {
- return GIF_INSUFFICIENT_MEMORY;
+ frame->frame_delay = data[3] | (data[4] << 8);
+ if (data[2] & GIF_TRANSPARENCY_MASK) {
+ frame->transparency = true;
+ frame->transparency_index = data[5];
}
- /* Decode the flags */
- flags = gif_data[9];
- colour_table_size = 2 << (flags & GIF_COLOUR_TABLE_SIZE_MASK);
- interlace = flags & GIF_INTERLACE_MASK;
+ frame->disposal_method = ((data[2] & GIF_DISPOSAL_MASK) >> 2);
+ /* I have encountered documentation and GIFs in the
+ * wild that use 0x04 to restore the previous frame,
+ * rather than the officially documented 0x03. I
+ * believe some (older?) software may even actually
+ * export this way. We handle this as a type of
+ * "quirks" mode. */
+ if (frame->disposal_method == GIF_DISPOSAL_RESTORE_QUIRK) {
+ frame->disposal_method = GIF_DISPOSAL_RESTORE_PREV;
+ }
+
+ /* if we are clearing the background then we need to
+ * redraw enough to cover the previous frame too. */
+ frame->redraw_required =
+ frame->disposal_method == GIF_DISPOSAL_RESTORE_BG ||
+ frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV;
+
+ return GIF_OK;
+}
- /* Advance data pointer to next block either colour table or image
- * data.
+/**
+ * Parse the application extension
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] data The data to decode.
+ * \param[in] len Byte length of data.
+ * \return GIF_INSUFFICIENT_DATA if more data is needed,
+ * GIF_OK for success.
+ */
+static gif_result gif__parse_extension_application(
+ struct gif_animation *gif,
+ const uint8_t *data,
+ size_t len)
+{
+ /* 14-byte+ Application Extension is:
+ *
+ * +0 CHAR Application Extension Label
+ * +1 CHAR Block Size
+ * +2 8CHARS Application Identifier
+ * +10 3CHARS Appl. Authentication Code
+ * +13 1-256 Application Data (Data sub-blocks)
*/
- gif_data += 10;
- gif_bytes = (gif_end - gif_data);
-
- /* Set up the colour table */
- if (flags & GIF_COLOUR_TABLE_MASK) {
- if (gif_bytes < (int)(3 * colour_table_size)) {
- return_value = GIF_INSUFFICIENT_FRAME_DATA;
- goto gif_decode_frame_exit;
+ if (len < 17) {
+ return GIF_INSUFFICIENT_DATA;
+ }
+
+ if ((data[1] == 0x0b) &&
+ (strncmp((const char *)data + 2, "NETSCAPE2.0", 11) == 0) &&
+ (data[13] == 0x03) && (data[14] == 0x01)) {
+ gif->loop_count = data[15] | (data[16] << 8);
+ }
+
+ return GIF_OK;
+}
+
+/**
+ * Parse the frame's extensions
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to parse extensions for.
+ * \param[in] decode Whether to decode or skip over the extension.
+ * \return GIF_INSUFFICIENT_DATA if more data is needed,
+ * GIF_OK for success.
+ */
+static gif_result gif__parse_frame_extensions(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ const uint8_t **pos,
+ bool decode)
+{
+ const uint8_t *gif_data = *pos;
+ const uint8_t *gif_end = gif->gif_data + gif->buffer_size;
+ int gif_bytes = gif_end - gif_data;
+
+ /* Initialise the extensions */
+ while (gif_bytes > 0 && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
+ bool block_step = true;
+ gif_result ret;
+
+ gif_data++;
+ gif_bytes--;
+
+ if (gif_bytes == 0) {
+ return GIF_INSUFFICIENT_DATA;
}
- colour_table = gif->local_colour_table;
- if (!clear_image) {
- for (index = 0; index < colour_table_size; index++) {
- /* Gif colour map contents are r,g,b.
- *
- * We want to pack them bytewise into the
- * colour table, such that the red component
- * is in byte 0 and the alpha component is in
- * byte 3.
- */
- unsigned char *entry =
- (unsigned char *) &colour_table[index];
-
- entry[0] = gif_data[0]; /* r */
- entry[1] = gif_data[1]; /* g */
- entry[2] = gif_data[2]; /* b */
- entry[3] = 0xff; /* a */
-
- gif_data += 3;
+
+ /* Switch on extension label */
+ switch (gif_data[0]) {
+ case GIF_EXTENSION_GRAPHIC_CONTROL:
+ if (decode) {
+ ret = gif__parse_extension_graphic_control(
+ frame, gif_data, gif_bytes);
+ if (ret != GIF_OK) {
+ return ret;
+ }
}
- } else {
- gif_data += 3 * colour_table_size;
+ break;
+
+ case GIF_EXTENSION_APPLICATION:
+ if (decode) {
+ ret = gif__parse_extension_application(
+ gif, gif_data, gif_bytes);
+ if (ret != GIF_OK) {
+ return ret;
+ }
+ }
+ break;
+
+ case GIF_EXTENSION_COMMENT:
+ /* Move the pointer to the first data sub-block Skip 1
+ * byte for the extension label. */
+ ++gif_data;
+ block_step = false;
+ break;
+
+ default:
+ break;
+ }
+
+ if (block_step) {
+ /* Move the pointer to the first data sub-block Skip 2
+ * bytes for the extension label and size fields Skip
+ * the extension size itself
+ */
+ if (gif_bytes < 2) {
+ return GIF_INSUFFICIENT_DATA;
+ }
+ gif_data += 2 + gif_data[1];
}
- gif_bytes = (gif_end - gif_data);
- } else {
- colour_table = gif->global_colour_table;
- }
- /* Ensure sufficient data remains */
- if (gif_bytes < 1) {
- return_value = GIF_INSUFFICIENT_FRAME_DATA;
- goto gif_decode_frame_exit;
+ /* Repeatedly skip blocks until we get a zero block or run out
+ * of data. This data is ignored by this gif decoder. */
+ while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) {
+ gif_data += gif_data[0] + 1;
+ if (gif_data >= gif_end) {
+ return GIF_INSUFFICIENT_DATA;
+ }
+ }
+ gif_data++;
+ gif_bytes = gif_end - gif_data;
}
- /* check for an end marker */
- if (gif_data[0] == GIF_TRAILER) {
- return_value = GIF_OK;
- goto gif_decode_frame_exit;
+ if (gif_data > gif_end) {
+ gif_data = gif_end;
}
- /* Get the frame data */
- assert(gif->bitmap_callbacks.bitmap_get_buffer);
- frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
- if (!frame_data) {
- return GIF_INSUFFICIENT_MEMORY;
+ /* Set buffer position and return */
+ *pos = gif_data;
+ return GIF_OK;
+}
+
+/**
+ * Parse a GIF Image Descriptor.
+ *
+ * The format is:
+ *
+ * +0 CHAR Image Separator (0x2c)
+ * +1 SHORT Image Left Position
+ * +3 SHORT Image Top Position
+ * +5 SHORT Width
+ * +7 SHORT Height
+ * +9 CHAR __Packed Fields__
+ * 1BIT Local Colour Table Flag
+ * 1BIT Interlace Flag
+ * 1BIT Sort Flag
+ * 2BITS Reserved
+ * 3BITS Size of Local Colour Table
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to parse an image descriptor for.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_image_descriptor(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ const uint8_t **pos,
+ bool decode)
+{
+ const uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
+ enum {
+ GIF_IMAGE_DESCRIPTOR_LEN = 10u,
+ GIF_IMAGE_SEPARATOR = 0x2Cu,
+ };
+
+ assert(gif != NULL);
+ assert(frame != NULL);
+
+ if (len < GIF_IMAGE_DESCRIPTOR_LEN) {
+ return GIF_INSUFFICIENT_DATA;
}
- /* If we are clearing the image we just clear, if not decode */
- if (!clear_image) {
- /* Ensure we have enough data for a 1-byte LZW code size +
- * 1-byte gif trailer
- */
- if (gif_bytes < 2) {
- return_value = GIF_INSUFFICIENT_FRAME_DATA;
- goto gif_decode_frame_exit;
- }
+ if (decode) {
+ unsigned x, y, w, h;
- /* If we only have a 1-byte LZW code size + 1-byte gif trailer,
- * we're finished
- */
- if ((gif_bytes == 2) && (gif_data[1] == GIF_TRAILER)) {
- return_value = GIF_OK;
- goto gif_decode_frame_exit;
+ if (data[0] != GIF_IMAGE_SEPARATOR) {
+ return GIF_FRAME_DATA_ERROR;
}
- /* If the previous frame's disposal method requires we restore
- * the background colour or this is the first frame, clear
- * the frame data
- */
- if ((frame == 0) || (gif->decoded_frame == GIF_INVALID_FRAME)) {
- memset((char*)frame_data,
- GIF_TRANSPARENT_COLOUR,
- gif->width * gif->height * sizeof(int));
- gif->decoded_frame = frame;
- /* The line below would fill the image with its
- * background color, but because GIFs support
- * transparency we likely wouldn't want to do that. */
- /* memset((char*)frame_data, colour_table[gif->background_index], gif->width * gif->height * sizeof(int)); */
- } else if ((frame != 0) &&
- (gif->frames[frame - 1].disposal_method == GIF_FRAME_CLEAR)) {
- return_value = gif_internal_decode_frame(gif,
- (frame - 1),
- true);
- if (return_value != GIF_OK) {
- goto gif_decode_frame_exit;
- }
+ x = data[1] | (data[2] << 8);
+ y = data[3] | (data[4] << 8);
+ w = data[5] | (data[6] << 8);
+ h = data[7] | (data[8] << 8);
+ frame->flags = data[9];
+
+ frame->redraw_x = x;
+ frame->redraw_y = y;
+ frame->redraw_width = w;
+ frame->redraw_height = h;
+
+ /* Frame size may have grown. */
+ gif->width = (x + w > gif->width ) ? x + w : gif->width;
+ gif->height = (y + h > gif->height) ? y + h : gif->height;
+ }
+
+ *pos += GIF_IMAGE_DESCRIPTOR_LEN;
+ return GIF_OK;
+}
- } else if ((frame != 0) &&
- (gif->frames[frame - 1].disposal_method == GIF_FRAME_RESTORE)) {
- /*
- * If the previous frame's disposal method requires we
- * restore the previous image, restore our saved image.
+/**
+ * Extract a GIF colour table into a LibNSGIF colour table buffer.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] colour_table The colour table to populate.
+ * \param[in] colour_table_entries The number of colour table entries.
+ * \param[in] pos Current position in data, updated on exit.
+ * \param[in] decode Whether to decode the colour table.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__colour_table_extract(
+ struct gif_animation *gif,
+ uint32_t *colour_table,
+ size_t colour_table_entries,
+ const uint8_t **pos,
+ bool decode)
+{
+ const uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
+
+ if (len < colour_table_entries * 3) {
+ return GIF_INSUFFICIENT_DATA;
+ }
+
+ if (decode) {
+ int count = colour_table_entries;
+ uint8_t *entry = (uint8_t *)colour_table;
+
+ while (count--) {
+ /* Gif colour map contents are r,g,b.
+ *
+ * We want to pack them bytewise into the
+ * colour table, such that the red component
+ * is in byte 0 and the alpha component is in
+ * byte 3.
*/
- err = gif__recover_previous_frame(gif);
- if (err != GIF_OK) {
- /* see notes above on transparency
- * vs. background color
- */
- memset((char*)frame_data,
- GIF_TRANSPARENT_COLOUR,
- gif->width * gif->height * sizeof(int));
+
+ *entry++ = *data++; /* r */
+ *entry++ = *data++; /* g */
+ *entry++ = *data++; /* b */
+ *entry++ = 0xff; /* a */
+ }
+ }
+
+ *pos += colour_table_entries * 3;
+ return GIF_OK;
+}
+
+/**
+ * Get a frame's colour table.
+ *
+ * Sets up gif->colour_table for the frame.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to get the colour table for.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_colour_table(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ const uint8_t **pos,
+ bool decode)
+{
+ gif_result ret;
+
+ assert(gif != NULL);
+ assert(frame != NULL);
+
+ if ((frame->flags & GIF_COLOUR_TABLE_MASK) == 0) {
+ gif->colour_table = gif->global_colour_table;
+ return GIF_OK;
+ }
+
+ ret = gif__colour_table_extract(gif, gif->local_colour_table,
+ 2 << (frame->flags & GIF_COLOUR_TABLE_SIZE_MASK),
+ pos, decode);
+ if (ret != GIF_OK) {
+ return ret;
+ }
+
+ gif->colour_table = gif->local_colour_table;
+ return GIF_OK;
+}
+
+/**
+ * Parse the image data for a gif frame.
+ *
+ * Sets up gif->colour_table for the frame.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to get the colour table for.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_image_data(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ const uint8_t **pos,
+ bool decode)
+{
+ const uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
+ uint32_t frame_idx = frame - gif->frames;
+ uint8_t minimum_code_size;
+ gif_result ret;
+
+ assert(gif != NULL);
+ assert(frame != NULL);
+
+ if (!decode) {
+ gif->frame_count_partial = frame_idx + 1;
+ }
+
+ /* Ensure sufficient data remains. A gif trailer or a minimum lzw code
+ * followed by a gif trailer is treated as OK, although without any
+ * image data. */
+ switch (len) {
+ default: if (data[0] == GIF_TRAILER) return GIF_OK;
+ break;
+ case 2: if (data[1] == GIF_TRAILER) return GIF_OK;
+ /* Fall through. */
+ case 1: if (data[0] == GIF_TRAILER) return GIF_OK;
+ /* Fall through. */
+ case 0: return GIF_INSUFFICIENT_DATA;
+ }
+
+ minimum_code_size = data[0];
+ if (minimum_code_size >= LZW_CODE_MAX) {
+ return GIF_DATA_ERROR;
+ }
+
+ if (decode) {
+ ret = gif__update_bitmap(gif, frame, data, frame_idx);
+ } else {
+ uint32_t block_size = 0;
+
+ /* Skip the minimum code size. */
+ data++;
+ len--;
+
+ while (block_size != 1) {
+ if (len < 1) return GIF_INSUFFICIENT_DATA;
+ block_size = data[0] + 1;
+ /* Check if the frame data runs off the end of the file */
+ if (block_size > len) {
+ block_size = len;
+ return GIF_OK;
}
+
+ len -= block_size;
+ data += block_size;
}
- if (gif->frames[frame].disposal_method == GIF_FRAME_RESTORE) {
- /* Store the previous frame for later restoration */
- gif__record_previous_frame(gif);
+ gif->frame_count = frame_idx + 1;
+ gif->frames[frame_idx].display = true;
+ *pos = data;
+
+ /* Check if we've finished */
+ if (len < 1) {
+ return GIF_INSUFFICIENT_DATA;
+ } else {
+ if (data[0] == GIF_TRAILER) {
+ return GIF_OK;
+ }
}
- gif->decoded_frame = frame;
- gif->buffer_position = (gif_data - gif->gif_data) + 1;
+ return GIF_WORKING;
+ }
- return_value = gif__decode(gif, frame, width, height,
- offset_x, offset_y, interlace, gif_data[0],
- frame_data, colour_table);
+ return ret;
+}
+
+static struct gif_frame *gif__get_frame(
+ struct gif_animation *gif,
+ uint32_t frame_idx)
+{
+ struct gif_frame *frame;
+
+ if (gif->frame_holders > frame_idx) {
+ frame = &gif->frames[frame_idx];
} else {
- /* Clear our frame */
- if (gif->frames[frame].disposal_method == GIF_FRAME_CLEAR) {
- unsigned int y;
- for (y = 0; y < height; y++) {
- unsigned int *frame_scanline;
- frame_scanline = frame_data + offset_x + ((offset_y + y) * gif->width);
- if (gif->frames[frame].transparency) {
- memset(frame_scanline,
- GIF_TRANSPARENT_COLOUR,
- width * 4);
- } else {
- memset(frame_scanline,
- colour_table[gif->background_index],
- width * 4);
- }
- }
+ /* Allocate more memory */
+ size_t count = frame_idx + 1;
+ struct gif_frame *temp;
+
+ temp = realloc(gif->frames, count * sizeof(*frame));
+ if (temp == NULL) {
+ return NULL;
}
+ gif->frames = temp;
+ gif->frame_holders = count;
+
+ frame = &gif->frames[frame_idx];
+
+ frame->transparency = false;
+ frame->transparency_index = GIF_NO_TRANSPARENCY;
+ frame->frame_pointer = gif->buffer_position;
+ frame->redraw_required = false;
+ frame->disposal_method = 0;
+ frame->frame_delay = 100;
+ frame->display = false;
+ frame->virgin = true;
+ }
+
+ return frame;
+}
+
+/**
+ * Attempts to initialise the next frame
+ *
+ * \param[in] gif The animation context
+ * \param[in] frame_idx The frame number to decode.
+ * \param[in] decode Whether to decode the graphical image data.
+ * \return error code
+ * - GIF_INSUFFICIENT_DATA reached unexpected end of source data.
+ * - GIF_FRAME_DATA_ERROR for GIF frame data error
+ * - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
+ * - GIF_DATA_ERROR for GIF error (invalid frame header)
+ * - GIF_OK for successful decoding
+ * - GIF_WORKING for successful decoding if more frames are expected
+*/
+static gif_result gif__process_frame(
+ struct gif_animation *gif,
+ uint32_t frame_idx,
+ bool decode)
+{
+ gif_result ret;
+ const uint8_t *pos;
+ const uint8_t *end;
+ struct gif_frame *frame;
+
+ frame = gif__get_frame(gif, frame_idx);
+ if (frame == NULL) {
+ return GIF_INSUFFICIENT_MEMORY;
}
-gif_decode_frame_exit:
- if (!clear_image) {
- if (gif->bitmap_callbacks.bitmap_modified) {
- gif->bitmap_callbacks.bitmap_modified(gif->frame_image);
+ end = gif->gif_data + gif->buffer_size;
+
+ if (decode) {
+ pos = gif->gif_data + frame->frame_pointer;
+
+ /* Ensure this frame is supposed to be decoded */
+ if (frame->display == false) {
+ return GIF_OK;
}
- /* Check if we should test for optimisation */
- if (gif->frames[frame].virgin) {
- if (gif->bitmap_callbacks.bitmap_test_opaque) {
- gif->frames[frame].opaque = gif->bitmap_callbacks.bitmap_test_opaque(gif->frame_image);
- } else {
- gif->frames[frame].opaque = false;
- }
- gif->frames[frame].virgin = false;
+ /* Ensure the frame is in range to decode */
+ if (frame_idx > gif->frame_count_partial) {
+ return GIF_INSUFFICIENT_DATA;
}
- if (gif->bitmap_callbacks.bitmap_set_opaque) {
- gif->bitmap_callbacks.bitmap_set_opaque(gif->frame_image, gif->frames[frame].opaque);
+ /* Done if frame is already decoded */
+ if ((int)frame_idx == gif->decoded_frame) {
+ return GIF_OK;
+ }
+ } else {
+ pos = (uint8_t *)(gif->gif_data + gif->buffer_position);
+
+ /* Check if we've finished */
+ if (pos < end && pos[0] == GIF_TRAILER) {
+ return GIF_OK;
+ }
+
+ /* We could theoretically get some junk data that gives us
+ * millions of frames, so we ensure that we don't have a
+ * silly number. */
+ if (frame_idx > 4096) {
+ return GIF_FRAME_DATA_ERROR;
}
}
- /* Restore the buffer position */
- gif->buffer_position = save_buffer_position;
+ /* Initialise any extensions */
+ ret = gif__parse_frame_extensions(gif, frame, &pos, !decode);
+ if (ret != GIF_OK) {
+ goto cleanup;
+ }
- return return_value;
-}
+ ret = gif__parse_image_descriptor(gif, frame, &pos, !decode);
+ if (ret != GIF_OK) {
+ goto cleanup;
+ }
+ ret = gif__parse_colour_table(gif, frame, &pos, decode);
+ if (ret != GIF_OK) {
+ goto cleanup;
+ }
+
+ ret = gif__parse_image_data(gif, frame, &pos, decode);
+ if (ret != GIF_OK) {
+ goto cleanup;
+ }
+
+cleanup:
+ if (!decode) {
+ gif->buffer_position = pos - gif->gif_data;
+ }
+
+ return ret;
+}
/* exported function documented in libnsgif.h */
void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks)
@@ -1085,33 +1113,100 @@ void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks)
gif->prev_index = GIF_INVALID_FRAME;
}
-
-/* exported function documented in libnsgif.h */
-gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data)
+/**
+ * Read GIF header.
+ *
+ * 6-byte GIF file header is:
+ *
+ * +0 3CHARS Signature ('GIF')
+ * +3 3CHARS Version ('87a' or '89a')
+ *
+ * \param[in] gif The GIF object we're decoding.
+ * \param[in,out] pos The current buffer position, updated on success.
+ * \param[in] strict Whether to require a known GIF version.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_header(
+ struct gif_animation *gif,
+ const uint8_t **pos,
+ bool strict)
{
- unsigned char *gif_data;
- unsigned int index;
- gif_result return_value;
+ const uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
- /* Initialize values */
- gif->buffer_size = size;
- gif->gif_data = data;
+ if (len < 6) {
+ return GIF_INSUFFICIENT_DATA;
+ }
- if (gif->lzw_ctx == NULL) {
- lzw_result res = lzw_context_create(
- (struct lzw_ctx **)&gif->lzw_ctx);
- if (res != LZW_OK) {
- return gif_error_from_lzw(res);
+ if (strncmp((const char *) data, "GIF", 3) != 0) {
+ return GIF_DATA_ERROR;
+ }
+ data += 3;
+
+ if (strict == true) {
+ if ((strncmp((const char *) data, "87a", 3) != 0) &&
+ (strncmp((const char *) data, "89a", 3) != 0)) {
+ return GIF_DATA_ERROR;
}
}
+ data += 3;
- /* Check for sufficient data to be a GIF (6-byte header + 7-byte
- * logical screen descriptor)
- */
- if (gif->buffer_size < GIF_STANDARD_HEADER_SIZE) {
+ *pos = data;
+ return GIF_OK;
+}
+
+/**
+ * Read Logical Screen Descriptor.
+ *
+ * 7-byte Logical Screen Descriptor is:
+ *
+ * +0 SHORT Logical Screen Width
+ * +2 SHORT Logical Screen Height
+ * +4 CHAR __Packed Fields__
+ * 1BIT Global Colour Table Flag
+ * 3BITS Colour Resolution
+ * 1BIT Sort Flag
+ * 3BITS Size of Global Colour Table
+ * +5 CHAR Background Colour Index
+ * +6 CHAR Pixel Aspect Ratio
+ *
+ * \param[in] gif The GIF object we're decoding.
+ * \param[in,out] pos The current buffer position, updated on success.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_logical_screen_descriptor(
+ struct gif_animation *gif,
+ const uint8_t **pos)
+{
+ const uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
+
+ if (len < 7) {
return GIF_INSUFFICIENT_DATA;
}
+ gif->width = data[0] | (data[1] << 8);
+ gif->height = data[2] | (data[3] << 8);
+ gif->global_colours = data[4] & GIF_COLOUR_TABLE_MASK;
+ gif->colour_table_size = 2 << (data[4] & GIF_COLOUR_TABLE_SIZE_MASK);
+ gif->bg_index = data[5];
+ gif->aspect_ratio = data[6];
+ gif->loop_count = 1;
+
+ *pos += 7;
+ return GIF_OK;
+}
+
+/* exported function documented in libnsgif.h */
+gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
+{
+ const uint8_t *gif_data;
+ gif_result ret;
+
+ /* Initialize values */
+ gif->buffer_size = size;
+ gif->gif_data = data;
+
/* Get our current processing position */
gif_data = gif->gif_data + gif->buffer_position;
@@ -1122,6 +1217,7 @@ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data)
*/
gif->frame_image = NULL;
gif->frames = NULL;
+ gif->frame_holders = 0;
gif->local_colour_table = NULL;
gif->global_colour_table = NULL;
@@ -1130,44 +1226,18 @@ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data)
gif->frame_count_partial = 0;
gif->decoded_frame = GIF_INVALID_FRAME;
- /* 6-byte GIF file header is:
- *
- * +0 3CHARS Signature ('GIF')
- * +3 3CHARS Version ('87a' or '89a')
- */
- if (strncmp((const char *) gif_data, "GIF", 3) != 0) {
- return GIF_DATA_ERROR;
+ ret = gif__parse_header(gif, &gif_data, false);
+ if (ret != GIF_OK) {
+ return ret;
}
- gif_data += 3;
- /* Ensure GIF reports version 87a or 89a */
- /*
- if ((strncmp(gif_data, "87a", 3) != 0) &&
- (strncmp(gif_data, "89a", 3) != 0))
- LOG(("Unknown GIF format - proceeding anyway"));
- */
- gif_data += 3;
-
- /* 7-byte Logical Screen Descriptor is:
- *
- * +0 SHORT Logical Screen Width
- * +2 SHORT Logical Screen Height
- * +4 CHAR __Packed Fields__
- * 1BIT Global Colour Table Flag
- * 3BITS Colour Resolution
- * 1BIT Sort Flag
- * 3BITS Size of Global Colour Table
- * +5 CHAR Background Colour Index
- * +6 CHAR Pixel Aspect Ratio
- */
- gif->width = gif_data[0] | (gif_data[1] << 8);
- gif->height = gif_data[2] | (gif_data[3] << 8);
- gif->global_colours = (gif_data[4] & GIF_COLOUR_TABLE_MASK);
- gif->colour_table_size = (2 << (gif_data[4] & GIF_COLOUR_TABLE_SIZE_MASK));
- gif->background_index = gif_data[5];
- gif->aspect_ratio = gif_data[6];
- gif->loop_count = 1;
- gif_data += 7;
+ ret = gif__parse_logical_screen_descriptor(gif, &gif_data);
+ if (ret != GIF_OK) {
+ return ret;
+ }
+
+ /* Remember we've done this now */
+ gif->buffer_position = gif_data - gif->gif_data;
/* Some broken GIFs report the size as the screen size they
* were created in. As such, we detect for the common cases and
@@ -1191,8 +1261,8 @@ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data)
* is lying to us. It's far better to give the wrong colours
* than to trample over some memory somewhere.
*/
- gif->global_colour_table = calloc(GIF_MAX_COLOURS, sizeof(unsigned int));
- gif->local_colour_table = calloc(GIF_MAX_COLOURS, sizeof(unsigned int));
+ gif->global_colour_table = calloc(GIF_MAX_COLOURS, sizeof(uint32_t));
+ gif->local_colour_table = calloc(GIF_MAX_COLOURS, sizeof(uint32_t));
if ((gif->global_colour_table == NULL) ||
(gif->local_colour_table == NULL)) {
gif_finalise(gif);
@@ -1208,23 +1278,11 @@ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data)
* termination block) Although generally useless, the GIF
* specification does not expressly prohibit this
*/
- if (gif->buffer_size == (GIF_STANDARD_HEADER_SIZE + 1)) {
+ if (gif->buffer_size == gif->buffer_position + 1) {
if (gif_data[0] == GIF_TRAILER) {
return GIF_OK;
- } else {
- return GIF_INSUFFICIENT_DATA;
}
}
-
- /* Initialise enough workspace for a frame */
- if ((gif->frames = (gif_frame *)malloc(sizeof(gif_frame))) == NULL) {
- gif_finalise(gif);
- return GIF_INSUFFICIENT_MEMORY;
- }
- gif->frame_holders = 1;
-
- /* Remember we've done this now */
- gif->buffer_position = gif_data - gif->gif_data;
}
/* Do the colour map if we haven't already. As the top byte is always
@@ -1234,71 +1292,59 @@ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data)
if (gif->global_colour_table[0] == GIF_PROCESS_COLOURS) {
/* Check for a global colour map signified by bit 7 */
if (gif->global_colours) {
- if (gif->buffer_size < (gif->colour_table_size * 3 + GIF_STANDARD_HEADER_SIZE)) {
- return GIF_INSUFFICIENT_DATA;
- }
- for (index = 0; index < gif->colour_table_size; index++) {
- /* Gif colour map contents are r,g,b.
- *
- * We want to pack them bytewise into the
- * colour table, such that the red component
- * is in byte 0 and the alpha component is in
- * byte 3.
- */
- unsigned char *entry = (unsigned char *) &gif->
- global_colour_table[index];
-
- entry[0] = gif_data[0]; /* r */
- entry[1] = gif_data[1]; /* g */
- entry[2] = gif_data[2]; /* b */
- entry[3] = 0xff; /* a */
-
- gif_data += 3;
+ ret = gif__colour_table_extract(gif,
+ gif->global_colour_table,
+ gif->colour_table_size,
+ &gif_data, true);
+ if (ret != GIF_OK) {
+ return ret;
}
+
gif->buffer_position = (gif_data - gif->gif_data);
} else {
/* Create a default colour table with the first two
* colours as black and white
*/
- unsigned int *entry = gif->global_colour_table;
+ uint32_t *entry = gif->global_colour_table;
entry[0] = 0x00000000;
/* Force Alpha channel to opaque */
- ((unsigned char *) entry)[3] = 0xff;
+ ((uint8_t *) entry)[3] = 0xff;
entry[1] = 0xffffffff;
}
- }
- /* Repeatedly try to initialise frames */
- while ((return_value = gif_initialise_frame(gif)) == GIF_WORKING);
-
- /* If there was a memory error tell the caller */
- if ((return_value == GIF_INSUFFICIENT_MEMORY) ||
- (return_value == GIF_DATA_ERROR)) {
- return return_value;
+ if (gif->global_colours &&
+ gif->bg_index < gif->colour_table_size) {
+ size_t bg_idx = gif->bg_index;
+ gif->bg_colour = gif->global_colour_table[bg_idx];
+ } else {
+ gif->bg_colour = gif->global_colour_table[0];
+ }
}
- /* If we didn't have some frames then a GIF_INSUFFICIENT_DATA becomes a
- * GIF_INSUFFICIENT_FRAME_DATA
- */
- if ((return_value == GIF_INSUFFICIENT_DATA) &&
- (gif->frame_count_partial > 0)) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ if (gif->lzw_ctx == NULL) {
+ lzw_result res = lzw_context_create(
+ (struct lzw_ctx **)&gif->lzw_ctx);
+ if (res != LZW_OK) {
+ return gif_error_from_lzw(res);
+ }
}
- /* Return how many we got */
- return return_value;
-}
+ /* Repeatedly try to initialise frames */
+ do {
+ ret = gif__process_frame(gif, gif->frame_count, false);
+ } while (ret == GIF_WORKING);
+ return ret;
+}
/* exported function documented in libnsgif.h */
gif_result gif_decode_frame(gif_animation *gif, unsigned int frame)
{
- return gif_internal_decode_frame(gif, frame, false);
+ return gif__process_frame(gif, frame, true);
}
-
/* exported function documented in libnsgif.h */
void gif_finalise(gif_animation *gif)
{
diff --git a/src/lzw.c b/src/lzw.c
index 710895e..6f85caa 100644
--- a/src/lzw.c
+++ b/src/lzw.c
@@ -96,7 +96,6 @@ struct lzw_ctx {
uint8_t stack_base[LZW_TABLE_ENTRY_MAX];
};
-
/* Exported function, documented in lzw.h */
lzw_result lzw_context_create(struct lzw_ctx **ctx)
{
@@ -109,14 +108,12 @@ lzw_result lzw_context_create(struct lzw_ctx **ctx)
return LZW_OK;
}
-
/* Exported function, documented in lzw.h */
void lzw_context_destroy(struct lzw_ctx *ctx)
{
free(ctx);
}
-
/**
* Advance the context to the next sub-block in the input data.
*
@@ -153,7 +150,6 @@ static lzw_result lzw__block_advance(struct lzw_read_ctx *restrict ctx)
return LZW_OK;
}
-
/**
* Get the next LZW code of given size from the raw input data.
*
@@ -223,7 +219,6 @@ static inline lzw_result lzw__read_code(
return LZW_OK;
}
-
/**
* Handle clear code.
*
diff --git a/src/lzw.h b/src/lzw.h
index 4549c60..c68753a 100644
--- a/src/lzw.h
+++ b/src/lzw.h
@@ -4,6 +4,7 @@
* http://www.opensource.org/licenses/mit-license.php
*
* Copyright 2017 Michael Drake <michael.drake(a)codethink.co.uk>
+ * Copyright 2021 Michael Drake <tlsa(a)netsurf-browser.org>
*/
#ifndef LZW_H_
@@ -19,11 +20,9 @@
/** Maximum LZW code size in bits */
#define LZW_CODE_MAX 12
-
/* Declare lzw internal context structure */
struct lzw_ctx;
-
/** LZW decoding response codes */
typedef enum lzw_result {
LZW_OK, /**< Success */
@@ -37,7 +36,6 @@ typedef enum lzw_result {
LZW_BAD_CODE, /**< Error: Bad LZW code */
} lzw_result;
-
/**
* Create an LZW decompression context.
*
diff --git a/test/decode_gif.c b/test/decode_gif.c
index 64387ef..52d73b6 100644
--- a/test/decode_gif.c
+++ b/test/decode_gif.c
@@ -113,9 +113,6 @@ static void warning(const char *context, gif_result code)
fprintf(stderr, "%s failed: ", context);
switch (code)
{
- case GIF_INSUFFICIENT_FRAME_DATA:
- fprintf(stderr, "GIF_INSUFFICIENT_FRAME_DATA");
- break;
case GIF_FRAME_DATA_ERROR:
fprintf(stderr, "GIF_FRAME_DATA_ERROR");
break;
--
NetSurf GIF Decoder
1 year, 10 months
libnsgif: branch tlsa/rewrite updated. release/0.2.1-99-g69581f8
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/libnsgif.git/shortlog/69581f88b3dd5be49b4d...
...commit http://git.netsurf-browser.org/libnsgif.git/commit/69581f88b3dd5be49b4de5...
...tree http://git.netsurf-browser.org/libnsgif.git/tree/69581f88b3dd5be49b4de539...
The branch, tlsa/rewrite has been updated
discards c8c36737b57bd5ed7a1872e74efc3226e6031aaf (commit)
via 69581f88b3dd5be49b4de539e3a836006a988235 (commit)
This update added new revisions after undoing existing revisions. That is
to say, the old revision is not a strict subset of the new revision. This
situation occurs when you --force push a change and generate a repository
containing something like this:
* -- * -- B -- O -- O -- O (c8c36737b57bd5ed7a1872e74efc3226e6031aaf)
\
N -- N -- N (69581f88b3dd5be49b4de539e3a836006a988235)
When this happens we assume that you've already had alert emails for all
of the O revisions, and so we here report only the revisions in the N
branch from the common base, B.
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=69581f88b3dd5be49b...
commit 69581f88b3dd5be49b4de539e3a836006a988235
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Optimise deinterlacing of frame rows.
The original interlacing handling looked like this:
```c
static inline uint32_t f1(int height, int y)
{
if ((y << 3) < height) {
return (y << 3); /* Pass 1 */
}
y -= ((height + 7) >> 3);
if ((y << 3) < (height - 4)) {
return (y << 3) + 4; /* Pass 2 */
}
y -= ((height + 3) >> 3);
if ((y << 2) < (height - 2)) {
return (y << 2) + 2; /* Pass 3 */
}
y -= ((height + 1) >> 2);
return (y << 1) + 1; /* Pass 4 */
}
```
Annotated with comments to show the passes. Thought it should be possible
to do the job without all the calculations and subtractions from `y`, and
I also didn't like that it was not super obvious how it aligned with the
spec or how it dealt with pass 4 last, when it is the most common case,
happening for 50% of image rows.
See the following excerpt from from the GIF89a Specification.
> The rows of an Interlaced images are arranged in the following order:
>
> Group 1 : Every 8th. row, starting with row 0. (Pass 1)
> Group 2 : Every 8th. row, starting with row 4. (Pass 2)
> Group 3 : Every 4th. row, starting with row 2. (Pass 3)
> Group 4 : Every 2nd. row, starting with row 1. (Pass 4)
>
> The Following example illustrates how the rows of an interlaced image are
> ordered.
>
> Row Number Interlace Pass
>
> 0 ----------------------------------------- 1
> 1 ----------------------------------------- 4
> 2 ----------------------------------------- 3
> 3 ----------------------------------------- 4
> 4 ----------------------------------------- 2
> 5 ----------------------------------------- 4
> 6 ----------------------------------------- 3
> 7 ----------------------------------------- 4
> 8 ----------------------------------------- 1
> 9 ----------------------------------------- 4
> 10 ----------------------------------------- 3
> 11 ----------------------------------------- 4
> 12 ----------------------------------------- 2
> 13 ----------------------------------------- 4
> 14 ----------------------------------------- 3
> 15 ----------------------------------------- 4
> 16 ----------------------------------------- 1
> 17 ----------------------------------------- 4
> 18 ----------------------------------------- 3
> 19 ----------------------------------------- 4
-- https://www.w3.org/Graphics/GIF/spec-gif89a.txt
To test performance I ran it for all possible GIF heights:
```c
for (unsigned height = 1; height < UINT16_MAX; height++) {
for (uint32_t y = 0; y < height; y++) {
use_val(f1(height, y));
}
}
```
For the original implementation (`f1`), I got the following performance:
| Compiler | Time |
| -------- | ------ |
| Clang | 3.830s |
| GCC | 4.737s |
All tests used GCC 11.2 and Clang 13.0, with `-O2` optimisation.
Next I rewrote it to more closely resemble my reading of the spec.
The compiler can convert the multiplies to shifts and I found this
more readable than `f1`, and more closely matching the spec.
The `height / N * N` bits all use powers of two for N so they boil
down the a simple bitwise AND with an appropriate mask to round down
to the nearest multiple of N.
```c
static inline uint32_t f2(uint32_t height, uint32_t y)
{
height--;
if (y * 8 <= height) {
return y * 8; /* Pass 1 */
}
if (y * 4 <= height) {
return y * 8 - height / 8 * 8 - 4; /* Pass 2 */
}
if (y * 2 <= height) {
return y * 4 - height / 4 * 4 - 2; /* Pass 3 */
}
return y * 2 - height / 2 * 2 - 1; /* Pass 4 */
}
```
For `f2`, I got the following performance:
| Compiler | Time |
| -------- | ------ |
| Clang | 2.758s |
| GCC | 3.909s |
So it's faster than `f1`, and Clang is still doing better than GCC.
After that I reversed the order, so that pass 4 would be handled first.
```c
static inline uint32_t f3(uint32_t height, uint32_t y)
{
height--;
if (y * 2 > height) {
return y * 2 - height / 2 * 2 - 1;
}
if (y * 4 > height) {
return y * 4 - height / 4 * 4 - 2;
}
if (y * 8 > height) {
return y * 8 - height / 8 * 8 - 4;
}
return y * 8;
}
```
For `f3`, I got the following performance:
| Compiler | Time |
| -------- | ------ |
| Clang | 3.329s |
| GCC | 3.630s |
Interestingly this was consistently faster than `f2` with GCC, but
slower than `f2` when built with Clang. (It's still faster than the
original `f1` with both compilers.)
I thought it might be because the branches in `f2` are more
predictable than in `f3`. For example the first branch in
`f2` is only true one eighth of the time, while all the branches
in `f3` are 50:50.
However, running under `perf` showed this was not the case.
Actually the faster `f2` for Clang had more branch misses than
the Clang `f3`.
| Compiler | Implementation | Branches | Branch misses |
| -------- | -------------- | -------------- | ------------- |
| Clang | `f2` | 13,690,378,148 | 656,517 |
| Clang | `f3` | 10,738,688,711 | 275,147 |
| GCC | `f2` | 10,738,659,995 | 336,992 |
| GCC | `f3` | 10,201,842,339 | 1,314,445 |
After this I tried changing the implementation completely. All of
the previous implementations were based on the idea of mapping from
an uninterlaced row number to an interlaced one.
```c
static inline bool f4(uint32_t height, uint32_t *y, uint8_t *pass)
{
*y += *pass == 0 ? 8 : 16 >> *pass;
if (*y < height) return true;
switch (*pass) {
case 0: *pass += 1; *y = 4; if (*y < height) return true;
case 1: *pass += 1; *y = 2; if (*y < height) return true;
case 2: *pass += 1; *y = 1; return true;
}
return false;
}
```
With `f4` we simply step to the next line in interlaced order.
The `16 >> *pass` generates the correct step size for all but the
first pass, so the first pass is special cased.
The switch statement is for handling advancing from one pass to
the next, so it is reached a maximum of four times per frame.
The rest of the time, we return on the `if (*y < height) return true;`
above. The switch falls through to handle the case where the frame
height is less than the start row for the given pass.
For `f4`, I got the following performance:
| Compiler | Time |
| -------- | ------ |
| Clang | 2.315s |
| GCC | 3.479s |
Again, this was tested for all possible GIF heights, but the `for`
loop for rows changes to a do-while loop:
```c
for (unsigned height = 1; height < UINT16_MAX; height++) {
uint8_t pass = 0;
uint32_t y = 0;
do {
use_val(y);
} while (f4(height, &y, &pass));
}
```
This is the fastest yet for both compilers, with Clang still performing
best.
One thing I didn't like about `f4` is that it calculates the y step
size every call, although the step only actually changes three times,
when we change to the next pass.
So for `f5` the switch was updated to set the step size.
```c
static inline bool f5(uint32_t height, uint32_t *y, uint8_t *step)
{
*y += *step & 0xf;
if (*y < height) return true;
switch (*step) {
case 24: *y = 4; *step = 8; if (*y < height) return true;
case 8: *y = 2; *step = 4; if (*y < height) return true;
case 4: *y = 1; *step = 2; return true;
}
return false;
}
```
To avoid the caller having to maintain extra state, the pass number
is no longer used, and we use the step size to keep track of which
pass we are on. However, the step size for the first two passes is
the same, so that led to the strange use of 24 for the initial step
size, and the masking of step to ensure only the bottom four bits are
used.
For `f5`, I got the following performance:
| Compiler | Time |
| -------- | ------ |
| Clang | 2.364s |
| GCC | 2.891s |
This is the fastest yet for both compilers, with Clang still winning
easily.
Note that when the functions are not allowed to inline, `f2` and `f3`
are the fastest.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 7d48b7e..6046769 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -247,21 +247,56 @@ static gif_result gif__recover_frame(
return GIF_OK;
}
-static uint32_t gif_interlaced_line(int height, int y)
+/**
+ * Get the next line for GIF decode.
+ *
+ * Note that the step size must be initialised to 24 at the start of the frame
+ * (when y == 0). This is because of the first two passes of the frame have
+ * the same step size of 8, and the step size is used to determine the current
+ * pass.
+ *
+ * \param[in] height Frame height in pixels.
+ * \param[in,out] y Current row, starting from 0, updated on exit.
+ * \param[in,out] step Current step starting with 24, updated on exit.
+ * \return true if there is a row to process, false at the end of the frame.
+ */
+static inline bool gif__deinterlace(uint32_t height, uint32_t *y, uint8_t *step)
{
- if ((y << 3) < height) {
- return (y << 3);
+ *y += *step & 0xf;
+
+ if (*y < height) return true;
+
+ switch (*step) {
+ case 24: *y = 4; *step = 8; if (*y < height) return true;
+ /* Fall through. */
+ case 8: *y = 2; *step = 4; if (*y < height) return true;
+ /* Fall through. */
+ case 4: *y = 1; *step = 2; if (*y < height) return true;
+ /* Fall through. */
+ default:
+ break;
}
- y -= ((height + 7) >> 3);
- if ((y << 3) < (height - 4)) {
- return (y << 3) + 4;
- }
- y -= ((height + 3) >> 3);
- if ((y << 2) < (height - 2)) {
- return (y << 2) + 2;
+
+ return false;
+}
+
+/**
+ * Get the next line for GIF decode.
+ *
+ * \param[in] interlace Non-zero if the frame is not interlaced.
+ * \param[in] height Frame height in pixels.
+ * \param[in,out] y Current row, starting from 0, updated on exit.
+ * \param[in,out] step Current step starting with 24, updated on exit.
+ * \return true if there is a row to process, false at the end of the frame.
+ */
+static inline bool gif__next_row(uint32_t interlace,
+ uint32_t height, uint32_t *y, uint8_t *step)
+{
+ if (!interlace) {
+ return (++*y != height);
+ } else {
+ return gif__deinterlace(height, y, step);
}
- y -= ((height + 1) >> 2);
- return (y << 1) + 1;
}
static gif_result gif__decode_complex(
@@ -276,9 +311,15 @@ static gif_result gif__decode_complex(
uint32_t *restrict frame_data,
uint32_t *restrict colour_table)
{
- uint32_t available = 0;
- gif_result ret = GIF_OK;
lzw_result res;
+ gif_result ret = GIF_OK;
+ uint32_t available = 0;
+ uint8_t step = 24;
+ uint32_t y = 0;
+
+ if (height == 0) {
+ return GIF_OK;
+ }
/* Initialise the LZW decoding */
res = lzw_decode_init(gif->lzw_ctx, data[0],
@@ -288,17 +329,12 @@ static gif_result gif__decode_complex(
return gif_error_from_lzw(res);
}
- for (uint32_t y = 0; y < height; y++) {
+ do {
uint32_t x;
- uint32_t decode_y;
uint32_t *frame_scanline;
- if (interlace) {
- decode_y = gif_interlaced_line(height, y) + offset_y;
- } else {
- decode_y = y + offset_y;
- }
- frame_scanline = frame_data + offset_x + (decode_y * gif->width);
+ frame_scanline = frame_data + offset_x +
+ (y + offset_y) * gif->width;
x = width;
while (x > 0) {
@@ -338,7 +374,8 @@ static gif_result gif__decode_complex(
}
}
}
- }
+ } while (gif__next_row(interlace, height, &y, &step));
+
return ret;
}
-----------------------------------------------------------------------
Summary of changes:
--
NetSurf GIF Decoder
1 year, 10 months
libnsgif: branch tlsa/rewrite updated. release/0.2.1-99-gc8c3673
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/libnsgif.git/shortlog/c8c36737b57bd5ed7a18...
...commit http://git.netsurf-browser.org/libnsgif.git/commit/c8c36737b57bd5ed7a1872...
...tree http://git.netsurf-browser.org/libnsgif.git/tree/c8c36737b57bd5ed7a1872e7...
The branch, tlsa/rewrite has been updated
via c8c36737b57bd5ed7a1872e74efc3226e6031aaf (commit)
from 1b0c8759f5d96e96ded4cb50bb2772103780e2eb (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=c8c36737b57bd5ed7a...
commit c8c36737b57bd5ed7a1872e74efc3226e6031aaf
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Optimise deinterlacing of frame rows.
The original interlacing handling looked like this:
```c
static inline uint32_t f1(int height, int y)
{
if ((y << 3) < height) {
return (y << 3); /* Pass 1 */
}
y -= ((height + 7) >> 3);
if ((y << 3) < (height - 4)) {
return (y << 3) + 4; /* Pass 2 */
}
y -= ((height + 3) >> 3);
if ((y << 2) < (height - 2)) {
return (y << 2) + 2; /* Pass 3 */
}
y -= ((height + 1) >> 2);
return (y << 1) + 1; /* Pass 4 */
}
```
Annotated with comments to show the passes. Thought it should be possible
to do the job without all the calculations and subtractions from `y`, and
I also didn't like that it was not super obvious how it aligned with the
spec or how it dealt with pass 4 last, when it is the most common case,
happening for 50% of image rows.
See the following excerpt from from the GIF89a Specification.
> The rows of an Interlaced images are arranged in the following order:
>
> Group 1 : Every 8th. row, starting with row 0. (Pass 1)
> Group 2 : Every 8th. row, starting with row 4. (Pass 2)
> Group 3 : Every 4th. row, starting with row 2. (Pass 3)
> Group 4 : Every 2nd. row, starting with row 1. (Pass 4)
>
> The Following example illustrates how the rows of an interlaced image are
> ordered.
>
> Row Number Interlace Pass
>
> 0 ----------------------------------------- 1
> 1 ----------------------------------------- 4
> 2 ----------------------------------------- 3
> 3 ----------------------------------------- 4
> 4 ----------------------------------------- 2
> 5 ----------------------------------------- 4
> 6 ----------------------------------------- 3
> 7 ----------------------------------------- 4
> 8 ----------------------------------------- 1
> 9 ----------------------------------------- 4
> 10 ----------------------------------------- 3
> 11 ----------------------------------------- 4
> 12 ----------------------------------------- 2
> 13 ----------------------------------------- 4
> 14 ----------------------------------------- 3
> 15 ----------------------------------------- 4
> 16 ----------------------------------------- 1
> 17 ----------------------------------------- 4
> 18 ----------------------------------------- 3
> 19 ----------------------------------------- 4
-- https://www.w3.org/Graphics/GIF/spec-gif89a.txt
To test performance I ran it for all possible GIF heights:
```c
for (unsigned height = 1; height < UINT16_MAX; height++) {
for (uint32_t y = 0; y < height; y++) {
use_val(f1(height, y));
}
}
```
For the original implementation (`f1`), I got the following performance:
| Compiler | Time |
| -------- | ------ |
| Clang | 3.830s |
| GCC | 4.737s |
All tests used GCC 11.2 and Clang 13.0, with `-O2` optimisation.
Next I rewrote it to more closely resemble my reading of the spec.
The compiler can convert the multiplies to shifts and I found this
more readable than `f1`, and more closely matching the spec.
The `height / N * N` bits all use powers of two for N so they boil
down the a simple bitwise AND with an appropriate mask to round down
to the nearest multiple of N.
```c
static inline uint32_t f2(uint32_t height, uint32_t y)
{
height--;
if (y * 8 <= height) {
return y * 8; /* Pass 1 */
}
if (y * 4 <= height) {
return y * 8 - height / 8 * 8 - 4; /* Pass 2 */
}
if (y * 2 <= height) {
return y * 4 - height / 4 * 4 - 2; /* Pass 3 */
}
return y * 2 - height / 2 * 2 - 1; /* Pass 4 */
}
```
For `f2`, I got the following performance:
| Compiler | Time |
| -------- | ------ |
| Clang | 2.758s |
| GCC | 3.909s |
So it's faster than `f1`, and Clang is still doing better than GCC.
After that I reversed the order, so that pass 4 would be handled first.
```c
static inline uint32_t f3(uint32_t height, uint32_t y)
{
height--;
if (y * 2 > height) {
return y * 2 - height / 2 * 2 - 1;
}
if (y * 4 > height) {
return y * 4 - height / 4 * 4 - 2;
}
if (y * 8 > height) {
return y * 8 - height / 8 * 8 - 4;
}
return y * 8;
}
```
For `f3`, I got the following performance:
| Compiler | Time |
| -------- | ------ |
| Clang | 3.329s |
| GCC | 3.630s |
Interestingly this was consistently faster than `f2` with GCC, but
slower than `f2` when built with Clang. (It's still faster than the
original `f1` with both compilers.)
I thought it might be because the branches in `f2` are more
predictable than in `f3`. For example the first branch in
`f2` is only true one eighth of the time, while all the branches
in `f3` are 50:50.
However, running under `perf` showed this was not the case.
Actually the faster `f2` for Clang had more branch misses than
the Clang `f3`.
| Compiler | Implementation | Branches | Branch misses |
| -------- | -------------- | -------------- | ------------- |
| Clang | `f2` | 13,690,378,148 | 656,517 |
| Clang | `f3` | 10,738,688,711 | 275,147 |
| GCC | `f2` | 10,738,659,995 | 336,992 |
| GCC | `f3` | 10,201,842,339 | 1,314,445 |
After this I tried changing the implementation completely. All of
the previous implementations were based on the idea of mapping from
an uninterlaced row number to an interlaced one.
```c
static inline bool f4(uint32_t height, uint32_t *y, uint8_t *pass)
{
*y += *pass == 0 ? 8 : 16 >> *pass;
if (*y < height) return true;
switch (*pass) {
case 0: *pass += 1; *y = 4; if (*y < height) return true;
case 1: *pass += 1; *y = 2; if (*y < height) return true;
case 2: *pass += 1; *y = 1; return true;
}
return false;
}
```
With `f4` we simply step to the next line in interlaced order.
The `16 >> *pass` generates the correct step size for all but the
first pass, so the first pass is special cased.
The switch statement if for handling advancing from one pass to
the next, so it is reached a maximum of four times per frame.
The rest of the time, we return on the `if (*y < height) return true;`
above. The switch falls through to handle the case where the frame
height is less than the start row for the given pass.
For `f4`, I got the following performance:
| Compiler | Time |
| -------- | ------ |
| Clang | 2.315s |
| GCC | 3.479s |
Again, this was tested for all possible GIF heights, but the `for`
loop for rows changes to a do-while loop:
```c
for (unsigned height = 1; height < UINT16_MAX; height++) {
uint8_t pass = 0;
uint32_t y = 0;
do {
use_val(y);
} while (f4(height, &y, &pass));
}
```
This is the fastest yet for both compilers, with Clang still performing
best.
One thing I didn't like about `f4` is that it calculates the y step
size every call, although the step only actually changes three times,
when we change to the next pass.
So for `f5` the switch was updated to set the step size.
```c
static inline bool f5(uint32_t height, uint32_t *y, uint8_t *step)
{
*y += *step & 0xf;
if (*y < height) return true;
switch (*step) {
case 24: *y = 4; *step = 8; if (*y < height) return true;
case 8: *y = 2; *step = 4; if (*y < height) return true;
case 4: *y = 1; *step = 2; return true;
}
return false;
}
```
To avoid the caller having to maintain extra state, the pass number
is no longer used, and we use the step size to keep track of which
pass we are on. However, the step size for the first two passes is
the same, so that led to the strange use of 24 for the initial step
size, and the masking of step to ensure only the bottom four bits are
used.
For `f5`, I got the following performance:
| Compiler | Time |
| -------- | ------ |
| Clang | 2.364s |
| GCC | 2.891s |
This is the fastest yet for both compilers, with Clang still winning
easily.
Note that when the functions are not allowed to inline, `f2` and `f3`
are the fastest.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 7d48b7e..6046769 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -247,21 +247,56 @@ static gif_result gif__recover_frame(
return GIF_OK;
}
-static uint32_t gif_interlaced_line(int height, int y)
+/**
+ * Get the next line for GIF decode.
+ *
+ * Note that the step size must be initialised to 24 at the start of the frame
+ * (when y == 0). This is because of the first two passes of the frame have
+ * the same step size of 8, and the step size is used to determine the current
+ * pass.
+ *
+ * \param[in] height Frame height in pixels.
+ * \param[in,out] y Current row, starting from 0, updated on exit.
+ * \param[in,out] step Current step starting with 24, updated on exit.
+ * \return true if there is a row to process, false at the end of the frame.
+ */
+static inline bool gif__deinterlace(uint32_t height, uint32_t *y, uint8_t *step)
{
- if ((y << 3) < height) {
- return (y << 3);
+ *y += *step & 0xf;
+
+ if (*y < height) return true;
+
+ switch (*step) {
+ case 24: *y = 4; *step = 8; if (*y < height) return true;
+ /* Fall through. */
+ case 8: *y = 2; *step = 4; if (*y < height) return true;
+ /* Fall through. */
+ case 4: *y = 1; *step = 2; if (*y < height) return true;
+ /* Fall through. */
+ default:
+ break;
}
- y -= ((height + 7) >> 3);
- if ((y << 3) < (height - 4)) {
- return (y << 3) + 4;
- }
- y -= ((height + 3) >> 3);
- if ((y << 2) < (height - 2)) {
- return (y << 2) + 2;
+
+ return false;
+}
+
+/**
+ * Get the next line for GIF decode.
+ *
+ * \param[in] interlace Non-zero if the frame is not interlaced.
+ * \param[in] height Frame height in pixels.
+ * \param[in,out] y Current row, starting from 0, updated on exit.
+ * \param[in,out] step Current step starting with 24, updated on exit.
+ * \return true if there is a row to process, false at the end of the frame.
+ */
+static inline bool gif__next_row(uint32_t interlace,
+ uint32_t height, uint32_t *y, uint8_t *step)
+{
+ if (!interlace) {
+ return (++*y != height);
+ } else {
+ return gif__deinterlace(height, y, step);
}
- y -= ((height + 1) >> 2);
- return (y << 1) + 1;
}
static gif_result gif__decode_complex(
@@ -276,9 +311,15 @@ static gif_result gif__decode_complex(
uint32_t *restrict frame_data,
uint32_t *restrict colour_table)
{
- uint32_t available = 0;
- gif_result ret = GIF_OK;
lzw_result res;
+ gif_result ret = GIF_OK;
+ uint32_t available = 0;
+ uint8_t step = 24;
+ uint32_t y = 0;
+
+ if (height == 0) {
+ return GIF_OK;
+ }
/* Initialise the LZW decoding */
res = lzw_decode_init(gif->lzw_ctx, data[0],
@@ -288,17 +329,12 @@ static gif_result gif__decode_complex(
return gif_error_from_lzw(res);
}
- for (uint32_t y = 0; y < height; y++) {
+ do {
uint32_t x;
- uint32_t decode_y;
uint32_t *frame_scanline;
- if (interlace) {
- decode_y = gif_interlaced_line(height, y) + offset_y;
- } else {
- decode_y = y + offset_y;
- }
- frame_scanline = frame_data + offset_x + (decode_y * gif->width);
+ frame_scanline = frame_data + offset_x +
+ (y + offset_y) * gif->width;
x = width;
while (x > 0) {
@@ -338,7 +374,8 @@ static gif_result gif__decode_complex(
}
}
}
- }
+ } while (gif__next_row(interlace, height, &y, &step));
+
return ret;
}
-----------------------------------------------------------------------
Summary of changes:
src/libnsgif.c | 83 ++++++++++++++++++++++++++++++++++++++++----------------
1 file changed, 60 insertions(+), 23 deletions(-)
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 7d48b7e..6046769 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -247,21 +247,56 @@ static gif_result gif__recover_frame(
return GIF_OK;
}
-static uint32_t gif_interlaced_line(int height, int y)
+/**
+ * Get the next line for GIF decode.
+ *
+ * Note that the step size must be initialised to 24 at the start of the frame
+ * (when y == 0). This is because of the first two passes of the frame have
+ * the same step size of 8, and the step size is used to determine the current
+ * pass.
+ *
+ * \param[in] height Frame height in pixels.
+ * \param[in,out] y Current row, starting from 0, updated on exit.
+ * \param[in,out] step Current step starting with 24, updated on exit.
+ * \return true if there is a row to process, false at the end of the frame.
+ */
+static inline bool gif__deinterlace(uint32_t height, uint32_t *y, uint8_t *step)
{
- if ((y << 3) < height) {
- return (y << 3);
+ *y += *step & 0xf;
+
+ if (*y < height) return true;
+
+ switch (*step) {
+ case 24: *y = 4; *step = 8; if (*y < height) return true;
+ /* Fall through. */
+ case 8: *y = 2; *step = 4; if (*y < height) return true;
+ /* Fall through. */
+ case 4: *y = 1; *step = 2; if (*y < height) return true;
+ /* Fall through. */
+ default:
+ break;
}
- y -= ((height + 7) >> 3);
- if ((y << 3) < (height - 4)) {
- return (y << 3) + 4;
- }
- y -= ((height + 3) >> 3);
- if ((y << 2) < (height - 2)) {
- return (y << 2) + 2;
+
+ return false;
+}
+
+/**
+ * Get the next line for GIF decode.
+ *
+ * \param[in] interlace Non-zero if the frame is not interlaced.
+ * \param[in] height Frame height in pixels.
+ * \param[in,out] y Current row, starting from 0, updated on exit.
+ * \param[in,out] step Current step starting with 24, updated on exit.
+ * \return true if there is a row to process, false at the end of the frame.
+ */
+static inline bool gif__next_row(uint32_t interlace,
+ uint32_t height, uint32_t *y, uint8_t *step)
+{
+ if (!interlace) {
+ return (++*y != height);
+ } else {
+ return gif__deinterlace(height, y, step);
}
- y -= ((height + 1) >> 2);
- return (y << 1) + 1;
}
static gif_result gif__decode_complex(
@@ -276,9 +311,15 @@ static gif_result gif__decode_complex(
uint32_t *restrict frame_data,
uint32_t *restrict colour_table)
{
- uint32_t available = 0;
- gif_result ret = GIF_OK;
lzw_result res;
+ gif_result ret = GIF_OK;
+ uint32_t available = 0;
+ uint8_t step = 24;
+ uint32_t y = 0;
+
+ if (height == 0) {
+ return GIF_OK;
+ }
/* Initialise the LZW decoding */
res = lzw_decode_init(gif->lzw_ctx, data[0],
@@ -288,17 +329,12 @@ static gif_result gif__decode_complex(
return gif_error_from_lzw(res);
}
- for (uint32_t y = 0; y < height; y++) {
+ do {
uint32_t x;
- uint32_t decode_y;
uint32_t *frame_scanline;
- if (interlace) {
- decode_y = gif_interlaced_line(height, y) + offset_y;
- } else {
- decode_y = y + offset_y;
- }
- frame_scanline = frame_data + offset_x + (decode_y * gif->width);
+ frame_scanline = frame_data + offset_x +
+ (y + offset_y) * gif->width;
x = width;
while (x > 0) {
@@ -338,7 +374,8 @@ static gif_result gif__decode_complex(
}
}
}
- }
+ } while (gif__next_row(interlace, height, &y, &step));
+
return ret;
}
--
NetSurf GIF Decoder
1 year, 10 months
libnsgif: branch tlsa/rewrite updated. release/0.2.1-98-g1b0c875
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/libnsgif.git/shortlog/1b0c8759f5d96e96ded4...
...commit http://git.netsurf-browser.org/libnsgif.git/commit/1b0c8759f5d96e96ded4cb...
...tree http://git.netsurf-browser.org/libnsgif.git/tree/1b0c8759f5d96e96ded4cb50...
The branch, tlsa/rewrite has been updated
via 1b0c8759f5d96e96ded4cb50bb2772103780e2eb (commit)
via d1762afcee8b6541a974eeb37edea2a828096b8a (commit)
from 947586eb1b14c267ee996ba655af13899488c0f3 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=1b0c8759f5d96e96de...
commit 1b0c8759f5d96e96ded4cb50bb2772103780e2eb
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
API: Fix typos in public header.
diff --git a/include/libnsgif.h b/include/libnsgif.h
index adbacbe..3087a4d 100644
--- a/include/libnsgif.h
+++ b/include/libnsgif.h
@@ -46,11 +46,11 @@ typedef struct gif_frame {
bool virgin;
/** whether the frame is totally opaque */
bool opaque;
- /** whether a forcable screen redraw is required */
+ /** whether a full image redraw is required */
bool redraw_required;
/** how the previous frame should be disposed; affects plotting */
unsigned char disposal_method;
- /** whether we acknoledge transparency */
+ /** whether we acknowledge transparency */
bool transparency;
/** the index designating a transparent pixel */
unsigned int transparency_index;
@@ -89,7 +89,7 @@ typedef struct gif_bitmap_callback_vt {
gif_bitmap_cb_set_opaque bitmap_set_opaque;
/** Tests whether a bitmap has an opaque alpha channel. */
gif_bitmap_cb_test_opaque bitmap_test_opaque;
- /** The bitmap image has changed, so flush any persistant cache. */
+ /** The bitmap image has changed, so flush any persistent cache. */
gif_bitmap_cb_modified bitmap_modified;
} gif_bitmap_callback_vt;
@@ -103,7 +103,7 @@ typedef struct gif_animation {
const uint8_t *gif_data;
/** width of GIF (may increase during decoding) */
unsigned int width;
- /** heigth of GIF (may increase during decoding) */
+ /** height of GIF (may increase during decoding) */
unsigned int height;
/** number of frames decoded */
unsigned int frame_count;
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=d1762afcee8b6541a9...
commit d1762afcee8b6541a974eeb37edea2a828096b8a
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
API: Clean up public header indentation.
diff --git a/include/libnsgif.h b/include/libnsgif.h
index 36f59a4..adbacbe 100644
--- a/include/libnsgif.h
+++ b/include/libnsgif.h
@@ -20,50 +20,50 @@
/* Error return values */
typedef enum {
- GIF_WORKING = 1,
- GIF_OK = 0,
- GIF_INSUFFICIENT_FRAME_DATA = -1,
- GIF_FRAME_DATA_ERROR = -2,
- GIF_INSUFFICIENT_DATA = -3,
- GIF_DATA_ERROR = -4,
- GIF_INSUFFICIENT_MEMORY = -5,
- GIF_FRAME_NO_DISPLAY = -6,
- GIF_END_OF_FRAME = -7
+ GIF_WORKING = 1,
+ GIF_OK = 0,
+ GIF_INSUFFICIENT_FRAME_DATA = -1,
+ GIF_FRAME_DATA_ERROR = -2,
+ GIF_INSUFFICIENT_DATA = -3,
+ GIF_DATA_ERROR = -4,
+ GIF_INSUFFICIENT_MEMORY = -5,
+ GIF_FRAME_NO_DISPLAY = -6,
+ GIF_END_OF_FRAME = -7
} gif_result;
/** GIF frame data */
typedef struct gif_frame {
- /** whether the frame should be displayed/animated */
- bool display;
- /** delay (in cs) before animating the frame */
- unsigned int frame_delay;
-
- /* Internal members are listed below */
-
- /** offset (in bytes) to the GIF frame data */
- unsigned int frame_pointer;
- /** whether the frame has previously been used */
- bool virgin;
- /** whether the frame is totally opaque */
- bool opaque;
- /** whether a forcable screen redraw is required */
- bool redraw_required;
- /** how the previous frame should be disposed; affects plotting */
- unsigned char disposal_method;
- /** whether we acknoledge transparency */
- bool transparency;
- /** the index designating a transparent pixel */
- unsigned int transparency_index;
- /** x co-ordinate of redraw rectangle */
- unsigned int redraw_x;
- /** y co-ordinate of redraw rectangle */
- unsigned int redraw_y;
- /** width of redraw rectangle */
- unsigned int redraw_width;
- /** height of redraw rectangle */
- unsigned int redraw_height;
- /* Frame flags */
- unsigned int flags;
+ /** whether the frame should be displayed/animated */
+ bool display;
+ /** delay (in cs) before animating the frame */
+ unsigned int frame_delay;
+
+ /* Internal members are listed below */
+
+ /** offset (in bytes) to the GIF frame data */
+ unsigned int frame_pointer;
+ /** whether the frame has previously been used */
+ bool virgin;
+ /** whether the frame is totally opaque */
+ bool opaque;
+ /** whether a forcable screen redraw is required */
+ bool redraw_required;
+ /** how the previous frame should be disposed; affects plotting */
+ unsigned char disposal_method;
+ /** whether we acknoledge transparency */
+ bool transparency;
+ /** the index designating a transparent pixel */
+ unsigned int transparency_index;
+ /** x co-ordinate of redraw rectangle */
+ unsigned int redraw_x;
+ /** y co-ordinate of redraw rectangle */
+ unsigned int redraw_y;
+ /** width of redraw rectangle */
+ unsigned int redraw_width;
+ /** height of redraw rectangle */
+ unsigned int redraw_height;
+ /* Frame flags */
+ unsigned int flags;
} gif_frame;
/* API for Bitmap callbacks */
@@ -76,81 +76,81 @@ typedef void (*gif_bitmap_cb_modified)(void *bitmap);
/** Bitmap callbacks function table */
typedef struct gif_bitmap_callback_vt {
- /** Create a bitmap. */
- gif_bitmap_cb_create bitmap_create;
- /** Free a bitmap. */
- gif_bitmap_cb_destroy bitmap_destroy;
- /** Return a pointer to the pixel data in a bitmap. */
- gif_bitmap_cb_get_buffer bitmap_get_buffer;
-
- /* Members below are optional */
-
- /** Sets whether a bitmap should be plotted opaque. */
- gif_bitmap_cb_set_opaque bitmap_set_opaque;
- /** Tests whether a bitmap has an opaque alpha channel. */
- gif_bitmap_cb_test_opaque bitmap_test_opaque;
- /** The bitmap image has changed, so flush any persistant cache. */
- gif_bitmap_cb_modified bitmap_modified;
+ /** Create a bitmap. */
+ gif_bitmap_cb_create bitmap_create;
+ /** Free a bitmap. */
+ gif_bitmap_cb_destroy bitmap_destroy;
+ /** Return a pointer to the pixel data in a bitmap. */
+ gif_bitmap_cb_get_buffer bitmap_get_buffer;
+
+ /* Members below are optional */
+
+ /** Sets whether a bitmap should be plotted opaque. */
+ gif_bitmap_cb_set_opaque bitmap_set_opaque;
+ /** Tests whether a bitmap has an opaque alpha channel. */
+ gif_bitmap_cb_test_opaque bitmap_test_opaque;
+ /** The bitmap image has changed, so flush any persistant cache. */
+ gif_bitmap_cb_modified bitmap_modified;
} gif_bitmap_callback_vt;
/** GIF animation data */
typedef struct gif_animation {
- /** LZW decode context */
- void *lzw_ctx;
- /** callbacks for bitmap functions */
- gif_bitmap_callback_vt bitmap_callbacks;
- /** pointer to GIF data */
- const uint8_t *gif_data;
- /** width of GIF (may increase during decoding) */
- unsigned int width;
- /** heigth of GIF (may increase during decoding) */
- unsigned int height;
- /** number of frames decoded */
- unsigned int frame_count;
- /** number of frames partially decoded */
- unsigned int frame_count_partial;
- /** decoded frames */
- gif_frame *frames;
- /** current frame decoded to bitmap */
- int decoded_frame;
- /** currently decoded image; stored as bitmap from bitmap_create callback */
- void *frame_image;
- /** number of times to loop animation */
- int loop_count;
-
- /* Internal members are listed below */
-
- /** current index into GIF data */
- unsigned int buffer_position;
- /** total number of bytes of GIF data available */
- unsigned int buffer_size;
- /** current number of frame holders */
- unsigned int frame_holders;
- /** background index */
- unsigned int bg_index;
- /** background colour */
- unsigned int bg_colour;
- /** image aspect ratio (ignored) */
- unsigned int aspect_ratio;
- /** size of colour table (in entries) */
- unsigned int colour_table_size;
- /** whether the GIF has a global colour table */
- bool global_colours;
- /** global colour table */
- unsigned int *global_colour_table;
- /** local colour table */
- unsigned int *local_colour_table;
- /** current colour table */
- unsigned int *colour_table;
-
- /** previous frame for GIF_FRAME_RESTORE */
- void *prev_frame;
- /** previous frame index */
- int prev_index;
- /** previous frame width */
- unsigned prev_width;
- /** previous frame height */
- unsigned prev_height;
+ /** LZW decode context */
+ void *lzw_ctx;
+ /** callbacks for bitmap functions */
+ gif_bitmap_callback_vt bitmap_callbacks;
+ /** pointer to GIF data */
+ const uint8_t *gif_data;
+ /** width of GIF (may increase during decoding) */
+ unsigned int width;
+ /** heigth of GIF (may increase during decoding) */
+ unsigned int height;
+ /** number of frames decoded */
+ unsigned int frame_count;
+ /** number of frames partially decoded */
+ unsigned int frame_count_partial;
+ /** decoded frames */
+ gif_frame *frames;
+ /** current frame decoded to bitmap */
+ int decoded_frame;
+ /** currently decoded image; stored as bitmap from bitmap_create callback */
+ void *frame_image;
+ /** number of times to loop animation */
+ int loop_count;
+
+ /* Internal members are listed below */
+
+ /** current index into GIF data */
+ unsigned int buffer_position;
+ /** total number of bytes of GIF data available */
+ unsigned int buffer_size;
+ /** current number of frame holders */
+ unsigned int frame_holders;
+ /** background index */
+ unsigned int bg_index;
+ /** background colour */
+ unsigned int bg_colour;
+ /** image aspect ratio (ignored) */
+ unsigned int aspect_ratio;
+ /** size of colour table (in entries) */
+ unsigned int colour_table_size;
+ /** whether the GIF has a global colour table */
+ bool global_colours;
+ /** global colour table */
+ unsigned int *global_colour_table;
+ /** local colour table */
+ unsigned int *local_colour_table;
+ /** current colour table */
+ unsigned int *colour_table;
+
+ /** previous frame for GIF_FRAME_RESTORE */
+ void *prev_frame;
+ /** previous frame index */
+ int prev_index;
+ /** previous frame width */
+ unsigned prev_width;
+ /** previous frame height */
+ unsigned prev_height;
} gif_animation;
/**
@@ -163,7 +163,7 @@ void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks);
* any information that hasn't already been decoded.
* If an error occurs, all previously decoded frames are retained.
*
- * @return Error return value.
+ * \return Error return value.
* - GIF_FRAME_DATA_ERROR for GIF frame data error
* - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to process
* any more frames
@@ -178,9 +178,7 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data);
/**
* Decodes a GIF frame.
*
- * @return Error return value. If a frame does not contain any image data,
- * GIF_OK is returned and gif->current_error is set to
- * GIF_FRAME_NO_DISPLAY
+ * \return Error return value.
* - GIF_FRAME_DATA_ERROR for GIF frame data error
* - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
* - GIF_DATA_ERROR for GIF error (invalid frame header)
-----------------------------------------------------------------------
Summary of changes:
include/libnsgif.h | 228 ++++++++++++++++++++++++++--------------------------
1 file changed, 113 insertions(+), 115 deletions(-)
diff --git a/include/libnsgif.h b/include/libnsgif.h
index 36f59a4..3087a4d 100644
--- a/include/libnsgif.h
+++ b/include/libnsgif.h
@@ -20,50 +20,50 @@
/* Error return values */
typedef enum {
- GIF_WORKING = 1,
- GIF_OK = 0,
- GIF_INSUFFICIENT_FRAME_DATA = -1,
- GIF_FRAME_DATA_ERROR = -2,
- GIF_INSUFFICIENT_DATA = -3,
- GIF_DATA_ERROR = -4,
- GIF_INSUFFICIENT_MEMORY = -5,
- GIF_FRAME_NO_DISPLAY = -6,
- GIF_END_OF_FRAME = -7
+ GIF_WORKING = 1,
+ GIF_OK = 0,
+ GIF_INSUFFICIENT_FRAME_DATA = -1,
+ GIF_FRAME_DATA_ERROR = -2,
+ GIF_INSUFFICIENT_DATA = -3,
+ GIF_DATA_ERROR = -4,
+ GIF_INSUFFICIENT_MEMORY = -5,
+ GIF_FRAME_NO_DISPLAY = -6,
+ GIF_END_OF_FRAME = -7
} gif_result;
/** GIF frame data */
typedef struct gif_frame {
- /** whether the frame should be displayed/animated */
- bool display;
- /** delay (in cs) before animating the frame */
- unsigned int frame_delay;
-
- /* Internal members are listed below */
-
- /** offset (in bytes) to the GIF frame data */
- unsigned int frame_pointer;
- /** whether the frame has previously been used */
- bool virgin;
- /** whether the frame is totally opaque */
- bool opaque;
- /** whether a forcable screen redraw is required */
- bool redraw_required;
- /** how the previous frame should be disposed; affects plotting */
- unsigned char disposal_method;
- /** whether we acknoledge transparency */
- bool transparency;
- /** the index designating a transparent pixel */
- unsigned int transparency_index;
- /** x co-ordinate of redraw rectangle */
- unsigned int redraw_x;
- /** y co-ordinate of redraw rectangle */
- unsigned int redraw_y;
- /** width of redraw rectangle */
- unsigned int redraw_width;
- /** height of redraw rectangle */
- unsigned int redraw_height;
- /* Frame flags */
- unsigned int flags;
+ /** whether the frame should be displayed/animated */
+ bool display;
+ /** delay (in cs) before animating the frame */
+ unsigned int frame_delay;
+
+ /* Internal members are listed below */
+
+ /** offset (in bytes) to the GIF frame data */
+ unsigned int frame_pointer;
+ /** whether the frame has previously been used */
+ bool virgin;
+ /** whether the frame is totally opaque */
+ bool opaque;
+ /** whether a full image redraw is required */
+ bool redraw_required;
+ /** how the previous frame should be disposed; affects plotting */
+ unsigned char disposal_method;
+ /** whether we acknowledge transparency */
+ bool transparency;
+ /** the index designating a transparent pixel */
+ unsigned int transparency_index;
+ /** x co-ordinate of redraw rectangle */
+ unsigned int redraw_x;
+ /** y co-ordinate of redraw rectangle */
+ unsigned int redraw_y;
+ /** width of redraw rectangle */
+ unsigned int redraw_width;
+ /** height of redraw rectangle */
+ unsigned int redraw_height;
+ /* Frame flags */
+ unsigned int flags;
} gif_frame;
/* API for Bitmap callbacks */
@@ -76,81 +76,81 @@ typedef void (*gif_bitmap_cb_modified)(void *bitmap);
/** Bitmap callbacks function table */
typedef struct gif_bitmap_callback_vt {
- /** Create a bitmap. */
- gif_bitmap_cb_create bitmap_create;
- /** Free a bitmap. */
- gif_bitmap_cb_destroy bitmap_destroy;
- /** Return a pointer to the pixel data in a bitmap. */
- gif_bitmap_cb_get_buffer bitmap_get_buffer;
-
- /* Members below are optional */
-
- /** Sets whether a bitmap should be plotted opaque. */
- gif_bitmap_cb_set_opaque bitmap_set_opaque;
- /** Tests whether a bitmap has an opaque alpha channel. */
- gif_bitmap_cb_test_opaque bitmap_test_opaque;
- /** The bitmap image has changed, so flush any persistant cache. */
- gif_bitmap_cb_modified bitmap_modified;
+ /** Create a bitmap. */
+ gif_bitmap_cb_create bitmap_create;
+ /** Free a bitmap. */
+ gif_bitmap_cb_destroy bitmap_destroy;
+ /** Return a pointer to the pixel data in a bitmap. */
+ gif_bitmap_cb_get_buffer bitmap_get_buffer;
+
+ /* Members below are optional */
+
+ /** Sets whether a bitmap should be plotted opaque. */
+ gif_bitmap_cb_set_opaque bitmap_set_opaque;
+ /** Tests whether a bitmap has an opaque alpha channel. */
+ gif_bitmap_cb_test_opaque bitmap_test_opaque;
+ /** The bitmap image has changed, so flush any persistent cache. */
+ gif_bitmap_cb_modified bitmap_modified;
} gif_bitmap_callback_vt;
/** GIF animation data */
typedef struct gif_animation {
- /** LZW decode context */
- void *lzw_ctx;
- /** callbacks for bitmap functions */
- gif_bitmap_callback_vt bitmap_callbacks;
- /** pointer to GIF data */
- const uint8_t *gif_data;
- /** width of GIF (may increase during decoding) */
- unsigned int width;
- /** heigth of GIF (may increase during decoding) */
- unsigned int height;
- /** number of frames decoded */
- unsigned int frame_count;
- /** number of frames partially decoded */
- unsigned int frame_count_partial;
- /** decoded frames */
- gif_frame *frames;
- /** current frame decoded to bitmap */
- int decoded_frame;
- /** currently decoded image; stored as bitmap from bitmap_create callback */
- void *frame_image;
- /** number of times to loop animation */
- int loop_count;
-
- /* Internal members are listed below */
-
- /** current index into GIF data */
- unsigned int buffer_position;
- /** total number of bytes of GIF data available */
- unsigned int buffer_size;
- /** current number of frame holders */
- unsigned int frame_holders;
- /** background index */
- unsigned int bg_index;
- /** background colour */
- unsigned int bg_colour;
- /** image aspect ratio (ignored) */
- unsigned int aspect_ratio;
- /** size of colour table (in entries) */
- unsigned int colour_table_size;
- /** whether the GIF has a global colour table */
- bool global_colours;
- /** global colour table */
- unsigned int *global_colour_table;
- /** local colour table */
- unsigned int *local_colour_table;
- /** current colour table */
- unsigned int *colour_table;
-
- /** previous frame for GIF_FRAME_RESTORE */
- void *prev_frame;
- /** previous frame index */
- int prev_index;
- /** previous frame width */
- unsigned prev_width;
- /** previous frame height */
- unsigned prev_height;
+ /** LZW decode context */
+ void *lzw_ctx;
+ /** callbacks for bitmap functions */
+ gif_bitmap_callback_vt bitmap_callbacks;
+ /** pointer to GIF data */
+ const uint8_t *gif_data;
+ /** width of GIF (may increase during decoding) */
+ unsigned int width;
+ /** height of GIF (may increase during decoding) */
+ unsigned int height;
+ /** number of frames decoded */
+ unsigned int frame_count;
+ /** number of frames partially decoded */
+ unsigned int frame_count_partial;
+ /** decoded frames */
+ gif_frame *frames;
+ /** current frame decoded to bitmap */
+ int decoded_frame;
+ /** currently decoded image; stored as bitmap from bitmap_create callback */
+ void *frame_image;
+ /** number of times to loop animation */
+ int loop_count;
+
+ /* Internal members are listed below */
+
+ /** current index into GIF data */
+ unsigned int buffer_position;
+ /** total number of bytes of GIF data available */
+ unsigned int buffer_size;
+ /** current number of frame holders */
+ unsigned int frame_holders;
+ /** background index */
+ unsigned int bg_index;
+ /** background colour */
+ unsigned int bg_colour;
+ /** image aspect ratio (ignored) */
+ unsigned int aspect_ratio;
+ /** size of colour table (in entries) */
+ unsigned int colour_table_size;
+ /** whether the GIF has a global colour table */
+ bool global_colours;
+ /** global colour table */
+ unsigned int *global_colour_table;
+ /** local colour table */
+ unsigned int *local_colour_table;
+ /** current colour table */
+ unsigned int *colour_table;
+
+ /** previous frame for GIF_FRAME_RESTORE */
+ void *prev_frame;
+ /** previous frame index */
+ int prev_index;
+ /** previous frame width */
+ unsigned prev_width;
+ /** previous frame height */
+ unsigned prev_height;
} gif_animation;
/**
@@ -163,7 +163,7 @@ void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks);
* any information that hasn't already been decoded.
* If an error occurs, all previously decoded frames are retained.
*
- * @return Error return value.
+ * \return Error return value.
* - GIF_FRAME_DATA_ERROR for GIF frame data error
* - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to process
* any more frames
@@ -178,9 +178,7 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data);
/**
* Decodes a GIF frame.
*
- * @return Error return value. If a frame does not contain any image data,
- * GIF_OK is returned and gif->current_error is set to
- * GIF_FRAME_NO_DISPLAY
+ * \return Error return value.
* - GIF_FRAME_DATA_ERROR for GIF frame data error
* - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
* - GIF_DATA_ERROR for GIF error (invalid frame header)
--
NetSurf GIF Decoder
1 year, 10 months
libnsgif: branch tlsa/rewrite updated. release/0.2.1-96-g947586e
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/libnsgif.git/shortlog/947586eb1b14c267ee99...
...commit http://git.netsurf-browser.org/libnsgif.git/commit/947586eb1b14c267ee996b...
...tree http://git.netsurf-browser.org/libnsgif.git/tree/947586eb1b14c267ee996ba6...
The branch, tlsa/rewrite has been updated
discards eebf25066c18fbf04b8324030f1989870bb0f38e (commit)
via 947586eb1b14c267ee996ba655af13899488c0f3 (commit)
This update added new revisions after undoing existing revisions. That is
to say, the old revision is not a strict subset of the new revision. This
situation occurs when you --force push a change and generate a repository
containing something like this:
* -- * -- B -- O -- O -- O (eebf25066c18fbf04b8324030f1989870bb0f38e)
\
N -- N -- N (947586eb1b14c267ee996ba655af13899488c0f3)
When this happens we assume that you've already had alert emails for all
of the O revisions, and so we here report only the revisions in the N
branch from the common base, B.
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=947586eb1b14c267ee...
commit 947586eb1b14c267ee996ba655af13899488c0f3
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Constify raw source data.
diff --git a/include/libnsgif.h b/include/libnsgif.h
index ae6691c..36f59a4 100644
--- a/include/libnsgif.h
+++ b/include/libnsgif.h
@@ -15,8 +15,8 @@
#ifndef _LIBNSGIF_H_
#define _LIBNSGIF_H_
+#include <stdint.h>
#include <stdbool.h>
-#include <inttypes.h>
/* Error return values */
typedef enum {
@@ -100,7 +100,7 @@ typedef struct gif_animation {
/** callbacks for bitmap functions */
gif_bitmap_callback_vt bitmap_callbacks;
/** pointer to GIF data */
- unsigned char *gif_data;
+ const uint8_t *gif_data;
/** width of GIF (may increase during decoding) */
unsigned int width;
/** heigth of GIF (may increase during decoding) */
@@ -173,7 +173,7 @@ void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks);
* - GIF_OK for successful decoding
* - GIF_WORKING for successful decoding if more frames are expected
*/
-gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data);
+gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data);
/**
* Decodes a GIF frame.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index c2a8c73..7d48b7e 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -527,7 +527,7 @@ static gif_result gif__update_bitmap(
*/
static gif_result gif__parse_extension_graphic_control(
struct gif_frame *frame,
- uint8_t *data,
+ const uint8_t *data,
size_t len)
{
/* 6-byte Graphic Control Extension is:
@@ -583,7 +583,7 @@ static gif_result gif__parse_extension_graphic_control(
*/
static gif_result gif__parse_extension_application(
struct gif_animation *gif,
- uint8_t *data,
+ const uint8_t *data,
size_t len)
{
/* 14-byte+ Application Extension is:
@@ -619,11 +619,11 @@ static gif_result gif__parse_extension_application(
static gif_result gif__parse_frame_extensions(
struct gif_animation *gif,
struct gif_frame *frame,
- uint8_t **pos,
+ const uint8_t **pos,
bool decode)
{
- uint8_t *gif_data = *pos;
- uint8_t *gif_end = gif->gif_data + gif->buffer_size;
+ const uint8_t *gif_data = *pos;
+ const uint8_t *gif_end = gif->gif_data + gif->buffer_size;
int gif_bytes = gif_end - gif_data;
/* Initialise the extensions */
@@ -727,7 +727,7 @@ static gif_result gif__parse_frame_extensions(
static gif_result gif__parse_image_descriptor(
struct gif_animation *gif,
struct gif_frame *frame,
- uint8_t **pos,
+ const uint8_t **pos,
bool decode)
{
const uint8_t *data = *pos;
@@ -783,7 +783,7 @@ static gif_result gif__parse_image_descriptor(
static gif_result gif__parse_colour_table(
struct gif_animation *gif,
struct gif_frame *frame,
- uint8_t **pos,
+ const uint8_t **pos,
bool decode)
{
unsigned colour_table_size;
@@ -840,10 +840,10 @@ static gif_result gif__parse_colour_table(
static gif_result gif__parse_image_data(
struct gif_animation *gif,
struct gif_frame *frame,
- uint8_t **pos,
+ const uint8_t **pos,
bool decode)
{
- uint8_t *data = *pos;
+ const uint8_t *data = *pos;
size_t len = gif->gif_data + gif->buffer_size - data;
uint32_t frame_idx = frame - gif->frames;
uint8_t minimum_code_size;
@@ -970,9 +970,9 @@ static gif_result gif__process_frame(
uint32_t frame_idx,
bool decode)
{
- uint8_t *pos;
- uint8_t *end;
gif_result ret;
+ const uint8_t *pos;
+ const uint8_t *end;
struct gif_frame *frame;
frame = gif__get_frame(gif, frame_idx);
@@ -980,7 +980,7 @@ static gif_result gif__process_frame(
return GIF_INSUFFICIENT_MEMORY;
}
- end = (uint8_t *)(gif->gif_data + gif->buffer_size);
+ end = gif->gif_data + gif->buffer_size;
if (decode) {
pos = gif->gif_data + frame->frame_pointer;
@@ -1055,9 +1055,9 @@ void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks)
/* exported function documented in libnsgif.h */
-gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data)
+gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
{
- uint8_t *gif_data;
+ const uint8_t *gif_data;
uint32_t index;
gif_result ret;
-----------------------------------------------------------------------
Summary of changes:
include/libnsgif.h | 2 +-
src/libnsgif.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/include/libnsgif.h b/include/libnsgif.h
index d0d16ac..36f59a4 100644
--- a/include/libnsgif.h
+++ b/include/libnsgif.h
@@ -173,7 +173,7 @@ void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks);
* - GIF_OK for successful decoding
* - GIF_WORKING for successful decoding if more frames are expected
*/
-gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data);
+gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data);
/**
* Decodes a GIF frame.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 21f319e..7d48b7e 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -1055,7 +1055,7 @@ void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks)
/* exported function documented in libnsgif.h */
-gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data)
+gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
{
const uint8_t *gif_data;
uint32_t index;
--
NetSurf GIF Decoder
1 year, 10 months
libnsgif: branch tlsa/rewrite updated. release/0.2.1-96-geebf250
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/libnsgif.git/shortlog/eebf25066c18fbf04b83...
...commit http://git.netsurf-browser.org/libnsgif.git/commit/eebf25066c18fbf04b8324...
...tree http://git.netsurf-browser.org/libnsgif.git/tree/eebf25066c18fbf04b832403...
The branch, tlsa/rewrite has been updated
via eebf25066c18fbf04b8324030f1989870bb0f38e (commit)
via 24c503f95e1f9ef726e546e616649120b666d15d (commit)
from 5e0642811e2edce8d8845fdf0bcb8f9eefc71f80 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=eebf25066c18fbf04b...
commit eebf25066c18fbf04b8324030f1989870bb0f38e
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Constify raw source data.
diff --git a/include/libnsgif.h b/include/libnsgif.h
index ae6691c..d0d16ac 100644
--- a/include/libnsgif.h
+++ b/include/libnsgif.h
@@ -15,8 +15,8 @@
#ifndef _LIBNSGIF_H_
#define _LIBNSGIF_H_
+#include <stdint.h>
#include <stdbool.h>
-#include <inttypes.h>
/* Error return values */
typedef enum {
@@ -100,7 +100,7 @@ typedef struct gif_animation {
/** callbacks for bitmap functions */
gif_bitmap_callback_vt bitmap_callbacks;
/** pointer to GIF data */
- unsigned char *gif_data;
+ const uint8_t *gif_data;
/** width of GIF (may increase during decoding) */
unsigned int width;
/** heigth of GIF (may increase during decoding) */
diff --git a/src/libnsgif.c b/src/libnsgif.c
index c2a8c73..21f319e 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -527,7 +527,7 @@ static gif_result gif__update_bitmap(
*/
static gif_result gif__parse_extension_graphic_control(
struct gif_frame *frame,
- uint8_t *data,
+ const uint8_t *data,
size_t len)
{
/* 6-byte Graphic Control Extension is:
@@ -583,7 +583,7 @@ static gif_result gif__parse_extension_graphic_control(
*/
static gif_result gif__parse_extension_application(
struct gif_animation *gif,
- uint8_t *data,
+ const uint8_t *data,
size_t len)
{
/* 14-byte+ Application Extension is:
@@ -619,11 +619,11 @@ static gif_result gif__parse_extension_application(
static gif_result gif__parse_frame_extensions(
struct gif_animation *gif,
struct gif_frame *frame,
- uint8_t **pos,
+ const uint8_t **pos,
bool decode)
{
- uint8_t *gif_data = *pos;
- uint8_t *gif_end = gif->gif_data + gif->buffer_size;
+ const uint8_t *gif_data = *pos;
+ const uint8_t *gif_end = gif->gif_data + gif->buffer_size;
int gif_bytes = gif_end - gif_data;
/* Initialise the extensions */
@@ -727,7 +727,7 @@ static gif_result gif__parse_frame_extensions(
static gif_result gif__parse_image_descriptor(
struct gif_animation *gif,
struct gif_frame *frame,
- uint8_t **pos,
+ const uint8_t **pos,
bool decode)
{
const uint8_t *data = *pos;
@@ -783,7 +783,7 @@ static gif_result gif__parse_image_descriptor(
static gif_result gif__parse_colour_table(
struct gif_animation *gif,
struct gif_frame *frame,
- uint8_t **pos,
+ const uint8_t **pos,
bool decode)
{
unsigned colour_table_size;
@@ -840,10 +840,10 @@ static gif_result gif__parse_colour_table(
static gif_result gif__parse_image_data(
struct gif_animation *gif,
struct gif_frame *frame,
- uint8_t **pos,
+ const uint8_t **pos,
bool decode)
{
- uint8_t *data = *pos;
+ const uint8_t *data = *pos;
size_t len = gif->gif_data + gif->buffer_size - data;
uint32_t frame_idx = frame - gif->frames;
uint8_t minimum_code_size;
@@ -970,9 +970,9 @@ static gif_result gif__process_frame(
uint32_t frame_idx,
bool decode)
{
- uint8_t *pos;
- uint8_t *end;
gif_result ret;
+ const uint8_t *pos;
+ const uint8_t *end;
struct gif_frame *frame;
frame = gif__get_frame(gif, frame_idx);
@@ -980,7 +980,7 @@ static gif_result gif__process_frame(
return GIF_INSUFFICIENT_MEMORY;
}
- end = (uint8_t *)(gif->gif_data + gif->buffer_size);
+ end = gif->gif_data + gif->buffer_size;
if (decode) {
pos = gif->gif_data + frame->frame_pointer;
@@ -1057,7 +1057,7 @@ void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks)
/* exported function documented in libnsgif.h */
gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data)
{
- uint8_t *gif_data;
+ const uint8_t *gif_data;
uint32_t index;
gif_result ret;
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=24c503f95e1f9ef726...
commit 24c503f95e1f9ef726e546e616649120b666d15d
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Don't write into the GIF source data to handle errors.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 2a1fdfe..c2a8c73 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -887,25 +887,13 @@ static gif_result gif__parse_image_data(
if (len < 1) return GIF_INSUFFICIENT_FRAME_DATA;
block_size = data[0] + 1;
/* Check if the frame data runs off the end of the file */
- if ((int)(len - block_size) < 0) {
- /* Try to recover by signaling the end of the gif.
- * Once we get garbage data, there is no logical way to
- * determine where the next frame is. It's probably
- * better to partially load the gif than not at all.
- */
- if (len >= 2) {
- data[0] = 0;
- data[1] = GIF_TRAILER;
- len = 1;
- data++;
- break;
- } else {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- } else {
- len -= block_size;
- data += block_size;
+ if (block_size > len) {
+ block_size = len;
+ return GIF_OK;
}
+
+ len -= block_size;
+ data += block_size;
}
gif->frame_count = frame_idx + 1;
-----------------------------------------------------------------------
Summary of changes:
include/libnsgif.h | 4 ++--
src/libnsgif.c | 50 +++++++++++++++++++-------------------------------
2 files changed, 21 insertions(+), 33 deletions(-)
diff --git a/include/libnsgif.h b/include/libnsgif.h
index ae6691c..d0d16ac 100644
--- a/include/libnsgif.h
+++ b/include/libnsgif.h
@@ -15,8 +15,8 @@
#ifndef _LIBNSGIF_H_
#define _LIBNSGIF_H_
+#include <stdint.h>
#include <stdbool.h>
-#include <inttypes.h>
/* Error return values */
typedef enum {
@@ -100,7 +100,7 @@ typedef struct gif_animation {
/** callbacks for bitmap functions */
gif_bitmap_callback_vt bitmap_callbacks;
/** pointer to GIF data */
- unsigned char *gif_data;
+ const uint8_t *gif_data;
/** width of GIF (may increase during decoding) */
unsigned int width;
/** heigth of GIF (may increase during decoding) */
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 2a1fdfe..21f319e 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -527,7 +527,7 @@ static gif_result gif__update_bitmap(
*/
static gif_result gif__parse_extension_graphic_control(
struct gif_frame *frame,
- uint8_t *data,
+ const uint8_t *data,
size_t len)
{
/* 6-byte Graphic Control Extension is:
@@ -583,7 +583,7 @@ static gif_result gif__parse_extension_graphic_control(
*/
static gif_result gif__parse_extension_application(
struct gif_animation *gif,
- uint8_t *data,
+ const uint8_t *data,
size_t len)
{
/* 14-byte+ Application Extension is:
@@ -619,11 +619,11 @@ static gif_result gif__parse_extension_application(
static gif_result gif__parse_frame_extensions(
struct gif_animation *gif,
struct gif_frame *frame,
- uint8_t **pos,
+ const uint8_t **pos,
bool decode)
{
- uint8_t *gif_data = *pos;
- uint8_t *gif_end = gif->gif_data + gif->buffer_size;
+ const uint8_t *gif_data = *pos;
+ const uint8_t *gif_end = gif->gif_data + gif->buffer_size;
int gif_bytes = gif_end - gif_data;
/* Initialise the extensions */
@@ -727,7 +727,7 @@ static gif_result gif__parse_frame_extensions(
static gif_result gif__parse_image_descriptor(
struct gif_animation *gif,
struct gif_frame *frame,
- uint8_t **pos,
+ const uint8_t **pos,
bool decode)
{
const uint8_t *data = *pos;
@@ -783,7 +783,7 @@ static gif_result gif__parse_image_descriptor(
static gif_result gif__parse_colour_table(
struct gif_animation *gif,
struct gif_frame *frame,
- uint8_t **pos,
+ const uint8_t **pos,
bool decode)
{
unsigned colour_table_size;
@@ -840,10 +840,10 @@ static gif_result gif__parse_colour_table(
static gif_result gif__parse_image_data(
struct gif_animation *gif,
struct gif_frame *frame,
- uint8_t **pos,
+ const uint8_t **pos,
bool decode)
{
- uint8_t *data = *pos;
+ const uint8_t *data = *pos;
size_t len = gif->gif_data + gif->buffer_size - data;
uint32_t frame_idx = frame - gif->frames;
uint8_t minimum_code_size;
@@ -887,25 +887,13 @@ static gif_result gif__parse_image_data(
if (len < 1) return GIF_INSUFFICIENT_FRAME_DATA;
block_size = data[0] + 1;
/* Check if the frame data runs off the end of the file */
- if ((int)(len - block_size) < 0) {
- /* Try to recover by signaling the end of the gif.
- * Once we get garbage data, there is no logical way to
- * determine where the next frame is. It's probably
- * better to partially load the gif than not at all.
- */
- if (len >= 2) {
- data[0] = 0;
- data[1] = GIF_TRAILER;
- len = 1;
- data++;
- break;
- } else {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- } else {
- len -= block_size;
- data += block_size;
+ if (block_size > len) {
+ block_size = len;
+ return GIF_OK;
}
+
+ len -= block_size;
+ data += block_size;
}
gif->frame_count = frame_idx + 1;
@@ -982,9 +970,9 @@ static gif_result gif__process_frame(
uint32_t frame_idx,
bool decode)
{
- uint8_t *pos;
- uint8_t *end;
gif_result ret;
+ const uint8_t *pos;
+ const uint8_t *end;
struct gif_frame *frame;
frame = gif__get_frame(gif, frame_idx);
@@ -992,7 +980,7 @@ static gif_result gif__process_frame(
return GIF_INSUFFICIENT_MEMORY;
}
- end = (uint8_t *)(gif->gif_data + gif->buffer_size);
+ end = gif->gif_data + gif->buffer_size;
if (decode) {
pos = gif->gif_data + frame->frame_pointer;
@@ -1069,7 +1057,7 @@ void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks)
/* exported function documented in libnsgif.h */
gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data)
{
- uint8_t *gif_data;
+ const uint8_t *gif_data;
uint32_t index;
gif_result ret;
--
NetSurf GIF Decoder
1 year, 10 months
libnsgif: branch tlsa/rewrite updated. release/0.2.1-94-g5e06428
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/libnsgif.git/shortlog/5e0642811e2edce8d884...
...commit http://git.netsurf-browser.org/libnsgif.git/commit/5e0642811e2edce8d8845f...
...tree http://git.netsurf-browser.org/libnsgif.git/tree/5e0642811e2edce8d8845fdf...
The branch, tlsa/rewrite has been updated
discards f2c94c53e1a05e11bd5efda79748f847f24c7f7d (commit)
discards f19df341f8a3f3ec5a319fd4621b4a8f53699dcb (commit)
discards ec55ce31acd8aa8b2601db2a74e5b1725fa61da2 (commit)
discards 170edd84170a8de5801dcf5c022a562374f83b2f (commit)
via 5e0642811e2edce8d8845fdf0bcb8f9eefc71f80 (commit)
via 9f5e2f1af86d35247895dd57bf03f32c5539a04d (commit)
via 5e93ea19730e0df42e9dc3a777cde466f5a8f73d (commit)
via 920ba6c423181ee0eec932c5daeda75fb13fc140 (commit)
via 1b8341cb075731cffbf69141c24d8954fef4ee50 (commit)
via 8a5130c5559989c3b9ea6f74469427abe08c7193 (commit)
via 82d8eff773bbb56cd2021187949e39fcc28899c7 (commit)
via 677ba295b37cc1671f40383f92ad133fe6db26d9 (commit)
This update added new revisions after undoing existing revisions. That is
to say, the old revision is not a strict subset of the new revision. This
situation occurs when you --force push a change and generate a repository
containing something like this:
* -- * -- B -- O -- O -- O (f2c94c53e1a05e11bd5efda79748f847f24c7f7d)
\
N -- N -- N (5e0642811e2edce8d8845fdf0bcb8f9eefc71f80)
When this happens we assume that you've already had alert emails for all
of the O revisions, and so we here report only the revisions in the N
branch from the common base, B.
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=5e0642811e2edce8d8...
commit 5e0642811e2edce8d8845fdf0bcb8f9eefc71f80
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Use same function for frame initialisation and decoding.
Now there aren't two entirely separate code paths that must be
kept in sync.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index ce13041..2a1fdfe 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -967,6 +967,7 @@ static struct gif_frame *gif__get_frame(
*
* \param[in] gif The animation context
* \param[in] frame_idx The frame number to decode.
+ * \param[in] decode Whether to decode the graphical image data.
* \return error code
* - GIF_INSUFFICIENT_DATA for insufficient data to do anything
* - GIF_FRAME_DATA_ERROR for GIF frame data error
@@ -976,127 +977,85 @@ static struct gif_frame *gif__get_frame(
* - GIF_OK for successful decoding
* - GIF_WORKING for successful decoding if more frames are expected
*/
-static gif_result gif_initialise_frame(
+static gif_result gif__process_frame(
struct gif_animation *gif,
- uint32_t frame_idx)
+ uint32_t frame_idx,
+ bool decode)
{
uint8_t *pos;
uint8_t *end;
gif_result ret;
struct gif_frame *frame;
- /* Get our buffer position etc. */
- pos = (uint8_t *)(gif->gif_data + gif->buffer_position);
- end = (uint8_t *)(gif->gif_data + gif->buffer_size);
-
- /* Check if we've finished */
- if (pos < end && pos[0] == GIF_TRAILER) {
- return GIF_OK;
- }
-
- /* We could theoretically get some junk data that gives us millions of
- * frames, so we ensure that we don't have a silly number
- */
- if (frame_idx > 4096) {
- return GIF_FRAME_DATA_ERROR;
- }
-
frame = gif__get_frame(gif, frame_idx);
if (frame == NULL) {
return GIF_INSUFFICIENT_MEMORY;
}
- /* Initialise any extensions */
- ret = gif__parse_frame_extensions(gif, frame, &pos, true);
- if (ret != GIF_OK) {
- goto cleanup;
- }
-
- ret = gif__parse_image_descriptor(gif, frame, &pos, true);
- if (ret != GIF_OK) {
- goto cleanup;
- }
-
- ret = gif__parse_colour_table(gif, frame, &pos, false);
- if (ret != GIF_OK) {
- goto cleanup;
- }
-
- ret = gif__parse_image_data(gif, frame, &pos, false);
- if (ret != GIF_OK) {
- goto cleanup;
- }
-
-cleanup:
- gif->buffer_position = pos - gif->gif_data;
+ end = (uint8_t *)(gif->gif_data + gif->buffer_size);
- return ret;
-}
+ if (decode) {
+ pos = gif->gif_data + frame->frame_pointer;
-/**
- * decode a gif frame
- *
- * \param gif gif animation context.
- * \param frame The frame number to decode.
- * \param clear_image flag for image data being cleared instead of plotted.
- */
-static gif_result gif_internal_decode_frame(
- struct gif_animation *gif,
- uint32_t frame_idx)
-{
- uint8_t *pos;
- gif_result ret;
- struct gif_frame *frame;
+ /* Ensure this frame is supposed to be decoded */
+ if (frame->display == false) {
+ return GIF_OK;
+ }
- /* Ensure the frame is in range to decode */
- if (frame_idx > gif->frame_count_partial) {
- return GIF_INSUFFICIENT_DATA;
- }
+ /* Ensure the frame is in range to decode */
+ if (frame_idx > gif->frame_count_partial) {
+ return GIF_INSUFFICIENT_DATA;
+ }
- /* Done if frame is already decoded */
- if (((int)frame_idx == gif->decoded_frame)) {
- return GIF_OK;
- }
+ /* Done if frame is already decoded */
+ if ((int)frame_idx == gif->decoded_frame) {
+ return GIF_OK;
+ }
+ } else {
+ pos = (uint8_t *)(gif->gif_data + gif->buffer_position);
- frame = gif__get_frame(gif, frame_idx);
- if (frame == NULL) {
- return GIF_INSUFFICIENT_MEMORY;
- }
+ /* Check if we've finished */
+ if (pos < end && pos[0] == GIF_TRAILER) {
+ return GIF_OK;
+ }
- /* Ensure this frame is supposed to be decoded */
- if (frame->display == false) {
- return GIF_OK;
+ /* We could theoretically get some junk data that gives us
+ * millions of frames, so we ensure that we don't have a
+ * silly number. */
+ if (frame_idx > 4096) {
+ return GIF_FRAME_DATA_ERROR;
+ }
}
- /* Get the start of our frame data and the end of the GIF data. */
- pos = gif->gif_data + frame->frame_pointer;
-
- /* Skip any extensions because they have already been processed */
- ret = gif__parse_frame_extensions(gif, frame, &pos, false);
+ /* Initialise any extensions */
+ ret = gif__parse_frame_extensions(gif, frame, &pos, !decode);
if (ret != GIF_OK) {
goto cleanup;
}
- ret = gif__parse_image_descriptor(gif, frame, &pos, false);
+ ret = gif__parse_image_descriptor(gif, frame, &pos, !decode);
if (ret != GIF_OK) {
goto cleanup;
}
- ret = gif__parse_colour_table(gif, frame, &pos, true);
+ ret = gif__parse_colour_table(gif, frame, &pos, decode);
if (ret != GIF_OK) {
goto cleanup;
}
- ret = gif__parse_image_data(gif, frame, &pos, true);
+ ret = gif__parse_image_data(gif, frame, &pos, decode);
if (ret != GIF_OK) {
goto cleanup;
}
cleanup:
+ if (!decode) {
+ gif->buffer_position = pos - gif->gif_data;
+ }
+
return ret;
}
-
/* exported function documented in libnsgif.h */
void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks)
{
@@ -1294,7 +1253,7 @@ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data)
}
/* Repeatedly try to initialise frames */
- while ((ret = gif_initialise_frame(gif, gif->frame_count)) == GIF_WORKING);
+ while ((ret = gif__process_frame(gif, gif->frame_count, false)) == GIF_WORKING);
/* If there was a memory error tell the caller */
if ((ret == GIF_INSUFFICIENT_MEMORY) ||
@@ -1317,7 +1276,7 @@ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data)
/* exported function documented in libnsgif.h */
gif_result gif_decode_frame(gif_animation *gif, unsigned int frame)
{
- return gif_internal_decode_frame(gif, frame);
+ return gif__process_frame(gif, frame, true);
}
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=9f5e2f1af86d352478...
commit 9f5e2f1af86d35247895dd57bf03f32c5539a04d
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Pass data position into the decoders.
This avoids storing the state on in the gif structure, and having
to save and restore it when processing frames.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index ce500a9..ce13041 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -271,7 +271,7 @@ static gif_result gif__decode_complex(
uint32_t offset_x,
uint32_t offset_y,
uint32_t interlace,
- uint8_t minimum_code_size,
+ const uint8_t *data,
uint32_t transparency_index,
uint32_t *restrict frame_data,
uint32_t *restrict colour_table)
@@ -281,8 +281,9 @@ static gif_result gif__decode_complex(
lzw_result res;
/* Initialise the LZW decoding */
- res = lzw_decode_init(gif->lzw_ctx, minimum_code_size,
- gif->gif_data, gif->buffer_size, gif->buffer_position);
+ res = lzw_decode_init(gif->lzw_ctx, data[0],
+ gif->gif_data, gif->buffer_size,
+ data + 1 - gif->gif_data);
if (res != LZW_OK) {
return gif_error_from_lzw(res);
}
@@ -345,7 +346,7 @@ static gif_result gif__decode_simple(
struct gif_animation *gif,
uint32_t height,
uint32_t offset_y,
- uint8_t minimum_code_size,
+ const uint8_t *data,
uint32_t transparency_index,
uint32_t *restrict frame_data,
uint32_t *restrict colour_table)
@@ -356,9 +357,10 @@ static gif_result gif__decode_simple(
lzw_result res;
/* Initialise the LZW decoding */
- res = lzw_decode_init_map(gif->lzw_ctx,
- minimum_code_size, transparency_index, colour_table,
- gif->gif_data, gif->buffer_size, gif->buffer_position);
+ res = lzw_decode_init_map(gif->lzw_ctx, data[0],
+ transparency_index, colour_table,
+ gif->gif_data, gif->buffer_size,
+ data + 1 - gif->gif_data);
if (res != LZW_OK) {
return gif_error_from_lzw(res);
}
@@ -391,7 +393,7 @@ static gif_result gif__decode_simple(
static inline gif_result gif__decode(
struct gif_animation *gif,
struct gif_frame *frame,
- uint8_t minimum_code_size,
+ const uint8_t *data,
uint32_t *restrict frame_data)
{
gif_result ret;
@@ -405,12 +407,12 @@ static inline gif_result gif__decode(
if (interlace == false && width == gif->width && offset_x == 0) {
ret = gif__decode_simple(gif, height, offset_y,
- minimum_code_size, transparency_index,
+ data, transparency_index,
frame_data, colour_table);
} else {
ret = gif__decode_complex(gif, width, height,
offset_x, offset_y, interlace,
- minimum_code_size, transparency_index,
+ data, transparency_index,
frame_data, colour_table);
}
@@ -464,7 +466,7 @@ static void gif__restore_bg(
static gif_result gif__update_bitmap(
struct gif_animation *gif,
struct gif_frame *frame,
- uint8_t minimum_code_size,
+ const uint8_t *data,
uint32_t frame_idx)
{
gif_result ret;
@@ -501,7 +503,7 @@ static gif_result gif__update_bitmap(
gif__record_frame(gif, bitmap);
}
- ret = gif__decode(gif, frame, minimum_code_size, bitmap);
+ ret = gif__decode(gif, frame, data, bitmap);
gif__bitmap_modified(gif);
@@ -617,15 +619,12 @@ static gif_result gif__parse_extension_application(
static gif_result gif__parse_frame_extensions(
struct gif_animation *gif,
struct gif_frame *frame,
+ uint8_t **pos,
bool decode)
{
- uint8_t *gif_data, *gif_end;
- int gif_bytes;
-
- /* Get our buffer position etc. */
- gif_data = gif->gif_data + gif->buffer_position;
- gif_end = gif->gif_data + gif->buffer_size;
- gif_bytes = gif_end - gif_data;
+ uint8_t *gif_data = *pos;
+ uint8_t *gif_end = gif->gif_data + gif->buffer_size;
+ int gif_bytes = gif_end - gif_data;
/* Initialise the extensions */
while (gif_bytes > 0 && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
@@ -700,7 +699,7 @@ static gif_result gif__parse_frame_extensions(
}
/* Set buffer position and return */
- gif->buffer_position = gif_data - gif->gif_data;
+ *pos = gif_data;
return GIF_OK;
}
@@ -728,10 +727,11 @@ static gif_result gif__parse_frame_extensions(
static gif_result gif__parse_image_descriptor(
struct gif_animation *gif,
struct gif_frame *frame,
+ uint8_t **pos,
bool decode)
{
- const uint8_t *data = gif->gif_data + gif->buffer_position;
- size_t len = gif->buffer_size - gif->buffer_position;
+ const uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
enum {
GIF_IMAGE_DESCRIPTOR_LEN = 10u,
GIF_IMAGE_SEPARATOR = 0x2Cu,
@@ -767,7 +767,7 @@ static gif_result gif__parse_image_descriptor(
gif->height = (y + h > gif->height) ? y + h : gif->height;
}
- gif->buffer_position += GIF_IMAGE_DESCRIPTOR_LEN;
+ *pos += GIF_IMAGE_DESCRIPTOR_LEN;
return GIF_OK;
}
@@ -783,11 +783,12 @@ static gif_result gif__parse_image_descriptor(
static gif_result gif__parse_colour_table(
struct gif_animation *gif,
struct gif_frame *frame,
+ uint8_t **pos,
bool decode)
{
- const uint8_t *data = gif->gif_data + gif->buffer_position;
- size_t len = gif->buffer_size - gif->buffer_position;
unsigned colour_table_size;
+ const uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
assert(gif != NULL);
assert(frame != NULL);
@@ -822,8 +823,8 @@ static gif_result gif__parse_colour_table(
}
}
- gif->buffer_position += colour_table_size * 3;
gif->colour_table = gif->local_colour_table;
+ *pos += colour_table_size * 3;
return GIF_OK;
}
@@ -839,10 +840,11 @@ static gif_result gif__parse_colour_table(
static gif_result gif__parse_image_data(
struct gif_animation *gif,
struct gif_frame *frame,
+ uint8_t **pos,
bool decode)
{
- uint8_t *data = gif->gif_data + gif->buffer_position;
- size_t len = gif->buffer_size - gif->buffer_position;
+ uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
uint32_t frame_idx = frame - gif->frames;
uint8_t minimum_code_size;
gif_result ret;
@@ -871,16 +873,16 @@ static gif_result gif__parse_image_data(
if (minimum_code_size >= LZW_CODE_MAX) {
return GIF_DATA_ERROR;
}
- gif->buffer_position++;
- data++;
- len--;
if (decode) {
- ret = gif__update_bitmap(gif, frame, minimum_code_size,
- frame_idx);
+ ret = gif__update_bitmap(gif, frame, data, frame_idx);
} else {
uint32_t block_size = 0;
+ /* Skip the minimum code size. */
+ data++;
+ len--;
+
while (block_size != 1) {
if (len < 1) return GIF_INSUFFICIENT_FRAME_DATA;
block_size = data[0] + 1;
@@ -906,9 +908,9 @@ static gif_result gif__parse_image_data(
}
}
- gif->buffer_position = data - gif->gif_data;
gif->frame_count = frame_idx + 1;
gif->frames[frame_idx].display = true;
+ *pos = data;
/* Check if we've finished */
if (len < 1) {
@@ -978,17 +980,17 @@ static gif_result gif_initialise_frame(
struct gif_animation *gif,
uint32_t frame_idx)
{
+ uint8_t *pos;
+ uint8_t *end;
gif_result ret;
- uint8_t *gif_end;
- uint8_t *gif_data;
struct gif_frame *frame;
/* Get our buffer position etc. */
- gif_data = (uint8_t *)(gif->gif_data + gif->buffer_position);
- gif_end = (uint8_t *)(gif->gif_data + gif->buffer_size);
+ pos = (uint8_t *)(gif->gif_data + gif->buffer_position);
+ end = (uint8_t *)(gif->gif_data + gif->buffer_size);
/* Check if we've finished */
- if (gif_data < gif_end && (gif_data[0] == GIF_TRAILER)) {
+ if (pos < end && pos[0] == GIF_TRAILER) {
return GIF_OK;
}
@@ -1005,27 +1007,30 @@ static gif_result gif_initialise_frame(
}
/* Initialise any extensions */
- ret = gif__parse_frame_extensions(gif, frame, true);
+ ret = gif__parse_frame_extensions(gif, frame, &pos, true);
if (ret != GIF_OK) {
- return ret;
+ goto cleanup;
}
- ret = gif__parse_image_descriptor(gif, frame, true);
+ ret = gif__parse_image_descriptor(gif, frame, &pos, true);
if (ret != GIF_OK) {
- return ret;
+ goto cleanup;
}
- ret = gif__parse_colour_table(gif, frame, false);
+ ret = gif__parse_colour_table(gif, frame, &pos, false);
if (ret != GIF_OK) {
- return ret;
+ goto cleanup;
}
- ret = gif__parse_image_data(gif, frame, false);
+ ret = gif__parse_image_data(gif, frame, &pos, false);
if (ret != GIF_OK) {
- return ret;
+ goto cleanup;
}
- return GIF_OK;
+cleanup:
+ gif->buffer_position = pos - gif->gif_data;
+
+ return ret;
}
/**
@@ -1039,10 +1044,9 @@ static gif_result gif_internal_decode_frame(
struct gif_animation *gif,
uint32_t frame_idx)
{
+ uint8_t *pos;
gif_result ret;
- uint8_t *gif_data;
struct gif_frame *frame;
- uint32_t save_buffer_position;
/* Ensure the frame is in range to decode */
if (frame_idx > gif->frame_count_partial) {
@@ -1064,39 +1068,31 @@ static gif_result gif_internal_decode_frame(
return GIF_OK;
}
- /* Get the start of our frame data and the end of the GIF data */
- gif_data = gif->gif_data + frame->frame_pointer;
-
- /* Save the buffer position */
- save_buffer_position = gif->buffer_position;
- gif->buffer_position = gif_data - gif->gif_data;
+ /* Get the start of our frame data and the end of the GIF data. */
+ pos = gif->gif_data + frame->frame_pointer;
/* Skip any extensions because they have already been processed */
- ret = gif__parse_frame_extensions(gif, frame, false);
+ ret = gif__parse_frame_extensions(gif, frame, &pos, false);
if (ret != GIF_OK) {
- goto gif_decode_frame_exit;
+ goto cleanup;
}
- ret = gif__parse_image_descriptor(gif, frame, false);
+ ret = gif__parse_image_descriptor(gif, frame, &pos, false);
if (ret != GIF_OK) {
- goto gif_decode_frame_exit;
+ goto cleanup;
}
- ret = gif__parse_colour_table(gif, frame, true);
+ ret = gif__parse_colour_table(gif, frame, &pos, true);
if (ret != GIF_OK) {
- goto gif_decode_frame_exit;
+ goto cleanup;
}
- ret = gif__parse_image_data(gif, frame, true);
+ ret = gif__parse_image_data(gif, frame, &pos, true);
if (ret != GIF_OK) {
- goto gif_decode_frame_exit;
+ goto cleanup;
}
-gif_decode_frame_exit:
-
- /* Restore the buffer position */
- gif->buffer_position = save_buffer_position;
-
+cleanup:
return ret;
}
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=5e93ea19730e0df42e...
commit 5e93ea19730e0df42e9dc3a777cde466f5a8f73d
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Frame initialisation: Don't need to calculate gif_bytes.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 54da5e1..ce500a9 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -979,17 +979,16 @@ static gif_result gif_initialise_frame(
uint32_t frame_idx)
{
gif_result ret;
+ uint8_t *gif_end;
+ uint8_t *gif_data;
struct gif_frame *frame;
- uint8_t *gif_data, *gif_end;
- int gif_bytes;
/* Get our buffer position etc. */
gif_data = (uint8_t *)(gif->gif_data + gif->buffer_position);
gif_end = (uint8_t *)(gif->gif_data + gif->buffer_size);
- gif_bytes = (gif_end - gif_data);
/* Check if we've finished */
- if ((gif_bytes > 0) && (gif_data[0] == GIF_TRAILER)) {
+ if (gif_data < gif_end && (gif_data[0] == GIF_TRAILER)) {
return GIF_OK;
}
@@ -1006,7 +1005,6 @@ static gif_result gif_initialise_frame(
}
/* Initialise any extensions */
- gif->buffer_position = gif_data - gif->gif_data;
ret = gif__parse_frame_extensions(gif, frame, true);
if (ret != GIF_OK) {
return ret;
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=920ba6c423181ee0ee...
commit 920ba6c423181ee0eec932c5daeda75fb13fc140
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Use frame rather than indexing frames array.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 80c4530..54da5e1 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -1007,22 +1007,22 @@ static gif_result gif_initialise_frame(
/* Initialise any extensions */
gif->buffer_position = gif_data - gif->gif_data;
- ret = gif__parse_frame_extensions(gif, &gif->frames[frame_idx], true);
+ ret = gif__parse_frame_extensions(gif, frame, true);
if (ret != GIF_OK) {
return ret;
}
- ret = gif__parse_image_descriptor(gif, &gif->frames[frame_idx], true);
+ ret = gif__parse_image_descriptor(gif, frame, true);
if (ret != GIF_OK) {
return ret;
}
- ret = gif__parse_colour_table(gif, &gif->frames[frame_idx], false);
+ ret = gif__parse_colour_table(gif, frame, false);
if (ret != GIF_OK) {
return ret;
}
- ret = gif__parse_image_data(gif, &gif->frames[frame_idx], false);
+ ret = gif__parse_image_data(gif, frame, false);
if (ret != GIF_OK) {
return ret;
}
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=1b8341cb075731cffb...
commit 1b8341cb075731cffbf69141c24d8954fef4ee50
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Remove redundant check and comment from frame initialiser.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 53186d9..80c4530 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -993,14 +993,6 @@ static gif_result gif_initialise_frame(
return GIF_OK;
}
- /* Check if there is enough data remaining. The shortest block of data
- * is a 4-byte comment extension + 1-byte block terminator + 1-byte gif
- * trailer
- */
- if (gif_bytes < 6) {
- return GIF_INSUFFICIENT_DATA;
- }
-
/* We could theoretically get some junk data that gives us millions of
* frames, so we ensure that we don't have a silly number
*/
@@ -1013,12 +1005,6 @@ static gif_result gif_initialise_frame(
return GIF_INSUFFICIENT_MEMORY;
}
- /* We pretend to initialise the frames, but really we just skip over
- * all the data contained within. This is all basically a cut down
- * version of gif_decode_frame that doesn't have any of the LZW bits in
- * it.
- */
-
/* Initialise any extensions */
gif->buffer_position = gif_data - gif->gif_data;
ret = gif__parse_frame_extensions(gif, &gif->frames[frame_idx], true);
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=8a5130c5559989c3b9...
commit 8a5130c5559989c3b9ea6f74469427abe08c7193
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Use image parsing frunction for frame initialisation.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index bc3c83b..53186d9 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -982,7 +982,6 @@ static gif_result gif_initialise_frame(
struct gif_frame *frame;
uint8_t *gif_data, *gif_end;
int gif_bytes;
- uint32_t block_size;
/* Get our buffer position etc. */
gif_data = (uint8_t *)(gif->gif_data + gif->buffer_position);
@@ -1036,67 +1035,13 @@ static gif_result gif_initialise_frame(
if (ret != GIF_OK) {
return ret;
}
- gif_data = gif->gif_data + gif->buffer_position;
- gif_bytes = (gif_end - gif_data);
- /* Move our data onwards and remember we've got a bit of this frame */
- gif->frame_count_partial = frame_idx + 1;
-
- /* Ensure we have a correct code size */
- if (gif_bytes < 1) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- if (gif_data[0] >= LZW_CODE_MAX) {
- return GIF_DATA_ERROR;
- }
-
- /* Move our pointer to the actual image data */
- gif_data++;
- --gif_bytes;
-
- /* Repeatedly skip blocks until we get a zero block or run out of data
- * These blocks of image data are processed later by gif_decode_frame()
- */
- block_size = 0;
- while (block_size != 1) {
- if (gif_bytes < 1) return GIF_INSUFFICIENT_FRAME_DATA;
- block_size = gif_data[0] + 1;
- /* Check if the frame data runs off the end of the file */
- if ((int)(gif_bytes - block_size) < 0) {
- /* Try to recover by signaling the end of the gif.
- * Once we get garbage data, there is no logical way to
- * determine where the next frame is. It's probably
- * better to partially load the gif than not at all.
- */
- if (gif_bytes >= 2) {
- gif_data[0] = 0;
- gif_data[1] = GIF_TRAILER;
- gif_bytes = 1;
- ++gif_data;
- break;
- } else {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- } else {
- gif_bytes -= block_size;
- gif_data += block_size;
- }
+ ret = gif__parse_image_data(gif, &gif->frames[frame_idx], false);
+ if (ret != GIF_OK) {
+ return ret;
}
- /* Add the frame and set the display flag */
- gif->buffer_position = gif_data - gif->gif_data;
- gif->frame_count = frame_idx + 1;
- gif->frames[frame_idx].display = true;
-
- /* Check if we've finished */
- if (gif_bytes < 1) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- } else {
- if (gif_data[0] == GIF_TRAILER) {
- return GIF_OK;
- }
- }
- return GIF_WORKING;
+ return GIF_OK;
}
/**
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=82d8eff773bbb56cd2...
commit 82d8eff773bbb56cd2021187949e39fcc28899c7
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Reorder functions so frame initialise can use image decoder.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index b95b912..bc3c83b 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -70,6 +70,21 @@ enum gif_disposal {
/** standard GIF header size */
#define GIF_STANDARD_HEADER_SIZE 13
+static gif_result gif_error_from_lzw(lzw_result l_res)
+{
+ static const gif_result g_res[] = {
+ [LZW_OK] = GIF_OK,
+ [LZW_OK_EOD] = GIF_END_OF_FRAME,
+ [LZW_NO_MEM] = GIF_INSUFFICIENT_MEMORY,
+ [LZW_NO_DATA] = GIF_INSUFFICIENT_FRAME_DATA,
+ [LZW_EOI_CODE] = GIF_FRAME_DATA_ERROR,
+ [LZW_BAD_ICODE] = GIF_FRAME_DATA_ERROR,
+ [LZW_BAD_CODE] = GIF_FRAME_DATA_ERROR,
+ };
+ assert(l_res != LZW_BAD_PARAM);
+ assert(l_res != LZW_NO_COLOUR);
+ return g_res[l_res];
+}
/**
* Updates the sprite memory size
@@ -79,10 +94,10 @@ enum gif_disposal {
* \param height The height of the sprite
* \return GIF_INSUFFICIENT_MEMORY for a memory error GIF_OK for success
*/
-static gif_result
-gif_initialise_sprite(gif_animation *gif,
- uint32_t width,
- uint32_t height)
+static gif_result gif_initialise_sprite(
+ struct gif_animation *gif,
+ uint32_t width,
+ uint32_t height)
{
/* Already allocated? */
if (gif->frame_image) {
@@ -99,909 +114,717 @@ gif_initialise_sprite(gif_animation *gif,
}
/**
- * Parse the application extension
+ * Helper to get the rendering bitmap for a gif.
*
- * \param[in] frame The gif object we're decoding.
- * \param[in] data The data to decode.
- * \param[in] len Byte length of data.
- * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
- * GIF_OK for success.
+ * \param[in] gif The gif object we're decoding.
+ * \return Client pixel buffer for rendering into.
*/
-static gif_result gif__parse_extension_graphic_control(
- struct gif_frame *frame,
- uint8_t *data,
- size_t len)
+static inline uint32_t* gif__bitmap_get(
+ struct gif_animation *gif)
{
- /* 6-byte Graphic Control Extension is:
- *
- * +0 CHAR Graphic Control Label
- * +1 CHAR Block Size
- * +2 CHAR __Packed Fields__
- * 3BITS Reserved
- * 3BITS Disposal Method
- * 1BIT User Input Flag
- * 1BIT Transparent Color Flag
- * +3 SHORT Delay Time
- * +5 CHAR Transparent Color Index
- */
- if (len < 6) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
-
- frame->frame_delay = data[3] | (data[4] << 8);
- if (data[2] & GIF_TRANSPARENCY_MASK) {
- frame->transparency = true;
- frame->transparency_index = data[5];
- }
+ gif_result ret;
- frame->disposal_method = ((data[2] & GIF_DISPOSAL_MASK) >> 2);
- /* I have encountered documentation and GIFs in the
- * wild that use 0x04 to restore the previous frame,
- * rather than the officially documented 0x03. I
- * believe some (older?) software may even actually
- * export this way. We handle this as a type of
- * "quirks" mode. */
- if (frame->disposal_method == GIF_DISPOSAL_RESTORE_QUIRK) {
- frame->disposal_method = GIF_DISPOSAL_RESTORE_PREV;
+ /* Make sure we have a buffer to decode to. */
+ ret = gif_initialise_sprite(gif, gif->width, gif->height);
+ if (ret != GIF_OK) {
+ return NULL;
}
- /* if we are clearing the background then we need to
- * redraw enough to cover the previous frame too. */
- frame->redraw_required =
- frame->disposal_method == GIF_DISPOSAL_RESTORE_BG ||
- frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV;
-
- return GIF_OK;
+ /* Get the frame data */
+ assert(gif->bitmap_callbacks.bitmap_get_buffer);
+ return (uint32_t *)gif->bitmap_callbacks.bitmap_get_buffer(
+ gif->frame_image);
}
/**
- * Parse the application extension
+ * Helper to tell the client that their bitmap was modified.
*
- * \param[in] gif The gif object we're decoding.
- * \param[in] data The data to decode.
- * \param[in] len Byte length of data.
- * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
- * GIF_OK for success.
+ * \param[in] gif The gif object we're decoding.
*/
-static gif_result gif__parse_extension_application(
- struct gif_animation *gif,
- uint8_t *data,
- size_t len)
+static inline void gif__bitmap_modified(
+ const struct gif_animation *gif)
{
- /* 14-byte+ Application Extension is:
- *
- * +0 CHAR Application Extension Label
- * +1 CHAR Block Size
- * +2 8CHARS Application Identifier
- * +10 3CHARS Appl. Authentication Code
- * +13 1-256 Application Data (Data sub-blocks)
- */
- if (len < 17) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ if (gif->bitmap_callbacks.bitmap_modified) {
+ gif->bitmap_callbacks.bitmap_modified(gif->frame_image);
}
+}
- if ((data[1] == 0x0b) &&
- (strncmp((const char *)data + 2, "NETSCAPE2.0", 11) == 0) &&
- (data[13] == 0x03) && (data[14] == 0x01)) {
- gif->loop_count = data[15] | (data[16] << 8);
+/**
+ * Helper to tell the client that whether the bitmap is opaque.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frmae The frame that has been decoded.
+ */
+static inline void gif__bitmap_set_opaque(
+ const struct gif_animation *gif,
+ const struct gif_frame *frame)
+{
+ if (gif->bitmap_callbacks.bitmap_set_opaque) {
+ gif->bitmap_callbacks.bitmap_set_opaque(
+ gif->frame_image, frame->opaque);
}
-
- return GIF_OK;
}
/**
- * Parse the frame's extensions
+ * Helper to get the client to determine if the bitmap is opaque.
*
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to parse extensions for.
- * \param[in] decode Whether to decode or skip over the extension.
- * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
- * GIF_OK for success.
+ * \todo: We don't really need to get the client to do this for us.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \return true if the bitmap is opaque, false otherwise.
*/
-static gif_result gif__parse_frame_extensions(
- struct gif_animation *gif,
- struct gif_frame *frame,
- bool decode)
+static inline bool gif__bitmap_get_opaque(
+ const struct gif_animation *gif)
{
- uint8_t *gif_data, *gif_end;
- int gif_bytes;
-
- /* Get our buffer position etc. */
- gif_data = gif->gif_data + gif->buffer_position;
- gif_end = gif->gif_data + gif->buffer_size;
- gif_bytes = gif_end - gif_data;
-
- /* Initialise the extensions */
- while (gif_bytes > 0 && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
- bool block_step = true;
- gif_result ret;
-
- gif_data++;
- gif_bytes--;
-
- if (gif_bytes == 0) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
+ if (gif->bitmap_callbacks.bitmap_test_opaque) {
+ return gif->bitmap_callbacks.bitmap_test_opaque(
+ gif->frame_image);
+ }
- /* Switch on extension label */
- switch (gif_data[0]) {
- case GIF_EXTENSION_GRAPHIC_CONTROL:
- if (decode) {
- ret = gif__parse_extension_graphic_control(
- frame, gif_data, gif_bytes);
- if (ret != GIF_OK) {
- return ret;
- }
- }
- break;
+ return false;
+}
- case GIF_EXTENSION_APPLICATION:
- if (decode) {
- ret = gif__parse_extension_application(
- gif, gif_data, gif_bytes);
- if (ret != GIF_OK) {
- return ret;
- }
- }
- break;
+static void gif__record_frame(
+ struct gif_animation *gif,
+ const uint32_t *bitmap)
+{
+ bool need_alloc = gif->prev_frame == NULL;
+ uint32_t *prev_frame;
- case GIF_EXTENSION_COMMENT:
- /* Move the pointer to the first data sub-block Skip 1
- * byte for the extension label. */
- ++gif_data;
- block_step = false;
- break;
+ if (gif->decoded_frame == GIF_INVALID_FRAME ||
+ gif->decoded_frame == gif->prev_index) {
+ /* No frame to copy, or already have this frame recorded. */
+ return;
+ }
- default:
- break;
- }
+ bitmap = gif__bitmap_get(gif);
+ if (bitmap == NULL) {
+ return;
+ }
- if (block_step) {
- /* Move the pointer to the first data sub-block Skip 2
- * bytes for the extension label and size fields Skip
- * the extension size itself
- */
- if (gif_bytes < 2) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- gif_data += 2 + gif_data[1];
- }
+ if (gif->prev_frame != NULL &&
+ gif->width * gif->height > gif->prev_width * gif->prev_height) {
+ need_alloc = true;
+ }
- /* Repeatedly skip blocks until we get a zero block or run out
- * of data. This data is ignored by this gif decoder. */
- while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) {
- gif_data += gif_data[0] + 1;
- if (gif_data >= gif_end) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
+ if (need_alloc) {
+ prev_frame = realloc(gif->prev_frame,
+ gif->width * gif->height * 4);
+ if (prev_frame == NULL) {
+ return;
}
- gif_data++;
- gif_bytes = gif_end - gif_data;
+ } else {
+ prev_frame = gif->prev_frame;
}
- if (gif_data > gif_end) {
- gif_data = gif_end;
- }
+ memcpy(prev_frame, bitmap, gif->width * gif->height * 4);
- /* Set buffer position and return */
- gif->buffer_position = gif_data - gif->gif_data;
- return GIF_OK;
+ gif->prev_frame = prev_frame;
+ gif->prev_width = gif->width;
+ gif->prev_height = gif->height;
+ gif->prev_index = gif->decoded_frame;
}
-/**
- * Parse a GIF Image Descriptor.
- *
- * The format is:
- *
- * +0 CHAR Image Separator (0x2c)
- * +1 SHORT Image Left Position
- * +3 SHORT Image Top Position
- * +5 SHORT Width
- * +7 SHORT Height
- * +9 CHAR __Packed Fields__
- * 1BIT Local Colour Table Flag
- * 1BIT Interlace Flag
- * 1BIT Sort Flag
- * 2BITS Reserved
- * 3BITS Size of Local Colour Table
- *
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to parse an image descriptor for.
- * \return GIF_OK on success, appropriate error otherwise.
- */
-static gif_result gif__parse_image_descriptor(
- struct gif_animation *gif,
- struct gif_frame *frame,
- bool decode)
+static gif_result gif__recover_frame(
+ const struct gif_animation *gif,
+ uint32_t *bitmap)
{
- const uint8_t *data = gif->gif_data + gif->buffer_position;
- size_t len = gif->buffer_size - gif->buffer_position;
- enum {
- GIF_IMAGE_DESCRIPTOR_LEN = 10u,
- GIF_IMAGE_SEPARATOR = 0x2Cu,
- };
-
- assert(gif != NULL);
- assert(frame != NULL);
+ const uint32_t *prev_frame = gif->prev_frame;
+ unsigned height = gif->height < gif->prev_height ? gif->height : gif->prev_height;
+ unsigned width = gif->width < gif->prev_width ? gif->width : gif->prev_width;
- if (len < GIF_IMAGE_DESCRIPTOR_LEN) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ if (prev_frame == NULL) {
+ return GIF_FRAME_DATA_ERROR;
}
- if (decode) {
- unsigned x, y, w, h;
-
- if (data[0] != GIF_IMAGE_SEPARATOR) {
- return GIF_FRAME_DATA_ERROR;
- }
-
- x = data[1] | (data[2] << 8);
- y = data[3] | (data[4] << 8);
- w = data[5] | (data[6] << 8);
- h = data[7] | (data[8] << 8);
- frame->flags = data[9];
-
- frame->redraw_x = x;
- frame->redraw_y = y;
- frame->redraw_width = w;
- frame->redraw_height = h;
+ for (unsigned y = 0; y < height; y++) {
+ memcpy(bitmap, prev_frame, width * 4);
- /* Frame size may have grown. */
- gif->width = (x + w > gif->width ) ? x + w : gif->width;
- gif->height = (y + h > gif->height) ? y + h : gif->height;
+ bitmap += gif->width;
+ prev_frame += gif->prev_width;
}
- gif->buffer_position += GIF_IMAGE_DESCRIPTOR_LEN;
return GIF_OK;
}
-/**
- * Get a frame's colour table.
- *
- * Sets up gif->colour_table for the frame.
- *
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to get the colour table for.
- * \return GIF_OK on success, appropriate error otherwise.
- */
-static gif_result gif__parse_colour_table(
- struct gif_animation *gif,
- struct gif_frame *frame,
- bool decode)
+static uint32_t gif_interlaced_line(int height, int y)
{
- const uint8_t *data = gif->gif_data + gif->buffer_position;
- size_t len = gif->buffer_size - gif->buffer_position;
- unsigned colour_table_size;
-
- assert(gif != NULL);
- assert(frame != NULL);
-
- if ((frame->flags & GIF_COLOUR_TABLE_MASK) == 0) {
- gif->colour_table = gif->global_colour_table;
- return GIF_OK;
+ if ((y << 3) < height) {
+ return (y << 3);
}
-
- colour_table_size = 2 << (frame->flags & GIF_COLOUR_TABLE_SIZE_MASK);
- if (len < colour_table_size * 3) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ y -= ((height + 7) >> 3);
+ if ((y << 3) < (height - 4)) {
+ return (y << 3) + 4;
}
-
- if (decode) {
- int count = colour_table_size;
- uint8_t *entry = (uint8_t *)gif->local_colour_table;
-
- while (count--) {
- /* Gif colour map contents are r,g,b.
- *
- * We want to pack them bytewise into the
- * colour table, such that the red component
- * is in byte 0 and the alpha component is in
- * byte 3.
- */
-
- *entry++ = *data++; /* r */
- *entry++ = *data++; /* g */
- *entry++ = *data++; /* b */
- *entry++ = 0xff; /* a */
- }
+ y -= ((height + 3) >> 3);
+ if ((y << 2) < (height - 2)) {
+ return (y << 2) + 2;
}
-
- gif->buffer_position += colour_table_size * 3;
- gif->colour_table = gif->local_colour_table;
- return GIF_OK;
+ y -= ((height + 1) >> 2);
+ return (y << 1) + 1;
}
-static struct gif_frame *gif__get_frame(
+static gif_result gif__decode_complex(
struct gif_animation *gif,
- uint32_t frame_idx)
+ uint32_t width,
+ uint32_t height,
+ uint32_t offset_x,
+ uint32_t offset_y,
+ uint32_t interlace,
+ uint8_t minimum_code_size,
+ uint32_t transparency_index,
+ uint32_t *restrict frame_data,
+ uint32_t *restrict colour_table)
{
- struct gif_frame *frame;
+ uint32_t available = 0;
+ gif_result ret = GIF_OK;
+ lzw_result res;
- if (gif->frame_holders > frame_idx) {
- frame = &gif->frames[frame_idx];
- } else {
- /* Allocate more memory */
- size_t count = frame_idx + 1;
- struct gif_frame *temp;
+ /* Initialise the LZW decoding */
+ res = lzw_decode_init(gif->lzw_ctx, minimum_code_size,
+ gif->gif_data, gif->buffer_size, gif->buffer_position);
+ if (res != LZW_OK) {
+ return gif_error_from_lzw(res);
+ }
- temp = realloc(gif->frames, count * sizeof(*frame));
- if (temp == NULL) {
- return NULL;
+ for (uint32_t y = 0; y < height; y++) {
+ uint32_t x;
+ uint32_t decode_y;
+ uint32_t *frame_scanline;
+
+ if (interlace) {
+ decode_y = gif_interlaced_line(height, y) + offset_y;
+ } else {
+ decode_y = y + offset_y;
}
- gif->frames = temp;
- gif->frame_holders = count;
+ frame_scanline = frame_data + offset_x + (decode_y * gif->width);
- frame = &gif->frames[frame_idx];
+ x = width;
+ while (x > 0) {
+ const uint8_t *uncompressed;
+ unsigned row_available;
+ if (available == 0) {
+ if (res != LZW_OK) {
+ /* Unexpected end of frame, try to recover */
+ if (res == LZW_OK_EOD) {
+ ret = GIF_OK;
+ } else {
+ ret = gif_error_from_lzw(res);
+ }
+ break;
+ }
+ res = lzw_decode(gif->lzw_ctx,
+ &uncompressed, &available);
+ }
- frame->transparency = false;
- frame->transparency_index = GIF_NO_TRANSPARENCY;
- frame->frame_pointer = gif->buffer_position;
- frame->redraw_required = false;
- frame->disposal_method = 0;
- frame->frame_delay = 100;
- frame->display = false;
- frame->virgin = true;
+ row_available = x < available ? x : available;
+ x -= row_available;
+ available -= row_available;
+ if (transparency_index > 0xFF) {
+ while (row_available-- > 0) {
+ *frame_scanline++ =
+ colour_table[*uncompressed++];
+ }
+ } else {
+ while (row_available-- > 0) {
+ register uint32_t colour;
+ colour = *uncompressed++;
+ if (colour != transparency_index) {
+ *frame_scanline =
+ colour_table[colour];
+ }
+ frame_scanline++;
+ }
+ }
+ }
}
-
- return frame;
+ return ret;
}
-/**
- * Attempts to initialise the next frame
- *
- * \param[in] gif The animation context
- * \param[in] frame_idx The frame number to decode.
- * \return error code
- * - GIF_INSUFFICIENT_DATA for insufficient data to do anything
- * - GIF_FRAME_DATA_ERROR for GIF frame data error
- * - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
- * - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
- * - GIF_DATA_ERROR for GIF error (invalid frame header)
- * - GIF_OK for successful decoding
- * - GIF_WORKING for successful decoding if more frames are expected
-*/
-static gif_result gif_initialise_frame(gif_animation *gif,
- uint32_t frame_idx)
+static gif_result gif__decode_simple(
+ struct gif_animation *gif,
+ uint32_t height,
+ uint32_t offset_y,
+ uint8_t minimum_code_size,
+ uint32_t transparency_index,
+ uint32_t *restrict frame_data,
+ uint32_t *restrict colour_table)
{
- gif_result ret;
- struct gif_frame *frame;
- uint8_t *gif_data, *gif_end;
- int gif_bytes;
- uint32_t block_size;
-
- /* Get our buffer position etc. */
- gif_data = (uint8_t *)(gif->gif_data + gif->buffer_position);
- gif_end = (uint8_t *)(gif->gif_data + gif->buffer_size);
- gif_bytes = (gif_end - gif_data);
-
- /* Check if we've finished */
- if ((gif_bytes > 0) && (gif_data[0] == GIF_TRAILER)) {
- return GIF_OK;
- }
-
- /* Check if there is enough data remaining. The shortest block of data
- * is a 4-byte comment extension + 1-byte block terminator + 1-byte gif
- * trailer
- */
- if (gif_bytes < 6) {
- return GIF_INSUFFICIENT_DATA;
- }
-
- /* We could theoretically get some junk data that gives us millions of
- * frames, so we ensure that we don't have a silly number
- */
- if (frame_idx > 4096) {
- return GIF_FRAME_DATA_ERROR;
- }
-
- frame = gif__get_frame(gif, frame_idx);
- if (frame == NULL) {
- return GIF_INSUFFICIENT_MEMORY;
- }
-
- /* We pretend to initialise the frames, but really we just skip over
- * all the data contained within. This is all basically a cut down
- * version of gif_decode_frame that doesn't have any of the LZW bits in
- * it.
- */
-
- /* Initialise any extensions */
- gif->buffer_position = gif_data - gif->gif_data;
- ret = gif__parse_frame_extensions(gif, &gif->frames[frame_idx], true);
- if (ret != GIF_OK) {
- return ret;
- }
-
- ret = gif__parse_image_descriptor(gif, &gif->frames[frame_idx], true);
- if (ret != GIF_OK) {
- return ret;
- }
-
- ret = gif__parse_colour_table(gif, &gif->frames[frame_idx], false);
- if (ret != GIF_OK) {
- return ret;
- }
- gif_data = gif->gif_data + gif->buffer_position;
- gif_bytes = (gif_end - gif_data);
-
- /* Move our data onwards and remember we've got a bit of this frame */
- gif->frame_count_partial = frame_idx + 1;
+ uint32_t pixels = gif->width * height;
+ uint32_t written = 0;
+ gif_result ret = GIF_OK;
+ lzw_result res;
- /* Ensure we have a correct code size */
- if (gif_bytes < 1) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- if (gif_data[0] >= LZW_CODE_MAX) {
- return GIF_DATA_ERROR;
+ /* Initialise the LZW decoding */
+ res = lzw_decode_init_map(gif->lzw_ctx,
+ minimum_code_size, transparency_index, colour_table,
+ gif->gif_data, gif->buffer_size, gif->buffer_position);
+ if (res != LZW_OK) {
+ return gif_error_from_lzw(res);
}
- /* Move our pointer to the actual image data */
- gif_data++;
- --gif_bytes;
+ frame_data += (offset_y * gif->width);
- /* Repeatedly skip blocks until we get a zero block or run out of data
- * These blocks of image data are processed later by gif_decode_frame()
- */
- block_size = 0;
- while (block_size != 1) {
- if (gif_bytes < 1) return GIF_INSUFFICIENT_FRAME_DATA;
- block_size = gif_data[0] + 1;
- /* Check if the frame data runs off the end of the file */
- if ((int)(gif_bytes - block_size) < 0) {
- /* Try to recover by signaling the end of the gif.
- * Once we get garbage data, there is no logical way to
- * determine where the next frame is. It's probably
- * better to partially load the gif than not at all.
- */
- if (gif_bytes >= 2) {
- gif_data[0] = 0;
- gif_data[1] = GIF_TRAILER;
- gif_bytes = 1;
- ++gif_data;
- break;
+ while (pixels > 0) {
+ res = lzw_decode_map(gif->lzw_ctx,
+ frame_data, pixels, &written);
+ pixels -= written;
+ frame_data += written;
+ if (res != LZW_OK) {
+ /* Unexpected end of frame, try to recover */
+ if (res == LZW_OK_EOD) {
+ ret = GIF_OK;
} else {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ ret = gif_error_from_lzw(res);
}
- } else {
- gif_bytes -= block_size;
- gif_data += block_size;
- }
- }
-
- /* Add the frame and set the display flag */
- gif->buffer_position = gif_data - gif->gif_data;
- gif->frame_count = frame_idx + 1;
- gif->frames[frame_idx].display = true;
-
- /* Check if we've finished */
- if (gif_bytes < 1) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- } else {
- if (gif_data[0] == GIF_TRAILER) {
- return GIF_OK;
+ break;
}
}
- return GIF_WORKING;
-}
-static uint32_t gif_interlaced_line(int height, int y)
-{
- if ((y << 3) < height) {
- return (y << 3);
- }
- y -= ((height + 7) >> 3);
- if ((y << 3) < (height - 4)) {
- return (y << 3) + 4;
- }
- y -= ((height + 3) >> 3);
- if ((y << 2) < (height - 2)) {
- return (y << 2) + 2;
+ if (pixels == 0) {
+ ret = GIF_OK;
}
- y -= ((height + 1) >> 2);
- return (y << 1) + 1;
-}
-
-static gif_result gif_error_from_lzw(lzw_result l_res)
-{
- static const gif_result g_res[] = {
- [LZW_OK] = GIF_OK,
- [LZW_OK_EOD] = GIF_END_OF_FRAME,
- [LZW_NO_MEM] = GIF_INSUFFICIENT_MEMORY,
- [LZW_NO_DATA] = GIF_INSUFFICIENT_FRAME_DATA,
- [LZW_EOI_CODE] = GIF_FRAME_DATA_ERROR,
- [LZW_BAD_ICODE] = GIF_FRAME_DATA_ERROR,
- [LZW_BAD_CODE] = GIF_FRAME_DATA_ERROR,
- };
- assert(l_res != LZW_BAD_PARAM);
- assert(l_res != LZW_NO_COLOUR);
- return g_res[l_res];
+ return ret;
}
-/**
- * Helper to get the rendering bitmap for a gif.
- *
- * \param[in] gif The gif object we're decoding.
- * \return Client pixel buffer for rendering into.
- */
-static inline uint32_t* gif__bitmap_get(
- struct gif_animation *gif)
+static inline gif_result gif__decode(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ uint8_t minimum_code_size,
+ uint32_t *restrict frame_data)
{
gif_result ret;
+ uint32_t offset_x = frame->redraw_x;
+ uint32_t offset_y = frame->redraw_y;
+ uint32_t width = frame->redraw_width;
+ uint32_t height = frame->redraw_height;
+ uint32_t interlace = frame->flags & GIF_INTERLACE_MASK;
+ uint32_t transparency_index = frame->transparency_index;
+ uint32_t *restrict colour_table = gif->colour_table;
- /* Make sure we have a buffer to decode to. */
- ret = gif_initialise_sprite(gif, gif->width, gif->height);
- if (ret != GIF_OK) {
- return NULL;
- }
-
- /* Get the frame data */
- assert(gif->bitmap_callbacks.bitmap_get_buffer);
- return (uint32_t *)gif->bitmap_callbacks.bitmap_get_buffer(
- gif->frame_image);
-}
-
-/**
- * Helper to tell the client that their bitmap was modified.
- *
- * \param[in] gif The gif object we're decoding.
- */
-static inline void gif__bitmap_modified(
- const struct gif_animation *gif)
-{
- if (gif->bitmap_callbacks.bitmap_modified) {
- gif->bitmap_callbacks.bitmap_modified(gif->frame_image);
+ if (interlace == false && width == gif->width && offset_x == 0) {
+ ret = gif__decode_simple(gif, height, offset_y,
+ minimum_code_size, transparency_index,
+ frame_data, colour_table);
+ } else {
+ ret = gif__decode_complex(gif, width, height,
+ offset_x, offset_y, interlace,
+ minimum_code_size, transparency_index,
+ frame_data, colour_table);
}
-}
-/**
- * Helper to tell the client that whether the bitmap is opaque.
- *
- * \param[in] gif The gif object we're decoding.
- * \param[in] frmae The frame that has been decoded.
- */
-static inline void gif__bitmap_set_opaque(
- const struct gif_animation *gif,
- const struct gif_frame *frame)
-{
- if (gif->bitmap_callbacks.bitmap_set_opaque) {
- gif->bitmap_callbacks.bitmap_set_opaque(
- gif->frame_image, frame->opaque);
- }
+ return ret;
}
/**
- * Helper to get the client to determine if the bitmap is opaque.
- *
- * \todo: We don't really need to get the client to do this for us.
+ * Restore a GIF to the background colour.
*
- * \param[in] gif The gif object we're decoding.
- * \return true if the bitmap is opaque, false otherwise.
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to clear, or NULL.
+ * \param[in] bitmap The bitmap to clear the frame in.
*/
-static inline bool gif__bitmap_get_opaque(
- const struct gif_animation *gif)
+static void gif__restore_bg(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ uint32_t *bitmap)
{
- if (gif->bitmap_callbacks.bitmap_test_opaque) {
- return gif->bitmap_callbacks.bitmap_test_opaque(
- gif->frame_image);
- }
+ if (frame == NULL) {
+ memset(bitmap, GIF_TRANSPARENT_COLOUR,
+ gif->width * gif->height * sizeof(*bitmap));
+ } else {
+ uint32_t offset_x = frame->redraw_x;
+ uint32_t offset_y = frame->redraw_y;
+ uint32_t width = frame->redraw_width;
+ uint32_t height = frame->redraw_height;
- return false;
+ if (frame->display == false) {
+ return;
+ }
+
+ if (frame->transparency) {
+ for (uint32_t y = 0; y < height; y++) {
+ uint32_t *scanline = bitmap + offset_x +
+ (offset_y + y) * gif->width;
+ memset(scanline, GIF_TRANSPARENT_COLOUR,
+ width * sizeof(*bitmap));
+ }
+ } else {
+ for (uint32_t y = 0; y < height; y++) {
+ uint32_t *scanline = bitmap + offset_x +
+ (offset_y + y) * gif->width;
+ for (uint32_t x = 0; x < width; x++) {
+ scanline[x] = gif->bg_colour;
+ }
+ }
+ }
+ }
}
-static void gif__record_frame(
+static gif_result gif__update_bitmap(
struct gif_animation *gif,
- const uint32_t *bitmap)
+ struct gif_frame *frame,
+ uint8_t minimum_code_size,
+ uint32_t frame_idx)
{
- bool need_alloc = gif->prev_frame == NULL;
- uint32_t *prev_frame;
+ gif_result ret;
+ uint32_t *bitmap;
- if (gif->decoded_frame == GIF_INVALID_FRAME ||
- gif->decoded_frame == gif->prev_index) {
- /* No frame to copy, or already have this frame recorded. */
- return;
- }
+ gif->decoded_frame = frame_idx;
bitmap = gif__bitmap_get(gif);
if (bitmap == NULL) {
- return;
+ return GIF_INSUFFICIENT_MEMORY;
}
- if (gif->prev_frame != NULL &&
- gif->width * gif->height > gif->prev_width * gif->prev_height) {
- need_alloc = true;
- }
+ /* Handle any bitmap clearing/restoration required before decoding this
+ * frame. */
+ if (frame_idx == 0 || gif->decoded_frame == GIF_INVALID_FRAME) {
+ gif__restore_bg(gif, NULL, bitmap);
- if (need_alloc) {
- prev_frame = realloc(gif->prev_frame,
- gif->width * gif->height * 4);
- if (prev_frame == NULL) {
- return;
- }
} else {
- prev_frame = gif->prev_frame;
+ struct gif_frame *prev = &gif->frames[frame_idx - 1];
+
+ if (prev->disposal_method == GIF_DISPOSAL_RESTORE_BG) {
+ gif__restore_bg(gif, prev, bitmap);
+
+ } else if (prev->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
+ ret = gif__recover_frame(gif, bitmap);
+ if (ret != GIF_OK) {
+ gif__restore_bg(gif, prev, bitmap);
+ }
+ }
}
- memcpy(prev_frame, bitmap, gif->width * gif->height * 4);
+ if (frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
+ /* Store the previous frame for later restoration */
+ gif__record_frame(gif, bitmap);
+ }
- gif->prev_frame = prev_frame;
- gif->prev_width = gif->width;
- gif->prev_height = gif->height;
- gif->prev_index = gif->decoded_frame;
+ ret = gif__decode(gif, frame, minimum_code_size, bitmap);
+
+ gif__bitmap_modified(gif);
+
+ if (frame->virgin) {
+ frame->opaque = gif__bitmap_get_opaque(gif);
+ frame->virgin = false;
+ }
+ gif__bitmap_set_opaque(gif, frame);
+
+ return ret;
}
-static gif_result gif__recover_frame(
- const struct gif_animation *gif,
- uint32_t *bitmap)
+/**
+ * Parse the application extension
+ *
+ * \param[in] frame The gif object we're decoding.
+ * \param[in] data The data to decode.
+ * \param[in] len Byte length of data.
+ * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
+ * GIF_OK for success.
+ */
+static gif_result gif__parse_extension_graphic_control(
+ struct gif_frame *frame,
+ uint8_t *data,
+ size_t len)
{
- const uint32_t *prev_frame = gif->prev_frame;
- unsigned height = gif->height < gif->prev_height ? gif->height : gif->prev_height;
- unsigned width = gif->width < gif->prev_width ? gif->width : gif->prev_width;
+ /* 6-byte Graphic Control Extension is:
+ *
+ * +0 CHAR Graphic Control Label
+ * +1 CHAR Block Size
+ * +2 CHAR __Packed Fields__
+ * 3BITS Reserved
+ * 3BITS Disposal Method
+ * 1BIT User Input Flag
+ * 1BIT Transparent Color Flag
+ * +3 SHORT Delay Time
+ * +5 CHAR Transparent Color Index
+ */
+ if (len < 6) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
- if (prev_frame == NULL) {
- return GIF_FRAME_DATA_ERROR;
+ frame->frame_delay = data[3] | (data[4] << 8);
+ if (data[2] & GIF_TRANSPARENCY_MASK) {
+ frame->transparency = true;
+ frame->transparency_index = data[5];
}
- for (unsigned y = 0; y < height; y++) {
- memcpy(bitmap, prev_frame, width * 4);
+ frame->disposal_method = ((data[2] & GIF_DISPOSAL_MASK) >> 2);
+ /* I have encountered documentation and GIFs in the
+ * wild that use 0x04 to restore the previous frame,
+ * rather than the officially documented 0x03. I
+ * believe some (older?) software may even actually
+ * export this way. We handle this as a type of
+ * "quirks" mode. */
+ if (frame->disposal_method == GIF_DISPOSAL_RESTORE_QUIRK) {
+ frame->disposal_method = GIF_DISPOSAL_RESTORE_PREV;
+ }
- bitmap += gif->width;
- prev_frame += gif->prev_width;
+ /* if we are clearing the background then we need to
+ * redraw enough to cover the previous frame too. */
+ frame->redraw_required =
+ frame->disposal_method == GIF_DISPOSAL_RESTORE_BG ||
+ frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV;
+
+ return GIF_OK;
+}
+
+/**
+ * Parse the application extension
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] data The data to decode.
+ * \param[in] len Byte length of data.
+ * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
+ * GIF_OK for success.
+ */
+static gif_result gif__parse_extension_application(
+ struct gif_animation *gif,
+ uint8_t *data,
+ size_t len)
+{
+ /* 14-byte+ Application Extension is:
+ *
+ * +0 CHAR Application Extension Label
+ * +1 CHAR Block Size
+ * +2 8CHARS Application Identifier
+ * +10 3CHARS Appl. Authentication Code
+ * +13 1-256 Application Data (Data sub-blocks)
+ */
+ if (len < 17) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
+
+ if ((data[1] == 0x0b) &&
+ (strncmp((const char *)data + 2, "NETSCAPE2.0", 11) == 0) &&
+ (data[13] == 0x03) && (data[14] == 0x01)) {
+ gif->loop_count = data[15] | (data[16] << 8);
}
return GIF_OK;
}
-static gif_result
-gif__decode_complex(
+/**
+ * Parse the frame's extensions
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to parse extensions for.
+ * \param[in] decode Whether to decode or skip over the extension.
+ * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
+ * GIF_OK for success.
+ */
+static gif_result gif__parse_frame_extensions(
struct gif_animation *gif,
- uint32_t width,
- uint32_t height,
- uint32_t offset_x,
- uint32_t offset_y,
- uint32_t interlace,
- uint8_t minimum_code_size,
- uint32_t transparency_index,
- uint32_t *restrict frame_data,
- uint32_t *restrict colour_table)
+ struct gif_frame *frame,
+ bool decode)
{
- uint32_t available = 0;
- gif_result ret = GIF_OK;
- lzw_result res;
+ uint8_t *gif_data, *gif_end;
+ int gif_bytes;
+
+ /* Get our buffer position etc. */
+ gif_data = gif->gif_data + gif->buffer_position;
+ gif_end = gif->gif_data + gif->buffer_size;
+ gif_bytes = gif_end - gif_data;
- /* Initialise the LZW decoding */
- res = lzw_decode_init(gif->lzw_ctx, minimum_code_size,
- gif->gif_data, gif->buffer_size, gif->buffer_position);
- if (res != LZW_OK) {
- return gif_error_from_lzw(res);
- }
+ /* Initialise the extensions */
+ while (gif_bytes > 0 && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
+ bool block_step = true;
+ gif_result ret;
- for (uint32_t y = 0; y < height; y++) {
- uint32_t x;
- uint32_t decode_y;
- uint32_t *frame_scanline;
+ gif_data++;
+ gif_bytes--;
- if (interlace) {
- decode_y = gif_interlaced_line(height, y) + offset_y;
- } else {
- decode_y = y + offset_y;
+ if (gif_bytes == 0) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
}
- frame_scanline = frame_data + offset_x + (decode_y * gif->width);
- x = width;
- while (x > 0) {
- const uint8_t *uncompressed;
- unsigned row_available;
- if (available == 0) {
- if (res != LZW_OK) {
- /* Unexpected end of frame, try to recover */
- if (res == LZW_OK_EOD) {
- ret = GIF_OK;
- } else {
- ret = gif_error_from_lzw(res);
- }
- break;
+ /* Switch on extension label */
+ switch (gif_data[0]) {
+ case GIF_EXTENSION_GRAPHIC_CONTROL:
+ if (decode) {
+ ret = gif__parse_extension_graphic_control(
+ frame, gif_data, gif_bytes);
+ if (ret != GIF_OK) {
+ return ret;
}
- res = lzw_decode(gif->lzw_ctx,
- &uncompressed, &available);
}
+ break;
- row_available = x < available ? x : available;
- x -= row_available;
- available -= row_available;
- if (transparency_index > 0xFF) {
- while (row_available-- > 0) {
- *frame_scanline++ =
- colour_table[*uncompressed++];
- }
- } else {
- while (row_available-- > 0) {
- register uint32_t colour;
- colour = *uncompressed++;
- if (colour != transparency_index) {
- *frame_scanline =
- colour_table[colour];
- }
- frame_scanline++;
+ case GIF_EXTENSION_APPLICATION:
+ if (decode) {
+ ret = gif__parse_extension_application(
+ gif, gif_data, gif_bytes);
+ if (ret != GIF_OK) {
+ return ret;
}
}
- }
- }
- return ret;
-}
-
-static gif_result
-gif__decode_simple(
- struct gif_animation *gif,
- uint32_t height,
- uint32_t offset_y,
- uint8_t minimum_code_size,
- uint32_t transparency_index,
- uint32_t *restrict frame_data,
- uint32_t *restrict colour_table)
-{
- uint32_t pixels = gif->width * height;
- uint32_t written = 0;
- gif_result ret = GIF_OK;
- lzw_result res;
+ break;
- /* Initialise the LZW decoding */
- res = lzw_decode_init_map(gif->lzw_ctx,
- minimum_code_size, transparency_index, colour_table,
- gif->gif_data, gif->buffer_size, gif->buffer_position);
- if (res != LZW_OK) {
- return gif_error_from_lzw(res);
- }
+ case GIF_EXTENSION_COMMENT:
+ /* Move the pointer to the first data sub-block Skip 1
+ * byte for the extension label. */
+ ++gif_data;
+ block_step = false;
+ break;
- frame_data += (offset_y * gif->width);
+ default:
+ break;
+ }
- while (pixels > 0) {
- res = lzw_decode_map(gif->lzw_ctx,
- frame_data, pixels, &written);
- pixels -= written;
- frame_data += written;
- if (res != LZW_OK) {
- /* Unexpected end of frame, try to recover */
- if (res == LZW_OK_EOD) {
- ret = GIF_OK;
- } else {
- ret = gif_error_from_lzw(res);
+ if (block_step) {
+ /* Move the pointer to the first data sub-block Skip 2
+ * bytes for the extension label and size fields Skip
+ * the extension size itself
+ */
+ if (gif_bytes < 2) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
}
- break;
+ gif_data += 2 + gif_data[1];
}
- }
- if (pixels == 0) {
- ret = GIF_OK;
+ /* Repeatedly skip blocks until we get a zero block or run out
+ * of data. This data is ignored by this gif decoder. */
+ while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) {
+ gif_data += gif_data[0] + 1;
+ if (gif_data >= gif_end) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
+ }
+ gif_data++;
+ gif_bytes = gif_end - gif_data;
}
- return ret;
-}
-
-static inline gif_result gif__decode(
- struct gif_animation *gif,
- struct gif_frame *frame,
- uint8_t minimum_code_size,
- uint32_t *restrict frame_data)
-{
- gif_result ret;
- uint32_t offset_x = frame->redraw_x;
- uint32_t offset_y = frame->redraw_y;
- uint32_t width = frame->redraw_width;
- uint32_t height = frame->redraw_height;
- uint32_t interlace = frame->flags & GIF_INTERLACE_MASK;
- uint32_t transparency_index = frame->transparency_index;
- uint32_t *restrict colour_table = gif->colour_table;
-
- if (interlace == false && width == gif->width && offset_x == 0) {
- ret = gif__decode_simple(gif, height, offset_y,
- minimum_code_size, transparency_index,
- frame_data, colour_table);
- } else {
- ret = gif__decode_complex(gif, width, height,
- offset_x, offset_y, interlace,
- minimum_code_size, transparency_index,
- frame_data, colour_table);
+ if (gif_data > gif_end) {
+ gif_data = gif_end;
}
- return ret;
+ /* Set buffer position and return */
+ gif->buffer_position = gif_data - gif->gif_data;
+ return GIF_OK;
}
/**
- * Restore a GIF to the background colour.
+ * Parse a GIF Image Descriptor.
*
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to clear, or NULL.
- * \param[in] bitmap The bitmap to clear the frame in.
+ * The format is:
+ *
+ * +0 CHAR Image Separator (0x2c)
+ * +1 SHORT Image Left Position
+ * +3 SHORT Image Top Position
+ * +5 SHORT Width
+ * +7 SHORT Height
+ * +9 CHAR __Packed Fields__
+ * 1BIT Local Colour Table Flag
+ * 1BIT Interlace Flag
+ * 1BIT Sort Flag
+ * 2BITS Reserved
+ * 3BITS Size of Local Colour Table
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to parse an image descriptor for.
+ * \return GIF_OK on success, appropriate error otherwise.
*/
-static void gif__restore_bg(
+static gif_result gif__parse_image_descriptor(
struct gif_animation *gif,
struct gif_frame *frame,
- uint32_t *bitmap)
+ bool decode)
{
- if (frame == NULL) {
- memset(bitmap, GIF_TRANSPARENT_COLOUR,
- gif->width * gif->height * sizeof(*bitmap));
- } else {
- uint32_t offset_x = frame->redraw_x;
- uint32_t offset_y = frame->redraw_y;
- uint32_t width = frame->redraw_width;
- uint32_t height = frame->redraw_height;
+ const uint8_t *data = gif->gif_data + gif->buffer_position;
+ size_t len = gif->buffer_size - gif->buffer_position;
+ enum {
+ GIF_IMAGE_DESCRIPTOR_LEN = 10u,
+ GIF_IMAGE_SEPARATOR = 0x2Cu,
+ };
- if (frame->display == false) {
- return;
- }
+ assert(gif != NULL);
+ assert(frame != NULL);
- if (frame->transparency) {
- for (uint32_t y = 0; y < height; y++) {
- uint32_t *scanline = bitmap + offset_x +
- (offset_y + y) * gif->width;
- memset(scanline, GIF_TRANSPARENT_COLOUR,
- width * sizeof(*bitmap));
- }
- } else {
- for (uint32_t y = 0; y < height; y++) {
- uint32_t *scanline = bitmap + offset_x +
- (offset_y + y) * gif->width;
- for (uint32_t x = 0; x < width; x++) {
- scanline[x] = gif->bg_colour;
- }
- }
+ if (len < GIF_IMAGE_DESCRIPTOR_LEN) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
+
+ if (decode) {
+ unsigned x, y, w, h;
+
+ if (data[0] != GIF_IMAGE_SEPARATOR) {
+ return GIF_FRAME_DATA_ERROR;
}
+
+ x = data[1] | (data[2] << 8);
+ y = data[3] | (data[4] << 8);
+ w = data[5] | (data[6] << 8);
+ h = data[7] | (data[8] << 8);
+ frame->flags = data[9];
+
+ frame->redraw_x = x;
+ frame->redraw_y = y;
+ frame->redraw_width = w;
+ frame->redraw_height = h;
+
+ /* Frame size may have grown. */
+ gif->width = (x + w > gif->width ) ? x + w : gif->width;
+ gif->height = (y + h > gif->height) ? y + h : gif->height;
}
+
+ gif->buffer_position += GIF_IMAGE_DESCRIPTOR_LEN;
+ return GIF_OK;
}
-static gif_result gif__update_bitmap(
+/**
+ * Get a frame's colour table.
+ *
+ * Sets up gif->colour_table for the frame.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to get the colour table for.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_colour_table(
struct gif_animation *gif,
struct gif_frame *frame,
- uint8_t minimum_code_size,
- uint32_t frame_idx)
+ bool decode)
{
- gif_result ret;
- uint32_t *bitmap;
-
- gif->decoded_frame = frame_idx;
-
- bitmap = gif__bitmap_get(gif);
- if (bitmap == NULL) {
- return GIF_INSUFFICIENT_MEMORY;
- }
-
- /* Handle any bitmap clearing/restoration required before decoding this
- * frame. */
- if (frame_idx == 0 || gif->decoded_frame == GIF_INVALID_FRAME) {
- gif__restore_bg(gif, NULL, bitmap);
-
- } else {
- struct gif_frame *prev = &gif->frames[frame_idx - 1];
+ const uint8_t *data = gif->gif_data + gif->buffer_position;
+ size_t len = gif->buffer_size - gif->buffer_position;
+ unsigned colour_table_size;
- if (prev->disposal_method == GIF_DISPOSAL_RESTORE_BG) {
- gif__restore_bg(gif, prev, bitmap);
+ assert(gif != NULL);
+ assert(frame != NULL);
- } else if (prev->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
- ret = gif__recover_frame(gif, bitmap);
- if (ret != GIF_OK) {
- gif__restore_bg(gif, prev, bitmap);
- }
- }
+ if ((frame->flags & GIF_COLOUR_TABLE_MASK) == 0) {
+ gif->colour_table = gif->global_colour_table;
+ return GIF_OK;
}
- if (frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
- /* Store the previous frame for later restoration */
- gif__record_frame(gif, bitmap);
+ colour_table_size = 2 << (frame->flags & GIF_COLOUR_TABLE_SIZE_MASK);
+ if (len < colour_table_size * 3) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
}
- ret = gif__decode(gif, frame, minimum_code_size, bitmap);
+ if (decode) {
+ int count = colour_table_size;
+ uint8_t *entry = (uint8_t *)gif->local_colour_table;
- gif__bitmap_modified(gif);
+ while (count--) {
+ /* Gif colour map contents are r,g,b.
+ *
+ * We want to pack them bytewise into the
+ * colour table, such that the red component
+ * is in byte 0 and the alpha component is in
+ * byte 3.
+ */
- if (frame->virgin) {
- frame->opaque = gif__bitmap_get_opaque(gif);
- frame->virgin = false;
+ *entry++ = *data++; /* r */
+ *entry++ = *data++; /* g */
+ *entry++ = *data++; /* b */
+ *entry++ = 0xff; /* a */
+ }
}
- gif__bitmap_set_opaque(gif, frame);
- return ret;
+ gif->buffer_position += colour_table_size * 3;
+ gif->colour_table = gif->local_colour_table;
+ return GIF_OK;
}
/**
@@ -1102,6 +925,180 @@ static gif_result gif__parse_image_data(
return ret;
}
+static struct gif_frame *gif__get_frame(
+ struct gif_animation *gif,
+ uint32_t frame_idx)
+{
+ struct gif_frame *frame;
+
+ if (gif->frame_holders > frame_idx) {
+ frame = &gif->frames[frame_idx];
+ } else {
+ /* Allocate more memory */
+ size_t count = frame_idx + 1;
+ struct gif_frame *temp;
+
+ temp = realloc(gif->frames, count * sizeof(*frame));
+ if (temp == NULL) {
+ return NULL;
+ }
+ gif->frames = temp;
+ gif->frame_holders = count;
+
+ frame = &gif->frames[frame_idx];
+
+ frame->transparency = false;
+ frame->transparency_index = GIF_NO_TRANSPARENCY;
+ frame->frame_pointer = gif->buffer_position;
+ frame->redraw_required = false;
+ frame->disposal_method = 0;
+ frame->frame_delay = 100;
+ frame->display = false;
+ frame->virgin = true;
+ }
+
+ return frame;
+}
+
+/**
+ * Attempts to initialise the next frame
+ *
+ * \param[in] gif The animation context
+ * \param[in] frame_idx The frame number to decode.
+ * \return error code
+ * - GIF_INSUFFICIENT_DATA for insufficient data to do anything
+ * - GIF_FRAME_DATA_ERROR for GIF frame data error
+ * - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
+ * - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
+ * - GIF_DATA_ERROR for GIF error (invalid frame header)
+ * - GIF_OK for successful decoding
+ * - GIF_WORKING for successful decoding if more frames are expected
+*/
+static gif_result gif_initialise_frame(
+ struct gif_animation *gif,
+ uint32_t frame_idx)
+{
+ gif_result ret;
+ struct gif_frame *frame;
+ uint8_t *gif_data, *gif_end;
+ int gif_bytes;
+ uint32_t block_size;
+
+ /* Get our buffer position etc. */
+ gif_data = (uint8_t *)(gif->gif_data + gif->buffer_position);
+ gif_end = (uint8_t *)(gif->gif_data + gif->buffer_size);
+ gif_bytes = (gif_end - gif_data);
+
+ /* Check if we've finished */
+ if ((gif_bytes > 0) && (gif_data[0] == GIF_TRAILER)) {
+ return GIF_OK;
+ }
+
+ /* Check if there is enough data remaining. The shortest block of data
+ * is a 4-byte comment extension + 1-byte block terminator + 1-byte gif
+ * trailer
+ */
+ if (gif_bytes < 6) {
+ return GIF_INSUFFICIENT_DATA;
+ }
+
+ /* We could theoretically get some junk data that gives us millions of
+ * frames, so we ensure that we don't have a silly number
+ */
+ if (frame_idx > 4096) {
+ return GIF_FRAME_DATA_ERROR;
+ }
+
+ frame = gif__get_frame(gif, frame_idx);
+ if (frame == NULL) {
+ return GIF_INSUFFICIENT_MEMORY;
+ }
+
+ /* We pretend to initialise the frames, but really we just skip over
+ * all the data contained within. This is all basically a cut down
+ * version of gif_decode_frame that doesn't have any of the LZW bits in
+ * it.
+ */
+
+ /* Initialise any extensions */
+ gif->buffer_position = gif_data - gif->gif_data;
+ ret = gif__parse_frame_extensions(gif, &gif->frames[frame_idx], true);
+ if (ret != GIF_OK) {
+ return ret;
+ }
+
+ ret = gif__parse_image_descriptor(gif, &gif->frames[frame_idx], true);
+ if (ret != GIF_OK) {
+ return ret;
+ }
+
+ ret = gif__parse_colour_table(gif, &gif->frames[frame_idx], false);
+ if (ret != GIF_OK) {
+ return ret;
+ }
+ gif_data = gif->gif_data + gif->buffer_position;
+ gif_bytes = (gif_end - gif_data);
+
+ /* Move our data onwards and remember we've got a bit of this frame */
+ gif->frame_count_partial = frame_idx + 1;
+
+ /* Ensure we have a correct code size */
+ if (gif_bytes < 1) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
+ if (gif_data[0] >= LZW_CODE_MAX) {
+ return GIF_DATA_ERROR;
+ }
+
+ /* Move our pointer to the actual image data */
+ gif_data++;
+ --gif_bytes;
+
+ /* Repeatedly skip blocks until we get a zero block or run out of data
+ * These blocks of image data are processed later by gif_decode_frame()
+ */
+ block_size = 0;
+ while (block_size != 1) {
+ if (gif_bytes < 1) return GIF_INSUFFICIENT_FRAME_DATA;
+ block_size = gif_data[0] + 1;
+ /* Check if the frame data runs off the end of the file */
+ if ((int)(gif_bytes - block_size) < 0) {
+ /* Try to recover by signaling the end of the gif.
+ * Once we get garbage data, there is no logical way to
+ * determine where the next frame is. It's probably
+ * better to partially load the gif than not at all.
+ */
+ if (gif_bytes >= 2) {
+ gif_data[0] = 0;
+ gif_data[1] = GIF_TRAILER;
+ gif_bytes = 1;
+ ++gif_data;
+ break;
+ } else {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
+ } else {
+ gif_bytes -= block_size;
+ gif_data += block_size;
+ }
+ }
+
+ /* Add the frame and set the display flag */
+ gif->buffer_position = gif_data - gif->gif_data;
+ gif->frame_count = frame_idx + 1;
+ gif->frames[frame_idx].display = true;
+
+ /* Check if we've finished */
+ if (gif_bytes < 1) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ } else {
+ if (gif_data[0] == GIF_TRAILER) {
+ return GIF_OK;
+ }
+ }
+ return GIF_WORKING;
+}
+
/**
* decode a gif frame
*
@@ -1109,9 +1106,9 @@ static gif_result gif__parse_image_data(
* \param frame The frame number to decode.
* \param clear_image flag for image data being cleared instead of plotted.
*/
-static gif_result
-gif_internal_decode_frame(gif_animation *gif,
- uint32_t frame_idx)
+static gif_result gif_internal_decode_frame(
+ struct gif_animation *gif,
+ uint32_t frame_idx)
{
gif_result ret;
uint8_t *gif_data;
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=677ba295b37cc1671f...
commit 677ba295b37cc1671f40383f92ad133fe6db26d9
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Split out image parsing.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index b527953..b95b912 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -960,6 +960,8 @@ static gif_result gif__update_bitmap(
gif_result ret;
uint32_t *bitmap;
+ gif->decoded_frame = frame_idx;
+
bitmap = gif__bitmap_get(gif);
if (bitmap == NULL) {
return GIF_INSUFFICIENT_MEMORY;
@@ -1003,6 +1005,104 @@ static gif_result gif__update_bitmap(
}
/**
+ * Parse the image data for a gif frame.
+ *
+ * Sets up gif->colour_table for the frame.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to get the colour table for.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_image_data(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ bool decode)
+{
+ uint8_t *data = gif->gif_data + gif->buffer_position;
+ size_t len = gif->buffer_size - gif->buffer_position;
+ uint32_t frame_idx = frame - gif->frames;
+ uint8_t minimum_code_size;
+ gif_result ret;
+
+ assert(gif != NULL);
+ assert(frame != NULL);
+
+ if (!decode) {
+ gif->frame_count_partial = frame_idx + 1;
+ }
+
+ /* Ensure sufficient data remains. A gif trailer or a minimum lzw code
+ * followed by a gif trailer is treated as OK, although without any
+ * image data. */
+ switch (len) {
+ default: if (data[0] == GIF_TRAILER) return GIF_OK;
+ break;
+ case 2: if (data[1] == GIF_TRAILER) return GIF_OK;
+ /* Fall through. */
+ case 1: if (data[0] == GIF_TRAILER) return GIF_OK;
+ /* Fall through. */
+ case 0: return GIF_INSUFFICIENT_FRAME_DATA;
+ }
+
+ minimum_code_size = data[0];
+ if (minimum_code_size >= LZW_CODE_MAX) {
+ return GIF_DATA_ERROR;
+ }
+ gif->buffer_position++;
+ data++;
+ len--;
+
+ if (decode) {
+ ret = gif__update_bitmap(gif, frame, minimum_code_size,
+ frame_idx);
+ } else {
+ uint32_t block_size = 0;
+
+ while (block_size != 1) {
+ if (len < 1) return GIF_INSUFFICIENT_FRAME_DATA;
+ block_size = data[0] + 1;
+ /* Check if the frame data runs off the end of the file */
+ if ((int)(len - block_size) < 0) {
+ /* Try to recover by signaling the end of the gif.
+ * Once we get garbage data, there is no logical way to
+ * determine where the next frame is. It's probably
+ * better to partially load the gif than not at all.
+ */
+ if (len >= 2) {
+ data[0] = 0;
+ data[1] = GIF_TRAILER;
+ len = 1;
+ data++;
+ break;
+ } else {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
+ } else {
+ len -= block_size;
+ data += block_size;
+ }
+ }
+
+ gif->buffer_position = data - gif->gif_data;
+ gif->frame_count = frame_idx + 1;
+ gif->frames[frame_idx].display = true;
+
+ /* Check if we've finished */
+ if (len < 1) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ } else {
+ if (data[0] == GIF_TRAILER) {
+ return GIF_OK;
+ }
+ }
+
+ return GIF_WORKING;
+ }
+
+ return ret;
+}
+
+/**
* decode a gif frame
*
* \param gif gif animation context.
@@ -1014,9 +1114,8 @@ gif_internal_decode_frame(gif_animation *gif,
uint32_t frame_idx)
{
gif_result ret;
+ uint8_t *gif_data;
struct gif_frame *frame;
- uint8_t *gif_data, *gif_end;
- int gif_bytes;
uint32_t save_buffer_position;
/* Ensure the frame is in range to decode */
@@ -1041,8 +1140,6 @@ gif_internal_decode_frame(gif_animation *gif,
/* Get the start of our frame data and the end of the GIF data */
gif_data = gif->gif_data + frame->frame_pointer;
- gif_end = gif->gif_data + gif->buffer_size;
- gif_bytes = (gif_end - gif_data);
/* Save the buffer position */
save_buffer_position = gif->buffer_position;
@@ -1061,44 +1158,14 @@ gif_internal_decode_frame(gif_animation *gif,
ret = gif__parse_colour_table(gif, frame, true);
if (ret != GIF_OK) {
- return ret;
- }
- gif_data = gif->gif_data + gif->buffer_position;
- gif_bytes = (gif_end - gif_data);
-
- /* Ensure sufficient data remains */
- if (gif_bytes < 1) {
- ret = GIF_INSUFFICIENT_FRAME_DATA;
- goto gif_decode_frame_exit;
- }
-
- /* check for an end marker */
- if (gif_data[0] == GIF_TRAILER) {
- ret = GIF_OK;
- goto gif_decode_frame_exit;
- }
-
- /* Ensure we have enough data for a 1-byte LZW code size +
- * 1-byte gif trailer
- */
- if (gif_bytes < 2) {
- ret = GIF_INSUFFICIENT_FRAME_DATA;
goto gif_decode_frame_exit;
}
- /* If we only have a 1-byte LZW code size + 1-byte gif trailer,
- * we're finished
- */
- if ((gif_bytes == 2) && (gif_data[1] == GIF_TRAILER)) {
- ret = GIF_OK;
+ ret = gif__parse_image_data(gif, frame, true);
+ if (ret != GIF_OK) {
goto gif_decode_frame_exit;
}
- gif->decoded_frame = frame_idx;
- gif->buffer_position = (gif_data - gif->gif_data) + 1;
-
- ret = gif__update_bitmap(gif, frame, gif_data[0], frame_idx);
-
gif_decode_frame_exit:
/* Restore the buffer position */
-----------------------------------------------------------------------
Summary of changes:
src/libnsgif.c | 217 ++++++++++++++++++++++----------------------------------
1 file changed, 86 insertions(+), 131 deletions(-)
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 1280798..2a1fdfe 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -271,7 +271,7 @@ static gif_result gif__decode_complex(
uint32_t offset_x,
uint32_t offset_y,
uint32_t interlace,
- uint8_t minimum_code_size,
+ const uint8_t *data,
uint32_t transparency_index,
uint32_t *restrict frame_data,
uint32_t *restrict colour_table)
@@ -281,8 +281,9 @@ static gif_result gif__decode_complex(
lzw_result res;
/* Initialise the LZW decoding */
- res = lzw_decode_init(gif->lzw_ctx, minimum_code_size,
- gif->gif_data, gif->buffer_size, gif->buffer_position);
+ res = lzw_decode_init(gif->lzw_ctx, data[0],
+ gif->gif_data, gif->buffer_size,
+ data + 1 - gif->gif_data);
if (res != LZW_OK) {
return gif_error_from_lzw(res);
}
@@ -345,7 +346,7 @@ static gif_result gif__decode_simple(
struct gif_animation *gif,
uint32_t height,
uint32_t offset_y,
- uint8_t minimum_code_size,
+ const uint8_t *data,
uint32_t transparency_index,
uint32_t *restrict frame_data,
uint32_t *restrict colour_table)
@@ -356,9 +357,10 @@ static gif_result gif__decode_simple(
lzw_result res;
/* Initialise the LZW decoding */
- res = lzw_decode_init_map(gif->lzw_ctx,
- minimum_code_size, transparency_index, colour_table,
- gif->gif_data, gif->buffer_size, gif->buffer_position);
+ res = lzw_decode_init_map(gif->lzw_ctx, data[0],
+ transparency_index, colour_table,
+ gif->gif_data, gif->buffer_size,
+ data + 1 - gif->gif_data);
if (res != LZW_OK) {
return gif_error_from_lzw(res);
}
@@ -391,7 +393,7 @@ static gif_result gif__decode_simple(
static inline gif_result gif__decode(
struct gif_animation *gif,
struct gif_frame *frame,
- uint8_t minimum_code_size,
+ const uint8_t *data,
uint32_t *restrict frame_data)
{
gif_result ret;
@@ -405,12 +407,12 @@ static inline gif_result gif__decode(
if (interlace == false && width == gif->width && offset_x == 0) {
ret = gif__decode_simple(gif, height, offset_y,
- minimum_code_size, transparency_index,
+ data, transparency_index,
frame_data, colour_table);
} else {
ret = gif__decode_complex(gif, width, height,
offset_x, offset_y, interlace,
- minimum_code_size, transparency_index,
+ data, transparency_index,
frame_data, colour_table);
}
@@ -464,7 +466,7 @@ static void gif__restore_bg(
static gif_result gif__update_bitmap(
struct gif_animation *gif,
struct gif_frame *frame,
- uint8_t minimum_code_size,
+ const uint8_t *data,
uint32_t frame_idx)
{
gif_result ret;
@@ -501,7 +503,7 @@ static gif_result gif__update_bitmap(
gif__record_frame(gif, bitmap);
}
- ret = gif__decode(gif, frame, minimum_code_size, bitmap);
+ ret = gif__decode(gif, frame, data, bitmap);
gif__bitmap_modified(gif);
@@ -617,15 +619,12 @@ static gif_result gif__parse_extension_application(
static gif_result gif__parse_frame_extensions(
struct gif_animation *gif,
struct gif_frame *frame,
+ uint8_t **pos,
bool decode)
{
- uint8_t *gif_data, *gif_end;
- int gif_bytes;
-
- /* Get our buffer position etc. */
- gif_data = gif->gif_data + gif->buffer_position;
- gif_end = gif->gif_data + gif->buffer_size;
- gif_bytes = gif_end - gif_data;
+ uint8_t *gif_data = *pos;
+ uint8_t *gif_end = gif->gif_data + gif->buffer_size;
+ int gif_bytes = gif_end - gif_data;
/* Initialise the extensions */
while (gif_bytes > 0 && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
@@ -700,7 +699,7 @@ static gif_result gif__parse_frame_extensions(
}
/* Set buffer position and return */
- gif->buffer_position = gif_data - gif->gif_data;
+ *pos = gif_data;
return GIF_OK;
}
@@ -728,10 +727,11 @@ static gif_result gif__parse_frame_extensions(
static gif_result gif__parse_image_descriptor(
struct gif_animation *gif,
struct gif_frame *frame,
+ uint8_t **pos,
bool decode)
{
- const uint8_t *data = gif->gif_data + gif->buffer_position;
- size_t len = gif->buffer_size - gif->buffer_position;
+ const uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
enum {
GIF_IMAGE_DESCRIPTOR_LEN = 10u,
GIF_IMAGE_SEPARATOR = 0x2Cu,
@@ -767,7 +767,7 @@ static gif_result gif__parse_image_descriptor(
gif->height = (y + h > gif->height) ? y + h : gif->height;
}
- gif->buffer_position += GIF_IMAGE_DESCRIPTOR_LEN;
+ *pos += GIF_IMAGE_DESCRIPTOR_LEN;
return GIF_OK;
}
@@ -783,11 +783,12 @@ static gif_result gif__parse_image_descriptor(
static gif_result gif__parse_colour_table(
struct gif_animation *gif,
struct gif_frame *frame,
+ uint8_t **pos,
bool decode)
{
- const uint8_t *data = gif->gif_data + gif->buffer_position;
- size_t len = gif->buffer_size - gif->buffer_position;
unsigned colour_table_size;
+ const uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
assert(gif != NULL);
assert(frame != NULL);
@@ -822,8 +823,8 @@ static gif_result gif__parse_colour_table(
}
}
- gif->buffer_position += colour_table_size * 3;
gif->colour_table = gif->local_colour_table;
+ *pos += colour_table_size * 3;
return GIF_OK;
}
@@ -839,10 +840,11 @@ static gif_result gif__parse_colour_table(
static gif_result gif__parse_image_data(
struct gif_animation *gif,
struct gif_frame *frame,
+ uint8_t **pos,
bool decode)
{
- uint8_t *data = gif->gif_data + gif->buffer_position;
- size_t len = gif->buffer_size - gif->buffer_position;
+ uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
uint32_t frame_idx = frame - gif->frames;
uint8_t minimum_code_size;
gif_result ret;
@@ -858,6 +860,8 @@ static gif_result gif__parse_image_data(
* followed by a gif trailer is treated as OK, although without any
* image data. */
switch (len) {
+ default: if (data[0] == GIF_TRAILER) return GIF_OK;
+ break;
case 2: if (data[1] == GIF_TRAILER) return GIF_OK;
/* Fall through. */
case 1: if (data[0] == GIF_TRAILER) return GIF_OK;
@@ -869,16 +873,16 @@ static gif_result gif__parse_image_data(
if (minimum_code_size >= LZW_CODE_MAX) {
return GIF_DATA_ERROR;
}
- gif->buffer_position++;
- data++;
- len--;
if (decode) {
- ret = gif__update_bitmap(gif, frame, minimum_code_size,
- frame_idx);
+ ret = gif__update_bitmap(gif, frame, data, frame_idx);
} else {
uint32_t block_size = 0;
+ /* Skip the minimum code size. */
+ data++;
+ len--;
+
while (block_size != 1) {
if (len < 1) return GIF_INSUFFICIENT_FRAME_DATA;
block_size = data[0] + 1;
@@ -904,9 +908,9 @@ static gif_result gif__parse_image_data(
}
}
- gif->buffer_position = data - gif->gif_data;
gif->frame_count = frame_idx + 1;
gif->frames[frame_idx].display = true;
+ *pos = data;
/* Check if we've finished */
if (len < 1) {
@@ -963,6 +967,7 @@ static struct gif_frame *gif__get_frame(
*
* \param[in] gif The animation context
* \param[in] frame_idx The frame number to decode.
+ * \param[in] decode Whether to decode the graphical image data.
* \return error code
* - GIF_INSUFFICIENT_DATA for insufficient data to do anything
* - GIF_FRAME_DATA_ERROR for GIF frame data error
@@ -972,135 +977,85 @@ static struct gif_frame *gif__get_frame(
* - GIF_OK for successful decoding
* - GIF_WORKING for successful decoding if more frames are expected
*/
-static gif_result gif_initialise_frame(
+static gif_result gif__process_frame(
struct gif_animation *gif,
- uint32_t frame_idx)
+ uint32_t frame_idx,
+ bool decode)
{
+ uint8_t *pos;
+ uint8_t *end;
gif_result ret;
struct gif_frame *frame;
- uint8_t *gif_data, *gif_end;
- int gif_bytes;
-
- /* Get our buffer position etc. */
- gif_data = (uint8_t *)(gif->gif_data + gif->buffer_position);
- gif_end = (uint8_t *)(gif->gif_data + gif->buffer_size);
- gif_bytes = (gif_end - gif_data);
-
- /* Check if we've finished */
- if ((gif_bytes > 0) && (gif_data[0] == GIF_TRAILER)) {
- return GIF_OK;
- }
-
- /* We could theoretically get some junk data that gives us millions of
- * frames, so we ensure that we don't have a silly number
- */
- if (frame_idx > 4096) {
- return GIF_FRAME_DATA_ERROR;
- }
frame = gif__get_frame(gif, frame_idx);
if (frame == NULL) {
return GIF_INSUFFICIENT_MEMORY;
}
- /* Initialise any extensions */
- gif->buffer_position = gif_data - gif->gif_data;
- ret = gif__parse_frame_extensions(gif, &gif->frames[frame_idx], true);
- if (ret != GIF_OK) {
- return ret;
- }
-
- ret = gif__parse_image_descriptor(gif, &gif->frames[frame_idx], true);
- if (ret != GIF_OK) {
- return ret;
- }
-
- ret = gif__parse_colour_table(gif, &gif->frames[frame_idx], false);
- if (ret != GIF_OK) {
- return ret;
- }
+ end = (uint8_t *)(gif->gif_data + gif->buffer_size);
- ret = gif__parse_image_data(gif, &gif->frames[frame_idx], false);
- if (ret != GIF_OK) {
- return ret;
- }
-
- return GIF_OK;
-}
+ if (decode) {
+ pos = gif->gif_data + frame->frame_pointer;
-/**
- * decode a gif frame
- *
- * \param gif gif animation context.
- * \param frame The frame number to decode.
- * \param clear_image flag for image data being cleared instead of plotted.
- */
-static gif_result gif_internal_decode_frame(
- struct gif_animation *gif,
- uint32_t frame_idx)
-{
- gif_result ret;
- uint8_t *gif_data;
- struct gif_frame *frame;
- uint32_t save_buffer_position;
+ /* Ensure this frame is supposed to be decoded */
+ if (frame->display == false) {
+ return GIF_OK;
+ }
- /* Ensure the frame is in range to decode */
- if (frame_idx > gif->frame_count_partial) {
- return GIF_INSUFFICIENT_DATA;
- }
+ /* Ensure the frame is in range to decode */
+ if (frame_idx > gif->frame_count_partial) {
+ return GIF_INSUFFICIENT_DATA;
+ }
- /* Done if frame is already decoded */
- if (((int)frame_idx == gif->decoded_frame)) {
- return GIF_OK;
- }
+ /* Done if frame is already decoded */
+ if ((int)frame_idx == gif->decoded_frame) {
+ return GIF_OK;
+ }
+ } else {
+ pos = (uint8_t *)(gif->gif_data + gif->buffer_position);
- frame = gif__get_frame(gif, frame_idx);
- if (frame == NULL) {
- return GIF_INSUFFICIENT_MEMORY;
- }
+ /* Check if we've finished */
+ if (pos < end && pos[0] == GIF_TRAILER) {
+ return GIF_OK;
+ }
- /* Ensure this frame is supposed to be decoded */
- if (frame->display == false) {
- return GIF_OK;
+ /* We could theoretically get some junk data that gives us
+ * millions of frames, so we ensure that we don't have a
+ * silly number. */
+ if (frame_idx > 4096) {
+ return GIF_FRAME_DATA_ERROR;
+ }
}
- /* Get the start of our frame data and the end of the GIF data */
- gif_data = gif->gif_data + frame->frame_pointer;
-
- /* Save the buffer position */
- save_buffer_position = gif->buffer_position;
- gif->buffer_position = gif_data - gif->gif_data;
-
- /* Skip any extensions because they have already been processed */
- ret = gif__parse_frame_extensions(gif, frame, false);
+ /* Initialise any extensions */
+ ret = gif__parse_frame_extensions(gif, frame, &pos, !decode);
if (ret != GIF_OK) {
- goto gif_decode_frame_exit;
+ goto cleanup;
}
- ret = gif__parse_image_descriptor(gif, frame, false);
+ ret = gif__parse_image_descriptor(gif, frame, &pos, !decode);
if (ret != GIF_OK) {
- goto gif_decode_frame_exit;
+ goto cleanup;
}
- ret = gif__parse_colour_table(gif, frame, true);
+ ret = gif__parse_colour_table(gif, frame, &pos, decode);
if (ret != GIF_OK) {
- goto gif_decode_frame_exit;
+ goto cleanup;
}
- ret = gif__parse_image_data(gif, frame, true);
+ ret = gif__parse_image_data(gif, frame, &pos, decode);
if (ret != GIF_OK) {
- goto gif_decode_frame_exit;
+ goto cleanup;
}
-gif_decode_frame_exit:
-
- /* Restore the buffer position */
- gif->buffer_position = save_buffer_position;
+cleanup:
+ if (!decode) {
+ gif->buffer_position = pos - gif->gif_data;
+ }
return ret;
}
-
/* exported function documented in libnsgif.h */
void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks)
{
@@ -1298,7 +1253,7 @@ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data)
}
/* Repeatedly try to initialise frames */
- while ((ret = gif_initialise_frame(gif, gif->frame_count)) == GIF_WORKING);
+ while ((ret = gif__process_frame(gif, gif->frame_count, false)) == GIF_WORKING);
/* If there was a memory error tell the caller */
if ((ret == GIF_INSUFFICIENT_MEMORY) ||
@@ -1321,7 +1276,7 @@ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data)
/* exported function documented in libnsgif.h */
gif_result gif_decode_frame(gif_animation *gif, unsigned int frame)
{
- return gif_internal_decode_frame(gif, frame);
+ return gif__process_frame(gif, frame, true);
}
--
NetSurf GIF Decoder
1 year, 10 months
libnsgif: branch tlsa/rewrite updated. release/0.2.1-90-gf2c94c5
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/libnsgif.git/shortlog/f2c94c53e1a05e11bd5e...
...commit http://git.netsurf-browser.org/libnsgif.git/commit/f2c94c53e1a05e11bd5efd...
...tree http://git.netsurf-browser.org/libnsgif.git/tree/f2c94c53e1a05e11bd5efda7...
The branch, tlsa/rewrite has been updated
via f2c94c53e1a05e11bd5efda79748f847f24c7f7d (commit)
via f19df341f8a3f3ec5a319fd4621b4a8f53699dcb (commit)
via ec55ce31acd8aa8b2601db2a74e5b1725fa61da2 (commit)
via 170edd84170a8de5801dcf5c022a562374f83b2f (commit)
via 1e4a781b695992fbe09459baa3518637fba96223 (commit)
via c17ab37852fadbc6fe7ae80a969aa408f3d809b1 (commit)
via 0f7bcedeedff2cdee82644213741b5b96d655d92 (commit)
via 22b5b8f627a770172fbcc9e37656dcf40836f5df (commit)
via 1587b6a1a936ba11dfc951d07766562dbb4e8cf9 (commit)
from d96f028f305b5286618dcfc682e0de2515b3b7bd (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=f2c94c53e1a05e11bd...
commit f2c94c53e1a05e11bd5efda79748f847f24c7f7d
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Remove redundant check and comment from frame initialiser.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 366b4ef..1280798 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -991,14 +991,6 @@ static gif_result gif_initialise_frame(
return GIF_OK;
}
- /* Check if there is enough data remaining. The shortest block of data
- * is a 4-byte comment extension + 1-byte block terminator + 1-byte gif
- * trailer
- */
- if (gif_bytes < 6) {
- return GIF_INSUFFICIENT_DATA;
- }
-
/* We could theoretically get some junk data that gives us millions of
* frames, so we ensure that we don't have a silly number
*/
@@ -1011,12 +1003,6 @@ static gif_result gif_initialise_frame(
return GIF_INSUFFICIENT_MEMORY;
}
- /* We pretend to initialise the frames, but really we just skip over
- * all the data contained within. This is all basically a cut down
- * version of gif_decode_frame that doesn't have any of the LZW bits in
- * it.
- */
-
/* Initialise any extensions */
gif->buffer_position = gif_data - gif->gif_data;
ret = gif__parse_frame_extensions(gif, &gif->frames[frame_idx], true);
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=f19df341f8a3f3ec5a...
commit f19df341f8a3f3ec5a319fd4621b4a8f53699dcb
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Use image parsing frunction for frame initialisation.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index fc826a8..366b4ef 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -980,7 +980,6 @@ static gif_result gif_initialise_frame(
struct gif_frame *frame;
uint8_t *gif_data, *gif_end;
int gif_bytes;
- uint32_t block_size;
/* Get our buffer position etc. */
gif_data = (uint8_t *)(gif->gif_data + gif->buffer_position);
@@ -1034,67 +1033,13 @@ static gif_result gif_initialise_frame(
if (ret != GIF_OK) {
return ret;
}
- gif_data = gif->gif_data + gif->buffer_position;
- gif_bytes = (gif_end - gif_data);
- /* Move our data onwards and remember we've got a bit of this frame */
- gif->frame_count_partial = frame_idx + 1;
-
- /* Ensure we have a correct code size */
- if (gif_bytes < 1) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- if (gif_data[0] >= LZW_CODE_MAX) {
- return GIF_DATA_ERROR;
- }
-
- /* Move our pointer to the actual image data */
- gif_data++;
- --gif_bytes;
-
- /* Repeatedly skip blocks until we get a zero block or run out of data
- * These blocks of image data are processed later by gif_decode_frame()
- */
- block_size = 0;
- while (block_size != 1) {
- if (gif_bytes < 1) return GIF_INSUFFICIENT_FRAME_DATA;
- block_size = gif_data[0] + 1;
- /* Check if the frame data runs off the end of the file */
- if ((int)(gif_bytes - block_size) < 0) {
- /* Try to recover by signaling the end of the gif.
- * Once we get garbage data, there is no logical way to
- * determine where the next frame is. It's probably
- * better to partially load the gif than not at all.
- */
- if (gif_bytes >= 2) {
- gif_data[0] = 0;
- gif_data[1] = GIF_TRAILER;
- gif_bytes = 1;
- ++gif_data;
- break;
- } else {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- } else {
- gif_bytes -= block_size;
- gif_data += block_size;
- }
+ ret = gif__parse_image_data(gif, &gif->frames[frame_idx], false);
+ if (ret != GIF_OK) {
+ return ret;
}
- /* Add the frame and set the display flag */
- gif->buffer_position = gif_data - gif->gif_data;
- gif->frame_count = frame_idx + 1;
- gif->frames[frame_idx].display = true;
-
- /* Check if we've finished */
- if (gif_bytes < 1) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- } else {
- if (gif_data[0] == GIF_TRAILER) {
- return GIF_OK;
- }
- }
- return GIF_WORKING;
+ return GIF_OK;
}
/**
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=ec55ce31acd8aa8b26...
commit ec55ce31acd8aa8b2601db2a74e5b1725fa61da2
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Reorder functions so frame initialise can use image decoder.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 3b1df7e..fc826a8 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -70,6 +70,21 @@ enum gif_disposal {
/** standard GIF header size */
#define GIF_STANDARD_HEADER_SIZE 13
+static gif_result gif_error_from_lzw(lzw_result l_res)
+{
+ static const gif_result g_res[] = {
+ [LZW_OK] = GIF_OK,
+ [LZW_OK_EOD] = GIF_END_OF_FRAME,
+ [LZW_NO_MEM] = GIF_INSUFFICIENT_MEMORY,
+ [LZW_NO_DATA] = GIF_INSUFFICIENT_FRAME_DATA,
+ [LZW_EOI_CODE] = GIF_FRAME_DATA_ERROR,
+ [LZW_BAD_ICODE] = GIF_FRAME_DATA_ERROR,
+ [LZW_BAD_CODE] = GIF_FRAME_DATA_ERROR,
+ };
+ assert(l_res != LZW_BAD_PARAM);
+ assert(l_res != LZW_NO_COLOUR);
+ return g_res[l_res];
+}
/**
* Updates the sprite memory size
@@ -79,10 +94,10 @@ enum gif_disposal {
* \param height The height of the sprite
* \return GIF_INSUFFICIENT_MEMORY for a memory error GIF_OK for success
*/
-static gif_result
-gif_initialise_sprite(gif_animation *gif,
- uint32_t width,
- uint32_t height)
+static gif_result gif_initialise_sprite(
+ struct gif_animation *gif,
+ uint32_t width,
+ uint32_t height)
{
/* Already allocated? */
if (gif->frame_image) {
@@ -99,909 +114,717 @@ gif_initialise_sprite(gif_animation *gif,
}
/**
- * Parse the application extension
+ * Helper to get the rendering bitmap for a gif.
*
- * \param[in] frame The gif object we're decoding.
- * \param[in] data The data to decode.
- * \param[in] len Byte length of data.
- * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
- * GIF_OK for success.
+ * \param[in] gif The gif object we're decoding.
+ * \return Client pixel buffer for rendering into.
*/
-static gif_result gif__parse_extension_graphic_control(
- struct gif_frame *frame,
- uint8_t *data,
- size_t len)
+static inline uint32_t* gif__bitmap_get(
+ struct gif_animation *gif)
{
- /* 6-byte Graphic Control Extension is:
- *
- * +0 CHAR Graphic Control Label
- * +1 CHAR Block Size
- * +2 CHAR __Packed Fields__
- * 3BITS Reserved
- * 3BITS Disposal Method
- * 1BIT User Input Flag
- * 1BIT Transparent Color Flag
- * +3 SHORT Delay Time
- * +5 CHAR Transparent Color Index
- */
- if (len < 6) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
-
- frame->frame_delay = data[3] | (data[4] << 8);
- if (data[2] & GIF_TRANSPARENCY_MASK) {
- frame->transparency = true;
- frame->transparency_index = data[5];
- }
+ gif_result ret;
- frame->disposal_method = ((data[2] & GIF_DISPOSAL_MASK) >> 2);
- /* I have encountered documentation and GIFs in the
- * wild that use 0x04 to restore the previous frame,
- * rather than the officially documented 0x03. I
- * believe some (older?) software may even actually
- * export this way. We handle this as a type of
- * "quirks" mode. */
- if (frame->disposal_method == GIF_DISPOSAL_RESTORE_QUIRK) {
- frame->disposal_method = GIF_DISPOSAL_RESTORE_PREV;
+ /* Make sure we have a buffer to decode to. */
+ ret = gif_initialise_sprite(gif, gif->width, gif->height);
+ if (ret != GIF_OK) {
+ return NULL;
}
- /* if we are clearing the background then we need to
- * redraw enough to cover the previous frame too. */
- frame->redraw_required =
- frame->disposal_method == GIF_DISPOSAL_RESTORE_BG ||
- frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV;
-
- return GIF_OK;
+ /* Get the frame data */
+ assert(gif->bitmap_callbacks.bitmap_get_buffer);
+ return (uint32_t *)gif->bitmap_callbacks.bitmap_get_buffer(
+ gif->frame_image);
}
/**
- * Parse the application extension
+ * Helper to tell the client that their bitmap was modified.
*
- * \param[in] gif The gif object we're decoding.
- * \param[in] data The data to decode.
- * \param[in] len Byte length of data.
- * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
- * GIF_OK for success.
+ * \param[in] gif The gif object we're decoding.
*/
-static gif_result gif__parse_extension_application(
- struct gif_animation *gif,
- uint8_t *data,
- size_t len)
+static inline void gif__bitmap_modified(
+ const struct gif_animation *gif)
{
- /* 14-byte+ Application Extension is:
- *
- * +0 CHAR Application Extension Label
- * +1 CHAR Block Size
- * +2 8CHARS Application Identifier
- * +10 3CHARS Appl. Authentication Code
- * +13 1-256 Application Data (Data sub-blocks)
- */
- if (len < 17) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ if (gif->bitmap_callbacks.bitmap_modified) {
+ gif->bitmap_callbacks.bitmap_modified(gif->frame_image);
}
+}
- if ((data[1] == 0x0b) &&
- (strncmp((const char *)data + 2, "NETSCAPE2.0", 11) == 0) &&
- (data[13] == 0x03) && (data[14] == 0x01)) {
- gif->loop_count = data[15] | (data[16] << 8);
+/**
+ * Helper to tell the client that whether the bitmap is opaque.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frmae The frame that has been decoded.
+ */
+static inline void gif__bitmap_set_opaque(
+ const struct gif_animation *gif,
+ const struct gif_frame *frame)
+{
+ if (gif->bitmap_callbacks.bitmap_set_opaque) {
+ gif->bitmap_callbacks.bitmap_set_opaque(
+ gif->frame_image, frame->opaque);
}
-
- return GIF_OK;
}
/**
- * Parse the frame's extensions
+ * Helper to get the client to determine if the bitmap is opaque.
*
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to parse extensions for.
- * \param[in] decode Whether to decode or skip over the extension.
- * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
- * GIF_OK for success.
+ * \todo: We don't really need to get the client to do this for us.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \return true if the bitmap is opaque, false otherwise.
*/
-static gif_result gif__parse_frame_extensions(
- struct gif_animation *gif,
- struct gif_frame *frame,
- bool decode)
+static inline bool gif__bitmap_get_opaque(
+ const struct gif_animation *gif)
{
- uint8_t *gif_data, *gif_end;
- int gif_bytes;
-
- /* Get our buffer position etc. */
- gif_data = gif->gif_data + gif->buffer_position;
- gif_end = gif->gif_data + gif->buffer_size;
- gif_bytes = gif_end - gif_data;
-
- /* Initialise the extensions */
- while (gif_bytes > 0 && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
- bool block_step = true;
- gif_result ret;
-
- gif_data++;
- gif_bytes--;
-
- if (gif_bytes == 0) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
+ if (gif->bitmap_callbacks.bitmap_test_opaque) {
+ return gif->bitmap_callbacks.bitmap_test_opaque(
+ gif->frame_image);
+ }
- /* Switch on extension label */
- switch (gif_data[0]) {
- case GIF_EXTENSION_GRAPHIC_CONTROL:
- if (decode) {
- ret = gif__parse_extension_graphic_control(
- frame, gif_data, gif_bytes);
- if (ret != GIF_OK) {
- return ret;
- }
- }
- break;
+ return false;
+}
- case GIF_EXTENSION_APPLICATION:
- if (decode) {
- ret = gif__parse_extension_application(
- gif, gif_data, gif_bytes);
- if (ret != GIF_OK) {
- return ret;
- }
- }
- break;
+static void gif__record_frame(
+ struct gif_animation *gif,
+ const uint32_t *bitmap)
+{
+ bool need_alloc = gif->prev_frame == NULL;
+ uint32_t *prev_frame;
- case GIF_EXTENSION_COMMENT:
- /* Move the pointer to the first data sub-block Skip 1
- * byte for the extension label. */
- ++gif_data;
- block_step = false;
- break;
+ if (gif->decoded_frame == GIF_INVALID_FRAME ||
+ gif->decoded_frame == gif->prev_index) {
+ /* No frame to copy, or already have this frame recorded. */
+ return;
+ }
- default:
- break;
- }
+ bitmap = gif__bitmap_get(gif);
+ if (bitmap == NULL) {
+ return;
+ }
- if (block_step) {
- /* Move the pointer to the first data sub-block Skip 2
- * bytes for the extension label and size fields Skip
- * the extension size itself
- */
- if (gif_bytes < 2) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- gif_data += 2 + gif_data[1];
- }
+ if (gif->prev_frame != NULL &&
+ gif->width * gif->height > gif->prev_width * gif->prev_height) {
+ need_alloc = true;
+ }
- /* Repeatedly skip blocks until we get a zero block or run out
- * of data. This data is ignored by this gif decoder. */
- while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) {
- gif_data += gif_data[0] + 1;
- if (gif_data >= gif_end) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
+ if (need_alloc) {
+ prev_frame = realloc(gif->prev_frame,
+ gif->width * gif->height * 4);
+ if (prev_frame == NULL) {
+ return;
}
- gif_data++;
- gif_bytes = gif_end - gif_data;
+ } else {
+ prev_frame = gif->prev_frame;
}
- if (gif_data > gif_end) {
- gif_data = gif_end;
- }
+ memcpy(prev_frame, bitmap, gif->width * gif->height * 4);
- /* Set buffer position and return */
- gif->buffer_position = gif_data - gif->gif_data;
- return GIF_OK;
+ gif->prev_frame = prev_frame;
+ gif->prev_width = gif->width;
+ gif->prev_height = gif->height;
+ gif->prev_index = gif->decoded_frame;
}
-/**
- * Parse a GIF Image Descriptor.
- *
- * The format is:
- *
- * +0 CHAR Image Separator (0x2c)
- * +1 SHORT Image Left Position
- * +3 SHORT Image Top Position
- * +5 SHORT Width
- * +7 SHORT Height
- * +9 CHAR __Packed Fields__
- * 1BIT Local Colour Table Flag
- * 1BIT Interlace Flag
- * 1BIT Sort Flag
- * 2BITS Reserved
- * 3BITS Size of Local Colour Table
- *
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to parse an image descriptor for.
- * \return GIF_OK on success, appropriate error otherwise.
- */
-static gif_result gif__parse_image_descriptor(
- struct gif_animation *gif,
- struct gif_frame *frame,
- bool decode)
+static gif_result gif__recover_frame(
+ const struct gif_animation *gif,
+ uint32_t *bitmap)
{
- const uint8_t *data = gif->gif_data + gif->buffer_position;
- size_t len = gif->buffer_size - gif->buffer_position;
- enum {
- GIF_IMAGE_DESCRIPTOR_LEN = 10u,
- GIF_IMAGE_SEPARATOR = 0x2Cu,
- };
-
- assert(gif != NULL);
- assert(frame != NULL);
+ const uint32_t *prev_frame = gif->prev_frame;
+ unsigned height = gif->height < gif->prev_height ? gif->height : gif->prev_height;
+ unsigned width = gif->width < gif->prev_width ? gif->width : gif->prev_width;
- if (len < GIF_IMAGE_DESCRIPTOR_LEN) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ if (prev_frame == NULL) {
+ return GIF_FRAME_DATA_ERROR;
}
- if (decode) {
- unsigned x, y, w, h;
-
- if (data[0] != GIF_IMAGE_SEPARATOR) {
- return GIF_FRAME_DATA_ERROR;
- }
-
- x = data[1] | (data[2] << 8);
- y = data[3] | (data[4] << 8);
- w = data[5] | (data[6] << 8);
- h = data[7] | (data[8] << 8);
- frame->flags = data[9];
-
- frame->redraw_x = x;
- frame->redraw_y = y;
- frame->redraw_width = w;
- frame->redraw_height = h;
+ for (unsigned y = 0; y < height; y++) {
+ memcpy(bitmap, prev_frame, width * 4);
- /* Frame size may have grown. */
- gif->width = (x + w > gif->width ) ? x + w : gif->width;
- gif->height = (y + h > gif->height) ? y + h : gif->height;
+ bitmap += gif->width;
+ prev_frame += gif->prev_width;
}
- gif->buffer_position += GIF_IMAGE_DESCRIPTOR_LEN;
return GIF_OK;
}
-/**
- * Get a frame's colour table.
- *
- * Sets up gif->colour_table for the frame.
- *
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to get the colour table for.
- * \return GIF_OK on success, appropriate error otherwise.
- */
-static gif_result gif__parse_colour_table(
- struct gif_animation *gif,
- struct gif_frame *frame,
- bool decode)
+static uint32_t gif_interlaced_line(int height, int y)
{
- const uint8_t *data = gif->gif_data + gif->buffer_position;
- size_t len = gif->buffer_size - gif->buffer_position;
- unsigned colour_table_size;
-
- assert(gif != NULL);
- assert(frame != NULL);
-
- if ((frame->flags & GIF_COLOUR_TABLE_MASK) == 0) {
- gif->colour_table = gif->global_colour_table;
- return GIF_OK;
+ if ((y << 3) < height) {
+ return (y << 3);
}
-
- colour_table_size = 2 << (frame->flags & GIF_COLOUR_TABLE_SIZE_MASK);
- if (len < colour_table_size * 3) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ y -= ((height + 7) >> 3);
+ if ((y << 3) < (height - 4)) {
+ return (y << 3) + 4;
}
-
- if (decode) {
- int count = colour_table_size;
- uint8_t *entry = (uint8_t *)gif->local_colour_table;
-
- while (count--) {
- /* Gif colour map contents are r,g,b.
- *
- * We want to pack them bytewise into the
- * colour table, such that the red component
- * is in byte 0 and the alpha component is in
- * byte 3.
- */
-
- *entry++ = *data++; /* r */
- *entry++ = *data++; /* g */
- *entry++ = *data++; /* b */
- *entry++ = 0xff; /* a */
- }
+ y -= ((height + 3) >> 3);
+ if ((y << 2) < (height - 2)) {
+ return (y << 2) + 2;
}
-
- gif->buffer_position += colour_table_size * 3;
- gif->colour_table = gif->local_colour_table;
- return GIF_OK;
+ y -= ((height + 1) >> 2);
+ return (y << 1) + 1;
}
-static struct gif_frame *gif__get_frame(
+static gif_result gif__decode_complex(
struct gif_animation *gif,
- uint32_t frame_idx)
+ uint32_t width,
+ uint32_t height,
+ uint32_t offset_x,
+ uint32_t offset_y,
+ uint32_t interlace,
+ uint8_t minimum_code_size,
+ uint32_t transparency_index,
+ uint32_t *restrict frame_data,
+ uint32_t *restrict colour_table)
{
- struct gif_frame *frame;
+ uint32_t available = 0;
+ gif_result ret = GIF_OK;
+ lzw_result res;
- if (gif->frame_holders > frame_idx) {
- frame = &gif->frames[frame_idx];
- } else {
- /* Allocate more memory */
- size_t count = frame_idx + 1;
- struct gif_frame *temp;
+ /* Initialise the LZW decoding */
+ res = lzw_decode_init(gif->lzw_ctx, minimum_code_size,
+ gif->gif_data, gif->buffer_size, gif->buffer_position);
+ if (res != LZW_OK) {
+ return gif_error_from_lzw(res);
+ }
- temp = realloc(gif->frames, count * sizeof(*frame));
- if (temp == NULL) {
- return NULL;
+ for (uint32_t y = 0; y < height; y++) {
+ uint32_t x;
+ uint32_t decode_y;
+ uint32_t *frame_scanline;
+
+ if (interlace) {
+ decode_y = gif_interlaced_line(height, y) + offset_y;
+ } else {
+ decode_y = y + offset_y;
}
- gif->frames = temp;
- gif->frame_holders = count;
+ frame_scanline = frame_data + offset_x + (decode_y * gif->width);
- frame = &gif->frames[frame_idx];
+ x = width;
+ while (x > 0) {
+ const uint8_t *uncompressed;
+ unsigned row_available;
+ if (available == 0) {
+ if (res != LZW_OK) {
+ /* Unexpected end of frame, try to recover */
+ if (res == LZW_OK_EOD) {
+ ret = GIF_OK;
+ } else {
+ ret = gif_error_from_lzw(res);
+ }
+ break;
+ }
+ res = lzw_decode(gif->lzw_ctx,
+ &uncompressed, &available);
+ }
- frame->transparency = false;
- frame->transparency_index = GIF_NO_TRANSPARENCY;
- frame->frame_pointer = gif->buffer_position;
- frame->redraw_required = false;
- frame->disposal_method = 0;
- frame->frame_delay = 100;
- frame->display = false;
- frame->virgin = true;
+ row_available = x < available ? x : available;
+ x -= row_available;
+ available -= row_available;
+ if (transparency_index > 0xFF) {
+ while (row_available-- > 0) {
+ *frame_scanline++ =
+ colour_table[*uncompressed++];
+ }
+ } else {
+ while (row_available-- > 0) {
+ register uint32_t colour;
+ colour = *uncompressed++;
+ if (colour != transparency_index) {
+ *frame_scanline =
+ colour_table[colour];
+ }
+ frame_scanline++;
+ }
+ }
+ }
}
-
- return frame;
+ return ret;
}
-/**
- * Attempts to initialise the next frame
- *
- * \param[in] gif The animation context
- * \param[in] frame_idx The frame number to decode.
- * \return error code
- * - GIF_INSUFFICIENT_DATA for insufficient data to do anything
- * - GIF_FRAME_DATA_ERROR for GIF frame data error
- * - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
- * - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
- * - GIF_DATA_ERROR for GIF error (invalid frame header)
- * - GIF_OK for successful decoding
- * - GIF_WORKING for successful decoding if more frames are expected
-*/
-static gif_result gif_initialise_frame(gif_animation *gif,
- uint32_t frame_idx)
+static gif_result gif__decode_simple(
+ struct gif_animation *gif,
+ uint32_t height,
+ uint32_t offset_y,
+ uint8_t minimum_code_size,
+ uint32_t transparency_index,
+ uint32_t *restrict frame_data,
+ uint32_t *restrict colour_table)
{
- gif_result ret;
- struct gif_frame *frame;
- uint8_t *gif_data, *gif_end;
- int gif_bytes;
- uint32_t block_size;
-
- /* Get our buffer position etc. */
- gif_data = (uint8_t *)(gif->gif_data + gif->buffer_position);
- gif_end = (uint8_t *)(gif->gif_data + gif->buffer_size);
- gif_bytes = (gif_end - gif_data);
-
- /* Check if we've finished */
- if ((gif_bytes > 0) && (gif_data[0] == GIF_TRAILER)) {
- return GIF_OK;
- }
-
- /* Check if there is enough data remaining. The shortest block of data
- * is a 4-byte comment extension + 1-byte block terminator + 1-byte gif
- * trailer
- */
- if (gif_bytes < 6) {
- return GIF_INSUFFICIENT_DATA;
- }
-
- /* We could theoretically get some junk data that gives us millions of
- * frames, so we ensure that we don't have a silly number
- */
- if (frame_idx > 4096) {
- return GIF_FRAME_DATA_ERROR;
- }
-
- frame = gif__get_frame(gif, frame_idx);
- if (frame == NULL) {
- return GIF_INSUFFICIENT_MEMORY;
- }
-
- /* We pretend to initialise the frames, but really we just skip over
- * all the data contained within. This is all basically a cut down
- * version of gif_decode_frame that doesn't have any of the LZW bits in
- * it.
- */
-
- /* Initialise any extensions */
- gif->buffer_position = gif_data - gif->gif_data;
- ret = gif__parse_frame_extensions(gif, &gif->frames[frame_idx], true);
- if (ret != GIF_OK) {
- return ret;
- }
-
- ret = gif__parse_image_descriptor(gif, &gif->frames[frame_idx], true);
- if (ret != GIF_OK) {
- return ret;
- }
-
- ret = gif__parse_colour_table(gif, &gif->frames[frame_idx], false);
- if (ret != GIF_OK) {
- return ret;
- }
- gif_data = gif->gif_data + gif->buffer_position;
- gif_bytes = (gif_end - gif_data);
-
- /* Move our data onwards and remember we've got a bit of this frame */
- gif->frame_count_partial = frame_idx + 1;
+ uint32_t pixels = gif->width * height;
+ uint32_t written = 0;
+ gif_result ret = GIF_OK;
+ lzw_result res;
- /* Ensure we have a correct code size */
- if (gif_bytes < 1) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- if (gif_data[0] >= LZW_CODE_MAX) {
- return GIF_DATA_ERROR;
+ /* Initialise the LZW decoding */
+ res = lzw_decode_init_map(gif->lzw_ctx,
+ minimum_code_size, transparency_index, colour_table,
+ gif->gif_data, gif->buffer_size, gif->buffer_position);
+ if (res != LZW_OK) {
+ return gif_error_from_lzw(res);
}
- /* Move our pointer to the actual image data */
- gif_data++;
- --gif_bytes;
+ frame_data += (offset_y * gif->width);
- /* Repeatedly skip blocks until we get a zero block or run out of data
- * These blocks of image data are processed later by gif_decode_frame()
- */
- block_size = 0;
- while (block_size != 1) {
- if (gif_bytes < 1) return GIF_INSUFFICIENT_FRAME_DATA;
- block_size = gif_data[0] + 1;
- /* Check if the frame data runs off the end of the file */
- if ((int)(gif_bytes - block_size) < 0) {
- /* Try to recover by signaling the end of the gif.
- * Once we get garbage data, there is no logical way to
- * determine where the next frame is. It's probably
- * better to partially load the gif than not at all.
- */
- if (gif_bytes >= 2) {
- gif_data[0] = 0;
- gif_data[1] = GIF_TRAILER;
- gif_bytes = 1;
- ++gif_data;
- break;
+ while (pixels > 0) {
+ res = lzw_decode_map(gif->lzw_ctx,
+ frame_data, pixels, &written);
+ pixels -= written;
+ frame_data += written;
+ if (res != LZW_OK) {
+ /* Unexpected end of frame, try to recover */
+ if (res == LZW_OK_EOD) {
+ ret = GIF_OK;
} else {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ ret = gif_error_from_lzw(res);
}
- } else {
- gif_bytes -= block_size;
- gif_data += block_size;
- }
- }
-
- /* Add the frame and set the display flag */
- gif->buffer_position = gif_data - gif->gif_data;
- gif->frame_count = frame_idx + 1;
- gif->frames[frame_idx].display = true;
-
- /* Check if we've finished */
- if (gif_bytes < 1) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- } else {
- if (gif_data[0] == GIF_TRAILER) {
- return GIF_OK;
+ break;
}
}
- return GIF_WORKING;
-}
-static uint32_t gif_interlaced_line(int height, int y)
-{
- if ((y << 3) < height) {
- return (y << 3);
- }
- y -= ((height + 7) >> 3);
- if ((y << 3) < (height - 4)) {
- return (y << 3) + 4;
- }
- y -= ((height + 3) >> 3);
- if ((y << 2) < (height - 2)) {
- return (y << 2) + 2;
+ if (pixels == 0) {
+ ret = GIF_OK;
}
- y -= ((height + 1) >> 2);
- return (y << 1) + 1;
-}
-
-static gif_result gif_error_from_lzw(lzw_result l_res)
-{
- static const gif_result g_res[] = {
- [LZW_OK] = GIF_OK,
- [LZW_OK_EOD] = GIF_END_OF_FRAME,
- [LZW_NO_MEM] = GIF_INSUFFICIENT_MEMORY,
- [LZW_NO_DATA] = GIF_INSUFFICIENT_FRAME_DATA,
- [LZW_EOI_CODE] = GIF_FRAME_DATA_ERROR,
- [LZW_BAD_ICODE] = GIF_FRAME_DATA_ERROR,
- [LZW_BAD_CODE] = GIF_FRAME_DATA_ERROR,
- };
- assert(l_res != LZW_BAD_PARAM);
- assert(l_res != LZW_NO_COLOUR);
- return g_res[l_res];
+ return ret;
}
-/**
- * Helper to get the rendering bitmap for a gif.
- *
- * \param[in] gif The gif object we're decoding.
- * \return Client pixel buffer for rendering into.
- */
-static inline uint32_t* gif__bitmap_get(
- struct gif_animation *gif)
+static inline gif_result gif__decode(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ uint8_t minimum_code_size,
+ uint32_t *restrict frame_data)
{
gif_result ret;
+ uint32_t offset_x = frame->redraw_x;
+ uint32_t offset_y = frame->redraw_y;
+ uint32_t width = frame->redraw_width;
+ uint32_t height = frame->redraw_height;
+ uint32_t interlace = frame->flags & GIF_INTERLACE_MASK;
+ uint32_t transparency_index = frame->transparency_index;
+ uint32_t *restrict colour_table = gif->colour_table;
- /* Make sure we have a buffer to decode to. */
- ret = gif_initialise_sprite(gif, gif->width, gif->height);
- if (ret != GIF_OK) {
- return NULL;
- }
-
- /* Get the frame data */
- assert(gif->bitmap_callbacks.bitmap_get_buffer);
- return (uint32_t *)gif->bitmap_callbacks.bitmap_get_buffer(
- gif->frame_image);
-}
-
-/**
- * Helper to tell the client that their bitmap was modified.
- *
- * \param[in] gif The gif object we're decoding.
- */
-static inline void gif__bitmap_modified(
- const struct gif_animation *gif)
-{
- if (gif->bitmap_callbacks.bitmap_modified) {
- gif->bitmap_callbacks.bitmap_modified(gif->frame_image);
+ if (interlace == false && width == gif->width && offset_x == 0) {
+ ret = gif__decode_simple(gif, height, offset_y,
+ minimum_code_size, transparency_index,
+ frame_data, colour_table);
+ } else {
+ ret = gif__decode_complex(gif, width, height,
+ offset_x, offset_y, interlace,
+ minimum_code_size, transparency_index,
+ frame_data, colour_table);
}
-}
-/**
- * Helper to tell the client that whether the bitmap is opaque.
- *
- * \param[in] gif The gif object we're decoding.
- * \param[in] frmae The frame that has been decoded.
- */
-static inline void gif__bitmap_set_opaque(
- const struct gif_animation *gif,
- const struct gif_frame *frame)
-{
- if (gif->bitmap_callbacks.bitmap_set_opaque) {
- gif->bitmap_callbacks.bitmap_set_opaque(
- gif->frame_image, frame->opaque);
- }
+ return ret;
}
/**
- * Helper to get the client to determine if the bitmap is opaque.
- *
- * \todo: We don't really need to get the client to do this for us.
+ * Restore a GIF to the background colour.
*
- * \param[in] gif The gif object we're decoding.
- * \return true if the bitmap is opaque, false otherwise.
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to clear, or NULL.
+ * \param[in] bitmap The bitmap to clear the frame in.
*/
-static inline bool gif__bitmap_get_opaque(
- const struct gif_animation *gif)
+static void gif__restore_bg(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ uint32_t *bitmap)
{
- if (gif->bitmap_callbacks.bitmap_test_opaque) {
- return gif->bitmap_callbacks.bitmap_test_opaque(
- gif->frame_image);
- }
+ if (frame == NULL) {
+ memset(bitmap, GIF_TRANSPARENT_COLOUR,
+ gif->width * gif->height * sizeof(*bitmap));
+ } else {
+ uint32_t offset_x = frame->redraw_x;
+ uint32_t offset_y = frame->redraw_y;
+ uint32_t width = frame->redraw_width;
+ uint32_t height = frame->redraw_height;
- return false;
+ if (frame->display == false) {
+ return;
+ }
+
+ if (frame->transparency) {
+ for (uint32_t y = 0; y < height; y++) {
+ uint32_t *scanline = bitmap + offset_x +
+ (offset_y + y) * gif->width;
+ memset(scanline, GIF_TRANSPARENT_COLOUR,
+ width * sizeof(*bitmap));
+ }
+ } else {
+ for (uint32_t y = 0; y < height; y++) {
+ uint32_t *scanline = bitmap + offset_x +
+ (offset_y + y) * gif->width;
+ for (uint32_t x = 0; x < width; x++) {
+ scanline[x] = gif->bg_colour;
+ }
+ }
+ }
+ }
}
-static void gif__record_frame(
+static gif_result gif__update_bitmap(
struct gif_animation *gif,
- const uint32_t *bitmap)
+ struct gif_frame *frame,
+ uint8_t minimum_code_size,
+ uint32_t frame_idx)
{
- bool need_alloc = gif->prev_frame == NULL;
- uint32_t *prev_frame;
+ gif_result ret;
+ uint32_t *bitmap;
- if (gif->decoded_frame == GIF_INVALID_FRAME ||
- gif->decoded_frame == gif->prev_index) {
- /* No frame to copy, or already have this frame recorded. */
- return;
- }
+ gif->decoded_frame = frame_idx;
bitmap = gif__bitmap_get(gif);
if (bitmap == NULL) {
- return;
+ return GIF_INSUFFICIENT_MEMORY;
}
- if (gif->prev_frame != NULL &&
- gif->width * gif->height > gif->prev_width * gif->prev_height) {
- need_alloc = true;
- }
+ /* Handle any bitmap clearing/restoration required before decoding this
+ * frame. */
+ if (frame_idx == 0 || gif->decoded_frame == GIF_INVALID_FRAME) {
+ gif__restore_bg(gif, NULL, bitmap);
- if (need_alloc) {
- prev_frame = realloc(gif->prev_frame,
- gif->width * gif->height * 4);
- if (prev_frame == NULL) {
- return;
- }
} else {
- prev_frame = gif->prev_frame;
+ struct gif_frame *prev = &gif->frames[frame_idx - 1];
+
+ if (prev->disposal_method == GIF_DISPOSAL_RESTORE_BG) {
+ gif__restore_bg(gif, prev, bitmap);
+
+ } else if (prev->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
+ ret = gif__recover_frame(gif, bitmap);
+ if (ret != GIF_OK) {
+ gif__restore_bg(gif, prev, bitmap);
+ }
+ }
}
- memcpy(prev_frame, bitmap, gif->width * gif->height * 4);
+ if (frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
+ /* Store the previous frame for later restoration */
+ gif__record_frame(gif, bitmap);
+ }
- gif->prev_frame = prev_frame;
- gif->prev_width = gif->width;
- gif->prev_height = gif->height;
- gif->prev_index = gif->decoded_frame;
+ ret = gif__decode(gif, frame, minimum_code_size, bitmap);
+
+ gif__bitmap_modified(gif);
+
+ if (frame->virgin) {
+ frame->opaque = gif__bitmap_get_opaque(gif);
+ frame->virgin = false;
+ }
+ gif__bitmap_set_opaque(gif, frame);
+
+ return ret;
}
-static gif_result gif__recover_frame(
- const struct gif_animation *gif,
- uint32_t *bitmap)
+/**
+ * Parse the application extension
+ *
+ * \param[in] frame The gif object we're decoding.
+ * \param[in] data The data to decode.
+ * \param[in] len Byte length of data.
+ * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
+ * GIF_OK for success.
+ */
+static gif_result gif__parse_extension_graphic_control(
+ struct gif_frame *frame,
+ uint8_t *data,
+ size_t len)
{
- const uint32_t *prev_frame = gif->prev_frame;
- unsigned height = gif->height < gif->prev_height ? gif->height : gif->prev_height;
- unsigned width = gif->width < gif->prev_width ? gif->width : gif->prev_width;
+ /* 6-byte Graphic Control Extension is:
+ *
+ * +0 CHAR Graphic Control Label
+ * +1 CHAR Block Size
+ * +2 CHAR __Packed Fields__
+ * 3BITS Reserved
+ * 3BITS Disposal Method
+ * 1BIT User Input Flag
+ * 1BIT Transparent Color Flag
+ * +3 SHORT Delay Time
+ * +5 CHAR Transparent Color Index
+ */
+ if (len < 6) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
- if (prev_frame == NULL) {
- return GIF_FRAME_DATA_ERROR;
+ frame->frame_delay = data[3] | (data[4] << 8);
+ if (data[2] & GIF_TRANSPARENCY_MASK) {
+ frame->transparency = true;
+ frame->transparency_index = data[5];
}
- for (unsigned y = 0; y < height; y++) {
- memcpy(bitmap, prev_frame, width * 4);
+ frame->disposal_method = ((data[2] & GIF_DISPOSAL_MASK) >> 2);
+ /* I have encountered documentation and GIFs in the
+ * wild that use 0x04 to restore the previous frame,
+ * rather than the officially documented 0x03. I
+ * believe some (older?) software may even actually
+ * export this way. We handle this as a type of
+ * "quirks" mode. */
+ if (frame->disposal_method == GIF_DISPOSAL_RESTORE_QUIRK) {
+ frame->disposal_method = GIF_DISPOSAL_RESTORE_PREV;
+ }
- bitmap += gif->width;
- prev_frame += gif->prev_width;
+ /* if we are clearing the background then we need to
+ * redraw enough to cover the previous frame too. */
+ frame->redraw_required =
+ frame->disposal_method == GIF_DISPOSAL_RESTORE_BG ||
+ frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV;
+
+ return GIF_OK;
+}
+
+/**
+ * Parse the application extension
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] data The data to decode.
+ * \param[in] len Byte length of data.
+ * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
+ * GIF_OK for success.
+ */
+static gif_result gif__parse_extension_application(
+ struct gif_animation *gif,
+ uint8_t *data,
+ size_t len)
+{
+ /* 14-byte+ Application Extension is:
+ *
+ * +0 CHAR Application Extension Label
+ * +1 CHAR Block Size
+ * +2 8CHARS Application Identifier
+ * +10 3CHARS Appl. Authentication Code
+ * +13 1-256 Application Data (Data sub-blocks)
+ */
+ if (len < 17) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
+
+ if ((data[1] == 0x0b) &&
+ (strncmp((const char *)data + 2, "NETSCAPE2.0", 11) == 0) &&
+ (data[13] == 0x03) && (data[14] == 0x01)) {
+ gif->loop_count = data[15] | (data[16] << 8);
}
return GIF_OK;
}
-static gif_result
-gif__decode_complex(
+/**
+ * Parse the frame's extensions
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to parse extensions for.
+ * \param[in] decode Whether to decode or skip over the extension.
+ * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
+ * GIF_OK for success.
+ */
+static gif_result gif__parse_frame_extensions(
struct gif_animation *gif,
- uint32_t width,
- uint32_t height,
- uint32_t offset_x,
- uint32_t offset_y,
- uint32_t interlace,
- uint8_t minimum_code_size,
- uint32_t transparency_index,
- uint32_t *restrict frame_data,
- uint32_t *restrict colour_table)
+ struct gif_frame *frame,
+ bool decode)
{
- uint32_t available = 0;
- gif_result ret = GIF_OK;
- lzw_result res;
+ uint8_t *gif_data, *gif_end;
+ int gif_bytes;
+
+ /* Get our buffer position etc. */
+ gif_data = gif->gif_data + gif->buffer_position;
+ gif_end = gif->gif_data + gif->buffer_size;
+ gif_bytes = gif_end - gif_data;
- /* Initialise the LZW decoding */
- res = lzw_decode_init(gif->lzw_ctx, minimum_code_size,
- gif->gif_data, gif->buffer_size, gif->buffer_position);
- if (res != LZW_OK) {
- return gif_error_from_lzw(res);
- }
+ /* Initialise the extensions */
+ while (gif_bytes > 0 && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
+ bool block_step = true;
+ gif_result ret;
- for (uint32_t y = 0; y < height; y++) {
- uint32_t x;
- uint32_t decode_y;
- uint32_t *frame_scanline;
+ gif_data++;
+ gif_bytes--;
- if (interlace) {
- decode_y = gif_interlaced_line(height, y) + offset_y;
- } else {
- decode_y = y + offset_y;
+ if (gif_bytes == 0) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
}
- frame_scanline = frame_data + offset_x + (decode_y * gif->width);
- x = width;
- while (x > 0) {
- const uint8_t *uncompressed;
- unsigned row_available;
- if (available == 0) {
- if (res != LZW_OK) {
- /* Unexpected end of frame, try to recover */
- if (res == LZW_OK_EOD) {
- ret = GIF_OK;
- } else {
- ret = gif_error_from_lzw(res);
- }
- break;
+ /* Switch on extension label */
+ switch (gif_data[0]) {
+ case GIF_EXTENSION_GRAPHIC_CONTROL:
+ if (decode) {
+ ret = gif__parse_extension_graphic_control(
+ frame, gif_data, gif_bytes);
+ if (ret != GIF_OK) {
+ return ret;
}
- res = lzw_decode(gif->lzw_ctx,
- &uncompressed, &available);
}
+ break;
- row_available = x < available ? x : available;
- x -= row_available;
- available -= row_available;
- if (transparency_index > 0xFF) {
- while (row_available-- > 0) {
- *frame_scanline++ =
- colour_table[*uncompressed++];
- }
- } else {
- while (row_available-- > 0) {
- register uint32_t colour;
- colour = *uncompressed++;
- if (colour != transparency_index) {
- *frame_scanline =
- colour_table[colour];
- }
- frame_scanline++;
+ case GIF_EXTENSION_APPLICATION:
+ if (decode) {
+ ret = gif__parse_extension_application(
+ gif, gif_data, gif_bytes);
+ if (ret != GIF_OK) {
+ return ret;
}
}
- }
- }
- return ret;
-}
-
-static gif_result
-gif__decode_simple(
- struct gif_animation *gif,
- uint32_t height,
- uint32_t offset_y,
- uint8_t minimum_code_size,
- uint32_t transparency_index,
- uint32_t *restrict frame_data,
- uint32_t *restrict colour_table)
-{
- uint32_t pixels = gif->width * height;
- uint32_t written = 0;
- gif_result ret = GIF_OK;
- lzw_result res;
+ break;
- /* Initialise the LZW decoding */
- res = lzw_decode_init_map(gif->lzw_ctx,
- minimum_code_size, transparency_index, colour_table,
- gif->gif_data, gif->buffer_size, gif->buffer_position);
- if (res != LZW_OK) {
- return gif_error_from_lzw(res);
- }
+ case GIF_EXTENSION_COMMENT:
+ /* Move the pointer to the first data sub-block Skip 1
+ * byte for the extension label. */
+ ++gif_data;
+ block_step = false;
+ break;
- frame_data += (offset_y * gif->width);
+ default:
+ break;
+ }
- while (pixels > 0) {
- res = lzw_decode_map(gif->lzw_ctx,
- frame_data, pixels, &written);
- pixels -= written;
- frame_data += written;
- if (res != LZW_OK) {
- /* Unexpected end of frame, try to recover */
- if (res == LZW_OK_EOD) {
- ret = GIF_OK;
- } else {
- ret = gif_error_from_lzw(res);
+ if (block_step) {
+ /* Move the pointer to the first data sub-block Skip 2
+ * bytes for the extension label and size fields Skip
+ * the extension size itself
+ */
+ if (gif_bytes < 2) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
}
- break;
+ gif_data += 2 + gif_data[1];
}
- }
- if (pixels == 0) {
- ret = GIF_OK;
+ /* Repeatedly skip blocks until we get a zero block or run out
+ * of data. This data is ignored by this gif decoder. */
+ while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) {
+ gif_data += gif_data[0] + 1;
+ if (gif_data >= gif_end) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
+ }
+ gif_data++;
+ gif_bytes = gif_end - gif_data;
}
- return ret;
-}
-
-static inline gif_result gif__decode(
- struct gif_animation *gif,
- struct gif_frame *frame,
- uint8_t minimum_code_size,
- uint32_t *restrict frame_data)
-{
- gif_result ret;
- uint32_t offset_x = frame->redraw_x;
- uint32_t offset_y = frame->redraw_y;
- uint32_t width = frame->redraw_width;
- uint32_t height = frame->redraw_height;
- uint32_t interlace = frame->flags & GIF_INTERLACE_MASK;
- uint32_t transparency_index = frame->transparency_index;
- uint32_t *restrict colour_table = gif->colour_table;
-
- if (interlace == false && width == gif->width && offset_x == 0) {
- ret = gif__decode_simple(gif, height, offset_y,
- minimum_code_size, transparency_index,
- frame_data, colour_table);
- } else {
- ret = gif__decode_complex(gif, width, height,
- offset_x, offset_y, interlace,
- minimum_code_size, transparency_index,
- frame_data, colour_table);
+ if (gif_data > gif_end) {
+ gif_data = gif_end;
}
- return ret;
+ /* Set buffer position and return */
+ gif->buffer_position = gif_data - gif->gif_data;
+ return GIF_OK;
}
/**
- * Restore a GIF to the background colour.
+ * Parse a GIF Image Descriptor.
*
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to clear, or NULL.
- * \param[in] bitmap The bitmap to clear the frame in.
+ * The format is:
+ *
+ * +0 CHAR Image Separator (0x2c)
+ * +1 SHORT Image Left Position
+ * +3 SHORT Image Top Position
+ * +5 SHORT Width
+ * +7 SHORT Height
+ * +9 CHAR __Packed Fields__
+ * 1BIT Local Colour Table Flag
+ * 1BIT Interlace Flag
+ * 1BIT Sort Flag
+ * 2BITS Reserved
+ * 3BITS Size of Local Colour Table
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to parse an image descriptor for.
+ * \return GIF_OK on success, appropriate error otherwise.
*/
-static void gif__restore_bg(
+static gif_result gif__parse_image_descriptor(
struct gif_animation *gif,
struct gif_frame *frame,
- uint32_t *bitmap)
+ bool decode)
{
- if (frame == NULL) {
- memset(bitmap, GIF_TRANSPARENT_COLOUR,
- gif->width * gif->height * sizeof(*bitmap));
- } else {
- uint32_t offset_x = frame->redraw_x;
- uint32_t offset_y = frame->redraw_y;
- uint32_t width = frame->redraw_width;
- uint32_t height = frame->redraw_height;
+ const uint8_t *data = gif->gif_data + gif->buffer_position;
+ size_t len = gif->buffer_size - gif->buffer_position;
+ enum {
+ GIF_IMAGE_DESCRIPTOR_LEN = 10u,
+ GIF_IMAGE_SEPARATOR = 0x2Cu,
+ };
- if (frame->display == false) {
- return;
- }
+ assert(gif != NULL);
+ assert(frame != NULL);
- if (frame->transparency) {
- for (uint32_t y = 0; y < height; y++) {
- uint32_t *scanline = bitmap + offset_x +
- (offset_y + y) * gif->width;
- memset(scanline, GIF_TRANSPARENT_COLOUR,
- width * sizeof(*bitmap));
- }
- } else {
- for (uint32_t y = 0; y < height; y++) {
- uint32_t *scanline = bitmap + offset_x +
- (offset_y + y) * gif->width;
- for (uint32_t x = 0; x < width; x++) {
- scanline[x] = gif->bg_colour;
- }
- }
+ if (len < GIF_IMAGE_DESCRIPTOR_LEN) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
+
+ if (decode) {
+ unsigned x, y, w, h;
+
+ if (data[0] != GIF_IMAGE_SEPARATOR) {
+ return GIF_FRAME_DATA_ERROR;
}
+
+ x = data[1] | (data[2] << 8);
+ y = data[3] | (data[4] << 8);
+ w = data[5] | (data[6] << 8);
+ h = data[7] | (data[8] << 8);
+ frame->flags = data[9];
+
+ frame->redraw_x = x;
+ frame->redraw_y = y;
+ frame->redraw_width = w;
+ frame->redraw_height = h;
+
+ /* Frame size may have grown. */
+ gif->width = (x + w > gif->width ) ? x + w : gif->width;
+ gif->height = (y + h > gif->height) ? y + h : gif->height;
}
+
+ gif->buffer_position += GIF_IMAGE_DESCRIPTOR_LEN;
+ return GIF_OK;
}
-static gif_result gif__update_bitmap(
+/**
+ * Get a frame's colour table.
+ *
+ * Sets up gif->colour_table for the frame.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to get the colour table for.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_colour_table(
struct gif_animation *gif,
struct gif_frame *frame,
- uint8_t minimum_code_size,
- uint32_t frame_idx)
+ bool decode)
{
- gif_result ret;
- uint32_t *bitmap;
-
- gif->decoded_frame = frame_idx;
-
- bitmap = gif__bitmap_get(gif);
- if (bitmap == NULL) {
- return GIF_INSUFFICIENT_MEMORY;
- }
-
- /* Handle any bitmap clearing/restoration required before decoding this
- * frame. */
- if (frame_idx == 0 || gif->decoded_frame == GIF_INVALID_FRAME) {
- gif__restore_bg(gif, NULL, bitmap);
-
- } else {
- struct gif_frame *prev = &gif->frames[frame_idx - 1];
+ const uint8_t *data = gif->gif_data + gif->buffer_position;
+ size_t len = gif->buffer_size - gif->buffer_position;
+ unsigned colour_table_size;
- if (prev->disposal_method == GIF_DISPOSAL_RESTORE_BG) {
- gif__restore_bg(gif, prev, bitmap);
+ assert(gif != NULL);
+ assert(frame != NULL);
- } else if (prev->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
- ret = gif__recover_frame(gif, bitmap);
- if (ret != GIF_OK) {
- gif__restore_bg(gif, prev, bitmap);
- }
- }
+ if ((frame->flags & GIF_COLOUR_TABLE_MASK) == 0) {
+ gif->colour_table = gif->global_colour_table;
+ return GIF_OK;
}
- if (frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
- /* Store the previous frame for later restoration */
- gif__record_frame(gif, bitmap);
+ colour_table_size = 2 << (frame->flags & GIF_COLOUR_TABLE_SIZE_MASK);
+ if (len < colour_table_size * 3) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
}
- ret = gif__decode(gif, frame, minimum_code_size, bitmap);
+ if (decode) {
+ int count = colour_table_size;
+ uint8_t *entry = (uint8_t *)gif->local_colour_table;
- gif__bitmap_modified(gif);
+ while (count--) {
+ /* Gif colour map contents are r,g,b.
+ *
+ * We want to pack them bytewise into the
+ * colour table, such that the red component
+ * is in byte 0 and the alpha component is in
+ * byte 3.
+ */
- if (frame->virgin) {
- frame->opaque = gif__bitmap_get_opaque(gif);
- frame->virgin = false;
+ *entry++ = *data++; /* r */
+ *entry++ = *data++; /* g */
+ *entry++ = *data++; /* b */
+ *entry++ = 0xff; /* a */
+ }
}
- gif__bitmap_set_opaque(gif, frame);
- return ret;
+ gif->buffer_position += colour_table_size * 3;
+ gif->colour_table = gif->local_colour_table;
+ return GIF_OK;
}
/**
@@ -1100,6 +923,180 @@ static gif_result gif__parse_image_data(
return ret;
}
+static struct gif_frame *gif__get_frame(
+ struct gif_animation *gif,
+ uint32_t frame_idx)
+{
+ struct gif_frame *frame;
+
+ if (gif->frame_holders > frame_idx) {
+ frame = &gif->frames[frame_idx];
+ } else {
+ /* Allocate more memory */
+ size_t count = frame_idx + 1;
+ struct gif_frame *temp;
+
+ temp = realloc(gif->frames, count * sizeof(*frame));
+ if (temp == NULL) {
+ return NULL;
+ }
+ gif->frames = temp;
+ gif->frame_holders = count;
+
+ frame = &gif->frames[frame_idx];
+
+ frame->transparency = false;
+ frame->transparency_index = GIF_NO_TRANSPARENCY;
+ frame->frame_pointer = gif->buffer_position;
+ frame->redraw_required = false;
+ frame->disposal_method = 0;
+ frame->frame_delay = 100;
+ frame->display = false;
+ frame->virgin = true;
+ }
+
+ return frame;
+}
+
+/**
+ * Attempts to initialise the next frame
+ *
+ * \param[in] gif The animation context
+ * \param[in] frame_idx The frame number to decode.
+ * \return error code
+ * - GIF_INSUFFICIENT_DATA for insufficient data to do anything
+ * - GIF_FRAME_DATA_ERROR for GIF frame data error
+ * - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
+ * - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
+ * - GIF_DATA_ERROR for GIF error (invalid frame header)
+ * - GIF_OK for successful decoding
+ * - GIF_WORKING for successful decoding if more frames are expected
+*/
+static gif_result gif_initialise_frame(
+ struct gif_animation *gif,
+ uint32_t frame_idx)
+{
+ gif_result ret;
+ struct gif_frame *frame;
+ uint8_t *gif_data, *gif_end;
+ int gif_bytes;
+ uint32_t block_size;
+
+ /* Get our buffer position etc. */
+ gif_data = (uint8_t *)(gif->gif_data + gif->buffer_position);
+ gif_end = (uint8_t *)(gif->gif_data + gif->buffer_size);
+ gif_bytes = (gif_end - gif_data);
+
+ /* Check if we've finished */
+ if ((gif_bytes > 0) && (gif_data[0] == GIF_TRAILER)) {
+ return GIF_OK;
+ }
+
+ /* Check if there is enough data remaining. The shortest block of data
+ * is a 4-byte comment extension + 1-byte block terminator + 1-byte gif
+ * trailer
+ */
+ if (gif_bytes < 6) {
+ return GIF_INSUFFICIENT_DATA;
+ }
+
+ /* We could theoretically get some junk data that gives us millions of
+ * frames, so we ensure that we don't have a silly number
+ */
+ if (frame_idx > 4096) {
+ return GIF_FRAME_DATA_ERROR;
+ }
+
+ frame = gif__get_frame(gif, frame_idx);
+ if (frame == NULL) {
+ return GIF_INSUFFICIENT_MEMORY;
+ }
+
+ /* We pretend to initialise the frames, but really we just skip over
+ * all the data contained within. This is all basically a cut down
+ * version of gif_decode_frame that doesn't have any of the LZW bits in
+ * it.
+ */
+
+ /* Initialise any extensions */
+ gif->buffer_position = gif_data - gif->gif_data;
+ ret = gif__parse_frame_extensions(gif, &gif->frames[frame_idx], true);
+ if (ret != GIF_OK) {
+ return ret;
+ }
+
+ ret = gif__parse_image_descriptor(gif, &gif->frames[frame_idx], true);
+ if (ret != GIF_OK) {
+ return ret;
+ }
+
+ ret = gif__parse_colour_table(gif, &gif->frames[frame_idx], false);
+ if (ret != GIF_OK) {
+ return ret;
+ }
+ gif_data = gif->gif_data + gif->buffer_position;
+ gif_bytes = (gif_end - gif_data);
+
+ /* Move our data onwards and remember we've got a bit of this frame */
+ gif->frame_count_partial = frame_idx + 1;
+
+ /* Ensure we have a correct code size */
+ if (gif_bytes < 1) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
+ if (gif_data[0] >= LZW_CODE_MAX) {
+ return GIF_DATA_ERROR;
+ }
+
+ /* Move our pointer to the actual image data */
+ gif_data++;
+ --gif_bytes;
+
+ /* Repeatedly skip blocks until we get a zero block or run out of data
+ * These blocks of image data are processed later by gif_decode_frame()
+ */
+ block_size = 0;
+ while (block_size != 1) {
+ if (gif_bytes < 1) return GIF_INSUFFICIENT_FRAME_DATA;
+ block_size = gif_data[0] + 1;
+ /* Check if the frame data runs off the end of the file */
+ if ((int)(gif_bytes - block_size) < 0) {
+ /* Try to recover by signaling the end of the gif.
+ * Once we get garbage data, there is no logical way to
+ * determine where the next frame is. It's probably
+ * better to partially load the gif than not at all.
+ */
+ if (gif_bytes >= 2) {
+ gif_data[0] = 0;
+ gif_data[1] = GIF_TRAILER;
+ gif_bytes = 1;
+ ++gif_data;
+ break;
+ } else {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
+ } else {
+ gif_bytes -= block_size;
+ gif_data += block_size;
+ }
+ }
+
+ /* Add the frame and set the display flag */
+ gif->buffer_position = gif_data - gif->gif_data;
+ gif->frame_count = frame_idx + 1;
+ gif->frames[frame_idx].display = true;
+
+ /* Check if we've finished */
+ if (gif_bytes < 1) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ } else {
+ if (gif_data[0] == GIF_TRAILER) {
+ return GIF_OK;
+ }
+ }
+ return GIF_WORKING;
+}
+
/**
* decode a gif frame
*
@@ -1107,9 +1104,9 @@ static gif_result gif__parse_image_data(
* \param frame The frame number to decode.
* \param clear_image flag for image data being cleared instead of plotted.
*/
-static gif_result
-gif_internal_decode_frame(gif_animation *gif,
- uint32_t frame_idx)
+static gif_result gif_internal_decode_frame(
+ struct gif_animation *gif,
+ uint32_t frame_idx)
{
gif_result ret;
uint8_t *gif_data;
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=170edd84170a8de580...
commit 170edd84170a8de5801dcf5c022a562374f83b2f
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Split out image parsing.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index b527953..3b1df7e 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -960,6 +960,8 @@ static gif_result gif__update_bitmap(
gif_result ret;
uint32_t *bitmap;
+ gif->decoded_frame = frame_idx;
+
bitmap = gif__bitmap_get(gif);
if (bitmap == NULL) {
return GIF_INSUFFICIENT_MEMORY;
@@ -1003,6 +1005,102 @@ static gif_result gif__update_bitmap(
}
/**
+ * Parse the image data for a gif frame.
+ *
+ * Sets up gif->colour_table for the frame.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to get the colour table for.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_image_data(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ bool decode)
+{
+ uint8_t *data = gif->gif_data + gif->buffer_position;
+ size_t len = gif->buffer_size - gif->buffer_position;
+ uint32_t frame_idx = frame - gif->frames;
+ uint8_t minimum_code_size;
+ gif_result ret;
+
+ assert(gif != NULL);
+ assert(frame != NULL);
+
+ if (!decode) {
+ gif->frame_count_partial = frame_idx + 1;
+ }
+
+ /* Ensure sufficient data remains. A gif trailer or a minimum lzw code
+ * followed by a gif trailer is treated as OK, although without any
+ * image data. */
+ switch (len) {
+ case 2: if (data[1] == GIF_TRAILER) return GIF_OK;
+ /* Fall through. */
+ case 1: if (data[0] == GIF_TRAILER) return GIF_OK;
+ /* Fall through. */
+ case 0: return GIF_INSUFFICIENT_FRAME_DATA;
+ }
+
+ minimum_code_size = data[0];
+ if (minimum_code_size >= LZW_CODE_MAX) {
+ return GIF_DATA_ERROR;
+ }
+ gif->buffer_position++;
+ data++;
+ len--;
+
+ if (decode) {
+ ret = gif__update_bitmap(gif, frame, minimum_code_size,
+ frame_idx);
+ } else {
+ uint32_t block_size = 0;
+
+ while (block_size != 1) {
+ if (len < 1) return GIF_INSUFFICIENT_FRAME_DATA;
+ block_size = data[0] + 1;
+ /* Check if the frame data runs off the end of the file */
+ if ((int)(len - block_size) < 0) {
+ /* Try to recover by signaling the end of the gif.
+ * Once we get garbage data, there is no logical way to
+ * determine where the next frame is. It's probably
+ * better to partially load the gif than not at all.
+ */
+ if (len >= 2) {
+ data[0] = 0;
+ data[1] = GIF_TRAILER;
+ len = 1;
+ data++;
+ break;
+ } else {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
+ } else {
+ len -= block_size;
+ data += block_size;
+ }
+ }
+
+ gif->buffer_position = data - gif->gif_data;
+ gif->frame_count = frame_idx + 1;
+ gif->frames[frame_idx].display = true;
+
+ /* Check if we've finished */
+ if (len < 1) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ } else {
+ if (data[0] == GIF_TRAILER) {
+ return GIF_OK;
+ }
+ }
+
+ return GIF_WORKING;
+ }
+
+ return ret;
+}
+
+/**
* decode a gif frame
*
* \param gif gif animation context.
@@ -1014,9 +1112,8 @@ gif_internal_decode_frame(gif_animation *gif,
uint32_t frame_idx)
{
gif_result ret;
+ uint8_t *gif_data;
struct gif_frame *frame;
- uint8_t *gif_data, *gif_end;
- int gif_bytes;
uint32_t save_buffer_position;
/* Ensure the frame is in range to decode */
@@ -1041,8 +1138,6 @@ gif_internal_decode_frame(gif_animation *gif,
/* Get the start of our frame data and the end of the GIF data */
gif_data = gif->gif_data + frame->frame_pointer;
- gif_end = gif->gif_data + gif->buffer_size;
- gif_bytes = (gif_end - gif_data);
/* Save the buffer position */
save_buffer_position = gif->buffer_position;
@@ -1061,44 +1156,14 @@ gif_internal_decode_frame(gif_animation *gif,
ret = gif__parse_colour_table(gif, frame, true);
if (ret != GIF_OK) {
- return ret;
- }
- gif_data = gif->gif_data + gif->buffer_position;
- gif_bytes = (gif_end - gif_data);
-
- /* Ensure sufficient data remains */
- if (gif_bytes < 1) {
- ret = GIF_INSUFFICIENT_FRAME_DATA;
- goto gif_decode_frame_exit;
- }
-
- /* check for an end marker */
- if (gif_data[0] == GIF_TRAILER) {
- ret = GIF_OK;
- goto gif_decode_frame_exit;
- }
-
- /* Ensure we have enough data for a 1-byte LZW code size +
- * 1-byte gif trailer
- */
- if (gif_bytes < 2) {
- ret = GIF_INSUFFICIENT_FRAME_DATA;
goto gif_decode_frame_exit;
}
- /* If we only have a 1-byte LZW code size + 1-byte gif trailer,
- * we're finished
- */
- if ((gif_bytes == 2) && (gif_data[1] == GIF_TRAILER)) {
- ret = GIF_OK;
+ ret = gif__parse_image_data(gif, frame, true);
+ if (ret != GIF_OK) {
goto gif_decode_frame_exit;
}
- gif->decoded_frame = frame_idx;
- gif->buffer_position = (gif_data - gif->gif_data) + 1;
-
- ret = gif__update_bitmap(gif, frame, gif_data[0], frame_idx);
-
gif_decode_frame_exit:
/* Restore the buffer position */
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=1e4a781b695992fbe0...
commit 1e4a781b695992fbe09459baa3518637fba96223
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Bitmaps: Encapsulate bitmap handling.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 97d5b79..b527953 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -618,10 +618,82 @@ static gif_result gif_error_from_lzw(lzw_result l_res)
return g_res[l_res];
}
-static void gif__record_frame(gif_animation *gif)
+/**
+ * Helper to get the rendering bitmap for a gif.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \return Client pixel buffer for rendering into.
+ */
+static inline uint32_t* gif__bitmap_get(
+ struct gif_animation *gif)
+{
+ gif_result ret;
+
+ /* Make sure we have a buffer to decode to. */
+ ret = gif_initialise_sprite(gif, gif->width, gif->height);
+ if (ret != GIF_OK) {
+ return NULL;
+ }
+
+ /* Get the frame data */
+ assert(gif->bitmap_callbacks.bitmap_get_buffer);
+ return (uint32_t *)gif->bitmap_callbacks.bitmap_get_buffer(
+ gif->frame_image);
+}
+
+/**
+ * Helper to tell the client that their bitmap was modified.
+ *
+ * \param[in] gif The gif object we're decoding.
+ */
+static inline void gif__bitmap_modified(
+ const struct gif_animation *gif)
+{
+ if (gif->bitmap_callbacks.bitmap_modified) {
+ gif->bitmap_callbacks.bitmap_modified(gif->frame_image);
+ }
+}
+
+/**
+ * Helper to tell the client that whether the bitmap is opaque.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frmae The frame that has been decoded.
+ */
+static inline void gif__bitmap_set_opaque(
+ const struct gif_animation *gif,
+ const struct gif_frame *frame)
+{
+ if (gif->bitmap_callbacks.bitmap_set_opaque) {
+ gif->bitmap_callbacks.bitmap_set_opaque(
+ gif->frame_image, frame->opaque);
+ }
+}
+
+/**
+ * Helper to get the client to determine if the bitmap is opaque.
+ *
+ * \todo: We don't really need to get the client to do this for us.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \return true if the bitmap is opaque, false otherwise.
+ */
+static inline bool gif__bitmap_get_opaque(
+ const struct gif_animation *gif)
+{
+ if (gif->bitmap_callbacks.bitmap_test_opaque) {
+ return gif->bitmap_callbacks.bitmap_test_opaque(
+ gif->frame_image);
+ }
+
+ return false;
+}
+
+static void gif__record_frame(
+ struct gif_animation *gif,
+ const uint32_t *bitmap)
{
bool need_alloc = gif->prev_frame == NULL;
- const uint32_t *frame_data;
uint32_t *prev_frame;
if (gif->decoded_frame == GIF_INVALID_FRAME ||
@@ -630,9 +702,8 @@ static void gif__record_frame(gif_animation *gif)
return;
}
- assert(gif->bitmap_callbacks.bitmap_get_buffer);
- frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
- if (!frame_data) {
+ bitmap = gif__bitmap_get(gif);
+ if (bitmap == NULL) {
return;
}
@@ -651,7 +722,7 @@ static void gif__record_frame(gif_animation *gif)
prev_frame = gif->prev_frame;
}
- memcpy(prev_frame, frame_data, gif->width * gif->height * 4);
+ memcpy(prev_frame, bitmap, gif->width * gif->height * 4);
gif->prev_frame = prev_frame;
gif->prev_width = gif->width;
@@ -659,27 +730,22 @@ static void gif__record_frame(gif_animation *gif)
gif->prev_index = gif->decoded_frame;
}
-static gif_result gif__recover_frame(const gif_animation *gif)
+static gif_result gif__recover_frame(
+ const struct gif_animation *gif,
+ uint32_t *bitmap)
{
const uint32_t *prev_frame = gif->prev_frame;
unsigned height = gif->height < gif->prev_height ? gif->height : gif->prev_height;
unsigned width = gif->width < gif->prev_width ? gif->width : gif->prev_width;
- uint32_t *frame_data;
if (prev_frame == NULL) {
return GIF_FRAME_DATA_ERROR;
}
- assert(gif->bitmap_callbacks.bitmap_get_buffer);
- frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
- if (!frame_data) {
- return GIF_INSUFFICIENT_MEMORY;
- }
-
for (unsigned y = 0; y < height; y++) {
- memcpy(frame_data, prev_frame, width * 4);
+ memcpy(bitmap, prev_frame, width * 4);
- frame_data += gif->width;
+ bitmap += gif->width;
prev_frame += gif->prev_width;
}
@@ -885,6 +951,57 @@ static void gif__restore_bg(
}
}
+static gif_result gif__update_bitmap(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ uint8_t minimum_code_size,
+ uint32_t frame_idx)
+{
+ gif_result ret;
+ uint32_t *bitmap;
+
+ bitmap = gif__bitmap_get(gif);
+ if (bitmap == NULL) {
+ return GIF_INSUFFICIENT_MEMORY;
+ }
+
+ /* Handle any bitmap clearing/restoration required before decoding this
+ * frame. */
+ if (frame_idx == 0 || gif->decoded_frame == GIF_INVALID_FRAME) {
+ gif__restore_bg(gif, NULL, bitmap);
+
+ } else {
+ struct gif_frame *prev = &gif->frames[frame_idx - 1];
+
+ if (prev->disposal_method == GIF_DISPOSAL_RESTORE_BG) {
+ gif__restore_bg(gif, prev, bitmap);
+
+ } else if (prev->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
+ ret = gif__recover_frame(gif, bitmap);
+ if (ret != GIF_OK) {
+ gif__restore_bg(gif, prev, bitmap);
+ }
+ }
+ }
+
+ if (frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
+ /* Store the previous frame for later restoration */
+ gif__record_frame(gif, bitmap);
+ }
+
+ ret = gif__decode(gif, frame, minimum_code_size, bitmap);
+
+ gif__bitmap_modified(gif);
+
+ if (frame->virgin) {
+ frame->opaque = gif__bitmap_get_opaque(gif);
+ frame->virgin = false;
+ }
+ gif__bitmap_set_opaque(gif, frame);
+
+ return ret;
+}
+
/**
* decode a gif frame
*
@@ -900,7 +1017,6 @@ gif_internal_decode_frame(gif_animation *gif,
struct gif_frame *frame;
uint8_t *gif_data, *gif_end;
int gif_bytes;
- uint32_t *frame_data = 0; // Set to 0 for no warnings
uint32_t save_buffer_position;
/* Ensure the frame is in range to decode */
@@ -962,20 +1078,6 @@ gif_internal_decode_frame(gif_animation *gif,
goto gif_decode_frame_exit;
}
- /* Make sure we have a buffer to decode to. */
- if (gif_initialise_sprite(gif, gif->width, gif->height)) {
- ret = GIF_INSUFFICIENT_MEMORY;
- goto gif_decode_frame_exit;
- }
-
- /* Get the frame data */
- assert(gif->bitmap_callbacks.bitmap_get_buffer);
- frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
- if (!frame_data) {
- ret = GIF_INSUFFICIENT_MEMORY;
- goto gif_decode_frame_exit;
- }
-
/* Ensure we have enough data for a 1-byte LZW code size +
* 1-byte gif trailer
*/
@@ -992,59 +1094,13 @@ gif_internal_decode_frame(gif_animation *gif,
goto gif_decode_frame_exit;
}
- /* Handle any bitmap clearing/restoration required before decoding this
- * frame. */
- if (frame_idx == 0 || gif->decoded_frame == GIF_INVALID_FRAME) {
- gif__restore_bg(gif, NULL, frame_data);
-
- } else {
- struct gif_frame *prev = &gif->frames[frame_idx - 1];
-
- if (prev->disposal_method == GIF_DISPOSAL_RESTORE_BG) {
- gif__restore_bg(gif, prev, frame_data);
-
- } else if (prev->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
- /*
- * If the previous frame's disposal method requires we
- * restore the previous image, restore our saved image.
- */
- ret = gif__recover_frame(gif);
- if (ret != GIF_OK) {
- gif__restore_bg(gif, prev, frame_data);
- }
- }
- }
-
- if (frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
- /* Store the previous frame for later restoration */
- gif__record_frame(gif);
- }
-
gif->decoded_frame = frame_idx;
gif->buffer_position = (gif_data - gif->gif_data) + 1;
- ret = gif__decode(gif, frame, gif_data[0], frame_data);
+ ret = gif__update_bitmap(gif, frame, gif_data[0], frame_idx);
gif_decode_frame_exit:
- if (gif->bitmap_callbacks.bitmap_modified) {
- gif->bitmap_callbacks.bitmap_modified(gif->frame_image);
- }
-
- /* Check if we should test for optimisation */
- if (frame->virgin) {
- if (gif->bitmap_callbacks.bitmap_test_opaque) {
- frame->opaque = gif->bitmap_callbacks.bitmap_test_opaque(gif->frame_image);
- } else {
- frame->opaque = false;
- }
- frame->virgin = false;
- }
-
- if (gif->bitmap_callbacks.bitmap_set_opaque) {
- gif->bitmap_callbacks.bitmap_set_opaque(gif->frame_image, frame->opaque);
- }
-
/* Restore the buffer position */
gif->buffer_position = save_buffer_position;
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=c17ab37852fadbc6fe...
commit c17ab37852fadbc6fe7ae80a969aa408f3d809b1
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Unify frame clearing functions.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index fb1c675..97d5b79 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -842,63 +842,50 @@ static inline gif_result gif__decode(
}
/**
- * Clear a gif frame.
+ * Restore a GIF to the background colour.
*
* \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to clear.
+ * \param[in] frame The frame to clear, or NULL.
* \param[in] bitmap The bitmap to clear the frame in.
*/
-static void gif_clear_frame(
+static void gif__restore_bg(
struct gif_animation *gif,
struct gif_frame *frame,
uint32_t *bitmap)
{
- uint32_t width;
- uint32_t height;
- uint32_t offset_x;
- uint32_t offset_y;
-
- assert(frame->disposal_method == GIF_DISPOSAL_RESTORE_BG);
-
- /* Ensure this frame is supposed to be decoded */
- if (frame->display == false) {
- return;
- }
+ if (frame == NULL) {
+ memset(bitmap, GIF_TRANSPARENT_COLOUR,
+ gif->width * gif->height * sizeof(*bitmap));
+ } else {
+ uint32_t offset_x = frame->redraw_x;
+ uint32_t offset_y = frame->redraw_y;
+ uint32_t width = frame->redraw_width;
+ uint32_t height = frame->redraw_height;
- offset_x = frame->redraw_x;
- offset_y = frame->redraw_y;
- width = frame->redraw_width;
- height = frame->redraw_height;
+ if (frame->display == false) {
+ return;
+ }
- /* Clear our frame */
- for (uint32_t y = 0; y < height; y++) {
- uint32_t *scanline;
- scanline = bitmap + offset_x + ((offset_y + y) * gif->width);
if (frame->transparency) {
- memset(scanline, GIF_TRANSPARENT_COLOUR, width * 4);
+ for (uint32_t y = 0; y < height; y++) {
+ uint32_t *scanline = bitmap + offset_x +
+ (offset_y + y) * gif->width;
+ memset(scanline, GIF_TRANSPARENT_COLOUR,
+ width * sizeof(*bitmap));
+ }
} else {
- for (uint32_t x = 0; x < width; x++) {
- scanline[x] = gif->bg_colour;
+ for (uint32_t y = 0; y < height; y++) {
+ uint32_t *scanline = bitmap + offset_x +
+ (offset_y + y) * gif->width;
+ for (uint32_t x = 0; x < width; x++) {
+ scanline[x] = gif->bg_colour;
+ }
}
}
}
}
/**
- * Wipe bitmap to transparent.
- *
- * \param[in] gif The gif object we're decoding.
- * \param[in] bitmap The bitmap to wipe.
- */
-static inline void gif__wipe_bitmap(
- const struct gif_animation *gif,
- uint32_t *bitmap)
-{
- memset((char*)bitmap, GIF_TRANSPARENT_COLOUR,
- gif->width * gif->height * sizeof(*bitmap));
-}
-
-/**
* decode a gif frame
*
* \param gif gif animation context.
@@ -1008,13 +995,13 @@ gif_internal_decode_frame(gif_animation *gif,
/* Handle any bitmap clearing/restoration required before decoding this
* frame. */
if (frame_idx == 0 || gif->decoded_frame == GIF_INVALID_FRAME) {
- gif__wipe_bitmap(gif, frame_data);
+ gif__restore_bg(gif, NULL, frame_data);
} else {
struct gif_frame *prev = &gif->frames[frame_idx - 1];
if (prev->disposal_method == GIF_DISPOSAL_RESTORE_BG) {
- gif_clear_frame(gif, prev, frame_data);
+ gif__restore_bg(gif, prev, frame_data);
} else if (prev->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
/*
@@ -1023,7 +1010,7 @@ gif_internal_decode_frame(gif_animation *gif,
*/
ret = gif__recover_frame(gif);
if (ret != GIF_OK) {
- gif__wipe_bitmap(gif, frame_data);
+ gif__restore_bg(gif, prev, frame_data);
}
}
}
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=0f7bcedeedff2cdee8...
commit 0f7bcedeedff2cdee82644213741b5b96d655d92
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Align disposal methods with spec.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 9001941..fb1c675 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -45,12 +45,15 @@
/** No transparency */
#define GIF_NO_TRANSPARENCY (0xFFFFFFFFu)
-/* GIF Flags */
-#define GIF_FRAME_COMBINE 1
-#define GIF_FRAME_CLEAR 2
-#define GIF_FRAME_RESTORE 3
-#define GIF_FRAME_QUIRKS_RESTORE 4
+enum gif_disposal {
+ GIF_DISPOSAL_UNSPECIFIED,
+ GIF_DISPOSAL_NONE,
+ GIF_DISPOSAL_RESTORE_BG,
+ GIF_DISPOSAL_RESTORE_PREV,
+ GIF_DISPOSAL_RESTORE_QUIRK, /**< Alias for GIF_DISPOSAL_RESTORE_PREV. */
+};
+/* GIF Flags */
#define GIF_INTERLACE_MASK 0x40
#define GIF_COLOUR_TABLE_MASK 0x80
#define GIF_COLOUR_TABLE_SIZE_MASK 0x07
@@ -138,15 +141,15 @@ static gif_result gif__parse_extension_graphic_control(
* believe some (older?) software may even actually
* export this way. We handle this as a type of
* "quirks" mode. */
- if (frame->disposal_method == GIF_FRAME_QUIRKS_RESTORE) {
- frame->disposal_method = GIF_FRAME_RESTORE;
+ if (frame->disposal_method == GIF_DISPOSAL_RESTORE_QUIRK) {
+ frame->disposal_method = GIF_DISPOSAL_RESTORE_PREV;
}
/* if we are clearing the background then we need to
* redraw enough to cover the previous frame too. */
frame->redraw_required =
- frame->disposal_method == GIF_FRAME_CLEAR ||
- frame->disposal_method == GIF_FRAME_RESTORE;
+ frame->disposal_method == GIF_DISPOSAL_RESTORE_BG ||
+ frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV;
return GIF_OK;
}
@@ -855,7 +858,7 @@ static void gif_clear_frame(
uint32_t offset_x;
uint32_t offset_y;
- assert(frame->disposal_method == GIF_FRAME_CLEAR);
+ assert(frame->disposal_method == GIF_DISPOSAL_RESTORE_BG);
/* Ensure this frame is supposed to be decoded */
if (frame->display == false) {
@@ -1010,10 +1013,10 @@ gif_internal_decode_frame(gif_animation *gif,
} else {
struct gif_frame *prev = &gif->frames[frame_idx - 1];
- if (prev->disposal_method == GIF_FRAME_CLEAR) {
+ if (prev->disposal_method == GIF_DISPOSAL_RESTORE_BG) {
gif_clear_frame(gif, prev, frame_data);
- } else if (prev->disposal_method == GIF_FRAME_RESTORE) {
+ } else if (prev->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
/*
* If the previous frame's disposal method requires we
* restore the previous image, restore our saved image.
@@ -1025,7 +1028,7 @@ gif_internal_decode_frame(gif_animation *gif,
}
}
- if (frame->disposal_method == GIF_FRAME_RESTORE) {
+ if (frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
/* Store the previous frame for later restoration */
gif__record_frame(gif);
}
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=22b5b8f627a770172f...
commit 22b5b8f627a770172fbcc9e37656dcf40836f5df
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Clear can't fail.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 973c592..9001941 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -844,9 +844,8 @@ static inline gif_result gif__decode(
* \param[in] gif The gif object we're decoding.
* \param[in] frame The frame to clear.
* \param[in] bitmap The bitmap to clear the frame in.
- * \return GIF_OK on success, appropriate error otherwise.
*/
-static gif_result gif_clear_frame(
+static void gif_clear_frame(
struct gif_animation *gif,
struct gif_frame *frame,
uint32_t *bitmap)
@@ -860,7 +859,7 @@ static gif_result gif_clear_frame(
/* Ensure this frame is supposed to be decoded */
if (frame->display == false) {
- return GIF_OK;
+ return;
}
offset_x = frame->redraw_x;
@@ -880,8 +879,6 @@ static gif_result gif_clear_frame(
}
}
}
-
- return GIF_OK;
}
/**
@@ -1014,10 +1011,8 @@ gif_internal_decode_frame(gif_animation *gif,
struct gif_frame *prev = &gif->frames[frame_idx - 1];
if (prev->disposal_method == GIF_FRAME_CLEAR) {
- ret = gif_clear_frame(gif, prev, frame_data);
- if (ret != GIF_OK) {
- goto gif_decode_frame_exit;
- }
+ gif_clear_frame(gif, prev, frame_data);
+
} else if (prev->disposal_method == GIF_FRAME_RESTORE) {
/*
* If the previous frame's disposal method requires we
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=1587b6a1a936ba11df...
commit 1587b6a1a936ba11dfc951d07766562dbb4e8cf9
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Helper to get the frame structure for given frame index.
Removes weird special casing of first frame.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 6ee7ab3..973c592 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -408,6 +408,41 @@ static gif_result gif__parse_colour_table(
return GIF_OK;
}
+static struct gif_frame *gif__get_frame(
+ struct gif_animation *gif,
+ uint32_t frame_idx)
+{
+ struct gif_frame *frame;
+
+ if (gif->frame_holders > frame_idx) {
+ frame = &gif->frames[frame_idx];
+ } else {
+ /* Allocate more memory */
+ size_t count = frame_idx + 1;
+ struct gif_frame *temp;
+
+ temp = realloc(gif->frames, count * sizeof(*frame));
+ if (temp == NULL) {
+ return NULL;
+ }
+ gif->frames = temp;
+ gif->frame_holders = count;
+
+ frame = &gif->frames[frame_idx];
+
+ frame->transparency = false;
+ frame->transparency_index = GIF_NO_TRANSPARENCY;
+ frame->frame_pointer = gif->buffer_position;
+ frame->redraw_required = false;
+ frame->disposal_method = 0;
+ frame->frame_delay = 100;
+ frame->display = false;
+ frame->virgin = true;
+ }
+
+ return frame;
+}
+
/**
* Attempts to initialise the next frame
*
@@ -426,7 +461,7 @@ static gif_result gif_initialise_frame(gif_animation *gif,
uint32_t frame_idx)
{
gif_result ret;
- gif_frame *temp_buf;
+ struct gif_frame *frame;
uint8_t *gif_data, *gif_end;
int gif_bytes;
uint32_t block_size;
@@ -456,32 +491,9 @@ static gif_result gif_initialise_frame(gif_animation *gif,
return GIF_FRAME_DATA_ERROR;
}
- /* Get some memory to store our pointers in etc. */
- if (gif->frame_holders <= frame_idx) {
- /* Allocate more memory */
- temp_buf = (gif_frame *)realloc(gif->frames, (frame_idx + 1) * sizeof(gif_frame));
- if (temp_buf == NULL) {
- return GIF_INSUFFICIENT_MEMORY;
- }
- gif->frames = temp_buf;
- gif->frame_holders = frame_idx + 1;
- }
-
- /* Store our frame pointer. We would do it when allocating except we
- * start off with one frame allocated so we can always use realloc.
- */
- gif->frames[frame_idx].frame_pointer = gif->buffer_position;
- gif->frames[frame_idx].display = false;
- gif->frames[frame_idx].virgin = true;
- gif->frames[frame_idx].disposal_method = 0;
- gif->frames[frame_idx].transparency = false;
- gif->frames[frame_idx].transparency_index = GIF_NO_TRANSPARENCY;
- gif->frames[frame_idx].frame_delay = 100;
- gif->frames[frame_idx].redraw_required = false;
-
- /* Invalidate any previous decoding we have of this frame */
- if (gif->decoded_frame == (int)frame_idx) {
- gif->decoded_frame = GIF_INVALID_FRAME;
+ frame = gif__get_frame(gif, frame_idx);
+ if (frame == NULL) {
+ return GIF_INSUFFICIENT_MEMORY;
}
/* We pretend to initialise the frames, but really we just skip over
@@ -914,7 +926,10 @@ gif_internal_decode_frame(gif_animation *gif,
return GIF_OK;
}
- frame = &gif->frames[frame_idx];
+ frame = gif__get_frame(gif, frame_idx);
+ if (frame == NULL) {
+ return GIF_INSUFFICIENT_MEMORY;
+ }
/* Ensure this frame is supposed to be decoded */
if (frame->display == false) {
@@ -1098,6 +1113,7 @@ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data)
*/
gif->frame_image = NULL;
gif->frames = NULL;
+ gif->frame_holders = 0;
gif->local_colour_table = NULL;
gif->global_colour_table = NULL;
@@ -1192,13 +1208,6 @@ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data)
}
}
- /* Initialise enough workspace for a frame */
- if ((gif->frames = (gif_frame *)malloc(sizeof(gif_frame))) == NULL) {
- gif_finalise(gif);
- return GIF_INSUFFICIENT_MEMORY;
- }
- gif->frame_holders = 1;
-
/* Remember we've done this now */
gif->buffer_position = gif_data - gif->gif_data;
}
-----------------------------------------------------------------------
Summary of changes:
src/libnsgif.c | 1527 +++++++++++++++++++++++++++++---------------------------
1 file changed, 785 insertions(+), 742 deletions(-)
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 6ee7ab3..1280798 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -45,12 +45,15 @@
/** No transparency */
#define GIF_NO_TRANSPARENCY (0xFFFFFFFFu)
-/* GIF Flags */
-#define GIF_FRAME_COMBINE 1
-#define GIF_FRAME_CLEAR 2
-#define GIF_FRAME_RESTORE 3
-#define GIF_FRAME_QUIRKS_RESTORE 4
+enum gif_disposal {
+ GIF_DISPOSAL_UNSPECIFIED,
+ GIF_DISPOSAL_NONE,
+ GIF_DISPOSAL_RESTORE_BG,
+ GIF_DISPOSAL_RESTORE_PREV,
+ GIF_DISPOSAL_RESTORE_QUIRK, /**< Alias for GIF_DISPOSAL_RESTORE_PREV. */
+};
+/* GIF Flags */
#define GIF_INTERLACE_MASK 0x40
#define GIF_COLOUR_TABLE_MASK 0x80
#define GIF_COLOUR_TABLE_SIZE_MASK 0x07
@@ -67,6 +70,21 @@
/** standard GIF header size */
#define GIF_STANDARD_HEADER_SIZE 13
+static gif_result gif_error_from_lzw(lzw_result l_res)
+{
+ static const gif_result g_res[] = {
+ [LZW_OK] = GIF_OK,
+ [LZW_OK_EOD] = GIF_END_OF_FRAME,
+ [LZW_NO_MEM] = GIF_INSUFFICIENT_MEMORY,
+ [LZW_NO_DATA] = GIF_INSUFFICIENT_FRAME_DATA,
+ [LZW_EOI_CODE] = GIF_FRAME_DATA_ERROR,
+ [LZW_BAD_ICODE] = GIF_FRAME_DATA_ERROR,
+ [LZW_BAD_CODE] = GIF_FRAME_DATA_ERROR,
+ };
+ assert(l_res != LZW_BAD_PARAM);
+ assert(l_res != LZW_NO_COLOUR);
+ return g_res[l_res];
+}
/**
* Updates the sprite memory size
@@ -76,10 +94,10 @@
* \param height The height of the sprite
* \return GIF_INSUFFICIENT_MEMORY for a memory error GIF_OK for success
*/
-static gif_result
-gif_initialise_sprite(gif_animation *gif,
- uint32_t width,
- uint32_t height)
+static gif_result gif_initialise_sprite(
+ struct gif_animation *gif,
+ uint32_t width,
+ uint32_t height)
{
/* Already allocated? */
if (gif->frame_image) {
@@ -96,794 +114,918 @@ gif_initialise_sprite(gif_animation *gif,
}
/**
- * Parse the application extension
+ * Helper to get the rendering bitmap for a gif.
*
- * \param[in] frame The gif object we're decoding.
- * \param[in] data The data to decode.
- * \param[in] len Byte length of data.
- * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
- * GIF_OK for success.
+ * \param[in] gif The gif object we're decoding.
+ * \return Client pixel buffer for rendering into.
*/
-static gif_result gif__parse_extension_graphic_control(
- struct gif_frame *frame,
- uint8_t *data,
- size_t len)
+static inline uint32_t* gif__bitmap_get(
+ struct gif_animation *gif)
{
- /* 6-byte Graphic Control Extension is:
- *
- * +0 CHAR Graphic Control Label
- * +1 CHAR Block Size
- * +2 CHAR __Packed Fields__
- * 3BITS Reserved
- * 3BITS Disposal Method
- * 1BIT User Input Flag
- * 1BIT Transparent Color Flag
- * +3 SHORT Delay Time
- * +5 CHAR Transparent Color Index
- */
- if (len < 6) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
-
- frame->frame_delay = data[3] | (data[4] << 8);
- if (data[2] & GIF_TRANSPARENCY_MASK) {
- frame->transparency = true;
- frame->transparency_index = data[5];
- }
+ gif_result ret;
- frame->disposal_method = ((data[2] & GIF_DISPOSAL_MASK) >> 2);
- /* I have encountered documentation and GIFs in the
- * wild that use 0x04 to restore the previous frame,
- * rather than the officially documented 0x03. I
- * believe some (older?) software may even actually
- * export this way. We handle this as a type of
- * "quirks" mode. */
- if (frame->disposal_method == GIF_FRAME_QUIRKS_RESTORE) {
- frame->disposal_method = GIF_FRAME_RESTORE;
+ /* Make sure we have a buffer to decode to. */
+ ret = gif_initialise_sprite(gif, gif->width, gif->height);
+ if (ret != GIF_OK) {
+ return NULL;
}
- /* if we are clearing the background then we need to
- * redraw enough to cover the previous frame too. */
- frame->redraw_required =
- frame->disposal_method == GIF_FRAME_CLEAR ||
- frame->disposal_method == GIF_FRAME_RESTORE;
-
- return GIF_OK;
+ /* Get the frame data */
+ assert(gif->bitmap_callbacks.bitmap_get_buffer);
+ return (uint32_t *)gif->bitmap_callbacks.bitmap_get_buffer(
+ gif->frame_image);
}
/**
- * Parse the application extension
+ * Helper to tell the client that their bitmap was modified.
*
- * \param[in] gif The gif object we're decoding.
- * \param[in] data The data to decode.
- * \param[in] len Byte length of data.
- * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
- * GIF_OK for success.
+ * \param[in] gif The gif object we're decoding.
*/
-static gif_result gif__parse_extension_application(
- struct gif_animation *gif,
- uint8_t *data,
- size_t len)
+static inline void gif__bitmap_modified(
+ const struct gif_animation *gif)
{
- /* 14-byte+ Application Extension is:
- *
- * +0 CHAR Application Extension Label
- * +1 CHAR Block Size
- * +2 8CHARS Application Identifier
- * +10 3CHARS Appl. Authentication Code
- * +13 1-256 Application Data (Data sub-blocks)
- */
- if (len < 17) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ if (gif->bitmap_callbacks.bitmap_modified) {
+ gif->bitmap_callbacks.bitmap_modified(gif->frame_image);
}
+}
- if ((data[1] == 0x0b) &&
- (strncmp((const char *)data + 2, "NETSCAPE2.0", 11) == 0) &&
- (data[13] == 0x03) && (data[14] == 0x01)) {
- gif->loop_count = data[15] | (data[16] << 8);
+/**
+ * Helper to tell the client that whether the bitmap is opaque.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frmae The frame that has been decoded.
+ */
+static inline void gif__bitmap_set_opaque(
+ const struct gif_animation *gif,
+ const struct gif_frame *frame)
+{
+ if (gif->bitmap_callbacks.bitmap_set_opaque) {
+ gif->bitmap_callbacks.bitmap_set_opaque(
+ gif->frame_image, frame->opaque);
}
-
- return GIF_OK;
}
/**
- * Parse the frame's extensions
+ * Helper to get the client to determine if the bitmap is opaque.
*
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to parse extensions for.
- * \param[in] decode Whether to decode or skip over the extension.
- * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
- * GIF_OK for success.
+ * \todo: We don't really need to get the client to do this for us.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \return true if the bitmap is opaque, false otherwise.
*/
-static gif_result gif__parse_frame_extensions(
- struct gif_animation *gif,
- struct gif_frame *frame,
- bool decode)
+static inline bool gif__bitmap_get_opaque(
+ const struct gif_animation *gif)
{
- uint8_t *gif_data, *gif_end;
- int gif_bytes;
+ if (gif->bitmap_callbacks.bitmap_test_opaque) {
+ return gif->bitmap_callbacks.bitmap_test_opaque(
+ gif->frame_image);
+ }
- /* Get our buffer position etc. */
- gif_data = gif->gif_data + gif->buffer_position;
- gif_end = gif->gif_data + gif->buffer_size;
- gif_bytes = gif_end - gif_data;
+ return false;
+}
- /* Initialise the extensions */
- while (gif_bytes > 0 && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
- bool block_step = true;
- gif_result ret;
+static void gif__record_frame(
+ struct gif_animation *gif,
+ const uint32_t *bitmap)
+{
+ bool need_alloc = gif->prev_frame == NULL;
+ uint32_t *prev_frame;
- gif_data++;
- gif_bytes--;
+ if (gif->decoded_frame == GIF_INVALID_FRAME ||
+ gif->decoded_frame == gif->prev_index) {
+ /* No frame to copy, or already have this frame recorded. */
+ return;
+ }
- if (gif_bytes == 0) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
+ bitmap = gif__bitmap_get(gif);
+ if (bitmap == NULL) {
+ return;
+ }
- /* Switch on extension label */
- switch (gif_data[0]) {
- case GIF_EXTENSION_GRAPHIC_CONTROL:
- if (decode) {
- ret = gif__parse_extension_graphic_control(
- frame, gif_data, gif_bytes);
- if (ret != GIF_OK) {
- return ret;
- }
- }
- break;
+ if (gif->prev_frame != NULL &&
+ gif->width * gif->height > gif->prev_width * gif->prev_height) {
+ need_alloc = true;
+ }
- case GIF_EXTENSION_APPLICATION:
- if (decode) {
- ret = gif__parse_extension_application(
- gif, gif_data, gif_bytes);
- if (ret != GIF_OK) {
- return ret;
- }
- }
- break;
+ if (need_alloc) {
+ prev_frame = realloc(gif->prev_frame,
+ gif->width * gif->height * 4);
+ if (prev_frame == NULL) {
+ return;
+ }
+ } else {
+ prev_frame = gif->prev_frame;
+ }
- case GIF_EXTENSION_COMMENT:
- /* Move the pointer to the first data sub-block Skip 1
- * byte for the extension label. */
- ++gif_data;
- block_step = false;
- break;
+ memcpy(prev_frame, bitmap, gif->width * gif->height * 4);
- default:
- break;
- }
+ gif->prev_frame = prev_frame;
+ gif->prev_width = gif->width;
+ gif->prev_height = gif->height;
+ gif->prev_index = gif->decoded_frame;
+}
- if (block_step) {
- /* Move the pointer to the first data sub-block Skip 2
- * bytes for the extension label and size fields Skip
- * the extension size itself
- */
- if (gif_bytes < 2) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- gif_data += 2 + gif_data[1];
- }
+static gif_result gif__recover_frame(
+ const struct gif_animation *gif,
+ uint32_t *bitmap)
+{
+ const uint32_t *prev_frame = gif->prev_frame;
+ unsigned height = gif->height < gif->prev_height ? gif->height : gif->prev_height;
+ unsigned width = gif->width < gif->prev_width ? gif->width : gif->prev_width;
- /* Repeatedly skip blocks until we get a zero block or run out
- * of data. This data is ignored by this gif decoder. */
- while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) {
- gif_data += gif_data[0] + 1;
- if (gif_data >= gif_end) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- }
- gif_data++;
- gif_bytes = gif_end - gif_data;
+ if (prev_frame == NULL) {
+ return GIF_FRAME_DATA_ERROR;
}
- if (gif_data > gif_end) {
- gif_data = gif_end;
+ for (unsigned y = 0; y < height; y++) {
+ memcpy(bitmap, prev_frame, width * 4);
+
+ bitmap += gif->width;
+ prev_frame += gif->prev_width;
}
- /* Set buffer position and return */
- gif->buffer_position = gif_data - gif->gif_data;
return GIF_OK;
}
-/**
- * Parse a GIF Image Descriptor.
- *
- * The format is:
- *
- * +0 CHAR Image Separator (0x2c)
- * +1 SHORT Image Left Position
- * +3 SHORT Image Top Position
- * +5 SHORT Width
- * +7 SHORT Height
- * +9 CHAR __Packed Fields__
- * 1BIT Local Colour Table Flag
- * 1BIT Interlace Flag
- * 1BIT Sort Flag
- * 2BITS Reserved
- * 3BITS Size of Local Colour Table
- *
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to parse an image descriptor for.
- * \return GIF_OK on success, appropriate error otherwise.
- */
-static gif_result gif__parse_image_descriptor(
- struct gif_animation *gif,
- struct gif_frame *frame,
- bool decode)
+static uint32_t gif_interlaced_line(int height, int y)
{
- const uint8_t *data = gif->gif_data + gif->buffer_position;
- size_t len = gif->buffer_size - gif->buffer_position;
- enum {
- GIF_IMAGE_DESCRIPTOR_LEN = 10u,
- GIF_IMAGE_SEPARATOR = 0x2Cu,
- };
-
- assert(gif != NULL);
- assert(frame != NULL);
+ if ((y << 3) < height) {
+ return (y << 3);
+ }
+ y -= ((height + 7) >> 3);
+ if ((y << 3) < (height - 4)) {
+ return (y << 3) + 4;
+ }
+ y -= ((height + 3) >> 3);
+ if ((y << 2) < (height - 2)) {
+ return (y << 2) + 2;
+ }
+ y -= ((height + 1) >> 2);
+ return (y << 1) + 1;
+}
- if (len < GIF_IMAGE_DESCRIPTOR_LEN) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+static gif_result gif__decode_complex(
+ struct gif_animation *gif,
+ uint32_t width,
+ uint32_t height,
+ uint32_t offset_x,
+ uint32_t offset_y,
+ uint32_t interlace,
+ uint8_t minimum_code_size,
+ uint32_t transparency_index,
+ uint32_t *restrict frame_data,
+ uint32_t *restrict colour_table)
+{
+ uint32_t available = 0;
+ gif_result ret = GIF_OK;
+ lzw_result res;
+
+ /* Initialise the LZW decoding */
+ res = lzw_decode_init(gif->lzw_ctx, minimum_code_size,
+ gif->gif_data, gif->buffer_size, gif->buffer_position);
+ if (res != LZW_OK) {
+ return gif_error_from_lzw(res);
}
- if (decode) {
- unsigned x, y, w, h;
+ for (uint32_t y = 0; y < height; y++) {
+ uint32_t x;
+ uint32_t decode_y;
+ uint32_t *frame_scanline;
- if (data[0] != GIF_IMAGE_SEPARATOR) {
- return GIF_FRAME_DATA_ERROR;
+ if (interlace) {
+ decode_y = gif_interlaced_line(height, y) + offset_y;
+ } else {
+ decode_y = y + offset_y;
}
+ frame_scanline = frame_data + offset_x + (decode_y * gif->width);
- x = data[1] | (data[2] << 8);
- y = data[3] | (data[4] << 8);
- w = data[5] | (data[6] << 8);
- h = data[7] | (data[8] << 8);
- frame->flags = data[9];
-
- frame->redraw_x = x;
- frame->redraw_y = y;
- frame->redraw_width = w;
- frame->redraw_height = h;
+ x = width;
+ while (x > 0) {
+ const uint8_t *uncompressed;
+ unsigned row_available;
+ if (available == 0) {
+ if (res != LZW_OK) {
+ /* Unexpected end of frame, try to recover */
+ if (res == LZW_OK_EOD) {
+ ret = GIF_OK;
+ } else {
+ ret = gif_error_from_lzw(res);
+ }
+ break;
+ }
+ res = lzw_decode(gif->lzw_ctx,
+ &uncompressed, &available);
+ }
- /* Frame size may have grown. */
- gif->width = (x + w > gif->width ) ? x + w : gif->width;
- gif->height = (y + h > gif->height) ? y + h : gif->height;
+ row_available = x < available ? x : available;
+ x -= row_available;
+ available -= row_available;
+ if (transparency_index > 0xFF) {
+ while (row_available-- > 0) {
+ *frame_scanline++ =
+ colour_table[*uncompressed++];
+ }
+ } else {
+ while (row_available-- > 0) {
+ register uint32_t colour;
+ colour = *uncompressed++;
+ if (colour != transparency_index) {
+ *frame_scanline =
+ colour_table[colour];
+ }
+ frame_scanline++;
+ }
+ }
+ }
}
-
- gif->buffer_position += GIF_IMAGE_DESCRIPTOR_LEN;
- return GIF_OK;
+ return ret;
}
-/**
- * Get a frame's colour table.
- *
- * Sets up gif->colour_table for the frame.
- *
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to get the colour table for.
- * \return GIF_OK on success, appropriate error otherwise.
- */
-static gif_result gif__parse_colour_table(
+static gif_result gif__decode_simple(
struct gif_animation *gif,
- struct gif_frame *frame,
- bool decode)
+ uint32_t height,
+ uint32_t offset_y,
+ uint8_t minimum_code_size,
+ uint32_t transparency_index,
+ uint32_t *restrict frame_data,
+ uint32_t *restrict colour_table)
{
- const uint8_t *data = gif->gif_data + gif->buffer_position;
- size_t len = gif->buffer_size - gif->buffer_position;
- unsigned colour_table_size;
+ uint32_t pixels = gif->width * height;
+ uint32_t written = 0;
+ gif_result ret = GIF_OK;
+ lzw_result res;
- assert(gif != NULL);
- assert(frame != NULL);
+ /* Initialise the LZW decoding */
+ res = lzw_decode_init_map(gif->lzw_ctx,
+ minimum_code_size, transparency_index, colour_table,
+ gif->gif_data, gif->buffer_size, gif->buffer_position);
+ if (res != LZW_OK) {
+ return gif_error_from_lzw(res);
+ }
- if ((frame->flags & GIF_COLOUR_TABLE_MASK) == 0) {
- gif->colour_table = gif->global_colour_table;
- return GIF_OK;
+ frame_data += (offset_y * gif->width);
+
+ while (pixels > 0) {
+ res = lzw_decode_map(gif->lzw_ctx,
+ frame_data, pixels, &written);
+ pixels -= written;
+ frame_data += written;
+ if (res != LZW_OK) {
+ /* Unexpected end of frame, try to recover */
+ if (res == LZW_OK_EOD) {
+ ret = GIF_OK;
+ } else {
+ ret = gif_error_from_lzw(res);
+ }
+ break;
+ }
}
- colour_table_size = 2 << (frame->flags & GIF_COLOUR_TABLE_SIZE_MASK);
- if (len < colour_table_size * 3) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ if (pixels == 0) {
+ ret = GIF_OK;
}
- if (decode) {
- int count = colour_table_size;
- uint8_t *entry = (uint8_t *)gif->local_colour_table;
+ return ret;
+}
- while (count--) {
- /* Gif colour map contents are r,g,b.
- *
- * We want to pack them bytewise into the
- * colour table, such that the red component
- * is in byte 0 and the alpha component is in
- * byte 3.
- */
+static inline gif_result gif__decode(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ uint8_t minimum_code_size,
+ uint32_t *restrict frame_data)
+{
+ gif_result ret;
+ uint32_t offset_x = frame->redraw_x;
+ uint32_t offset_y = frame->redraw_y;
+ uint32_t width = frame->redraw_width;
+ uint32_t height = frame->redraw_height;
+ uint32_t interlace = frame->flags & GIF_INTERLACE_MASK;
+ uint32_t transparency_index = frame->transparency_index;
+ uint32_t *restrict colour_table = gif->colour_table;
- *entry++ = *data++; /* r */
- *entry++ = *data++; /* g */
- *entry++ = *data++; /* b */
- *entry++ = 0xff; /* a */
- }
+ if (interlace == false && width == gif->width && offset_x == 0) {
+ ret = gif__decode_simple(gif, height, offset_y,
+ minimum_code_size, transparency_index,
+ frame_data, colour_table);
+ } else {
+ ret = gif__decode_complex(gif, width, height,
+ offset_x, offset_y, interlace,
+ minimum_code_size, transparency_index,
+ frame_data, colour_table);
}
- gif->buffer_position += colour_table_size * 3;
- gif->colour_table = gif->local_colour_table;
- return GIF_OK;
+ return ret;
}
/**
- * Attempts to initialise the next frame
+ * Restore a GIF to the background colour.
*
- * \param[in] gif The animation context
- * \param[in] frame_idx The frame number to decode.
- * \return error code
- * - GIF_INSUFFICIENT_DATA for insufficient data to do anything
- * - GIF_FRAME_DATA_ERROR for GIF frame data error
- * - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
- * - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
- * - GIF_DATA_ERROR for GIF error (invalid frame header)
- * - GIF_OK for successful decoding
- * - GIF_WORKING for successful decoding if more frames are expected
-*/
-static gif_result gif_initialise_frame(gif_animation *gif,
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to clear, or NULL.
+ * \param[in] bitmap The bitmap to clear the frame in.
+ */
+static void gif__restore_bg(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ uint32_t *bitmap)
+{
+ if (frame == NULL) {
+ memset(bitmap, GIF_TRANSPARENT_COLOUR,
+ gif->width * gif->height * sizeof(*bitmap));
+ } else {
+ uint32_t offset_x = frame->redraw_x;
+ uint32_t offset_y = frame->redraw_y;
+ uint32_t width = frame->redraw_width;
+ uint32_t height = frame->redraw_height;
+
+ if (frame->display == false) {
+ return;
+ }
+
+ if (frame->transparency) {
+ for (uint32_t y = 0; y < height; y++) {
+ uint32_t *scanline = bitmap + offset_x +
+ (offset_y + y) * gif->width;
+ memset(scanline, GIF_TRANSPARENT_COLOUR,
+ width * sizeof(*bitmap));
+ }
+ } else {
+ for (uint32_t y = 0; y < height; y++) {
+ uint32_t *scanline = bitmap + offset_x +
+ (offset_y + y) * gif->width;
+ for (uint32_t x = 0; x < width; x++) {
+ scanline[x] = gif->bg_colour;
+ }
+ }
+ }
+ }
+}
+
+static gif_result gif__update_bitmap(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ uint8_t minimum_code_size,
uint32_t frame_idx)
{
gif_result ret;
- gif_frame *temp_buf;
- uint8_t *gif_data, *gif_end;
- int gif_bytes;
- uint32_t block_size;
+ uint32_t *bitmap;
- /* Get our buffer position etc. */
- gif_data = (uint8_t *)(gif->gif_data + gif->buffer_position);
- gif_end = (uint8_t *)(gif->gif_data + gif->buffer_size);
- gif_bytes = (gif_end - gif_data);
+ gif->decoded_frame = frame_idx;
- /* Check if we've finished */
- if ((gif_bytes > 0) && (gif_data[0] == GIF_TRAILER)) {
- return GIF_OK;
+ bitmap = gif__bitmap_get(gif);
+ if (bitmap == NULL) {
+ return GIF_INSUFFICIENT_MEMORY;
}
- /* Check if there is enough data remaining. The shortest block of data
- * is a 4-byte comment extension + 1-byte block terminator + 1-byte gif
- * trailer
- */
- if (gif_bytes < 6) {
- return GIF_INSUFFICIENT_DATA;
- }
+ /* Handle any bitmap clearing/restoration required before decoding this
+ * frame. */
+ if (frame_idx == 0 || gif->decoded_frame == GIF_INVALID_FRAME) {
+ gif__restore_bg(gif, NULL, bitmap);
- /* We could theoretically get some junk data that gives us millions of
- * frames, so we ensure that we don't have a silly number
- */
- if (frame_idx > 4096) {
- return GIF_FRAME_DATA_ERROR;
- }
+ } else {
+ struct gif_frame *prev = &gif->frames[frame_idx - 1];
- /* Get some memory to store our pointers in etc. */
- if (gif->frame_holders <= frame_idx) {
- /* Allocate more memory */
- temp_buf = (gif_frame *)realloc(gif->frames, (frame_idx + 1) * sizeof(gif_frame));
- if (temp_buf == NULL) {
- return GIF_INSUFFICIENT_MEMORY;
+ if (prev->disposal_method == GIF_DISPOSAL_RESTORE_BG) {
+ gif__restore_bg(gif, prev, bitmap);
+
+ } else if (prev->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
+ ret = gif__recover_frame(gif, bitmap);
+ if (ret != GIF_OK) {
+ gif__restore_bg(gif, prev, bitmap);
+ }
}
- gif->frames = temp_buf;
- gif->frame_holders = frame_idx + 1;
}
- /* Store our frame pointer. We would do it when allocating except we
- * start off with one frame allocated so we can always use realloc.
- */
- gif->frames[frame_idx].frame_pointer = gif->buffer_position;
- gif->frames[frame_idx].display = false;
- gif->frames[frame_idx].virgin = true;
- gif->frames[frame_idx].disposal_method = 0;
- gif->frames[frame_idx].transparency = false;
- gif->frames[frame_idx].transparency_index = GIF_NO_TRANSPARENCY;
- gif->frames[frame_idx].frame_delay = 100;
- gif->frames[frame_idx].redraw_required = false;
-
- /* Invalidate any previous decoding we have of this frame */
- if (gif->decoded_frame == (int)frame_idx) {
- gif->decoded_frame = GIF_INVALID_FRAME;
+ if (frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
+ /* Store the previous frame for later restoration */
+ gif__record_frame(gif, bitmap);
}
- /* We pretend to initialise the frames, but really we just skip over
- * all the data contained within. This is all basically a cut down
- * version of gif_decode_frame that doesn't have any of the LZW bits in
- * it.
- */
+ ret = gif__decode(gif, frame, minimum_code_size, bitmap);
- /* Initialise any extensions */
- gif->buffer_position = gif_data - gif->gif_data;
- ret = gif__parse_frame_extensions(gif, &gif->frames[frame_idx], true);
- if (ret != GIF_OK) {
- return ret;
+ gif__bitmap_modified(gif);
+
+ if (frame->virgin) {
+ frame->opaque = gif__bitmap_get_opaque(gif);
+ frame->virgin = false;
}
+ gif__bitmap_set_opaque(gif, frame);
- ret = gif__parse_image_descriptor(gif, &gif->frames[frame_idx], true);
- if (ret != GIF_OK) {
- return ret;
+ return ret;
+}
+
+/**
+ * Parse the application extension
+ *
+ * \param[in] frame The gif object we're decoding.
+ * \param[in] data The data to decode.
+ * \param[in] len Byte length of data.
+ * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
+ * GIF_OK for success.
+ */
+static gif_result gif__parse_extension_graphic_control(
+ struct gif_frame *frame,
+ uint8_t *data,
+ size_t len)
+{
+ /* 6-byte Graphic Control Extension is:
+ *
+ * +0 CHAR Graphic Control Label
+ * +1 CHAR Block Size
+ * +2 CHAR __Packed Fields__
+ * 3BITS Reserved
+ * 3BITS Disposal Method
+ * 1BIT User Input Flag
+ * 1BIT Transparent Color Flag
+ * +3 SHORT Delay Time
+ * +5 CHAR Transparent Color Index
+ */
+ if (len < 6) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
}
- ret = gif__parse_colour_table(gif, &gif->frames[frame_idx], false);
- if (ret != GIF_OK) {
- return ret;
+ frame->frame_delay = data[3] | (data[4] << 8);
+ if (data[2] & GIF_TRANSPARENCY_MASK) {
+ frame->transparency = true;
+ frame->transparency_index = data[5];
}
- gif_data = gif->gif_data + gif->buffer_position;
- gif_bytes = (gif_end - gif_data);
- /* Move our data onwards and remember we've got a bit of this frame */
- gif->frame_count_partial = frame_idx + 1;
+ frame->disposal_method = ((data[2] & GIF_DISPOSAL_MASK) >> 2);
+ /* I have encountered documentation and GIFs in the
+ * wild that use 0x04 to restore the previous frame,
+ * rather than the officially documented 0x03. I
+ * believe some (older?) software may even actually
+ * export this way. We handle this as a type of
+ * "quirks" mode. */
+ if (frame->disposal_method == GIF_DISPOSAL_RESTORE_QUIRK) {
+ frame->disposal_method = GIF_DISPOSAL_RESTORE_PREV;
+ }
- /* Ensure we have a correct code size */
- if (gif_bytes < 1) {
+ /* if we are clearing the background then we need to
+ * redraw enough to cover the previous frame too. */
+ frame->redraw_required =
+ frame->disposal_method == GIF_DISPOSAL_RESTORE_BG ||
+ frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV;
+
+ return GIF_OK;
+}
+
+/**
+ * Parse the application extension
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] data The data to decode.
+ * \param[in] len Byte length of data.
+ * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
+ * GIF_OK for success.
+ */
+static gif_result gif__parse_extension_application(
+ struct gif_animation *gif,
+ uint8_t *data,
+ size_t len)
+{
+ /* 14-byte+ Application Extension is:
+ *
+ * +0 CHAR Application Extension Label
+ * +1 CHAR Block Size
+ * +2 8CHARS Application Identifier
+ * +10 3CHARS Appl. Authentication Code
+ * +13 1-256 Application Data (Data sub-blocks)
+ */
+ if (len < 17) {
return GIF_INSUFFICIENT_FRAME_DATA;
}
- if (gif_data[0] >= LZW_CODE_MAX) {
- return GIF_DATA_ERROR;
+
+ if ((data[1] == 0x0b) &&
+ (strncmp((const char *)data + 2, "NETSCAPE2.0", 11) == 0) &&
+ (data[13] == 0x03) && (data[14] == 0x01)) {
+ gif->loop_count = data[15] | (data[16] << 8);
}
- /* Move our pointer to the actual image data */
- gif_data++;
- --gif_bytes;
+ return GIF_OK;
+}
+
+/**
+ * Parse the frame's extensions
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to parse extensions for.
+ * \param[in] decode Whether to decode or skip over the extension.
+ * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
+ * GIF_OK for success.
+ */
+static gif_result gif__parse_frame_extensions(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ bool decode)
+{
+ uint8_t *gif_data, *gif_end;
+ int gif_bytes;
+
+ /* Get our buffer position etc. */
+ gif_data = gif->gif_data + gif->buffer_position;
+ gif_end = gif->gif_data + gif->buffer_size;
+ gif_bytes = gif_end - gif_data;
+
+ /* Initialise the extensions */
+ while (gif_bytes > 0 && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
+ bool block_step = true;
+ gif_result ret;
+
+ gif_data++;
+ gif_bytes--;
+
+ if (gif_bytes == 0) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
+
+ /* Switch on extension label */
+ switch (gif_data[0]) {
+ case GIF_EXTENSION_GRAPHIC_CONTROL:
+ if (decode) {
+ ret = gif__parse_extension_graphic_control(
+ frame, gif_data, gif_bytes);
+ if (ret != GIF_OK) {
+ return ret;
+ }
+ }
+ break;
+
+ case GIF_EXTENSION_APPLICATION:
+ if (decode) {
+ ret = gif__parse_extension_application(
+ gif, gif_data, gif_bytes);
+ if (ret != GIF_OK) {
+ return ret;
+ }
+ }
+ break;
+
+ case GIF_EXTENSION_COMMENT:
+ /* Move the pointer to the first data sub-block Skip 1
+ * byte for the extension label. */
+ ++gif_data;
+ block_step = false;
+ break;
- /* Repeatedly skip blocks until we get a zero block or run out of data
- * These blocks of image data are processed later by gif_decode_frame()
- */
- block_size = 0;
- while (block_size != 1) {
- if (gif_bytes < 1) return GIF_INSUFFICIENT_FRAME_DATA;
- block_size = gif_data[0] + 1;
- /* Check if the frame data runs off the end of the file */
- if ((int)(gif_bytes - block_size) < 0) {
- /* Try to recover by signaling the end of the gif.
- * Once we get garbage data, there is no logical way to
- * determine where the next frame is. It's probably
- * better to partially load the gif than not at all.
+ default:
+ break;
+ }
+
+ if (block_step) {
+ /* Move the pointer to the first data sub-block Skip 2
+ * bytes for the extension label and size fields Skip
+ * the extension size itself
*/
- if (gif_bytes >= 2) {
- gif_data[0] = 0;
- gif_data[1] = GIF_TRAILER;
- gif_bytes = 1;
- ++gif_data;
- break;
- } else {
+ if (gif_bytes < 2) {
return GIF_INSUFFICIENT_FRAME_DATA;
}
- } else {
- gif_bytes -= block_size;
- gif_data += block_size;
+ gif_data += 2 + gif_data[1];
}
- }
-
- /* Add the frame and set the display flag */
- gif->buffer_position = gif_data - gif->gif_data;
- gif->frame_count = frame_idx + 1;
- gif->frames[frame_idx].display = true;
- /* Check if we've finished */
- if (gif_bytes < 1) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- } else {
- if (gif_data[0] == GIF_TRAILER) {
- return GIF_OK;
+ /* Repeatedly skip blocks until we get a zero block or run out
+ * of data. This data is ignored by this gif decoder. */
+ while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) {
+ gif_data += gif_data[0] + 1;
+ if (gif_data >= gif_end) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
}
+ gif_data++;
+ gif_bytes = gif_end - gif_data;
}
- return GIF_WORKING;
-}
-static uint32_t gif_interlaced_line(int height, int y)
-{
- if ((y << 3) < height) {
- return (y << 3);
- }
- y -= ((height + 7) >> 3);
- if ((y << 3) < (height - 4)) {
- return (y << 3) + 4;
- }
- y -= ((height + 3) >> 3);
- if ((y << 2) < (height - 2)) {
- return (y << 2) + 2;
+ if (gif_data > gif_end) {
+ gif_data = gif_end;
}
- y -= ((height + 1) >> 2);
- return (y << 1) + 1;
-}
-
-static gif_result gif_error_from_lzw(lzw_result l_res)
-{
- static const gif_result g_res[] = {
- [LZW_OK] = GIF_OK,
- [LZW_OK_EOD] = GIF_END_OF_FRAME,
- [LZW_NO_MEM] = GIF_INSUFFICIENT_MEMORY,
- [LZW_NO_DATA] = GIF_INSUFFICIENT_FRAME_DATA,
- [LZW_EOI_CODE] = GIF_FRAME_DATA_ERROR,
- [LZW_BAD_ICODE] = GIF_FRAME_DATA_ERROR,
- [LZW_BAD_CODE] = GIF_FRAME_DATA_ERROR,
- };
- assert(l_res != LZW_BAD_PARAM);
- assert(l_res != LZW_NO_COLOUR);
- return g_res[l_res];
+ /* Set buffer position and return */
+ gif->buffer_position = gif_data - gif->gif_data;
+ return GIF_OK;
}
-static void gif__record_frame(gif_animation *gif)
+/**
+ * Parse a GIF Image Descriptor.
+ *
+ * The format is:
+ *
+ * +0 CHAR Image Separator (0x2c)
+ * +1 SHORT Image Left Position
+ * +3 SHORT Image Top Position
+ * +5 SHORT Width
+ * +7 SHORT Height
+ * +9 CHAR __Packed Fields__
+ * 1BIT Local Colour Table Flag
+ * 1BIT Interlace Flag
+ * 1BIT Sort Flag
+ * 2BITS Reserved
+ * 3BITS Size of Local Colour Table
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to parse an image descriptor for.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_image_descriptor(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ bool decode)
{
- bool need_alloc = gif->prev_frame == NULL;
- const uint32_t *frame_data;
- uint32_t *prev_frame;
+ const uint8_t *data = gif->gif_data + gif->buffer_position;
+ size_t len = gif->buffer_size - gif->buffer_position;
+ enum {
+ GIF_IMAGE_DESCRIPTOR_LEN = 10u,
+ GIF_IMAGE_SEPARATOR = 0x2Cu,
+ };
- if (gif->decoded_frame == GIF_INVALID_FRAME ||
- gif->decoded_frame == gif->prev_index) {
- /* No frame to copy, or already have this frame recorded. */
- return;
- }
+ assert(gif != NULL);
+ assert(frame != NULL);
- assert(gif->bitmap_callbacks.bitmap_get_buffer);
- frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
- if (!frame_data) {
- return;
+ if (len < GIF_IMAGE_DESCRIPTOR_LEN) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
}
- if (gif->prev_frame != NULL &&
- gif->width * gif->height > gif->prev_width * gif->prev_height) {
- need_alloc = true;
- }
+ if (decode) {
+ unsigned x, y, w, h;
- if (need_alloc) {
- prev_frame = realloc(gif->prev_frame,
- gif->width * gif->height * 4);
- if (prev_frame == NULL) {
- return;
+ if (data[0] != GIF_IMAGE_SEPARATOR) {
+ return GIF_FRAME_DATA_ERROR;
}
- } else {
- prev_frame = gif->prev_frame;
- }
-
- memcpy(prev_frame, frame_data, gif->width * gif->height * 4);
-
- gif->prev_frame = prev_frame;
- gif->prev_width = gif->width;
- gif->prev_height = gif->height;
- gif->prev_index = gif->decoded_frame;
-}
-
-static gif_result gif__recover_frame(const gif_animation *gif)
-{
- const uint32_t *prev_frame = gif->prev_frame;
- unsigned height = gif->height < gif->prev_height ? gif->height : gif->prev_height;
- unsigned width = gif->width < gif->prev_width ? gif->width : gif->prev_width;
- uint32_t *frame_data;
-
- if (prev_frame == NULL) {
- return GIF_FRAME_DATA_ERROR;
- }
- assert(gif->bitmap_callbacks.bitmap_get_buffer);
- frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
- if (!frame_data) {
- return GIF_INSUFFICIENT_MEMORY;
- }
+ x = data[1] | (data[2] << 8);
+ y = data[3] | (data[4] << 8);
+ w = data[5] | (data[6] << 8);
+ h = data[7] | (data[8] << 8);
+ frame->flags = data[9];
- for (unsigned y = 0; y < height; y++) {
- memcpy(frame_data, prev_frame, width * 4);
+ frame->redraw_x = x;
+ frame->redraw_y = y;
+ frame->redraw_width = w;
+ frame->redraw_height = h;
- frame_data += gif->width;
- prev_frame += gif->prev_width;
+ /* Frame size may have grown. */
+ gif->width = (x + w > gif->width ) ? x + w : gif->width;
+ gif->height = (y + h > gif->height) ? y + h : gif->height;
}
+ gif->buffer_position += GIF_IMAGE_DESCRIPTOR_LEN;
return GIF_OK;
}
-static gif_result
-gif__decode_complex(
+/**
+ * Get a frame's colour table.
+ *
+ * Sets up gif->colour_table for the frame.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to get the colour table for.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_colour_table(
struct gif_animation *gif,
- uint32_t width,
- uint32_t height,
- uint32_t offset_x,
- uint32_t offset_y,
- uint32_t interlace,
- uint8_t minimum_code_size,
- uint32_t transparency_index,
- uint32_t *restrict frame_data,
- uint32_t *restrict colour_table)
+ struct gif_frame *frame,
+ bool decode)
{
- uint32_t available = 0;
- gif_result ret = GIF_OK;
- lzw_result res;
-
- /* Initialise the LZW decoding */
- res = lzw_decode_init(gif->lzw_ctx, minimum_code_size,
- gif->gif_data, gif->buffer_size, gif->buffer_position);
- if (res != LZW_OK) {
- return gif_error_from_lzw(res);
- }
+ const uint8_t *data = gif->gif_data + gif->buffer_position;
+ size_t len = gif->buffer_size - gif->buffer_position;
+ unsigned colour_table_size;
- for (uint32_t y = 0; y < height; y++) {
- uint32_t x;
- uint32_t decode_y;
- uint32_t *frame_scanline;
+ assert(gif != NULL);
+ assert(frame != NULL);
- if (interlace) {
- decode_y = gif_interlaced_line(height, y) + offset_y;
- } else {
- decode_y = y + offset_y;
- }
- frame_scanline = frame_data + offset_x + (decode_y * gif->width);
+ if ((frame->flags & GIF_COLOUR_TABLE_MASK) == 0) {
+ gif->colour_table = gif->global_colour_table;
+ return GIF_OK;
+ }
- x = width;
- while (x > 0) {
- const uint8_t *uncompressed;
- unsigned row_available;
- if (available == 0) {
- if (res != LZW_OK) {
- /* Unexpected end of frame, try to recover */
- if (res == LZW_OK_EOD) {
- ret = GIF_OK;
- } else {
- ret = gif_error_from_lzw(res);
- }
- break;
- }
- res = lzw_decode(gif->lzw_ctx,
- &uncompressed, &available);
- }
+ colour_table_size = 2 << (frame->flags & GIF_COLOUR_TABLE_SIZE_MASK);
+ if (len < colour_table_size * 3) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
- row_available = x < available ? x : available;
- x -= row_available;
- available -= row_available;
- if (transparency_index > 0xFF) {
- while (row_available-- > 0) {
- *frame_scanline++ =
- colour_table[*uncompressed++];
- }
- } else {
- while (row_available-- > 0) {
- register uint32_t colour;
- colour = *uncompressed++;
- if (colour != transparency_index) {
- *frame_scanline =
- colour_table[colour];
- }
- frame_scanline++;
- }
- }
+ if (decode) {
+ int count = colour_table_size;
+ uint8_t *entry = (uint8_t *)gif->local_colour_table;
+
+ while (count--) {
+ /* Gif colour map contents are r,g,b.
+ *
+ * We want to pack them bytewise into the
+ * colour table, such that the red component
+ * is in byte 0 and the alpha component is in
+ * byte 3.
+ */
+
+ *entry++ = *data++; /* r */
+ *entry++ = *data++; /* g */
+ *entry++ = *data++; /* b */
+ *entry++ = 0xff; /* a */
}
}
- return ret;
+
+ gif->buffer_position += colour_table_size * 3;
+ gif->colour_table = gif->local_colour_table;
+ return GIF_OK;
}
-static gif_result
-gif__decode_simple(
+/**
+ * Parse the image data for a gif frame.
+ *
+ * Sets up gif->colour_table for the frame.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to get the colour table for.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_image_data(
struct gif_animation *gif,
- uint32_t height,
- uint32_t offset_y,
- uint8_t minimum_code_size,
- uint32_t transparency_index,
- uint32_t *restrict frame_data,
- uint32_t *restrict colour_table)
+ struct gif_frame *frame,
+ bool decode)
{
- uint32_t pixels = gif->width * height;
- uint32_t written = 0;
- gif_result ret = GIF_OK;
- lzw_result res;
+ uint8_t *data = gif->gif_data + gif->buffer_position;
+ size_t len = gif->buffer_size - gif->buffer_position;
+ uint32_t frame_idx = frame - gif->frames;
+ uint8_t minimum_code_size;
+ gif_result ret;
- /* Initialise the LZW decoding */
- res = lzw_decode_init_map(gif->lzw_ctx,
- minimum_code_size, transparency_index, colour_table,
- gif->gif_data, gif->buffer_size, gif->buffer_position);
- if (res != LZW_OK) {
- return gif_error_from_lzw(res);
+ assert(gif != NULL);
+ assert(frame != NULL);
+
+ if (!decode) {
+ gif->frame_count_partial = frame_idx + 1;
}
- frame_data += (offset_y * gif->width);
+ /* Ensure sufficient data remains. A gif trailer or a minimum lzw code
+ * followed by a gif trailer is treated as OK, although without any
+ * image data. */
+ switch (len) {
+ case 2: if (data[1] == GIF_TRAILER) return GIF_OK;
+ /* Fall through. */
+ case 1: if (data[0] == GIF_TRAILER) return GIF_OK;
+ /* Fall through. */
+ case 0: return GIF_INSUFFICIENT_FRAME_DATA;
+ }
- while (pixels > 0) {
- res = lzw_decode_map(gif->lzw_ctx,
- frame_data, pixels, &written);
- pixels -= written;
- frame_data += written;
- if (res != LZW_OK) {
- /* Unexpected end of frame, try to recover */
- if (res == LZW_OK_EOD) {
- ret = GIF_OK;
+ minimum_code_size = data[0];
+ if (minimum_code_size >= LZW_CODE_MAX) {
+ return GIF_DATA_ERROR;
+ }
+ gif->buffer_position++;
+ data++;
+ len--;
+
+ if (decode) {
+ ret = gif__update_bitmap(gif, frame, minimum_code_size,
+ frame_idx);
+ } else {
+ uint32_t block_size = 0;
+
+ while (block_size != 1) {
+ if (len < 1) return GIF_INSUFFICIENT_FRAME_DATA;
+ block_size = data[0] + 1;
+ /* Check if the frame data runs off the end of the file */
+ if ((int)(len - block_size) < 0) {
+ /* Try to recover by signaling the end of the gif.
+ * Once we get garbage data, there is no logical way to
+ * determine where the next frame is. It's probably
+ * better to partially load the gif than not at all.
+ */
+ if (len >= 2) {
+ data[0] = 0;
+ data[1] = GIF_TRAILER;
+ len = 1;
+ data++;
+ break;
+ } else {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
} else {
- ret = gif_error_from_lzw(res);
+ len -= block_size;
+ data += block_size;
}
- break;
}
- }
- if (pixels == 0) {
- ret = GIF_OK;
+ gif->buffer_position = data - gif->gif_data;
+ gif->frame_count = frame_idx + 1;
+ gif->frames[frame_idx].display = true;
+
+ /* Check if we've finished */
+ if (len < 1) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ } else {
+ if (data[0] == GIF_TRAILER) {
+ return GIF_OK;
+ }
+ }
+
+ return GIF_WORKING;
}
return ret;
}
-static inline gif_result gif__decode(
+static struct gif_frame *gif__get_frame(
struct gif_animation *gif,
- struct gif_frame *frame,
- uint8_t minimum_code_size,
- uint32_t *restrict frame_data)
+ uint32_t frame_idx)
{
- gif_result ret;
- uint32_t offset_x = frame->redraw_x;
- uint32_t offset_y = frame->redraw_y;
- uint32_t width = frame->redraw_width;
- uint32_t height = frame->redraw_height;
- uint32_t interlace = frame->flags & GIF_INTERLACE_MASK;
- uint32_t transparency_index = frame->transparency_index;
- uint32_t *restrict colour_table = gif->colour_table;
+ struct gif_frame *frame;
- if (interlace == false && width == gif->width && offset_x == 0) {
- ret = gif__decode_simple(gif, height, offset_y,
- minimum_code_size, transparency_index,
- frame_data, colour_table);
+ if (gif->frame_holders > frame_idx) {
+ frame = &gif->frames[frame_idx];
} else {
- ret = gif__decode_complex(gif, width, height,
- offset_x, offset_y, interlace,
- minimum_code_size, transparency_index,
- frame_data, colour_table);
+ /* Allocate more memory */
+ size_t count = frame_idx + 1;
+ struct gif_frame *temp;
+
+ temp = realloc(gif->frames, count * sizeof(*frame));
+ if (temp == NULL) {
+ return NULL;
+ }
+ gif->frames = temp;
+ gif->frame_holders = count;
+
+ frame = &gif->frames[frame_idx];
+
+ frame->transparency = false;
+ frame->transparency_index = GIF_NO_TRANSPARENCY;
+ frame->frame_pointer = gif->buffer_position;
+ frame->redraw_required = false;
+ frame->disposal_method = 0;
+ frame->frame_delay = 100;
+ frame->display = false;
+ frame->virgin = true;
}
- return ret;
+ return frame;
}
/**
- * Clear a gif frame.
+ * Attempts to initialise the next frame
*
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to clear.
- * \param[in] bitmap The bitmap to clear the frame in.
- * \return GIF_OK on success, appropriate error otherwise.
- */
-static gif_result gif_clear_frame(
+ * \param[in] gif The animation context
+ * \param[in] frame_idx The frame number to decode.
+ * \return error code
+ * - GIF_INSUFFICIENT_DATA for insufficient data to do anything
+ * - GIF_FRAME_DATA_ERROR for GIF frame data error
+ * - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
+ * - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
+ * - GIF_DATA_ERROR for GIF error (invalid frame header)
+ * - GIF_OK for successful decoding
+ * - GIF_WORKING for successful decoding if more frames are expected
+*/
+static gif_result gif_initialise_frame(
struct gif_animation *gif,
- struct gif_frame *frame,
- uint32_t *bitmap)
+ uint32_t frame_idx)
{
- uint32_t width;
- uint32_t height;
- uint32_t offset_x;
- uint32_t offset_y;
+ gif_result ret;
+ struct gif_frame *frame;
+ uint8_t *gif_data, *gif_end;
+ int gif_bytes;
- assert(frame->disposal_method == GIF_FRAME_CLEAR);
+ /* Get our buffer position etc. */
+ gif_data = (uint8_t *)(gif->gif_data + gif->buffer_position);
+ gif_end = (uint8_t *)(gif->gif_data + gif->buffer_size);
+ gif_bytes = (gif_end - gif_data);
- /* Ensure this frame is supposed to be decoded */
- if (frame->display == false) {
+ /* Check if we've finished */
+ if ((gif_bytes > 0) && (gif_data[0] == GIF_TRAILER)) {
return GIF_OK;
}
- offset_x = frame->redraw_x;
- offset_y = frame->redraw_y;
- width = frame->redraw_width;
- height = frame->redraw_height;
+ /* We could theoretically get some junk data that gives us millions of
+ * frames, so we ensure that we don't have a silly number
+ */
+ if (frame_idx > 4096) {
+ return GIF_FRAME_DATA_ERROR;
+ }
- /* Clear our frame */
- for (uint32_t y = 0; y < height; y++) {
- uint32_t *scanline;
- scanline = bitmap + offset_x + ((offset_y + y) * gif->width);
- if (frame->transparency) {
- memset(scanline, GIF_TRANSPARENT_COLOUR, width * 4);
- } else {
- for (uint32_t x = 0; x < width; x++) {
- scanline[x] = gif->bg_colour;
- }
- }
+ frame = gif__get_frame(gif, frame_idx);
+ if (frame == NULL) {
+ return GIF_INSUFFICIENT_MEMORY;
}
- return GIF_OK;
-}
+ /* Initialise any extensions */
+ gif->buffer_position = gif_data - gif->gif_data;
+ ret = gif__parse_frame_extensions(gif, &gif->frames[frame_idx], true);
+ if (ret != GIF_OK) {
+ return ret;
+ }
-/**
- * Wipe bitmap to transparent.
- *
- * \param[in] gif The gif object we're decoding.
- * \param[in] bitmap The bitmap to wipe.
- */
-static inline void gif__wipe_bitmap(
- const struct gif_animation *gif,
- uint32_t *bitmap)
-{
- memset((char*)bitmap, GIF_TRANSPARENT_COLOUR,
- gif->width * gif->height * sizeof(*bitmap));
+ ret = gif__parse_image_descriptor(gif, &gif->frames[frame_idx], true);
+ if (ret != GIF_OK) {
+ return ret;
+ }
+
+ ret = gif__parse_colour_table(gif, &gif->frames[frame_idx], false);
+ if (ret != GIF_OK) {
+ return ret;
+ }
+
+ ret = gif__parse_image_data(gif, &gif->frames[frame_idx], false);
+ if (ret != GIF_OK) {
+ return ret;
+ }
+
+ return GIF_OK;
}
/**
@@ -893,15 +1035,13 @@ static inline void gif__wipe_bitmap(
* \param frame The frame number to decode.
* \param clear_image flag for image data being cleared instead of plotted.
*/
-static gif_result
-gif_internal_decode_frame(gif_animation *gif,
- uint32_t frame_idx)
+static gif_result gif_internal_decode_frame(
+ struct gif_animation *gif,
+ uint32_t frame_idx)
{
gif_result ret;
+ uint8_t *gif_data;
struct gif_frame *frame;
- uint8_t *gif_data, *gif_end;
- int gif_bytes;
- uint32_t *frame_data = 0; // Set to 0 for no warnings
uint32_t save_buffer_position;
/* Ensure the frame is in range to decode */
@@ -914,7 +1054,10 @@ gif_internal_decode_frame(gif_animation *gif,
return GIF_OK;
}
- frame = &gif->frames[frame_idx];
+ frame = gif__get_frame(gif, frame_idx);
+ if (frame == NULL) {
+ return GIF_INSUFFICIENT_MEMORY;
+ }
/* Ensure this frame is supposed to be decoded */
if (frame->display == false) {
@@ -923,8 +1066,6 @@ gif_internal_decode_frame(gif_animation *gif,
/* Get the start of our frame data and the end of the GIF data */
gif_data = gif->gif_data + frame->frame_pointer;
- gif_end = gif->gif_data + gif->buffer_size;
- gif_bytes = (gif_end - gif_data);
/* Save the buffer position */
save_buffer_position = gif->buffer_position;
@@ -943,108 +1084,16 @@ gif_internal_decode_frame(gif_animation *gif,
ret = gif__parse_colour_table(gif, frame, true);
if (ret != GIF_OK) {
- return ret;
- }
- gif_data = gif->gif_data + gif->buffer_position;
- gif_bytes = (gif_end - gif_data);
-
- /* Ensure sufficient data remains */
- if (gif_bytes < 1) {
- ret = GIF_INSUFFICIENT_FRAME_DATA;
- goto gif_decode_frame_exit;
- }
-
- /* check for an end marker */
- if (gif_data[0] == GIF_TRAILER) {
- ret = GIF_OK;
- goto gif_decode_frame_exit;
- }
-
- /* Make sure we have a buffer to decode to. */
- if (gif_initialise_sprite(gif, gif->width, gif->height)) {
- ret = GIF_INSUFFICIENT_MEMORY;
- goto gif_decode_frame_exit;
- }
-
- /* Get the frame data */
- assert(gif->bitmap_callbacks.bitmap_get_buffer);
- frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
- if (!frame_data) {
- ret = GIF_INSUFFICIENT_MEMORY;
- goto gif_decode_frame_exit;
- }
-
- /* Ensure we have enough data for a 1-byte LZW code size +
- * 1-byte gif trailer
- */
- if (gif_bytes < 2) {
- ret = GIF_INSUFFICIENT_FRAME_DATA;
goto gif_decode_frame_exit;
}
- /* If we only have a 1-byte LZW code size + 1-byte gif trailer,
- * we're finished
- */
- if ((gif_bytes == 2) && (gif_data[1] == GIF_TRAILER)) {
- ret = GIF_OK;
+ ret = gif__parse_image_data(gif, frame, true);
+ if (ret != GIF_OK) {
goto gif_decode_frame_exit;
}
- /* Handle any bitmap clearing/restoration required before decoding this
- * frame. */
- if (frame_idx == 0 || gif->decoded_frame == GIF_INVALID_FRAME) {
- gif__wipe_bitmap(gif, frame_data);
-
- } else {
- struct gif_frame *prev = &gif->frames[frame_idx - 1];
-
- if (prev->disposal_method == GIF_FRAME_CLEAR) {
- ret = gif_clear_frame(gif, prev, frame_data);
- if (ret != GIF_OK) {
- goto gif_decode_frame_exit;
- }
- } else if (prev->disposal_method == GIF_FRAME_RESTORE) {
- /*
- * If the previous frame's disposal method requires we
- * restore the previous image, restore our saved image.
- */
- ret = gif__recover_frame(gif);
- if (ret != GIF_OK) {
- gif__wipe_bitmap(gif, frame_data);
- }
- }
- }
-
- if (frame->disposal_method == GIF_FRAME_RESTORE) {
- /* Store the previous frame for later restoration */
- gif__record_frame(gif);
- }
-
- gif->decoded_frame = frame_idx;
- gif->buffer_position = (gif_data - gif->gif_data) + 1;
-
- ret = gif__decode(gif, frame, gif_data[0], frame_data);
-
gif_decode_frame_exit:
- if (gif->bitmap_callbacks.bitmap_modified) {
- gif->bitmap_callbacks.bitmap_modified(gif->frame_image);
- }
-
- /* Check if we should test for optimisation */
- if (frame->virgin) {
- if (gif->bitmap_callbacks.bitmap_test_opaque) {
- frame->opaque = gif->bitmap_callbacks.bitmap_test_opaque(gif->frame_image);
- } else {
- frame->opaque = false;
- }
- frame->virgin = false;
- }
-
- if (gif->bitmap_callbacks.bitmap_set_opaque) {
- gif->bitmap_callbacks.bitmap_set_opaque(gif->frame_image, frame->opaque);
- }
-
/* Restore the buffer position */
gif->buffer_position = save_buffer_position;
@@ -1098,6 +1147,7 @@ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data)
*/
gif->frame_image = NULL;
gif->frames = NULL;
+ gif->frame_holders = 0;
gif->local_colour_table = NULL;
gif->global_colour_table = NULL;
@@ -1192,13 +1242,6 @@ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data)
}
}
- /* Initialise enough workspace for a frame */
- if ((gif->frames = (gif_frame *)malloc(sizeof(gif_frame))) == NULL) {
- gif_finalise(gif);
- return GIF_INSUFFICIENT_MEMORY;
- }
- gif->frame_holders = 1;
-
/* Remember we've done this now */
gif->buffer_position = gif_data - gif->gif_data;
}
--
NetSurf GIF Decoder
1 year, 10 months