first commit

This commit is contained in:
Alejandro Guerrero 2026-09-27 17:21:39 +02:00
commit e6a42e28e9
4355 changed files with 4626605 additions and 0 deletions

View file

@ -0,0 +1,181 @@
#define LGFX_USE_V1
#include <LovyanGFX.hpp>
static LGFX lcd;
static LGFX_Sprite canvas(&lcd); // オフスクリーン描画用バッファ
static LGFX_Sprite clockbase(&canvas); // 時計の文字盤パーツ
static LGFX_Sprite needle1(&canvas); // 長針・短針パーツ
static LGFX_Sprite shadow1(&canvas); // 長針・短針の影パーツ
static LGFX_Sprite needle2(&canvas); // 秒針パーツ
static LGFX_Sprite shadow2(&canvas); // 秒針の影パーツ
static constexpr uint64_t oneday = 86400000; // 1日 = 1000msec x 60sec x 60min x 24hour = 86400000
static uint64_t count = rand() % oneday; // 現在時刻 (ミリ秒カウンタ)
static int32_t width = 239; // 時計の縦横画像サイズ
static int32_t halfwidth = width >> 1; // 時計盤の中心座標
static auto transpalette = 0; // 透過色パレット番号
static float zoom; // 表示倍率
#ifdef min
#undef min
#endif
void setup(void)
{
lcd.init();
zoom = (float)(std::min(lcd.width(), lcd.height())) / width; // 表示が画面にフィットするよう倍率を調整
lcd.setPivot(lcd.width() >> 1, lcd.height() >> 1); // 時計描画時の中心を画面中心に合わせる
canvas.setColorDepth(lgfx::palette_4bit); // 各部品を4ビットパレットモードで準備する
clockbase.setColorDepth(lgfx::palette_4bit);
needle1.setColorDepth(lgfx::palette_4bit);
shadow1.setColorDepth(lgfx::palette_4bit);
needle2.setColorDepth(lgfx::palette_4bit);
shadow2.setColorDepth(lgfx::palette_4bit);
// パレットの初期色はグレースケールのグラデーションとなっており、
// 0番が黒(0,0,0)、15番が白(255,255,255)
// 1番~14番は黒から白へ段階的に明るさが変化している
//
// パレットを使う場合、描画関数は色の代わりに0~15のパレット番号を指定する
canvas.createSprite(width, width); // メモリ確保
clockbase.createSprite(width, width);
needle1.createSprite(9, 119);
shadow1.createSprite(9, 119);
needle2.createSprite(3, 119);
shadow2.createSprite(3, 119);
canvas.fillScreen(transpalette); // 透過色で背景を塗り潰す (create直後は0埋めされているので省略可能)
clockbase.fillScreen(transpalette);
needle1.fillScreen(transpalette);
shadow1.fillScreen(transpalette);
clockbase.setTextFont(4); // フォント種類を変更(時計盤の文字用)
clockbase.setTextDatum(lgfx::middle_center);
clockbase.fillCircle(halfwidth, halfwidth, halfwidth , 6); // 時計盤の背景の円を塗る
clockbase.drawCircle(halfwidth, halfwidth, halfwidth - 1, 15);
for (int i = 1; i <= 60; ++i) {
float rad = i * 6 * - 0.0174532925; // 時計盤外周の目盛り座標を求める
float cosy = - cos(rad) * (halfwidth * 10 / 11);
float sinx = - sin(rad) * (halfwidth * 10 / 11);
bool flg = 0 == (i % 5); // 5目盛り毎フラグ
clockbase.fillCircle(halfwidth + sinx + 1, halfwidth + cosy + 1, flg * 3 + 1, 4); // 目盛りを描画
clockbase.fillCircle(halfwidth + sinx , halfwidth + cosy , flg * 3 + 1, 12);
if (flg) { // 文字描画
cosy = - cos(rad) * (halfwidth * 10 / 13);
sinx = - sin(rad) * (halfwidth * 10 / 13);
clockbase.setTextColor(1);
clockbase.drawNumber(i/5, halfwidth + sinx + 1, halfwidth + cosy + 4);
clockbase.setTextColor(15);
clockbase.drawNumber(i/5, halfwidth + sinx , halfwidth + cosy + 3);
}
}
clockbase.setTextFont(7);
needle1.setPivot(4, 100); // 針パーツの回転中心座標を設定する
shadow1.setPivot(4, 100);
needle2.setPivot(1, 100);
shadow2.setPivot(1, 100);
for (int i = 6; i >= 0; --i) { // 針パーツの画像を作成する
needle1.fillTriangle(4, - 16 - (i<<1), 8, needle1.height() - (i<<1), 0, needle1.height() - (i<<1), 15 - i);
shadow1.fillTriangle(4, - 16 - (i<<1), 8, shadow1.height() - (i<<1), 0, shadow1.height() - (i<<1), 1 + i);
}
for (int i = 0; i < 7; ++i) {
needle1.fillTriangle(4, 16 + (i<<1), 8, needle1.height() + 32 + (i<<1), 0, needle1.height() + 32 + (i<<1), 15 - i);
shadow1.fillTriangle(4, 16 + (i<<1), 8, shadow1.height() + 32 + (i<<1), 0, shadow1.height() + 32 + (i<<1), 1 + i);
}
needle1.fillTriangle(4, 32, 8, needle1.height() + 64, 0, needle1.height() + 64, 0);
shadow1.fillTriangle(4, 32, 8, shadow1.height() + 64, 0, shadow1.height() + 64, 0);
needle1.fillRect(0, 117, 9, 2, 15);
shadow1.fillRect(0, 117, 9, 2, 1);
needle1.drawFastHLine(1, 117, 7, 12);
shadow1.drawFastHLine(1, 117, 7, 4);
needle1.fillCircle(4, 100, 4, 15);
shadow1.fillCircle(4, 100, 4, 1);
needle1.drawCircle(4, 100, 4, 14);
needle2.fillScreen(9);
shadow2.fillScreen(3);
needle2.drawFastVLine(1, 0, 119, 8);
shadow2.drawFastVLine(1, 0, 119, 1);
needle2.fillRect(0, 99, 3, 3, 8);
lcd.startWrite();
// shadow1.pushSprite(&lcd, 0, 0); // デバッグ用、パーツを直接LCDに描画する
// needle1.pushSprite(&lcd, 10, 0);
// shadow2.pushSprite(&lcd, 20, 0);
// needle2.pushSprite(&lcd, 25, 0);
}
void update7Seg(int32_t hour, int32_t min)
{ // 時計盤のデジタル表示部の描画
int x = clockbase.getPivotX() - 69;
int y = clockbase.getPivotY();
clockbase.setCursor(x, y);
clockbase.setTextColor(5); // 消去色で 88:88 を描画する
clockbase.print("88:88");
clockbase.setCursor(x, y);
clockbase.setTextColor(12); // 表示色で時:分を描画する
clockbase.printf("%02d:%02d", hour, min);
}
void drawDot(int pos, int palette)
{
bool flg = 0 == (pos % 5); // 5目盛り毎フラグ
float rad = pos * 6 * - 0.0174532925; // 時計盤外周の目盛り座標を求める
float cosy = - cos(rad) * (halfwidth * 10 / 11);
float sinx = - sin(rad) * (halfwidth * 10 / 11);
canvas.fillCircle(halfwidth + sinx, halfwidth + cosy, flg * 3 + 1, palette);
}
void drawClock(uint64_t time)
{ // 時計の描画
static int32_t p_min = -1;
int32_t sec = time / 1000;
int32_t min = sec / 60;
if (p_min != min) { // 分の値が変化していれば時計盤のデジタル表示部分を更新
p_min = min;
update7Seg(min / 60, min % 60);
}
clockbase.pushSprite(0, 0); // 描画用バッファに時計盤の画像を上書き
drawDot(sec % 60, 14);
drawDot(min % 60, 15);
drawDot(((min/60)*5)%60, 15);
float fhour = (float)time / 120000; // 短針の角度
float fmin = (float)time / 10000; // 長針の角度
float fsec = (float)time*6 / 1000; // 秒針の角度
int px = canvas.getPivotX();
int py = canvas.getPivotY();
shadow1.pushRotateZoom(px+2, py+2, fhour, 1.0, 0.7, transpalette); // 針の影を右下方向にずらして描画する
shadow1.pushRotateZoom(px+3, py+3, fmin , 1.0, 1.0, transpalette);
shadow2.pushRotateZoom(px+4, py+4, fsec , 1.0, 1.0, transpalette);
needle1.pushRotateZoom( fhour, 1.0, 0.7, transpalette); // 針を描画する
needle1.pushRotateZoom( fmin , 1.0, 1.0, transpalette);
needle2.pushRotateZoom( fsec , 1.0, 1.0, transpalette);
canvas.pushRotateZoom(0, zoom, zoom, transpalette); // 完了した時計盤をLCDに描画する
lcd.display();
}
void loop(void)
{
static uint32_t p_milli = 0;
uint32_t milli = lgfx::millis() % 1000;
if (p_milli > milli) count += 1000 + (milli - p_milli);
else count += (milli - p_milli);
p_milli = milli;
int32_t tmp = (count % 1000) >> 3;
canvas.setPaletteColor(8, 255 - (tmp>>1), 255 - (tmp>>1), 200 - tmp); // 秒針の描画色を変化させる
//count += 60000;
if ( count > oneday ) { count -= oneday; }
drawClock(count);
}

View file

