Hello,
I've added animated GIF bootsplash support to SeaBIOS.
Demo: https://www.youtube.com/watch?v=0mmMk40uDJk
Is this only of interest to me, or should I submit a patch for consideration?
I adapted this public domain gif decoder https://github.com/lecram/gifdec
It is my understanding that the patents that were encumbering GIF use in open source have long since expired so there should not be regulatory blockers.
Thank you kindly.
Interesting. Have you tested also on real hardware and/or old hardware? It might work fine in QEMU, but it might fail on a real mainboard test.
On Tue, Aug 16, 2022 at 1:06 PM Michael Bacarella < michael.bacarella@gmail.com> wrote:
Hello,
I've added animated GIF bootsplash support to SeaBIOS.
Demo: https://www.youtube.com/watch?v=0mmMk40uDJk
Is this only of interest to me, or should I submit a patch for consideration?
I adapted this public domain gif decoder https://github.com/lecram/gifdec
It is my understanding that the patents that were encumbering GIF use in open source have long since expired so there should not be regulatory blockers.
Thank you kindly. _______________________________________________ SeaBIOS mailing list -- seabios@seabios.org To unsubscribe send an email to seabios-leave@seabios.org
Only on QEMU so far, though I do have a System76 laptop standing by ready to be improved. I can report back after that. Probably in a weekend or two.
On Wed, Aug 17, 2022 at 7:22 AM R S rene.shuster@bcsemail.org wrote:
Interesting. Have you tested also on real hardware and/or old hardware? It might work fine in QEMU, but it might fail on a real mainboard test.
On Tue, Aug 16, 2022 at 1:06 PM Michael Bacarella < michael.bacarella@gmail.com> wrote:
Hello,
I've added animated GIF bootsplash support to SeaBIOS.
Demo: https://www.youtube.com/watch?v=0mmMk40uDJk
Is this only of interest to me, or should I submit a patch for consideration?
I adapted this public domain gif decoder https://github.com/lecram/gifdec
It is my understanding that the patents that were encumbering GIF use in open source have long since expired so there should not be regulatory blockers.
Thank you kindly. _______________________________________________ SeaBIOS mailing list -- seabios@seabios.org To unsubscribe send an email to seabios-leave@seabios.org
Whoops. I thought because System 76 laptops ship with coreboot that they'd also ship with SeaBIOS, but that's wrong.
What's the cheapest easiest possible thing to flash to do a decent hardware test with?
On Wed, Aug 17, 2022, 07:51 Michael Bacarella michael.bacarella@gmail.com wrote:
Only on QEMU so far, though I do have a System76 laptop standing by ready to be improved. I can report back after that. Probably in a weekend or two.
On Wed, Aug 17, 2022 at 7:22 AM R S rene.shuster@bcsemail.org wrote:
Interesting. Have you tested also on real hardware and/or old hardware? It might work fine in QEMU, but it might fail on a real mainboard test.
On Tue, Aug 16, 2022 at 1:06 PM Michael Bacarella < michael.bacarella@gmail.com> wrote:
Hello,
I've added animated GIF bootsplash support to SeaBIOS.
Demo: https://www.youtube.com/watch?v=0mmMk40uDJk
Is this only of interest to me, or should I submit a patch for consideration?
I adapted this public domain gif decoder https://github.com/lecram/gifdec
It is my understanding that the patents that were encumbering GIF use in open source have long since expired so there should not be regulatory blockers.
Thank you kindly. _______________________________________________ SeaBIOS mailing list -- seabios@seabios.org To unsubscribe send an email to seabios-leave@seabios.org
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.
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
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.
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