Add ability to plot Sprites with a "transparent" colour

Can now specify a colour that will not be plotted so some part of the
Sprite are "transparent" and the TFT backforund shows through. New
example added for demonstration.
This commit is contained in:
Bodmer
2017-12-03 02:29:39 +00:00
parent 9d2c7f21fc
commit d2104f6d85
3 changed files with 327 additions and 23 deletions

View File

@@ -387,7 +387,7 @@ void TFT_eSPI::writedata(uint8_t c)
** Function name: readcommand8 (for ILI9341 Interface II i.e. IM [3:0] = "1101") ** 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 ** 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(); spi_begin();
index = 0x10 + (index & 0x0F); 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") ** 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 ** 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; uint32_t reg = 0;
reg |= (readcommand8(cmd_function, index + 0) << 8); 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") ** 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 ** 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; 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") ** Function name: read rectangle (for SPI Interface II i.e. IM [3:0] = "1101")
** Description: Read 565 pixel colours from a defined area ** 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; 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") ** Function name: push rectangle (for SPI Interface II i.e. IM [3:0] = "1101")
** Description: push 565 pixel colours into a defined area ** 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; 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 ** Function name: push sprite
** Description: plot 16 bit sprite in a defined area with clipping ** 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; 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(); 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 ** Function name: push sprite
** Description: plot 8 bit sprite with clipping using a line buffer ** 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; 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") ** Function name: read rectangle (for SPI Interface II i.e. IM [3:0] = "1101")
** Description: Read RGB pixel colours from a defined area ** 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 // 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(); spi_begin();
@@ -4301,7 +4430,7 @@ void TFT_eSprite::deleteSprite(void)
/*************************************************************************************** /***************************************************************************************
** Function name: pushSprite ** Function name: pushSprite
** Description: Delete the sprite to free up memory (RAM) ** Description: Push the sprite to the TFT at x, y
*************************************************************************************x*/ *************************************************************************************x*/
void TFT_eSprite::pushSprite(int32_t x, int32_t y) 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 ** Function name: readPixel
** Description: Read 565 colour of a pixel at defined coordinates ** 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 // Use memset if possible as it is super fast
if(( (uint8_t)color == (uint8_t)(color>>8) ) && _bpp16) if(( (uint8_t)color == (uint8_t)(color>>8) ) && _bpp16)
memset(_img, (uint8_t)color, _iwidth * _iheight * 2); 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); 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) if (_bpp16)
{ {
color = (color >> 8) | (color << 8); 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 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 ((y + h) > _iheight) h = _iheight - y;
if ((w < 1) || (h < 1)) return; if ((w < 1) || (h < 1)) return;
int32_t yp = _iwidth * y;
if (_bpp16) if (_bpp16)
{ {
color = (color >> 8) | (color << 8); color = (color >> 8) | (color << 8);
while (h--) while (h--)
{ {
int32_t ix = x, iw = w; uint32_t ix = x, iw = w;
while (iw--) _img[_iwidth * y + ix++] = (uint16_t) color; while (iw--) _img[yp + ix++] = (uint16_t) color;
y++; yp += _iwidth;
} }
} }
else 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; color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3;
while (h--) while (h--)
{ {
//int32_t ix = x, iw = w; memset(_img8 + yp + x, (uint8_t)color, w);
//while (iw--) _img8[_iwidth * y + ix++] = (uint8_t) color; yp += _iwidth;
memset(_img8 + _iwidth * y + x, (uint8_t)color, w);
y++;
} }
} }
} }

View File

@@ -246,10 +246,14 @@
#define TFT_MAGENTA 0xF81F /* 255, 0, 255 */ #define TFT_MAGENTA 0xF81F /* 255, 0, 255 */
#define TFT_YELLOW 0xFFE0 /* 255, 255, 0 */ #define TFT_YELLOW 0xFFE0 /* 255, 255, 0 */
#define TFT_WHITE 0xFFFF /* 255, 255, 255 */ #define TFT_WHITE 0xFFFF /* 255, 255, 255 */
#define TFT_ORANGE 0xFD20 /* 255, 165, 0 */ #define TFT_ORANGE 0xFDA0 /* 255, 180, 0 */
#define TFT_GREENYELLOW 0xAFE5 /* 173, 255, 47 */ #define TFT_GREENYELLOW 0xB7E0 /* 180, 255, 0 */
#define TFT_PINK 0xF81F #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 // Swap any type
template <typename T> static inline void template <typename T> 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 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, 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, 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 // 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 // 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 // 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 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);
void pushSprite(int32_t x, int32_t y, uint16_t transparent);
int16_t drawChar(unsigned int uniCode, int x, int y, int font), int16_t drawChar(unsigned int uniCode, int x, int y, int font),
drawChar(unsigned int uniCode, int x, int y); drawChar(unsigned int uniCode, int x, int y);
@@ -607,11 +615,11 @@ class TFT_eSprite : public TFT_eSPI {
uint16_t *_img; uint16_t *_img;
uint8_t *_img8; uint8_t *_img8;
bool _created; bool _created, _bpp16;
int32_t _icursor_x, _icursor_y, _xs, _ys, _xe, _ye, _xptr, _yptr; int32_t _icursor_x, _icursor_y, _xs, _ys, _xe, _ye, _xptr, _yptr;
int32_t _iwidth, _iheight, _bpp16; int32_t _iwidth, _iheight;
}; };

View File

@@ -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 <TFT_eSPI.h> // 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();
}