acidportal

- 😈 Worlds smallest Evil Portal on a LilyGo T-QT
git clone git://git.acid.vegas/acidportal.git
Log | Files | Refs | Archive | README | LICENSE

Sprite.cpp (81023B)

      1 /**************************************************************************************
      2 // The following class creates Sprites in RAM, graphics can then be drawn in the Sprite
      3 // and rendered quickly onto the TFT screen. The class inherits the graphics functions
      4 // from the TFT_eSPI class. Some functions are overridden by this class so that the
      5 // graphics are written to the Sprite rather than the TFT.
      6 // Coded by Bodmer, see license file in root folder
      7 ***************************************************************************************/
      8 /***************************************************************************************
      9 // Color bytes are swapped when writing to RAM, this introduces a small overhead but
     10 // there is a nett performance gain by using swapped bytes.
     11 ***************************************************************************************/
     12 
     13 /***************************************************************************************
     14 ** Function name:           TFT_eSprite
     15 ** Description:             Class constructor
     16 ***************************************************************************************/
     17 TFT_eSprite::TFT_eSprite(TFT_eSPI *tft)
     18 {
     19   _tft = tft;     // Pointer to tft class so we can call member functions
     20 
     21   _iwidth    = 0; // Initialise width and height to 0 (it does not exist yet)
     22   _iheight   = 0;
     23   _bpp = 16;
     24   _swapBytes = false;   // Do not swap pushImage colour bytes by default
     25 
     26   _created = false;
     27   _vpOoB   = true;
     28 
     29   _xs = 0;  // window bounds for pushColor
     30   _ys = 0;
     31   _xe = 0;
     32   _ye = 0;
     33 
     34   _xptr = 0; // pushColor coordinate
     35   _yptr = 0;
     36 
     37   _colorMap = nullptr;
     38 
     39   _psram_enable = true;
     40   
     41   // Ensure end_tft_write() does nothing in inherited functions.
     42   lockTransaction = true;
     43 }
     44 
     45 
     46 /***************************************************************************************
     47 ** Function name:           createSprite
     48 ** Description:             Create a sprite (bitmap) of defined width and height
     49 ***************************************************************************************/
     50 // cast returned value to (uint8_t*) for 8 bit or (uint16_t*) for 16 bit colours
     51 void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames)
     52 {
     53 
     54   if ( _created ) return _img8_1;
     55 
     56   if ( w < 1 || h < 1 ) return nullptr;
     57 
     58   _iwidth  = _dwidth  = _bitwidth = w;
     59   _iheight = _dheight = h;
     60 
     61   cursor_x = 0;
     62   cursor_y = 0;
     63 
     64   // Default scroll rectangle and gap fill colour
     65   _sx = 0;
     66   _sy = 0;
     67   _sw = w;
     68   _sh = h;
     69   _scolor = TFT_BLACK;
     70 
     71   _img8   = (uint8_t*) callocSprite(w, h, frames);
     72   _img8_1 = _img8;
     73   _img8_2 = _img8;
     74   _img    = (uint16_t*) _img8;
     75   _img4   = _img8;
     76 
     77   if ( (_bpp == 16) && (frames > 1) ) {
     78     _img8_2 = _img8 + (w * h * 2 + 1);
     79   }
     80 
     81   // ESP32 only 16bpp check
     82   //if (esp_ptr_dma_capable(_img8_1)) Serial.println("DMA capable Sprite pointer _img8_1");
     83   //else Serial.println("Not a DMA capable Sprite pointer _img8_1");
     84   //if (esp_ptr_dma_capable(_img8_2)) Serial.println("DMA capable Sprite pointer _img8_2");
     85   //else Serial.println("Not a DMA capable Sprite pointer _img8_2");
     86 
     87   if ( (_bpp == 8) && (frames > 1) ) {
     88     _img8_2 = _img8 + (w * h + 1);
     89   }
     90 
     91   // This is to make it clear what pointer size is expected to be used
     92   // but casting in the user sketch is needed due to the use of void*
     93   if ( (_bpp == 1) && (frames > 1) )
     94   {
     95     w = (w+7) & 0xFFF8;
     96     _img8_2 = _img8 + ( (w>>3) * h + 1 );
     97   }
     98 
     99   if (_img8)
    100   {
    101     _created = true;
    102     if ( (_bpp == 4) && (_colorMap == nullptr)) createPalette(default_4bit_palette);
    103 
    104     rotation = 0;
    105     setViewport(0, 0, _dwidth, _dheight);
    106     setPivot(_iwidth/2, _iheight/2);
    107     return _img8_1;
    108   }
    109 
    110   return nullptr;
    111 }
    112 
    113 
    114 /***************************************************************************************
    115 ** Function name:           getPointer
    116 ** Description:             Returns pointer to start of sprite memory area
    117 ***************************************************************************************/
    118 void* TFT_eSprite::getPointer(void)
    119 {
    120   if (!_created) return nullptr;
    121   return _img8_1;
    122 }
    123 
    124 
    125 /***************************************************************************************
    126 ** Function name:           created
    127 ** Description:             Returns true if sprite has been created
    128 ***************************************************************************************/
    129 bool TFT_eSprite::created(void)
    130 {
    131   return _created;
    132 }
    133 
    134 
    135 /***************************************************************************************
    136 ** Function name:           ~TFT_eSprite
    137 ** Description:             Class destructor
    138 ***************************************************************************************/
    139 TFT_eSprite::~TFT_eSprite(void)
    140 {
    141   deleteSprite();
    142 
    143 #ifdef SMOOTH_FONT
    144   if(fontLoaded) unloadFont();
    145 #endif
    146 }
    147 
    148 
    149 /***************************************************************************************
    150 ** Function name:           callocSprite
    151 ** Description:             Allocate a memory area for the Sprite and return pointer
    152 ***************************************************************************************/
    153 void* TFT_eSprite::callocSprite(int16_t w, int16_t h, uint8_t frames)
    154 {
    155   // Add one extra "off screen" pixel to point out-of-bounds setWindow() coordinates
    156   // this means push/writeColor functions do not need additional bounds checks and
    157   // hence will run faster in normal circumstances.
    158   uint8_t* ptr8 = nullptr;
    159 
    160   if (frames > 2) frames = 2; // Currently restricted to 2 frame buffers
    161   if (frames < 1) frames = 1;
    162 
    163   if (_bpp == 16)
    164   {
    165 #if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT)
    166     if ( psramFound() && _psram_enable && !_tft->DMA_Enabled)
    167     {
    168       ptr8 = ( uint8_t*) ps_calloc(frames * w * h + frames, sizeof(uint16_t));
    169       //Serial.println("PSRAM");
    170     }
    171     else
    172 #endif
    173     {
    174       ptr8 = ( uint8_t*) calloc(frames * w * h + frames, sizeof(uint16_t));
    175       //Serial.println("Normal RAM");
    176     }
    177   }
    178 
    179   else if (_bpp == 8)
    180   {
    181 #if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT)
    182     if ( psramFound() && _psram_enable ) ptr8 = ( uint8_t*) ps_calloc(frames * w * h + frames, sizeof(uint8_t));
    183     else
    184 #endif
    185     ptr8 = ( uint8_t*) calloc(frames * w * h + frames, sizeof(uint8_t));
    186   }
    187 
    188   else if (_bpp == 4)
    189   {
    190     w = (w+1) & 0xFFFE; // width needs to be multiple of 2, with an extra "off screen" pixel
    191     _iwidth = w;
    192 #if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT)
    193     if ( psramFound() && _psram_enable ) ptr8 = ( uint8_t*) ps_calloc(((frames * w * h) >> 1) + frames, sizeof(uint8_t));
    194     else
    195 #endif
    196     ptr8 = ( uint8_t*) calloc(((frames * w * h) >> 1) + frames, sizeof(uint8_t));
    197   }
    198 
    199   else // Must be 1 bpp
    200   {
    201     //_dwidth   Display width+height in pixels always in rotation 0 orientation
    202     //_dheight  Not swapped for sprite rotations
    203     // Note: for 1bpp _iwidth and _iheight are swapped during Sprite rotations
    204 
    205     w =  (w+7) & 0xFFF8; // width should be the multiple of 8 bits to be compatible with epdpaint
    206     _iwidth = w;         // _iwidth is rounded up to be multiple of 8, so might not be = _dwidth
    207     _bitwidth = w;       // _bitwidth will not be rotated whereas _iwidth may be
    208 
    209 #if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT)
    210     if ( psramFound() && _psram_enable ) ptr8 = ( uint8_t*) ps_calloc(frames * (w>>3) * h + frames, sizeof(uint8_t));
    211     else
    212 #endif
    213     ptr8 = ( uint8_t*) calloc(frames * (w>>3) * h + frames, sizeof(uint8_t));
    214   }
    215 
    216   return ptr8;
    217 }
    218 
    219 
    220 /***************************************************************************************
    221 ** Function name:           createPalette (from RAM array)
    222 ** Description:             Set a palette for a 4-bit per pixel sprite
    223 ***************************************************************************************/
    224 void TFT_eSprite::createPalette(uint16_t colorMap[], uint8_t colors)
    225 {
    226   if (!_created) return;
    227 
    228   if (colorMap == nullptr)
    229   {
    230     // Create a color map using the default FLASH map
    231     createPalette(default_4bit_palette);
    232     return;
    233   }
    234 
    235   // Allocate and clear memory for 16 color map
    236   if (_colorMap == nullptr) _colorMap = (uint16_t *)calloc(16, sizeof(uint16_t));
    237 
    238   if (colors > 16) colors = 16;
    239 
    240   // Copy map colors
    241   for (uint8_t i = 0; i < colors; i++)
    242   {
    243     _colorMap[i] = colorMap[i];
    244   }
    245 }
    246 
    247 
    248 /***************************************************************************************
    249 ** Function name:           createPalette (from FLASH array)
    250 ** Description:             Set a palette for a 4-bit per pixel sprite
    251 ***************************************************************************************/
    252 void TFT_eSprite::createPalette(const uint16_t colorMap[], uint8_t colors)
    253 {
    254   if (!_created) return;
    255 
    256   if (colorMap == nullptr)
    257   {
    258     // Create a color map using the default FLASH map
    259     colorMap = default_4bit_palette;
    260   }
    261 
    262   // Allocate and clear memory for 16 color map
    263   if (_colorMap == nullptr) _colorMap = (uint16_t *)calloc(16, sizeof(uint16_t));
    264 
    265   if (colors > 16) colors = 16;
    266 
    267   // Copy map colors
    268   for (uint8_t i = 0; i < colors; i++)
    269   {
    270     _colorMap[i] = pgm_read_word(colorMap++);
    271   }
    272 }
    273 
    274 
    275 /***************************************************************************************
    276 ** Function name:           frameBuffer
    277 ** Description:             For 1 bpp Sprites, select the frame used for graphics
    278 ***************************************************************************************/
    279 // Frames are numbered 1 and 2
    280 void* TFT_eSprite::frameBuffer(int8_t f)
    281 {
    282   if (!_created) return nullptr;
    283 
    284   if ( f == 2 ) _img8 = _img8_2;
    285   else          _img8 = _img8_1;
    286 
    287   if (_bpp == 16) _img = (uint16_t*)_img8;
    288 
    289   //if (_bpp == 8) _img8 = _img8;
    290 
    291   if (_bpp == 4) _img4 = _img8;
    292 
    293   return _img8;
    294 }
    295 
    296 
    297 /***************************************************************************************
    298 ** Function name:           setColorDepth
    299 ** Description:             Set bits per pixel for colour (1, 8 or 16)
    300 ***************************************************************************************/
    301 void* TFT_eSprite::setColorDepth(int8_t b)
    302 {
    303   // Do not re-create the sprite if the colour depth does not change
    304   if (_bpp == b) return _img8_1;
    305 
    306   // Validate the new colour depth
    307   if ( b > 8 ) _bpp = 16;  // Bytes per pixel
    308   else if ( b > 4 ) _bpp = 8;
    309   else if ( b > 1 ) _bpp = 4;
    310   else _bpp = 1;
    311 
    312   // Can't change an existing sprite's colour depth so delete and create a new one
    313   if (_created) {
    314     deleteSprite();
    315     return createSprite(_dwidth, _dheight);
    316   }
    317 
    318   return nullptr;
    319 }
    320 
    321 
    322 /***************************************************************************************
    323 ** Function name:           getColorDepth
    324 ** Description:             Get bits per pixel for colour (1, 8 or 16)
    325 ***************************************************************************************/
    326 int8_t TFT_eSprite::getColorDepth(void)
    327 {
    328   if (_created) return _bpp;
    329   else return 0;
    330 }
    331 
    332 
    333 /***************************************************************************************
    334 ** Function name:           setBitmapColor
    335 ** Description:             Set the 1bpp foreground foreground and background colour
    336 ***************************************************************************************/
    337 void TFT_eSprite::setBitmapColor(uint16_t c, uint16_t b)
    338 {
    339   if (c == b) b = ~c;
    340   _tft->bitmap_fg = c;
    341   _tft->bitmap_bg = b;
    342 }
    343 
    344 
    345 /***************************************************************************************
    346 ** Function name:           setPaletteColor
    347 ** Description:             Set the 4bpp palette color at the given index
    348 ***************************************************************************************/
    349 void TFT_eSprite::setPaletteColor(uint8_t index, uint16_t color)
    350 {
    351   if (_colorMap == nullptr || index > 15) return; // out of bounds
    352 
    353   _colorMap[index] = color;
    354 }
    355 
    356 
    357 /***************************************************************************************
    358 ** Function name:           getPaletteColor
    359 ** Description:             Return the palette color at 4bpp index, or 0 on error.
    360 ***************************************************************************************/
    361 uint16_t TFT_eSprite::getPaletteColor(uint8_t index)
    362 {
    363   if (_colorMap == nullptr || index > 15) return 0; // out of bounds
    364 
    365   return _colorMap[index];
    366 }
    367 
    368 
    369 /***************************************************************************************
    370 ** Function name:           deleteSprite
    371 ** Description:             Delete the sprite to free up memory (RAM)
    372 ***************************************************************************************/
    373 void TFT_eSprite::deleteSprite(void)
    374 {
    375   if (_colorMap != nullptr)
    376   {
    377     free(_colorMap);
    378     _colorMap = nullptr;
    379   }
    380 
    381   if (_created)
    382   {
    383     free(_img8_1);
    384     _img8 = nullptr;
    385     _created = false;
    386     _vpOoB   = true;  // TFT_eSPI class write() uses this to check for valid sprite
    387   }
    388 }
    389 
    390 
    391 /***************************************************************************************
    392 ** Function name:           pushRotated - Fast fixed point integer maths version
    393 ** Description:             Push rotated Sprite to TFT screen
    394 ***************************************************************************************/
    395 #define FP_SCALE 10
    396 bool TFT_eSprite::pushRotated(int16_t angle, uint32_t transp)
    397 {
    398   if ( !_created || _tft->_vpOoB) return false;
    399 
    400   // Bounding box parameters
    401   int16_t min_x;
    402   int16_t min_y;
    403   int16_t max_x;
    404   int16_t max_y;
    405 
    406   // Get the bounding box of this rotated source Sprite relative to Sprite pivot
    407   if ( !getRotatedBounds(angle, &min_x, &min_y, &max_x, &max_y) ) return false;
    408 
    409   uint16_t sline_buffer[max_x - min_x + 1];
    410 
    411   int32_t xt = min_x - _tft->_xPivot;
    412   int32_t yt = min_y - _tft->_yPivot;
    413   uint32_t xe = _dwidth << FP_SCALE;
    414   uint32_t ye = _dheight << FP_SCALE;
    415   uint16_t tpcolor = (uint16_t)transp;
    416 
    417   if (transp != 0x00FFFFFF) {
    418     if (_bpp == 4) tpcolor = _colorMap[transp & 0x0F];
    419     tpcolor = tpcolor>>8 | tpcolor<<8; // Working with swapped color bytes
    420   }
    421   _tft->startWrite(); // Avoid transaction overhead for every tft pixel
    422 
    423   // Scan destination bounding box and fetch transformed pixels from source Sprite
    424   for (int32_t y = min_y; y <= max_y; y++, yt++) {
    425     int32_t x = min_x;
    426     uint32_t xs = (_cosra * xt - (_sinra * yt - (_xPivot << FP_SCALE)) + (1 << (FP_SCALE - 1)));
    427     uint32_t ys = (_sinra * xt + (_cosra * yt + (_yPivot << FP_SCALE)) + (1 << (FP_SCALE - 1)));
    428 
    429     while ((xs >= xe || ys >= ye) && x < max_x) { x++; xs += _cosra; ys += _sinra; }
    430     if (x == max_x) continue;
    431 
    432     uint32_t pixel_count = 0;
    433     do {
    434       uint32_t rp;
    435       int32_t xp = xs >> FP_SCALE;
    436       int32_t yp = ys >> FP_SCALE;
    437       if (_bpp == 16) {rp = _img[xp + yp * _iwidth]; }
    438       else { rp = readPixel(xp, yp); rp = (uint16_t)(rp>>8 | rp<<8); }
    439       if (transp != 0x00FFFFFF && tpcolor == rp) {
    440         if (pixel_count) {
    441           // TFT window is already clipped, so this is faster than pushImage()
    442           _tft->setWindow(x - pixel_count, y, x - 1, y);
    443           _tft->pushPixels(sline_buffer, pixel_count);
    444           pixel_count = 0;
    445         }
    446       }
    447       else {
    448         sline_buffer[pixel_count++] = rp;
    449       }
    450     } while (++x < max_x && (xs += _cosra) < xe && (ys += _sinra) < ye);
    451     if (pixel_count) {
    452       // TFT window is already clipped, so this is faster than pushImage()
    453       _tft->setWindow(x - pixel_count, y, x - 1, y);
    454       _tft->pushPixels(sline_buffer, pixel_count);
    455     }
    456   }
    457 
    458   _tft->endWrite(); // End transaction
    459 
    460   return true;
    461 }
    462 
    463 
    464 /***************************************************************************************
    465 ** Function name:           pushRotated - Fast fixed point integer maths version
    466 ** Description:             Push a rotated copy of the Sprite to another Sprite
    467 ***************************************************************************************/
    468 // Not compatible with 4bpp
    469 bool TFT_eSprite::pushRotated(TFT_eSprite *spr, int16_t angle, uint32_t transp)
    470 {
    471   if ( !_created  || _bpp == 4) return false; // Check this Sprite is created
    472   if ( !spr->_created  || spr->_bpp == 4) return false;  // Ckeck destination Sprite is created
    473 
    474   // Bounding box parameters
    475   int16_t min_x;
    476   int16_t min_y;
    477   int16_t max_x;
    478   int16_t max_y;
    479 
    480   // Get the bounding box of this rotated source Sprite
    481   if ( !getRotatedBounds(spr, angle, &min_x, &min_y, &max_x, &max_y) ) return false;
    482 
    483   uint16_t sline_buffer[max_x - min_x + 1];
    484 
    485   int32_t xt = min_x - spr->_xPivot;
    486   int32_t yt = min_y - spr->_yPivot;
    487   uint32_t xe = _dwidth << FP_SCALE;
    488   uint32_t ye = _dheight << FP_SCALE;
    489   uint16_t tpcolor = (uint16_t)transp;
    490   
    491   if (transp != 0x00FFFFFF) {
    492     if (_bpp == 4) tpcolor = _colorMap[transp & 0x0F];
    493     tpcolor = tpcolor>>8 | tpcolor<<8; // Working with swapped color bytes
    494   }
    495 
    496   bool oldSwapBytes = spr->getSwapBytes();
    497   spr->setSwapBytes(false);
    498 
    499   // Scan destination bounding box and fetch transformed pixels from source Sprite
    500   for (int32_t y = min_y; y <= max_y; y++, yt++) {
    501     int32_t x = min_x;
    502     uint32_t xs = (_cosra * xt - (_sinra * yt - (_xPivot << FP_SCALE)) + (1 << (FP_SCALE - 1)));
    503     uint32_t ys = (_sinra * xt + (_cosra * yt + (_yPivot << FP_SCALE)) + (1 << (FP_SCALE - 1)));
    504 
    505     while ((xs >= xe || ys >= ye) && x < max_x) { x++; xs += _cosra; ys += _sinra; }
    506     if (x == max_x) continue;
    507 
    508     uint32_t pixel_count = 0;
    509     do {
    510       uint32_t rp;
    511       int32_t xp = xs >> FP_SCALE;
    512       int32_t yp = ys >> FP_SCALE;
    513       if (_bpp == 16) rp = _img[xp + yp * _iwidth];
    514       else { rp = readPixel(xp, yp); rp = (uint16_t)(rp>>8 | rp<<8); }
    515       if (transp != 0x00FFFFFF && tpcolor == rp) {
    516         if (pixel_count) {
    517           spr->pushImage(x - pixel_count, y, pixel_count, 1, sline_buffer);
    518           pixel_count = 0;
    519         }
    520       }
    521       else {
    522         sline_buffer[pixel_count++] = rp;
    523       }
    524     } while (++x < max_x && (xs += _cosra) < xe && (ys += _sinra) < ye);
    525     if (pixel_count) spr->pushImage(x - pixel_count, y, pixel_count, 1, sline_buffer);
    526   }
    527   spr->setSwapBytes(oldSwapBytes);
    528   return true;
    529 }
    530 
    531 
    532 /***************************************************************************************
    533 ** Function name:           getRotatedBounds
    534 ** Description:             Get TFT bounding box of a rotated Sprite wrt pivot
    535 ***************************************************************************************/
    536 bool TFT_eSprite::getRotatedBounds(int16_t angle, int16_t *min_x, int16_t *min_y,
    537                                                   int16_t *max_x, int16_t *max_y)
    538 {
    539   // Get the bounding box of this rotated source Sprite relative to Sprite pivot
    540   getRotatedBounds(angle, width(), height(), _xPivot, _yPivot, min_x, min_y, max_x, max_y);
    541 
    542   // Move bounding box so source Sprite pivot coincides with TFT pivot
    543   *min_x += _tft->_xPivot;
    544   *max_x += _tft->_xPivot;
    545   *min_y += _tft->_yPivot;
    546   *max_y += _tft->_yPivot;
    547 
    548   // Return if bounding box is outside of TFT viewport
    549   if (*min_x > _tft->_vpW) return false;
    550   if (*min_y > _tft->_vpH) return false;
    551   if (*max_x < _tft->_vpX) return false;
    552   if (*max_y < _tft->_vpY) return false;
    553 
    554   // Clip bounding box to be within TFT viewport
    555   if (*min_x < _tft->_vpX) *min_x = _tft->_vpX;
    556   if (*min_y < _tft->_vpY) *min_y = _tft->_vpY;
    557   if (*max_x > _tft->_vpW) *max_x = _tft->_vpW;
    558   if (*max_y > _tft->_vpH) *max_y = _tft->_vpH;
    559 
    560   return true;
    561 }
    562 
    563 
    564 /***************************************************************************************
    565 ** Function name:           getRotatedBounds
    566 ** Description:             Get destination Sprite bounding box of a rotated Sprite wrt pivot
    567 ***************************************************************************************/
    568 bool TFT_eSprite::getRotatedBounds(TFT_eSprite *spr, int16_t angle, int16_t *min_x, int16_t *min_y,
    569                                                                     int16_t *max_x, int16_t *max_y)
    570 {
    571   // Get the bounding box of this rotated source Sprite relative to Sprite pivot
    572   getRotatedBounds(angle, width(), height(), _xPivot, _yPivot, min_x, min_y, max_x, max_y);
    573 
    574   // Move bounding box so source Sprite pivot coincides with destination Sprite pivot
    575   *min_x += spr->_xPivot;
    576   *max_x += spr->_xPivot;
    577   *min_y += spr->_yPivot;
    578   *max_y += spr->_yPivot;
    579 
    580   // Test only to show bounding box
    581   // spr->fillSprite(TFT_BLACK);
    582   // spr->drawRect(min_x, min_y, max_x - min_x + 1, max_y - min_y + 1, TFT_GREEN);
    583 
    584   // Return if bounding box is completely outside of destination Sprite
    585   if (*min_x > spr->width()) return true;
    586   if (*min_y > spr->height()) return true;
    587   if (*max_x < 0) return true;
    588   if (*max_y < 0) return true;
    589 
    590   // Clip bounding box to Sprite boundaries
    591   // Clipping to a viewport will be done by destination Sprite pushImage function
    592   if (*min_x < 0) min_x = 0;
    593   if (*min_y < 0) min_y = 0;
    594   if (*max_x > spr->width())  *max_x = spr->width();
    595   if (*max_y > spr->height()) *max_y = spr->height();
    596 
    597   return true;
    598 }
    599 
    600 
    601 /***************************************************************************************
    602 ** Function name:           rotatedBounds
    603 ** Description:             Get bounding box of a rotated Sprite wrt pivot
    604 ***************************************************************************************/
    605 void TFT_eSprite::getRotatedBounds(int16_t angle, int16_t w, int16_t h, int16_t xp, int16_t yp,
    606                                    int16_t *min_x, int16_t *min_y, int16_t *max_x, int16_t *max_y)
    607 {
    608   // Trig values for the rotation
    609   float radAngle = -angle * 0.0174532925; // Convert degrees to radians
    610   float sina = sin(radAngle);
    611   float cosa = cos(radAngle);
    612 
    613   w -= xp; // w is now right edge coordinate relative to xp
    614   h -= yp; // h is now bottom edge coordinate relative to yp
    615 
    616   // Calculate new corner coordinates
    617   int16_t x0 = -xp * cosa - yp * sina;
    618   int16_t y0 =  xp * sina - yp * cosa;
    619 
    620   int16_t x1 =  w * cosa - yp * sina;
    621   int16_t y1 = -w * sina - yp * cosa;
    622 
    623   int16_t x2 =  h * sina + w * cosa;
    624   int16_t y2 =  h * cosa - w * sina;
    625 
    626   int16_t x3 =  h * sina - xp * cosa;
    627   int16_t y3 =  h * cosa + xp * sina;
    628 
    629   // Find bounding box extremes, enlarge box to accomodate rounding errors
    630   *min_x = x0-2;
    631   if (x1 < *min_x) *min_x = x1-2;
    632   if (x2 < *min_x) *min_x = x2-2;
    633   if (x3 < *min_x) *min_x = x3-2;
    634 
    635   *max_x = x0+2;
    636   if (x1 > *max_x) *max_x = x1+2;
    637   if (x2 > *max_x) *max_x = x2+2;
    638   if (x3 > *max_x) *max_x = x3+2;
    639 
    640   *min_y = y0-2;
    641   if (y1 < *min_y) *min_y = y1-2;
    642   if (y2 < *min_y) *min_y = y2-2;
    643   if (y3 < *min_y) *min_y = y3-2;
    644 
    645   *max_y = y0+2;
    646   if (y1 > *max_y) *max_y = y1+2;
    647   if (y2 > *max_y) *max_y = y2+2;
    648   if (y3 > *max_y) *max_y = y3+2;
    649 
    650   _sinra = round(sina * (1<<FP_SCALE));
    651   _cosra = round(cosa * (1<<FP_SCALE));
    652 }
    653 
    654 
    655 /***************************************************************************************
    656 ** Function name:           pushSprite
    657 ** Description:             Push the sprite to the TFT at x, y
    658 ***************************************************************************************/
    659 void TFT_eSprite::pushSprite(int32_t x, int32_t y)
    660 {
    661   if (!_created) return;
    662 
    663   if (_bpp == 16)
    664   {
    665     bool oldSwapBytes = _tft->getSwapBytes();
    666     _tft->setSwapBytes(false);
    667     _tft->pushImage(x, y, _dwidth, _dheight, _img );
    668     _tft->setSwapBytes(oldSwapBytes);
    669   }
    670   else if (_bpp == 4)
    671   {
    672     _tft->pushImage(x, y, _dwidth, _dheight, _img4, false, _colorMap);
    673   }
    674   else _tft->pushImage(x, y, _dwidth, _dheight, _img8, (bool)(_bpp == 8));
    675 }
    676 
    677 
    678 /***************************************************************************************
    679 ** Function name:           pushSprite
    680 ** Description:             Push the sprite to the TFT at x, y with transparent colour
    681 ***************************************************************************************/
    682 void TFT_eSprite::pushSprite(int32_t x, int32_t y, uint16_t transp)
    683 {
    684   if (!_created) return;
    685 
    686   if (_bpp == 16)
    687   {
    688     bool oldSwapBytes = _tft->getSwapBytes();
    689     _tft->setSwapBytes(false);
    690     _tft->pushImage(x, y, _dwidth, _dheight, _img, transp );
    691     _tft->setSwapBytes(oldSwapBytes);
    692   }
    693   else if (_bpp == 8)
    694   {
    695     transp = (uint8_t)((transp & 0xE000)>>8 | (transp & 0x0700)>>6 | (transp & 0x0018)>>3);
    696     _tft->pushImage(x, y, _dwidth, _dheight, _img8, (uint8_t)transp, (bool)true);
    697   }
    698   else if (_bpp == 4)
    699   {
    700     _tft->pushImage(x, y, _dwidth, _dheight, _img4, (uint8_t)(transp & 0x0F), false, _colorMap);
    701   }
    702   else _tft->pushImage(x, y, _dwidth, _dheight, _img8, 0, (bool)false);
    703 }
    704 
    705 
    706 /***************************************************************************************
    707 ** Function name:           pushToSprite
    708 ** Description:             Push the sprite to another sprite at x, y
    709 ***************************************************************************************/
    710 // Note: The following sprite to sprite colour depths are currently supported:
    711 //    Source    Destination
    712 //    16bpp  -> 16bpp
    713 //    16bpp  ->  8bpp
    714 //     8bpp  ->  8bpp
    715 //     4bpp  ->  4bpp (note: color translation depends on the 2 sprites palette colors)
    716 //     1bpp  ->  1bpp (note: color translation depends on the 2 sprites bitmap colors)
    717 
    718 bool TFT_eSprite::pushToSprite(TFT_eSprite *dspr, int32_t x, int32_t y)
    719 {
    720   if (!_created) return false;
    721   if (!dspr->created()) return false;
    722 
    723   // Check destination sprite compatibility
    724   int8_t ds_bpp = dspr->getColorDepth();
    725   if (_bpp == 16 && ds_bpp != 16 && ds_bpp !=  8) return false;
    726   if (_bpp ==  8 && ds_bpp !=  8) return false;
    727   if (_bpp ==  4 && ds_bpp !=  4) return false;
    728   if (_bpp ==  1 && ds_bpp !=  1) return false;
    729 
    730   bool oldSwapBytes = dspr->getSwapBytes();
    731   dspr->setSwapBytes(false);
    732   dspr->pushImage(x, y, _dwidth, _dheight, _img, _bpp);
    733   dspr->setSwapBytes(oldSwapBytes);
    734 
    735   return true;
    736 }
    737 
    738 
    739 /***************************************************************************************
    740 ** Function name:           pushToSprite
    741 ** Description:             Push the sprite to another sprite at x, y with transparent colour
    742 ***************************************************************************************/
    743 // Note: The following sprite to sprite colour depths are currently supported:
    744 //    Source    Destination
    745 //    16bpp  -> 16bpp
    746 //    16bpp  ->  8bpp
    747 //     8bpp  ->  8bpp
    748 //     1bpp  ->  1bpp
    749 
    750 bool TFT_eSprite::pushToSprite(TFT_eSprite *dspr, int32_t x, int32_t y, uint16_t transp)
    751 {
    752   if ( !_created  || !dspr->_created) return false; // Check Sprites exist
    753 
    754   // Check destination sprite compatibility
    755   int8_t ds_bpp = dspr->getColorDepth();
    756   if (_bpp == 16 && ds_bpp != 16 && ds_bpp !=  8) return false;
    757   if (_bpp ==  8 && ds_bpp !=  8) return false;
    758   if (_bpp ==  4 || ds_bpp ==  4) return false;
    759   if (_bpp ==  1 && ds_bpp !=  1) return false;
    760 
    761   bool oldSwapBytes = dspr->getSwapBytes();
    762   uint16_t sline_buffer[width()];
    763 
    764   transp = transp>>8 | transp<<8;
    765 
    766   // Scan destination bounding box and fetch transformed pixels from source Sprite
    767   for (int32_t ys = 0; ys < height(); ys++) {
    768     int32_t ox = x;
    769     uint32_t pixel_count = 0;
    770 
    771     for (int32_t xs = 0; xs < width(); xs++) {
    772       uint16_t rp = 0;
    773       if (_bpp == 16) rp = _img[xs + ys * width()];
    774       else { rp = readPixel(xs, ys); rp = rp>>8 | rp<<8; }
    775       //dspr->drawPixel(xs, ys, rp);
    776 
    777       if (transp == rp) {
    778         if (pixel_count) {
    779           dspr->pushImage(ox, y, pixel_count, 1, sline_buffer, _bpp);
    780           ox += pixel_count;
    781           pixel_count = 0;
    782         }
    783         ox++;
    784       }
    785       else {
    786         sline_buffer[pixel_count++] = rp;
    787       }
    788     }
    789     if (pixel_count) dspr->pushImage(ox, y, pixel_count, 1, sline_buffer);
    790     y++;
    791   }
    792   dspr->setSwapBytes(oldSwapBytes);
    793   return true;
    794 }
    795 
    796 
    797 /***************************************************************************************
    798 ** Function name:           pushSprite
    799 ** Description:             Push a cropped sprite to the TFT at tx, ty
    800 ***************************************************************************************/
    801 bool TFT_eSprite::pushSprite(int32_t tx, int32_t ty, int32_t sx, int32_t sy, int32_t sw, int32_t sh)
    802 {
    803   if (!_created) return false;
    804 
    805   // Perform window boundary checks and crop if needed
    806   setWindow(sx, sy, sx + sw - 1, sy + sh - 1);
    807 
    808   /* These global variables are now populated for the sprite
    809   _xs = x start coordinate
    810   _ys = y start coordinate
    811   _xe = x end coordinate (inclusive)
    812   _ye = y end coordinate (inclusive)
    813   */
    814 
    815   // Calculate new sprite window bounding box width and height
    816   sw = _xe - _xs + 1;
    817   sh = _ye - _ys + 1;
    818 
    819   if (_ys >= _iheight) return false;
    820 
    821   if (_bpp == 16)
    822   {
    823     bool oldSwapBytes = _tft->getSwapBytes();
    824     _tft->setSwapBytes(false);
    825 
    826     // Check if a faster block copy to screen is possible
    827     if ( sx == 0 && sw == _dwidth)
    828       _tft->pushImage(tx, ty, sw, sh, _img + _iwidth * _ys );
    829     else // Render line by line
    830       while (sh--)
    831         _tft->pushImage(tx, ty++, sw, 1, _img + _xs + _iwidth * _ys++ );
    832 
    833     _tft->setSwapBytes(oldSwapBytes);
    834   }
    835   else if (_bpp == 8)
    836   {
    837     // Check if a faster block copy to screen is possible
    838     if ( sx == 0 && sw == _dwidth)
    839       _tft->pushImage(tx, ty, sw, sh, _img8 + _iwidth * _ys, (bool)true );
    840     else // Render line by line
    841     while (sh--)
    842       _tft->pushImage(tx, ty++, sw, 1, _img8 + _xs + _iwidth * _ys++, (bool)true );
    843   }
    844   else if (_bpp == 4)
    845   {
    846     // Check if a faster block copy to screen is possible
    847     if ( sx == 0 && sw == _dwidth)
    848       _tft->pushImage(tx, ty, sw, sh, _img4 + (_iwidth>>1) * _ys, false, _colorMap );
    849     else // Render line by line
    850     {
    851       int32_t ds = _xs&1; // Odd x start pixel
    852 
    853       int32_t de = 0;     // Odd x end pixel
    854       if ((sw > ds) && (_xe&1)) de = 1;
    855 
    856       uint32_t dm = 0;     // Midsection pixel count
    857       if (sw > (ds+de)) dm = sw - ds - de;
    858       sw--;
    859 
    860       uint32_t yp = (_xs + ds + _iwidth * _ys)>>1;
    861       _tft->startWrite();
    862       while (sh--)
    863       {
    864         if (ds) _tft->drawPixel(tx, ty, readPixel(_xs, _ys) );
    865         if (dm) _tft->pushImage(tx + ds, ty, dm, 1, _img4 + yp, false, _colorMap );
    866         if (de) _tft->drawPixel(tx + sw, ty, readPixel(_xe, _ys) );
    867         _ys++;
    868         ty++;
    869         yp += (_iwidth>>1);
    870       }
    871       _tft->endWrite();
    872     }
    873   }
    874   else // 1bpp
    875   {
    876     // Check if a faster block copy to screen is possible
    877     if ( sx == 0 && sw == _dwidth)
    878       _tft->pushImage(tx, ty, sw, sh, _img8 + (_bitwidth>>3) * _ys, (bool)false );
    879     else // Render line by line
    880     {
    881       _tft->startWrite();
    882       while (sh--)
    883       {
    884         _tft->pushImage(tx, ty++, sw, 1, _img8 + (_bitwidth>>3) * _ys++, (bool)false );
    885       }
    886       _tft->endWrite();
    887     }
    888   }
    889 
    890   return true;
    891 }
    892 
    893 
    894 /***************************************************************************************
    895 ** Function name:           readPixelValue
    896 ** Description:             Read the color map index of a pixel at defined coordinates
    897 ***************************************************************************************/
    898 uint16_t TFT_eSprite::readPixelValue(int32_t x, int32_t y)
    899 {
    900   if (_vpOoB  || !_created) return 0xFF;
    901 
    902   x+= _xDatum;
    903   y+= _yDatum;
    904 
    905   // Range checking
    906   if ((x < _vpX) || (y < _vpY) ||(x >= _vpW) || (y >= _vpH)) return 0xFF;
    907 
    908   if (_bpp == 16)
    909   {
    910     // Return the pixel colour
    911     return readPixel(x - _xDatum, y - _yDatum);
    912   }
    913 
    914   if (_bpp == 8)
    915   {
    916     // Return the pixel byte value
    917     return _img8[x + y * _iwidth];
    918   }
    919 
    920   if (_bpp == 4)
    921   {
    922     if (x >= _dwidth) return 0xFF;
    923     if ((x & 0x01) == 0)
    924       return _img4[((x+y*_iwidth)>>1)] >> 4;   // even index = bits 7 .. 4
    925     else
    926       return _img4[((x+y*_iwidth)>>1)] & 0x0F; // odd index = bits 3 .. 0.
    927   }
    928 
    929   if (_bpp == 1)
    930   {
    931     // Note: _dwidth and _dheight bounds not checked (rounded up -iwidth and _iheight used)
    932     if (rotation == 1)
    933     {
    934       uint16_t tx = x;
    935       x = _dheight - y - 1;
    936       y = tx;
    937     }
    938     else if (rotation == 2)
    939     {
    940       x = _dwidth - x - 1;
    941       y = _dheight - y - 1;
    942     }
    943     else if (rotation == 3)
    944     {
    945       uint16_t tx = x;
    946       x = y;
    947       y = _dwidth - tx - 1;
    948     }
    949     // Return 1 or 0
    950     return (_img8[(x + y * _bitwidth)>>3] >> (7-(x & 0x7))) & 0x01;
    951   }
    952 
    953   return 0;
    954 }
    955 
    956 /***************************************************************************************
    957 ** Function name:           readPixel
    958 ** Description:             Read 565 colour of a pixel at defined coordinates
    959 ***************************************************************************************/
    960 uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y)
    961 {
    962   if (_vpOoB  || !_created) return 0xFFFF;
    963 
    964   x+= _xDatum;
    965   y+= _yDatum;
    966 
    967   // Range checking
    968   if ((x < _vpX) || (y < _vpY) ||(x >= _vpW) || (y >= _vpH)) return 0xFFFF;
    969 
    970   if (_bpp == 16)
    971   {
    972     uint16_t color = _img[x + y * _iwidth];
    973     return (color >> 8) | (color << 8);
    974   }
    975 
    976   if (_bpp == 8)
    977   {
    978     uint16_t color = _img8[x + y * _iwidth];
    979     if (color != 0)
    980     {
    981     uint8_t  blue[] = {0, 11, 21, 31};
    982       color =   (color & 0xE0)<<8 | (color & 0xC0)<<5
    983               | (color & 0x1C)<<6 | (color & 0x1C)<<3
    984               | blue[color & 0x03];
    985     }
    986     return color;
    987   }
    988 
    989   if (_bpp == 4)
    990   {
    991     if (x >= _dwidth) return 0xFFFF;
    992     uint16_t color;
    993     if ((x & 0x01) == 0)
    994       color = _colorMap[_img4[((x+y*_iwidth)>>1)] >> 4];   // even index = bits 7 .. 4
    995     else
    996       color = _colorMap[_img4[((x+y*_iwidth)>>1)] & 0x0F]; // odd index = bits 3 .. 0.
    997     return color;
    998   }
    999 
   1000   // Note: Must be 1bpp
   1001   // _dwidth and _dheight bounds not checked (rounded up -iwidth and _iheight used)
   1002   if (rotation == 1)
   1003   {
   1004     uint16_t tx = x;
   1005     x = _dheight - y - 1;
   1006     y = tx;
   1007   }
   1008   else if (rotation == 2)
   1009   {
   1010     x = _dwidth - x - 1;
   1011     y = _dheight - y - 1;
   1012   }
   1013   else if (rotation == 3)
   1014   {
   1015     uint16_t tx = x;
   1016     x = y;
   1017     y = _dwidth - tx - 1;
   1018   }
   1019 
   1020   uint16_t color = (_img8[(x + y * _bitwidth)>>3] << (x & 0x7)) & 0x80;
   1021 
   1022   if (color) return _tft->bitmap_fg;
   1023   else       return _tft->bitmap_bg;
   1024 }
   1025 
   1026 
   1027 /***************************************************************************************
   1028 ** Function name:           pushImage
   1029 ** Description:             push image into a defined area of a sprite
   1030 ***************************************************************************************/
   1031 void  TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data, uint8_t sbpp)
   1032 {
   1033   if (data == nullptr || !_created) return;
   1034 
   1035   PI_CLIP;
   1036 
   1037   if (_bpp == 16) // Plot a 16 bpp image into a 16 bpp Sprite
   1038   {
   1039     // Pointer within original image
   1040     uint8_t *ptro = (uint8_t *)data + ((dx + dy * w) << 1);
   1041     // Pointer within sprite image
   1042     uint8_t *ptrs = (uint8_t *)_img + ((x + y * _iwidth) << 1);
   1043 
   1044     if(_swapBytes)
   1045     {
   1046       while (dh--)
   1047       {
   1048         // Fast copy with a 1 byte shift
   1049         memcpy(ptrs+1, ptro, (dw<<1) - 1);
   1050         // Now correct just the even numbered bytes
   1051         for (int32_t xp = 0; xp < (dw<<1); xp+=2)
   1052         {
   1053           ptrs[xp] = ptro[xp+1];;
   1054         }
   1055         ptro += w<<1;
   1056         ptrs += _iwidth<<1;
   1057       }
   1058     }
   1059     else
   1060     {
   1061       while (dh--)
   1062       {
   1063         memcpy(ptrs, ptro, dw<<1);
   1064         ptro += w << 1;
   1065         ptrs += _iwidth << 1;
   1066       }
   1067     }
   1068   }
   1069   else if (_bpp == 8 && sbpp == 8) // Plot a 8 bpp image into a 8 bpp Sprite
   1070   {
   1071     // Pointer within original image
   1072     uint8_t *ptro = (uint8_t *)data + (dx + dy * w);
   1073     // Pointer within sprite image
   1074     uint8_t *ptrs = (uint8_t *)_img + (x + y * _iwidth);
   1075 
   1076     while (dh--)
   1077     {
   1078       memcpy(ptrs, ptro, dw);
   1079       ptro += w;
   1080       ptrs += _iwidth;
   1081     }
   1082   }
   1083   else if (_bpp == 8) // Plot a 16 bpp image into a 8 bpp Sprite
   1084   {
   1085     uint16_t lastColor = 0;
   1086     uint8_t  color8    = 0;
   1087     for (int32_t yp = dy; yp < dy + dh; yp++)
   1088     {
   1089       int32_t xyw = x + y * _iwidth;
   1090       int32_t dxypw = dx + yp * w;
   1091       for (int32_t xp = dx; xp < dx + dw; xp++)
   1092       {
   1093         uint16_t color = data[dxypw++];
   1094         if (color != lastColor) {
   1095           // When data source is a sprite, the bytes are already swapped
   1096           if(!_swapBytes) color8 = (uint8_t)((color & 0xE0) | (color & 0x07)<<2 | (color & 0x1800)>>11);
   1097           else color8 = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3);
   1098         }
   1099         lastColor = color;
   1100         _img8[xyw++] = color8;
   1101       }
   1102       y++;
   1103     }
   1104   }
   1105   else if (_bpp == 4)
   1106   {
   1107     // The image is assumed to be 4 bit, where each byte corresponds to two pixels.
   1108     // much faster when aligned to a byte boundary, because the alternative is slower, requiring
   1109     // tedious bit operations.
   1110 
   1111     int sWidth = (_iwidth >> 1);
   1112     uint8_t *ptr = (uint8_t *)data;
   1113 
   1114     if ((x & 0x01) == 0 && (dx & 0x01) == 0 && (dw & 0x01) == 0)
   1115     {
   1116       x = (x >> 1) + y * sWidth;
   1117       dw = (dw >> 1);
   1118       dx = (dx >> 1) + dy * (w>>1);
   1119       while (dh--)
   1120       {
   1121         memcpy(_img4 + x, ptr + dx, dw);
   1122         dx += (w >> 1);
   1123         x += sWidth;
   1124       }
   1125     }
   1126     else  // not optimized
   1127     {
   1128       for (int32_t yp = dy; yp < dy + dh; yp++)
   1129       {
   1130         int32_t ox = x;
   1131         for (int32_t xp = dx; xp < dx + dw; xp++)
   1132         {
   1133           uint32_t color;
   1134           if ((xp & 0x01) == 0)
   1135             color = (ptr[((xp+yp*w)>>1)] & 0xF0) >> 4; // even index = bits 7 .. 4
   1136           else
   1137             color = ptr[((xp-1+yp*w)>>1)] & 0x0F;      // odd index = bits 3 .. 0.
   1138           drawPixel(ox, y, color);
   1139           ox++;
   1140         }
   1141         y++;
   1142       }
   1143     }
   1144   }
   1145 
   1146   else // 1bpp
   1147   {
   1148     // Plot a 1bpp image into a 1bpp Sprite
   1149     uint32_t ww =  (w+7)>>3; // Width of source image line in bytes
   1150     uint8_t *ptr = (uint8_t *)data;
   1151     for (int32_t yp = dy;  yp < dy + dh; yp++)
   1152     {
   1153       uint32_t yw = yp * ww;              // Byte starting the line containing source pixel
   1154       int32_t ox = x;
   1155       for (int32_t xp = dx; xp < dx + dw; xp++)
   1156       {
   1157         uint16_t readPixel = (ptr[(xp>>3) + yw] & (0x80 >> (xp & 0x7)) );
   1158         drawPixel(ox++, y, readPixel);
   1159       }
   1160       y++;
   1161     }
   1162   }
   1163 }
   1164 
   1165 
   1166 /***************************************************************************************
   1167 ** Function name:           pushImage
   1168 ** Description:             push 565 colour FLASH (PROGMEM) image into a defined area
   1169 ***************************************************************************************/
   1170 void  TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data)
   1171 {
   1172 #ifdef ESP32
   1173   pushImage(x, y, w, h, (uint16_t*) data);
   1174 #else
   1175   // Partitioned memory FLASH processor
   1176   if (data == nullptr || !_created) return;
   1177 
   1178   PI_CLIP;
   1179 
   1180   if (_bpp == 16) // Plot a 16 bpp image into a 16 bpp Sprite
   1181   {
   1182     for (int32_t yp = dy; yp < dy + dh; yp++)
   1183     {
   1184       int32_t ox = x;
   1185       for (int32_t xp = dx; xp < dx + dw; xp++)
   1186       {
   1187         uint16_t color = pgm_read_word(data + xp + yp * w);
   1188         if(_swapBytes) color = color<<8 | color>>8;
   1189         _img[ox + y * _iwidth] = color;
   1190         ox++;
   1191       }
   1192       y++;
   1193     }
   1194   }
   1195 
   1196   else if (_bpp == 8) // Plot a 16 bpp image into a 8 bpp Sprite
   1197   {
   1198     for (int32_t yp = dy; yp < dy + dh; yp++)
   1199     {
   1200       int32_t ox = x;
   1201       for (int32_t xp = dx; xp < dx + dw; xp++)
   1202       {
   1203         uint16_t color = pgm_read_word(data + xp + yp * w);
   1204         if(_swapBytes) color = color<<8 | color>>8;
   1205         _img8[ox + y * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3);
   1206         ox++;
   1207       }
   1208       y++;
   1209     }
   1210   }
   1211 
   1212   else if (_bpp == 4)
   1213   {
   1214     #ifdef TFT_eSPI_DEBUG
   1215     Serial.println("TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data) not implemented");
   1216     #endif
   1217     return;
   1218   }
   1219 
   1220   else // Plot a 1bpp image into a 1bpp Sprite
   1221   {
   1222     x-= _xDatum;   // Remove offsets, drawPixel will add
   1223     y-= _yDatum;
   1224     uint16_t bsw =  (w+7) >> 3; // Width in bytes of source image line
   1225     uint8_t *ptr = ((uint8_t*)data) + dy * bsw;
   1226     
   1227     while (dh--) {
   1228       int32_t odx = dx;
   1229       int32_t ox  = x;
   1230       while (odx < dx + dw) {
   1231         uint8_t pbyte = pgm_read_byte(ptr + (odx>>3));
   1232         uint8_t mask = 0x80 >> (odx & 7);
   1233         while (mask) {
   1234           uint8_t p = pbyte & mask;
   1235           mask = mask >> 1;
   1236           drawPixel(ox++, y, p);
   1237           odx++;
   1238         }
   1239       }
   1240       ptr += bsw;
   1241       y++;
   1242     }
   1243   }
   1244 #endif // if ESP32 check
   1245 }
   1246 
   1247 
   1248 /***************************************************************************************
   1249 ** Function name:           setWindow
   1250 ** Description:             Set the bounds of a window in the sprite
   1251 ***************************************************************************************/
   1252 // Intentionally not constrained to viewport area, does not manage 1bpp rotations
   1253 void TFT_eSprite::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1)
   1254 {
   1255   if (x0 > x1) transpose(x0, x1);
   1256   if (y0 > y1) transpose(y0, y1);
   1257   
   1258   int32_t w = width();
   1259   int32_t h = height();
   1260 
   1261   if ((x0 >= w) || (x1 < 0) || (y0 >= h) || (y1 < 0))
   1262   { // Point to that extra "off screen" pixel
   1263     _xs = 0;
   1264     _ys = _dheight;
   1265     _xe = 0;
   1266     _ye = _dheight;
   1267   }
   1268   else
   1269   {
   1270     if (x0 < 0) x0 = 0;
   1271     if (x1 >= w) x1 = w - 1;
   1272     if (y0 < 0) y0 = 0;
   1273     if (y1 >= h) y1 = h - 1;
   1274 
   1275     _xs = x0;
   1276     _ys = y0;
   1277     _xe = x1;
   1278     _ye = y1;
   1279   }
   1280 
   1281   _xptr = _xs;
   1282   _yptr = _ys;
   1283 }
   1284 
   1285 
   1286 /***************************************************************************************
   1287 ** Function name:           pushColor
   1288 ** Description:             Send a new pixel to the set window
   1289 ***************************************************************************************/
   1290 void TFT_eSprite::pushColor(uint16_t color)
   1291 {
   1292   if (!_created ) return;
   1293 
   1294   // Write the colour to RAM in set window
   1295   if (_bpp == 16)
   1296     _img [_xptr + _yptr * _iwidth] = (uint16_t) (color >> 8) | (color << 8);
   1297 
   1298   else  if (_bpp == 8)
   1299     _img8[_xptr + _yptr * _iwidth] = (uint8_t )((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3);
   1300 
   1301   else if (_bpp == 4)
   1302   {
   1303     uint8_t c = (uint8_t)color & 0x0F;
   1304     if ((_xptr & 0x01) == 0) {
   1305       _img4[(_xptr + _yptr * _iwidth)>>1] = (c << 4) | (_img4[(_xptr + _yptr * _iwidth)>>1] & 0x0F);  // new color is in bits 7 .. 4
   1306     }
   1307     else {
   1308       _img4[(_xptr + _yptr * _iwidth)>>1] = (_img4[(_xptr + _yptr * _iwidth)>>1] & 0xF0) | c; // new color is the low bits
   1309     }
   1310   }
   1311 
   1312   else drawPixel(_xptr, _yptr, color);
   1313 
   1314   // Increment x
   1315   _xptr++;
   1316 
   1317   // Wrap on x and y to start, increment y if needed
   1318   if (_xptr > _xe)
   1319   {
   1320     _xptr = _xs;
   1321     _yptr++;
   1322     if (_yptr > _ye) _yptr = _ys;
   1323   }
   1324 
   1325 }
   1326 
   1327 
   1328 /***************************************************************************************
   1329 ** Function name:           pushColor
   1330 ** Description:             Send a "len" new pixels to the set window
   1331 ***************************************************************************************/
   1332 void TFT_eSprite::pushColor(uint16_t color, uint32_t len)
   1333 {
   1334   if (!_created ) return;
   1335 
   1336   uint16_t pixelColor;
   1337 
   1338   if (_bpp == 16)
   1339     pixelColor = (uint16_t) (color >> 8) | (color << 8);
   1340 
   1341   else  if (_bpp == 8)
   1342     pixelColor = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3;
   1343 
   1344   else pixelColor = (uint16_t) color; // for 1bpp or 4bpp
   1345 
   1346   while(len--) writeColor(pixelColor);
   1347 }
   1348 
   1349 
   1350 /***************************************************************************************
   1351 ** Function name:           writeColor
   1352 ** Description:             Write a pixel with pre-formatted colour to the set window
   1353 ***************************************************************************************/
   1354 void TFT_eSprite::writeColor(uint16_t color)
   1355 {
   1356   if (!_created ) return;
   1357 
   1358   // Write 16 bit RGB 565 encoded colour to RAM
   1359   if (_bpp == 16) _img [_xptr + _yptr * _iwidth] = color;
   1360 
   1361   // Write 8 bit RGB 332 encoded colour to RAM
   1362   else if (_bpp == 8) _img8[_xptr + _yptr * _iwidth] = (uint8_t) color;
   1363 
   1364   else if (_bpp == 4)
   1365   {
   1366     uint8_t c = (uint8_t)color & 0x0F;
   1367     if ((_xptr & 0x01) == 0)
   1368       _img4[(_xptr + _yptr * _iwidth)>>1] = (c << 4) | (_img4[(_xptr + _yptr * _iwidth)>>1] & 0x0F);  // new color is in bits 7 .. 4
   1369     else
   1370       _img4[(_xptr + _yptr * _iwidth)>>1] = (_img4[(_xptr + _yptr * _iwidth)>>1] & 0xF0) | c; // new color is the low bits (x is odd)
   1371   }
   1372 
   1373   else drawPixel(_xptr, _yptr, color);
   1374 
   1375   // Increment x
   1376   _xptr++;
   1377 
   1378   // Wrap on x and y to start, increment y if needed
   1379   if (_xptr > _xe)
   1380   {
   1381     _xptr = _xs;
   1382     _yptr++;
   1383     if (_yptr > _ye) _yptr = _ys;
   1384   }
   1385 }
   1386 
   1387 
   1388 /***************************************************************************************
   1389 ** Function name:           setScrollRect
   1390 ** Description:             Set scroll area within the sprite and the gap fill colour
   1391 ***************************************************************************************/
   1392 // Intentionally not constrained to viewport area
   1393 void TFT_eSprite::setScrollRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t color)
   1394 {
   1395   if ((x >= _iwidth) || (y >= _iheight) || !_created ) return;
   1396 
   1397   if (x < 0) { w += x; x = 0; }
   1398   if (y < 0) { h += y; y = 0; }
   1399 
   1400   if ((x + w) > _iwidth ) w = _iwidth  - x;
   1401   if ((y + h) > _iheight) h = _iheight - y;
   1402 
   1403   if ( w < 1 || h < 1) return;
   1404 
   1405   _sx = x;
   1406   _sy = y;
   1407   _sw = w;
   1408   _sh = h;
   1409 
   1410   _scolor = color;
   1411 }
   1412 
   1413 
   1414 /***************************************************************************************
   1415 ** Function name:           scroll
   1416 ** Description:             Scroll dx,dy pixels, positive right,down, negative left,up
   1417 ***************************************************************************************/
   1418 void TFT_eSprite::scroll(int16_t dx, int16_t dy)
   1419 {
   1420   if (abs(dx) >= _sw || abs(dy) >= _sh)
   1421   {
   1422     fillRect (_sx, _sy, _sw, _sh, _scolor);
   1423     return;
   1424   }
   1425 
   1426   // Fetch the scroll area width and height set by setScrollRect()
   1427   uint32_t w  = _sw - abs(dx); // line width to copy
   1428   uint32_t h  = _sh - abs(dy); // lines to copy
   1429   int32_t iw  = _iwidth;       // rounded up width of sprite
   1430 
   1431   // Fetch the x,y origin set by setScrollRect()
   1432   uint32_t tx = _sx; // to x
   1433   uint32_t fx = _sx; // from x
   1434   uint32_t ty = _sy; // to y
   1435   uint32_t fy = _sy; // from y
   1436 
   1437   // Adjust for x delta
   1438   if (dx <= 0) fx -= dx;
   1439   else tx += dx;
   1440 
   1441   // Adjust for y delta
   1442   if (dy <= 0) fy -= dy;
   1443   else
   1444   { // Scrolling down so start copy from bottom
   1445     ty = ty + _sh - 1; // "To" pointer
   1446     iw = -iw;          // Pointer moves backwards
   1447     fy = ty - dy;      // "From" pointer
   1448   }
   1449 
   1450   // Calculate "from y" and "to y" pointers in RAM
   1451   uint32_t fyp = fx + fy * _iwidth;
   1452   uint32_t typ = tx + ty * _iwidth;
   1453 
   1454   // Now move the pixels in RAM
   1455   if (_bpp == 16)
   1456   {
   1457     while (h--)
   1458     { // move pixel lines (to, from, byte count)
   1459       memmove( _img + typ, _img + fyp, w<<1);
   1460       typ += iw;
   1461       fyp += iw;
   1462     }
   1463   }
   1464   else if (_bpp == 8)
   1465   {
   1466     while (h--)
   1467     { // move pixel lines (to, from, byte count)
   1468       memmove( _img8 + typ, _img8 + fyp, w);
   1469       typ += iw;
   1470       fyp += iw;
   1471     }
   1472   }
   1473   else if (_bpp == 4)
   1474   {
   1475     // could optimize for scrolling by even # pixels using memove (later)
   1476     if (dx >  0) { tx += w; fx += w; } // Start from right edge
   1477     while (h--)
   1478     { // move pixels one by one
   1479       for (uint16_t xp = 0; xp < w; xp++)
   1480       {
   1481         if (dx <= 0) drawPixel(tx + xp, ty, readPixelValue(fx + xp, fy));
   1482         if (dx >  0) drawPixel(tx - xp, ty, readPixelValue(fx - xp, fy));
   1483       }
   1484       if (dy <= 0)  { ty++; fy++; }
   1485       else  { ty--; fy--; }
   1486     }
   1487   }
   1488   else if (_bpp == 1 )
   1489   {
   1490     if (dx >  0) { tx += w; fx += w; } // Start from right edge
   1491     while (h--)
   1492     { // move pixels one by one
   1493       for (uint16_t xp = 0; xp < w; xp++)
   1494       {
   1495         if (dx <= 0) drawPixel(tx + xp, ty, readPixelValue(fx + xp, fy));
   1496         if (dx >  0) drawPixel(tx - xp, ty, readPixelValue(fx - xp, fy));
   1497       }
   1498       if (dy <= 0)  { ty++; fy++; }
   1499       else  { ty--; fy--; }
   1500     }
   1501   }
   1502   else return; // Not 1, 4, 8 or 16 bpp
   1503 
   1504   // Fill the gap left by the scrolling
   1505   if (dx > 0) fillRect(_sx, _sy, dx, _sh, _scolor);
   1506   if (dx < 0) fillRect(_sx + _sw + dx, _sy, -dx, _sh, _scolor);
   1507   if (dy > 0) fillRect(_sx, _sy, _sw, dy, _scolor);
   1508   if (dy < 0) fillRect(_sx, _sy + _sh + dy, _sw, -dy, _scolor);
   1509 }
   1510 
   1511 
   1512 /***************************************************************************************
   1513 ** Function name:           fillSprite
   1514 ** Description:             Fill the whole sprite with defined colour
   1515 ***************************************************************************************/
   1516 void TFT_eSprite::fillSprite(uint32_t color)
   1517 {
   1518   if (!_created || _vpOoB) return;
   1519 
   1520   // Use memset if possible as it is super fast
   1521   if(_xDatum == 0 && _yDatum == 0  &&  _xWidth == width())
   1522   {
   1523     if(_bpp == 16) {
   1524       if ( (uint8_t)color == (uint8_t)(color>>8) ) {
   1525         memset(_img,  (uint8_t)color, _iwidth * _yHeight * 2);
   1526       }
   1527       else fillRect(_vpX, _vpY, _xWidth, _yHeight, color);
   1528     }
   1529     else if (_bpp == 8)
   1530     {
   1531       color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3;
   1532       memset(_img8, (uint8_t)color, _iwidth * _yHeight);
   1533     }
   1534     else if (_bpp == 4)
   1535     {
   1536       uint8_t c = ((color & 0x0F) | (((color & 0x0F) << 4) & 0xF0));
   1537       memset(_img4, c, (_iwidth * _yHeight) >> 1);
   1538     }
   1539     else if (_bpp == 1)
   1540     {
   1541       if(color) memset(_img8, 0xFF, (_bitwidth>>3) * _dheight + 1);
   1542       else      memset(_img8, 0x00, (_bitwidth>>3) * _dheight + 1);
   1543     }
   1544   }
   1545   else fillRect(_vpX - _xDatum, _vpY - _yDatum, _xWidth, _yHeight, color);
   1546 }
   1547 
   1548 
   1549 /***************************************************************************************
   1550 ** Function name:           width
   1551 ** Description:             Return the width of sprite
   1552 ***************************************************************************************/
   1553 // Return the size of the sprite
   1554 int16_t TFT_eSprite::width(void)
   1555 {
   1556   if (!_created ) return 0;
   1557 
   1558   if (_bpp > 1) {
   1559     if (_vpDatum) return _xWidth;
   1560     return _dwidth;
   1561   }
   1562 
   1563   if (rotation & 1) {
   1564     if (_vpDatum) return _xWidth;
   1565     return _dheight;
   1566   }
   1567 
   1568   if (_vpDatum) return _xWidth;
   1569   return _dwidth;
   1570 }
   1571 
   1572 
   1573 /***************************************************************************************
   1574 ** Function name:           height
   1575 ** Description:             Return the height of sprite
   1576 ***************************************************************************************/
   1577 int16_t TFT_eSprite::height(void)
   1578 {
   1579   if (!_created ) return 0;
   1580 
   1581   if (_bpp > 1) {
   1582     if (_vpDatum) return _yHeight;
   1583     return _dheight;
   1584   }
   1585 
   1586   if (rotation & 1) {
   1587     if (_vpDatum) return _yHeight;
   1588     return _dwidth;
   1589   }
   1590 
   1591   if (_vpDatum) return _yHeight;
   1592   return _dheight;
   1593 }
   1594 
   1595 
   1596 /***************************************************************************************
   1597 ** Function name:           setRotation
   1598 ** Description:             Rotate coordinate frame for 1bpp sprite
   1599 ***************************************************************************************/
   1600 // Does nothing for 4, 8 and 16 bpp sprites.
   1601 void TFT_eSprite::setRotation(uint8_t r)
   1602 {
   1603   if (_bpp != 1) return;
   1604 
   1605   rotation = r;
   1606   
   1607   if (rotation&1) {
   1608     resetViewport();
   1609   }
   1610   else {
   1611     resetViewport();
   1612   }
   1613 }
   1614 
   1615 
   1616 /***************************************************************************************
   1617 ** Function name:           getRotation
   1618 ** Description:             Get rotation for 1bpp sprite
   1619 ***************************************************************************************/
   1620 uint8_t TFT_eSprite::getRotation(void)
   1621 {
   1622   return rotation;
   1623 }
   1624 
   1625 
   1626 /***************************************************************************************
   1627 ** Function name:           drawPixel
   1628 ** Description:             push a single pixel at an arbitrary position
   1629 ***************************************************************************************/
   1630 void TFT_eSprite::drawPixel(int32_t x, int32_t y, uint32_t color)
   1631 {
   1632   if (!_created || _vpOoB) return;
   1633 
   1634   x+= _xDatum;
   1635   y+= _yDatum;
   1636 
   1637   // Range checking
   1638   if ((x < _vpX) || (y < _vpY) ||(x >= _vpW) || (y >= _vpH)) return;
   1639 
   1640   if (_bpp == 16)
   1641   {
   1642     color = (color >> 8) | (color << 8);
   1643     _img[x+y*_iwidth] = (uint16_t) color;
   1644   }
   1645   else if (_bpp == 8)
   1646   {
   1647     _img8[x+y*_iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3);
   1648   }
   1649   else if (_bpp == 4)
   1650   {
   1651     uint8_t c = color & 0x0F;
   1652     int index = (x+y*_iwidth)>>1;;
   1653     if ((x & 0x01) == 0) {
   1654       _img4[index] = (uint8_t)((c << 4) | (_img4[index] & 0x0F));
   1655     }
   1656     else {
   1657       _img4[index] =  (uint8_t)(c | (_img4[index] & 0xF0));
   1658     }
   1659   }
   1660   else // 1 bpp
   1661   {
   1662     if (rotation == 1)
   1663     {
   1664       uint16_t tx = x;
   1665       x = _dwidth - y - 1;
   1666       y = tx;
   1667     }
   1668     else if (rotation == 2)
   1669     {
   1670       x = _dwidth - x - 1;
   1671       y = _dheight - y - 1;
   1672     }
   1673     else if (rotation == 3)
   1674     {
   1675       uint16_t tx = x;
   1676       x = y;
   1677       y = _dheight - tx - 1;
   1678     }
   1679 
   1680     if (color) _img8[(x + y * _bitwidth)>>3] |=  (0x80 >> (x & 0x7));
   1681     else       _img8[(x + y * _bitwidth)>>3] &= ~(0x80 >> (x & 0x7));
   1682   }
   1683 }
   1684 
   1685 
   1686 /***************************************************************************************
   1687 ** Function name:           drawLine
   1688 ** Description:             draw a line between 2 arbitrary points
   1689 ***************************************************************************************/
   1690 void TFT_eSprite::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color)
   1691 {
   1692   if (!_created || _vpOoB) return;
   1693 
   1694   //_xDatum and _yDatum Not added here, it is added by drawPixel & drawFastxLine
   1695 
   1696   bool steep = abs(y1 - y0) > abs(x1 - x0);
   1697   if (steep) {
   1698     transpose(x0, y0);
   1699     transpose(x1, y1);
   1700   }
   1701 
   1702   if (x0 > x1) {
   1703     transpose(x0, x1);
   1704     transpose(y0, y1);
   1705   }
   1706 
   1707   int32_t dx = x1 - x0, dy = abs(y1 - y0);;
   1708 
   1709   int32_t err = dx >> 1, ystep = -1, xs = x0, dlen = 0;
   1710 
   1711   if (y0 < y1) ystep = 1;
   1712 
   1713   // Split into steep and not steep for FastH/V separation
   1714   if (steep) {
   1715     for (; x0 <= x1; x0++) {
   1716       dlen++;
   1717       err -= dy;
   1718       if (err < 0) {
   1719         err += dx;
   1720         if (dlen == 1) drawPixel(y0, xs, color);
   1721         else drawFastVLine(y0, xs, dlen, color);
   1722         dlen = 0; y0 += ystep; xs = x0 + 1;
   1723       }
   1724     }
   1725     if (dlen) drawFastVLine(y0, xs, dlen, color);
   1726   }
   1727   else
   1728   {
   1729     for (; x0 <= x1; x0++) {
   1730       dlen++;
   1731       err -= dy;
   1732       if (err < 0) {
   1733         err += dx;
   1734         if (dlen == 1) drawPixel(xs, y0, color);
   1735         else drawFastHLine(xs, y0, dlen, color);
   1736         dlen = 0; y0 += ystep; xs = x0 + 1;
   1737       }
   1738     }
   1739     if (dlen) drawFastHLine(xs, y0, dlen, color);
   1740   }
   1741 }
   1742 
   1743 
   1744 /***************************************************************************************
   1745 ** Function name:           drawFastVLine
   1746 ** Description:             draw a vertical line
   1747 ***************************************************************************************/
   1748 void TFT_eSprite::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color)
   1749 {
   1750   if (!_created || _vpOoB) return;
   1751 
   1752   x+= _xDatum;
   1753   y+= _yDatum;
   1754 
   1755   // Clipping
   1756   if ((x < _vpX) || (x >= _vpW) || (y >= _vpH)) return;
   1757 
   1758   if (y < _vpY) { h += y - _vpY; y = _vpY; }
   1759 
   1760   if ((y + h) > _vpH) h = _vpH - y;
   1761 
   1762   if (h < 1) return;
   1763 
   1764   if (_bpp == 16)
   1765   {
   1766     color = (color >> 8) | (color << 8);
   1767     int32_t yp = x + _iwidth * y;
   1768     while (h--) {_img[yp] = (uint16_t) color; yp += _iwidth;}
   1769   }
   1770   else if (_bpp == 8)
   1771   {
   1772     color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3;
   1773     while (h--) _img8[x + _iwidth * y++] = (uint8_t) color;
   1774   }
   1775   else if (_bpp == 4)
   1776   {
   1777     if ((x & 0x01) == 0)
   1778     {
   1779       uint8_t c = (uint8_t) (color & 0xF) << 4;
   1780       while (h--) {
   1781         _img4[(x + _iwidth * y)>>1] = (uint8_t) (c | (_img4[(x + _iwidth * y)>>1] & 0x0F));
   1782         y++;
   1783       }
   1784     }
   1785     else {
   1786       uint8_t c = (uint8_t)color & 0xF;
   1787       while (h--) {
   1788         _img4[(x + _iwidth * y)>>1] = (uint8_t) (c | (_img4[(x + _iwidth * y)>>1] & 0xF0)); // x is odd; new color goes into the low bits.
   1789         y++;
   1790       }
   1791     }
   1792   }
   1793   else
   1794   {
   1795     x -= _xDatum; // Remove any offset as it will be added by drawPixel
   1796     y -= _yDatum;
   1797     while (h--)
   1798     {
   1799       drawPixel(x, y++, color);
   1800     }
   1801   }
   1802 }
   1803 
   1804 
   1805 /***************************************************************************************
   1806 ** Function name:           drawFastHLine
   1807 ** Description:             draw a horizontal line
   1808 ***************************************************************************************/
   1809 void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color)
   1810 {
   1811   if (!_created || _vpOoB) return;
   1812 
   1813   x+= _xDatum;
   1814   y+= _yDatum;
   1815 
   1816   // Clipping
   1817   if ((y < _vpY) || (x >= _vpW) || (y >= _vpH)) return;
   1818 
   1819   if (x < _vpX) { w += x - _vpX; x = _vpX; }
   1820 
   1821   if ((x + w) > _vpW) w = _vpW - x;
   1822 
   1823   if (w < 1) return;
   1824 
   1825   if (_bpp == 16)
   1826   {
   1827     color = (color >> 8) | (color << 8);
   1828     while (w--) _img[_iwidth * y + x++] = (uint16_t) color;
   1829   }
   1830   else if (_bpp == 8)
   1831   {
   1832     color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3;
   1833     memset(_img8+_iwidth * y + x, (uint8_t)color, w);
   1834   }
   1835   else if (_bpp == 4)
   1836   {
   1837     uint8_t c = (uint8_t)color & 0x0F;
   1838     uint8_t c2 = (c | ((c << 4) & 0xF0));
   1839     if ((x & 0x01) == 1)
   1840     {
   1841       drawPixel(x - _xDatum, y - _yDatum, color);
   1842       x++; w--;
   1843       if (w < 1)
   1844         return;
   1845     }
   1846 
   1847     if (((w + x) & 0x01) == 1)
   1848     {
   1849       // handle the extra one at the other end
   1850       drawPixel(x - _xDatum + w - 1, y - _yDatum, color);
   1851       w--;
   1852       if (w < 1) return;
   1853     }
   1854     memset(_img4 + ((_iwidth * y + x) >> 1), c2, (w >> 1));
   1855   }
   1856   else {
   1857     x -= _xDatum; // Remove any offset as it will be added by drawPixel
   1858     y -= _yDatum;
   1859 
   1860     while (w--)
   1861     {
   1862       drawPixel(x++, y, color);
   1863     }
   1864   }
   1865 }
   1866 
   1867 
   1868 /***************************************************************************************
   1869 ** Function name:           fillRect
   1870 ** Description:             draw a filled rectangle
   1871 ***************************************************************************************/
   1872 void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color)
   1873 {
   1874   if (!_created || _vpOoB) return;
   1875 
   1876   x+= _xDatum;
   1877   y+= _yDatum;
   1878 
   1879   // Clipping
   1880   if ((x >= _vpW) || (y >= _vpH)) return;
   1881 
   1882   if (x < _vpX) { w += x - _vpX; x = _vpX; }
   1883   if (y < _vpY) { h += y - _vpY; y = _vpY; }
   1884 
   1885   if ((x + w) > _vpW) w = _vpW - x;
   1886   if ((y + h) > _vpH) h = _vpH - y;
   1887 
   1888   if ((w < 1) || (h < 1)) return;
   1889 
   1890   int32_t yp = _iwidth * y + x;
   1891 
   1892   if (_bpp == 16)
   1893   {
   1894     color = (color >> 8) | (color << 8);
   1895     uint32_t iw = w;
   1896     int32_t ys = yp;
   1897     if(h--)  {while (iw--) _img[yp++] = (uint16_t) color;}
   1898     yp = ys;
   1899     while (h--)
   1900     {
   1901       yp += _iwidth;
   1902       memcpy( _img+yp, _img+ys, w<<1);
   1903     }
   1904   }
   1905   else if (_bpp == 8)
   1906   {
   1907     color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3;
   1908     while (h--)
   1909     {
   1910       memset(_img8 + yp, (uint8_t)color, w);
   1911       yp += _iwidth;
   1912     }
   1913   }
   1914   else if (_bpp == 4)
   1915   {
   1916     uint8_t c1 = (uint8_t)color & 0x0F;
   1917     uint8_t c2 = c1 | ((c1 << 4) & 0xF0);
   1918     if ((x & 0x01) == 0 && (w & 0x01) == 0)
   1919     {
   1920       yp = (yp >> 1);
   1921       while (h--)
   1922       {
   1923         memset(_img4 + yp, c2, (w>>1));
   1924         yp += (_iwidth >> 1);
   1925       }
   1926     }
   1927     else if ((x & 0x01) == 0)
   1928     {
   1929 
   1930       // same as above but you have a hangover on the right.
   1931       yp = (yp >> 1);
   1932       while (h--)
   1933       {
   1934         if (w > 1)
   1935           memset(_img4 + yp, c2, (w-1)>>1);
   1936         // handle the rightmost pixel by calling drawPixel
   1937         drawPixel(x+w-1-_xDatum, y+h-_yDatum, c1);
   1938         yp += (_iwidth >> 1);
   1939       }
   1940     }
   1941     else if ((w & 0x01) == 1)
   1942     {
   1943       yp = (yp + 1) >> 1;
   1944       while (h--) {
   1945         drawPixel(x-_xDatum, y+h-_yDatum, color & 0x0F);
   1946         if (w > 1)
   1947           memset(_img4 + yp, c2, (w-1)>>1);
   1948         // same as above but you have a hangover on the left instead
   1949         yp += (_iwidth >> 1);
   1950       }
   1951     }
   1952     else
   1953     {
   1954       yp = (yp + 1) >> 1;
   1955       while (h--) {
   1956         drawPixel(x-_xDatum, y+h-_yDatum, color & 0x0F);
   1957         if (w > 1) drawPixel(x+w-1-_xDatum, y+h-_yDatum, color & 0x0F);
   1958         if (w > 2)
   1959           memset(_img4 + yp, c2, (w-2)>>1);
   1960         // maximal hacking, single pixels on left and right.
   1961         yp += (_iwidth >> 1);
   1962       }
   1963     }
   1964   }
   1965   else
   1966   {
   1967     x -= _xDatum;
   1968     y -= _yDatum;
   1969     while (h--)
   1970     {
   1971       int32_t ww = w;
   1972       int32_t xx = x;
   1973       while (ww--) drawPixel(xx++, y, color);
   1974       y++;
   1975     }
   1976   }
   1977 }
   1978 
   1979 
   1980 /***************************************************************************************
   1981 ** Function name:           drawChar
   1982 ** Description:             draw a single character in the Adafruit GLCD or freefont
   1983 ***************************************************************************************/
   1984 void TFT_eSprite::drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32_t bg, uint8_t size)
   1985 {
   1986   if ( _vpOoB || !_created ) return;
   1987 
   1988   if (c < 32) return;
   1989 #ifdef LOAD_GLCD
   1990 //>>>>>>>>>>>>>>>>>>
   1991 #ifdef LOAD_GFXFF
   1992   if(!gfxFont) { // 'Classic' built-in font
   1993 #endif
   1994 //>>>>>>>>>>>>>>>>>>
   1995 
   1996   if ((x >= _vpW - _xDatum) || // Clip right
   1997       (y >= _vpH - _yDatum))   // Clip bottom
   1998     return;
   1999 
   2000   if (((x + 6 * size - 1) < (_vpX - _xDatum)) || // Clip left
   2001       ((y + 8 * size - 1) < (_vpY - _yDatum)))   // Clip top
   2002     return;
   2003 
   2004   bool fillbg = (bg != color);
   2005 
   2006   if ((size==1) && fillbg)
   2007   {
   2008     uint8_t column[6];
   2009     uint8_t mask = 0x1;
   2010 
   2011     for (int8_t i = 0; i < 5; i++ ) column[i] = pgm_read_byte(font + (c * 5) + i);
   2012     column[5] = 0;
   2013 
   2014     int8_t j, k;
   2015     for (j = 0; j < 8; j++) {
   2016       for (k = 0; k < 5; k++ ) {
   2017         if (column[k] & mask) {
   2018           drawPixel(x + k, y + j, color);
   2019         }
   2020         else {
   2021           drawPixel(x + k, y + j, bg);
   2022         }
   2023       }
   2024 
   2025       mask <<= 1;
   2026 
   2027       drawPixel(x + k, y + j, bg);
   2028     }
   2029   }
   2030   else
   2031   {
   2032     for (int8_t i = 0; i < 6; i++ ) {
   2033       uint8_t line;
   2034       if (i == 5)
   2035         line = 0x0;
   2036       else
   2037         line = pgm_read_byte(font + (c * 5) + i);
   2038 
   2039       if (size == 1) // default size
   2040       {
   2041         for (int8_t j = 0; j < 8; j++) {
   2042           if (line & 0x1) drawPixel(x + i, y + j, color);
   2043           line >>= 1;
   2044         }
   2045       }
   2046       else {  // big size
   2047         for (int8_t j = 0; j < 8; j++) {
   2048           if (line & 0x1) fillRect(x + (i * size), y + (j * size), size, size, color);
   2049           else if (fillbg) fillRect(x + i * size, y + j * size, size, size, bg);
   2050           line >>= 1;
   2051         }
   2052       }
   2053     }
   2054   }
   2055 
   2056 //>>>>>>>>>>>>>>>>>>>>>>>>>>>
   2057 #ifdef LOAD_GFXFF
   2058   } else { // Custom font
   2059 #endif
   2060 //>>>>>>>>>>>>>>>>>>>>>>>>>>>
   2061 #endif // LOAD_GLCD
   2062 
   2063 #ifdef LOAD_GFXFF
   2064     // Filter out bad characters not present in font
   2065     if ((c >= pgm_read_word(&gfxFont->first)) && (c <= pgm_read_word(&gfxFont->last )))
   2066     {
   2067 //>>>>>>>>>>>>>>>>>>>>>>>>>>>
   2068 
   2069       c -= pgm_read_word(&gfxFont->first);
   2070       GFXglyph *glyph  = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]);
   2071 
   2072       uint8_t  w  = pgm_read_byte(&glyph->width),
   2073                h  = pgm_read_byte(&glyph->height);
   2074       int8_t   xo = pgm_read_byte(&glyph->xOffset),
   2075                yo = pgm_read_byte(&glyph->yOffset);
   2076 
   2077       if (((x + xo + w * size - 1) < (_vpX - _xDatum)) || // Clip left
   2078           ((y + yo + h * size - 1) < (_vpY - _yDatum)))   // Clip top
   2079         return;
   2080 
   2081       uint8_t  *bitmap = (uint8_t *)pgm_read_dword(&gfxFont->bitmap);
   2082       uint32_t bo = pgm_read_word(&glyph->bitmapOffset);
   2083 
   2084       uint8_t  xx, yy, bits=0, bit=0;
   2085       //uint8_t  xa = pgm_read_byte(&glyph->xAdvance);
   2086       int16_t  xo16 = 0, yo16 = 0;
   2087 
   2088       if(size > 1) {
   2089         xo16 = xo;
   2090         yo16 = yo;
   2091       }
   2092 
   2093       uint16_t hpc = 0; // Horizontal foreground pixel count
   2094       for(yy=0; yy<h; yy++) {
   2095         for(xx=0; xx<w; xx++) {
   2096           if(bit == 0) {
   2097             bits = pgm_read_byte(&bitmap[bo++]);
   2098             bit  = 0x80;
   2099           }
   2100           if(bits & bit) hpc++;
   2101           else {
   2102             if (hpc) {
   2103               if(size == 1) drawFastHLine(x+xo+xx-hpc, y+yo+yy, hpc, color);
   2104               else fillRect(x+(xo16+xx-hpc)*size, y+(yo16+yy)*size, size*hpc, size, color);
   2105               hpc=0;
   2106             }
   2107           }
   2108           bit >>= 1;
   2109         }
   2110         // Draw pixels for this line as we are about to increment yy
   2111         if (hpc) {
   2112           if(size == 1) drawFastHLine(x+xo+xx-hpc, y+yo+yy, hpc, color);
   2113           else fillRect(x+(xo16+xx-hpc)*size, y+(yo16+yy)*size, size*hpc, size, color);
   2114           hpc=0;
   2115         }
   2116       }
   2117     }
   2118 #endif
   2119 
   2120 
   2121 #ifdef LOAD_GLCD
   2122   #ifdef LOAD_GFXFF
   2123   } // End classic vs custom font
   2124   #endif
   2125 #else
   2126   #ifndef LOAD_GFXFF
   2127     color = color;
   2128     bg = bg;
   2129     size = size;
   2130   #endif
   2131 #endif
   2132 
   2133 }
   2134 
   2135 
   2136 /***************************************************************************************
   2137 ** Function name:           drawChar
   2138 ** Description:             draw a unicode glyph into the sprite
   2139 ***************************************************************************************/
   2140   // TODO: Rationalise with TFT_eSPI
   2141   // Any UTF-8 decoding must be done before calling drawChar()
   2142 int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y)
   2143 {
   2144   return drawChar(uniCode, x, y, textfont);
   2145 }
   2146 
   2147   // Any UTF-8 decoding must be done before calling drawChar()
   2148 int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font)
   2149 {
   2150   if (_vpOoB || !uniCode) return 0;
   2151 
   2152   if (font==1) {
   2153 #ifdef LOAD_GLCD
   2154   #ifndef LOAD_GFXFF
   2155     drawChar(x, y, uniCode, textcolor, textbgcolor, textsize);
   2156     return 6 * textsize;
   2157   #endif
   2158 #else
   2159   #ifndef LOAD_GFXFF
   2160     return 0;
   2161   #endif
   2162 #endif
   2163 
   2164 #ifdef LOAD_GFXFF
   2165     drawChar(x, y, uniCode, textcolor, textbgcolor, textsize);
   2166     if(!gfxFont) { // 'Classic' built-in font
   2167     #ifdef LOAD_GLCD
   2168       return 6 * textsize;
   2169     #else
   2170       return 0;
   2171     #endif
   2172     }
   2173     else {
   2174       if((uniCode >= pgm_read_word(&gfxFont->first)) && (uniCode <= pgm_read_word(&gfxFont->last) )) {
   2175         uint16_t   c2    = uniCode - pgm_read_word(&gfxFont->first);
   2176         GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]);
   2177         return pgm_read_byte(&glyph->xAdvance) * textsize;
   2178       }
   2179       else {
   2180         return 0;
   2181       }
   2182     }
   2183 #endif
   2184   }
   2185 
   2186   if ((font>1) && (font<9) && ((uniCode < 32) || (uniCode > 127))) return 0;
   2187 
   2188   int32_t width  = 0;
   2189   int32_t height = 0;
   2190   uint32_t flash_address = 0;
   2191   uniCode -= 32;
   2192 
   2193 #ifdef LOAD_FONT2
   2194   if (font == 2) {
   2195     flash_address = pgm_read_dword(&chrtbl_f16[uniCode]);
   2196     width = pgm_read_byte(widtbl_f16 + uniCode);
   2197     height = chr_hgt_f16;
   2198   }
   2199   #ifdef LOAD_RLE
   2200   else
   2201   #endif
   2202 #endif
   2203 
   2204 #ifdef LOAD_RLE
   2205   {
   2206     if ((font>2) && (font<9)) {
   2207       flash_address = pgm_read_dword( (const void*)(pgm_read_dword( &(fontdata[font].chartbl ) ) + uniCode*sizeof(void *)) );
   2208       width = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[font].widthtbl ) ) + uniCode );
   2209       height= pgm_read_byte( &fontdata[font].height );
   2210     }
   2211   }
   2212 #endif
   2213 
   2214   int32_t xd = x + _xDatum;
   2215   int32_t yd = y + _yDatum;
   2216 
   2217   if ((xd + width * textsize < _vpX || xd >= _vpW) && (yd + height * textsize < _vpY || yd >= _vpH)) return width * textsize ;
   2218 
   2219   int32_t w = width;
   2220   int32_t pX      = 0;
   2221   int32_t pY      = y;
   2222   uint8_t line = 0;
   2223   bool clip = xd < _vpX || xd + width  * textsize >= _vpW || yd < _vpY || yd + height * textsize >= _vpH;
   2224 
   2225 #ifdef LOAD_FONT2 // chop out code if we do not need it
   2226   if (font == 2) {
   2227     w = w + 6; // Should be + 7 but we need to compensate for width increment
   2228     w = w / 8;
   2229 
   2230     for (int32_t i = 0; i < height; i++)
   2231     {
   2232       if (textcolor != textbgcolor) fillRect(x, pY, width * textsize, textsize, textbgcolor);
   2233 
   2234       for (int32_t k = 0; k < w; k++)
   2235       {
   2236         line = pgm_read_byte((uint8_t *)flash_address + w * i + k);
   2237         if (line) {
   2238           if (textsize == 1) {
   2239             pX = x + k * 8;
   2240             if (line & 0x80) drawPixel(pX, pY, textcolor);
   2241             if (line & 0x40) drawPixel(pX + 1, pY, textcolor);
   2242             if (line & 0x20) drawPixel(pX + 2, pY, textcolor);
   2243             if (line & 0x10) drawPixel(pX + 3, pY, textcolor);
   2244             if (line & 0x08) drawPixel(pX + 4, pY, textcolor);
   2245             if (line & 0x04) drawPixel(pX + 5, pY, textcolor);
   2246             if (line & 0x02) drawPixel(pX + 6, pY, textcolor);
   2247             if (line & 0x01) drawPixel(pX + 7, pY, textcolor);
   2248           }
   2249           else {
   2250             pX = x + k * 8 * textsize;
   2251             if (line & 0x80) fillRect(pX, pY, textsize, textsize, textcolor);
   2252             if (line & 0x40) fillRect(pX + textsize, pY, textsize, textsize, textcolor);
   2253             if (line & 0x20) fillRect(pX + 2 * textsize, pY, textsize, textsize, textcolor);
   2254             if (line & 0x10) fillRect(pX + 3 * textsize, pY, textsize, textsize, textcolor);
   2255             if (line & 0x08) fillRect(pX + 4 * textsize, pY, textsize, textsize, textcolor);
   2256             if (line & 0x04) fillRect(pX + 5 * textsize, pY, textsize, textsize, textcolor);
   2257             if (line & 0x02) fillRect(pX + 6 * textsize, pY, textsize, textsize, textcolor);
   2258             if (line & 0x01) fillRect(pX + 7 * textsize, pY, textsize, textsize, textcolor);
   2259           }
   2260         }
   2261       }
   2262       pY += textsize;
   2263     }
   2264   }
   2265 
   2266   #ifdef LOAD_RLE
   2267   else
   2268   #endif
   2269 #endif  //FONT2
   2270 
   2271 #ifdef LOAD_RLE  //674 bytes of code
   2272   // Font is not 2 and hence is RLE encoded
   2273   {
   2274     w *= height; // Now w is total number of pixels in the character
   2275     int16_t color = textcolor;
   2276     if (_bpp == 16) color = (textcolor >> 8) | (textcolor << 8);
   2277     else if (_bpp == 8) color = ((textcolor & 0xE000)>>8 | (textcolor & 0x0700)>>6 | (textcolor & 0x0018)>>3);
   2278 
   2279     int16_t bgcolor = textbgcolor;
   2280     if (_bpp == 16) bgcolor = (textbgcolor >> 8) | (textbgcolor << 8);
   2281     else if (_bpp == 8) bgcolor = ((textbgcolor & 0xE000)>>8 | (textbgcolor & 0x0700)>>6 | (textbgcolor & 0x0018)>>3);
   2282 
   2283     if (textcolor == textbgcolor && !clip && _bpp != 1) {
   2284       int32_t px = 0, py = pY; // To hold character block start and end column and row values
   2285       int32_t pc = 0; // Pixel count
   2286       uint8_t np = textsize * textsize; // Number of pixels in a drawn pixel
   2287 
   2288       uint8_t tnp = 0; // Temporary copy of np for while loop
   2289       uint8_t ts = textsize - 1; // Temporary copy of textsize
   2290       // 16 bit pixel count so maximum font size is equivalent to 180x180 pixels in area
   2291       // w is total number of pixels to plot to fill character block
   2292       while (pc < w) {
   2293         line = pgm_read_byte((uint8_t *)flash_address);
   2294         flash_address++;
   2295         if (line & 0x80) {
   2296           line &= 0x7F;
   2297           line++;
   2298           if (ts) {
   2299             px = xd + textsize * (pc % width); // Keep these px and py calculations outside the loop as they are slow
   2300             py = yd + textsize * (pc / width);
   2301           }
   2302           else {
   2303             px = xd + pc % width; // Keep these px and py calculations outside the loop as they are slow
   2304             py = yd + pc / width;
   2305           }
   2306           while (line--) { // In this case the while(line--) is faster
   2307             pc++; // This is faster than putting pc+=line before while()?
   2308             setWindow(px, py, px + ts, py + ts);
   2309 
   2310             if (ts) {
   2311               tnp = np;
   2312               while (tnp--) writeColor(color);
   2313             }
   2314             else writeColor(color);
   2315 
   2316             px += textsize;
   2317 
   2318             if (px >= (xd + width * textsize)) {
   2319               px = xd;
   2320               py += textsize;
   2321             }
   2322           }
   2323         }
   2324         else {
   2325           line++;
   2326           pc += line;
   2327         }
   2328       }
   2329     }
   2330     else {
   2331       // Text colour != background and textsize = 1 and character is within viewport area
   2332       // so use faster drawing of characters and background using block write
   2333       if (textcolor != textbgcolor && textsize == 1 && !clip && _bpp != 1)
   2334       {
   2335         setWindow(xd, yd, xd + width - 1, yd + height - 1);
   2336 
   2337         // Maximum font size is equivalent to 180x180 pixels in area
   2338         while (w > 0) {
   2339           line = pgm_read_byte((uint8_t *)flash_address++); // 8 bytes smaller when incrementing here
   2340           if (line & 0x80) {
   2341             line &= 0x7F;
   2342             line++; w -= line;
   2343             while (line--) writeColor(color);
   2344           }
   2345           else {
   2346             line++; w -= line;
   2347             while (line--) writeColor(bgcolor);
   2348           }
   2349         }
   2350       }
   2351       else
   2352       {
   2353         int32_t px = 0, py = 0;  // To hold character pixel coords
   2354         int32_t tx = 0, ty = 0;  // To hold character TFT pixel coords
   2355         int32_t pc = 0;          // Pixel count
   2356         int32_t pl = 0;          // Pixel line length
   2357         uint16_t pcol = 0;       // Pixel color
   2358         bool     pf = true;      // Flag for plotting
   2359         while (pc < w) {
   2360           line = pgm_read_byte((uint8_t *)flash_address);
   2361           flash_address++;
   2362           if (line & 0x80) { pcol = textcolor; line &= 0x7F; pf = true;}
   2363           else { pcol = textbgcolor; if (textcolor == textbgcolor) pf = false;}
   2364           line++;
   2365           px = pc % width;
   2366           tx = x + textsize * px;
   2367           py = pc / width;
   2368           ty = y + textsize * py;
   2369 
   2370           pl = 0;
   2371           pc += line;
   2372           while (line--) {
   2373             pl++;
   2374             if ((px+pl) >= width) {
   2375               if (pf) fillRect(tx, ty, pl * textsize, textsize, pcol);
   2376               pl = 0;
   2377               px = 0;
   2378               tx = x;
   2379               py ++;
   2380               ty += textsize;
   2381             }
   2382           }
   2383           if (pl && pf) fillRect(tx, ty, pl * textsize, textsize, pcol);
   2384         }
   2385       }
   2386     }
   2387   }
   2388   // End of RLE font rendering
   2389 #endif
   2390 
   2391 #if !defined (LOAD_FONT2) && !defined (LOAD_RLE)
   2392   // Stop warnings
   2393   flash_address = flash_address;
   2394   w = w;
   2395   pX = pX;
   2396   pY = pY;
   2397   line = line;
   2398   clip = clip;
   2399 #endif
   2400 
   2401   return width * textsize;    // x +
   2402 }
   2403 
   2404 
   2405 #ifdef SMOOTH_FONT
   2406 /***************************************************************************************
   2407 ** Function name:           drawGlyph
   2408 ** Description:             Write a character to the sprite cursor position
   2409 ***************************************************************************************/
   2410 //
   2411 void TFT_eSprite::drawGlyph(uint16_t code)
   2412 {
   2413   uint16_t fg = textcolor;
   2414   uint16_t bg = textbgcolor;
   2415   bool getBG  = false;
   2416   if (fg == bg) getBG = true;
   2417 
   2418   // Check if cursor has moved
   2419   if (last_cursor_x != cursor_x)
   2420   {
   2421     bg_cursor_x = cursor_x;
   2422     last_cursor_x = cursor_x;
   2423   }
   2424 
   2425   if (code < 0x21)
   2426   {
   2427     if (code == 0x20) {
   2428       if (_fillbg) fillRect(bg_cursor_x, cursor_y, (cursor_x + gFont.spaceWidth) - bg_cursor_x, gFont.yAdvance, bg);
   2429       cursor_x += gFont.spaceWidth;
   2430       bg_cursor_x = cursor_x;
   2431       last_cursor_x = cursor_x;
   2432       return;
   2433     }
   2434 
   2435     if (code == '\n') {
   2436       cursor_x = 0;
   2437       bg_cursor_x = 0;
   2438       last_cursor_x = 0;
   2439       cursor_y += gFont.yAdvance;
   2440       if (textwrapY && (cursor_y >= height())) cursor_y = 0;
   2441       return;
   2442     }
   2443   }
   2444 
   2445   uint16_t gNum = 0;
   2446   bool found = getUnicodeIndex(code, &gNum);
   2447 
   2448   if (found)
   2449   {
   2450 
   2451     bool newSprite = !_created;
   2452 
   2453     if (newSprite)
   2454     {
   2455       createSprite(gWidth[gNum], gFont.yAdvance);
   2456       if(fg != bg) fillSprite(bg);
   2457       cursor_x = -gdX[gNum];
   2458       bg_cursor_x = cursor_x;
   2459       last_cursor_x = cursor_x;
   2460       cursor_y = 0;
   2461     }
   2462     else
   2463     {
   2464       if( textwrapX && ((cursor_x + gWidth[gNum] + gdX[gNum]) > width())) {
   2465         cursor_y += gFont.yAdvance;
   2466         cursor_x = 0;
   2467         bg_cursor_x = 0;
   2468         last_cursor_x = 0;
   2469       }
   2470 
   2471       if( textwrapY && ((cursor_y + gFont.yAdvance) > height())) cursor_y = 0;
   2472       if ( cursor_x == 0) cursor_x -= gdX[gNum];
   2473     }
   2474 
   2475     uint8_t* pbuffer = nullptr;
   2476     const uint8_t* gPtr = (const uint8_t*) gFont.gArray;
   2477 
   2478 #ifdef FONT_FS_AVAILABLE
   2479     if (fs_font) {
   2480       fontFile.seek(gBitmap[gNum], fs::SeekSet); // This is slow for a significant position shift!
   2481       pbuffer =  (uint8_t*)malloc(gWidth[gNum]);
   2482     }
   2483 #endif
   2484 
   2485     int16_t cy = cursor_y + gFont.maxAscent - gdY[gNum];
   2486     int16_t cx = cursor_x + gdX[gNum];
   2487 
   2488     //  if (cx > width() && bg_cursor_x > width()) return;
   2489     //  if (cursor_y > height()) return;
   2490 
   2491     int16_t  fxs = cx;
   2492     uint32_t fl = 0;
   2493     int16_t  bxs = cx;
   2494     uint32_t bl = 0;
   2495     int16_t  bx = 0;
   2496     uint8_t pixel = 0;
   2497 
   2498     int16_t fillwidth  = 0;
   2499     int16_t fillheight = 0;
   2500 
   2501     // Fill area above glyph
   2502     if (_fillbg) {
   2503       fillwidth  = (cursor_x + gxAdvance[gNum]) - bg_cursor_x;
   2504       if (fillwidth > 0) {
   2505         fillheight = gFont.maxAscent - gdY[gNum];
   2506         if (fillheight > 0) {
   2507           fillRect(bg_cursor_x, cursor_y, fillwidth, fillheight, textbgcolor);
   2508         }
   2509       }
   2510       else {
   2511         // Could be negative
   2512         fillwidth = 0;
   2513       }
   2514 
   2515       // Fill any area to left of glyph                              
   2516       if (bg_cursor_x < cx) fillRect(bg_cursor_x, cy, cx - bg_cursor_x, gHeight[gNum], textbgcolor);
   2517       // Set x position in glyph area where background starts
   2518       if (bg_cursor_x > cx) bx = bg_cursor_x - cx;
   2519       // Fill any area to right of glyph
   2520       if (cx + gWidth[gNum] < cursor_x + gxAdvance[gNum]) {
   2521         fillRect(cx + gWidth[gNum], cy, (cursor_x + gxAdvance[gNum]) - (cx + gWidth[gNum]), gHeight[gNum], textbgcolor);
   2522       }
   2523     }
   2524 
   2525     for (int32_t y = 0; y < gHeight[gNum]; y++)
   2526     {
   2527 #ifdef FONT_FS_AVAILABLE
   2528       if (fs_font) {
   2529         fontFile.read(pbuffer, gWidth[gNum]);
   2530       }
   2531 #endif
   2532 
   2533       for (int32_t x = 0; x < gWidth[gNum]; x++)
   2534       {
   2535 #ifdef FONT_FS_AVAILABLE
   2536         if (fs_font) pixel = pbuffer[x];
   2537         else
   2538 #endif
   2539         pixel = pgm_read_byte(gPtr + gBitmap[gNum] + x + gWidth[gNum] * y);
   2540 
   2541         if (pixel)
   2542         {
   2543           if (bl) { drawFastHLine( bxs, y + cy, bl, bg); bl = 0; }
   2544           if (pixel != 0xFF)
   2545           {
   2546             if (fl) {
   2547               if (fl==1) drawPixel(fxs, y + cy, fg);
   2548               else drawFastHLine( fxs, y + cy, fl, fg);
   2549               fl = 0;
   2550             }
   2551             if (getBG) bg = readPixel(x + cx, y + cy);
   2552             drawPixel(x + cx, y + cy, alphaBlend(pixel, fg, bg));
   2553           }
   2554           else
   2555           {
   2556             if (fl==0) fxs = x + cx;
   2557             fl++;
   2558           }
   2559         }
   2560         else
   2561         {
   2562           if (fl) { drawFastHLine( fxs, y + cy, fl, fg); fl = 0; }
   2563           if (_fillbg) {
   2564             if (x >= bx) {
   2565               if (bl==0) bxs = x + cx;
   2566               bl++;
   2567             }
   2568           }
   2569         }
   2570       }
   2571       if (fl) { drawFastHLine( fxs, y + cy, fl, fg); fl = 0; }
   2572       if (bl) { drawFastHLine( bxs, y + cy, bl, bg); bl = 0; }
   2573     }
   2574 
   2575     // Fill area below glyph
   2576     if (fillwidth > 0) {
   2577       fillheight = (cursor_y + gFont.yAdvance) - (cy + gHeight[gNum]);
   2578       if (fillheight > 0) {
   2579         fillRect(bg_cursor_x, cy + gHeight[gNum], fillwidth, fillheight, textbgcolor);
   2580       }
   2581     }
   2582 
   2583     if (pbuffer) free(pbuffer);
   2584     cursor_x += gxAdvance[gNum];
   2585 
   2586     if (newSprite)
   2587     {
   2588       pushSprite(cx, cursor_y);
   2589       deleteSprite();
   2590     }
   2591   }
   2592   else
   2593   {
   2594     // Not a Unicode in font so draw a rectangle and move on cursor
   2595     drawRect(cursor_x, cursor_y + gFont.maxAscent - gFont.ascent, gFont.spaceWidth, gFont.ascent, fg);
   2596     cursor_x += gFont.spaceWidth + 1;
   2597   }
   2598   bg_cursor_x = cursor_x;
   2599   last_cursor_x = cursor_x;
   2600 }
   2601 
   2602 
   2603 /***************************************************************************************
   2604 ** Function name:           printToSprite
   2605 ** Description:             Write a string to the sprite cursor position
   2606 ***************************************************************************************/
   2607 void TFT_eSprite::printToSprite(String string)
   2608 {
   2609   if(!fontLoaded) return;
   2610   printToSprite((char*)string.c_str(), string.length());
   2611 }
   2612 
   2613 
   2614 /***************************************************************************************
   2615 ** Function name:           printToSprite
   2616 ** Description:             Write a string to the sprite cursor position
   2617 ***************************************************************************************/
   2618 void TFT_eSprite::printToSprite(char *cbuffer, uint16_t len) //String string)
   2619 {
   2620   if(!fontLoaded) return;
   2621 
   2622   uint16_t n = 0;
   2623   bool newSprite = !_created;
   2624   int16_t  cursorX = _tft->cursor_x;
   2625 
   2626   if (newSprite)
   2627   {
   2628     int16_t sWidth = 0;
   2629     uint16_t index = 0;
   2630     bool     first = true;
   2631     while (n < len)
   2632     {
   2633       uint16_t unicode = decodeUTF8((uint8_t*)cbuffer, &n, len - n);
   2634       if (getUnicodeIndex(unicode, &index))
   2635       {
   2636         if (first) {
   2637           first = false;
   2638           sWidth -= gdX[index];
   2639           cursorX += gdX[index];
   2640         }
   2641         if (n == len) sWidth += ( gWidth[index] + gdX[index]);
   2642         else sWidth += gxAdvance[index];
   2643       }
   2644       else sWidth += gFont.spaceWidth + 1;
   2645     }
   2646 
   2647     createSprite(sWidth, gFont.yAdvance);
   2648 
   2649     if (textcolor != textbgcolor) fillSprite(textbgcolor);
   2650   }
   2651 
   2652   n = 0;
   2653 
   2654   while (n < len)
   2655   {
   2656     uint16_t unicode = decodeUTF8((uint8_t*)cbuffer, &n, len - n);
   2657     //Serial.print("Decoded Unicode = 0x");Serial.println(unicode,HEX);
   2658     //Serial.print("n = ");Serial.println(n);
   2659     drawGlyph(unicode);
   2660   }
   2661 
   2662   if (newSprite)
   2663   { // The sprite had to be created so place at TFT cursor
   2664     pushSprite(cursorX, _tft->cursor_y);
   2665     deleteSprite();
   2666   }
   2667 }
   2668 
   2669 
   2670 /***************************************************************************************
   2671 ** Function name:           printToSprite
   2672 ** Description:             Print character in a Sprite, create sprite if needed
   2673 ***************************************************************************************/
   2674 int16_t TFT_eSprite::printToSprite(int16_t x, int16_t y, uint16_t index)
   2675 {
   2676   bool newSprite = !_created;
   2677   int16_t sWidth = gWidth[index];
   2678 
   2679   if (newSprite)
   2680   {
   2681     createSprite(sWidth, gFont.yAdvance);
   2682 
   2683     if (textcolor != textbgcolor) fillSprite(textbgcolor);
   2684 
   2685     drawGlyph(gUnicode[index]);
   2686 
   2687     pushSprite(x + gdX[index], y, textbgcolor);
   2688     deleteSprite();
   2689   }
   2690 
   2691   else drawGlyph(gUnicode[index]);
   2692 
   2693   return gxAdvance[index];
   2694 }
   2695 #endif