Merge pull request #8 from rdebath/patch-palettes
[congif.git] / gif.c
blob544574c4cfc92a45178f9b89386835f51a89545b
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <fcntl.h>
7 #include <unistd.h>
9 #include "gif.h"
11 /* helper to write a little-endian 16-bit number portably */
12 #define write_num(fd, n) write((fd), (uint8_t []) {(n) & 0xFF, (n) >> 8}, 2)
14 struct Node {
15 uint16_t key;
16 struct Node *children[0x10];
18 typedef struct Node Node;
20 static Node *
21 new_node(uint16_t key)
23 Node *node = calloc(1, sizeof(*node));
24 if (node)
25 node->key = key;
26 return node;
29 static Node *
30 new_trie(int *nkeys)
32 Node *root = new_node(0);
33 /* Create nodes for single pixels. */
34 for (*nkeys = 0; *nkeys < 0x10; (*nkeys)++)
35 root->children[*nkeys] = new_node(*nkeys);
36 *nkeys += 2; /* skip clear code and stop code */
37 return root;
40 static void
41 del_trie(Node *root)
43 int i;
45 if (!root)
46 return;
47 for (i = 0; i < 0x10; i++)
48 del_trie(root->children[i]);
49 free(root);
52 static void put_loop(GIF *gif, uint16_t loop);
54 GIF *
55 new_gif(const char *fname, uint16_t w, uint16_t h, uint8_t *gct, int loop)
57 GIF *gif = calloc(1, sizeof(*gif) + 2*w*h);
58 if (!gif)
59 goto no_gif;
60 gif->w = w; gif->h = h;
61 gif->cur = (uint8_t *) &gif[1];
62 gif->old = &gif->cur[w*h];
63 /* fill back-buffer with invalid pixels to force overwrite */
64 memset(gif->old, 0x10, w*h);
65 gif->fd = creat(fname, 0666);
66 if (gif->fd == -1)
67 goto no_fd;
68 write(gif->fd, "GIF89a", 6);
69 write_num(gif->fd, w);
70 write_num(gif->fd, h);
71 write(gif->fd, (uint8_t []) {0xF3, 0x00, 0x00}, 3);
72 write(gif->fd, gct, 0x30);
73 if (loop >= 0 && loop <= 0xFFFF)
74 put_loop(gif, (uint16_t) loop);
75 return gif;
76 no_fd:
77 free(gif);
78 no_gif:
79 return NULL;
82 static void
83 put_loop(GIF *gif, uint16_t loop)
85 write(gif->fd, (uint8_t []) {'!', 0xFF, 0x0B}, 3);
86 write(gif->fd, "NETSCAPE2.0", 11);
87 write(gif->fd, (uint8_t []) {0x03, 0x01}, 2);
88 write_num(gif->fd, loop);
89 write(gif->fd, "\0", 1);
92 /* Add packed key to buffer, updating offset and partial.
93 * gif->offset holds position to put next *bit*
94 * gif->partial holds bits to include in next byte */
95 static void
96 put_key(GIF *gif, uint16_t key, int key_size)
98 int byte_offset, bit_offset, bits_to_write;
99 byte_offset = gif->offset / 8;
100 bit_offset = gif->offset % 8;
101 gif->partial |= ((uint32_t) key) << bit_offset;
102 bits_to_write = bit_offset + key_size;
103 while (bits_to_write >= 8) {
104 gif->buffer[byte_offset++] = gif->partial & 0xFF;
105 if (byte_offset == 0xFF) {
106 write(gif->fd, "\xFF", 1);
107 write(gif->fd, gif->buffer, 0xFF);
108 byte_offset = 0;
110 gif->partial >>= 8;
111 bits_to_write -= 8;
113 gif->offset = (gif->offset + key_size) % (0xFF * 8);
116 static void
117 end_key(GIF *gif)
119 int byte_offset;
120 byte_offset = gif->offset / 8;
121 if (gif->offset % 8)
122 gif->buffer[byte_offset++] = gif->partial & 0xFF;
123 if (byte_offset) {
124 write(gif->fd, (uint8_t []) {byte_offset}, 1);
125 write(gif->fd, gif->buffer, byte_offset);
127 write(gif->fd, "\0", 1);
128 gif->offset = gif->partial = 0;
131 static void
132 put_image(GIF *gif, uint16_t w, uint16_t h, uint16_t x, uint16_t y)
134 int nkeys, key_size, i, j;
135 Node *node, *child, *root;
136 uint8_t id_packed = 0x00;
138 if (gif->plt) {
139 id_packed &= ~0x7;
140 id_packed |= 0x83; /* Local clut, 4 bits. */
143 root = malloc(sizeof(*root));
144 write(gif->fd, ",", 1);
145 write_num(gif->fd, x);
146 write_num(gif->fd, y);
147 write_num(gif->fd, w);
148 write_num(gif->fd, h);
149 write(gif->fd, &id_packed, 1);
150 if (id_packed & 0x80)
151 write(gif->fd, gif->plt, 3<<((id_packed & 0x7)+1));
153 write(gif->fd, "\x04", 1); /* Min code size */
154 root = node = new_trie(&nkeys);
155 key_size = 5;
156 put_key(gif, 0x10, key_size); /* clear code */
157 for (i = y; i < y+h; i++) {
158 for (j = x; j < x+w; j++) {
159 uint8_t pixel = gif->cur[i*gif->w+j];
160 child = node->children[pixel];
161 if (child) {
162 node = child;
163 } else {
164 put_key(gif, node->key, key_size);
165 if (nkeys < 0x1000) {
166 if (nkeys == (1 << key_size))
167 key_size++;
168 node->children[pixel] = new_node(nkeys++);
169 } else {
170 put_key(gif, 0x10, key_size); /* clear code */
171 del_trie(root);
172 root = node = new_trie(&nkeys);
173 key_size = 5;
175 node = root->children[pixel];
179 put_key(gif, node->key, key_size);
180 put_key(gif, 0x11, key_size); /* stop code */
181 end_key(gif);
182 del_trie(root);
185 static int
186 get_bbox(GIF *gif, uint16_t *w, uint16_t *h, uint16_t *x, uint16_t *y)
188 int i, j, k;
189 int left, right, top, bottom;
190 left = gif->w; right = 0;
191 top = gif->h; bottom = 0;
192 k = 0;
193 for (i = 0; i < gif->h; i++) {
194 for (j = 0; j < gif->w; j++, k++) {
195 if (gif->cur[k] != gif->old[k]) {
196 if (j < left) left = j;
197 if (j > right) right = j;
198 if (i < top) top = i;
199 if (i > bottom) bottom = i;
203 if (left != gif->w && top != gif->h) {
204 *x = left; *y = top;
205 *w = right - left + 1;
206 *h = bottom - top + 1;
207 return 1;
208 } else {
209 return 0;
213 static void
214 set_delay(GIF *gif, uint16_t d)
216 write(gif->fd, (uint8_t []) {'!', 0xF9, 0x04, 0x04}, 4);
217 write_num(gif->fd, d);
218 write(gif->fd, "\0\0", 2);
221 void
222 add_frame(GIF *gif, uint16_t d)
224 uint16_t w, h, x, y;
225 uint8_t *tmp;
227 if (d)
228 set_delay(gif, d);
229 if (gif->plt_dirty) {
230 w = gif->w; h = gif->h; x = y = 0;
231 gif->plt_dirty = 0;
232 } else
233 if (!get_bbox(gif, &w, &h, &x, &y)) {
234 /* image's not changed; save one pixel just to add delay */
235 w = h = 1;
236 x = y = 0;
238 put_image(gif, w, h, x, y);
239 tmp = gif->old;
240 gif->old = gif->cur;
241 gif->cur = tmp;
244 void
245 close_gif(GIF* gif)
247 write(gif->fd, ";", 1);
248 close(gif->fd);
249 free(gif);