diff --git a/Extensions/Button.cpp b/Extensions/Button.cpp new file mode 100644 index 0000000..88a8bc0 --- /dev/null +++ b/Extensions/Button.cpp @@ -0,0 +1,76 @@ +/*************************************************************************************** +** Code for the GFX button UI element +** Grabbed from Adafruit_GFX library and enhanced to handle any label font +***************************************************************************************/ +TFT_eSPI_Button::TFT_eSPI_Button(void) { + _gfx = 0; +} + +// Classic initButton() function: pass center & size +void TFT_eSPI_Button::initButton( + TFT_eSPI *gfx, int16_t x, int16_t y, uint16_t w, uint16_t h, + uint16_t outline, uint16_t fill, uint16_t textcolor, + char *label, uint8_t textsize) +{ + // Tweak arguments and pass to the newer initButtonUL() function... + initButtonUL(gfx, x - (w / 2), y - (h / 2), w, h, outline, fill, + textcolor, label, textsize); +} + +// Newer function instead accepts upper-left corner & size +void TFT_eSPI_Button::initButtonUL( + TFT_eSPI *gfx, int16_t x1, int16_t y1, uint16_t w, uint16_t h, + uint16_t outline, uint16_t fill, uint16_t textcolor, + char *label, uint8_t textsize) +{ + _x1 = x1; + _y1 = y1; + _w = w; + _h = h; + _outlinecolor = outline; + _fillcolor = fill; + _textcolor = textcolor; + _textsize = textsize; + _gfx = gfx; + strncpy(_label, label, 9); +} + +void TFT_eSPI_Button::drawButton(boolean inverted) { + uint16_t fill, outline, text; + + if(!inverted) { + fill = _fillcolor; + outline = _outlinecolor; + text = _textcolor; + } else { + fill = _textcolor; + outline = _outlinecolor; + text = _fillcolor; + } + + uint8_t r = min(_w, _h) / 4; // Corner radius + _gfx->fillRoundRect(_x1, _y1, _w, _h, r, fill); + _gfx->drawRoundRect(_x1, _y1, _w, _h, r, outline); + + _gfx->setTextColor(text); + _gfx->setTextSize(_textsize); + + uint8_t tempdatum = _gfx->getTextDatum(); + _gfx->setTextDatum(MC_DATUM); + _gfx->drawString(_label, _x1 + (_w/2), _y1 + (_h/2)); + _gfx->setTextDatum(tempdatum); +} + +boolean TFT_eSPI_Button::contains(int16_t x, int16_t y) { + return ((x >= _x1) && (x < (_x1 + _w)) && + (y >= _y1) && (y < (_y1 + _h))); +} + +void TFT_eSPI_Button::press(boolean p) { + laststate = currstate; + currstate = p; +} + +boolean TFT_eSPI_Button::isPressed() { return currstate; } +boolean TFT_eSPI_Button::justPressed() { return (currstate && !laststate); } +boolean TFT_eSPI_Button::justReleased() { return (!currstate && laststate); } diff --git a/Extensions/Button.h b/Extensions/Button.h new file mode 100644 index 0000000..e44a8f4 --- /dev/null +++ b/Extensions/Button.h @@ -0,0 +1,38 @@ +/*************************************************************************************** +// The following button class has been ported over from the Adafruit_GFX library so +// should be compatible. +// A slightly different implementation in this TFT_eSPI library allows the button +// legends to be in any font +***************************************************************************************/ + +class TFT_eSPI_Button { + + public: + TFT_eSPI_Button(void); + // "Classic" initButton() uses center & size + void initButton(TFT_eSPI *gfx, int16_t x, int16_t y, + uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, + uint16_t textcolor, char *label, uint8_t textsize); + + // New/alt initButton() uses upper-left corner & size + void initButtonUL(TFT_eSPI *gfx, int16_t x1, int16_t y1, + uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, + uint16_t textcolor, char *label, uint8_t textsize); + void drawButton(boolean inverted = false); + boolean contains(int16_t x, int16_t y); + + void press(boolean p); + boolean isPressed(); + boolean justPressed(); + boolean justReleased(); + + private: + TFT_eSPI *_gfx; + int16_t _x1, _y1; // Coordinates of top-left corner + uint16_t _w, _h; + uint8_t _textsize; + uint16_t _outlinecolor, _fillcolor, _textcolor; + char _label[10]; + + boolean currstate, laststate; +}; diff --git a/Extensions/Smooth_font.cpp b/Extensions/Smooth_font.cpp new file mode 100644 index 0000000..dc41edb --- /dev/null +++ b/Extensions/Smooth_font.cpp @@ -0,0 +1,511 @@ + // Coded by Bodmer 10/2/18, see license in root directory. + // This is part of the TFT_eSPI class and is associated with anti-aliased font functions + + +//////////////////////////////////////////////////////////////////////////////////////// +// New anti-aliased (smoothed) font functions added below +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: loadFont +** Description: loads parameters from a new font vlw file stored in SPIFFS +*************************************************************************************x*/ +void TFT_eSPI::loadFont(String fontName) +{ + /* + The vlw font format does not appear to be documented anywhere, so some reverse + engineering has been applied! + + Header of vlw file comprises 6 uint32_t parameters (24 bytes total): + 1. The gCount (number of character glyphs) + 2. A version number (0xB = 11 for the one I am using) + 3. The font size (in points, not pixels) + 4. Deprecated mboxY parameter (typically set to 0) + 5. Ascent in pixels from baseline to top of "d" + 6. Descent in pixels from baseline to bottom of "p" + + Next are gCount sets of values for each glyph, each set comprises 7 int32t parameters (28 bytes): + 1. Glyph Unicode stored as a 32 bit value + 2. Height of bitmap bounding box + 3. Width of bitmap bounding box + 4. gxAdvance for cursor (setWidth in Processing) + 5. dY = distance from cursor baseline to top of glyph bitmap (signed value +ve = up) + 6. dX = distance from cursor to left side of glyph bitmap (signed value -ve = left) + 7. padding value, typically 0 + + The bitmaps start next at 24 + (28 * gCount) bytes from the start of the file. + Each pixel is 1 byte, an 8 bit Alpha value which represents the transparency from + 0xFF foreground colour, 0x00 background. The sketch uses a linear interpolation + between the foreground and background RGB component colours. e.g. + pixelRed = ((fgRed * alpha) + (bgRed * (255 - alpha))/255 + To gain a performance advantage fixed point arithmetic is used with rounding and + division by 256 (shift right 8 bits is faster). + + After the bitmaps is: + 1 byte for font name string length (excludes null) + a zero terminated character string giving the font name + 1 byte for Postscript name string length + a zero/one terminated character string giving the font name + last byte is 0 for non-anti-aliased and 1 for anti-aliased (smoothed) + + Then the font name seen by Java when it's created + Then the postscript name of the font + Then a boolean to tell if smoothing is on or not. + + Glyph bitmap example is: + // Cursor coordinate positions for this and next character are marked by 'C' + // C<------- gxAdvance ------->C gxAdvance is how far to move cursor for next glyph cursor position + // | | + // | | ascent is top of "d", descent is bottom of "p" + // +-- gdX --+ ascent + // | +-- gWidth--+ | gdX is offset to left edge of glyph bitmap + // | + x@.........@x + | gdX may be negative e.g. italic "y" tail extending to left of + // | | @@.........@@ | | cursor position, plot top left corner of bitmap at (cursorX + gdX) + // | | @@.........@@ gdY | gWidth and gHeight are glyph bitmap dimensions + // | | .@@@.....@@@@ | | + // | gHeight ....@@@@@..@@ + + <-- baseline + // | | ...........@@ | + // | | ...........@@ | gdY is the offset to the top edge of the bitmap + // | | .@@.......@@. descent plot top edge of bitmap at (cursorY + yAdvance - gdY) + // | + x..@@@@@@@..x | x marks the corner pixels of the bitmap + // | | + // +---------------------------+ yAdvance is y delta for the next line, font size or (ascent + descent) + // some fonts can overlay in y direction so may need a user adjust value + + */ + + _gFontFilename = "/" + fontName + ".vlw"; + + fontFile = SPIFFS.open( _gFontFilename, "r"); + + if(!fontFile) return; + + //unloadFont(); + + fontFile.seek(0, fs::SeekSet); + + gFont.gCount = (uint16_t)readInt32(); // glyph count in file + readInt32(); // vlw encoder version - discard + gFont.yAdvance = (uint16_t)readInt32(); // Font size in points, not pixels + readInt32(); // discard + gFont.ascent = (uint16_t)readInt32(); // top of "d" + gFont.descent = (uint16_t)readInt32(); // bottom of "p" + + // These next gFont values will be updated when the Metrics are fetched + gFont.maxAscent = gFont.ascent; // Determined from metrics + gFont.maxDescent = gFont.descent; // Determined from metrics + gFont.yAdvance = gFont.ascent + gFont.descent; + gFont.spaceWidth = gFont.yAdvance / 4; // Guess at space width + + fontLoaded = true; + + // Fetch the metrics for each glyph + loadMetrics(gFont.gCount); + + //fontFile.close(); +} + + +/*************************************************************************************** +** Function name: loadMetrics +** Description: Get the metrics for each glyph and store in RAM +*************************************************************************************x*/ +//#define SHOW_ASCENT_DESCENT +void TFT_eSPI::loadMetrics(uint16_t gCount) +{ + uint32_t headerPtr = 24; + uint32_t bitmapPtr = 24 + gCount * 28; + + gUnicode = (uint16_t*)malloc( gCount * 2); // Unicode 16 bit Basic Multilingual Plane (0-FFFF) + gHeight = (uint8_t*)malloc( gCount ); // Height of glyph + gWidth = (uint8_t*)malloc( gCount ); // Width of glyph + gxAdvance = (uint8_t*)malloc( gCount ); // xAdvance - to move x cursor + gdY = (int8_t*)malloc( gCount ); // offset from bitmap top edge from lowest point in any character + gdX = (int8_t*)malloc( gCount ); // offset for bitmap left edge relative to cursor X + gBitmap = (uint32_t*)malloc( gCount * 4); // seek pointer to glyph bitmap in SPIFFS file + +#ifdef SHOW_ASCENT_DESCENT + Serial.print("ascent = "); Serial.println(gFont.ascent); + Serial.print("descent = "); Serial.println(gFont.descent); +#endif + + uint16_t gNum = 0; + fontFile.seek(headerPtr, fs::SeekSet); + while (gNum < gCount) + { + gUnicode[gNum] = (uint16_t)readInt32(); // Unicode code point value + gHeight[gNum] = (uint8_t)readInt32(); // Height of glyph + gWidth[gNum] = (uint8_t)readInt32(); // Width of glyph + gxAdvance[gNum] = (uint8_t)readInt32(); // xAdvance - to move x cursor + gdY[gNum] = (int8_t)readInt32(); // y delta from baseline + gdX[gNum] = (int8_t)readInt32(); // x delta from cursor + readInt32(); // ignored + + // Different glyph sets have different ascent values not always based on "d", so get maximum glyph ascent + if (gdY[gNum] > gFont.maxAscent) + { + // Avoid UTF coding values and characters that tend to give duff values + if (((gUnicode[gNum] > 0x20) && (gUnicode[gNum] < 0xA0) && (gUnicode[gNum] != 0x7F)) || (gUnicode[gNum] > 0xFF)) + { + gFont.maxAscent = gdY[gNum]; +#ifdef SHOW_ASCENT_DESCENT + Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", maxAscent = "); Serial.println(gFont.maxAscent); +#endif + } + } + + // Different glyph sets have different descent values not always based on "p", so get maximum glyph descent + if (((int16_t)gHeight[gNum] - (int16_t)gdY[gNum]) > gFont.maxDescent) + { + // Avoid UTF coding values and characters that tend to give duff values + if (((gUnicode[gNum] > 0x20) && (gUnicode[gNum] < 0xA0) && (gUnicode[gNum] != 0x7F)) || (gUnicode[gNum] > 0xFF)) + { + gFont.maxDescent = gHeight[gNum] - gdY[gNum]; +#ifdef SHOW_ASCENT_DESCENT + Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", maxDescent = "); Serial.println(gHeight[gNum] - gdY[gNum]); +#endif + } + } + + gBitmap[gNum] = bitmapPtr; + + headerPtr += 28; + + bitmapPtr += gWidth[gNum] * gHeight[gNum]; + + gNum++; + yield(); + } + + gFont.yAdvance = gFont.maxAscent + gFont.maxDescent; + + gFont.spaceWidth = (gFont.ascent + gFont.descent) * 2/7; // Guess at space width +} + + +/*************************************************************************************** +** Function name: deleteMetrics +** Description: Delete the old glyph metrics and free up the memory +*************************************************************************************x*/ +void TFT_eSPI::unloadFont( void ) +{ + if (gUnicode) + { + free(gUnicode); + gUnicode = NULL; + } + + if (gHeight) + { + free(gHeight); + gHeight = NULL; + } + + if (gWidth) + { + free(gWidth); + gWidth = NULL; + } + + if (gxAdvance) + { + free(gxAdvance); + gxAdvance = NULL; + } + + if (gdY) + { + free(gdY); + gdY = NULL; + } + + if (gdX) + { + free(gdX); + gdX = NULL; + } + + if (gBitmap) + { + free(gBitmap); + gBitmap = NULL; + } + fontFile.close(); + fontLoaded = false; +} + + +/*************************************************************************************** +** Function name: decodeUTF8 +** Description: Line buffer UTF-8 decoder with fall-back to extended ASCII +*************************************************************************************x*/ +#define DECODE_UTF8 +uint16_t TFT_eSPI::decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining) +{ + byte c = buf[(*index)++]; + //Serial.print("Byte from string = 0x"); Serial.println(c, HEX); + +#ifdef DECODE_UTF8 + // 7 bit Unicode + if ((c & 0x80) == 0x00) return c; + + // 11 bit Unicode + if (((c & 0xE0) == 0xC0) && (remaining > 1)) + return ((c & 0x1F)<<6) | (buf[(*index)++]&0x3F); + + // 16 bit Unicode + if (((c & 0xF0) == 0xE0) && (remaining > 2)) + return ((c & 0x0F)<<12) | ((buf[(*index)++]&0x3F)<<6) | ((buf[(*index)++]&0x3F)); + + // 21 bit Unicode not supported so fall-back to extended ASCII + // if ((c & 0xF8) == 0xF0) return c; +#endif + + return c; // fall-back to extended ASCII +} + +/*************************************************************************************** +** Function name: decodeUTF8 +** Description: Serial UTF-8 decoder with fall-back to extended ASCII +*************************************************************************************x*/ +uint16_t TFT_eSPI::decodeUTF8(uint8_t c) +{ + +#ifdef DECODE_UTF8 + if (decoderState == 0) + { + // 7 bit Unicode + if ((c & 0x80) == 0x00) return (uint16_t)c; + + // 11 bit Unicode + if ((c & 0xE0) == 0xC0) + { + decoderBuffer = ((c & 0x1F)<<6); + decoderState = 1; + return 0; + } + + // 16 bit Unicode + if ((c & 0xF0) == 0xE0) + { + decoderBuffer = ((c & 0x0F)<<12); + decoderState = 2; + return 0; + } + // 21 bit Unicode not supported so fall-back to extended ASCII + if ((c & 0xF8) == 0xF0) return (uint16_t)c; + } + else + { + if (decoderState == 2) + { + decoderBuffer |= ((c & 0x3F)<<6); + decoderState--; + return 0; + } + else + { + decoderBuffer |= (c & 0x3F); + decoderState = 0; + return decoderBuffer; + } + } +#endif + + return (uint16_t)c; // fall-back to extended ASCII +} + + + +/*************************************************************************************** +** Function name: alphaBlend +** Description: Blend foreground and background and return new colour +*************************************************************************************x*/ +uint16_t TFT_eSPI::alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc) +{ + // For speed use fixed point maths and rounding to permit a power of 2 division + uint16_t fgR = ((fgc >> 10) & 0x3E) + 1; + uint16_t fgG = ((fgc >> 4) & 0x7E) + 1; + uint16_t fgB = ((fgc << 1) & 0x3E) + 1; + + uint16_t bgR = ((bgc >> 10) & 0x3E) + 1; + uint16_t bgG = ((bgc >> 4) & 0x7E) + 1; + uint16_t bgB = ((bgc << 1) & 0x3E) + 1; + + // Shift right 1 to drop rounding bit and shift right 8 to divide by 256 + uint16_t r = (((fgR * alpha) + (bgR * (255 - alpha))) >> 9); + uint16_t g = (((fgG * alpha) + (bgG * (255 - alpha))) >> 9); + uint16_t b = (((fgB * alpha) + (bgB * (255 - alpha))) >> 9); + + // Combine RGB565 colours into 16 bits + return (r << 11) | (g << 5) | (b << 0); +} + + +/*************************************************************************************** +** Function name: readInt32 +** Description: Get a 32 bit integer from the font file +*************************************************************************************x*/ +uint32_t TFT_eSPI::readInt32(void) +{ + uint32_t val = 0; + val |= fontFile.read() << 24; + val |= fontFile.read() << 16; + val |= fontFile.read() << 8; + val |= fontFile.read(); + return val; +} + + +/*************************************************************************************** +** Function name: getUnicodeIndex +** Description: Get the font file index of a Unicode character +*************************************************************************************x*/ +bool TFT_eSPI::getUnicodeIndex(uint16_t unicode, uint16_t *index) +{ + for (uint16_t i = 0; i < gFont.gCount; i++) + { + if (gUnicode[i] == unicode) + { + *index = i; + return true; + } + } + return false; +} + + +/*************************************************************************************** +** Function name: drawGlyph +** Description: Write a character to the TFT cursor position +*************************************************************************************x*/ +// Expects file to be open +void TFT_eSPI::drawGlyph(uint16_t code) +{ + if (code < 0x21) + { + if (code == 0x20) { + cursor_x += gFont.spaceWidth; + return; + } + + if (code == '\n') { + cursor_x = 0; + cursor_y += gFont.yAdvance; + if (cursor_y >= _height) cursor_y = 0; + return; + } + } + + uint16_t gNum = 0; + bool found = getUnicodeIndex(code, &gNum); + + uint16_t fg = textcolor; + uint16_t bg = textbgcolor; + + if (found) + { + + if (textwrapX && (cursor_x + gWidth[gNum] + gdX[gNum] > _width)) + { + cursor_y += gFont.yAdvance; + cursor_x = 0; + } + if (textwrapY && ((cursor_y + gFont.yAdvance) >= _height)) cursor_y = 0; + if (cursor_x == 0) cursor_x -= gdX[gNum]; + + fontFile.seek(gBitmap[gNum], fs::SeekSet); // This is taking >30ms for a significant position shift + + uint8_t pbuffer[gWidth[gNum]]; + + uint16_t xs = 0; + uint16_t dl = 0; + + for (int y = 0; y < gHeight[gNum]; y++) + { + fontFile.read(pbuffer, gWidth[gNum]); //= width()) { + cursorX = -gdX[i]; + + cursorY += gFont.yAdvance; + if (cursorY + gFont.maxAscent + gFont.descent >= height()) { + cursorX = -gdX[i]; + cursorY = 0; + delay(timeDelay); + timeDelay = td; + fillScreen(textbgcolor); + } + } + + setCursor(cursorX, cursorY); + drawGlyph(gUnicode[i]); + cursorX += gxAdvance[i]; + //cursorX += printToSprite( cursorX, cursorY, i ); + yield(); + } + + delay(timeDelay); + fillScreen(textbgcolor); + //fontFile.close(); + +} diff --git a/Extensions/Smooth_font.h b/Extensions/Smooth_font.h new file mode 100644 index 0000000..6ab09fc --- /dev/null +++ b/Extensions/Smooth_font.h @@ -0,0 +1,54 @@ + // Coded by Bodmer 10/2/18, see license in root directory. + // This is part of the TFT_eSPI class and is associated with anti-aliased font functions + + public: + + // These are for the new antialiased fonts + void loadFont(String fontName); + void unloadFont( void ); + bool getUnicodeIndex(uint16_t unicode, uint16_t *index); + + uint16_t decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining); + uint16_t decodeUTF8(uint8_t c); + + uint16_t alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc); + + void drawGlyph(uint16_t code); + void showFont(uint32_t td); + + fs::File fontFile; + + // This is for the whole font + typedef struct + { + uint16_t gCount; // Total number of characters + uint16_t yAdvance; // Line advance + uint16_t spaceWidth; // Width of a space character + int16_t ascent; // Height of top of 'd' above baseline, other characters may be taller + int16_t descent; // Offset to bottom of 'p', other characters may have a larger descent + uint16_t maxAscent; // Maximum ascent found in font + uint16_t maxDescent; // Maximum descent found in font + } fontMetrics; + +fontMetrics gFont = { 0, 0, 0, 0, 0, 0, 0 }; + + // These are for the metrics for each individual glyph (so we don't need to seek this in file and waste time) + uint16_t* gUnicode = NULL; //UTF-16 code, the codes are searched so do not need to be sequential + uint8_t* gHeight = NULL; //cheight + uint8_t* gWidth = NULL; //cwidth + uint8_t* gxAdvance = NULL; //setWidth + int8_t* gdY = NULL; //topExtent + int8_t* gdX = NULL; //leftExtent + uint32_t* gBitmap = NULL; //file pointer to greyscale bitmap + + String _gFontFilename; + + uint8_t decoderState = 0; // UTF8 decoder state + uint16_t decoderBuffer; // Unicode code-point buffer + + bool fontLoaded = false; // Flags when a anti-aliased font is loaded + + private: + + void loadMetrics(uint16_t gCount); + uint32_t readInt32(void); diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp new file mode 100644 index 0000000..ab5d368 --- /dev/null +++ b/Extensions/Sprite.cpp @@ -0,0 +1,1409 @@ +/************************************************************************************** +// 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, see license file in root folder +***************************************************************************************/ +/*************************************************************************************** +// Color bytes are swapped when writing to RAM, this introduces a small overhead but +// there is a nett performance gain by using swapped bytes. +***************************************************************************************/ + +/*************************************************************************************** +** 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; + _bpp16 = true; + _iswapBytes = false; // Do not swap pushImage colour bytes by default + + _created = false; + + _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*/ +// cast returned value to (uint8_t*) for 8 bit or (uint16_t*) for 16 bit colours +void* TFT_eSprite::createSprite(int16_t w, int16_t h) +{ + + if ( _created ) + { + if ( _bpp16 ) return _img; + return _img8; + } + + if ( w < 1 || h < 1 ) return NULL; + + _iwidth = w; + _iheight = h; + + _icursor_x = 0; + _icursor_y = 0; + + // Default scroll rectangle and gap fill colour + _sx = 0; + _sy = 0; + _sw = w; + _sh = h; + _scolor = TFT_BLACK; + + // Add one extra "off screen" pixel to point out-of-bounds setWindow() coordinates + // this means push/writeColor functions do not need additional bounds checks and + // hence will run faster in normal circumstances. + if(_bpp16) + { + _img = (uint16_t*) calloc(w * h + 1, sizeof(uint16_t)); + if (_img) + { + _created = true; + return _img; + } + } + else + { + _img8 = ( uint8_t*) calloc(w * h + 1, sizeof(uint8_t)); + if (_img8) + { + _created = true; + return _img8; + } + } + + return NULL; +} + + +/*************************************************************************************** +** Function name: setDepth +** Description: Set bits per pixel for colour (8 or 16) +*************************************************************************************x*/ + +void* TFT_eSprite::setColorDepth(int8_t b) +{ + // Can't change an existing sprite's colour depth so delete it + if (_created) + { + if (_bpp16) free(_img); + else free(_img8); + } + + // Now define the new colour depth + if ( b > 8 ) _bpp16 = true; // Bytes per pixel + else _bpp16 = false; + + // If it existed, re-create the sprite with the new colour depth + if (_created) + { + _created = false; + return createSprite(_iwidth, _iheight); + } + + return NULL; +} + + +/*************************************************************************************** +** Function name: deleteSprite +** Description: Delete the sprite to free up memory (RAM) +*************************************************************************************x*/ +void TFT_eSprite::deleteSprite(void) +{ + if (!_created ) return; + + if (_bpp16) free(_img); + else free(_img8); + + _created = false; +} + + +/*************************************************************************************** +** Function name: pushSprite +** Description: Push the sprite to the TFT at x, y +*************************************************************************************x*/ +void TFT_eSprite::pushSprite(int32_t x, int32_t y) +{ + if (!_created ) return; + + if (_bpp16) _tft->pushImage(x, y, _iwidth, _iheight, _img ); + //if (_bpp16) TFT_eSPI::pushImage(x, y, _iwidth, _iheight, _img ); + else _tft->pushImage(x, y, _iwidth, _iheight, _img8); +} + + +/*************************************************************************************** +** Function name: pushSprite +** Description: Push the sprite to the TFT at x, y with transparent colour +*************************************************************************************x*/ +void TFT_eSprite::pushSprite(int32_t x, int32_t y, uint16_t transp) +{ + if (!_created ) return; + + if (_bpp16) _tft->pushImage(x, y, _iwidth, _iheight, _img, transp ); + else + { + transp = (uint8_t)((transp & 0xE000)>>8 | (transp & 0x0700)>>6 | (transp & 0x0018)>>3); + _tft->pushImage(x, y, _iwidth, _iheight, _img8, (uint8_t)transp); + } +} + + +/*************************************************************************************** +** Function name: readPixel +** Description: Read 565 colour of a pixel at defined coordinates +*************************************************************************************x*/ +uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) +{ + if (!_created ) return 0; + + if (_bpp16) + { + uint16_t color = _img[x + y * _iwidth]; + return (color >> 8) | (color << 8); + } + + uint16_t color = _img8[x + y * _iwidth]; + if (color != 0) + { + uint8_t blue[] = {0, 11, 21, 31}; + color = (color & 0xE0)<<8 | (color & 0xC0)<<5 + | (color & 0x1C)<<6 | (color & 0x1C)<<3 + | blue[color & 0x03]; + } + + return color; +} + + +/*************************************************************************************** +** Function name: pushImage +** Description: push 565 colour image into a defined area of a sprite +*************************************************************************************x*/ +void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t *data) +{ + if ((x > _iwidth) || (y > _iheight) || (w == 0) || (h == 0) || !_created) return; + + if (_bpp16) + { + for (uint32_t yp = y; yp < y + h; yp++) + { + for (uint32_t xp = x; xp < x + w; xp++) + { + uint16_t color = *data++; + if(!_iswapBytes) color = color<<8 | color>>8; + _img[xp + yp * _iwidth] = color; + } + } + } + else + { + for (uint32_t yp = y; yp < y + h; yp++) + { + for (uint32_t xp = x; xp < x + w; xp++) + { + uint16_t color = *data++; + if(_iswapBytes) color = color<<8 | color>>8; + _img8[xp + yp * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + } + } + } +} + + +/*************************************************************************************** +** Function name: pushImage +** Description: push 565 colour FLASH (PROGMEM) image into a defined area +*************************************************************************************x*/ +void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uint16_t *data) +{ + if ((x > _iwidth) || (y > _iheight) || (w == 0) || (h == 0) || !_created) return; + + if (_bpp16) + { + for (uint32_t yp = y; yp < y + h; yp++) + { + for (uint32_t xp = x; xp < x + w; xp++) + { + uint16_t color = pgm_read_word(data++); + if(!_iswapBytes) color = color<<8 | color>>8; + _img[xp + yp * _iwidth] = color; + } + } + } + else + { + for (uint32_t yp = y; yp < y + h; yp++) + { + for (uint32_t xp = x; xp < x + w; xp++) + { + uint16_t color = pgm_read_word(data++); + if(_iswapBytes) color = color<<8 | color>>8; + _img8[xp + yp * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + } + } + } +} + + +/*************************************************************************************** +** Function name: setSwapBytes +** Description: Used by 16 bit pushImage() to swap byte order in colours +***************************************************************************************/ +void TFT_eSprite::setSwapBytes(bool swap) +{ + _iswapBytes = swap; +} + + +/*************************************************************************************** +** Function name: getSwapBytes +** Description: Return the swap byte order for colours +***************************************************************************************/ +bool TFT_eSprite::getSwapBytes(void) +{ + return _iswapBytes; +} + + +/*************************************************************************************** +** Function name: setWindow +** Description: Set the bounds of a window for pushColor and writeColor +*************************************************************************************x*/ +void TFT_eSprite::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) +{ + bool duff_coord = false; + + if (x0 > x1) swap_coord(x0, x1); + if (y0 > y1) swap_coord(y0, y1); + + if (x0 < 0) x0 = 0; + if (x0 >= _iwidth) duff_coord = true; + if (x1 < 0) x1 = 0; + if (x1 >= _iwidth) x1 = _iwidth - 1; + + if (y0 < 0) y0 = 0; + if (y0 >= _iheight) duff_coord = true; + if (y1 < 0) y1 = 0; + if (y1 >= _iheight) y1 = _iheight - 1; + + if (duff_coord) + { // Point to that extra "off screen" pixel + _xs = 0; + _ys = _iheight; + _xe = 0; + _ye = _iheight; + } + else + { + _xs = x0; + _ys = y0; + _xe = x1; + _ye = y1; + } + + _xptr = _xs; + _yptr = _ys; +} + + +/*************************************************************************************** +** Function name: pushColor +** Description: Send a new pixel to the set window +*************************************************************************************x*/ +void TFT_eSprite::pushColor(uint32_t color) +{ + if (!_created ) return; + + // Write the colour to RAM in set window + if (_bpp16) + _img [_xptr + _yptr * _iwidth] = (uint16_t) (color >> 8) | (color << 8); + + else + _img8[_xptr + _yptr * _iwidth] = (uint8_t )((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + + // Increment x + _xptr++; + + // Wrap on x and y to start, increment y if needed + if (_xptr > _xe) + { + _xptr = _xs; + _yptr++; + if (_yptr > _ye) _yptr = _ys; + } + +} + + +/*************************************************************************************** +** Function name: pushColor +** Description: Send a "len" new pixels to the set window +*************************************************************************************x*/ +void TFT_eSprite::pushColor(uint32_t color, uint16_t len) +{ + if (!_created ) return; + + uint16_t pixelColor; + if (_bpp16) + pixelColor = (uint16_t) (color >> 8) | (color << 8); + + else + pixelColor = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; + + while(len--) writeColor(pixelColor); +} + + +/*************************************************************************************** +** Function name: writeColor +** Description: Write a pixel with pre-formatted colour to the set window +*************************************************************************************x*/ +void TFT_eSprite::writeColor(uint16_t color) +{ + if (!_created ) return; + + // Write 16 bit RGB 565 encoded colour to RAM + if (_bpp16) _img [_xptr + _yptr * _iwidth] = color; + + // Write 8 bit RGB 332 encoded colour to RAM + else _img8[_xptr + _yptr * _iwidth] = (uint8_t) color; + + // Increment x + _xptr++; + + // Wrap on x and y to start, increment y if needed + if (_xptr > _xe) + { + _xptr = _xs; + _yptr++; + if (_yptr > _ye) _yptr = _ys; + } +} + + +/*************************************************************************************** +** Function name: setScrollRect +** Description: Set scroll area within the sprite and the gap fill colour +*************************************************************************************x*/ +void TFT_eSprite::setScrollRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t color) +{ + if ((x >= _iwidth) || (y >= _iheight) || !_created ) return; + + if (x < 0) x = 0; + if (y < 0) y = 0; + + if ((x + w) > _iwidth ) w = _iwidth - x; + if ((y + h) > _iheight) h = _iheight - y; + + if ( w < 1 || h < 1) return; + + _sx = x; + _sy = y; + _sw = w; + _sh = h; + + _scolor = color; +} + + +/*************************************************************************************** +** Function name: scroll +** Description: Scroll dx,dy pixels, positive right,down, negative left,up +*************************************************************************************x*/ +void TFT_eSprite::scroll(int16_t dx, int16_t dy) +{ + if (abs(dx) >= _sw || abs(dy) >= _sh) + { + fillRect (_sx, _sy, _sw, _sh, _scolor); + return; + } + + // Fetch the scroll area width and height set by setScrollRect() + uint32_t w = _sw - abs(dx); // line width to copy + uint32_t h = _sh - abs(dy); // lines to copy + int32_t iw = _iwidth; // width of sprite + + // Fetch the x,y origin set by setScrollRect() + uint32_t tx = _sx; // to x + uint32_t fx = _sx; // from x + uint32_t ty = _sy; // to y + uint32_t fy = _sy; // from y + + // Adjust for x delta + if (dx <= 0) fx -= dx; + else tx += dx; + + // Adjust for y delta + if (dy <= 0) fy -= dy; + else + { // Scrolling down so start copy from bottom + ty = ty + _sh - 1; // "To" pointer + iw = -iw; // Pointer moves backwards + fy = ty - dy; // "From" pointer + } + + // Calculate "from y" and "to y" pointers in RAM + uint32_t fyp = fx + fy * _iwidth; + uint32_t typ = tx + ty * _iwidth; + + // Now move the pixels in RAM + if (_bpp16) + { + while (h--) + { // move pixel lines (to, from, byte count) + memmove( _img + typ, _img + fyp, w<<1); + typ += iw; + fyp += iw; + } + } + else + { + while (h--) + { // move pixel lines (to, from, byte count) + memmove( _img8 + typ, _img8 + fyp, w); + typ += iw; + fyp += iw; + } + } + + // Fill the gap left by the scrolling + if (dx > 0) fillRect(_sx, _sy, dx, _sh, _scolor); + if (dx < 0) fillRect(_sx + _sw + dx, _sy, -dx, _sh, _scolor); + if (dy > 0) fillRect(_sx, _sy, _sw, dy, _scolor); + if (dy < 0) fillRect(_sx, _sy + _sh + dy, _sw, -dy, _scolor); +} + + +/*************************************************************************************** +** Function name: fillSprite +** Description: Fill the whole sprite with defined colour +*************************************************************************************x*/ +void TFT_eSprite::fillSprite(uint32_t color) +{ + if (!_created ) return; + + // Use memset if possible as it is super fast + if(( (uint8_t)color == (uint8_t)(color>>8) ) && _bpp16) + memset(_img, (uint8_t)color, _iwidth * _iheight * 2); + else if (!_bpp16) + { + color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; + memset(_img8, (uint8_t)color, _iwidth * _iheight); + } + + else fillRect(0, 0, _iwidth, _iheight, color); +} + + +/*************************************************************************************** +** 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 +** Description: Return the width of sprite +*************************************************************************************x*/ +// Return the size of the display +int16_t TFT_eSprite::width(void) +{ + if (!_created ) return 0; + return _iwidth; +} + + +/*************************************************************************************** +** Function name: height +** Description: Return the height of sprite +*************************************************************************************x*/ +int16_t TFT_eSprite::height(void) +{ + if (!_created ) return 0; + return _iheight; +} + + +/*************************************************************************************** +** 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) || !_created) return; + + if (_bpp16) + { + color = (color >> 8) | (color << 8); + _img[x+y*_iwidth] = (uint16_t) color; + } + else + { + _img8[x+y*_iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + } +} + + +/*************************************************************************************** +** 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) +{ + if (!_created ) return; + + 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) || !_created) return; + + if (y < 0) { h += y; y = 0; } + + if ((y + h) > _iheight) h = _iheight - y; + + if (h < 1) return; + + if (_bpp16) + { + color = (color >> 8) | (color << 8); + int32_t yp = x + _iwidth * y; + while (h--) {_img[yp] = (uint16_t) color; yp += _iwidth;} + } + else + { + color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; + while (h--) _img8[x + _iwidth * y++] = (uint8_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) || !_created) return; + + if (x < 0) { w += x; x = 0; } + + if ((x + w) > _iwidth) w = _iwidth - x; + + if (w < 1) return; + + if (_bpp16) + { + color = (color >> 8) | (color << 8); + while (w--) _img[_iwidth * y + x++] = (uint16_t) color; + } + else + { + color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; + memset(_img8+_iwidth * y + x, (uint8_t)color, w); + } +} + + +/*************************************************************************************** +** 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 (!_created ) return; + + 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; + + int32_t yp = _iwidth * y + x; + + if (_bpp16) + { + color = (color >> 8) | (color << 8); + uint32_t iw = w; + int32_t ys = yp; + if(h--) {while (iw--) _img[yp++] = (uint16_t) color;} + yp = ys; + while (h--) + { + yp += _iwidth; + memcpy( _img+yp, _img+ys, w<<1); + } + } + else + { + color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; + while (h--) + { + memset(_img8 + yp, (uint8_t)color, w); + yp += _iwidth; + } + } +} + + +/*************************************************************************************** +** Function name: write +** Description: draw characters piped through serial stream +*************************************************************************************x*/ +size_t TFT_eSprite::write(uint8_t utf8) +{ + if (utf8 == '\r') return 1; + +#ifdef SMOOTH_FONT + if(fontLoaded) + { + uint16_t unicode = decodeUTF8(utf8); + if (unicode < 32 && utf8 != '\n') return 0; + + fontFile = SPIFFS.open( _gFontFilename, "r" ); + + if(!fontFile) + { + fontLoaded = false; + return 0; + } + + drawGlyph(unicode); + fontFile.close(); + return 0; + } +#endif + + if (!_created ) return 0; + + + uint8_t uniCode = utf8; // Work with a copy + if (utf8 == '\n') uniCode+=22; // Make it a valid space character to stop errors + else if (utf8 < 32) return 0; + + 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) + { + if (utf8 > 127) return 0; + // 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)) + { + if (utf8 > 127) return 0; + // 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 (textwrapX && (_icursor_x + width * textsize > _iwidth)) + { + _icursor_y += height; + _icursor_x = 0; + } + if (textwrapY && (_icursor_y >= _iheight)) _icursor_y = 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 > (uint8_t)pgm_read_byte(&gfxFont->last )) return 0; + if (uniCode < (uint8_t)pgm_read_byte(&gfxFont->first)) return 0; + + 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(textwrapX && ((_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); + } + if (textwrapY && (_icursor_y >= _iheight)) _icursor_y = 0; + 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 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 (!_created ) return; + + if ((x >= _iwidth) || // Clip right + (y >= _iheight) || // Clip bottom + ((x + 6 * size - 1) < 0) || // Clip left + ((y + 8 * size - 1) < 0)) // Clip top + return; + +#ifdef LOAD_GLCD +//>>>>>>>>>>>>>>>>>> +#ifdef LOAD_GFXFF + if(!gfxFont) { // 'Classic' built-in font +#endif +//>>>>>>>>>>>>>>>>>> + + boolean fillbg = (bg != color); + + if ((size==1) && fillbg) + { + uint8_t column[6]; + uint8_t 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 + // Filter out bad characters not present in font + if ((c >= (uint8_t)pgm_read_byte(&gfxFont->first)) && (c <= (uint8_t)pgm_read_byte(&gfxFont->last ))) + { +//>>>>>>>>>>>>>>>>>>>>>>>>>>> + + 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 = 0, yo16 = 0; + + 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: 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 (!_created ) return 0; + + 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->first)) && (uniCode <= pgm_read_byte(&gfxFont->last) )) + { + 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 + } + + if ((font>1) && (font<9) && ((uniCode < 32) || (uniCode > 127))) return 0; + + 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; + uint8_t 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 >= _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); + int16_t color; + if (_bpp16) color = (textcolor >> 8) | (textcolor << 8); + else color = ((textcolor & 0xE000)>>8 | (textcolor & 0x0700)>>6 | (textcolor & 0x0018)>>3); + int px = 0, py = pY; // To hold character block start and end column and row values + int pc = 0; // Pixel count + uint8_t np = textsize * textsize; // Number of pixels in a drawn pixel + uint8_t tnp = 0; // Temporary copy of np for while loop + uint8_t 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--) writeColor(color); } + else writeColor(color); + + 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 + +} + +#ifdef SMOOTH_FONT +/*************************************************************************************** +** Function name: drawGlyph +** Description: Write a character to the sprite cursor position +*************************************************************************************x*/ +void TFT_eSprite::drawGlyph(uint16_t code) +{ + if (code < 0x21) + { + if (code == 0x20) { + if (_created) _icursor_x += _tft->gFont.spaceWidth; + else _tft->cursor_x += _tft->gFont.spaceWidth; + return; + } + + if (code == '\n') { + if (_created) + { + _icursor_x = 0; + _icursor_y += _tft->gFont.yAdvance; + if (_icursor_y >= _height) _icursor_y = 0; + return; + } + else + { + cursor_x = 0; + cursor_y += gFont.yAdvance; + if (cursor_y >= _height) cursor_y = 0; + return; + } + } + } + + uint16_t gNum = 0; + bool found = _tft->getUnicodeIndex(code, &gNum); + + uint16_t fg = _tft->textcolor; + uint16_t bg = _tft->textbgcolor; + + if (found) + { + + bool newSprite = !_created; + + if (newSprite) + { + createSprite(_tft->gWidth[gNum], _tft->gFont.yAdvance); + if(bg) fillSprite(bg); + _icursor_x = -_tft->gdX[gNum]; + _icursor_y = 0; + } + + fontFile.seek(_tft->gBitmap[gNum], fs::SeekSet); // This is slow for a significant position shift! + + uint8_t pbuffer[_tft->gWidth[gNum]]; + + uint16_t xs = 0; + uint16_t dl = 0; + + for (int y = 0; y < _tft->gHeight[gNum]; y++) + { + fontFile.read(pbuffer, _tft->gWidth[gNum]); + for (int x = 0; x < _tft->gWidth[gNum]; x++) + { + uint8_t pixel = pbuffer[x]; + if (pixel) + { + if (pixel != 0xFF) + { + if (dl) { drawFastHLine( xs, y + _icursor_y + _tft->gFont.maxAscent - _tft->gdY[gNum], dl, fg); dl = 0; } + drawPixel(x + _icursor_x + _tft->gdX[gNum], y + _icursor_y + _tft->gFont.maxAscent - _tft->gdY[gNum], alphaBlend(pixel, fg, bg)); + } + else + { + if (dl==0) xs = x + _icursor_x + _tft->gdX[gNum]; + dl++; + } + } + else + { + if (dl) { drawFastHLine( xs, y + _icursor_y + _tft->gFont.maxAscent - _tft->gdY[gNum], dl, fg); dl = 0; } + } + } + if (dl) { drawFastHLine( xs, y + _icursor_y + _tft->gFont.maxAscent - _tft->gdY[gNum], dl, fg); dl = 0; } + } + + if (newSprite) + { + pushSprite(_tft->cursor_x + _tft->gdX[gNum], _tft->cursor_y, bg); + deleteSprite(); + _tft->cursor_x += _tft->gxAdvance[gNum]; + } + else _icursor_x += _tft->gxAdvance[gNum]; + } + else + { + // Not a Unicode in font so draw a rectangle and move on cursor + drawRect(_icursor_x, _icursor_y + _tft->gFont.maxAscent - _tft->gFont.ascent, _tft->gFont.spaceWidth, _tft->gFont.ascent, fg); + _icursor_x += _tft->gFont.spaceWidth + 1; + } +} + + +/*************************************************************************************** +** Function name: printToSprite +** Description: Write a string to the sprite cursor position +*************************************************************************************x*/ +void TFT_eSprite::printToSprite(String string) +{ + if(!_tft->fontLoaded) return; + int16_t len = string.length(); + char cbuffer[len + 1]; // Add 1 for the null + string.toCharArray(cbuffer, len + 1); // Add 1 for the null, otherwise characters get dropped + printToSprite(cbuffer, len); +} + + +/*************************************************************************************** +** Function name: printToSprite +** Description: Write a string to the sprite cursor position +*************************************************************************************x*/ +void TFT_eSprite::printToSprite(char *cbuffer, int len) //String string) +{ + if(!_tft->fontLoaded) return; + + fontFile = SPIFFS.open( _tft->_gFontFilename, "r" ); + + if(!fontFile) + { + _tft->fontLoaded = false; + return; + } + + uint16_t n = 0; + bool newSprite = !_created; + + if (newSprite) + { + int16_t sWidth = 0; + uint16_t index = 0; + + while (n < len) + { + uint16_t unicode = decodeUTF8((uint8_t*)cbuffer, &n, len - n); + if (_tft->getUnicodeIndex(unicode, &index)) + { + if (n == 0) sWidth -= _tft->gdX[index]; + if (n == len-1) sWidth += ( _tft->gWidth[index] + _tft->gdX[index]); + else sWidth += _tft->gxAdvance[index]; + } + else sWidth += _tft->gFont.spaceWidth + 1; + } + + createSprite(sWidth, _tft->gFont.yAdvance); + uint16_t transparent = TFT_BLACK; + + if (_tft->textbgcolor != TFT_BLACK) fillSprite(_tft->textbgcolor); + } + + n = 0; + + while (n < len) + { + uint16_t unicode = decodeUTF8((uint8_t*)cbuffer, &n, len - n); + //Serial.print("Decoded Unicode = 0x");Serial.println(unicode,HEX); + //Serial.print("n = ");Serial.println(n); + drawGlyph(unicode); + } + + if (newSprite) + { + pushSprite(_tft->cursor_x, _tft->cursor_y); + deleteSprite(); + } + + fontFile.close(); +} + + +/*************************************************************************************** +** Function name: printToSprite +** Description: Print character in a Sprite, create sprite if needed +*************************************************************************************x*/ +int16_t TFT_eSprite::printToSprite(int16_t x, int16_t y, uint16_t index) +{ + bool newSprite = !_created; + int16_t sWidth = _tft->gWidth[index]; + + if (newSprite) + { + createSprite(sWidth, _tft->gFont.yAdvance); + uint16_t transparent = TFT_BLACK; + if (_tft->textbgcolor != TFT_BLACK) fillSprite(_tft->textbgcolor); + + drawGlyph(_tft->gUnicode[index]); + + pushSprite(x + _tft->gdX[index], y, _tft->textbgcolor); + deleteSprite(); + } + + else drawGlyph(_tft->gUnicode[index]); + + return _tft->gxAdvance[index]; +} +#endif diff --git a/Extensions/Sprite.h b/Extensions/Sprite.h new file mode 100644 index 0000000..85945ff --- /dev/null +++ b/Extensions/Sprite.h @@ -0,0 +1,111 @@ +/*************************************************************************************** +// 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); + + // Create a sprite of width x height pixels, return a pointer to the RAM area + // Sketch can cast returned value to (uint16_t*) for 16 bit depth if needed + // RAM required is 1 byte per pixel for 8 bit colour depth, 2 bytes for 16 bit + void* createSprite(int16_t width, int16_t height); + + // Delete the sprite to free up the RAM + void deleteSprite(void); + + // Set the colour depth to 8 or 16 bits. Can be used to change depth an existing + // sprite, but clears it to black, returns a new pointer if sprite is re-created. + void* setColorDepth(int8_t b); + + 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), + + // Define a window to push 16 bit colour pixels into is a raster order + // Colours are converted to 8 bit if depth is set to 8 + setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1), + pushColor(uint32_t color), + pushColor(uint32_t color, uint16_t len), + // Push a pixel preformatted as a 8 or 16 bit colour (avoids conversion overhead) + writeColor(uint16_t color), + + // Set the scroll zone, top left corner at x,y with defined width and height + // The colour (optional, black is default) is used to fill the gap after the scroll + setScrollRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t color = TFT_BLACK), + // Scroll the defined zone dx,dy pixels. Negative values left,up, positive right,down + // dy is optional (default is then no up/down scroll). + // The sprite coordinate frame does not move because pixels are moved + scroll(int16_t dx, int16_t dy = 0), + + 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), + + // Set the sprite text cursor position for print class (does not change the TFT screen cursor) + 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 an image (colour bitmap) to the sprite + void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint16_t *data); + void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, const uint16_t *data); + + // Swap the byte order for pushImage() - corrects different image endianness + void setSwapBytes(bool swap); + bool getSwapBytes(void); + + // Push the sprite to the TFT screen, this fn calls pushImage() in the TFT class. + // Optionally a "transparent" colour can be defined, pixels of that colour will not be rendered + void pushSprite(int32_t x, int32_t y); + void pushSprite(int32_t x, int32_t y, uint16_t transparent); + + int16_t drawChar(unsigned int uniCode, int x, int y, int font), + drawChar(unsigned int uniCode, int x, int y); + + // Return the width and height of the sprite + int16_t width(void), + height(void); + + // Used by print class to print text to cursor position + size_t write(uint8_t); + + // Functions associated with anti-aliased fonts + void drawGlyph(uint16_t code); + void printToSprite(String string); + void printToSprite(char *cbuffer, int len); + int16_t printToSprite(int16_t x, int16_t y, uint16_t index); + + private: + + TFT_eSPI *_tft; + + protected: + + uint16_t *_img; // pointer to 16 bit sprite + uint8_t *_img8; // pointer to 8 bit sprite + bool _created, _bpp16; // created and bits per pixel depth flags + + bool _gFont = false; + + int32_t _icursor_x, _icursor_y; + int32_t _xs, _ys, _xe, _ye, _xptr, _yptr; // for setWindow + int32_t _sx, _sy; // x,y for scroll zone + uint32_t _sw, _sh; // w,h for scroll zone + uint32_t _scolor; // gap fill colour for scroll zone + + boolean _iswapBytes; // Swap the byte order for Sprite pushImage() + + int32_t _iwidth, _iheight; // Sprite image width and height + +}; diff --git a/Extensions/Touch.cpp b/Extensions/Touch.cpp new file mode 100644 index 0000000..d077e13 --- /dev/null +++ b/Extensions/Touch.cpp @@ -0,0 +1,319 @@ +// The following touch screen support code by maxpautsch was merged 1/10/17 +// https://github.com/maxpautsch +// Define TOUCH_CS is the user setup file to enable this code +// A demo is provided in examples Generic folder +// Additions by Bodmer to double sample and use Z value to improve detection reliability +// See license in root directory. + +#ifdef TOUCH_CS // If a pin has been allocated to the Touch screen load functions +/*************************************************************************************** +** Function name: getTouchRaw +** Description: read raw touch position. Return false if not pressed. +***************************************************************************************/ +uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ + uint16_t tmp; + CS_H; + + spi_begin_touch(); + + T_CS_L; + + // Start bit + YP sample request for x position + tmp = SPI.transfer(0xd0); + tmp = SPI.transfer(0); + tmp = tmp <<5; + tmp |= 0x1f & (SPI.transfer(0)>>3); + + *x = tmp; + + // Start bit + XP sample request for y position + SPI.transfer(0x90); + tmp = SPI.transfer(0); + tmp = tmp <<5; + tmp |= 0x1f & (SPI.transfer(0)>>3); + + *y = tmp; + + T_CS_H; + + spi_end_touch(); + + return true; +} + +/*************************************************************************************** +** Function name: getTouchRawZ +** Description: read raw pressure on touchpad and return Z value. +***************************************************************************************/ +uint16_t TFT_eSPI::getTouchRawZ(void){ + CS_H; + + spi_begin_touch(); + + T_CS_L; + + // Z sample request + uint16_t tz = 0xFFF; + SPI.transfer(0xb1); + tz += SPI.transfer16(0xc1) >> 3; + tz -= SPI.transfer16(0x91) >> 3; + + T_CS_H; + + spi_end_touch(); + + return tz; +} + +/*************************************************************************************** +** Function name: validTouch +** Description: read validated position. Return false if not pressed. +***************************************************************************************/ +#define _RAWERR 10 // Deadband in position samples +uint8_t TFT_eSPI::validTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ + uint16_t x_tmp, y_tmp, x_tmp2, y_tmp2; + + // Wait until pressure stops increasing + uint16_t z1 = 1; + uint16_t z2 = 0; + while (z1 > z2) + { + z2 = z1; + z1 = getTouchRawZ(); + delay(1); + } + + // Serial.print("Z = ");Serial.println(z1); + + if (z1 <= threshold) return false; + + getTouchRaw(&x_tmp,&y_tmp); + + // Serial.print("Sample 1 x,y = "); Serial.print(x_tmp);Serial.print(",");Serial.print(y_tmp); + // Serial.print(", Z = ");Serial.println(z1); + + delay(1); // Small delay to the next sample + if (getTouchRawZ() <= threshold) return false; + + delay(2); // Small delay to the next sample + getTouchRaw(&x_tmp2,&y_tmp2); + + // Serial.print("Sample 2 x,y = "); Serial.print(x_tmp2);Serial.print(",");Serial.println(y_tmp2); + // Serial.print("Sample difference = ");Serial.print(abs(x_tmp - x_tmp2));Serial.print(",");Serial.println(abs(y_tmp - y_tmp2)); + + if (abs(x_tmp - x_tmp2) > _RAWERR) return false; + if (abs(y_tmp - y_tmp2) > _RAWERR) return false; + + *x = x_tmp; + *y = y_tmp; + + return true; +} + +/*************************************************************************************** +** Function name: getTouch +** Description: read callibrated position. Return false if not pressed. +***************************************************************************************/ +#define Z_THRESHOLD 350 // Touch pressure threshold for validating touches +uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ + uint16_t x_tmp, y_tmp, xx, yy; + + if (threshold<20) threshold = 20; + if (_pressTime > millis()) threshold=20; + + uint8_t n = 5; + uint8_t valid = 0; + while (n--) + { + if (validTouch(&x_tmp, &y_tmp, threshold)) valid++;; + } + + if (valid<1) { _pressTime = 0; return false; } + + _pressTime = millis() + 50; + + if(!touchCalibration_rotate){ + xx=(x_tmp-touchCalibration_x0)*_width/touchCalibration_x1; + yy=(y_tmp-touchCalibration_y0)*_height/touchCalibration_y1; + if(touchCalibration_invert_x) + xx = _width - xx; + if(touchCalibration_invert_y) + yy = _height - yy; + } else { + yy=(x_tmp-touchCalibration_x0)*_height/touchCalibration_x1; + xx=(y_tmp-touchCalibration_y0)*_width/touchCalibration_y1; + if(touchCalibration_invert_x) + xx = _width - xx; + if(touchCalibration_invert_y) + yy = _height - yy; + } + + if (xx >= _width || yy >= _height) return valid; + + _pressX = xx; + _pressY = yy; + *x = _pressX; + *y = _pressY; + return valid; +} + +/*************************************************************************************** +** Function name: calibrateTouch +** Description: generates calibration parameters for touchscreen. +***************************************************************************************/ +void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_fg, uint32_t color_bg, uint8_t size){ + int16_t values[] = {0,0,0,0,0,0,0,0}; + uint16_t x_tmp, y_tmp; + + + + for(uint8_t i = 0; i<4; i++){ + fillRect(0, 0, size+1, size+1, color_bg); + fillRect(0, _height-size-1, size+1, size+1, color_bg); + fillRect(_width-size-1, 0, size+1, size+1, color_bg); + fillRect(_width-size-1, _height-size-1, size+1, size+1, color_bg); + + if (i == 5) break; // used to clear the arrows + + switch (i) { + case 0: // up left + drawLine(0, 0, 0, size, color_fg); + drawLine(0, 0, size, 0, color_fg); + drawLine(0, 0, size , size, color_fg); + break; + case 1: // bot left + drawLine(0, _height-size-1, 0, _height-1, color_fg); + drawLine(0, _height-1, size, _height-1, color_fg); + drawLine(size, _height-size-1, 0, _height-1 , color_fg); + break; + case 2: // up right + drawLine(_width-size-1, 0, _width-1, 0, color_fg); + drawLine(_width-size-1, size, _width-1, 0, color_fg); + drawLine(_width-1, size, _width-1, 0, color_fg); + break; + case 3: // bot right + drawLine(_width-size-1, _height-size-1, _width-1, _height-1, color_fg); + drawLine(_width-1, _height-1-size, _width-1, _height-1, color_fg); + drawLine(_width-1-size, _height-1, _width-1, _height-1, color_fg); + break; + } + + // user has to get the chance to release + if(i>0) delay(1000); + + for(uint8_t j= 0; j<8; j++){ + // Use a lower detect threshold as corners tend to be less sensitive + while(!validTouch(&x_tmp, &y_tmp, Z_THRESHOLD/4)); + values[i*2 ] += x_tmp; + values[i*2+1] += y_tmp; + } + values[i*2 ] /= 8; + values[i*2+1] /= 8; + } + + + + // check orientation + // from case 0 to case 1, the y value changed. + // If the measured delta of the touch x axis is bigger than the delta of the y axis, the touch and TFT axes are switched. + touchCalibration_rotate = false; + if(abs(values[0]-values[2]) > abs(values[1]-values[3])){ + touchCalibration_rotate = true; + touchCalibration_x0 = (values[0] + values[4])/2; // calc min x + touchCalibration_x1 = (values[2] + values[6])/2; // calc max x + touchCalibration_y0 = (values[1] + values[3])/2; // calc min y + touchCalibration_y1 = (values[5] + values[7])/2; // calc max y + } else { + touchCalibration_x0 = (values[0] + values[2])/2; // calc min x + touchCalibration_x1 = (values[4] + values[6])/2; // calc max x + touchCalibration_y0 = (values[1] + values[5])/2; // calc min y + touchCalibration_y1 = (values[3] + values[7])/2; // calc max y + } + + // in addition, the touch screen axis could be in the opposit direction of the TFT axis + touchCalibration_invert_x = false; + if(touchCalibration_x0 > touchCalibration_x1){ + values[0]=touchCalibration_x0; + touchCalibration_x0 = touchCalibration_x1; + touchCalibration_x1 = values[0]; + touchCalibration_invert_x = true; + } + touchCalibration_invert_y = false; + if(touchCalibration_y0 > touchCalibration_y1){ + values[0]=touchCalibration_y0; + touchCalibration_y0 = touchCalibration_y1; + touchCalibration_y1 = values[0]; + touchCalibration_invert_y = true; + } + + // pre calculate + touchCalibration_x1 -= touchCalibration_x0; + touchCalibration_y1 -= touchCalibration_y0; + + if(touchCalibration_x0 == 0) touchCalibration_x0 = 1; + if(touchCalibration_x1 == 0) touchCalibration_x1 = 1; + if(touchCalibration_y0 == 0) touchCalibration_y0 = 1; + if(touchCalibration_y1 == 0) touchCalibration_y1 = 1; + + // export parameters, if pointer valid + if(parameters != NULL){ + parameters[0] = touchCalibration_x0; + parameters[1] = touchCalibration_x1; + parameters[2] = touchCalibration_y0; + parameters[3] = touchCalibration_y1; + parameters[4] = touchCalibration_rotate | (touchCalibration_invert_x <<1) | (touchCalibration_invert_y <<2); + } +} + + +/*************************************************************************************** +** Function name: setTouch +** Description: imports calibration parameters for touchscreen. +***************************************************************************************/ +void TFT_eSPI::setTouch(uint16_t *parameters){ + touchCalibration_x0 = parameters[0]; + touchCalibration_x1 = parameters[1]; + touchCalibration_y0 = parameters[2]; + touchCalibration_y1 = parameters[3]; + + if(touchCalibration_x0 == 0) touchCalibration_x0 = 1; + if(touchCalibration_x1 == 0) touchCalibration_x1 = 1; + if(touchCalibration_y0 == 0) touchCalibration_y0 = 1; + if(touchCalibration_y1 == 0) touchCalibration_y1 = 1; + + touchCalibration_rotate = parameters[4] & 0x01; + touchCalibration_invert_x = parameters[4] & 0x02; + touchCalibration_invert_y = parameters[4] & 0x04; +} + + +#else // TOUCH CS is not defined so generate dummy functions that do nothing + +/*************************************************************************************** +** Function name: Dummy functions for case where chip select pin is undefined +** Description: +***************************************************************************************/ + +uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ + return true; +} + +uint16_t TFT_eSPI::getTouchRawZ(void){ + return true; +} + +uint8_t TFT_eSPI::validTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ + return true; +} + +uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ + return true; +} + +void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_bg, uint32_t color_fg, uint8_t size){ +} + +void TFT_eSPI::setTouch(uint16_t *parameters){ +} + +#endif // TOUCH_CS diff --git a/Extensions/Touch.h b/Extensions/Touch.h new file mode 100644 index 0000000..7f3b22a --- /dev/null +++ b/Extensions/Touch.h @@ -0,0 +1,24 @@ + // Coded by Bodmer 10/2/18, see license in root directory. + // This is part of the TFT_eSPI class and is associated with the Touch Screen handlers + + public: + + uint8_t getTouchRaw(uint16_t *x, uint16_t *y); + uint16_t getTouchRawZ(void); + uint8_t getTouch(uint16_t *x, uint16_t *y, uint16_t threshold = 600); + + void calibrateTouch(uint16_t *data, uint32_t color_fg, uint32_t color_bg, uint8_t size); + void setTouch(uint16_t *data); + + private: + + inline void spi_begin_touch() __attribute__((always_inline)); + inline void spi_end_touch() __attribute__((always_inline)); + + // These are associated with the Touch Screen handlers + uint8_t validTouch(uint16_t *x, uint16_t *y, uint16_t threshold = 600); + // Initialise with example calibration values so processor does not crash if setTouch() not called in setup() + uint16_t touchCalibration_x0 = 300, touchCalibration_x1 = 3600, touchCalibration_y0 = 300, touchCalibration_y1 = 3600; + uint8_t touchCalibration_rotate = 1, touchCalibration_invert_x = 2, touchCalibration_invert_y = 0; + uint32_t _pressTime; + uint16_t _pressX, _pressY; diff --git a/Fonts/Font7srle.c b/Fonts/Font7srle.c index 6235093..bb292d7 100644 --- a/Fonts/Font7srle.c +++ b/Fonts/Font7srle.c @@ -12,7 +12,7 @@ PROGMEM const unsigned char widtbl_f7s[96] = // character width table { 12, 12, 12, 12, 12, 12, 12, 12, // char 32 - 39 - 12, 12, 12, 12, 12, 17, 12, 12, // char 40 - 47 + 12, 12, 12, 12, 12, 32, 12, 12, // char 40 - 47 32, 32, 32, 32, 32, 32, 32, 32, // char 48 - 55 32, 32, 12, 12, 12, 12, 12, 12, // char 56 - 63 12, 12, 12, 12, 12, 12, 12, 12, // char 64 - 71 @@ -32,10 +32,12 @@ PROGMEM const unsigned char chr_f7s_20[] = 0x7F, 0x7F, 0x7F, 0x7F, 0x3F }; +// Make - sign look like a segment PROGMEM const unsigned char chr_f7s_2D[] = { -0x7F, 0x7F, 0x45, 0x8A, 0x05, 0x8A, 0x05, 0x8A, -0x05, 0x8A, 0x7F, 0x7F, 0x7F, 0x2B +0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x27, 0x8E, 0x0E, +0x92, 0x0A, 0x96, 0x09, 0x94, 0x0C, 0x90, 0x7F, +0x7F, 0x7F, 0x7F, 0x7F, 0x47 }; PROGMEM const unsigned char chr_f7s_2E[] = diff --git a/README.md b/README.md index 4647721..79bf602 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # TFT_eSPI +>>> This branch includes new antialiased font capability, this is a work-in-progress <<< + An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with a driver for ILI9341, ILI9163, ST7735 and S6D02A1 based TFT displays that support SPI. The library also supports TFT displays designed for the Raspberry Pi that are based on a ILI9486 driver chip with a 480 x 320 pixel screen. This display must be of the Waveshare design and use a 16 bit serial interface based on the 74HC04, 74HC4040 and 2 x 74HC4094 logic chips. A modification to these displays is possible (see mod image in Tools folder) to make many graphics functions much faster (e.g. 23ms to clear the screen, 1.2ms to draw a 72 pixel high numeral). @@ -32,7 +34,7 @@ Configuration of the library font selections, pins used to interface with the TF I have made some changes that will be uploaded soon that improves sprite and image rendering performance by up to 3x faster on the ESP8266. These updates are currently being tested/debugged. -**2. Anti-aliased fonts - see Smooth_font branch for beta version** +**2. Anti-aliased fonts** I have been experimenting with anti-aliased font files in "vlw" format generated by the free [Processing IDE](https://processing.org/). This IDE can be used to generate font files from your computer's font set and include **any** Unicode characters. This means Greek, Japanese and any other UTF-16 glyphs can be used. diff --git a/TFT_Drivers/ILI9163_Rotation.h b/TFT_Drivers/ILI9163_Rotation.h index 7232430..3323169 100644 --- a/TFT_Drivers/ILI9163_Rotation.h +++ b/TFT_Drivers/ILI9163_Rotation.h @@ -7,8 +7,8 @@ switch (rotation) { case 0: writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; #ifdef CGRAM_OFFSET colstart = 0; rowstart = 0; @@ -16,8 +16,8 @@ break; case 1: writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_BGR); - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; #ifdef CGRAM_OFFSET colstart = 0; rowstart = 0; @@ -25,8 +25,8 @@ break; case 2: writedata(TFT_MAD_BGR); - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; #ifdef CGRAM_OFFSET colstart = 0; rowstart = 32; @@ -34,8 +34,8 @@ break; case 3: writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_BGR); - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; #ifdef CGRAM_OFFSET colstart = 32; rowstart = 0; diff --git a/TFT_Drivers/ILI9341_Rotation.h b/TFT_Drivers/ILI9341_Rotation.h index 6966f37..f5e9b38 100644 --- a/TFT_Drivers/ILI9341_Rotation.h +++ b/TFT_Drivers/ILI9341_Rotation.h @@ -11,8 +11,8 @@ #else writedata(TFT_MAD_MX | TFT_MAD_BGR); #endif - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 1: #ifdef M5STACK @@ -20,8 +20,8 @@ #else writedata(TFT_MAD_MV | TFT_MAD_BGR); #endif - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; case 2: #ifdef M5STACK @@ -29,8 +29,8 @@ #else writedata(TFT_MAD_MY | TFT_MAD_BGR); #endif - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 3: #ifdef M5STACK @@ -38,8 +38,8 @@ #else writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); #endif - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; // These next rotations are for bottom up BMP drawing case 4: @@ -48,8 +48,8 @@ #else writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); #endif - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 5: #ifdef M5STACK @@ -57,8 +57,8 @@ #else writedata(TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_BGR); #endif - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; case 6: #ifdef M5STACK @@ -66,8 +66,8 @@ #else writedata(TFT_MAD_BGR); #endif - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 7: #ifdef M5STACK @@ -75,8 +75,8 @@ #else writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); #endif - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; } diff --git a/TFT_Drivers/RPI_ILI9486_Rotation.h b/TFT_Drivers/RPI_ILI9486_Rotation.h index 53afc2a..495d675 100644 --- a/TFT_Drivers/RPI_ILI9486_Rotation.h +++ b/TFT_Drivers/RPI_ILI9486_Rotation.h @@ -5,43 +5,43 @@ switch (rotation) { case 0: // Portrait writedata(TFT_MAD_BGR | TFT_MAD_MX); - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 1: // Landscape (Portrait + 90) writedata(TFT_MAD_BGR | TFT_MAD_MV); - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; case 2: // Inverter portrait writedata( TFT_MAD_BGR | TFT_MAD_MY); - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 3: // Inverted landscape writedata(TFT_MAD_BGR | TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_MY); - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; case 4: // Portrait writedata(TFT_MAD_BGR | TFT_MAD_MX | TFT_MAD_MY); - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 5: // Landscape (Portrait + 90) writedata(TFT_MAD_BGR | TFT_MAD_MV | TFT_MAD_MX); - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; case 6: // Inverter portrait writedata( TFT_MAD_BGR); - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 7: // Inverted landscape writedata(TFT_MAD_BGR | TFT_MAD_MV | TFT_MAD_MY); - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; } diff --git a/TFT_Drivers/S6D02A1_Rotation.h b/TFT_Drivers/S6D02A1_Rotation.h index dfa6cdb..7fa6eec 100644 --- a/TFT_Drivers/S6D02A1_Rotation.h +++ b/TFT_Drivers/S6D02A1_Rotation.h @@ -7,22 +7,22 @@ switch (rotation) { case 0: writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 1: writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_BGR); - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; case 2: writedata(TFT_MAD_BGR); - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 3: writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_BGR); - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; } diff --git a/TFT_Drivers/ST7735_Rotation.h b/TFT_Drivers/ST7735_Rotation.h index 6113886..4a8bfdc 100644 --- a/TFT_Drivers/ST7735_Rotation.h +++ b/TFT_Drivers/ST7735_Rotation.h @@ -23,8 +23,8 @@ } else { writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); } - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 1: if (tabcolor == INITR_BLACKTAB) { @@ -44,8 +44,8 @@ } else { writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); } - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; case 2: if (tabcolor == INITR_BLACKTAB) { @@ -65,8 +65,8 @@ } else { writedata(TFT_MAD_BGR); } - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 3: if (tabcolor == INITR_BLACKTAB) { @@ -86,30 +86,30 @@ } else { writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_BGR); } - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; // These next rotations are for bottum up BMP drawing /* case 4: writedata(ST7735_TFT_MAD_MX | ST7735_TFT_MAD_MY | ST7735_TFT_MAD_BGR); - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 5: writedata(ST7735_TFT_MAD_MV | ST7735_TFT_MAD_MX | ST7735_TFT_MAD_BGR); - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; case 6: writedata(ST7735_TFT_MAD_BGR); - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 7: writedata(ST7735_TFT_MAD_MY | ST7735_TFT_MAD_MV | ST7735_TFT_MAD_BGR); - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; */ } diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index a7c535c..901f490 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -51,21 +51,25 @@ inline void TFT_eSPI::spi_end(void){ #endif } -inline void TFT_eSPI::spi_begin_touch(void){ -#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 -} +#if defined (TOUCH_CS) && defined (SPI_TOUCH_FREQUENCY) + + inline void TFT_eSPI::spi_begin_touch(void){ + #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 + } + + inline void TFT_eSPI::spi_end_touch(void){ + #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) + if(!inTransaction) {if (!locked) {locked = true; SPI.endTransaction();}} + #else + SPI.setFrequency(SPI_FREQUENCY); + #endif + } -inline void TFT_eSPI::spi_end_touch(void){ -#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) - if(!inTransaction) {if (!locked) {locked = true; SPI.endTransaction();}} -#else - SPI.setFrequency(SPI_FREQUENCY); #endif -} /*************************************************************************************** ** Function name: TFT_eSPI @@ -105,8 +109,8 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) } #endif - _width_orig = _width = w; // Set by specific xxxxx_Defines.h file or by users sketch - _height_orig = _height = h; // Set by specific xxxxx_Defines.h file or by users sketch + _init_width = _width = w; // Set by specific xxxxx_Defines.h file or by users sketch + _init_height = _height = h; // Set by specific xxxxx_Defines.h file or by users sketch rotation = 0; cursor_y = cursor_x = 0; textfont = 1; @@ -114,7 +118,8 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) textcolor = 0xFFFF; // White textbgcolor = 0x0000; // Black padX = 0; // No padding - textwrap = true; // Wrap text when using print stream + textwrapX = true; // Wrap text at end of line when using print stream + textwrapY = false; // Wrap text at bottom of screen when using print stream textdatum = TL_DATUM; // Top Left text alignment is default fontsloaded = 0; @@ -123,8 +128,6 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) locked = true; // ESP32 transaction mutex lock flags inTransaction = false; - _booted = true; - addr_row = 0xFFFF; addr_col = 0xFFFF; @@ -171,8 +174,6 @@ void TFT_eSPI::begin(void) ***************************************************************************************/ void TFT_eSPI::init(void) { - if (_booted) - { #if !defined (ESP32) #ifdef TFT_CS cspinmask = (uint32_t) digitalPinToBitMask(TFT_CS); @@ -193,7 +194,7 @@ void TFT_eSPI::init(void) SPI.pins(6, 7, 8, 0); #endif - SPI.begin(); // This will set HMISO to input + SPI.begin(); // This will set HMISO to input #else #if defined (TFT_MOSI) && !defined (TFT_SPI_OVERLAP) SPI.begin(TFT_SCLK, TFT_MISO, TFT_MOSI, -1); @@ -203,35 +204,31 @@ void TFT_eSPI::init(void) #endif - inTransaction = false; - locked = true; + inTransaction = false; + locked = true; - // SUPPORT_TRANSACTIONS is mandatory for ESP32 so the hal mutex is toggled + // SUPPORT_TRANSACTIONS is manadatory for ESP32 so the hal mutex is toggled // so the code here is for ESP8266 only #if !defined (SUPPORT_TRANSACTIONS) && defined (ESP8266) - SPI.setBitOrder(MSBFIRST); - SPI.setDataMode(SPI_MODE0); - SPI.setFrequency(SPI_FREQUENCY); + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(SPI_MODE0); + SPI.setFrequency(SPI_FREQUENCY); #endif // Set to output once again in case D6 (MISO) is used for CS #ifdef TFT_CS - digitalWrite(TFT_CS, HIGH); // Chip select high (inactive) - pinMode(TFT_CS, OUTPUT); + digitalWrite(TFT_CS, HIGH); // Chip select high (inactive) + pinMode(TFT_CS, OUTPUT); #else - SPI.setHwCs(1); // Use hardware SS toggling + SPI.setHwCs(1); // Use hardware SS toggling #endif // Set to output once again in case D6 (MISO) is used for DC #ifdef TFT_DC - digitalWrite(TFT_DC, HIGH); // Data/Command high = data mode - pinMode(TFT_DC, OUTPUT); + digitalWrite(TFT_DC, HIGH); // Data/Command high = data mode + pinMode(TFT_DC, OUTPUT); #endif - _booted = false; - } // end of: if just _booted - - // Toggle RST low to reset #ifdef TFT_RST if (TFT_RST >= 0) { @@ -242,13 +239,13 @@ void TFT_eSPI::init(void) digitalWrite(TFT_RST, HIGH); delay(150); } -#else - // Or use the software reset +#endif + spi_begin(); writecommand(TFT_SWRST); // Software reset spi_end(); - delay(120); // Wait for software reset to complete -#endif + + delay(5); // Wait for software reset to complete spi_begin(); @@ -272,7 +269,6 @@ void TFT_eSPI::init(void) spi_end(); - setRotation(rotation); } @@ -1591,9 +1587,10 @@ void TFT_eSPI::setTextColor(uint16_t c, uint16_t b) ** Function name: setTextWrap ** Description: Define if text should wrap at end of line ***************************************************************************************/ -void TFT_eSPI::setTextWrap(boolean w) +void TFT_eSPI::setTextWrap(boolean wrapX, boolean wrapY) { - textwrap = w; + textwrapX = wrapX; + textwrapY = wrapY; } @@ -1684,7 +1681,35 @@ int16_t TFT_eSPI::textWidth(const char *string) int16_t TFT_eSPI::textWidth(const char *string, int font) { - unsigned int str_width = 0; + int str_width = 0; + +#ifdef SMOOTH_FONT + if(fontLoaded) + { + while (*string) + { + uint16_t unicode = decodeUTF8(*string++); + if (unicode) + { + if (unicode == 0x20) str_width += gFont.spaceWidth; + else + { + uint16_t gNum = 0; + bool found = getUnicodeIndex(unicode, &gNum); + if (found) + { + if(str_width == 0 && gdX[gNum] < 0) str_width -= gdX[gNum]; + if (*string) str_width += gxAdvance[gNum]; + else str_width += (gdX[gNum] + gWidth[gNum]); + } + else str_width += gFont.spaceWidth + 1; + } + } + } + return str_width; + } +#endif + unsigned char uniCode; char *widthtable; @@ -1750,6 +1775,10 @@ uint16_t TFT_eSPI::fontsLoaded(void) ***************************************************************************************/ int16_t TFT_eSPI::fontHeight(int16_t font) { +#ifdef SMOOTH_FONT + if(fontLoaded) return gFont.yAdvance; +#endif + #ifdef LOAD_GFXFF if (font==1) { @@ -2744,10 +2773,9 @@ void TFT_eSPI::pushColor(uint16_t color, uint16_t len) spi_end(); } - /*************************************************************************************** ** Function name: pushColors -** Description: push an aray of pixels for 16 bit raw image drawing +** Description: push an array of pixels for 16 bit raw image drawing ***************************************************************************************/ // Assumed that setWindow() has previously been called @@ -2861,6 +2889,41 @@ else SPI.writeBytes((uint8_t*)data,len<<1); SPI1CMD |= SPIBUSY; } +/* // Smaller version but slower + uint32_t count = 0; + while(len) + { + if(len>15) {count = 16; len -= 16;} + else {count = len; len = 0;} + uint32_t bits = (count*16-1); // bits left to shift - 1 + if (swap) + { + uint16_t* ptr = (uint16_t*)color; + while(count--) + { + *ptr++ = (*(data) >> 8) | (uint16_t)(*(data) << 8); + data++; + } + } + else + { + memcpy(color,data,count<<1); + data += 16; + } + while(SPI1CMD & SPIBUSY) {} + SPI1U1 = (SPI1U1 & mask) | (bits << SPILMOSI) | (bits << SPILMISO); + SPI1W0 = color[0]; + SPI1W1 = color[1]; + SPI1W2 = color[2]; + SPI1W3 = color[3]; + SPI1W4 = color[4]; + SPI1W5 = color[5]; + SPI1W6 = color[6]; + SPI1W7 = color[7]; + SPI1CMD |= SPIBUSY; + } +*/ + while(SPI1CMD & SPIBUSY) {} #endif @@ -3215,15 +3278,33 @@ uint16_t TFT_eSPI::color565(uint8_t r, uint8_t g, uint8_t b) /*************************************************************************************** -** Function name: color332 +** Function name: color16to8 ** Description: convert 16 bit colour to an 8 bit 332 RGB colour value ***************************************************************************************/ -uint8_t TFT_eSPI::color332(uint16_t c) +uint8_t TFT_eSPI::color16to8(uint16_t c) { return ((c & 0xE000)>>8) | ((c & 0x0700)>>6) | ((c & 0x0018)>>3); } +/*************************************************************************************** +** Function name: color8to16 +** Description: convert 8 bit colour to a 16 bit 565 colour value +***************************************************************************************/ +uint16_t TFT_eSPI::color8to16(uint8_t color) +{ + uint8_t blue[] = {0, 11, 21, 31}; // blue 2 to 5 bit colour lookup table + uint16_t color16 = 0; + + // =====Green===== ===============Red============== + color16 = (color & 0x1C)<<6 | (color & 0xC0)<<5 | (color & 0xE0)<<8; + // =====Green===== =======Blue====== + color16 |= (color & 0x1C)<<3 | blue[color & 0x03]; + + return color16; +} + + /*************************************************************************************** ** Function name: invertDisplay ** Description: invert the display colours i = 1 invert, i = 0 normal @@ -3246,6 +3327,27 @@ size_t TFT_eSPI::write(uint8_t utf8) { if (utf8 == '\r') return 1; +#ifdef SMOOTH_FONT + if(fontLoaded) + { + uint16_t unicode = decodeUTF8(utf8); + if (!unicode) return 0; + + //fontFile = SPIFFS.open( _gFontFilename, "r" ); + + if(!fontFile) + { + fontLoaded = false; + return 0; + } + + drawGlyph(unicode); + + //fontFile.close(); + return 1; + } +#endif + uint8_t uniCode = utf8; // Work with a copy if (utf8 == '\n') uniCode+=22; // Make it a valid space character to stop errors else if (utf8 < 32) return 0; @@ -3274,7 +3376,7 @@ size_t TFT_eSPI::write(uint8_t utf8) 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 + width = width * 8; // Width converted back to pixels } #ifdef LOAD_RLE else @@ -3312,12 +3414,12 @@ size_t TFT_eSPI::write(uint8_t utf8) } else { - if (textwrap && (cursor_x + width * textsize > _width)) + if (textwrapX && (cursor_x + width * textsize > _width)) { cursor_y += height; cursor_x = 0; } - //if (cursor_y >= _height) cursor_y = 0; + if (textwrapY && (cursor_y >= _height)) cursor_y = 0; cursor_x += drawChar(uniCode, cursor_x, cursor_y, textfont); } @@ -3341,13 +3443,13 @@ size_t TFT_eSPI::write(uint8_t utf8) 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 && ((cursor_x + textsize * (xo + w)) > _width)) { + if(textwrapX && ((cursor_x + textsize * (xo + w)) > _width)) { // Drawing character would go off right edge; wrap to new line cursor_x = 0; cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); } - //if (cursor_y >= _height) cursor_y = 0; + if (textwrapY && (cursor_y >= _height)) cursor_y = 0; drawChar(cursor_x, cursor_y, uniCode, textcolor, textbgcolor, textsize); } cursor_x += pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize; @@ -3362,7 +3464,7 @@ size_t TFT_eSPI::write(uint8_t utf8) /*************************************************************************************** ** Function name: drawChar -** Description: draw a unicode onto the screen +** Description: draw a Unicode onto the screen ***************************************************************************************/ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y) { @@ -3551,7 +3653,7 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) while (pc < w) { line = pgm_read_byte((uint8_t *)flash_address); - flash_address++; // 20 bytes smaller by incrementing here + flash_address++; if (line & 0x80) { line &= 0x7F; line++; @@ -3679,7 +3781,7 @@ int16_t TFT_eSPI::drawString(const char *string, int poX, int poY, int font) baseline = cheight; padding =101; // Different padding method used for Free Fonts - // We need to make an adjustment for the botom of the string (eg 'y' character) + // We need to make an adjustment for the bottom of the string (eg 'y' character) if ((textdatum == BL_DATUM) || (textdatum == BC_DATUM) || (textdatum == BR_DATUM)) { cheight += glyph_bb * textsize; } @@ -3690,7 +3792,15 @@ int16_t TFT_eSPI::drawString(const char *string, int poX, int poY, int font) if (textdatum || padX) { - // If it is not font 1 (GLCD or free font) get the basline and pixel height of the font + // If it is not font 1 (GLCD or free font) get the baseline and pixel height of the font +#ifdef SMOOTH_FONT + if(fontLoaded) { + baseline = gFont.maxAscent; + cheight = fontHeight(0); + } + + else +#endif if (font!=1) { baseline = pgm_read_byte( &fontdata[font].baseline ) * textsize; cheight = fontHeight(font); @@ -3780,7 +3890,28 @@ int16_t TFT_eSPI::drawString(const char *string, int poX, int poY, int font) } #endif - while (*string) sumX += drawChar(*(string++), poX+sumX, poY, font); +#ifdef SMOOTH_FONT + if(fontLoaded) + { + if (textcolor!=textbgcolor) fillRect(poX, poY, cwidth, cheight, textbgcolor); + //drawLine(poX - 5, poY, poX + 5, poY, TFT_GREEN); + //drawLine(poX, poY - 5, poX, poY + 5, TFT_GREEN); + //fontFile = SPIFFS.open( _gFontFilename, "r"); + if(!fontFile) return 0; + uint16_t len = strlen(string); + uint16_t n = 0; + setCursor(poX, poY); + while (n < len) + { + uint16_t unicode = decodeUTF8((uint8_t*)string, &n, len - n); + drawGlyph(unicode); + } + sumX += cwidth; + //fontFile.close(); + } + else +#endif + while (*string) sumX += drawChar(*(string++), poX+sumX, poY, font); //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv DEBUG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // Switch on debugging for the padding areas @@ -4127,16 +4258,17 @@ void spiWriteBlock(uint16_t color, uint32_t repeat) SPI1U = SPIUMOSI | SPIUDUPLEX | SPIUSSE; } -#elif ESP8266 // ESP32 or a ESP8266 running at 80MHz SPI so slow things down -#define BUFFER_SIZE 64 -void spiWriteBlock(uint16_t color, uint32_t repeat) -{ - - uint8_t colorBin[] = { (uint8_t) (color >> 8), (uint8_t) color}; - SPI.writePattern(&colorBin[0], 2, repeat); - -} +//#elif ESP8266 // ESP32 or a ESP8266 running at 80MHz SPI so slow things down +// +//#define BUFFER_SIZE 64 +//void spiWriteBlock(uint16_t color, uint32_t repeat) +//{ +// +// uint8_t colorBin[] = { (uint8_t) (color >> 8), (uint8_t) color}; +// SPI.writePattern(&colorBin[0], 2, repeat); +// +//} #else // Low level register based ESP32 code @@ -4174,1601 +4306,19 @@ void spiWriteBlock(uint16_t color, uint32_t repeat) } #endif -// The following touch screen support code by maxpautsch was merged 1/10/17 -// https://github.com/maxpautsch -// Define TOUCH_CS is the user setup file to enable this code -// A demo is provided in examples Generic folder -// Additions by Bodmer to double sample and use Z value to improve detection reliability + +//////////////////////////////////////////////////////////////////////////////////////// #ifdef TOUCH_CS -/*************************************************************************************** -** Function name: getTouchRaw -** Description: read raw touch position. Return false if not pressed. -***************************************************************************************/ -uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ - uint16_t tmp; - CS_H; - - spi_begin_touch(); - - T_CS_L; - - // Start bit + YP sample request for x position - tmp = SPI.transfer(0xd0); - tmp = SPI.transfer(0); - tmp = tmp <<5; - tmp |= 0x1f & (SPI.transfer(0)>>3); - - *x = tmp; - - // Start bit + XP sample request for y position - SPI.transfer(0x90); - tmp = SPI.transfer(0); - tmp = tmp <<5; - tmp |= 0x1f & (SPI.transfer(0)>>3); - - *y = tmp; - - T_CS_H; - - spi_end_touch(); - - return true; -} - -/*************************************************************************************** -** Function name: getTouchRawZ -** Description: read raw pressure on touchpad and return Z value. -***************************************************************************************/ -uint16_t TFT_eSPI::getTouchRawZ(void){ - CS_H; - - spi_begin_touch(); - - T_CS_L; - - // Z sample request - uint16_t tz = 0xFFF; - SPI.transfer(0xb1); - tz += SPI.transfer16(0xc1) >> 3; - tz -= SPI.transfer16(0x91) >> 3; - - T_CS_H; - - spi_end_touch(); - - return tz; -} - -/*************************************************************************************** -** Function name: validTouch -** Description: read validated position. Return false if not pressed. -***************************************************************************************/ -#define _RAWERR 10 // Deadband in position samples -uint8_t TFT_eSPI::validTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ - uint16_t x_tmp, y_tmp, x_tmp2, y_tmp2; - - // Wait until pressure stops increasing - uint16_t z1 = 1; - uint16_t z2 = 0; - while (z1 > z2) - { - z2 = z1; - z1 = getTouchRawZ(); - delay(1); - } - - // Serial.print("Z = ");Serial.println(z1); - - if (z1 <= threshold) return false; - - getTouchRaw(&x_tmp,&y_tmp); - - // Serial.print("Sample 1 x,y = "); Serial.print(x_tmp);Serial.print(",");Serial.print(y_tmp); - // Serial.print(", Z = ");Serial.println(z1); - - delay(1); // Small delay to the next sample - if (getTouchRawZ() <= threshold) return false; - - delay(2); // Small delay to the next sample - getTouchRaw(&x_tmp2,&y_tmp2); - - // Serial.print("Sample 2 x,y = "); Serial.print(x_tmp2);Serial.print(",");Serial.println(y_tmp2); - // Serial.print("Sample difference = ");Serial.print(abs(x_tmp - x_tmp2));Serial.print(",");Serial.println(abs(y_tmp - y_tmp2)); - - if (abs(x_tmp - x_tmp2) > _RAWERR) return false; - if (abs(y_tmp - y_tmp2) > _RAWERR) return false; - - *x = x_tmp; - *y = y_tmp; - - return true; -} - -/*************************************************************************************** -** Function name: getTouch -** Description: read callibrated position. Return false if not pressed. -***************************************************************************************/ -#define Z_THRESHOLD 350 // Touch pressure threshold for validating touches -uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ - uint16_t x_tmp, y_tmp, xx, yy; - - if (threshold<20) threshold = 20; - if (_pressTime > millis()) threshold=20; - - uint8_t n = 5; - uint8_t valid = 0; - while (n--) - { - if (validTouch(&x_tmp, &y_tmp, threshold)) valid++;; - } - - if (valid<1) { _pressTime = 0; return false; } - - _pressTime = millis() + 50; - - if(!touchCalibration_rotate){ - xx=(x_tmp-touchCalibration_x0)*_width/touchCalibration_x1; - yy=(y_tmp-touchCalibration_y0)*_height/touchCalibration_y1; - if(touchCalibration_invert_x) - xx = _width - xx; - if(touchCalibration_invert_y) - yy = _height - yy; - } else { - yy=(x_tmp-touchCalibration_x0)*_height/touchCalibration_x1; - xx=(y_tmp-touchCalibration_y0)*_width/touchCalibration_y1; - if(touchCalibration_invert_x) - xx = _width - xx; - if(touchCalibration_invert_y) - yy = _height - yy; - } - - if (xx >= _width || yy >= _height) return valid; - - _pressX = xx; - _pressY = yy; - *x = _pressX; - *y = _pressY; - return valid; -} - -/*************************************************************************************** -** Function name: calibrateTouch -** Description: generates calibration parameters for touchscreen. -***************************************************************************************/ -void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_fg, uint32_t color_bg, uint8_t size){ - int16_t values[] = {0,0,0,0,0,0,0,0}; - uint16_t x_tmp, y_tmp; - - - - for(uint8_t i = 0; i<4; i++){ - fillRect(0, 0, size+1, size+1, color_bg); - fillRect(0, _height-size-1, size+1, size+1, color_bg); - fillRect(_width-size-1, 0, size+1, size+1, color_bg); - fillRect(_width-size-1, _height-size-1, size+1, size+1, color_bg); - - if (i == 5) break; // used to clear the arrows - - switch (i) { - case 0: // up left - drawLine(0, 0, 0, size, color_fg); - drawLine(0, 0, size, 0, color_fg); - drawLine(0, 0, size , size, color_fg); - break; - case 1: // bot left - drawLine(0, _height-size-1, 0, _height-1, color_fg); - drawLine(0, _height-1, size, _height-1, color_fg); - drawLine(size, _height-size-1, 0, _height-1 , color_fg); - break; - case 2: // up right - drawLine(_width-size-1, 0, _width-1, 0, color_fg); - drawLine(_width-size-1, size, _width-1, 0, color_fg); - drawLine(_width-1, size, _width-1, 0, color_fg); - break; - case 3: // bot right - drawLine(_width-size-1, _height-size-1, _width-1, _height-1, color_fg); - drawLine(_width-1, _height-1-size, _width-1, _height-1, color_fg); - drawLine(_width-1-size, _height-1, _width-1, _height-1, color_fg); - break; - } - - // user has to get the chance to release - if(i>0) delay(1000); - - for(uint8_t j= 0; j<8; j++){ - // Use a lower detect threshold as corners tend to be less sensitive - while(!validTouch(&x_tmp, &y_tmp, Z_THRESHOLD/4)); - values[i*2 ] += x_tmp; - values[i*2+1] += y_tmp; - } - values[i*2 ] /= 8; - values[i*2+1] /= 8; - } - - - - // check orientation - // from case 0 to case 1, the y value changed. - // If the measured delta of the touch x axis is bigger than the delta of the y axis, the touch and TFT axes are switched. - touchCalibration_rotate = false; - if(abs(values[0]-values[2]) > abs(values[1]-values[3])){ - touchCalibration_rotate = true; - touchCalibration_x0 = (values[0] + values[4])/2; // calc min x - touchCalibration_x1 = (values[2] + values[6])/2; // calc max x - touchCalibration_y0 = (values[1] + values[3])/2; // calc min y - touchCalibration_y1 = (values[5] + values[7])/2; // calc max y - } else { - touchCalibration_x0 = (values[0] + values[2])/2; // calc min x - touchCalibration_x1 = (values[4] + values[6])/2; // calc max x - touchCalibration_y0 = (values[1] + values[5])/2; // calc min y - touchCalibration_y1 = (values[3] + values[7])/2; // calc max y - } - - // in addition, the touch screen axis could be in the opposit direction of the TFT axis - touchCalibration_invert_x = false; - if(touchCalibration_x0 > touchCalibration_x1){ - values[0]=touchCalibration_x0; - touchCalibration_x0 = touchCalibration_x1; - touchCalibration_x1 = values[0]; - touchCalibration_invert_x = true; - } - touchCalibration_invert_y = false; - if(touchCalibration_y0 > touchCalibration_y1){ - values[0]=touchCalibration_y0; - touchCalibration_y0 = touchCalibration_y1; - touchCalibration_y1 = values[0]; - touchCalibration_invert_y = true; - } - - // pre calculate - touchCalibration_x1 -= touchCalibration_x0; - touchCalibration_y1 -= touchCalibration_y0; - - if(touchCalibration_x0 == 0) touchCalibration_x0 = 1; - if(touchCalibration_x1 == 0) touchCalibration_x1 = 1; - if(touchCalibration_y0 == 0) touchCalibration_y0 = 1; - if(touchCalibration_y1 == 0) touchCalibration_y1 = 1; - - // export parameters, if pointer valid - if(parameters != NULL){ - parameters[0] = touchCalibration_x0; - parameters[1] = touchCalibration_x1; - parameters[2] = touchCalibration_y0; - parameters[3] = touchCalibration_y1; - parameters[4] = touchCalibration_rotate | (touchCalibration_invert_x <<1) | (touchCalibration_invert_y <<2); - } -} - - -/*************************************************************************************** -** Function name: setTouch -** Description: imports calibration parameters for touchscreen. -***************************************************************************************/ -void TFT_eSPI::setTouch(uint16_t *parameters){ - touchCalibration_x0 = parameters[0]; - touchCalibration_x1 = parameters[1]; - touchCalibration_y0 = parameters[2]; - touchCalibration_y1 = parameters[3]; - - if(touchCalibration_x0 == 0) touchCalibration_x0 = 1; - if(touchCalibration_x1 == 0) touchCalibration_x1 = 1; - if(touchCalibration_y0 == 0) touchCalibration_y0 = 1; - if(touchCalibration_y1 == 0) touchCalibration_y1 = 1; - - touchCalibration_rotate = parameters[4] & 0x01; - touchCalibration_invert_x = parameters[4] & 0x02; - touchCalibration_invert_y = parameters[4] & 0x04; -} - -#else // TOUCH CS is not defined so generate dummy functions that do nothing - -/*************************************************************************************** -** Function name: getTouchRaw -** Description: read raw touch position. Return false if not pressed. -***************************************************************************************/ -uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ - return true; -} - -/*************************************************************************************** -** Function name: getTouchRawZ -** Description: read raw pressure on touchpad and return Z value. -***************************************************************************************/ -uint16_t TFT_eSPI::getTouchRawZ(void){ - return true; -} - -/*************************************************************************************** -** Function name: validTouch -** Description: read validated position. Return false if not pressed. -***************************************************************************************/ -uint8_t TFT_eSPI::validTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ - return true; -} - -/*************************************************************************************** -** Function name: getTouch -** Description: read callibrated position. Return false if not pressed. -***************************************************************************************/ -uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ - return true; -} - -/*************************************************************************************** -** Function name: calibrateTouch -** Description: generates calibration parameters for touchscreen. -***************************************************************************************/ -void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_bg, uint32_t color_fg, uint8_t size){ -} - - -/*************************************************************************************** -** Function name: setTouch -** Description: imports calibration parameters for touchscreen. -***************************************************************************************/ -void TFT_eSPI::setTouch(uint16_t *parameters){ -} - -#endif // TOUCH_CS - - - -/*************************************************************************************** -** Code for the GFX button UI element -** Grabbed from Adafruit_GFX library and enhanced to handle any label font -***************************************************************************************/ -TFT_eSPI_Button::TFT_eSPI_Button(void) { - _gfx = 0; -} - -// Classic initButton() function: pass center & size -void TFT_eSPI_Button::initButton( - TFT_eSPI *gfx, int16_t x, int16_t y, uint16_t w, uint16_t h, - uint16_t outline, uint16_t fill, uint16_t textcolor, - char *label, uint8_t textsize) -{ - // Tweak arguments and pass to the newer initButtonUL() function... - initButtonUL(gfx, x - (w / 2), y - (h / 2), w, h, outline, fill, - textcolor, label, textsize); -} - -// Newer function instead accepts upper-left corner & size -void TFT_eSPI_Button::initButtonUL( - TFT_eSPI *gfx, int16_t x1, int16_t y1, uint16_t w, uint16_t h, - uint16_t outline, uint16_t fill, uint16_t textcolor, - char *label, uint8_t textsize) -{ - _x1 = x1; - _y1 = y1; - _w = w; - _h = h; - _outlinecolor = outline; - _fillcolor = fill; - _textcolor = textcolor; - _textsize = textsize; - _gfx = gfx; - strncpy(_label, label, 9); -} - -void TFT_eSPI_Button::drawButton(boolean inverted) { - uint16_t fill, outline, text; - - if(!inverted) { - fill = _fillcolor; - outline = _outlinecolor; - text = _textcolor; - } else { - fill = _textcolor; - outline = _outlinecolor; - text = _fillcolor; - } - - uint8_t r = min(_w, _h) / 4; // Corner radius - _gfx->fillRoundRect(_x1, _y1, _w, _h, r, fill); - _gfx->drawRoundRect(_x1, _y1, _w, _h, r, outline); - - _gfx->setTextColor(text); - _gfx->setTextSize(_textsize); - - uint8_t tempdatum = _gfx->getTextDatum(); - _gfx->setTextDatum(MC_DATUM); - _gfx->drawString(_label, _x1 + (_w/2), _y1 + (_h/2)); - _gfx->setTextDatum(tempdatum); -} - -boolean TFT_eSPI_Button::contains(int16_t x, int16_t y) { - return ((x >= _x1) && (x < (_x1 + _w)) && - (y >= _y1) && (y < (_y1 + _h))); -} - -void TFT_eSPI_Button::press(boolean p) { - laststate = currstate; - currstate = p; -} - -boolean TFT_eSPI_Button::isPressed() { return currstate; } -boolean TFT_eSPI_Button::justPressed() { return (currstate && !laststate); } -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 -***************************************************************************************/ -/*************************************************************************************** -// Color bytes are swapped when writing to RAM, this introduces a small overhead but -// then rendering to screen can use the faster call. In general rendering graphics in -// the Sprite is very fast, but writing to the TFT is slow, so there is a performance -// gain by using swapped bytes. -***************************************************************************************/ - -/*************************************************************************************** -** 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; - _bpp16 = true; - _iswapBytes = false; // Do not swap pushImage colour bytes by default - - _created = false; - - _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*/ -// returns a uint8_t* pointer, cast returned value to (uint16_t*) for 16 bit colours -uint8_t* TFT_eSprite::createSprite(int16_t w, int16_t h) -{ - - if ( _created ) - { - if ( _bpp16 ) return ( uint8_t*)_img; - return _img8; - } - - if ( w < 1 || h < 1 ) return NULL; - - _iwidth = w; - _iheight = h; - - _sx = 0; - _sy = 0; - _sw = w; - _sh = h; - _scolor = TFT_BLACK; - - // Add one extra "off screen" pixel to point out-of-bounds setWindow() coordinates - // this means push/writeColor functions do not need additional bounds checks. - if(_bpp16) - { - _img = (uint16_t*) malloc(w * h * 2 + 1); - if (_img) - { - _created = true; - fillSprite(TFT_BLACK); - return (uint8_t*)_img; - } - } - else - { - _img8 = ( uint8_t*) malloc(w * h + 1); - if (_img8) - { - _created = true; - fillSprite(TFT_BLACK); - return _img8; - } - } - - return NULL; -} - - -/*************************************************************************************** -** Function name: setDepth -** Description: Set bits per pixel for colour (8 or 16) -*************************************************************************************x*/ - -uint8_t* TFT_eSprite::setColorDepth(int8_t b) -{ - // Can't change an existing sprite's colour depth so delete it - if (_created) - { - if (_bpp16) free(_img); - else free(_img8); - } - - // Now define the new colour depth - if ( b > 8 ) _bpp16 = true; // Bytes per pixel - else _bpp16 = false; - - // If it existed, re-create the sprite with the new colour depth - if (_created) - { - _created = false; - return createSprite(_iwidth, _iheight); - } - - return NULL; -} - - -/*************************************************************************************** -** Function name: deleteSprite -** Description: Delete the sprite to free up memory (RAM) -*************************************************************************************x*/ -void TFT_eSprite::deleteSprite(void) -{ - if (!_created ) return; - - if (_bpp16) free(_img); - else free(_img8); - - _created = false; -} - - -/*************************************************************************************** -** Function name: pushSprite -** Description: Push the sprite to the TFT at x, y -*************************************************************************************x*/ -void TFT_eSprite::pushSprite(int32_t x, int32_t y) -{ - if (!_created ) return; - - if (_bpp16) _tft->pushImage(x, y, _iwidth, _iheight, _img ); - else _tft->pushImage(x, y, _iwidth, _iheight, _img8); -} - - -/*************************************************************************************** -** Function name: pushSprite -** Description: Push the sprite to the TFT at x, y with transparent colour -*************************************************************************************x*/ -void TFT_eSprite::pushSprite(int32_t x, int32_t y, uint16_t transp) -{ - if (!_created ) return; - - if (_bpp16) _tft->pushImage(x, y, _iwidth, _iheight, _img, transp ); - else - { - transp = (uint8_t)((transp & 0xE000)>>8 | (transp & 0x0700)>>6 | (transp & 0x0018)>>3); - _tft->pushImage(x, y, _iwidth, _iheight, _img8, (uint8_t)transp); - } -} - - -/*************************************************************************************** -** Function name: readPixel -** Description: Read 565 colour of a pixel at defined coordinates -*************************************************************************************x*/ -uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) -{ - if (!_created ) return 0; - - if (_bpp16) - { - uint16_t color = _img[x + y * _iwidth]; - return (color >> 8) | (color << 8); - } - - uint16_t color = _img8[x + y * _iwidth]; - if (color != 0) { - uint8_t blue[] = {0, 11, 21, 31}; - color = (color & 0xE0)<<8 | (color & 0xC0)<<5 - | (color & 0x1C)<<6 | (color & 0x1C)<<3 - | blue[color & 0x03]; - } - - return color; -} - - -/*************************************************************************************** -** Function name: pushImage -** Description: push 565 colour image into a defined area of a sprite -*************************************************************************************x*/ -void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t *data) -{ - if ((x > _iwidth) || (y > _iheight) || (w == 0) || (h == 0) || !_created) return; - - if (_bpp16) - { - for (uint32_t yp = y; yp < y + h; yp++) - { - for (uint32_t xp = x; xp < x + w; xp++) - { - uint16_t color = *data++; - if(!_iswapBytes) color = color<<8 | color>>8; - _img[xp + yp * _iwidth] = color; - } - } - } - else - { - for (uint32_t yp = y; yp < y + h; yp++) - { - for (uint32_t xp = x; xp < x + w; xp++) - { - uint16_t color = *data++; - if(_iswapBytes) color = color<<8 | color>>8; - _img8[xp + yp * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); - } - } - } -} - - -/*************************************************************************************** -** Function name: pushImage -** Description: push 565 colour FLASH (PROGMEM) image into a defined area -*************************************************************************************x*/ -void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uint16_t *data) -{ - if ((x > _iwidth) || (y > _iheight) || (w == 0) || (h == 0) || !_created) return; - - if (_bpp16) - { - for (uint32_t yp = y; yp < y + h; yp++) - { - for (uint32_t xp = x; xp < x + w; xp++) - { - uint16_t color = pgm_read_word(data++); - if(!_iswapBytes) color = color<<8 | color>>8; - _img[xp + yp * _iwidth] = color; - } - } - } - else - { - for (uint32_t yp = y; yp < y + h; yp++) - { - for (uint32_t xp = x; xp < x + w; xp++) - { - uint16_t color = pgm_read_word(data++); - if(_iswapBytes) color = color<<8 | color>>8; - _img8[xp + yp * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); - } - } - } -} - - -/*************************************************************************************** -** Function name: setSwapBytes -** Description: Used by 16 bit pushImage() to swap byte order in colours -***************************************************************************************/ -void TFT_eSprite::setSwapBytes(bool swap) -{ - _iswapBytes = swap; -} - - -/*************************************************************************************** -** Function name: getSwapBytes -** Description: Return the swap byte order for colours -***************************************************************************************/ -bool TFT_eSprite::getSwapBytes(void) -{ - return _iswapBytes; -} - - -/*************************************************************************************** -** Function name: setWindow -** Description: Set the bounds of a window for pushColor and writeColor -*************************************************************************************x*/ -void TFT_eSprite::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) -{ - bool duff_coord = false; - - if (x0 > x1) swap_coord(x0, x1); - if (y0 > y1) swap_coord(y0, y1); - - if (x0 < 0) x0 = 0; - if (x0 >= _iwidth) duff_coord = true; - if (x1 < 0) x1 = 0; - if (x1 >= _iwidth) x1 = _iwidth - 1; - - if (y0 < 0) y0 = 0; - if (y0 >= _iheight) duff_coord = true; - if (y1 < 0) y1 = 0; - if (y1 >= _iheight) y1 = _iheight - 1; - - if (duff_coord) - { // Point to that extra "off screen" pixel - _xs = 0; - _ys = _iheight; - _xe = 0; - _ye = _iheight; - } - else - { - _xs = x0; - _ys = y0; - _xe = x1; - _ye = y1; - } - - _xptr = _xs; - _yptr = _ys; -} - - -/*************************************************************************************** -** Function name: pushColor -** Description: Send a new pixel to the set window -*************************************************************************************x*/ -void TFT_eSprite::pushColor(uint32_t color) -{ - if (!_created ) return; - - // Write the colour to RAM in set window - if (_bpp16) - _img [_xptr + _yptr * _iwidth] = (uint16_t) (color >> 8) | (color << 8); - - else - _img8[_xptr + _yptr * _iwidth] = (uint8_t )((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); - - // Increment x - _xptr++; - - // Wrap on x and y to start, increment y if needed - if (_xptr > _xe) - { - _xptr = _xs; - _yptr++; - if (_yptr > _ye) _yptr = _ys; - } - -} - - -/*************************************************************************************** -** Function name: pushColor -** Description: Send a "len" new pixels to the set window -*************************************************************************************x*/ -void TFT_eSprite::pushColor(uint32_t color, uint16_t len) -{ - if (!_created ) return; - - uint16_t pixelColor; - if (_bpp16) - pixelColor = (uint16_t) (color >> 8) | (color << 8); - - else - pixelColor = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; - - while(len--) writeColor(pixelColor); -} - - -/*************************************************************************************** -** Function name: writeColor -** Description: Write a pixel with pre-formatted colour to the set window -*************************************************************************************x*/ -void TFT_eSprite::writeColor(uint16_t color) -{ - if (!_created ) return; - - // Write 16 bit RGB 565 encoded colour to RAM - if (_bpp16) _img [_xptr + _yptr * _iwidth] = color; - - // Write 8 bit RGB 332 encoded colour to RAM - else _img8[_xptr + _yptr * _iwidth] = (uint8_t) color; - - // Increment x - _xptr++; - - // Wrap on x and y to start, increment y if needed - if (_xptr > _xe) - { - _xptr = _xs; - _yptr++; - if (_yptr > _ye) _yptr = _ys; - } -} - - -/*************************************************************************************** -** Function name: setScrollRect -** Description: Set scroll area within the sprite and the gap fill colour -*************************************************************************************x*/ -void TFT_eSprite::setScrollRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t color) -{ - if ((x >= _iwidth) || (y >= _iheight) || !_created ) return; - - if (x < 0) x = 0; - if (y < 0) y = 0; - - if ((x + w) > _iwidth ) w = _iwidth - x; - if ((y + h) > _iheight) h = _iheight - y; - - if ( w < 1 || h < 1) return; - - _sx = x; - _sy = y; - _sw = w; - _sh = h; - - _scolor = color; -} - - -/*************************************************************************************** -** Function name: scroll -** Description: Scroll dx,dy pixels, positive right,down, negative left,up -*************************************************************************************x*/ -void TFT_eSprite::scroll(int16_t dx, int16_t dy) -{ - if (abs(dx) >= _sw || abs(dy) >= _sh) - { - fillRect (_sx, _sy, _sw, _sh, _scolor); - return; - } - - // Fetch the scroll area width and height set by setScrollRect() - uint32_t w = _sw - abs(dx); // line width to copy - uint32_t h = _sh - abs(dy); // lines to copy - int32_t iw = _iwidth; // width of sprite - - // Fetch the x,y origin set by setScrollRect() - uint32_t tx = _sx; // to x - uint32_t fx = _sx; // from x - uint32_t ty = _sy; // to y - uint32_t fy = _sy; // from y - - // Adjust for x delta - if (dx <= 0) fx -= dx; - else tx += dx; - - // Adjust for y delta - if (dy <= 0) fy -= dy; - else - { // Scrolling down so start copy from bottom - ty = ty + _sh - 1; // "To" pointer - iw = -iw; // Pointer moves backwards - fy = ty - dy; // "From" pointer - } - - // Calculate "from y" and "to y" pointers in RAM - uint32_t fyp = fx + fy * _iwidth; - uint32_t typ = tx + ty * _iwidth; - - // Now move the pixels in RAM - if (_bpp16) - { - while (h--) - { // move pixel lines (to, from, byte count) - memmove( _img + typ, _img + fyp, w<<1); - typ += iw; - fyp += iw; - } - } - else - { - while (h--) - { // move pixel lines (to, from, byte count) - memmove( _img8 + typ, _img8 + fyp, w); - typ += iw; - fyp += iw; - } - } - - // Fill the gap left by the scrolling - if (dx > 0) fillRect(_sx, _sy, dx, _sh, _scolor); - if (dx < 0) fillRect(_sx + _sw + dx, _sy, -dx, _sh, _scolor); - if (dy > 0) fillRect(_sx, _sy, _sw, dy, _scolor); - if (dy < 0) fillRect(_sx, _sy + _sh + dy, _sw, -dy, _scolor); -} - - -/*************************************************************************************** -** Function name: fillSprite -** Description: Fill the whole sprite with defined colour -*************************************************************************************x*/ -void TFT_eSprite::fillSprite(uint32_t color) -{ - if (!_created ) return; - - // Use memset if possible as it is super fast - if(( (uint8_t)color == (uint8_t)(color>>8) ) && _bpp16) - memset(_img, (uint8_t)color, _iwidth * _iheight * 2); - else if (!_bpp16) - { - color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; - memset(_img8, (uint8_t)color, _iwidth * _iheight); - } - - else fillRect(0, 0, _iwidth, _iheight, color); -} - - -/*************************************************************************************** -** 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 -** Description: Return the width of sprite -*************************************************************************************x*/ -// Return the size of the display -int16_t TFT_eSprite::width(void) -{ - if (!_created ) return 0; - return _iwidth; -} - - -/*************************************************************************************** -** Function name: height -** Description: Return the height of sprite -*************************************************************************************x*/ -int16_t TFT_eSprite::height(void) -{ - if (!_created ) return 0; - return _iheight; -} - - -/*************************************************************************************** -** 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 (!_created ) return; - - if ((x >= _iwidth) || // Clip right - (y >= _iheight) || // Clip bottom - ((x + 6 * size - 1) < 0) || // Clip left - ((y + 8 * size - 1) < 0)) // Clip top - return; - -#ifdef LOAD_GLCD -//>>>>>>>>>>>>>>>>>> -#ifdef LOAD_GFXFF - if(!gfxFont) { // 'Classic' built-in font -#endif -//>>>>>>>>>>>>>>>>>> - - boolean fillbg = (bg != color); - - if ((size==1) && fillbg) - { - uint8_t column[6]; - uint8_t 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 - // Filter out bad characters not present in font - if ((c >= (uint8_t)pgm_read_byte(&gfxFont->first)) && (c <= (uint8_t)pgm_read_byte(&gfxFont->last ))) - { -//>>>>>>>>>>>>>>>>>>>>>>>>>>> - - 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 = 0, yo16 = 0; - - 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; - } - } - } + #include "Extensions/Touch.cpp" #endif +#include "Extensions/Button.cpp" -#ifdef LOAD_GLCD - #ifdef LOAD_GFXFF - } // End classic vs custom font - #endif +#include "Extensions/Sprite.cpp" + +#ifdef SMOOTH_FONT + #include "Extensions/Smooth_font.cpp" #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) || !_created) return; - - if (_bpp16) - { - color = (color >> 8) | (color << 8); - _img[x+y*_iwidth] = (uint16_t) color; - } - else - { - _img8[x+y*_iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); - } -} - - -/*************************************************************************************** -** 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) -{ - if (!_created ) return; - - 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) || !_created) return; - - if (y < 0) { h += y; y = 0; } - - if ((y + h) > _iheight) h = _iheight - y; - - if (h < 1) return; - - if (_bpp16) - { - color = (color >> 8) | (color << 8); - int32_t yp = x + _iwidth * y; - while (h--) {_img[yp] = (uint16_t) color; yp += _iwidth;} - } - else - { - color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; - while (h--) _img8[x + _iwidth * y++] = (uint8_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) || !_created) return; - - if (x < 0) { w += x; x = 0; } - - if ((x + w) > _iwidth) w = _iwidth - x; - - if (w < 1) return; - - if (_bpp16) - { - color = (color >> 8) | (color << 8); - while (w--) _img[_iwidth * y + x++] = (uint16_t) color; - } - else - { - color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; - memset(_img8+_iwidth * y + x, (uint8_t)color, w); - } -} - - -/*************************************************************************************** -** 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 (!_created ) return; - - 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; - - int32_t yp = _iwidth * y + x; - - if (_bpp16) - { - color = (color >> 8) | (color << 8); - uint32_t iw = w; - int32_t ys = yp; - if(h--) {while (iw--) _img[yp++] = (uint16_t) color;} - yp = ys; - while (h--) - { - yp += _iwidth; - memcpy( _img+yp, _img+ys, w<<1); - } - } - else - { - color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; - while (h--) - { - memset(_img8 + yp, (uint8_t)color, w); - yp += _iwidth; - } - } -} - - -/*************************************************************************************** -** Function name: write -** Description: draw characters piped through serial stream -*************************************************************************************x*/ -size_t TFT_eSprite::write(uint8_t utf8) -{ - if (!_created ) return 0; - - 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 - else if (utf8 < 32) return 0; - - 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) - { - if (utf8 > 127) return 0; - // 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)) - { - if (utf8 > 127) return 0; - // 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; - } - if (_icursor_y >= _iheight) _icursor_y = 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 > (uint8_t)pgm_read_byte(&gfxFont->last )) return 0; - if (uniCode < (uint8_t)pgm_read_byte(&gfxFont->first)) return 0; - - 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); - } - if (_icursor_y >= _iheight) _icursor_y = 0; - 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 (!_created ) return 0; - - 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->first)) && (uniCode <= pgm_read_byte(&gfxFont->last) )) - { - 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 - } - - if ((font>1) && (font<9) && ((uniCode < 32) || (uniCode > 127))) return 0; - - 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; - uint8_t 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 >= _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); - int16_t color; - if (_bpp16) color = (textcolor >> 8) | (textcolor << 8); - else color = ((textcolor & 0xE000)>>8 | (textcolor & 0x0700)>>6 | (textcolor & 0x0018)>>3); - int px = 0, py = pY; // To hold character block start and end column and row values - int pc = 0; // Pixel count - uint8_t np = textsize * textsize; // Number of pixels in a drawn pixel - uint8_t tnp = 0; // Temporary copy of np for while loop - uint8_t 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--) writeColor(color); } - else writeColor(color); - - 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 bae7afe..8148f59 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -1,9 +1,9 @@ /*************************************************** - Arduino TFT graphics library targetted at ESP8266 + Arduino TFT graphics library targeted at ESP8266 and ESP32 based boards. This is a standalone library that contains the - hardware driver, the graphics funtions and the + hardware driver, the graphics functions and the proportional fonts. The larger fonts are Run Length Encoded to reduce @@ -74,6 +74,17 @@ #include +#ifdef SMOOTH_FONT + // Call up the SPIFFS FLASH filing system for the anti-aliased fonts + #define FS_NO_GLOBALS + #include + + #ifdef ESP32 + #include "SPIFFS.h" + #endif +#endif + + #if defined (ESP8266) && defined (D0_USED_FOR_DC) #define DC_C digitalWrite(TFT_DC, LOW) #define DC_D digitalWrite(TFT_DC, HIGH) @@ -255,7 +266,11 @@ template static inline void swap_coord(T& a, T& b) { T t = a; a = b; b = t; } -// This is a structure to conveniently hold infomation on the default fonts +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +// This is a structure to conveniently hold information on the default fonts // Stores pointer to font character image address table, width table and height // Create a null set in case some fonts not used (to prevent crash) @@ -318,7 +333,6 @@ const PROGMEM fontinfo fontdata [] = { }; - // Class functions and variables class TFT_eSPI : public Print { @@ -343,9 +357,8 @@ class TFT_eSPI : public Print { void setWindow(int16_t x0, int16_t y0, int16_t x1, int16_t y1), pushColor(uint16_t color), pushColor(uint16_t color, uint16_t len), - //pushColors(uint16_t *data, uint8_t len), - pushColors(uint8_t *data, uint32_t len), pushColors(uint16_t *data, uint32_t len, bool swap = true), // With byte swap option + pushColors(uint8_t *data, uint32_t len), fillScreen(uint32_t color); @@ -375,7 +388,7 @@ class TFT_eSPI : public Print { setTextColor(uint16_t fgcolor, uint16_t bgcolor), setTextSize(uint8_t size), - setTextWrap(boolean wrap), + setTextWrap(boolean wrapX, boolean wrapY = false), setTextDatum(uint8_t datum), setTextPadding(uint16_t x_width), @@ -427,13 +440,14 @@ class TFT_eSPI : public Print { uint8_t getRotation(void), getTextDatum(void), - color332(uint16_t color565); // Convert 16 bit colour to 8 bits + color16to8(uint16_t color565); // Convert 16 bit colour to 8 bits int16_t getCursorX(void), getCursorY(void); uint16_t fontsLoaded(void), - color565(uint8_t r, uint8_t g, uint8_t b); + color565(uint8_t r, uint8_t g, uint8_t b), + color8to16(uint8_t color332); // Convert 8 bit colour to 16 bits int16_t drawNumber(long long_num,int poX, int poY, int font), drawNumber(long long_num,int poX, int poY), @@ -462,49 +476,39 @@ class TFT_eSPI : public Print { void setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye); - // These are associated with the Touch Screen handlers - uint8_t getTouchRaw(uint16_t *x, uint16_t *y); - uint16_t getTouchRawZ(void); - uint8_t getTouch(uint16_t *x, uint16_t *y, uint16_t threshold = 600); - - void calibrateTouch(uint16_t *data, uint32_t color_fg, uint32_t color_bg, uint8_t size); - void setTouch(uint16_t *data); size_t write(uint8_t); + int32_t cursor_x, cursor_y; + uint32_t textcolor, textbgcolor; + + private: inline void spi_begin() __attribute__((always_inline)); inline void spi_end() __attribute__((always_inline)); - inline void spi_begin_touch() __attribute__((always_inline)); - inline void spi_end_touch() __attribute__((always_inline)); void readAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye); - + uint8_t tabcolor, colstart = 0, rowstart = 0; // some ST7735 displays need this changed - volatile uint32_t *dcport, *csport;//, *mosiport, *clkport, *rsport; + volatile uint32_t *dcport, *csport; - uint32_t cspinmask, dcpinmask, wrpinmask;//, mosipinmask, clkpinmask; + uint32_t cspinmask, dcpinmask, wrpinmask; uint32_t lastColor = 0xFFFF; - // These are associated with the Touch Screen handlers - uint8_t validTouch(uint16_t *x, uint16_t *y, uint16_t threshold = 600); - // Initialise with example calibration values so processor does not crash if setTouch() not called in setup() - uint16_t touchCalibration_x0 = 300, touchCalibration_x1 = 3600, touchCalibration_y0 = 300, touchCalibration_y1 = 3600; - uint8_t touchCalibration_rotate = 1, touchCalibration_invert_x = 2, touchCalibration_invert_y = 0; - uint32_t _pressTime; - uint16_t _pressX, _pressY; protected: - int32_t cursor_x, cursor_y, win_xe, win_ye, padX; + int32_t win_xe, win_ye, padX; - uint32_t _width_orig, _height_orig; // Display w/h as input, used by setRotation() + uint32_t _init_width, _init_height; // Display w/h as input, used by setRotation() uint32_t _width, _height; // Display w/h as modified by current rotation - uint32_t textcolor, textbgcolor, fontsloaded, addr_row, addr_col; + uint32_t addr_row, addr_col; + + uint32_t fontsloaded; uint8_t glyph_ab, // glyph height above baseline glyph_bb, // glyph height below baseline @@ -513,164 +517,32 @@ class TFT_eSPI : public Print { textdatum, // Text reference datum rotation; // Display rotation (0-3) - bool textwrap; // If set, 'wrap' text at right edge of display + bool textwrapX, textwrapY; // If set, 'wrap' text at right and optionally bottom edge of display bool _swapBytes; // Swap the byte order for TFT pushImage() bool locked, inTransaction; // Transaction and mutex lock flags for ESP32 - bool _booted; int32_t _lastColor; #ifdef LOAD_GFXFF - GFXfont - *gfxFont; + GFXfont *gfxFont; #endif -}; +// Load the Touch extension +#ifdef TOUCH_CS + #include "Extensions/Touch.h" +#endif -/*************************************************************************************** -// The following button class has been ported over from the Adafruit_GFX library so -// should be compatible. -// A slightly different implementation in this TFT_eSPI library allows the button -// legends to be in any font -***************************************************************************************/ +// Load the Anti-aliased font extension +#ifdef SMOOTH_FONT + #include "Extensions/Smooth_font.h" +#endif -class TFT_eSPI_Button { - - public: - TFT_eSPI_Button(void); - // "Classic" initButton() uses center & size - void initButton(TFT_eSPI *gfx, int16_t x, int16_t y, - uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, - uint16_t textcolor, char *label, uint8_t textsize); - - // New/alt initButton() uses upper-left corner & size - void initButtonUL(TFT_eSPI *gfx, int16_t x1, int16_t y1, - uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, - uint16_t textcolor, char *label, uint8_t textsize); - void drawButton(boolean inverted = false); - boolean contains(int16_t x, int16_t y); - - void press(boolean p); - boolean isPressed(); - boolean justPressed(); - boolean justReleased(); - - private: - TFT_eSPI *_gfx; - int16_t _x1, _y1; // Coordinates of top-left corner - uint16_t _w, _h; - uint8_t _textsize; - uint16_t _outlinecolor, _fillcolor, _textcolor; - char _label[10]; - - 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); - - // Create a sprite of width x height pixels, return a pointer to the RAM area - // Sketch can cast returned value to (uint16_t*) for 16 bit depth if needed - // RAM required is 1 byte per pixel for 8 bit colour depth, 2 bytes for 16 bit - uint8_t* createSprite(int16_t width, int16_t height); - - // Delete the sprite to free up the RAM - void deleteSprite(void); - - // Set the colour depth to 8 or 16 bits. Can be used to change depth an existing - // sprite, but clears it to black, returns a new pointer if sprite is re-created. - uint8_t* setColorDepth(int8_t b); - - 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), - - // Define a window to push 16 bit colour pixels into is a raster order - // Colours are converted to 8 bit if depth is set to 8 - setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1), - pushColor(uint32_t color), - pushColor(uint32_t color, uint16_t len), - // Push a pixel preformatted as a 8 or 16 bit colour (avoids conversion overhead) - writeColor(uint16_t color), - - // Set the scroll zone, top left corner at x,y with defined width and height - // The colour (optional, black is default) is used to fill the gap after the scroll - setScrollRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t color = TFT_BLACK), - // Scroll the defined zone dx,dy pixels. Negative values left,up, positive right,down - // dy is optional (default is then no up/down scroll). - // The sprite coordinate frame does not move because pixels are moved - scroll(int16_t dx, int16_t dy = 0), - - 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), - - // Set the sprite text cursor position for print class (does not change the TFT screen cursor) - 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 an image (colour bitmap) to the sprite - void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint16_t *data); - void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, const uint16_t *data); - - // Swap the byte order for pushImage() - corrects different image endianness - void setSwapBytes(bool swap); - bool getSwapBytes(void); - - // Push the sprite to the TFT screen, this fn calls pushImage() in the TFT class. - // Optionally a "transparent" colour can be defined, pixels of that colour will not be rendered - void pushSprite(int32_t x, int32_t y); - void pushSprite(int32_t x, int32_t y, uint16_t transparent); - - int16_t drawChar(unsigned int uniCode, int x, int y, int font), - drawChar(unsigned int uniCode, int x, int y); - - // Return the width and height of the sprite - int16_t width(void), - height(void); - - // Used by print class to print text to cursor position - size_t write(uint8_t); - - private: - - TFT_eSPI *_tft; - - protected: - - uint16_t *_img; // pointer to 16 bit sprite - uint8_t *_img8; // pointer to 8 bit sprite - bool _created, _bpp16; // created and bits per pixel depth flags - - int32_t _icursor_x, _icursor_y; - int32_t _xs, _ys, _xe, _ye, _xptr, _yptr; // for setWindow - int32_t _sx, _sy; // x,y for scroll zone - uint32_t _sw, _sh; // w,h for scroll zone - uint32_t _scolor; // gap fill colour for scroll zone - - boolean _iswapBytes; // Swap the byte order for Sprite pushImage() - - int32_t _iwidth, _iheight; // Sprite image width and height - -}; +}; // End of class TFT_eSPI +// Load the Button Class +#include "Extensions/Button.h" +// Load the Sprite Class +#include "Extensions/Sprite.h" #endif diff --git a/Tools/Create_Smooth_Font/Create_font_5/Create_font_5.pde b/Tools/Create_Smooth_Font/Create_font_5/Create_font_5.pde new file mode 100644 index 0000000..d89d6a9 --- /dev/null +++ b/Tools/Create_Smooth_Font/Create_font_5/Create_font_5.pde @@ -0,0 +1,483 @@ +// Select the character range in the user configure section starting at line 100 + +/* +Software License Agreement (FreeBSD License) + + Copyright (c) 2018 Bodmer (https://github.com/Bodmer) + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. 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 OWNER 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. + + The views and conclusions contained in the software and documentation are those + of the authors and should not be interpreted as representing official policies, + either expressed or implied, of the FreeBSD Project. + */ + +//////////////////////////////////////////////////////////////////////////////////////////////// + + +// This is a processing sketch to create font files for the TFT_eSPI library: + +// https://github.com/Bodmer/TFT_eSPI + +// Coded by Bodmer January 2018 + +// See comments below in code for specifying the font parameters +// (point size, unicode blocks to include etc). Ranges of characers or +// specific individual unicodes can be included in the created font file/ + +// Created fonts are saved in the sketches "FontFiles" folder. Press Ctrl+K to +// see that folder. + +// 16 bit unicodes in the range 0x0000 - 0xFFFF are supported. + +// The sketch will convert True Type (a .ttf or .otf file) file stored in the +// sketches "Data" folder as well as your computers system fonts. + +// To maximise rendering performance only include the characters you will use. +// Characters at the start of the file will render faster than those at the end. + +// Once created the files must be loaded into the ESP32 or ESP8266 SPIFFS memory +// using the Arduino IDE plugin detailed here: +// https://github.com/esp8266/arduino-esp8266fs-plugin +// https://github.com/me-no-dev/arduino-esp32fs-plugin + +// The sketch list all the available PC fonts to the console, you may need to increase +// console line count (in preferences.txt) to stop some fonts scrolling out of view. +// See link in File>Preferences to locate "preferences.txt" file. You must close +// Processing then edit the file lines. If Processing is not closed first then the +// edits will be overwritten by defaults! Edit "preferences.txt" as follows for +// 1000 lines, then save, then run Processing again: + + /* + console.length=1000 // Line 4 in file + console.scrollback.lines=1000 // Line 7 in file + */ + +// Useful links: + /* + + https://en.wikipedia.org/wiki/Unicode_font + + https://www.gnu.org/software/freefont/ + https://www.gnu.org/software/freefont/sources/ + https://www.gnu.org/software/freefont/ranges/ + http://savannah.gnu.org/projects/freefont/ + + http://www.google.com/get/noto/ + + https://github.com/Bodmer/TFT_eSPI + https://github.com/esp8266/arduino-esp8266fs-plugin + https://github.com/me-no-dev/arduino-esp32fs-plugin + + */ +//////////////////////////////////////////////////////////////////////////////////////////////// + +import java.awt.Desktop; + +// >>>>>>>>>> USER CONFIGURED PARAMETERS START HERE <<<<<<<<<< + + +// Use font name for ttf files placed in the "Data" folder or the font number seen in IDE Console for system fonts +// the font numbers are listed when the sketch is run. +// | 1 2 | Maximum filename size for SPIFFS is 32 including leading / +// 1234567890123456789012345 and added point size and .vlw extension, so max is 25 +String fontName = "Final-Frontier"; //Manually crop the filename length later after creation if needed + +String fontType = ".ttf"; //SPIFFS does not accept underscore in filename! +//String fontType = ".otf"; + +// Use font number instead of name, -1 means use name above, or a value >=0 means use system font number from list. +int fontNumber = -1; // << Use [Number] in brackets from the fonts listed in console window + +// Define the font size in points for the created font file +int fontSize = 28; + +// Font size to use in the Processing sketch display window that pops up (can be different to above) +int displayFontSize = 28; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Next we specify which unicode blocks from the the Basic Multilingual Plane (BMP) are included in the final font file. // +// Note: The ttf/otf font file MAY NOT contain all possible Unicode characters, refer to the fonts online documentation. // +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static final int[] unicodeBlocks = { + // The list below has been created from the table here: https://en.wikipedia.org/wiki/Unicode_block + // Remove // at start of lines below to include that unicode block, different code ranges can also be specified by + // editting the start and end of range values. Multiple lines from the list below can be included, limited only by + // the final font file size! + + // Block range, //Block name, Code points, Assigned characters, Scripts + // First, last, //Range is inclusive of first and last codes + 0x0021, 0x007E, //Basic Latin, 128, 128, Latin (52 characters), Common (76 characters) + //0x0080, 0x00FF, //Latin-1 Supplement, 128, 128, Latin (64 characters), Common (64 characters) + //0x0100, 0x017F, //Latin Extended-A, 128, 128, Latin + //0x0180, 0x024F, //Latin Extended-B, 208, 208, Latin + //0x0250, 0x02AF, //IPA Extensions, 96, 96, Latin + //0x02B0, 0x02FF, //Spacing Modifier Letters, 80, 80, Bopomofo (2 characters), Latin (14 characters), Common (64 characters) + //0x0300, 0x036F, //Combining Diacritical Marks, 112, 112, Inherited + //0x0370, 0x03FF, //Greek and Coptic, 144, 135, Coptic (14 characters), Greek (117 characters), Common (4 characters) + //0x0400, 0x04FF, //Cyrillic, 256, 256, Cyrillic (254 characters), Inherited (2 characters) + //0x0500, 0x052F, //Cyrillic Supplement, 48, 48, Cyrillic + //0x0530, 0x058F, //Armenian, 96, 89, Armenian (88 characters), Common (1 character) + //0x0590, 0x05FF, //Hebrew, 112, 87, Hebrew + //0x0600, 0x06FF, //Arabic, 256, 255, Arabic (237 characters), Common (6 characters), Inherited (12 characters) + //0x0700, 0x074F, //Syriac, 80, 77, Syriac + //0x0750, 0x077F, //Arabic Supplement, 48, 48, Arabic + //0x0780, 0x07BF, //Thaana, 64, 50, Thaana + //0x07C0, 0x07FF, //NKo, 64, 59, Nko + //0x0800, 0x083F, //Samaritan, 64, 61, Samaritan + //0x0840, 0x085F, //Mandaic, 32, 29, Mandaic + //0x0860, 0x086F, //Syriac Supplement, 16, 11, Syriac + //0x08A0, 0x08FF, //Arabic Extended-A, 96, 73, Arabic (72 characters), Common (1 character) + //0x0900, 0x097F, //Devanagari, 128, 128, Devanagari (124 characters), Common (2 characters), Inherited (2 characters) + //0x0980, 0x09FF, //Bengali, 128, 95, Bengali + //0x0A00, 0x0A7F, //Gurmukhi, 128, 79, Gurmukhi + //0x0A80, 0x0AFF, //Gujarati, 128, 91, Gujarati + //0x0B00, 0x0B7F, //Oriya, 128, 90, Oriya + //0x0B80, 0x0BFF, //Tamil, 128, 72, Tamil + //0x0C00, 0x0C7F, //Telugu, 128, 96, Telugu + //0x0C80, 0x0CFF, //Kannada, 128, 88, Kannada + //0x0D00, 0x0D7F, //Malayalam, 128, 117, Malayalam + //0x0D80, 0x0DFF, //Sinhala, 128, 90, Sinhala + //0x0E00, 0x0E7F, //Thai, 128, 87, Thai (86 characters), Common (1 character) + //0x0E80, 0x0EFF, //Lao, 128, 67, Lao + //0x0F00, 0x0FFF, //Tibetan, 256, 211, Tibetan (207 characters), Common (4 characters) + //0x1000, 0x109F, //Myanmar, 160, 160, Myanmar + //0x10A0, 0x10FF, //Georgian, 96, 88, Georgian (87 characters), Common (1 character) + //0x1100, 0x11FF, //Hangul Jamo, 256, 256, Hangul + //0x1200, 0x137F, //Ethiopic, 384, 358, Ethiopic + //0x1380, 0x139F, //Ethiopic Supplement, 32, 26, Ethiopic + //0x13A0, 0x13FF, //Cherokee, 96, 92, Cherokee + //0x1400, 0x167F, //Unified Canadian Aboriginal Syllabics, 640, 640, Canadian Aboriginal + //0x1680, 0x169F, //Ogham, 32, 29, Ogham + //0x16A0, 0x16FF, //Runic, 96, 89, Runic (86 characters), Common (3 characters) + //0x1700, 0x171F, //Tagalog, 32, 20, Tagalog + //0x1720, 0x173F, //Hanunoo, 32, 23, Hanunoo (21 characters), Common (2 characters) + //0x1740, 0x175F, //Buhid, 32, 20, Buhid + //0x1760, 0x177F, //Tagbanwa, 32, 18, Tagbanwa + //0x1780, 0x17FF, //Khmer, 128, 114, Khmer + //0x1800, 0x18AF, //Mongolian, 176, 156, Mongolian (153 characters), Common (3 characters) + //0x18B0, 0x18FF, //Unified Canadian Aboriginal Syllabics Extended, 80, 70, Canadian Aboriginal + //0x1900, 0x194F, //Limbu, 80, 68, Limbu + //0x1950, 0x197F, //Tai Le, 48, 35, Tai Le + //0x1980, 0x19DF, //New Tai Lue, 96, 83, New Tai Lue + //0x19E0, 0x19FF, //Khmer Symbols, 32, 32, Khmer + //0x1A00, 0x1A1F, //Buginese, 32, 30, Buginese + //0x1A20, 0x1AAF, //Tai Tham, 144, 127, Tai Tham + //0x1AB0, 0x1AFF, //Combining Diacritical Marks Extended, 80, 15, Inherited + //0x1B00, 0x1B7F, //Balinese, 128, 121, Balinese + //0x1B80, 0x1BBF, //Sundanese, 64, 64, Sundanese + //0x1BC0, 0x1BFF, //Batak, 64, 56, Batak + //0x1C00, 0x1C4F, //Lepcha, 80, 74, Lepcha + //0x1C50, 0x1C7F, //Ol Chiki, 48, 48, Ol Chiki + //0x1C80, 0x1C8F, //Cyrillic Extended-C, 16, 9, Cyrillic + //0x1CC0, 0x1CCF, //Sundanese Supplement, 16, 8, Sundanese + //0x1CD0, 0x1CFF, //Vedic Extensions, 48, 42, Common (15 characters), Inherited (27 characters) + //0x1D00, 0x1D7F, //Phonetic Extensions, 128, 128, Cyrillic (2 characters), Greek (15 characters), Latin (111 characters) + //0x1D80, 0x1DBF, //Phonetic Extensions Supplement, 64, 64, Greek (1 character), Latin (63 characters) + //0x1DC0, 0x1DFF, //Combining Diacritical Marks Supplement, 64, 63, Inherited + //0x1E00, 0x1EFF, //Latin Extended Additional, 256, 256, Latin + //0x1F00, 0x1FFF, //Greek Extended, 256, 233, Greek + //0x2000, 0x206F, //General Punctuation, 112, 111, Common (109 characters), Inherited (2 characters) + //0x2070, 0x209F, //Superscripts and Subscripts, 48, 42, Latin (15 characters), Common (27 characters) + //0x20A0, 0x20CF, //Currency Symbols, 48, 32, Common + //0x20D0, 0x20FF, //Combining Diacritical Marks for Symbols, 48, 33, Inherited + //0x2100, 0x214F, //Letterlike Symbols, 80, 80, Greek (1 character), Latin (4 characters), Common (75 characters) + //0x2150, 0x218F, //Number Forms, 64, 60, Latin (41 characters), Common (19 characters) + //0x2190, 0x21FF, //Arrows, 112, 112, Common + //0x2200, 0x22FF, //Mathematical Operators, 256, 256, Common + //0x2300, 0x23FF, //Miscellaneous Technical, 256, 256, Common + //0x2400, 0x243F, //Control Pictures, 64, 39, Common + //0x2440, 0x245F, //Optical Character Recognition, 32, 11, Common + //0x2460, 0x24FF, //Enclosed Alphanumerics, 160, 160, Common + //0x2500, 0x257F, //Box Drawing, 128, 128, Common + //0x2580, 0x259F, //Block Elements, 32, 32, Common + //0x25A0, 0x25FF, //Geometric Shapes, 96, 96, Common + //0x2600, 0x26FF, //Miscellaneous Symbols, 256, 256, Common + //0x2700, 0x27BF, //Dingbats, 192, 192, Common + //0x27C0, 0x27EF, //Miscellaneous Mathematical Symbols-A, 48, 48, Common + //0x27F0, 0x27FF, //Supplemental Arrows-A, 16, 16, Common + //0x2800, 0x28FF, //Braille Patterns, 256, 256, Braille + //0x2900, 0x297F, //Supplemental Arrows-B, 128, 128, Common + //0x2980, 0x29FF, //Miscellaneous Mathematical Symbols-B, 128, 128, Common + //0x2A00, 0x2AFF, //Supplemental Mathematical Operators, 256, 256, Common + //0x2B00, 0x2BFF, //Miscellaneous Symbols and Arrows, 256, 207, Common + //0x2C00, 0x2C5F, //Glagolitic, 96, 94, Glagolitic + //0x2C60, 0x2C7F, //Latin Extended-C, 32, 32, Latin + //0x2C80, 0x2CFF, //Coptic, 128, 123, Coptic + //0x2D00, 0x2D2F, //Georgian Supplement, 48, 40, Georgian + //0x2D30, 0x2D7F, //Tifinagh, 80, 59, Tifinagh + //0x2D80, 0x2DDF, //Ethiopic Extended, 96, 79, Ethiopic + //0x2DE0, 0x2DFF, //Cyrillic Extended-A, 32, 32, Cyrillic + //0x2E00, 0x2E7F, //Supplemental Punctuation, 128, 74, Common + //0x2E80, 0x2EFF, //CJK Radicals Supplement, 128, 115, Han + //0x2F00, 0x2FDF, //Kangxi Radicals, 224, 214, Han + //0x2FF0, 0x2FFF, //Ideographic Description Characters, 16, 12, Common + //0x3000, 0x303F, //CJK Symbols and Punctuation, 64, 64, Han (15 characters), Hangul (2 characters), Common (43 characters), Inherited (4 characters) + //0x3040, 0x309F, //Hiragana, 96, 93, Hiragana (89 characters), Common (2 characters), Inherited (2 characters) + //0x30A0, 0x30FF, //Katakana, 96, 96, Katakana (93 characters), Common (3 characters) + //0x3100, 0x312F, //Bopomofo, 48, 42, Bopomofo + //0x3130, 0x318F, //Hangul Compatibility Jamo, 96, 94, Hangul + //0x3190, 0x319F, //Kanbun, 16, 16, Common + //0x31A0, 0x31BF, //Bopomofo Extended, 32, 27, Bopomofo + //0x31C0, 0x31EF, //CJK Strokes, 48, 36, Common + //0x31F0, 0x31FF, //Katakana Phonetic Extensions, 16, 16, Katakana + //0x3200, 0x32FF, //Enclosed CJK Letters and Months, 256, 254, Hangul (62 characters), Katakana (47 characters), Common (145 characters) + //0x3300, 0x33FF, //CJK Compatibility, 256, 256, Katakana (88 characters), Common (168 characters) + //0x3400, 0x4DBF, //CJK Unified Ideographs Extension A, 6,592, 6,582, Han + //0x4DC0, 0x4DFF, //Yijing Hexagram Symbols, 64, 64, Common + //0x4E00, 0x9FFF, //CJK Unified Ideographs, 20,992, 20,971, Han + //0xA000, 0xA48F, //Yi Syllables, 1,168, 1,165, Yi + //0xA490, 0xA4CF, //Yi Radicals, 64, 55, Yi + //0xA4D0, 0xA4FF, //Lisu, 48, 48, Lisu + //0xA500, 0xA63F, //Vai, 320, 300, Vai + //0xA640, 0xA69F, //Cyrillic Extended-B, 96, 96, Cyrillic + //0xA6A0, 0xA6FF, //Bamum, 96, 88, Bamum + //0xA700, 0xA71F, //Modifier Tone Letters, 32, 32, Common + //0xA720, 0xA7FF, //Latin Extended-D, 224, 160, Latin (155 characters), Common (5 characters) + //0xA800, 0xA82F, //Syloti Nagri, 48, 44, Syloti Nagri + //0xA830, 0xA83F, //Common Indic Number Forms, 16, 10, Common + //0xA840, 0xA87F, //Phags-pa, 64, 56, Phags Pa + //0xA880, 0xA8DF, //Saurashtra, 96, 82, Saurashtra + //0xA8E0, 0xA8FF, //Devanagari Extended, 32, 30, Devanagari + //0xA900, 0xA92F, //Kayah Li, 48, 48, Kayah Li (47 characters), Common (1 character) + //0xA930, 0xA95F, //Rejang, 48, 37, Rejang + //0xA960, 0xA97F, //Hangul Jamo Extended-A, 32, 29, Hangul + //0xA980, 0xA9DF, //Javanese, 96, 91, Javanese (90 characters), Common (1 character) + //0xA9E0, 0xA9FF, //Myanmar Extended-B, 32, 31, Myanmar + //0xAA00, 0xAA5F, //Cham, 96, 83, Cham + //0xAA60, 0xAA7F, //Myanmar Extended-A, 32, 32, Myanmar + //0xAA80, 0xAADF, //Tai Viet, 96, 72, Tai Viet + //0xAAE0, 0xAAFF, //Meetei Mayek Extensions, 32, 23, Meetei Mayek + //0xAB00, 0xAB2F, //Ethiopic Extended-A, 48, 32, Ethiopic + //0xAB30, 0xAB6F, //Latin Extended-E, 64, 54, Latin (52 characters), Greek (1 character), Common (1 character) + //0xAB70, 0xABBF, //Cherokee Supplement, 80, 80, Cherokee + //0xABC0, 0xABFF, //Meetei Mayek, 64, 56, Meetei Mayek + //0xAC00, 0xD7AF, //Hangul Syllables, 11,184, 11,172, Hangul + //0xD7B0, 0xD7FF, //Hangul Jamo Extended-B, 80, 72, Hangul + //0xD800, 0xDB7F, //High Surrogates, 896, 0, Unknown + //0xDB80, 0xDBFF, //High Private Use Surrogates, 128, 0, Unknown + //0xDC00, 0xDFFF, //Low Surrogates, 1,024, 0, Unknown + //0xE000, 0xF8FF, //Private Use Area, 6,400, 6,400, Unknown + //0xF900, 0xFAFF, //CJK Compatibility Ideographs, 512, 472, Han + //0xFB00, 0xFB4F, //Alphabetic Presentation Forms, 80, 58, Armenian (5 characters), Hebrew (46 characters), Latin (7 characters) + //0xFB50, 0xFDFF, //Arabic Presentation Forms-A, 688, 611, Arabic (609 characters), Common (2 characters) + //0xFE00, 0xFE0F, //Variation Selectors, 16, 16, Inherited + //0xFE10, 0xFE1F, //Vertical Forms, 16, 10, Common + //0xFE20, 0xFE2F, //Combining Half Marks, 16, 16, Cyrillic (2 characters), Inherited (14 characters) + //0xFE30, 0xFE4F, //CJK Compatibility Forms, 32, 32, Common + //0xFE50, 0xFE6F, //Small Form Variants, 32, 26, Common + //0xFE70, 0xFEFF, //Arabic Presentation Forms-B, 144, 141, Arabic (140 characters), Common (1 character) + //0xFF00, 0xFFEF, //Halfwidth and Fullwidth Forms, 240, 225, Hangul (52 characters), Katakana (55 characters), Latin (52 characters), Common (66 characters) + //0xFFF0, 0xFFFF, //Specials, 16, 5, Common + + //0x0030, 0x0039, //Example custom range (numbers 0-9) + //0x0041, 0x005A, //Example custom range (Upper case A-Z) + //0x0061, 0x007A, //Example custom range (Lower case a-z) +}; + +// Here we specify specific individual Unicodes to be included (appended at end of selected range) +static final int[] specificUnicodes = { + + // Commonly used codes, add or remove // in next line + // 0x00A3, 0x00B0, 0x00B5, 0x03A9, 0x20AC, // £ ° µ Ω € + + // Numbers and characters for showing time, change next line to //* to use + /* + 0x002B, 0x002D, 0x002E, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, // - + . 0 1 2 3 4 + 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x0061, 0x006D, // 5 6 7 8 9 : a m + 0x0070, // p + //*/ + + // More characters, change next line to //* to use + /* + 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x010C, 0x010D, + 0x010E, 0x010F, 0x0110, 0x0111, 0x0118, 0x0119, 0x011A, 0x011B, + + 0x0131, 0x0139, 0x013A, 0x013D, 0x013E, 0x0141, 0x0142, 0x0143, + 0x0144, 0x0147, 0x0148, 0x0150, 0x0151, 0x0152, 0x0153, 0x0154, + 0x0155, 0x0158, 0x0159, 0x015A, 0x015B, 0x015E, 0x015F, 0x0160, + 0x0161, 0x0162, 0x0163, 0x0164, 0x0165, 0x016E, 0x016F, 0x0170, + 0x0171, 0x0178, 0x0179, 0x017A, 0x017B, 0x017C, 0x017D, 0x017E, + 0x0192, + + 0x02C6, 0x02C7, 0x02D8, 0x02D9, 0x02DA, 0x02DB, 0x02DC, 0x02DD, + 0x03A9, 0x03C0, 0x2013, 0x2014, 0x2018, 0x2019, 0x201A, 0x201C, + 0x201D, 0x201E, 0x2020, 0x2021, 0x2022, 0x2026, 0x2030, 0x2039, + 0x203A, 0x2044, 0x20AC, + + 0x2122, 0x2202, 0x2206, 0x220F, + + 0x2211, 0x221A, 0x221E, 0x222B, 0x2248, 0x2260, 0x2264, 0x2265, + 0x25CA, + + 0xF8FF, 0xFB01, 0xFB02, + //*/ +}; + + +// >>>>>>>>>> USER CONFIGURED PARAMETERS END HERE <<<<<<<<<< +//////////////////////////////////////////////////////////////////////////////////////////////// + +// Variable to hold the inclusive Unicode range (16 bit values only for this sketch) +int firstUnicode = 0; +int lastUnicode = 0; + +PFont myFont; + +void setup() { + + size(1000, 800); + + // Print the available fonts to the console as a list: + String[] fontList = PFont.list(); + printArray(fontList); + + // Set the fontName from the array number or the defined fontName + if (fontNumber >= 0) + { + fontName = fontList[fontNumber]; + fontType = ""; + } + + char[] charset; + int index = 0, count = 0; + + int blockCount = unicodeBlocks.length; + + for (int i = 0; i < blockCount; i+=2) { + firstUnicode = unicodeBlocks[i]; + lastUnicode = unicodeBlocks[i+1]; + if (lastUnicode < firstUnicode) { + delay(100); + System.err.println("ERROR: Bad Unicode range secified, last < first!"); + System.err.print("first in range = 0x" + hex(firstUnicode, 4)); + System.err.println(", last in range = 0x" + hex(lastUnicode, 4)); + while (true); + } + // calculate the number of characters + count += (lastUnicode - firstUnicode + 1); + } + + count += specificUnicodes.length; + + println(); + println("====================="); + println("Creating font file..."); + println("Unicode blocks included = " + (blockCount/2)); + println("Specific unicodes included = " + specificUnicodes.length); + println("Total number of characters = " + count); + + if (count == 0) { + delay(100); + System.err.println("ERROR: No Unicode range or specific codes have been defined!"); + while (true); + } + + // allocate memory + charset = new char[count]; + + for (int i = 0; i < blockCount; i+=2) { + firstUnicode = unicodeBlocks[i]; + lastUnicode = unicodeBlocks[i+1]; + + // loading the range specified + for (int code = firstUnicode; code <= lastUnicode; code++) { + charset[index] = Character.toChars(code)[0]; + index++; + } + } + + // loading the range specified + for (int i = 0; i < specificUnicodes.length; i++) { + charset[index] = Character.toChars(specificUnicodes[i])[0]; + index++; + } + // Create the font in memory + myFont = createFont(fontName+fontType, 32, true, charset); + + // Print a few characters to the sketch window + fill(0, 0, 0); + textFont(myFont); + + // Set the left and top margin + int margin = displayFontSize; + translate(margin/2, margin); + + int gapx = displayFontSize*10/8; + int gapy = displayFontSize*10/8; + index = 0; + fill(0); + + textSize(displayFontSize); + + for (int y = 0; y < height-gapy; y += gapy) { + int x = 0; + while (x < width) { + + int unicode = charset[index]; + float cwidth = textWidth((char)unicode) + 2; + if ( (x + cwidth) > (width - gapx) ) break; + + // Draw the letter to the screen + text(new String(Character.toChars(unicode)), x, y); + + // Move cursor + x += cwidth; + // Increment the counter + index++; + if (index >= count) break; + } + if (index >= count) break; + } + + + // creating font + PFont font; + + font = createFont(fontName+fontType, fontSize, true, charset); + + println("Created font " + fontName + ".vlw"); + + // creating file + try { + print("Saving to sketch FontFiles folder... "); + + OutputStream output = createOutput("FontFiles/" + fontName + str(fontSize) + ".vlw"); + font.save(output); + output.close(); + + println("OK!"); + + delay(100); + + // Open up the FontFiles folder to access the saved file + String path = sketchPath(); + Desktop.getDesktop().open(new File(path+"/FontFiles")); + + System.err.println("All done! Note: Rectangles are displayed for non-existant characters."); + } + catch(IOException e) { + println("Doh! Failed to create the file"); + } +} \ No newline at end of file diff --git a/Tools/Create_Smooth_Font/Create_font_5/data/Final-Frontier.ttf b/Tools/Create_Smooth_Font/Create_font_5/data/Final-Frontier.ttf new file mode 100644 index 0000000..823b9a5 Binary files /dev/null and b/Tools/Create_Smooth_Font/Create_font_5/data/Final-Frontier.ttf differ diff --git a/Tools/PlatformIO/Configuring options.txt b/Tools/PlatformIO/Configuring options.txt new file mode 100644 index 0000000..3d0a02c --- /dev/null +++ b/Tools/PlatformIO/Configuring options.txt @@ -0,0 +1,33 @@ +PlatformIO User notes: + +It is possible to load settings from the calling program rather than modifying +the library for each project by modifying the "platformio.ini" file. + +The User_Setup_Select.h file will not load the user setting header files if +USER_SETUP_LOADED is defined. + +Instead of using #define, use the -D prefix, for example: + +[env:esp32dev] +platform = https://github.com/platformio/platform-espressif32.git#feature/stage +board = esp32dev +framework = arduino +upload_port = ESP32-Test-2481CE9C.local + +build_flags = + -Os + -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG + -DUSER_SETUP_LOADED=1 + -DILI9163_DRIVER=1 + -DTFT_WIDTH=128 + -DTFT_HEIGHT=160 + -DTFT_MISO=19 + -DTFT_MOSI=23 + -DTFT_SCLK=18 + -DTFT_CS=5 + -DTFT_DC=19 + -DTFT_RST=-1 + -DLOAD_GLCD=1 + -DSPI_FREQUENCY=27000000 + +lib_extra_dirs = B:\Projects\ESP32\ESP32Lib diff --git a/User_Setup.h b/User_Setup.h index 3b635f9..98f7d50 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -88,7 +88,7 @@ #define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V -//#define TOUCH_CS PIN_D1 // Chip select pin (T_CS) of touch screen +#define TOUCH_CS PIN_D2 // Chip select pin (T_CS) of touch screen //#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only @@ -164,10 +164,14 @@ #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters #define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm -#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. +#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + // ################################################################################## // // Section 4. Not used @@ -192,8 +196,8 @@ // #define SPI_FREQUENCY 5000000 // #define SPI_FREQUENCY 10000000 // #define SPI_FREQUENCY 20000000 -#define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 -// #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS +//#define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 + #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS // #define SPI_FREQUENCY 80000000 // The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: @@ -210,4 +214,4 @@ // Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) // so changing it here has no effect -// #define SUPPORT_TRANSACTIONS + #define SUPPORT_TRANSACTIONS diff --git a/User_Setup_Select.h b/User_Setup_Select.h index 739db4c..93a0ac8 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -9,7 +9,7 @@ // changes being needed. It also improves the portability of users sketches to other // hardware configurations and compatible libraries. // -// Create a shortcut to this file on your desktop to permit quick access for editting. +// Create a shortcut to this file on your desktop to permit quick access for editing. // Re-compile and upload after making and saving any changes to this file. // Customised User_Setup files are stored in the "User_Setups" folder. @@ -34,6 +34,8 @@ //#include // Setup file configured for my stock RPi TFT with touch //#include // Setup file for the ESP32 based M5Stack +//#include + //#include // Setup file template for copying/editting diff --git a/examples/Smooth Fonts/Print_Smooth_Font/Print_Smooth_Font.ino b/examples/Smooth Fonts/Print_Smooth_Font/Print_Smooth_Font.ino new file mode 100644 index 0000000..10f9197 --- /dev/null +++ b/examples/Smooth Fonts/Print_Smooth_Font/Print_Smooth_Font.ino @@ -0,0 +1,195 @@ +/* + Sketch to demonstrate using the print class with smooth fonts + + Sketch is writtent for a 240 x 320 display + + Load the font file into SPIFFS first by using the Arduino IDE + Sketch Data Upload menu option. Font files must be stored in the + sketch data folder (Ctrl+k to view). + https://github.com/esp8266/arduino-esp8266fs-plugin + https://github.com/me-no-dev/arduino-esp32fs-plugin + + New font files in the .vlw format can be created using the Processing + sketch in the library Tools folder. The Processing sketch can convert + TrueType fonts in *.ttf or *.otf files. + + Note: SPIFFS does not accept an underscore _ in filenames! + + The library supports 16 bit unicode characters: + https://en.wikipedia.org/wiki/Unicode_font + + The characters supported are in the in the Basic Multilingal Plane: + https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane + + Make sure all the display driver and pin connenctions 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 ###### + ######################################################################### +*/ + +// Font file is stored in SPIFFS +#define FS_NO_GLOBALS +#include + +// Graphics and font library +#include +#include + +TFT_eSPI tft = TFT_eSPI(); // Invoke library + +// ------------------------------------------------------------------------- +// Setup +// ------------------------------------------------------------------------- +void setup(void) { + Serial.begin(115200); // Used for messages + + tft.init(); + tft.setRotation(1); + + if (!SPIFFS.begin()) { + Serial.println("SPIFFS initialisation failed!"); + while (1) yield(); // Stay here twiddling thumbs waiting + } + Serial.println("\r\nInitialisation done."); + + listFiles(); // Lists the files so you can see what is in the SPIFFS + +} + +// ------------------------------------------------------------------------- +// Main loop +// ------------------------------------------------------------------------- +void loop() { + // Wrap test at right and bottom of screen + tft.setTextWrap(true, true); + + // Name of font file (library adds leading / and .vlw) + String fileName = "Final-Frontier-28"; + + // Font and background colour, background colour is used for anti-alias blending + tft.setTextColor(TFT_WHITE, TFT_BLACK); + + // Load the font + tft.loadFont(fileName); + + // Display all characters of the font + tft.showFont(2000); + + // Set "cursor" at top left corner of display (0,0) + // (cursor will move to next line automatically during printing with 'tft.println' + // or stay on the line is there is room for the text with tft.print) + tft.setCursor(0, 0); + + // Set the font colour to be white with a black background, set text size multiplier to 1 + tft.setTextColor(TFT_WHITE, TFT_BLACK); + + // We can now plot text on screen using the "print" class + tft.println("Hello World!"); + + // Set the font colour to be yellow + tft.setTextColor(TFT_YELLOW, TFT_BLACK); + tft.println(1234.56); + + // Set the font colour to be red + tft.setTextColor(TFT_RED, TFT_BLACK); + tft.println((uint32_t)3735928559, HEX); // Should print DEADBEEF + + // Set the font colour to be green with black background + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.println("Anti-aliased font!"); + tft.println(""); + + // Test some print formatting functions + float fnumber = 123.45; + + // Set the font colour to be blue + tft.setTextColor(TFT_BLUE, TFT_BLACK); + tft.print("Float = "); tft.println(fnumber); // Print floating point number + tft.print("Binary = "); tft.println((int)fnumber, BIN); // Print as integer value in binary + tft.print("Hexadecimal = "); tft.println((int)fnumber, HEX); // Print as integer number in Hexadecimal + + // Unload the font to recover used RAM + tft.unloadFont(); + + delay(10000); +} + + +// ------------------------------------------------------------------------- +// List files in ESP8266 or ESP32 SPIFFS memory +// ------------------------------------------------------------------------- +void listFiles(void) { + Serial.println(); + Serial.println("SPIFFS files found:"); + +#ifdef ESP32 + listDir(SPIFFS, "/", true); +#else + fs::Dir dir = SPIFFS.openDir("/"); // Root directory + String line = "====================================="; + + Serial.println(line); + Serial.println(" File name Size"); + Serial.println(line); + + while (dir.next()) { + String fileName = dir.fileName(); + Serial.print(fileName); + int spaces = 25 - fileName.length(); // Tabulate nicely + if (spaces < 0) spaces = 1; + while (spaces--) Serial.print(" "); + fs::File f = dir.openFile("r"); + Serial.print(f.size()); Serial.println(" bytes"); + yield(); + } + + Serial.println(line); +#endif + Serial.println(); + delay(1000); +} + +#ifdef ESP32 +void listDir(fs::FS &fs, const char * dirname, uint8_t levels) { + Serial.printf("Listing directory: %s\n", dirname); + + fs::File root = fs.open(dirname); + if (!root) { + Serial.println("Failed to open directory"); + return; + } + if (!root.isDirectory()) { + Serial.println("Not a directory"); + return; + } + + fs::File file = root.openNextFile(); + while (file) { + + if (file.isDirectory()) { + Serial.print("DIR : "); + String fileName = file.name(); + Serial.print(fileName); + if (levels) { + listDir(fs, file.name(), levels - 1); + } + } else { + String fileName = file.name(); + Serial.print(" " + fileName); + int spaces = 32 - fileName.length(); // Tabulate nicely + if (spaces < 1) spaces = 1; + while (spaces--) Serial.print(" "); + String fileSize = (String) file.size(); + spaces = 8 - fileSize.length(); // Tabulate nicely + if (spaces < 1) spaces = 1; + while (spaces--) Serial.print(" "); + Serial.println(fileSize + " bytes"); + } + + file = root.openNextFile(); + } +} +#endif +// ------------------------------------------------------------------------- diff --git a/examples/Smooth Fonts/Print_Smooth_Font/data/Final-Frontier-28.vlw b/examples/Smooth Fonts/Print_Smooth_Font/data/Final-Frontier-28.vlw new file mode 100644 index 0000000..2872fd5 Binary files /dev/null and b/examples/Smooth Fonts/Print_Smooth_Font/data/Final-Frontier-28.vlw differ diff --git a/examples/Smooth Fonts/Unicode_test/SPIFFS_functions.ino b/examples/Smooth Fonts/Unicode_test/SPIFFS_functions.ino new file mode 100644 index 0000000..1415556 --- /dev/null +++ b/examples/Smooth Fonts/Unicode_test/SPIFFS_functions.ino @@ -0,0 +1,83 @@ +/*==================================================================================== + This sketch supports for the ESP6266 and ESP32 SPIFFS filing system + + Created by Bodmer 15th Jan 2017 + ==================================================================================*/ + +//==================================================================================== +// Print a SPIFFS directory list (root directory) +//==================================================================================== + +void listFiles(void) { + Serial.println(); + Serial.println("SPIFFS files found:"); + +#ifdef ESP32 + listDir(SPIFFS, "/", true); +#else + fs::Dir dir = SPIFFS.openDir("/"); // Root directory + String line = "====================================="; + + Serial.println(line); + Serial.println(" File name Size"); + Serial.println(line); + + while (dir.next()) { + String fileName = dir.fileName(); + Serial.print(fileName); + int spaces = 25 - fileName.length(); // Tabulate nicely + if (spaces < 0) spaces = 1; + while (spaces--) Serial.print(" "); + fs::File f = dir.openFile("r"); + Serial.print(f.size()); Serial.println(" bytes"); + yield(); + } + + Serial.println(line); +#endif + Serial.println(); + delay(1000); +} +//==================================================================================== + +#ifdef ESP32 +void listDir(fs::FS &fs, const char * dirname, uint8_t levels) { + Serial.printf("Listing directory: %s\n", dirname); + + fs::File root = fs.open(dirname); + if (!root) { + Serial.println("Failed to open directory"); + return; + } + if (!root.isDirectory()) { + Serial.println("Not a directory"); + return; + } + + fs::File file = root.openNextFile(); + while (file) { + + if (file.isDirectory()) { + Serial.print("DIR : "); + String fileName = file.name(); + Serial.print(fileName); + if (levels) { + listDir(fs, file.name(), levels - 1); + } + } else { + String fileName = file.name(); + Serial.print(" " + fileName); + int spaces = 32 - fileName.length(); // Tabulate nicely + if (spaces < 1) spaces = 1; + while (spaces--) Serial.print(" "); + String fileSize = (String) file.size(); + spaces = 8 - fileSize.length(); // Tabulate nicely + if (spaces < 1) spaces = 1; + while (spaces--) Serial.print(" "); + Serial.println(fileSize + " bytes"); + } + + file = root.openNextFile(); + } +} +#endif diff --git a/examples/Smooth Fonts/Unicode_test/Unicode_test.ino b/examples/Smooth Fonts/Unicode_test/Unicode_test.ino new file mode 100644 index 0000000..aac4926 --- /dev/null +++ b/examples/Smooth Fonts/Unicode_test/Unicode_test.ino @@ -0,0 +1,148 @@ +// Created by Bodmer 24th Jan 2017 - Tested in Arduino IDE 1.8.5 esp8266 Core 2.4.0 + +// The latest Arduino IDE versions support UTF-8 encoding of Unicode characters +// within sketches: +// https://playground.arduino.cc/Code/UTF-8 + +/* + The library expects strings to be in UTF-8 encoded format: + https://www.fileformat.info/info/unicode/utf8.htm + + Creating varaibles needs to be done with care when using character arrays: + char c = 'µ'; // Wrong + char bad[4] = "5µA"; // Wrong + char good[] = "5µA"; // Good + String okay = "5µA"; // Good + + This is because UTF-8 characters outside the basic Latin set occupy more than + 1 byte per character! A 16 bit unicode character occupies 3 bytes! + +*/ + +//==================================================================================== +// Libraries +//==================================================================================== +// Call up the SPIFFS FLASH filing system this is part of the ESP Core + +#include // Hardware-specific library + +TFT_eSPI tft = TFT_eSPI(); // Invoke custom library + +uint16_t bg = TFT_BLACK; +uint16_t fg = TFT_WHITE; + + +//==================================================================================== +// Setup +//==================================================================================== +void setup() +{ + Serial.begin(115200); // Used for messages and the C array generator + + Serial.println("NodeMCU vlw font test!"); + + if (!SPIFFS.begin()) { + Serial.println("SPIFFS initialisation failed!"); + while (1) yield(); // Stay here twiddling thumbs waiting + } + Serial.println("\r\nInitialisation done."); + + listFiles(); // Lists the files so you can see what is in the SPIFFS + + tft.begin(); + tft.setRotation(0); // portrait + + fg = TFT_WHITE; + bg = TFT_BLACK; +} + +//==================================================================================== +// Loop +//==================================================================================== +void loop() +{ + tft.setTextColor(fg, bg); + + //---------------------------------------------------------------------------- + // Anti-aliased font test + + String test1 = "Hello World"; + + // Load a smooth font from SPIFFS + tft.loadFont("Final-Frontier-28"); + + tft.setRotation(0); + + // Show all characters on screen with 2 second (2000ms) delay between screens + tft.showFont(2000); // Note: This function moves the cursor position! + + tft.fillScreen(bg); + tft.setCursor(0,0); + + tft.println(test1); + + // Remove font parameters from memory to recover RAM + tft.unloadFont(); + + delay(2000); + + //---------------------------------------------------------------------------- + // We can have any random mix of characters in the font + + String test2 = "仝倀"; // Unicodes 0x4EDD, 0x5000 + + tft.loadFont("Unicode-Test-72"); + + tft.setRotation(1); + + // Show all characters on screen with 2 second (2000ms) delay between screens + tft.showFont(2000); // Note: This function moves the cursor position! + + tft.fillScreen(bg); + tft.setCursor(0,0); + + tft.setTextColor(TFT_CYAN, bg); + tft.println(test2); + + tft.setTextColor(TFT_YELLOW, bg); + tft.println("12:00pm"); + + tft.setTextColor(TFT_MAGENTA, bg); + tft.println("1000Ω"); + + // Remove font parameters from memory to recover RAM + tft.unloadFont(); + + delay(2000); + + //---------------------------------------------------------------------------- + // Latin and Hiragana font mix + + String test3 = "こんにちは"; + + tft.loadFont("Latin-Hiragana-24"); + + tft.setRotation(0); + + // Show all characters on screen with 2 second (2000ms) delay between screens + tft.showFont(2000); // Note: This function moves the cursor position! + + tft.fillScreen(bg); + tft.setTextColor(TFT_GREEN, bg); + tft.setCursor(0,0); + + tft.println("Konnichiwa"); + tft.println(test3); + tft.println(); + tft.println("Sayonara"); + tft.println("さようなら"); // Sayonara + + // Remove font parameters from memory to recover RAM + tft.unloadFont(); + + delay(2000); + // + //---------------------------------------------------------------------------- +} +//==================================================================================== + diff --git a/examples/Smooth Fonts/Unicode_test/data/Final-Frontier-28.vlw b/examples/Smooth Fonts/Unicode_test/data/Final-Frontier-28.vlw new file mode 100644 index 0000000..2872fd5 Binary files /dev/null and b/examples/Smooth Fonts/Unicode_test/data/Final-Frontier-28.vlw differ diff --git a/examples/Smooth Fonts/Unicode_test/data/Latin-Hiragana-24.vlw b/examples/Smooth Fonts/Unicode_test/data/Latin-Hiragana-24.vlw new file mode 100644 index 0000000..b2f128b Binary files /dev/null and b/examples/Smooth Fonts/Unicode_test/data/Latin-Hiragana-24.vlw differ diff --git a/examples/Smooth Fonts/Unicode_test/data/Unicode-Test-72.vlw b/examples/Smooth Fonts/Unicode_test/data/Unicode-Test-72.vlw new file mode 100644 index 0000000..c475756 Binary files /dev/null and b/examples/Smooth Fonts/Unicode_test/data/Unicode-Test-72.vlw differ diff --git a/examples/Smooth Fonts/alphaBlend_Test/alphaBlend_Test.ino b/examples/Smooth Fonts/alphaBlend_Test/alphaBlend_Test.ino new file mode 100644 index 0000000..f3f4cdf --- /dev/null +++ b/examples/Smooth Fonts/alphaBlend_Test/alphaBlend_Test.ino @@ -0,0 +1,194 @@ +/* + 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 // 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; +} + + diff --git a/keywords.txt b/keywords.txt index 13748fc..5d4f945 100644 --- a/keywords.txt +++ b/keywords.txt @@ -56,7 +56,8 @@ getRotation KEYWORD2 getTextDatum KEYWORD2 fontsLoaded KEYWORD2 color565 KEYWORD2 -color332 KEYWORD2 +color16to8 KEYWORD2 +color8to16 KEYWORD2 drawNumber KEYWORD2 drawFloat KEYWORD2 drawString KEYWORD2 @@ -98,3 +99,8 @@ pushBitmap KEYWORD2 pushSprite KEYWORD2 setScrollRect KEYWORD2 scroll KEYWORD2 + +alphaBlend KEYWORD2 +showFont KEYWORD2 +loadFont KEYWORD2 +unloadFont KEYWORD2 diff --git a/library.json b/library.json index 2041a92..fd60348 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.18.17", + "version": "0.18.20", "keywords": "tft, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266 and ESP32", "repository": diff --git a/library.properties b/library.properties index e235cd1..a70b692 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.18.17 +version=0.18.20 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE diff --git a/license.txt b/license.txt index befa9a4..8fe1fb4 100644 --- a/license.txt +++ b/license.txt @@ -28,7 +28,7 @@ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvStartvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv Selected functions from the Adafruit_GFX library (as it was in 2015) have been imported into the TFT_eSPI.cpp file and modified to improve -perfromance, add features and make them compatible with the ESP8266 and +performance, add features and make them compatible with the ESP8266 and ESP32. The fonts from the Adafruit_GFX and Button functions were added later. @@ -68,7 +68,7 @@ POSSIBILITY OF SUCH DAMAGE. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^End^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Due to the evolution of the TFT_eSPI library the orignal code may no longer +Due to the evolution of the TFT_eSPI library the original code may no longer be recognisable, however in most cases the function names can be used as a reference point since the aim is to retain a level of compatibility with the popular Adafruit_GFX graphics functions.