acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

gifdec.c (18515B)

      1 #include "gifdec.h"
      2 #include "../../../misc/lv_log.h"
      3 #include "../../../misc/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 static gd_GIF *  gif_open(gd_GIF * gif);
     27 static bool f_gif_open(gd_GIF * gif, const void * path, bool is_file);
     28 static void f_gif_read(gd_GIF * gif, void * buf, size_t len);
     29 static int f_gif_seek(gd_GIF * gif, size_t pos, int k);
     30 static void f_gif_close(gd_GIF * gif);
     31 
     32 static uint16_t
     33 read_num(gd_GIF * gif)
     34 {
     35     uint8_t bytes[2];
     36 
     37     f_gif_read(gif, bytes, 2);
     38     return bytes[0] + (((uint16_t) bytes[1]) << 8);
     39 }
     40 
     41 
     42 
     43 gd_GIF *
     44 gd_open_gif_file(const char *fname)
     45 {
     46     gd_GIF gif_base;
     47     memset(&gif_base, 0, sizeof(gif_base));
     48 
     49     bool res = f_gif_open(&gif_base, fname, true);
     50     if(!res) return NULL;
     51 
     52     return gif_open(&gif_base);
     53 }
     54 
     55 
     56 gd_GIF *
     57 gd_open_gif_data(const void *data)
     58 {
     59     gd_GIF gif_base;
     60     memset(&gif_base, 0, sizeof(gif_base));
     61 
     62     bool res = f_gif_open(&gif_base, data, false);
     63     if(!res) return NULL;
     64 
     65     return gif_open(&gif_base);
     66 }
     67 
     68 static gd_GIF * gif_open(gd_GIF * gif_base)
     69 {
     70     uint8_t sigver[3];
     71     uint16_t width, height, depth;
     72     uint8_t fdsz, bgidx, aspect;
     73     int i;
     74     uint8_t *bgcolor;
     75     int gct_sz;
     76     gd_GIF *gif = NULL;
     77 
     78     /* Header */
     79     f_gif_read(gif_base, sigver, 3);
     80     if (memcmp(sigver, "GIF", 3) != 0) {
     81         LV_LOG_WARN("invalid signature\n");
     82         goto fail;
     83     }
     84     /* Version */
     85     f_gif_read(gif_base, sigver, 3);
     86     if (memcmp(sigver, "89a", 3) != 0) {
     87         LV_LOG_WARN("invalid version\n");
     88         goto fail;
     89     }
     90     /* Width x Height */
     91     width  = read_num(gif_base);
     92     height = read_num(gif_base);
     93     /* FDSZ */
     94     f_gif_read(gif_base, &fdsz, 1);
     95     /* Presence of GCT */
     96     if (!(fdsz & 0x80)) {
     97         LV_LOG_WARN("no global color table\n");
     98         goto fail;
     99     }
    100     /* Color Space's Depth */
    101     depth = ((fdsz >> 4) & 7) + 1;
    102     /* Ignore Sort Flag. */
    103     /* GCT Size */
    104     gct_sz = 1 << ((fdsz & 0x07) + 1);
    105     /* Background Color Index */
    106     f_gif_read(gif_base, &bgidx, 1);
    107     /* Aspect Ratio */
    108     f_gif_read(gif_base, &aspect, 1);
    109     /* Create gd_GIF Structure. */
    110 #if LV_COLOR_DEPTH == 32
    111     gif = lv_mem_alloc(sizeof(gd_GIF) + 5 * width * height);
    112 #elif LV_COLOR_DEPTH == 16
    113     gif = lv_mem_alloc(sizeof(gd_GIF) + 4 * width * height);
    114 #elif LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
    115     gif = lv_mem_alloc(sizeof(gd_GIF) + 3 * width * height);
    116 #endif
    117 
    118     if (!gif) goto fail;
    119     memcpy(gif, gif_base, sizeof(gd_GIF));
    120     gif->width  = width;
    121     gif->height = height;
    122     gif->depth  = depth;
    123     /* Read GCT */
    124     gif->gct.size = gct_sz;
    125     f_gif_read(gif, gif->gct.colors, 3 * gif->gct.size);
    126     gif->palette = &gif->gct;
    127     gif->bgindex = bgidx;
    128     gif->canvas = (uint8_t *) &gif[1];
    129 #if LV_COLOR_DEPTH == 32
    130     gif->frame = &gif->canvas[4 * width * height];
    131 #elif LV_COLOR_DEPTH == 16
    132     gif->frame = &gif->canvas[3 * width * height];
    133 #elif LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
    134     gif->frame = &gif->canvas[2 * width * height];
    135 #endif
    136     if (gif->bgindex) {
    137         memset(gif->frame, gif->bgindex, gif->width * gif->height);
    138     }
    139     bgcolor = &gif->palette->colors[gif->bgindex*3];
    140 
    141     for (i = 0; i < gif->width * gif->height; i++) {
    142 #if LV_COLOR_DEPTH == 32
    143         gif->canvas[i*4 + 0] = *(bgcolor + 2);
    144         gif->canvas[i*4 + 1] = *(bgcolor + 1);
    145         gif->canvas[i*4 + 2] = *(bgcolor + 0);
    146         gif->canvas[i*4 + 3] = 0xff;
    147 #elif LV_COLOR_DEPTH == 16
    148         lv_color_t c = lv_color_make(*(bgcolor + 0), *(bgcolor + 1), *(bgcolor + 2));
    149         gif->canvas[i*3 + 0] = c.full & 0xff;
    150         gif->canvas[i*3 + 1] = (c.full >> 8) & 0xff;
    151         gif->canvas[i*3 + 2] = 0xff;
    152 #elif LV_COLOR_DEPTH == 8
    153         lv_color_t c = lv_color_make(*(bgcolor + 0), *(bgcolor + 1), *(bgcolor + 2));
    154         gif->canvas[i*2 + 0] = c.full;
    155         gif->canvas[i*2 + 1] = 0xff;
    156 #elif LV_COLOR_DEPTH == 1
    157         lv_color_t c = lv_color_make(*(bgcolor + 0), *(bgcolor + 1), *(bgcolor + 2));
    158         gif->canvas[i*2 + 0] = c.ch.red > 128 ? 1 : 0;
    159         gif->canvas[i*2 + 1] = 0xff;
    160 #endif
    161     }
    162     gif->anim_start = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
    163     goto ok;
    164 fail:
    165     f_gif_close(gif_base);
    166 ok:
    167     return gif;
    168 }
    169 
    170 static void
    171 discard_sub_blocks(gd_GIF *gif)
    172 {
    173     uint8_t size;
    174 
    175     do {
    176         f_gif_read(gif, &size, 1);
    177         f_gif_seek(gif, size, LV_FS_SEEK_CUR);
    178     } while (size);
    179 }
    180 
    181 static void
    182 read_plain_text_ext(gd_GIF *gif)
    183 {
    184     if (gif->plain_text) {
    185         uint16_t tx, ty, tw, th;
    186         uint8_t cw, ch, fg, bg;
    187         size_t sub_block;
    188         f_gif_seek(gif, 1, LV_FS_SEEK_CUR); /* block size = 12 */
    189         tx = read_num(gif);
    190         ty = read_num(gif);
    191         tw = read_num(gif);
    192         th = read_num(gif);
    193         f_gif_read(gif, &cw, 1);
    194         f_gif_read(gif, &ch, 1);
    195         f_gif_read(gif, &fg, 1);
    196         f_gif_read(gif, &bg, 1);
    197         sub_block = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
    198         gif->plain_text(gif, tx, ty, tw, th, cw, ch, fg, bg);
    199         f_gif_seek(gif, sub_block, LV_FS_SEEK_SET);
    200     } else {
    201         /* Discard plain text metadata. */
    202         f_gif_seek(gif, 13, LV_FS_SEEK_CUR);
    203     }
    204     /* Discard plain text sub-blocks. */
    205     discard_sub_blocks(gif);
    206 }
    207 
    208 static void
    209 read_graphic_control_ext(gd_GIF *gif)
    210 {
    211     uint8_t rdit;
    212 
    213     /* Discard block size (always 0x04). */
    214     f_gif_seek(gif, 1, LV_FS_SEEK_CUR);
    215     f_gif_read(gif, &rdit, 1);
    216     gif->gce.disposal = (rdit >> 2) & 3;
    217     gif->gce.input = rdit & 2;
    218     gif->gce.transparency = rdit & 1;
    219     gif->gce.delay = read_num(gif);
    220     f_gif_read(gif, &gif->gce.tindex, 1);
    221     /* Skip block terminator. */
    222     f_gif_seek(gif, 1, LV_FS_SEEK_CUR);
    223 }
    224 
    225 static void
    226 read_comment_ext(gd_GIF *gif)
    227 {
    228     if (gif->comment) {
    229         size_t sub_block = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
    230         gif->comment(gif);
    231         f_gif_seek(gif, sub_block, LV_FS_SEEK_SET);
    232     }
    233     /* Discard comment sub-blocks. */
    234     discard_sub_blocks(gif);
    235 }
    236 
    237 static void
    238 read_application_ext(gd_GIF *gif)
    239 {
    240     char app_id[8];
    241     char app_auth_code[3];
    242 
    243     /* Discard block size (always 0x0B). */
    244     f_gif_seek(gif, 1, LV_FS_SEEK_CUR);
    245     /* Application Identifier. */
    246     f_gif_read(gif, app_id, 8);
    247     /* Application Authentication Code. */
    248     f_gif_read(gif, app_auth_code, 3);
    249     if (!strncmp(app_id, "NETSCAPE", sizeof(app_id))) {
    250         /* Discard block size (0x03) and constant byte (0x01). */
    251         f_gif_seek(gif, 2, LV_FS_SEEK_CUR);
    252         gif->loop_count = read_num(gif);
    253         /* Skip block terminator. */
    254         f_gif_seek(gif, 1, LV_FS_SEEK_CUR);
    255     } else if (gif->application) {
    256         size_t sub_block = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
    257         gif->application(gif, app_id, app_auth_code);
    258         f_gif_seek(gif, sub_block, LV_FS_SEEK_SET);
    259         discard_sub_blocks(gif);
    260     } else {
    261         discard_sub_blocks(gif);
    262     }
    263 }
    264 
    265 static void
    266 read_ext(gd_GIF *gif)
    267 {
    268     uint8_t label;
    269 
    270     f_gif_read(gif, &label, 1);
    271     switch (label) {
    272     case 0x01:
    273         read_plain_text_ext(gif);
    274         break;
    275     case 0xF9:
    276         read_graphic_control_ext(gif);
    277         break;
    278     case 0xFE:
    279         read_comment_ext(gif);
    280         break;
    281     case 0xFF:
    282         read_application_ext(gif);
    283         break;
    284     default:
    285         LV_LOG_WARN("unknown extension: %02X\n", label);
    286     }
    287 }
    288 
    289 static Table *
    290 new_table(int key_size)
    291 {
    292     int key;
    293     int init_bulk = MAX(1 << (key_size + 1), 0x100);
    294     Table *table = lv_mem_alloc(sizeof(*table) + sizeof(Entry) * init_bulk);
    295     if (table) {
    296         table->bulk = init_bulk;
    297         table->nentries = (1 << key_size) + 2;
    298         table->entries = (Entry *) &table[1];
    299         for (key = 0; key < (1 << key_size); key++)
    300             table->entries[key] = (Entry) {1, 0xFFF, key};
    301     }
    302     return table;
    303 }
    304 
    305 /* Add table entry. Return value:
    306  *  0 on success
    307  *  +1 if key size must be incremented after this addition
    308  *  -1 if could not realloc table */
    309 static int
    310 add_entry(Table **tablep, uint16_t length, uint16_t prefix, uint8_t suffix)
    311 {
    312     Table *table = *tablep;
    313     if (table->nentries == table->bulk) {
    314         table->bulk *= 2;
    315         table = lv_mem_realloc(table, sizeof(*table) + sizeof(Entry) * table->bulk);
    316         if (!table) return -1;
    317         table->entries = (Entry *) &table[1];
    318         *tablep = table;
    319     }
    320     table->entries[table->nentries] = (Entry) {length, prefix, suffix};
    321     table->nentries++;
    322     if ((table->nentries & (table->nentries - 1)) == 0)
    323         return 1;
    324     return 0;
    325 }
    326 
    327 static uint16_t
    328 get_key(gd_GIF *gif, int key_size, uint8_t *sub_len, uint8_t *shift, uint8_t *byte)
    329 {
    330     int bits_read;
    331     int rpad;
    332     int frag_size;
    333     uint16_t key;
    334 
    335     key = 0;
    336     for (bits_read = 0; bits_read < key_size; bits_read += frag_size) {
    337         rpad = (*shift + bits_read) % 8;
    338         if (rpad == 0) {
    339             /* Update byte. */
    340             if (*sub_len == 0) {
    341                 f_gif_read(gif, sub_len, 1); /* Must be nonzero! */
    342                 if (*sub_len == 0) return 0x1000;
    343             }
    344             f_gif_read(gif, byte, 1);
    345             (*sub_len)--;
    346         }
    347         frag_size = MIN(key_size - bits_read, 8 - rpad);
    348         key |= ((uint16_t) ((*byte) >> rpad)) << bits_read;
    349     }
    350     /* Clear extra bits to the left. */
    351     key &= (1 << key_size) - 1;
    352     *shift = (*shift + key_size) % 8;
    353     return key;
    354 }
    355 
    356 /* Compute output index of y-th input line, in frame of height h. */
    357 static int
    358 interlaced_line_index(int h, int y)
    359 {
    360     int p; /* number of lines in current pass */
    361 
    362     p = (h - 1) / 8 + 1;
    363     if (y < p) /* pass 1 */
    364         return y * 8;
    365     y -= p;
    366     p = (h - 5) / 8 + 1;
    367     if (y < p) /* pass 2 */
    368         return y * 8 + 4;
    369     y -= p;
    370     p = (h - 3) / 4 + 1;
    371     if (y < p) /* pass 3 */
    372         return y * 4 + 2;
    373     y -= p;
    374     /* pass 4 */
    375     return y * 2 + 1;
    376 }
    377 
    378 /* Decompress image pixels.
    379  * Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table). */
    380 static int
    381 read_image_data(gd_GIF *gif, int interlace)
    382 {
    383     uint8_t sub_len, shift, byte;
    384     int init_key_size, key_size, table_is_full=0;
    385     int frm_off, frm_size, str_len=0, i, p, x, y;
    386     uint16_t key, clear, stop;
    387     int ret;
    388     Table *table;
    389     Entry entry = {0};
    390     size_t start, end;
    391 
    392     f_gif_read(gif, &byte, 1);
    393     key_size = (int) byte;
    394     start = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
    395     discard_sub_blocks(gif);
    396     end = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
    397     f_gif_seek(gif, start, LV_FS_SEEK_SET);
    398     clear = 1 << key_size;
    399     stop = clear + 1;
    400     table = new_table(key_size);
    401     key_size++;
    402     init_key_size = key_size;
    403     sub_len = shift = 0;
    404     key = get_key(gif, key_size, &sub_len, &shift, &byte); /* clear code */
    405     frm_off = 0;
    406     ret = 0;
    407     frm_size = gif->fw*gif->fh;
    408     while (frm_off < frm_size) {
    409         if (key == clear) {
    410             key_size = init_key_size;
    411             table->nentries = (1 << (key_size - 1)) + 2;
    412             table_is_full = 0;
    413         } else if (!table_is_full) {
    414             ret = add_entry(&table, str_len + 1, key, entry.suffix);
    415             if (ret == -1) {
    416                 lv_mem_free(table);
    417                 return -1;
    418             }
    419             if (table->nentries == 0x1000) {
    420                 ret = 0;
    421                 table_is_full = 1;
    422             }
    423         }
    424         key = get_key(gif, key_size, &sub_len, &shift, &byte);
    425         if (key == clear) continue;
    426         if (key == stop || key == 0x1000) break;
    427         if (ret == 1) key_size++;
    428         entry = table->entries[key];
    429         str_len = entry.length;
    430         for (i = 0; i < str_len; i++) {
    431             p = frm_off + entry.length - 1;
    432             x = p % gif->fw;
    433             y = p / gif->fw;
    434             if (interlace)
    435                 y = interlaced_line_index((int) gif->fh, y);
    436             gif->frame[(gif->fy + y) * gif->width + gif->fx + x] = entry.suffix;
    437             if (entry.prefix == 0xFFF)
    438                 break;
    439             else
    440                 entry = table->entries[entry.prefix];
    441         }
    442         frm_off += str_len;
    443         if (key < table->nentries - 1 && !table_is_full)
    444             table->entries[table->nentries - 1].suffix = entry.suffix;
    445     }
    446     lv_mem_free(table);
    447     if (key == stop) f_gif_read(gif, &sub_len, 1); /* Must be zero! */
    448     f_gif_seek(gif, end, LV_FS_SEEK_SET);
    449     return 0;
    450 }
    451 
    452 /* Read image.
    453  * Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table). */
    454 static int
    455 read_image(gd_GIF *gif)
    456 {
    457     uint8_t fisrz;
    458     int interlace;
    459 
    460     /* Image Descriptor. */
    461     gif->fx = read_num(gif);
    462     gif->fy = read_num(gif);
    463     gif->fw = read_num(gif);
    464     gif->fh = read_num(gif);
    465     f_gif_read(gif, &fisrz, 1);
    466     interlace = fisrz & 0x40;
    467     /* Ignore Sort Flag. */
    468     /* Local Color Table? */
    469     if (fisrz & 0x80) {
    470         /* Read LCT */
    471         gif->lct.size = 1 << ((fisrz & 0x07) + 1);
    472         f_gif_read(gif, gif->lct.colors, 3 * gif->lct.size);
    473         gif->palette = &gif->lct;
    474     } else
    475         gif->palette = &gif->gct;
    476     /* Image Data. */
    477     return read_image_data(gif, interlace);
    478 }
    479 
    480 static void
    481 render_frame_rect(gd_GIF *gif, uint8_t *buffer)
    482 {
    483     int i, j, k;
    484     uint8_t index, *color;
    485     i = gif->fy * gif->width + gif->fx;
    486     for (j = 0; j < gif->fh; j++) {
    487         for (k = 0; k < gif->fw; k++) {
    488             index = gif->frame[(gif->fy + j) * gif->width + gif->fx + k];
    489             color = &gif->palette->colors[index*3];
    490             if (!gif->gce.transparency || index != gif->gce.tindex) {
    491 #if LV_COLOR_DEPTH == 32
    492                 buffer[(i+k)*4 + 0] = *(color + 2);
    493                 buffer[(i+k)*4 + 1] = *(color + 1);
    494                 buffer[(i+k)*4 + 2] = *(color + 0);
    495                 buffer[(i+k)*4 + 3] = 0xFF;
    496 #elif LV_COLOR_DEPTH == 16
    497                 lv_color_t c = lv_color_make(*(color + 0), *(color + 1), *(color + 2));
    498                 buffer[(i+k)*3 + 0] = c.full & 0xff;
    499                 buffer[(i+k)*3 + 1] = (c.full >> 8) & 0xff;
    500                 buffer[(i+k)*3 + 2] = 0xff;
    501 #elif LV_COLOR_DEPTH == 8
    502                 lv_color_t c = lv_color_make(*(color + 0), *(color + 1), *(color + 2));
    503                 buffer[(i+k)*2 + 0] = c.full;
    504                 buffer[(i+k)*2 + 1] = 0xff;
    505 #elif LV_COLOR_DEPTH == 1
    506                 uint8_t b = (*(color + 0)) | (*(color + 1)) | (*(color + 2));
    507                 buffer[(i+k)*2 + 0] = b > 128 ? 1 : 0;
    508                 buffer[(i+k)*2 + 1] = 0xff;
    509 #endif
    510             }
    511         }
    512         i += gif->width;
    513     }
    514 }
    515 
    516 static void
    517 dispose(gd_GIF *gif)
    518 {
    519     int i, j, k;
    520     uint8_t *bgcolor;
    521     switch (gif->gce.disposal) {
    522     case 2: /* Restore to background color. */
    523         bgcolor = &gif->palette->colors[gif->bgindex*3];
    524 
    525         uint8_t opa = 0xff;
    526         if(gif->gce.transparency) opa = 0x00;
    527 
    528         i = gif->fy * gif->width + gif->fx;
    529         for (j = 0; j < gif->fh; j++) {
    530             for (k = 0; k < gif->fw; k++) {
    531 #if LV_COLOR_DEPTH == 32
    532                 gif->canvas[(i+k)*4 + 0] = *(bgcolor + 2);
    533                 gif->canvas[(i+k)*4 + 1] = *(bgcolor + 1);
    534                 gif->canvas[(i+k)*4 + 2] = *(bgcolor + 0);
    535                 gif->canvas[(i+k)*4 + 3] = opa;
    536 #elif LV_COLOR_DEPTH == 16
    537                 lv_color_t c = lv_color_make(*(bgcolor + 0), *(bgcolor + 1), *(bgcolor + 2));
    538                 gif->canvas[(i+k)*3 + 0] = c.full & 0xff;
    539                 gif->canvas[(i+k)*3 + 1] = (c.full >> 8) & 0xff;
    540                 gif->canvas[(i+k)*3 + 2] = opa;
    541 #elif LV_COLOR_DEPTH == 8
    542                 lv_color_t c = lv_color_make(*(bgcolor + 0), *(bgcolor + 1), *(bgcolor + 2));
    543                 gif->canvas[(i+k)*2 + 0] = c.full;
    544                 gif->canvas[(i+k)*2 + 1] = opa;
    545 #elif LV_COLOR_DEPTH == 1
    546                 uint8_t b = (*(bgcolor + 0)) | (*(bgcolor + 1)) | (*(bgcolor + 2));
    547                 gif->canvas[(i+k)*2 + 0] = b > 128 ? 1 : 0;
    548                 gif->canvas[(i+k)*2 + 1] = opa;
    549 #endif
    550             }
    551             i += gif->width;
    552         }
    553         break;
    554     case 3: /* Restore to previous, i.e., don't update canvas.*/
    555         break;
    556     default:
    557         /* Add frame non-transparent pixels to canvas. */
    558         render_frame_rect(gif, gif->canvas);
    559     }
    560 }
    561 
    562 /* Return 1 if got a frame; 0 if got GIF trailer; -1 if error. */
    563 int
    564 gd_get_frame(gd_GIF *gif)
    565 {
    566     char sep;
    567 
    568     dispose(gif);
    569     f_gif_read(gif, &sep, 1);
    570     while (sep != ',') {
    571         if (sep == ';')
    572             return 0;
    573         if (sep == '!')
    574             read_ext(gif);
    575         else return -1;
    576         f_gif_read(gif, &sep, 1);
    577     }
    578     if (read_image(gif) == -1)
    579         return -1;
    580     return 1;
    581 }
    582 
    583 void
    584 gd_render_frame(gd_GIF *gif, uint8_t *buffer)
    585 {
    586 //    uint32_t i;
    587 //    uint32_t j;
    588 //    for(i = 0, j = 0; i < gif->width * gif->height * 3; i+= 3, j+=4) {
    589 //        buffer[j + 0] = gif->canvas[i + 2];
    590 //        buffer[j + 1] = gif->canvas[i + 1];
    591 //        buffer[j + 2] = gif->canvas[i + 0];
    592 //        buffer[j + 3] = 0xFF;
    593 //    }
    594 //    memcpy(buffer, gif->canvas, gif->width * gif->height * 3);
    595     render_frame_rect(gif, buffer);
    596 }
    597 
    598 void
    599 gd_rewind(gd_GIF *gif)
    600 {
    601     f_gif_seek(gif, gif->anim_start, LV_FS_SEEK_SET);
    602 }
    603 
    604 void
    605 gd_close_gif(gd_GIF *gif)
    606 {
    607     f_gif_close(gif);
    608     lv_mem_free(gif);
    609 }
    610 
    611 static bool f_gif_open(gd_GIF * gif, const void * path, bool is_file)
    612 {
    613     gif->f_rw_p = 0;
    614     gif->data = NULL;
    615     gif->is_file = is_file;
    616 
    617     if(is_file) {
    618         lv_fs_res_t res = lv_fs_open(&gif->fd, path, LV_FS_MODE_RD);
    619         if(res != LV_FS_RES_OK) return false;
    620         else return true;
    621     } else {
    622         gif->data = path;
    623         return true;
    624     }
    625 }
    626 
    627 static void f_gif_read(gd_GIF * gif, void * buf, size_t len)
    628 {
    629     if(gif->is_file) {
    630         lv_fs_read(&gif->fd, buf, len, NULL);
    631     } else
    632     {
    633         memcpy(buf, &gif->data[gif->f_rw_p], len);
    634         gif->f_rw_p += len;
    635     }
    636 }
    637 
    638 static int f_gif_seek(gd_GIF * gif, size_t pos, int k)
    639 {
    640     if(gif->is_file) {
    641         lv_fs_seek(&gif->fd, pos, k);
    642         uint32_t x;
    643         lv_fs_tell(&gif->fd, &x);
    644         return x;
    645     } else {
    646         if(k == LV_FS_SEEK_CUR) gif->f_rw_p += pos;
    647         else if(k == LV_FS_SEEK_SET) gif->f_rw_p = pos;
    648         return gif->f_rw_p;
    649     }
    650 }
    651 
    652 static void f_gif_close(gd_GIF * gif)
    653 {
    654     if(gif->is_file) {
    655         lv_fs_close(&gif->fd);
    656     }
    657 }
    658 
    659 #endif /*LV_USE_GIF*/