Update LibNSGIF to latest upstream (#2791)

* libnsgif: Update to latest upstream

* nsgifload: Add palette info to debug output.
This commit is contained in:
Michael Drake 2022-05-09 10:20:57 +01:00 committed by GitHub
parent 460a19b78e
commit ceaa2d2096
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 287 additions and 52 deletions

View File

@ -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

View File

@ -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)
{ {

View File

@ -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.
* *

View File

@ -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) {

View File

@ -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] );