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 // 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) void TFT_eSprite::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1)
{ {
if (x0 > x1) swap_coord(x0, x1); if (x0 > x1) transpose(x0, x1);
if (y0 > y1) swap_coord(y0, y1); if (y0 > y1) transpose(y0, y1);
int32_t w = width(); int32_t w = width();
int32_t h = height(); 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); bool steep = abs(y1 - y0) > abs(x1 - x0);
if (steep) { if (steep) {
swap_coord(x0, y0); transpose(x0, y0);
swap_coord(x1, y1); transpose(x1, y1);
} }
if (x0 > x1) { if (x0 > x1) {
swap_coord(x0, x1); transpose(x0, x1);
swap_coord(y0, y1); transpose(y0, y1);
} }
int32_t dx = x1 - x0, dy = abs(y1 - y0);; int32_t dx = x1 - x0, dy = abs(y1 - y0);;

View File

@ -19,7 +19,7 @@
#define DMA_BUSY_CHECK // DMA not available, leave blank #define DMA_BUSY_CHECK // DMA not available, leave blank
// Initialise processor specific SPI functions, used by init() // 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 \ #define INIT_TFT_DATA_BUS \
spi.setBitOrder(MSBFIRST); \ spi.setBitOrder(MSBFIRST); \
spi.setDataMode(TFT_SPI_MODE); \ 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: These are created in header file:

View File

@ -65,9 +65,20 @@
#define DMA_BUSY_CHECK #define DMA_BUSY_CHECK
#endif #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 #if !defined (RP2040_PIO_INTERFACE) // SPI
#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() // Initialise processor specific SPI functions, used by init()
#define INIT_TFT_DATA_BUS // Not used #define INIT_TFT_DATA_BUS // Not used
#endif
// Wait for tx to end, flush rx FIFO, clear rx overrun // 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) {}; \ #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 #if !defined (RP2040_PIO_INTERFACE)// SPI
//#define DC_C sio_hw->gpio_clr = (1ul << TFT_DC) //#define DC_C sio_hw->gpio_clr = (1ul << TFT_DC)
//#define DC_D sio_hw->gpio_set = (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_C digitalWrite(TFT_DC, LOW);
#define DC_D digitalWrite(TFT_DC, HIGH); #define DC_D digitalWrite(TFT_DC, HIGH);
#else #else
@ -167,7 +178,7 @@
#define CS_H // No macro allocated so it generates no code #define CS_H // No macro allocated so it generates no code
#else #else
#if !defined (RP2040_PIO_INTERFACE) // SPI #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_L digitalWrite(TFT_CS, LOW);
#define CS_H digitalWrite(TFT_CS, HIGH); #define CS_H digitalWrite(TFT_CS, HIGH);
#else #else
@ -287,7 +298,28 @@
// Macros to write commands/pixel colour data to other displays // Macros to write commands/pixel colour data to other displays
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
#else #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_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_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)) #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; SET_BUS_READ_MODE;
} }
/*************************************************************************************** /***************************************************************************************
** Function name: end_tft_read (was called spi_end_read) ** Function name: end_tft_read (was called spi_end_read)
** Description: End transaction for reads and deselect TFT ** 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(" _vpX=");Serial.print( _vpX);Serial.print(", _vpY=");Serial.print( _vpY);
//Serial.print(", _vpW=");Serial.print(_vpW);Serial.print(", _vpH=");Serial.println(_vpH); //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) ** Function name: commandList, used for FLASH based lists only (e.g. ST7735)
** Description: Get initialisation commands from FLASH and send to TFT ** 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(); 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 ** 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) // Sort coordinates by Y order (y2 >= y1 >= y0)
if (y0 > y1) { if (y0 > y1) {
swap_coord(y0, y1); swap_coord(x0, x1); transpose(y0, y1); transpose(x0, x1);
} }
if (y1 > y2) { if (y1 > y2) {
swap_coord(y2, y1); swap_coord(x2, x1); transpose(y2, y1); transpose(x2, x1);
} }
if (y0 > y1) { 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 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; sa += dx01;
sb += dx02; sb += dx02;
if (a > b) swap_coord(a, b); if (a > b) transpose(a, b);
drawFastHLine(a, y, b - a + 1, color); 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; sa += dx12;
sb += dx02; sb += dx02;
if (a > b) swap_coord(a, b); if (a > b) transpose(a, b);
drawFastHLine(a, y, b - a + 1, color); drawFastHLine(a, y, b - a + 1, color);
} }
@ -2837,15 +2962,6 @@ uint16_t TFT_eSPI::getTextPadding(void)
return padX; 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 ** Function name: getTextDatum
** Description: Return the text datum value (as used by setTextDatum()) ** 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; addr_col = 0xFFFF;
#if defined (ILI9225_DRIVER) #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; SPI_BUSY_CHECK;
DC_C; tft_Write_8(TFT_CASET1); DC_C; tft_Write_8(TFT_CASET1);
DC_D; tft_Write_16(x0); 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 #endif
#elif defined (SSD1351_DRIVER) #elif defined (SSD1351_DRIVER)
if (rotation & 1) { if (rotation & 1) {
swap_coord(x0, y0); transpose(x0, y0);
swap_coord(x1, y1); transpose(x1, y1);
} }
SPI_BUSY_CHECK; SPI_BUSY_CHECK;
DC_C; tft_Write_8(TFT_CASET); 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; DC_D;
#else #else
#if defined (SSD1963_DRIVER) #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 #endif
#ifdef CGRAM_OFFSET #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; addr_row = 0xFFFF;
#if defined (SSD1963_DRIVER) #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 #endif
#ifdef CGRAM_OFFSET #ifdef CGRAM_OFFSET
@ -3420,7 +3536,7 @@ void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color)
begin_tft_write(); begin_tft_write();
#if defined (ILI9225_DRIVER) #if defined (ILI9225_DRIVER)
if (rotation & 0x01) { swap_coord(x, y); } if (rotation & 0x01) { transpose(x, y); }
SPI_BUSY_CHECK; SPI_BUSY_CHECK;
// Set window to full screen to optimise sequential pixel rendering // 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) #elif (defined (ARDUINO_ARCH_RP2040) || defined (ARDUINO_ARCH_MBED)) && !defined (SSD1351_DRIVER)
#if defined (SSD1963_DRIVER) #if defined (SSD1963_DRIVER)
if ((rotation & 0x1) == 0) { swap_coord(x, y); } if ((rotation & 0x1) == 0) { transpose(x, y); }
#endif #endif
#if !defined(RP2040_PIO_INTERFACE) #if !defined(RP2040_PIO_INTERFACE)
@ -3535,13 +3651,13 @@ void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color)
#else #else
#if defined (SSD1963_DRIVER) #if defined (SSD1963_DRIVER)
if ((rotation & 0x1) == 0) { swap_coord(x, y); } if ((rotation & 0x1) == 0) { transpose(x, y); }
#endif #endif
SPI_BUSY_CHECK; SPI_BUSY_CHECK;
#if defined (SSD1351_DRIVER) #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) // No need to send x if it has not changed (speeds things up)
if (addr_col != x) { if (addr_col != x) {
DC_C; tft_Write_8(TFT_CASET); 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); bool steep = abs(y1 - y0) > abs(x1 - x0);
if (steep) { if (steep) {
swap_coord(x0, y0); transpose(x0, y0);
swap_coord(x1, y1); transpose(x1, y1);
} }
if (x0 > x1) { if (x0 > x1) {
swap_coord(x0, x1); transpose(x0, x1);
swap_coord(y0, y1); transpose(y0, y1);
} }
int32_t dx = x1 - x0, dy = abs(y1 - y0);; 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 PixelAlphaGain = 255.0;
constexpr float LoAlphaTheshold = 1.0/32.0; constexpr float LoAlphaTheshold = 1.0/32.0;
constexpr float HiAlphaTheshold = 1.0 - LoAlphaTheshold; constexpr float HiAlphaTheshold = 1.0 - LoAlphaTheshold;
constexpr float deg2rad = 3.14159265359/180.0;
/*************************************************************************************** /***************************************************************************************
** Function name: drawPixel (alpha blended) ** 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; 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 ** Function name: fillSmoothCircle
** Description: Draw a filled anti-aliased circle ** 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; int32_t hyp2 = (r - cx) * (r - cx) + dy2;
if (hyp2 <= r1) break; if (hyp2 <= r1) break;
if (hyp2 >= r2) continue; 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); float alphaf = (float)r - sqrtf(hyp2);
if (alphaf > HiAlphaTheshold) break; if (alphaf > HiAlphaTheshold) break;
xs = cx; xs = cx;
if (alphaf < LoAlphaTheshold) continue; if (alphaf < LoAlphaTheshold) continue;
uint8_t alpha = alphaf * 255; uint8_t alpha = alphaf * 255;
//*/
if (bg_color == 0x00FFFFFF) { 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);
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 ** Function name: fillSmoothRoundRect
** Description: Draw a filled anti-aliased rounded corner rectangle ** Description: Draw a filled anti-aliased rounded corner rectangle
@ -3823,6 +4345,7 @@ 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) 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; inTransaction = true;
int32_t xs = 0; int32_t xs = 0;
int32_t cx = 0; int32_t cx = 0;
@ -3834,9 +4357,11 @@ void TFT_eSPI::fillSmoothRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, i
y += r; y += r;
h -= 2*r; h -= 2*r;
fillRect(x, y, w, h, color); fillRect(x, y, w, h, color);
h--; h--;
x += r; x += r;
w -= 2*r+1; w -= 2*r+1;
int32_t r1 = r * r; int32_t r1 = r * r;
r++; r++;
int32_t r2 = 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; int32_t hyp2 = (r - cx) * (r - cx) + dy2;
if (hyp2 <= r1) break; if (hyp2 <= r1) break;
if (hyp2 >= r2) continue; 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); float alphaf = (float)r - sqrtf(hyp2);
if (alphaf > HiAlphaTheshold) break; if (alphaf > HiAlphaTheshold) break;
xs = cx; xs = cx;
if (alphaf < LoAlphaTheshold) continue; if (alphaf < LoAlphaTheshold) continue;
uint8_t alpha = alphaf * 255; uint8_t alpha = alphaf * 255;
*/
drawPixel(x + cx - r, y + cy - r, color, alpha, bg_color); 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, color, alpha, bg_color);
drawPixel(x - cx + r + w, y - cy + r + h, 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 ** Function name: drawSpot - maths intensive, so for small filled circles
** Description: Draw an anti-aliased filled circle at ax,ay with radius r ** 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) 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 // 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) 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 ( (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 // Find line bounding box
int32_t x0 = (int32_t)floorf(fminf(ax-ar, bx-br)); 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); pushColor(fg_color);
continue; continue;
} }
//Blend color with background and plot //Blend colour with background and plot
if (bg_color == 0x00FFFFFF) { if (bg_color == 0x00FFFFFF) {
bg = readPixel(xp, yp); swin = true; 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(); end_nin_write();
} }
// Calculate distance of px,py to closest part of line
/*************************************************************************************** /***************************************************************************************
** Function name: lineDistance - private helper function for drawWedgeLine ** Function name: lineDistance - private helper function for drawWedgeLine
** Description: returns distance of px,py to closest part of a to b wedge ** 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 ** Function name: alphaBlend
** Description: Blend 16bit foreground and background ** Description: Blend 16bit foreground and background
*************************************************************************************x*/ *************************************************************************************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 uint32_t rxb = bgc & 0xF81F;
uint16_t fgR = ((fgc >> 10) & 0x3E) + 1; rxb += ((fgc & 0xF81F) - rxb) * (alpha >> 2) >> 6;
uint16_t fgG = ((fgc >> 4) & 0x7E) + 1; uint32_t xgx = bgc & 0x07E0;
uint16_t fgB = ((fgc << 1) & 0x3E) + 1; xgx += ((fgc & 0x07E0) - xgx) * alpha >> 8;
return (rxb & 0xF81F) | (xgx & 0x07E0);
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);
} }
/*************************************************************************************** /***************************************************************************************
@ -4427,22 +4946,13 @@ uint32_t TFT_eSPI::alphaBlend24(uint8_t alpha, uint32_t fgc, uint32_t bgc, uint8
if (alphaDither >255) alpha = 255; if (alphaDither >255) alpha = 255;
} }
// For speed use fixed point maths and rounding to permit a power of 2 division uint32_t rxx = bgc & 0xFF0000;
uint16_t fgR = ((fgc >> 15) & 0x1FE) + 1; rxx += ((fgc & 0xFF0000) - rxx) * alpha >> 8;
uint16_t fgG = ((fgc >> 7) & 0x1FE) + 1; uint32_t xgx = bgc & 0x00FF00;
uint16_t fgB = ((fgc << 1) & 0x1FE) + 1; xgx += ((fgc & 0xFF0000) - xgx) * alpha >> 8;
uint32_t xxb = bgc & 0x0000FF;
uint16_t bgR = ((bgc >> 15) & 0x1FE) + 1; xxb += ((fgc & 0xFF0000) - xxb) * alpha >> 8;
uint16_t bgG = ((bgc >> 7) & 0x1FE) + 1; return (rxx & 0xFF0000) | (xgx & 0x00FF00) | (xxb & 0x0000FF);
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);
} }
/*************************************************************************************** /***************************************************************************************

View File

@ -16,7 +16,7 @@
#ifndef _TFT_eSPIH_ #ifndef _TFT_eSPIH_
#define _TFT_eSPIH_ #define _TFT_eSPIH_
#define TFT_ESPI_VERSION "2.4.79" #define TFT_ESPI_VERSION "2.5.81b"
// Bit level feature flags // Bit level feature flags
// Bit 0 set: viewport capability // 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 ** 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 // Callback prototype for smooth font pixel colour read
typedef uint16_t (*getColorCallback)(uint16_t x, uint16_t y); 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 void setRotation(uint8_t r); // Set the display image orientation to 0, 1, 2 or 3
uint8_t getRotation(void); // Read the current rotation 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 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 void end_SDA_Read(void); // Restore MOSI to output
#endif #endif
// Graphics drawing // Graphics drawing
void fillScreen(uint32_t color), void fillScreen(uint32_t color),
drawRect(int32_t x, int32_t y, int32_t w, int32_t h, 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 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); 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), 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), 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), 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), 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); 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 // Image rendering
// Swap the byte order for pushImage() and pushPixels() - corrects endianness // Swap the byte order for pushImage() and pushPixels() - corrects endianness
void setSwapBytes(bool swap); 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); 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 // 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); 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 // 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 // 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 // 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); 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 // 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 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 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() 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() 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 // Text rendering and font handling support funtions
void setCursor(int16_t x, int16_t y), // Set cursor for tft.print() 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() setCursor(int16_t x, int16_t y, uint8_t font); // Set cursor and font number for tft.print()
@ -645,6 +680,7 @@ class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has ac
uint16_t fontsLoaded(void); // Each bit in returned value represents a font type that is loaded - used for debug/error handling only 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 // Low level read/write
void spiwrite(uint8_t); // legacy support only void spiwrite(uint8_t); // legacy support only
#ifndef RM68120_DRIVER #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 blend 2 colours, see generic "alphaBlend_Test" example
// alpha = 0 = 100% background colour // alpha = 0 = 100% background colour
// alpha = 255 = 100% foreground 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) // 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); uint16_t alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc, uint8_t dither);
// 24 bit colour alphaBlend with optional alpha 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); uint32_t alphaBlend24(uint8_t alpha, uint32_t fgc, uint32_t bgc, uint8_t dither = 0);
// Direct Memory Access (DMA) support functions
// DMA support functions - these are currently just for SPI writes when using the ESP32 or STM32 processors // These can be used for SPI writes when using the ESP32 (original) or STM32 processors.
// DMA works also on RP2040 and PIO SPI, 8 bit parallel and 16 bit parallel // 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 // 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 // to manage by noobs. The functions have however been designed to be noob friendly and
// avoid a few DMA behaviour "gotchas". // 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 // 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 // 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 // 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". // 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); 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 // Single GPIO input/output direction control
void gpioMode(uint8_t gpio, uint8_t mode); 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 // 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); 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 }; // 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 ** Section 10: Additional extension classes
***************************************************************************************/ ***************************************************************************************/

