mirror of
https://github.com/Bodmer/TFT_eSPI.git
synced 2025-08-07 06:34:44 +02:00
Add new animated dial example
See Sprite "Animated_dial" example.
This commit is contained in:
@@ -378,14 +378,6 @@ bool TFT_eSprite::pushRotated(int16_t angle, int32_t transp)
|
||||
{
|
||||
if ( !_created ) return false;
|
||||
|
||||
// Trig values for the rotation
|
||||
float radAngle = -angle * 0.0174532925; // Convert degrees to radians
|
||||
float sinraf = sin(radAngle);
|
||||
float cosraf = cos(radAngle);
|
||||
|
||||
int32_t sinra = round(sinraf * (1<<FP_SCALE));
|
||||
int32_t cosra = round(cosraf * (1<<FP_SCALE));
|
||||
|
||||
// Bounding box parameters
|
||||
int16_t min_x;
|
||||
int16_t min_y;
|
||||
@@ -393,28 +385,7 @@ bool TFT_eSprite::pushRotated(int16_t angle, int32_t transp)
|
||||
int16_t max_y;
|
||||
|
||||
// Get the bounding box of this rotated source Sprite relative to Sprite pivot
|
||||
getRotatedBounds(sinraf, cosraf, width(), height(), _xpivot, _ypivot, &min_x, &min_y, &max_x, &max_y);
|
||||
|
||||
// Move bounding box so source Sprite pivot coincides with TFT pivot
|
||||
min_x += _tft->_xpivot;
|
||||
max_x += _tft->_xpivot;
|
||||
min_y += _tft->_ypivot;
|
||||
max_y += _tft->_ypivot;
|
||||
|
||||
// Test only to show bounding box on TFT
|
||||
// _tft->drawRect(min_x, min_y, max_x - min_x + 1, max_y - min_y + 1, TFT_GREEN);
|
||||
|
||||
// Return if bounding box is outside of TFT area
|
||||
if (min_x > _tft->width()) return true;
|
||||
if (min_y > _tft->height()) return true;
|
||||
if (max_x < 0) return true;
|
||||
if (max_y < 0) return true;
|
||||
|
||||
// Clip bounding box to be within TFT area
|
||||
if (min_x < 0) min_x = 0;
|
||||
if (min_y < 0) min_y = 0;
|
||||
if (max_x > _tft->width()) max_x = _tft->width();
|
||||
if (max_y > _tft->height()) max_y = _tft->height();
|
||||
if ( !getRotatedBounds(angle, &min_x, &min_y, &max_x, &max_y) ) return false;
|
||||
|
||||
uint16_t sline_buffer[max_x - min_x + 1];
|
||||
|
||||
@@ -424,15 +395,15 @@ bool TFT_eSprite::pushRotated(int16_t angle, int32_t transp)
|
||||
uint32_t ye = _iheight << FP_SCALE;
|
||||
uint32_t tpcolor = transp; // convert to unsigned
|
||||
|
||||
_tft->startWrite(); // ESP32: avoid transaction overhead for every tft pixel
|
||||
_tft->startWrite(); // Avoid transaction overhead for every tft pixel
|
||||
|
||||
// Scan destination bounding box and fetch transformed pixels from source Sprite
|
||||
for (int32_t y = min_y; y <= max_y; y++, yt++) {
|
||||
int32_t x = min_x;
|
||||
uint32_t xs = (cosra * xt - (sinra * yt - (_xpivot << FP_SCALE)) + (1 << (FP_SCALE - 1)));
|
||||
uint32_t ys = (sinra * xt + (cosra * yt + (_ypivot << FP_SCALE)) + (1 << (FP_SCALE - 1)));
|
||||
uint32_t xs = (_cosra * xt - (_sinra * yt - (_xpivot << FP_SCALE)) + (1 << (FP_SCALE - 1)));
|
||||
uint32_t ys = (_sinra * xt + (_cosra * yt + (_ypivot << FP_SCALE)) + (1 << (FP_SCALE - 1)));
|
||||
|
||||
while ((xs >= xe || ys >= ye) && x < max_x) { x++; xs += cosra; ys += sinra; }
|
||||
while ((xs >= xe || ys >= ye) && x < max_x) { x++; xs += _cosra; ys += _sinra; }
|
||||
if (x == max_x) continue;
|
||||
|
||||
uint32_t pixel_count = 0;
|
||||
@@ -444,104 +415,24 @@ bool TFT_eSprite::pushRotated(int16_t angle, int32_t transp)
|
||||
else rp = readPixel(xp, yp);
|
||||
if (tpcolor == rp) {
|
||||
if (pixel_count) {
|
||||
_tft->pushImage(x - pixel_count, y, pixel_count, 1, sline_buffer);
|
||||
// TFT window is already clipped, so this is faster than pushImage()
|
||||
_tft->setWindow(x - pixel_count, y, x, y);
|
||||
_tft->pushPixels(sline_buffer, pixel_count);
|
||||
pixel_count = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
sline_buffer[pixel_count++] = rp>>8 | rp<<8;
|
||||
}
|
||||
} while (++x < max_x && (xs += cosra) < xe && (ys += sinra) < ye);
|
||||
if (pixel_count) _tft->pushImage(x - pixel_count, y, pixel_count, 1, sline_buffer);
|
||||
}
|
||||
|
||||
_tft->endWrite(); // ESP32: end transaction
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: pushRotatedHP - Higher Precision version of pushRotated
|
||||
** Description: Push rotated Sprite to TFT screen
|
||||
*************************************************************************************x*/
|
||||
bool TFT_eSprite::pushRotatedHP(int16_t angle, int32_t transp)
|
||||
{
|
||||
if ( !_created ) return false;
|
||||
|
||||
// Trig values for the rotation
|
||||
float radAngle = -angle * 0.0174532925; // Convert degrees to radians
|
||||
float sinra = sin(radAngle);
|
||||
float cosra = cos(radAngle);
|
||||
|
||||
// Bounding box parameters
|
||||
int16_t min_x;
|
||||
int16_t min_y;
|
||||
int16_t max_x;
|
||||
int16_t max_y;
|
||||
|
||||
// Get the bounding box of this rotated source Sprite relative to Sprite pivot
|
||||
getRotatedBounds(sinra, cosra, width(), height(), _xpivot, _ypivot, &min_x, &min_y, &max_x, &max_y);
|
||||
|
||||
// Move bounding box so source Sprite pivot coincides with TFT pivot
|
||||
min_x += _tft->_xpivot;
|
||||
max_x += _tft->_xpivot;
|
||||
min_y += _tft->_ypivot;
|
||||
max_y += _tft->_ypivot;
|
||||
|
||||
// Test only to show bounding box on TFT
|
||||
// _tft->drawRect(min_x, min_y, max_x - min_x + 1, max_y - min_y + 1, TFT_GREEN);
|
||||
|
||||
// Return if bounding box is outside of TFT area
|
||||
if (min_x > _tft->width()) return true;
|
||||
if (min_y > _tft->height()) return true;
|
||||
if (max_x < 0) return true;
|
||||
if (max_y < 0) return true;
|
||||
|
||||
// Clip bounding box to be within TFT area
|
||||
if (min_x < 0) min_x = 0;
|
||||
if (min_y < 0) min_y = 0;
|
||||
if (max_x > _tft->width()) max_x = _tft->width();
|
||||
if (max_y > _tft->height()) max_y = _tft->height();
|
||||
|
||||
uint16_t sline_buffer[max_y - min_y + 1];
|
||||
|
||||
_tft->startWrite(); // ESP32: avoid transaction overhead for every tft pixel
|
||||
|
||||
// Scan destination bounding box and fetch transformed pixels from source Sprite
|
||||
for (int32_t x = min_x; x <= max_x; x++) {
|
||||
int32_t xt = x - _tft->_xpivot;
|
||||
float cxt = cosra * xt + _xpivot;
|
||||
float sxt = sinra * xt + _ypivot;
|
||||
bool column_drawn = false;
|
||||
uint32_t pixel_count = 0;
|
||||
int32_t y_start = 0;
|
||||
for (int32_t y = min_y; y <= max_y; y++) {
|
||||
int32_t yt = y - _tft->_ypivot;
|
||||
int32_t xs = (int32_t)round(cxt - sinra * yt);
|
||||
// Do not calculate ys unless xs is in bounds
|
||||
if (xs >= 0 && xs < width())
|
||||
{
|
||||
int32_t ys = (int32_t)round(sxt + cosra * yt);
|
||||
// Check if ys is in bounds
|
||||
if (ys >= 0 && ys < height()) {
|
||||
int32_t rp = readPixel(xs, ys);
|
||||
if (transp >= 0 ) {
|
||||
if (rp != transp) _tft->drawPixel(x, y, rp);
|
||||
}
|
||||
else {
|
||||
if (!column_drawn) y_start = y;
|
||||
sline_buffer[pixel_count++] = rp>>8 | rp<<8;
|
||||
}
|
||||
column_drawn = true;
|
||||
}
|
||||
}
|
||||
else if (column_drawn) y = max_y; // Skip remaining column pixels
|
||||
} while (++x < max_x && (xs += _cosra) < xe && (ys += _sinra) < ye);
|
||||
if (pixel_count) {
|
||||
// TFT window is already clipped, so this is faster than pushImage()
|
||||
_tft->setWindow(x - pixel_count, y, x, y);
|
||||
_tft->pushPixels(sline_buffer, pixel_count);
|
||||
}
|
||||
if (pixel_count) _tft->pushImage(x, y_start, 1, pixel_count, sline_buffer);
|
||||
}
|
||||
|
||||
_tft->endWrite(); // ESP32: end transaction
|
||||
_tft->endWrite(); // End transaction
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -556,14 +447,6 @@ bool TFT_eSprite::pushRotated(TFT_eSprite *spr, int16_t angle, int32_t transp)
|
||||
if ( !_created ) return false; // Check this Sprite is created
|
||||
if ( !spr->_created ) return false; // Ckeck destination Sprite is created
|
||||
|
||||
// Trig values for the rotation
|
||||
float radAngle = -angle * 0.0174532925; // Convert degrees to radians
|
||||
float sinraf = sin(radAngle);
|
||||
float cosraf = cos(radAngle);
|
||||
|
||||
int32_t sinra = round(sinraf * (1<<FP_SCALE));
|
||||
int32_t cosra = round(cosraf * (1<<FP_SCALE));
|
||||
|
||||
// Bounding box parameters
|
||||
int16_t min_x;
|
||||
int16_t min_y;
|
||||
@@ -571,29 +454,7 @@ bool TFT_eSprite::pushRotated(TFT_eSprite *spr, int16_t angle, int32_t transp)
|
||||
int16_t max_y;
|
||||
|
||||
// Get the bounding box of this rotated source Sprite
|
||||
getRotatedBounds(sinraf, cosraf, width(), height(), _xpivot, _ypivot, &min_x, &min_y, &max_x, &max_y);
|
||||
|
||||
// Move bounding box so source Sprite pivot coincides with destination Sprite pivot
|
||||
min_x += spr->_xpivot;
|
||||
max_x += spr->_xpivot;
|
||||
min_y += spr->_ypivot;
|
||||
max_y += spr->_ypivot;
|
||||
|
||||
// Test only to show bounding box
|
||||
// spr->fillSprite(TFT_BLACK);
|
||||
// spr->drawRect(min_x, min_y, max_x - min_x + 1, max_y - min_y + 1, TFT_GREEN);
|
||||
|
||||
// Return if bounding box is completely outside of destination Sprite
|
||||
if (min_x > spr->width()) return true;
|
||||
if (min_y > spr->height()) return true;
|
||||
if (max_x < 0) return true;
|
||||
if (max_y < 0) return true;
|
||||
|
||||
// Clip bounding box if it is partially within destination Sprite
|
||||
if (min_x < 0) min_x = 0;
|
||||
if (min_y < 0) min_y = 0;
|
||||
if (max_x > spr->width()) max_x = spr->width();
|
||||
if (max_y > spr->height()) max_y = spr->height();
|
||||
if ( !getRotatedBounds(spr, angle, &min_x, &min_y, &max_x, &max_y) ) return false;
|
||||
|
||||
uint16_t sline_buffer[max_x - min_x + 1];
|
||||
|
||||
@@ -609,10 +470,10 @@ bool TFT_eSprite::pushRotated(TFT_eSprite *spr, int16_t angle, int32_t transp)
|
||||
// Scan destination bounding box and fetch transformed pixels from source Sprite
|
||||
for (int32_t y = min_y; y <= max_y; y++, yt++) {
|
||||
int32_t x = min_x;
|
||||
uint32_t xs = (cosra * xt - (sinra * yt - (_xpivot << FP_SCALE)) + (1 << (FP_SCALE - 1)));
|
||||
uint32_t ys = (sinra * xt + (cosra * yt + (_ypivot << FP_SCALE)) + (1 << (FP_SCALE - 1)));
|
||||
uint32_t xs = (_cosra * xt - (_sinra * yt - (_xpivot << FP_SCALE)) + (1 << (FP_SCALE - 1)));
|
||||
uint32_t ys = (_sinra * xt + (_cosra * yt + (_ypivot << FP_SCALE)) + (1 << (FP_SCALE - 1)));
|
||||
|
||||
while ((xs >= xe || ys >= ye) && x < max_x) { x++; xs += cosra; ys += sinra; }
|
||||
while ((xs >= xe || ys >= ye) && x < max_x) { x++; xs += _cosra; ys += _sinra; }
|
||||
if (x == max_x) continue;
|
||||
|
||||
uint32_t pixel_count = 0;
|
||||
@@ -631,7 +492,7 @@ bool TFT_eSprite::pushRotated(TFT_eSprite *spr, int16_t angle, int32_t transp)
|
||||
else {
|
||||
sline_buffer[pixel_count++] = rp;
|
||||
}
|
||||
} while (++x < max_x && (xs += cosra) < xe && (ys += sinra) < ye);
|
||||
} while (++x < max_x && (xs += _cosra) < xe && (ys += _sinra) < ye);
|
||||
if (pixel_count) spr->pushImage(x - pixel_count, y, pixel_count, 1, sline_buffer);
|
||||
}
|
||||
spr->setSwapBytes(oldSwapBytes);
|
||||
@@ -640,79 +501,68 @@ bool TFT_eSprite::pushRotated(TFT_eSprite *spr, int16_t angle, int32_t transp)
|
||||
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: pushRotated - Higher Precision version of pushRotated
|
||||
** Description: Push a rotated copy of the Sprite to another Sprite
|
||||
** Function name: getRotatedBounds
|
||||
** Description: Get TFT bounding box of a rotated Sprite wrt pivot
|
||||
*************************************************************************************x*/
|
||||
bool TFT_eSprite::pushRotatedHP(TFT_eSprite *spr, int16_t angle, int32_t transp)
|
||||
bool TFT_eSprite::getRotatedBounds(int16_t angle, int16_t *min_x, int16_t *min_y,
|
||||
int16_t *max_x, int16_t *max_y)
|
||||
{
|
||||
if ( !_created ) return false; // Check this Sprite is created
|
||||
if ( !spr->_created ) return false; // Ckeck destination Sprite is created
|
||||
// Get the bounding box of this rotated source Sprite relative to Sprite pivot
|
||||
getRotatedBounds(angle, width(), height(), _xpivot, _ypivot, min_x, min_y, max_x, max_y);
|
||||
|
||||
// Trig values for the rotation
|
||||
float radAngle = -angle * 0.0174532925; // Convert degrees to radians
|
||||
float sinra = sin(radAngle);
|
||||
float cosra = cos(radAngle);
|
||||
// Move bounding box so source Sprite pivot coincides with TFT pivot
|
||||
*min_x += _tft->_xpivot;
|
||||
*max_x += _tft->_xpivot;
|
||||
*min_y += _tft->_ypivot;
|
||||
*max_y += _tft->_ypivot;
|
||||
|
||||
// Bounding box parameters
|
||||
int16_t min_x;
|
||||
int16_t min_y;
|
||||
int16_t max_x;
|
||||
int16_t max_y;
|
||||
// Return if bounding box is outside of TFT area
|
||||
if (*min_x > _tft->width()) return false;
|
||||
if (*min_y > _tft->height()) return false;
|
||||
if (*max_x < 0) return false;
|
||||
if (*max_y < 0) return false;
|
||||
|
||||
// Get the bounding box of this rotated source Sprite
|
||||
getRotatedBounds(sinra, cosra, width(), height(), _xpivot, _ypivot, &min_x, &min_y, &max_x, &max_y);
|
||||
// Clip bounding box to be within TFT area
|
||||
if (*min_x < 0) *min_x = 0;
|
||||
if (*min_y < 0) *min_y = 0;
|
||||
if (*max_x > _tft->width()) *max_x = _tft->width();
|
||||
if (*max_y > _tft->height()) *max_y = _tft->height();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: getRotatedBounds
|
||||
** Description: Get destination Sprite bounding box of a rotated Sprite wrt pivot
|
||||
*************************************************************************************x*/
|
||||
bool TFT_eSprite::getRotatedBounds(TFT_eSprite *spr, int16_t angle, int16_t *min_x, int16_t *min_y,
|
||||
int16_t *max_x, int16_t *max_y)
|
||||
{
|
||||
// Get the bounding box of this rotated source Sprite relative to Sprite pivot
|
||||
getRotatedBounds(angle, width(), height(), _xpivot, _ypivot, min_x, min_y, max_x, max_y);
|
||||
|
||||
// Move bounding box so source Sprite pivot coincides with destination Sprite pivot
|
||||
min_x += spr->_xpivot;
|
||||
max_x += spr->_xpivot;
|
||||
min_y += spr->_ypivot;
|
||||
max_y += spr->_ypivot;
|
||||
*min_x += spr->_xpivot;
|
||||
*max_x += spr->_xpivot;
|
||||
*min_y += spr->_ypivot;
|
||||
*max_y += spr->_ypivot;
|
||||
|
||||
// Test only to show bounding box
|
||||
// spr->fillSprite(TFT_BLACK);
|
||||
// spr->drawRect(min_x, min_y, max_x - min_x + 1, max_y - min_y + 1, TFT_GREEN);
|
||||
|
||||
// Return if bounding box is completely outside of destination Sprite
|
||||
if (min_x > spr->width()) return true;
|
||||
if (min_y > spr->height()) return true;
|
||||
if (max_x < 0) return true;
|
||||
if (max_y < 0) return true;
|
||||
if (*min_x > spr->width()) return true;
|
||||
if (*min_y > spr->height()) return true;
|
||||
if (*max_x < 0) return true;
|
||||
if (*max_y < 0) return true;
|
||||
|
||||
// Clip bounding box if it is partially within destination Sprite
|
||||
if (min_x < 0) min_x = 0;
|
||||
if (min_y < 0) min_y = 0;
|
||||
if (max_x > spr->width()) max_x = spr->width();
|
||||
if (max_y > spr->height()) max_y = spr->height();
|
||||
|
||||
// Scan destination bounding box and fetch transformed pixels from source Sprite
|
||||
for (int32_t x = min_x; x <= max_x; x++)
|
||||
{
|
||||
int32_t xt = x - spr->_xpivot;
|
||||
float cxt = cosra * xt + _xpivot;
|
||||
float sxt = sinra * xt + _ypivot;
|
||||
bool column_drawn = false;
|
||||
for (int32_t y = min_y; y <= max_y; y++)
|
||||
{
|
||||
int32_t yt = y - spr->_ypivot;
|
||||
int32_t xs = (int32_t)round(cxt - sinra * yt);
|
||||
// Do not calculate ys unless xs is in bounds
|
||||
if (xs >= 0 && xs < width())
|
||||
{
|
||||
int32_t ys = (int32_t)round(sxt + cosra * yt);
|
||||
// Check if ys is in bounds
|
||||
if (ys >= 0 && ys < height())
|
||||
{
|
||||
uint32_t rp;
|
||||
// Can avoid bounds check overhead for reading 16bpp
|
||||
if (_bpp == 16) {rp = _img[xs + ys * _iwidth]; rp = rp>>8 | rp<<8; }
|
||||
else rp = readPixel(xs, ys);
|
||||
if (rp != transp) spr->drawPixel(x, y, rp);
|
||||
column_drawn = true;
|
||||
}
|
||||
}
|
||||
else if (column_drawn) y = max_y; // Skip the remaining pixels below the Sprite
|
||||
}
|
||||
}
|
||||
if (*min_x < 0) min_x = 0;
|
||||
if (*min_y < 0) min_y = 0;
|
||||
if (*max_x > spr->width()) *max_x = spr->width();
|
||||
if (*max_y > spr->height()) *max_y = spr->height();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -722,9 +572,14 @@ bool TFT_eSprite::pushRotatedHP(TFT_eSprite *spr, int16_t angle, int32_t transp)
|
||||
** Function name: rotatedBounds
|
||||
** Description: Get bounding box of a rotated Sprite wrt pivot
|
||||
*************************************************************************************x*/
|
||||
void TFT_eSprite::getRotatedBounds(float sina, float cosa, int16_t w, int16_t h, int16_t xp, int16_t yp,
|
||||
void TFT_eSprite::getRotatedBounds(int16_t angle, int16_t w, int16_t h, int16_t xp, int16_t yp,
|
||||
int16_t *min_x, int16_t *min_y, int16_t *max_x, int16_t *max_y)
|
||||
{
|
||||
// Trig values for the rotation
|
||||
float radAngle = -angle * 0.0174532925; // Convert degrees to radians
|
||||
float sina = sin(radAngle);
|
||||
float cosa = cos(radAngle);
|
||||
|
||||
w -= xp; // w is now right edge coordinate relative to xp
|
||||
h -= yp; // h is now bottom edge coordinate relative to yp
|
||||
|
||||
@@ -762,6 +617,8 @@ void TFT_eSprite::getRotatedBounds(float sina, float cosa, int16_t w, int16_t h,
|
||||
if (y2 > *max_y) *max_y = y2+2;
|
||||
if (y3 > *max_y) *max_y = y3+2;
|
||||
|
||||
_sinra = round(sina * (1<<FP_SCALE));
|
||||
_cosra = round(cosa * (1<<FP_SCALE));
|
||||
}
|
||||
|
||||
|
||||
@@ -835,7 +692,7 @@ uint8_t TFT_eSprite::readPixelValue(int32_t x, int32_t y)
|
||||
else
|
||||
return _img4[((x-1+y*_iwidth)>>1)] & 0x0F; // odd index = bits 3 .. 0.
|
||||
}
|
||||
return 0;
|
||||
return readPixel(x, y);
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
|
@@ -5,10 +5,6 @@
|
||||
// graphics are written to the Sprite rather than the TFT.
|
||||
***************************************************************************************/
|
||||
|
||||
// pushRotated support - Bitwise truncation of fixed point integer value V scaled by S
|
||||
//#define truncateFP(V,S) ((V + (V < 0 ? -1<<(S-1) : 0))>>S)
|
||||
#define truncateFP(V,S) ((V + (V < 0 ? -1<<(S-1) : 1<<(S-1)))>>S)
|
||||
|
||||
class TFT_eSprite : public TFT_eSPI {
|
||||
|
||||
public:
|
||||
@@ -94,18 +90,22 @@ class TFT_eSprite : public TFT_eSPI {
|
||||
|
||||
// Push a rotated copy of Sprite to TFT with optional transparent colour
|
||||
bool pushRotated(int16_t angle, int32_t transp = -1); // Using fixed point maths
|
||||
bool pushRotatedHP(int16_t angle, int32_t transp = -1); // Using higher precision floating point maths
|
||||
// Push a rotated copy of Sprite to another different Sprite with optional transparent colour
|
||||
bool pushRotated(TFT_eSprite *spr, int16_t angle, int32_t transp = -1); // Using fixed point maths
|
||||
bool pushRotatedHP(TFT_eSprite *spr, int16_t angle, int32_t transp = -1); // Using higher precision floating point maths
|
||||
// Set and get the pivot point for this Sprite
|
||||
|
||||
// Set and get the pivot point for this Sprite
|
||||
void setPivot(int16_t x, int16_t y);
|
||||
int16_t getPivotX(void),
|
||||
getPivotY(void);
|
||||
|
||||
// Get the bounding box for a rotated copy of this Sprite
|
||||
void getRotatedBounds(float sina, float cosa, int16_t w, int16_t h, int16_t xp, int16_t yp,
|
||||
int16_t *min_x, int16_t *min_y, int16_t *max_x, int16_t *max_y);
|
||||
// Get the TFT bounding box for a rotated copy of this Sprite
|
||||
bool getRotatedBounds(int16_t angle, int16_t *min_x, int16_t *min_y, int16_t *max_x, int16_t *max_y);
|
||||
// Get the destination Sprite bounding box for a rotated copy of this Sprite
|
||||
bool getRotatedBounds(TFT_eSprite *spr, int16_t angle, int16_t *min_x, int16_t *min_y,
|
||||
int16_t *max_x, int16_t *max_y);
|
||||
// Bounding box support function
|
||||
void getRotatedBounds(int16_t angle, int16_t w, int16_t h, int16_t xp, int16_t yp,
|
||||
int16_t *min_x, int16_t *min_y, int16_t *max_x, int16_t *max_y);
|
||||
|
||||
// Read the colour of a pixel at x,y and return value in 565 format
|
||||
uint16_t readPixel(int32_t x0, int32_t y0);
|
||||
@@ -162,6 +162,8 @@ class TFT_eSprite : public TFT_eSPI {
|
||||
|
||||
int16_t _xpivot; // x pivot point coordinate
|
||||
int16_t _ypivot; // y pivot point coordinate
|
||||
int32_t _sinra;
|
||||
int32_t _cosra;
|
||||
|
||||
bool _created; // A Sprite has been created and memory reserved
|
||||
bool _gFont = false;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/***************************************************
|
||||
Arduino TFT graphics library targeted at ESP8266
|
||||
and ESP32 based boards.
|
||||
Arduino TFT graphics library targeted at 32 bit
|
||||
processors such as ESP32, ESP8266 and STM32.
|
||||
|
||||
This is a standalone library that contains the
|
||||
hardware driver, the graphics functions and the
|
||||
@@ -10,7 +10,7 @@
|
||||
size.
|
||||
|
||||
Created by Bodmer 2/12/16
|
||||
Last update by Bodmer 27/12/19
|
||||
Last update by Bodmer 20/03/20
|
||||
****************************************************/
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "Processors/TFT_eSPI_ESP32.c"
|
||||
#elif defined (ESP8266)
|
||||
#include "Processors/TFT_eSPI_ESP8266.c"
|
||||
#elif defined (STM32) //(STM32F7xx) //(_VARIANT_ARDUINO_STM32_) stm32_def.h
|
||||
#elif defined (STM32) // (_VARIANT_ARDUINO_STM32_) stm32_def.h
|
||||
#include "Processors/TFT_eSPI_STM32.c"
|
||||
#else
|
||||
#include "Processors/TFT_eSPI_Generic.c"
|
||||
|
@@ -16,7 +16,7 @@
|
||||
#ifndef _TFT_eSPIH_
|
||||
#define _TFT_eSPIH_
|
||||
|
||||
#define TFT_ESPI_VERSION "2.1.6"
|
||||
#define TFT_ESPI_VERSION "2.1.7"
|
||||
|
||||
/***************************************************************************************
|
||||
** Section 1: Load required header files
|
||||
|
218
examples/Sprite/Animated_dial/Animated_dial.ino
Normal file
218
examples/Sprite/Animated_dial/Animated_dial.ino
Normal file
@@ -0,0 +1,218 @@
|
||||
// This example draws an animated dial with a rotating needle.
|
||||
|
||||
// The dial is a jpeg image, the needle is created using a rotated
|
||||
// Sprite. The example operates by reading blocks of pixels from the
|
||||
// TFT, thus the TFT setup must support reading from the TFT CGRAM.
|
||||
|
||||
// The sketch operates by creating a copy of the screen block where
|
||||
// the needle will be drawn, the needle is then drawn on the screen.
|
||||
// When the needle moves, the original copy of the sreen area is
|
||||
// pushed to the screen to over-write the needle graphic. A copy
|
||||
// of the screen where the new position will be drawn is then made
|
||||
// before drawing the needle in the new postion. This technique
|
||||
// allows the needle to move over other screen graphics.
|
||||
|
||||
// The sketch calculates the size of the buffer memory required and
|
||||
// reserves the memory for the TFT block copy.
|
||||
|
||||
// Created by Bodmer 17/3/20 as an example to the TFT_eSPI library:
|
||||
// https://github.com/Bodmer/TFT_eSPI
|
||||
|
||||
#define NEEDLE_LENGTH 35 // Visible length
|
||||
#define NEEDLE_WIDTH 5 // Width of needle - make it an odd number
|
||||
#define NEEDLE_RADIUS 90 // Radius at tip
|
||||
#define NEEDLE_COLOR1 TFT_MAROON // Needle periphery colour
|
||||
#define NEEDLE_COLOR2 TFT_RED // Needle centre colour
|
||||
#define DIAL_CENTRE_X 120
|
||||
#define DIAL_CENTRE_Y 120
|
||||
|
||||
// Font attached to this sketch
|
||||
#include "NotoSansBold36.h"
|
||||
#define AA_FONT_LARGE NotoSansBold36
|
||||
|
||||
#include <TFT_eSPI.h>
|
||||
TFT_eSPI tft = TFT_eSPI();
|
||||
TFT_eSprite needle = TFT_eSprite(&tft); // Sprite object for needle
|
||||
TFT_eSprite spr = TFT_eSprite(&tft); // Sprite for meter reading
|
||||
|
||||
// Jpeg image array attached to this sketch
|
||||
#include "dial.h"
|
||||
|
||||
// Include the jpeg decoder library
|
||||
#include <TJpg_Decoder.h>
|
||||
|
||||
uint16_t* tft_buffer;
|
||||
bool buffer_loaded = false;
|
||||
uint16_t spr_width = 0;
|
||||
|
||||
// =======================================================================================
|
||||
// This function will be called during decoding of the jpeg file
|
||||
// =======================================================================================
|
||||
bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap)
|
||||
{
|
||||
// Stop further decoding as image is running off bottom of screen
|
||||
if ( y >= tft.height() ) return 0;
|
||||
|
||||
// This function will clip the image block rendering automatically at the TFT boundaries
|
||||
tft.pushImage(x, y, w, h, bitmap);
|
||||
|
||||
// Return 1 to decode next block
|
||||
return 1;
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
// Setup
|
||||
// =======================================================================================
|
||||
void setup() {
|
||||
Serial.begin(115200); // Debug only
|
||||
|
||||
// The byte order can be swapped (set true for TFT_eSPI)
|
||||
TJpgDec.setSwapBytes(true);
|
||||
|
||||
// The jpeg decoder must be given the exact name of the rendering function above
|
||||
TJpgDec.setCallback(tft_output);
|
||||
|
||||
tft.begin();
|
||||
tft.setRotation(0);
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
|
||||
// Draw the dial
|
||||
TJpgDec.drawJpg(0, 0, dial, sizeof(dial));
|
||||
tft.drawCircle(DIAL_CENTRE_X, DIAL_CENTRE_Y, NEEDLE_RADIUS-NEEDLE_LENGTH, TFT_DARKGREY);
|
||||
|
||||
// Load the font and create the Sprite for reporting the value
|
||||
spr.loadFont(AA_FONT_LARGE);
|
||||
spr_width = spr.textWidth("188");
|
||||
spr.createSprite(spr_width, spr.fontHeight());
|
||||
uint16_t bg_color = tft.readPixel(120, 120); // Get colour from dial centre
|
||||
spr.fillSprite(bg_color);
|
||||
spr.setTextColor(TFT_WHITE, bg_color);
|
||||
spr.setTextDatum(MC_DATUM);
|
||||
spr.setTextPadding(spr_width);
|
||||
spr.drawNumber(0, spr_width/2, 0);
|
||||
spr.pushSprite(DIAL_CENTRE_X - spr_width / 2, DIAL_CENTRE_Y - spr.fontHeight() / 2);
|
||||
|
||||
// Plot the label text
|
||||
tft.setTextColor(TFT_WHITE, bg_color);
|
||||
tft.setTextDatum(MC_DATUM);
|
||||
tft.drawString("(degrees)", DIAL_CENTRE_X, DIAL_CENTRE_Y + 46, 2);
|
||||
|
||||
// Define where the needle pivot point is on the TFT before
|
||||
// creating the needle so boundary calculation is correct
|
||||
tft.setPivot(DIAL_CENTRE_X, DIAL_CENTRE_Y);
|
||||
|
||||
// Create the needle Sprite
|
||||
createNeedle();
|
||||
|
||||
// Reset needle position to 0
|
||||
plotNeedle(0, 0);
|
||||
|
||||
delay(2000);
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
// Loop
|
||||
// =======================================================================================
|
||||
void loop() {
|
||||
uint16_t angle = random(241); // random speed in range 0 to 240
|
||||
|
||||
// Plot needle at random angle in range 0 to 240, speed 40ms per increment
|
||||
plotNeedle(angle, 30);
|
||||
|
||||
// Pause at new position
|
||||
delay(2500);
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
// Create the needle Sprite
|
||||
// =======================================================================================
|
||||
void createNeedle(void)
|
||||
{
|
||||
needle.setColorDepth(16);
|
||||
needle.createSprite(NEEDLE_WIDTH, NEEDLE_LENGTH); // create the needle Sprite
|
||||
|
||||
needle.fillSprite(TFT_BLACK); // Fill with black
|
||||
|
||||
// Define needle pivot point relative to top left corner of Sprite
|
||||
uint16_t piv_x = NEEDLE_WIDTH / 2; // pivot x in Sprite (middle)
|
||||
uint16_t piv_y = NEEDLE_RADIUS; // pivot y in Sprite
|
||||
needle.setPivot(piv_x, piv_y); // Set pivot point in this Sprite
|
||||
|
||||
// Draw the red needle in the Sprite
|
||||
needle.fillRect(0, 0, NEEDLE_WIDTH, NEEDLE_LENGTH, TFT_MAROON);
|
||||
needle.fillRect(1, 1, NEEDLE_WIDTH-2, NEEDLE_LENGTH-2, TFT_RED);
|
||||
|
||||
// Bounding box parameters to be populated
|
||||
int16_t min_x;
|
||||
int16_t min_y;
|
||||
int16_t max_x;
|
||||
int16_t max_y;
|
||||
|
||||
// Work out the worst case area that must be grabbed from the TFT,
|
||||
// this is at a 45 degree rotation
|
||||
needle.getRotatedBounds(45, &min_x, &min_y, &max_x, &max_y);
|
||||
|
||||
// Calculate the size and allocate the buffer for the grabbed TFT area
|
||||
tft_buffer = (uint16_t*) malloc( ((max_x - min_x) + 2) * ((max_y - min_y) + 2) * 2 );
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
// Move the needle to a new position
|
||||
// =======================================================================================
|
||||
void plotNeedle(int16_t angle, uint16_t ms_delay)
|
||||
{
|
||||
static int16_t old_angle = -120; // Starts at -120 degrees
|
||||
|
||||
// Trig values for the rotation
|
||||
int32_t sinra;
|
||||
int32_t cosra;
|
||||
|
||||
// Bounding box parameters
|
||||
static int16_t min_x;
|
||||
static int16_t min_y;
|
||||
static int16_t max_x;
|
||||
static int16_t max_y;
|
||||
|
||||
if (angle < 0) angle = 0; // Limit angle to emulate needle end stops
|
||||
if (angle > 240) angle = 240;
|
||||
|
||||
angle -= 120; // Starts at -120 degrees
|
||||
|
||||
// Move the needle until new angle reached
|
||||
while (angle != old_angle || !buffer_loaded) {
|
||||
|
||||
if (old_angle < angle) old_angle++;
|
||||
else old_angle--;
|
||||
|
||||
// Only plot needle at even values to improve plotting performance
|
||||
if ( (old_angle & 1) == 0)
|
||||
{
|
||||
if (buffer_loaded) {
|
||||
// Paste back the original needle free image area
|
||||
tft.pushRect(min_x, min_y, 1 + max_x - min_x, 1 + max_y - min_y, tft_buffer);
|
||||
}
|
||||
|
||||
if ( needle.getRotatedBounds(old_angle, &min_x, &min_y, &max_x, &max_y) )
|
||||
{
|
||||
// Grab a copy of the area before needle is drawn
|
||||
tft.readRect(min_x, min_y, 1 + max_x - min_x, 1 + max_y - min_y, tft_buffer);
|
||||
buffer_loaded = true;
|
||||
}
|
||||
|
||||
// Draw the needle in the new postion, black in needle image is transparent
|
||||
needle.pushRotated(old_angle, TFT_BLACK);
|
||||
|
||||
// Wait before next update
|
||||
delay(ms_delay);
|
||||
}
|
||||
|
||||
// Update the number at the centre of the dial
|
||||
spr.drawNumber(old_angle+120, spr_width/2, 0);
|
||||
spr.pushSprite(120 - spr_width / 2, 120 - spr.fontHeight() / 2);
|
||||
|
||||
// Slow needle down slightly as it approaches the new position
|
||||
if (abs(old_angle - angle) < 10) ms_delay += ms_delay / 5;
|
||||
}
|
||||
}
|
||||
|
||||
// =======================================================================================
|
2784
examples/Sprite/Animated_dial/NotoSansBold36.h
Normal file
2784
examples/Sprite/Animated_dial/NotoSansBold36.h
Normal file
File diff suppressed because it is too large
Load Diff
1153
examples/Sprite/Animated_dial/dial.h
Normal file
1153
examples/Sprite/Animated_dial/dial.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "TFT_eSPI",
|
||||
"version": "2.1.6",
|
||||
"version": "2.1.7",
|
||||
"keywords": "Arduino, tft, ePaper, display, STM32, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486, ST7789, RM68140",
|
||||
"description": "A TFT and ePaper SPI graphics library with optimisation for ESP8266, ESP32 and STM32",
|
||||
"repository":
|
||||
|
@@ -1,5 +1,5 @@
|
||||
name=TFT_eSPI
|
||||
version=2.1.6
|
||||
version=2.1.7
|
||||
author=Bodmer
|
||||
maintainer=Bodmer
|
||||
sentence=TFT graphics library for Arduino processors with performance optimisation for STM32, ESP8266 and ESP32
|
||||
|
Reference in New Issue
Block a user