1
0
mirror of https://github.com/thooge/esp32-nmea2000-obp60.git synced 2026-08-18 18:32:30 +02:00

Implement shaddow frame buffer for TFT display

This commit is contained in:
Norbert Walter
2026-03-01 15:55:34 +00:00
parent 907a817573
commit 762004ac9f
3 changed files with 187 additions and 11 deletions
+30 -5
View File
@@ -57,9 +57,30 @@ GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> & getdisplay(){r
#endif
#ifdef DISPLAY_ST7796
// only instantiate; class defined in header
static LGFX display;
LGFX & getdisplay(){return display;}
// panel device + offscreen shadow framebuffer
static LGFX panelDisplay;
static LGFXCanvas shadowDisplay(&panelDisplay);
static bool shadowDisplayInitialized = false;
LGFXCanvas & getdisplay(){return shadowDisplay;}
LGFX & getpaneldisplay(){return panelDisplay;}
bool initDisplayShadowBuffer(){
if (shadowDisplayInitialized) return true;
shadowDisplay.setPsram(true);
shadowDisplay.setColorDepth(16);
shadowDisplay.setTextDatum(textdatum_t::baseline_left);
if (shadowDisplay.createSprite(GxEPD_WIDTH, GxEPD_HEIGHT) == nullptr) {
shadowDisplayInitialized = false;
return false;
}
shadowDisplay.fillScreen(GxEPD_BLACK);
shadowDisplayInitialized = true;
return true;
}
#endif
// Horter I2C moduls
@@ -258,7 +279,9 @@ void deepSleep(CommonData &common){
getdisplay().setCursor(65, 175);
getdisplay().print("To wake up press key and wait 5s");
displayNextPage(); // Update display contents
#ifndef DISPLAY_ST7796
#ifdef DISPLAY_ST7796
getpaneldisplay().powerSave(true); // Display power save
#else
getdisplay().powerOff(); // Display power off
#endif
setPortPin(OBP_POWER_50, false); // Power off ePaper display
@@ -285,7 +308,9 @@ void deepSleep(CommonData &common){
getdisplay().setCursor(65, 175);
getdisplay().print("To wake up press wheel and wait 5s");
displayNextPage(); // Partial update
#ifndef DISPLAY_ST7796
#ifdef DISPLAY_ST7796
getpaneldisplay().powerSave(true); // Display power save
#else
getdisplay().powerOff(); // Display power off
#endif
setPortPin(OBP_POWER_EPD, false); // Power off ePaper display
+146 -3
View File
@@ -268,7 +268,148 @@ private:
lgfx::Panel_ST7796 _panel_instance;
};
LGFX & getdisplay();
class LGFXCanvas : public lgfx::LGFX_Sprite {
public:
explicit LGFXCanvas(lgfx::LGFX_Device* parent = nullptr) : lgfx::LGFX_Sprite(parent) {}
using lgfx::LGFX_Sprite::setFont;
void setFont(const GFXfont *font) {
if (font == nullptr) {
_currentAdfFont = nullptr;
lgfx::LGFX_Sprite::setFont(nullptr);
return;
}
if (font->glyph == nullptr || font->bitmap == nullptr || font->last < font->first) {
_currentAdfFont = nullptr;
lgfx::LGFX_Sprite::setFont(nullptr);
return;
}
const uint16_t glyphCount = static_cast<uint16_t>(font->last - font->first + 1);
if (glyphCount == 0) {
lgfx::LGFX_Sprite::setFont(nullptr);
return;
}
if (_adfGlyphCount != glyphCount || _adfGlyphBridge == nullptr) {
if (_adfGlyphBridge != nullptr) {
free(_adfGlyphBridge);
_adfGlyphBridge = nullptr;
_adfGlyphCount = 0;
}
_adfGlyphBridge = static_cast<lgfx::GFXglyph*>(malloc(sizeof(lgfx::GFXglyph) * glyphCount));
if (_adfGlyphBridge == nullptr) {
lgfx::LGFX_Sprite::setFont(nullptr);
return;
}
_adfGlyphCount = glyphCount;
}
for (uint16_t index = 0; index < glyphCount; ++index) {
_adfGlyphBridge[index].bitmapOffset = font->glyph[index].bitmapOffset;
_adfGlyphBridge[index].width = font->glyph[index].width;
_adfGlyphBridge[index].height = font->glyph[index].height;
_adfGlyphBridge[index].xAdvance = font->glyph[index].xAdvance;
_adfGlyphBridge[index].xOffset = font->glyph[index].xOffset;
_adfGlyphBridge[index].yOffset = font->glyph[index].yOffset;
}
_adfFontBridge = lgfx::GFXfont(
const_cast<uint8_t*>(font->bitmap),
_adfGlyphBridge,
font->first,
font->last,
font->yAdvance
);
_currentAdfFont = font;
lgfx::LGFX_Sprite::setFont(&_adfFontBridge);
}
void getTextBounds(const String &txt, int16_t x, int16_t y,
int16_t *x0, int16_t *y0,
uint16_t *w, uint16_t *h) {
if (w == nullptr || h == nullptr) {
return;
}
if (_currentAdfFont == nullptr || txt.length() == 0) {
*w = textWidth(txt);
*h = fontHeight();
if (x0) *x0 = x;
if (y0) *y0 = y - static_cast<int16_t>(*h);
return;
}
const float sx = getTextSizeX();
const float sy = getTextSizeY();
int32_t cursorX = x;
int32_t cursorY = y;
int32_t minX = 0;
int32_t minY = 0;
int32_t maxX = 0;
int32_t maxY = 0;
bool hasPixel = false;
for (size_t i = 0; i < txt.length(); ++i) {
char c = txt[i];
if (c == '\r') continue;
if (c == '\n') {
cursorX = x;
cursorY += static_cast<int32_t>(_currentAdfFont->yAdvance * sy);
continue;
}
if (c < _currentAdfFont->first || c > _currentAdfFont->last) {
continue;
}
const GFXglyph* glyph = &_currentAdfFont->glyph[static_cast<uint16_t>(c) - _currentAdfFont->first];
const int32_t gw = static_cast<int32_t>(glyph->width * sx);
const int32_t gh = static_cast<int32_t>(glyph->height * sy);
const int32_t gx1 = cursorX + static_cast<int32_t>(glyph->xOffset * sx);
const int32_t gy1 = cursorY + static_cast<int32_t>(glyph->yOffset * sy);
if (gw > 0 && gh > 0) {
const int32_t gx2 = gx1 + gw - 1;
const int32_t gy2 = gy1 + gh - 1;
if (!hasPixel) {
minX = gx1; minY = gy1; maxX = gx2; maxY = gy2;
hasPixel = true;
} else {
if (gx1 < minX) minX = gx1;
if (gy1 < minY) minY = gy1;
if (gx2 > maxX) maxX = gx2;
if (gy2 > maxY) maxY = gy2;
}
}
cursorX += static_cast<int32_t>(glyph->xAdvance * sx);
}
if (hasPixel) {
if (x0) *x0 = static_cast<int16_t>(minX);
if (y0) *y0 = static_cast<int16_t>(minY);
*w = static_cast<uint16_t>(maxX - minX + 1);
*h = static_cast<uint16_t>(maxY - minY + 1);
} else {
if (x0) *x0 = x;
if (y0) *y0 = y;
*w = 0;
*h = 0;
}
}
void setFullWindow() { /* no-op on TFT */ }
private:
lgfx::GFXfont _adfFontBridge { nullptr, nullptr, 0, 0, 0 };
lgfx::GFXglyph* _adfGlyphBridge = nullptr;
uint16_t _adfGlyphCount = 0;
const GFXfont* _currentAdfFont = nullptr;
};
LGFXCanvas & getdisplay();
LGFX & getpaneldisplay();
bool initDisplayShadowBuffer();
#endif
// Page display return values
@@ -344,7 +485,7 @@ inline void displayDrawBitmap(int16_t x, int16_t y,
inline void displayFirstPage() {
#ifdef DISPLAY_ST7796
// TFT LCD doesn't need firstPage()
initDisplayShadowBuffer();
#else
getdisplay().firstPage();
#endif
@@ -352,7 +493,9 @@ inline void displayFirstPage() {
inline void displayNextPage() {
#ifdef DISPLAY_ST7796
// TFT LCD doesn't need nextPage() for refresh
if (initDisplayShadowBuffer()) {
getdisplay().pushSprite(0, 0);
}
#else
getdisplay().nextPage();
#endif
+11 -3
View File
@@ -285,7 +285,9 @@ void underVoltageError(CommonData &common) {
getdisplay().setCursor(65, 175);
getdisplay().print("Charge battery and restart system");
displayNextPage(); // Partial update
#ifndef DISPLAY_ST7796
#ifdef DISPLAY_ST7796
getpaneldisplay().powerSave(true); // Display power save
#else
getdisplay().powerOff(); // Display power off
#endif
setPortPin(OBP_POWER_EPD, false); // Power off ePaper display
@@ -307,7 +309,9 @@ void underVoltageError(CommonData &common) {
getdisplay().setCursor(65, 175);
getdisplay().print("To wake up repower system");
displayNextPage(); // Partial update
#ifndef DISPLAY_ST7796
#ifdef DISPLAY_ST7796
getpaneldisplay().powerSave(true); // Display power save
#else
getdisplay().powerOff(); // Display power off
#endif
#endif
@@ -380,12 +384,16 @@ void OBP60Task(GwApi *api){
#ifdef DISPLAY_GDEY042T81
getdisplay().init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
#elif defined DISPLAY_ST7796
getdisplay().init(); // Init for ST7796 TFT LCD
getpaneldisplay().init(); // Init for ST7796 TFT LCD panel
#else
getdisplay().init(115200); // Init for normal displays
#endif
#ifdef DISPLAY_ST7796
getpaneldisplay().setRotation(0); // Set display orientation (horizontal)
#else
getdisplay().setRotation(0); // Set display orientation (horizontal)
#endif
displaySetFullWindow(); // Set full Refresh (E-Ink only)
displayFirstPage(); // set first page
getdisplay().fillScreen(commonData.bgcolor);