From 0631647241618112481faa43cf09df535514f848 Mon Sep 17 00:00:00 2001 From: Richard Wilson Date: Tue, 24 Jan 2006 23:04:49 +0000 Subject: [PATCH] [project @ 2006-01-24 23:04:49 by rjw] Faster GIF decoding. svn path=/import/netsurf/; revision=2028 --- image/gifread.c | 221 ++++++++++++++++++++++++------------------------ image/gifread.h | 1 + 2 files changed, 113 insertions(+), 109 deletions(-) diff --git a/image/gifread.c b/image/gifread.c index 411702d6a..76a067c68 100644 --- a/image/gifread.c +++ b/image/gifread.c @@ -66,21 +66,20 @@ static unsigned int gif_interlaced_line(int height, int y); /* Internal LZW routines */ static void gif_init_LZW(struct gif_animation *gif); -static int gif_next_LZW(struct gif_animation *gif); +static bool gif_next_LZW(struct gif_animation *gif); static int gif_next_code(struct gif_animation *gif, int code_size); -#define gif_read_LZW(gif) ((stack_pointer > stack) ? *--stack_pointer : gif_next_LZW(gif)) /* General LZW values. They are shared for all GIFs being decoded, and thus we can't handle progressive decoding efficiently without having the data for each image which would use an extra 10Kb or so per GIF. */ -static unsigned char buf[280]; +static unsigned char buf[4]; +static unsigned char *direct; static int maskTbl[16] = {0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff}; -static int table[2][(1<< GIF_MAX_LZW)]; -static int stack[(1 << GIF_MAX_LZW) * 2]; -static int *stack_pointer; -static int *stack_end; +static int table[2][(1 << GIF_MAX_LZW)]; +static char stack[(1 << GIF_MAX_LZW) * 2]; +static char *stack_pointer; static int code_size, set_code_size; static int max_code, max_code_size; static int clear_code, end_code; @@ -581,13 +580,16 @@ int gif_decode_frame(struct gif_animation *gif, unsigned int frame) { /* Check we have enough data for the header */ - if (gif_bytes < 9) return GIF_INSUFFICIENT_DATA; + if (gif_bytes < 9) + return GIF_INSUFFICIENT_DATA; /* Clear the previous frame totally. We can't just pretend we've got a smaller sprite and clear what we need as some frames have multiple images which would produce errors. */ frame_data = (unsigned int *)bitmap_get_buffer(gif->frame_image); + if (!frame_data) + return GIF_INSUFFICIENT_MEMORY; if (!clear_image) { if ((frame == 0) || (gif->decoded_frame == -1)) memset((char*)frame_data, 0x00, gif->width * gif->height * sizeof(int)); @@ -726,6 +728,7 @@ int gif_decode_frame(struct gif_animation *gif, unsigned int frame) { curbit = lastbit = 0; last_byte = 2; get_done = false; + direct = buf; gif_init_LZW(gif); /* Decompress the data @@ -741,27 +744,22 @@ int gif_decode_frame(struct gif_animation *gif, unsigned int frame) { of data to remove the need for end-of data checks every pixel. */ x = width; - while (x-- > 0) { - /* Do the first pixel to get the stack of some sort - */ - if ((colour = gif_read_LZW(gif)) >= 0) { - if (colour != transparency_index) - *frame_scanline = colour_table[colour]; - frame_scanline++; - } else { - return_value = GIF_INSUFFICIENT_FRAME_DATA; - goto gif_decode_frame_exit; - } - - /* Try to burst some bytes out - */ + while (x > 0) { burst_bytes = (stack_pointer - stack); - if (burst_bytes > x) burst_bytes = x; - x -= burst_bytes; - while (burst_bytes-- > 0) { - if ((colour = *--stack_pointer) != transparency_index) - *frame_scanline = colour_table[colour]; - frame_scanline++; + if (burst_bytes > 0) { + if (burst_bytes > x) + burst_bytes = x; + x -= burst_bytes; + while (burst_bytes-- > 0) { + if ((colour = *--stack_pointer) != transparency_index) + *frame_scanline = colour_table[colour]; + frame_scanline++; + } + } else { + if (!gif_next_LZW(gif)) { + return_value = gif->current_error; + goto gif_decode_frame_exit; + } } } } @@ -851,9 +849,11 @@ void gif_finalise(struct gif_animation *gif) { void gif_init_LZW(struct gif_animation *gif) { int i; + gif->current_error = 0; if (clear_code >= (1 << GIF_MAX_LZW)) { stack_pointer = stack; - *stack_pointer++ = -2; + gif->current_error = GIF_FRAME_DATA_ERROR; + return; } /* initialise our table */ @@ -866,7 +866,6 @@ void gif_init_LZW(struct gif_animation *gif) { max_code_size = clear_code << 1; max_code = clear_code + 2; stack_pointer = stack; - stack_end = (int *)((char *)stack + sizeof(stack)); do { firstcode = oldcode = gif_next_code(gif, code_size); } while (firstcode == clear_code); @@ -874,96 +873,96 @@ void gif_init_LZW(struct gif_animation *gif) { } -static int gif_next_LZW(struct gif_animation *gif) { +static bool gif_next_LZW(struct gif_animation *gif) { int code, incode; int block_size; - int stack_remaining; + int new_code; - while ((code = gif_next_code(gif, code_size)) >= 0) { - if (code == clear_code) { - gif_init_LZW(gif); - return *--stack_pointer; + code = gif_next_code(gif, code_size); + if (code < 0) { + gif->current_error = code; + return false; + } else if (code == clear_code) { + gif_init_LZW(gif); + return true; + } else if (code == end_code) { + /* skip to the end of our data so multi-image GIFs work */ + if (zero_data_block) { + gif->current_error = GIF_FRAME_DATA_ERROR; + return false; } - - if (code == end_code) { - /* skip to the end of our data so multi-image GIFs work */ - if (zero_data_block) - return -2; - block_size = 0; - while (block_size != 1) { - block_size = gif->gif_data[gif->buffer_position] + 1; - gif->buffer_position += block_size; - } - return -2; + block_size = 0; + while (block_size != 1) { + block_size = gif->gif_data[gif->buffer_position] + 1; + gif->buffer_position += block_size; } - - /* fill the stack with some data */ - incode = code; - if (code >= max_code) { - *stack_pointer++ = firstcode; - code = oldcode; - } - - stack_remaining = (stack_end - stack_pointer) / sizeof(int); - /* flush out odd number of words */ - if ((code >= clear_code) && (stack_remaining & 1)) { - *stack_pointer++ = table[1][code]; - if ((code == table[0][code]) || (--stack_remaining <= 0)) - return code; - code = table[0][code]; - } - stack_remaining = stack_remaining >> 1; - while (code >= clear_code) { - *stack_pointer++ = table[1][code]; - code = table[0][code]; - if (code < clear_code) - break; - *stack_pointer++ = table[1][code]; - if ((code == table[0][code]) || (--stack_remaining <= 0)) - return code; - code = table[0][code]; - } - - *stack_pointer++ = firstcode = table[1][code]; - - if ((code = max_code) < (1 << GIF_MAX_LZW)) { - table[0][code] = oldcode; - table[1][code] = firstcode; - ++max_code; - if ((max_code >= max_code_size) && (max_code_size < (1 << GIF_MAX_LZW))) { - max_code_size = max_code_size << 1; - ++code_size; - } - } - oldcode = incode; - - if (stack_pointer > stack) - return *--stack_pointer; + gif->current_error = GIF_FRAME_DATA_ERROR; + return false; } - return code; + + incode = code; + if (code >= max_code) { + *stack_pointer++ = firstcode; + code = oldcode; + } + + /* The following loop is the most important in the GIF decoding cycle as every + * single pixel passes through it. + * + * Note: our stack is always big enough to hold a complete decompressed chunk. */ + while (code >= clear_code) { + *stack_pointer++ = table[1][code]; + new_code = table[0][code]; + if (new_code < clear_code) { + code = new_code; + break; + } + *stack_pointer++ = table[1][new_code]; + code = table[0][new_code]; + if (code == new_code) { + gif->current_error = GIF_FRAME_DATA_ERROR; + return false; + } + } + + *stack_pointer++ = firstcode = table[1][code]; + + if ((code = max_code) < (1 << GIF_MAX_LZW)) { + table[0][code] = oldcode; + table[1][code] = firstcode; + ++max_code; + if ((max_code >= max_code_size) && (max_code_size < (1 << GIF_MAX_LZW))) { + max_code_size = max_code_size << 1; + ++code_size; + } + } + oldcode = incode; + return true; } static int gif_next_code(struct gif_animation *gif, int code_size) { - int i, j, end, count; - unsigned int ret; - unsigned char *gif_data; + int i, j, end, count, ret; + unsigned char *b; end = curbit + code_size; if (end >= lastbit) { if (get_done) - return -1; - buf[0] = buf[last_byte - 2]; - buf[1] = buf[last_byte - 1]; + return GIF_INSUFFICIENT_FRAME_DATA; + buf[0] = direct[last_byte - 2]; + buf[1] = direct[last_byte - 1]; /* get the next block */ - gif_data = gif->gif_data + gif->buffer_position; - zero_data_block = ((count = gif_data[0]) == 0); + direct = gif->gif_data + gif->buffer_position; + zero_data_block = ((count = direct[0]) == 0); if ((gif->buffer_position + count) >= gif->buffer_size) - return -1; + return GIF_INSUFFICIENT_FRAME_DATA; if (count == 0) get_done = true; - else - memcpy(&buf[2], gif_data + 1, count); + else { + direct -= 1; + buf[2] = direct[2]; + buf[3] = direct[3]; + } gif->buffer_position += count + 1; /* update our variables */ @@ -973,16 +972,20 @@ static int gif_next_code(struct gif_animation *gif, int code_size) { end = curbit + code_size; } - j = end >> 3; i = curbit >> 3; - if (i == j) - ret = buf[i]; - else if (i + 1 == j) - ret = buf[i] | (buf[i+1] << 8); + if (i < 2) + b = buf; else - ret = buf[i] | (buf[i+1] << 8) | (buf[i+2] << 16); + b = direct; + ret = b[i]; + j = (end >> 3) - 1; + if (i <= j) { + ret |= (b[i + 1] << 8); + if (i < j) + ret |= (b[i + 2] << 16); + } ret = (ret >> (curbit % 8)) & maskTbl[code_size]; curbit += code_size; - return (int)ret; + return ret; } diff --git a/image/gifread.h b/image/gifread.h index 8e91b7e40..5099ae4fe 100644 --- a/image/gifread.h +++ b/image/gifread.h @@ -67,6 +67,7 @@ typedef struct gif_animation { unsigned int *local_colour_table; /**< local colour table */ int dirty_frame; /**< the current dirty frame, or -1 for none */ struct bitmap *frame_image; /**< currently decoded image */ + int current_error; /**< current error type, or 0 for none*/ } gif_animation; int gif_initialise(struct gif_animation *gif);