| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598 |
- /**************************************************************************************
- // The following class creates Sprites in RAM, graphics can then be drawn in the Sprite
- // and rendered quickly onto the TFT screen. The class inherits the graphics functions
- // from the TFT_eSPI class. Some functions are overridden by this class so that the
- // graphics are written to the Sprite rather than the TFT.
- // Coded by Bodmer, see license file in root folder
- ***************************************************************************************/
- /***************************************************************************************
- // Color bytes are swapped when writing to RAM, this introduces a small overhead but
- // there is a nett performance gain by using swapped bytes.
- ***************************************************************************************/
- /***************************************************************************************
- ** Function name: TFT_eSprite
- ** Description: Class constructor
- ***************************************************************************************/
- TFT_eSprite::TFT_eSprite(TFT_eSPI *tft)
- {
- _tft = tft; // Pointer to tft class so we can call member functions
- _iwidth = 0; // Initialise width and height to 0 (it does not exist yet)
- _iheight = 0;
- _bpp = 16;
- _swapBytes = false; // Do not swap pushImage colour bytes by default
- _created = false;
- _vpOoB = true;
- _xs = 0; // window bounds for pushColor
- _ys = 0;
- _xe = 0;
- _ye = 0;
- _xptr = 0; // pushColor coordinate
- _yptr = 0;
- _colorMap = nullptr;
- _psram_enable = true;
- }
- /***************************************************************************************
- ** Function name: createSprite
- ** Description: Create a sprite (bitmap) of defined width and height
- ***************************************************************************************/
- // cast returned value to (uint8_t*) for 8 bit or (uint16_t*) for 16 bit colours
- void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames)
- {
- if ( _created ) return _img8_1;
- if ( w < 1 || h < 1 ) return nullptr;
- _iwidth = _dwidth = _bitwidth = w;
- _iheight = _dheight = h;
- cursor_x = 0;
- cursor_y = 0;
- // Default scroll rectangle and gap fill colour
- _sx = 0;
- _sy = 0;
- _sw = w;
- _sh = h;
- _scolor = TFT_BLACK;
- _img8 = (uint8_t*) callocSprite(w, h, frames);
- _img8_1 = _img8;
- _img8_2 = _img8;
- _img = (uint16_t*) _img8;
- _img4 = _img8;
- if ( (_bpp == 16) && (frames > 1) ) {
- _img8_2 = _img8 + (w * h * 2 + 1);
- }
- // ESP32 only 16bpp check
- //if (esp_ptr_dma_capable(_img8_1)) Serial.println("DMA capable Sprite pointer _img8_1");
- //else Serial.println("Not a DMA capable Sprite pointer _img8_1");
- //if (esp_ptr_dma_capable(_img8_2)) Serial.println("DMA capable Sprite pointer _img8_2");
- //else Serial.println("Not a DMA capable Sprite pointer _img8_2");
- if ( (_bpp == 8) && (frames > 1) ) {
- _img8_2 = _img8 + (w * h + 1);
- }
- if ( (_bpp == 4) && (_colorMap == nullptr)) createPalette(default_4bit_palette);
- // This is to make it clear what pointer size is expected to be used
- // but casting in the user sketch is needed due to the use of void*
- if ( (_bpp == 1) && (frames > 1) )
- {
- w = (w+7) & 0xFFF8;
- _img8_2 = _img8 + ( (w>>3) * h + 1 );
- }
- if (_img8)
- {
- _created = true;
- rotation = 0;
- setViewport(0, 0, _dwidth, _dheight);
- setPivot(_iwidth/2, _iheight/2);
- return _img8_1;
- }
- return nullptr;
- }
- /***************************************************************************************
- ** Function name: getPointer
- ** Description: Returns pointer to start of sprite memory area
- ***************************************************************************************/
- void* TFT_eSprite::getPointer(void)
- {
- if (!_created) return nullptr;
- return _img8_1;
- }
- /***************************************************************************************
- ** Function name: created
- ** Description: Returns true if sprite has been created
- ***************************************************************************************/
- bool TFT_eSprite::created(void)
- {
- return _created;
- }
- /***************************************************************************************
- ** Function name: ~TFT_eSprite
- ** Description: Class destructor
- ***************************************************************************************/
- TFT_eSprite::~TFT_eSprite(void)
- {
- deleteSprite();
- #ifdef SMOOTH_FONT
- if(fontLoaded) unloadFont();
- #endif
- }
- /***************************************************************************************
- ** 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
- // this means push/writeColor functions do not need additional bounds checks and
- // hence will run faster in normal circumstances.
- uint8_t* ptr8 = nullptr;
- if (frames > 2) frames = 2; // Currently restricted to 2 frame buffers
- if (frames < 1) frames = 1;
- if (_bpp == 16)
- {
- #if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT)
- if ( psramFound() && _psram_enable && !_tft->DMA_Enabled)
- {
- ptr8 = ( uint8_t*) ps_calloc(frames * w * h + frames, sizeof(uint16_t));
- //Serial.println("PSRAM");
- }
- else
- #endif
- {
- ptr8 = ( uint8_t*) calloc(frames * w * h + frames, sizeof(uint16_t));
- //Serial.println("Normal RAM");
- }
- }
- else if (_bpp == 8)
- {
- #if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT)
- 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));
- }
- else if (_bpp == 4)
- {
- 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() && _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));
- }
- else // Must be 1 bpp
- {
- //_dwidth Display width+height in pixels always in rotation 0 orientation
- //_dheight Not swapped for sprite rotations
- // Note: for 1bpp _iwidth and _iheight are swapped during Sprite rotations
- 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 will not be rotated whereas _iwidth may be
- #if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT)
- 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));
- }
- 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)
- {
- free(_colorMap);
- }
- if (colorMap == nullptr)
- {
- // Create a color map using the default FLASH map
- createPalette(default_4bit_palette);
- return;
- }
- // Allocate and clear memory for 16 color map
- _colorMap = (uint16_t *)calloc(16, sizeof(uint16_t));
- if (colors > 16) colors = 16;
- // Copy map colors
- for (uint8_t i = 0; i < colors; i++)
- {
- _colorMap[i] = colorMap[i];
- }
- }
- /***************************************************************************************
- ** 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)
- {
- // Create a color map using the default FLASH map
- colorMap = default_4bit_palette;
- }
- // Allocate and clear memory for 16 color map
- _colorMap = (uint16_t *)calloc(16, sizeof(uint16_t));
- if (colors > 16) colors = 16;
- // Copy map colors
- for (uint8_t i = 0; i < colors; i++)
- {
- _colorMap[i] = pgm_read_word(colorMap++);
- }
- }
- /***************************************************************************************
- ** Function name: frameBuffer
- ** Description: For 1 bpp Sprites, select the frame used for graphics
- ***************************************************************************************/
- // Frames are numbered 1 and 2
- void* TFT_eSprite::frameBuffer(int8_t f)
- {
- if (!_created) return nullptr;
- if ( f == 2 ) _img8 = _img8_2;
- else _img8 = _img8_1;
- if (_bpp == 16) _img = (uint16_t*)_img8;
- //if (_bpp == 8) _img8 = _img8;
- if (_bpp == 4) _img4 = _img8;
- 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
- if (_bpp == b) return _img8_1;
- // Validate the new colour depth
- if ( b > 8 ) _bpp = 16; // Bytes per pixel
- else if ( b > 4 ) _bpp = 8;
- else if ( b > 1 ) _bpp = 4;
- else _bpp = 1;
- // Can't change an existing sprite's colour depth so delete it
- if (_created) free(_img8_1);
- // If it existed, re-create the sprite with the new colour depth
- if (_created)
- {
- _created = false;
- return createSprite(_dwidth, _dheight);
- }
- return nullptr;
- }
- /***************************************************************************************
- ** Function name: getColorDepth
- ** Description: Get bits per pixel for colour (1, 8 or 16)
- ***************************************************************************************/
- int8_t TFT_eSprite::getColorDepth(void)
- {
- if (_created) return _bpp;
- else return 0;
- }
- /***************************************************************************************
- ** Function name: setBitmapColor
- ** Description: Set the 1bpp foreground foreground and background colour
- ***************************************************************************************/
- void TFT_eSprite::setBitmapColor(uint16_t c, uint16_t b)
- {
- if (c == b) b = ~c;
- _tft->bitmap_fg = c;
- _tft->bitmap_bg = b;
- }
- /***************************************************************************************
- ** Function name: setPaletteColor
- ** Description: Set the 4bpp palette color at the given index
- ***************************************************************************************/
- void TFT_eSprite::setPaletteColor(uint8_t index, uint16_t color)
- {
- if (_colorMap == nullptr || index > 15) return; // out of bounds
- _colorMap[index] = color;
- }
- /***************************************************************************************
- ** Function name: getPaletteColor
- ** Description: Return the palette color at 4bpp index, or 0 on error.
- ***************************************************************************************/
- uint16_t TFT_eSprite::getPaletteColor(uint8_t index)
- {
- if (_colorMap == nullptr || index > 15) return 0; // out of bounds
- return _colorMap[index];
- }
- /***************************************************************************************
- ** Function name: deleteSprite
- ** Description: Delete the sprite to free up memory (RAM)
- ***************************************************************************************/
- void TFT_eSprite::deleteSprite(void)
- {
- if (_colorMap != nullptr)
- {
- free(_colorMap);
- _colorMap = nullptr;
- }
- if (_created)
- {
- free(_img8_1);
- _img8 = nullptr;
- _created = false;
- _vpOoB = true; // TFT_eSPI class write() uses this to check for valid sprite
- }
- }
- /***************************************************************************************
- ** Function name: pushRotated - Fast fixed point integer maths version
- ** Description: Push rotated Sprite to TFT screen
- ***************************************************************************************/
- #define FP_SCALE 10
- bool TFT_eSprite::pushRotated(int16_t angle, uint32_t transp)
- {
- if ( !_created || _tft->_vpOoB) return false;
- // Bounding box parameters
- int16_t min_x;
- int16_t min_y;
- int16_t max_x;
- int16_t max_y;
- // Get the bounding box of this rotated source Sprite relative to Sprite pivot
- if ( !getRotatedBounds(angle, &min_x, &min_y, &max_x, &max_y) ) return false;
- uint16_t sline_buffer[max_x - min_x + 1];
- 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;
- uint32_t tpcolor = transp;
- if (transp != 0x00FFFFFF) {
- if (_bpp == 4) tpcolor = _colorMap[transp & 0x0F];
- tpcolor = tpcolor>>8 | tpcolor<<8; // Working with swapped color bytes
- }
- _tft->startWrite(); // Avoid transaction overhead for every tft pixel
- // Scan destination bounding box and fetch transformed pixels from source Sprite
- for (int32_t y = min_y; y <= max_y; y++, yt++) {
- int32_t x = min_x;
- uint32_t xs = (_cosra * xt - (_sinra * yt - (_xPivot << FP_SCALE)) + (1 << (FP_SCALE - 1)));
- uint32_t ys = (_sinra * xt + (_cosra * yt + (_yPivot << FP_SCALE)) + (1 << (FP_SCALE - 1)));
- while ((xs >= xe || ys >= ye) && x < max_x) { x++; xs += _cosra; ys += _sinra; }
- if (x == max_x) continue;
- uint32_t pixel_count = 0;
- do {
- uint32_t rp;
- int32_t xp = xs >> FP_SCALE;
- int32_t yp = ys >> FP_SCALE;
- if (_bpp == 16) {rp = _img[xp + yp * _iwidth]; }
- else { rp = readPixel(xp, yp); rp = (uint16_t)(rp>>8 | rp<<8); }
- if (tpcolor == rp) {
- if (pixel_count) {
- // TFT window is already clipped, so this is faster than pushImage()
- _tft->setWindow(x - pixel_count, y, x, y);
- _tft->pushPixels(sline_buffer, pixel_count);
- pixel_count = 0;
- }
- }
- else {
- sline_buffer[pixel_count++] = rp;
- }
- } while (++x < max_x && (xs += _cosra) < xe && (ys += _sinra) < ye);
- if (pixel_count) {
- // TFT window is already clipped, so this is faster than pushImage()
- _tft->setWindow(x - pixel_count, y, x, y);
- _tft->pushPixels(sline_buffer, pixel_count);
- }
- }
- _tft->endWrite(); // End transaction
- return true;
- }
- /***************************************************************************************
- ** Function name: pushRotated - Fast fixed point integer maths version
- ** Description: Push a rotated copy of the Sprite to another Sprite
- ***************************************************************************************/
- // Not compatible with 4bpp
- bool TFT_eSprite::pushRotated(TFT_eSprite *spr, int16_t angle, uint32_t transp)
- {
- if ( !_created || _bpp == 4) return false; // Check this Sprite is created
- if ( !spr->_created || spr->_bpp == 4) return false; // Ckeck destination Sprite is created
- // Bounding box parameters
- int16_t min_x;
- int16_t min_y;
- int16_t max_x;
- int16_t max_y;
- // Get the bounding box of this rotated source Sprite
- if ( !getRotatedBounds(spr, angle, &min_x, &min_y, &max_x, &max_y) ) return false;
- uint16_t sline_buffer[max_x - min_x + 1];
- int32_t xt = min_x - spr->_xPivot;
- int32_t yt = min_y - spr->_yPivot;
- uint32_t xe = _dwidth << FP_SCALE;
- uint32_t ye = _dheight << FP_SCALE;
- uint32_t tpcolor = transp;
-
- if (transp != 0x00FFFFFF) {
- if (_bpp == 4) tpcolor = _colorMap[transp & 0x0F];
- tpcolor = tpcolor>>8 | tpcolor<<8; // Working with swapped color bytes
- }
- bool oldSwapBytes = spr->getSwapBytes();
- spr->setSwapBytes(false);
- // Scan destination bounding box and fetch transformed pixels from source Sprite
- for (int32_t y = min_y; y <= max_y; y++, yt++) {
- int32_t x = min_x;
- uint32_t xs = (_cosra * xt - (_sinra * yt - (_xPivot << FP_SCALE)) + (1 << (FP_SCALE - 1)));
- uint32_t ys = (_sinra * xt + (_cosra * yt + (_yPivot << FP_SCALE)) + (1 << (FP_SCALE - 1)));
- while ((xs >= xe || ys >= ye) && x < max_x) { x++; xs += _cosra; ys += _sinra; }
- if (x == max_x) continue;
- uint32_t pixel_count = 0;
- do {
- uint32_t rp;
- int32_t xp = xs >> FP_SCALE;
- int32_t yp = ys >> FP_SCALE;
- if (_bpp == 16) rp = _img[xp + yp * _iwidth];
- else { rp = readPixel(xp, yp); rp = (uint16_t)(rp>>8 | rp<<8); }
- if (tpcolor == rp) {
- if (pixel_count) {
- spr->pushImage(x - pixel_count, y, pixel_count, 1, sline_buffer);
- pixel_count = 0;
- }
- }
- else {
- sline_buffer[pixel_count++] = rp;
- }
- } while (++x < max_x && (xs += _cosra) < xe && (ys += _sinra) < ye);
- if (pixel_count) spr->pushImage(x - pixel_count, y, pixel_count, 1, sline_buffer);
- }
- spr->setSwapBytes(oldSwapBytes);
- return true;
- }
- /***************************************************************************************
- ** Function name: getRotatedBounds
- ** Description: Get TFT bounding box of a rotated Sprite wrt pivot
- ***************************************************************************************/
- bool TFT_eSprite::getRotatedBounds(int16_t angle, int16_t *min_x, int16_t *min_y,
- int16_t *max_x, int16_t *max_y)
- {
- // Get the bounding box of this rotated source Sprite relative to Sprite pivot
- 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;
- // 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 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;
- }
- /***************************************************************************************
- ** Function name: getRotatedBounds
- ** Description: Get destination Sprite bounding box of a rotated Sprite wrt pivot
- ***************************************************************************************/
- bool TFT_eSprite::getRotatedBounds(TFT_eSprite *spr, int16_t angle, int16_t *min_x, int16_t *min_y,
- int16_t *max_x, int16_t *max_y)
- {
- // Get the bounding box of this rotated source Sprite relative to Sprite pivot
- getRotatedBounds(angle, width(), height(), _xPivot, _yPivot, min_x, min_y, max_x, max_y);
- // Move bounding box so source Sprite pivot coincides with destination Sprite pivot
- *min_x += spr->_xPivot;
- *max_x += spr->_xPivot;
- *min_y += spr->_yPivot;
- *max_y += spr->_yPivot;
- // Test only to show bounding box
- // spr->fillSprite(TFT_BLACK);
- // spr->drawRect(min_x, min_y, max_x - min_x + 1, max_y - min_y + 1, TFT_GREEN);
- // Return if bounding box is completely outside of destination Sprite
- if (*min_x > spr->width()) return true;
- if (*min_y > spr->height()) return true;
- if (*max_x < 0) return true;
- if (*max_y < 0) return true;
- // 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();
- if (*max_y > spr->height()) *max_y = spr->height();
- return true;
- }
- /***************************************************************************************
- ** Function name: rotatedBounds
- ** Description: Get bounding box of a rotated Sprite wrt pivot
- ***************************************************************************************/
- void TFT_eSprite::getRotatedBounds(int16_t angle, int16_t w, int16_t h, int16_t xp, int16_t yp,
- int16_t *min_x, int16_t *min_y, int16_t *max_x, int16_t *max_y)
- {
- // Trig values for the rotation
- float radAngle = -angle * 0.0174532925; // Convert degrees to radians
- float sina = sin(radAngle);
- float cosa = cos(radAngle);
- w -= xp; // w is now right edge coordinate relative to xp
- h -= yp; // h is now bottom edge coordinate relative to yp
- // Calculate new corner coordinates
- int16_t x0 = -xp * cosa - yp * sina;
- int16_t y0 = xp * sina - yp * cosa;
- int16_t x1 = w * cosa - yp * sina;
- int16_t y1 = -w * sina - yp * cosa;
- int16_t x2 = h * sina + w * cosa;
- int16_t y2 = h * cosa - w * sina;
- int16_t x3 = h * sina - xp * cosa;
- int16_t y3 = h * cosa + xp * sina;
- // Find bounding box extremes, enlarge box to accomodate rounding errors
- *min_x = x0-2;
- if (x1 < *min_x) *min_x = x1-2;
- if (x2 < *min_x) *min_x = x2-2;
- if (x3 < *min_x) *min_x = x3-2;
- *max_x = x0+2;
- if (x1 > *max_x) *max_x = x1+2;
- if (x2 > *max_x) *max_x = x2+2;
- if (x3 > *max_x) *max_x = x3+2;
- *min_y = y0-2;
- if (y1 < *min_y) *min_y = y1-2;
- if (y2 < *min_y) *min_y = y2-2;
- if (y3 < *min_y) *min_y = y3-2;
- *max_y = y0+2;
- if (y1 > *max_y) *max_y = y1+2;
- if (y2 > *max_y) *max_y = y2+2;
- if (y3 > *max_y) *max_y = y3+2;
- _sinra = round(sina * (1<<FP_SCALE));
- _cosra = round(cosa * (1<<FP_SCALE));
- }
- /***************************************************************************************
- ** Function name: pushSprite
- ** Description: Push the sprite to the TFT at x, y
- ***************************************************************************************/
- void TFT_eSprite::pushSprite(int32_t x, int32_t y)
- {
- if (!_created) return;
- if (_bpp == 16)
- {
- bool oldSwapBytes = _tft->getSwapBytes();
- _tft->setSwapBytes(false);
- _tft->pushImage(x, y, _dwidth, _dheight, _img );
- _tft->setSwapBytes(oldSwapBytes);
- }
- else if (_bpp == 4)
- {
- _tft->pushImage(x, y, _dwidth, _dheight, _img4, false, _colorMap);
- }
- else _tft->pushImage(x, y, _dwidth, _dheight, _img8, (bool)(_bpp == 8));
- }
- /***************************************************************************************
- ** Function name: pushSprite
- ** Description: Push the sprite to the TFT at x, y with transparent colour
- ***************************************************************************************/
- void TFT_eSprite::pushSprite(int32_t x, int32_t y, uint16_t transp)
- {
- if (!_created) return;
- if (_bpp == 16)
- {
- bool oldSwapBytes = _tft->getSwapBytes();
- _tft->setSwapBytes(false);
- _tft->pushImage(x, y, _dwidth, _dheight, _img, transp );
- _tft->setSwapBytes(oldSwapBytes);
- }
- else if (_bpp == 8)
- {
- transp = (uint8_t)((transp & 0xE000)>>8 | (transp & 0x0700)>>6 | (transp & 0x0018)>>3);
- _tft->pushImage(x, y, _dwidth, _dheight, _img8, (uint8_t)transp, (bool)true);
- }
- else if (_bpp == 4)
- {
- _tft->pushImage(x, y, _dwidth, _dheight, _img4, (uint8_t)(transp & 0x0F), false, _colorMap);
- }
- else _tft->pushImage(x, y, _dwidth, _dheight, _img8, 0, (bool)false);
- }
- /***************************************************************************************
- ** Function name: pushToSprite
- ** Description: Push the sprite to another sprite at x, y
- ***************************************************************************************/
- // Note: The following sprite to sprite colour depths are currently supported:
- // Source Destination
- // 16bpp -> 16bpp
- // 16bpp -> 8bpp
- // 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)
- {
- if (!_created) return false;
- if (!dspr->created()) return false;
- // Check destination sprite compatibility
- int8_t ds_bpp = dspr->getColorDepth();
- if (_bpp == 16 && ds_bpp != 16 && ds_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, _bpp);
- dspr->setSwapBytes(oldSwapBytes);
- return true;
- }
- /***************************************************************************************
- ** Function name: pushToSprite
- ** Description: Push the sprite to another sprite at x, y with transparent colour
- ***************************************************************************************/
- // Note: The following sprite to sprite colour depths are currently supported:
- // Source Destination
- // 16bpp -> 16bpp
- // 16bpp -> 8bpp
- // 8bpp -> 8bpp
- // 1bpp -> 1bpp
- bool TFT_eSprite::pushToSprite(TFT_eSprite *dspr, int32_t x, int32_t y, uint16_t transp)
- {
- if ( !_created || !dspr->_created) return false; // Check Sprites exist
- // Check destination sprite compatibility
- int8_t ds_bpp = dspr->getColorDepth();
- if (_bpp == 16 && ds_bpp != 16 && ds_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();
- uint16_t sline_buffer[width()];
- transp = transp>>8 | transp<<8;
- // Scan destination bounding box and fetch transformed pixels from source Sprite
- for (int32_t ys = 0; ys < height(); ys++) {
- int32_t ox = x;
- uint32_t pixel_count = 0;
- for (int32_t xs = 0; xs < width(); xs++) {
- uint16_t rp = 0;
- if (_bpp == 16) rp = _img[xs + ys * width()];
- else { rp = readPixel(xs, ys); rp = rp>>8 | rp<<8; }
- //dspr->drawPixel(xs, ys, rp);
- if (transp == rp) {
- if (pixel_count) {
- dspr->pushImage(ox, y, pixel_count, 1, sline_buffer, _bpp);
- ox += pixel_count;
- pixel_count = 0;
- }
- else ox++;
- }
- else {
- sline_buffer[pixel_count++] = rp;
- }
- }
- if (pixel_count) dspr->pushImage(ox, y, pixel_count, 1, sline_buffer);
- y++;
- }
- dspr->setSwapBytes(oldSwapBytes);
- return true;
- }
- /***************************************************************************************
- ** Function name: pushSprite
- ** Description: Push a cropped sprite to the TFT at tx, ty
- ***************************************************************************************/
- bool TFT_eSprite::pushSprite(int32_t tx, int32_t ty, int32_t sx, int32_t sy, int32_t sw, int32_t sh)
- {
- if (!_created) return false;
- // Perform window boundary checks and crop if needed
- setWindow(sx, sy, sx + sw - 1, sy + sh - 1);
- /* These global variables are now populated for the sprite
- _xs = x start coordinate
- _ys = y start coordinate
- _xe = x end coordinate (inclusive)
- _ye = y end coordinate (inclusive)
- */
- // Calculate new sprite window bounding box width and height
- sw = _xe - _xs + 1;
- sh = _ye - _ys + 1;
- if (_ys >= _iheight) return false;
- if (_bpp == 16)
- {
- bool oldSwapBytes = _tft->getSwapBytes();
- _tft->setSwapBytes(false);
- // Check if a faster block copy to screen is possible
- if ( sx == 0 && sw == _dwidth)
- _tft->pushImage(tx, ty, sw, sh, _img + _iwidth * _ys );
- else // Render line by line
- while (sh--)
- _tft->pushImage(tx, ty++, sw, 1, _img + _xs + _iwidth * _ys++ );
- _tft->setSwapBytes(oldSwapBytes);
- }
- else if (_bpp == 8)
- {
- // Check if a faster block copy to screen is possible
- if ( sx == 0 && sw == _dwidth)
- _tft->pushImage(tx, ty, sw, sh, _img8 + _iwidth * _ys, (bool)true );
- else // Render line by line
- while (sh--)
- _tft->pushImage(tx, ty++, sw, 1, _img8 + _xs + _iwidth * _ys++, (bool)true );
- }
- else if (_bpp == 4)
- {
- // Check if a faster block copy to screen is possible
- if ( sx == 0 && sw == _dwidth)
- _tft->pushImage(tx, ty, sw, sh, _img4 + (_iwidth>>1) * _ys, false, _colorMap );
- else // Render line by line
- {
- int32_t ds = _xs&1; // Odd x start pixel
- int32_t de = 0; // Odd x end pixel
- if ((sw > ds) && (_xe&1)) de = 1;
- uint32_t dm = 0; // Midsection pixel count
- if (sw > (ds+de)) dm = sw - ds - de;
- sw--;
- uint32_t yp = (_xs + ds + _iwidth * _ys)>>1;
- _tft->startWrite();
- while (sh--)
- {
- if (ds) _tft->drawPixel(tx, ty, readPixel(_xs, _ys) );
- if (dm) _tft->pushImage(tx + ds, ty, dm, 1, _img4 + yp, false, _colorMap );
- if (de) _tft->drawPixel(tx + sw, ty, readPixel(_xe, _ys) );
- _ys++;
- ty++;
- yp += (_iwidth>>1);
- }
- _tft->endWrite();
- }
- }
- else // 1bpp
- {
- // Check if a faster block copy to screen is possible
- if ( sx == 0 && sw == _dwidth)
- _tft->pushImage(tx, ty, sw, sh, _img8 + (_bitwidth>>3) * _ys, (bool)false );
- else // Render line by line
- {
- _tft->startWrite();
- while (sh--)
- {
- _tft->pushImage(tx, ty++, sw, 1, _img8 + (_bitwidth>>3) * _ys, (bool)false );
- }
- _tft->endWrite();
- }
- }
- return true;
- }
- /***************************************************************************************
- ** Function name: readPixelValue
- ** Description: Read the color map index of a pixel at defined coordinates
- ***************************************************************************************/
- uint16_t TFT_eSprite::readPixelValue(int32_t x, int32_t y)
- {
- 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 - _xDatum, y - _yDatum);
- }
- if (_bpp == 8)
- {
- // Return the pixel byte value
- return _img8[x + y * _iwidth];
- }
- if (_bpp == 4)
- {
- if (x >= _dwidth) return 0xFF;
- if ((x & 0x01) == 0)
- return _img4[((x+y*_iwidth)>>1)] >> 4; // even index = bits 7 .. 4
- else
- return _img4[((x+y*_iwidth)>>1)] & 0x0F; // odd index = bits 3 .. 0.
- }
- if (_bpp == 1)
- {
- // Note: _dwidth and _dheight bounds not checked (rounded up -iwidth and _iheight used)
- if (rotation == 1)
- {
- uint16_t tx = x;
- x = _dheight - y - 1;
- y = tx;
- }
- else if (rotation == 2)
- {
- x = _dwidth - x - 1;
- y = _dheight - y - 1;
- }
- else if (rotation == 3)
- {
- uint16_t tx = x;
- x = y;
- y = _dwidth - tx - 1;
- }
- // Return 1 or 0
- return (_img8[(x + y * _bitwidth)>>3] >> (7-(x & 0x7))) & 0x01;
- }
- return 0;
- }
- /***************************************************************************************
- ** Function name: readPixel
- ** Description: Read 565 colour of a pixel at defined coordinates
- ***************************************************************************************/
- uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y)
- {
- 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)
- {
- uint16_t color = _img[x + y * _iwidth];
- return (color >> 8) | (color << 8);
- }
- if (_bpp == 8)
- {
- uint16_t color = _img8[x + y * _iwidth];
- if (color != 0)
- {
- uint8_t blue[] = {0, 11, 21, 31};
- color = (color & 0xE0)<<8 | (color & 0xC0)<<5
- | (color & 0x1C)<<6 | (color & 0x1C)<<3
- | blue[color & 0x03];
- }
- return color;
- }
- if (_bpp == 4)
- {
- if (x >= _dwidth) return 0xFFFF;
- uint16_t color;
- if ((x & 0x01) == 0)
- color = _colorMap[_img4[((x+y*_iwidth)>>1)] >> 4]; // even index = bits 7 .. 4
- else
- color = _colorMap[_img4[((x+y*_iwidth)>>1)] & 0x0F]; // odd index = bits 3 .. 0.
- return color;
- }
- // Note: Must be 1bpp
- // _dwidth and _dheight bounds not checked (rounded up -iwidth and _iheight used)
- if (rotation == 1)
- {
- uint16_t tx = x;
- x = _dheight - y - 1;
- y = tx;
- }
- else if (rotation == 2)
- {
- x = _dwidth - x - 1;
- y = _dheight - y - 1;
- }
- else if (rotation == 3)
- {
- uint16_t tx = x;
- x = y;
- y = _dwidth - tx - 1;
- }
- uint16_t color = (_img8[(x + y * _bitwidth)>>3] << (x & 0x7)) & 0x80;
- if (color) return _tft->bitmap_fg;
- else return _tft->bitmap_bg;
- }
- /***************************************************************************************
- ** 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, uint8_t sbpp)
- {
- if (data == nullptr || !_created) return;
- 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 + ((dx + dy * w) << 1);
- // Pointer within sprite image
- uint8_t *ptrs = (uint8_t *)_img + ((x + y * _iwidth) << 1);
- if(_swapBytes)
- {
- while (dh--)
- {
- // Fast copy with a 1 byte shift
- memcpy(ptrs+1, ptro, (dw<<1) - 1);
- // Now correct just the even numbered bytes
- for (int32_t xp = 0; xp < (dw<<1); xp+=2)
- {
- ptrs[xp] = ptro[xp+1];;
- }
- ptro += w<<1;
- ptrs += _iwidth<<1;
- }
- }
- else
- {
- while (dh--)
- {
- 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
- {
- uint16_t lastColor = 0;
- uint8_t color8 = 0;
- for (int32_t yp = dy; yp < dy + dh; yp++)
- {
- 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[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;
- }
- y++;
- }
- }
- else if (_bpp == 4)
- {
- // The image is assumed to be 4 bit, where each byte corresponds to two pixels.
- // much faster when aligned to a byte boundary, because the alternative is slower, requiring
- // tedious bit operations.
- int sWidth = (_iwidth >> 1);
- uint8_t *ptr = (uint8_t *)data;
- if ((x & 0x01) == 0 && (dx & 0x01) == 0 && (dw & 0x01) == 0)
- {
- x = (x >> 1) + y * sWidth;
- dw = (dw >> 1);
- dx = (dx >> 1) + dy * (w>>1);
- while (dh--)
- {
- memcpy(_img4 + x, ptr + dx, dw);
- dx += (w >> 1);
- x += sWidth;
- }
- }
- else // not optimized
- {
- for (int32_t yp = dy; yp < dy + dh; yp++)
- {
- int32_t ox = x;
- for (int32_t xp = dx; xp < dx + dw; xp++)
- {
- uint32_t color;
- if ((xp & 0x01) == 0)
- color = (ptr[((xp+yp*w)>>1)] & 0xF0) >> 4; // even index = bits 7 .. 4
- else
- color = ptr[((xp-1+yp*w)>>1)] & 0x0F; // odd index = bits 3 .. 0.
- drawPixel(ox, y, color);
- ox++;
- }
- y++;
- }
- }
- }
- else // 1bpp
- {
- // Plot a 1bpp image into a 1bpp Sprite
- uint32_t ww = (w+7)>>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; // Byte starting the line containing source pixel
- int32_t ox = x;
- for (int32_t xp = dx; xp < dx + dw; xp++)
- {
- uint16_t readPixel = (ptr[(xp>>3) + yw] & (0x80 >> (xp & 0x7)) );
- drawPixel(ox++, y, readPixel);
- }
- y++;
- }
- }
- }
- /***************************************************************************************
- ** Function name: pushImage
- ** Description: push 565 colour FLASH (PROGMEM) image into a defined area
- ***************************************************************************************/
- void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data)
- {
- #ifdef ESP32
- pushImage(x, y, w, h, (uint16_t*) data);
- #else
- // Partitioned memory FLASH processor
- if (data == nullptr || !_created) return;
- PI_CLIP;
- if (_bpp == 16) // Plot a 16 bpp image into a 16 bpp Sprite
- {
- for (int32_t yp = dy; yp < dy + dh; yp++)
- {
- int32_t ox = x;
- for (int32_t xp = dx; xp < dx + dw; xp++)
- {
- uint16_t color = pgm_read_word(data + xp + yp * w);
- if(_swapBytes) color = color<<8 | color>>8;
- _img[ox + y * _iwidth] = color;
- ox++;
- }
- y++;
- }
- }
- else if (_bpp == 8) // Plot a 16 bpp image into a 8 bpp Sprite
- {
- for (int32_t yp = dy; yp < dy + dh; yp++)
- {
- int32_t ox = x;
- for (int32_t xp = dx; xp < dx + dw; xp++)
- {
- uint16_t color = pgm_read_word(data + xp + yp * w);
- if(_swapBytes) color = color<<8 | color>>8;
- _img8[ox + y * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3);
- ox++;
- }
- y++;
- }
- }
- else if (_bpp == 4)
- {
- #ifdef TFT_eSPI_DEBUG
- Serial.println("TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data) not implemented");
- #endif
- return;
- }
- else // Plot a 1bpp image into a 1bpp Sprite
- {
- x-= _xDatum; // Remove offsets, drawPixel will add
- y-= _yDatum;
- uint16_t bsw = (w+7) >> 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 check
- }
- /***************************************************************************************
- ** 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 >= w) || (x1 < 0) || (y0 >= h) || (y1 < 0))
- { // Point to that extra "off screen" pixel
- _xs = 0;
- _ys = _dheight;
- _xe = 0;
- _ye = _dheight;
- }
- else
- {
- if (x0 < 0) x0 = 0;
- if (x1 >= w) x1 = w - 1;
- if (y0 < 0) y0 = 0;
- if (y1 >= h) y1 = h - 1;
- _xs = x0;
- _ys = y0;
- _xe = x1;
- _ye = y1;
- }
- _xptr = _xs;
- _yptr = _ys;
- }
- /***************************************************************************************
- ** Function name: pushColor
- ** Description: Send a new pixel to the set window
- ***************************************************************************************/
- void TFT_eSprite::pushColor(uint32_t color)
- {
- if (!_created ) return;
- // Write the colour to RAM in set window
- if (_bpp == 16)
- _img [_xptr + _yptr * _iwidth] = (uint16_t) (color >> 8) | (color << 8);
- else if (_bpp == 8)
- _img8[_xptr + _yptr * _iwidth] = (uint8_t )((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3);
- else if (_bpp == 4)
- {
- uint8_t c = (uint8_t)color & 0x0F;
- if ((_xptr & 0x01) == 0) {
- _img4[(_xptr + _yptr * _iwidth)>>1] = (c << 4) | (_img4[(_xptr + _yptr * _iwidth)>>1] & 0x0F); // new color is in bits 7 .. 4
- }
- else {
- _img4[(_xptr + _yptr * _iwidth)>>1] = (_img4[(_xptr + _yptr * _iwidth)>>1] & 0xF0) | c; // new color is the low bits
- }
- }
- else drawPixel(_xptr, _yptr, color);
- // Increment x
- _xptr++;
- // Wrap on x and y to start, increment y if needed
- if (_xptr > _xe)
- {
- _xptr = _xs;
- _yptr++;
- if (_yptr > _ye) _yptr = _ys;
- }
- }
- /***************************************************************************************
- ** Function name: pushColor
- ** Description: Send a "len" new pixels to the set window
- ***************************************************************************************/
- void TFT_eSprite::pushColor(uint32_t color, uint16_t len)
- {
- if (!_created ) return;
- uint16_t pixelColor;
- if (_bpp == 16)
- pixelColor = (uint16_t) (color >> 8) | (color << 8);
- else if (_bpp == 8)
- pixelColor = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3;
- else pixelColor = (uint16_t) color; // for 1bpp or 4bpp
- while(len--) writeColor(pixelColor);
- }
- /***************************************************************************************
- ** Function name: writeColor
- ** Description: Write a pixel with pre-formatted colour to the set window
- ***************************************************************************************/
- void TFT_eSprite::writeColor(uint16_t color)
- {
- if (!_created ) return;
- // Write 16 bit RGB 565 encoded colour to RAM
- if (_bpp == 16) _img [_xptr + _yptr * _iwidth] = color;
- // Write 8 bit RGB 332 encoded colour to RAM
- else if (_bpp == 8) _img8[_xptr + _yptr * _iwidth] = (uint8_t) color;
- else if (_bpp == 4)
- {
- uint8_t c = (uint8_t)color & 0x0F;
- if ((_xptr & 0x01) == 0)
- _img4[(_xptr + _yptr * _iwidth)>>1] = (c << 4) | (_img4[(_xptr + _yptr * _iwidth)>>1] & 0x0F); // new color is in bits 7 .. 4
- else
- _img4[(_xptr + _yptr * _iwidth)>>1] = (_img4[(_xptr + _yptr * _iwidth)>>1] & 0xF0) | c; // new color is the low bits (x is odd)
- }
- else drawPixel(_xptr, _yptr, color);
- // Increment x
- _xptr++;
- // Wrap on x and y to start, increment y if needed
- if (_xptr > _xe)
- {
- _xptr = _xs;
- _yptr++;
- if (_yptr > _ye) _yptr = _ys;
- }
- }
- /***************************************************************************************
- ** Function name: setScrollRect
- ** Description: Set scroll area within the sprite and the gap fill colour
- ***************************************************************************************/
- // 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;
- if (x < 0) { w += x; x = 0; }
- if (y < 0) { h += y; y = 0; }
- if ((x + w) > _iwidth ) w = _iwidth - x;
- if ((y + h) > _iheight) h = _iheight - y;
- if ( w < 1 || h < 1) return;
- _sx = x;
- _sy = y;
- _sw = w;
- _sh = h;
- _scolor = color;
- }
- /***************************************************************************************
- ** Function name: scroll
- ** Description: Scroll dx,dy pixels, positive right,down, negative left,up
- ***************************************************************************************/
- void TFT_eSprite::scroll(int16_t dx, int16_t dy)
- {
- if (abs(dx) >= _sw || abs(dy) >= _sh)
- {
- fillRect (_sx, _sy, _sw, _sh, _scolor);
- return;
- }
- // Fetch the scroll area width and height set by setScrollRect()
- uint32_t w = _sw - abs(dx); // line width to copy
- uint32_t h = _sh - abs(dy); // lines to copy
- int32_t iw = _iwidth; // rounded up width of sprite
- // Fetch the x,y origin set by setScrollRect()
- uint32_t tx = _sx; // to x
- uint32_t fx = _sx; // from x
- uint32_t ty = _sy; // to y
- uint32_t fy = _sy; // from y
- // Adjust for x delta
- if (dx <= 0) fx -= dx;
- else tx += dx;
- // Adjust for y delta
- if (dy <= 0) fy -= dy;
- else
- { // Scrolling down so start copy from bottom
- ty = ty + _sh - 1; // "To" pointer
- iw = -iw; // Pointer moves backwards
- fy = ty - dy; // "From" pointer
- }
- // Calculate "from y" and "to y" pointers in RAM
- uint32_t fyp = fx + fy * _iwidth;
- uint32_t typ = tx + ty * _iwidth;
- // Now move the pixels in RAM
- if (_bpp == 16)
- {
- while (h--)
- { // move pixel lines (to, from, byte count)
- memmove( _img + typ, _img + fyp, w<<1);
- typ += iw;
- fyp += iw;
- }
- }
- else if (_bpp == 8)
- {
- while (h--)
- { // move pixel lines (to, from, byte count)
- memmove( _img8 + typ, _img8 + fyp, w);
- typ += iw;
- fyp += iw;
- }
- }
- else if (_bpp == 4)
- {
- // could optimize for scrolling by even # pixels using memove (later)
- if (dx > 0) { tx += w; fx += w; } // Start from right edge
- while (h--)
- { // move pixels one by one
- for (uint16_t xp = 0; xp < w; xp++)
- {
- if (dx <= 0) drawPixel(tx + xp, ty, readPixelValue(fx + xp, fy));
- if (dx > 0) drawPixel(tx - xp, ty, readPixelValue(fx - xp, fy));
- }
- if (dy <= 0) { ty++; fy++; }
- else { ty--; fy--; }
- }
- }
- else if (_bpp == 1 )
- {
- if (dx > 0) { tx += w; fx += w; } // Start from right edge
- while (h--)
- { // move pixels one by one
- for (uint16_t xp = 0; xp < w; xp++)
- {
- if (dx <= 0) drawPixel(tx + xp, ty, readPixelValue(fx + xp, fy));
- if (dx > 0) drawPixel(tx - xp, ty, readPixelValue(fx - xp, fy));
- }
- if (dy <= 0) { ty++; fy++; }
- else { ty--; fy--; }
- }
- }
- else return; // Not 1, 4, 8 or 16 bpp
- // Fill the gap left by the scrolling
- if (dx > 0) fillRect(_sx, _sy, dx, _sh, _scolor);
- if (dx < 0) fillRect(_sx + _sw + dx, _sy, -dx, _sh, _scolor);
- if (dy > 0) fillRect(_sx, _sy, _sw, dy, _scolor);
- if (dy < 0) fillRect(_sx, _sy + _sh + dy, _sw, -dy, _scolor);
- }
- /***************************************************************************************
- ** Function name: fillSprite
- ** Description: Fill the whole sprite with defined colour
- ***************************************************************************************/
- void TFT_eSprite::fillSprite(uint32_t color)
- {
- if (!_created || _vpOoB) return;
- // Use memset if possible as it is super fast
- if(_xDatum == 0 && _yDatum == 0 && _xWidth == width())
- {
- 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 fillRect(_vpX - _xDatum, _vpY - _yDatum, _xWidth, _yHeight, color);
- }
- /***************************************************************************************
- ** Function name: width
- ** Description: Return the width of sprite
- ***************************************************************************************/
- // Return the size of the display
- int16_t TFT_eSprite::width(void)
- {
- if (!_created ) return 0;
- if (_bpp > 1) {
- if (_vpDatum) return _xWidth;
- return _dwidth;
- }
- if (rotation & 1) {
- if (_vpDatum) return _xWidth;
- return _dheight;
- }
- if (_vpDatum) return _xWidth;
- return _dwidth;
- }
- /***************************************************************************************
- ** Function name: height
- ** Description: Return the height of sprite
- ***************************************************************************************/
- int16_t TFT_eSprite::height(void)
- {
- if (!_created ) return 0;
- if (_bpp > 1) {
- if (_vpDatum) return _yHeight;
- return _dheight;
- }
- if (rotation & 1) {
- if (_vpDatum) return _yHeight;
- return _dwidth;
- }
- if (_vpDatum) return _yHeight;
- return _dheight;
- }
- /***************************************************************************************
- ** Function name: setRotation
- ** Description: Rotate coordinate frame for 1bpp sprite
- ***************************************************************************************/
- // Does nothing for 4, 8 and 16 bpp sprites.
- void TFT_eSprite::setRotation(uint8_t r)
- {
- if (_bpp != 1) return;
- rotation = r;
-
- if (rotation&1) {
- resetViewport();
- }
- else {
- resetViewport();
- }
- }
- /***************************************************************************************
- ** Function name: getRotation
- ** Description: Get rotation for 1bpp sprite
- ***************************************************************************************/
- uint8_t TFT_eSprite::getRotation(void)
- {
- return rotation;
- }
- /***************************************************************************************
- ** Function name: drawPixel
- ** Description: push a single pixel at an arbitrary position
- ***************************************************************************************/
- 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 < _vpX) || (y < _vpY) ||(x >= _vpW) || (y >= _vpH)) return;
- if (_bpp == 16)
- {
- color = (color >> 8) | (color << 8);
- _img[x+y*_iwidth] = (uint16_t) color;
- }
- else if (_bpp == 8)
- {
- _img8[x+y*_iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3);
- }
- else if (_bpp == 4)
- {
- uint8_t c = color & 0x0F;
- int index = (x+y*_iwidth)>>1;;
- if ((x & 0x01) == 0) {
- _img4[index] = (uint8_t)((c << 4) | (_img4[index] & 0x0F));
- }
- else {
- _img4[index] = (uint8_t)(c | (_img4[index] & 0xF0));
- }
- }
- else // 1 bpp
- {
- if (rotation == 1)
- {
- uint16_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)
- {
- uint16_t tx = x;
- x = y;
- y = _dheight - tx - 1;
- }
- if (color) _img8[(x + y * _bitwidth)>>3] |= (0x80 >> (x & 0x7));
- else _img8[(x + y * _bitwidth)>>3] &= ~(0x80 >> (x & 0x7));
- }
- }
- /***************************************************************************************
- ** Function name: drawLine
- ** Description: draw a line between 2 arbitrary points
- ***************************************************************************************/
- void TFT_eSprite::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color)
- {
- 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) {
- swap_coord(x0, y0);
- swap_coord(x1, y1);
- }
- if (x0 > x1) {
- swap_coord(x0, x1);
- swap_coord(y0, y1);
- }
- int32_t dx = x1 - x0, dy = abs(y1 - y0);;
- int32_t err = dx >> 1, ystep = -1, xs = x0, dlen = 0;
- if (y0 < y1) ystep = 1;
- // Split into steep and not steep for FastH/V separation
- if (steep) {
- for (; x0 <= x1; x0++) {
- dlen++;
- err -= dy;
- if (err < 0) {
- err += dx;
- if (dlen == 1) drawPixel(y0, xs, color);
- else drawFastVLine(y0, xs, dlen, color);
- dlen = 0; y0 += ystep; xs = x0 + 1;
- }
- }
- if (dlen) drawFastVLine(y0, xs, dlen, color);
- }
- else
- {
- for (; x0 <= x1; x0++) {
- dlen++;
- err -= dy;
- if (err < 0) {
- err += dx;
- if (dlen == 1) drawPixel(xs, y0, color);
- else drawFastHLine(xs, y0, dlen, color);
- dlen = 0; y0 += ystep; xs = x0 + 1;
- }
- }
- if (dlen) drawFastHLine(xs, y0, dlen, color);
- }
- }
- /***************************************************************************************
- ** Function name: drawFastVLine
- ** Description: draw a vertical line
- ***************************************************************************************/
- void TFT_eSprite::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color)
- {
- if (!_created || _vpOoB) return;
- x+= _xDatum;
- y+= _yDatum;
- // Clipping
- if ((x < _vpX) || (x >= _vpW) || (y >= _vpH)) return;
- if (y < _vpY) { h += y - _vpY; y = _vpY; }
- if ((y + h) > _vpH) h = _vpH - y;
- if (h < 1) return;
- if (_bpp == 16)
- {
- color = (color >> 8) | (color << 8);
- int32_t yp = x + _iwidth * y;
- while (h--) {_img[yp] = (uint16_t) color; yp += _iwidth;}
- }
- else if (_bpp == 8)
- {
- color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3;
- while (h--) _img8[x + _iwidth * y++] = (uint8_t) color;
- }
- else if (_bpp == 4)
- {
- if ((x & 0x01) == 0)
- {
- uint8_t c = (uint8_t) (color & 0xF) << 4;
- while (h--) {
- _img4[(x + _iwidth * y)>>1] = (uint8_t) (c | (_img4[(x + _iwidth * y)>>1] & 0x0F));
- y++;
- }
- }
- else {
- uint8_t c = (uint8_t)color & 0xF;
- while (h--) {
- _img4[(x + _iwidth * y)>>1] = (uint8_t) (c | (_img4[(x + _iwidth * y)>>1] & 0xF0)); // x is odd; new color goes into the low bits.
- y++;
- }
- }
- }
- else
- {
- x -= _xDatum; // Remove any offset as it will be added by drawPixel
- y -= _yDatum;
- while (h--)
- {
- drawPixel(x, y++, color);
- }
- }
- }
- /***************************************************************************************
- ** Function name: drawFastHLine
- ** Description: draw a horizontal line
- ***************************************************************************************/
- void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color)
- {
- if (!_created || _vpOoB) return;
- x+= _xDatum;
- y+= _yDatum;
- // Clipping
- if ((y < _vpY) || (x >= _vpW) || (y >= _vpH)) return;
- if (x < _vpX) { w += x - _vpX; x = _vpX; }
- if ((x + w) > _vpW) w = _vpW - x;
- if (w < 1) return;
- if (_bpp == 16)
- {
- color = (color >> 8) | (color << 8);
- while (w--) _img[_iwidth * y + x++] = (uint16_t) color;
- }
- else if (_bpp == 8)
- {
- color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3;
- memset(_img8+_iwidth * y + x, (uint8_t)color, w);
- }
- else if (_bpp == 4)
- {
- uint8_t c = (uint8_t)color & 0x0F;
- uint8_t c2 = (c | ((c << 4) & 0xF0));
- if ((x & 0x01) == 1)
- {
- drawPixel(x - _xDatum, y - _yDatum, color);
- x++; w--;
- if (w < 1)
- return;
- }
- if (((w + x) & 0x01) == 1)
- {
- // handle the extra one at the other end
- 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);
- }
- }
- }
- /***************************************************************************************
- ** Function name: fillRect
- ** Description: draw a filled rectangle
- ***************************************************************************************/
- void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color)
- {
- if (!_created || _vpOoB) return;
- x+= _xDatum;
- y+= _yDatum;
- // Clipping
- if ((x >= _vpW) || (y >= _vpH)) return;
- 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;
- int32_t yp = _iwidth * y + x;
- if (_bpp == 16)
- {
- color = (color >> 8) | (color << 8);
- uint32_t iw = w;
- int32_t ys = yp;
- if(h--) {while (iw--) _img[yp++] = (uint16_t) color;}
- yp = ys;
- while (h--)
- {
- yp += _iwidth;
- memcpy( _img+yp, _img+ys, w<<1);
- }
- }
- else if (_bpp == 8)
- {
- color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3;
- while (h--)
- {
- memset(_img8 + yp, (uint8_t)color, w);
- yp += _iwidth;
- }
- }
- else if (_bpp == 4)
- {
- uint8_t c1 = (uint8_t)color & 0x0F;
- uint8_t c2 = c1 | ((c1 << 4) & 0xF0);
- if ((x & 0x01) == 0 && (w & 0x01) == 0)
- {
- yp = (yp >> 1);
- while (h--)
- {
- memset(_img4 + yp, c2, (w>>1));
- yp += (_iwidth >> 1);
- }
- }
- else if ((x & 0x01) == 0)
- {
- // same as above but you have a hangover on the right.
- yp = (yp >> 1);
- while (h--)
- {
- if (w > 1)
- memset(_img4 + yp, c2, (w-1)>>1);
- // handle the rightmost pixel by calling drawPixel
- drawPixel(x+w-1-_xDatum, y+h-_yDatum, c1);
- yp += (_iwidth >> 1);
- }
- }
- else if ((w & 0x01) == 1)
- {
- yp = (yp + 1) >> 1;
- while (h--) {
- 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
- yp += (_iwidth >> 1);
- }
- }
- else
- {
- yp = (yp + 1) >> 1;
- while (h--) {
- 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.
- yp += (_iwidth >> 1);
- }
- }
- }
- else
- {
- x -= _xDatum;
- y -= _yDatum;
- while (h--)
- {
- int32_t ww = w;
- int32_t xx = x;
- while (ww--) drawPixel(xx++, y, color);
- y++;
- }
- }
- }
- /***************************************************************************************
- ** 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 ( _vpOoB || !_created ) return;
- 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;
- #ifdef LOAD_GLCD
- //>>>>>>>>>>>>>>>>>>
- #ifdef LOAD_GFXFF
- if(!gfxFont) { // 'Classic' built-in font
- #endif
- //>>>>>>>>>>>>>>>>>>
- bool fillbg = (bg != color);
- if ((size==1) && fillbg)
- {
- uint8_t column[6];
- uint8_t mask = 0x1;
- for (int8_t i = 0; i < 5; i++ ) column[i] = pgm_read_byte(font + (c * 5) + i);
- column[5] = 0;
- int8_t j, k;
- for (j = 0; j < 8; j++) {
- for (k = 0; k < 5; k++ ) {
- if (column[k] & mask) {
- drawPixel(x + k, y + j, color);
- }
- else {
- drawPixel(x + k, y + j, bg);
- }
- }
- mask <<= 1;
- drawPixel(x + k, y + j, bg);
- }
- }
- else
- {
- for (int8_t i = 0; i < 6; i++ ) {
- uint8_t line;
- if (i == 5)
- line = 0x0;
- else
- line = pgm_read_byte(font + (c * 5) + i);
- if (size == 1) // default size
- {
- for (int8_t j = 0; j < 8; j++) {
- if (line & 0x1) drawPixel(x + i, y + j, color);
- line >>= 1;
- }
- }
- else { // big size
- for (int8_t j = 0; j < 8; j++) {
- if (line & 0x1) fillRect(x + (i * size), y + (j * size), size, size, color);
- else if (fillbg) fillRect(x + i * size, y + j * size, size, size, bg);
- line >>= 1;
- }
- }
- }
- }
- //>>>>>>>>>>>>>>>>>>>>>>>>>>>
- #ifdef LOAD_GFXFF
- } else { // Custom font
- #endif
- //>>>>>>>>>>>>>>>>>>>>>>>>>>>
- #endif // LOAD_GLCD
- #ifdef LOAD_GFXFF
- // Filter out bad characters not present in font
- if ((c >= pgm_read_word(&gfxFont->first)) && (c <= pgm_read_word(&gfxFont->last )))
- {
- //>>>>>>>>>>>>>>>>>>>>>>>>>>>
- c -= pgm_read_word(&gfxFont->first);
- GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]);
- uint8_t *bitmap = (uint8_t *)pgm_read_dword(&gfxFont->bitmap);
- uint32_t bo = pgm_read_word(&glyph->bitmapOffset);
- uint8_t w = pgm_read_byte(&glyph->width),
- h = pgm_read_byte(&glyph->height);
- //xa = pgm_read_byte(&glyph->xAdvance);
- int8_t xo = pgm_read_byte(&glyph->xOffset),
- yo = pgm_read_byte(&glyph->yOffset);
- uint8_t xx, yy, bits=0, bit=0;
- int16_t xo16 = 0, yo16 = 0;
- if(size > 1) {
- xo16 = xo;
- yo16 = yo;
- }
- uint16_t hpc = 0; // Horizontal foreground pixel count
- for(yy=0; yy<h; yy++) {
- for(xx=0; xx<w; xx++) {
- if(bit == 0) {
- bits = pgm_read_byte(&bitmap[bo++]);
- bit = 0x80;
- }
- if(bits & bit) hpc++;
- else {
- if (hpc) {
- if(size == 1) drawFastHLine(x+xo+xx-hpc, y+yo+yy, hpc, color);
- else fillRect(x+(xo16+xx-hpc)*size, y+(yo16+yy)*size, size*hpc, size, color);
- hpc=0;
- }
- }
- bit >>= 1;
- }
- // Draw pixels for this line as we are about to increment yy
- if (hpc) {
- if(size == 1) drawFastHLine(x+xo+xx-hpc, y+yo+yy, hpc, color);
- else fillRect(x+(xo16+xx-hpc)*size, y+(yo16+yy)*size, size*hpc, size, color);
- hpc=0;
- }
- }
- }
- #endif
- #ifdef LOAD_GLCD
- #ifdef LOAD_GFXFF
- } // End classic vs custom font
- #endif
- #endif
- }
- /***************************************************************************************
- ** Function name: drawChar
- ** Description: draw a unicode 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)
- {
- return drawChar(uniCode, x, y, textfont);
- }
- // 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 (_vpOoB || !uniCode) return 0;
- if (font==1) {
- #ifdef LOAD_GLCD
- #ifndef LOAD_GFXFF
- drawChar(x, y, uniCode, textcolor, textbgcolor, textsize);
- return 6 * textsize;
- #endif
- #else
- #ifndef LOAD_GFXFF
- return 0;
- #endif
- #endif
- #ifdef LOAD_GFXFF
- drawChar(x, y, uniCode, textcolor, textbgcolor, textsize);
- if(!gfxFont) { // 'Classic' built-in font
- #ifdef LOAD_GLCD
- return 6 * textsize;
- #else
- return 0;
- #endif
- }
- else {
- if((uniCode >= pgm_read_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 {
- return 0;
- }
- }
- #endif
- }
- if ((font>1) && (font<9) && ((uniCode < 32) || (uniCode > 127))) return 0;
- int32_t width = 0;
- int32_t height = 0;
- uint32_t flash_address = 0;
- uniCode -= 32;
- #ifdef LOAD_FONT2
- if (font == 2) {
- flash_address = pgm_read_dword(&chrtbl_f16[uniCode]);
- width = pgm_read_byte(widtbl_f16 + uniCode);
- height = chr_hgt_f16;
- }
- #ifdef LOAD_RLE
- else
- #endif
- #endif
- #ifdef LOAD_RLE
- {
- if ((font>2) && (font<9)) {
- 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;
- for (int32_t i = 0; i < height; i++)
- {
- if (textcolor != textbgcolor) fillRect(x, pY, width * textsize, textsize, textbgcolor);
- for (int32_t k = 0; k < w; k++)
- {
- line = pgm_read_byte((uint8_t *)flash_address + w * i + k);
- if (line) {
- if (textsize == 1) {
- pX = x + k * 8;
- if (line & 0x80) drawPixel(pX, pY, textcolor);
- if (line & 0x40) drawPixel(pX + 1, pY, textcolor);
- if (line & 0x20) drawPixel(pX + 2, pY, textcolor);
- if (line & 0x10) drawPixel(pX + 3, pY, textcolor);
- if (line & 0x08) drawPixel(pX + 4, pY, textcolor);
- if (line & 0x04) drawPixel(pX + 5, pY, textcolor);
- if (line & 0x02) drawPixel(pX + 6, pY, textcolor);
- if (line & 0x01) drawPixel(pX + 7, pY, textcolor);
- }
- else {
- pX = x + k * 8 * textsize;
- if (line & 0x80) fillRect(pX, pY, textsize, textsize, textcolor);
- if (line & 0x40) fillRect(pX + textsize, pY, textsize, textsize, textcolor);
- if (line & 0x20) fillRect(pX + 2 * textsize, pY, textsize, textsize, textcolor);
- if (line & 0x10) fillRect(pX + 3 * textsize, pY, textsize, textsize, textcolor);
- if (line & 0x08) fillRect(pX + 4 * textsize, pY, textsize, textsize, textcolor);
- if (line & 0x04) fillRect(pX + 5 * textsize, pY, textsize, textsize, textcolor);
- if (line & 0x02) fillRect(pX + 6 * textsize, pY, textsize, textsize, textcolor);
- if (line & 0x01) fillRect(pX + 7 * textsize, pY, textsize, textsize, textcolor);
- }
- }
- }
- pY += textsize;
- }
- }
- #ifdef LOAD_RLE
- else
- #endif
- #endif //FONT2
- #ifdef LOAD_RLE //674 bytes of code
- // Font is not 2 and hence is RLE encoded
- {
- w *= height; // Now w is total number of pixels in the character
- 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);
- 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 {
- line++;
- pc += line;
- }
- }
- }
- 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);
- // 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
- {
- 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);
- }
- }
- }
- }
- // End of RLE font rendering
- #endif
- return width * textsize; // x +
- }
- #ifdef SMOOTH_FONT
- /***************************************************************************************
- ** Function name: drawGlyph
- ** Description: Write a character to the sprite cursor position
- ***************************************************************************************/
- //
- void TFT_eSprite::drawGlyph(uint16_t code)
- {
- uint16_t fg = textcolor;
- uint16_t bg = textbgcolor;
- if (code < 0x21)
- {
- if (code == 0x20) {
- cursor_x += gFont.spaceWidth;
- return;
- }
- if (code == '\n') {
- cursor_x = 0;
- cursor_y += gFont.yAdvance;
- if (textwrapY && (cursor_y >= height())) cursor_y = 0;
- return;
- }
- }
- uint16_t gNum = 0;
- bool found = getUnicodeIndex(code, &gNum);
- if (found)
- {
- bool newSprite = !_created;
- if (newSprite)
- {
- createSprite(gWidth[gNum], gFont.yAdvance);
- if(fg != bg) fillSprite(bg);
- cursor_x = -gdX[gNum];
- cursor_y = 0;
- }
- else
- {
- if( textwrapX && ((cursor_x + gWidth[gNum] + gdX[gNum]) > width())) {
- cursor_y += gFont.yAdvance;
- cursor_x = 0;
- }
- if( textwrapY && ((cursor_y + gFont.yAdvance) > height())) cursor_y = 0;
- if ( cursor_x == 0) cursor_x -= gdX[gNum];
- }
- uint8_t* pbuffer = nullptr;
- const uint8_t* gPtr = (const uint8_t*) gFont.gArray;
- #ifdef FONT_FS_AVAILABLE
- if (fs_font) {
- fontFile.seek(gBitmap[gNum], fs::SeekSet); // This is slow for a significant position shift!
- pbuffer = (uint8_t*)malloc(gWidth[gNum]);
- }
- #endif
- int16_t xs = 0;
- uint16_t dl = 0;
- uint8_t pixel = 0;
- int32_t cgy = cursor_y + gFont.maxAscent - gdY[gNum];
- int32_t cgx = cursor_x + gdX[gNum];
- for (int32_t y = 0; y < gHeight[gNum]; y++)
- {
- #ifdef FONT_FS_AVAILABLE
- if (fs_font) {
- fontFile.read(pbuffer, gWidth[gNum]);
- }
- #endif
- for (int32_t x = 0; x < gWidth[gNum]; x++)
- {
- #ifdef FONT_FS_AVAILABLE
- if (fs_font) {
- pixel = pbuffer[x];
- }
- else
- #endif
- pixel = pgm_read_byte(gPtr + gBitmap[gNum] + x + gWidth[gNum] * y);
- if (pixel)
- {
- if (pixel != 0xFF)
- {
- if (dl) { drawFastHLine( xs, y + cgy, dl, fg); dl = 0; }
- if (_bpp != 1) {
- if (fg == bg) drawPixel(x + cgx, y + cgy, alphaBlend(pixel, fg, readPixel(x + cgx, y + cgy)));
- else drawPixel(x + cgx, y + cgy, alphaBlend(pixel, fg, bg));
- }
- else if (pixel>127) drawPixel(x + cgx, y + cgy, fg);
- }
- else
- {
- if (dl==0) xs = x + cgx;
- dl++;
- }
- }
- else
- {
- if (dl) { drawFastHLine( xs, y + cgy, dl, fg); dl = 0; }
- }
- }
- if (dl) { drawFastHLine( xs, y + cgy, dl, fg); dl = 0; }
- }
- if (pbuffer) free(pbuffer);
- if (newSprite)
- {
- pushSprite(cgx, cursor_y);
- deleteSprite();
- }
- cursor_x += gxAdvance[gNum];
- }
- else
- {
- // Not a Unicode in font so draw a rectangle and move on cursor
- drawRect(cursor_x, cursor_y + gFont.maxAscent - gFont.ascent, gFont.spaceWidth, gFont.ascent, fg);
- cursor_x += gFont.spaceWidth + 1;
- }
- }
- /***************************************************************************************
- ** Function name: printToSprite
- ** Description: Write a string to the sprite cursor position
- ***************************************************************************************/
- void TFT_eSprite::printToSprite(String string)
- {
- if(!fontLoaded) return;
- printToSprite((char*)string.c_str(), string.length());
- }
- /***************************************************************************************
- ** Function name: printToSprite
- ** Description: Write a string to the sprite cursor position
- ***************************************************************************************/
- void TFT_eSprite::printToSprite(char *cbuffer, uint16_t len) //String string)
- {
- if(!fontLoaded) return;
- uint16_t n = 0;
- bool newSprite = !_created;
- if (newSprite)
- {
- int16_t sWidth = 1;
- uint16_t index = 0;
- while (n < len)
- {
- uint16_t unicode = decodeUTF8((uint8_t*)cbuffer, &n, len - n);
- if (getUnicodeIndex(unicode, &index))
- {
- if (n == 0) sWidth -= gdX[index];
- if (n == len-1) sWidth += ( gWidth[index] + gdX[index]);
- else sWidth += gxAdvance[index];
- }
- else sWidth += gFont.spaceWidth + 1;
- }
- createSprite(sWidth, gFont.yAdvance);
- if (textcolor != textbgcolor) fillSprite(textbgcolor);
- }
- n = 0;
- while (n < len)
- {
- uint16_t unicode = decodeUTF8((uint8_t*)cbuffer, &n, len - n);
- //Serial.print("Decoded Unicode = 0x");Serial.println(unicode,HEX);
- //Serial.print("n = ");Serial.println(n);
- drawGlyph(unicode);
- }
- if (newSprite)
- { // The sprite had to be created so place at TFT cursor
- pushSprite(_tft->cursor_x, _tft->cursor_y);
- deleteSprite();
- }
- }
- /***************************************************************************************
- ** Function name: printToSprite
- ** Description: Print character in a Sprite, create sprite if needed
- ***************************************************************************************/
- int16_t TFT_eSprite::printToSprite(int16_t x, int16_t y, uint16_t index)
- {
- bool newSprite = !_created;
- int16_t sWidth = gWidth[index];
- if (newSprite)
- {
- createSprite(sWidth, gFont.yAdvance);
- if (textcolor != textbgcolor) fillSprite(textbgcolor);
- drawGlyph(gUnicode[index]);
- pushSprite(x + gdX[index], y, textbgcolor);
- deleteSprite();
- }
- else drawGlyph(gUnicode[index]);
- return gxAdvance[index];
- }
- #endif
|