summaryrefslogtreecommitdiff
path: root/src/libnsgif.c
diff options
context:
space:
mode:
authorMichael Drake <tlsa@netsurf-browser.org>2021-04-04 23:40:01 +0100
committerMichael Drake <tlsa@netsurf-browser.org>2021-04-18 17:28:12 +0100
commitdecfeceb211697b18b2525ec533a95ed03418e1d (patch)
tree5ea3d3b1b6b7251d9df87d48021a88a58a58798f /src/libnsgif.c
parentdb49e5b61beb1759469f3e6bd53850c6bc0a38ac (diff)
downloadlibnsgif-decfeceb211697b18b2525ec533a95ed03418e1d.tar.gz
libnsgif-decfeceb211697b18b2525ec533a95ed03418e1d.tar.bz2
lzw: Direct output into frame data, avoiding stack.
If the frame is non-interlaced, and has the same rowstride as the full image, then we can decode lzw directly into the output image.
Diffstat (limited to 'src/libnsgif.c')
-rw-r--r--src/libnsgif.c83
1 files changed, 81 insertions, 2 deletions
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 752f8c2..3c49218 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -626,8 +626,8 @@ static gif_result gif__recover_previous_frame(const gif_animation *gif)
return GIF_OK;
}
-static inline gif_result
-gif__decode(gif_animation *gif,
+static gif_result
+gif__decode_complex(gif_animation *gif,
unsigned int frame,
unsigned int width,
unsigned int height,
@@ -701,6 +701,85 @@ gif__decode(gif_animation *gif,
return ret;
}
+static gif_result
+gif__decode_simple(gif_animation *gif,
+ unsigned int frame,
+ unsigned int height,
+ unsigned int offset_y,
+ uint8_t minimum_code_size,
+ unsigned int *restrict frame_data,
+ unsigned int *restrict colour_table)
+{
+ unsigned int transparency_index;
+ 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(gif->lzw_ctx, gif->gif_data,
+ gif->buffer_size, gif->buffer_position,
+ minimum_code_size);
+ if (res != LZW_OK) {
+ return gif_error_from_lzw(res);
+ }
+
+ transparency_index = gif->frames[frame].transparency ?
+ gif->frames[frame].transparency_index :
+ GIF_NO_TRANSPARENCY;
+
+ frame_data += (offset_y * gif->width);
+
+ while (pixels > 0) {
+ res = lzw_decode_map_continuous(gif->lzw_ctx,
+ transparency_index, colour_table,
+ 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(gif_animation *gif,
+ unsigned int frame,
+ unsigned int width,
+ unsigned int height,
+ unsigned int offset_x,
+ unsigned int offset_y,
+ unsigned int interlace,
+ uint8_t minimum_code_size,
+ unsigned int *restrict frame_data,
+ unsigned int *restrict colour_table)
+{
+ gif_result ret;
+
+ if (interlace == false && width == gif->width && offset_x == 0) {
+ ret = gif__decode_simple(gif, frame, height, offset_y,
+ minimum_code_size, frame_data, colour_table);
+ } else {
+ ret = gif__decode_complex(gif, frame, width, height,
+ offset_x, offset_y, interlace,
+ minimum_code_size, frame_data, colour_table);
+ }
+
+ return ret;
+}
+
/**
* decode a gif frame
*