libnsgif: branch master updated. release/0.1.4-20-g099b873
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/libnsgif.git/shortlog/099b873abca6b868f159...
...commit http://git.netsurf-browser.org/libnsgif.git/commit/099b873abca6b868f159e1...
...tree http://git.netsurf-browser.org/libnsgif.git/tree/099b873abca6b868f159e168...
The branch, master has been updated
via 099b873abca6b868f159e16894f3640d40dca5aa (commit)
via f9797e4ae82090808036b1e1ec4318d0f1bdc456 (commit)
via 34f3a9a81824dcda5a3e1944d14b0273298ec0c3 (commit)
via 86ebd63cc1e9570cf247da26635375e31fc57d40 (commit)
via 960c3b6d2229dcc7cc4a4ef60838329b78880e66 (commit)
via 8c42f9f934005ba40645ba326b74c8f8b0675df2 (commit)
via fe2a38686946c708c2e52031459673a10db41dc8 (commit)
from 4a6136b81993da5c838df13eb3b1433deb87abf4 (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=099b873abca6b868f1...
commit 099b873abca6b868f159e16894f3640d40dca5aa
Merge: 4a6136b f9797e4
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
Merge branch 'tlsa/lzw'
-----------------------------------------------------------------------
Summary of changes:
include/libnsgif.h | 2 +
src/Makefile | 2 +-
src/libnsgif.c | 368 ++++++---------------------------------------------
src/lzw.c | 372 ++++++++++++++++++++++++++++++++++++++++++++++++++++
src/lzw.h | 105 +++++++++++++++
test/decode_gif.c | 66 ++++++----
6 files changed, 564 insertions(+), 351 deletions(-)
create mode 100644 src/lzw.c
create mode 100644 src/lzw.h
diff --git a/include/libnsgif.h b/include/libnsgif.h
index fd92a0a..a819fec 100644
--- a/include/libnsgif.h
+++ b/include/libnsgif.h
@@ -93,6 +93,8 @@ typedef struct 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 */
diff --git a/src/Makefile b/src/Makefile
index 0fa4dc6..cb5d31f 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,4 +1,4 @@
# Sources
-DIR_SOURCES := libnsgif.c
+DIR_SOURCES := libnsgif.c lzw.c
include $(NSBUILD)/Makefile.subdir
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 998f8d7..6bf9956 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -8,6 +8,7 @@
*/
#include <stdbool.h>
+#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
@@ -15,6 +16,8 @@
#include "libnsgif.h"
#include "utils/log.h"
+#include "lzw.h"
+
/**
*
* \file
@@ -36,9 +39,6 @@
/** Internal flag that a frame is invalid/unprocessed */
#define GIF_INVALID_FRAME -1
-/** Maximum LZW bits available */
-#define GIF_MAX_LZW 12
-
/** Transparent colour */
#define GIF_TRANSPARENT_COLOUR 0x00
@@ -65,313 +65,6 @@
/** standard GIF header size */
#define GIF_STANDARD_HEADER_SIZE 13
-/** LZW parameters */
-struct lzw_s {
- unsigned char buf[4];
- const unsigned char *direct;
- int table[2][(1 << GIF_MAX_LZW)];
- unsigned char stack[(1 << GIF_MAX_LZW) * 2];
- unsigned char *stack_pointer;
-
- int code_size, set_code_size;
-
- int max_code, max_code_size;
-
- int clear_code, end_code;
-
- int curbit, lastbit, last_byte;
-
- int firstcode;
- int oldcode;
-
- bool zero_data_block;
- bool get_done;
-};
-
-
-/* General LZW values. They are shared for all GIFs being decoded, and thus we
- * can't handle progressive decoding efficiently without having the data for
- * each image which would use an extra 10Kb or so per GIF.
- */
-static struct lzw_s lzw_params = {
- .zero_data_block = false,
-};
-
-static struct lzw_s *lzw = &lzw_params;
-
-/**
- * get the next LZW code from the GIF
- *
- * reads codes from the input data stream coping with GIF data sub blocking
- *
- * \param[in] compressed_data LZW compressed data
- * \param[in] compressed_data_len Byte size of compressed_data
- * \param[in,out] compressed_data_pos Current position in compressed_data
- * updated on exit.
- * \param[in] code_size Number of bits in the current LZW code
- * \return The next code to process or error return code
- */
-static int gif_next_code(
- const uint8_t *compressed_data,
- uint32_t compressed_data_len,
- uint32_t *compressed_data_pos,
- int code_size)
-{
- int i, j, end, count, ret;
- uint32_t pos = *compressed_data_pos;
- const unsigned char *b;
- static const int maskTbl[16] = {
- 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f,
- 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff
- };
-
- end = lzw->curbit + code_size;
- if (end >= lzw->lastbit) {
- if (lzw->get_done) {
- return GIF_END_OF_FRAME;
- }
- lzw->buf[0] = lzw->direct[lzw->last_byte - 2];
- lzw->buf[1] = lzw->direct[lzw->last_byte - 1];
-
- /* get the next block */
- lzw->direct = compressed_data + pos;
- if (pos >= compressed_data_len) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
-
- count = lzw->direct[0];
- lzw->zero_data_block = (count == 0);
- if ((pos + count) >= compressed_data_len) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
-
- if (count == 0) {
- lzw->get_done = true;
- } else {
- if (pos + 3 >= compressed_data_len) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- lzw->direct -= 1;
- lzw->buf[2] = lzw->direct[2];
- lzw->buf[3] = lzw->direct[3];
- }
- pos += count + 1;
-
- /* update our variables */
- lzw->last_byte = 2 + count;
- lzw->curbit = (lzw->curbit - lzw->lastbit) + 16;
- lzw->lastbit = (2 + count) << 3;
- end = lzw->curbit + code_size;
- }
-
- i = lzw->curbit >> 3;
- if (i < 2) {
- b = lzw->buf;
- } else {
- b = lzw->direct;
- }
-
- ret = b[i];
- j = (end >> 3) - 1;
- if (i <= j) {
- ret |= (b[i + 1] << 8);
- if (i < j) {
- ret |= (b[i + 2] << 16);
- }
- }
- ret = (ret >> (lzw->curbit % 8)) & maskTbl[code_size];
- lzw->curbit += code_size;
-
- *compressed_data_pos = pos;
- return ret;
-}
-
-
-/**
- * Clear LZW code dictionary
- *
- * \param[in] compressed_data LZW compressed data
- * \param[in] compressed_data_len Byte size of compressed_data
- * \param[in,out] compressed_data_pos Current position in compressed_data
- * updated on exit.
- * \return GIF_OK or error code.
- */
-static gif_result gif_clear_codes_LZW(
- const uint8_t *compressed_data,
- uint32_t compressed_data_len,
- uint32_t *compressed_data_pos)
-{
- int i;
- int code;
-
- if (lzw->clear_code >= (1 << GIF_MAX_LZW)) {
- lzw->stack_pointer = lzw->stack;
- return GIF_FRAME_DATA_ERROR;
- }
-
- /* initialise our table */
- memset(lzw->table, 0x00, (1 << GIF_MAX_LZW) * 8);
- for (i = 0; i < lzw->clear_code; ++i) {
- lzw->table[1][i] = i;
- }
-
- /* reset LZW parameters */
- lzw->code_size = lzw->set_code_size + 1;
- lzw->max_code_size = lzw->clear_code << 1;
- lzw->max_code = lzw->clear_code + 2;
- lzw->stack_pointer = lzw->stack;
-
- /* process repeated clear codes */
- do {
- code = gif_next_code(compressed_data, compressed_data_len,
- compressed_data_pos, lzw->code_size);
- if (code < 0) {
- return code;
- }
- } while (code == lzw->clear_code);
- lzw->firstcode = lzw->oldcode = code;
-
- *lzw->stack_pointer++ = lzw->firstcode;
-
- return GIF_OK;
-}
-
-
-/**
- * Initialise LZW
- *
- * This initialises a LZW context ready to commence decompression.
- *
- * \param[in] compressed_data LZW compressed data
- * \param[in] compressed_data_len Byte size of compressed_data
- * \param[in,out] compressed_data_pos Current position in compressed_data
- * updated on exit.
- * \param[in] initial_code_size Size of codes used on clearing of code dictionary
- */
-static gif_result gif_initialise_LZW(
- const uint8_t *compressed_data,
- uint32_t compressed_data_len,
- uint32_t *compressed_data_pos,
- int initial_code_size)
-{
- lzw->set_code_size = initial_code_size;
- lzw->code_size = lzw->set_code_size + 1;
- lzw->clear_code = (1 << lzw->set_code_size);
- lzw->end_code = lzw->clear_code + 1;
- lzw->max_code_size = lzw->clear_code << 1;
- lzw->max_code = lzw->clear_code + 2;
- lzw->curbit = lzw->lastbit = 0;
- lzw->last_byte = 2;
- lzw->get_done = false;
- lzw->direct = lzw->buf;
-
- return gif_clear_codes_LZW(compressed_data,
- compressed_data_len,
- compressed_data_pos);
-}
-
-/**
- * fill the LZW stack with decompressed data
- *
- * \param[in] compressed_data LZW compressed data
- * \param[in] compressed_data_len Byte size of compressed_data
- * \param[in,out] compressed_data_pos Current position in compressed_data
- * updated on exit.
- * \return true on sucessful decode of the next LZW code else false.
- */
-static gif_result gif_next_LZW(
- const uint8_t *compressed_data,
- uint32_t compressed_data_len,
- uint32_t *compressed_data_pos)
-{
- int code, incode;
- int block_size;
- int new_code;
-
- code = gif_next_code(compressed_data, compressed_data_len,
- compressed_data_pos, lzw->code_size);
- if (code < 0) {
- return code;
- }
-
- if (code == lzw->clear_code) {
- return gif_clear_codes_LZW(compressed_data,
- compressed_data_len,
- compressed_data_pos);
- }
-
- if (code == lzw->end_code) {
- /* skip to the end of our data so multi-image GIFs work */
- if (lzw->zero_data_block) {
- return GIF_FRAME_DATA_ERROR;
- }
- block_size = 0;
- while (block_size != 1 &&
- *compressed_data_pos < compressed_data_len) {
- block_size = compressed_data[*compressed_data_pos] + 1;
- *compressed_data_pos += block_size;
- }
- return GIF_FRAME_DATA_ERROR;
- }
-
- incode = code;
- if (code >= lzw->max_code) {
- if (lzw->stack_pointer >= lzw->stack + ((1 << GIF_MAX_LZW) * 2)) {
- return GIF_FRAME_DATA_ERROR;
- }
- *lzw->stack_pointer++ = lzw->firstcode;
- code = lzw->oldcode;
- }
-
- /* The following loop is the most important in the GIF decoding cycle
- * as every single pixel passes through it.
- *
- * Note: our stack is always big enough to hold a complete decompressed
- * chunk.
- */
- while (code >= lzw->clear_code) {
- if (lzw->stack_pointer >= lzw->stack + ((1 << GIF_MAX_LZW) * 2) ||
- code >= (1 << GIF_MAX_LZW)) {
- return GIF_FRAME_DATA_ERROR;
- }
- *lzw->stack_pointer++ = lzw->table[1][code];
- new_code = lzw->table[0][code];
- if (new_code < lzw->clear_code) {
- code = new_code;
- break;
- }
-
- if (lzw->stack_pointer >= lzw->stack + ((1 << GIF_MAX_LZW) * 2) ||
- new_code >= (1 << GIF_MAX_LZW)) {
- return GIF_FRAME_DATA_ERROR;
- }
- *lzw->stack_pointer++ = lzw->table[1][new_code];
- code = lzw->table[0][new_code];
- if (code == new_code) {
- return GIF_FRAME_DATA_ERROR;
- }
- }
-
- if (lzw->stack_pointer >= lzw->stack + ((1 << GIF_MAX_LZW) * 2)) {
- return GIF_FRAME_DATA_ERROR;
- }
- *lzw->stack_pointer++ = lzw->firstcode = lzw->table[1][code];
-
- code = lzw->max_code;
- if (code < (1 << GIF_MAX_LZW)) {
- lzw->table[0][code] = lzw->oldcode;
- lzw->table[1][code] = lzw->firstcode;
- ++lzw->max_code;
- if ((lzw->max_code >= lzw->max_code_size) &&
- (lzw->max_code_size < (1 << GIF_MAX_LZW))) {
- lzw->max_code_size = lzw->max_code_size << 1;
- ++lzw->code_size;
- }
- }
- lzw->oldcode = incode;
- return GIF_OK;
-}
-
/**
* Updates the sprite memory size
@@ -725,7 +418,7 @@ static gif_result gif_initialise_frame(gif_animation *gif)
if (gif_bytes < 1) {
return GIF_INSUFFICIENT_FRAME_DATA;
}
- if (gif_data[0] > GIF_MAX_LZW) {
+ if (gif_data[0] > LZW_CODE_MAX) {
return GIF_DATA_ERROR;
}
@@ -863,7 +556,19 @@ static unsigned int gif_interlaced_line(int height, int y) {
}
-
+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,
+ };
+ return g_res[l_res];
+}
/**
@@ -878,7 +583,6 @@ gif_internal_decode_frame(gif_animation *gif,
unsigned int frame,
bool clear_image)
{
- gif_result res;
unsigned int index = 0;
unsigned char *gif_data, *gif_end;
int gif_bytes;
@@ -1038,6 +742,10 @@ gif_internal_decode_frame(gif_animation *gif,
/* If we are clearing the image we just clear, if not decode */
if (!clear_image) {
+ lzw_result res;
+ const uint8_t *stack_base;
+ const uint8_t *stack_pos;
+
/* Ensure we have enough data for a 1-byte LZW code size +
* 1-byte gif trailer
*/
@@ -1114,10 +822,11 @@ gif_internal_decode_frame(gif_animation *gif,
gif->buffer_position = (gif_data - gif->gif_data) + 1;
/* Initialise the LZW decoding */
- res = gif_initialise_LZW(gif->gif_data, gif->buffer_size,
- &gif->buffer_position, gif_data[0]);
- if (res != GIF_OK) {
- return res;
+ res = lzw_decode_init(gif->lzw_ctx, gif->gif_data,
+ gif->buffer_size, gif->buffer_position,
+ gif_data[0], &stack_base, &stack_pos);
+ if (res != LZW_OK) {
+ return gif_error_from_lzw(res);
}
/* Decompress the data */
@@ -1135,14 +844,14 @@ gif_internal_decode_frame(gif_animation *gif,
*/
x = width;
while (x > 0) {
- burst_bytes = (lzw->stack_pointer - lzw->stack);
+ burst_bytes = (stack_pos - stack_base);
if (burst_bytes > 0) {
if (burst_bytes > x) {
burst_bytes = x;
}
x -= burst_bytes;
while (burst_bytes-- > 0) {
- colour = *--lzw->stack_pointer;
+ colour = *--stack_pos;
if (((gif->frames[frame].transparency) &&
(colour != gif->frames[frame].transparency_index)) ||
(!gif->frames[frame].transparency)) {
@@ -1151,15 +860,13 @@ gif_internal_decode_frame(gif_animation *gif,
frame_scanline++;
}
} else {
- res = gif_next_LZW(gif->gif_data,
- gif->buffer_size,
- &gif->buffer_position);
- if (res != GIF_OK) {
+ res = lzw_decode(gif->lzw_ctx, &stack_pos);
+ if (res != LZW_OK) {
/* Unexpected end of frame, try to recover */
- if (res == GIF_END_OF_FRAME) {
+ if (res == LZW_OK_EOD) {
return_value = GIF_OK;
} else {
- return_value = res;
+ return_value = gif_error_from_lzw(res);
}
goto gif_decode_frame_exit;
}
@@ -1230,6 +937,14 @@ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *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);
+ }
+ }
+
/* Check for sufficient data to be a GIF (6-byte header + 7-byte
* logical screen descriptor)
*/
@@ -1448,4 +1163,7 @@ void gif_finalise(gif_animation *gif)
gif->local_colour_table = NULL;
free(gif->global_colour_table);
gif->global_colour_table = NULL;
+
+ lzw_context_destroy(gif->lzw_ctx);
+ gif->lzw_ctx = NULL;
}
diff --git a/src/lzw.c b/src/lzw.c
new file mode 100644
index 0000000..6b7156e
--- /dev/null
+++ b/src/lzw.c
@@ -0,0 +1,372 @@
+/*
+ * This file is part of NetSurf's LibNSGIF, http://www.netsurf-browser.org/
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * Copyright 2017 Michael Drake <michael.drake(a)codethink.co.uk>
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "lzw.h"
+
+/**
+ * \file
+ * \brief LZW decompression (implementation)
+ *
+ * Decoder for GIF LZW data.
+ */
+
+
+/**
+ * Context for reading LZW data.
+ *
+ * LZW data is split over multiple sub-blocks. Each sub-block has a
+ * byte at the start, which says the sub-block size, and then the data.
+ * Zero-size sub-blocks have no data, and the biggest sub-block size is
+ * 255, which means there are 255 bytes of data following the sub-block
+ * size entry.
+ *
+ * Note that an individual LZW code can be split over up to three sub-blocks.
+ */
+struct lzw_read_ctx {
+ const uint8_t *data; /**< Pointer to start of input data */
+ uint32_t data_len; /**< Input data length */
+ uint32_t data_sb_next; /**< Offset to sub-block size */
+
+ const uint8_t *sb_data; /**< Pointer to current sub-block in data */
+ uint32_t sb_bit; /**< Current bit offset in sub-block */
+ uint32_t sb_bit_count; /**< Bit count in sub-block */
+};
+
+/**
+ * LZW dictionary entry.
+ *
+ * Records in the dictionary are composed of 1 or more entries.
+ * Entries point to previous entries which can be followed to compose
+ * the complete record. To compose the record in reverse order, take
+ * the `last_value` from each entry, and move to the previous entry.
+ * If the previous_entry's index is < the current clear_code, then it
+ * is the last entry in the record.
+ */
+struct lzw_dictionary_entry {
+ uint8_t last_value; /**< Last value for record ending at entry. */
+ uint8_t first_value; /**< First value for entry's record. */
+ uint16_t previous_entry; /**< Offset in dictionary to previous entry. */
+};
+
+/**
+ * LZW decompression context.
+ */
+struct lzw_ctx {
+ /** Input reading context */
+ struct lzw_read_ctx input;
+
+ uint32_t previous_code; /**< Code read from input previously. */
+ uint32_t previous_code_first; /**< First value of previous code. */
+
+ uint32_t initial_code_size; /**< Starting LZW code size. */
+ uint32_t current_code_size; /**< Current LZW code size. */
+ uint32_t current_code_size_max; /**< Max code value for current size. */
+
+ uint32_t clear_code; /**< Special Clear code value */
+ uint32_t eoi_code; /**< Special End of Information code value */
+
+ uint32_t current_entry; /**< Next position in table to fill. */
+
+ /** Output value stack. */
+ uint8_t stack_base[1 << LZW_CODE_MAX];
+
+ /** LZW decode dictionary. Generated during decode. */
+ struct lzw_dictionary_entry table[1 << LZW_CODE_MAX];
+};
+
+
+/* Exported function, documented in lzw.h */
+lzw_result lzw_context_create(struct lzw_ctx **ctx)
+{
+ struct lzw_ctx *c = malloc(sizeof(*c));
+ if (c == NULL) {
+ return LZW_NO_MEM;
+ }
+
+ *ctx = c;
+ 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.
+ *
+ * \param[in] ctx LZW reading context, updated on success.
+ * \return LZW_OK or LZW_OK_EOD on success, appropriate error otherwise.
+ */
+static lzw_result lzw__block_advance(struct lzw_read_ctx *ctx)
+{
+ uint32_t block_size;
+ uint32_t next_block_pos = ctx->data_sb_next;
+ const uint8_t *data_next = ctx->data + next_block_pos;
+
+ if (next_block_pos >= ctx->data_len) {
+ return LZW_NO_DATA;
+ }
+
+ block_size = *data_next;
+
+ if ((next_block_pos + block_size) >= ctx->data_len) {
+ return LZW_NO_DATA;
+ }
+
+ ctx->sb_bit = 0;
+ ctx->sb_bit_count = block_size * 8;
+
+ if (block_size == 0) {
+ ctx->data_sb_next += 1;
+ return LZW_OK_EOD;
+ }
+
+ ctx->sb_data = data_next + 1;
+ ctx->data_sb_next += block_size + 1;
+
+ return LZW_OK;
+}
+
+
+/**
+ * Get the next LZW code of given size from the raw input data.
+ *
+ * Reads codes from the input data stream coping with GIF data sub-blocks.
+ *
+ * \param[in] ctx LZW reading context, updated.
+ * \param[in] code_size Size of LZW code to get from data.
+ * \param[out] code_out Returns an LZW code on success.
+ * \return LZW_OK or LZW_OK_EOD on success, appropriate error otherwise.
+ */
+static inline lzw_result lzw__next_code(
+ struct lzw_read_ctx *ctx,
+ uint8_t code_size,
+ uint32_t *code_out)
+{
+ uint32_t code = 0;
+ uint8_t current_bit = ctx->sb_bit & 0x7;
+ uint8_t byte_advance = (current_bit + code_size) >> 3;
+
+ if (ctx->sb_bit + code_size < ctx->sb_bit_count) {
+ /* Fast path: code fully inside this sub-block */
+ const uint8_t *data = ctx->sb_data + (ctx->sb_bit >> 3);
+ switch (byte_advance) {
+ case 2: code |= data[2] << 16;
+ case 1: code |= data[1] << 8;
+ case 0: code |= data[0] << 0;
+ }
+ ctx->sb_bit += code_size;
+ } else {
+ /* Slow path: code spans sub-blocks */
+ uint8_t byte = 0;
+ uint8_t bits_remaining_0 = (code_size < (8 - current_bit)) ?
+ code_size : (8 - current_bit);
+ uint8_t bits_remaining_1 = code_size - bits_remaining_0;
+ uint8_t bits_used[3] = {
+ [0] = bits_remaining_0,
+ [1] = bits_remaining_1 < 8 ? bits_remaining_1 : 8,
+ [2] = bits_remaining_1 - 8,
+ };
+
+ while (true) {
+ const uint8_t *data = ctx->sb_data;
+ lzw_result res;
+
+ /* Get any data from end of this sub-block */
+ while (byte <= byte_advance &&
+ ctx->sb_bit < ctx->sb_bit_count) {
+ code |= data[ctx->sb_bit >> 3] << (byte << 3);
+ ctx->sb_bit += bits_used[byte];
+ byte++;
+ }
+
+ /* Check if we have all we need */
+ if (byte > byte_advance) {
+ break;
+ }
+
+ /* Move to next sub-block */
+ res = lzw__block_advance(ctx);
+ if (res != LZW_OK) {
+ return res;
+ }
+ }
+ }
+
+ *code_out = (code >> current_bit) & ((1 << code_size) - 1);
+ return LZW_OK;
+}
+
+
+/**
+ * Clear LZW code dictionary.
+ *
+ * \param[in] ctx LZW reading context, updated.
+ * \param[out] stack_pos_out Returns current stack position.
+ * \return LZW_OK or error code.
+ */
+static lzw_result lzw__clear_codes(
+ struct lzw_ctx *ctx,
+ const uint8_t ** const stack_pos_out)
+{
+ uint32_t code;
+ uint8_t *stack_pos;
+
+ /* Reset dictionary building context */
+ ctx->current_code_size = ctx->initial_code_size + 1;
+ ctx->current_code_size_max = (1 << ctx->current_code_size) - 1;;
+ ctx->current_entry = (1 << ctx->initial_code_size) + 2;
+
+ /* There might be a sequence of clear codes, so process them all */
+ do {
+ lzw_result res = lzw__next_code(&ctx->input,
+ ctx->current_code_size, &code);
+ if (res != LZW_OK) {
+ return res;
+ }
+ } while (code == ctx->clear_code);
+
+ /* The initial code must be from the initial dictionary. */
+ if (code > ctx->clear_code) {
+ return LZW_BAD_ICODE;
+ }
+
+ /* Record this initial code as "previous" code, needed during decode. */
+ ctx->previous_code = code;
+ ctx->previous_code_first = code;
+
+ /* Reset the stack, and add first non-clear code added as first item. */
+ stack_pos = ctx->stack_base;
+ *stack_pos++ = code;
+
+ *stack_pos_out = stack_pos;
+ return LZW_OK;
+}
+
+
+/* Exported function, documented in lzw.h */
+lzw_result lzw_decode_init(
+ struct lzw_ctx *ctx,
+ const uint8_t *compressed_data,
+ uint32_t compressed_data_len,
+ uint32_t compressed_data_pos,
+ uint8_t code_size,
+ const uint8_t ** const stack_base_out,
+ const uint8_t ** const stack_pos_out)
+{
+ struct lzw_dictionary_entry *table = ctx->table;
+
+ /* Initialise the input reading context */
+ ctx->input.data = compressed_data;
+ ctx->input.data_len = compressed_data_len;
+ ctx->input.data_sb_next = compressed_data_pos;
+
+ ctx->input.sb_bit = 0;
+ ctx->input.sb_bit_count = 0;
+
+ /* Initialise the dictionary building context */
+ ctx->initial_code_size = code_size;
+
+ ctx->clear_code = (1 << code_size) + 0;
+ ctx->eoi_code = (1 << code_size) + 1;
+
+ /* Initialise the standard dictionary entries */
+ for (uint32_t i = 0; i < ctx->clear_code; ++i) {
+ table[i].first_value = i;
+ table[i].last_value = i;
+ }
+
+ *stack_base_out = ctx->stack_base;
+ return lzw__clear_codes(ctx, stack_pos_out);
+}
+
+
+/* Exported function, documented in lzw.h */
+lzw_result lzw_decode(struct lzw_ctx *ctx,
+ const uint8_t ** const stack_pos_out)
+{
+ lzw_result res;
+ uint32_t code_new;
+ uint32_t code_out;
+ uint8_t last_value;
+ uint8_t *stack_pos = ctx->stack_base;
+ uint32_t clear_code = ctx->clear_code;
+ uint32_t current_entry = ctx->current_entry;
+ struct lzw_dictionary_entry * const table = ctx->table;
+
+ /* Get a new code from the input */
+ res = lzw__next_code(&ctx->input, ctx->current_code_size, &code_new);
+ if (res != LZW_OK) {
+ return res;
+ }
+
+ if (code_new == clear_code) {
+ /* Got Clear code */
+ return lzw__clear_codes(ctx, stack_pos_out);
+
+ } else if (code_new == ctx->eoi_code) {
+ /* Got End of Information code */
+ return LZW_EOI_CODE;
+ }
+
+ if (code_new > current_entry) {
+ /* Code is invalid */
+ return LZW_BAD_CODE;
+ } else if (code_new < current_entry) {
+ /* Code is in table */
+ code_out = code_new;
+ last_value = table[code_new].first_value;
+ } else {
+ /* Code not in table */
+ *stack_pos++ = ctx->previous_code_first;
+ code_out = ctx->previous_code;
+ last_value = ctx->previous_code_first;
+ }
+
+ /* Add to the dictionary, only if there's space */
+ if (current_entry < (1 << LZW_CODE_MAX)) {
+ struct lzw_dictionary_entry *entry = table + current_entry;
+ entry->last_value = last_value;
+ entry->first_value = ctx->previous_code_first;
+ entry->previous_entry = ctx->previous_code;
+ ctx->current_entry++;
+ }
+
+ /* Ensure code size is increased, if needed. */
+ if (current_entry == ctx->current_code_size_max) {
+ if (ctx->current_code_size < LZW_CODE_MAX) {
+ ctx->current_code_size++;
+ ctx->current_code_size_max =
+ (1 << ctx->current_code_size) - 1;
+ }
+ }
+
+ ctx->previous_code_first = table[code_new].first_value;
+ ctx->previous_code = code_new;
+
+ /* Put rest of data for this code on output stack.
+ * Note, in the case of "code not in table", the last entry of the
+ * current code has already been placed on the stack above. */
+ while (code_out > clear_code) {
+ struct lzw_dictionary_entry *entry = table + code_out;
+ *stack_pos++ = entry->last_value;
+ code_out = entry->previous_entry;
+ }
+ *stack_pos++ = table[code_out].last_value;
+
+ *stack_pos_out = stack_pos;
+ return LZW_OK;
+}
diff --git a/src/lzw.h b/src/lzw.h
new file mode 100644
index 0000000..385b425
--- /dev/null
+++ b/src/lzw.h
@@ -0,0 +1,105 @@
+/*
+ * This file is part of NetSurf's LibNSGIF, http://www.netsurf-browser.org/
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * Copyright 2017 Michael Drake <michael.drake(a)codethink.co.uk>
+ */
+
+#ifndef LZW_H_
+#define LZW_H_
+
+/**
+ * \file
+ * \brief LZW decompression (interface)
+ *
+ * Decoder for GIF LZW data.
+ */
+
+
+/** 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 */
+ LZW_OK_EOD, /**< Success; reached zero-length sub-block */
+ LZW_NO_MEM, /**< Error: Out of memory */
+ LZW_NO_DATA, /**< Error: Out of data */
+ LZW_EOI_CODE, /**< Error: End of Information code */
+ LZW_BAD_ICODE, /**< Error: Bad initial LZW code */
+ LZW_BAD_CODE, /**< Error: Bad LZW code */
+} lzw_result;
+
+
+/**
+ * Create an LZW decompression context.
+ *
+ * \param[out] ctx Returns an LZW decompression context. Caller owned,
+ * free with lzw_context_destroy().
+ * \return LZW_OK on success, or appropriate error code otherwise.
+ */
+lzw_result lzw_context_create(
+ struct lzw_ctx **ctx);
+
+/**
+ * Destroy an LZW decompression context.
+ *
+ * \param[in] ctx The LZW decompression context to destroy.
+ */
+void lzw_context_destroy(
+ struct lzw_ctx *ctx);
+
+/**
+ * Initialise an LZW decompression context for decoding.
+ *
+ * Caller owns neither `stack_base_out` or `stack_pos_out`.
+ *
+ * \param[in] ctx The LZW decompression context to initialise.
+ * \param[in] compressed_data The compressed data.
+ * \param[in] compressed_data_len Byte length of compressed data.
+ * \param[in] compressed_data_pos Start position in data. Must be position
+ * of a size byte at sub-block start.
+ * \param[in] code_size The initial LZW code size to use.
+ * \param[out] stack_base_out Returns base of decompressed data stack.
+ * \param[out] stack_pos_out Returns current stack position.
+ * There are `stack_pos_out - stack_base_out`
+ * current stack entries.
+ * \return LZW_OK on success, or appropriate error code otherwise.
+ */
+lzw_result lzw_decode_init(
+ struct lzw_ctx *ctx,
+ const uint8_t *compressed_data,
+ uint32_t compressed_data_len,
+ uint32_t compressed_data_pos,
+ uint8_t code_size,
+ const uint8_t ** const stack_base_out,
+ const uint8_t ** const stack_pos_out);
+
+/**
+ * Fill the LZW stack with decompressed data
+ *
+ * Ensure anything on the stack is used before calling this, as anything
+ * on the stack before this call will be trampled.
+ *
+ * Caller does not own `stack_pos_out`.
+ *
+ * \param[in] ctx LZW reading context, updated.
+ * \param[out] stack_pos_out Returns current stack position.
+ * Use with `stack_base_out` value from previous
+ * lzw_decode_init() call.
+ * There are `stack_pos_out - stack_base_out`
+ * current stack entries.
+ * \return LZW_OK on success, or appropriate error code otherwise.
+ */
+lzw_result lzw_decode(
+ struct lzw_ctx *ctx,
+ const uint8_t ** const stack_pos_out);
+
+
+#endif
diff --git a/test/decode_gif.c b/test/decode_gif.c
index aafe7f6..0ce53c7 100644
--- a/test/decode_gif.c
+++ b/test/decode_gif.c
@@ -135,19 +135,22 @@ static void warning(const char *context, gif_result code)
fprintf(stderr, "\n");
}
-static void write_ppm(FILE* fh, const char *name, gif_animation *gif)
+static void write_ppm(FILE* fh, const char *name, gif_animation *gif,
+ bool no_write)
{
unsigned int i;
gif_result code;
- fprintf(fh, "P3\n");
- fprintf(fh, "# %s\n", name);
- fprintf(fh, "# width %u \n", gif->width);
- fprintf(fh, "# height %u \n", gif->height);
- fprintf(fh, "# frame_count %u \n", gif->frame_count);
- fprintf(fh, "# frame_count_partial %u \n", gif->frame_count_partial);
- fprintf(fh, "# loop_count %u \n", gif->loop_count);
- fprintf(fh, "%u %u 256\n", gif->width, gif->height * gif->frame_count);
+ if (!no_write) {
+ fprintf(fh, "P3\n");
+ fprintf(fh, "# %s\n", name);
+ fprintf(fh, "# width %u \n", gif->width);
+ fprintf(fh, "# height %u \n", gif->height);
+ fprintf(fh, "# frame_count %u \n", gif->frame_count);
+ fprintf(fh, "# frame_count_partial %u \n", gif->frame_count_partial);
+ fprintf(fh, "# loop_count %u \n", gif->loop_count);
+ fprintf(fh, "%u %u 256\n", gif->width, gif->height * gif->frame_count);
+ }
/* decode the frames */
for (i = 0; i != gif->frame_count; i++) {
@@ -158,17 +161,19 @@ static void write_ppm(FILE* fh, const char *name, gif_animation *gif)
if (code != GIF_OK)
warning("gif_decode_frame", code);
- fprintf(fh, "# frame %u:\n", i);
- image = (unsigned char *) gif->frame_image;
- for (row = 0; row != gif->height; row++) {
- for (col = 0; col != gif->width; col++) {
- size_t z = (row * gif->width + col) * 4;
- fprintf(fh, "%u %u %u ",
- (unsigned char) image[z],
- (unsigned char) image[z + 1],
- (unsigned char) image[z + 2]);
+ if (!no_write) {
+ fprintf(fh, "# frame %u:\n", i);
+ image = (unsigned char *) gif->frame_image;
+ for (row = 0; row != gif->height; row++) {
+ for (col = 0; col != gif->width; col++) {
+ size_t z = (row * gif->width + col) * 4;
+ fprintf(fh, "%u %u %u ",
+ (unsigned char) image[z],
+ (unsigned char) image[z + 1],
+ (unsigned char) image[z + 2]);
+ }
+ fprintf(fh, "\n");
}
- fprintf(fh, "\n");
}
}
@@ -189,17 +194,28 @@ int main(int argc, char *argv[])
gif_result code;
unsigned char *data;
FILE *outf = stdout;
+ bool no_write = false;
if (argc < 2) {
fprintf(stderr, "Usage: %s image.gif [out]\n", argv[0]);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "If [out] is NOWRITE, the gif will be docoded "
+ "but not output.\n");
+ fprintf(stderr, "Otherwise [out] is an output filename.\n");
+ fprintf(stderr, "When [out] is unset, output is to stdout.\n");
+
return 1;
}
if (argc > 2) {
- outf = fopen(argv[2], "w+");
- if (outf == NULL) {
- fprintf(stderr, "Unable to open %s for writing\n", argv[2]);
- return 2;
+ if (strcmp(argv[2], "NOWRITE") == 0) {
+ no_write = true;
+ } else {
+ outf = fopen(argv[2], "w+");
+ if (outf == NULL) {
+ fprintf(stderr, "Unable to open %s for writing\n", argv[2]);
+ return 2;
+ }
}
}
@@ -218,9 +234,9 @@ int main(int argc, char *argv[])
}
} while (code != GIF_OK);
- write_ppm(outf, argv[1], &gif);
+ write_ppm(outf, argv[1], &gif, no_write);
- if (argc > 2) {
+ if (argc > 2 && !no_write) {
fclose(outf);
}
--
NetSurf GIF Decoder
5 years, 9 months
libnsgif: branch tlsa/lzw updated. release/0.1.4-19-gf9797e4
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/libnsgif.git/shortlog/f9797e4ae82090808036...
...commit http://git.netsurf-browser.org/libnsgif.git/commit/f9797e4ae82090808036b1...
...tree http://git.netsurf-browser.org/libnsgif.git/tree/f9797e4ae82090808036b1e1...
The branch, tlsa/lzw has been updated
discards 51074f364244b2a556ba72592940bc4a6015d4fe (commit)
discards 0ac1f4582eee1769db326c3cdaa8b82e58303a35 (commit)
discards 31526dd358255092b424efbcd90b17a1be25f000 (commit)
via f9797e4ae82090808036b1e1ec4318d0f1bdc456 (commit)
via 34f3a9a81824dcda5a3e1944d14b0273298ec0c3 (commit)
via 86ebd63cc1e9570cf247da26635375e31fc57d40 (commit)
This update added new revisions after undoing existing revisions. That is
to say, the old revision is not a strict subset of the new revision. This
situation occurs when you --force push a change and generate a repository
containing something like this:
* -- * -- B -- O -- O -- O (51074f364244b2a556ba72592940bc4a6015d4fe)
\
N -- N -- N (f9797e4ae82090808036b1e1ec4318d0f1bdc456)
When this happens we assume that you've already had alert emails for all
of the O revisions, and so we here report only the revisions in the N
branch from the common base, B.
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=f9797e4ae820908080...
commit f9797e4ae82090808036b1e1ec4318d0f1bdc456
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
New LZW decoder: Real-world fix; continue after dictionary is full.
diff --git a/src/libnsgif.c b/src/libnsgif.c
index fffbd94..6bf9956 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -566,7 +566,6 @@ static gif_result gif_error_from_lzw(lzw_result l_res)
[LZW_EOI_CODE] = GIF_FRAME_DATA_ERROR,
[LZW_BAD_ICODE] = GIF_FRAME_DATA_ERROR,
[LZW_BAD_CODE] = GIF_FRAME_DATA_ERROR,
- [LZW_BAD_DATA] = GIF_FRAME_DATA_ERROR,
};
return g_res[l_res];
}
diff --git a/src/lzw.c b/src/lzw.c
index b161799..6b7156e 100644
--- a/src/lzw.c
+++ b/src/lzw.c
@@ -301,7 +301,7 @@ lzw_result lzw_decode(struct lzw_ctx *ctx,
lzw_result res;
uint32_t code_new;
uint32_t code_out;
- struct lzw_dictionary_entry *entry;
+ uint8_t last_value;
uint8_t *stack_pos = ctx->stack_base;
uint32_t clear_code = ctx->clear_code;
uint32_t current_entry = ctx->current_entry;
@@ -322,28 +322,28 @@ lzw_result lzw_decode(struct lzw_ctx *ctx,
return LZW_EOI_CODE;
}
- if (current_entry >= (1 << LZW_CODE_MAX)) {
- /* No space in table for new entries, only Clear and
- * End of Information codes were allowed. */
- return LZW_BAD_DATA;
- }
-
- entry = table + current_entry;
if (code_new > current_entry) {
/* Code is invalid */
return LZW_BAD_CODE;
} else if (code_new < current_entry) {
/* Code is in table */
code_out = code_new;
- entry->last_value = table[code_new].first_value;
+ last_value = table[code_new].first_value;
} else {
/* Code not in table */
*stack_pos++ = ctx->previous_code_first;
code_out = ctx->previous_code;
- entry->last_value = ctx->previous_code_first;
+ last_value = ctx->previous_code_first;
+ }
+
+ /* Add to the dictionary, only if there's space */
+ if (current_entry < (1 << LZW_CODE_MAX)) {
+ struct lzw_dictionary_entry *entry = table + current_entry;
+ entry->last_value = last_value;
+ entry->first_value = ctx->previous_code_first;
+ entry->previous_entry = ctx->previous_code;
+ ctx->current_entry++;
}
- entry->first_value = ctx->previous_code_first;
- entry->previous_entry = ctx->previous_code;
/* Ensure code size is increased, if needed. */
if (current_entry == ctx->current_code_size_max) {
@@ -353,7 +353,6 @@ lzw_result lzw_decode(struct lzw_ctx *ctx,
(1 << ctx->current_code_size) - 1;
}
}
- ctx->current_entry++;
ctx->previous_code_first = table[code_new].first_value;
ctx->previous_code = code_new;
@@ -362,7 +361,7 @@ lzw_result lzw_decode(struct lzw_ctx *ctx,
* Note, in the case of "code not in table", the last entry of the
* current code has already been placed on the stack above. */
while (code_out > clear_code) {
- entry = table + code_out;
+ struct lzw_dictionary_entry *entry = table + code_out;
*stack_pos++ = entry->last_value;
code_out = entry->previous_entry;
}
diff --git a/src/lzw.h b/src/lzw.h
index 5812c0d..385b425 100644
--- a/src/lzw.h
+++ b/src/lzw.h
@@ -34,7 +34,6 @@ typedef enum lzw_result {
LZW_EOI_CODE, /**< Error: End of Information code */
LZW_BAD_ICODE, /**< Error: Bad initial LZW code */
LZW_BAD_CODE, /**< Error: Bad LZW code */
- LZW_BAD_DATA, /**< Error: Bad data */
} lzw_result;
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=34f3a9a81824dcda5a...
commit 34f3a9a81824dcda5a3e1944d14b0273298ec0c3
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
GIF decoding: Remove old LZW decoder and port library to new implementation.
diff --git a/include/libnsgif.h b/include/libnsgif.h
index fd92a0a..a819fec 100644
--- a/include/libnsgif.h
+++ b/include/libnsgif.h
@@ -93,6 +93,8 @@ typedef struct 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 */
diff --git a/src/Makefile b/src/Makefile
index 0fa4dc6..cb5d31f 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,4 +1,4 @@
# Sources
-DIR_SOURCES := libnsgif.c
+DIR_SOURCES := libnsgif.c lzw.c
include $(NSBUILD)/Makefile.subdir
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 998f8d7..fffbd94 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -8,6 +8,7 @@
*/
#include <stdbool.h>
+#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
@@ -15,6 +16,8 @@
#include "libnsgif.h"
#include "utils/log.h"
+#include "lzw.h"
+
/**
*
* \file
@@ -36,9 +39,6 @@
/** Internal flag that a frame is invalid/unprocessed */
#define GIF_INVALID_FRAME -1
-/** Maximum LZW bits available */
-#define GIF_MAX_LZW 12
-
/** Transparent colour */
#define GIF_TRANSPARENT_COLOUR 0x00
@@ -65,313 +65,6 @@
/** standard GIF header size */
#define GIF_STANDARD_HEADER_SIZE 13
-/** LZW parameters */
-struct lzw_s {
- unsigned char buf[4];
- const unsigned char *direct;
- int table[2][(1 << GIF_MAX_LZW)];
- unsigned char stack[(1 << GIF_MAX_LZW) * 2];
- unsigned char *stack_pointer;
-
- int code_size, set_code_size;
-
- int max_code, max_code_size;
-
- int clear_code, end_code;
-
- int curbit, lastbit, last_byte;
-
- int firstcode;
- int oldcode;
-
- bool zero_data_block;
- bool get_done;
-};
-
-
-/* General LZW values. They are shared for all GIFs being decoded, and thus we
- * can't handle progressive decoding efficiently without having the data for
- * each image which would use an extra 10Kb or so per GIF.
- */
-static struct lzw_s lzw_params = {
- .zero_data_block = false,
-};
-
-static struct lzw_s *lzw = &lzw_params;
-
-/**
- * get the next LZW code from the GIF
- *
- * reads codes from the input data stream coping with GIF data sub blocking
- *
- * \param[in] compressed_data LZW compressed data
- * \param[in] compressed_data_len Byte size of compressed_data
- * \param[in,out] compressed_data_pos Current position in compressed_data
- * updated on exit.
- * \param[in] code_size Number of bits in the current LZW code
- * \return The next code to process or error return code
- */
-static int gif_next_code(
- const uint8_t *compressed_data,
- uint32_t compressed_data_len,
- uint32_t *compressed_data_pos,
- int code_size)
-{
- int i, j, end, count, ret;
- uint32_t pos = *compressed_data_pos;
- const unsigned char *b;
- static const int maskTbl[16] = {
- 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f,
- 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff
- };
-
- end = lzw->curbit + code_size;
- if (end >= lzw->lastbit) {
- if (lzw->get_done) {
- return GIF_END_OF_FRAME;
- }
- lzw->buf[0] = lzw->direct[lzw->last_byte - 2];
- lzw->buf[1] = lzw->direct[lzw->last_byte - 1];
-
- /* get the next block */
- lzw->direct = compressed_data + pos;
- if (pos >= compressed_data_len) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
-
- count = lzw->direct[0];
- lzw->zero_data_block = (count == 0);
- if ((pos + count) >= compressed_data_len) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
-
- if (count == 0) {
- lzw->get_done = true;
- } else {
- if (pos + 3 >= compressed_data_len) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- lzw->direct -= 1;
- lzw->buf[2] = lzw->direct[2];
- lzw->buf[3] = lzw->direct[3];
- }
- pos += count + 1;
-
- /* update our variables */
- lzw->last_byte = 2 + count;
- lzw->curbit = (lzw->curbit - lzw->lastbit) + 16;
- lzw->lastbit = (2 + count) << 3;
- end = lzw->curbit + code_size;
- }
-
- i = lzw->curbit >> 3;
- if (i < 2) {
- b = lzw->buf;
- } else {
- b = lzw->direct;
- }
-
- ret = b[i];
- j = (end >> 3) - 1;
- if (i <= j) {
- ret |= (b[i + 1] << 8);
- if (i < j) {
- ret |= (b[i + 2] << 16);
- }
- }
- ret = (ret >> (lzw->curbit % 8)) & maskTbl[code_size];
- lzw->curbit += code_size;
-
- *compressed_data_pos = pos;
- return ret;
-}
-
-
-/**
- * Clear LZW code dictionary
- *
- * \param[in] compressed_data LZW compressed data
- * \param[in] compressed_data_len Byte size of compressed_data
- * \param[in,out] compressed_data_pos Current position in compressed_data
- * updated on exit.
- * \return GIF_OK or error code.
- */
-static gif_result gif_clear_codes_LZW(
- const uint8_t *compressed_data,
- uint32_t compressed_data_len,
- uint32_t *compressed_data_pos)
-{
- int i;
- int code;
-
- if (lzw->clear_code >= (1 << GIF_MAX_LZW)) {
- lzw->stack_pointer = lzw->stack;
- return GIF_FRAME_DATA_ERROR;
- }
-
- /* initialise our table */
- memset(lzw->table, 0x00, (1 << GIF_MAX_LZW) * 8);
- for (i = 0; i < lzw->clear_code; ++i) {
- lzw->table[1][i] = i;
- }
-
- /* reset LZW parameters */
- lzw->code_size = lzw->set_code_size + 1;
- lzw->max_code_size = lzw->clear_code << 1;
- lzw->max_code = lzw->clear_code + 2;
- lzw->stack_pointer = lzw->stack;
-
- /* process repeated clear codes */
- do {
- code = gif_next_code(compressed_data, compressed_data_len,
- compressed_data_pos, lzw->code_size);
- if (code < 0) {
- return code;
- }
- } while (code == lzw->clear_code);
- lzw->firstcode = lzw->oldcode = code;
-
- *lzw->stack_pointer++ = lzw->firstcode;
-
- return GIF_OK;
-}
-
-
-/**
- * Initialise LZW
- *
- * This initialises a LZW context ready to commence decompression.
- *
- * \param[in] compressed_data LZW compressed data
- * \param[in] compressed_data_len Byte size of compressed_data
- * \param[in,out] compressed_data_pos Current position in compressed_data
- * updated on exit.
- * \param[in] initial_code_size Size of codes used on clearing of code dictionary
- */
-static gif_result gif_initialise_LZW(
- const uint8_t *compressed_data,
- uint32_t compressed_data_len,
- uint32_t *compressed_data_pos,
- int initial_code_size)
-{
- lzw->set_code_size = initial_code_size;
- lzw->code_size = lzw->set_code_size + 1;
- lzw->clear_code = (1 << lzw->set_code_size);
- lzw->end_code = lzw->clear_code + 1;
- lzw->max_code_size = lzw->clear_code << 1;
- lzw->max_code = lzw->clear_code + 2;
- lzw->curbit = lzw->lastbit = 0;
- lzw->last_byte = 2;
- lzw->get_done = false;
- lzw->direct = lzw->buf;
-
- return gif_clear_codes_LZW(compressed_data,
- compressed_data_len,
- compressed_data_pos);
-}
-
-/**
- * fill the LZW stack with decompressed data
- *
- * \param[in] compressed_data LZW compressed data
- * \param[in] compressed_data_len Byte size of compressed_data
- * \param[in,out] compressed_data_pos Current position in compressed_data
- * updated on exit.
- * \return true on sucessful decode of the next LZW code else false.
- */
-static gif_result gif_next_LZW(
- const uint8_t *compressed_data,
- uint32_t compressed_data_len,
- uint32_t *compressed_data_pos)
-{
- int code, incode;
- int block_size;
- int new_code;
-
- code = gif_next_code(compressed_data, compressed_data_len,
- compressed_data_pos, lzw->code_size);
- if (code < 0) {
- return code;
- }
-
- if (code == lzw->clear_code) {
- return gif_clear_codes_LZW(compressed_data,
- compressed_data_len,
- compressed_data_pos);
- }
-
- if (code == lzw->end_code) {
- /* skip to the end of our data so multi-image GIFs work */
- if (lzw->zero_data_block) {
- return GIF_FRAME_DATA_ERROR;
- }
- block_size = 0;
- while (block_size != 1 &&
- *compressed_data_pos < compressed_data_len) {
- block_size = compressed_data[*compressed_data_pos] + 1;
- *compressed_data_pos += block_size;
- }
- return GIF_FRAME_DATA_ERROR;
- }
-
- incode = code;
- if (code >= lzw->max_code) {
- if (lzw->stack_pointer >= lzw->stack + ((1 << GIF_MAX_LZW) * 2)) {
- return GIF_FRAME_DATA_ERROR;
- }
- *lzw->stack_pointer++ = lzw->firstcode;
- code = lzw->oldcode;
- }
-
- /* The following loop is the most important in the GIF decoding cycle
- * as every single pixel passes through it.
- *
- * Note: our stack is always big enough to hold a complete decompressed
- * chunk.
- */
- while (code >= lzw->clear_code) {
- if (lzw->stack_pointer >= lzw->stack + ((1 << GIF_MAX_LZW) * 2) ||
- code >= (1 << GIF_MAX_LZW)) {
- return GIF_FRAME_DATA_ERROR;
- }
- *lzw->stack_pointer++ = lzw->table[1][code];
- new_code = lzw->table[0][code];
- if (new_code < lzw->clear_code) {
- code = new_code;
- break;
- }
-
- if (lzw->stack_pointer >= lzw->stack + ((1 << GIF_MAX_LZW) * 2) ||
- new_code >= (1 << GIF_MAX_LZW)) {
- return GIF_FRAME_DATA_ERROR;
- }
- *lzw->stack_pointer++ = lzw->table[1][new_code];
- code = lzw->table[0][new_code];
- if (code == new_code) {
- return GIF_FRAME_DATA_ERROR;
- }
- }
-
- if (lzw->stack_pointer >= lzw->stack + ((1 << GIF_MAX_LZW) * 2)) {
- return GIF_FRAME_DATA_ERROR;
- }
- *lzw->stack_pointer++ = lzw->firstcode = lzw->table[1][code];
-
- code = lzw->max_code;
- if (code < (1 << GIF_MAX_LZW)) {
- lzw->table[0][code] = lzw->oldcode;
- lzw->table[1][code] = lzw->firstcode;
- ++lzw->max_code;
- if ((lzw->max_code >= lzw->max_code_size) &&
- (lzw->max_code_size < (1 << GIF_MAX_LZW))) {
- lzw->max_code_size = lzw->max_code_size << 1;
- ++lzw->code_size;
- }
- }
- lzw->oldcode = incode;
- return GIF_OK;
-}
-
/**
* Updates the sprite memory size
@@ -725,7 +418,7 @@ static gif_result gif_initialise_frame(gif_animation *gif)
if (gif_bytes < 1) {
return GIF_INSUFFICIENT_FRAME_DATA;
}
- if (gif_data[0] > GIF_MAX_LZW) {
+ if (gif_data[0] > LZW_CODE_MAX) {
return GIF_DATA_ERROR;
}
@@ -863,7 +556,20 @@ static unsigned int gif_interlaced_line(int height, int y) {
}
-
+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,
+ [LZW_BAD_DATA] = GIF_FRAME_DATA_ERROR,
+ };
+ return g_res[l_res];
+}
/**
@@ -878,7 +584,6 @@ gif_internal_decode_frame(gif_animation *gif,
unsigned int frame,
bool clear_image)
{
- gif_result res;
unsigned int index = 0;
unsigned char *gif_data, *gif_end;
int gif_bytes;
@@ -1038,6 +743,10 @@ gif_internal_decode_frame(gif_animation *gif,
/* If we are clearing the image we just clear, if not decode */
if (!clear_image) {
+ lzw_result res;
+ const uint8_t *stack_base;
+ const uint8_t *stack_pos;
+
/* Ensure we have enough data for a 1-byte LZW code size +
* 1-byte gif trailer
*/
@@ -1114,10 +823,11 @@ gif_internal_decode_frame(gif_animation *gif,
gif->buffer_position = (gif_data - gif->gif_data) + 1;
/* Initialise the LZW decoding */
- res = gif_initialise_LZW(gif->gif_data, gif->buffer_size,
- &gif->buffer_position, gif_data[0]);
- if (res != GIF_OK) {
- return res;
+ res = lzw_decode_init(gif->lzw_ctx, gif->gif_data,
+ gif->buffer_size, gif->buffer_position,
+ gif_data[0], &stack_base, &stack_pos);
+ if (res != LZW_OK) {
+ return gif_error_from_lzw(res);
}
/* Decompress the data */
@@ -1135,14 +845,14 @@ gif_internal_decode_frame(gif_animation *gif,
*/
x = width;
while (x > 0) {
- burst_bytes = (lzw->stack_pointer - lzw->stack);
+ burst_bytes = (stack_pos - stack_base);
if (burst_bytes > 0) {
if (burst_bytes > x) {
burst_bytes = x;
}
x -= burst_bytes;
while (burst_bytes-- > 0) {
- colour = *--lzw->stack_pointer;
+ colour = *--stack_pos;
if (((gif->frames[frame].transparency) &&
(colour != gif->frames[frame].transparency_index)) ||
(!gif->frames[frame].transparency)) {
@@ -1151,15 +861,13 @@ gif_internal_decode_frame(gif_animation *gif,
frame_scanline++;
}
} else {
- res = gif_next_LZW(gif->gif_data,
- gif->buffer_size,
- &gif->buffer_position);
- if (res != GIF_OK) {
+ res = lzw_decode(gif->lzw_ctx, &stack_pos);
+ if (res != LZW_OK) {
/* Unexpected end of frame, try to recover */
- if (res == GIF_END_OF_FRAME) {
+ if (res == LZW_OK_EOD) {
return_value = GIF_OK;
} else {
- return_value = res;
+ return_value = gif_error_from_lzw(res);
}
goto gif_decode_frame_exit;
}
@@ -1230,6 +938,14 @@ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *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);
+ }
+ }
+
/* Check for sufficient data to be a GIF (6-byte header + 7-byte
* logical screen descriptor)
*/
@@ -1448,4 +1164,7 @@ void gif_finalise(gif_animation *gif)
gif->local_colour_table = NULL;
free(gif->global_colour_table);
gif->global_colour_table = NULL;
+
+ lzw_context_destroy(gif->lzw_ctx);
+ gif->lzw_ctx = NULL;
}
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=86ebd63cc1e9570cf2...
commit 86ebd63cc1e9570cf247da26635375e31fc57d40
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
New LZW decoder: Add client calls to initialise LZW, and perform decode.
diff --git a/src/lzw.c b/src/lzw.c
index 4f7ff8f..b161799 100644
--- a/src/lzw.c
+++ b/src/lzw.c
@@ -42,11 +42,45 @@ struct lzw_read_ctx {
};
/**
+ * LZW dictionary entry.
+ *
+ * Records in the dictionary are composed of 1 or more entries.
+ * Entries point to previous entries which can be followed to compose
+ * the complete record. To compose the record in reverse order, take
+ * the `last_value` from each entry, and move to the previous entry.
+ * If the previous_entry's index is < the current clear_code, then it
+ * is the last entry in the record.
+ */
+struct lzw_dictionary_entry {
+ uint8_t last_value; /**< Last value for record ending at entry. */
+ uint8_t first_value; /**< First value for entry's record. */
+ uint16_t previous_entry; /**< Offset in dictionary to previous entry. */
+};
+
+/**
* LZW decompression context.
*/
struct lzw_ctx {
/** Input reading context */
struct lzw_read_ctx input;
+
+ uint32_t previous_code; /**< Code read from input previously. */
+ uint32_t previous_code_first; /**< First value of previous code. */
+
+ uint32_t initial_code_size; /**< Starting LZW code size. */
+ uint32_t current_code_size; /**< Current LZW code size. */
+ uint32_t current_code_size_max; /**< Max code value for current size. */
+
+ uint32_t clear_code; /**< Special Clear code value */
+ uint32_t eoi_code; /**< Special End of Information code value */
+
+ uint32_t current_entry; /**< Next position in table to fill. */
+
+ /** Output value stack. */
+ uint8_t stack_base[1 << LZW_CODE_MAX];
+
+ /** LZW decode dictionary. Generated during decode. */
+ struct lzw_dictionary_entry table[1 << LZW_CODE_MAX];
};
@@ -175,3 +209,165 @@ static inline lzw_result lzw__next_code(
*code_out = (code >> current_bit) & ((1 << code_size) - 1);
return LZW_OK;
}
+
+
+/**
+ * Clear LZW code dictionary.
+ *
+ * \param[in] ctx LZW reading context, updated.
+ * \param[out] stack_pos_out Returns current stack position.
+ * \return LZW_OK or error code.
+ */
+static lzw_result lzw__clear_codes(
+ struct lzw_ctx *ctx,
+ const uint8_t ** const stack_pos_out)
+{
+ uint32_t code;
+ uint8_t *stack_pos;
+
+ /* Reset dictionary building context */
+ ctx->current_code_size = ctx->initial_code_size + 1;
+ ctx->current_code_size_max = (1 << ctx->current_code_size) - 1;;
+ ctx->current_entry = (1 << ctx->initial_code_size) + 2;
+
+ /* There might be a sequence of clear codes, so process them all */
+ do {
+ lzw_result res = lzw__next_code(&ctx->input,
+ ctx->current_code_size, &code);
+ if (res != LZW_OK) {
+ return res;
+ }
+ } while (code == ctx->clear_code);
+
+ /* The initial code must be from the initial dictionary. */
+ if (code > ctx->clear_code) {
+ return LZW_BAD_ICODE;
+ }
+
+ /* Record this initial code as "previous" code, needed during decode. */
+ ctx->previous_code = code;
+ ctx->previous_code_first = code;
+
+ /* Reset the stack, and add first non-clear code added as first item. */
+ stack_pos = ctx->stack_base;
+ *stack_pos++ = code;
+
+ *stack_pos_out = stack_pos;
+ return LZW_OK;
+}
+
+
+/* Exported function, documented in lzw.h */
+lzw_result lzw_decode_init(
+ struct lzw_ctx *ctx,
+ const uint8_t *compressed_data,
+ uint32_t compressed_data_len,
+ uint32_t compressed_data_pos,
+ uint8_t code_size,
+ const uint8_t ** const stack_base_out,
+ const uint8_t ** const stack_pos_out)
+{
+ struct lzw_dictionary_entry *table = ctx->table;
+
+ /* Initialise the input reading context */
+ ctx->input.data = compressed_data;
+ ctx->input.data_len = compressed_data_len;
+ ctx->input.data_sb_next = compressed_data_pos;
+
+ ctx->input.sb_bit = 0;
+ ctx->input.sb_bit_count = 0;
+
+ /* Initialise the dictionary building context */
+ ctx->initial_code_size = code_size;
+
+ ctx->clear_code = (1 << code_size) + 0;
+ ctx->eoi_code = (1 << code_size) + 1;
+
+ /* Initialise the standard dictionary entries */
+ for (uint32_t i = 0; i < ctx->clear_code; ++i) {
+ table[i].first_value = i;
+ table[i].last_value = i;
+ }
+
+ *stack_base_out = ctx->stack_base;
+ return lzw__clear_codes(ctx, stack_pos_out);
+}
+
+
+/* Exported function, documented in lzw.h */
+lzw_result lzw_decode(struct lzw_ctx *ctx,
+ const uint8_t ** const stack_pos_out)
+{
+ lzw_result res;
+ uint32_t code_new;
+ uint32_t code_out;
+ struct lzw_dictionary_entry *entry;
+ uint8_t *stack_pos = ctx->stack_base;
+ uint32_t clear_code = ctx->clear_code;
+ uint32_t current_entry = ctx->current_entry;
+ struct lzw_dictionary_entry * const table = ctx->table;
+
+ /* Get a new code from the input */
+ res = lzw__next_code(&ctx->input, ctx->current_code_size, &code_new);
+ if (res != LZW_OK) {
+ return res;
+ }
+
+ if (code_new == clear_code) {
+ /* Got Clear code */
+ return lzw__clear_codes(ctx, stack_pos_out);
+
+ } else if (code_new == ctx->eoi_code) {
+ /* Got End of Information code */
+ return LZW_EOI_CODE;
+ }
+
+ if (current_entry >= (1 << LZW_CODE_MAX)) {
+ /* No space in table for new entries, only Clear and
+ * End of Information codes were allowed. */
+ return LZW_BAD_DATA;
+ }
+
+ entry = table + current_entry;
+ if (code_new > current_entry) {
+ /* Code is invalid */
+ return LZW_BAD_CODE;
+ } else if (code_new < current_entry) {
+ /* Code is in table */
+ code_out = code_new;
+ entry->last_value = table[code_new].first_value;
+ } else {
+ /* Code not in table */
+ *stack_pos++ = ctx->previous_code_first;
+ code_out = ctx->previous_code;
+ entry->last_value = ctx->previous_code_first;
+ }
+ entry->first_value = ctx->previous_code_first;
+ entry->previous_entry = ctx->previous_code;
+
+ /* Ensure code size is increased, if needed. */
+ if (current_entry == ctx->current_code_size_max) {
+ if (ctx->current_code_size < LZW_CODE_MAX) {
+ ctx->current_code_size++;
+ ctx->current_code_size_max =
+ (1 << ctx->current_code_size) - 1;
+ }
+ }
+ ctx->current_entry++;
+
+ ctx->previous_code_first = table[code_new].first_value;
+ ctx->previous_code = code_new;
+
+ /* Put rest of data for this code on output stack.
+ * Note, in the case of "code not in table", the last entry of the
+ * current code has already been placed on the stack above. */
+ while (code_out > clear_code) {
+ entry = table + code_out;
+ *stack_pos++ = entry->last_value;
+ code_out = entry->previous_entry;
+ }
+ *stack_pos++ = table[code_out].last_value;
+
+ *stack_pos_out = stack_pos;
+ return LZW_OK;
+}
diff --git a/src/lzw.h b/src/lzw.h
index a908414..5812c0d 100644
--- a/src/lzw.h
+++ b/src/lzw.h
@@ -17,6 +17,10 @@
*/
+/** Maximum LZW code size in bits */
+#define LZW_CODE_MAX 12
+
+
/* Declare lzw internal context structure */
struct lzw_ctx;
@@ -27,6 +31,10 @@ typedef enum lzw_result {
LZW_OK_EOD, /**< Success; reached zero-length sub-block */
LZW_NO_MEM, /**< Error: Out of memory */
LZW_NO_DATA, /**< Error: Out of data */
+ LZW_EOI_CODE, /**< Error: End of Information code */
+ LZW_BAD_ICODE, /**< Error: Bad initial LZW code */
+ LZW_BAD_CODE, /**< Error: Bad LZW code */
+ LZW_BAD_DATA, /**< Error: Bad data */
} lzw_result;
@@ -48,5 +56,51 @@ lzw_result lzw_context_create(
void lzw_context_destroy(
struct lzw_ctx *ctx);
+/**
+ * Initialise an LZW decompression context for decoding.
+ *
+ * Caller owns neither `stack_base_out` or `stack_pos_out`.
+ *
+ * \param[in] ctx The LZW decompression context to initialise.
+ * \param[in] compressed_data The compressed data.
+ * \param[in] compressed_data_len Byte length of compressed data.
+ * \param[in] compressed_data_pos Start position in data. Must be position
+ * of a size byte at sub-block start.
+ * \param[in] code_size The initial LZW code size to use.
+ * \param[out] stack_base_out Returns base of decompressed data stack.
+ * \param[out] stack_pos_out Returns current stack position.
+ * There are `stack_pos_out - stack_base_out`
+ * current stack entries.
+ * \return LZW_OK on success, or appropriate error code otherwise.
+ */
+lzw_result lzw_decode_init(
+ struct lzw_ctx *ctx,
+ const uint8_t *compressed_data,
+ uint32_t compressed_data_len,
+ uint32_t compressed_data_pos,
+ uint8_t code_size,
+ const uint8_t ** const stack_base_out,
+ const uint8_t ** const stack_pos_out);
+
+/**
+ * Fill the LZW stack with decompressed data
+ *
+ * Ensure anything on the stack is used before calling this, as anything
+ * on the stack before this call will be trampled.
+ *
+ * Caller does not own `stack_pos_out`.
+ *
+ * \param[in] ctx LZW reading context, updated.
+ * \param[out] stack_pos_out Returns current stack position.
+ * Use with `stack_base_out` value from previous
+ * lzw_decode_init() call.
+ * There are `stack_pos_out - stack_base_out`
+ * current stack entries.
+ * \return LZW_OK on success, or appropriate error code otherwise.
+ */
+lzw_result lzw_decode(
+ struct lzw_ctx *ctx,
+ const uint8_t ** const stack_pos_out);
+
#endif
-----------------------------------------------------------------------
Summary of changes:
src/libnsgif.c | 1 -
src/lzw.c | 1 -
src/lzw.h | 1 -
3 files changed, 3 deletions(-)
diff --git a/src/libnsgif.c b/src/libnsgif.c
index fffbd94..6bf9956 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -566,7 +566,6 @@ static gif_result gif_error_from_lzw(lzw_result l_res)
[LZW_EOI_CODE] = GIF_FRAME_DATA_ERROR,
[LZW_BAD_ICODE] = GIF_FRAME_DATA_ERROR,
[LZW_BAD_CODE] = GIF_FRAME_DATA_ERROR,
- [LZW_BAD_DATA] = GIF_FRAME_DATA_ERROR,
};
return g_res[l_res];
}
diff --git a/src/lzw.c b/src/lzw.c
index 4877ed7..6b7156e 100644
--- a/src/lzw.c
+++ b/src/lzw.c
@@ -317,7 +317,6 @@ lzw_result lzw_decode(struct lzw_ctx *ctx,
/* Got Clear code */
return lzw__clear_codes(ctx, stack_pos_out);
-
} else if (code_new == ctx->eoi_code) {
/* Got End of Information code */
return LZW_EOI_CODE;
diff --git a/src/lzw.h b/src/lzw.h
index 5812c0d..385b425 100644
--- a/src/lzw.h
+++ b/src/lzw.h
@@ -34,7 +34,6 @@ typedef enum lzw_result {
LZW_EOI_CODE, /**< Error: End of Information code */
LZW_BAD_ICODE, /**< Error: Bad initial LZW code */
LZW_BAD_CODE, /**< Error: Bad LZW code */
- LZW_BAD_DATA, /**< Error: Bad data */
} lzw_result;
--
NetSurf GIF Decoder
5 years, 9 months
libnsgif: branch tlsa/lzw updated. release/0.1.4-19-g51074f3
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/libnsgif.git/shortlog/51074f364244b2a556ba...
...commit http://git.netsurf-browser.org/libnsgif.git/commit/51074f364244b2a556ba72...
...tree http://git.netsurf-browser.org/libnsgif.git/tree/51074f364244b2a556ba7259...
The branch, tlsa/lzw has been updated
via 51074f364244b2a556ba72592940bc4a6015d4fe (commit)
from 0ac1f4582eee1769db326c3cdaa8b82e58303a35 (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=51074f364244b2a556...
commit 51074f364244b2a556ba72592940bc4a6015d4fe
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
New LZW decoder: Real-world fix; continue after dictionary is full.
diff --git a/src/lzw.c b/src/lzw.c
index baeba0a..4877ed7 100644
--- a/src/lzw.c
+++ b/src/lzw.c
@@ -301,7 +301,7 @@ lzw_result lzw_decode(struct lzw_ctx *ctx,
lzw_result res;
uint32_t code_new;
uint32_t code_out;
- struct lzw_dictionary_entry *entry;
+ uint8_t last_value;
uint8_t *stack_pos = ctx->stack_base;
uint32_t clear_code = ctx->clear_code;
uint32_t current_entry = ctx->current_entry;
@@ -323,28 +323,28 @@ lzw_result lzw_decode(struct lzw_ctx *ctx,
return LZW_EOI_CODE;
}
- if (current_entry >= (1 << LZW_CODE_MAX)) {
- /* No space in table for new entries, only Clear and
- * End of Information codes were allowed. */
- return LZW_BAD_DATA;
- }
-
- entry = table + current_entry;
if (code_new > current_entry) {
/* Code is invalid */
return LZW_BAD_CODE;
} else if (code_new < current_entry) {
/* Code is in table */
code_out = code_new;
- entry->last_value = table[code_new].first_value;
+ last_value = table[code_new].first_value;
} else {
/* Code not in table */
*stack_pos++ = ctx->previous_code_first;
code_out = ctx->previous_code;
- entry->last_value = ctx->previous_code_first;
+ last_value = ctx->previous_code_first;
+ }
+
+ /* Add to the dictionary, only if there's space */
+ if (current_entry < (1 << LZW_CODE_MAX)) {
+ struct lzw_dictionary_entry *entry = table + current_entry;
+ entry->last_value = last_value;
+ entry->first_value = ctx->previous_code_first;
+ entry->previous_entry = ctx->previous_code;
+ ctx->current_entry++;
}
- entry->first_value = ctx->previous_code_first;
- entry->previous_entry = ctx->previous_code;
/* Ensure code size is increased, if needed. */
if (current_entry == ctx->current_code_size_max) {
@@ -354,7 +354,6 @@ lzw_result lzw_decode(struct lzw_ctx *ctx,
(1 << ctx->current_code_size) - 1;
}
}
- ctx->current_entry++;
ctx->previous_code_first = table[code_new].first_value;
ctx->previous_code = code_new;
@@ -363,7 +362,7 @@ lzw_result lzw_decode(struct lzw_ctx *ctx,
* Note, in the case of "code not in table", the last entry of the
* current code has already been placed on the stack above. */
while (code_out > clear_code) {
- entry = table + code_out;
+ struct lzw_dictionary_entry *entry = table + code_out;
*stack_pos++ = entry->last_value;
code_out = entry->previous_entry;
}
-----------------------------------------------------------------------
Summary of changes:
src/lzw.c | 27 +++++++++++++--------------
1 file changed, 13 insertions(+), 14 deletions(-)
diff --git a/src/lzw.c b/src/lzw.c
index baeba0a..4877ed7 100644
--- a/src/lzw.c
+++ b/src/lzw.c
@@ -301,7 +301,7 @@ lzw_result lzw_decode(struct lzw_ctx *ctx,
lzw_result res;
uint32_t code_new;
uint32_t code_out;
- struct lzw_dictionary_entry *entry;
+ uint8_t last_value;
uint8_t *stack_pos = ctx->stack_base;
uint32_t clear_code = ctx->clear_code;
uint32_t current_entry = ctx->current_entry;
@@ -323,28 +323,28 @@ lzw_result lzw_decode(struct lzw_ctx *ctx,
return LZW_EOI_CODE;
}
- if (current_entry >= (1 << LZW_CODE_MAX)) {
- /* No space in table for new entries, only Clear and
- * End of Information codes were allowed. */
- return LZW_BAD_DATA;
- }
-
- entry = table + current_entry;
if (code_new > current_entry) {
/* Code is invalid */
return LZW_BAD_CODE;
} else if (code_new < current_entry) {
/* Code is in table */
code_out = code_new;
- entry->last_value = table[code_new].first_value;
+ last_value = table[code_new].first_value;
} else {
/* Code not in table */
*stack_pos++ = ctx->previous_code_first;
code_out = ctx->previous_code;
- entry->last_value = ctx->previous_code_first;
+ last_value = ctx->previous_code_first;
+ }
+
+ /* Add to the dictionary, only if there's space */
+ if (current_entry < (1 << LZW_CODE_MAX)) {
+ struct lzw_dictionary_entry *entry = table + current_entry;
+ entry->last_value = last_value;
+ entry->first_value = ctx->previous_code_first;
+ entry->previous_entry = ctx->previous_code;
+ ctx->current_entry++;
}
- entry->first_value = ctx->previous_code_first;
- entry->previous_entry = ctx->previous_code;
/* Ensure code size is increased, if needed. */
if (current_entry == ctx->current_code_size_max) {
@@ -354,7 +354,6 @@ lzw_result lzw_decode(struct lzw_ctx *ctx,
(1 << ctx->current_code_size) - 1;
}
}
- ctx->current_entry++;
ctx->previous_code_first = table[code_new].first_value;
ctx->previous_code = code_new;
@@ -363,7 +362,7 @@ lzw_result lzw_decode(struct lzw_ctx *ctx,
* Note, in the case of "code not in table", the last entry of the
* current code has already been placed on the stack above. */
while (code_out > clear_code) {
- entry = table + code_out;
+ struct lzw_dictionary_entry *entry = table + code_out;
*stack_pos++ = entry->last_value;
code_out = entry->previous_entry;
}
--
NetSurf GIF Decoder
5 years, 9 months
libnsgif: branch tlsa/lzw updated. release/0.1.4-18-g0ac1f45
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/libnsgif.git/shortlog/0ac1f4582eee1769db32...
...commit http://git.netsurf-browser.org/libnsgif.git/commit/0ac1f4582eee1769db326c...
...tree http://git.netsurf-browser.org/libnsgif.git/tree/0ac1f4582eee1769db326c3c...
The branch, tlsa/lzw has been updated
discards be548274229f3541f779557d7109e68881f2bfab (commit)
discards a7d46c720d4328ae840c43903bde11404280ec97 (commit)
discards e7efcc3c309c0e7cdce75d108b180fb3b3c7de96 (commit)
discards 116e7850b55124d6ba85cef3593a4fba72f8c2f9 (commit)
via 0ac1f4582eee1769db326c3cdaa8b82e58303a35 (commit)
via 31526dd358255092b424efbcd90b17a1be25f000 (commit)
via 960c3b6d2229dcc7cc4a4ef60838329b78880e66 (commit)
via 8c42f9f934005ba40645ba326b74c8f8b0675df2 (commit)
This update added new revisions after undoing existing revisions. That is
to say, the old revision is not a strict subset of the new revision. This
situation occurs when you --force push a change and generate a repository
containing something like this:
* -- * -- B -- O -- O -- O (be548274229f3541f779557d7109e68881f2bfab)
\
N -- N -- N (0ac1f4582eee1769db326c3cdaa8b82e58303a35)
When this happens we assume that you've already had alert emails for all
of the O revisions, and so we here report only the revisions in the N
branch from the common base, B.
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=0ac1f4582eee1769db...
commit 0ac1f4582eee1769db326c3cdaa8b82e58303a35
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
GIF decoding: Remove old LZW decoder and port library to new implementation.
diff --git a/include/libnsgif.h b/include/libnsgif.h
index fd92a0a..a819fec 100644
--- a/include/libnsgif.h
+++ b/include/libnsgif.h
@@ -93,6 +93,8 @@ typedef struct 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 */
diff --git a/src/Makefile b/src/Makefile
index 0fa4dc6..cb5d31f 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,4 +1,4 @@
# Sources
-DIR_SOURCES := libnsgif.c
+DIR_SOURCES := libnsgif.c lzw.c
include $(NSBUILD)/Makefile.subdir
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 998f8d7..fffbd94 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -8,6 +8,7 @@
*/
#include <stdbool.h>
+#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
@@ -15,6 +16,8 @@
#include "libnsgif.h"
#include "utils/log.h"
+#include "lzw.h"
+
/**
*
* \file
@@ -36,9 +39,6 @@
/** Internal flag that a frame is invalid/unprocessed */
#define GIF_INVALID_FRAME -1
-/** Maximum LZW bits available */
-#define GIF_MAX_LZW 12
-
/** Transparent colour */
#define GIF_TRANSPARENT_COLOUR 0x00
@@ -65,313 +65,6 @@
/** standard GIF header size */
#define GIF_STANDARD_HEADER_SIZE 13
-/** LZW parameters */
-struct lzw_s {
- unsigned char buf[4];
- const unsigned char *direct;
- int table[2][(1 << GIF_MAX_LZW)];
- unsigned char stack[(1 << GIF_MAX_LZW) * 2];
- unsigned char *stack_pointer;
-
- int code_size, set_code_size;
-
- int max_code, max_code_size;
-
- int clear_code, end_code;
-
- int curbit, lastbit, last_byte;
-
- int firstcode;
- int oldcode;
-
- bool zero_data_block;
- bool get_done;
-};
-
-
-/* General LZW values. They are shared for all GIFs being decoded, and thus we
- * can't handle progressive decoding efficiently without having the data for
- * each image which would use an extra 10Kb or so per GIF.
- */
-static struct lzw_s lzw_params = {
- .zero_data_block = false,
-};
-
-static struct lzw_s *lzw = &lzw_params;
-
-/**
- * get the next LZW code from the GIF
- *
- * reads codes from the input data stream coping with GIF data sub blocking
- *
- * \param[in] compressed_data LZW compressed data
- * \param[in] compressed_data_len Byte size of compressed_data
- * \param[in,out] compressed_data_pos Current position in compressed_data
- * updated on exit.
- * \param[in] code_size Number of bits in the current LZW code
- * \return The next code to process or error return code
- */
-static int gif_next_code(
- const uint8_t *compressed_data,
- uint32_t compressed_data_len,
- uint32_t *compressed_data_pos,
- int code_size)
-{
- int i, j, end, count, ret;
- uint32_t pos = *compressed_data_pos;
- const unsigned char *b;
- static const int maskTbl[16] = {
- 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f,
- 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff
- };
-
- end = lzw->curbit + code_size;
- if (end >= lzw->lastbit) {
- if (lzw->get_done) {
- return GIF_END_OF_FRAME;
- }
- lzw->buf[0] = lzw->direct[lzw->last_byte - 2];
- lzw->buf[1] = lzw->direct[lzw->last_byte - 1];
-
- /* get the next block */
- lzw->direct = compressed_data + pos;
- if (pos >= compressed_data_len) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
-
- count = lzw->direct[0];
- lzw->zero_data_block = (count == 0);
- if ((pos + count) >= compressed_data_len) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
-
- if (count == 0) {
- lzw->get_done = true;
- } else {
- if (pos + 3 >= compressed_data_len) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- lzw->direct -= 1;
- lzw->buf[2] = lzw->direct[2];
- lzw->buf[3] = lzw->direct[3];
- }
- pos += count + 1;
-
- /* update our variables */
- lzw->last_byte = 2 + count;
- lzw->curbit = (lzw->curbit - lzw->lastbit) + 16;
- lzw->lastbit = (2 + count) << 3;
- end = lzw->curbit + code_size;
- }
-
- i = lzw->curbit >> 3;
- if (i < 2) {
- b = lzw->buf;
- } else {
- b = lzw->direct;
- }
-
- ret = b[i];
- j = (end >> 3) - 1;
- if (i <= j) {
- ret |= (b[i + 1] << 8);
- if (i < j) {
- ret |= (b[i + 2] << 16);
- }
- }
- ret = (ret >> (lzw->curbit % 8)) & maskTbl[code_size];
- lzw->curbit += code_size;
-
- *compressed_data_pos = pos;
- return ret;
-}
-
-
-/**
- * Clear LZW code dictionary
- *
- * \param[in] compressed_data LZW compressed data
- * \param[in] compressed_data_len Byte size of compressed_data
- * \param[in,out] compressed_data_pos Current position in compressed_data
- * updated on exit.
- * \return GIF_OK or error code.
- */
-static gif_result gif_clear_codes_LZW(
- const uint8_t *compressed_data,
- uint32_t compressed_data_len,
- uint32_t *compressed_data_pos)
-{
- int i;
- int code;
-
- if (lzw->clear_code >= (1 << GIF_MAX_LZW)) {
- lzw->stack_pointer = lzw->stack;
- return GIF_FRAME_DATA_ERROR;
- }
-
- /* initialise our table */
- memset(lzw->table, 0x00, (1 << GIF_MAX_LZW) * 8);
- for (i = 0; i < lzw->clear_code; ++i) {
- lzw->table[1][i] = i;
- }
-
- /* reset LZW parameters */
- lzw->code_size = lzw->set_code_size + 1;
- lzw->max_code_size = lzw->clear_code << 1;
- lzw->max_code = lzw->clear_code + 2;
- lzw->stack_pointer = lzw->stack;
-
- /* process repeated clear codes */
- do {
- code = gif_next_code(compressed_data, compressed_data_len,
- compressed_data_pos, lzw->code_size);
- if (code < 0) {
- return code;
- }
- } while (code == lzw->clear_code);
- lzw->firstcode = lzw->oldcode = code;
-
- *lzw->stack_pointer++ = lzw->firstcode;
-
- return GIF_OK;
-}
-
-
-/**
- * Initialise LZW
- *
- * This initialises a LZW context ready to commence decompression.
- *
- * \param[in] compressed_data LZW compressed data
- * \param[in] compressed_data_len Byte size of compressed_data
- * \param[in,out] compressed_data_pos Current position in compressed_data
- * updated on exit.
- * \param[in] initial_code_size Size of codes used on clearing of code dictionary
- */
-static gif_result gif_initialise_LZW(
- const uint8_t *compressed_data,
- uint32_t compressed_data_len,
- uint32_t *compressed_data_pos,
- int initial_code_size)
-{
- lzw->set_code_size = initial_code_size;
- lzw->code_size = lzw->set_code_size + 1;
- lzw->clear_code = (1 << lzw->set_code_size);
- lzw->end_code = lzw->clear_code + 1;
- lzw->max_code_size = lzw->clear_code << 1;
- lzw->max_code = lzw->clear_code + 2;
- lzw->curbit = lzw->lastbit = 0;
- lzw->last_byte = 2;
- lzw->get_done = false;
- lzw->direct = lzw->buf;
-
- return gif_clear_codes_LZW(compressed_data,
- compressed_data_len,
- compressed_data_pos);
-}
-
-/**
- * fill the LZW stack with decompressed data
- *
- * \param[in] compressed_data LZW compressed data
- * \param[in] compressed_data_len Byte size of compressed_data
- * \param[in,out] compressed_data_pos Current position in compressed_data
- * updated on exit.
- * \return true on sucessful decode of the next LZW code else false.
- */
-static gif_result gif_next_LZW(
- const uint8_t *compressed_data,
- uint32_t compressed_data_len,
- uint32_t *compressed_data_pos)
-{
- int code, incode;
- int block_size;
- int new_code;
-
- code = gif_next_code(compressed_data, compressed_data_len,
- compressed_data_pos, lzw->code_size);
- if (code < 0) {
- return code;
- }
-
- if (code == lzw->clear_code) {
- return gif_clear_codes_LZW(compressed_data,
- compressed_data_len,
- compressed_data_pos);
- }
-
- if (code == lzw->end_code) {
- /* skip to the end of our data so multi-image GIFs work */
- if (lzw->zero_data_block) {
- return GIF_FRAME_DATA_ERROR;
- }
- block_size = 0;
- while (block_size != 1 &&
- *compressed_data_pos < compressed_data_len) {
- block_size = compressed_data[*compressed_data_pos] + 1;
- *compressed_data_pos += block_size;
- }
- return GIF_FRAME_DATA_ERROR;
- }
-
- incode = code;
- if (code >= lzw->max_code) {
- if (lzw->stack_pointer >= lzw->stack + ((1 << GIF_MAX_LZW) * 2)) {
- return GIF_FRAME_DATA_ERROR;
- }
- *lzw->stack_pointer++ = lzw->firstcode;
- code = lzw->oldcode;
- }
-
- /* The following loop is the most important in the GIF decoding cycle
- * as every single pixel passes through it.
- *
- * Note: our stack is always big enough to hold a complete decompressed
- * chunk.
- */
- while (code >= lzw->clear_code) {
- if (lzw->stack_pointer >= lzw->stack + ((1 << GIF_MAX_LZW) * 2) ||
- code >= (1 << GIF_MAX_LZW)) {
- return GIF_FRAME_DATA_ERROR;
- }
- *lzw->stack_pointer++ = lzw->table[1][code];
- new_code = lzw->table[0][code];
- if (new_code < lzw->clear_code) {
- code = new_code;
- break;
- }
-
- if (lzw->stack_pointer >= lzw->stack + ((1 << GIF_MAX_LZW) * 2) ||
- new_code >= (1 << GIF_MAX_LZW)) {
- return GIF_FRAME_DATA_ERROR;
- }
- *lzw->stack_pointer++ = lzw->table[1][new_code];
- code = lzw->table[0][new_code];
- if (code == new_code) {
- return GIF_FRAME_DATA_ERROR;
- }
- }
-
- if (lzw->stack_pointer >= lzw->stack + ((1 << GIF_MAX_LZW) * 2)) {
- return GIF_FRAME_DATA_ERROR;
- }
- *lzw->stack_pointer++ = lzw->firstcode = lzw->table[1][code];
-
- code = lzw->max_code;
- if (code < (1 << GIF_MAX_LZW)) {
- lzw->table[0][code] = lzw->oldcode;
- lzw->table[1][code] = lzw->firstcode;
- ++lzw->max_code;
- if ((lzw->max_code >= lzw->max_code_size) &&
- (lzw->max_code_size < (1 << GIF_MAX_LZW))) {
- lzw->max_code_size = lzw->max_code_size << 1;
- ++lzw->code_size;
- }
- }
- lzw->oldcode = incode;
- return GIF_OK;
-}
-
/**
* Updates the sprite memory size
@@ -725,7 +418,7 @@ static gif_result gif_initialise_frame(gif_animation *gif)
if (gif_bytes < 1) {
return GIF_INSUFFICIENT_FRAME_DATA;
}
- if (gif_data[0] > GIF_MAX_LZW) {
+ if (gif_data[0] > LZW_CODE_MAX) {
return GIF_DATA_ERROR;
}
@@ -863,7 +556,20 @@ static unsigned int gif_interlaced_line(int height, int y) {
}
-
+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,
+ [LZW_BAD_DATA] = GIF_FRAME_DATA_ERROR,
+ };
+ return g_res[l_res];
+}
/**
@@ -878,7 +584,6 @@ gif_internal_decode_frame(gif_animation *gif,
unsigned int frame,
bool clear_image)
{
- gif_result res;
unsigned int index = 0;
unsigned char *gif_data, *gif_end;
int gif_bytes;
@@ -1038,6 +743,10 @@ gif_internal_decode_frame(gif_animation *gif,
/* If we are clearing the image we just clear, if not decode */
if (!clear_image) {
+ lzw_result res;
+ const uint8_t *stack_base;
+ const uint8_t *stack_pos;
+
/* Ensure we have enough data for a 1-byte LZW code size +
* 1-byte gif trailer
*/
@@ -1114,10 +823,11 @@ gif_internal_decode_frame(gif_animation *gif,
gif->buffer_position = (gif_data - gif->gif_data) + 1;
/* Initialise the LZW decoding */
- res = gif_initialise_LZW(gif->gif_data, gif->buffer_size,
- &gif->buffer_position, gif_data[0]);
- if (res != GIF_OK) {
- return res;
+ res = lzw_decode_init(gif->lzw_ctx, gif->gif_data,
+ gif->buffer_size, gif->buffer_position,
+ gif_data[0], &stack_base, &stack_pos);
+ if (res != LZW_OK) {
+ return gif_error_from_lzw(res);
}
/* Decompress the data */
@@ -1135,14 +845,14 @@ gif_internal_decode_frame(gif_animation *gif,
*/
x = width;
while (x > 0) {
- burst_bytes = (lzw->stack_pointer - lzw->stack);
+ burst_bytes = (stack_pos - stack_base);
if (burst_bytes > 0) {
if (burst_bytes > x) {
burst_bytes = x;
}
x -= burst_bytes;
while (burst_bytes-- > 0) {
- colour = *--lzw->stack_pointer;
+ colour = *--stack_pos;
if (((gif->frames[frame].transparency) &&
(colour != gif->frames[frame].transparency_index)) ||
(!gif->frames[frame].transparency)) {
@@ -1151,15 +861,13 @@ gif_internal_decode_frame(gif_animation *gif,
frame_scanline++;
}
} else {
- res = gif_next_LZW(gif->gif_data,
- gif->buffer_size,
- &gif->buffer_position);
- if (res != GIF_OK) {
+ res = lzw_decode(gif->lzw_ctx, &stack_pos);
+ if (res != LZW_OK) {
/* Unexpected end of frame, try to recover */
- if (res == GIF_END_OF_FRAME) {
+ if (res == LZW_OK_EOD) {
return_value = GIF_OK;
} else {
- return_value = res;
+ return_value = gif_error_from_lzw(res);
}
goto gif_decode_frame_exit;
}
@@ -1230,6 +938,14 @@ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *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);
+ }
+ }
+
/* Check for sufficient data to be a GIF (6-byte header + 7-byte
* logical screen descriptor)
*/
@@ -1448,4 +1164,7 @@ void gif_finalise(gif_animation *gif)
gif->local_colour_table = NULL;
free(gif->global_colour_table);
gif->global_colour_table = NULL;
+
+ lzw_context_destroy(gif->lzw_ctx);
+ gif->lzw_ctx = NULL;
}
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=31526dd358255092b4...
commit 31526dd358255092b424efbcd90b17a1be25f000
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
New LZW decoder: Add client calls to initialise LZW, and perform decode.
diff --git a/src/lzw.c b/src/lzw.c
index 4f7ff8f..baeba0a 100644
--- a/src/lzw.c
+++ b/src/lzw.c
@@ -42,11 +42,45 @@ struct lzw_read_ctx {
};
/**
+ * LZW dictionary entry.
+ *
+ * Records in the dictionary are composed of 1 or more entries.
+ * Entries point to previous entries which can be followed to compose
+ * the complete record. To compose the record in reverse order, take
+ * the `last_value` from each entry, and move to the previous entry.
+ * If the previous_entry's index is < the current clear_code, then it
+ * is the last entry in the record.
+ */
+struct lzw_dictionary_entry {
+ uint8_t last_value; /**< Last value for record ending at entry. */
+ uint8_t first_value; /**< First value for entry's record. */
+ uint16_t previous_entry; /**< Offset in dictionary to previous entry. */
+};
+
+/**
* LZW decompression context.
*/
struct lzw_ctx {
/** Input reading context */
struct lzw_read_ctx input;
+
+ uint32_t previous_code; /**< Code read from input previously. */
+ uint32_t previous_code_first; /**< First value of previous code. */
+
+ uint32_t initial_code_size; /**< Starting LZW code size. */
+ uint32_t current_code_size; /**< Current LZW code size. */
+ uint32_t current_code_size_max; /**< Max code value for current size. */
+
+ uint32_t clear_code; /**< Special Clear code value */
+ uint32_t eoi_code; /**< Special End of Information code value */
+
+ uint32_t current_entry; /**< Next position in table to fill. */
+
+ /** Output value stack. */
+ uint8_t stack_base[1 << LZW_CODE_MAX];
+
+ /** LZW decode dictionary. Generated during decode. */
+ struct lzw_dictionary_entry table[1 << LZW_CODE_MAX];
};
@@ -175,3 +209,166 @@ static inline lzw_result lzw__next_code(
*code_out = (code >> current_bit) & ((1 << code_size) - 1);
return LZW_OK;
}
+
+
+/**
+ * Clear LZW code dictionary.
+ *
+ * \param[in] ctx LZW reading context, updated.
+ * \param[out] stack_pos_out Returns current stack position.
+ * \return LZW_OK or error code.
+ */
+static lzw_result lzw__clear_codes(
+ struct lzw_ctx *ctx,
+ const uint8_t ** const stack_pos_out)
+{
+ uint32_t code;
+ uint8_t *stack_pos;
+
+ /* Reset dictionary building context */
+ ctx->current_code_size = ctx->initial_code_size + 1;
+ ctx->current_code_size_max = (1 << ctx->current_code_size) - 1;;
+ ctx->current_entry = (1 << ctx->initial_code_size) + 2;
+
+ /* There might be a sequence of clear codes, so process them all */
+ do {
+ lzw_result res = lzw__next_code(&ctx->input,
+ ctx->current_code_size, &code);
+ if (res != LZW_OK) {
+ return res;
+ }
+ } while (code == ctx->clear_code);
+
+ /* The initial code must be from the initial dictionary. */
+ if (code > ctx->clear_code) {
+ return LZW_BAD_ICODE;
+ }
+
+ /* Record this initial code as "previous" code, needed during decode. */
+ ctx->previous_code = code;
+ ctx->previous_code_first = code;
+
+ /* Reset the stack, and add first non-clear code added as first item. */
+ stack_pos = ctx->stack_base;
+ *stack_pos++ = code;
+
+ *stack_pos_out = stack_pos;
+ return LZW_OK;
+}
+
+
+/* Exported function, documented in lzw.h */
+lzw_result lzw_decode_init(
+ struct lzw_ctx *ctx,
+ const uint8_t *compressed_data,
+ uint32_t compressed_data_len,
+ uint32_t compressed_data_pos,
+ uint8_t code_size,
+ const uint8_t ** const stack_base_out,
+ const uint8_t ** const stack_pos_out)
+{
+ struct lzw_dictionary_entry *table = ctx->table;
+
+ /* Initialise the input reading context */
+ ctx->input.data = compressed_data;
+ ctx->input.data_len = compressed_data_len;
+ ctx->input.data_sb_next = compressed_data_pos;
+
+ ctx->input.sb_bit = 0;
+ ctx->input.sb_bit_count = 0;
+
+ /* Initialise the dictionary building context */
+ ctx->initial_code_size = code_size;
+
+ ctx->clear_code = (1 << code_size) + 0;
+ ctx->eoi_code = (1 << code_size) + 1;
+
+ /* Initialise the standard dictionary entries */
+ for (uint32_t i = 0; i < ctx->clear_code; ++i) {
+ table[i].first_value = i;
+ table[i].last_value = i;
+ }
+
+ *stack_base_out = ctx->stack_base;
+ return lzw__clear_codes(ctx, stack_pos_out);
+}
+
+
+/* Exported function, documented in lzw.h */
+lzw_result lzw_decode(struct lzw_ctx *ctx,
+ const uint8_t ** const stack_pos_out)
+{
+ lzw_result res;
+ uint32_t code_new;
+ uint32_t code_out;
+ struct lzw_dictionary_entry *entry;
+ uint8_t *stack_pos = ctx->stack_base;
+ uint32_t clear_code = ctx->clear_code;
+ uint32_t current_entry = ctx->current_entry;
+ struct lzw_dictionary_entry * const table = ctx->table;
+
+ /* Get a new code from the input */
+ res = lzw__next_code(&ctx->input, ctx->current_code_size, &code_new);
+ if (res != LZW_OK) {
+ return res;
+ }
+
+ if (code_new == clear_code) {
+ /* Got Clear code */
+ return lzw__clear_codes(ctx, stack_pos_out);
+
+
+ } else if (code_new == ctx->eoi_code) {
+ /* Got End of Information code */
+ return LZW_EOI_CODE;
+ }
+
+ if (current_entry >= (1 << LZW_CODE_MAX)) {
+ /* No space in table for new entries, only Clear and
+ * End of Information codes were allowed. */
+ return LZW_BAD_DATA;
+ }
+
+ entry = table + current_entry;
+ if (code_new > current_entry) {
+ /* Code is invalid */
+ return LZW_BAD_CODE;
+ } else if (code_new < current_entry) {
+ /* Code is in table */
+ code_out = code_new;
+ entry->last_value = table[code_new].first_value;
+ } else {
+ /* Code not in table */
+ *stack_pos++ = ctx->previous_code_first;
+ code_out = ctx->previous_code;
+ entry->last_value = ctx->previous_code_first;
+ }
+ entry->first_value = ctx->previous_code_first;
+ entry->previous_entry = ctx->previous_code;
+
+ /* Ensure code size is increased, if needed. */
+ if (current_entry == ctx->current_code_size_max) {
+ if (ctx->current_code_size < LZW_CODE_MAX) {
+ ctx->current_code_size++;
+ ctx->current_code_size_max =
+ (1 << ctx->current_code_size) - 1;
+ }
+ }
+ ctx->current_entry++;
+
+ ctx->previous_code_first = table[code_new].first_value;
+ ctx->previous_code = code_new;
+
+ /* Put rest of data for this code on output stack.
+ * Note, in the case of "code not in table", the last entry of the
+ * current code has already been placed on the stack above. */
+ while (code_out > clear_code) {
+ entry = table + code_out;
+ *stack_pos++ = entry->last_value;
+ code_out = entry->previous_entry;
+ }
+ *stack_pos++ = table[code_out].last_value;
+
+ *stack_pos_out = stack_pos;
+ return LZW_OK;
+}
diff --git a/src/lzw.h b/src/lzw.h
index a908414..5812c0d 100644
--- a/src/lzw.h
+++ b/src/lzw.h
@@ -17,6 +17,10 @@
*/
+/** Maximum LZW code size in bits */
+#define LZW_CODE_MAX 12
+
+
/* Declare lzw internal context structure */
struct lzw_ctx;
@@ -27,6 +31,10 @@ typedef enum lzw_result {
LZW_OK_EOD, /**< Success; reached zero-length sub-block */
LZW_NO_MEM, /**< Error: Out of memory */
LZW_NO_DATA, /**< Error: Out of data */
+ LZW_EOI_CODE, /**< Error: End of Information code */
+ LZW_BAD_ICODE, /**< Error: Bad initial LZW code */
+ LZW_BAD_CODE, /**< Error: Bad LZW code */
+ LZW_BAD_DATA, /**< Error: Bad data */
} lzw_result;
@@ -48,5 +56,51 @@ lzw_result lzw_context_create(
void lzw_context_destroy(
struct lzw_ctx *ctx);
+/**
+ * Initialise an LZW decompression context for decoding.
+ *
+ * Caller owns neither `stack_base_out` or `stack_pos_out`.
+ *
+ * \param[in] ctx The LZW decompression context to initialise.
+ * \param[in] compressed_data The compressed data.
+ * \param[in] compressed_data_len Byte length of compressed data.
+ * \param[in] compressed_data_pos Start position in data. Must be position
+ * of a size byte at sub-block start.
+ * \param[in] code_size The initial LZW code size to use.
+ * \param[out] stack_base_out Returns base of decompressed data stack.
+ * \param[out] stack_pos_out Returns current stack position.
+ * There are `stack_pos_out - stack_base_out`
+ * current stack entries.
+ * \return LZW_OK on success, or appropriate error code otherwise.
+ */
+lzw_result lzw_decode_init(
+ struct lzw_ctx *ctx,
+ const uint8_t *compressed_data,
+ uint32_t compressed_data_len,
+ uint32_t compressed_data_pos,
+ uint8_t code_size,
+ const uint8_t ** const stack_base_out,
+ const uint8_t ** const stack_pos_out);
+
+/**
+ * Fill the LZW stack with decompressed data
+ *
+ * Ensure anything on the stack is used before calling this, as anything
+ * on the stack before this call will be trampled.
+ *
+ * Caller does not own `stack_pos_out`.
+ *
+ * \param[in] ctx LZW reading context, updated.
+ * \param[out] stack_pos_out Returns current stack position.
+ * Use with `stack_base_out` value from previous
+ * lzw_decode_init() call.
+ * There are `stack_pos_out - stack_base_out`
+ * current stack entries.
+ * \return LZW_OK on success, or appropriate error code otherwise.
+ */
+lzw_result lzw_decode(
+ struct lzw_ctx *ctx,
+ const uint8_t ** const stack_pos_out);
+
#endif
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=960c3b6d2229dcc7cc...
commit 960c3b6d2229dcc7cc4a4ef60838329b78880e66
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
New LZW decoder: Add client calls to create/destroy LZW contexts.
diff --git a/src/lzw.c b/src/lzw.c
index 23c815e..4f7ff8f 100644
--- a/src/lzw.c
+++ b/src/lzw.c
@@ -41,6 +41,34 @@ struct lzw_read_ctx {
uint32_t sb_bit_count; /**< Bit count in sub-block */
};
+/**
+ * LZW decompression context.
+ */
+struct lzw_ctx {
+ /** Input reading context */
+ struct lzw_read_ctx input;
+};
+
+
+/* Exported function, documented in lzw.h */
+lzw_result lzw_context_create(struct lzw_ctx **ctx)
+{
+ struct lzw_ctx *c = malloc(sizeof(*c));
+ if (c == NULL) {
+ return LZW_NO_MEM;
+ }
+
+ *ctx = c;
+ 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.
diff --git a/src/lzw.h b/src/lzw.h
index 0683ad0..a908414 100644
--- a/src/lzw.h
+++ b/src/lzw.h
@@ -17,12 +17,36 @@
*/
+/* Declare lzw internal context structure */
+struct lzw_ctx;
+
+
/** LZW decoding response codes */
typedef enum lzw_result {
LZW_OK, /**< Success */
LZW_OK_EOD, /**< Success; reached zero-length sub-block */
+ LZW_NO_MEM, /**< Error: Out of memory */
LZW_NO_DATA, /**< Error: Out of data */
} lzw_result;
+/**
+ * Create an LZW decompression context.
+ *
+ * \param[out] ctx Returns an LZW decompression context. Caller owned,
+ * free with lzw_context_destroy().
+ * \return LZW_OK on success, or appropriate error code otherwise.
+ */
+lzw_result lzw_context_create(
+ struct lzw_ctx **ctx);
+
+/**
+ * Destroy an LZW decompression context.
+ *
+ * \param[in] ctx The LZW decompression context to destroy.
+ */
+void lzw_context_destroy(
+ struct lzw_ctx *ctx);
+
+
#endif
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=8c42f9f934005ba406...
commit 8c42f9f934005ba40645ba326b74c8f8b0675df2
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
New LZW decoder: Routine to extract LZW codes from GIF sub-block sequences.
diff --git a/src/lzw.c b/src/lzw.c
new file mode 100644
index 0000000..23c815e
--- /dev/null
+++ b/src/lzw.c
@@ -0,0 +1,149 @@
+/*
+ * This file is part of NetSurf's LibNSGIF, http://www.netsurf-browser.org/
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * Copyright 2017 Michael Drake <michael.drake(a)codethink.co.uk>
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "lzw.h"
+
+/**
+ * \file
+ * \brief LZW decompression (implementation)
+ *
+ * Decoder for GIF LZW data.
+ */
+
+
+/**
+ * Context for reading LZW data.
+ *
+ * LZW data is split over multiple sub-blocks. Each sub-block has a
+ * byte at the start, which says the sub-block size, and then the data.
+ * Zero-size sub-blocks have no data, and the biggest sub-block size is
+ * 255, which means there are 255 bytes of data following the sub-block
+ * size entry.
+ *
+ * Note that an individual LZW code can be split over up to three sub-blocks.
+ */
+struct lzw_read_ctx {
+ const uint8_t *data; /**< Pointer to start of input data */
+ uint32_t data_len; /**< Input data length */
+ uint32_t data_sb_next; /**< Offset to sub-block size */
+
+ const uint8_t *sb_data; /**< Pointer to current sub-block in data */
+ uint32_t sb_bit; /**< Current bit offset in sub-block */
+ uint32_t sb_bit_count; /**< Bit count in sub-block */
+};
+
+
+/**
+ * Advance the context to the next sub-block in the input data.
+ *
+ * \param[in] ctx LZW reading context, updated on success.
+ * \return LZW_OK or LZW_OK_EOD on success, appropriate error otherwise.
+ */
+static lzw_result lzw__block_advance(struct lzw_read_ctx *ctx)
+{
+ uint32_t block_size;
+ uint32_t next_block_pos = ctx->data_sb_next;
+ const uint8_t *data_next = ctx->data + next_block_pos;
+
+ if (next_block_pos >= ctx->data_len) {
+ return LZW_NO_DATA;
+ }
+
+ block_size = *data_next;
+
+ if ((next_block_pos + block_size) >= ctx->data_len) {
+ return LZW_NO_DATA;
+ }
+
+ ctx->sb_bit = 0;
+ ctx->sb_bit_count = block_size * 8;
+
+ if (block_size == 0) {
+ ctx->data_sb_next += 1;
+ return LZW_OK_EOD;
+ }
+
+ ctx->sb_data = data_next + 1;
+ ctx->data_sb_next += block_size + 1;
+
+ return LZW_OK;
+}
+
+
+/**
+ * Get the next LZW code of given size from the raw input data.
+ *
+ * Reads codes from the input data stream coping with GIF data sub-blocks.
+ *
+ * \param[in] ctx LZW reading context, updated.
+ * \param[in] code_size Size of LZW code to get from data.
+ * \param[out] code_out Returns an LZW code on success.
+ * \return LZW_OK or LZW_OK_EOD on success, appropriate error otherwise.
+ */
+static inline lzw_result lzw__next_code(
+ struct lzw_read_ctx *ctx,
+ uint8_t code_size,
+ uint32_t *code_out)
+{
+ uint32_t code = 0;
+ uint8_t current_bit = ctx->sb_bit & 0x7;
+ uint8_t byte_advance = (current_bit + code_size) >> 3;
+
+ if (ctx->sb_bit + code_size < ctx->sb_bit_count) {
+ /* Fast path: code fully inside this sub-block */
+ const uint8_t *data = ctx->sb_data + (ctx->sb_bit >> 3);
+ switch (byte_advance) {
+ case 2: code |= data[2] << 16;
+ case 1: code |= data[1] << 8;
+ case 0: code |= data[0] << 0;
+ }
+ ctx->sb_bit += code_size;
+ } else {
+ /* Slow path: code spans sub-blocks */
+ uint8_t byte = 0;
+ uint8_t bits_remaining_0 = (code_size < (8 - current_bit)) ?
+ code_size : (8 - current_bit);
+ uint8_t bits_remaining_1 = code_size - bits_remaining_0;
+ uint8_t bits_used[3] = {
+ [0] = bits_remaining_0,
+ [1] = bits_remaining_1 < 8 ? bits_remaining_1 : 8,
+ [2] = bits_remaining_1 - 8,
+ };
+
+ while (true) {
+ const uint8_t *data = ctx->sb_data;
+ lzw_result res;
+
+ /* Get any data from end of this sub-block */
+ while (byte <= byte_advance &&
+ ctx->sb_bit < ctx->sb_bit_count) {
+ code |= data[ctx->sb_bit >> 3] << (byte << 3);
+ ctx->sb_bit += bits_used[byte];
+ byte++;
+ }
+
+ /* Check if we have all we need */
+ if (byte > byte_advance) {
+ break;
+ }
+
+ /* Move to next sub-block */
+ res = lzw__block_advance(ctx);
+ if (res != LZW_OK) {
+ return res;
+ }
+ }
+ }
+
+ *code_out = (code >> current_bit) & ((1 << code_size) - 1);
+ return LZW_OK;
+}
diff --git a/src/lzw.h b/src/lzw.h
new file mode 100644
index 0000000..0683ad0
--- /dev/null
+++ b/src/lzw.h
@@ -0,0 +1,28 @@
+/*
+ * This file is part of NetSurf's LibNSGIF, http://www.netsurf-browser.org/
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * Copyright 2017 Michael Drake <michael.drake(a)codethink.co.uk>
+ */
+
+#ifndef LZW_H_
+#define LZW_H_
+
+/**
+ * \file
+ * \brief LZW decompression (interface)
+ *
+ * Decoder for GIF LZW data.
+ */
+
+
+/** LZW decoding response codes */
+typedef enum lzw_result {
+ LZW_OK, /**< Success */
+ LZW_OK_EOD, /**< Success; reached zero-length sub-block */
+ LZW_NO_DATA, /**< Error: Out of data */
+} lzw_result;
+
+
+#endif
-----------------------------------------------------------------------
Summary of changes:
src/lzw.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/lzw.c b/src/lzw.c
index e10da84..baeba0a 100644
--- a/src/lzw.c
+++ b/src/lzw.c
@@ -172,7 +172,8 @@ static inline lzw_result lzw__next_code(
} else {
/* Slow path: code spans sub-blocks */
uint8_t byte = 0;
- uint8_t bits_remaining_0 = 8 - current_bit;
+ uint8_t bits_remaining_0 = (code_size < (8 - current_bit)) ?
+ code_size : (8 - current_bit);
uint8_t bits_remaining_1 = code_size - bits_remaining_0;
uint8_t bits_used[3] = {
[0] = bits_remaining_0,
--
NetSurf GIF Decoder
5 years, 9 months
libnsgif: branch tlsa/lzw created. release/0.1.4-18-gbe54827
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/libnsgif.git/shortlog/be548274229f3541f779...
...commit http://git.netsurf-browser.org/libnsgif.git/commit/be548274229f3541f77955...
...tree http://git.netsurf-browser.org/libnsgif.git/tree/be548274229f3541f779557d...
The branch, tlsa/lzw has been created
at be548274229f3541f779557d7109e68881f2bfab (commit)
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=be548274229f3541f7...
commit be548274229f3541f779557d7109e68881f2bfab
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
GIF decoding: Remove old LZW decoder and port library to new implementation.
diff --git a/include/libnsgif.h b/include/libnsgif.h
index fd92a0a..a819fec 100644
--- a/include/libnsgif.h
+++ b/include/libnsgif.h
@@ -93,6 +93,8 @@ typedef struct 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 */
diff --git a/src/Makefile b/src/Makefile
index 0fa4dc6..cb5d31f 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,4 +1,4 @@
# Sources
-DIR_SOURCES := libnsgif.c
+DIR_SOURCES := libnsgif.c lzw.c
include $(NSBUILD)/Makefile.subdir
diff --git a/src/libnsgif.c b/src/libnsgif.c
index 998f8d7..fffbd94 100644
--- a/src/libnsgif.c
+++ b/src/libnsgif.c
@@ -8,6 +8,7 @@
*/
#include <stdbool.h>
+#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
@@ -15,6 +16,8 @@
#include "libnsgif.h"
#include "utils/log.h"
+#include "lzw.h"
+
/**
*
* \file
@@ -36,9 +39,6 @@
/** Internal flag that a frame is invalid/unprocessed */
#define GIF_INVALID_FRAME -1
-/** Maximum LZW bits available */
-#define GIF_MAX_LZW 12
-
/** Transparent colour */
#define GIF_TRANSPARENT_COLOUR 0x00
@@ -65,313 +65,6 @@
/** standard GIF header size */
#define GIF_STANDARD_HEADER_SIZE 13
-/** LZW parameters */
-struct lzw_s {
- unsigned char buf[4];
- const unsigned char *direct;
- int table[2][(1 << GIF_MAX_LZW)];
- unsigned char stack[(1 << GIF_MAX_LZW) * 2];
- unsigned char *stack_pointer;
-
- int code_size, set_code_size;
-
- int max_code, max_code_size;
-
- int clear_code, end_code;
-
- int curbit, lastbit, last_byte;
-
- int firstcode;
- int oldcode;
-
- bool zero_data_block;
- bool get_done;
-};
-
-
-/* General LZW values. They are shared for all GIFs being decoded, and thus we
- * can't handle progressive decoding efficiently without having the data for
- * each image which would use an extra 10Kb or so per GIF.
- */
-static struct lzw_s lzw_params = {
- .zero_data_block = false,
-};
-
-static struct lzw_s *lzw = &lzw_params;
-
-/**
- * get the next LZW code from the GIF
- *
- * reads codes from the input data stream coping with GIF data sub blocking
- *
- * \param[in] compressed_data LZW compressed data
- * \param[in] compressed_data_len Byte size of compressed_data
- * \param[in,out] compressed_data_pos Current position in compressed_data
- * updated on exit.
- * \param[in] code_size Number of bits in the current LZW code
- * \return The next code to process or error return code
- */
-static int gif_next_code(
- const uint8_t *compressed_data,
- uint32_t compressed_data_len,
- uint32_t *compressed_data_pos,
- int code_size)
-{
- int i, j, end, count, ret;
- uint32_t pos = *compressed_data_pos;
- const unsigned char *b;
- static const int maskTbl[16] = {
- 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f,
- 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff
- };
-
- end = lzw->curbit + code_size;
- if (end >= lzw->lastbit) {
- if (lzw->get_done) {
- return GIF_END_OF_FRAME;
- }
- lzw->buf[0] = lzw->direct[lzw->last_byte - 2];
- lzw->buf[1] = lzw->direct[lzw->last_byte - 1];
-
- /* get the next block */
- lzw->direct = compressed_data + pos;
- if (pos >= compressed_data_len) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
-
- count = lzw->direct[0];
- lzw->zero_data_block = (count == 0);
- if ((pos + count) >= compressed_data_len) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
-
- if (count == 0) {
- lzw->get_done = true;
- } else {
- if (pos + 3 >= compressed_data_len) {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
- lzw->direct -= 1;
- lzw->buf[2] = lzw->direct[2];
- lzw->buf[3] = lzw->direct[3];
- }
- pos += count + 1;
-
- /* update our variables */
- lzw->last_byte = 2 + count;
- lzw->curbit = (lzw->curbit - lzw->lastbit) + 16;
- lzw->lastbit = (2 + count) << 3;
- end = lzw->curbit + code_size;
- }
-
- i = lzw->curbit >> 3;
- if (i < 2) {
- b = lzw->buf;
- } else {
- b = lzw->direct;
- }
-
- ret = b[i];
- j = (end >> 3) - 1;
- if (i <= j) {
- ret |= (b[i + 1] << 8);
- if (i < j) {
- ret |= (b[i + 2] << 16);
- }
- }
- ret = (ret >> (lzw->curbit % 8)) & maskTbl[code_size];
- lzw->curbit += code_size;
-
- *compressed_data_pos = pos;
- return ret;
-}
-
-
-/**
- * Clear LZW code dictionary
- *
- * \param[in] compressed_data LZW compressed data
- * \param[in] compressed_data_len Byte size of compressed_data
- * \param[in,out] compressed_data_pos Current position in compressed_data
- * updated on exit.
- * \return GIF_OK or error code.
- */
-static gif_result gif_clear_codes_LZW(
- const uint8_t *compressed_data,
- uint32_t compressed_data_len,
- uint32_t *compressed_data_pos)
-{
- int i;
- int code;
-
- if (lzw->clear_code >= (1 << GIF_MAX_LZW)) {
- lzw->stack_pointer = lzw->stack;
- return GIF_FRAME_DATA_ERROR;
- }
-
- /* initialise our table */
- memset(lzw->table, 0x00, (1 << GIF_MAX_LZW) * 8);
- for (i = 0; i < lzw->clear_code; ++i) {
- lzw->table[1][i] = i;
- }
-
- /* reset LZW parameters */
- lzw->code_size = lzw->set_code_size + 1;
- lzw->max_code_size = lzw->clear_code << 1;
- lzw->max_code = lzw->clear_code + 2;
- lzw->stack_pointer = lzw->stack;
-
- /* process repeated clear codes */
- do {
- code = gif_next_code(compressed_data, compressed_data_len,
- compressed_data_pos, lzw->code_size);
- if (code < 0) {
- return code;
- }
- } while (code == lzw->clear_code);
- lzw->firstcode = lzw->oldcode = code;
-
- *lzw->stack_pointer++ = lzw->firstcode;
-
- return GIF_OK;
-}
-
-
-/**
- * Initialise LZW
- *
- * This initialises a LZW context ready to commence decompression.
- *
- * \param[in] compressed_data LZW compressed data
- * \param[in] compressed_data_len Byte size of compressed_data
- * \param[in,out] compressed_data_pos Current position in compressed_data
- * updated on exit.
- * \param[in] initial_code_size Size of codes used on clearing of code dictionary
- */
-static gif_result gif_initialise_LZW(
- const uint8_t *compressed_data,
- uint32_t compressed_data_len,
- uint32_t *compressed_data_pos,
- int initial_code_size)
-{
- lzw->set_code_size = initial_code_size;
- lzw->code_size = lzw->set_code_size + 1;
- lzw->clear_code = (1 << lzw->set_code_size);
- lzw->end_code = lzw->clear_code + 1;
- lzw->max_code_size = lzw->clear_code << 1;
- lzw->max_code = lzw->clear_code + 2;
- lzw->curbit = lzw->lastbit = 0;
- lzw->last_byte = 2;
- lzw->get_done = false;
- lzw->direct = lzw->buf;
-
- return gif_clear_codes_LZW(compressed_data,
- compressed_data_len,
- compressed_data_pos);
-}
-
-/**
- * fill the LZW stack with decompressed data
- *
- * \param[in] compressed_data LZW compressed data
- * \param[in] compressed_data_len Byte size of compressed_data
- * \param[in,out] compressed_data_pos Current position in compressed_data
- * updated on exit.
- * \return true on sucessful decode of the next LZW code else false.
- */
-static gif_result gif_next_LZW(
- const uint8_t *compressed_data,
- uint32_t compressed_data_len,
- uint32_t *compressed_data_pos)
-{
- int code, incode;
- int block_size;
- int new_code;
-
- code = gif_next_code(compressed_data, compressed_data_len,
- compressed_data_pos, lzw->code_size);
- if (code < 0) {
- return code;
- }
-
- if (code == lzw->clear_code) {
- return gif_clear_codes_LZW(compressed_data,
- compressed_data_len,
- compressed_data_pos);
- }
-
- if (code == lzw->end_code) {
- /* skip to the end of our data so multi-image GIFs work */
- if (lzw->zero_data_block) {
- return GIF_FRAME_DATA_ERROR;
- }
- block_size = 0;
- while (block_size != 1 &&
- *compressed_data_pos < compressed_data_len) {
- block_size = compressed_data[*compressed_data_pos] + 1;
- *compressed_data_pos += block_size;
- }
- return GIF_FRAME_DATA_ERROR;
- }
-
- incode = code;
- if (code >= lzw->max_code) {
- if (lzw->stack_pointer >= lzw->stack + ((1 << GIF_MAX_LZW) * 2)) {
- return GIF_FRAME_DATA_ERROR;
- }
- *lzw->stack_pointer++ = lzw->firstcode;
- code = lzw->oldcode;
- }
-
- /* The following loop is the most important in the GIF decoding cycle
- * as every single pixel passes through it.
- *
- * Note: our stack is always big enough to hold a complete decompressed
- * chunk.
- */
- while (code >= lzw->clear_code) {
- if (lzw->stack_pointer >= lzw->stack + ((1 << GIF_MAX_LZW) * 2) ||
- code >= (1 << GIF_MAX_LZW)) {
- return GIF_FRAME_DATA_ERROR;
- }
- *lzw->stack_pointer++ = lzw->table[1][code];
- new_code = lzw->table[0][code];
- if (new_code < lzw->clear_code) {
- code = new_code;
- break;
- }
-
- if (lzw->stack_pointer >= lzw->stack + ((1 << GIF_MAX_LZW) * 2) ||
- new_code >= (1 << GIF_MAX_LZW)) {
- return GIF_FRAME_DATA_ERROR;
- }
- *lzw->stack_pointer++ = lzw->table[1][new_code];
- code = lzw->table[0][new_code];
- if (code == new_code) {
- return GIF_FRAME_DATA_ERROR;
- }
- }
-
- if (lzw->stack_pointer >= lzw->stack + ((1 << GIF_MAX_LZW) * 2)) {
- return GIF_FRAME_DATA_ERROR;
- }
- *lzw->stack_pointer++ = lzw->firstcode = lzw->table[1][code];
-
- code = lzw->max_code;
- if (code < (1 << GIF_MAX_LZW)) {
- lzw->table[0][code] = lzw->oldcode;
- lzw->table[1][code] = lzw->firstcode;
- ++lzw->max_code;
- if ((lzw->max_code >= lzw->max_code_size) &&
- (lzw->max_code_size < (1 << GIF_MAX_LZW))) {
- lzw->max_code_size = lzw->max_code_size << 1;
- ++lzw->code_size;
- }
- }
- lzw->oldcode = incode;
- return GIF_OK;
-}
-
/**
* Updates the sprite memory size
@@ -725,7 +418,7 @@ static gif_result gif_initialise_frame(gif_animation *gif)
if (gif_bytes < 1) {
return GIF_INSUFFICIENT_FRAME_DATA;
}
- if (gif_data[0] > GIF_MAX_LZW) {
+ if (gif_data[0] > LZW_CODE_MAX) {
return GIF_DATA_ERROR;
}
@@ -863,7 +556,20 @@ static unsigned int gif_interlaced_line(int height, int y) {
}
-
+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,
+ [LZW_BAD_DATA] = GIF_FRAME_DATA_ERROR,
+ };
+ return g_res[l_res];
+}
/**
@@ -878,7 +584,6 @@ gif_internal_decode_frame(gif_animation *gif,
unsigned int frame,
bool clear_image)
{
- gif_result res;
unsigned int index = 0;
unsigned char *gif_data, *gif_end;
int gif_bytes;
@@ -1038,6 +743,10 @@ gif_internal_decode_frame(gif_animation *gif,
/* If we are clearing the image we just clear, if not decode */
if (!clear_image) {
+ lzw_result res;
+ const uint8_t *stack_base;
+ const uint8_t *stack_pos;
+
/* Ensure we have enough data for a 1-byte LZW code size +
* 1-byte gif trailer
*/
@@ -1114,10 +823,11 @@ gif_internal_decode_frame(gif_animation *gif,
gif->buffer_position = (gif_data - gif->gif_data) + 1;
/* Initialise the LZW decoding */
- res = gif_initialise_LZW(gif->gif_data, gif->buffer_size,
- &gif->buffer_position, gif_data[0]);
- if (res != GIF_OK) {
- return res;
+ res = lzw_decode_init(gif->lzw_ctx, gif->gif_data,
+ gif->buffer_size, gif->buffer_position,
+ gif_data[0], &stack_base, &stack_pos);
+ if (res != LZW_OK) {
+ return gif_error_from_lzw(res);
}
/* Decompress the data */
@@ -1135,14 +845,14 @@ gif_internal_decode_frame(gif_animation *gif,
*/
x = width;
while (x > 0) {
- burst_bytes = (lzw->stack_pointer - lzw->stack);
+ burst_bytes = (stack_pos - stack_base);
if (burst_bytes > 0) {
if (burst_bytes > x) {
burst_bytes = x;
}
x -= burst_bytes;
while (burst_bytes-- > 0) {
- colour = *--lzw->stack_pointer;
+ colour = *--stack_pos;
if (((gif->frames[frame].transparency) &&
(colour != gif->frames[frame].transparency_index)) ||
(!gif->frames[frame].transparency)) {
@@ -1151,15 +861,13 @@ gif_internal_decode_frame(gif_animation *gif,
frame_scanline++;
}
} else {
- res = gif_next_LZW(gif->gif_data,
- gif->buffer_size,
- &gif->buffer_position);
- if (res != GIF_OK) {
+ res = lzw_decode(gif->lzw_ctx, &stack_pos);
+ if (res != LZW_OK) {
/* Unexpected end of frame, try to recover */
- if (res == GIF_END_OF_FRAME) {
+ if (res == LZW_OK_EOD) {
return_value = GIF_OK;
} else {
- return_value = res;
+ return_value = gif_error_from_lzw(res);
}
goto gif_decode_frame_exit;
}
@@ -1230,6 +938,14 @@ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *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);
+ }
+ }
+
/* Check for sufficient data to be a GIF (6-byte header + 7-byte
* logical screen descriptor)
*/
@@ -1448,4 +1164,7 @@ void gif_finalise(gif_animation *gif)
gif->local_colour_table = NULL;
free(gif->global_colour_table);
gif->global_colour_table = NULL;
+
+ lzw_context_destroy(gif->lzw_ctx);
+ gif->lzw_ctx = NULL;
}
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=a7d46c720d4328ae84...
commit a7d46c720d4328ae840c43903bde11404280ec97
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
New LZW decoder: Add client calls to initialise LZW, and perform decode.
diff --git a/src/lzw.c b/src/lzw.c
index 719380e..e10da84 100644
--- a/src/lzw.c
+++ b/src/lzw.c
@@ -42,11 +42,45 @@ struct lzw_read_ctx {
};
/**
+ * LZW dictionary entry.
+ *
+ * Records in the dictionary are composed of 1 or more entries.
+ * Entries point to previous entries which can be followed to compose
+ * the complete record. To compose the record in reverse order, take
+ * the `last_value` from each entry, and move to the previous entry.
+ * If the previous_entry's index is < the current clear_code, then it
+ * is the last entry in the record.
+ */
+struct lzw_dictionary_entry {
+ uint8_t last_value; /**< Last value for record ending at entry. */
+ uint8_t first_value; /**< First value for entry's record. */
+ uint16_t previous_entry; /**< Offset in dictionary to previous entry. */
+};
+
+/**
* LZW decompression context.
*/
struct lzw_ctx {
/** Input reading context */
struct lzw_read_ctx input;
+
+ uint32_t previous_code; /**< Code read from input previously. */
+ uint32_t previous_code_first; /**< First value of previous code. */
+
+ uint32_t initial_code_size; /**< Starting LZW code size. */
+ uint32_t current_code_size; /**< Current LZW code size. */
+ uint32_t current_code_size_max; /**< Max code value for current size. */
+
+ uint32_t clear_code; /**< Special Clear code value */
+ uint32_t eoi_code; /**< Special End of Information code value */
+
+ uint32_t current_entry; /**< Next position in table to fill. */
+
+ /** Output value stack. */
+ uint8_t stack_base[1 << LZW_CODE_MAX];
+
+ /** LZW decode dictionary. Generated during decode. */
+ struct lzw_dictionary_entry table[1 << LZW_CODE_MAX];
};
@@ -174,3 +208,166 @@ static inline lzw_result lzw__next_code(
*code_out = (code >> current_bit) & ((1 << code_size) - 1);
return LZW_OK;
}
+
+
+/**
+ * Clear LZW code dictionary.
+ *
+ * \param[in] ctx LZW reading context, updated.
+ * \param[out] stack_pos_out Returns current stack position.
+ * \return LZW_OK or error code.
+ */
+static lzw_result lzw__clear_codes(
+ struct lzw_ctx *ctx,
+ const uint8_t ** const stack_pos_out)
+{
+ uint32_t code;
+ uint8_t *stack_pos;
+
+ /* Reset dictionary building context */
+ ctx->current_code_size = ctx->initial_code_size + 1;
+ ctx->current_code_size_max = (1 << ctx->current_code_size) - 1;;
+ ctx->current_entry = (1 << ctx->initial_code_size) + 2;
+
+ /* There might be a sequence of clear codes, so process them all */
+ do {
+ lzw_result res = lzw__next_code(&ctx->input,
+ ctx->current_code_size, &code);
+ if (res != LZW_OK) {
+ return res;
+ }
+ } while (code == ctx->clear_code);
+
+ /* The initial code must be from the initial dictionary. */
+ if (code > ctx->clear_code) {
+ return LZW_BAD_ICODE;
+ }
+
+ /* Record this initial code as "previous" code, needed during decode. */
+ ctx->previous_code = code;
+ ctx->previous_code_first = code;
+
+ /* Reset the stack, and add first non-clear code added as first item. */
+ stack_pos = ctx->stack_base;
+ *stack_pos++ = code;
+
+ *stack_pos_out = stack_pos;
+ return LZW_OK;
+}
+
+
+/* Exported function, documented in lzw.h */
+lzw_result lzw_decode_init(
+ struct lzw_ctx *ctx,
+ const uint8_t *compressed_data,
+ uint32_t compressed_data_len,
+ uint32_t compressed_data_pos,
+ uint8_t code_size,
+ const uint8_t ** const stack_base_out,
+ const uint8_t ** const stack_pos_out)
+{
+ struct lzw_dictionary_entry *table = ctx->table;
+
+ /* Initialise the input reading context */
+ ctx->input.data = compressed_data;
+ ctx->input.data_len = compressed_data_len;
+ ctx->input.data_sb_next = compressed_data_pos;
+
+ ctx->input.sb_bit = 0;
+ ctx->input.sb_bit_count = 0;
+
+ /* Initialise the dictionary building context */
+ ctx->initial_code_size = code_size;
+
+ ctx->clear_code = (1 << code_size) + 0;
+ ctx->eoi_code = (1 << code_size) + 1;
+
+ /* Initialise the standard dictionary entries */
+ for (uint32_t i = 0; i < ctx->clear_code; ++i) {
+ table[i].first_value = i;
+ table[i].last_value = i;
+ }
+
+ *stack_base_out = ctx->stack_base;
+ return lzw__clear_codes(ctx, stack_pos_out);
+}
+
+
+/* Exported function, documented in lzw.h */
+lzw_result lzw_decode(struct lzw_ctx *ctx,
+ const uint8_t ** const stack_pos_out)
+{
+ lzw_result res;
+ uint32_t code_new;
+ uint32_t code_out;
+ struct lzw_dictionary_entry *entry;
+ uint8_t *stack_pos = ctx->stack_base;
+ uint32_t clear_code = ctx->clear_code;
+ uint32_t current_entry = ctx->current_entry;
+ struct lzw_dictionary_entry * const table = ctx->table;
+
+ /* Get a new code from the input */
+ res = lzw__next_code(&ctx->input, ctx->current_code_size, &code_new);
+ if (res != LZW_OK) {
+ return res;
+ }
+
+ if (code_new == clear_code) {
+ /* Got Clear code */
+ return lzw__clear_codes(ctx, stack_pos_out);
+
+
+ } else if (code_new == ctx->eoi_code) {
+ /* Got End of Information code */
+ return LZW_EOI_CODE;
+ }
+
+ if (current_entry >= (1 << LZW_CODE_MAX)) {
+ /* No space in table for new entries, only Clear and
+ * End of Information codes were allowed. */
+ return LZW_BAD_DATA;
+ }
+
+ entry = table + current_entry;
+ if (code_new > current_entry) {
+ /* Code is invalid */
+ return LZW_BAD_CODE;
+ } else if (code_new < current_entry) {
+ /* Code is in table */
+ code_out = code_new;
+ entry->last_value = table[code_new].first_value;
+ } else {
+ /* Code not in table */
+ *stack_pos++ = ctx->previous_code_first;
+ code_out = ctx->previous_code;
+ entry->last_value = ctx->previous_code_first;
+ }
+ entry->first_value = ctx->previous_code_first;
+ entry->previous_entry = ctx->previous_code;
+
+ /* Ensure code size is increased, if needed. */
+ if (current_entry == ctx->current_code_size_max) {
+ if (ctx->current_code_size < LZW_CODE_MAX) {
+ ctx->current_code_size++;
+ ctx->current_code_size_max =
+ (1 << ctx->current_code_size) - 1;
+ }
+ }
+ ctx->current_entry++;
+
+ ctx->previous_code_first = table[code_new].first_value;
+ ctx->previous_code = code_new;
+
+ /* Put rest of data for this code on output stack.
+ * Note, in the case of "code not in table", the last entry of the
+ * current code has already been placed on the stack above. */
+ while (code_out > clear_code) {
+ entry = table + code_out;
+ *stack_pos++ = entry->last_value;
+ code_out = entry->previous_entry;
+ }
+ *stack_pos++ = table[code_out].last_value;
+
+ *stack_pos_out = stack_pos;
+ return LZW_OK;
+}
diff --git a/src/lzw.h b/src/lzw.h
index a908414..5812c0d 100644
--- a/src/lzw.h
+++ b/src/lzw.h
@@ -17,6 +17,10 @@
*/
+/** Maximum LZW code size in bits */
+#define LZW_CODE_MAX 12
+
+
/* Declare lzw internal context structure */
struct lzw_ctx;
@@ -27,6 +31,10 @@ typedef enum lzw_result {
LZW_OK_EOD, /**< Success; reached zero-length sub-block */
LZW_NO_MEM, /**< Error: Out of memory */
LZW_NO_DATA, /**< Error: Out of data */
+ LZW_EOI_CODE, /**< Error: End of Information code */
+ LZW_BAD_ICODE, /**< Error: Bad initial LZW code */
+ LZW_BAD_CODE, /**< Error: Bad LZW code */
+ LZW_BAD_DATA, /**< Error: Bad data */
} lzw_result;
@@ -48,5 +56,51 @@ lzw_result lzw_context_create(
void lzw_context_destroy(
struct lzw_ctx *ctx);
+/**
+ * Initialise an LZW decompression context for decoding.
+ *
+ * Caller owns neither `stack_base_out` or `stack_pos_out`.
+ *
+ * \param[in] ctx The LZW decompression context to initialise.
+ * \param[in] compressed_data The compressed data.
+ * \param[in] compressed_data_len Byte length of compressed data.
+ * \param[in] compressed_data_pos Start position in data. Must be position
+ * of a size byte at sub-block start.
+ * \param[in] code_size The initial LZW code size to use.
+ * \param[out] stack_base_out Returns base of decompressed data stack.
+ * \param[out] stack_pos_out Returns current stack position.
+ * There are `stack_pos_out - stack_base_out`
+ * current stack entries.
+ * \return LZW_OK on success, or appropriate error code otherwise.
+ */
+lzw_result lzw_decode_init(
+ struct lzw_ctx *ctx,
+ const uint8_t *compressed_data,
+ uint32_t compressed_data_len,
+ uint32_t compressed_data_pos,
+ uint8_t code_size,
+ const uint8_t ** const stack_base_out,
+ const uint8_t ** const stack_pos_out);
+
+/**
+ * Fill the LZW stack with decompressed data
+ *
+ * Ensure anything on the stack is used before calling this, as anything
+ * on the stack before this call will be trampled.
+ *
+ * Caller does not own `stack_pos_out`.
+ *
+ * \param[in] ctx LZW reading context, updated.
+ * \param[out] stack_pos_out Returns current stack position.
+ * Use with `stack_base_out` value from previous
+ * lzw_decode_init() call.
+ * There are `stack_pos_out - stack_base_out`
+ * current stack entries.
+ * \return LZW_OK on success, or appropriate error code otherwise.
+ */
+lzw_result lzw_decode(
+ struct lzw_ctx *ctx,
+ const uint8_t ** const stack_pos_out);
+
#endif
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=e7efcc3c309c0e7cdc...
commit e7efcc3c309c0e7cdce75d108b180fb3b3c7de96
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
New LZW decoder: Add client calls to create/destroy LZW contexts.
diff --git a/src/lzw.c b/src/lzw.c
index 7f17dc1..719380e 100644
--- a/src/lzw.c
+++ b/src/lzw.c
@@ -41,6 +41,34 @@ struct lzw_read_ctx {
uint32_t sb_bit_count; /**< Bit count in sub-block */
};
+/**
+ * LZW decompression context.
+ */
+struct lzw_ctx {
+ /** Input reading context */
+ struct lzw_read_ctx input;
+};
+
+
+/* Exported function, documented in lzw.h */
+lzw_result lzw_context_create(struct lzw_ctx **ctx)
+{
+ struct lzw_ctx *c = malloc(sizeof(*c));
+ if (c == NULL) {
+ return LZW_NO_MEM;
+ }
+
+ *ctx = c;
+ 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.
diff --git a/src/lzw.h b/src/lzw.h
index 0683ad0..a908414 100644
--- a/src/lzw.h
+++ b/src/lzw.h
@@ -17,12 +17,36 @@
*/
+/* Declare lzw internal context structure */
+struct lzw_ctx;
+
+
/** LZW decoding response codes */
typedef enum lzw_result {
LZW_OK, /**< Success */
LZW_OK_EOD, /**< Success; reached zero-length sub-block */
+ LZW_NO_MEM, /**< Error: Out of memory */
LZW_NO_DATA, /**< Error: Out of data */
} lzw_result;
+/**
+ * Create an LZW decompression context.
+ *
+ * \param[out] ctx Returns an LZW decompression context. Caller owned,
+ * free with lzw_context_destroy().
+ * \return LZW_OK on success, or appropriate error code otherwise.
+ */
+lzw_result lzw_context_create(
+ struct lzw_ctx **ctx);
+
+/**
+ * Destroy an LZW decompression context.
+ *
+ * \param[in] ctx The LZW decompression context to destroy.
+ */
+void lzw_context_destroy(
+ struct lzw_ctx *ctx);
+
+
#endif
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=116e7850b55124d6ba...
commit 116e7850b55124d6ba85cef3593a4fba72f8c2f9
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
New LZW decoder: Routine to extract LZW codes from GIF sub-block sequences.
diff --git a/src/lzw.c b/src/lzw.c
new file mode 100644
index 0000000..7f17dc1
--- /dev/null
+++ b/src/lzw.c
@@ -0,0 +1,148 @@
+/*
+ * This file is part of NetSurf's LibNSGIF, http://www.netsurf-browser.org/
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * Copyright 2017 Michael Drake <michael.drake(a)codethink.co.uk>
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "lzw.h"
+
+/**
+ * \file
+ * \brief LZW decompression (implementation)
+ *
+ * Decoder for GIF LZW data.
+ */
+
+
+/**
+ * Context for reading LZW data.
+ *
+ * LZW data is split over multiple sub-blocks. Each sub-block has a
+ * byte at the start, which says the sub-block size, and then the data.
+ * Zero-size sub-blocks have no data, and the biggest sub-block size is
+ * 255, which means there are 255 bytes of data following the sub-block
+ * size entry.
+ *
+ * Note that an individual LZW code can be split over up to three sub-blocks.
+ */
+struct lzw_read_ctx {
+ const uint8_t *data; /**< Pointer to start of input data */
+ uint32_t data_len; /**< Input data length */
+ uint32_t data_sb_next; /**< Offset to sub-block size */
+
+ const uint8_t *sb_data; /**< Pointer to current sub-block in data */
+ uint32_t sb_bit; /**< Current bit offset in sub-block */
+ uint32_t sb_bit_count; /**< Bit count in sub-block */
+};
+
+
+/**
+ * Advance the context to the next sub-block in the input data.
+ *
+ * \param[in] ctx LZW reading context, updated on success.
+ * \return LZW_OK or LZW_OK_EOD on success, appropriate error otherwise.
+ */
+static lzw_result lzw__block_advance(struct lzw_read_ctx *ctx)
+{
+ uint32_t block_size;
+ uint32_t next_block_pos = ctx->data_sb_next;
+ const uint8_t *data_next = ctx->data + next_block_pos;
+
+ if (next_block_pos >= ctx->data_len) {
+ return LZW_NO_DATA;
+ }
+
+ block_size = *data_next;
+
+ if ((next_block_pos + block_size) >= ctx->data_len) {
+ return LZW_NO_DATA;
+ }
+
+ ctx->sb_bit = 0;
+ ctx->sb_bit_count = block_size * 8;
+
+ if (block_size == 0) {
+ ctx->data_sb_next += 1;
+ return LZW_OK_EOD;
+ }
+
+ ctx->sb_data = data_next + 1;
+ ctx->data_sb_next += block_size + 1;
+
+ return LZW_OK;
+}
+
+
+/**
+ * Get the next LZW code of given size from the raw input data.
+ *
+ * Reads codes from the input data stream coping with GIF data sub-blocks.
+ *
+ * \param[in] ctx LZW reading context, updated.
+ * \param[in] code_size Size of LZW code to get from data.
+ * \param[out] code_out Returns an LZW code on success.
+ * \return LZW_OK or LZW_OK_EOD on success, appropriate error otherwise.
+ */
+static inline lzw_result lzw__next_code(
+ struct lzw_read_ctx *ctx,
+ uint8_t code_size,
+ uint32_t *code_out)
+{
+ uint32_t code = 0;
+ uint8_t current_bit = ctx->sb_bit & 0x7;
+ uint8_t byte_advance = (current_bit + code_size) >> 3;
+
+ if (ctx->sb_bit + code_size < ctx->sb_bit_count) {
+ /* Fast path: code fully inside this sub-block */
+ const uint8_t *data = ctx->sb_data + (ctx->sb_bit >> 3);
+ switch (byte_advance) {
+ case 2: code |= data[2] << 16;
+ case 1: code |= data[1] << 8;
+ case 0: code |= data[0] << 0;
+ }
+ ctx->sb_bit += code_size;
+ } else {
+ /* Slow path: code spans sub-blocks */
+ uint8_t byte = 0;
+ uint8_t bits_remaining_0 = 8 - current_bit;
+ uint8_t bits_remaining_1 = code_size - bits_remaining_0;
+ uint8_t bits_used[3] = {
+ [0] = bits_remaining_0,
+ [1] = bits_remaining_1 < 8 ? bits_remaining_1 : 8,
+ [2] = bits_remaining_1 - 8,
+ };
+
+ while (true) {
+ const uint8_t *data = ctx->sb_data;
+ lzw_result res;
+
+ /* Get any data from end of this sub-block */
+ while (byte <= byte_advance &&
+ ctx->sb_bit < ctx->sb_bit_count) {
+ code |= data[ctx->sb_bit >> 3] << (byte << 3);
+ ctx->sb_bit += bits_used[byte];
+ byte++;
+ }
+
+ /* Check if we have all we need */
+ if (byte > byte_advance) {
+ break;
+ }
+
+ /* Move to next sub-block */
+ res = lzw__block_advance(ctx);
+ if (res != LZW_OK) {
+ return res;
+ }
+ }
+ }
+
+ *code_out = (code >> current_bit) & ((1 << code_size) - 1);
+ return LZW_OK;
+}
diff --git a/src/lzw.h b/src/lzw.h
new file mode 100644
index 0000000..0683ad0
--- /dev/null
+++ b/src/lzw.h
@@ -0,0 +1,28 @@
+/*
+ * This file is part of NetSurf's LibNSGIF, http://www.netsurf-browser.org/
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * Copyright 2017 Michael Drake <michael.drake(a)codethink.co.uk>
+ */
+
+#ifndef LZW_H_
+#define LZW_H_
+
+/**
+ * \file
+ * \brief LZW decompression (interface)
+ *
+ * Decoder for GIF LZW data.
+ */
+
+
+/** LZW decoding response codes */
+typedef enum lzw_result {
+ LZW_OK, /**< Success */
+ LZW_OK_EOD, /**< Success; reached zero-length sub-block */
+ LZW_NO_DATA, /**< Error: Out of data */
+} lzw_result;
+
+
+#endif
commitdiff http://git.netsurf-browser.org/libnsgif.git/commit/?id=fe2a38686946c708c2...
commit fe2a38686946c708c2e52031459673a10db41dc8
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
Test tool: Add option to disable output.
diff --git a/test/decode_gif.c b/test/decode_gif.c
index aafe7f6..0ce53c7 100644
--- a/test/decode_gif.c
+++ b/test/decode_gif.c
@@ -135,19 +135,22 @@ static void warning(const char *context, gif_result code)
fprintf(stderr, "\n");
}
-static void write_ppm(FILE* fh, const char *name, gif_animation *gif)
+static void write_ppm(FILE* fh, const char *name, gif_animation *gif,
+ bool no_write)
{
unsigned int i;
gif_result code;
- fprintf(fh, "P3\n");
- fprintf(fh, "# %s\n", name);
- fprintf(fh, "# width %u \n", gif->width);
- fprintf(fh, "# height %u \n", gif->height);
- fprintf(fh, "# frame_count %u \n", gif->frame_count);
- fprintf(fh, "# frame_count_partial %u \n", gif->frame_count_partial);
- fprintf(fh, "# loop_count %u \n", gif->loop_count);
- fprintf(fh, "%u %u 256\n", gif->width, gif->height * gif->frame_count);
+ if (!no_write) {
+ fprintf(fh, "P3\n");
+ fprintf(fh, "# %s\n", name);
+ fprintf(fh, "# width %u \n", gif->width);
+ fprintf(fh, "# height %u \n", gif->height);
+ fprintf(fh, "# frame_count %u \n", gif->frame_count);
+ fprintf(fh, "# frame_count_partial %u \n", gif->frame_count_partial);
+ fprintf(fh, "# loop_count %u \n", gif->loop_count);
+ fprintf(fh, "%u %u 256\n", gif->width, gif->height * gif->frame_count);
+ }
/* decode the frames */
for (i = 0; i != gif->frame_count; i++) {
@@ -158,17 +161,19 @@ static void write_ppm(FILE* fh, const char *name, gif_animation *gif)
if (code != GIF_OK)
warning("gif_decode_frame", code);
- fprintf(fh, "# frame %u:\n", i);
- image = (unsigned char *) gif->frame_image;
- for (row = 0; row != gif->height; row++) {
- for (col = 0; col != gif->width; col++) {
- size_t z = (row * gif->width + col) * 4;
- fprintf(fh, "%u %u %u ",
- (unsigned char) image[z],
- (unsigned char) image[z + 1],
- (unsigned char) image[z + 2]);
+ if (!no_write) {
+ fprintf(fh, "# frame %u:\n", i);
+ image = (unsigned char *) gif->frame_image;
+ for (row = 0; row != gif->height; row++) {
+ for (col = 0; col != gif->width; col++) {
+ size_t z = (row * gif->width + col) * 4;
+ fprintf(fh, "%u %u %u ",
+ (unsigned char) image[z],
+ (unsigned char) image[z + 1],
+ (unsigned char) image[z + 2]);
+ }
+ fprintf(fh, "\n");
}
- fprintf(fh, "\n");
}
}
@@ -189,17 +194,28 @@ int main(int argc, char *argv[])
gif_result code;
unsigned char *data;
FILE *outf = stdout;
+ bool no_write = false;
if (argc < 2) {
fprintf(stderr, "Usage: %s image.gif [out]\n", argv[0]);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "If [out] is NOWRITE, the gif will be docoded "
+ "but not output.\n");
+ fprintf(stderr, "Otherwise [out] is an output filename.\n");
+ fprintf(stderr, "When [out] is unset, output is to stdout.\n");
+
return 1;
}
if (argc > 2) {
- outf = fopen(argv[2], "w+");
- if (outf == NULL) {
- fprintf(stderr, "Unable to open %s for writing\n", argv[2]);
- return 2;
+ if (strcmp(argv[2], "NOWRITE") == 0) {
+ no_write = true;
+ } else {
+ outf = fopen(argv[2], "w+");
+ if (outf == NULL) {
+ fprintf(stderr, "Unable to open %s for writing\n", argv[2]);
+ return 2;
+ }
}
}
@@ -218,9 +234,9 @@ int main(int argc, char *argv[])
}
} while (code != GIF_OK);
- write_ppm(outf, argv[1], &gif);
+ write_ppm(outf, argv[1], &gif, no_write);
- if (argc > 2) {
+ if (argc > 2 && !no_write) {
fclose(outf);
}
-----------------------------------------------------------------------
--
NetSurf GIF Decoder
5 years, 9 months
toolchains: branch master updated. eb01b89e3e2cd6f74053b825cd1a31e07a60dd36
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/toolchains.git/shortlog/eb01b89e3e2cd6f740...
...commit http://git.netsurf-browser.org/toolchains.git/commit/eb01b89e3e2cd6f74053...
...tree http://git.netsurf-browser.org/toolchains.git/tree/eb01b89e3e2cd6f74053b8...
The branch, master has been updated
via eb01b89e3e2cd6f74053b825cd1a31e07a60dd36 (commit)
via 042c35930cc063b68b646765d633d7ffd99fc519 (commit)
from 3b27b283d9f4dd4fcdf0c5c0da40322d9d5e7582 (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/toolchains.git/commit/?id=eb01b89e3e2cd6f7...
commit eb01b89e3e2cd6f74053b825cd1a31e07a60dd36
Author: Chris Young <chris(a)unsatisfactorysoftware.co.uk>
Commit: Chris Young <chris(a)unsatisfactorysoftware.co.uk>
Fix Curl build for m68k-unknown-amigaos
This patch shouldn't be needed; this needs fixing upstream
diff --git a/sdk/recipes/patches/libcurl/m68k-unknown-amigaos/src.tool_operate.c.p b/sdk/recipes/patches/libcurl/m68k-unknown-amigaos/src.tool_operate.c.p
new file mode 100644
index 0000000..b0feb48
--- /dev/null
+++ b/sdk/recipes/patches/libcurl/m68k-unknown-amigaos/src.tool_operate.c.p
@@ -0,0 +1,18 @@
+--- src/tool_operate.c 2017-02-23 22:14:58.000000000 +0000
++++ src/tool_operate.c 2017-03-26 23:54:19.624614960 +0100
+@@ -1742,15 +1742,6 @@ static CURLcode operate_do(struct Global
+ }
+ }
+
+-#ifdef __AMIGA__
+- if(!result && outs.s_isreg && outs.filename) {
+- /* Set the url (up to 80 chars) as comment for the file */
+- if(strlen(url) > 78)
+- url[79] = '\0';
+- SetComment(outs.filename, url);
+- }
+-#endif
+-
+ #if defined(HAVE_UTIME) || \
+ (defined(WIN32) && (CURL_SIZEOF_CURL_OFF_T >= 8))
+ /* File time can only be set _after_ the file has been closed */
commitdiff http://git.netsurf-browser.org/toolchains.git/commit/?id=042c35930cc063b6...
commit 042c35930cc063b68b646765d633d7ffd99fc519
Author: Chris Young <chris(a)unsatisfactorysoftware.co.uk>
Commit: Chris Young <chris(a)unsatisfactorysoftware.co.uk>
Fix OpenSSL build for m68k-unknown-amigaos
diff --git a/sdk/recipes/files/openssl/m68k-unknown-amigaos/crypto/rand/rand_amiga.c b/sdk/recipes/files/openssl/m68k-unknown-amigaos/crypto/rand/rand_amiga.c
index cd3f236..3c18b7d 100644
--- a/sdk/recipes/files/openssl/m68k-unknown-amigaos/crypto/rand/rand_amiga.c
+++ b/sdk/recipes/files/openssl/m68k-unknown-amigaos/crypto/rand/rand_amiga.c
@@ -39,8 +39,15 @@
/* OS3 has a different but compatible TimeVal definition */
struct TimeVal
{
- uint32 Seconds;
- uint32 Microseconds;
+ ULONG Seconds;
+ ULONG Microseconds;
+};
+
+/* ...and ditto for TimeRequest */
+struct TimeRequest
+{
+ struct IORequest Request;
+ struct TimeVal Time;
};
#endif /* !__amigaos4__ */
-----------------------------------------------------------------------
Summary of changes:
.../openssl/m68k-unknown-amigaos/crypto/rand/rand_amiga.c | 11 +++++++++--
.../src.tool_operate.c.p | 0
2 files changed, 9 insertions(+), 2 deletions(-)
copy sdk/recipes/patches/libcurl/{ppc-amigaos => m68k-unknown-amigaos}/src.tool_operate.c.p (100%)
diff --git a/sdk/recipes/files/openssl/m68k-unknown-amigaos/crypto/rand/rand_amiga.c b/sdk/recipes/files/openssl/m68k-unknown-amigaos/crypto/rand/rand_amiga.c
index cd3f236..3c18b7d 100644
--- a/sdk/recipes/files/openssl/m68k-unknown-amigaos/crypto/rand/rand_amiga.c
+++ b/sdk/recipes/files/openssl/m68k-unknown-amigaos/crypto/rand/rand_amiga.c
@@ -39,8 +39,15 @@
/* OS3 has a different but compatible TimeVal definition */
struct TimeVal
{
- uint32 Seconds;
- uint32 Microseconds;
+ ULONG Seconds;
+ ULONG Microseconds;
+};
+
+/* ...and ditto for TimeRequest */
+struct TimeRequest
+{
+ struct IORequest Request;
+ struct TimeVal Time;
};
#endif /* !__amigaos4__ */
diff --git a/sdk/recipes/patches/libcurl/ppc-amigaos/src.tool_operate.c.p b/sdk/recipes/patches/libcurl/m68k-unknown-amigaos/src.tool_operate.c.p
similarity index 100%
copy from sdk/recipes/patches/libcurl/ppc-amigaos/src.tool_operate.c.p
copy to sdk/recipes/patches/libcurl/m68k-unknown-amigaos/src.tool_operate.c.p
--
Cross-compilation toolchains and environments
5 years, 9 months
buildsystem: branch ashmew2/kolibri created. release/1.5-3-g5f994a6
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/buildsystem.git/shortlog/5f994a6f7580e5d06...
...commit http://git.netsurf-browser.org/buildsystem.git/commit/5f994a6f7580e5d064b...
...tree http://git.netsurf-browser.org/buildsystem.git/tree/5f994a6f7580e5d064b4b...
The branch, ashmew2/kolibri has been created
at 5f994a6f7580e5d064b4b9e9578ad51a217c6727 (commit)
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/buildsystem.git/commit/?id=5f994a6f7580e5d...
commit 5f994a6f7580e5d064b4b9e9578ad51a217c6727
Author: Ashish Gupta <ashmew2(a)gmail.com>
Commit: Ashish Gupta <ashmew2(a)gmail.com>
Add support for Cross compiling for KolibriOS from Linux
Patch Makefile.tools to add build time parameters required for kolibrios
diff --git a/makefiles/Makefile.tools b/makefiles/Makefile.tools
index 86fac4c..1250c56 100644
--- a/makefiles/Makefile.tools
+++ b/makefiles/Makefile.tools
@@ -140,6 +140,11 @@ else
CC__ := $(realpath $(toolpath_))
toolpath_ := $(dir $(CC__))
toolprefix_ := $(subst :,/,$(dir $(subst -,/,$(subst /,:,$(CC__)))))
+
+ ifeq ($(HOST),kolibrios)
+ toolprefix_ := $(subst kos32/,kos32,$(toolprefix_))
+ endif
+
ifeq ($(origin AR),default)
AR__ := $(toolprefix_)-ar
endif
@@ -242,6 +247,25 @@ else
PREFIX ?= $(GCCSDK_INSTALL_ENV)
endif
+
+ ifeq ($(HOST),kolibrios)
+ #Cross compiling for KolibriOS (x86 only for now)
+ #expat was added for libdom.
+ #SDL was added for libnsfb.
+ #Both of them should probably be moved to respective library Makefiles.
+ CFLAGS := $(CFLAGS) -c -Wno-error=undef -Wno-error=char-subscripts -Wno-error=pedantic -U__STRICT_ANSI__ -I $(HOME)/kolibrios/contrib/sdk/sources/newlib/libc/include -I $(HOME)/kolibrios/contrib/sdk/sources/newlib/libc/include/machine -I $(HOME)/kolibrios/contrib/sdk/sources/SDL-1.2.2/include -I$(HOME)/kolibrios/contrib/sdk/sources/expat/lib \
+ -std=c99 -g -U_Win32 -U_WIN32 -U__MINGW32__ \
+ -U__BSD_VISIBLE -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=700 \
+ -D_POSIX_C_SOURCE=200809L -D_NETBSD_SOURCE
+
+ CXXFLAGS := $(CXXFLAGS) -U__STRICT_ANSI__ -I$(GCCSDK_INSTALL_ENV)/newlib/libc/include
+
+ LDFLAGS := $(LDFLAGS) -T$(GCCSDK_INSTALL_ENV)/newlib/libc/static.lds -nostdlib -static \
+ -image-base 0 -L/home/autobuild/tools/win32/mingw32/lib -lgcc \
+ /home/autobuild/tools/win32/lib/libz.a /home/autobuild/tools/win32/lib/libdll.a \
+ /home/autobuild/tools/win32/lib/libapp.a /home/autobuild/tools/win32/lib/libc.dll.a
+
+ endif
endif
# Default prefix
-----------------------------------------------------------------------
--
NetSurf Project build system
5 years, 10 months
libnsfb: branch ashmew2/kolibri updated. release/0.1.5-3-ge5f4fcd
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/libnsfb.git/shortlog/e5f4fcde2a7140a82d480...
...commit http://git.netsurf-browser.org/libnsfb.git/commit/e5f4fcde2a7140a82d48086...
...tree http://git.netsurf-browser.org/libnsfb.git/tree/e5f4fcde2a7140a82d4808672...
The branch, ashmew2/kolibri has been updated
via e5f4fcde2a7140a82d480867241fddfb3ef62368 (commit)
via 167205c109291aa1957ba64667efa12ce53bba5d (commit)
via 3271d2753e88ec8ea4e4cbc2eeb0cc88a21071f7 (commit)
via 9fc2238945747b27858a34c26c7c64cf895df7e8 (commit)
via 6742ef25b910a3a69cce5c9bfd8b9a64b8e7f121 (commit)
via bce2c233988e86ad034f6e0c8b96385db0d47a2e (commit)
via 6fe884b805a97e4185703afb62de4da8bdf3edde (commit)
from 18c18d139e7ba8251d6b6c3d62ef1407ff3fc4ad (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/libnsfb.git/commit/?id=e5f4fcde2a7140a82d4...
commit e5f4fcde2a7140a82d480867241fddfb3ef62368
Merge: 18c18d1 167205c
Author: Ashish Gupta <ashmew2(a)gmail.com>
Commit: Ashish Gupta <ashmew2(a)gmail.com>
Merge libnsfb master
-----------------------------------------------------------------------
Summary of changes:
COPYING | 20 +++++++++++++++++
Makefile | 2 +-
README | 45 +++++++++++++++++++++++++++++++++++++
src/plot.h | 55 +++++++++++++++++++++++++++++++++++++++++++++
src/plot/24bpp.c | 46 +++++++++++++++++++++++++------------
src/plot/32bpp-xbgr8888.c | 55 +++++++++++++++++++++++++++++++++------------
src/plot/32bpp-xrgb8888.c | 52 ++++++++++++++++++++++++++++++++----------
7 files changed, 234 insertions(+), 41 deletions(-)
create mode 100644 COPYING
create mode 100644 README
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..a90a8a7
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,20 @@
+Copyright (C) 2009-2016 Vincent Sanders <vince(a)netsurf-browser.org>
+Copyright (C) 2009-2016 Michael Drake <tlsa(a)netsurf-browser.org>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Makefile b/Makefile
index b4eaee6..6c0741c 100644
--- a/Makefile
+++ b/Makefile
@@ -6,7 +6,7 @@
# Component settings
COMPONENT := nsfb
-COMPONENT_VERSION := 0.1.3
+COMPONENT_VERSION := 0.1.5
# Default to a static library
COMPONENT_TYPE ?= lib-static
diff --git a/README b/README
new file mode 100644
index 0000000..732e850
--- /dev/null
+++ b/README
@@ -0,0 +1,45 @@
+Libnsfb - NetSurf Framebuffer Library
+=====================================
+
+Overview
+--------
+
+ Libnsfb is a framebuffer drawing library with a simple interface,
+ providing a consistent API across a number of framebuffer implementations.
+
+Requirements
+------------
+
+ Libnsfb requires the following tools:
+
+ + A C99 capable C compiler
+ + GNU make or compatible
+ + Pkg-config
+
+ Libnsfb also requires the following libraries to be installed:
+
+ + SDL 1.2 (for the SDL backend)
+ + libxcb* (for the X11 backend)
+
+Compilation
+-----------
+
+ If necessary, modify the toolchain settings in the Makefile.
+ Invoke make:
+ $ make
+
+Verification
+------------
+
+ To verify that the parser is working, it is necessary to specify a
+ different makefile target than that used for normal compilation, thus:
+
+ $ make test
+
+API documentation
+-----------------
+
+ Currently, there is none. However, the code is well commented and the
+ public API may be found in the "include" directory. The testcase sources
+ may also be of use in working out how to use it.
+
diff --git a/src/plot.h b/src/plot.h
index 4b1545d..2fd2ebf 100644
--- a/src/plot.h
+++ b/src/plot.h
@@ -1,4 +1,58 @@
+/*
+ * Copyright 2009 Vincent Sanders <vince(a)simtec.co.uk>
+ *
+ * This file is part of libnsfb, http://www.netsurf-browser.org/
+ * Licenced under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ */
+
+/**
+ * \file internal plotter interace.
+ */
+#ifndef LIBNSFB_PLOT_H
+#define LIBNSFB_PLOT_H
+
+/*
+ * Do the best we can to determine integer byte ordering
+ *
+ * This series of tests attempts to determine, at compile time, if the integer
+ * ordering in memory is big or little endian. This allows the plotters to make
+ * assumptions about memory ordering to greatly improve software rendering
+ * performance.
+ *
+ * \note This utterly ignores PDP endianess
+ */
+#undef NSFB_BE_BYTE_ORDER
+#if defined(_WIN32)
+ /* windows does not have endian.h but uses these macros */
+ #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ #define NSFB_BE_BYTE_ORDER
+ #endif
+#elif defined(__APPLE__)
+ /* mac os x has the include somewhere different */
+ #include <machine/endian.h>
+ #if __DARWIN_BYTE_ORDER == __DARWIN_BIG_ENDIAN
+ #define NSFB_BE_BYTE_ORDER
+ #endif
+#else
+ #include <endian.h>
+ #if defined(__BYTE_ORDER__)
+ #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ #define NSFB_BE_BYTE_ORDER
+ #endif
+ #elif defined(__BYTE_ORDER)
+ #if __BYTE_ORDER == __BIG_ENDIAN
+ #define NSFB_BE_BYTE_ORDER
+ #endif
+ #elif defined(BYTE_ORDER)
+ #if BYTE_ORDER == BIG_ENDIAN
+ #define NSFB_BE_BYTE_ORDER
+ #endif
+ #else
+ #error "Endian determination failed"
+ #endif
+#endif
/** Clears plotting area to a flat colour (if needed)
*/
@@ -127,3 +181,4 @@ typedef struct nsfb_plotter_fns_s {
bool select_plotters(nsfb_t *nsfb);
+#endif
diff --git a/src/plot/24bpp.c b/src/plot/24bpp.c
index 011765a..651abe1 100644
--- a/src/plot/24bpp.c
+++ b/src/plot/24bpp.c
@@ -9,13 +9,6 @@
#include <stdbool.h>
#include <stdlib.h>
-#ifndef _WIN32
-#include <endian.h>
-#else
-#define __BYTE_ORDER __BYTE_ORDER__
-#define __BIG_ENDIAN __ORDER_BIG_ENDIAN__
-#endif
-
#include "libnsfb.h"
#include "libnsfb_plot.h"
#include "libnsfb_plot_util.h"
@@ -23,24 +16,43 @@
#include "nsfb.h"
#include "plot.h"
-static inline uint8_t *
-get_xy_loc(nsfb_t *nsfb, int x, int y)
+/**
+ * Get the address of a logical location on the framebuffer
+ */
+static inline uint8_t *get_xy_loc(nsfb_t *nsfb, int x, int y)
{
return (uint8_t *)(nsfb->ptr + (y * nsfb->linelen) + (x * 3));
}
-#if __BYTE_ORDER == __BIG_ENDIAN
-static inline nsfb_colour_t pixel_to_colour(uint8_t pixel)
+#ifdef NSFB_BE_BYTE_ORDER
+
+/**
+ * convert a 24bpp big endian pixel value to netsurf colour
+ */
+static inline nsfb_colour_t pixel_to_colour(uint32_t pixel)
{
return (pixel >> 8) & ~0xFF000000U;
}
-/* convert a colour value to a 32bpp pixel value ready for screen output */
+/**
+ * convert a colour value to a big endian 24bpp pixel value
+ *
+ * The resulting value is ready for screen output
+ */
static inline uint32_t colour_to_pixel(nsfb_colour_t c)
{
return (c << 8);
}
-#else /* __BYTE_ORDER == __BIG_ENDIAN */
+
+#else
+
+/**
+ * convert a 24bpp little endian pixel value to netsurf colour
+ *
+ * \param nsfb The framebuffer
+ * \param pixel The pixel values
+ * \return The netsurf colour value.
+ */
static inline nsfb_colour_t pixel_to_colour(uint32_t pixel)
{
return ((pixel & 0xFF) << 16) |
@@ -48,11 +60,17 @@ static inline nsfb_colour_t pixel_to_colour(uint32_t pixel)
((pixel & 0xFF0000) >> 16);
}
-/* convert a colour value to a 32bpp pixel value ready for screen output */
+/**
+ * convert a colour value to a little endian 24bpp pixel value
+ *
+ * \param c The netsurf colour
+ * \return A pixel value ready for screen output.
+ */
static inline uint32_t colour_to_pixel(nsfb_colour_t c)
{
return ((c & 0xff0000) >> 16) | (c & 0xff00) | ((c & 0xff) << 16);
}
+
#endif
#define SIGN(x) ((x<0) ? -1 : ((x>0) ? 1 : 0))
diff --git a/src/plot/32bpp-xbgr8888.c b/src/plot/32bpp-xbgr8888.c
index a0ed066..2492054 100644
--- a/src/plot/32bpp-xbgr8888.c
+++ b/src/plot/32bpp-xbgr8888.c
@@ -10,13 +10,6 @@
#include <stdbool.h>
#include <stdlib.h>
-#ifndef _WIN32
-#include <endian.h>
-#else
-#define __BYTE_ORDER __BYTE_ORDER__
-#define __BIG_ENDIAN __ORDER_BIG_ENDIAN__
-#endif
-
#include "libnsfb.h"
#include "libnsfb_plot.h"
#include "libnsfb_plot_util.h"
@@ -24,37 +17,71 @@
#include "nsfb.h"
#include "plot.h"
+#define UNUSED __attribute__((unused))
-#define UNUSED __attribute__((unused))
+/**
+ * Get the address of a logical location on the framebuffer
+ */
static inline uint32_t *get_xy_loc(nsfb_t *nsfb, int x, int y)
{
return (void *)(nsfb->ptr + (y * nsfb->linelen) + (x << 2));
}
-#if __BYTE_ORDER == __BIG_ENDIAN
+#ifdef NSFB_BE_BYTE_ORDER
+
+/**
+ * convert a 32bpp big endian pixel value to netsurf colour
+ *
+ * \param nsfb The framebuffer
+ * \param pixel The pixel value
+ * \return The netsurf colour value.
+ */
static inline nsfb_colour_t pixel_to_colour(UNUSED nsfb_t *nsfb, uint32_t pixel)
{
- /* TODO: FIX */
+ /** \todo fix this conversion as it is probably wrong */
return (pixel >> 8) & ~0xFF000000U;
}
-/* convert a colour value to a 32bpp pixel value ready for screen output */
+/**
+ * convert a colour value to a big endian 32bpp pixel value
+ *
+ * \param nsfb The framebuffer
+ * \param c The framebuffer colour
+ * \return A pixel value ready for screen output.
+ */
static inline uint32_t colour_to_pixel(UNUSED nsfb_t *nsfb, nsfb_colour_t c)
{
- return ((c & 0xFF) << 24) | ((c & 0xFF00) << 8) | ((c & 0xFF0000) >> 8);
+ return ((c & 0xFF) << 24) | ((c & 0xFF00) << 8) | ((c & 0xFF0000) >> 8);
}
-#else /* __BYTE_ORDER == __BIG_ENDIAN */
+
+#else
+
+/**
+ * convert a 32bpp little endian pixel value to netsurf colour
+ *
+ * \param nsfb The framebuffer
+ * \param pixel The pixel value
+ * \return The netsurf colour value.
+ */
static inline nsfb_colour_t pixel_to_colour(UNUSED nsfb_t *nsfb, uint32_t pixel)
{
return pixel | 0xFF000000U;
}
-/* convert a colour value to a 32bpp pixel value ready for screen output */
+
+/**
+ * convert a colour value to a little endian 32bpp pixel value
+ *
+ * \param nsfb The framebuffer
+ * \param c The netsurf colour
+ * \return A pixel value ready for screen output.
+ */
static inline uint32_t colour_to_pixel(UNUSED nsfb_t *nsfb, nsfb_colour_t c)
{
return c;
}
+
#endif
#define PLOT_TYPE uint32_t
diff --git a/src/plot/32bpp-xrgb8888.c b/src/plot/32bpp-xrgb8888.c
index 476f6b2..6f77f44 100644
--- a/src/plot/32bpp-xrgb8888.c
+++ b/src/plot/32bpp-xrgb8888.c
@@ -10,13 +10,6 @@
#include <stdbool.h>
#include <stdlib.h>
-#ifndef _WIN32
-#include <endian.h>
-#else
-#define __BYTE_ORDER __BYTE_ORDER__
-#define __BIG_ENDIAN __ORDER_BIG_ENDIAN__
-#endif
-
#include "libnsfb.h"
#include "libnsfb_plot.h"
#include "libnsfb_plot_util.h"
@@ -24,26 +17,53 @@
#include "nsfb.h"
#include "plot.h"
+#define UNUSED __attribute__((unused))
-#define UNUSED __attribute__((unused))
+/**
+ * Get the address of a logical location on the framebuffer
+ */
static inline uint32_t *get_xy_loc(nsfb_t *nsfb, int x, int y)
{
return (void *)(nsfb->ptr + (y * nsfb->linelen) + (x << 2));
}
-#if __BYTE_ORDER == __BIG_ENDIAN
+
+#ifdef NSFB_BE_BYTE_ORDER
+
+/**
+ * convert a 32bpp big endian pixel value to netsurf colour
+ *
+ * \param nsfb The framebuffer
+ * \param pixel The pixel value
+ * \return The netsurf colour value.
+ */
static inline nsfb_colour_t pixel_to_colour(UNUSED nsfb_t *nsfb, uint32_t pixel)
{
return (pixel >> 8) & ~0xFF000000U;
}
-/* convert a colour value to a 32bpp pixel value ready for screen output */
+/**
+ * convert a colour value to a big endian 32bpp pixel value
+ *
+ * \param nsfb The framebuffer
+ * \param c The framebuffer colour
+ * \return A pixel value ready for screen output.
+ */
static inline uint32_t colour_to_pixel(UNUSED nsfb_t *nsfb, nsfb_colour_t c)
{
return (c << 8);
}
-#else /* __BYTE_ORDER == __BIG_ENDIAN */
+
+#else
+
+/**
+ * convert a 32bpp little endian pixel value to netsurf colour
+ *
+ * \param nsfb The framebuffer
+ * \param pixel The pixel value
+ * \return The netsurf colour value.
+ */
static inline nsfb_colour_t pixel_to_colour(UNUSED nsfb_t *nsfb, uint32_t pixel)
{
return ((pixel & 0xFF) << 16) |
@@ -51,11 +71,19 @@ static inline nsfb_colour_t pixel_to_colour(UNUSED nsfb_t *nsfb, uint32_t pixel)
((pixel & 0xFF0000) >> 16);
}
-/* convert a colour value to a 32bpp pixel value ready for screen output */
+
+/**
+ * convert a colour value to a little endian 32bpp pixel value
+ *
+ * \param nsfb The framebuffer
+ * \param c The netsurf colour
+ * \return A pixel value ready for screen output.
+ */
static inline uint32_t colour_to_pixel(UNUSED nsfb_t *nsfb, nsfb_colour_t c)
{
return ((c & 0xff0000) >> 16) | (c & 0xff00) | ((c & 0xff) << 16);
}
+
#endif
#define PLOT_TYPE uint32_t
--
NetSurf Framebuffer library
5 years, 10 months