updale libnsgif from upstream

seems slightly slower overall
This commit is contained in:
John Cupitt 2021-10-07 10:48:12 +01:00
parent 1192d87acf
commit 60bae63644
4 changed files with 244 additions and 172 deletions

View File

@ -8,9 +8,9 @@ but within the libvips build system.
Run `./update.sh` to update this copy of libnsgif from the upstream repo. It
will also patch libnsgif.c to prevent it modifying the input.
Last updated 28 Feb 2021.
Last updated 7 Oct 2021.
# To do
No attempt made to run tests or build docs. Though the gif loader is tested as
part of the libvips test suite.
No attempt made to run tests or build docs. Though the gif loader is tested
as part of the libvips test suite.

View File

@ -549,6 +549,8 @@ static gif_result gif_error_from_lzw(lzw_result l_res)
[LZW_BAD_ICODE] = GIF_FRAME_DATA_ERROR,
[LZW_BAD_CODE] = GIF_FRAME_DATA_ERROR,
};
assert(l_res != LZW_BAD_PARAM);
assert(l_res != LZW_NO_COLOUR);
return g_res[l_res];
}
@ -638,9 +640,8 @@ gif__decode_complex(gif_animation *gif,
lzw_result res;
/* Initialise the LZW decoding */
res = lzw_decode_init(gif->lzw_ctx, gif->gif_data,
gif->buffer_size, gif->buffer_position,
minimum_code_size);
res = lzw_decode_init(gif->lzw_ctx, minimum_code_size,
gif->gif_data, gif->buffer_size, gif->buffer_position);
if (res != LZW_OK) {
return gif_error_from_lzw(res);
}
@ -675,20 +676,28 @@ gif__decode_complex(gif_animation *gif,
}
break;
}
res = lzw_decode_continuous(gif->lzw_ctx,
res = lzw_decode(gif->lzw_ctx,
&uncompressed, &available);
}
row_available = x < available ? x : available;
x -= row_available;
available -= row_available;
while (row_available-- > 0) {
register unsigned int colour;
colour = *uncompressed++;
if (colour != transparency_index) {
*frame_scanline = colour_table[colour];
if (transparency_index > 0xFF) {
while (row_available-- > 0) {
*frame_scanline++ =
colour_table[*uncompressed++];
}
} else {
while (row_available-- > 0) {
register unsigned int colour;
colour = *uncompressed++;
if (colour != transparency_index) {
*frame_scanline =
colour_table[colour];
}
frame_scanline++;
}
frame_scanline++;
}
}
}
@ -710,23 +719,22 @@ gif__decode_simple(gif_animation *gif,
gif_result ret = GIF_OK;
lzw_result res;
/* Initialise the LZW decoding */
res = lzw_decode_init(gif->lzw_ctx, gif->gif_data,
gif->buffer_size, gif->buffer_position,
minimum_code_size);
if (res != LZW_OK) {
return gif_error_from_lzw(res);
}
transparency_index = gif->frames[frame].transparency ?
gif->frames[frame].transparency_index :
GIF_NO_TRANSPARENCY;
/* Initialise the LZW decoding */
res = lzw_decode_init_map(gif->lzw_ctx,
minimum_code_size, transparency_index, colour_table,
gif->gif_data, gif->buffer_size, gif->buffer_position);
if (res != LZW_OK) {
return gif_error_from_lzw(res);
}
frame_data += (offset_y * gif->width);
while (pixels > 0) {
res = lzw_decode_map_continuous(gif->lzw_ctx,
transparency_index, colour_table,
res = lzw_decode_map(gif->lzw_ctx,
frame_data, pixels, &written);
pixels -= written;
frame_data += written;

View File

@ -68,31 +68,32 @@ struct lzw_table_entry {
struct lzw_ctx {
struct lzw_read_ctx input; /**< Input reading context */
uint32_t prev_code; /**< Code read from input previously. */
uint32_t prev_code_first; /**< First value of previous code. */
uint32_t prev_code_count; /**< Total values for previous code. */
uint16_t prev_code; /**< Code read from input previously. */
uint16_t prev_code_first; /**< First value of previous code. */
uint16_t prev_code_count; /**< Total values for previous code. */
uint32_t initial_code_size; /**< Starting LZW code size. */
uint8_t initial_code_size; /**< Starting LZW code size. */
uint32_t code_size; /**< Current LZW code size. */
uint32_t code_max; /**< Max code value for current code size. */
uint8_t code_size; /**< Current LZW code size. */
uint16_t code_max; /**< Max code value for current code size. */
uint32_t clear_code; /**< Special Clear code value */
uint32_t eoi_code; /**< Special End of Information code value */
uint16_t clear_code; /**< Special Clear code value */
uint16_t eoi_code; /**< Special End of Information code value */
uint32_t table_size; /**< Next position in table to fill. */
uint16_t table_size; /**< Next position in table to fill. */
uint32_t output_code; /**< Code that has been partially output. */
uint32_t output_left; /**< Number of values left for output_code. */
uint16_t output_code; /**< Code that has been partially output. */
uint16_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];
bool has_transparency; /**< Whether the image is opaque. */
uint8_t transparency_idx; /**< Index representing transparency. */
const uint32_t *restrict colour_map; /**< Index to colour mapping. */
/** LZW code table. Generated during decode. */
struct lzw_table_entry table[LZW_TABLE_ENTRY_MAX];
/** Output value stack. */
uint8_t stack_base[LZW_TABLE_ENTRY_MAX];
};
@ -165,8 +166,8 @@ static lzw_result lzw__block_advance(struct lzw_read_ctx *restrict ctx)
*/
static inline lzw_result lzw__read_code(
struct lzw_read_ctx *restrict ctx,
uint32_t code_size,
uint32_t *restrict code_out)
uint16_t code_size,
uint16_t *restrict code_out)
{
uint32_t code = 0;
uint32_t current_bit = ctx->sb_bit & 0x7;
@ -232,9 +233,9 @@ static inline lzw_result lzw__read_code(
*/
static inline lzw_result lzw__handle_clear(
struct lzw_ctx *ctx,
uint32_t *code_out)
uint16_t *code_out)
{
uint32_t code;
uint16_t code;
/* Reset table building context */
ctx->code_size = ctx->initial_code_size;
@ -259,27 +260,26 @@ static inline lzw_result lzw__handle_clear(
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 minimum_code_size)
uint8_t minimum_code_size,
const uint8_t *input_data,
uint32_t input_length,
uint32_t input_pos)
{
struct lzw_table_entry *table = ctx->table;
lzw_result res;
uint32_t code;
uint16_t code;
if (minimum_code_size >= LZW_CODE_MAX) {
return LZW_BAD_ICODE;
}
/* 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.data = input_data;
ctx->input.data_len = input_length;
ctx->input.data_sb_next = input_pos;
ctx->input.sb_bit = 0;
ctx->input.sb_bit_count = 0;
@ -293,7 +293,7 @@ lzw_result lzw_decode_init(
ctx->output_left = 0;
/* Initialise the standard table entries */
for (uint32_t i = 0; i < ctx->clear_code; ++i) {
for (uint16_t i = 0; i < ctx->clear_code; i++) {
table[i].first = i;
table[i].value = i;
table[i].count = 1;
@ -313,6 +313,39 @@ lzw_result lzw_decode_init(
ctx->output_code = code;
ctx->output_left = 1;
ctx->has_transparency = false;
ctx->transparency_idx = 0;
ctx->colour_map = NULL;
return LZW_OK;
}
/* Exported function, documented in lzw.h */
lzw_result lzw_decode_init_map(
struct lzw_ctx *ctx,
uint8_t minimum_code_size,
uint32_t transparency_idx,
const uint32_t *colour_table,
const uint8_t *input_data,
uint32_t input_length,
uint32_t input_pos)
{
lzw_result res;
if (colour_table == NULL) {
return LZW_BAD_PARAM;
}
res = lzw_decode_init(ctx, minimum_code_size,
input_data, input_length, input_pos);
if (res != LZW_OK) {
return res;
}
ctx->has_transparency = (transparency_idx <= 0xFF);
ctx->transparency_idx = transparency_idx;
ctx->colour_map = colour_table;
return LZW_OK;
}
@ -324,7 +357,7 @@ lzw_result lzw_decode_init(
*/
static inline void lzw__table_add_entry(
struct lzw_ctx *ctx,
uint32_t code)
uint16_t code)
{
struct lzw_table_entry *entry = &ctx->table[ctx->table_size];
@ -338,30 +371,31 @@ static inline void lzw__table_add_entry(
typedef uint32_t (*lzw_writer_fn)(
struct lzw_ctx *ctx,
void *restrict output,
uint32_t length,
uint32_t used,
uint32_t code,
uint32_t left);
void *restrict output_data,
uint32_t output_length,
uint32_t output_pos,
uint16_t code,
uint16_t left);
/**
* Get the next LZW code and write its value(s) to output buffer.
*
* \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] write_pixels Function for writing pixels to output.
* \param[in,out] used Number of values written. Updated on exit.
* \param[in] ctx LZW reading context, updated.
* \param[in] write_fn Function for writing pixels to output.
* \param[in] output_data Array to write output values into.
* \param[in] output_length Size of output array.
* \param[in,out] output_written Number of values written. Updated on exit.
* \return LZW_OK on success, or appropriate error code otherwise.
*/
static inline lzw_result lzw__decode(struct lzw_ctx *ctx,
void *restrict output,
uint32_t length,
lzw_writer_fn write_pixels,
uint32_t *restrict used)
static inline lzw_result lzw__decode(
struct lzw_ctx *ctx,
lzw_writer_fn write_fn,
void *restrict output_data,
uint32_t output_length,
uint32_t *restrict output_written)
{
lzw_result res;
uint32_t code;
uint16_t code;
/* Get a new code from the input */
res = lzw__read_code(&ctx->input, ctx->code_size, &code);
@ -385,7 +419,7 @@ static inline lzw_result lzw__decode(struct lzw_ctx *ctx,
}
} else if (ctx->table_size < LZW_TABLE_ENTRY_MAX) {
uint32_t size = ctx->table_size;
uint16_t size = ctx->table_size;
lzw__table_add_entry(ctx, (code < size) ?
ctx->table[code].first :
ctx->prev_code_first);
@ -397,8 +431,9 @@ static inline lzw_result lzw__decode(struct lzw_ctx *ctx,
}
}
*used += write_pixels(ctx, output, length, *used, code,
ctx->table[code].count);
*output_written += write_fn(ctx,
output_data, output_length, *output_written,
code, ctx->table[code].count);
/* Store details of this code as "previous code" to the context. */
ctx->prev_code_first = ctx->table[code].first;
@ -416,25 +451,25 @@ static inline lzw_result lzw__decode(struct lzw_ctx *ctx,
* 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.
* \param[in] ctx LZW reading context, updated.
* \param[in] output_data Array to write output values into.
* \param[in] output_length length Size of output array.
* \param[in] output_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 code.
* \return Number of pixel values written.
*/
static inline uint32_t lzw__write_pixels(struct lzw_ctx *ctx,
void *restrict output,
uint32_t length,
uint32_t used,
uint32_t code,
uint32_t left)
static inline uint32_t lzw__write_fn(struct lzw_ctx *ctx,
void *restrict output_data,
uint32_t output_length,
uint32_t output_used,
uint16_t code,
uint16_t left)
{
uint8_t *restrict output_pos = (uint8_t *)output + used;
uint8_t *restrict output_pos = (uint8_t *)output_data + output_used;
const struct lzw_table_entry * const table = ctx->table;
uint32_t space = length - used;
uint32_t count = left;
uint32_t space = output_length - output_used;
uint16_t count = left;
if (count > space) {
left = count - space;
@ -463,23 +498,24 @@ static inline uint32_t lzw__write_pixels(struct lzw_ctx *ctx,
}
/* Exported function, documented in lzw.h */
lzw_result lzw_decode_continuous(struct lzw_ctx *ctx,
const uint8_t *restrict *const restrict data,
uint32_t *restrict used)
lzw_result lzw_decode(struct lzw_ctx *ctx,
const uint8_t *restrict *const restrict output_data,
uint32_t *restrict output_written)
{
*used = 0;
*data = ctx->stack_base;
const uint32_t output_length = sizeof(ctx->stack_base);
*output_written = 0;
*output_data = ctx->stack_base;
if (ctx->output_left != 0) {
*used += lzw__write_pixels(ctx,
ctx->stack_base, sizeof(ctx->stack_base), *used,
*output_written += lzw__write_fn(ctx,
ctx->stack_base, output_length, *output_written,
ctx->output_code, ctx->output_left);
}
while (*used != sizeof(ctx->stack_base)) {
lzw_result res = lzw__decode(ctx,
ctx->stack_base, sizeof(ctx->stack_base),
lzw__write_pixels, used);
while (*output_written != output_length) {
lzw_result res = lzw__decode(ctx, lzw__write_fn,
ctx->stack_base, output_length, output_written);
if (res != LZW_OK) {
return res;
}
@ -489,32 +525,32 @@ lzw_result lzw_decode_continuous(struct lzw_ctx *ctx,
}
/**
* Write colour mapped values for this code to the output stack.
* Write colour mapped values for this code to the output.
*
* 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.
* \param[in] ctx LZW reading context, updated.
* \param[in] output_data Array to write output values into.
* \param[in] output_length Size of output array.
* \param[in] output_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 code.
* \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)
static inline uint32_t lzw__map_write_fn(struct lzw_ctx *ctx,
void *restrict output_data,
uint32_t output_length,
uint32_t output_used,
uint16_t code,
uint16_t left)
{
uint32_t *restrict stack_pos = (uint32_t *)buffer + used;
uint32_t *restrict output_pos = (uint32_t *)output_data + output_used;
const struct lzw_table_entry * const table = ctx->table;
uint32_t space = length - used;
uint32_t count = left;
uint32_t space = output_length - output_used;
uint16_t count = left;
if (count > space) {
left = count - space;
@ -531,40 +567,48 @@ static inline uint32_t lzw__write_pixels_map(struct lzw_ctx *ctx,
code = entry->extends;
}
stack_pos += count;
for (unsigned i = count; i != 0; i--) {
const struct lzw_table_entry *entry = table + code;
--stack_pos;
if (entry->value != ctx->transparency_idx) {
*stack_pos = ctx->colour_map[entry->value];
output_pos += count;
if (ctx->has_transparency) {
for (unsigned i = count; i != 0; i--) {
const struct lzw_table_entry *entry = table + code;
--output_pos;
if (entry->value != ctx->transparency_idx) {
*output_pos = ctx->colour_map[entry->value];
}
code = entry->extends;
}
} else {
for (unsigned i = count; i != 0; i--) {
const struct lzw_table_entry *entry = table + code;
*--output_pos = ctx->colour_map[entry->value];
code = entry->extends;
}
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)
lzw_result lzw_decode_map(struct lzw_ctx *ctx,
uint32_t *restrict output_data,
uint32_t output_length,
uint32_t *restrict output_written)
{
*used = 0;
*output_written = 0;
ctx->transparency_idx = transparency_idx;
ctx->colour_map = colour_map;
if (ctx->colour_map == NULL) {
return LZW_NO_COLOUR;
}
if (ctx->output_left != 0) {
*used += lzw__write_pixels_map(ctx, data, length, *used,
*output_written += lzw__map_write_fn(ctx,
output_data, output_length, *output_written,
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);
while (*output_written != output_length) {
lzw_result res = lzw__decode(ctx, lzw__map_write_fn,
output_data, output_length, output_written);
if (res != LZW_OK) {
return res;
}

View File

@ -16,7 +16,6 @@
* Decoder for GIF LZW data.
*/
/** Maximum LZW code size in bits */
#define LZW_CODE_MAX 12
@ -32,7 +31,9 @@ typedef enum lzw_result {
LZW_NO_MEM, /**< Error: Out of memory */
LZW_NO_DATA, /**< Error: Out of data */
LZW_EOI_CODE, /**< Error: End of Information code */
LZW_NO_COLOUR, /**< Error: No colour map provided. */
LZW_BAD_ICODE, /**< Error: Bad initial LZW code */
LZW_BAD_PARAM, /**< Error: Bad function parameter. */
LZW_BAD_CODE, /**< Error: Bad LZW code */
} lzw_result;
@ -58,62 +59,81 @@ void lzw_context_destroy(
/**
* 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] minimum_code_size The LZW Minimum Code Size.
* \param[out] stack_base_out Returns base of decompressed data stack.
* \param[in] ctx The LZW decompression context to initialise.
* \param[in] minimum_code_size The LZW Minimum Code Size.
* \param[in] input_data The compressed data.
* \param[in] input_length Byte length of compressed data.
* \param[in] input_pos Start position in data. Must be position
* of a size byte at sub-block start.
* \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 minimum_code_size);
uint8_t minimum_code_size,
const uint8_t *input_data,
uint32_t input_length,
uint32_t input_pos);
/**
* Read input codes until end of lzw context owned output buffer.
* Read input codes until end of LZW context owned output buffer.
*
* Ensure anything in output is used before calling this, as anything
* on the there before this call will be trampled.
* there before this call will be trampled.
*
* \param[in] ctx LZW reading context, updated.
* \param[out] data Returns pointer to array of output values.
* \param[out] used Returns the number of values written to data.
* \param[in] ctx LZW reading context, updated.
* \param[out] output_data Returns pointer to array of output values.
* \param[out] output_written Returns the number of values written to data.
* \return LZW_OK on success, or appropriate error code otherwise.
*/
lzw_result lzw_decode_continuous(struct lzw_ctx *ctx,
const uint8_t *restrict *const restrict data,
uint32_t *restrict used);
lzw_result lzw_decode(struct lzw_ctx *ctx,
const uint8_t *restrict *const restrict output_data,
uint32_t *restrict output_written);
/**
* Read LZW codes into client buffer, mapping output to colours.
*
* Ensure anything in output is used before calling this, as anything
* on the there before this call will be trampled.
* Initialise an LZW decompression context for decoding to colour map values.
*
* For transparency to work correctly, the given client buffer must have
* the values from the previous frame. The transparency_idx should be a value
* of 256 or above, if the frame does not have transparency.
*
* \param[in] ctx LZW reading context, updated.
* \param[in] transparency_idx Index representing transparency.
* \param[in] colour_map Index to pixel colour mapping
* \param[in] data Client buffer to fill with colour mapped values.
* \param[in] length Size of output array.
* \param[out] used Returns the number of values written to data.
* \param[in] ctx The LZW decompression context to initialise.
* \param[in] minimum_code_size The LZW Minimum Code Size.
* \param[in] transparency_idx Index representing transparency.
* \param[in] colour_table Index to pixel colour mapping.
* \param[in] input_data The compressed data.
* \param[in] input_length Byte length of compressed data.
* \param[in] input_pos Start position in data. Must be position
* of a size byte at sub-block start.
* \return LZW_OK on success, or appropriate error code otherwise.
*/
lzw_result lzw_decode_map_continuous(struct lzw_ctx *ctx,
lzw_result lzw_decode_init_map(
struct lzw_ctx *ctx,
uint8_t minimum_code_size,
uint32_t transparency_idx,
uint32_t *restrict colour_table,
uint32_t *restrict data,
uint32_t length,
uint32_t *restrict used);
const uint32_t *colour_table,
const uint8_t *input_data,
uint32_t input_length,
uint32_t input_pos);
/**
* Read LZW codes into client buffer, mapping output to colours.
*
* The context must have been initialised using \ref lzw_decode_init_map
* before calling this function, in order to provide the colour mapping table
* and any transparency index.
*
* Ensure anything in output is used before calling this, as anything
* there before this call will be trampled.
*
* \param[in] ctx LZW reading context, updated.
* \param[in] output_data Client buffer to fill with colour mapped values.
* \param[in] output_length Size of output array.
* \param[out] output_written Returns the number of values written to data.
* \return LZW_OK on success, or appropriate error code otherwise.
*/
lzw_result lzw_decode_map(struct lzw_ctx *ctx,
uint32_t *restrict output_data,
uint32_t output_length,
uint32_t *restrict output_written);
#endif