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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user