@ -0,0 +1,325 @@
#define LGFX_USE_V1
#include <LovyanGFX.hpp>
static LGFX lcd;
static LGFX_Sprite _sprites[2];
struct ball_info_t {
int32_t x;
int32_t y;
int32_t dx;
int32_t dy;
int32_t r;
int32_t m;
uint32_t color;
};
static constexpr std::uint32_t SHIFTSIZE = 8;
static constexpr std::uint32_t BALL_MAX = 256;
static ball_info_t _balls[2][BALL_MAX];
static std::uint32_t _ball_count = 0, _fps = 0;
static std::uint32_t ball_count = 0;
static std::uint32_t sec, psec;
static std::uint32_t fps = 0, frame_count = 0;
static std::uint32_t _width;
static std::uint32_t _height;
volatile bool _is_running;
volatile std::uint32_t _draw_count;
volatile std::uint32_t _loop_count;
static void diffDraw(LGFX_Sprite* sp0, LGFX_Sprite* sp1)
{
union
{
std::uint32_t* s32;
std::uint8_t* s;
};
union
{
std::uint32_t* p32;
std::uint8_t* p;
};
s32 = (std::uint32_t*)sp0->getBuffer();
p32 = (std::uint32_t*)sp1->getBuffer();
auto width = sp0->width();
auto height = sp0->height();
auto w32 = (width+3) >> 2;
std::int32_t y = 0;
do
{
std::int32_t x32 = 0;
do
{
while (s32[x32] == p32[x32] && ++x32 < w32);
if (x32 == w32) break;
std::int32_t xs = x32 << 2;
while (s[xs] == p[xs]) ++xs;
while (++x32 < w32 && s32[x32] != p32[x32]);
std::int32_t xe = (x32 << 2) - 1;
if (xe >= width) xe = width - 1;
while (s[xe] == p[xe]) --xe;
lcd.pushImage(xs, y, xe - xs + 1, 1, &s[xs]);
} while (x32 < w32);
s32 += w32;
p32 += w32;
} while (++y < height);
lcd.display();
}
static void drawfunc(void)
{
ball_info_t *balls;
ball_info_t *a;
LGFX_Sprite *sprite;
auto width = _sprites[0].width();
auto height = _sprites[0].height();
std::size_t flip = _draw_count & 1;
balls = &_balls[flip][0];
sprite = &(_sprites[flip]);
sprite->clear();
for (int32_t i = 8; i < width; i += 16) {
sprite->drawFastVLine(i, 0, height, 0x1F);
}
for (int32_t i = 8; i < height; i += 16) {
sprite->drawFastHLine(0, i, width, 0x1F);
}
for (std::uint32_t i = 0; i < _ball_count; i++) {
a = &balls[i];
sprite->fillCircle( a->x >> SHIFTSIZE
, a->y >> SHIFTSIZE
, a->r >> SHIFTSIZE
, a->color);
}
sprite->setCursor(1,1);
sprite->setTextColor(TFT_BLACK);
sprite->printf("obj:%d fps:%d", _ball_count, _fps);
sprite->setCursor(0,0);
sprite->setTextColor(TFT_WHITE);
sprite->printf("obj:%d fps:%d", _ball_count, _fps);
diffDraw(&_sprites[flip], &_sprites[!flip]);
++_draw_count;
}
static void mainfunc(void)
{
static constexpr float e = 0.999; // Coefficient of friction
sec = lgfx::millis() / 1000;
if (psec != sec) {
psec = sec;
fps = frame_count;
frame_count = 0;
if (++ball_count >= BALL_MAX) { ball_count = 1; }
auto a = &_balls[_loop_count & 1][ball_count - 1];
a->color = lgfx::color888(100+(rand()%155), 100+(rand()%155), 100+(rand()%155));
a->x = 0;
a->y = 0;
a->dx = (rand() & (3 << SHIFTSIZE)) + 1;
a->dy = (rand() & (3 << SHIFTSIZE)) + 1;
a->r = (4 + (ball_count & 0x07)) << SHIFTSIZE;
a->m = 4 + (ball_count & 0x07);
#if defined (ESP32) || defined (CONFIG_IDF_TARGET_ESP32) || defined (ESP_PLATFORM)
vTaskDelay(1);
#endif
}
frame_count++;
_loop_count++;
ball_info_t *a, *b, *balls;
int32_t rr, len, vx2vy2;
float vx, vy, distance, t;
size_t f = _loop_count & 1;
balls = a = &_balls[f][0];
b = &_balls[!f][0];
memcpy(a, b, sizeof(ball_info_t) * ball_count);
for (int i = 0; i != ball_count; i++) {
a = &balls[i];
// a->dy += 4; // gravity
a->x += a->dx;
if (a->x < a->r) {
a->x = a->r;
if (a->dx < 0) a->dx = - a->dx*e;
} else if (a->x >= _width - a->r) {
a->x = _width - a->r -1;
if (a->dx > 0) a->dx = - a->dx*e;
}
a->y += a->dy;
if (a->y < a->r) {
a->y = a->r;
if (a->dy < 0) a->dy = - a->dy*e;
} else if (a->y >= _height - a->r) {
a->y = _height - a->r -1;
if (a->dy > 0) a->dy = - a->dy*e;
}
for (int j = i + 1; j != ball_count; j++) {
b = &balls[j];
rr = a->r + b->r;
vx = a->x - b->x;
if (abs(vx) > rr) continue;
vy = a->y - b->y;
if (abs(vy) > rr) continue;
len = sqrt(vx * vx + vy * vy);
if (len >= rr) continue;
if (len == 0.0) continue;
distance = (rr - len) >> 1;
vx *= distance / len;
vy *= distance / len;
a->x += vx;
b->x -= vx;
vx = b->x - a->x;
a->y += vy;
b->y -= vy;
vy = b->y - a->y;
vx2vy2 = vx * vx + vy * vy;
t = -(vx * a->dx + vy * a->dy) / vx2vy2;
float arx = a->dx + vx * t;
float ary = a->dy + vy * t;
t = -(-vy * a->dx + vx * a->dy) / vx2vy2;
float amx = a->dx - vy * t;
float amy = a->dy + vx * t;
t = -(vx * b->dx + vy * b->dy) / vx2vy2;
float brx = b->dx + vx * t;
float bry = b->dy + vy * t;
t = -(-vy * b->dx + vx * b->dy) / vx2vy2;
float bmx = b->dx - vy * t;
float bmy = b->dy + vx * t;
float adx = (a->m * amx + b->m * bmx + bmx * e * b->m - amx * e * b->m) / (a->m + b->m);
float bdx = - e * (bmx - amx) + adx;
float ady = (a->m * amy + b->m * bmy + bmy * e * b->m - amy * e * b->m) / (a->m + b->m);
float bdy = - e * (bmy - amy) + ady;
a->dx = roundf(adx + arx);
a->dy = roundf(ady + ary);
b->dx = roundf(bdx + brx);
b->dy = roundf(bdy + bry);
}
}
_fps = fps;
_ball_count = ball_count;
}
#if defined (ESP32) || defined (CONFIG_IDF_TARGET_ESP32) || defined (ESP_PLATFORM)
static void taskDraw(void*)
{
while ( _is_running )
{
while (_loop_count == _draw_count) { taskYIELD(); }
drawfunc();
}
vTaskDelete(NULL);
}
#endif
void setup(void)
{
lcd.begin();
lcd.startWrite();
lcd.setColorDepth(8);
if (lcd.width() < lcd.height()) lcd.setRotation(lcd.getRotation() ^ 1);
auto lcd_width = lcd.width();
auto lcd_height = lcd.height();
for (std::uint32_t i = 0; i < 2; ++i)
{
_sprites[i].setTextSize(2);
_sprites[i].setColorDepth(8);
}
bool fail = false;
for (std::uint32_t i = 0; !fail && i < 2; ++i)
{
fail = !_sprites[i].createSprite(lcd_width, lcd_height);
}
if (fail)
{
fail = false;
for (std::uint32_t i = 0; !fail && i < 2; ++i)
{
_sprites[i].setPsram(true);
fail = !_sprites[i].createSprite(lcd_width, lcd_height);
}
if (fail)
{
fail = false;
if (lcd_width > 320) lcd_width = 320;
if (lcd_height > 240) lcd_height = 240;
for (std::uint32_t i = 0; !fail && i < 2; ++i)
{
_sprites[i].setPsram(true);
fail = !_sprites[i].createSprite(lcd_width, lcd_height);
}
if (fail)
{
lcd.print("createSprite fail...");
lgfx::delay(3000);
}
}
}
_width = lcd_width << SHIFTSIZE;
_height = lcd_height << SHIFTSIZE;
for (std::uint32_t i = 0; i < ball_count; ++i)
{
auto a = &_balls[_loop_count & 1][i];
a->color = lgfx::color888(100+(rand()%155), 100+(rand()%155), 100+(rand()%155));
a->x = 0;
a->y = 0;
a->dx = (rand() & (3 << SHIFTSIZE)) + 1;
a->dy = (rand() & (3 << SHIFTSIZE)) + 1;
a->r = (4 + (i & 0x07)) << SHIFTSIZE;
a->m = 4 + (i & 0x07);
}
_is_running = true;
_draw_count = 0;
_loop_count = 0;
#if defined (CONFIG_IDF_TARGET_ESP32)
xTaskCreate(taskDraw, "taskDraw", 2048, NULL, 0, NULL);
#endif
}
void loop(void)
{
mainfunc();
#if defined (CONFIG_IDF_TARGET_ESP32)
while (_loop_count != _draw_count) { taskYIELD(); }
#else
drawfunc();
#endif
}

View file

