Pressing the manage button (GPIO1) overlays a small QR code -- "SCAN TO MANAGE" -- in the top-right corner of whatever photo is currently on screen, linking to the server's config page, then reverts to the plain photo after 30 seconds. The overlay is spliced into the existing streaming fetch as chunks pass through (frame_client.c's http_read_fn), rather than buffering the full 192,000-byte frame in RAM: only the small overlay rectangle itself (~30KB) is ever held in memory, generated via new stride-parameterized drawing helpers (epd_draw_*_ex in epd_draw.c) that let the existing QR/text drawing code target an arbitrarily-sized buffer instead of a full-frame one. epd7in3e.c is untouched -- it has no idea an overlay exists.
106 lines
3.7 KiB
C
106 lines
3.7 KiB
C
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "epd_draw.h"
|
|
|
|
void epd_draw_pixel_ex(uint8_t *buf, int stride, int width, int height, int x, int y, epd_color_t color)
|
|
{
|
|
if (x < 0 || x >= width || y < 0 || y >= height) {
|
|
return;
|
|
}
|
|
size_t byte_index = (size_t)y * stride + (size_t)x / 2;
|
|
uint8_t nibble = (uint8_t)color & 0x0F;
|
|
if (x % 2 == 0) {
|
|
buf[byte_index] = (buf[byte_index] & 0x0F) | (nibble << 4);
|
|
} else {
|
|
buf[byte_index] = (buf[byte_index] & 0xF0) | nibble;
|
|
}
|
|
}
|
|
|
|
void epd_draw_pixel(uint8_t *frame, int x, int y, epd_color_t color)
|
|
{
|
|
epd_draw_pixel_ex(frame, EPD_BYTES_PER_ROW, EPD_WIDTH, EPD_HEIGHT, x, y, color);
|
|
}
|
|
|
|
/* fonts.h's sFONT tables cover the printable ASCII range starting at ' '
|
|
* (0x20), one glyph per character, MSB-first within each row (see
|
|
* components/epaper_fonts). */
|
|
static bool font_pixel_set(const sFONT *font, char c, int row, int col)
|
|
{
|
|
if (c < ' ' || (unsigned char)c > 0x7E) {
|
|
return false;
|
|
}
|
|
int bytes_per_row = (font->Width + 7) / 8;
|
|
size_t glyph_index = (size_t)(c - ' ');
|
|
const uint8_t *glyph = font->table + glyph_index * font->Height * bytes_per_row + (size_t)row * bytes_per_row;
|
|
int byte_idx = col / 8;
|
|
int bit_in_byte = 7 - (col % 8);
|
|
return (glyph[byte_idx] >> bit_in_byte) & 0x1;
|
|
}
|
|
|
|
void epd_draw_text_ex(uint8_t *buf, int stride, int width, int height, const sFONT *font, const char *text,
|
|
int origin_x, int origin_y)
|
|
{
|
|
int cursor_x = origin_x;
|
|
for (const char *p = text; *p != '\0'; p++) {
|
|
char c = (char)toupper((unsigned char)*p);
|
|
for (int row = 0; row < font->Height; row++) {
|
|
for (int col = 0; col < font->Width; col++) {
|
|
if (font_pixel_set(font, c, row, col)) {
|
|
epd_draw_pixel_ex(buf, stride, width, height, cursor_x + col, origin_y + row, EPD_COLOR_BLACK);
|
|
}
|
|
}
|
|
}
|
|
cursor_x += font->Width;
|
|
}
|
|
}
|
|
|
|
void epd_draw_text(uint8_t *frame, const sFONT *font, const char *text, int origin_x, int origin_y)
|
|
{
|
|
epd_draw_text_ex(frame, EPD_BYTES_PER_ROW, EPD_WIDTH, EPD_HEIGHT, font, text, origin_x, origin_y);
|
|
}
|
|
|
|
void epd_draw_text_centered_ex(uint8_t *buf, int stride, int width, int height, const sFONT *font, const char *text,
|
|
int center_x, int y)
|
|
{
|
|
int text_width = (int)strlen(text) * font->Width;
|
|
epd_draw_text_ex(buf, stride, width, height, font, text, center_x - text_width / 2, y);
|
|
}
|
|
|
|
void epd_draw_text_centered(uint8_t *frame, const sFONT *font, const char *text, int center_x, int y)
|
|
{
|
|
epd_draw_text_centered_ex(frame, EPD_BYTES_PER_ROW, EPD_WIDTH, EPD_HEIGHT, font, text, center_x, y);
|
|
}
|
|
|
|
void epd_draw_line(uint8_t *frame, int x0, int y0, int x1, int y1, int thickness)
|
|
{
|
|
int dx = x1 - x0;
|
|
int dy = y1 - y0;
|
|
int steps = abs(dx) > abs(dy) ? abs(dx) : abs(dy);
|
|
if (steps == 0) {
|
|
steps = 1;
|
|
}
|
|
int half = thickness / 2;
|
|
for (int i = 0; i <= steps; i++) {
|
|
int x = x0 + dx * i / steps;
|
|
int y = y0 + dy * i / steps;
|
|
for (int oy = -half; oy <= half; oy++) {
|
|
for (int ox = -half; ox <= half; ox++) {
|
|
epd_draw_pixel(frame, x + ox, y + oy, EPD_COLOR_BLACK);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void epd_draw_checkmark(uint8_t *frame, int x, int y, int size)
|
|
{
|
|
int thickness = size / 6;
|
|
if (thickness < 2) {
|
|
thickness = 2;
|
|
}
|
|
/* Short down-stroke, then long up-stroke -- classic checkmark proportions. */
|
|
epd_draw_line(frame, x, y + size / 2, x + size / 3, y + size - thickness, thickness);
|
|
epd_draw_line(frame, x + size / 3, y + size - thickness, x + size, y, thickness);
|
|
}
|