This commit is contained in:
2026-02-12 01:27:25 -08:00
parent b5c81d79f0
commit e9678dd1ea
322 changed files with 297928 additions and 93 deletions

View File

@@ -0,0 +1,637 @@
/*******************************************************************************
* GIFDEC Wrapper Class
*
* Rewrite from: https://github.com/BasementCat/arduino-tft-gif
******************************************************************************/
#pragma once
// #define GIF_HANDLE_TRANSPARENT
/* 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 processed_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->processed_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);
}
#ifdef GIF_HANDLE_TRANSPARENT
if ((!gif->processed_first_frame) || (tindex != entry.suffix))
#endif
{
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->processed_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);
}
int16_t gif_buf_last_idx, gif_buf_idx, file_pos;
uint8_t gif_buf[GIF_BUF_SIZE];
};

View File

@@ -0,0 +1,248 @@
/*******************************************************************************
* Animated GIF Image Viewer
* This is a simple Animated GIF image viewer example
* Image Source: https://www.pexels.com/video/earth-rotating-video-856356/
* cropped: x: 598 y: 178 width: 720 height: 720 resized: 240x240
* optimized with ezgif.com
*
* GIFDEC original source: https://github.com/BasementCat/arduino-tft-gif
*
* Setup steps:
* 1. Change your LCD parameters in Arduino_GFX setting
* 2. Upload Animated 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
******************************************************************************/
/* Wio Terminal */
#if defined(ARDUINO_ARCH_SAMD) && defined(SEEED_GROVE_UI_WIRELESS)
#define GIF_FILENAME "/ezgif.com-optimize.gif"
#elif defined(TARGET_RP2040) || defined(PICO_RP2350)
#define GIF_FILENAME "/ezgif.com-optimize.gif"
#elif defined(ESP32)
#define GIF_FILENAME "/ezgif.com-optimize.gif"
#else
#define GIF_FILENAME "/ezgif.com-resize.gif"
#endif
/*******************************************************************************
* 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-C2/3 various dev board: CS: 7, DC: 2, RST: 1, BL: 3, SCK: 4, MOSI: 6, MISO: nil
* ESP32-C5 various dev board : CS: 23, DC: 24, RST: 25, BL: 26, SCK: 10, MOSI: 8, MISO: nil
* ESP32-C6 various dev board : CS: 18, DC: 22, RST: 23, BL: 15, SCK: 21, MOSI: 19, MISO: nil
* ESP32-H2 various dev board : CS: 0, DC: 12, RST: 8, BL: 22, SCK: 10, MOSI: 25, MISO: nil
* ESP32-P4 various dev board : CS: 26, DC: 27, RST: 25, BL: 24, SCK: 36, MOSI: 32, 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) */
/*******************************************************************************
* 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) || defined(PICO_RP2350)
#include <LittleFS.h>
#include <SD.h>
#elif defined(ESP32)
#include <FFat.h>
#include <LittleFS.h>
#include <SPIFFS.h>
#include <SD.h>
#include <SD_MMC.h>
#elif defined(ESP8266)
#include <LittleFS.h>
#include <SD.h>
#else
#include <SD.h>
#endif
#include "GifClass.h"
static GifClass gifClass;
void setup()
{
#ifdef DEV_DEVICE_INIT
DEV_DEVICE_INIT();
#endif
Serial.begin(115200);
// Serial.setDebugOutput(true);
// while(!Serial);
Serial.println("Arduino_GFX Animated GIF Image Viewer example");
// Init Display
if (!gfx->begin())
{
Serial.println("gfx->begin() failed!");
}
gfx->fillScreen(RGB565_BLACK);
#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) || defined(PICO_RP2350)
if (!LittleFS.begin())
// if (!SD.begin(SS))
#elif defined(ESP32)
// if (!FFat.begin())
if (!LittleFS.begin())
// if (!SPIFFS.begin())
// SPI.begin(12 /* CLK */, 13 /* D0/MISO */, 11 /* CMD/MOSI */);
// if (!SD.begin(10 /* CS */, SPI))
// pinMode(10 /* CS */, OUTPUT);
// digitalWrite(SD_CS, HIGH);
// SD_MMC.setPins(12 /* CLK */, 11 /* CMD/MOSI */, 13 /* D0/MISO */);
// if (!SD_MMC.begin("/root", true /* mode1bit */, false /* format_if_mount_failed */, SDMMC_FREQ_DEFAULT))
// SD_MMC.setPins(12 /* CLK */, 11 /* CMD/MOSI */, 13 /* D0/MISO */, 14 /* D1 */, 15 /* D2 */, 10 /* D3/CS */);
// if (!SD_MMC.begin("/root", false /* mode1bit */, false /* format_if_mount_failed */, SDMMC_FREQ_HIGHSPEED))
#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!"));
}
}
void loop()
{
/* Wio Terminal */
#if defined(ARDUINO_ARCH_SAMD) && defined(SEEED_GROVE_UI_WIRELESS)
File gifFile = SD.open(GIF_FILENAME, "r");
#elif defined(TARGET_RP2040) || defined(PICO_RP2350)
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
{
uint8_t *buf = (uint8_t *)malloc(gif->width * gif->height);
if (!buf)
{
Serial.println(F("buf malloc failed!"));
}
else
{
int16_t x = (gfx->width() - gif->width) / 2;
int16_t y = (gfx->height() - gif->height) / 2;
Serial.println(F("GIF video start"));
int32_t start_ms = millis(), t_delay = 0, delay_until;
int32_t res = 1;
int32_t duration = 0, remain = 0;
while (res > 0)
{
t_delay = gif->gce.delay * 10;
res = gifClass.gd_get_frame(gif, buf);
if (res < 0)
{
Serial.println(F("ERROR: gd_get_frame() failed!"));
break;
}
else if (res > 0)
{
gfx->drawIndexedBitmap(x, y, buf, gif->palette->colors, gif->width, gif->height);
duration += t_delay;
delay_until = start_ms + duration;
while (millis() < delay_until)
{
delay(1);
remain++;
}
}
}
Serial.println(F("GIF video end"));
Serial.print(F("Actual duration: "));
Serial.print(millis() - start_ms);
Serial.print(F(", expected duration: "));
Serial.print(duration);
Serial.print(F(", remain: "));
Serial.print(remain);
Serial.print(F(" ("));
Serial.print(100.0 * remain / duration);
Serial.println(F("%)"));
gifClass.gd_close_gif(gif);
free(buf);
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 775 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

View File

@@ -0,0 +1,359 @@
/*******************************************************************************
* Animated GIF Image Viewer
* This is a simple Animated GIF image viewer example
* Image Source: https://www.pexels.com/video/earth-rotating-video-856356/
* cropped: x: 598 y: 178 width: 720 height: 720 resized: 240x240
* optimized with ezgif.com
*
* Dependent libraries:
* AnimatedGIF: https://github.com/bitbank2/AnimatedGIF.git
*
* Setup steps:
* 1. Change your LCD parameters in Arduino_GFX setting
* 2. Upload Animated 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
******************************************************************************/
/* Wio Terminal */
#if defined(ARDUINO_ARCH_SAMD) && defined(SEEED_GROVE_UI_WIRELESS)
#define GIF_FILENAME "/ezgif.com-optimize.gif"
#elif defined(TARGET_RP2040) || defined(PICO_RP2350)
#define GIF_FILENAME "/ezgif.com-optimize.gif"
#elif defined(ESP32)
#define GIF_FILENAME "/ezgif.com-optimize.gif"
#else
#define GIF_FILENAME "/ezgif.com-resize.gif"
#endif
/*******************************************************************************
* 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-C2/3 various dev board: CS: 7, DC: 2, RST: 1, BL: 3, SCK: 4, MOSI: 6, MISO: nil
* ESP32-C5 various dev board : CS: 23, DC: 24, RST: 25, BL: 26, SCK: 10, MOSI: 8, MISO: nil
* ESP32-C6 various dev board : CS: 18, DC: 22, RST: 23, BL: 15, SCK: 21, MOSI: 19, MISO: nil
* ESP32-H2 various dev board : CS: 0, DC: 12, RST: 8, BL: 22, SCK: 10, MOSI: 25, MISO: nil
* ESP32-P4 various dev board : CS: 26, DC: 27, RST: 25, BL: 24, SCK: 36, MOSI: 32, 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) */
/*******************************************************************************
* 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) || defined(PICO_RP2350)
#include <LittleFS.h>
#include <SD.h>
#elif defined(ESP32)
#include <FFat.h>
#include <LittleFS.h>
#include <SPIFFS.h>
#include <SD.h>
#include <SD_MMC.h>
#elif defined(ESP8266)
#include <LittleFS.h>
#include <SD.h>
#else
#include <SD.h>
#endif
#include <AnimatedGIF.h>
AnimatedGIF gif;
File f;
int16_t display_width, display_height;
void *GIFOpenFile(const char *fname, int32_t *pSize)
{
/* Wio Terminal */
#if defined(ARDUINO_ARCH_SAMD) && defined(SEEED_GROVE_UI_WIRELESS)
f = SD.open(fname, "r");
#elif defined(TARGET_RP2040) || defined(PICO_RP2350)
f = LittleFS.open(fname, "r");
// f = SD.open(fname, "r");
#elif defined(ESP32)
// f = FFat.open(fname, "r");
f = LittleFS.open(fname, "r");
// f = SPIFFS.open(fname, "r");
// f = SD.open(fname, "r");
#elif defined(ESP8266)
f = LittleFS.open(fname, "r");
// f = SD.open(fname, "r");
#else
f = SD.open(fname, FILE_READ);
#endif
if (f)
{
*pSize = f.size();
return (void *)&f;
}
return NULL;
} /* GIFOpenFile() */
void GIFCloseFile(void *pHandle)
{
File *f = static_cast<File *>(pHandle);
if (f != NULL)
{
f->close();
}
} /* GIFCloseFile() */
int32_t GIFReadFile(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen)
{
int32_t iBytesRead;
iBytesRead = iLen;
File *f = static_cast<File *>(pFile->fHandle);
// Note: If you read a file all the way to the last byte, seek() stops working
if ((pFile->iSize - pFile->iPos) < iLen)
{
iBytesRead = pFile->iSize - pFile->iPos - 1; // <-- ugly work-around
}
if (iBytesRead <= 0)
{
return 0;
}
iBytesRead = (int32_t)f->read(pBuf, iBytesRead);
pFile->iPos = f->position();
return iBytesRead;
} /* GIFReadFile() */
int32_t GIFSeekFile(GIFFILE *pFile, int32_t iPosition)
{
int i = micros();
File *f = static_cast<File *>(pFile->fHandle);
f->seek(iPosition);
pFile->iPos = (int32_t)f->position();
i = micros() - i;
// Serial.printf("Seek time = %d us\n", i);
return pFile->iPos;
} /* GIFSeekFile() */
// Draw a line of image directly on the LCD
void GIFDraw(GIFDRAW *pDraw)
{
uint8_t *s;
uint16_t *d, *usPalette, usTemp[320];
int x, y, iWidth;
iWidth = pDraw->iWidth;
if (iWidth + pDraw->iX > display_width)
{
iWidth = display_width - pDraw->iX;
}
usPalette = pDraw->pPalette;
y = pDraw->iY + pDraw->y; // current line
if (y >= display_height || pDraw->iX >= display_width || iWidth < 1)
{
return;
}
s = pDraw->pPixels;
if (pDraw->ucDisposalMethod == 2) // restore to background color
{
for (x = 0; x < iWidth; x++)
{
if (s[x] == pDraw->ucTransparent)
{
s[x] = pDraw->ucBackground;
}
}
pDraw->ucHasTransparency = 0;
}
// Apply the new pixels to the main image
if (pDraw->ucHasTransparency) // if transparency used
{
uint8_t *pEnd, c, ucTransparent = pDraw->ucTransparent;
int x, iCount;
pEnd = s + iWidth;
x = 0;
iCount = 0; // count non-transparent pixels
while (x < iWidth)
{
c = ucTransparent - 1;
d = usTemp;
while (c != ucTransparent && s < pEnd)
{
c = *s++;
if (c == ucTransparent) // done, stop
{
s--; // back up to treat it like transparent
}
else // opaque
{
*d++ = usPalette[c];
iCount++;
}
} // while looking for opaque pixels
if (iCount) // any opaque pixels?
{
gfx->draw16bitBeRGBBitmap(pDraw->iX + x, y, usTemp, iCount, 1);
x += iCount;
iCount = 0;
}
// no, look for a run of transparent pixels
c = ucTransparent;
while (c == ucTransparent && s < pEnd)
{
c = *s++;
if (c == ucTransparent)
{
iCount++;
}
else
{
s--;
}
}
if (iCount)
{
x += iCount; // skip these
iCount = 0;
}
}
}
else
{
s = pDraw->pPixels;
// Translate the 8-bit pixels through the RGB565 palette (already byte reversed)
for (x = 0; x < iWidth; x++)
{
usTemp[x] = usPalette[*s++];
}
gfx->draw16bitBeRGBBitmap(pDraw->iX, y, usTemp, iWidth, 1);
}
} /* GIFDraw() */
void setup()
{
#ifdef DEV_DEVICE_INIT
DEV_DEVICE_INIT();
#endif
Serial.begin(115200);
// Serial.setDebugOutput(true);
// while(!Serial);
Serial.println("Arduino_GFX Animated GIF Image Viewer example");
// Init Display
if (!gfx->begin())
{
Serial.println("gfx->begin() failed!");
}
gfx->fillScreen(RGB565_BLACK);
#ifdef GFX_BL
pinMode(GFX_BL, OUTPUT);
digitalWrite(GFX_BL, HIGH);
#endif
display_width = gfx->width();
display_height = gfx->height();
/* 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) || defined(PICO_RP2350)
if (!LittleFS.begin())
// if (!SD.begin(SS))
#elif defined(ESP32)
// if (!FFat.begin())
if (!LittleFS.begin())
// if (!SPIFFS.begin())
// SPI.begin(12 /* CLK */, 13 /* D0/MISO */, 11 /* CMD/MOSI */);
// if (!SD.begin(10 /* CS */, SPI))
// pinMode(10 /* CS */, OUTPUT);
// digitalWrite(SD_CS, HIGH);
// SD_MMC.setPins(12 /* CLK */, 11 /* CMD/MOSI */, 13 /* D0/MISO */);
// if (!SD_MMC.begin("/root", true /* mode1bit */, false /* format_if_mount_failed */, SDMMC_FREQ_DEFAULT))
// SD_MMC.setPins(12 /* CLK */, 11 /* CMD/MOSI */, 13 /* D0/MISO */, 14 /* D1 */, 15 /* D2 */, 10 /* D3/CS */);
// if (!SD_MMC.begin("/root", false /* mode1bit */, false /* format_if_mount_failed */, SDMMC_FREQ_HIGHSPEED))
#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!"));
}
gif.begin(BIG_ENDIAN_PIXELS);
}
void loop()
{
if (gif.open(GIF_FILENAME, GIFOpenFile, GIFCloseFile, GIFReadFile, GIFSeekFile, GIFDraw))
{
Serial.printf("Successfully opened GIF; Canvas size = %d x %d\n", gif.getCanvasWidth(), gif.getCanvasHeight());
GIFINFO gi;
if (gif.getInfo(&gi))
{
Serial.printf("frame count: %d\n", gi.iFrameCount);
Serial.printf("duration: %d ms\n", gi.iDuration);
Serial.printf("max delay: %d ms\n", gi.iMaxDelay);
Serial.printf("min delay: %d ms\n", gi.iMinDelay);
}
unsigned long start_ms = millis();
int iFrames = 0;
while (gif.playFrame(true, NULL))
{
iFrames++;
};
Serial.printf("total decode time for %d frames = %lu ms\n", iFrames, millis() - start_ms);
gif.close();
}
else
{
Serial.printf("Error opening file = %d\n", gif.getLastError());
while (1)
{
};
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 775 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

View File

@@ -0,0 +1,259 @@
/*******************************************************************************
* BMP Class
*
* Rewrite from: https://github.com/Jaycar-Electronics/Arduino-Picture-Frame.git
******************************************************************************/
#ifndef _BMPCLASS_H_
#define _BMPCLASS_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
typedef void(BMP_DRAW_CALLBACK)(int16_t x, int16_t y, uint16_t *bitmap, int16_t w, int16_t h);
class BmpClass
{
public:
void draw(
File *f, BMP_DRAW_CALLBACK *bmpDrawCallback, bool useBigEndian,
int16_t x, int16_t y, int16_t widthLimit, int16_t heightLimit)
{
_bmpDrawCallback = bmpDrawCallback;
_useBigEndian = useBigEndian;
_heightLimit = heightLimit;
int16_t u, v;
uint32_t xend;
getbmpparms(f);
// validate bitmap
if ((bmtype == 19778) && (bmwidth > 0) && (bmheight > 0) && (bmbpp > 0))
{
// centre image
u = (widthLimit - bmwidth) / 2;
v = (heightLimit - bmheight) / 2;
u = (u < 0) ? x : x + u;
v = (v < 0) ? y : y + v;
xend = (bmwidth > widthLimit) ? widthLimit : bmwidth;
bmpRow = (uint16_t *)malloc(xend * 2);
if (!bmpRow)
{
Serial.println(F("bmpRow malloc failed."));
}
if (bmbpp < 9)
{
bmplt = (uint16_t *)malloc(bmpltsize * 2);
if (!bmplt)
{
Serial.println(F("bmplt malloc failed."));
}
bmloadplt(f); // load palette if palettized
drawbmpal(f, u, v, xend);
free(bmplt);
}
else if (bmbpp == 16)
{
// TODO: bpp 16 should have 3 pixel types
drawbmRgb565(f, u, v, xend);
}
else
{
drawbmtrue(f, u, v, xend);
}
free(bmpRow);
}
}
private:
void bmloadplt(File *f)
{
byte r, g, b;
if (bmpltsize == 0)
{
bmpltsize = 1 << bmbpp; // load default palette size
}
f->seek(54); // palette position in type 0x28 bitmaps
for (int16_t i = 0; i < bmpltsize; i++)
{
b = f->read();
g = f->read();
r = f->read();
f->read(); // dummy byte
bmplt[i] = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
}
}
void drawbmpal(File *f, int16_t u, int16_t v, uint32_t xend)
{
byte bmbitmask;
int16_t i, ystart, bmppb, p, d;
int16_t x, y;
uint16_t c;
bmbpl = ((bmbpp * bmwidth + 31) / 32) * 4; // bytes per line
bmppb = 8 / bmbpp; // pixels/byte
bmbitmask = ((1 << bmbpp) - 1); // mask for each pixel
ystart = 0;
if (bmheight > _heightLimit)
{
ystart = bmheight - _heightLimit; // don't draw if it's outside screen
}
for (y = ystart; y < bmheight; y++)
{ // invert in calculation (y=0 is bottom)
f->seek(bmdataptr + y * bmbpl); // seek to start of line
x = 0;
p = 0;
while (x < xend)
{
if (p < 1)
{
d = f->read();
p = bmppb;
}
d = d << bmbpp;
c = bmplt[(bmbitmask & (d >> 8))];
bmpRow[x] = (_useBigEndian) ? ((c >> 8) | (c << 8)) : c;
p--;
x++;
}
_bmpDrawCallback(u, v + bmheight - 1 - y, bmpRow, xend, 1);
}
}
// draw 16-bit colour (RGB565) bitmap
void drawbmRgb565(File *f, int16_t u, int16_t v, uint32_t xend)
{
int16_t i, ystart;
uint32_t x, y;
byte lo, hi;
bmbpl = ((bmbpp * bmwidth + 31) / 32) * 4; // bytes per line, due to 32bit chunks
ystart = 0;
if (bmheight > _heightLimit)
{
ystart = bmheight - _heightLimit; // don't draw if it's outside screen
}
Serial.println(xend);
for (y = ystart; y < bmheight; y++)
{ // invert in calculation (y=0 is bottom)
f->seek(bmdataptr + (y * bmbpl)); // seek at start of line
for (x = 0; x < xend; x++)
{
lo = f->read();
hi = f->read();
if (_useBigEndian)
{
bmpRow[x] = hi | lo << 8;
}
else
{
bmpRow[x] = lo | hi << 8;
}
}
_bmpDrawCallback(u, v + bmheight - 1 - y, bmpRow, xend, 1);
}
}
// draw true colour bitmap at (u,v) handles 24/32 not 16bpp yet
void drawbmtrue(File *f, int16_t u, int16_t v, uint32_t xend)
{
int16_t i, ystart;
uint32_t x, y;
byte r, g, b;
bmbpl = ((bmbpp * bmwidth + 31) / 32) * 4; // bytes per line, due to 32bit chunks
ystart = 0;
if (bmheight > _heightLimit)
{
ystart = bmheight - _heightLimit; // don't draw if it's outside screen
}
for (y = ystart; y < bmheight; y++)
{ // invert in calculation (y=0 is bottom)
f->seek(bmdataptr + y * bmbpl); // seek at start of line
for (x = 0; x < xend; x++)
{
b = f->read();
g = f->read();
r = f->read();
if (bmbpp == 32)
{
f->read(); // dummy byte for 32bit
}
bmpRow[x] = (_useBigEndian) ? ((r & 0xf8) | (g >> 5) | ((g & 0x1c) << 11) | ((b & 0xf8) << 5)) : (((r & 0xf8) << 8) | ((g & 0xfc) << 3) | (b >> 3));
}
_bmpDrawCallback(u, v + bmheight - 1 - y, bmpRow, xend, 1);
}
}
void getbmpparms(File *f)
{ // load into globals as ints-some parameters are 32 bit, but we can't handle this size anyway
byte h[48]; // header is 54 bytes typically, but we don't need it all
int16_t i;
f->seek(0); // set start of file
for (i = 0; i < 48; i++)
{
h[i] = f->read(); // read header
}
bmtype = h[0] + (h[1] << 8); // offset 0 'BM'
bmdataptr = h[10] + (h[11] << 8); // offset 0xA pointer to image data
bmhdrsize = h[14] + (h[15] << 8); // dib header size (0x28 is usual)
// files may vary here, if !=28, unsupported type, put default values
bmwidth = 0;
bmheight = 0;
bmbpp = 0;
bmpltsize = 0;
if ((bmhdrsize == 0x28) || (bmhdrsize == 0x38))
{
bmwidth = h[18] + (h[19] << 8); // width
bmheight = h[22] + (h[23] << 8); // height
bmbpp = h[28] + (h[29] << 8); // bits per pixel
bmpltsize = h[46] + (h[47] << 8); // palette size
}
// Serial.printf("bmtype: %d, bmhdrsize: %d, bmwidth: %d, bmheight: %d, bmbpp: %d\n", bmtype, bmhdrsize, bmwidth, bmheight, bmbpp);
}
byte isbmp(char n[])
{ // check if bmp extension
int16_t k;
k = strlen(n);
if (k < 5)
{
return 0; // name not long enough
}
if (n[k - 1] != 'P')
{
return 0;
}
if (n[k - 2] != 'M')
{
return 0;
}
if (n[k - 3] != 'B')
{
return 0;
}
if (n[k - 4] != '.')
{
return 0;
}
return 1; // passes all tests
}
BMP_DRAW_CALLBACK *_bmpDrawCallback;
bool _useBigEndian;
int16_t _heightLimit;
uint16_t bmtype, bmdataptr; // from header
uint32_t bmhdrsize, bmwidth, bmheight, bmbpp, bmpltsize; // from DIB Header
uint16_t bmbpl; // bytes per line- derived
uint16_t *bmplt; // palette- stored encoded for LCD
uint16_t *bmpRow;
};
#endif // _BMPCLASS_H_

View File

@@ -0,0 +1,193 @@
/*******************************************************************************
* BMP Image Viewer
* This is a simple BMP image viewer example
* Image Source: https://github.blog/2014-11-24-from-sticker-to-sculpture-the-making-of-the-octocat-figurine/
*
* BMP Class original source: https://github.com/Jaycar-Electronics/Arduino-Picture-Frame.git
*
* Setup steps:
* 1. Change your LCD parameters in Arduino_GFX setting
* 2. Upload BMP 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 BMP_FILENAME "/octocatS.bmp" // 243x128@IndexedColor
#define BMP_FILENAME "/octocatM.bmp" // 456x240@16bit
// #define BMP_FILENAME "/octocatL.bmp" // 608x320@24bit
/*******************************************************************************
* 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-C2/3 various dev board: CS: 7, DC: 2, RST: 1, BL: 3, SCK: 4, MOSI: 6, MISO: nil
* ESP32-C5 various dev board : CS: 23, DC: 24, RST: 25, BL: 26, SCK: 10, MOSI: 8, MISO: nil
* ESP32-C6 various dev board : CS: 18, DC: 22, RST: 23, BL: 15, SCK: 21, MOSI: 19, MISO: nil
* ESP32-H2 various dev board : CS: 0, DC: 12, RST: 8, BL: 22, SCK: 10, MOSI: 25, MISO: nil
* ESP32-P4 various dev board : CS: 26, DC: 27, RST: 25, BL: 24, SCK: 36, MOSI: 32, 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) */
/*******************************************************************************
* 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) || defined(PICO_RP2350)
#include <LittleFS.h>
#include <SD.h>
#elif defined(ESP32)
#include <FFat.h>
#include <LittleFS.h>
#include <SPIFFS.h>
#include <SD.h>
#include <SD_MMC.h>
#elif defined(ESP8266)
#include <LittleFS.h>
#include <SD.h>
#else
#include <SD.h>
#endif
#include "BmpClass.h"
static BmpClass bmpClass;
// pixel drawing callback
static void bmpDrawCallback(int16_t x, int16_t y, uint16_t *bitmap, int16_t w, int16_t h)
{
// Serial.printf("Draw pos = %d, %d. size = %d x %d\n", x, y, w, h);
gfx->draw16bitRGBBitmap(x, y, bitmap, w, h);
}
void setup()
{
#ifdef DEV_DEVICE_INIT
DEV_DEVICE_INIT();
#endif
Serial.begin(115200);
// Serial.setDebugOutput(true);
// while(!Serial);
Serial.println("Arduino_GFX BMP Image Viewer example");
// Init Display
if (!gfx->begin())
{
Serial.println("gfx->begin() failed!");
}
gfx->fillScreen(RGB565_BLACK);
#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) || defined(PICO_RP2350)
if (!LittleFS.begin())
// if (!SD.begin(SS))
#elif defined(ESP32)
// if (!FFat.begin())
if (!LittleFS.begin())
// if (!SPIFFS.begin())
// SPI.begin(12 /* CLK */, 13 /* D0/MISO */, 11 /* CMD/MOSI */);
// if (!SD.begin(10 /* CS */, SPI))
// pinMode(10 /* CS */, OUTPUT);
// digitalWrite(SD_CS, HIGH);
// SD_MMC.setPins(12 /* CLK */, 11 /* CMD/MOSI */, 13 /* D0/MISO */);
// if (!SD_MMC.begin("/root", true /* mode1bit */, false /* format_if_mount_failed */, SDMMC_FREQ_DEFAULT))
// SD_MMC.setPins(12 /* CLK */, 11 /* CMD/MOSI */, 13 /* D0/MISO */, 14 /* D1 */, 15 /* D2 */, 10 /* D3/CS */);
// if (!SD_MMC.begin("/root", false /* mode1bit */, false /* format_if_mount_failed */, SDMMC_FREQ_HIGHSPEED))
#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!"));
}
else
{
unsigned long start = millis();
/* Wio Terminal */
#if defined(ARDUINO_ARCH_SAMD) && defined(SEEED_GROVE_UI_WIRELESS)
File bmpFile = SD.open(BMP_FILENAME, "r");
#elif defined(TARGET_RP2040) || defined(PICO_RP2350)
File bmpFile = LittleFS.open(BMP_FILENAME, "r");
// File bmpFile = SD.open(BMP_FILENAME, "r");
#elif defined(ESP32)
// File bmpFile = FFat.open(BMP_FILENAME, "r");
File bmpFile = LittleFS.open(BMP_FILENAME, "r");
// File bmpFile = SPIFFS.open(BMP_FILENAME, "r");
// File bmpFile = SD.open(BMP_FILENAME, "r");
#elif defined(ESP8266)
File bmpFile = LittleFS.open(BMP_FILENAME, "r");
// File bmpFile = SD.open(BMP_FILENAME, "r");
#else
File bmpFile = SD.open(BMP_FILENAME, FILE_READ);
#endif
// read BMP file header
bmpClass.draw(
&bmpFile, bmpDrawCallback, false /* useBigEndian */,
0 /* x */, 0 /* y */, gfx->width() /* widthLimit */, gfx->height() /* heightLimit */);
bmpFile.close();
Serial.print("Time used: ");
Serial.println(millis() - start);
}
}
void loop()
{
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 570 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -0,0 +1,182 @@
/*******************************************************************************
* JPEG Image Viewer
* This is a simple JPEG image viewer example
* Image Source: https://github.blog/2014-11-24-from-sticker-to-sculpture-the-making-of-the-octocat-figurine/
*
* Dependent libraries:
* JPEGDEC: https://github.com/bitbank2/JPEGDEC.git
*
* Setup steps:
* 1. Change your LCD parameters in Arduino_GFX setting
* 2. Upload JPEG 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 JPEG_FILENAME "/octocat.jpg"
/*******************************************************************************
* 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-C2/3 various dev board: CS: 7, DC: 2, RST: 1, BL: 3, SCK: 4, MOSI: 6, MISO: nil
* ESP32-C5 various dev board : CS: 23, DC: 24, RST: 25, BL: 26, SCK: 10, MOSI: 8, MISO: nil
* ESP32-C6 various dev board : CS: 18, DC: 22, RST: 23, BL: 15, SCK: 21, MOSI: 19, MISO: nil
* ESP32-H2 various dev board : CS: 0, DC: 12, RST: 8, BL: 22, SCK: 10, MOSI: 25, MISO: nil
* ESP32-P4 various dev board : CS: 26, DC: 27, RST: 25, BL: 24, SCK: 36, MOSI: 32, 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) */
/*******************************************************************************
* 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) || defined(PICO_RP2350)
#include <LittleFS.h>
#include <SD.h>
#elif defined(ESP32)
#include <FFat.h>
#include <LittleFS.h>
#include <SPIFFS.h>
#include <SD.h>
#include <SD_MMC.h>
#elif defined(ESP8266)
#include <LittleFS.h>
#include <SD.h>
#else
#include <SD.h>
#endif
#include "JpegFunc.h"
// pixel drawing callback
static int jpegDrawCallback(JPEGDRAW *pDraw)
{
// Serial.printf("Draw pos = %d,%d. size = %d x %d\n", pDraw->x, pDraw->y, pDraw->iWidth, pDraw->iHeight);
gfx->draw16bitBeRGBBitmap(pDraw->x, pDraw->y, pDraw->pPixels, pDraw->iWidth, pDraw->iHeight);
return 1;
}
void setup()
{
#ifdef DEV_DEVICE_INIT
DEV_DEVICE_INIT();
#endif
Serial.begin(115200);
// Serial.setDebugOutput(true);
// while(!Serial);
Serial.println("Arduino_GFX JPEG Image Viewer example");
// Init Display
if (!gfx->begin())
{
Serial.println("gfx->begin() failed!");
}
gfx->fillScreen(RGB565_BLACK);
#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) || defined(PICO_RP2350)
if (!LittleFS.begin())
// if (!SD.begin(SS))
#elif defined(ESP32)
// if (!FFat.begin())
if (!LittleFS.begin())
// if (!SPIFFS.begin())
// SPI.begin(12 /* CLK */, 13 /* D0/MISO */, 11 /* CMD/MOSI */);
// if (!SD.begin(10 /* CS */, SPI))
// pinMode(10 /* CS */, OUTPUT);
// digitalWrite(10 /* CS */, HIGH);
// SD_MMC.setPins(12 /* CLK */, 11 /* CMD/MOSI */, 13 /* D0/MISO */);
// if (!SD_MMC.begin("/root", true /* mode1bit */, false /* format_if_mount_failed */, SDMMC_FREQ_DEFAULT))
// SD_MMC.setPins(12 /* CLK */, 11 /* CMD/MOSI */, 13 /* D0/MISO */, 14 /* D1 */, 15 /* D2 */, 10 /* D3/CS */);
// if (!SD_MMC.begin("/root", false /* mode1bit */, false /* format_if_mount_failed */, SDMMC_FREQ_HIGHSPEED))
#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!"));
}
else
{
unsigned long start = millis();
jpegDraw(JPEG_FILENAME, jpegDrawCallback, true /* useBigEndian */,
0 /* x */, 0 /* y */, gfx->width() /* widthLimit */, gfx->height() /* heightLimit */);
Serial.printf("Time used: %lu\n", millis() - start);
}
delay(5000);
}
void loop()
{
int w = gfx->width();
int h = gfx->height();
unsigned long start = millis();
jpegDraw(JPEG_FILENAME, jpegDrawCallback, true /* useBigEndian */,
random(w * 2) - w /* x */,
random(h * 2) - h /* y */,
w /* widthLimit */, h /* heightLimit */);
Serial.printf("Time used: %lu\n", millis() - start);
delay(1000);
}

