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