Update LibNSGIF to latest upstream (#2791)
* libnsgif: Update to latest upstream * nsgifload: Add palette info to debug output.
This commit is contained in:
parent
460a19b78e
commit
ceaa2d2096
@ -8,7 +8,7 @@ but within the libvips build system.
|
|||||||
Run `./update.sh` to update this copy of libnsgif from the upstream repo. It
|
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.
|
will also patch libnsgif.c to prevent it modifying the input.
|
||||||
|
|
||||||
Last updated 15 Apr 2022.
|
Last updated 8 May 2022.
|
||||||
|
|
||||||
# To do
|
# To do
|
||||||
|
|
||||||
|
@ -17,9 +17,6 @@
|
|||||||
#include "lzw.h"
|
#include "lzw.h"
|
||||||
#include "nsgif.h"
|
#include "nsgif.h"
|
||||||
|
|
||||||
/** Maximum colour table size */
|
|
||||||
#define NSGIF_MAX_COLOURS 256
|
|
||||||
|
|
||||||
/** Default minimum allowable frame delay in cs. */
|
/** Default minimum allowable frame delay in cs. */
|
||||||
#define NSGIF_FRAME_DELAY_MIN 2
|
#define NSGIF_FRAME_DELAY_MIN 2
|
||||||
|
|
||||||
@ -35,7 +32,7 @@ typedef struct nsgif_frame {
|
|||||||
struct nsgif_frame_info info;
|
struct nsgif_frame_info info;
|
||||||
|
|
||||||
/** offset (in bytes) to the GIF frame data */
|
/** offset (in bytes) to the GIF frame data */
|
||||||
uint32_t frame_pointer;
|
uint32_t frame_offset;
|
||||||
/** whether the frame has previously been decoded. */
|
/** whether the frame has previously been decoded. */
|
||||||
bool decoded;
|
bool decoded;
|
||||||
/** whether the frame is totally opaque */
|
/** whether the frame is totally opaque */
|
||||||
@ -46,6 +43,9 @@ typedef struct nsgif_frame {
|
|||||||
/** the index designating a transparent pixel */
|
/** the index designating a transparent pixel */
|
||||||
uint32_t transparency_index;
|
uint32_t transparency_index;
|
||||||
|
|
||||||
|
/** offset to frame colour table */
|
||||||
|
uint32_t colour_table_offset;
|
||||||
|
|
||||||
/* Frame flags */
|
/* Frame flags */
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
} nsgif_frame;
|
} nsgif_frame;
|
||||||
@ -72,8 +72,11 @@ struct nsgif {
|
|||||||
uint32_t frame;
|
uint32_t frame;
|
||||||
/** current frame decoded to bitmap */
|
/** current frame decoded to bitmap */
|
||||||
uint32_t decoded_frame;
|
uint32_t decoded_frame;
|
||||||
|
|
||||||
/** currently decoded image; stored as bitmap from bitmap_create callback */
|
/** currently decoded image; stored as bitmap from bitmap_create callback */
|
||||||
nsgif_bitmap_t *frame_image;
|
nsgif_bitmap_t *frame_image;
|
||||||
|
/** Row span of frame_image in pixels. */
|
||||||
|
uint32_t rowspan;
|
||||||
|
|
||||||
/** Minimum allowable frame delay. */
|
/** Minimum allowable frame delay. */
|
||||||
uint16_t delay_min;
|
uint16_t delay_min;
|
||||||
@ -100,11 +103,9 @@ struct nsgif {
|
|||||||
uint32_t bg_index;
|
uint32_t bg_index;
|
||||||
/** image aspect ratio (ignored) */
|
/** image aspect ratio (ignored) */
|
||||||
uint32_t aspect_ratio;
|
uint32_t aspect_ratio;
|
||||||
/** size of colour table (in entries) */
|
/** size of global colour table (in entries) */
|
||||||
uint32_t colour_table_size;
|
uint32_t colour_table_size;
|
||||||
|
|
||||||
/** whether the GIF has a global colour table */
|
|
||||||
bool global_colours;
|
|
||||||
/** current colour table */
|
/** current colour table */
|
||||||
uint32_t *colour_table;
|
uint32_t *colour_table;
|
||||||
/** Client's colour component order. */
|
/** Client's colour component order. */
|
||||||
@ -223,6 +224,11 @@ static inline uint32_t* nsgif__bitmap_get(
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gif->rowspan = gif->info.width;
|
||||||
|
if (gif->bitmap.get_rowspan) {
|
||||||
|
gif->rowspan = gif->bitmap.get_rowspan(gif->frame_image);
|
||||||
|
}
|
||||||
|
|
||||||
/* Get the frame data */
|
/* Get the frame data */
|
||||||
assert(gif->bitmap.get_buffer);
|
assert(gif->bitmap.get_buffer);
|
||||||
return (void *)gif->bitmap.get_buffer(gif->frame_image);
|
return (void *)gif->bitmap.get_buffer(gif->frame_image);
|
||||||
@ -463,7 +469,7 @@ static nsgif_error nsgif__decode_complex(
|
|||||||
uint32_t *frame_scanline;
|
uint32_t *frame_scanline;
|
||||||
|
|
||||||
frame_scanline = frame_data + offset_x +
|
frame_scanline = frame_data + offset_x +
|
||||||
(y + offset_y) * gif->info.width;
|
(y + offset_y) * gif->rowspan;
|
||||||
|
|
||||||
x = width;
|
x = width;
|
||||||
while (x > 0) {
|
while (x > 0) {
|
||||||
@ -594,7 +600,9 @@ static inline nsgif_error nsgif__decode(
|
|||||||
uint32_t transparency_index = frame->transparency_index;
|
uint32_t transparency_index = frame->transparency_index;
|
||||||
uint32_t *restrict colour_table = gif->colour_table;
|
uint32_t *restrict colour_table = gif->colour_table;
|
||||||
|
|
||||||
if (interlace == false && width == gif->info.width && offset_x == 0) {
|
if (interlace == false && offset_x == 0 &&
|
||||||
|
width == gif->info.width &&
|
||||||
|
width == gif->rowspan) {
|
||||||
ret = nsgif__decode_simple(gif, height, offset_y,
|
ret = nsgif__decode_simple(gif, height, offset_y,
|
||||||
data, transparency_index,
|
data, transparency_index,
|
||||||
frame_data, colour_table);
|
frame_data, colour_table);
|
||||||
@ -1031,6 +1039,38 @@ static nsgif_error nsgif__parse_image_descriptor(
|
|||||||
return NSGIF_OK;
|
return NSGIF_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract a GIF colour table into a LibNSGIF colour table buffer.
|
||||||
|
*
|
||||||
|
* \param[in] colour_table The colour table to populate.
|
||||||
|
* \param[in] layout la.
|
||||||
|
* \param[in] colour_table_entries The number of colour table entries.
|
||||||
|
* \param[in] Data Raw colour table data.
|
||||||
|
*/
|
||||||
|
static void nsgif__colour_table_decode(
|
||||||
|
uint32_t colour_table[NSGIF_MAX_COLOURS],
|
||||||
|
const struct nsgif_colour_layout *layout,
|
||||||
|
size_t colour_table_entries,
|
||||||
|
const uint8_t *data)
|
||||||
|
{
|
||||||
|
uint8_t *entry = (uint8_t *)colour_table;
|
||||||
|
|
||||||
|
while (colour_table_entries--) {
|
||||||
|
/* Gif colour map contents are r,g,b.
|
||||||
|
*
|
||||||
|
* We want to pack them bytewise into the colour table,
|
||||||
|
* according to the client colour layout.
|
||||||
|
*/
|
||||||
|
|
||||||
|
entry[layout->r] = *data++;
|
||||||
|
entry[layout->g] = *data++;
|
||||||
|
entry[layout->b] = *data++;
|
||||||
|
entry[layout->a] = 0xff;
|
||||||
|
|
||||||
|
entry += sizeof(uint32_t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract a GIF colour table into a LibNSGIF colour table buffer.
|
* Extract a GIF colour table into a LibNSGIF colour table buffer.
|
||||||
*
|
*
|
||||||
@ -1041,42 +1081,25 @@ static nsgif_error nsgif__parse_image_descriptor(
|
|||||||
* \param[in] decode Whether to decode the colour table.
|
* \param[in] decode Whether to decode the colour table.
|
||||||
* \return NSGIF_OK on success, appropriate error otherwise.
|
* \return NSGIF_OK on success, appropriate error otherwise.
|
||||||
*/
|
*/
|
||||||
static nsgif_error nsgif__colour_table_extract(
|
static inline nsgif_error nsgif__colour_table_extract(
|
||||||
struct nsgif *gif,
|
|
||||||
uint32_t colour_table[NSGIF_MAX_COLOURS],
|
uint32_t colour_table[NSGIF_MAX_COLOURS],
|
||||||
const struct nsgif_colour_layout *layout,
|
const struct nsgif_colour_layout *layout,
|
||||||
size_t colour_table_entries,
|
size_t colour_table_entries,
|
||||||
const uint8_t **pos,
|
const uint8_t *data,
|
||||||
|
size_t data_len,
|
||||||
|
size_t *used,
|
||||||
bool decode)
|
bool decode)
|
||||||
{
|
{
|
||||||
const uint8_t *data = *pos;
|
if (data_len < colour_table_entries * 3) {
|
||||||
size_t len = gif->buf + gif->buf_len - data;
|
|
||||||
|
|
||||||
if (len < colour_table_entries * 3) {
|
|
||||||
return NSGIF_ERR_END_OF_DATA;
|
return NSGIF_ERR_END_OF_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decode) {
|
if (decode) {
|
||||||
int count = colour_table_entries;
|
nsgif__colour_table_decode(colour_table, layout,
|
||||||
uint8_t *entry = (uint8_t *)colour_table;
|
colour_table_entries, data);
|
||||||
|
|
||||||
while (count--) {
|
|
||||||
/* Gif colour map contents are r,g,b.
|
|
||||||
*
|
|
||||||
* We want to pack them bytewise into the colour table,
|
|
||||||
* according to the client colour layout.
|
|
||||||
*/
|
|
||||||
|
|
||||||
entry[layout->r] = *data++;
|
|
||||||
entry[layout->g] = *data++;
|
|
||||||
entry[layout->b] = *data++;
|
|
||||||
entry[layout->a] = 0xff;
|
|
||||||
|
|
||||||
entry += sizeof(uint32_t);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*pos += colour_table_entries * 3;
|
*used = colour_table_entries * 3;
|
||||||
return NSGIF_OK;
|
return NSGIF_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1098,6 +1121,9 @@ static nsgif_error nsgif__parse_colour_table(
|
|||||||
bool decode)
|
bool decode)
|
||||||
{
|
{
|
||||||
nsgif_error ret;
|
nsgif_error ret;
|
||||||
|
const uint8_t *data = *pos;
|
||||||
|
size_t len = gif->buf + gif->buf_len - data;
|
||||||
|
size_t used_bytes;
|
||||||
|
|
||||||
assert(gif != NULL);
|
assert(gif != NULL);
|
||||||
assert(frame != NULL);
|
assert(frame != NULL);
|
||||||
@ -1107,15 +1133,25 @@ static nsgif_error nsgif__parse_colour_table(
|
|||||||
return NSGIF_OK;
|
return NSGIF_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = nsgif__colour_table_extract(gif,
|
if (decode == false) {
|
||||||
|
frame->colour_table_offset = *pos - gif->buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = nsgif__colour_table_extract(
|
||||||
gif->local_colour_table, &gif->colour_layout,
|
gif->local_colour_table, &gif->colour_layout,
|
||||||
2 << (frame->flags & NSGIF_COLOUR_TABLE_SIZE_MASK),
|
2 << (frame->flags & NSGIF_COLOUR_TABLE_SIZE_MASK),
|
||||||
pos, decode);
|
data, len, &used_bytes, decode);
|
||||||
if (ret != NSGIF_OK) {
|
if (ret != NSGIF_OK) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
*pos += used_bytes;
|
||||||
|
|
||||||
|
if (decode) {
|
||||||
|
gif->colour_table = gif->local_colour_table;
|
||||||
|
} else {
|
||||||
|
frame->info.local_palette = true;
|
||||||
|
}
|
||||||
|
|
||||||
gif->colour_table = gif->local_colour_table;
|
|
||||||
return NSGIF_OK;
|
return NSGIF_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1223,7 +1259,8 @@ static struct nsgif_frame *nsgif__get_frame(
|
|||||||
frame = &gif->frames[frame_idx];
|
frame = &gif->frames[frame_idx];
|
||||||
|
|
||||||
frame->transparency_index = NSGIF_NO_TRANSPARENCY;
|
frame->transparency_index = NSGIF_NO_TRANSPARENCY;
|
||||||
frame->frame_pointer = gif->buf_pos;
|
frame->frame_offset = gif->buf_pos;
|
||||||
|
frame->info.local_palette = false;
|
||||||
frame->info.transparency = false;
|
frame->info.transparency = false;
|
||||||
frame->redraw_required = false;
|
frame->redraw_required = false;
|
||||||
frame->info.display = false;
|
frame->info.display = false;
|
||||||
@ -1261,7 +1298,7 @@ static nsgif_error nsgif__process_frame(
|
|||||||
end = gif->buf + gif->buf_len;
|
end = gif->buf + gif->buf_len;
|
||||||
|
|
||||||
if (decode) {
|
if (decode) {
|
||||||
pos = gif->buf + frame->frame_pointer;
|
pos = gif->buf + frame->frame_offset;
|
||||||
|
|
||||||
/* Ensure this frame is supposed to be decoded */
|
/* Ensure this frame is supposed to be decoded */
|
||||||
if (frame->info.display == false) {
|
if (frame->info.display == false) {
|
||||||
@ -1536,7 +1573,7 @@ static nsgif_error nsgif__parse_logical_screen_descriptor(
|
|||||||
|
|
||||||
gif->info.width = data[0] | (data[1] << 8);
|
gif->info.width = data[0] | (data[1] << 8);
|
||||||
gif->info.height = data[2] | (data[3] << 8);
|
gif->info.height = data[2] | (data[3] << 8);
|
||||||
gif->global_colours = data[4] & NSGIF_COLOUR_TABLE_MASK;
|
gif->info.global_palette = data[4] & NSGIF_COLOUR_TABLE_MASK;
|
||||||
gif->colour_table_size = 2 << (data[4] & NSGIF_COLOUR_TABLE_SIZE_MASK);
|
gif->colour_table_size = 2 << (data[4] & NSGIF_COLOUR_TABLE_SIZE_MASK);
|
||||||
gif->bg_index = data[5];
|
gif->bg_index = data[5];
|
||||||
gif->aspect_ratio = data[6];
|
gif->aspect_ratio = data[6];
|
||||||
@ -1630,16 +1667,20 @@ nsgif_error nsgif_data_scan(
|
|||||||
*/
|
*/
|
||||||
if (gif->global_colour_table[0] == NSGIF_PROCESS_COLOURS) {
|
if (gif->global_colour_table[0] == NSGIF_PROCESS_COLOURS) {
|
||||||
/* Check for a global colour map signified by bit 7 */
|
/* Check for a global colour map signified by bit 7 */
|
||||||
if (gif->global_colours) {
|
if (gif->info.global_palette) {
|
||||||
ret = nsgif__colour_table_extract(gif,
|
size_t remaining = gif->buf + gif->buf_len - nsgif_data;
|
||||||
|
size_t used;
|
||||||
|
|
||||||
|
ret = nsgif__colour_table_extract(
|
||||||
gif->global_colour_table,
|
gif->global_colour_table,
|
||||||
&gif->colour_layout,
|
&gif->colour_layout,
|
||||||
gif->colour_table_size,
|
gif->colour_table_size,
|
||||||
&nsgif_data, true);
|
nsgif_data, remaining, &used, true);
|
||||||
if (ret != NSGIF_OK) {
|
if (ret != NSGIF_OK) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsgif_data += used;
|
||||||
gif->buf_pos = (nsgif_data - gif->buf);
|
gif->buf_pos = (nsgif_data - gif->buf);
|
||||||
} else {
|
} else {
|
||||||
/* Create a default colour table with the first two
|
/* Create a default colour table with the first two
|
||||||
@ -1659,9 +1700,11 @@ nsgif_error nsgif_data_scan(
|
|||||||
entry[gif->colour_layout.g] = 0xFF;
|
entry[gif->colour_layout.g] = 0xFF;
|
||||||
entry[gif->colour_layout.b] = 0xFF;
|
entry[gif->colour_layout.b] = 0xFF;
|
||||||
entry[gif->colour_layout.a] = 0xFF;
|
entry[gif->colour_layout.a] = 0xFF;
|
||||||
|
|
||||||
|
gif->colour_table_size = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gif->global_colours &&
|
if (gif->info.global_palette &&
|
||||||
gif->bg_index < gif->colour_table_size) {
|
gif->bg_index < gif->colour_table_size) {
|
||||||
size_t bg_idx = gif->bg_index;
|
size_t bg_idx = gif->bg_index;
|
||||||
gif->info.background = gif->global_colour_table[bg_idx];
|
gif->info.background = gif->global_colour_table[bg_idx];
|
||||||
@ -1895,6 +1938,43 @@ const nsgif_frame_info_t *nsgif_get_frame_info(
|
|||||||
return &gif->frames[frame].info;
|
return &gif->frames[frame].info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* exported function documented in nsgif.h */
|
||||||
|
void nsgif_global_palette(
|
||||||
|
const nsgif_t *gif,
|
||||||
|
uint32_t table[NSGIF_MAX_COLOURS],
|
||||||
|
size_t *entries)
|
||||||
|
{
|
||||||
|
size_t len = sizeof(*table) * NSGIF_MAX_COLOURS;
|
||||||
|
|
||||||
|
memcpy(table, gif->global_colour_table, len);
|
||||||
|
*entries = gif->colour_table_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* exported function documented in nsgif.h */
|
||||||
|
bool nsgif_local_palette(
|
||||||
|
const nsgif_t *gif,
|
||||||
|
uint32_t frame,
|
||||||
|
uint32_t table[NSGIF_MAX_COLOURS],
|
||||||
|
size_t *entries)
|
||||||
|
{
|
||||||
|
const nsgif_frame *f;
|
||||||
|
|
||||||
|
if (frame >= gif->frame_count_partial) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
f = &gif->frames[frame];
|
||||||
|
if (f->info.local_palette == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*entries = 2 << (f->flags & NSGIF_COLOUR_TABLE_SIZE_MASK);
|
||||||
|
nsgif__colour_table_decode(table, &gif->colour_layout,
|
||||||
|
*entries, gif->buf + f->colour_table_offset);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* exported function documented in nsgif.h */
|
/* exported function documented in nsgif.h */
|
||||||
const char *nsgif_strerror(nsgif_error err)
|
const char *nsgif_strerror(nsgif_error err)
|
||||||
{
|
{
|
||||||
|
@ -23,6 +23,9 @@
|
|||||||
/** Representation of infinity. */
|
/** Representation of infinity. */
|
||||||
#define NSGIF_INFINITE (UINT32_MAX)
|
#define NSGIF_INFINITE (UINT32_MAX)
|
||||||
|
|
||||||
|
/** Maximum colour table size */
|
||||||
|
#define NSGIF_MAX_COLOURS 256
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opaque type used by LibNSGIF to represent a GIF object in memory.
|
* Opaque type used by LibNSGIF to represent a GIF object in memory.
|
||||||
*/
|
*/
|
||||||
@ -162,6 +165,11 @@ typedef enum nsgif_bitmap_fmt {
|
|||||||
* but they are owned by a \ref nsgif_t.
|
* but they are owned by a \ref nsgif_t.
|
||||||
*
|
*
|
||||||
* See \ref nsgif_bitmap_fmt for pixel format information.
|
* See \ref nsgif_bitmap_fmt for pixel format information.
|
||||||
|
*
|
||||||
|
* The bitmap may have a row_span greater than the bitmap width, but the
|
||||||
|
* difference between row span and width must be a whole number of pixels
|
||||||
|
* (a multiple of four bytes). If row span is greater than width, the
|
||||||
|
* \ref get_rowspan callback must be provided.
|
||||||
*/
|
*/
|
||||||
typedef void nsgif_bitmap_t;
|
typedef void nsgif_bitmap_t;
|
||||||
|
|
||||||
@ -186,7 +194,8 @@ typedef struct nsgif_bitmap_cb_vt {
|
|||||||
/**
|
/**
|
||||||
* Get pointer to pixel buffer in a bitmap.
|
* Get pointer to pixel buffer in a bitmap.
|
||||||
*
|
*
|
||||||
* The pixel buffer must be `width * height * sizeof(uint32_t)`.
|
* The pixel buffer must be `(width + N) * height * sizeof(uint32_t)`.
|
||||||
|
* Where `N` is any number greater than or equal to 0.
|
||||||
* Note that the returned pointer to uint8_t must be 4-byte aligned.
|
* Note that the returned pointer to uint8_t must be 4-byte aligned.
|
||||||
*
|
*
|
||||||
* \param[in] bitmap The bitmap.
|
* \param[in] bitmap The bitmap.
|
||||||
@ -218,6 +227,15 @@ typedef struct nsgif_bitmap_cb_vt {
|
|||||||
* \param[in] bitmap The bitmap.
|
* \param[in] bitmap The bitmap.
|
||||||
*/
|
*/
|
||||||
void (*modified)(nsgif_bitmap_t *bitmap);
|
void (*modified)(nsgif_bitmap_t *bitmap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get row span in pixels.
|
||||||
|
*
|
||||||
|
* If this callback is not provided, LibNSGIF will use the width.
|
||||||
|
*
|
||||||
|
* \param[in] bitmap The bitmap.
|
||||||
|
*/
|
||||||
|
uint32_t (*get_rowspan)(nsgif_bitmap_t *bitmap);
|
||||||
} nsgif_bitmap_cb_vt;
|
} nsgif_bitmap_cb_vt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -345,6 +363,8 @@ typedef struct nsgif_info {
|
|||||||
int loop_max;
|
int loop_max;
|
||||||
/** background colour in same pixel format as \ref nsgif_bitmap_t. */
|
/** background colour in same pixel format as \ref nsgif_bitmap_t. */
|
||||||
uint32_t background;
|
uint32_t background;
|
||||||
|
/** whether the GIF has a global colour table */
|
||||||
|
bool global_palette;
|
||||||
} nsgif_info_t;
|
} nsgif_info_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -377,6 +397,8 @@ typedef struct nsgif_frame_info {
|
|||||||
bool display;
|
bool display;
|
||||||
/** whether the frame may have transparency */
|
/** whether the frame may have transparency */
|
||||||
bool transparency;
|
bool transparency;
|
||||||
|
/** whether the frame has a local colour table */
|
||||||
|
bool local_palette;
|
||||||
|
|
||||||
/** Disposal method for previous frame; affects plotting */
|
/** Disposal method for previous frame; affects plotting */
|
||||||
uint8_t disposal;
|
uint8_t disposal;
|
||||||
@ -408,6 +430,43 @@ const nsgif_frame_info_t *nsgif_get_frame_info(
|
|||||||
const nsgif_t *gif,
|
const nsgif_t *gif,
|
||||||
uint32_t frame);
|
uint32_t frame);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the global colour palette.
|
||||||
|
*
|
||||||
|
* If the GIF has no global colour table, this will return the default
|
||||||
|
* colour palette.
|
||||||
|
*
|
||||||
|
* Colours in same pixel format as \ref nsgif_bitmap_t.
|
||||||
|
*
|
||||||
|
* \param[in] gif The \ref nsgif_t object.
|
||||||
|
* \param[out] table Client buffer to hold the colour table.
|
||||||
|
* \param[out] entries The number of used entries in the colour table.
|
||||||
|
*/
|
||||||
|
void nsgif_global_palette(
|
||||||
|
const nsgif_t *gif,
|
||||||
|
uint32_t table[NSGIF_MAX_COLOURS],
|
||||||
|
size_t *entries);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the local colour palette for a frame.
|
||||||
|
*
|
||||||
|
* Frames may have no local palette. In this case they use the global palette.
|
||||||
|
* This function returns false if the frame has no local palette.
|
||||||
|
*
|
||||||
|
* Colours in same pixel format as \ref nsgif_bitmap_t.
|
||||||
|
*
|
||||||
|
* \param[in] gif The \ref nsgif_t object.
|
||||||
|
* \param[in] frame The \ref frame to get the palette for.
|
||||||
|
* \param[out] table Client buffer to hold the colour table.
|
||||||
|
* \param[out] entries The number of used entries in the colour table.
|
||||||
|
* \return true if a palette is returned, false otherwise.
|
||||||
|
*/
|
||||||
|
bool nsgif_local_palette(
|
||||||
|
const nsgif_t *gif,
|
||||||
|
uint32_t frame,
|
||||||
|
uint32_t table[NSGIF_MAX_COLOURS],
|
||||||
|
size_t *entries);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure handling of small frame delays.
|
* Configure handling of small frame delays.
|
||||||
*
|
*
|
||||||
|
@ -27,6 +27,7 @@ static struct nsgif_options {
|
|||||||
const char *file;
|
const char *file;
|
||||||
const char *ppm;
|
const char *ppm;
|
||||||
uint64_t loops;
|
uint64_t loops;
|
||||||
|
bool palette;
|
||||||
bool info;
|
bool info;
|
||||||
} nsgif_options;
|
} nsgif_options;
|
||||||
|
|
||||||
@ -53,6 +54,13 @@ static const struct cli_table_entry cli_entries[] = {
|
|||||||
.d = "Loop through decoding all frames N times. "
|
.d = "Loop through decoding all frames N times. "
|
||||||
"The default is 1."
|
"The default is 1."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.s = 'p',
|
||||||
|
.l = "palette",
|
||||||
|
.t = CLI_BOOL,
|
||||||
|
.v.b = &nsgif_options.palette,
|
||||||
|
.d = "Save palette images."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.p = true,
|
.p = true,
|
||||||
.l = "FILE",
|
.l = "FILE",
|
||||||
@ -142,6 +150,7 @@ static void print_gif_info(const nsgif_info_t *info)
|
|||||||
fprintf(stdout, " height: %"PRIu32"\n", info->height);
|
fprintf(stdout, " height: %"PRIu32"\n", info->height);
|
||||||
fprintf(stdout, " max-loops: %"PRIu32"\n", info->loop_max);
|
fprintf(stdout, " max-loops: %"PRIu32"\n", info->loop_max);
|
||||||
fprintf(stdout, " frame-count: %"PRIu32"\n", info->frame_count);
|
fprintf(stdout, " frame-count: %"PRIu32"\n", info->frame_count);
|
||||||
|
fprintf(stdout, " global palette: %s\n", info->global_palette ? "yes" : "no");
|
||||||
fprintf(stdout, " background:\n");
|
fprintf(stdout, " background:\n");
|
||||||
fprintf(stdout, " red: 0x%"PRIx8"\n", bg[0]);
|
fprintf(stdout, " red: 0x%"PRIx8"\n", bg[0]);
|
||||||
fprintf(stdout, " green: 0x%"PRIx8"\n", bg[1]);
|
fprintf(stdout, " green: 0x%"PRIx8"\n", bg[1]);
|
||||||
@ -154,6 +163,7 @@ static void print_gif_frame_info(const nsgif_frame_info_t *info, uint32_t i)
|
|||||||
const char *disposal = nsgif_str_disposal(info->disposal);
|
const char *disposal = nsgif_str_disposal(info->disposal);
|
||||||
|
|
||||||
fprintf(stdout, " - frame: %"PRIu32"\n", i);
|
fprintf(stdout, " - frame: %"PRIu32"\n", i);
|
||||||
|
fprintf(stdout, " local palette: %s\n", info->local_palette ? "yes" : "no");
|
||||||
fprintf(stdout, " disposal-method: %s\n", disposal);
|
fprintf(stdout, " disposal-method: %s\n", disposal);
|
||||||
fprintf(stdout, " transparency: %s\n", info->transparency ? "yes" : "no");
|
fprintf(stdout, " transparency: %s\n", info->transparency ? "yes" : "no");
|
||||||
fprintf(stdout, " display: %s\n", info->display ? "yes" : "no");
|
fprintf(stdout, " display: %s\n", info->display ? "yes" : "no");
|
||||||
@ -165,7 +175,82 @@ static void print_gif_frame_info(const nsgif_frame_info_t *info, uint32_t i)
|
|||||||
fprintf(stdout, " h: %"PRIu32"\n", info->rect.y1 - info->rect.y0);
|
fprintf(stdout, " h: %"PRIu32"\n", info->rect.y1 - info->rect.y0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void decode(FILE* ppm, const char *name, nsgif_t *gif)
|
static bool save_palette(
|
||||||
|
const char *img_filename,
|
||||||
|
const char *palette_filename,
|
||||||
|
const uint32_t palette[NSGIF_MAX_COLOURS],
|
||||||
|
size_t used_entries)
|
||||||
|
{
|
||||||
|
enum {
|
||||||
|
SIZE = 32,
|
||||||
|
COUNT = 16,
|
||||||
|
};
|
||||||
|
FILE *f;
|
||||||
|
int size = COUNT * SIZE + 1;
|
||||||
|
|
||||||
|
f = fopen(palette_filename, "w+");
|
||||||
|
if (f == NULL) {
|
||||||
|
fprintf(stderr, "Unable to open %s for writing\n",
|
||||||
|
palette_filename);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(f, "P3\n");
|
||||||
|
fprintf(f, "# %s: %s\n", img_filename, palette_filename);
|
||||||
|
fprintf(f, "# Colour count: %zu\n", used_entries);
|
||||||
|
fprintf(f, "%u %u 256\n", size, size);
|
||||||
|
|
||||||
|
for (int y = 0; y < size; y++) {
|
||||||
|
for (int x = 0; x < size; x++) {
|
||||||
|
if (x % SIZE == 0 || y % SIZE == 0) {
|
||||||
|
fprintf(f, "0 0 0 ");
|
||||||
|
} else {
|
||||||
|
size_t offset = y / SIZE * COUNT + x / SIZE;
|
||||||
|
uint8_t *entry = (uint8_t *)&palette[offset];
|
||||||
|
|
||||||
|
fprintf(f, "%u %u %u ",
|
||||||
|
entry[0],
|
||||||
|
entry[1],
|
||||||
|
entry[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(f, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool save_global_palette(const nsgif_t *gif)
|
||||||
|
{
|
||||||
|
uint32_t table[NSGIF_MAX_COLOURS];
|
||||||
|
size_t entries;
|
||||||
|
|
||||||
|
nsgif_global_palette(gif, table, &entries);
|
||||||
|
|
||||||
|
return save_palette(nsgif_options.file, "global-palette.ppm",
|
||||||
|
table, entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool save_local_palette(const nsgif_t *gif, uint32_t frame)
|
||||||
|
{
|
||||||
|
static uint32_t table[NSGIF_MAX_COLOURS];
|
||||||
|
char filename[64];
|
||||||
|
size_t entries;
|
||||||
|
|
||||||
|
snprintf(filename, sizeof(filename), "local-palette-%"PRIu32".ppm",
|
||||||
|
frame);
|
||||||
|
|
||||||
|
if (!nsgif_local_palette(gif, frame, table, &entries)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return save_palette(nsgif_options.file, filename, table, entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void decode(FILE* ppm, const char *name, nsgif_t *gif, bool first)
|
||||||
{
|
{
|
||||||
nsgif_error err;
|
nsgif_error err;
|
||||||
uint32_t frame_prev = 0;
|
uint32_t frame_prev = 0;
|
||||||
@ -173,7 +258,7 @@ static void decode(FILE* ppm, const char *name, nsgif_t *gif)
|
|||||||
|
|
||||||
info = nsgif_get_info(gif);
|
info = nsgif_get_info(gif);
|
||||||
|
|
||||||
if (ppm != NULL) {
|
if (first && ppm != NULL) {
|
||||||
fprintf(ppm, "P3\n");
|
fprintf(ppm, "P3\n");
|
||||||
fprintf(ppm, "# %s\n", name);
|
fprintf(ppm, "# %s\n", name);
|
||||||
fprintf(ppm, "# width %u \n", info->width);
|
fprintf(ppm, "# width %u \n", info->width);
|
||||||
@ -184,9 +269,12 @@ static void decode(FILE* ppm, const char *name, nsgif_t *gif)
|
|||||||
info->height * info->frame_count);
|
info->height * info->frame_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nsgif_options.info == true) {
|
if (first && nsgif_options.info) {
|
||||||
print_gif_info(info);
|
print_gif_info(info);
|
||||||
}
|
}
|
||||||
|
if (first && nsgif_options.palette && info->global_palette) {
|
||||||
|
save_global_palette(gif);
|
||||||
|
}
|
||||||
|
|
||||||
/* decode the frames */
|
/* decode the frames */
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -210,7 +298,7 @@ static void decode(FILE* ppm, const char *name, nsgif_t *gif)
|
|||||||
}
|
}
|
||||||
frame_prev = frame_new;
|
frame_prev = frame_new;
|
||||||
|
|
||||||
if (nsgif_options.info == true) {
|
if (first && nsgif_options.info) {
|
||||||
const nsgif_frame_info_t *f_info;
|
const nsgif_frame_info_t *f_info;
|
||||||
|
|
||||||
f_info = nsgif_get_frame_info(gif, frame_new);
|
f_info = nsgif_get_frame_info(gif, frame_new);
|
||||||
@ -218,6 +306,9 @@ static void decode(FILE* ppm, const char *name, nsgif_t *gif)
|
|||||||
print_gif_frame_info(f_info, frame_new);
|
print_gif_frame_info(f_info, frame_new);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (first && nsgif_options.palette) {
|
||||||
|
save_local_palette(gif, frame_new);
|
||||||
|
}
|
||||||
|
|
||||||
err = nsgif_frame_decode(gif, frame_new, &bitmap);
|
err = nsgif_frame_decode(gif, frame_new, &bitmap);
|
||||||
if (err != NSGIF_OK) {
|
if (err != NSGIF_OK) {
|
||||||
@ -226,7 +317,7 @@ static void decode(FILE* ppm, const char *name, nsgif_t *gif)
|
|||||||
frame_new, nsgif_strerror(err));
|
frame_new, nsgif_strerror(err));
|
||||||
/* Continue decoding the rest of the frames. */
|
/* Continue decoding the rest of the frames. */
|
||||||
|
|
||||||
} else if (ppm != NULL) {
|
} else if (first && ppm != NULL) {
|
||||||
fprintf(ppm, "# frame %u:\n", frame_new);
|
fprintf(ppm, "# frame %u:\n", frame_new);
|
||||||
image = (const uint8_t *) bitmap;
|
image = (const uint8_t *) bitmap;
|
||||||
for (uint32_t y = 0; y != info->height; y++) {
|
for (uint32_t y = 0; y != info->height; y++) {
|
||||||
@ -299,7 +390,10 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (uint64_t i = 0; i < nsgif_options.loops; i++) {
|
for (uint64_t i = 0; i < nsgif_options.loops; i++) {
|
||||||
decode((i == 0) ? ppm : NULL, nsgif_options.file, gif);
|
decode(ppm, nsgif_options.file, gif, i == 0);
|
||||||
|
|
||||||
|
/* We want to ignore any loop limit in the GIF. */
|
||||||
|
nsgif_reset(gif);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ppm != NULL) {
|
if (ppm != NULL) {
|
||||||
|
@ -201,6 +201,7 @@ print_frame( const nsgif_frame_info_t *frame_info )
|
|||||||
|
|
||||||
printf( "frame_info:\n" );
|
printf( "frame_info:\n" );
|
||||||
printf( " display = %d\n", frame_info->display );
|
printf( " display = %d\n", frame_info->display );
|
||||||
|
printf( " local_palette = %d\n", frame_info->local_palette );
|
||||||
printf( " transparency = %d\n", frame_info->transparency );
|
printf( " transparency = %d\n", frame_info->transparency );
|
||||||
printf( " disposal = %d (%s)\n",
|
printf( " disposal = %d (%s)\n",
|
||||||
frame_info->disposal,
|
frame_info->disposal,
|
||||||
@ -222,6 +223,7 @@ print_animation( nsgif_t *anim, const nsgif_info_t *info )
|
|||||||
printf( " width = %d\n", info->width );
|
printf( " width = %d\n", info->width );
|
||||||
printf( " height = %d\n", info->height );
|
printf( " height = %d\n", info->height );
|
||||||
printf( " frame_count = %d\n", info->frame_count );
|
printf( " frame_count = %d\n", info->frame_count );
|
||||||
|
printf( " global_palette = %d\n", info->global_palette );
|
||||||
printf( " loop_max = %d\n", info->loop_max );
|
printf( " loop_max = %d\n", info->loop_max );
|
||||||
printf( " background = %d %d %d %d\n",
|
printf( " background = %d %d %d %d\n",
|
||||||
bg[0], bg[1], bg[2], bg[3] );
|
bg[0], bg[1], bg[2], bg[3] );
|
||||||
|
Loading…
Reference in New Issue
Block a user