View File

@@ -0,0 +1,107 @@
/*******************************************************************************
* JPEGDEC related function
*
* Dependent libraries:
* JPEGDEC: https://github.com/bitbank2/JPEGDEC.git
******************************************************************************/
#ifndef _JPEGFUNC_H_
#define _JPEGFUNC_H_
#include <JPEGDEC.h>
static JPEGDEC _jpeg;
static File _f;
static int _x, _y, _x_bound, _y_bound;
static void *jpegOpenFile(const char *szFilename, int32_t *pFileSize)
{
// Serial.println("jpegOpenFile");
#if defined(ARDUINO_ARCH_SAMD) && defined(SEEED_GROVE_UI_WIRELESS)
_f = SD.open(szFilename, "r");
#elif defined(TARGET_RP2040) || defined(PICO_RP2350)
_f = LittleFS.open(szFilename, "r");
// _f = SDFS.open(szFilename, "r");
#elif defined(ESP32)
// _f = FFat.open(szFilename, "r");
_f = LittleFS.open(szFilename, "r");
// _f = SPIFFS.open(szFilename, "r");
// _f = SD.open(szFilename, "r");
// _f = SD_MMC.open(szFilename, "r");
#elif defined(ESP8266)
_f = LittleFS.open(szFilename, "r");
// _f = SD.open(szFilename, "r");
#else
_f = SD.open(szFilename, FILE_READ);
#endif
*pFileSize = _f.size();
return &_f;
}
static void jpegCloseFile(void *pHandle)
{
// Serial.println("jpegCloseFile");
File *f = static_cast<File *>(pHandle);
f->close();
}
static int32_t jpegReadFile(JPEGFILE *pFile, uint8_t *pBuf, int32_t iLen)
{
// Serial.printf("jpegReadFile, iLen: %d\n", iLen);
File *f = static_cast<File *>(pFile->fHandle);
size_t r = f->read(pBuf, iLen);
return r;
}
static int32_t jpegSeekFile(JPEGFILE *pFile, int32_t iPosition)
{
// Serial.printf("jpegSeekFile, pFile->iPos: %d, iPosition: %d\n", pFile->iPos, iPosition);
File *f = static_cast<File *>(pFile->fHandle);
f->seek(iPosition);
return iPosition;
}
static void jpegDraw(
const char *filename, JPEG_DRAW_CALLBACK *jpegDrawCallback, bool useBigEndian,
int x, int y, int widthLimit, int heightLimit)
{
_x = x;
_y = y;
_x_bound = _x + widthLimit - 1;
_y_bound = _y + heightLimit - 1;
_jpeg.open(filename, jpegOpenFile, jpegCloseFile, jpegReadFile, jpegSeekFile, jpegDrawCallback);
// scale to fit height
int _scale;
int iMaxMCUs;
float ratio = (float)_jpeg.getHeight() / heightLimit;
if (ratio <= 1)
{
_scale = 0;
iMaxMCUs = widthLimit / 16;
}
else if (ratio <= 2)
{
_scale = JPEG_SCALE_HALF;
iMaxMCUs = widthLimit / 8;
}
else if (ratio <= 4)
{
_scale = JPEG_SCALE_QUARTER;
iMaxMCUs = widthLimit / 4;
}
else
{
_scale = JPEG_SCALE_EIGHTH;
iMaxMCUs = widthLimit / 2;
}
_jpeg.setMaxOutputSize(iMaxMCUs);
if (useBigEndian)
{
_jpeg.setPixelType(RGB565_BIG_ENDIAN);
}
_jpeg.decode(x, y, _scale);
_jpeg.close();
}
#endif // _JPEGFUNC_H_

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

View File

