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