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