@@ -0,0 +1,174 @@
/*******************************************************************************
* ESP32 SIMD Motion JPEG Image Viewer
* This is a simple Motion JPEG image viewer example using ESP32 SIMD
* Image Source: https://www.pexels.com/video/earth-rotating-video-856356/
* cropped: x: 598 y: 178 width: 720 height: 720 resized: 240x240
* ffmpeg -i "Pexels Videos 3931.mp4" -ss 0 -t 20.4s -vf "reverse,setpts=0.5*PTS,fps=10,vflip,hflip,rotate=90,crop=720:720:178:598,scale=240:240:flags=lanczos" -q:v 11 earth.mjpeg
*
* Dependent libraries:
* ESP32_JPEG: https://github.com/esp-arduino-libs/ESP32_JPEG.git
*
* Setup steps:
* 1. Change your LCD parameters in Arduino_GFX setting
* 2. Upload Motion JPEG file
* FFat/LittleFS:
* upload FFat (FatFS) data with ESP32 Sketch Data Upload:
* ESP32: https://github.com/lorol/arduino-esp32fs-plugin
* SD:
* Most Arduino system built-in support SD file system.
******************************************************************************/
#define ROOT "/root"
#define MJPEG_FILENAME ROOT "/earth.mjpeg"
#define MJPEG_OUTPUT_SIZE (240 * 240 * 2) // memory for a output image frame
#define MJPEG_BUFFER_SIZE (MJPEG_OUTPUT_SIZE / 10) // memory for a single JPEG frame
/*******************************************************************************
* 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:
* ESP32-P4 various dev board : CS: 26, DC: 27, RST: 25, BL: 24, SCK: 36, MOSI: 32, MISO: nil
******************************************************************************/
#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) */
/*******************************************************************************
* End of Arduino_GFX setting
******************************************************************************/
#include <FFat.h>
#include <LittleFS.h>
#include <SPIFFS.h>
#include <SD.h>
#include <SD_MMC.h>
#include "MjpegClass.h"
static MjpegClass mjpeg;
/* variables */
static int total_frames = 0;
static unsigned long total_read_video = 0;
static unsigned long total_decode_video = 0;
static unsigned long total_show_video = 0;
static unsigned long start_ms, curr_ms;
static int16_t x = -1, y = -1, w = -1, h = -1;
void setup()
{
#ifdef DEV_DEVICE_INIT
DEV_DEVICE_INIT();
#endif
Serial.begin(115200);
// Serial.setDebugOutput(true);
// while(!Serial);
Serial.println("Arduino_GFX Motion JPEG ESP32P4 Decoder Image Viewer example");
// Init Display
if (!gfx->begin())
{
Serial.println("gfx->begin() failed!");
}
gfx->fillScreen(RGB565_BLACK);
#ifdef GFX_BL
pinMode(GFX_BL, OUTPUT);
digitalWrite(GFX_BL, HIGH);
#endif
// if (!FFat.begin(false, ROOT))
if (!LittleFS.begin(false, ROOT))
// if (!SPIFFS.begin(false, ROOT))
// SPI.begin(12 /* CLK */, 13 /* D0/MISO */, 11 /* CMD/MOSI */);
// if (!SD.begin(10 /* CS */, SPI, 80000000L, ROOT))
// pinMode(10 /* CS */, OUTPUT);
// digitalWrite(SD_CS, HIGH);
// SD_MMC.setPins(12 /* CLK */, 11 /* CMD/MOSI */, 13 /* D0/MISO */);
// if (!SD_MMC.begin(ROOT, true /* mode1bit */, false /* format_if_mount_failed */, SDMMC_FREQ_DEFAULT))
// SD_MMC.setPins(12 /* CLK */, 11 /* CMD/MOSI */, 13 /* D0/MISO */, 14 /* D1 */, 15 /* D2 */, 10 /* D3/CS */);
// if (!SD_MMC.begin(ROOT, false /* mode1bit */, false /* format_if_mount_failed */, SDMMC_FREQ_HIGHSPEED))
{
Serial.println(F("ERROR: File System Mount Failed!"));
gfx->println(F("ERROR: File System Mount Failed!"));
}
else
{
Serial.println(F("MJPEG start"));
start_ms = millis();
curr_ms = millis();
if (!mjpeg.setup(MJPEG_FILENAME))
{
Serial.println(F("mjpeg.setup() failed!"));
}
else
{
while (mjpeg.readMjpegBuf())
{
// Read video
total_read_video += millis() - curr_ms;
curr_ms = millis();
// Play video
mjpeg.decodeJpg();
total_decode_video += millis() - curr_ms;
curr_ms = millis();
if (x == -1)
{
w = mjpeg.getWidth();
h = mjpeg.getHeight();
x = (w > gfx->width()) ? 0 : ((gfx->width() - w) / 2);
y = (h > gfx->height()) ? 0 : ((gfx->height() - h) / 2);
}
gfx->draw16bitRGBBitmap(x, y, mjpeg.getOutBuf(), w, h);
total_show_video += millis() - curr_ms;
curr_ms = millis();
total_frames++;
}
int time_used = millis() - start_ms;
Serial.println(F("MJPEG end"));
float fps = 1000.0 * total_frames / time_used;
Serial.printf("Arduino_GFX ESP32 SIMD MJPEG decoder\n\n");
Serial.printf("Frame size: %d x %d\n", mjpeg.getWidth(), mjpeg.getHeight());
Serial.printf("Total frames: %d\n", total_frames);
Serial.printf("Time used: %d ms\n", time_used);
Serial.printf("Average FPS: %0.1f\n", fps);
Serial.printf("Read MJPEG: %lu ms (%0.1f %%)\n", total_read_video, 100.0 * total_read_video / time_used);
Serial.printf("Decode video: %lu ms (%0.1f %%)\n", total_decode_video, 100.0 * total_decode_video / time_used);
Serial.printf("Show video: %lu ms (%0.1f %%)\n", total_show_video, 100.0 * total_show_video / time_used);
gfx->setCursor(0, 0);
gfx->printf("Arduino_GFX ESP32 SIMD MJPEG decoder\n\n");
gfx->printf("Frame size: %d x %d\n", mjpeg.getWidth(), mjpeg.getHeight());
gfx->printf("Total frames: %d\n", total_frames);
gfx->printf("Time used: %d ms\n", time_used);
gfx->printf("Average FPS: %0.1f\n", fps);
gfx->printf("Read MJPEG: %lu ms (%0.1f %%)\n", total_read_video, 100.0 * total_read_video / time_used);
gfx->printf("Decode video: %lu ms (%0.1f %%)\n", total_decode_video, 100.0 * total_decode_video / time_used);
gfx->printf("Show video: %lu ms (%0.1f %%)\n", total_show_video, 100.0 * total_show_video / time_used);
mjpeg.close();
}
}
}
void loop()
{
}

View File

@@ -0,0 +1,196 @@
/*******************************************************************************
* ESP32_JPEG Wrapper Class
*
* Dependent libraries:
* ESP32_JPEG: https://github.com/esp-arduino-libs/ESP32_JPEG.git
******************************************************************************/
#pragma once
#if defined(ESP32)
#include <driver/jpeg_decode.h>
#define READ_BATCH_SIZE 1024
class MjpegClass
{
public:
bool setup(const char *path)
{
_input = fopen(path, "r");
_read = 0;
jpeg_decode_engine_cfg_t decode_eng_cfg = {
.intr_priority = 0,
.timeout_ms = 40,
};
ESP_ERROR_CHECK(jpeg_new_decoder_engine(&decode_eng_cfg, &_decoder_engine));
jpeg_decode_cfg_t decode_cfg_rgb = {
.output_format = JPEG_DECODE_OUT_FORMAT_RGB565,
.rgb_order = JPEG_DEC_RGB_ELEMENT_ORDER_BGR,
};
size_t tx_buffer_size;
size_t rx_buffer_size;
jpeg_decode_memory_alloc_cfg_t rx_mem_cfg = {
.buffer_direction = JPEG_DEC_ALLOC_OUTPUT_BUFFER,
};
jpeg_decode_memory_alloc_cfg_t tx_mem_cfg = {
.buffer_direction = JPEG_DEC_ALLOC_INPUT_BUFFER,
};
uint32_t out_buf_size = MJPEG_OUTPUT_SIZE;
uint32_t bit_stream_size = MJPEG_BUFFER_SIZE;
_mjpeg_buf = (uint8_t *)jpeg_alloc_decoder_mem(bit_stream_size, &tx_mem_cfg, &tx_buffer_size);
_output_buf = (uint16_t *)jpeg_alloc_decoder_mem(out_buf_size, &rx_mem_cfg, &rx_buffer_size);
return true;
}
bool readMjpegBuf()
{
if (_read == 0)
{
// _mjpeg_buf empty
_read = fread(_mjpeg_buf, 1, READ_BATCH_SIZE, _input);
}
else
{
// pad previous remain data to the start of _mjpeg_buf
memcpy(_mjpeg_buf, _p, _read);
}
bool found_FFD8 = false;
_p = _mjpeg_buf;
while ((_read > 0) && (!found_FFD8))
{
while ((_read > 1) && (!found_FFD8))
{
--_read;
if ((*_p++ == 0xFF) && (*_p == 0xD8)) // JPEG header
{
// Serial.printf("Found FFD8 at: %d.\n", i);
found_FFD8 = true;
}
}
if (!found_FFD8)
{
if (*_p == 0xFF)
{
_mjpeg_buf[0] = 0xFF;
_read = fread(_mjpeg_buf + 1, 1, READ_BATCH_SIZE, _input) + 1;
}
else
{
_read = fread(_mjpeg_buf, 1, READ_BATCH_SIZE, _input);
}
_p = _mjpeg_buf;
}
}
if (!found_FFD8)
{
return false;
}
// rewind 1 byte
--_p;
++_read;
// pad JPEG header to the start of _mjpeg_buf
if (_p > _mjpeg_buf)
{
Serial.println("(_p > _mjpeg_buf)");
memcpy(_mjpeg_buf, _p, _read);
}
// skip JPEG header
_p += 2;
_read -= 2;
if (_read == 0)
{
_read = fread(_p, 1, READ_BATCH_SIZE, _input);
}
bool found_FFD9 = false;
while ((_read > 0) && (!found_FFD9))
{
while ((_read > 1) && (!found_FFD9))
{
--_read;
if ((*_p++ == 0xFF) && (*_p == 0xD9)) // JPEG trailer
{
// Serial.printf("Found FFD9 at: %d.\n", i);
found_FFD9 = true;
}
}
if (!found_FFD9)
{
_read += fread(_p + _read, 1, READ_BATCH_SIZE, _input);
// Serial.printf("_read: %d\n", _read - 1);
}
}
if (found_FFD9)
{
++_p;
--_read;
return true;
}
return false;
}
bool decodeJpg()
{
jpeg_decode_picture_info_t header_info;
ESP_ERROR_CHECK(jpeg_decoder_get_info(_mjpeg_buf, _p - _mjpeg_buf, &header_info));
_w = header_info.width;
_h = header_info.height;
uint32_t out_size;
jpeg_decode_cfg_t decode_cfg_rgb = {
.output_format = JPEG_DECODE_OUT_FORMAT_RGB565,
.rgb_order = JPEG_DEC_RGB_ELEMENT_ORDER_BGR,
};
ESP_ERROR_CHECK(jpeg_decoder_process(_decoder_engine, &decode_cfg_rgb, (const uint8_t *)_mjpeg_buf, _p - _mjpeg_buf, (uint8_t *)_output_buf, MJPEG_OUTPUT_SIZE, &out_size));
return true;
}
int16_t getWidth()
{
return _w;
}
int16_t getHeight()
{
return _h;
}
uint16_t *getOutBuf()
{
return _output_buf;
}
void close()
{
fclose(_input);
}
private:
FILE *_input;
uint8_t *_mjpeg_buf;
uint16_t *_output_buf;
jpeg_decoder_handle_t _decoder_engine;
int16_t _w = 0, _h = 0;
uint8_t *_p;
int32_t _read;
};
#endif // defined(ESP32)

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 KiB

View File

@@ -0,0 +1,199 @@
/*******************************************************************************
* ESP32 SIMD Motion JPEG Image Viewer
* This is a simple Motion JPEG image viewer example using ESP32 SIMD
* Image Source: https://www.pexels.com/video/earth-rotating-video-856356/
* cropped: x: 598 y: 178 width: 720 height: 720 resized: 240x240
* ffmpeg -i "Pexels Videos 3931.mp4" -ss 0 -t 20.4s -vf "reverse,setpts=0.5*PTS,fps=10,vflip,hflip,rotate=90,crop=720:720:178:598,scale=240:240:flags=lanczos" -q:v 11 earth.mjpeg
*
* Dependent libraries:
* ESP32_JPEG: https://github.com/esp-arduino-libs/ESP32_JPEG.git
*
* Setup steps:
* 1. Change your LCD parameters in Arduino_GFX setting
* 2. Upload Motion JPEG file
* FFat/LittleFS:
* upload FFat (FatFS) data with ESP32 Sketch Data Upload:
* ESP32: https://github.com/lorol/arduino-esp32fs-plugin
* SD:
* Most Arduino system built-in support SD file system.
******************************************************************************/
#define ROOT "/root"
#define MJPEG_FILENAME ROOT "/earth.mjpeg"
#define MJPEG_OUTPUT_SIZE (240 * 240 * 2) // memory for a output image frame
#define MJPEG_BUFFER_SIZE (MJPEG_OUTPUT_SIZE / 10) // memory for a single JPEG frame
/*******************************************************************************
* 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:
* ESP32 various dev board : CS: 5, DC: 27, RST: 33, BL: 22, SCK: 18, MOSI: 23, MISO: nil
* ESP32-C2/3 various dev board: CS: 7, DC: 2, RST: 1, BL: 3, SCK: 4, MOSI: 6, MISO: nil
* ESP32-C5 various dev board : CS: 23, DC: 24, RST: 25, BL: 26, SCK: 10, MOSI: 8, MISO: nil
* ESP32-C6 various dev board : CS: 18, DC: 22, RST: 23, BL: 15, SCK: 21, MOSI: 19, MISO: nil
* ESP32-H2 various dev board : CS: 0, DC: 12, RST: 8, BL: 22, SCK: 10, MOSI: 25, MISO: nil
* ESP32-P4 various dev board : CS: 26, DC: 27, RST: 25, BL: 24, SCK: 36, MOSI: 32, 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
******************************************************************************/
#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) */
/*******************************************************************************
* End of Arduino_GFX setting
******************************************************************************/
#include <FFat.h>
#include <LittleFS.h>
#include <SPIFFS.h>
#include <SD.h>
#include <SD_MMC.h>
#include "MjpegClass.h"
static MjpegClass mjpeg;
/* variables */
static int total_frames = 0;
static unsigned long total_read_video = 0;
static unsigned long total_decode_video = 0;
static unsigned long total_show_video = 0;
static unsigned long start_ms, curr_ms;
static int16_t x = -1, y = -1, w = -1, h = -1;
void setup()
{
#ifdef DEV_DEVICE_INIT
DEV_DEVICE_INIT();
#endif
Serial.begin(115200);
// Serial.setDebugOutput(true);
// while(!Serial);
Serial.println("Arduino_GFX Motion JPEG SIMD Decoder Image Viewer example");
// Init Display
if (!gfx->begin())
{
Serial.println("gfx->begin() failed!");
}
gfx->fillScreen(RGB565_BLACK);
#ifdef GFX_BL
pinMode(GFX_BL, OUTPUT);
digitalWrite(GFX_BL, HIGH);
#endif
// if (!FFat.begin(false, ROOT))
if (!LittleFS.begin(false, ROOT))
// if (!SPIFFS.begin(false, ROOT))
// SPI.begin(12 /* CLK */, 13 /* D0/MISO */, 11 /* CMD/MOSI */);
// if (!SD.begin(10 /* CS */, SPI, 80000000L, ROOT))
// pinMode(10 /* CS */, OUTPUT);
// digitalWrite(SD_CS, HIGH);
// SD_MMC.setPins(12 /* CLK */, 11 /* CMD/MOSI */, 13 /* D0/MISO */);
// if (!SD_MMC.begin(ROOT, true /* mode1bit */, false /* format_if_mount_failed */, SDMMC_FREQ_DEFAULT))
// SD_MMC.setPins(12 /* CLK */, 11 /* CMD/MOSI */, 13 /* D0/MISO */, 14 /* D1 */, 15 /* D2 */, 10 /* D3/CS */);
// if (!SD_MMC.begin(ROOT, false /* mode1bit */, false /* format_if_mount_failed */, SDMMC_FREQ_HIGHSPEED))
{
Serial.println(F("ERROR: File System Mount Failed!"));
gfx->println(F("ERROR: File System Mount Failed!"));
}
else
{
uint8_t *mjpeg_buf = (uint8_t *)aligned_alloc(16, MJPEG_BUFFER_SIZE);
if (!mjpeg_buf)
{
Serial.println(F("mjpeg_buf malloc failed!"));
}
else
{
uint16_t *output_buf = (uint16_t *)aligned_alloc(16, MJPEG_OUTPUT_SIZE);
if (!output_buf)
{
Serial.println(F("output_buf malloc failed!"));
}
else
{
Serial.println(F("MJPEG start"));
start_ms = millis();
curr_ms = millis();
if (!mjpeg.setup(
MJPEG_FILENAME, mjpeg_buf,
output_buf, MJPEG_OUTPUT_SIZE, true /* useBigEndian */))
{
Serial.println(F("mjpeg.setup() failed!"));
}
else
{
while (mjpeg.readMjpegBuf())
{
// Read video
total_read_video += millis() - curr_ms;
curr_ms = millis();
// Play video
mjpeg.decodeJpg();
total_decode_video += millis() - curr_ms;
curr_ms = millis();
if (x == -1)
{
w = mjpeg.getWidth();
h = mjpeg.getHeight();
x = (w > gfx->width()) ? 0 : ((gfx->width() - w) / 2);
y = (h > gfx->height()) ? 0 : ((gfx->height() - h) / 2);
}
gfx->draw16bitBeRGBBitmap(x, y, output_buf, w, h);
total_show_video += millis() - curr_ms;
curr_ms = millis();
total_frames++;
}
int time_used = millis() - start_ms;
Serial.println(F("MJPEG end"));
float fps = 1000.0 * total_frames / time_used;
Serial.printf("Arduino_GFX ESP32 SIMD MJPEG decoder\n\n");
Serial.printf("Frame size: %d x %d\n", mjpeg.getWidth(), mjpeg.getHeight());
Serial.printf("Total frames: %d\n", total_frames);
Serial.printf("Time used: %d ms\n", time_used);
Serial.printf("Average FPS: %0.1f\n", fps);
Serial.printf("Read MJPEG: %lu ms (%0.1f %%)\n", total_read_video, 100.0 * total_read_video / time_used);
Serial.printf("Decode video: %lu ms (%0.1f %%)\n", total_decode_video, 100.0 * total_decode_video / time_used);
Serial.printf("Show video: %lu ms (%0.1f %%)\n", total_show_video, 100.0 * total_show_video / time_used);
gfx->setCursor(0, 0);
gfx->printf("Arduino_GFX ESP32 SIMD MJPEG decoder\n\n");
gfx->printf("Frame size: %d x %d\n", mjpeg.getWidth(), mjpeg.getHeight());
gfx->printf("Total frames: %d\n", total_frames);
gfx->printf("Time used: %d ms\n", time_used);
gfx->printf("Average FPS: %0.1f\n", fps);
gfx->printf("Read MJPEG: %lu ms (%0.1f %%)\n", total_read_video, 100.0 * total_read_video / time_used);
gfx->printf("Decode video: %lu ms (%0.1f %%)\n", total_decode_video, 100.0 * total_decode_video / time_used);
gfx->printf("Show video: %lu ms (%0.1f %%)\n", total_show_video, 100.0 * total_show_video / time_used);
mjpeg.close();
}
}
}
}
}
void loop()
{
}

View File

