diff options
-rw-r--r-- | host/Makefile | 4 | ||||
-rw-r--r-- | host/color.h | 10 | ||||
-rw-r--r-- | host/gif.c | 115 | ||||
-rw-r--r-- | host/gif.h | 5 | ||||
-rw-r--r-- | host/main.c | 50 | ||||
-rw-r--r-- | host/main.h | 7 |
6 files changed, 132 insertions, 59 deletions
diff --git a/host/Makefile b/host/Makefile index 1f90145..90517f9 100644 --- a/host/Makefile +++ b/host/Makefile @@ -1,6 +1,6 @@ -all: main.c font.c font.h color.c color.h - gcc -std=gnu11 -Wall -lm -o matelight -g -O0 main.c font.c color.c +all: main.c font.c font.h color.c color.h gif.h gif.c + gcc -std=gnu11 -Wall -lm -lgif -o matelight -g -O0 main.c font.c color.c gif.c clean: rm matelight diff --git a/host/color.h b/host/color.h index 5051c4c..1fbfe57 100644 --- a/host/color.h +++ b/host/color.h @@ -2,6 +2,7 @@ #define __COLOR_H__ #include <stdint.h> +#include <stdlib.h> /* For easier memsetting we use an inverted alpha channel, i.e. 0 ≘ fully opaque; 255 ≘ fully transparent */ typedef struct { @@ -13,8 +14,8 @@ typedef struct { typedef struct { color_t *data; - unsigned int w; - unsigned int h; + size_t w; + size_t h; } framebuffer_t; int xterm_color_index(color_t c); @@ -25,5 +26,10 @@ int xterm_color_index(color_t c); #define DEFAULT_BG_COLOR 0 extern color_t colortable[256]; +static inline void framebuffer_free(framebuffer_t *fb){ + if(fb) + free(fb->data); + free(fb); +} #endif//__COLOR_H__ @@ -2,6 +2,7 @@ #include "gif.h" #include <stdio.h> #include <stdlib.h> +#include <string.h> typedef struct { uint8_t *data; @@ -9,8 +10,8 @@ typedef struct { unsigned int length; } readBuffer_t; -int gifBufferInput (GifFileType *gif, GifByteType *dest, int n){ - readBuffer_t *rb = gif->userData; +int gif_buffer_input (GifFileType *gif, GifByteType *dest, int n){ + readBuffer_t *rb = gif->UserData; if(rb->index+n > rb->length) n = rb->length - rb->index; memcpy(dest, rb->data, n); @@ -20,20 +21,32 @@ int gifBufferInput (GifFileType *gif, GifByteType *dest, int n){ struct _gifAnimationState { int idx; GifFileType *gif; - framebuffer_t frame; + framebuffer_t *frame; }; +void color_fill(color_t *dest, color_t src, size_t size){ + /* Some magic to reduce the amount of calls to small-block memcpys */ + dest[0] = src; + size_t i = 1; + size_t j = 2; + while(j < size){ + memcpy(dest+i, dest, i*sizeof(color_t)); + i = j; + j *= 2; + } + memcpy(dest+i, dest, (size-i)*sizeof(color_t)); +} + gifAnimationState_t *gif_read(uint8_t *buf, unsigned int buflength){ - /* On first invocation, parse gif and store it in the state struct */ - gifAnimationState_t *st = malloc(sizeof(*state)); + gifAnimationState_t *st = malloc(sizeof(gifAnimationState_t)); if(!st){ - fprintf(stderr, "Failed to allocate %d bytes\n", sizeof(*state)); + fprintf(stderr, "Failed to allocate %lu bytes\n", sizeof(*st)); return 0; } readBuffer_t readBuf = {buf, 0, buflength}; int err = 0; - GifFileType *gif = DGifOpen(readBuf, OutputFunc writeFunc, &err); + GifFileType *gif = DGifOpen(&readBuf, gif_buffer_input, &err); if(err){ fprintf(stderr, "Could not read GIF data: %s\n", GifErrorString(err)); goto error; @@ -50,18 +63,9 @@ gifAnimationState_t *gif_read(uint8_t *buf, unsigned int buflength){ goto error; } if(gif->SColorMap){ /* Initially fill framebuffer with background color */ - GifColorType *col = gif->SColorMap[gif->SBackGroundColor]->Colors; + GifColorType *col = gif->SColorMap[gif->SBackGroundColor].Colors; color_t c = {col->Red, col->Green, col->Blue, 255}; - /* Some magic to reduce the amount of calls to small-block memcpys */ - fb[0] = c; - int i = 1; - int j = 2; - while(j < framesize){ - memcpy(fb+i*sizeof(color_t), fb, i*sizeof(color_t)); - i = j; - j *= 2; - } - memcpy(fb+i*sizeof(color_t), fb, (framesize-i)*sizeof(color_t)) + color_fill(fb, c, framesize); }else{ /* Set all pixels to a transparent black */ memset(fb, 0, framesize*sizeof(color_t)); @@ -79,12 +83,21 @@ error: return 0; } +void gifAnimationState_free(gifAnimationState_t *st){ + if(st) + framebuffer_free(st->frame); + free(st); +} + /* buf ⇜ input data - * state ⇜ internal state, initialize as NULL */ -framebuffer_t* gif_render(uint8_t *buf, unsigned int buflength, gifAnimationState_t **state){ + * state ⇜ internal state, initialize as NULL + * delay ⇜ is filled with this frame's delay value in milliseconds (if it is not NULL). -1 means "No delay given". + * Small note: I mean, rendering a GIF in python is not quite trivial, either (about 20loc). But come on, this is just ridiculous. */ +framebuffer_t *gif_render(uint8_t *buf, unsigned int buflength, gifAnimationState_t **state, int *delay){ gifAnimationState_t *st; if(*state == NULL){ - st = gif_read(buf, buflength, state); + /* On first invocation, parse gif and store it in the state struct */ + st = gif_read(buf, buflength); if(!st) goto error; *state = st; @@ -94,41 +107,71 @@ framebuffer_t* gif_render(uint8_t *buf, unsigned int buflength, gifAnimationStat GifFileType *gif = st->gif; - SavedImage *img = gif->images[st->idx]; - cmo = img->ImageDesc->ColorMap; + /* Find this image's color map */ + SavedImage *img = gif->SavedImages + st->idx; + ColorMapObject *cmo = img->ImageDesc.ColorMap; if(!cmo){ - ColorMapObject *cmo = gif->SColorMap; + cmo = gif->SColorMap; if(!cmo){ fprintf(stderr, "Missing color table for GIF frame %d\n", st->idx); goto error; } } - unsigned int ix = img->ImageDesc->Left; - unsigned int iy = img->ImageDesc->Top; - unsigned int iw = img->ImageDesc->Width; - unsigned int ih = img->ImageDesc->Height; - if((iy+ih)*gif->SWidth + ix+iw > st->frame->w*st->frame->h;){ + + /* Extract and validate image's bounds*/ + unsigned int ix = img->ImageDesc.Left; + unsigned int iy = img->ImageDesc.Top; + unsigned int iw = img->ImageDesc.Width; + unsigned int ih = img->ImageDesc.Height; + if((iy+ih)*gif->SWidth + ix+iw > st->frame->w*st->frame->h){ fprintf(stderr, "Invalid gif: Image %d (x %d y %d w %d h %d) out of bounds (w %d h %d)\n", st->idx, ix, iy, iw, ih, gif->SWidth, gif->SHeight); goto error; } - uint8_t *p = img->RasterBits; + + /* Find and parse this image's GCB (if it exists) */ + int transparent = -1; + int disposal = DISPOSAL_UNSPECIFIED; + if(delay) + *delay = -1; /* in milliseconds */ + for(unsigned int i=0; i<img->ExtensionBlockCount; i++){ + ExtensionBlock *eb = img->ExtensionBlocks + i; + if(eb->Function == GRAPHICS_EXT_FUNC_CODE){ + GraphicsControlBlock *gcb = (GraphicsControlBlock *)eb->Bytes; + transparent = gcb->TransparentColor; + disposal = gcb->DisposalMode; + if(delay) + *delay = gcb->DelayTime * 10; /* 10ms → 1ms */ + } + } + color_t *fb = st->frame->data; + if(disposal == DISPOSE_BACKGROUND || disposal == DISPOSAL_UNSPECIFIED){ + GifColorType *gc = gif->SColorMap[gif->SBackGroundColor].Colors; + color_t c = {gc->Red, gc->Green, gc->Blue, 255}; + color_fill(fb, c, st->frame->w*st->frame->h); + } + /* FIXME DISPOSE_PREVIOUS is currently unhandled. */ + + /* Render gif bitmap to RGB */ + uint8_t *p = img->RasterBits; for(unsigned int y = iy; y < iy+ih; y++){ for(unsigned int x = ix; x < ix+iw; x++){ - GifColorType col = cmo[*p++]->Colors; - fb[y*st->frame->w + x] = {col.Red, col.Green, col.Blue, 255}; + int c = *p++; + if(c != transparent){ + GifColorType *col = cmo[c].Colors; + color_t ct = {col->Red, col->Green, col->Blue, 255}; + fb[y*st->frame->w + x] = ct; + } } } st->idx++; if(st->idx >= gif->ImageCount) st->idx = 0; - return fb; + return st->frame; error: - if(st) - free(st->frame->data); - free(st); + gifAnimationState_free(st); return 0; } @@ -2,9 +2,12 @@ #define __GIF_H__ #include <gif_lib.h> +#include "color.h" typedef struct _gifAnimationState gifAnimationState_t; -color_t* gif_render(uint8_t *buf, unsigned int buflength, gifAnimationState_t **state); +gifAnimationState_t *gif_read(uint8_t *buf, unsigned int buflength); +framebuffer_t* gif_render(uint8_t *buf, unsigned int buflength, gifAnimationState_t **state, int *delay); +void gifAnimationState_free(gifAnimationState_t *st); #endif//__GIF_H__ diff --git a/host/main.c b/host/main.c index 81ad7b1..29e56f9 100644 --- a/host/main.c +++ b/host/main.c @@ -14,7 +14,7 @@ /* CAUTION: REQUIRES INPUT TO BE \0-TERMINATED * ...also, it does a hardcodes setlocale of LC_CTYPE to en_US.utf8 for... reasons. */ -color_t * framebuffer_render_text(char *s, glyph_t **glyph_table, unsigned int glyph_table_size){ +framebuffer_t *framebuffer_render_text(char *s, glyph_t **glyph_table, unsigned int glyph_table_size){ unsigned int len = strlen(s); color_t *gbuf = NULL; @@ -71,10 +71,10 @@ color_t * framebuffer_render_text(char *s, glyph_t **glyph_table, unsigned int g /* For easier rendering on the terminal, round up to multiples of two */ gbufheight += gbufheight&1; - unsigned int gbufsize = gbufwidth*gbufheight; - gbuf = malloc(gbufsize, sizeof(color_t)); - if(gbuf == 0){ - fprintf(stderr, "Cannot malloc() %d bytes.\n", gbufsize*sizeof(color_t)); + size_t gbufsize = gbufwidth*gbufheight; + gbuf = calloc(gbufsize, sizeof(color_t)); + if(!gbuf){ + fprintf(stderr, "Cannot malloc() %lu bytes.\n", gbufsize*sizeof(color_t)); goto error; } memset(gbuf, 0, gbufsize*sizeof(color_t)); @@ -281,21 +281,32 @@ color_t * framebuffer_render_text(char *s, glyph_t **glyph_table, unsigned int g } x += g->width; } - return gbuf; - error: - free(gbuf); - return 0; + framebuffer_t *fb = malloc(sizeof(framebuffer_t)); + if(!fb){ + fprintf(stderr, "Cannot malloc() %lu bytes.\n", sizeof(framebuffer_t)); + goto error; + } + fb->w = gbufwidth; + fb->h = gbufheight; + fb->data = gbuf; + return fb; +error: + free(gbuf); + return 0; } -void console_render_buffer(uint8_t *gbuf, unsigned int gbufwidth, unsigned int gbufheight){ +void console_render_buffer(framebuffer_t *fb){ /* Render framebuffer to terminal, two pixels per character using Unicode box drawing stuff */ color_t lastfg = {0, 0, 0}, lastbg = {0, 0, 0}; printf("\e[38;5;0;48;5;0m"); - for(unsigned int y=0; y < gbufheight; y+=2){ - for(unsigned int x=0; x < gbufwidth; x++){ + color_t *data = fb->data; + size_t w = fb->w; + size_t h = fb->h; + for(size_t y=0; y < h; y+=2){ + for(size_t x=0; x < w; x++){ /* Da magicks: ▀█▄ */ - color_t ct = *((color_t *)(gbuf + (y*gbufwidth + x)*sizeof(color_t))); /* Top pixel */ - color_t cb = *((color_t *)(gbuf + ((y+1)*gbufwidth + x)*sizeof(color_t))); /* Bottom pixel */ + color_t ct = data[y*w + x]; /* Top pixel */ + color_t cb = data[(y+1)*w + x]; /* Bottom pixel */ /* The following, rather convoluted logic tries to "save" escape sequences when rendering. */ if(!memcmp(&ct, &lastfg, sizeof(color_t))){ if(!memcmp(&cb, &lastbg, sizeof(color_t))){ @@ -331,10 +342,14 @@ void console_render_buffer(uint8_t *gbuf, unsigned int gbufwidth, unsigned int g } printf("\n"); } - return 0; } int main(int argc, char **argv){ + if(argc < 2){ + fprintf(stderr, "No input text given\n"); + return 1; + } + FILE *f = fopen("unifont.bdf", "r"); if(!f){ fprintf(stderr, "Error opening font file: %s\n", strerror(errno)); @@ -345,13 +360,16 @@ int main(int argc, char **argv){ fprintf(stderr, "Error reading font file.\n"); return 1; } + for(;;){ printf("\033[2J"); for(unsigned int i=1; i<argc; i++){ - if(console_render(argv[i], glyph_table, BLP_SIZE)){ + framebuffer_t *fb = framebuffer_render_text(argv[i], glyph_table, BLP_SIZE); + if(!fb){ fprintf(stderr, "Error rendering text.\n"); return 1; } + console_render_buffer(fb); printf("\n"); } usleep(20000); diff --git a/host/main.h b/host/main.h index a33bea1..bbfcf74 100644 --- a/host/main.h +++ b/host/main.h @@ -1,3 +1,6 @@ -color_t * framebuffer_render_text(char *s, glyph_t **glyph_table, unsigned int glyph_table_size); -void console_render_buffer(uint8_t *gbuf, unsigned int gbufwidth, unsigned int gbufheight); +#include "color.h" +#include "font.h" + +framebuffer_t *framebuffer_render_text(char *s, glyph_t **glyph_table, unsigned int glyph_table_size); +void console_render_buffer(framebuffer_t *fb); |