From b6db90ada464a7410f879ee772f140815ba2660a Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 3 Feb 2022 15:37:44 +0000 Subject: [PATCH] Add new anit-aliased graphics functions Examples to follow. --- Extensions/Sprite.cpp | 4 +- Extensions/Sprite.h | 8 +- Processors/TFT_eSPI_ESP32.h | 4 + Processors/TFT_eSPI_RP2040.h | 14 +- TFT_eSPI.cpp | 363 +++++++++++++++++++++++++++++++---- TFT_eSPI.h | 58 +++++- keywords.txt | 9 + library.json | 2 +- library.properties | 2 +- 9 files changed, 408 insertions(+), 56 deletions(-) diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index 723cbde..4177f52 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -1292,7 +1292,7 @@ void TFT_eSprite::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) ** Function name: pushColor ** Description: Send a new pixel to the set window ***************************************************************************************/ -void TFT_eSprite::pushColor(uint32_t color) +void TFT_eSprite::pushColor(uint16_t color) { if (!_created ) return; @@ -1334,7 +1334,7 @@ void TFT_eSprite::pushColor(uint32_t color) ** Function name: pushColor ** Description: Send a "len" new pixels to the set window ***************************************************************************************/ -void TFT_eSprite::pushColor(uint32_t color, uint16_t len) +void TFT_eSprite::pushColor(uint16_t color, uint32_t len) { if (!_created ) return; diff --git a/Extensions/Sprite.h b/Extensions/Sprite.h index 951099b..8e27259 100644 --- a/Extensions/Sprite.h +++ b/Extensions/Sprite.h @@ -63,9 +63,9 @@ class TFT_eSprite : public TFT_eSPI { // Colours are converted to the set Sprite colour bit depth setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1), // Push a color (aka singe pixel) to the screen - pushColor(uint32_t color), + pushColor(uint16_t color), // Push len colors (pixels) to the screen - pushColor(uint32_t color, uint16_t len), + pushColor(uint16_t color, uint32_t len), // Push a pixel preformatted as a 8 or 16 bit colour (avoids conversion overhead) writeColor(uint16_t color), @@ -149,6 +149,10 @@ class TFT_eSprite : public TFT_eSPI { // Reserve memory for the Sprite and return a pointer void* callocSprite(int16_t width, int16_t height, uint8_t frames = 1); + // Override the non-inlined TFT_eSPI functions + void begin_nin_write(void) { ; } + void end_nin_write(void) { ; } + protected: uint8_t _bpp; // bits per pixel (1, 8 or 16) diff --git a/Processors/TFT_eSPI_ESP32.h b/Processors/TFT_eSPI_ESP32.h index 7d2729e..addac2b 100644 --- a/Processors/TFT_eSPI_ESP32.h +++ b/Processors/TFT_eSPI_ESP32.h @@ -426,6 +426,10 @@ SPI3_HOST = 2 #define RD_L #define RD_H #endif + #else + #define TFT_RD -1 + #define RD_L + #define RD_H #endif //////////////////////////////////////////////////////////////////////////////////////// diff --git a/Processors/TFT_eSPI_RP2040.h b/Processors/TFT_eSPI_RP2040.h index e811428..f863aa8 100644 --- a/Processors/TFT_eSPI_RP2040.h +++ b/Processors/TFT_eSPI_RP2040.h @@ -160,8 +160,20 @@ // Make sure TFT_RD is defined if not used to avoid an error message //////////////////////////////////////////////////////////////////////////////////////// // At the moment read is not supported for parallel mode, tie TFT signal high -#ifndef TFT_RD +#ifdef TFT_RD + #if (TFT_RD >= 0) + #define RD_L sio_hw->gpio_clr = (1ul << TFT_RD) + //#define RD_L digitalWrite(TFT_WR, LOW) + #define RD_H sio_hw->gpio_set = (1ul << TFT_RD) + //#define RD_H digitalWrite(TFT_WR, HIGH) + #else + #define RD_L + #define RD_H + #endif +#else #define TFT_RD -1 + #define RD_L + #define RD_H #endif //////////////////////////////////////////////////////////////////////////////////////// diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 9703e71..ab64c6a 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -53,6 +53,15 @@ \ if (dw < 1 || dh < 1) return; +/*************************************************************************************** +** Function name: Legacy - deprecated +** Description: Start/end transaction +***************************************************************************************/ + void TFT_eSPI::spi_begin() {begin_tft_write();} + void TFT_eSPI::spi_end() { end_tft_write();} + void TFT_eSPI::spi_begin_read() {begin_tft_read(); } + void TFT_eSPI::spi_end_read() { end_tft_read(); } + /*************************************************************************************** ** Function name: begin_tft_write (was called spi_begin) ** Description: Start SPI transaction for writes and select TFT @@ -68,6 +77,18 @@ inline void TFT_eSPI::begin_tft_write(void){ } } +// Non-inlined version to permit override +void TFT_eSPI::begin_nin_write(void){ + if (locked) { + locked = false; // Flag to show SPI access now unlocked +#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) + spi.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, TFT_SPI_MODE)); +#endif + CS_L; + SET_BUS_WRITE_MODE; // Some processors (e.g. ESP32) allow recycling the tx buffer when rx is not used + } +} + /*************************************************************************************** ** Function name: end_tft_write (was called spi_end) ** Description: End transaction for write and deselect TFT @@ -86,6 +107,21 @@ inline void TFT_eSPI::end_tft_write(void){ } } +// Non-inlined version to permit override +inline void TFT_eSPI::end_nin_write(void){ + if(!inTransaction) { // Flag to stop ending transaction during multiple graphics calls + if (!locked) { // Locked when beginTransaction has been called + locked = true; // Flag to show SPI access now locked + SPI_BUSY_CHECK; // Check send complete and clean out unused rx data + CS_H; +#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) + spi.endTransaction(); +#endif + } + SET_BUS_READ_MODE; // In case SPI has been configured for tx only + } +} + /*************************************************************************************** ** Function name: begin_tft_read (was called spi_begin_read) ** Description: Start transaction for reads and select TFT @@ -108,6 +144,29 @@ 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 +***************************************************************************************/ +inline void TFT_eSPI::end_tft_read(void){ +#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) + if(!inTransaction) { + if (!locked) { + locked = true; + CS_H; + spi.endTransaction(); + } + } +#else + #if !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) + spi.setFrequency(SPI_FREQUENCY); + #endif + if(!inTransaction) {CS_H;} +#endif + SET_BUS_WRITE_MODE; +} + /*************************************************************************************** ** Function name: setViewport ** Description: Set the clipping region for the TFT screen @@ -183,7 +242,7 @@ bool TFT_eSPI::checkViewport(int32_t x, int32_t y, int32_t w, int32_t h) x+= _xDatum; y+= _yDatum; - if ((x >= _vpW) || (y >= _vpH)) return false; + if ((x >= _vpW) || (y >= _vpH)) return false; int32_t dx = 0; int32_t dy = 0; @@ -290,7 +349,7 @@ void TFT_eSPI::frameViewport(uint16_t color, int32_t w) // a large negative width will clear the screen outside the viewport { w = -w; - + // Save old values int32_t _xT = _vpX; _vpX = 0; int32_t _yT = _vpY; _vpY = 0; @@ -320,35 +379,55 @@ void TFT_eSPI::frameViewport(uint16_t color, int32_t w) } /*************************************************************************************** -** Function name: end_tft_read (was called spi_end_read) -** Description: End transaction for reads and deselect TFT +** Function name: clipAddrWindow +** Description: Clip address window x,y,w,h to screen and viewport ***************************************************************************************/ -inline void TFT_eSPI::end_tft_read(void){ -#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) - if(!inTransaction) { - if (!locked) { - locked = true; - CS_H; - spi.endTransaction(); - } - } -#else - #if !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) - spi.setFrequency(SPI_FREQUENCY); - #endif - if(!inTransaction) {CS_H;} -#endif - SET_BUS_WRITE_MODE; +bool TFT_eSPI::clipAddrWindow(int32_t *x, int32_t *y, int32_t *w, int32_t *h) +{ + if (_vpOoB) return false; // Area is outside of viewport + + *x+= _xDatum; + *y+= _yDatum; + + if ((*x >= _vpW) || (*y >= _vpH)) return false; // Area is outside of viewport + + // Crop drawing area bounds + if (*x < _vpX) { *w -= _vpX - *x; *x = _vpX; } + if (*y < _vpY) { *h -= _vpY - *y; *y = _vpY; } + + if ((*x + *w) > _vpW ) *w = _vpW - *x; + if ((*y + *h) > _vpH ) *h = _vpH - *y; + + if (*w < 1 || *h < 1) return false; // No area is inside viewport + + return true; // Area is wholly or partially inside viewport } /*************************************************************************************** -** Function name: Legacy - deprecated -** Description: Start/end transaction +** Function name: clipWindow +** Description: Clip window xs,yx,xe,ye to screen and viewport ***************************************************************************************/ - void TFT_eSPI::spi_begin() {begin_tft_write();} - void TFT_eSPI::spi_end() { end_tft_write();} - void TFT_eSPI::spi_begin_read() {begin_tft_read(); } - void TFT_eSPI::spi_end_read() { end_tft_read(); } +bool TFT_eSPI::clipWindow(int32_t *xs, int32_t *ys, int32_t *xe, int32_t *ye) +{ + if (_vpOoB) return false; // Area is outside of viewport + + *xs+= _xDatum; + *ys+= _yDatum; + *xe+= _xDatum; + *ye+= _yDatum; + + if ((*xs >= _vpW) || (*ys >= _vpH)) return false; // Area is outside of viewport + if ((*xe < _vpX) || (*ye < _vpY)) return false; // Area is outside of viewport + + // Crop drawing area bounds + if (*xs < _vpX) *xs = _vpX; + if (*ys < _vpY) *ys = _vpY; + + if (*xe > _vpW) *xe = _vpW - 1; + if (*ye > _vpH) *ye = _vpH - 1; + + return true; // Area is wholly or partially inside viewport +} /*************************************************************************************** ** Function name: TFT_eSPI @@ -1896,7 +1975,7 @@ void TFT_eSPI::readRectRGB(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_ uint8_t* buf565 = data + len; readRect(x0, y0, w, h, (uint16_t*)buf565); - + while (len--) { uint16_t pixel565 = (*buf565++)<<8; pixel565 |= *buf565++; @@ -1975,7 +2054,7 @@ void TFT_eSPI::drawCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color) int32_t xs = -1; int32_t xe = 0; int32_t len = 0; - + bool first = true; do { while (f < 0) { @@ -2837,7 +2916,6 @@ int16_t TFT_eSPI::textWidth(const char *string, uint8_t font) ** Description: return an encoded 16 bit value showing the fonts loaded ***************************************************************************************/ // Returns a value showing which fonts are loaded (bit N set = Font N loaded) - uint16_t TFT_eSPI::fontsLoaded(void) { return fontsloaded; @@ -3250,7 +3328,7 @@ void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) x+= _xDatum; y+= _yDatum; - // Range checking + // Range checking if ((x < _vpX) || (y < _vpY) ||(x >= _vpW) || (y >= _vpH)) return; #ifdef CGRAM_OFFSET @@ -3276,7 +3354,7 @@ void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) DC_D; tft_Write_16(0); DC_C; tft_Write_8(TFT_CASET2); DC_D; tft_Write_16(175); - + DC_C; tft_Write_8(TFT_PASET1); DC_D; tft_Write_16(0); DC_C; tft_Write_8(TFT_PASET2); @@ -3298,9 +3376,9 @@ void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) #endif // Temporary solution is to include the RP2040 optimised code here -#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); } #endif @@ -3375,7 +3453,7 @@ void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) #else - #if defined (SSD1351_DRIVER) || defined (SSD1963_DRIVER) + #if defined (SSD1351_DRIVER) || defined (SSD1963_DRIVER) if ((rotation & 0x1) == 0) { swap_coord(x, y); } #endif @@ -3412,7 +3490,7 @@ void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) #endif DC_C; tft_Write_8(TFT_RAMWR); - + #if defined(TFT_PARALLEL_8_BIT) || !defined(ESP32) DC_D; tft_Write_16(color); #else @@ -3431,7 +3509,8 @@ void TFT_eSPI::pushColor(uint16_t color) { begin_tft_write(); - tft_Write_16(color); + SPI_BUSY_CHECK; + tft_Write_16N(color); end_tft_write(); } @@ -3583,6 +3662,196 @@ void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t } +/*************************************************************************************** +** Description: Constants for anti-aliased line drawing on TFT and in Sprites +***************************************************************************************/ +constexpr float PixelAlphaGain = 255.0; +constexpr float LoAlphaTheshold = 1.0/31.0; +constexpr float HiAlphaTheshold = 1.0 - LoAlphaTheshold; + +/*************************************************************************************** +** Function name: drawPixel (aplha blended) +** Description: Draw a pixel blended with the screen or bg pixel colour +***************************************************************************************/ +uint16_t TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color, uint8_t alpha, uint32_t bg_color) +{ + if (bg_color == 0x00FFFFFF) bg_color = readPixel(x, y); + bg_color = alphaBlend(alpha, color, bg_color); + drawPixel(x, y, bg_color); + return bg_color; +} + +/*************************************************************************************** +** Function name: fillSmoothCircle +** Description: Draw a filled anti-aliased circle +***************************************************************************************/ +void TFT_eSPI::fillSmoothCircle(int32_t x, int32_t y, int32_t r, uint32_t color, uint32_t bg_color) +{ + inTransaction = true; + int16_t xs = 0; + int16_t cx; + r++; + for (int16_t cy = r; cy > 0; cy--) + { + for (cx = xs; cx <= xs + 1 && cx < r; cx++) + { + float deltaX = r - cx; + float deltaY = r - cy; + float weight = r - sqrtf(deltaX * deltaX + deltaY * deltaY); + if (weight > 1.0) continue; + xs = cx; + if (weight < LoAlphaTheshold) continue; + uint8_t alpha = weight * 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, y - cy + r, color, alpha, bg_color); + drawPixel(x + cx - r, y - cy + r, color, alpha, bg_color); + } + cx--; + drawFastHLine(x + cx - r, y + cy - r, 2 * (r - cx) + 1, color); + drawFastHLine(x + cx - r, y - cy + r, 2 * (r - cx) + 1, color); + } + inTransaction = lockTransaction; + end_tft_write(); +} + +/*************************************************************************************** +** Function name: fillSmoothCircle +** Description: Draw a filled anti-aliased circle +***************************************************************************************/ +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; + int16_t xs = 0; + int16_t cx; + + y += r; + h -= 2*r; + fillRect(x, y, w, h, color); + x += r; + w -= 2*r+1; + r++; + + for (int16_t cy = r; cy > 0; cy--) + { + for (cx = xs; cx <= xs + 1 && cx < r; cx++) + { + float deltaX = r - cx; + float deltaY = r - cy; + float weight = r - sqrtf(deltaX * deltaX + deltaY * deltaY); + if (weight > 1.0) continue; + xs = cx; + if (weight < LoAlphaTheshold) continue; + uint8_t alpha = weight * 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); + drawPixel(x + cx - r, y - cy + r + h, color, alpha, bg_color); + } + cx--; + drawFastHLine(x + cx - r, y + cy - r, 2 * (r - cx) + 1 + w, color); + drawFastHLine(x + cx - r, y - cy + r + h, 2 * (r - cx) + 1 + w, color); + } + inTransaction = lockTransaction; + end_tft_write(); +} + +/*************************************************************************************** +** Function name: drawSpot - maths intensive, so for small filled circles +** Description: Draw an anti-aliased filled circle at ax,ay with radius r +***************************************************************************************/ +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 + drawWedgeLine( ax, ay, ax, ay, r, r, fg_color, bg_color); +} + +/*************************************************************************************** +** Function name: drawWideLine - background colour specified or pixel read +** Description: draw an anti-aliased line with rounded ends, width wd +***************************************************************************************/ +void TFT_eSPI::drawWideLine(float ax, float ay, float bx, float by, float wd, uint32_t fg_color, uint32_t bg_color) +{ + drawWedgeLine( ax, ay, bx, by, wd/2.0, wd/2.0, fg_color, bg_color); +} + +/*************************************************************************************** +** Function name: drawWedgeLine +** Description: draw an anti-aliased line with different width radiused ends +***************************************************************************************/ +// For sprites and TFT screens where the background pixel colour is fixed +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 ( (abs(ax - bx) < 0.01f) && (abs(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)); + int32_t x1 = (int32_t) ceilf(fmaxf(ax+ar, bx+br)); + int32_t y0 = (int32_t)floorf(fminf(ay-ar, by-br)); + int32_t y1 = (int32_t) ceilf(fmaxf(ay+ar, by+br)); + + if (!clipWindow(&x0, &y0, &x1, &y1)) return; + + // Establish slope direction + int32_t xs = x0, yp = y1, yinc = -1; + if (((ax-ar)>(bx-br) && (ay>by)) || ((ax-ar)<(bx-br) && ay (y0+ri)) && (xp > xs)) xs = xp; } + if (alpha > HiAlphaTheshold) { + //drawPixel(xp, yp, fg_color); + if (swin) { setWindow(xp, yp, width()-1, yp); swin = false; } + pushColor(fg_color); // 778960 .v. 1337554 + continue; + } + //Blend color with background and plot + if (bg_color == 0x00FFFFFF) { + bg = readPixel(xp, yp); swin = true; + } + //drawPixel(xp, yp, alphaBlend((uint8_t)(alpha * PixelAlphaGain), fg_color, bg)); + if (swin) { setWindow(xp, yp, width()-1, yp); swin = false; } + pushColor(alphaBlend((uint8_t)(alpha * PixelAlphaGain), fg_color, bg)); + } + yp+=yinc; + } + + inTransaction = lockTransaction; + 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 +***************************************************************************************/ +inline float TFT_eSPI::wedgeLineDistance(float xpax, float ypay, float bax, float bay, float dr) +{ + float h = fmaxf(fminf((xpax * bax + ypay * bay) / (bax * bax + bay * bay), 1.0f), 0.0f); + float dx = xpax - bax * h, dy = ypay - bay * h; + return sqrtf(dx * dx + dy * dy) + h * dr; +} + + /*************************************************************************************** ** Function name: drawFastVLine ** Description: draw a vertical line @@ -3747,7 +4016,7 @@ void TFT_eSPI::fillRectHGradient(int16_t x, int16_t y, int16_t w, int16_t h, uin if ((w < 1) || (h < 1)) return; begin_tft_write(); - + float delta = -255.0/w; float alpha = 255.0; uint32_t color = color1; @@ -4005,7 +4274,7 @@ uint16_t TFT_eSPI::alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc, uint8_t if (alphaDither < 0) alpha = 0; if (alphaDither >255) alpha = 255; } - + return alphaBlend(alpha, fgc, bgc); } @@ -4041,6 +4310,22 @@ uint32_t TFT_eSPI::alphaBlend24(uint8_t alpha, uint32_t fgc, uint32_t bgc, uint8 return (r << 16) | (g << 8) | (b << 0); } +/*************************************************************************************** +** Function name: write +** Description: draw characters piped through serial stream +***************************************************************************************/ +size_t TFT_eSPI::write(const uint8_t *buf, size_t len) +{ + inTransaction = true; + + uint8_t *lbuf = (uint8_t *)buf; + while(len--) write(*lbuf++); + + inTransaction = lockTransaction; + end_tft_write(); + return 1; +} + /*************************************************************************************** ** Function name: write ** Description: draw characters piped through serial stream @@ -4341,7 +4626,7 @@ int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font) w *= height; // Now w is total number of pixels in the character if (textcolor == textbgcolor && !clip) { - + int32_t px = 0, py = pY; // To hold character block start and end column and row values int32_t pc = 0; // Pixel count uint8_t np = textsize * textsize; // Number of pixels in a drawn pixel diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 006686a..895f1e7 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -16,7 +16,7 @@ #ifndef _TFT_eSPIH_ #define _TFT_eSPIH_ -#define TFT_ESPI_VERSION "2.4.32" +#define TFT_ESPI_VERSION "2.4.33" // Bit level feature flags // Bit 0 set: viewport capability @@ -407,6 +407,18 @@ class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has ac height(void), width(void); + // Read the colour of a pixel at x,y and return value in 565 format + virtual uint16_t readPixel(int32_t x, int32_t y); + + virtual void setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye); // Note: start + end coordinates + + // Push (aka write pixel) colours to the set window + virtual void pushColor(uint16_t color); + + // These are non-inlined to enable override + virtual void begin_nin_write(); + virtual void end_nin_write(); + void setRotation(uint8_t r); // Set the display image orientation to 0, 1, 2 or 3 uint8_t getRotation(void); // Read the current rotation @@ -414,8 +426,7 @@ class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has ac // The TFT_eSprite class inherits the following functions (not all are useful to Sprite class - void setAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h), // Note: start coordinates + width and height - setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye); // Note: start + end coordinates + void setAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h); // Note: start coordinates + width and height // Viewport commands, see "Viewport_Demo" sketch void setViewport(int32_t x, int32_t y, int32_t w, int32_t h, bool vpDatum = true); @@ -428,9 +439,13 @@ class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has ac void frameViewport(uint16_t color, int32_t w); void resetViewport(void); - // Push (aka write pixel) colours to the TFT (use setAddrWindow() first) - void pushColor(uint16_t color), - pushColor(uint16_t color, uint32_t len), // Deprecated, use pushBlock() + // Clip input window to viewport bounds, return false if whole area is out of bounds + bool clipAddrWindow(int32_t* x, int32_t* y, int32_t* w, int32_t* h); + // Clip input window area to viewport bounds, return false if whole area is out of bounds + bool clipWindow(int32_t* xs, int32_t* ys, int32_t* xe, int32_t* ye); + + // Push (aka write pixel) colours to the TFT (use setAddrWindow() first) + void pushColor(uint16_t color, uint32_t len), // Deprecated, use pushBlock() pushColors(uint16_t *data, uint32_t len, bool swap = true), // With byte swap option pushColors(uint8_t *data, uint32_t len); // Deprecated, use pushPixels() @@ -440,9 +455,6 @@ class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has ac // Write a set of pixels stored in memory, use setSwapBytes(true/false) function to correct endianess void pushPixels(const void * data_in, uint32_t len); - // Read the colour of a pixel at x,y and return value in 565 format - uint16_t readPixel(int32_t x, int32_t y); - // Support for half duplex (bi-directional SDA) SPI bus where MOSI must be switched to input #ifdef TFT_SDA_READ #if defined (TFT_eSPI_ENABLE_8_BIT_READ) @@ -461,6 +473,28 @@ 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), @@ -577,7 +611,8 @@ 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); + // Used by Smooth font class to fetch a pixel colour for the anti-aliasing void setCallback(getColorCallback getCol); @@ -744,6 +779,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); + // 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); + // Display variant settings uint8_t tabcolor, // ST7735 screen protector "tab" colour (now invalid) colstart = 0, rowstart = 0; // Screen display area to CGRAM area coordinate offsets diff --git a/keywords.txt b/keywords.txt index c5e1a9d..d0c780f 100644 --- a/keywords.txt +++ b/keywords.txt @@ -27,6 +27,15 @@ getViewportHeight KEYWORD2 getViewportDatum KEYWORD2 frameViewport KEYWORD2 +# Smooth (anti-aliased) graphics functions +fillRectHGradient KEYWORD2 +fillRectVGradient KEYWORD2 +fillSmoothCircle KEYWORD2 +fillSmoothRoundRect KEYWORD2 +drawSpot KEYWORD2 +drawWideLine KEYWORD2 +drawWedgeLine KEYWORD2 + pushColor KEYWORD2 pushColors KEYWORD2 pushBlock KEYWORD2 diff --git a/library.json b/library.json index 49a1a70..248d49c 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "2.4.32", + "version": "2.4.33", "keywords": "Arduino, tft, ePaper, display, Pico, RP2040, STM32, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9481, ILI9486, ILI9488, ST7789, RM68140, SSD1351, SSD1963, ILI9225, HX8357D", "description": "A TFT and ePaper SPI graphics library with optimisation for Raspberry Pi Pico, ESP8266, ESP32 and STM32", "repository": diff --git a/library.properties b/library.properties index 8d06f6b..c0fa37d 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=2.4.32 +version=2.4.33 author=Bodmer maintainer=Bodmer sentence=TFT graphics library for Arduino processors with performance optimisation for RP2040, STM32, ESP8266 and ESP32