// 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 #include #include #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