Add sprite class

Sprites (images) can now be created in RAM, written to with text and
graphics and rendered to screen quickly, this makes it easier to get
flicker frre screen updates.
ESP8266 can typically create up to a 160x128 sprite, and ESP32 about
200x200 pixels. 16 bit colour only at the moment, may soon implement 8
bit colour to reduce RAM.
This commit is contained in:
Bodmer committed 2017-11-15 20:26:22 +00:00
1 parent 62c02b67f7
commit 943c18facf
8 files changed
+1625 -136

No files matched your search

@@ -0,0 +1,199 @@
/*
Display all the fast rendering fonts.
Make sure all the display driver and pin comnections are correct by
editting the User_Setup.h file in the TFT_eSPI library folder.
#########################################################################
###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ######
#########################################################################
*/
// Create a sprite 160 x 128 pixels (needs 40Kbytes of RAM!)
#define IWIDTH 160
#define IHEIGHT 128
// New background colour
#define TFT_BROWN 0x38E0
// Pause in milliseconds between screens, change to 0 to time font rendering
#define WAIT 500
#include <TFT_eSPI.h> // Graphics and font library for ST7735 driver chip
#include <SPI.h>
TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.h
TFT_eSprite img = TFT_eSprite(&tft);
unsigned long targetTime = 0; // Used for testing draw times
void setup(void) {
tft.init();
tft.setRotation(0);
tft.fillScreen(TFT_BLUE);
img.createSprite(IWIDTH, IHEIGHT);
img.fillSprite(TFT_BLACK);
}
void loop() {
targetTime = millis();
// First we test them with a background colour set
img.setTextSize(1);
img.fillSprite(TFT_BLACK);
img.setTextColor(TFT_GREEN, TFT_BLACK);
img.drawString(" !\"#$%&'()*+,-./0123456", 0, 0, 2);
img.drawString("789:;<=>?@ABCDEFGHIJKL", 0, 16, 2);
img.drawString("MNOPQRSTUVWXYZ[\\]^_`", 0, 32, 2);
img.drawString("abcdefghijklmnopqrstuvw", 0, 48, 2);
int xpos = 0;
xpos += img.drawString("xyz{|}~", 0, 64, 2);
img.drawChar(127, xpos, 64, 2);
img.pushSprite(0, 0); delay(WAIT);
img.fillSprite(TFT_BLACK);
img.setTextColor(TFT_GREEN, TFT_BLACK);
img.drawString(" !\"#$%&'()*+,-.", 0, 0, 4);
img.drawString("/0123456789:;", 0, 26, 4);
img.drawString("<=>?@ABCDE", 0, 52, 4);
img.drawString("FGHIJKLMNO", 0, 78, 4);
img.drawString("PQRSTUVWX", 0, 104, 4);
img.pushSprite(0, 0); delay(WAIT);
img.fillSprite(TFT_BLACK);
img.drawString("YZ[\\]^_`abc", 0, 0, 4);
img.drawString("defghijklmno", 0, 26, 4);
img.drawString("pqrstuvwxyz", 0, 52, 4);
xpos = 0;
xpos += img.drawString("{|}~", 0, 78, 4);
img.drawChar(127, xpos, 78, 4);
img.pushSprite(0, 0); delay(WAIT);
img.fillSprite(TFT_BLACK);
img.setTextColor(TFT_BLUE, TFT_BLACK);
img.drawString("012345", 0, 0, 6);
img.drawString("6789", 0, 40, 6);
img.drawString("apm-:.", 0, 80, 6);
img.pushSprite(0, 0); delay(WAIT);
img.fillSprite(TFT_BLACK);
img.setTextColor(TFT_RED, TFT_BLACK);
img.drawString("0123", 0, 0, 7);
img.drawString("4567", 0, 60, 7);
img.pushSprite(0, 0); delay(WAIT);
img.fillSprite(TFT_BLACK);
img.drawString("890:.", 0, 0, 7);
img.drawString("", 0, 60, 7);
img.pushSprite(0, 0); delay(WAIT);
img.fillSprite(TFT_BLACK);
img.setTextColor(TFT_YELLOW, TFT_BLACK);
img.drawString("01", 0, 0, 8);
img.pushSprite(0, 0); delay(WAIT);
img.drawString("23", 0, 0, 8);
img.pushSprite(0, 0); delay(WAIT);
img.drawString("45", 0, 0, 8);
img.pushSprite(0, 0); delay(WAIT);
img.drawString("67", 0, 0, 8);
img.pushSprite(0, 0); delay(WAIT);
img.drawString("89", 0, 0, 8);
img.pushSprite(0, 0); delay(WAIT);
img.drawString("0:.", 0, 0, 8);
img.pushSprite(0, 0); delay(WAIT);
img.setTextColor(TFT_WHITE);
img.drawNumber(millis() - targetTime, 0, 100, 4);
img.pushSprite(0, 0); delay(WAIT);
delay(4000);
// Now test them with transparent background
targetTime = millis();
img.setTextSize(1);
img.fillSprite(TFT_BROWN);
img.setTextColor(TFT_GREEN);
img.drawString(" !\"#$%&'()*+,-./0123456", 0, 0, 2);
img.drawString("789:;<=>?@ABCDEFGHIJKL", 0, 16, 2);
img.drawString("MNOPQRSTUVWXYZ[\\]^_`", 0, 32, 2);
img.drawString("abcdefghijklmnopqrstuvw", 0, 48, 2);
xpos = 0;
xpos += img.drawString("xyz{|}~", 0, 64, 2);
img.drawChar(127, xpos, 64, 2);
img.pushSprite(0, 0); delay(WAIT);
img.fillSprite(TFT_BROWN);
img.setTextColor(TFT_GREEN);
img.drawString(" !\"#$%&'()*+,-.", 0, 0, 4);
img.drawString("/0123456789:;", 0, 26, 4);
img.drawString("<=>?@ABCDE", 0, 52, 4);
img.drawString("FGHIJKLMNO", 0, 78, 4);
img.drawString("PQRSTUVWX", 0, 104, 4);
img.pushSprite(0, 0); delay(WAIT);
img.fillSprite(TFT_BROWN);
img.drawString("YZ[\\]^_`abc", 0, 0, 4);
img.drawString("defghijklmno", 0, 26, 4);
img.drawString("pqrstuvwxyz", 0, 52, 4);
xpos = 0;
xpos += img.drawString("{|}~", 0, 78, 4);
img.drawChar(127, xpos, 78, 4);
img.pushSprite(0, 0); delay(WAIT);
img.fillSprite(TFT_BROWN);
img.setTextColor(TFT_BLUE);
img.drawString("012345", 0, 0, 6);
img.drawString("6789", 0, 40, 6);
img.drawString("apm-:.", 0, 80, 6);
img.pushSprite(0, 0); delay(WAIT);
img.fillSprite(TFT_BROWN);
img.setTextColor(TFT_RED);
img.drawString("0123", 0, 0, 7);
img.drawString("4567", 0, 60, 7);
img.pushSprite(0, 0); delay(WAIT);
img.fillSprite(TFT_BROWN);
img.drawString("890:.", 0, 0, 7);
img.drawString("", 0, 60, 7);
img.pushSprite(0, 0); delay(WAIT);
img.fillSprite(TFT_BROWN);
img.setTextColor(TFT_YELLOW);
img.drawString("0123", 0, 0, 8);
img.pushSprite(0, 0); delay(WAIT);
img.fillSprite(TFT_BROWN);
img.drawString("4567", 0, 0, 8);
img.pushSprite(0, 0); delay(WAIT);
img.fillSprite(TFT_BROWN);
img.drawString("890:.", 0, 0, 8);
img.pushSprite(0, 0); delay(WAIT);
img.setTextColor(TFT_WHITE);
img.drawNumber(millis() - targetTime, 0, 100, 4);
img.pushSprite(0, 0); delay(WAIT);
delay(4000);;
}
@@ -0,0 +1,140 @@
/*
An example showing rainbow colours on a 160x128 TFT LCD screen
and to show a basic example of font use.
This example plots the text in a sprite then pushes the sprite to the
TFT screen.
Make sure all the display driver and pin comnenctions are correct by
editting the User_Setup.h file in the TFT_eSPI library folder.
Note that yield() or delay(0) must be called in long duration for/while
loops to stop the ESP8266 watchdog triggering.
#########################################################################
###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ######
#########################################################################
*/
#define IWIDTH 160
#define IHEIGHT 128
#include <TFT_eSPI.h> // Graphics and font library
#include <SPI.h>
TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.h
TFT_eSprite img = TFT_eSprite(&tft);
unsigned long targetTime = 0;
byte red = 31;
byte green = 0;
byte blue = 0;
byte state = 0;
unsigned int colour = red << 11;
void setup(void) {
tft.init();
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
imgPtr = img.createSprite(IWIDTH, IHEIGHT);
img.fillSprite(TFT_BLACK);
targetTime = millis() + 1000;
}
void loop() {
if (targetTime < millis()) {
targetTime = millis() + 100;//10000;
// Colour changing state machine
for (int i = 0; i < 160; i++) {
img.drawFastVLine(i, 0, img.height(), colour);
switch (state) {
case 0:
green += 2;
if (green == 64) {
green = 63;
state = 1;
}
break;
case 1:
red--;
if (red == 255) {
red = 0;
state = 2;
}
break;
case 2:
blue ++;
if (blue == 32) {
blue = 31;
state = 3;
}
break;
case 3:
green -= 2;
if (green == 255) {
green = 0;
state = 4;
}
break;
case 4:
red ++;
if (red == 32) {
red = 31;
state = 5;
}
break;
case 5:
blue --;
if (blue == 255) {
blue = 0;
state = 0;
}
break;
}
colour = red << 11 | green << 5 | blue;
}
// The standard ADAFruit font still works as before
img.setTextColor(TFT_BLACK);
img.setCursor (12, 5);
img.print("Original ADAfruit font!");
// The new larger fonts do not use the .setCursor call, coords are embedded
img.setTextColor(TFT_BLACK, TFT_BLACK); // Do not plot the background colour
// Overlay the black text on top of the rainbow plot (the advantage of not drawing the backgorund colour!)
img.drawCentreString("Font size 2", 80, 14, 2); // Draw text centre at position 80, 12 using font 2
//img.drawCentreString("Font size 2",81,12,2); // Draw text centre at position 80, 12 using font 2
img.drawCentreString("Font size 4", 80, 30, 4); // Draw text centre at position 80, 24 using font 4
img.drawCentreString("12.34", 80, 54, 6); // Draw text centre at position 80, 24 using font 6
img.drawCentreString("12.34 is in font size 6", 80, 92, 2); // Draw text centre at position 80, 90 using font 2
// Note the x position is the top left of the font!
// draw a floating point number
float pi = 3.14159; // Value to print
int precision = 3; // Number of digits after decimal point
int xpos = 50; // x position
int ypos = 110; // y position
int font = 2; // font number only 2,4,6,7 valid. Font 6 only contains characters [space] 0 1 2 3 4 5 6 7 8 9 0 : a p m
xpos += img.drawFloat(pi, precision, xpos, ypos, font); // Draw rounded number and return new xpos delta for next print position
img.drawString(" is pi", xpos, ypos, font); // Continue printing from new x position
tft.pushSprite(0, 0, IWIDTH, IHEIGHT, imgPtr);
}
}
@@ -0,0 +1,134 @@
/*
Sketch to show how a Sprite is created, how to draw pixels
and text within the Sprite and then split the Sprite onto
the display screen.
Example for library:
https://github.com/Bodmer/TFT_eSPI
A Sprite is notionally an invisibly graphics screen that is
kept in the processors RAM. Graphics can be drawn into the
Sprite just as it can be drawn directly to the screen. Once
the Sprite is completed it can be plotted onto the screen in
any position. If there is sufficient RAM then the Sprite can
be the same size as the screen and used as a frame buffer.
The Sprite occupies (2 * width * height) bytes in RAM.
On a ESP8266 Sprite sizes up to 126 x 160 can be accomodated,
this size requires 40kBytes of RAM.
*/
// Set delay after plotting the sprite
#define DELAY 1000
// Width and height of sprite
#define WIDTH 128
#define HEIGHT 128
#include <TFT_eSPI.h> // Include the graphics library (this includes the sprite functions)
TFT_eSPI tft = TFT_eSPI(); // Declare object "tft"
TFT_eSprite spr = TFT_eSprite(&tft); // Declare Sprite object "spr" with pointer to "tft" object
void setup()
{
Serial.begin(250000);
Serial.println();
// Initialise the TFT registers
tft.init();
// Create a sprite of defined size
spr.createSprite(WIDTH, HEIGHT);
// Clear the TFT screen to blue
tft.fillScreen(TFT_BLUE);
}
void loop(void)
{
// Fill the whole sprite with black (Sprite is in memory so not visible yet)
spr.fillSprite(TFT_BLACK);
// Number of pixels to draw
uint16_t n = 100;
// Draw 100 random colour pixels at random positions in sprite
while (n--)
{
uint16_t colour = random(0x10000); // Returns colour 0 - 0xFFFF
int16_t x = random(WIDTH); // Random x coordinate
int16_t y = random(HEIGHT); // Random y coordinate
spr.drawPixel( x, y, colour); // Draw pixel in sprite
}
// Draw some lines
spr.drawLine(1, 0, WIDTH, HEIGHT-1, TFT_GREEN);
spr.drawLine(0, 0, WIDTH, HEIGHT, TFT_GREEN);
spr.drawLine(0, 1, WIDTH-1, HEIGHT, TFT_GREEN);
spr.drawLine(0, HEIGHT-1, WIDTH-1, 0, TFT_RED);
spr.drawLine(0, HEIGHT, WIDTH, 0, TFT_RED);
spr.drawLine(1, HEIGHT, WIDTH, 1, TFT_RED);
// Draw some text with Middle Centre datum
spr.setTextDatum(MC_DATUM);
spr.drawString("Sprite", WIDTH / 2, HEIGHT / 2, 4);
// Now push the sprite to the TFT at position 0,0 on screen
spr.pushSprite(-40, -40);
spr.pushSprite(tft.width() / 2 - WIDTH / 2, tft.height() / 2 - HEIGHT / 2);
spr.pushSprite(tft.width() - WIDTH + 40, tft.height() - HEIGHT + 40);
delay(DELAY);
// Fill TFT screen with blue
tft.fillScreen(TFT_BLUE);
// Draw a blue rectangle in sprite so when we move it 1 pixel it does not leave a trail
// on the blue screen background
spr.drawRect(0, 0, WIDTH, HEIGHT, TFT_BLUE);
int x = tft.width() / 2 - WIDTH / 2;
int y = tft.height() / 2 - HEIGHT / 2;
uint32_t updateTime = 0; // time for next update
while (true)
{
// Random movement direction
int dx = 1; if (random(2)) dx = -1;
int dy = 1; if (random(2)) dy = -1;
// Pull it back onto screen if it wanders off
if (x < -WIDTH/2) dx = 1;
if (x >= tft.width()-WIDTH/2) dx = -1;
if (y < -HEIGHT/2) dy = 1;
if (y >= tft.height()-HEIGHT/2) dy = -1;
// Draw it 50 time, moving in random direct or staying still
n = 50;
int wait = random (50);
while (n)
{
if (updateTime <= millis())
{
// Use time delay so sprtie does not move fast when not all on screen
updateTime = millis() + wait;
// Push the sprite to the TFT screen
spr.pushSprite(x, y);
// Change coord for next loop
x += dx;
y += dy;
n--;
yield(); // Stop watchdog reset
}
}
} // Infinite while, will not exit!
}
@@ -0,0 +1,194 @@
/*
Display "flicker free" scrolling text and updating number
Example for library:
https://github.com/Bodmer/TFT_eSPI
The sketch has been tested on a 320x240 ILI9341 based TFT, it
coule be adapted for other screen sizes.
A Sprite is notionally an invisibly graphics screen that is
kept in the processors RAM. Graphics can be drawn into the
Sprite just as it can be drawn directly to the screen. Once
the Sprite is completed it can be plotted onto the screen in
any position. If there is sufficient RAM then the Sprite can
be the same size as the screen and used as a frame buffer.
The Sprite occupies (2 * width * height) bytes.
On a ESP8266 Sprite sizes up to 128 x 160 can be accomodated,
this size requires 128*160*2 bytes (40kBytes) of RAM, this must be
available or the processor will crash. You need to make the sprite
small enough to fit, with RAM spare for any "local variables" that
may be needed by your sketch and libraries.
Created by Bodmer 15/11/17
#########################################################################
###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ######
#########################################################################
*/
// Size of sprite image for the scrolling text, this requires ~14 Kbytes of RAM
#define IWIDTH 240
#define IHEIGHT 30
// Pause in milliseconds to set scroll speed
#define WAIT 0
#include <TFT_eSPI.h> // Include the graphics library (this includes the sprite functions)
TFT_eSPI tft = TFT_eSPI(); // Create object "tft"
TFT_eSprite img = TFT_eSprite(&tft); // Create Sprite object "img" with pointer to "tft" object
// // the pointer is used by pushSprite() to push it onto the TFT
// -------------------------------------------------------------------------
// Setup
// -------------------------------------------------------------------------
void setup(void) {
tft.init();
tft.setRotation(0);
tft.fillScreen(TFT_BLUE);
}
// -------------------------------------------------------------------------
// Main loop
// -------------------------------------------------------------------------
void loop() {
while (1)
{
// Create the sprite and clear background to black
img.createSprite(IWIDTH, IHEIGHT);
//img.fillSprite(TFT_BLACK); // Optional here as we fill the sprite later anyway
for (int pos = IWIDTH; pos > 0; pos--)
{
build_banner("Hello World", pos);
img.pushSprite(0, 0);
build_banner("TFT_eSPI sprite" , pos);
img.pushSprite(0, 50);
delay(WAIT);
}
// Delete sprite to free up the memory
img.deleteSprite();
// Create a sprite of a different size
numberBox(random(100), 60, 100);
}
}
// #########################################################################
// Build the scrolling sprite image from scratch, draw text at x = xpos
// #########################################################################
void build_banner(String msg, int xpos)
{
int h = IHEIGHT;
// We could just use fillSprite(color) but lets be a bit more creative...
// Fill with rainbow stripes
while (h--) img.drawFastHLine(0, h, IWIDTH, rainbow(h * 4));
// Draw some graphics, the text will apear to scroll over these
img.fillRect (IWIDTH / 2 - 20, IHEIGHT / 2 - 10, 40, 20, TFT_YELLOW);
img.fillCircle(IWIDTH / 2, IHEIGHT / 2, 10, TFT_ORANGE);
// Now print text on top of the graphics
img.setTextSize(1); // Font size scaling is x1
img.setTextFont(4); // Font 4 selected
img.setTextColor(TFT_BLACK); // Black text, no background colour
img.setTextWrap(false); // Turn of wrap so we can print past end of sprite
// Need to print twice so text appears to wrap around at left and right edges
img.setCursor(xpos, 2); // Print text at xpos
img.print(msg);
img.setCursor(xpos - IWIDTH, 2); // Print text at xpos - sprite width
img.print(msg);
}
// #########################################################################
// Create sprite, plot graphics in it, plot to screen, then delete sprite
// #########################################################################
void numberBox(int num, int x, int y)
{
// Create a sprite 80 pixels wide, 50 high (8kbytes of RAM needed)
img.createSprite(80, 50);
// Fill it with black
img.fillSprite(TFT_BLACK);
// Draw a backgorund of 2 filled triangles
img.fillTriangle( 0, 0, 0, 49, 40, 25, TFT_RED);
img.fillTriangle( 79, 0, 79, 49, 40, 25, TFT_DARKGREEN);
// Set the font parameters
img.setTextSize(1); // Font size scaling is x1
img.setFreeFont(&FreeSerifBoldItalic24pt7b); // Select free font
img.setTextColor(TFT_WHITE); // White text, no background colour
// Set text coordinate datum to middle centre
img.setTextDatum(MC_DATUM);
// Draw the number in middle of 80 x 50 sprite
img.drawNumber(num, 40, 25);
// Push sprite to TFT screen CGRAM at coordinate x,y (top left corner)
img.pushSprite(x, y);
// Delete sprite to free up the RAM
img.deleteSprite();
}
// #########################################################################
// Return a 16 bit rainbow colour
// #########################################################################
unsigned int rainbow(byte value)
{
// Value is expected to be in range 0-127
// The value is converted to a spectrum colour from 0 = red through to 127 = blue
byte red = 0; // Red is the top 5 bits of a 16 bit colour value
byte green = 0;// Green is the middle 6 bits
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;
blue = 0;
break;
case 1:
red = 0x1F - amplit;
green = 0x1F;
blue = 0;
break;
case 2:
red = 0;
green = 0x1F;
blue = amplit;
break;
case 3:
red = 0;
green = 0x1F - amplit;
blue = 0x1F;
break;
}
return red << 11 | green << 6 | blue;
}