[SeaBIOS] Re: Animated GIF bootsplash

Michael Bacarella posted 1 patch 1 month ago
Failed in applying to current master (apply log)
Makefile         |   2 +-
src/boot.c       |   7 +-
src/bootsplash.c | 165 ++++++++---
src/gif.c        | 710 +++++++++++++++++++++++++++++++++++++++++++++++
src/string.c     |  24 +-
src/string.h     |   4 +-
src/util.h       |  16 +-
7 files changed, 871 insertions(+), 57 deletions(-)
create mode 100644 src/gif.c
[SeaBIOS] Re: Animated GIF bootsplash
Posted by Michael Bacarella 1 month ago
On Sat, Aug 20, 2022 at 11:45 PM Paul Menzel <pmenzel@molgen.mpg.de> wrote:
>
> Dear Michael,
>
>
> Thank you for implementing this. If you send a patch, I can test on real
> hardware.
>
>
> Kind regards,
>
> Paul
>
>
> PS: It’d be great, if you could used interleaved style, when responding.

Patch attached. I replied here rather than with a new email with a PATCH subject
because I don't consider it ready to merge, more like a POC.

Also I'm using gmail, hope I don't embarrass myself.

[PATCH] bootsplash: support GIFs, with animation

Adapts public domain GIF library from /github.com/lecram/gifdec

Signed-off-by: Michael Bacarella <m@bacarella.com>
---
 Makefile         |   2 +-
 src/boot.c       |   7 +-
 src/bootsplash.c | 165 ++++++++---
 src/gif.c        | 710 +++++++++++++++++++++++++++++++++++++++++++++++
 src/string.c     |  24 +-
 src/string.h     |   4 +-
 src/util.h       |  16 +-
 7 files changed, 871 insertions(+), 57 deletions(-)
 create mode 100644 src/gif.c

diff --git a/Makefile b/Makefile
index c108f87..f0f7742 100644
--- a/Makefile
+++ b/Makefile
@@ -38,7 +38,7 @@ SRCBOTH=misc.c stacks.c output.c string.c block.c
cdrom.c disk.c \
     hw/mpt-scsi.c
 SRC16=$(SRCBOTH)
 SRC32FLAT=$(SRCBOTH) post.c e820map.c malloc.c romfile.c x86.c \
-    optionroms.c pmm.c font.c boot.c bootsplash.c jpeg.c bmp.c \
+    optionroms.c pmm.c font.c boot.c bootsplash.c gif.c jpeg.c bmp.c \
     tcgbios.c sha1.c hw/pcidevice.c hw/ahci.c hw/pvscsi.c \
     hw/usb-xhci.c hw/usb-hub.c hw/sdcard.c fw/coreboot.c \
     fw/lzmadecode.c fw/multiboot.c fw/csm.c fw/biostables.c \
diff --git a/src/boot.c b/src/boot.c
index 1effd80..641a418 100644
--- a/src/boot.c
+++ b/src/boot.c
@@ -717,8 +717,11 @@ interactive_bootmenu(void)
     free(bootmsg);

     u32 menutime = romfile_loadint("etc/boot-menu-wait",
DEFAULT_BOOTMENU_WAIT);
-    enable_bootsplash();
-    int scan_code = get_keystroke(menutime);
+    int scan_code = !menukey;
+    if (enable_bootsplash(menutime, &scan_code) != 1)
+    {
+        scan_code = get_keystroke(menutime);
+    }
     disable_bootsplash();
     if (scan_code != menukey)
         return;
