acid-drop- Hacking the planet from a LilyGo T-Deck using custom firmware |
git clone git://git.acid.vegas/acid-drop.git |
Log | Files | Refs | Archive | README | LICENSE |
Sprite.cpp (81021B)
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 if ( (_bpp == 4) && (_colorMap == nullptr)) createPalette(default_4bit_palette); 92 93 // This is to make it clear what pointer size is expected to be used 94 // but casting in the user sketch is needed due to the use of void* 95 if ( (_bpp == 1) && (frames > 1) ) 96 { 97 w = (w+7) & 0xFFF8; 98 _img8_2 = _img8 + ( (w>>3) * h + 1 ); 99 } 100 101 if (_img8) 102 { 103 _created = true; 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 ((x >= _vpW - _xDatum) || // Clip right 1989 (y >= _vpH - _yDatum)) // Clip bottom 1990 return; 1991 1992 if (c < 32) return; 1993 #ifdef LOAD_GLCD 1994 //>>>>>>>>>>>>>>>>>> 1995 #ifdef LOAD_GFXFF 1996 if(!gfxFont) { // 'Classic' built-in font 1997 #endif 1998 //>>>>>>>>>>>>>>>>>> 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