View File

@ -11,7 +11,7 @@
#include <Timezone.h> #include <Timezone.h>
// Choose library to load // Choose library to load
#ifdef ESP8266 #ifdef ARDUINO_ARCH_ESP8266
// ESP8266 // ESP8266
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#elif (defined(ARDUINO_ARCH_MBED) || defined(ARDUINO_ARCH_RP2040)) && !defined(ARDUINO_RASPBERRY_PI_PICO_W) #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 TFT_eSPI tft = TFT_eSPI(); // Invoke library
#ifdef ESP8266 #ifdef ARDUINO_ARCH_ESP8266
ADC_MODE(ADC_VCC); // Read the supply voltage ADC_MODE(ADC_VCC); // Read the supply voltage
#endif #endif
@ -45,15 +45,15 @@ Serial.print("\n[code]\n");
Serial.print ("TFT_eSPI ver = "); Serial.println(user.version); Serial.print ("TFT_eSPI ver = "); Serial.println(user.version);
printProcessorName(); 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"); } if (user.esp < 0x32F000 || user.esp > 0x32FFFF) { Serial.print("Frequency = "); Serial.print(ESP.getCpuFreqMHz());Serial.println("MHz"); }
#endif #endif
#ifdef ESP8266 #ifdef ARDUINO_ARCH_ESP8266
Serial.print("Voltage = "); Serial.print(ESP.getVcc() / 918.0); Serial.println("V"); // 918 empirically determined Serial.print("Voltage = "); Serial.print(ESP.getVcc() / 918.0); Serial.println("V"); // 918 empirically determined
#endif #endif
Serial.print("Transactions = "); Serial.println((user.trans == 1) ? "Yes" : "No"); Serial.print("Transactions = "); Serial.println((user.trans == 1) ? "Yes" : "No");
Serial.print("Interface = "); Serial.println((user.serial == 1) ? "SPI" : "Parallel"); 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"); } if (user.serial == 1){ Serial.print("SPI overlap = "); Serial.println((user.overlap == 1) ? "Yes\n" : "No\n"); }
#endif #endif
if (user.tft_driver != 0xE9D) // For ePaper displays the size is defined in the sketch 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_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)); } 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) if (user.overlap == true)
{ {
Serial.println("Overlap selected, following pins MUST be used:"); Serial.println("Overlap selected, following pins MUST be used:");
@ -92,7 +92,7 @@ if (user.overlap == true)
} }
#endif #endif
String pinNameRef = "GPIO "; String pinNameRef = "GPIO ";
#ifdef ESP8266 #ifdef ARDUINO_ARCH_ESP8266
pinNameRef = "PIN_D"; pinNameRef = "PIN_D";
#endif #endif

View File

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