feat: rework GxEPDDisplay for rotation support and integrate e-ink build

- Remove fixed 128×128 virtual space and scaling; use 1:1 pixel mapping
- Display dimensions derived from panel native size + DISPLAY_ROTATION
- setCursor compensates for GFX font baseline offset (ascender per font size)
- Add getLineHeight() / getCharWidth() overrides for FreeSans fonts
- New envs: landscape (250×122) and portrait (122×250), BLE and dual variants
- Integrate all wio-tracker-l1-improvements features into e-ink build

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-05-20 19:12:17 +02:00
parent 67bccd3dd4
commit cffe960f3f
3 changed files with 120 additions and 102 deletions

View File

@@ -1,4 +1,3 @@
#include "GxEPDDisplay.h"
#ifdef EXP_PIN_BACKLIGHT
@@ -7,13 +6,22 @@
#endif
#ifndef DISPLAY_ROTATION
#define DISPLAY_ROTATION 3
#define DISPLAY_ROTATION 0
#endif
#ifdef ESP32
SPIClass SPI1 = SPIClass(FSPI);
#endif
// GFX fonts use the baseline as the cursor origin. UI code assumes top-of-cell
// coordinates (same convention as the OLED driver). Add the font ascender so
// the two conventions match.
static int fontAscender(int sz) {
if (sz == 3) return 26; // FreeSans18pt7b
if (sz == 2) return 17; // FreeSansBold12pt7b
return 13; // FreeSans9pt7b (sz == 1)
}
bool GxEPDDisplay::begin() {
display.epd2.selectSPI(SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0));
#ifdef ESP32
@@ -23,15 +31,14 @@ bool GxEPDDisplay::begin() {
#endif
display.init(115200, true, 2, false);
display.setRotation(DISPLAY_ROTATION);
setTextSize(1); // Default to size 1
setTextSize(1);
display.setPartialWindow(0, 0, display.width(), display.height());
display.fillScreen(GxEPD_WHITE);
display.display(true);
#if DISP_BACKLIGHT
#if DISP_BACKLIGHT
digitalWrite(DISP_BACKLIGHT, LOW);
pinMode(DISP_BACKLIGHT, OUTPUT);
#endif
#endif
_init = true;
return true;
}
@@ -64,30 +71,24 @@ void GxEPDDisplay::clear() {
void GxEPDDisplay::startFrame(Color bkg) {
display.fillScreen(GxEPD_WHITE);
display.setTextColor(_curr_color = GxEPD_BLACK);
_text_sz = 1;
display.setFont(&FreeSans9pt7b);
display_crc.reset();
}
void GxEPDDisplay::setTextSize(int sz) {
_text_sz = sz;
display_crc.update<int>(sz);
switch(sz) {
case 1: // Small
display.setFont(&FreeSans9pt7b);
break;
case 2: // Medium Bold
display.setFont(&FreeSansBold12pt7b);
break;
case 3: // Large
display.setFont(&FreeSans18pt7b);
break;
default:
display.setFont(&FreeSans9pt7b);
break;
switch (sz) {
case 2: display.setFont(&FreeSansBold12pt7b); break;
case 3: display.setFont(&FreeSans18pt7b); break;
default: display.setFont(&FreeSans9pt7b); break;
}
}
void GxEPDDisplay::setColor(Color c) {
display_crc.update<Color> (c);
// colours need to be inverted for epaper displays
display_crc.update<Color>(c);
// e-ink: DARK background = white paper, LIGHT foreground = black ink
if (c == DARK) {
display.setTextColor(_curr_color = GxEPD_WHITE);
} else {
@@ -98,7 +99,9 @@ void GxEPDDisplay::setColor(Color c) {
void GxEPDDisplay::setCursor(int x, int y) {
display_crc.update<int>(x);
display_crc.update<int>(y);
display.setCursor((x+offset_x)*scale_x, (y+offset_y)*scale_y);
// Offset y by the font ascender: callers pass top-of-cell y, GFX fonts
// expect baseline y. Without this, text would be clipped at the top.
display.setCursor(x, y + fontAscender(_text_sz));
}
void GxEPDDisplay::print(const char* str) {
@@ -111,7 +114,7 @@ void GxEPDDisplay::fillRect(int x, int y, int w, int h) {
display_crc.update<int>(y);
display_crc.update<int>(w);
display_crc.update<int>(h);
display.fillRect(x*scale_x, y*scale_y, w*scale_x, h*scale_y, _curr_color);
display.fillRect(x, y, w, h, _curr_color);
}
void GxEPDDisplay::drawRect(int x, int y, int w, int h) {
@@ -119,7 +122,7 @@ void GxEPDDisplay::drawRect(int x, int y, int w, int h) {
display_crc.update<int>(y);
display_crc.update<int>(w);
display_crc.update<int>(h);
display.drawRect(x*scale_x, y*scale_y, w*scale_x, h*scale_y, _curr_color);
display.drawRect(x, y, w, h, _curr_color);
}
void GxEPDDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) {
@@ -128,36 +131,13 @@ void GxEPDDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) {
display_crc.update<int>(w);
display_crc.update<int>(h);
display_crc.update<uint8_t>(bits, w * h / 8);
// Calculate the base position in display coordinates
uint16_t startX = x * scale_x;
uint16_t startY = y * scale_y;
// Width in bytes for bitmap processing
uint16_t widthInBytes = (w + 7) / 8;
// Process the bitmap row by row
for (uint16_t by = 0; by < h; by++) {
// Calculate the target y-coordinates for this logical row
int y1 = startY + (int)(by * scale_y);
int y2 = startY + (int)((by + 1) * scale_y);
int block_h = y2 - y1;
// Scan across the row bit by bit
for (uint16_t bx = 0; bx < w; bx++) {
// Calculate the target x-coordinates for this logical column
int x1 = startX + (int)(bx * scale_x);
int x2 = startX + (int)((bx + 1) * scale_x);
int block_w = x2 - x1;
// Get the current bit
uint16_t byteOffset = (by * widthInBytes) + (bx / 8);
uint8_t bitMask = 0x80 >> (bx & 7);
bool bitSet = pgm_read_byte(bits + byteOffset) & bitMask;
// If the bit is set, draw a block of pixels
if (bitSet) {
// Draw the block as a filled rectangle
display.fillRect(x1, y1, block_w, block_h, _curr_color);
if (pgm_read_byte(bits + byteOffset) & bitMask) {
display.drawPixel(x + bx, y + by, _curr_color);
}
}
}
@@ -167,7 +147,7 @@ uint16_t GxEPDDisplay::getTextWidth(const char* str) {
int16_t x1, y1;
uint16_t w, h;
display.getTextBounds(str, 0, 0, &x1, &y1, &w, &h);
return ceil((w + 1) / scale_x);
return w;
}
void GxEPDDisplay::endFrame() {