Merge pull request #12 from G4Vi/fix-delay-0-transparent
[gifenc.git] / gifenc.c
bloba0cb1d01ba6f59eac382eef5559f55e743699fe2
1 #include "gifenc.h"
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <fcntl.h>
9 #ifdef _WIN32
10 #include <io.h>
11 #else
12 #include <unistd.h>
13 #endif
15 /* helper to write a little-endian 16-bit number portably */
16 #define write_num(fd, n) write((fd), (uint8_t []) {(n) & 0xFF, (n) >> 8}, 2)
18 static uint8_t vga[0x30] = {
19 0x00, 0x00, 0x00,
20 0xAA, 0x00, 0x00,
21 0x00, 0xAA, 0x00,
22 0xAA, 0x55, 0x00,
23 0x00, 0x00, 0xAA,
24 0xAA, 0x00, 0xAA,
25 0x00, 0xAA, 0xAA,
26 0xAA, 0xAA, 0xAA,
27 0x55, 0x55, 0x55,
28 0xFF, 0x55, 0x55,
29 0x55, 0xFF, 0x55,
30 0xFF, 0xFF, 0x55,
31 0x55, 0x55, 0xFF,
32 0xFF, 0x55, 0xFF,
33 0x55, 0xFF, 0xFF,
34 0xFF, 0xFF, 0xFF,
37 struct Node {
38 uint16_t key;
39 struct Node *children[];
41 typedef struct Node Node;
43 static Node *
44 new_node(uint16_t key, int degree)
46 Node *node = calloc(1, sizeof(*node) + degree * sizeof(Node *));
47 if (node)
48 node->key = key;
49 return node;
52 static Node *
53 new_trie(int degree, int *nkeys)
55 Node *root = new_node(0, degree);
56 /* Create nodes for single pixels. */
57 for (*nkeys = 0; *nkeys < degree; (*nkeys)++)
58 root->children[*nkeys] = new_node(*nkeys, degree);
59 *nkeys += 2; /* skip clear code and stop code */
60 return root;
63 static void
64 del_trie(Node *root, int degree)
66 if (!root)
67 return;
68 for (int i = 0; i < degree; i++)
69 del_trie(root->children[i], degree);
70 free(root);
73 #define write_and_store(s, dst, fd, src, n) \
74 do { \
75 write(fd, src, n); \
76 if (s) { \
77 memcpy(dst, src, n); \
78 dst += n; \
79 } \
80 } while (0);
82 static void put_loop(ge_GIF *gif, uint16_t loop);
84 ge_GIF *
85 ge_new_gif(
86 const char *fname, uint16_t width, uint16_t height,
87 uint8_t *palette, int depth, int bgindex, int loop
90 int i, r, g, b, v;
91 int store_gct, custom_gct;
92 int nbuffers = bgindex < 0 ? 2 : 1;
93 ge_GIF *gif = calloc(1, sizeof(*gif) + nbuffers*width*height);
94 if (!gif)
95 goto no_gif;
96 gif->w = width; gif->h = height;
97 gif->bgindex = bgindex;
98 gif->frame = (uint8_t *) &gif[1];
99 gif->back = &gif->frame[width*height];
100 #ifdef _WIN32
101 gif->fd = creat(fname, S_IWRITE);
102 #else
103 gif->fd = creat(fname, 0666);
104 #endif
105 if (gif->fd == -1)
106 goto no_fd;
107 #ifdef _WIN32
108 setmode(gif->fd, O_BINARY);
109 #endif
110 write(gif->fd, "GIF89a", 6);
111 write_num(gif->fd, width);
112 write_num(gif->fd, height);
113 store_gct = custom_gct = 0;
114 if (palette) {
115 if (depth < 0)
116 store_gct = 1;
117 else
118 custom_gct = 1;
120 if (depth < 0)
121 depth = -depth;
122 gif->depth = depth > 1 ? depth : 2;
123 write(gif->fd, (uint8_t []) {0xF0 | (depth-1), (uint8_t) bgindex, 0x00}, 3);
124 if (custom_gct) {
125 write(gif->fd, palette, 3 << depth);
126 } else if (depth <= 4) {
127 write_and_store(store_gct, palette, gif->fd, vga, 3 << depth);
128 } else {
129 write_and_store(store_gct, palette, gif->fd, vga, sizeof(vga));
130 i = 0x10;
131 for (r = 0; r < 6; r++) {
132 for (g = 0; g < 6; g++) {
133 for (b = 0; b < 6; b++) {
134 write_and_store(store_gct, palette, gif->fd,
135 ((uint8_t []) {r*51, g*51, b*51}), 3
137 if (++i == 1 << depth)
138 goto done_gct;
142 for (i = 1; i <= 24; i++) {
143 v = i * 0xFF / 25;
144 write_and_store(store_gct, palette, gif->fd,
145 ((uint8_t []) {v, v, v}), 3
149 done_gct:
150 if (loop >= 0 && loop <= 0xFFFF)
151 put_loop(gif, (uint16_t) loop);
152 return gif;
153 no_fd:
154 free(gif);
155 no_gif:
156 return NULL;
159 static void
160 put_loop(ge_GIF *gif, uint16_t loop)
162 write(gif->fd, (uint8_t []) {'!', 0xFF, 0x0B}, 3);
163 write(gif->fd, "NETSCAPE2.0", 11);
164 write(gif->fd, (uint8_t []) {0x03, 0x01}, 2);
165 write_num(gif->fd, loop);
166 write(gif->fd, "\0", 1);
169 /* Add packed key to buffer, updating offset and partial.
170 * gif->offset holds position to put next *bit*
171 * gif->partial holds bits to include in next byte */
172 static void
173 put_key(ge_GIF *gif, uint16_t key, int key_size)
175 int byte_offset, bit_offset, bits_to_write;
176 byte_offset = gif->offset / 8;
177 bit_offset = gif->offset % 8;
178 gif->partial |= ((uint32_t) key) << bit_offset;
179 bits_to_write = bit_offset + key_size;
180 while (bits_to_write >= 8) {
181 gif->buffer[byte_offset++] = gif->partial & 0xFF;
182 if (byte_offset == 0xFF) {
183 write(gif->fd, "\xFF", 1);
184 write(gif->fd, gif->buffer, 0xFF);
185 byte_offset = 0;
187 gif->partial >>= 8;
188 bits_to_write -= 8;
190 gif->offset = (gif->offset + key_size) % (0xFF * 8);
193 static void
194 end_key(ge_GIF *gif)
196 int byte_offset;
197 byte_offset = gif->offset / 8;
198 if (gif->offset % 8)
199 gif->buffer[byte_offset++] = gif->partial & 0xFF;
200 if (byte_offset) {
201 write(gif->fd, (uint8_t []) {byte_offset}, 1);
202 write(gif->fd, gif->buffer, byte_offset);
204 write(gif->fd, "\0", 1);
205 gif->offset = gif->partial = 0;
208 static void
209 put_image(ge_GIF *gif, uint16_t w, uint16_t h, uint16_t x, uint16_t y)
211 int nkeys, key_size, i, j;
212 Node *node, *child, *root;
213 int degree = 1 << gif->depth;
215 write(gif->fd, ",", 1);
216 write_num(gif->fd, x);
217 write_num(gif->fd, y);
218 write_num(gif->fd, w);
219 write_num(gif->fd, h);
220 write(gif->fd, (uint8_t []) {0x00, gif->depth}, 2);
221 root = node = new_trie(degree, &nkeys);
222 key_size = gif->depth + 1;
223 put_key(gif, degree, key_size); /* clear code */
224 for (i = y; i < y+h; i++) {
225 for (j = x; j < x+w; j++) {
226 uint8_t pixel = gif->frame[i*gif->w+j] & (degree - 1);
227 child = node->children[pixel];
228 if (child) {
229 node = child;
230 } else {
231 put_key(gif, node->key, key_size);
232 if (nkeys < 0x1000) {
233 if (nkeys == (1 << key_size))
234 key_size++;
235 node->children[pixel] = new_node(nkeys++, degree);
236 } else {
237 put_key(gif, degree, key_size); /* clear code */
238 del_trie(root, degree);
239 root = node = new_trie(degree, &nkeys);
240 key_size = gif->depth + 1;
242 node = root->children[pixel];
246 put_key(gif, node->key, key_size);
247 put_key(gif, degree + 1, key_size); /* stop code */
248 end_key(gif);
249 del_trie(root, degree);
252 static int
253 get_bbox(ge_GIF *gif, uint16_t *w, uint16_t *h, uint16_t *x, uint16_t *y)
255 int i, j, k;
256 int left, right, top, bottom;
257 uint8_t back;
258 left = gif->w; right = 0;
259 top = gif->h; bottom = 0;
260 k = 0;
261 for (i = 0; i < gif->h; i++) {
262 for (j = 0; j < gif->w; j++, k++) {
263 back = gif->bgindex >= 0 ? gif->bgindex : gif->back[k];
264 if (gif->frame[k] != back) {
265 if (j < left) left = j;
266 if (j > right) right = j;
267 if (i < top) top = i;
268 if (i > bottom) bottom = i;
272 if (left != gif->w && top != gif->h) {
273 *x = left; *y = top;
274 *w = right - left + 1;
275 *h = bottom - top + 1;
276 return 1;
277 } else {
278 return 0;
282 static void
283 add_graphics_control_extension(ge_GIF *gif, uint16_t d)
285 uint8_t flags = ((gif->bgindex >= 0 ? 2 : 1) << 2) + 1;
286 write(gif->fd, (uint8_t []) {'!', 0xF9, 0x04, flags}, 4);
287 write_num(gif->fd, d);
288 write(gif->fd, (uint8_t []) {(uint8_t) gif->bgindex, 0x00}, 2);
291 void
292 ge_add_frame(ge_GIF *gif, uint16_t delay)
294 uint16_t w, h, x, y;
295 uint8_t *tmp;
297 if (delay || (gif->bgindex >= 0))
298 add_graphics_control_extension(gif, delay);
299 if (gif->nframes == 0) {
300 w = gif->w;
301 h = gif->h;
302 x = y = 0;
303 } else if (!get_bbox(gif, &w, &h, &x, &y)) {
304 /* image's not changed; save one pixel just to add delay */
305 w = h = 1;
306 x = y = 0;
308 put_image(gif, w, h, x, y);
309 gif->nframes++;
310 if (gif->bgindex < 0) {
311 tmp = gif->back;
312 gif->back = gif->frame;
313 gif->frame = tmp;
317 void
318 ge_close_gif(ge_GIF* gif)
320 write(gif->fd, ";", 1);
321 close(gif->fd);
322 free(gif);