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*/