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
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
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
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
© 2016 - 2024 Red Hat, Inc.