summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Drake <tlsa@netsurf-browser.org>2021-11-03 20:35:13 +0000
committerMichael Drake <tlsa@netsurf-browser.org>2021-11-04 10:51:16 +0000
commit82d8eff773bbb56cd2021187949e39fcc28899c7 (patch)
tree8defae8ca2aba3d77eeeeba1173d4b1f87f2bbfd
parent677ba295b37cc1671f40383f92ad133fe6db26d9 (diff)
downloadlibnsgif-82d8eff773bbb56cd2021187949e39fcc28899c7.tar.gz
libnsgif-82d8eff773bbb56cd2021187949e39fcc28899c7.tar.bz2
GIF: Reorder functions so frame initialise can use image decoder.
-rw-r--r--src/libnsgif.c1049
1 files changed, 523 insertions, 526 deletions
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,6 +114,407 @@ gif_initialise_sprite(gif_animation *gif,
}
/**
+ * Helper to get the rendering bitmap for a gif.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \return Client pixel buffer for rendering into.
+ */
+static inline uint32_t* gif__bitmap_get(
+ struct gif_animation *gif)
+{
+ gif_result ret;
+
+ /* Make sure we have a buffer to decode to. */
+ ret = gif_initialise_sprite(gif, gif->width, gif->height);
+ if (ret != GIF_OK) {
+ return NULL;
+ }
+
+ /* Get the frame data */
+ assert(gif->bitmap_callbacks.bitmap_get_buffer);
+ return (uint32_t *)gif->bitmap_callbacks.bitmap_get_buffer(
+ gif->frame_image);
+}
+
+/**
+ * Helper to tell the client that their bitmap was modified.
+ *
+ * \param[in] gif The gif object we're decoding.
+ */
+static inline void gif__bitmap_modified(
+ const struct gif_animation *gif)
+{
+ if (gif->bitmap_callbacks.bitmap_modified) {
+ gif->bitmap_callbacks.bitmap_modified(gif->frame_image);
+ }
+}
+
+/**
+ * Helper to tell the client that whether the bitmap is opaque.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frmae The frame that has been decoded.
+ */
+static inline void gif__bitmap_set_opaque(
+ const struct gif_animation *gif,
+ const struct gif_frame *frame)
+{
+ if (gif->bitmap_callbacks.bitmap_set_opaque) {
+ gif->bitmap_callbacks.bitmap_set_opaque(
+ gif->frame_image, frame->opaque);
+ }
+}
+
+/**
+ * Helper to get the client to determine if the bitmap is opaque.
+ *
+ * \todo: We don't really need to get the client to do this for us.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \return true if the bitmap is opaque, false otherwise.
+ */
+static inline bool gif__bitmap_get_opaque(
+ const struct gif_animation *gif)
+{
+ if (gif->bitmap_callbacks.bitmap_test_opaque) {
+ return gif->bitmap_callbacks.bitmap_test_opaque(
+ gif->frame_image);
+ }
+
+ return false;
+}
+
+static void gif__record_frame(
+ struct gif_animation *gif,
+ const uint32_t *bitmap)
+{
+ bool need_alloc = gif->prev_frame == NULL;
+ uint32_t *prev_frame;
+
+ if (gif->decoded_frame == GIF_INVALID_FRAME ||
+ gif->decoded_frame == gif->prev_index) {
+ /* No frame to copy, or already have this frame recorded. */
+ return;
+ }
+
+ bitmap = gif__bitmap_get(gif);
+ if (bitmap == NULL) {
+ return;
+ }
+
+ if (gif->prev_frame != NULL &&
+ gif->width * gif->height > gif->prev_width * gif->prev_height) {
+ need_alloc = true;
+ }
+
+ if (need_alloc) {
+ prev_frame = realloc(gif->prev_frame,
+ gif->width * gif->height * 4);
+ if (prev_frame == NULL) {
+ return;
+ }
+ } else {
+ prev_frame = gif->prev_frame;
+ }
+
+ memcpy(prev_frame, bitmap, gif->width * gif->height * 4);
+
+ gif->prev_frame = prev_frame;
+ gif->prev_width = gif->width;
+ gif->prev_height = gif->height;
+ gif->prev_index = gif->decoded_frame;
+}
+
+static gif_result gif__recover_frame(
+ const struct gif_animation *gif,
+ uint32_t *bitmap)
+{
+ const uint32_t *prev_frame = gif->prev_frame;
+ unsigned height = gif->height < gif->prev_height ? gif->height : gif->prev_height;
+ unsigned width = gif->width < gif->prev_width ? gif->width : gif->prev_width;
+
+ if (prev_frame == NULL) {
+ return GIF_FRAME_DATA_ERROR;
+ }
+
+ for (unsigned y = 0; y < height; y++) {
+ memcpy(bitmap, prev_frame, width * 4);
+
+ bitmap += gif->width;
+ prev_frame += gif->prev_width;
+ }
+
+ return GIF_OK;
+}
+
+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;
+ }
+ y -= ((height + 1) >> 2);
+ return (y << 1) + 1;
+}
+
+static gif_result gif__decode_complex(
+ struct gif_animation *gif,
+ uint32_t width,
+ uint32_t height,
+ uint32_t offset_x,
+ uint32_t offset_y,
+ uint32_t interlace,
+ uint8_t minimum_code_size,
+ uint32_t transparency_index,
+ uint32_t *restrict frame_data,
+ uint32_t *restrict colour_table)
+{
+ uint32_t available = 0;
+ gif_result ret = GIF_OK;
+ lzw_result res;
+
+ /* Initialise the LZW decoding */
+ res = lzw_decode_init(gif->lzw_ctx, minimum_code_size,
+ gif->gif_data, gif->buffer_size, gif->buffer_position);
+ if (res != LZW_OK) {
+ return gif_error_from_lzw(res);
+ }
+
+ 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;
+ }
+ 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;
+ }
+ res = lzw_decode(gif->lzw_ctx,
+ &uncompressed, &available);
+ }
+
+ 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 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;
+
+ /* 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);
+ }
+
+ frame_data += (offset_y * gif->width);
+
+ while (pixels > 0) {
+ res = lzw_decode_map(gif->lzw_ctx,
+ frame_data, pixels, &written);
+ pixels -= written;
+ frame_data += written;
+ if (res != LZW_OK) {
+ /* Unexpected end of frame, try to recover */
+ if (res == LZW_OK_EOD) {
+ ret = GIF_OK;
+ } else {
+ ret = gif_error_from_lzw(res);
+ }
+ break;
+ }
+ }
+
+ if (pixels == 0) {
+ ret = GIF_OK;
+ }
+
+ 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);
+ }
+
+ return ret;
+}
+
+/**
+ * Restore a GIF to the background colour.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to clear, or NULL.
+ * \param[in] bitmap The bitmap to clear the frame in.
+ */
+static void gif__restore_bg(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ uint32_t *bitmap)
+{
+ if (frame == NULL) {
+ memset(bitmap, GIF_TRANSPARENT_COLOUR,
+ gif->width * gif->height * sizeof(*bitmap));
+ } else {
+ uint32_t offset_x = frame->redraw_x;
+ uint32_t offset_y = frame->redraw_y;
+ uint32_t width = frame->redraw_width;
+ uint32_t height = frame->redraw_height;
+
+ if (frame->display == false) {
+ return;
+ }
+
+ if (frame->transparency) {
+ for (uint32_t y = 0; y < height; y++) {
+ uint32_t *scanline = bitmap + offset_x +
+ (offset_y + y) * gif->width;
+ memset(scanline, GIF_TRANSPARENT_COLOUR,
+ width * sizeof(*bitmap));
+ }
+ } else {
+ for (uint32_t y = 0; y < height; y++) {
+ uint32_t *scanline = bitmap + offset_x +
+ (offset_y + y) * gif->width;
+ for (uint32_t x = 0; x < width; x++) {
+ scanline[x] = gif->bg_colour;
+ }
+ }
+ }
+ }
+}
+
+static gif_result gif__update_bitmap(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ uint8_t minimum_code_size,
+ uint32_t frame_idx)
+{
+ gif_result ret;
+ 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];
+
+ if (prev->disposal_method == GIF_DISPOSAL_RESTORE_BG) {
+ gif__restore_bg(gif, prev, bitmap);
+
+ } else if (prev->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
+ ret = gif__recover_frame(gif, bitmap);
+ if (ret != GIF_OK) {
+ gif__restore_bg(gif, prev, bitmap);
+ }
+ }
+ }
+
+ if (frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
+ /* Store the previous frame for later restoration */
+ gif__record_frame(gif, bitmap);
+ }
+
+ ret = gif__decode(gif, frame, minimum_code_size, bitmap);
+
+ gif__bitmap_modified(gif);
+
+ if (frame->virgin) {
+ frame->opaque = gif__bitmap_get_opaque(gif);
+ frame->virgin = false;
+ }
+ gif__bitmap_set_opaque(gif, frame);
+
+ return ret;
+}
+
+/**
* Parse the application extension
*
* \param[in] frame The gif object we're decoding.
@@ -411,6 +827,104 @@ static gif_result gif__parse_colour_table(
return GIF_OK;
}
+/**
+ * Parse the image data for a gif frame.
+ *
+ * Sets up gif->colour_table for the frame.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to get the colour table for.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_image_data(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ 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;
+}
+
static struct gif_frame *gif__get_frame(
struct gif_animation *gif,
uint32_t frame_idx)
@@ -460,7 +974,8 @@ 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(gif_animation *gif,
+static gif_result gif_initialise_frame(
+ struct gif_animation *gif,
uint32_t frame_idx)
{
gif_result ret;
@@ -584,524 +1099,6 @@ static gif_result gif_initialise_frame(gif_animation *gif,
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;
- }
- 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];
-}
-
-/**
- * Helper to get the rendering bitmap for a gif.
- *
- * \param[in] gif The gif object we're decoding.
- * \return Client pixel buffer for rendering into.
- */
-static inline uint32_t* gif__bitmap_get(
- struct gif_animation *gif)
-{
- gif_result ret;
-
- /* Make sure we have a buffer to decode to. */
- ret = gif_initialise_sprite(gif, gif->width, gif->height);
- if (ret != GIF_OK) {
- return NULL;
- }
-
- /* Get the frame data */
- assert(gif->bitmap_callbacks.bitmap_get_buffer);
- return (uint32_t *)gif->bitmap_callbacks.bitmap_get_buffer(
- gif->frame_image);
-}
-
-/**
- * Helper to tell the client that their bitmap was modified.
- *
- * \param[in] gif The gif object we're decoding.
- */
-static inline void gif__bitmap_modified(
- const struct gif_animation *gif)
-{
- if (gif->bitmap_callbacks.bitmap_modified) {
- gif->bitmap_callbacks.bitmap_modified(gif->frame_image);
- }
-}
-
-/**
- * Helper to tell the client that whether the bitmap is opaque.
- *
- * \param[in] gif The gif object we're decoding.
- * \param[in] frmae The frame that has been decoded.
- */
-static inline void gif__bitmap_set_opaque(
- const struct gif_animation *gif,
- const struct gif_frame *frame)
-{
- if (gif->bitmap_callbacks.bitmap_set_opaque) {
- gif->bitmap_callbacks.bitmap_set_opaque(
- gif->frame_image, frame->opaque);
- }
-}
-
-/**
- * Helper to get the client to determine if the bitmap is opaque.
- *
- * \todo: We don't really need to get the client to do this for us.
- *
- * \param[in] gif The gif object we're decoding.
- * \return true if the bitmap is opaque, false otherwise.
- */
-static inline bool gif__bitmap_get_opaque(
- const struct gif_animation *gif)
-{
- if (gif->bitmap_callbacks.bitmap_test_opaque) {
- return gif->bitmap_callbacks.bitmap_test_opaque(
- gif->frame_image);
- }
-
- return false;
-}
-
-static void gif__record_frame(
- struct gif_animation *gif,
- const uint32_t *bitmap)
-{
- bool need_alloc = gif->prev_frame == NULL;
- uint32_t *prev_frame;
-
- if (gif->decoded_frame == GIF_INVALID_FRAME ||
- gif->decoded_frame == gif->prev_index) {
- /* No frame to copy, or already have this frame recorded. */
- return;
- }
-
- bitmap = gif__bitmap_get(gif);
- if (bitmap == NULL) {
- return;
- }
-
- if (gif->prev_frame != NULL &&
- gif->width * gif->height > gif->prev_width * gif->prev_height) {
- need_alloc = true;
- }
-
- if (need_alloc) {
- prev_frame = realloc(gif->prev_frame,
- gif->width * gif->height * 4);
- if (prev_frame == NULL) {
- return;
- }
- } else {
- prev_frame = gif->prev_frame;
- }
-
- memcpy(prev_frame, bitmap, gif->width * gif->height * 4);
-
- gif->prev_frame = prev_frame;
- gif->prev_width = gif->width;
- gif->prev_height = gif->height;
- gif->prev_index = gif->decoded_frame;
-}
-
-static gif_result gif__recover_frame(
- const struct gif_animation *gif,
- uint32_t *bitmap)
-{
- const uint32_t *prev_frame = gif->prev_frame;
- unsigned height = gif->height < gif->prev_height ? gif->height : gif->prev_height;
- unsigned width = gif->width < gif->prev_width ? gif->width : gif->prev_width;
-
- if (prev_frame == NULL) {
- return GIF_FRAME_DATA_ERROR;
- }
-
- for (unsigned y = 0; y < height; y++) {
- memcpy(bitmap, prev_frame, width * 4);
-
- bitmap += gif->width;
- prev_frame += gif->prev_width;
- }
-
- return GIF_OK;
-}
-
-static gif_result
-gif__decode_complex(
- struct gif_animation *gif,
- uint32_t width,
- uint32_t height,
- uint32_t offset_x,
- uint32_t offset_y,
- uint32_t interlace,
- uint8_t minimum_code_size,
- uint32_t transparency_index,
- uint32_t *restrict frame_data,
- uint32_t *restrict colour_table)
-{
- uint32_t available = 0;
- gif_result ret = GIF_OK;
- lzw_result res;
-
- /* Initialise the LZW decoding */
- res = lzw_decode_init(gif->lzw_ctx, minimum_code_size,
- gif->gif_data, gif->buffer_size, gif->buffer_position);
- if (res != LZW_OK) {
- return gif_error_from_lzw(res);
- }
-
- 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;
- }
- 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;
- }
- res = lzw_decode(gif->lzw_ctx,
- &uncompressed, &available);
- }
-
- 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 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;
-
- /* 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);
- }
-
- frame_data += (offset_y * gif->width);
-
- while (pixels > 0) {
- res = lzw_decode_map(gif->lzw_ctx,
- frame_data, pixels, &written);
- pixels -= written;
- frame_data += written;
- if (res != LZW_OK) {
- /* Unexpected end of frame, try to recover */
- if (res == LZW_OK_EOD) {
- ret = GIF_OK;
- } else {
- ret = gif_error_from_lzw(res);
- }
- break;
- }
- }
-
- if (pixels == 0) {
- ret = GIF_OK;
- }
-
- 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);
- }
-
- return ret;
-}
-
-/**
- * Restore a GIF to the background colour.
- *
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to clear, or NULL.
- * \param[in] bitmap The bitmap to clear the frame in.
- */
-static void gif__restore_bg(
- struct gif_animation *gif,
- struct gif_frame *frame,
- uint32_t *bitmap)
-{
- if (frame == NULL) {
- memset(bitmap, GIF_TRANSPARENT_COLOUR,
- gif->width * gif->height * sizeof(*bitmap));
- } else {
- uint32_t offset_x = frame->redraw_x;
- uint32_t offset_y = frame->redraw_y;
- uint32_t width = frame->redraw_width;
- uint32_t height = frame->redraw_height;
-
- if (frame->display == false) {
- return;
- }
-
- if (frame->transparency) {
- for (uint32_t y = 0; y < height; y++) {
- uint32_t *scanline = bitmap + offset_x +
- (offset_y + y) * gif->width;
- memset(scanline, GIF_TRANSPARENT_COLOUR,
- width * sizeof(*bitmap));
- }
- } else {
- for (uint32_t y = 0; y < height; y++) {
- uint32_t *scanline = bitmap + offset_x +
- (offset_y + y) * gif->width;
- for (uint32_t x = 0; x < width; x++) {
- scanline[x] = gif->bg_colour;
- }
- }
- }
- }
-}
-
-static gif_result gif__update_bitmap(
- struct gif_animation *gif,
- struct gif_frame *frame,
- uint8_t minimum_code_size,
- uint32_t frame_idx)
-{
- gif_result ret;
- 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];
-
- if (prev->disposal_method == GIF_DISPOSAL_RESTORE_BG) {
- gif__restore_bg(gif, prev, bitmap);
-
- } else if (prev->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
- ret = gif__recover_frame(gif, bitmap);
- if (ret != GIF_OK) {
- gif__restore_bg(gif, prev, bitmap);
- }
- }
- }
-
- if (frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
- /* Store the previous frame for later restoration */
- gif__record_frame(gif, bitmap);
- }
-
- ret = gif__decode(gif, frame, minimum_code_size, bitmap);
-
- gif__bitmap_modified(gif);
-
- if (frame->virgin) {
- frame->opaque = gif__bitmap_get_opaque(gif);
- frame->virgin = false;
- }
- gif__bitmap_set_opaque(gif, frame);
-
- return ret;
-}
-
-/**
- * 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
*
@@ -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;