// ═══════════════════════════════════════════════════════════════════ // Arduino_GFX adapter implementation // Only compiled when USE_ARDUINO_GFX is set (Waveshare path). // For the TFT_eSPI path this file compiles to nothing. // ═══════════════════════════════════════════════════════════════════ #include "BoardConfig.h" #include #if USE_ARDUINO_GFX #include "DisplayDriver.h" // ───────────────────────────────────────────────────────────────── // Gfx adapter // ───────────────────────────────────────────────────────────────── Gfx::Gfx() {} void Gfx::init() { // Waveshare ESP32-S3 Touch LCD 4.3" — RGB parallel, ST7262 panel Arduino_ESP32RGBPanel *rgbpanel = new Arduino_ESP32RGBPanel( LCD_DE, LCD_VSYNC, LCD_HSYNC, LCD_PCLK, LCD_R0, LCD_R1, LCD_R2, LCD_R3, LCD_R4, LCD_G0, LCD_G1, LCD_G2, LCD_G3, LCD_G4, LCD_G5, LCD_B0, LCD_B1, LCD_B2, LCD_B3, LCD_B4, 1 /* hsync_polarity */, 10 /* hsync_front_porch */, 8 /* hsync_pulse_width */, 50 /* hsync_back_porch */, 1 /* vsync_polarity */, 10 /* vsync_front_porch */, 8 /* vsync_pulse_width */, 20 /* vsync_back_porch */, 1 /* pclk_active_neg */, 16000000 /* prefer_speed = 16MHz PCLK */ ); _gfx = new Arduino_RGB_Display( SCREEN_WIDTH, SCREEN_HEIGHT, rgbpanel, DISPLAY_ROTATION, true /* auto_flush */); if (!_gfx->begin()) { Serial.println("[GFX] Display init FAILED"); return; } Serial.printf("[GFX] Display OK: %dx%d\n", SCREEN_WIDTH, SCREEN_HEIGHT); _gfx->fillScreen(0xF800); // RED TEST delay(2000); _gfx->fillScreen(0x07E0); // GREEN TEST delay(2000); _gfx->fillScreen(0x001F); // BLUE TEST delay(2000); _gfx->fillScreen(0x07E0); // GREEN TEST delay(2000); _gfx->fillScreen(0x001F); // BLUE TEST delay(2000); } void Gfx::setRotation(uint8_t r) { if (_gfx) _gfx->setRotation(r); } void Gfx::fillScreen(uint16_t c) { if (_gfx) _gfx->fillScreen(c); } void Gfx::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t c) { if (_gfx) _gfx->fillRect(x, y, w, h, c); } void Gfx::fillRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t r, uint16_t c) { if (_gfx) _gfx->fillRoundRect(x, y, w, h, r, c); } void Gfx::drawRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t r, uint16_t c) { if (_gfx) _gfx->drawRoundRect(x, y, w, h, r, c); } void Gfx::drawFastVLine(int32_t x, int32_t y, int32_t h, uint16_t c) { if (_gfx) _gfx->drawFastVLine(x, y, h, c); } void Gfx::setTextColor(uint16_t fg, uint16_t bg) { _textFg = fg; _textBg = bg; if (_gfx) _gfx->setTextColor(fg, bg); } void Gfx::setTextFont(uint8_t font) { // TFT_eSPI font IDs don't map 1:1 to Arduino_GFX. // Using default built-in font; setTextSize controls scale. // TODO: Map to GFXfont pointers for better visual fidelity. (void)font; } void Gfx::setTextSize(uint8_t s) { _textSize = s; if (_gfx) _gfx->setTextSize(s); } void Gfx::setTextDatum(uint8_t d) { _textDatum = d; } void Gfx::drawString(const char* str, int32_t x, int32_t y) { if (!_gfx || !str) return; int16_t bx, by; uint16_t tw, th; _gfx->getTextBounds(str, 0, 0, &bx, &by, &tw, &th); // Horizontal alignment from datum int hAlign = _textDatum % 3; if (hAlign == 1) x -= (int32_t)tw / 2; // center else if (hAlign == 2) x -= (int32_t)tw; // right // Vertical alignment from datum int vAlign = _textDatum / 3; if (vAlign == 1) y -= (int32_t)th / 2; // middle else if (vAlign == 2) y -= (int32_t)th; // bottom _gfx->setCursor(x - bx, y - by); _gfx->print(str); } int16_t Gfx::textWidth(const char* str) { if (!_gfx || !str) return 0; int16_t bx, by; uint16_t tw, th; _gfx->getTextBounds(str, 0, 0, &bx, &by, &tw, &th); return (int16_t)tw; } void Gfx::setCursor(int32_t x, int32_t y) { if (_gfx) _gfx->setCursor(x, y); } void Gfx::print(const char* str) { if (_gfx) _gfx->print(str); } // ───────────────────────────────────────────────────────────────── // GfxSprite adapter // // On the 800x480 RGB panel the LCD controller has its own GRAM, // so direct drawing rarely tears. This implementation is // intentionally minimal — it renders tiles as direct draws. // // TODO: For flicker-free tile updates, allocate an Arduino_Canvas // backed by PSRAM and blit via draw16bitBeRGBBitmap(). // ───────────────────────────────────────────────────────────────── GfxSprite::GfxSprite(Gfx* parent) : _parent(parent) {} void GfxSprite::createSprite(int16_t w, int16_t h) { _w = w; _h = h; Serial.printf("[GFX] Sprite %dx%d created (direct-draw mode)\n", w, h); } void GfxSprite::deleteSprite() { _w = 0; _h = 0; } void GfxSprite::fillSprite(uint16_t color) { if (_parent && _parent->raw()) _parent->raw()->fillRect(_pushX, _pushY, _w, _h, color); } void GfxSprite::fillRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t r, uint16_t c) { if (_parent && _parent->raw()) _parent->raw()->fillRoundRect(_pushX + x, _pushY + y, w, h, r, c); } void GfxSprite::drawRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t r, uint16_t c) { if (_parent && _parent->raw()) _parent->raw()->drawRoundRect(_pushX + x, _pushY + y, w, h, r, c); } void GfxSprite::setTextColor(uint16_t fg, uint16_t bg) { _textFg = fg; _textBg = bg; if (_parent && _parent->raw()) _parent->raw()->setTextColor(fg, bg); } void GfxSprite::setTextFont(uint8_t font) { (void)font; } void GfxSprite::setTextSize(uint8_t size) { _textSize = size; if (_parent && _parent->raw()) _parent->raw()->setTextSize(size); } void GfxSprite::setTextDatum(uint8_t datum) { _textDatum = datum; } void GfxSprite::drawString(const char* str, int32_t x, int32_t y) { if (!_parent) return; _parent->setTextDatum(_textDatum); _parent->setTextSize(_textSize); _parent->setTextColor(_textFg, _textBg); _parent->drawString(str, _pushX + x, _pushY + y); } void GfxSprite::pushSprite(int32_t x, int32_t y) { // Record the offset for subsequent draw calls in the next cycle. // NOTE: In the TFT_eSPI path, sprite drawing happens BEFORE pushSprite // (draws into offscreen buffer, then blits). In this direct-draw stub, // the offset from the PREVIOUS pushSprite call is used. After one full // drawAll() cycle, all tiles render at the correct positions. _pushX = x; _pushY = y; } #endif // USE_ARDUINO_GFX