@@ -0,0 +1,198 @@
/*******************************************************************************
* ESP32_JPEG Wrapper Class
*
* Dependent libraries:
* ESP32_JPEG: https://github.com/esp-arduino-libs/ESP32_JPEG.git
******************************************************************************/
#pragma once
#if defined(ESP32)
#include <ESP32_JPEG_Library.h>
#define READ_BATCH_SIZE 1024
class MjpegClass
{
public:
bool setup(
const char *path, uint8_t *mjpeg_buf,
uint16_t *output_buf, size_t output_buf_size, bool useBigEndian)
{
_input = fopen(path, "r");
_mjpeg_buf = mjpeg_buf;
_output_buf = (uint8_t *)output_buf;
_output_buf_size = output_buf_size;
_useBigEndian = useBigEndian;
_read = 0;
return true;
}
bool readMjpegBuf()
{
if (_read == 0)
{
// _mjpeg_buf empty
_read = fread(_mjpeg_buf, 1, READ_BATCH_SIZE, _input);
}
else
{
// pad previous remain data to the start of _mjpeg_buf
memcpy(_mjpeg_buf, _p, _read);
}
bool found_FFD8 = false;
_p = _mjpeg_buf;
while ((_read > 0) && (!found_FFD8))
{
while ((_read > 1) && (!found_FFD8))
{
--_read;
if ((*_p++ == 0xFF) && (*_p == 0xD8)) // JPEG header
{
// Serial.printf("Found FFD8 at: %d.\n", i);
found_FFD8 = true;
}
}
if (!found_FFD8)
{
if (*_p == 0xFF)
{
_mjpeg_buf[0] = 0xFF;
_read = fread(_mjpeg_buf + 1, 1, READ_BATCH_SIZE, _input) + 1;
}
else
{
_read = fread(_mjpeg_buf, 1, READ_BATCH_SIZE, _input);
}
_p = _mjpeg_buf;
}
}
if (!found_FFD8)
{
return false;
}
// rewind 1 byte
--_p;
++_read;
// pad JPEG header to the start of _mjpeg_buf
if (_p > _mjpeg_buf)
{
Serial.println("(_p > _mjpeg_buf)");
memcpy(_mjpeg_buf, _p, _read);
}
// skip JPEG header
_p += 2;
_read -= 2;
if (_read == 0)
{
_read = fread(_p, 1, READ_BATCH_SIZE, _input);
}
bool found_FFD9 = false;
while ((_read > 0) && (!found_FFD9))
{
while ((_read > 1) && (!found_FFD9))
{
--_read;
if ((*_p++ == 0xFF) && (*_p == 0xD9)) // JPEG trailer
{
// Serial.printf("Found FFD9 at: %d.\n", i);
found_FFD9 = true;
}
}
if (!found_FFD9)
{
_read += fread(_p + _read, 1, READ_BATCH_SIZE, _input);
// Serial.printf("_read: %d\n", _read - 1);
}
}
if (found_FFD9)
{
++_p;
--_read;
return true;
}
return false;
}
bool decodeJpg()
{
// Generate default configuration
jpeg_dec_config_t config = {
.output_type = JPEG_RAW_TYPE_RGB565_BE,
.rotate = JPEG_ROTATE_0D,
};
// Create jpeg_dec
_jpeg_dec = jpeg_dec_open(&config);
// Create io_callback handle
_jpeg_io = (jpeg_dec_io_t *)calloc(1, sizeof(jpeg_dec_io_t));
// Create out_info handle
_out_info = (jpeg_dec_header_info_t *)calloc(1, sizeof(jpeg_dec_header_info_t));
// Set input buffer and buffer len to io_callback
_jpeg_io->inbuf = _mjpeg_buf;
_jpeg_io->inbuf_len = _p - _mjpeg_buf;
jpeg_dec_parse_header(_jpeg_dec, _jpeg_io, _out_info);
_w = _out_info->width;
_h = _out_info->height;
if ((_w * _h * 2) > _output_buf_size)
{
return false;
}
_jpeg_io->outbuf = _output_buf;
jpeg_dec_process(_jpeg_dec, _jpeg_io);
jpeg_dec_close(_jpeg_dec);
free(_jpeg_io);
free(_out_info);
return true;
}
int16_t getWidth()
{
return _w;
}
int16_t getHeight()
{
return _h;
}
void close()
{
fclose(_input);
}
private:
FILE *_input;
uint8_t *_mjpeg_buf;
uint8_t *_output_buf;
size_t _output_buf_size;
bool _useBigEndian;
jpeg_dec_handle_t *_jpeg_dec;
jpeg_dec_io_t *_jpeg_io;
jpeg_dec_header_info_t *_out_info;
int16_t _w = 0, _h = 0;
uint8_t *_p;
int32_t _read;
};
#endif // defined(ESP32)

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 KiB

View File

@@ -0,0 +1,250 @@
/*******************************************************************************
* Motion JPEG Image Viewer
* This is a simple Motion JPEG image viewer example
* Image Source: https://www.pexels.com/video/earth-rotating-video-856356/
* cropped: x: 598 y: 178 width: 720 height: 720 resized: 240x240
* ffmpeg -i "Pexels Videos 3931.mp4" -ss 0 -t 20.4s -vf "reverse,setpts=0.5*PTS,fps=10,vflip,hflip,rotate=90,crop=720:720:178:598,scale=240:240:flags=lanczos" -q:v 9 earth.mjpeg
*
* Dependent libraries:
* JPEGDEC: https://github.com/bitbank2/JPEGDEC.git
*
* Setup steps:
* 1. Change your LCD parameters in Arduino_GFX setting
* 2. Upload Motion JPEG 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 MJPEG_FILENAME "/earth.mjpeg"
#define MJPEG_BUFFER_SIZE (240 * 240 * 2 / 10) // memory for a single JPEG frame
// #define MJPEG_FILENAME "/earth128.mjpeg"
// #define MJPEG_BUFFER_SIZE (128 * 128 * 2 / 10) // memory for a single JPEG frame
/*******************************************************************************
* 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-C2/3 various dev board: CS: 7, DC: 2, RST: 1, BL: 3, SCK: 4, MOSI: 6, MISO: nil
* ESP32-C5 various dev board : CS: 23, DC: 24, RST: 25, BL: 26, SCK: 10, MOSI: 8, MISO: nil
* ESP32-C6 various dev board : CS: 18, DC: 22, RST: 23, BL: 15, SCK: 21, MOSI: 19, MISO: nil
* ESP32-H2 various dev board : CS: 0, DC: 12, RST: 8, BL: 22, SCK: 10, MOSI: 25, MISO: nil
* ESP32-P4 various dev board : CS: 26, DC: 27, RST: 25, BL: 24, SCK: 36, MOSI: 32, 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) */
/*******************************************************************************
* 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) || defined(PICO_RP2350)
#include <LittleFS.h>
#include <SD.h>
#elif defined(ESP32)
#include <FFat.h>
#include <LittleFS.h>
#include <SPIFFS.h>
#include <SD.h>
#include <SD_MMC.h>
#elif defined(ESP8266)
#include <LittleFS.h>
#include <SD.h>
#else
#include <SD.h>
#endif
#include "MjpegClass.h"
static MjpegClass mjpeg;
/* variables */
static int total_frames = 0;
static unsigned long total_read_video = 0;
static unsigned long total_decode_video = 0;
static unsigned long total_show_video = 0;
static unsigned long start_ms, curr_ms;
// pixel drawing callback
static int jpegDrawCallback(JPEGDRAW *pDraw)
{
// Serial.printf("Draw pos = %d,%d. size = %d x %d\n", pDraw->x, pDraw->y, pDraw->iWidth, pDraw->iHeight);
unsigned long start = millis();
gfx->draw16bitBeRGBBitmap(pDraw->x, pDraw->y, pDraw->pPixels, pDraw->iWidth, pDraw->iHeight);
total_show_video += millis() - start;
return 1;
}
void setup()
{
#ifdef DEV_DEVICE_INIT
DEV_DEVICE_INIT();
#endif
Serial.begin(115200);
// Serial.setDebugOutput(true);
// while(!Serial);
Serial.println("Arduino_GFX Motion JPEG Image Viewer example");
// Init Display
if (!gfx->begin())
{
Serial.println("gfx->begin() failed!");
}
gfx->fillScreen(RGB565_BLACK);
#ifdef GFX_BL
pinMode(GFX_BL, OUTPUT);
digitalWrite(GFX_BL, HIGH);
#endif
/* Wio Terminal */
#if defined(ARDUINO_ARCH_SAMD) && defined(SEEED_GROVE_UI_WIRELESS)
// Init SPIFLASH
if (!SD.begin(SDCARD_SS_PIN, SDCARD_SPI, 4000000UL))
#elif defined(TARGET_RP2040) || defined(PICO_RP2350)
if (!LittleFS.begin())
// if (!SD.begin(SS))
#elif defined(ESP32)
// if (!FFat.begin())
if (!LittleFS.begin())
// if (!SPIFFS.begin())
// if (!SD.begin(SS))
// pinMode(10 /* CS */, OUTPUT);
// digitalWrite(10 /* CS */, HIGH);
// SD_MMC.setPins(12 /* CLK */, 11 /* CMD/MOSI */, 13 /* D0/MISO */);
// if (!SD_MMC.begin("/root", true))
#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!"));
}
else
{
#if defined(ARDUINO_ARCH_SAMD) && defined(SEEED_GROVE_UI_WIRELESS)
File mjpegFile = SD.open(MJPEG_FILENAME, "r");
#elif defined(TARGET_RP2040) || defined(PICO_RP2350)
File mjpegFile = LittleFS.open(MJPEG_FILENAME, "r");
// File mjpegFile = SD.open(MJPEG_FILENAME, "r");
#elif defined(ESP32)
// File mjpegFile = FFat.open(MJPEG_FILENAME, "r");
File mjpegFile = LittleFS.open(MJPEG_FILENAME, "r");
// File mjpegFile = SPIFFS.open(MJPEG_FILENAME, "r");
// File mjpegFile = SD.open(MJPEG_FILENAME, "r");
// File mjpegFile = SD_MMC.open(MJPEG_FILENAME, "r");
#elif defined(ESP8266)
File mjpegFile = LittleFS.open(MJPEG_FILENAME, "r");
// File mjpegFile = SD.open(MJPEG_FILENAME, "r");
#else
File mjpegFile = SD.open(MJPEG_FILENAME, FILE_READ);
#endif
if (!mjpegFile || mjpegFile.isDirectory())
{
Serial.println(F("ERROR: Failed to open " MJPEG_FILENAME " file for reading"));
gfx->println(F("ERROR: Failed to open " MJPEG_FILENAME " file for reading"));
}
else
{
uint8_t *mjpeg_buf = (uint8_t *)malloc(MJPEG_BUFFER_SIZE);
if (!mjpeg_buf)
{
Serial.println(F("mjpeg_buf malloc failed!"));
}
else
{
Serial.println(F("MJPEG start"));
start_ms = millis();
curr_ms = millis();
mjpeg.setup(
&mjpegFile, mjpeg_buf, jpegDrawCallback, true /* useBigEndian */,
0 /* x */, 0 /* y */, gfx->width() /* widthLimit */, gfx->height() /* heightLimit */);
while (mjpegFile.available() && mjpeg.readMjpegBuf())
{
// Read video
total_read_video += millis() - curr_ms;
curr_ms = millis();
// Play video
mjpeg.drawJpg();
total_decode_video += millis() - curr_ms;
curr_ms = millis();
total_frames++;
}
int time_used = millis() - start_ms;
Serial.println(F("MJPEG end"));
mjpegFile.close();
float fps = 1000.0 * total_frames / time_used;
total_decode_video -= total_show_video;
Serial.printf("Total frames: %d\n", total_frames);
Serial.printf("Time used: %d ms\n", time_used);
Serial.printf("Average FPS: %0.1f\n", fps);
Serial.printf("Read MJPEG: %lu ms (%0.1f %%)\n", total_read_video, 100.0 * total_read_video / time_used);
Serial.printf("Decode video: %lu ms (%0.1f %%)\n", total_decode_video, 100.0 * total_decode_video / time_used);
Serial.printf("Show video: %lu ms (%0.1f %%)\n", total_show_video, 100.0 * total_show_video / time_used);
gfx->setCursor(0, 0);
gfx->printf("Total frames: %d\n", total_frames);
gfx->printf("Time used: %d ms\n", time_used);
gfx->printf("Average FPS: %0.1f\n", fps);
gfx->printf("Read MJPEG: %lu ms (%0.1f %%)\n", total_read_video, 100.0 * total_read_video / time_used);
gfx->printf("Decode video: %lu ms (%0.1f %%)\n", total_decode_video, 100.0 * total_decode_video / time_used);
gfx->printf("Show video: %lu ms (%0.1f %%)\n", total_show_video, 100.0 * total_show_video / time_used);
}
}
}
}
void loop()
{
}

View File

