Commit 8209c003 authored by Bodmer's avatar Bodmer

Add new alphaBlend functions

Added 24 bit colour handling alphaBlend to reduce precision loss in multiple blend stages (e.g. in 2D colour gradients). Added option for alpha dither to reduce colour banding in gradients with 16 bit colours.

Get rid of compile warnings.
parent 0e0fd752
......@@ -827,7 +827,7 @@ void TFT_eSprite::pushSprite(int32_t x, int32_t y, uint16_t transp)
*************************************************************************************x*/
uint8_t TFT_eSprite::readPixelValue(int32_t x, int32_t y)
{
if ((x < 0) || (x >= _iwidth) || (y < 0) || (y >= _iheight) || !_created) return 0xFFFF;
if ((x < 0) || (x >= _iwidth) || (y < 0) || (y >= _iheight) || !_created) return 0xFF;
if (_bpp == 4)
{
......
......@@ -10,6 +10,44 @@
// See license in root directory.
/***************************************************************************************
** Function name: begin_touch_read_write - was spi_begin_touch
** Description: Start transaction and select touch controller
***************************************************************************************/
// The touch controller has a low SPI clock rate
inline void TFT_eSPI::begin_touch_read_write(void){
DMA_BUSY_CHECK;
CS_H; // Just in case it has been left low
#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS)
if (locked) {locked = false; spi.beginTransaction(SPISettings(SPI_TOUCH_FREQUENCY, MSBFIRST, SPI_MODE0));}
#else
spi.setFrequency(SPI_TOUCH_FREQUENCY);
#endif
SET_BUS_READ_MODE;
T_CS_L;
}
/***************************************************************************************
** Function name: end_touch_read_write - was spi_end_touch
** Description: End transaction and deselect touch controller
***************************************************************************************/
inline void TFT_eSPI::end_touch_read_write(void){
T_CS_H;
#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS)
if(!inTransaction) {if (!locked) {locked = true; spi.endTransaction();}}
#else
spi.setFrequency(SPI_FREQUENCY);
#endif
SET_BUS_WRITE_MODE;
}
/***************************************************************************************
** Function name: Legacy - deprecated
** Description: Start/end transaction
***************************************************************************************/
void TFT_eSPI::spi_begin_touch() {begin_touch_read_write();}
void TFT_eSPI::spi_end_touch() { end_touch_read_write();}
/***************************************************************************************
** Function name: getTouchRaw
** Description: read raw touch position. Always returns true.
......
......@@ -18,9 +18,9 @@
void setTouch(uint16_t *data);
private:
// Legacy support only - deprecated
void spi_begin_touch() {begin_touch_read_write();}
void spi_end_touch() { end_touch_read_write();}
// Legacy support only - deprecated TODO: delete
void spi_begin_touch();
void spi_end_touch();
// Handlers for the touch controller bus settings
inline void begin_touch_read_write() __attribute__((always_inline));
......
......@@ -107,41 +107,14 @@ inline void TFT_eSPI::end_tft_read(void){
SET_BUS_WRITE_MODE;
}
#if defined (TOUCH_CS) && defined (SPI_TOUCH_FREQUENCY)
/***************************************************************************************
** Function name: begin_touch_read_write - was spi_begin_touch
** Description: Start transaction and select touch controller
** Function name: Legacy - deprecated
** Description: Start/end transaction
***************************************************************************************/
// The touch controller has a low SPI clock rate
inline void TFT_eSPI::begin_touch_read_write(void){
DMA_BUSY_CHECK;
CS_H; // Just in case it has been left low
#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS)
if (locked) {locked = false; spi.beginTransaction(SPISettings(SPI_TOUCH_FREQUENCY, MSBFIRST, SPI_MODE0));}
#else
spi.setFrequency(SPI_TOUCH_FREQUENCY);
#endif
SET_BUS_READ_MODE;
T_CS_L;
}
/***************************************************************************************
** Function name: end_touch_read_write - was spi_end_touch
** Description: End transaction and deselect touch controller
***************************************************************************************/
inline void TFT_eSPI::end_touch_read_write(void){
T_CS_H;
#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS)
if(!inTransaction) {if (!locked) {locked = true; spi.endTransaction();}}
#else
spi.setFrequency(SPI_FREQUENCY);
#endif
SET_BUS_WRITE_MODE;
}
#endif
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: TFT_eSPI
......@@ -2969,6 +2942,31 @@ uint16_t TFT_eSPI::color8to16(uint8_t color)
return color16;
}
/***************************************************************************************
** Function name: color16to24
** Description: convert 16 bit colour to a 24 bit 888 colour value
***************************************************************************************/
uint32_t TFT_eSPI::color16to24(uint16_t color565)
{
uint8_t r = (color565 >> 8) & 0xF8; r |= (r >> 5);
uint8_t g = (color565 >> 3) & 0xFC; g |= (g >> 6);
uint8_t b = (color565 << 3) & 0xF8; b |= (b >> 5);
return ((uint32_t)r << 16) | ((uint32_t)g << 8) | ((uint32_t)b << 0);
}
/***************************************************************************************
** Function name: color24to16
** Description: convert 24 bit colour to a 16 bit 565 colour value
***************************************************************************************/
uint32_t TFT_eSPI::color24to16(uint32_t color888)
{
uint16_t r = (color888 >> 8) & 0xF800;
uint16_t g = (color888 >> 5) & 0x07E0;
uint16_t b = (color888 >> 3) & 0x001F;
return (r | g | b);
}
/***************************************************************************************
** Function name: invertDisplay
......@@ -3108,7 +3106,7 @@ uint16_t TFT_eSPI::decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining)
/***************************************************************************************
** Function name: alphaBlend
** Description: Blend foreground and background and return new colour
** Description: Blend 16bit foreground and background
*************************************************************************************x*/
uint16_t TFT_eSPI::alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc)
{
......@@ -3132,6 +3130,53 @@ uint16_t TFT_eSPI::alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc)
return (r << 11) | (g << 5) | (b << 0);
}
/***************************************************************************************
** Function name: alphaBlend
** Description: Blend 16bit foreground and background with dither
*************************************************************************************x*/
uint16_t TFT_eSPI::alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc, uint8_t dither)
{
if (dither) {
int16_t alphaDither = (int16_t)alpha - dither + random(2*dither+1); // +/-4 randomised
alpha = (uint8_t)alphaDither;
if (alphaDither < 0) alpha = 0;
if (alphaDither >255) alpha = 255;
}
return alphaBlend(alpha, fgc, bgc);
}
/***************************************************************************************
** Function name: alphaBlend
** Description: Blend 24bit foreground and background with optional dither
*************************************************************************************x*/
uint32_t TFT_eSPI::alphaBlend24(uint8_t alpha, uint32_t fgc, uint32_t bgc, uint8_t dither)
{
if (dither) {
int16_t alphaDither = (int16_t)alpha - dither + random(2*dither+1); // +/-dither randomised
alpha = (uint8_t)alphaDither;
if (alphaDither < 0) alpha = 0;
if (alphaDither >255) alpha = 255;
}
// For speed use fixed point maths and rounding to permit a power of 2 division
uint16_t fgR = ((fgc >> 15) & 0x1FE) + 1;
uint16_t fgG = ((fgc >> 7) & 0x1FE) + 1;
uint16_t fgB = ((fgc << 1) & 0x1FE) + 1;
uint16_t bgR = ((bgc >> 15) & 0x1FE) + 1;
uint16_t bgG = ((bgc >> 7) & 0x1FE) + 1;
uint16_t bgB = ((bgc << 1) & 0x1FE) + 1;
// Shift right 1 to drop rounding bit and shift right 8 to divide by 256
uint16_t r = (((fgR * alpha) + (bgR * (255 - alpha))) >> 9);
uint16_t g = (((fgG * alpha) + (bgG * (255 - alpha))) >> 9);
uint16_t b = (((fgB * alpha) + (bgB * (255 - alpha))) >> 9);
// Combine RGB colours into 24 bits
return (r << 16) | (g << 8) | (b << 0);
}
/***************************************************************************************
** Function name: write
......
......@@ -16,7 +16,7 @@
#ifndef _TFT_eSPIH_
#define _TFT_eSPIH_
#define TFT_ESPI_VERSION "2.0.0"
#define TFT_ESPI_VERSION "2.0.1"
/***************************************************************************************
** Section 1: Load required header files
......@@ -558,10 +558,18 @@ class TFT_eSPI : public Print {
// Convert 16 bit colour to 8 bits
uint8_t color16to8(uint16_t color565);
// Convert 16 bit colour to/from 24 bit, R+G+B concatenated into LS 24 bits
uint32_t color16to24(uint16_t color565);
uint32_t color24to16(uint32_t color888);
// Alpha blend 2 colours, see generic "alphaBlend_Test" example
// alpha = 0 = 100% background colour
// alpha = 255 = 100% foreground colour
uint16_t alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc);
// 16 bit colour alphaBlend with alpha dither (dither reduces colour banding)
uint16_t alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc, uint8_t dither);
// 24 bit colour alphaBlend with optional alpha dither
uint32_t alphaBlend24(uint8_t alpha, uint32_t fgc, uint32_t bgc, uint8_t dither = 0);
// DMA support functions - these are currently just for SPI writes whe using the STM32 processors
......@@ -640,20 +648,20 @@ class TFT_eSPI : public Print {
textdatum, // Text reference datum
rotation; // Display rotation (0-3)
int16_t _xpivot; // TFT x pivot point coordinate for rotated Sprites
int16_t _ypivot; // TFT x pivot point coordinate for rotated Sprites
int16_t _xpivot; // TFT x pivot point coordinate for rotated Sprites
int16_t _ypivot; // TFT x pivot point coordinate for rotated Sprites
uint8_t decoderState = 0; // UTF8 decoder state - not for user access
uint16_t decoderBuffer; // Unicode code-point buffer - not for user access
//--------------------------------------- private ------------------------------------//
private:
// Legacy begin and end prototypes - deprecated
void spi_begin() {begin_tft_write();}
void spi_end() { end_tft_write();}
// Legacy begin and end prototypes - deprecated TODO: delete
void spi_begin();
void spi_end();
void spi_begin_read() {begin_tft_read(); }
void spi_end_read() { end_tft_read(); }
void spi_begin_read();
void spi_end_read();
// New begin and end prototypes
// begin/end a TFT write transaction
......
/*
This tests the alpha blending function that is used with the antialiased
fonts:
Alpha = 0 = 100% background, alpha = 255 = 100% foreground colour
blendedColor = tft.alphaBlend(alpha, fg_color, bg_color);
The alphaBlend() function operates on 16 bit colours only
A test is included where the colours are mapped to 8 bits after blending
Information on alpha blending is here
https://en.wikipedia.org/wiki/Alpha_compositing
Example for library:
https://github.com/Bodmer/TFT_eSPI
The sketch has been tested on a 320x240 ILI9341 based TFT, it
could be adapted for other screen sizes.
Created by Bodmer 10/2/18
#########################################################################
###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ######
#########################################################################
*/
#include <TFT_eSPI.h> // Include the graphics library
TFT_eSPI tft = TFT_eSPI(); // Create object "tft"
// -------------------------------------------------------------------------
// Setup
// -------------------------------------------------------------------------
void setup(void) {
tft.init();
tft.setRotation(0);
tft.fillScreen(TFT_DARKGREY);
}
// -------------------------------------------------------------------------
// Main loop
// -------------------------------------------------------------------------
void loop()
{
// 16 bit colours (5 bits red, 6 bits green, 5 bits blue)
// Blend from white to full spectrum
for (int a = 0; a < 256; a+=2) // Alpha 0 = 100% background, alpha 255 = 100% foreground
{
for (int c = 0; c < 192; c++) tft.drawPixel(c, a/2, tft.alphaBlend(a, rainbow(c), TFT_WHITE));
}
// Blend from full spectrum to black
for (int a = 255; a > 2; a-=2)
{
for (int c = 0; c < 192; c++) tft.drawPixel(c, 128 + (255-a)/2, tft.alphaBlend(a, rainbow(c), TFT_BLACK));
}
// Blend from white to black (32 grey levels)
for (uint16_t a = 0; a < 255; a++) // Alpha 0 = 100% background, alpha 255 = 100% foreground
{
tft.drawFastHLine(192, a, 12, tft.alphaBlend(a, TFT_BLACK, TFT_WHITE));
tft.drawFastHLine(204, a, 12, tft.alphaBlend(a, TFT_BLACK, TFT_RED));
tft.drawFastHLine(216, a, 12, tft.alphaBlend(a, TFT_BLACK, TFT_GREEN));
tft.drawFastHLine(228, a, 12, tft.alphaBlend(a, TFT_BLACK, TFT_BLUE));
}
delay(4000);
// Blend from white to colour (32 grey levels)
for (uint16_t a = 0; a < 255; a++) // Alpha 0 = 100% background, alpha 255 = 100% foreground
{
//tft.drawFastHLine(192, a, 12, tft.alphaBlend(a, TFT_BLACK, TFT_WHITE));
tft.drawFastHLine(204, a, 12, tft.alphaBlend(a, TFT_RED, TFT_WHITE));
tft.drawFastHLine(216, a, 12, tft.alphaBlend(a, TFT_GREEN, TFT_WHITE));
tft.drawFastHLine(228, a, 12, tft.alphaBlend(a, TFT_BLUE, TFT_WHITE));
}
delay(4000);
//*
// Decrease to 8 bit colour (3 bits red, 3 bits green, 2 bits blue)
// Blend from white to full spectrum
for (int a = 0; a < 256; a+=2) // Alpha 0 = 100% background, alpha 255 = 100% foreground
{
// Convert blended 16 bit colour to 8 bits to reduce colour resolution, then map back to 16 bits for displaying
for (int c = 0; c < 192; c++) tft.drawPixel(c, a/2, tft.color8to16(tft.color16to8(tft.alphaBlend(a, rainbow(c), 0xFFFF))));
}
// Blend from full spectrum to black
for (int a = 255; a > 2; a-=2)
{
// Convert blended 16 bit colour to 8 bits to reduce colour resolution, then map back to 16 bits for displaying
for (int c = 0; c < 192; c++) tft.drawPixel(c, 128 + (255-a)/2, tft.color8to16(tft.color16to8(tft.alphaBlend(a, rainbow(c), 0))));
}
// Blend from white to black (4 grey levels - it will draw 4 more with a blue tinge due to lower blue bit count)
// Blend from black to a primary colour
for (uint16_t a = 0; a < 255; a++) // Alpha 0 = 100% background, alpha 255 = 100% foreground
{
tft.drawFastHLine(192, a, 12, tft.color8to16(tft.color16to8(tft.alphaBlend(a, TFT_BLACK, TFT_WHITE))));
tft.drawFastHLine(204, a, 12, tft.color8to16(tft.color16to8(tft.alphaBlend(a, TFT_BLACK, TFT_RED))));
tft.drawFastHLine(216, a, 12, tft.color8to16(tft.color16to8(tft.alphaBlend(a, TFT_BLACK, TFT_GREEN))));
tft.drawFastHLine(228, a, 12, tft.color8to16(tft.color16to8(tft.alphaBlend(a, TFT_BLACK, TFT_BLUE))));
}
delay(4000);
//*/
/*
// 16 bit colours (5 bits red, 6 bits green, 5 bits blue)
for (int a = 0; a < 256; a+=2) // Alpha 0 = 100% background, alpha 255 = 100% foreground
{
for (int c = 0; c < 192; c++) tft.drawPixel(c, a/2, tft.alphaBlend(a, rainbow(c), TFT_CYAN));
}
// Blend from full spectrum to cyan
for (int a = 255; a > 2; a-=2)
{
for (int c = 0; c < 192; c++) tft.drawPixel(c, 128 + (255-a)/2, tft.alphaBlend(a, rainbow(c), TFT_YELLOW));
}
//*/
/*
// Blend other colour transitions for test purposes
for (uint16_t a = 0; a < 255; a++) // Alpha 0 = 100% background, alpha 255 = 100% foreground
{
tft.drawFastHLine(192, a, 12, tft.alphaBlend(a, TFT_WHITE, TFT_WHITE)); // Should show as solid white
tft.drawFastHLine(204, a, 12, tft.alphaBlend(a, TFT_BLACK, TFT_BLACK)); // Should show as solid black
tft.drawFastHLine(216, a, 12, tft.alphaBlend(a, TFT_YELLOW, TFT_CYAN)); // Brightness should be fairly even
tft.drawFastHLine(228, a, 12, tft.alphaBlend(a, TFT_CYAN, TFT_MAGENTA));// Brightness should be fairly even
}
delay(4000);
//*/
}
// #########################################################################
// Return a 16 bit rainbow colour
// #########################################################################
unsigned int rainbow(byte value)
{
// If 'value' is in the range 0-159 it is converted to a spectrum colour
// from 0 = red through to 127 = blue to 159 = violet
// Extending the range to 0-191 adds a further violet to red band
value = value%192;
byte red = 0; // Red is the top 5 bits of a 16 bit colour value
byte green = 0; // Green is the middle 6 bits, but only top 5 bits used here
byte blue = 0; // Blue is the bottom 5 bits
byte sector = value >> 5;
byte amplit = value & 0x1F;
switch (sector)
{
case 0:
red = 0x1F;
green = amplit; // Green ramps up
blue = 0;
break;
case 1:
red = 0x1F - amplit; // Red ramps down
green = 0x1F;
blue = 0;
break;
case 2:
red = 0;
green = 0x1F;
blue = amplit; // Blue ramps up
break;
case 3:
red = 0;
green = 0x1F - amplit; // Green ramps down
blue = 0x1F;
break;
case 4:
red = amplit; // Red ramps up
green = 0;
blue = 0x1F;
break;
case 5:
red = 0x1F;
green = 0;
blue = 0x1F - amplit; // Blue ramps down
break;
}
return red << 11 | green << 6 | blue;
}
{
"name": "TFT_eSPI",
"version": "2.0.0",
"version": "2.0.1",
"keywords": "Arduino, tft, ePaper, display, STM32, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486, ST7789, RM68140",
"description": "A TFT and ePaper SPI graphics library with optimisation for ESP8266, ESP32 and STM32",
"repository":
......
name=TFT_eSPI
version=2.0.0
version=2.0.1
author=Bodmer
maintainer=Bodmer
sentence=TFT graphics library for Arduino processors with performance optimisation for STM32, ESP8266 and ESP32
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment