added color maps and examples

This commit is contained in:
kamorris
2020-01-24 22:07:45 -08:00
parent 04eacf56c9
commit e019b6dcf6
7 changed files with 947 additions and 37 deletions

View File

@@ -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--)

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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 <TFT_eSPI.h> // 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!
}

View File

@@ -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.h>
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
//==========================================================================================

View File

@@ -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 <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
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();
}