Unverified Commit b9543728 authored by Bodmer's avatar Bodmer Committed by GitHub

Merge pull request #528 from kamorris/add_color_maps

Add Sprite 4 bit color depth option with a defined palette of 16 colors. Add new Sprite examples.
parents 04eacf56 7fd29d50
......@@ -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,33 @@ void* TFT_eSprite::callocSprite(int16_t w, int16_t h, uint8_t frames)
return ptr8;
}
/***************************************************************************************
** Function name: createPalette
** Description: Set a palette for a 4-bit per pixel sprite
*************************************************************************************x*/
void TFT_eSprite::createPalette(uint16_t colorMap[], int colors)
{
if (_colorMap != nullptr)
{
free(_colorMap);
}
if (colorMap == nullptr)
{
return; // do nothing other than clear the existing map
}
// 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 +210,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 +230,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
......@@ -222,6 +268,28 @@ void TFT_eSprite::setBitmapColor(uint16_t c, uint16_t b)
_tft->bitmap_bg = b;
}
/***************************************************************************************
** Function name: setPaletteColor
** Description: Set the palette color at the given index
***************************************************************************************/
void TFT_eSprite::setPaletteColor(uint8_t index, uint16_t color)
{
if (_colorMap == nullptr || index > 15)
return; // out of bounds
_colorMap[index] = color;
}
/***************************************************************************************
** Function name: getPaletteColor
** Description: Return the palette color at index, or 0 (black) on error.
***************************************************************************************/
uint16_t TFT_eSprite::getPaletteColor(uint8_t index)
{
if (_colorMap == nullptr || index > 15)
return 0;
return _colorMap[index];
}
/***************************************************************************************
** Function name: deleteSprite
......@@ -231,6 +299,11 @@ void TFT_eSprite::deleteSprite(void)
{
if (!_created ) return;
if (_colorMap != nullptr)
{
free(_colorMap);
}
free(_img8_1);
_created = false;
......@@ -667,6 +740,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 +772,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: readPixelValue
** Description: Read the color map index of a pixel at defined coordinates
*************************************************************************************x*/
uint8_t TFT_eSprite::readPixelValue(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 +826,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 +915,15 @@ 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?
#ifdef TFT_eSPI_DEBUG
Serial.println("pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data) not implemented");
#endif
return;
}
else // 1bpp
{
......@@ -900,6 +1022,14 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const u
}
}
else if (_bpp == 4)
{
#ifdef TFT_eSPI_DEBUG
Serial.println("TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data) not implemented");
#endif
return;
}
else // 1bpp
{
// Move coordinate rotation to support fn
......@@ -1008,6 +1138,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);
// Increment x
......@@ -1040,7 +1181,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 +1201,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 +1309,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, readPixelValue(fx + xp, fy));
if (dx > 0) drawPixel(tx - xp, ty, readPixelValue(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 +1338,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 +1364,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 +1471,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 +1592,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 +1646,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 +1719,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--)
......
......@@ -32,11 +32,20 @@ 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 palette for a 4 bit depth sprite. Only the first 16 colours in the map are used.
void createPalette(uint16_t *palette, int colors = 16);
// Set a single palette index to the given color
void setPaletteColor(uint8_t index, uint16_t color);
// Get the color at the given palette index
uint16_t getPaletteColor(uint8_t index);
// Set foreground and background colours for 1 bit per pixel Sprite
void setBitmapColor(uint16_t fg, uint16_t bg);
......@@ -100,7 +109,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 readPixelValue(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 +153,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
......
......@@ -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;
......@@ -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 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;
......@@ -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
......
......@@ -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);
......
/*
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.createPalette(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.createPalette(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.createPalette(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!
}
/*
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.createPalette(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.createPalette(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.createPalette(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
//==========================================================================================
/*
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.createPalette(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, 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();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment