update firmware source code to v0.18 (#9)

This commit is contained in:
Forairaaaaa
2026-03-25 11:11:14 +08:00
committed by GitHub
parent 5001b7081b
commit 605b575fcc
123 changed files with 24590 additions and 1899 deletions
@@ -0,0 +1,99 @@
/*
* SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
#include "neon_light.h"
#include <hal/hal.h>
using namespace stackchan::addon;
void NeonLight::init()
{
// Setup color animation
_color_anim.duration = 0.3f;
_color_anim.begin();
_is_inited = true;
}
void NeonLight::update()
{
if (!_is_inited) {
init();
}
// Keep update in at most 50Hz
if (GetHAL().millis() - _last_tick < 20) {
return;
}
_last_tick = GetHAL().millis();
// Apply color animation
if (!_color_anim.done()) {
_color_anim.updateWithDelta(0.02f); // Fixed delta time for consistency
for (int i = 0; i < _led_count; i++) {
set_rgb_color_impl(i, _color_anim.r, _color_anim.g, _color_anim.b);
}
refresh_rgb_impl();
}
// Snap to target angle when animation ends
else if (_snap_to_target_on_rest) {
_snap_to_target_on_rest = false;
for (int i = 0; i < _led_count; i++) {
set_rgb_color_impl(i, _color_anim.r, _color_anim.g, _color_anim.b);
}
refresh_rgb_impl();
}
}
void NeonLight::setColor(uint8_t r, uint8_t g, uint8_t b)
{
_color_anim.move(r, g, b);
_snap_to_target_on_rest = true;
}
void NeonLight::setColor(const uitk::color::Rgb_t& rgb)
{
_color_anim.move(rgb);
_snap_to_target_on_rest = true;
}
void NeonLight::setColor(uint32_t hex)
{
_color_anim.move(hex);
_snap_to_target_on_rest = true;
}
void NeonLight::setColor(std::string_view hex)
{
_color_anim.move(hex);
_snap_to_target_on_rest = true;
}
void NeonLight::setDuration(float durationSec)
{
_color_anim.duration = durationSec;
_color_anim.begin();
}
void LeftNeonLight::set_rgb_color_impl(uint8_t index, uint8_t r, uint8_t g, uint8_t b)
{
GetHAL().setRgbColor(index, r, g, b);
}
void LeftNeonLight::refresh_rgb_impl()
{
GetHAL().refreshRgb();
}
void RightNeonLight::set_rgb_color_impl(uint8_t index, uint8_t r, uint8_t g, uint8_t b)
{
GetHAL().setRgbColor(index + 6, r, g, b);
}
void RightNeonLight::refresh_rgb_impl()
{
GetHAL().refreshRgb();
}
@@ -0,0 +1,80 @@
/*
* SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstdint>
#include <smooth_ui_toolkit.hpp>
#include <uitk/short_namespace.hpp>
#include "core/color/color.hpp"
#include <string_view>
namespace stackchan::addon {
/**
* @brief
*
*/
class NeonLight {
public:
NeonLight(int ledCount) : _led_count(ledCount)
{
}
void init();
void update();
void setColor(uint8_t r, uint8_t g, uint8_t b);
void setColor(const uitk::color::Rgb_t& rgb);
void setColor(uint32_t hex);
void setColor(std::string_view hex);
void setDuration(float durationSec);
int getLedCount() const
{
return _led_count;
}
protected:
virtual void set_rgb_color_impl(uint8_t index, uint8_t r, uint8_t g, uint8_t b) = 0;
virtual void refresh_rgb_impl() = 0;
private:
int _led_count;
bool _is_inited = false;
bool _snap_to_target_on_rest = false;
uint32_t _last_tick = 0;
uitk::color::AnimateRgb_t _color_anim;
};
/**
* @brief
*
*/
class LeftNeonLight : public NeonLight {
public:
LeftNeonLight() : NeonLight(6)
{
}
private:
void set_rgb_color_impl(uint8_t index, uint8_t r, uint8_t g, uint8_t b) override;
void refresh_rgb_impl() override;
};
/**
* @brief
*
*/
class RightNeonLight : public NeonLight {
public:
RightNeonLight() : NeonLight(6)
{
}
private:
void set_rgb_color_impl(uint8_t index, uint8_t r, uint8_t g, uint8_t b) override;
void refresh_rgb_impl() override;
};
} // namespace stackchan::addon
@@ -19,6 +19,7 @@ void Keyframe::apply()
feat.setPosition(kf.position);
feat.setRotation(kf.rotation);
feat.setWeight(kf.weight);
feat.setSize(kf.size);
};
apply_feature(leftEye, avatar.leftEye());
apply_feature(rightEye, avatar.rightEye());
@@ -27,6 +28,9 @@ void Keyframe::apply()
auto apply_servo = [&](const ServoKeyframe& kf, motion::Servo& servo) { servo.moveWithSpeed(kf.angle, kf.speed); };
apply_servo(yawServo, motion.yawServo());
apply_servo(pitchServo, motion.pitchServo());
stackchan.leftNeonLight().setColor(leftRgbColor);
stackchan.rightNeonLight().setColor(rightRgbColor);
}
void Timeline::start()
@@ -8,6 +8,7 @@
#include <uitk/short_namespace.hpp>
#include <cstdint>
#include <vector>
#include <string>
namespace stackchan::animation {
@@ -19,6 +20,7 @@ struct FeatureKeyframe {
uitk::Vector2i position;
int rotation;
int weight;
int size;
FeatureKeyframe(int x = 0, int y = 0, int rotation = 0, int weight = 0)
: position(x, y), rotation(rotation), weight(weight)
@@ -49,6 +51,8 @@ struct Keyframe {
FeatureKeyframe mouth;
ServoKeyframe yawServo;
ServoKeyframe pitchServo;
std::string leftRgbColor;
std::string rightRgbColor;
uint32_t durationMs = 0;
Keyframe()
@@ -97,6 +97,16 @@ public:
}
}
void setModifyLock(bool locked)
{
_is_modify_locked = locked;
}
bool isModifyLocked()
{
return _is_modify_locked;
}
/* ---------------------------- Decorator helpers --------------------------- */
int addDecorator(std::unique_ptr<Decorator> decorator)
@@ -120,6 +130,8 @@ protected:
Emotion _emotion = Emotion::Neutral;
KeyElements_t _key_elements;
ObjectPool<Decorator> _decorator_pool;
bool _is_modify_locked = false;
};
} // namespace stackchan::avatar
@@ -19,7 +19,7 @@ public:
virtual ~Element() = default;
/**
* @brief (-100~100, -100~100)
* @brief (-100 ~ 100, -100 ~ 100)
*
* @param position
*/
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: MIT
*/
#include "decorators.h"
#include <hal/hal.h>
#include <vector>
using namespace uitk;
@@ -17,47 +18,77 @@ static const std::vector<int> _angry_rotation_frames = {150, 200};
LV_IMAGE_DECLARE(decorator_angry);
AngryDecorator::AngryDecorator(lv_obj_t* parent, uint32_t destroyAfterMs, uint32_t animationIntervalMs)
: _animation_interval_ms(animationIntervalMs)
{
// 初始化 UI 组件
_angry = std::make_unique<Image>(parent);
_angry->setSrc(&decorator_angry);
_angry->setAlign(LV_ALIGN_CENTER);
_angry->setPos(_angry_default_position.x, _angry_default_position.y);
// 设置旋转中心和初始角度
_angry->setTransformPivot(_angry->getWidth() / 2, _angry->getHeight() / 2);
_angry->setRotation(_angry_rotation_frames[1]);
_angry->setRotation(_angry_rotation_frames[0]);
// 设置颜色偏置
_angry->setImageRecolorOpa(LV_OPA_COVER);
_angry->setImageRecolor(_angry_default_color);
if (destroyAfterMs != 0) {
scheduleDestroy(destroyAfterMs);
uint32_t now = GetHAL().millis();
// 初始化销毁倒计时
if (destroyAfterMs > 0) {
_destroy_at = now + destroyAfterMs;
_has_lifetime = true;
}
if (animationIntervalMs != 0) {
getTimer().addTask(animationIntervalMs, -1, 0, [this]() {
setRotation(_angry_rotation_frames[_animation_index]);
_animation_index++;
if (_animation_index >= _angry_rotation_frames.size()) {
_animation_index = 0;
}
});
// 初始化动画倒计时
if (_animation_interval_ms > 0) {
_next_animation_tick = now + _animation_interval_ms;
}
}
AngryDecorator::~AngryDecorator()
{
_angry.reset();
}
void AngryDecorator::_update()
{
uint32_t now = GetHAL().millis();
// 检查自动销毁
if (_has_lifetime && now >= _destroy_at) {
requestDestroy();
return;
}
// 检查动画跳变
if (_animation_interval_ms > 0 && now >= _next_animation_tick) {
_next_animation_tick = now + _animation_interval_ms;
// 切换帧
_animation_index = (_animation_index + 1) % _angry_rotation_frames.size();
_angry->setRotation(_angry_rotation_frames[_animation_index]);
}
}
void AngryDecorator::setPosition(int x, int y)
{
_angry->setPos(x, y);
if (_angry) {
_angry->setPos(x, y);
}
}
void AngryDecorator::setRotation(int rotation)
{
_angry->setRotation(rotation);
if (_angry) {
_angry->setRotation(rotation);
}
}
void AngryDecorator::setColor(lv_color_t color)
{
_angry->setImageRecolor(color);
if (_angry) {
_angry->setImageRecolor(color);
}
}
@@ -0,0 +1,233 @@
#ifdef __has_include
#if __has_include("lvgl.h")
#ifndef LV_LVGL_H_INCLUDE_SIMPLE
#define LV_LVGL_H_INCLUDE_SIMPLE
#endif
#endif
#endif
#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
#include "lvgl.h"
#else
#include "lvgl/lvgl.h"
#endif
#ifndef LV_ATTRIBUTE_MEM_ALIGN
#define LV_ATTRIBUTE_MEM_ALIGN
#endif
#ifndef LV_ATTRIBUTE_IMAGE_DECORATOR_DIZZY
#define LV_ATTRIBUTE_IMAGE_DECORATOR_DIZZY
#endif
const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_DECORATOR_DIZZY uint8_t
decorator_dizzy_map[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
0x80, 0xaf, 0xdf, 0xff, 0xff, 0xcf, 0xbf, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x6f, 0xbf, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xdf, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x20, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x8f, 0x5f, 0x40, 0x5f, 0x80, 0xbf, 0xff, 0xff,
0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0xef,
0xff, 0xff, 0xff, 0xff, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xaf, 0xff, 0xff, 0xff,
0xff, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xbf,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xbf, 0xff, 0xff, 0xff, 0xef, 0x70, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xcf, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x20, 0xdf, 0xff, 0xff, 0xff, 0xcf, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0xff, 0xff, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
0xdf, 0xff, 0xff, 0xff, 0x8f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x5f, 0x80, 0x80, 0x8f, 0x80, 0x80,
0x5f, 0x40, 0x20, 0x00, 0x00, 0x10, 0xaf, 0xef, 0x70, 0x00, 0x00, 0x00, 0x00, 0x20, 0xdf, 0xff, 0xff, 0xff,
0x80, 0x00, 0x00, 0x00, 0x00, 0x40, 0x8f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x9f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xdf, 0xff, 0xff, 0xff, 0x60, 0x00, 0x00, 0x00,
0x20, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcf, 0x10,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, 0xff, 0xff, 0xff, 0x60, 0x00, 0x00, 0x00, 0x30, 0xdf, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xef, 0xdf, 0xdf, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x60, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x60, 0xff, 0xff, 0xff, 0xff, 0xef, 0x9f, 0x40,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x30, 0xdf, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x20, 0xdf,
0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x20, 0xef, 0xff, 0xff, 0xff, 0x8f, 0x10, 0x00, 0x00, 0x00, 0x30, 0x40,
0x20, 0x00, 0x00, 0x00, 0x00, 0x40, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xdf, 0x20,
0x00, 0x00, 0x20, 0xdf, 0xff, 0xff, 0xef, 0x70, 0x00, 0x00, 0x00, 0x30, 0xbf, 0xff, 0xff, 0xff, 0x50, 0x00,
0x00, 0x00, 0x00, 0xbf, 0xff, 0xff, 0xcf, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0x60, 0x00, 0x00, 0x00, 0x9f,
0xff, 0xff, 0xff, 0x50, 0x00, 0x00, 0x00, 0x70, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x10, 0x00, 0x00, 0x00,
0x80, 0xff, 0xff, 0xff, 0x10, 0x00, 0x50, 0xff, 0xff, 0xdf, 0x00, 0x00, 0x00, 0x30, 0xff, 0xff, 0xff, 0x80,
0x00, 0x00, 0x10, 0xaf, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0x5f, 0x00, 0x00, 0x00, 0x40, 0xff, 0xff,
0xff, 0x40, 0x00, 0x9f, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x9f, 0xff, 0xff, 0xdf, 0x00, 0x00, 0x10, 0xcf,
0xff, 0xff, 0xff, 0xdf, 0x30, 0xdf, 0xff, 0xff, 0x8f, 0x00, 0x00, 0x00, 0x20, 0xff, 0xff, 0xff, 0x5f, 0x00,
0xbf, 0xff, 0xff, 0x50, 0x00, 0x00, 0x00, 0xcf, 0xff, 0xff, 0x6f, 0x00, 0x00, 0xaf, 0xff, 0xff, 0xff, 0xaf,
0x10, 0x00, 0xbf, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x10, 0xff, 0xff, 0xff, 0x5f, 0x00, 0xef, 0xff, 0xff,
0x30, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x40, 0x00, 0x70, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0xbf,
0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x10, 0xff, 0xff, 0xff, 0x5f, 0x00, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00,
0x00, 0xff, 0xff, 0xff, 0x40, 0x00, 0xbf, 0xff, 0xff, 0xaf, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xff, 0xff, 0x9f,
0x00, 0x00, 0x00, 0x30, 0xff, 0xff, 0xff, 0x40, 0x00, 0xdf, 0xff, 0xff, 0x30, 0x00, 0x00, 0x00, 0xef, 0xff,
0xff, 0x6f, 0x00, 0x70, 0xff, 0xdf, 0x20, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00,
0x60, 0xff, 0xff, 0xff, 0x20, 0x00, 0xcf, 0xff, 0xff, 0x5f, 0x00, 0x00, 0x00, 0x9f, 0xff, 0xff, 0xdf, 0x10,
0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0xbf, 0xff, 0xff,
0xdf, 0x00, 0x00, 0x8f, 0xff, 0xff, 0xaf, 0x00, 0x00, 0x00, 0x40, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x10, 0xef, 0xff, 0xff, 0x80, 0x00, 0x00, 0x20, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00,
0x40, 0xff, 0xff, 0xef, 0x10, 0x00, 0x00, 0x00, 0xaf, 0xff, 0xff, 0xff, 0xbf, 0x20, 0x00, 0x00, 0x00, 0x00,
0x00, 0x80, 0xff, 0xff, 0xff, 0x50, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0xef, 0xff,
0xff, 0x9f, 0x00, 0x00, 0x00, 0x10, 0xcf, 0xff, 0xff, 0xff, 0xef, 0x9f, 0x40, 0x20, 0x40, 0xaf, 0xff, 0xff,
0xff, 0xdf, 0x00, 0x00, 0x8f, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x60, 0xff, 0xff, 0xff, 0x50,
0x00, 0x00, 0x00, 0x20, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x50, 0x00,
0x80, 0xff, 0xff, 0xff, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xff, 0xff, 0xef, 0x20, 0x00, 0x00,
0x00, 0x00, 0x80, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x70, 0x00, 0x80, 0xff, 0xff, 0xff,
0xdf, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xef, 0xff, 0xff, 0xdf, 0x20, 0x00, 0x00, 0x00, 0x00,
0x20, 0x8f, 0xdf, 0xff, 0xff, 0xff, 0xef, 0x8f, 0x20, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x30, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xff, 0xff, 0xff, 0xdf, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x10, 0x30, 0x20, 0x00, 0x00, 0x10, 0x9f, 0xff, 0xff, 0xff, 0xff, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xdf, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x40, 0xdf, 0xff, 0xff, 0xff, 0xef, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x20, 0x80, 0xdf, 0xff, 0xff,
0xff, 0xff, 0xdf, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xbf, 0xcf, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x20,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0xdf,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x8f, 0xdf, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x80, 0x9f, 0x9f,
0x8f, 0x5f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const lv_image_dsc_t decorator_dizzy = {
.header.cf = LV_COLOR_FORMAT_RGB565A8,
.header.magic = LV_IMAGE_HEADER_MAGIC,
.header.w = 33,
.header.h = 36,
.data_size = 1188 * 3,
.data = decorator_dizzy_map,
};
@@ -0,0 +1,145 @@
#ifdef __has_include
#if __has_include("lvgl.h")
#ifndef LV_LVGL_H_INCLUDE_SIMPLE
#define LV_LVGL_H_INCLUDE_SIMPLE
#endif
#endif
#endif
#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
#include "lvgl.h"
#else
#include "lvgl/lvgl.h"
#endif
#ifndef LV_ATTRIBUTE_MEM_ALIGN
#define LV_ATTRIBUTE_MEM_ALIGN
#endif
#ifndef LV_ATTRIBUTE_IMAGE_DECORATOR_SHY
#define LV_ATTRIBUTE_IMAGE_DECORATOR_SHY
#endif
const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_DECORATOR_SHY uint8_t decorator_shy_map[] = {
0x54, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x54, 0xfd, 0x14, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x54, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xfd, 0x54, 0xfd,
0x34, 0xfd, 0x34, 0xfd, 0x14, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x54, 0xfd, 0x54, 0xfd, 0x34, 0xfd, 0x54, 0xfd, 0x14, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34,
0xfd, 0x14, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xfd,
0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34,
0xfd, 0x34, 0xfd, 0x33, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xfd, 0x34, 0xfd, 0x34,
0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x54, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x54, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x33, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd,
0x34, 0xfd, 0x34, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xfd, 0x34,
0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd,
0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x54, 0xfd, 0x14, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x33, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xf5, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x54, 0xfd, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xfd, 0x34, 0xfd,
0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x14, 0xf5, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34,
0xfd, 0x14, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x33, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34,
0xfd, 0x00, 0x00, 0x00, 0x00, 0x54, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34,
0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x54, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34,
0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd,
0x34, 0xfd, 0x14, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xf5, 0x34, 0xfd, 0x34,
0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xf5, 0x54, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34,
0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x54, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd,
0x34, 0xfd, 0x34, 0xfd, 0x54, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54,
0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xfd, 0x34, 0xfd, 0x34,
0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xfd, 0x34, 0xfd,
0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x33, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14,
0xf5, 0x54, 0xfd, 0x54, 0xfd, 0x34, 0xfd, 0x54, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x54, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x14, 0xf5, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xfd, 0x54, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xfd, 0x34, 0xfd, 0x34, 0xfd, 0x54, 0xfd, 0x14, 0xf5, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x20, 0xcf, 0xef, 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xbf, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xbf,
0xff, 0xaf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xbf, 0xff, 0xbf, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf,
0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xff, 0xff, 0xff, 0xaf, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0xff, 0xff, 0xff, 0xff, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xef, 0xff, 0xff, 0xff, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xff, 0xff, 0xff, 0xff, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xbf, 0xff, 0xff, 0xff, 0xff, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, 0xff, 0xff, 0xff, 0xff,
0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xff, 0xff, 0xff, 0xff, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x6f, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xdf, 0xff, 0xff,
0xff, 0xff, 0xff, 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcf, 0xff, 0xff, 0xff, 0xff, 0x40, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x10, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xef,
0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x80, 0x10, 0x00, 0x00, 0x00, 0x60, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x30, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x80, 0x00, 0x00, 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0x30,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x20, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x9f, 0xff, 0xff, 0xff, 0xff,
0xdf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xff, 0xff, 0xff, 0xff, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x10, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaf, 0x00, 0x00, 0x20, 0xef, 0xff, 0xff,
0xff, 0xff, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xff, 0xff, 0xff, 0xff, 0x60, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x80, 0xff,
0xff, 0xff, 0xff, 0xff, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x50, 0xbf, 0xcf, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00,
0xaf, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcf, 0xff, 0xff, 0xff, 0x60, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x10, 0xdf, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xbf, 0xdf, 0x70, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x20, 0xaf, 0xcf, 0x8f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const lv_image_dsc_t decorator_shy = {
.header.cf = LV_COLOR_FORMAT_RGB565A8,
.header.magic = LV_IMAGE_HEADER_MAGIC,
.header.w = 39,
.header.h = 18,
.data_size = 702 * 3,
.data = decorator_shy_map,
};
@@ -4,7 +4,6 @@
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "../../utils/timer.h"
#include "../avatar/decorator.h"
#include <lvgl.h>
#include <smooth_lvgl.hpp>
@@ -13,36 +12,11 @@
namespace stackchan::avatar {
/**
* @brief Decorator with a timer
*
*/
class TimerDecorator : public Decorator {
public:
void scheduleDestroy(uint32_t ms)
{
_timer.addTask(ms, 1, 0, [this]() { requestDestroy(); });
}
Timer& getTimer()
{
return _timer;
}
virtual void _update() override
{
_timer.update();
}
private:
Timer _timer;
};
/**
* @brief
*
*/
class HeartDecorator : public TimerDecorator {
class HeartDecorator : public Decorator {
public:
/**
* @brief
@@ -54,14 +28,22 @@ public:
HeartDecorator(lv_obj_t* parent, uint32_t destroyAfterMs = 0, uint32_t animationIntervalMs = 500);
~HeartDecorator();
void _update() override;
using Element::setPosition;
void setPosition(int x, int y);
void setRotation(int rotation);
void setRotation(int rotation) override;
void setColor(lv_color_t color);
private:
std::unique_ptr<uitk::lvgl_cpp::Image> _heart;
uint32_t _destroy_at = 0;
uint32_t _next_animation_tick = 0;
uint32_t _animation_interval_ms = 0;
bool _has_lifetime = false;
int _animation_index = 0;
};
@@ -69,7 +51,7 @@ private:
* @brief
*
*/
class AngryDecorator : public TimerDecorator {
class AngryDecorator : public Decorator {
public:
/**
* @brief
@@ -81,14 +63,22 @@ public:
AngryDecorator(lv_obj_t* parent, uint32_t destroyAfterMs = 0, uint32_t animationIntervalMs = 500);
~AngryDecorator();
void _update() override;
using Element::setPosition;
void setPosition(int x, int y);
void setRotation(int rotation);
void setRotation(int rotation) override;
void setColor(lv_color_t color);
private:
std::unique_ptr<uitk::lvgl_cpp::Image> _angry;
uint32_t _destroy_at = 0;
uint32_t _next_animation_tick = 0;
uint32_t _animation_interval_ms = 0;
bool _has_lifetime = false;
int _animation_index = 0;
};
@@ -96,7 +86,7 @@ private:
* @brief
*
*/
class SweatDecorator : public TimerDecorator {
class SweatDecorator : public Decorator {
public:
/**
* @brief
@@ -108,6 +98,8 @@ public:
SweatDecorator(lv_obj_t* parent, uint32_t destroyAfterMs = 0, uint32_t animationIntervalMs = 700);
~SweatDecorator();
void _update() override;
using Element::setPosition;
void setPosition(int x, int y);
@@ -117,6 +109,81 @@ public:
private:
std::unique_ptr<uitk::lvgl_cpp::Image> _sweat;
uint32_t _destroy_at = 0;
uint32_t _next_animation_tick = 0;
uint32_t _animation_interval_ms = 0;
bool _has_lifetime = false;
int _animation_index = 0;
};
/**
* @brief
*
*/
class ShyDecorator : public Decorator {
public:
/**
* @brief
*
* @param parent
* @param destroyAfterMs Destroy after milliseconds, 0 for infinite
*/
ShyDecorator(lv_obj_t* parent, uint32_t destroyAfterMs = 0);
~ShyDecorator();
void _update() override;
using Element::setPosition;
void setPosition(int x, int y);
void setRotation(int rotation) override;
void setColor(lv_color_t color);
void setVisible(bool visible) override;
private:
std::unique_ptr<uitk::lvgl_cpp::Image> _left;
std::unique_ptr<uitk::lvgl_cpp::Image> _right;
uint32_t _destroy_at = 0;
bool _has_lifetime = false;
};
/**
* @brief
*
*/
class DizzyDecorator : public Decorator {
public:
/**
* @brief
*
* @param parent
* @param destroyAfterMs Destroy after milliseconds, 0 for infinite
* @param animationIntervalMs Animation update interval in milliseconds, 0 for none
*/
DizzyDecorator(lv_obj_t* parent, uint32_t destroyAfterMs = 0, uint32_t animationIntervalMs = 500);
~DizzyDecorator();
void _update() override;
using Element::setPosition;
void setPosition(int x, int y);
void setRotation(int rotation) override;
void setColor(lv_color_t color);
void setVisible(bool visible) override;
private:
std::unique_ptr<uitk::lvgl_cpp::Image> _left;
std::unique_ptr<uitk::lvgl_cpp::Image> _right;
uint32_t _destroy_at = 0;
uint32_t _next_animation_tick = 0;
uint32_t _animation_interval_ms = 0;
bool _has_lifetime = false;
int _animation_index = 0;
};
@@ -0,0 +1,127 @@
/*
* SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
#include "decorators.h"
#include <hal/hal.h>
#include <vector>
using namespace uitk;
using namespace uitk::lvgl_cpp;
using namespace stackchan::avatar;
static const Vector2i _dizzy_left_default_position = Vector2i(-70, -16);
static const Vector2i _dizzy_right_default_position = Vector2i(70, -16);
static const lv_color_t _dizzy_default_color = lv_color_hex(0xFFFFFF);
LV_IMAGE_DECLARE(decorator_dizzy);
DizzyDecorator::DizzyDecorator(lv_obj_t* parent, uint32_t destroyAfterMs, uint32_t animationIntervalMs)
: _animation_interval_ms(animationIntervalMs)
{
// Initialize Left Image
_left = std::make_unique<Image>(parent);
_left->setSrc(&decorator_dizzy);
_left->setAlign(LV_ALIGN_CENTER);
_left->setPos(_dizzy_left_default_position.x, _dizzy_left_default_position.y);
_left->setTransformPivot(_left->getWidth() / 2, _left->getHeight() / 2);
_left->setRotation(0);
_left->setImageRecolorOpa(LV_OPA_COVER);
_left->setImageRecolor(_dizzy_default_color);
// Initialize Right Image
_right = std::make_unique<Image>(parent);
_right->setSrc(&decorator_dizzy);
_right->setAlign(LV_ALIGN_CENTER);
_right->setPos(_dizzy_right_default_position.x, _dizzy_right_default_position.y);
_right->setTransformPivot(_right->getWidth() / 2, _right->getHeight() / 2);
_right->setRotation(450);
_right->setImageRecolorOpa(LV_OPA_COVER);
_right->setImageRecolor(_dizzy_default_color);
uint32_t now = GetHAL().millis();
if (destroyAfterMs > 0) {
_destroy_at = now + destroyAfterMs;
_has_lifetime = true;
}
if (_animation_interval_ms > 0) {
_next_animation_tick = now + _animation_interval_ms;
}
_animation_index = 0; // 用作当前旋转角度
}
DizzyDecorator::~DizzyDecorator()
{
}
void DizzyDecorator::_update()
{
uint32_t now = GetHAL().millis();
if (_has_lifetime && now >= _destroy_at) {
requestDestroy();
return;
}
if (_animation_interval_ms > 0 && now >= _next_animation_tick) {
_next_animation_tick = now + _animation_interval_ms;
// 自加30度 (300 unit)
_animation_index = (_animation_index + 300) % 3600;
int rotation = -_animation_index;
if (_left) {
_left->setRotation(rotation);
}
if (_right) {
_right->setRotation((rotation + 450) % 3600);
}
}
}
void DizzyDecorator::setPosition(int x, int y)
{
if (_left) {
_left->setPos(x + _dizzy_left_default_position.x, y + _dizzy_left_default_position.y);
}
if (_right) {
_right->setPos(x + _dizzy_right_default_position.x, y + _dizzy_right_default_position.y);
}
}
void DizzyDecorator::setRotation(int rotation)
{
// 这里要注意,手动设置的 rotation 可能会被 update 中的动画覆盖
// 但按照 HeartDecorator 的逻辑,setRotation 也是直接设置的
if (_left) {
_left->setRotation(rotation);
}
if (_right) {
_right->setRotation(rotation);
}
}
void DizzyDecorator::setColor(lv_color_t color)
{
if (_left) {
_left->setImageRecolor(color);
}
if (_right) {
_right->setImageRecolor(color);
}
}
void DizzyDecorator::setVisible(bool visible)
{
if (_left) {
_left->setHidden(!visible);
}
if (_right) {
_right->setHidden(!visible);
}
}
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: MIT
*/
#include "decorators.h"
#include <hal/hal.h>
#include <vector>
using namespace uitk;
@@ -17,47 +18,77 @@ static const std::vector<int> _heart_rotation_frames = {150, 200};
LV_IMAGE_DECLARE(decorator_heart);
HeartDecorator::HeartDecorator(lv_obj_t* parent, uint32_t destroyAfterMs, uint32_t animationIntervalMs)
: _animation_interval_ms(animationIntervalMs)
{
// 初始化图像
_heart = std::make_unique<Image>(parent);
_heart->setSrc(&decorator_heart);
_heart->setAlign(LV_ALIGN_CENTER);
_heart->setPos(_heart_default_position.x, _heart_default_position.y);
// 设置旋转中心为中心点
_heart->setTransformPivot(_heart->getWidth() / 2, _heart->getHeight() / 2);
_heart->setRotation(_heart_rotation_frames[1]);
_heart->setRotation(_heart_rotation_frames[0]);
// 设置颜色偏向
_heart->setImageRecolorOpa(LV_OPA_COVER);
_heart->setImageRecolor(_heart_default_color);
if (destroyAfterMs != 0) {
scheduleDestroy(destroyAfterMs);
uint32_t now = GetHAL().millis();
// 设置销毁计时
if (destroyAfterMs > 0) {
_destroy_at = now + destroyAfterMs;
_has_lifetime = true;
}
if (animationIntervalMs != 0) {
getTimer().addTask(animationIntervalMs, -1, 0, [this]() {
setRotation(_heart_rotation_frames[_animation_index]);
_animation_index++;
if (_animation_index >= _heart_rotation_frames.size()) {
_animation_index = 0;
}
});
// 设置动画计时
if (_animation_interval_ms > 0) {
_next_animation_tick = now + _animation_interval_ms;
}
}
HeartDecorator::~HeartDecorator()
{
_heart.reset();
}
void HeartDecorator::_update()
{
uint32_t now = GetHAL().millis();
// 1. 处理销毁
if (_has_lifetime && now >= _destroy_at) {
requestDestroy();
return;
}
// 2. 处理动画跳变(心跳效果)
if (_animation_interval_ms > 0 && now >= _next_animation_tick) {
_next_animation_tick = now + _animation_interval_ms;
// 切换旋转角度
_animation_index = (_animation_index + 1) % _heart_rotation_frames.size();
_heart->setRotation(_heart_rotation_frames[_animation_index]);
}
}
void HeartDecorator::setPosition(int x, int y)
{
_heart->setPos(x, y);
if (_heart) {
_heart->setPos(x, y);
}
}
void HeartDecorator::setRotation(int rotation)
{
_heart->setRotation(rotation);
if (_heart) {
_heart->setRotation(rotation);
}
}
void HeartDecorator::setColor(lv_color_t color)
{
_heart->setImageRecolor(color);
if (_heart) {
_heart->setImageRecolor(color);
}
}
@@ -0,0 +1,100 @@
/*
* SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
#include "decorators.h"
#include <hal/hal.h>
#include <vector>
using namespace uitk;
using namespace uitk::lvgl_cpp;
using namespace stackchan::avatar;
static const Vector2i _shy_left_default_position = Vector2i(-108, 28);
static const Vector2i _shy_right_default_position = Vector2i(108, 28);
static const lv_color_t _shy_default_color = lv_color_hex(0xF7A59E);
LV_IMAGE_DECLARE(decorator_shy);
ShyDecorator::ShyDecorator(lv_obj_t* parent, uint32_t destroyAfterMs)
{
// Initialize Left Image
_left = std::make_unique<Image>(parent);
_left->setSrc(&decorator_shy);
_left->setAlign(LV_ALIGN_CENTER);
_left->setPos(_shy_left_default_position.x, _shy_left_default_position.y);
_left->setTransformPivot(_left->getWidth() / 2, _left->getHeight() / 2);
_left->setImageRecolorOpa(LV_OPA_COVER);
_left->setImageRecolor(_shy_default_color);
// Initialize Right Image
_right = std::make_unique<Image>(parent);
_right->setSrc(&decorator_shy);
_right->setAlign(LV_ALIGN_CENTER);
_right->setPos(_shy_right_default_position.x, _shy_right_default_position.y);
_right->setTransformPivot(_right->getWidth() / 2, _right->getHeight() / 2);
_right->setImageRecolorOpa(LV_OPA_COVER);
_right->setImageRecolor(_shy_default_color);
uint32_t now = GetHAL().millis();
if (destroyAfterMs > 0) {
_destroy_at = now + destroyAfterMs;
_has_lifetime = true;
}
}
ShyDecorator::~ShyDecorator()
{
}
void ShyDecorator::_update()
{
uint32_t now = GetHAL().millis();
if (_has_lifetime && now >= _destroy_at) {
requestDestroy();
return;
}
}
void ShyDecorator::setPosition(int x, int y)
{
if (_left) {
_left->setPos(x + _shy_left_default_position.x, y + _shy_left_default_position.y);
}
if (_right) {
_right->setPos(x + _shy_right_default_position.x, y + _shy_right_default_position.y);
}
}
void ShyDecorator::setRotation(int rotation)
{
if (_left) {
_left->setRotation(rotation);
}
if (_right) {
_right->setRotation(rotation);
}
}
void ShyDecorator::setColor(lv_color_t color)
{
if (_left) {
_left->setImageRecolor(color);
}
if (_right) {
_right->setImageRecolor(color);
}
}
void ShyDecorator::setVisible(bool visible)
{
if (_left) {
_left->setHidden(!visible);
}
if (_right) {
_right->setHidden(!visible);
}
}
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: MIT
*/
#include "decorators.h"
#include <hal/hal.h>
#include <vector>
using namespace uitk;
@@ -17,59 +18,89 @@ static const std::vector<int> _sweat_pos_y_frames = {-72, -68, -62, -58, 0};
LV_IMAGE_DECLARE(decorator_sweat);
SweatDecorator::SweatDecorator(lv_obj_t* parent, uint32_t destroyAfterMs, uint32_t animationIntervalMs)
: _animation_interval_ms(animationIntervalMs)
{
// 初始化图像
_sweat = std::make_unique<Image>(parent);
_sweat->setSrc(&decorator_sweat);
_sweat->setAlign(LV_ALIGN_CENTER);
_sweat->setPos(_sweat_default_position.x, _sweat_default_position.y);
_sweat->setTransformPivot(_sweat->getWidth() / 2, _sweat->getHeight() / 2);
_sweat->setImageRecolorOpa(LV_OPA_COVER);
_sweat->setImageRecolor(_sweat_default_color);
if (destroyAfterMs != 0) {
scheduleDestroy(destroyAfterMs);
uint32_t now = GetHAL().millis();
if (destroyAfterMs > 0) {
_destroy_at = now + destroyAfterMs;
_has_lifetime = true;
}
if (animationIntervalMs != 0) {
getTimer().addTask(animationIntervalMs, -1, 0, [this]() {
if (_sweat_pos_y_frames[_animation_index] == 0) {
setVisible(false);
} else {
setVisible(true);
setPosition(_sweat_default_position.x, _sweat_pos_y_frames[_animation_index]);
}
_animation_index++;
if (_animation_index >= _sweat_pos_y_frames.size()) {
_animation_index = 0;
}
});
if (_animation_interval_ms > 0) {
_next_animation_tick = now + _animation_interval_ms;
}
}
SweatDecorator::~SweatDecorator()
{
_sweat.reset();
}
void SweatDecorator::_update()
{
uint32_t now = GetHAL().millis();
// 检查自动销毁
if (_has_lifetime && now >= _destroy_at) {
requestDestroy();
return;
}
// 检查动画帧更新
if (_animation_interval_ms > 0 && now >= _next_animation_tick) {
_next_animation_tick = now + _animation_interval_ms;
int current_y_frame = _sweat_pos_y_frames[_animation_index];
if (current_y_frame == 0) {
// 特殊帧:隐藏图像
setVisible(false);
} else {
// 普通帧:移动位置并显示
setVisible(true);
_sweat->setPos(_sweat_default_position.x, current_y_frame);
}
// 步进索引
_animation_index = (_animation_index + 1) % _sweat_pos_y_frames.size();
}
}
void SweatDecorator::setPosition(int x, int y)
{
Element::setPosition({x, y});
_sweat->setPos(x, y);
// 注意:这里的 setPosition 会覆盖动画中的 x 坐标
if (_sweat) {
_sweat->setPos(x, y);
}
}
void SweatDecorator::setRotation(int rotation)
{
Element::setRotation(rotation);
_sweat->setRotation(rotation);
if (_sweat) {
_sweat->setRotation(rotation);
}
}
void SweatDecorator::setColor(lv_color_t color)
{
_sweat->setImageRecolor(color);
if (_sweat) {
_sweat->setImageRecolor(color);
}
}
void SweatDecorator::setVisible(bool visible)
{
Element::setVisible(visible);
_sweat->setHidden(!visible);
if (_sweat) {
_sweat->setHidden(!visible);
}
}
@@ -157,6 +157,9 @@ static FeatureKeyframe parse_feature(ArduinoJson::JsonObject jsonObject)
if (jsonObject["weight"].is<int>()) {
kf.weight = jsonObject["weight"];
}
if (jsonObject["size"].is<int>()) {
kf.size = jsonObject["size"];
}
return kf;
}
@@ -210,6 +213,12 @@ KeyframeSequence parse_sequence_from_json(const char* jsonContent)
if (kfObj["pitchServo"].is<ArduinoJson::JsonObject>()) {
kf.pitchServo = parse_servo(kfObj["pitchServo"]);
}
if (kfObj["leftRgbColor"].is<const char*>()) {
kf.leftRgbColor = kfObj["leftRgbColor"].as<std::string>();
}
if (kfObj["rightRgbColor"].is<const char*>()) {
kf.rightRgbColor = kfObj["rightRgbColor"].as<std::string>();
}
if (kfObj["durationMs"].is<int>()) {
kf.durationMs = kfObj["durationMs"];
}
@@ -221,3 +230,35 @@ KeyframeSequence parse_sequence_from_json(const char* jsonContent)
}
} // namespace stackchan::animation
namespace stackchan::addon {
void update_neon_light_from_json(NeonLight* left, NeonLight* right, const char* jsonContent)
{
if (!left || !right || !jsonContent) {
return;
}
ArduinoJson::JsonDocument doc;
auto error = ArduinoJson::deserializeJson(doc, jsonContent);
if (error) {
mclog::tagError(_tag, "deserializeJson failed: {}", error.c_str());
return;
}
if (doc["leftRgbDuration"].is<float>()) {
left->setDuration(doc["leftRgbDuration"].as<float>());
}
if (doc["leftRgbColor"].is<std::string>()) {
left->setColor(doc["leftRgbColor"].as<std::string>());
}
if (doc["rightRgbDuration"].is<float>()) {
right->setDuration(doc["rightRgbDuration"].as<float>());
}
if (doc["rightRgbColor"].is<std::string>()) {
right->setColor(doc["rightRgbColor"].as<std::string>());
}
}
} // namespace stackchan::addon
@@ -7,6 +7,7 @@
#include "../avatar/avatar.h"
#include "../motion/motion.h"
#include "../animation/animation.h"
#include "../addons/neon_light/neon_light.h"
namespace stackchan {
@@ -22,4 +23,8 @@ namespace animation {
KeyframeSequence parse_sequence_from_json(const char* jsonContent);
}
namespace addon {
void update_neon_light_from_json(NeonLight* left, NeonLight* right, const char* jsonContent);
}
} // namespace stackchan
+7
View File
@@ -6,6 +6,7 @@
#pragma once
#include "avatar/avatar.h"
#include "motion/motion.h"
#include "addons/neon_light/neon_light.h"
#include "utils/object_pool.h"
namespace stackchan {
@@ -23,6 +24,12 @@ public:
virtual avatar::Avatar& avatar() = 0;
virtual bool hasAvatar() = 0;
// virtual bool hasMotion() = 0; // Motion must be always present
virtual addon::NeonLight& leftNeonLight() = 0;
virtual addon::NeonLight& rightNeonLight() = 0;
};
/**
+65 -28
View File
@@ -4,7 +4,9 @@
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "timed.h"
#include "../modifiable.h"
#include "../utils/random.h"
#include <hal/hal.h>
#include <cstdint>
namespace stackchan {
@@ -13,16 +15,27 @@ namespace stackchan {
* @brief
*
*/
class BlinkModifier : public TimerModifier {
class BlinkModifier : public Modifier {
public:
/**
* @param destroyAfterMs 0
* @param openIntervalMs
* @param closeIntervalMs
*/
BlinkModifier(uint32_t destroyAfterMs = 0, uint32_t openIntervalMs = 5200, uint32_t closeIntervalMs = 200)
: _open_interval_ms(openIntervalMs), _close_interval_ms(closeIntervalMs)
{
if (destroyAfterMs != 0) {
scheduleDestroy(destroyAfterMs);
uint32_t now = GetHAL().millis();
// 处理销毁计时
if (destroyAfterMs > 0) {
_destroy_at = now + destroyAfterMs;
_has_lifetime = true;
}
_should_close = true;
// 初始化:从睁眼状态开始,立即准备闭眼
_state = State::OPEN;
_next_state_tick = now + _open_interval_ms;
}
void resyncEyeWeights()
@@ -32,49 +45,73 @@ public:
void _update(Modifiable& stackchan) override
{
TimerModifier::_update(stackchan);
if (!stackchan.hasAvatar()) {
if (!stackchan.hasAvatar() || stackchan.avatar().isModifyLocked()) {
return;
}
uint32_t now = GetHAL().millis();
// 1. 处理销毁逻辑
if (_has_lifetime && now >= _destroy_at) {
// 销毁前确保眼睛是睁开的
if (_state == State::CLOSED) {
apply_eye_weights(stackchan, _left_eye_weight, _right_eye_weight);
}
requestDestroy();
return;
}
// 2. 处理权重同步请求
// 如果眼睛正闭着,我们只记录权重,等睁眼时再应用
if (_needs_resync) {
_needs_resync = false;
_left_eye_weight = stackchan.avatar().leftEye().getWeight();
_right_eye_weight = stackchan.avatar().rightEye().getWeight();
}
if (_should_close) {
_should_close = false;
// 3. 状态机切换逻辑
if (now >= _next_state_tick) {
if (_state == State::OPEN) {
// 睁眼 -> 闭眼
_state = State::CLOSED;
_next_state_tick = now + _close_interval_ms;
_left_eye_weight = stackchan.avatar().leftEye().getWeight();
_right_eye_weight = stackchan.avatar().rightEye().getWeight();
// 闭眼瞬间,先备份当前权重(以防外部中途修改了权重)
_left_eye_weight = stackchan.avatar().leftEye().getWeight();
_right_eye_weight = stackchan.avatar().rightEye().getWeight();
stackchan.avatar().leftEye().setWeight(25);
stackchan.avatar().rightEye().setWeight(25);
apply_eye_weights(stackchan, 25, 25);
} else {
// 闭眼 -> 睁眼
_state = State::OPEN;
// 睁眼时间可以加一点随机抖动,看起来更自然
uint32_t jitter = Random::getInstance().getInt(0, 500);
_next_state_tick = now + _open_interval_ms + jitter;
getTimer().addTask(_close_interval_ms, 1, 0, [this]() { _should_open = true; });
}
if (_should_open) {
_should_open = false;
stackchan.avatar().leftEye().setWeight(_left_eye_weight);
stackchan.avatar().rightEye().setWeight(_right_eye_weight);
getTimer().addTask(_open_interval_ms, 1, 0, [this]() { _should_close = true; });
apply_eye_weights(stackchan, _left_eye_weight, _right_eye_weight);
}
}
}
private:
enum class State { OPEN, CLOSED };
void apply_eye_weights(Modifiable& stackchan, int left, int right)
{
stackchan.avatar().leftEye().setWeight(left);
stackchan.avatar().rightEye().setWeight(right);
}
State _state;
uint32_t _next_state_tick = 0;
uint32_t _open_interval_ms;
uint32_t _close_interval_ms;
bool _should_close = false;
bool _should_open = false;
uint32_t _destroy_at = 0;
bool _has_lifetime = false;
bool _needs_resync = false;
int _left_eye_weight = 0;
int _right_eye_weight = 0;
int _left_eye_weight = 100;
int _right_eye_weight = 100;
};
} // namespace stackchan
+68 -57
View File
@@ -4,8 +4,10 @@
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "timed.h"
#include "../modifiable.h"
#include <hal/hal.h>
#include <cstdint>
#include <cmath>
namespace stackchan {
@@ -13,83 +15,92 @@ namespace stackchan {
* @brief
*
*/
class BreathModifier : public TimerModifier {
class BreathModifier : public Modifier {
public:
BreathModifier(uint32_t destroyAfterMs = 0, uint32_t updateIntervalMs = 600)
/**
* @param destroyAfterMs 0
* @param amplitude
* @param breathCycleMs +
* @param updateIntervalMs
*/
BreathModifier(uint32_t destroyAfterMs = 0, int amplitude = 16, uint32_t breathCycleMs = 6600,
uint32_t updateIntervalMs = 600)
: _amplitude(amplitude), _breath_cycle_ms(breathCycleMs), _update_interval_ms(updateIntervalMs)
{
if (destroyAfterMs != 0) {
scheduleDestroy(destroyAfterMs);
_start_tick = GetHAL().millis();
if (destroyAfterMs > 0) {
_destroy_at = _start_tick + destroyAfterMs;
_has_lifetime = true;
}
if (updateIntervalMs == 0) {
requestDestroy();
}
getTimer().addTask(updateIntervalMs, -1, 0, [this]() { _update_flag = true; });
}
void _update(Modifiable& stackchan) override
{
TimerModifier::_update(stackchan);
if (!stackchan.hasAvatar()) return;
if (!stackchan.hasAvatar()) {
uint32_t now = GetHAL().millis();
// 销毁逻辑
if (_has_lifetime && now >= _destroy_at) {
reset_position(stackchan.avatar()); // 销毁前复位
requestDestroy();
return;
}
if (!_update_flag) {
if (now - _last_update_tick < _update_interval_ms) {
return;
}
_update_flag = false;
_last_update_tick = now;
// Check state switch
_breathe_count++;
if (_is_breathing_in) {
if (_breathe_count >= _breathe_in_count) {
_is_breathing_in = false;
_breathe_count = 0;
}
} else {
if (_breathe_count >= _breathe_out_count) {
_is_breathing_in = true;
_breathe_count = 0;
}
}
// 使用正弦波计算偏移量
// (now - _start_tick) / cycle 得到进度,乘以 2PI 传给 sin
float phase = (float)((now - _start_tick) % _breath_cycle_ms) / _breath_cycle_ms;
float sin_val = sinf(phase * 2.0f * M_PI);
// Update breath
auto& avatar = stackchan.avatar();
_left_eye_position = avatar.leftEye().getPosition();
_right_eye_position = avatar.rightEye().getPosition();
_mouth_position = avatar.mouth().getPosition();
// 计算当前偏移
int current_offset = static_cast<int>(sin_val * _amplitude);
if (_is_breathing_in) {
_left_eye_position.y += _breathe_in_offset;
_right_eye_position.y += _breathe_in_offset;
_mouth_position.y += _breathe_in_offset;
} else {
_left_eye_position.y += _breathe_out_offset;
_right_eye_position.y += _breathe_out_offset;
_mouth_position.y += _breathe_out_offset;
}
avatar.leftEye().setPosition(_left_eye_position);
avatar.rightEye().setPosition(_right_eye_position);
avatar.mouth().setPosition(_mouth_position);
// 应用增量偏移
apply_relative_offset(stackchan.avatar(), current_offset);
}
private:
// Keep in and out total offset added up to 0
const int _breathe_in_count = 7;
const int _breathe_in_offset = -4;
const int _breathe_out_count = 7;
const int _breathe_out_offset = 4;
void apply_relative_offset(avatar::Avatar& avatar, int new_offset)
{
// 计算本次需要移动的差值
int delta = new_offset - _last_applied_offset;
if (delta == 0) return;
bool _update_flag = false;
bool _is_breathing_in = true;
int _breathe_count = 0;
// 批量移动五官
move_component(avatar.leftEye(), delta);
move_component(avatar.rightEye(), delta);
move_component(avatar.mouth(), delta);
uitk::Vector2i _left_eye_position;
uitk::Vector2i _right_eye_position;
uitk::Vector2i _mouth_position;
_last_applied_offset = new_offset;
}
void move_component(avatar::Feature& comp, int delta_y)
{
auto pos = comp.getPosition();
pos.y += delta_y;
comp.setPosition(pos);
}
void reset_position(avatar::Avatar& avatar)
{
// 将偏移归零
apply_relative_offset(avatar, 0);
}
int _amplitude;
uint32_t _breath_cycle_ms;
uint32_t _update_interval_ms;
uint32_t _start_tick = 0;
uint32_t _last_update_tick = 0;
uint32_t _destroy_at = 0;
bool _has_lifetime = false;
int _last_applied_offset = 0; // 记录上一次应用的偏移量,用于增量更新
};
} // namespace stackchan
+93 -76
View File
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "timed.h"
#include "../modifiable.h"
#include "../avatar/decorators/decorators.h"
#include "../utils/random.h"
#include <smooth_ui_toolkit.hpp>
@@ -14,22 +14,20 @@
namespace stackchan {
class HeadPetModifier : public TimerModifier {
/**
* @brief
*
*/
class HeadPetModifier : public Modifier {
public:
HeadPetModifier(uint32_t restoreDelayMs = 3000) : _restore_delay_ms(restoreDelayMs)
{
// 绑定信号
_signal_connection = GetHAL().onHeadPetGesture.connect([this](HeadPetGesture gesture) {
LvglLockGuard lock;
if (gesture == HeadPetGesture::SwipeForward || gesture == HeadPetGesture::SwipeBackward) {
_trigger_happy = true;
_trigger_release = false;
// 每次抚摸都打断恢复倒计时
_should_restore = false;
_restore_timer_active = false;
_event_swipe = true;
} else if (gesture == HeadPetGesture::Release) {
_trigger_release = true;
_event_release = true;
}
});
}
@@ -41,105 +39,124 @@ public:
void _update(Modifiable& stackchan) override
{
TimerModifier::_update(stackchan);
uint32_t now = GetHAL().millis();
if (_trigger_happy) {
_trigger_happy = false;
// 1. 首次进入记录当前位置
if (!_in_happy_state) {
_prev_emotion = stackchan.avatar().getEmotion();
_prev_pitch = stackchan.motion().getCurrentPitchAngle();
_prev_yaw = stackchan.motion().getCurrentYawAngle();
_in_happy_state = true;
}
// 2. 表情反馈
stackchan.avatar().setEmotion(avatar::Emotion::Happy);
int duration = Random::getInstance().getInt(1500, 2500);
stackchan.avatar().addDecorator(
std::make_unique<avatar::HeartDecorator>(lv_screen_active(), duration, 500));
// 3. 执行随机动作
perform_random_motion(stackchan);
// 处理“被抚摸中”事件
if (_event_swipe) {
_event_swipe = false;
handle_swipe(stackchan);
// 只要在摸,就推迟恢复时间
_is_waiting_restore = false;
}
if (_trigger_release) {
_trigger_release = false;
// 处理“手松开”事件
if (_event_release) {
_event_release = false;
if (_in_happy_state) {
// 启动恢复计时
_restore_timer_active = true;
getTimer().addTask(_restore_delay_ms, 1, 0, [this]() { _should_restore = true; });
_is_waiting_restore = true;
_restore_tick = now + _restore_delay_ms;
}
}
if (_should_restore) {
_should_restore = false;
if (_restore_timer_active && _in_happy_state) {
// 恢复原状
stackchan.avatar().setEmotion(_prev_emotion);
auto& motion = stackchan.motion();
motion.moveWithSpeed(_prev_yaw, _prev_pitch, 200);
_in_happy_state = false;
_restore_timer_active = false;
}
// 处理恢复逻辑
if (_is_waiting_restore && now >= _restore_tick) {
_is_waiting_restore = false;
restore_original_state(stackchan);
}
}
private:
void perform_random_motion(Modifiable& stackchan)
void handle_swipe(Modifiable& stackchan)
{
auto& avatar = stackchan.avatar();
// 首次进入开心状态,记录原始信息
if (!_in_happy_state) {
_in_happy_state = true;
_prev_emotion = avatar.getEmotion();
auto angles = stackchan.motion().getCurrentAngles();
_prev_yaw = angles.x;
_prev_pitch = angles.y;
}
// 视觉反馈
avatar.setEmotion(avatar::Emotion::Happy);
// 添加爱心装饰
int duration = Random::getInstance().getInt(1500, 2500);
avatar.removeDecorator(_heart_decorator_id);
avatar.removeDecorator(_shy_decorator_id);
_heart_decorator_id =
avatar.addDecorator(std::make_unique<avatar::HeartDecorator>(lv_screen_active(), duration, 500));
_shy_decorator_id = avatar.addDecorator(std::make_unique<avatar::ShyDecorator>(lv_screen_active(), duration));
// 动作反馈
perform_pet_motion(stackchan);
}
void restore_original_state(Modifiable& stackchan)
{
if (!_in_happy_state) {
return;
}
stackchan.avatar().setEmotion(_prev_emotion);
stackchan.motion().moveWithSpeed(_prev_yaw, _prev_pitch, 200);
_in_happy_state = false;
}
void perform_pet_motion(Modifiable& stackchan)
{
int action = Random::getInstance().getInt(0, 2);
auto& motion = stackchan.motion();
if (motion.isModifyLocked() || motion.isMoving()) {
return;
}
int speed = Random::getInstance().getInt(400, 800);
int action = Random::getInstance().getInt(0, 2);
int speed = Random::getInstance().getInt(300, 500);
int32_t target_pitch = _prev_pitch;
int32_t target_yaw = _prev_yaw;
int32_t target_pitch = _prev_pitch;
// 角度限制: 10 units = 1度
switch (action) {
case 0: // 舒服地蹭手/抬头
case 0: // 抬头
target_pitch += Random::getInstance().getInt(150, 250);
target_yaw += Random::getInstance().getInt(-50, 50); // 微调 Yaw
target_yaw += Random::getInstance().getInt(-50, 50);
break;
case 1: // 开心歪头/摇晃
target_pitch -= Random::getInstance().getInt(0, 50); // 略微低头
if (Random::getInstance().getInt(0, 1) == 0) {
target_yaw += Random::getInstance().getInt(100, 200);
} else {
target_yaw -= Random::getInstance().getInt(100, 200);
}
case 1: // 歪头
target_pitch -= Random::getInstance().getInt(0, 50);
target_yaw += (Random::getInstance().getInt(0, 1) == 0 ? 150 : -150);
break;
case 2: // 兴奋大幅度抬头
case 2: // 大幅度开心
target_pitch += Random::getInstance().getInt(250, 400);
break;
}
target_pitch = uitk::clamp(target_pitch, 0, 900);
target_yaw = uitk::clamp(target_yaw, -1280, 1280);
// 自然范围限制
target_pitch = uitk::clamp(target_pitch, 0, 540);
target_yaw = uitk::clamp(target_yaw, -512, 512);
motion.moveWithSpeed(target_yaw, target_pitch, speed);
}
// 信号相关
int _signal_connection;
volatile bool _event_swipe = false;
volatile bool _event_release = false;
// 状态机相关
bool _in_happy_state = false;
bool _is_waiting_restore = false;
uint32_t _restore_tick = 0;
uint32_t _restore_delay_ms;
int _heart_decorator_id = -1;
int _shy_decorator_id = -1;
bool _trigger_happy = false;
bool _trigger_release = false;
bool _in_happy_state = false;
bool _restore_timer_active = false;
bool _should_restore = false;
// 记忆相关
avatar::Emotion _prev_emotion = avatar::Emotion::Neutral;
int32_t _prev_pitch = 0;
int32_t _prev_yaw = 0;
int32_t _prev_pitch = 0;
};
} // namespace stackchan

Some files were not shown because too many files have changed in this diff Show More