summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Drake <tlsa@netsurf-browser.org>2022-11-03 17:35:24 +0000
committerMichael Drake <tlsa@netsurf-browser.org>2022-11-04 11:28:49 +0000
commitf429a4f329cfaebc3e6644cac9610b89c74d9ba2 (patch)
tree300a287db6f94801fdaa536a4a32554408e39d86
parentcb7f357b58c85bc089221060aa57a6c4ea5e9c06 (diff)
downloadlibnsgif-f429a4f329cfaebc3e6644cac9610b89c74d9ba2.tar.gz
libnsgif-f429a4f329cfaebc3e6644cac9610b89c74d9ba2.tar.bz2
API: Add call to indicate input data is complete
By distinguising between waiting for more data and a broken truncated file, we can decode what we can of any final truncated frame.
-rw-r--r--include/nsgif.h29
-rw-r--r--src/gif.c97
2 files changed, 107 insertions, 19 deletions
diff --git a/include/nsgif.h b/include/nsgif.h
index 208bc27..7e6aa83 100644
--- a/include/nsgif.h
+++ b/include/nsgif.h
@@ -90,6 +90,11 @@ typedef enum {
NSGIF_ERR_END_OF_DATA,
/**
+ * Can't supply more data after calling \ref nsgif_data_complete.
+ */
+ NSGIF_ERR_DATA_COMPLETE,
+
+ /**
* The current frame cannot be displayed.
*/
NSGIF_ERR_FRAME_DISPLAY,
@@ -277,6 +282,8 @@ void nsgif_destroy(nsgif_t *gif);
* 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.
*
+ * Once all the data has been provided, call \ref nsgif_data_complete.
+ *
* 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_scan with a
* size of 35 bytes, and the whole 35 bytes must be contiguous memory. It is
@@ -300,12 +307,34 @@ nsgif_error nsgif_data_scan(
const uint8_t *data);
/**
+ * Tell libnsgif that all the gif data has been provided.
+ *
+ * Call this after calling \ref nsgif_data_scan with the the entire GIF
+ * source data. You can call \ref nsgif_data_scan multiple times up until
+ * this is called, and after this is called, \ref nsgif_data_scan will
+ * return an error.
+ *
+ * You can decode a GIF before this is called, however, it will fail to
+ * decode any truncated final frame data and will not perform loops when
+ * driven via \ref nsgif_frame_prepare (because it doesn't know if there
+ * will be more frames supplied in future data).
+ *
+ * \param[in] gif The \ref nsgif_t object.
+ */
+void nsgif_data_complete(
+ nsgif_t *gif);
+
+/**
* Prepare to show a frame.
*
* If this is the last frame of an animation with a finite loop count, the
* returned `delay_cs` will be \ref NSGIF_INFINITE, indicating that the frame
* should be shown forever.
*
+ * Note that if \ref nsgif_data_complete has not been called on this gif,
+ * animated GIFs will not loop back to the start. Instead it will return
+ * \ref NSGIF_ERR_END_OF_DATA.
+ *
* \param[in] gif The \ref nsgif_t 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.
diff --git a/src/gif.c b/src/gif.c
index a5c44ee..2dc631e 100644
--- a/src/gif.c
+++ b/src/gif.c
@@ -40,6 +40,9 @@ typedef struct nsgif_frame {
/** whether a full image redraw is required */
bool redraw_required;
+ /** Amount of LZW data found in scan */
+ uint32_t lzw_data_length;
+
/** the index designating a transparent pixel */
uint32_t transparency_index;
@@ -90,6 +93,12 @@ struct nsgif {
/** number of frames partially decoded */
uint32_t frame_count_partial;
+ /**
+ * Whether all the GIF data has been supplied, or if there may be
+ * more to come.
+ */
+ bool data_complete;
+
/** pointer to GIF data */
const uint8_t *buf;
/** current index into GIF data */
@@ -613,6 +622,11 @@ static inline nsgif_error nsgif__decode(
frame_data, colour_table);
}
+ if (gif->data_complete && ret == NSGIF_ERR_END_OF_DATA) {
+ /* This is all the data there is, so make do. */
+ ret = NSGIF_OK;
+ }
+
return ret;
}
@@ -1213,16 +1227,19 @@ static nsgif_error nsgif__parse_image_data(
len--;
while (block_size != 1) {
- if (len < 1) return NSGIF_ERR_END_OF_DATA;
+ if (len < 1) {
+ return NSGIF_ERR_END_OF_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;
+ frame->lzw_data_length += len;
return NSGIF_OK;
}
len -= block_size;
data += block_size;
+ frame->lzw_data_length += block_size;
}
*pos = data;
@@ -1258,14 +1275,16 @@ static struct nsgif_frame *nsgif__get_frame(
frame = &gif->frames[frame_idx];
- frame->transparency_index = NSGIF_NO_TRANSPARENCY;
- frame->frame_offset = gif->buf_pos;
frame->info.local_palette = false;
frame->info.transparency = false;
- frame->redraw_required = false;
frame->info.display = false;
frame->info.disposal = 0;
frame->info.delay = 10;
+
+ frame->transparency_index = NSGIF_NO_TRANSPARENCY;
+ frame->frame_offset = gif->buf_pos;
+ frame->redraw_required = false;
+ frame->lzw_data_length = 0;
frame->decoded = false;
}
@@ -1593,6 +1612,10 @@ nsgif_error nsgif_data_scan(
nsgif_error ret;
uint32_t frames;
+ if (gif->data_complete) {
+ return NSGIF_ERR_DATA_COMPLETE;
+ }
+
/* Initialize values */
gif->buf_len = size;
gif->buf = data;
@@ -1734,6 +1757,32 @@ nsgif_error nsgif_data_scan(
return ret;
}
+/* exported function documented in nsgif.h */
+void nsgif_data_complete(
+ nsgif_t *gif)
+{
+ if (gif->data_complete == false) {
+ uint32_t start = gif->info.frame_count;
+ uint32_t end = gif->frame_count_partial;
+
+ for (uint32_t f = start; f < end; f++) {
+ nsgif_frame *frame = &gif->frames[f];
+
+ if (frame->lzw_data_length > 0) {
+ frame->info.display = true;
+ gif->info.frame_count = f + 1;
+
+ if (f == 0) {
+ frame->info.transparency = true;
+ }
+ break;
+ }
+ }
+ }
+
+ gif->data_complete = true;
+}
+
static void nsgif__redraw_rect_extend(
const nsgif_rect_t *frame,
nsgif_rect_t *redraw)
@@ -1782,7 +1831,11 @@ static nsgif_error nsgif__next_displayable_frame(
do {
next = nsgif__frame_next(gif, false, next);
- if (next == *frame || next == NSGIF_FRAME_INVALID) {
+ if (next <= *frame && *frame != NSGIF_FRAME_INVALID &&
+ gif->data_complete == false) {
+ return NSGIF_ERR_END_OF_DATA;
+
+ } else if (next == *frame || next == NSGIF_FRAME_INVALID) {
return NSGIF_ERR_FRAME_DISPLAY;
}
@@ -1850,21 +1903,26 @@ nsgif_error nsgif_frame_prepare(
gif->loop_count++;
}
- if (gif->info.frame_count == 1) {
- delay = NSGIF_INFINITE;
+ if (gif->data_complete) {
+ /* Check for last frame, which has infinite delay. */
- } else if (gif->info.loop_max != 0) {
- uint32_t frame_next = frame;
- ret = nsgif__next_displayable_frame(gif, &frame_next, NULL);
- if (ret != NSGIF_OK) {
- return ret;
- }
+ if (gif->info.frame_count == 1) {
+ delay = NSGIF_INFINITE;
+ } else if (gif->info.loop_max != 0) {
+ uint32_t frame_next = frame;
- if (frame_next < frame) {
- if (nsgif__animation_complete(
- gif->loop_count + 1,
- gif->info.loop_max)) {
- delay = NSGIF_INFINITE;
+ ret = nsgif__next_displayable_frame(gif,
+ &frame_next, NULL);
+ if (ret != NSGIF_OK) {
+ return ret;
+ }
+
+ if (gif->data_complete && frame_next < frame) {
+ if (nsgif__animation_complete(
+ gif->loop_count + 1,
+ gif->info.loop_max)) {
+ delay = NSGIF_INFINITE;
+ }
}
}
}
@@ -1986,6 +2044,7 @@ const char *nsgif_strerror(nsgif_error err)
[NSGIF_ERR_DATA_FRAME] = "Invalid frame data",
[NSGIF_ERR_FRAME_COUNT] = "Excessive number of frames",
[NSGIF_ERR_END_OF_DATA] = "Unexpected end of GIF source data",
+ [NSGIF_ERR_DATA_COMPLETE] = "Can't add data to completed GIF",
[NSGIF_ERR_FRAME_DISPLAY] = "Frame can't be displayed",
[NSGIF_ERR_ANIMATION_END] = "Animation complete",
};