[gegl] subprojects/libnsgif: update to latest upstream



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]