175 lines
4.3 KiB
C
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;
|
|
}
|