Files
xiaozhi-esp32/main/display/lvgl_display/gif/gifdec.c
Xiaoxia f7284a57df Enhance memory management in asset download and OTA processes by repl… (#1716)
* Enhance memory management in asset download and OTA processes by replacing static buffer allocations with dynamic memory allocation using heap capabilities. Update SPIRAM configuration values for improved memory usage. Add logging for error handling in buffer allocation failures. Introduce a new parameter in CloseAudioChannel to control goodbye message sending in MQTT and WebSocket protocols.

* Update component versions in idf_component.yml and refactor GIF decoder functions for improved performance. Bump versions for audio effects, audio codec, LED strip, and other dependencies. Change GIF read and seek functions to inline for optimization.

* Update language files to include new phrases for flight mode and connection status across multiple locales. Added translations for "FLIGHT_MODE_ON", "FLIGHT_MODE_OFF", "CONNECTION_SUCCESSFUL", and "MODEM_INIT_ERROR" in various languages, enhancing user experience and localization support.

* fix wechat display
2026-01-31 22:58:08 +08:00

822 lines
22 KiB
C

#include "gifdec.h"
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <esp_log.h>
#define TAG "GIF"
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#define MAX(A, B) ((A) > (B) ? (A) : (B))
typedef struct Entry {
uint16_t length;
uint16_t prefix;
uint8_t suffix;
} Entry;
typedef struct Table {
int bulk;
int nentries;
Entry * entries;
} Table;
#if LV_GIF_CACHE_DECODE_DATA
#define LZW_MAXBITS 12
#define LZW_TABLE_SIZE (1 << LZW_MAXBITS)
#define LZW_CACHE_SIZE (LZW_TABLE_SIZE * 4)
#endif
static gd_GIF * gif_open(gd_GIF * gif);
static bool f_gif_open(gd_GIF * gif, const void * path, bool is_file);
static inline void f_gif_read(gd_GIF * gif, void * buf, size_t len);
static inline int f_gif_seek(gd_GIF * gif, size_t pos, int k);
static void f_gif_close(gd_GIF * gif);
#if LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_HELIUM
#include "gifdec_mve.h"
#endif
static uint16_t
read_num(gd_GIF * gif)
{
uint8_t bytes[2];
f_gif_read(gif, bytes, 2);
return bytes[0] + (((uint16_t) bytes[1]) << 8);
}
gd_GIF *
gd_open_gif_file(const char * fname)
{
gd_GIF gif_base;
memset(&gif_base, 0, sizeof(gif_base));
bool res = f_gif_open(&gif_base, fname, true);
if(!res) return NULL;
return gif_open(&gif_base);
}
gd_GIF *
gd_open_gif_data(const void * data)
{
gd_GIF gif_base;
memset(&gif_base, 0, sizeof(gif_base));
bool res = f_gif_open(&gif_base, data, false);
if(!res) return NULL;
return gif_open(&gif_base);
}
static gd_GIF * gif_open(gd_GIF * gif_base)
{
uint8_t sigver[3];
uint16_t width, height, depth;
uint8_t fdsz, bgidx, aspect;
uint8_t * bgcolor;
int gct_sz;
gd_GIF * gif = NULL;
/* Header */
f_gif_read(gif_base, sigver, 3);
if(memcmp(sigver, "GIF", 3) != 0) {
ESP_LOGW(TAG, "invalid signature");
goto fail;
}
/* Version */
f_gif_read(gif_base, sigver, 3);
if(memcmp(sigver, "89a", 3) != 0 && memcmp(sigver, "87a", 3) != 0) {
ESP_LOGW(TAG, "invalid version");
goto fail;
}
/* Width x Height */
width = read_num(gif_base);
height = read_num(gif_base);
/* FDSZ */
f_gif_read(gif_base, &fdsz, 1);
/* Presence of GCT */
if(!(fdsz & 0x80)) {
ESP_LOGW(TAG, "no global color table");
goto fail;
}
/* Color Space's Depth */
depth = ((fdsz >> 4) & 7) + 1;
/* Ignore Sort Flag. */
/* GCT Size */
gct_sz = 1 << ((fdsz & 0x07) + 1);
/* Background Color Index */
f_gif_read(gif_base, &bgidx, 1);
/* Aspect Ratio */
f_gif_read(gif_base, &aspect, 1);
/* Create gd_GIF Structure. */
if(0 == width || 0 == height){
ESP_LOGW(TAG, "Zero size image");
goto fail;
}
#if LV_GIF_CACHE_DECODE_DATA
if(0 == (INT_MAX - sizeof(gd_GIF) - LZW_CACHE_SIZE) / width / height / 5){
ESP_LOGW(TAG, "Image dimensions are too large");
goto fail;
}
gif = lv_malloc(sizeof(gd_GIF) + 5 * width * height + LZW_CACHE_SIZE);
#else
if(0 == (INT_MAX - sizeof(gd_GIF)) / width / height / 5){
ESP_LOGW(TAG, "Image dimensions are too large");
goto fail;
}
gif = lv_malloc(sizeof(gd_GIF) + 5 * width * height);
#endif
if(!gif) goto fail;
memcpy(gif, gif_base, sizeof(gd_GIF));
gif->width = width;
gif->height = height;
gif->depth = depth;
/* Read GCT */
gif->gct.size = gct_sz;
f_gif_read(gif, gif->gct.colors, 3 * gif->gct.size);
gif->palette = &gif->gct;
gif->bgindex = bgidx;
gif->canvas = (uint8_t *) &gif[1];
gif->frame = &gif->canvas[4 * width * height];
if(gif->bgindex) {
memset(gif->frame, gif->bgindex, gif->width * gif->height);
}
bgcolor = &gif->palette->colors[gif->bgindex * 3];
#if LV_GIF_CACHE_DECODE_DATA
gif->lzw_cache = gif->frame + width * height;
#endif
#ifdef GIFDEC_FILL_BG
GIFDEC_FILL_BG(gif->canvas, gif->width * gif->height, 1, gif->width * gif->height, bgcolor, 0x00);
#else
for(int i = 0; i < gif->width * gif->height; i++) {
gif->canvas[i * 4 + 0] = *(bgcolor + 2);
gif->canvas[i * 4 + 1] = *(bgcolor + 1);
gif->canvas[i * 4 + 2] = *(bgcolor + 0);
gif->canvas[i * 4 + 3] = 0x00; // 初始化为透明,让第一帧根据自己的透明度设置来渲染
}
#endif
gif->anim_start = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
gif->loop_count = -1;
goto ok;
fail:
f_gif_close(gif_base);
ok:
return gif;
}
static void
discard_sub_blocks(gd_GIF * gif)
{
uint8_t size;
do {
f_gif_read(gif, &size, 1);
f_gif_seek(gif, size, LV_FS_SEEK_CUR);
} while(size);
}
static void
read_plain_text_ext(gd_GIF * gif)
{
if(gif->plain_text) {
uint16_t tx, ty, tw, th;
uint8_t cw, ch, fg, bg;
size_t sub_block;
f_gif_seek(gif, 1, LV_FS_SEEK_CUR); /* block size = 12 */
tx = read_num(gif);
ty = read_num(gif);
tw = read_num(gif);
th = read_num(gif);
f_gif_read(gif, &cw, 1);
f_gif_read(gif, &ch, 1);
f_gif_read(gif, &fg, 1);
f_gif_read(gif, &bg, 1);
sub_block = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
gif->plain_text(gif, tx, ty, tw, th, cw, ch, fg, bg);
f_gif_seek(gif, sub_block, LV_FS_SEEK_SET);
}
else {
/* Discard plain text metadata. */
f_gif_seek(gif, 13, LV_FS_SEEK_CUR);
}
/* Discard plain text sub-blocks. */
discard_sub_blocks(gif);
}
static void
read_graphic_control_ext(gd_GIF * gif)
{
uint8_t rdit;
/* Discard block size (always 0x04). */
f_gif_seek(gif, 1, LV_FS_SEEK_CUR);
f_gif_read(gif, &rdit, 1);
gif->gce.disposal = (rdit >> 2) & 3;
gif->gce.input = rdit & 2;
gif->gce.transparency = rdit & 1;
gif->gce.delay = read_num(gif);
f_gif_read(gif, &gif->gce.tindex, 1);
/* Skip block terminator. */
f_gif_seek(gif, 1, LV_FS_SEEK_CUR);
}
static void
read_comment_ext(gd_GIF * gif)
{
if(gif->comment) {
size_t sub_block = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
gif->comment(gif);
f_gif_seek(gif, sub_block, LV_FS_SEEK_SET);
}
/* Discard comment sub-blocks. */
discard_sub_blocks(gif);
}
static void
read_application_ext(gd_GIF * gif)
{
char app_id[8];
char app_auth_code[3];
uint16_t loop_count;
/* Discard block size (always 0x0B). */
f_gif_seek(gif, 1, LV_FS_SEEK_CUR);
/* Application Identifier. */
f_gif_read(gif, app_id, 8);
/* Application Authentication Code. */
f_gif_read(gif, app_auth_code, 3);
if(!strncmp(app_id, "NETSCAPE", sizeof(app_id))) {
/* Discard block size (0x03) and constant byte (0x01). */
f_gif_seek(gif, 2, LV_FS_SEEK_CUR);
loop_count = read_num(gif);
if(gif->loop_count < 0) {
if(loop_count == 0) {
gif->loop_count = 0;
}
else {
gif->loop_count = loop_count + 1;
}
}
/* Skip block terminator. */
f_gif_seek(gif, 1, LV_FS_SEEK_CUR);
}
else if(gif->application) {
size_t sub_block = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
gif->application(gif, app_id, app_auth_code);
f_gif_seek(gif, sub_block, LV_FS_SEEK_SET);
discard_sub_blocks(gif);
}
else {
discard_sub_blocks(gif);
}
}
static void
read_ext(gd_GIF * gif)
{
uint8_t label;
f_gif_read(gif, &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:
ESP_LOGW(TAG, "unknown extension: %02X\n", label);
}
}
static uint16_t
get_key(gd_GIF *gif, int key_size, uint8_t *sub_len, uint8_t *shift, uint8_t *byte)
{
int bits_read;
int rpad;
int 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) {
f_gif_read(gif, sub_len, 1); /* Must be nonzero! */
if (*sub_len == 0) return 0x1000;
}
f_gif_read(gif, 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;
}
#if LV_GIF_CACHE_DECODE_DATA
/* Decompress image pixels.
* Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table) or parse error. */
static int
read_image_data(gd_GIF *gif, int interlace)
{
uint8_t sub_len, shift, byte;
int ret = 0;
int key_size;
int y, pass, linesize;
uint8_t *ptr = NULL;
uint8_t *ptr_row_start = NULL;
uint8_t *ptr_base = NULL;
size_t start, end;
uint16_t key, clear_code, stop_code, curr_code;
int frm_off, frm_size,curr_size,top_slot,new_codes,slot;
/* The first value of the value sequence corresponding to key */
int first_value;
int last_key;
uint8_t *sp = NULL;
uint8_t *p_stack = NULL;
uint8_t *p_suffix = NULL;
uint16_t *p_prefix = NULL;
/* get initial key size and clear code, stop code */
f_gif_read(gif, &byte, 1);
key_size = (int) byte;
clear_code = 1 << key_size;
stop_code = clear_code + 1;
key = 0;
start = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
discard_sub_blocks(gif);
end = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
f_gif_seek(gif, start, LV_FS_SEEK_SET);
linesize = gif->width;
ptr_base = &gif->frame[gif->fy * linesize + gif->fx];
ptr_row_start = ptr_base;
ptr = ptr_row_start;
sub_len = shift = 0;
/* decoder */
pass = 0;
y = 0;
p_stack = gif->lzw_cache;
p_suffix = gif->lzw_cache + LZW_TABLE_SIZE;
p_prefix = (uint16_t*)(gif->lzw_cache + LZW_TABLE_SIZE * 2);
frm_off = 0;
frm_size = gif->fw * gif->fh;
curr_size = key_size + 1;
top_slot = 1 << curr_size;
new_codes = clear_code + 2;
slot = new_codes;
first_value = -1;
last_key = -1;
sp = p_stack;
while (frm_off < frm_size) {
/* copy data to frame buffer */
while (sp > p_stack) {
if(frm_off >= frm_size){
ESP_LOGW(TAG, "LZW table token overflows the frame buffer");
return -1;
}
*ptr++ = *(--sp);
frm_off += 1;
/* read one line */
if ((ptr - ptr_row_start) == gif->fw) {
if (interlace) {
switch(pass) {
case 0:
case 1:
y += 8;
ptr_row_start += linesize * 8;
break;
case 2:
y += 4;
ptr_row_start += linesize * 4;
break;
case 3:
y += 2;
ptr_row_start += linesize * 2;
break;
default:
break;
}
while (y >= gif->fh) {
y = 4 >> pass;
ptr_row_start = ptr_base + linesize * y;
pass++;
}
} else {
ptr_row_start += linesize;
}
ptr = ptr_row_start;
}
}
key = get_key(gif, curr_size, &sub_len, &shift, &byte);
if (key == stop_code || key >= LZW_TABLE_SIZE)
break;
if (key == clear_code) {
curr_size = key_size + 1;
slot = new_codes;
top_slot = 1 << curr_size;
first_value = last_key = -1;
sp = p_stack;
continue;
}
curr_code = key;
/*
* If the current code is a code that will be added to the decoding
* dictionary, it is composed of the data list corresponding to the
* previous key and its first data.
* */
if (curr_code == slot && first_value >= 0) {
*sp++ = first_value;
curr_code = last_key;
}else if(curr_code >= slot)
break;
while (curr_code >= new_codes) {
*sp++ = p_suffix[curr_code];
curr_code = p_prefix[curr_code];
}
*sp++ = curr_code;
/* Add code to decoding dictionary */
if (slot < top_slot && last_key >= 0) {
p_suffix[slot] = curr_code;
p_prefix[slot++] = last_key;
}
first_value = curr_code;
last_key = key;
if (slot >= top_slot) {
if (curr_size < LZW_MAXBITS) {
top_slot <<= 1;
curr_size += 1;
}
}
}
if (key == stop_code) f_gif_read(gif, &sub_len, 1); /* Must be zero! */
f_gif_seek(gif, end, LV_FS_SEEK_SET);
return ret;
}
#else
static Table *
new_table(int key_size)
{
int key;
int init_bulk = MAX(1 << (key_size + 1), 0x100);
Table * table = lv_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;
}
/* Add table entry. Return value:
* 0 on success
* +1 if key size must be incremented after this addition
* -1 if could not realloc table */
static int
add_entry(Table ** tablep, uint16_t length, uint16_t prefix, uint8_t suffix)
{
Table * table = *tablep;
if(table->nentries == table->bulk) {
table->bulk *= 2;
table = lv_realloc(table, sizeof(*table) + sizeof(Entry) * table->bulk);
if(!table) return -1;
table->entries = (Entry *) &table[1];
*tablep = table;
}
table->entries[table->nentries] = (Entry) {
length, prefix, suffix
};
table->nentries++;
if((table->nentries & (table->nentries - 1)) == 0)
return 1;
return 0;
}
/* Compute output index of y-th input line, in frame of height h. */
static int
interlaced_line_index(int h, int y)
{
int 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) or parse error. */
static int
read_image_data(gd_GIF * gif, int interlace)
{
uint8_t sub_len, shift, byte;
int init_key_size, key_size, table_is_full = 0;
int frm_off, frm_size, str_len = 0, i, p, x, y;
uint16_t key, clear, stop;
int ret;
Table * table;
Entry entry = {0};
size_t start, end;
f_gif_read(gif, &byte, 1);
key_size = (int) byte;
start = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
discard_sub_blocks(gif);
end = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
f_gif_seek(gif, start, LV_FS_SEEK_SET);
clear = 1 << key_size;
stop = clear + 1;
table = new_table(key_size);
key_size++;
init_key_size = key_size;
sub_len = shift = 0;
key = get_key(gif, key_size, &sub_len, &shift, &byte); /* clear code */
frm_off = 0;
ret = 0;
frm_size = gif->fw * gif->fh;
while(frm_off < frm_size) {
if(key == clear) {
key_size = init_key_size;
table->nentries = (1 << (key_size - 1)) + 2;
table_is_full = 0;
}
else if(!table_is_full) {
ret = add_entry(&table, str_len + 1, key, entry.suffix);
if(ret == -1) {
lv_free(table);
return -1;
}
if(table->nentries == 0x1000) {
ret = 0;
table_is_full = 1;
}
}
key = get_key(gif, key_size, &sub_len, &shift, &byte);
if(key == clear) continue;
if(key == stop || key == 0x1000) break;
if(ret == 1) key_size++;
entry = table->entries[key];
str_len = entry.length;
if(frm_off + str_len > frm_size){
ESP_LOGW(TAG, "LZW table token overflows the frame buffer");
lv_free(table);
return -1;
}
for(i = 0; i < str_len; i++) {
p = frm_off + entry.length - 1;
x = p % gif->fw;
y = p / gif->fw;
if(interlace)
y = interlaced_line_index((int) gif->fh, y);
gif->frame[(gif->fy + y) * gif->width + gif->fx + x] = entry.suffix;
if(entry.prefix == 0xFFF)
break;
else
entry = table->entries[entry.prefix];
}
frm_off += str_len;
if(key < table->nentries - 1 && !table_is_full)
table->entries[table->nentries - 1].suffix = entry.suffix;
}
lv_free(table);
if(key == stop) f_gif_read(gif, &sub_len, 1); /* Must be zero! */
f_gif_seek(gif, end, LV_FS_SEEK_SET);
return 0;
}
#endif
/* Read image.
* Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table) or parse error. */
static int
read_image(gd_GIF * gif)
{
uint8_t fisrz;
int interlace;
/* Image Descriptor. */
gif->fx = read_num(gif);
gif->fy = read_num(gif);
gif->fw = read_num(gif);
gif->fh = read_num(gif);
if(gif->fx + (uint32_t)gif->fw > gif->width || gif->fy + (uint32_t)gif->fh > gif->height){
ESP_LOGW(TAG, "Frame coordinates out of image bounds");
return -1;
}
f_gif_read(gif, &fisrz, 1);
interlace = fisrz & 0x40;
/* Ignore Sort Flag. */
/* Local Color Table? */
if(fisrz & 0x80) {
/* Read LCT */
gif->lct.size = 1 << ((fisrz & 0x07) + 1);
f_gif_read(gif, gif->lct.colors, 3 * gif->lct.size);
gif->palette = &gif->lct;
}
else
gif->palette = &gif->gct;
/* Image Data. */
return read_image_data(gif, interlace);
}
static void
render_frame_rect(gd_GIF * gif, uint8_t * buffer)
{
int i = gif->fy * gif->width + gif->fx;
#ifdef GIFDEC_RENDER_FRAME
GIFDEC_RENDER_FRAME(&buffer[i * 4], gif->fw, gif->fh, gif->width,
&gif->frame[i], gif->palette->colors,
gif->gce.transparency ? gif->gce.tindex : 0x100);
#else
int j, k;
uint8_t index, * color;
for(j = 0; j < gif->fh; j++) {
for(k = 0; k < gif->fw; k++) {
index = gif->frame[(gif->fy + j) * gif->width + gif->fx + k];
color = &gif->palette->colors[index * 3];
if(!gif->gce.transparency || index != gif->gce.tindex) {
buffer[(i + k) * 4 + 0] = *(color + 2);
buffer[(i + k) * 4 + 1] = *(color + 1);
buffer[(i + k) * 4 + 2] = *(color + 0);
buffer[(i + k) * 4 + 3] = 0xFF;
}
}
i += gif->width;
}
#endif
}
static void
dispose(gd_GIF * gif)
{
int i;
uint8_t * bgcolor;
switch(gif->gce.disposal) {
case 2: /* Restore to background color. */
bgcolor = &gif->palette->colors[gif->bgindex * 3];
uint8_t opa = 0xff;
if(gif->gce.transparency) opa = 0x00;
i = gif->fy * gif->width + gif->fx;
#ifdef GIFDEC_FILL_BG
GIFDEC_FILL_BG(&(gif->canvas[i * 4]), gif->fw, gif->fh, gif->width, bgcolor, opa);
#else
int j, k;
for(j = 0; j < gif->fh; j++) {
for(k = 0; k < gif->fw; k++) {
gif->canvas[(i + k) * 4 + 0] = *(bgcolor + 2);
gif->canvas[(i + k) * 4 + 1] = *(bgcolor + 1);
gif->canvas[(i + k) * 4 + 2] = *(bgcolor + 0);
gif->canvas[(i + k) * 4 + 3] = opa;
}
i += gif->width;
}
#endif
break;
case 3: /* Restore to previous, i.e., don't update canvas.*/
break;
default:
/* Add frame non-transparent pixels to canvas. */
render_frame_rect(gif, gif->canvas);
}
}
/* Return 1 if got a frame; 0 if got GIF trailer; -1 if error. */
int
gd_get_frame(gd_GIF * gif)
{
char sep;
dispose(gif);
f_gif_read(gif, &sep, 1);
while(sep != ',') {
if(sep == ';') {
f_gif_seek(gif, gif->anim_start, LV_FS_SEEK_SET);
if(gif->loop_count == 1 || gif->loop_count < 0) {
return 0;
}
else if(gif->loop_count > 1) {
gif->loop_count--;
}
}
else if(sep == '!')
read_ext(gif);
else return -1;
f_gif_read(gif, &sep, 1);
}
if(read_image(gif) == -1)
return -1;
return 1;
}
void
gd_render_frame(gd_GIF * gif, uint8_t * buffer)
{
render_frame_rect(gif, buffer);
}
void
gd_rewind(gd_GIF * gif)
{
gif->loop_count = -1;
f_gif_seek(gif, gif->anim_start, LV_FS_SEEK_SET);
}
void
gd_close_gif(gd_GIF * gif)
{
f_gif_close(gif);
lv_free(gif);
}
static bool f_gif_open(gd_GIF * gif, const void * path, bool is_file)
{
gif->f_rw_p = 0;
gif->data = NULL;
gif->is_file = is_file;
if(is_file) {
lv_fs_res_t res = lv_fs_open(&gif->fd, path, LV_FS_MODE_RD);
if(res != LV_FS_RES_OK) return false;
else return true;
}
else {
gif->data = path;
return true;
}
}
static void f_gif_read(gd_GIF * gif, void * buf, size_t len)
{
if(gif->is_file) {
lv_fs_read(&gif->fd, buf, len, NULL);
}
else {
memcpy(buf, &gif->data[gif->f_rw_p], len);
gif->f_rw_p += len;
}
}
static int f_gif_seek(gd_GIF * gif, size_t pos, int k)
{
if(gif->is_file) {
lv_fs_seek(&gif->fd, pos, k);
uint32_t x;
lv_fs_tell(&gif->fd, &x);
return x;
}
else {
if(k == LV_FS_SEEK_CUR) gif->f_rw_p += pos;
else if(k == LV_FS_SEEK_SET) gif->f_rw_p = pos;
return gif->f_rw_p;
}
}
static void f_gif_close(gd_GIF * gif)
{
if(gif->is_file) {
lv_fs_close(&gif->fd);
}
}