Gitweb links:

...log 
http://git.netsurf-browser.org/libnsgif.git/shortlog/f9797e4ae82090808036b1e1ec4318d0f1bdc456
...commit 
http://git.netsurf-browser.org/libnsgif.git/commit/f9797e4ae82090808036b1e1ec4318d0f1bdc456
...tree 
http://git.netsurf-browser.org/libnsgif.git/tree/f9797e4ae82090808036b1e1ec4318d0f1bdc456

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=f9797e4ae82090808036b1e1ec4318d0f1bdc456
commit f9797e4ae82090808036b1e1ec4318d0f1bdc456
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    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=34f3a9a81824dcda5a3e1944d14b0273298ec0c3
commit 34f3a9a81824dcda5a3e1944d14b0273298ec0c3
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    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=86ebd63cc1e9570cf247da26635375e31fc57d40
commit 86ebd63cc1e9570cf247da26635375e31fc57d40
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    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

_______________________________________________
netsurf-commits mailing list
[email protected]
http://listmaster.pepperfish.net/cgi-bin/mailman/listinfo/netsurf-commits-netsurf-browser.org

Reply via email to