@@ -0,0 +1,203 @@
/*******************************************************************************
* JPEGDEC Wrapper Class
*
* Dependent libraries:
* JPEGDEC: https://github.com/bitbank2/JPEGDEC.git
******************************************************************************/
#pragma once
#include <JPEGDEC.h>
#define READ_BATCH_SIZE 1024
/* 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
class MjpegClass
{
public:
bool setup(
Stream *input, uint8_t *mjpeg_buf, JPEG_DRAW_CALLBACK *pfnDraw, bool useBigEndian,
int x, int y, int widthLimit, int heightLimit)
{
_input = input;
_mjpeg_buf = mjpeg_buf;
_pfnDraw = pfnDraw;
_useBigEndian = useBigEndian;
_x = x;
_y = y;
_widthLimit = widthLimit;
_heightLimit = heightLimit;
_read = 0;
return true;
}
bool readMjpegBuf()
{
if (_read == 0)
{
// _mjpeg_buf empty
_read = _input->readBytes(_mjpeg_buf, READ_BATCH_SIZE);
}
else
{
// pad previous remain data to the start of _mjpeg_buf
memcpy(_mjpeg_buf, _p, _read);
}
bool found_FFD8 = false;
_p = _mjpeg_buf;
while ((_read > 0) && (!found_FFD8))
{
while ((_read > 1) && (!found_FFD8))
{
--_read;
if ((*_p++ == 0xFF) && (*_p == 0xD8)) // JPEG header
{
// Serial.printf("Found FFD8 at: %d.\n", i);
found_FFD8 = true;
}
}
if (!found_FFD8)
{
if (*_p == 0xFF)
{
_mjpeg_buf[0] = 0xFF;
_read = _input->readBytes(_mjpeg_buf + 1, READ_BATCH_SIZE) + 1;
}
else
{
_read = _input->readBytes(_mjpeg_buf, READ_BATCH_SIZE);
}
_p = _mjpeg_buf;
}
}
if (!found_FFD8)
{
return false;
}
// rewind 1 byte
--_p;
++_read;
// pad JPEG header to the start of _mjpeg_buf
if (_p > _mjpeg_buf)
{
Serial.println("(_p > _mjpeg_buf)");
memcpy(_mjpeg_buf, _p, _read);
}
// skip JPEG header
_p += 2;
_read -= 2;
if (_read == 0)
{
_read = _input->readBytes(_p, READ_BATCH_SIZE);
}
bool found_FFD9 = false;
while ((_read > 0) && (!found_FFD9))
{
while ((_read > 1) && (!found_FFD9))
{
--_read;
if ((*_p++ == 0xFF) && (*_p == 0xD9)) // JPEG trailer
{
// Serial.printf("Found FFD9 at: %d.\n", i);
found_FFD9 = true;
}
}
if (!found_FFD9)
{
_read += _input->readBytes(_p + _read, READ_BATCH_SIZE);
// Serial.printf("_read: %d\n", _read - 1);
}
}
if (found_FFD9)
{
++_p;
--_read;
return true;
}
return false;
}
bool drawJpg()
{
_jpeg.openRAM(_mjpeg_buf, _p - _mjpeg_buf, _pfnDraw);
if (_scale == -1)
{
// scale to fit height
int iMaxMCUs;
int w = _jpeg.getWidth();
int h = _jpeg.getHeight();
float ratio = (float)h / _heightLimit;
if (ratio <= 1)
{
_scale = 0;
iMaxMCUs = _widthLimit / 16;
}
else if (ratio <= 2)
{
_scale = JPEG_SCALE_HALF;
iMaxMCUs = _widthLimit / 8;
w /= 2;
h /= 2;
}
else if (ratio <= 4)
{
_scale = JPEG_SCALE_QUARTER;
iMaxMCUs = _widthLimit / 4;
w /= 4;
h /= 4;
}
else
{
_scale = JPEG_SCALE_EIGHTH;
iMaxMCUs = _widthLimit / 2;
w /= 8;
h /= 8;
}
_jpeg.setMaxOutputSize(iMaxMCUs);
_x = (w > _widthLimit) ? 0 : ((_widthLimit - w) / 2);
_y = (_heightLimit - h) / 2;
}
if (_useBigEndian)
{
_jpeg.setPixelType(RGB565_BIG_ENDIAN);
}
_jpeg.decode(_x, _y, _scale);
_jpeg.close();
return true;
}
private:
Stream *_input;
uint8_t *_mjpeg_buf;
JPEG_DRAW_CALLBACK *_pfnDraw;
bool _useBigEndian;
int _x;
int _y;
int _widthLimit;
int _heightLimit;
int _scale = -1;
JPEGDEC _jpeg;
uint8_t *_p;
int32_t _read;
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

View File

@@ -0,0 +1,550 @@
/* GIMP RGB C-Source image dump (Arduino_UNO_Rev3_Ok.c) */
/* 1. Comment out exported struct */
// static const struct {
// unsigned int width;
// unsigned int height;
// unsigned int bytes_per_pixel; /* 2:RGB16, 3:RGB, 4:RGBA */
// unsigned char pixel_data[100 * 71 * 2 + 1];
// } gimp_image = {
// 100, 71, 2,
/* 2. Add PROGMEM data declaration */
/* 3. Fix the file tail */
#ifdef __AVR__
#include <avr/io.h>
#include <avr/pgmspace.h>
#elif defined(ESP8266)
#include <pgmspace.h>
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)
// PROGMEM is defefind for T4 to place data in specific memory section
#undef PROGMEM
#define PROGMEM
#else
#define PROGMEM
#endif
static const unsigned char Arduino_UNO_Rev3_Ok[] PROGMEM =
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\367<\337\034\327\034\327\034\327<\337<\327<\337<\337<\327<\327"
"<\337<\327<\327<\327\034\327<\327<\327=\327\\\327\\\317<\327\\\327<\337<\337"
"<\337<\337<\337<\337<\337<\337<\327<\337\236\367\277\367\277\367=\337<\337"
"<\337<\337<\337<\337<\337<\337<\337\034\337<\337<\337<\327<\327<\327<\337"
"\\\337<\327<\327<\327]\337\\\327\\\337]\327<\327\\\327<\327<\327=\327<\327"
"<\327]\327<\327\\\327<\327=\327=\327<\327=\327\\\327<\327<\327]\327=\327"
"=\327<\327<\327\\\327]\327\337\367\377\367\337\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376\377"
"\232\276\267\215X\246\326\225\071\276z\326z\316z\316\232\316z\316z\306\071"
"\306v\235u\225\370\235\327\205\267\215VeP\024PD\222\\P$NSnk\257s\216k\217"
"k\317s\216k\216k\317s\217knk\257s\320{\216kNc\217s\217k\217s\257s\216kNk"
"\257s\257s\217k\320s\257s\216k\317snkNcnk\216knknk\314R\015[Nc\314Z\015[-c"
"\015[-c-[Nc\314R\254R-c\015[-c\015[\015[,[\014[-[\014[\015[\014[-[-[-[\014S\314R\354"
"Rr\224\377\367\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\337\377\027\226\263l\363\234\367\305"
"Y\306\030\276\031\306\071\306z\316z\316\370\275v\245\323\224\027\306\323\234"
"\321Lq\004\217\013\222t\231\306y\316u\235\354ZD!E)Mk(Bf)jJ\014c%)E)\216sE)E)"
"\253ZjR%!\307\071\014c\344\040\004!\313Z\247\071E)\010BjR%!\004!\314Z\004!%)jJ)B$!\206"
")\015c\317sMc\303\030\344\030\253R\206\061\004!\350\071jJ\004!\344\030\253RE!\004\031"
"JJ\310\071$\031\207\061\313Z\004\031\004\031\213R\206\061%)\010BIJ\344\030\004!\014S\272"
"\276\337\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\337\377\273\276\323d\267\255Q|\330\265\273\316"
"y\306y\316y\316\030\276\370\275\327\265\360\203T\225\025\215Q,q\004-\013Q\204"
"\024\245\327\275\267\275\216kIJ)Jnk\253ZIJ\314ZnkiJ\212R\257s)B\313Z\015c\314"
"Z)B\010BMk\350\071\010BnkIJ\010B\213R\014c\010B\350\071nk\206\061\347\071\313ZJJ\206"
"\061\247\071\217s\360{-c\247\061\307\071\314Z\350\071\206\061IJ\212Rf\061E!\014["
"E!\206\061\213R\351Af)\206\061\253ZE)\243\030\254ZF)\004!\350\071)J\344\040\303"
"\020\015S\071\266\276\367\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\337\377\277\377\232\276\064\205\367\235u\225"
"\031\276\334\336y\316\271\326\271\326X\316\030\276z\306\317sl;P,q\004\222\004m"
"\023\223\214Z\316\374\336\333\336\353Z-c\354ZIB\212RMk\313Z\212J\317k\320"
"sjB\223\224\324\214\324\224\253R\014[,[\010\062M[mc\212J\313RM[\014[\212J\256"
"cm[jJ\354RM[\212J\313R\354R\313J\350\061(\062):\353R\313RI:\313JM[\253BjBM"
"[\354RJ:\015S\014[iB\252JM[\353RiB\014[\353RiB\014S\257c\252J\313J\360k\014S\253"
"B\367\245\236\367\337\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\277\377\232\276u\215\273\336\367\275\030"
"\276\373\326y\316\272\326\332\336\231\326\370\265Y\306nk\323d\262,\221\014"
"r\024\360\033\222d\273\316=\337\266\235(\"\202\010a\010a\000a\010a\000a\010\343\000E"
"\011E\011e\021\217k\364\214\324\204\020l\216[\216[\257[\216cn[\257c\257[n[\256"
"[\257c\216[\216[\216[n[n[\216[\216[nSnS\257c\360c\256[\216[mS\256[nSmS\353"
":\354B\313:\353:\014C\313:\253B\354Z\353B\313BMKMK\354B\014K\353:\013;\014;M"
"KMK,CMK\253\"\326\235^\367\276\367\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\337\377\276\377\232\276\064\205\327\245v"
"\245\071\276=\347X\306\231\326\271\336\266\275\327\275\233\316\257k\017\024"
"r\024\317\033\327\245\367\245r\\\260[\061\204\320[\317[HJ\206\061a\010$)\206\061"
"E)e\031\246\011\306\011\206\021\324\214\330\245\330\245\353:i\"\353:(\002\212\""
"i\"\211\"(\002\007\002\251\"\010\002\216S\014;'\002\252\062\013C'\002H\032\313:i*\007\002\007\002"
"\007\002\212\062\347\011\007\002I\"\353B\347\001(\032i\062(\032\347\001I*\312:\245\011H*\312"
":\347\001(\032I*\212*\347\001\346\001\211\"\007\002\353\062\216[\007\002\353\012\226\235]"
"\357\276\367\337\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\277\377\333\306\267\235\070\256\024\215\232\316\034\337"
"\333\326\327\275U\255\266\265Z\316\374\326\320{\360k\065\205\353\002\070\306"
"\030\316\263|\260[r\224Nc\314J\262\234\307\071\347\071ns\257{\206\061E\021nK\263"
"l\307\011\370\255r|\267\235-;\226\215\065\205i\022\364t\323t\061d\360S\222t\323"
"t\253\"\327\235v\215I\002\364\204\226\225M;\020d\327\225\222li\002I\002H\012U\205"
"Q\\H\002\263|\327\235\252*\020\\\263|\060\\I\022\024\205\262|'\012\222t\064\205\212"
"*qd\020d\322t\313\062\323t\364|\350\001U\205u\225\353B\353\022\226\235=\347\236"
"\367\337\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\337\377\277\377Y\246\222\\\327\265\267\275\373\336\034\337\233\316\232"
"\316z\316z\306\071\306\232\316u\255x\316\064\235\253\012r\214\263\224\216KI"
"\062\343\030\344\040)J\307\071\303\030\247\061E)%)\343\020H*mKRl\212\032z\276\267"
"\235{\276\357C\013\003\360C,\013\354\002,\033\357;\014\003\353\002\317C,\003\216\063\360"
"K,\003,\023\320C,\003\354\002\013\003\013\003\013\003\013\003\013\003\013\013\353\002\353\002\312\002\317"
"C\353\002\353\002\216C\312\002\353\002\252\012\353\032\312\002i\002\317C\353\002\312\002\353"
"\012\353\012\313\002\352\012\353\012\013\003\353\022\215CMCM+\225\235\034\347\236\367"
"\337\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\337\377\336\377\027\216\060\034\030\246\322tq|\024\235\024\235\223\214\262\224"
"\263\224\364\234\024\235\317sU\235\364\204n+.S\354Z\213\"\252:E)%!jR\307\071"
"f))B\010BE)\004!\313B\222t\330\235\353\032\374\316\327\245\274\306qL\030\216\370"
"\225\315\023\226}\267\205\363\\\363T\226\215Uu\017$\027\206\327\215\215\003\215"
"\003\323T\256\003\216\003\216\003\216\003\216\003\216\003\216\003\215\003\215\003\215\003m\013\222"
"Ln\003m\003\323\\L\003m\003M\003L\003M\003\014\003\323\\M\003m\003m\003m\003\216\023\330\225\364lm"
"\003r\\\226\205\256\033m\013v\235<\347~\357\337\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\336\377\276\367\030\216r\004\357\013\222"
"Ty\306X\306\023\215\262t\263\224\364\224\024\235\360{\320c\015\003\015\013\360\063"
"V\235\327\255\021\\M[\060\204IJ\015c\024\255\313R\253R\360\203\020\204\010:I\032"
"\023m\327\215M\023=\317\232\276\374\316QL\357\013P\004\060\014\060\004\060\014P\004P\004"
"u]\357\003\060\004\263L\263D\020\004/\004\017\014/\004\020\004\017\004\017\004\017\004\017\004\360\003\357"
"\013\357\003\017\004\017\004\357\003\357\003\357\003\357\003\357\003\357\003\317\003\316\003\317\003"
"\256\003\216\013\317\003\357\003\357\003\357\003\357\013\262T\017,\017\004P\064Tm\357\003\257"
"\013\226\225\034\347\236\367\277\367\236\367]\357]\357]\357]\357]\357]\357"
"]\357<\357=\347\327\225\322T\322Tz\266\027\316W\316\333\316\027\246U\255\030"
"\306z\316\323\244\323|n\003N\003\216#U\245\326\275\257S\360c\350\071\314Z-[ns"
"\252R\314ZNk\212R\212J\354\"\323d\232\246\061$\323dU\205\266\255T\235\023\225"
"qdT]\064]\064]\024e\023]\225m\363\\\064]\064e\064e\024]\024]\023]\023]\023]\363\\\363"
"\\\363d\363\\\023e\222<P\004\061\004P\004P\004P\004P\004P\004\060\004\060\004\060\004\017\004\060\004\020"
"\004\060\004\060\004P\004Q\004P\004\060\004\070\216\226u\060\014\064]\070\236P\004\317\023\225\235"
"\034\347~\357\276\367\226\275u\265\030\316\232\336\332\346\373\346\034\357\034"
"\357\033\357<\367<\357<\367\\\367<\357\\\367}\357<\367]\357<\357<\357<\357"
"\231\326\357S\360\033N\003\216#\232\306\272\326\364|\257S\212B\253B)*-[\212"
"J\247!\257cIB\010:\313\002\257#\020\064\322\024\262\034\020\064\226\245q\234\326\265"
"\334\336\272\266\333\316\272\266\273\276=\317\272\266y\256\034\317\373\276"
"\333\316\237\357~\347=\337\236\347\272\276y\266\333\276y\256\272\276\374"
"\326\030\226\262\004\221\004\222\004q\004r\004q\004q\004q\004q\004p\004P\004P\004P\004p\004q\004\221\004\221"
"\004\221\004P\004\070\206\064M\261\004\225m\272\246\221\004\360\013\226\225\035\347~\357"
"\277\367U\255\322\244\326\305\027\306X\316\272\336\272\336\333\336\373\346"
"\373\346\373\346\\\357<\357\034\347]\357]\357<\357\034\347\033\347\034\347<\357"
"\367\305+\063M\033\260\033\216#\357S\060T\222L\360;(\012\010\012\060T\020l\357{\256"
"K\320k\020t\216[\257;n\003\061,\322\034\363\014\222$\222T\322d\236\347\236\357"
"\272\246\373\276\272\246\333\256\333\266\273\256\034\277\374\276\333\256z"
"\246~\337\236\347\236\347\236\347\333\276]\307y\246\034\277\034\317\236\337"
"\272\246\266]\226]\225]\225]\225]\225]\225]\226]\226]\225]u]v]\226]\226]"
"\226]\226]\266]\226]\226]\266e\226]\266]\266e\327m\221\024\020\014\266\225\035"
"\357~\357\276\367U\255\064\255\326\305\027\306y\326\231\326\332\336\333\346"
"\332\336\333\336<\357\034\347]\357\\\357<\357<\357\373\346\033\347\373\346"
"<\357\034\347\070\306i:\010\002\216#\323lrt\222t\320;N\013\014\023n#Rd\272\326\373"
"\326n\063V\235\033\337\064\215\061L\320#\061$\064M\023-RdX\316\371\336x\306V\235"
"\323D\323\064\363\064\322\064\322\064\323\064\323\064\322\064\322\064\322\064\322"
",\322,\322,\323\064\263,\263,\263\064\262\064\262\064\222\064\262,\262,\262\064"
"\262,\262,\222,\222,\262,\262,\262,\222,\221,\222,\221,\262,\262,\262,\322"
",\322,\322,\262,\262,\222\064\262,\262,q\014\360\013\266\225=\357~\357\276\367"
"U\265\064\255\326\305\030\316X\316\272\336\231\326\232\336\373\346\373\346"
"\034\347\034\347\034\347<\357\373\346\373\346\373\346\373\346\373\346<\357\373"
"\346\027\306\252B\212\022j\012\221\214\023\255\063\245\222lrD\257#\317\033\262"
"T\070\276\071\276n\023\364\204\034\337\263tqT\021,\062$\226}\030~\222l\271\326\270"
"\336W\316v\235\317\063\221T\020\004\262\004\322\004\322\004\262\004\262\014\262\004\262"
"\004\262\004\262\004\222\014\262\004\261\004\262\004\261\004\221\004\221\004T]\327\205\363D"
"q\004q\004\221\004p\004p\004q\004\221\004q\004q\004q\004q\004q\004q\004q\004\222\004q\004\221\004\221\004\222"
"\004\222\004\222\004\222\004\221\004\221\004\020\014\226\225<\347}\357\276\367U\265\064"
"\255\225\275\326\305\030\316y\326y\326\232\336\272\336\034\347\033\347<\357"
"<\357\034\347\034\347<\357\333\336\272\336\373\346]\357\\\357\326\305i\062\010"
"\012,\023qd\257;\323l\223T\217\003\360#\320#\025]\060<\257#\321#QD\257#QT\360c"
"\357#\021$\262T\021<\257\033rd\313B\216[\360Cq<\061l\317\023\323\004\222\014\262"
"\014\327}y\236\071\236U]\262\004\262\004\322\004\221\004ueY\236\071\246\266m\220\004\326"
"u\272\256\266m\221\004q\004q\004q\004q\004q\004\221\004q\004q\004q\004q\004q\004q\004\221\004\221\004\221"
"\004\221\004\221\004\221\004\221\004\221\004\221\004q\004Q$\017\014u\215<\347~\357\236\367"
"T\255\064\265\266\305\027\316\070\316\231\326\373\346\272\336\332\336\033\347"
"\373\346\373\346\373\346\333\336<\357\034\347\373\346\333\336\373\346\034\347"
"<\357u\265\357s\350\011\252\012\257;\216\063\020L\021\064\022$\223T\065u\263d\231"
"\276\373\316\061<\267\235\374\336\064\205\323\\\020,\021,\020DrLq,\262tIR\316"
"srTq\064\360\063\061\064P\014\023=<\307\236\357}\337\236\337\237\347\272\256\262"
"\034\262\064\273\276\237\347~\337}\337\277\347\374\306\266u\370\205\225]q\004"
"q\004q\004q\004q\004q\004q\004q\004q\004q\004q\004q\004q\004q\004q\004\222\004\222\004\222\004\221\004\222\004"
"\221\004\261\004\221\024\363\\\017$\263\\=\347]\367\236\357U\265u\265\367\305\027"
"\316\070\316\070\316\272\336\232\336\272\336\373\346\373\346\333\336<\357\034"
"\347\373\346\373\346\373\346\373\346\333\336\373\346<\357\266\275q\204I\032"
"(\012\021|\222\214\226\255\256C\061,\263\\\064u\263d\374\326\236\357\357\063\367"
"\245\236\367U\215rL\021,\062,\257\003\257\003Q,\221|\216\203\316{rTrD\222D\323"
"T\360#\333\306~\337\225m\222$\263\064\070\226\236\347\272\256\333\276~\337"
"\027\216\221,q$\227\205\236\337z\266q\014q\004q\004q\004q\004q\004q\004q\004q\004q\004q\004q\004"
"q\004q\004q\004\221\004q\004\221\004\261\004\261\004\262\004\262\004\262\004\262\004\221\004\221\004"
"Q\014\357\003\327\215~\357\275\367T\255U\265\326\305\367\305\027\306\231\326"
"\231\326\231\326y\326\232\336\231\326\232\336\373\346\333\346\333\336\333"
"\346\333\346\272\336\373\346\373\346\033\347\266\275\262\204i\032(\012\320c"
"\353J\257k\217;R$\363d\026\205\065e\024m\065u\263\\\323dU\215\363d\222D\020,\061"
"$\257\013\260\003\317\023\262|\014k\316kQTr<sT\060T\222T\276\347\027\206\222\024"
"\221$\221\034\221\004\030\226\236\357\236\357\326}\221\014\367\215\266\205\261"
"\004\071\236~\337\222,q\004\221\004q\004q\004q\004q\004q\004q\004q\004q\004q\004q\004q\004q\004q\004\222\004"
"\221\004\221\004\222\004\221\004\262\004\262\004\262\004\261\004\222\004q\004P\004\360\013\030\216"
"\236\367T\255\064\265\266\275\367\305\027\306\070\316y\326\070\316Y\326y\326"
"\272\336\231\326\272\336\333\336\272\336\232\336\373\346\373\346\333\336"
"\232\326\332\336\266\275\322\214\010\022m\063\357c\060t\322\224qT\061,\317#\061"
"$\217\033.\033M#\317;\257C\357;\257;\257#\020,\021$\262L\364T\222<\364|\354R"
"Q\204\024U\024U\060<\222T\364t\236\357Ue\266e~\277~\307\023E\221$}\337]\327\221"
"\034ve~\327~\327\024M\266}~\357\323\064q\004\221\004q\004q\004q\004q\004q\004\221\004q\004q\004"
"q\004q\004q\004q\004q\014\221\014P\004P\004\222\004\221\004\222\004\262\004\262\004\262\004q\004\020\004"
"\317\003\360\013\060\024Y\256\064\255T\265\326\305\027\316Y\326\070\316Y\326Y\326"
"\070\316\070\316X\316Y\326\232\336\070\316y\326\332\336\332\336Y\316\232\336"
"\272\336\272\336u\265\363\234I\032\212\012\313:\020\204q\214\256+R,\360\033\021"
"$rD\260SN[\360k\317k\360s\217c\354\"\021\064\324d\363t\262d\020L\063\215S\235"
"\064\235qd\360CPT\062\064QD\236\347Y\236\262\004q\004q\004\261\024\272\256~\347~\357"
"Y\236\221\004\225uUm\221\004\272\256^\337\221\024\221\004q\004\221\004q\004q\004q\004q\004q"
"\004q\004q\004q\004q\004\221\004q\014qT\063\235\264\235\063\205\222d\064U\064U\363D\322\004"
"\222\004\317\003-#-\063L#\317\013\226}U\265\024\255\266\275\070\316\070\316\030\316"
"\030\316Y\326\070\316\030\316\027\316y\326X\316\231\326\231\326y\326\231\326"
"Y\316y\326z\326z\336u\265\215k\307\001\253\022\312*\262\214\262\234\256+\061"
",\021,\021$\360KNcNcMcNcNc.c\217c\360;\030\246\065\205Y\246\226\225\331\336\267"
"\366\267\336\065\235\263TrD\061<R\064z\266\276\337\071\236\064UTm\333\276~\357"
"\067\216y\236~\357\272\266Te\064]z\256\236\337\070\226q\004q\004q\004q\004q\004Q\004q\004"
"q\004q\004\221\004q\004q\004q\004\222\004\020\024V\225\227\346\327\346w\326\322\224Y\246"
"\034\277\232\236\221\004\061\014\313\"\014S\317{\357s\257;Uu\064\255\024\255\266"
"\305\367\305\326\305\266\275\030\316\367\305\326\305\367\305\266\275\027\316"
"X\326\027\306X\316\070\316\272\336\232\326y\326y\326\070\316\064\255H:\307\001"
"\253\022\317S\360\203\263\234m+R,r\064\061$\323t\015c-c-c.c.cN[NcqD\263T\323"
"d\223L\320C\020d\356k\017tQT\263L\222T\060\\\021\034\322$\232\256\236\347\276"
"\347\237\357\236\327\367\205r\014\222\014\030\226~\337\276\347\236\357~\347"
"Y\236\221\014\221\004q\004p\004q\004q\004p\004p\004q\004q\004q\004q\004q\004\221\004\221\004\020$\061T\316"
"k\356cMKr\\\362<\322\064\262,\221\004\357\013\014C\064\255y\306\070\316\262|Tu\064"
"\255\323\244u\265u\275\327\305\266\305\226\275u\265\266\305\225\275\266\305"
"\367\305\027\316\070\316\030\316\070\316\030\316\070\316\027\316\367\305\070\316"
"T\265hB\307\001J\002\313\032mC\317Sq<\361#r\064Q<\064\215-c-c-c.k.c\015c-[RL\365"
"d\262T\020<rTU\215t\245t\225V\205\061<\061<\323d\363\064\262\014\262\014\023=\226"
"u\226e\222$\262\014\261\004\221\004p\014q,um\225m\262\064q\004p\014q\004p\004p\004P\004q\004"
"q\004q\004\221\004q\004Q\014q\004\221\004\221\004q\014\360\023\061,U\225IBR\204\357+\061\024"
"Q\004\221\014\261\004\360\013rt\273\326=\347=\357U\225\064uT\265Q\224\064\255\323"
"\244\363\254T\265\363\244\064\255\064\265u\275T\265T\265\226\275\327\275\367"
"\305\030\316\366\305\070\316\027\316\327\305u\265\322\244\013S\014\063\216K\061"
"t\324\214\024\205\257\063\061\064\222L\065e\024\225Nc-c-c.c.c.cM[QT\233\266Y\266"
"\232\256u\225\031\347\070\357\331\346u\235\357K\221dr<P$\322T\030\226U}z\256"
"U}\231\256\064}u\205\266\215\226\215z\256\266\215\024}V\205X\246\065}\020,\020"
"$\357#\357#\020$\020$\020$\020$\360#R<\360\033\360\013\060\014\060\014\217\023\061<\263"
"\214+c\060t\360;\061<\021<\257\023Q\004\061\014M#Y\276]\347\232\306\017<UmT\265\017"
"\214\060\224p\234\017\214q\234P\224P\224p\224\321\234p\224\322\244\023\255\363"
"\244\064\255\065\265\225\275T\265\063\255\023\255\322\244\356\213\312:\253\022"
"L#U\225M[U\245\317K\317\033\364L\323T\262tNcNk-cMkMk.cMcr\\\364d\364d\364"
"\\\323d\023}\362|\322\204\323l\223D\222D\263D\323\\\030\256\033\317\030\256\333"
"\316\071\266\267\245\232\276\031\266Y\266qt\370\255\070\266\232\306\232\306"
"\225\225Y\266\222TrTrT\222TrT\222LqD\360\063\020\064\257+\257+\061\064Q<Q<\062"
"<\060D\323\214+c\020|\061D\061<\317;\317#\257\013P\004\020\014\256\013\360\063\223D"
"\221\064\025u\367\305\367\305\367\305\367\305\367\305\327\305\327\305\367\305"
"\367\305\327\315\027\276\267\265\266\265\266\275\326\305\326\305\326\305\327"
"\275\367\265\327\265\326\265\323\234\014Kj\012\353\012\061\\\223T\364\\\364T"
"\223<r<\263L\323t-c.c.c.c.c-cM[\061LQ<Q<\323T\263L\263Tq\\qT\263L\262L\222"
"LqDsTZ\256Y\256y\256X\256\027\236\373\276\266\225\232\266Y\256\027\236\373"
"\306Y\246v\235\327\255\273\306\267\215\222TrTrT\222TrL\222L\360\063\217\033"
"\360\063Q<\360\063\020\064\020<\020\064\061\064\020<\323\214\307\071\020|\317+\317;\060"
"\\\015\013\020\064\360;Uu\363d\364l\266m\316\013\025u\377\377\277\367\236\367<"
"\347\272\326\232\326y\316\071\306\030\276\267\275pdi\002\306\001\216khR\220\234"
"\315{\247\001f\001\206\001\206\001\306\001\347\001\212\012m\023\357C\020<\061<r<r<\020,\262"
"D\222LnS\317k\020tL[,[,SmK\323T\263T\223DQ\064\061D\363|\022\235\262\214\060T"
"\221\\\221D\323D\060<\017<\317#\216\023\256\023\256\013\256\023\357\033\317\023\257"
"\023\216\023\215\003\256\013/\014\017,/\034\060\034P\024P\024P\024P\024P\024P\024P\024\256"
"\023\361;\323<P\034Q,R\064\317#\020D\060\064r\\\357K\256C\221$\060\034\217\033\025"
"U\316\033\215\033rdQdql\262\\\356\013\025m\377\377\337\377\337\377~\367\034\347"
"\373\336\333\336y\316\070\306\327\305Pd\211\012H\002P\204\221\224\326\305\020"
"|\307\001\347\001\347\001\007\002\350\001(\002-#\013\013\216K\317CQ\064q,\020,\262$q\014\222"
"\004\317#\316+\317\063\060L\060T\060T\061TQLQ<Q\064Q$rD\367\245o\214P\214\322d\322"
"\\\363$\360\023\317K\061d\021<\061LrLrTQLQLrLQLQLqD\021Dn+\256S/\064\060\004\060\004"
"P\004P\004P\004P\004\060\004P\004Q\034\363\064p\004q\004P\004\060\004\256\003\360S\256;\061<\020<r<Q"
"\034q\014\216\023q,j*\013S\313J\314J\021|Mc\216\063\024m\377\377\377\377\337\377"
"\276\367]\357<\347\034\347\272\326Y\316\367\275Pl\252\002H\002(\012\060|\017t\010"
"\032\347\001\347\001\010\002(\002\010\002i\012\313\022\317[\221\214\222\214Q\\UE\363T\261"
"Tq\\\357;\060T\216\063\223d\223dQT\263l\364t\222T\360#\361#\061D\020\064\264\204"
"O|q|\020D\021\034\360\023QL\231\276\273\306\273\306\333\306\334\326\374\316\333"
"\326\333\316\374\316\374\316\070\246\065u\364l\324l\323d\063]UUT]\061\014\060\004"
"\060\004P\004\060\004P\004P\004P\004P\004Q\004P\004P\004\060\014\360+\262DP\034\360C\225\215\065\215"
"\257+\217#\020$\206!\252R\014c)B\354Z\020\204i\032\024u\377\377\377\377\377\377"
"\337\377\276\367}\357<\347\373\336y\316\030\306ql\353\012\252\022\215S\020t\357"
"s\256c,K\353\032\353\012,\033,\033\013\023\014\023\323\214\063\245\022\245\323|\263"
"\024\266\235\071\306\232\326\060|\030\276\222\214\363\214V\255\323\204\027\306"
"\231\326\060|\060<\357\033\317KpT\367\255\061\245T\255\360[\222d\357+\320C^\357"
"~\347\374\336\374\326\233\316=\327=\347Z\306\375\336~\347\330\255\060T\226"
"\215qT\222d\364t\357\023\064MP\014\355#\017\024\060\004P\004\060\004P\004P\004P\004P\004\060\004"
"\060\004P\004\060\004\060\004P\004\061d\272\326\027\306\261\\Q$\017\024\206!E)\307\071jJ\212"
"R$!\212\"\364t\377\377\377\377\377\377\377\377\337\377\276\367}\357\034\347"
"\232\326\071\306\262lM\033\353\032\366\275\026\316R\275\024\316Ns++,C,\003,\003m\013"
"\217+\317;q<\263D\024=\023\015\064u\221d\216S\060d\363t\222l\262d\222l\060T\317"
"[\060L\364t\360+\317#O\013n\033\065\215\223d\020<\256#r\\\220#-\023\237\357^\347"
"Y\276\367\245\226\225u\225\273\276z\256\070\236^\347Y\236\023m\071\256\327\225"
"Q\\y\266\216\033\262\\P\014\017D\060,\060\004P\004\060\004P\004P\004P\004\060\004\060\004P\004\060\004"
"\060\004\060\004P\004\360S\024\245U\255\317Kn#Q$\247\031e\061\307\071\253R\010B\205)\252"
"*\024m\377\377\377\377\377\377\377\377\377\377\337\377\276\367]\357\333\336"
"\232\326\363t\216\033m+\226\326V\377\067\377x\377\251Z\316\033\020\064\353*\256"
"C\023m\357+\257\033\322\024\363\004\023\005\023%\070\256\231\316\231\326\271\316\231"
"\326\231\306X\306\231\306y\316y\316\070\266\323d\316\033\222\\Y\256\023u<\337"
"\024\225X\266\071\256n#-\003M\033\277\347^\347\266\225\071\266\031\266V\245\374"
"\316\327\225z\256^\347\070\236\363T\070\226\267\205\364ty\256N#\222d\360\023"
"\017\014\060\004P\004\060\004\060\004P\004\060\004\060\004\060\004\060\004\060\004\060\004\060\004\060\004P\004QD"
"r\204\222\214\017$\216\013\017$F!\350A,cJJ-c\317{i\"\364t\377\377\377\377\377"
"\377\377\377\377\377\377\377\276\367}\357\034\347\333\336\064}\316\023\357\023"
"\214k\356\213\213s\254\203lK\357\033l\033jJ\357{\266\245\357+\317\033\323\024"
"\363\014\064\005\366\245\231\326\034\357\373\346\333\346\332\346\332\336\373\346"
"\373\346\373\346\332\336X\326U\255Pd\262t\327\225\221l\035\347\363\244\\\357"
"\060lPD\263LQL~\357^\347\236\357\237\357~\347\236\357\236\347\236\347~\347"
"~\347\071\236q,q,q\064\257;\360C\020D\263d\216+\216\033\216\023/\014\060\004\060\004"
"P\004\060\004P\004\060\004/\004\060\004\060\004\060\004\060\004\060\004\215\023\011B\010:\216\033\216\023"
"\017$\207)e)\010BjRJJIJ\212*\364t\377\377\377\377\377\377\377\377\377\377\377"
"\377\337\377\236\367]\357<\347u\205\020\024q\024o\204\315\203\021\255N\224\013"
"#\323l\266\255*J\354J\263\\\360\063\222\034\362\004\064\005\023%\363\244\034\347\272"
"\336\272\336\231\326\272\336\272\336\272\336\272\336\332\336\232\336\271"
"\316\030\306\317s\017\064-\023\363l\070\266\317k\030\266v\225\216#.\003-\003sl\363"
"|\263|\024u\324l\064e\064e\064e\064]\064e\363T\024E\024M\323L\023]qT\262\\\060LM\033"
"\360C\320;\216\063\256\033\060\014\060\004P\004\060\004\060\004/\004/\004/\004\060\004\060\004P\014\020"
"\\u\265u\255m+\216\023\060\034E\031e)\246\061jJIB$!\252\"\364l\377\377\377\377"
"\377\377\377\377\377\377\377\377\337\377\276\367\235\357}\357\266\205q\034"
"\221\024\315kO\234\355\213\016\214\016\\\222D\364\204\212Bmk\064\215\323L\061"
"$\023\015\064\005\024=\362\244\373\336\272\336\332\336\272\336\332\336\332\336"
"\332\336\272\336\332\336\333\336\272\336Y\316\253R\323\\u}\024m\065u\064\205"
"rdqT\061\064n\023\015\003n+\020TQT\357#\360#\222\024\222\004\222\014q\014\060\024\020d\357"
"kQl\020l\316\023\061\024\217\033\257#\257\063\020LM#mC\216;\357\013\060\004\060\004\060"
"\004\060\004\020\004\020\004\060\004\060\004\060\004P\014\021d\364\234\323\214\216\063\216\033P\034"
"\206!)JiJjJMk\014c\252*\364t\377\377\377\377\377\377\377\377\377\377\377\377"
"\337\377\337\377\236\367\236\367\327\205\222\034q\034\067\306;\357[\357\\\357"
"t\235\222\064\261\024\253\032\060dv\205Q\064r\064Q<\022%\023\025\225\245\070\316y\326"
"y\326\232\336\272\336\332\336\332\346\373\346\332\336\332\336\232\336\023"
"\245mcrL\327\235QL\363|Mk\263\214\216\023N\003\017,\317+\360S-\023\317\033\360"
"+\020\024q\004\221\004\221\004\221\004P\064\363\244\221\234P\224\017\214\255+P\004\060\034"
"\216\033\256#\317K\354\032m\063\317C\060,\017\014\357\003\356\003\316\003\316\003\316\003"
"\017\014\060\024\357\003\356\003\216\023\017\\M\063Q,\216\013\020\034f!e)\313Z)BIB\313"
"Z\253\062\364l\377\377\377\377\377\377\377\377\377\377\377\377\377\377\337"
"\377\276\367\236\367\370\215\222\034\221$\026\276\271\326\265\265\271\326Q"
"\214\322\064QD\362\004\362\014\323\014R\024Q$\263T\063\065T\005\322D\226\255\033\347"
"\373\346\373\336\332\336\372\336\033\347\272\336\231\336y\326\224\265\316"
"k\215;\263L\071\246\262<\024\225\262\224\222\214QT\021DQ<M\023\262\\\354\012\317"
"\033\360+\357\023q\004\221\004\221\004q\004P,\015ciJ(B\212Zl\033P\004\060\014\020\024\216\033"
"\020,QD\061L\061L\327\225\071\256y\256X\246\031\246Y\246\071\256\327\215\222l\327"
"\215\267\215\060D\060T\257;\060$n\013\020\034e\031\303\020E!iB\347\061\206\031\353"
"\032\024m\377\377\377\377\377\377\377\377\377\377\377\377\377\377\337\377\276"
"\367\276\367\370\215\262\034\262$u\215\226\245\225\225\266\235\065}\023%TM\363"
"\014\023\015\024\005q\004Q$\262\034\063\005\064\005\023\005p\024\256+L\063,C\215S\256c\317k\020"
"t\061t\222|\316c\253\062\020<r,Ue\024M\363\214/|\222\214\060L-\003\357\033.\013\317"
"#-\023\320#\317+\357\023\221\014q\004\221\004\221\004q\014\061d\014CmC\222t/\014P\004qT"
"\357C\216K\257S\354\032rL\222L\323d\024uTu\363l\065u\064u\323l\323d\323\\\023"
"m\364lqD\060DQDQ<\222<\357\023(\012\307\021\247\011\347\001\007\002i\002L\003\364l\377\377"
"\377\377\377\377\377\377\377\377\377\377\377\377\337\377\337\377\276\367"
"\030\216\262\034\323\024\322\014\322\034\362\024\323\034\323\024\364\004\023\005\363\004"
"\023\005\024\005Q\014R,Q$\363\034\024\015\064\005\023\015\222\024\320+\317#\360C\256K\256"
"K\216K\317K\317C\320CrLr<\263<\324L\324Lt\205\353Z\364\224\360CN\003\061,-\003"
"\360#M\013\317#\257#\257#Q\034\221\004\221\004q\004\020\024\263\204U\245\263\214V\245"
"\256\033P\024\221|\326\275T\255\222tq,\317\023m\023M\033M\023m\023M\023M\023L\023M"
"\013M\023m\013m\023n\023n\023n\023\216\023\216\023\216\023M\013\021D\216+\216\023m\003m"
"\003\316\003\357\003\364l\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\337\377\337\377\337\367\030\216\262\034\322\034\222<\363\004\023\005\363\004\063\005"
"\023\005\023\005\363\004\221,\363\034\061\014\257\033RLQ\064\363\024\023\005\363\024q$Q\064"
"Q\064\257Cr\\rd\363\\\064UuM\024U\222dQ,r\064\317#\060\064\261\\\360;\020L\222L"
"\263Dn#QDn\033-\023\257#\354\012\320\063\317\063r,\221\034\221\014\060\034\221\\Q"
"\\qdqd\256\033\221\014\262T\256\063\256;\320KP\024\357+\222L\323D\262Dr\\\222"
"D\262D\262D\262D\222D\262<\222D\222D\262D\262<\262<\323<\263<\060,\317+\017"
"$\357\003\017\004\357\003\060\004\017\004\364l\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\337\377\337\377\277\367\030\216\262\034\362,\322\\\322\014"
"\363\004\322\004\323\014\363\004\322\004\262\004\221L\322,Q\014-\003\360S\020L\322\024\023"
"\015\021\034r\064qd\364\234,[\014[,c,[\322\214Qdq<\363\\\363$\360\033r<rL\322\\"
"\061\034\021D\313\062\313\062-;-C\354:-CnC-C\015C\257K-K\015C\317KNCnC\016CnC-C-"
"Cn;M;\216;M;\014;-;\015Cm;\253:-K\253R\252\062\313\062m;\014;\313:mC,;j\062\014"
"\063m;\313:\014Cm;\354\062\253:\014\063\357\023\060\004\060\004q\004P\004\065m\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\377\337\377\337\377\337\367\030\216"
"\221\024\363\024\363\014\362\004\362\014\060<\261,\216;mCL;mK\216C\222t\020T\262"
"t\256\033Q$q\034\061$\222<\266\245\064\245\353R\313R\313Z\313Z\363\244u\235\064"
"]\323T\224\\\263T\323L\223T\261LR<\253*mk\256s%!Mk\020\204\206\061iJ\357{\313"
"Z\206\061\316{\256s\303\020\014c\357{\347\071iJ\317{\014c\004!mk\256s\344\030\253"
"R\316{HJ\306\071\357{,k\303\030Mk\256{$)\313Z\256s\253Z\246\071\317{mk\303\030"
"ms\357{\246\061\212R\357\203(J\215\023P\004q\004\221\004q\014Um\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\337\377\337\377\337\367\030\216\262\034"
"\363\024\323\004\363\004\262$\364Tp,)B\307A\010:)J\266\265T\255\332\336z\306\322"
"D\364L\363<\357\033\061$u\205\323\234\252Z\253R\212R\313Z\023\235\064\225\264"
"<r\064R<Q<\262d\260#\025=\222<\354BQ\214\262\224\354Z\262\234\024\245-c\020\204"
"\024\245\021\214ns\024\245\263\234-k\222\224\064\245\216s\360\203\064\245R\214"
"\355b\323\234\263\234\014c\061\214\363\234\217sns\364\244Q\214\253R\222\224"
"\323\234\354Z\317{\364\234\360{\354b\024\245\222\224\212Rr\224\363\234\014"
"cnk\262\224)B,\023\060\004\221\004\262\004\221\014Uu\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\337\377\337\377\337\377\030\216\262\034\023\025\362"
"\004\327}\033\327\373\316\034\317\350IHJIJjJi:qt\222|\024\205q<\323D\060$\364\064"
"\320\023\323d\023\205\253BjJjB\212B\364|\323t\222D\360+\322DQ<\322d\360\033"
"\363\024q\024\355:\350\071)B\011B\011:\011B\351\071\350\071\350\071\350\071\011:\011"
"B\350\071\350\071)B*B*B*B)B\351\071\351\071\310\071\350\071\350\071\011B\350\071\350"
"\071\310\071\310\061\310\061\310\061\310\061\310\061\310\061\310\071\310\071\351\071"
"\011B\011:\011:\011B)BJJ)B*BkJ)B\353\022\020\004\221\004\262\004q\014uu\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\337\377\337\377\337\377\030\216\262"
"\034\322\024\262\014\367\205\030\306\272\326\236\367)J)JiJIR\252B\064\205\364"
"\204\222t\222\034\363\004\363\034Q\034\263$q\024\061\014\222\014q\014q\014Q\014\262\024"
"\020<qd\021\034\363$R$\262,\061\034\362\004r\014\253\062F)\207\061\247\061\247\061\207"
"\061F)E)\206\061\307\071\011B\310\071F)F)\247\061\011B)B\011B\011:\350\071IJjJ\212"
"R\213R\213R\253R\253R\213R\350\071\351\071\310\071\350\071\310\071\310\071)BJB"
"jJ\213RjJ\011:\011:IBIBJB\011:Nk):\313\022\357\003\261\004\222$q,vu\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\337\377\337\377\337\367\030\216\222"
"\034\322<qT\266\205\326\275\272\326\235\367IB)J)JIJ\363\234\362\224\333\336"
"\267\255p\034\363\024p<R,\223\024q\024\020\014\363\014\322\014\363\014\262\014\322"
"\014\221$\222L\323,\323\024P\014r,\017\024\322\004Q\024\354:\005!E)F)F)E)%)%)\247\061"
"\310\071\247\061\350\071\247\071F)\207\061\350\071\011B)B\011B\350\071)B\212RjRjJ"
"jJjJjJjJ\011BJB)B)B*B\351\071\213R\253R\247\061\307\071\212JjJ\011:*B*BJB)B)B"
"):\313\022\357\003\221\004\222<\221DUu\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\337\377\337\377\337\367\030\216\221\024\322\024\024E\226}\327"
"\275\232\326~\367\010J)JJRIJ\014[ql\360sT\205\221\034\363\024\023U\221$\323\034"
"Q\034\060\024\024eu\215\020,\061T\360[\317K\316S\023}\024urL\024}\317+\261\004\060\024"
"\253\062\344\040\344\030\004!%)\004!\004!\004)\247\061\303\030\243\030\303\030\350\071f)"
"\247\061\350\071)B\213R\253RJJ\313Z\354Z\314Z\313Z\354Z\354Z\313Z\354Z\314"
"Z\213R\313ZjJjJ\011:jJ\004!\243\020\243\020f\061jJ)BJBJJJB\011:\310\071\010:\313\022"
"\317\003\221\004\262\004\221\004vu\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\337\377\337\377\337\367\027\216\262\034\323\014\322\014\266}y\316x\316"
"\272\306\307IHJjR)B\350)\262l\323t\363d\323\024\363\004\023\015\222\024\323\034"
"q$\360\023QdT\245\357\063\364|\014[\253R\213R\060t\262l\317C\265\265\015\023\262"
"\004\061\024\253:\344\040\343\030\004!E)\004!\303\040\004!\206\061\344\040\303\030\303\030"
"\350\071\206\061\247\061\350\071*BjJ\212JJJ\253R\253R\253Z\253R\253R\253R\212"
"R\313Z\253R\212R\253RJJJJ\011:\011B\243\020\243\030\242\020\005!\213R\011BJBjJ*B"
"\351\071\207\061JJ\313\022\317\003q\004\322\004\222\014vu\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\337\377\337\377\337\367\027\216\221\034\262\014"
"\322\004\323\064v]\064e\322L\011B)B)RIJ\023\235\363\234\272\326\232\316\222$\362"
"\004\024\005q\014\263$P\024R,\017\\\224\265\216+\323\204\256s\313b\253J\221<\323"
"<\020\064\026\306n#\261\004\061\024\314:\005!\004!%)F\061\004!\344\040%)\206\061E)\303\030"
"\350\071\010Bf\061\350\071JJjJ\253R\014c\212R\212J\314Z\354Z\014c\213R-k\014c\014"
"c\014c\014c\354b\314ZjJ)B\310\071\247\061\243\020%)JJkJ\011BJBJJjJ\011:\247\061J"
"B\313\032\316\003\221\004\262\004\262\014Uu\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\337\377\337\377\336\367\367\215r\034\322\014\322\004\263\014"
"\322\014PL\262\064g!\206)E)e)\212:\256[Qlv\225\221\034\323\004\023\005r\014\263\034"
"\216\003\060$\263|\363\224\322D\227\235\317s\354b\353Zu\245Qd\363\\\323\234"
"\222L\016<\020,\015C%)\004!\004!F)%)\344\040E)f\061\307\071\253RJJ\310\071\207\061\350"
"\071JJJJjR\253R\213RjJjJ\213R\253RjJ\313Z\353b,c\014c\353b\354bIJ\011B\310\071"
"\247\061)BjJ\253R\213RjJ\350\071\011B*B)B\011:\350\071):\312\032\256\003p\004\262\004"
"\221\014vu\377\377\377\377\377\377\377\377\377\377\377\377\377\377\337\377"
"\337\367\277\377\367\225\263,\023=\262L\362\024\363\014\362\014\363\014P\014/\014"
"/\014\317C\221,\020\034q\034\261\024\322\004\362\004\023\005r\014\262\034o\013N\003\014\033"
"m;q,\020<\357\063I\032\353\"\221<\262<r\064pT\222\064\023U\020\034NCE)%!\004!%!%)E"
")\206\061\206\061\207\061\310\071\310\071\207\061\206\061\247\061\310\071\310\071\247"
"\061\247\061\310\061\310\061\310\071\310\071\310\071\310\071\213R\212R\212RjJ\212"
"R\010B\207\061\206\061f)\206)\206\061\247\061\310\071\310\071\310\071\247\061\310"
"\071\350\071\350\071\350\071\350\071):\312\032\256\003p\004\262\004\221\014uu\377\377"
"\377\377\377\377\377\377\377\377\377\377}\357\030\306\327\275\367\275\323"
"|\357K\262d\323\204\023U\064]\064U\025U\064E\024M\024M\363l\023UQ<\360;q,\262\014"
"\322\004\023\015\222\014\263$-\003\015\013\323|\370\265\061Tq,q\014\222\004\323\004\262"
"\004\261,\222tY\276\361c\222,\060\024\015C\313Z\313Z\350A\014c-c\350A\253R-k\212"
"R\010B-c\014c\350\071\313Z-k\010B)J-k\252R\307\071\014c\014c\206\061\212R\354Z\010"
"B\307\071\354Z\212Rf\061\313Z\354Z\206\061)B\354ZJJ\350\071\313Z\252R\350\071"
"\212R\313Z)BIJ\253RIB\353\032\256\003q\004\262\004\221\014vu\377\377\377\377\377"
"\377\377\377\377\377\377\377\333\336\354Z\314b-c-c\347\071,[mk\317{\360\203"
"\360{\256s\215c\014c\353R,[N[j:q\\\262T\322\024\363\004\023\005\222\014r\064-Cn[\024"
"\235\327\275\257kMC\017$\261$\262$\357;\014K\222\224\226\265\357{\014K\215C"
"j\062Q\214\323\234\303\030\024\245\226\265\307\071\317{v\255\020\204\010B\226\265"
"\024\245\243\020\263\224\226\265\212R\256sv\265q\214e)U\255U\255\344\030q\214"
"\226\265Mk\354Zv\255\263\224\344\030\065\245\226\265\307\071\060\204\226\265"
"\020\204(B\226\265\024\245\243\020\024\245\267\265\313Z\256su\255%!\312\022\317"
"\003q\004\262\004q\014vu\377\377\377\377\377\377\377\377\377\377\377\377\272\326"
"\354b\015c\314Z-cE)E)e)e)\205)\206\061\206\061\206)E)%)E!E)\206\061\347\071F!"
"m\013\323\014\023\005\262\024nK\364\234<\347\337\377\337\377\276\367U\245\212:"
"\060d\020d\317{\033\337\235\367\337\377\337\377z\316\354Z\015[\353Z\353Z\313"
"R\313Z\313R\313R\014[\354Z\313R\253R\313R\253R\313R\353R\253R\253R\253R\253"
"JjJ\253R\313Z\253R\253R\253R\313R\212J\213RjJ\252R\313R\313R\252RjJ\212J"
"\212JiJIBiJjJJBjJIBIBiJiJ\252J,\033\317\003q\004q\034q\024vu\377\377\377\377\377"
"\377\377\377\377\377\377\377\232\326\354b-c-c-cf\061\247\061\206\061f\061E)E"
")E)E)E)E)%)E)E)e)\344\040\211\"\221\014\064\005\262\034\222\214\236\377\067\306"
"\276\367\337\377}\357\232\326\263\224,;\216[]\347\273\326\034\347\337\377"
"\276\367\232\326\330\275\247\061e\021\242\010E!%\031E\021D\001e\001e\001e\001e\001e\001E\011"
"E\011D\011$\001e\001$\001d\001\255k\312Z\212ZIJD\001e\001E\001e\001d\001\003\001\004\001E\001D\001\004\011E"
"\001E\001\004\011\004\001d\001\004\001$\011e\001D\001\004\011D\001E\001\004\001\206\011,\003\317\003\220\004qD\222"
"D\226u\377\377\377\377\377\377\377\377\377\377\377\377Y\316\015c-cNk\015c\061"
"\204R\214\260{-c\015c\014c\014c\014c\313Z\253Z\253Z\213R\212RIBHB\364\234\257"
";\023\025\364$z\316\276\367\231\326<\357\236\357]\357\273\336\232\326\014KV"
"\245\337\377\033\337<\347\034\347}\357\333\336\374\336\354Z\252\032\010\032\347"
"\031I\032\007\022i\012i\012\007\012\007\022\007\012\007\012\347\021\010\032\007\032\247\031\206\031"
"%\011\007\032I:\212B\211:\353J(\022H\012i\002i\002H\002\206\031\206\021\010\022\307\021\206"
"\021\010\022\347\021\206\021\246\021(\022\206\021\246\021\007\012\306\021e\021\347\021\347"
"\021\206\021\010\022M\003m\003\256\013\020\034\221\024\226u\377\377\377\377\377\377\377"
"\377\377\377\377\377\030\306\015c.c\015c\014c\031\306\031\306\330\275\066\245\065"
"\245V\255V\255w\265V\255V\255v\265\062\214\025\245\263\224Lc\060\214\071\276"
"Q\034\363\034]\337\337\377\332\336<\347]\357<\357\373\346\333\336M[\030\306"
"\276\367\\\347]\357]\357\\\357\272\326\034\337\361{\215\033\216;m;\212\022\257"
"K\313\"\216+UuUuum\024u\064\205\023}\262|\323\204\226\235\030\256\367\235\370"
"\255Y\246\327\225z\266v\225\330\245\353\"\354\062\263l\370\255\071\256\070\236"
"\030\236z\276\273\276\266\225Y\266z\276\232\276\330\255\071\256\370\235\024"
"u\364\204\064u\064m\364|\065u-#-;nS\256;\020\024\266u\377\377\377\377\377\377"
"\377\377\377\377\377\377\327\275Nk-c\015c\354Zr\214s\214\062\204\021\204\062"
"\204R\214\263\224\324\234\364\234\364\234\065\245\223\224\365\234\263\224"
"\017\204\357{\071\306\360\063\262,\034\337\276\377\332\326\\\357Y\316<\357\374"
"\346\273\326MS\327\265\337\367\034\347]\357\373\336\373\336\373\336\232\316"
"\256kl\023uu\323l\317\063\263t\065}\256+\060\034p\014Q\014\216#\267\245\030\276\363"
"\234\327\275\070\266\070\256\070\256y\266u\205\327\225\232\276U\215\272\256"
"\060L\317S\024}\266\235\327\235\327\225\226\225\070\266\334\306\266\225\071\266"
"z\306]\327Y\266\266\225\370\225\256\023M\033/\024\020\024n\033n\033\014;Q\214T\245"
"\024\245PT\326\205\377\377\377\377\377\377\377\377\377\377\377\377\266\265"
"-k.c\015c\253Z\354Z\015c\354Z\314Z\314Z\253R\314Z\314Z\314Z\213R\213R\014[N"
"kLk\315\203r\224Z\306\324L\060\064\025\245\231\316Y\316\030\306\030\306\327\275"
"\226\255\222\214\350!r\214\071\276\030\306\370\275\327\265\226\255u\245\262"
"\214\347)M\023\267\205\027~\360\033\064m\070\246q<\317+\262D\221\014\360\033\363"
"|\061|\216kQ\204\364\204\327\225\367\215\326}\266\205\266\205\226\205u\205"
"\226\205\357\063\020L\323T\226\215u\205\267\205\267\205u\215\226\215\266}\226"
"\215\226\205\266\205u\205v\215\225m\017\014o#P\024Q\024\257#\216#qt\232\316<"
"\347\034\337v\235\326}\377\377\377\377\377\377\377\377\377\377\377\377\226"
"\265-c\015c\015c\314Zr\214\223\214\062\204\361{\360{\320s\260s\320{nknknk\014"
"c\355b\216s\017\214\065\235\256K\061\034\262\064Nc\064\235\364\234\363\224\263"
"\224\263\214\222\224\253R(*\252B\323\234\323\224\323\224\263\224r\214R\204"
"IB\205!\215+\327\205\030~\222\024\363d\030\236\020,Q<\070\236\023E\221\024\060\034"
"\216\023\222\064\262L\023M\363D\322D\323D\362<q$Vu\324l\221\014r\024\322$\221"
"\014\064mZ\266Ue\060$\065}\370}P\014v\215\327\215\322\064\364t\030\236veq\064\071"
"\256y\226P\034\064u\071\246\024\205>\357~\367~\367\023u\327\205\377\377\377\377"
"\377\377\377\377\377\377\377\377\024\245\015c\355b\015c\313Z\061\204\263\224"
"r\204\223\214s\214s\214\324\224\025\235\223\214\264\224\025\235\320sok\360"
"{\211J\313*\017\014R\014q$\015K\354ZU\245\267\275\226\255\262\204\313R\350\061"
"i*\313:)B\222\214\266\255\370\275q\204I:%\031H*\020T\225}\327u\262\014\262\\"
"\226}\262,\317\033\030\216\064e\262\024\323l\024u\266\205\337\347]\347}\337\\"
"\327}\337\277\357\363DUm\065u\262\034\222\004\262\004\221\014\322\\\370\255uer,"
"\025}vu\220\004\222d\023m\262\064\363\\\020TUe\060\014\323t\064m\262\034\226\205\363"
"l\060\064u\205Y\266\363t\322<\071\216\377\377\377\377\377\377\377\377\377\377"
"\377\377\323\234\354b\015c\015c\313Z\350\071JJJJJJJJJJ\212R\253Z\314Z\314Z\014"
"c\015c\314ZJJ\343\020,\003\020\014\263\004\222\024\257#\214\013\353\"\323\204v\245"
"\010\022\307\011(\002M\003\215\013i\012\252\022\256c\030\276\313:\007\002H\002L\013\020\014\267"
"u\367u\222\024\323ly\246\363$\020\034v}\065]\262\034\226\205\323l\326\215\236"
"\357\373\306\333\306Y\266=\327\277\347\363D\025u\263T\321\014\262\014\322\004"
"\322\004\024e\272\266ueQ\034y\246Y\236\221\024\267\225\272\256\262$\364t\272\276"
"\226eq,Y\256Y\236\221\024\327\225\273\266\363,R\024r$\064E\262,\232\256\377"
"\377\377\377\377\377\377\377\377\377\377\377r\224\253Z\314Z\314ZjR\212R\314"
"Z\353Z\354Z\354b\354Z\354b\354Z\354b\314b\313Z\253Z\212RjJ\307\021M\013N\023"
"\317#Q\034\223\064q\034\020\024M+L+\252\022\212\032L\033\357\033\017\024m+\256++\033"
"\256\023K\013,\013\215\033\060\024\221\004\225]\226]\262\034\363\\\330\225\362$\020"
"\034\024]\024U\322\034UuR\\\367\205\236\357\237\357\276\357\276\357\236\357\276"
"\347\023=Um\263T\322\034\362\004\363\004\362\004\060,U}\266]P\014\323d\027~q\024\060D"
"\367\215\322$\020,U\205\265U\060\034\323l\027~\322\024QL\367\215\023\035\261\004\222"
"\014q\014\262<\034\317\377\377\377\377\377\377\377\377\377\377\377\377\034\347"
"\232\326\071\316\266\255-S,CH*\251R,k\211JHRj:\253J\323\234U\255\364\244\360"
"\203\216s\212\062i\002\353\"\317c\020|q|\020\064\262$r\034\262Dz\316\020\204\212"
"R\354RMS\014K\360{\373\326\267\265\017\004\316#\014\063\314:\216;\060\064QT\364l"
"\020D\317;\223l\222L\216\063\262\\r\\\263TPL\061L\266\215=\337\035\337=\337\035"
"\337=\337=\337\323\\\061LQL\262LQ,\060$Q,\357\063\320K\263TQD\360CrT\222L\360"
"C\020T\262L\360;\060L\262T\061L\320C\222\\\262L\320CQT\263TQD\061\024\221\064\030"
"\256=\347\377\377\377\377\377\377\377\377\377\377\377\377\337\377\276\367"
"]\367\232\326\017Tql\007\002\350Jo\224\355{gJh\012mK\332\326X\306\070\316\226\275"
"\262\234LC\252\002\060lT\255\370\305\071\306\024\205\322\024\263\034\262TY\306u"
"\265IJIJIJ\212R\212ZP\214\323\234\221\004\256;\313J-c\253RJB\015c\314RjB\014"
"[\015c\212B\313J,[\313R\212B\014[\015[JB\313Z\014[\253R\253R\015[\314RkJ\313R"
"\014[JBI:\212B\212B\212B\354R\313J\212J\253R\253JI:JJ\253R\252B)B\213JiBj"
"B\213R\253J\212B):\212B*BjBP\064\327\245\333\336\236\367\377\377\377\377\377"
"\377\377\377\377\377\377\377\337\377\276\367}\357\373\326\222trd\210\002\310"
"*\010S\351Z\311:\013\013,+\327\265\030\306\370\275\030\276\063\225\014#M\013\323"
"\214\272\326\034\347<\347\367\235\323\014\064\015q<}\347\030\306(B\211RjRiR\353"
"bt\255\326\255\322\014\360;\243\030\000\010%!\005!A\000\203\030E)a\010\202\020%)\344"
"\040\040\010\344\030%)A\010\201\010\206\061\304\030b\010E)\004!a\010\303\030\206\061\202"
"\020a\020e))JJJ\213R\247\071a\010b\020F)\243\020A\010\004!\004!b\010\242\030E)\202\020"
"a\020%!\344\030A\000\344\040%!\201\010\303\020):\065\205\272\326\236\357\277\377"
"\377\377\377\377\377\377\377\377\377\377\377\377\337\377\276\367}\367\034"
"\337\024}\323l+\013m\033\256\063\357;\357\033\357\023\357\023\356;\027\256\231\316"
"\327\245\256\063\356\023/\014\060T\373\326\235\357]\347\362T\024\025\063\005\262,"
"\367\235U\215\256[\353:\252\062\252:\013Kt\215\267\215\023\025\360;IBjJ)B\307"
"\071\212JjJf\061\212JjJ\350\071\010:jJIB\206)IBIB\307\071(B)J\010B\247\071)B\310"
"\071\247\071)B)B\247\061\010:IB\347\071\307\061\010:\350\071\206\061\307\061\010:\206"
"\061f)\010:\247\061\206)\207\061\247\061\247\061f)\307\071f\061f\061\247\071\247\071"
"J:\367\245]\357\276\367\337\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\337\377\276\367\235\357]\357\266\215P$\060,\060$P$P$Q$q$Q\034q\034\222\064"
"\221\064\261\064q\034q\034\222\034\222\014\362\064T]\023\065\023\035\363\024\363\024\363"
"\034\023%\362,\221\034q\024P\024P\024P\024\322$\023%\063\015\221$\256\063\216\063\255"
"\063l+\255\063\255\063m+\316;\256\063\215+\255\063\316;\256\063\215#\256;\316\063"
"\262t:\306Z\276Z\276Z\276Z\276Z\276\316;\215;m\063M+,#,#\014#L\063m;m\063,+M"
"\063M\063,+,+M\063L\063L\063M\063L\063,\063,+,\063,+,\063M\063M\063\255\063\272\306\235"
"\367\276\367\337\377\377\377\377\377\377\377\377\377\377\377\377\377\337"
"\377\337\377\276\367]\367<\337\070\266\225\225\225\225\226\235\266\235\266"
"\235\326\235\266\235\266\235\265\225\266\235\266\235\266\235\266\235\326"
"\225\326\235\027\236x\246\071\266\030\246\367\235\367\235\327\245\367\235\367"
"\245\367\235\367\235\367\235\370\235\370\235\370\235\030\236\370\245\367\245"
"\326\245\266\235\266\245\266\245\266\245\266\245\266\235\266\235\266\235"
"\266\245\266\245\266\245\266\245\266\245\266\235\327\235\367\255y\306y\306"
"z\306z\306\232\306y\306\266\245\266\235\266\245\266\235\266\235\226\235\226"
"\235\225\235\226\235\225\235\225\235u\235u\235u\235u\235u\225u\225u\225u"
"\225U\225u\225U\225U\225T\225T\225\064\225\064\215\266\225\034\347\236\367\276"
"\367\337\377"//,
// };
;

