Gitweb links:
...log
http://git.netsurf-browser.org/libnsgif.git/shortlog/5e0642811e2edce8d884...
...commit
http://git.netsurf-browser.org/libnsgif.git/commit/5e0642811e2edce8d8845f...
...tree
http://git.netsurf-browser.org/libnsgif.git/tree/5e0642811e2edce8d8845fdf...
The branch, tlsa/rewrite has been updated
discards f2c94c53e1a05e11bd5efda79748f847f24c7f7d (commit)
discards f19df341f8a3f3ec5a319fd4621b4a8f53699dcb (commit)
discards ec55ce31acd8aa8b2601db2a74e5b1725fa61da2 (commit)
discards 170edd84170a8de5801dcf5c022a562374f83b2f (commit)
via 5e0642811e2edce8d8845fdf0bcb8f9eefc71f80 (commit)
via 9f5e2f1af86d35247895dd57bf03f32c5539a04d (commit)
via 5e93ea19730e0df42e9dc3a777cde466f5a8f73d (commit)
via 920ba6c423181ee0eec932c5daeda75fb13fc140 (commit)
via 1b8341cb075731cffbf69141c24d8954fef4ee50 (commit)
via 8a5130c5559989c3b9ea6f74469427abe08c7193 (commit)
via 82d8eff773bbb56cd2021187949e39fcc28899c7 (commit)
via 677ba295b37cc1671f40383f92ad133fe6db26d9 (commit)
This update added new revisions after undoing existing revisions. That is
to say, the old revision is not a strict subset of the new revision. This
situation occurs when you --force push a change and generate a repository
containing something like this:
* -- * -- B -- O -- O -- O (f2c94c53e1a05e11bd5efda79748f847f24c7f7d)
\
N -- N -- N (5e0642811e2edce8d8845fdf0bcb8f9eefc71f80)
When this happens we assume that you've already had alert emails for all
of the O revisions, and so we here report only the revisions in the N
branch from the common base, B.
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff
http://git.netsurf-browser.org/libnsgif.git/commit/?id=5e0642811e2edce8d8...
commit 5e0642811e2edce8d8845fdf0bcb8f9eefc71f80
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Use same function for frame initialisation and decoding.
Now there aren't two entirely separate code paths that must be
kept in sync.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index ce13041..2a1fdfe 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -967,6 +967,7 @@ static struct gif_frame *gif__get_frame(
*
* \param[in] gif The animation context
* \param[in] frame_idx The frame number to decode.
+ * \param[in] decode Whether to decode the graphical image data.
* \return error code
* - GIF_INSUFFICIENT_DATA for insufficient data to do anything
* - GIF_FRAME_DATA_ERROR for GIF frame data error
@@ -976,127 +977,85 @@ static struct gif_frame *gif__get_frame(
* - GIF_OK for successful decoding
* - GIF_WORKING for successful decoding if more frames are expected
*/
-static gif_result gif_initialise_frame(
+static gif_result gif__process_frame(
struct gif_animation *gif,
- uint32_t frame_idx)
+ uint32_t frame_idx,
+ bool decode)
{
uint8_t *pos;
uint8_t *end;
gif_result ret;
struct gif_frame *frame;
- /* Get our buffer position etc. */
- pos = (uint8_t *)(gif->gif_data + gif->buffer_position);
- end = (uint8_t *)(gif->gif_data + gif->buffer_size);
-
- /* Check if we've finished */
- if (pos < end && pos[0] == GIF_TRAILER) {
- return GIF_OK;
- }
-
- /* We could theoretically get some junk data that gives us millions of
- * frames, so we ensure that we don't have a silly number
- */
- if (frame_idx > 4096) {
- return GIF_FRAME_DATA_ERROR;
- }
-
frame = gif__get_frame(gif, frame_idx);
if (frame == NULL) {
return GIF_INSUFFICIENT_MEMORY;
}
- /* Initialise any extensions */
- ret = gif__parse_frame_extensions(gif, frame, &pos, true);
- if (ret != GIF_OK) {
- goto cleanup;
- }
-
- ret = gif__parse_image_descriptor(gif, frame, &pos, true);
- if (ret != GIF_OK) {
- goto cleanup;
- }
-
- ret = gif__parse_colour_table(gif, frame, &pos, false);
- if (ret != GIF_OK) {
- goto cleanup;
- }
-
- ret = gif__parse_image_data(gif, frame, &pos, false);
- if (ret != GIF_OK) {
- goto cleanup;
- }
-
-cleanup:
- gif->buffer_position = pos - gif->gif_data;
+ end = (uint8_t *)(gif->gif_data + gif->buffer_size);
- return ret;
-}
+ if (decode) {
+ pos = gif->gif_data + frame->frame_pointer;
-/**
- * decode a gif frame
- *
- * \param gif gif animation context.
- * \param frame The frame number to decode.
- * \param clear_image flag for image data being cleared instead of plotted.
- */
-static gif_result gif_internal_decode_frame(
- struct gif_animation *gif,
- uint32_t frame_idx)
-{
- uint8_t *pos;
- gif_result ret;
- struct gif_frame *frame;
+ /* Ensure this frame is supposed to be decoded */
+ if (frame->display == false) {
+ return GIF_OK;
+ }
- /* Ensure the frame is in range to decode */
- if (frame_idx > gif->frame_count_partial) {
- return GIF_INSUFFICIENT_DATA;
- }
+ /* Ensure the frame is in range to decode */
+ if (frame_idx > gif->frame_count_partial) {
+ return GIF_INSUFFICIENT_DATA;
+ }
- /* Done if frame is already decoded */
- if (((int)frame_idx == gif->decoded_frame)) {
- return GIF_OK;
- }
+ /* Done if frame is already decoded */
+ if ((int)frame_idx == gif->decoded_frame) {
+ return GIF_OK;
+ }
+ } else {
+ pos = (uint8_t *)(gif->gif_data + gif->buffer_position);
- frame = gif__get_frame(gif, frame_idx);
- if (frame == NULL) {
- return GIF_INSUFFICIENT_MEMORY;
- }
+ /* Check if we've finished */
+ if (pos < end && pos[0] == GIF_TRAILER) {
+ return GIF_OK;
+ }
- /* Ensure this frame is supposed to be decoded */
- if (frame->display == false) {
- return GIF_OK;
+ /* We could theoretically get some junk data that gives us
+ * millions of frames, so we ensure that we don't have a
+ * silly number. */
+ if (frame_idx > 4096) {
+ return GIF_FRAME_DATA_ERROR;
+ }
}
- /* Get the start of our frame data and the end of the GIF data. */
- pos = gif->gif_data + frame->frame_pointer;
-
- /* Skip any extensions because they have already been processed */
- ret = gif__parse_frame_extensions(gif, frame, &pos, false);
+ /* Initialise any extensions */
+ ret = gif__parse_frame_extensions(gif, frame, &pos, !decode);
if (ret != GIF_OK) {
goto cleanup;
}
- ret = gif__parse_image_descriptor(gif, frame, &pos, false);
+ ret = gif__parse_image_descriptor(gif, frame, &pos, !decode);
if (ret != GIF_OK) {
goto cleanup;
}
- ret = gif__parse_colour_table(gif, frame, &pos, true);
+ ret = gif__parse_colour_table(gif, frame, &pos, decode);
if (ret != GIF_OK) {
goto cleanup;
}
- ret = gif__parse_image_data(gif, frame, &pos, true);
+ ret = gif__parse_image_data(gif, frame, &pos, decode);
if (ret != GIF_OK) {
goto cleanup;
}
cleanup:
+ if (!decode) {
+ gif->buffer_position = pos - gif->gif_data;
+ }
+
return ret;
}
-
/* exported function documented in libnsgif.h */
void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks)
{
@@ -1294,7 +1253,7 @@ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned
char *data)
}
/* Repeatedly try to initialise frames */
- while ((ret = gif_initialise_frame(gif, gif->frame_count)) == GIF_WORKING);
+ while ((ret = gif__process_frame(gif, gif->frame_count, false)) == GIF_WORKING);
/* If there was a memory error tell the caller */
if ((ret == GIF_INSUFFICIENT_MEMORY) ||
@@ -1317,7 +1276,7 @@ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned
char *data)
/* exported function documented in libnsgif.h */
gif_result gif_decode_frame(gif_animation *gif, unsigned int frame)
{
- return gif_internal_decode_frame(gif, frame);
+ return gif__process_frame(gif, frame, true);
}
commitdiff
http://git.netsurf-browser.org/libnsgif.git/commit/?id=9f5e2f1af86d352478...
commit 9f5e2f1af86d35247895dd57bf03f32c5539a04d
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Pass data position into the decoders.
This avoids storing the state on in the gif structure, and having
to save and restore it when processing frames.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index ce500a9..ce13041 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -271,7 +271,7 @@ static gif_result gif__decode_complex(
uint32_t offset_x,
uint32_t offset_y,
uint32_t interlace,
- uint8_t minimum_code_size,
+ const uint8_t *data,
uint32_t transparency_index,
uint32_t *restrict frame_data,
uint32_t *restrict colour_table)
@@ -281,8 +281,9 @@ static gif_result gif__decode_complex(
lzw_result res;
/* Initialise the LZW decoding */
- res = lzw_decode_init(gif->lzw_ctx, minimum_code_size,
- gif->gif_data, gif->buffer_size, gif->buffer_position);
+ res = lzw_decode_init(gif->lzw_ctx, data[0],
+ gif->gif_data, gif->buffer_size,
+ data + 1 - gif->gif_data);
if (res != LZW_OK) {
return gif_error_from_lzw(res);
}
@@ -345,7 +346,7 @@ static gif_result gif__decode_simple(
struct gif_animation *gif,
uint32_t height,
uint32_t offset_y,
- uint8_t minimum_code_size,
+ const uint8_t *data,
uint32_t transparency_index,
uint32_t *restrict frame_data,
uint32_t *restrict colour_table)
@@ -356,9 +357,10 @@ static gif_result gif__decode_simple(
lzw_result res;
/* Initialise the LZW decoding */
- res = lzw_decode_init_map(gif->lzw_ctx,
- minimum_code_size, transparency_index, colour_table,
- gif->gif_data, gif->buffer_size, gif->buffer_position);
+ res = lzw_decode_init_map(gif->lzw_ctx, data[0],
+ transparency_index, colour_table,
+ gif->gif_data, gif->buffer_size,
+ data + 1 - gif->gif_data);
if (res != LZW_OK) {
return gif_error_from_lzw(res);
}
@@ -391,7 +393,7 @@ static gif_result gif__decode_simple(
static inline gif_result gif__decode(
struct gif_animation *gif,
struct gif_frame *frame,
- uint8_t minimum_code_size,
+ const uint8_t *data,
uint32_t *restrict frame_data)
{
gif_result ret;
@@ -405,12 +407,12 @@ static inline gif_result gif__decode(
if (interlace == false && width == gif->width && offset_x == 0) {
ret = gif__decode_simple(gif, height, offset_y,
- minimum_code_size, transparency_index,
+ data, transparency_index,
frame_data, colour_table);
} else {
ret = gif__decode_complex(gif, width, height,
offset_x, offset_y, interlace,
- minimum_code_size, transparency_index,
+ data, transparency_index,
frame_data, colour_table);
}
@@ -464,7 +466,7 @@ static void gif__restore_bg(
static gif_result gif__update_bitmap(
struct gif_animation *gif,
struct gif_frame *frame,
- uint8_t minimum_code_size,
+ const uint8_t *data,
uint32_t frame_idx)
{
gif_result ret;
@@ -501,7 +503,7 @@ static gif_result gif__update_bitmap(
gif__record_frame(gif, bitmap);
}
- ret = gif__decode(gif, frame, minimum_code_size, bitmap);
+ ret = gif__decode(gif, frame, data, bitmap);
gif__bitmap_modified(gif);
@@ -617,15 +619,12 @@ static gif_result gif__parse_extension_application(
static gif_result gif__parse_frame_extensions(
struct gif_animation *gif,
struct gif_frame *frame,
+ uint8_t **pos,
bool decode)
{
- uint8_t *gif_data, *gif_end;
- int gif_bytes;
-
- /* Get our buffer position etc. */
- gif_data = gif->gif_data + gif->buffer_position;
- gif_end = gif->gif_data + gif->buffer_size;
- gif_bytes = gif_end - gif_data;
+ uint8_t *gif_data = *pos;
+ uint8_t *gif_end = gif->gif_data + gif->buffer_size;
+ int gif_bytes = gif_end - gif_data;
/* Initialise the extensions */
while (gif_bytes > 0 && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
@@ -700,7 +699,7 @@ static gif_result gif__parse_frame_extensions(
}
/* Set buffer position and return */
- gif->buffer_position = gif_data - gif->gif_data;
+ *pos = gif_data;
return GIF_OK;
}
@@ -728,10 +727,11 @@ static gif_result gif__parse_frame_extensions(
static gif_result gif__parse_image_descriptor(
struct gif_animation *gif,
struct gif_frame *frame,
+ uint8_t **pos,
bool decode)
{
- const uint8_t *data = gif->gif_data + gif->buffer_position;
- size_t len = gif->buffer_size - gif->buffer_position;
+ const uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
enum {
GIF_IMAGE_DESCRIPTOR_LEN = 10u,
GIF_IMAGE_SEPARATOR = 0x2Cu,
@@ -767,7 +767,7 @@ static gif_result gif__parse_image_descriptor(
gif->height = (y + h > gif->height) ? y + h : gif->height;
}
- gif->buffer_position += GIF_IMAGE_DESCRIPTOR_LEN;
+ *pos += GIF_IMAGE_DESCRIPTOR_LEN;
return GIF_OK;
}
@@ -783,11 +783,12 @@ static gif_result gif__parse_image_descriptor(
static gif_result gif__parse_colour_table(
struct gif_animation *gif,
struct gif_frame *frame,
+ uint8_t **pos,
bool decode)
{
- const uint8_t *data = gif->gif_data + gif->buffer_position;
- size_t len = gif->buffer_size - gif->buffer_position;
unsigned colour_table_size;
+ const uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
assert(gif != NULL);
assert(frame != NULL);
@@ -822,8 +823,8 @@ static gif_result gif__parse_colour_table(
}
}
- gif->buffer_position += colour_table_size * 3;
gif->colour_table = gif->local_colour_table;
+ *pos += colour_table_size * 3;
return GIF_OK;
}
@@ -839,10 +840,11 @@ static gif_result gif__parse_colour_table(
static gif_result gif__parse_image_data(
struct gif_animation *gif,
struct gif_frame *frame,
+ uint8_t **pos,
bool decode)
{
- uint8_t *data = gif->gif_data + gif->buffer_position;
- size_t len = gif->buffer_size - gif->buffer_position;
+ uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
uint32_t frame_idx = frame - gif->frames;
uint8_t minimum_code_size;
gif_result ret;
@@ -871,16 +873,16 @@ static gif_result gif__parse_image_data(
if (minimum_code_size >= LZW_CODE_MAX) {
return GIF_DATA_ERROR;
}
- gif->buffer_position++;
- data++;
- len--;
if (decode) {
- ret = gif__update_bitmap(gif, frame, minimum_code_size,
- frame_idx);
+ ret = gif__update_bitmap(gif, frame, data, frame_idx);
} else {
uint32_t block_size = 0;
+ /* Skip the minimum code size. */
+ data++;
+ len--;
+
while (block_size != 1) {
if (len < 1) return GIF_INSUFFICIENT_FRAME_DATA;
block_size = data[0] + 1;
@@ -906,9 +908,9 @@ static gif_result gif__parse_image_data(
}
}
- gif->buffer_position = data - gif->gif_data;
gif->frame_count = frame_idx + 1;
gif->frames[frame_idx].display = true;
+ *pos = data;
/* Check if we've finished */
if (len < 1) {
@@ -978,17 +980,17 @@ static gif_result gif_initialise_frame(
struct gif_animation *gif,
uint32_t frame_idx)
{
+ uint8_t *pos;
+ uint8_t *end;
gif_result ret;
- uint8_t *gif_end;
- uint8_t *gif_data;
struct gif_frame *frame;
/* Get our buffer position etc. */
- gif_data = (uint8_t *)(gif->gif_data + gif->buffer_position);
- gif_end = (uint8_t *)(gif->gif_data + gif->buffer_size);
+ pos = (uint8_t *)(gif->gif_data + gif->buffer_position);
+ end = (uint8_t *)(gif->gif_data + gif->buffer_size);
/* Check if we've finished */
- if (gif_data < gif_end && (gif_data[0] == GIF_TRAILER)) {
+ if (pos < end && pos[0] == GIF_TRAILER) {
return GIF_OK;
}
@@ -1005,27 +1007,30 @@ static gif_result gif_initialise_frame(
}
/* Initialise any extensions */
- ret = gif__parse_frame_extensions(gif, frame, true);
+ ret = gif__parse_frame_extensions(gif, frame, &pos, true);
if (ret != GIF_OK) {
- return ret;
+ goto cleanup;
}
- ret = gif__parse_image_descriptor(gif, frame, true);
+ ret = gif__parse_image_descriptor(gif, frame, &pos, true);
if (ret != GIF_OK) {
- return ret;
+ goto cleanup;
}
- ret = gif__parse_colour_table(gif, frame, false);
+ ret = gif__parse_colour_table(gif, frame, &pos, false);
if (ret != GIF_OK) {
- return ret;
+ goto cleanup;
}
- ret = gif__parse_image_data(gif, frame, false);
+ ret = gif__parse_image_data(gif, frame, &pos, false);
if (ret != GIF_OK) {
- return ret;
+ goto cleanup;
}
- return GIF_OK;
+cleanup:
+ gif->buffer_position = pos - gif->gif_data;
+
+ return ret;
}
/**
@@ -1039,10 +1044,9 @@ static gif_result gif_internal_decode_frame(
struct gif_animation *gif,
uint32_t frame_idx)
{
+ uint8_t *pos;
gif_result ret;
- uint8_t *gif_data;
struct gif_frame *frame;
- uint32_t save_buffer_position;
/* Ensure the frame is in range to decode */
if (frame_idx > gif->frame_count_partial) {
@@ -1064,39 +1068,31 @@ static gif_result gif_internal_decode_frame(
return GIF_OK;
}
- /* Get the start of our frame data and the end of the GIF data */
- gif_data = gif->gif_data + frame->frame_pointer;
-
- /* Save the buffer position */
- save_buffer_position = gif->buffer_position;
- gif->buffer_position = gif_data - gif->gif_data;
+ /* Get the start of our frame data and the end of the GIF data. */
+ pos = gif->gif_data + frame->frame_pointer;
/* Skip any extensions because they have already been processed */
- ret = gif__parse_frame_extensions(gif, frame, false);
+ ret = gif__parse_frame_extensions(gif, frame, &pos, false);
if (ret != GIF_OK) {
- goto gif_decode_frame_exit;
+ goto cleanup;
}
- ret = gif__parse_image_descriptor(gif, frame, false);
+ ret = gif__parse_image_descriptor(gif, frame, &pos, false);
if (ret != GIF_OK) {
- goto gif_decode_frame_exit;
+ goto cleanup;
}
- ret = gif__parse_colour_table(gif, frame, true);
+ ret = gif__parse_colour_table(gif, frame, &pos, true);
if (ret != GIF_OK) {
- goto gif_decode_frame_exit;
+ goto cleanup;
}
- ret = gif__parse_image_data(gif, frame, true);
+ ret = gif__parse_image_data(gif, frame, &pos, true);
if (ret != GIF_OK) {
- goto gif_decode_frame_exit;
+ goto cleanup;
}
-gif_decode_frame_exit:
-
- /* Restore the buffer position */
- gif->buffer_position = save_buffer_position;
-
+cleanup:
return ret;
}
commitdiff
http://git.netsurf-browser.org/libnsgif.git/commit/?id=5e93ea19730e0df42e...
commit 5e93ea19730e0df42e9dc3a777cde466f5a8f73d
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Frame initialisation: Don't need to calculate gif_bytes.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 54da5e1..ce500a9 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -979,17 +979,16 @@ static gif_result gif_initialise_frame(
uint32_t frame_idx)
{
gif_result ret;
+ uint8_t *gif_end;
+ uint8_t *gif_data;
struct gif_frame *frame;
- uint8_t *gif_data, *gif_end;
- int gif_bytes;
/* Get our buffer position etc. */
gif_data = (uint8_t *)(gif->gif_data + gif->buffer_position);
gif_end = (uint8_t *)(gif->gif_data + gif->buffer_size);
- gif_bytes = (gif_end - gif_data);
/* Check if we've finished */
- if ((gif_bytes > 0) && (gif_data[0] == GIF_TRAILER)) {
+ if (gif_data < gif_end && (gif_data[0] == GIF_TRAILER)) {
return GIF_OK;
}
@@ -1006,7 +1005,6 @@ static gif_result gif_initialise_frame(
}
/* Initialise any extensions */
- gif->buffer_position = gif_data - gif->gif_data;
ret = gif__parse_frame_extensions(gif, frame, true);
if (ret != GIF_OK) {
return ret;
commitdiff
http://git.netsurf-browser.org/libnsgif.git/commit/?id=920ba6c423181ee0ee...
commit 920ba6c423181ee0eec932c5daeda75fb13fc140
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Use frame rather than indexing frames array.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 80c4530..54da5e1 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -1007,22 +1007,22 @@ static gif_result gif_initialise_frame(
/* Initialise any extensions */
gif->buffer_position = gif_data - gif->gif_data;
- ret = gif__parse_frame_extensions(gif, &gif->frames[frame_idx], true);
+ ret = gif__parse_frame_extensions(gif, frame, true);
if (ret != GIF_OK) {
return ret;
}
- ret = gif__parse_image_descriptor(gif, &gif->frames[frame_idx], true);
+ ret = gif__parse_image_descriptor(gif, frame, true);
if (ret != GIF_OK) {
return ret;
}
- ret = gif__parse_colour_table(gif, &gif->frames[frame_idx], false);
+ ret = gif__parse_colour_table(gif, frame, false);
if (ret != GIF_OK) {
return ret;
}
- ret = gif__parse_image_data(gif, &gif->frames[frame_idx], false);
+ ret = gif__parse_image_data(gif, frame, false);
if (ret != GIF_OK) {
return ret;
}
commitdiff
http://git.netsurf-browser.org/libnsgif.git/commit/?id=1b8341cb075731cffb...
commit 1b8341cb075731cffbf69141c24d8954fef4ee50
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Remove redundant check and comment from frame initialiser.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 53186d9..80c4530 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -993,14 +993,6 @@ static gif_result gif_initialise_frame(
return GIF_OK;
}
- /* Check if there is enough data remaining. The shortest block of data
- * is a 4-byte comment extension + 1-byte block terminator + 1-byte gif
- * trailer
- */
- if (gif_bytes < 6) {
- return GIF_INSUFFICIENT_DATA;
- }
-
/* We could theoretically get some junk data that gives us millions of
* frames, so we ensure that we don't have a silly number
*/
@@ -1013,12 +1005,6 @@ static gif_result gif_initialise_frame(
return GIF_INSUFFICIENT_MEMORY;
}
- /* We pretend to initialise the frames, but really we just skip over
- * all the data contained within. This is all basically a cut down
- * version of gif_decode_frame that doesn't have any of the LZW bits in
- * it.
- */
-
/* Initialise any extensions */
gif->buffer_position = gif_data - gif->gif_data;
ret = gif__parse_frame_extensions(gif, &gif->frames[frame_idx], true);
commitdiff
http://git.netsurf-browser.org/libnsgif.git/commit/?id=8a5130c5559989c3b9...
commit 8a5130c5559989c3b9ea6f74469427abe08c7193
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Use image parsing frunction for frame initialisation.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index bc3c83b..53186d9 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -982,7 +982,6 @@ static gif_result gif_initialise_frame(
struct gif_frame *frame;
uint8_t *gif_data, *gif_end;
int gif_bytes;
- uint32_t block_size;
/* Get our buffer position etc. */
gif_data = (uint8_t *)(gif->gif_data + gif->buffer_position);
@@ -1036,67 +1035,13 @@ static gif_result gif_initialise_frame(
if (ret != GIF_OK) {
return ret;
}
- gif_data = gif->gif_data + gif->buffer_position;
- gif_bytes = (gif_end - gif_data);
- /* Move our data onwards and remember we've got a bit of this frame */
- gif->frame_count_partial = frame_idx + 1;
-
- /* Ensure we have a correct code size */
- if (gif_bytes < 1) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- if (gif_data[0] >= LZW_CODE_MAX) {
- return GIF_DATA_ERROR;
- }
-
- /* Move our pointer to the actual image data */
- gif_data++;
- --gif_bytes;
-
- /* Repeatedly skip blocks until we get a zero block or run out of data
- * These blocks of image data are processed later by gif_decode_frame()
- */
- block_size = 0;
- while (block_size != 1) {
- if (gif_bytes < 1) return GIF_INSUFFICIENT_FRAME_DATA;
- block_size = gif_data[0] + 1;
- /* Check if the frame data runs off the end of the file */
- if ((int)(gif_bytes - block_size) < 0) {
- /* Try to recover by signaling the end of the gif.
- * Once we get garbage data, there is no logical way to
- * determine where the next frame is. It's probably
- * better to partially load the gif than not at all.
- */
- if (gif_bytes >= 2) {
- gif_data[0] = 0;
- gif_data[1] = GIF_TRAILER;
- gif_bytes = 1;
- ++gif_data;
- break;
- } else {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- } else {
- gif_bytes -= block_size;
- gif_data += block_size;
- }
+ ret = gif__parse_image_data(gif, &gif->frames[frame_idx], false);
+ if (ret != GIF_OK) {
+ return ret;
}
- /* Add the frame and set the display flag */
- gif->buffer_position = gif_data - gif->gif_data;
- gif->frame_count = frame_idx + 1;
- gif->frames[frame_idx].display = true;
-
- /* Check if we've finished */
- if (gif_bytes < 1) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- } else {
- if (gif_data[0] == GIF_TRAILER) {
- return GIF_OK;
- }
- }
- return GIF_WORKING;
+ return GIF_OK;
}
/**
commitdiff
http://git.netsurf-browser.org/libnsgif.git/commit/?id=82d8eff773bbb56cd2...
commit 82d8eff773bbb56cd2021187949e39fcc28899c7
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Reorder functions so frame initialise can use image decoder.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index b95b912..bc3c83b 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -70,6 +70,21 @@ enum gif_disposal {
/** standard GIF header size */
#define GIF_STANDARD_HEADER_SIZE 13
+static gif_result gif_error_from_lzw(lzw_result l_res)
+{
+ static const gif_result g_res[] = {
+ [LZW_OK] = GIF_OK,
+ [LZW_OK_EOD] = GIF_END_OF_FRAME,
+ [LZW_NO_MEM] = GIF_INSUFFICIENT_MEMORY,
+ [LZW_NO_DATA] = GIF_INSUFFICIENT_FRAME_DATA,
+ [LZW_EOI_CODE] = GIF_FRAME_DATA_ERROR,
+ [LZW_BAD_ICODE] = GIF_FRAME_DATA_ERROR,
+ [LZW_BAD_CODE] = GIF_FRAME_DATA_ERROR,
+ };
+ assert(l_res != LZW_BAD_PARAM);
+ assert(l_res != LZW_NO_COLOUR);
+ return g_res[l_res];
+}
/**
* Updates the sprite memory size
@@ -79,10 +94,10 @@ enum gif_disposal {
* \param height The height of the sprite
* \return GIF_INSUFFICIENT_MEMORY for a memory error GIF_OK for success
*/
-static gif_result
-gif_initialise_sprite(gif_animation *gif,
- uint32_t width,
- uint32_t height)
+static gif_result gif_initialise_sprite(
+ struct gif_animation *gif,
+ uint32_t width,
+ uint32_t height)
{
/* Already allocated? */
if (gif->frame_image) {
@@ -99,909 +114,717 @@ gif_initialise_sprite(gif_animation *gif,
}
/**
- * Parse the application extension
+ * Helper to get the rendering bitmap for a gif.
*
- * \param[in] frame The gif object we're decoding.
- * \param[in] data The data to decode.
- * \param[in] len Byte length of data.
- * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
- * GIF_OK for success.
+ * \param[in] gif The gif object we're decoding.
+ * \return Client pixel buffer for rendering into.
*/
-static gif_result gif__parse_extension_graphic_control(
- struct gif_frame *frame,
- uint8_t *data,
- size_t len)
+static inline uint32_t* gif__bitmap_get(
+ struct gif_animation *gif)
{
- /* 6-byte Graphic Control Extension is:
- *
- * +0 CHAR Graphic Control Label
- * +1 CHAR Block Size
- * +2 CHAR __Packed Fields__
- * 3BITS Reserved
- * 3BITS Disposal Method
- * 1BIT User Input Flag
- * 1BIT Transparent Color Flag
- * +3 SHORT Delay Time
- * +5 CHAR Transparent Color Index
- */
- if (len < 6) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
-
- frame->frame_delay = data[3] | (data[4] << 8);
- if (data[2] & GIF_TRANSPARENCY_MASK) {
- frame->transparency = true;
- frame->transparency_index = data[5];
- }
+ gif_result ret;
- frame->disposal_method = ((data[2] & GIF_DISPOSAL_MASK) >> 2);
- /* I have encountered documentation and GIFs in the
- * wild that use 0x04 to restore the previous frame,
- * rather than the officially documented 0x03. I
- * believe some (older?) software may even actually
- * export this way. We handle this as a type of
- * "quirks" mode. */
- if (frame->disposal_method == GIF_DISPOSAL_RESTORE_QUIRK) {
- frame->disposal_method = GIF_DISPOSAL_RESTORE_PREV;
+ /* Make sure we have a buffer to decode to. */
+ ret = gif_initialise_sprite(gif, gif->width, gif->height);
+ if (ret != GIF_OK) {
+ return NULL;
}
- /* if we are clearing the background then we need to
- * redraw enough to cover the previous frame too. */
- frame->redraw_required =
- frame->disposal_method == GIF_DISPOSAL_RESTORE_BG ||
- frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV;
-
- return GIF_OK;
+ /* Get the frame data */
+ assert(gif->bitmap_callbacks.bitmap_get_buffer);
+ return (uint32_t *)gif->bitmap_callbacks.bitmap_get_buffer(
+ gif->frame_image);
}
/**
- * Parse the application extension
+ * Helper to tell the client that their bitmap was modified.
*
- * \param[in] gif The gif object we're decoding.
- * \param[in] data The data to decode.
- * \param[in] len Byte length of data.
- * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
- * GIF_OK for success.
+ * \param[in] gif The gif object we're decoding.
*/
-static gif_result gif__parse_extension_application(
- struct gif_animation *gif,
- uint8_t *data,
- size_t len)
+static inline void gif__bitmap_modified(
+ const struct gif_animation *gif)
{
- /* 14-byte+ Application Extension is:
- *
- * +0 CHAR Application Extension Label
- * +1 CHAR Block Size
- * +2 8CHARS Application Identifier
- * +10 3CHARS Appl. Authentication Code
- * +13 1-256 Application Data (Data sub-blocks)
- */
- if (len < 17) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ if (gif->bitmap_callbacks.bitmap_modified) {
+ gif->bitmap_callbacks.bitmap_modified(gif->frame_image);
}
+}
- if ((data[1] == 0x0b) &&
- (strncmp((const char *)data + 2, "NETSCAPE2.0", 11) == 0) &&
- (data[13] == 0x03) && (data[14] == 0x01)) {
- gif->loop_count = data[15] | (data[16] << 8);
+/**
+ * Helper to tell the client that whether the bitmap is opaque.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frmae The frame that has been decoded.
+ */
+static inline void gif__bitmap_set_opaque(
+ const struct gif_animation *gif,
+ const struct gif_frame *frame)
+{
+ if (gif->bitmap_callbacks.bitmap_set_opaque) {
+ gif->bitmap_callbacks.bitmap_set_opaque(
+ gif->frame_image, frame->opaque);
}
-
- return GIF_OK;
}
/**
- * Parse the frame's extensions
+ * Helper to get the client to determine if the bitmap is opaque.
*
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to parse extensions for.
- * \param[in] decode Whether to decode or skip over the extension.
- * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
- * GIF_OK for success.
+ * \todo: We don't really need to get the client to do this for us.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \return true if the bitmap is opaque, false otherwise.
*/
-static gif_result gif__parse_frame_extensions(
- struct gif_animation *gif,
- struct gif_frame *frame,
- bool decode)
+static inline bool gif__bitmap_get_opaque(
+ const struct gif_animation *gif)
{
- uint8_t *gif_data, *gif_end;
- int gif_bytes;
-
- /* Get our buffer position etc. */
- gif_data = gif->gif_data + gif->buffer_position;
- gif_end = gif->gif_data + gif->buffer_size;
- gif_bytes = gif_end - gif_data;
-
- /* Initialise the extensions */
- while (gif_bytes > 0 && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
- bool block_step = true;
- gif_result ret;
-
- gif_data++;
- gif_bytes--;
-
- if (gif_bytes == 0) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
+ if (gif->bitmap_callbacks.bitmap_test_opaque) {
+ return gif->bitmap_callbacks.bitmap_test_opaque(
+ gif->frame_image);
+ }
- /* Switch on extension label */
- switch (gif_data[0]) {
- case GIF_EXTENSION_GRAPHIC_CONTROL:
- if (decode) {
- ret = gif__parse_extension_graphic_control(
- frame, gif_data, gif_bytes);
- if (ret != GIF_OK) {
- return ret;
- }
- }
- break;
+ return false;
+}
- case GIF_EXTENSION_APPLICATION:
- if (decode) {
- ret = gif__parse_extension_application(
- gif, gif_data, gif_bytes);
- if (ret != GIF_OK) {
- return ret;
- }
- }
- break;
+static void gif__record_frame(
+ struct gif_animation *gif,
+ const uint32_t *bitmap)
+{
+ bool need_alloc = gif->prev_frame == NULL;
+ uint32_t *prev_frame;
- case GIF_EXTENSION_COMMENT:
- /* Move the pointer to the first data sub-block Skip 1
- * byte for the extension label. */
- ++gif_data;
- block_step = false;
- break;
+ if (gif->decoded_frame == GIF_INVALID_FRAME ||
+ gif->decoded_frame == gif->prev_index) {
+ /* No frame to copy, or already have this frame recorded. */
+ return;
+ }
- default:
- break;
- }
+ bitmap = gif__bitmap_get(gif);
+ if (bitmap == NULL) {
+ return;
+ }
- if (block_step) {
- /* Move the pointer to the first data sub-block Skip 2
- * bytes for the extension label and size fields Skip
- * the extension size itself
- */
- if (gif_bytes < 2) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- gif_data += 2 + gif_data[1];
- }
+ if (gif->prev_frame != NULL &&
+ gif->width * gif->height > gif->prev_width * gif->prev_height) {
+ need_alloc = true;
+ }
- /* Repeatedly skip blocks until we get a zero block or run out
- * of data. This data is ignored by this gif decoder. */
- while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) {
- gif_data += gif_data[0] + 1;
- if (gif_data >= gif_end) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
+ if (need_alloc) {
+ prev_frame = realloc(gif->prev_frame,
+ gif->width * gif->height * 4);
+ if (prev_frame == NULL) {
+ return;
}
- gif_data++;
- gif_bytes = gif_end - gif_data;
+ } else {
+ prev_frame = gif->prev_frame;
}
- if (gif_data > gif_end) {
- gif_data = gif_end;
- }
+ memcpy(prev_frame, bitmap, gif->width * gif->height * 4);
- /* Set buffer position and return */
- gif->buffer_position = gif_data - gif->gif_data;
- return GIF_OK;
+ gif->prev_frame = prev_frame;
+ gif->prev_width = gif->width;
+ gif->prev_height = gif->height;
+ gif->prev_index = gif->decoded_frame;
}
-/**
- * Parse a GIF Image Descriptor.
- *
- * The format is:
- *
- * +0 CHAR Image Separator (0x2c)
- * +1 SHORT Image Left Position
- * +3 SHORT Image Top Position
- * +5 SHORT Width
- * +7 SHORT Height
- * +9 CHAR __Packed Fields__
- * 1BIT Local Colour Table Flag
- * 1BIT Interlace Flag
- * 1BIT Sort Flag
- * 2BITS Reserved
- * 3BITS Size of Local Colour Table
- *
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to parse an image descriptor for.
- * \return GIF_OK on success, appropriate error otherwise.
- */
-static gif_result gif__parse_image_descriptor(
- struct gif_animation *gif,
- struct gif_frame *frame,
- bool decode)
+static gif_result gif__recover_frame(
+ const struct gif_animation *gif,
+ uint32_t *bitmap)
{
- const uint8_t *data = gif->gif_data + gif->buffer_position;
- size_t len = gif->buffer_size - gif->buffer_position;
- enum {
- GIF_IMAGE_DESCRIPTOR_LEN = 10u,
- GIF_IMAGE_SEPARATOR = 0x2Cu,
- };
-
- assert(gif != NULL);
- assert(frame != NULL);
+ const uint32_t *prev_frame = gif->prev_frame;
+ unsigned height = gif->height < gif->prev_height ? gif->height :
gif->prev_height;
+ unsigned width = gif->width < gif->prev_width ? gif->width :
gif->prev_width;
- if (len < GIF_IMAGE_DESCRIPTOR_LEN) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ if (prev_frame == NULL) {
+ return GIF_FRAME_DATA_ERROR;
}
- if (decode) {
- unsigned x, y, w, h;
-
- if (data[0] != GIF_IMAGE_SEPARATOR) {
- return GIF_FRAME_DATA_ERROR;
- }
-
- x = data[1] | (data[2] << 8);
- y = data[3] | (data[4] << 8);
- w = data[5] | (data[6] << 8);
- h = data[7] | (data[8] << 8);
- frame->flags = data[9];
-
- frame->redraw_x = x;
- frame->redraw_y = y;
- frame->redraw_width = w;
- frame->redraw_height = h;
+ for (unsigned y = 0; y < height; y++) {
+ memcpy(bitmap, prev_frame, width * 4);
- /* Frame size may have grown. */
- gif->width = (x + w > gif->width ) ? x + w : gif->width;
- gif->height = (y + h > gif->height) ? y + h : gif->height;
+ bitmap += gif->width;
+ prev_frame += gif->prev_width;
}
- gif->buffer_position += GIF_IMAGE_DESCRIPTOR_LEN;
return GIF_OK;
}
-/**
- * Get a frame's colour table.
- *
- * Sets up gif->colour_table for the frame.
- *
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to get the colour table for.
- * \return GIF_OK on success, appropriate error otherwise.
- */
-static gif_result gif__parse_colour_table(
- struct gif_animation *gif,
- struct gif_frame *frame,
- bool decode)
+static uint32_t gif_interlaced_line(int height, int y)
{
- const uint8_t *data = gif->gif_data + gif->buffer_position;
- size_t len = gif->buffer_size - gif->buffer_position;
- unsigned colour_table_size;
-
- assert(gif != NULL);
- assert(frame != NULL);
-
- if ((frame->flags & GIF_COLOUR_TABLE_MASK) == 0) {
- gif->colour_table = gif->global_colour_table;
- return GIF_OK;
+ if ((y << 3) < height) {
+ return (y << 3);
}
-
- colour_table_size = 2 << (frame->flags & GIF_COLOUR_TABLE_SIZE_MASK);
- if (len < colour_table_size * 3) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ y -= ((height + 7) >> 3);
+ if ((y << 3) < (height - 4)) {
+ return (y << 3) + 4;
}
-
- if (decode) {
- int count = colour_table_size;
- uint8_t *entry = (uint8_t *)gif->local_colour_table;
-
- while (count--) {
- /* Gif colour map contents are r,g,b.
- *
- * We want to pack them bytewise into the
- * colour table, such that the red component
- * is in byte 0 and the alpha component is in
- * byte 3.
- */
-
- *entry++ = *data++; /* r */
- *entry++ = *data++; /* g */
- *entry++ = *data++; /* b */
- *entry++ = 0xff; /* a */
- }
+ y -= ((height + 3) >> 3);
+ if ((y << 2) < (height - 2)) {
+ return (y << 2) + 2;
}
-
- gif->buffer_position += colour_table_size * 3;
- gif->colour_table = gif->local_colour_table;
- return GIF_OK;
+ y -= ((height + 1) >> 2);
+ return (y << 1) + 1;
}
-static struct gif_frame *gif__get_frame(
+static gif_result gif__decode_complex(
struct gif_animation *gif,
- uint32_t frame_idx)
+ uint32_t width,
+ uint32_t height,
+ uint32_t offset_x,
+ uint32_t offset_y,
+ uint32_t interlace,
+ uint8_t minimum_code_size,
+ uint32_t transparency_index,
+ uint32_t *restrict frame_data,
+ uint32_t *restrict colour_table)
{
- struct gif_frame *frame;
+ uint32_t available = 0;
+ gif_result ret = GIF_OK;
+ lzw_result res;
- if (gif->frame_holders > frame_idx) {
- frame = &gif->frames[frame_idx];
- } else {
- /* Allocate more memory */
- size_t count = frame_idx + 1;
- struct gif_frame *temp;
+ /* Initialise the LZW decoding */
+ res = lzw_decode_init(gif->lzw_ctx, minimum_code_size,
+ gif->gif_data, gif->buffer_size, gif->buffer_position);
+ if (res != LZW_OK) {
+ return gif_error_from_lzw(res);
+ }
- temp = realloc(gif->frames, count * sizeof(*frame));
- if (temp == NULL) {
- return NULL;
+ for (uint32_t y = 0; y < height; y++) {
+ uint32_t x;
+ uint32_t decode_y;
+ uint32_t *frame_scanline;
+
+ if (interlace) {
+ decode_y = gif_interlaced_line(height, y) + offset_y;
+ } else {
+ decode_y = y + offset_y;
}
- gif->frames = temp;
- gif->frame_holders = count;
+ frame_scanline = frame_data + offset_x + (decode_y * gif->width);
- frame = &gif->frames[frame_idx];
+ x = width;
+ while (x > 0) {
+ const uint8_t *uncompressed;
+ unsigned row_available;
+ if (available == 0) {
+ if (res != LZW_OK) {
+ /* Unexpected end of frame, try to recover */
+ if (res == LZW_OK_EOD) {
+ ret = GIF_OK;
+ } else {
+ ret = gif_error_from_lzw(res);
+ }
+ break;
+ }
+ res = lzw_decode(gif->lzw_ctx,
+ &uncompressed, &available);
+ }
- frame->transparency = false;
- frame->transparency_index = GIF_NO_TRANSPARENCY;
- frame->frame_pointer = gif->buffer_position;
- frame->redraw_required = false;
- frame->disposal_method = 0;
- frame->frame_delay = 100;
- frame->display = false;
- frame->virgin = true;
+ row_available = x < available ? x : available;
+ x -= row_available;
+ available -= row_available;
+ if (transparency_index > 0xFF) {
+ while (row_available-- > 0) {
+ *frame_scanline++ =
+ colour_table[*uncompressed++];
+ }
+ } else {
+ while (row_available-- > 0) {
+ register uint32_t colour;
+ colour = *uncompressed++;
+ if (colour != transparency_index) {
+ *frame_scanline =
+ colour_table[colour];
+ }
+ frame_scanline++;
+ }
+ }
+ }
}
-
- return frame;
+ return ret;
}
-/**
- * Attempts to initialise the next frame
- *
- * \param[in] gif The animation context
- * \param[in] frame_idx The frame number to decode.
- * \return error code
- * - GIF_INSUFFICIENT_DATA for insufficient data to do anything
- * - GIF_FRAME_DATA_ERROR for GIF frame data error
- * - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
- * - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
- * - GIF_DATA_ERROR for GIF error (invalid frame header)
- * - GIF_OK for successful decoding
- * - GIF_WORKING for successful decoding if more frames are expected
-*/
-static gif_result gif_initialise_frame(gif_animation *gif,
- uint32_t frame_idx)
+static gif_result gif__decode_simple(
+ struct gif_animation *gif,
+ uint32_t height,
+ uint32_t offset_y,
+ uint8_t minimum_code_size,
+ uint32_t transparency_index,
+ uint32_t *restrict frame_data,
+ uint32_t *restrict colour_table)
{
- gif_result ret;
- struct gif_frame *frame;
- uint8_t *gif_data, *gif_end;
- int gif_bytes;
- uint32_t block_size;
-
- /* Get our buffer position etc. */
- gif_data = (uint8_t *)(gif->gif_data + gif->buffer_position);
- gif_end = (uint8_t *)(gif->gif_data + gif->buffer_size);
- gif_bytes = (gif_end - gif_data);
-
- /* Check if we've finished */
- if ((gif_bytes > 0) && (gif_data[0] == GIF_TRAILER)) {
- return GIF_OK;
- }
-
- /* Check if there is enough data remaining. The shortest block of data
- * is a 4-byte comment extension + 1-byte block terminator + 1-byte gif
- * trailer
- */
- if (gif_bytes < 6) {
- return GIF_INSUFFICIENT_DATA;
- }
-
- /* We could theoretically get some junk data that gives us millions of
- * frames, so we ensure that we don't have a silly number
- */
- if (frame_idx > 4096) {
- return GIF_FRAME_DATA_ERROR;
- }
-
- frame = gif__get_frame(gif, frame_idx);
- if (frame == NULL) {
- return GIF_INSUFFICIENT_MEMORY;
- }
-
- /* We pretend to initialise the frames, but really we just skip over
- * all the data contained within. This is all basically a cut down
- * version of gif_decode_frame that doesn't have any of the LZW bits in
- * it.
- */
-
- /* Initialise any extensions */
- gif->buffer_position = gif_data - gif->gif_data;
- ret = gif__parse_frame_extensions(gif, &gif->frames[frame_idx], true);
- if (ret != GIF_OK) {
- return ret;
- }
-
- ret = gif__parse_image_descriptor(gif, &gif->frames[frame_idx], true);
- if (ret != GIF_OK) {
- return ret;
- }
-
- ret = gif__parse_colour_table(gif, &gif->frames[frame_idx], false);
- if (ret != GIF_OK) {
- return ret;
- }
- gif_data = gif->gif_data + gif->buffer_position;
- gif_bytes = (gif_end - gif_data);
-
- /* Move our data onwards and remember we've got a bit of this frame */
- gif->frame_count_partial = frame_idx + 1;
+ uint32_t pixels = gif->width * height;
+ uint32_t written = 0;
+ gif_result ret = GIF_OK;
+ lzw_result res;
- /* Ensure we have a correct code size */
- if (gif_bytes < 1) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- if (gif_data[0] >= LZW_CODE_MAX) {
- return GIF_DATA_ERROR;
+ /* Initialise the LZW decoding */
+ res = lzw_decode_init_map(gif->lzw_ctx,
+ minimum_code_size, transparency_index, colour_table,
+ gif->gif_data, gif->buffer_size, gif->buffer_position);
+ if (res != LZW_OK) {
+ return gif_error_from_lzw(res);
}
- /* Move our pointer to the actual image data */
- gif_data++;
- --gif_bytes;
+ frame_data += (offset_y * gif->width);
- /* Repeatedly skip blocks until we get a zero block or run out of data
- * These blocks of image data are processed later by gif_decode_frame()
- */
- block_size = 0;
- while (block_size != 1) {
- if (gif_bytes < 1) return GIF_INSUFFICIENT_FRAME_DATA;
- block_size = gif_data[0] + 1;
- /* Check if the frame data runs off the end of the file */
- if ((int)(gif_bytes - block_size) < 0) {
- /* Try to recover by signaling the end of the gif.
- * Once we get garbage data, there is no logical way to
- * determine where the next frame is. It's probably
- * better to partially load the gif than not at all.
- */
- if (gif_bytes >= 2) {
- gif_data[0] = 0;
- gif_data[1] = GIF_TRAILER;
- gif_bytes = 1;
- ++gif_data;
- break;
+ while (pixels > 0) {
+ res = lzw_decode_map(gif->lzw_ctx,
+ frame_data, pixels, &written);
+ pixels -= written;
+ frame_data += written;
+ if (res != LZW_OK) {
+ /* Unexpected end of frame, try to recover */
+ if (res == LZW_OK_EOD) {
+ ret = GIF_OK;
} else {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ ret = gif_error_from_lzw(res);
}
- } else {
- gif_bytes -= block_size;
- gif_data += block_size;
- }
- }
-
- /* Add the frame and set the display flag */
- gif->buffer_position = gif_data - gif->gif_data;
- gif->frame_count = frame_idx + 1;
- gif->frames[frame_idx].display = true;
-
- /* Check if we've finished */
- if (gif_bytes < 1) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- } else {
- if (gif_data[0] == GIF_TRAILER) {
- return GIF_OK;
+ break;
}
}
- return GIF_WORKING;
-}
-static uint32_t gif_interlaced_line(int height, int y)
-{
- if ((y << 3) < height) {
- return (y << 3);
- }
- y -= ((height + 7) >> 3);
- if ((y << 3) < (height - 4)) {
- return (y << 3) + 4;
- }
- y -= ((height + 3) >> 3);
- if ((y << 2) < (height - 2)) {
- return (y << 2) + 2;
+ if (pixels == 0) {
+ ret = GIF_OK;
}
- y -= ((height + 1) >> 2);
- return (y << 1) + 1;
-}
-
-static gif_result gif_error_from_lzw(lzw_result l_res)
-{
- static const gif_result g_res[] = {
- [LZW_OK] = GIF_OK,
- [LZW_OK_EOD] = GIF_END_OF_FRAME,
- [LZW_NO_MEM] = GIF_INSUFFICIENT_MEMORY,
- [LZW_NO_DATA] = GIF_INSUFFICIENT_FRAME_DATA,
- [LZW_EOI_CODE] = GIF_FRAME_DATA_ERROR,
- [LZW_BAD_ICODE] = GIF_FRAME_DATA_ERROR,
- [LZW_BAD_CODE] = GIF_FRAME_DATA_ERROR,
- };
- assert(l_res != LZW_BAD_PARAM);
- assert(l_res != LZW_NO_COLOUR);
- return g_res[l_res];
+ return ret;
}
-/**
- * Helper to get the rendering bitmap for a gif.
- *
- * \param[in] gif The gif object we're decoding.
- * \return Client pixel buffer for rendering into.
- */
-static inline uint32_t* gif__bitmap_get(
- struct gif_animation *gif)
+static inline gif_result gif__decode(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ uint8_t minimum_code_size,
+ uint32_t *restrict frame_data)
{
gif_result ret;
+ uint32_t offset_x = frame->redraw_x;
+ uint32_t offset_y = frame->redraw_y;
+ uint32_t width = frame->redraw_width;
+ uint32_t height = frame->redraw_height;
+ uint32_t interlace = frame->flags & GIF_INTERLACE_MASK;
+ uint32_t transparency_index = frame->transparency_index;
+ uint32_t *restrict colour_table = gif->colour_table;
- /* Make sure we have a buffer to decode to. */
- ret = gif_initialise_sprite(gif, gif->width, gif->height);
- if (ret != GIF_OK) {
- return NULL;
- }
-
- /* Get the frame data */
- assert(gif->bitmap_callbacks.bitmap_get_buffer);
- return (uint32_t *)gif->bitmap_callbacks.bitmap_get_buffer(
- gif->frame_image);
-}
-
-/**
- * Helper to tell the client that their bitmap was modified.
- *
- * \param[in] gif The gif object we're decoding.
- */
-static inline void gif__bitmap_modified(
- const struct gif_animation *gif)
-{
- if (gif->bitmap_callbacks.bitmap_modified) {
- gif->bitmap_callbacks.bitmap_modified(gif->frame_image);
+ if (interlace == false && width == gif->width && offset_x == 0) {
+ ret = gif__decode_simple(gif, height, offset_y,
+ minimum_code_size, transparency_index,
+ frame_data, colour_table);
+ } else {
+ ret = gif__decode_complex(gif, width, height,
+ offset_x, offset_y, interlace,
+ minimum_code_size, transparency_index,
+ frame_data, colour_table);
}
-}
-/**
- * Helper to tell the client that whether the bitmap is opaque.
- *
- * \param[in] gif The gif object we're decoding.
- * \param[in] frmae The frame that has been decoded.
- */
-static inline void gif__bitmap_set_opaque(
- const struct gif_animation *gif,
- const struct gif_frame *frame)
-{
- if (gif->bitmap_callbacks.bitmap_set_opaque) {
- gif->bitmap_callbacks.bitmap_set_opaque(
- gif->frame_image, frame->opaque);
- }
+ return ret;
}
/**
- * Helper to get the client to determine if the bitmap is opaque.
- *
- * \todo: We don't really need to get the client to do this for us.
+ * Restore a GIF to the background colour.
*
- * \param[in] gif The gif object we're decoding.
- * \return true if the bitmap is opaque, false otherwise.
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to clear, or NULL.
+ * \param[in] bitmap The bitmap to clear the frame in.
*/
-static inline bool gif__bitmap_get_opaque(
- const struct gif_animation *gif)
+static void gif__restore_bg(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ uint32_t *bitmap)
{
- if (gif->bitmap_callbacks.bitmap_test_opaque) {
- return gif->bitmap_callbacks.bitmap_test_opaque(
- gif->frame_image);
- }
+ if (frame == NULL) {
+ memset(bitmap, GIF_TRANSPARENT_COLOUR,
+ gif->width * gif->height * sizeof(*bitmap));
+ } else {
+ uint32_t offset_x = frame->redraw_x;
+ uint32_t offset_y = frame->redraw_y;
+ uint32_t width = frame->redraw_width;
+ uint32_t height = frame->redraw_height;
- return false;
+ if (frame->display == false) {
+ return;
+ }
+
+ if (frame->transparency) {
+ for (uint32_t y = 0; y < height; y++) {
+ uint32_t *scanline = bitmap + offset_x +
+ (offset_y + y) * gif->width;
+ memset(scanline, GIF_TRANSPARENT_COLOUR,
+ width * sizeof(*bitmap));
+ }
+ } else {
+ for (uint32_t y = 0; y < height; y++) {
+ uint32_t *scanline = bitmap + offset_x +
+ (offset_y + y) * gif->width;
+ for (uint32_t x = 0; x < width; x++) {
+ scanline[x] = gif->bg_colour;
+ }
+ }
+ }
+ }
}
-static void gif__record_frame(
+static gif_result gif__update_bitmap(
struct gif_animation *gif,
- const uint32_t *bitmap)
+ struct gif_frame *frame,
+ uint8_t minimum_code_size,
+ uint32_t frame_idx)
{
- bool need_alloc = gif->prev_frame == NULL;
- uint32_t *prev_frame;
+ gif_result ret;
+ uint32_t *bitmap;
- if (gif->decoded_frame == GIF_INVALID_FRAME ||
- gif->decoded_frame == gif->prev_index) {
- /* No frame to copy, or already have this frame recorded. */
- return;
- }
+ gif->decoded_frame = frame_idx;
bitmap = gif__bitmap_get(gif);
if (bitmap == NULL) {
- return;
+ return GIF_INSUFFICIENT_MEMORY;
}
- if (gif->prev_frame != NULL &&
- gif->width * gif->height > gif->prev_width * gif->prev_height) {
- need_alloc = true;
- }
+ /* Handle any bitmap clearing/restoration required before decoding this
+ * frame. */
+ if (frame_idx == 0 || gif->decoded_frame == GIF_INVALID_FRAME) {
+ gif__restore_bg(gif, NULL, bitmap);
- if (need_alloc) {
- prev_frame = realloc(gif->prev_frame,
- gif->width * gif->height * 4);
- if (prev_frame == NULL) {
- return;
- }
} else {
- prev_frame = gif->prev_frame;
+ struct gif_frame *prev = &gif->frames[frame_idx - 1];
+
+ if (prev->disposal_method == GIF_DISPOSAL_RESTORE_BG) {
+ gif__restore_bg(gif, prev, bitmap);
+
+ } else if (prev->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
+ ret = gif__recover_frame(gif, bitmap);
+ if (ret != GIF_OK) {
+ gif__restore_bg(gif, prev, bitmap);
+ }
+ }
}
- memcpy(prev_frame, bitmap, gif->width * gif->height * 4);
+ if (frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
+ /* Store the previous frame for later restoration */
+ gif__record_frame(gif, bitmap);
+ }
- gif->prev_frame = prev_frame;
- gif->prev_width = gif->width;
- gif->prev_height = gif->height;
- gif->prev_index = gif->decoded_frame;
+ ret = gif__decode(gif, frame, minimum_code_size, bitmap);
+
+ gif__bitmap_modified(gif);
+
+ if (frame->virgin) {
+ frame->opaque = gif__bitmap_get_opaque(gif);
+ frame->virgin = false;
+ }
+ gif__bitmap_set_opaque(gif, frame);
+
+ return ret;
}
-static gif_result gif__recover_frame(
- const struct gif_animation *gif,
- uint32_t *bitmap)
+/**
+ * Parse the application extension
+ *
+ * \param[in] frame The gif object we're decoding.
+ * \param[in] data The data to decode.
+ * \param[in] len Byte length of data.
+ * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
+ * GIF_OK for success.
+ */
+static gif_result gif__parse_extension_graphic_control(
+ struct gif_frame *frame,
+ uint8_t *data,
+ size_t len)
{
- const uint32_t *prev_frame = gif->prev_frame;
- unsigned height = gif->height < gif->prev_height ? gif->height :
gif->prev_height;
- unsigned width = gif->width < gif->prev_width ? gif->width :
gif->prev_width;
+ /* 6-byte Graphic Control Extension is:
+ *
+ * +0 CHAR Graphic Control Label
+ * +1 CHAR Block Size
+ * +2 CHAR __Packed Fields__
+ * 3BITS Reserved
+ * 3BITS Disposal Method
+ * 1BIT User Input Flag
+ * 1BIT Transparent Color Flag
+ * +3 SHORT Delay Time
+ * +5 CHAR Transparent Color Index
+ */
+ if (len < 6) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
- if (prev_frame == NULL) {
- return GIF_FRAME_DATA_ERROR;
+ frame->frame_delay = data[3] | (data[4] << 8);
+ if (data[2] & GIF_TRANSPARENCY_MASK) {
+ frame->transparency = true;
+ frame->transparency_index = data[5];
}
- for (unsigned y = 0; y < height; y++) {
- memcpy(bitmap, prev_frame, width * 4);
+ frame->disposal_method = ((data[2] & GIF_DISPOSAL_MASK) >> 2);
+ /* I have encountered documentation and GIFs in the
+ * wild that use 0x04 to restore the previous frame,
+ * rather than the officially documented 0x03. I
+ * believe some (older?) software may even actually
+ * export this way. We handle this as a type of
+ * "quirks" mode. */
+ if (frame->disposal_method == GIF_DISPOSAL_RESTORE_QUIRK) {
+ frame->disposal_method = GIF_DISPOSAL_RESTORE_PREV;
+ }
- bitmap += gif->width;
- prev_frame += gif->prev_width;
+ /* if we are clearing the background then we need to
+ * redraw enough to cover the previous frame too. */
+ frame->redraw_required =
+ frame->disposal_method == GIF_DISPOSAL_RESTORE_BG ||
+ frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV;
+
+ return GIF_OK;
+}
+
+/**
+ * Parse the application extension
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] data The data to decode.
+ * \param[in] len Byte length of data.
+ * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
+ * GIF_OK for success.
+ */
+static gif_result gif__parse_extension_application(
+ struct gif_animation *gif,
+ uint8_t *data,
+ size_t len)
+{
+ /* 14-byte+ Application Extension is:
+ *
+ * +0 CHAR Application Extension Label
+ * +1 CHAR Block Size
+ * +2 8CHARS Application Identifier
+ * +10 3CHARS Appl. Authentication Code
+ * +13 1-256 Application Data (Data sub-blocks)
+ */
+ if (len < 17) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
+
+ if ((data[1] == 0x0b) &&
+ (strncmp((const char *)data + 2, "NETSCAPE2.0", 11) == 0) &&
+ (data[13] == 0x03) && (data[14] == 0x01)) {
+ gif->loop_count = data[15] | (data[16] << 8);
}
return GIF_OK;
}
-static gif_result
-gif__decode_complex(
+/**
+ * Parse the frame's extensions
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to parse extensions for.
+ * \param[in] decode Whether to decode or skip over the extension.
+ * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
+ * GIF_OK for success.
+ */
+static gif_result gif__parse_frame_extensions(
struct gif_animation *gif,
- uint32_t width,
- uint32_t height,
- uint32_t offset_x,
- uint32_t offset_y,
- uint32_t interlace,
- uint8_t minimum_code_size,
- uint32_t transparency_index,
- uint32_t *restrict frame_data,
- uint32_t *restrict colour_table)
+ struct gif_frame *frame,
+ bool decode)
{
- uint32_t available = 0;
- gif_result ret = GIF_OK;
- lzw_result res;
+ uint8_t *gif_data, *gif_end;
+ int gif_bytes;
+
+ /* Get our buffer position etc. */
+ gif_data = gif->gif_data + gif->buffer_position;
+ gif_end = gif->gif_data + gif->buffer_size;
+ gif_bytes = gif_end - gif_data;
- /* Initialise the LZW decoding */
- res = lzw_decode_init(gif->lzw_ctx, minimum_code_size,
- gif->gif_data, gif->buffer_size, gif->buffer_position);
- if (res != LZW_OK) {
- return gif_error_from_lzw(res);
- }
+ /* Initialise the extensions */
+ while (gif_bytes > 0 && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
+ bool block_step = true;
+ gif_result ret;
- for (uint32_t y = 0; y < height; y++) {
- uint32_t x;
- uint32_t decode_y;
- uint32_t *frame_scanline;
+ gif_data++;
+ gif_bytes--;
- if (interlace) {
- decode_y = gif_interlaced_line(height, y) + offset_y;
- } else {
- decode_y = y + offset_y;
+ if (gif_bytes == 0) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
}
- frame_scanline = frame_data + offset_x + (decode_y * gif->width);
- x = width;
- while (x > 0) {
- const uint8_t *uncompressed;
- unsigned row_available;
- if (available == 0) {
- if (res != LZW_OK) {
- /* Unexpected end of frame, try to recover */
- if (res == LZW_OK_EOD) {
- ret = GIF_OK;
- } else {
- ret = gif_error_from_lzw(res);
- }
- break;
+ /* Switch on extension label */
+ switch (gif_data[0]) {
+ case GIF_EXTENSION_GRAPHIC_CONTROL:
+ if (decode) {
+ ret = gif__parse_extension_graphic_control(
+ frame, gif_data, gif_bytes);
+ if (ret != GIF_OK) {
+ return ret;
}
- res = lzw_decode(gif->lzw_ctx,
- &uncompressed, &available);
}
+ break;
- row_available = x < available ? x : available;
- x -= row_available;
- available -= row_available;
- if (transparency_index > 0xFF) {
- while (row_available-- > 0) {
- *frame_scanline++ =
- colour_table[*uncompressed++];
- }
- } else {
- while (row_available-- > 0) {
- register uint32_t colour;
- colour = *uncompressed++;
- if (colour != transparency_index) {
- *frame_scanline =
- colour_table[colour];
- }
- frame_scanline++;
+ case GIF_EXTENSION_APPLICATION:
+ if (decode) {
+ ret = gif__parse_extension_application(
+ gif, gif_data, gif_bytes);
+ if (ret != GIF_OK) {
+ return ret;
}
}
- }
- }
- return ret;
-}
-
-static gif_result
-gif__decode_simple(
- struct gif_animation *gif,
- uint32_t height,
- uint32_t offset_y,
- uint8_t minimum_code_size,
- uint32_t transparency_index,
- uint32_t *restrict frame_data,
- uint32_t *restrict colour_table)
-{
- uint32_t pixels = gif->width * height;
- uint32_t written = 0;
- gif_result ret = GIF_OK;
- lzw_result res;
+ break;
- /* Initialise the LZW decoding */
- res = lzw_decode_init_map(gif->lzw_ctx,
- minimum_code_size, transparency_index, colour_table,
- gif->gif_data, gif->buffer_size, gif->buffer_position);
- if (res != LZW_OK) {
- return gif_error_from_lzw(res);
- }
+ case GIF_EXTENSION_COMMENT:
+ /* Move the pointer to the first data sub-block Skip 1
+ * byte for the extension label. */
+ ++gif_data;
+ block_step = false;
+ break;
- frame_data += (offset_y * gif->width);
+ default:
+ break;
+ }
- while (pixels > 0) {
- res = lzw_decode_map(gif->lzw_ctx,
- frame_data, pixels, &written);
- pixels -= written;
- frame_data += written;
- if (res != LZW_OK) {
- /* Unexpected end of frame, try to recover */
- if (res == LZW_OK_EOD) {
- ret = GIF_OK;
- } else {
- ret = gif_error_from_lzw(res);
+ if (block_step) {
+ /* Move the pointer to the first data sub-block Skip 2
+ * bytes for the extension label and size fields Skip
+ * the extension size itself
+ */
+ if (gif_bytes < 2) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
}
- break;
+ gif_data += 2 + gif_data[1];
}
- }
- if (pixels == 0) {
- ret = GIF_OK;
+ /* Repeatedly skip blocks until we get a zero block or run out
+ * of data. This data is ignored by this gif decoder. */
+ while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) {
+ gif_data += gif_data[0] + 1;
+ if (gif_data >= gif_end) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
+ }
+ gif_data++;
+ gif_bytes = gif_end - gif_data;
}
- return ret;
-}
-
-static inline gif_result gif__decode(
- struct gif_animation *gif,
- struct gif_frame *frame,
- uint8_t minimum_code_size,
- uint32_t *restrict frame_data)
-{
- gif_result ret;
- uint32_t offset_x = frame->redraw_x;
- uint32_t offset_y = frame->redraw_y;
- uint32_t width = frame->redraw_width;
- uint32_t height = frame->redraw_height;
- uint32_t interlace = frame->flags & GIF_INTERLACE_MASK;
- uint32_t transparency_index = frame->transparency_index;
- uint32_t *restrict colour_table = gif->colour_table;
-
- if (interlace == false && width == gif->width && offset_x == 0) {
- ret = gif__decode_simple(gif, height, offset_y,
- minimum_code_size, transparency_index,
- frame_data, colour_table);
- } else {
- ret = gif__decode_complex(gif, width, height,
- offset_x, offset_y, interlace,
- minimum_code_size, transparency_index,
- frame_data, colour_table);
+ if (gif_data > gif_end) {
+ gif_data = gif_end;
}
- return ret;
+ /* Set buffer position and return */
+ gif->buffer_position = gif_data - gif->gif_data;
+ return GIF_OK;
}
/**
- * Restore a GIF to the background colour.
+ * Parse a GIF Image Descriptor.
*
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to clear, or NULL.
- * \param[in] bitmap The bitmap to clear the frame in.
+ * The format is:
+ *
+ * +0 CHAR Image Separator (0x2c)
+ * +1 SHORT Image Left Position
+ * +3 SHORT Image Top Position
+ * +5 SHORT Width
+ * +7 SHORT Height
+ * +9 CHAR __Packed Fields__
+ * 1BIT Local Colour Table Flag
+ * 1BIT Interlace Flag
+ * 1BIT Sort Flag
+ * 2BITS Reserved
+ * 3BITS Size of Local Colour Table
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to parse an image descriptor for.
+ * \return GIF_OK on success, appropriate error otherwise.
*/
-static void gif__restore_bg(
+static gif_result gif__parse_image_descriptor(
struct gif_animation *gif,
struct gif_frame *frame,
- uint32_t *bitmap)
+ bool decode)
{
- if (frame == NULL) {
- memset(bitmap, GIF_TRANSPARENT_COLOUR,
- gif->width * gif->height * sizeof(*bitmap));
- } else {
- uint32_t offset_x = frame->redraw_x;
- uint32_t offset_y = frame->redraw_y;
- uint32_t width = frame->redraw_width;
- uint32_t height = frame->redraw_height;
+ const uint8_t *data = gif->gif_data + gif->buffer_position;
+ size_t len = gif->buffer_size - gif->buffer_position;
+ enum {
+ GIF_IMAGE_DESCRIPTOR_LEN = 10u,
+ GIF_IMAGE_SEPARATOR = 0x2Cu,
+ };
- if (frame->display == false) {
- return;
- }
+ assert(gif != NULL);
+ assert(frame != NULL);
- if (frame->transparency) {
- for (uint32_t y = 0; y < height; y++) {
- uint32_t *scanline = bitmap + offset_x +
- (offset_y + y) * gif->width;
- memset(scanline, GIF_TRANSPARENT_COLOUR,
- width * sizeof(*bitmap));
- }
- } else {
- for (uint32_t y = 0; y < height; y++) {
- uint32_t *scanline = bitmap + offset_x +
- (offset_y + y) * gif->width;
- for (uint32_t x = 0; x < width; x++) {
- scanline[x] = gif->bg_colour;
- }
- }
+ if (len < GIF_IMAGE_DESCRIPTOR_LEN) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
+
+ if (decode) {
+ unsigned x, y, w, h;
+
+ if (data[0] != GIF_IMAGE_SEPARATOR) {
+ return GIF_FRAME_DATA_ERROR;
}
+
+ x = data[1] | (data[2] << 8);
+ y = data[3] | (data[4] << 8);
+ w = data[5] | (data[6] << 8);
+ h = data[7] | (data[8] << 8);
+ frame->flags = data[9];
+
+ frame->redraw_x = x;
+ frame->redraw_y = y;
+ frame->redraw_width = w;
+ frame->redraw_height = h;
+
+ /* Frame size may have grown. */
+ gif->width = (x + w > gif->width ) ? x + w : gif->width;
+ gif->height = (y + h > gif->height) ? y + h : gif->height;
}
+
+ gif->buffer_position += GIF_IMAGE_DESCRIPTOR_LEN;
+ return GIF_OK;
}
-static gif_result gif__update_bitmap(
+/**
+ * Get a frame's colour table.
+ *
+ * Sets up gif->colour_table for the frame.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to get the colour table for.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_colour_table(
struct gif_animation *gif,
struct gif_frame *frame,
- uint8_t minimum_code_size,
- uint32_t frame_idx)
+ bool decode)
{
- gif_result ret;
- uint32_t *bitmap;
-
- gif->decoded_frame = frame_idx;
-
- bitmap = gif__bitmap_get(gif);
- if (bitmap == NULL) {
- return GIF_INSUFFICIENT_MEMORY;
- }
-
- /* Handle any bitmap clearing/restoration required before decoding this
- * frame. */
- if (frame_idx == 0 || gif->decoded_frame == GIF_INVALID_FRAME) {
- gif__restore_bg(gif, NULL, bitmap);
-
- } else {
- struct gif_frame *prev = &gif->frames[frame_idx - 1];
+ const uint8_t *data = gif->gif_data + gif->buffer_position;
+ size_t len = gif->buffer_size - gif->buffer_position;
+ unsigned colour_table_size;
- if (prev->disposal_method == GIF_DISPOSAL_RESTORE_BG) {
- gif__restore_bg(gif, prev, bitmap);
+ assert(gif != NULL);
+ assert(frame != NULL);
- } else if (prev->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
- ret = gif__recover_frame(gif, bitmap);
- if (ret != GIF_OK) {
- gif__restore_bg(gif, prev, bitmap);
- }
- }
+ if ((frame->flags & GIF_COLOUR_TABLE_MASK) == 0) {
+ gif->colour_table = gif->global_colour_table;
+ return GIF_OK;
}
- if (frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
- /* Store the previous frame for later restoration */
- gif__record_frame(gif, bitmap);
+ colour_table_size = 2 << (frame->flags & GIF_COLOUR_TABLE_SIZE_MASK);
+ if (len < colour_table_size * 3) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
}
- ret = gif__decode(gif, frame, minimum_code_size, bitmap);
+ if (decode) {
+ int count = colour_table_size;
+ uint8_t *entry = (uint8_t *)gif->local_colour_table;
- gif__bitmap_modified(gif);
+ while (count--) {
+ /* Gif colour map contents are r,g,b.
+ *
+ * We want to pack them bytewise into the
+ * colour table, such that the red component
+ * is in byte 0 and the alpha component is in
+ * byte 3.
+ */
- if (frame->virgin) {
- frame->opaque = gif__bitmap_get_opaque(gif);
- frame->virgin = false;
+ *entry++ = *data++; /* r */
+ *entry++ = *data++; /* g */
+ *entry++ = *data++; /* b */
+ *entry++ = 0xff; /* a */
+ }
}
- gif__bitmap_set_opaque(gif, frame);
- return ret;
+ gif->buffer_position += colour_table_size * 3;
+ gif->colour_table = gif->local_colour_table;
+ return GIF_OK;
}
/**
@@ -1102,6 +925,180 @@ static gif_result gif__parse_image_data(
return ret;
}
+static struct gif_frame *gif__get_frame(
+ struct gif_animation *gif,
+ uint32_t frame_idx)
+{
+ struct gif_frame *frame;
+
+ if (gif->frame_holders > frame_idx) {
+ frame = &gif->frames[frame_idx];
+ } else {
+ /* Allocate more memory */
+ size_t count = frame_idx + 1;
+ struct gif_frame *temp;
+
+ temp = realloc(gif->frames, count * sizeof(*frame));
+ if (temp == NULL) {
+ return NULL;
+ }
+ gif->frames = temp;
+ gif->frame_holders = count;
+
+ frame = &gif->frames[frame_idx];
+
+ frame->transparency = false;
+ frame->transparency_index = GIF_NO_TRANSPARENCY;
+ frame->frame_pointer = gif->buffer_position;
+ frame->redraw_required = false;
+ frame->disposal_method = 0;
+ frame->frame_delay = 100;
+ frame->display = false;
+ frame->virgin = true;
+ }
+
+ return frame;
+}
+
+/**
+ * Attempts to initialise the next frame
+ *
+ * \param[in] gif The animation context
+ * \param[in] frame_idx The frame number to decode.
+ * \return error code
+ * - GIF_INSUFFICIENT_DATA for insufficient data to do anything
+ * - GIF_FRAME_DATA_ERROR for GIF frame data error
+ * - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
+ * - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
+ * - GIF_DATA_ERROR for GIF error (invalid frame header)
+ * - GIF_OK for successful decoding
+ * - GIF_WORKING for successful decoding if more frames are expected
+*/
+static gif_result gif_initialise_frame(
+ struct gif_animation *gif,
+ uint32_t frame_idx)
+{
+ gif_result ret;
+ struct gif_frame *frame;
+ uint8_t *gif_data, *gif_end;
+ int gif_bytes;
+ uint32_t block_size;
+
+ /* Get our buffer position etc. */
+ gif_data = (uint8_t *)(gif->gif_data + gif->buffer_position);
+ gif_end = (uint8_t *)(gif->gif_data + gif->buffer_size);
+ gif_bytes = (gif_end - gif_data);
+
+ /* Check if we've finished */
+ if ((gif_bytes > 0) && (gif_data[0] == GIF_TRAILER)) {
+ return GIF_OK;
+ }
+
+ /* Check if there is enough data remaining. The shortest block of data
+ * is a 4-byte comment extension + 1-byte block terminator + 1-byte gif
+ * trailer
+ */
+ if (gif_bytes < 6) {
+ return GIF_INSUFFICIENT_DATA;
+ }
+
+ /* We could theoretically get some junk data that gives us millions of
+ * frames, so we ensure that we don't have a silly number
+ */
+ if (frame_idx > 4096) {
+ return GIF_FRAME_DATA_ERROR;
+ }
+
+ frame = gif__get_frame(gif, frame_idx);
+ if (frame == NULL) {
+ return GIF_INSUFFICIENT_MEMORY;
+ }
+
+ /* We pretend to initialise the frames, but really we just skip over
+ * all the data contained within. This is all basically a cut down
+ * version of gif_decode_frame that doesn't have any of the LZW bits in
+ * it.
+ */
+
+ /* Initialise any extensions */
+ gif->buffer_position = gif_data - gif->gif_data;
+ ret = gif__parse_frame_extensions(gif, &gif->frames[frame_idx], true);
+ if (ret != GIF_OK) {
+ return ret;
+ }
+
+ ret = gif__parse_image_descriptor(gif, &gif->frames[frame_idx], true);
+ if (ret != GIF_OK) {
+ return ret;
+ }
+
+ ret = gif__parse_colour_table(gif, &gif->frames[frame_idx], false);
+ if (ret != GIF_OK) {
+ return ret;
+ }
+ gif_data = gif->gif_data + gif->buffer_position;
+ gif_bytes = (gif_end - gif_data);
+
+ /* Move our data onwards and remember we've got a bit of this frame */
+ gif->frame_count_partial = frame_idx + 1;
+
+ /* Ensure we have a correct code size */
+ if (gif_bytes < 1) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
+ if (gif_data[0] >= LZW_CODE_MAX) {
+ return GIF_DATA_ERROR;
+ }
+
+ /* Move our pointer to the actual image data */
+ gif_data++;
+ --gif_bytes;
+
+ /* Repeatedly skip blocks until we get a zero block or run out of data
+ * These blocks of image data are processed later by gif_decode_frame()
+ */
+ block_size = 0;
+ while (block_size != 1) {
+ if (gif_bytes < 1) return GIF_INSUFFICIENT_FRAME_DATA;
+ block_size = gif_data[0] + 1;
+ /* Check if the frame data runs off the end of the file */
+ if ((int)(gif_bytes - block_size) < 0) {
+ /* Try to recover by signaling the end of the gif.
+ * Once we get garbage data, there is no logical way to
+ * determine where the next frame is. It's probably
+ * better to partially load the gif than not at all.
+ */
+ if (gif_bytes >= 2) {
+ gif_data[0] = 0;
+ gif_data[1] = GIF_TRAILER;
+ gif_bytes = 1;
+ ++gif_data;
+ break;
+ } else {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
+ } else {
+ gif_bytes -= block_size;
+ gif_data += block_size;
+ }
+ }
+
+ /* Add the frame and set the display flag */
+ gif->buffer_position = gif_data - gif->gif_data;
+ gif->frame_count = frame_idx + 1;
+ gif->frames[frame_idx].display = true;
+
+ /* Check if we've finished */
+ if (gif_bytes < 1) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ } else {
+ if (gif_data[0] == GIF_TRAILER) {
+ return GIF_OK;
+ }
+ }
+ return GIF_WORKING;
+}
+
/**
* decode a gif frame
*
@@ -1109,9 +1106,9 @@ static gif_result gif__parse_image_data(
* \param frame The frame number to decode.
* \param clear_image flag for image data being cleared instead of plotted.
*/
-static gif_result
-gif_internal_decode_frame(gif_animation *gif,
- uint32_t frame_idx)
+static gif_result gif_internal_decode_frame(
+ struct gif_animation *gif,
+ uint32_t frame_idx)
{
gif_result ret;
uint8_t *gif_data;
commitdiff
http://git.netsurf-browser.org/libnsgif.git/commit/?id=677ba295b37cc1671f...
commit 677ba295b37cc1671f40383f92ad133fe6db26d9
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Split out image parsing.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index b527953..b95b912 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -960,6 +960,8 @@ static gif_result gif__update_bitmap(
gif_result ret;
uint32_t *bitmap;
+ gif->decoded_frame = frame_idx;
+
bitmap = gif__bitmap_get(gif);
if (bitmap == NULL) {
return GIF_INSUFFICIENT_MEMORY;
@@ -1003,6 +1005,104 @@ static gif_result gif__update_bitmap(
}
/**
+ * Parse the image data for a gif frame.
+ *
+ * Sets up gif->colour_table for the frame.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to get the colour table for.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_image_data(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ bool decode)
+{
+ uint8_t *data = gif->gif_data + gif->buffer_position;
+ size_t len = gif->buffer_size - gif->buffer_position;
+ uint32_t frame_idx = frame - gif->frames;
+ uint8_t minimum_code_size;
+ gif_result ret;
+
+ assert(gif != NULL);
+ assert(frame != NULL);
+
+ if (!decode) {
+ gif->frame_count_partial = frame_idx + 1;
+ }
+
+ /* Ensure sufficient data remains. A gif trailer or a minimum lzw code
+ * followed by a gif trailer is treated as OK, although without any
+ * image data. */
+ switch (len) {
+ default: if (data[0] == GIF_TRAILER) return GIF_OK;
+ break;
+ case 2: if (data[1] == GIF_TRAILER) return GIF_OK;
+ /* Fall through. */
+ case 1: if (data[0] == GIF_TRAILER) return GIF_OK;
+ /* Fall through. */
+ case 0: return GIF_INSUFFICIENT_FRAME_DATA;
+ }
+
+ minimum_code_size = data[0];
+ if (minimum_code_size >= LZW_CODE_MAX) {
+ return GIF_DATA_ERROR;
+ }
+ gif->buffer_position++;
+ data++;
+ len--;
+
+ if (decode) {
+ ret = gif__update_bitmap(gif, frame, minimum_code_size,
+ frame_idx);
+ } else {
+ uint32_t block_size = 0;
+
+ while (block_size != 1) {
+ if (len < 1) return GIF_INSUFFICIENT_FRAME_DATA;
+ block_size = data[0] + 1;
+ /* Check if the frame data runs off the end of the file */
+ if ((int)(len - block_size) < 0) {
+ /* Try to recover by signaling the end of the gif.
+ * Once we get garbage data, there is no logical way to
+ * determine where the next frame is. It's probably
+ * better to partially load the gif than not at all.
+ */
+ if (len >= 2) {
+ data[0] = 0;
+ data[1] = GIF_TRAILER;
+ len = 1;
+ data++;
+ break;
+ } else {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ }
+ } else {
+ len -= block_size;
+ data += block_size;
+ }
+ }
+
+ gif->buffer_position = data - gif->gif_data;
+ gif->frame_count = frame_idx + 1;
+ gif->frames[frame_idx].display = true;
+
+ /* Check if we've finished */
+ if (len < 1) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ } else {
+ if (data[0] == GIF_TRAILER) {
+ return GIF_OK;
+ }
+ }
+
+ return GIF_WORKING;
+ }
+
+ return ret;
+}
+
+/**
* decode a gif frame
*
* \param gif gif animation context.
@@ -1014,9 +1114,8 @@ gif_internal_decode_frame(gif_animation *gif,
uint32_t frame_idx)
{
gif_result ret;
+ uint8_t *gif_data;
struct gif_frame *frame;
- uint8_t *gif_data, *gif_end;
- int gif_bytes;
uint32_t save_buffer_position;
/* Ensure the frame is in range to decode */
@@ -1041,8 +1140,6 @@ gif_internal_decode_frame(gif_animation *gif,
/* Get the start of our frame data and the end of the GIF data */
gif_data = gif->gif_data + frame->frame_pointer;
- gif_end = gif->gif_data + gif->buffer_size;
- gif_bytes = (gif_end - gif_data);
/* Save the buffer position */
save_buffer_position = gif->buffer_position;
@@ -1061,44 +1158,14 @@ gif_internal_decode_frame(gif_animation *gif,
ret = gif__parse_colour_table(gif, frame, true);
if (ret != GIF_OK) {
- return ret;
- }
- gif_data = gif->gif_data + gif->buffer_position;
- gif_bytes = (gif_end - gif_data);
-
- /* Ensure sufficient data remains */
- if (gif_bytes < 1) {
- ret = GIF_INSUFFICIENT_FRAME_DATA;
- goto gif_decode_frame_exit;
- }
-
- /* check for an end marker */
- if (gif_data[0] == GIF_TRAILER) {
- ret = GIF_OK;
- goto gif_decode_frame_exit;
- }
-
- /* Ensure we have enough data for a 1-byte LZW code size +
- * 1-byte gif trailer
- */
- if (gif_bytes < 2) {
- ret = GIF_INSUFFICIENT_FRAME_DATA;
goto gif_decode_frame_exit;
}
- /* If we only have a 1-byte LZW code size + 1-byte gif trailer,
- * we're finished
- */
- if ((gif_bytes == 2) && (gif_data[1] == GIF_TRAILER)) {
- ret = GIF_OK;
+ ret = gif__parse_image_data(gif, frame, true);
+ if (ret != GIF_OK) {
goto gif_decode_frame_exit;
}
- gif->decoded_frame = frame_idx;
- gif->buffer_position = (gif_data - gif->gif_data) + 1;
-
- ret = gif__update_bitmap(gif, frame, gif_data[0], frame_idx);
-
gif_decode_frame_exit:
/* Restore the buffer position */
-----------------------------------------------------------------------
Summary of changes:
src/libnsgif.c | 217 ++++++++++++++++++++++----------------------------------
1 file changed, 86 insertions(+), 131 deletions(-)
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 1280798..2a1fdfe 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -271,7 +271,7 @@ static gif_result gif__decode_complex(
uint32_t offset_x,
uint32_t offset_y,
uint32_t interlace,
- uint8_t minimum_code_size,
+ const uint8_t *data,
uint32_t transparency_index,
uint32_t *restrict frame_data,
uint32_t *restrict colour_table)
@@ -281,8 +281,9 @@ static gif_result gif__decode_complex(
lzw_result res;
/* Initialise the LZW decoding */
- res = lzw_decode_init(gif->lzw_ctx, minimum_code_size,
- gif->gif_data, gif->buffer_size, gif->buffer_position);
+ res = lzw_decode_init(gif->lzw_ctx, data[0],
+ gif->gif_data, gif->buffer_size,
+ data + 1 - gif->gif_data);
if (res != LZW_OK) {
return gif_error_from_lzw(res);
}
@@ -345,7 +346,7 @@ static gif_result gif__decode_simple(
struct gif_animation *gif,
uint32_t height,
uint32_t offset_y,
- uint8_t minimum_code_size,
+ const uint8_t *data,
uint32_t transparency_index,
uint32_t *restrict frame_data,
uint32_t *restrict colour_table)
@@ -356,9 +357,10 @@ static gif_result gif__decode_simple(
lzw_result res;
/* Initialise the LZW decoding */
- res = lzw_decode_init_map(gif->lzw_ctx,
- minimum_code_size, transparency_index, colour_table,
- gif->gif_data, gif->buffer_size, gif->buffer_position);
+ res = lzw_decode_init_map(gif->lzw_ctx, data[0],
+ transparency_index, colour_table,
+ gif->gif_data, gif->buffer_size,
+ data + 1 - gif->gif_data);
if (res != LZW_OK) {
return gif_error_from_lzw(res);
}
@@ -391,7 +393,7 @@ static gif_result gif__decode_simple(
static inline gif_result gif__decode(
struct gif_animation *gif,
struct gif_frame *frame,
- uint8_t minimum_code_size,
+ const uint8_t *data,
uint32_t *restrict frame_data)
{
gif_result ret;
@@ -405,12 +407,12 @@ static inline gif_result gif__decode(
if (interlace == false && width == gif->width && offset_x == 0) {
ret = gif__decode_simple(gif, height, offset_y,
- minimum_code_size, transparency_index,
+ data, transparency_index,
frame_data, colour_table);
} else {
ret = gif__decode_complex(gif, width, height,
offset_x, offset_y, interlace,
- minimum_code_size, transparency_index,
+ data, transparency_index,
frame_data, colour_table);
}
@@ -464,7 +466,7 @@ static void gif__restore_bg(
static gif_result gif__update_bitmap(
struct gif_animation *gif,
struct gif_frame *frame,
- uint8_t minimum_code_size,
+ const uint8_t *data,
uint32_t frame_idx)
{
gif_result ret;
@@ -501,7 +503,7 @@ static gif_result gif__update_bitmap(
gif__record_frame(gif, bitmap);
}
- ret = gif__decode(gif, frame, minimum_code_size, bitmap);
+ ret = gif__decode(gif, frame, data, bitmap);
gif__bitmap_modified(gif);
@@ -617,15 +619,12 @@ static gif_result gif__parse_extension_application(
static gif_result gif__parse_frame_extensions(
struct gif_animation *gif,
struct gif_frame *frame,
+ uint8_t **pos,
bool decode)
{
- uint8_t *gif_data, *gif_end;
- int gif_bytes;
-
- /* Get our buffer position etc. */
- gif_data = gif->gif_data + gif->buffer_position;
- gif_end = gif->gif_data + gif->buffer_size;
- gif_bytes = gif_end - gif_data;
+ uint8_t *gif_data = *pos;
+ uint8_t *gif_end = gif->gif_data + gif->buffer_size;
+ int gif_bytes = gif_end - gif_data;
/* Initialise the extensions */
while (gif_bytes > 0 && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
@@ -700,7 +699,7 @@ static gif_result gif__parse_frame_extensions(
}
/* Set buffer position and return */
- gif->buffer_position = gif_data - gif->gif_data;
+ *pos = gif_data;
return GIF_OK;
}
@@ -728,10 +727,11 @@ static gif_result gif__parse_frame_extensions(
static gif_result gif__parse_image_descriptor(
struct gif_animation *gif,
struct gif_frame *frame,
+ uint8_t **pos,
bool decode)
{
- const uint8_t *data = gif->gif_data + gif->buffer_position;
- size_t len = gif->buffer_size - gif->buffer_position;
+ const uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
enum {
GIF_IMAGE_DESCRIPTOR_LEN = 10u,
GIF_IMAGE_SEPARATOR = 0x2Cu,
@@ -767,7 +767,7 @@ static gif_result gif__parse_image_descriptor(
gif->height = (y + h > gif->height) ? y + h : gif->height;
}
- gif->buffer_position += GIF_IMAGE_DESCRIPTOR_LEN;
+ *pos += GIF_IMAGE_DESCRIPTOR_LEN;
return GIF_OK;
}
@@ -783,11 +783,12 @@ static gif_result gif__parse_image_descriptor(
static gif_result gif__parse_colour_table(
struct gif_animation *gif,
struct gif_frame *frame,
+ uint8_t **pos,
bool decode)
{
- const uint8_t *data = gif->gif_data + gif->buffer_position;
- size_t len = gif->buffer_size - gif->buffer_position;
unsigned colour_table_size;
+ const uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
assert(gif != NULL);
assert(frame != NULL);
@@ -822,8 +823,8 @@ static gif_result gif__parse_colour_table(
}
}
- gif->buffer_position += colour_table_size * 3;
gif->colour_table = gif->local_colour_table;
+ *pos += colour_table_size * 3;
return GIF_OK;
}
@@ -839,10 +840,11 @@ static gif_result gif__parse_colour_table(
static gif_result gif__parse_image_data(
struct gif_animation *gif,
struct gif_frame *frame,
+ uint8_t **pos,
bool decode)
{
- uint8_t *data = gif->gif_data + gif->buffer_position;
- size_t len = gif->buffer_size - gif->buffer_position;
+ uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
uint32_t frame_idx = frame - gif->frames;
uint8_t minimum_code_size;
gif_result ret;
@@ -858,6 +860,8 @@ static gif_result gif__parse_image_data(
* followed by a gif trailer is treated as OK, although without any
* image data. */
switch (len) {
+ default: if (data[0] == GIF_TRAILER) return GIF_OK;
+ break;
case 2: if (data[1] == GIF_TRAILER) return GIF_OK;
/* Fall through. */
case 1: if (data[0] == GIF_TRAILER) return GIF_OK;
@@ -869,16 +873,16 @@ static gif_result gif__parse_image_data(
if (minimum_code_size >= LZW_CODE_MAX) {
return GIF_DATA_ERROR;
}
- gif->buffer_position++;
- data++;
- len--;
if (decode) {
- ret = gif__update_bitmap(gif, frame, minimum_code_size,
- frame_idx);
+ ret = gif__update_bitmap(gif, frame, data, frame_idx);
} else {
uint32_t block_size = 0;
+ /* Skip the minimum code size. */
+ data++;
+ len--;
+
while (block_size != 1) {
if (len < 1) return GIF_INSUFFICIENT_FRAME_DATA;
block_size = data[0] + 1;
@@ -904,9 +908,9 @@ static gif_result gif__parse_image_data(
}
}
- gif->buffer_position = data - gif->gif_data;
gif->frame_count = frame_idx + 1;
gif->frames[frame_idx].display = true;
+ *pos = data;
/* Check if we've finished */
if (len < 1) {
@@ -963,6 +967,7 @@ static struct gif_frame *gif__get_frame(
*
* \param[in] gif The animation context
* \param[in] frame_idx The frame number to decode.
+ * \param[in] decode Whether to decode the graphical image data.
* \return error code
* - GIF_INSUFFICIENT_DATA for insufficient data to do anything
* - GIF_FRAME_DATA_ERROR for GIF frame data error
@@ -972,135 +977,85 @@ static struct gif_frame *gif__get_frame(
* - GIF_OK for successful decoding
* - GIF_WORKING for successful decoding if more frames are expected
*/
-static gif_result gif_initialise_frame(
+static gif_result gif__process_frame(
struct gif_animation *gif,
- uint32_t frame_idx)
+ uint32_t frame_idx,
+ bool decode)
{
+ uint8_t *pos;
+ uint8_t *end;
gif_result ret;
struct gif_frame *frame;
- uint8_t *gif_data, *gif_end;
- int gif_bytes;
-
- /* Get our buffer position etc. */
- gif_data = (uint8_t *)(gif->gif_data + gif->buffer_position);
- gif_end = (uint8_t *)(gif->gif_data + gif->buffer_size);
- gif_bytes = (gif_end - gif_data);
-
- /* Check if we've finished */
- if ((gif_bytes > 0) && (gif_data[0] == GIF_TRAILER)) {
- return GIF_OK;
- }
-
- /* We could theoretically get some junk data that gives us millions of
- * frames, so we ensure that we don't have a silly number
- */
- if (frame_idx > 4096) {
- return GIF_FRAME_DATA_ERROR;
- }
frame = gif__get_frame(gif, frame_idx);
if (frame == NULL) {
return GIF_INSUFFICIENT_MEMORY;
}
- /* Initialise any extensions */
- gif->buffer_position = gif_data - gif->gif_data;
- ret = gif__parse_frame_extensions(gif, &gif->frames[frame_idx], true);
- if (ret != GIF_OK) {
- return ret;
- }
-
- ret = gif__parse_image_descriptor(gif, &gif->frames[frame_idx], true);
- if (ret != GIF_OK) {
- return ret;
- }
-
- ret = gif__parse_colour_table(gif, &gif->frames[frame_idx], false);
- if (ret != GIF_OK) {
- return ret;
- }
+ end = (uint8_t *)(gif->gif_data + gif->buffer_size);
- ret = gif__parse_image_data(gif, &gif->frames[frame_idx], false);
- if (ret != GIF_OK) {
- return ret;
- }
-
- return GIF_OK;
-}
+ if (decode) {
+ pos = gif->gif_data + frame->frame_pointer;
-/**
- * decode a gif frame
- *
- * \param gif gif animation context.
- * \param frame The frame number to decode.
- * \param clear_image flag for image data being cleared instead of plotted.
- */
-static gif_result gif_internal_decode_frame(
- struct gif_animation *gif,
- uint32_t frame_idx)
-{
- gif_result ret;
- uint8_t *gif_data;
- struct gif_frame *frame;
- uint32_t save_buffer_position;
+ /* Ensure this frame is supposed to be decoded */
+ if (frame->display == false) {
+ return GIF_OK;
+ }
- /* Ensure the frame is in range to decode */
- if (frame_idx > gif->frame_count_partial) {
- return GIF_INSUFFICIENT_DATA;
- }
+ /* Ensure the frame is in range to decode */
+ if (frame_idx > gif->frame_count_partial) {
+ return GIF_INSUFFICIENT_DATA;
+ }
- /* Done if frame is already decoded */
- if (((int)frame_idx == gif->decoded_frame)) {
- return GIF_OK;
- }
+ /* Done if frame is already decoded */
+ if ((int)frame_idx == gif->decoded_frame) {
+ return GIF_OK;
+ }
+ } else {
+ pos = (uint8_t *)(gif->gif_data + gif->buffer_position);
- frame = gif__get_frame(gif, frame_idx);
- if (frame == NULL) {
- return GIF_INSUFFICIENT_MEMORY;
- }
+ /* Check if we've finished */
+ if (pos < end && pos[0] == GIF_TRAILER) {
+ return GIF_OK;
+ }
- /* Ensure this frame is supposed to be decoded */
- if (frame->display == false) {
- return GIF_OK;
+ /* We could theoretically get some junk data that gives us
+ * millions of frames, so we ensure that we don't have a
+ * silly number. */
+ if (frame_idx > 4096) {
+ return GIF_FRAME_DATA_ERROR;
+ }
}
- /* Get the start of our frame data and the end of the GIF data */
- gif_data = gif->gif_data + frame->frame_pointer;
-
- /* Save the buffer position */
- save_buffer_position = gif->buffer_position;
- gif->buffer_position = gif_data - gif->gif_data;
-
- /* Skip any extensions because they have already been processed */
- ret = gif__parse_frame_extensions(gif, frame, false);
+ /* Initialise any extensions */
+ ret = gif__parse_frame_extensions(gif, frame, &pos, !decode);
if (ret != GIF_OK) {
- goto gif_decode_frame_exit;
+ goto cleanup;
}
- ret = gif__parse_image_descriptor(gif, frame, false);
+ ret = gif__parse_image_descriptor(gif, frame, &pos, !decode);
if (ret != GIF_OK) {
- goto gif_decode_frame_exit;
+ goto cleanup;
}
- ret = gif__parse_colour_table(gif, frame, true);
+ ret = gif__parse_colour_table(gif, frame, &pos, decode);
if (ret != GIF_OK) {
- goto gif_decode_frame_exit;
+ goto cleanup;
}
- ret = gif__parse_image_data(gif, frame, true);
+ ret = gif__parse_image_data(gif, frame, &pos, decode);
if (ret != GIF_OK) {
- goto gif_decode_frame_exit;
+ goto cleanup;
}
-gif_decode_frame_exit:
-
- /* Restore the buffer position */
- gif->buffer_position = save_buffer_position;
+cleanup:
+ if (!decode) {
+ gif->buffer_position = pos - gif->gif_data;
+ }
return ret;
}
-
/* exported function documented in libnsgif.h */
void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks)
{
@@ -1298,7 +1253,7 @@ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned
char *data)
}
/* Repeatedly try to initialise frames */
- while ((ret = gif_initialise_frame(gif, gif->frame_count)) == GIF_WORKING);
+ while ((ret = gif__process_frame(gif, gif->frame_count, false)) == GIF_WORKING);
/* If there was a memory error tell the caller */
if ((ret == GIF_INSUFFICIENT_MEMORY) ||
@@ -1321,7 +1276,7 @@ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned
char *data)
/* exported function documented in libnsgif.h */
gif_result gif_decode_frame(gif_animation *gif, unsigned int frame)
{
- return gif_internal_decode_frame(gif, frame);
+ return gif__process_frame(gif, frame, true);
}
--
NetSurf GIF Decoder