acidportal- 😈 Worlds smallest Evil Portal on a LilyGo T-QT |
git clone git://git.acid.vegas/acidportal.git |
Log | Files | Refs | Archive | README | LICENSE |
TFT_eSPI.cpp (195592B)
1 /*************************************************** 2 Arduino TFT graphics library targeted at 32 bit 3 processors such as ESP32, ESP8266 and STM32. 4 5 This is a stand-alone library that contains the 6 hardware driver, the graphics functions and the 7 proportional fonts. 8 9 The larger fonts are Run Length Encoded to reduce their 10 size. 11 12 Created by Bodmer 2/12/16 13 Last update by Bodmer 20/03/20 14 ****************************************************/ 15 16 #include "TFT_eSPI.h" 17 18 #if defined (ESP32) 19 #if defined(CONFIG_IDF_TARGET_ESP32S3) 20 #include "Processors/TFT_eSPI_ESP32_S3.c" // Tested with SPI and 8 bit parallel 21 #elif defined(CONFIG_IDF_TARGET_ESP32C3) 22 #include "Processors/TFT_eSPI_ESP32_C3.c" // Tested with SPI (8 bit parallel will probably work too!) 23 #else 24 #include "Processors/TFT_eSPI_ESP32.c" 25 #endif 26 #elif defined (ARDUINO_ARCH_ESP8266) 27 #include "Processors/TFT_eSPI_ESP8266.c" 28 #elif defined (STM32) // (_VARIANT_ARDUINO_STM32_) stm32_def.h 29 #include "Processors/TFT_eSPI_STM32.c" 30 #elif defined (ARDUINO_ARCH_RP2040) || defined (ARDUINO_ARCH_MBED) // Raspberry Pi Pico 31 #include "Processors/TFT_eSPI_RP2040.c" 32 #else 33 #include "Processors/TFT_eSPI_Generic.c" 34 #endif 35 36 #ifndef SPI_BUSY_CHECK 37 #define SPI_BUSY_CHECK 38 #endif 39 40 // Clipping macro for pushImage 41 #define PI_CLIP \ 42 if (_vpOoB) return; \ 43 x+= _xDatum; \ 44 y+= _yDatum; \ 45 \ 46 if ((x >= _vpW) || (y >= _vpH)) return; \ 47 \ 48 int32_t dx = 0; \ 49 int32_t dy = 0; \ 50 int32_t dw = w; \ 51 int32_t dh = h; \ 52 \ 53 if (x < _vpX) { dx = _vpX - x; dw -= dx; x = _vpX; } \ 54 if (y < _vpY) { dy = _vpY - y; dh -= dy; y = _vpY; } \ 55 \ 56 if ((x + dw) > _vpW ) dw = _vpW - x; \ 57 if ((y + dh) > _vpH ) dh = _vpH - y; \ 58 \ 59 if (dw < 1 || dh < 1) return; 60 61 /*************************************************************************************** 62 ** Function name: Legacy - deprecated 63 ** Description: Start/end transaction 64 ***************************************************************************************/ 65 void TFT_eSPI::spi_begin() {begin_tft_write();} 66 void TFT_eSPI::spi_end() { end_tft_write();} 67 void TFT_eSPI::spi_begin_read() {begin_tft_read(); } 68 void TFT_eSPI::spi_end_read() { end_tft_read(); } 69 70 /*************************************************************************************** 71 ** Function name: begin_tft_write (was called spi_begin) 72 ** Description: Start SPI transaction for writes and select TFT 73 ***************************************************************************************/ 74 inline void TFT_eSPI::begin_tft_write(void){ 75 if (locked) { 76 locked = false; // Flag to show SPI access now unlocked 77 #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) 78 spi.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, TFT_SPI_MODE)); 79 #endif 80 CS_L; 81 SET_BUS_WRITE_MODE; // Some processors (e.g. ESP32) allow recycling the tx buffer when rx is not used 82 } 83 } 84 85 // Non-inlined version to permit override 86 void TFT_eSPI::begin_nin_write(void){ 87 if (locked) { 88 locked = false; // Flag to show SPI access now unlocked 89 #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) 90 spi.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, TFT_SPI_MODE)); 91 #endif 92 CS_L; 93 SET_BUS_WRITE_MODE; // Some processors (e.g. ESP32) allow recycling the tx buffer when rx is not used 94 } 95 } 96 97 /*************************************************************************************** 98 ** Function name: end_tft_write (was called spi_end) 99 ** Description: End transaction for write and deselect TFT 100 ***************************************************************************************/ 101 inline void TFT_eSPI::end_tft_write(void){ 102 if(!inTransaction) { // Flag to stop ending transaction during multiple graphics calls 103 if (!locked) { // Locked when beginTransaction has been called 104 locked = true; // Flag to show SPI access now locked 105 SPI_BUSY_CHECK; // Check send complete and clean out unused rx data 106 CS_H; 107 SET_BUS_READ_MODE; // In case bus has been configured for tx only 108 #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) 109 spi.endTransaction(); 110 #endif 111 } 112 } 113 } 114 115 // Non-inlined version to permit override 116 inline void TFT_eSPI::end_nin_write(void){ 117 if(!inTransaction) { // Flag to stop ending transaction during multiple graphics calls 118 if (!locked) { // Locked when beginTransaction has been called 119 locked = true; // Flag to show SPI access now locked 120 SPI_BUSY_CHECK; // Check send complete and clean out unused rx data 121 CS_H; 122 SET_BUS_READ_MODE; // In case SPI has been configured for tx only 123 #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) 124 spi.endTransaction(); 125 #endif 126 } 127 } 128 } 129 130 /*************************************************************************************** 131 ** Function name: begin_tft_read (was called spi_begin_read) 132 ** Description: Start transaction for reads and select TFT 133 ***************************************************************************************/ 134 // Reads require a lower SPI clock rate than writes 135 inline void TFT_eSPI::begin_tft_read(void){ 136 DMA_BUSY_CHECK; // Wait for any DMA transfer to complete before changing SPI settings 137 #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) 138 if (locked) { 139 locked = false; 140 spi.beginTransaction(SPISettings(SPI_READ_FREQUENCY, MSBFIRST, TFT_SPI_MODE)); 141 CS_L; 142 } 143 #else 144 #if !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) 145 spi.setFrequency(SPI_READ_FREQUENCY); 146 #endif 147 CS_L; 148 #endif 149 SET_BUS_READ_MODE; 150 } 151 152 /*************************************************************************************** 153 ** Function name: end_tft_read (was called spi_end_read) 154 ** Description: End transaction for reads and deselect TFT 155 ***************************************************************************************/ 156 inline void TFT_eSPI::end_tft_read(void){ 157 #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) 158 if(!inTransaction) { 159 if (!locked) { 160 locked = true; 161 CS_H; 162 spi.endTransaction(); 163 } 164 } 165 #else 166 #if !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) 167 spi.setFrequency(SPI_FREQUENCY); 168 #endif 169 if(!inTransaction) {CS_H;} 170 #endif 171 SET_BUS_WRITE_MODE; 172 } 173 174 /*************************************************************************************** 175 ** Function name: setViewport 176 ** Description: Set the clipping region for the TFT screen 177 ***************************************************************************************/ 178 void TFT_eSPI::setViewport(int32_t x, int32_t y, int32_t w, int32_t h, bool vpDatum) 179 { 180 // Viewport metrics (not clipped) 181 _xDatum = x; // Datum x position in screen coordinates 182 _yDatum = y; // Datum y position in screen coordinates 183 _xWidth = w; // Viewport width 184 _yHeight = h; // Viewport height 185 186 // Full size default viewport 187 _vpDatum = false; // Datum is at top left corner of screen (true = top left of viewport) 188 _vpOoB = false; // Out of Bounds flag (true is all of viewport is off screen) 189 _vpX = 0; // Viewport top left corner x coordinate 190 _vpY = 0; // Viewport top left corner y coordinate 191 _vpW = width(); // Equivalent of TFT width (Nb: viewport right edge coord + 1) 192 _vpH = height(); // Equivalent of TFT height (Nb: viewport bottom edge coord + 1) 193 194 // Clip viewport to screen area 195 if (x<0) { w += x; x = 0; } 196 if (y<0) { h += y; y = 0; } 197 if ((x + w) > width() ) { w = width() - x; } 198 if ((y + h) > height() ) { h = height() - y; } 199 200 //Serial.print(" x=");Serial.print( x);Serial.print(", y=");Serial.print( y); 201 //Serial.print(", w=");Serial.print(w);Serial.print(", h=");Serial.println(h); 202 203 // Check if viewport is entirely out of bounds 204 if (w < 1 || h < 1) 205 { 206 // Set default values and Out of Bounds flag in case of error 207 _xDatum = 0; 208 _yDatum = 0; 209 _xWidth = width(); 210 _yHeight = height(); 211 _vpOoB = true; // Set Out of Bounds flag to inhibit all drawing 212 return; 213 } 214 215 if (!vpDatum) 216 { 217 _xDatum = 0; // Reset to top left of screen if not using a viewport datum 218 _yDatum = 0; 219 _xWidth = width(); 220 _yHeight = height(); 221 } 222 223 // Store the clipped screen viewport metrics and datum position 224 _vpX = x; 225 _vpY = y; 226 _vpW = x + w; 227 _vpH = y + h; 228 _vpDatum = vpDatum; 229 230 //Serial.print(" _xDatum=");Serial.print( _xDatum);Serial.print(", _yDatum=");Serial.print( _yDatum); 231 //Serial.print(", _xWidth=");Serial.print(_xWidth);Serial.print(", _yHeight=");Serial.println(_yHeight); 232 233 //Serial.print(" _vpX=");Serial.print( _vpX);Serial.print(", _vpY=");Serial.print( _vpY); 234 //Serial.print(", _vpW=");Serial.print(_vpW);Serial.print(", _vpH=");Serial.println(_vpH); 235 } 236 237 /*************************************************************************************** 238 ** Function name: checkViewport 239 ** Description: Check if any part of specified area is visible in viewport 240 ***************************************************************************************/ 241 // Note: Setting w and h to 1 will check if coordinate x,y is in area 242 bool TFT_eSPI::checkViewport(int32_t x, int32_t y, int32_t w, int32_t h) 243 { 244 if (_vpOoB) return false; 245 x+= _xDatum; 246 y+= _yDatum; 247 248 if ((x >= _vpW) || (y >= _vpH)) return false; 249 250 int32_t dx = 0; 251 int32_t dy = 0; 252 int32_t dw = w; 253 int32_t dh = h; 254 255 if (x < _vpX) { dx = _vpX - x; dw -= dx; x = _vpX; } 256 if (y < _vpY) { dy = _vpY - y; dh -= dy; y = _vpY; } 257 258 if ((x + dw) > _vpW ) dw = _vpW - x; 259 if ((y + dh) > _vpH ) dh = _vpH - y; 260 261 if (dw < 1 || dh < 1) return false; 262 263 return true; 264 } 265 266 /*************************************************************************************** 267 ** Function name: resetViewport 268 ** Description: Reset viewport to whole TFT screen, datum at 0,0 269 ***************************************************************************************/ 270 void TFT_eSPI::resetViewport(void) 271 { 272 // Reset viewport to the whole screen (or sprite) area 273 _vpDatum = false; 274 _vpOoB = false; 275 _xDatum = 0; 276 _yDatum = 0; 277 _vpX = 0; 278 _vpY = 0; 279 _vpW = width(); 280 _vpH = height(); 281 _xWidth = width(); 282 _yHeight = height(); 283 } 284 285 /*************************************************************************************** 286 ** Function name: getViewportX 287 ** Description: Get x position of the viewport datum 288 ***************************************************************************************/ 289 int32_t TFT_eSPI::getViewportX(void) 290 { 291 return _xDatum; 292 } 293 294 /*************************************************************************************** 295 ** Function name: getViewportY 296 ** Description: Get y position of the viewport datum 297 ***************************************************************************************/ 298 int32_t TFT_eSPI::getViewportY(void) 299 { 300 return _yDatum; 301 } 302 303 /*************************************************************************************** 304 ** Function name: getViewportWidth 305 ** Description: Get width of the viewport 306 ***************************************************************************************/ 307 int32_t TFT_eSPI::getViewportWidth(void) 308 { 309 return _xWidth; 310 } 311 312 /*************************************************************************************** 313 ** Function name: getViewportHeight 314 ** Description: Get height of the viewport 315 ***************************************************************************************/ 316 int32_t TFT_eSPI::getViewportHeight(void) 317 { 318 return _yHeight; 319 } 320 321 /*************************************************************************************** 322 ** Function name: getViewportDatum 323 ** Description: Get datum flag of the viewport (true = viewport corner) 324 ***************************************************************************************/ 325 bool TFT_eSPI::getViewportDatum(void) 326 { 327 return _vpDatum; 328 } 329 330 /*************************************************************************************** 331 ** Function name: frameViewport 332 ** Description: Draw a frame inside or outside the viewport of width w 333 ***************************************************************************************/ 334 void TFT_eSPI::frameViewport(uint16_t color, int32_t w) 335 { 336 // Save datum position 337 bool _dT = _vpDatum; 338 339 // If w is positive the frame is drawn inside the viewport 340 // a large positive width will clear the screen inside the viewport 341 if (w>0) 342 { 343 // Set vpDatum true to simplify coordinate derivation 344 _vpDatum = true; 345 fillRect(0, 0, _vpW - _vpX, w, color); // Top 346 fillRect(0, w, w, _vpH - _vpY - w - w, color); // Left 347 fillRect(_xWidth - w, w, w, _yHeight - w - w, color); // Right 348 fillRect(0, _yHeight - w, _xWidth, w, color); // Bottom 349 } 350 else 351 // If w is negative the frame is drawn outside the viewport 352 // a large negative width will clear the screen outside the viewport 353 { 354 w = -w; 355 356 // Save old values 357 int32_t _xT = _vpX; _vpX = 0; 358 int32_t _yT = _vpY; _vpY = 0; 359 int32_t _wT = _vpW; 360 int32_t _hT = _vpH; 361 362 // Set vpDatum false so frame can be drawn outside window 363 _vpDatum = false; // When false the full width and height is accessed 364 _vpH = height(); 365 _vpW = width(); 366 367 // Draw frame 368 fillRect(_xT - w - _xDatum, _yT - w - _yDatum, _wT - _xT + w + w, w, color); // Top 369 fillRect(_xT - w - _xDatum, _yT - _yDatum, w, _hT - _yT, color); // Left 370 fillRect(_wT - _xDatum, _yT - _yDatum, w, _hT - _yT, color); // Right 371 fillRect(_xT - w - _xDatum, _hT - _yDatum, _wT - _xT + w + w, w, color); // Bottom 372 373 // Restore old values 374 _vpX = _xT; 375 _vpY = _yT; 376 _vpW = _wT; 377 _vpH = _hT; 378 } 379 380 // Restore vpDatum 381 _vpDatum = _dT; 382 } 383 384 /*************************************************************************************** 385 ** Function name: clipAddrWindow 386 ** Description: Clip address window x,y,w,h to screen and viewport 387 ***************************************************************************************/ 388 bool TFT_eSPI::clipAddrWindow(int32_t *x, int32_t *y, int32_t *w, int32_t *h) 389 { 390 if (_vpOoB) return false; // Area is outside of viewport 391 392 *x+= _xDatum; 393 *y+= _yDatum; 394 395 if ((*x >= _vpW) || (*y >= _vpH)) return false; // Area is outside of viewport 396 397 // Crop drawing area bounds 398 if (*x < _vpX) { *w -= _vpX - *x; *x = _vpX; } 399 if (*y < _vpY) { *h -= _vpY - *y; *y = _vpY; } 400 401 if ((*x + *w) > _vpW ) *w = _vpW - *x; 402 if ((*y + *h) > _vpH ) *h = _vpH - *y; 403 404 if (*w < 1 || *h < 1) return false; // No area is inside viewport 405 406 return true; // Area is wholly or partially inside viewport 407 } 408 409 /*************************************************************************************** 410 ** Function name: clipWindow 411 ** Description: Clip window xs,yx,xe,ye to screen and viewport 412 ***************************************************************************************/ 413 bool TFT_eSPI::clipWindow(int32_t *xs, int32_t *ys, int32_t *xe, int32_t *ye) 414 { 415 if (_vpOoB) return false; // Area is outside of viewport 416 417 *xs+= _xDatum; 418 *ys+= _yDatum; 419 *xe+= _xDatum; 420 *ye+= _yDatum; 421 422 if ((*xs >= _vpW) || (*ys >= _vpH)) return false; // Area is outside of viewport 423 if ((*xe < _vpX) || (*ye < _vpY)) return false; // Area is outside of viewport 424 425 // Crop drawing area bounds 426 if (*xs < _vpX) *xs = _vpX; 427 if (*ys < _vpY) *ys = _vpY; 428 429 if (*xe > _vpW) *xe = _vpW - 1; 430 if (*ye > _vpH) *ye = _vpH - 1; 431 432 return true; // Area is wholly or partially inside viewport 433 } 434 435 /*************************************************************************************** 436 ** Function name: TFT_eSPI 437 ** Description: Constructor , we must use hardware SPI pins 438 ***************************************************************************************/ 439 TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) 440 { 441 _init_width = _width = w; // Set by specific xxxxx_Defines.h file or by users sketch 442 _init_height = _height = h; // Set by specific xxxxx_Defines.h file or by users sketch 443 444 // Reset the viewport to the whole screen 445 resetViewport(); 446 447 rotation = 0; 448 cursor_y = cursor_x = last_cursor_x = bg_cursor_x = 0; 449 textfont = 1; 450 textsize = 1; 451 textcolor = bitmap_fg = 0xFFFF; // White 452 textbgcolor = bitmap_bg = 0x0000; // Black 453 padX = 0; // No padding 454 455 _fillbg = false; // Smooth font only at the moment, force text background fill 456 457 isDigits = false; // No bounding box adjustment 458 textwrapX = true; // Wrap text at end of line when using print stream 459 textwrapY = false; // Wrap text at bottom of screen when using print stream 460 textdatum = TL_DATUM; // Top Left text alignment is default 461 fontsloaded = 0; 462 463 _swapBytes = false; // Do not swap colour bytes by default 464 465 locked = true; // Transaction mutex lock flag to ensure begin/endTranaction pairing 466 inTransaction = false; // Flag to prevent multiple sequential functions to keep bus access open 467 lockTransaction = false; // start/endWrite lock flag to allow sketch to keep SPI bus access open 468 469 _booted = true; // Default attributes 470 _cp437 = true; // Legacy GLCD font bug fix 471 _utf8 = true; // UTF8 decoding enabled 472 473 #if defined (FONT_FS_AVAILABLE) && defined (SMOOTH_FONT) 474 fs_font = true; // Smooth font filing system or array (fs_font = false) flag 475 #endif 476 477 #if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) 478 if (psramFound()) _psram_enable = true; // Enable the use of PSRAM (if available) 479 else 480 #endif 481 _psram_enable = false; 482 483 addr_row = 0xFFFF; // drawPixel command length optimiser 484 addr_col = 0xFFFF; // drawPixel command length optimiser 485 486 _xPivot = 0; 487 _yPivot = 0; 488 489 // Legacy support for bit GPIO masks 490 cspinmask = 0; 491 dcpinmask = 0; 492 wrpinmask = 0; 493 sclkpinmask = 0; 494 495 // Flags for which fonts are loaded 496 #ifdef LOAD_GLCD 497 fontsloaded = 0x0002; // Bit 1 set 498 #endif 499 500 #ifdef LOAD_FONT2 501 fontsloaded |= 0x0004; // Bit 2 set 502 #endif 503 504 #ifdef LOAD_FONT4 505 fontsloaded |= 0x0010; // Bit 4 set 506 #endif 507 508 #ifdef LOAD_FONT6 509 fontsloaded |= 0x0040; // Bit 6 set 510 #endif 511 512 #ifdef LOAD_FONT7 513 fontsloaded |= 0x0080; // Bit 7 set 514 #endif 515 516 #ifdef LOAD_FONT8 517 fontsloaded |= 0x0100; // Bit 8 set 518 #endif 519 520 #ifdef LOAD_FONT8N 521 fontsloaded |= 0x0200; // Bit 9 set 522 #endif 523 524 #ifdef SMOOTH_FONT 525 fontsloaded |= 0x8000; // Bit 15 set 526 #endif 527 } 528 529 /*************************************************************************************** 530 ** Function name: initBus 531 ** Description: initialise the SPI or parallel bus 532 ***************************************************************************************/ 533 void TFT_eSPI::initBus(void) { 534 535 #ifdef TFT_CS 536 if (TFT_CS >= 0) { 537 pinMode(TFT_CS, OUTPUT); 538 digitalWrite(TFT_CS, HIGH); // Chip select high (inactive) 539 } 540 #endif 541 542 // Configure chip select for touchscreen controller if present 543 #ifdef TOUCH_CS 544 if (TOUCH_CS >= 0) { 545 pinMode(TOUCH_CS, OUTPUT); 546 digitalWrite(TOUCH_CS, HIGH); // Chip select high (inactive) 547 } 548 #endif 549 550 // In parallel mode and with the RP2040 processor, the TFT_WR line is handled in the PIO 551 #if defined (TFT_WR) && !defined (ARDUINO_ARCH_RP2040) && !defined (ARDUINO_ARCH_MBED) 552 if (TFT_WR >= 0) { 553 pinMode(TFT_WR, OUTPUT); 554 digitalWrite(TFT_WR, HIGH); // Set write strobe high (inactive) 555 } 556 #endif 557 558 #ifdef TFT_DC 559 if (TFT_DC >= 0) { 560 pinMode(TFT_DC, OUTPUT); 561 digitalWrite(TFT_DC, HIGH); // Data/Command high = data mode 562 } 563 #endif 564 565 #ifdef TFT_RST 566 if (TFT_RST >= 0) { 567 pinMode(TFT_RST, OUTPUT); 568 digitalWrite(TFT_RST, HIGH); // Set high, do not share pin with another SPI device 569 } 570 #endif 571 572 #if defined (TFT_PARALLEL_8_BIT) 573 574 // Make sure read is high before we set the bus to output 575 if (TFT_RD >= 0) { 576 pinMode(TFT_RD, OUTPUT); 577 digitalWrite(TFT_RD, HIGH); 578 } 579 580 #if !defined (ARDUINO_ARCH_RP2040) && !defined (ARDUINO_ARCH_MBED)// PIO manages pins 581 // Set TFT data bus lines to output 582 pinMode(TFT_D0, OUTPUT); digitalWrite(TFT_D0, HIGH); 583 pinMode(TFT_D1, OUTPUT); digitalWrite(TFT_D1, HIGH); 584 pinMode(TFT_D2, OUTPUT); digitalWrite(TFT_D2, HIGH); 585 pinMode(TFT_D3, OUTPUT); digitalWrite(TFT_D3, HIGH); 586 pinMode(TFT_D4, OUTPUT); digitalWrite(TFT_D4, HIGH); 587 pinMode(TFT_D5, OUTPUT); digitalWrite(TFT_D5, HIGH); 588 pinMode(TFT_D6, OUTPUT); digitalWrite(TFT_D6, HIGH); 589 pinMode(TFT_D7, OUTPUT); digitalWrite(TFT_D7, HIGH); 590 #endif 591 592 PARALLEL_INIT_TFT_DATA_BUS; 593 594 #endif 595 } 596 597 /*************************************************************************************** 598 ** Function name: begin 599 ** Description: Included for backwards compatibility 600 ***************************************************************************************/ 601 void TFT_eSPI::begin(uint8_t tc) 602 { 603 init(tc); 604 } 605 606 607 /*************************************************************************************** 608 ** Function name: init (tc is tab colour for ST7735 displays only) 609 ** Description: Reset, then initialise the TFT display registers 610 ***************************************************************************************/ 611 void TFT_eSPI::init(uint8_t tc) 612 { 613 if (_booted) 614 { 615 initBus(); 616 617 #if !defined (ESP32) && !defined(TFT_PARALLEL_8_BIT) && !defined(ARDUINO_ARCH_RP2040) && !defined (ARDUINO_ARCH_MBED) 618 // Legacy bitmasks for GPIO 619 #if defined (TFT_CS) && (TFT_CS >= 0) 620 cspinmask = (uint32_t) digitalPinToBitMask(TFT_CS); 621 #endif 622 623 #if defined (TFT_DC) && (TFT_DC >= 0) 624 dcpinmask = (uint32_t) digitalPinToBitMask(TFT_DC); 625 #endif 626 627 #if defined (TFT_WR) && (TFT_WR >= 0) 628 wrpinmask = (uint32_t) digitalPinToBitMask(TFT_WR); 629 #endif 630 631 #if defined (TFT_SCLK) && (TFT_SCLK >= 0) 632 sclkpinmask = (uint32_t) digitalPinToBitMask(TFT_SCLK); 633 #endif 634 635 #if defined (TFT_SPI_OVERLAP) && defined (ARDUINO_ARCH_ESP8266) 636 // Overlap mode SD0=MISO, SD1=MOSI, CLK=SCLK must use D3 as CS 637 // pins(int8_t sck, int8_t miso, int8_t mosi, int8_t ss); 638 //spi.pins( 6, 7, 8, 0); 639 spi.pins(6, 7, 8, 0); 640 #endif 641 642 spi.begin(); // This will set HMISO to input 643 644 #else 645 #if !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) 646 #if defined (TFT_MOSI) && !defined (TFT_SPI_OVERLAP) && !defined(ARDUINO_ARCH_RP2040) && !defined (ARDUINO_ARCH_MBED) 647 spi.begin(TFT_SCLK, TFT_MISO, TFT_MOSI, -1); // This will set MISO to input 648 #else 649 spi.begin(); // This will set MISO to input 650 #endif 651 #endif 652 #endif 653 lockTransaction = false; 654 inTransaction = false; 655 locked = true; 656 657 INIT_TFT_DATA_BUS; 658 659 660 #if defined (TFT_CS) && !defined(RP2040_PIO_INTERFACE) 661 // Set to output once again in case MISO is used for CS 662 if (TFT_CS >= 0) { 663 pinMode(TFT_CS, OUTPUT); 664 digitalWrite(TFT_CS, HIGH); // Chip select high (inactive) 665 } 666 #elif defined (ARDUINO_ARCH_ESP8266) && !defined (TFT_PARALLEL_8_BIT) && !defined (RP2040_PIO_SPI) 667 spi.setHwCs(1); // Use hardware SS toggling 668 #endif 669 670 671 // Set to output once again in case MISO is used for DC 672 #if defined (TFT_DC) && !defined(RP2040_PIO_INTERFACE) 673 if (TFT_DC >= 0) { 674 pinMode(TFT_DC, OUTPUT); 675 digitalWrite(TFT_DC, HIGH); // Data/Command high = data mode 676 } 677 #endif 678 679 _booted = false; 680 end_tft_write(); 681 } // end of: if just _booted 682 683 // Toggle RST low to reset 684 #ifdef TFT_RST 685 #if !defined(RP2040_PIO_INTERFACE) 686 // Set to output once again in case MISO is used for TFT_RST 687 if (TFT_RST >= 0) { 688 pinMode(TFT_RST, OUTPUT); 689 } 690 #endif 691 if (TFT_RST >= 0) { 692 writecommand(0x00); // Put SPI bus in known state for TFT with CS tied low 693 digitalWrite(TFT_RST, HIGH); 694 delay(5); 695 digitalWrite(TFT_RST, LOW); 696 delay(20); 697 digitalWrite(TFT_RST, HIGH); 698 } 699 else writecommand(TFT_SWRST); // Software reset 700 #else 701 writecommand(TFT_SWRST); // Software reset 702 #endif 703 704 delay(150); // Wait for reset to complete 705 706 begin_tft_write(); 707 708 tc = tc; // Suppress warning 709 710 // This loads the driver specific initialisation code <<<<<<<<<<<<<<<<<<<<< ADD NEW DRIVERS TO THE LIST HERE <<<<<<<<<<<<<<<<<<<<<<< 711 #if defined (ILI9341_DRIVER) || defined(ILI9341_2_DRIVER) || defined (ILI9342_DRIVER) 712 #include "TFT_Drivers/ILI9341_Init.h" 713 714 #elif defined (ST7735_DRIVER) 715 tabcolor = tc; 716 #include "TFT_Drivers/ST7735_Init.h" 717 718 #elif defined (ILI9163_DRIVER) 719 #include "TFT_Drivers/ILI9163_Init.h" 720 721 #elif defined (S6D02A1_DRIVER) 722 #include "TFT_Drivers/S6D02A1_Init.h" 723 724 #elif defined (ST7796_DRIVER) 725 #include "TFT_Drivers/ST7796_Init.h" 726 727 #elif defined (ILI9486_DRIVER) 728 #include "TFT_Drivers/ILI9486_Init.h" 729 730 #elif defined (ILI9481_DRIVER) 731 #include "TFT_Drivers/ILI9481_Init.h" 732 733 #elif defined (ILI9488_DRIVER) 734 #include "TFT_Drivers/ILI9488_Init.h" 735 736 #elif defined (HX8357D_DRIVER) 737 #include "TFT_Drivers/HX8357D_Init.h" 738 739 #elif defined (ST7789_DRIVER) 740 #include "TFT_Drivers/ST7789_Init.h" 741 742 #elif defined (R61581_DRIVER) 743 #include "TFT_Drivers/R61581_Init.h" 744 745 #elif defined (RM68140_DRIVER) 746 #include "TFT_Drivers/RM68140_Init.h" 747 748 #elif defined (ST7789_2_DRIVER) 749 #include "TFT_Drivers/ST7789_2_Init.h" 750 751 #elif defined (SSD1351_DRIVER) 752 #include "TFT_Drivers/SSD1351_Init.h" 753 754 #elif defined (SSD1963_DRIVER) 755 #include "TFT_Drivers/SSD1963_Init.h" 756 757 #elif defined (GC9A01_DRIVER) 758 #include "TFT_Drivers/GC9A01_Init.h" 759 760 #elif defined (ILI9225_DRIVER) 761 #include "TFT_Drivers/ILI9225_Init.h" 762 763 #elif defined (RM68120_DRIVER) 764 #include "TFT_Drivers/RM68120_Init.h" 765 766 #elif defined (HX8357B_DRIVER) 767 #include "TFT_Drivers/HX8357B_Init.h" 768 769 #elif defined (HX8357C_DRIVER) 770 #include "TFT_Drivers/HX8357C_Init.h" 771 772 #endif 773 774 #ifdef TFT_INVERSION_ON 775 writecommand(TFT_INVON); 776 #endif 777 778 #ifdef TFT_INVERSION_OFF 779 writecommand(TFT_INVOFF); 780 #endif 781 782 end_tft_write(); 783 784 setRotation(rotation); 785 786 #if defined (TFT_BL) && defined (TFT_BACKLIGHT_ON) 787 if (TFT_BL >= 0) { 788 pinMode(TFT_BL, OUTPUT); 789 digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); 790 } 791 #else 792 #if defined (TFT_BL) && defined (M5STACK) 793 // Turn on the back-light LED 794 if (TFT_BL >= 0) { 795 pinMode(TFT_BL, OUTPUT); 796 digitalWrite(TFT_BL, HIGH); 797 } 798 #endif 799 #endif 800 } 801 802 803 /*************************************************************************************** 804 ** Function name: setRotation 805 ** Description: rotate the screen orientation m = 0-3 or 4-7 for BMP drawing 806 ***************************************************************************************/ 807 void TFT_eSPI::setRotation(uint8_t m) 808 { 809 810 begin_tft_write(); 811 812 // This loads the driver specific rotation code <<<<<<<<<<<<<<<<<<<<< ADD NEW DRIVERS TO THE LIST HERE <<<<<<<<<<<<<<<<<<<<<<< 813 #if defined (ILI9341_DRIVER) || defined(ILI9341_2_DRIVER) || defined (ILI9342_DRIVER) 814 #include "TFT_Drivers/ILI9341_Rotation.h" 815 816 #elif defined (ST7735_DRIVER) 817 #include "TFT_Drivers/ST7735_Rotation.h" 818 819 #elif defined (ILI9163_DRIVER) 820 #include "TFT_Drivers/ILI9163_Rotation.h" 821 822 #elif defined (S6D02A1_DRIVER) 823 #include "TFT_Drivers/S6D02A1_Rotation.h" 824 825 #elif defined (ST7796_DRIVER) 826 #include "TFT_Drivers/ST7796_Rotation.h" 827 828 #elif defined (ILI9486_DRIVER) 829 #include "TFT_Drivers/ILI9486_Rotation.h" 830 831 #elif defined (ILI9481_DRIVER) 832 #include "TFT_Drivers/ILI9481_Rotation.h" 833 834 #elif defined (ILI9488_DRIVER) 835 #include "TFT_Drivers/ILI9488_Rotation.h" 836 837 #elif defined (HX8357D_DRIVER) 838 #include "TFT_Drivers/HX8357D_Rotation.h" 839 840 #elif defined (ST7789_DRIVER) 841 #include "TFT_Drivers/ST7789_Rotation.h" 842 843 #elif defined (R61581_DRIVER) 844 #include "TFT_Drivers/R61581_Rotation.h" 845 846 #elif defined (RM68140_DRIVER) 847 #include "TFT_Drivers/RM68140_Rotation.h" 848 849 #elif defined (ST7789_2_DRIVER) 850 #include "TFT_Drivers/ST7789_2_Rotation.h" 851 852 #elif defined (SSD1351_DRIVER) 853 #include "TFT_Drivers/SSD1351_Rotation.h" 854 855 #elif defined (SSD1963_DRIVER) 856 #include "TFT_Drivers/SSD1963_Rotation.h" 857 858 #elif defined (GC9A01_DRIVER) 859 #include "TFT_Drivers/GC9A01_Rotation.h" 860 861 #elif defined (ILI9225_DRIVER) 862 #include "TFT_Drivers/ILI9225_Rotation.h" 863 864 #elif defined (RM68120_DRIVER) 865 #include "TFT_Drivers/RM68120_Rotation.h" 866 867 #elif defined (HX8357B_DRIVER) 868 #include "TFT_Drivers/HX8357B_Rotation.h" 869 870 #elif defined (HX8357C_DRIVER) 871 #include "TFT_Drivers/HX8357C_Rotation.h" 872 873 #endif 874 875 delayMicroseconds(10); 876 877 end_tft_write(); 878 879 addr_row = 0xFFFF; 880 addr_col = 0xFFFF; 881 882 // Reset the viewport to the whole screen 883 resetViewport(); 884 } 885 886 887 /*************************************************************************************** 888 ** Function name: getRotation 889 ** Description: Return the rotation value (as used by setRotation()) 890 ***************************************************************************************/ 891 uint8_t TFT_eSPI::getRotation(void) 892 { 893 return rotation; 894 } 895 896 897 /*************************************************************************************** 898 ** Function name: setOrigin 899 ** Description: Set graphics origin to position x,y wrt to top left corner 900 ***************************************************************************************/ 901 //Note: setRotation, setViewport and resetViewport will revert origin to top left 902 void TFT_eSPI::setOrigin(int32_t x, int32_t y) 903 { 904 _xDatum = x; 905 _yDatum = y; 906 } 907 908 909 /*************************************************************************************** 910 ** Function name: getOriginX 911 ** Description: Set graphics origin to position x 912 ***************************************************************************************/ 913 int32_t TFT_eSPI::getOriginX(void) 914 { 915 return _xDatum; 916 } 917 918 919 /*************************************************************************************** 920 ** Function name: getOriginY 921 ** Description: Set graphics origin to position y 922 ***************************************************************************************/ 923 int32_t TFT_eSPI::getOriginY(void) 924 { 925 return _yDatum; 926 } 927 928 929 /*************************************************************************************** 930 ** Function name: commandList, used for FLASH based lists only (e.g. ST7735) 931 ** Description: Get initialisation commands from FLASH and send to TFT 932 ***************************************************************************************/ 933 void TFT_eSPI::commandList (const uint8_t *addr) 934 { 935 uint8_t numCommands; 936 uint8_t numArgs; 937 uint8_t ms; 938 939 numCommands = pgm_read_byte(addr++); // Number of commands to follow 940 941 while (numCommands--) // For each command... 942 { 943 writecommand(pgm_read_byte(addr++)); // Read, issue command 944 numArgs = pgm_read_byte(addr++); // Number of args to follow 945 ms = numArgs & TFT_INIT_DELAY; // If hibit set, delay follows args 946 numArgs &= ~TFT_INIT_DELAY; // Mask out delay bit 947 948 while (numArgs--) // For each argument... 949 { 950 writedata(pgm_read_byte(addr++)); // Read, issue argument 951 } 952 953 if (ms) 954 { 955 ms = pgm_read_byte(addr++); // Read post-command delay time (ms) 956 delay( (ms==255 ? 500 : ms) ); 957 } 958 } 959 960 } 961 962 963 /*************************************************************************************** 964 ** Function name: spiwrite 965 ** Description: Write 8 bits to SPI port (legacy support only) 966 ***************************************************************************************/ 967 void TFT_eSPI::spiwrite(uint8_t c) 968 { 969 begin_tft_write(); 970 tft_Write_8(c); 971 end_tft_write(); 972 } 973 974 975 /*************************************************************************************** 976 ** Function name: writecommand 977 ** Description: Send an 8 bit command to the TFT 978 ***************************************************************************************/ 979 #ifndef RM68120_DRIVER 980 void TFT_eSPI::writecommand(uint8_t c) 981 { 982 begin_tft_write(); 983 984 DC_C; 985 986 tft_Write_8(c); 987 988 DC_D; 989 990 end_tft_write(); 991 } 992 #else 993 void TFT_eSPI::writecommand(uint16_t c) 994 { 995 begin_tft_write(); 996 997 DC_C; 998 999 tft_Write_16(c); 1000 1001 DC_D; 1002 1003 end_tft_write(); 1004 1005 } 1006 void TFT_eSPI::writeRegister8(uint16_t c, uint8_t d) 1007 { 1008 begin_tft_write(); 1009 1010 DC_C; 1011 1012 tft_Write_16(c); 1013 1014 DC_D; 1015 1016 tft_Write_8(d); 1017 1018 end_tft_write(); 1019 1020 } 1021 void TFT_eSPI::writeRegister16(uint16_t c, uint16_t d) 1022 { 1023 begin_tft_write(); 1024 1025 DC_C; 1026 1027 tft_Write_16(c); 1028 1029 DC_D; 1030 1031 tft_Write_16(d); 1032 1033 end_tft_write(); 1034 1035 } 1036 1037 #endif 1038 1039 /*************************************************************************************** 1040 ** Function name: writedata 1041 ** Description: Send a 8 bit data value to the TFT 1042 ***************************************************************************************/ 1043 void TFT_eSPI::writedata(uint8_t d) 1044 { 1045 begin_tft_write(); 1046 1047 DC_D; // Play safe, but should already be in data mode 1048 1049 tft_Write_8(d); 1050 1051 CS_L; // Allow more hold time for low VDI rail 1052 1053 end_tft_write(); 1054 } 1055 1056 1057 /*************************************************************************************** 1058 ** Function name: readcommand8 1059 ** Description: Read a 8 bit data value from an indexed command register 1060 ***************************************************************************************/ 1061 uint8_t TFT_eSPI::readcommand8(uint8_t cmd_function, uint8_t index) 1062 { 1063 uint8_t reg = 0; 1064 #if defined(TFT_PARALLEL_8_BIT) || defined(RP2040_PIO_INTERFACE) 1065 1066 writecommand(cmd_function); // Sets DC and CS high 1067 1068 busDir(GPIO_DIR_MASK, INPUT); 1069 1070 CS_L; 1071 1072 // Read nth parameter (assumes caller discards 1st parameter or points index to 2nd) 1073 while(index--) reg = readByte(); 1074 1075 busDir(GPIO_DIR_MASK, OUTPUT); 1076 1077 CS_H; 1078 1079 #else // SPI interface 1080 // Tested with ILI9341 set to Interface II i.e. IM [3:0] = "1101" 1081 begin_tft_read(); 1082 index = 0x10 + (index & 0x0F); 1083 1084 DC_C; tft_Write_8(0xD9); 1085 DC_D; tft_Write_8(index); 1086 1087 CS_H; // Some displays seem to need CS to be pulsed here, or is just a delay needed? 1088 CS_L; 1089 1090 DC_C; tft_Write_8(cmd_function); 1091 DC_D; 1092 reg = tft_Read_8(); 1093 1094 end_tft_read(); 1095 #endif 1096 return reg; 1097 } 1098 1099 1100 /*************************************************************************************** 1101 ** Function name: readcommand16 1102 ** Description: Read a 16 bit data value from an indexed command register 1103 ***************************************************************************************/ 1104 uint16_t TFT_eSPI::readcommand16(uint8_t cmd_function, uint8_t index) 1105 { 1106 uint32_t reg; 1107 1108 reg = (readcommand8(cmd_function, index + 0) << 8); 1109 reg |= (readcommand8(cmd_function, index + 1) << 0); 1110 1111 return reg; 1112 } 1113 1114 1115 /*************************************************************************************** 1116 ** Function name: readcommand32 1117 ** Description: Read a 32 bit data value from an indexed command register 1118 ***************************************************************************************/ 1119 uint32_t TFT_eSPI::readcommand32(uint8_t cmd_function, uint8_t index) 1120 { 1121 uint32_t reg; 1122 1123 reg = ((uint32_t)readcommand8(cmd_function, index + 0) << 24); 1124 reg |= ((uint32_t)readcommand8(cmd_function, index + 1) << 16); 1125 reg |= ((uint32_t)readcommand8(cmd_function, index + 2) << 8); 1126 reg |= ((uint32_t)readcommand8(cmd_function, index + 3) << 0); 1127 1128 return reg; 1129 } 1130 1131 1132 /*************************************************************************************** 1133 ** Function name: read pixel (for SPI Interface II i.e. IM [3:0] = "1101") 1134 ** Description: Read 565 pixel colours from a pixel 1135 ***************************************************************************************/ 1136 uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) 1137 { 1138 if (_vpOoB) return 0; 1139 1140 x0+= _xDatum; 1141 y0+= _yDatum; 1142 1143 // Range checking 1144 if ((x0 < _vpX) || (y0 < _vpY) ||(x0 >= _vpW) || (y0 >= _vpH)) return 0; 1145 1146 #if defined(TFT_PARALLEL_8_BIT) || defined(RP2040_PIO_INTERFACE) 1147 1148 if (!inTransaction) { CS_L; } // CS_L can be multi-statement 1149 1150 readAddrWindow(x0, y0, 1, 1); 1151 1152 // Set masked pins D0- D7 to input 1153 busDir(GPIO_DIR_MASK, INPUT); 1154 1155 #if !defined (SSD1963_DRIVER) 1156 // Dummy read to throw away don't care value 1157 readByte(); 1158 #endif 1159 1160 // Fetch the 16 bit BRG pixel 1161 //uint16_t rgb = (readByte() << 8) | readByte(); 1162 1163 #if defined (ILI9341_DRIVER) || defined(ILI9341_2_DRIVER) || defined (ILI9488_DRIVER) || defined (SSD1963_DRIVER)// Read 3 bytes 1164 1165 // Read window pixel 24 bit RGB values and fill in LS bits 1166 uint16_t rgb = ((readByte() & 0xF8) << 8) | ((readByte() & 0xFC) << 3) | (readByte() >> 3); 1167 1168 if (!inTransaction) { CS_H; } // CS_H can be multi-statement 1169 1170 // Set masked pins D0- D7 to output 1171 busDir(GPIO_DIR_MASK, OUTPUT); 1172 1173 return rgb; 1174 1175 #else // ILI9481 or ILI9486 16 bit read 1176 1177 // Fetch the 16 bit BRG pixel 1178 uint16_t bgr = (readByte() << 8) | readByte(); 1179 1180 if (!inTransaction) { CS_H; } // CS_H can be multi-statement 1181 1182 // Set masked pins D0- D7 to output 1183 busDir(GPIO_DIR_MASK, OUTPUT); 1184 1185 #if defined (ILI9486_DRIVER) || defined (ST7796_DRIVER) 1186 return bgr; 1187 #else 1188 // Swap Red and Blue (could check MADCTL setting to see if this is needed) 1189 return (bgr>>11) | (bgr<<11) | (bgr & 0x7E0); 1190 #endif 1191 1192 #endif 1193 1194 #else // Not TFT_PARALLEL_8_BIT 1195 1196 // This function can get called during anti-aliased font rendering 1197 // so a transaction may be in progress 1198 bool wasInTransaction = inTransaction; 1199 if (inTransaction) { inTransaction= false; end_tft_write();} 1200 1201 uint16_t color = 0; 1202 1203 begin_tft_read(); // Sets CS low 1204 1205 readAddrWindow(x0, y0, 1, 1); 1206 1207 #ifdef TFT_SDA_READ 1208 begin_SDA_Read(); 1209 #endif 1210 1211 // Dummy read to throw away don't care value 1212 tft_Read_8(); 1213 1214 //#if !defined (ILI9488_DRIVER) 1215 1216 #if defined (ST7796_DRIVER) 1217 // Read the 2 bytes 1218 color = ((tft_Read_8()) << 8) | (tft_Read_8()); 1219 #elif defined (ST7735_DRIVER) 1220 // Read the 3 RGB bytes, colour is in LS 6 bits of the top 7 bits of each byte 1221 // as the TFT stores colours as 18 bits 1222 uint8_t r = tft_Read_8()<<1; 1223 uint8_t g = tft_Read_8()<<1; 1224 uint8_t b = tft_Read_8()<<1; 1225 color = color565(r, g, b); 1226 #else 1227 // Read the 3 RGB bytes, colour is actually only in the top 6 bits of each byte 1228 // as the TFT stores colours as 18 bits 1229 uint8_t r = tft_Read_8(); 1230 uint8_t g = tft_Read_8(); 1231 uint8_t b = tft_Read_8(); 1232 color = color565(r, g, b); 1233 #endif 1234 1235 /* 1236 #else 1237 1238 // The 6 colour bits are in MS 6 bits of each byte, but the ILI9488 needs an extra clock pulse 1239 // so bits appear shifted right 1 bit, so mask the middle 6 bits then shift 1 place left 1240 uint8_t r = (tft_Read_8()&0x7E)<<1; 1241 uint8_t g = (tft_Read_8()&0x7E)<<1; 1242 uint8_t b = (tft_Read_8()&0x7E)<<1; 1243 color = color565(r, g, b); 1244 1245 #endif 1246 */ 1247 CS_H; 1248 1249 #ifdef TFT_SDA_READ 1250 end_SDA_Read(); 1251 #endif 1252 1253 end_tft_read(); 1254 1255 // Reinstate the transaction if one was in progress 1256 if(wasInTransaction) { begin_tft_write(); inTransaction = true; } 1257 1258 return color; 1259 1260 #endif 1261 } 1262 1263 void TFT_eSPI::setCallback(getColorCallback getCol) 1264 { 1265 getColor = getCol; 1266 } 1267 1268 1269 /*************************************************************************************** 1270 ** Function name: read rectangle (for SPI Interface II i.e. IM [3:0] = "1101") 1271 ** Description: Read 565 pixel colours from a defined area 1272 ***************************************************************************************/ 1273 void TFT_eSPI::readRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data) 1274 { 1275 PI_CLIP ; 1276 1277 #if defined(TFT_PARALLEL_8_BIT) || defined(RP2040_PIO_INTERFACE) 1278 1279 CS_L; 1280 1281 readAddrWindow(x, y, dw, dh); 1282 1283 data += dx + dy * w; 1284 1285 // Set masked pins D0- D7 to input 1286 busDir(GPIO_DIR_MASK, INPUT); 1287 1288 #if defined (ILI9341_DRIVER) || defined(ILI9341_2_DRIVER) || defined (ILI9488_DRIVER) // Read 3 bytes 1289 // Dummy read to throw away don't care value 1290 readByte(); 1291 1292 // Fetch the 24 bit RGB value 1293 while (dh--) { 1294 int32_t lw = dw; 1295 uint16_t* line = data; 1296 while (lw--) { 1297 // Assemble the RGB 16 bit colour 1298 uint16_t rgb = ((readByte() & 0xF8) << 8) | ((readByte() & 0xFC) << 3) | (readByte() >> 3); 1299 1300 // Swapped byte order for compatibility with pushRect() 1301 *line++ = (rgb<<8) | (rgb>>8); 1302 } 1303 data += w; 1304 } 1305 1306 #elif defined (SSD1963_DRIVER) 1307 // Fetch the 18 bit BRG pixels 1308 while (dh--) { 1309 int32_t lw = dw; 1310 uint16_t* line = data; 1311 while (lw--) { 1312 uint16_t bgr = ((readByte() & 0xF8) >> 3);; // CS_L adds a small delay 1313 bgr |= ((readByte() & 0xFC) << 3); 1314 bgr |= (readByte() << 8); 1315 // Swap Red and Blue (could check MADCTL setting to see if this is needed) 1316 uint16_t rgb = (bgr>>11) | (bgr<<11) | (bgr & 0x7E0); 1317 // Swapped byte order for compatibility with pushRect() 1318 *line++ = (rgb<<8) | (rgb>>8); 1319 } 1320 data += w; 1321 } 1322 1323 #else // ILI9481 reads as 16 bits 1324 // Dummy read to throw away don't care value 1325 readByte(); 1326 1327 // Fetch the 16 bit BRG pixels 1328 while (dh--) { 1329 int32_t lw = dw; 1330 uint16_t* line = data; 1331 while (lw--) { 1332 #if defined (ILI9486_DRIVER) || defined (ST7796_DRIVER) 1333 // Read the RGB 16 bit colour 1334 *line++ = readByte() | (readByte() << 8); 1335 #else 1336 // Read the BRG 16 bit colour 1337 uint16_t bgr = (readByte() << 8) | readByte(); 1338 // Swap Red and Blue (could check MADCTL setting to see if this is needed) 1339 uint16_t rgb = (bgr>>11) | (bgr<<11) | (bgr & 0x7E0); 1340 // Swapped byte order for compatibility with pushRect() 1341 *line++ = (rgb<<8) | (rgb>>8); 1342 #endif 1343 } 1344 data += w; 1345 } 1346 #endif 1347 1348 CS_H; 1349 1350 // Set masked pins D0- D7 to output 1351 busDir(GPIO_DIR_MASK, OUTPUT); 1352 1353 #else // SPI interface 1354 1355 // This function can get called after a begin_tft_write 1356 // so a transaction may be in progress 1357 bool wasInTransaction = inTransaction; 1358 if (inTransaction) { inTransaction= false; end_tft_write();} 1359 1360 uint16_t color = 0; 1361 1362 begin_tft_read(); 1363 1364 readAddrWindow(x, y, dw, dh); 1365 1366 data += dx + dy * w; 1367 1368 #ifdef TFT_SDA_READ 1369 begin_SDA_Read(); 1370 #endif 1371 1372 // Dummy read to throw away don't care value 1373 tft_Read_8(); 1374 1375 // Read window pixel 24 bit RGB values 1376 while (dh--) { 1377 int32_t lw = dw; 1378 uint16_t* line = data; 1379 while (lw--) { 1380 1381 #if !defined (ILI9488_DRIVER) 1382 1383 #if defined (ST7796_DRIVER) 1384 // Read the 2 bytes 1385 color = ((tft_Read_8()) << 8) | (tft_Read_8()); 1386 #elif defined (ST7735_DRIVER) 1387 // Read the 3 RGB bytes, colour is in LS 6 bits of the top 7 bits of each byte 1388 // as the TFT stores colours as 18 bits 1389 uint8_t r = tft_Read_8()<<1; 1390 uint8_t g = tft_Read_8()<<1; 1391 uint8_t b = tft_Read_8()<<1; 1392 color = color565(r, g, b); 1393 #else 1394 // Read the 3 RGB bytes, colour is actually only in the top 6 bits of each byte 1395 // as the TFT stores colours as 18 bits 1396 uint8_t r = tft_Read_8(); 1397 uint8_t g = tft_Read_8(); 1398 uint8_t b = tft_Read_8(); 1399 color = color565(r, g, b); 1400 #endif 1401 1402 #else 1403 1404 // The 6 colour bits are in MS 6 bits of each byte but we do not include the extra clock pulse 1405 // so we use a trick and mask the middle 6 bits of the byte, then only shift 1 place left 1406 uint8_t r = (tft_Read_8()&0x7E)<<1; 1407 uint8_t g = (tft_Read_8()&0x7E)<<1; 1408 uint8_t b = (tft_Read_8()&0x7E)<<1; 1409 color = color565(r, g, b); 1410 #endif 1411 1412 // Swapped colour byte order for compatibility with pushRect() 1413 *line++ = color << 8 | color >> 8; 1414 } 1415 data += w; 1416 } 1417 1418 //CS_H; 1419 1420 #ifdef TFT_SDA_READ 1421 end_SDA_Read(); 1422 #endif 1423 1424 end_tft_read(); 1425 1426 // Reinstate the transaction if one was in progress 1427 if(wasInTransaction) { begin_tft_write(); inTransaction = true; } 1428 #endif 1429 } 1430 1431 1432 /*************************************************************************************** 1433 ** Function name: push rectangle 1434 ** Description: push 565 pixel colours into a defined area 1435 ***************************************************************************************/ 1436 void TFT_eSPI::pushRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data) 1437 { 1438 bool swap = _swapBytes; _swapBytes = false; 1439 pushImage(x, y, w, h, data); 1440 _swapBytes = swap; 1441 } 1442 1443 1444 /*************************************************************************************** 1445 ** Function name: pushImage 1446 ** Description: plot 16 bit colour sprite or image onto TFT 1447 ***************************************************************************************/ 1448 void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data) 1449 { 1450 PI_CLIP; 1451 1452 begin_tft_write(); 1453 inTransaction = true; 1454 1455 setWindow(x, y, x + dw - 1, y + dh - 1); 1456 1457 data += dx + dy * w; 1458 1459 // Check if whole image can be pushed 1460 if (dw == w) pushPixels(data, dw * dh); 1461 else { 1462 // Push line segments to crop image 1463 while (dh--) 1464 { 1465 pushPixels(data, dw); 1466 data += w; 1467 } 1468 } 1469 1470 inTransaction = lockTransaction; 1471 end_tft_write(); 1472 } 1473 1474 /*************************************************************************************** 1475 ** Function name: pushImage 1476 ** Description: plot 16 bit sprite or image with 1 colour being transparent 1477 ***************************************************************************************/ 1478 void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data, uint16_t transp) 1479 { 1480 PI_CLIP; 1481 1482 begin_tft_write(); 1483 inTransaction = true; 1484 1485 data += dx + dy * w; 1486 1487 1488 uint16_t lineBuf[dw]; // Use buffer to minimise setWindow call count 1489 1490 // The little endian transp color must be byte swapped if the image is big endian 1491 if (!_swapBytes) transp = transp >> 8 | transp << 8; 1492 1493 while (dh--) 1494 { 1495 int32_t len = dw; 1496 uint16_t* ptr = data; 1497 int32_t px = x, sx = x; 1498 bool move = true; 1499 uint16_t np = 0; 1500 1501 while (len--) 1502 { 1503 if (transp != *ptr) 1504 { 1505 if (move) { move = false; sx = px; } 1506 lineBuf[np] = *ptr; 1507 np++; 1508 } 1509 else 1510 { 1511 move = true; 1512 if (np) 1513 { 1514 setWindow(sx, y, sx + np - 1, y); 1515 pushPixels((uint16_t*)lineBuf, np); 1516 np = 0; 1517 } 1518 } 1519 px++; 1520 ptr++; 1521 } 1522 if (np) { setWindow(sx, y, sx + np - 1, y); pushPixels((uint16_t*)lineBuf, np); } 1523 1524 y++; 1525 data += w; 1526 } 1527 1528 inTransaction = lockTransaction; 1529 end_tft_write(); 1530 } 1531 1532 1533 /*************************************************************************************** 1534 ** Function name: pushImage - for FLASH (PROGMEM) stored images 1535 ** Description: plot 16 bit image 1536 ***************************************************************************************/ 1537 void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data) 1538 { 1539 // Requires 32 bit aligned access, so use PROGMEM 16 bit word functions 1540 PI_CLIP; 1541 1542 begin_tft_write(); 1543 inTransaction = true; 1544 1545 data += dx + dy * w; 1546 1547 uint16_t buffer[dw]; 1548 1549 setWindow(x, y, x + dw - 1, y + dh - 1); 1550 1551 // Fill and send line buffers to TFT 1552 for (int32_t i = 0; i < dh; i++) { 1553 for (int32_t j = 0; j < dw; j++) { 1554 buffer[j] = pgm_read_word(&data[i * w + j]); 1555 } 1556 pushPixels(buffer, dw); 1557 } 1558 1559 inTransaction = lockTransaction; 1560 end_tft_write(); 1561 } 1562 1563 /*************************************************************************************** 1564 ** Function name: pushImage - for FLASH (PROGMEM) stored images 1565 ** Description: plot 16 bit image with 1 colour being transparent 1566 ***************************************************************************************/ 1567 void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data, uint16_t transp) 1568 { 1569 // Requires 32 bit aligned access, so use PROGMEM 16 bit word functions 1570 PI_CLIP; 1571 1572 begin_tft_write(); 1573 inTransaction = true; 1574 1575 data += dx + dy * w; 1576 1577 1578 uint16_t lineBuf[dw]; 1579 1580 // The little endian transp color must be byte swapped if the image is big endian 1581 if (!_swapBytes) transp = transp >> 8 | transp << 8; 1582 1583 while (dh--) { 1584 int32_t len = dw; 1585 uint16_t* ptr = (uint16_t*)data; 1586 int32_t px = x, sx = x; 1587 bool move = true; 1588 1589 uint16_t np = 0; 1590 1591 while (len--) { 1592 uint16_t color = pgm_read_word(ptr); 1593 if (transp != color) { 1594 if (move) { move = false; sx = px; } 1595 lineBuf[np] = color; 1596 np++; 1597 } 1598 else { 1599 move = true; 1600 if (np) { 1601 setWindow(sx, y, sx + np - 1, y); 1602 pushPixels(lineBuf, np); 1603 np = 0; 1604 } 1605 } 1606 px++; 1607 ptr++; 1608 } 1609 if (np) { setWindow(sx, y, sx + np - 1, y); pushPixels(lineBuf, np); } 1610 1611 y++; 1612 data += w; 1613 } 1614 1615 inTransaction = lockTransaction; 1616 end_tft_write(); 1617 } 1618 1619 /*************************************************************************************** 1620 ** Function name: pushImage 1621 ** Description: plot 8 bit or 4 bit or 1 bit image or sprite using a line buffer 1622 ***************************************************************************************/ 1623 void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint8_t *data, bool bpp8, uint16_t *cmap) 1624 { 1625 PI_CLIP; 1626 1627 begin_tft_write(); 1628 inTransaction = true; 1629 bool swap = _swapBytes; 1630 1631 setWindow(x, y, x + dw - 1, y + dh - 1); // Sets CS low and sent RAMWR 1632 1633 // Line buffer makes plotting faster 1634 uint16_t lineBuf[dw]; 1635 1636 if (bpp8) 1637 { 1638 _swapBytes = false; 1639 1640 uint8_t blue[] = {0, 11, 21, 31}; // blue 2 to 5 bit colour lookup table 1641 1642 _lastColor = -1; // Set to illegal value 1643 1644 // Used to store last shifted colour 1645 uint8_t msbColor = 0; 1646 uint8_t lsbColor = 0; 1647 1648 data += dx + dy * w; 1649 while (dh--) { 1650 uint32_t len = dw; 1651 uint8_t* ptr = (uint8_t*)data; 1652 uint8_t* linePtr = (uint8_t*)lineBuf; 1653 1654 while(len--) { 1655 uint32_t color = pgm_read_byte(ptr++); 1656 1657 // Shifts are slow so check if colour has changed first 1658 if (color != _lastColor) { 1659 // =====Green===== ===============Red============== 1660 msbColor = (color & 0x1C)>>2 | (color & 0xC0)>>3 | (color & 0xE0); 1661 // =====Green===== =======Blue====== 1662 lsbColor = (color & 0x1C)<<3 | blue[color & 0x03]; 1663 _lastColor = color; 1664 } 1665 1666 *linePtr++ = msbColor; 1667 *linePtr++ = lsbColor; 1668 } 1669 1670 pushPixels(lineBuf, dw); 1671 1672 data += w; 1673 } 1674 _swapBytes = swap; // Restore old value 1675 } 1676 else if (cmap != nullptr) // Must be 4bpp 1677 { 1678 _swapBytes = true; 1679 1680 w = (w+1) & 0xFFFE; // if this is a sprite, w will already be even; this does no harm. 1681 bool splitFirst = (dx & 0x01) != 0; // split first means we have to push a single px from the left of the sprite / image 1682 1683 if (splitFirst) { 1684 data += ((dx - 1 + dy * w) >> 1); 1685 } 1686 else { 1687 data += ((dx + dy * w) >> 1); 1688 } 1689 1690 while (dh--) { 1691 uint32_t len = dw; 1692 uint8_t * ptr = (uint8_t*)data; 1693 uint16_t *linePtr = lineBuf; 1694 uint8_t colors; // two colors in one byte 1695 uint16_t index; 1696 1697 if (splitFirst) { 1698 colors = pgm_read_byte(ptr); 1699 index = (colors & 0x0F); 1700 *linePtr++ = cmap[index]; 1701 len--; 1702 ptr++; 1703 } 1704 1705 while (len--) 1706 { 1707 colors = pgm_read_byte(ptr); 1708 index = ((colors & 0xF0) >> 4) & 0x0F; 1709 *linePtr++ = cmap[index]; 1710 1711 if (len--) 1712 { 1713 index = colors & 0x0F; 1714 *linePtr++ = cmap[index]; 1715 } else { 1716 break; // nothing to do here 1717 } 1718 1719 ptr++; 1720 } 1721 1722 pushPixels(lineBuf, dw); 1723 data += (w >> 1); 1724 } 1725 _swapBytes = swap; // Restore old value 1726 } 1727 else // Must be 1bpp 1728 { 1729 _swapBytes = false; 1730 uint8_t * ptr = (uint8_t*)data; 1731 uint32_t ww = (w+7)>>3; // Width of source image line in bytes 1732 for (int32_t yp = dy; yp < dy + dh; yp++) 1733 { 1734 uint8_t* linePtr = (uint8_t*)lineBuf; 1735 for (int32_t xp = dx; xp < dx + dw; xp++) 1736 { 1737 uint16_t col = (pgm_read_byte(ptr + (xp>>3)) & (0x80 >> (xp & 0x7)) ); 1738 if (col) {*linePtr++ = bitmap_fg>>8; *linePtr++ = (uint8_t) bitmap_fg;} 1739 else {*linePtr++ = bitmap_bg>>8; *linePtr++ = (uint8_t) bitmap_bg;} 1740 } 1741 ptr += ww; 1742 pushPixels(lineBuf, dw); 1743 } 1744 } 1745 1746 _swapBytes = swap; // Restore old value 1747 inTransaction = lockTransaction; 1748 end_tft_write(); 1749 } 1750 1751 1752 /*************************************************************************************** 1753 ** Function name: pushImage 1754 ** Description: plot 8 bit or 4 bit or 1 bit image or sprite using a line buffer 1755 ***************************************************************************************/ 1756 void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *data, bool bpp8, uint16_t *cmap) 1757 { 1758 PI_CLIP; 1759 1760 begin_tft_write(); 1761 inTransaction = true; 1762 bool swap = _swapBytes; 1763 1764 setWindow(x, y, x + dw - 1, y + dh - 1); // Sets CS low and sent RAMWR 1765 1766 // Line buffer makes plotting faster 1767 uint16_t lineBuf[dw]; 1768 1769 if (bpp8) 1770 { 1771 _swapBytes = false; 1772 1773 uint8_t blue[] = {0, 11, 21, 31}; // blue 2 to 5 bit colour lookup table 1774 1775 _lastColor = -1; // Set to illegal value 1776 1777 // Used to store last shifted colour 1778 uint8_t msbColor = 0; 1779 uint8_t lsbColor = 0; 1780 1781 data += dx + dy * w; 1782 while (dh--) { 1783 uint32_t len = dw; 1784 uint8_t* ptr = data; 1785 uint8_t* linePtr = (uint8_t*)lineBuf; 1786 1787 while(len--) { 1788 uint32_t color = *ptr++; 1789 1790 // Shifts are slow so check if colour has changed first 1791 if (color != _lastColor) { 1792 // =====Green===== ===============Red============== 1793 msbColor = (color & 0x1C)>>2 | (color & 0xC0)>>3 | (color & 0xE0); 1794 // =====Green===== =======Blue====== 1795 lsbColor = (color & 0x1C)<<3 | blue[color & 0x03]; 1796 _lastColor = color; 1797 } 1798 1799 *linePtr++ = msbColor; 1800 *linePtr++ = lsbColor; 1801 } 1802 1803 pushPixels(lineBuf, dw); 1804 1805 data += w; 1806 } 1807 _swapBytes = swap; // Restore old value 1808 } 1809 else if (cmap != nullptr) // Must be 4bpp 1810 { 1811 _swapBytes = true; 1812 1813 w = (w+1) & 0xFFFE; // if this is a sprite, w will already be even; this does no harm. 1814 bool splitFirst = (dx & 0x01) != 0; // split first means we have to push a single px from the left of the sprite / image 1815 1816 if (splitFirst) { 1817 data += ((dx - 1 + dy * w) >> 1); 1818 } 1819 else { 1820 data += ((dx + dy * w) >> 1); 1821 } 1822 1823 while (dh--) { 1824 uint32_t len = dw; 1825 uint8_t * ptr = data; 1826 uint16_t *linePtr = lineBuf; 1827 uint8_t colors; // two colors in one byte 1828 uint16_t index; 1829 1830 if (splitFirst) { 1831 colors = *ptr; 1832 index = (colors & 0x0F); 1833 *linePtr++ = cmap[index]; 1834 len--; 1835 ptr++; 1836 } 1837 1838 while (len--) 1839 { 1840 colors = *ptr; 1841 index = ((colors & 0xF0) >> 4) & 0x0F; 1842 *linePtr++ = cmap[index]; 1843 1844 if (len--) 1845 { 1846 index = colors & 0x0F; 1847 *linePtr++ = cmap[index]; 1848 } else { 1849 break; // nothing to do here 1850 } 1851 1852 ptr++; 1853 } 1854 1855 pushPixels(lineBuf, dw); 1856 data += (w >> 1); 1857 } 1858 _swapBytes = swap; // Restore old value 1859 } 1860 else // Must be 1bpp 1861 { 1862 _swapBytes = false; 1863 1864 uint32_t ww = (w+7)>>3; // Width of source image line in bytes 1865 for (int32_t yp = dy; yp < dy + dh; yp++) 1866 { 1867 uint8_t* linePtr = (uint8_t*)lineBuf; 1868 for (int32_t xp = dx; xp < dx + dw; xp++) 1869 { 1870 uint16_t col = (data[(xp>>3)] & (0x80 >> (xp & 0x7)) ); 1871 if (col) {*linePtr++ = bitmap_fg>>8; *linePtr++ = (uint8_t) bitmap_fg;} 1872 else {*linePtr++ = bitmap_bg>>8; *linePtr++ = (uint8_t) bitmap_bg;} 1873 } 1874 data += ww; 1875 pushPixels(lineBuf, dw); 1876 } 1877 } 1878 1879 _swapBytes = swap; // Restore old value 1880 inTransaction = lockTransaction; 1881 end_tft_write(); 1882 } 1883 1884 1885 /*************************************************************************************** 1886 ** Function name: pushImage 1887 ** Description: plot 8 or 4 or 1 bit image or sprite with a transparent colour 1888 ***************************************************************************************/ 1889 void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *data, uint8_t transp, bool bpp8, uint16_t *cmap) 1890 { 1891 PI_CLIP; 1892 1893 begin_tft_write(); 1894 inTransaction = true; 1895 bool swap = _swapBytes; 1896 1897 1898 // Line buffer makes plotting faster 1899 uint16_t lineBuf[dw]; 1900 1901 if (bpp8) { // 8 bits per pixel 1902 _swapBytes = false; 1903 1904 data += dx + dy * w; 1905 1906 uint8_t blue[] = {0, 11, 21, 31}; // blue 2 to 5 bit colour lookup table 1907 1908 _lastColor = -1; // Set to illegal value 1909 1910 // Used to store last shifted colour 1911 uint8_t msbColor = 0; 1912 uint8_t lsbColor = 0; 1913 1914 while (dh--) { 1915 int32_t len = dw; 1916 uint8_t* ptr = data; 1917 uint8_t* linePtr = (uint8_t*)lineBuf; 1918 1919 int32_t px = x, sx = x; 1920 bool move = true; 1921 uint16_t np = 0; 1922 1923 while (len--) { 1924 if (transp != *ptr) { 1925 if (move) { move = false; sx = px; } 1926 uint8_t color = *ptr; 1927 1928 // Shifts are slow so check if colour has changed first 1929 if (color != _lastColor) { 1930 // =====Green===== ===============Red============== 1931 msbColor = (color & 0x1C)>>2 | (color & 0xC0)>>3 | (color & 0xE0); 1932 // =====Green===== =======Blue====== 1933 lsbColor = (color & 0x1C)<<3 | blue[color & 0x03]; 1934 _lastColor = color; 1935 } 1936 *linePtr++ = msbColor; 1937 *linePtr++ = lsbColor; 1938 np++; 1939 } 1940 else { 1941 move = true; 1942 if (np) { 1943 setWindow(sx, y, sx + np - 1, y); 1944 pushPixels(lineBuf, np); 1945 linePtr = (uint8_t*)lineBuf; 1946 np = 0; 1947 } 1948 } 1949 px++; 1950 ptr++; 1951 } 1952 1953 if (np) { setWindow(sx, y, sx + np - 1, y); pushPixels(lineBuf, np); } 1954 y++; 1955 data += w; 1956 } 1957 } 1958 else if (cmap != nullptr) // 4bpp with color map 1959 { 1960 _swapBytes = true; 1961 1962 w = (w+1) & 0xFFFE; // here we try to recreate iwidth from dwidth. 1963 bool splitFirst = ((dx & 0x01) != 0); 1964 if (splitFirst) { 1965 data += ((dx - 1 + dy * w) >> 1); 1966 } 1967 else { 1968 data += ((dx + dy * w) >> 1); 1969 } 1970 1971 while (dh--) { 1972 uint32_t len = dw; 1973 uint8_t * ptr = data; 1974 1975 int32_t px = x, sx = x; 1976 bool move = true; 1977 uint16_t np = 0; 1978 1979 uint8_t index; // index into cmap. 1980 1981 if (splitFirst) { 1982 index = (*ptr & 0x0F); // odd = bits 3 .. 0 1983 if (index != transp) { 1984 move = false; sx = px; 1985 lineBuf[np] = cmap[index]; 1986 np++; 1987 } 1988 px++; ptr++; 1989 len--; 1990 } 1991 1992 while (len--) 1993 { 1994 uint8_t color = *ptr; 1995 1996 // find the actual color you care about. There will be two pixels here! 1997 // but we may only want one at the end of the row 1998 uint16_t index = ((color & 0xF0) >> 4) & 0x0F; // high bits are the even numbers 1999 if (index != transp) { 2000 if (move) { 2001 move = false; sx = px; 2002 } 2003 lineBuf[np] = cmap[index]; 2004 np++; // added a pixel 2005 } 2006 else { 2007 move = true; 2008 if (np) { 2009 setWindow(sx, y, sx + np - 1, y); 2010 pushPixels(lineBuf, np); 2011 np = 0; 2012 } 2013 } 2014 px++; 2015 2016 if (len--) 2017 { 2018 index = color & 0x0F; // the odd number is 3 .. 0 2019 if (index != transp) { 2020 if (move) { 2021 move = false; sx = px; 2022 } 2023 lineBuf[np] = cmap[index]; 2024 np++; 2025 } 2026 else { 2027 move = true; 2028 if (np) { 2029 setWindow(sx, y, sx + np - 1, y); 2030 pushPixels(lineBuf, np); 2031 np = 0; 2032 } 2033 } 2034 px++; 2035 } 2036 else { 2037 break; // we are done with this row. 2038 } 2039 ptr++; // we only increment ptr once in the loop (deliberate) 2040 } 2041 2042 if (np) { 2043 setWindow(sx, y, sx + np - 1, y); 2044 pushPixels(lineBuf, np); 2045 np = 0; 2046 } 2047 data += (w>>1); 2048 y++; 2049 } 2050 } 2051 else { // 1 bit per pixel 2052 _swapBytes = false; 2053 2054 uint32_t ww = (w+7)>>3; // Width of source image line in bytes 2055 uint16_t np = 0; 2056 2057 for (int32_t yp = dy; yp < dy + dh; yp++) 2058 { 2059 int32_t px = x, sx = x; 2060 bool move = true; 2061 for (int32_t xp = dx; xp < dx + dw; xp++) 2062 { 2063 if (data[(xp>>3)] & (0x80 >> (xp & 0x7))) { 2064 if (move) { 2065 move = false; 2066 sx = px; 2067 } 2068 np++; 2069 } 2070 else { 2071 move = true; 2072 if (np) { 2073 setWindow(sx, y, sx + np - 1, y); 2074 pushBlock(bitmap_fg, np); 2075 np = 0; 2076 } 2077 } 2078 px++; 2079 } 2080 if (np) { setWindow(sx, y, sx + np - 1, y); pushBlock(bitmap_fg, np); np = 0; } 2081 y++; 2082 data += ww; 2083 } 2084 } 2085 _swapBytes = swap; // Restore old value 2086 inTransaction = lockTransaction; 2087 end_tft_write(); 2088 } 2089 2090 /*************************************************************************************** 2091 ** Function name: pushMaskedImage 2092 ** Description: Render a 16 bit colour image to TFT with a 1bpp mask 2093 ***************************************************************************************/ 2094 // Can be used with a 16bpp sprite and a 1bpp sprite for the mask 2095 void TFT_eSPI::pushMaskedImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *img, uint8_t *mask) 2096 { 2097 if (_vpOoB || w < 1 || h < 1) return; 2098 2099 // To simplify mask handling the window clipping is done by the pushImage function 2100 // Each mask image line assumed to be padded to and integer number of bytes & padding bits are 0 2101 2102 begin_tft_write(); 2103 inTransaction = true; 2104 2105 uint8_t *mptr = mask; 2106 uint8_t *eptr = mask + ((w + 7) >> 3); 2107 uint16_t *iptr = img; 2108 uint32_t setCount = 0; 2109 2110 // For each line in the image 2111 while (h--) { 2112 uint32_t xp = 0; 2113 uint32_t clearCount = 0; 2114 uint8_t mbyte= *mptr++; 2115 uint32_t bits = 8; 2116 // Scan through each byte of the bitmap and determine run lengths 2117 do { 2118 setCount = 0; 2119 2120 //Get run length for clear bits to determine x offset 2121 while ((mbyte & 0x80) == 0x00) { 2122 // Check if remaining bits in byte are clear (reduce shifts) 2123 if (mbyte == 0) { 2124 clearCount += bits; // bits not always 8 here 2125 if (mptr >= eptr) break; // end of line 2126 mbyte = *mptr++; 2127 bits = 8; 2128 continue; 2129 } 2130 mbyte = mbyte << 1; // 0's shifted in 2131 clearCount ++; 2132 if (--bits) continue;; 2133 if (mptr >= eptr) break; 2134 mbyte = *mptr++; 2135 bits = 8; 2136 } 2137 2138 //Get run length for set bits to determine render width 2139 while ((mbyte & 0x80) == 0x80) { 2140 // Check if all bits are set (reduces shifts) 2141 if (mbyte == 0xFF) { 2142 setCount += bits; 2143 if (mptr >= eptr) break; 2144 mbyte = *mptr++; 2145 //bits = 8; // NR, bits always 8 here unless 1's shifted in 2146 continue; 2147 } 2148 mbyte = mbyte << 1; //or mbyte += mbyte + 1 to shift in 1's 2149 setCount ++; 2150 if (--bits) continue; 2151 if (mptr >= eptr) break; 2152 mbyte = *mptr++; 2153 bits = 8; 2154 } 2155 2156 // A mask boundary or mask end has been found, so render the pixel line 2157 if (setCount) { 2158 xp += clearCount; 2159 clearCount = 0; 2160 pushImage(x + xp, y, setCount, 1, iptr + xp); // pushImage handles clipping 2161 xp += setCount; 2162 } 2163 } while (setCount || mptr < eptr); 2164 2165 y++; 2166 iptr += w; 2167 eptr += ((w + 7) >> 3); 2168 } 2169 2170 inTransaction = lockTransaction; 2171 end_tft_write(); 2172 } 2173 2174 2175 /*************************************************************************************** 2176 ** Function name: setSwapBytes 2177 ** Description: Used by 16 bit pushImage() to swap byte order in colours 2178 ***************************************************************************************/ 2179 void TFT_eSPI::setSwapBytes(bool swap) 2180 { 2181 _swapBytes = swap; 2182 } 2183 2184 2185 /*************************************************************************************** 2186 ** Function name: getSwapBytes 2187 ** Description: Return the swap byte order for colours 2188 ***************************************************************************************/ 2189 bool TFT_eSPI::getSwapBytes(void) 2190 { 2191 return _swapBytes; 2192 } 2193 2194 2195 /*************************************************************************************** 2196 ** Function name: read rectangle (for SPI Interface II i.e. IM [3:0] = "1101") 2197 ** Description: Read RGB pixel colours from a defined area 2198 ***************************************************************************************/ 2199 // If w and h are 1, then 1 pixel is read, *data array size must be 3 bytes per pixel 2200 void TFT_eSPI::readRectRGB(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_t *data) 2201 { 2202 #if defined(TFT_PARALLEL_8_BIT) || defined(RP2040_PIO_INTERFACE) 2203 2204 uint32_t len = w * h; 2205 uint8_t* buf565 = data + len; 2206 2207 readRect(x0, y0, w, h, (uint16_t*)buf565); 2208 2209 while (len--) { 2210 uint16_t pixel565 = (*buf565++)<<8; 2211 pixel565 |= *buf565++; 2212 uint8_t red = (pixel565 & 0xF800) >> 8; red |= red >> 5; 2213 uint8_t green = (pixel565 & 0x07E0) >> 3; green |= green >> 6; 2214 uint8_t blue = (pixel565 & 0x001F) << 3; blue |= blue >> 5; 2215 *data++ = red; 2216 *data++ = green; 2217 *data++ = blue; 2218 } 2219 2220 #else // Not TFT_PARALLEL_8_BIT 2221 2222 begin_tft_read(); 2223 2224 readAddrWindow(x0, y0, w, h); // Sets CS low 2225 2226 #ifdef TFT_SDA_READ 2227 begin_SDA_Read(); 2228 #endif 2229 2230 // Dummy read to throw away don't care value 2231 tft_Read_8(); 2232 2233 // Read window pixel 24 bit RGB values, buffer must be set in sketch to 3 * w * h 2234 uint32_t len = w * h; 2235 while (len--) { 2236 2237 #if !defined (ILI9488_DRIVER) 2238 2239 // Read the 3 RGB bytes, colour is actually only in the top 6 bits of each byte 2240 // as the TFT stores colours as 18 bits 2241 *data++ = tft_Read_8(); 2242 *data++ = tft_Read_8(); 2243 *data++ = tft_Read_8(); 2244 2245 #else 2246 2247 // The 6 colour bits are in MS 6 bits of each byte, but the ILI9488 needs an extra clock pulse 2248 // so bits appear shifted right 1 bit, so mask the middle 6 bits then shift 1 place left 2249 *data++ = (tft_Read_8()&0x7E)<<1; 2250 *data++ = (tft_Read_8()&0x7E)<<1; 2251 *data++ = (tft_Read_8()&0x7E)<<1; 2252 2253 #endif 2254 2255 } 2256 2257 CS_H; 2258 2259 #ifdef TFT_SDA_READ 2260 end_SDA_Read(); 2261 #endif 2262 2263 end_tft_read(); 2264 2265 #endif 2266 } 2267 2268 2269 /*************************************************************************************** 2270 ** Function name: drawCircle 2271 ** Description: Draw a circle outline 2272 ***************************************************************************************/ 2273 // Optimised midpoint circle algorithm 2274 void TFT_eSPI::drawCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color) 2275 { 2276 if ( r <= 0 ) return; 2277 2278 //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() 2279 inTransaction = true; 2280 2281 int32_t f = 1 - r; 2282 int32_t ddF_y = -2 * r; 2283 int32_t ddF_x = 1; 2284 int32_t xs = -1; 2285 int32_t xe = 0; 2286 int32_t len = 0; 2287 2288 bool first = true; 2289 do { 2290 while (f < 0) { 2291 ++xe; 2292 f += (ddF_x += 2); 2293 } 2294 f += (ddF_y += 2); 2295 2296 if (xe-xs>1) { 2297 if (first) { 2298 len = 2*(xe - xs)-1; 2299 drawFastHLine(x0 - xe, y0 + r, len, color); 2300 drawFastHLine(x0 - xe, y0 - r, len, color); 2301 drawFastVLine(x0 + r, y0 - xe, len, color); 2302 drawFastVLine(x0 - r, y0 - xe, len, color); 2303 first = false; 2304 } 2305 else { 2306 len = xe - xs++; 2307 drawFastHLine(x0 - xe, y0 + r, len, color); 2308 drawFastHLine(x0 - xe, y0 - r, len, color); 2309 drawFastHLine(x0 + xs, y0 - r, len, color); 2310 drawFastHLine(x0 + xs, y0 + r, len, color); 2311 2312 drawFastVLine(x0 + r, y0 + xs, len, color); 2313 drawFastVLine(x0 + r, y0 - xe, len, color); 2314 drawFastVLine(x0 - r, y0 - xe, len, color); 2315 drawFastVLine(x0 - r, y0 + xs, len, color); 2316 } 2317 } 2318 else { 2319 ++xs; 2320 drawPixel(x0 - xe, y0 + r, color); 2321 drawPixel(x0 - xe, y0 - r, color); 2322 drawPixel(x0 + xs, y0 - r, color); 2323 drawPixel(x0 + xs, y0 + r, color); 2324 2325 drawPixel(x0 + r, y0 + xs, color); 2326 drawPixel(x0 + r, y0 - xe, color); 2327 drawPixel(x0 - r, y0 - xe, color); 2328 drawPixel(x0 - r, y0 + xs, color); 2329 } 2330 xs = xe; 2331 } while (xe < --r); 2332 2333 inTransaction = lockTransaction; 2334 end_tft_write(); // Does nothing if Sprite class uses this function 2335 } 2336 2337 2338 /*************************************************************************************** 2339 ** Function name: drawCircleHelper 2340 ** Description: Support function for drawRoundRect() 2341 ***************************************************************************************/ 2342 void TFT_eSPI::drawCircleHelper( int32_t x0, int32_t y0, int32_t rr, uint8_t cornername, uint32_t color) 2343 { 2344 if (rr <= 0) return; 2345 int32_t f = 1 - rr; 2346 int32_t ddF_x = 1; 2347 int32_t ddF_y = -2 * rr; 2348 int32_t xe = 0; 2349 int32_t xs = 0; 2350 int32_t len = 0; 2351 2352 //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() 2353 inTransaction = true; 2354 2355 while (xe < rr--) 2356 { 2357 while (f < 0) { 2358 ++xe; 2359 f += (ddF_x += 2); 2360 } 2361 f += (ddF_y += 2); 2362 2363 if (xe-xs==1) { 2364 if (cornername & 0x1) { // left top 2365 drawPixel(x0 - xe, y0 - rr, color); 2366 drawPixel(x0 - rr, y0 - xe, color); 2367 } 2368 if (cornername & 0x2) { // right top 2369 drawPixel(x0 + rr , y0 - xe, color); 2370 drawPixel(x0 + xs + 1, y0 - rr, color); 2371 } 2372 if (cornername & 0x4) { // right bottom 2373 drawPixel(x0 + xs + 1, y0 + rr , color); 2374 drawPixel(x0 + rr, y0 + xs + 1, color); 2375 } 2376 if (cornername & 0x8) { // left bottom 2377 drawPixel(x0 - rr, y0 + xs + 1, color); 2378 drawPixel(x0 - xe, y0 + rr , color); 2379 } 2380 } 2381 else { 2382 len = xe - xs++; 2383 if (cornername & 0x1) { // left top 2384 drawFastHLine(x0 - xe, y0 - rr, len, color); 2385 drawFastVLine(x0 - rr, y0 - xe, len, color); 2386 } 2387 if (cornername & 0x2) { // right top 2388 drawFastVLine(x0 + rr, y0 - xe, len, color); 2389 drawFastHLine(x0 + xs, y0 - rr, len, color); 2390 } 2391 if (cornername & 0x4) { // right bottom 2392 drawFastHLine(x0 + xs, y0 + rr, len, color); 2393 drawFastVLine(x0 + rr, y0 + xs, len, color); 2394 } 2395 if (cornername & 0x8) { // left bottom 2396 drawFastVLine(x0 - rr, y0 + xs, len, color); 2397 drawFastHLine(x0 - xe, y0 + rr, len, color); 2398 } 2399 } 2400 xs = xe; 2401 } 2402 inTransaction = lockTransaction; 2403 end_tft_write(); // Does nothing if Sprite class uses this function 2404 } 2405 2406 /*************************************************************************************** 2407 ** Function name: fillCircle 2408 ** Description: draw a filled circle 2409 ***************************************************************************************/ 2410 // Optimised midpoint circle algorithm, changed to horizontal lines (faster in sprites) 2411 // Improved algorithm avoids repetition of lines 2412 void TFT_eSPI::fillCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color) 2413 { 2414 int32_t x = 0; 2415 int32_t dx = 1; 2416 int32_t dy = r+r; 2417 int32_t p = -(r>>1); 2418 2419 //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() 2420 inTransaction = true; 2421 2422 drawFastHLine(x0 - r, y0, dy+1, color); 2423 2424 while(x<r){ 2425 2426 if(p>=0) { 2427 drawFastHLine(x0 - x, y0 + r, dx, color); 2428 drawFastHLine(x0 - x, y0 - r, dx, color); 2429 dy-=2; 2430 p-=dy; 2431 r--; 2432 } 2433 2434 dx+=2; 2435 p+=dx; 2436 x++; 2437 2438 drawFastHLine(x0 - r, y0 + x, dy+1, color); 2439 drawFastHLine(x0 - r, y0 - x, dy+1, color); 2440 2441 } 2442 2443 inTransaction = lockTransaction; 2444 end_tft_write(); // Does nothing if Sprite class uses this function 2445 } 2446 2447 /*************************************************************************************** 2448 ** Function name: fillCircleHelper 2449 ** Description: Support function for fillRoundRect() 2450 ***************************************************************************************/ 2451 // Support drawing roundrects, changed to horizontal lines (faster in sprites) 2452 void TFT_eSPI::fillCircleHelper(int32_t x0, int32_t y0, int32_t r, uint8_t cornername, int32_t delta, uint32_t color) 2453 { 2454 int32_t f = 1 - r; 2455 int32_t ddF_x = 1; 2456 int32_t ddF_y = -r - r; 2457 int32_t y = 0; 2458 2459 delta++; 2460 2461 while (y < r) { 2462 if (f >= 0) { 2463 if (cornername & 0x1) drawFastHLine(x0 - y, y0 + r, y + y + delta, color); 2464 if (cornername & 0x2) drawFastHLine(x0 - y, y0 - r, y + y + delta, color); 2465 r--; 2466 ddF_y += 2; 2467 f += ddF_y; 2468 } 2469 2470 y++; 2471 ddF_x += 2; 2472 f += ddF_x; 2473 2474 if (cornername & 0x1) drawFastHLine(x0 - r, y0 + y, r + r + delta, color); 2475 if (cornername & 0x2) drawFastHLine(x0 - r, y0 - y, r + r + delta, color); 2476 } 2477 } 2478 2479 2480 /*************************************************************************************** 2481 ** Function name: drawEllipse 2482 ** Description: Draw a ellipse outline 2483 ***************************************************************************************/ 2484 void TFT_eSPI::drawEllipse(int16_t x0, int16_t y0, int32_t rx, int32_t ry, uint16_t color) 2485 { 2486 if (rx<2) return; 2487 if (ry<2) return; 2488 int32_t x, y; 2489 int32_t rx2 = rx * rx; 2490 int32_t ry2 = ry * ry; 2491 int32_t fx2 = 4 * rx2; 2492 int32_t fy2 = 4 * ry2; 2493 int32_t s; 2494 2495 //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() 2496 inTransaction = true; 2497 2498 for (x = 0, y = ry, s = 2*ry2+rx2*(1-2*ry); ry2*x <= rx2*y; x++) { 2499 // These are ordered to minimise coordinate changes in x or y 2500 // drawPixel can then send fewer bounding box commands 2501 drawPixel(x0 + x, y0 + y, color); 2502 drawPixel(x0 - x, y0 + y, color); 2503 drawPixel(x0 - x, y0 - y, color); 2504 drawPixel(x0 + x, y0 - y, color); 2505 if (s >= 0) { 2506 s += fx2 * (1 - y); 2507 y--; 2508 } 2509 s += ry2 * ((4 * x) + 6); 2510 } 2511 2512 for (x = rx, y = 0, s = 2*rx2+ry2*(1-2*rx); rx2*y <= ry2*x; y++) { 2513 // These are ordered to minimise coordinate changes in x or y 2514 // drawPixel can then send fewer bounding box commands 2515 drawPixel(x0 + x, y0 + y, color); 2516 drawPixel(x0 - x, y0 + y, color); 2517 drawPixel(x0 - x, y0 - y, color); 2518 drawPixel(x0 + x, y0 - y, color); 2519 if (s >= 0) 2520 { 2521 s += fy2 * (1 - x); 2522 x--; 2523 } 2524 s += rx2 * ((4 * y) + 6); 2525 } 2526 2527 inTransaction = lockTransaction; 2528 end_tft_write(); // Does nothing if Sprite class uses this function 2529 } 2530 2531 2532 /*************************************************************************************** 2533 ** Function name: fillEllipse 2534 ** Description: draw a filled ellipse 2535 ***************************************************************************************/ 2536 void TFT_eSPI::fillEllipse(int16_t x0, int16_t y0, int32_t rx, int32_t ry, uint16_t color) 2537 { 2538 if (rx<2) return; 2539 if (ry<2) return; 2540 int32_t x, y; 2541 int32_t rx2 = rx * rx; 2542 int32_t ry2 = ry * ry; 2543 int32_t fx2 = 4 * rx2; 2544 int32_t fy2 = 4 * ry2; 2545 int32_t s; 2546 2547 //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() 2548 inTransaction = true; 2549 2550 for (x = 0, y = ry, s = 2*ry2+rx2*(1-2*ry); ry2*x <= rx2*y; x++) { 2551 drawFastHLine(x0 - x, y0 - y, x + x + 1, color); 2552 drawFastHLine(x0 - x, y0 + y, x + x + 1, color); 2553 2554 if (s >= 0) { 2555 s += fx2 * (1 - y); 2556 y--; 2557 } 2558 s += ry2 * ((4 * x) + 6); 2559 } 2560 2561 for (x = rx, y = 0, s = 2*rx2+ry2*(1-2*rx); rx2*y <= ry2*x; y++) { 2562 drawFastHLine(x0 - x, y0 - y, x + x + 1, color); 2563 drawFastHLine(x0 - x, y0 + y, x + x + 1, color); 2564 2565 if (s >= 0) { 2566 s += fy2 * (1 - x); 2567 x--; 2568 } 2569 s += rx2 * ((4 * y) + 6); 2570 } 2571 2572 inTransaction = lockTransaction; 2573 end_tft_write(); // Does nothing if Sprite class uses this function 2574 } 2575 2576 2577 /*************************************************************************************** 2578 ** Function name: fillScreen 2579 ** Description: Clear the screen to defined colour 2580 ***************************************************************************************/ 2581 void TFT_eSPI::fillScreen(uint32_t color) 2582 { 2583 fillRect(0, 0, _width, _height, color); 2584 } 2585 2586 2587 /*************************************************************************************** 2588 ** Function name: drawRect 2589 ** Description: Draw a rectangle outline 2590 ***************************************************************************************/ 2591 // Draw a rectangle 2592 void TFT_eSPI::drawRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) 2593 { 2594 //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() 2595 inTransaction = true; 2596 2597 drawFastHLine(x, y, w, color); 2598 drawFastHLine(x, y + h - 1, w, color); 2599 // Avoid drawing corner pixels twice 2600 drawFastVLine(x, y+1, h-2, color); 2601 drawFastVLine(x + w - 1, y+1, h-2, color); 2602 2603 inTransaction = lockTransaction; 2604 end_tft_write(); // Does nothing if Sprite class uses this function 2605 } 2606 2607 2608 /*************************************************************************************** 2609 ** Function name: drawRoundRect 2610 ** Description: Draw a rounded corner rectangle outline 2611 ***************************************************************************************/ 2612 // Draw a rounded rectangle 2613 void TFT_eSPI::drawRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t r, uint32_t color) 2614 { 2615 //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() 2616 inTransaction = true; 2617 2618 // smarter version 2619 drawFastHLine(x + r , y , w - r - r, color); // Top 2620 drawFastHLine(x + r , y + h - 1, w - r - r, color); // Bottom 2621 drawFastVLine(x , y + r , h - r - r, color); // Left 2622 drawFastVLine(x + w - 1, y + r , h - r - r, color); // Right 2623 // draw four corners 2624 drawCircleHelper(x + r , y + r , r, 1, color); 2625 drawCircleHelper(x + w - r - 1, y + r , r, 2, color); 2626 drawCircleHelper(x + w - r - 1, y + h - r - 1, r, 4, color); 2627 drawCircleHelper(x + r , y + h - r - 1, r, 8, color); 2628 2629 inTransaction = lockTransaction; 2630 end_tft_write(); // Does nothing if Sprite class uses this function 2631 } 2632 2633 2634 /*************************************************************************************** 2635 ** Function name: fillRoundRect 2636 ** Description: Draw a rounded corner filled rectangle 2637 ***************************************************************************************/ 2638 // Fill a rounded rectangle, changed to horizontal lines (faster in sprites) 2639 void TFT_eSPI::fillRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t r, uint32_t color) 2640 { 2641 //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() 2642 inTransaction = true; 2643 2644 // smarter version 2645 fillRect(x, y + r, w, h - r - r, color); 2646 2647 // draw four corners 2648 fillCircleHelper(x + r, y + h - r - 1, r, 1, w - r - r - 1, color); 2649 fillCircleHelper(x + r , y + r, r, 2, w - r - r - 1, color); 2650 2651 inTransaction = lockTransaction; 2652 end_tft_write(); // Does nothing if Sprite class uses this function 2653 } 2654 2655 2656 /*************************************************************************************** 2657 ** Function name: drawTriangle 2658 ** Description: Draw a triangle outline using 3 arbitrary points 2659 ***************************************************************************************/ 2660 // Draw a triangle 2661 void TFT_eSPI::drawTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t color) 2662 { 2663 //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() 2664 inTransaction = true; 2665 2666 drawLine(x0, y0, x1, y1, color); 2667 drawLine(x1, y1, x2, y2, color); 2668 drawLine(x2, y2, x0, y0, color); 2669 2670 inTransaction = lockTransaction; 2671 end_tft_write(); // Does nothing if Sprite class uses this function 2672 } 2673 2674 2675 /*************************************************************************************** 2676 ** Function name: fillTriangle 2677 ** Description: Draw a filled triangle using 3 arbitrary points 2678 ***************************************************************************************/ 2679 // Fill a triangle - original Adafruit function works well and code footprint is small 2680 void TFT_eSPI::fillTriangle ( int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t color) 2681 { 2682 int32_t a, b, y, last; 2683 2684 // Sort coordinates by Y order (y2 >= y1 >= y0) 2685 if (y0 > y1) { 2686 transpose(y0, y1); transpose(x0, x1); 2687 } 2688 if (y1 > y2) { 2689 transpose(y2, y1); transpose(x2, x1); 2690 } 2691 if (y0 > y1) { 2692 transpose(y0, y1); transpose(x0, x1); 2693 } 2694 2695 if (y0 == y2) { // Handle awkward all-on-same-line case as its own thing 2696 a = b = x0; 2697 if (x1 < a) a = x1; 2698 else if (x1 > b) b = x1; 2699 if (x2 < a) a = x2; 2700 else if (x2 > b) b = x2; 2701 drawFastHLine(a, y0, b - a + 1, color); 2702 return; 2703 } 2704 2705 //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() 2706 inTransaction = true; 2707 2708 int32_t 2709 dx01 = x1 - x0, 2710 dy01 = y1 - y0, 2711 dx02 = x2 - x0, 2712 dy02 = y2 - y0, 2713 dx12 = x2 - x1, 2714 dy12 = y2 - y1, 2715 sa = 0, 2716 sb = 0; 2717 2718 // For upper part of triangle, find scanline crossings for segments 2719 // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1 2720 // is included here (and second loop will be skipped, avoiding a /0 2721 // error there), otherwise scanline y1 is skipped here and handled 2722 // in the second loop...which also avoids a /0 error here if y0=y1 2723 // (flat-topped triangle). 2724 if (y1 == y2) last = y1; // Include y1 scanline 2725 else last = y1 - 1; // Skip it 2726 2727 for (y = y0; y <= last; y++) { 2728 a = x0 + sa / dy01; 2729 b = x0 + sb / dy02; 2730 sa += dx01; 2731 sb += dx02; 2732 2733 if (a > b) transpose(a, b); 2734 drawFastHLine(a, y, b - a + 1, color); 2735 } 2736 2737 // For lower part of triangle, find scanline crossings for segments 2738 // 0-2 and 1-2. This loop is skipped if y1=y2. 2739 sa = dx12 * (y - y1); 2740 sb = dx02 * (y - y0); 2741 for (; y <= y2; y++) { 2742 a = x1 + sa / dy12; 2743 b = x0 + sb / dy02; 2744 sa += dx12; 2745 sb += dx02; 2746 2747 if (a > b) transpose(a, b); 2748 drawFastHLine(a, y, b - a + 1, color); 2749 } 2750 2751 inTransaction = lockTransaction; 2752 end_tft_write(); // Does nothing if Sprite class uses this function 2753 } 2754 2755 2756 /*************************************************************************************** 2757 ** Function name: drawBitmap 2758 ** Description: Draw an image stored in an array on the TFT 2759 ***************************************************************************************/ 2760 void TFT_eSPI::drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color) 2761 { 2762 //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() 2763 inTransaction = true; 2764 2765 int32_t i, j, byteWidth = (w + 7) / 8; 2766 2767 for (j = 0; j < h; j++) { 2768 for (i = 0; i < w; i++ ) { 2769 if (pgm_read_byte(bitmap + j * byteWidth + i / 8) & (128 >> (i & 7))) { 2770 drawPixel(x + i, y + j, color); 2771 } 2772 } 2773 } 2774 2775 inTransaction = lockTransaction; 2776 end_tft_write(); // Does nothing if Sprite class uses this function 2777 } 2778 2779 2780 /*************************************************************************************** 2781 ** Function name: drawBitmap 2782 ** Description: Draw an image stored in an array on the TFT 2783 ***************************************************************************************/ 2784 void TFT_eSPI::drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t fgcolor, uint16_t bgcolor) 2785 { 2786 //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() 2787 inTransaction = true; 2788 2789 int32_t i, j, byteWidth = (w + 7) / 8; 2790 2791 for (j = 0; j < h; j++) { 2792 for (i = 0; i < w; i++ ) { 2793 if (pgm_read_byte(bitmap + j * byteWidth + i / 8) & (128 >> (i & 7))) 2794 drawPixel(x + i, y + j, fgcolor); 2795 else drawPixel(x + i, y + j, bgcolor); 2796 } 2797 } 2798 2799 inTransaction = lockTransaction; 2800 end_tft_write(); // Does nothing if Sprite class uses this function 2801 } 2802 2803 /*************************************************************************************** 2804 ** Function name: drawXBitmap 2805 ** Description: Draw an image stored in an XBM array onto the TFT 2806 ***************************************************************************************/ 2807 void TFT_eSPI::drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color) 2808 { 2809 //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() 2810 inTransaction = true; 2811 2812 int32_t i, j, byteWidth = (w + 7) / 8; 2813 2814 for (j = 0; j < h; j++) { 2815 for (i = 0; i < w; i++ ) { 2816 if (pgm_read_byte(bitmap + j * byteWidth + i / 8) & (1 << (i & 7))) { 2817 drawPixel(x + i, y + j, color); 2818 } 2819 } 2820 } 2821 2822 inTransaction = lockTransaction; 2823 end_tft_write(); // Does nothing if Sprite class uses this function 2824 } 2825 2826 2827 /*************************************************************************************** 2828 ** Function name: drawXBitmap 2829 ** Description: Draw an XBM image with foreground and background colors 2830 ***************************************************************************************/ 2831 void TFT_eSPI::drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bgcolor) 2832 { 2833 //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() 2834 inTransaction = true; 2835 2836 int32_t i, j, byteWidth = (w + 7) / 8; 2837 2838 for (j = 0; j < h; j++) { 2839 for (i = 0; i < w; i++ ) { 2840 if (pgm_read_byte(bitmap + j * byteWidth + i / 8) & (1 << (i & 7))) 2841 drawPixel(x + i, y + j, color); 2842 else drawPixel(x + i, y + j, bgcolor); 2843 } 2844 } 2845 2846 inTransaction = lockTransaction; 2847 end_tft_write(); // Does nothing if Sprite class uses this function 2848 } 2849 2850 2851 /*************************************************************************************** 2852 ** Function name: setCursor 2853 ** Description: Set the text cursor x,y position 2854 ***************************************************************************************/ 2855 void TFT_eSPI::setCursor(int16_t x, int16_t y) 2856 { 2857 cursor_x = x; 2858 cursor_y = y; 2859 } 2860 2861 2862 /*************************************************************************************** 2863 ** Function name: setCursor 2864 ** Description: Set the text cursor x,y position and font 2865 ***************************************************************************************/ 2866 void TFT_eSPI::setCursor(int16_t x, int16_t y, uint8_t font) 2867 { 2868 textfont = font; 2869 cursor_x = x; 2870 cursor_y = y; 2871 } 2872 2873 2874 /*************************************************************************************** 2875 ** Function name: getCursorX 2876 ** Description: Get the text cursor x position 2877 ***************************************************************************************/ 2878 int16_t TFT_eSPI::getCursorX(void) 2879 { 2880 return cursor_x; 2881 } 2882 2883 /*************************************************************************************** 2884 ** Function name: getCursorY 2885 ** Description: Get the text cursor y position 2886 ***************************************************************************************/ 2887 int16_t TFT_eSPI::getCursorY(void) 2888 { 2889 return cursor_y; 2890 } 2891 2892 2893 /*************************************************************************************** 2894 ** Function name: setTextSize 2895 ** Description: Set the text size multiplier 2896 ***************************************************************************************/ 2897 void TFT_eSPI::setTextSize(uint8_t s) 2898 { 2899 if (s>7) s = 7; // Limit the maximum size multiplier so byte variables can be used for rendering 2900 textsize = (s > 0) ? s : 1; // Don't allow font size 0 2901 } 2902 2903 2904 /*************************************************************************************** 2905 ** Function name: setTextColor 2906 ** Description: Set the font foreground colour (background is transparent) 2907 ***************************************************************************************/ 2908 void TFT_eSPI::setTextColor(uint16_t c) 2909 { 2910 // For 'transparent' background, we'll set the bg 2911 // to the same as fg instead of using a flag 2912 textcolor = textbgcolor = c; 2913 } 2914 2915 2916 /*************************************************************************************** 2917 ** Function name: setTextColor 2918 ** Description: Set the font foreground and background colour 2919 ***************************************************************************************/ 2920 // Smooth fonts use the background colour for anti-aliasing and by default the 2921 // background is not filled. If bgfill = true, then a smooth font background fill will 2922 // be used. 2923 void TFT_eSPI::setTextColor(uint16_t c, uint16_t b, bool bgfill) 2924 { 2925 textcolor = c; 2926 textbgcolor = b; 2927 _fillbg = bgfill; 2928 } 2929 2930 2931 /*************************************************************************************** 2932 ** Function name: setPivot 2933 ** Description: Set the pivot point on the TFT 2934 *************************************************************************************x*/ 2935 void TFT_eSPI::setPivot(int16_t x, int16_t y) 2936 { 2937 _xPivot = x; 2938 _yPivot = y; 2939 } 2940 2941 2942 /*************************************************************************************** 2943 ** Function name: getPivotX 2944 ** Description: Get the x pivot position 2945 ***************************************************************************************/ 2946 int16_t TFT_eSPI::getPivotX(void) 2947 { 2948 return _xPivot; 2949 } 2950 2951 2952 /*************************************************************************************** 2953 ** Function name: getPivotY 2954 ** Description: Get the y pivot position 2955 ***************************************************************************************/ 2956 int16_t TFT_eSPI::getPivotY(void) 2957 { 2958 return _yPivot; 2959 } 2960 2961 2962 /*************************************************************************************** 2963 ** Function name: setBitmapColor 2964 ** Description: Set the foreground foreground and background colour 2965 ***************************************************************************************/ 2966 void TFT_eSPI::setBitmapColor(uint16_t c, uint16_t b) 2967 { 2968 if (c == b) b = ~c; 2969 bitmap_fg = c; 2970 bitmap_bg = b; 2971 } 2972 2973 2974 /*************************************************************************************** 2975 ** Function name: setTextWrap 2976 ** Description: Define if text should wrap at end of line 2977 ***************************************************************************************/ 2978 void TFT_eSPI::setTextWrap(bool wrapX, bool wrapY) 2979 { 2980 textwrapX = wrapX; 2981 textwrapY = wrapY; 2982 } 2983 2984 2985 /*************************************************************************************** 2986 ** Function name: setTextDatum 2987 ** Description: Set the text position reference datum 2988 ***************************************************************************************/ 2989 void TFT_eSPI::setTextDatum(uint8_t d) 2990 { 2991 textdatum = d; 2992 } 2993 2994 2995 /*************************************************************************************** 2996 ** Function name: setTextPadding 2997 ** Description: Define padding width (aids erasing old text and numbers) 2998 ***************************************************************************************/ 2999 void TFT_eSPI::setTextPadding(uint16_t x_width) 3000 { 3001 padX = x_width; 3002 } 3003 3004 /*************************************************************************************** 3005 ** Function name: setTextPadding 3006 ** Description: Define padding width (aids erasing old text and numbers) 3007 ***************************************************************************************/ 3008 uint16_t TFT_eSPI::getTextPadding(void) 3009 { 3010 return padX; 3011 } 3012 3013 /*************************************************************************************** 3014 ** Function name: getTextDatum 3015 ** Description: Return the text datum value (as used by setTextDatum()) 3016 ***************************************************************************************/ 3017 uint8_t TFT_eSPI::getTextDatum(void) 3018 { 3019 return textdatum; 3020 } 3021 3022 3023 /*************************************************************************************** 3024 ** Function name: width 3025 ** Description: Return the pixel width of display (per current rotation) 3026 ***************************************************************************************/ 3027 // Return the size of the display (per current rotation) 3028 int16_t TFT_eSPI::width(void) 3029 { 3030 if (_vpDatum) return _xWidth; 3031 return _width; 3032 } 3033 3034 3035 /*************************************************************************************** 3036 ** Function name: height 3037 ** Description: Return the pixel height of display (per current rotation) 3038 ***************************************************************************************/ 3039 int16_t TFT_eSPI::height(void) 3040 { 3041 if (_vpDatum) return _yHeight; 3042 return _height; 3043 } 3044 3045 3046 /*************************************************************************************** 3047 ** Function name: textWidth 3048 ** Description: Return the width in pixels of a string in a given font 3049 ***************************************************************************************/ 3050 int16_t TFT_eSPI::textWidth(const String& string) 3051 { 3052 int16_t len = string.length() + 2; 3053 char buffer[len]; 3054 string.toCharArray(buffer, len); 3055 return textWidth(buffer, textfont); 3056 } 3057 3058 int16_t TFT_eSPI::textWidth(const String& string, uint8_t font) 3059 { 3060 int16_t len = string.length() + 2; 3061 char buffer[len]; 3062 string.toCharArray(buffer, len); 3063 return textWidth(buffer, font); 3064 } 3065 3066 int16_t TFT_eSPI::textWidth(const char *string) 3067 { 3068 return textWidth(string, textfont); 3069 } 3070 3071 int16_t TFT_eSPI::textWidth(const char *string, uint8_t font) 3072 { 3073 int32_t str_width = 0; 3074 uint16_t uniCode = 0; 3075 3076 #ifdef SMOOTH_FONT 3077 if(fontLoaded) { 3078 while (*string) { 3079 uniCode = decodeUTF8(*string++); 3080 if (uniCode) { 3081 if (uniCode == 0x20) str_width += gFont.spaceWidth; 3082 else { 3083 uint16_t gNum = 0; 3084 bool found = getUnicodeIndex(uniCode, &gNum); 3085 if (found) { 3086 if(str_width == 0 && gdX[gNum] < 0) str_width -= gdX[gNum]; 3087 if (*string || isDigits) str_width += gxAdvance[gNum]; 3088 else str_width += (gdX[gNum] + gWidth[gNum]); 3089 } 3090 else str_width += gFont.spaceWidth + 1; 3091 } 3092 } 3093 } 3094 isDigits = false; 3095 return str_width; 3096 } 3097 #endif 3098 3099 if (font>1 && font<9) { 3100 char *widthtable = (char *)pgm_read_dword( &(fontdata[font].widthtbl ) ) - 32; //subtract the 32 outside the loop 3101 3102 while (*string) { 3103 uniCode = *(string++); 3104 if (uniCode > 31 && uniCode < 128) 3105 str_width += pgm_read_byte( widthtable + uniCode); // Normally we need to subtract 32 from uniCode 3106 else str_width += pgm_read_byte( widthtable + 32); // Set illegal character = space width 3107 } 3108 3109 } 3110 else { 3111 3112 #ifdef LOAD_GFXFF 3113 if(gfxFont) { // New font 3114 while (*string) { 3115 uniCode = decodeUTF8(*string++); 3116 if ((uniCode >= pgm_read_word(&gfxFont->first)) && (uniCode <= pgm_read_word(&gfxFont->last ))) { 3117 uniCode -= pgm_read_word(&gfxFont->first); 3118 GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[uniCode]); 3119 // If this is not the last character or is a digit then use xAdvance 3120 if (*string || isDigits) str_width += pgm_read_byte(&glyph->xAdvance); 3121 // Else use the offset plus width since this can be bigger than xAdvance 3122 else str_width += ((int8_t)pgm_read_byte(&glyph->xOffset) + pgm_read_byte(&glyph->width)); 3123 } 3124 } 3125 } 3126 else 3127 #endif 3128 { 3129 #ifdef LOAD_GLCD 3130 while (*string++) str_width += 6; 3131 #endif 3132 } 3133 } 3134 isDigits = false; 3135 return str_width * textsize; 3136 } 3137 3138 3139 /*************************************************************************************** 3140 ** Function name: fontsLoaded 3141 ** Description: return an encoded 16 bit value showing the fonts loaded 3142 ***************************************************************************************/ 3143 // Returns a value showing which fonts are loaded (bit N set = Font N loaded) 3144 uint16_t TFT_eSPI::fontsLoaded(void) 3145 { 3146 return fontsloaded; 3147 } 3148 3149 3150 /*************************************************************************************** 3151 ** Function name: fontHeight 3152 ** Description: return the height of a font (yAdvance for free fonts) 3153 ***************************************************************************************/ 3154 int16_t TFT_eSPI::fontHeight(int16_t font) 3155 { 3156 #ifdef SMOOTH_FONT 3157 if(fontLoaded) return gFont.yAdvance; 3158 #endif 3159 3160 #ifdef LOAD_GFXFF 3161 if (font==1) { 3162 if(gfxFont) { // New font 3163 return pgm_read_byte(&gfxFont->yAdvance) * textsize; 3164 } 3165 } 3166 #endif 3167 return pgm_read_byte( &fontdata[font].height ) * textsize; 3168 } 3169 3170 int16_t TFT_eSPI::fontHeight(void) 3171 { 3172 return fontHeight(textfont); 3173 } 3174 3175 /*************************************************************************************** 3176 ** Function name: drawChar 3177 ** Description: draw a single character in the GLCD or GFXFF font 3178 ***************************************************************************************/ 3179 void TFT_eSPI::drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32_t bg, uint8_t size) 3180 { 3181 if (_vpOoB) return; 3182 3183 #ifdef LOAD_GLCD 3184 //>>>>>>>>>>>>>>>>>> 3185 #ifdef LOAD_GFXFF 3186 if(!gfxFont) { // 'Classic' built-in GLCD font 3187 #endif 3188 //>>>>>>>>>>>>>>>>>> 3189 3190 if (c > 255) return; 3191 3192 int32_t xd = x + _xDatum; 3193 int32_t yd = y + _yDatum; 3194 3195 if ((xd >= _vpW) || // Clip right 3196 ( yd >= _vpH) || // Clip bottom 3197 ((xd + 6 * size - 1) < _vpX) || // Clip left 3198 ((yd + 8 * size - 1) < _vpY)) // Clip top 3199 return; 3200 3201 bool fillbg = (bg != color); 3202 bool clip = xd < _vpX || xd + 6 * textsize >= _vpW || yd < _vpY || yd + 8 * textsize >= _vpH; 3203 3204 if ((size==1) && fillbg && !clip) { 3205 uint8_t column[6]; 3206 uint8_t mask = 0x1; 3207 begin_tft_write(); 3208 3209 setWindow(xd, yd, xd+5, yd+7); 3210 3211 for (int8_t i = 0; i < 5; i++ ) column[i] = pgm_read_byte(font + (c * 5) + i); 3212 column[5] = 0; 3213 3214 for (int8_t j = 0; j < 8; j++) { 3215 for (int8_t k = 0; k < 5; k++ ) { 3216 if (column[k] & mask) {tft_Write_16(color);} 3217 else {tft_Write_16(bg);} 3218 } 3219 mask <<= 1; 3220 tft_Write_16(bg); 3221 } 3222 3223 end_tft_write(); 3224 } 3225 else { 3226 //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() 3227 inTransaction = true; 3228 3229 for (int8_t i = 0; i < 6; i++ ) { 3230 uint8_t line; 3231 if (i == 5) 3232 line = 0x0; 3233 else 3234 line = pgm_read_byte(font + (c * 5) + i); 3235 3236 if (size == 1 && !fillbg) { // default size 3237 for (int8_t j = 0; j < 8; j++) { 3238 if (line & 0x1) drawPixel(x + i, y + j, color); 3239 line >>= 1; 3240 } 3241 } 3242 else { // big size or clipped 3243 for (int8_t j = 0; j < 8; j++) { 3244 if (line & 0x1) fillRect(x + (i * size), y + (j * size), size, size, color); 3245 else if (fillbg) fillRect(x + i * size, y + j * size, size, size, bg); 3246 line >>= 1; 3247 } 3248 } 3249 } 3250 inTransaction = lockTransaction; 3251 end_tft_write(); // Does nothing if Sprite class uses this function 3252 } 3253 3254 //>>>>>>>>>>>>>>>>>>>>>>>>>>> 3255 #ifdef LOAD_GFXFF 3256 } else { // Custom font 3257 #endif 3258 //>>>>>>>>>>>>>>>>>>>>>>>>>>> 3259 #endif // LOAD_GLCD 3260 3261 #ifdef LOAD_GFXFF 3262 // Filter out bad characters not present in font 3263 if ((c >= pgm_read_word(&gfxFont->first)) && (c <= pgm_read_word(&gfxFont->last ))) { 3264 //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() 3265 inTransaction = true; 3266 //>>>>>>>>>>>>>>>>>>>>>>>>>>> 3267 3268 c -= pgm_read_word(&gfxFont->first); 3269 GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]); 3270 uint8_t *bitmap = (uint8_t *)pgm_read_dword(&gfxFont->bitmap); 3271 3272 uint32_t bo = pgm_read_word(&glyph->bitmapOffset); 3273 uint8_t w = pgm_read_byte(&glyph->width), 3274 h = pgm_read_byte(&glyph->height); 3275 //xa = pgm_read_byte(&glyph->xAdvance); 3276 int8_t xo = pgm_read_byte(&glyph->xOffset), 3277 yo = pgm_read_byte(&glyph->yOffset); 3278 uint8_t xx, yy, bits=0, bit=0; 3279 int16_t xo16 = 0, yo16 = 0; 3280 3281 if(size > 1) { 3282 xo16 = xo; 3283 yo16 = yo; 3284 } 3285 3286 // GFXFF rendering speed up 3287 uint16_t hpc = 0; // Horizontal foreground pixel count 3288 for(yy=0; yy<h; yy++) { 3289 for(xx=0; xx<w; xx++) { 3290 if(bit == 0) { 3291 bits = pgm_read_byte(&bitmap[bo++]); 3292 bit = 0x80; 3293 } 3294 if(bits & bit) hpc++; 3295 else { 3296 if (hpc) { 3297 if(size == 1) drawFastHLine(x+xo+xx-hpc, y+yo+yy, hpc, color); 3298 else fillRect(x+(xo16+xx-hpc)*size, y+(yo16+yy)*size, size*hpc, size, color); 3299 hpc=0; 3300 } 3301 } 3302 bit >>= 1; 3303 } 3304 // Draw pixels for this line as we are about to increment yy 3305 if (hpc) { 3306 if(size == 1) drawFastHLine(x+xo+xx-hpc, y+yo+yy, hpc, color); 3307 else fillRect(x+(xo16+xx-hpc)*size, y+(yo16+yy)*size, size*hpc, size, color); 3308 hpc=0; 3309 } 3310 } 3311 3312 inTransaction = lockTransaction; 3313 end_tft_write(); // Does nothing if Sprite class uses this function 3314 } 3315 #endif 3316 3317 #ifdef LOAD_GLCD 3318 #ifdef LOAD_GFXFF 3319 } // End classic vs custom font 3320 #endif 3321 #else 3322 #ifndef LOAD_GFXFF 3323 // Avoid warnings if fonts are disabled 3324 x = x; 3325 y = y; 3326 color = color; 3327 bg = bg; 3328 size = size; 3329 #endif 3330 #endif 3331 3332 } 3333 3334 3335 /*************************************************************************************** 3336 ** Function name: setAddrWindow 3337 ** Description: define an area to receive a stream of pixels 3338 ***************************************************************************************/ 3339 // Chip select is high at the end of this function 3340 void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t w, int32_t h) 3341 { 3342 begin_tft_write(); 3343 3344 setWindow(x0, y0, x0 + w - 1, y0 + h - 1); 3345 3346 end_tft_write(); 3347 } 3348 3349 3350 /*************************************************************************************** 3351 ** Function name: setWindow 3352 ** Description: define an area to receive a stream of pixels 3353 ***************************************************************************************/ 3354 // Chip select stays low, call begin_tft_write first. Use setAddrWindow() from sketches 3355 void TFT_eSPI::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) 3356 { 3357 //begin_tft_write(); // Must be called before setWindow 3358 addr_row = 0xFFFF; 3359 addr_col = 0xFFFF; 3360 3361 #if defined (ILI9225_DRIVER) 3362 if (rotation & 0x01) { transpose(x0, y0); transpose(x1, y1); } 3363 SPI_BUSY_CHECK; 3364 DC_C; tft_Write_8(TFT_CASET1); 3365 DC_D; tft_Write_16(x0); 3366 DC_C; tft_Write_8(TFT_CASET2); 3367 DC_D; tft_Write_16(x1); 3368 3369 DC_C; tft_Write_8(TFT_PASET1); 3370 DC_D; tft_Write_16(y0); 3371 DC_C; tft_Write_8(TFT_PASET2); 3372 DC_D; tft_Write_16(y1); 3373 3374 DC_C; tft_Write_8(TFT_RAM_ADDR1); 3375 DC_D; tft_Write_16(x0); 3376 DC_C; tft_Write_8(TFT_RAM_ADDR2); 3377 DC_D; tft_Write_16(y0); 3378 3379 // write to RAM 3380 DC_C; tft_Write_8(TFT_RAMWR); 3381 DC_D; 3382 // Temporary solution is to include the RP2040 code here 3383 #if (defined(ARDUINO_ARCH_RP2040) || defined (ARDUINO_ARCH_MBED)) && !defined(RP2040_PIO_INTERFACE) 3384 // For ILI9225 and RP2040 the slower Arduino SPI transfer calls were used, so need to swap back to 16 bit mode 3385 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 3386 hw_write_masked(&spi_get_hw(SPI_X)->cr0, (16 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); 3387 #endif 3388 #elif defined (SSD1351_DRIVER) 3389 if (rotation & 1) { 3390 transpose(x0, y0); 3391 transpose(x1, y1); 3392 } 3393 SPI_BUSY_CHECK; 3394 DC_C; tft_Write_8(TFT_CASET); 3395 DC_D; tft_Write_16(x1 | (x0 << 8)); 3396 DC_C; tft_Write_8(TFT_PASET); 3397 DC_D; tft_Write_16(y1 | (y0 << 8)); 3398 DC_C; tft_Write_8(TFT_RAMWR); 3399 DC_D; 3400 #else 3401 #if defined (SSD1963_DRIVER) 3402 if ((rotation & 0x1) == 0) { transpose(x0, y0); transpose(x1, y1); } 3403 #endif 3404 3405 #ifdef CGRAM_OFFSET 3406 x0+=colstart; 3407 x1+=colstart; 3408 y0+=rowstart; 3409 y1+=rowstart; 3410 #endif 3411 3412 // Temporary solution is to include the RP2040 optimised code here 3413 #if (defined(ARDUINO_ARCH_RP2040) || defined (ARDUINO_ARCH_MBED)) 3414 #if !defined(RP2040_PIO_INTERFACE) 3415 // Use hardware SPI port, this code does not swap from 8 to 16 bit 3416 // to avoid the spi_set_format() call overhead 3417 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 3418 DC_C; 3419 #if !defined (SPI_18BIT_DRIVER) 3420 #if defined (RPI_DISPLAY_TYPE) // RPi TFT type always needs 16 bit transfers 3421 hw_write_masked(&spi_get_hw(SPI_X)->cr0, (16 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); 3422 #else 3423 hw_write_masked(&spi_get_hw(SPI_X)->cr0, (8 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); 3424 #endif 3425 #endif 3426 spi_get_hw(SPI_X)->dr = (uint32_t)TFT_CASET; 3427 3428 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 3429 DC_D; 3430 spi_get_hw(SPI_X)->dr = (uint32_t)x0>>8; 3431 spi_get_hw(SPI_X)->dr = (uint32_t)x0; 3432 spi_get_hw(SPI_X)->dr = (uint32_t)x1>>8; 3433 spi_get_hw(SPI_X)->dr = (uint32_t)x1; 3434 3435 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 3436 DC_C; 3437 spi_get_hw(SPI_X)->dr = (uint32_t)TFT_PASET; 3438 3439 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 3440 DC_D; 3441 spi_get_hw(SPI_X)->dr = (uint32_t)y0>>8; 3442 spi_get_hw(SPI_X)->dr = (uint32_t)y0; 3443 spi_get_hw(SPI_X)->dr = (uint32_t)y1>>8; 3444 spi_get_hw(SPI_X)->dr = (uint32_t)y1; 3445 3446 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 3447 DC_C; 3448 spi_get_hw(SPI_X)->dr = (uint32_t)TFT_RAMWR; 3449 3450 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 3451 #if !defined (SPI_18BIT_DRIVER) 3452 hw_write_masked(&spi_get_hw(SPI_X)->cr0, (16 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); 3453 #endif 3454 DC_D; 3455 #elif defined (RM68120_DRIVER) 3456 DC_C; tft_Write_16(TFT_CASET+0); DC_D; tft_Write_16(x0 >> 8); 3457 DC_C; tft_Write_16(TFT_CASET+1); DC_D; tft_Write_16(x0 & 0xFF); 3458 DC_C; tft_Write_16(TFT_CASET+2); DC_D; tft_Write_16(x1 >> 8); 3459 DC_C; tft_Write_16(TFT_CASET+3); DC_D; tft_Write_16(x1 & 0xFF); 3460 DC_C; tft_Write_16(TFT_PASET+0); DC_D; tft_Write_16(y0 >> 8); 3461 DC_C; tft_Write_16(TFT_PASET+1); DC_D; tft_Write_16(y0 & 0xFF); 3462 DC_C; tft_Write_16(TFT_PASET+2); DC_D; tft_Write_16(y1 >> 8); 3463 DC_C; tft_Write_16(TFT_PASET+3); DC_D; tft_Write_16(y1 & 0xFF); 3464 3465 DC_C; tft_Write_16(TFT_RAMWR); 3466 DC_D; 3467 #else 3468 // This is for the RP2040 and PIO interface (SPI or parallel) 3469 WAIT_FOR_STALL; 3470 tft_pio->sm[pio_sm].instr = pio_instr_addr; 3471 3472 TX_FIFO = TFT_CASET; 3473 TX_FIFO = (x0<<16) | x1; 3474 TX_FIFO = TFT_PASET; 3475 TX_FIFO = (y0<<16) | y1; 3476 TX_FIFO = TFT_RAMWR; 3477 #endif 3478 #else 3479 SPI_BUSY_CHECK; 3480 DC_C; tft_Write_8(TFT_CASET); 3481 DC_D; tft_Write_32C(x0, x1); 3482 DC_C; tft_Write_8(TFT_PASET); 3483 DC_D; tft_Write_32C(y0, y1); 3484 DC_C; tft_Write_8(TFT_RAMWR); 3485 DC_D; 3486 #endif // RP2040 SPI 3487 #endif 3488 //end_tft_write(); // Must be called after setWindow 3489 } 3490 3491 /*************************************************************************************** 3492 ** Function name: readAddrWindow 3493 ** Description: define an area to read a stream of pixels 3494 ***************************************************************************************/ 3495 void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h) 3496 { 3497 //begin_tft_write(); // Must be called before readAddrWindow or CS set low 3498 3499 int32_t xe = xs + w - 1; 3500 int32_t ye = ys + h - 1; 3501 3502 addr_col = 0xFFFF; 3503 addr_row = 0xFFFF; 3504 3505 #if defined (SSD1963_DRIVER) 3506 if ((rotation & 0x1) == 0) { transpose(xs, ys); transpose(xe, ye); } 3507 #endif 3508 3509 #ifdef CGRAM_OFFSET 3510 xs += colstart; 3511 xe += colstart; 3512 ys += rowstart; 3513 ye += rowstart; 3514 #endif 3515 3516 // Temporary solution is to include the RP2040 optimised code here 3517 #if (defined(ARDUINO_ARCH_RP2040) || defined (ARDUINO_ARCH_MBED)) && !defined(RP2040_PIO_INTERFACE) 3518 // Use hardware SPI port, this code does not swap from 8 to 16 bit 3519 // to avoid the spi_set_format() call overhead 3520 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 3521 DC_C; 3522 hw_write_masked(&spi_get_hw(SPI_X)->cr0, (8 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); 3523 spi_get_hw(SPI_X)->dr = (uint32_t)TFT_CASET; 3524 3525 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 3526 DC_D; 3527 spi_get_hw(SPI_X)->dr = (uint32_t)xs>>8; 3528 spi_get_hw(SPI_X)->dr = (uint32_t)xs; 3529 spi_get_hw(SPI_X)->dr = (uint32_t)xe>>8; 3530 spi_get_hw(SPI_X)->dr = (uint32_t)xe; 3531 3532 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 3533 DC_C; 3534 spi_get_hw(SPI_X)->dr = (uint32_t)TFT_PASET; 3535 3536 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 3537 DC_D; 3538 spi_get_hw(SPI_X)->dr = (uint32_t)ys>>8; 3539 spi_get_hw(SPI_X)->dr = (uint32_t)ys; 3540 spi_get_hw(SPI_X)->dr = (uint32_t)ye>>8; 3541 spi_get_hw(SPI_X)->dr = (uint32_t)ye; 3542 3543 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 3544 DC_C; 3545 spi_get_hw(SPI_X)->dr = (uint32_t)TFT_RAMRD; 3546 3547 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 3548 DC_D; 3549 3550 // Flush the rx buffer and reset overflow flag 3551 while (spi_is_readable(SPI_X)) (void)spi_get_hw(SPI_X)->dr; 3552 spi_get_hw(SPI_X)->icr = SPI_SSPICR_RORIC_BITS; 3553 3554 #else 3555 // Column addr set 3556 DC_C; tft_Write_8(TFT_CASET); 3557 DC_D; tft_Write_32C(xs, xe); 3558 3559 // Row addr set 3560 DC_C; tft_Write_8(TFT_PASET); 3561 DC_D; tft_Write_32C(ys, ye); 3562 3563 // Read CGRAM command 3564 DC_C; tft_Write_8(TFT_RAMRD); 3565 3566 DC_D; 3567 #endif // RP2040 SPI 3568 3569 //end_tft_write(); // Must be called after readAddrWindow or CS set high 3570 } 3571 3572 3573 /*************************************************************************************** 3574 ** Function name: drawPixel 3575 ** Description: push a single pixel at an arbitrary position 3576 ***************************************************************************************/ 3577 void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) 3578 { 3579 if (_vpOoB) return; 3580 3581 x+= _xDatum; 3582 y+= _yDatum; 3583 3584 // Range checking 3585 if ((x < _vpX) || (y < _vpY) ||(x >= _vpW) || (y >= _vpH)) return; 3586 3587 #ifdef CGRAM_OFFSET 3588 x+=colstart; 3589 y+=rowstart; 3590 #endif 3591 3592 #if (defined (MULTI_TFT_SUPPORT) || defined (GC9A01_DRIVER)) && !defined (ILI9225_DRIVER) 3593 addr_row = 0xFFFF; 3594 addr_col = 0xFFFF; 3595 #endif 3596 3597 begin_tft_write(); 3598 3599 #if defined (ILI9225_DRIVER) 3600 if (rotation & 0x01) { transpose(x, y); } 3601 SPI_BUSY_CHECK; 3602 3603 // Set window to full screen to optimise sequential pixel rendering 3604 if (addr_row != 0x9225) { 3605 addr_row = 0x9225; // addr_row used for flag 3606 DC_C; tft_Write_8(TFT_CASET1); 3607 DC_D; tft_Write_16(0); 3608 DC_C; tft_Write_8(TFT_CASET2); 3609 DC_D; tft_Write_16(175); 3610 3611 DC_C; tft_Write_8(TFT_PASET1); 3612 DC_D; tft_Write_16(0); 3613 DC_C; tft_Write_8(TFT_PASET2); 3614 DC_D; tft_Write_16(219); 3615 } 3616 3617 // Define pixel coordinate 3618 DC_C; tft_Write_8(TFT_RAM_ADDR1); 3619 DC_D; tft_Write_16(x); 3620 DC_C; tft_Write_8(TFT_RAM_ADDR2); 3621 DC_D; tft_Write_16(y); 3622 3623 // write to RAM 3624 DC_C; tft_Write_8(TFT_RAMWR); 3625 #if defined(TFT_PARALLEL_8_BIT) || defined(TFT_PARALLEL_16_BIT) || !defined(ESP32) 3626 DC_D; tft_Write_16(color); 3627 #else 3628 DC_D; tft_Write_16N(color); 3629 #endif 3630 3631 // Temporary solution is to include the RP2040 optimised code here 3632 #elif (defined (ARDUINO_ARCH_RP2040) || defined (ARDUINO_ARCH_MBED)) && !defined (SSD1351_DRIVER) 3633 3634 #if defined (SSD1963_DRIVER) 3635 if ((rotation & 0x1) == 0) { transpose(x, y); } 3636 #endif 3637 3638 #if !defined(RP2040_PIO_INTERFACE) 3639 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 3640 3641 #if defined (RPI_DISPLAY_TYPE) // RPi TFT type always needs 16 bit transfers 3642 hw_write_masked(&spi_get_hw(SPI_X)->cr0, (16 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); 3643 #else 3644 hw_write_masked(&spi_get_hw(SPI_X)->cr0, (8 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); 3645 #endif 3646 3647 if (addr_col != x) { 3648 DC_C; 3649 spi_get_hw(SPI_X)->dr = (uint32_t)TFT_CASET; 3650 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS){}; 3651 DC_D; 3652 spi_get_hw(SPI_X)->dr = (uint32_t)x>>8; 3653 spi_get_hw(SPI_X)->dr = (uint32_t)x; 3654 spi_get_hw(SPI_X)->dr = (uint32_t)x>>8; 3655 spi_get_hw(SPI_X)->dr = (uint32_t)x; 3656 addr_col = x; 3657 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 3658 } 3659 3660 if (addr_row != y) { 3661 DC_C; 3662 spi_get_hw(SPI_X)->dr = (uint32_t)TFT_PASET; 3663 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 3664 DC_D; 3665 spi_get_hw(SPI_X)->dr = (uint32_t)y>>8; 3666 spi_get_hw(SPI_X)->dr = (uint32_t)y; 3667 spi_get_hw(SPI_X)->dr = (uint32_t)y>>8; 3668 spi_get_hw(SPI_X)->dr = (uint32_t)y; 3669 addr_row = y; 3670 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 3671 } 3672 3673 DC_C; 3674 spi_get_hw(SPI_X)->dr = (uint32_t)TFT_RAMWR; 3675 3676 #if defined (SPI_18BIT_DRIVER) // SPI 18 bit colour 3677 uint8_t r = (color & 0xF800)>>8; 3678 uint8_t g = (color & 0x07E0)>>3; 3679 uint8_t b = (color & 0x001F)<<3; 3680 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 3681 DC_D; 3682 tft_Write_8N(r); tft_Write_8N(g); tft_Write_8N(b); 3683 #else 3684 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 3685 DC_D; 3686 #if defined (RPI_DISPLAY_TYPE) // RPi TFT type always needs 16 bit transfers 3687 spi_get_hw(SPI_X)->dr = (uint32_t)color; 3688 #else 3689 spi_get_hw(SPI_X)->dr = (uint32_t)color>>8; 3690 spi_get_hw(SPI_X)->dr = (uint32_t)color; 3691 #endif 3692 #endif 3693 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 3694 #elif defined (RM68120_DRIVER) 3695 if (addr_col != x) { 3696 DC_C; tft_Write_16(TFT_CASET+0); DC_D; tft_Write_16(x >> 8); 3697 DC_C; tft_Write_16(TFT_CASET+1); DC_D; tft_Write_16(x & 0xFF); 3698 DC_C; tft_Write_16(TFT_CASET+2); DC_D; tft_Write_16(x >> 8); 3699 DC_C; tft_Write_16(TFT_CASET+3); DC_D; tft_Write_16(x & 0xFF); 3700 addr_col = x; 3701 } 3702 if (addr_row != y) { 3703 DC_C; tft_Write_16(TFT_PASET+0); DC_D; tft_Write_16(y >> 8); 3704 DC_C; tft_Write_16(TFT_PASET+1); DC_D; tft_Write_16(y & 0xFF); 3705 DC_C; tft_Write_16(TFT_PASET+2); DC_D; tft_Write_16(y >> 8); 3706 DC_C; tft_Write_16(TFT_PASET+3); DC_D; tft_Write_16(y & 0xFF); 3707 addr_row = y; 3708 } 3709 DC_C; tft_Write_16(TFT_RAMWR); DC_D; 3710 3711 TX_FIFO = color; 3712 #else 3713 // This is for the RP2040 and PIO interface (SPI or parallel) 3714 WAIT_FOR_STALL; 3715 tft_pio->sm[pio_sm].instr = pio_instr_addr; 3716 TX_FIFO = TFT_CASET; 3717 TX_FIFO = (x<<16) | x; 3718 TX_FIFO = TFT_PASET; 3719 TX_FIFO = (y<<16) | y; 3720 TX_FIFO = TFT_RAMWR; 3721 //DC set high by PIO 3722 #if defined (SPI_18BIT_DRIVER) || (defined (SSD1963_DRIVER) && defined (TFT_PARALLEL_8_BIT)) 3723 TX_FIFO = ((color & 0xF800)<<8) | ((color & 0x07E0)<<5) | ((color & 0x001F)<<3); 3724 #else 3725 TX_FIFO = color; 3726 #endif 3727 3728 #endif 3729 3730 #else 3731 3732 #if defined (SSD1963_DRIVER) 3733 if ((rotation & 0x1) == 0) { transpose(x, y); } 3734 #endif 3735 3736 SPI_BUSY_CHECK; 3737 3738 #if defined (SSD1351_DRIVER) 3739 if (rotation & 0x1) { transpose(x, y); } 3740 // No need to send x if it has not changed (speeds things up) 3741 if (addr_col != x) { 3742 DC_C; tft_Write_8(TFT_CASET); 3743 DC_D; tft_Write_16(x | (x << 8)); 3744 addr_col = x; 3745 } 3746 3747 // No need to send y if it has not changed (speeds things up) 3748 if (addr_row != y) { 3749 DC_C; tft_Write_8(TFT_PASET); 3750 DC_D; tft_Write_16(y | (y << 8)); 3751 addr_row = y; 3752 } 3753 #else 3754 // No need to send x if it has not changed (speeds things up) 3755 if (addr_col != x) { 3756 DC_C; tft_Write_8(TFT_CASET); 3757 DC_D; tft_Write_32D(x); 3758 addr_col = x; 3759 } 3760 3761 // No need to send y if it has not changed (speeds things up) 3762 if (addr_row != y) { 3763 DC_C; tft_Write_8(TFT_PASET); 3764 DC_D; tft_Write_32D(y); 3765 addr_row = y; 3766 } 3767 #endif 3768 3769 DC_C; tft_Write_8(TFT_RAMWR); 3770 3771 #if defined(TFT_PARALLEL_8_BIT) || defined(TFT_PARALLEL_16_BIT) || !defined(ESP32) 3772 DC_D; tft_Write_16(color); 3773 #else 3774 DC_D; tft_Write_16N(color); 3775 #endif 3776 #endif 3777 3778 end_tft_write(); 3779 } 3780 3781 /*************************************************************************************** 3782 ** Function name: pushColor 3783 ** Description: push a single pixel 3784 ***************************************************************************************/ 3785 void TFT_eSPI::pushColor(uint16_t color) 3786 { 3787 begin_tft_write(); 3788 3789 SPI_BUSY_CHECK; 3790 tft_Write_16N(color); 3791 3792 end_tft_write(); 3793 } 3794 3795 3796 /*************************************************************************************** 3797 ** Function name: pushColor 3798 ** Description: push a single colour to "len" pixels 3799 ***************************************************************************************/ 3800 void TFT_eSPI::pushColor(uint16_t color, uint32_t len) 3801 { 3802 begin_tft_write(); 3803 3804 pushBlock(color, len); 3805 3806 end_tft_write(); 3807 } 3808 3809 /*************************************************************************************** 3810 ** Function name: startWrite 3811 ** Description: begin transaction with CS low, MUST later call endWrite 3812 ***************************************************************************************/ 3813 void TFT_eSPI::startWrite(void) 3814 { 3815 begin_tft_write(); 3816 lockTransaction = true; // Lock transaction for all sequentially run sketch functions 3817 inTransaction = true; 3818 } 3819 3820 /*************************************************************************************** 3821 ** Function name: endWrite 3822 ** Description: end transaction with CS high 3823 ***************************************************************************************/ 3824 void TFT_eSPI::endWrite(void) 3825 { 3826 lockTransaction = false; // Release sketch induced transaction lock 3827 inTransaction = false; 3828 DMA_BUSY_CHECK; // Safety check - user code should have checked this! 3829 end_tft_write(); // Release SPI bus 3830 } 3831 3832 /*************************************************************************************** 3833 ** Function name: writeColor (use startWrite() and endWrite() before & after) 3834 ** Description: raw write of "len" pixels avoiding transaction check 3835 ***************************************************************************************/ 3836 void TFT_eSPI::writeColor(uint16_t color, uint32_t len) 3837 { 3838 pushBlock(color, len); 3839 } 3840 3841 /*************************************************************************************** 3842 ** Function name: pushColors 3843 ** Description: push an array of pixels for 16 bit raw image drawing 3844 ***************************************************************************************/ 3845 // Assumed that setAddrWindow() has previously been called 3846 // len is number of bytes, not pixels 3847 void TFT_eSPI::pushColors(uint8_t *data, uint32_t len) 3848 { 3849 begin_tft_write(); 3850 3851 pushPixels(data, len>>1); 3852 3853 end_tft_write(); 3854 } 3855 3856 3857 /*************************************************************************************** 3858 ** Function name: pushColors 3859 ** Description: push an array of pixels, for image drawing 3860 ***************************************************************************************/ 3861 void TFT_eSPI::pushColors(uint16_t *data, uint32_t len, bool swap) 3862 { 3863 begin_tft_write(); 3864 if (swap) {swap = _swapBytes; _swapBytes = true; } 3865 3866 pushPixels(data, len); 3867 3868 _swapBytes = swap; // Restore old value 3869 end_tft_write(); 3870 } 3871 3872 3873 /*************************************************************************************** 3874 ** Function name: drawLine 3875 ** Description: draw a line between 2 arbitrary points 3876 ***************************************************************************************/ 3877 // Bresenham's algorithm - thx wikipedia - speed enhanced by Bodmer to use 3878 // an efficient FastH/V Line draw routine for line segments of 2 pixels or more 3879 void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color) 3880 { 3881 if (_vpOoB) return; 3882 3883 //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() 3884 inTransaction = true; 3885 3886 //x+= _xDatum; // Not added here, added by drawPixel & drawFastXLine 3887 //y+= _yDatum; 3888 3889 bool steep = abs(y1 - y0) > abs(x1 - x0); 3890 if (steep) { 3891 transpose(x0, y0); 3892 transpose(x1, y1); 3893 } 3894 3895 if (x0 > x1) { 3896 transpose(x0, x1); 3897 transpose(y0, y1); 3898 } 3899 3900 int32_t dx = x1 - x0, dy = abs(y1 - y0);; 3901 3902 int32_t err = dx >> 1, ystep = -1, xs = x0, dlen = 0; 3903 3904 if (y0 < y1) ystep = 1; 3905 3906 // Split into steep and not steep for FastH/V separation 3907 if (steep) { 3908 for (; x0 <= x1; x0++) { 3909 dlen++; 3910 err -= dy; 3911 if (err < 0) { 3912 if (dlen == 1) drawPixel(y0, xs, color); 3913 else drawFastVLine(y0, xs, dlen, color); 3914 dlen = 0; 3915 y0 += ystep; xs = x0 + 1; 3916 err += dx; 3917 } 3918 } 3919 if (dlen) drawFastVLine(y0, xs, dlen, color); 3920 } 3921 else 3922 { 3923 for (; x0 <= x1; x0++) { 3924 dlen++; 3925 err -= dy; 3926 if (err < 0) { 3927 if (dlen == 1) drawPixel(xs, y0, color); 3928 else drawFastHLine(xs, y0, dlen, color); 3929 dlen = 0; 3930 y0 += ystep; xs = x0 + 1; 3931 err += dx; 3932 } 3933 } 3934 if (dlen) drawFastHLine(xs, y0, dlen, color); 3935 } 3936 3937 inTransaction = lockTransaction; 3938 end_tft_write(); 3939 } 3940 3941 3942 /*************************************************************************************** 3943 ** Description: Constants for anti-aliased line drawing on TFT and in Sprites 3944 ***************************************************************************************/ 3945 constexpr float PixelAlphaGain = 255.0; 3946 constexpr float LoAlphaTheshold = 1.0/32.0; 3947 constexpr float HiAlphaTheshold = 1.0 - LoAlphaTheshold; 3948 constexpr float deg2rad = 3.14159265359/180.0; 3949 3950 /*************************************************************************************** 3951 ** Function name: drawPixel (alpha blended) 3952 ** Description: Draw a pixel blended with the screen or bg pixel colour 3953 ***************************************************************************************/ 3954 uint16_t TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color, uint8_t alpha, uint32_t bg_color) 3955 { 3956 if (bg_color == 0x00FFFFFF) bg_color = readPixel(x, y); 3957 color = fastBlend(alpha, color, bg_color); 3958 drawPixel(x, y, color); 3959 return color; 3960 } 3961 3962 3963 /*************************************************************************************** 3964 ** Function name: drawSmoothArc 3965 ** Description: Draw a smooth arc clockwise from 6 o'clock 3966 ***************************************************************************************/ 3967 void TFT_eSPI::drawSmoothArc(int32_t x, int32_t y, int32_t r, int32_t ir, uint32_t startAngle, uint32_t endAngle, uint32_t fg_color, uint32_t bg_color, bool roundEnds) 3968 // Centre at x,y 3969 // r = arc outer radius, ir = arc inner radius. Inclusive so arc thickness = r - ir + 1 3970 // Angles in range 0-360 3971 // Arc foreground colour anti-aliased with background colour at edges 3972 // anti-aliased roundEnd is optional, default is anti-aliased straight end 3973 // Note: rounded ends extend the arc angle so can overlap, user sketch to manage this. 3974 { 3975 inTransaction = true; 3976 3977 if (endAngle != startAngle && (startAngle != 0 || endAngle != 360)) 3978 { 3979 float sx = -sinf(startAngle * deg2rad); 3980 float sy = +cosf(startAngle * deg2rad); 3981 float ex = -sinf( endAngle * deg2rad); 3982 float ey = +cosf( endAngle * deg2rad); 3983 3984 if (roundEnds) 3985 { // Round ends 3986 sx = sx * (r + ir)/2.0 + x; 3987 sy = sy * (r + ir)/2.0 + y; 3988 drawSpot(sx, sy, (r - ir)/2.0, fg_color, bg_color); 3989 3990 ex = ex * (r + ir)/2.0 + x; 3991 ey = ey * (r + ir)/2.0 + y; 3992 drawSpot(ex, ey, (r - ir)/2.0, fg_color, bg_color); 3993 } 3994 else 3995 { // Square ends 3996 float asx = sx * ir + x; 3997 float asy = sy * ir + y; 3998 float aex = sx * r + x; 3999 float aey = sy * r + y; 4000 drawWedgeLine(asx, asy, aex, aey, 0.3, 0.3, fg_color, bg_color); 4001 4002 asx = ex * ir + x; 4003 asy = ey * ir + y; 4004 aex = ex * r + x; 4005 aey = ey * r + y; 4006 drawWedgeLine(asx, asy, aex, aey, 0.3, 0.3, fg_color, bg_color); 4007 } 4008 4009 // Draw arc 4010 drawArc(x, y, r, ir, startAngle, endAngle, fg_color, bg_color); 4011 4012 } 4013 else // Draw full 360 4014 { 4015 drawArc(x, y, r, ir, 0, 360, fg_color, bg_color); 4016 } 4017 4018 inTransaction = lockTransaction; 4019 end_tft_write(); 4020 } 4021 4022 /*************************************************************************************** 4023 ** Function name: sqrt_fraction (private function) 4024 ** Description: Smooth graphics support function for alpha derivation 4025 ***************************************************************************************/ 4026 // Compute the fixed point square root of an integer and 4027 // return the 8 MS bits of fractional part. 4028 // Quicker than sqrt() for processors that do not have an FPU (e.g. RP2040) 4029 inline uint8_t TFT_eSPI::sqrt_fraction(uint32_t num) { 4030 if (num > (0x40000000)) return 0; 4031 uint32_t bsh = 0x00004000; 4032 uint32_t fpr = 0; 4033 uint32_t osh = 0; 4034 4035 // Auto adjust from U8:8 up to U15:16 4036 while (num>bsh) {bsh <<= 2; osh++;} 4037 4038 do { 4039 uint32_t bod = bsh + fpr; 4040 if(num >= bod) 4041 { 4042 num -= bod; 4043 fpr = bsh + bod; 4044 } 4045 num <<= 1; 4046 } while(bsh >>= 1); 4047 4048 return fpr>>osh; 4049 } 4050 4051 /*************************************************************************************** 4052 ** Function name: drawArc 4053 ** Description: Draw an arc clockwise from 6 o'clock position 4054 ***************************************************************************************/ 4055 // Centre at x,y 4056 // r = arc outer radius, ir = arc inner radius. Inclusive, so arc thickness = r-ir+1 4057 // Angles MUST be in range 0-360 4058 // Arc foreground fg_color anti-aliased with background colour along sides 4059 // smooth is optional, default is true, smooth=false means no antialiasing 4060 // Note: Arc ends are not anti-aliased (use drawSmoothArc instead for that) 4061 void TFT_eSPI::drawArc(int32_t x, int32_t y, int32_t r, int32_t ir, 4062 uint32_t startAngle, uint32_t endAngle, 4063 uint32_t fg_color, uint32_t bg_color, 4064 bool smooth) 4065 { 4066 if (endAngle > 360) endAngle = 360; 4067 if (startAngle > 360) startAngle = 360; 4068 if (_vpOoB || startAngle == endAngle) return; 4069 if (r < ir) transpose(r, ir); // Required that r > ir 4070 if (r <= 0 || ir < 0) return; // Invalid r, ir can be zero (circle sector) 4071 4072 if (endAngle < startAngle) { 4073 // Arc sweeps through 6 o'clock so draw in two parts 4074 if (startAngle < 360) drawArc(x, y, r, ir, startAngle, 360, fg_color, bg_color, smooth); 4075 if (endAngle == 0) return; 4076 startAngle = 0; 4077 } 4078 inTransaction = true; 4079 4080 int32_t xs = 0; // x start position for quadrant scan 4081 uint8_t alpha = 0; // alpha value for blending pixels 4082 4083 uint32_t r2 = r * r; // Outer arc radius^2 4084 if (smooth) r++; // Outer AA zone radius 4085 uint32_t r1 = r * r; // Outer AA radius^2 4086 int16_t w = r - ir; // Width of arc (r - ir + 1) 4087 uint32_t r3 = ir * ir; // Inner arc radius^2 4088 if (smooth) ir--; // Inner AA zone radius 4089 uint32_t r4 = ir * ir; // Inner AA radius^2 4090 4091 // 1 | 2 4092 // ---¦--- Arc quadrant index 4093 // 0 | 3 4094 // Fixed point U16.16 slope table for arc start/end in each quadrant 4095 uint32_t startSlope[4] = {0, 0, 0xFFFFFFFF, 0}; 4096 uint32_t endSlope[4] = {0, 0xFFFFFFFF, 0, 0}; 4097 4098 // Ensure maximum U16.16 slope of arc ends is ~ 0x8000 0000 4099 constexpr float minDivisor = 1.0f/0x8000; 4100 4101 // Fill in start slope table and empty quadrants 4102 float fabscos = fabsf(cosf(startAngle * deg2rad)); 4103 float fabssin = fabsf(sinf(startAngle * deg2rad)); 4104 4105 // U16.16 slope of arc start 4106 uint32_t slope = (fabscos/(fabssin + minDivisor)) * (float)(1<<16); 4107 4108 // Update slope table, add slope for arc start 4109 if (startAngle <= 90) { 4110 startSlope[0] = slope; 4111 } 4112 else if (startAngle <= 180) { 4113 startSlope[1] = slope; 4114 } 4115 else if (startAngle <= 270) { 4116 startSlope[1] = 0xFFFFFFFF; 4117 startSlope[2] = slope; 4118 } 4119 else { 4120 startSlope[1] = 0xFFFFFFFF; 4121 startSlope[2] = 0; 4122 startSlope[3] = slope; 4123 } 4124 4125 // Fill in end slope table and empty quadrants 4126 fabscos = fabsf(cosf(endAngle * deg2rad)); 4127 fabssin = fabsf(sinf(endAngle * deg2rad)); 4128 4129 // U16.16 slope of arc end 4130 slope = (uint32_t)((fabscos/(fabssin + minDivisor)) * (float)(1<<16)); 4131 4132 // Work out which quadrants will need to be drawn and add slope for arc end 4133 if (endAngle <= 90) { 4134 endSlope[0] = slope; 4135 endSlope[1] = 0; 4136 startSlope[2] = 0; 4137 } 4138 else if (endAngle <= 180) { 4139 endSlope[1] = slope; 4140 startSlope[2] = 0; 4141 } 4142 else if (endAngle <= 270) { 4143 endSlope[2] = slope; 4144 } 4145 else { 4146 endSlope[3] = slope; 4147 } 4148 4149 // Scan quadrant 4150 for (int32_t cy = r - 1; cy > 0; cy--) 4151 { 4152 uint32_t len[4] = { 0, 0, 0, 0}; // Pixel run length 4153 int32_t xst[4] = {-1, -1, -1, -1}; // Pixel run x start 4154 uint32_t dy2 = (r - cy) * (r - cy); 4155 4156 // Find and track arc zone start point 4157 while ((r - xs) * (r - xs) + dy2 >= r1) xs++; 4158 4159 for (int32_t cx = xs; cx < r; cx++) 4160 { 4161 // Calculate radius^2 4162 uint32_t hyp = (r - cx) * (r - cx) + dy2; 4163 4164 // If in outer zone calculate alpha 4165 if (hyp > r2) { 4166 alpha = ~sqrt_fraction(hyp); // Outer AA zone 4167 } 4168 // If within arc fill zone, get line start and lengths for each quadrant 4169 else if (hyp >= r3) { 4170 // Calculate U16.16 slope 4171 slope = ((r - cy) << 16)/(r - cx); 4172 if (slope <= startSlope[0] && slope >= endSlope[0]) { // slope hi -> lo 4173 xst[0] = cx; // Bottom left line end 4174 len[0]++; 4175 } 4176 if (slope >= startSlope[1] && slope <= endSlope[1]) { // slope lo -> hi 4177 xst[1] = cx; // Top left line end 4178 len[1]++; 4179 } 4180 if (slope <= startSlope[2] && slope >= endSlope[2]) { // slope hi -> lo 4181 xst[2] = cx; // Bottom right line start 4182 len[2]++; 4183 } 4184 if (slope <= endSlope[3] && slope >= startSlope[3]) { // slope lo -> hi 4185 xst[3] = cx; // Top right line start 4186 len[3]++; 4187 } 4188 continue; // Next x 4189 } 4190 else { 4191 if (hyp <= r4) break; // Skip inner pixels 4192 alpha = sqrt_fraction(hyp); // Inner AA zone 4193 } 4194 4195 if (alpha < 16) continue; // Skip low alpha pixels 4196 4197 // If background is read it must be done in each quadrant 4198 uint16_t pcol = fastBlend(alpha, fg_color, bg_color); 4199 // Check if an AA pixels need to be drawn 4200 slope = ((r - cy)<<16)/(r - cx); 4201 if (slope <= startSlope[0] && slope >= endSlope[0]) // BL 4202 drawPixel(x + cx - r, y - cy + r, pcol); 4203 if (slope >= startSlope[1] && slope <= endSlope[1]) // TL 4204 drawPixel(x + cx - r, y + cy - r, pcol); 4205 if (slope <= startSlope[2] && slope >= endSlope[2]) // TR 4206 drawPixel(x - cx + r, y + cy - r, pcol); 4207 if (slope <= endSlope[3] && slope >= startSlope[3]) // BR 4208 drawPixel(x - cx + r, y - cy + r, pcol); 4209 } 4210 // Add line in inner zone 4211 if (len[0]) drawFastHLine(x + xst[0] - len[0] + 1 - r, y - cy + r, len[0], fg_color); // BL 4212 if (len[1]) drawFastHLine(x + xst[1] - len[1] + 1 - r, y + cy - r, len[1], fg_color); // TL 4213 if (len[2]) drawFastHLine(x - xst[2] + r, y + cy - r, len[2], fg_color); // TR 4214 if (len[3]) drawFastHLine(x - xst[3] + r, y - cy + r, len[3], fg_color); // BR 4215 } 4216 4217 // Fill in centre lines 4218 if (startAngle == 0 || endAngle == 360) drawFastVLine(x, y + r - w, w, fg_color); // Bottom 4219 if (startAngle <= 90 && endAngle >= 90) drawFastHLine(x - r + 1, y, w, fg_color); // Left 4220 if (startAngle <= 180 && endAngle >= 180) drawFastVLine(x, y - r + 1, w, fg_color); // Top 4221 if (startAngle <= 270 && endAngle >= 270) drawFastHLine(x + r - w, y, w, fg_color); // Right 4222 4223 inTransaction = lockTransaction; 4224 end_tft_write(); 4225 } 4226 4227 /*************************************************************************************** 4228 ** Function name: drawSmoothCircle 4229 ** Description: Draw a smooth circle 4230 ***************************************************************************************/ 4231 // To have effective anti-aliasing the circle will be 3 pixels thick 4232 void TFT_eSPI::drawSmoothCircle(int32_t x, int32_t y, int32_t r, uint32_t fg_color, uint32_t bg_color) 4233 { 4234 drawSmoothRoundRect(x-r, y-r, r, r-1, 0, 0, fg_color, bg_color); 4235 } 4236 4237 /*************************************************************************************** 4238 ** Function name: fillSmoothCircle 4239 ** Description: Draw a filled anti-aliased circle 4240 ***************************************************************************************/ 4241 void TFT_eSPI::fillSmoothCircle(int32_t x, int32_t y, int32_t r, uint32_t color, uint32_t bg_color) 4242 { 4243 if (r <= 0) return; 4244 4245 inTransaction = true; 4246 4247 drawFastHLine(x - r, y, 2 * r + 1, color); 4248 int32_t xs = 1; 4249 int32_t cx = 0; 4250 4251 int32_t r1 = r * r; 4252 r++; 4253 int32_t r2 = r * r; 4254 4255 for (int32_t cy = r - 1; cy > 0; cy--) 4256 { 4257 int32_t dy2 = (r - cy) * (r - cy); 4258 for (cx = xs; cx < r; cx++) 4259 { 4260 int32_t hyp2 = (r - cx) * (r - cx) + dy2; 4261 if (hyp2 <= r1) break; 4262 if (hyp2 >= r2) continue; 4263 4264 uint8_t alpha = ~sqrt_fraction(hyp2); 4265 if (alpha > 246) break; 4266 xs = cx; 4267 if (alpha < 9) continue; 4268 4269 if (bg_color == 0x00FFFFFF) { 4270 drawPixel(x + cx - r, y + cy - r, color, alpha, bg_color); 4271 drawPixel(x - cx + r, y + cy - r, color, alpha, bg_color); 4272 drawPixel(x - cx + r, y - cy + r, color, alpha, bg_color); 4273 drawPixel(x + cx - r, y - cy + r, color, alpha, bg_color); 4274 } 4275 else { 4276 uint16_t pcol = drawPixel(x + cx - r, y + cy - r, color, alpha, bg_color); 4277 drawPixel(x - cx + r, y + cy - r, pcol); 4278 drawPixel(x - cx + r, y - cy + r, pcol); 4279 drawPixel(x + cx - r, y - cy + r, pcol); 4280 } 4281 } 4282 drawFastHLine(x + cx - r, y + cy - r, 2 * (r - cx) + 1, color); 4283 drawFastHLine(x + cx - r, y - cy + r, 2 * (r - cx) + 1, color); 4284 } 4285 inTransaction = lockTransaction; 4286 end_tft_write(); 4287 } 4288 4289 4290 /*************************************************************************************** 4291 ** Function name: drawSmoothRoundRect 4292 ** Description: Draw a rounded rectangle 4293 ***************************************************************************************/ 4294 // x,y is top left corner of bounding box for a complete rounded rectangle 4295 // r = arc outer corner radius, ir = arc inner radius. Arc thickness = r-ir+1 4296 // w and h are width and height of the bounding rectangle 4297 // If w and h are < radius (e.g. 0,0) a circle will be drawn with centre at x+r,y+r 4298 // Arc foreground fg_color anti-aliased with background colour at edges 4299 // A subset of corners can be drawn by specifying a quadrants mask. A bit set in the 4300 // mask means draw that quadrant (all are drawn if parameter missing): 4301 // 0x1 | 0x2 4302 // ---¦--- Arc quadrant mask select bits (as in drawCircleHelper fn) 4303 // 0x8 | 0x4 4304 void TFT_eSPI::drawSmoothRoundRect(int32_t x, int32_t y, int32_t r, int32_t ir, int32_t w, int32_t h, uint32_t fg_color, uint32_t bg_color, uint8_t quadrants) 4305 { 4306 if (_vpOoB) return; 4307 if (r < ir) transpose(r, ir); // Required that r > ir 4308 if (r <= 0 || ir < 0) return; // Invalid 4309 4310 w -= 2*r; 4311 h -= 2*r; 4312 4313 if (w < 0) w = 0; 4314 if (h < 0) h = 0; 4315 4316 inTransaction = true; 4317 4318 x += r; 4319 y += r; 4320 4321 uint16_t t = r - ir + 1; 4322 int32_t xs = 0; 4323 int32_t cx = 0; 4324 4325 int32_t r2 = r * r; // Outer arc radius^2 4326 r++; 4327 int32_t r1 = r * r; // Outer AA zone radius^2 4328 4329 int32_t r3 = ir * ir; // Inner arc radius^2 4330 ir--; 4331 int32_t r4 = ir * ir; // Inner AA zone radius^2 4332 4333 uint8_t alpha = 0; 4334 4335 // Scan top left quadrant x y r ir fg_color bg_color 4336 for (int32_t cy = r - 1; cy > 0; cy--) 4337 { 4338 int32_t len = 0; // Pixel run length 4339 int32_t lxst = 0; // Left side run x start 4340 int32_t rxst = 0; // Right side run x start 4341 int32_t dy2 = (r - cy) * (r - cy); 4342 4343 // Find and track arc zone start point 4344 while ((r - xs) * (r - xs) + dy2 >= r1) xs++; 4345 4346 for (cx = xs; cx < r; cx++) 4347 { 4348 // Calculate radius^2 4349 int32_t hyp = (r - cx) * (r - cx) + dy2; 4350 4351 // If in outer zone calculate alpha 4352 if (hyp > r2) { 4353 alpha = ~sqrt_fraction(hyp); // Outer AA zone 4354 } 4355 // If within arc fill zone, get line lengths for each quadrant 4356 else if (hyp >= r3) { 4357 rxst = cx; // Right side start 4358 len++; // Line segment length 4359 continue; // Next x 4360 } 4361 else { 4362 if (hyp <= r4) break; // Skip inner pixels 4363 alpha = sqrt_fraction(hyp); // Inner AA zone 4364 } 4365 4366 if (alpha < 16) continue; // Skip low alpha pixels 4367 4368 // If background is read it must be done in each quadrant - TODO 4369 uint16_t pcol = fastBlend(alpha, fg_color, bg_color); 4370 if (quadrants & 0x8) drawPixel(x + cx - r, y - cy + r + h, pcol); // BL 4371 if (quadrants & 0x1) drawPixel(x + cx - r, y + cy - r, pcol); // TL 4372 if (quadrants & 0x2) drawPixel(x - cx + r + w, y + cy - r, pcol); // TR 4373 if (quadrants & 0x4) drawPixel(x - cx + r + w, y - cy + r + h, pcol); // BR 4374 } 4375 // Fill arc inner zone in each quadrant 4376 lxst = rxst - len + 1; // Calculate line segment start for left side 4377 if (quadrants & 0x8) drawFastHLine(x + lxst - r, y - cy + r + h, len, fg_color); // BL 4378 if (quadrants & 0x1) drawFastHLine(x + lxst - r, y + cy - r, len, fg_color); // TL 4379 if (quadrants & 0x2) drawFastHLine(x - rxst + r + w, y + cy - r, len, fg_color); // TR 4380 if (quadrants & 0x4) drawFastHLine(x - rxst + r + w, y - cy + r + h, len, fg_color); // BR 4381 } 4382 4383 // Draw sides 4384 if ((quadrants & 0xC) == 0xC) fillRect(x, y + r - t + h, w + 1, t, fg_color); // Bottom 4385 if ((quadrants & 0x9) == 0x9) fillRect(x - r + 1, y, t, h + 1, fg_color); // Left 4386 if ((quadrants & 0x3) == 0x3) fillRect(x, y - r + 1, w + 1, t, fg_color); // Top 4387 if ((quadrants & 0x6) == 0x6) fillRect(x + r - t + w, y, t, h + 1, fg_color); // Right 4388 4389 inTransaction = lockTransaction; 4390 end_tft_write(); 4391 } 4392 4393 /*************************************************************************************** 4394 ** Function name: fillSmoothRoundRect 4395 ** Description: Draw a filled anti-aliased rounded corner rectangle 4396 ***************************************************************************************/ 4397 void TFT_eSPI::fillSmoothRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t r, uint32_t color, uint32_t bg_color) 4398 { 4399 inTransaction = true; 4400 4401 int32_t xs = 0; 4402 int32_t cx = 0; 4403 4404 // Limit radius to half width or height 4405 if (r < 0) r = 0; 4406 if (r > w/2) r = w/2; 4407 if (r > h/2) r = h/2; 4408 4409 y += r; 4410 h -= 2*r; 4411 fillRect(x, y, w, h, color); 4412 4413 h--; 4414 x += r; 4415 w -= 2*r+1; 4416 4417 int32_t r1 = r * r; 4418 r++; 4419 int32_t r2 = r * r; 4420 4421 for (int32_t cy = r - 1; cy > 0; cy--) 4422 { 4423 int32_t dy2 = (r - cy) * (r - cy); 4424 for (cx = xs; cx < r; cx++) 4425 { 4426 int32_t hyp2 = (r - cx) * (r - cx) + dy2; 4427 if (hyp2 <= r1) break; 4428 if (hyp2 >= r2) continue; 4429 4430 uint8_t alpha = ~sqrt_fraction(hyp2); 4431 if (alpha > 246) break; 4432 xs = cx; 4433 if (alpha < 9) continue; 4434 4435 drawPixel(x + cx - r, y + cy - r, color, alpha, bg_color); 4436 drawPixel(x - cx + r + w, y + cy - r, color, alpha, bg_color); 4437 drawPixel(x - cx + r + w, y - cy + r + h, color, alpha, bg_color); 4438 drawPixel(x + cx - r, y - cy + r + h, color, alpha, bg_color); 4439 } 4440 drawFastHLine(x + cx - r, y + cy - r, 2 * (r - cx) + 1 + w, color); 4441 drawFastHLine(x + cx - r, y - cy + r + h, 2 * (r - cx) + 1 + w, color); 4442 } 4443 inTransaction = lockTransaction; 4444 end_tft_write(); 4445 } 4446 4447 /*************************************************************************************** 4448 ** Function name: drawSpot - maths intensive, so for small filled circles 4449 ** Description: Draw an anti-aliased filled circle at ax,ay with radius r 4450 ***************************************************************************************/ 4451 // Coordinates are floating point to achieve sub-pixel positioning 4452 void TFT_eSPI::drawSpot(float ax, float ay, float r, uint32_t fg_color, uint32_t bg_color) 4453 { 4454 // Filled circle can be created by the wide line function with zero line length 4455 drawWedgeLine( ax, ay, ax, ay, r, r, fg_color, bg_color); 4456 } 4457 4458 /*************************************************************************************** 4459 ** Function name: drawWideLine - background colour specified or pixel read 4460 ** Description: draw an anti-aliased line with rounded ends, width wd 4461 ***************************************************************************************/ 4462 void TFT_eSPI::drawWideLine(float ax, float ay, float bx, float by, float wd, uint32_t fg_color, uint32_t bg_color) 4463 { 4464 drawWedgeLine( ax, ay, bx, by, wd/2.0, wd/2.0, fg_color, bg_color); 4465 } 4466 4467 /*************************************************************************************** 4468 ** Function name: drawWedgeLine - background colour specified or pixel read 4469 ** Description: draw an anti-aliased line with different width radiused ends 4470 ***************************************************************************************/ 4471 void TFT_eSPI::drawWedgeLine(float ax, float ay, float bx, float by, float ar, float br, uint32_t fg_color, uint32_t bg_color) 4472 { 4473 if ( (ar < 0.0) || (br < 0.0) )return; 4474 if ( (fabsf(ax - bx) < 0.01f) && (fabsf(ay - by) < 0.01f) ) bx += 0.01f; // Avoid divide by zero 4475 4476 // Find line bounding box 4477 int32_t x0 = (int32_t)floorf(fminf(ax-ar, bx-br)); 4478 int32_t x1 = (int32_t) ceilf(fmaxf(ax+ar, bx+br)); 4479 int32_t y0 = (int32_t)floorf(fminf(ay-ar, by-br)); 4480 int32_t y1 = (int32_t) ceilf(fmaxf(ay+ar, by+br)); 4481 4482 if (!clipWindow(&x0, &y0, &x1, &y1)) return; 4483 4484 // Establish x start and y start 4485 int32_t ys = ay; 4486 if ((ax-ar)>(bx-br)) ys = by; 4487 4488 float rdt = ar - br; // Radius delta 4489 float alpha = 1.0f; 4490 ar += 0.5; 4491 4492 uint16_t bg = bg_color; 4493 float xpax, ypay, bax = bx - ax, bay = by - ay; 4494 4495 begin_nin_write(); 4496 inTransaction = true; 4497 4498 int32_t xs = x0; 4499 // Scan bounding box from ys down, calculate pixel intensity from distance to line 4500 for (int32_t yp = ys; yp <= y1; yp++) { 4501 bool swin = true; // Flag to start new window area 4502 bool endX = false; // Flag to skip pixels 4503 ypay = yp - ay; 4504 for (int32_t xp = xs; xp <= x1; xp++) { 4505 if (endX) if (alpha <= LoAlphaTheshold) break; // Skip right side 4506 xpax = xp - ax; 4507 alpha = ar - wedgeLineDistance(xpax, ypay, bax, bay, rdt); 4508 if (alpha <= LoAlphaTheshold ) continue; 4509 // Track edge to minimise calculations 4510 if (!endX) { endX = true; xs = xp; } 4511 if (alpha > HiAlphaTheshold) { 4512 #ifdef GC9A01_DRIVER 4513 drawPixel(xp, yp, fg_color); 4514 #else 4515 if (swin) { setWindow(xp, yp, x1, yp); swin = false; } 4516 pushColor(fg_color); 4517 #endif 4518 continue; 4519 } 4520 //Blend color with background and plot 4521 if (bg_color == 0x00FFFFFF) { 4522 bg = readPixel(xp, yp); swin = true; 4523 } 4524 #ifdef GC9A01_DRIVER 4525 uint16_t pcol = fastBlend((uint8_t)(alpha * PixelAlphaGain), fg_color, bg); 4526 drawPixel(xp, yp, pcol); 4527 swin = swin; 4528 #else 4529 if (swin) { setWindow(xp, yp, x1, yp); swin = false; } 4530 pushColor(fastBlend((uint8_t)(alpha * PixelAlphaGain), fg_color, bg)); 4531 #endif 4532 } 4533 } 4534 4535 // Reset x start to left side of box 4536 xs = x0; 4537 // Scan bounding box from ys-1 up, calculate pixel intensity from distance to line 4538 for (int32_t yp = ys-1; yp >= y0; yp--) { 4539 bool swin = true; // Flag to start new window area 4540 bool endX = false; // Flag to skip pixels 4541 ypay = yp - ay; 4542 for (int32_t xp = xs; xp <= x1; xp++) { 4543 if (endX) if (alpha <= LoAlphaTheshold) break; // Skip right side of drawn line 4544 xpax = xp - ax; 4545 alpha = ar - wedgeLineDistance(xpax, ypay, bax, bay, rdt); 4546 if (alpha <= LoAlphaTheshold ) continue; 4547 // Track line boundary 4548 if (!endX) { endX = true; xs = xp; } 4549 if (alpha > HiAlphaTheshold) { 4550 #ifdef GC9A01_DRIVER 4551 drawPixel(xp, yp, fg_color); 4552 #else 4553 if (swin) { setWindow(xp, yp, x1, yp); swin = false; } 4554 pushColor(fg_color); 4555 #endif 4556 continue; 4557 } 4558 //Blend colour with background and plot 4559 if (bg_color == 0x00FFFFFF) { 4560 bg = readPixel(xp, yp); swin = true; 4561 } 4562 #ifdef GC9A01_DRIVER 4563 uint16_t pcol = fastBlend((uint8_t)(alpha * PixelAlphaGain), fg_color, bg); 4564 drawPixel(xp, yp, pcol); 4565 swin = swin; 4566 #else 4567 if (swin) { setWindow(xp, yp, x1, yp); swin = false; } 4568 pushColor(fastBlend((uint8_t)(alpha * PixelAlphaGain), fg_color, bg)); 4569 #endif 4570 } 4571 } 4572 4573 inTransaction = lockTransaction; 4574 end_nin_write(); 4575 } 4576 4577 4578 /*************************************************************************************** 4579 ** Function name: lineDistance - private helper function for drawWedgeLine 4580 ** Description: returns distance of px,py to closest part of a to b wedge 4581 ***************************************************************************************/ 4582 inline float TFT_eSPI::wedgeLineDistance(float xpax, float ypay, float bax, float bay, float dr) 4583 { 4584 float h = fmaxf(fminf((xpax * bax + ypay * bay) / (bax * bax + bay * bay), 1.0f), 0.0f); 4585 float dx = xpax - bax * h, dy = ypay - bay * h; 4586 return sqrtf(dx * dx + dy * dy) + h * dr; 4587 } 4588 4589 4590 /*************************************************************************************** 4591 ** Function name: drawFastVLine 4592 ** Description: draw a vertical line 4593 ***************************************************************************************/ 4594 void TFT_eSPI::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) 4595 { 4596 if (_vpOoB) return; 4597 4598 x+= _xDatum; 4599 y+= _yDatum; 4600 4601 // Clipping 4602 if ((x < _vpX) || (x >= _vpW) || (y >= _vpH)) return; 4603 4604 if (y < _vpY) { h += y - _vpY; y = _vpY; } 4605 4606 if ((y + h) > _vpH) h = _vpH - y; 4607 4608 if (h < 1) return; 4609 4610 begin_tft_write(); 4611 4612 setWindow(x, y, x, y + h - 1); 4613 4614 pushBlock(color, h); 4615 4616 end_tft_write(); 4617 } 4618 4619 4620 /*************************************************************************************** 4621 ** Function name: drawFastHLine 4622 ** Description: draw a horizontal line 4623 ***************************************************************************************/ 4624 void TFT_eSPI::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) 4625 { 4626 if (_vpOoB) return; 4627 4628 x+= _xDatum; 4629 y+= _yDatum; 4630 4631 // Clipping 4632 if ((y < _vpY) || (x >= _vpW) || (y >= _vpH)) return; 4633 4634 if (x < _vpX) { w += x - _vpX; x = _vpX; } 4635 4636 if ((x + w) > _vpW) w = _vpW - x; 4637 4638 if (w < 1) return; 4639 4640 begin_tft_write(); 4641 4642 setWindow(x, y, x + w - 1, y); 4643 4644 pushBlock(color, w); 4645 4646 end_tft_write(); 4647 } 4648 4649 4650 /*************************************************************************************** 4651 ** Function name: fillRect 4652 ** Description: draw a filled rectangle 4653 ***************************************************************************************/ 4654 void TFT_eSPI::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) 4655 { 4656 if (_vpOoB) return; 4657 4658 x+= _xDatum; 4659 y+= _yDatum; 4660 4661 // Clipping 4662 if ((x >= _vpW) || (y >= _vpH)) return; 4663 4664 if (x < _vpX) { w += x - _vpX; x = _vpX; } 4665 if (y < _vpY) { h += y - _vpY; y = _vpY; } 4666 4667 if ((x + w) > _vpW) w = _vpW - x; 4668 if ((y + h) > _vpH) h = _vpH - y; 4669 4670 if ((w < 1) || (h < 1)) return; 4671 4672 //Serial.print(" _xDatum=");Serial.print( _xDatum);Serial.print(", _yDatum=");Serial.print( _yDatum); 4673 //Serial.print(", _xWidth=");Serial.print(_xWidth);Serial.print(", _yHeight=");Serial.println(_yHeight); 4674 4675 //Serial.print(" _vpX=");Serial.print( _vpX);Serial.print(", _vpY=");Serial.print( _vpY); 4676 //Serial.print(", _vpW=");Serial.print(_vpW);Serial.print(", _vpH=");Serial.println(_vpH); 4677 4678 //Serial.print(" x=");Serial.print( y);Serial.print(", y=");Serial.print( y); 4679 //Serial.print(", w=");Serial.print(w);Serial.print(", h=");Serial.println(h); 4680 4681 begin_tft_write(); 4682 4683 setWindow(x, y, x + w - 1, y + h - 1); 4684 4685 pushBlock(color, w * h); 4686 4687 end_tft_write(); 4688 } 4689 4690 4691 /*************************************************************************************** 4692 ** Function name: fillRectVGradient 4693 ** Description: draw a filled rectangle with a vertical colour gradient 4694 ***************************************************************************************/ 4695 void TFT_eSPI::fillRectVGradient(int16_t x, int16_t y, int16_t w, int16_t h, uint32_t color1, uint32_t color2) 4696 { 4697 if (_vpOoB) return; 4698 4699 x+= _xDatum; 4700 y+= _yDatum; 4701 4702 // Clipping 4703 if ((x >= _vpW) || (y >= _vpH)) return; 4704 4705 if (x < _vpX) { w += x - _vpX; x = _vpX; } 4706 if (y < _vpY) { h += y - _vpY; y = _vpY; } 4707 4708 if ((x + w) > _vpW) w = _vpW - x; 4709 if ((y + h) > _vpH) h = _vpH - y; 4710 4711 if ((w < 1) || (h < 1)) return; 4712 4713 begin_nin_write(); 4714 4715 float delta = -255.0/h; 4716 float alpha = 255.0; 4717 uint32_t color = color1; 4718 4719 while (h--) { 4720 drawFastHLine(x, y++, w, color); 4721 alpha += delta; 4722 color = fastBlend((uint8_t)alpha, color1, color2); 4723 } 4724 4725 end_nin_write(); 4726 } 4727 4728 4729 /*************************************************************************************** 4730 ** Function name: fillRectHGradient 4731 ** Description: draw a filled rectangle with a horizontal colour gradient 4732 ***************************************************************************************/ 4733 void TFT_eSPI::fillRectHGradient(int16_t x, int16_t y, int16_t w, int16_t h, uint32_t color1, uint32_t color2) 4734 { 4735 if (_vpOoB) return; 4736 4737 x+= _xDatum; 4738 y+= _yDatum; 4739 4740 // Clipping 4741 if ((x >= _vpW) || (y >= _vpH)) return; 4742 4743 if (x < _vpX) { w += x - _vpX; x = _vpX; } 4744 if (y < _vpY) { h += y - _vpY; y = _vpY; } 4745 4746 if ((x + w) > _vpW) w = _vpW - x; 4747 if ((y + h) > _vpH) h = _vpH - y; 4748 4749 if ((w < 1) || (h < 1)) return; 4750 4751 begin_nin_write(); 4752 4753 float delta = -255.0/w; 4754 float alpha = 255.0; 4755 uint32_t color = color1; 4756 4757 while (w--) { 4758 drawFastVLine(x++, y, h, color); 4759 alpha += delta; 4760 color = fastBlend((uint8_t)alpha, color1, color2); 4761 } 4762 4763 end_nin_write(); 4764 } 4765 4766 4767 /*************************************************************************************** 4768 ** Function name: color565 4769 ** Description: convert three 8 bit RGB levels to a 16 bit colour value 4770 ***************************************************************************************/ 4771 uint16_t TFT_eSPI::color565(uint8_t r, uint8_t g, uint8_t b) 4772 { 4773 return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); 4774 } 4775 4776 4777 /*************************************************************************************** 4778 ** Function name: color16to8 4779 ** Description: convert 16 bit colour to an 8 bit 332 RGB colour value 4780 ***************************************************************************************/ 4781 uint8_t TFT_eSPI::color16to8(uint16_t c) 4782 { 4783 return ((c & 0xE000)>>8) | ((c & 0x0700)>>6) | ((c & 0x0018)>>3); 4784 } 4785 4786 4787 /*************************************************************************************** 4788 ** Function name: color8to16 4789 ** Description: convert 8 bit colour to a 16 bit 565 colour value 4790 ***************************************************************************************/ 4791 uint16_t TFT_eSPI::color8to16(uint8_t color) 4792 { 4793 uint8_t blue[] = {0, 11, 21, 31}; // blue 2 to 5 bit colour lookup table 4794 uint16_t color16 = 0; 4795 4796 // =====Green===== ===============Red============== 4797 color16 = (color & 0x1C)<<6 | (color & 0xC0)<<5 | (color & 0xE0)<<8; 4798 // =====Green===== =======Blue====== 4799 color16 |= (color & 0x1C)<<3 | blue[color & 0x03]; 4800 4801 return color16; 4802 } 4803 4804 /*************************************************************************************** 4805 ** Function name: color16to24 4806 ** Description: convert 16 bit colour to a 24 bit 888 colour value 4807 ***************************************************************************************/ 4808 uint32_t TFT_eSPI::color16to24(uint16_t color565) 4809 { 4810 uint8_t r = (color565 >> 8) & 0xF8; r |= (r >> 5); 4811 uint8_t g = (color565 >> 3) & 0xFC; g |= (g >> 6); 4812 uint8_t b = (color565 << 3) & 0xF8; b |= (b >> 5); 4813 4814 return ((uint32_t)r << 16) | ((uint32_t)g << 8) | ((uint32_t)b << 0); 4815 } 4816 4817 /*************************************************************************************** 4818 ** Function name: color24to16 4819 ** Description: convert 24 bit colour to a 16 bit 565 colour value 4820 ***************************************************************************************/ 4821 uint32_t TFT_eSPI::color24to16(uint32_t color888) 4822 { 4823 uint16_t r = (color888 >> 8) & 0xF800; 4824 uint16_t g = (color888 >> 5) & 0x07E0; 4825 uint16_t b = (color888 >> 3) & 0x001F; 4826 4827 return (r | g | b); 4828 } 4829 4830 /*************************************************************************************** 4831 ** Function name: invertDisplay 4832 ** Description: invert the display colours i = 1 invert, i = 0 normal 4833 ***************************************************************************************/ 4834 void TFT_eSPI::invertDisplay(bool i) 4835 { 4836 begin_tft_write(); 4837 // Send the command twice as otherwise it does not always work! 4838 writecommand(i ? TFT_INVON : TFT_INVOFF); 4839 writecommand(i ? TFT_INVON : TFT_INVOFF); 4840 end_tft_write(); 4841 } 4842 4843 4844 /************************************************************************** 4845 ** Function name: setAttribute 4846 ** Description: Sets a control parameter of an attribute 4847 **************************************************************************/ 4848 void TFT_eSPI::setAttribute(uint8_t attr_id, uint8_t param) { 4849 switch (attr_id) { 4850 break; 4851 case CP437_SWITCH: 4852 _cp437 = param; 4853 break; 4854 case UTF8_SWITCH: 4855 _utf8 = param; 4856 decoderState = 0; 4857 break; 4858 case PSRAM_ENABLE: 4859 #if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) 4860 if (psramFound()) _psram_enable = param; // Enable the use of PSRAM (if available) 4861 else 4862 #endif 4863 _psram_enable = false; 4864 break; 4865 //case 4: // TBD future feature control 4866 // _tbd = param; 4867 // break; 4868 } 4869 } 4870 4871 4872 /************************************************************************** 4873 ** Function name: getAttribute 4874 ** Description: Get value of an attribute (control parameter) 4875 **************************************************************************/ 4876 uint8_t TFT_eSPI::getAttribute(uint8_t attr_id) { 4877 switch (attr_id) { 4878 case CP437_SWITCH: // ON/OFF control of full CP437 character set 4879 return _cp437; 4880 case UTF8_SWITCH: // ON/OFF control of UTF-8 decoding 4881 return _utf8; 4882 case PSRAM_ENABLE: 4883 return _psram_enable; 4884 //case 3: // TBD future feature control 4885 // return _tbd; 4886 // break; 4887 } 4888 4889 return false; 4890 } 4891 4892 /*************************************************************************************** 4893 ** Function name: decodeUTF8 4894 ** Description: Serial UTF-8 decoder with fall-back to extended ASCII 4895 *************************************************************************************x*/ 4896 uint16_t TFT_eSPI::decodeUTF8(uint8_t c) 4897 { 4898 if (!_utf8) return c; 4899 4900 // 7 bit Unicode Code Point 4901 if ((c & 0x80) == 0x00) { 4902 decoderState = 0; 4903 return c; 4904 } 4905 4906 if (decoderState == 0) { 4907 // 11 bit Unicode Code Point 4908 if ((c & 0xE0) == 0xC0) { 4909 decoderBuffer = ((c & 0x1F)<<6); 4910 decoderState = 1; 4911 return 0; 4912 } 4913 // 16 bit Unicode Code Point 4914 if ((c & 0xF0) == 0xE0) { 4915 decoderBuffer = ((c & 0x0F)<<12); 4916 decoderState = 2; 4917 return 0; 4918 } 4919 // 21 bit Unicode Code Point not supported so fall-back to extended ASCII 4920 // if ((c & 0xF8) == 0xF0) return c; 4921 } 4922 else { 4923 if (decoderState == 2) { 4924 decoderBuffer |= ((c & 0x3F)<<6); 4925 decoderState--; 4926 return 0; 4927 } 4928 else { 4929 decoderBuffer |= (c & 0x3F); 4930 decoderState = 0; 4931 return decoderBuffer; 4932 } 4933 } 4934 4935 decoderState = 0; 4936 4937 return c; // fall-back to extended ASCII 4938 } 4939 4940 4941 /*************************************************************************************** 4942 ** Function name: decodeUTF8 4943 ** Description: Line buffer UTF-8 decoder with fall-back to extended ASCII 4944 *************************************************************************************x*/ 4945 uint16_t TFT_eSPI::decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining) 4946 { 4947 uint16_t c = buf[(*index)++]; 4948 //Serial.print("Byte from string = 0x"); Serial.println(c, HEX); 4949 4950 if (!_utf8) return c; 4951 4952 // 7 bit Unicode 4953 if ((c & 0x80) == 0x00) return c; 4954 4955 // 11 bit Unicode 4956 if (((c & 0xE0) == 0xC0) && (remaining > 1)) 4957 return ((c & 0x1F)<<6) | (buf[(*index)++]&0x3F); 4958 4959 // 16 bit Unicode 4960 if (((c & 0xF0) == 0xE0) && (remaining > 2)) { 4961 c = ((c & 0x0F)<<12) | ((buf[(*index)++]&0x3F)<<6); 4962 return c | ((buf[(*index)++]&0x3F)); 4963 } 4964 4965 // 21 bit Unicode not supported so fall-back to extended ASCII 4966 // if ((c & 0xF8) == 0xF0) return c; 4967 4968 return c; // fall-back to extended ASCII 4969 } 4970 4971 4972 /*************************************************************************************** 4973 ** Function name: alphaBlend 4974 ** Description: Blend 16bit foreground and background 4975 *************************************************************************************x*/ 4976 uint16_t TFT_eSPI::alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc) 4977 { 4978 // Split out and blend 5 bit red and blue channels 4979 uint32_t rxb = bgc & 0xF81F; 4980 rxb += ((fgc & 0xF81F) - rxb) * (alpha >> 2) >> 6; 4981 // Split out and blend 6 bit green channel 4982 uint32_t xgx = bgc & 0x07E0; 4983 xgx += ((fgc & 0x07E0) - xgx) * alpha >> 8; 4984 // Recombine channels 4985 return (rxb & 0xF81F) | (xgx & 0x07E0); 4986 } 4987 4988 /*************************************************************************************** 4989 ** Function name: alphaBlend 4990 ** Description: Blend 16bit foreground and background with dither 4991 *************************************************************************************x*/ 4992 uint16_t TFT_eSPI::alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc, uint8_t dither) 4993 { 4994 if (dither) { 4995 int16_t alphaDither = (int16_t)alpha - dither + random(2*dither+1); // +/-4 randomised 4996 alpha = (uint8_t)alphaDither; 4997 if (alphaDither < 0) alpha = 0; 4998 if (alphaDither >255) alpha = 255; 4999 } 5000 5001 return alphaBlend(alpha, fgc, bgc); 5002 } 5003 5004 /*************************************************************************************** 5005 ** Function name: alphaBlend 5006 ** Description: Blend 24bit foreground and background with optional dither 5007 *************************************************************************************x*/ 5008 uint32_t TFT_eSPI::alphaBlend24(uint8_t alpha, uint32_t fgc, uint32_t bgc, uint8_t dither) 5009 { 5010 5011 if (dither) { 5012 int16_t alphaDither = (int16_t)alpha - dither + random(2*dither+1); // +/-dither randomised 5013 alpha = (uint8_t)alphaDither; 5014 if (alphaDither < 0) alpha = 0; 5015 if (alphaDither >255) alpha = 255; 5016 } 5017 5018 uint32_t rxx = bgc & 0xFF0000; 5019 rxx += ((fgc & 0xFF0000) - rxx) * alpha >> 8; 5020 uint32_t xgx = bgc & 0x00FF00; 5021 xgx += ((fgc & 0x00FF00) - xgx) * alpha >> 8; 5022 uint32_t xxb = bgc & 0x0000FF; 5023 xxb += ((fgc & 0x0000FF) - xxb) * alpha >> 8; 5024 return (rxx & 0xFF0000) | (xgx & 0x00FF00) | (xxb & 0x0000FF); 5025 } 5026 5027 /*************************************************************************************** 5028 ** Function name: write 5029 ** Description: draw characters piped through serial stream 5030 ***************************************************************************************/ 5031 /* // Not all processors support buffered write 5032 #ifndef ARDUINO_ARCH_ESP8266 // Avoid ESP8266 board package bug 5033 size_t TFT_eSPI::write(const uint8_t *buf, size_t len) 5034 { 5035 inTransaction = true; 5036 5037 uint8_t *lbuf = (uint8_t *)buf; 5038 while(*lbuf !=0 && len--) write(*lbuf++); 5039 5040 inTransaction = lockTransaction; 5041 end_tft_write(); 5042 return 1; 5043 } 5044 #endif 5045 */ 5046 /*************************************************************************************** 5047 ** Function name: write 5048 ** Description: draw characters piped through serial stream 5049 ***************************************************************************************/ 5050 size_t TFT_eSPI::write(uint8_t utf8) 5051 { 5052 if (_vpOoB) return 1; 5053 5054 uint16_t uniCode = decodeUTF8(utf8); 5055 5056 if (!uniCode) return 1; 5057 5058 if (utf8 == '\r') return 1; 5059 5060 #ifdef SMOOTH_FONT 5061 if(fontLoaded) { 5062 if (uniCode < 32 && utf8 != '\n') return 1; 5063 5064 drawGlyph(uniCode); 5065 5066 return 1; 5067 } 5068 #endif 5069 5070 if (uniCode == '\n') uniCode+=22; // Make it a valid space character to stop errors 5071 5072 uint16_t cwidth = 0; 5073 uint16_t cheight = 0; 5074 5075 //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv DEBUG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv 5076 //Serial.print((uint8_t) uniCode); // Debug line sends all printed TFT text to serial port 5077 //Serial.println(uniCode, HEX); // Debug line sends all printed TFT text to serial port 5078 //delay(5); // Debug optional wait for serial port to flush through 5079 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DEBUG ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 5080 5081 //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 5082 #ifdef LOAD_GFXFF 5083 if(!gfxFont) { 5084 #endif 5085 //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 5086 5087 #ifdef LOAD_FONT2 5088 if (textfont == 2) { 5089 if (uniCode < 32 || uniCode > 127) return 1; 5090 5091 cwidth = pgm_read_byte(widtbl_f16 + uniCode-32); 5092 cheight = chr_hgt_f16; 5093 // Font 2 is rendered in whole byte widths so we must allow for this 5094 cwidth = (cwidth + 6) / 8; // Width in whole bytes for font 2, should be + 7 but must allow for font width change 5095 cwidth = cwidth * 8; // Width converted back to pixels 5096 } 5097 #ifdef LOAD_RLE 5098 else 5099 #endif 5100 #endif 5101 5102 #ifdef LOAD_RLE 5103 { 5104 if ((textfont>2) && (textfont<9)) { 5105 if (uniCode < 32 || uniCode > 127) return 1; 5106 // Uses the fontinfo struct array to avoid lots of 'if' or 'switch' statements 5107 cwidth = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[textfont].widthtbl ) ) + uniCode-32 ); 5108 cheight= pgm_read_byte( &fontdata[textfont].height ); 5109 } 5110 } 5111 #endif 5112 5113 #ifdef LOAD_GLCD 5114 if (textfont==1) { 5115 cwidth = 6; 5116 cheight = 8; 5117 } 5118 #else 5119 if (textfont==1) return 1; 5120 #endif 5121 5122 cheight = cheight * textsize; 5123 5124 if (utf8 == '\n') { 5125 cursor_y += cheight; 5126 cursor_x = 0; 5127 } 5128 else { 5129 if (textwrapX && (cursor_x + cwidth * textsize > width())) { 5130 cursor_y += cheight; 5131 cursor_x = 0; 5132 } 5133 if (textwrapY && (cursor_y >= (int32_t) height())) cursor_y = 0; 5134 cursor_x += drawChar(uniCode, cursor_x, cursor_y, textfont); 5135 } 5136 5137 //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 5138 #ifdef LOAD_GFXFF 5139 } // Custom GFX font 5140 else { 5141 if(utf8 == '\n') { 5142 cursor_x = 0; 5143 cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); 5144 } else { 5145 if (uniCode > pgm_read_word(&gfxFont->last )) return 1; 5146 if (uniCode < pgm_read_word(&gfxFont->first)) return 1; 5147 5148 uint16_t c2 = uniCode - pgm_read_word(&gfxFont->first); 5149 GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); 5150 uint8_t w = pgm_read_byte(&glyph->width), 5151 h = pgm_read_byte(&glyph->height); 5152 if((w > 0) && (h > 0)) { // Is there an associated bitmap? 5153 int16_t xo = (int8_t)pgm_read_byte(&glyph->xOffset); 5154 if(textwrapX && ((cursor_x + textsize * (xo + w)) > width())) { 5155 // Drawing character would go off right edge; wrap to new line 5156 cursor_x = 0; 5157 cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); 5158 } 5159 if (textwrapY && (cursor_y >= (int32_t) height())) cursor_y = 0; 5160 drawChar(cursor_x, cursor_y, uniCode, textcolor, textbgcolor, textsize); 5161 } 5162 cursor_x += pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize; 5163 } 5164 } 5165 #endif // LOAD_GFXFF 5166 //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 5167 5168 return 1; 5169 } 5170 5171 5172 /*************************************************************************************** 5173 ** Function name: drawChar 5174 ** Description: draw a Unicode glyph onto the screen 5175 ***************************************************************************************/ 5176 // TODO: Rationalise with TFT_eSprite 5177 // Any UTF-8 decoding must be done before calling drawChar() 5178 int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y) 5179 { 5180 return drawChar(uniCode, x, y, textfont); 5181 } 5182 5183 // Any UTF-8 decoding must be done before calling drawChar() 5184 int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font) 5185 { 5186 if (_vpOoB || !uniCode) return 0; 5187 5188 if (font==1) { 5189 #ifdef LOAD_GLCD 5190 #ifndef LOAD_GFXFF 5191 drawChar(x, y, uniCode, textcolor, textbgcolor, textsize); 5192 return 6 * textsize; 5193 #endif 5194 #else 5195 #ifndef LOAD_GFXFF 5196 return 0; 5197 #endif 5198 #endif 5199 5200 #ifdef LOAD_GFXFF 5201 drawChar(x, y, uniCode, textcolor, textbgcolor, textsize); 5202 if(!gfxFont) { // 'Classic' built-in font 5203 #ifdef LOAD_GLCD 5204 return 6 * textsize; 5205 #else 5206 return 0; 5207 #endif 5208 } 5209 else { 5210 if((uniCode >= pgm_read_word(&gfxFont->first)) && (uniCode <= pgm_read_word(&gfxFont->last) )) { 5211 uint16_t c2 = uniCode - pgm_read_word(&gfxFont->first); 5212 GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); 5213 return pgm_read_byte(&glyph->xAdvance) * textsize; 5214 } 5215 else { 5216 return 0; 5217 } 5218 } 5219 #endif 5220 } 5221 5222 if ((font>1) && (font<9) && ((uniCode < 32) || (uniCode > 127))) return 0; 5223 5224 int32_t width = 0; 5225 int32_t height = 0; 5226 uint32_t flash_address = 0; 5227 uniCode -= 32; 5228 5229 #ifdef LOAD_FONT2 5230 if (font == 2) { 5231 flash_address = pgm_read_dword(&chrtbl_f16[uniCode]); 5232 width = pgm_read_byte(widtbl_f16 + uniCode); 5233 height = chr_hgt_f16; 5234 } 5235 #ifdef LOAD_RLE 5236 else 5237 #endif 5238 #endif 5239 5240 #ifdef LOAD_RLE 5241 { 5242 if ((font>2) && (font<9)) { 5243 flash_address = pgm_read_dword( (const void*)(pgm_read_dword( &(fontdata[font].chartbl ) ) + uniCode*sizeof(void *)) ); 5244 width = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[font].widthtbl ) ) + uniCode ); 5245 height= pgm_read_byte( &fontdata[font].height ); 5246 } 5247 } 5248 #endif 5249 5250 int32_t xd = x + _xDatum; 5251 int32_t yd = y + _yDatum; 5252 5253 if ((xd + width * textsize < _vpX || xd >= _vpW) && (yd + height * textsize < _vpY || yd >= _vpH)) return width * textsize ; 5254 5255 int32_t w = width; 5256 int32_t pX = 0; 5257 int32_t pY = y; 5258 uint8_t line = 0; 5259 bool clip = xd < _vpX || xd + width * textsize >= _vpW || yd < _vpY || yd + height * textsize >= _vpH; 5260 5261 #ifdef LOAD_FONT2 // chop out code if we do not need it 5262 if (font == 2) { 5263 w = w + 6; // Should be + 7 but we need to compensate for width increment 5264 w = w / 8; 5265 5266 if (textcolor == textbgcolor || textsize != 1 || clip) { 5267 //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() 5268 inTransaction = true; 5269 5270 for (int32_t i = 0; i < height; i++) { 5271 if (textcolor != textbgcolor) fillRect(x, pY, width * textsize, textsize, textbgcolor); 5272 5273 for (int32_t k = 0; k < w; k++) { 5274 line = pgm_read_byte((uint8_t *)flash_address + w * i + k); 5275 if (line) { 5276 if (textsize == 1) { 5277 pX = x + k * 8; 5278 if (line & 0x80) drawPixel(pX, pY, textcolor); 5279 if (line & 0x40) drawPixel(pX + 1, pY, textcolor); 5280 if (line & 0x20) drawPixel(pX + 2, pY, textcolor); 5281 if (line & 0x10) drawPixel(pX + 3, pY, textcolor); 5282 if (line & 0x08) drawPixel(pX + 4, pY, textcolor); 5283 if (line & 0x04) drawPixel(pX + 5, pY, textcolor); 5284 if (line & 0x02) drawPixel(pX + 6, pY, textcolor); 5285 if (line & 0x01) drawPixel(pX + 7, pY, textcolor); 5286 } 5287 else { 5288 pX = x + k * 8 * textsize; 5289 if (line & 0x80) fillRect(pX, pY, textsize, textsize, textcolor); 5290 if (line & 0x40) fillRect(pX + textsize, pY, textsize, textsize, textcolor); 5291 if (line & 0x20) fillRect(pX + 2 * textsize, pY, textsize, textsize, textcolor); 5292 if (line & 0x10) fillRect(pX + 3 * textsize, pY, textsize, textsize, textcolor); 5293 if (line & 0x08) fillRect(pX + 4 * textsize, pY, textsize, textsize, textcolor); 5294 if (line & 0x04) fillRect(pX + 5 * textsize, pY, textsize, textsize, textcolor); 5295 if (line & 0x02) fillRect(pX + 6 * textsize, pY, textsize, textsize, textcolor); 5296 if (line & 0x01) fillRect(pX + 7 * textsize, pY, textsize, textsize, textcolor); 5297 } 5298 } 5299 } 5300 pY += textsize; 5301 } 5302 5303 inTransaction = lockTransaction; 5304 end_tft_write(); 5305 } 5306 else { // Faster drawing of characters and background using block write 5307 5308 begin_tft_write(); 5309 5310 setWindow(xd, yd, xd + width - 1, yd + height - 1); 5311 5312 uint8_t mask; 5313 for (int32_t i = 0; i < height; i++) { 5314 pX = width; 5315 for (int32_t k = 0; k < w; k++) { 5316 line = pgm_read_byte((uint8_t *) (flash_address + w * i + k) ); 5317 mask = 0x80; 5318 while (mask && pX) { 5319 if (line & mask) {tft_Write_16(textcolor);} 5320 else {tft_Write_16(textbgcolor);} 5321 pX--; 5322 mask = mask >> 1; 5323 } 5324 } 5325 if (pX) {tft_Write_16(textbgcolor);} 5326 } 5327 5328 end_tft_write(); 5329 } 5330 } 5331 5332 #ifdef LOAD_RLE 5333 else 5334 #endif 5335 #endif //FONT2 5336 5337 #ifdef LOAD_RLE //674 bytes of code 5338 // Font is not 2 and hence is RLE encoded 5339 { 5340 begin_tft_write(); 5341 inTransaction = true; 5342 5343 w *= height; // Now w is total number of pixels in the character 5344 if (textcolor == textbgcolor && !clip) { 5345 5346 int32_t px = 0, py = pY; // To hold character block start and end column and row values 5347 int32_t pc = 0; // Pixel count 5348 uint8_t np = textsize * textsize; // Number of pixels in a drawn pixel 5349 5350 uint8_t tnp = 0; // Temporary copy of np for while loop 5351 uint8_t ts = textsize - 1; // Temporary copy of textsize 5352 // 16 bit pixel count so maximum font size is equivalent to 180x180 pixels in area 5353 // w is total number of pixels to plot to fill character block 5354 while (pc < w) { 5355 line = pgm_read_byte((uint8_t *)flash_address); 5356 flash_address++; 5357 if (line & 0x80) { 5358 line &= 0x7F; 5359 line++; 5360 if (ts) { 5361 px = xd + textsize * (pc % width); // Keep these px and py calculations outside the loop as they are slow 5362 py = yd + textsize * (pc / width); 5363 } 5364 else { 5365 px = xd + pc % width; // Keep these px and py calculations outside the loop as they are slow 5366 py = yd + pc / width; 5367 } 5368 while (line--) { // In this case the while(line--) is faster 5369 pc++; // This is faster than putting pc+=line before while()? 5370 setWindow(px, py, px + ts, py + ts); 5371 5372 if (ts) { 5373 tnp = np; 5374 while (tnp--) {tft_Write_16(textcolor);} 5375 } 5376 else {tft_Write_16(textcolor);} 5377 px += textsize; 5378 5379 if (px >= (xd + width * textsize)) { 5380 px = xd; 5381 py += textsize; 5382 } 5383 } 5384 } 5385 else { 5386 line++; 5387 pc += line; 5388 } 5389 } 5390 } 5391 else { 5392 // Text colour != background and textsize = 1 and character is within viewport area 5393 // so use faster drawing of characters and background using block write 5394 if (textcolor != textbgcolor && textsize == 1 && !clip) 5395 { 5396 setWindow(xd, yd, xd + width - 1, yd + height - 1); 5397 5398 // Maximum font size is equivalent to 180x180 pixels in area 5399 while (w > 0) { 5400 line = pgm_read_byte((uint8_t *)flash_address++); // 8 bytes smaller when incrementing here 5401 if (line & 0x80) { 5402 line &= 0x7F; 5403 line++; w -= line; 5404 pushBlock(textcolor,line); 5405 } 5406 else { 5407 line++; w -= line; 5408 pushBlock(textbgcolor,line); 5409 } 5410 } 5411 } 5412 else 5413 { 5414 int32_t px = 0, py = 0; // To hold character pixel coords 5415 int32_t tx = 0, ty = 0; // To hold character TFT pixel coords 5416 int32_t pc = 0; // Pixel count 5417 int32_t pl = 0; // Pixel line length 5418 uint16_t pcol = 0; // Pixel color 5419 bool pf = true; // Flag for plotting 5420 while (pc < w) { 5421 line = pgm_read_byte((uint8_t *)flash_address); 5422 flash_address++; 5423 if (line & 0x80) { pcol = textcolor; line &= 0x7F; pf = true;} 5424 else { pcol = textbgcolor; if (textcolor == textbgcolor) pf = false;} 5425 line++; 5426 px = pc % width; 5427 tx = x + textsize * px; 5428 py = pc / width; 5429 ty = y + textsize * py; 5430 5431 pl = 0; 5432 pc += line; 5433 while (line--) { 5434 pl++; 5435 if ((px+pl) >= width) { 5436 if (pf) fillRect(tx, ty, pl * textsize, textsize, pcol); 5437 pl = 0; 5438 px = 0; 5439 tx = x; 5440 py ++; 5441 ty += textsize; 5442 } 5443 } 5444 if (pl && pf) fillRect(tx, ty, pl * textsize, textsize, pcol); 5445 } 5446 } 5447 } 5448 inTransaction = lockTransaction; 5449 end_tft_write(); 5450 } 5451 // End of RLE font rendering 5452 #endif 5453 5454 #if !defined (LOAD_FONT2) && !defined (LOAD_RLE) 5455 // Stop warnings 5456 flash_address = flash_address; 5457 w = w; 5458 pX = pX; 5459 pY = pY; 5460 line = line; 5461 clip = clip; 5462 #endif 5463 5464 return width * textsize; // x + 5465 } 5466 5467 5468 /*************************************************************************************** 5469 ** Function name: drawString (with or without user defined font) 5470 ** Description : draw string with padding if it is defined 5471 ***************************************************************************************/ 5472 // Without font number, uses font set by setTextFont() 5473 int16_t TFT_eSPI::drawString(const String& string, int32_t poX, int32_t poY) 5474 { 5475 int16_t len = string.length() + 2; 5476 char buffer[len]; 5477 string.toCharArray(buffer, len); 5478 return drawString(buffer, poX, poY, textfont); 5479 } 5480 // With font number 5481 int16_t TFT_eSPI::drawString(const String& string, int32_t poX, int32_t poY, uint8_t font) 5482 { 5483 int16_t len = string.length() + 2; 5484 char buffer[len]; 5485 string.toCharArray(buffer, len); 5486 return drawString(buffer, poX, poY, font); 5487 } 5488 5489 // Without font number, uses font set by setTextFont() 5490 int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY) 5491 { 5492 return drawString(string, poX, poY, textfont); 5493 } 5494 5495 // With font number. Note: font number is over-ridden if a smooth font is loaded 5496 int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8_t font) 5497 { 5498 int16_t sumX = 0; 5499 uint8_t padding = 1, baseline = 0; 5500 uint16_t cwidth = textWidth(string, font); // Find the pixel width of the string in the font 5501 uint16_t cheight = 8 * textsize; 5502 5503 #ifdef LOAD_GFXFF 5504 #ifdef SMOOTH_FONT 5505 bool freeFont = (font == 1 && gfxFont && !fontLoaded); 5506 #else 5507 bool freeFont = (font == 1 && gfxFont); 5508 #endif 5509 5510 if (freeFont) { 5511 cheight = glyph_ab * textsize; 5512 poY += cheight; // Adjust for baseline datum of free fonts 5513 baseline = cheight; 5514 padding =101; // Different padding method used for Free Fonts 5515 5516 // We need to make an adjustment for the bottom of the string (eg 'y' character) 5517 if ((textdatum == BL_DATUM) || (textdatum == BC_DATUM) || (textdatum == BR_DATUM)) { 5518 cheight += glyph_bb * textsize; 5519 } 5520 } 5521 #endif 5522 5523 5524 // If it is not font 1 (GLCD or free font) get the baseline and pixel height of the font 5525 #ifdef SMOOTH_FONT 5526 if(fontLoaded) { 5527 baseline = gFont.maxAscent; 5528 cheight = fontHeight(); 5529 } 5530 else 5531 #endif 5532 if (font!=1) { 5533 baseline = pgm_read_byte( &fontdata[font].baseline ) * textsize; 5534 cheight = fontHeight(font); 5535 } 5536 5537 if (textdatum || padX) { 5538 5539 switch(textdatum) { 5540 case TC_DATUM: 5541 poX -= cwidth/2; 5542 padding += 1; 5543 break; 5544 case TR_DATUM: 5545 poX -= cwidth; 5546 padding += 2; 5547 break; 5548 case ML_DATUM: 5549 poY -= cheight/2; 5550 //padding += 0; 5551 break; 5552 case MC_DATUM: 5553 poX -= cwidth/2; 5554 poY -= cheight/2; 5555 padding += 1; 5556 break; 5557 case MR_DATUM: 5558 poX -= cwidth; 5559 poY -= cheight/2; 5560 padding += 2; 5561 break; 5562 case BL_DATUM: 5563 poY -= cheight; 5564 //padding += 0; 5565 break; 5566 case BC_DATUM: 5567 poX -= cwidth/2; 5568 poY -= cheight; 5569 padding += 1; 5570 break; 5571 case BR_DATUM: 5572 poX -= cwidth; 5573 poY -= cheight; 5574 padding += 2; 5575 break; 5576 case L_BASELINE: 5577 poY -= baseline; 5578 //padding += 0; 5579 break; 5580 case C_BASELINE: 5581 poX -= cwidth/2; 5582 poY -= baseline; 5583 padding += 1; 5584 break; 5585 case R_BASELINE: 5586 poX -= cwidth; 5587 poY -= baseline; 5588 padding += 2; 5589 break; 5590 } 5591 } 5592 5593 5594 int8_t xo = 0; 5595 #ifdef LOAD_GFXFF 5596 if (freeFont && (textcolor!=textbgcolor)) { 5597 cheight = (glyph_ab + glyph_bb) * textsize; 5598 // Get the offset for the first character only to allow for negative offsets 5599 uint16_t c2 = 0; 5600 uint16_t len = strlen(string); 5601 uint16_t n = 0; 5602 5603 while (n < len && c2 == 0) c2 = decodeUTF8((uint8_t*)string, &n, len - n); 5604 5605 if((c2 >= pgm_read_word(&gfxFont->first)) && (c2 <= pgm_read_word(&gfxFont->last) )) { 5606 c2 -= pgm_read_word(&gfxFont->first); 5607 GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); 5608 xo = pgm_read_byte(&glyph->xOffset) * textsize; 5609 // Adjust for negative xOffset 5610 if (xo > 0) xo = 0; 5611 else cwidth -= xo; 5612 // Add 1 pixel of padding all round 5613 //cheight +=2; 5614 //fillRect(poX+xo-1, poY - 1 - glyph_ab * textsize, cwidth+2, cheight, textbgcolor); 5615 fillRect(poX+xo, poY - glyph_ab * textsize, cwidth, cheight, textbgcolor); 5616 } 5617 padding -=100; 5618 } 5619 #endif 5620 5621 uint16_t len = strlen(string); 5622 uint16_t n = 0; 5623 5624 #ifdef SMOOTH_FONT 5625 if(fontLoaded) { 5626 setCursor(poX, poY); 5627 5628 bool fillbg = _fillbg; 5629 // If padding is requested then fill the text background 5630 if (padX && !_fillbg) _fillbg = true; 5631 5632 while (n < len) { 5633 uint16_t uniCode = decodeUTF8((uint8_t*)string, &n, len - n); 5634 drawGlyph(uniCode); 5635 } 5636 _fillbg = fillbg; // restore state 5637 sumX += cwidth; 5638 //fontFile.close(); 5639 } 5640 else 5641 #endif 5642 { 5643 while (n < len) { 5644 uint16_t uniCode = decodeUTF8((uint8_t*)string, &n, len - n); 5645 sumX += drawChar(uniCode, poX+sumX, poY, font); 5646 } 5647 } 5648 5649 //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv DEBUG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv 5650 // Switch on debugging for the padding areas 5651 //#define PADDING_DEBUG 5652 5653 #ifndef PADDING_DEBUG 5654 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DEBUG ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 5655 5656 if((padX>cwidth) && (textcolor!=textbgcolor)) { 5657 int16_t padXc = poX+cwidth+xo; 5658 #ifdef LOAD_GFXFF 5659 if (freeFont) { 5660 poX +=xo; // Adjust for negative offset start character 5661 poY -= glyph_ab * textsize; 5662 sumX += poX; 5663 } 5664 #endif 5665 switch(padding) { 5666 case 1: 5667 fillRect(padXc,poY,padX-cwidth,cheight, textbgcolor); 5668 break; 5669 case 2: 5670 fillRect(padXc,poY,(padX-cwidth)>>1,cheight, textbgcolor); 5671 padXc = poX - ((padX-cwidth)>>1); 5672 fillRect(padXc,poY,(padX-cwidth)>>1,cheight, textbgcolor); 5673 break; 5674 case 3: 5675 if (padXc>padX) padXc = padX; 5676 fillRect(poX + cwidth - padXc,poY,padXc-cwidth,cheight, textbgcolor); 5677 break; 5678 } 5679 } 5680 5681 5682 #else 5683 5684 //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv DEBUG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv 5685 // This is debug code to show text (green box) and blanked (white box) areas 5686 // It shows that the padding areas are being correctly sized and positioned 5687 5688 if((padX>sumX) && (textcolor!=textbgcolor)) { 5689 int16_t padXc = poX+sumX; // Maximum left side padding 5690 #ifdef LOAD_GFXFF 5691 if ((font == 1) && (gfxFont)) poY -= glyph_ab; 5692 #endif 5693 drawRect(poX,poY,sumX,cheight, TFT_GREEN); 5694 switch(padding) { 5695 case 1: 5696 drawRect(padXc,poY,padX-sumX,cheight, TFT_WHITE); 5697 break; 5698 case 2: 5699 drawRect(padXc,poY,(padX-sumX)>>1, cheight, TFT_WHITE); 5700 padXc = (padX-sumX)>>1; 5701 drawRect(poX - padXc,poY,(padX-sumX)>>1,cheight, TFT_WHITE); 5702 break; 5703 case 3: 5704 if (padXc>padX) padXc = padX; 5705 drawRect(poX + sumX - padXc,poY,padXc-sumX,cheight, TFT_WHITE); 5706 break; 5707 } 5708 } 5709 #endif 5710 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DEBUG ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 5711 5712 return sumX; 5713 } 5714 5715 5716 /*************************************************************************************** 5717 ** Function name: drawCentreString (deprecated, use setTextDatum()) 5718 ** Descriptions: draw string centred on dX 5719 ***************************************************************************************/ 5720 int16_t TFT_eSPI::drawCentreString(const String& string, int32_t dX, int32_t poY, uint8_t font) 5721 { 5722 int16_t len = string.length() + 2; 5723 char buffer[len]; 5724 string.toCharArray(buffer, len); 5725 return drawCentreString(buffer, dX, poY, font); 5726 } 5727 5728 int16_t TFT_eSPI::drawCentreString(const char *string, int32_t dX, int32_t poY, uint8_t font) 5729 { 5730 uint8_t tempdatum = textdatum; 5731 int32_t sumX = 0; 5732 textdatum = TC_DATUM; 5733 sumX = drawString(string, dX, poY, font); 5734 textdatum = tempdatum; 5735 return sumX; 5736 } 5737 5738 5739 /*************************************************************************************** 5740 ** Function name: drawRightString (deprecated, use setTextDatum()) 5741 ** Descriptions: draw string right justified to dX 5742 ***************************************************************************************/ 5743 int16_t TFT_eSPI::drawRightString(const String& string, int32_t dX, int32_t poY, uint8_t font) 5744 { 5745 int16_t len = string.length() + 2; 5746 char buffer[len]; 5747 string.toCharArray(buffer, len); 5748 return drawRightString(buffer, dX, poY, font); 5749 } 5750 5751 int16_t TFT_eSPI::drawRightString(const char *string, int32_t dX, int32_t poY, uint8_t font) 5752 { 5753 uint8_t tempdatum = textdatum; 5754 int16_t sumX = 0; 5755 textdatum = TR_DATUM; 5756 sumX = drawString(string, dX, poY, font); 5757 textdatum = tempdatum; 5758 return sumX; 5759 } 5760 5761 5762 /*************************************************************************************** 5763 ** Function name: drawNumber 5764 ** Description: draw a long integer 5765 ***************************************************************************************/ 5766 int16_t TFT_eSPI::drawNumber(long long_num, int32_t poX, int32_t poY) 5767 { 5768 isDigits = true; // Eliminate jiggle in monospaced fonts 5769 char str[12]; 5770 ltoa(long_num, str, 10); 5771 return drawString(str, poX, poY, textfont); 5772 } 5773 5774 int16_t TFT_eSPI::drawNumber(long long_num, int32_t poX, int32_t poY, uint8_t font) 5775 { 5776 isDigits = true; // Eliminate jiggle in monospaced fonts 5777 char str[12]; 5778 ltoa(long_num, str, 10); 5779 return drawString(str, poX, poY, font); 5780 } 5781 5782 5783 /*************************************************************************************** 5784 ** Function name: drawFloat 5785 ** Descriptions: drawFloat, prints 7 non zero digits maximum 5786 ***************************************************************************************/ 5787 // Assemble and print a string, this permits alignment relative to a datum 5788 // looks complicated but much more compact and actually faster than using print class 5789 int16_t TFT_eSPI::drawFloat(float floatNumber, uint8_t dp, int32_t poX, int32_t poY) 5790 { 5791 return drawFloat(floatNumber, dp, poX, poY, textfont); 5792 } 5793 5794 int16_t TFT_eSPI::drawFloat(float floatNumber, uint8_t dp, int32_t poX, int32_t poY, uint8_t font) 5795 { 5796 isDigits = true; 5797 char str[14]; // Array to contain decimal string 5798 uint8_t ptr = 0; // Initialise pointer for array 5799 int8_t digits = 1; // Count the digits to avoid array overflow 5800 float rounding = 0.5; // Round up down delta 5801 bool negative = false; 5802 5803 if (dp > 7) dp = 7; // Limit the size of decimal portion 5804 5805 // Adjust the rounding value 5806 for (uint8_t i = 0; i < dp; ++i) rounding /= 10.0; 5807 5808 if (floatNumber < -rounding) { // add sign, avoid adding - sign to 0.0! 5809 str[ptr++] = '-'; // Negative number 5810 str[ptr] = 0; // Put a null in the array as a precaution 5811 digits = 0; // Set digits to 0 to compensate so pointer value can be used later 5812 floatNumber = -floatNumber; // Make positive 5813 negative = true; 5814 } 5815 5816 floatNumber += rounding; // Round up or down 5817 5818 if (dp == 0) { 5819 if (negative) floatNumber = -floatNumber; 5820 return drawNumber((long)floatNumber, poX, poY, font); 5821 } 5822 5823 // For error put ... in string and return (all TFT_eSPI library fonts contain . character) 5824 if (floatNumber >= 2147483647) { 5825 strcpy(str, "..."); 5826 return drawString(str, poX, poY, font); 5827 } 5828 // No chance of overflow from here on 5829 5830 // Get integer part 5831 uint32_t temp = (uint32_t)floatNumber; 5832 5833 // Put integer part into array 5834 ltoa(temp, str + ptr, 10); 5835 5836 // Find out where the null is to get the digit count loaded 5837 while ((uint8_t)str[ptr] != 0) ptr++; // Move the pointer along 5838 digits += ptr; // Count the digits 5839 5840 str[ptr++] = '.'; // Add decimal point 5841 str[ptr] = '0'; // Add a dummy zero 5842 str[ptr + 1] = 0; // Add a null but don't increment pointer so it can be overwritten 5843 5844 // Get the decimal portion 5845 floatNumber = floatNumber - temp; 5846 5847 // Get decimal digits one by one and put in array 5848 // Limit digit count so we don't get a false sense of resolution 5849 uint8_t i = 0; 5850 while ((i < dp) && (digits < 9)) { // while (i < dp) for no limit but array size must be increased 5851 i++; 5852 floatNumber *= 10; // for the next decimal 5853 temp = floatNumber; // get the decimal 5854 ltoa(temp, str + ptr, 10); 5855 ptr++; digits++; // Increment pointer and digits count 5856 floatNumber -= temp; // Remove that digit 5857 } 5858 5859 // Finally we can plot the string and return pixel length 5860 return drawString(str, poX, poY, font); 5861 } 5862 5863 5864 /*************************************************************************************** 5865 ** Function name: setFreeFont 5866 ** Descriptions: Sets the GFX free font to use 5867 ***************************************************************************************/ 5868 5869 #ifdef LOAD_GFXFF 5870 5871 void TFT_eSPI::setFreeFont(const GFXfont *f) 5872 { 5873 if (f == nullptr) { // Fix issue #400 (ESP32 crash) 5874 setTextFont(1); // Use GLCD font 5875 return; 5876 } 5877 5878 textfont = 1; 5879 gfxFont = (GFXfont *)f; 5880 5881 glyph_ab = 0; 5882 glyph_bb = 0; 5883 uint16_t numChars = pgm_read_word(&gfxFont->last) - pgm_read_word(&gfxFont->first); 5884 5885 // Find the biggest above and below baseline offsets 5886 for (uint16_t c = 0; c < numChars; c++) { 5887 GFXglyph *glyph1 = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]); 5888 int8_t ab = -pgm_read_byte(&glyph1->yOffset); 5889 if (ab > glyph_ab) glyph_ab = ab; 5890 int8_t bb = pgm_read_byte(&glyph1->height) - ab; 5891 if (bb > glyph_bb) glyph_bb = bb; 5892 } 5893 } 5894 5895 5896 /*************************************************************************************** 5897 ** Function name: setTextFont 5898 ** Description: Set the font for the print stream 5899 ***************************************************************************************/ 5900 void TFT_eSPI::setTextFont(uint8_t f) 5901 { 5902 textfont = (f > 0) ? f : 1; // Don't allow font 0 5903 gfxFont = NULL; 5904 } 5905 5906 #else 5907 5908 5909 /*************************************************************************************** 5910 ** Function name: setFreeFont 5911 ** Descriptions: Sets the GFX free font to use 5912 ***************************************************************************************/ 5913 5914 // Alternative to setTextFont() so we don't need two different named functions 5915 void TFT_eSPI::setFreeFont(uint8_t font) 5916 { 5917 setTextFont(font); 5918 } 5919 5920 5921 /*************************************************************************************** 5922 ** Function name: setTextFont 5923 ** Description: Set the font for the print stream 5924 ***************************************************************************************/ 5925 void TFT_eSPI::setTextFont(uint8_t f) 5926 { 5927 textfont = (f > 0) ? f : 1; // Don't allow font 0 5928 } 5929 #endif 5930 5931 5932 /*************************************************************************************** 5933 ** Function name: getSPIinstance 5934 ** Description: Get the instance of the SPI class 5935 ***************************************************************************************/ 5936 #if !defined (TFT_PARALLEL_8_BIT) && !defined (RP2040_PIO_INTERFACE) 5937 SPIClass& TFT_eSPI::getSPIinstance(void) 5938 { 5939 return spi; 5940 } 5941 #endif 5942 5943 5944 /*************************************************************************************** 5945 ** Function name: verifySetupID 5946 ** Description: Compare the ID if USER_SETUP_ID defined in user setup file 5947 ***************************************************************************************/ 5948 bool TFT_eSPI::verifySetupID(uint32_t id) 5949 { 5950 #if defined (USER_SETUP_ID) 5951 if (USER_SETUP_ID == id) return true; 5952 #else 5953 id = id; // Avoid warning 5954 #endif 5955 return false; 5956 } 5957 5958 /*************************************************************************************** 5959 ** Function name: getSetup 5960 ** Description: Get the setup details for diagnostic and sketch access 5961 ***************************************************************************************/ 5962 void TFT_eSPI::getSetup(setup_t &tft_settings) 5963 { 5964 // tft_settings.version is set in header file 5965 5966 #if defined (USER_SETUP_INFO) 5967 tft_settings.setup_info = USER_SETUP_INFO; 5968 #else 5969 tft_settings.setup_info = "NA"; 5970 #endif 5971 5972 #if defined (USER_SETUP_ID) 5973 tft_settings.setup_id = USER_SETUP_ID; 5974 #else 5975 tft_settings.setup_id = 0; 5976 #endif 5977 5978 #if defined (PROCESSOR_ID) 5979 tft_settings.esp = PROCESSOR_ID; 5980 #else 5981 tft_settings.esp = -1; 5982 #endif 5983 5984 #if defined (SUPPORT_TRANSACTIONS) 5985 tft_settings.trans = true; 5986 #else 5987 tft_settings.trans = false; 5988 #endif 5989 5990 #if defined (TFT_PARALLEL_8_BIT) || defined(TFT_PARALLEL_16_BIT) 5991 tft_settings.serial = false; 5992 tft_settings.tft_spi_freq = 0; 5993 #else 5994 tft_settings.serial = true; 5995 tft_settings.tft_spi_freq = SPI_FREQUENCY/100000; 5996 #ifdef SPI_READ_FREQUENCY 5997 tft_settings.tft_rd_freq = SPI_READ_FREQUENCY/100000; 5998 #endif 5999 #ifndef GENERIC_PROCESSOR 6000 #ifdef TFT_SPI_PORT 6001 tft_settings.port = TFT_SPI_PORT; 6002 #else 6003 tft_settings.port = 255; 6004 #endif 6005 #endif 6006 #ifdef RP2040_PIO_SPI 6007 tft_settings.interface = 0x10; 6008 #else 6009 tft_settings.interface = 0x0; 6010 #endif 6011 #endif 6012 6013 #if defined(TFT_SPI_OVERLAP) 6014 tft_settings.overlap = true; 6015 #else 6016 tft_settings.overlap = false; 6017 #endif 6018 6019 tft_settings.tft_driver = TFT_DRIVER; 6020 tft_settings.tft_width = _init_width; 6021 tft_settings.tft_height = _init_height; 6022 6023 #ifdef CGRAM_OFFSET 6024 tft_settings.r0_x_offset = colstart; 6025 tft_settings.r0_y_offset = rowstart; 6026 tft_settings.r1_x_offset = 0; 6027 tft_settings.r1_y_offset = 0; 6028 tft_settings.r2_x_offset = 0; 6029 tft_settings.r2_y_offset = 0; 6030 tft_settings.r3_x_offset = 0; 6031 tft_settings.r3_y_offset = 0; 6032 #else 6033 tft_settings.r0_x_offset = 0; 6034 tft_settings.r0_y_offset = 0; 6035 tft_settings.r1_x_offset = 0; 6036 tft_settings.r1_y_offset = 0; 6037 tft_settings.r2_x_offset = 0; 6038 tft_settings.r2_y_offset = 0; 6039 tft_settings.r3_x_offset = 0; 6040 tft_settings.r3_y_offset = 0; 6041 #endif 6042 6043 #if defined (TFT_MOSI) 6044 tft_settings.pin_tft_mosi = TFT_MOSI; 6045 #else 6046 tft_settings.pin_tft_mosi = -1; 6047 #endif 6048 6049 #if defined (TFT_MISO) 6050 tft_settings.pin_tft_miso = TFT_MISO; 6051 #else 6052 tft_settings.pin_tft_miso = -1; 6053 #endif 6054 6055 #if defined (TFT_SCLK) 6056 tft_settings.pin_tft_clk = TFT_SCLK; 6057 #else 6058 tft_settings.pin_tft_clk = -1; 6059 #endif 6060 6061 #if defined (TFT_CS) 6062 tft_settings.pin_tft_cs = TFT_CS; 6063 #else 6064 tft_settings.pin_tft_cs = -1; 6065 #endif 6066 6067 #if defined (TFT_DC) 6068 tft_settings.pin_tft_dc = TFT_DC; 6069 #else 6070 tft_settings.pin_tft_dc = -1; 6071 #endif 6072 6073 #if defined (TFT_RD) 6074 tft_settings.pin_tft_rd = TFT_RD; 6075 #else 6076 tft_settings.pin_tft_rd = -1; 6077 #endif 6078 6079 #if defined (TFT_WR) 6080 tft_settings.pin_tft_wr = TFT_WR; 6081 #else 6082 tft_settings.pin_tft_wr = -1; 6083 #endif 6084 6085 #if defined (TFT_RST) 6086 tft_settings.pin_tft_rst = TFT_RST; 6087 #else 6088 tft_settings.pin_tft_rst = -1; 6089 #endif 6090 6091 #if defined (TFT_PARALLEL_8_BIT) || defined(TFT_PARALLEL_16_BIT) 6092 tft_settings.pin_tft_d0 = TFT_D0; 6093 tft_settings.pin_tft_d1 = TFT_D1; 6094 tft_settings.pin_tft_d2 = TFT_D2; 6095 tft_settings.pin_tft_d3 = TFT_D3; 6096 tft_settings.pin_tft_d4 = TFT_D4; 6097 tft_settings.pin_tft_d5 = TFT_D5; 6098 tft_settings.pin_tft_d6 = TFT_D6; 6099 tft_settings.pin_tft_d7 = TFT_D7; 6100 #else 6101 tft_settings.pin_tft_d0 = -1; 6102 tft_settings.pin_tft_d1 = -1; 6103 tft_settings.pin_tft_d2 = -1; 6104 tft_settings.pin_tft_d3 = -1; 6105 tft_settings.pin_tft_d4 = -1; 6106 tft_settings.pin_tft_d5 = -1; 6107 tft_settings.pin_tft_d6 = -1; 6108 tft_settings.pin_tft_d7 = -1; 6109 #endif 6110 6111 #if defined (TFT_BL) 6112 tft_settings.pin_tft_led = TFT_BL; 6113 #endif 6114 6115 #if defined (TFT_BACKLIGHT_ON) 6116 tft_settings.pin_tft_led_on = TFT_BACKLIGHT_ON; 6117 #endif 6118 6119 #if defined (TOUCH_CS) 6120 tft_settings.pin_tch_cs = TOUCH_CS; 6121 tft_settings.tch_spi_freq = SPI_TOUCH_FREQUENCY/100000; 6122 #else 6123 tft_settings.pin_tch_cs = -1; 6124 tft_settings.tch_spi_freq = 0; 6125 #endif 6126 } 6127 6128 6129 //////////////////////////////////////////////////////////////////////////////////////// 6130 #ifdef TOUCH_CS 6131 #include "Extensions/Touch.cpp" 6132 #endif 6133 6134 #include "Extensions/Button.cpp" 6135 6136 #include "Extensions/Sprite.cpp" 6137 6138 #ifdef SMOOTH_FONT 6139 #include "Extensions/Smooth_font.cpp" 6140 #endif 6141 6142 #ifdef AA_GRAPHICS 6143 #include "Extensions/AA_graphics.cpp" // Loaded if SMOOTH_FONT is defined by user 6144 #endif 6145 //////////////////////////////////////////////////////////////////////////////////////// 6146