diff --git a/Extensions/Button.cpp b/Extensions/Button.cpp index fa53197..9467d63 100644 --- a/Extensions/Button.cpp +++ b/Extensions/Button.cpp @@ -68,7 +68,7 @@ void TFT_eSPI_Button::drawButton(bool inverted, String long_name) { uint8_t tempdatum = _gfx->getTextDatum(); _gfx->setTextDatum(_textdatum); - uint16_t tempPadding = _gfx->padX; + uint16_t tempPadding = _gfx->getTextPadding(); _gfx->setTextPadding(0); if (long_name == "") diff --git a/Extensions/Smooth_font.cpp b/Extensions/Smooth_font.cpp index e431cc2..fa94c59 100644 --- a/Extensions/Smooth_font.cpp +++ b/Extensions/Smooth_font.cpp @@ -373,7 +373,7 @@ void TFT_eSPI::drawGlyph(uint16_t code) if (code == '\n') { cursor_x = 0; cursor_y += gFont.yAdvance; - if (cursor_y >= _height) cursor_y = 0; + if (textwrapY && (cursor_y >= height())) cursor_y = 0; return; } } diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index 088a54d..e377013 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -21,9 +21,10 @@ TFT_eSprite::TFT_eSprite(TFT_eSPI *tft) _iwidth = 0; // Initialise width and height to 0 (it does not exist yet) _iheight = 0; _bpp = 16; - _iswapBytes = false; // Do not swap pushImage colour bytes by default + _swapBytes = false; // Do not swap pushImage colour bytes by default _created = false; + _vpOoB = true; _xs = 0; // window bounds for pushColor _ys = 0; @@ -33,14 +34,9 @@ TFT_eSprite::TFT_eSprite(TFT_eSPI *tft) _xptr = 0; // pushColor coordinate _yptr = 0; - _xPivot = 0; - _yPivot = 0; - _colorMap = nullptr; - this->cursor_y = this->cursor_x = 0; // Text cursor position - - this->_psram_enable = true; + _psram_enable = true; } @@ -59,8 +55,8 @@ void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames) _iwidth = _dwidth = _bitwidth = w; _iheight = _dheight = h; - this->cursor_x = 0; - this->cursor_y = 0; + cursor_x = 0; + cursor_y = 0; // Default scroll rectangle and gap fill colour _sx = 0; @@ -69,9 +65,6 @@ void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames) _sh = h; _scolor = TFT_BLACK; - _xPivot = w/2; - _yPivot = h/2; - _img8 = (uint8_t*) callocSprite(w, h, frames); _img8_1 = _img8; _img8_2 = _img8; @@ -105,6 +98,9 @@ void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames) if (_img8) { _created = true; + rotation = 0; + setViewport(0, 0, _dwidth, _dheight); + setPivot(_iwidth/2, _iheight/2); return _img8_1; } @@ -142,7 +138,7 @@ TFT_eSprite::~TFT_eSprite(void) deleteSprite(); #ifdef SMOOTH_FONT - if(this->fontLoaded) this->unloadFont(); + if(fontLoaded) unloadFont(); #endif } @@ -151,7 +147,6 @@ TFT_eSprite::~TFT_eSprite(void) ** Function name: callocSprite ** Description: Allocate a memory area for the Sprite and return pointer ***************************************************************************************/ - void* TFT_eSprite::callocSprite(int16_t w, int16_t h, uint8_t frames) { // Add one extra "off screen" pixel to point out-of-bounds setWindow() coordinates @@ -165,7 +160,7 @@ void* TFT_eSprite::callocSprite(int16_t w, int16_t h, uint8_t frames) if (_bpp == 16) { #if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) - if ( psramFound() && this->_psram_enable && !_tft->DMA_Enabled) + if ( psramFound() && _psram_enable && !_tft->DMA_Enabled) { ptr8 = ( uint8_t*) ps_calloc(frames * w * h + frames, sizeof(uint16_t)); //Serial.println("PSRAM"); @@ -181,7 +176,7 @@ void* TFT_eSprite::callocSprite(int16_t w, int16_t h, uint8_t frames) else if (_bpp == 8) { #if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) - if ( psramFound() && this->_psram_enable ) ptr8 = ( uint8_t*) ps_calloc(frames * w * h + frames, sizeof(uint8_t)); + if ( psramFound() && _psram_enable ) ptr8 = ( uint8_t*) ps_calloc(frames * w * h + frames, sizeof(uint8_t)); else #endif ptr8 = ( uint8_t*) calloc(frames * w * h + frames, sizeof(uint8_t)); @@ -192,7 +187,7 @@ void* TFT_eSprite::callocSprite(int16_t w, int16_t h, uint8_t frames) w = (w+1) & 0xFFFE; // width needs to be multiple of 2, with an extra "off screen" pixel _iwidth = w; #if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) - if ( psramFound() && this->_psram_enable ) ptr8 = ( uint8_t*) ps_calloc(((frames * w * h) >> 1) + frames, sizeof(uint8_t)); + if ( psramFound() && _psram_enable ) ptr8 = ( uint8_t*) ps_calloc(((frames * w * h) >> 1) + frames, sizeof(uint8_t)); else #endif ptr8 = ( uint8_t*) calloc(((frames * w * h) >> 1) + frames, sizeof(uint8_t)); @@ -206,10 +201,10 @@ void* TFT_eSprite::callocSprite(int16_t w, int16_t h, uint8_t frames) w = (w+7) & 0xFFF8; // width should be the multiple of 8 bits to be compatible with epdpaint _iwidth = w; // _iwidth is rounded up to be multiple of 8, so might not be = _dwidth - _bitwidth = w; + _bitwidth = w; // _bitwidth will not be rotated whereas _iwidth may be #if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) - if ( psramFound() && this->_psram_enable ) ptr8 = ( uint8_t*) ps_calloc(frames * (w>>3) * h + frames, sizeof(uint8_t)); + if ( psramFound() && _psram_enable ) ptr8 = ( uint8_t*) ps_calloc(frames * (w>>3) * h + frames, sizeof(uint8_t)); else #endif ptr8 = ( uint8_t*) calloc(frames * (w>>3) * h + frames, sizeof(uint8_t)); @@ -218,11 +213,11 @@ void* TFT_eSprite::callocSprite(int16_t w, int16_t h, uint8_t frames) return ptr8; } + /*************************************************************************************** ** Function name: createPalette (from RAM array) ** Description: Set a palette for a 4-bit per pixel sprite ***************************************************************************************/ - void TFT_eSprite::createPalette(uint16_t colorMap[], uint8_t colors) { if (_colorMap != nullptr) @@ -249,11 +244,11 @@ void TFT_eSprite::createPalette(uint16_t colorMap[], uint8_t colors) } } + /*************************************************************************************** ** Function name: createPalette (from FLASH array) ** Description: Set a palette for a 4-bit per pixel sprite ***************************************************************************************/ - void TFT_eSprite::createPalette(const uint16_t colorMap[], uint8_t colors) { if (colorMap == nullptr) @@ -274,6 +269,7 @@ void TFT_eSprite::createPalette(const uint16_t colorMap[], uint8_t colors) } } + /*************************************************************************************** ** Function name: frameBuffer ** Description: For 1 bpp Sprites, select the frame used for graphics @@ -295,11 +291,11 @@ void* TFT_eSprite::frameBuffer(int8_t f) return _img8; } + /*************************************************************************************** ** Function name: setColorDepth ** Description: Set bits per pixel for colour (1, 8 or 16) ***************************************************************************************/ - void* TFT_eSprite::setColorDepth(int8_t b) { // Do not re-create the sprite if the colour depth does not change @@ -329,7 +325,6 @@ void* TFT_eSprite::setColorDepth(int8_t b) ** Function name: getColorDepth ** Description: Get bits per pixel for colour (1, 8 or 16) ***************************************************************************************/ - int8_t TFT_eSprite::getColorDepth(void) { if (_created) return _bpp; @@ -348,6 +343,7 @@ void TFT_eSprite::setBitmapColor(uint16_t c, uint16_t b) _tft->bitmap_bg = b; } + /*************************************************************************************** ** Function name: setPaletteColor ** Description: Set the 4bpp palette color at the given index @@ -359,6 +355,7 @@ void TFT_eSprite::setPaletteColor(uint8_t index, uint16_t color) _colorMap[index] = color; } + /*************************************************************************************** ** Function name: getPaletteColor ** Description: Return the palette color at 4bpp index, or 0 on error. @@ -370,6 +367,7 @@ uint16_t TFT_eSprite::getPaletteColor(uint8_t index) return _colorMap[index]; } + /*************************************************************************************** ** Function name: deleteSprite ** Description: Delete the sprite to free up memory (RAM) @@ -386,42 +384,12 @@ void TFT_eSprite::deleteSprite(void) { free(_img8_1); _img8 = nullptr; - _created = false; + _created = false; + _vpOoB = true; // TFT_eSPI class write() uses this to check for valid sprite } } -/*************************************************************************************** -** Function name: setPivot -** Description: Set the pivot point in this Sprite -***************************************************************************************/ -void TFT_eSprite::setPivot(int16_t x, int16_t y) -{ - _xPivot = x; - _yPivot = y; -} - - -/*************************************************************************************** -** Function name: getPivotX -** Description: Get the x pivot position -***************************************************************************************/ -int16_t TFT_eSprite::getPivotX(void) -{ - return _xPivot; -} - - -/*************************************************************************************** -** Function name: getPivotY -** Description: Get the y pivot position -***************************************************************************************/ -int16_t TFT_eSprite::getPivotY(void) -{ - return _yPivot; -} - - /*************************************************************************************** ** Function name: pushRotated - Fast fixed point integer maths version ** Description: Push rotated Sprite to TFT screen @@ -429,7 +397,7 @@ int16_t TFT_eSprite::getPivotY(void) #define FP_SCALE 10 bool TFT_eSprite::pushRotated(int16_t angle, int32_t transp) { - if ( !_created) return false; + if ( !_created || _tft->_vpOoB) return false; // Bounding box parameters int16_t min_x; @@ -442,8 +410,8 @@ bool TFT_eSprite::pushRotated(int16_t angle, int32_t transp) uint16_t sline_buffer[max_x - min_x + 1]; - int32_t xt = min_x - _tft->_xpivot; - int32_t yt = min_y - _tft->_ypivot; + int32_t xt = min_x - _tft->_xPivot; + int32_t yt = min_y - _tft->_yPivot; uint32_t xe = _dwidth << FP_SCALE; uint32_t ye = _dheight << FP_SCALE; uint16_t tpcolor = transp; // convert to unsigned @@ -566,22 +534,22 @@ bool TFT_eSprite::getRotatedBounds(int16_t angle, int16_t *min_x, int16_t *min_y getRotatedBounds(angle, width(), height(), _xPivot, _yPivot, min_x, min_y, max_x, max_y); // Move bounding box so source Sprite pivot coincides with TFT pivot - *min_x += _tft->_xpivot; - *max_x += _tft->_xpivot; - *min_y += _tft->_ypivot; - *max_y += _tft->_ypivot; + *min_x += _tft->_xPivot; + *max_x += _tft->_xPivot; + *min_y += _tft->_yPivot; + *max_y += _tft->_yPivot; - // Return if bounding box is outside of TFT area - if (*min_x > _tft->width()) return false; - if (*min_y > _tft->height()) return false; - if (*max_x < 0) return false; - if (*max_y < 0) return false; + // Return if bounding box is outside of TFT viewport + if (*min_x > _tft->_vpW) return false; + if (*min_y > _tft->_vpH) return false; + if (*max_x < _tft->_vpX) return false; + if (*max_y < _tft->_vpY) return false; - // Clip bounding box to be within TFT area - if (*min_x < 0) *min_x = 0; - if (*min_y < 0) *min_y = 0; - if (*max_x > _tft->width()) *max_x = _tft->width(); - if (*max_y > _tft->height()) *max_y = _tft->height(); + // Clip bounding box to be within TFT viewport + if (*min_x < _tft->_vpX) *min_x = _tft->_vpX; + if (*min_y < _tft->_vpY) *min_y = _tft->_vpY; + if (*max_x > _tft->_vpW) *max_x = _tft->_vpW; + if (*max_y > _tft->_vpH) *max_y = _tft->_vpH; return true; } @@ -613,7 +581,8 @@ bool TFT_eSprite::getRotatedBounds(TFT_eSprite *spr, int16_t angle, int16_t *min if (*max_x < 0) return true; if (*max_y < 0) return true; - // Clip bounding box if it is partially within destination Sprite + // Clip bounding box to Sprite boundaries + // Clipping to a viewport will be done by destination Sprite pushImage function if (*min_x < 0) min_x = 0; if (*min_y < 0) min_y = 0; if (*max_x > spr->width()) *max_x = spr->width(); @@ -736,7 +705,8 @@ void TFT_eSprite::pushSprite(int32_t x, int32_t y, uint16_t transp) // Source Destination // 16bpp -> 16bpp // 16bpp -> 8bpp -// 4bpp -> 4bpp (note: color translation depends on the 2 sprites pallet colors) +// 8bpp -> 8bpp +// 4bpp -> 4bpp (note: color translation depends on the 2 sprites pallete colors) // 1bpp -> 1bpp (note: color translation depends on the 2 sprites bitmap colors) bool TFT_eSprite::pushToSprite(TFT_eSprite *dspr, int32_t x, int32_t y) @@ -747,13 +717,13 @@ bool TFT_eSprite::pushToSprite(TFT_eSprite *dspr, int32_t x, int32_t y) // Check destination sprite compatibility int8_t ds_bpp = dspr->getColorDepth(); if (_bpp == 16 && ds_bpp != 16 && ds_bpp != 8) return false; - if (_bpp == 8) return false; + if (_bpp == 8 && ds_bpp != 8) return false; if (_bpp == 4 && ds_bpp != 4) return false; if (_bpp == 1 && ds_bpp != 1) return false; bool oldSwapBytes = dspr->getSwapBytes(); dspr->setSwapBytes(false); - dspr->pushImage(x, y, _dwidth, _dheight, _img ); + dspr->pushImage(x, y, _dwidth, _dheight, _img, _bpp); dspr->setSwapBytes(oldSwapBytes); return true; @@ -868,18 +838,15 @@ bool TFT_eSprite::pushSprite(int32_t tx, int32_t ty, int32_t sx, int32_t sy, int } else // 1bpp { - // Check if a faster block copy to screen is possible + // Check if a faster block copy to screen is possible if ( sx == 0 && sw == _dwidth) - _tft->pushImage(tx, ty, sw, sh, _img8 + (_iwidth>>3) * _ys, (bool)false ); + _tft->pushImage(tx, ty, sw, sh, _img8 + (_bitwidth>>3) * _ys, (bool)false ); else // Render line by line { _tft->startWrite(); - _tft->setWindow(tx, ty, tx+sw-1, ty+sh-1); while (sh--) { - for (int32_t dx = _xs; dx < _xs + sw; dx++) _tft->pushColor(readPixel(dx, _ys)); - ty++; - _ys++; + _tft->pushImage(tx, ty++, sw, 1, _img8 + (_bitwidth>>3) * _ys, (bool)false ); } _tft->endWrite(); } @@ -895,12 +862,18 @@ bool TFT_eSprite::pushSprite(int32_t tx, int32_t ty, int32_t sx, int32_t sy, int ***************************************************************************************/ uint16_t TFT_eSprite::readPixelValue(int32_t x, int32_t y) { - if ((x < 0) || (x >= _iwidth) || (y < 0) || (y >= _iheight) || !_created) return 0xFF; + if (_vpOoB || !_created) return 0xFF; + + x+= _xDatum; + y+= _yDatum; + + // Range checking + if ((x < _vpX) || (y < _vpY) ||(x >= _vpW) || (y >= _vpH)) return 0xFF; if (_bpp == 16) { // Return the pixel colour - return readPixel(x, y); + return readPixel(x - _xDatum, y - _yDatum); } if (_bpp == 8) @@ -921,22 +894,22 @@ uint16_t TFT_eSprite::readPixelValue(int32_t x, int32_t y) if (_bpp == 1) { // Note: _dwidth and _dheight bounds not checked (rounded up -iwidth and _iheight used) - if (_rotation == 1) + if (rotation == 1) { uint16_t tx = x; - x = _dwidth - y - 1; + x = _dheight - y - 1; y = tx; } - else if (_rotation == 2) + else if (rotation == 2) { x = _dwidth - x - 1; y = _dheight - y - 1; } - else if (_rotation == 3) + else if (rotation == 3) { uint16_t tx = x; x = y; - y = _dheight - tx - 1; + y = _dwidth - tx - 1; } // Return 1 or 0 return (_img8[(x + y * _bitwidth)>>3] >> (7-(x & 0x7))) & 0x01; @@ -951,7 +924,13 @@ uint16_t TFT_eSprite::readPixelValue(int32_t x, int32_t y) ***************************************************************************************/ uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) { - if ((x < 0) || (x >= _iwidth) || (y < 0) || (y >= _iheight) || !_created) return 0xFFFF; + if (_vpOoB || !_created) return 0xFFFF; + + x+= _xDatum; + y+= _yDatum; + + // Range checking + if ((x < _vpX) || (y < _vpY) ||(x >= _vpW) || (y >= _vpH)) return 0xFFFF; if (_bpp == 16) { @@ -985,22 +964,22 @@ uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) // Note: Must be 1bpp // _dwidth and _dheight bounds not checked (rounded up -iwidth and _iheight used) - if (_rotation == 1) + if (rotation == 1) { uint16_t tx = x; - x = _dwidth - y - 1; + x = _dheight - y - 1; y = tx; } - else if (_rotation == 2) + else if (rotation == 2) { x = _dwidth - x - 1; y = _dheight - y - 1; } - else if (_rotation == 3) + else if (rotation == 3) { uint16_t tx = x; x = y; - y = _dheight - tx - 1; + y = _dwidth - tx - 1; } uint16_t color = (_img8[(x + y * _bitwidth)>>3] << (x & 0x7)) & 0x80; @@ -1014,42 +993,27 @@ uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) ** Function name: pushImage ** Description: push image into a defined area of a sprite ***************************************************************************************/ -void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data) +void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data, uint8_t sbpp) { if (data == nullptr || !_created) return; - if ((x >= _iwidth) || (y >= _iheight) || (w == 0) || (h == 0)) return; - if ((x + w < 0) || (y + h < 0)) return; - int32_t xo = 0; - int32_t yo = 0; - - int32_t xs = x; - int32_t ys = y; - - int32_t ws = w; - int32_t hs = h; - - if (x < 0) { xo = -x; ws += x; xs = 0; } - if (y < 0) { yo = -y; hs += y; ys = 0; } - - if (xs + ws >= (int32_t)_iwidth) ws = _iwidth - xs; - if (ys + hs >= (int32_t)_iheight) hs = _iheight - ys; + PI_CLIP; if (_bpp == 16) // Plot a 16 bpp image into a 16 bpp Sprite { // Pointer within original image - uint8_t *ptro = (uint8_t *)data + ((xo + yo * w) << 1); + uint8_t *ptro = (uint8_t *)data + ((dx + dy * w) << 1); // Pointer within sprite image - uint8_t *ptrs = (uint8_t *)_img + ((xs + ys * _iwidth) << 1); + uint8_t *ptrs = (uint8_t *)_img + ((x + y * _iwidth) << 1); - if(_iswapBytes) + if(_swapBytes) { - while (hs--) + while (dh--) { // Fast copy with a 1 byte shift - memcpy(ptrs+1, ptro, (ws<<1) - 1); + memcpy(ptrs+1, ptro, (dw<<1) - 1); // Now correct just the even numbered bytes - for (int32_t xp = 0; xp < (ws<<1); xp+=2) + for (int32_t xp = 0; xp < (dw<<1); xp+=2) { ptrs[xp] = ptro[xp+1];; } @@ -1059,28 +1023,48 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_ } else { - while (hs--) + while (dh--) { - memcpy(ptrs, ptro, ws<<1); + memcpy(ptrs, ptro, dw<<1); ptro += w << 1; ptrs += _iwidth << 1; } } } + else if (_bpp == 8 && sbpp == 8) // Plot a 8 bpp image into a 8 bpp Sprite + { + // Pointer within original image + uint8_t *ptro = (uint8_t *)data + (dx + dy * w); + // Pointer within sprite image + uint8_t *ptrs = (uint8_t *)_img + (x + y * _iwidth); + + while (dh--) + { + memcpy(ptrs, ptro, dw); + ptro += w; + ptrs += _iwidth; + } + } else if (_bpp == 8) // Plot a 16 bpp image into a 8 bpp Sprite { - for (int32_t yp = yo; yp < yo + hs; yp++) + uint16_t lastColor = 0; + uint8_t color8 = 0; + for (int32_t yp = dy; yp < dy + dh; yp++) { - x = xs; - for (int32_t xp = xo; xp < xo + ws; xp++) + int32_t xyw = x + y * _iwidth; + int32_t dxypw = dx + yp * w; + for (int32_t xp = dx; xp < dx + dw; xp++) { - uint16_t color = data[xp + yp * w]; - // When data source is a sprite, the bytes are already swapped - if(!_iswapBytes) _img8[x + ys * _iwidth] = (uint8_t)((color & 0xE0) | (color & 0x07)<<2 | (color & 0x1800)>>11); - else _img8[x + ys * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); - x++; + uint16_t color = data[dxypw++]; + if (color != lastColor) { + // When data source is a sprite, the bytes are already swapped + if(!_swapBytes) color8 = (uint8_t)((color & 0xE0) | (color & 0x07)<<2 | (color & 0x1800)>>11); + else color8 = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + } + lastColor = color; + _img8[xyw++] = color8; } - ys++; + y++; } } else if (_bpp == 4) @@ -1089,77 +1073,56 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_ // much faster when aligned to a byte boundary, because the alternative is slower, requiring // tedious bit operations. - const uint8_t *dataBuf = (uint8_t *)data; int sWidth = (_iwidth >> 1); + uint8_t *ptr = (uint8_t *)data; - if ((xs & 0x01) == 0 && (xo & 0x01) == 0 && (ws & 0x01) == 0) + if ((x & 0x01) == 0 && (dx & 0x01) == 0 && (dw & 0x01) == 0) { - if ((ws & 0x01) == 0) // use memcpy for better perf. + x = (x >> 1) + y * sWidth; + dw = (dw >> 1); + dx = (dx >> 1) + dy * (w>>1); + while (dh--) { - xs = (xs >> 1) + ys * sWidth; - ws = (ws >> 1); - xo = (xo >> 1) + yo * (w>>1); - while (hs--) - { - memcpy(_img4 + xs, dataBuf + xo, ws); - xo += (w >> 1); - xs += sWidth; - } + memcpy(_img4 + x, ptr + dx, dw); + dx += (w >> 1); + x += sWidth; } } else // not optimized { - // _dwidth and _dheight bounds not checked (rounded up -iwidth and _iheight used) - for (int32_t yp = yo; yp < yo + hs; yp++) + for (int32_t yp = dy; yp < dy + dh; yp++) { - x = xs; - for (int32_t xp = xo; xp < xo + ws; xp++) + int32_t ox = x; + for (int32_t xp = dx; xp < dx + dw; xp++) { uint32_t color; if ((xp & 0x01) == 0) - color = (dataBuf[((xp+yp*w)>>1)] & 0xF0) >> 4; // even index = bits 7 .. 4 + color = (ptr[((xp+yp*w)>>1)] & 0xF0) >> 4; // even index = bits 7 .. 4 else - color = dataBuf[((xp-1+yp*w)>>1)] & 0x0F; // odd index = bits 3 .. 0. - drawPixel(x, ys, color); - x++; + color = ptr[((xp-1+yp*w)>>1)] & 0x0F; // odd index = bits 3 .. 0. + drawPixel(ox, y, color); + ox++; } - ys++; + y++; } } } else // 1bpp { - // Move coordinate rotation to support fn - if (_rotation == 1) - { - int32_t tx = x; - x = _dwidth - y - 1; - y = tx; - } - else if (_rotation == 2) - { - x = _dwidth - x - 1; - y = _dheight - y - 1; - } - else if (_rotation == 3) - { - int32_t tx = x; - x = y; - y = _dheight - tx - 1; - } // Plot a 1bpp image into a 1bpp Sprite - uint8_t* pdata = (uint8_t* ) data; - uint32_t ww = (w+7) & 0xFFF8; - for (int32_t yp = 0; yp>3; // Width of source image line in bytes + uint8_t *ptr = (uint8_t *)data; + for (int32_t yp = dy; yp < dy + dh; yp++) { - uint32_t yw = (yp * ww)>>3; - uint32_t yyp = y + yp; - for (int32_t xp = 0; xp>3) + yw] & (0x80 >> (xp & 0x7)) ); - drawPixel(x+xp, yyp, readPixel); + uint16_t readPixel = (ptr[(xp>>3) + yw] & (0x80 >> (xp & 0x7)) ); + drawPixel(ox++, y, readPixel); } + y++; } } } @@ -1175,53 +1138,39 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const u pushImage(x, y, w, h, (uint16_t*) data); #else // Partitioned memory FLASH processor - if ((x >= _iwidth) || (y >= _iheight) || (w == 0) || (h == 0) || !_created) return; - if ((x + w < 0) || (y + h < 0)) return; + if (data == nullptr || !_created) return; - int32_t xo = 0; - int32_t yo = 0; - - int32_t xs = x; - int32_t ys = y; - - int32_t ws = w; - int32_t hs = h; - - if (x < 0) { xo = -x; ws += x; xs = 0; } - if (y < 0) { yo = -y; hs += y; ys = 0; } - - if (xs + ws >= (int32_t)_iwidth) ws = _iwidth - xs; - if (ys + hs >= (int32_t)_iheight) hs = _iheight - ys; + PI_CLIP; if (_bpp == 16) // Plot a 16 bpp image into a 16 bpp Sprite { - for (int32_t yp = yo; yp < yo + hs; yp++) + for (int32_t yp = dy; yp < dy + dh; yp++) { - x = xs; - for (int32_t xp = xo; xp < xo + ws; xp++) + int32_t ox = x; + for (int32_t xp = dx; xp < dx + dw; xp++) { uint16_t color = pgm_read_word(data + xp + yp * w); - if(_iswapBytes) color = color<<8 | color>>8; - _img[x + ys * _iwidth] = color; - x++; + if(_swapBytes) color = color<<8 | color>>8; + _img[ox + y * _iwidth] = color; + ox++; } - ys++; + y++; } } else if (_bpp == 8) // Plot a 16 bpp image into a 8 bpp Sprite { - for (int32_t yp = yo; yp < yo + hs; yp++) + for (int32_t yp = dy; yp < dy + dh; yp++) { - x = xs; - for (int32_t xp = xo; xp < xo + ws; xp++) + int32_t ox = x; + for (int32_t xp = dx; xp < dx + dw; xp++) { uint16_t color = pgm_read_word(data + xp + yp * w); - if(_iswapBytes) color = color<<8 | color>>8; - _img8[x + ys * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); - x++; + if(_swapBytes) color = color<<8 | color>>8; + _img8[ox + y * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + ox++; } - ys++; + y++; } } @@ -1233,63 +1182,31 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const u return; } - else // 1bpp + else // Plot a 1bpp image into a 1bpp Sprite { - // _dwidth and _dheight bounds not checked (rounded up -iwidth and _iheight used) - // Move coordinate rotation to support fn - if (_rotation == 1) - { - int32_t tx = x; - x = _dwidth - y - 1; - y = tx; - } - else if (_rotation == 2) - { - x = _dwidth - x - 1; - y = _dheight - y - 1; - } - else if (_rotation == 3) - { - int32_t tx = x; - x = y; - y = _dheight - tx - 1; - } - // Plot a 1bpp image into a 1bpp Sprite - const uint8_t* pdata = (const uint8_t* ) data; - uint32_t ww = (w+7) & 0xFFF8; - for (int32_t yp = 0; yp> 3; // Width in bytes of source image line + uint8_t *ptr = ((uint8_t*)data) + dy * bsw; + + while (dh--) { + int32_t odx = dx; + int32_t ox = x; + while (odx < dx + dw) { + uint8_t pbyte = pgm_read_byte(ptr + (odx>>3)); + uint8_t mask = 0x80 >> (odx & 7); + while (mask) { + uint8_t p = pbyte & mask; + mask = mask >> 1; + drawPixel(ox++, y, p); + odx++; } } + ptr += bsw; + y++; } } -#endif // if ESP32 else ESP8266 check -} - - -/*************************************************************************************** -** 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; +#endif // if ESP32 check } @@ -1297,12 +1214,16 @@ bool TFT_eSprite::getSwapBytes(void) ** Function name: setWindow ** Description: Set the bounds of a window in the sprite ***************************************************************************************/ +// Intentionally not constrained to viewport area, does not manage 1bpp rotations void TFT_eSprite::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) { if (x0 > x1) swap_coord(x0, x1); if (y0 > y1) swap_coord(y0, y1); + + int32_t w = width(); + int32_t h = height(); - if ((x0 >= _dwidth) || (x1 < 0) || (y0 >= _dheight) || (y1 < 0)) + if ((x0 >= w) || (x1 < 0) || (y0 >= h) || (y1 < 0)) { // Point to that extra "off screen" pixel _xs = 0; _ys = _dheight; @@ -1312,9 +1233,9 @@ void TFT_eSprite::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) else { if (x0 < 0) x0 = 0; - if (x1 >= _dwidth) x1 = _dwidth - 1; + if (x1 >= w) x1 = w - 1; if (y0 < 0) y0 = 0; - if (y1 >= _dheight) y1 = _dheight - 1; + if (y1 >= h) y1 = h - 1; _xs = x0; _ys = y0; @@ -1433,6 +1354,7 @@ void TFT_eSprite::writeColor(uint16_t color) ** Function name: setScrollRect ** Description: Set scroll area within the sprite and the gap fill colour ***************************************************************************************/ +// Intentionally not constrained to viewport area void TFT_eSprite::setScrollRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t color) { if ((x >= _iwidth) || (y >= _iheight) || !_created ) return; @@ -1558,43 +1480,37 @@ void TFT_eSprite::scroll(int16_t dx, int16_t dy) ***************************************************************************************/ void TFT_eSprite::fillSprite(uint32_t color) { - if (!_created ) return; + if (!_created || _vpOoB) return; // Use memset if possible as it is super fast - if(( (uint8_t)color == (uint8_t)(color>>8) ) && _bpp == 16) - memset(_img, (uint8_t)color, _iwidth * _iheight * 2); - else if (_bpp == 8) + if(_xDatum == 0 && _yDatum == 0 && _xWidth == width()) { - color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; - memset(_img8, (uint8_t)color, _iwidth * _iheight); + if(_bpp == 16) { + if ( (uint8_t)color == (uint8_t)(color>>8) ) { + memset(_img, (uint8_t)color, _iwidth * _yHeight * 2); + } + else fillRect(_vpX, _vpY, _xWidth, _yHeight, color); + } + else if (_bpp == 8) + { + color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; + memset(_img8, (uint8_t)color, _iwidth * _yHeight); + } + else if (_bpp == 4) + { + uint8_t c = ((color & 0x0F) | (((color & 0x0F) << 4) & 0xF0)); + memset(_img4, c, (_iwidth * _yHeight) >> 1); + } + else if (_bpp == 1) + { + if(color) memset(_img8, 0xFF, (_bitwidth>>3) * _dheight + 1); + else memset(_img8, 0x00, (_bitwidth>>3) * _dheight + 1); + } } - else if (_bpp == 4) - { - uint8_t c = ((color & 0x0F) | (((color & 0x0F) << 4) & 0xF0)); - memset(_img4, c, (_iwidth * _iheight) >> 1); - } - else if (_bpp == 1) - { - if(color) memset(_img8, 0xFF, (_iwidth>>3) * _iheight + 1); - else memset(_img8, 0x00, (_iwidth>>3) * _iheight + 1); - } - - else fillRect(0, 0, _iwidth, _iheight, color); + else fillRect(_vpX - _xDatum, _vpY - _yDatum, _xWidth, _yHeight, color); } -/*************************************************************************************** -** Function name: setCursor -** Description: Set the sprite text cursor x,y position -***************************************************************************************/ -// Not needed - using TFT_eSPI class function and this->cursor_x/y -//void TFT_eSprite::setCursor(int16_t x, int16_t y) -//{ -// this->cursor_x = x; -// this->cursor_y = y; -//} - - /*************************************************************************************** ** Function name: width ** Description: Return the width of sprite @@ -1604,10 +1520,17 @@ int16_t TFT_eSprite::width(void) { if (!_created ) return 0; - if (_bpp > 1) return _dwidth; + if (_bpp > 1) { + if (_vpDatum) return _xWidth; + return _dwidth; + } - if (_rotation == 1 || _rotation == 3) return _dheight; + if (rotation & 1) { + if (_vpDatum) return _xWidth; + return _dheight; + } + if (_vpDatum) return _xWidth; return _dwidth; } @@ -1620,10 +1543,17 @@ int16_t TFT_eSprite::height(void) { if (!_created ) return 0; - if (_bpp > 4) return _dheight; + if (_bpp > 1) { + if (_vpDatum) return _yHeight; + return _dheight; + } - if (_rotation == 1 || _rotation == 3) return _dwidth; + if (rotation & 1) { + if (_vpDatum) return _yHeight; + return _dwidth; + } + if (_vpDatum) return _yHeight; return _dheight; } @@ -1632,16 +1562,19 @@ int16_t TFT_eSprite::height(void) ** Function name: setRotation ** Description: Rotate coordinate frame for 1bpp sprite ***************************************************************************************/ -// Does nothing for 8 and 16 bpp sprites. TODO allow rotation of these sprites -void TFT_eSprite::setRotation(uint8_t rotation) +// Does nothing for 4, 8 and 16 bpp sprites. +void TFT_eSprite::setRotation(uint8_t r) { if (_bpp != 1) return; - _rotation = rotation; - if (rotation == 0 && _iwidth > _iheight) swap_coord(_iwidth, _iheight); - if (rotation == 1 && _iwidth < _iheight) swap_coord(_iwidth, _iheight); - if (rotation == 2 && _iwidth > _iheight) swap_coord(_iwidth, _iheight); - if (rotation == 3 && _iwidth < _iheight) swap_coord(_iwidth, _iheight); + rotation = r; + + if (rotation&1) { + resetViewport(); + } + else { + resetViewport(); + } } @@ -1649,10 +1582,9 @@ void TFT_eSprite::setRotation(uint8_t rotation) ** Function name: getRotation ** Description: Get rotation for 1bpp sprite ***************************************************************************************/ - uint8_t TFT_eSprite::getRotation(void) { - return _rotation; + return rotation; } @@ -1662,9 +1594,13 @@ uint8_t TFT_eSprite::getRotation(void) ***************************************************************************************/ void TFT_eSprite::drawPixel(int32_t x, int32_t y, uint32_t color) { + if (!_created || _vpOoB) return; + + x+= _xDatum; + y+= _yDatum; + // Range checking - if ((x < 0) || (y < 0) || !_created) return; - if ((x >= _iwidth) || (y >= _iheight)) return; + if ((x < _vpX) || (y < _vpY) ||(x >= _vpW) || (y >= _vpH)) return; if (_bpp == 16) { @@ -1688,18 +1624,18 @@ void TFT_eSprite::drawPixel(int32_t x, int32_t y, uint32_t color) } else // 1 bpp { - if (_rotation == 1) + if (rotation == 1) { uint16_t tx = x; x = _dwidth - y - 1; y = tx; } - else if (_rotation == 2) + else if (rotation == 2) { x = _dwidth - x - 1; y = _dheight - y - 1; } - else if (_rotation == 3) + else if (rotation == 3) { uint16_t tx = x; x = y; @@ -1718,7 +1654,9 @@ void TFT_eSprite::drawPixel(int32_t x, int32_t y, uint32_t color) ***************************************************************************************/ void TFT_eSprite::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color) { - if (!_created ) return; + if (!_created || _vpOoB) return; + + //_xDatum and _yDatum Not added here, it is added by drawPixel & drawFastxLine bool steep = abs(y1 - y0) > abs(x1 - x0); if (steep) { @@ -1774,12 +1712,17 @@ void TFT_eSprite::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint3 ***************************************************************************************/ void TFT_eSprite::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) { + if (!_created || _vpOoB) return; - if ((x < 0) || (x >= _iwidth) || (y >= _iheight) || !_created) return; + x+= _xDatum; + y+= _yDatum; - if (y < 0) { h += y; y = 0; } + // Clipping + if ((x < _vpX) || (x >= _vpW) || (y >= _vpH)) return; - if ((y + h) > _iheight) h = _iheight - y; + if (y < _vpY) { h += y - _vpY; y = _vpY; } + + if ((y + h) > _vpH) h = _vpH - y; if (h < 1) return; @@ -1814,10 +1757,11 @@ void TFT_eSprite::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) } else { + x -= _xDatum; // Remove any offset as it will be added by drawPixel + y -= _yDatum; while (h--) { - drawPixel(x, y, color); - y++; + drawPixel(x, y++, color); } } } @@ -1829,12 +1773,17 @@ void TFT_eSprite::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) ***************************************************************************************/ void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) { + if (!_created || _vpOoB) return; - if ((y < 0) || (x >= _iwidth) || (y >= _iheight) || !_created) return; + x+= _xDatum; + y+= _yDatum; - if (x < 0) { w += x; x = 0; } + // Clipping + if ((y < _vpY) || (x >= _vpW) || (y >= _vpH)) return; - if ((x + w) > _iwidth) w = _iwidth - x; + if (x < _vpX) { w += x - _vpX; x = _vpX; } + + if ((x + w) > _vpW) w = _vpW - x; if (w < 1) return; @@ -1854,7 +1803,7 @@ void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) uint8_t c2 = (c | ((c << 4) & 0xF0)); if ((x & 0x01) == 1) { - drawPixel(x, y, color); + drawPixel(x - _xDatum, y - _yDatum, color); x++; w--; if (w < 1) return; @@ -1863,17 +1812,19 @@ void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) if (((w + x) & 0x01) == 1) { // handle the extra one at the other end - drawPixel(x + w - 1, y, color); + drawPixel(x - _xDatum + w - 1, y - _yDatum, color); w--; if (w < 1) return; } memset(_img4 + ((_iwidth * y + x) >> 1), c2, (w >> 1)); } else { + x -= _xDatum; // Remove any offset as it will be added by drawPixel + y -= _yDatum; + while (w--) { - drawPixel(x, y, color); - x++; + drawPixel(x++, y, color); } } } @@ -1885,15 +1836,19 @@ void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) ***************************************************************************************/ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) { - if (!_created ) return; + if (!_created || _vpOoB) return; - if ((x >= _iwidth) || (y >= _iheight)) return; + x+= _xDatum; + y+= _yDatum; - if (x < 0) { w += x; x = 0; } - if (y < 0) { h += y; y = 0; } + // Clipping + if ((x >= _vpW) || (y >= _vpH)) return; - if ((x + w) > _iwidth) w = _iwidth - x; - if ((y + h) > _iheight) h = _iheight - y; + if (x < _vpX) { w += x - _vpX; x = _vpX; } + if (y < _vpY) { h += y - _vpY; y = _vpY; } + + if ((x + w) > _vpW) w = _vpW - x; + if ((y + h) > _vpH) h = _vpH - y; if ((w < 1) || (h < 1)) return; @@ -1936,6 +1891,7 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t } else if ((x & 0x01) == 0) { + // same as above but you have a hangover on the right. yp = (yp >> 1); while (h--) @@ -1943,7 +1899,7 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t if (w > 1) memset(_img4 + yp, c2, (w-1)>>1); // handle the rightmost pixel by calling drawPixel - drawPixel(x+w-1, y+h, c1); + drawPixel(x+w-1-_xDatum, y+h-_yDatum, c1); yp += (_iwidth >> 1); } } @@ -1951,7 +1907,7 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t { yp = (yp + 1) >> 1; while (h--) { - drawPixel(x, y+h, color & 0x0F); + drawPixel(x-_xDatum, y+h-_yDatum, color & 0x0F); if (w > 1) memset(_img4 + yp, c2, (w-1)>>1); // same as above but you have a hangover on the left instead @@ -1962,8 +1918,8 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t { yp = (yp + 1) >> 1; while (h--) { - drawPixel(x, y+h, color & 0x0F); - if (w > 1) drawPixel(x+w-1, y+h, color & 0x0F); + drawPixel(x-_xDatum, y+h-_yDatum, color & 0x0F); + if (w > 1) drawPixel(x+w-1-_xDatum, y+h-_yDatum, color & 0x0F); if (w > 2) memset(_img4 + yp, c2, (w-2)>>1); // maximal hacking, single pixels on left and right. @@ -1973,6 +1929,8 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t } else { + x -= _xDatum; + y -= _yDatum; while (h--) { int32_t ww = w; @@ -1984,155 +1942,18 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t } -/*************************************************************************************** -** Function name: write -** Description: draw characters piped through serial stream -***************************************************************************************/ -size_t TFT_eSprite::write(uint8_t utf8) -{ - uint16_t uniCode = decodeUTF8(utf8); - - if (!uniCode) return 1; - - if (utf8 == '\r') return 1; - -#ifdef SMOOTH_FONT - if(this->fontLoaded) - { - if (uniCode < 32 && utf8 != '\n') return 1; - - //Serial.print("Decoded Unicode = 0x");Serial.println(unicode,HEX); - - drawGlyph(uniCode); - - return 1; - } -#endif - - if (!_created ) return 1; - - if (uniCode == '\n') uniCode+=22; // Make it a valid space character to stop errors - else if (uniCode < 32) return 1; - - 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 1; - - 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 1; - // Uses the fontinfo struct array to avoid lots of 'if' or 'switch' statements - 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 1; -#endif - - height = height * textsize; - - if (utf8 == '\n') - { - this->cursor_y += height; - this->cursor_x = 0; - } - else - { - if (textwrapX && (this->cursor_x + width * textsize > _iwidth)) - { - this->cursor_y += height; - this->cursor_x = 0; - } - if (textwrapY && (this->cursor_y >= _iheight)) this->cursor_y = 0; - this->cursor_x += drawChar(uniCode, this->cursor_x, this->cursor_y, textfont); - } - -//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< -#ifdef LOAD_GFXFF - } // Custom GFX font - else - { - if(utf8 == '\n') { - this->cursor_x = 0; - this->cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); - } else { - if (uniCode > pgm_read_word(&gfxFont->last )) return 1; - if (uniCode < pgm_read_word(&gfxFont->first)) return 1; - - uint8_t c2 = uniCode - pgm_read_word(&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 && ((this->cursor_x + textsize * (xo + w)) > _iwidth)) { - // Drawing character would go off right edge; wrap to new line - this->cursor_x = 0; - this->cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); - } - if (textwrapY && (this->cursor_y >= _iheight)) this->cursor_y = 0; - drawChar(this->cursor_x, this->cursor_y, uniCode, textcolor, textbgcolor, textsize); - } - this->cursor_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 ***************************************************************************************/ void TFT_eSprite::drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32_t bg, uint8_t size) { - if (!_created ) return; + if ( _vpOoB || !_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 + if ((x >= _vpW - _xDatum) || // Clip right + (y >= _vpH - _yDatum) || // Clip bottom + ((x + 6 * size - 1) < (_vpX - _xDatum)) || // Clip left + ((y + 8 * size - 1) < (_vpY - _yDatum))) // Clip top return; if (c < 32) return; @@ -2265,8 +2086,9 @@ void TFT_eSprite::drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uin /*************************************************************************************** ** Function name: drawChar -** Description: draw a unicode onto the screen +** Description: draw a unicode glyph onto the screen ***************************************************************************************/ + // TODO: Rationalise with TFT_eSPI // Any UTF-8 decoding must be done before calling drawChar() int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y) { @@ -2276,12 +2098,9 @@ int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y) // Any UTF-8 decoding must be done before calling drawChar() int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font) { - if (!_created ) return 0; + if (_vpOoB || !uniCode) return 0; - if (!uniCode) return 0; - - if (font==1) - { + if (font==1) { #ifdef LOAD_GLCD #ifndef LOAD_GFXFF drawChar(x, y, uniCode, textcolor, textbgcolor, textsize); @@ -2302,16 +2121,13 @@ int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t fo return 0; #endif } - else - { - if((uniCode >= pgm_read_word(&gfxFont->first)) && (uniCode <= pgm_read_word(&gfxFont->last) )) - { + else { + if((uniCode >= pgm_read_word(&gfxFont->first)) && (uniCode <= pgm_read_word(&gfxFont->last) )) { uint16_t c2 = uniCode - pgm_read_word(&gfxFont->first); GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); return pgm_read_byte(&glyph->xAdvance) * textsize; } - else - { + else { return 0; } } @@ -2326,9 +2142,7 @@ int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t fo uniCode -= 32; #ifdef LOAD_FONT2 - if (font == 2) - { - // This is faster than using the fontdata structure + if (font == 2) { flash_address = pgm_read_dword(&chrtbl_f16[uniCode]); width = pgm_read_byte(widtbl_f16 + uniCode); height = chr_hgt_f16; @@ -2340,26 +2154,29 @@ int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t fo #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( (const void*) (pgm_read_dword( &(fontdata[font].chartbl ) ) + uniCode*sizeof(void *)) ); + if ((font>2) && (font<9)) { + flash_address = pgm_read_dword( (const void*)(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 + int32_t xd = x + _xDatum; + int32_t yd = y + _yDatum; + + if ((xd + width * textsize < _vpX || xd >= _vpW) && (yd + height * textsize < _vpY || yd >= _vpH)) return width * textsize ; + int32_t w = width; int32_t pX = 0; int32_t pY = y; uint8_t line = 0; + bool clip = xd < _vpX || xd + width * textsize >= _vpW || yd < _vpY || yd + height * textsize >= _vpH; #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 (int32_t i = 0; i < height; i++) { @@ -2406,51 +2223,116 @@ int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t fo // 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 = textcolor; if (_bpp == 16) color = (textcolor >> 8) | (textcolor << 8); else if (_bpp == 8) color = ((textcolor & 0xE000)>>8 | (textcolor & 0x0700)>>6 | (textcolor & 0x0018)>>3); - int32_t px = 0, py = pY; // To hold character block start and end column and row values - int32_t pc = 0; // Pixel count - uint8_t np = textsize * textsize; // Number of pixels in a drawn pixel - 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); + + int16_t bgcolor = textbgcolor; + if (_bpp == 16) bgcolor = (textbgcolor >> 8) | (textbgcolor << 8); + else if (_bpp == 8) bgcolor = ((textbgcolor & 0xE000)>>8 | (textbgcolor & 0x0700)>>6 | (textbgcolor & 0x0018)>>3); + + if (textcolor == textbgcolor && !clip && _bpp != 1) { + int32_t px = 0, py = pY; // To hold character block start and end column and row values + int32_t pc = 0; // Pixel count + uint8_t np = textsize * textsize; // Number of pixels in a drawn pixel + + 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++; + if (line & 0x80) { + line &= 0x7F; + line++; + if (ts) { + px = xd + textsize * (pc % width); // Keep these px and py calculations outside the loop as they are slow + py = yd + textsize * (pc / width); + } + else { + px = xd + pc % width; // Keep these px and py calculations outside the loop as they are slow + py = yd + pc / width; + } + while (line--) { // In this case the while(line--) is faster + pc++; // This is faster than putting pc+=line before while()? + setWindow(px, py, px + ts, py + ts); + + if (ts) { + tnp = np; + while (tnp--) writeColor(color); + } + else writeColor(color); + + px += textsize; + + if (px >= (xd + width * textsize)) { + px = xd; + py += textsize; + } + } } else { - px = x + pc % width; // Keep these px and py calculations outside the loop as they are slow - py = y + pc / width; + line++; + pc += line; } - while (line--) { - pc++; - setWindow(px, py, px + ts, py + ts); - if (ts) { tnp = np; while (tnp--) writeColor(color); } - else writeColor(color); + } + } + else { + // Text colour != background and textsize = 1 and character is within viewport area + // so use faster drawing of characters and background using block write + if (textcolor != textbgcolor && textsize == 1 && !clip && _bpp != 1) + { + setWindow(xd, yd, xd + width - 1, yd + height - 1); - px += textsize; - - if (px >= (x + width * textsize)) - { - px = x; - py += textsize; + // Maximum font size is equivalent to 180x180 pixels in area + while (w > 0) { + line = pgm_read_byte((uint8_t *)flash_address++); // 8 bytes smaller when incrementing here + if (line & 0x80) { + line &= 0x7F; + line++; w -= line; + while (line--) writeColor(color); + } + else { + line++; w -= line; + while (line--) writeColor(bgcolor); } } } - else { - line++; - pc += line; + else + { + int32_t px = 0, py = 0; // To hold character pixel coords + int32_t tx = 0, ty = 0; // To hold character TFT pixel coords + int32_t pc = 0; // Pixel count + int32_t pl = 0; // Pixel line length + uint16_t pcol = 0; // Pixel color + bool pf = true; // Flag for plotting + while (pc < w) { + line = pgm_read_byte((uint8_t *)flash_address); + flash_address++; + if (line & 0x80) { pcol = textcolor; line &= 0x7F; pf = true;} + else { pcol = textbgcolor; if (textcolor == textbgcolor) pf = false;} + line++; + px = pc % width; + tx = x + textsize * px; + py = pc / width; + ty = y + textsize * py; + + pl = 0; + pc += line; + while (line--) { + pl++; + if ((px+pl) >= width) { + if (pf) fillRect(tx, ty, pl * textsize, textsize, pcol); + pl = 0; + px = 0; + tx = x; + py ++; + ty += textsize; + } + } + if (pl && pf) fillRect(tx, ty, pl * textsize, textsize, pcol); + } } } } @@ -2459,44 +2341,35 @@ int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t fo return width * textsize; // x + } + #ifdef SMOOTH_FONT /*************************************************************************************** ** Function name: drawGlyph ** Description: Write a character to the sprite cursor position ***************************************************************************************/ +// void TFT_eSprite::drawGlyph(uint16_t code) { + uint16_t fg = textcolor; + uint16_t bg = textbgcolor; + if (code < 0x21) { if (code == 0x20) { - if (_created) this->cursor_x += this->gFont.spaceWidth; - else this->cursor_x += this->gFont.spaceWidth; + cursor_x += gFont.spaceWidth; return; } if (code == '\n') { - if (_created) - { - this->cursor_x = 0; - this->cursor_y += this->gFont.yAdvance; - if (this->cursor_y >= _height) this->cursor_y = 0; - return; - } - else - { - cursor_x = 0; - cursor_y += gFont.yAdvance; - if (cursor_y >= _height) cursor_y = 0; - return; - } + cursor_x = 0; + cursor_y += gFont.yAdvance; + if (textwrapY && (cursor_y >= height())) cursor_y = 0; + return; } } uint16_t gNum = 0; - bool found = this->getUnicodeIndex(code, &gNum); - - uint16_t fg = this->textcolor; - uint16_t bg = this->textbgcolor; + bool found = getUnicodeIndex(code, &gNum); if (found) { @@ -2505,31 +2378,30 @@ void TFT_eSprite::drawGlyph(uint16_t code) if (newSprite) { - createSprite(this->gWidth[gNum], this->gFont.yAdvance); + createSprite(gWidth[gNum], gFont.yAdvance); if(bg) fillSprite(bg); - this->cursor_x = -this->gdX[gNum]; - this->cursor_y = 0; + cursor_x = -gdX[gNum]; + cursor_y = 0; } else { - if( this->textwrapX && ((this->cursor_x + this->gWidth[gNum] + this->gdX[gNum]) > _iwidth)) { - this->cursor_y += this->gFont.yAdvance; - this->cursor_x = 0; + if( textwrapX && ((cursor_x + gWidth[gNum] + gdX[gNum]) > width())) { + cursor_y += gFont.yAdvance; + cursor_x = 0; } - if( this->textwrapY && ((this->cursor_y + this->gFont.yAdvance) > _iheight)) this->cursor_y = 0; - - if ( this->cursor_x == 0) this->cursor_x -= this->gdX[gNum]; + if( textwrapY && ((cursor_y + gFont.yAdvance) > height())) cursor_y = 0; + if ( cursor_x == 0) cursor_x -= gdX[gNum]; } uint8_t* pbuffer = nullptr; - const uint8_t* gPtr = (const uint8_t*) this->gFont.gArray; + const uint8_t* gPtr = (const uint8_t*) gFont.gArray; #ifdef FONT_FS_AVAILABLE - if (this->fs_font) { - this->fontFile.seek(this->gBitmap[gNum], fs::SeekSet); // This is slow for a significant position shift! - pbuffer = (uint8_t*)malloc(this->gWidth[gNum]); + if (fs_font) { + fontFile.seek(gBitmap[gNum], fs::SeekSet); // This is slow for a significant position shift! + pbuffer = (uint8_t*)malloc(gWidth[gNum]); } #endif @@ -2537,60 +2409,60 @@ void TFT_eSprite::drawGlyph(uint16_t code) uint16_t dl = 0; uint8_t pixel = 0; - for (int32_t y = 0; y < this->gHeight[gNum]; y++) + for (int32_t y = 0; y < gHeight[gNum]; y++) { #ifdef FONT_FS_AVAILABLE - if (this->fs_font) { - this->fontFile.read(pbuffer, this->gWidth[gNum]); + if (fs_font) { + fontFile.read(pbuffer, gWidth[gNum]); } #endif - for (int32_t x = 0; x < this->gWidth[gNum]; x++) + for (int32_t x = 0; x < gWidth[gNum]; x++) { #ifdef FONT_FS_AVAILABLE - if (this->fs_font) { + if (fs_font) { pixel = pbuffer[x]; } else #endif - pixel = pgm_read_byte(gPtr + this->gBitmap[gNum] + x + this->gWidth[gNum] * y); + pixel = pgm_read_byte(gPtr + gBitmap[gNum] + x + gWidth[gNum] * y); if (pixel) { if (pixel != 0xFF) { - if (dl) { drawFastHLine( xs, y + this->cursor_y + this->gFont.maxAscent - this->gdY[gNum], dl, fg); dl = 0; } - if (_bpp != 1) drawPixel(x + this->cursor_x + this->gdX[gNum], y + this->cursor_y + this->gFont.maxAscent - this->gdY[gNum], alphaBlend(pixel, fg, bg)); - else if (pixel>127) drawPixel(x + this->cursor_x + this->gdX[gNum], y + this->cursor_y + this->gFont.maxAscent - this->gdY[gNum], fg); + if (dl) { drawFastHLine( xs, y + cursor_y + gFont.maxAscent - gdY[gNum], dl, fg); dl = 0; } + if (_bpp != 1) drawPixel(x + cursor_x + gdX[gNum], y + cursor_y + gFont.maxAscent - gdY[gNum], alphaBlend(pixel, fg, bg)); + else if (pixel>127) drawPixel(x + cursor_x + gdX[gNum], y + cursor_y + gFont.maxAscent - gdY[gNum], fg); } else { - if (dl==0) xs = x + this->cursor_x + this->gdX[gNum]; + if (dl==0) xs = x + cursor_x + gdX[gNum]; dl++; } } else { - if (dl) { drawFastHLine( xs, y + this->cursor_y + this->gFont.maxAscent - this->gdY[gNum], dl, fg); dl = 0; } + if (dl) { drawFastHLine( xs, y + cursor_y + gFont.maxAscent - gdY[gNum], dl, fg); dl = 0; } } } - if (dl) { drawFastHLine( xs, y + this->cursor_y + this->gFont.maxAscent - this->gdY[gNum], dl, fg); dl = 0; } + if (dl) { drawFastHLine( xs, y + cursor_y + gFont.maxAscent - gdY[gNum], dl, fg); dl = 0; } } if (pbuffer) free(pbuffer); if (newSprite) { - pushSprite(this->cursor_x + this->gdX[gNum], this->cursor_y, bg); + pushSprite(cursor_x + gdX[gNum], cursor_y, bg); deleteSprite(); - this->cursor_x += this->gxAdvance[gNum]; + cursor_x += gxAdvance[gNum]; } - else this->cursor_x += this->gxAdvance[gNum]; + else cursor_x += gxAdvance[gNum]; } else { // Not a Unicode in font so draw a rectangle and move on cursor - drawRect(this->cursor_x, this->cursor_y + this->gFont.maxAscent - this->gFont.ascent, this->gFont.spaceWidth, this->gFont.ascent, fg); - this->cursor_x += this->gFont.spaceWidth + 1; + drawRect(cursor_x, cursor_y + gFont.maxAscent - gFont.ascent, gFont.spaceWidth, gFont.ascent, fg); + cursor_x += gFont.spaceWidth + 1; } } @@ -2601,7 +2473,7 @@ void TFT_eSprite::drawGlyph(uint16_t code) ***************************************************************************************/ void TFT_eSprite::printToSprite(String string) { - if(!this->fontLoaded) return; + if(!fontLoaded) return; uint16_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 @@ -2616,7 +2488,7 @@ void TFT_eSprite::printToSprite(String string) ***************************************************************************************/ void TFT_eSprite::printToSprite(char *cbuffer, uint16_t len) //String string) { - if(!this->fontLoaded) return; + if(!fontLoaded) return; uint16_t n = 0; bool newSprite = !_created; @@ -2629,18 +2501,18 @@ void TFT_eSprite::printToSprite(char *cbuffer, uint16_t len) //String string) while (n < len) { uint16_t unicode = decodeUTF8((uint8_t*)cbuffer, &n, len - n); - if (this->getUnicodeIndex(unicode, &index)) + if (getUnicodeIndex(unicode, &index)) { - if (n == 0) sWidth -= this->gdX[index]; - if (n == len-1) sWidth += ( this->gWidth[index] + this->gdX[index]); - else sWidth += this->gxAdvance[index]; + if (n == 0) sWidth -= gdX[index]; + if (n == len-1) sWidth += ( gWidth[index] + gdX[index]); + else sWidth += gxAdvance[index]; } - else sWidth += this->gFont.spaceWidth + 1; + else sWidth += gFont.spaceWidth + 1; } - createSprite(sWidth, this->gFont.yAdvance); + createSprite(sWidth, gFont.yAdvance); - if (this->textbgcolor != TFT_BLACK) fillSprite(this->textbgcolor); + if (textbgcolor != TFT_BLACK) fillSprite(textbgcolor); } n = 0; @@ -2668,22 +2540,22 @@ void TFT_eSprite::printToSprite(char *cbuffer, uint16_t len) //String string) int16_t TFT_eSprite::printToSprite(int16_t x, int16_t y, uint16_t index) { bool newSprite = !_created; - int16_t sWidth = this->gWidth[index]; + int16_t sWidth = gWidth[index]; if (newSprite) { - createSprite(sWidth, this->gFont.yAdvance); + createSprite(sWidth, gFont.yAdvance); - if (this->textbgcolor != TFT_BLACK) fillSprite(this->textbgcolor); + if (textbgcolor != TFT_BLACK) fillSprite(textbgcolor); - drawGlyph(this->gUnicode[index]); + drawGlyph(gUnicode[index]); - pushSprite(x + this->gdX[index], y, this->textbgcolor); + pushSprite(x + gdX[index], y, textbgcolor); deleteSprite(); } - else drawGlyph(this->gUnicode[index]); + else drawGlyph(gUnicode[index]); - return this->gxAdvance[index]; + return gxAdvance[index]; } #endif diff --git a/Extensions/Sprite.h b/Extensions/Sprite.h index 3b8d556..73c168c 100644 --- a/Extensions/Sprite.h +++ b/Extensions/Sprite.h @@ -85,9 +85,6 @@ class TFT_eSprite : public TFT_eSPI { // Fill a rectangular area with a color (aka draw a filled rectangle) 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); // Not needed, so uses TFT class function - // Set the coordinate rotation of the Sprite (for 1bpp Sprites only) // Note: this uses coordinate rotation and is primarily for ePaper which does not support // CGRAM rotation (like TFT drivers do) within the displays internal hardware @@ -99,11 +96,6 @@ class TFT_eSprite : public TFT_eSPI { // Push a rotated copy of Sprite to another different Sprite with optional transparent colour bool pushRotated(TFT_eSprite *spr, int16_t angle, int32_t transp = -1); // Using fixed point maths - // Set and get the pivot point for this Sprite - void setPivot(int16_t x, int16_t y); - int16_t getPivotX(void), - getPivotY(void); - // Get the TFT bounding box for a rotated copy of this Sprite bool getRotatedBounds(int16_t angle, int16_t *min_x, int16_t *min_y, int16_t *max_x, int16_t *max_y); // Get the destination Sprite bounding box for a rotated copy of this Sprite @@ -121,13 +113,9 @@ class TFT_eSprite : public TFT_eSPI { uint16_t readPixelValue(int32_t x, int32_t y); // Write an image (colour bitmap) to the sprite. - void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, uint16_t *data); + void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, uint16_t *data, uint8_t sbpp = 0); void pushImage(int32_t x0, int32_t y0, int32_t w, int32_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); @@ -148,9 +136,6 @@ class TFT_eSprite : public TFT_eSPI { 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); @@ -175,23 +160,17 @@ class TFT_eSprite : public TFT_eSPI { uint16_t *_colorMap; // color map: 16 entries, used with 4 bit color map. - int16_t _xPivot; // x pivot point coordinate - int16_t _yPivot; // y pivot point coordinate int32_t _sinra; int32_t _cosra; bool _created; // A Sprite has been created and memory reserved bool _gFont = false; -// int32_t _icursor_x, _icursor_y; - uint8_t _rotation = 0; 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 - bool _iswapBytes; // Swap the byte order for Sprite pushImage() - int32_t _iwidth, _iheight; // Sprite memory image bit width and height (swapped during rotations) int32_t _dwidth, _dheight; // Real display width and height (for <8bpp Sprites) int32_t _bitwidth; // Sprite image bit width for drawPixel (for <8bpp Sprites, not swapped) diff --git a/README.md b/README.md index fe41dff..13b7210 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,53 @@ -# Sprite class change -The Sprite class has been updated to remove an inconsistency for the setSwapBytes() function. Although all the examples are unchanged, user sketches may be affected. If the colors of the sprite change when loading this new version 2.2.16 then it may be necessary to change the swap bytes setting, e.g. for a sprite instance "spr" use either: spr.setSwapBytes(true) or spr.setSwapBytes(false) to correct the colour. - # News -1. The library now provides a "viewport" capability. See "Viewport_Demo" and "Viewport_graphicstest" examples. When a viewport is defined graphics will only appear within that window. The coordinate datum by default moves to the top left corner of the viewport, but can optionally remain at top left corner of TFT. The GUIslice library will make use of this feature to speed up the rendering of GUI objects ([see #769](https://github.com/Bodmer/TFT_eSPI/issues/769)). +1. Viewports can now be applied to sprites e.g. spr.setViewport(5, 5, 20, 20); so graphics can be restricted to a particular area of the sprite. This operates in the same way as the TFT viewports, see 2. below. -2. The library now supports SSD1963 based screen, this has been tested on a [480x800 screen](https://www.buydisplay.com/7-tft-screen-touch-lcd-display-module-w-ssd1963-controller-board-mcu) with an ESP32. The interface is 8 bit parallel only as that controller does not support a SPI interface. +2. The library now provides a "viewport" capability. See "Viewport_Demo" and "Viewport_graphicstest" examples. When a viewport is defined graphics will only appear within that window. The coordinate datum by default moves to the top left corner of the viewport, but can optionally remain at top left corner of TFT. The GUIslice library will make use of this feature to speed up the rendering of GUI objects ([see #769](https://github.com/Bodmer/TFT_eSPI/issues/769)). -3. A companion library [U8g2_for_TFT_eSPI](https://github.com/Bodmer/U8g2_for_TFT_eSPI) has been created to allow U8g2 library fonts to be used with TFT_eSPI. +3. The library now supports SSD1963 based screen, this has been tested on a [480x800 screen](https://www.buydisplay.com/7-tft-screen-touch-lcd-display-module-w-ssd1963-controller-board-mcu) with an ESP32. The interface is 8 bit parallel only as that controller does not support a SPI interface. -4. The library now supports SPI DMA transfers for both ESP32 and STM32 processors. The DMA Test examples now work on the ESP32 for SPI displays (excluding RPi type and ILI9488). +4. A companion library [U8g2_for_TFT_eSPI](https://github.com/Bodmer/U8g2_for_TFT_eSPI) has been created to allow U8g2 library fonts to be used with TFT_eSPI. -5. A new option has been added for STM32 processors to optimise performance where Port A (or B) pins 0-7 are used for the 8 bit parallel interface data pins 0-7 to the TFT. This gives a dramatic 8 times better rendering performance for the lower clock rate STM32 processors such as the STM32F103 "Blue Pill" or STM411 "Black Pill" since no time consuming data bit manipulation is required. See setup file "User_Setups/Setup35_ILI9341_STM32_Port_Bus.h". +5. The library now supports SPI DMA transfers for both ESP32 and STM32 processors. The DMA Test examples now work on the ESP32 for SPI displays (excluding RPi type and ILI9488). -6. A new "Animated_dial" example has been added to show how dials can be created using a rotated Sprite for the needle. To run this example the TFT must support reading from the screen RAM. The dial rim and scale is a jpeg image, created using a paint program. +6. A new option has been added for STM32 processors to optimise performance where Port A (or B) pins 0-7 are used for the 8 bit parallel interface data pins 0-7 to the TFT. This gives a dramatic 8 times better rendering performance for the lower clock rate STM32 processors such as the STM32F103 "Blue Pill" or STM411 "Black Pill" since no time consuming data bit manipulation is required. See setup file "User_Setups/Setup35_ILI9341_STM32_Port_Bus.h". + +7. A new "Animated_dial" example has been added to show how dials can be created using a rotated Sprite for the needle. To run this example the TFT must support reading from the screen RAM. The dial rim and scale is a jpeg image, created using a paint program. ![Animated_dial](https://i.imgur.com/S736Rg6.png) -7. Anti-aliased (smooth) fonts can now be stored as arrays in FLASH (program) memory. This means that processors such as STM32 that do not have SPIFFS support can use the fonts. The processor must have sufficient FLASH memory to store the fonts used. - -8. The Sprite class now supports 4 bits per pixel with a 16 color palette. Three new examples have been added. +8. Anti-aliased (smooth) fonts can now be stored as arrays in FLASH (program) memory. This means that processors such as STM32 that do not have SPIFFS support can use the fonts. The processor must have sufficient FLASH memory to store the fonts used. # TFT_eSPI -An Arduino IDE compatible graphics and fonts library for 32 bit processors. The library is targetted at 32 bit processors, it has been performance optimised for STM32, ESP8266 and ESP32 types. The library includes drivers for ILI9341, ILI9163, ST7735, S6D02A1, ILI9481, ILI9486, ILI9488, HX8357D, ST7789 and ST7796 based TFT displays that support SPI or 8 bit parallel (parallel not supported on ESP8266). The library can be loaded using the Arduino IDE's Library Manager. +An Arduino IDE compatible graphics and fonts library for 32 bit processors. The library is targetted at 32 bit processors, it has been performance optimised for STM32, ESP8266 and ESP32 types. The library can be loaded using the Arduino IDE's Library Manager. Direct Memory Access (DMA) can be used with the ESP32 and STM32 processors to improve rendering performance. -8 bit parallel interface TFTs (e.g. UNO format mcufriend shields) can used with the STM32 Nucleo 64/144 range or the UNO format ESP32 (see below for ESP32). +"Four wire" SPI and 8 bit parallel interfaces are supported. Due to lack of GPIO pins the 8 bit parallel interface is NOT supported on the ESP8266. 8 bit parallel interface TFTs (e.g. UNO format mcufriend shields) can used with the STM32 Nucleo 64/144 range or the UNO format ESP32 (see below for ESP32). -The library supports TFT displays designed for the Raspberry Pi (RPi) that are based on a ILI9486 or ST7796 driver chip with a 480 x 320 pixel screen. The ILI9486 RPi 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). The RPi ST7796 display is supported and is superior to the Waveshare design, it must be of the MHS-4.0 inch Display-B type. +Displays using the following controllers are supported: -Some displays permit the internal TFT screen RAM to be read. The library supports reading from ILI9341, ST7789 and ILI9488 SPI and other supported 8 bit parallel displays. The TFT_Screen_Capture example allows full screens to be captured and sent to a PC, this is handy to create program documentation. +* ILI9163 +* ILI9225 +* ILI9341 +* ILI9481 +* ILI9486 +* ILI9488 +* HX8357D +* S6D02A1 +* SSD1963 +* ST7735 +* ST7789 +* ST7796 -Support has been added recently for Waveshare 2 and 3 colour ePaper displays using full frame buffers. This addition is currently relatively immature and thus only one example has been provided. +ILI9341 and ST7796 SPI based displays are recommended as starting point for experimenting with this library. + +The library supports some TFT displays designed for the Raspberry Pi (RPi) that are based on a ILI9486 or ST7796 driver chip with a 480 x 320 pixel screen. The ILI9486 RPi 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. Note that due to design variations between these displays not all RPi displays will work with this library, so purchaing a RPi display of these types soley for use with this library is not recommended. + +The "good" RPi displays are the [MHS-4.0 inch Display-B type ST7796](http://www.lcdwiki.com/MHS-4.0inch_Display-B) and [Waveshare 3.5 inch ILI9486 Type C](https://www.waveshare.com/wiki/3.5inch_RPi_LCD_(C)) displays are supported and provides good performance. These have a dedicated controller and can be clocked at up to 80MHz with the ESP32 (55MHz with STM32 and 40MHz with ESP8266). + +Some displays permit the internal TFT screen RAM to be read, some of the examples use this feature. The TFT_Screen_Capture example allows full screens to be captured and sent to a PC, this is handy to create program documentation. + +The library supports Waveshare 2 and 3 colour ePaper displays using full frame buffers. This addition is relatively immature and thus only one example has been provided. The library includes a "Sprite" class, this enables flicker free updates of complex graphics. Direct writes to the TFT with graphics functions are still available, so existing sketches do not need to be changed. diff --git a/TFT_Drivers/ILI9225_Defines.h b/TFT_Drivers/ILI9225_Defines.h index e6726a0..1a083e0 100644 --- a/TFT_Drivers/ILI9225_Defines.h +++ b/TFT_Drivers/ILI9225_Defines.h @@ -3,50 +3,82 @@ #define TFT_WIDTH 176 #define TFT_HEIGHT 220 -/* ILI9225 LCD Registers */ -#define ILI9225_DRIVER_OUTPUT_CTRL (0x01u) // Driver Output Control -#define ILI9225_LCD_AC_DRIVING_CTRL (0x02u) // LCD AC Driving Control -#define ILI9225_ENTRY_MODE (0x03u) // Entry Mode -#define ILI9225_DISP_CTRL1 (0x07u) // Display Control 1 -#define ILI9225_BLANK_PERIOD_CTRL1 (0x08u) // Blank Period Control -#define ILI9225_FRAME_CYCLE_CTRL (0x0Bu) // Frame Cycle Control -#define ILI9225_INTERFACE_CTRL (0x0Cu) // Interface Control -#define ILI9225_OSC_CTRL (0x0Fu) // Osc Control -#define ILI9225_POWER_CTRL1 (0x10u) // Power Control 1 -#define ILI9225_POWER_CTRL2 (0x11u) // Power Control 2 -#define ILI9225_POWER_CTRL3 (0x12u) // Power Control 3 -#define ILI9225_POWER_CTRL4 (0x13u) // Power Control 4 -#define ILI9225_POWER_CTRL5 (0x14u) // Power Control 5 -#define ILI9225_VCI_RECYCLING (0x15u) // VCI Recycling -#define ILI9225_RAM_ADDR_SET1 (0x20u) // Horizontal GRAM Address Set -#define ILI9225_RAM_ADDR_SET2 (0x21u) // Vertical GRAM Address Set -#define ILI9225_GRAM_DATA_REG (0x22u) // GRAM Data Register -#define ILI9225_GATE_SCAN_CTRL (0x30u) // Gate Scan Control Register -#define ILI9225_VERTICAL_SCROLL_CTRL1 (0x31u) // Vertical Scroll Control 1 Register -#define ILI9225_VERTICAL_SCROLL_CTRL2 (0x32u) // Vertical Scroll Control 2 Register -#define ILI9225_VERTICAL_SCROLL_CTRL3 (0x33u) // Vertical Scroll Control 3 Register -#define ILI9225_PARTIAL_DRIVING_POS1 (0x34u) // Partial Driving Position 1 Register -#define ILI9225_PARTIAL_DRIVING_POS2 (0x35u) // Partial Driving Position 2 Register -#define ILI9225_HORIZONTAL_WINDOW_ADDR1 (0x36u) // Horizontal Address Start Position -#define ILI9225_HORIZONTAL_WINDOW_ADDR2 (0x37u) // Horizontal Address End Position -#define ILI9225_VERTICAL_WINDOW_ADDR1 (0x38u) // Vertical Address Start Position -#define ILI9225_VERTICAL_WINDOW_ADDR2 (0x39u) // Vertical Address End Position -#define ILI9225_GAMMA_CTRL1 (0x50u) // Gamma Control 1 -#define ILI9225_GAMMA_CTRL2 (0x51u) // Gamma Control 2 -#define ILI9225_GAMMA_CTRL3 (0x52u) // Gamma Control 3 -#define ILI9225_GAMMA_CTRL4 (0x53u) // Gamma Control 4 -#define ILI9225_GAMMA_CTRL5 (0x54u) // Gamma Control 5 -#define ILI9225_GAMMA_CTRL6 (0x55u) // Gamma Control 6 -#define ILI9225_GAMMA_CTRL7 (0x56u) // Gamma Control 7 -#define ILI9225_GAMMA_CTRL8 (0x57u) // Gamma Control 8 -#define ILI9225_GAMMA_CTRL9 (0x58u) // Gamma Control 9 -#define ILI9225_GAMMA_CTRL10 (0x59u) // Gamma Control 10 - -#define TFT_INVOFF 0x20 -#define TFT_INVON 0x21 +// Generic commands used by TFT_eSPI.cpp +#define TFT_NOP 0x00 #define TFT_SWRST 0x28 -// Delay between some initialisation commands -#define TFT_INIT_DELAY 0x80 // Not used unless commandlist invoked +#define TFT_CASET 0 +#define TFT_PASET 0 + +#define TFT_CASET1 ILI9225_HORIZONTAL_WINDOW_ADDR2 +#define TFT_CASET2 ILI9225_HORIZONTAL_WINDOW_ADDR1 + +#define TFT_PASET1 ILI9225_VERTICAL_WINDOW_ADDR2 +#define TFT_PASET2 ILI9225_VERTICAL_WINDOW_ADDR1 + +#define TFT_RAM_ADDR1 ILI9225_RAM_ADDR_SET1 +#define TFT_RAM_ADDR2 ILI9225_RAM_ADDR_SET2 + +#define TFT_RAMWR ILI9225_GRAM_DATA_REG + +#define TFT_MAD_BGR 0x10 +#define TFT_MAD_RGB 0x00 + +#ifdef TFT_RGB_ORDER + #if (TFT_RGB_ORDER == 1) + #define TFT_MAD_COLOR_ORDER TFT_MAD_RGB + #else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR + #endif +#else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR +#endif + +// Not used +#define TFT_INVOFF 0x00 +#define TFT_INVON 0x00 +#define TFT_RAMRD 0x00 +#define TFT_IDXRD 0x00 + +/* ILI9225 Registers */ +#define ILI9225_DRIVER_OUTPUT_CTRL 0x01 // Driver Output Control +#define ILI9225_LCD_AC_DRIVING_CTRL 0x02 // LCD AC Driving Control +#define ILI9225_ENTRY_MODE 0x03 // Entry Mode +#define ILI9225_DISP_CTRL1 0x07 // Display Control 1 +#define ILI9225_BLANK_PERIOD_CTRL1 0x08 // Blank Period Control +#define ILI9225_FRAME_CYCLE_CTRL 0x0B // Frame Cycle Control +#define ILI9225_INTERFACE_CTRL 0x0C // Interface Control +#define ILI9225_OSC_CTRL 0x0F // Osc Control +#define ILI9225_POWER_CTRL1 0x10 // Power Control 1 +#define ILI9225_POWER_CTRL2 0x11 // Power Control 2 +#define ILI9225_POWER_CTRL3 0x12 // Power Control 3 +#define ILI9225_POWER_CTRL4 0x13 // Power Control 4 +#define ILI9225_POWER_CTRL5 0x14 // Power Control 5 +#define ILI9225_VCI_RECYCLING 0x15 // VCI Recycling +#define ILI9225_RAM_ADDR_SET1 0x20 // Horizontal GRAM Address Set +#define ILI9225_RAM_ADDR_SET2 0x21 // Vertical GRAM Address Set +#define ILI9225_GRAM_DATA_REG 0x22 // GRAM Data Register +#define ILI9225_GATE_SCAN_CTRL 0x30 // Gate Scan Control Register +#define ILI9225_VERTICAL_SCROLL_CTRL1 0x31 // Vertical Scroll Control 1 Register +#define ILI9225_VERTICAL_SCROLL_CTRL2 0x32 // Vertical Scroll Control 2 Register +#define ILI9225_VERTICAL_SCROLL_CTRL3 0x33 // Vertical Scroll Control 3 Register +#define ILI9225_PARTIAL_DRIVING_POS1 0x34 // Partial Driving Position 1 Register +#define ILI9225_PARTIAL_DRIVING_POS2 0x35 // Partial Driving Position 2 Register +#define ILI9225_HORIZONTAL_WINDOW_ADDR1 0x36 // Horizontal Address Start Position +#define ILI9225_HORIZONTAL_WINDOW_ADDR2 0x37 // Horizontal Address End Position +#define ILI9225_VERTICAL_WINDOW_ADDR1 0x38 // Vertical Address Start Position +#define ILI9225_VERTICAL_WINDOW_ADDR2 0x39 // Vertical Address End Position +#define ILI9225_GAMMA_CTRL1 0x50 // Gamma Control 1 +#define ILI9225_GAMMA_CTRL2 0x51 // Gamma Control 2 +#define ILI9225_GAMMA_CTRL3 0x52 // Gamma Control 3 +#define ILI9225_GAMMA_CTRL4 0x53 // Gamma Control 4 +#define ILI9225_GAMMA_CTRL5 0x54 // Gamma Control 5 +#define ILI9225_GAMMA_CTRL6 0x55 // Gamma Control 6 +#define ILI9225_GAMMA_CTRL7 0x56 // Gamma Control 7 +#define ILI9225_GAMMA_CTRL8 0x57 // Gamma Control 8 +#define ILI9225_GAMMA_CTRL9 0x58 // Gamma Control 9 +#define ILI9225_GAMMA_CTRL10 0x59 // Gamma Control 10 + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x00 // Not used unless commandlist invoked -#define TFT_MAD_BGR 0x10 \ No newline at end of file diff --git a/TFT_Drivers/ILI9225_Init.h b/TFT_Drivers/ILI9225_Init.h index 74c0907..741be43 100644 --- a/TFT_Drivers/ILI9225_Init.h +++ b/TFT_Drivers/ILI9225_Init.h @@ -1,65 +1,56 @@ - // This is the command sequence that initialises the ILI9225 driver { - /* Start Initial Sequence */ - - //LCD Init For 2.2inch LCD Panel with ILI9225. writecommand(ILI9225_POWER_CTRL1); - writedata(0x00);writedata(0x00); // Set SAP,DSTB,STB + writedata(0x00);writedata(0x00); writecommand(ILI9225_POWER_CTRL2); - writedata(0x00);writedata(0x00); // Set APON,PON,AON,VCI1EN,VC + writedata(0x00);writedata(0x00); writecommand(ILI9225_POWER_CTRL3); - writedata(0x00);writedata(0x00); // Set BT,DC1,DC2,DC3 + writedata(0x00);writedata(0x00); writecommand(ILI9225_POWER_CTRL4); - writedata(0x00);writedata(0x00); // Set GVDD + writedata(0x00);writedata(0x00); writecommand(ILI9225_POWER_CTRL5); - writedata(0x00);writedata(0x00); // Set VCOMH/VCOML voltage - spi_end(); + writedata(0x00);writedata(0x00); + delay(40); - spi_begin(); - // Power-on sequence writecommand(ILI9225_POWER_CTRL2); - writedata(0x00);writedata(0x18); // Set APON,PON,AON,VCI1EN,VC + writedata(0x00);writedata(0x18); writecommand(ILI9225_POWER_CTRL3); - writedata(0x61);writedata(0x21); // Set BT,DC1,DC2,DC3 + writedata(0x61);writedata(0x21); writecommand(ILI9225_POWER_CTRL4); - writedata(0x00);writedata(0x6F); // Set GVDD /*007F 0088 */ + writedata(0x00);writedata(0x6F); writecommand(ILI9225_POWER_CTRL5); - writedata(0x49);writedata(0x5F); // Set VCOMH/VCOML voltage + writedata(0x49);writedata(0x5F); writecommand(ILI9225_POWER_CTRL1); - writedata(0x08);writedata(0x00); // Set SAP,DSTB,STB - spi_end(); + writedata(0x08);writedata(0x00); + delay(10); - spi_begin(); writecommand(ILI9225_POWER_CTRL2); - writedata(0x10);writedata(0x3B); // Set APON,PON,AON,VCI1EN,VC - spi_end(); + writedata(0x10);writedata(0x3B); + delay(50); - spi_begin(); writecommand(ILI9225_LCD_AC_DRIVING_CTRL); - writedata(0x01);writedata(0x00); // set 1 line inversion + writedata(0x01);writedata(0x00); writecommand(ILI9225_DISP_CTRL1); - writedata(0x00);writedata(0x00); // Display off + writedata(0x00);writedata(0x00); writecommand(ILI9225_BLANK_PERIOD_CTRL1); - writedata(0x08);writedata(0x08); // set the back porch and front porch + writedata(0x08);writedata(0x08); writecommand(ILI9225_FRAME_CYCLE_CTRL); - writedata(0x11);writedata(0x00); // set the clocks number per line + writedata(0x11);writedata(0x00); writecommand(ILI9225_INTERFACE_CTRL); - writedata(0x00);writedata(0x00); // CPU interface + writedata(0x00);writedata(0x00); writecommand(ILI9225_OSC_CTRL); - writedata(0x0D);writedata(0x01); // Set Osc /*0e01*/ + writedata(0x0D);writedata(0x01); writecommand(ILI9225_VCI_RECYCLING); - writedata(0x00);writedata(0x20); // Set VCI recycling + writedata(0x00);writedata(0x20); writecommand(ILI9225_RAM_ADDR_SET1); - writedata(0x00);writedata(0x00); // RAM Address + writedata(0x00);writedata(0x00); writecommand(ILI9225_RAM_ADDR_SET2); - writedata(0x00);writedata(0x00); // RAM Address + writedata(0x00);writedata(0x00); - /* Set GRAM area */ writecommand(ILI9225_GATE_SCAN_CTRL); writedata(0x00);writedata(0x00); writecommand(ILI9225_VERTICAL_SCROLL_CTRL1); @@ -106,9 +97,9 @@ writecommand(ILI9225_DISP_CTRL1); writedata(0x00);writedata(0x12); - spi_end(); delay(50); - spi_begin(); + writecommand(ILI9225_DISP_CTRL1); writedata(0x10);writedata(0x17); + } \ No newline at end of file diff --git a/TFT_Drivers/ILI9225_Rotation.h b/TFT_Drivers/ILI9225_Rotation.h index 661c6cc..d7cef9a 100644 --- a/TFT_Drivers/ILI9225_Rotation.h +++ b/TFT_Drivers/ILI9225_Rotation.h @@ -8,7 +8,7 @@ writecommand(ILI9225_DRIVER_OUTPUT_CTRL); writedata(0x01);writedata(0x1C); writecommand(ILI9225_ENTRY_MODE); - writedata(TFT_MAD_BGR);writedata(0x30); + writedata(TFT_MAD_COLOR_ORDER);writedata(0x30); _width = _init_width; _height = _init_height; break; @@ -16,7 +16,7 @@ writecommand(ILI9225_DRIVER_OUTPUT_CTRL); writedata(0x00);writedata(0x1C); writecommand(ILI9225_ENTRY_MODE); - writedata(TFT_MAD_BGR);writedata(0x38); + writedata(TFT_MAD_COLOR_ORDER);writedata(0x38); _width = _init_height; _height = _init_width; break; @@ -24,7 +24,7 @@ writecommand(ILI9225_DRIVER_OUTPUT_CTRL); writedata(0x02);writedata(0x1C); writecommand(ILI9225_ENTRY_MODE); - writedata(TFT_MAD_BGR);writedata(0x30); + writedata(TFT_MAD_COLOR_ORDER);writedata(0x30); _width = _init_width; _height = _init_height; break; @@ -32,7 +32,7 @@ writecommand(ILI9225_DRIVER_OUTPUT_CTRL); writedata(0x03);writedata(0x1C); writecommand(ILI9225_ENTRY_MODE); - writedata(TFT_MAD_BGR);writedata(0x38); + writedata(TFT_MAD_COLOR_ORDER);writedata(0x38); _width = _init_height; _height = _init_width; break; diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 8211823..f67438b 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -57,11 +57,13 @@ inline void TFT_eSPI::begin_tft_write(void){ locked = false; spi.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, TFT_SPI_MODE)); CS_L; + SET_BUS_WRITE_MODE } #else CS_L; -#endif SET_BUS_WRITE_MODE; +#endif + } /*************************************************************************************** @@ -76,10 +78,10 @@ inline void TFT_eSPI::end_tft_write(void){ CS_H; spi.endTransaction(); } + SET_BUS_READ_MODE; } - SET_BUS_READ_MODE; #else - if(!inTransaction) {CS_H;} + if(!inTransaction) {CS_H; SET_BUS_READ_MODE;} #endif } @@ -111,26 +113,25 @@ inline void TFT_eSPI::begin_tft_read(void){ ***************************************************************************************/ void TFT_eSPI::setViewport(int32_t x, int32_t y, int32_t w, int32_t h, bool vpDatum) { - // Viewport + // Viewport metrics (not clipped) _xDatum = x; // Datum x position in screen coordinates _yDatum = y; // Datum y position in screen coordinates _xWidth = w; // Viewport width _yHeight = h; // Viewport height - // Clipped viewport - _vpX = 0; // Viewport top left corner x coordinate - _vpY = 0; // Viewport top left corner y coordinate - _vpW = _width; // Equivalent of TFT width (Nb: viewport right edge coord + 1) - _vpH = _height; // Equivalent of TFT height (Nb: viewport bottom edge coord + 1) - + // Full size default viewport _vpDatum = false; // Datum is at top left corner of screen (true = top left of viewport) - _vpOoB = false; // Out of Bounds flag (true is all of viewport is off screen) + _vpOoB = false; // Out of Bounds flag (true is all of viewport is off screen) + _vpX = 0; // Viewport top left corner x coordinate + _vpY = 0; // Viewport top left corner y coordinate + _vpW = width(); // Equivalent of TFT width (Nb: viewport right edge coord + 1) + _vpH = height(); // Equivalent of TFT height (Nb: viewport bottom edge coord + 1) // Clip viewport to screen area if (x<0) { w += x; x = 0; } if (y<0) { h += y; y = 0; } - if ((x + w) > _width ) { w = _width - x; } - if ((y + h) > _height) { h = _height - y; } + if ((x + w) > width() ) { w = width() - x; } + if ((y + h) > height() ) { h = height() - y; } //Serial.print(" x=");Serial.print( x);Serial.print(", y=");Serial.print( y); //Serial.print(", w=");Serial.print(w);Serial.print(", h=");Serial.println(h); @@ -141,21 +142,21 @@ void TFT_eSPI::setViewport(int32_t x, int32_t y, int32_t w, int32_t h, bool vpDa // Set default values and Out of Bounds flag in case of error _xDatum = 0; _yDatum = 0; - _xWidth = _width; - _yHeight = _height; + _xWidth = width(); + _yHeight = height(); _vpOoB = true; // Set Out of Bounds flag to inhibit all drawing return; } if (!vpDatum) { - _xDatum = 0; // Reset to top left of screen if not useing a viewport datum + _xDatum = 0; // Reset to top left of screen if not using a viewport datum _yDatum = 0; - _xWidth = _width; - _yHeight = _height; + _xWidth = width(); + _yHeight = height(); } - // Store the on screen viewport metrics and datum position + // Store the clipped screen viewport metrics and datum position _vpX = x; _vpY = y; _vpW = x + w; @@ -205,23 +206,22 @@ bool TFT_eSPI::checkViewport(int32_t x, int32_t y, int32_t w, int32_t h) ***************************************************************************************/ void TFT_eSPI::resetViewport(void) { - // Reset viewport to the whole screen + // Reset viewport to the whole screen (or sprite) area + _vpDatum = false; + _vpOoB = false; _xDatum = 0; _yDatum = 0; _vpX = 0; _vpY = 0; - _vpW = _width; - _vpH = _height; - _xWidth = _width; - _yHeight = _height; - - _vpDatum = false; - _vpOoB = false; + _vpW = width(); + _vpH = height(); + _xWidth = width(); + _yHeight = height(); } /*************************************************************************************** ** Function name: getViewportX -** Description: Get x position of the viewport +** Description: Get x position of the viewport datum ***************************************************************************************/ int32_t TFT_eSPI::getViewportX(void) { @@ -230,7 +230,7 @@ int32_t TFT_eSPI::getViewportX(void) /*************************************************************************************** ** Function name: getViewportY -** Description: Get y position of the viewport +** Description: Get y position of the viewport datum ***************************************************************************************/ int32_t TFT_eSPI::getViewportY(void) { @@ -243,7 +243,7 @@ int32_t TFT_eSPI::getViewportY(void) ***************************************************************************************/ int32_t TFT_eSPI::getViewportWidth(void) { - return _vpW - _vpX; + return _xWidth; } /*************************************************************************************** @@ -252,12 +252,12 @@ int32_t TFT_eSPI::getViewportWidth(void) ***************************************************************************************/ int32_t TFT_eSPI::getViewportHeight(void) { - return _vpH - _vpY; + return _yHeight; } /*************************************************************************************** ** Function name: getViewportDatum -** Description: Get datum of the viewport (true = viewport corner) +** Description: Get datum flag of the viewport (true = viewport corner) ***************************************************************************************/ bool TFT_eSPI::getViewportDatum(void) { @@ -270,37 +270,52 @@ bool TFT_eSPI::getViewportDatum(void) ***************************************************************************************/ void TFT_eSPI::frameViewport(uint16_t color, int32_t w) { + // Save datum position + bool _dT = _vpDatum; + // If w is positive the frame is drawn inside the viewport // a large positive width will clear the screen inside the viewport if (w>0) { - fillRect(_vpX, _vpY, _vpW - _vpX, w, color); - fillRect(_vpX, _vpY + w, w, _vpH - _vpY - w - w, color); - fillRect(_vpW - w, _vpY + w, w, _vpH - _vpY - w - w, color); - fillRect(_vpX, _vpH - w, _vpW - _vpX, w, color); + // Set vpDatum true to simplify coordinate derivation + _vpDatum = true; + fillRect(0, 0, _vpW - _vpX, w, color); // Top + fillRect(0, w, w, _vpH - _vpY - w - w, color); // Left + fillRect(_xWidth - w, w, w, _yHeight - w - w, color); // Right + fillRect(0, _yHeight - w, _xWidth, w, color); // Bottom } else // If w is negative the frame is drawn outside the viewport // a large negative width will clear the screen outside the viewport { w = -w; - int32_t _xTemp = _vpX; _vpX = 0; - int32_t _yTemp = _vpY; _vpY = 0; - int32_t _wTemp = _vpW; _vpW = _width; - int32_t _hTemp = _vpH; _vpH = _height; - bool _dTemp = _vpDatum; _vpDatum = false; + + // Save old values + int32_t _xT = _vpX; _vpX = 0; + int32_t _yT = _vpY; _vpY = 0; + int32_t _wT = _vpW; + int32_t _hT = _vpH; - fillRect(_xTemp - _xDatum, _yTemp - w - _yDatum, _wTemp - _xTemp + w + w, w, color); - fillRect(_xTemp - w - _xDatum, _yTemp - _yDatum, w, _hTemp - _yTemp, color); - fillRect(_wTemp - _xDatum, _yTemp - _yDatum, w, _hTemp - _yTemp, color); - fillRect(_xTemp - w - _xDatum, _hTemp - _yDatum, _wTemp - _xTemp + w + w, w, color); + // Set vpDatum false so frame can be drawn outside window + _vpDatum = false; // When false the full width and height is accessed + _vpH = height(); + _vpW = width(); - _vpX = _xTemp; - _vpY = _yTemp; - _vpW = _wTemp; - _vpH = _hTemp; - _vpDatum = _dTemp; + // Draw frame + fillRect(_xT - w - _xDatum, _yT - w - _yDatum, _wT - _xT + w + w, w, color); // Top + fillRect(_xT - w - _xDatum, _yT - _yDatum, w, _hT - _yT, color); // Left + fillRect(_wT - _xDatum, _yT - _yDatum, w, _hT - _yT, color); // Right + fillRect(_xT - w - _xDatum, _hT - _yDatum, _wT - _xT + w + w, w, color); // Bottom + + // Restore old values + _vpX = _xT; + _vpY = _yT; + _vpW = _wT; + _vpH = _hT; } + + // Restore vpDatum + _vpDatum = _dT; } /*************************************************************************************** @@ -438,8 +453,8 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) addr_row = 0xFFFF; addr_col = 0xFFFF; - _xpivot = 0; - _ypivot = 0; + _xPivot = 0; + _yPivot = 0; cspinmask = 0; dcpinmask = 0; @@ -1365,6 +1380,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *da begin_tft_write(); inTransaction = true; + bool swap = _swapBytes; setWindow(x, y, x + dw - 1, y + dh - 1); // Sets CS low and sent RAMWR @@ -1373,7 +1389,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *da if (bpp8) { - bool swap = _swapBytes; _swapBytes = false; + _swapBytes = false; uint8_t blue[] = {0, 11, 21, 31}; // blue 2 to 5 bit colour lookup table @@ -1413,7 +1429,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *da } else if (cmap != nullptr) // Must be 4bpp { - bool swap = _swapBytes; _swapBytes = true; + _swapBytes = true; w = (w+1) & 0xFFFE; // if this is a sprite, w will already be even; this does no harm. bool splitFirst = (dx & 0x01) != 0; // split first means we have to push a single px from the left of the sprite / image @@ -1464,35 +1480,24 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *da } else // Must be 1bpp { - bool swap = _swapBytes; _swapBytes = false; - while (dh--) { - w = (w+7) & 0xFFF8; + _swapBytes = false; - int32_t len = dw; - uint8_t* ptr = data; + uint32_t ww = (w+7)>>3; // Width of source image line in bytes + for (int32_t yp = dy; yp < dy + dh; yp++) + { uint8_t* linePtr = (uint8_t*)lineBuf; - uint8_t bits = 8; - while(len>0) { - if (len < 8) bits = len; - uint32_t xp = dx; - for (uint16_t i = 0; i < bits; i++) { - uint8_t col = (ptr[(xp + dy * w)>>3] << (xp & 0x7)) & 0x80; - if (col) {*linePtr++ = bitmap_fg>>8; *linePtr++ = (uint8_t) bitmap_fg;} - else {*linePtr++ = bitmap_bg>>8; *linePtr++ = (uint8_t) bitmap_bg;} - //if (col) drawPixel((dw-len)+xp,h-dh,bitmap_fg); - //else drawPixel((dw-len)+xp,h-dh,bitmap_bg); - xp++; - } - ptr++; - len -= 8; + for (int32_t xp = dx; xp < dx + dw; xp++) + { + uint16_t col = (data[(xp>>3)] & (0x80 >> (xp & 0x7)) ); + if (col) {*linePtr++ = bitmap_fg>>8; *linePtr++ = (uint8_t) bitmap_fg;} + else {*linePtr++ = bitmap_bg>>8; *linePtr++ = (uint8_t) bitmap_bg;} } - + data += ww; pushPixels(lineBuf, dw); - dy++; } - _swapBytes = swap; // Restore old value } + _swapBytes = swap; // Restore old value inTransaction = false; end_tft_write(); } @@ -1508,6 +1513,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *da begin_tft_write(); inTransaction = true; + bool swap = _swapBytes; int32_t xe = x + dw - 1, ye = y + dh - 1; @@ -1515,7 +1521,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *da uint16_t lineBuf[dw]; if (bpp8) { // 8 bits per pixel - bool swap = _swapBytes; _swapBytes = false; + _swapBytes = false; data += dx + dy * w; @@ -1527,8 +1533,6 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *da uint8_t msbColor = 0; uint8_t lsbColor = 0; - //int32_t spx = x, spy = y; - while (dh--) { int32_t len = dw; uint8_t* ptr = data; @@ -1571,11 +1575,10 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *da y++; data += w; } - _swapBytes = swap; // Restore old value } else if (cmap != nullptr) // 4bpp with color map { - bool swap = _swapBytes; _swapBytes = true; + _swapBytes = true; w = (w+1) & 0xFFFE; // here we try to recreate iwidth from dwidth. bool splitFirst = ((dx & 0x01) != 0); @@ -1662,52 +1665,41 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *da data += (w>>1); y++; } - _swapBytes = swap; // Restore old value } else { // 1 bit per pixel - bool swap = _swapBytes; _swapBytes = false; - w = (w+7) & 0xFFF8; - while (dh--) { + _swapBytes = false; + + uint32_t ww = (w+7)>>3; // Width of source image line in bytes + uint16_t np = 0; + + for (int32_t yp = dy; yp < dy + dh; yp++) + { int32_t px = x; bool move = true; - uint16_t np = 0; - int32_t len = dw; - uint8_t* ptr = data; - uint8_t bits = 8; - while(len>0) { - if (len < 8) bits = len; - uint32_t xp = dx; - uint32_t yp = (dy * w)>>3; - for (uint16_t i = 0; i < bits; i++) { - //uint8_t col = (ptr[(xp + dy * w)>>3] << (xp & 0x7)) & 0x80; - if ((ptr[(xp>>3) + yp] << (xp & 0x7)) & 0x80) { - if (move) { - move = false; - setWindow(px, y, xe, ye); - } - np++; + for (int32_t xp = dx; xp < dx + dw; xp++) + { + if (data[(xp>>3)] & (0x80 >> (xp & 0x7))) { + if (move) { + move = false; + setWindow(px, y, xe, ye); } - else { - if (np) { - pushBlock(bitmap_fg, np); - np = 0; - move = true; - } - } - px++; - xp++; + np++; } - ptr++; - len -= 8; + else { + if (np) { + pushBlock(bitmap_fg, np); + np = 0; + move = true; + } + } + px++; } - - if (np) pushBlock(bitmap_fg, np); y++; - dy++; + data += ww; + if (np) { pushBlock(bitmap_fg, np); np = 0; } } - _swapBytes = swap; // Restore old value } - + _swapBytes = swap; // Restore old value inTransaction = false; end_tft_write(); } @@ -2427,8 +2419,8 @@ void TFT_eSPI::setTextColor(uint16_t c, uint16_t b) *************************************************************************************x*/ void TFT_eSPI::setPivot(int16_t x, int16_t y) { - _xpivot = x; - _ypivot = y; + _xPivot = x; + _yPivot = y; } @@ -2438,7 +2430,7 @@ void TFT_eSPI::setPivot(int16_t x, int16_t y) ***************************************************************************************/ int16_t TFT_eSPI::getPivotX(void) { - return _xpivot; + return _xPivot; } @@ -2448,7 +2440,7 @@ int16_t TFT_eSPI::getPivotX(void) ***************************************************************************************/ int16_t TFT_eSPI::getPivotY(void) { - return _ypivot; + return _yPivot; } @@ -2529,7 +2521,8 @@ uint8_t TFT_eSPI::getTextDatum(void) // Return the size of the display (per current rotation) int16_t TFT_eSPI::width(void) { - return _xWidth; + if (_vpDatum) return _xWidth; + return _width; } @@ -2539,7 +2532,8 @@ int16_t TFT_eSPI::width(void) ***************************************************************************************/ int16_t TFT_eSPI::height(void) { - return _yHeight; + if (_vpDatum) return _yHeight; + return _height; } @@ -2846,6 +2840,32 @@ void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t w, int32_t h) void TFT_eSPI::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) { //begin_tft_write(); // Must be called before setWindow +#if defined (ILI9225_DRIVER) + if (rotation & 0x01) { swap_coord(x0, y0); swap_coord(x1, y1); } + + addr_row = 0xFFFF; + addr_col = 0xFFFF; + + DC_C; tft_Write_8(TFT_CASET1); + DC_D; tft_Write_16(x0); + DC_C; tft_Write_8(TFT_CASET2); + DC_D; tft_Write_16(x1); + + DC_C; tft_Write_8(TFT_PASET1); + DC_D; tft_Write_16(y0); + DC_C; tft_Write_8(TFT_PASET2); + DC_D; tft_Write_16(y1); + + DC_C; tft_Write_8(TFT_RAM_ADDR1); + DC_D; tft_Write_16(x0); + DC_C; tft_Write_8(TFT_RAM_ADDR2); + DC_D; tft_Write_16(y0); + + // write to RAM + DC_C; tft_Write_8(TFT_RAMWR); + DC_D; + +#else #if defined (SSD1963_DRIVER) if ((rotation & 0x1) == 0) { swap_coord(x0, y0); swap_coord(x1, y1); } @@ -2869,6 +2889,7 @@ void TFT_eSPI::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) DC_D; //end_tft_write(); // Must be called after setWindow +#endif } @@ -2925,7 +2946,7 @@ void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) x+= _xDatum; y+= _yDatum; - // Range checking + // Range checking if ((x < _vpX) || (y < _vpY) ||(x >= _vpW) || (y >= _vpH)) return; #ifdef CGRAM_OFFSET @@ -2933,12 +2954,42 @@ void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) y+=rowstart; #endif + begin_tft_write(); + +#if defined (ILI9225_DRIVER) + + if (rotation & 0x01) { swap_coord(x, y); } + + // Set window to full screen to optimise sequential pixel rendering + if (addr_row != 0x9225) { + addr_row = 0x9225; // addr_row used for flag + DC_C; tft_Write_8(TFT_CASET1); + DC_D; tft_Write_16(0); + DC_C; tft_Write_8(TFT_CASET2); + DC_D; tft_Write_16(175); + + DC_C; tft_Write_8(TFT_PASET1); + DC_D; tft_Write_16(0); + DC_C; tft_Write_8(TFT_PASET2); + DC_D; tft_Write_16(219); + } + + // Define pixel coordinate + DC_C; tft_Write_8(TFT_RAM_ADDR1); + DC_D; tft_Write_16(x); + DC_C; tft_Write_8(TFT_RAM_ADDR2); + DC_D; tft_Write_16(y); + + // write to RAM + DC_C; tft_Write_8(TFT_RAMWR); + DC_D; tft_Write_16(color); + +#else + #if defined (SSD1963_DRIVER) if ((rotation & 0x1) == 0) { swap_coord(x, y); } #endif - begin_tft_write(); - #ifdef MULTI_TFT_SUPPORT // No optimisation DC_C; tft_Write_8(TFT_CASET); @@ -2960,9 +3011,9 @@ void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) addr_row = (y<<16 | y); } #endif - DC_C; tft_Write_8(TFT_RAMWR); DC_D; tft_Write_16(color); +#endif end_tft_write(); } @@ -3064,9 +3115,14 @@ void TFT_eSPI::pushColors(uint16_t *data, uint32_t len, bool swap) // an efficient FastH/V Line draw routine for line segments of 2 pixels or more void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color) { - //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + if (_vpOoB) return; + + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() inTransaction = true; + //x+= _xDatum; // Not added here, added by drawPixel & drawFastXLine + //y+= _yDatum; + bool steep = abs(y1 - y0) > abs(x1 - x0); if (steep) { swap_coord(x0, y0); @@ -3507,30 +3563,20 @@ uint32_t TFT_eSPI::alphaBlend24(uint8_t alpha, uint32_t fgc, uint32_t bgc, uint8 ***************************************************************************************/ size_t TFT_eSPI::write(uint8_t utf8) { + if (_vpOoB) return 1; + + uint16_t uniCode = decodeUTF8(utf8); + + if (!uniCode) return 1; + if (utf8 == '\r') return 1; - uint16_t uniCode = utf8; - - if (_utf8) uniCode = decodeUTF8(utf8); - - if (uniCode == 0) return 1; - #ifdef SMOOTH_FONT if(fontLoaded) { - //Serial.print("UniCode="); Serial.println(uniCode); - //Serial.print("UTF8 ="); Serial.println(utf8); - - //fontFile = SPIFFS.open( _gFontFilename, "r" ); - - //if(!fontFile) - //{ - // fontLoaded = false; - // return 1; - //} + if (uniCode < 32 && utf8 != '\n') return 1; drawGlyph(uniCode); - //fontFile.close(); return 1; } #endif @@ -3538,8 +3584,8 @@ size_t TFT_eSPI::write(uint8_t utf8) if (uniCode == '\n') uniCode+=22; // Make it a valid space character to stop errors else if (uniCode < 32) return 1; - uint16_t width = 0; - uint16_t height = 0; + uint16_t cwidth = 0; + uint16_t cheight = 0; //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv DEBUG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv //Serial.print((uint8_t) uniCode); // Debug line sends all printed TFT text to serial port @@ -3557,11 +3603,11 @@ size_t TFT_eSPI::write(uint8_t utf8) if (textfont == 2) { if (uniCode > 127) return 1; - width = pgm_read_byte(widtbl_f16 + uniCode-32); - height = chr_hgt_f16; + cwidth = pgm_read_byte(widtbl_f16 + uniCode-32); + cheight = 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 pixels + cwidth = (cwidth + 6) / 8; // Width in whole bytes for font 2, should be + 7 but must allow for font width change + cwidth = cwidth * 8; // Width converted back to pixels } #ifdef LOAD_RLE else @@ -3573,33 +3619,33 @@ size_t TFT_eSPI::write(uint8_t utf8) if ((textfont>2) && (textfont<9)) { if (uniCode > 127) return 1; // Uses the fontinfo struct array to avoid lots of 'if' or 'switch' statements - width = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[textfont].widthtbl ) ) + uniCode-32 ); - height= pgm_read_byte( &fontdata[textfont].height ); + cwidth = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[textfont].widthtbl ) ) + uniCode-32 ); + cheight= pgm_read_byte( &fontdata[textfont].height ); } } #endif #ifdef LOAD_GLCD if (textfont==1) { - width = 6; - height = 8; + cwidth = 6; + cheight = 8; } #else if (textfont==1) return 1; #endif - height = height * textsize; + cheight = cheight * textsize; if (utf8 == '\n') { - cursor_y += height; + cursor_y += cheight; cursor_x = 0; } else { - if (textwrapX && (cursor_x + width * textsize > this->width())) { - cursor_y += height; + if (textwrapX && (cursor_x + cwidth * textsize > width())) { + cursor_y += cheight; cursor_x = 0; } - if (textwrapY && (cursor_y >= (int32_t)this->height())) cursor_y = 0; + if (textwrapY && (cursor_y >= (int32_t) height())) cursor_y = 0; cursor_x += drawChar(uniCode, cursor_x, cursor_y, textfont); } @@ -3609,10 +3655,8 @@ size_t TFT_eSPI::write(uint8_t utf8) else { if(utf8 == '\n') { cursor_x = 0; - cursor_y += (int16_t)textsize * - (uint8_t)pgm_read_byte(&gfxFont->yAdvance); - } - else { + cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + } else { if (uniCode > pgm_read_word(&gfxFont->last )) return 1; if (uniCode < pgm_read_word(&gfxFont->first)) return 1; @@ -3622,13 +3666,12 @@ 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(textwrapX && ((cursor_x + textsize * (xo + w)) > this->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); + cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); } - if (textwrapY && (cursor_y >= (int32_t)this->height())) cursor_y = 0; + if (textwrapY && (cursor_y >= (int32_t) height())) cursor_y = 0; drawChar(cursor_x, cursor_y, uniCode, textcolor, textbgcolor, textsize); } cursor_x += pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize; @@ -3645,6 +3688,7 @@ size_t TFT_eSPI::write(uint8_t utf8) ** Function name: drawChar ** Description: draw a Unicode glyph onto the screen ***************************************************************************************/ + // TODO: Rationalise with TFT_eSprite // Any UTF-8 decoding must be done before calling drawChar() int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y) { @@ -4087,11 +4131,11 @@ int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8 /* // The above only works for a single text line, not if the text is going to wrap... // So need to use code like this in a while loop to fix it: - if (textwrapX && (cursor_x + width * textsize > this->width())) { + if (textwrapX && (cursor_x + width * textsize > width())) { cursor_y += height; cursor_x = 0; } - if (textwrapY && (cursor_y >= (int32_t)this->height())) cursor_y = 0; + if (textwrapY && (cursor_y >= (int32_t)height())) cursor_y = 0; cursor_x += drawChar(uniCode, cursor_x, cursor_y, textfont); */ setCursor(poX, poY); diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 117f097..07a6b85 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -9,14 +9,14 @@ The built-in fonts 4, 6, 7 and 8 are Run Length Encoded (RLE) to reduce the FLASH footprint. - Last review/edit by Bodmer: 26/01/20 + Last review/edit by Bodmer: 01/12/20 ****************************************************/ // Stop fonts etc being loaded multiple times #ifndef _TFT_eSPIH_ #define _TFT_eSPIH_ -#define TFT_ESPI_VERSION "2.3.42" +#define TFT_ESPI_VERSION "2.3.5" // Bit level feature flags // Bit 0 set: viewport capability @@ -360,7 +360,7 @@ swap_coord(T& a, T& b) { T t = a; a = b; b = t; } typedef uint16_t (*getColorCallback)(uint16_t x, uint16_t y); // Class functions and variables -class TFT_eSPI : public Print { +class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has access to protected members //--------------------------------------- public ------------------------------------// public: @@ -662,7 +662,6 @@ class TFT_eSPI : public Print { // Global variables static SPIClass& getSPIinstance(void); // Get SPI class handle - int32_t cursor_x, cursor_y, padX; // Text cursor x,y and padding setting uint32_t textcolor, textbgcolor; // Text foreground and background colours uint32_t bitmap_fg, bitmap_bg; // Bitmap foreground (bit=1) and background (bit=0) colours @@ -672,9 +671,6 @@ class TFT_eSPI : public Print { textdatum, // Text reference datum rotation; // Display rotation (0-3) - int16_t _xpivot; // TFT x pivot point coordinate for rotated Sprites - int16_t _ypivot; // TFT x pivot point coordinate for rotated Sprites - uint8_t decoderState = 0; // UTF8 decoder state - not for user access uint16_t decoderBuffer; // Unicode code-point buffer - not for user access @@ -743,6 +739,9 @@ class TFT_eSPI : public Print { int32_t _width, _height; // Display w/h as modified by current rotation int32_t addr_row, addr_col; // Window position - used to minimise window commands + int16_t _xPivot; // TFT x pivot point coordinate for rotated Sprites + int16_t _yPivot; // TFT x pivot point coordinate for rotated Sprites + // Viewport variables int32_t _vpX, _vpY, _vpW, _vpH; // Note: x start, y start, x end + 1, y end + 1 int32_t _xDatum; @@ -752,6 +751,8 @@ class TFT_eSPI : public Print { bool _vpDatum; bool _vpOoB; + int32_t cursor_x, cursor_y, padX; // Text cursor x,y and padding setting + uint32_t fontsloaded; // Bit field of fonts loaded uint8_t glyph_ab, // Smooth font glyph delta Y (height) above baseline diff --git a/User_Setup.h b/User_Setup.h index 3297b26..9014b8a 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -50,9 +50,10 @@ //#define R61581_DRIVER //#define RM68140_DRIVER //#define ST7796_DRIVER -//#define SSD1963_480_DRIVER // Untested -//#define SSD1963_800_DRIVER // Untested -//#define SSD1963_800ALT_DRIVER // Untested +//#define SSD1963_480_DRIVER +//#define SSD1963_800_DRIVER +//#define SSD1963_800ALT_DRIVER +//#define ILI9225_DRIVER // Some displays support SPI reads via the MISO pin, other displays have a single // bi-directional SDA pin and the library will try to read this via the MOSI line. diff --git a/User_Setups/Setup10_RPi_touch_ILI9486.h b/User_Setups/Setup10_RPi_touch_ILI9486.h index fb414e3..452eee8 100644 --- a/User_Setups/Setup10_RPi_touch_ILI9486.h +++ b/User_Setups/Setup10_RPi_touch_ILI9486.h @@ -1,7 +1,8 @@ +// For ESP8266 // See SetupX_Template.h for all options available #define RPI_DISPLAY_TYPE -#define ILI9486_DRIVER // 20MHz maximum SPI +#define ILI9486_DRIVER // For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation #define TFT_CS PIN_D2 // Chip select control pin D2 @@ -23,7 +24,7 @@ #define SMOOTH_FONT -#define SPI_FREQUENCY 16000000 +#define SPI_FREQUENCY 16000000 // Some displays will operate at higher frequencies #define SPI_TOUCH_FREQUENCY 2500000 diff --git a/User_Setups/Setup11_RPi_touch_ILI9486.h b/User_Setups/Setup11_RPi_touch_ILI9486.h index 9b0ba44..5f81691 100644 --- a/User_Setups/Setup11_RPi_touch_ILI9486.h +++ b/User_Setups/Setup11_RPi_touch_ILI9486.h @@ -1,6 +1,7 @@ +// For ESP32 // See SetupX_Template.h for all options available #define RPI_DISPLAY_TYPE -#define ILI9486_DRIVER // 20MHz maximum SPI +#define ILI9486_DRIVER #define TFT_MISO 19 #define TFT_MOSI 23 @@ -24,6 +25,6 @@ #define SMOOTH_FONT -#define SPI_FREQUENCY 20000000 +#define SPI_FREQUENCY 20000000 // Some displays will operate at higher frequencies #define SPI_TOUCH_FREQUENCY 2500000 diff --git a/examples/Sprite/Sprite_scroll_1bit/Sprite_scroll_1bit.ino b/examples/Sprite/Sprite_scroll_1bit/Sprite_scroll_1bit.ino index ab4a53e..13d0dc9 100644 --- a/examples/Sprite/Sprite_scroll_1bit/Sprite_scroll_1bit.ino +++ b/examples/Sprite/Sprite_scroll_1bit/Sprite_scroll_1bit.ino @@ -6,21 +6,6 @@ In a 1 bit Sprite any colour except TFT_BLACK turns a pixel "ON" TFT_BLACK turns a pixel "OFF". - The 1 bpp Sprite has a unique property that other bit depth Sprites - do not have, you can set the rotation of the coordinate frame e.g.: - spr.setRotation(1); - - This is similar to screen rotations, so for example text can - be drawn rotated: - Rotation 0: Normal orientation - Rotation 1: Coordinate frame rotated clockwise 90 degrees - Rotation 2: Coordinate frame rotated clockwise 180 degrees (upside down) - Rotation 3: Coordinate frame rotated clockwise 270 degrees - - When pushSprite is used the sprite is drawn with the width and height - staying as created, so the created Sprite itself is not rotated during - rendering. See stext2 sprite example below at line 83. - ON and OFF pixels can be set to any two colours before rendering to the screen with pushSprite, for example: tft.setBitmapColor(ON_COLOR, OFF_COLOR); @@ -83,8 +68,7 @@ void setup() { // Create a sprite for Hello World stext2.setColorDepth(1); - stext2.createSprite(16, 80); // Narrow and tall - stext2.setRotation(1); // Plot with 90 deg. clockwise rotation + stext2.createSprite(80, 16); stext2.fillSprite(TFT_BLACK); stext2.setScrollRect(0, 0, 40, 16, TFT_BLACK); // Scroll the "Hello" in the first 40 pixels stext2.setTextColor(TFT_WHITE); // White text, no background diff --git a/library.json b/library.json index 1307aca..3e5afe6 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "2.3.42", + "version": "2.3.5", "keywords": "Arduino, tft, ePaper, display, STM32, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486, ST7789, RM68140", "description": "A TFT and ePaper SPI graphics library with optimisation for ESP8266, ESP32 and STM32", "repository": diff --git a/library.properties b/library.properties index 70d4bd5..abca8da 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=2.3.42 +version=2.3.5 author=Bodmer maintainer=Bodmer sentence=TFT graphics library for Arduino processors with performance optimisation for STM32, ESP8266 and ESP32