[gegl] subprojects/libnsgif: update to latest upstream
- From: Øyvind "pippin" Kolås <ok src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl] subprojects/libnsgif: update to latest upstream
- Date: Mon, 25 Apr 2022 03:48:53 +0000 (UTC)
commit f0a2e1d1b3c0f0b03d63afe5b17ffb115c16c8c5
Author: Michael Drake <michael drake codethink co uk>
Date: Sat Apr 23 22:06:25 2022 +0100
subprojects/libnsgif: update to latest upstream
LibNSGIF now supports decoding to multiple different pixel
buffer formats. It has also had slight improvements to the
handling of malformed GIFs.
Upstream: https://source.netsurf-browser.org/libnsgif.git
Ref: 75ed38539447571ec961c985c8a1e9cbd76573e2
subprojects/libnsgif/README.md | 11 +-
subprojects/libnsgif/gif.c | 224 ++++++++++++++++++++++++++++++-----------
subprojects/libnsgif/nsgif.h | 102 +++++++++++++++++--
3 files changed, 264 insertions(+), 73 deletions(-)
---
diff --git a/subprojects/libnsgif/README.md b/subprojects/libnsgif/README.md
index 63ccdc7f3..e6219aab0 100644
--- a/subprojects/libnsgif/README.md
+++ b/subprojects/libnsgif/README.md
@@ -22,14 +22,17 @@ Using
LibNSGIF allows the client to allocate the bitmap into which the GIF is
decoded. The client can have an arbitrary bitmap structure, that is simply
a void pointer to LibNSGIF. The client must provide a callback table for
-interacting with bitmaps. This table must include as a minimum functions to
-create and destroy bitmaps, and a function to get a pointer to the bitmap's
-pixel data buffer.
+interacting with bitmaps, and the required client bitmap pixel format.
+The bitmap table must include as a minimum functions to create and destroy
+bitmaps, and a function to get a pointer to the bitmap's pixel data buffer.
+
+LibNSGIF always decodes to a 32bpp, 8 bits per channel bitmap pixel format,
+however it allows the client to control the colour component ordering.
To load a GIF, first create an nsgif object with `nsgif_create()`.
```c
- err = nsgif_create(&bitmap_callbacks, &gif);
+ err = nsgif_create(&bitmap_callbacks, NSGIF_BITMAP_FMT_R8G8B8A8, &gif);
if (err != NSGIF_OK) {
fprintf(stderr, "%s\n", nsgif_strerror(err));
// Handle error
diff --git a/subprojects/libnsgif/gif.c b/subprojects/libnsgif/gif.c
index 287b63235..84d4209b3 100644
--- a/subprojects/libnsgif/gif.c
+++ b/subprojects/libnsgif/gif.c
@@ -20,6 +20,16 @@
/** Maximum colour table size */
#define NSGIF_MAX_COLOURS 256
+/** Default minimum allowable frame delay in cs. */
+#define NSGIF_FRAME_DELAY_MIN 2
+
+/**
+ * Default frame delay to apply.
+ *
+ * Used when a frame delay lower than the minimum is requested.
+ */
+#define NSGIF_FRAME_DELAY_DEFAULT 10
+
/** GIF frame data */
typedef struct nsgif_frame {
struct nsgif_frame_info info;
@@ -40,6 +50,14 @@ typedef struct nsgif_frame {
uint32_t flags;
} nsgif_frame;
+/** Pixel format: colour component order. */
+struct nsgif_colour_layout {
+ uint8_t r; /**< Byte offset within pixel to red component. */
+ uint8_t g; /**< Byte offset within pixel to green component. */
+ uint8_t b; /**< Byte offset within pixel to blue component. */
+ uint8_t a; /**< Byte offset within pixel to alpha component. */
+};
+
/** GIF animation data */
struct nsgif {
struct nsgif_info info;
@@ -57,9 +75,15 @@ struct nsgif {
/** currently decoded image; stored as bitmap from bitmap_create callback */
nsgif_bitmap_t *frame_image;
+ /** Minimum allowable frame delay. */
uint16_t delay_min;
+
+ /** Frame delay to apply when delay is less than \ref delay_min. */
uint16_t delay_default;
+ /** number of animation loops so far */
+ int loop_count;
+
/** number of frames partially decoded */
uint32_t frame_count_partial;
@@ -83,6 +107,8 @@ struct nsgif {
bool global_colours;
/** current colour table */
uint32_t *colour_table;
+ /** Client's colour component order. */
+ struct nsgif_colour_layout colour_layout;
/** global colour table */
uint32_t global_colour_table[NSGIF_MAX_COLOURS];
/** local colour table */
@@ -445,7 +471,8 @@ static nsgif_error nsgif__decode_complex(
while (available == 0) {
if (res != LZW_OK) {
/* Unexpected end of frame, try to recover */
- if (res == LZW_OK_EOD) {
+ if (res == LZW_OK_EOD ||
+ res == LZW_EOI_CODE) {
ret = NSGIF_OK;
} else {
ret = nsgif__error_from_lzw(res);
@@ -498,7 +525,7 @@ static nsgif_error nsgif__decode_simple(
uint32_t *restrict frame_data,
uint32_t *restrict colour_table)
{
- uint32_t pixels = gif->info.width * height;
+ uint32_t pixels;
uint32_t written = 0;
nsgif_error ret = NSGIF_OK;
lzw_result res;
@@ -523,6 +550,7 @@ static nsgif_error nsgif__decode_simple(
}
frame_data += (offset_y * gif->info.width);
+ pixels = gif->info.width * height;
while (pixels > 0) {
res = lzw_decode_map(gif->lzw_ctx,
@@ -531,7 +559,7 @@ static nsgif_error nsgif__decode_simple(
frame_data += written;
if (res != LZW_OK) {
/* Unexpected end of frame, try to recover */
- if (res == LZW_OK_EOD) {
+ if (res == LZW_OK_EOD || res == LZW_EOI_CODE) {
ret = NSGIF_OK;
} else {
ret = nsgif__error_from_lzw(res);
@@ -580,30 +608,6 @@ static inline nsgif_error nsgif__decode(
return ret;
}
-/**
- * Helper to assign a pixel representation from a gif background colour array.
- *
- * \param[in] bg The background colour to read from.
- * \param[out] px The pixel colour to write.
- */
-static inline void nsgif__gif_bg_to_px(
- const uint8_t bg[4], uint32_t *px)
-{
- *px = *(uint32_t *)bg;
-}
-
-/**
- * Helper to assign a gif background colour array from a pixel representation.
- *
- * \param[in] px The pixel colour to read from.
- * \param[out] bg The background colour to write.
- */
-static inline void nsgif__gif_px_to_bg(
- const uint32_t *px, uint8_t bg[4])
-{
- *(uint32_t *)bg = *px;
-}
-
/**
* Restore a GIF to the background colour.
*
@@ -646,9 +650,7 @@ static void nsgif__restore_bg(
uint32_t *scanline = bitmap + offset_x +
(offset_y + y) * gif->info.width;
for (uint32_t x = 0; x < width; x++) {
- nsgif__gif_bg_to_px(
- gif->info.background,
- &scanline[x]);
+ scanline[x] = gif->info.background;
}
}
}
@@ -1042,6 +1044,7 @@ static nsgif_error nsgif__parse_image_descriptor(
static nsgif_error nsgif__colour_table_extract(
struct nsgif *gif,
uint32_t colour_table[NSGIF_MAX_COLOURS],
+ const struct nsgif_colour_layout *layout,
size_t colour_table_entries,
const uint8_t **pos,
bool decode)
@@ -1060,16 +1063,16 @@ static nsgif_error nsgif__colour_table_extract(
while (count--) {
/* Gif colour map contents are r,g,b.
*
- * We want to pack them bytewise into the
- * colour table, such that the red component
- * is in byte 0 and the alpha component is in
- * byte 3.
+ * We want to pack them bytewise into the colour table,
+ * according to the client colour layout.
*/
- *entry++ = *data++; /* r */
- *entry++ = *data++; /* g */
- *entry++ = *data++; /* b */
- *entry++ = 0xff; /* a */
+ entry[layout->r] = *data++;
+ entry[layout->g] = *data++;
+ entry[layout->b] = *data++;
+ entry[layout->a] = 0xff;
+
+ entry += sizeof(uint32_t);
}
}
@@ -1104,7 +1107,8 @@ static nsgif_error nsgif__parse_colour_table(
return NSGIF_OK;
}
- ret = nsgif__colour_table_extract(gif, gif->local_colour_table,
+ ret = nsgif__colour_table_extract(gif,
+ gif->local_colour_table, &gif->colour_layout,
2 << (frame->flags & NSGIF_COLOUR_TABLE_SIZE_MASK),
pos, decode);
if (ret != NSGIF_OK) {
@@ -1343,8 +1347,90 @@ void nsgif_destroy(nsgif_t *gif)
free(gif);
}
+/**
+ * Check whether the host is little endian.
+ *
+ * Checks whether least significant bit is in the first byte of a `uint16_t`.
+ *
+ * \return true if host is little endian.
+ */
+static inline bool nsgif__host_is_little_endian(void)
+{
+ const uint16_t test = 1;
+
+ return ((const uint8_t *) &test)[0];
+}
+
+static struct nsgif_colour_layout nsgif__bitmap_fmt_to_colour_layout(
+ nsgif_bitmap_fmt_t bitmap_fmt)
+{
+ bool le = nsgif__host_is_little_endian();
+
+ /* Map endian-dependant formats to byte-wise format for the host. */
+ switch (bitmap_fmt) {
+ case NSGIF_BITMAP_FMT_RGBA8888:
+ bitmap_fmt = (le) ? NSGIF_BITMAP_FMT_A8B8G8R8
+ : NSGIF_BITMAP_FMT_R8G8B8A8;
+ break;
+ case NSGIF_BITMAP_FMT_BGRA8888:
+ bitmap_fmt = (le) ? NSGIF_BITMAP_FMT_A8R8G8B8
+ : NSGIF_BITMAP_FMT_B8G8R8A8;
+ break;
+ case NSGIF_BITMAP_FMT_ARGB8888:
+ bitmap_fmt = (le) ? NSGIF_BITMAP_FMT_B8G8R8A8
+ : NSGIF_BITMAP_FMT_A8R8G8B8;
+ break;
+ case NSGIF_BITMAP_FMT_ABGR8888:
+ bitmap_fmt = (le) ? NSGIF_BITMAP_FMT_R8G8B8A8
+ : NSGIF_BITMAP_FMT_A8B8G8R8;
+ break;
+ default:
+ break;
+ }
+
+ /* Set up colour component order for bitmap format. */
+ switch (bitmap_fmt) {
+ default:
+ /* Fall through. */
+ case NSGIF_BITMAP_FMT_R8G8B8A8:
+ return (struct nsgif_colour_layout) {
+ .r = 0,
+ .g = 1,
+ .b = 2,
+ .a = 3,
+ };
+
+ case NSGIF_BITMAP_FMT_B8G8R8A8:
+ return (struct nsgif_colour_layout) {
+ .b = 0,
+ .g = 1,
+ .r = 2,
+ .a = 3,
+ };
+
+ case NSGIF_BITMAP_FMT_A8R8G8B8:
+ return (struct nsgif_colour_layout) {
+ .a = 0,
+ .r = 1,
+ .g = 2,
+ .b = 3,
+ };
+
+ case NSGIF_BITMAP_FMT_A8B8G8R8:
+ return (struct nsgif_colour_layout) {
+ .a = 0,
+ .b = 1,
+ .g = 2,
+ .r = 3,
+ };
+ }
+}
+
/* exported function documented in nsgif.h */
-nsgif_error nsgif_create(const nsgif_bitmap_cb_vt *bitmap_vt, nsgif_t **gif_out)
+nsgif_error nsgif_create(
+ const nsgif_bitmap_cb_vt *bitmap_vt,
+ nsgif_bitmap_fmt_t bitmap_fmt,
+ nsgif_t **gif_out)
{
nsgif_t *gif;
@@ -1357,13 +1443,25 @@ nsgif_error nsgif_create(const nsgif_bitmap_cb_vt *bitmap_vt, nsgif_t **gif_out)
gif->decoded_frame = NSGIF_FRAME_INVALID;
gif->prev_index = NSGIF_FRAME_INVALID;
- gif->delay_min = 2;
- gif->delay_default = 10;
+ gif->delay_min = NSGIF_FRAME_DELAY_MIN;
+ gif->delay_default = NSGIF_FRAME_DELAY_DEFAULT;
+
+ gif->colour_layout = nsgif__bitmap_fmt_to_colour_layout(bitmap_fmt);
*gif_out = gif;
return NSGIF_OK;
}
+/* exported function documented in nsgif.h */
+void nsgif_set_frame_delay_behaviour(
+ nsgif_t *gif,
+ uint16_t delay_min,
+ uint16_t delay_default)
+{
+ gif->delay_min = delay_min;
+ gif->delay_default = delay_default;
+}
+
/**
* Read GIF header.
*
@@ -1535,6 +1633,7 @@ nsgif_error nsgif_data_scan(
if (gif->global_colours) {
ret = nsgif__colour_table_extract(gif,
gif->global_colour_table,
+ &gif->colour_layout,
gif->colour_table_size,
&nsgif_data, true);
if (ret != NSGIF_OK) {
@@ -1544,27 +1643,30 @@ nsgif_error nsgif_data_scan(
gif->buf_pos = (nsgif_data - gif->buf);
} else {
/* Create a default colour table with the first two
- * colours as black and white
- */
- uint32_t *entry = gif->global_colour_table;
-
- entry[0] = 0x00000000;
- /* Force Alpha channel to opaque */
- ((uint8_t *) entry)[3] = 0xff;
-
- entry[1] = 0xffffffff;
+ * colours as black and white. */
+ uint8_t *entry = (uint8_t *)gif->global_colour_table;
+
+ /* Black */
+ entry[gif->colour_layout.r] = 0x00;
+ entry[gif->colour_layout.g] = 0x00;
+ entry[gif->colour_layout.b] = 0x00;
+ entry[gif->colour_layout.a] = 0xFF;
+
+ entry += sizeof(uint32_t);
+
+ /* White */
+ entry[gif->colour_layout.r] = 0xFF;
+ entry[gif->colour_layout.g] = 0xFF;
+ entry[gif->colour_layout.b] = 0xFF;
+ entry[gif->colour_layout.a] = 0xFF;
}
if (gif->global_colours &&
gif->bg_index < gif->colour_table_size) {
size_t bg_idx = gif->bg_index;
- nsgif__gif_px_to_bg(
- &gif->global_colour_table[bg_idx],
- gif->info.background);
+ gif->info.background = gif->global_colour_table[bg_idx];
} else {
- nsgif__gif_px_to_bg(
- &gif->global_colour_table[0],
- gif->info.background);
+ gif->info.background = gif->global_colour_table[0];
}
}
@@ -1663,7 +1765,7 @@ static inline bool nsgif__animation_complete(int count, int max)
nsgif_error nsgif_reset(
nsgif_t *gif)
{
- gif->info.loop_count = 0;
+ gif->loop_count = 0;
gif->frame = NSGIF_FRAME_INVALID;
return NSGIF_OK;
@@ -1691,7 +1793,7 @@ nsgif_error nsgif_frame_prepare(
}
if (nsgif__animation_complete(
- gif->info.loop_count,
+ gif->loop_count,
gif->info.loop_max)) {
return NSGIF_ERR_ANIMATION_END;
}
@@ -1702,7 +1804,7 @@ nsgif_error nsgif_frame_prepare(
}
if (gif->frame != NSGIF_FRAME_INVALID && frame < gif->frame) {
- gif->info.loop_count++;
+ gif->loop_count++;
}
if (gif->info.frame_count == 1) {
@@ -1717,7 +1819,7 @@ nsgif_error nsgif_frame_prepare(
if (frame_next < frame) {
if (nsgif__animation_complete(
- gif->info.loop_count + 1,
+ gif->loop_count + 1,
gif->info.loop_max)) {
delay = NSGIF_INFINITE;
}
diff --git a/subprojects/libnsgif/nsgif.h b/subprojects/libnsgif/nsgif.h
index 54dcd7190..a02597c3e 100644
--- a/subprojects/libnsgif/nsgif.h
+++ b/subprojects/libnsgif/nsgif.h
@@ -98,15 +98,70 @@ typedef enum {
NSGIF_ERR_ANIMATION_END,
} nsgif_error;
+/**
+ * NSGIF \ref nsgif_bitmap_t pixel format.
+ *
+ * All pixel formats are 32 bits per pixel (bpp). The different formats
+ * allow control over the ordering of the colour channels. All colour
+ * channels are 8 bits wide.
+ *
+ * Note that the GIF file format only supports an on/off mask, so the
+ * alpha (A) component (opacity) will always have a value of `0` (fully
+ * transparent) or `255` (fully opaque).
+ */
+typedef enum nsgif_bitmap_fmt {
+ /** Bite-wise RGBA: Byte order: 0xRR, 0xGG, 0xBB, 0xAA. */
+ NSGIF_BITMAP_FMT_R8G8B8A8,
+
+ /** Bite-wise BGRA: Byte order: 0xBB, 0xGG, 0xRR, 0xAA. */
+ NSGIF_BITMAP_FMT_B8G8R8A8,
+
+ /** Bite-wise ARGB: Byte order: 0xAA, 0xRR, 0xGG, 0xBB. */
+ NSGIF_BITMAP_FMT_A8R8G8B8,
+
+ /** Bite-wise ABGR: Byte order: 0xAA, 0xBB, 0xGG, 0xRR. */
+ NSGIF_BITMAP_FMT_A8B8G8R8,
+
+ /**
+ * 32-bit RGBA (0xRRGGBBAA).
+ *
+ * * On little endian host, same as \ref NSGIF_BITMAP_FMT_A8B8G8R8.
+ * * On big endian host, same as \ref NSGIF_BITMAP_FMT_R8G8B8A8.
+ */
+ NSGIF_BITMAP_FMT_RGBA8888,
+
+ /**
+ * 32-bit BGRA (0xBBGGRRAA).
+ *
+ * * On little endian host, same as \ref NSGIF_BITMAP_FMT_A8R8G8B8.
+ * * On big endian host, same as \ref NSGIF_BITMAP_FMT_B8G8R8A8.
+ */
+ NSGIF_BITMAP_FMT_BGRA8888,
+
+ /**
+ * 32-bit ARGB (0xAARRGGBB).
+ *
+ * * On little endian host, same as \ref NSGIF_BITMAP_FMT_B8G8R8A8.
+ * * On big endian host, same as \ref NSGIF_BITMAP_FMT_A8R8G8B8.
+ */
+ NSGIF_BITMAP_FMT_ARGB8888,
+
+ /**
+ * 32-bit BGRA (0xAABBGGRR).
+ *
+ * * On little endian host, same as \ref NSGIF_BITMAP_FMT_R8G8B8A8.
+ * * On big endian host, same as \ref NSGIF_BITMAP_FMT_A8B8G8R8.
+ */
+ NSGIF_BITMAP_FMT_ABGR8888,
+} nsgif_bitmap_fmt_t;
+
/**
* Client bitmap type.
*
* These are client-created and destroyed, via the \ref bitmap callbacks,
* but they are owned by a \ref nsgif_t.
*
- * The pixel buffer is is 32bpp, treated as individual bytes in the component
- * order RR GG BB AA. For example, a 1x1 image with a single orange pixel would
- * be encoded as the following sequence of bytes: 0xff, 0x88, 0x00, 0x00.
+ * See \ref nsgif_bitmap_fmt for pixel format information.
*/
typedef void nsgif_bitmap_t;
@@ -176,13 +231,15 @@ const char *nsgif_strerror(nsgif_error err);
/**
* Create the NSGIF object.
*
- * \param[in] bitmap_vt Bitmap operation functions v-table.
- * \param[out] gif_out Return \ref nsgif_t object on success.
+ * \param[in] bitmap_vt Bitmap operation functions v-table.
+ * \param[in] bitmap_fmt Bitmap pixel format specification.
+ * \param[out] gif_out Return \ref nsgif_t object on success.
*
* \return NSGIF_OK on success, or appropriate error otherwise.
*/
nsgif_error nsgif_create(
const nsgif_bitmap_cb_vt *bitmap_vt,
+ nsgif_bitmap_fmt_t bitmap_fmt,
nsgif_t **gif_out);
/**
@@ -286,10 +343,8 @@ typedef struct nsgif_info {
uint32_t frame_count;
/** number of times to play animation (zero means loop forever) */
int loop_max;
- /** number of animation loops so far */
- int loop_count;
/** background colour in same pixel format as \ref nsgif_bitmap_t. */
- uint8_t background[4];
+ uint32_t background;
} nsgif_info_t;
/**
@@ -353,4 +408,35 @@ const nsgif_frame_info_t *nsgif_get_frame_info(
const nsgif_t *gif,
uint32_t frame);
+/**
+ * Configure handling of small frame delays.
+ *
+ * Historically people created GIFs with a tiny frame delay, however the slow
+ * hardware of the time meant they actually played much slower. As computers
+ * sped up, to prevent animations playing faster than intended, decoders came
+ * to ignore overly small frame delays.
+ *
+ * By default a \ref nsgif_frame_prepare() managed animation will override
+ * frame delays of less than 2 centiseconds with a default frame delay of
+ * 10 centiseconds. This matches the behaviour of web browsers and other
+ * renderers.
+ *
+ * Both the minimum and the default values can be overridden for a given GIF
+ * by the client. To get frame delays exactly as specified by the GIF file, set
+ * \ref delay_min to zero.
+ *
+ * Note that this does not affect the frame delay in the frame info
+ * (\ref nsgif_frame_info_t) structure, which will always contain values
+ * specified by the GIF.
+ *
+ * \param[in] gif The \ref nsgif_t object to configure.
+ * \param[in] delay_min The minimum frame delay in centiseconds.
+ * \param[in] delay_default The delay to use if a frame delay is less than
+ * \ref delay_min.
+ */
+void nsgif_set_frame_delay_behaviour(
+ nsgif_t *gif,
+ uint16_t delay_min,
+ uint16_t delay_default);
+
#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]