@ -0,0 +1,42 @@
#define LGFX_USE_V1
#include <LovyanGFX.hpp>
#include "image320x240x16.h"
#include "image480x320x8.h"
#include "image640x480x4.h"
#include "image1280x960x1.h"
static LGFX lcd;
static LGFX_Sprite sprite;
void setup()
{
lcd.init();
if (lcd.width() < lcd.height()) { lcd.setRotation(lcd.getRotation() ^ 1); }
// setBufferを使用することで、予め用意されたデータを用いてスプライトを使用できるようにする。
sprite.setBuffer(const_cast<std::uint8_t*>(image320x240x16), 320, 240, 16);
// sprite.setBuffer(const_cast<std::uint8_t*>(image480x320x8), 480, 320, 8);
// sprite.setBuffer(const_cast<std::uint8_t*>(image640x480x4), 640, 480, 4);
// sprite.setBuffer(const_cast<std::uint8_t*>(image1280x960x1), 1280, 960, 1);
// sprite.createPalette();
// ※ 本来const(書換不可能)なデータをconst_castにより非constと偽って使用しています。
// このため、このスプライトに対して描画を行うとプログラムはクラッシュします。
// LCDや他のスプライトに描画する関数など、読出しを行う関数のみが使用できます。
// OK) sprite.pushSprite
// OK) sprite.pushRotated
// OK) sprite.pushRotateZoom
// OK) sprite.readRect
// NG) sprite.drawPixel
// NG) sprite.fillRect
// NG) sprite.scroll
}
void loop() {
static std::uint32_t count;
if (++count == 360) count = 0;
float zoom = (((float)count) / 200) + 0.1;
sprite.pushRotateZoom(&lcd, lcd.width() >> 1, lcd.height() >> 1, count, zoom, zoom);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,114 @@
#define LGFX_USE_V1
#include <LovyanGFX.hpp>
static LGFX lcd;
static LGFX_Sprite canvas(&lcd); // オフスクリーン描画用バッファ
static LGFX_Sprite base(&canvas); // 文字盤パーツ
static LGFX_Sprite needle(&canvas); // 針パーツ
static int32_t width = 239; // 画像サイズ
static int32_t halfwidth = width >> 1; // 中心座標
static auto transpalette = 0; // 透過色パレット番号
static float zoom; // 表示倍率
#ifdef min
#undef min
#endif
void setup(void)
{
lcd.init();
int lw = std::min(lcd.width(), lcd.height());
zoom = (float)lw / width; // 表示が画面にフィットするよう倍率を調整
int px = lcd.width() >> 1;
int py = lcd.height() >> 1;
lcd.setPivot(px, py); // 描画時の中心を画面中心に合わせる
lcd.setColorDepth(24);
for (int i = 0; i < 180; i+=2) { // 外周を描画する
lcd.setColor(lcd.color888(i*1.4,i*1.4+2,i*1.4+4));
lcd.fillArc(px, py, (lw>>1) , (lw>>1)-zoom*3, 90+i, 92+i);
lcd.fillArc(px, py, (lw>>1) , (lw>>1)-zoom*3, 88-i, 90-i);
}
for (int i = 0; i < 180; i+=2) { // 外周を描画する
lcd.setColor(lcd.color888(i*1.4,i*1.4+2,i*1.4+4));
lcd.fillArc(px, py, (lw>>1)-zoom*4, (lw>>1)-zoom*7, 270+i, 272+i);
lcd.fillArc(px, py, (lw>>1)-zoom*4, (lw>>1)-zoom*7, 268-i, 270-i);
}
lcd.setColorDepth(16);
canvas.setColorDepth(lgfx::palette_2bit); // 各部品を2ビットパレットモードで準備する
base .setColorDepth(lgfx::palette_2bit);
needle.setColorDepth(lgfx::palette_2bit);
canvas.createSprite(width, width); // メモリ確保
base .createSprite(width, width);
needle.createSprite(3, 11);
base.setFont(&fonts::Orbitron_Light_24); // フォント種類を変更(盤の文字用)
//base.setFont(&fonts::Roboto_Thin_24); // フォント種類を変更(盤の文字用)
base.setTextDatum(lgfx::middle_center);
base.fillCircle(halfwidth, halfwidth, halfwidth - 8, 1);
base.fillArc(halfwidth, halfwidth, halfwidth - 10, halfwidth - 11, 135, 45, 3);
base.fillArc(halfwidth, halfwidth, halfwidth - 20, halfwidth - 23, 2, 43, 2);
base.fillArc(halfwidth, halfwidth, halfwidth - 20, halfwidth - 23, 317, 358, 2);
base.setTextColor(3);
for (int i = -5; i <= 25; ++i) {
bool flg = 0 == (i % 5); // 5目盛り毎フラグ
if (flg) {
// 大きい目盛り描画
base.fillArc(halfwidth, halfwidth, halfwidth - 10, halfwidth - 24, 179.8 + i*9, 180.2 + i*9, 3);
base.fillArc(halfwidth, halfwidth, halfwidth - 10, halfwidth - 20, 179.4 + i*9, 180.6 + i*9, 3);
base.fillArc(halfwidth, halfwidth, halfwidth - 10, halfwidth - 14, 179 + i*9, 181 + i*9, 3);
float rad = i * 9 * 0.0174532925;
float ty = - sin(rad) * (halfwidth * 10 / 15);
float tx = - cos(rad) * (halfwidth * 10 / 17);
base.drawFloat((float)i/10, 1, halfwidth + tx, halfwidth + ty); // 数値描画
} else {
// 小さい目盛り描画
base.fillArc(halfwidth, halfwidth, halfwidth - 10, halfwidth - 17, 179.5 + i*9, 180.5 + i*9, 3);
}
}
needle.setPivot(1, 9); // 針パーツの回転中心座標を設定する
needle.drawRect(0, 0, 3, 11, 2);
canvas.setPaletteColor(1, 0, 0, 15);
canvas.setPaletteColor(2, 255, 31, 31);
canvas.setPaletteColor(3, 255, 255, 191);
lcd.startWrite();
}
void draw(float value)
{
base.pushSprite(0, 0); // 描画用バッファに盤の画像を上書き
float angle = 270 + value * 90.0;
needle.pushRotateZoom( angle, 3.0, 10.0, transpalette); // 針をバッファに描画する
canvas.fillCircle(halfwidth, halfwidth, 7, 3);
canvas.pushRotateZoom(0, zoom, zoom, transpalette); // 完了した盤をLCDに描画する
if (value >= 1.5)
lcd.fillCircle(lcd.width()>>1, (lcd.height()>>1) + width * 4/10, 5, 0x007FFFU);
}
void loop(void)
{
float value = 0;
do {
draw(value);
} while (1.9 > (value += 0.005 + value * 0.05));
do {
draw(value);
} while (-0.5 < (value -= 0.1));
do {
draw(value);
} while (0.0 > (value += 0.05));
}

View file

@ -0,0 +1,127 @@
#define LGFX_USE_V1
#include <LovyanGFX.hpp>
#define LGFX_AUTODETECT
#include <LGFX_AUTODETECT.hpp> // クラス"LGFX"を準備します
// #include <lgfx_user/LGFX_ESP32_sample.hpp> // またはユーザ自身が用意したLGFXクラスを準備します
static LGFX lcd;
static std::uint32_t sec, psec;
static std::uint32_t fps = 0, frame_count = 0;
static std::int32_t lcd_width ;
static std::int32_t lcd_height;
struct obj_info_t {
std::int32_t x;
std::int32_t y;
std::int32_t r;
std::int32_t dx;
std::int32_t dy;
std::uint32_t color;
void move()
{
x += dx;
y += dy;
if (x < 0) {
x = 0;
if (dx < 0) dx = - dx;
} else if (x >= lcd_width) {
x = lcd_width -1;
if (dx > 0) dx = - dx;
}
if (y < 0) {
y = 0;
if (dy < 0) dy = - dy;
} else if (y >= lcd_height) {
y = lcd_height - 1;
if (dy > 0) dy = - dy;
}
}
};
static constexpr std::uint32_t obj_count = 200;
static obj_info_t objects[obj_count];
static LGFX_Sprite sprites[2];
static int_fast16_t sprite_height;
void setup(void)
{
lcd.init();
if (lcd.width() < lcd.height())
{
lcd.setRotation(lcd.getRotation() ^ 1);
}
lcd_width = lcd.width();
lcd_height = lcd.height();
obj_info_t *a;
for (std::uint32_t i = 0; i < obj_count; ++i) {
a = &objects[i];
a->color = (uint32_t)rand() | 0x808080U;
a->x = rand() % lcd_width;
a->y = rand() % lcd_height;
a->dx = ((rand() & 3) + 1) * (i & 1 ? 1 : -1);
a->dy = ((rand() & 3) + 1) * (i & 2 ? 1 : -1);
a->r = 8 + (i & 0x07);
}
uint32_t div = 2;
for (;;) {
sprite_height = (lcd_height + div - 1) / div;
bool fail = false;
for (std::uint32_t i = 0; !fail && i < 2; ++i)
{
sprites[i].setColorDepth(lcd.getColorDepth());
sprites[i].setFont(&fonts::Font2);
fail = !sprites[i].createSprite(lcd_width, sprite_height);
}
if (!fail) break;
for (std::uint32_t i = 0; i < 2; ++i)
{
sprites[i].deleteSprite();
}
++div;
}
lcd.startWrite();
}
void loop(void)
{
static std::uint_fast8_t flip = 0;
obj_info_t *a;
for (std::uint32_t i = 0; i != obj_count; i++) {
objects[i].move();
}
for (std::int32_t y = 0; y < lcd_height; y += sprite_height) {
flip = flip ? 0 : 1;
sprites[flip].clear();
for (std::uint32_t i = 0; i != obj_count; i++) {
a = &objects[i];
if (( a->y + a->r >= y ) && ( a->y - a->r <= y + sprite_height ))
sprites[flip].drawCircle(a->x, a->y - y, a->r, a->color);
}
if (y == 0) {
sprites[flip].setCursor(1,1);
sprites[flip].setTextColor(TFT_BLACK);
sprites[flip].printf("obj:%d fps:%d", obj_count, fps);
sprites[flip].setCursor(0,0);
sprites[flip].setTextColor(TFT_WHITE);
sprites[flip].printf("obj:%d fps:%d", obj_count, fps);
}
sprites[flip].pushSprite(&lcd, 0, y);
}
lcd.display();
++frame_count;
sec = lgfx::millis() / 1000;
if (psec != sec) {
psec = sec;
fps = frame_count;
frame_count = 0;
}
}

View file

@ -0,0 +1,269 @@
#define LGFX_USE_V1
#include <LovyanGFX.hpp>
#define LGFX_AUTODETECT
#include <LGFX_AUTODETECT.hpp> // クラス"LGFX"を準備します
// #include <lgfx_user/LGFX_ESP32_sample.hpp> // またはユーザ自身が用意したLGFXクラスを準備します
static LGFX lcd;
extern const unsigned short info[];
extern const unsigned short alert[];
extern const unsigned short closeX[];
static constexpr unsigned short infoWidth = 32;
static constexpr unsigned short infoHeight = 32;
static constexpr unsigned short alertWidth = 32;
static constexpr unsigned short alertHeight = 32;
static constexpr unsigned short closeWidth = 32;
static constexpr unsigned short closeHeight = 32;
static uint32_t sec, psec;
static size_t fps = 0, frame_count = 0;
static uint32_t lcd_width ;
static uint32_t lcd_height;
struct obj_info_t {
int_fast16_t x;
int_fast16_t y;
int_fast16_t dx;
int_fast16_t dy;
int_fast8_t img;
float r;
float z;
float dr;
float dz;
void move()
{
r += dr;
x += dx;
if (x < 0) {
x = 0;
if (dx < 0) dx = - dx;
} else if (x >= lcd_width) {
x = lcd_width -1;
if (dx > 0) dx = - dx;
}
y += dy;
if (y < 0) {
y = 0;
if (dy < 0) dy = - dy;
} else if (y >= lcd_height) {
y = lcd_height - 1;
if (dy > 0) dy = - dy;
}
z += dz;
if (z < .5) {
z = .5;
if (dz < .0) dz = - dz;
} else if (z >= 2.0) {
z = 2.0;
if (dz > .0) dz = - dz;
}
}
};
static constexpr size_t obj_count = 50;
static obj_info_t objects[obj_count];
static LGFX_Sprite sprites[2];
static LGFX_Sprite icons[3];
static int_fast16_t sprite_height;
void setup(void)
{
lcd.init();
if (lcd.width() < lcd.height())
{
lcd.setRotation(lcd.getRotation() ^ 1);
}
lcd_width = lcd.width();
lcd_height = lcd.height();
obj_info_t *a;
for (size_t i = 0; i < obj_count; ++i) {
a = &objects[i];
a->img = i % 3;
a->x = rand() % lcd_width;
a->y = rand() % lcd_height;
a->dx = ((rand() & 3) + 1) * (i & 1 ? 1 : -1);
a->dy = ((rand() & 3) + 1) * (i & 2 ? 1 : -1);
a->dr = ((rand() & 3) + 1) * (i & 2 ? 1 : -1);
a->r = 0;
a->z = (float)((rand() % 10)+10) / 10;
a->dz = (float)((rand() % 10)+1) / 100;
}
uint32_t div = 2;
for (;;) {
sprite_height = (lcd_height + div - 1) / div;
bool fail = false;
for (std::uint32_t i = 0; !fail && i < 2; ++i)
{
sprites[i].setColorDepth(lcd.getColorDepth());
sprites[i].setFont(&fonts::Font2);
fail = !sprites[i].createSprite(lcd_width, sprite_height);
}
if (!fail) break;
for (std::uint32_t i = 0; i < 2; ++i)
{
sprites[i].deleteSprite();
}
++div;
}
icons[0].createSprite(infoWidth, infoHeight);
icons[1].createSprite(alertWidth, alertHeight);
icons[2].createSprite(closeWidth, closeHeight);
icons[0].setSwapBytes(true);
icons[1].setSwapBytes(true);
icons[2].setSwapBytes(true);
icons[0].pushImage(0, 0, infoWidth, infoHeight, info);
icons[1].pushImage(0, 0, alertWidth, alertHeight, alert);
icons[2].pushImage(0, 0, closeWidth, closeHeight, closeX);
lcd.startWrite();
}
void loop(void)
{
static uint8_t flip = 0;
obj_info_t *a;
for (int i = 0; i != obj_count; i++) {
objects[i].move();
}
for (int_fast16_t y = 0; y < lcd_height; y += sprite_height) {
flip = flip ? 0 : 1;
sprites[flip].clear();
for (size_t i = 0; i != obj_count; i++) {
a = &objects[i];
icons[a->img].pushRotateZoom(&sprites[flip], a->x, a->y - y, a->r, a->z, a->z, 0);
}
if (y == 0) {
sprites[flip].setCursor(0,0);
sprites[flip].setFont(&fonts::Font4);
sprites[flip].setTextColor(0xFFFFFFU);
sprites[flip].printf("obj:%d fps:%d", obj_count, fps);
}
sprites[flip].pushSprite(&lcd, 0, y);
}
lcd.display();
++frame_count;
sec = lgfx::millis() / 1000;
if (psec != sec) {
psec = sec;
fps = frame_count;
frame_count = 0;
}
}
constexpr unsigned short info[1024] ={
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 0, 32 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0861,0x4A69,0x8C71,0xA514,0xBDF7,0xBDF7,0xA514,0x8C71,0x4A69,0x0861,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 1, 64 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x39E7,0x9CF3,0xEF7D,0xF79E,0xFFDF,0xFFDF,0xFFDF,0xFFDF,0xFFDF,0xFFDF,0xF79E,0xEF7D,0x9CF3,0x39E7,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 2, 96 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2965,0x9492,0xF79E,0xFFDF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFDF,0xF79E,0x9492,0x2965,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 3, 128 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x630C,0xEF7D,0xFFDF,0xFFFF,0xFFFF,0xFFFF,0xD75F,0xB6BF,0x9E5F,0x963F,0x963F,0x9E5F,0xB6BF,0xD75F,0xFFFF,0xFFFF,0xFFFF,0xFFDF,0xEF7D,0x630C,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 4, 160 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x73AE,0xEF7D,0xFFDF,0xFFFF,0xFFDF,0xBEDF,0x7DBF,0x7DBF,0x7DDF,0x7DDF,0x7DDF,0x7DDF,0x7DDF,0x7DBF,0x759F,0x7DBE,0xBEBF,0xFFDF,0xFFFF,0xFFDF,0xEF7D,0x73AE,0x0000,0x0000,0x0000,0x0000,0x0000, // row 5, 192 pixels
0x0000,0x0000,0x0000,0x0000,0x630C,0xEF7D,0xFFFF,0xFFFF,0xE77F,0x7DBE,0x759E,0x759F,0x7DBF,0x7DDF,0x7DDF,0x85FF,0x7DDF,0x7DDF,0x7DBF,0x759F,0x759E,0x6D7E,0x7DBE,0xDF7F,0xFFFF,0xFFFF,0xEF7D,0x630C,0x0000,0x0000,0x0000,0x0000, // row 6, 224 pixels
0x0000,0x0000,0x0000,0x31A6,0xEF5D,0xFFDF,0xFFFF,0xCF1E,0x6D7E,0x6D7E,0x759E,0x759F,0x7DBF,0x7DDF,0x8E1F,0xBEDF,0xC6FF,0x8DFF,0x75BF,0x759F,0x759E,0x6D7E,0x655E,0x655D,0xCF1E,0xFFFF,0xFFDF,0xEF5D,0x31A6,0x0000,0x0000,0x0000, // row 7, 256 pixels
0x0000,0x0000,0x0000,0x94B2,0xF7BE,0xFFFF,0xDF5E,0x655D,0x655D,0x6D7E,0x6D7E,0x759E,0x75BF,0x759F,0xEFBF,0xFFFF,0xFFFF,0xEFBF,0x759F,0x759E,0x6D7E,0x6D7E,0x655D,0x653D,0x653D,0xDF5E,0xFFFF,0xF7BE,0x94B2,0x0000,0x0000,0x0000, // row 8, 288 pixels
0x0000,0x0000,0x4228,0xEF7D,0xFFFF,0xF7BF,0x6D5D,0x653D,0x655D,0x6D5E,0x6D7E,0x759E,0x759E,0x85DF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0x8DFE,0x6D7E,0x6D7E,0x6D5E,0x655D,0x653D,0x5D1D,0x6D5D,0xF7BF,0xFFFF,0xEF7D,0x4228,0x0000,0x0000, // row 9, 320 pixels
0x0000,0x0000,0xA534,0xFFDF,0xFFDF,0xA65D,0x5D1D,0x5D1D,0x653D,0x655E,0x6D7E,0x6D7E,0x6D7E,0x651E,0xE77F,0xFFFF,0xFFFF,0xF7BF,0x5CFE,0x6D7E,0x6D7E,0x655E,0x653D,0x5D1D,0x5D1D,0x54FC,0xA65D,0xFFDF,0xFFDF,0xA534,0x0000,0x0000, // row 10, 352 pixels
0x0000,0x18E3,0xEF5D,0xFFFF,0xEF9E,0x5CFC,0x54FC,0x5D1D,0x5D3D,0x653D,0x655E,0x6D7E,0x6D7E,0x653E,0x6D3E,0xB67E,0xBEBE,0x755E,0x5D1E,0x6D5E,0x655E,0x653D,0x5D3D,0x5D1D,0x54FC,0x54DC,0x54FC,0xEF9E,0xFFFF,0xEF5D,0x18E3,0x0000, // row 11, 384 pixels
0x0000,0x630C,0xEF7D,0xFFDF,0xB69D,0x54DC,0x54FC,0x5CFC,0x5D1D,0x653D,0x653D,0x655E,0x6D5E,0x655E,0x5CFE,0x4C9D,0x4C7D,0x54DD,0x653E,0x655E,0x653D,0x653D,0x5D1D,0x5CFC,0x54FC,0x54DC,0x4CBC,0xB69D,0xFFDF,0xEF7D,0x630C,0x0000, // row 12, 416 pixels
0x0000,0x94B2,0xF7BE,0xFFDF,0x85BC,0x4CBC,0x54DC,0x54FC,0x5CFD,0x5D1D,0x5D3D,0x653D,0x655D,0x653D,0x85DE,0xC6FE,0xC6FE,0x85BE,0x653D,0x653D,0x5D3D,0x5D1D,0x5CFD,0x54FC,0x54DC,0x4CBC,0x4CBB,0x85BC,0xFFDF,0xF7BE,0x94B2,0x0000, // row 13, 448 pixels
0x0000,0xB5B6,0xFFDF,0xF7BE,0x651C,0x4CBB,0x4CBC,0x54DC,0x54FC,0x5CFC,0x5D1D,0x5D1D,0x653D,0x5D1D,0xE77E,0xFFDF,0xFFDF,0xEF9E,0x5CFD,0x5D1D,0x5D1D,0x5CFC,0x54FC,0x54DC,0x4CBC,0x4CBB,0x449B,0x651B,0xF7BE,0xFFDF,0xB5B6,0x0000, // row 14, 480 pixels
0x0000,0xC638,0xFFDF,0xF7BE,0x54DB,0x449B,0x4CBB,0x4CBC,0x54DC,0x54FC,0x54FC,0x5D1D,0x5D1D,0x7D7D,0xF7BE,0xF7BE,0xF7BE,0xF7BE,0x7D7D,0x5CFD,0x54FC,0x54FC,0x54DC,0x4CBC,0x4CBB,0x449B,0x447B,0x54BB,0xF7BE,0xFFDF,0xC638,0x0000, // row 15, 512 pixels
0x0000,0xC638,0xFFDF,0xF79E,0x4CBB,0x449B,0x449B,0x4CBB,0x4CBC,0x54DC,0x54DC,0x54FC,0x54DC,0x753C,0xF7BE,0xF7BE,0xF7BE,0xF7BE,0x753C,0x54DC,0x54DC,0x54DC,0x4CBC,0x4CBB,0x449B,0x449B,0x3C7B,0x4C9B,0xF79E,0xFFDF,0xC638,0x0000, // row 16, 544 pixels
0x0000,0xB5B6,0xFFDF,0xF7BE,0x5CFB,0x3C7B,0x447B,0x449B,0x4CBB,0x4CBC,0x4CBC,0x4CDC,0x4CBC,0x6D1C,0xF7BE,0xF7BE,0xF7BE,0xF7BE,0x6CFC,0x4CBC,0x4CBC,0x4CBC,0x4CBB,0x449B,0x447B,0x3C7B,0x3C5A,0x54DB,0xF7BE,0xFFDF,0xB5B6,0x0000, // row 17, 576 pixels
0x0000,0x94B2,0xF7BE,0xF7BE,0x755B,0x3C5A,0x3C7B,0x447B,0x449B,0x449B,0x4CBB,0x4CBB,0x4C9B,0x6CFB,0xF79E,0xF79E,0xF79E,0xF79E,0x64FB,0x449B,0x4CBB,0x449B,0x449B,0x447B,0x3C7B,0x3C5A,0x3C5A,0x753B,0xF7BE,0xF7BE,0x9CD3,0x0000, // row 18, 608 pixels
0x0000,0x6B4D,0xEF7D,0xF7BE,0xA61C,0x3C5A,0x3C5A,0x3C7B,0x447B,0x447B,0x449B,0x449B,0x447B,0x64DB,0xF79E,0xF79E,0xF79E,0xF79E,0x64DB,0x447B,0x449B,0x447B,0x447B,0x3C7B,0x3C5A,0x3C5A,0x343A,0xA61C,0xF7BE,0xEF7D,0x6B4D,0x0000, // row 19, 640 pixels
0x0000,0x2124,0xE71C,0xFFDF,0xDF3D,0x3C5A,0x343A,0x3C5A,0x3C5A,0x3C7B,0x3C7B,0x447B,0x3C5B,0x64BA,0xF79E,0xF79E,0xF79E,0xF79E,0x64BA,0x3C5B,0x3C7B,0x3C7B,0x3C5A,0x3C5A,0x343A,0x343A,0x343A,0xDF3D,0xFFDF,0xE71C,0x2124,0x0000, // row 20, 672 pixels
0x0000,0x0000,0xAD75,0xF7BE,0xF79E,0x859B,0x343A,0x343A,0x345A,0x3C5A,0x3C5A,0x3C5A,0x3C5A,0x5C9A,0xEF7D,0xEF7D,0xEF7D,0xEF7D,0x5C9A,0x3C3A,0x3C5A,0x3C5A,0x345A,0x343A,0x343A,0x341A,0x859B,0xF79E,0xF7BE,0xAD75,0x0000,0x0000, // row 21, 704 pixels
0x0000,0x0000,0x528A,0xE71C,0xFFDF,0xDF3D,0x3C5A,0x343A,0x343A,0x343A,0x343A,0x3C5A,0x343A,0x4C5A,0xEF7D,0xEF7D,0xEF7D,0xEF7D,0x4C59,0x343A,0x343A,0x343A,0x343A,0x343A,0x341A,0x3C5A,0xDF3D,0xFFDF,0xE71C,0x528A,0x0000,0x0000, // row 22, 736 pixels
0x0000,0x0000,0x0000,0x9CD3,0xF79E,0xF7BE,0xBE7C,0x3419,0x341A,0x341A,0x343A,0x343A,0x341A,0x2B99,0xC69C,0xEF7D,0xEF7D,0xD6DC,0x2398,0x341A,0x343A,0x341A,0x341A,0x2C19,0x2C19,0xBE7C,0xF7BE,0xF79E,0x9CD3,0x0000,0x0000,0x0000, // row 23, 768 pixels
0x0000,0x0000,0x0000,0x39E7,0xDEDB,0xFFDF,0xF79E,0x9DFB,0x2C19,0x2C19,0x2C1A,0x341A,0x341A,0x2BB9,0x2B57,0x6459,0x74B9,0x2337,0x2BB9,0x341A,0x2C1A,0x2C19,0x2C19,0x2C19,0x9DFB,0xF79E,0xFFDF,0xDEDB,0x39E7,0x0000,0x0000,0x0000, // row 24, 800 pixels
0x0000,0x0000,0x0000,0x0000,0x632C,0xDEFB,0xFFDF,0xEF7D,0xB65C,0x3C39,0x2BF9,0x2C19,0x2C19,0x2BF9,0x2398,0x1B58,0x1B37,0x2398,0x2BF9,0x2C19,0x2BF9,0x2BF9,0x3439,0xB65C,0xEF7D,0xFFDF,0xDEFB,0x632C,0x0000,0x0000,0x0000,0x0000, // row 25, 832 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x73AE,0xDEFB,0xF7BE,0xF79E,0xDF1C,0x7D5A,0x2BF9,0x2BF9,0x2BF9,0x2BF9,0x23D9,0x23D9,0x2BF9,0x2BF9,0x2BF9,0x2BF9,0x7D5A,0xDF1C,0xF79E,0xF7BE,0xDEFB,0x73AE,0x0000,0x0000,0x0000,0x0000,0x0000, // row 26, 864 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x632C,0xDEDB,0xF79E,0xFFDF,0xEF7D,0xD6FC,0x9DFB,0x5CDA,0x4C9A,0x3419,0x3419,0x4C9A,0x5CDA,0x9DFB,0xD6FC,0xEF7D,0xFFDF,0xF79E,0xDEDB,0x632C,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 27, 896 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x4208,0x94B2,0xDEFB,0xF7BE,0xFFDF,0xF7BE,0xF79E,0xEF7D,0xEF5D,0xEF5D,0xEF7D,0xF79E,0xF7BE,0xFFDF,0xF7BE,0xDEFB,0x94B2,0x4208,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 28, 928 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x528A,0xA534,0xDEDB,0xE73C,0xF79E,0xF7BE,0xF7BE,0xF7BE,0xF7BE,0xF79E,0xE73C,0xDEDB,0xA534,0x528A,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 29, 960 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x18C3,0x5AEB,0x8C71,0xAD55,0xBDD7,0xBDD7,0xAD55,0x8C71,0x5AEB,0x18C3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 30, 992 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000}; // row 31, 1024 pixels
constexpr unsigned short alert[1024] ={
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0840,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 0, 32 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1080,0xAC66,0xEDE8,0xFE69,0xC4C6,0x2901,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 1, 64 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xBCC6,0xFE68,0xFE68,0xFE6A,0xFE68,0xEDE8,0x18A1,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 2, 96 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x8344,0xFE48,0xFE8C,0xFFDD,0xFFFF,0xFEF0,0xFE48,0xB466,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 3, 128 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1880,0xEDC7,0xFE48,0xFF99,0xFFBC,0xFF9B,0xFFBD,0xFE6A,0xFE48,0x5A23,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 4, 160 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x9BE5,0xFE28,0xFED0,0xFFBC,0xFF7A,0xFF9A,0xFF9B,0xFF35,0xFE28,0xBCA6,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 5, 192 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3962,0xFE28,0xFE28,0xFF9A,0xFF79,0xFF9A,0xFF9B,0xFF9A,0xFFBD,0xFE6B,0xFE28,0x72E3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 6, 224 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xB465,0xFE28,0xFEF2,0xFF7A,0xFF79,0xFF7A,0xFF9A,0xFF7A,0xFF7A,0xFF78,0xFE28,0xDD67,0x0860,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 7, 256 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x5A22,0xFE07,0xFE29,0xFF9B,0xFF37,0xFF58,0xFF79,0xFF79,0xFF79,0xFF58,0xFF9B,0xFEAE,0xFE07,0x93A4,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 8, 288 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xC4A5,0xFE07,0xFF15,0xFF37,0xFF36,0xAD11,0x2965,0x2965,0xCDF4,0xFF37,0xFF37,0xFF79,0xFE07,0xFE07,0x2901,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 9, 320 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x7B03,0xFDE7,0xFE4B,0xFF79,0xFEF4,0xFF15,0xB552,0x2945,0x2945,0xDE55,0xFF16,0xFF15,0xFF58,0xFED1,0xFDE7,0xAC25,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 10, 352 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0840,0xDD26,0xFDE7,0xFF57,0xFED3,0xFED2,0xFEF4,0xBD93,0x2124,0x2124,0xDE75,0xFF14,0xFED3,0xFED3,0xFF7A,0xFE08,0xFDE7,0x49A2,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 11, 384 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x9BA4,0xFDC6,0xFE6E,0xFF36,0xFE90,0xFEB1,0xFED3,0xC592,0x2124,0x2124,0xE675,0xFED3,0xFEB2,0xFEB1,0xFEF3,0xFEF3,0xFDC6,0xBC45,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 12, 416 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3141,0xF5C6,0xF5C7,0xFF58,0xFE90,0xFE6F,0xFE8F,0xFEB1,0xCDB2,0x2104,0x2104,0xF6B4,0xFEB1,0xFE90,0xFE8F,0xFE90,0xFF58,0xFE0A,0xF5C6,0x72A3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 13, 448 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xABE4,0xF5A6,0xFEB1,0xFED3,0xFE4E,0xFE6E,0xFE6F,0xFE90,0xD5F2,0x18E3,0x18E3,0xFED4,0xFE90,0xFE6F,0xFE6F,0xFE6E,0xFE91,0xFF36,0xF5A6,0xCCA5,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 14, 480 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x5202,0xF5A6,0xF5C7,0xFF58,0xFE4D,0xFE4D,0xFE4D,0xFE4E,0xFE6F,0xDE11,0x18C3,0x18C3,0xFED3,0xFE6F,0xFE6E,0xFE4E,0xFE4D,0xFE4D,0xFF16,0xFE2C,0xF5A6,0x9363,0x0000,0x0000,0x0000,0x0000,0x0000, // row 15, 512 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0xBC44,0xF585,0xFED3,0xFE6F,0xFE2C,0xFE2C,0xFE2D,0xFE4D,0xFE4E,0xE630,0x10A2,0x2104,0xFED1,0xFE4E,0xFE4D,0xFE4D,0xFE2D,0xFE2C,0xFE4D,0xFF37,0xF586,0xF585,0x28E1,0x0000,0x0000,0x0000,0x0000, // row 16, 544 pixels
0x0000,0x0000,0x0000,0x0000,0x7282,0xF565,0xF5EA,0xFF16,0xFE0B,0xFE0B,0xFE0B,0xFE2C,0xFE2C,0xFE4D,0xF670,0x1082,0x2924,0xFEB0,0xFE2D,0xFE2C,0xFE2C,0xFE2C,0xFE0B,0xFE0B,0xFEB2,0xFE6F,0xF565,0xA383,0x0000,0x0000,0x0000,0x0000, // row 17, 576 pixels
0x0000,0x0000,0x0000,0x0840,0xD4C4,0xF565,0xFEF5,0xFE0C,0xFDE9,0xFDEA,0xFE0A,0xFE0B,0xFE0B,0xFE2C,0xFE8F,0x0861,0x2964,0xFE8F,0xFE2C,0xFE0B,0xFE0B,0xFE0B,0xFE0A,0xFDEA,0xFE0B,0xFF37,0xF586,0xF565,0x4181,0x0000,0x0000,0x0000, // row 18, 608 pixels
0x0000,0x0000,0x0000,0x9343,0xF545,0xF60C,0xFED3,0xFDC8,0xFDC8,0xFDC9,0xFDE9,0xFDEA,0xFDEA,0xFE0B,0xFE8E,0x0861,0x3184,0xFE6D,0xFE0B,0xFE0A,0xFDEA,0xFDEA,0xFDE9,0xFDC9,0xFDC9,0xFE4E,0xFEB2,0xF545,0xB3E3,0x0000,0x0000,0x0000, // row 19, 640 pixels
0x0000,0x0000,0x28E0,0xF544,0xF545,0xFF17,0xFDC8,0xFDA7,0xFDA7,0xFDC8,0xFDC8,0xFDC9,0xFDC9,0xFDE9,0xFE6C,0x10A2,0x39C4,0xFE4C,0xFDEA,0xFDE9,0xFDC9,0xFDC9,0xFDC8,0xFDC8,0xFDA7,0xFDA8,0xFF16,0xF588,0xF544,0x6222,0x0000,0x0000, // row 20, 672 pixels
0x0000,0x0000,0xA383,0xF524,0xF64E,0xFE4E,0xFD86,0xFD86,0xFD87,0xFDA7,0xFDA7,0xFDA8,0xFDC8,0xFDC8,0xFE2A,0xA469,0xB4EA,0xFE2A,0xFDC9,0xFDC8,0xFDC8,0xFDA8,0xFDA7,0xFDA7,0xFD87,0xFD86,0xFDEA,0xFED3,0xF524,0xC443,0x0000,0x0000, // row 21, 704 pixels
0x0000,0x51C1,0xF504,0xF546,0xFF16,0xF565,0xFD65,0xFD65,0xFD86,0xFD86,0xFD86,0xFDA7,0xFDA7,0xFDA7,0xFDE8,0xFE6A,0xFE4A,0xFDE8,0xFDA7,0xFDA7,0xFDA7,0xFDA7,0xFD86,0xFD86,0xFD86,0xFD65,0xFD65,0xFEB2,0xF5CA,0xF504,0x8AE2,0x0000, // row 22, 736 pixels
0x0000,0xB3A2,0xED03,0xFE92,0xFDC9,0xF543,0xF544,0xFD44,0xFD65,0xFD65,0xFD65,0xFD86,0xFD86,0xFD86,0xFDA7,0xFDC7,0xFDC7,0xFDA7,0xFD86,0xFD86,0xFD86,0xFD86,0xFD65,0xFD65,0xFD65,0xFD44,0xF544,0xFD86,0xFEF5,0xED03,0xE4C3,0x1880, // row 23, 768 pixels
0x7241,0xECE3,0xF567,0xFED3,0xF523,0xF523,0xF523,0xF543,0xF544,0xF544,0xFD65,0xFD65,0xFD65,0xFD65,0xD4E6,0x39C5,0x39A5,0xD4E6,0xFD86,0xFD65,0xFD65,0xFD65,0xFD65,0xF544,0xF544,0xF543,0xF523,0xF523,0xFE2E,0xF5EC,0xECE3,0x9B42, // row 24, 800 pixels
0xD443,0xECE3,0xFED4,0xF565,0xF502,0xF502,0xF522,0xF523,0xF523,0xF543,0xF544,0xF544,0xF544,0xFD65,0x8B64,0x18C3,0x18C3,0x8344,0xFD85,0xFD44,0xF544,0xF544,0xF544,0xF543,0xF523,0xF523,0xF522,0xF502,0xF523,0xFEF5,0xED04,0xECE3, // row 25, 832 pixels
0xECC3,0xF5AB,0xFE6F,0xF501,0xF4E1,0xF501,0xF502,0xF502,0xF522,0xF522,0xF523,0xF523,0xF523,0xFD84,0xC504,0x20E1,0x18E1,0xC4E4,0xFD84,0xF543,0xF523,0xF523,0xF523,0xF522,0xF522,0xF502,0xF502,0xF501,0xF501,0xFDC9,0xF62F,0xECC3, // row 26, 864 pixels
0xECC2,0xFE92,0xF523,0xF4E0,0xF4E0,0xF4E1,0xF4E1,0xF501,0xF501,0xF502,0xF502,0xF522,0xF522,0xF543,0xFDE3,0xFEA5,0xF6A4,0xFE04,0xF543,0xF522,0xF522,0xF522,0xF502,0xF502,0xF501,0xF501,0xF4E1,0xF4E1,0xF4E0,0xF4E1,0xFED4,0xECC2, // row 27, 896 pixels
0xECA2,0xF5EC,0xF4E0,0xF4C0,0xF4E0,0xF4E0,0xF4E0,0xF4E1,0xF4E1,0xF501,0xF501,0xF501,0xF502,0xF502,0xF542,0xFDA2,0xFDA2,0xF542,0xF502,0xF502,0xF502,0xF501,0xF501,0xF501,0xF4E1,0xF4E1,0xF4E0,0xF4E0,0xF4E0,0xF4C0,0xF5A9,0xECA2, // row 28, 928 pixels
0xECA2,0xECA2,0xECC2,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4E1,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xECC2,0xECC3,0xECA2, // row 29, 960 pixels
0x8AC1,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0x9B01, // row 30, 992 pixels
0x0000,0x1880,0x51A0,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x61E0,0x28E0,0x0000}; // row 31, 1024 pixels
constexpr unsigned short closeX[1024] ={
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x30C3,0x4124,0x61C7,0x61C7,0x4124,0x30E3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 0, 32 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x48E3,0xA249,0xEB8E,0xFCB2,0xFD14,0xFD75,0xFD96,0xFD34,0xFCF3,0xEBEF,0xA28A,0x4904,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 1, 64 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x58E3,0xC228,0xFC10,0xFD34,0xFE18,0xFE59,0xFE79,0xFE9A,0xFE9A,0xFE9A,0xFE9A,0xFE59,0xFD75,0xFC51,0xC28A,0x5904,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 2, 96 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2041,0x8945,0xF34D,0xFD34,0xFDB6,0xFD75,0xFD55,0xFD55,0xFD96,0xFDD7,0xFDF7,0xFDF7,0xFDB6,0xFDB6,0xFDD7,0xFDF7,0xFD75,0xF38E,0x8965,0x2041,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 3, 128 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x4082,0xE208,0xF410,0xFD34,0xFC92,0xFBEF,0xFBAE,0xFBEF,0xFC71,0xFD14,0xFD75,0xFDB6,0xFD75,0xFD14,0xFC92,0xFC51,0xFC71,0xFCF3,0xFD75,0xFC30,0xEA28,0x40A2,0x0000,0x0000,0x0000,0x0000,0x0000, // row 4, 160 pixels
0x0000,0x0000,0x0000,0x0000,0x3861,0xE1E7,0xF451,0xFC92,0xFB4D,0xFA49,0xFA49,0xFAEB,0xFBAE,0xFC71,0xFD34,0xFDB6,0xFE18,0xFDB6,0xFD34,0xFC71,0xFBAE,0xFB0C,0xFAEB,0xFBAE,0xFCD3,0xFC71,0xE208,0x4082,0x0000,0x0000,0x0000,0x0000, // row 5, 192 pixels
0x0000,0x0000,0x0000,0x1020,0xD986,0xF430,0xFC30,0xFA28,0xF924,0xF965,0xFA8A,0xFB0C,0xFBAE,0xFC51,0xFD14,0xFD75,0xFDB6,0xFD75,0xFD14,0xFC51,0xFC71,0xFBEF,0xFA28,0xF9C7,0xFA8A,0xFC51,0xF430,0xD9A6,0x1020,0x0000,0x0000,0x0000, // row 6, 224 pixels
0x0000,0x0000,0x0000,0x78A2,0xEB6D,0xFC30,0xF9C7,0xF861,0xF8A2,0xFA08,0xFEDB,0xFD55,0xFB4D,0xFC10,0xFC92,0xFD14,0xFD34,0xFD14,0xFC92,0xFCB2,0xFF7D,0xFF7D,0xFB2C,0xF945,0xF8E3,0xF9E7,0xFC30,0xEB8E,0x78C3,0x0000,0x0000,0x0000, // row 7, 256 pixels
0x0000,0x0000,0x3841,0xD9E7,0xF492,0xF208,0xF041,0xF800,0xF945,0xFE9A,0xFFFF,0xFFFF,0xFD75,0xFB8E,0xFC10,0xFC51,0xFC71,0xFC51,0xFCB2,0xFF7D,0xFFFF,0xFFFF,0xFF3C,0xFA8A,0xF882,0xF841,0xFA08,0xFC92,0xDA08,0x3841,0x0000,0x0000, // row 8, 288 pixels
0x0000,0x0000,0x88A2,0xEBCF,0xF2EB,0xF061,0xF000,0xF8E3,0xFE79,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFD75,0xFB4D,0xFBAE,0xFBAE,0xFC71,0xFF7D,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFEFB,0xFA28,0xF800,0xF061,0xF2EB,0xEBEF,0x90C3,0x0000,0x0000, // row 9, 320 pixels
0x0000,0x2820,0xD1C7,0xF410,0xE945,0xE800,0xF000,0xFE9A,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFD34,0xFAEB,0xFBCF,0xFF5D,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFF1C,0xF986,0xF000,0xF145,0xF410,0xD1E7,0x2820,0x0000, // row 10, 352 pixels
0x0000,0x6841,0xDB2C,0xEACB,0xE041,0xE800,0xF000,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFD14,0xFF1C,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFBCF,0xF082,0xF000,0xE841,0xEACB,0xE34D,0x7061,0x0000, // row 11, 384 pixels
0x0000,0x9861,0xE3CF,0xE186,0xE000,0xE800,0xE800,0xF145,0xFEDB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFB8E,0xF000,0xF000,0xE800,0xE800,0xE986,0xEBCF,0xA082,0x0000, // row 12, 416 pixels
0x0800,0xB8A2,0xE3AE,0xD8A2,0xD800,0xE000,0xE800,0xE800,0xF145,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFB8E,0xF000,0xF000,0xE800,0xE800,0xE000,0xE0A2,0xEBAE,0xC0C3,0x0800, // row 13, 448 pixels
0x1800,0xC124,0xE30C,0xD020,0xD800,0xE000,0xE000,0xE800,0xE800,0xF145,0xFEDB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFB8E,0xF000,0xF000,0xE800,0xE800,0xE000,0xE000,0xD820,0xE30C,0xC124,0x1800, // row 14, 480 pixels
0x2800,0xC165,0xDAAA,0xC800,0xD000,0xD800,0xE000,0xE000,0xE800,0xE800,0xF124,0xFE79,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFB6D,0xF000,0xF000,0xE800,0xE800,0xE000,0xE000,0xD800,0xD000,0xDAAA,0xC165,0x2800, // row 15, 512 pixels
0x2000,0xB924,0xD269,0xC800,0xD000,0xD000,0xD800,0xE000,0xE000,0xE800,0xE924,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xF36D,0xE800,0xE800,0xE800,0xE000,0xE000,0xD800,0xD000,0xD000,0xDA69,0xC145,0x2800, // row 16, 544 pixels
0x1000,0xB0A2,0xD28A,0xC000,0xC800,0xD000,0xD000,0xD800,0xD800,0xE165,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xF3AE,0xE000,0xE000,0xD800,0xD800,0xD000,0xD000,0xC800,0xD28A,0xB8C3,0x1000, // row 17, 576 pixels
0x0000,0xA800,0xD2AA,0xB800,0xC000,0xC800,0xC800,0xD000,0xD965,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xEBAE,0xD800,0xD800,0xD000,0xC800,0xC800,0xC000,0xD2AA,0xB020,0x0000, // row 18, 608 pixels
0x0000,0x8000,0xCA69,0xB841,0xB800,0xC000,0xC800,0xD186,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xEBCF,0xD000,0xC800,0xC800,0xC000,0xC041,0xCA69,0x8000,0x0000, // row 19, 640 pixels
0x0000,0x4800,0xC1C7,0xB8E3,0xB800,0xB800,0xC000,0xF69A,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xEBEF,0xFE79,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xE410,0xC841,0xC000,0xB800,0xC0E3,0xC1C7,0x4800,0x0000, // row 20, 672 pixels
0x0000,0x1000,0xB061,0xC1E7,0xB000,0xB000,0xB800,0xD269,0xFFBE,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xE38E,0xD000,0xD965,0xF69A,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xDB0C,0xC020,0xB800,0xB000,0xC1E7,0xB061,0x1000,0x0000, // row 21, 704 pixels
0x0000,0x0000,0x6000,0xB9C7,0xB061,0xB000,0xB000,0xB800,0xCA49,0xFF9E,0xFFFF,0xFFFF,0xFFFF,0xE38E,0xC800,0xC800,0xC800,0xD186,0xF69A,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xDB0C,0xB800,0xB800,0xB000,0xB061,0xC1C7,0x6000,0x0000,0x0000, // row 22, 736 pixels
0x0000,0x0000,0x1800,0xB041,0xB986,0xA800,0xA800,0xB000,0xB000,0xCA49,0xFF7D,0xFFFF,0xDB8E,0xC000,0xC000,0xC000,0xC000,0xC000,0xC986,0xF6DB,0xFFFF,0xFFFF,0xD30C,0xB800,0xB000,0xB000,0xA800,0xB986,0xB041,0x1800,0x0000,0x0000, // row 23, 768 pixels
0x0000,0x0000,0x0000,0x5800,0xB0E3,0xA8C3,0xA800,0xA800,0xA800,0xB000,0xCACB,0xD38E,0xB000,0xB800,0xB800,0xB800,0xB800,0xB800,0xB800,0xC145,0xF6DB,0xD34D,0xB000,0xB000,0xA800,0xA800,0xB0C3,0xB0E3,0x5800,0x0000,0x0000,0x0000, // row 24, 800 pixels
0x0000,0x0000,0x0000,0x0000,0x6000,0xB124,0xA882,0xA000,0xA800,0xA800,0xA800,0xA800,0xB000,0xB000,0xB000,0xB000,0xB000,0xB000,0xB000,0xB000,0xB000,0xA800,0xA800,0xA800,0xA800,0xA882,0xB124,0x6000,0x0000,0x0000,0x0000,0x0000, // row 25, 832 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x6000,0xB104,0xA882,0xA000,0xA000,0xA000,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA000,0xA000,0xA882,0xB104,0x6000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 26, 864 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x6000,0xB0A2,0xA8C3,0xA020,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA020,0xA8C3,0xB0A2,0x6000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 27, 896 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x4800,0xA800,0xB0C3,0xA0A2,0x9800,0x9800,0x9800,0x9800,0xA000,0xA000,0xA000,0x9800,0x9800,0x9800,0xA082,0xB0E3,0xA800,0x4800,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 28, 928 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x5800,0xA800,0xB0A2,0xA8E3,0xA0A2,0xA041,0x9800,0x9800,0xA041,0xA0A2,0xA8E3,0xB0A2,0xA800,0x5800,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 29, 960 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3000,0x6000,0x8800,0xA000,0xA800,0xA800,0xA000,0x8800,0x6000,0x3000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 30, 992 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000}; // row 31, 1024 pixels

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,78 @@
#define LGFX_USE_V1
#include <LovyanGFX.hpp>
static LGFX lcd;
static LGFX_Sprite sprite(&lcd);
struct meter_t
{
int32_t pivot_x;
int32_t pivot_y;
float angle = 0;
float add = 0;
void advance(void)
{
if ((angle >= 256 && add > 0.0)
|| (angle <= 0 && add < 0.0))
{
add = -add;
}
angle += add;
}
void drawGauge(uint32_t color)
{
lcd.setPivot(pivot_x, pivot_y);
sprite.setPaletteColor(1, color);
sprite.pushRotated(angle - 127);
advance();
}
};
struct meter_t meter1, meter2, meter3;
void setup(void)
{
lcd.init();
if (lcd.width() < lcd.height()) {
lcd.setRotation(lcd.getRotation() ^ 1);
}
sprite.setColorDepth(2);
sprite.createSprite(3,lcd.height() / 8);
sprite.setPivot(1, lcd.height() / 4);
sprite.drawFastVLine(1, 0, sprite.height() , 3);
sprite.drawFastVLine(0, 2, sprite.height() - 4, 1);
meter1.pivot_x = lcd.width() >> 1;
meter1.pivot_y = (lcd.height() >> 2) + 2;
meter1.add = 0.01;
meter2.pivot_x = lcd.width() >> 2;
meter2.pivot_y = (lcd.height() * 3) >>2;
meter2.add = 0.11;
meter3.pivot_x = (lcd.width() * 3) >> 2;
meter3.pivot_y = (lcd.height() * 3) >> 2;
meter3.add = 0.57;
lcd.fillScreen(lcd.color565(0,0,127));
for (int i = 0; i < 20; i++) {
lcd.drawFastHLine(0, (i*2+1) * lcd.height() / 40, lcd.width(), 0xFFFF);
lcd.drawFastVLine((i*2+1) * lcd.width() / 40, 0, lcd.height() , 0xFFFF);
}
}
void loop(void)
{
meter1.drawGauge(sprite.color888(255, meter1.angle, 127));
meter2.drawGauge(sprite.color888(255 - meter2.angle, meter2.angle, 127));
meter3.drawGauge(sprite.color888(0, 127, meter3.angle));
}

View file

@ -0,0 +1,188 @@
#define LGFX_USE_V1
#include <LovyanGFX.hpp>
static LGFX lcd;
static LGFX_Sprite sp;
static LGFX_Sprite sprites[2];
static bool flip;
static int sprite_height;
inline uint16_t getBackColor(int x, int y)
{
return lcd.swap565(abs((x&31)-16)<<3, 0, abs((y&31)-16)<<3);
//return lcd.swap565(x, 0, y);
}
void setup(void)
{
lcd.init();
if (lcd.width() < lcd.height()) { lcd.setRotation(lcd.getRotation()^1); }
for (int i = 0; i < 2; i++)
{
sprites[i].setColorDepth(lcd.getColorDepth());
sprites[i].setFont(&fonts::Font2);
sprites[i].setTextColor(TFT_WHITE);
sprites[i].setTextDatum(textdatum_t::top_right);
}
int div = 1;
for (;;)
{
sprite_height = (lcd.height() + div - 1) / div;
bool fail = false;
for (int i = 0; !fail && i < 2; ++i)
{
fail = !sprites[i].createSprite(lcd.width(), sprite_height);
}
if (!fail) break;
for (int i = 0; i < 2; ++i)
{
sprites[i].deleteSprite();
}
div++;
}
sp.setColorDepth(8);
sp.setFont(&fonts::Font2);
sp.setTextDatum(textdatum_t::middle_center);
lcd.startWrite();
}
void loop(void)
{
static int count;
static int prev_sec;
static int prev_fps;
static int fps;
++fps;
int sec = millis() / 1000;
if (prev_sec != sec)
{
prev_sec = sec;
prev_fps = fps;
fps = 0;
}
count++;
float y_center = (std::max(lcd.width(), lcd.height()));
float distance;
float zoom_x, zoom_y;
for (int div_y = 0; div_y < lcd.height(); div_y += sprite_height)
{
float angle = (float)(count * 36) / 100;
// sprites[flip].clear();
//* // draw background
if (lcd.getColorDepth() == 16)
{
std::uint16_t* rawbuf = (std::uint16_t*)sprites[flip].getBuffer();
for (int y = 0; y < sprite_height; y++)
{
std::uint16_t* rawline = &rawbuf[y * lcd.width()];
for (int x = 0; x < lcd.width(); x++)
{
rawline[x] = lcd.swap565(abs((x&31)-16)<<3, 0, abs(((div_y + y)&31)-16)<<3);
}
}
}
else
if (lcd.getColorDepth() > 16)
{
lgfx::bgr888_t* rawbuf = (lgfx::bgr888_t*)sprites[flip].getBuffer();
for (int y = 0; y < sprite_height; y++)
{
lgfx::bgr888_t* rawline = &rawbuf[y * lcd.width()];
for (int x = 0; x < lcd.width(); x++)
{
rawline[x] = lcd.swap888(abs((x&31)-16)<<3, 0, abs(((div_y + y)&31)-16)<<3);
}
}
}
if (div_y == 0)
{
// draw fps and counter
sprites[flip].setCursor(0,0);
sprites[flip].printf("fps:%03d", prev_fps);
sprites[flip].drawNumber(count, lcd.width(), 0);
distance = y_center;
zoom_x = distance / 512.0f;
zoom_y = distance / 256.0f;
sp.createSprite(21, 11);
sp.fillTriangle(0, 0, 20, 0, 10, 10, TFT_WHITE);
sp.setPivot(10, distance / zoom_y - 0.5);
sp.pushRotateZoomWithAA(&sprites[flip], lcd.width() / 2, y_center - div_y, 0, zoom_x, zoom_y, 0);
}
// draw outer dial
distance = y_center * 0.95;
zoom_y = distance / 30;
sp.createSprite(1, 1);
sp.clear(TFT_WHITE);
for (int i = 0; i < 100; i++)
{
float a = fmodf(360.0f + angle - i * 3.6f, 360.0f);
if (a > 40 && a < 320) continue;
zoom_x = distance / (i%5 ? 50 : 30);
zoom_y = distance / (i%5 ? 30 : ((i % 10) ? 20 : 15));
sp.setPivot(0, distance / zoom_y - 0.5);
sp.pushRotateZoomWithAA(&sprites[flip], lcd.width() / 2, y_center - div_y, a, zoom_x, zoom_y, 0);
}
// draw outer number
distance *= 0.92;
zoom_x = distance / 128.0f;
zoom_y = distance / 128.0f;
sp.createSprite(18, 14);
sp.setPivot((float)sp.width() / 2, distance / zoom_y);
sp.setTextColor(TFT_WHITE, TFT_BLACK);
for (int i = 0; i < 100; i += 10)
{
float a = fmodf(360.0f + angle - i * 3.6f, 360.0f);
if (a > 50 && a < 310) continue;
sp.drawNumber(i, sp.width()>>1, sp.height()>>1);
sp.pushRotateZoomWithAA(&sprites[flip], lcd.width() / 2, y_center - div_y, a, zoom_x, zoom_y, 0);
}
// draw inner dial
angle /= 10;
distance *= 0.85;
sp.createSprite(1, 1);
sp.clear(TFT_YELLOW);
for (int i = 0; i < 100; i++)
{
float a = fmodf(360.0f + angle - i * 3.6f, 360.0f);
if (a > 60 && a < 300) continue;
zoom_x = distance / (i%5 ? 50 : 30);
zoom_y = distance / (i%5 ? 30 : ((i % 10) ? 20 : 15));
sp.setPivot(0, distance / zoom_y - 0.5);
sp.pushRotateZoomWithAA(&sprites[flip], lcd.width() / 2, y_center - div_y, a, zoom_x, zoom_y, 0);
}
// draw inner number
distance *= 0.9;
zoom_x = distance / 128.0f;
zoom_y = distance / 128.0f;
sp.createSprite(21, 14);
sp.setPivot((float)sp.width() / 2, distance / zoom_y);
sp.setTextColor(TFT_YELLOW, TFT_BLACK);
for (int i = 0; i < 100; i += 10)
{
float a = fmodf(360.0f + angle - i * 3.6f, 360.0f);
if (a > 70 && a < 290) continue;
sp.drawFloat(float(i)/100, 1, sp.width()>>1, sp.height()>>1);
sp.pushRotateZoomWithAA(&sprites[flip], lcd.width() / 2, y_center - div_y, a, zoom_x, zoom_y, 0);
}
sprites[flip].pushSprite(&lcd, 0, div_y);
flip = !flip;
}
}

View file

@ -0,0 +1,40 @@
#define LGFX_USE_V1
#include <LovyanGFX.hpp>
static LGFX lcd;
static LGFX_Sprite sprite1(&lcd);
static LGFX_Sprite sprite2(&lcd);
static LGFX_Sprite* sprites[2] = {&sprite1, &sprite2};
static int32_t width = 280;
static size_t count = 0;
void setup(void)
{
lcd.init();
lcd.setPivot(lcd.width()>>1, lcd.height()>>1);
width = std::min(width, (std::max(lcd.width(), lcd.height())+10)) | 1;
for (int i = 0; i < 2; ++i) {
sprites[i]->setColorDepth(8);
sprites[i]->createSprite(width, width);
sprites[i]->createPalette();
}
}
void loop(void)
{
++count;
bool flip = count & 1;
for (int i = 0; i < 256; ++i) {
sprites[flip]->setPaletteColor(i, ((i-count-1) & 0x7F)<<1, ((i-count-1) & 0xFF), ((i-count-1) & 0x3F)<<2);
}
sprites[!flip]->clearClipRect();
sprites[!flip]->setColor(count);
sprites[!flip]->fillRect(0,0,width,3);
sprites[!flip]->fillRect(0,0,3,width);
sprites[!flip]->fillRect(width-3,0,3,width);
sprites[!flip]->fillRect(0,width-3,width,3);
sprites[!flip]->setClipRect(3,3,width-6,width-6);
sprites[!flip]->pushRotateZoom(sprites[flip], width>>1, (width>>1)+10, ((float)count)*.5, 0.9, 0.95);
sprites[flip]->pushSprite((lcd.width() - width) >> 1, (lcd.height() - width) >> 1);
}

View file

@ -0,0 +1,440 @@
#include <LovyanGFX.hpp>
/*\
*
* Push your LGFX sprites with style!
*
* LGFX_SpriteFX: A LGFX Layer brought to you by @tobozo, copyleft (c+) 2023
*
\*/
namespace lgfx
{
namespace easing
{
/*\
* Easing Functions - inspired from :
* - http://gizma.com/easing
* - https://easings.net/
* Only considering the t value for the range [0, 1] => [0, 1]
\*/
typedef float (*easing_fn_t)(float in);
// for trigo
const float c1 = 1.70158f;
const float c2 = c1 * 1.525f;
const float c3 = c1 + 1.0f;
const float c4 = (2.0f * PI) / 3.0f;
const float c5 = (2.0f * PI) / 4.5f;
// for bounce
const float n1 = 7.5625f;
const float d1 = 2.75f;
float linear(float t) { return t; }
float easeInQuad(float t) { return powf(t,2.0f); }
float easeOutQuad(float t) { return t*(2.0f-t); }
float easeInOutQuad(float t) { return t<.5f ? 2.0f*powf(t,2) : -1.0f+(4.0f-2.0f*t)*t; }
float easeInCubic(float t) { return powf(t,3.0f); }
float easeOutCubic(float t) { return 1.0f - powf(1.0f - t, 3.0f); }
float easeInOutCubic(float t) { return t<.5f ? 4.0f*powf(t,3) : (t-1.0f)*(2.0f*t-2.0f)*(2.0f*t-2.0f)+1.0f; }
float easeInQuart(float t) { return powf(t,4.0f); }
float easeOutQuart(float t) { return 1.0f - powf(1.0f - t, 4.0f); }
float easeInOutQuart(float t) { return t < 0.5 ? 8.0f * t * t * t * t : 1.0f - powf(-2.0f * t + 2.0f, 4.0f) / 2.0f; }
float easeInQuint(float t) { return powf(t,5.0f); }
float easeOutQuint(float t) { return 1.0f - powf(1.0f - t, 5.0f); }
float easeInOutQuint(float t) { return t < 0.5 ? 16.0f * t * t * t * t * t : 1.0f - powf(-2.0f * t + 2.0f, 5.0f) / 2.0f;/*t<.5f ? 16.0f*powf(t,5) : 1.0f+16.0f*(--t)*powf(t,4);*/ }
float easeInSine(float t) { return 1.0f - cosf((t * PI) / 2.0f); }
float easeOutSine(float t) { return sinf((t * PI) / 2.0f); }
float easeInOutSine(float t) { return -(cosf(PI * t) - 1.0f) / 2.0f; }
float easeInExpo(float t) { return t==0 ? 0 : powf(2, 10.0f * t - 10.0f); }
float easeOutExpo(float t) { return t==1.0f ? 1.0f : 1.0f - powf(2, -10.0f * t); }
float easeInOutExpo(float t) { return t==0 ? 0 : t==1.0f ? 1.0f : t < 0.5f ? powf(2, 20.0f * t - 10.0f) / 2.0f : (2.0f - powf(2, -20.0f * t + 10.0f)) / 2.0f; }
float easeInCirc(float t) { return 1.0f - sqrtf(1.0f - powf(t, 2.0f)); }
float easeOutCirc(float t) { return sqrtf(1.0f - powf(t - 1, 2.0f)); }
float easeInOutCirc(float t) { return t < 0.5f ? (1.0f - sqrtf(1.0f - powf(2.0f * t, 2.0f))) / 2.0f : (sqrtf(1 - powf(-2.0f * t + 2.0f, 2.0f)) + 1.0f) / 2.0f; }
float easeInBack(float t) { return c3 * t * t * t - c1 * t * t; }
float easeOutBack(float t) { return 1.0f + c3 * powf(t - 1.0f, 3.0f) + c1 * powf(t - 1.0f, 2.0f); }
float easeInOutBack(float t) { return t < 0.5f ? (powf(2.0*t, 2.0f) * ((c2 + 1.0f) * 2.0f * t - c2)) / 2.0f : (powf(2.0*t-2, 2.0f) * ((c2 + 1.0f) * (t * 2.0f - 2.0f) + c2) + 2.0f) / 2.0f; }
float easeInElastic(float t) { return t==0 ? 0 : t==1.0f ? 1.0f : -powf(2, 10.0f * t - 10.0f) * sinf((t * 10.0f - 10.75f) * c4); }
float easeOutElastic(float t) { return t==0 ? 0 : t==1.0f ? 1.0f : powf(2, -10.0f * t) * sinf((t * 10.0f - 0.75f) * c4) + 1; }
float easeInOutElastic(float t) { return t==0 ? 0 : t==1 ? 1 : t<0.5 ? -(powf(2, 20.0f*t-10.0f)*sinf((20.0f*t-11.125f)*c5))/2.0f : (powf(2,-20.0f*t+10.0f)*sinf((20.0f*t-11.125f)*c5))/2.0f+1.0f; }
float easeOutBounce(float t) { return t<1.0f/d1 ? n1*t*t : t<2.0f/d1 ? n1*(t-=1.5f/d1)*t+0.75f : t<2.5f/d1 ? n1*(t-=2.25f/d1)*t+0.9375f : n1*(t-=2.625f/d1)*t+0.984375f; }
}; // end namespace easing
namespace transition_fx
{
enum trans_type_t // all transitions effects supported by this addon are listed here
{
PIXEL_STRETCH, // pixel stretch animation, vertical/horizontal both directions
PIXEL_SLICE, // scrolled portions
PIXEL_SPIN,
};
struct anim_param_t // abstract item to hold transition pointer, used by pushAnimated()
{
const trans_type_t type;
const void *ptr;
};
enum trans_spindir_t
{
SPIN_RIGHT,
SPIN_LEFT
};
struct spin_params_t
{
const int32_t x;
const int32_t y;
const trans_spindir_t dir; // spin direction
const uint32_t duration_ms; // easing duration
const easing::easing_fn_t easingFunc;
};
enum trans_axis_t // transition orientation for PIXEL_SLICE
{
AXIS_H,
AXIS_V
};
struct slice_params_t // transition parameters for PIXEL_SLICE
{
const int32_t x;
const int32_t y;
const trans_axis_t dir; // slicing direction
const uint32_t slices; // slices amount
const uint32_t duration_ms; // easing duration
const easing::easing_fn_t easingFunc;
};
struct sliceRect_t // grouped properties for PIXEL_SLICE
{
sliceRect_t() {};
sliceRect_t(int32_t x_, int32_t y_, int32_t clipx_, int32_t clipy_, int32_t clipw_, int32_t cliph_ ) :
x(x_), y(y_), clipx(clipx_), clipy(clipy_), clipw(clipw_), cliph(cliph_) { }
int32_t x{0};
int32_t y{0};
int32_t clipx{0};
int32_t clipy{0};
int32_t clipw{0};
int32_t cliph{0};
};
enum trans_datum_t // transition direction for PIXEL_STRETCH
{
LTR_DATUM, // left to righ
RTL_DATUM, // right to left
TTB_DATUM, // top to bottom
BTT_DATUM // bottom to top
};
struct stretch_params_t // transition parameters for PIXEL_STRETCH
{
const int32_t x;
const int32_t y;
const trans_datum_t dir; // stretch direction
const uint32_t delay_ms; // mininal delay per frame
};
struct coords_float_t // x/y coords as float for PIXEL_STRETCH
{
template <typename T> coords_float_t(T x_, T y_ ) : x(x_+0.0f), y(y_+0.0f) { }
float x;
float y;
};
struct pixel_stretch_fx_t // direction and loop controls for PIXEL_STRETCH
{
public:
pixel_stretch_fx_t( coords_float_t pos_, uint32_t w_, uint32_t h_, trans_datum_t direction_=LTR_DATUM, uint32_t delay_ms_=1 )
: pos(pos_), clipWidth(w_), clipHeight(h_), direction(direction_), delay_ms(delay_ms_)
{
swapx = (direction==RTL_DATUM||direction==BTT_DATUM);
swapy = (direction==TTB_DATUM||direction==BTT_DATUM);
auto clip1 = !swapy ? clipHeight : clipWidth;
auto clip2 = !swapy ? clipWidth : clipHeight;
auto &_pos = !swapy ? pos.y : pos.x;
auto &_middle = !swapy ? middle.y : middle.x;
auto &_topleft = !swapy ? topleft.y : topleft.x;
auto &_scan = !swapy ? scanheight : scanwidth;
_middle = _pos + clip1/2.0f;
_topleft = _pos;
_scan = clip1;
loopstart = swapx ? clip2+1.0f : 0;
loopend = swapx ? -1.0f : clip2;
loopdir = swapx ?-1.0f : 1.0f;
tick();
last_ms = anim_start = millis();
};
void tick()
{
auto &_scan = !swapy ? scanpos.x : scanpos.y;
auto &_zoom = !swapy ? zoom.x : zoom.y;
auto &_middle = !swapy ? middle.x : middle.y;
auto &_topleft = !swapy ? topleft.x : topleft.y;
auto &_pos = !swapy ? pos.x : pos.y;
auto _clip = !swapx ? !swapy ? clipWidth : clipHeight : 0;
_scan = -loopstart;
_zoom = (!swapx ? _clip-loopstart : loopstart) - 0.5f;
_middle = ((!swapx ? _pos+loopstart : _pos) + _zoom/2.0f) - 0.5f;
_topleft = _pos + loopstart;
}
bool next()
{
loopstart += loopdir;
handleTimer();
tick();
return loopstart!=loopend;
}
void handleTimer()
{
uint32_t render_ms_tmp = millis() - last_ms;
if( delay_ms > 1 ) {
if( render_ms_tmp < delay_ms ) {
vTaskDelay( delay_ms - render_ms_tmp );
}
}
last_ms = millis();
}
// scanline
coords_float_t scanpos = { 0, 0 };
float scanwidth = 1;
float scanheight = 1;
// projection zone
coords_float_t middle = { 0, 0 };
coords_float_t zoom = { 1, 1 };
coords_float_t topleft = { 0, 0 };
// timer helpers
uint32_t anim_start = 0;
uint32_t last_ms = 0;
const float rotate = 0;
private:
const coords_float_t pos;
const float clipWidth;
const float clipHeight;
const trans_datum_t direction;
const uint32_t delay_ms;// 15=66fps
float loopstart = 0;
float loopend = 0;
float loopdir = 1;
bool swapx = false;
bool swapy = false;
};
}; // end namespace transition_fx
using namespace transition_fx;
using namespace easing;
class LGFX_SpriteFx : public LGFX_Sprite
{
public:
LGFX_SpriteFx(LovyanGFX* parent) : LGFX_Sprite(parent) { };
void spin( int32_t x=0, int32_t y=0, trans_spindir_t dir=SPIN_RIGHT, uint32_t duration=255, easing_fn_t easing=linear ) { spin_impl({x,y,dir,duration,easing}); }
void spinC( int32_t x=0, int32_t y=0, uint32_t duration=255, easing_fn_t easing=linear ) { spin_impl({x,y,SPIN_RIGHT,duration,easing}); }
void spinA( int32_t x=0, int32_t y=0, uint32_t duration=255, easing_fn_t easing=linear ) { spin_impl({x,y,SPIN_LEFT,duration,easing}); }
void slice( int32_t x=0, int32_t y=0, trans_axis_t axis=AXIS_V, uint32_t slices=8,
uint32_t duration=255, easing_fn_t easing=linear ) { slice_impl({x,y,axis,slices,duration,easing}); }
void sliceH( int32_t x=0, int32_t y=0, uint8_t slices=8,
uint32_t duration=255, easing_fn_t easing=linear ) { slice_impl({x,y,AXIS_H,slices,duration,easing}); }
void sliceV( int32_t x=0, int32_t y=0, uint8_t slices=8,
uint32_t duration=255, easing_fn_t easing=linear ) { slice_impl({x,y,AXIS_V,slices,duration,easing}); }
void stretch( int32_t x=0, int32_t y=0, trans_datum_t dir=LTR_DATUM, uint32_t delay_ms=0 ) { stretch_impl({x,y,dir,delay_ms}); }
void stretchLTR( int32_t x=0, int32_t y=0, uint32_t delay_ms=0 ) { stretch_impl({x,y,LTR_DATUM,delay_ms}); }
void stretchRTL( int32_t x=0, int32_t y=0, uint32_t delay_ms=0 ) { stretch_impl({x,y,RTL_DATUM,delay_ms}); }
void stretchTTB( int32_t x=0, int32_t y=0, uint32_t delay_ms=0 ) { stretch_impl({x,y,TTB_DATUM,delay_ms}); }
void stretchBTT( int32_t x=0, int32_t y=0, uint32_t delay_ms=0 ) { stretch_impl({x,y,BTT_DATUM,delay_ms}); }
void pushAnimated( anim_param_t* params )
{
assert( params );
assert( params->ptr );
switch( params->type ) {
case PIXEL_STRETCH: {
auto pixel_stretch = (stretch_params_t*) params->ptr;
stretch_impl( {pixel_stretch->x, pixel_stretch->y, pixel_stretch->dir, pixel_stretch->delay_ms} );
}
break;
case PIXEL_SLICE: {
auto pixel_slice = (slice_params_t*) params->ptr;
slice_impl( {pixel_slice->x, pixel_slice->y, pixel_slice->dir, pixel_slice->slices, pixel_slice->duration_ms, pixel_slice->easingFunc} );
}
break;
case PIXEL_SPIN: {
auto pixel_spin = (spin_params_t*) params->ptr;
spin_impl( {pixel_spin->x, pixel_spin->y, pixel_spin->dir, pixel_spin->duration_ms, pixel_spin->easingFunc} );
}
}
}
private:
void spin_impl( spin_params_t params )
{
if( _parent == nullptr ) {
log_e("Sprite has no parent, can't guess display");
return;
}
auto x = params.x;
auto y = params.y;
auto dir = params.dir;
auto duration = params.duration_ms;
auto easingFunc = params.easingFunc;
const uint32_t timer_start = millis();
const uint32_t timer_end = timer_start + duration;
const uint32_t max_steps = 1000;
const float middlex = float(x) + float(this->width()/2.0f);// - float(x/2.0f);
const float middley = float(y) + float(this->height()/2.0f);// - float(y/2.0f);
const int anglestart = dir==SPIN_RIGHT ? -360:360;
const int angleend = -anglestart;
uint32_t timer_pos = 0;
uint32_t timer_now = 0;
_parent->setClipRect( x, y, this->width(), this->height() );
do {
timer_now = millis();
if( timer_now>timer_end ) timer_now=timer_end;
timer_pos = map( timer_now, timer_start, timer_end, 0, max_steps );
float ifloat = float(timer_pos)/float(max_steps);
float zoom = easingFunc( ifloat );
int angle = map( int(zoom*max_steps), 0, max_steps, anglestart, angleend );
this->pushRotateZoom( _parent, middlex, middley, angle, zoom, zoom );
} while( timer_now<timer_end );
_parent->clearClipRect();
}
void slice_impl( slice_params_t params )
{
if( _parent == nullptr ) {
log_e("Sprite has no parent, can't guess display");
return;
}
auto x = params.x;
auto y = params.y;
auto slices = params.slices;
auto axis = params.dir;
auto duration = params.duration_ms;
auto easingFunc = params.easingFunc;
const uint32_t timer_start = millis();
const uint32_t timer_end = timer_start + duration;
const uint32_t max_steps = 1000;
const bool is_vertical = axis==AXIS_V;
const int32_t scroll_pan_max = is_vertical ? this->width() : this->height();
const int32_t scroll_pos_max = is_vertical ? this->height() : this->width();
if( slices < 2 ) slices = 2;
if( slices > scroll_pan_max/2 ) slices = scroll_pan_max/2;
const int32_t slice_pad = scroll_pan_max/slices; // slice width or slice height depending axis
int32_t scroll_pos = 0;
uint32_t steps = 1000;
uint32_t timer_pos = 0;
uint32_t timer_now = 0;
do {
timer_now = millis();
if( timer_now>timer_end ) timer_now=timer_end;
timer_pos = map( timer_now, timer_start, timer_end, 0, max_steps );
float ifloat = float(timer_pos)/float(steps);
float eased = easingFunc( ifloat );
scroll_pos = map( int(eased*steps), 0, steps, 0, scroll_pos_max );
const int32_t slice1 = is_vertical ? (y-this->height())+scroll_pos : (x-this->width())+scroll_pos;
const int32_t slice2 = is_vertical ? (y+this->height())-scroll_pos : (x+this->width())-scroll_pos;
int32_t slice_num = 0;
do { // secondary axis (opposite slicing)
const int32_t offset = slice_num*slice_pad;
const auto rect = (is_vertical)
? sliceRect_t( x, slice_num%2==0 ? slice2 : slice1, x+offset, y, slice_pad, this->height() )
: sliceRect_t( slice_num%2==0 ? slice2 : slice1, y, x, y+offset, this->width(), slice_pad )
;
_parent->setClipRect( rect.clipx, rect.clipy, rect.clipw, rect.cliph );
this->pushSprite( _parent, rect.x, rect.y );
slice_num++;
} while( slice_num <= slices );
} while( timer_now<timer_end );
_parent->clearClipRect();
}
void stretch_impl( stretch_params_t params )
{
if( _parent == nullptr ) {
log_e("Sprite has no parent, can't guess display");
return;
}
auto x = params.x;
auto y = params.y;
auto direction = params.dir;
auto delay_ms = params.delay_ms;
pixel_stretch_fx_t fx( {x, y}, this->width(), this->height(), direction, delay_ms );
LGFX_Sprite *scanline = new LGFX_Sprite();
scanline->setColorDepth( this->getColorDepth() );
scanline->setPsram( false ); // use heap for faster frame rate
if( !scanline->createSprite( fx.scanwidth, fx.scanheight ) ) {
log_e("Not enough ram to create stretch effect");
this->pushSprite( _parent, x, y );
delete scanline;
return;
}
_parent->setClipRect( x, y, this->width(), this->height() );
do {
this->pushSprite( scanline, fx.scanpos.x, fx.scanpos.y );
scanline->pushRotateZoom( _parent, fx.middle.x, fx.middle.y, fx.rotate, fx.zoom.x, fx.zoom.y );
} while( fx.next() );
_parent->clearClipRect();
scanline->deleteSprite();
delete scanline;
}
};
};
using lgfx::LGFX_SpriteFx;

View file

@ -0,0 +1,197 @@
/*\
*
* Push your LGFX sprites with style!
*
* LGFX_SpriteFX: A LGFX Layer brought to you by @tobozo, copyleft (c+) 2023
*
* This demo demonstrates the different ways a sprite can
* be printed to the display using transitions.
*
* Transition effects:
* - Spin: appears in a zommed spin
* - Slice: sliced and merged
* - Stretch: spread from one edge
*
* Easing function: plenty!
*
* Note: "Strech" transition does not support easing, only min delay between frames.
*
* Simple usage:
*
* // create a sprite using "LGFX_SpriteFx" instead of "LGFX_Sprite" constructor.
*
* auto sprite = new LGFX_SpriteFx(&tft);
*
* // draw some stuff in the sprite...
*
* // when the sprite is ready to be pushed, instead of sprite->pushSprite( x, y ), use one of these these:
*
* sprite->spin( x, y );
* sprite->slice( x, y );
* sprite->stretch( x, y );
*
*
* Advanced usage:
*
* See LGFX_SpriteFX.hpp and this sketch for more advanced examples.
*
\*/
#define LGFX_AUTODETECT
#include "LGFX_SpriteFX.hpp"
#include "assets.h" // some example images for this demo
struct easingFuncDesc_t
{
const char* name;
lgfx::easing::easing_fn_t func;
};
struct img_t
{
const unsigned char* data;
const unsigned int len;
const uint32_t w;
const uint32_t h;
};
static LGFX tft;
static LGFX_SpriteFx *sptr;
const img_t dog = img_t { dog_200_200_jpg, dog_200_200_jpg_len, 200, 200 };
const img_t logo = img_t { lgfx_logo_201x197_jpg, lgfx_logo_201x197_jpg_len, 201, 197 };
const img_t palette1 = img_t { palette_300x128_jpg, palette_300x128_jpg_len, 300, 128 };
const img_t palette2 = img_t { palette_128x200_jpg, palette_128x200_jpg_len, 128, 200 };
const img_t *images[] = { &palette1, &palette2, &dog, &logo };
const easingFuncDesc_t easingFunctions[] =
{
{ "easeOutBounce", lgfx::easing::easeOutBounce },
{ "easeOutElastic", lgfx::easing::easeOutElastic },
// { "easeInElastic", lgfx::easing::easeInElastic },
// { "easeInOutElastic", lgfx::easing::easeInOutElastic },
// { "easeInQuad", lgfx::easing::easeInQuad },
// { "easeOutQuad", lgfx::easing::easeOutQuad },
// { "easeInOutQuad", lgfx::easing::easeInOutQuad },
// { "easeInCubic", lgfx::easing::easeInCubic },
// { "easeOutCubic", lgfx::easing::easeOutCubic },
// { "easeInOutCubic", lgfx::easing::easeInOutCubic },
// { "easeInQuart", lgfx::easing::easeInQuart },
// { "easeOutQuart", lgfx::easing::easeOutQuart },
// { "easeInOutQuart", lgfx::easing::easeInOutQuart },
// { "easeInQuint", lgfx::easing::easeInQuint },
// { "easeOutQuint", lgfx::easing::easeOutQuint },
// { "easeInOutQuint", lgfx::easing::easeInOutQuint },
// { "easeInSine", lgfx::easing::easeInSine },
// { "easeOutSine", lgfx::easing::easeOutSine },
// { "easeInOutSine", lgfx::easing::easeInOutSine },
// { "easeInExpo", lgfx::easing::easeInExpo },
// { "easeOutExpo", lgfx::easing::easeOutExpo },
// { "easeInOutExpo", lgfx::easing::easeInOutExpo },
// { "easeInCirc", lgfx::easing::easeInCirc },
// { "easeOutCirc", lgfx::easing::easeOutCirc },
// { "easeInOutCirc", lgfx::easing::easeInOutCirc },
// { "easeInBack", lgfx::easing::easeInBack },
// { "easeOutBack", lgfx::easing::easeOutBack },
// { "easeInOutBack", lgfx::easing::easeInOutBack },
// { "linear", lgfx::easing::linear },
};
void clearZone( int x, int y, int w, int h, uint32_t delay_ms )
{
delay( delay_ms );
tft.fillRect( x, y, w, h, TFT_WHITE );
}
void setup()
{
Serial.begin( 115200 );
tft.init();
tft.setTextDatum( TL_DATUM );
tft.setTextPadding( tft.width() );
tft.setTextColor( TFT_WHITE, TFT_BLACK );
tft.setTextSize(2);
tft.setTextWrap( false );
sptr = new LGFX_SpriteFx(&tft);
sptr->setPsram( false );
}
void loop()
{
static uint32_t imgidx = 0;
static uint32_t imgcount = sizeof(images)/sizeof(img_t*);
static uint32_t min_delay_ms = 8;
auto imgmod = imgidx%imgcount;
min_delay_ms = (imgmod==0) ? 8-min_delay_ms : min_delay_ms;
auto img = images[imgmod];
auto posx = tft.width()/2 - img->w/2;
auto posy = tft.height()/2 - img->h/2;
if( ! sptr->createSprite( img->w, img->h ) ) return;
// highlight zone
tft.fillRect( posx, posy, sptr->width(), sptr->height(), TFT_WHITE );
tft.drawRect( posx-1, posy-1, sptr->width()+2, sptr->height()+2, TFT_RED );
// fill sprite with image
sptr->drawJpg( img->data, img->len, 0, 0, img->w, img->h );
sptr->stretchRTL( posx, posy, min_delay_ms ); // clearZone( posx, posy, img->w, img->h, 500 );
sptr->stretchBTT( posx, posy, min_delay_ms ); // clearZone( posx, posy, img->w, img->h, 500 );
sptr->stretchLTR( posx, posy, min_delay_ms ); // clearZone( posx, posy, img->w, img->h, 500 );
sptr->stretchTTB( posx, posy, min_delay_ms ); // clearZone( posx, posy, img->w, img->h, 500 );
size_t easing_functions_count = sizeof(easingFunctions)/sizeof(easingFuncDesc_t);
for( int i=0;i<easing_functions_count;i++ ) {
auto easingFunc = easingFunctions[i].func;
auto funcName = easingFunctions[i].name;
Serial.printf("Easing: %s\n", funcName );
tft.setCursor( 0, 0 );
tft.printf("Easing: %-32s", funcName );
sptr->spinC( posx, posy, 1000, easingFunc ); // clearZone( posx, posy, img->w, img->h, 500 );
sptr->spinA( posx, posy, 1000, easingFunc ); // clearZone( posx, posy, img->w, img->h, 500 );
sptr->sliceH( posx, posy, 0, 1000, easingFunc ); // clearZone( posx, posy, img->w, img->h, 500 );
sptr->sliceV( posx, posy, 0, 1000, easingFunc ); // clearZone( posx, posy, img->w, img->h, 500 );
sptr->sliceH( posx, posy, 4, 1000, easingFunc ); // clearZone( posx, posy, img->w, img->h, 500 );
sptr->sliceV( posx, posy, 4, 1000, easingFunc ); // clearZone( posx, posy, img->w, img->h, 500 );
sptr->sliceH( posx, posy, 8, 1000, easingFunc ); // clearZone( posx, posy, img->w, img->h, 500 );
sptr->sliceV( posx, posy, 8, 1000, easingFunc ); // clearZone( posx, posy, img->w, img->h, 500 );
sptr->sliceH( posx, posy, 16, 1000, easingFunc ); // clearZone( posx, posy, img->w, img->h, 500 );
sptr->sliceV( posx, posy, 16, 1000, easingFunc ); // clearZone( posx, posy, img->w, img->h, 500 );
sptr->sliceH( posx, posy, 255, 1000, easingFunc ); // clearZone( posx, posy, img->w, img->h, 500 );
sptr->sliceV( posx, posy, 255, 1000, easingFunc ); // clearZone( posx, posy, img->w, img->h, 500 );
}
tft.setCursor( 0, 0 );
tft.printf("%64s", "" );
// clear zone
tft.fillRect( posx-1, posy-1, img->w+2, img->h+2, TFT_BLACK );
imgidx++;
}

File diff suppressed because it is too large Load diff