diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 8be3a37..8091383 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -387,7 +387,7 @@ void TFT_eSPI::writedata(uint8_t c) ** Function name: readcommand8 (for ILI9341 Interface II i.e. IM [3:0] = "1101") ** Description: Read a 8 bit data value from an indexed command register ***************************************************************************************/ - uint8_t TFT_eSPI::readcommand8(uint8_t cmd_function, uint8_t index) +uint8_t TFT_eSPI::readcommand8(uint8_t cmd_function, uint8_t index) { spi_begin(); index = 0x10 + (index & 0x0F); @@ -415,7 +415,7 @@ void TFT_eSPI::writedata(uint8_t c) ** Function name: readcommand16 (for ILI9341 Interface II i.e. IM [3:0] = "1101") ** Description: Read a 16 bit data value from an indexed command register ***************************************************************************************/ - uint16_t TFT_eSPI::readcommand16(uint8_t cmd_function, uint8_t index) +uint16_t TFT_eSPI::readcommand16(uint8_t cmd_function, uint8_t index) { uint32_t reg = 0; reg |= (readcommand8(cmd_function, index + 0) << 8); @@ -429,7 +429,7 @@ void TFT_eSPI::writedata(uint8_t c) ** Function name: readcommand32 (for ILI9341 Interface II i.e. IM [3:0] = "1101") ** Description: Read a 32 bit data value from an indexed command register ***************************************************************************************/ - uint32_t TFT_eSPI::readcommand32(uint8_t cmd_function, uint8_t index) +uint32_t TFT_eSPI::readcommand32(uint8_t cmd_function, uint8_t index) { uint32_t reg; @@ -472,7 +472,7 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) ** Function name: read rectangle (for SPI Interface II i.e. IM [3:0] = "1101") ** Description: Read 565 pixel colours from a defined area ***************************************************************************************/ - void TFT_eSPI::readRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) +void TFT_eSPI::readRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) { if ((x > _width) || (y > _height) || (w == 0) || (h == 0)) return; @@ -510,7 +510,7 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) ** Function name: push rectangle (for SPI Interface II i.e. IM [3:0] = "1101") ** Description: push 565 pixel colours into a defined area ***************************************************************************************/ - void TFT_eSPI::pushRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) +void TFT_eSPI::pushRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) { if ((x >= _width) || (y >= _height) || (w == 0) || (h == 0)) return; @@ -538,7 +538,7 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) ** Function name: push sprite ** Description: plot 16 bit sprite in a defined area with clipping ***************************************************************************************/ - void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t *data) +void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t *data) { if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; @@ -586,12 +586,68 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) spi_end(); } +/*************************************************************************************** +** Function name: push sprite +** Description: plot 16 bit sprite with 1 colour being transparent +***************************************************************************************/ +void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t *data, uint16_t transp) +{ + + if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; + + int32_t dx = 0; + int32_t dy = 0; + int32_t dw = w; + int32_t dh = h; + + if (x < 0) { dw += x; dx = -x; x = 0; } + if (y < 0) { dh += y; dy = -y; y = 0; } + + if ((x + w) > _width ) dw = _width - x; + if ((y + h) > _height) dh = _height - y; + + if (dw < 1 || dh < 1) return; + + spi_begin(); + + data += dx + dy * w; + + int32_t xe = x + dw - 1, ye = y + dh - 1; + + while (dh--) + { + int32_t len = dw; + uint16_t* ptr = data; + int32_t px = x; + boolean move = true; + + while (len--) + { + if (transp != *ptr) + { + if (move) { move = false; setAddrWindow(px, y, xe, ye); } + SPI.write16(*ptr>>8 | *ptr <<8); + } + else move = true; + px++; + ptr++; + } + + y++; + data += w; + } + + CS_H; + + spi_end(); +} + /*************************************************************************************** ** Function name: push sprite ** Description: plot 8 bit sprite with clipping using a line buffer ***************************************************************************************/ - void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t *data) +void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t *data) { if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; @@ -658,12 +714,85 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) } +/*************************************************************************************** +** Function name: push sprite +** Description: plot 8 bit sprite with 1 colour being transparent +***************************************************************************************/ +void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t *data, uint8_t transp) +{ + if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; + + int32_t dx = 0; + int32_t dy = 0; + int32_t dw = w; + int32_t dh = h; + + if (x < 0) { dw += x; dx = -x; x = 0; } + if (y < 0) { dh += y; dy = -y; y = 0; } + + if ((x + w) > _width ) dw = _width - x; + if ((y + h) > _height) dh = _height - y; + + if (dw < 1 || dh < 1) return; + + spi_begin(); + + data += dx + dy * w; + + int32_t xe = x + dw - 1, ye = y + dh - 1; + + uint8_t blue[] = {0, 11, 21, 31}; // blue 2 to 5 bit colour lookup table + + _lastColor = -1; // Set to illegal value + + // Used to store last shifted colour + uint16_t color565 = 0; + + while (dh--) + { + int32_t len = dw; + uint8_t* ptr = data; //<<<<<<<<< changed to 8 bit + int32_t px = x; + boolean move = true; + + while (len--) + { + if (transp != *ptr) + { + if (move) { move = false; setAddrWindow(px, y, xe, ye); } + uint32_t color = *ptr; + + // Shifts are slow so check if colour has changed first + if (color != _lastColor) { + // =====Green===== ===============Red============== + color565 = (color & 0x1C)<<6 | (color & 0xC0)<<5 | (color & 0xE0)<<8 + // =====Green===== =======Blue====== + | (color & 0x1C)<<3 | blue[color & 0x03]; + _lastColor = color; + } + SPI.write16(color565); + } + else move = true; + px++; + ptr++; + } + + y++; + data += w; + } + + CS_H; + + spi_end(); +} + + /*************************************************************************************** ** Function name: read rectangle (for SPI Interface II i.e. IM [3:0] = "1101") ** Description: Read RGB pixel colours from a defined area ***************************************************************************************/ // If w and h are 1, then 1 pixel is read, *data array size must be 3 bytes per pixel - void TFT_eSPI::readRectRGB(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_t *data) +void TFT_eSPI::readRectRGB(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_t *data) { spi_begin(); @@ -4301,7 +4430,7 @@ void TFT_eSprite::deleteSprite(void) /*************************************************************************************** ** Function name: pushSprite -** Description: Delete the sprite to free up memory (RAM) +** Description: Push the sprite to the TFT at x, y *************************************************************************************x*/ void TFT_eSprite::pushSprite(int32_t x, int32_t y) { @@ -4312,6 +4441,23 @@ void TFT_eSprite::pushSprite(int32_t x, int32_t y) } +/*************************************************************************************** +** Function name: pushSprite +** Description: Push the sprite to the TFT at x, y with transparent colour +*************************************************************************************x*/ +void TFT_eSprite::pushSprite(int32_t x, int32_t y, uint16_t transp) +{ + if (!_created ) return; + + if (_bpp16) _tft->pushSprite(x, y, _iwidth, _iheight, _img, transp ); + else + { + transp = (uint8_t)((transp & 0xE000)>>8 | (transp & 0x0700)>>6 | (transp & 0x0018)>>3); + _tft->pushSprite(x, y, _iwidth, _iheight, _img8, (uint8_t) transp); + } +} + + /*************************************************************************************** ** Function name: readPixel ** Description: Read 565 colour of a pixel at defined coordinates @@ -4493,7 +4639,11 @@ void TFT_eSprite::fillSprite(uint32_t color) // Use memset if possible as it is super fast if(( (uint8_t)color == (uint8_t)(color>>8) ) && _bpp16) memset(_img, (uint8_t)color, _iwidth * _iheight * 2); - else if (!_bpp16) memset(_img8, (uint8_t)color, _iwidth * _iheight); + else if (!_bpp16) + { + color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; + memset(_img8, (uint8_t)color, _iwidth * _iheight); + } else fillRect(0, 0, _iwidth, _iheight, color); } @@ -4770,7 +4920,8 @@ void TFT_eSprite::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) if (_bpp16) { color = (color >> 8) | (color << 8); - while (h--) _img[x + _iwidth * y++] = (uint16_t) color; + int32_t yp = x + _iwidth * y; + while (h--) {_img[yp] = (uint16_t) color; yp += _iwidth;} } else { @@ -4823,14 +4974,17 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t if ((y + h) > _iheight) h = _iheight - y; if ((w < 1) || (h < 1)) return; + int32_t yp = _iwidth * y; + if (_bpp16) { color = (color >> 8) | (color << 8); + while (h--) { - int32_t ix = x, iw = w; - while (iw--) _img[_iwidth * y + ix++] = (uint16_t) color; - y++; + uint32_t ix = x, iw = w; + while (iw--) _img[yp + ix++] = (uint16_t) color; + yp += _iwidth; } } else @@ -4838,10 +4992,8 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; while (h--) { - //int32_t ix = x, iw = w; - //while (iw--) _img8[_iwidth * y + ix++] = (uint8_t) color; - memset(_img8 + _iwidth * y + x, (uint8_t)color, w); - y++; + memset(_img8 + yp + x, (uint8_t)color, w); + yp += _iwidth; } } } diff --git a/TFT_eSPI.h b/TFT_eSPI.h index d38bf6e..ef4ecf8 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -246,10 +246,14 @@ #define TFT_MAGENTA 0xF81F /* 255, 0, 255 */ #define TFT_YELLOW 0xFFE0 /* 255, 255, 0 */ #define TFT_WHITE 0xFFFF /* 255, 255, 255 */ -#define TFT_ORANGE 0xFD20 /* 255, 165, 0 */ -#define TFT_GREENYELLOW 0xAFE5 /* 173, 255, 47 */ -#define TFT_PINK 0xF81F +#define TFT_ORANGE 0xFDA0 /* 255, 180, 0 */ +#define TFT_GREENYELLOW 0xB7E0 /* 180, 255, 0 */ +#define TFT_PINK 0xFC9F +// Next is a special 16 bit colour value that encodes to 8 bits +// and will then decode back to the same 16 bit value. +// Convenient for 8 bit and 16 bit transparent sprites. +#define TFT_TRANSPARENT 0x0120 // Swap any type template static inline void @@ -406,6 +410,9 @@ class TFT_eSPI : public Print { void pushRect(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, uint16_t *data); void pushSprite(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint16_t *data); void pushSprite(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint8_t *data); + void pushSprite(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint16_t *data, uint16_t transparent); + void pushSprite(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint8_t *data, uint8_t transparent); + // This next function has been used successfully to dump the TFT screen to a PC for documentation purposes // It reads a screen area and returns the RGB 8 bit colour values of each pixel // Set w and h to 1 to read 1 pixel's colour. The data buffer must be at least w * h * 3 bytes @@ -590,6 +597,7 @@ class TFT_eSprite : public TFT_eSPI { void pushBitmap(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, uint16_t *data); void pushSprite(int32_t x, int32_t y); + void pushSprite(int32_t x, int32_t y, uint16_t transparent); int16_t drawChar(unsigned int uniCode, int x, int y, int font), drawChar(unsigned int uniCode, int x, int y); @@ -607,11 +615,11 @@ class TFT_eSprite : public TFT_eSPI { uint16_t *_img; uint8_t *_img8; - bool _created; + bool _created, _bpp16; int32_t _icursor_x, _icursor_y, _xs, _ys, _xe, _ye, _xptr, _yptr; - int32_t _iwidth, _iheight, _bpp16; + int32_t _iwidth, _iheight; }; diff --git a/examples/Sprite/Transparent_Sprite_Demo/Transparent_Sprite_Demo.ino b/examples/Sprite/Transparent_Sprite_Demo/Transparent_Sprite_Demo.ino new file mode 100644 index 0000000..c8039e7 --- /dev/null +++ b/examples/Sprite/Transparent_Sprite_Demo/Transparent_Sprite_Demo.ino @@ -0,0 +1,144 @@ +/* + 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 + +void setup(void) { + Serial.begin(250000); + + tft.init(); + + tft.setRotation(0); +} + +void loop() { + + tft.fillScreen(TFT_NAVY); + + // 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(0x10000); // Random colour + drawStar(x, y, 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(0x10000); // 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(8); + img.createSprite(70, 80); + + // 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(TFT_TRANSPARENT); + + // 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 + img.fillTriangle(35, 7, 6,56, 63,56, TFT_TRANSPARENT); + img.fillTriangle(35,73, 6,24, 63,24, TFT_TRANSPARENT); + + // Push sprite to TFT screen CGRAM at coordinate x,y (top left corner) + // Specify what colour is to be treated as transparent. + img.pushSprite(x, y, TFT_TRANSPARENT); + + // 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, 4); + + // 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(); +} +