diff options
author | Michael Drake <tlsa@netsurf-browser.org> | 2022-02-21 18:47:54 +0000 |
---|---|---|
committer | Michael Drake <tlsa@netsurf-browser.org> | 2022-02-21 18:47:54 +0000 |
commit | 152c4a7429d9364ab163997dd6304665f5baa2e5 (patch) | |
tree | 5bfd0d021d6c9ea7d2ca2a245a8fe23f37c5dd87 /src/libnsgif.c | |
parent | 3832f7edc4cd117642ad7890eb1476d1f9d8e918 (diff) | |
download | libnsgif-152c4a7429d9364ab163997dd6304665f5baa2e5.tar.gz libnsgif-152c4a7429d9364ab163997dd6304665f5baa2e5.tar.bz2 |
gif: decode: Add support for clipped frames.
Diffstat (limited to 'src/libnsgif.c')
-rw-r--r-- | src/libnsgif.c | 79 |
1 files changed, 75 insertions, 4 deletions
diff --git a/src/libnsgif.c b/src/libnsgif.c index ecddf78..2a5ff8d 100644 --- a/src/libnsgif.c +++ b/src/libnsgif.c @@ -300,6 +300,48 @@ static inline bool gif__next_row(uint32_t interlace, } } +/** + * Get any frame clip adjustment for the image extent. + * + * \param[in] frame_off Frame's X or Y offset. + * \param[in] frame_dim Frame width or height. + * \param[in] image_ext Image width or height constraint. + * \return the amount the frame needs to be clipped to fit the image in given + * dimension. + */ +static inline uint32_t gif__clip( + uint32_t frame_off, + uint32_t frame_dim, + uint32_t image_ext) +{ + uint32_t frame_ext = frame_off + frame_dim; + + if (frame_ext <= image_ext) { + return 0; + } + + return frame_ext - image_ext; +} + +/** + * Perform any jump over decoded data, to accommodate clipped portion of frame. + * + * \param[in,out] skip Number of pixels of data to jump. + * \param[in,out] available Number of pixels of data currently available. + * \param[in,out] pos Position in decoded pixel value data. + */ +static inline void gif__jump_data( + uint32_t *skip, + uint32_t *available, + const uint8_t **pos) +{ + uint32_t jump = (*skip < *available) ? *skip : *available; + + *skip -= jump; + *available -= jump; + *pos += jump; +} + static gif_result gif__decode_complex( struct gif_animation *gif, uint32_t width, @@ -313,12 +355,24 @@ static gif_result gif__decode_complex( uint32_t *restrict colour_table) { lzw_result res; + uint32_t clip_x = gif__clip(offset_x, width, gif->width); + uint32_t clip_y = gif__clip(offset_y, height, gif->height); + const uint8_t *uncompressed; gif_result ret = GIF_OK; uint32_t available = 0; uint8_t step = 24; + uint32_t skip = 0; uint32_t y = 0; - if (height == 0) { + if (offset_x >= gif->width || + offset_y >= gif->height) { + return GIF_OK; + } + + width -= clip_x; + height -= clip_y; + + if (width == 0 || height == 0) { return GIF_OK; } @@ -339,9 +393,8 @@ static gif_result gif__decode_complex( x = width; while (x > 0) { - const uint8_t *uncompressed; unsigned row_available; - if (available == 0) { + while (available == 0) { if (res != LZW_OK) { /* Unexpected end of frame, try to recover */ if (res == LZW_OK_EOD) { @@ -349,10 +402,15 @@ static gif_result gif__decode_complex( } else { ret = gif__error_from_lzw(res); } - break; + return ret; } res = lzw_decode(gif->lzw_ctx, &uncompressed, &available); + + if (available == 0) { + return GIF_OK; + } + gif__jump_data(&skip, &available, &uncompressed); } row_available = x < available ? x : available; @@ -375,6 +433,9 @@ static gif_result gif__decode_complex( } } } + + skip = clip_x; + gif__jump_data(&skip, &available, &uncompressed); } while (gif__next_row(interlace, height, &y, &step)); return ret; @@ -394,6 +455,16 @@ static gif_result gif__decode_simple( gif_result ret = GIF_OK; lzw_result res; + if (offset_y >= gif->height) { + return GIF_OK; + } + + height -= gif__clip(offset_y, height, gif->height); + + if (height == 0) { + return GIF_OK; + } + /* Initialise the LZW decoding */ res = lzw_decode_init_map(gif->lzw_ctx, data[0], transparency_index, colour_table, |