View File

@@ -0,0 +1,96 @@
/*******************************************************************************
* PROGMEM Image Viewer
* This is a simple PROGMEM bitmap image viewer example
* Image Source: https://support.arduino.cc/hc/en-us/articles/360020652100
*
* How to create custom image:
* 1. Open image in the GIMP
* 2. Resize image to fit for the display and MCU memory
* 3. Export Image as C-Source
* 4. Uncheck all option and check "Save as RGB565 (16-bit)"
* 5. Revise exported file just like "Arduino_UNO_Rev3_Ok.c"
******************************************************************************/
#include "Arduino_UNO_Rev3_Ok.c"
#define IMG_WIDTH 100
#define IMG_HEIGHT 71
/*******************************************************************************
* 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-C2/3 various dev board: CS: 7, DC: 2, RST: 1, BL: 3, SCK: 4, MOSI: 6, MISO: nil
* ESP32-C5 various dev board : CS: 23, DC: 24, RST: 25, BL: 26, SCK: 10, MOSI: 8, MISO: nil
* ESP32-C6 various dev board : CS: 18, DC: 22, RST: 23, BL: 15, SCK: 21, MOSI: 19, MISO: nil
* ESP32-H2 various dev board : CS: 0, DC: 12, RST: 8, BL: 22, SCK: 10, MOSI: 25, MISO: nil
* ESP32-P4 various dev board : CS: 26, DC: 27, RST: 25, BL: 24, SCK: 36, MOSI: 32, 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) */
/*******************************************************************************
* End of Arduino_GFX setting
******************************************************************************/
void setup()
{
#ifdef DEV_DEVICE_INIT
DEV_DEVICE_INIT();
#endif
Serial.begin(115200);
// Serial.setDebugOutput(true);
// while(!Serial);
Serial.println("Arduino_GFX PROGMEM Image Viewer example");
// Init Display
if (!gfx->begin())
{
Serial.println("gfx->begin() failed!");
}
gfx->fillScreen(RGB565_BLACK);
#ifdef GFX_BL
pinMode(GFX_BL, OUTPUT);
digitalWrite(GFX_BL, HIGH);
#endif
gfx->draw16bitRGBBitmap(0, 0, (const uint16_t *)Arduino_UNO_Rev3_Ok, IMG_WIDTH, IMG_HEIGHT);
delay(5000); // 5 seconds
}
void loop()
{
int16_t x = random(gfx->width());
int16_t y = random(gfx->height());
gfx->draw16bitRGBBitmap(x, y, (const uint16_t *)Arduino_UNO_Rev3_Ok, IMG_WIDTH, IMG_HEIGHT);
delay(1000); // 1 second
}

