mirror of
https://github.com/lsdlsd88/JC4827W543.git
synced 2026-05-05 00:20:13 +01:00
initial commit
This commit is contained in:
@@ -0,0 +1,657 @@
|
||||
/*******************************************************************************
|
||||
* GIFDEC Wrapper Class
|
||||
*
|
||||
* Rewrite from: https://github.com/BasementCat/arduino-tft-gif
|
||||
******************************************************************************/
|
||||
#ifndef _GIFCLASS_H_
|
||||
#define _GIFCLASS_H_
|
||||
|
||||
/* Wio Terminal */
|
||||
#if defined(ARDUINO_ARCH_SAMD) && defined(SEEED_GROVE_UI_WIRELESS)
|
||||
#include <Seeed_FS.h>
|
||||
#elif defined(ESP32) || defined(ESP8266)
|
||||
#include <FS.h>
|
||||
#else
|
||||
#include <SD.h>
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(A, B) ((A) < (B) ? (A) : (B))
|
||||
#endif
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(A, B) ((A) > (B) ? (A) : (B))
|
||||
#endif
|
||||
|
||||
#define GIF_BUF_SIZE 1024
|
||||
|
||||
typedef struct gd_Palette
|
||||
{
|
||||
int16_t len;
|
||||
uint16_t colors[256];
|
||||
} gd_Palette;
|
||||
|
||||
typedef struct gd_GCE
|
||||
{
|
||||
uint16_t delay;
|
||||
uint8_t tindex;
|
||||
uint8_t disposal;
|
||||
uint8_t input;
|
||||
uint8_t transparency;
|
||||
} gd_GCE;
|
||||
|
||||
typedef struct gd_Entry
|
||||
{
|
||||
int32_t len;
|
||||
uint16_t prefix;
|
||||
uint8_t suffix;
|
||||
} gd_Entry;
|
||||
|
||||
typedef struct gd_Table
|
||||
{
|
||||
int16_t bulk;
|
||||
int16_t nentries;
|
||||
gd_Entry *entries;
|
||||
} gd_Table;
|
||||
|
||||
typedef struct gd_GIF
|
||||
{
|
||||
File *fd;
|
||||
off_t anim_start;
|
||||
uint16_t width, height;
|
||||
uint16_t depth;
|
||||
uint16_t loop_count;
|
||||
gd_GCE gce;
|
||||
gd_Palette *palette;
|
||||
gd_Palette lct, gct;
|
||||
void (*plain_text)(
|
||||
struct gd_GIF *gif, uint16_t tx, uint16_t ty,
|
||||
uint16_t tw, uint16_t th, uint8_t cw, uint8_t ch,
|
||||
uint8_t fg, uint8_t bg);
|
||||
void (*comment)(struct gd_GIF *gif);
|
||||
void (*application)(struct gd_GIF *gif, char id[8], char auth[3]);
|
||||
uint16_t fx, fy, fw, fh;
|
||||
uint8_t bgindex;
|
||||
gd_Table *table;
|
||||
bool read_first_frame;
|
||||
} gd_GIF;
|
||||
|
||||
class GifClass
|
||||
{
|
||||
public:
|
||||
gd_GIF *gd_open_gif(File *fd)
|
||||
{
|
||||
uint8_t sigver[3];
|
||||
uint16_t width, height, depth;
|
||||
uint8_t fdsz, bgidx, aspect;
|
||||
int16_t gct_sz;
|
||||
gd_GIF *gif;
|
||||
|
||||
// init global variables
|
||||
gif_buf_last_idx = GIF_BUF_SIZE;
|
||||
gif_buf_idx = gif_buf_last_idx; // no buffer yet
|
||||
file_pos = 0;
|
||||
|
||||
/* Header */
|
||||
gif_buf_read(fd, sigver, 3);
|
||||
if (memcmp(sigver, "GIF", 3) != 0)
|
||||
{
|
||||
Serial.println(F("invalid signature"));
|
||||
return NULL;
|
||||
}
|
||||
/* Version */
|
||||
gif_buf_read(fd, sigver, 3);
|
||||
if (memcmp(sigver, "89a", 3) != 0)
|
||||
{
|
||||
Serial.println(F("invalid version"));
|
||||
return NULL;
|
||||
}
|
||||
/* Width x Height */
|
||||
width = gif_buf_read16(fd);
|
||||
height = gif_buf_read16(fd);
|
||||
/* FDSZ */
|
||||
gif_buf_read(fd, &fdsz, 1);
|
||||
/* Presence of GCT */
|
||||
if (!(fdsz & 0x80))
|
||||
{
|
||||
Serial.println(F("no global color table"));
|
||||
return NULL;
|
||||
}
|
||||
/* Color Space's Depth */
|
||||
depth = ((fdsz >> 4) & 7) + 1;
|
||||
/* Ignore Sort Flag. */
|
||||
/* GCT Size */
|
||||
gct_sz = 1 << ((fdsz & 0x07) + 1);
|
||||
/* Background Color Index */
|
||||
gif_buf_read(fd, &bgidx, 1);
|
||||
/* Aspect Ratio */
|
||||
gif_buf_read(fd, &aspect, 1);
|
||||
/* Create gd_GIF Structure. */
|
||||
gif = (gd_GIF *)calloc(1, sizeof(*gif));
|
||||
gif->fd = fd;
|
||||
gif->width = width;
|
||||
gif->height = height;
|
||||
gif->depth = depth;
|
||||
/* Read GCT */
|
||||
read_palette(fd, &gif->gct, gct_sz);
|
||||
gif->palette = &gif->gct;
|
||||
gif->bgindex = bgidx;
|
||||
gif->anim_start = file_pos; // fd->position();
|
||||
gif->table = new_table();
|
||||
gif->read_first_frame = false;
|
||||
return gif;
|
||||
}
|
||||
|
||||
/* Return 1 if got a frame; 0 if got GIF trailer; -1 if error. */
|
||||
int32_t gd_get_frame(gd_GIF *gif, uint8_t *frame)
|
||||
{
|
||||
char sep;
|
||||
|
||||
while (1)
|
||||
{
|
||||
gif_buf_read(gif->fd, (uint8_t *)&sep, 1);
|
||||
if (sep == 0)
|
||||
{
|
||||
gif_buf_read(gif->fd, (uint8_t *)&sep, 1);
|
||||
}
|
||||
if (sep == ',')
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (sep == ';')
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (sep == '!')
|
||||
{
|
||||
read_ext(gif);
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.print(F("Read sep: ["));
|
||||
Serial.print(sep);
|
||||
Serial.println(F("].\n"));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// Serial.println("Do read image");
|
||||
if (read_image(gif, frame) == -1)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void gd_rewind(gd_GIF *gif)
|
||||
{
|
||||
#if defined(ESP32) || defined(ESP8266)
|
||||
gif->fd->seek(gif->anim_start, SeekSet);
|
||||
#else
|
||||
gif->fd->seek(gif->anim_start);
|
||||
#endif
|
||||
file_pos = gif->anim_start;
|
||||
gif_buf_idx = gif_buf_last_idx; // reset buffer
|
||||
}
|
||||
|
||||
void gd_close_gif(gd_GIF *gif)
|
||||
{
|
||||
gif->fd->close();
|
||||
free(gif->table);
|
||||
free(gif);
|
||||
}
|
||||
|
||||
private:
|
||||
bool gif_buf_seek(File *fd, int16_t len)
|
||||
{
|
||||
if (len > (gif_buf_last_idx - gif_buf_idx))
|
||||
{
|
||||
#if defined(ESP32) || defined(ESP8266)
|
||||
// fd->seek(len - (gif_buf_last_idx - gif_buf_idx), SeekCur);
|
||||
fd->seek(file_pos + len - (gif_buf_last_idx - gif_buf_idx), SeekSet);
|
||||
#else
|
||||
fd->seek(file_pos + len - (gif_buf_last_idx - gif_buf_idx));
|
||||
#endif
|
||||
|
||||
gif_buf_idx = gif_buf_last_idx;
|
||||
}
|
||||
else
|
||||
{
|
||||
gif_buf_idx += len;
|
||||
}
|
||||
file_pos += len;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int16_t gif_buf_read(File *fd, uint8_t *dest, int16_t len)
|
||||
{
|
||||
while (len--)
|
||||
{
|
||||
if (gif_buf_idx == gif_buf_last_idx)
|
||||
{
|
||||
gif_buf_last_idx = fd->read(gif_buf, GIF_BUF_SIZE);
|
||||
gif_buf_idx = 0;
|
||||
}
|
||||
|
||||
file_pos++;
|
||||
*(dest++) = gif_buf[gif_buf_idx++];
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
uint8_t gif_buf_read(File *fd)
|
||||
{
|
||||
if (gif_buf_idx == gif_buf_last_idx)
|
||||
{
|
||||
gif_buf_last_idx = fd->read(gif_buf, GIF_BUF_SIZE);
|
||||
gif_buf_idx = 0;
|
||||
}
|
||||
|
||||
file_pos++;
|
||||
return gif_buf[gif_buf_idx++];
|
||||
}
|
||||
|
||||
uint16_t gif_buf_read16(File *fd)
|
||||
{
|
||||
return gif_buf_read(fd) + (((uint16_t)gif_buf_read(fd)) << 8);
|
||||
}
|
||||
|
||||
void read_palette(File *fd, gd_Palette *dest, int16_t num_colors)
|
||||
{
|
||||
uint8_t r, g, b;
|
||||
dest->len = num_colors;
|
||||
for (int16_t i = 0; i < num_colors; i++)
|
||||
{
|
||||
r = gif_buf_read(fd);
|
||||
g = gif_buf_read(fd);
|
||||
b = gif_buf_read(fd);
|
||||
dest->colors[i] = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3);
|
||||
}
|
||||
}
|
||||
|
||||
void discard_sub_blocks(gd_GIF *gif)
|
||||
{
|
||||
uint8_t len;
|
||||
|
||||
do
|
||||
{
|
||||
gif_buf_read(gif->fd, &len, 1);
|
||||
gif_buf_seek(gif->fd, len);
|
||||
} while (len);
|
||||
}
|
||||
|
||||
void read_plain_text_ext(gd_GIF *gif)
|
||||
{
|
||||
if (gif->plain_text)
|
||||
{
|
||||
uint16_t tx, ty, tw, th;
|
||||
uint8_t cw, ch, fg, bg;
|
||||
gif_buf_seek(gif->fd, 1); /* block size = 12 */
|
||||
tx = gif_buf_read16(gif->fd);
|
||||
ty = gif_buf_read16(gif->fd);
|
||||
tw = gif_buf_read16(gif->fd);
|
||||
th = gif_buf_read16(gif->fd);
|
||||
cw = gif_buf_read(gif->fd);
|
||||
ch = gif_buf_read(gif->fd);
|
||||
fg = gif_buf_read(gif->fd);
|
||||
bg = gif_buf_read(gif->fd);
|
||||
gif->plain_text(gif, tx, ty, tw, th, cw, ch, fg, bg);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Discard plain text metadata. */
|
||||
gif_buf_seek(gif->fd, 13);
|
||||
}
|
||||
/* Discard plain text sub-blocks. */
|
||||
discard_sub_blocks(gif);
|
||||
}
|
||||
|
||||
void read_graphic_control_ext(gd_GIF *gif)
|
||||
{
|
||||
uint8_t rdit;
|
||||
|
||||
/* Discard block size (always 0x04). */
|
||||
gif_buf_seek(gif->fd, 1);
|
||||
gif_buf_read(gif->fd, &rdit, 1);
|
||||
gif->gce.disposal = (rdit >> 2) & 3;
|
||||
gif->gce.input = rdit & 2;
|
||||
gif->gce.transparency = rdit & 1;
|
||||
gif->gce.delay = gif_buf_read16(gif->fd);
|
||||
gif_buf_read(gif->fd, &gif->gce.tindex, 1);
|
||||
/* Skip block terminator. */
|
||||
gif_buf_seek(gif->fd, 1);
|
||||
}
|
||||
|
||||
void read_comment_ext(gd_GIF *gif)
|
||||
{
|
||||
if (gif->comment)
|
||||
{
|
||||
gif->comment(gif);
|
||||
}
|
||||
/* Discard comment sub-blocks. */
|
||||
discard_sub_blocks(gif);
|
||||
}
|
||||
|
||||
void read_application_ext(gd_GIF *gif)
|
||||
{
|
||||
char app_id[8];
|
||||
char app_auth_code[3];
|
||||
|
||||
/* Discard block size (always 0x0B). */
|
||||
gif_buf_seek(gif->fd, 1);
|
||||
/* Application Identifier. */
|
||||
gif_buf_read(gif->fd, (uint8_t *)app_id, 8);
|
||||
/* Application Authentication Code. */
|
||||
gif_buf_read(gif->fd, (uint8_t *)app_auth_code, 3);
|
||||
if (!strncmp(app_id, "NETSCAPE", sizeof(app_id)))
|
||||
{
|
||||
/* Discard block size (0x03) and constant byte (0x01). */
|
||||
gif_buf_seek(gif->fd, 2);
|
||||
gif->loop_count = gif_buf_read16(gif->fd);
|
||||
/* Skip block terminator. */
|
||||
gif_buf_seek(gif->fd, 1);
|
||||
}
|
||||
else if (gif->application)
|
||||
{
|
||||
gif->application(gif, app_id, app_auth_code);
|
||||
discard_sub_blocks(gif);
|
||||
}
|
||||
else
|
||||
{
|
||||
discard_sub_blocks(gif);
|
||||
}
|
||||
}
|
||||
|
||||
void read_ext(gd_GIF *gif)
|
||||
{
|
||||
uint8_t label;
|
||||
|
||||
gif_buf_read(gif->fd, &label, 1);
|
||||
switch (label)
|
||||
{
|
||||
case 0x01:
|
||||
read_plain_text_ext(gif);
|
||||
break;
|
||||
case 0xF9:
|
||||
read_graphic_control_ext(gif);
|
||||
break;
|
||||
case 0xFE:
|
||||
read_comment_ext(gif);
|
||||
break;
|
||||
case 0xFF:
|
||||
read_application_ext(gif);
|
||||
break;
|
||||
default:
|
||||
Serial.print("unknown extension: ");
|
||||
Serial.println(label, HEX);
|
||||
}
|
||||
}
|
||||
|
||||
gd_Table *new_table()
|
||||
{
|
||||
// uint16_t key;
|
||||
// int16_t init_bulk = MAX(1 << (key_size + 1), 0x100);
|
||||
// Table *table = (Table*) malloc(sizeof(*table) + sizeof(Entry) * init_bulk);
|
||||
// if (table) {
|
||||
// table->bulk = init_bulk;
|
||||
// table->nentries = (1 << key_size) + 2;
|
||||
// table->entries = (Entry *) &table[1];
|
||||
// for (key = 0; key < (1 << key_size); key++)
|
||||
// table->entries[key] = (Entry) {1, 0xFFF, key};
|
||||
// }
|
||||
// return table;
|
||||
int32_t s = sizeof(gd_Table) + (sizeof(gd_Entry) * 4096);
|
||||
gd_Table *table = (gd_Table *)malloc(s);
|
||||
if (table)
|
||||
{
|
||||
Serial.print(F("new_table() malloc: "));
|
||||
Serial.println(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.print(F("new_table() malloc failed: "));
|
||||
Serial.println(s);
|
||||
}
|
||||
table->entries = (gd_Entry *)&table[1];
|
||||
return table;
|
||||
}
|
||||
|
||||
void reset_table(gd_Table *table, uint16_t key_size)
|
||||
{
|
||||
table->nentries = (1 << key_size) + 2;
|
||||
for (uint16_t key = 0; key < (1 << key_size); key++)
|
||||
{
|
||||
table->entries[key] = (gd_Entry){1, 0xFFF, (uint8_t)key};
|
||||
}
|
||||
}
|
||||
|
||||
/* Add table entry. Return value:
|
||||
* 0 on success
|
||||
* +1 if key size must be incremented after this addition
|
||||
* -1 if could not realloc table */
|
||||
int32_t add_entry(gd_Table *table, int32_t len, uint16_t prefix, uint8_t suffix)
|
||||
{
|
||||
// Table *table = *tablep;
|
||||
// if (table->nentries == table->bulk) {
|
||||
// table->bulk *= 2;
|
||||
// table = (Table*) realloc(table, sizeof(*table) + sizeof(Entry) * table->bulk);
|
||||
// if (!table) return -1;
|
||||
// table->entries = (Entry *) &table[1];
|
||||
// *tablep = table;
|
||||
// }
|
||||
table->entries[table->nentries] = (gd_Entry){len, prefix, suffix};
|
||||
table->nentries++;
|
||||
if ((table->nentries & (table->nentries - 1)) == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t get_key(gd_GIF *gif, uint16_t key_size, uint8_t *sub_len, uint8_t *shift, uint8_t *byte)
|
||||
{
|
||||
int16_t bits_read;
|
||||
int16_t rpad;
|
||||
int16_t frag_size;
|
||||
uint16_t key;
|
||||
|
||||
key = 0;
|
||||
for (bits_read = 0; bits_read < key_size; bits_read += frag_size)
|
||||
{
|
||||
rpad = (*shift + bits_read) % 8;
|
||||
if (rpad == 0)
|
||||
{
|
||||
/* Update byte. */
|
||||
if (*sub_len == 0)
|
||||
gif_buf_read(gif->fd, sub_len, 1); /* Must be nonzero! */
|
||||
gif_buf_read(gif->fd, byte, 1);
|
||||
(*sub_len)--;
|
||||
}
|
||||
frag_size = MIN(key_size - bits_read, 8 - rpad);
|
||||
key |= ((uint16_t)((*byte) >> rpad)) << bits_read;
|
||||
}
|
||||
/* Clear extra bits to the left. */
|
||||
key &= (1 << key_size) - 1;
|
||||
*shift = (*shift + key_size) % 8;
|
||||
return key;
|
||||
}
|
||||
|
||||
/* Compute output index of y-th input line, in frame of height h. */
|
||||
int16_t interlaced_line_index(int16_t h, int16_t y)
|
||||
{
|
||||
int16_t p; /* number of lines in current pass */
|
||||
|
||||
p = (h - 1) / 8 + 1;
|
||||
if (y < p) /* pass 1 */
|
||||
return y * 8;
|
||||
y -= p;
|
||||
p = (h - 5) / 8 + 1;
|
||||
if (y < p) /* pass 2 */
|
||||
return y * 8 + 4;
|
||||
y -= p;
|
||||
p = (h - 3) / 4 + 1;
|
||||
if (y < p) /* pass 3 */
|
||||
return y * 4 + 2;
|
||||
y -= p;
|
||||
/* pass 4 */
|
||||
return y * 2 + 1;
|
||||
}
|
||||
|
||||
/* Decompress image pixels.
|
||||
* Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table). */
|
||||
int8_t read_image_data(gd_GIF *gif, int16_t interlace, uint8_t *frame)
|
||||
{
|
||||
uint8_t sub_len, shift, byte, table_is_full = 0;
|
||||
uint16_t init_key_size, key_size;
|
||||
int32_t frm_off, str_len = 0, p, x, y;
|
||||
uint16_t key, clear, stop;
|
||||
int32_t ret;
|
||||
gd_Entry entry = {0, 0, 0};
|
||||
|
||||
// Serial.println("Read key size");
|
||||
gif_buf_read(gif->fd, &byte, 1);
|
||||
key_size = (uint16_t)byte;
|
||||
// Serial.println("Set pos, discard sub blocks");
|
||||
// start = gif->fd->position();
|
||||
// discard_sub_blocks(gif);
|
||||
// end = gif->fd->position();
|
||||
// gif_buf_seek(gif->fd, start, SeekSet);
|
||||
clear = 1 << key_size;
|
||||
stop = clear + 1;
|
||||
// Serial.println("New LZW table");
|
||||
// table = new_table(key_size);
|
||||
reset_table(gif->table, key_size);
|
||||
key_size++;
|
||||
init_key_size = key_size;
|
||||
sub_len = shift = 0;
|
||||
// Serial.println("Get init key");
|
||||
key = get_key(gif, key_size, &sub_len, &shift, &byte); /* clear code */
|
||||
frm_off = 0;
|
||||
ret = 0;
|
||||
while (1)
|
||||
{
|
||||
if (key == clear)
|
||||
{
|
||||
// Serial.println("Clear key, reset nentries");
|
||||
key_size = init_key_size;
|
||||
gif->table->nentries = (1 << (key_size - 1)) + 2;
|
||||
table_is_full = 0;
|
||||
}
|
||||
else if (!table_is_full)
|
||||
{
|
||||
// Serial.println("Add entry to table");
|
||||
ret = add_entry(gif->table, str_len + 1, key, entry.suffix);
|
||||
// if (ret == -1) {
|
||||
// // Serial.println("Table entry add failure");
|
||||
// free(table);
|
||||
// return -1;
|
||||
// }
|
||||
if (gif->table->nentries == 0x1000)
|
||||
{
|
||||
// Serial.println("Table is full");
|
||||
ret = 0;
|
||||
table_is_full = 1;
|
||||
}
|
||||
}
|
||||
// Serial.println("Get key");
|
||||
key = get_key(gif, key_size, &sub_len, &shift, &byte);
|
||||
if (key == clear)
|
||||
continue;
|
||||
if (key == stop)
|
||||
break;
|
||||
if (ret == 1)
|
||||
key_size++;
|
||||
entry = gif->table->entries[key];
|
||||
str_len = entry.len;
|
||||
uint8_t tindex = gif->gce.tindex;
|
||||
// Serial.println("Interpret key");
|
||||
while (1)
|
||||
{
|
||||
p = frm_off + entry.len - 1;
|
||||
x = p % gif->fw;
|
||||
y = p / gif->fw;
|
||||
if (interlace)
|
||||
{
|
||||
y = interlaced_line_index((int16_t)gif->fh, y);
|
||||
}
|
||||
if ((!gif->read_first_frame) || (tindex != entry.suffix))
|
||||
{
|
||||
frame[(gif->fy + y) * gif->width + gif->fx + x] = entry.suffix;
|
||||
}
|
||||
if (entry.prefix == 0xFFF)
|
||||
break;
|
||||
else
|
||||
entry = gif->table->entries[entry.prefix];
|
||||
}
|
||||
frm_off += str_len;
|
||||
if (key < gif->table->nentries - 1 && !table_is_full)
|
||||
gif->table->entries[gif->table->nentries - 1].suffix = entry.suffix;
|
||||
}
|
||||
// Serial.println("Done w/ img data, free table and seek to end");
|
||||
// free(table);
|
||||
gif_buf_read(gif->fd, &sub_len, 1); /* Must be zero! */
|
||||
// gif_buf_seek(gif->fd, end, SeekSet);
|
||||
|
||||
gif->read_first_frame = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read image.
|
||||
* Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table). */
|
||||
int8_t read_image(gd_GIF *gif, uint8_t *frame)
|
||||
{
|
||||
uint8_t fisrz;
|
||||
int16_t interlace;
|
||||
|
||||
/* Image Descriptor. */
|
||||
// Serial.println("Read image descriptor");
|
||||
gif->fx = gif_buf_read16(gif->fd);
|
||||
gif->fy = gif_buf_read16(gif->fd);
|
||||
gif->fw = gif_buf_read16(gif->fd);
|
||||
gif->fh = gif_buf_read16(gif->fd);
|
||||
// Serial.println("Read fisrz?");
|
||||
gif_buf_read(gif->fd, &fisrz, 1);
|
||||
interlace = fisrz & 0x40;
|
||||
/* Ignore Sort Flag. */
|
||||
/* Local Color Table? */
|
||||
if (fisrz & 0x80)
|
||||
{
|
||||
/* Read LCT */
|
||||
// Serial.println("Read LCT");
|
||||
read_palette(gif->fd, &gif->lct, 1 << ((fisrz & 0x07) + 1));
|
||||
gif->palette = &gif->lct;
|
||||
}
|
||||
else
|
||||
{
|
||||
gif->palette = &gif->gct;
|
||||
}
|
||||
/* Image Data. */
|
||||
// Serial.println("Read image data");
|
||||
return read_image_data(gif, interlace, frame);
|
||||
}
|
||||
|
||||
void render_frame_rect(gd_GIF *gif, uint16_t *buffer, uint8_t *frame)
|
||||
{
|
||||
int16_t i, j, k;
|
||||
uint8_t index;
|
||||
i = gif->fy * gif->width + gif->fx;
|
||||
for (j = 0; j < gif->fh; j++)
|
||||
{
|
||||
for (k = 0; k < gif->fw; k++)
|
||||
{
|
||||
index = frame[(gif->fy + j) * gif->width + gif->fx + k];
|
||||
// color = &gif->palette->colors[index*2];
|
||||
if ((!gif->gce.transparency) || (index != gif->gce.tindex))
|
||||
{
|
||||
buffer[(i + k)] = gif->palette->colors[index];
|
||||
}
|
||||
// memcpy(&buffer[(i+k)*2], color, 2);
|
||||
}
|
||||
i += gif->width;
|
||||
}
|
||||
}
|
||||
|
||||
int16_t gif_buf_last_idx, gif_buf_idx, file_pos;
|
||||
uint8_t gif_buf[GIF_BUF_SIZE];
|
||||
};
|
||||
|
||||
#endif /* _GIFCLASS_H_ */
|
||||
@@ -0,0 +1,92 @@
|
||||
class IndexedSprite
|
||||
{
|
||||
public:
|
||||
IndexedSprite(int16_t x, uint16_t y, uint8_t *bitmap, uint16_t *palette, int16_t w, int16_t h, int16_t x_skip, bool loop, size_t frames, int16_t speed_divider)
|
||||
: _x(x), _y(y), _bitmap(bitmap), _palette(palette), _w(w), _h(h), _x_skip(x_skip), _loop(loop), _frames(frames), _speed_divider(speed_divider)
|
||||
{
|
||||
}
|
||||
IndexedSprite(int16_t x, uint16_t y, uint8_t *bitmap, uint16_t *palette, int16_t w, int16_t h, int16_t x_skip, bool loop, size_t frames, int16_t speed_divider, uint8_t chroma_key)
|
||||
: _x(x), _y(y), _bitmap(bitmap), _palette(palette), _w(w), _h(h), _x_skip(x_skip), _loop(loop), _frames(frames), _speed_divider(speed_divider), _chroma_key(chroma_key)
|
||||
{
|
||||
_has_chroma_key = true;
|
||||
}
|
||||
|
||||
void h_scroll(int16_t v)
|
||||
{
|
||||
h_scroll(v, _w);
|
||||
}
|
||||
|
||||
void h_scroll(int16_t v, int16_t bound)
|
||||
{
|
||||
_curr_sub_scroll += v;
|
||||
while (_curr_sub_scroll > _speed_divider)
|
||||
{
|
||||
++_x;
|
||||
_curr_sub_scroll -= _speed_divider;
|
||||
}
|
||||
while (_curr_sub_scroll < -_speed_divider)
|
||||
{
|
||||
--_x;
|
||||
_curr_sub_scroll += _speed_divider;
|
||||
}
|
||||
if (_x < -(_w))
|
||||
{
|
||||
_x = bound;
|
||||
}
|
||||
else if (_x > bound)
|
||||
{
|
||||
_x = -(_w);
|
||||
}
|
||||
}
|
||||
|
||||
void next_frame()
|
||||
{
|
||||
_curr_sub_frame++;
|
||||
if (_curr_sub_frame > _speed_divider)
|
||||
{
|
||||
++_curr_frame;
|
||||
_curr_sub_frame -= _speed_divider;
|
||||
}
|
||||
if (_curr_frame >= _frames)
|
||||
{
|
||||
_curr_frame = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void draw(Arduino_GFX *gfx)
|
||||
{
|
||||
if (_has_chroma_key)
|
||||
{
|
||||
gfx->drawIndexedBitmap(_x, _y, _bitmap + (_curr_frame * _w), _palette, _chroma_key, _w, _h, _x_skip);
|
||||
if (_loop)
|
||||
{
|
||||
gfx->drawIndexedBitmap((_x < 0) ? (_x + _w) : (_x - _w), _y, _bitmap + (_curr_frame * _w), _palette, _chroma_key, _w, _h, _x_skip);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gfx->drawIndexedBitmap(_x, _y, _bitmap + (_curr_frame * _w), _palette, _w, _h, _x_skip);
|
||||
if (_loop)
|
||||
{
|
||||
gfx->drawIndexedBitmap((_x < 0) ? (_x + _w) : (_x - _w), _y, _bitmap + (_curr_frame * _w), _palette, _w, _h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int16_t _x;
|
||||
int16_t _y;
|
||||
uint16_t *_palette;
|
||||
uint8_t *_bitmap;
|
||||
int16_t _w;
|
||||
int16_t _h;
|
||||
int16_t _x_skip;
|
||||
bool _loop;
|
||||
uint8_t _chroma_key;
|
||||
bool _has_chroma_key = false;
|
||||
size_t _frames;
|
||||
int16_t _speed_divider;
|
||||
size_t _curr_frame = 0;
|
||||
int16_t _curr_sub_scroll = 0;
|
||||
int16_t _curr_sub_frame = 0;
|
||||
};
|
||||
@@ -0,0 +1,270 @@
|
||||
/*******************************************************************************
|
||||
* Sprite GIF Demo
|
||||
* This is a sprite demo using a static GIF as a master image
|
||||
* Image Source:
|
||||
* https://www.freepik.com/free-vector/urban-life-drawing_727890.htm#query=city%20road
|
||||
* https://giphy.com/gifs/car-carro-nQaMsylXcTIRNorQLJ
|
||||
*
|
||||
* Setup steps:
|
||||
* 1. Change your LCD parameters in Arduino_GFX setting
|
||||
* 2. Upload GIF file
|
||||
* FFat (ESP32):
|
||||
* upload FFat (FatFS) data with ESP32 Sketch Data Upload:
|
||||
* ESP32: https://github.com/lorol/arduino-esp32fs-plugin
|
||||
* LittleFS (ESP32 / ESP8266 / Pico):
|
||||
* upload LittleFS data with ESP8266 LittleFS Data Upload:
|
||||
* ESP32: https://github.com/lorol/arduino-esp32fs-plugin
|
||||
* ESP8266: https://github.com/earlephilhower/arduino-esp8266littlefs-plugin
|
||||
* Pico: https://github.com/earlephilhower/arduino-pico-littlefs-plugin.git
|
||||
* SPIFFS (ESP32):
|
||||
* upload SPIFFS data with ESP32 Sketch Data Upload:
|
||||
* ESP32: https://github.com/lorol/arduino-esp32fs-plugin
|
||||
* SD:
|
||||
* Most Arduino system built-in support SD file system.
|
||||
* Wio Terminal require extra dependant Libraries:
|
||||
* - Seeed_Arduino_FS: https://github.com/Seeed-Studio/Seeed_Arduino_FS.git
|
||||
* - Seeed_Arduino_SFUD: https://github.com/Seeed-Studio/Seeed_Arduino_SFUD.git
|
||||
******************************************************************************/
|
||||
#define GIF_FILENAME "/city17_240.gif"
|
||||
|
||||
/*******************************************************************************
|
||||
* Start of Arduino_GFX setting
|
||||
*
|
||||
* Arduino_GFX try to find the settings depends on selected board in Arduino IDE
|
||||
* Or you can define the display dev kit not in the board list
|
||||
* Defalult pin list for non display dev kit:
|
||||
* Arduino Nano, Micro and more: CS: 9, DC: 8, RST: 7, BL: 6, SCK: 13, MOSI: 11, MISO: 12
|
||||
* ESP32 various dev board : CS: 5, DC: 27, RST: 33, BL: 22, SCK: 18, MOSI: 23, MISO: nil
|
||||
* ESP32-C3 various dev board : CS: 7, DC: 2, RST: 1, BL: 3, SCK: 4, MOSI: 6, MISO: nil
|
||||
* ESP32-S2 various dev board : CS: 34, DC: 38, RST: 33, BL: 21, SCK: 36, MOSI: 35, MISO: nil
|
||||
* ESP32-S3 various dev board : CS: 40, DC: 41, RST: 42, BL: 48, SCK: 36, MOSI: 35, MISO: nil
|
||||
* ESP8266 various dev board : CS: 15, DC: 4, RST: 2, BL: 5, SCK: 14, MOSI: 13, MISO: 12
|
||||
* Raspberry Pi Pico dev board : CS: 17, DC: 27, RST: 26, BL: 28, SCK: 18, MOSI: 19, MISO: 16
|
||||
* RTL8720 BW16 old patch core : CS: 18, DC: 17, RST: 2, BL: 23, SCK: 19, MOSI: 21, MISO: 20
|
||||
* RTL8720_BW16 Official core : CS: 9, DC: 8, RST: 6, BL: 3, SCK: 10, MOSI: 12, MISO: 11
|
||||
* RTL8722 dev board : CS: 18, DC: 17, RST: 22, BL: 23, SCK: 13, MOSI: 11, MISO: 12
|
||||
* RTL8722_mini dev board : CS: 12, DC: 14, RST: 15, BL: 13, SCK: 11, MOSI: 9, MISO: 10
|
||||
* Seeeduino XIAO dev board : CS: 3, DC: 2, RST: 1, BL: 0, SCK: 8, MOSI: 10, MISO: 9
|
||||
* Teensy 4.1 dev board : CS: 39, DC: 41, RST: 40, BL: 22, SCK: 13, MOSI: 11, MISO: 12
|
||||
******************************************************************************/
|
||||
#include <Arduino_GFX_Library.h>
|
||||
|
||||
#define GFX_BL DF_GFX_BL // default backlight pin, you may replace DF_GFX_BL to actual backlight pin
|
||||
|
||||
/* More dev device declaration: https://github.com/moononournation/Arduino_GFX/wiki/Dev-Device-Declaration */
|
||||
#if defined(DISPLAY_DEV_KIT)
|
||||
Arduino_GFX *gfx = create_default_Arduino_GFX();
|
||||
#else /* !defined(DISPLAY_DEV_KIT) */
|
||||
|
||||
/* More data bus class: https://github.com/moononournation/Arduino_GFX/wiki/Data-Bus-Class */
|
||||
Arduino_DataBus *bus = create_default_Arduino_DataBus();
|
||||
|
||||
/* More display class: https://github.com/moononournation/Arduino_GFX/wiki/Display-Class */
|
||||
Arduino_GFX *gfx = new Arduino_ILI9341(bus, DF_GFX_RST, 3 /* rotation */, false /* IPS */);
|
||||
|
||||
#endif /* !defined(DISPLAY_DEV_KIT) */
|
||||
Arduino_Canvas_Indexed *canvasGfx = new Arduino_Canvas_Indexed(320 /* width */, 240 /* height */, gfx);
|
||||
/*******************************************************************************
|
||||
* End of Arduino_GFX setting
|
||||
******************************************************************************/
|
||||
|
||||
/* Wio Terminal */
|
||||
#if defined(ARDUINO_ARCH_SAMD) && defined(SEEED_GROVE_UI_WIRELESS)
|
||||
#include <Seeed_FS.h>
|
||||
#include <SD/Seeed_SD.h>
|
||||
#elif defined(TARGET_RP2040)
|
||||
#include <LittleFS.h>
|
||||
#include <SD.h>
|
||||
#elif defined(ESP32)
|
||||
#include <FFat.h>
|
||||
#include <LittleFS.h>
|
||||
#include <SPIFFS.h>
|
||||
#include <SD.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <LittleFS.h>
|
||||
#include <SD.h>
|
||||
#else
|
||||
#include <SD.h>
|
||||
#endif
|
||||
|
||||
#include "GifClass.h"
|
||||
static GifClass gifClass;
|
||||
|
||||
uint8_t *spriteMaster;
|
||||
bool spriteInitiated = false;
|
||||
|
||||
#include "IndexedSprite.h"
|
||||
IndexedSprite *background;
|
||||
IndexedSprite *road;
|
||||
IndexedSprite *cars;
|
||||
IndexedSprite *birds;
|
||||
IndexedSprite *sun;
|
||||
IndexedSprite *clouds;
|
||||
IndexedSprite *mpv;
|
||||
|
||||
int frame = 0;
|
||||
int fpsSnapShot = 0;
|
||||
unsigned long nextSnap = 0;
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
// Serial.setDebugOutput(true);
|
||||
// while(!Serial);
|
||||
Serial.println("Arduino_GFX GIF Sprite example");
|
||||
|
||||
#ifdef GFX_EXTRA_PRE_INIT
|
||||
GFX_EXTRA_PRE_INIT();
|
||||
#endif
|
||||
|
||||
// Init Display
|
||||
if (!canvasGfx->begin())
|
||||
{
|
||||
Serial.println("canvasGfx->begin() failed!");
|
||||
}
|
||||
canvasGfx->fillScreen(BLACK);
|
||||
canvasGfx->flush();
|
||||
canvasGfx->setDirectUseColorIndex(true);
|
||||
|
||||
#ifdef GFX_BL
|
||||
pinMode(GFX_BL, OUTPUT);
|
||||
digitalWrite(GFX_BL, HIGH);
|
||||
#endif
|
||||
|
||||
/* Wio Terminal */
|
||||
#if defined(ARDUINO_ARCH_SAMD) && defined(SEEED_GROVE_UI_WIRELESS)
|
||||
if (!SD.begin(SDCARD_SS_PIN, SDCARD_SPI, 4000000UL))
|
||||
#elif defined(TARGET_RP2040)
|
||||
if (!LittleFS.begin())
|
||||
// if (!SD.begin(SS))
|
||||
#elif defined(ESP32)
|
||||
// if (!FFat.begin())
|
||||
if (!LittleFS.begin())
|
||||
// if (!SPIFFS.begin())
|
||||
// if (!SD.begin(SS))
|
||||
#elif defined(ESP8266)
|
||||
if (!LittleFS.begin())
|
||||
// if (!SD.begin(SS))
|
||||
#else
|
||||
if (!SD.begin())
|
||||
#endif
|
||||
{
|
||||
Serial.println(F("ERROR: File System Mount Failed!"));
|
||||
gfx->println(F("ERROR: File System Mount Failed!"));
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* Wio Terminal */
|
||||
#if defined(ARDUINO_ARCH_SAMD) && defined(SEEED_GROVE_UI_WIRELESS)
|
||||
File gifFile = SD.open(GIF_FILENAME, "r");
|
||||
#elif defined(TARGET_RP2040)
|
||||
File gifFile = LittleFS.open(GIF_FILENAME, "r");
|
||||
// File gifFile = SD.open(GIF_FILENAME, "r");
|
||||
#elif defined(ESP32)
|
||||
// File gifFile = FFat.open(GIF_FILENAME, "r");
|
||||
File gifFile = LittleFS.open(GIF_FILENAME, "r");
|
||||
// File gifFile = SPIFFS.open(GIF_FILENAME, "r");
|
||||
// File gifFile = SD.open(GIF_FILENAME, "r");
|
||||
#elif defined(ESP8266)
|
||||
File gifFile = LittleFS.open(GIF_FILENAME, "r");
|
||||
// File gifFile = SD.open(GIF_FILENAME, "r");
|
||||
#else
|
||||
File gifFile = SD.open(GIF_FILENAME, FILE_READ);
|
||||
#endif
|
||||
if (!gifFile || gifFile.isDirectory())
|
||||
{
|
||||
Serial.println(F("ERROR: open gifFile Failed!"));
|
||||
gfx->println(F("ERROR: open gifFile Failed!"));
|
||||
}
|
||||
else
|
||||
{
|
||||
// read GIF file header
|
||||
gd_GIF *gif = gifClass.gd_open_gif(&gifFile);
|
||||
if (!gif)
|
||||
{
|
||||
Serial.println(F("gd_open_gif() failed!"));
|
||||
}
|
||||
else
|
||||
{
|
||||
spriteMaster = (uint8_t *)malloc(gif->width * gif->height / 2);
|
||||
if (!spriteMaster)
|
||||
{
|
||||
Serial.println(F("spriteMaster malloc failed!"));
|
||||
}
|
||||
else
|
||||
{
|
||||
int32_t res = gifClass.gd_get_frame(gif, spriteMaster);
|
||||
|
||||
if (res > 0)
|
||||
{
|
||||
// inital palette
|
||||
uint16_t *palette = canvasGfx->getColorIndex();
|
||||
memcpy(palette, gif->palette->colors, gif->palette->len * 2);
|
||||
|
||||
//IndexedSprite(x, y, *bitmap, *palette, w, h, x_skip, loop, frames, speed_divider, chroma_key)
|
||||
background = new IndexedSprite(0, 0, spriteMaster, palette, 405, 180, 0, true, 1, 3);
|
||||
road = new IndexedSprite(0, 180, spriteMaster + (180 * 405), palette, 405, 60, 0, true, 1, 1);
|
||||
cars = new IndexedSprite(0, 182, spriteMaster + (240 * 405), palette, 405, 11, 0, true, 1, 1, gif->gce.tindex);
|
||||
birds = new IndexedSprite(0, 80, spriteMaster + (251 * 405), palette, 51, 32, (405 - 51), false, 4, 4, gif->gce.tindex);
|
||||
sun = new IndexedSprite(16, 16, spriteMaster + (251 * 405) + 210, palette, 30, 30, (405 - 30), false, 1, 0, gif->gce.tindex);
|
||||
clouds = new IndexedSprite(0, 2, spriteMaster + (283 * 405), palette, 405, 94, 0, true, 1, 2, gif->gce.tindex);
|
||||
mpv = new IndexedSprite((canvasGfx->width() - 70) / 2, 182, spriteMaster + (377 * 405), palette, 50, 30, (405 - 50), false, 8, 2, gif->gce.tindex);
|
||||
|
||||
spriteInitiated = true;
|
||||
}
|
||||
|
||||
gifClass.gd_close_gif(gif);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
canvasGfx->setTextColor(0xfd, 0x00);
|
||||
canvasGfx->setTextSize(1);
|
||||
}
|
||||
|
||||
bool otherFrame = false;
|
||||
void testingLoop(void)
|
||||
{
|
||||
if (spriteInitiated)
|
||||
{
|
||||
background->h_scroll(-1);
|
||||
background->draw(canvasGfx);
|
||||
|
||||
road->h_scroll(-3);
|
||||
road->draw(canvasGfx);
|
||||
|
||||
cars->h_scroll(-6);
|
||||
cars->draw(canvasGfx);
|
||||
|
||||
birds->h_scroll(1, 480);
|
||||
birds->next_frame();
|
||||
birds->draw(canvasGfx);
|
||||
|
||||
sun->draw(canvasGfx);
|
||||
|
||||
clouds->h_scroll(1);
|
||||
clouds->draw(canvasGfx);
|
||||
|
||||
mpv->next_frame();
|
||||
mpv->draw(canvasGfx);
|
||||
}
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
testingLoop();
|
||||
|
||||
canvasGfx->setCursor(8, 8);
|
||||
canvasGfx->print(fpsSnapShot);
|
||||
|
||||
canvasGfx->flush();
|
||||
|
||||
// calculate FPS
|
||||
frame++;
|
||||
if (millis() > nextSnap)
|
||||
{
|
||||
fpsSnapShot = frame;
|
||||
frame = 0;
|
||||
nextSnap = millis() + 1000;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
Reference in New Issue
Block a user