gif: Split out gif frame data decode.

This commit is contained in:
Michael Drake 2021-04-02 12:34:35 +01:00 committed by Michael Drake
parent c9a639d2bd
commit 8d8aa0dd46

View File

@ -618,6 +618,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
*
@ -638,11 +715,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) {
@ -796,10 +870,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
*/
@ -863,62 +933,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,