diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/gif.c | 168 | ||||
-rw-r--r-- | src/lzw.c | 18 | ||||
-rw-r--r-- | src/lzw.h | 8 |
3 files changed, 127 insertions, 67 deletions
@@ -32,7 +32,7 @@ typedef struct nsgif_frame { struct nsgif_frame_info info; /** offset (in bytes) to the GIF frame data */ - uint32_t frame_offset; + size_t frame_offset; /** whether the frame has previously been decoded. */ bool decoded; /** whether the frame is totally opaque */ @@ -40,6 +40,9 @@ typedef struct nsgif_frame { /** whether a full image redraw is required */ bool redraw_required; + /** Amount of LZW data found in scan */ + uint32_t lzw_data_length; + /** the index designating a transparent pixel */ uint32_t transparency_index; @@ -90,12 +93,18 @@ struct nsgif { /** number of frames partially decoded */ uint32_t frame_count_partial; + /** + * Whether all the GIF data has been supplied, or if there may be + * more to come. + */ + bool data_complete; + /** pointer to GIF data */ const uint8_t *buf; /** current index into GIF data */ - uint32_t buf_pos; + size_t buf_pos; /** total number of bytes of GIF data available */ - uint32_t buf_len; + size_t buf_len; /** current number of frame holders */ uint32_t frame_holders; @@ -286,6 +295,9 @@ static void nsgif__record_frame( struct nsgif *gif, const uint32_t *bitmap) { + size_t pixel_bytes = sizeof(*bitmap); + size_t height = gif->info.height; + size_t width = gif->info.width; uint32_t *prev_frame; if (gif->decoded_frame == NSGIF_FRAME_INVALID || @@ -301,7 +313,7 @@ static void nsgif__record_frame( if (gif->prev_frame == NULL) { prev_frame = realloc(gif->prev_frame, - gif->info.width * gif->info.height * 4); + width * height * pixel_bytes); if (prev_frame == NULL) { return; } @@ -309,7 +321,7 @@ static void nsgif__record_frame( prev_frame = gif->prev_frame; } - memcpy(prev_frame, bitmap, gif->info.width * gif->info.height * 4); + memcpy(prev_frame, bitmap, width * height * pixel_bytes); gif->prev_frame = prev_frame; gif->prev_index = gif->decoded_frame; @@ -320,10 +332,11 @@ static nsgif_error nsgif__recover_frame( uint32_t *bitmap) { const uint32_t *prev_frame = gif->prev_frame; - unsigned height = gif->info.height; - unsigned width = gif->info.width; + size_t pixel_bytes = sizeof(*bitmap); + size_t height = gif->info.height; + size_t width = gif->info.width; - memcpy(bitmap, prev_frame, height * width * sizeof(*bitmap)); + memcpy(bitmap, prev_frame, height * width * pixel_bytes); return NSGIF_OK; } @@ -587,20 +600,15 @@ static inline nsgif_error nsgif__decode( const uint8_t *data, uint32_t *restrict frame_data) { - enum { - GIF_MASK_INTERLACE = 0x40, - }; - nsgif_error ret; uint32_t width = frame->info.rect.x1 - frame->info.rect.x0; uint32_t height = frame->info.rect.y1 - frame->info.rect.y0; uint32_t offset_x = frame->info.rect.x0; uint32_t offset_y = frame->info.rect.y0; - uint32_t interlace = frame->flags & GIF_MASK_INTERLACE; uint32_t transparency_index = frame->transparency_index; uint32_t *restrict colour_table = gif->colour_table; - if (interlace == false && offset_x == 0 && + if (frame->info.interlaced == false && offset_x == 0 && width == gif->info.width && width == gif->rowspan) { ret = nsgif__decode_simple(gif, height, offset_y, @@ -608,11 +616,16 @@ static inline nsgif_error nsgif__decode( frame_data, colour_table); } else { ret = nsgif__decode_complex(gif, width, height, - offset_x, offset_y, interlace, + offset_x, offset_y, frame->info.interlaced, data, transparency_index, frame_data, colour_table); } + if (gif->data_complete && ret == NSGIF_ERR_END_OF_DATA) { + /* This is all the data there is, so make do. */ + ret = NSGIF_OK; + } + return ret; } @@ -628,9 +641,14 @@ static void nsgif__restore_bg( struct nsgif_frame *frame, uint32_t *bitmap) { + size_t pixel_bytes = sizeof(*bitmap); + if (frame == NULL) { + size_t width = gif->info.width; + size_t height = gif->info.height; + memset(bitmap, NSGIF_TRANSPARENT_COLOUR, - gif->info.width * gif->info.height * sizeof(*bitmap)); + width * height * pixel_bytes); } else { uint32_t width = frame->info.rect.x1 - frame->info.rect.x0; uint32_t height = frame->info.rect.y1 - frame->info.rect.y0; @@ -651,7 +669,7 @@ static void nsgif__restore_bg( uint32_t *scanline = bitmap + offset_x + (offset_y + y) * gif->info.width; memset(scanline, NSGIF_TRANSPARENT_COLOUR, - width * sizeof(*bitmap)); + width * pixel_bytes); } } else { for (uint32_t y = 0; y < height; y++) { @@ -997,6 +1015,7 @@ static nsgif_error nsgif__parse_image_descriptor( enum { NSGIF_IMAGE_DESCRIPTOR_LEN = 10u, NSGIF_IMAGE_SEPARATOR = 0x2Cu, + NSGIF_MASK_INTERLACE = 0x40u, }; assert(gif != NULL); @@ -1024,6 +1043,8 @@ static nsgif_error nsgif__parse_image_descriptor( frame->info.rect.x1 = x + w; frame->info.rect.y1 = y + h; + frame->info.interlaced = frame->flags & NSGIF_MASK_INTERLACE; + /* Allow first frame to grow image dimensions. */ if (gif->info.frame_count == 0) { if (x + w > gif->info.width) { @@ -1045,7 +1066,7 @@ static nsgif_error nsgif__parse_image_descriptor( * \param[in] colour_table The colour table to populate. * \param[in] layout la. * \param[in] colour_table_entries The number of colour table entries. - * \param[in] Data Raw colour table data. + * \param[in] data Raw colour table data. */ static void nsgif__colour_table_decode( uint32_t colour_table[NSGIF_MAX_COLOURS], @@ -1074,11 +1095,13 @@ static void nsgif__colour_table_decode( /** * Extract a GIF colour table into a LibNSGIF colour table buffer. * - * \param[in] gif The gif object we're decoding. - * \param[in] colour_table The colour table to populate. - * \param[in] colour_table_entries The number of colour table entries. - * \param[in] pos Current position in data, updated on exit. - * \param[in] decode Whether to decode the colour table. + * \param[in] colour_table The colour table to populate. + * \param[in] layout The target pixel format to decode to. + * \param[in] colour_table_entries The number of colour table entries. + * \param[in] data Current position in data. + * \param[in] data_len The available length of `data`. + * \param[out] used Number of colour table bytes read. + * \param[in] decode Whether to decode the colour table. * \return NSGIF_OK on success, appropriate error otherwise. */ static inline nsgif_error nsgif__colour_table_extract( @@ -1213,16 +1236,19 @@ static nsgif_error nsgif__parse_image_data( len--; while (block_size != 1) { - if (len < 1) return NSGIF_ERR_END_OF_DATA; + if (len < 1) { + return NSGIF_ERR_END_OF_DATA; + } block_size = data[0] + 1; /* Check if the frame data runs off the end of the file */ if (block_size > len) { - block_size = len; - return NSGIF_OK; + frame->lzw_data_length += len; + return NSGIF_ERR_END_OF_DATA; } len -= block_size; data += block_size; + frame->lzw_data_length += block_size; } *pos = data; @@ -1258,14 +1284,16 @@ static struct nsgif_frame *nsgif__get_frame( frame = &gif->frames[frame_idx]; - frame->transparency_index = NSGIF_NO_TRANSPARENCY; - frame->frame_offset = gif->buf_pos; frame->info.local_palette = false; frame->info.transparency = false; - frame->redraw_required = false; frame->info.display = false; frame->info.disposal = 0; frame->info.delay = 10; + + frame->transparency_index = NSGIF_NO_TRANSPARENCY; + frame->frame_offset = gif->buf_pos; + frame->redraw_required = false; + frame->lzw_data_length = 0; frame->decoded = false; } @@ -1315,19 +1343,12 @@ static nsgif_error nsgif__process_frame( return NSGIF_OK; } } else { - pos = (uint8_t *)(gif->buf + gif->buf_pos); + pos = gif->buf + gif->buf_pos; /* Check if we've finished */ if (pos < end && pos[0] == NSGIF_TRAILER) { return NSGIF_OK; } - - /* We could theoretically get some junk data that gives us - * millions of frames, so we ensure that we don't have a - * silly number. */ - if (frame_idx > 4096) { - return NSGIF_ERR_FRAME_COUNT; - } } ret = nsgif__parse_frame_extensions(gif, frame, &pos, !decode); @@ -1593,6 +1614,10 @@ nsgif_error nsgif_data_scan( nsgif_error ret; uint32_t frames; + if (gif->data_complete) { + return NSGIF_ERR_DATA_COMPLETE; + } + /* Initialize values */ gif->buf_len = size; gif->buf = data; @@ -1734,6 +1759,32 @@ nsgif_error nsgif_data_scan( return ret; } +/* exported function documented in nsgif.h */ +void nsgif_data_complete( + nsgif_t *gif) +{ + if (gif->data_complete == false) { + uint32_t start = gif->info.frame_count; + uint32_t end = gif->frame_count_partial; + + for (uint32_t f = start; f < end; f++) { + nsgif_frame *frame = &gif->frames[f]; + + if (frame->lzw_data_length > 0) { + frame->info.display = true; + gif->info.frame_count = f + 1; + + if (f == 0) { + frame->info.transparency = true; + } + break; + } + } + } + + gif->data_complete = true; +} + static void nsgif__redraw_rect_extend( const nsgif_rect_t *frame, nsgif_rect_t *redraw) @@ -1757,7 +1808,7 @@ static void nsgif__redraw_rect_extend( } static uint32_t nsgif__frame_next( - nsgif_t *gif, + const nsgif_t *gif, bool partial, uint32_t frame) { @@ -1774,7 +1825,7 @@ static uint32_t nsgif__frame_next( } static nsgif_error nsgif__next_displayable_frame( - nsgif_t *gif, + const nsgif_t *gif, uint32_t *frame, uint32_t *delay) { @@ -1782,7 +1833,11 @@ static nsgif_error nsgif__next_displayable_frame( do { next = nsgif__frame_next(gif, false, next); - if (next == *frame || next == NSGIF_FRAME_INVALID) { + if (next <= *frame && *frame != NSGIF_FRAME_INVALID && + gif->data_complete == false) { + return NSGIF_ERR_END_OF_DATA; + + } else if (next == *frame || next == NSGIF_FRAME_INVALID) { return NSGIF_ERR_FRAME_DISPLAY; } @@ -1850,21 +1905,26 @@ nsgif_error nsgif_frame_prepare( gif->loop_count++; } - if (gif->info.frame_count == 1) { - delay = NSGIF_INFINITE; + if (gif->data_complete) { + /* Check for last frame, which has infinite delay. */ - } else if (gif->info.loop_max != 0) { - uint32_t frame_next = frame; - ret = nsgif__next_displayable_frame(gif, &frame_next, NULL); - if (ret != NSGIF_OK) { - return ret; - } + if (gif->info.frame_count == 1) { + delay = NSGIF_INFINITE; + } else if (gif->info.loop_max != 0) { + uint32_t frame_next = frame; - if (frame_next < frame) { - if (nsgif__animation_complete( - gif->loop_count + 1, - gif->info.loop_max)) { - delay = NSGIF_INFINITE; + ret = nsgif__next_displayable_frame(gif, + &frame_next, NULL); + if (ret != NSGIF_OK) { + return ret; + } + + if (gif->data_complete && frame_next < frame) { + if (nsgif__animation_complete( + gif->loop_count + 1, + gif->info.loop_max)) { + delay = NSGIF_INFINITE; + } } } } @@ -1984,8 +2044,8 @@ const char *nsgif_strerror(nsgif_error err) [NSGIF_ERR_DATA] = "Invalid source data", [NSGIF_ERR_BAD_FRAME] = "Requested frame does not exist", [NSGIF_ERR_DATA_FRAME] = "Invalid frame data", - [NSGIF_ERR_FRAME_COUNT] = "Excessive number of frames", [NSGIF_ERR_END_OF_DATA] = "Unexpected end of GIF source data", + [NSGIF_ERR_DATA_COMPLETE] = "Can't add data to completed GIF", [NSGIF_ERR_FRAME_DISPLAY] = "Frame can't be displayed", [NSGIF_ERR_ANIMATION_END] = "Animation complete", }; @@ -36,9 +36,9 @@ * Note that an individual LZW code can be split over up to three sub-blocks. */ struct lzw_read_ctx { - const uint8_t *restrict data; /**< Pointer to start of input data */ - uint32_t data_len; /**< Input data length */ - uint32_t data_sb_next; /**< Offset to sub-block size */ + const uint8_t *restrict data; /**< Pointer to start of input data */ + size_t data_len; /**< Input data length */ + size_t data_sb_next; /**< Offset to sub-block size */ const uint8_t *sb_data; /**< Pointer to current sub-block in data */ size_t sb_bit; /**< Current bit offset in sub-block */ @@ -122,8 +122,8 @@ void lzw_context_destroy(struct lzw_ctx *ctx) */ static lzw_result lzw__block_advance(struct lzw_read_ctx *restrict ctx) { - uint32_t block_size; - uint32_t next_block_pos = ctx->data_sb_next; + size_t block_size; + size_t next_block_pos = ctx->data_sb_next; const uint8_t *data_next = ctx->data + next_block_pos; if (next_block_pos >= ctx->data_len) { @@ -260,8 +260,8 @@ lzw_result lzw_decode_init( struct lzw_ctx *ctx, uint8_t minimum_code_size, const uint8_t *input_data, - uint32_t input_length, - uint32_t input_pos) + size_t input_length, + size_t input_pos) { struct lzw_table_entry *table = ctx->table; lzw_result res; @@ -322,8 +322,8 @@ lzw_result lzw_decode_init_map( uint32_t transparency_idx, const uint32_t *colour_table, const uint8_t *input_data, - uint32_t input_length, - uint32_t input_pos) + size_t input_length, + size_t input_pos) { lzw_result res; @@ -69,8 +69,8 @@ lzw_result lzw_decode_init( struct lzw_ctx *ctx, uint8_t minimum_code_size, const uint8_t *input_data, - uint32_t input_length, - uint32_t input_pos); + size_t input_length, + size_t input_pos); /** * Read input codes until end of LZW context owned output buffer. @@ -110,8 +110,8 @@ lzw_result lzw_decode_init_map( uint32_t transparency_idx, const uint32_t *colour_table, const uint8_t *input_data, - uint32_t input_length, - uint32_t input_pos); + size_t input_length, + size_t input_pos); /** * Read LZW codes into client buffer, mapping output to colours. |