Gitweb links:

...log 
http://git.netsurf-browser.org/libnsgif.git/shortlog/5e0642811e2edce8d8845fdf0bcb8f9eefc71f80
...commit 
http://git.netsurf-browser.org/libnsgif.git/commit/5e0642811e2edce8d8845fdf0bcb8f9eefc71f80
...tree 
http://git.netsurf-browser.org/libnsgif.git/tree/5e0642811e2edce8d8845fdf0bcb8f9eefc71f80

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=5e0642811e2edce8d8845fdf0bcb8f9eefc71f80
commit 5e0642811e2edce8d8845fdf0bcb8f9eefc71f80
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    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=9f5e2f1af86d35247895dd57bf03f32c5539a04d
commit 9f5e2f1af86d35247895dd57bf03f32c5539a04d
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    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=5e93ea19730e0df42e9dc3a777cde466f5a8f73d
commit 5e93ea19730e0df42e9dc3a777cde466f5a8f73d
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    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=920ba6c423181ee0eec932c5daeda75fb13fc140
commit 920ba6c423181ee0eec932c5daeda75fb13fc140
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    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=1b8341cb075731cffbf69141c24d8954fef4ee50
commit 1b8341cb075731cffbf69141c24d8954fef4ee50
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    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=8a5130c5559989c3b9ea6f74469427abe08c7193
commit 8a5130c5559989c3b9ea6f74469427abe08c7193
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    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=82d8eff773bbb56cd2021187949e39fcc28899c7
commit 82d8eff773bbb56cd2021187949e39fcc28899c7
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    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=677ba295b37cc1671f40383f92ad133fe6db26d9
commit 677ba295b37cc1671f40383f92ad133fe6db26d9
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    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
_______________________________________________
netsurf-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to