diff --git a/Keywords.txt b/Keywords.txt index 47a42a8..0dc74be 100644 --- a/Keywords.txt +++ b/Keywords.txt @@ -5,6 +5,7 @@ drawPixel KEYWORD2 drawChar KEYWORD2 setAddrWindow KEYWORD2 setWindow KEYWORD2 +readAddrWindow KEYWORD2 pushColor KEYWORD2 pushColor KEYWORD2 pushColors KEYWORD2 @@ -49,8 +50,10 @@ readcommand32 KEYWORD2 readPixel KEYWORD2 readRect KEYWORD2 pushRect KEYWORD2 +pushSprite KEYWORD2 readRectRGB KEYWORD2 getRotation KEYWORD2 +getTextDatum KEYWORD2 fontsLoaded KEYWORD2 color565 KEYWORD2 drawChar KEYWORD2 @@ -64,3 +67,32 @@ width KEYWORD2 textWidth KEYWORD2 fontHeight KEYWORD2 +getTouchRaw KEYWORD2 +getTouchRawZ KEYWORD2 +getTouch KEYWORD2 +calibrateTouch KEYWORD2 +setTouch KEYWORD2 +validTouch KEYWORD2 + + +TFT_eSPI_Button KEYWORD1 + +initButton KEYWORD2 +textcolor KEYWORD2 +initButtonUL KEYWORD2 +drawButton KEYWORD2 +contains KEYWORD2 +press KEYWORD2 +isPressed KEYWORD2 +justPressed KEYWORD2 +justReleased KEYWORD2 + + +TFT_eSprite KEYWORD1 + +createSprite KEYWORD2 +deleteSprite KEYWORD2 +fillSprite KEYWORD2 +setWindow KEYWORD2 +pushBitmap KEYWORD2 +pushSprite KEYWORD2 diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index ee1ac08..edf7860 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -512,13 +512,14 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) ***************************************************************************************/ void TFT_eSPI::pushRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) { - if ((x > _width) || (y > _height) || (w == 0) || (h == 0)) return; + if ((x >= _width) || (y >= _height) || (w == 0) || (h == 0)) return; spi_begin(); setAddrWindow(x, y, x + w - 1, y + h - 1); // Sets CS low and sent RAMWR uint32_t len = w * h * 2; + // Push pixels into window rectangle, data is a 16 bit pointer thus increment is halved while ( len >=32 ) {SPI.writeBytes((uint8_t*)data, 32); data += 16; len -= 32; } if (len) SPI.writeBytes((uint8_t*)data, len); @@ -529,6 +530,62 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) } +/*************************************************************************************** +** Function name: push rectangle (for SPI Interface II i.e. IM [3:0] = "1101") +** Description: push 565 pixel colours into a defined area +***************************************************************************************/ + void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t *data) +{ + + if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; + + int32_t dx = 0; + int32_t dy = 0; + int32_t dw = w; + int32_t dh = h; + + if (x < 0) { dw += x; dx = -x; x = 0; } + if (y < 0) { dh += y; dy = -y; y = 0; } + + if ((x + w) >= _width ) dw = _width - x; + if ((y + h) >= _height) dh = _height - y; + + if (dw < 0 || dh < 0) return; + + //Serial.print("dx="); Serial.println(dx); + //Serial.print("dy="); Serial.println(dy); + //Serial.print("dw="); Serial.println(dw); + //Serial.print("dh="); Serial.println(dh); + + spi_begin(); + + setAddrWindow(x, y, x + dw - 1, y + dh - 1); // Sets CS low and sent RAMWR + + data += dx + dy * w; + + while (dh--) + { + /* + uint32_t len = dw; + uint16_t* ptr = data; + // Push pixels into window rectangle, data is a 16 bit pointer thus increment is halved + while ( len--) SPI.write16(*ptr++, 0); + data += w; + */ + uint32_t len = dw * 2; + uint8_t* ptr = (uint8_t*)data; + // Push pixels into window rectangle, data is a 16 bit pointer thus increment is halved + while ( len>32) { SPI.writePattern(ptr, 32, 1); len -= 32; ptr += 32; } + if (len) SPI.writePattern((uint8_t*)ptr, len, 1); + data += w; + } + + CS_H; + + spi_end(); +} + + /*************************************************************************************** ** Function name: read rectangle (for SPI Interface II i.e. IM [3:0] = "1101") ** Description: Read RGB pixel colours from a defined area @@ -3906,7 +3963,7 @@ void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_fg, uint32_t parameters[2] = touchCalibration_y0; parameters[3] = touchCalibration_y1; parameters[4] = touchCalibration_rotate | (touchCalibration_invert_x <<1) | (touchCalibration_invert_y <<2); - } + } } @@ -4057,134 +4114,787 @@ boolean TFT_eSPI_Button::justReleased() { return (!currstate && laststate); } + /*************************************************************************************** +// The following class creates Sprites in RAM, graphics can then be drawn in the Sprite +// and rendered quickly onto the TFT screen. The class inherits the graphics functions +// from the TFT_eSPI class. Some functions are overridden by this class so that the +// graphics are written to the Sprite rather than the TFT. +// Coded by Bodmer +***************************************************************************************/ + +/*************************************************************************************** +** Function name: TFT_eSprite +** Description: Class constructor +*************************************************************************************x*/ +TFT_eSprite::TFT_eSprite(TFT_eSPI *tft) +{ + _tft = tft; // Pointer to tft class so we can call member functions + + _iwidth = 0; // Initialise width and height to 0 (it does not exist yet) + _iheight = 0; + + _xs = 0; // window bounds for pushColor + _ys = 0; + _xe = 0; + _ye = 0; + + _xptr = 0; // pushColor coordinate + _yptr = 0; + + _icursor_y = _icursor_x = 0; // Text cursor position +} +/*************************************************************************************** +** Function name: createSprite +** Description: Create a sprite (bitmap) of defined width and height +*************************************************************************************x*/ +uint16_t* TFT_eSprite::createSprite(int16_t w, int16_t h) +{ + if ( w < 1 || h < 1) return 0; + _iwidth = w; + _iheight = h; + _img = (uint16_t*) malloc(w * h * sizeof(uint16_t)); + return _img; +} -/*************************************************** - The majority of code in this file is "FunWare", the only condition of use of - those portions is that users have fun! Most of the effort has been spent on - the creation and incorporation of the proportional Run Length Encoded fonts - that can be rendered over an SPI bus at high speeds. + +/*************************************************************************************** +** Function name: deleteSprite +** Description: Delete the sprite to free up memory (RAM) +*************************************************************************************x*/ +void TFT_eSprite::deleteSprite(void) +{ + free(_img); +} + + +/*************************************************************************************** +** Function name: pushSprite +** Description: Delete the sprite to free up memory (RAM) +*************************************************************************************x*/ +void TFT_eSprite::pushSprite(int32_t x, int32_t y) +{ + _tft->pushSprite(x, y, _iwidth, _iheight, _img); +} + + +/*************************************************************************************** +** Function name: readPixel +** Description: Read 565 colour of a pixel at deined coordinates +*************************************************************************************x*/ +uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) +{ + uint16_t color = _img[x + y * _iwidth]; + return (color >> 8) | (color << 8); +} + + +/*************************************************************************************** +** Function name: pushRect (same as pushBitmap) +** Description: push 565 colour bitmap into a defined area +*************************************************************************************x*/ +void TFT_eSprite::pushRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) +{ + if ((x > _iwidth) || (y > _iheight) || (w == 0) || (h == 0)) return; + + for (uint32_t yp = y; yp < y + h; yp++) + { + for (uint32_t xp = x; xp < x + w; xp++) + { + _img[xp + yp * _iwidth] = *data++; + } + } +} + + +/*************************************************************************************** +** Function name: pushBitmap (same as pushRect) +** Description: push 565 colour bitmap into a defined area +***************************************************************************************/ +void TFT_eSprite::pushBitmap(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) +{ + if ((x > _iwidth) || (y > _iheight) || (w == 0) || (h == 0)) return; + + for (uint32_t yp = y; yp < y + h; yp++) + { + for (uint32_t xp = x; xp < x + w; xp++) + { + _img[xp + yp * _iwidth] = *data++; + } + } +} + + +/*************************************************************************************** +** Function name: spriteWindow +** Description: Set the bounds of a window for pushColor +*************************************************************************************x*/ +void TFT_eSprite::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) +{ + // Bounds will be checked by drawPixel + if (x0 > x1) swap_coord(x0, x1); + if (y0 > y1) swap_coord(y0, y1); + + _xs = x0; + _ys = y0; + _xe = x1; + _ye = y1; - A significant number of new features have been added to the original source - libraries. Functions names have been retained where practical to allow old - Adafruit_GFX TFT screen compatible sketches to be easily adapted. - - A significant level of effort has been made to optimise the library for speed - so that graphics intensive sketches can run at an acceptable speed over the - SPI bus. SPI bus speeds up to 80MHz can be used with some driver chips. At - this clock rate screen and block clears can achieve an average bit rate of - 75Mbps. - - The functions incldued are comaptible with the JPEGDecoder library here: - https://github.com/Bodmer/JPEGDecoder - - This allows allows the ESP8266 (or ESP32) sketches to retrieve images from the - internet, store them in SPIFFS and render the images to the screen at an - acceptable speed. So some really cool IoT sketches are possible without tedious - manual loading of images. - - Other portions of code are protected by the licenses as noted below. - - The library would not have been created without the initial inspiration from - Adafruit_ILI9341 and Adafruit_GFX libraries. + _xptr = _xs; + _yptr = _ys; +} - If any other conditions of use have been missed then please raise this as an - issue on GitHub: +/*************************************************************************************** +** Function name: pushColor +** Description: Send a new pixel to the sprite window +*************************************************************************************x*/ +void TFT_eSprite::pushColor(uint32_t color) +{ + drawPixel(_xptr++, _yptr, color); + if (_xptr > _xe) { _xptr = _xs; _yptr++; } + if (_yptr > _ye) { _yptr = _ys; } +} + +void TFT_eSprite::pushColor(uint32_t color, uint16_t len) +{ + drawPixel(_xptr++, _yptr, color); + if (_xptr > _xe) { _xptr = _xs; _yptr++; } + if (_yptr > _ye) { _yptr = _ys; } +} -/*************************************************** - The Adafruit_ILI9341 library has been used as a starting point - for this library. - - ORIGINAL LIBRARY HEADER - - This is our library for the Adafruit ILI9341 Breakout and Shield - ----> http://www.adafruit.com/products/1651 - - Check out the links above for our tutorials and wiring diagrams - These displays use SPI to communicate, 4 or 5 pins are required to - interface (RST is optional) - Adafruit invests time and resources providing this open source code, - please support Adafruit and open-source hardware by purchasing - products from Adafruit! - - Written by Limor Fried/Ladyada for Adafruit Industries. - MIT license, all text above must be included in any redistribution - - ****************************************************/ +/*************************************************************************************** +** Function name: fillSprite +** Description: Fill the whole sprite with defined colour +*************************************************************************************x*/ +void TFT_eSprite::fillSprite(uint32_t color) +{ + // Use memset if possible as it is super fast + if((uint8_t)color == (color>>8)) memset(_img, (uint8_t)color, _iwidth * _iheight * 2); + else fillRect(0, 0, _iwidth, _iheight, color); +} -/**************************************************** - - Some member funtions have been imported from the Adafruit_GFX - library. The license associated with these is reproduced below. - - ORIGINAL LIBRARY HEADER from Adafruit_GFX.cpp - - This is the core graphics library for all our displays, providing a common - set of graphics primitives (points, lines, circles, etc.). It needs to be - paired with a hardware-specific library for each display device we carry - (to handle the lower-level functions). - - Adafruit invests time and resources providing this open source code, please - support Adafruit & open-source hardware by purchasing products from Adafruit! - - Copyright (c) 2013 Adafruit Industries. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - -- Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. -- Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - ****************************************************/ +/*************************************************************************************** +** Function name: setCursor +** Description: Set the sprite text cursor x,y position +*************************************************************************************x*/ +void TFT_eSprite::setCursor(int16_t x, int16_t y) +{ + _icursor_x = x; + _icursor_y = y; +} -/**************************************************** +/*************************************************************************************** +** Function name: width or spriteWidth +** Description: Return the width of sprite +*************************************************************************************x*/ +// Return the size of the display +int16_t TFT_eSprite::width(void) +{ + return _iwidth; +} - In compliance with the licence.txt file for the Adafruit_GFX library the - following is included. +/* +// Return the size of the display +int16_t TFT_eSprite::spriteWidth(void) +{ + return _iwidth; +} +*/ - Software License Agreement (BSD License) +/*************************************************************************************** +** Function name: height or spriteHeight +** Description: Return the height of sprite +*************************************************************************************x*/ +int16_t TFT_eSprite::height(void) +{ + return _iheight; +} - Copyright (c) 2012 Adafruit Industries. All rights reserved. +/* +int16_t TFT_eSprite::spriteHeight(void) +{ + return _iheight; +} +*/ - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: +/*************************************************************************************** +** Function name: drawChar +** Description: draw a single character in the Adafruit GLCD or freefont +*************************************************************************************x*/ +void TFT_eSprite::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t size) +{ + if ((x >= (int16_t)_iwidth) || // Clip right + (y >= (int16_t)_iheight) || // Clip bottom + ((x + 6 * size - 1) < 0) || // Clip left + ((y + 8 * size - 1) < 0)) // Clip top + return; -- Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. -- Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. +#ifdef LOAD_GLCD +//>>>>>>>>>>>>>>>>>> +#ifdef LOAD_GFXFF + if(!gfxFont) { // 'Classic' built-in font +#endif +//>>>>>>>>>>>>>>>>>> - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. + boolean fillbg = (bg != color); + + if ((size==1) && fillbg) + { + byte column[6]; + byte mask = 0x1; + + for (int8_t i = 0; i < 5; i++ ) column[i] = pgm_read_byte(font + (c * 5) + i); + column[5] = 0; + + int8_t j, k; + for (j = 0; j < 8; j++) { + for (k = 0; k < 5; k++ ) { + if (column[k] & mask) { + drawPixel(x + k, y + j, color); + } + else { + drawPixel(x + k, y + j, bg); + } + } + + mask <<= 1; + + drawPixel(x + k, y + j, bg); + } + } + else + { + for (int8_t i = 0; i < 6; i++ ) { + uint8_t line; + if (i == 5) + line = 0x0; + else + line = pgm_read_byte(font + (c * 5) + i); + + if (size == 1) // default size + { + for (int8_t j = 0; j < 8; j++) { + if (line & 0x1) drawPixel(x + i, y + j, color); + line >>= 1; + } + } + else { // big size + for (int8_t j = 0; j < 8; j++) { + if (line & 0x1) fillRect(x + (i * size), y + (j * size), size, size, color); + else if (fillbg) fillRect(x + i * size, y + j * size, size, size, bg); + line >>= 1; + } + } + } + } + +//>>>>>>>>>>>>>>>>>>>>>>>>>>> +#ifdef LOAD_GFXFF + } else { // Custom font +#endif +//>>>>>>>>>>>>>>>>>>>>>>>>>>> +#endif // LOAD_GLCD + +#ifdef LOAD_GFXFF + +//>>>>>>>>>>>>>>>>>>>>>>>>>>> + // Character is assumed previously filtered by write() to eliminate + // newlines, returns, non-printable characters, etc. Calling drawChar() + // directly with 'bad' characters of font may cause mayhem! + if (c > pgm_read_byte(&gfxFont->last)) c = pgm_read_byte(&gfxFont->first);; + c -= pgm_read_byte(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]); + uint8_t *bitmap = (uint8_t *)pgm_read_dword(&gfxFont->bitmap); + + uint16_t bo = pgm_read_word(&glyph->bitmapOffset); + uint8_t w = pgm_read_byte(&glyph->width), + h = pgm_read_byte(&glyph->height), + xa = pgm_read_byte(&glyph->xAdvance); + int8_t xo = pgm_read_byte(&glyph->xOffset), + yo = pgm_read_byte(&glyph->yOffset); + uint8_t xx, yy, bits, bit=0; + int16_t xo16, yo16; + + if(size > 1) { + xo16 = xo; + yo16 = yo; + } + + uint16_t hpc = 0; // Horizontal foreground pixel count + for(yy=0; yy>= 1; + } + // Draw pixels for this line as we are about to increment yy + if (hpc) { + if(size == 1) drawFastHLine(x+xo+xx-hpc, y+yo+yy, hpc, color); + else fillRect(x+(xo16+xx-hpc)*size, y+(yo16+yy)*size, size*hpc, size, color); + hpc=0; + } + } + +#endif + + +#ifdef LOAD_GLCD + #ifdef LOAD_GFXFF + } // End classic vs custom font + #endif +#endif + +} + + +/*************************************************************************************** +** Function name: drawPixel +** Description: push a single pixel at an arbitrary position +*************************************************************************************x*/ +void TFT_eSprite::drawPixel(uint32_t x, uint32_t y, uint32_t color) +{ + // x and y are unsigned so that -ve coordinates turn into large positive ones + // this make bounds checking a bit faster + if ((x >= _iwidth) || (y >= _iheight)) return; + color = (color >> 8) | (color << 8); + _img[x+y*_iwidth] = (uint16_t) color; +} + + +/*************************************************************************************** +** Function name: drawLine +** Description: draw a line between 2 arbitrary points +*************************************************************************************x*/ +void TFT_eSprite::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color) +{ + boolean steep = abs(y1 - y0) > abs(x1 - x0); + if (steep) { + swap_coord(x0, y0); + swap_coord(x1, y1); + } + + if (x0 > x1) { + swap_coord(x0, x1); + swap_coord(y0, y1); + } + + int32_t dx = x1 - x0, dy = abs(y1 - y0);; + + int32_t err = dx >> 1, ystep = -1, xs = x0, dlen = 0; + + if (y0 < y1) ystep = 1; + + // Split into steep and not steep for FastH/V separation + if (steep) { + for (; x0 <= x1; x0++) { + dlen++; + err -= dy; + if (err < 0) { + err += dx; + if (dlen == 1) drawPixel(y0, xs, color); + else drawFastVLine(y0, xs, dlen, color); + dlen = 0; y0 += ystep; xs = x0 + 1; + } + } + if (dlen) drawFastVLine(y0, xs, dlen, color); + } + else + { + for (; x0 <= x1; x0++) { + dlen++; + err -= dy; + if (err < 0) { + err += dx; + if (dlen == 1) drawPixel(xs, y0, color); + else drawFastHLine(xs, y0, dlen, color); + dlen = 0; y0 += ystep; xs = x0 + 1; + } + } + if (dlen) drawFastHLine(xs, y0, dlen, color); + } +} + + +/*************************************************************************************** +** Function name: drawFastVLine +** Description: draw a vertical line +*************************************************************************************x*/ +void TFT_eSprite::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) +{ + if ((x < 0) || (x >= _iwidth) || (y >= _iheight)) return; + if (y < 0) { h += y; y = 0; } + if ((y + h) > _iheight) h = _iheight - y; + + if (h < 1) return; + color = (color >> 8) | (color << 8); + while (h--) _img[x + _iwidth * y++] = (uint16_t) color; +} + + +/*************************************************************************************** +** Function name: drawFastHLine +** Description: draw a horizontal line +*************************************************************************************x*/ +void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) +{ + if ((y < 0) || (x >= _iwidth) || (y >= _iheight)) return; + if (x < 0) { w += x; x = 0; } + if ((x + w) > _iwidth) w = _iwidth - x; + + if (w < 1) return; + color = (color >> 8) | (color << 8); + while (w--) _img[_iwidth * y + x++] = (uint16_t) color; +} + + +/*************************************************************************************** +** Function name: fillRect +** Description: draw a filled rectangle +*************************************************************************************x*/ +void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) +{ + if (x < 0) { w += x; x = 0; } + + if ((x < 0) || (y < 0) || (x >= _iwidth) || (y >= _iheight)) return; + if ((x + w) > _iwidth) w = _iwidth - x; + if ((y + h) > _iheight) h = _iheight - y; + if ((w < 1) || (h < 1)) return; + color = (color >> 8) | (color << 8); + + while (h--) { + int32_t ix = x, iw = w; + while (iw--) _img[_iwidth * y + ix++] = (uint16_t) color; + y++; + } +} + + +/*************************************************************************************** +** Function name: write +** Description: draw characters piped through serial stream +*************************************************************************************x*/ +size_t TFT_eSprite::write(uint8_t utf8) +{ + if (utf8 == '\r') return 1; + + uint8_t uniCode = utf8; // Work with a copy + if (utf8 == '\n') uniCode+=22; // Make it a valid space character to stop errors + + uint16_t width = 0; + uint16_t height = 0; + +//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv DEBUG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + //Serial.print((uint8_t) uniCode); // Debug line sends all printed TFT text to serial port + //Serial.println(uniCode, HEX); // Debug line sends all printed TFT text to serial port + //delay(5); // Debug optional wait for serial port to flush through +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DEBUG ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +#ifdef LOAD_GFXFF + if(!gfxFont) { +#endif +//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + +#ifdef LOAD_FONT2 + if (textfont == 2) + { + // This is 20us faster than using the fontdata structure (0.443ms per character instead of 0.465ms) + width = pgm_read_byte(widtbl_f16 + uniCode-32); + height = chr_hgt_f16; + // Font 2 is rendered in whole byte widths so we must allow for this + width = (width + 6) / 8; // Width in whole bytes for font 2, should be + 7 but must allow for font width change + width = width * 8; // Width converted back to pixles + } + #ifdef LOAD_RLE + else + #endif +#endif + +#ifdef LOAD_RLE + { + if ((textfont>2) && (textfont<9)) + { + // Uses the fontinfo struct array to avoid lots of 'if' or 'switch' statements + // A tad slower than above but this is not significant and is more convenient for the RLE fonts + width = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[textfont].widthtbl ) ) + uniCode-32 ); + height= pgm_read_byte( &fontdata[textfont].height ); + } + } +#endif + +#ifdef LOAD_GLCD + if (textfont==1) + { + width = 6; + height = 8; + } +#else + if (textfont==1) return 0; +#endif + + height = height * textsize; + + if (utf8 == '\n') + { + _icursor_y += height; + _icursor_x = 0; + } + else + { + if (textwrap && (_icursor_x + width * textsize > _iwidth)) + { + _icursor_y += height; + _icursor_x = 0; + } + _icursor_x += drawChar(uniCode, _icursor_x, _icursor_y, textfont); + } + +//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +#ifdef LOAD_GFXFF + } // Custom GFX font + else + { + + if(utf8 == '\n') { + _icursor_x = 0; + _icursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + } else if(uniCode != '\r') { + if (uniCode > (uint8_t)pgm_read_byte(&gfxFont->last)) uniCode = pgm_read_byte(&gfxFont->first); + + if(uniCode >= pgm_read_byte(&gfxFont->first)) { + uint8_t c2 = uniCode - pgm_read_byte(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); + uint8_t w = pgm_read_byte(&glyph->width), + h = pgm_read_byte(&glyph->height); + if((w > 0) && (h > 0)) { // Is there an associated bitmap? + int16_t xo = (int8_t)pgm_read_byte(&glyph->xOffset); + if(textwrap && ((_icursor_x + textsize * (xo + w)) > _iwidth)) { + // Drawing character would go off right edge; wrap to new line + _icursor_x = 0; + _icursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + } + drawChar(_icursor_x, _icursor_y, uniCode, textcolor, textbgcolor, textsize); + } + _icursor_x += pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize; + } + } + + } +#endif // LOAD_GFXFF +//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + return 1; +} + + +/*************************************************************************************** +** Function name: drawChar +** Description: draw a unicode onto the screen +*************************************************************************************x*/ +int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y) +{ + return drawChar(uniCode, x, y, textfont); +} + +int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y, int font) +{ + + if (font==1) + { +#ifdef LOAD_GLCD + #ifndef LOAD_GFXFF + drawChar(x, y, uniCode, textcolor, textbgcolor, textsize); + return 6 * textsize; + #endif +#else + #ifndef LOAD_GFXFF + return 0; + #endif +#endif + +#ifdef LOAD_GFXFF + drawChar(x, y, uniCode, textcolor, textbgcolor, textsize); + if(!gfxFont) { // 'Classic' built-in font + #ifdef LOAD_GLCD + return 6 * textsize; + #else + return 0; + #endif + } + else + { + if (uniCode > pgm_read_byte(&gfxFont->last)) uniCode = pgm_read_byte(&gfxFont->first); + + if(uniCode >= pgm_read_byte(&gfxFont->first)) + { + uint8_t c2 = uniCode - pgm_read_byte(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); + return pgm_read_byte(&glyph->xAdvance) * textsize; + } + else + { + return 0; + } + } +#endif + } + + int width = 0; + int height = 0; + uint32_t flash_address = 0; + uniCode -= 32; + +#ifdef LOAD_FONT2 + if (font == 2) + { + // This is faster than using the fontdata structure + flash_address = pgm_read_dword(&chrtbl_f16[uniCode]); + width = pgm_read_byte(widtbl_f16 + uniCode); + height = chr_hgt_f16; + } + #ifdef LOAD_RLE + else + #endif +#endif + +#ifdef LOAD_RLE + { + if ((font>2) && (font<9)) + { + // This is slower than above but is more convenient for the RLE fonts + flash_address = pgm_read_dword( pgm_read_dword( &(fontdata[font].chartbl ) ) + uniCode*sizeof(void *) ); + width = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[font].widthtbl ) ) + uniCode ); + height= pgm_read_byte( &fontdata[font].height ); + } + } +#endif + + int w = width; + int pX = 0; + int pY = y; + byte line = 0; + +#ifdef LOAD_FONT2 // chop out code if we do not need it + if (font == 2) { + w = w + 6; // Should be + 7 but we need to compensate for width increment + w = w / 8; + if (x + width * textsize >= (int16_t)_iwidth) return width * textsize ; + + for (int i = 0; i < height; i++) + { + if (textcolor != textbgcolor) fillRect(x, pY, width * textsize, textsize, textbgcolor); + + for (int k = 0; k < w; k++) + { + line = pgm_read_byte((uint8_t *)flash_address + w * i + k); + if (line) { + if (textsize == 1) { + pX = x + k * 8; + if (line & 0x80) drawPixel(pX, pY, textcolor); + if (line & 0x40) drawPixel(pX + 1, pY, textcolor); + if (line & 0x20) drawPixel(pX + 2, pY, textcolor); + if (line & 0x10) drawPixel(pX + 3, pY, textcolor); + if (line & 0x08) drawPixel(pX + 4, pY, textcolor); + if (line & 0x04) drawPixel(pX + 5, pY, textcolor); + if (line & 0x02) drawPixel(pX + 6, pY, textcolor); + if (line & 0x01) drawPixel(pX + 7, pY, textcolor); + } + else { + pX = x + k * 8 * textsize; + if (line & 0x80) fillRect(pX, pY, textsize, textsize, textcolor); + if (line & 0x40) fillRect(pX + textsize, pY, textsize, textsize, textcolor); + if (line & 0x20) fillRect(pX + 2 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x10) fillRect(pX + 3 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x08) fillRect(pX + 4 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x04) fillRect(pX + 5 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x02) fillRect(pX + 6 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x01) fillRect(pX + 7 * textsize, pY, textsize, textsize, textcolor); + } + } + } + pY += textsize; + } + } + + #ifdef LOAD_RLE + else + #endif +#endif //FONT2 + +#ifdef LOAD_RLE //674 bytes of code + // Font is not 2 and hence is RLE encoded + { + w *= height; // Now w is total number of pixels in the character + + if (textcolor != textbgcolor) fillRect(x, pY, width * textsize, textsize * height, textbgcolor); + int px = 0, py = pY; // To hold character block start and end column and row values + int pc = 0; // Pixel count + byte np = textsize * textsize; // Number of pixels in a drawn pixel + + byte tnp = 0; // Temporary copy of np for while loop + byte ts = textsize - 1; // Temporary copy of textsize + // 16 bit pixel count so maximum font size is equivalent to 180x180 pixels in area + // w is total number of pixels to plot to fill character block + while (pc < w) + { + line = pgm_read_byte((uint8_t *)flash_address); + flash_address++; // 20 bytes smaller by incrementing here + if (line & 0x80) { + line &= 0x7F; + line++; + if (ts) { + px = x + textsize * (pc % width); // Keep these px and py calculations outside the loop as they are slow + py = y + textsize * (pc / width); + } + else { + px = x + pc % width; // Keep these px and py calculations outside the loop as they are slow + py = y + pc / width; + } + while (line--) { + pc++; + setWindow(px, py, px + ts, py + ts); + + if (ts) { + tnp = np; + while (tnp--) { + pushColor(textcolor); + } + } + else { + pushColor(textcolor); + } + px += textsize; + + if (px >= (x + width * textsize)) + { + px = x; + py += textsize; + } + } + } + else { + line++; + pc += line; + } + } + } + // End of RLE font rendering +#endif + return width * textsize; // x + +} - *****************************************************/ diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 29571f5..ffb1c23 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -30,6 +30,11 @@ #define SPI_FREQUENCY 20000000 #endif +// If the frequency is not defined, set a default +#ifndef SPI_TOUCH_FREQUENCY + #define SPI_TOUCH_FREQUENCY 2500000 +#endif + // Only load the fonts defined in User_Setup.h (to save space) // Set flag so RLE rendering code is optionally compiled #ifdef LOAD_GLCD @@ -267,8 +272,11 @@ typedef struct { // Now fill the structure const PROGMEM fontinfo fontdata [] = { + #ifdef LOAD_GLCD + { (const uint8_t *)font, widtbl_null, 0, 0 }, + #else { (const uint8_t *)chrtbl_null, widtbl_null, 0, 0 }, - + #endif // GLCD font (Font 1) does not have all parameters { (const uint8_t *)chrtbl_null, widtbl_null, 8, 7 }, @@ -320,25 +328,32 @@ class TFT_eSPI : public Print { void init(void), begin(void); // Same - begin included for backwards compatibility - void drawPixel(uint32_t x, uint32_t y, uint32_t color); + // These are virtual so the TFT_eSprite class can override them with sprite specific functions + virtual void drawPixel(uint32_t x, uint32_t y, uint32_t color), + drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t font), + setWindow(int16_t x0, int16_t y0, int16_t x1, int16_t y1), + pushColor(uint16_t color), + pushColor(uint16_t color, uint16_t len), + drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color), + drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color), + drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color), + fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color); - void drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t font), - setWindow(int16_t x0, int16_t y0, int16_t x1, int16_t y1), + virtual int16_t drawChar(unsigned int uniCode, int x, int y, int font), + drawChar(unsigned int uniCode, int x, int y), + height(void), + width(void); - pushColor(uint16_t color), - pushColor(uint16_t color, uint16_t len), + virtual size_t write(uint8_t); - pushColors(uint16_t *data, uint8_t len), + + // The TFT_eSprite class inherits the following functions + void pushColors(uint16_t *data, uint8_t len), pushColors(uint8_t *data, uint32_t len), - fillScreen(uint32_t color), + fillScreen(uint32_t color); - drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color), - drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color), - drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color), - - drawRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color), - fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color), + void drawRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color), drawRoundRect(int32_t x0, int32_t y0, int32_t w, int32_t h, int32_t radius, uint32_t color), fillRoundRect(int32_t x0, int32_t y0, int32_t w, int32_t h, int32_t radius, uint32_t color), @@ -392,6 +407,7 @@ class TFT_eSPI : public Print { void readRect(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, uint16_t *data); // Write a block of pixels to the screen void pushRect(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, uint16_t *data); + void pushSprite(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint16_t *data); // 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 RGB 8 bit colour values of each pixel @@ -403,10 +419,8 @@ class TFT_eSPI : public Print { uint16_t fontsLoaded(void), color565(uint8_t r, uint8_t g, uint8_t b); - - int16_t drawChar(unsigned int uniCode, int x, int y, int font), - drawChar(unsigned int uniCode, int x, int y), - drawNumber(long long_num,int poX, int poY, int font), + + int16_t drawNumber(long long_num,int poX, int poY, int font), drawNumber(long long_num,int poX, int poY), drawFloat(float floatNumber,int decimal,int poX, int poY, int font), drawFloat(float floatNumber,int decimal,int poX, int poY), @@ -423,9 +437,7 @@ class TFT_eSPI : public Print { drawCentreString(const String& string, int dX, int poY, int font), // Deprecated, use setTextDatum() and drawString() drawRightString(const String& string, int dX, int poY, int font); // Deprecated, use setTextDatum() and drawString() - int16_t height(void), - width(void), - textWidth(const char *string, int font), + int16_t textWidth(const char *string, int font), textWidth(const char *string), textWidth(const String& string, int font), textWidth(const String& string), @@ -441,7 +453,6 @@ class TFT_eSPI : public Print { void calibrateTouch(uint16_t *data, uint32_t color_fg, uint32_t color_bg, uint8_t size); void setTouch(uint16_t *data); - virtual size_t write(uint8_t); private: @@ -531,6 +542,74 @@ class TFT_eSPI_Button { boolean currstate, laststate; }; + +/*************************************************************************************** +// The following class creates Sprites in RAM, graphics can then be drawn in the Sprite +// and rendered quickly onto the TFT screen. The class inherits the graphics functions +// from the TFT_eSPI class. Some functions are overridden by this class so that the +// graphics are written to the Sprite rather than the TFT. +***************************************************************************************/ + +class TFT_eSprite : public TFT_eSPI { + + public: + + TFT_eSprite(TFT_eSPI *tft); + + uint16_t* createSprite(int16_t w, int16_t y); // 16 bpp + void deleteSprite(void); + + void drawPixel(uint32_t x, uint32_t y, uint32_t color); + + void drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t size), + + fillSprite(uint32_t color), + + setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1), + pushColor(uint32_t color), + pushColor(uint32_t color, uint16_t len), + + drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color), + drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color), + drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color), + + fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color), + + setCursor(int16_t x, int16_t y); + + // Read the colour of a pixel at x,y and return value in 565 format + uint16_t readPixel(int32_t x0, int32_t y0); + + // Write a block of pixels to the sprite + void pushRect(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, uint16_t *data); + void pushBitmap(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, uint16_t *data); + + void pushSprite(int32_t x, int32_t y); + + int16_t drawChar(unsigned int uniCode, int x, int y, int font), + drawChar(unsigned int uniCode, int x, int y); + + int16_t height(void), + width(void); + + size_t write(uint8_t); + + private: + + TFT_eSPI *_tft; + + protected: + + uint16_t *_img; + + int32_t _icursor_x, _icursor_y, _xs, _ys, _xe, _ye, _xptr, _yptr; + + int32_t _iwidth, _iheight; // Display w/h as modified by current rotation + +}; + + + #endif /*************************************************** diff --git a/User_Setup_Select.h b/User_Setup_Select.h index 8129612..71050e4 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -28,6 +28,7 @@ //#include // Setup file configured for my ILI9163 128x128 display //#include // Setup file configured for my ST7735 //#include // Setup file configured for my stock RPi TFT with touch +//#include // Setup file configured for my stock RPi TFT with touch //#include // Setup file template for copying/editting diff --git a/examples/Sprite/Sprite_RLE_Font_test/Sprite_RLE_Font_test.ino b/examples/Sprite/Sprite_RLE_Font_test/Sprite_RLE_Font_test.ino new file mode 100644 index 0000000..32c22b3 --- /dev/null +++ b/examples/Sprite/Sprite_RLE_Font_test/Sprite_RLE_Font_test.ino @@ -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 // Graphics and font library for ST7735 driver chip +#include + +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);; +} + diff --git a/examples/Sprite/Sprite_TFT_Rainbow/Sprite_TFT_Rainbow.ino b/examples/Sprite/Sprite_TFT_Rainbow/Sprite_TFT_Rainbow.ino new file mode 100644 index 0000000..8a680a8 --- /dev/null +++ b/examples/Sprite/Sprite_TFT_Rainbow/Sprite_TFT_Rainbow.ino @@ -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 // Graphics and font library +#include + +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); + } +} + + + + + + diff --git a/examples/Sprite/Sprite_drawPixel/Sprite_drawPixel.ino b/examples/Sprite/Sprite_drawPixel/Sprite_drawPixel.ino new file mode 100644 index 0000000..bc64952 --- /dev/null +++ b/examples/Sprite/Sprite_drawPixel/Sprite_drawPixel.ino @@ -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 // 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! +} + diff --git a/examples/Sprite/Sprite_scroll_text/Sprite_scroll_text.ino b/examples/Sprite/Sprite_scroll_text/Sprite_scroll_text.ino new file mode 100644 index 0000000..ce8cc1b --- /dev/null +++ b/examples/Sprite/Sprite_scroll_text/Sprite_scroll_text.ino @@ -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 // 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; +} + +