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