Files
ESP32_Serial_Swiss_Army_Knife/src/local_boot_animation.c
T

175 lines
4.3 KiB
C

/* SPDX-License-Identifier: GPL-3.0-only */
/* Compact monochrome derivative of images/logo.png for the 128x48 content panel. */
#include "local_boot_animation.h"
#include <stdbool.h>
#include <stdint.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "local_display.h"
#define BOOT_ANIMATION_FRAMES 20U
#define BOOT_ANIMATION_FRAME_MS 250U
#define BOOT_ANIMATION_NAME_SPEED 16U
static const char s_device_name[] = "ESP32 SERIAL SWISS ARMY KNIFE";
static void pixel(int x, int y)
{
if (x >= 0 && x < (int)LOCAL_DISPLAY_WIDTH &&
y >= 0 && y < (int)LOCAL_DISPLAY_CONTENT_HEIGHT) {
local_display_frame_set_pixel(LOCAL_DISPLAY_PANEL_CONTENT, (uint8_t)x,
(uint8_t)y, true);
}
}
static void line(int x0, int y0, int x1, int y1)
{
int dx = x1 >= x0 ? x1 - x0 : x0 - x1;
int sx = x0 < x1 ? 1 : -1;
int dy = y1 >= y0 ? y0 - y1 : y1 - y0;
int sy = y0 < y1 ? 1 : -1;
int error = dx + dy;
for (;;) {
pixel(x0, y0);
if (x0 == x1 && y0 == y1) {
return;
}
int twice_error = 2 * error;
if (twice_error >= dy) {
error += dy;
x0 += sx;
}
if (twice_error <= dx) {
error += dx;
y0 += sy;
}
}
}
static void rectangle(int x, int y, int width, int height)
{
line(x, y, x + width - 1, y);
line(x, y, x, y + height - 1);
line(x + width - 1, y, x + width - 1, y + height - 1);
line(x, y + height - 1, x + width - 1, y + height - 1);
}
static void draw_serial_connector(void)
{
rectangle(2, 8, 16, 10);
rectangle(4, 10, 12, 6);
for (int column = 0; column < 5; ++column) {
pixel(6 + column * 2, 12);
pixel(6 + column * 2, 14);
}
line(18, 13, 30, 22);
line(18, 16, 28, 25);
}
static void draw_usb_connector(void)
{
rectangle(2, 31, 14, 11);
rectangle(4, 33, 10, 7);
line(16, 35, 29, 30);
line(16, 39, 30, 34);
pixel(7, 35);
pixel(10, 38);
}
static void draw_terminal(bool cursor_on)
{
rectangle(28, 21, 42, 22);
rectangle(30, 23, 38, 18);
/* Keep the prompt upright even though the source logo is reoriented. */
line(38, 28, 45, 33);
line(45, 33, 38, 38);
if (cursor_on) {
line(51, 37, 59, 37);
}
line(31, 27, 31, 39);
line(67, 27, 67, 39);
line(34, 22, 34, 20);
line(63, 22, 63, 20);
}
static void draw_board(void)
{
rectangle(72, 7, 28, 36);
rectangle(78, 14, 16, 17);
for (int row = 0; row < 5; ++row) {
pixel(74, 11 + row * 6);
pixel(76, 11 + row * 6);
pixel(96, 11 + row * 6);
pixel(98, 11 + row * 6);
}
for (int column = 0; column < 6; ++column) {
pixel(79 + column * 3, 35);
pixel(79 + column * 3, 38);
}
line(70, 28, 72, 28);
line(70, 34, 72, 34);
}
static void draw_wifi(uint8_t frame)
{
bool outer = (frame % 2U) == 0U;
if (outer) {
line(103, 8, 109, 3);
line(109, 3, 115, 8);
}
line(105, 12, 109, 8);
line(109, 8, 113, 12);
line(107, 16, 109, 14);
line(109, 14, 111, 16);
pixel(109, 19);
}
static void draw_logo(uint8_t frame)
{
draw_serial_connector();
draw_usb_connector();
draw_terminal((frame % 2U) == 0U);
draw_board();
draw_wifi(frame);
}
static void draw_scrolling_name(uint8_t frame)
{
int text_width = ((int)sizeof(s_device_name) - 1) * 6;
int x = (int)LOCAL_DISPLAY_WIDTH - (int)frame * BOOT_ANIMATION_NAME_SPEED;
if (x < -text_width) {
x += text_width + (int)LOCAL_DISPLAY_WIDTH;
}
const char *text = s_device_name;
while (x < 0 && *text != '\0') {
x += 6;
++text;
}
if (x < (int)LOCAL_DISPLAY_WIDTH && *text != '\0') {
local_display_frame_draw_text(LOCAL_DISPLAY_PANEL_STATUS, (uint8_t)x, 4U, text);
}
}
esp_err_t local_boot_animation_play(void)
{
for (uint8_t frame = 0U; frame < BOOT_ANIMATION_FRAMES; ++frame) {
esp_err_t error = local_display_frame_begin();
if (error != ESP_OK) {
return error;
}
local_display_frame_clear_all();
draw_scrolling_name(frame);
draw_logo(frame);
error = local_display_frame_end();
if (error != ESP_OK) {
return error;
}
vTaskDelay(pdMS_TO_TICKS(BOOT_ANIMATION_FRAME_MS));
}
return ESP_OK;
}