Files
meshtastic_firmware/src/graphics/TFTColorRegions.h
T

185 lines
7.5 KiB
C++

#pragma once
#include "configuration.h"
#include <stdint.h>
namespace graphics
{
struct TFTColorRegion {
int16_t x;
int16_t y;
int16_t width;
int16_t height;
uint16_t onColorBe;
uint16_t offColorBe;
// Required by ST7789 driver: it scans until the first disabled entry.
bool enabled = false;
};
static constexpr size_t MAX_TFT_COLOR_REGIONS = 48;
extern TFTColorRegion colorRegions[MAX_TFT_COLOR_REGIONS];
enum class TFTColorRole : uint8_t {
HeaderBackground = 0,
HeaderTitle,
HeaderStatus,
SignalBars,
ConnectionIcon,
UtilizationFill,
FavoriteNode,
ActionMenuBorder,
ActionMenuBody,
ActionMenuTitle,
FrameMono,
BootSplash,
FavoriteNodeBGHighlight,
NavigationBar,
NavigationArrow,
Count
};
#if HAS_TFT || defined(HAS_SPI_TFT)
#define GRAPHICS_TFT_COLORING_ENABLED 1
#else
#define GRAPHICS_TFT_COLORING_ENABLED 0
#endif
static constexpr bool kTFTColoringEnabled = GRAPHICS_TFT_COLORING_ENABLED != 0;
constexpr bool isTFTColoringEnabled()
{
return kTFTColoringEnabled;
}
void setTFTColorRole(TFTColorRole role, uint16_t onColor, uint16_t offColor);
void registerTFTColorRegion(TFTColorRole role, int16_t x, int16_t y, int16_t width, int16_t height);
// Convenience helper for the common "set role then register one region" flow.
void setAndRegisterTFTColorRole(TFTColorRole role, uint16_t onColor, uint16_t offColor, int16_t x, int16_t y, int16_t width,
int16_t height);
// Register a region using explicit colors (no role lookup). Use when the
// color comes from a theme field rather than a role (e.g. battery fill).
void registerTFTColorRegionDirect(int16_t x, int16_t y, int16_t width, int16_t height, uint16_t onColor, uint16_t offColor);
void registerTFTActionMenuRegions(int16_t boxLeft, int16_t boxTop, int16_t boxWidth, int16_t boxHeight);
uint32_t getTFTColorFrameSignature();
uint8_t getTFTColorRegionCount();
void clearTFTColorRegions();
uint16_t resolveTFTColorPixel(int16_t x, int16_t y, bool isset, uint16_t defaultOnColor, uint16_t defaultOffColor);
// Resolve effective region-mapped OFF color at a coordinate in native-endian RGB565.
uint16_t resolveTFTOffColorAt(int16_t x, int16_t y, uint16_t defaultOffColor);
// -- Per-row fast path for the hot pixel loops in TFTDisplay::display() --------
// resolveTFTColorPixel() is O(regions) per pixel; for a full 800x480 repaint that
// dominates redraw time. Regions are vertically localized, so cull to the regions
// overlapping the current row once per row, then resolve each pixel against only
// those (inlined here, so no per-pixel cross-TU call).
extern uint8_t tftColorRowRegions[MAX_TFT_COLOR_REGIONS]; // indices into colorRegions[], ascending
extern uint8_t tftColorRowCount;
// Build tftColorRowRegions for row y. Call once per row before resolveTFTColorPixelRow().
void beginTFTColorRow(int16_t y);
// Resolve one pixel against the current row's active regions (set by beginTFTColorRow()).
// Highest-index region wins, matching resolveTFTColorPixel()'s precedence.
inline uint16_t resolveTFTColorPixelRow(int16_t x, bool isset, uint16_t defaultOnColor, uint16_t defaultOffColor)
{
for (int j = static_cast<int>(tftColorRowCount) - 1; j >= 0; j--) {
const TFTColorRegion &r = colorRegions[tftColorRowRegions[j]];
if (x >= r.x && x < r.x + r.width)
return isset ? r.onColorBe : r.offColorBe;
}
return isset ? defaultOnColor : defaultOffColor;
}
// -- Theme engine ------------------------------------------------------
// Each theme has four fields that work together:
//
// id - ThemeID:: constant, used for in-code references.
// name - human-readable label shown in the theme picker.
// uniqueIdentifier - the stable numeric value persisted to
// uiconfig.screen_rgb_color and restored at boot.
// This is a CONTRACT with saved configs on disk - once
// assigned, never reuse or renumber, even if the theme is
// deleted or the kThemes[] array is reordered.
// visible - controls whether a theme appears in the picker menu.
// Hidden themes can still be restored and applied if their
// uniqueIdentifier is persisted.
//
// Display order in the menu is controlled by kThemes[] array order among
// themes where visible == true, NOT by any numeric value above.
//
// To add a new theme:
// 1. Add a unique constant in ThemeID below (next unused value).
// 2. Add a kThemes[] entry at the desired menu position, with a unique
// uniqueIdentifier that has never been used by any prior theme.
// 3. Set visible=true if it should appear in the picker.
//
// To retire a theme without breaking saved configs:
// - Preferred: keep the entry and set visible=false so existing saved
// uniqueIdentifier values still resolve to the same theme.
// - If you remove the entry, resolveThemeIndex() falls back to DefaultDark
// when the persisted uniqueIdentifier no longer matches any theme.
// - Do NOT reuse a retired uniqueIdentifier for a future theme.
namespace ThemeID
{
constexpr uint32_t DefaultDark = 0;
constexpr uint32_t DefaultLight = 1;
constexpr uint32_t Christmas = 2;
constexpr uint32_t Pink = 3;
constexpr uint32_t Blue = 4;
constexpr uint32_t Creamsicle = 5;
constexpr uint32_t MeshtasticGreen = 6;
constexpr uint32_t ClassicRed = 7;
constexpr uint32_t MonochromeWhite = 8;
} // namespace ThemeID
// Per-role color pair stored in native (little-endian) RGB565 format.
struct TFTThemeRoleColor {
uint16_t onColor;
uint16_t offColor;
};
// Complete theme definition.
struct TFTThemeDef {
uint32_t id; // ThemeID constant - in-code identifier for this theme.
const char *name; // Human-readable label shown in the theme picker.
uint32_t uniqueIdentifier; // Stable persisted value copied into uiconfig.screen_rgb_color.
// Never reuse or renumber - see file-level notes above.
TFTThemeRoleColor roles[static_cast<size_t>(TFTColorRole::Count)];
uint16_t batteryFillGood;
uint16_t batteryFillMedium;
uint16_t batteryFillBad;
bool fullFrameInvert; // Apply full-frame FrameMono inversion (ST7789 light themes)
bool visible; // Show in the theme picker menu. Hidden themes still apply
// correctly if their uniqueIdentifier is persisted (dev/legacy themes).
};
// Count of themes whose .visible flag is true. Use this when building menus.
size_t getVisibleThemeCount();
// Access the Nth visible theme (0 .. getVisibleThemeCount()-1). Hidden themes
// are skipped, preserving kThemes[] order among the visible entries.
const TFTThemeDef &getVisibleThemeByIndex(size_t visibleIndex);
// Return the theme that matches uiconfig.screen_rgb_color (falls back to Dark).
const TFTThemeDef &getActiveTheme();
// Return the visible-theme index for the currently active theme, or SIZE_MAX
// if the active theme is hidden (so menus can show "no selection").
size_t getActiveVisibleThemeIndex();
// Convenience accessors - safe to call even when coloring is compiled out.
uint16_t getThemeHeaderBg();
uint16_t getThemeHeaderText();
uint16_t getThemeHeaderStatus();
uint16_t getThemeBodyBg();
uint16_t getThemeBodyFg();
bool isThemeFullFrameInvert();
uint16_t getThemeBatteryFillColor(int batteryPercent);
// Reinitialise default roleColors from the active theme. Call after a
// theme change so that any role registered without a prior setTFTColorRole()
// picks up theme-appropriate defaults.
void loadThemeDefaults();
} // namespace graphics