diff options
author | Michael Drake <tlsa@netsurf-browser.org> | 2021-04-04 23:40:01 +0100 |
---|---|---|
committer | Michael Drake <tlsa@netsurf-browser.org> | 2021-04-18 17:28:12 +0100 |
commit | decfeceb211697b18b2525ec533a95ed03418e1d (patch) | |
tree | 5ea3d3b1b6b7251d9df87d48021a88a58a58798f /src/lzw.c | |
parent | db49e5b61beb1759469f3e6bd53850c6bc0a38ac (diff) | |
download | libnsgif-decfeceb211697b18b2525ec533a95ed03418e1d.tar.gz libnsgif-decfeceb211697b18b2525ec533a95ed03418e1d.tar.bz2 |
lzw: Direct output into frame data, avoiding stack.
If the frame is non-interlaced, and has the same rowstride as the
full image, then we can decode lzw directly into the output image.
Diffstat (limited to 'src/lzw.c')
-rw-r--r-- | src/lzw.c | 90 |
1 files changed, 89 insertions, 1 deletions
@@ -85,6 +85,9 @@ struct lzw_ctx { uint32_t output_code; /**< Code that has been partially output. */ uint32_t output_left; /**< Number of values left for output_code. */ + uint32_t transparency_idx; /**< Index representing transparency. */ + uint32_t *restrict colour_map; /**< Index to pixel colour mapping */ + /** Output value stack. */ uint8_t stack_base[LZW_TABLE_ENTRY_MAX]; @@ -320,7 +323,7 @@ typedef uint32_t (*lzw_writer_fn)( * \return LZW_OK on success, or appropriate error code otherwise. */ static inline lzw_result lzw__decode(struct lzw_ctx *ctx, - uint8_t *restrict output, + void *restrict output, uint32_t length, lzw_writer_fn write_pixels, uint32_t *restrict used) @@ -463,3 +466,88 @@ lzw_result lzw_decode_continuous(struct lzw_ctx *ctx, return LZW_OK; } + +/** + * Write colour mapped values for this code to the output stack. + * + * If there isn't enough space in the output stack, this function will write + * the as many as it can into the output. If `ctx->output_left > 0` after + * this call, then there is more data for this code left to output. The code + * is stored to the context as `ctx->output_code`. + * + * \param[in] ctx LZW reading context, updated. + * \param[in] output Array to write output values into. + * \param[in] length Size of output array. + * \param[in] used Current position in output array. + * \param[in] code LZW code to output values for. + * \param[in] left Number of values remaining to output for this value. + * \return Number of pixel values written. + */ +static inline uint32_t lzw__write_pixels_map(struct lzw_ctx *ctx, + void *restrict buffer, + uint32_t length, + uint32_t used, + uint32_t code, + uint32_t left) +{ + uint32_t *restrict stack_pos = (uint32_t *)buffer + used; + struct lzw_table_entry * const table = ctx->table; + uint32_t space = length - used; + uint32_t count = left; + + if (count > space) { + left = count - space; + count = space; + } else { + left = 0; + } + + ctx->output_code = code; + ctx->output_left = left; + + for (unsigned i = left; i != 0; i--) { + struct lzw_table_entry *entry = table + code; + code = entry->extends; + } + + stack_pos += count; + for (unsigned i = count; i != 0; i--) { + struct lzw_table_entry *entry = table + code; + --stack_pos; + if (entry->value != ctx->transparency_idx) { + *stack_pos = ctx->colour_map[entry->value]; + } + code = entry->extends; + } + + return count; +} + +/* Exported function, documented in lzw.h */ +lzw_result lzw_decode_map_continuous(struct lzw_ctx *ctx, + uint32_t transparency_idx, + uint32_t *restrict colour_map, + uint32_t *restrict data, + uint32_t length, + uint32_t *restrict used) +{ + *used = 0; + + ctx->transparency_idx = transparency_idx; + ctx->colour_map = colour_map; + + if (ctx->output_left != 0) { + *used += lzw__write_pixels_map(ctx, data, length, *used, + ctx->output_code, ctx->output_left); + } + + while (*used != sizeof(ctx->stack_base)) { + lzw_result res = lzw__decode(ctx, data, length, + lzw__write_pixels_map, used); + if (res != LZW_OK) { + return res; + } + } + + return LZW_OK; +} |