Added some gif rendering skeleton
Amazingly, text rendering seems to still work.
This commit is contained in:
parent
d27f9ecf7e
commit
4c84439c10
6 changed files with 132 additions and 59 deletions
|
|
@ -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
|
||||
|
|
|
|||
10
host/color.h
10
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__
|
||||
|
|
|
|||
115
host/gif.c
115
host/gif.c
|
|
@ -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__
|
||||
|
|
|
|||
50
host/main.c
50
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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue