summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gif.c168
-rw-r--r--src/lzw.c18
-rw-r--r--src/lzw.h8
3 files changed, 127 insertions, 67 deletions
diff --git a/src/gif.c b/src/gif.c
index d2b6054..73814bf 100644
--- a/src/gif.c
+++ b/src/gif.c
@@ -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",
};
diff --git a/src/lzw.c b/src/lzw.c
index 6f85caa..3098ef5 100644
--- a/src/lzw.c
+++ b/src/lzw.c
@@ -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;
diff --git a/src/lzw.h b/src/lzw.h
index c68753a..f19432f 100644
--- a/src/lzw.h
+++ b/src/lzw.h
@@ -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.