Gitweb links:
...log
http://git.netsurf-browser.org/libnsgif.git/shortlog/032e62c80df60b5fcedc...
...commit
http://git.netsurf-browser.org/libnsgif.git/commit/032e62c80df60b5fcedcd7...
...tree
http://git.netsurf-browser.org/libnsgif.git/tree/032e62c80df60b5fcedcd75f...
The branch, master has been updated
via 032e62c80df60b5fcedcd75f3043e0a18e114b6e (commit)
via 4f20f753689e93c8448c58fd9c4245e0817298fa (commit)
via 2487a3b82a88ce5d2c44028ebb1e86e22a2ed179 (commit)
via 16b5148bb734d9b7bb39419d81294c681b04b556 (commit)
via 2b2fa970ff094ae4ffc219603a793cb54a960480 (commit)
via 893c7c14c9ff58ecfd2d370e20e15eac6da779db (commit)
via c240b835fe6e3774237b6d968ddb405c8085690f (commit)
via 15670d3d0e3dcf9db168244428cab8e43460251a (commit)
via e2458d5e968323735a2a836da5f5838e62caf6b6 (commit)
via 470c70dac58c146ac280f6c7920764e59a885a42 (commit)
via 9f28b76c419889ef76832bcb65912acd42f9e685 (commit)
via b7952d1c53dffeced0feb9d75e66da972d68318a (commit)
via 69581f88b3dd5be49b4de539e3a836006a988235 (commit)
via 1b0c8759f5d96e96ded4cb50bb2772103780e2eb (commit)
via d1762afcee8b6541a974eeb37edea2a828096b8a (commit)
via 947586eb1b14c267ee996ba655af13899488c0f3 (commit)
via 24c503f95e1f9ef726e546e616649120b666d15d (commit)
via 5e0642811e2edce8d8845fdf0bcb8f9eefc71f80 (commit)
via 9f5e2f1af86d35247895dd57bf03f32c5539a04d (commit)
via 5e93ea19730e0df42e9dc3a777cde466f5a8f73d (commit)
via 920ba6c423181ee0eec932c5daeda75fb13fc140 (commit)
via 1b8341cb075731cffbf69141c24d8954fef4ee50 (commit)
via 8a5130c5559989c3b9ea6f74469427abe08c7193 (commit)
via 82d8eff773bbb56cd2021187949e39fcc28899c7 (commit)
via 677ba295b37cc1671f40383f92ad133fe6db26d9 (commit)
via 1e4a781b695992fbe09459baa3518637fba96223 (commit)
via c17ab37852fadbc6fe7ae80a969aa408f3d809b1 (commit)
via 0f7bcedeedff2cdee82644213741b5b96d655d92 (commit)
via 22b5b8f627a770172fbcc9e37656dcf40836f5df (commit)
via 1587b6a1a936ba11dfc951d07766562dbb4e8cf9 (commit)
via d96f028f305b5286618dcfc682e0de2515b3b7bd (commit)
via 2d440af6eed4d0ff82b8ef5143c7d2aee3856cc8 (commit)
via ea368d768fa12bffd51b0c8f38c374c5d398c859 (commit)
via 999dd692409595601648495543e16e70bc946e45 (commit)
via 1fbc97ea763c3f76eea43695637fe283ce831746 (commit)
via 41f33c7495e7b83b40a93e4a76481d770f0c5f7b (commit)
via 42cfb0d9a5b4e6dbf0981262916e160be438157b (commit)
via 6765615f800be363c88f085f7b2ebf1dd3d68b26 (commit)
via 58efffc9ec484899333458746bc169b03d69293b (commit)
via b8ae276840d1ad997d2400b001d2ca47b94ed4a7 (commit)
via a13205c215f879c0e63d736fca2f56028b243ffa (commit)
via 1f87439ecacaecbf70fdd740708745b1b90cad89 (commit)
via 827903b2cc1222b476c7f47bb462f526cb14a52c (commit)
via eb855e5e40417b0c3c54c937df7b35940288fd00 (commit)
via 496870618e9977cfcac4b0433bf17b6e8e4d2434 (commit)
via 8b30f0709564df422c549cccc1a7868588c30a67 (commit)
via eb0aa2326fd1da0bf4c74d083f889eec1632e1dd (commit)
via 811922e7a5c7de1a83a016ce76d0ea009b26d9fa (commit)
via e1c7bbf7fa9e762ac406310c62c3e30cde40f998 (commit)
via d78b6d12479e2567da7e078d52d9bc8fd0ad587c (commit)
via 838a69e759309db257c2ece5102ad732590847db (commit)
via bca8f74754f2316f40374368edad191a6803a1aa (commit)
via 6661c46dc8f836052785ef5db1d363b4a0bfcf25 (commit)
via b6d35c01d3955c4c247c710187f14c1ca2e4cd94 (commit)
via 623953b935cc9910e3cb7df178773a6092723913 (commit)
via 6a5c2a60d82fa39d1d653ced3c25eb15b3a0007f (commit)
via 2aa9bae406866de8dfeea142e4e02c619a20ae23 (commit)
via 323d6e3827258c8db1d0e49be9ac154d253efece (commit)
via cc492166b9988972b87492fb37b92c759de6b4de (commit)
via e60c1f6c8de7e154c12019140f92bd05b8966194 (commit)
via 4e957db96569b680d7db86ac1a072f0805ee512b (commit)
via e9a47faf8062f9b7db406c273fcbafa2aa6388e1 (commit)
via 46d7a0bb13a36dabaa5ede2158d22e3e702f5ad2 (commit)
via 8eac839eed55432446077659c052b6459509af2a (commit)
via 12580f6d6737c7a549f465cd76183798cb6539bc (commit)
via 6344f78d4faac7f141edd2ecb21223f95988f582 (commit)
via 5af2a3dddfebff63742bfd83618d037b4d95bca2 (commit)
via bd01b79660e5c41e04180ad291bf82619d420c79 (commit)
via 3d3dd3d63af6aa0c7ee126978c4ee39d2dd7e817 (commit)
from dabc6307eaa9e93958fd7d6084bb154e0ae03c54 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff
http://git.netsurf-browser.org/libnsgif.git/commit/?id=032e62c80df60b5fce...
commit 032e62c80df60b5fcedcd75f3043e0a18e114b6e
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Delay LZW creation until frame proccessing.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 5ba092a..6c21a7c 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -1207,14 +1207,6 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const
uint8_t *data)
gif->buffer_size = size;
gif->gif_data = data;
- if (gif->lzw_ctx == NULL) {
- lzw_result res = lzw_context_create(
- (struct lzw_ctx **)&gif->lzw_ctx);
- if (res != LZW_OK) {
- return gif_error_from_lzw(res);
- }
- }
-
/* Get our current processing position */
gif_data = gif->gif_data + gif->buffer_position;
@@ -1331,6 +1323,14 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const
uint8_t *data)
}
}
+ if (gif->lzw_ctx == NULL) {
+ lzw_result res = lzw_context_create(
+ (struct lzw_ctx **)&gif->lzw_ctx);
+ if (res != LZW_OK) {
+ return gif_error_from_lzw(res);
+ }
+ }
+
/* Repeatedly try to initialise frames */
do {
ret = gif__process_frame(gif, gif->frame_count, false);
commitdiff
http://git.netsurf-browser.org/libnsgif.git/commit/?id=4f20f753689e93c844...
commit 4f20f753689e93c8448c58fd9c4245e0817298fa
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Simplify check for no frame data.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 6a93dc8..5ba092a 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -67,9 +67,6 @@ enum gif_disposal {
#define GIF_BLOCK_TERMINATOR 0x00
#define GIF_TRAILER 0x3b
-/** standard GIF header size */
-#define GIF_STANDARD_HEADER_SIZE 13
-
static gif_result gif_error_from_lzw(lzw_result l_res)
{
static const gif_result g_res[] = {
@@ -1247,6 +1244,9 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const
uint8_t *data)
return ret;
}
+ /* Remember we've done this now */
+ gif->buffer_position = gif_data - gif->gif_data;
+
/* Some broken GIFs report the size as the screen size they
* were created in. As such, we detect for the common cases and
* set the sizes as 0 if they are found which results in the
@@ -1286,16 +1286,11 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const
uint8_t *data)
* termination block) Although generally useless, the GIF
* specification does not expressly prohibit this
*/
- if (gif->buffer_size == (GIF_STANDARD_HEADER_SIZE + 1)) {
+ if (gif->buffer_size == gif->buffer_position + 1) {
if (gif_data[0] == GIF_TRAILER) {
return GIF_OK;
- } else {
- return GIF_INSUFFICIENT_DATA;
}
}
-
- /* Remember we've done this now */
- gif->buffer_position = gif_data - gif->gif_data;
}
/* Do the colour map if we haven't already. As the top byte is always
commitdiff
http://git.netsurf-browser.org/libnsgif.git/commit/?id=2487a3b82a88ce5d2c...
commit 2487a3b82a88ce5d2c44028ebb1e86e22a2ed179
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Split out logical screen descriptor extraction.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 4e419a2..6a93dc8 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -1158,6 +1158,48 @@ static gif_result gif__parse_header(
return GIF_OK;
}
+/**
+ * Read Logical Screen Descriptor.
+ *
+ * 7-byte Logical Screen Descriptor is:
+ *
+ * +0 SHORT Logical Screen Width
+ * +2 SHORT Logical Screen Height
+ * +4 CHAR __Packed Fields__
+ * 1BIT Global Colour Table Flag
+ * 3BITS Colour Resolution
+ * 1BIT Sort Flag
+ * 3BITS Size of Global Colour Table
+ * +5 CHAR Background Colour Index
+ * +6 CHAR Pixel Aspect Ratio
+ *
+ * \param[in] gif The GIF object we're decoding.
+ * \param[in,out] pos The current buffer position, updated on success.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_logical_screen_descriptor(
+ struct gif_animation *gif,
+ const uint8_t **pos)
+{
+ const uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
+
+ if (len < 7) {
+ return GIF_INSUFFICIENT_DATA;
+ }
+
+ gif->width = data[0] | (data[1] << 8);
+ gif->height = data[2] | (data[3] << 8);
+ gif->global_colours = data[4] & GIF_COLOUR_TABLE_MASK;
+ gif->colour_table_size = 2 << (data[4] & GIF_COLOUR_TABLE_SIZE_MASK);
+ gif->bg_index = data[5];
+ gif->aspect_ratio = data[6];
+ gif->loop_count = 1;
+
+ *pos += 7;
+ return GIF_OK;
+}
+
/* exported function documented in libnsgif.h */
gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
{
@@ -1176,13 +1218,6 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const
uint8_t *data)
}
}
- /* Check for sufficient data to be a GIF (6-byte header + 7-byte
- * logical screen descriptor)
- */
- if (gif->buffer_size < GIF_STANDARD_HEADER_SIZE) {
- return GIF_INSUFFICIENT_DATA;
- }
-
/* Get our current processing position */
gif_data = gif->gif_data + gif->buffer_position;
@@ -1207,26 +1242,10 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const
uint8_t *data)
return ret;
}
- /* 7-byte Logical Screen Descriptor is:
- *
- * +0 SHORT Logical Screen Width
- * +2 SHORT Logical Screen Height
- * +4 CHAR __Packed Fields__
- * 1BIT Global Colour Table Flag
- * 3BITS Colour Resolution
- * 1BIT Sort Flag
- * 3BITS Size of Global Colour Table
- * +5 CHAR Background Colour Index
- * +6 CHAR Pixel Aspect Ratio
- */
- gif->width = gif_data[0] | (gif_data[1] << 8);
- gif->height = gif_data[2] | (gif_data[3] << 8);
- gif->global_colours = (gif_data[4] & GIF_COLOUR_TABLE_MASK);
- gif->colour_table_size = (2 << (gif_data[4] &
GIF_COLOUR_TABLE_SIZE_MASK));
- gif->bg_index = gif_data[5];
- gif->aspect_ratio = gif_data[6];
- gif->loop_count = 1;
- gif_data += 7;
+ ret = gif__parse_logical_screen_descriptor(gif, &gif_data);
+ if (ret != GIF_OK) {
+ return ret;
+ }
/* Some broken GIFs report the size as the screen size they
* were created in. As such, we detect for the common cases and
commitdiff
http://git.netsurf-browser.org/libnsgif.git/commit/?id=16b5148bb734d9b7bb...
commit 16b5148bb734d9b7bb39419d81294c681b04b556
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
LZW: Remove double blank lines.
diff --git a/src/lzw.c b/src/lzw.c
index 710895e..6f85caa 100644
--- a/src/lzw.c
+++ b/src/lzw.c
@@ -96,7 +96,6 @@ struct lzw_ctx {
uint8_t stack_base[LZW_TABLE_ENTRY_MAX];
};
-
/* Exported function, documented in lzw.h */
lzw_result lzw_context_create(struct lzw_ctx **ctx)
{
@@ -109,14 +108,12 @@ lzw_result lzw_context_create(struct lzw_ctx **ctx)
return LZW_OK;
}
-
/* Exported function, documented in lzw.h */
void lzw_context_destroy(struct lzw_ctx *ctx)
{
free(ctx);
}
-
/**
* Advance the context to the next sub-block in the input data.
*
@@ -153,7 +150,6 @@ static lzw_result lzw__block_advance(struct lzw_read_ctx *restrict
ctx)
return LZW_OK;
}
-
/**
* Get the next LZW code of given size from the raw input data.
*
@@ -223,7 +219,6 @@ static inline lzw_result lzw__read_code(
return LZW_OK;
}
-
/**
* Handle clear code.
*
diff --git a/src/lzw.h b/src/lzw.h
index 4568298..c68753a 100644
--- a/src/lzw.h
+++ b/src/lzw.h
@@ -20,11 +20,9 @@
/** Maximum LZW code size in bits */
#define LZW_CODE_MAX 12
-
/* Declare lzw internal context structure */
struct lzw_ctx;
-
/** LZW decoding response codes */
typedef enum lzw_result {
LZW_OK, /**< Success */
@@ -38,7 +36,6 @@ typedef enum lzw_result {
LZW_BAD_CODE, /**< Error: Bad LZW code */
} lzw_result;
-
/**
* Create an LZW decompression context.
*
commitdiff
http://git.netsurf-browser.org/libnsgif.git/commit/?id=2b2fa970ff094ae4ff...
commit 2b2fa970ff094ae4ffc219603a793cb54a960480
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Remove double blank lines.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 3e11dde..4e419a2 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -30,7 +30,6 @@
* \todo Plain text and comment extensions should be implemented.
*/
-
/** Maximum colour table size */
#define GIF_MAX_COLOURS 256
@@ -1326,14 +1325,12 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const
uint8_t *data)
return ret;
}
-
/* exported function documented in libnsgif.h */
gif_result gif_decode_frame(gif_animation *gif, unsigned int frame)
{
return gif__process_frame(gif, frame, true);
}
-
/* exported function documented in libnsgif.h */
void gif_finalise(gif_animation *gif)
{
commitdiff
http://git.netsurf-browser.org/libnsgif.git/commit/?id=893c7c14c9ff58ecfd...
commit 893c7c14c9ff58ecfd2d370e20e15eac6da779db
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Initilisation: Make frame initialisation loop more readable.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 457ce00..3e11dde 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -1319,7 +1319,9 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const
uint8_t *data)
}
/* Repeatedly try to initialise frames */
- while ((ret = gif__process_frame(gif, gif->frame_count, false)) == GIF_WORKING);
+ do {
+ ret = gif__process_frame(gif, gif->frame_count, false);
+ } while (ret == GIF_WORKING);
return ret;
}
commitdiff
http://git.netsurf-browser.org/libnsgif.git/commit/?id=c240b835fe6e377423...
commit c240b835fe6e3774237b6d968ddb405c8085690f
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Unify insufficient data error codes.
There is no difference in what the client needs to do.
If there are displayable frames, they can display them.
Otherwise more data is needed.
Internally only `GIF_INSUFFICIENT_DATA` is used now.
To remove the `GIF_INSUFFICIENT_FRAME_DATA` could make existing
client applications fail to compile, so it is left as an alias
to the same value.
At some point the API will be changed drastically, but for now
I want existing applications to still build.
diff --git a/include/libnsgif.h b/include/libnsgif.h
index 5bd26b4..309e2f0 100644
--- a/include/libnsgif.h
+++ b/include/libnsgif.h
@@ -23,9 +23,9 @@
typedef enum {
GIF_WORKING = 1,
GIF_OK = 0,
- GIF_INSUFFICIENT_FRAME_DATA = -1,
+ GIF_INSUFFICIENT_DATA = -1,
+ GIF_INSUFFICIENT_FRAME_DATA = GIF_INSUFFICIENT_DATA,
GIF_FRAME_DATA_ERROR = -2,
- GIF_INSUFFICIENT_DATA = -3,
GIF_DATA_ERROR = -4,
GIF_INSUFFICIENT_MEMORY = -5,
GIF_FRAME_NO_DISPLAY = -6,
@@ -166,11 +166,9 @@ void gif_create(gif_animation *gif, gif_bitmap_callback_vt
*bitmap_callbacks);
*
* \return Error return value.
* - GIF_FRAME_DATA_ERROR for GIF frame data error
- * - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to process
- * any more frames
+ * - GIF_INSUFFICIENT_DATA reached unexpected end of source data
* - GIF_INSUFFICIENT_MEMORY for memory error
* - GIF_DATA_ERROR for GIF error
- * - GIF_INSUFFICIENT_DATA for insufficient data to do anything
* - GIF_OK for successful decoding
* - GIF_WORKING for successful decoding if more frames are expected
*/
@@ -181,9 +179,8 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const
uint8_t *data);
*
* \return Error return value.
* - GIF_FRAME_DATA_ERROR for GIF frame data error
- * - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
* - GIF_DATA_ERROR for GIF error (invalid frame header)
- * - GIF_INSUFFICIENT_DATA for insufficient data to do anything
+ * - GIF_INSUFFICIENT_DATA reached unexpected end of source data
* - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
* - GIF_OK for successful decoding
*/
diff --git a/src/libnsgif.c b/src/libnsgif.c
index f0fab44..457ce00 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -77,7 +77,7 @@ static gif_result gif_error_from_lzw(lzw_result l_res)
[LZW_OK] = GIF_OK,
[LZW_OK_EOD] = GIF_END_OF_FRAME,
[LZW_NO_MEM] = GIF_INSUFFICIENT_MEMORY,
- [LZW_NO_DATA] = GIF_INSUFFICIENT_FRAME_DATA,
+ [LZW_NO_DATA] = GIF_INSUFFICIENT_DATA,
[LZW_EOI_CODE] = GIF_FRAME_DATA_ERROR,
[LZW_BAD_ICODE] = GIF_FRAME_DATA_ERROR,
[LZW_BAD_CODE] = GIF_FRAME_DATA_ERROR,
@@ -560,7 +560,7 @@ static gif_result gif__update_bitmap(
* \param[in] frame The gif object we're decoding.
* \param[in] data The data to decode.
* \param[in] len Byte length of data.
- * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
+ * \return GIF_INSUFFICIENT_DATA if more data is needed,
* GIF_OK for success.
*/
static gif_result gif__parse_extension_graphic_control(
@@ -581,7 +581,7 @@ static gif_result gif__parse_extension_graphic_control(
* +5 CHAR Transparent Color Index
*/
if (len < 6) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ return GIF_INSUFFICIENT_DATA;
}
frame->frame_delay = data[3] | (data[4] << 8);
@@ -616,7 +616,7 @@ static gif_result gif__parse_extension_graphic_control(
* \param[in] gif The gif object we're decoding.
* \param[in] data The data to decode.
* \param[in] len Byte length of data.
- * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
+ * \return GIF_INSUFFICIENT_DATA if more data is needed,
* GIF_OK for success.
*/
static gif_result gif__parse_extension_application(
@@ -633,7 +633,7 @@ static gif_result gif__parse_extension_application(
* +13 1-256 Application Data (Data sub-blocks)
*/
if (len < 17) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ return GIF_INSUFFICIENT_DATA;
}
if ((data[1] == 0x0b) &&
@@ -651,7 +651,7 @@ static gif_result gif__parse_extension_application(
* \param[in] gif The gif object we're decoding.
* \param[in] frame The frame to parse extensions for.
* \param[in] decode Whether to decode or skip over the extension.
- * \return GIF_INSUFFICIENT_FRAME_DATA if more data is needed,
+ * \return GIF_INSUFFICIENT_DATA if more data is needed,
* GIF_OK for success.
*/
static gif_result gif__parse_frame_extensions(
@@ -673,7 +673,7 @@ static gif_result gif__parse_frame_extensions(
gif_bytes--;
if (gif_bytes == 0) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ return GIF_INSUFFICIENT_DATA;
}
/* Switch on extension label */
@@ -715,7 +715,7 @@ static gif_result gif__parse_frame_extensions(
* the extension size itself
*/
if (gif_bytes < 2) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ return GIF_INSUFFICIENT_DATA;
}
gif_data += 2 + gif_data[1];
}
@@ -725,7 +725,7 @@ static gif_result gif__parse_frame_extensions(
while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) {
gif_data += gif_data[0] + 1;
if (gif_data >= gif_end) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ return GIF_INSUFFICIENT_DATA;
}
}
gif_data++;
@@ -779,7 +779,7 @@ static gif_result gif__parse_image_descriptor(
assert(frame != NULL);
if (len < GIF_IMAGE_DESCRIPTOR_LEN) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ return GIF_INSUFFICIENT_DATA;
}
if (decode) {
@@ -830,11 +830,7 @@ static gif_result gif__colour_table_extract(
size_t len = gif->gif_data + gif->buffer_size - data;
if (len < colour_table_entries * 3) {
- if (colour_table == gif->local_colour_table) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- } else {
- return GIF_INSUFFICIENT_DATA;
- }
+ return GIF_INSUFFICIENT_DATA;
}
if (decode) {
@@ -935,7 +931,7 @@ static gif_result gif__parse_image_data(
/* Fall through. */
case 1: if (data[0] == GIF_TRAILER) return GIF_OK;
/* Fall through. */
- case 0: return GIF_INSUFFICIENT_FRAME_DATA;
+ case 0: return GIF_INSUFFICIENT_DATA;
}
minimum_code_size = data[0];
@@ -953,7 +949,7 @@ static gif_result gif__parse_image_data(
len--;
while (block_size != 1) {
- if (len < 1) return GIF_INSUFFICIENT_FRAME_DATA;
+ if (len < 1) return GIF_INSUFFICIENT_DATA;
block_size = data[0] + 1;
/* Check if the frame data runs off the end of the file */
if (block_size > len) {
@@ -971,7 +967,7 @@ static gif_result gif__parse_image_data(
/* Check if we've finished */
if (len < 1) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ return GIF_INSUFFICIENT_DATA;
} else {
if (data[0] == GIF_TRAILER) {
return GIF_OK;
@@ -1026,10 +1022,9 @@ static struct gif_frame *gif__get_frame(
* \param[in] frame_idx The frame number to decode.
* \param[in] decode Whether to decode the graphical image data.
* \return error code
- * - GIF_INSUFFICIENT_DATA for insufficient data to do anything
+ * - GIF_INSUFFICIENT_DATA reached unexpected end of source data.
* - GIF_FRAME_DATA_ERROR for GIF frame data error
* - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
- * - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
* - GIF_DATA_ERROR for GIF error (invalid frame header)
* - GIF_OK for successful decoding
* - GIF_WORKING for successful decoding if more frames are expected
@@ -1326,19 +1321,6 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const
uint8_t *data)
/* Repeatedly try to initialise frames */
while ((ret = gif__process_frame(gif, gif->frame_count, false)) == GIF_WORKING);
- /* If there was a memory error tell the caller */
- if ((ret == GIF_INSUFFICIENT_MEMORY) ||
- (ret == GIF_DATA_ERROR)) {
- return ret;
- }
-
- /* If we didn't have some frames then a GIF_INSUFFICIENT_DATA becomes a
- * GIF_INSUFFICIENT_FRAME_DATA
- */
- if (ret == GIF_INSUFFICIENT_DATA && gif->frame_count_partial > 0) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
-
return ret;
}
diff --git a/test/decode_gif.c b/test/decode_gif.c
index 64387ef..52d73b6 100644
--- a/test/decode_gif.c
+++ b/test/decode_gif.c
@@ -113,9 +113,6 @@ static void warning(const char *context, gif_result code)
fprintf(stderr, "%s failed: ", context);
switch (code)
{
- case GIF_INSUFFICIENT_FRAME_DATA:
- fprintf(stderr, "GIF_INSUFFICIENT_FRAME_DATA");
- break;
case GIF_FRAME_DATA_ERROR:
fprintf(stderr, "GIF_FRAME_DATA_ERROR");
break;
commitdiff
http://git.netsurf-browser.org/libnsgif.git/commit/?id=15670d3d0e3dcf9db1...
commit 15670d3d0e3dcf9db168244428cab8e43460251a
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Use common colour table code for global colour table.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index b7b007b..f0fab44 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -830,7 +830,11 @@ static gif_result gif__colour_table_extract(
size_t len = gif->gif_data + gif->buffer_size - data;
if (len < colour_table_entries * 3) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ if (colour_table == gif->local_colour_table) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ } else {
+ return GIF_INSUFFICIENT_DATA;
+ }
}
if (decode) {
@@ -1164,7 +1168,6 @@ static gif_result gif__parse_header(
gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
{
const uint8_t *gif_data;
- uint32_t index;
gif_result ret;
/* Initialize values */
@@ -1289,27 +1292,14 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const
uint8_t *data)
if (gif->global_colour_table[0] == GIF_PROCESS_COLOURS) {
/* Check for a global colour map signified by bit 7 */
if (gif->global_colours) {
- if (gif->buffer_size < (gif->colour_table_size * 3 +
GIF_STANDARD_HEADER_SIZE)) {
- return GIF_INSUFFICIENT_DATA;
- }
- for (index = 0; index < gif->colour_table_size; index++) {
- /* Gif colour map contents are r,g,b.
- *
- * We want to pack them bytewise into the
- * colour table, such that the red component
- * is in byte 0 and the alpha component is in
- * byte 3.
- */
- uint8_t *entry = (uint8_t *) &gif->
- global_colour_table[index];
-
- entry[0] = gif_data[0]; /* r */
- entry[1] = gif_data[1]; /* g */
- entry[2] = gif_data[2]; /* b */
- entry[3] = 0xff; /* a */
-
- gif_data += 3;
+ ret = gif__colour_table_extract(gif,
+ gif->global_colour_table,
+ gif->colour_table_size,
+ &gif_data, true);
+ if (ret != GIF_OK) {
+ return ret;
}
+
gif->buffer_position = (gif_data - gif->gif_data);
} else {
/* Create a default colour table with the first two
commitdiff
http://git.netsurf-browser.org/libnsgif.git/commit/?id=e2458d5e968323735a...
commit e2458d5e968323735a2a836da5f5838e62caf6b6
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Split out colour table extraction.
This will allow the local and global colour table extraction to
share code.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 6de26a0..b7b007b 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -810,40 +810,32 @@ static gif_result gif__parse_image_descriptor(
}
/**
- * Get a frame's colour table.
- *
- * Sets up gif->colour_table for the frame.
+ * Extract a GIF colour table into a LibNSGIF colour table buffer.
*
- * \param[in] gif The gif object we're decoding.
- * \param[in] frame The frame to get the colour table for.
+ * \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.
* \return GIF_OK on success, appropriate error otherwise.
*/
-static gif_result gif__parse_colour_table(
+static gif_result gif__colour_table_extract(
struct gif_animation *gif,
- struct gif_frame *frame,
+ uint32_t *colour_table,
+ size_t colour_table_entries,
const uint8_t **pos,
bool decode)
{
- unsigned colour_table_size;
const uint8_t *data = *pos;
size_t len = gif->gif_data + gif->buffer_size - data;
- assert(gif != NULL);
- assert(frame != NULL);
-
- if ((frame->flags & GIF_COLOUR_TABLE_MASK) == 0) {
- gif->colour_table = gif->global_colour_table;
- return GIF_OK;
- }
-
- colour_table_size = 2 << (frame->flags & GIF_COLOUR_TABLE_SIZE_MASK);
- if (len < colour_table_size * 3) {
+ if (len < colour_table_entries * 3) {
return GIF_INSUFFICIENT_FRAME_DATA;
}
if (decode) {
- int count = colour_table_size;
- uint8_t *entry = (uint8_t *)gif->local_colour_table;
+ int count = colour_table_entries;
+ uint8_t *entry = (uint8_t *)colour_table;
while (count--) {
/* Gif colour map contents are r,g,b.
@@ -861,8 +853,43 @@ static gif_result gif__parse_colour_table(
}
}
+ *pos += colour_table_entries * 3;
+ return GIF_OK;
+}
+
+/**
+ * Get a frame's colour table.
+ *
+ * Sets up gif->colour_table for the frame.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to get the colour table for.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_colour_table(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ const uint8_t **pos,
+ bool decode)
+{
+ gif_result ret;
+
+ assert(gif != NULL);
+ assert(frame != NULL);
+
+ if ((frame->flags & GIF_COLOUR_TABLE_MASK) == 0) {
+ gif->colour_table = gif->global_colour_table;
+ return GIF_OK;
+ }
+
+ ret = gif__colour_table_extract(gif, gif->local_colour_table,
+ 2 << (frame->flags & GIF_COLOUR_TABLE_SIZE_MASK),
+ pos, decode);
+ if (ret != GIF_OK) {
+ return ret;
+ }
+
gif->colour_table = gif->local_colour_table;
- *pos += colour_table_size * 3;
return GIF_OK;
}
commitdiff
http://git.netsurf-browser.org/libnsgif.git/commit/?id=470c70dac58c146ac2...
commit 470c70dac58c146ac280f6c7920764e59a885a42
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Split header reader out into helper function.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 5cfa020..6de26a0 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -1091,6 +1091,47 @@ void gif_create(gif_animation *gif, gif_bitmap_callback_vt
*bitmap_callbacks)
gif->prev_index = GIF_INVALID_FRAME;
}
+/**
+ * Read GIF header.
+ *
+ * 6-byte GIF file header is:
+ *
+ * +0 3CHARS Signature ('GIF')
+ * +3 3CHARS Version ('87a' or '89a')
+ *
+ * \param[in] gif The GIF object we're decoding.
+ * \param[in,out] pos The current buffer position, updated on success.
+ * \param[in] strict Whether to require a known GIF version.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_header(
+ struct gif_animation *gif,
+ const uint8_t **pos,
+ bool strict)
+{
+ const uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
+
+ if (len < 6) {
+ return GIF_INSUFFICIENT_DATA;
+ }
+
+ if (strncmp((const char *) data, "GIF", 3) != 0) {
+ return GIF_DATA_ERROR;
+ }
+ data += 3;
+
+ if (strict == true) {
+ if ((strncmp((const char *) data, "87a", 3) != 0) &&
+ (strncmp((const char *) data, "89a", 3) != 0)) {
+ return GIF_DATA_ERROR;
+ }
+ }
+ data += 3;
+
+ *pos = data;
+ return GIF_OK;
+}
/* exported function documented in libnsgif.h */
gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
@@ -1137,23 +1178,10 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const
uint8_t *data)
gif->frame_count_partial = 0;
gif->decoded_frame = GIF_INVALID_FRAME;
- /* 6-byte GIF file header is:
- *
- * +0 3CHARS Signature ('GIF')
- * +3 3CHARS Version ('87a' or '89a')
- */
- if (strncmp((const char *) gif_data, "GIF", 3) != 0) {
- return GIF_DATA_ERROR;
+ ret = gif__parse_header(gif, &gif_data, false);
+ if (ret != GIF_OK) {
+ return ret;
}
- gif_data += 3;
-
- /* Ensure GIF reports version 87a or 89a */
- /*
- if ((strncmp(gif_data, "87a", 3) != 0) &&
- (strncmp(gif_data, "89a", 3) != 0))
- LOG(("Unknown GIF format - proceeding anyway"));
- */
- gif_data += 3;
/* 7-byte Logical Screen Descriptor is:
*
commitdiff
http://git.netsurf-browser.org/libnsgif.git/commit/?id=9f28b76c419889ef76...
commit 9f28b76c419889ef76832bcb65912acd42f9e685
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
Add myself to the copyright headers.
diff --git a/COPYING b/COPYING
index c6e1688..9334b89 100644
--- a/COPYING
+++ b/COPYING
@@ -1,5 +1,6 @@
Copyright (C) 2004 Richard Wilson
Copyright (C) 2008 Sean Fox
+Copyright (C) 2013-2021 Michael Drake
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/include/libnsgif.h b/include/libnsgif.h
index 3087a4d..5bd26b4 100644
--- a/include/libnsgif.h
+++ b/include/libnsgif.h
@@ -1,6 +1,7 @@
/*
* Copyright 2004 Richard Wilson <richard.wilson(a)netsurf-browser.org>
* Copyright 2008 Sean Fox <dyntryx(a)gmail.com>
+ * Copyright 2013-2021 Michael Drake <tlsa(a)netsurf-browser.org>
*
* This file is part of NetSurf's libnsgif,
http://www.netsurf-browser.org/
* Licenced under the MIT License,
diff --git a/src/libnsgif.c b/src/libnsgif.c
index a87c136..5cfa020 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -1,6 +1,7 @@
/*
* Copyright 2004 Richard Wilson <richard.wilson(a)netsurf-browser.org>
* Copyright 2008 Sean Fox <dyntryx(a)gmail.com>
+ * Copyright 2013-2021 Michael Drake <tlsa(a)netsurf-browser.org>
*
* This file is part of NetSurf's libnsgif,
http://www.netsurf-browser.org/
* Licenced under the MIT License,
diff --git a/src/lzw.h b/src/lzw.h
index 4549c60..4568298 100644
--- a/src/lzw.h
+++ b/src/lzw.h
@@ -4,6 +4,7 @@
*
http://www.opensource.org/licenses/mit-license.php
*
* Copyright 2017 Michael Drake <michael.drake(a)codethink.co.uk>
+ * Copyright 2021 Michael Drake <tlsa(a)netsurf-browser.org>
*/
#ifndef LZW_H_
commitdiff
http://git.netsurf-browser.org/libnsgif.git/commit/?id=b7952d1c53dffeced0...
commit b7952d1c53dffeced0feb9d75e66da972d68318a
Author: Michael Drake <tlsa(a)netsurf-browser.org>
Commit: Michael Drake <tlsa(a)netsurf-browser.org>
GIF: Initialisation: Remove misleading comment.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 6046769..a87c136 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -1293,7 +1293,6 @@ gif_result gif_initialise(gif_animation *gif, size_t size, const
uint8_t *data)
return GIF_INSUFFICIENT_FRAME_DATA;
}
- /* Return how many we got */
return ret;
}
-----------------------------------------------------------------------
Summary of changes:
COPYING | 1 +
include/libnsgif.h | 234 +++----
src/libnsgif.c | 1784 +++++++++++++++++++++++++++-------------------------
src/lzw.c | 5 -
src/lzw.h | 4 +-
test/decode_gif.c | 3 -
6 files changed, 1035 insertions(+), 996 deletions(-)
diff --git a/COPYING b/COPYING
index c6e1688..9334b89 100644
--- a/COPYING
+++ b/COPYING
@@ -1,5 +1,6 @@
Copyright (C) 2004 Richard Wilson
Copyright (C) 2008 Sean Fox
+Copyright (C) 2013-2021 Michael Drake
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/include/libnsgif.h b/include/libnsgif.h
index 50dc688..309e2f0 100644
--- a/include/libnsgif.h
+++ b/include/libnsgif.h
@@ -1,6 +1,7 @@
/*
* Copyright 2004 Richard Wilson <richard.wilson(a)netsurf-browser.org>
* Copyright 2008 Sean Fox <dyntryx(a)gmail.com>
+ * Copyright 2013-2021 Michael Drake <tlsa(a)netsurf-browser.org>
*
* This file is part of NetSurf's libnsgif,
http://www.netsurf-browser.org/
* Licenced under the MIT License,
@@ -15,53 +16,55 @@
#ifndef _LIBNSGIF_H_
#define _LIBNSGIF_H_
+#include <stdint.h>
#include <stdbool.h>
-#include <inttypes.h>
/* Error return values */
typedef enum {
- GIF_WORKING = 1,
- GIF_OK = 0,
- GIF_INSUFFICIENT_FRAME_DATA = -1,
- GIF_FRAME_DATA_ERROR = -2,
- GIF_INSUFFICIENT_DATA = -3,
- GIF_DATA_ERROR = -4,
- GIF_INSUFFICIENT_MEMORY = -5,
- GIF_FRAME_NO_DISPLAY = -6,
- GIF_END_OF_FRAME = -7
+ GIF_WORKING = 1,
+ GIF_OK = 0,
+ GIF_INSUFFICIENT_DATA = -1,
+ GIF_INSUFFICIENT_FRAME_DATA = GIF_INSUFFICIENT_DATA,
+ GIF_FRAME_DATA_ERROR = -2,
+ GIF_DATA_ERROR = -4,
+ GIF_INSUFFICIENT_MEMORY = -5,
+ GIF_FRAME_NO_DISPLAY = -6,
+ GIF_END_OF_FRAME = -7
} gif_result;
/** GIF frame data */
typedef struct gif_frame {
- /** whether the frame should be displayed/animated */
- bool display;
- /** delay (in cs) before animating the frame */
- unsigned int frame_delay;
-
- /* Internal members are listed below */
-
- /** offset (in bytes) to the GIF frame data */
- unsigned int frame_pointer;
- /** whether the frame has previously been used */
- bool virgin;
- /** whether the frame is totally opaque */
- bool opaque;
- /** whether a forcable screen redraw is required */
- bool redraw_required;
- /** how the previous frame should be disposed; affects plotting */
- unsigned char disposal_method;
- /** whether we acknoledge transparency */
- bool transparency;
- /** the index designating a transparent pixel */
- unsigned char transparency_index;
- /** x co-ordinate of redraw rectangle */
- unsigned int redraw_x;
- /** y co-ordinate of redraw rectangle */
- unsigned int redraw_y;
- /** width of redraw rectangle */
- unsigned int redraw_width;
- /** height of redraw rectangle */
- unsigned int redraw_height;
+ /** whether the frame should be displayed/animated */
+ bool display;
+ /** delay (in cs) before animating the frame */
+ unsigned int frame_delay;
+
+ /* Internal members are listed below */
+
+ /** offset (in bytes) to the GIF frame data */
+ unsigned int frame_pointer;
+ /** whether the frame has previously been used */
+ bool virgin;
+ /** 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 */
+ unsigned char disposal_method;
+ /** whether we acknowledge transparency */
+ bool transparency;
+ /** the index designating a transparent pixel */
+ unsigned int transparency_index;
+ /** x co-ordinate of redraw rectangle */
+ unsigned int redraw_x;
+ /** y co-ordinate of redraw rectangle */
+ unsigned int redraw_y;
+ /** width of redraw rectangle */
+ unsigned int redraw_width;
+ /** height of redraw rectangle */
+ unsigned int redraw_height;
+ /* Frame flags */
+ unsigned int flags;
} gif_frame;
/* API for Bitmap callbacks */
@@ -74,77 +77,81 @@ typedef void (*gif_bitmap_cb_modified)(void *bitmap);
/** Bitmap callbacks function table */
typedef struct gif_bitmap_callback_vt {
- /** Create a bitmap. */
- gif_bitmap_cb_create bitmap_create;
- /** Free a bitmap. */
- gif_bitmap_cb_destroy bitmap_destroy;
- /** Return a pointer to the pixel data in a bitmap. */
- gif_bitmap_cb_get_buffer bitmap_get_buffer;
-
- /* Members below are optional */
-
- /** Sets whether a bitmap should be plotted opaque. */
- gif_bitmap_cb_set_opaque bitmap_set_opaque;
- /** Tests whether a bitmap has an opaque alpha channel. */
- gif_bitmap_cb_test_opaque bitmap_test_opaque;
- /** The bitmap image has changed, so flush any persistant cache. */
- gif_bitmap_cb_modified bitmap_modified;
+ /** Create a bitmap. */
+ gif_bitmap_cb_create bitmap_create;
+ /** Free a bitmap. */
+ gif_bitmap_cb_destroy bitmap_destroy;
+ /** Return a pointer to the pixel data in a bitmap. */
+ gif_bitmap_cb_get_buffer bitmap_get_buffer;
+
+ /* Members below are optional */
+
+ /** Sets whether a bitmap should be plotted opaque. */
+ gif_bitmap_cb_set_opaque bitmap_set_opaque;
+ /** Tests whether a bitmap has an opaque alpha channel. */
+ gif_bitmap_cb_test_opaque bitmap_test_opaque;
+ /** The bitmap image has changed, so flush any persistent cache. */
+ gif_bitmap_cb_modified bitmap_modified;
} gif_bitmap_callback_vt;
/** GIF animation data */
typedef struct gif_animation {
- /** LZW decode context */
- void *lzw_ctx;
- /** callbacks for bitmap functions */
- gif_bitmap_callback_vt bitmap_callbacks;
- /** pointer to GIF data */
- unsigned char *gif_data;
- /** width of GIF (may increase during decoding) */
- unsigned int width;
- /** heigth of GIF (may increase during decoding) */
- unsigned int height;
- /** number of frames decoded */
- unsigned int frame_count;
- /** number of frames partially decoded */
- unsigned int frame_count_partial;
- /** decoded frames */
- gif_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 */
- unsigned int buffer_position;
- /** total number of bytes of GIF data available */
- unsigned int buffer_size;
- /** current number of frame holders */
- unsigned int frame_holders;
- /** index in the colour table for the background colour */
- unsigned int background_index;
- /** image aspect ratio (ignored) */
- unsigned int aspect_ratio;
- /** size of colour table (in entries) */
- unsigned int colour_table_size;
- /** whether the GIF has a global colour table */
- bool global_colours;
- /** global colour table */
- unsigned int *global_colour_table;
- /** local colour table */
- unsigned int *local_colour_table;
-
- /** previous frame for GIF_FRAME_RESTORE */
- void *prev_frame;
- /** previous frame index */
- int prev_index;
- /** previous frame width */
- unsigned prev_width;
- /** previous frame height */
- unsigned prev_height;
+ /** LZW decode context */
+ void *lzw_ctx;
+ /** callbacks for bitmap functions */
+ gif_bitmap_callback_vt bitmap_callbacks;
+ /** pointer to GIF data */
+ const uint8_t *gif_data;
+ /** width of GIF (may increase during decoding) */
+ unsigned int width;
+ /** height of GIF (may increase during decoding) */
+ unsigned int height;
+ /** number of frames decoded */
+ unsigned int frame_count;
+ /** number of frames partially decoded */
+ unsigned int frame_count_partial;
+ /** decoded frames */
+ gif_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 */
+ unsigned int buffer_position;
+ /** total number of bytes of GIF data available */
+ unsigned int buffer_size;
+ /** current number of frame holders */
+ unsigned int frame_holders;
+ /** background index */
+ unsigned int bg_index;
+ /** background colour */
+ unsigned int bg_colour;
+ /** image aspect ratio (ignored) */
+ unsigned int aspect_ratio;
+ /** size of colour table (in entries) */
+ unsigned int colour_table_size;
+ /** whether the GIF has a global colour table */
+ bool global_colours;
+ /** global colour table */
+ unsigned int *global_colour_table;
+ /** local colour table */
+ unsigned int *local_colour_table;
+ /** current colour table */
+ unsigned int *colour_table;
+
+ /** previous frame for GIF_FRAME_RESTORE */
+ void *prev_frame;
+ /** previous frame index */
+ int prev_index;
+ /** previous frame width */
+ unsigned prev_width;
+ /** previous frame height */
+ unsigned prev_height;
} gif_animation;
/**
@@ -157,28 +164,23 @@ void gif_create(gif_animation *gif, gif_bitmap_callback_vt
*bitmap_callbacks);
* any information that hasn't already been decoded.
* If an error occurs, all previously decoded frames are retained.
*
- * @return Error return value.
+ * \return Error return value.
* - GIF_FRAME_DATA_ERROR for GIF frame data error
- * - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to process
- * any more frames
+ * - GIF_INSUFFICIENT_DATA reached unexpected end of source data
* - GIF_INSUFFICIENT_MEMORY for memory error
* - GIF_DATA_ERROR for GIF error
- * - GIF_INSUFFICIENT_DATA for insufficient data to do anything
* - GIF_OK for successful decoding
* - GIF_WORKING for successful decoding if more frames are expected
*/
-gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data);
+gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data);
/**
* Decodes a GIF frame.
*
- * @return Error return value. If a frame does not contain any image data,
- * GIF_OK is returned and gif->current_error is set to
- * GIF_FRAME_NO_DISPLAY
+ * \return Error return value.
* - GIF_FRAME_DATA_ERROR for GIF frame data error
- * - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
* - GIF_DATA_ERROR for GIF error (invalid frame header)
- * - GIF_INSUFFICIENT_DATA for insufficient data to do anything
+ * - GIF_INSUFFICIENT_DATA reached unexpected end of source data
* - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
* - GIF_OK for successful decoding
*/
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 16bb78a..6c21a7c 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -1,6 +1,7 @@
/*
* Copyright 2004 Richard Wilson <richard.wilson(a)netsurf-browser.org>
* Copyright 2008 Sean Fox <dyntryx(a)gmail.com>
+ * Copyright 2013-2021 Michael Drake <tlsa(a)netsurf-browser.org>
*
* This file is part of NetSurf's libnsgif,
http://www.netsurf-browser.org/
* Licenced under the MIT License,
@@ -29,7 +30,6 @@
* \todo Plain text and comment extensions should be implemented.
*/
-
/** Maximum colour table size */
#define GIF_MAX_COLOURS 256
@@ -45,13 +45,15 @@
/** No transparency */
#define GIF_NO_TRANSPARENCY (0xFFFFFFFFu)
-/* GIF Flags */
-#define GIF_FRAME_COMBINE 1
-#define GIF_FRAME_CLEAR 2
-#define GIF_FRAME_RESTORE 3
-#define GIF_FRAME_QUIRKS_RESTORE 4
+enum gif_disposal {
+ GIF_DISPOSAL_UNSPECIFIED,
+ GIF_DISPOSAL_NONE,
+ GIF_DISPOSAL_RESTORE_BG,
+ GIF_DISPOSAL_RESTORE_PREV,
+ GIF_DISPOSAL_RESTORE_QUIRK, /**< Alias for GIF_DISPOSAL_RESTORE_PREV. */
+};
-#define GIF_IMAGE_SEPARATOR 0x2c
+/* GIF Flags */
#define GIF_INTERLACE_MASK 0x40
#define GIF_COLOUR_TABLE_MASK 0x80
#define GIF_COLOUR_TABLE_SIZE_MASK 0x07
@@ -65,9 +67,21 @@
#define GIF_BLOCK_TERMINATOR 0x00
#define GIF_TRAILER 0x3b
-/** standard GIF header size */
-#define GIF_STANDARD_HEADER_SIZE 13
-
+static gif_result gif_error_from_lzw(lzw_result l_res)
+{
+ static const gif_result g_res[] = {
+ [LZW_OK] = GIF_OK,
+ [LZW_OK_EOD] = GIF_END_OF_FRAME,
+ [LZW_NO_MEM] = GIF_INSUFFICIENT_MEMORY,
+ [LZW_NO_DATA] = GIF_INSUFFICIENT_DATA,
+ [LZW_EOI_CODE] = GIF_FRAME_DATA_ERROR,
+ [LZW_BAD_ICODE] = GIF_FRAME_DATA_ERROR,
+ [LZW_BAD_CODE] = GIF_FRAME_DATA_ERROR,
+ };
+ assert(l_res != LZW_BAD_PARAM);
+ assert(l_res != LZW_NO_COLOUR);
+ return g_res[l_res];
+}
/**
* Updates the sprite memory size
@@ -77,10 +91,10 @@
* \param height The height of the sprite
* \return GIF_INSUFFICIENT_MEMORY for a memory error GIF_OK for success
*/
-static gif_result
-gif_initialise_sprite(gif_animation *gif,
- unsigned int width,
- unsigned int height)
+static gif_result gif_initialise_sprite(
+ struct gif_animation *gif,
+ uint32_t width,
+ uint32_t height)
{
/* Already allocated? */
if (gif->frame_image) {
@@ -96,473 +110,82 @@ gif_initialise_sprite(gif_animation *gif,
return GIF_OK;
}
-
/**
- * Attempts to initialise the frame's extensions
+ * Helper to get the rendering bitmap for a gif.
*
- * \param gif The animation context
- * \param frame The frame number
- * @return GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the
- * frame GIF_OK for successful initialisation.
+ * \param[in] gif The gif object we're decoding.
+ * \return Client pixel buffer for rendering into.
*/
-static gif_result
-gif_initialise_frame_extensions(gif_animation *gif, const int frame)
+static inline uint32_t* gif__bitmap_get(
+ struct gif_animation *gif)
{
- unsigned char *gif_data, *gif_end;
- int gif_bytes;
- unsigned int block_size;
-
- /* Get our buffer position etc. */
- gif_data = (unsigned char *)(gif->gif_data + gif->buffer_position);
- gif_end = (unsigned char *)(gif->gif_data + gif->buffer_size);
-
- /* Initialise the extensions */
- while (gif_data < gif_end && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
- ++gif_data;
- if ((gif_bytes = (gif_end - gif_data)) < 1) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
-
- /* Switch on extension label */
- switch (gif_data[0]) {
- case GIF_EXTENSION_GRAPHIC_CONTROL:
- /* 6-byte Graphic Control Extension is:
- *
- * +0 CHAR Graphic Control Label
- * +1 CHAR Block Size
- * +2 CHAR __Packed Fields__
- * 3BITS Reserved
- * 3BITS Disposal Method
- * 1BIT User Input Flag
- * 1BIT Transparent Color Flag
- * +3 SHORT Delay Time
- * +5 CHAR Transparent Color Index
- */
- if (gif_bytes < 6) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
-
- gif->frames[frame].frame_delay = gif_data[3] | (gif_data[4] << 8);
- if (gif_data[2] & GIF_TRANSPARENCY_MASK) {
- gif->frames[frame].transparency = true;
- gif->frames[frame].transparency_index = gif_data[5];
- }
- gif->frames[frame].disposal_method = ((gif_data[2] & GIF_DISPOSAL_MASK)
>> 2);
- /* I have encountered documentation and GIFs in the
- * wild that use 0x04 to restore the previous frame,
- * rather than the officially documented 0x03. I
- * believe some (older?) software may even actually
- * export this way. We handle this as a type of
- * "quirks" mode.
- */
- if (gif->frames[frame].disposal_method == GIF_FRAME_QUIRKS_RESTORE) {
- gif->frames[frame].disposal_method = GIF_FRAME_RESTORE;
- }
- gif_data += (2 + gif_data[1]);
- break;
-
- case GIF_EXTENSION_APPLICATION:
- /* 14-byte+ Application Extension is:
- *
- * +0 CHAR Application Extension Label
- * +1 CHAR Block Size
- * +2 8CHARS Application Identifier
- * +10 3CHARS Appl. Authentication Code
- * +13 1-256 Application Data (Data sub-blocks)
- */
- if (gif_bytes < 17) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- if ((gif_data[1] == 0x0b) &&
- (strncmp((const char *) gif_data + 2,
- "NETSCAPE2.0", 11) == 0) &&
- (gif_data[13] == 0x03) &&
- (gif_data[14] == 0x01)) {
- gif->loop_count = gif_data[15] | (gif_data[16] << 8);
- }
- gif_data += (2 + gif_data[1]);
- break;
-
- case GIF_EXTENSION_COMMENT:
- /* Move the pointer to the first data sub-block Skip 1
- * byte for the extension label
- */
- ++gif_data;
- break;
-
- default:
- /* Move the pointer to the first data sub-block Skip 2
- * bytes for the extension label and size fields Skip
- * the extension size itself
- */
- if (gif_bytes < 2) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- gif_data += (2 + gif_data[1]);
- }
+ gif_result ret;
- /* Repeatedly skip blocks until we get a zero block or run out
- * of data This data is ignored by this gif decoder
- */
- gif_bytes = (gif_end - gif_data);
- block_size = 0;
- while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) {
- block_size = gif_data[0] + 1;
- if ((gif_bytes -= block_size) < 0) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- gif_data += block_size;
- }
- ++gif_data;
+ /* Make sure we have a buffer to decode to. */
+ ret = gif_initialise_sprite(gif, gif->width, gif->height);
+ if (ret != GIF_OK) {
+ return NULL;
}
- /* Set buffer position and return */
- gif->buffer_position = (gif_data - gif->gif_data);
- return GIF_OK;
+ /* Get the frame data */
+ assert(gif->bitmap_callbacks.bitmap_get_buffer);
+ return (uint32_t *)gif->bitmap_callbacks.bitmap_get_buffer(
+ gif->frame_image);
}
-
/**
- * Attempts to initialise the next frame
+ * Helper to tell the client that their bitmap was modified.
*
- * \param gif The animation context
- * \return error code
- * - GIF_INSUFFICIENT_DATA for insufficient data to do anything
- * - GIF_FRAME_DATA_ERROR for GIF frame data error
- * - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
- * - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
- * - GIF_DATA_ERROR for GIF error (invalid frame header)
- * - GIF_OK for successful decoding
- * - GIF_WORKING for successful decoding if more frames are expected
-*/
-static gif_result gif_initialise_frame(gif_animation *gif)
+ * \param[in] gif The gif object we're decoding.
+ */
+static inline void gif__bitmap_modified(
+ const struct gif_animation *gif)
{
- int frame;
- gif_frame *temp_buf;
-
- unsigned char *gif_data, *gif_end;
- int gif_bytes;
- unsigned int flags = 0;
- unsigned int width, height, offset_x, offset_y;
- unsigned int block_size, colour_table_size;
- bool first_image = true;
- gif_result return_value;
-
- /* Get the frame to decode and our data position */
- frame = gif->frame_count;
-
- /* Get our buffer position etc. */
- gif_data = (unsigned char *)(gif->gif_data + gif->buffer_position);
- gif_end = (unsigned char *)(gif->gif_data + gif->buffer_size);
- gif_bytes = (gif_end - gif_data);
-
- /* Check if we've finished */
- if ((gif_bytes > 0) && (gif_data[0] == GIF_TRAILER)) {
- return GIF_OK;
- }
-
- /* Check if there is enough data remaining. The shortest block of data
- * is a 4-byte comment extension + 1-byte block terminator + 1-byte gif
- * trailer
- */
- if (gif_bytes < 6) {
- return GIF_INSUFFICIENT_DATA;
- }
-
- /* 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 > 4096) {
- return GIF_FRAME_DATA_ERROR;
- }
-
- /* Get some memory to store our pointers in etc. */
- if ((int)gif->frame_holders <= frame) {
- /* Allocate more memory */
- temp_buf = (gif_frame *)realloc(gif->frames, (frame + 1) * sizeof(gif_frame));
- if (temp_buf == NULL) {
- return GIF_INSUFFICIENT_MEMORY;
- }
- gif->frames = temp_buf;
- gif->frame_holders = frame + 1;
- }
-
- /* Store our frame pointer. We would do it when allocating except we
- * start off with one frame allocated so we can always use realloc.
- */
- gif->frames[frame].frame_pointer = gif->buffer_position;
- gif->frames[frame].display = false;
- gif->frames[frame].virgin = true;
- gif->frames[frame].disposal_method = 0;
- gif->frames[frame].transparency = false;
- gif->frames[frame].frame_delay = 100;
- gif->frames[frame].redraw_required = false;
-
- /* Invalidate any previous decoding we have of this frame */
- if (gif->decoded_frame == frame) {
- gif->decoded_frame = GIF_INVALID_FRAME;
- }
-
- /* We pretend to initialise the frames, but really we just skip over
- * all the data contained within. This is all basically a cut down
- * version of gif_decode_frame that doesn't have any of the LZW bits in
- * it.
- */
-
- /* Initialise any extensions */
- gif->buffer_position = gif_data - gif->gif_data;
- return_value = gif_initialise_frame_extensions(gif, frame);
- if (return_value != GIF_OK) {
- return return_value;
- }
- gif_data = (gif->gif_data + gif->buffer_position);
- gif_bytes = (gif_end - gif_data);
-
- /* Check if we've finished */
- if ((gif_bytes = (gif_end - gif_data)) < 1) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
-
- if (gif_data[0] == GIF_TRAILER) {
- gif->buffer_position = (gif_data - gif->gif_data);
- gif->frame_count = frame + 1;
- return GIF_OK;
- }
-
- /* If we're not done, there should be an image descriptor */
- if (gif_data[0] != GIF_IMAGE_SEPARATOR) {
- return GIF_FRAME_DATA_ERROR;
- }
-
- /* Do some simple boundary checking */
- if (gif_bytes < 10) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- offset_x = gif_data[1] | (gif_data[2] << 8);
- offset_y = gif_data[3] | (gif_data[4] << 8);
- width = gif_data[5] | (gif_data[6] << 8);
- height = gif_data[7] | (gif_data[8] << 8);
-
- /* Set up the redraw characteristics. We have to check for extending
- * the area due to multi-image frames.
- */
- if (!first_image) {
- if (gif->frames[frame].redraw_x > offset_x) {
- gif->frames[frame].redraw_width += (gif->frames[frame].redraw_x - offset_x);
- gif->frames[frame].redraw_x = offset_x;
- }
-
- if (gif->frames[frame].redraw_y > offset_y) {
- gif->frames[frame].redraw_height += (gif->frames[frame].redraw_y - offset_y);
- gif->frames[frame].redraw_y = offset_y;
- }
-
- if ((offset_x + width) > (gif->frames[frame].redraw_x +
gif->frames[frame].redraw_width)) {
- gif->frames[frame].redraw_width = (offset_x + width) -
gif->frames[frame].redraw_x;
- }
-
- if ((offset_y + height) > (gif->frames[frame].redraw_y +
gif->frames[frame].redraw_height)) {
- gif->frames[frame].redraw_height = (offset_y + height) -
gif->frames[frame].redraw_y;
- }
- } else {
- first_image = false;
- gif->frames[frame].redraw_x = offset_x;
- gif->frames[frame].redraw_y = offset_y;
- gif->frames[frame].redraw_width = width;
- gif->frames[frame].redraw_height = height;
- }
-
- /* if we are clearing the background then we need to redraw enough to
- * cover the previous frame too
- */
- gif->frames[frame].redraw_required =
- ((gif->frames[frame].disposal_method == GIF_FRAME_CLEAR) ||
- (gif->frames[frame].disposal_method == GIF_FRAME_RESTORE));
-
- /* Frame size may have grown.
- */
- gif->width = (offset_x + width > gif->width) ?
- offset_x + width : gif->width;
- gif->height = (offset_y + height > gif->height) ?
- offset_y + height : gif->height;
-
- /* Decode the flags */
- flags = gif_data[9];
- colour_table_size = 2 << (flags & GIF_COLOUR_TABLE_SIZE_MASK);
-
- /* Move our data onwards and remember we've got a bit of this frame */
- gif_data += 10;
- gif_bytes = (gif_end - gif_data);
- gif->frame_count_partial = frame + 1;
-
- /* Skip the local colour table */
- if (flags & GIF_COLOUR_TABLE_MASK) {
- gif_data += 3 * colour_table_size;
- if ((gif_bytes = (gif_end - gif_data)) < 0) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- }
-
- /* Ensure we have a correct code size */
- if (gif_bytes < 1) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- if (gif_data[0] >= LZW_CODE_MAX) {
- return GIF_DATA_ERROR;
- }
-
- /* Move our pointer to the actual image data */
- gif_data++;
- --gif_bytes;
-
- /* Repeatedly skip blocks until we get a zero block or run out of data
- * These blocks of image data are processed later by gif_decode_frame()
- */
- block_size = 0;
- while (block_size != 1) {
- if (gif_bytes < 1) return GIF_INSUFFICIENT_FRAME_DATA;
- block_size = gif_data[0] + 1;
- /* Check if the frame data runs off the end of the file */
- if ((int)(gif_bytes - block_size) < 0) {
- /* Try to recover by signaling the end of the gif.
- * Once we get garbage data, there is no logical way to
- * determine where the next frame is. It's probably
- * better to partially load the gif than not at all.
- */
- if (gif_bytes >= 2) {
- gif_data[0] = 0;
- gif_data[1] = GIF_TRAILER;
- gif_bytes = 1;
- ++gif_data;
- break;
- } else {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- } else {
- gif_bytes -= block_size;
- gif_data += block_size;
- }
- }
-
- /* Add the frame and set the display flag */
- gif->buffer_position = gif_data - gif->gif_data;
- gif->frame_count = frame + 1;
- gif->frames[frame].display = true;
-
- /* Check if we've finished */
- if (gif_bytes < 1) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- } else {
- if (gif_data[0] == GIF_TRAILER) {
- return GIF_OK;
- }
+ if (gif->bitmap_callbacks.bitmap_modified) {
+ gif->bitmap_callbacks.bitmap_modified(gif->frame_image);
}
- return GIF_WORKING;
}
-
/**
- * Skips the frame's extensions (which have been previously initialised)
+ * Helper to tell the client that whether the bitmap is opaque.
*
- * \param gif The animation context
- * \return GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the
- * frame GIF_OK for successful decoding
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frmae The frame that has been decoded.
*/
-static gif_result gif_skip_frame_extensions(gif_animation *gif)
+static inline void gif__bitmap_set_opaque(
+ const struct gif_animation *gif,
+ const struct gif_frame *frame)
{
- unsigned char *gif_data, *gif_end;
- int gif_bytes;
- unsigned int block_size;
-
- /* Get our buffer position etc. */
- gif_data = (unsigned char *)(gif->gif_data + gif->buffer_position);
- gif_end = (unsigned char *)(gif->gif_data + gif->buffer_size);
- gif_bytes = (gif_end - gif_data);
-
- /* Skip the extensions */
- while (gif_data < gif_end && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
- ++gif_data;
- if (gif_data >= gif_end) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
-
- /* Switch on extension label */
- switch(gif_data[0]) {
- case GIF_EXTENSION_COMMENT:
- /* Move the pointer to the first data sub-block
- * 1 byte for the extension label
- */
- ++gif_data;
- break;
-
- default:
- /* Move the pointer to the first data sub-block 2 bytes
- * for the extension label and size fields Skip the
- * extension size itself
- */
- if (gif_data + 1 >= gif_end) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- gif_data += (2 + gif_data[1]);
- }
-
- /* Repeatedly skip blocks until we get a zero block or run out
- * of data This data is ignored by this gif decoder
- */
- gif_bytes = (gif_end - gif_data);
- block_size = 0;
- while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) {
- block_size = gif_data[0] + 1;
- if ((gif_bytes -= block_size) < 0) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- gif_data += block_size;
- }
- ++gif_data;
+ if (gif->bitmap_callbacks.bitmap_set_opaque) {
+ gif->bitmap_callbacks.bitmap_set_opaque(
+ gif->frame_image, frame->opaque);
}
-
- /* Set buffer position and return */
- gif->buffer_position = (gif_data - gif->gif_data);
- return GIF_OK;
}
-static unsigned int gif_interlaced_line(int height, int y) {
- if ((y << 3) < height) {
- return (y << 3);
- }
- y -= ((height + 7) >> 3);
- if ((y << 3) < (height - 4)) {
- return (y << 3) + 4;
- }
- y -= ((height + 3) >> 3);
- if ((y << 2) < (height - 2)) {
- return (y << 2) + 2;
+/**
+ * Helper to get the client to determine if the bitmap is opaque.
+ *
+ * \todo: We don't really need to get the client to do this for us.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \return true if the bitmap is opaque, false otherwise.
+ */
+static inline bool gif__bitmap_get_opaque(
+ const struct gif_animation *gif)
+{
+ if (gif->bitmap_callbacks.bitmap_test_opaque) {
+ return gif->bitmap_callbacks.bitmap_test_opaque(
+ gif->frame_image);
}
- y -= ((height + 1) >> 2);
- return (y << 1) + 1;
-}
-
-static gif_result gif_error_from_lzw(lzw_result l_res)
-{
- static const gif_result g_res[] = {
- [LZW_OK] = GIF_OK,
- [LZW_OK_EOD] = GIF_END_OF_FRAME,
- [LZW_NO_MEM] = GIF_INSUFFICIENT_MEMORY,
- [LZW_NO_DATA] = GIF_INSUFFICIENT_FRAME_DATA,
- [LZW_EOI_CODE] = GIF_FRAME_DATA_ERROR,
- [LZW_BAD_ICODE] = GIF_FRAME_DATA_ERROR,
- [LZW_BAD_CODE] = GIF_FRAME_DATA_ERROR,
- };
- assert(l_res != LZW_BAD_PARAM);
- assert(l_res != LZW_NO_COLOUR);
- return g_res[l_res];
+ return false;
}
-static void gif__record_previous_frame(gif_animation *gif)
+static void gif__record_frame(
+ struct gif_animation *gif,
+ const uint32_t *bitmap)
{
bool need_alloc = gif->prev_frame == NULL;
- const uint32_t *frame_data;
uint32_t *prev_frame;
if (gif->decoded_frame == GIF_INVALID_FRAME ||
@@ -571,9 +194,8 @@ static void gif__record_previous_frame(gif_animation *gif)
return;
}
- assert(gif->bitmap_callbacks.bitmap_get_buffer);
- frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
- if (!frame_data) {
+ bitmap = gif__bitmap_get(gif);
+ if (bitmap == NULL) {
return;
}
@@ -592,7 +214,7 @@ static void gif__record_previous_frame(gif_animation *gif)
prev_frame = gif->prev_frame;
}
- memcpy(prev_frame, frame_data, gif->width * gif->height * 4);
+ memcpy(prev_frame, bitmap, gif->width * gif->height * 4);
gif->prev_frame = prev_frame;
gif->prev_width = gif->width;
@@ -600,72 +222,116 @@ static void gif__record_previous_frame(gif_animation *gif)
gif->prev_index = gif->decoded_frame;
}
-static gif_result gif__recover_previous_frame(const gif_animation *gif)
+static gif_result gif__recover_frame(
+ const struct gif_animation *gif,
+ 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;
- uint32_t *frame_data;
if (prev_frame == NULL) {
return GIF_FRAME_DATA_ERROR;
}
- assert(gif->bitmap_callbacks.bitmap_get_buffer);
- frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
- if (!frame_data) {
- return GIF_INSUFFICIENT_MEMORY;
- }
-
for (unsigned y = 0; y < height; y++) {
- memcpy(frame_data, prev_frame, width * 4);
+ memcpy(bitmap, prev_frame, width * 4);
- frame_data += gif->width;
+ bitmap += gif->width;
prev_frame += gif->prev_width;
}
return GIF_OK;
}
-static gif_result
-gif__decode_complex(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)
+/**
+ * Get the next line for GIF decode.
+ *
+ * Note that the step size must be initialised to 24 at the start of the frame
+ * (when y == 0). This is because of the first two passes of the frame have
+ * the same step size of 8, and the step size is used to determine the current
+ * pass.
+ *
+ * \param[in] height Frame height in pixels.
+ * \param[in,out] y Current row, starting from 0, updated on exit.
+ * \param[in,out] step Current step starting with 24, updated on exit.
+ * \return true if there is a row to process, false at the end of the frame.
+ */
+static inline bool gif__deinterlace(uint32_t height, uint32_t *y, uint8_t *step)
+{
+ *y += *step & 0xf;
+
+ if (*y < height) return true;
+
+ switch (*step) {
+ case 24: *y = 4; *step = 8; if (*y < height) return true;
+ /* Fall through. */
+ case 8: *y = 2; *step = 4; if (*y < height) return true;
+ /* Fall through. */
+ case 4: *y = 1; *step = 2; if (*y < height) return true;
+ /* Fall through. */
+ default:
+ break;
+ }
+
+ return false;
+}
+
+/**
+ * Get the next line for GIF decode.
+ *
+ * \param[in] interlace Non-zero if the frame is not interlaced.
+ * \param[in] height Frame height in pixels.
+ * \param[in,out] y Current row, starting from 0, updated on exit.
+ * \param[in,out] step Current step starting with 24, updated on exit.
+ * \return true if there is a row to process, false at the end of the frame.
+ */
+static inline bool gif__next_row(uint32_t interlace,
+ uint32_t height, uint32_t *y, uint8_t *step)
+{
+ if (!interlace) {
+ return (++*y != height);
+ } else {
+ return gif__deinterlace(height, y, step);
+ }
+}
+
+static gif_result gif__decode_complex(
+ struct gif_animation *gif,
+ uint32_t width,
+ uint32_t height,
+ uint32_t offset_x,
+ uint32_t offset_y,
+ uint32_t interlace,
+ const uint8_t *data,
+ uint32_t transparency_index,
+ uint32_t *restrict frame_data,
+ uint32_t *restrict colour_table)
{
- unsigned int transparency_index;
- uint32_t available = 0;
- gif_result ret = GIF_OK;
lzw_result res;
+ gif_result ret = GIF_OK;
+ uint32_t available = 0;
+ uint8_t step = 24;
+ uint32_t y = 0;
+
+ if (height == 0) {
+ return GIF_OK;
+ }
/* Initialise the LZW decoding */
- res = lzw_decode_init(gif->lzw_ctx, minimum_code_size,
- gif->gif_data, gif->buffer_size, gif->buffer_position);
+ res = lzw_decode_init(gif->lzw_ctx, data[0],
+ gif->gif_data, gif->buffer_size,
+ data + 1 - gif->gif_data);
if (res != LZW_OK) {
return gif_error_from_lzw(res);
}
- transparency_index = gif->frames[frame].transparency ?
- gif->frames[frame].transparency_index :
- GIF_NO_TRANSPARENCY;
+ do {
+ uint32_t x;
+ uint32_t *frame_scanline;
- 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);
+ frame_scanline = frame_data + offset_x +
+ (y + offset_y) * gif->width;
x = width;
while (x > 0) {
@@ -695,7 +361,7 @@ gif__decode_complex(gif_animation *gif,
}
} else {
while (row_available-- > 0) {
- register unsigned int colour;
+ register uint32_t colour;
colour = *uncompressed++;
if (colour != transparency_index) {
*frame_scanline =
@@ -705,33 +371,30 @@ gif__decode_complex(gif_animation *gif,
}
}
}
- }
+ } while (gif__next_row(interlace, height, &y, &step));
+
return ret;
}
-static gif_result
-gif__decode_simple(gif_animation *gif,
- unsigned int frame,
- unsigned int height,
- unsigned int offset_y,
- uint8_t minimum_code_size,
- unsigned int *restrict frame_data,
- unsigned int *restrict colour_table)
+static gif_result gif__decode_simple(
+ struct gif_animation *gif,
+ uint32_t height,
+ uint32_t offset_y,
+ const uint8_t *data,
+ uint32_t transparency_index,
+ uint32_t *restrict frame_data,
+ uint32_t *restrict colour_table)
{
- unsigned int transparency_index;
uint32_t pixels = gif->width * height;
uint32_t written = 0;
gif_result ret = GIF_OK;
lzw_result res;
- transparency_index = gif->frames[frame].transparency ?
- gif->frames[frame].transparency_index :
- GIF_NO_TRANSPARENCY;
-
/* Initialise the LZW decoding */
- res = lzw_decode_init_map(gif->lzw_ctx,
- minimum_code_size, transparency_index, colour_table,
- gif->gif_data, gif->buffer_size, gif->buffer_position);
+ res = lzw_decode_init_map(gif->lzw_ctx, data[0],
+ transparency_index, colour_table,
+ gif->gif_data, gif->buffer_size,
+ data + 1 - gif->gif_data);
if (res != LZW_OK) {
return gif_error_from_lzw(res);
}
@@ -761,320 +424,685 @@ gif__decode_simple(gif_animation *gif,
return ret;
}
-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)
+static inline gif_result gif__decode(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ const uint8_t *data,
+ uint32_t *restrict frame_data)
{
gif_result ret;
+ uint32_t offset_x = frame->redraw_x;
+ uint32_t offset_y = frame->redraw_y;
+ uint32_t width = frame->redraw_width;
+ uint32_t height = frame->redraw_height;
+ uint32_t interlace = frame->flags & GIF_INTERLACE_MASK;
+ uint32_t transparency_index = frame->transparency_index;
+ uint32_t *restrict colour_table = gif->colour_table;
if (interlace == false && width == gif->width && offset_x == 0) {
- ret = gif__decode_simple(gif, frame, height, offset_y,
- minimum_code_size, frame_data, colour_table);
+ ret = gif__decode_simple(gif, height, offset_y,
+ data, transparency_index,
+ frame_data, colour_table);
} else {
- ret = gif__decode_complex(gif, frame, width, height,
+ ret = gif__decode_complex(gif, width, height,
offset_x, offset_y, interlace,
- minimum_code_size, frame_data, colour_table);
+ data, transparency_index,
+ frame_data, colour_table);
}
return ret;
}
/**
- * decode a gif frame
+ * Restore a GIF to the background colour.
*
- * \param gif gif animation context.
- * \param frame The frame number to decode.
- * \param clear_image flag for image data being cleared instead of plotted.
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to clear, or NULL.
+ * \param[in] bitmap The bitmap to clear the frame in.
*/
-static gif_result
-gif_internal_decode_frame(gif_animation *gif,
- unsigned int frame,
- bool clear_image)
+static void gif__restore_bg(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ uint32_t *bitmap)
{
- gif_result err;
- unsigned int index = 0;
- unsigned char *gif_data, *gif_end;
- int gif_bytes;
- unsigned int width, height, offset_x, offset_y;
- unsigned int flags, colour_table_size, interlace;
- unsigned int *colour_table;
- unsigned int *frame_data = 0; // Set to 0 for no warnings
- unsigned int save_buffer_position;
- unsigned int return_value = 0;
-
- /* Ensure this frame is supposed to be decoded */
- if (gif->frames[frame].display == false) {
- return GIF_OK;
- }
+ if (frame == NULL) {
+ memset(bitmap, GIF_TRANSPARENT_COLOUR,
+ gif->width * gif->height * sizeof(*bitmap));
+ } else {
+ uint32_t offset_x = frame->redraw_x;
+ uint32_t offset_y = frame->redraw_y;
+ uint32_t width = frame->redraw_width;
+ uint32_t height = frame->redraw_height;
- /* Ensure the frame is in range to decode */
- if (frame > gif->frame_count_partial) {
- return GIF_INSUFFICIENT_DATA;
- }
+ if (frame->display == false) {
+ return;
+ }
- /* done if frame is already decoded */
- if ((!clear_image) &&
- ((int)frame == gif->decoded_frame)) {
- return GIF_OK;
+ if (frame->transparency) {
+ for (uint32_t y = 0; y < height; y++) {
+ uint32_t *scanline = bitmap + offset_x +
+ (offset_y + y) * gif->width;
+ memset(scanline, GIF_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;
+ for (uint32_t x = 0; x < width; x++) {
+ scanline[x] = gif->bg_colour;
+ }
+ }
+ }
}
+}
- /* Get the start of our frame data and the end of the GIF data */
- gif_data = gif->gif_data + gif->frames[frame].frame_pointer;
- gif_end = gif->gif_data + gif->buffer_size;
- gif_bytes = (gif_end - gif_data);
+static gif_result gif__update_bitmap(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ const uint8_t *data,
+ uint32_t frame_idx)
+{
+ gif_result ret;
+ uint32_t *bitmap;
- /*
- * Ensure there is a minimal amount of data to proceed. The shortest
- * block of data is a 10-byte image descriptor + 1-byte gif trailer
- */
- if (gif_bytes < 12) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ gif->decoded_frame = frame_idx;
+
+ bitmap = gif__bitmap_get(gif);
+ if (bitmap == NULL) {
+ return GIF_INSUFFICIENT_MEMORY;
}
- /* Save the buffer position */
- save_buffer_position = gif->buffer_position;
- gif->buffer_position = gif_data - gif->gif_data;
+ /* Handle any bitmap clearing/restoration required before decoding this
+ * frame. */
+ if (frame_idx == 0 || gif->decoded_frame == GIF_INVALID_FRAME) {
+ gif__restore_bg(gif, NULL, bitmap);
+
+ } else {
+ struct gif_frame *prev = &gif->frames[frame_idx - 1];
+
+ if (prev->disposal_method == GIF_DISPOSAL_RESTORE_BG) {
+ gif__restore_bg(gif, prev, bitmap);
- /* Skip any extensions because they have already been processed */
- if ((return_value = gif_skip_frame_extensions(gif)) != GIF_OK) {
- goto gif_decode_frame_exit;
+ } else if (prev->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
+ ret = gif__recover_frame(gif, bitmap);
+ if (ret != GIF_OK) {
+ gif__restore_bg(gif, prev, bitmap);
+ }
+ }
}
- gif_data = (gif->gif_data + gif->buffer_position);
- gif_bytes = (gif_end - gif_data);
- /* Ensure we have enough data for the 10-byte image descriptor + 1-byte
- * gif trailer
- */
- if (gif_bytes < 12) {
- return_value = GIF_INSUFFICIENT_FRAME_DATA;
- goto gif_decode_frame_exit;
+ if (frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV) {
+ /* Store the previous frame for later restoration */
+ gif__record_frame(gif, bitmap);
}
- /* 10-byte Image Descriptor is:
- *
- * +0 CHAR Image Separator (0x2c)
- * +1 SHORT Image Left Position
- * +3 SHORT Image Top Position
- * +5 SHORT Width
- * +7 SHORT Height
- * +9 CHAR __Packed Fields__
- * 1BIT Local Colour Table Flag
- * 1BIT Interlace Flag
- * 1BIT Sort Flag
- * 2BITS Reserved
- * 3BITS Size of Local Colour Table
- */
- if (gif_data[0] != GIF_IMAGE_SEPARATOR) {
- return_value = GIF_DATA_ERROR;
- goto gif_decode_frame_exit;
+ ret = gif__decode(gif, frame, data, bitmap);
+
+ gif__bitmap_modified(gif);
+
+ if (frame->virgin) {
+ frame->opaque = gif__bitmap_get_opaque(gif);
+ frame->virgin = false;
}
- offset_x = gif_data[1] | (gif_data[2] << 8);
- offset_y = gif_data[3] | (gif_data[4] << 8);
- width = gif_data[5] | (gif_data[6] << 8);
- height = gif_data[7] | (gif_data[8] << 8);
+ gif__bitmap_set_opaque(gif, frame);
+
+ return ret;
+}
- /* Boundary checking - shouldn't ever happen except unless the data has
- * been modified since initialisation.
+/**
+ * Parse the application extension
+ *
+ * \param[in] frame The gif object we're decoding.
+ * \param[in] data The data to decode.
+ * \param[in] len Byte length of data.
+ * \return GIF_INSUFFICIENT_DATA if more data is needed,
+ * GIF_OK for success.
+ */
+static gif_result gif__parse_extension_graphic_control(
+ struct gif_frame *frame,
+ const uint8_t *data,
+ size_t len)
+{
+ /* 6-byte Graphic Control Extension is:
+ *
+ * +0 CHAR Graphic Control Label
+ * +1 CHAR Block Size
+ * +2 CHAR __Packed Fields__
+ * 3BITS Reserved
+ * 3BITS Disposal Method
+ * 1BIT User Input Flag
+ * 1BIT Transparent Color Flag
+ * +3 SHORT Delay Time
+ * +5 CHAR Transparent Color Index
*/
- if ((offset_x + width > gif->width) ||
- (offset_y + height > gif->height)) {
- return_value = GIF_DATA_ERROR;
- goto gif_decode_frame_exit;
+ if (len < 6) {
+ return GIF_INSUFFICIENT_DATA;
}
- /* Make sure we have a buffer to decode to.
- */
- if (gif_initialise_sprite(gif, gif->width, gif->height)) {
- return GIF_INSUFFICIENT_MEMORY;
+ frame->frame_delay = data[3] | (data[4] << 8);
+ if (data[2] & GIF_TRANSPARENCY_MASK) {
+ frame->transparency = true;
+ frame->transparency_index = data[5];
}
- /* Decode the flags */
- flags = gif_data[9];
- colour_table_size = 2 << (flags & GIF_COLOUR_TABLE_SIZE_MASK);
- interlace = flags & GIF_INTERLACE_MASK;
+ frame->disposal_method = ((data[2] & GIF_DISPOSAL_MASK) >> 2);
+ /* I have encountered documentation and GIFs in the
+ * wild that use 0x04 to restore the previous frame,
+ * rather than the officially documented 0x03. I
+ * believe some (older?) software may even actually
+ * export this way. We handle this as a type of
+ * "quirks" mode. */
+ if (frame->disposal_method == GIF_DISPOSAL_RESTORE_QUIRK) {
+ frame->disposal_method = GIF_DISPOSAL_RESTORE_PREV;
+ }
+
+ /* if we are clearing the background then we need to
+ * redraw enough to cover the previous frame too. */
+ frame->redraw_required =
+ frame->disposal_method == GIF_DISPOSAL_RESTORE_BG ||
+ frame->disposal_method == GIF_DISPOSAL_RESTORE_PREV;
+
+ return GIF_OK;
+}
- /* Advance data pointer to next block either colour table or image
- * data.
+/**
+ * Parse the application extension
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] data The data to decode.
+ * \param[in] len Byte length of data.
+ * \return GIF_INSUFFICIENT_DATA if more data is needed,
+ * GIF_OK for success.
+ */
+static gif_result gif__parse_extension_application(
+ struct gif_animation *gif,
+ const uint8_t *data,
+ size_t len)
+{
+ /* 14-byte+ Application Extension is:
+ *
+ * +0 CHAR Application Extension Label
+ * +1 CHAR Block Size
+ * +2 8CHARS Application Identifier
+ * +10 3CHARS Appl. Authentication Code
+ * +13 1-256 Application Data (Data sub-blocks)
*/
- gif_data += 10;
- gif_bytes = (gif_end - gif_data);
-
- /* Set up the colour table */
- if (flags & GIF_COLOUR_TABLE_MASK) {
- if (gif_bytes < (int)(3 * colour_table_size)) {
- return_value = GIF_INSUFFICIENT_FRAME_DATA;
- goto gif_decode_frame_exit;
+ if (len < 17) {
+ return GIF_INSUFFICIENT_DATA;
+ }
+
+ 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);
+ }
+
+ return GIF_OK;
+}
+
+/**
+ * Parse the frame's extensions
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to parse extensions for.
+ * \param[in] decode Whether to decode or skip over the extension.
+ * \return GIF_INSUFFICIENT_DATA if more data is needed,
+ * GIF_OK for success.
+ */
+static gif_result gif__parse_frame_extensions(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ const uint8_t **pos,
+ bool decode)
+{
+ const uint8_t *gif_data = *pos;
+ const uint8_t *gif_end = gif->gif_data + gif->buffer_size;
+ int gif_bytes = gif_end - gif_data;
+
+ /* Initialise the extensions */
+ while (gif_bytes > 0 && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
+ bool block_step = true;
+ gif_result ret;
+
+ gif_data++;
+ gif_bytes--;
+
+ if (gif_bytes == 0) {
+ return GIF_INSUFFICIENT_DATA;
}
- colour_table = gif->local_colour_table;
- if (!clear_image) {
- for (index = 0; index < colour_table_size; index++) {
- /* Gif colour map contents are r,g,b.
- *
- * We want to pack them bytewise into the
- * colour table, such that the red component
- * is in byte 0 and the alpha component is in
- * byte 3.
- */
- unsigned char *entry =
- (unsigned char *) &colour_table[index];
-
- entry[0] = gif_data[0]; /* r */
- entry[1] = gif_data[1]; /* g */
- entry[2] = gif_data[2]; /* b */
- entry[3] = 0xff; /* a */
-
- gif_data += 3;
+
+ /* Switch on extension label */
+ switch (gif_data[0]) {
+ case GIF_EXTENSION_GRAPHIC_CONTROL:
+ if (decode) {
+ ret = gif__parse_extension_graphic_control(
+ frame, gif_data, gif_bytes);
+ if (ret != GIF_OK) {
+ return ret;
+ }
}
- } else {
- gif_data += 3 * colour_table_size;
+ break;
+
+ case GIF_EXTENSION_APPLICATION:
+ if (decode) {
+ ret = gif__parse_extension_application(
+ gif, gif_data, gif_bytes);
+ if (ret != GIF_OK) {
+ return ret;
+ }
+ }
+ break;
+
+ case GIF_EXTENSION_COMMENT:
+ /* Move the pointer to the first data sub-block Skip 1
+ * byte for the extension label. */
+ ++gif_data;
+ block_step = false;
+ break;
+
+ default:
+ break;
+ }
+
+ if (block_step) {
+ /* Move the pointer to the first data sub-block Skip 2
+ * bytes for the extension label and size fields Skip
+ * the extension size itself
+ */
+ if (gif_bytes < 2) {
+ return GIF_INSUFFICIENT_DATA;
+ }
+ gif_data += 2 + gif_data[1];
}
- gif_bytes = (gif_end - gif_data);
- } else {
- colour_table = gif->global_colour_table;
- }
- /* Ensure sufficient data remains */
- if (gif_bytes < 1) {
- return_value = GIF_INSUFFICIENT_FRAME_DATA;
- goto gif_decode_frame_exit;
+ /* Repeatedly skip blocks until we get a zero block or run out
+ * of data. This data is ignored by this gif decoder. */
+ while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) {
+ gif_data += gif_data[0] + 1;
+ if (gif_data >= gif_end) {
+ return GIF_INSUFFICIENT_DATA;
+ }
+ }
+ gif_data++;
+ gif_bytes = gif_end - gif_data;
}
- /* check for an end marker */
- if (gif_data[0] == GIF_TRAILER) {
- return_value = GIF_OK;
- goto gif_decode_frame_exit;
+ if (gif_data > gif_end) {
+ gif_data = gif_end;
}
- /* Get the frame data */
- assert(gif->bitmap_callbacks.bitmap_get_buffer);
- frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
- if (!frame_data) {
- return GIF_INSUFFICIENT_MEMORY;
+ /* Set buffer position and return */
+ *pos = gif_data;
+ return GIF_OK;
+}
+
+/**
+ * Parse a GIF Image Descriptor.
+ *
+ * The format is:
+ *
+ * +0 CHAR Image Separator (0x2c)
+ * +1 SHORT Image Left Position
+ * +3 SHORT Image Top Position
+ * +5 SHORT Width
+ * +7 SHORT Height
+ * +9 CHAR __Packed Fields__
+ * 1BIT Local Colour Table Flag
+ * 1BIT Interlace Flag
+ * 1BIT Sort Flag
+ * 2BITS Reserved
+ * 3BITS Size of Local Colour Table
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to parse an image descriptor for.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_image_descriptor(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ const uint8_t **pos,
+ bool decode)
+{
+ const uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
+ enum {
+ GIF_IMAGE_DESCRIPTOR_LEN = 10u,
+ GIF_IMAGE_SEPARATOR = 0x2Cu,
+ };
+
+ assert(gif != NULL);
+ assert(frame != NULL);
+
+ if (len < GIF_IMAGE_DESCRIPTOR_LEN) {
+ return GIF_INSUFFICIENT_DATA;
}
- /* If we are clearing the image we just clear, if not decode */
- if (!clear_image) {
- /* Ensure we have enough data for a 1-byte LZW code size +
- * 1-byte gif trailer
- */
- if (gif_bytes < 2) {
- return_value = GIF_INSUFFICIENT_FRAME_DATA;
- goto gif_decode_frame_exit;
- }
+ if (decode) {
+ unsigned x, y, w, h;
- /* If we only have a 1-byte LZW code size + 1-byte gif trailer,
- * we're finished
- */
- if ((gif_bytes == 2) && (gif_data[1] == GIF_TRAILER)) {
- return_value = GIF_OK;
- goto gif_decode_frame_exit;
+ if (data[0] != GIF_IMAGE_SEPARATOR) {
+ return GIF_FRAME_DATA_ERROR;
}
- /* If the previous frame's disposal method requires we restore
- * the background colour or this is the first frame, clear
- * the frame data
- */
- if ((frame == 0) || (gif->decoded_frame == GIF_INVALID_FRAME)) {
- memset((char*)frame_data,
- GIF_TRANSPARENT_COLOUR,
- gif->width * gif->height * sizeof(int));
- gif->decoded_frame = frame;
- /* The line below would fill the image with its
- * background color, but because GIFs support
- * transparency we likely wouldn't want to do that. */
- /* memset((char*)frame_data, colour_table[gif->background_index], gif->width *
gif->height * sizeof(int)); */
- } else if ((frame != 0) &&
- (gif->frames[frame - 1].disposal_method == GIF_FRAME_CLEAR)) {
- return_value = gif_internal_decode_frame(gif,
- (frame - 1),
- true);
- if (return_value != GIF_OK) {
- goto gif_decode_frame_exit;
- }
+ x = data[1] | (data[2] << 8);
+ y = data[3] | (data[4] << 8);
+ w = data[5] | (data[6] << 8);
+ h = data[7] | (data[8] << 8);
+ frame->flags = data[9];
+
+ frame->redraw_x = x;
+ frame->redraw_y = y;
+ frame->redraw_width = w;
+ frame->redraw_height = h;
+
+ /* Frame size may have grown. */
+ gif->width = (x + w > gif->width ) ? x + w : gif->width;
+ gif->height = (y + h > gif->height) ? y + h : gif->height;
+ }
+
+ *pos += GIF_IMAGE_DESCRIPTOR_LEN;
+ return GIF_OK;
+}
- } else if ((frame != 0) &&
- (gif->frames[frame - 1].disposal_method == GIF_FRAME_RESTORE)) {
- /*
- * If the previous frame's disposal method requires we
- * restore the previous image, restore our saved image.
+/**
+ * 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.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__colour_table_extract(
+ struct gif_animation *gif,
+ uint32_t *colour_table,
+ size_t colour_table_entries,
+ const uint8_t **pos,
+ bool decode)
+{
+ const uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
+
+ if (len < colour_table_entries * 3) {
+ return GIF_INSUFFICIENT_DATA;
+ }
+
+ if (decode) {
+ int count = colour_table_entries;
+ uint8_t *entry = (uint8_t *)colour_table;
+
+ while (count--) {
+ /* Gif colour map contents are r,g,b.
+ *
+ * We want to pack them bytewise into the
+ * colour table, such that the red component
+ * is in byte 0 and the alpha component is in
+ * byte 3.
*/
- err = gif__recover_previous_frame(gif);
- if (err != GIF_OK) {
- /* see notes above on transparency
- * vs. background color
- */
- memset((char*)frame_data,
- GIF_TRANSPARENT_COLOUR,
- gif->width * gif->height * sizeof(int));
+
+ *entry++ = *data++; /* r */
+ *entry++ = *data++; /* g */
+ *entry++ = *data++; /* b */
+ *entry++ = 0xff; /* a */
+ }
+ }
+
+ *pos += colour_table_entries * 3;
+ return GIF_OK;
+}
+
+/**
+ * Get a frame's colour table.
+ *
+ * Sets up gif->colour_table for the frame.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to get the colour table for.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_colour_table(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ const uint8_t **pos,
+ bool decode)
+{
+ gif_result ret;
+
+ assert(gif != NULL);
+ assert(frame != NULL);
+
+ if ((frame->flags & GIF_COLOUR_TABLE_MASK) == 0) {
+ gif->colour_table = gif->global_colour_table;
+ return GIF_OK;
+ }
+
+ ret = gif__colour_table_extract(gif, gif->local_colour_table,
+ 2 << (frame->flags & GIF_COLOUR_TABLE_SIZE_MASK),
+ pos, decode);
+ if (ret != GIF_OK) {
+ return ret;
+ }
+
+ gif->colour_table = gif->local_colour_table;
+ return GIF_OK;
+}
+
+/**
+ * Parse the image data for a gif frame.
+ *
+ * Sets up gif->colour_table for the frame.
+ *
+ * \param[in] gif The gif object we're decoding.
+ * \param[in] frame The frame to get the colour table for.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_image_data(
+ struct gif_animation *gif,
+ struct gif_frame *frame,
+ const uint8_t **pos,
+ bool decode)
+{
+ const uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
+ uint32_t frame_idx = frame - gif->frames;
+ uint8_t minimum_code_size;
+ gif_result ret;
+
+ assert(gif != NULL);
+ assert(frame != NULL);
+
+ if (!decode) {
+ gif->frame_count_partial = frame_idx + 1;
+ }
+
+ /* Ensure sufficient data remains. A gif trailer or a minimum lzw code
+ * followed by a gif trailer is treated as OK, although without any
+ * image data. */
+ switch (len) {
+ default: if (data[0] == GIF_TRAILER) return GIF_OK;
+ break;
+ case 2: if (data[1] == GIF_TRAILER) return GIF_OK;
+ /* Fall through. */
+ case 1: if (data[0] == GIF_TRAILER) return GIF_OK;
+ /* Fall through. */
+ case 0: return GIF_INSUFFICIENT_DATA;
+ }
+
+ minimum_code_size = data[0];
+ if (minimum_code_size >= LZW_CODE_MAX) {
+ return GIF_DATA_ERROR;
+ }
+
+ if (decode) {
+ ret = gif__update_bitmap(gif, frame, data, frame_idx);
+ } else {
+ uint32_t block_size = 0;
+
+ /* Skip the minimum code size. */
+ data++;
+ len--;
+
+ while (block_size != 1) {
+ if (len < 1) return GIF_INSUFFICIENT_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 GIF_OK;
}
+
+ len -= block_size;
+ data += block_size;
}
- if (gif->frames[frame].disposal_method == GIF_FRAME_RESTORE) {
- /* Store the previous frame for later restoration */
- gif__record_previous_frame(gif);
+ gif->frame_count = frame_idx + 1;
+ gif->frames[frame_idx].display = true;
+ *pos = data;
+
+ /* Check if we've finished */
+ if (len < 1) {
+ return GIF_INSUFFICIENT_DATA;
+ } else {
+ if (data[0] == GIF_TRAILER) {
+ return GIF_OK;
+ }
}
- gif->decoded_frame = frame;
- gif->buffer_position = (gif_data - gif->gif_data) + 1;
+ return GIF_WORKING;
+ }
- return_value = gif__decode(gif, frame, width, height,
- offset_x, offset_y, interlace, gif_data[0],
- frame_data, colour_table);
+ return ret;
+}
+
+static struct gif_frame *gif__get_frame(
+ struct gif_animation *gif,
+ uint32_t frame_idx)
+{
+ struct gif_frame *frame;
+
+ if (gif->frame_holders > frame_idx) {
+ frame = &gif->frames[frame_idx];
} 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,
- GIF_TRANSPARENT_COLOUR,
- width * 4);
- } else {
- memset(frame_scanline,
- colour_table[gif->background_index],
- width * 4);
- }
- }
+ /* Allocate more memory */
+ size_t count = frame_idx + 1;
+ struct gif_frame *temp;
+
+ temp = realloc(gif->frames, count * sizeof(*frame));
+ if (temp == NULL) {
+ return NULL;
}
+ gif->frames = temp;
+ gif->frame_holders = count;
+
+ frame = &gif->frames[frame_idx];
+
+ frame->transparency = false;
+ frame->transparency_index = GIF_NO_TRANSPARENCY;
+ frame->frame_pointer = gif->buffer_position;
+ frame->redraw_required = false;
+ frame->disposal_method = 0;
+ frame->frame_delay = 100;
+ frame->display = false;
+ frame->virgin = true;
+ }
+
+ return frame;
+}
+
+/**
+ * Attempts to initialise the next frame
+ *
+ * \param[in] gif The animation context
+ * \param[in] frame_idx The frame number to decode.
+ * \param[in] decode Whether to decode the graphical image data.
+ * \return error code
+ * - GIF_INSUFFICIENT_DATA reached unexpected end of source data.
+ * - GIF_FRAME_DATA_ERROR for GIF frame data error
+ * - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
+ * - GIF_DATA_ERROR for GIF error (invalid frame header)
+ * - GIF_OK for successful decoding
+ * - GIF_WORKING for successful decoding if more frames are expected
+*/
+static gif_result gif__process_frame(
+ struct gif_animation *gif,
+ uint32_t frame_idx,
+ bool decode)
+{
+ gif_result ret;
+ const uint8_t *pos;
+ const uint8_t *end;
+ struct gif_frame *frame;
+
+ frame = gif__get_frame(gif, frame_idx);
+ if (frame == NULL) {
+ return GIF_INSUFFICIENT_MEMORY;
}
-gif_decode_frame_exit:
- if (!clear_image) {
- if (gif->bitmap_callbacks.bitmap_modified) {
- gif->bitmap_callbacks.bitmap_modified(gif->frame_image);
+ end = gif->gif_data + gif->buffer_size;
+
+ if (decode) {
+ pos = gif->gif_data + frame->frame_pointer;
+
+ /* Ensure this frame is supposed to be decoded */
+ if (frame->display == false) {
+ return GIF_OK;
}
- /* Check if we should test for optimisation */
- if (gif->frames[frame].virgin) {
- if (gif->bitmap_callbacks.bitmap_test_opaque) {
- gif->frames[frame].opaque =
gif->bitmap_callbacks.bitmap_test_opaque(gif->frame_image);
- } else {
- gif->frames[frame].opaque = false;
- }
- gif->frames[frame].virgin = false;
+ /* Ensure the frame is in range to decode */
+ if (frame_idx > gif->frame_count_partial) {
+ return GIF_INSUFFICIENT_DATA;
}
- if (gif->bitmap_callbacks.bitmap_set_opaque) {
- gif->bitmap_callbacks.bitmap_set_opaque(gif->frame_image,
gif->frames[frame].opaque);
+ /* Done if frame is already decoded */
+ if ((int)frame_idx == gif->decoded_frame) {
+ return GIF_OK;
+ }
+ } else {
+ pos = (uint8_t *)(gif->gif_data + gif->buffer_position);
+
+ /* Check if we've finished */
+ if (pos < end && pos[0] == GIF_TRAILER) {
+ return GIF_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 GIF_FRAME_DATA_ERROR;
}
}
- /* Restore the buffer position */
- gif->buffer_position = save_buffer_position;
+ /* Initialise any extensions */
+ ret = gif__parse_frame_extensions(gif, frame, &pos, !decode);
+ if (ret != GIF_OK) {
+ goto cleanup;
+ }
- return return_value;
-}
+ ret = gif__parse_image_descriptor(gif, frame, &pos, !decode);
+ if (ret != GIF_OK) {
+ goto cleanup;
+ }
+ ret = gif__parse_colour_table(gif, frame, &pos, decode);
+ if (ret != GIF_OK) {
+ goto cleanup;
+ }
+
+ ret = gif__parse_image_data(gif, frame, &pos, decode);
+ if (ret != GIF_OK) {
+ goto cleanup;
+ }
+
+cleanup:
+ if (!decode) {
+ gif->buffer_position = pos - gif->gif_data;
+ }
+
+ return ret;
+}
/* exported function documented in libnsgif.h */
void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks)
@@ -1085,33 +1113,100 @@ void gif_create(gif_animation *gif, gif_bitmap_callback_vt
*bitmap_callbacks)
gif->prev_index = GIF_INVALID_FRAME;
}
-
-/* exported function documented in libnsgif.h */
-gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data)
+/**
+ * Read GIF header.
+ *
+ * 6-byte GIF file header is:
+ *
+ * +0 3CHARS Signature ('GIF')
+ * +3 3CHARS Version ('87a' or '89a')
+ *
+ * \param[in] gif The GIF object we're decoding.
+ * \param[in,out] pos The current buffer position, updated on success.
+ * \param[in] strict Whether to require a known GIF version.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_header(
+ struct gif_animation *gif,
+ const uint8_t **pos,
+ bool strict)
{
- unsigned char *gif_data;
- unsigned int index;
- gif_result return_value;
+ const uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
- /* Initialize values */
- gif->buffer_size = size;
- gif->gif_data = data;
+ if (len < 6) {
+ return GIF_INSUFFICIENT_DATA;
+ }
- if (gif->lzw_ctx == NULL) {
- lzw_result res = lzw_context_create(
- (struct lzw_ctx **)&gif->lzw_ctx);
- if (res != LZW_OK) {
- return gif_error_from_lzw(res);
+ if (strncmp((const char *) data, "GIF", 3) != 0) {
+ return GIF_DATA_ERROR;
+ }
+ data += 3;
+
+ if (strict == true) {
+ if ((strncmp((const char *) data, "87a", 3) != 0) &&
+ (strncmp((const char *) data, "89a", 3) != 0)) {
+ return GIF_DATA_ERROR;
}
}
+ data += 3;
- /* Check for sufficient data to be a GIF (6-byte header + 7-byte
- * logical screen descriptor)
- */
- if (gif->buffer_size < GIF_STANDARD_HEADER_SIZE) {
+ *pos = data;
+ return GIF_OK;
+}
+
+/**
+ * Read Logical Screen Descriptor.
+ *
+ * 7-byte Logical Screen Descriptor is:
+ *
+ * +0 SHORT Logical Screen Width
+ * +2 SHORT Logical Screen Height
+ * +4 CHAR __Packed Fields__
+ * 1BIT Global Colour Table Flag
+ * 3BITS Colour Resolution
+ * 1BIT Sort Flag
+ * 3BITS Size of Global Colour Table
+ * +5 CHAR Background Colour Index
+ * +6 CHAR Pixel Aspect Ratio
+ *
+ * \param[in] gif The GIF object we're decoding.
+ * \param[in,out] pos The current buffer position, updated on success.
+ * \return GIF_OK on success, appropriate error otherwise.
+ */
+static gif_result gif__parse_logical_screen_descriptor(
+ struct gif_animation *gif,
+ const uint8_t **pos)
+{
+ const uint8_t *data = *pos;
+ size_t len = gif->gif_data + gif->buffer_size - data;
+
+ if (len < 7) {
return GIF_INSUFFICIENT_DATA;
}
+ gif->width = data[0] | (data[1] << 8);
+ gif->height = data[2] | (data[3] << 8);
+ gif->global_colours = data[4] & GIF_COLOUR_TABLE_MASK;
+ gif->colour_table_size = 2 << (data[4] & GIF_COLOUR_TABLE_SIZE_MASK);
+ gif->bg_index = data[5];
+ gif->aspect_ratio = data[6];
+ gif->loop_count = 1;
+
+ *pos += 7;
+ return GIF_OK;
+}
+
+/* exported function documented in libnsgif.h */
+gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data)
+{
+ const uint8_t *gif_data;
+ gif_result ret;
+
+ /* Initialize values */
+ gif->buffer_size = size;
+ gif->gif_data = data;
+
/* Get our current processing position */
gif_data = gif->gif_data + gif->buffer_position;
@@ -1122,6 +1217,7 @@ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned
char *data)
*/
gif->frame_image = NULL;
gif->frames = NULL;
+ gif->frame_holders = 0;
gif->local_colour_table = NULL;
gif->global_colour_table = NULL;
@@ -1130,44 +1226,18 @@ gif_result gif_initialise(gif_animation *gif, size_t size,
unsigned char *data)
gif->frame_count_partial = 0;
gif->decoded_frame = GIF_INVALID_FRAME;
- /* 6-byte GIF file header is:
- *
- * +0 3CHARS Signature ('GIF')
- * +3 3CHARS Version ('87a' or '89a')
- */
- if (strncmp((const char *) gif_data, "GIF", 3) != 0) {
- return GIF_DATA_ERROR;
+ ret = gif__parse_header(gif, &gif_data, false);
+ if (ret != GIF_OK) {
+ return ret;
}
- gif_data += 3;
- /* Ensure GIF reports version 87a or 89a */
- /*
- if ((strncmp(gif_data, "87a", 3) != 0) &&
- (strncmp(gif_data, "89a", 3) != 0))
- LOG(("Unknown GIF format - proceeding anyway"));
- */
- gif_data += 3;
-
- /* 7-byte Logical Screen Descriptor is:
- *
- * +0 SHORT Logical Screen Width
- * +2 SHORT Logical Screen Height
- * +4 CHAR __Packed Fields__
- * 1BIT Global Colour Table Flag
- * 3BITS Colour Resolution
- * 1BIT Sort Flag
- * 3BITS Size of Global Colour Table
- * +5 CHAR Background Colour Index
- * +6 CHAR Pixel Aspect Ratio
- */
- gif->width = gif_data[0] | (gif_data[1] << 8);
- gif->height = gif_data[2] | (gif_data[3] << 8);
- gif->global_colours = (gif_data[4] & GIF_COLOUR_TABLE_MASK);
- gif->colour_table_size = (2 << (gif_data[4] &
GIF_COLOUR_TABLE_SIZE_MASK));
- gif->background_index = gif_data[5];
- gif->aspect_ratio = gif_data[6];
- gif->loop_count = 1;
- gif_data += 7;
+ ret = gif__parse_logical_screen_descriptor(gif, &gif_data);
+ if (ret != GIF_OK) {
+ return ret;
+ }
+
+ /* Remember we've done this now */
+ gif->buffer_position = gif_data - gif->gif_data;
/* Some broken GIFs report the size as the screen size they
* were created in. As such, we detect for the common cases and
@@ -1191,8 +1261,8 @@ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned
char *data)
* is lying to us. It's far better to give the wrong colours
* than to trample over some memory somewhere.
*/
- gif->global_colour_table = calloc(GIF_MAX_COLOURS, sizeof(unsigned int));
- gif->local_colour_table = calloc(GIF_MAX_COLOURS, sizeof(unsigned int));
+ gif->global_colour_table = calloc(GIF_MAX_COLOURS, sizeof(uint32_t));
+ gif->local_colour_table = calloc(GIF_MAX_COLOURS, sizeof(uint32_t));
if ((gif->global_colour_table == NULL) ||
(gif->local_colour_table == NULL)) {
gif_finalise(gif);
@@ -1208,23 +1278,11 @@ gif_result gif_initialise(gif_animation *gif, size_t size,
unsigned char *data)
* termination block) Although generally useless, the GIF
* specification does not expressly prohibit this
*/
- if (gif->buffer_size == (GIF_STANDARD_HEADER_SIZE + 1)) {
+ if (gif->buffer_size == gif->buffer_position + 1) {
if (gif_data[0] == GIF_TRAILER) {
return GIF_OK;
- } else {
- return GIF_INSUFFICIENT_DATA;
}
}
-
- /* Initialise enough workspace for a frame */
- if ((gif->frames = (gif_frame *)malloc(sizeof(gif_frame))) == NULL) {
- gif_finalise(gif);
- return GIF_INSUFFICIENT_MEMORY;
- }
- gif->frame_holders = 1;
-
- /* Remember we've done this now */
- gif->buffer_position = gif_data - gif->gif_data;
}
/* Do the colour map if we haven't already. As the top byte is always
@@ -1234,71 +1292,59 @@ gif_result gif_initialise(gif_animation *gif, size_t size,
unsigned char *data)
if (gif->global_colour_table[0] == GIF_PROCESS_COLOURS) {
/* Check for a global colour map signified by bit 7 */
if (gif->global_colours) {
- if (gif->buffer_size < (gif->colour_table_size * 3 +
GIF_STANDARD_HEADER_SIZE)) {
- return GIF_INSUFFICIENT_DATA;
- }
- for (index = 0; index < gif->colour_table_size; index++) {
- /* Gif colour map contents are r,g,b.
- *
- * We want to pack them bytewise into the
- * colour table, such that the red component
- * is in byte 0 and the alpha component is in
- * byte 3.
- */
- unsigned char *entry = (unsigned char *) &gif->
- global_colour_table[index];
-
- entry[0] = gif_data[0]; /* r */
- entry[1] = gif_data[1]; /* g */
- entry[2] = gif_data[2]; /* b */
- entry[3] = 0xff; /* a */
-
- gif_data += 3;
+ ret = gif__colour_table_extract(gif,
+ gif->global_colour_table,
+ gif->colour_table_size,
+ &gif_data, true);
+ if (ret != GIF_OK) {
+ return ret;
}
+
gif->buffer_position = (gif_data - gif->gif_data);
} else {
/* Create a default colour table with the first two
* colours as black and white
*/
- unsigned int *entry = gif->global_colour_table;
+ uint32_t *entry = gif->global_colour_table;
entry[0] = 0x00000000;
/* Force Alpha channel to opaque */
- ((unsigned char *) entry)[3] = 0xff;
+ ((uint8_t *) entry)[3] = 0xff;
entry[1] = 0xffffffff;
}
- }
- /* Repeatedly try to initialise frames */
- while ((return_value = gif_initialise_frame(gif)) == GIF_WORKING);
-
- /* If there was a memory error tell the caller */
- if ((return_value == GIF_INSUFFICIENT_MEMORY) ||
- (return_value == GIF_DATA_ERROR)) {
- return return_value;
+ if (gif->global_colours &&
+ gif->bg_index < gif->colour_table_size) {
+ size_t bg_idx = gif->bg_index;
+ gif->bg_colour = gif->global_colour_table[bg_idx];
+ } else {
+ gif->bg_colour = gif->global_colour_table[0];
+ }
}
- /* If we didn't have some frames then a GIF_INSUFFICIENT_DATA becomes a
- * GIF_INSUFFICIENT_FRAME_DATA
- */
- if ((return_value == GIF_INSUFFICIENT_DATA) &&
- (gif->frame_count_partial > 0)) {
- return GIF_INSUFFICIENT_FRAME_DATA;
+ if (gif->lzw_ctx == NULL) {
+ lzw_result res = lzw_context_create(
+ (struct lzw_ctx **)&gif->lzw_ctx);
+ if (res != LZW_OK) {
+ return gif_error_from_lzw(res);
+ }
}
- /* Return how many we got */
- return return_value;
-}
+ /* Repeatedly try to initialise frames */
+ do {
+ ret = gif__process_frame(gif, gif->frame_count, false);
+ } while (ret == GIF_WORKING);
+ return ret;
+}
/* exported function documented in libnsgif.h */
gif_result gif_decode_frame(gif_animation *gif, unsigned int frame)
{
- return gif_internal_decode_frame(gif, frame, false);
+ return gif__process_frame(gif, frame, true);
}
-
/* exported function documented in libnsgif.h */
void gif_finalise(gif_animation *gif)
{
diff --git a/src/lzw.c b/src/lzw.c
index 710895e..6f85caa 100644
--- a/src/lzw.c
+++ b/src/lzw.c
@@ -96,7 +96,6 @@ struct lzw_ctx {
uint8_t stack_base[LZW_TABLE_ENTRY_MAX];
};
-
/* Exported function, documented in lzw.h */
lzw_result lzw_context_create(struct lzw_ctx **ctx)
{
@@ -109,14 +108,12 @@ lzw_result lzw_context_create(struct lzw_ctx **ctx)
return LZW_OK;
}
-
/* Exported function, documented in lzw.h */
void lzw_context_destroy(struct lzw_ctx *ctx)
{
free(ctx);
}
-
/**
* Advance the context to the next sub-block in the input data.
*
@@ -153,7 +150,6 @@ static lzw_result lzw__block_advance(struct lzw_read_ctx *restrict
ctx)
return LZW_OK;
}
-
/**
* Get the next LZW code of given size from the raw input data.
*
@@ -223,7 +219,6 @@ static inline lzw_result lzw__read_code(
return LZW_OK;
}
-
/**
* Handle clear code.
*
diff --git a/src/lzw.h b/src/lzw.h
index 4549c60..c68753a 100644
--- a/src/lzw.h
+++ b/src/lzw.h
@@ -4,6 +4,7 @@
*
http://www.opensource.org/licenses/mit-license.php
*
* Copyright 2017 Michael Drake <michael.drake(a)codethink.co.uk>
+ * Copyright 2021 Michael Drake <tlsa(a)netsurf-browser.org>
*/
#ifndef LZW_H_
@@ -19,11 +20,9 @@
/** Maximum LZW code size in bits */
#define LZW_CODE_MAX 12
-
/* Declare lzw internal context structure */
struct lzw_ctx;
-
/** LZW decoding response codes */
typedef enum lzw_result {
LZW_OK, /**< Success */
@@ -37,7 +36,6 @@ typedef enum lzw_result {
LZW_BAD_CODE, /**< Error: Bad LZW code */
} lzw_result;
-
/**
* Create an LZW decompression context.
*
diff --git a/test/decode_gif.c b/test/decode_gif.c
index 64387ef..52d73b6 100644
--- a/test/decode_gif.c
+++ b/test/decode_gif.c
@@ -113,9 +113,6 @@ static void warning(const char *context, gif_result code)
fprintf(stderr, "%s failed: ", context);
switch (code)
{
- case GIF_INSUFFICIENT_FRAME_DATA:
- fprintf(stderr, "GIF_INSUFFICIENT_FRAME_DATA");
- break;
case GIF_FRAME_DATA_ERROR:
fprintf(stderr, "GIF_FRAME_DATA_ERROR");
break;
--
NetSurf GIF Decoder