mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-08-04 03:06:12 +00:00
Author mini-icons as ASCII-art rows packed to bytes at compile time via constexpr packRow(), so the glyph shape is visible in source while the binary holds only the packed bytes (strings never reach flash, identical to the previous hand-written hex). Bundle each icon's width/height with its data through MiniIcon and the MINI_ICON(name, width, ...) macro, so draw sites carry no magic numbers: miniIconDraw(d, x, y, ICON_CHECK). Also add an unused-by-design skeleton for full-size page icons (≤32 px): packRow32 / BigIcon / BIG_ICON / bigIconDraw, for authoring a future big glyph the same readable way. Existing XBM bitmaps left untouched. Flash size unchanged; verified no ASCII-art strings reach the .elf. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
238 lines
10 KiB
C++
238 lines
10 KiB
C++
#pragma once
|
||
|
||
#include <stdint.h>
|
||
#include <helpers/ui/DisplayDriver.h>
|
||
|
||
// ── Scalable mini-icons ──────────────────────────────────────────────────────
|
||
// Small procedural glyphs (delivery markers, etc.) authored on a 1× pixel grid
|
||
// and scaled to the current font, so they stay legible on large-font layouts
|
||
// (e.g. landscape e-ink renders text at 2×). Distinct from the XBM page icons
|
||
// further down, which are fixed-size full bitmaps drawn via drawXbm().
|
||
//
|
||
// To add a mini-icon:
|
||
// 1. Draw it as ASCII-art rows, one string per row (width ≤ 8): any char
|
||
// other than ' ' or '.' is a filled pixel. packRow() packs each string
|
||
// into one byte at compile time, so the strings never reach flash — the
|
||
// binary holds only the packed bytes, identical to hand-written hex.
|
||
// 2. Wrap the rows in a MINI_ICON(name, width, ...) — width and row count are
|
||
// bundled with the data, so the draw site carries no magic numbers.
|
||
// 3. Draw it with miniIconDraw(display, x, topY, name).
|
||
// Scaling is automatic — no per-display handling needed.
|
||
//
|
||
// packRow() is written as a single-expression recursion so it stays a valid
|
||
// constant expression on any C++ standard the firmware targets (≥ C++11), not
|
||
// just the relaxed C++14 constexpr with loops. It packs up to the string's NUL
|
||
// (capped at 8 cols), so width lives once in MINI_ICON, not in every row.
|
||
constexpr uint8_t packBit(const char* s, int x) {
|
||
return (s[x] && s[x] != ' ' && s[x] != '.') ? (uint8_t)(1u << x) : 0;
|
||
}
|
||
constexpr uint8_t packRow(const char* s, int x = 0) {
|
||
return (!s[x] || x >= 8) ? 0 : (uint8_t)(packBit(s, x) | packRow(s, x + 1));
|
||
}
|
||
|
||
// A mini-icon bundles its pixel data with its dimensions, so call sites can't
|
||
// pass a stale width/height. Built via the MINI_ICON macro below.
|
||
struct MiniIcon { uint8_t w, h; const uint8_t* rows; };
|
||
|
||
// Define a mini-icon: the row count (height) is derived from the initializer,
|
||
// the width is stated once. Emits a packed byte array plus a MiniIcon view.
|
||
#define MINI_ICON(name, width, ...) \
|
||
static constexpr uint8_t name##_rows[] = { __VA_ARGS__ }; \
|
||
static constexpr MiniIcon name = { (uint8_t)(width), \
|
||
(uint8_t)sizeof(name##_rows), name##_rows }
|
||
|
||
// Pixel scale from the font: 1× on an 8px OLED line, 2× on a 16px landscape
|
||
// e-ink line, etc. Bitmaps are authored on the 1× grid.
|
||
inline int miniIconScale(DisplayDriver& d) {
|
||
int s = d.getLineHeight() / 8;
|
||
return s < 1 ? 1 : s;
|
||
}
|
||
|
||
// Draw a w×h (w ≤ 8) bitmap with the current ink colour, scaled by the font and
|
||
// vertically centred in the text line that starts at top_y.
|
||
inline void miniIconDraw(DisplayDriver& d, int x, int top_y,
|
||
const uint8_t* rows, int w, int h) {
|
||
const int s = miniIconScale(d);
|
||
int y = top_y + (d.getLineHeight() - h * s) / 2;
|
||
if (y < top_y) y = top_y;
|
||
for (int r = 0; r < h; r++)
|
||
for (int c = 0; c < w; c++)
|
||
if (rows[r] & (1 << c)) d.fillRect(x + c * s, y + r * s, s, s);
|
||
}
|
||
|
||
// Preferred overload: dimensions travel with the icon — no magic numbers.
|
||
inline void miniIconDraw(DisplayDriver& d, int x, int top_y, const MiniIcon& ic) {
|
||
miniIconDraw(d, x, top_y, ic.rows, ic.w, ic.h);
|
||
}
|
||
|
||
// Horizontal row of `count` square dots (scaled, vertically centred). Used by
|
||
// the "awaiting ACK" marker, where the dot count = number of send attempts.
|
||
inline void miniIconDotRow(DisplayDriver& d, int x, int top_y, int count) {
|
||
const int s = miniIconScale(d);
|
||
const int dot = 2 * s, pitch = 3 * s; // 2px dot + 1px gap, scaled
|
||
int y = top_y + (d.getLineHeight() - dot) / 2;
|
||
if (y < top_y) y = top_y;
|
||
for (int i = 0; i < count; i++) d.fillRect(x + i * pitch, y, dot, dot);
|
||
}
|
||
|
||
// Mini-icon bitmaps (authored on the 1× grid as ASCII-art; see packRow above).
|
||
MINI_ICON(ICON_CHECK, 5, // ✓
|
||
packRow("....#"),
|
||
packRow("...#."),
|
||
packRow("#.#.."),
|
||
packRow(".#..."));
|
||
MINI_ICON(ICON_CROSS, 4, // ✗
|
||
packRow("#..#"),
|
||
packRow(".##."),
|
||
packRow(".##."),
|
||
packRow("#..#"));
|
||
|
||
// ── Big ASCII-art icons (skeleton, not yet used) ─────────────────────────────
|
||
// Same authoring idea as the mini-icons but for full page glyphs up to 32 px
|
||
// wide: one uint32_t per row. The existing XBM bitmaps below (logo/bluetooth/…)
|
||
// stay hand-encoded; reach for this when adding a *new* big icon so it's
|
||
// readable in source. Drawn at 1× (page icons aren't font-scaled).
|
||
//
|
||
// To add one:
|
||
// BIG_ICON(MY_ICON, 16,
|
||
// packRow32("......####......"),
|
||
// packRow32(".....######....."),
|
||
// ...);
|
||
// bigIconDraw(display, x, y, MY_ICON);
|
||
constexpr uint32_t packBit32(const char* s, int x) {
|
||
return (s[x] && s[x] != ' ' && s[x] != '.') ? (1u << x) : 0u;
|
||
}
|
||
constexpr uint32_t packRow32(const char* s, int x = 0) {
|
||
return (!s[x] || x >= 32) ? 0u : (packBit32(s, x) | packRow32(s, x + 1));
|
||
}
|
||
|
||
struct BigIcon { uint8_t w, h; const uint32_t* rows; };
|
||
|
||
#define BIG_ICON(name, width, ...) \
|
||
static constexpr uint32_t name##_rows[] = { __VA_ARGS__ }; \
|
||
static constexpr BigIcon name = { \
|
||
(uint8_t)(width), \
|
||
(uint8_t)(sizeof(name##_rows) / sizeof(uint32_t)), name##_rows }
|
||
|
||
// Draw a packed big icon at 1× with the current ink colour, top-left at (x, y).
|
||
inline void bigIconDraw(DisplayDriver& d, int x, int y, const BigIcon& ic) {
|
||
for (int r = 0; r < ic.h; r++)
|
||
for (int c = 0; c < ic.w; c++)
|
||
if (ic.rows[r] & (1u << c)) d.fillRect(x + c, y + r, 1, 1);
|
||
}
|
||
|
||
// 'meshcore', 128x13px
|
||
static const uint8_t meshcore_logo [] = {
|
||
0x3c, 0x01, 0xe3, 0xff, 0xc7, 0xff, 0x8f, 0x03, 0x87, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe,
|
||
0x3c, 0x03, 0xe3, 0xff, 0xc7, 0xff, 0x8e, 0x03, 0x8f, 0xfe, 0x3f, 0xfe, 0x1f, 0xff, 0x1f, 0xfe,
|
||
0x3e, 0x03, 0xc3, 0xff, 0x8f, 0xff, 0x0e, 0x07, 0x8f, 0xfe, 0x7f, 0xfe, 0x1f, 0xff, 0x1f, 0xfc,
|
||
0x3e, 0x07, 0xc7, 0x80, 0x0e, 0x00, 0x0e, 0x07, 0x9e, 0x00, 0x78, 0x0e, 0x3c, 0x0f, 0x1c, 0x00,
|
||
0x3e, 0x0f, 0xc7, 0x80, 0x1e, 0x00, 0x0e, 0x07, 0x1e, 0x00, 0x70, 0x0e, 0x38, 0x0f, 0x3c, 0x00,
|
||
0x7f, 0x0f, 0xc7, 0xfe, 0x1f, 0xfc, 0x1f, 0xff, 0x1c, 0x00, 0x70, 0x0e, 0x38, 0x0e, 0x3f, 0xf8,
|
||
0x7f, 0x1f, 0xc7, 0xfe, 0x0f, 0xff, 0x1f, 0xff, 0x1c, 0x00, 0xf0, 0x0e, 0x38, 0x0e, 0x3f, 0xf8,
|
||
0x7f, 0x3f, 0xc7, 0xfe, 0x0f, 0xff, 0x1f, 0xff, 0x1c, 0x00, 0xf0, 0x1e, 0x3f, 0xfe, 0x3f, 0xf0,
|
||
0x77, 0x3b, 0x87, 0x00, 0x00, 0x07, 0x1c, 0x0f, 0x3c, 0x00, 0xe0, 0x1c, 0x7f, 0xfc, 0x38, 0x00,
|
||
0x77, 0xfb, 0x8f, 0x00, 0x00, 0x07, 0x1c, 0x0f, 0x3c, 0x00, 0xe0, 0x1c, 0x7f, 0xf8, 0x38, 0x00,
|
||
0x73, 0xf3, 0x8f, 0xff, 0x0f, 0xff, 0x1c, 0x0e, 0x3f, 0xf8, 0xff, 0xfc, 0x70, 0x78, 0x7f, 0xf8,
|
||
0xe3, 0xe3, 0x8f, 0xff, 0x1f, 0xfe, 0x3c, 0x0e, 0x3f, 0xf8, 0xff, 0xfc, 0x70, 0x3c, 0x7f, 0xf8,
|
||
0xe3, 0xe3, 0x8f, 0xff, 0x1f, 0xfc, 0x3c, 0x0e, 0x1f, 0xf8, 0xff, 0xf8, 0x70, 0x3c, 0x7f, 0xf8,
|
||
};
|
||
|
||
static const uint8_t bluetooth_on[] = {
|
||
0x00, 0x00, 0x00, 0x00,
|
||
0x00, 0x00, 0x00, 0x00,
|
||
0x00, 0x00, 0x00, 0x00,
|
||
0x00, 0x30, 0x00, 0x00,
|
||
0x00, 0x3C, 0x00, 0x00,
|
||
0x00, 0x3E, 0x00, 0x00,
|
||
0x00, 0x3F, 0x80, 0x00,
|
||
0x00, 0x3F, 0xC0, 0x00,
|
||
0x00, 0x3B, 0xE0, 0x00,
|
||
0x30, 0x38, 0xF8, 0x00,
|
||
0x3C, 0x38, 0x7C, 0x00,
|
||
0x3E, 0x38, 0x7C, 0x00,
|
||
0x1F, 0xB8, 0xF8, 0x70,
|
||
0x07, 0xF9, 0xF0, 0x78,
|
||
0x03, 0xFF, 0xC0, 0x78,
|
||
0x00, 0xFF, 0x80, 0x3C,
|
||
0x00, 0x7F, 0x07, 0x1C,
|
||
0x00, 0x7E, 0x07, 0x1C,
|
||
0x03, 0xFF, 0x82, 0x1C,
|
||
0x03, 0xFF, 0xC0, 0x78,
|
||
0x07, 0xFB, 0xE0, 0x78,
|
||
0x0F, 0xB8, 0xF8, 0x70,
|
||
0x3E, 0x38, 0x7C, 0x00,
|
||
0x3C, 0x38, 0x7C, 0x00,
|
||
0x38, 0x38, 0xF8, 0x00,
|
||
0x00, 0x39, 0xF0, 0x00,
|
||
0x00, 0x3F, 0xC0, 0x00,
|
||
0x00, 0x3F, 0x80, 0x00,
|
||
0x00, 0x3E, 0x00, 0x00,
|
||
0x00, 0x3C, 0x00, 0x00,
|
||
0x00, 0x38, 0x00, 0x00,
|
||
0x00, 0x00, 0x00, 0x00,
|
||
};
|
||
|
||
static const uint8_t bluetooth_off[] = {
|
||
0x00, 0x00, 0x00, 0x00,
|
||
0x00, 0x00, 0x00, 0x00,
|
||
0x00, 0x03, 0x80, 0x00,
|
||
0x00, 0x03, 0xC0, 0x00,
|
||
0x00, 0x03, 0xE0, 0x00,
|
||
0x38, 0x03, 0xF8, 0x00,
|
||
0x3C, 0x03, 0xFC, 0x00,
|
||
0x3E, 0x03, 0xBF, 0x00,
|
||
0x0F, 0x83, 0x8F, 0x80,
|
||
0x07, 0xC3, 0x87, 0xC0,
|
||
0x03, 0xF0, 0x03, 0xC0,
|
||
0x00, 0xF8, 0x0F, 0x80,
|
||
0x00, 0x7C, 0x0F, 0x00,
|
||
0x00, 0x1F, 0x0E, 0x00,
|
||
0x00, 0x0F, 0x80, 0x00,
|
||
0x00, 0x07, 0xE0, 0x00,
|
||
0x00, 0x07, 0xF0, 0x00,
|
||
0x00, 0x0F, 0xF8, 0x00,
|
||
0x00, 0x3F, 0xBE, 0x00,
|
||
0x00, 0x7F, 0x9F, 0x00,
|
||
0x00, 0xFB, 0x8F, 0xC0,
|
||
0x03, 0xE3, 0x83, 0xE0,
|
||
0x03, 0xC3, 0x87, 0xF0,
|
||
0x03, 0x83, 0x8F, 0xFC,
|
||
0x00, 0x03, 0xBF, 0x3C,
|
||
0x00, 0x03, 0xFC, 0x1C,
|
||
0x00, 0x03, 0xF8, 0x00,
|
||
0x00, 0x03, 0xE0, 0x00,
|
||
0x00, 0x03, 0xC0, 0x00,
|
||
0x00, 0x03, 0x80, 0x00,
|
||
0x00, 0x00, 0x00, 0x00,
|
||
0x00, 0x00, 0x00, 0x00,
|
||
};
|
||
|
||
static const uint8_t power_icon[] = {
|
||
0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00,
|
||
0x00, 0x33, 0xCC, 0x00, 0x00, 0xF3, 0xCF, 0x00, 0x01, 0xF3, 0xCF, 0x80,
|
||
0x03, 0xF3, 0xCF, 0xC0, 0x07, 0xF3, 0xCF, 0xE0, 0x0F, 0xE3, 0xC7, 0xF0,
|
||
0x1F, 0xC3, 0xC3, 0xF8, 0x1F, 0x83, 0xC1, 0xF8, 0x3F, 0x03, 0xC0, 0xFC,
|
||
0x3E, 0x03, 0xC0, 0x7C, 0x3E, 0x03, 0xC0, 0x7C, 0x7E, 0x01, 0x80, 0x7E,
|
||
0x7C, 0x00, 0x00, 0x3E, 0x7C, 0x00, 0x00, 0x3E, 0x7C, 0x00, 0x00, 0x3E,
|
||
0x7C, 0x00, 0x00, 0x3E, 0x7C, 0x00, 0x00, 0x3E, 0x3E, 0x00, 0x00, 0x7C,
|
||
0x3E, 0x00, 0x00, 0x7C, 0x3F, 0x00, 0x00, 0xFC, 0x1F, 0x80, 0x01, 0xF8,
|
||
0x1F, 0xC0, 0x03, 0xF8, 0x0F, 0xE0, 0x07, 0xF0, 0x0F, 0xF8, 0x1F, 0xF0,
|
||
0x07, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0xC0, 0x00, 0xFF, 0xFF, 0x00,
|
||
0x00, 0x3F, 0xFC, 0x00, 0x00, 0x0F, 0xF0, 0x00,
|
||
};
|
||
|
||
static const uint8_t advert_icon[] = {
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x30,
|
||
0x1C, 0x00, 0x00, 0x38, 0x18, 0x00, 0x00, 0x18, 0x30, 0x00, 0x00, 0x0C,
|
||
0x30, 0x60, 0x06, 0x0C, 0x60, 0xE0, 0x07, 0x06, 0x61, 0xC0, 0x03, 0x86,
|
||
0xE1, 0x81, 0x81, 0x87, 0xC3, 0x07, 0xE0, 0xC3, 0xC3, 0x0F, 0xF0, 0xC3,
|
||
0xC3, 0x0F, 0xF0, 0xC3, 0xC3, 0x0F, 0xF0, 0xC3, 0xC3, 0x0F, 0xF0, 0xC3,
|
||
0xC3, 0x07, 0xE0, 0xC3, 0xC1, 0x83, 0xC1, 0x83, 0x61, 0x80, 0x01, 0x86,
|
||
0x60, 0xC0, 0x03, 0x06, 0x70, 0xE0, 0x07, 0x0E, 0x30, 0x40, 0x02, 0x0C,
|
||
0x38, 0x00, 0x00, 0x1C, 0x18, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x30,
|
||
0x04, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
};
|