From nobody Fri Dec 27 02:35:35 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of seabios.org designates 78.46.105.101 as permitted sender) client-ip=78.46.105.101; envelope-from=seabios-bounces@seabios.org; helo=coreboot.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of seabios.org designates 78.46.105.101 as permitted sender) smtp.mailfrom=seabios-bounces@seabios.org; dmarc=fail(p=none dis=none) header.from=gmail.com Return-Path: Received: from coreboot.org (coreboot.org [78.46.105.101]) by mx.zohomail.com with SMTPS id 1661196292347763.8300154567112; Mon, 22 Aug 2022 12:24:52 -0700 (PDT) Received: from authenticated-user (PRIMARY_HOSTNAME [PUBLIC_IP]) by coreboot.org (Postfix) with ESMTPA id 4157D2334D; Mon, 22 Aug 2022 19:24:46 +0000 (UTC) Received: from authenticated-user (PRIMARY_HOSTNAME [PUBLIC_IP]) by coreboot.org (Postfix) with ESMTP id E6B4C2B9B1 for ; Mon, 22 Aug 2022 19:24:30 +0000 (UTC) Received: from authenticated-user (PRIMARY_HOSTNAME [PUBLIC_IP]) for ; Mon, 22 Aug 2022 12:24:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc; bh=z/INBys4a1InEu0DaT1KhlGM9y5hvXNqfsNubYSo0zY=; b=L+oxOEtJegNJPfeHDTw3sXwqe53P6qMfaPfojDR6RbDrEoE0g1wLuAAIf0M+ctYPvj +zy+LO5/gqZGSuiHNlJkKS/pEyUw8SFWa5MFD3EMFlOHqUCrOGcIkDe+yeXuf477Kj9v WmLXgqxLucE6/dEB41T5kcDx5JM8g3vqKCow7JioCiTFfvwoV4KuSHoR2QvRdgekQSit fbPVNtxXh47ZNtQX2tF1XhJxTZf8cSiu3kSJe1tbMpyBpaKrSbNN1FCzibX1BuBvbye4 BzRn03UjzxaYDiv5EHjqjyaGAr9kfRny/6DIRGhac7O42QXtCkL0eYhL5zddRg/AHtml P8xw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc; bh=z/INBys4a1InEu0DaT1KhlGM9y5hvXNqfsNubYSo0zY=; b=XMU3I3NqTIpD5uBf2IZdP5ItgfY7p8uXfiFyBCSDEOREvD9cfG+xlbCgBO8/OA87nN Z6pDWblKdpZVMA7xmR42UbQj+jLWoWRmg25ApJFBDcEg5/VPMkRGuthImn+ERdfRN+1s SV6qyVkv1E9s454gXKVwWAMEN3Rk2jrtKmxNcdx8b+euJX5KbYdehRChHyrb244mBCNK AQpWQ8Ye4EGLKL3TnHvSZNZQ9zntjOg6X1P2J7Et8kjsNtjFg+w9KVFE4gYHxj1A+DI6 V2OD2wGzS9DvuN9ZK9C2YWAZ7n7KJzUyPgN3iBZlOEFUwpQsU/vwlkk6pijuEz18zNx3 Qw8Q== X-Gm-Message-State: ACgBeo1ok69aFiBGpLoAVmUeaYTYSXmdGHDLG1uqKcJl0QZq6+QSzV9D rLPY3W0UiajdmM4aARYMMbdcYg+eQfivzjSkJAlaQ2ZL X-Google-Smtp-Source: AA6agR458BNvyziN1em+NG7mEGcWx+wL+2hQOdeijP/NB24kBpESYUv5qVSfm+7+ltYjdm3fMuMQzQTK8uw28g6CZRg= X-Received: by 2002:a05:6000:178c:b0:222:f8ec:9977 with SMTP id e12-20020a056000178c00b00222f8ec9977mr11176635wrg.509.1661196270015; Mon, 22 Aug 2022 12:24:30 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: Michael Bacarella Date: Mon, 22 Aug 2022 12:23:53 -0700 Message-ID: To: Paul Menzel Message-ID-Hash: M3HK4YN5FZ4QQXWF6I6W7GYEHRATQYTA X-Message-ID-Hash: M3HK4YN5FZ4QQXWF6I6W7GYEHRATQYTA X-MailFrom: michael.bacarella@gmail.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-seabios.seabios.org-0; header-match-seabios.seabios.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: seabios@seabios.org X-Mailman-Version: 3.3.6b1 Precedence: list Subject: [SeaBIOS] Re: Animated GIF bootsplash List-Id: SeaBIOS mailing list Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Authentication-Results: coreboot.org; auth=pass smtp.auth=mailman@coreboot.org smtp.mailfrom=seabios-bounces@seabios.org X-Spamd-Bar: / X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1661196294363100001 On Sat, Aug 20, 2022 at 11:45 PM Paul Menzel wrote: > > Dear Michael, > > > Thank you for implementing this. If you send a patch, I can test on real > hardware. > > > Kind regards, > > Paul > > > PS: It=E2=80=99d be great, if you could used interleaved style, when resp= onding. Patch attached. I replied here rather than with a new email with a PATCH su= bject 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 --- 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=3Dmisc.c stacks.c output.c string.c block.c cdrom.c disk.c \ hw/mpt-scsi.c SRC16=3D$(SRCBOTH) SRC32FLAT=3D$(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 =3D romfile_loadint("etc/boot-menu-wait", DEFAULT_BOOTMENU_WAIT); - enable_bootsplash(); - int scan_code =3D get_keystroke(menutime); + int scan_code =3D !menukey; + if (enable_bootsplash(menutime, &scan_code) !=3D 1) + { + scan_code =3D get_keystroke(menutime); + } disable_bootsplash(); if (scan_code !=3D 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 =3D 0; /* 0 means jpg, 1 means bmp, default is 0=3Djpg */ + u8 type =3D 0; /* 0 means jpg, 1 means bmp, 2 means gif default is 0= =3Djpg */ int filesize; u8 *filedata =3D romfile_loadfile("bootsplash.jpg", &filesize); - if (!filedata) { + if (!filedata) + { + type =3D 1; filedata =3D romfile_loadfile("bootsplash.bmp", &filesize); if (!filedata) - return; - type =3D 1; + { + type =3D 2; + filedata =3D romfile_loadfile("bootsplash.gif", &filesize); + if (!filedata) + return 0; + } } dprintf(3, "start showing bootsplash\n"); u8 *picture =3D NULL; /* data buff used to be flushed to the video buf= */ struct jpeg_decdata *jpeg =3D NULL; struct bmp_decdata *bmp =3D NULL; + struct gif_decdata *gif =3D NULL; struct vbe_info *vesa_info =3D malloc_tmplow(sizeof(*vesa_info)); struct vbe_mode_info *mode_info =3D 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 =3D FLATPTR_TO_OFFSET(vesa_info); br.es =3D FLATPTR_TO_SEG(vesa_info); call16_int10(&br); - if (vesa_info->signature !=3D VESA_SIGNATURE) { - dprintf(1,"No VBE2 found.\n"); - goto done; + if (vesa_info->signature !=3D VESA_SIGNATURE) + { + dprintf(1, "No VBE2 found.\n"); + goto fail; } /* Print some debugging information about our card. */ char *vendor =3D SEGOFF_TO_FLATPTR(vesa_info->oem_vendor_string); char *product =3D 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 =3D 0; - if (type =3D=3D 0) { + if (type =3D=3D 0) + { jpeg =3D 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 =3D 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 =3D=3D 1) + { bmp =3D 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 =3D 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 =3D gif_alloc(); + if (!gif) + { + warn_noalloc(); + goto fail; + } + /* Parse gif and get image size. */ + dprintf(5, "Decoding bootsplash.gif\n"); + ret =3D 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 =3D 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=3Dany).= \n", - width, height, bpp_require); - goto done; + width, height, bpp_require); + goto fail; } void *framebuffer =3D (void *)mode_info->phys_base; int depth =3D mode_info->bits_per_pixel; @@ -195,26 +229,43 @@ enable_bootsplash(void) // Allocate space for image and decompress it. int imagesize =3D height * mode_info->bytes_per_scanline; picture =3D malloc_tmphigh(imagesize); - if (!picture) { + if (!picture) + { warn_noalloc(); - goto done; + goto fail; } - if (type =3D=3D 0) { + if (type =3D=3D 0) + { dprintf(5, "Decompressing bootsplash.jpg\n"); ret =3D 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 =3D=3D 1) + { dprintf(5, "Decompressing bootsplash.bmp\n"); ret =3D 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 =3D 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 =3D 0x4f02; br.bx =3D videomode | VBE_MODE_LINEAR_FRAME_BUFFER; call16_int10(&br); - if (br.ax !=3D 0x4f) { + if (br.ax !=3D 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 =3D 1; + /* Animate the gif */ + if (type =3D=3D 2) + { + u32 end =3D irqtimer_calc(menutime); + while (!irqtimer_check(end)) + { + // TODO: don't delay past end time + *scan_code =3D get_keystroke(gif_get_gce_delay(gif)); + if (*scan_code !=3D -1) + break; + ret =3D gif_show(gif, picture, width, height, depth, + mode_info->bytes_per_scanline); + iomemcpy(framebuffer, picture, imagesize); + + if (ret =3D=3D 0) + gif_rewind(gif); + } + ret =3D 1; + goto done; + } + else + { + ret =3D 0; + goto done; + } +fail: + ret =3D -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 =3D malloc_tmplow(sizeof(fdmap_t)); + if (p =3D=3D NULL) + { + dprintf(1, "fdmap_create: malloc failed\n"); + return NULL; + } + p->map =3D map; + p->pos =3D 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 +=3D len; + return len; +} + +static int +fdmap_lseek(fdmap_t *fdmap, int off, int whence) +{ + switch (whence) + { + case SEEK_SET: + fdmap->pos =3D off; + break; + case SEEK_CUR: + fdmap->pos +=3D 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 =3D fdmap->pos; + num =3D fdmap->map[pos] + (((u16)fdmap->map[pos + 1]) << 8); + fdmap->pos +=3D 2; + return num; +} + +struct gif_decdata * +gif_alloc(void) +{ + struct gif_decdata *gif =3D 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 =3D fdmap_create(buf); + if (fdmap =3D=3D NULL) + return -1; + /* Header */ + fdmap_read(fdmap, sigver, 3); + if (memcmp(sigver, "GIF", 3) !=3D 0) + { + dprintf(1, "invalid GIF signature\n"); + goto fail; + } + /* Version */ + fdmap_read(fdmap, sigver, 3); + if (memcmp(sigver, "89a", 3) !=3D 0) + { + dprintf(1, "invalid GIF version\n"); + goto fail; + } + /* Width x Height */ + width =3D read_num(fdmap); + height =3D 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 =3D ((fdsz >> 4) & 7) + 1; + /* Ignore Sort Flag. */ + /* GCT Size */ + gct_sz =3D 1 << ((fdsz & 0x07) + 1); + /* Background Color Index */ + fdmap_read(fdmap, &bgidx, 1); + /* Aspect Ratio */ + fdmap_read(fdmap, &aspect, 1); + + gif->fdmap =3D fdmap; + gif->width =3D width; + gif->height =3D height; + gif->depth =3D depth; + /* Read GCT */ + gif->gct.size =3D gct_sz; + fdmap_read(fdmap, gif->gct.colors, 3 * gif->gct.size); + gif->palette =3D &gif->gct; + gif->bgindex =3D bgidx; + gif->frame =3D malloc_tmphigh(4 * width * height); + if (!gif->frame) + { + free(gif); + goto fail; + } + memset(gif->frame, 0, 4 * width * height); + gif->canvas =3D &gif->frame[width * height]; + if (gif->bgindex) + memset(gif->frame, gif->bgindex, gif->width * gif->height); + bgcolor =3D &gif->palette->colors[gif->bgindex * 3]; + if (bgcolor[0] || bgcolor[1] || bgcolor[2]) + for (i =3D 0; i < gif->width * gif->height; i++) + memcpy(&gif->canvas[i * 3], bgcolor, 3); + gif->anim_start =3D 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 =3D gif->width; + *height =3D gif->height; + *bpp_require =3D 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 =3D 12 */ + tx =3D read_num(gif->fdmap); + ty =3D read_num(gif->fdmap); + tw =3D read_num(gif->fdmap); + th =3D 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 =3D 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 =3D (rdit >> 2) & 3; + gif->gce.input =3D rdit & 2; + gif->gce.transparency =3D rdit & 1; + gif->gce.delay =3D 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 =3D 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 =3D read_num(gif->fdmap); + /* Skip block terminator. */ + fdmap_lseek(gif->fdmap, 1, SEEK_CUR); + } + else if (gif->application) + { + off_t sub_block =3D 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 =3D MAX(1 << (key_size + 1), 0x100); + Table *table =3D malloc_tmphigh(sizeof(*table) + sizeof(Entry) * init_= bulk); + if (table) + { + table->bulk =3D init_bulk; + table->nentries =3D (1 << key_size) + 2; + table->entries =3D (Entry *)&table[1]; + for (key =3D 0; key < (1 << key_size); key++) + table->entries[key] =3D (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 =3D *tablep; + Table *new_table; + int old_size; + + if (table->nentries =3D=3D table->bulk) + { + old_size =3D sizeof(*table) + sizeof(Entry) * table->bulk; + table->bulk *=3D 2; + new_table =3D malloc_tmphigh(sizeof(*table) + sizeof(Entry) * table->bulk); + if (!table) + return -1; + memcpy(new_table, table, old_size); + free(table); + table =3D new_table; + table->entries =3D (Entry *)&table[1]; + *tablep =3D table; + } + table->entries[table->nentries] =3D (Entry){length, prefix, suffix}; + table->nentries++; + if ((table->nentries & (table->nentries - 1)) =3D=3D 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 =3D 0; + for (bits_read =3D 0; bits_read < key_size; bits_read +=3D frag_size) + { + rpad =3D (*shift + bits_read) % 8; + if (rpad =3D=3D 0) + { + /* Update byte. */ + if (*sub_len =3D=3D 0) + { + fdmap_read(gif->fdmap, sub_len, 1); /* Must be nonzero! */ + if (*sub_len =3D=3D 0) + return 0x1000; + } + fdmap_read(gif->fdmap, byte, 1); + (*sub_len)--; + } + frag_size =3D MIN(key_size - bits_read, 8 - rpad); + key |=3D ((u16)((*byte) >> rpad)) << bits_read; + } + /* Clear extra bits to the left. */ + key &=3D (1 << key_size) - 1; + *shift =3D (*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 =3D (h - 1) / 8 + 1; + if (y < p) /* pass 1 */ + return y * 8; + y -=3D p; + p =3D (h - 5) / 8 + 1; + if (y < p) /* pass 2 */ + return y * 8 + 4; + y -=3D p; + p =3D (h - 3) / 4 + 1; + if (y < p) /* pass 3 */ + return y * 4 + 2; + y -=3D 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 =3D (int)byte; + if (key_size < 2 || key_size > 8) + return -1; + + start =3D fdmap_lseek(gif->fdmap, 0, SEEK_CUR); + discard_sub_blocks(gif); + end =3D fdmap_lseek(gif->fdmap, 0, SEEK_CUR); + fdmap_lseek(gif->fdmap, start, SEEK_SET); + clear =3D 1 << key_size; + stop =3D clear + 1; + table =3D new_table(key_size); + key_size++; + init_key_size =3D key_size; + sub_len =3D shift =3D 0; + key =3D get_key(gif, key_size, &sub_len, &shift, &byte); /* clear code= */ + frm_off =3D 0; + ret =3D 0; + frm_size =3D gif->fw * gif->fh; + while (frm_off < frm_size) + { + if (key =3D=3D clear) + { + key_size =3D init_key_size; + table->nentries =3D (1 << (key_size - 1)) + 2; + table_is_full =3D 0; + } + else if (!table_is_full) + { + ret =3D add_entry(&table, str_len + 1, key, entry.suffix); + if (ret =3D=3D -1) + { + free(table); + return -1; + } + if (table->nentries =3D=3D 0x1000) + { + ret =3D 0; + table_is_full =3D 1; + } + } + key =3D get_key(gif, key_size, &sub_len, &shift, &byte); + if (key =3D=3D clear) + continue; + if (key =3D=3D stop || key =3D=3D 0x1000) + break; + if (ret =3D=3D 1) + key_size++; + entry =3D table->entries[key]; + str_len =3D entry.length; + for (i =3D 0; i < str_len; i++) + { + p =3D frm_off + entry.length - 1; + x =3D p % gif->fw; + y =3D p / gif->fw; + if (interlace) + y =3D interlaced_line_index((int)gif->fh, y); + gif->frame[(gif->fy + y) * gif->width + gif->fx + x] =3D entry.suffix; + if (entry.prefix =3D=3D 0xFFF) + break; + else + entry =3D table->entries[entry.prefix]; + } + frm_off +=3D str_len; + if (key < table->nentries - 1 && !table_is_full) + table->entries[table->nentries - 1].suffix =3D entry.suffix; + } + free(table); + if (key =3D=3D 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 =3D read_num(gif->fdmap); + gif->fy =3D read_num(gif->fdmap); + + if (gif->fx >=3D gif->width || gif->fy >=3D gif->height) + return -1; + + gif->fw =3D read_num(gif->fdmap); + gif->fh =3D read_num(gif->fdmap); + + gif->fw =3D MIN(gif->fw, gif->width - gif->fx); + gif->fh =3D MIN(gif->fh, gif->height - gif->fy); + + fdmap_read(gif->fdmap, &fisrz, 1); + interlace =3D fisrz & 0x40; + /* Ignore Sort Flag. */ + /* Local Color Table? */ + if (fisrz & 0x80) + { + /* Read LCT */ + gif->lct.size =3D 1 << ((fisrz & 0x07) + 1); + fdmap_read(gif->fdmap, gif->lct.colors, 3 * gif->lct.size); + gif->palette =3D &gif->lct; + } + else + gif->palette =3D &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 =3D gif->fy * gif->width + gif->fx; + for (j =3D 0; j < gif->fh; j++) + { + for (k =3D 0; k < gif->fw; k++) + { + index =3D gif->frame[(gif->fy + j) * gif->width + gif->fx + k]; + color =3D &gif->palette->colors[index * 3]; + if (!gif->gce.transparency || index !=3D gif->gce.tindex) + memcpy(&buffer[(i + k) * 3], color, 3); + } + i +=3D 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 =3D &gif->palette->colors[gif->bgindex * 3]; + i =3D gif->fy * gif->width + gif->fx; + for (j =3D 0; j < gif->fh; j++) + { + for (k =3D 0; k < gif->fw; k++) + memcpy(&gif->canvas[(i + k) * 3], bgcolor, 3); + i +=3D 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 !=3D ',') + { + if (sep =3D=3D ';') + return 0; + if (sep =3D=3D '!') + read_ext(gif); + else + return -1; + fdmap_read(gif->fdmap, &sep, 1); + } + if (read_image(gif) =3D=3D -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 !=3D height) + { + dprintf(1, "gif height %d !=3D picture height %d", gif->height, he= ight); + return -1; + } + + if (gif->width !=3D width) + { + dprintf(1, "gif height %d !=3D picture height %d", gif->width, wid= th); + return -1; + } + + ret =3D gd_get_frame(gif); + if (ret =3D=3D -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 !=3D *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 !=3D *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 =3D=3D 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 *b= pp); -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