acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

TFT_eSPI.cpp (195567B)

      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 = alphaBlend(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 = alphaBlend(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 = alphaBlend(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 = alphaBlend((uint8_t)(alpha * PixelAlphaGain), fg_color, bg);
   4526         drawPixel(xp, yp, pcol);
   4527       #else
   4528         if (swin) { setWindow(xp, yp, x1, yp); swin = false; }
   4529         pushColor(alphaBlend((uint8_t)(alpha * PixelAlphaGain), fg_color, bg));
   4530       #endif
   4531     }
   4532   }
   4533 
   4534   // Reset x start to left side of box
   4535   xs = x0;
   4536   // Scan bounding box from ys-1 up, calculate pixel intensity from distance to line
   4537   for (int32_t yp = ys-1; yp >= y0; yp--) {
   4538     bool swin = true;  // Flag to start new window area
   4539     bool endX = false; // Flag to skip pixels
   4540     ypay = yp - ay;
   4541     for (int32_t xp = xs; xp <= x1; xp++) {
   4542       if (endX) if (alpha <= LoAlphaTheshold) break;  // Skip right side of drawn line
   4543       xpax = xp - ax;
   4544       alpha = ar - wedgeLineDistance(xpax, ypay, bax, bay, rdt);
   4545       if (alpha <= LoAlphaTheshold ) continue;
   4546       // Track line boundary
   4547       if (!endX) { endX = true; xs = xp; }
   4548       if (alpha > HiAlphaTheshold) {
   4549         #ifdef GC9A01_DRIVER
   4550           drawPixel(xp, yp, fg_color);
   4551         #else
   4552           if (swin) { setWindow(xp, yp, x1, yp); swin = false; }
   4553           pushColor(fg_color);
   4554         #endif
   4555         continue;
   4556       }
   4557       //Blend colour with background and plot
   4558       if (bg_color == 0x00FFFFFF) {
   4559         bg = readPixel(xp, yp); swin = true;
   4560       }
   4561       #ifdef GC9A01_DRIVER
   4562         uint16_t pcol = alphaBlend((uint8_t)(alpha * PixelAlphaGain), fg_color, bg);
   4563         drawPixel(xp, yp, pcol);
   4564       #else
   4565         if (swin) { setWindow(xp, yp, x1, yp); swin = false; }
   4566         pushColor(alphaBlend((uint8_t)(alpha * PixelAlphaGain), fg_color, bg));
   4567       #endif
   4568     }
   4569   }
   4570 
   4571   inTransaction = lockTransaction;
   4572   end_nin_write();
   4573 }
   4574 
   4575 
   4576 /***************************************************************************************
   4577 ** Function name:           lineDistance - private helper function for drawWedgeLine
   4578 ** Description:             returns distance of px,py to closest part of a to b wedge
   4579 ***************************************************************************************/
   4580 inline float TFT_eSPI::wedgeLineDistance(float xpax, float ypay, float bax, float bay, float dr)
   4581 {
   4582   float h = fmaxf(fminf((xpax * bax + ypay * bay) / (bax * bax + bay * bay), 1.0f), 0.0f);
   4583   float dx = xpax - bax * h, dy = ypay - bay * h;
   4584   return sqrtf(dx * dx + dy * dy) + h * dr;
   4585 }
   4586 
   4587 
   4588 /***************************************************************************************
   4589 ** Function name:           drawFastVLine
   4590 ** Description:             draw a vertical line
   4591 ***************************************************************************************/
   4592 void TFT_eSPI::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color)
   4593 {
   4594   if (_vpOoB) return;
   4595 
   4596   x+= _xDatum;
   4597   y+= _yDatum;
   4598 
   4599   // Clipping
   4600   if ((x < _vpX) || (x >= _vpW) || (y >= _vpH)) return;
   4601 
   4602   if (y < _vpY) { h += y - _vpY; y = _vpY; }
   4603 
   4604   if ((y + h) > _vpH) h = _vpH - y;
   4605 
   4606   if (h < 1) return;
   4607 
   4608   begin_tft_write();
   4609 
   4610   setWindow(x, y, x, y + h - 1);
   4611 
   4612   pushBlock(color, h);
   4613 
   4614   end_tft_write();
   4615 }
   4616 
   4617 
   4618 /***************************************************************************************
   4619 ** Function name:           drawFastHLine
   4620 ** Description:             draw a horizontal line
   4621 ***************************************************************************************/
   4622 void TFT_eSPI::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color)
   4623 {
   4624   if (_vpOoB) return;
   4625 
   4626   x+= _xDatum;
   4627   y+= _yDatum;
   4628 
   4629   // Clipping
   4630   if ((y < _vpY) || (x >= _vpW) || (y >= _vpH)) return;
   4631 
   4632   if (x < _vpX) { w += x - _vpX; x = _vpX; }
   4633 
   4634   if ((x + w) > _vpW) w = _vpW - x;
   4635 
   4636   if (w < 1) return;
   4637 
   4638   begin_tft_write();
   4639 
   4640   setWindow(x, y, x + w - 1, y);
   4641 
   4642   pushBlock(color, w);
   4643 
   4644   end_tft_write();
   4645 }
   4646 
   4647 
   4648 /***************************************************************************************
   4649 ** Function name:           fillRect
   4650 ** Description:             draw a filled rectangle
   4651 ***************************************************************************************/
   4652 void TFT_eSPI::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color)
   4653 {
   4654   if (_vpOoB) return;
   4655 
   4656   x+= _xDatum;
   4657   y+= _yDatum;
   4658 
   4659   // Clipping
   4660   if ((x >= _vpW) || (y >= _vpH)) return;
   4661 
   4662   if (x < _vpX) { w += x - _vpX; x = _vpX; }
   4663   if (y < _vpY) { h += y - _vpY; y = _vpY; }
   4664 
   4665   if ((x + w) > _vpW) w = _vpW - x;
   4666   if ((y + h) > _vpH) h = _vpH - y;
   4667 
   4668   if ((w < 1) || (h < 1)) return;
   4669 
   4670   //Serial.print(" _xDatum=");Serial.print( _xDatum);Serial.print(", _yDatum=");Serial.print( _yDatum);
   4671   //Serial.print(", _xWidth=");Serial.print(_xWidth);Serial.print(", _yHeight=");Serial.println(_yHeight);
   4672 
   4673   //Serial.print(" _vpX=");Serial.print( _vpX);Serial.print(", _vpY=");Serial.print( _vpY);
   4674   //Serial.print(", _vpW=");Serial.print(_vpW);Serial.print(", _vpH=");Serial.println(_vpH);
   4675 
   4676   //Serial.print(" x=");Serial.print( y);Serial.print(", y=");Serial.print( y);
   4677   //Serial.print(", w=");Serial.print(w);Serial.print(", h=");Serial.println(h);
   4678 
   4679   begin_tft_write();
   4680 
   4681   setWindow(x, y, x + w - 1, y + h - 1);
   4682 
   4683   pushBlock(color, w * h);
   4684 
   4685   end_tft_write();
   4686 }
   4687 
   4688 
   4689 /***************************************************************************************
   4690 ** Function name:           fillRectVGradient
   4691 ** Description:             draw a filled rectangle with a vertical colour gradient
   4692 ***************************************************************************************/
   4693 void TFT_eSPI::fillRectVGradient(int16_t x, int16_t y, int16_t w, int16_t h, uint32_t color1, uint32_t color2)
   4694 {
   4695   if (_vpOoB) return;
   4696 
   4697   x+= _xDatum;
   4698   y+= _yDatum;
   4699 
   4700   // Clipping
   4701   if ((x >= _vpW) || (y >= _vpH)) return;
   4702 
   4703   if (x < _vpX) { w += x - _vpX; x = _vpX; }
   4704   if (y < _vpY) { h += y - _vpY; y = _vpY; }
   4705 
   4706   if ((x + w) > _vpW) w = _vpW - x;
   4707   if ((y + h) > _vpH) h = _vpH - y;
   4708 
   4709   if ((w < 1) || (h < 1)) return;
   4710 
   4711   begin_nin_write();
   4712 
   4713   float delta = -255.0/h;
   4714   float alpha = 255.0;
   4715   uint32_t color = color1;
   4716 
   4717   while (h--) {
   4718     drawFastHLine(x, y++, w, color);
   4719     alpha += delta;
   4720     color = alphaBlend((uint8_t)alpha, color1, color2);
   4721   }
   4722 
   4723   end_nin_write();
   4724 }
   4725 
   4726 
   4727 /***************************************************************************************
   4728 ** Function name:           fillRectHGradient
   4729 ** Description:             draw a filled rectangle with a horizontal colour gradient
   4730 ***************************************************************************************/
   4731 void TFT_eSPI::fillRectHGradient(int16_t x, int16_t y, int16_t w, int16_t h, uint32_t color1, uint32_t color2)
   4732 {
   4733   if (_vpOoB) return;
   4734 
   4735   x+= _xDatum;
   4736   y+= _yDatum;
   4737 
   4738   // Clipping
   4739   if ((x >= _vpW) || (y >= _vpH)) return;
   4740 
   4741   if (x < _vpX) { w += x - _vpX; x = _vpX; }
   4742   if (y < _vpY) { h += y - _vpY; y = _vpY; }
   4743 
   4744   if ((x + w) > _vpW) w = _vpW - x;
   4745   if ((y + h) > _vpH) h = _vpH - y;
   4746 
   4747   if ((w < 1) || (h < 1)) return;
   4748 
   4749   begin_nin_write();
   4750 
   4751   float delta = -255.0/w;
   4752   float alpha = 255.0;
   4753   uint32_t color = color1;
   4754 
   4755   while (w--) {
   4756     drawFastVLine(x++, y, h, color);
   4757     alpha += delta;
   4758     color = alphaBlend((uint8_t)alpha, color1, color2);
   4759   }
   4760 
   4761   end_nin_write();
   4762 }
   4763 
   4764 
   4765 /***************************************************************************************
   4766 ** Function name:           color565
   4767 ** Description:             convert three 8 bit RGB levels to a 16 bit colour value
   4768 ***************************************************************************************/
   4769 uint16_t TFT_eSPI::color565(uint8_t r, uint8_t g, uint8_t b)
   4770 {
   4771   return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
   4772 }
   4773 
   4774 
   4775 /***************************************************************************************
   4776 ** Function name:           color16to8
   4777 ** Description:             convert 16 bit colour to an 8 bit 332 RGB colour value
   4778 ***************************************************************************************/
   4779 uint8_t TFT_eSPI::color16to8(uint16_t c)
   4780 {
   4781   return ((c & 0xE000)>>8) | ((c & 0x0700)>>6) | ((c & 0x0018)>>3);
   4782 }
   4783 
   4784 
   4785 /***************************************************************************************
   4786 ** Function name:           color8to16
   4787 ** Description:             convert 8 bit colour to a 16 bit 565 colour value
   4788 ***************************************************************************************/
   4789 uint16_t TFT_eSPI::color8to16(uint8_t color)
   4790 {
   4791   uint8_t  blue[] = {0, 11, 21, 31}; // blue 2 to 5 bit colour lookup table
   4792   uint16_t color16 = 0;
   4793 
   4794   //        =====Green=====     ===============Red==============
   4795   color16  = (color & 0x1C)<<6 | (color & 0xC0)<<5 | (color & 0xE0)<<8;
   4796   //        =====Green=====    =======Blue======
   4797   color16 |= (color & 0x1C)<<3 | blue[color & 0x03];
   4798 
   4799   return color16;
   4800 }
   4801 
   4802 /***************************************************************************************
   4803 ** Function name:           color16to24
   4804 ** Description:             convert 16 bit colour to a 24 bit 888 colour value
   4805 ***************************************************************************************/
   4806 uint32_t TFT_eSPI::color16to24(uint16_t color565)
   4807 {
   4808   uint8_t r = (color565 >> 8) & 0xF8; r |= (r >> 5);
   4809   uint8_t g = (color565 >> 3) & 0xFC; g |= (g >> 6);
   4810   uint8_t b = (color565 << 3) & 0xF8; b |= (b >> 5);
   4811 
   4812   return ((uint32_t)r << 16) | ((uint32_t)g << 8) | ((uint32_t)b << 0);
   4813 }
   4814 
   4815 /***************************************************************************************
   4816 ** Function name:           color24to16
   4817 ** Description:             convert 24 bit colour to a 16 bit 565 colour value
   4818 ***************************************************************************************/
   4819 uint32_t TFT_eSPI::color24to16(uint32_t color888)
   4820 {
   4821   uint16_t r = (color888 >> 8) & 0xF800;
   4822   uint16_t g = (color888 >> 5) & 0x07E0;
   4823   uint16_t b = (color888 >> 3) & 0x001F;
   4824 
   4825   return (r | g | b);
   4826 }
   4827 
   4828 /***************************************************************************************
   4829 ** Function name:           invertDisplay
   4830 ** Description:             invert the display colours i = 1 invert, i = 0 normal
   4831 ***************************************************************************************/
   4832 void TFT_eSPI::invertDisplay(bool i)
   4833 {
   4834   begin_tft_write();
   4835   // Send the command twice as otherwise it does not always work!
   4836   writecommand(i ? TFT_INVON : TFT_INVOFF);
   4837   writecommand(i ? TFT_INVON : TFT_INVOFF);
   4838   end_tft_write();
   4839 }
   4840 
   4841 
   4842 /**************************************************************************
   4843 ** Function name:           setAttribute
   4844 ** Description:             Sets a control parameter of an attribute
   4845 **************************************************************************/
   4846 void TFT_eSPI::setAttribute(uint8_t attr_id, uint8_t param) {
   4847     switch (attr_id) {
   4848             break;
   4849         case CP437_SWITCH:
   4850             _cp437 = param;
   4851             break;
   4852         case UTF8_SWITCH:
   4853             _utf8  = param;
   4854             decoderState = 0;
   4855             break;
   4856         case PSRAM_ENABLE:
   4857 #if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT)
   4858             if (psramFound()) _psram_enable = param; // Enable the use of PSRAM (if available)
   4859             else
   4860 #endif
   4861             _psram_enable = false;
   4862             break;
   4863         //case 4: // TBD future feature control
   4864         //    _tbd = param;
   4865         //    break;
   4866     }
   4867 }
   4868 
   4869 
   4870 /**************************************************************************
   4871 ** Function name:           getAttribute
   4872 ** Description:             Get value of an attribute (control parameter)
   4873 **************************************************************************/
   4874 uint8_t TFT_eSPI::getAttribute(uint8_t attr_id) {
   4875     switch (attr_id) {
   4876         case CP437_SWITCH: // ON/OFF control of full CP437 character set
   4877             return _cp437;
   4878         case UTF8_SWITCH: // ON/OFF control of UTF-8 decoding
   4879             return _utf8;
   4880         case PSRAM_ENABLE:
   4881             return _psram_enable;
   4882         //case 3: // TBD future feature control
   4883         //    return _tbd;
   4884         //    break;
   4885     }
   4886 
   4887     return false;
   4888 }
   4889 
   4890 /***************************************************************************************
   4891 ** Function name:           decodeUTF8
   4892 ** Description:             Serial UTF-8 decoder with fall-back to extended ASCII
   4893 *************************************************************************************x*/
   4894 uint16_t TFT_eSPI::decodeUTF8(uint8_t c)
   4895 {
   4896   if (!_utf8) return c;
   4897 
   4898   // 7 bit Unicode Code Point
   4899   if ((c & 0x80) == 0x00) {
   4900     decoderState = 0;
   4901     return c;
   4902   }
   4903 
   4904   if (decoderState == 0) {
   4905     // 11 bit Unicode Code Point
   4906     if ((c & 0xE0) == 0xC0) {
   4907       decoderBuffer = ((c & 0x1F)<<6);
   4908       decoderState = 1;
   4909       return 0;
   4910     }
   4911     // 16 bit Unicode Code Point
   4912     if ((c & 0xF0) == 0xE0) {
   4913       decoderBuffer = ((c & 0x0F)<<12);
   4914       decoderState = 2;
   4915       return 0;
   4916     }
   4917     // 21 bit Unicode  Code Point not supported so fall-back to extended ASCII
   4918     // if ((c & 0xF8) == 0xF0) return c;
   4919   }
   4920   else {
   4921     if (decoderState == 2) {
   4922       decoderBuffer |= ((c & 0x3F)<<6);
   4923       decoderState--;
   4924       return 0;
   4925     }
   4926     else {
   4927       decoderBuffer |= (c & 0x3F);
   4928       decoderState = 0;
   4929       return decoderBuffer;
   4930     }
   4931   }
   4932 
   4933   decoderState = 0;
   4934 
   4935   return c; // fall-back to extended ASCII
   4936 }
   4937 
   4938 
   4939 /***************************************************************************************
   4940 ** Function name:           decodeUTF8
   4941 ** Description:             Line buffer UTF-8 decoder with fall-back to extended ASCII
   4942 *************************************************************************************x*/
   4943 uint16_t TFT_eSPI::decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining)
   4944 {
   4945   uint16_t c = buf[(*index)++];
   4946   //Serial.print("Byte from string = 0x"); Serial.println(c, HEX);
   4947 
   4948   if (!_utf8) return c;
   4949 
   4950   // 7 bit Unicode
   4951   if ((c & 0x80) == 0x00) return c;
   4952 
   4953   // 11 bit Unicode
   4954   if (((c & 0xE0) == 0xC0) && (remaining > 1))
   4955     return ((c & 0x1F)<<6) | (buf[(*index)++]&0x3F);
   4956 
   4957   // 16 bit Unicode
   4958   if (((c & 0xF0) == 0xE0) && (remaining > 2)) {
   4959     c = ((c & 0x0F)<<12) | ((buf[(*index)++]&0x3F)<<6);
   4960     return  c | ((buf[(*index)++]&0x3F));
   4961   }
   4962 
   4963   // 21 bit Unicode not supported so fall-back to extended ASCII
   4964   // if ((c & 0xF8) == 0xF0) return c;
   4965 
   4966   return c; // fall-back to extended ASCII
   4967 }
   4968 
   4969 
   4970 /***************************************************************************************
   4971 ** Function name:           alphaBlend
   4972 ** Description:             Blend 16bit foreground and background
   4973 *************************************************************************************x*/
   4974 inline uint16_t TFT_eSPI::alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc)
   4975 {
   4976   // Split out and blend 5 bit red and blue channels
   4977   uint32_t rxb = bgc & 0xF81F;
   4978   rxb += ((fgc & 0xF81F) - rxb) * (alpha >> 2) >> 6;
   4979   // Split out and blend 6 bit green channel
   4980   uint32_t xgx = bgc & 0x07E0;
   4981   xgx += ((fgc & 0x07E0) - xgx) * alpha >> 8;
   4982   // Recombine channels
   4983   return (rxb & 0xF81F) | (xgx & 0x07E0);
   4984 }
   4985 
   4986 /***************************************************************************************
   4987 ** Function name:           alphaBlend
   4988 ** Description:             Blend 16bit foreground and background with dither
   4989 *************************************************************************************x*/
   4990 uint16_t TFT_eSPI::alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc, uint8_t dither)
   4991 {
   4992   if (dither) {
   4993     int16_t alphaDither = (int16_t)alpha - dither + random(2*dither+1); // +/-4 randomised
   4994     alpha = (uint8_t)alphaDither;
   4995     if (alphaDither <  0) alpha = 0;
   4996     if (alphaDither >255) alpha = 255;
   4997   }
   4998 
   4999   return alphaBlend(alpha, fgc, bgc);
   5000 }
   5001 
   5002 /***************************************************************************************
   5003 ** Function name:           alphaBlend
   5004 ** Description:             Blend 24bit foreground and background with optional dither
   5005 *************************************************************************************x*/
   5006 uint32_t TFT_eSPI::alphaBlend24(uint8_t alpha, uint32_t fgc, uint32_t bgc, uint8_t dither)
   5007 {
   5008 
   5009   if (dither) {
   5010     int16_t alphaDither = (int16_t)alpha - dither + random(2*dither+1); // +/-dither randomised
   5011     alpha = (uint8_t)alphaDither;
   5012     if (alphaDither <  0) alpha = 0;
   5013     if (alphaDither >255) alpha = 255;
   5014   }
   5015 
   5016   uint32_t rxx = bgc & 0xFF0000;
   5017   rxx += ((fgc & 0xFF0000) - rxx) * alpha >> 8;
   5018   uint32_t xgx = bgc & 0x00FF00;
   5019   xgx += ((fgc & 0xFF0000) - xgx) * alpha >> 8;
   5020   uint32_t xxb = bgc & 0x0000FF;
   5021   xxb += ((fgc & 0xFF0000) - xxb) * alpha >> 8;
   5022   return (rxx & 0xFF0000) | (xgx & 0x00FF00) | (xxb & 0x0000FF);
   5023 }
   5024 
   5025 /***************************************************************************************
   5026 ** Function name:           write
   5027 ** Description:             draw characters piped through serial stream
   5028 ***************************************************************************************/
   5029 /* // Not all processors support buffered write
   5030 #ifndef ARDUINO_ARCH_ESP8266 // Avoid ESP8266 board package bug
   5031 size_t TFT_eSPI::write(const uint8_t *buf, size_t len)
   5032 {
   5033   inTransaction = true;
   5034 
   5035   uint8_t *lbuf = (uint8_t *)buf;
   5036   while(*lbuf !=0 && len--) write(*lbuf++);
   5037 
   5038   inTransaction = lockTransaction;
   5039   end_tft_write();
   5040   return 1;
   5041 }
   5042 #endif
   5043 */
   5044 /***************************************************************************************
   5045 ** Function name:           write
   5046 ** Description:             draw characters piped through serial stream
   5047 ***************************************************************************************/
   5048 size_t TFT_eSPI::write(uint8_t utf8)
   5049 {
   5050   if (_vpOoB) return 1;
   5051 
   5052   uint16_t uniCode = decodeUTF8(utf8);
   5053 
   5054   if (!uniCode) return 1;
   5055 
   5056   if (utf8 == '\r') return 1;
   5057 
   5058 #ifdef SMOOTH_FONT
   5059   if(fontLoaded) {
   5060     if (uniCode < 32 && utf8 != '\n') return 1;
   5061 
   5062     drawGlyph(uniCode);
   5063 
   5064     return 1;
   5065   }
   5066 #endif
   5067 
   5068   if (uniCode == '\n') uniCode+=22; // Make it a valid space character to stop errors
   5069 
   5070   uint16_t cwidth = 0;
   5071   uint16_t cheight = 0;
   5072 
   5073 //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv DEBUG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
   5074   //Serial.print((uint8_t) uniCode); // Debug line sends all printed TFT text to serial port
   5075   //Serial.println(uniCode, HEX); // Debug line sends all printed TFT text to serial port
   5076   //delay(5);                     // Debug optional wait for serial port to flush through
   5077 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DEBUG ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   5078 
   5079 //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   5080 #ifdef LOAD_GFXFF
   5081   if(!gfxFont) {
   5082 #endif
   5083 //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   5084 
   5085 #ifdef LOAD_FONT2
   5086   if (textfont == 2) {
   5087     if (uniCode < 32 || uniCode > 127) return 1;
   5088 
   5089     cwidth = pgm_read_byte(widtbl_f16 + uniCode-32);
   5090     cheight = chr_hgt_f16;
   5091     // Font 2 is rendered in whole byte widths so we must allow for this
   5092     cwidth = (cwidth + 6) / 8;  // Width in whole bytes for font 2, should be + 7 but must allow for font width change
   5093     cwidth = cwidth * 8;        // Width converted back to pixels
   5094   }
   5095   #ifdef LOAD_RLE
   5096   else
   5097   #endif
   5098 #endif
   5099 
   5100 #ifdef LOAD_RLE
   5101   {
   5102     if ((textfont>2) && (textfont<9)) {
   5103       if (uniCode < 32 || uniCode > 127) return 1;
   5104       // Uses the fontinfo struct array to avoid lots of 'if' or 'switch' statements
   5105       cwidth = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[textfont].widthtbl ) ) + uniCode-32 );
   5106       cheight= pgm_read_byte( &fontdata[textfont].height );
   5107     }
   5108   }
   5109 #endif
   5110 
   5111 #ifdef LOAD_GLCD
   5112   if (textfont==1) {
   5113       cwidth =  6;
   5114       cheight = 8;
   5115   }
   5116 #else
   5117   if (textfont==1) return 1;
   5118 #endif
   5119 
   5120   cheight = cheight * textsize;
   5121 
   5122   if (utf8 == '\n') {
   5123     cursor_y += cheight;
   5124     cursor_x  = 0;
   5125   }
   5126   else {
   5127     if (textwrapX && (cursor_x + cwidth * textsize > width())) {
   5128       cursor_y += cheight;
   5129       cursor_x = 0;
   5130     }
   5131     if (textwrapY && (cursor_y >= (int32_t) height())) cursor_y = 0;
   5132     cursor_x += drawChar(uniCode, cursor_x, cursor_y, textfont);
   5133   }
   5134 
   5135 //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   5136 #ifdef LOAD_GFXFF
   5137   } // Custom GFX font
   5138   else {
   5139     if(utf8 == '\n') {
   5140       cursor_x  = 0;
   5141       cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance);
   5142     } else {
   5143       if (uniCode > pgm_read_word(&gfxFont->last )) return 1;
   5144       if (uniCode < pgm_read_word(&gfxFont->first)) return 1;
   5145 
   5146       uint16_t   c2    = uniCode - pgm_read_word(&gfxFont->first);
   5147       GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]);
   5148       uint8_t   w     = pgm_read_byte(&glyph->width),
   5149                 h     = pgm_read_byte(&glyph->height);
   5150       if((w > 0) && (h > 0)) { // Is there an associated bitmap?
   5151         int16_t xo = (int8_t)pgm_read_byte(&glyph->xOffset);
   5152         if(textwrapX && ((cursor_x + textsize * (xo + w)) > width())) {
   5153           // Drawing character would go off right edge; wrap to new line
   5154           cursor_x  = 0;
   5155           cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance);
   5156         }
   5157         if (textwrapY && (cursor_y >= (int32_t) height())) cursor_y = 0;
   5158         drawChar(cursor_x, cursor_y, uniCode, textcolor, textbgcolor, textsize);
   5159       }
   5160       cursor_x += pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize;
   5161     }
   5162   }
   5163 #endif // LOAD_GFXFF
   5164 //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   5165 
   5166   return 1;
   5167 }
   5168 
   5169 
   5170 /***************************************************************************************
   5171 ** Function name:           drawChar
   5172 ** Description:             draw a Unicode glyph onto the screen
   5173 ***************************************************************************************/
   5174   // TODO: Rationalise with TFT_eSprite
   5175   // Any UTF-8 decoding must be done before calling drawChar()
   5176 int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y)
   5177 {
   5178   return drawChar(uniCode, x, y, textfont);
   5179 }
   5180 
   5181   // Any UTF-8 decoding must be done before calling drawChar()
   5182 int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font)
   5183 {
   5184   if (_vpOoB || !uniCode) return 0;
   5185 
   5186   if (font==1) {
   5187 #ifdef LOAD_GLCD
   5188   #ifndef LOAD_GFXFF
   5189     drawChar(x, y, uniCode, textcolor, textbgcolor, textsize);
   5190     return 6 * textsize;
   5191   #endif
   5192 #else
   5193   #ifndef LOAD_GFXFF
   5194     return 0;
   5195   #endif
   5196 #endif
   5197 
   5198 #ifdef LOAD_GFXFF
   5199     drawChar(x, y, uniCode, textcolor, textbgcolor, textsize);
   5200     if(!gfxFont) { // 'Classic' built-in font
   5201     #ifdef LOAD_GLCD
   5202       return 6 * textsize;
   5203     #else
   5204       return 0;
   5205     #endif
   5206     }
   5207     else {
   5208       if((uniCode >= pgm_read_word(&gfxFont->first)) && (uniCode <= pgm_read_word(&gfxFont->last) )) {
   5209         uint16_t   c2    = uniCode - pgm_read_word(&gfxFont->first);
   5210         GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]);
   5211         return pgm_read_byte(&glyph->xAdvance) * textsize;
   5212       }
   5213       else {
   5214         return 0;
   5215       }
   5216     }
   5217 #endif
   5218   }
   5219 
   5220   if ((font>1) && (font<9) && ((uniCode < 32) || (uniCode > 127))) return 0;
   5221 
   5222   int32_t width  = 0;
   5223   int32_t height = 0;
   5224   uint32_t flash_address = 0;
   5225   uniCode -= 32;
   5226 
   5227 #ifdef LOAD_FONT2
   5228   if (font == 2) {
   5229     flash_address = pgm_read_dword(&chrtbl_f16[uniCode]);
   5230     width = pgm_read_byte(widtbl_f16 + uniCode);
   5231     height = chr_hgt_f16;
   5232   }
   5233   #ifdef LOAD_RLE
   5234   else
   5235   #endif
   5236 #endif
   5237 
   5238 #ifdef LOAD_RLE
   5239   {
   5240     if ((font>2) && (font<9)) {
   5241       flash_address = pgm_read_dword( (const void*)(pgm_read_dword( &(fontdata[font].chartbl ) ) + uniCode*sizeof(void *)) );
   5242       width = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[font].widthtbl ) ) + uniCode );
   5243       height= pgm_read_byte( &fontdata[font].height );
   5244     }
   5245   }
   5246 #endif
   5247 
   5248   int32_t xd = x + _xDatum;
   5249   int32_t yd = y + _yDatum;
   5250 
   5251   if ((xd + width * textsize < _vpX || xd >= _vpW) && (yd + height * textsize < _vpY || yd >= _vpH)) return width * textsize ;
   5252 
   5253   int32_t w = width;
   5254   int32_t pX      = 0;
   5255   int32_t pY      = y;
   5256   uint8_t line = 0;
   5257   bool clip = xd < _vpX || xd + width  * textsize >= _vpW || yd < _vpY || yd + height * textsize >= _vpH;
   5258 
   5259 #ifdef LOAD_FONT2 // chop out code if we do not need it
   5260   if (font == 2) {
   5261     w = w + 6; // Should be + 7 but we need to compensate for width increment
   5262     w = w / 8;
   5263 
   5264     if (textcolor == textbgcolor || textsize != 1 || clip) {
   5265       //begin_tft_write();          // Sprite class can use this function, avoiding begin_tft_write()
   5266       inTransaction = true;
   5267 
   5268       for (int32_t i = 0; i < height; i++) {
   5269         if (textcolor != textbgcolor) fillRect(x, pY, width * textsize, textsize, textbgcolor);
   5270 
   5271         for (int32_t k = 0; k < w; k++) {
   5272           line = pgm_read_byte((uint8_t *)flash_address + w * i + k);
   5273           if (line) {
   5274             if (textsize == 1) {
   5275               pX = x + k * 8;
   5276               if (line & 0x80) drawPixel(pX, pY, textcolor);
   5277               if (line & 0x40) drawPixel(pX + 1, pY, textcolor);
   5278               if (line & 0x20) drawPixel(pX + 2, pY, textcolor);
   5279               if (line & 0x10) drawPixel(pX + 3, pY, textcolor);
   5280               if (line & 0x08) drawPixel(pX + 4, pY, textcolor);
   5281               if (line & 0x04) drawPixel(pX + 5, pY, textcolor);
   5282               if (line & 0x02) drawPixel(pX + 6, pY, textcolor);
   5283               if (line & 0x01) drawPixel(pX + 7, pY, textcolor);
   5284             }
   5285             else {
   5286               pX = x + k * 8 * textsize;
   5287               if (line & 0x80) fillRect(pX, pY, textsize, textsize, textcolor);
   5288               if (line & 0x40) fillRect(pX + textsize, pY, textsize, textsize, textcolor);
   5289               if (line & 0x20) fillRect(pX + 2 * textsize, pY, textsize, textsize, textcolor);
   5290               if (line & 0x10) fillRect(pX + 3 * textsize, pY, textsize, textsize, textcolor);
   5291               if (line & 0x08) fillRect(pX + 4 * textsize, pY, textsize, textsize, textcolor);
   5292               if (line & 0x04) fillRect(pX + 5 * textsize, pY, textsize, textsize, textcolor);
   5293               if (line & 0x02) fillRect(pX + 6 * textsize, pY, textsize, textsize, textcolor);
   5294               if (line & 0x01) fillRect(pX + 7 * textsize, pY, textsize, textsize, textcolor);
   5295             }
   5296           }
   5297         }
   5298         pY += textsize;
   5299       }
   5300 
   5301       inTransaction = lockTransaction;
   5302       end_tft_write();
   5303     }
   5304     else { // Faster drawing of characters and background using block write
   5305 
   5306       begin_tft_write();
   5307 
   5308       setWindow(xd, yd, xd + width - 1, yd + height - 1);
   5309 
   5310       uint8_t mask;
   5311       for (int32_t i = 0; i < height; i++) {
   5312         pX = width;
   5313         for (int32_t k = 0; k < w; k++) {
   5314           line = pgm_read_byte((uint8_t *) (flash_address + w * i + k) );
   5315           mask = 0x80;
   5316           while (mask && pX) {
   5317             if (line & mask) {tft_Write_16(textcolor);}
   5318             else {tft_Write_16(textbgcolor);}
   5319             pX--;
   5320             mask = mask >> 1;
   5321           }
   5322         }
   5323         if (pX) {tft_Write_16(textbgcolor);}
   5324       }
   5325 
   5326       end_tft_write();
   5327     }
   5328   }
   5329 
   5330   #ifdef LOAD_RLE
   5331   else
   5332   #endif
   5333 #endif  //FONT2
   5334 
   5335 #ifdef LOAD_RLE  //674 bytes of code
   5336   // Font is not 2 and hence is RLE encoded
   5337   {
   5338     begin_tft_write();
   5339     inTransaction = true;
   5340 
   5341     w *= height; // Now w is total number of pixels in the character
   5342     if (textcolor == textbgcolor && !clip) {
   5343 
   5344       int32_t px = 0, py = pY; // To hold character block start and end column and row values
   5345       int32_t pc = 0; // Pixel count
   5346       uint8_t np = textsize * textsize; // Number of pixels in a drawn pixel
   5347 
   5348       uint8_t tnp = 0; // Temporary copy of np for while loop
   5349       uint8_t ts = textsize - 1; // Temporary copy of textsize
   5350       // 16 bit pixel count so maximum font size is equivalent to 180x180 pixels in area
   5351       // w is total number of pixels to plot to fill character block
   5352       while (pc < w) {
   5353         line = pgm_read_byte((uint8_t *)flash_address);
   5354         flash_address++;
   5355         if (line & 0x80) {
   5356           line &= 0x7F;
   5357           line++;
   5358           if (ts) {
   5359             px = xd + textsize * (pc % width); // Keep these px and py calculations outside the loop as they are slow
   5360             py = yd + textsize * (pc / width);
   5361           }
   5362           else {
   5363             px = xd + pc % width; // Keep these px and py calculations outside the loop as they are slow
   5364             py = yd + pc / width;
   5365           }
   5366           while (line--) { // In this case the while(line--) is faster
   5367             pc++; // This is faster than putting pc+=line before while()?
   5368             setWindow(px, py, px + ts, py + ts);
   5369 
   5370             if (ts) {
   5371               tnp = np;
   5372               while (tnp--) {tft_Write_16(textcolor);}
   5373             }
   5374             else {tft_Write_16(textcolor);}
   5375             px += textsize;
   5376 
   5377             if (px >= (xd + width * textsize)) {
   5378               px = xd;
   5379               py += textsize;
   5380             }
   5381           }
   5382         }
   5383         else {
   5384           line++;
   5385           pc += line;
   5386         }
   5387       }
   5388     }
   5389     else {
   5390       // Text colour != background and textsize = 1 and character is within viewport area
   5391       // so use faster drawing of characters and background using block write
   5392       if (textcolor != textbgcolor && textsize == 1 && !clip)
   5393       {
   5394         setWindow(xd, yd, xd + width - 1, yd + height - 1);
   5395 
   5396         // Maximum font size is equivalent to 180x180 pixels in area
   5397         while (w > 0) {
   5398           line = pgm_read_byte((uint8_t *)flash_address++); // 8 bytes smaller when incrementing here
   5399           if (line & 0x80) {
   5400             line &= 0x7F;
   5401             line++; w -= line;
   5402             pushBlock(textcolor,line);
   5403           }
   5404           else {
   5405             line++; w -= line;
   5406             pushBlock(textbgcolor,line);
   5407           }
   5408         }
   5409       }
   5410       else
   5411       {
   5412         int32_t px = 0, py = 0;  // To hold character pixel coords
   5413         int32_t tx = 0, ty = 0;  // To hold character TFT pixel coords
   5414         int32_t pc = 0;          // Pixel count
   5415         int32_t pl = 0;          // Pixel line length
   5416         uint16_t pcol = 0;       // Pixel color
   5417         bool     pf = true;      // Flag for plotting
   5418         while (pc < w) {
   5419           line = pgm_read_byte((uint8_t *)flash_address);
   5420           flash_address++;
   5421           if (line & 0x80) { pcol = textcolor; line &= 0x7F; pf = true;}
   5422           else { pcol = textbgcolor; if (textcolor == textbgcolor) pf = false;}
   5423           line++;
   5424           px = pc % width;
   5425           tx = x + textsize * px;
   5426           py = pc / width;
   5427           ty = y + textsize * py;
   5428 
   5429           pl = 0;
   5430           pc += line;
   5431           while (line--) {
   5432             pl++;
   5433             if ((px+pl) >= width) {
   5434               if (pf) fillRect(tx, ty, pl * textsize, textsize, pcol);
   5435               pl = 0;
   5436               px = 0;
   5437               tx = x;
   5438               py ++;
   5439               ty += textsize;
   5440             }
   5441           }
   5442           if (pl && pf) fillRect(tx, ty, pl * textsize, textsize, pcol);
   5443         }
   5444       }
   5445     }
   5446     inTransaction = lockTransaction;
   5447     end_tft_write();
   5448   }
   5449   // End of RLE font rendering
   5450 #endif
   5451 
   5452 #if !defined (LOAD_FONT2) && !defined (LOAD_RLE)
   5453   // Stop warnings
   5454   flash_address = flash_address;
   5455   w = w;
   5456   pX = pX;
   5457   pY = pY;
   5458   line = line;
   5459   clip = clip;
   5460 #endif
   5461 
   5462   return width * textsize;    // x +
   5463 }
   5464 
   5465 
   5466 /***************************************************************************************
   5467 ** Function name:           drawString (with or without user defined font)
   5468 ** Description :            draw string with padding if it is defined
   5469 ***************************************************************************************/
   5470 // Without font number, uses font set by setTextFont()
   5471 int16_t TFT_eSPI::drawString(const String& string, int32_t poX, int32_t poY)
   5472 {
   5473   int16_t len = string.length() + 2;
   5474   char buffer[len];
   5475   string.toCharArray(buffer, len);
   5476   return drawString(buffer, poX, poY, textfont);
   5477 }
   5478 // With font number
   5479 int16_t TFT_eSPI::drawString(const String& string, int32_t poX, int32_t poY, uint8_t font)
   5480 {
   5481   int16_t len = string.length() + 2;
   5482   char buffer[len];
   5483   string.toCharArray(buffer, len);
   5484   return drawString(buffer, poX, poY, font);
   5485 }
   5486 
   5487 // Without font number, uses font set by setTextFont()
   5488 int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY)
   5489 {
   5490   return drawString(string, poX, poY, textfont);
   5491 }
   5492 
   5493 // With font number. Note: font number is over-ridden if a smooth font is loaded
   5494 int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8_t font)
   5495 {
   5496   int16_t sumX = 0;
   5497   uint8_t padding = 1, baseline = 0;
   5498   uint16_t cwidth = textWidth(string, font); // Find the pixel width of the string in the font
   5499   uint16_t cheight = 8 * textsize;
   5500 
   5501 #ifdef LOAD_GFXFF
   5502   #ifdef SMOOTH_FONT
   5503     bool freeFont = (font == 1 && gfxFont && !fontLoaded);
   5504   #else
   5505     bool freeFont = (font == 1 && gfxFont);
   5506   #endif
   5507 
   5508   if (freeFont) {
   5509     cheight = glyph_ab * textsize;
   5510     poY += cheight; // Adjust for baseline datum of free fonts
   5511     baseline = cheight;
   5512     padding =101; // Different padding method used for Free Fonts
   5513 
   5514     // We need to make an adjustment for the bottom of the string (eg 'y' character)
   5515     if ((textdatum == BL_DATUM) || (textdatum == BC_DATUM) || (textdatum == BR_DATUM)) {
   5516       cheight += glyph_bb * textsize;
   5517     }
   5518   }
   5519 #endif
   5520 
   5521 
   5522   // If it is not font 1 (GLCD or free font) get the baseline and pixel height of the font
   5523 #ifdef SMOOTH_FONT
   5524   if(fontLoaded) {
   5525     baseline = gFont.maxAscent;
   5526     cheight  = fontHeight();
   5527   }
   5528   else
   5529 #endif
   5530   if (font!=1) {
   5531     baseline = pgm_read_byte( &fontdata[font].baseline ) * textsize;
   5532     cheight = fontHeight(font);
   5533   }
   5534 
   5535   if (textdatum || padX) {
   5536 
   5537     switch(textdatum) {
   5538       case TC_DATUM:
   5539         poX -= cwidth/2;
   5540         padding += 1;
   5541         break;
   5542       case TR_DATUM:
   5543         poX -= cwidth;
   5544         padding += 2;
   5545         break;
   5546       case ML_DATUM:
   5547         poY -= cheight/2;
   5548         //padding += 0;
   5549         break;
   5550       case MC_DATUM:
   5551         poX -= cwidth/2;
   5552         poY -= cheight/2;
   5553         padding += 1;
   5554         break;
   5555       case MR_DATUM:
   5556         poX -= cwidth;
   5557         poY -= cheight/2;
   5558         padding += 2;
   5559         break;
   5560       case BL_DATUM:
   5561         poY -= cheight;
   5562         //padding += 0;
   5563         break;
   5564       case BC_DATUM:
   5565         poX -= cwidth/2;
   5566         poY -= cheight;
   5567         padding += 1;
   5568         break;
   5569       case BR_DATUM:
   5570         poX -= cwidth;
   5571         poY -= cheight;
   5572         padding += 2;
   5573         break;
   5574       case L_BASELINE:
   5575         poY -= baseline;
   5576         //padding += 0;
   5577         break;
   5578       case C_BASELINE:
   5579         poX -= cwidth/2;
   5580         poY -= baseline;
   5581         padding += 1;
   5582         break;
   5583       case R_BASELINE:
   5584         poX -= cwidth;
   5585         poY -= baseline;
   5586         padding += 2;
   5587         break;
   5588     }
   5589   }
   5590 
   5591 
   5592   int8_t xo = 0;
   5593 #ifdef LOAD_GFXFF
   5594   if (freeFont && (textcolor!=textbgcolor)) {
   5595       cheight = (glyph_ab + glyph_bb) * textsize;
   5596       // Get the offset for the first character only to allow for negative offsets
   5597       uint16_t c2 = 0;
   5598       uint16_t len = strlen(string);
   5599       uint16_t n = 0;
   5600 
   5601       while (n < len && c2 == 0) c2 = decodeUTF8((uint8_t*)string, &n, len - n);
   5602 
   5603       if((c2 >= pgm_read_word(&gfxFont->first)) && (c2 <= pgm_read_word(&gfxFont->last) )) {
   5604         c2 -= pgm_read_word(&gfxFont->first);
   5605         GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]);
   5606         xo = pgm_read_byte(&glyph->xOffset) * textsize;
   5607         // Adjust for negative xOffset
   5608         if (xo > 0) xo = 0;
   5609         else cwidth -= xo;
   5610         // Add 1 pixel of padding all round
   5611         //cheight +=2;
   5612         //fillRect(poX+xo-1, poY - 1 - glyph_ab * textsize, cwidth+2, cheight, textbgcolor);
   5613         fillRect(poX+xo, poY - glyph_ab * textsize, cwidth, cheight, textbgcolor);
   5614       }
   5615       padding -=100;
   5616     }
   5617 #endif
   5618 
   5619   uint16_t len = strlen(string);
   5620   uint16_t n = 0;
   5621 
   5622 #ifdef SMOOTH_FONT
   5623   if(fontLoaded) {
   5624     setCursor(poX, poY);
   5625 
   5626     bool fillbg = _fillbg;
   5627     // If padding is requested then fill the text background
   5628     if (padX && !_fillbg) _fillbg = true;
   5629 
   5630     while (n < len) {
   5631       uint16_t uniCode = decodeUTF8((uint8_t*)string, &n, len - n);
   5632       drawGlyph(uniCode);
   5633     }
   5634     _fillbg = fillbg; // restore state
   5635     sumX += cwidth;
   5636     //fontFile.close();
   5637   }
   5638   else
   5639 #endif
   5640   {
   5641     while (n < len) {
   5642       uint16_t uniCode = decodeUTF8((uint8_t*)string, &n, len - n);
   5643       sumX += drawChar(uniCode, poX+sumX, poY, font);
   5644     }
   5645   }
   5646 
   5647 //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv DEBUG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
   5648 // Switch on debugging for the padding areas
   5649 //#define PADDING_DEBUG
   5650 
   5651 #ifndef PADDING_DEBUG
   5652 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DEBUG ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   5653 
   5654   if((padX>cwidth) && (textcolor!=textbgcolor)) {
   5655     int16_t padXc = poX+cwidth+xo;
   5656 #ifdef LOAD_GFXFF
   5657     if (freeFont) {
   5658       poX +=xo; // Adjust for negative offset start character
   5659       poY -= glyph_ab * textsize;
   5660       sumX += poX;
   5661     }
   5662 #endif
   5663     switch(padding) {
   5664       case 1:
   5665         fillRect(padXc,poY,padX-cwidth,cheight, textbgcolor);
   5666         break;
   5667       case 2:
   5668         fillRect(padXc,poY,(padX-cwidth)>>1,cheight, textbgcolor);
   5669         padXc = poX - ((padX-cwidth)>>1);
   5670         fillRect(padXc,poY,(padX-cwidth)>>1,cheight, textbgcolor);
   5671         break;
   5672       case 3:
   5673         if (padXc>padX) padXc = padX;
   5674         fillRect(poX + cwidth - padXc,poY,padXc-cwidth,cheight, textbgcolor);
   5675         break;
   5676     }
   5677   }
   5678 
   5679 
   5680 #else
   5681 
   5682 //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv DEBUG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
   5683 // This is debug code to show text (green box) and blanked (white box) areas
   5684 // It shows that the padding areas are being correctly sized and positioned
   5685 
   5686   if((padX>sumX) && (textcolor!=textbgcolor)) {
   5687     int16_t padXc = poX+sumX; // Maximum left side padding
   5688 #ifdef LOAD_GFXFF
   5689     if ((font == 1) && (gfxFont)) poY -= glyph_ab;
   5690 #endif
   5691     drawRect(poX,poY,sumX,cheight, TFT_GREEN);
   5692     switch(padding) {
   5693       case 1:
   5694         drawRect(padXc,poY,padX-sumX,cheight, TFT_WHITE);
   5695         break;
   5696       case 2:
   5697         drawRect(padXc,poY,(padX-sumX)>>1, cheight, TFT_WHITE);
   5698         padXc = (padX-sumX)>>1;
   5699         drawRect(poX - padXc,poY,(padX-sumX)>>1,cheight, TFT_WHITE);
   5700         break;
   5701       case 3:
   5702         if (padXc>padX) padXc = padX;
   5703         drawRect(poX + sumX - padXc,poY,padXc-sumX,cheight, TFT_WHITE);
   5704         break;
   5705     }
   5706   }
   5707 #endif
   5708 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DEBUG ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   5709 
   5710 return sumX;
   5711 }
   5712 
   5713 
   5714 /***************************************************************************************
   5715 ** Function name:           drawCentreString (deprecated, use setTextDatum())
   5716 ** Descriptions:            draw string centred on dX
   5717 ***************************************************************************************/
   5718 int16_t TFT_eSPI::drawCentreString(const String& string, int32_t dX, int32_t poY, uint8_t font)
   5719 {
   5720   int16_t len = string.length() + 2;
   5721   char buffer[len];
   5722   string.toCharArray(buffer, len);
   5723   return drawCentreString(buffer, dX, poY, font);
   5724 }
   5725 
   5726 int16_t TFT_eSPI::drawCentreString(const char *string, int32_t dX, int32_t poY, uint8_t font)
   5727 {
   5728   uint8_t tempdatum = textdatum;
   5729   int32_t sumX = 0;
   5730   textdatum = TC_DATUM;
   5731   sumX = drawString(string, dX, poY, font);
   5732   textdatum = tempdatum;
   5733   return sumX;
   5734 }
   5735 
   5736 
   5737 /***************************************************************************************
   5738 ** Function name:           drawRightString (deprecated, use setTextDatum())
   5739 ** Descriptions:            draw string right justified to dX
   5740 ***************************************************************************************/
   5741 int16_t TFT_eSPI::drawRightString(const String& string, int32_t dX, int32_t poY, uint8_t font)
   5742 {
   5743   int16_t len = string.length() + 2;
   5744   char buffer[len];
   5745   string.toCharArray(buffer, len);
   5746   return drawRightString(buffer, dX, poY, font);
   5747 }
   5748 
   5749 int16_t TFT_eSPI::drawRightString(const char *string, int32_t dX, int32_t poY, uint8_t font)
   5750 {
   5751   uint8_t tempdatum = textdatum;
   5752   int16_t sumX = 0;
   5753   textdatum = TR_DATUM;
   5754   sumX = drawString(string, dX, poY, font);
   5755   textdatum = tempdatum;
   5756   return sumX;
   5757 }
   5758 
   5759 
   5760 /***************************************************************************************
   5761 ** Function name:           drawNumber
   5762 ** Description:             draw a long integer
   5763 ***************************************************************************************/
   5764 int16_t TFT_eSPI::drawNumber(long long_num, int32_t poX, int32_t poY)
   5765 {
   5766   isDigits = true; // Eliminate jiggle in monospaced fonts
   5767   char str[12];
   5768   ltoa(long_num, str, 10);
   5769   return drawString(str, poX, poY, textfont);
   5770 }
   5771 
   5772 int16_t TFT_eSPI::drawNumber(long long_num, int32_t poX, int32_t poY, uint8_t font)
   5773 {
   5774   isDigits = true; // Eliminate jiggle in monospaced fonts
   5775   char str[12];
   5776   ltoa(long_num, str, 10);
   5777   return drawString(str, poX, poY, font);
   5778 }
   5779 
   5780 
   5781 /***************************************************************************************
   5782 ** Function name:           drawFloat
   5783 ** Descriptions:            drawFloat, prints 7 non zero digits maximum
   5784 ***************************************************************************************/
   5785 // Assemble and print a string, this permits alignment relative to a datum
   5786 // looks complicated but much more compact and actually faster than using print class
   5787 int16_t TFT_eSPI::drawFloat(float floatNumber, uint8_t dp, int32_t poX, int32_t poY)
   5788 {
   5789   return drawFloat(floatNumber, dp, poX, poY, textfont);
   5790 }
   5791 
   5792 int16_t TFT_eSPI::drawFloat(float floatNumber, uint8_t dp, int32_t poX, int32_t poY, uint8_t font)
   5793 {
   5794   isDigits = true;
   5795   char str[14];               // Array to contain decimal string
   5796   uint8_t ptr = 0;            // Initialise pointer for array
   5797   int8_t  digits = 1;         // Count the digits to avoid array overflow
   5798   float rounding = 0.5;       // Round up down delta
   5799   bool negative = false;
   5800 
   5801   if (dp > 7) dp = 7; // Limit the size of decimal portion
   5802 
   5803   // Adjust the rounding value
   5804   for (uint8_t i = 0; i < dp; ++i) rounding /= 10.0;
   5805 
   5806   if (floatNumber < -rounding) {   // add sign, avoid adding - sign to 0.0!
   5807     str[ptr++] = '-'; // Negative number
   5808     str[ptr] = 0; // Put a null in the array as a precaution
   5809     digits = 0;   // Set digits to 0 to compensate so pointer value can be used later
   5810     floatNumber = -floatNumber; // Make positive
   5811     negative = true;
   5812   }
   5813 
   5814   floatNumber += rounding; // Round up or down
   5815 
   5816   if (dp == 0) {
   5817     if (negative) floatNumber = -floatNumber;
   5818     return drawNumber((long)floatNumber, poX, poY, font);
   5819   }
   5820 
   5821   // For error put ... in string and return (all TFT_eSPI library fonts contain . character)
   5822   if (floatNumber >= 2147483647) {
   5823     strcpy(str, "...");
   5824     return drawString(str, poX, poY, font);
   5825   }
   5826   // No chance of overflow from here on
   5827 
   5828   // Get integer part
   5829   uint32_t temp = (uint32_t)floatNumber;
   5830 
   5831   // Put integer part into array
   5832   ltoa(temp, str + ptr, 10);
   5833 
   5834   // Find out where the null is to get the digit count loaded
   5835   while ((uint8_t)str[ptr] != 0) ptr++; // Move the pointer along
   5836   digits += ptr;                  // Count the digits
   5837 
   5838   str[ptr++] = '.'; // Add decimal point
   5839   str[ptr] = '0';   // Add a dummy zero
   5840   str[ptr + 1] = 0; // Add a null but don't increment pointer so it can be overwritten
   5841 
   5842   // Get the decimal portion
   5843   floatNumber = floatNumber - temp;
   5844 
   5845   // Get decimal digits one by one and put in array
   5846   // Limit digit count so we don't get a false sense of resolution
   5847   uint8_t i = 0;
   5848   while ((i < dp) && (digits < 9)) { // while (i < dp) for no limit but array size must be increased
   5849     i++;
   5850     floatNumber *= 10;       // for the next decimal
   5851     temp = floatNumber;      // get the decimal
   5852     ltoa(temp, str + ptr, 10);
   5853     ptr++; digits++;         // Increment pointer and digits count
   5854     floatNumber -= temp;     // Remove that digit
   5855   }
   5856 
   5857   // Finally we can plot the string and return pixel length
   5858   return drawString(str, poX, poY, font);
   5859 }
   5860 
   5861 
   5862 /***************************************************************************************
   5863 ** Function name:           setFreeFont
   5864 ** Descriptions:            Sets the GFX free font to use
   5865 ***************************************************************************************/
   5866 
   5867 #ifdef LOAD_GFXFF
   5868 
   5869 void TFT_eSPI::setFreeFont(const GFXfont *f)
   5870 {
   5871   if (f == nullptr) { // Fix issue #400 (ESP32 crash)
   5872     setTextFont(1); // Use GLCD font
   5873     return;
   5874   }
   5875 
   5876   textfont = 1;
   5877   gfxFont = (GFXfont *)f;
   5878 
   5879   glyph_ab = 0;
   5880   glyph_bb = 0;
   5881   uint16_t numChars = pgm_read_word(&gfxFont->last) - pgm_read_word(&gfxFont->first);
   5882 
   5883   // Find the biggest above and below baseline offsets
   5884   for (uint16_t c = 0; c < numChars; c++) {
   5885     GFXglyph *glyph1  = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]);
   5886     int8_t ab = -pgm_read_byte(&glyph1->yOffset);
   5887     if (ab > glyph_ab) glyph_ab = ab;
   5888     int8_t bb = pgm_read_byte(&glyph1->height) - ab;
   5889     if (bb > glyph_bb) glyph_bb = bb;
   5890   }
   5891 }
   5892 
   5893 
   5894 /***************************************************************************************
   5895 ** Function name:           setTextFont
   5896 ** Description:             Set the font for the print stream
   5897 ***************************************************************************************/
   5898 void TFT_eSPI::setTextFont(uint8_t f)
   5899 {
   5900   textfont = (f > 0) ? f : 1; // Don't allow font 0
   5901   gfxFont = NULL;
   5902 }
   5903 
   5904 #else
   5905 
   5906 
   5907 /***************************************************************************************
   5908 ** Function name:           setFreeFont
   5909 ** Descriptions:            Sets the GFX free font to use
   5910 ***************************************************************************************/
   5911 
   5912 // Alternative to setTextFont() so we don't need two different named functions
   5913 void TFT_eSPI::setFreeFont(uint8_t font)
   5914 {
   5915   setTextFont(font);
   5916 }
   5917 
   5918 
   5919 /***************************************************************************************
   5920 ** Function name:           setTextFont
   5921 ** Description:             Set the font for the print stream
   5922 ***************************************************************************************/
   5923 void TFT_eSPI::setTextFont(uint8_t f)
   5924 {
   5925   textfont = (f > 0) ? f : 1; // Don't allow font 0
   5926 }
   5927 #endif
   5928 
   5929 
   5930 /***************************************************************************************
   5931 ** Function name:           getSPIinstance
   5932 ** Description:             Get the instance of the SPI class
   5933 ***************************************************************************************/
   5934 #if !defined (TFT_PARALLEL_8_BIT) && ! defined (RP2040_PIO_INTERFACE)
   5935 SPIClass& TFT_eSPI::getSPIinstance(void)
   5936 {
   5937   return spi;
   5938 }
   5939 #endif
   5940 
   5941 
   5942 /***************************************************************************************
   5943 ** Function name:           verifySetupID
   5944 ** Description:             Compare the ID if USER_SETUP_ID defined in user setup file
   5945 ***************************************************************************************/
   5946 bool TFT_eSPI::verifySetupID(uint32_t id)
   5947 {
   5948 #if defined (USER_SETUP_ID)
   5949   if (USER_SETUP_ID == id) return true;
   5950 #else
   5951   id = id; // Avoid warning
   5952 #endif
   5953   return false;
   5954 }
   5955 
   5956 /***************************************************************************************
   5957 ** Function name:           getSetup
   5958 ** Description:             Get the setup details for diagnostic and sketch access
   5959 ***************************************************************************************/
   5960 void TFT_eSPI::getSetup(setup_t &tft_settings)
   5961 {
   5962 // tft_settings.version is set in header file
   5963 
   5964 #if defined (USER_SETUP_INFO)
   5965   tft_settings.setup_info = USER_SETUP_INFO;
   5966 #else
   5967   tft_settings.setup_info = "NA";
   5968 #endif
   5969 
   5970 #if defined (USER_SETUP_ID)
   5971   tft_settings.setup_id = USER_SETUP_ID;
   5972 #else
   5973   tft_settings.setup_id = 0;
   5974 #endif
   5975 
   5976 #if defined (PROCESSOR_ID)
   5977   tft_settings.esp = PROCESSOR_ID;
   5978 #else
   5979   tft_settings.esp = -1;
   5980 #endif
   5981 
   5982 #if defined (SUPPORT_TRANSACTIONS)
   5983   tft_settings.trans = true;
   5984 #else
   5985   tft_settings.trans = false;
   5986 #endif
   5987 
   5988 #if defined (TFT_PARALLEL_8_BIT) || defined(TFT_PARALLEL_16_BIT)
   5989   tft_settings.serial = false;
   5990   tft_settings.tft_spi_freq = 0;
   5991 #else
   5992   tft_settings.serial = true;
   5993   tft_settings.tft_spi_freq = SPI_FREQUENCY/100000;
   5994   #ifdef SPI_READ_FREQUENCY
   5995     tft_settings.tft_rd_freq = SPI_READ_FREQUENCY/100000;
   5996   #endif
   5997   #ifndef GENERIC_PROCESSOR
   5998     #ifdef TFT_SPI_PORT
   5999       tft_settings.port = TFT_SPI_PORT;
   6000     #else
   6001       tft_settings.port = 255;
   6002     #endif
   6003   #endif
   6004   #ifdef RP2040_PIO_SPI
   6005     tft_settings.interface = 0x10;
   6006   #else
   6007     tft_settings.interface = 0x0;
   6008   #endif
   6009 #endif
   6010 
   6011 #if defined(TFT_SPI_OVERLAP)
   6012   tft_settings.overlap = true;
   6013 #else
   6014   tft_settings.overlap = false;
   6015 #endif
   6016 
   6017   tft_settings.tft_driver = TFT_DRIVER;
   6018   tft_settings.tft_width  = _init_width;
   6019   tft_settings.tft_height = _init_height;
   6020 
   6021 #ifdef CGRAM_OFFSET
   6022   tft_settings.r0_x_offset = colstart;
   6023   tft_settings.r0_y_offset = rowstart;
   6024   tft_settings.r1_x_offset = 0;
   6025   tft_settings.r1_y_offset = 0;
   6026   tft_settings.r2_x_offset = 0;
   6027   tft_settings.r2_y_offset = 0;
   6028   tft_settings.r3_x_offset = 0;
   6029   tft_settings.r3_y_offset = 0;
   6030 #else
   6031   tft_settings.r0_x_offset = 0;
   6032   tft_settings.r0_y_offset = 0;
   6033   tft_settings.r1_x_offset = 0;
   6034   tft_settings.r1_y_offset = 0;
   6035   tft_settings.r2_x_offset = 0;
   6036   tft_settings.r2_y_offset = 0;
   6037   tft_settings.r3_x_offset = 0;
   6038   tft_settings.r3_y_offset = 0;
   6039 #endif
   6040 
   6041 #if defined (TFT_MOSI)
   6042   tft_settings.pin_tft_mosi = TFT_MOSI;
   6043 #else
   6044   tft_settings.pin_tft_mosi = -1;
   6045 #endif
   6046 
   6047 #if defined (TFT_MISO)
   6048   tft_settings.pin_tft_miso = TFT_MISO;
   6049 #else
   6050   tft_settings.pin_tft_miso = -1;
   6051 #endif
   6052 
   6053 #if defined (TFT_SCLK)
   6054   tft_settings.pin_tft_clk  = TFT_SCLK;
   6055 #else
   6056   tft_settings.pin_tft_clk  = -1;
   6057 #endif
   6058 
   6059 #if defined (TFT_CS)
   6060   tft_settings.pin_tft_cs   = TFT_CS;
   6061 #else
   6062   tft_settings.pin_tft_cs   = -1;
   6063 #endif
   6064 
   6065 #if defined (TFT_DC)
   6066   tft_settings.pin_tft_dc  = TFT_DC;
   6067 #else
   6068   tft_settings.pin_tft_dc  = -1;
   6069 #endif
   6070 
   6071 #if defined (TFT_RD)
   6072   tft_settings.pin_tft_rd  = TFT_RD;
   6073 #else
   6074   tft_settings.pin_tft_rd  = -1;
   6075 #endif
   6076 
   6077 #if defined (TFT_WR)
   6078   tft_settings.pin_tft_wr  = TFT_WR;
   6079 #else
   6080   tft_settings.pin_tft_wr  = -1;
   6081 #endif
   6082 
   6083 #if defined (TFT_RST)
   6084   tft_settings.pin_tft_rst = TFT_RST;
   6085 #else
   6086   tft_settings.pin_tft_rst = -1;
   6087 #endif
   6088 
   6089 #if defined (TFT_PARALLEL_8_BIT) || defined(TFT_PARALLEL_16_BIT)
   6090   tft_settings.pin_tft_d0 = TFT_D0;
   6091   tft_settings.pin_tft_d1 = TFT_D1;
   6092   tft_settings.pin_tft_d2 = TFT_D2;
   6093   tft_settings.pin_tft_d3 = TFT_D3;
   6094   tft_settings.pin_tft_d4 = TFT_D4;
   6095   tft_settings.pin_tft_d5 = TFT_D5;
   6096   tft_settings.pin_tft_d6 = TFT_D6;
   6097   tft_settings.pin_tft_d7 = TFT_D7;
   6098 #else
   6099   tft_settings.pin_tft_d0 = -1;
   6100   tft_settings.pin_tft_d1 = -1;
   6101   tft_settings.pin_tft_d2 = -1;
   6102   tft_settings.pin_tft_d3 = -1;
   6103   tft_settings.pin_tft_d4 = -1;
   6104   tft_settings.pin_tft_d5 = -1;
   6105   tft_settings.pin_tft_d6 = -1;
   6106   tft_settings.pin_tft_d7 = -1;
   6107 #endif
   6108 
   6109 #if defined (TFT_BL)
   6110   tft_settings.pin_tft_led = TFT_BL;
   6111 #endif
   6112 
   6113 #if defined (TFT_BACKLIGHT_ON)
   6114   tft_settings.pin_tft_led_on = TFT_BACKLIGHT_ON;
   6115 #endif
   6116 
   6117 #if defined (TOUCH_CS)
   6118   tft_settings.pin_tch_cs   = TOUCH_CS;
   6119   tft_settings.tch_spi_freq = SPI_TOUCH_FREQUENCY/100000;
   6120 #else
   6121   tft_settings.pin_tch_cs   = -1;
   6122   tft_settings.tch_spi_freq = 0;
   6123 #endif
   6124 }
   6125 
   6126 
   6127 ////////////////////////////////////////////////////////////////////////////////////////
   6128 #ifdef TOUCH_CS
   6129   #include "Extensions/Touch.cpp"
   6130 #endif
   6131 
   6132 #include "Extensions/Button.cpp"
   6133 
   6134 #include "Extensions/Sprite.cpp"
   6135 
   6136 #ifdef SMOOTH_FONT
   6137   #include "Extensions/Smooth_font.cpp"
   6138 #endif
   6139 
   6140 #ifdef AA_GRAPHICS
   6141   #include "Extensions/AA_graphics.cpp"  // Loaded if SMOOTH_FONT is defined by user
   6142 #endif
   6143 ////////////////////////////////////////////////////////////////////////////////////////
   6144