netsurf: branch tlsa/gif-api updated. release/3.10-159-ge142000
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/netsurf.git/shortlog/e1420000609e3b1c059cc...
...commit http://git.netsurf-browser.org/netsurf.git/commit/e1420000609e3b1c059ccfd...
...tree http://git.netsurf-browser.org/netsurf.git/tree/e1420000609e3b1c059ccfd09...
The branch, tlsa/gif-api has been updated
discards 0f547ed94026145f48602e72d5c9bbafbbb72b10 (commit)
via e1420000609e3b1c059ccfd09af5b02be2d59067 (commit)
via d92b2696295e18146ff5c8fd15bc6ea198749f28 (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 (0f547ed94026145f48602e72d5c9bbafbbb72b10)
\
N -- N -- N (e1420000609e3b1c059ccfd09af5b02be2d59067)
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/netsurf.git/commit/?id=e1420000609e3b1c059...
commit e1420000609e3b1c059ccfd09af5b02be2d59067
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Update to latest libnsgif API.
diff --git a/content/handlers/image/gif.c b/content/handlers/image/gif.c
index e2a0ca5..fc896f8 100644
--- a/content/handlers/image/gif.c
+++ b/content/handlers/image/gif.c
@@ -34,7 +34,8 @@
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <libnsgif.h>
+
+#include <nsgif.h>
#include "utils/utils.h"
#include "utils/messages.h"
@@ -51,13 +52,31 @@
#include "image/image.h"
#include "image/gif.h"
-typedef struct nsgif_content {
+typedef struct gif_content {
struct content base;
- struct gif_animation *gif; /**< GIF animation data */
- int current_frame; /**< current frame to display [0...(max-1)] */
-} nsgif_content;
+ nsgif *gif; /**< GIF animation data */
+ uint32_t current_frame; /**< current frame to display [0...(max-1)] */
+} gif_content;
+
+static inline nserror gif__nsgif_error_to_ns(nsgif_result gif_res)
+{
+ nserror err;
+
+ switch (gif_res) {
+ case NSGIF_OK:
+ err = NSERROR_OK;
+ break;
+ case NSGIF_INSUFFICIENT_MEMORY:
+ err = NSERROR_NOMEM;
+ break;
+ default:
+ err = NSERROR_GIF_ERROR;
+ break;
+ }
+ return err;
+}
/**
* Callback for libnsgif; forwards the call to bitmap_create()
@@ -66,44 +85,42 @@ typedef struct nsgif_content {
* \param height width of image in pixels
* \return an opaque struct bitmap, or NULL on memory exhaustion
*/
-static void *nsgif_bitmap_create(int width, int height)
+static void *gif_bitmap_create(int width, int height)
{
return guit->bitmap->create(width, height, BITMAP_NEW);
}
-
-static nserror nsgif_create_gif_data(nsgif_content *c)
+static nserror gif_create_gif_data(gif_content *c)
{
- gif_bitmap_callback_vt gif_bitmap_callbacks = {
- .bitmap_create = nsgif_bitmap_create,
- .bitmap_destroy = guit->bitmap->destroy,
- .bitmap_get_buffer = guit->bitmap->get_buffer,
- .bitmap_set_opaque = guit->bitmap->set_opaque,
- .bitmap_test_opaque = guit->bitmap->test_opaque,
- .bitmap_modified = guit->bitmap->modified
+ nsgif_result gif_res;
+ const nsgif_bitmap_cb_vt gif_bitmap_callbacks = {
+ .create = gif_bitmap_create,
+ .destroy = guit->bitmap->destroy,
+ .get_buffer = guit->bitmap->get_buffer,
+ .set_opaque = guit->bitmap->set_opaque,
+ .test_opaque = guit->bitmap->test_opaque,
+ .modified = guit->bitmap->modified
};
- /* Initialise our data structure */
- c->gif = calloc(sizeof(gif_animation), 1);
- if (c->gif == NULL) {
- content_broadcast_error(&c->base, NSERROR_NOMEM, NULL);
- return NSERROR_NOMEM;
+ gif_res = nsgif_create(&gif_bitmap_callbacks, &c->gif);
+ if (gif_res != NSGIF_OK) {
+ nserror err = gif__nsgif_error_to_ns(gif_res);
+ content_broadcast_error(&c->base, err, NULL);
+ return err;
}
- gif_create(c->gif, &gif_bitmap_callbacks);
+
return NSERROR_OK;
}
-
-
-static nserror nsgif_create(const content_handler *handler,
- lwc_string *imime_type, const struct http_parameter *params,
+static nserror gif_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
llcache_handle *llcache, const char *fallback_charset,
bool quirks, struct content **c)
{
- nsgif_content *result;
+ gif_content *result;
nserror error;
- result = calloc(1, sizeof(nsgif_content));
+ result = calloc(1, sizeof(gif_content));
if (result == NULL)
return NSERROR_NOMEM;
@@ -114,7 +131,7 @@ static nserror nsgif_create(const content_handler *handler,
return error;
}
- error = nsgif_create_gif_data(result);
+ error = gif_create_gif_data(result);
if (error != NSERROR_OK) {
free(result);
return error;
@@ -126,99 +143,65 @@ static nserror nsgif_create(const content_handler *handler,
}
/**
+ * Scheduler callback. Performs any necessary animation.
+ *
+ * \param p The content to animate
+*/
+static void gif_animate_cb(void *p);
+
+/**
* Performs any necessary animation.
*
* \param p The content to animate
*/
-static void nsgif_animate(void *p)
+static nserror gif__animate(gif_content *gif, bool redraw)
{
- nsgif_content *gif = p;
- union content_msg_data data;
- int delay;
- int f;
-
- /* Advance by a frame, updating the loop count accordingly */
- gif->current_frame++;
- if (gif->current_frame == (int)gif->gif->frame_count_partial) {
- gif->current_frame = 0;
-
- /* A loop count of 0 has a special meaning of infinite */
- if (gif->gif->loop_count != 0) {
- gif->gif->loop_count--;
- if (gif->gif->loop_count == 0) {
- gif->current_frame =
- gif->gif->frame_count_partial - 1;
- gif->gif->loop_count = -1;
- }
- }
+ nsgif_result gif_res;
+ nsgif_rect rect;
+ uint32_t delay;
+ uint32_t f;
+
+ gif_res = nsgif_frame_prepare(gif->gif, &rect, &delay, &f);
+ if (gif_res != NSGIF_OK) {
+ return gif__nsgif_error_to_ns(gif_res);
}
+ gif->current_frame = f;
+
/* Continue animating if we should */
- if (gif->gif->loop_count >= 0) {
- delay = gif->gif->frames[gif->current_frame].frame_delay;
- if (delay <= 1) {
- /* Assuming too fast to be intended, set default. */
- delay = 10;
- }
- guit->misc->schedule(delay * 10, nsgif_animate, gif);
+ if (nsoption_bool(animate_images) && delay != NSGIF_INFINITE) {
+ guit->misc->schedule(delay * 10, gif_animate_cb, gif);
}
- if ((!nsoption_bool(animate_images)) ||
- (!gif->gif->frames[gif->current_frame].display)) {
- return;
- }
+ if (redraw) {
+ union content_msg_data data;
- /* area within gif to redraw */
- f = gif->current_frame;
- data.redraw.x = gif->gif->frames[f].redraw_x;
- data.redraw.y = gif->gif->frames[f].redraw_y;
- data.redraw.width = gif->gif->frames[f].redraw_width;
- data.redraw.height = gif->gif->frames[f].redraw_height;
-
- /* redraw background (true) or plot on top (false) */
- if (gif->current_frame > 0) {
- /* previous frame needed clearing: expand the redraw area to
- * cover it */
- if (gif->gif->frames[f - 1].redraw_required) {
- if (data.redraw.x >
- (int)(gif->gif->frames[f - 1].redraw_x)) {
- data.redraw.width += data.redraw.x -
- gif->gif->frames[f - 1].redraw_x;
- data.redraw.x =
- gif->gif->frames[f - 1].redraw_x;
- }
- if (data.redraw.y >
- (int)(gif->gif->frames[f - 1].redraw_y)) {
- data.redraw.height += (data.redraw.y -
- gif->gif->frames[f - 1].redraw_y);
- data.redraw.y =
- gif->gif->frames[f - 1].redraw_y;
- }
- if ((int)(gif->gif->frames[f - 1].redraw_x +
- gif->gif->frames[f - 1].redraw_width) >
- (data.redraw.x + data.redraw.width))
- data.redraw.width =
- gif->gif->frames[f - 1].redraw_x -
- data.redraw.x +
- gif->gif->frames[f - 1].redraw_width;
- if ((int)(gif->gif->frames[f - 1].redraw_y +
- gif->gif->frames[f - 1].redraw_height) >
- (data.redraw.y + data.redraw.height))
- data.redraw.height =
- gif->gif->frames[f - 1].redraw_y -
- data.redraw.y +
- gif->gif->frames[f - 1].redraw_height;
- }
+ /* area within gif to redraw */
+ data.redraw.x = rect.x0;
+ data.redraw.y = rect.y0;
+ data.redraw.width = rect.x1 - rect.x0;
+ data.redraw.height = rect.y1 - rect.y0;
+
+ content_broadcast(&gif->base, CONTENT_MSG_REDRAW, &data);
}
- content_broadcast(&gif->base, CONTENT_MSG_REDRAW, &data);
+ return NSERROR_OK;
+}
+
+static void gif_animate_cb(void *p)
+{
+ gif_content *gif = p;
+
+ gif__animate(gif, true);
}
-static bool nsgif_convert(struct content *c)
+static bool gif_convert(struct content *c)
{
- nsgif_content *gif = (nsgif_content *) c;
- int res;
+ gif_content *gif = (gif_content *) c;
+ const nsgif_info_t *gif_info;
const uint8_t *data;
+ nsgif_result res;
+ nserror err;
size_t size;
char *title;
@@ -226,37 +209,28 @@ static bool nsgif_convert(struct content *c)
data = content__get_source_data(c, &size);
/* Initialise the GIF */
- do {
- res = gif_initialise(gif->gif, size, (unsigned char *) data);
- if (res != GIF_OK && res != GIF_WORKING &&
- res != GIF_INSUFFICIENT_FRAME_DATA) {
- nserror error = NSERROR_UNKNOWN;
- switch (res) {
- case GIF_FRAME_DATA_ERROR:
- case GIF_INSUFFICIENT_DATA:
- case GIF_DATA_ERROR:
- error = NSERROR_GIF_ERROR;
- break;
- case GIF_INSUFFICIENT_MEMORY:
- error = NSERROR_NOMEM;
- break;
- }
- content_broadcast_error(c, error, NULL);
- return false;
- }
- } while (res != GIF_OK && res != GIF_INSUFFICIENT_FRAME_DATA);
+ res = nsgif_data_scan(gif->gif, size, data);
+ if (res != NSGIF_OK) {
+ err = gif__nsgif_error_to_ns(res);
+ content_broadcast_error(c, err, NULL);
+ return false;
+ }
+
+ gif_info = nsgif_get_info(gif->gif);
+ assert(gif_info != NULL);
/* Abort on bad GIFs */
- if ((gif->gif->frame_count_partial == 0) || (gif->gif->width == 0) ||
- (gif->gif->height == 0)) {
- content_broadcast_error(c, NSERROR_GIF_ERROR, NULL);
+ if (res != NSGIF_INSUFFICIENT_FRAME_DATA ||
+ gif_info->height == 0) {
+ err = gif__nsgif_error_to_ns(res);
+ content_broadcast_error(c, err, NULL);
return false;
}
/* Store our content width, height and calculate size */
- c->width = gif->gif->width;
- c->height = gif->gif->height;
- c->size += (gif->gif->width * gif->gif->height * 4) + 16 + 44;
+ c->width = gif_info->width;
+ c->height = gif_info->height;
+ c->size += (gif_info->width * gif_info->height * 4) + 16 + 44;
/* set title text */
title = messages_get_buff("GIFTitle",
@@ -267,12 +241,11 @@ static bool nsgif_convert(struct content *c)
free(title);
}
- /* Schedule the animation if we have one */
- gif->current_frame = 0;
- if (gif->gif->frame_count_partial > 1)
- guit->misc->schedule(gif->gif->frames[0].frame_delay * 10,
- nsgif_animate,
- c);
+ err = gif__animate(gif, false);
+ if (err != NSERROR_OK) {
+ content_broadcast_error(c, NSERROR_GIF_ERROR, NULL);
+ return false;
+ }
/* Exit as a success */
content_set_ready(c);
@@ -283,68 +256,51 @@ static bool nsgif_convert(struct content *c)
return true;
}
-
/**
* Updates the GIF bitmap to display the current frame
*
* \param gif The gif context to update.
- * \return GIF_OK on success else apropriate error code.
+ * \return NSGIF_OK on success else apropriate error code.
*/
-static gif_result nsgif_get_frame(nsgif_content *gif)
+static nsgif_result gif_get_frame(gif_content *gif,
+ nsgif_bitmap_t **bitmap)
{
- int previous_frame, current_frame, frame;
- gif_result res = GIF_OK;
-
- current_frame = gif->current_frame;
+ uint32_t current_frame = gif->current_frame;
if (!nsoption_bool(animate_images)) {
current_frame = 0;
}
- if (current_frame < gif->gif->decoded_frame) {
- previous_frame = 0;
- } else {
- previous_frame = gif->gif->decoded_frame + 1;
- }
-
- for (frame = previous_frame; frame <= current_frame; frame++) {
- res = gif_decode_frame(gif->gif, frame);
- }
-
- return res;
+ return nsgif_frame_decode(gif->gif, current_frame, bitmap);
}
-static bool nsgif_redraw(struct content *c, struct content_redraw_data *data,
+static bool gif_redraw(struct content *c, struct content_redraw_data *data,
const struct rect *clip, const struct redraw_context *ctx)
{
- nsgif_content *gif = (nsgif_content *) c;
+ gif_content *gif = (gif_content *) c;
+ nsgif_bitmap_t *bitmap;
- if (gif->current_frame != gif->gif->decoded_frame) {
- if (nsgif_get_frame(gif) != GIF_OK) {
- return false;
- }
+ if (gif_get_frame(gif, &bitmap) != NSGIF_OK) {
+ return false;
}
- return image_bitmap_plot(gif->gif->frame_image, data, clip, ctx);
+ return image_bitmap_plot(bitmap, data, clip, ctx);
}
-
-static void nsgif_destroy(struct content *c)
+static void gif_destroy(struct content *c)
{
- nsgif_content *gif = (nsgif_content *) c;
+ gif_content *gif = (gif_content *) c;
/* Free all the associated memory buffers */
- guit->misc->schedule(-1, nsgif_animate, c);
- gif_finalise(gif->gif);
- free(gif->gif);
+ guit->misc->schedule(-1, gif_animate_cb, c);
+ nsgif_destroy(gif->gif);
}
-
-static nserror nsgif_clone(const struct content *old, struct content **newc)
+static nserror gif_clone(const struct content *old, struct content **newc)
{
- nsgif_content *gif;
+ gif_content *gif;
nserror error;
- gif = calloc(1, sizeof(nsgif_content));
+ gif = calloc(1, sizeof(gif_content));
if (gif == NULL)
return NSERROR_NOMEM;
@@ -355,7 +311,7 @@ static nserror nsgif_clone(const struct content *old, struct content **newc)
}
/* Simply replay creation and conversion of content */
- error = nsgif_create_gif_data(gif);
+ error = gif_create_gif_data(gif);
if (error != NSERROR_OK) {
content_destroy(&gif->base);
return error;
@@ -363,7 +319,7 @@ static nserror nsgif_clone(const struct content *old, struct content **newc)
if (old->status == CONTENT_STATUS_READY ||
old->status == CONTENT_STATUS_DONE) {
- if (nsgif_convert(&gif->base) == false) {
+ if (gif_convert(&gif->base) == false) {
content_destroy(&gif->base);
return NSERROR_CLONE_FAILED;
}
@@ -374,9 +330,9 @@ static nserror nsgif_clone(const struct content *old, struct content **newc)
return NSERROR_OK;
}
-static void nsgif_add_user(struct content *c)
+static void gif_add_user(struct content *c)
{
- nsgif_content *gif = (nsgif_content *) c;
+ gif_content *gif = (gif_content *) c;
/* Ensure this content has already been converted.
* If it hasn't, the animation will start at the conversion phase instead. */
@@ -384,67 +340,66 @@ static void nsgif_add_user(struct content *c)
if (content_count_users(c) == 1) {
/* First user, and content already converted, so start the animation. */
- if (gif->gif->frame_count_partial > 1) {
- guit->misc->schedule(gif->gif->frames[0].frame_delay * 10,
- nsgif_animate, c);
+ if (nsgif_reset(gif->gif) == NSGIF_OK) {
+ gif__animate(gif, true);
}
}
}
-static void nsgif_remove_user(struct content *c)
+static void gif_remove_user(struct content *c)
{
if (content_count_users(c) == 1) {
/* Last user is about to be removed from this content, so stop the animation. */
- guit->misc->schedule(-1, nsgif_animate, c);
+ guit->misc->schedule(-1, gif_animate_cb, c);
}
}
-static void *nsgif_get_internal(const struct content *c, void *context)
+static nsgif_bitmap_t *gif_get_bitmap(
+ const struct content *c, void *context)
{
- nsgif_content *gif = (nsgif_content *) c;
+ gif_content *gif = (gif_content *) c;
+ nsgif_bitmap_t *bitmap;
- if (gif->current_frame != gif->gif->decoded_frame) {
- if (nsgif_get_frame(gif) != GIF_OK)
- return NULL;
+ if (gif_get_frame(gif, &bitmap) != NSGIF_OK) {
+ return NULL;
}
- return gif->gif->frame_image;
+ return bitmap;
}
-static content_type nsgif_content_type(void)
+static content_type gif_content_type(void)
{
return CONTENT_IMAGE;
}
-static bool nsgif_content_is_opaque(struct content *c)
+static bool gif_content_is_opaque(struct content *c)
{
- nsgif_content *gif = (nsgif_content *) c;
+ gif_content *gif = (gif_content *) c;
+ nsgif_bitmap_t *bitmap;
- if (gif->current_frame != gif->gif->decoded_frame) {
- if (nsgif_get_frame(gif) != GIF_OK) {
- return false;
- }
+ if (gif_get_frame(gif, &bitmap) != NSGIF_OK) {
+ return false;
}
- return guit->bitmap->get_opaque(gif->gif->frame_image);
+ return guit->bitmap->get_opaque(bitmap);
}
-static const content_handler nsgif_content_handler = {
- .create = nsgif_create,
- .data_complete = nsgif_convert,
- .destroy = nsgif_destroy,
- .redraw = nsgif_redraw,
- .clone = nsgif_clone,
- .add_user = nsgif_add_user,
- .remove_user = nsgif_remove_user,
- .get_internal = nsgif_get_internal,
- .type = nsgif_content_type,
- .is_opaque = nsgif_content_is_opaque,
+static const content_handler gif_content_handler = {
+ .create = gif_create,
+ .data_complete = gif_convert,
+ .destroy = gif_destroy,
+ .redraw = gif_redraw,
+ .clone = gif_clone,
+ .add_user = gif_add_user,
+ .remove_user = gif_remove_user,
+ .get_internal = gif_get_bitmap,
+ .type = gif_content_type,
+ .is_opaque = gif_content_is_opaque,
.no_share = false,
};
-static const char *nsgif_types[] = {
+static const char *gif_types[] = {
"image/gif"
};
-CONTENT_FACTORY_REGISTER_TYPES(nsgif, nsgif_types, nsgif_content_handler);
+CONTENT_FACTORY_REGISTER_TYPES(nsgif, gif_types, gif_content_handler);
-----------------------------------------------------------------------
Summary of changes:
content/handlers/image/gif.c | 113 ++++++++++++++++++++++--------------------
frontends/amiga/libs.c | 4 +-
2 files changed, 62 insertions(+), 55 deletions(-)
diff --git a/content/handlers/image/gif.c b/content/handlers/image/gif.c
index d7dedef..fc896f8 100644
--- a/content/handlers/image/gif.c
+++ b/content/handlers/image/gif.c
@@ -59,6 +59,25 @@ typedef struct gif_content {
uint32_t current_frame; /**< current frame to display [0...(max-1)] */
} gif_content;
+static inline nserror gif__nsgif_error_to_ns(nsgif_result gif_res)
+{
+ nserror err;
+
+ switch (gif_res) {
+ case NSGIF_OK:
+ err = NSERROR_OK;
+ break;
+ case NSGIF_INSUFFICIENT_MEMORY:
+ err = NSERROR_NOMEM;
+ break;
+ default:
+ err = NSERROR_GIF_ERROR;
+ break;
+ }
+
+ return err;
+}
+
/**
* Callback for libnsgif; forwards the call to bitmap_create()
*
@@ -73,7 +92,8 @@ static void *gif_bitmap_create(int width, int height)
static nserror gif_create_gif_data(gif_content *c)
{
- nsgif_bitmap_cb_vt gif_bitmap_callbacks = {
+ nsgif_result gif_res;
+ const nsgif_bitmap_cb_vt gif_bitmap_callbacks = {
.create = gif_bitmap_create,
.destroy = guit->bitmap->destroy,
.get_buffer = guit->bitmap->get_buffer,
@@ -82,13 +102,13 @@ static nserror gif_create_gif_data(gif_content *c)
.modified = guit->bitmap->modified
};
- /* Initialise our data structure */
- c->gif = calloc(sizeof(nsgif), 1);
- if (c->gif == NULL) {
- content_broadcast_error(&c->base, NSERROR_NOMEM, NULL);
- return NSERROR_NOMEM;
+ gif_res = nsgif_create(&gif_bitmap_callbacks, &c->gif);
+ if (gif_res != NSGIF_OK) {
+ nserror err = gif__nsgif_error_to_ns(gif_res);
+ content_broadcast_error(&c->base, err, NULL);
+ return err;
}
- nsgif_create(c->gif, &gif_bitmap_callbacks);
+
return NSERROR_OK;
}
@@ -129,25 +149,6 @@ static nserror gif_create(const content_handler *handler,
*/
static void gif_animate_cb(void *p);
-static inline nserror gif__nsgif_error_to_ns(nsgif_result gif_res)
-{
- nserror err;
-
- switch (gif_res) {
- case NSGIF_OK:
- err = NSERROR_OK;
- break;
- case NSGIF_INSUFFICIENT_MEMORY:
- err = NSERROR_NOMEM;
- break;
- default:
- err = NSERROR_GIF_ERROR;
- break;
- }
-
- return err;
-}
-
/**
* Performs any necessary animation.
*
@@ -197,6 +198,7 @@ static void gif_animate_cb(void *p)
static bool gif_convert(struct content *c)
{
gif_content *gif = (gif_content *) c;
+ const nsgif_info_t *gif_info;
const uint8_t *data;
nsgif_result res;
nserror err;
@@ -207,27 +209,28 @@ static bool gif_convert(struct content *c)
data = content__get_source_data(c, &size);
/* Initialise the GIF */
- do {
- res = nsgif_data_scan(gif->gif, size, data);
- if (res != NSGIF_OK && res != NSGIF_WORKING &&
- res != NSGIF_INSUFFICIENT_FRAME_DATA) {
- err = gif__nsgif_error_to_ns(res);
- content_broadcast_error(c, err, NULL);
- return false;
- }
- } while (res != NSGIF_OK && res != NSGIF_INSUFFICIENT_FRAME_DATA);
+ res = nsgif_data_scan(gif->gif, size, data);
+ if (res != NSGIF_OK) {
+ err = gif__nsgif_error_to_ns(res);
+ content_broadcast_error(c, err, NULL);
+ return false;
+ }
+
+ gif_info = nsgif_get_info(gif->gif);
+ assert(gif_info != NULL);
/* Abort on bad GIFs */
- if ((gif->gif->frame_count_partial == 0) || (gif->gif->width == 0) ||
- (gif->gif->height == 0)) {
- content_broadcast_error(c, NSERROR_GIF_ERROR, NULL);
+ if (res != NSGIF_INSUFFICIENT_FRAME_DATA ||
+ gif_info->height == 0) {
+ err = gif__nsgif_error_to_ns(res);
+ content_broadcast_error(c, err, NULL);
return false;
}
/* Store our content width, height and calculate size */
- c->width = gif->gif->width;
- c->height = gif->gif->height;
- c->size += (gif->gif->width * gif->gif->height * 4) + 16 + 44;
+ c->width = gif_info->width;
+ c->height = gif_info->height;
+ c->size += (gif_info->width * gif_info->height * 4) + 16 + 44;
/* set title text */
title = messages_get_buff("GIFTitle",
@@ -259,26 +262,28 @@ static bool gif_convert(struct content *c)
* \param gif The gif context to update.
* \return NSGIF_OK on success else apropriate error code.
*/
-static nsgif_result gif_get_frame(gif_content *gif)
+static nsgif_result gif_get_frame(gif_content *gif,
+ nsgif_bitmap_t **bitmap)
{
uint32_t current_frame = gif->current_frame;
if (!nsoption_bool(animate_images)) {
current_frame = 0;
}
- return nsgif_frame_decode(gif->gif, current_frame);
+ return nsgif_frame_decode(gif->gif, current_frame, bitmap);
}
static bool gif_redraw(struct content *c, struct content_redraw_data *data,
const struct rect *clip, const struct redraw_context *ctx)
{
gif_content *gif = (gif_content *) c;
+ nsgif_bitmap_t *bitmap;
- if (gif_get_frame(gif) != NSGIF_OK) {
+ if (gif_get_frame(gif, &bitmap) != NSGIF_OK) {
return false;
}
- return image_bitmap_plot(gif->gif->frame_image, data, clip, ctx);
+ return image_bitmap_plot(bitmap, data, clip, ctx);
}
static void gif_destroy(struct content *c)
@@ -287,8 +292,7 @@ static void gif_destroy(struct content *c)
/* Free all the associated memory buffers */
guit->misc->schedule(-1, gif_animate_cb, c);
- nsgif_finalise(gif->gif);
- free(gif->gif);
+ nsgif_destroy(gif->gif);
}
static nserror gif_clone(const struct content *old, struct content **newc)
@@ -350,15 +354,17 @@ static void gif_remove_user(struct content *c)
}
}
-static void *gif_get_internal(const struct content *c, void *context)
+static nsgif_bitmap_t *gif_get_bitmap(
+ const struct content *c, void *context)
{
gif_content *gif = (gif_content *) c;
+ nsgif_bitmap_t *bitmap;
- if (gif_get_frame(gif) != NSGIF_OK) {
+ if (gif_get_frame(gif, &bitmap) != NSGIF_OK) {
return NULL;
}
- return gif->gif->frame_image;
+ return bitmap;
}
static content_type gif_content_type(void)
@@ -369,12 +375,13 @@ static content_type gif_content_type(void)
static bool gif_content_is_opaque(struct content *c)
{
gif_content *gif = (gif_content *) c;
+ nsgif_bitmap_t *bitmap;
- if (gif_get_frame(gif) != NSGIF_OK) {
+ if (gif_get_frame(gif, &bitmap) != NSGIF_OK) {
return false;
}
- return guit->bitmap->get_opaque(gif->gif->frame_image);
+ return guit->bitmap->get_opaque(bitmap);
}
static const content_handler gif_content_handler = {
@@ -385,7 +392,7 @@ static const content_handler gif_content_handler = {
.clone = gif_clone,
.add_user = gif_add_user,
.remove_user = gif_remove_user,
- .get_internal = gif_get_internal,
+ .get_internal = gif_get_bitmap,
.type = gif_content_type,
.is_opaque = gif_content_is_opaque,
.no_share = false,
diff --git a/frontends/amiga/libs.c b/frontends/amiga/libs.c
index 3c09bcb..eb1f216 100644
--- a/frontends/amiga/libs.c
+++ b/frontends/amiga/libs.c
@@ -223,6 +223,7 @@ bool ami_libs_open(void)
#ifdef __amigaos4__
/* Libraries only needed on OS4 */
AMINS_LIB_OPEN("application.library", 53, Application, "application", 2, false)
+ AMINS_LIB_OPEN("dos.library", 37, DOS, "main", 1, true)
#else
/* Libraries we get automatically on OS4 but not OS3 */
AMINS_LIB_OPEN("utility.library", 37, Utility, "main", 1, true)
@@ -231,7 +232,6 @@ bool ami_libs_open(void)
AMINS_LIB_OPEN("asl.library", 37, Asl, "main", 1, true)
AMINS_LIB_OPEN("datatypes.library", 39, DataTypes, "main", 1, true)
AMINS_LIB_OPEN("diskfont.library", 40, Diskfont, "main", 1, true)
- AMINS_LIB_OPEN("dos.library", 37, DOS, "main", 1, true)
AMINS_LIB_OPEN("gadtools.library", 37, GadTools, "main", 1, true)
AMINS_LIB_OPEN("graphics.library", 40, Graphics, "main", 1, true)
AMINS_LIB_OPEN("icon.library", 44, Icon, "main", 1, true)
@@ -332,7 +332,6 @@ void ami_libs_close(void)
AMINS_LIB_CLOSE(Asl)
AMINS_LIB_CLOSE(DataTypes)
AMINS_LIB_CLOSE(Diskfont)
- AMINS_LIB_CLOSE(DOS)
AMINS_LIB_CLOSE(GadTools)
AMINS_LIB_CLOSE(Graphics)
AMINS_LIB_CLOSE(Icon)
@@ -345,6 +344,7 @@ void ami_libs_close(void)
AMINS_LIB_CLOSE(Workbench)
#ifdef __amigaos4__
AMINS_LIB_CLOSE(Application)
+ AMINS_LIB_CLOSE(DOS)
#else
AMINS_LIB_CLOSE(Utility)
#endif
--
NetSurf Browser
1 year, 9 months
libnsgif: branch tlsa/gif-api updated. release/0.2.1-139-ga04e9ba
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/libnsgif.git/shortlog/a04e9ba76c39bf020212...
...commit http://git.netsurf-browser.org/libnsgif.git/commit/a04e9ba76c39bf02021253...
...tree http://git.netsurf-browser.org/libnsgif.git/tree/a04e9ba76c39bf020212536e...
The branch, tlsa/gif-api has been updated
discards 3fdfe53755954ba41b2003a5d05c7e705d075d98 (commit)
discards cfe1f467ed248ebd8131b29634eadac89e00aa4d (commit)
discards ce17f177e640bdd979959782183ced4e636d7c98 (commit)
discards c1654e6dcfe4bb9f400fae7a82610a13f4abc899 (commit)
discards 56b04ab7b2617e090c1c82f828782b7e3253dfa9 (commit)
discards 3efe269c0ea83df2b8fc5885d36e0074ff904527 (commit)
discards 5c673578e310de6e254c27da735195285073483d (commit)
discards de0f5828f63d0db82c4157701a763796420eaa80 (commit)
discards c1a577e96ca0067817dce90a007a66321eae56a8 (commit)
discards 83b32786e2a3f15fbf921fe5204474382480d6cb (commit)
discards 5d732c75bf4012fe0398a979d72806fa10713966 (commit)
via a04e9ba76c39bf020212536e04d976a6370a1adf (commit)
via c5a9b1d67688166391346b94b03e36c8089305f7 (commit)
via d5d6e9057bccdb7e41e5243276b7a3e0a2748817 (commit)
via 45f97add7c916d68c1339245bd55643abda71822 (commit)
via 9c08b7c139cb95be8b3fbf21c46c564bfaff8c56 (commit)
via 88a078653a15d157b7c6f3ba57211f11f9121dab (commit)
via 0526c55f20ee7480533567fad62ea00e6bf31786 (commit)
via b5dfaef72b2e2725810320cd2cf703463b3e95e1 (commit)
via aec9ee665b18e5929d9af192e13efb5105c52254 (commit)
via fedd37d9ce70571f305c8c8e66fd9ec7d837585b (commit)
via 87c9a0ce41757c9c0ff2f3f19054edfd8296ef31 (commit)
via 2013c162af618e701ba4caf84fa304ac38c92501 (commit)
via 958ea3a13e8d5ffd23f2540d7a0eb2926667a206 (commit)
via 1e3f649e2b78bf936ac3ca83c0992b0cb9add327 (commit)
via 4a384cb88b02fb086e1035971dd142115cf9b377 (commit)
via abfffeaad949c9f47bb6058ee17e88c7092ffbe6 (commit)
via 0823ded6d3858a4acaaaca57b2a4803a0a5884c8 (commit)
via c9703eb11102d27e22f5f5119db111dd1b41f559 (commit)
via d15788827c453a1103b926672b7cdb1f1a21f487 (commit)
via 6d39a8f70009c43032d96da8ceb8934e6e67a159 (commit)
via 696b29ffeb71d77f9ede7dd19408cf713923684a (commit)
via 152c4a7429d9364ab163997dd6304665f5baa2e5 (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 (3fdfe53755954ba41b2003a5d05c7e705d075d98)
\
N -- N -- N (a04e9ba76c39bf020212536e04d976a6370a1adf)
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=a04e9ba76c39bf0202...
commit a04e9ba76c39bf020212536e04d976a6370a1adf
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
API: Clean up client bitmap interface.
diff --git a/include/nsgif.h b/include/nsgif.h
index 11378d1..1472842 100644
--- a/include/nsgif.h
+++ b/include/nsgif.h
@@ -73,31 +73,66 @@ typedef struct nsgif_rect {
uint32_t y1;
} nsgif_rect;
-/* API for Bitmap callbacks */
-typedef void* (*nsgif_bitmap_cb_create)(int width, int height);
-typedef void (*nsgif_bitmap_cb_destroy)(void *bitmap);
-typedef uint8_t* (*nsgif_bitmap_cb_get_buffer)(void *bitmap);
-typedef void (*nsgif_bitmap_cb_set_opaque)(void *bitmap, bool opaque);
-typedef bool (*nsgif_bitmap_cb_test_opaque)(void *bitmap);
-typedef void (*nsgif_bitmap_cb_modified)(void *bitmap);
+/**
+ * Client bitmap type.
+ *
+ * These are client-created and destroyed, via the \ref bitmap callbacks,
+ * but they are owned by a \ref nsgif.
+ */
+typedef void nsgif_bitmap_t;
/** Bitmap callbacks function table */
typedef struct nsgif_bitmap_cb_vt {
- /** Create a bitmap. */
- nsgif_bitmap_cb_create create;
- /** Free a bitmap. */
- nsgif_bitmap_cb_destroy destroy;
- /** Return a pointer to the pixel data in a bitmap. */
- nsgif_bitmap_cb_get_buffer get_buffer;
-
- /* Members below are optional */
-
- /** Sets whether a bitmap should be plotted opaque. */
- nsgif_bitmap_cb_set_opaque set_opaque;
- /** Tests whether a bitmap has an opaque alpha channel. */
- nsgif_bitmap_cb_test_opaque test_opaque;
- /** The bitmap image has changed, so flush any persistent cache. */
- nsgif_bitmap_cb_modified modified;
+ /**
+ * Callback to create a bitmap with the given dimensions.
+ *
+ * \param[in] width Required bitmap width in pixels.
+ * \param[in] height Required bitmap height in pixels.
+ * \return pointer to client's bitmap structure or NULL on error.
+ */
+ nsgif_bitmap_t* (*create)(int width, int height);
+
+ /**
+ * Callback to free a bitmap.
+ *
+ * \param[in] bitmap The bitmap to destroy.
+ */
+ void (*destroy)(nsgif_bitmap_t *bitmap);
+
+ /**
+ * Get pointer to pixel buffer in a bitmap.
+ *
+ * The pixel buffer must be `width * height * sizeof(uint32_t)`.
+ *
+ * \param[in] bitmap The bitmap.
+ * \return pointer to bitmap's pixel buffer.
+ */
+ uint8_t* (*get_buffer)(nsgif_bitmap_t *bitmap);
+
+ /* The following functions are optional. */
+
+ /**
+ * Set whether a bitmap can be plotted opaque.
+ *
+ * \param[in] bitmap The bitmap.
+ * \param[in] opaque Whether the current frame is opaque.
+ */
+ void (*set_opaque)(nsgif_bitmap_t *bitmap, bool opaque);
+
+ /**
+ * Tests whether a bitmap has an opaque alpha channel.
+ *
+ * \param[in] bitmap The bitmap.
+ * \return true if the bitmap is opaque, false otherwise.
+ */
+ bool (*test_opaque)(nsgif_bitmap_t *bitmap);
+
+ /**
+ * Bitmap modified notification.
+ *
+ * \param[in] bitmap The bitmap.
+ */
+ void (*modified)(nsgif_bitmap_t *bitmap);
} nsgif_bitmap_cb_vt;
/**
@@ -161,8 +196,10 @@ nsgif_result nsgif_frame_prepare(
/**
* Decodes a GIF frame.
*
- * \param[in] gif The nsgif object.
- * \param[in] frame The frame number to decode.
+ * \param[in] gif The nsgif object.
+ * \param[in] frame The frame number to decode.
+ * \param[out] bitmap On success, returns pointer to the client-allocated,
+ * nsgif-owned client bitmap structure.
* \return Error return value.
* - NSGIF_FRAME_DATA_ERROR for GIF frame data error
* - NSGIF_DATA_ERROR for GIF error (invalid frame header)
@@ -173,7 +210,7 @@ nsgif_result nsgif_frame_prepare(
nsgif_result nsgif_frame_decode(
nsgif *gif,
uint32_t frame,
- const uint32_t **buffer);
+ nsgif_bitmap_t **bitmap);
/**
* Reset a GIF animation.
diff --git a/src/gif.c b/src/gif.c
index dd29f0a..5ed38d8 100644
--- a/src/gif.c
+++ b/src/gif.c
@@ -67,7 +67,7 @@ struct nsgif {
/** current frame decoded to bitmap */
uint32_t decoded_frame;
/** currently decoded image; stored as bitmap from bitmap_create callback */
- void *frame_image;
+ nsgif_bitmap_t *frame_image;
uint16_t delay_default;
/** number of frames partially decoded */
@@ -1685,13 +1685,13 @@ nsgif_result nsgif_frame_prepare(
nsgif_result nsgif_frame_decode(
nsgif *gif,
uint32_t frame,
- const uint32_t **buffer)
+ nsgif_bitmap_t **bitmap)
{
uint32_t start_frame;
nsgif_result ret = NSGIF_OK;
if (gif->decoded_frame == frame) {
- *buffer = gif->frame_image;
+ *bitmap = gif->frame_image;
return NSGIF_OK;
} else if (gif->decoded_frame >= frame ||
@@ -1710,7 +1710,7 @@ nsgif_result nsgif_frame_decode(
}
}
- *buffer = gif->frame_image;
+ *bitmap = gif->frame_image;
return ret;
}
diff --git a/test/decode_gif.c b/test/decode_gif.c
index 5f5c377..acc2ef0 100644
--- a/test/decode_gif.c
+++ b/test/decode_gif.c
@@ -122,7 +122,7 @@ static void decode(FILE* fh, const char *name, nsgif *gif, bool write_ppm)
/* decode the frames */
while (true) {
- const uint32_t *buffer;
+ nsgif_bitmap_t *buffer;
const uint8_t *image;
uint32_t frame_new;
uint32_t delay_cs;
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=c5a9b1d67688166391...
commit c5a9b1d67688166391346b94b03e36c8089305f7
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
f
diff --git a/src/gif.c b/src/gif.c
index ff29b98..dd29f0a 100644
--- a/src/gif.c
+++ b/src/gif.c
@@ -101,10 +101,6 @@ struct nsgif {
void *prev_frame;
/** previous frame index */
uint32_t prev_index;
- /** previous frame width */
- uint32_t prev_width;
- /** previous frame height */
- uint32_t prev_height;
};
/**
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=d5d6e9057bccdb7e41...
commit d5d6e9057bccdb7e41e5243276b7a3e0a2748817
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Don't need previous frame size now.
The image dimensions are constant now.
diff --git a/src/gif.c b/src/gif.c
index 1656db8..ff29b98 100644
--- a/src/gif.c
+++ b/src/gif.c
@@ -267,7 +267,6 @@ static void nsgif__record_frame(
struct nsgif *gif,
const uint32_t *bitmap)
{
- bool need_alloc = gif->prev_frame == NULL;
uint32_t *prev_frame;
if (gif->decoded_frame == NSGIF_FRAME_INVALID ||
@@ -281,12 +280,7 @@ static void nsgif__record_frame(
return;
}
- if (gif->prev_frame != NULL &&
- gif->info.width * gif->info.height > gif->prev_width * gif->prev_height) {
- need_alloc = true;
- }
-
- if (need_alloc) {
+ if (gif->prev_frame == NULL) {
prev_frame = realloc(gif->prev_frame,
gif->info.width * gif->info.height * 4);
if (prev_frame == NULL) {
@@ -299,8 +293,6 @@ static void nsgif__record_frame(
memcpy(prev_frame, bitmap, gif->info.width * gif->info.height * 4);
gif->prev_frame = prev_frame;
- gif->prev_width = gif->info.width;
- gif->prev_height = gif->info.height;
gif->prev_index = gif->decoded_frame;
}
@@ -309,19 +301,14 @@ static nsgif_result nsgif__recover_frame(
uint32_t *bitmap)
{
const uint32_t *prev_frame = gif->prev_frame;
- unsigned height = gif->info.height < gif->prev_height ? gif->info.height : gif->prev_height;
- unsigned width = gif->info.width < gif->prev_width ? gif->info.width : gif->prev_width;
+ unsigned height = gif->info.height;
+ unsigned width = gif->info.width;
if (prev_frame == NULL) {
return NSGIF_FRAME_DATA_ERROR;
}
- for (unsigned y = 0; y < height; y++) {
- memcpy(bitmap, prev_frame, width * 4);
-
- bitmap += gif->info.width;
- prev_frame += gif->prev_width;
- }
+ memcpy(bitmap, prev_frame, height * width * sizeof(*bitmap));
return NSGIF_OK;
}
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=45f97add7c916d68c1...
commit 45f97add7c916d68c1339245bd55643abda71822
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Avoid two colour table allocations.
Just having the arrays in the nsgif object reduces allocations
and improves cache locality.
diff --git a/src/gif.c b/src/gif.c
index 163761a..1656db8 100644
--- a/src/gif.c
+++ b/src/gif.c
@@ -17,6 +17,9 @@
#include "lzw.h"
#include "nsgif.h"
+/** Maximum colour table size */
+#define NSGIF_MAX_COLOURS 256
+
/** GIF frame data */
typedef struct nsgif_frame {
/** whether the frame should be displayed/animated */
@@ -84,14 +87,15 @@ struct nsgif {
uint32_t aspect_ratio;
/** size of colour table (in entries) */
uint32_t colour_table_size;
+
/** whether the GIF has a global colour table */
bool global_colours;
- /** global colour table */
- uint32_t *global_colour_table;
- /** local colour table */
- uint32_t *local_colour_table;
/** current colour table */
uint32_t *colour_table;
+ /** global colour table */
+ uint32_t global_colour_table[NSGIF_MAX_COLOURS];
+ /** local colour table */
+ uint32_t local_colour_table[NSGIF_MAX_COLOURS];
/** previous frame for NSGIF_FRAME_RESTORE */
void *prev_frame;
@@ -114,9 +118,6 @@ struct nsgif {
* \todo Plain text and comment extensions should be implemented.
*/
-/** Maximum colour table size */
-#define NSGIF_MAX_COLOURS 256
-
/** Internal flag that the colour table needs to be processed */
#define NSGIF_PROCESS_COLOURS 0xaa000000
@@ -1005,7 +1006,7 @@ static nsgif_result nsgif__parse_image_descriptor(
*/
static nsgif_result nsgif__colour_table_extract(
struct nsgif *gif,
- uint32_t *colour_table,
+ uint32_t colour_table[NSGIF_MAX_COLOURS],
size_t colour_table_entries,
const uint8_t **pos,
bool decode)
@@ -1312,12 +1313,6 @@ void nsgif_destroy(nsgif *gif)
free(gif->frames);
gif->frames = NULL;
- free(gif->local_colour_table);
- gif->local_colour_table = NULL;
-
- free(gif->global_colour_table);
- gif->global_colour_table = NULL;
-
free(gif->prev_frame);
gif->prev_frame = NULL;
@@ -1456,8 +1451,6 @@ nsgif_result nsgif_data_scan(
gif->frame_image = NULL;
gif->frames = NULL;
gif->frame_holders = 0;
- gif->local_colour_table = NULL;
- gif->global_colour_table = NULL;
/* The caller may have been lazy and not reset any values */
gif->info.frame_count = 0;
@@ -1495,18 +1488,6 @@ nsgif_result nsgif_data_scan(
gif->info.height = 1;
}
- /* Allocate some data irrespective of whether we've got any
- * colour tables. We always get the maximum size in case a GIF
- * 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(NSGIF_MAX_COLOURS, sizeof(uint32_t));
- gif->local_colour_table = calloc(NSGIF_MAX_COLOURS, sizeof(uint32_t));
- if ((gif->global_colour_table == NULL) ||
- (gif->local_colour_table == NULL)) {
- return NSGIF_INSUFFICIENT_MEMORY;
- }
-
/* Set the first colour to a value that will never occur in
* reality so we know if we've processed it
*/
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=9c08b7c139cb95be8b...
commit 9c08b7c139cb95be8b3fbf21c46c564bfaff8c56
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
Test: Decoder: Simplify logic.
diff --git a/test/decode_gif.c b/test/decode_gif.c
index 5789c41..5f5c377 100644
--- a/test/decode_gif.c
+++ b/test/decode_gif.c
@@ -101,7 +101,7 @@ static void warning(const char *context, nsgif_result gif_res)
fprintf(stderr, "\n");
}
-static void write_ppm(FILE* fh, const char *name, nsgif *gif, bool no_write)
+static void decode(FILE* fh, const char *name, nsgif *gif, bool write_ppm)
{
nsgif_result gif_res;
uint32_t frame_prev = 0;
@@ -109,7 +109,7 @@ static void write_ppm(FILE* fh, const char *name, nsgif *gif, bool no_write)
info = nsgif_get_info(gif);
- if (!no_write) {
+ if (write_ppm) {
fprintf(fh, "P3\n");
fprintf(fh, "# %s\n", name);
fprintf(fh, "# width %u \n", info->width);
@@ -148,7 +148,7 @@ static void write_ppm(FILE* fh, const char *name, nsgif *gif, bool no_write)
return;
}
- if (!no_write) {
+ if (write_ppm) {
fprintf(fh, "# frame %u:\n", frame_new);
image = (const uint8_t *) buffer;
for (uint32_t y = 0; y != info->height; y++) {
@@ -211,18 +211,16 @@ int main(int argc, char *argv[])
/* load file into memory */
data = load_file(argv[1], &size);
- /* begin decoding */
- do {
- gif_res = nsgif_data_scan(gif, size, data);
- if (gif_res != NSGIF_OK && gif_res != NSGIF_WORKING) {
- warning("nsgif_data_scan", gif_res);
- nsgif_destroy(gif);
- free(data);
- return 1;
- }
- } while (gif_res != NSGIF_OK);
+ /* Scan the raw data */
+ gif_res = nsgif_data_scan(gif, size, data);
+ if (gif_res != NSGIF_OK) {
+ warning("nsgif_data_scan", gif_res);
+ nsgif_destroy(gif);
+ free(data);
+ return 1;
+ }
- write_ppm(outf, argv[1], gif, no_write);
+ decode(outf, argv[1], gif, !no_write);
if (argc > 2 && !no_write) {
fclose(outf);
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=88a078653a15d157b7...
commit 88a078653a15d157b7c6f3ba57211f11f9121dab
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
Test: Update decode test to new API.
diff --git a/test/decode_gif.c b/test/decode_gif.c
index a20d5f4..5789c41 100644
--- a/test/decode_gif.c
+++ b/test/decode_gif.c
@@ -18,51 +18,28 @@
#include "../include/nsgif.h"
#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)) {
+ /* Ensure a stupidly large bitmap is not created */
+ if (width > 4096 || height > 4096) {
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);
-}
-static bool bitmap_test_opaque(void *bitmap)
-{
- (void) bitmap; /* unused */
- assert(bitmap);
- return false;
+ return calloc(width * height, BYTES_PER_PIXEL);
}
static unsigned char *bitmap_get_buffer(void *bitmap)
{
- assert(bitmap);
return bitmap;
}
static void bitmap_destroy(void *bitmap)
{
- assert(bitmap);
free(bitmap);
}
-static void bitmap_modified(void *bitmap)
-{
- (void) bitmap; /* unused */
- assert(bitmap);
- return;
-}
-
-static unsigned char *load_file(const char *path, size_t *data_size)
+static uint8_t *load_file(const char *path, size_t *data_size)
{
FILE *fd;
struct stat sb;
@@ -101,11 +78,10 @@ static unsigned char *load_file(const char *path, size_t *data_size)
return buffer;
}
-static void warning(const char *context, nsgif_result code)
+static void warning(const char *context, nsgif_result gif_res)
{
fprintf(stderr, "%s failed: ", context);
- switch (code)
- {
+ switch (gif_res) {
case NSGIF_FRAME_DATA_ERROR:
fprintf(stderr, "NSGIF_FRAME_DATA_ERROR");
break;
@@ -119,52 +95,69 @@ static void warning(const char *context, nsgif_result code)
fprintf(stderr, "NSGIF_INSUFFICIENT_MEMORY");
break;
default:
- fprintf(stderr, "unknown code %i", code);
+ fprintf(stderr, "unknown code %i", gif_res);
break;
}
fprintf(stderr, "\n");
}
-static void write_ppm(FILE* fh, const char *name, nsgif *gif,
- bool no_write)
+static void write_ppm(FILE* fh, const char *name, nsgif *gif, bool no_write)
{
- unsigned int i;
- nsgif_result code;
+ nsgif_result gif_res;
+ uint32_t frame_prev = 0;
+ const nsgif_info_t *info;
+
+ info = nsgif_get_info(gif);
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);
+ fprintf(fh, "# width %u \n", info->width);
+ fprintf(fh, "# height %u \n", info->height);
+ fprintf(fh, "# frame_count %u \n", info->frame_count);
+ fprintf(fh, "# loop_max %u \n", info->loop_max);
+ fprintf(fh, "%u %u 256\n", info->width,
+ info->height * info->frame_count);
}
/* decode the frames */
- for (i = 0; i != gif->frame_count; i++) {
- unsigned int row, col;
- unsigned char *image;
+ while (true) {
+ const uint32_t *buffer;
+ const uint8_t *image;
+ uint32_t frame_new;
+ uint32_t delay_cs;
+ nsgif_rect area;
+
+ gif_res = nsgif_frame_prepare(gif, &area,
+ &delay_cs, &frame_new);
+ if (gif_res != NSGIF_OK) {
+ warning("nsgif_frame_prepare", gif_res);
+ return;
+ }
- code = nsgif_decode_frame(gif, i);
- if (code != NSGIF_OK)
- warning("nsgif_decode_frame", code);
+ if (frame_new < frame_prev) {
+ /* Must be an animation that loops. We only care about
+ * decoding each frame once. */
+ return;
+ }
+ frame_prev = frame_new;
- if (!gif->frames[i].display) {
- continue;
+ gif_res = nsgif_frame_decode(gif, frame_new, &buffer);
+ if (gif_res != NSGIF_OK) {
+ warning("nsgif_decode_frame", gif_res);
+ return;
}
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, "# frame %u:\n", frame_new);
+ image = (const uint8_t *) buffer;
+ for (uint32_t y = 0; y != info->height; y++) {
+ for (uint32_t x = 0; x != info->width; x++) {
+ size_t z = (y * info->width + x) * 4;
fprintf(fh, "%u %u %u ",
- (unsigned char) image[z],
- (unsigned char) image[z + 1],
- (unsigned char) image[z + 2]);
+ image[z],
+ image[z + 1],
+ image[z + 2]);
}
fprintf(fh, "\n");
}
@@ -174,19 +167,16 @@ static void write_ppm(FILE* fh, const char *name, nsgif *gif,
int main(int argc, char *argv[])
{
- nsgif_bitmap_cb_vt bitmap_callbacks = {
- bitmap_create,
- bitmap_destroy,
- bitmap_get_buffer,
- bitmap_set_opaque,
- bitmap_test_opaque,
- bitmap_modified
+ const nsgif_bitmap_cb_vt bitmap_callbacks = {
+ .create = bitmap_create,
+ .destroy = bitmap_destroy,
+ .get_buffer = bitmap_get_buffer,
};
- nsgif gif;
+ nsgif *gif;
size_t size;
- nsgif_result code;
- unsigned char *data;
+ uint8_t *data;
FILE *outf = stdout;
+ nsgif_result gif_res;
bool no_write = false;
if (argc < 2) {
@@ -213,30 +203,33 @@ int main(int argc, char *argv[])
}
/* create our gif animation */
- nsgif_create(&gif, &bitmap_callbacks);
+ gif_res = nsgif_create(&bitmap_callbacks, &gif);
+ if (gif_res != NSGIF_OK) {
+ return 1;
+ }
/* load file into memory */
data = load_file(argv[1], &size);
/* begin decoding */
do {
- code = nsgif_initialise(&gif, size, data);
- if (code != NSGIF_OK && code != NSGIF_WORKING) {
- warning("nsgif_initialise", code);
- nsgif_finalise(&gif);
+ gif_res = nsgif_data_scan(gif, size, data);
+ if (gif_res != NSGIF_OK && gif_res != NSGIF_WORKING) {
+ warning("nsgif_data_scan", gif_res);
+ nsgif_destroy(gif);
free(data);
return 1;
}
- } while (code != NSGIF_OK);
+ } while (gif_res != NSGIF_OK);
- write_ppm(outf, argv[1], &gif, no_write);
+ write_ppm(outf, argv[1], gif, no_write);
if (argc > 2 && !no_write) {
fclose(outf);
}
/* clean up */
- nsgif_finalise(&gif);
+ nsgif_destroy(gif);
free(data);
return 0;
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=0526c55f20ee748053...
commit 0526c55f20ee7480533567fad62ea00e6bf31786
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
API: Rework library API to keep internal workings private.
diff --git a/include/nsgif.h b/include/nsgif.h
index 5e69b7e..11378d1 100644
--- a/include/nsgif.h
+++ b/include/nsgif.h
@@ -18,6 +18,26 @@
#include <stdint.h>
#include <stdbool.h>
+#include <inttypes.h>
+
+#define NSGIF_INFINITE (UINT32_MAX)
+
+typedef struct nsgif nsgif;
+
+typedef struct nsgif_info {
+ /** width of GIF (may increase during decoding) */
+ uint32_t width;
+ /** height of GIF (may increase during decoding) */
+ uint32_t height;
+ /** number of frames decoded */
+ uint32_t frame_count;
+ /** number of times to loop animation */
+ int loop_max;
+ /** number of animation loops so far */
+ int loop_count;
+
+ uint16_t delay_min;
+} nsgif_info_t;
/* Error return values */
typedef enum {
@@ -29,51 +49,30 @@ typedef enum {
NSGIF_DATA_ERROR = -4,
NSGIF_INSUFFICIENT_MEMORY = -5,
NSGIF_FRAME_NO_DISPLAY = -6,
- NSGIF_END_OF_FRAME = -7
+ NSGIF_END_OF_FRAME = -7,
+ NSGIF_FRAME_INVALID = -8,
+ NSGIF_ANIMATION_COMPLETE = -9,
} nsgif_result;
-/** GIF rectangle structure. */
+/**
+ * GIF rectangle structure.
+ *
+ * * Top left coordinate is `(x0, y0)`.
+ * * Width is `x1 - x0`.
+ * * Height is `y1 - y0`.
+ * * Units are pixels.
+ */
typedef struct nsgif_rect {
- /** x co-ordinate of redraw rectangle */
- uint32_t x;
- /** y co-ordinate of redraw rectangle */
- uint32_t y;
- /** width of redraw rectangle */
- uint32_t w;
- /** height of redraw rectangle */
- uint32_t h;
+ /** x co-ordinate of redraw rectangle, left */
+ uint32_t x0;
+ /** y co-ordinate of redraw rectangle, top */
+ uint32_t y0;
+ /** x co-ordinate of redraw rectangle, right */
+ uint32_t x1;
+ /** y co-ordinate of redraw rectangle, bottom */
+ uint32_t y1;
} nsgif_rect;
-/** GIF frame data */
-typedef struct nsgif_frame {
- /** whether the frame should be displayed/animated */
- bool display;
- /** delay (in cs) before animating the frame */
- uint32_t frame_delay;
-
- /* Internal members are listed below */
-
- /** offset (in bytes) to the GIF frame data */
- uint32_t frame_pointer;
- /** whether the frame has previously been decoded. */
- bool decoded;
- /** 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 */
- uint8_t disposal_method;
- /** whether we acknowledge transparency */
- bool transparency;
- /** the index designating a transparent pixel */
- uint32_t transparency_index;
- /* Frame flags */
- uint32_t flags;
-
- /** Frame's redraw rectangle. */
- nsgif_rect redraw;
-} nsgif_frame;
-
/* API for Bitmap callbacks */
typedef void* (*nsgif_bitmap_cb_create)(int width, int height);
typedef void (*nsgif_bitmap_cb_destroy)(void *bitmap);
@@ -101,75 +100,36 @@ typedef struct nsgif_bitmap_cb_vt {
nsgif_bitmap_cb_modified modified;
} nsgif_bitmap_cb_vt;
-/** GIF animation data */
-typedef struct nsgif {
- /** LZW decode context */
- void *lzw_ctx;
- /** callbacks for bitmap functions */
- nsgif_bitmap_cb_vt bitmap;
- /** pointer to GIF data */
- const uint8_t *nsgif_data;
- /** width of GIF (may increase during decoding) */
- uint32_t width;
- /** height of GIF (may increase during decoding) */
- uint32_t height;
- /** number of frames decoded */
- uint32_t frame_count;
- /** number of frames partially decoded */
- uint32_t frame_count_partial;
- /** decoded frames */
- nsgif_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 */
- uint32_t buffer_position;
- /** total number of bytes of GIF data available */
- uint32_t buffer_size;
- /** current number of frame holders */
- uint32_t frame_holders;
- /** background index */
- uint32_t bg_index;
- /** background colour */
- uint32_t bg_colour;
- /** image aspect ratio (ignored) */
- uint32_t aspect_ratio;
- /** size of colour table (in entries) */
- uint32_t colour_table_size;
- /** whether the GIF has a global colour table */
- bool global_colours;
- /** global colour table */
- uint32_t *global_colour_table;
- /** local colour table */
- uint32_t *local_colour_table;
- /** current colour table */
- uint32_t *colour_table;
-
- /** previous frame for NSGIF_FRAME_RESTORE */
- void *prev_frame;
- /** previous frame index */
- int prev_index;
- /** previous frame width */
- unsigned prev_width;
- /** previous frame height */
- unsigned prev_height;
-} nsgif;
-
/**
- * Initialises necessary nsgif members.
+ * Create the NSGIF object.
+ *
+ * \param[in] bitmap_vt Bitmap operation functions v-table.
+ * \param[out] gif_out Return NSGIF object on success.
+ * \return NSGIF_OK on success, or appropriate error otherwise.
*/
-void nsgif_create(nsgif *gif, nsgif_bitmap_cb_vt *bitmap_callbacks);
+nsgif_result nsgif_create(const nsgif_bitmap_cb_vt *bitmap_vt, nsgif **gif_out);
/**
- * Initialises any workspace held by the animation and attempts to decode
- * any information that hasn't already been decoded.
- * If an error occurs, all previously decoded frames are retained.
+ * Scan the source image data.
+ *
+ * This is used to feed the source data into LibNSGIF. This must be called
+ * before calling \ref nsgif_frame_decode.
+ *
+ * It can be called multiple times with, with increasing sizes. If it is called
+ * several times, as more data is available (e.g. slow network fetch) the data
+ * already given to \ref nsgif_data_scan must be provided each time.
+ *
+ * For example, if you call \ref nsgif_data_scan with 25 bytes of data, and then
+ * fetch another 10 bytes, you would need to call \ref nsgif_data with a size of
+ * 35 bytes, and the whole 35 bytes must be contiguous memory. It is safe to
+ * `realloc` the source buffer between calls to \ref nsgif_data_scan. (The
+ * actual data pointer is allowed to be different.)
+ *
+ * If an error occurs, all previously scanned frames are retained.
+ *
+ * \param[in] gif The NSGIF object.
+ * \param[in] size Number of bytes in data.
+ * \param[in] data Raw source GIF data.
*
* \return Error return value.
* - NSGIF_FRAME_DATA_ERROR for GIF frame data error
@@ -179,11 +139,30 @@ void nsgif_create(nsgif *gif, nsgif_bitmap_cb_vt *bitmap_callbacks);
* - NSGIF_OK for successful decoding
* - NSGIF_WORKING for successful decoding if more frames are expected
*/
-nsgif_result nsgif_initialise(nsgif *gif, size_t size, const uint8_t *data);
+nsgif_result nsgif_data_scan(
+ nsgif *gif,
+ size_t size,
+ const uint8_t *data);
+
+/**
+ * Prepare to show a frame.
+ *
+ * \param[in] gif The NSGIF object.
+ * \param[out] area The area in pixels that must be redrawn.
+ * \param[out] delay_cs Time to wait after frame_new before next frame in cs.
+ * \param[out] frame_new The frame to decode.
+ */
+nsgif_result nsgif_frame_prepare(
+ nsgif *gif,
+ nsgif_rect *area,
+ uint32_t *delay_cs,
+ uint32_t *frame_new);
/**
* Decodes a GIF frame.
*
+ * \param[in] gif The nsgif object.
+ * \param[in] frame The frame number to decode.
* \return Error return value.
* - NSGIF_FRAME_DATA_ERROR for GIF frame data error
* - NSGIF_DATA_ERROR for GIF error (invalid frame header)
@@ -191,11 +170,40 @@ nsgif_result nsgif_initialise(nsgif *gif, size_t size, const uint8_t *data);
* - NSGIF_INSUFFICIENT_MEMORY for insufficient memory to process
* - NSGIF_OK for successful decoding
*/
-nsgif_result nsgif_decode_frame(nsgif *gif, uint32_t frame);
+nsgif_result nsgif_frame_decode(
+ nsgif *gif,
+ uint32_t frame,
+ const uint32_t **buffer);
/**
- * Releases any workspace held by a gif
+ * Reset a GIF animation.
+ *
+ * Some animations are only meant to loop N times, and then show the
+ * final frame forever. This function resets the loop and frame counters,
+ * so that the animation can be replayed without the overhead of recreating
+ * the NSGIF object and rescanning the raw data.
+ *
+ * \param[in] gif A NSGIF object.
+ *
+ * \return NSGIF_OK on success, or appropriate error otherwise.
+ */
+nsgif_result nsgif_reset(
+ nsgif *gif);
+
+/**
+ * Get information about a GIF from an NSGIF object.
+ *
+ * \param[in] gif The NSGIF object to get info for.
+ *
+ * \return The gif info, or NULL on error.
+ */
+const nsgif_info_t *nsgif_get_info(const nsgif *gif);
+
+/**
+ * Free a NSGIF object.
+ *
+ * \param[in] gif The NSGIF to free.
*/
-void nsgif_finalise(nsgif *gif);
+void nsgif_destroy(nsgif *gif);
#endif
diff --git a/src/gif.c b/src/gif.c
index cb784a9..163761a 100644
--- a/src/gif.c
+++ b/src/gif.c
@@ -17,6 +17,92 @@
#include "lzw.h"
#include "nsgif.h"
+/** GIF frame data */
+typedef struct nsgif_frame {
+ /** whether the frame should be displayed/animated */
+ bool display;
+ /** delay (in cs) before animating the frame */
+ uint32_t frame_delay;
+
+ /* Internal members are listed below */
+
+ /** offset (in bytes) to the GIF frame data */
+ uint32_t frame_pointer;
+ /** whether the frame has previously been decoded. */
+ bool decoded;
+ /** 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 */
+ uint8_t disposal_method;
+ /** whether we acknowledge transparency */
+ bool transparency;
+ /** the index designating a transparent pixel */
+ uint32_t transparency_index;
+ /* Frame flags */
+ uint32_t flags;
+
+ /** Frame's redraw rectangle. */
+ nsgif_rect redraw;
+} nsgif_frame;
+
+/** GIF animation data */
+struct nsgif {
+ struct nsgif_info info;
+
+ /** LZW decode context */
+ void *lzw_ctx;
+ /** callbacks for bitmap functions */
+ nsgif_bitmap_cb_vt bitmap;
+ /** pointer to GIF data */
+ const uint8_t *nsgif_data;
+ /** decoded frames */
+ nsgif_frame *frames;
+ /** current frame */
+ uint32_t frame;
+ /** current frame decoded to bitmap */
+ uint32_t decoded_frame;
+ /** currently decoded image; stored as bitmap from bitmap_create callback */
+ void *frame_image;
+
+ uint16_t delay_default;
+ /** number of frames partially decoded */
+ uint32_t frame_count_partial;
+
+ /** current index into GIF data */
+ uint32_t buffer_position;
+ /** total number of bytes of GIF data available */
+ uint32_t buffer_size;
+ /** current number of frame holders */
+ uint32_t frame_holders;
+ /** background index */
+ uint32_t bg_index;
+ /** background colour */
+ uint32_t bg_colour;
+ /** image aspect ratio (ignored) */
+ uint32_t aspect_ratio;
+ /** size of colour table (in entries) */
+ uint32_t colour_table_size;
+ /** whether the GIF has a global colour table */
+ bool global_colours;
+ /** global colour table */
+ uint32_t *global_colour_table;
+ /** local colour table */
+ uint32_t *local_colour_table;
+ /** current colour table */
+ uint32_t *colour_table;
+
+ /** previous frame for NSGIF_FRAME_RESTORE */
+ void *prev_frame;
+ /** previous frame index */
+ uint32_t prev_index;
+ /** previous frame width */
+ uint32_t prev_width;
+ /** previous frame height */
+ uint32_t prev_height;
+};
+
/**
*
* \file
@@ -35,7 +121,7 @@
#define NSGIF_PROCESS_COLOURS 0xaa000000
/** Internal flag that a frame is invalid/unprocessed */
-#define NSGIF_INVALID_FRAME -1
+#define NSGIF_FRAME_INVALID UINT32_MAX
/** Transparent colour */
#define NSGIF_TRANSPARENT_COLOUR 0x00
@@ -118,7 +204,7 @@ static inline uint32_t* nsgif__bitmap_get(
nsgif_result ret;
/* Make sure we have a buffer to decode to. */
- ret = nsgif__initialise_sprite(gif, gif->width, gif->height);
+ ret = nsgif__initialise_sprite(gif, gif->info.width, gif->info.height);
if (ret != NSGIF_OK) {
return NULL;
}
@@ -183,7 +269,7 @@ static void nsgif__record_frame(
bool need_alloc = gif->prev_frame == NULL;
uint32_t *prev_frame;
- if (gif->decoded_frame == NSGIF_INVALID_FRAME ||
+ if (gif->decoded_frame == NSGIF_FRAME_INVALID ||
gif->decoded_frame == gif->prev_index) {
/* No frame to copy, or already have this frame recorded. */
return;
@@ -195,13 +281,13 @@ static void nsgif__record_frame(
}
if (gif->prev_frame != NULL &&
- gif->width * gif->height > gif->prev_width * gif->prev_height) {
+ gif->info.width * gif->info.height > gif->prev_width * gif->prev_height) {
need_alloc = true;
}
if (need_alloc) {
prev_frame = realloc(gif->prev_frame,
- gif->width * gif->height * 4);
+ gif->info.width * gif->info.height * 4);
if (prev_frame == NULL) {
return;
}
@@ -209,11 +295,11 @@ static void nsgif__record_frame(
prev_frame = gif->prev_frame;
}
- memcpy(prev_frame, bitmap, gif->width * gif->height * 4);
+ memcpy(prev_frame, bitmap, gif->info.width * gif->info.height * 4);
gif->prev_frame = prev_frame;
- gif->prev_width = gif->width;
- gif->prev_height = gif->height;
+ gif->prev_width = gif->info.width;
+ gif->prev_height = gif->info.height;
gif->prev_index = gif->decoded_frame;
}
@@ -222,8 +308,8 @@ static nsgif_result nsgif__recover_frame(
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;
+ unsigned height = gif->info.height < gif->prev_height ? gif->info.height : gif->prev_height;
+ unsigned width = gif->info.width < gif->prev_width ? gif->info.width : gif->prev_width;
if (prev_frame == NULL) {
return NSGIF_FRAME_DATA_ERROR;
@@ -232,7 +318,7 @@ static nsgif_result nsgif__recover_frame(
for (unsigned y = 0; y < height; y++) {
memcpy(bitmap, prev_frame, width * 4);
- bitmap += gif->width;
+ bitmap += gif->info.width;
prev_frame += gif->prev_width;
}
@@ -347,16 +433,16 @@ static nsgif_result nsgif__decode_complex(
{
lzw_result res;
nsgif_result ret = NSGIF_OK;
- uint32_t clip_x = gif__clip(offset_x, width, gif->width);
- uint32_t clip_y = gif__clip(offset_y, height, gif->height);
+ uint32_t clip_x = gif__clip(offset_x, width, gif->info.width);
+ uint32_t clip_y = gif__clip(offset_y, height, gif->info.height);
const uint8_t *uncompressed;
uint32_t available = 0;
uint8_t step = 24;
uint32_t skip = 0;
uint32_t y = 0;
- if (offset_x >= gif->width ||
- offset_y >= gif->height) {
+ if (offset_x >= gif->info.width ||
+ offset_y >= gif->info.height) {
return NSGIF_OK;
}
@@ -380,7 +466,7 @@ static nsgif_result nsgif__decode_complex(
uint32_t *frame_scanline;
frame_scanline = frame_data + offset_x +
- (y + offset_y) * gif->width;
+ (y + offset_y) * gif->info.width;
x = width;
while (x > 0) {
@@ -441,16 +527,16 @@ static nsgif_result nsgif__decode_simple(
uint32_t *restrict frame_data,
uint32_t *restrict colour_table)
{
- uint32_t pixels = gif->width * height;
+ uint32_t pixels = gif->info.width * height;
uint32_t written = 0;
nsgif_result ret = NSGIF_OK;
lzw_result res;
- if (offset_y >= gif->height) {
+ if (offset_y >= gif->info.height) {
return NSGIF_OK;
}
- height -= gif__clip(offset_y, height, gif->height);
+ height -= gif__clip(offset_y, height, gif->info.height);
if (height == 0) {
return NSGIF_OK;
@@ -465,7 +551,7 @@ static nsgif_result nsgif__decode_simple(
return nsgif__error_from_lzw(res);
}
- frame_data += (offset_y * gif->width);
+ frame_data += (offset_y * gif->info.width);
while (pixels > 0) {
res = lzw_decode_map(gif->lzw_ctx,
@@ -501,15 +587,15 @@ static inline nsgif_result nsgif__decode(
};
nsgif_result ret;
- uint32_t width = frame->redraw.w;
- uint32_t height = frame->redraw.h;
- uint32_t offset_x = frame->redraw.x;
- uint32_t offset_y = frame->redraw.y;
+ uint32_t width = frame->redraw.x1 - frame->redraw.x0;
+ uint32_t height = frame->redraw.y1 - frame->redraw.y0;
+ uint32_t offset_x = frame->redraw.x0;
+ uint32_t offset_y = frame->redraw.y0;
uint32_t interlace = frame->flags & GIF_MASK_INTERLACE;
uint32_t transparency_index = frame->transparency_index;
uint32_t *restrict colour_table = gif->colour_table;
- if (interlace == false && width == gif->width && offset_x == 0) {
+ if (interlace == false && width == gif->info.width && offset_x == 0) {
ret = nsgif__decode_simple(gif, height, offset_y,
data, transparency_index,
frame_data, colour_table);
@@ -537,15 +623,15 @@ static void nsgif__restore_bg(
{
if (frame == NULL) {
memset(bitmap, NSGIF_TRANSPARENT_COLOUR,
- gif->width * gif->height * sizeof(*bitmap));
+ gif->info.width * gif->info.height * sizeof(*bitmap));
} else {
- uint32_t width = frame->redraw.w;
- uint32_t height = frame->redraw.h;
- uint32_t offset_x = frame->redraw.x;
- uint32_t offset_y = frame->redraw.y;
+ uint32_t width = frame->redraw.x1 - frame->redraw.x0;
+ uint32_t height = frame->redraw.y1 - frame->redraw.y0;
+ uint32_t offset_x = frame->redraw.x0;
+ uint32_t offset_y = frame->redraw.y0;
- width -= gif__clip(offset_x, width, gif->width);
- height -= gif__clip(offset_y, height, gif->height);
+ width -= gif__clip(offset_x, width, gif->info.width);
+ height -= gif__clip(offset_y, height, gif->info.height);
if (frame->display == false || width == 0) {
return;
@@ -554,14 +640,14 @@ static void nsgif__restore_bg(
if (frame->transparency) {
for (uint32_t y = 0; y < height; y++) {
uint32_t *scanline = bitmap + offset_x +
- (offset_y + y) * gif->width;
+ (offset_y + y) * gif->info.width;
memset(scanline, NSGIF_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;
+ (offset_y + y) * gif->info.width;
for (uint32_t x = 0; x < width; x++) {
scanline[x] = gif->bg_colour;
}
@@ -588,7 +674,7 @@ static nsgif_result nsgif__update_bitmap(
/* Handle any bitmap clearing/restoration required before decoding this
* frame. */
- if (frame_idx == 0 || gif->decoded_frame == NSGIF_INVALID_FRAME) {
+ if (frame_idx == 0 || gif->decoded_frame == NSGIF_FRAME_INVALID) {
nsgif__restore_bg(gif, NULL, bitmap);
} else {
@@ -626,6 +712,7 @@ static nsgif_result nsgif__update_bitmap(
/**
* Parse the graphic control extension
*
+ * \param[in] gif The gif object we're decoding.
* \param[in] frame The gif object we're decoding.
* \param[in] data The data to decode.
* \param[in] len Byte length of data.
@@ -633,6 +720,7 @@ static nsgif_result nsgif__update_bitmap(
* NSGIF_OK for success.
*/
static nsgif_result nsgif__parse_extension_graphic_control(
+ const struct nsgif *gif,
struct nsgif_frame *frame,
const uint8_t *data,
size_t len)
@@ -659,6 +747,10 @@ static nsgif_result nsgif__parse_extension_graphic_control(
}
frame->frame_delay = data[3] | (data[4] << 8);
+ if (frame->frame_delay < gif->info.delay_min) {
+ frame->frame_delay = gif->delay_default;
+ }
+
if (data[2] & GIF_MASK_TRANSPARENCY) {
frame->transparency = true;
frame->transparency_index = data[5];
@@ -713,7 +805,7 @@ static nsgif_result nsgif__parse_extension_application(
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);
+ gif->info.loop_max = data[15] | (data[16] << 8);
}
return NSGIF_OK;
@@ -763,7 +855,9 @@ static nsgif_result nsgif__parse_frame_extensions(
case GIF_EXT_GRAPHIC_CONTROL:
if (decode) {
ret = nsgif__parse_extension_graphic_control(
- frame, nsgif_data, nsgif_bytes);
+ gif, frame,
+ nsgif_data,
+ nsgif_bytes);
if (ret != NSGIF_OK) {
return ret;
}
@@ -867,7 +961,7 @@ static nsgif_result nsgif__parse_image_descriptor(
}
if (decode) {
- unsigned x, y, w, h;
+ uint32_t x, y, w, h;
if (data[0] != NSGIF_IMAGE_SEPARATOR) {
return NSGIF_FRAME_DATA_ERROR;
@@ -879,18 +973,18 @@ static nsgif_result nsgif__parse_image_descriptor(
h = data[7] | (data[8] << 8);
frame->flags = data[9];
- frame->redraw.x = x;
- frame->redraw.y = y;
- frame->redraw.w = w;
- frame->redraw.h = h;
+ frame->redraw.x0 = x;
+ frame->redraw.y0 = y;
+ frame->redraw.x1 = x + w;
+ frame->redraw.y1 = y + h;
/* Allow first frame to grow image dimensions. */
- if (gif->frame_count == 0) {
- if (x + w > gif->width) {
- gif->width = x + w;
+ if (gif->info.frame_count == 0) {
+ if (x + w > gif->info.width) {
+ gif->info.width = x + w;
}
- if (y + h > gif->height) {
- gif->height = y + h;
+ if (y + h > gif->info.height) {
+ gif->info.height = y + h;
}
}
}
@@ -1045,7 +1139,7 @@ static nsgif_result nsgif__parse_image_data(
while (block_size != 1) {
if (len < 1) return NSGIF_INSUFFICIENT_DATA;
block_size = data[0] + 1;
- /* Check if the frame data runs off the end of the file */
+ /* Check if the frame data runs off the end of the file */
if (block_size > len) {
block_size = len;
return NSGIF_OK;
@@ -1055,7 +1149,7 @@ static nsgif_result nsgif__parse_image_data(
data += block_size;
}
- gif->frame_count = frame_idx + 1;
+ gif->info.frame_count = frame_idx + 1;
gif->frames[frame_idx].display = true;
*pos = data;
@@ -1154,7 +1248,7 @@ static nsgif_result nsgif__process_frame(
}
/* Done if frame is already decoded */
- if ((int)frame_idx == gif->decoded_frame) {
+ if (frame_idx == gif->decoded_frame) {
return NSGIF_OK;
}
} else {
@@ -1201,13 +1295,57 @@ cleanup:
return ret;
}
-/* exported function documented in libnsgif.h */
-void nsgif_create(nsgif *gif, nsgif_bitmap_cb_vt *bitmap)
+/* exported function documented in nsgif.h */
+void nsgif_destroy(nsgif *gif)
{
- memset(gif, 0, sizeof(nsgif));
- gif->bitmap = *bitmap;
- gif->decoded_frame = NSGIF_INVALID_FRAME;
- gif->prev_index = NSGIF_INVALID_FRAME;
+ if (gif == NULL) {
+ return;
+ }
+
+ /* Release all our memory blocks */
+ if (gif->frame_image) {
+ assert(gif->bitmap.destroy);
+ gif->bitmap.destroy(gif->frame_image);
+ gif->frame_image = NULL;
+ }
+
+ free(gif->frames);
+ gif->frames = NULL;
+
+ free(gif->local_colour_table);
+ gif->local_colour_table = NULL;
+
+ free(gif->global_colour_table);
+ gif->global_colour_table = NULL;
+
+ free(gif->prev_frame);
+ gif->prev_frame = NULL;
+
+ lzw_context_destroy(gif->lzw_ctx);
+ gif->lzw_ctx = NULL;
+
+ free(gif);
+}
+
+/* exported function documented in nsgif.h */
+nsgif_result nsgif_create(const nsgif_bitmap_cb_vt *bitmap_vt, nsgif **gif_out)
+{
+ nsgif *gif;
+
+ gif = calloc(1, sizeof(*gif));
+ if (gif == NULL) {
+ return NSGIF_INSUFFICIENT_MEMORY;
+ }
+
+ gif->bitmap = *bitmap_vt;
+ gif->decoded_frame = NSGIF_FRAME_INVALID;
+ gif->prev_index = NSGIF_FRAME_INVALID;
+
+ gif->info.delay_min = 2;
+ gif->delay_default = 10;
+
+ *gif_out = gif;
+ return NSGIF_OK;
}
/**
@@ -1282,20 +1420,23 @@ static nsgif_result nsgif__parse_logical_screen_descriptor(
return NSGIF_INSUFFICIENT_DATA;
}
- gif->width = data[0] | (data[1] << 8);
- gif->height = data[2] | (data[3] << 8);
+ gif->info.width = data[0] | (data[1] << 8);
+ gif->info.height = data[2] | (data[3] << 8);
gif->global_colours = data[4] & NSGIF_COLOUR_TABLE_MASK;
gif->colour_table_size = 2 << (data[4] & NSGIF_COLOUR_TABLE_SIZE_MASK);
gif->bg_index = data[5];
gif->aspect_ratio = data[6];
- gif->loop_count = 1;
+ gif->info.loop_max = 1;
*pos += 7;
return NSGIF_OK;
}
-/* exported function documented in libnsgif.h */
-nsgif_result nsgif_initialise(nsgif *gif, size_t size, const uint8_t *data)
+/* exported function documented in nsgif.h */
+nsgif_result nsgif_data_scan(
+ nsgif *gif,
+ size_t size,
+ const uint8_t *data)
{
const uint8_t *nsgif_data;
nsgif_result ret;
@@ -1319,9 +1460,10 @@ nsgif_result nsgif_initialise(nsgif *gif, size_t size, const uint8_t *data)
gif->global_colour_table = NULL;
/* The caller may have been lazy and not reset any values */
- gif->frame_count = 0;
+ gif->info.frame_count = 0;
gif->frame_count_partial = 0;
- gif->decoded_frame = NSGIF_INVALID_FRAME;
+ gif->decoded_frame = NSGIF_FRAME_INVALID;
+ gif->frame = NSGIF_FRAME_INVALID;
ret = nsgif__parse_header(gif, &nsgif_data, false);
if (ret != NSGIF_OK) {
@@ -1341,16 +1483,16 @@ nsgif_result nsgif_initialise(nsgif *gif, size_t size, const uint8_t *data)
* set the sizes as 0 if they are found which results in the
* GIF being the maximum size of the frames.
*/
- if (((gif->width == 640) && (gif->height == 480)) ||
- ((gif->width == 640) && (gif->height == 512)) ||
- ((gif->width == 800) && (gif->height == 600)) ||
- ((gif->width == 1024) && (gif->height == 768)) ||
- ((gif->width == 1280) && (gif->height == 1024)) ||
- ((gif->width == 1600) && (gif->height == 1200)) ||
- ((gif->width == 0) || (gif->height == 0)) ||
- ((gif->width > 2048) || (gif->height > 2048))) {
- gif->width = 1;
- gif->height = 1;
+ if (((gif->info.width == 640) && (gif->info.height == 480)) ||
+ ((gif->info.width == 640) && (gif->info.height == 512)) ||
+ ((gif->info.width == 800) && (gif->info.height == 600)) ||
+ ((gif->info.width == 1024) && (gif->info.height == 768)) ||
+ ((gif->info.width == 1280) && (gif->info.height == 1024)) ||
+ ((gif->info.width == 1600) && (gif->info.height == 1200)) ||
+ ((gif->info.width == 0) || (gif->info.height == 0)) ||
+ ((gif->info.width > 2048) || (gif->info.height > 2048))) {
+ gif->info.width = 1;
+ gif->info.height = 1;
}
/* Allocate some data irrespective of whether we've got any
@@ -1362,7 +1504,6 @@ nsgif_result nsgif_initialise(nsgif *gif, size_t size, const uint8_t *data)
gif->local_colour_table = calloc(NSGIF_MAX_COLOURS, sizeof(uint32_t));
if ((gif->global_colour_table == NULL) ||
(gif->local_colour_table == NULL)) {
- nsgif_finalise(gif);
return NSGIF_INSUFFICIENT_MEMORY;
}
@@ -1430,38 +1571,186 @@ nsgif_result nsgif_initialise(nsgif *gif, size_t size, const uint8_t *data)
/* Repeatedly try to initialise frames */
do {
- ret = nsgif__process_frame(gif, gif->frame_count, false);
+ ret = nsgif__process_frame(gif, gif->info.frame_count, false);
} while (ret == NSGIF_WORKING);
return ret;
}
-/* exported function documented in libnsgif.h */
-nsgif_result nsgif_decode_frame(nsgif *gif, uint32_t frame)
+static void nsgif__redraw_rect_extend(const nsgif_rect *frame, nsgif_rect *redraw)
{
- return nsgif__process_frame(gif, frame, true);
+ if (redraw->x1 == 0 || redraw->y1 == 0) {
+ *redraw = *frame;
+ } else {
+ if (redraw->x0 > frame->x0) {
+ redraw->x0 = frame->x0;
+ }
+ if (redraw->x1 < frame->x1) {
+ redraw->x1 = frame->x1;
+ }
+ if (redraw->y0 > frame->y0) {
+ redraw->y0 = frame->y0;
+ }
+ if (redraw->y1 < frame->y1) {
+ redraw->y1 = frame->y1;
+ }
+ }
}
-/* exported function documented in libnsgif.h */
-void nsgif_finalise(nsgif *gif)
+static uint32_t nsgif__frame_next(
+ nsgif *gif,
+ bool partial,
+ uint32_t frame)
{
- /* Release all our memory blocks */
- if (gif->frame_image) {
- assert(gif->bitmap.destroy);
- gif->bitmap.destroy(gif->frame_image);
+ uint32_t frames = partial ?
+ gif->frame_count_partial :
+ gif->info.frame_count;
+
+ if (frames == 0) {
+ return NSGIF_FRAME_INVALID;
}
- gif->frame_image = NULL;
- free(gif->frames);
- gif->frames = NULL;
- free(gif->local_colour_table);
- gif->local_colour_table = NULL;
- free(gif->global_colour_table);
- gif->global_colour_table = NULL;
+ frame++;
+ return (frame >= frames) ? 0 : frame;
+}
- free(gif->prev_frame);
- gif->prev_frame = NULL;
+static nsgif_result nsgif__next_displayable_frame(
+ nsgif *gif,
+ uint32_t *frame,
+ uint32_t *delay)
+{
+ uint32_t next = *frame;
- lzw_context_destroy(gif->lzw_ctx);
- gif->lzw_ctx = NULL;
+ do {
+ next = nsgif__frame_next(gif, false, next);
+ if (next == *frame || next == NSGIF_FRAME_INVALID) {
+ return NSGIF_FRAME_NO_DISPLAY;
+ }
+
+ if (delay != NULL) {
+ *delay += gif->frames[next].frame_delay;
+ }
+
+ } while (gif->frames[next].display == false);
+
+ *frame = next;
+ return NSGIF_OK;
+}
+
+static inline bool nsgif__animation_complete(int count, int max)
+{
+ if (max == 0) {
+ return false;
+ }
+
+ return (count >= max);
+}
+
+nsgif_result nsgif_reset(
+ nsgif *gif)
+{
+ gif->info.loop_count = 0;
+ gif->frame = NSGIF_FRAME_INVALID;
+
+ return NSGIF_OK;
+}
+
+/* exported function documented in nsgif.h */
+nsgif_result nsgif_frame_prepare(
+ nsgif *gif,
+ nsgif_rect *area,
+ uint32_t *delay_cs,
+ uint32_t *frame_new)
+{
+ nsgif_result ret;
+ nsgif_rect rect = {
+ .x1 = 0,
+ .y1 = 0,
+ };
+ uint32_t delay = 0;
+ uint32_t frame = gif->frame;
+ uint32_t frame_next;
+
+ if (gif->frame != NSGIF_FRAME_INVALID &&
+ gif->frame != 0 &&
+ gif->frame < gif->info.frame_count &&
+ gif->frames[gif->frame].display) {
+ rect = gif->frames[gif->frame].redraw;
+ }
+
+ if (nsgif__animation_complete(
+ gif->info.loop_count,
+ gif->info.loop_max)) {
+ return NSGIF_ANIMATION_COMPLETE;
+ }
+
+ ret = nsgif__next_displayable_frame(gif, &frame, NULL);
+ if (ret != NSGIF_OK) {
+ return ret;
+ }
+
+ if (gif->frame != NSGIF_FRAME_INVALID && frame < gif->frame) {
+ gif->info.loop_count++;
+ }
+
+ frame_next = frame;
+ ret = nsgif__next_displayable_frame(gif, &frame_next, &delay);
+ if (ret != NSGIF_OK) {
+ return ret;
+ }
+
+ if (frame_next < frame) {
+ if (nsgif__animation_complete(
+ gif->info.loop_count + 1,
+ gif->info.loop_max)) {
+ delay = NSGIF_INFINITE;
+ }
+ }
+
+ gif->frame = frame;
+ nsgif__redraw_rect_extend(&gif->frames[frame].redraw, &rect);
+
+ *frame_new = gif->frame;
+ *delay_cs = delay;
+ *area = rect;
+
+ return NSGIF_OK;
+}
+
+/* exported function documented in nsgif.h */
+nsgif_result nsgif_frame_decode(
+ nsgif *gif,
+ uint32_t frame,
+ const uint32_t **buffer)
+{
+ uint32_t start_frame;
+ nsgif_result ret = NSGIF_OK;
+
+ if (gif->decoded_frame == frame) {
+ *buffer = gif->frame_image;
+ return NSGIF_OK;
+
+ } else if (gif->decoded_frame >= frame ||
+ gif->decoded_frame == NSGIF_FRAME_INVALID) {
+ /* Can skip to first frame or restart. */
+ start_frame = 0;
+ } else {
+ start_frame = nsgif__frame_next(
+ gif, false, gif->decoded_frame);
+ }
+
+ for (uint32_t f = start_frame; f <= frame; f++) {
+ ret = nsgif__process_frame(gif, f, true);
+ if (ret != NSGIF_OK) {
+ return ret;
+ }
+ }
+
+ *buffer = gif->frame_image;
+ return ret;
+}
+
+const nsgif_info_t *nsgif_get_info(const nsgif *gif)
+{
+ return &gif->info;
}
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=b5dfaef72b2e272581...
commit b5dfaef72b2e2725810320cd2cf703463b3e95e1
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
test: Add commented out echo line to test runner.
diff --git a/test/runtest.sh b/test/runtest.sh
index 06eaef4..05665a1 100755
--- a/test/runtest.sh
+++ b/test/runtest.sh
@@ -50,6 +50,7 @@ echo "Testing GIF decode"
for GIF in $(ls ${GIFTESTS});do
GIFTESTTOTC=$((GIFTESTTOTC+1))
+ #echo "${GIF}"
gifdecode ${GIF}
ECODE=$?
if [ "${ECODE}" -gt 127 ];then
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=aec9ee665b18e5929d...
commit aec9ee665b18e5929d9af192e13efb5105c52254
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
API: Rename nsgif_animation to nsgif.
diff --git a/include/nsgif.h b/include/nsgif.h
index 8e84519..5e69b7e 100644
--- a/include/nsgif.h
+++ b/include/nsgif.h
@@ -102,7 +102,7 @@ typedef struct nsgif_bitmap_cb_vt {
} nsgif_bitmap_cb_vt;
/** GIF animation data */
-typedef struct nsgif_animation {
+typedef struct nsgif {
/** LZW decode context */
void *lzw_ctx;
/** callbacks for bitmap functions */
@@ -159,12 +159,12 @@ typedef struct nsgif_animation {
unsigned prev_width;
/** previous frame height */
unsigned prev_height;
-} nsgif_animation;
+} nsgif;
/**
- * Initialises necessary nsgif_animation members.
+ * Initialises necessary nsgif members.
*/
-void nsgif_create(nsgif_animation *gif, nsgif_bitmap_cb_vt *bitmap_callbacks);
+void nsgif_create(nsgif *gif, nsgif_bitmap_cb_vt *bitmap_callbacks);
/**
* Initialises any workspace held by the animation and attempts to decode
@@ -179,7 +179,7 @@ void nsgif_create(nsgif_animation *gif, nsgif_bitmap_cb_vt *bitmap_callbacks);
* - NSGIF_OK for successful decoding
* - NSGIF_WORKING for successful decoding if more frames are expected
*/
-nsgif_result nsgif_initialise(nsgif_animation *gif, size_t size, const uint8_t *data);
+nsgif_result nsgif_initialise(nsgif *gif, size_t size, const uint8_t *data);
/**
* Decodes a GIF frame.
@@ -191,11 +191,11 @@ nsgif_result nsgif_initialise(nsgif_animation *gif, size_t size, const uint8_t *
* - NSGIF_INSUFFICIENT_MEMORY for insufficient memory to process
* - NSGIF_OK for successful decoding
*/
-nsgif_result nsgif_decode_frame(nsgif_animation *gif, uint32_t frame);
+nsgif_result nsgif_decode_frame(nsgif *gif, uint32_t frame);
/**
* Releases any workspace held by a gif
*/
-void nsgif_finalise(nsgif_animation *gif);
+void nsgif_finalise(nsgif *gif);
#endif
diff --git a/src/gif.c b/src/gif.c
index ec5c185..cb784a9 100644
--- a/src/gif.c
+++ b/src/gif.c
@@ -88,7 +88,7 @@ static nsgif_result nsgif__error_from_lzw(lzw_result l_res)
* \return NSGIF_INSUFFICIENT_MEMORY for a memory error NSGIF_OK for success
*/
static nsgif_result nsgif__initialise_sprite(
- struct nsgif_animation *gif,
+ struct nsgif *gif,
uint32_t width,
uint32_t height)
{
@@ -113,7 +113,7 @@ static nsgif_result nsgif__initialise_sprite(
* \return Client pixel buffer for rendering into.
*/
static inline uint32_t* nsgif__bitmap_get(
- struct nsgif_animation *gif)
+ struct nsgif *gif)
{
nsgif_result ret;
@@ -134,7 +134,7 @@ static inline uint32_t* nsgif__bitmap_get(
* \param[in] gif The gif object we're decoding.
*/
static inline void nsgif__bitmap_modified(
- const struct nsgif_animation *gif)
+ const struct nsgif *gif)
{
if (gif->bitmap.modified) {
gif->bitmap.modified(gif->frame_image);
@@ -148,7 +148,7 @@ static inline void nsgif__bitmap_modified(
* \param[in] frame The frame that has been decoded.
*/
static inline void nsgif__bitmap_set_opaque(
- const struct nsgif_animation *gif,
+ const struct nsgif *gif,
const struct nsgif_frame *frame)
{
if (gif->bitmap.set_opaque) {
@@ -166,7 +166,7 @@ static inline void nsgif__bitmap_set_opaque(
* \return true if the bitmap is opaque, false otherwise.
*/
static inline bool nsgif__bitmap_get_opaque(
- const struct nsgif_animation *gif)
+ const struct nsgif *gif)
{
if (gif->bitmap.test_opaque) {
return gif->bitmap.test_opaque(
@@ -177,7 +177,7 @@ static inline bool nsgif__bitmap_get_opaque(
}
static void nsgif__record_frame(
- struct nsgif_animation *gif,
+ struct nsgif *gif,
const uint32_t *bitmap)
{
bool need_alloc = gif->prev_frame == NULL;
@@ -218,7 +218,7 @@ static void nsgif__record_frame(
}
static nsgif_result nsgif__recover_frame(
- const struct nsgif_animation *gif,
+ const struct nsgif *gif,
uint32_t *bitmap)
{
const uint32_t *prev_frame = gif->prev_frame;
@@ -334,7 +334,7 @@ static inline void gif__jump_data(
}
static nsgif_result nsgif__decode_complex(
- struct nsgif_animation *gif,
+ struct nsgif *gif,
uint32_t width,
uint32_t height,
uint32_t offset_x,
@@ -433,7 +433,7 @@ static nsgif_result nsgif__decode_complex(
}
static nsgif_result nsgif__decode_simple(
- struct nsgif_animation *gif,
+ struct nsgif *gif,
uint32_t height,
uint32_t offset_y,
const uint8_t *data,
@@ -491,7 +491,7 @@ static nsgif_result nsgif__decode_simple(
}
static inline nsgif_result nsgif__decode(
- struct nsgif_animation *gif,
+ struct nsgif *gif,
struct nsgif_frame *frame,
const uint8_t *data,
uint32_t *restrict frame_data)
@@ -531,7 +531,7 @@ static inline nsgif_result nsgif__decode(
* \param[in] bitmap The bitmap to clear the frame in.
*/
static void nsgif__restore_bg(
- struct nsgif_animation *gif,
+ struct nsgif *gif,
struct nsgif_frame *frame,
uint32_t *bitmap)
{
@@ -571,7 +571,7 @@ static void nsgif__restore_bg(
}
static nsgif_result nsgif__update_bitmap(
- struct nsgif_animation *gif,
+ struct nsgif *gif,
struct nsgif_frame *frame,
const uint8_t *data,
uint32_t frame_idx)
@@ -694,7 +694,7 @@ static nsgif_result nsgif__parse_extension_graphic_control(
* NSGIF_OK for success.
*/
static nsgif_result nsgif__parse_extension_application(
- struct nsgif_animation *gif,
+ struct nsgif *gif,
const uint8_t *data,
size_t len)
{
@@ -730,7 +730,7 @@ static nsgif_result nsgif__parse_extension_application(
* NSGIF_OK for success.
*/
static nsgif_result nsgif__parse_frame_extensions(
- struct nsgif_animation *gif,
+ struct nsgif *gif,
struct nsgif_frame *frame,
const uint8_t **pos,
bool decode)
@@ -847,7 +847,7 @@ static nsgif_result nsgif__parse_frame_extensions(
* \return NSGIF_OK on success, appropriate error otherwise.
*/
static nsgif_result nsgif__parse_image_descriptor(
- struct nsgif_animation *gif,
+ struct nsgif *gif,
struct nsgif_frame *frame,
const uint8_t **pos,
bool decode)
@@ -910,7 +910,7 @@ static nsgif_result nsgif__parse_image_descriptor(
* \return NSGIF_OK on success, appropriate error otherwise.
*/
static nsgif_result nsgif__colour_table_extract(
- struct nsgif_animation *gif,
+ struct nsgif *gif,
uint32_t *colour_table,
size_t colour_table_entries,
const uint8_t **pos,
@@ -959,7 +959,7 @@ static nsgif_result nsgif__colour_table_extract(
* \return NSGIF_OK on success, appropriate error otherwise.
*/
static nsgif_result nsgif__parse_colour_table(
- struct nsgif_animation *gif,
+ struct nsgif *gif,
struct nsgif_frame *frame,
const uint8_t **pos,
bool decode)
@@ -997,7 +997,7 @@ static nsgif_result nsgif__parse_colour_table(
* \return NSGIF_OK on success, appropriate error otherwise.
*/
static nsgif_result nsgif__parse_image_data(
- struct nsgif_animation *gif,
+ struct nsgif *gif,
struct nsgif_frame *frame,
const uint8_t **pos,
bool decode)
@@ -1075,7 +1075,7 @@ static nsgif_result nsgif__parse_image_data(
}
static struct nsgif_frame *nsgif__get_frame(
- struct nsgif_animation *gif,
+ struct nsgif *gif,
uint32_t frame_idx)
{
struct nsgif_frame *frame;
@@ -1124,7 +1124,7 @@ static struct nsgif_frame *nsgif__get_frame(
* - NSGIF_WORKING for successful decoding if more frames are expected
*/
static nsgif_result nsgif__process_frame(
- struct nsgif_animation *gif,
+ struct nsgif *gif,
uint32_t frame_idx,
bool decode)
{
@@ -1202,9 +1202,9 @@ cleanup:
}
/* exported function documented in libnsgif.h */
-void nsgif_create(nsgif_animation *gif, nsgif_bitmap_cb_vt *bitmap)
+void nsgif_create(nsgif *gif, nsgif_bitmap_cb_vt *bitmap)
{
- memset(gif, 0, sizeof(nsgif_animation));
+ memset(gif, 0, sizeof(nsgif));
gif->bitmap = *bitmap;
gif->decoded_frame = NSGIF_INVALID_FRAME;
gif->prev_index = NSGIF_INVALID_FRAME;
@@ -1224,7 +1224,7 @@ void nsgif_create(nsgif_animation *gif, nsgif_bitmap_cb_vt *bitmap)
* \return NSGIF_OK on success, appropriate error otherwise.
*/
static nsgif_result nsgif__parse_header(
- struct nsgif_animation *gif,
+ struct nsgif *gif,
const uint8_t **pos,
bool strict)
{
@@ -1272,7 +1272,7 @@ static nsgif_result nsgif__parse_header(
* \return NSGIF_OK on success, appropriate error otherwise.
*/
static nsgif_result nsgif__parse_logical_screen_descriptor(
- struct nsgif_animation *gif,
+ struct nsgif *gif,
const uint8_t **pos)
{
const uint8_t *data = *pos;
@@ -1295,7 +1295,7 @@ static nsgif_result nsgif__parse_logical_screen_descriptor(
}
/* exported function documented in libnsgif.h */
-nsgif_result nsgif_initialise(nsgif_animation *gif, size_t size, const uint8_t *data)
+nsgif_result nsgif_initialise(nsgif *gif, size_t size, const uint8_t *data)
{
const uint8_t *nsgif_data;
nsgif_result ret;
@@ -1437,13 +1437,13 @@ nsgif_result nsgif_initialise(nsgif_animation *gif, size_t size, const uint8_t *
}
/* exported function documented in libnsgif.h */
-nsgif_result nsgif_decode_frame(nsgif_animation *gif, uint32_t frame)
+nsgif_result nsgif_decode_frame(nsgif *gif, uint32_t frame)
{
return nsgif__process_frame(gif, frame, true);
}
/* exported function documented in libnsgif.h */
-void nsgif_finalise(nsgif_animation *gif)
+void nsgif_finalise(nsgif *gif)
{
/* Release all our memory blocks */
if (gif->frame_image) {
diff --git a/test/decode_gif.c b/test/decode_gif.c
index 20cb571..a20d5f4 100644
--- a/test/decode_gif.c
+++ b/test/decode_gif.c
@@ -125,7 +125,7 @@ static void warning(const char *context, nsgif_result code)
fprintf(stderr, "\n");
}
-static void write_ppm(FILE* fh, const char *name, nsgif_animation *gif,
+static void write_ppm(FILE* fh, const char *name, nsgif *gif,
bool no_write)
{
unsigned int i;
@@ -182,7 +182,7 @@ int main(int argc, char *argv[])
bitmap_test_opaque,
bitmap_modified
};
- nsgif_animation gif;
+ nsgif gif;
size_t size;
nsgif_result code;
unsigned char *data;
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=fedd37d9ce70571f30...
commit fedd37d9ce70571f305c8c8e66fd9ec7d837585b
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
API: Split out redraw rectangle.
diff --git a/include/nsgif.h b/include/nsgif.h
index 114addf..8e84519 100644
--- a/include/nsgif.h
+++ b/include/nsgif.h
@@ -32,6 +32,18 @@ typedef enum {
NSGIF_END_OF_FRAME = -7
} nsgif_result;
+/** GIF rectangle structure. */
+typedef struct nsgif_rect {
+ /** x co-ordinate of redraw rectangle */
+ uint32_t x;
+ /** y co-ordinate of redraw rectangle */
+ uint32_t y;
+ /** width of redraw rectangle */
+ uint32_t w;
+ /** height of redraw rectangle */
+ uint32_t h;
+} nsgif_rect;
+
/** GIF frame data */
typedef struct nsgif_frame {
/** whether the frame should be displayed/animated */
@@ -55,16 +67,11 @@ typedef struct nsgif_frame {
bool transparency;
/** the index designating a transparent pixel */
uint32_t transparency_index;
- /** x co-ordinate of redraw rectangle */
- uint32_t redraw_x;
- /** y co-ordinate of redraw rectangle */
- uint32_t redraw_y;
- /** width of redraw rectangle */
- uint32_t redraw_width;
- /** height of redraw rectangle */
- uint32_t redraw_height;
/* Frame flags */
uint32_t flags;
+
+ /** Frame's redraw rectangle. */
+ nsgif_rect redraw;
} nsgif_frame;
/* API for Bitmap callbacks */
diff --git a/src/gif.c b/src/gif.c
index e9fa83e..ec5c185 100644
--- a/src/gif.c
+++ b/src/gif.c
@@ -501,10 +501,10 @@ static inline nsgif_result nsgif__decode(
};
nsgif_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 width = frame->redraw.w;
+ uint32_t height = frame->redraw.h;
+ uint32_t offset_x = frame->redraw.x;
+ uint32_t offset_y = frame->redraw.y;
uint32_t interlace = frame->flags & GIF_MASK_INTERLACE;
uint32_t transparency_index = frame->transparency_index;
uint32_t *restrict colour_table = gif->colour_table;
@@ -539,10 +539,10 @@ static void nsgif__restore_bg(
memset(bitmap, NSGIF_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;
+ uint32_t width = frame->redraw.w;
+ uint32_t height = frame->redraw.h;
+ uint32_t offset_x = frame->redraw.x;
+ uint32_t offset_y = frame->redraw.y;
width -= gif__clip(offset_x, width, gif->width);
height -= gif__clip(offset_y, height, gif->height);
@@ -879,10 +879,10 @@ static nsgif_result nsgif__parse_image_descriptor(
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->redraw.x = x;
+ frame->redraw.y = y;
+ frame->redraw.w = w;
+ frame->redraw.h = h;
/* Allow first frame to grow image dimensions. */
if (gif->frame_count == 0) {
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=87c9a0ce41757c9c0f...
commit 87c9a0ce41757c9c0ff2f3f19054edfd8296ef31
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Remove redundant comment.
diff --git a/src/gif.c b/src/gif.c
index cfd5278..e9fa83e 100644
--- a/src/gif.c
+++ b/src/gif.c
@@ -1173,7 +1173,6 @@ static nsgif_result nsgif__process_frame(
}
}
- /* Initialise any extensions */
ret = nsgif__parse_frame_extensions(gif, frame, &pos, !decode);
if (ret != NSGIF_OK) {
goto cleanup;
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=2013c162af618e701b...
commit 2013c162af618e701ba4caf84fa304ac38c92501
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Scope reduce frame interlace mask.
diff --git a/src/gif.c b/src/gif.c
index 74fe4f4..cfd5278 100644
--- a/src/gif.c
+++ b/src/gif.c
@@ -52,7 +52,6 @@ enum nsgif_disposal {
};
/* GIF Flags */
-#define NSGIF_INTERLACE_MASK 0x40
#define NSGIF_COLOUR_TABLE_MASK 0x80
#define NSGIF_COLOUR_TABLE_SIZE_MASK 0x07
#define NSGIF_BLOCK_TERMINATOR 0x00
@@ -497,12 +496,16 @@ static inline nsgif_result nsgif__decode(
const uint8_t *data,
uint32_t *restrict frame_data)
{
+ enum {
+ GIF_MASK_INTERLACE = 0x40,
+ };
+
nsgif_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 & NSGIF_INTERLACE_MASK;
+ uint32_t interlace = frame->flags & GIF_MASK_INTERLACE;
uint32_t transparency_index = frame->transparency_index;
uint32_t *restrict colour_table = gif->colour_table;
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=958ea3a13e8d5ffd23...
commit 958ea3a13e8d5ffd23f2540d7a0eb2926667a206
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Scope reduce graphic control extension masks.
diff --git a/src/gif.c b/src/gif.c
index 28a5b2d..74fe4f4 100644
--- a/src/gif.c
+++ b/src/gif.c
@@ -55,8 +55,6 @@ enum nsgif_disposal {
#define NSGIF_INTERLACE_MASK 0x40
#define NSGIF_COLOUR_TABLE_MASK 0x80
#define NSGIF_COLOUR_TABLE_SIZE_MASK 0x07
-#define NSGIF_DISPOSAL_MASK 0x1c
-#define NSGIF_TRANSPARENCY_MASK 0x01
#define NSGIF_BLOCK_TERMINATOR 0x00
#define NSGIF_TRAILER 0x3b
@@ -636,6 +634,11 @@ static nsgif_result nsgif__parse_extension_graphic_control(
const uint8_t *data,
size_t len)
{
+ enum {
+ GIF_MASK_TRANSPARENCY = 0x01,
+ GIF_MASK_DISPOSAL = 0x1c,
+ };
+
/* 6-byte Graphic Control Extension is:
*
* +0 CHAR Graphic Control Label
@@ -653,12 +656,12 @@ static nsgif_result nsgif__parse_extension_graphic_control(
}
frame->frame_delay = data[3] | (data[4] << 8);
- if (data[2] & NSGIF_TRANSPARENCY_MASK) {
+ if (data[2] & GIF_MASK_TRANSPARENCY) {
frame->transparency = true;
frame->transparency_index = data[5];
}
- frame->disposal_method = ((data[2] & NSGIF_DISPOSAL_MASK) >> 2);
+ frame->disposal_method = ((data[2] & GIF_MASK_DISPOSAL) >> 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
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=1e3f649e2b78bf936a...
commit 1e3f649e2b78bf936ac3ca83c0992b0cb9add327
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Scope reduce gif extension identifier magic values.
diff --git a/src/gif.c b/src/gif.c
index 28d7862..28a5b2d 100644
--- a/src/gif.c
+++ b/src/gif.c
@@ -55,13 +55,8 @@ enum nsgif_disposal {
#define NSGIF_INTERLACE_MASK 0x40
#define NSGIF_COLOUR_TABLE_MASK 0x80
#define NSGIF_COLOUR_TABLE_SIZE_MASK 0x07
-#define NSGIF_EXTENSION_INTRODUCER 0x21
-#define NSGIF_EXTENSION_GRAPHIC_CONTROL 0xf9
#define NSGIF_DISPOSAL_MASK 0x1c
#define NSGIF_TRANSPARENCY_MASK 0x01
-#define NSGIF_EXTENSION_COMMENT 0xfe
-#define NSGIF_EXTENSION_PLAIN_TEXT 0x01
-#define NSGIF_EXTENSION_APPLICATION 0xff
#define NSGIF_BLOCK_TERMINATOR 0x00
#define NSGIF_TRAILER 0x3b
@@ -734,12 +729,19 @@ static nsgif_result nsgif__parse_frame_extensions(
const uint8_t **pos,
bool decode)
{
+ enum {
+ GIF_EXT_INTRODUCER = 0x21,
+ GIF_EXT_GRAPHIC_CONTROL = 0xf9,
+ GIF_EXT_COMMENT = 0xfe,
+ GIF_EXT_PLAIN_TEXT = 0x01,
+ GIF_EXT_APPLICATION = 0xff,
+ };
const uint8_t *nsgif_data = *pos;
const uint8_t *nsgif_end = gif->nsgif_data + gif->buffer_size;
int nsgif_bytes = nsgif_end - nsgif_data;
/* Initialise the extensions */
- while (nsgif_bytes > 0 && nsgif_data[0] == NSGIF_EXTENSION_INTRODUCER) {
+ while (nsgif_bytes > 0 && nsgif_data[0] == GIF_EXT_INTRODUCER) {
bool block_step = true;
nsgif_result ret;
@@ -752,7 +754,7 @@ static nsgif_result nsgif__parse_frame_extensions(
/* Switch on extension label */
switch (nsgif_data[0]) {
- case NSGIF_EXTENSION_GRAPHIC_CONTROL:
+ case GIF_EXT_GRAPHIC_CONTROL:
if (decode) {
ret = nsgif__parse_extension_graphic_control(
frame, nsgif_data, nsgif_bytes);
@@ -762,7 +764,7 @@ static nsgif_result nsgif__parse_frame_extensions(
}
break;
- case NSGIF_EXTENSION_APPLICATION:
+ case GIF_EXT_APPLICATION:
if (decode) {
ret = nsgif__parse_extension_application(
gif, nsgif_data, nsgif_bytes);
@@ -772,7 +774,7 @@ static nsgif_result nsgif__parse_frame_extensions(
}
break;
- case NSGIF_EXTENSION_COMMENT:
+ case GIF_EXT_COMMENT:
/* Move the pointer to the first data sub-block Skip 1
* byte for the extension label. */
++nsgif_data;
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=4a384cb88b02fb086e...
commit 4a384cb88b02fb086e1035971dd142115cf9b377
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
LIB: Rename public header and source file.
This allows the library header to have a name consistent with
the library soname.
diff --git a/Makefile b/Makefile
index 2ae2406..42aba3a 100644
--- a/Makefile
+++ b/Makefile
@@ -44,6 +44,6 @@ include $(NSBUILD)/Makefile.top
# Extra installation rules
I := /$(INCLUDEDIR)
-INSTALL_ITEMS := $(INSTALL_ITEMS) $(I):include/libnsgif.h
+INSTALL_ITEMS := $(INSTALL_ITEMS) $(I):include/nsgif.h
INSTALL_ITEMS := $(INSTALL_ITEMS) /$(LIBDIR)/pkgconfig:lib$(COMPONENT).pc.in
INSTALL_ITEMS := $(INSTALL_ITEMS) /$(LIBDIR):$(OUTPUT)
diff --git a/include/libnsgif.h b/include/libnsgif.h
deleted file mode 100644
index 114addf..0000000
--- a/include/libnsgif.h
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * 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,
- * http://www.opensource.org/licenses/mit-license.php
- */
-
-/**
- * \file
- * Interface to progressive animated GIF file decoding.
- */
-
-#ifndef NSNSGIF_H
-#define NSNSGIF_H
-
-#include <stdint.h>
-#include <stdbool.h>
-
-/* Error return values */
-typedef enum {
- NSGIF_WORKING = 1,
- NSGIF_OK = 0,
- NSGIF_INSUFFICIENT_DATA = -1,
- NSGIF_INSUFFICIENT_FRAME_DATA = NSGIF_INSUFFICIENT_DATA,
- NSGIF_FRAME_DATA_ERROR = -2,
- NSGIF_DATA_ERROR = -4,
- NSGIF_INSUFFICIENT_MEMORY = -5,
- NSGIF_FRAME_NO_DISPLAY = -6,
- NSGIF_END_OF_FRAME = -7
-} nsgif_result;
-
-/** GIF frame data */
-typedef struct nsgif_frame {
- /** whether the frame should be displayed/animated */
- bool display;
- /** delay (in cs) before animating the frame */
- uint32_t frame_delay;
-
- /* Internal members are listed below */
-
- /** offset (in bytes) to the GIF frame data */
- uint32_t frame_pointer;
- /** whether the frame has previously been decoded. */
- bool decoded;
- /** 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 */
- uint8_t disposal_method;
- /** whether we acknowledge transparency */
- bool transparency;
- /** the index designating a transparent pixel */
- uint32_t transparency_index;
- /** x co-ordinate of redraw rectangle */
- uint32_t redraw_x;
- /** y co-ordinate of redraw rectangle */
- uint32_t redraw_y;
- /** width of redraw rectangle */
- uint32_t redraw_width;
- /** height of redraw rectangle */
- uint32_t redraw_height;
- /* Frame flags */
- uint32_t flags;
-} nsgif_frame;
-
-/* API for Bitmap callbacks */
-typedef void* (*nsgif_bitmap_cb_create)(int width, int height);
-typedef void (*nsgif_bitmap_cb_destroy)(void *bitmap);
-typedef uint8_t* (*nsgif_bitmap_cb_get_buffer)(void *bitmap);
-typedef void (*nsgif_bitmap_cb_set_opaque)(void *bitmap, bool opaque);
-typedef bool (*nsgif_bitmap_cb_test_opaque)(void *bitmap);
-typedef void (*nsgif_bitmap_cb_modified)(void *bitmap);
-
-/** Bitmap callbacks function table */
-typedef struct nsgif_bitmap_cb_vt {
- /** Create a bitmap. */
- nsgif_bitmap_cb_create create;
- /** Free a bitmap. */
- nsgif_bitmap_cb_destroy destroy;
- /** Return a pointer to the pixel data in a bitmap. */
- nsgif_bitmap_cb_get_buffer get_buffer;
-
- /* Members below are optional */
-
- /** Sets whether a bitmap should be plotted opaque. */
- nsgif_bitmap_cb_set_opaque set_opaque;
- /** Tests whether a bitmap has an opaque alpha channel. */
- nsgif_bitmap_cb_test_opaque test_opaque;
- /** The bitmap image has changed, so flush any persistent cache. */
- nsgif_bitmap_cb_modified modified;
-} nsgif_bitmap_cb_vt;
-
-/** GIF animation data */
-typedef struct nsgif_animation {
- /** LZW decode context */
- void *lzw_ctx;
- /** callbacks for bitmap functions */
- nsgif_bitmap_cb_vt bitmap;
- /** pointer to GIF data */
- const uint8_t *nsgif_data;
- /** width of GIF (may increase during decoding) */
- uint32_t width;
- /** height of GIF (may increase during decoding) */
- uint32_t height;
- /** number of frames decoded */
- uint32_t frame_count;
- /** number of frames partially decoded */
- uint32_t frame_count_partial;
- /** decoded frames */
- nsgif_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 */
- uint32_t buffer_position;
- /** total number of bytes of GIF data available */
- uint32_t buffer_size;
- /** current number of frame holders */
- uint32_t frame_holders;
- /** background index */
- uint32_t bg_index;
- /** background colour */
- uint32_t bg_colour;
- /** image aspect ratio (ignored) */
- uint32_t aspect_ratio;
- /** size of colour table (in entries) */
- uint32_t colour_table_size;
- /** whether the GIF has a global colour table */
- bool global_colours;
- /** global colour table */
- uint32_t *global_colour_table;
- /** local colour table */
- uint32_t *local_colour_table;
- /** current colour table */
- uint32_t *colour_table;
-
- /** previous frame for NSGIF_FRAME_RESTORE */
- void *prev_frame;
- /** previous frame index */
- int prev_index;
- /** previous frame width */
- unsigned prev_width;
- /** previous frame height */
- unsigned prev_height;
-} nsgif_animation;
-
-/**
- * Initialises necessary nsgif_animation members.
- */
-void nsgif_create(nsgif_animation *gif, nsgif_bitmap_cb_vt *bitmap_callbacks);
-
-/**
- * Initialises any workspace held by the animation and attempts to decode
- * any information that hasn't already been decoded.
- * If an error occurs, all previously decoded frames are retained.
- *
- * \return Error return value.
- * - NSGIF_FRAME_DATA_ERROR for GIF frame data error
- * - NSGIF_INSUFFICIENT_DATA reached unexpected end of source data
- * - NSGIF_INSUFFICIENT_MEMORY for memory error
- * - NSGIF_DATA_ERROR for GIF error
- * - NSGIF_OK for successful decoding
- * - NSGIF_WORKING for successful decoding if more frames are expected
- */
-nsgif_result nsgif_initialise(nsgif_animation *gif, size_t size, const uint8_t *data);
-
-/**
- * Decodes a GIF frame.
- *
- * \return Error return value.
- * - NSGIF_FRAME_DATA_ERROR for GIF frame data error
- * - NSGIF_DATA_ERROR for GIF error (invalid frame header)
- * - NSGIF_INSUFFICIENT_DATA reached unexpected end of source data
- * - NSGIF_INSUFFICIENT_MEMORY for insufficient memory to process
- * - NSGIF_OK for successful decoding
- */
-nsgif_result nsgif_decode_frame(nsgif_animation *gif, uint32_t frame);
-
-/**
- * Releases any workspace held by a gif
- */
-void nsgif_finalise(nsgif_animation *gif);
-
-#endif
diff --git a/include/nsgif.h b/include/nsgif.h
new file mode 100644
index 0000000..114addf
--- /dev/null
+++ b/include/nsgif.h
@@ -0,0 +1,194 @@
+/*
+ * 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,
+ * http://www.opensource.org/licenses/mit-license.php
+ */
+
+/**
+ * \file
+ * Interface to progressive animated GIF file decoding.
+ */
+
+#ifndef NSNSGIF_H
+#define NSNSGIF_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/* Error return values */
+typedef enum {
+ NSGIF_WORKING = 1,
+ NSGIF_OK = 0,
+ NSGIF_INSUFFICIENT_DATA = -1,
+ NSGIF_INSUFFICIENT_FRAME_DATA = NSGIF_INSUFFICIENT_DATA,
+ NSGIF_FRAME_DATA_ERROR = -2,
+ NSGIF_DATA_ERROR = -4,
+ NSGIF_INSUFFICIENT_MEMORY = -5,
+ NSGIF_FRAME_NO_DISPLAY = -6,
+ NSGIF_END_OF_FRAME = -7
+} nsgif_result;
+
+/** GIF frame data */
+typedef struct nsgif_frame {
+ /** whether the frame should be displayed/animated */
+ bool display;
+ /** delay (in cs) before animating the frame */
+ uint32_t frame_delay;
+
+ /* Internal members are listed below */
+
+ /** offset (in bytes) to the GIF frame data */
+ uint32_t frame_pointer;
+ /** whether the frame has previously been decoded. */
+ bool decoded;
+ /** 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 */
+ uint8_t disposal_method;
+ /** whether we acknowledge transparency */
+ bool transparency;
+ /** the index designating a transparent pixel */
+ uint32_t transparency_index;
+ /** x co-ordinate of redraw rectangle */
+ uint32_t redraw_x;
+ /** y co-ordinate of redraw rectangle */
+ uint32_t redraw_y;
+ /** width of redraw rectangle */
+ uint32_t redraw_width;
+ /** height of redraw rectangle */
+ uint32_t redraw_height;
+ /* Frame flags */
+ uint32_t flags;
+} nsgif_frame;
+
+/* API for Bitmap callbacks */
+typedef void* (*nsgif_bitmap_cb_create)(int width, int height);
+typedef void (*nsgif_bitmap_cb_destroy)(void *bitmap);
+typedef uint8_t* (*nsgif_bitmap_cb_get_buffer)(void *bitmap);
+typedef void (*nsgif_bitmap_cb_set_opaque)(void *bitmap, bool opaque);
+typedef bool (*nsgif_bitmap_cb_test_opaque)(void *bitmap);
+typedef void (*nsgif_bitmap_cb_modified)(void *bitmap);
+
+/** Bitmap callbacks function table */
+typedef struct nsgif_bitmap_cb_vt {
+ /** Create a bitmap. */
+ nsgif_bitmap_cb_create create;
+ /** Free a bitmap. */
+ nsgif_bitmap_cb_destroy destroy;
+ /** Return a pointer to the pixel data in a bitmap. */
+ nsgif_bitmap_cb_get_buffer get_buffer;
+
+ /* Members below are optional */
+
+ /** Sets whether a bitmap should be plotted opaque. */
+ nsgif_bitmap_cb_set_opaque set_opaque;
+ /** Tests whether a bitmap has an opaque alpha channel. */
+ nsgif_bitmap_cb_test_opaque test_opaque;
+ /** The bitmap image has changed, so flush any persistent cache. */
+ nsgif_bitmap_cb_modified modified;
+} nsgif_bitmap_cb_vt;
+
+/** GIF animation data */
+typedef struct nsgif_animation {
+ /** LZW decode context */
+ void *lzw_ctx;
+ /** callbacks for bitmap functions */
+ nsgif_bitmap_cb_vt bitmap;
+ /** pointer to GIF data */
+ const uint8_t *nsgif_data;
+ /** width of GIF (may increase during decoding) */
+ uint32_t width;
+ /** height of GIF (may increase during decoding) */
+ uint32_t height;
+ /** number of frames decoded */
+ uint32_t frame_count;
+ /** number of frames partially decoded */
+ uint32_t frame_count_partial;
+ /** decoded frames */
+ nsgif_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 */
+ uint32_t buffer_position;
+ /** total number of bytes of GIF data available */
+ uint32_t buffer_size;
+ /** current number of frame holders */
+ uint32_t frame_holders;
+ /** background index */
+ uint32_t bg_index;
+ /** background colour */
+ uint32_t bg_colour;
+ /** image aspect ratio (ignored) */
+ uint32_t aspect_ratio;
+ /** size of colour table (in entries) */
+ uint32_t colour_table_size;
+ /** whether the GIF has a global colour table */
+ bool global_colours;
+ /** global colour table */
+ uint32_t *global_colour_table;
+ /** local colour table */
+ uint32_t *local_colour_table;
+ /** current colour table */
+ uint32_t *colour_table;
+
+ /** previous frame for NSGIF_FRAME_RESTORE */
+ void *prev_frame;
+ /** previous frame index */
+ int prev_index;
+ /** previous frame width */
+ unsigned prev_width;
+ /** previous frame height */
+ unsigned prev_height;
+} nsgif_animation;
+
+/**
+ * Initialises necessary nsgif_animation members.
+ */
+void nsgif_create(nsgif_animation *gif, nsgif_bitmap_cb_vt *bitmap_callbacks);
+
+/**
+ * Initialises any workspace held by the animation and attempts to decode
+ * any information that hasn't already been decoded.
+ * If an error occurs, all previously decoded frames are retained.
+ *
+ * \return Error return value.
+ * - NSGIF_FRAME_DATA_ERROR for GIF frame data error
+ * - NSGIF_INSUFFICIENT_DATA reached unexpected end of source data
+ * - NSGIF_INSUFFICIENT_MEMORY for memory error
+ * - NSGIF_DATA_ERROR for GIF error
+ * - NSGIF_OK for successful decoding
+ * - NSGIF_WORKING for successful decoding if more frames are expected
+ */
+nsgif_result nsgif_initialise(nsgif_animation *gif, size_t size, const uint8_t *data);
+
+/**
+ * Decodes a GIF frame.
+ *
+ * \return Error return value.
+ * - NSGIF_FRAME_DATA_ERROR for GIF frame data error
+ * - NSGIF_DATA_ERROR for GIF error (invalid frame header)
+ * - NSGIF_INSUFFICIENT_DATA reached unexpected end of source data
+ * - NSGIF_INSUFFICIENT_MEMORY for insufficient memory to process
+ * - NSGIF_OK for successful decoding
+ */
+nsgif_result nsgif_decode_frame(nsgif_animation *gif, uint32_t frame);
+
+/**
+ * Releases any workspace held by a gif
+ */
+void nsgif_finalise(nsgif_animation *gif);
+
+#endif
diff --git a/src/Makefile b/src/Makefile
index cb5d31f..e1e1fa7 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,4 +1,4 @@
# Sources
-DIR_SOURCES := libnsgif.c lzw.c
+DIR_SOURCES := gif.c lzw.c
include $(NSBUILD)/Makefile.subdir
diff --git a/src/gif.c b/src/gif.c
new file mode 100644
index 0000000..28d7862
--- /dev/null
+++ b/src/gif.c
@@ -0,0 +1,1460 @@
+/*
+ * 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,
+ * http://www.opensource.org/licenses/mit-license.php
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "lzw.h"
+#include "nsgif.h"
+
+/**
+ *
+ * \file
+ * \brief GIF image decoder
+ *
+ * The GIF format is thoroughly documented; a full description can be found at
+ * http://www.w3.org/Graphics/GIF/spec-gif89a.txt
+ *
+ * \todo Plain text and comment extensions should be implemented.
+ */
+
+/** Maximum colour table size */
+#define NSGIF_MAX_COLOURS 256
+
+/** Internal flag that the colour table needs to be processed */
+#define NSGIF_PROCESS_COLOURS 0xaa000000
+
+/** Internal flag that a frame is invalid/unprocessed */
+#define NSGIF_INVALID_FRAME -1
+
+/** Transparent colour */
+#define NSGIF_TRANSPARENT_COLOUR 0x00
+
+/** No transparency */
+#define NSGIF_NO_TRANSPARENCY (0xFFFFFFFFu)
+
+enum nsgif_disposal {
+ NSGIF_DISPOSAL_UNSPECIFIED,
+ NSGIF_DISPOSAL_NONE,
+ NSGIF_DISPOSAL_RESTORE_BG,
+ NSGIF_DISPOSAL_RESTORE_PREV,
+ NSGIF_DISPOSAL_RESTORE_QUIRK, /**< Alias for NSGIF_DISPOSAL_RESTORE_PREV. */
+};
+
+/* GIF Flags */
+#define NSGIF_INTERLACE_MASK 0x40
+#define NSGIF_COLOUR_TABLE_MASK 0x80
+#define NSGIF_COLOUR_TABLE_SIZE_MASK 0x07
+#define NSGIF_EXTENSION_INTRODUCER 0x21
+#define NSGIF_EXTENSION_GRAPHIC_CONTROL 0xf9
+#define NSGIF_DISPOSAL_MASK 0x1c
+#define NSGIF_TRANSPARENCY_MASK 0x01
+#define NSGIF_EXTENSION_COMMENT 0xfe
+#define NSGIF_EXTENSION_PLAIN_TEXT 0x01
+#define NSGIF_EXTENSION_APPLICATION 0xff
+#define NSGIF_BLOCK_TERMINATOR 0x00
+#define NSGIF_TRAILER 0x3b
+
+/**
+ * Convert an LZW result code to equivalent GIF result code.
+ *
+ * \param[in] l_res LZW response code.
+ * \return GIF result code.
+ */
+static nsgif_result nsgif__error_from_lzw(lzw_result l_res)
+{
+ static const nsgif_result g_res[] = {
+ [LZW_OK] = NSGIF_OK,
+ [LZW_OK_EOD] = NSGIF_END_OF_FRAME,
+ [LZW_NO_MEM] = NSGIF_INSUFFICIENT_MEMORY,
+ [LZW_NO_DATA] = NSGIF_INSUFFICIENT_DATA,
+ [LZW_EOI_CODE] = NSGIF_FRAME_DATA_ERROR,
+ [LZW_BAD_ICODE] = NSGIF_FRAME_DATA_ERROR,
+ [LZW_BAD_CODE] = NSGIF_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
+ *
+ * \param gif The animation context
+ * \param width The width of the sprite
+ * \param height The height of the sprite
+ * \return NSGIF_INSUFFICIENT_MEMORY for a memory error NSGIF_OK for success
+ */
+static nsgif_result nsgif__initialise_sprite(
+ struct nsgif_animation *gif,
+ uint32_t width,
+ uint32_t height)
+{
+ /* Already allocated? */
+ if (gif->frame_image) {
+ return NSGIF_OK;
+ }
+
+ assert(gif->bitmap.create);
+ gif->frame_image = gif->bitmap.create(width, height);
+ if (gif->frame_image == NULL) {
+ return NSGIF_INSUFFICIENT_MEMORY;
+ }
+
+ return NSGIF_OK;
+}
+
+/**
+ * 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* nsgif__bitmap_get(
+ struct nsgif_animation *gif)
+{
+ nsgif_result ret;
+
+ /* Make sure we have a buffer to decode to. */
+ ret = nsgif__initialise_sprite(gif, gif->width, gif->height);
+ if (ret != NSGIF_OK) {
+ return NULL;
+ }
+
+ /* Get the frame data */
+ assert(gif->bitmap.get_buffer);
+ return (uint32_t *)gif->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 nsgif__bitmap_modified(
+ const struct nsgif_animation *gif)
+{
+ if (gif->bitmap.modified) {
+ gif->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] frame The frame that has been decoded.
+ */
+static inline void nsgif__bitmap_set_opaque(
+ const struct nsgif_animation *gif,
+ const struct nsgif_frame *frame)
+{
+ if (gif->bitmap.set_opaque) {
+ gif->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 nsgif__bitmap_get_opaque(
+ const struct nsgif_animation *gif)
+{
+ if (gif->bitmap.test_opaque) {
+ return gif->bitmap.test_opaque(
+ gif->frame_image);
+ }
+
+ return false;
+}
+
+static void nsgif__record_frame(
+ struct nsgif_animation *gif,
+ const uint32_t *bitmap)
+{
+ bool need_alloc = gif->prev_frame == NULL;
+ uint32_t *prev_frame;
+
+ if (gif->decoded_frame == NSGIF_INVALID_FRAME ||
+ gif->decoded_frame == gif->prev_index) {
+ /* No frame to copy, or already have this frame recorded. */
+ return;
+ }
+
+ bitmap = nsgif__bitmap_get(gif);
+ if (bitmap == NULL) {
+ return;
+ }
+
+ if (gif->prev_frame != NULL &&
+ gif->width * gif->height > gif->prev_width * gif->prev_height) {
+ need_alloc = true;
+ }
+
+ 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;
+ }
+
+ memcpy(prev_frame, bitmap, 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 nsgif_result nsgif__recover_frame(
+ const struct nsgif_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;
+
+ if (prev_frame == NULL) {
+ return NSGIF_FRAME_DATA_ERROR;
+ }
+
+ for (unsigned y = 0; y < height; y++) {
+ memcpy(bitmap, prev_frame, width * 4);
+
+ bitmap += gif->width;
+ prev_frame += gif->prev_width;
+ }
+
+ return NSGIF_OK;
+}
+
+/**
+ * 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 nsgif__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 nsgif__next_row(uint32_t interlace,
+ uint32_t height, uint32_t *y, uint8_t *step)
+{
+ if (!interlace) {
+ return (++*y != height);
+ } else {
+ return nsgif__deinterlace(height, y, step);
+ }
+}
+
+/**
+ * Get any frame clip adjustment for the image extent.
+ *
+ * \param[in] frame_off Frame's X or Y offset.
+ * \param[in] frame_dim Frame width or height.
+ * \param[in] image_ext Image width or height constraint.
+ * \return the amount the frame needs to be clipped to fit the image in given
+ * dimension.
+ */
+static inline uint32_t gif__clip(
+ uint32_t frame_off,
+ uint32_t frame_dim,
+ uint32_t image_ext)
+{
+ uint32_t frame_ext = frame_off + frame_dim;
+
+ if (frame_ext <= image_ext) {
+ return 0;
+ }
+
+ return frame_ext - image_ext;
+}
+
+/**
+ * Perform any jump over decoded data, to accommodate clipped portion of frame.
+ *
+ * \param[in,out] skip Number of pixels of data to jump.
+ * \param[in,out] available Number of pixels of data currently available.
+ * \param[in,out] pos Position in decoded pixel value data.
+ */
+static inline void gif__jump_data(
+ uint32_t *skip,
+ uint32_t *available,
+ const uint8_t **pos)
+{
+ uint32_t jump = (*skip < *available) ? *skip : *available;
+
+ *skip -= jump;
+ *available -= jump;
+ *pos += jump;
+}
+
+static nsgif_result nsgif__decode_complex(
+ struct nsgif_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)
+{
+ lzw_result res;
+ nsgif_result ret = NSGIF_OK;
+ uint32_t clip_x = gif__clip(offset_x, width, gif->width);
+ uint32_t clip_y = gif__clip(offset_y, height, gif->height);
+ const uint8_t *uncompressed;
+ uint32_t available = 0;
+ uint8_t step = 24;
+ uint32_t skip = 0;
+ uint32_t y = 0;
+
+ if (offset_x >= gif->width ||
+ offset_y >= gif->height) {
+ return NSGIF_OK;
+ }
+
+ width -= clip_x;
+ height -= clip_y;
+
+ if (width == 0 || height == 0) {
+ return NSGIF_OK;
+ }
+
+ /* Initialise the LZW decoding */
+ res = lzw_decode_init(gif->lzw_ctx, data[0],
+ gif->nsgif_data, gif->buffer_size,
+ data + 1 - gif->nsgif_data);
+ if (res != LZW_OK) {
+ return nsgif__error_from_lzw(res);
+ }
+
+ do {
+ uint32_t x;
+ uint32_t *frame_scanline;
+
+ frame_scanline = frame_data + offset_x +
+ (y + offset_y) * gif->width;
+
+ x = width;
+ while (x > 0) {
+ unsigned row_available;
+ while (available == 0) {
+ if (res != LZW_OK) {
+ /* Unexpected end of frame, try to recover */
+ if (res == LZW_OK_EOD) {
+ ret = NSGIF_OK;
+ } else {
+ ret = nsgif__error_from_lzw(res);
+ }
+ return ret;
+ }
+ res = lzw_decode(gif->lzw_ctx,
+ &uncompressed, &available);
+
+ if (available == 0) {
+ return NSGIF_OK;
+ }
+ gif__jump_data(&skip, &available, &uncompressed);
+ }
+
+ 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++;
+ }
+ }
+ }
+
+ skip = clip_x;
+ gif__jump_data(&skip, &available, &uncompressed);
+ } while (nsgif__next_row(interlace, height, &y, &step));
+
+ return ret;
+}
+
+static nsgif_result nsgif__decode_simple(
+ struct nsgif_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)
+{
+ uint32_t pixels = gif->width * height;
+ uint32_t written = 0;
+ nsgif_result ret = NSGIF_OK;
+ lzw_result res;
+
+ if (offset_y >= gif->height) {
+ return NSGIF_OK;
+ }
+
+ height -= gif__clip(offset_y, height, gif->height);
+
+ if (height == 0) {
+ return NSGIF_OK;
+ }
+
+ /* Initialise the LZW decoding */
+ res = lzw_decode_init_map(gif->lzw_ctx, data[0],
+ transparency_index, colour_table,
+ gif->nsgif_data, gif->buffer_size,
+ data + 1 - gif->nsgif_data);
+ if (res != LZW_OK) {
+ return nsgif__error_from_lzw(res);
+ }
+
+ 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 = NSGIF_OK;
+ } else {
+ ret = nsgif__error_from_lzw(res);
+ }
+ break;
+ }
+ }
+
+ if (pixels == 0) {
+ ret = NSGIF_OK;
+ }
+
+ return ret;
+}
+
+static inline nsgif_result nsgif__decode(
+ struct nsgif_animation *gif,
+ struct nsgif_frame *frame,
+ const uint8_t *data,
+ uint32_t *restrict frame_data)
+{
+ nsgif_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 & NSGIF_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 = nsgif__decode_simple(gif, height, offset_y,
+ data, transparency_index,
+ frame_data, colour_table);
+ } else {
+ ret = nsgif__decode_complex(gif, width, height,
+ offset_x, offset_y, interlace,
+ data, transparency_index,
+ frame_data, colour_table);
+ }
+
+ return ret;
+}
+
+/**
+ * Restore a GIF to the background colour.
+ *
+ * \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 nsgif__restore_bg(
+ struct nsgif_animation *gif,
+ struct nsgif_frame *frame,
+ uint32_t *bitmap)
+{
+ if (frame == NULL) {
+ memset(bitmap, NSGIF_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;
+
+ width -= gif__clip(offset_x, width, gif->width);
+ height -= gif__clip(offset_y, height, gif->height);
+
+ if (frame->display == false || width == 0) {
+ 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, NSGIF_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 nsgif_result nsgif__update_bitmap(
+ struct nsgif_animation *gif,
+ struct nsgif_frame *frame,
+ const uint8_t *data,
+ uint32_t frame_idx)
+{
+ nsgif_result ret;
+ uint32_t *bitmap;
+
+ gif->decoded_frame = frame_idx;
+
+ bitmap = nsgif__bitmap_get(gif);
+ if (bitmap == NULL) {
+ return NSGIF_INSUFFICIENT_MEMORY;
+ }
+
+ /* Handle any bitmap clearing/restoration required before decoding this
+ * frame. */
+ if (frame_idx == 0 || gif->decoded_frame == NSGIF_INVALID_FRAME) {
+ nsgif__restore_bg(gif, NULL, bitmap);
+
+ } else {
+ struct nsgif_frame *prev = &gif->frames[frame_idx - 1];
+
+ if (prev->disposal_method == NSGIF_DISPOSAL_RESTORE_BG) {
+ nsgif__restore_bg(gif, prev, bitmap);
+
+ } else if (prev->disposal_method == NSGIF_DISPOSAL_RESTORE_PREV) {
+ ret = nsgif__recover_frame(gif, bitmap);
+ if (ret != NSGIF_OK) {
+ nsgif__restore_bg(gif, prev, bitmap);
+ }
+ }
+ }
+
+ if (frame->disposal_method == NSGIF_DISPOSAL_RESTORE_PREV) {
+ /* Store the previous frame for later restoration */
+ nsgif__record_frame(gif, bitmap);
+ }
+
+ ret = nsgif__decode(gif, frame, data, bitmap);
+
+ nsgif__bitmap_modified(gif);
+
+ if (!frame->decoded) {
+ frame->opaque = nsgif__bitmap_get_opaque(gif);
+ frame->decoded = true;
+ }
+ nsgif__bitmap_set_opaque(gif, frame);
+
+ return ret;
+}
+
+/**
+ * Parse the graphic control 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 NSGIF_INSUFFICIENT_DATA if more data is needed,
+ * NSGIF_OK for success.
+ */
+static nsgif_result nsgif__parse_extension_graphic_control(
+ struct nsgif_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 (len < 6) {
+ return NSGIF_INSUFFICIENT_DATA;
+ }
+
+ frame->frame_delay = data[3] | (data[4] << 8);
+ if (data[2] & NSGIF_TRANSPARENCY_MASK) {
+ frame->transparency = true;
+ frame->transparency_index = data[5];
+ }
+
+ frame->disposal_method = ((data[2] & NSGIF_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 == NSGIF_DISPOSAL_RESTORE_QUIRK) {
+ frame->disposal_method = NSGIF_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 == NSGIF_DISPOSAL_RESTORE_BG ||
+ frame->disposal_method == NSGIF_DISPOSAL_RESTORE_PREV;
+
+ return NSGIF_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 NSGIF_INSUFFICIENT_DATA if more data is needed,
+ * NSGIF_OK for success.
+ */
+static nsgif_result nsgif__parse_extension_application(
+ struct nsgif_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)
+ */
+ if (len < 17) {
+ return NSGIF_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 NSGIF_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] pos Current position in data, updated on exit.
+ * \param[in] decode Whether to decode or skip over the extension.
+ * \return NSGIF_INSUFFICIENT_DATA if more data is needed,
+ * NSGIF_OK for success.
+ */
+static nsgif_result nsgif__parse_frame_extensions(
+ struct nsgif_animation *gif,
+ struct nsgif_frame *frame,
+ const uint8_t **pos,
+ bool decode)
+{
+ const uint8_t *nsgif_data = *pos;
+ const uint8_t *nsgif_end = gif->nsgif_data + gif->buffer_size;
+ int nsgif_bytes = nsgif_end - nsgif_data;
+
+ /* Initialise the extensions */
+ while (nsgif_bytes > 0 && nsgif_data[0] == NSGIF_EXTENSION_INTRODUCER) {
+ bool block_step = true;
+ nsgif_result ret;
+
+ nsgif_data++;
+ nsgif_bytes--;
+
+ if (nsgif_bytes == 0) {
+ return NSGIF_INSUFFICIENT_DATA;
+ }
+
+ /* Switch on extension label */
+ switch (nsgif_data[0]) {
+ case NSGIF_EXTENSION_GRAPHIC_CONTROL:
+ if (decode) {
+ ret = nsgif__parse_extension_graphic_control(
+ frame, nsgif_data, nsgif_bytes);
+ if (ret != NSGIF_OK) {
+ return ret;
+ }
+ }
+ break;
+
+ case NSGIF_EXTENSION_APPLICATION:
+ if (decode) {
+ ret = nsgif__parse_extension_application(
+ gif, nsgif_data, nsgif_bytes);
+ if (ret != NSGIF_OK) {
+ return ret;
+ }
+ }
+ break;
+
+ case NSGIF_EXTENSION_COMMENT:
+ /* Move the pointer to the first data sub-block Skip 1
+ * byte for the extension label. */
+ ++nsgif_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 (nsgif_bytes < 2) {
+ return NSGIF_INSUFFICIENT_DATA;
+ }
+ nsgif_data += 2 + nsgif_data[1];
+ }
+
+ /* Repeatedly skip blocks until we get a zero block or run out
+ * of data. This data is ignored by this gif decoder. */
+ while (nsgif_data < nsgif_end && nsgif_data[0] != NSGIF_BLOCK_TERMINATOR) {
+ nsgif_data += nsgif_data[0] + 1;
+ if (nsgif_data >= nsgif_end) {
+ return NSGIF_INSUFFICIENT_DATA;
+ }
+ }
+ nsgif_data++;
+ nsgif_bytes = nsgif_end - nsgif_data;
+ }
+
+ if (nsgif_data > nsgif_end) {
+ nsgif_data = nsgif_end;
+ }
+
+ /* Set buffer position and return */
+ *pos = nsgif_data;
+ return NSGIF_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.
+ * \param[in] pos Current position in data, updated on exit.
+ * \param[in] decode Whether to decode the image descriptor.
+ * \return NSGIF_OK on success, appropriate error otherwise.
+ */
+static nsgif_result nsgif__parse_image_descriptor(
+ struct nsgif_animation *gif,
+ struct nsgif_frame *frame,
+ const uint8_t **pos,
+ bool decode)
+{
+ const uint8_t *data = *pos;
+ size_t len = gif->nsgif_data + gif->buffer_size - data;
+ enum {
+ NSGIF_IMAGE_DESCRIPTOR_LEN = 10u,
+ NSGIF_IMAGE_SEPARATOR = 0x2Cu,
+ };
+
+ assert(gif != NULL);
+ assert(frame != NULL);
+
+ if (len < NSGIF_IMAGE_DESCRIPTOR_LEN) {
+ return NSGIF_INSUFFICIENT_DATA;
+ }
+
+ if (decode) {
+ unsigned x, y, w, h;
+
+ if (data[0] != NSGIF_IMAGE_SEPARATOR) {
+ return NSGIF_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;
+
+ /* Allow first frame to grow image dimensions. */
+ if (gif->frame_count == 0) {
+ if (x + w > gif->width) {
+ gif->width = x + w;
+ }
+ if (y + h > gif->height) {
+ gif->height = y + h;
+ }
+ }
+ }
+
+ *pos += NSGIF_IMAGE_DESCRIPTOR_LEN;
+ return NSGIF_OK;
+}
+
+/**
+ * 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 NSGIF_OK on success, appropriate error otherwise.
+ */
+static nsgif_result nsgif__colour_table_extract(
+ struct nsgif_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->nsgif_data + gif->buffer_size - data;
+
+ if (len < colour_table_entries * 3) {
+ return NSGIF_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.
+ */
+
+ *entry++ = *data++; /* r */
+ *entry++ = *data++; /* g */
+ *entry++ = *data++; /* b */
+ *entry++ = 0xff; /* a */
+ }
+ }
+
+ *pos += colour_table_entries * 3;
+ return NSGIF_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.
+ * \param[in] pos Current position in data, updated on exit.
+ * \param[in] decode Whether to decode the colour table.
+ * \return NSGIF_OK on success, appropriate error otherwise.
+ */
+static nsgif_result nsgif__parse_colour_table(
+ struct nsgif_animation *gif,
+ struct nsgif_frame *frame,
+ const uint8_t **pos,
+ bool decode)
+{
+ nsgif_result ret;
+
+ assert(gif != NULL);
+ assert(frame != NULL);
+
+ if ((frame->flags & NSGIF_COLOUR_TABLE_MASK) == 0) {
+ gif->colour_table = gif->global_colour_table;
+ return NSGIF_OK;
+ }
+
+ ret = nsgif__colour_table_extract(gif, gif->local_colour_table,
+ 2 << (frame->flags & NSGIF_COLOUR_TABLE_SIZE_MASK),
+ pos, decode);
+ if (ret != NSGIF_OK) {
+ return ret;
+ }
+
+ gif->colour_table = gif->local_colour_table;
+ return NSGIF_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 parse image data for.
+ * \param[in] pos Current position in data, updated on exit.
+ * \param[in] decode Whether to decode the image data.
+ * \return NSGIF_OK on success, appropriate error otherwise.
+ */
+static nsgif_result nsgif__parse_image_data(
+ struct nsgif_animation *gif,
+ struct nsgif_frame *frame,
+ const uint8_t **pos,
+ bool decode)
+{
+ const uint8_t *data = *pos;
+ size_t len = gif->nsgif_data + gif->buffer_size - data;
+ uint32_t frame_idx = frame - gif->frames;
+ uint8_t minimum_code_size;
+ nsgif_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] == NSGIF_TRAILER) return NSGIF_OK;
+ break;
+ case 2: if (data[1] == NSGIF_TRAILER) return NSGIF_OK;
+ /* Fall through. */
+ case 1: if (data[0] == NSGIF_TRAILER) return NSGIF_OK;
+ /* Fall through. */
+ case 0: return NSGIF_INSUFFICIENT_DATA;
+ }
+
+ minimum_code_size = data[0];
+ if (minimum_code_size >= LZW_CODE_MAX) {
+ return NSGIF_DATA_ERROR;
+ }
+
+ if (decode) {
+ ret = nsgif__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 NSGIF_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 NSGIF_OK;
+ }
+
+ len -= block_size;
+ data += block_size;
+ }
+
+ gif->frame_count = frame_idx + 1;
+ gif->frames[frame_idx].display = true;
+ *pos = data;
+
+ /* Check if we've finished */
+ if (len < 1) {
+ return NSGIF_INSUFFICIENT_DATA;
+ } else {
+ if (data[0] == NSGIF_TRAILER) {
+ return NSGIF_OK;
+ }
+ }
+
+ return NSGIF_WORKING;
+ }
+
+ return ret;
+}
+
+static struct nsgif_frame *nsgif__get_frame(
+ struct nsgif_animation *gif,
+ uint32_t frame_idx)
+{
+ struct nsgif_frame *frame;
+
+ if (gif->frame_holders > frame_idx) {
+ frame = &gif->frames[frame_idx];
+ } else {
+ /* Allocate more memory */
+ size_t count = frame_idx + 1;
+ struct nsgif_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 = NSGIF_NO_TRANSPARENCY;
+ frame->frame_pointer = gif->buffer_position;
+ frame->redraw_required = false;
+ frame->disposal_method = 0;
+ frame->frame_delay = 100;
+ frame->display = false;
+ frame->decoded = false;
+ }
+
+ 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
+ * - NSGIF_INSUFFICIENT_DATA reached unexpected end of source data.
+ * - NSGIF_FRAME_DATA_ERROR for GIF frame data error
+ * - NSGIF_INSUFFICIENT_MEMORY for insufficient memory to process
+ * - NSGIF_DATA_ERROR for GIF error (invalid frame header)
+ * - NSGIF_OK for successful decoding
+ * - NSGIF_WORKING for successful decoding if more frames are expected
+*/
+static nsgif_result nsgif__process_frame(
+ struct nsgif_animation *gif,
+ uint32_t frame_idx,
+ bool decode)
+{
+ nsgif_result ret;
+ const uint8_t *pos;
+ const uint8_t *end;
+ struct nsgif_frame *frame;
+
+ frame = nsgif__get_frame(gif, frame_idx);
+ if (frame == NULL) {
+ return NSGIF_INSUFFICIENT_MEMORY;
+ }
+
+ end = gif->nsgif_data + gif->buffer_size;
+
+ if (decode) {
+ pos = gif->nsgif_data + frame->frame_pointer;
+
+ /* Ensure this frame is supposed to be decoded */
+ if (frame->display == false) {
+ return NSGIF_OK;
+ }
+
+ /* Ensure the frame is in range to decode */
+ if (frame_idx > gif->frame_count_partial) {
+ return NSGIF_INSUFFICIENT_DATA;
+ }
+
+ /* Done if frame is already decoded */
+ if ((int)frame_idx == gif->decoded_frame) {
+ return NSGIF_OK;
+ }
+ } else {
+ pos = (uint8_t *)(gif->nsgif_data + gif->buffer_position);
+
+ /* Check if we've finished */
+ if (pos < end && pos[0] == NSGIF_TRAILER) {
+ return NSGIF_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 NSGIF_FRAME_DATA_ERROR;
+ }
+ }
+
+ /* Initialise any extensions */
+ ret = nsgif__parse_frame_extensions(gif, frame, &pos, !decode);
+ if (ret != NSGIF_OK) {
+ goto cleanup;
+ }
+
+ ret = nsgif__parse_image_descriptor(gif, frame, &pos, !decode);
+ if (ret != NSGIF_OK) {
+ goto cleanup;
+ }
+
+ ret = nsgif__parse_colour_table(gif, frame, &pos, decode);
+ if (ret != NSGIF_OK) {
+ goto cleanup;
+ }
+
+ ret = nsgif__parse_image_data(gif, frame, &pos, decode);
+ if (ret != NSGIF_OK) {
+ goto cleanup;
+ }
+
+cleanup:
+ if (!decode) {
+ gif->buffer_position = pos - gif->nsgif_data;
+ }
+
+ return ret;
+}
+
+/* exported function documented in libnsgif.h */
+void nsgif_create(nsgif_animation *gif, nsgif_bitmap_cb_vt *bitmap)
+{
+ memset(gif, 0, sizeof(nsgif_animation));
+ gif->bitmap = *bitmap;
+ gif->decoded_frame = NSGIF_INVALID_FRAME;
+ gif->prev_index = NSGIF_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 NSGIF_OK on success, appropriate error otherwise.
+ */
+static nsgif_result nsgif__parse_header(
+ struct nsgif_animation *gif,
+ const uint8_t **pos,
+ bool strict)
+{
+ const uint8_t *data = *pos;
+ size_t len = gif->nsgif_data + gif->buffer_size - data;
+
+ if (len < 6) {
+ return NSGIF_INSUFFICIENT_DATA;
+ }
+
+ if (strncmp((const char *) data, "GIF", 3) != 0) {
+ return NSGIF_DATA_ERROR;
+ }
+ data += 3;
+
+ if (strict == true) {
+ if ((strncmp((const char *) data, "87a", 3) != 0) &&
+ (strncmp((const char *) data, "89a", 3) != 0)) {
+ return NSGIF_DATA_ERROR;
+ }
+ }
+ data += 3;
+
+ *pos = data;
+ return NSGIF_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 NSGIF_OK on success, appropriate error otherwise.
+ */
+static nsgif_result nsgif__parse_logical_screen_descriptor(
+ struct nsgif_animation *gif,
+ const uint8_t **pos)
+{
+ const uint8_t *data = *pos;
+ size_t len = gif->nsgif_data + gif->buffer_size - data;
+
+ if (len < 7) {
+ return NSGIF_INSUFFICIENT_DATA;
+ }
+
+ gif->width = data[0] | (data[1] << 8);
+ gif->height = data[2] | (data[3] << 8);
+ gif->global_colours = data[4] & NSGIF_COLOUR_TABLE_MASK;
+ gif->colour_table_size = 2 << (data[4] & NSGIF_COLOUR_TABLE_SIZE_MASK);
+ gif->bg_index = data[5];
+ gif->aspect_ratio = data[6];
+ gif->loop_count = 1;
+
+ *pos += 7;
+ return NSGIF_OK;
+}
+
+/* exported function documented in libnsgif.h */
+nsgif_result nsgif_initialise(nsgif_animation *gif, size_t size, const uint8_t *data)
+{
+ const uint8_t *nsgif_data;
+ nsgif_result ret;
+
+ /* Initialize values */
+ gif->buffer_size = size;
+ gif->nsgif_data = data;
+
+ /* Get our current processing position */
+ nsgif_data = gif->nsgif_data + gif->buffer_position;
+
+ /* See if we should initialise the GIF */
+ if (gif->buffer_position == 0) {
+ /* We want everything to be NULL before we start so we've no
+ * chance of freeing bad pointers (paranoia)
+ */
+ gif->frame_image = NULL;
+ gif->frames = NULL;
+ gif->frame_holders = 0;
+ gif->local_colour_table = NULL;
+ gif->global_colour_table = NULL;
+
+ /* The caller may have been lazy and not reset any values */
+ gif->frame_count = 0;
+ gif->frame_count_partial = 0;
+ gif->decoded_frame = NSGIF_INVALID_FRAME;
+
+ ret = nsgif__parse_header(gif, &nsgif_data, false);
+ if (ret != NSGIF_OK) {
+ return ret;
+ }
+
+ ret = nsgif__parse_logical_screen_descriptor(gif, &nsgif_data);
+ if (ret != NSGIF_OK) {
+ return ret;
+ }
+
+ /* Remember we've done this now */
+ gif->buffer_position = nsgif_data - gif->nsgif_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
+ * GIF being the maximum size of the frames.
+ */
+ if (((gif->width == 640) && (gif->height == 480)) ||
+ ((gif->width == 640) && (gif->height == 512)) ||
+ ((gif->width == 800) && (gif->height == 600)) ||
+ ((gif->width == 1024) && (gif->height == 768)) ||
+ ((gif->width == 1280) && (gif->height == 1024)) ||
+ ((gif->width == 1600) && (gif->height == 1200)) ||
+ ((gif->width == 0) || (gif->height == 0)) ||
+ ((gif->width > 2048) || (gif->height > 2048))) {
+ gif->width = 1;
+ gif->height = 1;
+ }
+
+ /* Allocate some data irrespective of whether we've got any
+ * colour tables. We always get the maximum size in case a GIF
+ * 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(NSGIF_MAX_COLOURS, sizeof(uint32_t));
+ gif->local_colour_table = calloc(NSGIF_MAX_COLOURS, sizeof(uint32_t));
+ if ((gif->global_colour_table == NULL) ||
+ (gif->local_colour_table == NULL)) {
+ nsgif_finalise(gif);
+ return NSGIF_INSUFFICIENT_MEMORY;
+ }
+
+ /* Set the first colour to a value that will never occur in
+ * reality so we know if we've processed it
+ */
+ gif->global_colour_table[0] = NSGIF_PROCESS_COLOURS;
+
+ /* Check if the GIF has no frame data (13-byte header + 1-byte
+ * termination block) Although generally useless, the GIF
+ * specification does not expressly prohibit this
+ */
+ if (gif->buffer_size == gif->buffer_position + 1) {
+ if (nsgif_data[0] == NSGIF_TRAILER) {
+ return NSGIF_OK;
+ }
+ }
+ }
+
+ /* Do the colour map if we haven't already. As the top byte is always
+ * 0xff or 0x00 depending on the transparency we know if it's been
+ * filled in.
+ */
+ if (gif->global_colour_table[0] == NSGIF_PROCESS_COLOURS) {
+ /* Check for a global colour map signified by bit 7 */
+ if (gif->global_colours) {
+ ret = nsgif__colour_table_extract(gif,
+ gif->global_colour_table,
+ gif->colour_table_size,
+ &nsgif_data, true);
+ if (ret != NSGIF_OK) {
+ return ret;
+ }
+
+ gif->buffer_position = (nsgif_data - gif->nsgif_data);
+ } else {
+ /* Create a default colour table with the first two
+ * colours as black and white
+ */
+ uint32_t *entry = gif->global_colour_table;
+
+ entry[0] = 0x00000000;
+ /* Force Alpha channel to opaque */
+ ((uint8_t *) entry)[3] = 0xff;
+
+ entry[1] = 0xffffffff;
+ }
+
+ 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 (gif->lzw_ctx == NULL) {
+ lzw_result res = lzw_context_create(
+ (struct lzw_ctx **)&gif->lzw_ctx);
+ if (res != LZW_OK) {
+ return nsgif__error_from_lzw(res);
+ }
+ }
+
+ /* Repeatedly try to initialise frames */
+ do {
+ ret = nsgif__process_frame(gif, gif->frame_count, false);
+ } while (ret == NSGIF_WORKING);
+
+ return ret;
+}
+
+/* exported function documented in libnsgif.h */
+nsgif_result nsgif_decode_frame(nsgif_animation *gif, uint32_t frame)
+{
+ return nsgif__process_frame(gif, frame, true);
+}
+
+/* exported function documented in libnsgif.h */
+void nsgif_finalise(nsgif_animation *gif)
+{
+ /* Release all our memory blocks */
+ if (gif->frame_image) {
+ assert(gif->bitmap.destroy);
+ gif->bitmap.destroy(gif->frame_image);
+ }
+
+ gif->frame_image = NULL;
+ free(gif->frames);
+ gif->frames = NULL;
+ free(gif->local_colour_table);
+ gif->local_colour_table = NULL;
+ free(gif->global_colour_table);
+ gif->global_colour_table = NULL;
+
+ free(gif->prev_frame);
+ gif->prev_frame = NULL;
+
+ lzw_context_destroy(gif->lzw_ctx);
+ gif->lzw_ctx = NULL;
+}
diff --git a/src/libnsgif.c b/src/libnsgif.c
deleted file mode 100644
index a2f28b4..0000000
--- a/src/libnsgif.c
+++ /dev/null
@@ -1,1460 +0,0 @@
-/*
- * 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,
- * http://www.opensource.org/licenses/mit-license.php
- */
-
-#include <assert.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
-
-#include "lzw.h"
-#include "libnsgif.h"
-
-/**
- *
- * \file
- * \brief GIF image decoder
- *
- * The GIF format is thoroughly documented; a full description can be found at
- * http://www.w3.org/Graphics/GIF/spec-gif89a.txt
- *
- * \todo Plain text and comment extensions should be implemented.
- */
-
-/** Maximum colour table size */
-#define NSGIF_MAX_COLOURS 256
-
-/** Internal flag that the colour table needs to be processed */
-#define NSGIF_PROCESS_COLOURS 0xaa000000
-
-/** Internal flag that a frame is invalid/unprocessed */
-#define NSGIF_INVALID_FRAME -1
-
-/** Transparent colour */
-#define NSGIF_TRANSPARENT_COLOUR 0x00
-
-/** No transparency */
-#define NSGIF_NO_TRANSPARENCY (0xFFFFFFFFu)
-
-enum nsgif_disposal {
- NSGIF_DISPOSAL_UNSPECIFIED,
- NSGIF_DISPOSAL_NONE,
- NSGIF_DISPOSAL_RESTORE_BG,
- NSGIF_DISPOSAL_RESTORE_PREV,
- NSGIF_DISPOSAL_RESTORE_QUIRK, /**< Alias for NSGIF_DISPOSAL_RESTORE_PREV. */
-};
-
-/* GIF Flags */
-#define NSGIF_INTERLACE_MASK 0x40
-#define NSGIF_COLOUR_TABLE_MASK 0x80
-#define NSGIF_COLOUR_TABLE_SIZE_MASK 0x07
-#define NSGIF_EXTENSION_INTRODUCER 0x21
-#define NSGIF_EXTENSION_GRAPHIC_CONTROL 0xf9
-#define NSGIF_DISPOSAL_MASK 0x1c
-#define NSGIF_TRANSPARENCY_MASK 0x01
-#define NSGIF_EXTENSION_COMMENT 0xfe
-#define NSGIF_EXTENSION_PLAIN_TEXT 0x01
-#define NSGIF_EXTENSION_APPLICATION 0xff
-#define NSGIF_BLOCK_TERMINATOR 0x00
-#define NSGIF_TRAILER 0x3b
-
-/**
- * Convert an LZW result code to equivalent GIF result code.
- *
- * \param[in] l_res LZW response code.
- * \return GIF result code.
- */
-static nsgif_result nsgif__error_from_lzw(lzw_result l_res)
-{
- static const nsgif_result g_res[] = {
- [LZW_OK] = NSGIF_OK,
- [LZW_OK_EOD] = NSGIF_END_OF_FRAME,
- [LZW_NO_MEM] = NSGIF_INSUFFICIENT_MEMORY,
- [LZW_NO_DATA] = NSGIF_INSUFFICIENT_DATA,
- [LZW_EOI_CODE] = NSGIF_FRAME_DATA_ERROR,
- [LZW_BAD_ICODE] = NSGIF_FRAME_DATA_ERROR,
- [LZW_BAD_CODE] = NSGIF_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
- *
- * \param gif The animation context
- * \param width The width of the sprite
- * \param height The height of the sprite
- * \return NSGIF_INSUFFICIENT_MEMORY for a memory error NSGIF_OK for success
- */
-static nsgif_result nsgif__initialise_sprite(
- struct nsgif_animation *gif,
- uint32_t width,
- uint32_t height)
-{
- /* Already allocated? */
- if (gif->frame_image) {
- return NSGIF_OK;
- }
-
- assert(gif->bitmap.create);
- gif->frame_image = gif->bitmap.create(width, height);
- if (gif->frame_image == NULL) {
- return NSGIF_INSUFFICIENT_MEMORY;
- }
-
- return NSGIF_OK;
-}
-
-/**
- * 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* nsgif__bitmap_get(
- struct nsgif_animation *gif)
-{
- nsgif_result ret;
-
- /* Make sure we have a buffer to decode to. */
- ret = nsgif__initialise_sprite(gif, gif->width, gif->height);
- if (ret != NSGIF_OK) {
- return NULL;
- }
-
- /* Get the frame data */
- assert(gif->bitmap.get_buffer);
- return (uint32_t *)gif->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 nsgif__bitmap_modified(
- const struct nsgif_animation *gif)
-{
- if (gif->bitmap.modified) {
- gif->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] frame The frame that has been decoded.
- */
-static inline void nsgif__bitmap_set_opaque(
- const struct nsgif_animation *gif,
- const struct nsgif_frame *frame)
-{
- if (gif->bitmap.set_opaque) {
- gif->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 nsgif__bitmap_get_opaque(
- const struct nsgif_animation *gif)
-{
- if (gif->bitmap.test_opaque) {
- return gif->bitmap.test_opaque(
- gif->frame_image);
- }
-
- return false;
-}
-
-static void nsgif__record_frame(
- struct nsgif_animation *gif,
- const uint32_t *bitmap)
-{
- bool need_alloc = gif->prev_frame == NULL;
- uint32_t *prev_frame;
-
- if (gif->decoded_frame == NSGIF_INVALID_FRAME ||
- gif->decoded_frame == gif->prev_index) {
- /* No frame to copy, or already have this frame recorded. */
- return;
- }
-
- bitmap = nsgif__bitmap_get(gif);
- if (bitmap == NULL) {
- return;
- }
-
- if (gif->prev_frame != NULL &&
- gif->width * gif->height > gif->prev_width * gif->prev_height) {
- need_alloc = true;
- }
-
- 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;
- }
-
- memcpy(prev_frame, bitmap, 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 nsgif_result nsgif__recover_frame(
- const struct nsgif_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;
-
- if (prev_frame == NULL) {
- return NSGIF_FRAME_DATA_ERROR;
- }
-
- for (unsigned y = 0; y < height; y++) {
- memcpy(bitmap, prev_frame, width * 4);
-
- bitmap += gif->width;
- prev_frame += gif->prev_width;
- }
-
- return NSGIF_OK;
-}
-
-/**
- * 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 nsgif__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 nsgif__next_row(uint32_t interlace,
- uint32_t height, uint32_t *y, uint8_t *step)
-{
- if (!interlace) {
- return (++*y != height);
- } else {
- return nsgif__deinterlace(height, y, step);
- }
-}
-
-/**
- * Get any frame clip adjustment for the image extent.
- *
- * \param[in] frame_off Frame's X or Y offset.
- * \param[in] frame_dim Frame width or height.
- * \param[in] image_ext Image width or height constraint.
- * \return the amount the frame needs to be clipped to fit the image in given
- * dimension.
- */
-static inline uint32_t gif__clip(
- uint32_t frame_off,
- uint32_t frame_dim,
- uint32_t image_ext)
-{
- uint32_t frame_ext = frame_off + frame_dim;
-
- if (frame_ext <= image_ext) {
- return 0;
- }
-
- return frame_ext - image_ext;
-}
-
-/**
- * Perform any jump over decoded data, to accommodate clipped portion of frame.
- *
- * \param[in,out] skip Number of pixels of data to jump.
- * \param[in,out] available Number of pixels of data currently available.
- * \param[in,out] pos Position in decoded pixel value data.
- */
-static inline void gif__jump_data(
- uint32_t *skip,
- uint32_t *available,
- const uint8_t **pos)
-{
- uint32_t jump = (*skip < *available) ? *skip : *available;
-
- *skip -= jump;
- *available -= jump;
- *pos += jump;
-}
-
-static nsgif_result nsgif__decode_complex(
- struct nsgif_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)
-{
- lzw_result res;
- nsgif_result ret = NSGIF_OK;
- uint32_t clip_x = gif__clip(offset_x, width, gif->width);
- uint32_t clip_y = gif__clip(offset_y, height, gif->height);
- const uint8_t *uncompressed;
- uint32_t available = 0;
- uint8_t step = 24;
- uint32_t skip = 0;
- uint32_t y = 0;
-
- if (offset_x >= gif->width ||
- offset_y >= gif->height) {
- return NSGIF_OK;
- }
-
- width -= clip_x;
- height -= clip_y;
-
- if (width == 0 || height == 0) {
- return NSGIF_OK;
- }
-
- /* Initialise the LZW decoding */
- res = lzw_decode_init(gif->lzw_ctx, data[0],
- gif->nsgif_data, gif->buffer_size,
- data + 1 - gif->nsgif_data);
- if (res != LZW_OK) {
- return nsgif__error_from_lzw(res);
- }
-
- do {
- uint32_t x;
- uint32_t *frame_scanline;
-
- frame_scanline = frame_data + offset_x +
- (y + offset_y) * gif->width;
-
- x = width;
- while (x > 0) {
- unsigned row_available;
- while (available == 0) {
- if (res != LZW_OK) {
- /* Unexpected end of frame, try to recover */
- if (res == LZW_OK_EOD) {
- ret = NSGIF_OK;
- } else {
- ret = nsgif__error_from_lzw(res);
- }
- return ret;
- }
- res = lzw_decode(gif->lzw_ctx,
- &uncompressed, &available);
-
- if (available == 0) {
- return NSGIF_OK;
- }
- gif__jump_data(&skip, &available, &uncompressed);
- }
-
- 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++;
- }
- }
- }
-
- skip = clip_x;
- gif__jump_data(&skip, &available, &uncompressed);
- } while (nsgif__next_row(interlace, height, &y, &step));
-
- return ret;
-}
-
-static nsgif_result nsgif__decode_simple(
- struct nsgif_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)
-{
- uint32_t pixels = gif->width * height;
- uint32_t written = 0;
- nsgif_result ret = NSGIF_OK;
- lzw_result res;
-
- if (offset_y >= gif->height) {
- return NSGIF_OK;
- }
-
- height -= gif__clip(offset_y, height, gif->height);
-
- if (height == 0) {
- return NSGIF_OK;
- }
-
- /* Initialise the LZW decoding */
- res = lzw_decode_init_map(gif->lzw_ctx, data[0],
- transparency_index, colour_table,
- gif->nsgif_data, gif->buffer_size,
- data + 1 - gif->nsgif_data);
- if (res != LZW_OK) {
- return nsgif__error_from_lzw(res);
- }
-
- 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 = NSGIF_OK;
- } else {
- ret = nsgif__error_from_lzw(res);
- }
- break;
- }
- }
-
- if (pixels == 0) {
- ret = NSGIF_OK;
- }
-
- return ret;
-}
-
-static inline nsgif_result nsgif__decode(
- struct nsgif_animation *gif,
- struct nsgif_frame *frame,
- const uint8_t *data,
- uint32_t *restrict frame_data)
-{
- nsgif_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 & NSGIF_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 = nsgif__decode_simple(gif, height, offset_y,
- data, transparency_index,
- frame_data, colour_table);
- } else {
- ret = nsgif__decode_complex(gif, width, height,
- offset_x, offset_y, interlace,
- data, transparency_index,
- frame_data, colour_table);
- }
-
- return ret;
-}
-
-/**
- * Restore a GIF to the background colour.
- *
- * \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 nsgif__restore_bg(
- struct nsgif_animation *gif,
- struct nsgif_frame *frame,
- uint32_t *bitmap)
-{
- if (frame == NULL) {
- memset(bitmap, NSGIF_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;
-
- width -= gif__clip(offset_x, width, gif->width);
- height -= gif__clip(offset_y, height, gif->height);
-
- if (frame->display == false || width == 0) {
- 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, NSGIF_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 nsgif_result nsgif__update_bitmap(
- struct nsgif_animation *gif,
- struct nsgif_frame *frame,
- const uint8_t *data,
- uint32_t frame_idx)
-{
- nsgif_result ret;
- uint32_t *bitmap;
-
- gif->decoded_frame = frame_idx;
-
- bitmap = nsgif__bitmap_get(gif);
- if (bitmap == NULL) {
- return NSGIF_INSUFFICIENT_MEMORY;
- }
-
- /* Handle any bitmap clearing/restoration required before decoding this
- * frame. */
- if (frame_idx == 0 || gif->decoded_frame == NSGIF_INVALID_FRAME) {
- nsgif__restore_bg(gif, NULL, bitmap);
-
- } else {
- struct nsgif_frame *prev = &gif->frames[frame_idx - 1];
-
- if (prev->disposal_method == NSGIF_DISPOSAL_RESTORE_BG) {
- nsgif__restore_bg(gif, prev, bitmap);
-
- } else if (prev->disposal_method == NSGIF_DISPOSAL_RESTORE_PREV) {
- ret = nsgif__recover_frame(gif, bitmap);
- if (ret != NSGIF_OK) {
- nsgif__restore_bg(gif, prev, bitmap);
- }
- }
- }
-
- if (frame->disposal_method == NSGIF_DISPOSAL_RESTORE_PREV) {
- /* Store the previous frame for later restoration */
- nsgif__record_frame(gif, bitmap);
- }
-
- ret = nsgif__decode(gif, frame, data, bitmap);
-
- nsgif__bitmap_modified(gif);
-
- if (!frame->decoded) {
- frame->opaque = nsgif__bitmap_get_opaque(gif);
- frame->decoded = true;
- }
- nsgif__bitmap_set_opaque(gif, frame);
-
- return ret;
-}
-
-/**
- * Parse the graphic control 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 NSGIF_INSUFFICIENT_DATA if more data is needed,
- * NSGIF_OK for success.
- */
-static nsgif_result nsgif__parse_extension_graphic_control(
- struct nsgif_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 (len < 6) {
- return NSGIF_INSUFFICIENT_DATA;
- }
-
- frame->frame_delay = data[3] | (data[4] << 8);
- if (data[2] & NSGIF_TRANSPARENCY_MASK) {
- frame->transparency = true;
- frame->transparency_index = data[5];
- }
-
- frame->disposal_method = ((data[2] & NSGIF_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 == NSGIF_DISPOSAL_RESTORE_QUIRK) {
- frame->disposal_method = NSGIF_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 == NSGIF_DISPOSAL_RESTORE_BG ||
- frame->disposal_method == NSGIF_DISPOSAL_RESTORE_PREV;
-
- return NSGIF_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 NSGIF_INSUFFICIENT_DATA if more data is needed,
- * NSGIF_OK for success.
- */
-static nsgif_result nsgif__parse_extension_application(
- struct nsgif_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)
- */
- if (len < 17) {
- return NSGIF_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 NSGIF_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] pos Current position in data, updated on exit.
- * \param[in] decode Whether to decode or skip over the extension.
- * \return NSGIF_INSUFFICIENT_DATA if more data is needed,
- * NSGIF_OK for success.
- */
-static nsgif_result nsgif__parse_frame_extensions(
- struct nsgif_animation *gif,
- struct nsgif_frame *frame,
- const uint8_t **pos,
- bool decode)
-{
- const uint8_t *nsgif_data = *pos;
- const uint8_t *nsgif_end = gif->nsgif_data + gif->buffer_size;
- int nsgif_bytes = nsgif_end - nsgif_data;
-
- /* Initialise the extensions */
- while (nsgif_bytes > 0 && nsgif_data[0] == NSGIF_EXTENSION_INTRODUCER) {
- bool block_step = true;
- nsgif_result ret;
-
- nsgif_data++;
- nsgif_bytes--;
-
- if (nsgif_bytes == 0) {
- return NSGIF_INSUFFICIENT_DATA;
- }
-
- /* Switch on extension label */
- switch (nsgif_data[0]) {
- case NSGIF_EXTENSION_GRAPHIC_CONTROL:
- if (decode) {
- ret = nsgif__parse_extension_graphic_control(
- frame, nsgif_data, nsgif_bytes);
- if (ret != NSGIF_OK) {
- return ret;
- }
- }
- break;
-
- case NSGIF_EXTENSION_APPLICATION:
- if (decode) {
- ret = nsgif__parse_extension_application(
- gif, nsgif_data, nsgif_bytes);
- if (ret != NSGIF_OK) {
- return ret;
- }
- }
- break;
-
- case NSGIF_EXTENSION_COMMENT:
- /* Move the pointer to the first data sub-block Skip 1
- * byte for the extension label. */
- ++nsgif_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 (nsgif_bytes < 2) {
- return NSGIF_INSUFFICIENT_DATA;
- }
- nsgif_data += 2 + nsgif_data[1];
- }
-
- /* Repeatedly skip blocks until we get a zero block or run out
- * of data. This data is ignored by this gif decoder. */
- while (nsgif_data < nsgif_end && nsgif_data[0] != NSGIF_BLOCK_TERMINATOR) {
- nsgif_data += nsgif_data[0] + 1;
- if (nsgif_data >= nsgif_end) {
- return NSGIF_INSUFFICIENT_DATA;
- }
- }
- nsgif_data++;
- nsgif_bytes = nsgif_end - nsgif_data;
- }
-
- if (nsgif_data > nsgif_end) {
- nsgif_data = nsgif_end;
- }
-
- /* Set buffer position and return */
- *pos = nsgif_data;
- return NSGIF_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.
- * \param[in] pos Current position in data, updated on exit.
- * \param[in] decode Whether to decode the image descriptor.
- * \return NSGIF_OK on success, appropriate error otherwise.
- */
-static nsgif_result nsgif__parse_image_descriptor(
- struct nsgif_animation *gif,
- struct nsgif_frame *frame,
- const uint8_t **pos,
- bool decode)
-{
- const uint8_t *data = *pos;
- size_t len = gif->nsgif_data + gif->buffer_size - data;
- enum {
- NSGIF_IMAGE_DESCRIPTOR_LEN = 10u,
- NSGIF_IMAGE_SEPARATOR = 0x2Cu,
- };
-
- assert(gif != NULL);
- assert(frame != NULL);
-
- if (len < NSGIF_IMAGE_DESCRIPTOR_LEN) {
- return NSGIF_INSUFFICIENT_DATA;
- }
-
- if (decode) {
- unsigned x, y, w, h;
-
- if (data[0] != NSGIF_IMAGE_SEPARATOR) {
- return NSGIF_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;
-
- /* Allow first frame to grow image dimensions. */
- if (gif->frame_count == 0) {
- if (x + w > gif->width) {
- gif->width = x + w;
- }
- if (y + h > gif->height) {
- gif->height = y + h;
- }
- }
- }
-
- *pos += NSGIF_IMAGE_DESCRIPTOR_LEN;
- return NSGIF_OK;
-}
-
-/**
- * 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 NSGIF_OK on success, appropriate error otherwise.
- */
-static nsgif_result nsgif__colour_table_extract(
- struct nsgif_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->nsgif_data + gif->buffer_size - data;
-
- if (len < colour_table_entries * 3) {
- return NSGIF_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.
- */
-
- *entry++ = *data++; /* r */
- *entry++ = *data++; /* g */
- *entry++ = *data++; /* b */
- *entry++ = 0xff; /* a */
- }
- }
-
- *pos += colour_table_entries * 3;
- return NSGIF_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.
- * \param[in] pos Current position in data, updated on exit.
- * \param[in] decode Whether to decode the colour table.
- * \return NSGIF_OK on success, appropriate error otherwise.
- */
-static nsgif_result nsgif__parse_colour_table(
- struct nsgif_animation *gif,
- struct nsgif_frame *frame,
- const uint8_t **pos,
- bool decode)
-{
- nsgif_result ret;
-
- assert(gif != NULL);
- assert(frame != NULL);
-
- if ((frame->flags & NSGIF_COLOUR_TABLE_MASK) == 0) {
- gif->colour_table = gif->global_colour_table;
- return NSGIF_OK;
- }
-
- ret = nsgif__colour_table_extract(gif, gif->local_colour_table,
- 2 << (frame->flags & NSGIF_COLOUR_TABLE_SIZE_MASK),
- pos, decode);
- if (ret != NSGIF_OK) {
- return ret;
- }
-
- gif->colour_table = gif->local_colour_table;
- return NSGIF_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 parse image data for.
- * \param[in] pos Current position in data, updated on exit.
- * \param[in] decode Whether to decode the image data.
- * \return NSGIF_OK on success, appropriate error otherwise.
- */
-static nsgif_result nsgif__parse_image_data(
- struct nsgif_animation *gif,
- struct nsgif_frame *frame,
- const uint8_t **pos,
- bool decode)
-{
- const uint8_t *data = *pos;
- size_t len = gif->nsgif_data + gif->buffer_size - data;
- uint32_t frame_idx = frame - gif->frames;
- uint8_t minimum_code_size;
- nsgif_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] == NSGIF_TRAILER) return NSGIF_OK;
- break;
- case 2: if (data[1] == NSGIF_TRAILER) return NSGIF_OK;
- /* Fall through. */
- case 1: if (data[0] == NSGIF_TRAILER) return NSGIF_OK;
- /* Fall through. */
- case 0: return NSGIF_INSUFFICIENT_DATA;
- }
-
- minimum_code_size = data[0];
- if (minimum_code_size >= LZW_CODE_MAX) {
- return NSGIF_DATA_ERROR;
- }
-
- if (decode) {
- ret = nsgif__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 NSGIF_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 NSGIF_OK;
- }
-
- len -= block_size;
- data += block_size;
- }
-
- gif->frame_count = frame_idx + 1;
- gif->frames[frame_idx].display = true;
- *pos = data;
-
- /* Check if we've finished */
- if (len < 1) {
- return NSGIF_INSUFFICIENT_DATA;
- } else {
- if (data[0] == NSGIF_TRAILER) {
- return NSGIF_OK;
- }
- }
-
- return NSGIF_WORKING;
- }
-
- return ret;
-}
-
-static struct nsgif_frame *nsgif__get_frame(
- struct nsgif_animation *gif,
- uint32_t frame_idx)
-{
- struct nsgif_frame *frame;
-
- if (gif->frame_holders > frame_idx) {
- frame = &gif->frames[frame_idx];
- } else {
- /* Allocate more memory */
- size_t count = frame_idx + 1;
- struct nsgif_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 = NSGIF_NO_TRANSPARENCY;
- frame->frame_pointer = gif->buffer_position;
- frame->redraw_required = false;
- frame->disposal_method = 0;
- frame->frame_delay = 100;
- frame->display = false;
- frame->decoded = false;
- }
-
- 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
- * - NSGIF_INSUFFICIENT_DATA reached unexpected end of source data.
- * - NSGIF_FRAME_DATA_ERROR for GIF frame data error
- * - NSGIF_INSUFFICIENT_MEMORY for insufficient memory to process
- * - NSGIF_DATA_ERROR for GIF error (invalid frame header)
- * - NSGIF_OK for successful decoding
- * - NSGIF_WORKING for successful decoding if more frames are expected
-*/
-static nsgif_result nsgif__process_frame(
- struct nsgif_animation *gif,
- uint32_t frame_idx,
- bool decode)
-{
- nsgif_result ret;
- const uint8_t *pos;
- const uint8_t *end;
- struct nsgif_frame *frame;
-
- frame = nsgif__get_frame(gif, frame_idx);
- if (frame == NULL) {
- return NSGIF_INSUFFICIENT_MEMORY;
- }
-
- end = gif->nsgif_data + gif->buffer_size;
-
- if (decode) {
- pos = gif->nsgif_data + frame->frame_pointer;
-
- /* Ensure this frame is supposed to be decoded */
- if (frame->display == false) {
- return NSGIF_OK;
- }
-
- /* Ensure the frame is in range to decode */
- if (frame_idx > gif->frame_count_partial) {
- return NSGIF_INSUFFICIENT_DATA;
- }
-
- /* Done if frame is already decoded */
- if ((int)frame_idx == gif->decoded_frame) {
- return NSGIF_OK;
- }
- } else {
- pos = (uint8_t *)(gif->nsgif_data + gif->buffer_position);
-
- /* Check if we've finished */
- if (pos < end && pos[0] == NSGIF_TRAILER) {
- return NSGIF_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 NSGIF_FRAME_DATA_ERROR;
- }
- }
-
- /* Initialise any extensions */
- ret = nsgif__parse_frame_extensions(gif, frame, &pos, !decode);
- if (ret != NSGIF_OK) {
- goto cleanup;
- }
-
- ret = nsgif__parse_image_descriptor(gif, frame, &pos, !decode);
- if (ret != NSGIF_OK) {
- goto cleanup;
- }
-
- ret = nsgif__parse_colour_table(gif, frame, &pos, decode);
- if (ret != NSGIF_OK) {
- goto cleanup;
- }
-
- ret = nsgif__parse_image_data(gif, frame, &pos, decode);
- if (ret != NSGIF_OK) {
- goto cleanup;
- }
-
-cleanup:
- if (!decode) {
- gif->buffer_position = pos - gif->nsgif_data;
- }
-
- return ret;
-}
-
-/* exported function documented in libnsgif.h */
-void nsgif_create(nsgif_animation *gif, nsgif_bitmap_cb_vt *bitmap)
-{
- memset(gif, 0, sizeof(nsgif_animation));
- gif->bitmap = *bitmap;
- gif->decoded_frame = NSGIF_INVALID_FRAME;
- gif->prev_index = NSGIF_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 NSGIF_OK on success, appropriate error otherwise.
- */
-static nsgif_result nsgif__parse_header(
- struct nsgif_animation *gif,
- const uint8_t **pos,
- bool strict)
-{
- const uint8_t *data = *pos;
- size_t len = gif->nsgif_data + gif->buffer_size - data;
-
- if (len < 6) {
- return NSGIF_INSUFFICIENT_DATA;
- }
-
- if (strncmp((const char *) data, "GIF", 3) != 0) {
- return NSGIF_DATA_ERROR;
- }
- data += 3;
-
- if (strict == true) {
- if ((strncmp((const char *) data, "87a", 3) != 0) &&
- (strncmp((const char *) data, "89a", 3) != 0)) {
- return NSGIF_DATA_ERROR;
- }
- }
- data += 3;
-
- *pos = data;
- return NSGIF_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 NSGIF_OK on success, appropriate error otherwise.
- */
-static nsgif_result nsgif__parse_logical_screen_descriptor(
- struct nsgif_animation *gif,
- const uint8_t **pos)
-{
- const uint8_t *data = *pos;
- size_t len = gif->nsgif_data + gif->buffer_size - data;
-
- if (len < 7) {
- return NSGIF_INSUFFICIENT_DATA;
- }
-
- gif->width = data[0] | (data[1] << 8);
- gif->height = data[2] | (data[3] << 8);
- gif->global_colours = data[4] & NSGIF_COLOUR_TABLE_MASK;
- gif->colour_table_size = 2 << (data[4] & NSGIF_COLOUR_TABLE_SIZE_MASK);
- gif->bg_index = data[5];
- gif->aspect_ratio = data[6];
- gif->loop_count = 1;
-
- *pos += 7;
- return NSGIF_OK;
-}
-
-/* exported function documented in libnsgif.h */
-nsgif_result nsgif_initialise(nsgif_animation *gif, size_t size, const uint8_t *data)
-{
- const uint8_t *nsgif_data;
- nsgif_result ret;
-
- /* Initialize values */
- gif->buffer_size = size;
- gif->nsgif_data = data;
-
- /* Get our current processing position */
- nsgif_data = gif->nsgif_data + gif->buffer_position;
-
- /* See if we should initialise the GIF */
- if (gif->buffer_position == 0) {
- /* We want everything to be NULL before we start so we've no
- * chance of freeing bad pointers (paranoia)
- */
- gif->frame_image = NULL;
- gif->frames = NULL;
- gif->frame_holders = 0;
- gif->local_colour_table = NULL;
- gif->global_colour_table = NULL;
-
- /* The caller may have been lazy and not reset any values */
- gif->frame_count = 0;
- gif->frame_count_partial = 0;
- gif->decoded_frame = NSGIF_INVALID_FRAME;
-
- ret = nsgif__parse_header(gif, &nsgif_data, false);
- if (ret != NSGIF_OK) {
- return ret;
- }
-
- ret = nsgif__parse_logical_screen_descriptor(gif, &nsgif_data);
- if (ret != NSGIF_OK) {
- return ret;
- }
-
- /* Remember we've done this now */
- gif->buffer_position = nsgif_data - gif->nsgif_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
- * GIF being the maximum size of the frames.
- */
- if (((gif->width == 640) && (gif->height == 480)) ||
- ((gif->width == 640) && (gif->height == 512)) ||
- ((gif->width == 800) && (gif->height == 600)) ||
- ((gif->width == 1024) && (gif->height == 768)) ||
- ((gif->width == 1280) && (gif->height == 1024)) ||
- ((gif->width == 1600) && (gif->height == 1200)) ||
- ((gif->width == 0) || (gif->height == 0)) ||
- ((gif->width > 2048) || (gif->height > 2048))) {
- gif->width = 1;
- gif->height = 1;
- }
-
- /* Allocate some data irrespective of whether we've got any
- * colour tables. We always get the maximum size in case a GIF
- * 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(NSGIF_MAX_COLOURS, sizeof(uint32_t));
- gif->local_colour_table = calloc(NSGIF_MAX_COLOURS, sizeof(uint32_t));
- if ((gif->global_colour_table == NULL) ||
- (gif->local_colour_table == NULL)) {
- nsgif_finalise(gif);
- return NSGIF_INSUFFICIENT_MEMORY;
- }
-
- /* Set the first colour to a value that will never occur in
- * reality so we know if we've processed it
- */
- gif->global_colour_table[0] = NSGIF_PROCESS_COLOURS;
-
- /* Check if the GIF has no frame data (13-byte header + 1-byte
- * termination block) Although generally useless, the GIF
- * specification does not expressly prohibit this
- */
- if (gif->buffer_size == gif->buffer_position + 1) {
- if (nsgif_data[0] == NSGIF_TRAILER) {
- return NSGIF_OK;
- }
- }
- }
-
- /* Do the colour map if we haven't already. As the top byte is always
- * 0xff or 0x00 depending on the transparency we know if it's been
- * filled in.
- */
- if (gif->global_colour_table[0] == NSGIF_PROCESS_COLOURS) {
- /* Check for a global colour map signified by bit 7 */
- if (gif->global_colours) {
- ret = nsgif__colour_table_extract(gif,
- gif->global_colour_table,
- gif->colour_table_size,
- &nsgif_data, true);
- if (ret != NSGIF_OK) {
- return ret;
- }
-
- gif->buffer_position = (nsgif_data - gif->nsgif_data);
- } else {
- /* Create a default colour table with the first two
- * colours as black and white
- */
- uint32_t *entry = gif->global_colour_table;
-
- entry[0] = 0x00000000;
- /* Force Alpha channel to opaque */
- ((uint8_t *) entry)[3] = 0xff;
-
- entry[1] = 0xffffffff;
- }
-
- 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 (gif->lzw_ctx == NULL) {
- lzw_result res = lzw_context_create(
- (struct lzw_ctx **)&gif->lzw_ctx);
- if (res != LZW_OK) {
- return nsgif__error_from_lzw(res);
- }
- }
-
- /* Repeatedly try to initialise frames */
- do {
- ret = nsgif__process_frame(gif, gif->frame_count, false);
- } while (ret == NSGIF_WORKING);
-
- return ret;
-}
-
-/* exported function documented in libnsgif.h */
-nsgif_result nsgif_decode_frame(nsgif_animation *gif, uint32_t frame)
-{
- return nsgif__process_frame(gif, frame, true);
-}
-
-/* exported function documented in libnsgif.h */
-void nsgif_finalise(nsgif_animation *gif)
-{
- /* Release all our memory blocks */
- if (gif->frame_image) {
- assert(gif->bitmap.destroy);
- gif->bitmap.destroy(gif->frame_image);
- }
-
- gif->frame_image = NULL;
- free(gif->frames);
- gif->frames = NULL;
- free(gif->local_colour_table);
- gif->local_colour_table = NULL;
- free(gif->global_colour_table);
- gif->global_colour_table = NULL;
-
- free(gif->prev_frame);
- gif->prev_frame = NULL;
-
- lzw_context_destroy(gif->lzw_ctx);
- gif->lzw_ctx = NULL;
-}
diff --git a/test/decode_gif.c b/test/decode_gif.c
index 87f0738..20cb571 100644
--- a/test/decode_gif.c
+++ b/test/decode_gif.c
@@ -15,7 +15,7 @@
#include <string.h>
#include <sys/stat.h>
-#include "../include/libnsgif.h"
+#include "../include/nsgif.h"
#define BYTES_PER_PIXEL 4
#define MAX_IMAGE_BYTES (48 * 1024 * 1024)
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=abfffeaad949c9f47b...
commit abfffeaad949c9f47bb6058ee17e88c7092ffbe6
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
LIB: Use consistent nsgif_ / NSGIF_ namespace.
diff --git a/include/libnsgif.h b/include/libnsgif.h
index 6b8d521..114addf 100644
--- a/include/libnsgif.h
+++ b/include/libnsgif.h
@@ -13,27 +13,27 @@
* Interface to progressive animated GIF file decoding.
*/
-#ifndef _LIBNSGIF_H_
-#define _LIBNSGIF_H_
+#ifndef NSNSGIF_H
+#define NSNSGIF_H
#include <stdint.h>
#include <stdbool.h>
/* Error return values */
typedef enum {
- 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;
+ NSGIF_WORKING = 1,
+ NSGIF_OK = 0,
+ NSGIF_INSUFFICIENT_DATA = -1,
+ NSGIF_INSUFFICIENT_FRAME_DATA = NSGIF_INSUFFICIENT_DATA,
+ NSGIF_FRAME_DATA_ERROR = -2,
+ NSGIF_DATA_ERROR = -4,
+ NSGIF_INSUFFICIENT_MEMORY = -5,
+ NSGIF_FRAME_NO_DISPLAY = -6,
+ NSGIF_END_OF_FRAME = -7
+} nsgif_result;
/** GIF frame data */
-typedef struct gif_frame {
+typedef struct nsgif_frame {
/** whether the frame should be displayed/animated */
bool display;
/** delay (in cs) before animating the frame */
@@ -65,43 +65,43 @@ typedef struct gif_frame {
uint32_t redraw_height;
/* Frame flags */
uint32_t flags;
-} gif_frame;
+} nsgif_frame;
/* API for Bitmap callbacks */
-typedef void* (*gif_bitmap_cb_create)(int width, int height);
-typedef void (*gif_bitmap_cb_destroy)(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);
+typedef void* (*nsgif_bitmap_cb_create)(int width, int height);
+typedef void (*nsgif_bitmap_cb_destroy)(void *bitmap);
+typedef uint8_t* (*nsgif_bitmap_cb_get_buffer)(void *bitmap);
+typedef void (*nsgif_bitmap_cb_set_opaque)(void *bitmap, bool opaque);
+typedef bool (*nsgif_bitmap_cb_test_opaque)(void *bitmap);
+typedef void (*nsgif_bitmap_cb_modified)(void *bitmap);
/** Bitmap callbacks function table */
-typedef struct gif_bitmap_callback_vt {
+typedef struct nsgif_bitmap_cb_vt {
/** Create a bitmap. */
- gif_bitmap_cb_create bitmap_create;
+ nsgif_bitmap_cb_create create;
/** Free a bitmap. */
- gif_bitmap_cb_destroy bitmap_destroy;
+ nsgif_bitmap_cb_destroy destroy;
/** Return a pointer to the pixel data in a bitmap. */
- gif_bitmap_cb_get_buffer bitmap_get_buffer;
+ nsgif_bitmap_cb_get_buffer get_buffer;
/* Members below are optional */
/** Sets whether a bitmap should be plotted opaque. */
- gif_bitmap_cb_set_opaque bitmap_set_opaque;
+ nsgif_bitmap_cb_set_opaque set_opaque;
/** Tests whether a bitmap has an opaque alpha channel. */
- gif_bitmap_cb_test_opaque bitmap_test_opaque;
+ nsgif_bitmap_cb_test_opaque test_opaque;
/** The bitmap image has changed, so flush any persistent cache. */
- gif_bitmap_cb_modified bitmap_modified;
-} gif_bitmap_callback_vt;
+ nsgif_bitmap_cb_modified modified;
+} nsgif_bitmap_cb_vt;
/** GIF animation data */
-typedef struct gif_animation {
+typedef struct nsgif_animation {
/** LZW decode context */
void *lzw_ctx;
/** callbacks for bitmap functions */
- gif_bitmap_callback_vt bitmap_callbacks;
+ nsgif_bitmap_cb_vt bitmap;
/** pointer to GIF data */
- const uint8_t *gif_data;
+ const uint8_t *nsgif_data;
/** width of GIF (may increase during decoding) */
uint32_t width;
/** height of GIF (may increase during decoding) */
@@ -111,7 +111,7 @@ typedef struct gif_animation {
/** number of frames partially decoded */
uint32_t frame_count_partial;
/** decoded frames */
- gif_frame *frames;
+ nsgif_frame *frames;
/** current frame decoded to bitmap */
int decoded_frame;
/** currently decoded image; stored as bitmap from bitmap_create callback */
@@ -144,7 +144,7 @@ typedef struct gif_animation {
/** current colour table */
uint32_t *colour_table;
- /** previous frame for GIF_FRAME_RESTORE */
+ /** previous frame for NSGIF_FRAME_RESTORE */
void *prev_frame;
/** previous frame index */
int prev_index;
@@ -152,12 +152,12 @@ typedef struct gif_animation {
unsigned prev_width;
/** previous frame height */
unsigned prev_height;
-} gif_animation;
+} nsgif_animation;
/**
- * Initialises necessary gif_animation members.
+ * Initialises necessary nsgif_animation members.
*/
-void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks);
+void nsgif_create(nsgif_animation *gif, nsgif_bitmap_cb_vt *bitmap_callbacks);
/**
* Initialises any workspace held by the animation and attempts to decode
@@ -165,30 +165,30 @@ void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks);
* If an error occurs, all previously decoded frames are retained.
*
* \return Error return value.
- * - GIF_FRAME_DATA_ERROR for GIF frame data error
- * - GIF_INSUFFICIENT_DATA reached unexpected end of source data
- * - GIF_INSUFFICIENT_MEMORY for memory error
- * - GIF_DATA_ERROR for GIF error
- * - GIF_OK for successful decoding
- * - GIF_WORKING for successful decoding if more frames are expected
+ * - NSGIF_FRAME_DATA_ERROR for GIF frame data error
+ * - NSGIF_INSUFFICIENT_DATA reached unexpected end of source data
+ * - NSGIF_INSUFFICIENT_MEMORY for memory error
+ * - NSGIF_DATA_ERROR for GIF error
+ * - NSGIF_OK for successful decoding
+ * - NSGIF_WORKING for successful decoding if more frames are expected
*/
-gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data);
+nsgif_result nsgif_initialise(nsgif_animation *gif, size_t size, const uint8_t *data);
/**
* Decodes a GIF frame.
*
* \return Error return value.
- * - GIF_FRAME_DATA_ERROR for GIF frame data error
- * - GIF_DATA_ERROR for GIF error (invalid frame header)
- * - GIF_INSUFFICIENT_DATA reached unexpected end of source data
- * - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
- * - GIF_OK for successful decoding
+ * - NSGIF_FRAME_DATA_ERROR for GIF frame data error
+ * - NSGIF_DATA_ERROR for GIF error (invalid frame header)
+ * - NSGIF_INSUFFICIENT_DATA reached unexpected end of source data
+ * - NSGIF_INSUFFICIENT_MEMORY for insufficient memory to process
+ * - NSGIF_OK for successful decoding
*/
-gif_result gif_decode_frame(gif_animation *gif, uint32_t frame);
+nsgif_result nsgif_decode_frame(nsgif_animation *gif, uint32_t frame);
/**
* Releases any workspace held by a gif
*/
-void gif_finalise(gif_animation *gif);
+void nsgif_finalise(nsgif_animation *gif);
#endif
diff --git a/src/libnsgif.c b/src/libnsgif.c
index a52268e..a2f28b4 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -29,41 +29,41 @@
*/
/** Maximum colour table size */
-#define GIF_MAX_COLOURS 256
+#define NSGIF_MAX_COLOURS 256
/** Internal flag that the colour table needs to be processed */
-#define GIF_PROCESS_COLOURS 0xaa000000
+#define NSGIF_PROCESS_COLOURS 0xaa000000
/** Internal flag that a frame is invalid/unprocessed */
-#define GIF_INVALID_FRAME -1
+#define NSGIF_INVALID_FRAME -1
/** Transparent colour */
-#define GIF_TRANSPARENT_COLOUR 0x00
+#define NSGIF_TRANSPARENT_COLOUR 0x00
/** No transparency */
-#define GIF_NO_TRANSPARENCY (0xFFFFFFFFu)
-
-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 NSGIF_NO_TRANSPARENCY (0xFFFFFFFFu)
+
+enum nsgif_disposal {
+ NSGIF_DISPOSAL_UNSPECIFIED,
+ NSGIF_DISPOSAL_NONE,
+ NSGIF_DISPOSAL_RESTORE_BG,
+ NSGIF_DISPOSAL_RESTORE_PREV,
+ NSGIF_DISPOSAL_RESTORE_QUIRK, /**< Alias for NSGIF_DISPOSAL_RESTORE_PREV. */
};
/* GIF Flags */
-#define GIF_INTERLACE_MASK 0x40
-#define GIF_COLOUR_TABLE_MASK 0x80
-#define GIF_COLOUR_TABLE_SIZE_MASK 0x07
-#define GIF_EXTENSION_INTRODUCER 0x21
-#define GIF_EXTENSION_GRAPHIC_CONTROL 0xf9
-#define GIF_DISPOSAL_MASK 0x1c
-#define GIF_TRANSPARENCY_MASK 0x01
-#define GIF_EXTENSION_COMMENT 0xfe
-#define GIF_EXTENSION_PLAIN_TEXT 0x01
-#define GIF_EXTENSION_APPLICATION 0xff
-#define GIF_BLOCK_TERMINATOR 0x00
-#define GIF_TRAILER 0x3b
+#define NSGIF_INTERLACE_MASK 0x40
+#define NSGIF_COLOUR_TABLE_MASK 0x80
+#define NSGIF_COLOUR_TABLE_SIZE_MASK 0x07
+#define NSGIF_EXTENSION_INTRODUCER 0x21
+#define NSGIF_EXTENSION_GRAPHIC_CONTROL 0xf9
+#define NSGIF_DISPOSAL_MASK 0x1c
+#define NSGIF_TRANSPARENCY_MASK 0x01
+#define NSGIF_EXTENSION_COMMENT 0xfe
+#define NSGIF_EXTENSION_PLAIN_TEXT 0x01
+#define NSGIF_EXTENSION_APPLICATION 0xff
+#define NSGIF_BLOCK_TERMINATOR 0x00
+#define NSGIF_TRAILER 0x3b
/**
* Convert an LZW result code to equivalent GIF result code.
@@ -71,16 +71,16 @@ enum gif_disposal {
* \param[in] l_res LZW response code.
* \return GIF result code.
*/
-static gif_result gif__error_from_lzw(lzw_result l_res)
+static nsgif_result nsgif__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,
+ static const nsgif_result g_res[] = {
+ [LZW_OK] = NSGIF_OK,
+ [LZW_OK_EOD] = NSGIF_END_OF_FRAME,
+ [LZW_NO_MEM] = NSGIF_INSUFFICIENT_MEMORY,
+ [LZW_NO_DATA] = NSGIF_INSUFFICIENT_DATA,
+ [LZW_EOI_CODE] = NSGIF_FRAME_DATA_ERROR,
+ [LZW_BAD_ICODE] = NSGIF_FRAME_DATA_ERROR,
+ [LZW_BAD_CODE] = NSGIF_FRAME_DATA_ERROR,
};
assert(l_res != LZW_BAD_PARAM);
assert(l_res != LZW_NO_COLOUR);
@@ -93,25 +93,25 @@ static gif_result gif__error_from_lzw(lzw_result l_res)
* \param gif The animation context
* \param width The width of the sprite
* \param height The height of the sprite
- * \return GIF_INSUFFICIENT_MEMORY for a memory error GIF_OK for success
+ * \return NSGIF_INSUFFICIENT_MEMORY for a memory error NSGIF_OK for success
*/
-static gif_result gif__initialise_sprite(
- struct gif_animation *gif,
+static nsgif_result nsgif__initialise_sprite(
+ struct nsgif_animation *gif,
uint32_t width,
uint32_t height)
{
/* Already allocated? */
if (gif->frame_image) {
- return GIF_OK;
+ return NSGIF_OK;
}
- assert(gif->bitmap_callbacks.bitmap_create);
- gif->frame_image = gif->bitmap_callbacks.bitmap_create(width, height);
+ assert(gif->bitmap.create);
+ gif->frame_image = gif->bitmap.create(width, height);
if (gif->frame_image == NULL) {
- return GIF_INSUFFICIENT_MEMORY;
+ return NSGIF_INSUFFICIENT_MEMORY;
}
- return GIF_OK;
+ return NSGIF_OK;
}
/**
@@ -120,21 +120,20 @@ static gif_result gif__initialise_sprite(
* \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 uint32_t* nsgif__bitmap_get(
+ struct nsgif_animation *gif)
{
- gif_result ret;
+ nsgif_result ret;
/* Make sure we have a buffer to decode to. */
- ret = gif__initialise_sprite(gif, gif->width, gif->height);
- if (ret != GIF_OK) {
+ ret = nsgif__initialise_sprite(gif, gif->width, gif->height);
+ if (ret != NSGIF_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);
+ assert(gif->bitmap.get_buffer);
+ return (uint32_t *)gif->bitmap.get_buffer(gif->frame_image);
}
/**
@@ -142,11 +141,11 @@ static inline uint32_t* gif__bitmap_get(
*
* \param[in] gif The gif object we're decoding.
*/
-static inline void gif__bitmap_modified(
- const struct gif_animation *gif)
+static inline void nsgif__bitmap_modified(
+ const struct nsgif_animation *gif)
{
- if (gif->bitmap_callbacks.bitmap_modified) {
- gif->bitmap_callbacks.bitmap_modified(gif->frame_image);
+ if (gif->bitmap.modified) {
+ gif->bitmap.modified(gif->frame_image);
}
}
@@ -156,12 +155,12 @@ static inline void gif__bitmap_modified(
* \param[in] gif The gif object we're decoding.
* \param[in] frame The frame that has been decoded.
*/
-static inline void gif__bitmap_set_opaque(
- const struct gif_animation *gif,
- const struct gif_frame *frame)
+static inline void nsgif__bitmap_set_opaque(
+ const struct nsgif_animation *gif,
+ const struct nsgif_frame *frame)
{
- if (gif->bitmap_callbacks.bitmap_set_opaque) {
- gif->bitmap_callbacks.bitmap_set_opaque(
+ if (gif->bitmap.set_opaque) {
+ gif->bitmap.set_opaque(
gif->frame_image, frame->opaque);
}
}
@@ -174,31 +173,31 @@ static inline void gif__bitmap_set_opaque(
* \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)
+static inline bool nsgif__bitmap_get_opaque(
+ const struct nsgif_animation *gif)
{
- if (gif->bitmap_callbacks.bitmap_test_opaque) {
- return gif->bitmap_callbacks.bitmap_test_opaque(
+ if (gif->bitmap.test_opaque) {
+ return gif->bitmap.test_opaque(
gif->frame_image);
}
return false;
}
-static void gif__record_frame(
- struct gif_animation *gif,
+static void nsgif__record_frame(
+ struct nsgif_animation *gif,
const uint32_t *bitmap)
{
bool need_alloc = gif->prev_frame == NULL;
uint32_t *prev_frame;
- if (gif->decoded_frame == GIF_INVALID_FRAME ||
+ if (gif->decoded_frame == NSGIF_INVALID_FRAME ||
gif->decoded_frame == gif->prev_index) {
/* No frame to copy, or already have this frame recorded. */
return;
}
- bitmap = gif__bitmap_get(gif);
+ bitmap = nsgif__bitmap_get(gif);
if (bitmap == NULL) {
return;
}
@@ -226,8 +225,8 @@ static void gif__record_frame(
gif->prev_index = gif->decoded_frame;
}
-static gif_result gif__recover_frame(
- const struct gif_animation *gif,
+static nsgif_result nsgif__recover_frame(
+ const struct nsgif_animation *gif,
uint32_t *bitmap)
{
const uint32_t *prev_frame = gif->prev_frame;
@@ -235,7 +234,7 @@ static gif_result gif__recover_frame(
unsigned width = gif->width < gif->prev_width ? gif->width : gif->prev_width;
if (prev_frame == NULL) {
- return GIF_FRAME_DATA_ERROR;
+ return NSGIF_FRAME_DATA_ERROR;
}
for (unsigned y = 0; y < height; y++) {
@@ -245,7 +244,7 @@ static gif_result gif__recover_frame(
prev_frame += gif->prev_width;
}
- return GIF_OK;
+ return NSGIF_OK;
}
/**
@@ -261,7 +260,7 @@ static gif_result gif__recover_frame(
* \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)
+static inline bool nsgif__deinterlace(uint32_t height, uint32_t *y, uint8_t *step)
{
*y += *step & 0xf;
@@ -290,13 +289,13 @@ static inline bool gif__deinterlace(uint32_t height, uint32_t *y, uint8_t *step)
* \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,
+static inline bool nsgif__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);
+ return nsgif__deinterlace(height, y, step);
}
}
@@ -342,8 +341,8 @@ static inline void gif__jump_data(
*pos += jump;
}
-static gif_result gif__decode_complex(
- struct gif_animation *gif,
+static nsgif_result nsgif__decode_complex(
+ struct nsgif_animation *gif,
uint32_t width,
uint32_t height,
uint32_t offset_x,
@@ -355,10 +354,10 @@ static gif_result gif__decode_complex(
uint32_t *restrict colour_table)
{
lzw_result res;
+ nsgif_result ret = NSGIF_OK;
uint32_t clip_x = gif__clip(offset_x, width, gif->width);
uint32_t clip_y = gif__clip(offset_y, height, gif->height);
const uint8_t *uncompressed;
- gif_result ret = GIF_OK;
uint32_t available = 0;
uint8_t step = 24;
uint32_t skip = 0;
@@ -366,22 +365,22 @@ static gif_result gif__decode_complex(
if (offset_x >= gif->width ||
offset_y >= gif->height) {
- return GIF_OK;
+ return NSGIF_OK;
}
width -= clip_x;
height -= clip_y;
if (width == 0 || height == 0) {
- return GIF_OK;
+ return NSGIF_OK;
}
/* Initialise the LZW decoding */
res = lzw_decode_init(gif->lzw_ctx, data[0],
- gif->gif_data, gif->buffer_size,
- data + 1 - gif->gif_data);
+ gif->nsgif_data, gif->buffer_size,
+ data + 1 - gif->nsgif_data);
if (res != LZW_OK) {
- return gif__error_from_lzw(res);
+ return nsgif__error_from_lzw(res);
}
do {
@@ -398,9 +397,9 @@ static gif_result gif__decode_complex(
if (res != LZW_OK) {
/* Unexpected end of frame, try to recover */
if (res == LZW_OK_EOD) {
- ret = GIF_OK;
+ ret = NSGIF_OK;
} else {
- ret = gif__error_from_lzw(res);
+ ret = nsgif__error_from_lzw(res);
}
return ret;
}
@@ -408,7 +407,7 @@ static gif_result gif__decode_complex(
&uncompressed, &available);
if (available == 0) {
- return GIF_OK;
+ return NSGIF_OK;
}
gif__jump_data(&skip, &available, &uncompressed);
}
@@ -436,13 +435,13 @@ static gif_result gif__decode_complex(
skip = clip_x;
gif__jump_data(&skip, &available, &uncompressed);
- } while (gif__next_row(interlace, height, &y, &step));
+ } while (nsgif__next_row(interlace, height, &y, &step));
return ret;
}
-static gif_result gif__decode_simple(
- struct gif_animation *gif,
+static nsgif_result nsgif__decode_simple(
+ struct nsgif_animation *gif,
uint32_t height,
uint32_t offset_y,
const uint8_t *data,
@@ -452,26 +451,26 @@ static gif_result gif__decode_simple(
{
uint32_t pixels = gif->width * height;
uint32_t written = 0;
- gif_result ret = GIF_OK;
+ nsgif_result ret = NSGIF_OK;
lzw_result res;
if (offset_y >= gif->height) {
- return GIF_OK;
+ return NSGIF_OK;
}
height -= gif__clip(offset_y, height, gif->height);
if (height == 0) {
- return GIF_OK;
+ return NSGIF_OK;
}
/* Initialise the LZW decoding */
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);
+ gif->nsgif_data, gif->buffer_size,
+ data + 1 - gif->nsgif_data);
if (res != LZW_OK) {
- return gif__error_from_lzw(res);
+ return nsgif__error_from_lzw(res);
}
frame_data += (offset_y * gif->width);
@@ -484,42 +483,42 @@ static gif_result gif__decode_simple(
if (res != LZW_OK) {
/* Unexpected end of frame, try to recover */
if (res == LZW_OK_EOD) {
- ret = GIF_OK;
+ ret = NSGIF_OK;
} else {
- ret = gif__error_from_lzw(res);
+ ret = nsgif__error_from_lzw(res);
}
break;
}
}
if (pixels == 0) {
- ret = GIF_OK;
+ ret = NSGIF_OK;
}
return ret;
}
-static inline gif_result gif__decode(
- struct gif_animation *gif,
- struct gif_frame *frame,
+static inline nsgif_result nsgif__decode(
+ struct nsgif_animation *gif,
+ struct nsgif_frame *frame,
const uint8_t *data,
uint32_t *restrict frame_data)
{
- gif_result ret;
+ nsgif_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 interlace = frame->flags & NSGIF_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,
+ ret = nsgif__decode_simple(gif, height, offset_y,
data, transparency_index,
frame_data, colour_table);
} else {
- ret = gif__decode_complex(gif, width, height,
+ ret = nsgif__decode_complex(gif, width, height,
offset_x, offset_y, interlace,
data, transparency_index,
frame_data, colour_table);
@@ -535,13 +534,13 @@ static inline gif_result gif__decode(
* \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,
+static void nsgif__restore_bg(
+ struct nsgif_animation *gif,
+ struct nsgif_frame *frame,
uint32_t *bitmap)
{
if (frame == NULL) {
- memset(bitmap, GIF_TRANSPARENT_COLOUR,
+ memset(bitmap, NSGIF_TRANSPARENT_COLOUR,
gif->width * gif->height * sizeof(*bitmap));
} else {
uint32_t offset_x = frame->redraw_x;
@@ -560,7 +559,7 @@ static void gif__restore_bg(
for (uint32_t y = 0; y < height; y++) {
uint32_t *scanline = bitmap + offset_x +
(offset_y + y) * gif->width;
- memset(scanline, GIF_TRANSPARENT_COLOUR,
+ memset(scanline, NSGIF_TRANSPARENT_COLOUR,
width * sizeof(*bitmap));
}
} else {
@@ -575,55 +574,55 @@ static void gif__restore_bg(
}
}
-static gif_result gif__update_bitmap(
- struct gif_animation *gif,
- struct gif_frame *frame,
+static nsgif_result nsgif__update_bitmap(
+ struct nsgif_animation *gif,
+ struct nsgif_frame *frame,
const uint8_t *data,
uint32_t frame_idx)
{
- gif_result ret;
+ nsgif_result ret;
uint32_t *bitmap;
gif->decoded_frame = frame_idx;
- bitmap = gif__bitmap_get(gif);
+ bitmap = nsgif__bitmap_get(gif);
if (bitmap == NULL) {
- return GIF_INSUFFICIENT_MEMORY;
+ return NSGIF_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);
+ if (frame_idx == 0 || gif->decoded_frame == NSGIF_INVALID_FRAME) {
+ nsgif__restore_bg(gif, NULL, bitmap);
} else {
- struct gif_frame *prev = &gif->frames[frame_idx - 1];
+ struct nsgif_frame *prev = &gif->frames[frame_idx - 1];
- if (prev->disposal_method == GIF_DISPOSAL_RESTORE_BG) {
- gif__restore_bg(gif, prev, bitmap);
+ if (prev->disposal_method == NSGIF_DISPOSAL_RESTORE_BG) {
+ nsgif__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);
+ } else if (prev->disposal_method == NSGIF_DISPOSAL_RESTORE_PREV) {
+ ret = nsgif__recover_frame(gif, bitmap);
+ if (ret != NSGIF_OK) {
+ nsgif__restore_bg(gif, prev, bitmap);
}
}
}
- if (frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
+ if (frame->disposal_method == NSGIF_DISPOSAL_RESTORE_PREV) {
/* Store the previous frame for later restoration */
- gif__record_frame(gif, bitmap);
+ nsgif__record_frame(gif, bitmap);
}
- ret = gif__decode(gif, frame, data, bitmap);
+ ret = nsgif__decode(gif, frame, data, bitmap);
- gif__bitmap_modified(gif);
+ nsgif__bitmap_modified(gif);
if (!frame->decoded) {
- frame->opaque = gif__bitmap_get_opaque(gif);
+ frame->opaque = nsgif__bitmap_get_opaque(gif);
frame->decoded = true;
}
- gif__bitmap_set_opaque(gif, frame);
+ nsgif__bitmap_set_opaque(gif, frame);
return ret;
}
@@ -634,11 +633,11 @@ 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_DATA if more data is needed,
- * GIF_OK for success.
+ * \return NSGIF_INSUFFICIENT_DATA if more data is needed,
+ * NSGIF_OK for success.
*/
-static gif_result gif__parse_extension_graphic_control(
- struct gif_frame *frame,
+static nsgif_result nsgif__parse_extension_graphic_control(
+ struct nsgif_frame *frame,
const uint8_t *data,
size_t len)
{
@@ -655,33 +654,33 @@ static gif_result gif__parse_extension_graphic_control(
* +5 CHAR Transparent Color Index
*/
if (len < 6) {
- return GIF_INSUFFICIENT_DATA;
+ return NSGIF_INSUFFICIENT_DATA;
}
frame->frame_delay = data[3] | (data[4] << 8);
- if (data[2] & GIF_TRANSPARENCY_MASK) {
+ if (data[2] & NSGIF_TRANSPARENCY_MASK) {
frame->transparency = true;
frame->transparency_index = data[5];
}
- frame->disposal_method = ((data[2] & GIF_DISPOSAL_MASK) >> 2);
+ frame->disposal_method = ((data[2] & NSGIF_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 (frame->disposal_method == NSGIF_DISPOSAL_RESTORE_QUIRK) {
+ frame->disposal_method = NSGIF_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;
+ frame->disposal_method == NSGIF_DISPOSAL_RESTORE_BG ||
+ frame->disposal_method == NSGIF_DISPOSAL_RESTORE_PREV;
- return GIF_OK;
+ return NSGIF_OK;
}
/**
@@ -690,11 +689,11 @@ 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_DATA if more data is needed,
- * GIF_OK for success.
+ * \return NSGIF_INSUFFICIENT_DATA if more data is needed,
+ * NSGIF_OK for success.
*/
-static gif_result gif__parse_extension_application(
- struct gif_animation *gif,
+static nsgif_result nsgif__parse_extension_application(
+ struct nsgif_animation *gif,
const uint8_t *data,
size_t len)
{
@@ -707,7 +706,7 @@ static gif_result gif__parse_extension_application(
* +13 1-256 Application Data (Data sub-blocks)
*/
if (len < 17) {
- return GIF_INSUFFICIENT_DATA;
+ return NSGIF_INSUFFICIENT_DATA;
}
if ((data[1] == 0x0b) &&
@@ -716,7 +715,7 @@ static gif_result gif__parse_extension_application(
gif->loop_count = data[15] | (data[16] << 8);
}
- return GIF_OK;
+ return NSGIF_OK;
}
/**
@@ -726,57 +725,57 @@ static gif_result gif__parse_extension_application(
* \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.
+ * \return NSGIF_INSUFFICIENT_DATA if more data is needed,
+ * NSGIF_OK for success.
*/
-static gif_result gif__parse_frame_extensions(
- struct gif_animation *gif,
- struct gif_frame *frame,
+static nsgif_result nsgif__parse_frame_extensions(
+ struct nsgif_animation *gif,
+ struct nsgif_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;
+ const uint8_t *nsgif_data = *pos;
+ const uint8_t *nsgif_end = gif->nsgif_data + gif->buffer_size;
+ int nsgif_bytes = nsgif_end - nsgif_data;
/* Initialise the extensions */
- while (gif_bytes > 0 && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
+ while (nsgif_bytes > 0 && nsgif_data[0] == NSGIF_EXTENSION_INTRODUCER) {
bool block_step = true;
- gif_result ret;
+ nsgif_result ret;
- gif_data++;
- gif_bytes--;
+ nsgif_data++;
+ nsgif_bytes--;
- if (gif_bytes == 0) {
- return GIF_INSUFFICIENT_DATA;
+ if (nsgif_bytes == 0) {
+ return NSGIF_INSUFFICIENT_DATA;
}
/* Switch on extension label */
- switch (gif_data[0]) {
- case GIF_EXTENSION_GRAPHIC_CONTROL:
+ switch (nsgif_data[0]) {
+ case NSGIF_EXTENSION_GRAPHIC_CONTROL:
if (decode) {
- ret = gif__parse_extension_graphic_control(
- frame, gif_data, gif_bytes);
- if (ret != GIF_OK) {
+ ret = nsgif__parse_extension_graphic_control(
+ frame, nsgif_data, nsgif_bytes);
+ if (ret != NSGIF_OK) {
return ret;
}
}
break;
- case GIF_EXTENSION_APPLICATION:
+ case NSGIF_EXTENSION_APPLICATION:
if (decode) {
- ret = gif__parse_extension_application(
- gif, gif_data, gif_bytes);
- if (ret != GIF_OK) {
+ ret = nsgif__parse_extension_application(
+ gif, nsgif_data, nsgif_bytes);
+ if (ret != NSGIF_OK) {
return ret;
}
}
break;
- case GIF_EXTENSION_COMMENT:
+ case NSGIF_EXTENSION_COMMENT:
/* Move the pointer to the first data sub-block Skip 1
* byte for the extension label. */
- ++gif_data;
+ ++nsgif_data;
block_step = false;
break;
@@ -789,31 +788,31 @@ static gif_result gif__parse_frame_extensions(
* bytes for the extension label and size fields Skip
* the extension size itself
*/
- if (gif_bytes < 2) {
- return GIF_INSUFFICIENT_DATA;
+ if (nsgif_bytes < 2) {
+ return NSGIF_INSUFFICIENT_DATA;
}
- gif_data += 2 + gif_data[1];
+ nsgif_data += 2 + nsgif_data[1];
}
/* 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;
+ while (nsgif_data < nsgif_end && nsgif_data[0] != NSGIF_BLOCK_TERMINATOR) {
+ nsgif_data += nsgif_data[0] + 1;
+ if (nsgif_data >= nsgif_end) {
+ return NSGIF_INSUFFICIENT_DATA;
}
}
- gif_data++;
- gif_bytes = gif_end - gif_data;
+ nsgif_data++;
+ nsgif_bytes = nsgif_end - nsgif_data;
}
- if (gif_data > gif_end) {
- gif_data = gif_end;
+ if (nsgif_data > nsgif_end) {
+ nsgif_data = nsgif_end;
}
/* Set buffer position and return */
- *pos = gif_data;
- return GIF_OK;
+ *pos = nsgif_data;
+ return NSGIF_OK;
}
/**
@@ -837,33 +836,33 @@ static gif_result gif__parse_frame_extensions(
* \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.
+ * \return NSGIF_OK on success, appropriate error otherwise.
*/
-static gif_result gif__parse_image_descriptor(
- struct gif_animation *gif,
- struct gif_frame *frame,
+static nsgif_result nsgif__parse_image_descriptor(
+ struct nsgif_animation *gif,
+ struct nsgif_frame *frame,
const uint8_t **pos,
bool decode)
{
const uint8_t *data = *pos;
- size_t len = gif->gif_data + gif->buffer_size - data;
+ size_t len = gif->nsgif_data + gif->buffer_size - data;
enum {
- GIF_IMAGE_DESCRIPTOR_LEN = 10u,
- GIF_IMAGE_SEPARATOR = 0x2Cu,
+ NSGIF_IMAGE_DESCRIPTOR_LEN = 10u,
+ NSGIF_IMAGE_SEPARATOR = 0x2Cu,
};
assert(gif != NULL);
assert(frame != NULL);
- if (len < GIF_IMAGE_DESCRIPTOR_LEN) {
- return GIF_INSUFFICIENT_DATA;
+ if (len < NSGIF_IMAGE_DESCRIPTOR_LEN) {
+ return NSGIF_INSUFFICIENT_DATA;
}
if (decode) {
unsigned x, y, w, h;
- if (data[0] != GIF_IMAGE_SEPARATOR) {
- return GIF_FRAME_DATA_ERROR;
+ if (data[0] != NSGIF_IMAGE_SEPARATOR) {
+ return NSGIF_FRAME_DATA_ERROR;
}
x = data[1] | (data[2] << 8);
@@ -888,8 +887,8 @@ static gif_result gif__parse_image_descriptor(
}
}
- *pos += GIF_IMAGE_DESCRIPTOR_LEN;
- return GIF_OK;
+ *pos += NSGIF_IMAGE_DESCRIPTOR_LEN;
+ return NSGIF_OK;
}
/**
@@ -900,20 +899,20 @@ static gif_result gif__parse_image_descriptor(
* \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.
+ * \return NSGIF_OK on success, appropriate error otherwise.
*/
-static gif_result gif__colour_table_extract(
- struct gif_animation *gif,
+static nsgif_result nsgif__colour_table_extract(
+ struct nsgif_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;
+ size_t len = gif->nsgif_data + gif->buffer_size - data;
if (len < colour_table_entries * 3) {
- return GIF_INSUFFICIENT_DATA;
+ return NSGIF_INSUFFICIENT_DATA;
}
if (decode) {
@@ -937,7 +936,7 @@ static gif_result gif__colour_table_extract(
}
*pos += colour_table_entries * 3;
- return GIF_OK;
+ return NSGIF_OK;
}
/**
@@ -949,33 +948,33 @@ static gif_result gif__colour_table_extract(
* \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.
+ * \return NSGIF_OK on success, appropriate error otherwise.
*/
-static gif_result gif__parse_colour_table(
- struct gif_animation *gif,
- struct gif_frame *frame,
+static nsgif_result nsgif__parse_colour_table(
+ struct nsgif_animation *gif,
+ struct nsgif_frame *frame,
const uint8_t **pos,
bool decode)
{
- gif_result ret;
+ nsgif_result ret;
assert(gif != NULL);
assert(frame != NULL);
- if ((frame->flags & GIF_COLOUR_TABLE_MASK) == 0) {
+ if ((frame->flags & NSGIF_COLOUR_TABLE_MASK) == 0) {
gif->colour_table = gif->global_colour_table;
- return GIF_OK;
+ return NSGIF_OK;
}
- ret = gif__colour_table_extract(gif, gif->local_colour_table,
- 2 << (frame->flags & GIF_COLOUR_TABLE_SIZE_MASK),
+ ret = nsgif__colour_table_extract(gif, gif->local_colour_table,
+ 2 << (frame->flags & NSGIF_COLOUR_TABLE_SIZE_MASK),
pos, decode);
- if (ret != GIF_OK) {
+ if (ret != NSGIF_OK) {
return ret;
}
gif->colour_table = gif->local_colour_table;
- return GIF_OK;
+ return NSGIF_OK;
}
/**
@@ -987,19 +986,19 @@ static gif_result gif__parse_colour_table(
* \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.
+ * \return NSGIF_OK on success, appropriate error otherwise.
*/
-static gif_result gif__parse_image_data(
- struct gif_animation *gif,
- struct gif_frame *frame,
+static nsgif_result nsgif__parse_image_data(
+ struct nsgif_animation *gif,
+ struct nsgif_frame *frame,
const uint8_t **pos,
bool decode)
{
const uint8_t *data = *pos;
- size_t len = gif->gif_data + gif->buffer_size - data;
+ size_t len = gif->nsgif_data + gif->buffer_size - data;
uint32_t frame_idx = frame - gif->frames;
uint8_t minimum_code_size;
- gif_result ret;
+ nsgif_result ret;
assert(gif != NULL);
assert(frame != NULL);
@@ -1012,22 +1011,22 @@ 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;
+ default: if (data[0] == NSGIF_TRAILER) return NSGIF_OK;
break;
- case 2: if (data[1] == GIF_TRAILER) return GIF_OK;
+ case 2: if (data[1] == NSGIF_TRAILER) return NSGIF_OK;
/* Fall through. */
- case 1: if (data[0] == GIF_TRAILER) return GIF_OK;
+ case 1: if (data[0] == NSGIF_TRAILER) return NSGIF_OK;
/* Fall through. */
- case 0: return GIF_INSUFFICIENT_DATA;
+ case 0: return NSGIF_INSUFFICIENT_DATA;
}
minimum_code_size = data[0];
if (minimum_code_size >= LZW_CODE_MAX) {
- return GIF_DATA_ERROR;
+ return NSGIF_DATA_ERROR;
}
if (decode) {
- ret = gif__update_bitmap(gif, frame, data, frame_idx);
+ ret = nsgif__update_bitmap(gif, frame, data, frame_idx);
} else {
uint32_t block_size = 0;
@@ -1036,12 +1035,12 @@ static gif_result gif__parse_image_data(
len--;
while (block_size != 1) {
- if (len < 1) return GIF_INSUFFICIENT_DATA;
+ if (len < 1) return NSGIF_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;
+ return NSGIF_OK;
}
len -= block_size;
@@ -1054,31 +1053,31 @@ static gif_result gif__parse_image_data(
/* Check if we've finished */
if (len < 1) {
- return GIF_INSUFFICIENT_DATA;
+ return NSGIF_INSUFFICIENT_DATA;
} else {
- if (data[0] == GIF_TRAILER) {
- return GIF_OK;
+ if (data[0] == NSGIF_TRAILER) {
+ return NSGIF_OK;
}
}
- return GIF_WORKING;
+ return NSGIF_WORKING;
}
return ret;
}
-static struct gif_frame *gif__get_frame(
- struct gif_animation *gif,
+static struct nsgif_frame *nsgif__get_frame(
+ struct nsgif_animation *gif,
uint32_t frame_idx)
{
- struct gif_frame *frame;
+ struct nsgif_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;
+ struct nsgif_frame *temp;
temp = realloc(gif->frames, count * sizeof(*frame));
if (temp == NULL) {
@@ -1090,7 +1089,7 @@ static struct gif_frame *gif__get_frame(
frame = &gif->frames[frame_idx];
frame->transparency = false;
- frame->transparency_index = GIF_NO_TRANSPARENCY;
+ frame->transparency_index = NSGIF_NO_TRANSPARENCY;
frame->frame_pointer = gif->buffer_position;
frame->redraw_required = false;
frame->disposal_method = 0;
@@ -1109,99 +1108,99 @@ 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 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
+ * - NSGIF_INSUFFICIENT_DATA reached unexpected end of source data.
+ * - NSGIF_FRAME_DATA_ERROR for GIF frame data error
+ * - NSGIF_INSUFFICIENT_MEMORY for insufficient memory to process
+ * - NSGIF_DATA_ERROR for GIF error (invalid frame header)
+ * - NSGIF_OK for successful decoding
+ * - NSGIF_WORKING for successful decoding if more frames are expected
*/
-static gif_result gif__process_frame(
- struct gif_animation *gif,
+static nsgif_result nsgif__process_frame(
+ struct nsgif_animation *gif,
uint32_t frame_idx,
bool decode)
{
- gif_result ret;
+ nsgif_result ret;
const uint8_t *pos;
const uint8_t *end;
- struct gif_frame *frame;
+ struct nsgif_frame *frame;
- frame = gif__get_frame(gif, frame_idx);
+ frame = nsgif__get_frame(gif, frame_idx);
if (frame == NULL) {
- return GIF_INSUFFICIENT_MEMORY;
+ return NSGIF_INSUFFICIENT_MEMORY;
}
- end = gif->gif_data + gif->buffer_size;
+ end = gif->nsgif_data + gif->buffer_size;
if (decode) {
- pos = gif->gif_data + frame->frame_pointer;
+ pos = gif->nsgif_data + frame->frame_pointer;
/* Ensure this frame is supposed to be decoded */
if (frame->display == false) {
- return GIF_OK;
+ return NSGIF_OK;
}
/* Ensure the frame is in range to decode */
if (frame_idx > gif->frame_count_partial) {
- return GIF_INSUFFICIENT_DATA;
+ return NSGIF_INSUFFICIENT_DATA;
}
/* Done if frame is already decoded */
if ((int)frame_idx == gif->decoded_frame) {
- return GIF_OK;
+ return NSGIF_OK;
}
} else {
- pos = (uint8_t *)(gif->gif_data + gif->buffer_position);
+ pos = (uint8_t *)(gif->nsgif_data + gif->buffer_position);
/* Check if we've finished */
- if (pos < end && pos[0] == GIF_TRAILER) {
- return GIF_OK;
+ if (pos < end && pos[0] == NSGIF_TRAILER) {
+ return NSGIF_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;
+ return NSGIF_FRAME_DATA_ERROR;
}
}
/* Initialise any extensions */
- ret = gif__parse_frame_extensions(gif, frame, &pos, !decode);
- if (ret != GIF_OK) {
+ ret = nsgif__parse_frame_extensions(gif, frame, &pos, !decode);
+ if (ret != NSGIF_OK) {
goto cleanup;
}
- ret = gif__parse_image_descriptor(gif, frame, &pos, !decode);
- if (ret != GIF_OK) {
+ ret = nsgif__parse_image_descriptor(gif, frame, &pos, !decode);
+ if (ret != NSGIF_OK) {
goto cleanup;
}
- ret = gif__parse_colour_table(gif, frame, &pos, decode);
- if (ret != GIF_OK) {
+ ret = nsgif__parse_colour_table(gif, frame, &pos, decode);
+ if (ret != NSGIF_OK) {
goto cleanup;
}
- ret = gif__parse_image_data(gif, frame, &pos, decode);
- if (ret != GIF_OK) {
+ ret = nsgif__parse_image_data(gif, frame, &pos, decode);
+ if (ret != NSGIF_OK) {
goto cleanup;
}
cleanup:
if (!decode) {
- gif->buffer_position = pos - gif->gif_data;
+ gif->buffer_position = pos - gif->nsgif_data;
}
return ret;
}
/* exported function documented in libnsgif.h */
-void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks)
+void nsgif_create(nsgif_animation *gif, nsgif_bitmap_cb_vt *bitmap)
{
- memset(gif, 0, sizeof(gif_animation));
- gif->bitmap_callbacks = *bitmap_callbacks;
- gif->decoded_frame = GIF_INVALID_FRAME;
- gif->prev_index = GIF_INVALID_FRAME;
+ memset(gif, 0, sizeof(nsgif_animation));
+ gif->bitmap = *bitmap;
+ gif->decoded_frame = NSGIF_INVALID_FRAME;
+ gif->prev_index = NSGIF_INVALID_FRAME;
}
/**
@@ -1215,35 +1214,35 @@ void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks)
* \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.
+ * \return NSGIF_OK on success, appropriate error otherwise.
*/
-static gif_result gif__parse_header(
- struct gif_animation *gif,
+static nsgif_result nsgif__parse_header(
+ struct nsgif_animation *gif,
const uint8_t **pos,
bool strict)
{
const uint8_t *data = *pos;
- size_t len = gif->gif_data + gif->buffer_size - data;
+ size_t len = gif->nsgif_data + gif->buffer_size - data;
if (len < 6) {
- return GIF_INSUFFICIENT_DATA;
+ return NSGIF_INSUFFICIENT_DATA;
}
if (strncmp((const char *) data, "GIF", 3) != 0) {
- return GIF_DATA_ERROR;
+ return NSGIF_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;
+ return NSGIF_DATA_ERROR;
}
}
data += 3;
*pos = data;
- return GIF_OK;
+ return NSGIF_OK;
}
/**
@@ -1263,43 +1262,43 @@ static gif_result gif__parse_header(
*
* \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.
+ * \return NSGIF_OK on success, appropriate error otherwise.
*/
-static gif_result gif__parse_logical_screen_descriptor(
- struct gif_animation *gif,
+static nsgif_result nsgif__parse_logical_screen_descriptor(
+ struct nsgif_animation *gif,
const uint8_t **pos)
{
const uint8_t *data = *pos;
- size_t len = gif->gif_data + gif->buffer_size - data;
+ size_t len = gif->nsgif_data + gif->buffer_size - data;
if (len < 7) {
- return GIF_INSUFFICIENT_DATA;
+ return NSGIF_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->global_colours = data[4] & NSGIF_COLOUR_TABLE_MASK;
+ gif->colour_table_size = 2 << (data[4] & NSGIF_COLOUR_TABLE_SIZE_MASK);
gif->bg_index = data[5];
gif->aspect_ratio = data[6];
gif->loop_count = 1;
*pos += 7;
- return GIF_OK;
+ return NSGIF_OK;
}
/* exported function documented in libnsgif.h */
-gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
+nsgif_result nsgif_initialise(nsgif_animation *gif, size_t size, const uint8_t *data)
{
- const uint8_t *gif_data;
- gif_result ret;
+ const uint8_t *nsgif_data;
+ nsgif_result ret;
/* Initialize values */
gif->buffer_size = size;
- gif->gif_data = data;
+ gif->nsgif_data = data;
/* Get our current processing position */
- gif_data = gif->gif_data + gif->buffer_position;
+ nsgif_data = gif->nsgif_data + gif->buffer_position;
/* See if we should initialise the GIF */
if (gif->buffer_position == 0) {
@@ -1315,20 +1314,20 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
/* The caller may have been lazy and not reset any values */
gif->frame_count = 0;
gif->frame_count_partial = 0;
- gif->decoded_frame = GIF_INVALID_FRAME;
+ gif->decoded_frame = NSGIF_INVALID_FRAME;
- ret = gif__parse_header(gif, &gif_data, false);
- if (ret != GIF_OK) {
+ ret = nsgif__parse_header(gif, &nsgif_data, false);
+ if (ret != NSGIF_OK) {
return ret;
}
- ret = gif__parse_logical_screen_descriptor(gif, &gif_data);
- if (ret != GIF_OK) {
+ ret = nsgif__parse_logical_screen_descriptor(gif, &nsgif_data);
+ if (ret != NSGIF_OK) {
return ret;
}
/* Remember we've done this now */
- gif->buffer_position = gif_data - gif->gif_data;
+ gif->buffer_position = nsgif_data - gif->nsgif_data;
/* Some broken GIFs report the size as the screen size they
* were created in. As such, we detect for the common cases and
@@ -1352,26 +1351,26 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *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(uint32_t));
- gif->local_colour_table = calloc(GIF_MAX_COLOURS, sizeof(uint32_t));
+ gif->global_colour_table = calloc(NSGIF_MAX_COLOURS, sizeof(uint32_t));
+ gif->local_colour_table = calloc(NSGIF_MAX_COLOURS, sizeof(uint32_t));
if ((gif->global_colour_table == NULL) ||
(gif->local_colour_table == NULL)) {
- gif_finalise(gif);
- return GIF_INSUFFICIENT_MEMORY;
+ nsgif_finalise(gif);
+ return NSGIF_INSUFFICIENT_MEMORY;
}
/* Set the first colour to a value that will never occur in
* reality so we know if we've processed it
*/
- gif->global_colour_table[0] = GIF_PROCESS_COLOURS;
+ gif->global_colour_table[0] = NSGIF_PROCESS_COLOURS;
/* Check if the GIF has no frame data (13-byte header + 1-byte
* termination block) Although generally useless, the GIF
* specification does not expressly prohibit this
*/
if (gif->buffer_size == gif->buffer_position + 1) {
- if (gif_data[0] == GIF_TRAILER) {
- return GIF_OK;
+ if (nsgif_data[0] == NSGIF_TRAILER) {
+ return NSGIF_OK;
}
}
}
@@ -1380,18 +1379,18 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
* 0xff or 0x00 depending on the transparency we know if it's been
* filled in.
*/
- if (gif->global_colour_table[0] == GIF_PROCESS_COLOURS) {
+ if (gif->global_colour_table[0] == NSGIF_PROCESS_COLOURS) {
/* Check for a global colour map signified by bit 7 */
if (gif->global_colours) {
- ret = gif__colour_table_extract(gif,
+ ret = nsgif__colour_table_extract(gif,
gif->global_colour_table,
gif->colour_table_size,
- &gif_data, true);
- if (ret != GIF_OK) {
+ &nsgif_data, true);
+ if (ret != NSGIF_OK) {
return ret;
}
- gif->buffer_position = (gif_data - gif->gif_data);
+ gif->buffer_position = (nsgif_data - gif->nsgif_data);
} else {
/* Create a default colour table with the first two
* colours as black and white
@@ -1418,31 +1417,31 @@ 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 nsgif__error_from_lzw(res);
}
}
/* Repeatedly try to initialise frames */
do {
- ret = gif__process_frame(gif, gif->frame_count, false);
- } while (ret == GIF_WORKING);
+ ret = nsgif__process_frame(gif, gif->frame_count, false);
+ } while (ret == NSGIF_WORKING);
return ret;
}
/* exported function documented in libnsgif.h */
-gif_result gif_decode_frame(gif_animation *gif, uint32_t frame)
+nsgif_result nsgif_decode_frame(nsgif_animation *gif, uint32_t frame)
{
- return gif__process_frame(gif, frame, true);
+ return nsgif__process_frame(gif, frame, true);
}
/* exported function documented in libnsgif.h */
-void gif_finalise(gif_animation *gif)
+void nsgif_finalise(nsgif_animation *gif)
{
/* Release all our memory blocks */
if (gif->frame_image) {
- assert(gif->bitmap_callbacks.bitmap_destroy);
- gif->bitmap_callbacks.bitmap_destroy(gif->frame_image);
+ assert(gif->bitmap.destroy);
+ gif->bitmap.destroy(gif->frame_image);
}
gif->frame_image = NULL;
diff --git a/test/decode_gif.c b/test/decode_gif.c
index dfc2b84..87f0738 100644
--- a/test/decode_gif.c
+++ b/test/decode_gif.c
@@ -101,22 +101,22 @@ static unsigned char *load_file(const char *path, size_t *data_size)
return buffer;
}
-static void warning(const char *context, gif_result code)
+static void warning(const char *context, nsgif_result code)
{
fprintf(stderr, "%s failed: ", context);
switch (code)
{
- case GIF_FRAME_DATA_ERROR:
- fprintf(stderr, "GIF_FRAME_DATA_ERROR");
+ case NSGIF_FRAME_DATA_ERROR:
+ fprintf(stderr, "NSGIF_FRAME_DATA_ERROR");
break;
- case GIF_INSUFFICIENT_DATA:
- fprintf(stderr, "GIF_INSUFFICIENT_DATA");
+ case NSGIF_INSUFFICIENT_DATA:
+ fprintf(stderr, "NSGIF_INSUFFICIENT_DATA");
break;
- case GIF_DATA_ERROR:
- fprintf(stderr, "GIF_DATA_ERROR");
+ case NSGIF_DATA_ERROR:
+ fprintf(stderr, "NSGIF_DATA_ERROR");
break;
- case GIF_INSUFFICIENT_MEMORY:
- fprintf(stderr, "GIF_INSUFFICIENT_MEMORY");
+ case NSGIF_INSUFFICIENT_MEMORY:
+ fprintf(stderr, "NSGIF_INSUFFICIENT_MEMORY");
break;
default:
fprintf(stderr, "unknown code %i", code);
@@ -125,11 +125,11 @@ static void warning(const char *context, gif_result code)
fprintf(stderr, "\n");
}
-static void write_ppm(FILE* fh, const char *name, gif_animation *gif,
+static void write_ppm(FILE* fh, const char *name, nsgif_animation *gif,
bool no_write)
{
unsigned int i;
- gif_result code;
+ nsgif_result code;
if (!no_write) {
fprintf(fh, "P3\n");
@@ -147,9 +147,9 @@ static void write_ppm(FILE* fh, const char *name, gif_animation *gif,
unsigned int row, col;
unsigned char *image;
- code = gif_decode_frame(gif, i);
- if (code != GIF_OK)
- warning("gif_decode_frame", code);
+ code = nsgif_decode_frame(gif, i);
+ if (code != NSGIF_OK)
+ warning("nsgif_decode_frame", code);
if (!gif->frames[i].display) {
continue;
@@ -174,7 +174,7 @@ static void write_ppm(FILE* fh, const char *name, gif_animation *gif,
int main(int argc, char *argv[])
{
- gif_bitmap_callback_vt bitmap_callbacks = {
+ nsgif_bitmap_cb_vt bitmap_callbacks = {
bitmap_create,
bitmap_destroy,
bitmap_get_buffer,
@@ -182,9 +182,9 @@ int main(int argc, char *argv[])
bitmap_test_opaque,
bitmap_modified
};
- gif_animation gif;
+ nsgif_animation gif;
size_t size;
- gif_result code;
+ nsgif_result code;
unsigned char *data;
FILE *outf = stdout;
bool no_write = false;
@@ -213,21 +213,21 @@ int main(int argc, char *argv[])
}
/* create our gif animation */
- gif_create(&gif, &bitmap_callbacks);
+ nsgif_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);
+ code = nsgif_initialise(&gif, size, data);
+ if (code != NSGIF_OK && code != NSGIF_WORKING) {
+ warning("nsgif_initialise", code);
+ nsgif_finalise(&gif);
free(data);
return 1;
}
- } while (code != GIF_OK);
+ } while (code != NSGIF_OK);
write_ppm(outf, argv[1], &gif, no_write);
@@ -236,7 +236,7 @@ int main(int argc, char *argv[])
}
/* clean up */
- gif_finalise(&gif);
+ nsgif_finalise(&gif);
free(data);
return 0;
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=0823ded6d3858a4aca...
commit 0823ded6d3858a4acaaaca57b2a4803a0a5884c8
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Rename flag for whether the frame image has been decoded.
diff --git a/include/libnsgif.h b/include/libnsgif.h
index b281034..6b8d521 100644
--- a/include/libnsgif.h
+++ b/include/libnsgif.h
@@ -43,8 +43,8 @@ typedef struct gif_frame {
/** offset (in bytes) to the GIF frame data */
uint32_t frame_pointer;
- /** whether the frame has previously been used */
- bool virgin;
+ /** whether the frame has previously been decoded. */
+ bool decoded;
/** whether the frame is totally opaque */
bool opaque;
/** whether a full image redraw is required */
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 21867f3..a52268e 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -619,9 +619,9 @@ static gif_result gif__update_bitmap(
gif__bitmap_modified(gif);
- if (frame->virgin) {
+ if (!frame->decoded) {
frame->opaque = gif__bitmap_get_opaque(gif);
- frame->virgin = false;
+ frame->decoded = true;
}
gif__bitmap_set_opaque(gif, frame);
@@ -1096,7 +1096,7 @@ static struct gif_frame *gif__get_frame(
frame->disposal_method = 0;
frame->frame_delay = 100;
frame->display = false;
- frame->virgin = true;
+ frame->decoded = false;
}
return frame;
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=c9703eb11102d27e22...
commit c9703eb11102d27e22f5f5119db111dd1b41f559
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
Buildsystem: Add -Wextra to WARNFLAGS.
diff --git a/Makefile b/Makefile
index c348e20..2ae2406 100644
--- a/Makefile
+++ b/Makefile
@@ -19,7 +19,7 @@ include $(NSSHARED)/makefiles/Makefile.tools
TESTRUNNER = test/runtest.sh $(BUILDDIR) $(EXEEXT)
# Toolchain flags
-WARNFLAGS := -Wall -W -Wundef -Wpointer-arith -Wcast-align \
+WARNFLAGS := -Wall -Wextra -W -Wundef -Wpointer-arith -Wcast-align \
-Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes \
-Wmissing-declarations -Wnested-externs -pedantic
# BeOS/Haiku standard library headers create warnings
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=d15788827c453a1103...
commit d15788827c453a1103b926672b7cdb1f1a21f487
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
gif: Fix graphic control extension handler comment.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 25f98c6..21867f3 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -629,7 +629,7 @@ static gif_result gif__update_bitmap(
}
/**
- * Parse the application extension
+ * Parse the graphic control extension
*
* \param[in] frame The gif object we're decoding.
* \param[in] data The data to decode.
-----------------------------------------------------------------------
Summary of changes:
include/nsgif.h | 266 ++++++++++++++++----------------------
src/gif.c | 373 ++++++++++++++++++++++++++++++++++++++---------------
test/decode_gif.c | 156 +++++++++++-----------
test/runtest.sh | 1 +
4 files changed, 453 insertions(+), 343 deletions(-)
diff --git a/include/nsgif.h b/include/nsgif.h
index 0c95f88..1472842 100644
--- a/include/nsgif.h
+++ b/include/nsgif.h
@@ -22,6 +22,23 @@
#define NSGIF_INFINITE (UINT32_MAX)
+typedef struct nsgif nsgif;
+
+typedef struct nsgif_info {
+ /** width of GIF (may increase during decoding) */
+ uint32_t width;
+ /** height of GIF (may increase during decoding) */
+ uint32_t height;
+ /** number of frames decoded */
+ uint32_t frame_count;
+ /** number of times to loop animation */
+ int loop_max;
+ /** number of animation loops so far */
+ int loop_count;
+
+ uint16_t delay_min;
+} nsgif_info_t;
+
/* Error return values */
typedef enum {
NSGIF_WORKING = 1,
@@ -56,141 +73,83 @@ typedef struct nsgif_rect {
uint32_t y1;
} nsgif_rect;
-/** GIF frame data */
-typedef struct nsgif_frame {
- /** whether the frame should be displayed/animated */
- bool display;
- /** delay (in cs) before animating the frame */
- uint32_t frame_delay;
-
- /* Internal members are listed below */
-
- /** offset (in bytes) to the GIF frame data */
- uint32_t frame_pointer;
- /** whether the frame has previously been decoded. */
- bool decoded;
- /** 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 */
- uint8_t disposal_method;
- /** whether we acknowledge transparency */
- bool transparency;
- /** the index designating a transparent pixel */
- uint32_t transparency_index;
- /* Frame flags */
- uint32_t flags;
-
- /** Frame's redraw rectangle. */
- nsgif_rect redraw;
-} nsgif_frame;
-
-/* API for Bitmap callbacks */
-typedef void* (*nsgif_bitmap_cb_create)(int width, int height);
-typedef void (*nsgif_bitmap_cb_destroy)(void *bitmap);
-typedef uint8_t* (*nsgif_bitmap_cb_get_buffer)(void *bitmap);
-typedef void (*nsgif_bitmap_cb_set_opaque)(void *bitmap, bool opaque);
-typedef bool (*nsgif_bitmap_cb_test_opaque)(void *bitmap);
-typedef void (*nsgif_bitmap_cb_modified)(void *bitmap);
+/**
+ * Client bitmap type.
+ *
+ * These are client-created and destroyed, via the \ref bitmap callbacks,
+ * but they are owned by a \ref nsgif.
+ */
+typedef void nsgif_bitmap_t;
/** Bitmap callbacks function table */
typedef struct nsgif_bitmap_cb_vt {
- /** Create a bitmap. */
- nsgif_bitmap_cb_create create;
- /** Free a bitmap. */
- nsgif_bitmap_cb_destroy destroy;
- /** Return a pointer to the pixel data in a bitmap. */
- nsgif_bitmap_cb_get_buffer get_buffer;
-
- /* Members below are optional */
-
- /** Sets whether a bitmap should be plotted opaque. */
- nsgif_bitmap_cb_set_opaque set_opaque;
- /** Tests whether a bitmap has an opaque alpha channel. */
- nsgif_bitmap_cb_test_opaque test_opaque;
- /** The bitmap image has changed, so flush any persistent cache. */
- nsgif_bitmap_cb_modified modified;
+ /**
+ * Callback to create a bitmap with the given dimensions.
+ *
+ * \param[in] width Required bitmap width in pixels.
+ * \param[in] height Required bitmap height in pixels.
+ * \return pointer to client's bitmap structure or NULL on error.
+ */
+ nsgif_bitmap_t* (*create)(int width, int height);
+
+ /**
+ * Callback to free a bitmap.
+ *
+ * \param[in] bitmap The bitmap to destroy.
+ */
+ void (*destroy)(nsgif_bitmap_t *bitmap);
+
+ /**
+ * Get pointer to pixel buffer in a bitmap.
+ *
+ * The pixel buffer must be `width * height * sizeof(uint32_t)`.
+ *
+ * \param[in] bitmap The bitmap.
+ * \return pointer to bitmap's pixel buffer.
+ */
+ uint8_t* (*get_buffer)(nsgif_bitmap_t *bitmap);
+
+ /* The following functions are optional. */
+
+ /**
+ * Set whether a bitmap can be plotted opaque.
+ *
+ * \param[in] bitmap The bitmap.
+ * \param[in] opaque Whether the current frame is opaque.
+ */
+ void (*set_opaque)(nsgif_bitmap_t *bitmap, bool opaque);
+
+ /**
+ * Tests whether a bitmap has an opaque alpha channel.
+ *
+ * \param[in] bitmap The bitmap.
+ * \return true if the bitmap is opaque, false otherwise.
+ */
+ bool (*test_opaque)(nsgif_bitmap_t *bitmap);
+
+ /**
+ * Bitmap modified notification.
+ *
+ * \param[in] bitmap The bitmap.
+ */
+ void (*modified)(nsgif_bitmap_t *bitmap);
} nsgif_bitmap_cb_vt;
-/** GIF animation data */
-typedef struct nsgif {
- /** LZW decode context */
- void *lzw_ctx;
- /** callbacks for bitmap functions */
- nsgif_bitmap_cb_vt bitmap;
- /** pointer to GIF data */
- const uint8_t *nsgif_data;
- /** width of GIF (may increase during decoding) */
- uint32_t width;
- /** height of GIF (may increase during decoding) */
- uint32_t height;
- /** number of frames decoded */
- uint32_t frame_count;
- /** number of frames partially decoded */
- uint32_t frame_count_partial;
- /** decoded frames */
- nsgif_frame *frames;
- /** current frame */
- uint32_t frame;
- /** current frame decoded to bitmap */
- uint32_t decoded_frame;
- /** currently decoded image; stored as bitmap from bitmap_create callback */
- void *frame_image;
- /** number of animation loops so far */
- int loop_count;
- /** number of times to loop animation */
- int loop_max;
-
- /* Internal members are listed below */
-
- /** current index into GIF data */
- uint32_t buffer_position;
- /** total number of bytes of GIF data available */
- uint32_t buffer_size;
- /** current number of frame holders */
- uint32_t frame_holders;
- /** background index */
- uint32_t bg_index;
- /** background colour */
- uint32_t bg_colour;
- /** image aspect ratio (ignored) */
- uint32_t aspect_ratio;
- /** size of colour table (in entries) */
- uint32_t colour_table_size;
- /** whether the GIF has a global colour table */
- bool global_colours;
- /** global colour table */
- uint32_t *global_colour_table;
- /** local colour table */
- uint32_t *local_colour_table;
- /** current colour table */
- uint32_t *colour_table;
-
- uint16_t delay_min;
- uint16_t delay_default;
-
- /** previous frame for NSGIF_FRAME_RESTORE */
- void *prev_frame;
- /** previous frame index */
- uint32_t prev_index;
- /** previous frame width */
- uint32_t prev_width;
- /** previous frame height */
- uint32_t prev_height;
-} nsgif;
-
/**
- * Initialises necessary nsgif members.
+ * Create the NSGIF object.
+ *
+ * \param[in] bitmap_vt Bitmap operation functions v-table.
+ * \param[out] gif_out Return NSGIF object on success.
+ * \return NSGIF_OK on success, or appropriate error otherwise.
*/
-void nsgif_create(nsgif *gif, nsgif_bitmap_cb_vt *bitmap_callbacks);
+nsgif_result nsgif_create(const nsgif_bitmap_cb_vt *bitmap_vt, nsgif **gif_out);
/**
* Scan the source image data.
*
* This is used to feed the source data into LibNSGIF. This must be called
* before calling \ref nsgif_frame_decode.
- *
+ *
* It can be called multiple times with, with increasing sizes. If it is called
* several times, as more data is available (e.g. slow network fetch) the data
* already given to \ref nsgif_data_scan must be provided each time.
@@ -203,7 +162,7 @@ void nsgif_create(nsgif *gif, nsgif_bitmap_cb_vt *bitmap_callbacks);
*
* If an error occurs, all previously scanned frames are retained.
*
- * \param[in] gif The nsgif object.
+ * \param[in] gif The NSGIF object.
* \param[in] size Number of bytes in data.
* \param[in] data Raw source GIF data.
*
@@ -223,7 +182,7 @@ nsgif_result nsgif_data_scan(
/**
* Prepare to show a frame.
*
- * \param[in] gif The nsgif object.
+ * \param[in] gif The NSGIF object.
* \param[out] area The area in pixels that must be redrawn.
* \param[out] delay_cs Time to wait after frame_new before next frame in cs.
* \param[out] frame_new The frame to decode.
@@ -237,8 +196,10 @@ nsgif_result nsgif_frame_prepare(
/**
* Decodes a GIF frame.
*
- * \param[in] gif The nsgif object.
- * \param[in] frame The frame number to decode.
+ * \param[in] gif The nsgif object.
+ * \param[in] frame The frame number to decode.
+ * \param[out] bitmap On success, returns pointer to the client-allocated,
+ * nsgif-owned client bitmap structure.
* \return Error return value.
* - NSGIF_FRAME_DATA_ERROR for GIF frame data error
* - NSGIF_DATA_ERROR for GIF error (invalid frame header)
@@ -248,39 +209,38 @@ nsgif_result nsgif_frame_prepare(
*/
nsgif_result nsgif_frame_decode(
nsgif *gif,
- uint32_t frame);
+ uint32_t frame,
+ nsgif_bitmap_t **bitmap);
+/**
+ * Reset a GIF animation.
+ *
+ * Some animations are only meant to loop N times, and then show the
+ * final frame forever. This function resets the loop and frame counters,
+ * so that the animation can be replayed without the overhead of recreating
+ * the NSGIF object and rescanning the raw data.
+ *
+ * \param[in] gif A NSGIF object.
+ *
+ * \return NSGIF_OK on success, or appropriate error otherwise.
+ */
nsgif_result nsgif_reset(
nsgif *gif);
-/*
-
-get next frame
- 1. could wrap back to 0
- 2. could be no next frame
- 3. could be an un-displayable frame
- 4. could be an un-displayable frame at the end
- 5. could be a frame we don't have enough data for
- 6. could think we don't have enough data, but actually it's the last anyway
-
-want to be able to get next frame if there is one
-want to know if there isn't because more data is needed
-want to be able to know if we have all frames
-
-1. nsgif_data_scan(gif, data, size, &frames);
-2. nsgif_frame_prepare(gif, 0, &redraw_area, &delay_cs);
-
-render:
-3. nsgif_decode(gif, 0);
-
-animate:
-4. nsgif_frame_prepare(gif, N, &redraw_area, &delay_cs);
-
-*/
+/**
+ * Get information about a GIF from an NSGIF object.
+ *
+ * \param[in] gif The NSGIF object to get info for.
+ *
+ * \return The gif info, or NULL on error.
+ */
+const nsgif_info_t *nsgif_get_info(const nsgif *gif);
/**
- * Releases any workspace held by a gif
+ * Free a NSGIF object.
+ *
+ * \param[in] gif The NSGIF to free.
*/
-void nsgif_finalise(nsgif *gif);
+void nsgif_destroy(nsgif *gif);
#endif
diff --git a/src/gif.c b/src/gif.c
index 4019062..5ed38d8 100644
--- a/src/gif.c
+++ b/src/gif.c
@@ -17,6 +17,92 @@
#include "lzw.h"
#include "nsgif.h"
+/** Maximum colour table size */
+#define NSGIF_MAX_COLOURS 256
+
+/** GIF frame data */
+typedef struct nsgif_frame {
+ /** whether the frame should be displayed/animated */
+ bool display;
+ /** delay (in cs) before animating the frame */
+ uint32_t frame_delay;
+
+ /* Internal members are listed below */
+
+ /** offset (in bytes) to the GIF frame data */
+ uint32_t frame_pointer;
+ /** whether the frame has previously been decoded. */
+ bool decoded;
+ /** 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 */
+ uint8_t disposal_method;
+ /** whether we acknowledge transparency */
+ bool transparency;
+ /** the index designating a transparent pixel */
+ uint32_t transparency_index;
+ /* Frame flags */
+ uint32_t flags;
+
+ /** Frame's redraw rectangle. */
+ nsgif_rect redraw;
+} nsgif_frame;
+
+/** GIF animation data */
+struct nsgif {
+ struct nsgif_info info;
+
+ /** LZW decode context */
+ void *lzw_ctx;
+ /** callbacks for bitmap functions */
+ nsgif_bitmap_cb_vt bitmap;
+ /** pointer to GIF data */
+ const uint8_t *nsgif_data;
+ /** decoded frames */
+ nsgif_frame *frames;
+ /** current frame */
+ uint32_t frame;
+ /** current frame decoded to bitmap */
+ uint32_t decoded_frame;
+ /** currently decoded image; stored as bitmap from bitmap_create callback */
+ nsgif_bitmap_t *frame_image;
+
+ uint16_t delay_default;
+ /** number of frames partially decoded */
+ uint32_t frame_count_partial;
+
+ /** current index into GIF data */
+ uint32_t buffer_position;
+ /** total number of bytes of GIF data available */
+ uint32_t buffer_size;
+ /** current number of frame holders */
+ uint32_t frame_holders;
+ /** background index */
+ uint32_t bg_index;
+ /** background colour */
+ uint32_t bg_colour;
+ /** image aspect ratio (ignored) */
+ uint32_t aspect_ratio;
+ /** size of colour table (in entries) */
+ uint32_t colour_table_size;
+
+ /** whether the GIF has a global colour table */
+ bool global_colours;
+ /** current colour table */
+ uint32_t *colour_table;
+ /** global colour table */
+ uint32_t global_colour_table[NSGIF_MAX_COLOURS];
+ /** local colour table */
+ uint32_t local_colour_table[NSGIF_MAX_COLOURS];
+
+ /** previous frame for NSGIF_FRAME_RESTORE */
+ void *prev_frame;
+ /** previous frame index */
+ uint32_t prev_index;
+};
+
/**
*
* \file
@@ -28,9 +114,6 @@
* \todo Plain text and comment extensions should be implemented.
*/
-/** Maximum colour table size */
-#define NSGIF_MAX_COLOURS 256
-
/** Internal flag that the colour table needs to be processed */
#define NSGIF_PROCESS_COLOURS 0xaa000000
@@ -118,7 +201,7 @@ static inline uint32_t* nsgif__bitmap_get(
nsgif_result ret;
/* Make sure we have a buffer to decode to. */
- ret = nsgif__initialise_sprite(gif, gif->width, gif->height);
+ ret = nsgif__initialise_sprite(gif, gif->info.width, gif->info.height);
if (ret != NSGIF_OK) {
return NULL;
}
@@ -180,7 +263,6 @@ static void nsgif__record_frame(
struct nsgif *gif,
const uint32_t *bitmap)
{
- bool need_alloc = gif->prev_frame == NULL;
uint32_t *prev_frame;
if (gif->decoded_frame == NSGIF_FRAME_INVALID ||
@@ -194,14 +276,9 @@ static void nsgif__record_frame(
return;
}
- if (gif->prev_frame != NULL &&
- gif->width * gif->height > gif->prev_width * gif->prev_height) {
- need_alloc = true;
- }
-
- if (need_alloc) {
+ if (gif->prev_frame == NULL) {
prev_frame = realloc(gif->prev_frame,
- gif->width * gif->height * 4);
+ gif->info.width * gif->info.height * 4);
if (prev_frame == NULL) {
return;
}
@@ -209,11 +286,9 @@ static void nsgif__record_frame(
prev_frame = gif->prev_frame;
}
- memcpy(prev_frame, bitmap, gif->width * gif->height * 4);
+ memcpy(prev_frame, bitmap, gif->info.width * gif->info.height * 4);
gif->prev_frame = prev_frame;
- gif->prev_width = gif->width;
- gif->prev_height = gif->height;
gif->prev_index = gif->decoded_frame;
}
@@ -222,19 +297,14 @@ static nsgif_result nsgif__recover_frame(
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;
+ unsigned height = gif->info.height;
+ unsigned width = gif->info.width;
if (prev_frame == NULL) {
return NSGIF_FRAME_DATA_ERROR;
}
- for (unsigned y = 0; y < height; y++) {
- memcpy(bitmap, prev_frame, width * 4);
-
- bitmap += gif->width;
- prev_frame += gif->prev_width;
- }
+ memcpy(bitmap, prev_frame, height * width * sizeof(*bitmap));
return NSGIF_OK;
}
@@ -291,6 +361,48 @@ static inline bool nsgif__next_row(uint32_t interlace,
}
}
+/**
+ * Get any frame clip adjustment for the image extent.
+ *
+ * \param[in] frame_off Frame's X or Y offset.
+ * \param[in] frame_dim Frame width or height.
+ * \param[in] image_ext Image width or height constraint.
+ * \return the amount the frame needs to be clipped to fit the image in given
+ * dimension.
+ */
+static inline uint32_t gif__clip(
+ uint32_t frame_off,
+ uint32_t frame_dim,
+ uint32_t image_ext)
+{
+ uint32_t frame_ext = frame_off + frame_dim;
+
+ if (frame_ext <= image_ext) {
+ return 0;
+ }
+
+ return frame_ext - image_ext;
+}
+
+/**
+ * Perform any jump over decoded data, to accommodate clipped portion of frame.
+ *
+ * \param[in,out] skip Number of pixels of data to jump.
+ * \param[in,out] available Number of pixels of data currently available.
+ * \param[in,out] pos Position in decoded pixel value data.
+ */
+static inline void gif__jump_data(
+ uint32_t *skip,
+ uint32_t *available,
+ const uint8_t **pos)
+{
+ uint32_t jump = (*skip < *available) ? *skip : *available;
+
+ *skip -= jump;
+ *available -= jump;
+ *pos += jump;
+}
+
static nsgif_result nsgif__decode_complex(
struct nsgif *gif,
uint32_t width,
@@ -305,11 +417,23 @@ static nsgif_result nsgif__decode_complex(
{
lzw_result res;
nsgif_result ret = NSGIF_OK;
+ uint32_t clip_x = gif__clip(offset_x, width, gif->info.width);
+ uint32_t clip_y = gif__clip(offset_y, height, gif->info.height);
+ const uint8_t *uncompressed;
uint32_t available = 0;
uint8_t step = 24;
+ uint32_t skip = 0;
uint32_t y = 0;
- if (height == 0) {
+ if (offset_x >= gif->info.width ||
+ offset_y >= gif->info.height) {
+ return NSGIF_OK;
+ }
+
+ width -= clip_x;
+ height -= clip_y;
+
+ if (width == 0 || height == 0) {
return NSGIF_OK;
}
@@ -326,13 +450,12 @@ static nsgif_result nsgif__decode_complex(
uint32_t *frame_scanline;
frame_scanline = frame_data + offset_x +
- (y + offset_y) * gif->width;
+ (y + offset_y) * gif->info.width;
x = width;
while (x > 0) {
- const uint8_t *uncompressed;
unsigned row_available;
- if (available == 0) {
+ while (available == 0) {
if (res != LZW_OK) {
/* Unexpected end of frame, try to recover */
if (res == LZW_OK_EOD) {
@@ -340,10 +463,15 @@ static nsgif_result nsgif__decode_complex(
} else {
ret = nsgif__error_from_lzw(res);
}
- break;
+ return ret;
}
res = lzw_decode(gif->lzw_ctx,
&uncompressed, &available);
+
+ if (available == 0) {
+ return NSGIF_OK;
+ }
+ gif__jump_data(&skip, &available, &uncompressed);
}
row_available = x < available ? x : available;
@@ -366,6 +494,9 @@ static nsgif_result nsgif__decode_complex(
}
}
}
+
+ skip = clip_x;
+ gif__jump_data(&skip, &available, &uncompressed);
} while (nsgif__next_row(interlace, height, &y, &step));
return ret;
@@ -380,11 +511,21 @@ static nsgif_result nsgif__decode_simple(
uint32_t *restrict frame_data,
uint32_t *restrict colour_table)
{
- uint32_t pixels = gif->width * height;
+ uint32_t pixels = gif->info.width * height;
uint32_t written = 0;
nsgif_result ret = NSGIF_OK;
lzw_result res;
+ if (offset_y >= gif->info.height) {
+ return NSGIF_OK;
+ }
+
+ height -= gif__clip(offset_y, height, gif->info.height);
+
+ if (height == 0) {
+ return NSGIF_OK;
+ }
+
/* Initialise the LZW decoding */
res = lzw_decode_init_map(gif->lzw_ctx, data[0],
transparency_index, colour_table,
@@ -394,7 +535,7 @@ static nsgif_result nsgif__decode_simple(
return nsgif__error_from_lzw(res);
}
- frame_data += (offset_y * gif->width);
+ frame_data += (offset_y * gif->info.width);
while (pixels > 0) {
res = lzw_decode_map(gif->lzw_ctx,
@@ -438,7 +579,7 @@ static inline nsgif_result nsgif__decode(
uint32_t transparency_index = frame->transparency_index;
uint32_t *restrict colour_table = gif->colour_table;
- if (interlace == false && width == gif->width && offset_x == 0) {
+ if (interlace == false && width == gif->info.width && offset_x == 0) {
ret = nsgif__decode_simple(gif, height, offset_y,
data, transparency_index,
frame_data, colour_table);
@@ -466,28 +607,31 @@ static void nsgif__restore_bg(
{
if (frame == NULL) {
memset(bitmap, NSGIF_TRANSPARENT_COLOUR,
- gif->width * gif->height * sizeof(*bitmap));
+ gif->info.width * gif->info.height * sizeof(*bitmap));
} else {
uint32_t width = frame->redraw.x1 - frame->redraw.x0;
uint32_t height = frame->redraw.y1 - frame->redraw.y0;
uint32_t offset_x = frame->redraw.x0;
uint32_t offset_y = frame->redraw.y0;
- if (frame->display == false) {
+ width -= gif__clip(offset_x, width, gif->info.width);
+ height -= gif__clip(offset_y, height, gif->info.height);
+
+ if (frame->display == false || width == 0) {
return;
}
if (frame->transparency) {
for (uint32_t y = 0; y < height; y++) {
uint32_t *scanline = bitmap + offset_x +
- (offset_y + y) * gif->width;
+ (offset_y + y) * gif->info.width;
memset(scanline, NSGIF_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;
+ (offset_y + y) * gif->info.width;
for (uint32_t x = 0; x < width; x++) {
scanline[x] = gif->bg_colour;
}
@@ -552,7 +696,7 @@ static nsgif_result nsgif__update_bitmap(
/**
* Parse the graphic control extension
*
- * \param[in] gif The gif object we're decoding.
+ * \param[in] gif The gif object we're decoding.
* \param[in] frame The gif object we're decoding.
* \param[in] data The data to decode.
* \param[in] len Byte length of data.
@@ -587,7 +731,7 @@ static nsgif_result nsgif__parse_extension_graphic_control(
}
frame->frame_delay = data[3] | (data[4] << 8);
- if (frame->frame_delay < gif->delay_min) {
+ if (frame->frame_delay < gif->info.delay_min) {
frame->frame_delay = gif->delay_default;
}
@@ -645,7 +789,7 @@ static nsgif_result nsgif__parse_extension_application(
if ((data[1] == 0x0b) &&
(strncmp((const char *)data + 2, "NETSCAPE2.0", 11) == 0) &&
(data[13] == 0x03) && (data[14] == 0x01)) {
- gif->loop_max = data[15] | (data[16] << 8);
+ gif->info.loop_max = data[15] | (data[16] << 8);
}
return NSGIF_OK;
@@ -818,9 +962,15 @@ static nsgif_result nsgif__parse_image_descriptor(
frame->redraw.x1 = x + w;
frame->redraw.y1 = y + 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;
+ /* Allow first frame to grow image dimensions. */
+ if (gif->info.frame_count == 0) {
+ if (x + w > gif->info.width) {
+ gif->info.width = x + w;
+ }
+ if (y + h > gif->info.height) {
+ gif->info.height = y + h;
+ }
+ }
}
*pos += NSGIF_IMAGE_DESCRIPTOR_LEN;
@@ -839,7 +989,7 @@ static nsgif_result nsgif__parse_image_descriptor(
*/
static nsgif_result nsgif__colour_table_extract(
struct nsgif *gif,
- uint32_t *colour_table,
+ uint32_t colour_table[NSGIF_MAX_COLOURS],
size_t colour_table_entries,
const uint8_t **pos,
bool decode)
@@ -983,7 +1133,7 @@ static nsgif_result nsgif__parse_image_data(
data += block_size;
}
- gif->frame_count = frame_idx + 1;
+ gif->info.frame_count = frame_idx + 1;
gif->frames[frame_idx].display = true;
*pos = data;
@@ -1130,15 +1280,50 @@ cleanup:
}
/* exported function documented in nsgif.h */
-void nsgif_create(nsgif *gif, nsgif_bitmap_cb_vt *bitmap)
+void nsgif_destroy(nsgif *gif)
+{
+ if (gif == NULL) {
+ return;
+ }
+
+ /* Release all our memory blocks */
+ if (gif->frame_image) {
+ assert(gif->bitmap.destroy);
+ gif->bitmap.destroy(gif->frame_image);
+ gif->frame_image = NULL;
+ }
+
+ free(gif->frames);
+ gif->frames = NULL;
+
+ free(gif->prev_frame);
+ gif->prev_frame = NULL;
+
+ lzw_context_destroy(gif->lzw_ctx);
+ gif->lzw_ctx = NULL;
+
+ free(gif);
+}
+
+/* exported function documented in nsgif.h */
+nsgif_result nsgif_create(const nsgif_bitmap_cb_vt *bitmap_vt, nsgif **gif_out)
{
- memset(gif, 0, sizeof(nsgif));
- gif->bitmap = *bitmap;
+ nsgif *gif;
+
+ gif = calloc(1, sizeof(*gif));
+ if (gif == NULL) {
+ return NSGIF_INSUFFICIENT_MEMORY;
+ }
+
+ gif->bitmap = *bitmap_vt;
gif->decoded_frame = NSGIF_FRAME_INVALID;
gif->prev_index = NSGIF_FRAME_INVALID;
- gif->delay_min = 2;
+ gif->info.delay_min = 2;
gif->delay_default = 10;
+
+ *gif_out = gif;
+ return NSGIF_OK;
}
/**
@@ -1213,13 +1398,13 @@ static nsgif_result nsgif__parse_logical_screen_descriptor(
return NSGIF_INSUFFICIENT_DATA;
}
- gif->width = data[0] | (data[1] << 8);
- gif->height = data[2] | (data[3] << 8);
+ gif->info.width = data[0] | (data[1] << 8);
+ gif->info.height = data[2] | (data[3] << 8);
gif->global_colours = data[4] & NSGIF_COLOUR_TABLE_MASK;
gif->colour_table_size = 2 << (data[4] & NSGIF_COLOUR_TABLE_SIZE_MASK);
gif->bg_index = data[5];
gif->aspect_ratio = data[6];
- gif->loop_max = 1;
+ gif->info.loop_max = 1;
*pos += 7;
return NSGIF_OK;
@@ -1249,11 +1434,9 @@ nsgif_result nsgif_data_scan(
gif->frame_image = NULL;
gif->frames = NULL;
gif->frame_holders = 0;
- gif->local_colour_table = NULL;
- gif->global_colour_table = NULL;
/* The caller may have been lazy and not reset any values */
- gif->frame_count = 0;
+ gif->info.frame_count = 0;
gif->frame_count_partial = 0;
gif->decoded_frame = NSGIF_FRAME_INVALID;
gif->frame = NSGIF_FRAME_INVALID;
@@ -1276,29 +1459,16 @@ nsgif_result nsgif_data_scan(
* set the sizes as 0 if they are found which results in the
* GIF being the maximum size of the frames.
*/
- if (((gif->width == 640) && (gif->height == 480)) ||
- ((gif->width == 640) && (gif->height == 512)) ||
- ((gif->width == 800) && (gif->height == 600)) ||
- ((gif->width == 1024) && (gif->height == 768)) ||
- ((gif->width == 1280) && (gif->height == 1024)) ||
- ((gif->width == 1600) && (gif->height == 1200)) ||
- ((gif->width == 0) || (gif->height == 0)) ||
- ((gif->width > 2048) || (gif->height > 2048))) {
- gif->width = 1;
- gif->height = 1;
- }
-
- /* Allocate some data irrespective of whether we've got any
- * colour tables. We always get the maximum size in case a GIF
- * 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(NSGIF_MAX_COLOURS, sizeof(uint32_t));
- gif->local_colour_table = calloc(NSGIF_MAX_COLOURS, sizeof(uint32_t));
- if ((gif->global_colour_table == NULL) ||
- (gif->local_colour_table == NULL)) {
- nsgif_finalise(gif);
- return NSGIF_INSUFFICIENT_MEMORY;
+ if (((gif->info.width == 640) && (gif->info.height == 480)) ||
+ ((gif->info.width == 640) && (gif->info.height == 512)) ||
+ ((gif->info.width == 800) && (gif->info.height == 600)) ||
+ ((gif->info.width == 1024) && (gif->info.height == 768)) ||
+ ((gif->info.width == 1280) && (gif->info.height == 1024)) ||
+ ((gif->info.width == 1600) && (gif->info.height == 1200)) ||
+ ((gif->info.width == 0) || (gif->info.height == 0)) ||
+ ((gif->info.width > 2048) || (gif->info.height > 2048))) {
+ gif->info.width = 1;
+ gif->info.height = 1;
}
/* Set the first colour to a value that will never occur in
@@ -1365,7 +1535,7 @@ nsgif_result nsgif_data_scan(
/* Repeatedly try to initialise frames */
do {
- ret = nsgif__process_frame(gif, gif->frame_count, false);
+ ret = nsgif__process_frame(gif, gif->info.frame_count, false);
} while (ret == NSGIF_WORKING);
return ret;
@@ -1398,7 +1568,7 @@ static uint32_t nsgif__frame_next(
{
uint32_t frames = partial ?
gif->frame_count_partial :
- gif->frame_count;
+ gif->info.frame_count;
if (frames == 0) {
return NSGIF_FRAME_INVALID;
@@ -1443,7 +1613,7 @@ static inline bool nsgif__animation_complete(int count, int max)
nsgif_result nsgif_reset(
nsgif *gif)
{
- gif->loop_count = 0;
+ gif->info.loop_count = 0;
gif->frame = NSGIF_FRAME_INVALID;
return NSGIF_OK;
@@ -1467,12 +1637,14 @@ nsgif_result nsgif_frame_prepare(
if (gif->frame != NSGIF_FRAME_INVALID &&
gif->frame != 0 &&
- gif->frame < gif->frame_count &&
+ gif->frame < gif->info.frame_count &&
gif->frames[gif->frame].display) {
rect = gif->frames[gif->frame].redraw;
}
- if (nsgif__animation_complete(gif->loop_count, gif->loop_max)) {
+ if (nsgif__animation_complete(
+ gif->info.loop_count,
+ gif->info.loop_max)) {
return NSGIF_ANIMATION_COMPLETE;
}
@@ -1482,7 +1654,7 @@ nsgif_result nsgif_frame_prepare(
}
if (gif->frame != NSGIF_FRAME_INVALID && frame < gif->frame) {
- gif->loop_count++;
+ gif->info.loop_count++;
}
frame_next = frame;
@@ -1493,8 +1665,8 @@ nsgif_result nsgif_frame_prepare(
if (frame_next < frame) {
if (nsgif__animation_complete(
- gif->loop_count + 1,
- gif->loop_max)) {
+ gif->info.loop_count + 1,
+ gif->info.loop_max)) {
delay = NSGIF_INFINITE;
}
}
@@ -1510,12 +1682,16 @@ nsgif_result nsgif_frame_prepare(
}
/* exported function documented in nsgif.h */
-nsgif_result nsgif_frame_decode(nsgif *gif, uint32_t frame)
+nsgif_result nsgif_frame_decode(
+ nsgif *gif,
+ uint32_t frame,
+ nsgif_bitmap_t **bitmap)
{
uint32_t start_frame;
nsgif_result ret = NSGIF_OK;
if (gif->decoded_frame == frame) {
+ *bitmap = gif->frame_image;
return NSGIF_OK;
} else if (gif->decoded_frame >= frame ||
@@ -1523,7 +1699,8 @@ nsgif_result nsgif_frame_decode(nsgif *gif, uint32_t frame)
/* Can skip to first frame or restart. */
start_frame = 0;
} else {
- start_frame = nsgif__frame_next(gif, false, gif->decoded_frame);
+ start_frame = nsgif__frame_next(
+ gif, false, gif->decoded_frame);
}
for (uint32_t f = start_frame; f <= frame; f++) {
@@ -1533,29 +1710,11 @@ nsgif_result nsgif_frame_decode(nsgif *gif, uint32_t frame)
}
}
+ *bitmap = gif->frame_image;
return ret;
}
-/* exported function documented in nsgif.h */
-void nsgif_finalise(nsgif *gif)
+const nsgif_info_t *nsgif_get_info(const nsgif *gif)
{
- /* Release all our memory blocks */
- if (gif->frame_image) {
- assert(gif->bitmap.destroy);
- gif->bitmap.destroy(gif->frame_image);
- }
-
- gif->frame_image = NULL;
- free(gif->frames);
- gif->frames = NULL;
- free(gif->local_colour_table);
- gif->local_colour_table = NULL;
- free(gif->global_colour_table);
- gif->global_colour_table = NULL;
-
- free(gif->prev_frame);
- gif->prev_frame = NULL;
-
- lzw_context_destroy(gif->lzw_ctx);
- gif->lzw_ctx = NULL;
+ return &gif->info;
}
diff --git a/test/decode_gif.c b/test/decode_gif.c
index 853363e..acc2ef0 100644
--- a/test/decode_gif.c
+++ b/test/decode_gif.c
@@ -18,51 +18,28 @@
#include "../include/nsgif.h"
#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)) {
+ /* Ensure a stupidly large bitmap is not created */
+ if (width > 4096 || height > 4096) {
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);
-}
-static bool bitmap_test_opaque(void *bitmap)
-{
- (void) bitmap; /* unused */
- assert(bitmap);
- return false;
+ return calloc(width * height, BYTES_PER_PIXEL);
}
static unsigned char *bitmap_get_buffer(void *bitmap)
{
- assert(bitmap);
return bitmap;
}
static void bitmap_destroy(void *bitmap)
{
- assert(bitmap);
free(bitmap);
}
-static void bitmap_modified(void *bitmap)
-{
- (void) bitmap; /* unused */
- assert(bitmap);
- return;
-}
-
-static unsigned char *load_file(const char *path, size_t *data_size)
+static uint8_t *load_file(const char *path, size_t *data_size)
{
FILE *fd;
struct stat sb;
@@ -101,11 +78,10 @@ static unsigned char *load_file(const char *path, size_t *data_size)
return buffer;
}
-static void warning(const char *context, nsgif_result code)
+static void warning(const char *context, nsgif_result gif_res)
{
fprintf(stderr, "%s failed: ", context);
- switch (code)
- {
+ switch (gif_res) {
case NSGIF_FRAME_DATA_ERROR:
fprintf(stderr, "NSGIF_FRAME_DATA_ERROR");
break;
@@ -119,52 +95,69 @@ static void warning(const char *context, nsgif_result code)
fprintf(stderr, "NSGIF_INSUFFICIENT_MEMORY");
break;
default:
- fprintf(stderr, "unknown code %i", code);
+ fprintf(stderr, "unknown code %i", gif_res);
break;
}
fprintf(stderr, "\n");
}
-static void write_ppm(FILE* fh, const char *name, nsgif *gif, uint32_t frames,
- bool no_write)
+static void decode(FILE* fh, const char *name, nsgif *gif, bool write_ppm)
{
- unsigned int i;
- nsgif_result code;
+ nsgif_result gif_res;
+ uint32_t frame_prev = 0;
+ const nsgif_info_t *info;
+
+ info = nsgif_get_info(gif);
- if (!no_write) {
+ if (write_ppm) {
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", frames);
- 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);
+ fprintf(fh, "# width %u \n", info->width);
+ fprintf(fh, "# height %u \n", info->height);
+ fprintf(fh, "# frame_count %u \n", info->frame_count);
+ fprintf(fh, "# loop_max %u \n", info->loop_max);
+ fprintf(fh, "%u %u 256\n", info->width,
+ info->height * info->frame_count);
}
/* decode the frames */
- for (i = 0; i != frames; i++) {
- unsigned int row, col;
- unsigned char *image;
+ while (true) {
+ nsgif_bitmap_t *buffer;
+ const uint8_t *image;
+ uint32_t frame_new;
+ uint32_t delay_cs;
+ nsgif_rect area;
+
+ gif_res = nsgif_frame_prepare(gif, &area,
+ &delay_cs, &frame_new);
+ if (gif_res != NSGIF_OK) {
+ warning("nsgif_frame_prepare", gif_res);
+ return;
+ }
- code = nsgif_frame_decode(gif, i);
- if (code != NSGIF_OK)
- warning("nsgif_decode_frame", code);
+ if (frame_new < frame_prev) {
+ /* Must be an animation that loops. We only care about
+ * decoding each frame once. */
+ return;
+ }
+ frame_prev = frame_new;
- if (!gif->frames[i].display) {
- continue;
+ gif_res = nsgif_frame_decode(gif, frame_new, &buffer);
+ if (gif_res != NSGIF_OK) {
+ warning("nsgif_decode_frame", gif_res);
+ return;
}
- 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;
+ if (write_ppm) {
+ fprintf(fh, "# frame %u:\n", frame_new);
+ image = (const uint8_t *) buffer;
+ for (uint32_t y = 0; y != info->height; y++) {
+ for (uint32_t x = 0; x != info->width; x++) {
+ size_t z = (y * info->width + x) * 4;
fprintf(fh, "%u %u %u ",
- (unsigned char) image[z],
- (unsigned char) image[z + 1],
- (unsigned char) image[z + 2]);
+ image[z],
+ image[z + 1],
+ image[z + 2]);
}
fprintf(fh, "\n");
}
@@ -174,20 +167,16 @@ static void write_ppm(FILE* fh, const char *name, nsgif *gif, uint32_t frames,
int main(int argc, char *argv[])
{
- nsgif_bitmap_cb_vt bitmap_callbacks = {
- bitmap_create,
- bitmap_destroy,
- bitmap_get_buffer,
- bitmap_set_opaque,
- bitmap_test_opaque,
- bitmap_modified
+ const nsgif_bitmap_cb_vt bitmap_callbacks = {
+ .create = bitmap_create,
+ .destroy = bitmap_destroy,
+ .get_buffer = bitmap_get_buffer,
};
- nsgif gif;
+ nsgif *gif;
size_t size;
- uint32_t frames;
- nsgif_result code;
- unsigned char *data;
+ uint8_t *data;
FILE *outf = stdout;
+ nsgif_result gif_res;
bool no_write = false;
if (argc < 2) {
@@ -214,30 +203,31 @@ int main(int argc, char *argv[])
}
/* create our gif animation */
- nsgif_create(&gif, &bitmap_callbacks);
+ gif_res = nsgif_create(&bitmap_callbacks, &gif);
+ if (gif_res != NSGIF_OK) {
+ return 1;
+ }
/* load file into memory */
data = load_file(argv[1], &size);
- /* begin decoding */
- do {
- code = nsgif_data_scan(&gif, size, data, &frames);
- if (code != NSGIF_OK && code != NSGIF_WORKING) {
- warning("nsgif_data_scan", code);
- nsgif_finalise(&gif);
- free(data);
- return 1;
- }
- } while (code != NSGIF_OK);
+ /* Scan the raw data */
+ gif_res = nsgif_data_scan(gif, size, data);
+ if (gif_res != NSGIF_OK) {
+ warning("nsgif_data_scan", gif_res);
+ nsgif_destroy(gif);
+ free(data);
+ return 1;
+ }
- write_ppm(outf, argv[1], &gif, frames, no_write);
+ decode(outf, argv[1], gif, !no_write);
if (argc > 2 && !no_write) {
fclose(outf);
}
/* clean up */
- nsgif_finalise(&gif);
+ nsgif_destroy(gif);
free(data);
return 0;
diff --git a/test/runtest.sh b/test/runtest.sh
index 06eaef4..05665a1 100755
--- a/test/runtest.sh
+++ b/test/runtest.sh
@@ -50,6 +50,7 @@ echo "Testing GIF decode"
for GIF in $(ls ${GIFTESTS});do
GIFTESTTOTC=$((GIFTESTTOTC+1))
+ #echo "${GIF}"
gifdecode ${GIF}
ECODE=$?
if [ "${ECODE}" -gt 127 ];then
--
NetSurf GIF Decoder
1 year, 9 months
libnsgif: branch master updated. release/0.2.1-120-g6d39a8f
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/libnsgif.git/shortlog/6d39a8f70009c43032d9...
...commit http://git.netsurf-browser.org/libnsgif.git/commit/6d39a8f70009c43032d96d...
...tree http://git.netsurf-browser.org/libnsgif.git/tree/6d39a8f70009c43032d96da8...
The branch, master has been updated
via 6d39a8f70009c43032d96da8ceb8934e6e67a159 (commit)
via 696b29ffeb71d77f9ede7dd19408cf713923684a (commit)
via 152c4a7429d9364ab163997dd6304665f5baa2e5 (commit)
from 3832f7edc4cd117642ad7890eb1476d1f9d8e918 (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=6d39a8f70009c43032...
commit 6d39a8f70009c43032d96da8ceb8934e6e67a159
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
gif: Frame parse: Only grow image to accommodate first frame.
This matches the behaviour of other browsers.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 2b53492..25f98c6 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -877,9 +877,15 @@ static gif_result gif__parse_image_descriptor(
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;
+ /* Allow first frame to grow image dimensions. */
+ if (gif->frame_count == 0) {
+ if (x + w > gif->width) {
+ gif->width = x + w;
+ }
+ if (y + h > gif->height) {
+ gif->height = y + h;
+ }
+ }
}
*pos += GIF_IMAGE_DESCRIPTOR_LEN;
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=696b29ffeb71d77f9e...
commit 696b29ffeb71d77f9ede7dd19408cf713923684a
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
gif: Background restoration: Add support for clipped frames.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 2a5ff8d..2b53492 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -549,7 +549,10 @@ static void gif__restore_bg(
uint32_t width = frame->redraw_width;
uint32_t height = frame->redraw_height;
- if (frame->display == false) {
+ width -= gif__clip(offset_x, width, gif->width);
+ height -= gif__clip(offset_y, height, gif->height);
+
+ if (frame->display == false || width == 0) {
return;
}
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=152c4a7429d9364ab1...
commit 152c4a7429d9364ab163997dd6304665f5baa2e5
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
gif: decode: Add support for clipped frames.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index ecddf78..2a5ff8d 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -300,6 +300,48 @@ static inline bool gif__next_row(uint32_t interlace,
}
}
+/**
+ * Get any frame clip adjustment for the image extent.
+ *
+ * \param[in] frame_off Frame's X or Y offset.
+ * \param[in] frame_dim Frame width or height.
+ * \param[in] image_ext Image width or height constraint.
+ * \return the amount the frame needs to be clipped to fit the image in given
+ * dimension.
+ */
+static inline uint32_t gif__clip(
+ uint32_t frame_off,
+ uint32_t frame_dim,
+ uint32_t image_ext)
+{
+ uint32_t frame_ext = frame_off + frame_dim;
+
+ if (frame_ext <= image_ext) {
+ return 0;
+ }
+
+ return frame_ext - image_ext;
+}
+
+/**
+ * Perform any jump over decoded data, to accommodate clipped portion of frame.
+ *
+ * \param[in,out] skip Number of pixels of data to jump.
+ * \param[in,out] available Number of pixels of data currently available.
+ * \param[in,out] pos Position in decoded pixel value data.
+ */
+static inline void gif__jump_data(
+ uint32_t *skip,
+ uint32_t *available,
+ const uint8_t **pos)
+{
+ uint32_t jump = (*skip < *available) ? *skip : *available;
+
+ *skip -= jump;
+ *available -= jump;
+ *pos += jump;
+}
+
static gif_result gif__decode_complex(
struct gif_animation *gif,
uint32_t width,
@@ -313,12 +355,24 @@ static gif_result gif__decode_complex(
uint32_t *restrict colour_table)
{
lzw_result res;
+ uint32_t clip_x = gif__clip(offset_x, width, gif->width);
+ uint32_t clip_y = gif__clip(offset_y, height, gif->height);
+ const uint8_t *uncompressed;
gif_result ret = GIF_OK;
uint32_t available = 0;
uint8_t step = 24;
+ uint32_t skip = 0;
uint32_t y = 0;
- if (height == 0) {
+ if (offset_x >= gif->width ||
+ offset_y >= gif->height) {
+ return GIF_OK;
+ }
+
+ width -= clip_x;
+ height -= clip_y;
+
+ if (width == 0 || height == 0) {
return GIF_OK;
}
@@ -339,9 +393,8 @@ static gif_result gif__decode_complex(
x = width;
while (x > 0) {
- const uint8_t *uncompressed;
unsigned row_available;
- if (available == 0) {
+ while (available == 0) {
if (res != LZW_OK) {
/* Unexpected end of frame, try to recover */
if (res == LZW_OK_EOD) {
@@ -349,10 +402,15 @@ static gif_result gif__decode_complex(
} else {
ret = gif__error_from_lzw(res);
}
- break;
+ return ret;
}
res = lzw_decode(gif->lzw_ctx,
&uncompressed, &available);
+
+ if (available == 0) {
+ return GIF_OK;
+ }
+ gif__jump_data(&skip, &available, &uncompressed);
}
row_available = x < available ? x : available;
@@ -375,6 +433,9 @@ static gif_result gif__decode_complex(
}
}
}
+
+ skip = clip_x;
+ gif__jump_data(&skip, &available, &uncompressed);
} while (gif__next_row(interlace, height, &y, &step));
return ret;
@@ -394,6 +455,16 @@ static gif_result gif__decode_simple(
gif_result ret = GIF_OK;
lzw_result res;
+ if (offset_y >= gif->height) {
+ return GIF_OK;
+ }
+
+ height -= gif__clip(offset_y, height, gif->height);
+
+ if (height == 0) {
+ return GIF_OK;
+ }
+
/* Initialise the LZW decoding */
res = lzw_decode_init_map(gif->lzw_ctx, data[0],
transparency_index, colour_table,
-----------------------------------------------------------------------
Summary of changes:
src/libnsgif.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 88 insertions(+), 8 deletions(-)
diff --git a/src/libnsgif.c b/src/libnsgif.c
index ecddf78..25f98c6 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -300,6 +300,48 @@ static inline bool gif__next_row(uint32_t interlace,
}
}
+/**
+ * Get any frame clip adjustment for the image extent.
+ *
+ * \param[in] frame_off Frame's X or Y offset.
+ * \param[in] frame_dim Frame width or height.
+ * \param[in] image_ext Image width or height constraint.
+ * \return the amount the frame needs to be clipped to fit the image in given
+ * dimension.
+ */
+static inline uint32_t gif__clip(
+ uint32_t frame_off,
+ uint32_t frame_dim,
+ uint32_t image_ext)
+{
+ uint32_t frame_ext = frame_off + frame_dim;
+
+ if (frame_ext <= image_ext) {
+ return 0;
+ }
+
+ return frame_ext - image_ext;
+}
+
+/**
+ * Perform any jump over decoded data, to accommodate clipped portion of frame.
+ *
+ * \param[in,out] skip Number of pixels of data to jump.
+ * \param[in,out] available Number of pixels of data currently available.
+ * \param[in,out] pos Position in decoded pixel value data.
+ */
+static inline void gif__jump_data(
+ uint32_t *skip,
+ uint32_t *available,
+ const uint8_t **pos)
+{
+ uint32_t jump = (*skip < *available) ? *skip : *available;
+
+ *skip -= jump;
+ *available -= jump;
+ *pos += jump;
+}
+
static gif_result gif__decode_complex(
struct gif_animation *gif,
uint32_t width,
@@ -313,12 +355,24 @@ static gif_result gif__decode_complex(
uint32_t *restrict colour_table)
{
lzw_result res;
+ uint32_t clip_x = gif__clip(offset_x, width, gif->width);
+ uint32_t clip_y = gif__clip(offset_y, height, gif->height);
+ const uint8_t *uncompressed;
gif_result ret = GIF_OK;
uint32_t available = 0;
uint8_t step = 24;
+ uint32_t skip = 0;
uint32_t y = 0;
- if (height == 0) {
+ if (offset_x >= gif->width ||
+ offset_y >= gif->height) {
+ return GIF_OK;
+ }
+
+ width -= clip_x;
+ height -= clip_y;
+
+ if (width == 0 || height == 0) {
return GIF_OK;
}
@@ -339,9 +393,8 @@ static gif_result gif__decode_complex(
x = width;
while (x > 0) {
- const uint8_t *uncompressed;
unsigned row_available;
- if (available == 0) {
+ while (available == 0) {
if (res != LZW_OK) {
/* Unexpected end of frame, try to recover */
if (res == LZW_OK_EOD) {
@@ -349,10 +402,15 @@ static gif_result gif__decode_complex(
} else {
ret = gif__error_from_lzw(res);
}
- break;
+ return ret;
}
res = lzw_decode(gif->lzw_ctx,
&uncompressed, &available);
+
+ if (available == 0) {
+ return GIF_OK;
+ }
+ gif__jump_data(&skip, &available, &uncompressed);
}
row_available = x < available ? x : available;
@@ -375,6 +433,9 @@ static gif_result gif__decode_complex(
}
}
}
+
+ skip = clip_x;
+ gif__jump_data(&skip, &available, &uncompressed);
} while (gif__next_row(interlace, height, &y, &step));
return ret;
@@ -394,6 +455,16 @@ static gif_result gif__decode_simple(
gif_result ret = GIF_OK;
lzw_result res;
+ if (offset_y >= gif->height) {
+ return GIF_OK;
+ }
+
+ height -= gif__clip(offset_y, height, gif->height);
+
+ if (height == 0) {
+ return GIF_OK;
+ }
+
/* Initialise the LZW decoding */
res = lzw_decode_init_map(gif->lzw_ctx, data[0],
transparency_index, colour_table,
@@ -478,7 +549,10 @@ static void gif__restore_bg(
uint32_t width = frame->redraw_width;
uint32_t height = frame->redraw_height;
- if (frame->display == false) {
+ width -= gif__clip(offset_x, width, gif->width);
+ height -= gif__clip(offset_y, height, gif->height);
+
+ if (frame->display == false || width == 0) {
return;
}
@@ -803,9 +877,15 @@ static gif_result gif__parse_image_descriptor(
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;
+ /* Allow first frame to grow image dimensions. */
+ if (gif->frame_count == 0) {
+ if (x + w > gif->width) {
+ gif->width = x + w;
+ }
+ if (y + h > gif->height) {
+ gif->height = y + h;
+ }
+ }
}
*pos += GIF_IMAGE_DESCRIPTOR_LEN;
--
NetSurf GIF Decoder
1 year, 9 months