Public Access
Add a PC renderer for the themed pages; fix overlapping layouts
tools/render_pages.py compiles the page code natively with g++ against ESPHome's own display and font code and the font bitmaps of the last `esphome compile`, and draws pages to PNG at chosen moments with sample data (normal, empty, alert, busy), as a contact sheet, or as one example per page in docs/images/pages (now shown in the README). Fixes found with it: Cyber HUD zone names ran into the temperatures, the breach header label hit the tag and the column marker the matrix label; KonoSuba subtitle, guild weather icon, cabbage text and all-day deadline, and hills through the party; Steins;Gate D-mail recipient, the microwave display, the gadget card value and world line labels; LCARS room rows, readout bars and data cascade, warp core readouts, and the captain's log weather wording. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
13a499fd97
commit
d2e81a5a36
27 files changed
+627
-38
No files matched your search
@@ -0,0 +1,104 @@
|
||||
// Host stand-in for components/el512/el512_display.h, used by
|
||||
// tools/render_pages.py to draw the themed pages on a PC: the same drawing
|
||||
// surface (ESPHome's Display, 512 x 256, 1 bpp) and the same buffer effects,
|
||||
// without the panel, DMA or ESPHome components.
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#include "cyberpunk.h"
|
||||
#include "el512_helpers.h"
|
||||
#include "esphome/components/display/display.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "konosuba.h"
|
||||
#include "steins.h"
|
||||
#include "trek.h"
|
||||
|
||||
namespace esphome::el512 {
|
||||
|
||||
class EL512Display : public display::Display {
|
||||
public:
|
||||
static constexpr int WIDTH = 512, HEIGHT = 256, ROW = WIDTH / 8;
|
||||
|
||||
uint8_t fb[ROW * HEIGHT]{}; // row-major, MSB = leftmost pixel (also PBM P4 order)
|
||||
uint32_t shown_at{0}; // millis() when the page was "shown"
|
||||
|
||||
void update() override {}
|
||||
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_BINARY; }
|
||||
|
||||
void fill(Color color) override {
|
||||
if (this->get_clipping().is_set()) {
|
||||
Display::fill(color);
|
||||
return;
|
||||
}
|
||||
memset(this->fb, color.is_on() ? 0xFF : 0x00, sizeof(this->fb));
|
||||
}
|
||||
void draw_pixel_at(int x, int y, Color color) override {
|
||||
if (!this->get_clipping().inside(x, y))
|
||||
return;
|
||||
this->set(x, y, color.is_on());
|
||||
}
|
||||
|
||||
uint32_t page_age_ms() { return millis() - this->shown_at; }
|
||||
uint32_t page_shown_at() const { return this->shown_at; }
|
||||
|
||||
// Same effects as the device (el512_display.cpp).
|
||||
void shift_rows(int y, int height, int dx) {
|
||||
dx %= WIDTH;
|
||||
if (dx < 0)
|
||||
dx += WIDTH;
|
||||
if (dx == 0)
|
||||
return;
|
||||
uint8_t src[ROW];
|
||||
for (int r = std::max(y, 0); r < std::min(y + height, HEIGHT); r++) {
|
||||
uint8_t *row = this->fb + r * ROW;
|
||||
memcpy(src, row, ROW);
|
||||
memset(row, 0, ROW);
|
||||
for (int x = 0; x < WIDTH; x++) {
|
||||
const int s = (x - dx + WIDTH) % WIDTH;
|
||||
if (src[s >> 3] & (0x80 >> (s & 7)))
|
||||
row[x >> 3] |= 0x80 >> (x & 7);
|
||||
}
|
||||
}
|
||||
}
|
||||
void invert_rect(int x, int y, int width, int height) {
|
||||
for (int py = y; py < y + height; py++)
|
||||
for (int px = x; px < x + width; px++)
|
||||
if (px >= 0 && py >= 0 && px < WIDTH && py < HEIGHT)
|
||||
this->set(px, py, !this->get(px, py));
|
||||
}
|
||||
void dither_mask(int level) {
|
||||
if (level >= 16)
|
||||
return;
|
||||
static const uint8_t BAYER[4][4] = {{0, 8, 2, 10}, {12, 4, 14, 6}, {3, 11, 1, 9}, {15, 7, 13, 5}};
|
||||
uint8_t masks[4];
|
||||
for (int r = 0; r < 4; r++) {
|
||||
masks[r] = 0;
|
||||
for (int x = 0; x < 8; x++)
|
||||
if (BAYER[r][x & 3] < level)
|
||||
masks[r] |= 0x80 >> x;
|
||||
}
|
||||
for (int y = 0; y < HEIGHT; y++)
|
||||
for (int i = 0; i < ROW; i++)
|
||||
this->fb[y * ROW + i] &= masks[y & 3];
|
||||
}
|
||||
|
||||
bool get(int x, int y) const { return this->fb[y * ROW + (x >> 3)] & (0x80 >> (x & 7)); }
|
||||
void set(int x, int y, bool on) {
|
||||
if (x < 0 || y < 0 || x >= WIDTH || y >= HEIGHT)
|
||||
return;
|
||||
uint8_t &b = this->fb[y * ROW + (x >> 3)];
|
||||
if (on)
|
||||
b |= 0x80 >> (x & 7);
|
||||
else
|
||||
b &= ~(0x80 >> (x & 7));
|
||||
}
|
||||
|
||||
protected:
|
||||
int get_width_internal() override { return WIDTH; }
|
||||
int get_height_internal() override { return HEIGHT; }
|
||||
};
|
||||
|
||||
} // namespace esphome::el512
|
||||
Reference in new issue
Block a user