aboutsummaryrefslogtreecommitdiff
path: root/host/gif.c
blob: 5b30e1dd7fd6c480a3166f3f2438738db462b61d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include "gif.h"
#include <stdio.h>
#include <stdlib.h>

typedef struct {
	uint8_t *data;
	unsigned int index;
	unsigned int length;
} readBuffer_t;	

int gifBufferInput (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);
	return n;
}

struct _gifAnimationState {
	int idx;
	GifFileType *gif;
	framebuffer_t frame;
};

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));
	if(!st){
		fprintf(stderr, "Failed to allocate %d bytes\n", sizeof(*state));
		return 0;
	}

	readBuffer_t readBuf = {buf, 0, buflength};
	int err = 0;
	GifFileType *gif = DGifOpen(readBuf, OutputFunc writeFunc, &err);
	if(err){
		fprintf(stderr, "Could not read GIF data: %s\n", GifErrorString(err));
		goto error;
	}

	unsigned int framesize = gif->SWidth*gif->SHeight;
	if(framesize == 0){ /* Can this actually happen? */
		fprintf(stderr, "Invalid 0*0px gif\n");
		goto error;
	}
	color_t *fb = calloc(framesize, sizeof(color_t));
	if(!fb){
		fprintf(stderr, "Failed to allocate framebuffer for GIF (%d bytes)\n", gif->SWidth*gif->SHeight);
		goto error;
	}
	if(gif->SColorMap){ /* Initially fill framebuffer with background color */
		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))
	}else{
		/* Set all pixels to a transparent black */
		memset(fb, 0, framesize*sizeof(color_t));
	}

	st->gif = gif;
	st->idx = 0;
	st->frame->data = fb;
	st->frame->w = gif->SWidth;
	st->frame->h = gif->SHeight;
	return st;
error:
	free(fb);
	free(st);
	return 0;
}

/* buf ⇜ input data
 * state ⇜ internal state, initialize as NULL */
framebuffer_t* gif_render(uint8_t *buf, unsigned int buflength, gifAnimationState_t **state){
	gifAnimationState_t *st;
	if(*state == NULL){
		st = gif_read(buf, buflength, state);
		if(!st)
			goto error;
		*state = st;
	}else{
		st = *state;
	}

	GifFileType *gif = st->gif;

	SavedImage *img = gif->images[st->idx];
	cmo = img->ImageDesc->ColorMap;
	if(!cmo){
		ColorMapObject *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;){
		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;
	color_t *fb = st->frame->data;
	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};
		}
	}

	st->idx++;
	if(st->idx >= gif->ImageCount)
		st->idx = 0;
	return fb;
error:
	if(st)
		free(st->frame->data);
	free(st);
	return 0;
}