Add smooth arc drawing function

Update ESP8266 architecture reference
Add pushMaskedImage() to render 16bpp images with a 1bpp mask (used for transparent PNG images plus with sprites)

New functions added using drawArc:
drawSmoothArc
drawSmoothCircle
drawSmoothRoundRect
New sqrt_fraction() added to improve smooth graphics performance on processors without a FPU (e.g. RP2040)

Faster alphaBlend() function added which retains 6bpp for green

Rename swap_coord() to transpose()
This commit is contained in:
Bodmer 2023-01-12 22:39:03 +00:00
parent ea82a7c15a
commit 82d232adfc
9 changed files with 723 additions and 125 deletions

View File

@ -1257,8 +1257,8 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const u
// Intentionally not constrained to viewport area, does not manage 1bpp rotations
void TFT_eSprite::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1)
{
if (x0 > x1) swap_coord(x0, x1);
if (y0 > y1) swap_coord(y0, y1);
if (x0 > x1) transpose(x0, x1);
if (y0 > y1) transpose(y0, y1);
int32_t w = width();
int32_t h = height();
@ -1700,13 +1700,13 @@ void TFT_eSprite::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint3
bool steep = abs(y1 - y0) > abs(x1 - x0);
if (steep) {
swap_coord(x0, y0);
swap_coord(x1, y1);
transpose(x0, y0);
transpose(x1, y1);
}
if (x0 > x1) {
swap_coord(x0, x1);
swap_coord(y0, y1);
transpose(x0, x1);
transpose(y0, y1);
}
int32_t dx = x1 - x0, dy = abs(y1 - y0);;

View File

@ -19,7 +19,7 @@
#define DMA_BUSY_CHECK // DMA not available, leave blank
// Initialise processor specific SPI functions, used by init()
#if (!defined (SUPPORT_TRANSACTIONS) && defined (ESP8266))
#if (!defined (SUPPORT_TRANSACTIONS) && defined (ARDUINO_ARCH_ESP8266))
#define INIT_TFT_DATA_BUS \
spi.setBitOrder(MSBFIRST); \
spi.setDataMode(TFT_SPI_MODE); \

View File

@ -577,7 +577,7 @@ void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){
////////////////////////////////////////////////////////////////////////////////////////
#ifdef RP2040_DMA // DMA functions for 16 bit SPI and 8 bit parallel displays
#ifdef RP2040_DMA // DMA functions for 16 bit SPI and 8/16 bit parallel displays
////////////////////////////////////////////////////////////////////////////////////////
/*
These are created in header file:

View File

@ -65,9 +65,20 @@
#define DMA_BUSY_CHECK
#endif
// Handle high performance MHS RPi display type
#if defined (MHS_DISPLAY_TYPE) && !defined (RPI_DISPLAY_TYPE)
#define RPI_DISPLAY_TYPE
#endif
#if !defined (RP2040_PIO_INTERFACE) // SPI
// Initialise processor specific SPI functions, used by init()
#define INIT_TFT_DATA_BUS // Not used
#if defined (MHS_DISPLAY_TYPE) // High speed RPi TFT type always needs 16 bit transfers
// This swaps to 16 bit mode, used for commands so wait avoids clash with DC timing
#define INIT_TFT_DATA_BUS hw_write_masked(&spi_get_hw(SPI_X)->cr0, (16 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS)
#else
// Initialise processor specific SPI functions, used by init()
#define INIT_TFT_DATA_BUS // Not used
#endif
// Wait for tx to end, flush rx FIFO, clear rx overrun
#define SPI_BUSY_CHECK while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; \
@ -141,7 +152,7 @@
#if !defined (RP2040_PIO_INTERFACE)// SPI
//#define DC_C sio_hw->gpio_clr = (1ul << TFT_DC)
//#define DC_D sio_hw->gpio_set = (1ul << TFT_DC)
#if defined (RPI_DISPLAY_TYPE)
#if defined (RPI_DISPLAY_TYPE) && !defined (MHS_DISPLAY_TYPE)
#define DC_C digitalWrite(TFT_DC, LOW);
#define DC_D digitalWrite(TFT_DC, HIGH);
#else
@ -167,7 +178,7 @@
#define CS_H // No macro allocated so it generates no code
#else
#if !defined (RP2040_PIO_INTERFACE) // SPI
#if defined (RPI_DISPLAY_TYPE)
#if defined (RPI_DISPLAY_TYPE) && !defined (MHS_DISPLAY_TYPE)
#define CS_L digitalWrite(TFT_CS, LOW);
#define CS_H digitalWrite(TFT_CS, HIGH);
#else
@ -287,7 +298,28 @@
// Macros to write commands/pixel colour data to other displays
////////////////////////////////////////////////////////////////////////////////////////
#else
#if defined (RPI_DISPLAY_TYPE) // RPi TFT type always needs 16 bit transfers
#if defined (MHS_DISPLAY_TYPE) // High speed RPi TFT type always needs 16 bit transfers
// This swaps to 16 bit mode, used for commands so wait avoids clash with DC timing
#define tft_Write_8(C) while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; \
hw_write_masked(&spi_get_hw(SPI_X)->cr0, (16 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); \
spi_get_hw(SPI_X)->dr = (uint32_t)((C) | ((C)<<8)); \
while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; \
// Note: the following macros do not wait for the end of transmission
#define tft_Write_16(C) while (!spi_is_writable(SPI_X)){}; spi_get_hw(SPI_X)->dr = (uint32_t)(C)
#define tft_Write_16N(C) while (!spi_is_writable(SPI_X)){}; spi_get_hw(SPI_X)->dr = (uint32_t)(C)
#define tft_Write_16S(C) while (!spi_is_writable(SPI_X)){}; spi_get_hw(SPI_X)->dr = (uint32_t)(C)<<8 | (C)>>8
#define tft_Write_32(C) spi_get_hw(SPI_X)->dr = (uint32_t)((C)>>16); spi_get_hw(SPI_X)->dr = (uint32_t)(C)
#define tft_Write_32C(C,D) spi_get_hw(SPI_X)->dr = (uint32_t)(C); spi_get_hw(SPI_X)->dr = (uint32_t)(D)
#define tft_Write_32D(C) spi_get_hw(SPI_X)->dr = (uint32_t)(C); spi_get_hw(SPI_X)->dr = (uint32_t)(C)
#elif defined (RPI_DISPLAY_TYPE) // RPi TFT type always needs 16 bit transfers
#define tft_Write_8(C) spi.transfer(C); spi.transfer(C)
#define tft_Write_16(C) spi.transfer((uint8_t)((C)>>8));spi.transfer((uint8_t)((C)>>0))
#define tft_Write_16N(C) spi.transfer((uint8_t)((C)>>8));spi.transfer((uint8_t)((C)>>0))

View File

@ -149,7 +149,6 @@ inline void TFT_eSPI::begin_tft_read(void){
SET_BUS_READ_MODE;
}
/***************************************************************************************
** Function name: end_tft_read (was called spi_end_read)
** Description: End transaction for reads and deselect TFT
@ -233,7 +232,6 @@ void TFT_eSPI::setViewport(int32_t x, int32_t y, int32_t w, int32_t h, bool vpDa
//Serial.print(" _vpX=");Serial.print( _vpX);Serial.print(", _vpY=");Serial.print( _vpY);
//Serial.print(", _vpW=");Serial.print(_vpW);Serial.print(", _vpH=");Serial.println(_vpH);
}
/***************************************************************************************
@ -866,6 +864,48 @@ void TFT_eSPI::setRotation(uint8_t m)
}
/***************************************************************************************
** Function name: getRotation
** Description: Return the rotation value (as used by setRotation())
***************************************************************************************/
uint8_t TFT_eSPI::getRotation(void)
{
return rotation;
}
/***************************************************************************************
** Function name: setOrigin
** Description: Set graphics origin to position x,y wrt to top left corner
***************************************************************************************/
//Note: setRotation, setViewport and resetViewport will revert origin to top left
void TFT_eSPI::setOrigin(int32_t x, int32_t y)
{
_xDatum = x;
_yDatum = y;
}
/***************************************************************************************
** Function name: getOriginX
** Description: Set graphics origin to position x
***************************************************************************************/
int32_t TFT_eSPI::getOriginX(void)
{
return _xDatum;
}
/***************************************************************************************
** Function name: getOriginY
** Description: Set graphics origin to position y
***************************************************************************************/
int32_t TFT_eSPI::getOriginY(void)
{
return _yDatum;
}
/***************************************************************************************
** Function name: commandList, used for FLASH based lists only (e.g. ST7735)
** Description: Get initialisation commands from FLASH and send to TFT
@ -1998,6 +2038,91 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *da
end_tft_write();
}
/***************************************************************************************
** Function name: pushMaskedImage
** Description: Render a 16 bit colour image with a 1bpp mask
***************************************************************************************/
// Can be used with a 16bpp sprite and a 1bpp sprite for the mask
void TFT_eSPI::pushMaskedImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *img, uint8_t *mask)
{
if (_vpOoB || w < 1 || h < 1) return;
// To simplify mask handling the window clipping is done by the pushImage function
// Each mask image line assumed to be padded to and integer number of bytes & padding bits are 0
begin_tft_write();
inTransaction = true;
uint8_t *mptr = mask;
uint8_t *eptr = mask + ((w + 7) >> 3);
uint16_t *iptr = img;
uint32_t setCount = 0;
// For each line in the image
while (h--) {
uint32_t xp = 0;
uint32_t clearCount = 0;
uint8_t mbyte= *mptr++;
uint32_t bits = 8;
// Scan through each byte of the bitmap and determine run lengths
do {
setCount = 0;
//Get run length for clear bits to determine x offset
while ((mbyte & 0x80) == 0x00) {
// Check if remaining bits in byte are clear (reduce shifts)
if (mbyte == 0) {
clearCount += bits; // bits not always 8 here
if (mptr >= eptr) break; // end of line
mbyte = *mptr++;
bits = 8;
continue;
}
mbyte = mbyte << 1; // 0's shifted in
clearCount ++;
if (--bits) continue;;
if (mptr >= eptr) break;
mbyte = *mptr++;
bits = 8;
}
//Get run length for set bits to determine render width
while ((mbyte & 0x80) == 0x80) {
// Check if all bits are set (reduces shifts)
if (mbyte == 0xFF) {
setCount += bits;
if (mptr >= eptr) break;
mbyte = *mptr++;
//bits = 8; // NR, bits always 8 here unless 1's shifted in
continue;
}
mbyte = mbyte << 1; //or mbyte += mbyte + 1 to shift in 1's
setCount ++;
if (--bits) continue;
if (mptr >= eptr) break;
mbyte = *mptr++;
bits = 8;
}
// A mask boundary or mask end has been found, so render the pixel line
if (setCount) {
xp += clearCount;
clearCount = 0;
pushImage(x + xp, y, setCount, 1, iptr + xp); // pushImage handles clipping
//pushImageDMA(x + xp, y, setCount, 1, iptr + xp);
xp += setCount;
}
} while (setCount || mptr < eptr);
y++;
iptr += w;
eptr += ((w + 7) >> 3);
}
inTransaction = lockTransaction;
end_tft_write();
}
/***************************************************************************************
** Function name: setSwapBytes
@ -2510,13 +2635,13 @@ void TFT_eSPI::fillTriangle ( int32_t x0, int32_t y0, int32_t x1, int32_t y1, in
// Sort coordinates by Y order (y2 >= y1 >= y0)
if (y0 > y1) {
swap_coord(y0, y1); swap_coord(x0, x1);
transpose(y0, y1); transpose(x0, x1);
}
if (y1 > y2) {
swap_coord(y2, y1); swap_coord(x2, x1);
transpose(y2, y1); transpose(x2, x1);
}
if (y0 > y1) {
swap_coord(y0, y1); swap_coord(x0, x1);
transpose(y0, y1); transpose(x0, x1);
}
if (y0 == y2) { // Handle awkward all-on-same-line case as its own thing
@ -2557,7 +2682,7 @@ void TFT_eSPI::fillTriangle ( int32_t x0, int32_t y0, int32_t x1, int32_t y1, in
sa += dx01;
sb += dx02;
if (a > b) swap_coord(a, b);
if (a > b) transpose(a, b);
drawFastHLine(a, y, b - a + 1, color);
}
@ -2571,7 +2696,7 @@ void TFT_eSPI::fillTriangle ( int32_t x0, int32_t y0, int32_t x1, int32_t y1, in
sa += dx12;
sb += dx02;
if (a > b) swap_coord(a, b);
if (a > b) transpose(a, b);
drawFastHLine(a, y, b - a + 1, color);
}
@ -2837,15 +2962,6 @@ uint16_t TFT_eSPI::getTextPadding(void)
return padX;
}
/***************************************************************************************
** Function name: getRotation
** Description: Return the rotation value (as used by setRotation())
***************************************************************************************/
uint8_t TFT_eSPI::getRotation(void)
{
return rotation;
}
/***************************************************************************************
** Function name: getTextDatum
** Description: Return the text datum value (as used by setTextDatum())
@ -3194,7 +3310,7 @@ void TFT_eSPI::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1)
addr_col = 0xFFFF;
#if defined (ILI9225_DRIVER)
if (rotation & 0x01) { swap_coord(x0, y0); swap_coord(x1, y1); }
if (rotation & 0x01) { transpose(x0, y0); transpose(x1, y1); }
SPI_BUSY_CHECK;
DC_C; tft_Write_8(TFT_CASET1);
DC_D; tft_Write_16(x0);
@ -3222,8 +3338,8 @@ void TFT_eSPI::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1)
#endif
#elif defined (SSD1351_DRIVER)
if (rotation & 1) {
swap_coord(x0, y0);
swap_coord(x1, y1);
transpose(x0, y0);
transpose(x1, y1);
}
SPI_BUSY_CHECK;
DC_C; tft_Write_8(TFT_CASET);
@ -3234,7 +3350,7 @@ void TFT_eSPI::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1)
DC_D;
#else
#if defined (SSD1963_DRIVER)
if ((rotation & 0x1) == 0) { swap_coord(x0, y0); swap_coord(x1, y1); }
if ((rotation & 0x1) == 0) { transpose(x0, y0); transpose(x1, y1); }
#endif
#ifdef CGRAM_OFFSET
@ -3326,7 +3442,7 @@ void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h)
addr_row = 0xFFFF;
#if defined (SSD1963_DRIVER)
if ((rotation & 0x1) == 0) { swap_coord(xs, ys); swap_coord(xe, ye); }
if ((rotation & 0x1) == 0) { transpose(xs, ys); transpose(xe, ye); }
#endif
#ifdef CGRAM_OFFSET
@ -3420,7 +3536,7 @@ void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color)
begin_tft_write();
#if defined (ILI9225_DRIVER)
if (rotation & 0x01) { swap_coord(x, y); }
if (rotation & 0x01) { transpose(x, y); }
SPI_BUSY_CHECK;
// Set window to full screen to optimise sequential pixel rendering
@ -3455,7 +3571,7 @@ void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color)
#elif (defined (ARDUINO_ARCH_RP2040) || defined (ARDUINO_ARCH_MBED)) && !defined (SSD1351_DRIVER)
#if defined (SSD1963_DRIVER)
if ((rotation & 0x1) == 0) { swap_coord(x, y); }
if ((rotation & 0x1) == 0) { transpose(x, y); }
#endif
#if !defined(RP2040_PIO_INTERFACE)
@ -3535,13 +3651,13 @@ void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color)
#else
#if defined (SSD1963_DRIVER)
if ((rotation & 0x1) == 0) { swap_coord(x, y); }
if ((rotation & 0x1) == 0) { transpose(x, y); }
#endif
SPI_BUSY_CHECK;
#if defined (SSD1351_DRIVER)
if (rotation & 0x1) { swap_coord(x, y); }
if (rotation & 0x1) { transpose(x, y); }
// No need to send x if it has not changed (speeds things up)
if (addr_col != x) {
DC_C; tft_Write_8(TFT_CASET);
@ -3693,13 +3809,13 @@ void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t
bool steep = abs(y1 - y0) > abs(x1 - x0);
if (steep) {
swap_coord(x0, y0);
swap_coord(x1, y1);
transpose(x0, y0);
transpose(x1, y1);
}
if (x0 > x1) {
swap_coord(x0, x1);
swap_coord(y0, y1);
transpose(x0, x1);
transpose(y0, y1);
}
int32_t dx = x1 - x0, dy = abs(y1 - y0);;
@ -3750,6 +3866,7 @@ void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t
constexpr float PixelAlphaGain = 255.0;
constexpr float LoAlphaTheshold = 1.0/32.0;
constexpr float HiAlphaTheshold = 1.0 - LoAlphaTheshold;
constexpr float deg2rad = 3.14159265359/180.0;
/***************************************************************************************
** Function name: drawPixel (alpha blended)
@ -3763,6 +3880,291 @@ uint16_t TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color, uint8_t alpha
return color;
}
/***************************************************************************************
** Function name: drawSmoothArc
** Description: Draw a smooth arc clockwise from 6 o'clock
***************************************************************************************/
void TFT_eSPI::drawSmoothArc(int32_t x, int32_t y, int32_t r, int32_t ir, int32_t startAngle, int32_t endAngle, uint32_t fg_color, uint32_t bg_color, bool roundEnds)
// Centre at x,y
// r = arc outer radius, ir = arc inner radius. Inclusive so arc thickness = r - ir + 1
// Angles in range 0-360
// Arc foreground colour anti-aliased with background colour at edges
// anti-aliased roundEnd is optional, default is anti-aliased straight end
// Note: rounded ends extend the arc angle so can overlap, user sketch to manage this.
{
inTransaction = true;
if (endAngle != startAngle)
{
float sx = -sinf(startAngle * deg2rad);
float sy = +cosf(startAngle * deg2rad);
float ex = -sinf( endAngle * deg2rad);
float ey = +cosf( endAngle * deg2rad);
if (roundEnds)
{ // Round ends
sx = sx * (r + ir)/2.0 + x;
sy = sy * (r + ir)/2.0 + y;
drawSpot(sx, sy, (r - ir)/2.0, fg_color, bg_color);
ex = ex * (r + ir)/2.0 + x;
ey = ey * (r + ir)/2.0 + y;
drawSpot(ex, ey, (r - ir)/2.0, fg_color, bg_color);
}
else
{ // Square ends
float asx = sx * ir + x;
float asy = sy * ir + y;
float aex = sx * r + x;
float aey = sy * r + y;
drawWedgeLine(asx, asy, aex, aey, 0.3, 0.3, fg_color, bg_color);
asx = ex * ir + x;
asy = ey * ir + y;
aex = ex * r + x;
aey = ey * r + y;
drawWedgeLine(asx, asy, aex, aey, 0.3, 0.3, fg_color, bg_color);
}
if (endAngle > startAngle)
{
// Draw arc in single sweep
drawArc(x, y, r, ir, startAngle, endAngle, fg_color, bg_color);
}
else
{
// Arc sweeps through 6 o'clock so draw in two parts
drawArc(x, y, r, ir, startAngle, 360, fg_color, bg_color);
drawArc(x, y, r, ir, 0, endAngle, fg_color, bg_color);
}
}
else // Draw full 360
{
drawArc(x, y, r, ir, 0, 360, fg_color, bg_color);
}
inTransaction = lockTransaction;
end_tft_write();
}
/***************************************************************************************
** Function name: sqrt_fraction
** Description: Smooth graphics support function for alpha derivation
***************************************************************************************/
// Compute the fixed point square root of an integer and
// return the 8 MS bits of fractional part.
// Quicker than sqrt() for processors that do not have and FPU (e.g. RP2040)
inline uint8_t TFT_eSPI::sqrt_fraction(uint32_t num) {
if (num > (0x40000000)) return 0;
uint32_t bsh = 0x00004000;
uint32_t fpr = 0;
uint32_t osh = 0;
// Auto adjust from U8:8 up to U15:16
while (num>bsh) {bsh <<= 2; osh++;}
do {
uint32_t bod = bsh + fpr;
if(num >= bod)
{
num -= bod;
fpr = bsh + bod;
}
num <<= 1;
} while(bsh >>= 1);
return fpr>>osh;
}
/***************************************************************************************
** Function name: drawArc
** Description: Draw an arc clockwise from 6 o'clock position
***************************************************************************************/
// Centre at x,y
// r = arc outer radius, ir = arc inner radius. Inclusive, so arc thickness = r-ir+1
// Angles MUST be in range 0-360, end angle MUST be greater than start angle
// Arc foreground fg_color anti-aliased with background colour along sides
// smooth is optional, default is true, smooth=false means no antialiasing
// Note: Arc ends are not anti-aliased (use drawSmoothArc instead for that)
void TFT_eSPI::drawArc(int32_t x, int32_t y, int32_t r, int32_t ir,
int32_t startAngle, int32_t endAngle,
uint32_t fg_color, uint32_t bg_color,
bool smooth)
{
if (_vpOoB) return;
if (r < ir) transpose(r, ir); // Required that r > ir
if (r <= 0 || ir < 0) return; // Invalid r, ir can be zero (circle sector)
if (endAngle < startAngle) transpose(startAngle, endAngle);
if (startAngle < 0) startAngle = 0;
if (endAngle > 360) endAngle = 360;
inTransaction = true;
int32_t xs = 0; // x start position for quadrant scan
uint8_t alpha = 0; // alpha value for blending pixels
int32_t r2 = r * r; // Outer arc radius^2
if (smooth) r++; // Outer AA zone radius
int32_t r1 = r * r; // Outer AA radius^2
int16_t w = r - ir; // Width of arc (r - ir + 1)
int32_t r3 = ir * ir; // Inner arc radius^2
if (smooth) ir--; // Inner AA zone radius
int32_t r4 = ir * ir; // Inner AA radius^2
// Float variants of adjusted inner and outer arc radii
//float irf = ir;
//float rf = r;
// 1 | 2
// ---¦--- Arc quadrant index
// 0 | 3
// Fixed point U16.16 slope table for arc start/end in each quadrant
uint32_t startSlope[4] = {0, 0, 0xFFFFFFFF, 0};
uint32_t endSlope[4] = {0, 0xFFFFFFFF, 0, 0};
// Ensure maximum U16.16 slope of arc ends is ~ 0x8000 0000
constexpr float minDivisor = 1.0f/0x8000;
// Fill in start slope table and empty quadrants
float fabscos = fabsf(cosf(startAngle * deg2rad));
float fabssin = fabsf(sinf(startAngle * deg2rad));
// U16.16 slope of arc start
uint32_t slope = (fabscos/(fabssin + minDivisor)) * (float)(1<<16);
// Update slope table, add slope for arc start
if (startAngle < 90) {
startSlope[0] = slope;
}
else if (
< 180) {
startSlope[1] = slope;
}
else if (startAngle < 270) {
startSlope[1] = 0xFFFFFFFF;
startSlope[2] = slope;
}
else {
startSlope[1] = 0xFFFFFFFF;
startSlope[2] = 0;
startSlope[3] = slope;
}
// Fill in end slope table and empty quadrants
fabscos = fabsf(cosf(endAngle * deg2rad));
fabssin = fabsf(sinf(endAngle * deg2rad));
// U16.16 slope of arc end
slope = (uint32_t)((fabscos/(fabssin + minDivisor)) * (float)(1<<16));
// Work out which quadrants will need to be drawn and add slope for arc end
if (endAngle < 90) {
endSlope[0] = slope;
endSlope[1] = 0;
endSlope[2] = 0xFFFFFFFF;
}
else if (endAngle < 180) {
endSlope[1] = slope;
endSlope[2] = 0xFFFFFFFF;
}
else if (endAngle < 270) {
endSlope[2] = slope;
}
else {
endSlope[3] = slope;
}
// Scan quadrant
for (int32_t cy = r - 1; cy > 0; cy--)
{
uint32_t len[4] = { 0, 0, 0, 0}; // Pixel run length
int32_t xst[4] = {-1, -1, -1, -1}; // Pixel run x start
uint32_t dy2 = (r - cy) * (r - cy);
// Find and track arc zone start point
while ((r - xs) * (r - xs) + dy2 >= r1) xs++;
for (int32_t cx = xs; cx < r; cx++)
{
// Calculate radius^2
uint32_t hyp = (r - cx) * (r - cx) + dy2;
// If in outer zone calculate alpha
if (hyp > r2) {
//alpha = (uint8_t)((rf - sqrtf(hyp)) * 255);
alpha = ~sqrt_fraction(hyp); // Outer AA zone
}
// If within arc fill zone, get line start and lengths for each quadrant
else if (hyp >= r3) {
// Calculate U16.16 slope
slope = ((r - cy) << 16)/(r - cx);
if (slope <= startSlope[0] && slope >= endSlope[0]) { // slope hi -> lo
xst[0] = cx; // Bottom left line end
len[0]++;
}
if (slope >= startSlope[1] && slope <= endSlope[1]) { // slope lo -> hi
xst[1] = cx; // Top left line end
len[1]++;
}
if (slope <= startSlope[2] && slope >= endSlope[2]) { // slope hi -> lo
xst[2] = cx; // Bottom right line start
len[2]++;
}
if (slope >= startSlope[3] && slope <= endSlope[3]) { // slope lo -> hi
xst[3] = cx; // Top right line start
len[3]++;
}
continue; // Next x
}
else {
if (hyp <= r4) break; // Skip inner pixels
//alpha = (uint8_t)((sqrtf(hyp) - irf) * 255.0);
alpha = sqrt_fraction(hyp); // Inner AA zone
}
if (alpha < 16) continue; // Skip low alpha pixels
// If background is read it must be done in each quadrant
uint16_t pcol = alphaBlend(alpha, fg_color, bg_color);
// Check if an AA pixels need to be drawn
slope = ((r - cy)<<16)/(r - cx);
if (slope <= startSlope[0] && slope >= endSlope[0]) // BL
drawPixel(x + cx - r, y - cy + r, pcol);
if (slope >= startSlope[1] && slope <= endSlope[1]) // TL
drawPixel(x + cx - r, y + cy - r, pcol);
if (slope <= startSlope[2] && slope >= endSlope[2]) // TR
drawPixel(x - cx + r, y + cy - r, pcol);
if (slope >= startSlope[3] && slope <= endSlope[3]) // BR
drawPixel(x - cx + r, y - cy + r, pcol);
}
// Add line in inner zone
if (len[0]) drawFastHLine(x + xst[0] - len[0] + 1 - r, y - cy + r, len[0], fg_color); // BL
if (len[1]) drawFastHLine(x + xst[1] - len[1] + 1 - r, y + cy - r, len[1], fg_color); // TL
if (len[2]) drawFastHLine(x - xst[2] + r, y + cy - r, len[2], fg_color); // TR
if (len[3]) drawFastHLine(x - xst[3] + r, y - cy + r, len[3], fg_color); // BR
}
// Fill in centre lines
if (startAngle == 0 || endAngle == 360) drawFastVLine(x, y + r - w, w, fg_color); // Bottom
if (startAngle <= 90 && endAngle >= 90) drawFastHLine(x - r + 1, y, w, fg_color); // Left
if (startAngle <= 180 && endAngle >= 180) drawFastVLine(x, y - r + 1, w, fg_color); // Top
if (startAngle <= 270 && endAngle >= 270) drawFastHLine(x + r - w, y, w, fg_color); // Right
inTransaction = lockTransaction;
end_tft_write();
}
/***************************************************************************************
** Function name: drawSmoothCircle
** Description: Draw a smooth circle
***************************************************************************************/
// To have effective anti-aliasing the circle will be 3 pixels thick
void TFT_eSPI::drawSmoothCircle(int32_t x, int32_t y, int32_t r, uint32_t fg_color, uint32_t bg_color)
{
drawSmoothRoundRect(x-r, y-r, r, r-1, 0, 0, fg_color, bg_color);
}
/***************************************************************************************
** Function name: fillSmoothCircle
** Description: Draw a filled anti-aliased circle
@ -3789,12 +4191,19 @@ void TFT_eSPI::fillSmoothCircle(int32_t x, int32_t y, int32_t r, uint32_t color,
int32_t hyp2 = (r - cx) * (r - cx) + dy2;
if (hyp2 <= r1) break;
if (hyp2 >= r2) continue;
//*
uint8_t alpha = ~sqrt_fraction(hyp2);
if (alpha > 246) break;
xs = cx;
if (alpha < 9) continue;
//*/
/*
float alphaf = (float)r - sqrtf(hyp2);
if (alphaf > HiAlphaTheshold) break;
xs = cx;
if (alphaf < LoAlphaTheshold) continue;
uint8_t alpha = alphaf * 255;
//*/
if (bg_color == 0x00FFFFFF) {
drawPixel(x + cx - r, y + cy - r, color, alpha, bg_color);
drawPixel(x - cx + r, y + cy - r, color, alpha, bg_color);
@ -3816,6 +4225,119 @@ void TFT_eSPI::fillSmoothCircle(int32_t x, int32_t y, int32_t r, uint32_t color,
}
/***************************************************************************************
** Function name: drawSmoothRoundRect
** Description: Draw a rounded rectangle
***************************************************************************************/
// x,y is top left corner of bounding box for a complete rounded rectangle
// r = arc outer corner radius, ir = arc inner radius. Arc thickness = r-ir+1
// w and h are width and height of the bounding rectangle
// If w and h are < radius (e.g. 0,0) a circle will be drawn with centre at x+r,y+r
// Arc foreground fg_color anti-aliased with background colour at edges
// A subset of corners can be drawn by specifying a quadrants mask. A bit set in the
// mask means draw that quadrant (all are drawn if parameter missing):
// 0x1 | 0x2
// ---¦--- Arc quadrant mask select bits (as in drawCircleHelper fn)
// 0x8 | 0x4
void TFT_eSPI::drawSmoothRoundRect(int32_t x, int32_t y, int32_t r, int32_t ir, int32_t w, int32_t h, uint32_t fg_color, uint32_t bg_color, uint8_t quadrants)
{
if (_vpOoB) return;
if (r < ir) transpose(r, ir); // Required that r > ir
if (r <= 0 || ir < 0) return; // Invalid
w -= 2*r;
h -= 2*r;
if (w < 0) w = 0;
if (h < 0) h = 0;
inTransaction = true;
x += r;
y += r;
/*
float alphaGain = 1.0;
if (w != 0 || h != 0) {
if (r - ir < 2) alphaGain = 1.5; // Boost brightness for thin lines
if (r - ir < 1) alphaGain = 1.7;
}
*/
uint16_t t = r - ir + 1;
int32_t xs = 0;
int32_t cx = 0;
int32_t r2 = r * r; // Outer arc radius^2
r++;
int32_t r1 = r * r; // Outer AA zone radius^2
int32_t r3 = ir * ir; // Inner arc radius^2
ir--;
int32_t r4 = ir * ir; // Inner AA zone radius^2
//float irf = ir;
//float rf = r;
uint8_t alpha = 0;
// Scan top left quadrant x y r ir fg_color bg_color
for (int32_t cy = r - 1; cy > 0; cy--)
{
int32_t len = 0; // Pixel run length
int32_t lxst = 0; // Left side run x start
int32_t rxst = 0; // Right side run x start
int32_t dy2 = (r - cy) * (r - cy);
// Find and track arc zone start point
while ((r - xs) * (r - xs) + dy2 >= r1) xs++;
for (cx = xs; cx < r; cx++)
{
// Calculate radius^2
int32_t hyp = (r - cx) * (r - cx) + dy2;
// If in outer zone calculate alpha
if (hyp > r2) {
alpha = ~sqrt_fraction(hyp);
//alpha = (uint8_t)((rf - sqrtf(hyp)) * 255); // Outer AA zone
}
// If within arc fill zone, get line lengths for each quadrant
else if (hyp >= r3) {
rxst = cx; // Right side start
len++; // Line segment length
continue; // Next x
}
else {
if (hyp <= r4) break; // Skip inner pixels
//alpha = (uint8_t)((sqrtf(hyp) - irf) * 255); // Inner AA zone
alpha = sqrt_fraction(hyp);
}
if (alpha < 16) continue; // Skip low alpha pixels
// If background is read it must be done in each quadrant - TODO
uint16_t pcol = alphaBlend(alpha, fg_color, bg_color);
if (quadrants & 0x8) drawPixel(x + cx - r, y - cy + r + h, pcol); // BL
if (quadrants & 0x1) drawPixel(x + cx - r, y + cy - r, pcol); // TL
if (quadrants & 0x2) drawPixel(x - cx + r + w, y + cy - r, pcol); // TR
if (quadrants & 0x4) drawPixel(x - cx + r + w, y - cy + r + h, pcol); // BR
}
// Fill arc inner zone in each quadrant
lxst = rxst - len + 1; // Calculate line segment start for left side
if (quadrants & 0x8) drawFastHLine(x + lxst - r, y - cy + r + h, len, fg_color); // BL
if (quadrants & 0x1) drawFastHLine(x + lxst - r, y + cy - r, len, fg_color); // TL
if (quadrants & 0x2) drawFastHLine(x - rxst + r + w, y + cy - r, len, fg_color); // TR
if (quadrants & 0x4) drawFastHLine(x - rxst + r + w, y - cy + r + h, len, fg_color); // BR
}
// Draw sides
if ((quadrants & 0xC) == 0xC) fillRect(x, y + r - t + h, w + 1, t, fg_color); // Bottom
if ((quadrants & 0x9) == 0x9) fillRect(x - r + 1, y, t, h + 1, fg_color); // Left
if ((quadrants & 0x3) == 0x3) fillRect(x, y - r + 1, w + 1, t, fg_color); // Top
if ((quadrants & 0x6) == 0x6) fillRect(x + r - t + w, y, t, h + 1, fg_color); // Right
inTransaction = lockTransaction;
end_tft_write();
}
/***************************************************************************************
** Function name: fillSmoothRoundRect
** Description: Draw a filled anti-aliased rounded corner rectangle
@ -3823,20 +4345,23 @@ void TFT_eSPI::fillSmoothCircle(int32_t x, int32_t y, int32_t r, uint32_t color,
void TFT_eSPI::fillSmoothRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t r, uint32_t color, uint32_t bg_color)
{
inTransaction = true;
int32_t xs = 0;
int32_t cx = 0;
// Limit radius to half width or height
if (r < 0) r = 0;
if (r < 0) r = 0;
if (r > w/2) r = w/2;
if (r > h/2) r = h/2;
y += r;
h -= 2*r;
fillRect(x, y, w, h, color);
h--;
x += r;
w -= 2*r+1;
int32_t r1 = r * r;
r++;
int32_t r2 = r * r;
@ -3849,12 +4374,18 @@ void TFT_eSPI::fillSmoothRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, i
int32_t hyp2 = (r - cx) * (r - cx) + dy2;
if (hyp2 <= r1) break;
if (hyp2 >= r2) continue;
uint8_t alpha = ~sqrt_fraction(hyp2);
if (alpha > 246) break;
xs = cx;
if (alpha < 9) continue;
/*
float alphaf = (float)r - sqrtf(hyp2);
if (alphaf > HiAlphaTheshold) break;
xs = cx;
if (alphaf < LoAlphaTheshold) continue;
uint8_t alpha = alphaf * 255;
*/
drawPixel(x + cx - r, y + cy - r, color, alpha, bg_color);
drawPixel(x - cx + r + w, y + cy - r, color, alpha, bg_color);
drawPixel(x - cx + r + w, y - cy + r + h, color, alpha, bg_color);
@ -3871,6 +4402,7 @@ void TFT_eSPI::fillSmoothRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, i
** Function name: drawSpot - maths intensive, so for small filled circles
** Description: Draw an anti-aliased filled circle at ax,ay with radius r
***************************************************************************************/
// Coordinates are floating point to achieve sub-pixel positioning
void TFT_eSPI::drawSpot(float ax, float ay, float r, uint32_t fg_color, uint32_t bg_color)
{
// Filled circle can be created by the wide line function with zero line length
@ -3893,7 +4425,7 @@ void TFT_eSPI::drawWideLine(float ax, float ay, float bx, float by, float wd, ui
void TFT_eSPI::drawWedgeLine(float ax, float ay, float bx, float by, float ar, float br, uint32_t fg_color, uint32_t bg_color)
{
if ( (ar < 0.0) || (br < 0.0) )return;
if ( (abs(ax - bx) < 0.01f) && (abs(ay - by) < 0.01f) ) bx += 0.01f; // Avoid divide by zero
if ( (fabsf(ax - bx) < 0.01f) && (fabsf(ay - by) < 0.01f) ) bx += 0.01f; // Avoid divide by zero
// Find line bounding box
int32_t x0 = (int32_t)floorf(fminf(ax-ar, bx-br));
@ -3963,7 +4495,7 @@ void TFT_eSPI::drawWedgeLine(float ax, float ay, float bx, float by, float ar, f
pushColor(fg_color);
continue;
}
//Blend color with background and plot
//Blend colour with background and plot
if (bg_color == 0x00FFFFFF) {
bg = readPixel(xp, yp); swin = true;
}
@ -3976,7 +4508,7 @@ void TFT_eSPI::drawWedgeLine(float ax, float ay, float bx, float by, float ar, f
end_nin_write();
}
// Calculate distance of px,py to closest part of line
/***************************************************************************************
** Function name: lineDistance - private helper function for drawWedgeLine
** Description: returns distance of px,py to closest part of a to b wedge
@ -4375,26 +4907,13 @@ uint16_t TFT_eSPI::decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining)
** Function name: alphaBlend
** Description: Blend 16bit foreground and background
*************************************************************************************x*/
uint16_t TFT_eSPI::alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc)
inline uint16_t TFT_eSPI::alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc)
{
// For speed use fixed point maths and rounding to permit a power of 2 division
uint16_t fgR = ((fgc >> 10) & 0x3E) + 1;
uint16_t fgG = ((fgc >> 4) & 0x7E) + 1;
uint16_t fgB = ((fgc << 1) & 0x3E) + 1;
uint16_t bgR = ((bgc >> 10) & 0x3E) + 1;
uint16_t bgG = ((bgc >> 4) & 0x7E) + 1;
uint16_t bgB = ((bgc << 1) & 0x3E) + 1;
// Shift right 1 to drop rounding bit and shift right 8 to divide by 256
uint16_t r = (((fgR * alpha) + (bgR * (255 - alpha))) >> 9);
uint16_t g = (((fgG * alpha) + (bgG * (255 - alpha))) >> 9);
uint16_t b = (((fgB * alpha) + (bgB * (255 - alpha))) >> 9);
// Combine RGB565 colours into 16 bits
//return ((r&0x18) << 11) | ((g&0x30) << 5) | ((b&0x18) << 0); // 2 bit greyscale
//return ((r&0x1E) << 11) | ((g&0x3C) << 5) | ((b&0x1E) << 0); // 4 bit greyscale
return (r << 11) | (g << 5) | (b << 0);
uint32_t rxb = bgc & 0xF81F;
rxb += ((fgc & 0xF81F) - rxb) * (alpha >> 2) >> 6;
uint32_t xgx = bgc & 0x07E0;
xgx += ((fgc & 0x07E0) - xgx) * alpha >> 8;
return (rxb & 0xF81F) | (xgx & 0x07E0);
}
/***************************************************************************************
@ -4427,22 +4946,13 @@ uint32_t TFT_eSPI::alphaBlend24(uint8_t alpha, uint32_t fgc, uint32_t bgc, uint8
if (alphaDither >255) alpha = 255;
}
// For speed use fixed point maths and rounding to permit a power of 2 division
uint16_t fgR = ((fgc >> 15) & 0x1FE) + 1;
uint16_t fgG = ((fgc >> 7) & 0x1FE) + 1;
uint16_t fgB = ((fgc << 1) & 0x1FE) + 1;
uint16_t bgR = ((bgc >> 15) & 0x1FE) + 1;
uint16_t bgG = ((bgc >> 7) & 0x1FE) + 1;
uint16_t bgB = ((bgc << 1) & 0x1FE) + 1;
// Shift right 1 to drop rounding bit and shift right 8 to divide by 256
uint16_t r = (((fgR * alpha) + (bgR * (255 - alpha))) >> 9);
uint16_t g = (((fgG * alpha) + (bgG * (255 - alpha))) >> 9);
uint16_t b = (((fgB * alpha) + (bgB * (255 - alpha))) >> 9);
// Combine RGB colours into 24 bits
return (r << 16) | (g << 8) | (b << 0);
uint32_t rxx = bgc & 0xFF0000;
rxx += ((fgc & 0xFF0000) - rxx) * alpha >> 8;
uint32_t xgx = bgc & 0x00FF00;
xgx += ((fgc & 0xFF0000) - xgx) * alpha >> 8;
uint32_t xxb = bgc & 0x0000FF;
xxb += ((fgc & 0xFF0000) - xxb) * alpha >> 8;
return (rxx & 0xFF0000) | (xgx & 0x00FF00) | (xxb & 0x0000FF);
}
/***************************************************************************************

View File

@ -16,7 +16,7 @@
#ifndef _TFT_eSPIH_
#define _TFT_eSPIH_
#define TFT_ESPI_VERSION "2.4.79"
#define TFT_ESPI_VERSION "2.5.81b"
// Bit level feature flags
// Bit 0 set: viewport capability
@ -402,9 +402,6 @@ int16_t tch_spi_freq;// Touch controller read/write SPI frequency
/***************************************************************************************
** Section 8: Class member and support functions
***************************************************************************************/
// Swap any type
template <typename T> static inline void
swap_coord(T& a, T& b) { T t = a; a = b; b = t; }
// Callback prototype for smooth font pixel colour read
typedef uint16_t (*getColorCallback)(uint16_t x, uint16_t y);
@ -449,6 +446,12 @@ class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has ac
void setRotation(uint8_t r); // Set the display image orientation to 0, 1, 2 or 3
uint8_t getRotation(void); // Read the current rotation
// Change the origin position from the default top left
// Note: setRotation, setViewport and resetViewport will revert origin to top left corner of screen/sprite
void setOrigin(int32_t x, int32_t y);
int32_t getOriginX(void);
int32_t getOriginY(void);
void invertDisplay(bool i); // Tell TFT to invert all displayed colours
@ -491,6 +494,7 @@ class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has ac
void end_SDA_Read(void); // Restore MOSI to output
#endif
// Graphics drawing
void fillScreen(uint32_t color),
drawRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color),
@ -500,28 +504,6 @@ class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has ac
void fillRectVGradient(int16_t x, int16_t y, int16_t w, int16_t h, uint32_t color1, uint32_t color2);
void fillRectHGradient(int16_t x, int16_t y, int16_t w, int16_t h, uint32_t color1, uint32_t color2);
// Draw a pixel blended with the pixel colour on the TFT or sprite, return blended colour
// If bg_color is not included the background pixel colour will be read from TFT or sprite
uint16_t drawPixel(int32_t x, int32_t y, uint32_t color, uint8_t alpha, uint32_t bg_color = 0x00FFFFFF);
// Draw a small anti-aliased filled circle at ax,ay with radius r (uses drawWideLine)
// If bg_color is not included the background pixel colour will be read from TFT or sprite
void drawSpot(float ax, float ay, float r, uint32_t fg_color, uint32_t bg_color = 0x00FFFFFF);
// Draw an anti-aliased filled circle at x, y with radius r
// If bg_color is not included the background pixel colour will be read from TFT or sprite
void fillSmoothCircle(int32_t x, int32_t y, int32_t r, uint32_t color, uint32_t bg_color = 0x00FFFFFF);
void fillSmoothRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t radius, uint32_t color, uint32_t bg_color = 0x00FFFFFF);
// Draw an anti-aliased wide line from ax,ay to bx,by width wd with radiused ends (radius is wd/2)
// If bg_color is not included the background pixel colour will be read from TFT or sprite
void drawWideLine(float ax, float ay, float bx, float by, float wd, uint32_t fg_color, uint32_t bg_color = 0x00FFFFFF);
// Draw an anti-aliased wide line from ax,ay to bx,by with different width at each end aw, bw and with radiused ends
// If bg_color is not included the background pixel colour will be read from TFT or sprite
void drawWedgeLine(float ax, float ay, float bx, float by, float aw, float bw, uint32_t fg_color, uint32_t bg_color = 0x00FFFFFF);
void drawCircle(int32_t x, int32_t y, int32_t r, uint32_t color),
drawCircleHelper(int32_t x, int32_t y, int32_t r, uint8_t cornername, uint32_t color),
fillCircle(int32_t x, int32_t y, int32_t r, uint32_t color),
@ -534,6 +516,53 @@ class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has ac
drawTriangle(int32_t x1,int32_t y1, int32_t x2,int32_t y2, int32_t x3,int32_t y3, uint32_t color),
fillTriangle(int32_t x1,int32_t y1, int32_t x2,int32_t y2, int32_t x3,int32_t y3, uint32_t color);
// Smooth (anti-aliased) graphics drawing
// Draw a pixel blended with the background pixel colour (bg_color) specified, return blended colour
// If the bg_color is not specified, the background pixel colour will be read from TFT or sprite
uint16_t drawPixel(int32_t x, int32_t y, uint32_t color, uint8_t alpha, uint32_t bg_color = 0x00FFFFFF);
// Draw an anti-aliased (smooth) arc between start and end angles. Arc ends are anti-aliased.
// By default the arc is drawn with square ends unless the "roundEnds" parameter is included and set true
// Angle = 0 is at 6 o'clock position, 90 at 9 o'clock etc. The angles must be in range 0-360 or they will be clipped to these limits
// The start angle may be larger than the end angle. Arcs are always drawn clockwise from the start angle.
void drawSmoothArc(int32_t x, int32_t y, int32_t r, int32_t ir, int32_t startAngle, int32_t endAngle, uint32_t fg_color, uint32_t bg_color, bool roundEnds = false);
// As per "drawSmoothArc" except endAngle should be greater than startAngle (angles will be swapped otherwise)
// The sides of the arc are anti-aliased by default. If smoothArc is false sides will NOT be anti-aliased
// The ends of the arc are NOT anti-aliased, this facilitates dynamic arc length changes with arc segments and ensures clean segment joints
void drawArc(int32_t x, int32_t y, int32_t r, int32_t ir, int32_t startAngle, int32_t endAngle, uint32_t fg_color, uint32_t bg_color, bool smoothArc = true);
// Draw an anti-aliased filled circle at x, y with radius r
// Note: The thickness of line is 3 pixels to reduce the visible "braiding" effect of anti-aliasing narrow lines
// this means the inner anti-alias zone is always at r-1 and the outer zone at r+1
void drawSmoothCircle(int32_t x, int32_t y, int32_t r, uint32_t fg_color, uint32_t bg_color);
// Draw an anti-aliased filled circle at x, y with radius r
// If bg_color is not included the background pixel colour will be read from TFT or sprite
void fillSmoothCircle(int32_t x, int32_t y, int32_t r, uint32_t color, uint32_t bg_color = 0x00FFFFFF);
// Draw a rounded rectangle that has a line thickness of r-ir+1 and bounding box defined by x,y and w,h
// The outer corner radius is r, inner corner radius is ir
// The inside and outside of the border are anti-aliased
void drawSmoothRoundRect(int32_t x, int32_t y, int32_t r, int32_t ir, int32_t w, int32_t h, uint32_t fg_color, uint32_t bg_color = 0x00FFFFFF, uint8_t quadrants = 0xF);
// Draw a filled rounded rectangle , corner radius r and bounding box defined by x,y and w,h
void fillSmoothRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t radius, uint32_t color, uint32_t bg_color = 0x00FFFFFF);
// Draw a small anti-aliased filled circle at ax,ay with radius r (uses drawWideLine)
// If bg_color is not included the background pixel colour will be read from TFT or sprite
void drawSpot(float ax, float ay, float r, uint32_t fg_color, uint32_t bg_color = 0x00FFFFFF);
// Draw an anti-aliased wide line from ax,ay to bx,by width wd with radiused ends (radius is wd/2)
// If bg_color is not included the background pixel colour will be read from TFT or sprite
void drawWideLine(float ax, float ay, float bx, float by, float wd, uint32_t fg_color, uint32_t bg_color = 0x00FFFFFF);
// Draw an anti-aliased wide line from ax,ay to bx,by with different width at each end aw, bw and with radiused ends
// If bg_color is not included the background pixel colour will be read from TFT or sprite
void drawWedgeLine(float ax, float ay, float bx, float by, float aw, float bw, uint32_t fg_color, uint32_t bg_color = 0x00FFFFFF);
// Image rendering
// Swap the byte order for pushImage() and pushPixels() - corrects endianness
void setSwapBytes(bool swap);
@ -572,11 +601,16 @@ class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has ac
void pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *data, uint8_t transparent, bool bpp8 = true, uint16_t *cmap = nullptr);
// FLASH version
void pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint8_t *data, bool bpp8, uint16_t *cmap = nullptr);
// Render a 16 bit colour image with a 1bpp mask
void pushMaskedImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *img, uint8_t *mask);
// This next function has been used successfully to dump the TFT screen to a PC for documentation purposes
// It reads a screen area and returns the 3 RGB 8 bit colour values of each pixel in the buffer
// Set w and h to 1 to read 1 pixel's colour. The data buffer must be at least w * h * 3 bytes
void readRectRGB(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *data);
// Text rendering - value returned is the pixel width of the rendered text
int16_t drawNumber(long intNumber, int32_t x, int32_t y, uint8_t font), // Draw integer using specified font number
drawNumber(long intNumber, int32_t x, int32_t y), // Draw integer using current font
@ -598,6 +632,7 @@ class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has ac
drawCentreString(const String& string, int32_t x, int32_t y, uint8_t font),// Deprecated, use setTextDatum() and drawString()
drawRightString(const String& string, int32_t x, int32_t y, uint8_t font); // Deprecated, use setTextDatum() and drawString()
// Text rendering and font handling support funtions
void setCursor(int16_t x, int16_t y), // Set cursor for tft.print()
setCursor(int16_t x, int16_t y, uint8_t font); // Set cursor and font number for tft.print()
@ -638,13 +673,14 @@ class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has ac
// Support function to UTF8 decode and draw characters piped through print stream
size_t write(uint8_t);
// size_t write(const uint8_t *buf, size_t len);
// size_t write(const uint8_t *buf, size_t len);
// Used by Smooth font class to fetch a pixel colour for the anti-aliasing
void setCallback(getColorCallback getCol);
uint16_t fontsLoaded(void); // Each bit in returned value represents a font type that is loaded - used for debug/error handling only
// Low level read/write
void spiwrite(uint8_t); // legacy support only
#ifndef RM68120_DRIVER
@ -678,15 +714,15 @@ class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has ac
// Alpha blend 2 colours, see generic "alphaBlend_Test" example
// alpha = 0 = 100% background colour
// alpha = 255 = 100% foreground colour
uint16_t alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc);
inline uint16_t alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc);
// 16 bit colour alphaBlend with alpha dither (dither reduces colour banding)
uint16_t alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc, uint8_t dither);
// 24 bit colour alphaBlend with optional alpha dither
uint32_t alphaBlend24(uint8_t alpha, uint32_t fgc, uint32_t bgc, uint8_t dither = 0);
// DMA support functions - these are currently just for SPI writes when using the ESP32 or STM32 processors
// DMA works also on RP2040 and PIO SPI, 8 bit parallel and 16 bit parallel
// Direct Memory Access (DMA) support functions
// These can be used for SPI writes when using the ESP32 (original) or STM32 processors.
// DMA also works on a RP2040 processor with PIO based SPI and parallel (8 and 16 bit) interfaces
// Bear in mind DMA will only be of benefit in particular circumstances and can be tricky
// to manage by noobs. The functions have however been designed to be noob friendly and
// avoid a few DMA behaviour "gotchas".
@ -718,8 +754,13 @@ class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has ac
// Push an image to the TFT using DMA, buffer is optional and grabs (double buffers) a copy of the image
// Use the buffer if the image data will get over-written or destroyed while DMA is in progress
// If swapping colour bytes is defined, and the double buffer option is NOT used, then the bytes
// in the original data image will be swapped by the function before DMA is initiated.
//
// Note 1: If swapping colour bytes is defined, and the double buffer option is NOT used, then the bytes
// in the original image buffer content will be byte swapped by the function before DMA is initiated.
//
// Note 2: If part of the image will be off screen or outside of a set viewport, then the the original
// image buffer content will be altered to a correctly clipped image before DMA is initiated.
//
// The function will wait for the last DMA to complete if it is called while a previous DMA is still
// in progress, this simplifies the sketch and helps avoid "gotchas".
void pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t* data, uint16_t* buffer = nullptr);
@ -812,6 +853,9 @@ class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has ac
// Single GPIO input/output direction control
void gpioMode(uint8_t gpio, uint8_t mode);
// Smooth graphics helper
uint8_t sqrt_fraction(uint32_t num);
// Helper function: calculate distance of a point from a finite length line between two points
float wedgeLineDistance(float pax, float pay, float bax, float bay, float dr);
@ -917,6 +961,10 @@ class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has ac
}; // End of class TFT_eSPI
// Swap any type
template <typename T> static inline void
transpose(T& a, T& b) { T t = a; a = b; b = t; }
/***************************************************************************************
** Section 10: Additional extension classes
***************************************************************************************/