diff --git a/src/bootsplash.c b/src/bootsplash.c
index 538b316..1968ee8 100644
--- a/src/bootsplash.c
+++ b/src/bootsplash.c
@@ -94,32 +94,39 @@ find_videomode(struct vbe_info *vesa_info, struct
vbe_mode_info *mode_info

 static int BootsplashActive;

-void
-enable_bootsplash(void)
+int enable_bootsplash(u32 menutime, int *scan_code)
 {
     if (!CONFIG_BOOTSPLASH)
-        return;
-    /* splash picture can be bmp or jpeg file */
+        return 0;
+    /* splash picture can be bmp or jpeg or gif file */
     dprintf(3, "Checking for bootsplash\n");
-    u8 type = 0; /* 0 means jpg, 1 means bmp, default is 0=jpg */
+    u8 type = 0; /* 0 means jpg, 1 means bmp, 2 means gif default is 0=jpg */
     int filesize;
     u8 *filedata = romfile_loadfile("bootsplash.jpg", &filesize);
-    if (!filedata) {
+    if (!filedata)
+    {
+        type = 1;
         filedata = romfile_loadfile("bootsplash.bmp", &filesize);
         if (!filedata)
-            return;
-        type = 1;
+        {
+            type = 2;
+            filedata = romfile_loadfile("bootsplash.gif", &filesize);
+            if (!filedata)
+                return 0;
+        }
     }
     dprintf(3, "start showing bootsplash\n");

     u8 *picture = NULL; /* data buff used to be flushed to the video buf */
     struct jpeg_decdata *jpeg = NULL;
     struct bmp_decdata *bmp = NULL;
+    struct gif_decdata *gif = NULL;
     struct vbe_info *vesa_info = malloc_tmplow(sizeof(*vesa_info));
     struct vbe_mode_info *mode_info = malloc_tmplow(sizeof(*mode_info));
-    if (!vesa_info || !mode_info) {
+    if (!vesa_info || !mode_info)
+    {
         warn_noalloc();
-        goto done;
+        goto fail;
     }

     /* Check whether we have a VESA 2.0 compliant BIOS */
@@ -131,59 +138,86 @@ enable_bootsplash(void)
     br.di = FLATPTR_TO_OFFSET(vesa_info);
     br.es = FLATPTR_TO_SEG(vesa_info);
     call16_int10(&br);
-    if (vesa_info->signature != VESA_SIGNATURE) {
-        dprintf(1,"No VBE2 found.\n");
-        goto done;
+    if (vesa_info->signature != VESA_SIGNATURE)
+    {
+        dprintf(1, "No VBE2 found.\n");
+        goto fail;
     }

     /* Print some debugging information about our card. */
     char *vendor = SEGOFF_TO_FLATPTR(vesa_info->oem_vendor_string);
     char *product = SEGOFF_TO_FLATPTR(vesa_info->oem_product_string);
     dprintf(3, "VESA %d.%d\nVENDOR: %s\nPRODUCT: %s\n",
-            vesa_info->version>>8, vesa_info->version&0xff,
+            vesa_info->version >> 8, vesa_info->version & 0xff,
             vendor, product);

     int ret, width, height;
     int bpp_require = 0;
-    if (type == 0) {
+    if (type == 0)
+    {
         jpeg = jpeg_alloc();
-        if (!jpeg) {
+        if (!jpeg)
+        {
             warn_noalloc();
-            goto done;
+            goto fail;
         }
         /* Parse jpeg and get image size. */
         dprintf(5, "Decoding bootsplash.jpg\n");
         ret = jpeg_decode(jpeg, filedata);
-        if (ret) {
+        if (ret)
+        {
             dprintf(1, "jpeg_decode failed with return code %d...\n", ret);
-            goto done;
+            goto fail;
         }
         jpeg_get_size(jpeg, &width, &height);
-    } else {
+    }
+    else if (type == 1)
+    {
         bmp = bmp_alloc();
-        if (!bmp) {
+        if (!bmp)
+        {
             warn_noalloc();
-            goto done;
+            goto fail;
         }
         /* Parse bmp and get image size. */
         dprintf(5, "Decoding bootsplash.bmp\n");
         ret = bmp_decode(bmp, filedata, filesize);
-        if (ret) {
+        if (ret)
+        {
             dprintf(1, "bmp_decode failed with return code %d...\n", ret);
-            goto done;
+            goto fail;
         }
         bmp_get_info(bmp, &width, &height, &bpp_require);
     }
+    else
+    {
+        gif = gif_alloc();
+        if (!gif)
+        {
+            warn_noalloc();
+            goto fail;
+        }
+        /* Parse gif and get image size. */
+        dprintf(5, "Decoding bootsplash.gif\n");
+        ret = gif_decode(gif, filedata);
+        if (ret)
+        {
+            dprintf(1, "gif_decode failed with return code %d...\n", ret);
+            goto fail;
+        }
+        gif_get_info(gif, &width, &height, &bpp_require);
+    }

     // jpeg would use 16 or 24 bpp video mode, BMP uses 16/24/32 bpp mode.

     // Try to find a graphics mode with the corresponding dimensions.
     int videomode = find_videomode(vesa_info, mode_info, width, height,
-                                       bpp_require);
-    if (videomode < 0) {
+                                   bpp_require);
+    if (videomode < 0)
+    {
         dprintf(1, "failed to find a videomode with %dx%d %dbpp (0=any).\n",
-                    width, height, bpp_require);
-        goto done;
+                width, height, bpp_require);
+        goto fail;
     }
     void *framebuffer = (void *)mode_info->phys_base;
     int depth = mode_info->bits_per_pixel;
@@ -195,26 +229,43 @@ enable_bootsplash(void)
     // Allocate space for image and decompress it.
     int imagesize = height * mode_info->bytes_per_scanline;
     picture = malloc_tmphigh(imagesize);
-    if (!picture) {
+    if (!picture)
+    {
         warn_noalloc();
-        goto done;
+        goto fail;
     }

-    if (type == 0) {
+    if (type == 0)
+    {
         dprintf(5, "Decompressing bootsplash.jpg\n");
         ret = jpeg_show(jpeg, picture, width, height, depth,
-                            mode_info->bytes_per_scanline);
-        if (ret) {
+                        mode_info->bytes_per_scanline);
+        if (ret)
+        {
             dprintf(1, "jpeg_show failed with return code %d...\n", ret);
-            goto done;
+            goto fail;
         }
-    } else {
+    }
+    else if (type == 1)
+    {
         dprintf(5, "Decompressing bootsplash.bmp\n");
         ret = bmp_show(bmp, picture, width, height, depth,
-                           mode_info->bytes_per_scanline);
-        if (ret) {
+                       mode_info->bytes_per_scanline);
+        if (ret)
+        {
             dprintf(1, "bmp_show failed with return code %d...\n", ret);
-            goto done;
+            goto fail;
+        }
+    }
+    else
+    {
+        dprintf(5, "Decompressing bootsplash.gif\n");
+        ret = gif_show(gif, picture, width, height, depth,
+                       mode_info->bytes_per_scanline);
+        if (ret < 0)
+        {
+            dprintf(1, "gif_show failed with return code %d...\n", ret);
+            goto fail;
         }
     }

@@ -224,9 +275,10 @@ enable_bootsplash(void)
     br.ax = 0x4f02;
     br.bx = videomode | VBE_MODE_LINEAR_FRAME_BUFFER;
     call16_int10(&br);
-    if (br.ax != 0x4f) {
+    if (br.ax != 0x4f)
+    {
         dprintf(1, "set_mode failed.\n");
-        goto done;
+        goto fail;
     }

     /* Show the picture */
@@ -235,6 +287,33 @@ enable_bootsplash(void)
     dprintf(5, "Bootsplash copy complete\n");
     BootsplashActive = 1;

+    /* Animate the gif */
+    if (type == 2)
+    {
+        u32 end = irqtimer_calc(menutime);
+        while (!irqtimer_check(end))
+        {
+            // TODO: don't delay past end time
+            *scan_code = get_keystroke(gif_get_gce_delay(gif));
+            if (*scan_code != -1)
+                break;
+            ret = gif_show(gif, picture, width, height, depth,
+                           mode_info->bytes_per_scanline);
+            iomemcpy(framebuffer, picture, imagesize);
+
+            if (ret == 0)
+                gif_rewind(gif);
+        }
+        ret = 1;
+        goto done;
+    }
+    else
+    {
+        ret = 0;
+        goto done;
+    }
+fail:
+    ret = -1;
 done:
     free(filedata);
     free(picture);
@@ -242,11 +321,11 @@ done:
     free(mode_info);
     free(jpeg);
     free(bmp);
-    return;
+    free(gif);
+    return ret;
 }

-void
-disable_bootsplash(void)
+void disable_bootsplash(void)
 {
     if (!CONFIG_BOOTSPLASH || !BootsplashActive)
         return;
diff --git a/src/gif.c b/src/gif.c
new file mode 100644
index 0000000..87d2568
--- /dev/null
+++ b/src/gif.c
@@ -0,0 +1,710 @@
+
+#define __LITTLE_ENDIAN
+#include "malloc.h"
+#include "output.h" // dprintf
+#include "string.h"
+#include "util.h"
+
+#define MIN(A, B) ((A) < (B) ? (A) : (B))
+#define MAX(A, B) ((A) > (B) ? (A) : (B))
+
+typedef u64 off_t;
+
+enum whence
+{
+    SEEK_CUR,
+    SEEK_SET
+};
+
+typedef struct gd_Palette
+{
+    int size;
+    u8 colors[0x100 * 3];
+} gd_Palette;
+
+typedef struct gd_GCE
+{
+    u16 delay;
+    u8 tindex;
+    u8 disposal;
+    int input;
+    int transparency;
+} gd_GCE;
+
+typedef struct fdmap_t
+{
+    const unsigned char *map;
+    int pos;
+} fdmap_t;
+
+struct gif_decdata
+{
+    fdmap_t *fdmap;
+    off_t anim_start;
+    u16 width, height;
+    u16 depth;
+    u16 loop_count;
+    gd_GCE gce;
+    gd_Palette *palette;
+    gd_Palette lct, gct;
+    void (*plain_text)(
+        struct gif_decdata *gif, u16 tx, u16 ty,
+        u16 tw, u16 th, u8 cw, u8 ch,
+        u8 fg, u8 bg);
+    void (*comment)(struct gif_decdata *gif);
+    void (*application)(struct gif_decdata *gif, char id[8], char auth[3]);
+    u16 fx, fy, fw, fh;
+    u8 bgindex;
+    u8 *canvas, *frame;
+};
+
+typedef struct Entry
+{
+    u16 length;
+    u16 prefix;
+    u8 suffix;
+} Entry;
+
+typedef struct Table
+{
+    int bulk;
+    int nentries;
+    Entry *entries;
+} Table;
+
+static fdmap_t *
+fdmap_create(const unsigned char *map)
+{
+    fdmap_t *p = malloc_tmplow(sizeof(fdmap_t));
+    if (p == NULL)
+    {
+        dprintf(1, "fdmap_create: malloc failed\n");
+        return NULL;
+    }
+    p->map = map;
+    p->pos = 0;
+
+    return p;
+}
+
+static void
+fdmap_free(fdmap_t *p)
+{
+    free(p);
+}
+
+static int
+fdmap_read(fdmap_t *fdmap, void *buf, int len)
+{
+    memcpy(buf, fdmap->map + fdmap->pos, len);
+    fdmap->pos += len;
+    return len;
+}
+
+static int
+fdmap_lseek(fdmap_t *fdmap, int off, int whence)
+{
+    switch (whence)
+    {
+    case SEEK_SET:
+        fdmap->pos = off;
+        break;
+    case SEEK_CUR:
+        fdmap->pos += off;
+        break;
+    default:
+        dprintf(1, "fdmap_lseek whence %d not implemented", whence);
+        return -1;
+    }
+    return fdmap->pos;
+}
+
+static u16
+read_num(fdmap_t *fdmap)
+{
+    u16 num;
+    int pos = fdmap->pos;
+    num = fdmap->map[pos] + (((u16)fdmap->map[pos + 1]) << 8);
+    fdmap->pos += 2;
+    return num;
+}
+
+struct gif_decdata *
+gif_alloc(void)
+{
+    struct gif_decdata *gif = malloc_tmphigh(sizeof(*gif));
+    memset(gif, 0, sizeof(*gif));
+    return gif;
+}
+
+int gif_decode(struct gif_decdata *gif, unsigned char *buf)
+{
+    fdmap_t *fdmap;
+    u8 sigver[3];
+    u16 width, height, depth;
+    u8 fdsz, bgidx, aspect;
+    int i;
+    u8 *bgcolor;
+    int gct_sz;
+
+    if (!gif || !buf)
+        return -1;
+    fdmap = fdmap_create(buf);
+    if (fdmap == NULL)
+        return -1;
+    /* Header */
+    fdmap_read(fdmap, sigver, 3);
+    if (memcmp(sigver, "GIF", 3) != 0)
+    {
+        dprintf(1, "invalid GIF signature\n");
+        goto fail;
+    }
+    /* Version */
+    fdmap_read(fdmap, sigver, 3);
+    if (memcmp(sigver, "89a", 3) != 0)
+    {
+        dprintf(1, "invalid GIF version\n");
+        goto fail;
+    }
+    /* Width x Height */
+    width = read_num(fdmap);
+    height = read_num(fdmap);
+    /* FDSZ */
+    fdmap_read(fdmap, &fdsz, 1);
+    /* Presence of GCT */
+    if (!(fdsz & 0x80))
+    {
+        dprintf(1, "no global color table\n");
+        goto fail;
+    }
+    /* Color Space's Depth */
+    depth = ((fdsz >> 4) & 7) + 1;
+    /* Ignore Sort Flag. */
+    /* GCT Size */
+    gct_sz = 1 << ((fdsz & 0x07) + 1);
+    /* Background Color Index */
+    fdmap_read(fdmap, &bgidx, 1);
+    /* Aspect Ratio */
+    fdmap_read(fdmap, &aspect, 1);
+
+    gif->fdmap = fdmap;
+    gif->width = width;
+    gif->height = height;
+    gif->depth = depth;
+    /* Read GCT */
+    gif->gct.size = gct_sz;
+    fdmap_read(fdmap, gif->gct.colors, 3 * gif->gct.size);
+    gif->palette = &gif->gct;
+    gif->bgindex = bgidx;
+    gif->frame = malloc_tmphigh(4 * width * height);
+    if (!gif->frame)
+    {
+        free(gif);
+        goto fail;
+    }
+    memset(gif->frame, 0, 4 * width * height);
+    gif->canvas = &gif->frame[width * height];
+    if (gif->bgindex)
+        memset(gif->frame, gif->bgindex, gif->width * gif->height);
+    bgcolor = &gif->palette->colors[gif->bgindex * 3];
+    if (bgcolor[0] || bgcolor[1] || bgcolor[2])
+        for (i = 0; i < gif->width * gif->height; i++)
+            memcpy(&gif->canvas[i * 3], bgcolor, 3);
+    gif->anim_start = fdmap_lseek(fdmap, 0, SEEK_CUR);
+    goto ok;
+fail:
+    fdmap_free(fdmap);
+    return -1;
+ok:
+    return 0;
+}
+
+void gif_get_info(struct gif_decdata *gif, int *width, int *height,
int *bpp_require)
+{
+    *width = gif->width;
+    *height = gif->height;
+    *bpp_require = 24;
+}
+
+int gif_get_gce_delay(struct gif_decdata *gif)
+{
+    return gif->gce.delay;
+}
+
+static void
+discard_sub_blocks(struct gif_decdata *gif)
+{
+    u8 size;
+    do
+    {
+        fdmap_read(gif->fdmap, &size, 1);
+        fdmap_lseek(gif->fdmap, size, SEEK_CUR);
+    } while (size);
+}
+
+static void
+read_plain_text_ext(struct gif_decdata *gif)
+{
+    if (gif->plain_text)
+    {
+        u16 tx, ty, tw, th;
+        u8 cw, ch, fg, bg;
+        off_t sub_block;
+        fdmap_lseek(gif->fdmap, 1, SEEK_CUR); /* block size = 12 */
+        tx = read_num(gif->fdmap);
+        ty = read_num(gif->fdmap);
+        tw = read_num(gif->fdmap);
+        th = read_num(gif->fdmap);
+        fdmap_read(gif->fdmap, &cw, 1);
+        fdmap_read(gif->fdmap, &ch, 1);
+        fdmap_read(gif->fdmap, &fg, 1);
+        fdmap_read(gif->fdmap, &bg, 1);
+        sub_block = fdmap_lseek(gif->fdmap, 0, SEEK_CUR);
+        gif->plain_text(gif, tx, ty, tw, th, cw, ch, fg, bg);
+        fdmap_lseek(gif->fdmap, sub_block, SEEK_SET);
+    }
+    else
+    {
+        /* Discard plain text metadata. */
+        fdmap_lseek(gif->fdmap, 13, SEEK_CUR);
+    }
+    /* Discard plain text sub-blocks. */
+    discard_sub_blocks(gif);
+}
+
+static void
+read_graphic_control_ext(struct gif_decdata *gif)
+{
+    u8 rdit;
+
+    /* Discard block size (always 0x04). */
+    fdmap_lseek(gif->fdmap, 1, SEEK_CUR);
+    fdmap_read(gif->fdmap, &rdit, 1);
+    gif->gce.disposal = (rdit >> 2) & 3;
+    gif->gce.input = rdit & 2;
+    gif->gce.transparency = rdit & 1;
+    gif->gce.delay = read_num(gif->fdmap);
+    fdmap_read(gif->fdmap, &gif->gce.tindex, 1);
+    /* Skip block terminator. */
+    fdmap_lseek(gif->fdmap, 1, SEEK_CUR);
+}
+
+static void
+read_comment_ext(struct gif_decdata *gif)
+{
+    if (gif->comment)
+    {
+        off_t sub_block = fdmap_lseek(gif->fdmap, 0, SEEK_CUR);
+        gif->comment(gif);
+        fdmap_lseek(gif->fdmap, sub_block, SEEK_SET);
+    }
+    /* Discard comment sub-blocks. */
+    discard_sub_blocks(gif);
+}
+
+static void
+read_application_ext(struct gif_decdata *gif)
+{
+    char app_id[8];
+    char app_auth_code[3];
+
+    /* Discard block size (always 0x0B). */
+    fdmap_lseek(gif->fdmap, 1, SEEK_CUR);
+    /* Application Identifier. */
+    fdmap_read(gif->fdmap, app_id, 8);
+    /* Application Authentication Code. */
+    fdmap_read(gif->fdmap, app_auth_code, 3);
+    if (!strncmp(app_id, "NETSCAPE", sizeof(app_id)))
+    {
+        /* Discard block size (0x03) and constant byte (0x01). */
+        fdmap_lseek(gif->fdmap, 2, SEEK_CUR);
+        gif->loop_count = read_num(gif->fdmap);
+        /* Skip block terminator. */
+        fdmap_lseek(gif->fdmap, 1, SEEK_CUR);
+    }
+    else if (gif->application)
+    {
+        off_t sub_block = fdmap_lseek(gif->fdmap, 0, SEEK_CUR);
+        gif->application(gif, app_id, app_auth_code);
+        fdmap_lseek(gif->fdmap, sub_block, SEEK_SET);
+        discard_sub_blocks(gif);
+    }
+    else
+    {
+        discard_sub_blocks(gif);
+    }
+}
+
+static void
+read_ext(struct gif_decdata *gif)
+{
+    u8 label;
+
+    fdmap_read(gif->fdmap, &label, 1);
+    switch (label)
+    {
+    case 0x01:
+        read_plain_text_ext(gif);
+        break;
+    case 0xF9:
+        read_graphic_control_ext(gif);
+        break;
+    case 0xFE:
+        read_comment_ext(gif);
+        break;
+    case 0xFF:
+        read_application_ext(gif);
+        break;
+    default:
+        dprintf(1, "unknown extension: %02X\n", label);
+    }
+}
+
+static Table *
+new_table(int key_size)
+{
+    int key;
+    int init_bulk = MAX(1 << (key_size + 1), 0x100);
+    Table *table = malloc_tmphigh(sizeof(*table) + sizeof(Entry) * init_bulk);
+    if (table)
+    {
+        table->bulk = init_bulk;
+        table->nentries = (1 << key_size) + 2;
+        table->entries = (Entry *)&table[1];
+        for (key = 0; key < (1 << key_size); key++)
+            table->entries[key] = (Entry){1, 0xFFF, key};
+    }
+    return table;
+}
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+/* Add table entry. Return value:
+ *  0 on success
+ *  +1 if key size must be incremented after this addition
+ *  -1 if could not realloc table */
+static int
+add_entry(Table **tablep, u16 length, u16 prefix, u8 suffix)
+{
+    Table *table = *tablep;
+    Table *new_table;
+    int old_size;
+
+    if (table->nentries == table->bulk)
+    {
+        old_size = sizeof(*table) + sizeof(Entry) * table->bulk;
+        table->bulk *= 2;
+        new_table = malloc_tmphigh(sizeof(*table) + sizeof(Entry) *
table->bulk);
+        if (!table)
+            return -1;
+        memcpy(new_table, table, old_size);
+        free(table);
+        table = new_table;
+        table->entries = (Entry *)&table[1];
+        *tablep = table;
+    }
+    table->entries[table->nentries] = (Entry){length, prefix, suffix};
+    table->nentries++;
+    if ((table->nentries & (table->nentries - 1)) == 0)
+        return 1;
+    return 0;
+}
+#pragma GCC diagnostic pop
+
+static u16
+get_key(struct gif_decdata *gif, int key_size, u8 *sub_len, u8
*shift, u8 *byte)
+{
+    int bits_read;
+    int rpad;
+    int frag_size;
+    u16 key;
+
+    key = 0;
+    for (bits_read = 0; bits_read < key_size; bits_read += frag_size)
+    {
+        rpad = (*shift + bits_read) % 8;
+        if (rpad == 0)
+        {
+            /* Update byte. */
+            if (*sub_len == 0)
+            {
+                fdmap_read(gif->fdmap, sub_len, 1); /* Must be nonzero! */
+                if (*sub_len == 0)
+                    return 0x1000;
+            }
+            fdmap_read(gif->fdmap, byte, 1);
+            (*sub_len)--;
+        }
+        frag_size = MIN(key_size - bits_read, 8 - rpad);
+        key |= ((u16)((*byte) >> rpad)) << bits_read;
+    }
+    /* Clear extra bits to the left. */
+    key &= (1 << key_size) - 1;
+    *shift = (*shift + key_size) % 8;
+    return key;
+}
+
+/* Compute output index of y-th input line, in frame of height h. */
+static int
+interlaced_line_index(int h, int y)
+{
+    int p; /* number of lines in current pass */
+
+    p = (h - 1) / 8 + 1;
+    if (y < p) /* pass 1 */
+        return y * 8;
+    y -= p;
+    p = (h - 5) / 8 + 1;
+    if (y < p) /* pass 2 */
+        return y * 8 + 4;
+    y -= p;
+    p = (h - 3) / 4 + 1;
+    if (y < p) /* pass 3 */
+        return y * 4 + 2;
+    y -= p;
+    /* pass 4 */
+    return y * 2 + 1;
+}
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+/* Decompress image pixels.
+ * Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table). */
+static int
+read_image_data(struct gif_decdata *gif, int interlace)
+{
+    u8 sub_len, shift, byte;
+    int init_key_size, key_size, table_is_full;
+    int frm_off, frm_size, str_len, i, p, x, y;
+    u16 key, clear, stop;
+    int ret;
+    Table *table;
+    Entry entry;
+    off_t start, end;
+
+    fdmap_read(gif->fdmap, &byte, 1);
+    key_size = (int)byte;
+    if (key_size < 2 || key_size > 8)
+        return -1;
+
+    start = fdmap_lseek(gif->fdmap, 0, SEEK_CUR);
+    discard_sub_blocks(gif);
+    end = fdmap_lseek(gif->fdmap, 0, SEEK_CUR);
+    fdmap_lseek(gif->fdmap, start, SEEK_SET);
+    clear = 1 << key_size;
+    stop = clear + 1;
+    table = new_table(key_size);
+    key_size++;
+    init_key_size = key_size;
+    sub_len = shift = 0;
+    key = get_key(gif, key_size, &sub_len, &shift, &byte); /* clear code */
+    frm_off = 0;
+    ret = 0;
+    frm_size = gif->fw * gif->fh;
+    while (frm_off < frm_size)
+    {
+        if (key == clear)
+        {
+            key_size = init_key_size;
+            table->nentries = (1 << (key_size - 1)) + 2;
+            table_is_full = 0;
+        }
+        else if (!table_is_full)
+        {
+            ret = add_entry(&table, str_len + 1, key, entry.suffix);
+            if (ret == -1)
+            {
+                free(table);
+                return -1;
+            }
+            if (table->nentries == 0x1000)
+            {
+                ret = 0;
+                table_is_full = 1;
+            }
+        }
+        key = get_key(gif, key_size, &sub_len, &shift, &byte);
+        if (key == clear)
+            continue;
+        if (key == stop || key == 0x1000)
+            break;
+        if (ret == 1)
+            key_size++;
+        entry = table->entries[key];
+        str_len = entry.length;
+        for (i = 0; i < str_len; i++)
+        {
+            p = frm_off + entry.length - 1;
+            x = p % gif->fw;
+            y = p / gif->fw;
+            if (interlace)
+                y = interlaced_line_index((int)gif->fh, y);
+            gif->frame[(gif->fy + y) * gif->width + gif->fx + x] =
entry.suffix;
+            if (entry.prefix == 0xFFF)
+                break;
+            else
+                entry = table->entries[entry.prefix];
+        }
+        frm_off += str_len;
+        if (key < table->nentries - 1 && !table_is_full)
+            table->entries[table->nentries - 1].suffix = entry.suffix;
+    }
+    free(table);
+    if (key == stop)
+        fdmap_read(gif->fdmap, &sub_len, 1); /* Must be zero! */
+    fdmap_lseek(gif->fdmap, end, SEEK_SET);
+    return 0;
+}
+#pragma GCC diagnostic pop
+
+/* Read image.
+ * Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table). */
+static int
+read_image(struct gif_decdata *gif)
+{
+    u8 fisrz;
+    int interlace;
+
+    /* Image Descriptor. */
+    gif->fx = read_num(gif->fdmap);
+    gif->fy = read_num(gif->fdmap);
+
+    if (gif->fx >= gif->width || gif->fy >= gif->height)
+        return -1;
+
+    gif->fw = read_num(gif->fdmap);
+    gif->fh = read_num(gif->fdmap);
+
+    gif->fw = MIN(gif->fw, gif->width - gif->fx);
+    gif->fh = MIN(gif->fh, gif->height - gif->fy);
+
+    fdmap_read(gif->fdmap, &fisrz, 1);
+    interlace = fisrz & 0x40;
+    /* Ignore Sort Flag. */
+    /* Local Color Table? */
+    if (fisrz & 0x80)
+    {
+        /* Read LCT */
+        gif->lct.size = 1 << ((fisrz & 0x07) + 1);
+        fdmap_read(gif->fdmap, gif->lct.colors, 3 * gif->lct.size);
+        gif->palette = &gif->lct;
+    }
+    else
+        gif->palette = &gif->gct;
+    /* Image Data. */
+    return read_image_data(gif, interlace);
+}
+
+static void
+render_frame_rect(struct gif_decdata *gif, u8 *buffer)
+{
+    int i, j, k;
+    u8 index, *color;
+    i = gif->fy * gif->width + gif->fx;
+    for (j = 0; j < gif->fh; j++)
+    {
+        for (k = 0; k < gif->fw; k++)
+        {
+            index = gif->frame[(gif->fy + j) * gif->width + gif->fx + k];
+            color = &gif->palette->colors[index * 3];
+            if (!gif->gce.transparency || index != gif->gce.tindex)
+                memcpy(&buffer[(i + k) * 3], color, 3);
+        }
+        i += gif->width;
+    }
+}
+
+static void
+dispose(struct gif_decdata *gif)
+{
+    int i, j, k;
+    u8 *bgcolor;
+    switch (gif->gce.disposal)
+    {
+    case 2: /* Restore to background color. */
+        bgcolor = &gif->palette->colors[gif->bgindex * 3];
+        i = gif->fy * gif->width + gif->fx;
+        for (j = 0; j < gif->fh; j++)
+        {
+            for (k = 0; k < gif->fw; k++)
+                memcpy(&gif->canvas[(i + k) * 3], bgcolor, 3);
+            i += gif->width;
+        }
+        break;
+    case 3: /* Restore to previous, i.e., don't update canvas.*/
+        break;
+    default:
+        /* Add frame non-transparent pixels to canvas. */
+        render_frame_rect(gif, gif->canvas);
+    }
+}
+
+/* Return 1 if got a frame; 0 if got GIF trailer; -1 if error. */
+int gd_get_frame(struct gif_decdata *gif)
+{
+    char sep;
+
+    dispose(gif);
+    fdmap_read(gif->fdmap, &sep, 1);
+    while (sep != ',')
+    {
+        if (sep == ';')
+            return 0;
+        if (sep == '!')
+            read_ext(gif);
+        else
+            return -1;
+        fdmap_read(gif->fdmap, &sep, 1);
+    }
+    if (read_image(gif) == -1)
+        return -1;
+    return 1;
+}
+
+void gd_render_frame(struct gif_decdata *gif, u8 *buffer)
+{
+    memcpy(buffer, gif->canvas, gif->width * gif->height * 3);
+    render_frame_rect(gif, buffer);
+}
+
+int gd_is_bgcolor(struct gif_decdata *gif, u8 color[3])
+{
+    return !memcmp(&gif->palette->colors[gif->bgindex * 3], color, 3);
+}
+
+void gd_rewind(struct gif_decdata *gif)
+{
+    fdmap_lseek(gif->fdmap, gif->anim_start, SEEK_SET);
+}
+
+void gif_rewind(struct gif_decdata *gif) { gd_rewind(gif); }
+
+int gif_show(struct gif_decdata *gif, unsigned char *pic, int width,
int height, int depth, int bytes_per_line_dest)
+{
+    int ret;
+
+    if (gif->height != height)
+    {
+        dprintf(1, "gif height %d != picture height %d", gif->height, height);
+        return -1;
+    }
+
+    if (gif->width != width)
+    {
+        dprintf(1, "gif height %d != picture height %d", gif->width, width);
+        return -1;
+    }
+
+    ret = gd_get_frame(gif);
+    if (ret == -1)
+        return -1;
+    gd_render_frame(gif, pic);
+    return ret;
+}
+
+void gd_close_gif(struct gif_decdata *gif)
+{
+    fdmap_free(gif->fdmap);
+    free(gif->frame);
+    free(gif);
+}
diff --git a/src/string.c b/src/string.c
index adb8198..ba6437a 100644
--- a/src/string.c
+++ b/src/string.c
@@ -70,19 +70,35 @@ memcmp(const void *s1, const void *s2, size_t n)
 }

 // Compare two strings.
-int
-strcmp(const char *s1, const char *s2)
+int strcmp(const char *s1, const char *s2)
 {
-    for (;;) {
+    for (;;)
+    {
         if (*s1 != *s2)
             return *s1 < *s2 ? -1 : 1;
-        if (! *s1)
+        if (!*s1)
             return 0;
         s1++;
         s2++;
     }
 }

+// Compare two strings.
+int strncmp(const char *s1, const char *s2, size_t n)
+{
+    for (;;)
+    {
+        if (*s1 != *s2)
+            return *s1 < *s2 ? -1 : 1;
+        if (!*s1)
+            return 0;
+        if (!n)
+            return 0;
+        n--;
+        s1++;
+        s2++;
+    }
+}
 inline void
 memset_far(u16 d_seg, void *d_far, u8 c, size_t len)
 {
diff --git a/src/string.h b/src/string.h
index d069989..4d841fd 100644
--- a/src/string.h
+++ b/src/string.h
@@ -11,12 +11,12 @@ size_t strlen(const char *s);
 int memcmp_far(u16 s1seg, const void *s1, u16 s2seg, const void *s2, size_t n);
 int memcmp(const void *s1, const void *s2, size_t n);
 int strcmp(const char *s1, const char *s2);
+int strncmp(const char *s1, const char *s2, size_t n);
 void memset_far(u16 d_seg, void *d_far, u8 c, size_t len);
 void memset16_far(u16 d_seg, void *d_far, u16 c, size_t len);
 void *memset(void *s, int c, size_t n);
 void memset_fl(void *ptr, u8 val, size_t size);
-void memcpy_far(u16 d_seg, void *d_far
-                , u16 s_seg, const void *s_far, size_t len);
+void memcpy_far(u16 d_seg, void *d_far, u16 s_seg, const void *s_far,
size_t len);
 void memcpy_fl(void *d_fl, const void *s_fl, size_t len);
 void *memcpy(void *d1, const void *s1, size_t len);
 #if MODESEGMENT == 0
diff --git a/src/util.h b/src/util.h
index aff8e88..ba9582a 100644
--- a/src/util.h
+++ b/src/util.h
@@ -13,8 +13,7 @@ void handle_1553(struct bregs *regs);
 struct bmp_decdata *bmp_alloc(void);
 int bmp_decode(struct bmp_decdata *bmp, unsigned char *data, int data_size);
 void bmp_get_info(struct bmp_decdata *bmp, int *width, int *height, int *bpp);
-int bmp_show(struct bmp_decdata *bmp, unsigned char *pic, int width
-             , int height, int depth, int bytes_per_line_dest);
+int bmp_show(struct bmp_decdata *bmp, unsigned char *pic, int width,
int height, int depth, int bytes_per_line_dest);

 // boot.c
 void boot_init(void);
@@ -50,7 +49,7 @@ int boot_lchs_find_ata_device(struct pci_device
*pci, int chanid, int slave,

 // bootsplash.c
 void enable_vga_console(void);
-void enable_bootsplash(void);
+int enable_bootsplash(u32 menutime, int *scan_code);
 void disable_bootsplash(void);

 // cdrom.c
@@ -197,12 +196,19 @@ u32 ticks_to_ms(u32 ticks);
 u32 ticks_from_ms(u32 ms);
 void pit_setup(void);

+// gif.c
+struct gif_decdata *gif_alloc(void);
+int gif_decode(struct gif_decdata *gif, unsigned char *buf);
+void gif_get_info(struct gif_decdata *gif, int *width, int *height,
int *bpp_require);
+int gif_show(struct gif_decdata *gif, unsigned char *pic, int width,
int height, int depth, int bytes_per_line_dest);
+int gif_get_gce_delay(struct gif_decdata *gif);
+void gif_rewind(struct gif_decdata *gif);
+
 // jpeg.c
 struct jpeg_decdata *jpeg_alloc(void);
 int jpeg_decode(struct jpeg_decdata *jpeg, unsigned char *buf);
 void jpeg_get_size(struct jpeg_decdata *jpeg, int *width, int *height);
-int jpeg_show(struct jpeg_decdata *jpeg, unsigned char *pic, int width
-              , int height, int depth, int bytes_per_line_dest);
+int jpeg_show(struct jpeg_decdata *jpeg, unsigned char *pic, int
width, int height, int depth, int bytes_per_line_dest);

 // kbd.c
 void kbd_init(void);

--
2.34.1
_______________________________________________
SeaBIOS mailing list -- seabios@seabios.org
To unsubscribe send an email to seabios-leave@seabios.org
[SeaBIOS] Re: Animated GIF bootsplash
Posted by Paul Menzel 1 month ago
Dear Michael,


Am 22.08.22 um 21:23 schrieb Michael Bacarella:
> On Sat, Aug 20, 2022 at 11:45 PM Paul Menzel <pmenzel@molgen.mpg.de> wrote:

>> Thank you for implementing this. If you send a patch, I can test on real
>> hardware.

[…]

> Patch attached. I replied here rather than with a new email with a PATCH subject
> because I don't consider it ready to merge, more like a POC.
> 
> Also I'm using gmail, hope I don't embarrass myself.

Unfortunately, Gmail wraps lines after 72(?) characters so the diff is 
malformed. Maybe attach the file generated by `git format-patch -1`?

[…]


Kind regards,

Paul
_______________________________________________
SeaBIOS mailing list -- seabios@seabios.org
To unsubscribe send an email to seabios-leave@seabios.org
[SeaBIOS] Re: Animated GIF bootsplash
Posted by Michael Bacarella 1 month ago
On Mon, Aug 22, 2022 at 12:23 PM Michael Bacarella <
michael.bacarella@gmail.com> wrote:

> On Sat, Aug 20, 2022 at 11:45 PM Paul Menzel <pmenzel@molgen.mpg.de>
> wrote:
> >
> > Dear Michael,
> >
> >
> > Thank you for implementing this. If you send a patch, I can test on real
> > hardware.
> >
> >
> > Kind regards,
> >
> > Paul
> >
> >
> > PS: It’d be great, if you could used interleaved style, when responding.
>
> Patch attached. I replied here rather than with a new email with a PATCH
> subject
> because I don't consider it ready to merge, more like a POC.
>

Oh, also, here's the animated GIF I used in the video.

https://michael.bacarella.com/bootsplash.gif

>
>
_______________________________________________
SeaBIOS mailing list -- seabios@seabios.org
To unsubscribe send an email to seabios-leave@seabios.org