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 
165
166 void *data, long size
167 void (*gwfr)(void*, struct GIF_WHDR*), 
168 void (*eamf)(void*, struct GIF_WHDR*), 
169 void *anim, long skip
170 int& largestPaletteSize 
171
172
173 const long GIF_BLEN = (1 << 12) * sizeof(uint32_t); 
174 const uint8_t GIF_EHDM = 0x21, /** extension header mark **/ 
175 GIF_FHDM = 0x2C, /** frame header mark **/ 
176 GIF_EOFM = 0x3B, /** end-of-file mark **/ 
177 GIF_EGCM = 0xF9, /** extension: graphics control mark **/ 
178 GIF_EAMM = 0xFF; /** extension: app metadata mark **/ 
179 #pragma pack(push, 1) 
180 struct GIF_GHDR { /** ========== GLOBAL GIF HEADER: ========== **/ 
181 uint8_t head[6]; /** 'GIF87a' / 'GIF89a' header signature **/ 
182 uint16_t xdim, ydim; /** total image width, total image height **/ 
183 uint8_t flgs; /** FLAGS: 
184 GlobalPlt bit 7 1: global palette exists 
185 0: local in each frame 
186 ClrRes bit 6-4 bits/channel = ClrRes+1 
187 [reserved] bit 3 0 
188 PixelBits bit 2-0 |Plt| = 2 * 2^PixelBits 
189 **/ 
190 uint8_t bkgd, aspr; /** background color index, aspect ratio **/ 
191 } *ghdr = (struct GIF_GHDR*)data
192 struct GIF_FHDR { /** ======= GIF FRAME MASTER HEADER: ======= **/ 
193 uint16_t frxo, fryo; /** offset of this frame in a "full" image **/ 
194 uint16_t frxd, fryd; /** frame width, frame height **/ 
195 uint8_t flgs; /** FLAGS: 
196 LocalPlt bit 7 1: local palette exists 
197 0: global is used 
198 Interlaced bit 6 1: interlaced frame 
199 0: non-interlaced frame 
200 Sorted bit 5 usually 0 
201 [reserved] bit 4-3 [undefined] 
202 PixelBits bit 2-0 |Plt| = 2 * 2^PixelBits 
203 **/ 
204 } *fhdr
205 struct GIF_EGCH { /** ==== [EXT] GRAPHICS CONTROL HEADER: ==== **/ 
206 uint8_t flgs; /** FLAGS: 
207 [reserved] bit 7-5 [undefined] 
208 BlendMode bit 4-2 000: not set; static GIF 
209 001: leave result as is 
210 010: restore background 
211 011: restore previous 
212 1--: [undefined] 
213 UserInput bit 1 1: show frame till input 
214 0: default; ~99% of GIFs 
215 TransColor bit 0 1: got transparent color 
216 0: frame is fully opaque 
217 **/ 
218 uint16_t time; /** delay in GIF time units; 1 unit = 10 ms **/ 
219 uint8_t tran; /** transparent color index **/ 
220 } *egch = 0
221 #pragma pack(pop) 
222 struct GIF_WHDR wtmp, whdr = {.xdim: 0}; 
223 long desc, blen
224 uint8_t *buff
225 
226 /** checking if the stream is not empty and has a 'GIF8[79]a' signature, 
227 the data has sufficient size and frameskip value is non-negative **/ 
228 if (!ghdr || (size <= (long)sizeof(*ghdr)) || (*(buff = ghdr->head) != 71
229 || (buff[1] != 73) || (buff[2] != 70) || (buff[3] != 56) || (skip < 0
230 || ((buff[4] != 55) && (buff[4] != 57)) || (buff[5] != 97) || !gwfr
231 return 0
232 
233 // Begin tacent. 
234 largestPaletteSize = 0
235 int globalPalSize = 2 << (ghdr->flgs & 0x07); 
236 if (globalPalSize > largestPaletteSize
237 largestPaletteSize = globalPalSize
238 // End tacent. 
239 
240 buff = (uint8_t*)(ghdr + 1) /** skipping the global header and palette **/ 
241 + _GIF_LoadHeader(gflg: ghdr->flgs, buff: 0, rpal: 0, fflg: 0, size: 0, flen: 0L) * 3L
242 if ((size -= long(buff - (uint8_t*)ghdr)) <= 0
243 return 0
244 
245 whdr.xdim = _GIF_SWAP(ghdr->xdim); 
246 whdr.ydim = _GIF_SWAP(ghdr->ydim); 
247 for (whdr.bptr = buff, whdr.bkgd = ghdr->bkgd, blen = --size
248 (blen >= 0) && ((desc = *whdr.bptr++) != GIF_EOFM); /** sic: '>= 0' **/ 
249 blen = _GIF_SkipChunk(buff: &whdr.bptr, size: blen) - 1) /** count all frames **/ 
250 if (desc == GIF_FHDM
251
252 fhdr = (struct GIF_FHDR*)whdr.bptr
253 
254 // Begin tacent. 
255 int framePalSize = 2 << (fhdr->flgs & 0x07); 
256 if (framePalSize > largestPaletteSize
257 largestPaletteSize = framePalSize
258 // End tacent. 
259 
260 if (_GIF_LoadHeader(gflg: ghdr->flgs, buff: &whdr.bptr, rpal: (void**)&whdr.cpal
261 fflg: fhdr->flgs, size: &blen, flen: sizeof(*fhdr)) <= 0
262 break
263 whdr.frxd = _GIF_SWAP(fhdr->frxd); 
264 whdr.fryd = _GIF_SWAP(fhdr->fryd); 
265 whdr.frxo = (whdr.frxd > whdr.frxo)? whdr.frxd : whdr.frxo
266 whdr.fryo = (whdr.fryd > whdr.fryo)? whdr.fryd : whdr.fryo
267 whdr.ifrm++; 
268
269 blen = whdr.frxo * whdr.fryo * (long)sizeof(*whdr.bptr); 
270 GIF_MGET(whdr.bptr, (unsigned long)(blen + GIF_BLEN + 2), anim, 1
271 whdr.nfrm = (desc != GIF_EOFM)? -whdr.ifrm : whdr.ifrm
272 for (whdr.bptr += GIF_BLEN, whdr.ifrm = -1; blen /** load all frames **/ 
273 && (skip < ((whdr.nfrm < 0)? -whdr.nfrm : whdr.nfrm)) && (size >= 0); 
274 size = (desc != GIF_EOFM)? ((desc != GIF_FHDM) || (skip > whdr.ifrm))? 
275 _GIF_SkipChunk(buff: &buff, size) - 1 : size - 1 : -1
276 if ((desc = *buff++) == GIF_FHDM) { /** found a frame **/ 
277 whdr.intr = !!((fhdr = (struct GIF_FHDR*)buff)->flgs & 0x40); 
278 *(void**)&whdr.cpal = (void*)(ghdr + 1); /** interlaced? -^ **/ 
279 whdr.clrs = _GIF_LoadHeader(gflg: ghdr->flgs, buff: &buff, rpal: (void**)&whdr.cpal
280 fflg: fhdr->flgs, size: &size, flen: sizeof(*fhdr)); 
281 if ((skip <= ++whdr.ifrm) && ((whdr.clrs <= 0
282 || (_GIF_LoadFrame(buff: &buff, size: &size
283 bptr: whdr.bptr, blen: whdr.bptr + blen) < 0))) 
284 size = -(whdr.ifrm--) - 1; /** failed to load the frame **/ 
285 else if (skip <= whdr.ifrm) { 
286 whdr.frxd = _GIF_SWAP(fhdr->frxd); 
287 whdr.fryd = _GIF_SWAP(fhdr->fryd); 
288 whdr.frxo = _GIF_SWAP(fhdr->frxo); 
289 whdr.fryo = _GIF_SWAP(fhdr->fryo); 
290 whdr.time = (egch)? _GIF_SWAP(egch->time) : 0
291 whdr.tran = (egch && (egch->flgs & 0x01))? egch->tran : -1
292 whdr.time = (egch && (egch->flgs & 0x02))? -whdr.time - 1 
293 : whdr.time
294 whdr.mode = (egch && !(egch->flgs & 0x10))? 
295 (egch->flgs & 0x0C) >> 2 : GIF_NONE
296 egch = 0
297 wtmp = whdr
298 gwfr(anim, &wtmp); /** passing the frame to the caller **/ 
299
300
301 else if (desc == GIF_EHDM) { /** found an extension **/ 
302 if (*buff == GIF_EGCM) /** graphics control ext. **/ 
303 egch = (struct GIF_EGCH*)(buff + 1 + 1); 
304 else if ((*buff == GIF_EAMM) && eamf) { /** app metadata ext. **/ 
305 wtmp = whdr
306 wtmp.bptr = buff + 1 + 1; /** just passing the raw chunk **/ 
307 eamf(anim, &wtmp); 
308
309
310 whdr.bptr -= GIF_BLEN; /** for excess pixel codes ----v (here & above) **/ 
311 GIF_MGET(whdr.bptr, (unsigned long)(blen + GIF_BLEN + 2), anim, 0
312 return (whdr.nfrm < 0)? (skip - whdr.ifrm - 1) : (whdr.ifrm + 1); 
313
314 
315#undef _GIF_SWAP 
316#ifdef __cplusplus 
317
318#endif 
319#endif /** GIF_LOAD_H **/ 
320