summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Drake <tlsa@netsurf-browser.org>2022-02-22 18:47:19 +0000
committerMichael Drake <tlsa@netsurf-browser.org>2022-02-23 16:13:14 +0000
commit0526c55f20ee7480533567fad62ea00e6bf31786 (patch)
tree960a874ba4019027d13bfe73b3476cfa32f38b01
parentb5dfaef72b2e2725810320cd2cf703463b3e95e1 (diff)
downloadlibnsgif-0526c55f20ee7480533567fad62ea00e6bf31786.tar.gz
libnsgif-0526c55f20ee7480533567fad62ea00e6bf31786.tar.bz2
API: Rework library API to keep internal workings private.
-rw-r--r--include/nsgif.h226
-rw-r--r--src/gif.c479
2 files changed, 501 insertions, 204 deletions
diff --git a/include/nsgif.h b/include/nsgif.h
index 5e69b7e..11378d1 100644
--- a/include/nsgif.h
+++ b/include/nsgif.h
@@ -18,6 +18,26 @@
#include <stdint.h>
#include <stdbool.h>
+#include <inttypes.h>
+
+#define NSGIF_INFINITE (UINT32_MAX)
+
+typedef struct nsgif nsgif;
+
+typedef struct nsgif_info {
+ /** width of GIF (may increase during decoding) */
+ uint32_t width;
+ /** height of GIF (may increase during decoding) */
+ uint32_t height;
+ /** number of frames decoded */
+ uint32_t frame_count;
+ /** number of times to loop animation */
+ int loop_max;
+ /** number of animation loops so far */
+ int loop_count;
+
+ uint16_t delay_min;
+} nsgif_info_t;
/* Error return values */
typedef enum {
@@ -29,51 +49,30 @@ typedef enum {
NSGIF_DATA_ERROR = -4,
NSGIF_INSUFFICIENT_MEMORY = -5,
NSGIF_FRAME_NO_DISPLAY = -6,
- NSGIF_END_OF_FRAME = -7
+ NSGIF_END_OF_FRAME = -7,
+ NSGIF_FRAME_INVALID = -8,
+ NSGIF_ANIMATION_COMPLETE = -9,
} nsgif_result;
-/** GIF rectangle structure. */
+/**
+ * GIF rectangle structure.
+ *
+ * * Top left coordinate is `(x0, y0)`.
+ * * Width is `x1 - x0`.
+ * * Height is `y1 - y0`.
+ * * Units are pixels.
+ */
typedef struct nsgif_rect {
- /** x co-ordinate of redraw rectangle */
- uint32_t x;
- /** y co-ordinate of redraw rectangle */
- uint32_t y;
- /** width of redraw rectangle */
- uint32_t w;
- /** height of redraw rectangle */
- uint32_t h;
+ /** x co-ordinate of redraw rectangle, left */
+ uint32_t x0;
+ /** y co-ordinate of redraw rectangle, top */
+ uint32_t y0;
+ /** x co-ordinate of redraw rectangle, right */
+ uint32_t x1;
+ /** y co-ordinate of redraw rectangle, bottom */
+ uint32_t y1;
} nsgif_rect;
-/** GIF frame data */
-typedef struct nsgif_frame {
- /** whether the frame should be displayed/animated */
- bool display;
- /** delay (in cs) before animating the frame */
- uint32_t frame_delay;
-
- /* Internal members are listed below */
-
- /** offset (in bytes) to the GIF frame data */
- uint32_t frame_pointer;
- /** whether the frame has previously been decoded. */
- bool decoded;
- /** whether the frame is totally opaque */
- bool opaque;
- /** whether a full image redraw is required */
- bool redraw_required;
- /** how the previous frame should be disposed; affects plotting */
- uint8_t disposal_method;
- /** whether we acknowledge transparency */
- bool transparency;
- /** the index designating a transparent pixel */
- uint32_t transparency_index;
- /* Frame flags */
- uint32_t flags;
-
- /** Frame's redraw rectangle. */
- nsgif_rect redraw;
-} nsgif_frame;
-
/* API for Bitmap callbacks */
typedef void* (*nsgif_bitmap_cb_create)(int width, int height);
typedef void (*nsgif_bitmap_cb_destroy)(void *bitmap);
@@ -101,75 +100,36 @@ typedef struct nsgif_bitmap_cb_vt {
nsgif_bitmap_cb_modified modified;
} nsgif_bitmap_cb_vt;
-/** GIF animation data */
-typedef struct nsgif {
- /** LZW decode context */
- void *lzw_ctx;
- /** callbacks for bitmap functions */
- nsgif_bitmap_cb_vt bitmap;
- /** pointer to GIF data */
- const uint8_t *nsgif_data;
- /** width of GIF (may increase during decoding) */
- uint32_t width;
- /** height of GIF (may increase during decoding) */
- uint32_t height;
- /** number of frames decoded */
- uint32_t frame_count;
- /** number of frames partially decoded */
- uint32_t frame_count_partial;
- /** decoded frames */
- nsgif_frame *frames;
- /** current frame decoded to bitmap */
- int decoded_frame;
- /** currently decoded image; stored as bitmap from bitmap_create callback */
- void *frame_image;
- /** number of times to loop animation */
- int loop_count;
-
- /* Internal members are listed below */
-
- /** current index into GIF data */
- uint32_t buffer_position;
- /** total number of bytes of GIF data available */
- uint32_t buffer_size;
- /** current number of frame holders */
- uint32_t frame_holders;
- /** background index */
- uint32_t bg_index;
- /** background colour */
- uint32_t bg_colour;
- /** image aspect ratio (ignored) */
- uint32_t aspect_ratio;
- /** size of colour table (in entries) */
- uint32_t colour_table_size;
- /** whether the GIF has a global colour table */
- bool global_colours;
- /** global colour table */
- uint32_t *global_colour_table;
- /** local colour table */
- uint32_t *local_colour_table;
- /** current colour table */
- uint32_t *colour_table;
-
- /** previous frame for NSGIF_FRAME_RESTORE */
- void *prev_frame;
- /** previous frame index */
- int prev_index;
- /** previous frame width */
- unsigned prev_width;
- /** previous frame height */
- unsigned prev_height;
-} nsgif;
-
/**
- * Initialises necessary nsgif members.
+ * Create the NSGIF object.
+ *
+ * \param[in] bitmap_vt Bitmap operation functions v-table.
+ * \param[out] gif_out Return NSGIF object on success.
+ * \return NSGIF_OK on success, or appropriate error otherwise.
*/
-void nsgif_create(nsgif *gif, nsgif_bitmap_cb_vt *bitmap_callbacks);
+nsgif_result nsgif_create(const nsgif_bitmap_cb_vt *bitmap_vt, nsgif **gif_out);
/**
- * Initialises any workspace held by the animation and attempts to decode
- * any information that hasn't already been decoded.
- * If an error occurs, all previously decoded frames are retained.
+ * Scan the source image data.
+ *
+ * This is used to feed the source data into LibNSGIF. This must be called
+ * before calling \ref nsgif_frame_decode.
+ *
+ * It can be called multiple times with, with increasing sizes. If it is called
+ * several times, as more data is available (e.g. slow network fetch) the data
+ * already given to \ref nsgif_data_scan must be provided each time.
+ *
+ * For example, if you call \ref nsgif_data_scan with 25 bytes of data, and then
+ * fetch another 10 bytes, you would need to call \ref nsgif_data with a size of
+ * 35 bytes, and the whole 35 bytes must be contiguous memory. It is safe to
+ * `realloc` the source buffer between calls to \ref nsgif_data_scan. (The
+ * actual data pointer is allowed to be different.)
+ *
+ * If an error occurs, all previously scanned frames are retained.
+ *
+ * \param[in] gif The NSGIF object.
+ * \param[in] size Number of bytes in data.
+ * \param[in] data Raw source GIF data.
*
* \return Error return value.
* - NSGIF_FRAME_DATA_ERROR for GIF frame data error
@@ -179,11 +139,30 @@ void nsgif_create(nsgif *gif, nsgif_bitmap_cb_vt *bitmap_callbacks);
* - NSGIF_OK for successful decoding
* - NSGIF_WORKING for successful decoding if more frames are expected
*/
-nsgif_result nsgif_initialise(nsgif *gif, size_t size, const uint8_t *data);
+nsgif_result nsgif_data_scan(
+ nsgif *gif,
+ size_t size,
+ const uint8_t *data);
+
+/**
+ * Prepare to show a frame.
+ *
+ * \param[in] gif The NSGIF object.
+ * \param[out] area The area in pixels that must be redrawn.
+ * \param[out] delay_cs Time to wait after frame_new before next frame in cs.
+ * \param[out] frame_new The frame to decode.
+ */
+nsgif_result nsgif_frame_prepare(
+ nsgif *gif,
+ nsgif_rect *area,
+ uint32_t *delay_cs,
+ uint32_t *frame_new);
/**
* Decodes a GIF frame.
*
+ * \param[in] gif The nsgif object.
+ * \param[in] frame The frame number to decode.
* \return Error return value.
* - NSGIF_FRAME_DATA_ERROR for GIF frame data error
* - NSGIF_DATA_ERROR for GIF error (invalid frame header)
@@ -191,11 +170,40 @@ nsgif_result nsgif_initialise(nsgif *gif, size_t size, const uint8_t *data);
* - NSGIF_INSUFFICIENT_MEMORY for insufficient memory to process
* - NSGIF_OK for successful decoding
*/
-nsgif_result nsgif_decode_frame(nsgif *gif, uint32_t frame);
+nsgif_result nsgif_frame_decode(
+ nsgif *gif,
+ uint32_t frame,
+ const uint32_t **buffer);
/**
- * Releases any workspace held by a gif
+ * Reset a GIF animation.
+ *
+ * Some animations are only meant to loop N times, and then show the
+ * final frame forever. This function resets the loop and frame counters,
+ * so that the animation can be replayed without the overhead of recreating
+ * the NSGIF object and rescanning the raw data.
+ *
+ * \param[in] gif A NSGIF object.
+ *
+ * \return NSGIF_OK on success, or appropriate error otherwise.
+ */
+nsgif_result nsgif_reset(
+ nsgif *gif);
+
+/**
+ * Get information about a GIF from an NSGIF object.
+ *
+ * \param[in] gif The NSGIF object to get info for.
+ *
+ * \return The gif info, or NULL on error.
+ */
+const nsgif_info_t *nsgif_get_info(const nsgif *gif);
+
+/**
+ * Free a NSGIF object.
+ *
+ * \param[in] gif The NSGIF to free.
*/
-void nsgif_finalise(nsgif *gif);
+void nsgif_destroy(nsgif *gif);
#endif
diff --git a/src/gif.c b/src/gif.c
index cb784a9..163761a 100644
--- a/src/gif.c
+++ b/src/gif.c
@@ -17,6 +17,92 @@
#include "lzw.h"
#include "nsgif.h"
+/** GIF frame data */
+typedef struct nsgif_frame {
+ /** whether the frame should be displayed/animated */
+ bool display;
+ /** delay (in cs) before animating the frame */
+ uint32_t frame_delay;
+
+ /* Internal members are listed below */
+
+ /** offset (in bytes) to the GIF frame data */
+ uint32_t frame_pointer;
+ /** whether the frame has previously been decoded. */
+ bool decoded;
+ /** whether the frame is totally opaque */
+ bool opaque;
+ /** whether a full image redraw is required */
+ bool redraw_required;
+ /** how the previous frame should be disposed; affects plotting */
+ uint8_t disposal_method;
+ /** whether we acknowledge transparency */
+ bool transparency;
+ /** the index designating a transparent pixel */
+ uint32_t transparency_index;
+ /* Frame flags */
+ uint32_t flags;
+
+ /** Frame's redraw rectangle. */
+ nsgif_rect redraw;
+} nsgif_frame;
+
+/** GIF animation data */
+struct nsgif {
+ struct nsgif_info info;
+
+ /** LZW decode context */
+ void *lzw_ctx;
+ /** callbacks for bitmap functions */
+ nsgif_bitmap_cb_vt bitmap;
+ /** pointer to GIF data */
+ const uint8_t *nsgif_data;
+ /** decoded frames */
+ nsgif_frame *frames;
+ /** current frame */
+ uint32_t frame;
+ /** current frame decoded to bitmap */
+ uint32_t decoded_frame;
+ /** currently decoded image; stored as bitmap from bitmap_create callback */
+ void *frame_image;
+
+ uint16_t delay_default;
+ /** number of frames partially decoded */
+ uint32_t frame_count_partial;
+
+ /** current index into GIF data */
+ uint32_t buffer_position;
+ /** total number of bytes of GIF data available */
+ uint32_t buffer_size;
+ /** current number of frame holders */
+ uint32_t frame_holders;
+ /** background index */
+ uint32_t bg_index;
+ /** background colour */
+ uint32_t bg_colour;
+ /** image aspect ratio (ignored) */
+ uint32_t aspect_ratio;
+ /** size of colour table (in entries) */
+ uint32_t colour_table_size;
+ /** whether the GIF has a global colour table */
+ bool global_colours;
+ /** global colour table */
+ uint32_t *global_colour_table;
+ /** local colour table */
+ uint32_t *local_colour_table;
+ /** current colour table */
+ uint32_t *colour_table;
+
+ /** previous frame for NSGIF_FRAME_RESTORE */
+ void *prev_frame;
+ /** previous frame index */
+ uint32_t prev_index;
+ /** previous frame width */
+ uint32_t prev_width;
+ /** previous frame height */
+ uint32_t prev_height;
+};
+
/**
*
* \file
@@ -35,7 +121,7 @@
#define NSGIF_PROCESS_COLOURS 0xaa000000
/** Internal flag that a frame is invalid/unprocessed */
-#define NSGIF_INVALID_FRAME -1
+#define NSGIF_FRAME_INVALID UINT32_MAX
/** Transparent colour */
#define NSGIF_TRANSPARENT_COLOUR 0x00
@@ -118,7 +204,7 @@ static inline uint32_t* nsgif__bitmap_get(
nsgif_result ret;
/* Make sure we have a buffer to decode to. */
- ret = nsgif__initialise_sprite(gif, gif->width, gif->height);
+ ret = nsgif__initialise_sprite(gif, gif->info.width, gif->info.height);
if (ret != NSGIF_OK) {
return NULL;
}
@@ -183,7 +269,7 @@ static void nsgif__record_frame(
bool need_alloc = gif->prev_frame == NULL;
uint32_t *prev_frame;
- if (gif->decoded_frame == NSGIF_INVALID_FRAME ||
+ if (gif->decoded_frame == NSGIF_FRAME_INVALID ||
gif->decoded_frame == gif->prev_index) {
/* No frame to copy, or already have this frame recorded. */
return;
@@ -195,13 +281,13 @@ static void nsgif__record_frame(
}
if (gif->prev_frame != NULL &&
- gif->width * gif->height > gif->prev_width * gif->prev_height) {
+ gif->info.width * gif->info.height > gif->prev_width * gif->prev_height) {
need_alloc = true;
}
if (need_alloc) {
prev_frame = realloc(gif->prev_frame,
- gif->width * gif->height * 4);
+ gif->info.width * gif->info.height * 4);
if (prev_frame == NULL) {
return;
}
@@ -209,11 +295,11 @@ static void nsgif__record_frame(
prev_frame = gif->prev_frame;
}
- memcpy(prev_frame, bitmap, gif->width * gif->height * 4);
+ memcpy(prev_frame, bitmap, gif->info.width * gif->info.height * 4);
gif->prev_frame = prev_frame;
- gif->prev_width = gif->width;
- gif->prev_height = gif->height;
+ gif->prev_width = gif->info.width;
+ gif->prev_height = gif->info.height;
gif->prev_index = gif->decoded_frame;
}
@@ -222,8 +308,8 @@ static nsgif_result nsgif__recover_frame(
uint32_t *bitmap)
{
const uint32_t *prev_frame = gif->prev_frame;
- unsigned height = gif->height < gif->prev_height ? gif->height : gif->prev_height;
- unsigned width = gif->width < gif->prev_width ? gif->width : gif->prev_width;
+ unsigned height = gif->info.height < gif->prev_height ? gif->info.height : gif->prev_height;
+ unsigned width = gif->info.width < gif->prev_width ? gif->info.width : gif->prev_width;
if (prev_frame == NULL) {
return NSGIF_FRAME_DATA_ERROR;
@@ -232,7 +318,7 @@ static nsgif_result nsgif__recover_frame(
for (unsigned y = 0; y < height; y++) {
memcpy(bitmap, prev_frame, width * 4);
- bitmap += gif->width;
+ bitmap += gif->info.width;
prev_frame += gif->prev_width;
}
@@ -347,16 +433,16 @@ static nsgif_result nsgif__decode_complex(
{
lzw_result res;
nsgif_result ret = NSGIF_OK;
- uint32_t clip_x = gif__clip(offset_x, width, gif->width);
- uint32_t clip_y = gif__clip(offset_y, height, gif->height);
+ uint32_t clip_x = gif__clip(offset_x, width, gif->info.width);
+ uint32_t clip_y = gif__clip(offset_y, height, gif->info.height);
const uint8_t *uncompressed;
uint32_t available = 0;
uint8_t step = 24;
uint32_t skip = 0;
uint32_t y = 0;
- if (offset_x >= gif->width ||
- offset_y >= gif->height) {
+ if (offset_x >= gif->info.width ||
+ offset_y >= gif->info.height) {
return NSGIF_OK;
}
@@ -380,7 +466,7 @@ static nsgif_result nsgif__decode_complex(
uint32_t *frame_scanline;
frame_scanline = frame_data + offset_x +
- (y + offset_y) * gif->width;
+ (y + offset_y) * gif->info.width;
x = width;
while (x > 0) {
@@ -441,16 +527,16 @@ static nsgif_result nsgif__decode_simple(
uint32_t *restrict frame_data,
uint32_t *restrict colour_table)
{
- uint32_t pixels = gif->width * height;
+ uint32_t pixels = gif->info.width * height;
uint32_t written = 0;
nsgif_result ret = NSGIF_OK;
lzw_result res;
- if (offset_y >= gif->height) {
+ if (offset_y >= gif->info.height) {
return NSGIF_OK;
}
- height -= gif__clip(offset_y, height, gif->height);
+ height -= gif__clip(offset_y, height, gif->info.height);
if (height == 0) {
return NSGIF_OK;
@@ -465,7 +551,7 @@ static nsgif_result nsgif__decode_simple(
return nsgif__error_from_lzw(res);
}
- frame_data += (offset_y * gif->width);
+ frame_data += (offset_y * gif->info.width);
while (pixels > 0) {
res = lzw_decode_map(gif->lzw_ctx,
@@ -501,15 +587,15 @@ static inline nsgif_result nsgif__decode(
};
nsgif_result ret;
- uint32_t width = frame->redraw.w;
- uint32_t height = frame->redraw.h;
- uint32_t offset_x = frame->redraw.x;
- uint32_t offset_y = frame->redraw.y;
+ uint32_t width = frame->redraw.x1 - frame->redraw.x0;
+ uint32_t height = frame->redraw.y1 - frame->redraw.y0;
+ uint32_t offset_x = frame->redraw.x0;
+ uint32_t offset_y = frame->redraw.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 && width == gif->width && offset_x == 0) {
+ if (interlace == false && width == gif->info.width && offset_x == 0) {
ret = nsgif__decode_simple(gif, height, offset_y,
data, transparency_index,
frame_data, colour_table);
@@ -537,15 +623,15 @@ static void nsgif__restore_bg(
{
if (frame == NULL) {
memset(bitmap, NSGIF_TRANSPARENT_COLOUR,
- gif->width * gif->height * sizeof(*bitmap));
+ gif->info.width * gif->info.height * sizeof(*bitmap));
} else {
- uint32_t width = frame->redraw.w;
- uint32_t height = frame->redraw.h;
- uint32_t offset_x = frame->redraw.x;
- uint32_t offset_y = frame->redraw.y;
+ uint32_t width = frame->redraw.x1 - frame->redraw.x0;
+ uint32_t height = frame->redraw.y1 - frame->redraw.y0;
+ uint32_t offset_x = frame->redraw.x0;
+ uint32_t offset_y = frame->redraw.y0;
- width -= gif__clip(offset_x, width, gif->width);
- height -= gif__clip(offset_y, height, gif->height);
+ width -= gif__clip(offset_x, width, gif->info.width);
+ height -= gif__clip(offset_y, height, gif->info.height);
if (frame->display == false || width == 0) {
return;
@@ -554,14 +640,14 @@ static void nsgif__restore_bg(
if (frame->transparency) {
for (uint32_t y = 0; y < height; y++) {
uint32_t *scanline = bitmap + offset_x +
- (offset_y + y) * gif->width;
+ (offset_y + y) * gif->info.width;
memset(scanline, NSGIF_TRANSPARENT_COLOUR,
width * sizeof(*bitmap));
}
} else {
for (uint32_t y = 0; y < height; y++) {
uint32_t *scanline = bitmap + offset_x +
- (offset_y + y) * gif->width;
+ (offset_y + y) * gif->info.width;
for (uint32_t x = 0; x < width; x++) {
scanline[x] = gif->bg_colour;
}
@@ -588,7 +674,7 @@ static nsgif_result nsgif__update_bitmap(
/* Handle any bitmap clearing/restoration required before decoding this
* frame. */
- if (frame_idx == 0 || gif->decoded_frame == NSGIF_INVALID_FRAME) {
+ if (frame_idx == 0 || gif->decoded_frame == NSGIF_FRAME_INVALID) {
nsgif__restore_bg(gif, NULL, bitmap);
} else {
@@ -626,6 +712,7 @@ static nsgif_result nsgif__update_bitmap(
/**
* Parse the graphic control extension
*
+ * \param[in] gif The gif object we're decoding.
* \param[in] frame The gif object we're decoding.
* \param[in] data The data to decode.
* \param[in] len Byte length of data.
@@ -633,6 +720,7 @@ static nsgif_result nsgif__update_bitmap(
* NSGIF_OK for success.
*/
static nsgif_result nsgif__parse_extension_graphic_control(
+ const struct nsgif *gif,
struct nsgif_frame *frame,
const uint8_t *data,
size_t len)
@@ -659,6 +747,10 @@ static nsgif_result nsgif__parse_extension_graphic_control(
}
frame->frame_delay = data[3] | (data[4] << 8);
+ if (frame->frame_delay < gif->info.delay_min) {
+ frame->frame_delay = gif->delay_default;
+ }
+
if (data[2] & GIF_MASK_TRANSPARENCY) {
frame->transparency = true;
frame->transparency_index = data[5];
@@ -713,7 +805,7 @@ static nsgif_result nsgif__parse_extension_application(
if ((data[1] == 0x0b) &&
(strncmp((const char *)data + 2, "NETSCAPE2.0", 11) == 0) &&
(data[13] == 0x03) && (data[14] == 0x01)) {
- gif->loop_count = data[15] | (data[16] << 8);
+ gif->info.loop_max = data[15] | (data[16] << 8);
}
return NSGIF_OK;
@@ -763,7 +855,9 @@ static nsgif_result nsgif__parse_frame_extensions(
case GIF_EXT_GRAPHIC_CONTROL:
if (decode) {
ret = nsgif__parse_extension_graphic_control(
- frame, nsgif_data, nsgif_bytes);
+ gif, frame,
+ nsgif_data,
+ nsgif_bytes);
if (ret != NSGIF_OK) {
return ret;
}
@@ -867,7 +961,7 @@ static nsgif_result nsgif__parse_image_descriptor(
}
if (decode) {
- unsigned x, y, w, h;
+ uint32_t x, y, w, h;
if (data[0] != NSGIF_IMAGE_SEPARATOR) {
return NSGIF_FRAME_DATA_ERROR;
@@ -879,18 +973,18 @@ static nsgif_result nsgif__parse_image_descriptor(
h = data[7] | (data[8] << 8);
frame->flags = data[9];
- frame->redraw.x = x;
- frame->redraw.y = y;
- frame->redraw.w = w;
- frame->redraw.h = h;
+ frame->redraw.x0 = x;
+ frame->redraw.y0 = y;
+ frame->redraw.x1 = x + w;
+ frame->redraw.y1 = y + h;
/* Allow first frame to grow image dimensions. */
- if (gif->frame_count == 0) {
- if (x + w > gif->width) {
- gif->width = x + w;
+ if (gif->info.frame_count == 0) {
+ if (x + w > gif->info.width) {
+ gif->info.width = x + w;
}
- if (y + h > gif->height) {
- gif->height = y + h;
+ if (y + h > gif->info.height) {
+ gif->info.height = y + h;
}
}
}
@@ -1045,7 +1139,7 @@ static nsgif_result nsgif__parse_image_data(
while (block_size != 1) {
if (len < 1) return NSGIF_INSUFFICIENT_DATA;
block_size = data[0] + 1;
- /* Check if the frame data runs off the end of the file */
+ /* Check if the frame data runs off the end of the file */
if (block_size > len) {
block_size = len;
return NSGIF_OK;
@@ -1055,7 +1149,7 @@ static nsgif_result nsgif__parse_image_data(
data += block_size;
}
- gif->frame_count = frame_idx + 1;
+ gif->info.frame_count = frame_idx + 1;
gif->frames[frame_idx].display = true;
*pos = data;
@@ -1154,7 +1248,7 @@ static nsgif_result nsgif__process_frame(
}
/* Done if frame is already decoded */
- if ((int)frame_idx == gif->decoded_frame) {
+ if (frame_idx == gif->decoded_frame) {
return NSGIF_OK;
}
} else {
@@ -1201,13 +1295,57 @@ cleanup:
return ret;
}
-/* exported function documented in libnsgif.h */
-void nsgif_create(nsgif *gif, nsgif_bitmap_cb_vt *bitmap)
+/* exported function documented in nsgif.h */
+void nsgif_destroy(nsgif *gif)
{
- memset(gif, 0, sizeof(nsgif));
- gif->bitmap = *bitmap;
- gif->decoded_frame = NSGIF_INVALID_FRAME;
- gif->prev_index = NSGIF_INVALID_FRAME;
+ if (gif == NULL) {
+ return;
+ }
+
+ /* Release all our memory blocks */
+ if (gif->frame_image) {
+ assert(gif->bitmap.destroy);
+ gif->bitmap.destroy(gif->frame_image);
+ gif->frame_image = NULL;
+ }
+
+ free(gif->frames);
+ gif->frames = NULL;
+
+ free(gif->local_colour_table);
+ gif->local_colour_table = NULL;
+
+ free(gif->global_colour_table);
+ gif->global_colour_table = NULL;
+
+ free(gif->prev_frame);
+ gif->prev_frame = NULL;
+
+ lzw_context_destroy(gif->lzw_ctx);
+ gif->lzw_ctx = NULL;
+
+ free(gif);
+}
+
+/* exported function documented in nsgif.h */
+nsgif_result nsgif_create(const nsgif_bitmap_cb_vt *bitmap_vt, nsgif **gif_out)
+{
+ nsgif *gif;
+
+ gif = calloc(1, sizeof(*gif));
+ if (gif == NULL) {
+ return NSGIF_INSUFFICIENT_MEMORY;
+ }
+
+ gif->bitmap = *bitmap_vt;
+ gif->decoded_frame = NSGIF_FRAME_INVALID;
+ gif->prev_index = NSGIF_FRAME_INVALID;
+
+ gif->info.delay_min = 2;
+ gif->delay_default = 10;
+
+ *gif_out = gif;
+ return NSGIF_OK;
}
/**
@@ -1282,20 +1420,23 @@ static nsgif_result nsgif__parse_logical_screen_descriptor(
return NSGIF_INSUFFICIENT_DATA;
}
- gif->width = data[0] | (data[1] << 8);
- gif->height = data[2] | (data[3] << 8);
+ gif->info.width = data[0] | (data[1] << 8);
+ gif->info.height = data[2] | (data[3] << 8);
gif->global_colours = data[4] & NSGIF_COLOUR_TABLE_MASK;
gif->colour_table_size = 2 << (data[4] & NSGIF_COLOUR_TABLE_SIZE_MASK);
gif->bg_index = data[5];
gif->aspect_ratio = data[6];
- gif->loop_count = 1;
+ gif->info.loop_max = 1;
*pos += 7;
return NSGIF_OK;
}
-/* exported function documented in libnsgif.h */
-nsgif_result nsgif_initialise(nsgif *gif, size_t size, const uint8_t *data)
+/* exported function documented in nsgif.h */
+nsgif_result nsgif_data_scan(
+ nsgif *gif,
+ size_t size,
+ const uint8_t *data)
{
const uint8_t *nsgif_data;
nsgif_result ret;
@@ -1319,9 +1460,10 @@ nsgif_result nsgif_initialise(nsgif *gif, size_t size, const uint8_t *data)
gif->global_colour_table = NULL;
/* The caller may have been lazy and not reset any values */
- gif->frame_count = 0;
+ gif->info.frame_count = 0;
gif->frame_count_partial = 0;
- gif->decoded_frame = NSGIF_INVALID_FRAME;
+ gif->decoded_frame = NSGIF_FRAME_INVALID;
+ gif->frame = NSGIF_FRAME_INVALID;
ret = nsgif__parse_header(gif, &nsgif_data, false);
if (ret != NSGIF_OK) {
@@ -1341,16 +1483,16 @@ nsgif_result nsgif_initialise(nsgif *gif, size_t size, const uint8_t *data)
* set the sizes as 0 if they are found which results in the
* GIF being the maximum size of the frames.
*/
- if (((gif->width == 640) && (gif->height == 480)) ||
- ((gif->width == 640) && (gif->height == 512)) ||
- ((gif->width == 800) && (gif->height == 600)) ||
- ((gif->width == 1024) && (gif->height == 768)) ||
- ((gif->width == 1280) && (gif->height == 1024)) ||
- ((gif->width == 1600) && (gif->height == 1200)) ||
- ((gif->width == 0) || (gif->height == 0)) ||
- ((gif->width > 2048) || (gif->height > 2048))) {
- gif->width = 1;
- gif->height = 1;
+ if (((gif->info.width == 640) && (gif->info.height == 480)) ||
+ ((gif->info.width == 640) && (gif->info.height == 512)) ||
+ ((gif->info.width == 800) && (gif->info.height == 600)) ||
+ ((gif->info.width == 1024) && (gif->info.height == 768)) ||
+ ((gif->info.width == 1280) && (gif->info.height == 1024)) ||
+ ((gif->info.width == 1600) && (gif->info.height == 1200)) ||
+ ((gif->info.width == 0) || (gif->info.height == 0)) ||
+ ((gif->info.width > 2048) || (gif->info.height > 2048))) {
+ gif->info.width = 1;
+ gif->info.height = 1;
}
/* Allocate some data irrespective of whether we've got any
@@ -1362,7 +1504,6 @@ nsgif_result nsgif_initialise(nsgif *gif, size_t size, const uint8_t *data)
gif->local_colour_table = calloc(NSGIF_MAX_COLOURS, sizeof(uint32_t));
if ((gif->global_colour_table == NULL) ||
(gif->local_colour_table == NULL)) {
- nsgif_finalise(gif);
return NSGIF_INSUFFICIENT_MEMORY;
}
@@ -1430,38 +1571,186 @@ nsgif_result nsgif_initialise(nsgif *gif, size_t size, const uint8_t *data)
/* Repeatedly try to initialise frames */
do {
- ret = nsgif__process_frame(gif, gif->frame_count, false);
+ ret = nsgif__process_frame(gif, gif->info.frame_count, false);
} while (ret == NSGIF_WORKING);
return ret;
}
-/* exported function documented in libnsgif.h */
-nsgif_result nsgif_decode_frame(nsgif *gif, uint32_t frame)
+static void nsgif__redraw_rect_extend(const nsgif_rect *frame, nsgif_rect *redraw)
{
- return nsgif__process_frame(gif, frame, true);
+ if (redraw->x1 == 0 || redraw->y1 == 0) {
+ *redraw = *frame;
+ } else {
+ if (redraw->x0 > frame->x0) {
+ redraw->x0 = frame->x0;
+ }
+ if (redraw->x1 < frame->x1) {
+ redraw->x1 = frame->x1;
+ }
+ if (redraw->y0 > frame->y0) {
+ redraw->y0 = frame->y0;
+ }
+ if (redraw->y1 < frame->y1) {
+ redraw->y1 = frame->y1;
+ }
+ }
}
-/* exported function documented in libnsgif.h */
-void nsgif_finalise(nsgif *gif)
+static uint32_t nsgif__frame_next(
+ nsgif *gif,
+ bool partial,
+ uint32_t frame)
{
- /* Release all our memory blocks */
- if (gif->frame_image) {
- assert(gif->bitmap.destroy);
- gif->bitmap.destroy(gif->frame_image);
+ uint32_t frames = partial ?
+ gif->frame_count_partial :
+ gif->info.frame_count;
+
+ if (frames == 0) {
+ return NSGIF_FRAME_INVALID;
}
- gif->frame_image = NULL;
- free(gif->frames);
- gif->frames = NULL;
- free(gif->local_colour_table);
- gif->local_colour_table = NULL;
- free(gif->global_colour_table);
- gif->global_colour_table = NULL;
+ frame++;
+ return (frame >= frames) ? 0 : frame;
+}
- free(gif->prev_frame);
- gif->prev_frame = NULL;
+static nsgif_result nsgif__next_displayable_frame(
+ nsgif *gif,
+ uint32_t *frame,
+ uint32_t *delay)
+{
+ uint32_t next = *frame;
- lzw_context_destroy(gif->lzw_ctx);
- gif->lzw_ctx = NULL;
+ do {
+ next = nsgif__frame_next(gif, false, next);
+ if (next == *frame || next == NSGIF_FRAME_INVALID) {
+ return NSGIF_FRAME_NO_DISPLAY;
+ }
+
+ if (delay != NULL) {
+ *delay += gif->frames[next].frame_delay;
+ }
+
+ } while (gif->frames[next].display == false);
+
+ *frame = next;
+ return NSGIF_OK;
+}
+
+static inline bool nsgif__animation_complete(int count, int max)
+{
+ if (max == 0) {
+ return false;
+ }
+
+ return (count >= max);
+}
+
+nsgif_result nsgif_reset(
+ nsgif *gif)
+{
+ gif->info.loop_count = 0;
+ gif->frame = NSGIF_FRAME_INVALID;
+
+ return NSGIF_OK;
+}
+
+/* exported function documented in nsgif.h */
+nsgif_result nsgif_frame_prepare(
+ nsgif *gif,
+ nsgif_rect *area,
+ uint32_t *delay_cs,
+ uint32_t *frame_new)
+{
+ nsgif_result ret;
+ nsgif_rect rect = {
+ .x1 = 0,
+ .y1 = 0,
+ };
+ uint32_t delay = 0;
+ uint32_t frame = gif->frame;
+ uint32_t frame_next;
+
+ if (gif->frame != NSGIF_FRAME_INVALID &&
+ gif->frame != 0 &&
+ gif->frame < gif->info.frame_count &&
+ gif->frames[gif->frame].display) {
+ rect = gif->frames[gif->frame].redraw;
+ }
+
+ if (nsgif__animation_complete(
+ gif->info.loop_count,
+ gif->info.loop_max)) {
+ return NSGIF_ANIMATION_COMPLETE;
+ }
+
+ ret = nsgif__next_displayable_frame(gif, &frame, NULL);
+ if (ret != NSGIF_OK) {
+ return ret;
+ }
+
+ if (gif->frame != NSGIF_FRAME_INVALID && frame < gif->frame) {
+ gif->info.loop_count++;
+ }
+
+ frame_next = frame;
+ ret = nsgif__next_displayable_frame(gif, &frame_next, &delay);
+ if (ret != NSGIF_OK) {
+ return ret;
+ }
+
+ if (frame_next < frame) {
+ if (nsgif__animation_complete(
+ gif->info.loop_count + 1,
+ gif->info.loop_max)) {
+ delay = NSGIF_INFINITE;
+ }
+ }
+
+ gif->frame = frame;
+ nsgif__redraw_rect_extend(&gif->frames[frame].redraw, &rect);
+
+ *frame_new = gif->frame;
+ *delay_cs = delay;
+ *area = rect;
+
+ return NSGIF_OK;
+}
+
+/* exported function documented in nsgif.h */
+nsgif_result nsgif_frame_decode(
+ nsgif *gif,
+ uint32_t frame,
+ const uint32_t **buffer)
+{
+ uint32_t start_frame;
+ nsgif_result ret = NSGIF_OK;
+
+ if (gif->decoded_frame == frame) {
+ *buffer = gif->frame_image;
+ return NSGIF_OK;
+
+ } else if (gif->decoded_frame >= frame ||
+ gif->decoded_frame == NSGIF_FRAME_INVALID) {
+ /* Can skip to first frame or restart. */
+ start_frame = 0;
+ } else {
+ start_frame = nsgif__frame_next(
+ gif, false, gif->decoded_frame);
+ }
+
+ for (uint32_t f = start_frame; f <= frame; f++) {
+ ret = nsgif__process_frame(gif, f, true);
+ if (ret != NSGIF_OK) {
+ return ret;
+ }
+ }
+
+ *buffer = gif->frame_image;
+ return ret;
+}
+
+const nsgif_info_t *nsgif_get_info(const nsgif *gif)
+{
+ return &gif->info;
}