From 270155287a0f2e7788253a38178efefb608ffdd5 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Fri, 2 Apr 2021 12:34:35 +0100 Subject: gif: Split out gif frame data decode. --- src/libnsgif.c | 141 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 82 insertions(+), 59 deletions(-) (limited to 'src') diff --git a/src/libnsgif.c b/src/libnsgif.c index 512ebfe..ed394a2 100644 --- a/src/libnsgif.c +++ b/src/libnsgif.c @@ -623,6 +623,83 @@ static gif_result gif__recover_previous_frame(const gif_animation *gif) return GIF_OK; } +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) +{ + const uint8_t *stack_base; + const uint8_t *stack_pos; + 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, &stack_base, &stack_pos); + if (res != LZW_OK) { + return gif_error_from_lzw(res); + } + + for (unsigned int y = 0; y < height; y++) { + unsigned int x; + unsigned int decode_y; + unsigned int *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); + + /* Rather than decoding pixel by pixel, we try to burst + * out streams of data to remove the need for end-of + * data checks every pixel. + */ + x = width; + while (x > 0) { + unsigned int burst_bytes = (stack_pos - stack_base); + if (burst_bytes > 0) { + if (burst_bytes > x) { + burst_bytes = x; + } + x -= burst_bytes; + while (burst_bytes-- > 0) { + register unsigned char colour; + colour = *--stack_pos; + if (((gif->frames[frame].transparency) && + (colour != gif->frames[frame].transparency_index)) || + (!gif->frames[frame].transparency)) { + *frame_scanline = colour_table[colour]; + } + frame_scanline++; + } + } else { + res = lzw_decode(gif->lzw_ctx, &stack_pos); + 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; + } + } + } + } + + return ret; +} + /** * decode a gif frame * @@ -643,11 +720,8 @@ gif_internal_decode_frame(gif_animation *gif, unsigned int flags, colour_table_size, interlace; unsigned int *colour_table; unsigned int *frame_data = 0; // Set to 0 for no warnings - unsigned int *frame_scanline; unsigned int save_buffer_position; unsigned int return_value = 0; - unsigned int x, y, decode_y, burst_bytes; - register unsigned char colour; /* Ensure this frame is supposed to be decoded */ if (gif->frames[frame].display == false) { @@ -801,10 +875,6 @@ gif_internal_decode_frame(gif_animation *gif, /* If we are clearing the image we just clear, if not decode */ if (!clear_image) { - lzw_result res; - const uint8_t *stack_base; - const uint8_t *stack_pos; - /* Ensure we have enough data for a 1-byte LZW code size + * 1-byte gif trailer */ @@ -868,62 +938,15 @@ gif_internal_decode_frame(gif_animation *gif, gif->decoded_frame = frame; gif->buffer_position = (gif_data - gif->gif_data) + 1; - /* Initialise the LZW decoding */ - res = lzw_decode_init(gif->lzw_ctx, gif->gif_data, - gif->buffer_size, gif->buffer_position, - gif_data[0], &stack_base, &stack_pos); - if (res != LZW_OK) { - return gif_error_from_lzw(res); - } - - /* Decompress the data */ - for (y = 0; y < height; y++) { - 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); - - /* Rather than decoding pixel by pixel, we try to burst - * out streams of data to remove the need for end-of - * data checks every pixel. - */ - x = width; - while (x > 0) { - burst_bytes = (stack_pos - stack_base); - if (burst_bytes > 0) { - if (burst_bytes > x) { - burst_bytes = x; - } - x -= burst_bytes; - while (burst_bytes-- > 0) { - colour = *--stack_pos; - if (((gif->frames[frame].transparency) && - (colour != gif->frames[frame].transparency_index)) || - (!gif->frames[frame].transparency)) { - *frame_scanline = colour_table[colour]; - } - frame_scanline++; - } - } else { - res = lzw_decode(gif->lzw_ctx, &stack_pos); - if (res != LZW_OK) { - /* Unexpected end of frame, try to recover */ - if (res == LZW_OK_EOD) { - return_value = GIF_OK; - } else { - return_value = gif_error_from_lzw(res); - } - goto gif_decode_frame_exit; - } - } - } - } + return_value = gif__decode(gif, frame, width, height, + offset_x, offset_y, interlace, gif_data[0], + frame_data, colour_table); } else { /* Clear our frame */ if (gif->frames[frame].disposal_method == GIF_FRAME_CLEAR) { + unsigned int y; for (y = 0; y < height; y++) { + unsigned int *frame_scanline; frame_scanline = frame_data + offset_x + ((offset_y + y) * gif->width); if (gif->frames[frame].transparency) { memset(frame_scanline, -- cgit v1.2.3