View File

@@ -0,0 +1,282 @@
/*******************************************************************************
* PNG Image Viewer
* This is a simple PNG image viewer example
* Image Source: https://github.com/logos
*
* Dependent libraries:
* PNGdec: https://github.com/bitbank2/PNGdec.git
*
* Setup steps:
* 1. Change your LCD parameters in Arduino_GFX setting
* 2. Upload PNG 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 PNG_FILENAME "/octocat.png"
#define PNG_4BPP_FILENAME "/octocat-4bpp.png"
/*******************************************************************************
* 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-C2/3 various dev board: CS: 7, DC: 2, RST: 1, BL: 3, SCK: 4, MOSI: 6, MISO: nil
* ESP32-C5 various dev board : CS: 23, DC: 24, RST: 25, BL: 26, SCK: 10, MOSI: 8, MISO: nil
* ESP32-C6 various dev board : CS: 18, DC: 22, RST: 23, BL: 15, SCK: 21, MOSI: 19, MISO: nil
* ESP32-H2 various dev board : CS: 0, DC: 12, RST: 8, BL: 22, SCK: 10, MOSI: 25, MISO: nil
* ESP32-P4 various dev board : CS: 26, DC: 27, RST: 25, BL: 24, SCK: 36, MOSI: 32, 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) */
/*******************************************************************************
* 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) || defined(PICO_RP2350)
#include <LittleFS.h>
#include <SD.h>
#elif defined(ESP32)
#include <FFat.h>
#include <LittleFS.h>
#include <SPIFFS.h>
#include <SD.h>
#include <SD_MMC.h>
#elif defined(ESP8266)
#include <LittleFS.h>
#include <SD.h>
#else
#include <SD.h>
#endif
#include <PNGdec.h>
PNG png;
int16_t w, h, xOffset, yOffset;
// Functions to access a file on the SD card
File pngFile;
void *myOpen(const char *filename, int32_t *size)
{
/* Wio Terminal */
#if defined(ARDUINO_ARCH_SAMD) && defined(SEEED_GROVE_UI_WIRELESS)
pngFile = SD.open(filename, "r");
#elif defined(TARGET_RP2040) || defined(PICO_RP2350)
pngFile = LittleFS.open(filename, "r");
// pngFile = SD.open(filename, "r");
#elif defined(ESP32)
// pngFile = FFat.open(filename, "r");
pngFile = LittleFS.open(filename, "r");
// pngFile = SPIFFS.open(filename, "r");
// pngFile = SD.open(filename, "r");
// pngFile = SD_MMC.open(filename, "r");
#elif defined(ESP8266)
pngFile = LittleFS.open(filename, "r");
// pngFile = SD.open(filename, "r");
#else
pngFile = SD.open(filename, FILE_READ);
#endif
if (!pngFile || pngFile.isDirectory())
{
Serial.println(F("ERROR: Failed to open " PNG_FILENAME " file for reading"));
gfx->println(F("ERROR: Failed to open " PNG_FILENAME " file for reading"));
}
else
{
*size = pngFile.size();
Serial.printf("Opened '%s', size: %d\n", filename, *size);
}
return &pngFile;
}
void myClose(void *handle)
{
if (pngFile)
pngFile.close();
}
int32_t myRead(PNGFILE *handle, uint8_t *buffer, int32_t length)
{
if (!pngFile)
return 0;
return pngFile.read(buffer, length);
}
int32_t mySeek(PNGFILE *handle, int32_t position)
{
if (!pngFile)
return 0;
return pngFile.seek(position);
}
// Function to draw pixels to the display
void PNGDraw(PNGDRAW *pDraw)
{
uint16_t usPixels[320];
uint8_t usMask[320];
// Serial.printf("Draw pos = 0,%d. size = %d x 1\n", pDraw->y, pDraw->iWidth);
png.getLineAsRGB565(pDraw, usPixels, PNG_RGB565_LITTLE_ENDIAN, 0x00000000);
png.getAlphaMask(pDraw, usMask, 1);
gfx->draw16bitRGBBitmapWithMask(xOffset, yOffset + pDraw->y, usPixels, usMask, pDraw->iWidth, 1);
}
void setup()
{
#ifdef DEV_DEVICE_INIT
DEV_DEVICE_INIT();
#endif
Serial.begin(115200);
// Serial.setDebugOutput(true);
// while(!Serial);
Serial.println("Arduino_GFX PNG Image Viewer example");
// Init Display
if (!gfx->begin())
{
Serial.println("gfx->begin() failed!");
}
gfx->fillScreen(RGB565_BLACK);
w = gfx->width(), h = gfx->height();
gfx->fillScreen(RGB565_BLACK);
for (int16_t x = 0; x < w; x += 5)
{
gfx->drawFastVLine(x, 0, h, RGB565_LIGHTPINK);
}
#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) || defined(PICO_RP2350)
if (!LittleFS.begin())
// if (!SD.begin(SS))
#elif defined(ESP32)
// if (!FFat.begin())
if (!LittleFS.begin())
// if (!SPIFFS.begin())
// SPI.begin(12 /* CLK */, 13 /* D0/MISO */, 11 /* CMD/MOSI */);
// if (!SD.begin(10 /* CS */, SPI))
// pinMode(10 /* CS */, OUTPUT);
// digitalWrite(SD_CS, HIGH);
// SD_MMC.setPins(12 /* CLK */, 11 /* CMD/MOSI */, 13 /* D0/MISO */);
// if (!SD_MMC.begin("/root", true /* mode1bit */, false /* format_if_mount_failed */, SDMMC_FREQ_DEFAULT))
// SD_MMC.setPins(12 /* CLK */, 11 /* CMD/MOSI */, 13 /* D0/MISO */, 14 /* D1 */, 15 /* D2 */, 10 /* D3/CS */);
// if (!SD_MMC.begin("/root", false /* mode1bit */, false /* format_if_mount_failed */, SDMMC_FREQ_HIGHSPEED))
#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!"));
}
else
{
unsigned long start = millis();
int rc;
rc = png.open(PNG_FILENAME, myOpen, myClose, myRead, mySeek, PNGDraw);
if (rc == PNG_SUCCESS)
{
int16_t pw = png.getWidth();
int16_t ph = png.getHeight();
xOffset = (w - pw) / 2;
yOffset = (h - ph) / 2;
rc = png.decode(NULL, 0);
Serial.printf("Draw offset: (%d, %d), time used: %lu\n", xOffset, yOffset, millis() - start);
Serial.printf("image specs: (%d x %d), %d bpp, pixel type: %d\n", png.getWidth(), png.getHeight(), png.getBpp(), png.getPixelType());
png.close();
}
else
{
Serial.println("png.open() failed!");
}
}
delay(5000); // 5 seconds
}
void loop()
{
unsigned long start = millis();
int rc;
rc = png.open(PNG_4BPP_FILENAME, myOpen, myClose, myRead, mySeek, PNGDraw);
if (rc == PNG_SUCCESS)
{
// random draw position
int16_t pw = png.getWidth();
int16_t ph = png.getHeight();
xOffset = random(w) - (pw / 2);
yOffset = random(h) - (ph / 2);
rc = png.decode(NULL, 0);
Serial.printf("Draw offset: (%d, %d), time used: %lu\n", xOffset, yOffset, millis() - start);
Serial.printf("image specs: (%d x %d), %d bpp, pixel type: %d\n", png.getWidth(), png.getHeight(), png.getBpp(), png.getPixelType());
png.close();
}
else
{
Serial.println("png.open() failed!");
}
delay(1000); // 1 second
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 868 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -0,0 +1,253 @@
/*******************************************************************************
* TIFF Image Viewer
* This is a simple TIFF image viewer example
* Image Source: https://people.math.sc.edu/Burkardt/data/tif/tif.html
* ImageMagick script: convert at3_1m4_01.tif -threshold 40% -compress group4 at3_1m4_01_g4.tif
*
* Dependent libraries:
* TIFF_G4: https://github.com/bitbank2/TIFF_G4.git
*
* Setup steps:
* 1. Change your LCD parameters in Arduino_GFX setting
* 2. Upload TIFF 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 TIFF_FILENAME "/at3_1m4_01_g4.tif"
/*******************************************************************************
* 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-C2/3 various dev board: CS: 7, DC: 2, RST: 1, BL: 3, SCK: 4, MOSI: 6, MISO: nil
* ESP32-C5 various dev board : CS: 23, DC: 24, RST: 25, BL: 26, SCK: 10, MOSI: 8, MISO: nil
* ESP32-C6 various dev board : CS: 18, DC: 22, RST: 23, BL: 15, SCK: 21, MOSI: 19, MISO: nil
* ESP32-H2 various dev board : CS: 0, DC: 12, RST: 8, BL: 22, SCK: 10, MOSI: 25, MISO: nil
* ESP32-P4 various dev board : CS: 26, DC: 27, RST: 25, BL: 24, SCK: 36, MOSI: 32, 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) */
/*******************************************************************************
* 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) || defined(PICO_RP2350)
#include <LittleFS.h>
#include <SD.h>
#elif defined(ESP32)
#include <FFat.h>
#include <LittleFS.h>
#include <SPIFFS.h>
#include <SD.h>
#include <SD_MMC.h>
#elif defined(ESP8266)
#include <LittleFS.h>
#include <SD.h>
#else
#include <SD.h>
#endif
#include <TIFF_G4.h>
TIFFG4 tiff;
int16_t display_width, display_height;
const uint16_t us2BppToRGB565[4] = {0xffff, 0xa514, 0x528a, 0x0000};
uint8_t *tiffBuf;
void TIFFDraw(TIFFDRAW *pDraw)
{
// Serial.println("TIFFDraw");
uint8_t c, *src;
int i, l;
uint16_t usTemp[640], *d;
if (pDraw->y >= display_height)
{
return; // beyond bottom of the display
}
src = pDraw->pPixels;
if (pDraw->ucPixelType == TIFF_PIXEL_1BPP)
{
// Serial.printf("TIFF_PIXEL_1BPP, pDraw->iScaledWidth: %d\n", pDraw->iScaledWidth);
memset(usTemp, 0, sizeof(usTemp)); // start with black
for (i = 0; i < (pDraw->iScaledWidth + 7) / 8; i++)
{
d = &usTemp[i * 8];
c = *src++;
while (c)
{
if (c & 0x80)
*d = 0xffff;
c <<= 1;
d++;
}
}
}
else if (pDraw->ucPixelType == TIFF_PIXEL_2BPP) // 2-bit gray anti-aliased pixels
{
// Serial.printf("TIFF_PIXEL_2BPP, pDraw->iScaledWidth: %d\n", pDraw->iScaledWidth);
d = usTemp;
l = pDraw->iScaledWidth;
for (i = 0; i < l; i++)
{
if ((i & 0b11) == 0)
{
c = *src++;
}
*d++ = us2BppToRGB565[c >> 6];
c <<= 2;
}
}
gfx->draw16bitBeRGBBitmap(0, pDraw->y, usTemp, pDraw->iScaledWidth, 1);
} /* TIFFDraw() */
void setup()
{
#ifdef DEV_DEVICE_INIT
DEV_DEVICE_INIT();
#endif
Serial.begin(115200);
// Serial.setDebugOutput(true);
// while(!Serial);
Serial.println("Arduino_GFX TIFF Image Viewer example");
// Init Display
if (!gfx->begin())
{
Serial.println("gfx->begin() failed!");
}
gfx->fillScreen(RGB565_BLACK);
#ifdef GFX_BL
pinMode(GFX_BL, OUTPUT);
digitalWrite(GFX_BL, HIGH);
#endif
display_width = gfx->width();
display_height = gfx->height();
/* 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) || defined(PICO_RP2350)
if (!LittleFS.begin())
// if (!SD.begin(SS))
#elif defined(ESP32)
// if (!FFat.begin())
if (!LittleFS.begin())
// if (!SPIFFS.begin())
// SPI.begin(12 /* CLK */, 13 /* D0/MISO */, 11 /* CMD/MOSI */);
// if (!SD.begin(10 /* CS */, SPI))
// pinMode(10 /* CS */, OUTPUT);
// digitalWrite(10 /* CS */, HIGH);
// SD_MMC.setPins(12 /* CLK */, 11 /* CMD/MOSI */, 13 /* D0/MISO */);
// if (!SD_MMC.begin("/root", true /* mode1bit */, false /* format_if_mount_failed */, SDMMC_FREQ_DEFAULT))
// SD_MMC.setPins(12 /* CLK */, 11 /* CMD/MOSI */, 13 /* D0/MISO */, 14 /* D1 */, 15 /* D2 */, 10 /* D3/CS */);
// if (!SD_MMC.begin("/root", false /* mode1bit */, false /* format_if_mount_failed */, SDMMC_FREQ_HIGHSPEED))
#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!"));
}
else
{
/* Wio Terminal */
#if defined(ARDUINO_ARCH_SAMD) && defined(SEEED_GROVE_UI_WIRELESS)
File tiffFile = SD.open(TIFF_FILENAME, "r");
#elif defined(TARGET_RP2040) || defined(PICO_RP2350)
File tiffFile = LittleFS.open(TIFF_FILENAME, "r");
// File tiffFile = SD.open(TIFF_FILENAME, "r");
#elif defined(ESP32)
// File tiffFile = FFat.open(TIFF_FILENAME, "r");
File tiffFile = LittleFS.open(TIFF_FILENAME, "r");
// File tiffFile = SPIFFS.open(TIFF_FILENAME, "r");
// File tiffFile = SD.open(TIFF_FILENAME, "r");
#elif defined(ESP8266)
File tiffFile = LittleFS.open(TIFF_FILENAME, "r");
// File tiffFile = SD.open(TIFF_FILENAME, "r");
#else
File tiffFile = SD.open(TIFF_FILENAME, FILE_READ);
#endif
if (!tiffFile || tiffFile.isDirectory())
{
Serial.println(F("ERROR: Failed to open " TIFF_FILENAME " file for reading"));
gfx->println(F("ERROR: Failed to open " TIFF_FILENAME " file for reading"));
}
else
{
size_t fileSize = tiffFile.size();
tiffBuf = (uint8_t *)malloc(fileSize);
tiffFile.read(tiffBuf, fileSize);
if (!tiff.openTIFF(tiffBuf, fileSize, TIFFDraw))
{
Serial.println(F("ERROR: Failed to decode " TIFF_FILENAME " file"));
gfx->println(F("ERROR: Failed to decode " TIFF_FILENAME " file"));
}
else
{
int cx = tiff.getWidth(); // scale the image to fit the display
float fScale = (float)display_width / (float)cx;
// Use TIFF_PIXEL_1BPP for bitonal only, 2BPP for anti-aliased grayscale
tiff.setDrawParameters(fScale, TIFF_PIXEL_2BPP, 0, 0, display_width * 2, display_height * 2, NULL);
tiff.decode();
tiff.close();
}
}
}
}
void loop()
{
}