1 #include "gifdec.h"
2 #include "../../misc/lv_log.h"
3 #include "../../stdlib/lv_mem.h"
4 #include "../../misc/lv_color.h"
5 #if LV_USE_GIF
6 
7 #include <stdlib.h>
8 #include <string.h>
9 #include <stdbool.h>
10 
11 #define MIN(A, B) ((A) < (B) ? (A) : (B))
12 #define MAX(A, B) ((A) > (B) ? (A) : (B))
13 
14 typedef struct Entry {
15     uint16_t length;
16     uint16_t prefix;
17     uint8_t  suffix;
18 } Entry;
19 
20 typedef struct Table {
21     int bulk;
22     int nentries;
23     Entry * entries;
24 } Table;
25 
26 #if LV_GIF_CACHE_DECODE_DATA
27 #define LZW_MAXBITS                 12
28 #define LZW_TABLE_SIZE              (1 << LZW_MAXBITS)
29 #define LZW_CACHE_SIZE              (LZW_TABLE_SIZE * 4)
30 #endif
31 
32 static gd_GIF  * gif_open(gd_GIF * gif);
33 static bool f_gif_open(gd_GIF * gif, const void * path, bool is_file);
34 static void f_gif_read(gd_GIF * gif, void * buf, size_t len);
35 static int f_gif_seek(gd_GIF * gif, size_t pos, int k);
36 static void f_gif_close(gd_GIF * gif);
37 
38 #if LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_HELIUM
39     #include "gifdec_mve.h"
40 #endif
41 
42 static uint16_t
read_num(gd_GIF * gif)43 read_num(gd_GIF * gif)
44 {
45     uint8_t bytes[2];
46 
47     f_gif_read(gif, bytes, 2);
48     return bytes[0] + (((uint16_t) bytes[1]) << 8);
49 }
50 
51 gd_GIF *
gd_open_gif_file(const char * fname)52 gd_open_gif_file(const char * fname)
53 {
54     gd_GIF gif_base;
55     memset(&gif_base, 0, sizeof(gif_base));
56 
57     bool res = f_gif_open(&gif_base, fname, true);
58     if(!res) return NULL;
59 
60     return gif_open(&gif_base);
61 }
62 
63 gd_GIF *
gd_open_gif_data(const void * data)64 gd_open_gif_data(const void * data)
65 {
66     gd_GIF gif_base;
67     memset(&gif_base, 0, sizeof(gif_base));
68 
69     bool res = f_gif_open(&gif_base, data, false);
70     if(!res) return NULL;
71 
72     return gif_open(&gif_base);
73 }
74 
gif_open(gd_GIF * gif_base)75 static gd_GIF * gif_open(gd_GIF * gif_base)
76 {
77     uint8_t sigver[3];
78     uint16_t width, height, depth;
79     uint8_t fdsz, bgidx, aspect;
80     uint8_t * bgcolor;
81     int gct_sz;
82     gd_GIF * gif = NULL;
83 
84     /* Header */
85     f_gif_read(gif_base, sigver, 3);
86     if(memcmp(sigver, "GIF", 3) != 0) {
87         LV_LOG_WARN("invalid signature");
88         goto fail;
89     }
90     /* Version */
91     f_gif_read(gif_base, sigver, 3);
92     if(memcmp(sigver, "89a", 3) != 0) {
93         LV_LOG_WARN("invalid version");
94         goto fail;
95     }
96     /* Width x Height */
97     width  = read_num(gif_base);
98     height = read_num(gif_base);
99     /* FDSZ */
100     f_gif_read(gif_base, &fdsz, 1);
101     /* Presence of GCT */
102     if(!(fdsz & 0x80)) {
103         LV_LOG_WARN("no global color table");
104         goto fail;
105     }
106     /* Color Space's Depth */
107     depth = ((fdsz >> 4) & 7) + 1;
108     /* Ignore Sort Flag. */
109     /* GCT Size */
110     gct_sz = 1 << ((fdsz & 0x07) + 1);
111     /* Background Color Index */
112     f_gif_read(gif_base, &bgidx, 1);
113     /* Aspect Ratio */
114     f_gif_read(gif_base, &aspect, 1);
115     /* Create gd_GIF Structure. */
116     if(0 == width || 0 == height){
117         LV_LOG_WARN("Zero size image");
118         goto fail;
119     }
120 #if LV_GIF_CACHE_DECODE_DATA
121     if(0 == (INT_MAX - sizeof(gd_GIF) - LZW_CACHE_SIZE) / width / height / 5){
122         LV_LOG_WARN("Image dimensions are too large");
123         goto fail;
124     }
125     gif = lv_malloc(sizeof(gd_GIF) + 5 * width * height + LZW_CACHE_SIZE);
126     #else
127     if(0 == (INT_MAX - sizeof(gd_GIF)) / width / height / 5){
128         LV_LOG_WARN("Image dimensions are too large");
129         goto fail;
130     }
131     gif = lv_malloc(sizeof(gd_GIF) + 5 * width * height);
132     #endif
133     if(!gif) goto fail;
134     memcpy(gif, gif_base, sizeof(gd_GIF));
135     gif->width  = width;
136     gif->height = height;
137     gif->depth  = depth;
138     /* Read GCT */
139     gif->gct.size = gct_sz;
140     f_gif_read(gif, gif->gct.colors, 3 * gif->gct.size);
141     gif->palette = &gif->gct;
142     gif->bgindex = bgidx;
143     gif->canvas = (uint8_t *) &gif[1];
144     gif->frame = &gif->canvas[4 * width * height];
145     if(gif->bgindex) {
146         memset(gif->frame, gif->bgindex, gif->width * gif->height);
147     }
148     bgcolor = &gif->palette->colors[gif->bgindex * 3];
149     #if LV_GIF_CACHE_DECODE_DATA
150     gif->lzw_cache = gif->frame + width * height;
151     #endif
152 
153 #ifdef GIFDEC_FILL_BG
154     GIFDEC_FILL_BG(gif->canvas, gif->width * gif->height, 1, gif->width * gif->height, bgcolor, 0xff);
155 #else
156     for(int i = 0; i < gif->width * gif->height; i++) {
157         gif->canvas[i * 4 + 0] = *(bgcolor + 2);
158         gif->canvas[i * 4 + 1] = *(bgcolor + 1);
159         gif->canvas[i * 4 + 2] = *(bgcolor + 0);
160         gif->canvas[i * 4 + 3] = 0xff;
161     }
162 #endif
163     gif->anim_start = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
164     gif->loop_count = -1;
165     goto ok;
166 fail:
167     f_gif_close(gif_base);
168 ok:
169     return gif;
170 }
171 
172 static void
discard_sub_blocks(gd_GIF * gif)173 discard_sub_blocks(gd_GIF * gif)
174 {
175     uint8_t size;
176 
177     do {
178         f_gif_read(gif, &size, 1);
179         f_gif_seek(gif, size, LV_FS_SEEK_CUR);
180     } while(size);
181 }
182 
183 static void
read_plain_text_ext(gd_GIF * gif)184 read_plain_text_ext(gd_GIF * gif)
185 {
186     if(gif->plain_text) {
187         uint16_t tx, ty, tw, th;
188         uint8_t cw, ch, fg, bg;
189         size_t sub_block;
190         f_gif_seek(gif, 1, LV_FS_SEEK_CUR); /* block size = 12 */
191         tx = read_num(gif);
192         ty = read_num(gif);
193         tw = read_num(gif);
194         th = read_num(gif);
195         f_gif_read(gif, &cw, 1);
196         f_gif_read(gif, &ch, 1);
197         f_gif_read(gif, &fg, 1);
198         f_gif_read(gif, &bg, 1);
199         sub_block = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
200         gif->plain_text(gif, tx, ty, tw, th, cw, ch, fg, bg);
201         f_gif_seek(gif, sub_block, LV_FS_SEEK_SET);
202     }
203     else {
204         /* Discard plain text metadata. */
205         f_gif_seek(gif, 13, LV_FS_SEEK_CUR);
206     }
207     /* Discard plain text sub-blocks. */
208     discard_sub_blocks(gif);
209 }
210 
211 static void
read_graphic_control_ext(gd_GIF * gif)212 read_graphic_control_ext(gd_GIF * gif)
213 {
214     uint8_t rdit;
215 
216     /* Discard block size (always 0x04). */
217     f_gif_seek(gif, 1, LV_FS_SEEK_CUR);
218     f_gif_read(gif, &rdit, 1);
219     gif->gce.disposal = (rdit >> 2) & 3;
220     gif->gce.input = rdit & 2;
221     gif->gce.transparency = rdit & 1;
222     gif->gce.delay = read_num(gif);
223     f_gif_read(gif, &gif->gce.tindex, 1);
224     /* Skip block terminator. */
225     f_gif_seek(gif, 1, LV_FS_SEEK_CUR);
226 }
227 
228 static void
read_comment_ext(gd_GIF * gif)229 read_comment_ext(gd_GIF * gif)
230 {
231     if(gif->comment) {
232         size_t sub_block = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
233         gif->comment(gif);
234         f_gif_seek(gif, sub_block, LV_FS_SEEK_SET);
235     }
236     /* Discard comment sub-blocks. */
237     discard_sub_blocks(gif);
238 }
239 
240 static void
read_application_ext(gd_GIF * gif)241 read_application_ext(gd_GIF * gif)
242 {
243     char app_id[8];
244     char app_auth_code[3];
245     uint16_t loop_count;
246 
247     /* Discard block size (always 0x0B). */
248     f_gif_seek(gif, 1, LV_FS_SEEK_CUR);
249     /* Application Identifier. */
250     f_gif_read(gif, app_id, 8);
251     /* Application Authentication Code. */
252     f_gif_read(gif, app_auth_code, 3);
253     if(!strncmp(app_id, "NETSCAPE", sizeof(app_id))) {
254         /* Discard block size (0x03) and constant byte (0x01). */
255         f_gif_seek(gif, 2, LV_FS_SEEK_CUR);
256         loop_count = read_num(gif);
257         if(gif->loop_count < 0) {
258             if(loop_count == 0) {
259                 gif->loop_count = 0;
260             }
261             else {
262                 gif->loop_count = loop_count + 1;
263             }
264         }
265         /* Skip block terminator. */
266         f_gif_seek(gif, 1, LV_FS_SEEK_CUR);
267     }
268     else if(gif->application) {
269         size_t sub_block = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
270         gif->application(gif, app_id, app_auth_code);
271         f_gif_seek(gif, sub_block, LV_FS_SEEK_SET);
272         discard_sub_blocks(gif);
273     }
274     else {
275         discard_sub_blocks(gif);
276     }
277 }
278 
279 static void
read_ext(gd_GIF * gif)280 read_ext(gd_GIF * gif)
281 {
282     uint8_t label;
283 
284     f_gif_read(gif, &label, 1);
285     switch(label) {
286         case 0x01:
287             read_plain_text_ext(gif);
288             break;
289         case 0xF9:
290             read_graphic_control_ext(gif);
291             break;
292         case 0xFE:
293             read_comment_ext(gif);
294             break;
295         case 0xFF:
296             read_application_ext(gif);
297             break;
298         default:
299             LV_LOG_WARN("unknown extension: %02X\n", label);
300     }
301 }
302 
303 static uint16_t
get_key(gd_GIF * gif,int key_size,uint8_t * sub_len,uint8_t * shift,uint8_t * byte)304 get_key(gd_GIF *gif, int key_size, uint8_t *sub_len, uint8_t *shift, uint8_t *byte)
305 {
306     int bits_read;
307     int rpad;
308     int frag_size;
309     uint16_t key;
310 
311     key = 0;
312     for (bits_read = 0; bits_read < key_size; bits_read += frag_size) {
313         rpad = (*shift + bits_read) % 8;
314         if (rpad == 0) {
315             /* Update byte. */
316             if (*sub_len == 0) {
317                 f_gif_read(gif, sub_len, 1); /* Must be nonzero! */
318                 if (*sub_len == 0) return 0x1000;
319             }
320             f_gif_read(gif, byte, 1);
321             (*sub_len)--;
322         }
323         frag_size = MIN(key_size - bits_read, 8 - rpad);
324         key |= ((uint16_t) ((*byte) >> rpad)) << bits_read;
325     }
326     /* Clear extra bits to the left. */
327     key &= (1 << key_size) - 1;
328     *shift = (*shift + key_size) % 8;
329     return key;
330 }
331 
332 #if LV_GIF_CACHE_DECODE_DATA
333 /* Decompress image pixels.
334  * Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table) or parse error. */
335 static int
read_image_data(gd_GIF * gif,int interlace)336 read_image_data(gd_GIF *gif, int interlace)
337 {
338     uint8_t sub_len, shift, byte;
339     int ret = 0;
340     int key_size;
341     int y, pass, linesize;
342     uint8_t *ptr = NULL;
343     uint8_t *ptr_row_start = NULL;
344     uint8_t *ptr_base = NULL;
345     size_t start, end;
346     uint16_t key, clear_code, stop_code, curr_code;
347     int frm_off, frm_size,curr_size,top_slot,new_codes,slot;
348     /* The first value of the value sequence corresponding to key */
349     int first_value;
350     int last_key;
351     uint8_t *sp = NULL;
352     uint8_t *p_stack = NULL;
353     uint8_t *p_suffix = NULL;
354     uint16_t *p_prefix = NULL;
355 
356     /* get initial key size and clear code, stop code */
357     f_gif_read(gif, &byte, 1);
358     key_size = (int) byte;
359     clear_code = 1 << key_size;
360     stop_code = clear_code + 1;
361     key = 0;
362 
363     start = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
364     discard_sub_blocks(gif);
365     end = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
366     f_gif_seek(gif, start, LV_FS_SEEK_SET);
367 
368     linesize = gif->width;
369     ptr_base = &gif->frame[gif->fy * linesize + gif->fx];
370     ptr_row_start = ptr_base;
371     ptr = ptr_row_start;
372     sub_len = shift = 0;
373     /* decoder */
374     pass = 0;
375     y = 0;
376     p_stack = gif->lzw_cache;
377     p_suffix = gif->lzw_cache + LZW_TABLE_SIZE;
378     p_prefix = (uint16_t*)(gif->lzw_cache + LZW_TABLE_SIZE * 2);
379     frm_off = 0;
380     frm_size = gif->fw * gif->fh;
381     curr_size = key_size + 1;
382     top_slot = 1 << curr_size;
383     new_codes = clear_code + 2;
384     slot = new_codes;
385     first_value = -1;
386     last_key = -1;
387     sp = p_stack;
388 
389     while (frm_off < frm_size) {
390         /* copy data to frame buffer */
391         while (sp > p_stack) {
392             if(frm_off >= frm_size){
393                 LV_LOG_WARN("LZW table token overflows the frame buffer");
394                 return -1;
395             }
396             *ptr++ = *(--sp);
397             frm_off += 1;
398             /* read one line */
399             if ((ptr - ptr_row_start) == gif->fw) {
400                 if (interlace) {
401                     switch(pass) {
402                     case 0:
403                     case 1:
404                         y += 8;
405                         ptr_row_start += linesize * 8;
406                         break;
407                     case 2:
408                         y += 4;
409                         ptr_row_start += linesize * 4;
410                         break;
411                     case 3:
412                         y += 2;
413                         ptr_row_start += linesize * 2;
414                         break;
415                     default:
416                         break;
417                     }
418                     while (y >= gif->fh) {
419                         y  = 4 >> pass;
420                         ptr_row_start = ptr_base + linesize * y;
421                         pass++;
422                     }
423                 } else {
424                     ptr_row_start += linesize;
425                 }
426                 ptr = ptr_row_start;
427             }
428         }
429 
430         key = get_key(gif, curr_size, &sub_len, &shift, &byte);
431 
432         if (key == stop_code || key >= LZW_TABLE_SIZE)
433             break;
434 
435         if (key == clear_code) {
436             curr_size = key_size + 1;
437             slot = new_codes;
438             top_slot = 1 << curr_size;
439             first_value = last_key = -1;
440             sp = p_stack;
441             continue;
442         }
443 
444         curr_code = key;
445         /*
446          * If the current code is a code that will be added to the decoding
447          * dictionary, it is composed of the data list corresponding to the
448          * previous key and its first data.
449          * */
450         if (curr_code == slot && first_value >= 0) {
451             *sp++ = first_value;
452             curr_code = last_key;
453         }else if(curr_code >= slot)
454             break;
455 
456         while (curr_code >= new_codes) {
457             *sp++ = p_suffix[curr_code];
458             curr_code = p_prefix[curr_code];
459         }
460         *sp++ = curr_code;
461 
462         /* Add code to decoding dictionary */
463         if (slot < top_slot && last_key >= 0) {
464             p_suffix[slot] = curr_code;
465             p_prefix[slot++] = last_key;
466         }
467         first_value = curr_code;
468         last_key = key;
469         if (slot >= top_slot) {
470             if (curr_size < LZW_MAXBITS) {
471                 top_slot <<= 1;
472                 curr_size += 1;
473             }
474         }
475     }
476 
477     if (key == stop_code) f_gif_read(gif, &sub_len, 1); /* Must be zero! */
478     f_gif_seek(gif, end, LV_FS_SEEK_SET);
479     return ret;
480 }
481 #else
482 static Table *
new_table(int key_size)483 new_table(int key_size)
484 {
485     int key;
486     int init_bulk = MAX(1 << (key_size + 1), 0x100);
487     Table * table = lv_malloc(sizeof(*table) + sizeof(Entry) * init_bulk);
488     if(table) {
489         table->bulk = init_bulk;
490         table->nentries = (1 << key_size) + 2;
491         table->entries = (Entry *) &table[1];
492         for(key = 0; key < (1 << key_size); key++)
493             table->entries[key] = (Entry) {
494             1, 0xFFF, key
495         };
496     }
497     return table;
498 }
499 
500 /* Add table entry. Return value:
501  *  0 on success
502  *  +1 if key size must be incremented after this addition
503  *  -1 if could not realloc table */
504 static int
add_entry(Table ** tablep,uint16_t length,uint16_t prefix,uint8_t suffix)505 add_entry(Table ** tablep, uint16_t length, uint16_t prefix, uint8_t suffix)
506 {
507     Table * table = *tablep;
508     if(table->nentries == table->bulk) {
509         table->bulk *= 2;
510         table = lv_realloc(table, sizeof(*table) + sizeof(Entry) * table->bulk);
511         if(!table) return -1;
512         table->entries = (Entry *) &table[1];
513         *tablep = table;
514     }
515     table->entries[table->nentries] = (Entry) {
516         length, prefix, suffix
517     };
518     table->nentries++;
519     if((table->nentries & (table->nentries - 1)) == 0)
520         return 1;
521     return 0;
522 }
523 
524 /* Compute output index of y-th input line, in frame of height h. */
525 static int
interlaced_line_index(int h,int y)526 interlaced_line_index(int h, int y)
527 {
528     int p; /* number of lines in current pass */
529 
530     p = (h - 1) / 8 + 1;
531     if(y < p)  /* pass 1 */
532         return y * 8;
533     y -= p;
534     p = (h - 5) / 8 + 1;
535     if(y < p)  /* pass 2 */
536         return y * 8 + 4;
537     y -= p;
538     p = (h - 3) / 4 + 1;
539     if(y < p)  /* pass 3 */
540         return y * 4 + 2;
541     y -= p;
542     /* pass 4 */
543     return y * 2 + 1;
544 }
545 
546 /* Decompress image pixels.
547  * Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table) or parse error. */
548 static int
read_image_data(gd_GIF * gif,int interlace)549 read_image_data(gd_GIF * gif, int interlace)
550 {
551     uint8_t sub_len, shift, byte;
552     int init_key_size, key_size, table_is_full = 0;
553     int frm_off, frm_size, str_len = 0, i, p, x, y;
554     uint16_t key, clear, stop;
555     int ret;
556     Table * table;
557     Entry entry = {0};
558     size_t start, end;
559 
560     f_gif_read(gif, &byte, 1);
561     key_size = (int) byte;
562     start = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
563     discard_sub_blocks(gif);
564     end = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
565     f_gif_seek(gif, start, LV_FS_SEEK_SET);
566     clear = 1 << key_size;
567     stop = clear + 1;
568     table = new_table(key_size);
569     key_size++;
570     init_key_size = key_size;
571     sub_len = shift = 0;
572     key = get_key(gif, key_size, &sub_len, &shift, &byte); /* clear code */
573     frm_off = 0;
574     ret = 0;
575     frm_size = gif->fw * gif->fh;
576     while(frm_off < frm_size) {
577         if(key == clear) {
578             key_size = init_key_size;
579             table->nentries = (1 << (key_size - 1)) + 2;
580             table_is_full = 0;
581         }
582         else if(!table_is_full) {
583             ret = add_entry(&table, str_len + 1, key, entry.suffix);
584             if(ret == -1) {
585                 lv_free(table);
586                 return -1;
587             }
588             if(table->nentries == 0x1000) {
589                 ret = 0;
590                 table_is_full = 1;
591             }
592         }
593         key = get_key(gif, key_size, &sub_len, &shift, &byte);
594         if(key == clear) continue;
595         if(key == stop || key == 0x1000) break;
596         if(ret == 1) key_size++;
597         entry = table->entries[key];
598         str_len = entry.length;
599 	if(frm_off + str_len >= frm_size){
600 		LV_LOG_WARN("LZW table token overflows the frame buffer");
601 		return -1;
602 	}
603         for(i = 0; i < str_len; i++) {
604             p = frm_off + entry.length - 1;
605             x = p % gif->fw;
606             y = p / gif->fw;
607             if(interlace)
608                 y = interlaced_line_index((int) gif->fh, y);
609             gif->frame[(gif->fy + y) * gif->width + gif->fx + x] = entry.suffix;
610             if(entry.prefix == 0xFFF)
611                 break;
612             else
613                 entry = table->entries[entry.prefix];
614         }
615         frm_off += str_len;
616         if(key < table->nentries - 1 && !table_is_full)
617             table->entries[table->nentries - 1].suffix = entry.suffix;
618     }
619     lv_free(table);
620     if(key == stop) f_gif_read(gif, &sub_len, 1);  /* Must be zero! */
621     f_gif_seek(gif, end, LV_FS_SEEK_SET);
622     return 0;
623 }
624 
625 #endif
626 
627 /* Read image.
628  * Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table) or parse error. */
629 static int
read_image(gd_GIF * gif)630 read_image(gd_GIF * gif)
631 {
632     uint8_t fisrz;
633     int interlace;
634 
635     /* Image Descriptor. */
636     gif->fx = read_num(gif);
637     gif->fy = read_num(gif);
638     gif->fw = read_num(gif);
639     gif->fh = read_num(gif);
640     if(gif->fx + (uint32_t)gif->fw > gif->width || gif->fy + (uint32_t)gif->fh > gif->height){
641         LV_LOG_WARN("Frame coordinates out of image bounds");
642         return -1;
643     }
644     f_gif_read(gif, &fisrz, 1);
645     interlace = fisrz & 0x40;
646     /* Ignore Sort Flag. */
647     /* Local Color Table? */
648     if(fisrz & 0x80) {
649         /* Read LCT */
650         gif->lct.size = 1 << ((fisrz & 0x07) + 1);
651         f_gif_read(gif, gif->lct.colors, 3 * gif->lct.size);
652         gif->palette = &gif->lct;
653     }
654     else
655         gif->palette = &gif->gct;
656     /* Image Data. */
657     return read_image_data(gif, interlace);
658 }
659 
660 static void
render_frame_rect(gd_GIF * gif,uint8_t * buffer)661 render_frame_rect(gd_GIF * gif, uint8_t * buffer)
662 {
663     int i = gif->fy * gif->width + gif->fx;
664 #ifdef GIFDEC_RENDER_FRAME
665     GIFDEC_RENDER_FRAME(&buffer[i * 4], gif->fw, gif->fh, gif->width,
666                         &gif->frame[i], gif->palette->colors,
667                         gif->gce.transparency ? gif->gce.tindex : 0x100);
668 #else
669     int j, k;
670     uint8_t index, * color;
671 
672     for(j = 0; j < gif->fh; j++) {
673         for(k = 0; k < gif->fw; k++) {
674             index = gif->frame[(gif->fy + j) * gif->width + gif->fx + k];
675             color = &gif->palette->colors[index * 3];
676             if(!gif->gce.transparency || index != gif->gce.tindex) {
677                 buffer[(i + k) * 4 + 0] = *(color + 2);
678                 buffer[(i + k) * 4 + 1] = *(color + 1);
679                 buffer[(i + k) * 4 + 2] = *(color + 0);
680                 buffer[(i + k) * 4 + 3] = 0xFF;
681             }
682         }
683         i += gif->width;
684     }
685 #endif
686 }
687 
688 static void
dispose(gd_GIF * gif)689 dispose(gd_GIF * gif)
690 {
691     int i;
692     uint8_t * bgcolor;
693     switch(gif->gce.disposal) {
694         case 2: /* Restore to background color. */
695             bgcolor = &gif->palette->colors[gif->bgindex * 3];
696 
697             uint8_t opa = 0xff;
698             if(gif->gce.transparency) opa = 0x00;
699 
700             i = gif->fy * gif->width + gif->fx;
701 #ifdef GIFDEC_FILL_BG
702             GIFDEC_FILL_BG(&(gif->canvas[i * 4]), gif->fw, gif->fh, gif->width, bgcolor, opa);
703 #else
704             int j, k;
705             for(j = 0; j < gif->fh; j++) {
706                 for(k = 0; k < gif->fw; k++) {
707                     gif->canvas[(i + k) * 4 + 0] = *(bgcolor + 2);
708                     gif->canvas[(i + k) * 4 + 1] = *(bgcolor + 1);
709                     gif->canvas[(i + k) * 4 + 2] = *(bgcolor + 0);
710                     gif->canvas[(i + k) * 4 + 3] = opa;
711                 }
712                 i += gif->width;
713             }
714 #endif
715             break;
716         case 3: /* Restore to previous, i.e., don't update canvas.*/
717             break;
718         default:
719             /* Add frame non-transparent pixels to canvas. */
720             render_frame_rect(gif, gif->canvas);
721     }
722 }
723 
724 /* Return 1 if got a frame; 0 if got GIF trailer; -1 if error. */
725 int
gd_get_frame(gd_GIF * gif)726 gd_get_frame(gd_GIF * gif)
727 {
728     char sep;
729 
730     dispose(gif);
731     f_gif_read(gif, &sep, 1);
732     while(sep != ',') {
733         if(sep == ';') {
734             f_gif_seek(gif, gif->anim_start, LV_FS_SEEK_SET);
735             if(gif->loop_count == 1 || gif->loop_count < 0) {
736                 return 0;
737             }
738             else if(gif->loop_count > 1) {
739                 gif->loop_count--;
740             }
741         }
742         else if(sep == '!')
743             read_ext(gif);
744         else return -1;
745         f_gif_read(gif, &sep, 1);
746     }
747     if(read_image(gif) == -1)
748         return -1;
749     return 1;
750 }
751 
752 void
gd_render_frame(gd_GIF * gif,uint8_t * buffer)753 gd_render_frame(gd_GIF * gif, uint8_t * buffer)
754 {
755     render_frame_rect(gif, buffer);
756 }
757 
758 void
gd_rewind(gd_GIF * gif)759 gd_rewind(gd_GIF * gif)
760 {
761     gif->loop_count = -1;
762     f_gif_seek(gif, gif->anim_start, LV_FS_SEEK_SET);
763 }
764 
765 void
gd_close_gif(gd_GIF * gif)766 gd_close_gif(gd_GIF * gif)
767 {
768     f_gif_close(gif);
769     lv_free(gif);
770 }
771 
f_gif_open(gd_GIF * gif,const void * path,bool is_file)772 static bool f_gif_open(gd_GIF * gif, const void * path, bool is_file)
773 {
774     gif->f_rw_p = 0;
775     gif->data = NULL;
776     gif->is_file = is_file;
777 
778     if(is_file) {
779         lv_fs_res_t res = lv_fs_open(&gif->fd, path, LV_FS_MODE_RD);
780         if(res != LV_FS_RES_OK) return false;
781         else return true;
782     }
783     else {
784         gif->data = path;
785         return true;
786     }
787 }
788 
f_gif_read(gd_GIF * gif,void * buf,size_t len)789 static void f_gif_read(gd_GIF * gif, void * buf, size_t len)
790 {
791     if(gif->is_file) {
792         lv_fs_read(&gif->fd, buf, len, NULL);
793     }
794     else {
795         memcpy(buf, &gif->data[gif->f_rw_p], len);
796         gif->f_rw_p += len;
797     }
798 }
799 
f_gif_seek(gd_GIF * gif,size_t pos,int k)800 static int f_gif_seek(gd_GIF * gif, size_t pos, int k)
801 {
802     if(gif->is_file) {
803         lv_fs_seek(&gif->fd, pos, k);
804         uint32_t x;
805         lv_fs_tell(&gif->fd, &x);
806         return x;
807     }
808     else {
809         if(k == LV_FS_SEEK_CUR) gif->f_rw_p += pos;
810         else if(k == LV_FS_SEEK_SET) gif->f_rw_p = pos;
811         return gif->f_rw_p;
812     }
813 }
814 
f_gif_close(gd_GIF * gif)815 static void f_gif_close(gd_GIF * gif)
816 {
817     if(gif->is_file) {
818         lv_fs_close(&gif->fd);
819     }
820 }
821 
822 #endif /*LV_USE_GIF*/
823