1#ifndef GIF_LOAD_H 
2#define GIF_LOAD_H 
3 
4/** gif_load: A slim, fast and header-only GIF loader written in C. 
5 Original author: hidefromkgb (hidefromkgb@gmail.com) 
6 _________________________________________________________________________ 
7 
8 This is free and unencumbered software released into the public domain. 
9 
10 Anyone is free to copy, modify, publish, use, compile, sell, or 
11 distribute this software, either in source code form or as a compiled 
12 binary, for any purpose, commercial or non-commercial, and by any means. 
13 
14 In jurisdictions that recognize copyright laws, the author or authors 
15 of this software dedicate any and all copyright interest in the 
16 software to the public domain. We make this dedication for the benefit 
17 of the public at large and to the detriment of our heirs and 
18 successors. We intend this dedication to be an overt act of 
19 relinquishment in perpetuity of all present and future rights to this 
20 software under copyright law. 
21 
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
23 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
24 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
25 IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 
26 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
27 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
28 OTHER DEALINGS IN THE SOFTWARE. 
29 _________________________________________________________________________ 
30**/ 
31 
32#ifdef __cplusplus 
33extern "C" { 
34#endif 
35#include <stdint.h> /** imports uint8_t, uint16_t and uint32_t **/ 
36#ifndef GIF_MGET 
37 #include <stdlib.h> 
38 #define GIF_MGET(m,s,a,c) m = (uint8_t*)realloc((c)? 0 : m, (c)? s : 0UL); 
39#endif 
40#ifndef GIF_BIGE 
41 #define GIF_BIGE 0 
42#endif 
43#ifndef GIF_EXTR 
44 #define GIF_EXTR static 
45#endif 
46#define _GIF_SWAP(h) ((GIF_BIGE)? ((uint16_t)(h << 8) | (h >> 8)) : h) 
47 
48#pragma pack(push, 1) 
49struct GIF_WHDR { /** ======== frame writer info: ======== **/ 
50 long xdim, ydim, clrs, /** global dimensions, palette size **/ 
51 bkgd, tran, /** background index, transparent index **/ 
52 intr, mode, /** interlace flag, frame blending mode **/ 
53 frxd, fryd, frxo, fryo, /** current frame dimensions and offset **/ 
54 time, ifrm, nfrm; /** delay, frame number, frame count **/ 
55 uint8_t *bptr; /** frame pixel indices or metadata **/ 
56 struct { /** [==== GIF RGB palette element: ====] **/ 
57 uint8_t R, G, B; /** [color values - red, green, blue ] **/ 
58 } *cpal; /** current palette **/ 
59}; 
60#pragma pack(pop) 
61 
62enum {GIF_NONE = 0, GIF_CURR = 1, GIF_BKGD = 2, GIF_PREV = 3}; 
63 
64/** [ internal function, do not use ] **/ 
65static long _GIF_SkipChunk(uint8_t **buff, long size) { 
66 long skip; 
67 
68 for (skip = 2, ++size, ++(*buff); ((size -= skip) > 0) && (skip > 1); 
69 *buff += (skip = 1 + **buff)); 
70 return size; 
71} 
72 
73/** [ internal function, do not use ] **/ 
74static long _GIF_LoadHeader(unsigned gflg, uint8_t **buff, void **rpal, 
75 unsigned fflg, long *size, long flen) { 
76 if (flen && (!(*buff += flen) || ((*size -= flen) <= 0))) 
77 return -2; /** v--[ 0x80: "palette is present" flag ]--, **/ 
78 if (flen && (fflg & 0x80)) { /** local palette has priority | **/ 
79 *rpal = *buff; /** [ 3L: 3 uint8_t color channels ]--, | **/ 
80 *buff += (flen = 2 << (fflg & 7)) * 3L; /** <--| | **/ 
81 return ((*size -= flen * 3L) > 0)? flen : -1; /** <--' | **/ 
82 } /** no local palette found, checking for the global one | **/ 
83 return (gflg & 0x80)? (2 << (gflg & 7)) : 0; /** <-----' **/ 
84} 
85 
86/** [ internal function, do not use ] **/ 
87static long _GIF_LoadFrame(uint8_t **buff, long *size, 
88 uint8_t *bptr, uint8_t *blen) { 
89 typedef uint16_t GIF_H; 
90 const long GIF_HLEN = sizeof(GIF_H), /** to rid the scope of sizeof **/ 
91 GIF_CLEN = 1 << 12; /** code table length: 4096 items **/ 
92 GIF_H accu, mask; /** bit accumulator / bit mask **/ 
93 long ctbl, iter, /** last code table index / index string iterator **/ 
94 prev, curr, /** codes from the stream: previous / current **/ 
95 ctsz, ccsz, /** code table bit sizes: min LZW / current **/ 
96 bseq, bszc; /** counters: block sequence / bit size **/ 
97 uint32_t *code = (uint32_t*)bptr - GIF_CLEN; /** code table pointer **/ 
98 
99 /** preparing initial values **/ 
100 if ((--(*size) <= GIF_HLEN) || !*++(*buff)) 
101 return -4; /** unexpected end of the stream: insufficient size **/ 
102 mask = (GIF_H)((1 << (ccsz = (ctsz = *(*buff - 1)) + 1)) - 1); 
103 if ((ctsz < 2) || (ctsz > 8)) 
104 return -3; /** min LZW size is out of its nominal [2; 8] bounds **/ 
105 if ((ctbl = (1L << ctsz)) != (mask & _GIF_SWAP(*(GIF_H*)(*buff + 1)))) 
106 return -2; /** initial code is not equal to min LZW size **/ 
107 for (curr = ++ctbl; curr; code[--curr] = 0); /** actual color codes **/ 
108 
109 /** getting codes from stream (--size makes up for end-of-stream mark) **/ 
110 for (--(*size), bszc = -ccsz, prev = curr = 0; 
111 ((*size -= (bseq = *(*buff)++) + 1) >= 0) && bseq; *buff += bseq) 
112 for (; bseq > 0; bseq -= GIF_HLEN, *buff += GIF_HLEN) 
113 for (accu = (GIF_H)(_GIF_SWAP(*(GIF_H*)*buff) 
114 & ((bseq < GIF_HLEN)? ((1U << (8 * bseq)) - 1U) : ~0U)), 
115 curr |= accu << (ccsz + bszc), accu = (GIF_H)(accu >> -bszc), 
116 bszc += 8 * ((bseq < GIF_HLEN)? bseq : GIF_HLEN); 
117 bszc >= 0; bszc -= ccsz, prev = curr, curr = accu, 
118 accu = (GIF_H)(accu >> ccsz)) 
119 if (((curr &= mask) & ~1L) == (1L << ctsz)) { 
120 if (~(ctbl = curr + 1) & 1) /** end-of-data code (ED). **/ 
121 /** -1: no end-of-stream mark after ED; 1: decoded **/ 
122 return (*((*buff += bseq + 1) - 1))? -1 : 1; 
123 mask = (GIF_H)((1 << (ccsz = ctsz + 1)) - 1); 
124 } /** ^- table drop code (TD). TD = 1 << ctsz, ED = TD + 1 **/ 
125 else { /** single-pixel (SP) or multi-pixel (MP) code. **/ 
126 if (ctbl < GIF_CLEN) { /** is the code table full? **/ 
127 if ((ctbl == mask) && (ctbl < GIF_CLEN - 1)) { 
128 mask = (GIF_H)(mask + mask + 1); 
129 ccsz++; /** yes; extending **/ 
130 } /** prev = TD? => curr < ctbl = prev **/ 
131 code[ctbl] = (uint32_t)prev + (code[prev] & 0xFFF000); 
132 } /** appending SP / MP decoded pixels to the frame **/ 
133 prev = (long)code[iter = (ctbl > curr)? curr : prev]; 
134 if ((bptr += (prev = (prev >> 12) & 0xFFF)) > blen) 
135 continue; /** skipping pixels above frame capacity **/ 
136 for (prev++; (iter &= 0xFFF) >> ctsz; 
137 *bptr-- = (uint8_t)((iter = (long)code[iter]) >> 24)); 
138 (bptr += prev)[-prev] = (uint8_t)iter; 
139 if (ctbl < GIF_CLEN) { /** appending the code table **/ 
140 if (ctbl == curr) 
141 *bptr++ = (uint8_t)iter; 
142 else if (ctbl < curr) 
143 return -5; /** wrong code in the stream **/ 
144 code[ctbl++] += ((uint32_t)iter << 24) + 0x1000; 
145 } 
146 } /** 0: no ED before end-of-stream mark; -4: see above **/ 
147 return (++(*size) >= 0)? 0 : -4; /** ^- N.B.: 0 error is recoverable **/ 
148} 
149 
150/** _________________________________________________________________________ 
151 The main loading function. Returns the total number of frames if the data 
152 includes proper GIF ending, and otherwise it returns the number of frames 
153 loaded per current call, multiplied by -1. So, the data may be incomplete 
154 and in this case the function can be called again when more data arrives, 
155 just remember to keep SKIP up to date. 
156 _________________________________________________________________________ 
157 DATA: raw data chunk, may be partial 
158 SIZE: size of the data chunk that`s currently present 
159 GWFR: frame writer function, MANDATORY 
160 EAMF: metadata reader function, set to 0 if not needed 
161 ANIM: implementation-specific data (e.g. a structure or a pointer to it) 
162 SKIP: number of frames to skip before resuming 
163 **/ 
164GIF_EXTR long GIF_Load(void *data, long size, 
165 void (*gwfr)(void*, struct GIF_WHDR*), 
166 void (*eamf)(void*, struct GIF_WHDR*), 
167 void *anim, long skip) { 
168 const long GIF_BLEN = (1 << 12) * sizeof(uint32_t); 
169 const uint8_t GIF_EHDM = 0x21, /** extension header mark **/ 
170 GIF_FHDM = 0x2C, /** frame header mark **/ 
171 GIF_EOFM = 0x3B, /** end-of-file mark **/ 
172 GIF_EGCM = 0xF9, /** extension: graphics control mark **/ 
173 GIF_EAMM = 0xFF; /** extension: app metadata mark **/ 
174 #pragma pack(push, 1) 
175 struct GIF_GHDR { /** ========== GLOBAL GIF HEADER: ========== **/ 
176 uint8_t head[6]; /** 'GIF87a' / 'GIF89a' header signature **/ 
177 uint16_t xdim, ydim; /** total image width, total image height **/ 
178 uint8_t flgs; /** FLAGS: 
179 GlobalPlt bit 7 1: global palette exists 
180 0: local in each frame 
181 ClrRes bit 6-4 bits/channel = ClrRes+1 
182 [reserved] bit 3 0 
183 PixelBits bit 2-0 |Plt| = 2 * 2^PixelBits 
184 **/ 
185 uint8_t bkgd, aspr; /** background color index, aspect ratio **/ 
186 } *ghdr = (struct GIF_GHDR*)data; 
187 struct GIF_FHDR { /** ======= GIF FRAME MASTER HEADER: ======= **/ 
188 uint16_t frxo, fryo; /** offset of this frame in a "full" image **/ 
189 uint16_t frxd, fryd; /** frame width, frame height **/ 
190 uint8_t flgs; /** FLAGS: 
191 LocalPlt bit 7 1: local palette exists 
192 0: global is used 
193 Interlaced bit 6 1: interlaced frame 
194 0: non-interlaced frame 
195 Sorted bit 5 usually 0 
196 [reserved] bit 4-3 [undefined] 
197 PixelBits bit 2-0 |Plt| = 2 * 2^PixelBits 
198 **/ 
199 } *fhdr; 
200 struct GIF_EGCH { /** ==== [EXT] GRAPHICS CONTROL HEADER: ==== **/ 
201 uint8_t flgs; /** FLAGS: 
202 [reserved] bit 7-5 [undefined] 
203 BlendMode bit 4-2 000: not set; static GIF 
204 001: leave result as is 
205 010: restore background 
206 011: restore previous 
207 1--: [undefined] 
208 UserInput bit 1 1: show frame till input 
209 0: default; ~99% of GIFs 
210 TransColor bit 0 1: got transparent color 
211 0: frame is fully opaque 
212 **/ 
213 uint16_t time; /** delay in GIF time units; 1 unit = 10 ms **/ 
214 uint8_t tran; /** transparent color index **/ 
215 } *egch = 0; 
216 #pragma pack(pop) 
217 struct GIF_WHDR wtmp, whdr = {0}; 
218 long desc, blen; 
219 uint8_t *buff; 
220 
221 /** checking if the stream is not empty and has a 'GIF8[79]a' signature, 
222 the data has sufficient size and frameskip value is non-negative **/ 
223 if (!ghdr || (size <= (long)sizeof(*ghdr)) || (*(buff = ghdr->head) != 71) 
224 || (buff[1] != 73) || (buff[2] != 70) || (buff[3] != 56) || (skip < 0) 
225 || ((buff[4] != 55) && (buff[4] != 57)) || (buff[5] != 97) || !gwfr) 
226 return 0; 
227 
228 buff = (uint8_t*)(ghdr + 1) /** skipping the global header and palette **/ 
229 + _GIF_LoadHeader(ghdr->flgs, 0, 0, 0, 0, 0L) * 3L; 
230 if ((size -= long(buff - (uint8_t*)ghdr)) <= 0) 
231 return 0; 
232 
233 whdr.xdim = _GIF_SWAP(ghdr->xdim); 
234 whdr.ydim = _GIF_SWAP(ghdr->ydim); 
235 for (whdr.bptr = buff, whdr.bkgd = ghdr->bkgd, blen = --size; 
236 (blen >= 0) && ((desc = *whdr.bptr++) != GIF_EOFM); /** sic: '>= 0' **/ 
237 blen = _GIF_SkipChunk(&whdr.bptr, blen) - 1) /** count all frames **/ 
238 if (desc == GIF_FHDM) { 
239 fhdr = (struct GIF_FHDR*)whdr.bptr; 
240 if (_GIF_LoadHeader(ghdr->flgs, &whdr.bptr, (void**)&whdr.cpal, 
241 fhdr->flgs, &blen, sizeof(*fhdr)) <= 0) 
242 break; 
243 whdr.frxd = _GIF_SWAP(fhdr->frxd); 
244 whdr.fryd = _GIF_SWAP(fhdr->fryd); 
245 whdr.frxo = (whdr.frxd > whdr.frxo)? whdr.frxd : whdr.frxo; 
246 whdr.fryo = (whdr.fryd > whdr.fryo)? whdr.fryd : whdr.fryo; 
247 whdr.ifrm++; 
248 } 
249 blen = whdr.frxo * whdr.fryo * (long)sizeof(*whdr.bptr); 
250 GIF_MGET(whdr.bptr, (unsigned long)(blen + GIF_BLEN + 2), anim, 1) 
251 whdr.nfrm = (desc != GIF_EOFM)? -whdr.ifrm : whdr.ifrm; 
252 for (whdr.bptr += GIF_BLEN, whdr.ifrm = -1; blen /** load all frames **/ 
253 && (skip < ((whdr.nfrm < 0)? -whdr.nfrm : whdr.nfrm)) && (size >= 0); 
254 size = (desc != GIF_EOFM)? ((desc != GIF_FHDM) || (skip > whdr.ifrm))? 
255 _GIF_SkipChunk(&buff, size) - 1 : size - 1 : -1) 
256 if ((desc = *buff++) == GIF_FHDM) { /** found a frame **/ 
257 whdr.intr = !!((fhdr = (struct GIF_FHDR*)buff)->flgs & 0x40); 
258 *(void**)&whdr.cpal = (void*)(ghdr + 1); /** interlaced? -^ **/ 
259 whdr.clrs = _GIF_LoadHeader(ghdr->flgs, &buff, (void**)&whdr.cpal, 
260 fhdr->flgs, &size, sizeof(*fhdr)); 
261 if ((skip <= ++whdr.ifrm) && ((whdr.clrs <= 0) 
262 || (_GIF_LoadFrame(&buff, &size, 
263 whdr.bptr, whdr.bptr + blen) < 0))) 
264 size = -(whdr.ifrm--) - 1; /** failed to load the frame **/ 
265 else if (skip <= whdr.ifrm) { 
266 whdr.frxd = _GIF_SWAP(fhdr->frxd); 
267 whdr.fryd = _GIF_SWAP(fhdr->fryd); 
268 whdr.frxo = _GIF_SWAP(fhdr->frxo); 
269 whdr.fryo = _GIF_SWAP(fhdr->fryo); 
270 whdr.time = (egch)? _GIF_SWAP(egch->time) : 0; 
271 whdr.tran = (egch && (egch->flgs & 0x01))? egch->tran : -1; 
272 whdr.time = (egch && (egch->flgs & 0x02))? -whdr.time - 1 
273 : whdr.time; 
274 whdr.mode = (egch && !(egch->flgs & 0x10))? 
275 (egch->flgs & 0x0C) >> 2 : GIF_NONE; 
276 egch = 0; 
277 wtmp = whdr; 
278 gwfr(anim, &wtmp); /** passing the frame to the caller **/ 
279 } 
280 } 
281 else if (desc == GIF_EHDM) { /** found an extension **/ 
282 if (*buff == GIF_EGCM) /** graphics control ext. **/ 
283 egch = (struct GIF_EGCH*)(buff + 1 + 1); 
284 else if ((*buff == GIF_EAMM) && eamf) { /** app metadata ext. **/ 
285 wtmp = whdr; 
286 wtmp.bptr = buff + 1 + 1; /** just passing the raw chunk **/ 
287 eamf(anim, &wtmp); 
288 } 
289 } 
290 whdr.bptr -= GIF_BLEN; /** for excess pixel codes ----v (here & above) **/ 
291 GIF_MGET(whdr.bptr, (unsigned long)(blen + GIF_BLEN + 2), anim, 0) 
292 return (whdr.nfrm < 0)? (skip - whdr.ifrm - 1) : (whdr.ifrm + 1); 
293} 
294 
295#undef _GIF_SWAP 
296#ifdef __cplusplus 
297} 
298#endif 
299#endif /** GIF_LOAD_H **/ 
300