diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index 43c9d0e..b998041 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -36,6 +36,8 @@ TFT_eSprite::TFT_eSprite(TFT_eSPI *tft) _xpivot = 0; _ypivot = 0; + _colorMap = nullptr; + this->cursor_y = this->cursor_x = 0; // Text cursor position } @@ -55,6 +57,8 @@ void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames) _iwidth = _dwidth = _bitwidth = w; _iheight = _dheight = h; + _colorMap = nullptr; + this->cursor_x = 0; this->cursor_y = 0; @@ -72,6 +76,7 @@ void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames) _img8_1 = _img8; _img8_2 = _img8; _img = (uint16_t*) _img8; + _img4 = _img8; // This is to make it clear what pointer size is expected to be used // but casting in the user sketch is needed due to the use of void* @@ -132,6 +137,17 @@ void* TFT_eSprite::callocSprite(int16_t w, int16_t h, uint8_t frames) ptr8 = ( uint8_t*) calloc(w * h + 1, sizeof(uint8_t)); } + else if (_bpp == 4) + { + w = (w+1) & 0xFFFE; // width needs to be multiple of 2, with an extra "off screen" pixel + _iwidth = w; +#if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) + if ( psramFound() ) ptr8 = ( uint8_t*) ps_calloc(((w * h) >> 1) + 1, sizeof(uint8_t)); + else +#endif + ptr8 = ( uint8_t*) calloc(((w * h) >> 1) + 1, sizeof(uint8_t)); + } + else // Must be 1 bpp { //_dwidth Display width+height in pixels always in rotation 0 orientation @@ -154,6 +170,28 @@ void* TFT_eSprite::callocSprite(int16_t w, int16_t h, uint8_t frames) return ptr8; } +/*************************************************************************************** +** Function name: setColorMap +** Description: Set a colour map for a 4-bit per pixel sprite +*************************************************************************************x*/ + +void TFT_eSprite::setColorMap(uint16_t colorMap[], int colors) +{ + if (_colorMap != nullptr) + { + free(_colorMap); + } + + // allocate color map + _colorMap = (uint16_t *)calloc(16, sizeof(uint16_t)); + if (colors > 16) + colors = 16; + for (auto i = 0; i < colors; i++) + { + _colorMap[i] = colorMap[i]; + } +} + /*************************************************************************************** ** Function name: frameBuffer ** Description: For 1 bpp Sprites, select the frame used for graphics @@ -167,6 +205,8 @@ void* TFT_eSprite::frameBuffer(int8_t f) if (_bpp == 8) return _img8; + if (_bpp == 4) return _img4; + if ( f == 2 ) _img8 = _img8_2; else _img8 = _img8_1; @@ -185,7 +225,8 @@ void* TFT_eSprite::setColorDepth(int8_t b) // Now define the new colour depth if ( b > 8 ) _bpp = 16; // Bytes per pixel - else if ( b > 1 ) _bpp = 8; + else if ( b > 4 ) _bpp = 8; + else if ( b > 1 ) _bpp = 4; else _bpp = 1; // If it existed, re-create the sprite with the new colour depth @@ -231,6 +272,11 @@ void TFT_eSprite::deleteSprite(void) { if (!_created ) return; + if (_colorMap != nullptr) + { + free(_colorMap); + } + free(_img8_1); _created = false; @@ -667,6 +713,13 @@ void TFT_eSprite::pushSprite(int32_t x, int32_t y) _tft->pushImage(x, y, _iwidth, _iheight, _img ); _tft->setSwapBytes(oldSwapBytes); } + else if (_bpp == 4) + { + if (_colorMap == nullptr) { + return; + } + _tft->pushImage(x, y, _dwidth, _dheight, _img4, false, _colorMap); + } else _tft->pushImage(x, y, _dwidth, _dheight, _img8, (bool)(_bpp == 8)); } @@ -692,10 +745,33 @@ void TFT_eSprite::pushSprite(int32_t x, int32_t y, uint16_t transp) transp = (uint8_t)((transp & 0xE000)>>8 | (transp & 0x0700)>>6 | (transp & 0x0018)>>3); _tft->pushImage(x, y, _dwidth, _dheight, _img8, (uint8_t)transp, (bool)true); } + else if (_bpp == 4) + { + _tft->pushImage(x, y, _dwidth, _dheight, _img4, (uint8_t)(transp & 0x0F), false, _colorMap); + } else _tft->pushImage(x, y, _dwidth, _dheight, _img8, 0, (bool)false); } +/*************************************************************************************** +** Function name: cmapPixel +** Description: Read the color map index of a pixel at defined coordinates +*************************************************************************************x*/ +uint8_t TFT_eSprite::cmapPixel(int32_t x, int32_t y) +{ + if ((x < 0) || (x >= _iwidth) || (y < 0) || (y >= _iheight) || !_created) return 0xFFFF; + + if (_bpp == 4) + { + uint16_t color; + if ((x & 0x01) == 0) + return ((_img4[((x+y*_iwidth)>>1)] & 0xF0) >> 4) & 0x0F; // even index = bits 7 .. 4 + else + return _img4[((x-1+y*_iwidth)>>1)] & 0x0F; // odd index = bits 3 .. 0. + } + return 0; +} + /*************************************************************************************** ** Function name: readPixel ** Description: Read 565 colour of a pixel at defined coordinates @@ -723,6 +799,16 @@ uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) return color; } + if (_bpp == 4) + { + uint16_t color; + if ((x & 0x01) == 0) + color = _colorMap[((_img4[((x+y*_iwidth)>>1)] & 0xF0) >> 4) & 0x0F ]; // even index = bits 7 .. 4 + else + color = _colorMap[_img4[((x-1+y*_iwidth)>>1)] & 0x0F]; // odd index = bits 3 .. 0. + return color; + } + if (_rotation == 1) { uint16_t tx = x; @@ -802,6 +888,13 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_ ys++; } } + else if (_bpp == 4) + { + // not supported. The image is unlikely to have the correct colors for the color map. + // we could implement a way to push a 4-bit image using the color map? + Serial.println("pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data) not implemented"); + return; + } else // 1bpp { @@ -900,6 +993,12 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const u } } + else if (_bpp == 4) + { + Serial.println("TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data) not implemented"); + return; + } + else // 1bpp { // Move coordinate rotation to support fn @@ -1007,6 +1106,17 @@ void TFT_eSprite::pushColor(uint32_t color) else if (_bpp == 8) _img8[_xptr + _yptr * _iwidth] = (uint8_t )((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + + else if (_bpp == 4) + { + uint8_t c = (uint8_t)color & 0x0F; + if ((_xptr & 0x01) == 0) { + _img4[(_xptr + _yptr * _iwidth)>>1] = ((c << 4) & 0xF0) | (_img4[(_xptr + _yptr * _iwidth)>>1] & 0x0F); // new color is in bits 7 .. 4 + } + else { + _img4[(_xptr - 1 + _yptr * _iwidth)>>1] = (_img4[(_xptr - 1 + _yptr * _iwidth)>>1] & 0xF0) | c; // new color is the low bits + } + } else drawPixel(_xptr, _yptr, color); @@ -1040,7 +1150,7 @@ void TFT_eSprite::pushColor(uint32_t color, uint16_t len) else if (_bpp == 8) pixelColor = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; - else pixelColor = (uint16_t) color; // for 1bpp + else pixelColor = (uint16_t) color; // for 1bpp or 4bpp while(len--) writeColor(pixelColor); } @@ -1060,6 +1170,15 @@ void TFT_eSprite::writeColor(uint16_t color) // Write 8 bit RGB 332 encoded colour to RAM else if (_bpp == 8) _img8[_xptr + _yptr * _iwidth] = (uint8_t) color; + else if (_bpp == 4) + { + uint8_t c = (uint8_t)color & 0x0F; + if ((_xptr & 0x01) == 0) + _img4[(_xptr + _yptr * _iwidth)>>1] = ((c << 4) & 0xF0) | (_img4[(_xptr + _yptr * _iwidth)>>1] & 0x0F); // new color is in bits 7 .. 4 + else + _img4[(_xptr - 1 + _yptr * _iwidth)>>1] = (_img4[(_xptr - 1 + _yptr * _iwidth)>>1] & 0xF0) | c; // new color is the low bits (x is odd) + } + else drawPixel(_xptr, _yptr, color); // Increment x @@ -1159,7 +1278,22 @@ void TFT_eSprite::scroll(int16_t dx, int16_t dy) fyp += iw; } } - else if (_bpp == 1) + else if (_bpp == 4) + { + // could optimize for scrolling by even # pixels using memove (later) + if (dx > 0) { tx += w; fx += w; } // Start from right edge + while (h--) + { // move pixels one by one + for (uint16_t xp = 0; xp < w; xp++) + { + if (dx <= 0) drawPixel(tx + xp, ty, cmapPixel(fx + xp, fy)); + if (dx > 0) drawPixel(tx - xp, ty, cmapPixel(fx - xp, fy)); + } + if (dy <= 0) { ty++; fy++; } + else { ty--; fy--; } + } + } + else if (_bpp == 1 ) { if (dx > 0) { tx += w; fx += w; } // Start from right edge while (h--) @@ -1173,7 +1307,7 @@ void TFT_eSprite::scroll(int16_t dx, int16_t dy) else { ty--; fy--; } } } - else return; // Not 1, 8 or 16 bpp + else return; // Not 1, 4, 8 or 16 bpp // Fill the gap left by the scrolling if (dx > 0) fillRect(_sx, _sy, dx, _sh, _scolor); @@ -1199,6 +1333,11 @@ void TFT_eSprite::fillSprite(uint32_t color) color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; memset(_img8, (uint8_t)color, _iwidth * _iheight); } + else if (_bpp == 4) + { + uint8_t c = ((color & 0x0F) | (((color & 0x0F) << 4) & 0xF0)); + memset(_img4, c, (_iwidth * _iheight) >> 1); + } else if (_bpp == 1) { if(color) memset(_img8, 0xFF, (_iwidth>>3) * _iheight + 1); @@ -1301,6 +1440,19 @@ void TFT_eSprite::drawPixel(int32_t x, int32_t y, uint32_t color) { _img8[x+y*_iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); } + else if (_bpp == 4) + { + uint8_t c = color & 0x0F; + int index = 0; + if ((x & 0x01) == 0) { + index = (x+y*_iwidth)>>1; + _img4[index] = (uint8_t)(((c << 4) & 0xF0) | (_img4[index] & 0x0F)); + } + else { + index = (x-1+y*_iwidth)>>1; + _img4[index] = (uint8_t)(c | (_img4[index] & 0xF0)); + } + } else // 1 bpp { if (_rotation == 1) @@ -1409,6 +1561,24 @@ void TFT_eSprite::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; while (h--) _img8[x + _iwidth * y++] = (uint8_t) color; } + else if (_bpp == 4) + { + if ((x & 0x01) == 0) + { + uint8_t c = (uint8_t) (color & 0xF) << 4; + while (h--) { + _img4[(x + _iwidth * y)>>1] = (uint8_t) (c | _img4[(x + _iwidth * y)>>1] & 0x0F); + y++; + } + } + else { + uint8_t c = (uint8_t)color & 0xF; + while (h--) { + _img4[(x - 1 + _iwidth * y)>>1] = (uint8_t) (c | _img4[(x - 1 + _iwidth * y)>>1] & 0xF0); // x is odd; new color goes into the low bits. + y++; + } + } + } else { while (h--) @@ -1445,8 +1615,28 @@ void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; memset(_img8+_iwidth * y + x, (uint8_t)color, w); } - else + else if (_bpp == 4) { + uint8_t c = (uint8_t)color & 0x0F; + uint8_t c2 = (c | ((c << 4) & 0xF0)); + if ((x & 0x01) == 1) + { + drawPixel(x, y, color); + x++; w--; + if (w < 1) + return; + } + + if (((w + x) & 0x01) == 1) + { + // handle the extra one at the other end + drawPixel(x + w - 1, y, color); + w--; + if (w < 1) return; + } + memset(_img4 + ((_iwidth * y + x) >> 1), c2, (w >> 1)); + } + else { while (w--) { drawPixel(x, y, color); @@ -1498,6 +1688,56 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t yp += _iwidth; } } + else if (_bpp == 4) + { + uint8_t c1 = (uint8_t)color & 0x0F; + uint8_t c2 = c1 | ((c1 << 4) & 0xF0); + if ((x & 0x01) == 0 && (w & 0x01) == 0) + { + yp = (yp >> 1); + while (h--) + { + memset(_img4 + yp, c2, (w>>1)); + yp += (_iwidth >> 1); + } + } + else if ((x & 0x01) == 0) + { + // same as above but you have a hangover on the right. + yp = (yp >> 1); + while (h--) + { + if (w > 1) + memset(_img4 + yp, c2, (w-1)>>1); + // handle the rightmost pixel by calling drawPixel + drawPixel(x+w-1, y+h, c1); + yp += (_iwidth >> 1); + } + } + else if ((w & 0x01) == 1) + { + yp = (yp + 1) >> 1; + while (h--) { + drawPixel(x, y+h-1, color & 0x0F); + if (w > 1) + memset(_img4 + (yp + ((x-1)>>1)), c2, (w-1)>>1); + // same as above but you have a hangover on the left instead + yp += (_iwidth >> 1); + } + } + else + { + yp = (yp + 1) >> 1; + while (h--) { + drawPixel(x, y+h-1, color & 0x0F); + drawPixel(x+w-1, y+h-1, color & 0x0F); + if (w > 2) + memset(_img4 + (yp + ((x-1)>>1)), c2, (w-2)>>1); + // maximal hacking, single pixels on left and right. + yp += (_iwidth >> 1); + } + } + } else { while (h--) diff --git a/Extensions/Sprite.h b/Extensions/Sprite.h index 4c4b65b..caf3733 100644 --- a/Extensions/Sprite.h +++ b/Extensions/Sprite.h @@ -32,11 +32,14 @@ class TFT_eSprite : public TFT_eSPI { // Returns a pointer to the Sprite frame buffer void* frameBuffer(int8_t f); - // Set or get the colour depth to 8 or 16 bits. Can be used to change depth an existing + // Set or get the colour depth to 4, 8 or 16 bits. Can be used to change depth an existing // sprite, but clears it to black, returns a new pointer if sprite is re-created. void* setColorDepth(int8_t b); int8_t getColorDepth(void); + // Set the colour map for a 4 bit depth sprite. Only the first 16 colours in the map are used. + void setColorMap(uint16_t *colorMap, int colors); + // Set foreground and background colours for 1 bit per pixel Sprite void setBitmapColor(uint16_t fg, uint16_t bg); @@ -100,7 +103,10 @@ class TFT_eSprite : public TFT_eSPI { // Read the colour of a pixel at x,y and return value in 565 format uint16_t readPixel(int32_t x0, int32_t y0); - // Write an image (colour bitmap) to the sprite + // return the color map index of the pixel at x,y (used when scrolling) + uint8_t cmapPixel(int32_t x, int32_t y); + + // Write an image (colour bitmap) to the sprite. Not implemented for _bpp == 4. void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, uint16_t *data); void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, const uint16_t *data); @@ -141,9 +147,12 @@ class TFT_eSprite : public TFT_eSPI { uint8_t _bpp; // bits per pixel (1, 8 or 16) uint16_t *_img; // pointer to 16 bit sprite uint8_t *_img8; // pointer to 8 bit sprite + uint8_t *_img4; // pointer to 4 bit sprite (uses color map) uint8_t *_img8_1; // pointer to frame 1 uint8_t *_img8_2; // pointer to frame 2 + uint16_t *_colorMap; // color map: 16 entries, used with 4 bit color map. + int16_t _xpivot; // x pivot point coordinate int16_t _ypivot; // y pivot point coordinate diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 8051fd4..2e3217d 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -296,15 +296,15 @@ void TFT_eSPI::init(uint8_t tc) #if defined (TFT_DC) && (TFT_DC >= 0) dcpinmask = (uint32_t) digitalPinToBitMask(TFT_DC); #endif - + #if defined (TFT_WR) && (TFT_WR >= 0) wrpinmask = (uint32_t) digitalPinToBitMask(TFT_WR); #endif - + #if defined (TFT_SCLK) && (TFT_SCLK >= 0) sclkpinmask = (uint32_t) digitalPinToBitMask(TFT_SCLK); #endif - + #if defined (TFT_SPI_OVERLAP) && defined (ESP8266) // Overlap mode SD0=MISO, SD1=MOSI, CLK=SCLK must use D3 as CS // pins(int8_t sck, int8_t miso, int8_t mosi, int8_t ss); @@ -372,7 +372,7 @@ void TFT_eSPI::init(uint8_t tc) delay(150); // Wait for reset to complete spi_begin(); - + tc = tc; // Supress warning // This loads the driver specific initialisation code <<<<<<<<<<<<<<<<<<<<< ADD NEW DRIVERS TO THE LIST HERE <<<<<<<<<<<<<<<<<<<<<<< @@ -599,7 +599,7 @@ uint8_t TFT_eSPI::readcommand8(uint8_t cmd_function, uint8_t index) uint8_t reg = 0; #ifdef TFT_PARALLEL_8_BIT - writecommand(cmd_function); // Sets DC and CS high + writecommand(cmd_function); // Sets DC and CS high busDir(dir_mask, INPUT); @@ -676,7 +676,7 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) readAddrWindow(x0, y0, 1, 1); // Sets CS low // Set masked pins D0- D7 to input - busDir(dir_mask, INPUT); + busDir(dir_mask, INPUT); // Dummy read to throw away don't care value readByte(); @@ -727,7 +727,7 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) // Dummy read to throw away don't care value tft_Read_8(); - + //#if !defined (ILI9488_DRIVER) // Read the 3 RGB bytes, colour is actually only in the top 6 bits of each byte @@ -940,7 +940,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *d if (x < 0) { dw += x; dx = -x; x = 0; } if (y < 0) { dh += y; dy = -y; y = 0; } - + if ((x + dw) > _width ) dw = _width - x; if ((y + dh) > _height) dh = _height - y; @@ -1073,7 +1073,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint1 if (x < 0) { dw += x; dx = -x; x = 0; } if (y < 0) { dh += y; dy = -y; y = 0; } - + if ((x + dw) > _width ) dw = _width - x; if ((y + dh) > _height) dh = _height - y; @@ -1128,10 +1128,11 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint1 /*************************************************************************************** ** Function name: pushImage -** Description: plot 8 bit image or sprite using a line buffer +** Description: plot 8 bit or 4 bit or 1 bit image or sprite using a line buffer ***************************************************************************************/ -void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *data, bool bpp8) +void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *data, bool bpp8, uint16_t *cmap) { + if ((x >= _width) || (y >= (int32_t)_height)) return; int32_t dx = 0; @@ -1141,7 +1142,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *da if (x < 0) { dw += x; dx = -x; x = 0; } if (y < 0) { dh += y; dy = -y; y = 0; } - + if ((x + dw) > _width ) dw = _width - x; if ((y + dh) > _height) dh = _height - y; @@ -1192,7 +1193,59 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *da data += w; } } - else { + else if (cmap != nullptr) + { + bool swap = _swapBytes; _swapBytes = true; + + w = (w+1) & 0xFFFE; // if this is a sprite, w will already be even; this does no harm. + bool splitFirst = (dx & 0x01) != 0; // split first means we have to push a single px from the left of the sprite / image + + if (splitFirst) { + data += ((dx - 1 + dy * w) >> 1); + } + else { + data += ((dx + dy * w) >> 1); + } + + while (dh--) { + uint32_t len = dw; + uint8_t * ptr = data; + uint16_t *linePtr = lineBuf; + uint8_t colors; // two colors in one byte + uint16_t index; + + if (splitFirst) { + colors = *ptr; + index = (colors & 0x0F); + *linePtr++ = cmap[index]; + len--; + ptr++; + } + + while (len--) + { + colors = *ptr; + index = ((colors & 0xF0) >> 4) & 0x0F; + *linePtr++ = cmap[index]; + + if (len--) + { + index = colors & 0x0F; + *linePtr++ = cmap[index]; + } else { + break; // nothing to do here + } + + ptr++; + } + + pushPixels(lineBuf, dw); + data += (w >> 1); + } + _swapBytes = swap; // Restore old value + } + else + { while (dh--) { w = (w+7) & 0xFFF8; @@ -1228,9 +1281,9 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *da /*************************************************************************************** ** Function name: pushImage -** Description: plot 8 or 1 bit image or sprite with a transparent colour +** Description: plot 8 or 4 or 1 bit image or sprite with a transparent colour ***************************************************************************************/ -void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *data, uint8_t transp, bool bpp8) +void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *data, uint8_t transp, bool bpp8, uint16_t *cmap) { if ((x >= _width) || (y >= _height)) return; @@ -1241,7 +1294,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *da if (x < 0) { dw += x; dx = -x; x = 0; } if (y < 0) { dh += y; dy = -y; y = 0; } - + if ((x + dw) > _width ) dw = _width - x; if ((y + dh) > _height) dh = _height - y; @@ -1311,6 +1364,97 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *da data += w; } } + else if (cmap != nullptr) // 4bpp with color map + { + bool swap = _swapBytes; _swapBytes = true; + + w = (w+1) & 0xFFFE; // here we try to recreate iwidth from dwidth. + bool splitFirst = ((dx & 0x01) != 0); + if (splitFirst) { + data += ((dx - 1 + dy * w) >> 1); + } + else { + data += ((dx + dy * w) >> 1); + } + + while (dh--) { + uint32_t len = dw; + uint8_t * ptr = data; + + int32_t px = x; + bool move = true; + uint16_t np = 0; + + uint8_t index; // index into cmap. + + if (splitFirst) { + index = (*ptr & 0x0F); // odd = bits 3 .. 0 + if (index != transp) { + move = false; setWindow(px, y, xe, ye); + lineBuf[np] = cmap[index]; + np++; + } + px++; ptr++; + len--; + } + + while (len--) + { + uint8_t color = *ptr; + + // find the actual color you care about. There will be two pixels here! + // but we may only want one at the end of the row + uint16_t index = ((color & 0xF0) >> 4) & 0x0F; // high bits are the even numbers + if (index != transp) { + if (move) { + move = false; setWindow(px, y, xe, ye); + } + lineBuf[np] = cmap[index]; + np++; // added a pixel + } + else { + move = true; + if (np) { + pushPixels(lineBuf, np); + np = 0; + } + } + px++; + + if (len--) + { + index = color & 0x0F; // the odd number is 3 .. 0 + if (index != transp) { + if (move) { + move = false; setWindow(px, y, xe, ye); + } + lineBuf[np] = cmap[index]; + np++; + } + else { + move = true; + if (np) { + pushPixels(lineBuf, np); + np = 0; + } + } + px++; + } + else { + break; // we are done with this row. + } + ptr++; // we only increment ptr once in the loop (deliberate) + } + + if (np) { + pushPixels(lineBuf, np); + np = 0; + } + data += (w>>1); + y++; + } + _swapBytes = swap; // Restore old value + } else { // 1 bit per pixel w = (w+7) & 0xFFF8; while (dh--) { @@ -1357,7 +1501,6 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *da spi_end(); } - /*************************************************************************************** ** Function name: setSwapBytes ** Description: Used by 16 bit pushImage() to swap byte order in colours @@ -1566,7 +1709,7 @@ void TFT_eSPI::fillCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color) drawFastHLine(x0 - x, y0 - r, 2 * x+1, color); } - inTransaction = false; + inTransaction = false; spi_end(); // Does nothing if Sprite class uses this function } @@ -1756,7 +1899,7 @@ void TFT_eSPI::drawRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t drawCircleHelper(x + w - r - 1, y + r , r, 2, color); drawCircleHelper(x + w - r - 1, y + h - r - 1, r, 4, color); drawCircleHelper(x + r , y + h - r - 1, r, 8, color); - + inTransaction = false; spi_end(); // Does nothing if Sprite class uses this function } @@ -1778,7 +1921,7 @@ void TFT_eSPI::fillRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t // draw four corners fillCircleHelper(x + r, y + h - r - 1, r, 1, w - r - r - 1, color); fillCircleHelper(x + r , y + r, r, 2, w - r - r - 1, color); - + inTransaction = false; spi_end(); // Does nothing if Sprite class uses this function } @@ -2396,7 +2539,7 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32 yo = pgm_read_byte(&glyph->yOffset); uint8_t xx, yy, bits=0, bit=0; int16_t xo16 = 0, yo16 = 0; - + if(size > 1) { xo16 = xo; yo16 = yo; @@ -2644,7 +2787,7 @@ void TFT_eSPI::pushColors(uint16_t *data, uint32_t len, bool swap) { spi_begin(); if (swap) {swap = _swapBytes; _swapBytes = true; } - + pushPixels(data, len); _swapBytes = swap; // Restore old value @@ -2736,7 +2879,7 @@ void TFT_eSPI::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) setWindow(x, y, x, y + h - 1); pushBlock(color, h); - + spi_end(); } @@ -2761,7 +2904,7 @@ void TFT_eSPI::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) setWindow(x, y, x + w - 1, y); pushBlock(color, w); - + spi_end(); } @@ -2774,7 +2917,7 @@ void TFT_eSPI::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t col { // Clipping if ((x >= _width) || (y >= _height)) return; - + if (x < 0) { w += x; x = 0; } if (y < 0) { h += y; y = 0; } @@ -3790,7 +3933,7 @@ void TFT_eSPI::setFreeFont(const GFXfont *f) glyph_ab = 0; glyph_bb = 0; uint16_t numChars = pgm_read_word(&gfxFont->last) - pgm_read_word(&gfxFont->first); - + // Find the biggest above and below baseline offsets for (uint8_t c = 0; c < numChars; c++) { GFXglyph *glyph1 = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]); @@ -3814,7 +3957,7 @@ void TFT_eSPI::setTextFont(uint8_t f) #else - + /*************************************************************************************** ** Function name: setFreeFont ** Descriptions: Sets the GFX free font to use diff --git a/TFT_eSPI.h b/TFT_eSPI.h index a7c254e..2ecca75 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -399,13 +399,14 @@ class TFT_eSPI : public Print { void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, uint16_t *data); void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, uint16_t *data, uint16_t transparent); + // These are used to render images stored in FLASH (PROGMEM) void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, const uint16_t *data, uint16_t transparent); void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, const uint16_t *data); - // These are used by pushSprite for 1 and 8 bit colours - void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_t *data, bool bpp8 = true); - void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_t *data, uint8_t transparent, bool bpp8 = true); + // These are used by pushSprite for 1, 4 and 8 bit colours (color map needed for 4 bit) + void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_t *data, bool bpp8 = true, uint16_t *cmap = nullptr); + void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_t *data, uint8_t transparent, bool bpp8 = true, uint16_t *cmap = nullptr); // Write a solid block of a single colour void pushBlock(uint16_t color, uint32_t len); diff --git a/examples/Sprite/Sprite_colorMap/Sprite_colorMap.ino b/examples/Sprite/Sprite_colorMap/Sprite_colorMap.ino new file mode 100644 index 0000000..f3f1e07 --- /dev/null +++ b/examples/Sprite/Sprite_colorMap/Sprite_colorMap.ino @@ -0,0 +1,208 @@ +/* + + Sketch to show how a Sprite is created, how to draw pixels + and text within the Sprite and then push the Sprite onto + the display screen. + + Example for library: + https://github.com/Bodmer/TFT_eSPI + + A Sprite is notionally an invisible graphics screen that is + kept in the processors RAM. Graphics can be drawn into the + Sprite just as it can be drawn directly to the screen. Once + the Sprite is completed it can be plotted onto the screen in + any position. If there is sufficient RAM then the Sprite can + be the same size as the screen and used as a frame buffer. + + A 16 bit Sprite occupies (2 * width * height) bytes in RAM. + + On a ESP8266 Sprite sizes up to 126 x 160 can be accomodated, + this size requires 40kBytes of RAM for a 16 bit color depth. + + When 8 bit color depth sprites are created they occupy + (width * height) bytes in RAM, so larger sprites can be + created, or the RAM required is halved. + +*/ + +// Set delay after plotting the sprite +#define DELAY 1000 + +// Width and height of sprite +#define WIDTH 128 +#define HEIGHT 128 + +#include // Include the graphics library (this includes the sprite functions) + +TFT_eSPI tft = TFT_eSPI(); // Declare object "tft" + +TFT_eSprite spr = TFT_eSprite(&tft); // Declare Sprite object "spr" with pointer to "tft" object + + +void setup() +{ + Serial.begin(250000); + Serial.println(); + + delay(500); + + // Initialise the TFT registers + tft.init(); + + spr.setColorDepth(4); + + // Create a sprite of defined size + spr.createSprite(WIDTH, HEIGHT); + + // Clear the TFT screen to blue + tft.fillScreen(TFT_BLUE); +} + +void loop(void) +{ + // Fill the whole sprite with color 5 (Sprite is in memory so not visible yet) + spr.fillSprite(10); + + // create a color map with known colors + uint16_t cmap[16]; + + + cmap[0] = TFT_BLACK; + cmap[1] = TFT_NAVY; + cmap[2] = TFT_DARKGREEN; + cmap[3] = TFT_DARKCYAN; + cmap[4] = TFT_MAROON; + cmap[5] = TFT_PURPLE; + cmap[6] = TFT_OLIVE; + cmap[7] = TFT_LIGHTGREY; + cmap[8] = TFT_DARKGREY; + cmap[9] = TFT_BLUE; + cmap[10] = TFT_GREEN; + cmap[11] = TFT_CYAN; + cmap[12] = TFT_RED; + cmap[13] = TFT_MAGENTA; + cmap[14] = TFT_YELLOW; + cmap[15] = TFT_WHITE; + + spr.setColorMap(cmap, 16); + + spr.pushSprite(-40, -40); + spr.pushSprite(tft.width() / 2 - WIDTH / 2, tft.height() / 2 - HEIGHT / 2, 10); + spr.pushSprite(tft.width() - WIDTH + 40, tft.height() - HEIGHT + 40); + + // Number of pixels to draw + uint16_t n = 100; + // Draw 100 random color pixels at random positions in sprite + while (n--) + { + uint16_t color = random(0x10); // Returns color 0 - 0x0F + int16_t x = random(WIDTH); // Random x coordinate + int16_t y = random(HEIGHT); // Random y coordinate + spr.drawPixel( x, y, color); // Draw pixel in sprite + } + + spr.pushSprite(-40, -40); + spr.pushSprite(tft.width() / 2 - WIDTH / 2, tft.height() / 2 - HEIGHT / 2); + spr.pushSprite(tft.width() - WIDTH + 40, tft.height() - HEIGHT + 40); + + delay(DELAY); + + // Draw some lines + spr.drawLine(1, 0, WIDTH, HEIGHT-1, 6); + spr.drawLine(0, 0, WIDTH, HEIGHT, 6); + spr.drawLine(0, 1, WIDTH-1, HEIGHT, 2); + spr.drawLine(0, HEIGHT-1, WIDTH-1, 0, 2); + spr.drawLine(0, HEIGHT, WIDTH, 0, 3); + spr.drawLine(1, HEIGHT, WIDTH, 1, 3); + spr.drawLine(4, 0, 4, HEIGHT-1, 11); + spr.drawLine(0, 16, WIDTH-1, 16, 13); + + // draw some circles with random colors. + spr.drawCircle(20, 60, 10, 6); + spr.drawCircle(80, 60, 15, 7); + spr.drawCircle(50, 108, 5, 9); + spr.drawCircle(45, 86, 3, 8); + spr.fillCircle(102, 56, 4, 11); + + spr.fillRect(28, 32, 40, 4, 5); + //spr.fillRect(27, 42, 40, 14, 6); + //spr.fillRect(33, 55, 3, 4, 7); + //spr.fillRect(34, 32, 7, 4, 8); + + // Draw some text with Middle Centre datum + spr.setTextDatum(MC_DATUM); + spr.drawString("Sprite", WIDTH / 2, HEIGHT / 2, 1); + + // Now push the sprite to the TFT at position 0,0 on screen + spr.pushSprite(-40, -40); + spr.pushSprite(tft.width() / 2 - WIDTH / 2, tft.height() / 2 - HEIGHT / 2); + spr.pushSprite(tft.width() - WIDTH + 40, tft.height() - HEIGHT + 40); + + delay(DELAY * 4); + + // create a new color map and use it instead + + for (auto i = 0; i < 16; i++) + { + cmap[i] = random(0x10000); + } + + spr.setColorMap(cmap, 16); + // Now push the sprite to the TFT at position 0,0 on screen + + spr.pushSprite(-40, -40); + spr.pushSprite(tft.width() / 2 - WIDTH / 2, tft.height() / 2 - HEIGHT / 2); + spr.pushSprite(tft.width() - WIDTH + 40, tft.height() - HEIGHT + 40); + + delay(DELAY); + + // Fill TFT screen with blue + tft.fillScreen(TFT_BLUE); + + // Draw a blue rectangle in sprite so when we move it 1 pixel it does not leave a trail + // on the blue screen background + cmap[14] = TFT_BLUE; + spr.setColorMap(cmap, 16); + + spr.drawRect(0, 0, WIDTH, HEIGHT, 14); + + int x = tft.width() / 2 - WIDTH / 2; + int y = tft.height() / 2 - HEIGHT / 2; + + uint32_t updateTime = 0; // time for next update + + while (true) + { + // Random movement direction + int dx = 1; if (random(2)) dx = -1; + int dy = 1; if (random(2)) dy = -1; + + // Pull it back onto screen if it wanders off + if (x < -WIDTH/2) dx = 1; + if (x >= tft.width()-WIDTH/2) dx = -1; + if (y < -HEIGHT/2) dy = 1; + if (y >= tft.height()-HEIGHT/2) dy = -1; + + // Draw it 50 time, moving in random direct or staying still + n = 50; + int wait = random (50); + while (n) + { + if (updateTime <= millis()) + { + // Use time delay so sprite does not move fast when not all on screen + updateTime = millis() + wait; + + // Push the sprite to the TFT screen + spr.pushSprite(x, y); + + // Change coord for next loop + x += dx; + y += dy; + n--; + yield(); // Stop watchdog reset + } + } + } // Infinite while, will not exit! +} + diff --git a/examples/Sprite/Sprite_scroll_cmap/Sprite_scroll_cmap.ino b/examples/Sprite/Sprite_scroll_cmap/Sprite_scroll_cmap.ino new file mode 100644 index 0000000..9ac7262 --- /dev/null +++ b/examples/Sprite/Sprite_scroll_cmap/Sprite_scroll_cmap.ino @@ -0,0 +1,141 @@ +/* + Sketch to show scrolling of the graphics in sprites. + Scrolling in this way moves the pixels in a defined rectangle + within the Sprite. By defalt the whole sprite is scrolled. + The gap left by scrolling is filled with a defined colour. + + Example for library: + https://github.com/Bodmer/TFT_eSPI + + A Sprite is notionally an invisible graphics screen that is + kept in the processors RAM. Graphics can be drawn into the + Sprite just as it can be drawn directly to the screen. Once + the Sprite is completed it can be plotted onto the screen in + any position. If there is sufficient RAM then the Sprite can + be the same size as the screen and used as a frame buffer. + + A 16 bit Sprite occupies (2 * width * height) bytes in RAM. + + An 8 bit Sprite occupies (width * height) bytes in RAM. + +*/ + +#include + +TFT_eSPI tft = TFT_eSPI(); + +TFT_eSprite graph1 = TFT_eSprite(&tft); // Sprite object graph1 + +TFT_eSprite stext1 = TFT_eSprite(&tft); // Sprite object stext1 + +TFT_eSprite stext2 = TFT_eSprite(&tft); // Sprite object stext2 + +int graphVal = 1; +int delta = 1; +int grid = 0; +int tcount = 0; + +uint16_t cmap[16]; + +//========================================================================================== +void setup() { + Serial.begin(250000); + tft.init(); + tft.fillScreen(TFT_BLACK); + + cmap[0] = TFT_BLACK; + cmap[1] = TFT_ORANGE; + cmap[2] = TFT_DARKGREEN; + cmap[3] = TFT_DARKCYAN; + cmap[4] = TFT_MAROON; + cmap[5] = TFT_PURPLE; + cmap[6] = TFT_OLIVE; + cmap[7] = TFT_DARKGREY; + cmap[8] = TFT_ORANGE; + cmap[9] = TFT_BLUE; + cmap[10] = TFT_GREEN; + cmap[11] = TFT_CYAN; + cmap[12] = TFT_RED; + cmap[13] = TFT_NAVY; + cmap[14] = TFT_YELLOW; + cmap[15] = TFT_WHITE; + + // Create a sprite for the graph + graph1.setColorDepth(4); + graph1.createSprite(128, 61); + graph1.setColorMap(cmap, 16); + graph1.fillSprite(9); // Note: Sprite is filled with black when created + + // The scroll area is set to the full sprite size upon creation of the sprite + // but we can change that by defining a smaller area using "setScrollRect()"if needed + // parameters are x,y,w,h,color as in drawRect(), the color fills the gap left by scrolling + //graph1.setScrollRect(64, 0, 64, 61, TFT_DARKGREY); // Try this line to change the graph scroll area + + // Create a sprite for the scrolling numbers + stext1.setColorDepth(4); + stext1.createSprite(32, 64); + stext1.setColorMap(cmap, 16); + stext1.fillSprite(9); // Fill sprite with blue + stext1.setScrollRect(0, 0, 32, 64, 9); // here we set scroll gap fill color to blue + stext1.setTextColor(15); // White text, no background + stext1.setTextDatum(BR_DATUM); // Bottom right coordinate datum + + // Create a sprite for Hello World + stext2.setColorDepth(4); + stext2.createSprite(80, 16); + stext2.setColorMap(cmap, 16); + stext2.fillSprite(7); + stext2.setScrollRect(0, 0, 40, 16, 7); // Scroll the "Hello" in the first 40 pixels + stext2.setTextColor(15); // White text, no background +} + +//========================================================================================== +void loop() { + // Draw point in graph1 sprite at far right edge (this will scroll left later) + graph1.drawFastVLine(127,60-graphVal,2,14); // draw 2 pixel point on graph + + // Draw number in stext1 sprite at 31,63 (bottom right datum set) + stext1.drawNumber(graphVal, 31, 63, 2); // plot value in font 2 + + // Push the sprites onto the TFT at specied coordinates + graph1.pushSprite(0, 0); + stext1.pushSprite(0, 64); + stext2.pushSprite(40, 70); + + // Change the value to plot + graphVal+=delta; + + // If the value reaches a limit, then change delta of value + if (graphVal >= 60) delta = -1; // ramp down value + else if (graphVal <= 1) delta = +1; // ramp up value + + delay(50); // wait so things do not scroll too fast + + // Now scroll the sprites scroll(dt, dy) where: + // dx is pixels to scroll, left = negative value, right = positive value + // dy is pixels to scroll, up = negative value, down = positive value + graph1.scroll(-1, 0); // scroll graph 1 pixel left, 0 up/down + stext1.scroll(0,-16); // scroll stext 0 pixels left/right, 16 up + stext2.scroll(1); // scroll stext 1 pixel right, up/down default is 0 + + // Draw the grid on far right edge of sprite as graph has now moved 1 pixel left + grid++; + if (grid >= 10) + { // Draw a vertical line if we have scrolled 10 times (10 pixels) + grid = 0; + graph1.drawFastVLine(127, 0, 61, 13); // draw line on graph + } + else + { // Otherwise draw points spaced 10 pixels for the horizontal grid lines + for (int p = 0; p <= 60; p += 10) graph1.drawPixel(127, p, 13); + } + + tcount--; + if (tcount <=0) + { // If we have scrolled 40 pixels the redraw text + tcount = 40; + stext2.drawString("Hello World", 6, 0, 2); // draw at 6,0 in sprite, font 2 + } + +} // Loop back and do it all again +//========================================================================================== diff --git a/examples/Sprite/Transparent_Sprite_Demo_Colormap/Transparent_Sprite_Demo_Colormap.ino b/examples/Sprite/Transparent_Sprite_Demo_Colormap/Transparent_Sprite_Demo_Colormap.ino new file mode 100644 index 0000000..c11f655 --- /dev/null +++ b/examples/Sprite/Transparent_Sprite_Demo_Colormap/Transparent_Sprite_Demo_Colormap.ino @@ -0,0 +1,168 @@ +/* + Sketch to show creation of a sprite with a transparent + background, then plot it on the TFT. + + Example for library: + https://github.com/Bodmer/TFT_eSPI + + A Sprite is notionally an invisible graphics screen that is + kept in the processors RAM. Graphics can be drawn into the + Sprite just as it can be drawn directly to the screen. Once + the Sprite is completed it can be plotted onto the screen in + any position. If there is sufficient RAM then the Sprite can + be the same size as the screen and used as a frame buffer. + + A 16 bit Sprite occupies (2 * width * height) bytes in RAM. + + On a ESP8266 Sprite sizes up to 126 x 160 can be accomodated, + this size requires 40kBytes of RAM for a 16 bit colour depth. + + When 8 bit colour depth sprites are created they occupy + (width * height) bytes in RAM, so larger sprites can be + created, or the RAM required is halved. +*/ + +#include // Include the graphics library (this includes the sprite functions) + +TFT_eSPI tft = TFT_eSPI(); // Create object "tft" + +TFT_eSprite img = TFT_eSprite(&tft); // Create Sprite object "img" with pointer to "tft" object + // the pointer is used by pushSprite() to push it onto the TFT + +TFT_eSprite img2 = TFT_eSprite(&tft); + +void setup(void) { + Serial.begin(250000); + + tft.init(); + + tft.setRotation(0); +} + +void loop() { + + tft.fillScreen(TFT_NAVY); + + img.setColorDepth(4); + + // Draw 10 sprites containing a "transparent" colour + for (int i = 0; i < 10; i++) + { + int x = random(240-70); + int y = random(320-80); + int c = random(0x0F); // Random colour (4 bit index into color map). Leave 15 for transparent. + drawStar(x, y, c); // note: not random; should be c + } + + delay(2000); + + uint32_t dt = millis(); + + // Now go bananas and draw 500 nore + for (int i = 0; i < 500; i++) + { + int x = random(240-70); + int y = random(320-80); + int c = random(0x10); // Random colour + drawStar(x, y, c); + yield(); // Stop watchdog reset + } + + // Show time in milliseconds to draw and then push 1 sprite to TFT screen + numberBox( 10, 10, (millis()-dt)/500.0 ); + delay(2000); + +} + +// ######################################################################### +// Create sprite, plot graphics in it, plot to screen, then delete sprite +// ######################################################################### +void drawStar(int x, int y, int star_color) +{ + // Create an 8 bit sprite 70x 80 pixels (uses 5600 bytes of RAM) + img.setColorDepth(4); + img.createSprite(70, 80); + + uint16_t cmap[16]; + + cmap[0] = TFT_BLACK; + cmap[1] = TFT_ORANGE; + cmap[2] = TFT_DARKGREEN; + cmap[3] = TFT_DARKCYAN; + cmap[4] = TFT_MAROON; + cmap[5] = TFT_PURPLE; + cmap[6] = TFT_OLIVE; + cmap[7] = TFT_LIGHTGREY; + cmap[8] = TFT_DARKGREY; + cmap[9] = TFT_BLUE; + cmap[10] = TFT_GREEN; + cmap[11] = TFT_CYAN; + cmap[12] = TFT_RED; + cmap[13] = TFT_MAGENTA; + cmap[14] = TFT_YELLOW; + cmap[15] = TFT_WHITE; // this one will be transparent. + + img.setColorMap(cmap, 16); + + // Fill Sprite with a "transparent" colour + // TFT_TRANSPARENT is already defined for convenience + // We could also fill with any colour as "transparent" and later specify that + // same colour when we push the Sprite onto the screen. + img.fillSprite(15); + + // Draw 2 triangles to create a filled in star + img.fillTriangle(35, 0, 0,59, 69,59, star_color); + img.fillTriangle(35,79, 0,20, 69,20, star_color); + + // Punch a star shaped hole in the middle with a smaller transparent star + // this one damages on pixel in the second triangle + img.fillTriangle(35, 7, 6,56, 63,56, 15); + img.fillTriangle(35,73, 6,24, 63,24, 15); + + // Push sprite to TFT screen at coordinate x,y (top left corner) + // Specify what colour from the map is to be treated as transparent. + img.pushSprite(x, y, 15); + + // Delete it to free memory + img.deleteSprite(); +} + +// ######################################################################### +// Draw a number in a rounded rectangle with some transparent pixels +// ######################################################################### +void numberBox(int x, int y, float num ) +{ + + // Size of sprite + #define IWIDTH 80 + #define IHEIGHT 35 + + // Create a 8 bit sprite 80 pixels wide, 35 high (2800 bytes of RAM needed) + img.setColorDepth(8); + img.createSprite(IWIDTH, IHEIGHT); + + // Fill it with black (this will be the transparent colour this time) + img.fillSprite(TFT_BLACK); + + // Draw a background for the numbers + img.fillRoundRect( 0, 0, 80, 35, 15, TFT_RED); + img.drawRoundRect( 0, 0, 80, 35, 15, TFT_WHITE); + + // Set the font parameters + img.setTextSize(1); // Font size scaling is x1 + img.setTextColor(TFT_WHITE); // White text, no background colour + + // Set text coordinate datum to middle right + img.setTextDatum(MR_DATUM); + + // Draw the number to 3 decimal places at 70,20 in font 4 + img.drawFloat(num, 3, 70, 20, 1); + + // Push sprite to TFT screen CGRAM at coordinate x,y (top left corner) + // All black pixels will not be drawn hence will show as "transparent" + img.pushSprite(x, y, TFT_BLACK); + + // Delete sprite to free up the RAM + img.deleteSprite(); +} +