summaryrefslogtreecommitdiff
path: root/src/libnsgif.c
diff options
context:
space:
mode:
authorMichael Drake <tlsa@netsurf-browser.org>2022-02-21 18:47:54 +0000
committerMichael Drake <tlsa@netsurf-browser.org>2022-02-21 18:47:54 +0000
commit152c4a7429d9364ab163997dd6304665f5baa2e5 (patch)
tree5bfd0d021d6c9ea7d2ca2a245a8fe23f37c5dd87 /src/libnsgif.c
parent3832f7edc4cd117642ad7890eb1476d1f9d8e918 (diff)
downloadlibnsgif-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.c79
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,