View File

@ -11,7 +11,7 @@
#include <Timezone.h>
// Choose library to load
#ifdef ESP8266
#ifdef ARDUINO_ARCH_ESP8266
// ESP8266
#include <ESP8266WiFi.h>
#elif (defined(ARDUINO_ARCH_MBED) || defined(ARDUINO_ARCH_RP2040)) && !defined(ARDUINO_RASPBERRY_PI_PICO_W)

View File

@ -19,7 +19,7 @@
TFT_eSPI tft = TFT_eSPI(); // Invoke library
#ifdef ESP8266
#ifdef ARDUINO_ARCH_ESP8266
ADC_MODE(ADC_VCC); // Read the supply voltage
#endif
@ -45,15 +45,15 @@ Serial.print("\n[code]\n");
Serial.print ("TFT_eSPI ver = "); Serial.println(user.version);
printProcessorName();
#if defined (ESP32) || defined (ESP8266)
#if defined (ESP32) || defined (ARDUINO_ARCH_ESP8266)
if (user.esp < 0x32F000 || user.esp > 0x32FFFF) { Serial.print("Frequency = "); Serial.print(ESP.getCpuFreqMHz());Serial.println("MHz"); }
#endif
#ifdef ESP8266
#ifdef ARDUINO_ARCH_ESP8266
Serial.print("Voltage = "); Serial.print(ESP.getVcc() / 918.0); Serial.println("V"); // 918 empirically determined
#endif
Serial.print("Transactions = "); Serial.println((user.trans == 1) ? "Yes" : "No");
Serial.print("Interface = "); Serial.println((user.serial == 1) ? "SPI" : "Parallel");
#ifdef ESP8266
#ifdef ARDUINO_ARCH_ESP8266
if (user.serial == 1){ Serial.print("SPI overlap = "); Serial.println((user.overlap == 1) ? "Yes\n" : "No\n"); }
#endif
if (user.tft_driver != 0xE9D) // For ePaper displays the size is defined in the sketch
@ -78,7 +78,7 @@ if (user.pin_tft_mosi != -1) { Serial.print("MOSI = "); Serial.print("GPIO ")
if (user.pin_tft_miso != -1) { Serial.print("MISO = "); Serial.print("GPIO "); Serial.println(getPinName(user.pin_tft_miso)); }
if (user.pin_tft_clk != -1) { Serial.print("SCK = "); Serial.print("GPIO "); Serial.println(getPinName(user.pin_tft_clk)); }
#ifdef ESP8266
#ifdef ARDUINO_ARCH_ESP8266
if (user.overlap == true)
{
Serial.println("Overlap selected, following pins MUST be used:");
@ -92,7 +92,7 @@ if (user.overlap == true)
}
#endif
String pinNameRef = "GPIO ";
#ifdef ESP8266
#ifdef ARDUINO_ARCH_ESP8266
pinNameRef = "PIN_D";
#endif

View File

@ -18,6 +18,9 @@ pushColor KEYWORD2
setRotation KEYWORD2
getRotation KEYWORD2
setOrigin KEYWORD2
getOriginX KEYWORD2
getOriginY KEYWORD2
invertDisplay KEYWORD2
setAddrWindow KEYWORD2
@ -44,6 +47,8 @@ end_SDA_Read KEYWORD2
fillScreen KEYWORD2
drawRect KEYWORD2
fillRectHGradient KEYWORD2
fillRectVGradient KEYWORD2
drawRoundRect KEYWORD2
fillRoundRect KEYWORD2
@ -69,6 +74,7 @@ getPivotY KEYWORD2
readRect KEYWORD2
pushRect KEYWORD2
pushImage KEYWORD2
pushMaskedImage KEYWORD2
readRectRGB KEYWORD2
drawNumber KEYWORD2
@ -140,10 +146,12 @@ calibrateTouch KEYWORD2
setTouch KEYWORD2
# Smooth (anti-aliased) graphics functions
fillRectHGradient KEYWORD2
fillRectVGradient KEYWORD2
drawSmoothCircle KEYWORD2
fillSmoothCircle KEYWORD2
drawSmoothRoundRect KEYWORD2
fillSmoothRoundRect KEYWORD2
drawSmoothArc KEYWORD2
drawArc KEYWORD2
drawSpot KEYWORD2
drawWideLine KEYWORD2
drawWedgeLine KEYWORD2