diff options
author | Michael Drake <tlsa@netsurf-browser.org> | 2021-04-04 23:40:01 +0100 |
---|---|---|
committer | Michael Drake <tlsa@netsurf-browser.org> | 2021-04-18 17:28:12 +0100 |
commit | decfeceb211697b18b2525ec533a95ed03418e1d (patch) | |
tree | 5ea3d3b1b6b7251d9df87d48021a88a58a58798f /src/libnsgif.c | |
parent | db49e5b61beb1759469f3e6bd53850c6bc0a38ac (diff) | |
download | libnsgif-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.c | 83 |
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 * |