Sprite.cpp 76 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598
  1. /**************************************************************************************
  2. // The following class creates Sprites in RAM, graphics can then be drawn in the Sprite
  3. // and rendered quickly onto the TFT screen. The class inherits the graphics functions
  4. // from the TFT_eSPI class. Some functions are overridden by this class so that the
  5. // graphics are written to the Sprite rather than the TFT.
  6. // Coded by Bodmer, see license file in root folder
  7. ***************************************************************************************/
  8. /***************************************************************************************
  9. // Color bytes are swapped when writing to RAM, this introduces a small overhead but
  10. // there is a nett performance gain by using swapped bytes.
  11. ***************************************************************************************/
  12. /***************************************************************************************
  13. ** Function name: TFT_eSprite
  14. ** Description: Class constructor
  15. ***************************************************************************************/
  16. TFT_eSprite::TFT_eSprite(TFT_eSPI *tft)
  17. {
  18. _tft = tft; // Pointer to tft class so we can call member functions
  19. _iwidth = 0; // Initialise width and height to 0 (it does not exist yet)
  20. _iheight = 0;
  21. _bpp = 16;
  22. _swapBytes = false; // Do not swap pushImage colour bytes by default
  23. _created = false;
  24. _vpOoB = true;
  25. _xs = 0; // window bounds for pushColor
  26. _ys = 0;
  27. _xe = 0;
  28. _ye = 0;
  29. _xptr = 0; // pushColor coordinate
  30. _yptr = 0;
  31. _colorMap = nullptr;
  32. _psram_enable = true;
  33. }
  34. /***************************************************************************************
  35. ** Function name: createSprite
  36. ** Description: Create a sprite (bitmap) of defined width and height
  37. ***************************************************************************************/
  38. // cast returned value to (uint8_t*) for 8 bit or (uint16_t*) for 16 bit colours
  39. void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames)
  40. {
  41. if ( _created ) return _img8_1;
  42. if ( w < 1 || h < 1 ) return nullptr;
  43. _iwidth = _dwidth = _bitwidth = w;
  44. _iheight = _dheight = h;
  45. cursor_x = 0;
  46. cursor_y = 0;
  47. // Default scroll rectangle and gap fill colour
  48. _sx = 0;
  49. _sy = 0;
  50. _sw = w;
  51. _sh = h;
  52. _scolor = TFT_BLACK;
  53. _img8 = (uint8_t*) callocSprite(w, h, frames);
  54. _img8_1 = _img8;
  55. _img8_2 = _img8;
  56. _img = (uint16_t*) _img8;
  57. _img4 = _img8;
  58. if ( (_bpp == 16) && (frames > 1) ) {
  59. _img8_2 = _img8 + (w * h * 2 + 1);
  60. }
  61. // ESP32 only 16bpp check
  62. //if (esp_ptr_dma_capable(_img8_1)) Serial.println("DMA capable Sprite pointer _img8_1");
  63. //else Serial.println("Not a DMA capable Sprite pointer _img8_1");
  64. //if (esp_ptr_dma_capable(_img8_2)) Serial.println("DMA capable Sprite pointer _img8_2");
  65. //else Serial.println("Not a DMA capable Sprite pointer _img8_2");
  66. if ( (_bpp == 8) && (frames > 1) ) {
  67. _img8_2 = _img8 + (w * h + 1);
  68. }
  69. if ( (_bpp == 4) && (_colorMap == nullptr)) createPalette(default_4bit_palette);
  70. // This is to make it clear what pointer size is expected to be used
  71. // but casting in the user sketch is needed due to the use of void*
  72. if ( (_bpp == 1) && (frames > 1) )
  73. {
  74. w = (w+7) & 0xFFF8;
  75. _img8_2 = _img8 + ( (w>>3) * h + 1 );
  76. }
  77. if (_img8)
  78. {
  79. _created = true;
  80. rotation = 0;
  81. setViewport(0, 0, _dwidth, _dheight);
  82. setPivot(_iwidth/2, _iheight/2);
  83. return _img8_1;
  84. }
  85. return nullptr;
  86. }
  87. /***************************************************************************************
  88. ** Function name: getPointer
  89. ** Description: Returns pointer to start of sprite memory area
  90. ***************************************************************************************/
  91. void* TFT_eSprite::getPointer(void)
  92. {
  93. if (!_created) return nullptr;
  94. return _img8_1;
  95. }
  96. /***************************************************************************************
  97. ** Function name: created
  98. ** Description: Returns true if sprite has been created
  99. ***************************************************************************************/
  100. bool TFT_eSprite::created(void)
  101. {
  102. return _created;
  103. }
  104. /***************************************************************************************
  105. ** Function name: ~TFT_eSprite
  106. ** Description: Class destructor
  107. ***************************************************************************************/
  108. TFT_eSprite::~TFT_eSprite(void)
  109. {
  110. deleteSprite();
  111. #ifdef SMOOTH_FONT
  112. if(fontLoaded) unloadFont();
  113. #endif
  114. }
  115. /***************************************************************************************
  116. ** Function name: callocSprite
  117. ** Description: Allocate a memory area for the Sprite and return pointer
  118. ***************************************************************************************/
  119. void* TFT_eSprite::callocSprite(int16_t w, int16_t h, uint8_t frames)
  120. {
  121. // Add one extra "off screen" pixel to point out-of-bounds setWindow() coordinates
  122. // this means push/writeColor functions do not need additional bounds checks and
  123. // hence will run faster in normal circumstances.
  124. uint8_t* ptr8 = nullptr;
  125. if (frames > 2) frames = 2; // Currently restricted to 2 frame buffers
  126. if (frames < 1) frames = 1;
  127. if (_bpp == 16)
  128. {
  129. #if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT)
  130. if ( psramFound() && _psram_enable && !_tft->DMA_Enabled)
  131. {
  132. ptr8 = ( uint8_t*) ps_calloc(frames * w * h + frames, sizeof(uint16_t));
  133. //Serial.println("PSRAM");
  134. }
  135. else
  136. #endif
  137. {
  138. ptr8 = ( uint8_t*) calloc(frames * w * h + frames, sizeof(uint16_t));
  139. //Serial.println("Normal RAM");
  140. }
  141. }
  142. else if (_bpp == 8)
  143. {
  144. #if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT)
  145. if ( psramFound() && _psram_enable ) ptr8 = ( uint8_t*) ps_calloc(frames * w * h + frames, sizeof(uint8_t));
  146. else
  147. #endif
  148. ptr8 = ( uint8_t*) calloc(frames * w * h + frames, sizeof(uint8_t));
  149. }
  150. else if (_bpp == 4)
  151. {
  152. w = (w+1) & 0xFFFE; // width needs to be multiple of 2, with an extra "off screen" pixel
  153. _iwidth = w;
  154. #if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT)
  155. if ( psramFound() && _psram_enable ) ptr8 = ( uint8_t*) ps_calloc(((frames * w * h) >> 1) + frames, sizeof(uint8_t));
  156. else
  157. #endif
  158. ptr8 = ( uint8_t*) calloc(((frames * w * h) >> 1) + frames, sizeof(uint8_t));
  159. }
  160. else // Must be 1 bpp
  161. {
  162. //_dwidth Display width+height in pixels always in rotation 0 orientation
  163. //_dheight Not swapped for sprite rotations
  164. // Note: for 1bpp _iwidth and _iheight are swapped during Sprite rotations
  165. w = (w+7) & 0xFFF8; // width should be the multiple of 8 bits to be compatible with epdpaint
  166. _iwidth = w; // _iwidth is rounded up to be multiple of 8, so might not be = _dwidth
  167. _bitwidth = w; // _bitwidth will not be rotated whereas _iwidth may be
  168. #if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT)
  169. if ( psramFound() && _psram_enable ) ptr8 = ( uint8_t*) ps_calloc(frames * (w>>3) * h + frames, sizeof(uint8_t));
  170. else
  171. #endif
  172. ptr8 = ( uint8_t*) calloc(frames * (w>>3) * h + frames, sizeof(uint8_t));
  173. }
  174. return ptr8;
  175. }
  176. /***************************************************************************************
  177. ** Function name: createPalette (from RAM array)
  178. ** Description: Set a palette for a 4-bit per pixel sprite
  179. ***************************************************************************************/
  180. void TFT_eSprite::createPalette(uint16_t colorMap[], uint8_t colors)
  181. {
  182. if (_colorMap != nullptr)
  183. {
  184. free(_colorMap);
  185. }
  186. if (colorMap == nullptr)
  187. {
  188. // Create a color map using the default FLASH map
  189. createPalette(default_4bit_palette);
  190. return;
  191. }
  192. // Allocate and clear memory for 16 color map
  193. _colorMap = (uint16_t *)calloc(16, sizeof(uint16_t));
  194. if (colors > 16) colors = 16;
  195. // Copy map colors
  196. for (uint8_t i = 0; i < colors; i++)
  197. {
  198. _colorMap[i] = colorMap[i];
  199. }
  200. }
  201. /***************************************************************************************
  202. ** Function name: createPalette (from FLASH array)
  203. ** Description: Set a palette for a 4-bit per pixel sprite
  204. ***************************************************************************************/
  205. void TFT_eSprite::createPalette(const uint16_t colorMap[], uint8_t colors)
  206. {
  207. if (colorMap == nullptr)
  208. {
  209. // Create a color map using the default FLASH map
  210. colorMap = default_4bit_palette;
  211. }
  212. // Allocate and clear memory for 16 color map
  213. _colorMap = (uint16_t *)calloc(16, sizeof(uint16_t));
  214. if (colors > 16) colors = 16;
  215. // Copy map colors
  216. for (uint8_t i = 0; i < colors; i++)
  217. {
  218. _colorMap[i] = pgm_read_word(colorMap++);
  219. }
  220. }
  221. /***************************************************************************************
  222. ** Function name: frameBuffer
  223. ** Description: For 1 bpp Sprites, select the frame used for graphics
  224. ***************************************************************************************/
  225. // Frames are numbered 1 and 2
  226. void* TFT_eSprite::frameBuffer(int8_t f)
  227. {
  228. if (!_created) return nullptr;
  229. if ( f == 2 ) _img8 = _img8_2;
  230. else _img8 = _img8_1;
  231. if (_bpp == 16) _img = (uint16_t*)_img8;
  232. //if (_bpp == 8) _img8 = _img8;
  233. if (_bpp == 4) _img4 = _img8;
  234. return _img8;
  235. }
  236. /***************************************************************************************
  237. ** Function name: setColorDepth
  238. ** Description: Set bits per pixel for colour (1, 8 or 16)
  239. ***************************************************************************************/
  240. void* TFT_eSprite::setColorDepth(int8_t b)
  241. {
  242. // Do not re-create the sprite if the colour depth does not change
  243. if (_bpp == b) return _img8_1;
  244. // Validate the new colour depth
  245. if ( b > 8 ) _bpp = 16; // Bytes per pixel
  246. else if ( b > 4 ) _bpp = 8;
  247. else if ( b > 1 ) _bpp = 4;
  248. else _bpp = 1;
  249. // Can't change an existing sprite's colour depth so delete it
  250. if (_created) free(_img8_1);
  251. // If it existed, re-create the sprite with the new colour depth
  252. if (_created)
  253. {
  254. _created = false;
  255. return createSprite(_dwidth, _dheight);
  256. }
  257. return nullptr;
  258. }
  259. /***************************************************************************************
  260. ** Function name: getColorDepth
  261. ** Description: Get bits per pixel for colour (1, 8 or 16)
  262. ***************************************************************************************/
  263. int8_t TFT_eSprite::getColorDepth(void)
  264. {
  265. if (_created) return _bpp;
  266. else return 0;
  267. }
  268. /***************************************************************************************
  269. ** Function name: setBitmapColor
  270. ** Description: Set the 1bpp foreground foreground and background colour
  271. ***************************************************************************************/
  272. void TFT_eSprite::setBitmapColor(uint16_t c, uint16_t b)
  273. {
  274. if (c == b) b = ~c;
  275. _tft->bitmap_fg = c;
  276. _tft->bitmap_bg = b;
  277. }
  278. /***************************************************************************************
  279. ** Function name: setPaletteColor
  280. ** Description: Set the 4bpp palette color at the given index
  281. ***************************************************************************************/
  282. void TFT_eSprite::setPaletteColor(uint8_t index, uint16_t color)
  283. {
  284. if (_colorMap == nullptr || index > 15) return; // out of bounds
  285. _colorMap[index] = color;
  286. }
  287. /***************************************************************************************
  288. ** Function name: getPaletteColor
  289. ** Description: Return the palette color at 4bpp index, or 0 on error.
  290. ***************************************************************************************/
  291. uint16_t TFT_eSprite::getPaletteColor(uint8_t index)
  292. {
  293. if (_colorMap == nullptr || index > 15) return 0; // out of bounds
  294. return _colorMap[index];
  295. }
  296. /***************************************************************************************
  297. ** Function name: deleteSprite
  298. ** Description: Delete the sprite to free up memory (RAM)
  299. ***************************************************************************************/
  300. void TFT_eSprite::deleteSprite(void)
  301. {
  302. if (_colorMap != nullptr)
  303. {
  304. free(_colorMap);
  305. _colorMap = nullptr;
  306. }
  307. if (_created)
  308. {
  309. free(_img8_1);
  310. _img8 = nullptr;
  311. _created = false;
  312. _vpOoB = true; // TFT_eSPI class write() uses this to check for valid sprite
  313. }
  314. }
  315. /***************************************************************************************
  316. ** Function name: pushRotated - Fast fixed point integer maths version
  317. ** Description: Push rotated Sprite to TFT screen
  318. ***************************************************************************************/
  319. #define FP_SCALE 10
  320. bool TFT_eSprite::pushRotated(int16_t angle, uint32_t transp)
  321. {
  322. if ( !_created || _tft->_vpOoB) return false;
  323. // Bounding box parameters
  324. int16_t min_x;
  325. int16_t min_y;
  326. int16_t max_x;
  327. int16_t max_y;
  328. // Get the bounding box of this rotated source Sprite relative to Sprite pivot
  329. if ( !getRotatedBounds(angle, &min_x, &min_y, &max_x, &max_y) ) return false;
  330. uint16_t sline_buffer[max_x - min_x + 1];
  331. int32_t xt = min_x - _tft->_xPivot;
  332. int32_t yt = min_y - _tft->_yPivot;
  333. uint32_t xe = _dwidth << FP_SCALE;
  334. uint32_t ye = _dheight << FP_SCALE;
  335. uint32_t tpcolor = transp;
  336. if (transp != 0x00FFFFFF) {
  337. if (_bpp == 4) tpcolor = _colorMap[transp & 0x0F];
  338. tpcolor = tpcolor>>8 | tpcolor<<8; // Working with swapped color bytes
  339. }
  340. _tft->startWrite(); // Avoid transaction overhead for every tft pixel
  341. // Scan destination bounding box and fetch transformed pixels from source Sprite
  342. for (int32_t y = min_y; y <= max_y; y++, yt++) {
  343. int32_t x = min_x;
  344. uint32_t xs = (_cosra * xt - (_sinra * yt - (_xPivot << FP_SCALE)) + (1 << (FP_SCALE - 1)));
  345. uint32_t ys = (_sinra * xt + (_cosra * yt + (_yPivot << FP_SCALE)) + (1 << (FP_SCALE - 1)));
  346. while ((xs >= xe || ys >= ye) && x < max_x) { x++; xs += _cosra; ys += _sinra; }
  347. if (x == max_x) continue;
  348. uint32_t pixel_count = 0;
  349. do {
  350. uint32_t rp;
  351. int32_t xp = xs >> FP_SCALE;
  352. int32_t yp = ys >> FP_SCALE;
  353. if (_bpp == 16) {rp = _img[xp + yp * _iwidth]; }
  354. else { rp = readPixel(xp, yp); rp = (uint16_t)(rp>>8 | rp<<8); }
  355. if (tpcolor == rp) {
  356. if (pixel_count) {
  357. // TFT window is already clipped, so this is faster than pushImage()
  358. _tft->setWindow(x - pixel_count, y, x, y);
  359. _tft->pushPixels(sline_buffer, pixel_count);
  360. pixel_count = 0;
  361. }
  362. }
  363. else {
  364. sline_buffer[pixel_count++] = rp;
  365. }
  366. } while (++x < max_x && (xs += _cosra) < xe && (ys += _sinra) < ye);
  367. if (pixel_count) {
  368. // TFT window is already clipped, so this is faster than pushImage()
  369. _tft->setWindow(x - pixel_count, y, x, y);
  370. _tft->pushPixels(sline_buffer, pixel_count);
  371. }
  372. }
  373. _tft->endWrite(); // End transaction
  374. return true;
  375. }
  376. /***************************************************************************************
  377. ** Function name: pushRotated - Fast fixed point integer maths version
  378. ** Description: Push a rotated copy of the Sprite to another Sprite
  379. ***************************************************************************************/
  380. // Not compatible with 4bpp
  381. bool TFT_eSprite::pushRotated(TFT_eSprite *spr, int16_t angle, uint32_t transp)
  382. {
  383. if ( !_created || _bpp == 4) return false; // Check this Sprite is created
  384. if ( !spr->_created || spr->_bpp == 4) return false; // Ckeck destination Sprite is created
  385. // Bounding box parameters
  386. int16_t min_x;
  387. int16_t min_y;
  388. int16_t max_x;
  389. int16_t max_y;
  390. // Get the bounding box of this rotated source Sprite
  391. if ( !getRotatedBounds(spr, angle, &min_x, &min_y, &max_x, &max_y) ) return false;
  392. uint16_t sline_buffer[max_x - min_x + 1];
  393. int32_t xt = min_x - spr->_xPivot;
  394. int32_t yt = min_y - spr->_yPivot;
  395. uint32_t xe = _dwidth << FP_SCALE;
  396. uint32_t ye = _dheight << FP_SCALE;
  397. uint32_t tpcolor = transp;
  398. if (transp != 0x00FFFFFF) {
  399. if (_bpp == 4) tpcolor = _colorMap[transp & 0x0F];
  400. tpcolor = tpcolor>>8 | tpcolor<<8; // Working with swapped color bytes
  401. }
  402. bool oldSwapBytes = spr->getSwapBytes();
  403. spr->setSwapBytes(false);
  404. // Scan destination bounding box and fetch transformed pixels from source Sprite
  405. for (int32_t y = min_y; y <= max_y; y++, yt++) {
  406. int32_t x = min_x;
  407. uint32_t xs = (_cosra * xt - (_sinra * yt - (_xPivot << FP_SCALE)) + (1 << (FP_SCALE - 1)));
  408. uint32_t ys = (_sinra * xt + (_cosra * yt + (_yPivot << FP_SCALE)) + (1 << (FP_SCALE - 1)));
  409. while ((xs >= xe || ys >= ye) && x < max_x) { x++; xs += _cosra; ys += _sinra; }
  410. if (x == max_x) continue;
  411. uint32_t pixel_count = 0;
  412. do {
  413. uint32_t rp;
  414. int32_t xp = xs >> FP_SCALE;
  415. int32_t yp = ys >> FP_SCALE;
  416. if (_bpp == 16) rp = _img[xp + yp * _iwidth];
  417. else { rp = readPixel(xp, yp); rp = (uint16_t)(rp>>8 | rp<<8); }
  418. if (tpcolor == rp) {
  419. if (pixel_count) {
  420. spr->pushImage(x - pixel_count, y, pixel_count, 1, sline_buffer);
  421. pixel_count = 0;
  422. }
  423. }
  424. else {
  425. sline_buffer[pixel_count++] = rp;
  426. }
  427. } while (++x < max_x && (xs += _cosra) < xe && (ys += _sinra) < ye);
  428. if (pixel_count) spr->pushImage(x - pixel_count, y, pixel_count, 1, sline_buffer);
  429. }
  430. spr->setSwapBytes(oldSwapBytes);
  431. return true;
  432. }
  433. /***************************************************************************************
  434. ** Function name: getRotatedBounds
  435. ** Description: Get TFT bounding box of a rotated Sprite wrt pivot
  436. ***************************************************************************************/
  437. bool TFT_eSprite::getRotatedBounds(int16_t angle, int16_t *min_x, int16_t *min_y,
  438. int16_t *max_x, int16_t *max_y)
  439. {
  440. // Get the bounding box of this rotated source Sprite relative to Sprite pivot
  441. getRotatedBounds(angle, width(), height(), _xPivot, _yPivot, min_x, min_y, max_x, max_y);
  442. // Move bounding box so source Sprite pivot coincides with TFT pivot
  443. *min_x += _tft->_xPivot;
  444. *max_x += _tft->_xPivot;
  445. *min_y += _tft->_yPivot;
  446. *max_y += _tft->_yPivot;
  447. // Return if bounding box is outside of TFT viewport
  448. if (*min_x > _tft->_vpW) return false;
  449. if (*min_y > _tft->_vpH) return false;
  450. if (*max_x < _tft->_vpX) return false;
  451. if (*max_y < _tft->_vpY) return false;
  452. // Clip bounding box to be within TFT viewport
  453. if (*min_x < _tft->_vpX) *min_x = _tft->_vpX;
  454. if (*min_y < _tft->_vpY) *min_y = _tft->_vpY;
  455. if (*max_x > _tft->_vpW) *max_x = _tft->_vpW;
  456. if (*max_y > _tft->_vpH) *max_y = _tft->_vpH;
  457. return true;
  458. }
  459. /***************************************************************************************
  460. ** Function name: getRotatedBounds
  461. ** Description: Get destination Sprite bounding box of a rotated Sprite wrt pivot
  462. ***************************************************************************************/
  463. bool TFT_eSprite::getRotatedBounds(TFT_eSprite *spr, int16_t angle, int16_t *min_x, int16_t *min_y,
  464. int16_t *max_x, int16_t *max_y)
  465. {
  466. // Get the bounding box of this rotated source Sprite relative to Sprite pivot
  467. getRotatedBounds(angle, width(), height(), _xPivot, _yPivot, min_x, min_y, max_x, max_y);
  468. // Move bounding box so source Sprite pivot coincides with destination Sprite pivot
  469. *min_x += spr->_xPivot;
  470. *max_x += spr->_xPivot;
  471. *min_y += spr->_yPivot;
  472. *max_y += spr->_yPivot;
  473. // Test only to show bounding box
  474. // spr->fillSprite(TFT_BLACK);
  475. // spr->drawRect(min_x, min_y, max_x - min_x + 1, max_y - min_y + 1, TFT_GREEN);
  476. // Return if bounding box is completely outside of destination Sprite
  477. if (*min_x > spr->width()) return true;
  478. if (*min_y > spr->height()) return true;
  479. if (*max_x < 0) return true;
  480. if (*max_y < 0) return true;
  481. // Clip bounding box to Sprite boundaries
  482. // Clipping to a viewport will be done by destination Sprite pushImage function
  483. if (*min_x < 0) min_x = 0;
  484. if (*min_y < 0) min_y = 0;
  485. if (*max_x > spr->width()) *max_x = spr->width();
  486. if (*max_y > spr->height()) *max_y = spr->height();
  487. return true;
  488. }
  489. /***************************************************************************************
  490. ** Function name: rotatedBounds
  491. ** Description: Get bounding box of a rotated Sprite wrt pivot
  492. ***************************************************************************************/
  493. void TFT_eSprite::getRotatedBounds(int16_t angle, int16_t w, int16_t h, int16_t xp, int16_t yp,
  494. int16_t *min_x, int16_t *min_y, int16_t *max_x, int16_t *max_y)
  495. {
  496. // Trig values for the rotation
  497. float radAngle = -angle * 0.0174532925; // Convert degrees to radians
  498. float sina = sin(radAngle);
  499. float cosa = cos(radAngle);
  500. w -= xp; // w is now right edge coordinate relative to xp
  501. h -= yp; // h is now bottom edge coordinate relative to yp
  502. // Calculate new corner coordinates
  503. int16_t x0 = -xp * cosa - yp * sina;
  504. int16_t y0 = xp * sina - yp * cosa;
  505. int16_t x1 = w * cosa - yp * sina;
  506. int16_t y1 = -w * sina - yp * cosa;
  507. int16_t x2 = h * sina + w * cosa;
  508. int16_t y2 = h * cosa - w * sina;
  509. int16_t x3 = h * sina - xp * cosa;
  510. int16_t y3 = h * cosa + xp * sina;
  511. // Find bounding box extremes, enlarge box to accomodate rounding errors
  512. *min_x = x0-2;
  513. if (x1 < *min_x) *min_x = x1-2;
  514. if (x2 < *min_x) *min_x = x2-2;
  515. if (x3 < *min_x) *min_x = x3-2;
  516. *max_x = x0+2;
  517. if (x1 > *max_x) *max_x = x1+2;
  518. if (x2 > *max_x) *max_x = x2+2;
  519. if (x3 > *max_x) *max_x = x3+2;
  520. *min_y = y0-2;
  521. if (y1 < *min_y) *min_y = y1-2;
  522. if (y2 < *min_y) *min_y = y2-2;
  523. if (y3 < *min_y) *min_y = y3-2;
  524. *max_y = y0+2;
  525. if (y1 > *max_y) *max_y = y1+2;
  526. if (y2 > *max_y) *max_y = y2+2;
  527. if (y3 > *max_y) *max_y = y3+2;
  528. _sinra = round(sina * (1<<FP_SCALE));
  529. _cosra = round(cosa * (1<<FP_SCALE));
  530. }
  531. /***************************************************************************************
  532. ** Function name: pushSprite
  533. ** Description: Push the sprite to the TFT at x, y
  534. ***************************************************************************************/
  535. void TFT_eSprite::pushSprite(int32_t x, int32_t y)
  536. {
  537. if (!_created) return;
  538. if (_bpp == 16)
  539. {
  540. bool oldSwapBytes = _tft->getSwapBytes();
  541. _tft->setSwapBytes(false);
  542. _tft->pushImage(x, y, _dwidth, _dheight, _img );
  543. _tft->setSwapBytes(oldSwapBytes);
  544. }
  545. else if (_bpp == 4)
  546. {
  547. _tft->pushImage(x, y, _dwidth, _dheight, _img4, false, _colorMap);
  548. }
  549. else _tft->pushImage(x, y, _dwidth, _dheight, _img8, (bool)(_bpp == 8));
  550. }
  551. /***************************************************************************************
  552. ** Function name: pushSprite
  553. ** Description: Push the sprite to the TFT at x, y with transparent colour
  554. ***************************************************************************************/
  555. void TFT_eSprite::pushSprite(int32_t x, int32_t y, uint16_t transp)
  556. {
  557. if (!_created) return;
  558. if (_bpp == 16)
  559. {
  560. bool oldSwapBytes = _tft->getSwapBytes();
  561. _tft->setSwapBytes(false);
  562. _tft->pushImage(x, y, _dwidth, _dheight, _img, transp );
  563. _tft->setSwapBytes(oldSwapBytes);
  564. }
  565. else if (_bpp == 8)
  566. {
  567. transp = (uint8_t)((transp & 0xE000)>>8 | (transp & 0x0700)>>6 | (transp & 0x0018)>>3);
  568. _tft->pushImage(x, y, _dwidth, _dheight, _img8, (uint8_t)transp, (bool)true);
  569. }
  570. else if (_bpp == 4)
  571. {
  572. _tft->pushImage(x, y, _dwidth, _dheight, _img4, (uint8_t)(transp & 0x0F), false, _colorMap);
  573. }
  574. else _tft->pushImage(x, y, _dwidth, _dheight, _img8, 0, (bool)false);
  575. }
  576. /***************************************************************************************
  577. ** Function name: pushToSprite
  578. ** Description: Push the sprite to another sprite at x, y
  579. ***************************************************************************************/
  580. // Note: The following sprite to sprite colour depths are currently supported:
  581. // Source Destination
  582. // 16bpp -> 16bpp
  583. // 16bpp -> 8bpp
  584. // 8bpp -> 8bpp
  585. // 4bpp -> 4bpp (note: color translation depends on the 2 sprites pallete colors)
  586. // 1bpp -> 1bpp (note: color translation depends on the 2 sprites bitmap colors)
  587. bool TFT_eSprite::pushToSprite(TFT_eSprite *dspr, int32_t x, int32_t y)
  588. {
  589. if (!_created) return false;
  590. if (!dspr->created()) return false;
  591. // Check destination sprite compatibility
  592. int8_t ds_bpp = dspr->getColorDepth();
  593. if (_bpp == 16 && ds_bpp != 16 && ds_bpp != 8) return false;
  594. if (_bpp == 8 && ds_bpp != 8) return false;
  595. if (_bpp == 4 && ds_bpp != 4) return false;
  596. if (_bpp == 1 && ds_bpp != 1) return false;
  597. bool oldSwapBytes = dspr->getSwapBytes();
  598. dspr->setSwapBytes(false);
  599. dspr->pushImage(x, y, _dwidth, _dheight, _img, _bpp);
  600. dspr->setSwapBytes(oldSwapBytes);
  601. return true;
  602. }
  603. /***************************************************************************************
  604. ** Function name: pushToSprite
  605. ** Description: Push the sprite to another sprite at x, y with transparent colour
  606. ***************************************************************************************/
  607. // Note: The following sprite to sprite colour depths are currently supported:
  608. // Source Destination
  609. // 16bpp -> 16bpp
  610. // 16bpp -> 8bpp
  611. // 8bpp -> 8bpp
  612. // 1bpp -> 1bpp
  613. bool TFT_eSprite::pushToSprite(TFT_eSprite *dspr, int32_t x, int32_t y, uint16_t transp)
  614. {
  615. if ( !_created || !dspr->_created) return false; // Check Sprites exist
  616. // Check destination sprite compatibility
  617. int8_t ds_bpp = dspr->getColorDepth();
  618. if (_bpp == 16 && ds_bpp != 16 && ds_bpp != 8) return false;
  619. if (_bpp == 8 && ds_bpp != 8) return false;
  620. if (_bpp == 4 || ds_bpp == 4) return false;
  621. if (_bpp == 1 && ds_bpp != 1) return false;
  622. bool oldSwapBytes = dspr->getSwapBytes();
  623. uint16_t sline_buffer[width()];
  624. transp = transp>>8 | transp<<8;
  625. // Scan destination bounding box and fetch transformed pixels from source Sprite
  626. for (int32_t ys = 0; ys < height(); ys++) {
  627. int32_t ox = x;
  628. uint32_t pixel_count = 0;
  629. for (int32_t xs = 0; xs < width(); xs++) {
  630. uint16_t rp = 0;
  631. if (_bpp == 16) rp = _img[xs + ys * width()];
  632. else { rp = readPixel(xs, ys); rp = rp>>8 | rp<<8; }
  633. //dspr->drawPixel(xs, ys, rp);
  634. if (transp == rp) {
  635. if (pixel_count) {
  636. dspr->pushImage(ox, y, pixel_count, 1, sline_buffer, _bpp);
  637. ox += pixel_count;
  638. pixel_count = 0;
  639. }
  640. else ox++;
  641. }
  642. else {
  643. sline_buffer[pixel_count++] = rp;
  644. }
  645. }
  646. if (pixel_count) dspr->pushImage(ox, y, pixel_count, 1, sline_buffer);
  647. y++;
  648. }
  649. dspr->setSwapBytes(oldSwapBytes);
  650. return true;
  651. }
  652. /***************************************************************************************
  653. ** Function name: pushSprite
  654. ** Description: Push a cropped sprite to the TFT at tx, ty
  655. ***************************************************************************************/
  656. bool TFT_eSprite::pushSprite(int32_t tx, int32_t ty, int32_t sx, int32_t sy, int32_t sw, int32_t sh)
  657. {
  658. if (!_created) return false;
  659. // Perform window boundary checks and crop if needed
  660. setWindow(sx, sy, sx + sw - 1, sy + sh - 1);
  661. /* These global variables are now populated for the sprite
  662. _xs = x start coordinate
  663. _ys = y start coordinate
  664. _xe = x end coordinate (inclusive)
  665. _ye = y end coordinate (inclusive)
  666. */
  667. // Calculate new sprite window bounding box width and height
  668. sw = _xe - _xs + 1;
  669. sh = _ye - _ys + 1;
  670. if (_ys >= _iheight) return false;
  671. if (_bpp == 16)
  672. {
  673. bool oldSwapBytes = _tft->getSwapBytes();
  674. _tft->setSwapBytes(false);
  675. // Check if a faster block copy to screen is possible
  676. if ( sx == 0 && sw == _dwidth)
  677. _tft->pushImage(tx, ty, sw, sh, _img + _iwidth * _ys );
  678. else // Render line by line
  679. while (sh--)
  680. _tft->pushImage(tx, ty++, sw, 1, _img + _xs + _iwidth * _ys++ );
  681. _tft->setSwapBytes(oldSwapBytes);
  682. }
  683. else if (_bpp == 8)
  684. {
  685. // Check if a faster block copy to screen is possible
  686. if ( sx == 0 && sw == _dwidth)
  687. _tft->pushImage(tx, ty, sw, sh, _img8 + _iwidth * _ys, (bool)true );
  688. else // Render line by line
  689. while (sh--)
  690. _tft->pushImage(tx, ty++, sw, 1, _img8 + _xs + _iwidth * _ys++, (bool)true );
  691. }
  692. else if (_bpp == 4)
  693. {
  694. // Check if a faster block copy to screen is possible
  695. if ( sx == 0 && sw == _dwidth)
  696. _tft->pushImage(tx, ty, sw, sh, _img4 + (_iwidth>>1) * _ys, false, _colorMap );
  697. else // Render line by line
  698. {
  699. int32_t ds = _xs&1; // Odd x start pixel
  700. int32_t de = 0; // Odd x end pixel
  701. if ((sw > ds) && (_xe&1)) de = 1;
  702. uint32_t dm = 0; // Midsection pixel count
  703. if (sw > (ds+de)) dm = sw - ds - de;
  704. sw--;
  705. uint32_t yp = (_xs + ds + _iwidth * _ys)>>1;
  706. _tft->startWrite();
  707. while (sh--)
  708. {
  709. if (ds) _tft->drawPixel(tx, ty, readPixel(_xs, _ys) );
  710. if (dm) _tft->pushImage(tx + ds, ty, dm, 1, _img4 + yp, false, _colorMap );
  711. if (de) _tft->drawPixel(tx + sw, ty, readPixel(_xe, _ys) );
  712. _ys++;
  713. ty++;
  714. yp += (_iwidth>>1);
  715. }
  716. _tft->endWrite();
  717. }
  718. }
  719. else // 1bpp
  720. {
  721. // Check if a faster block copy to screen is possible
  722. if ( sx == 0 && sw == _dwidth)
  723. _tft->pushImage(tx, ty, sw, sh, _img8 + (_bitwidth>>3) * _ys, (bool)false );
  724. else // Render line by line
  725. {
  726. _tft->startWrite();
  727. while (sh--)
  728. {
  729. _tft->pushImage(tx, ty++, sw, 1, _img8 + (_bitwidth>>3) * _ys, (bool)false );
  730. }
  731. _tft->endWrite();
  732. }
  733. }
  734. return true;
  735. }
  736. /***************************************************************************************
  737. ** Function name: readPixelValue
  738. ** Description: Read the color map index of a pixel at defined coordinates
  739. ***************************************************************************************/
  740. uint16_t TFT_eSprite::readPixelValue(int32_t x, int32_t y)
  741. {
  742. if (_vpOoB || !_created) return 0xFF;
  743. x+= _xDatum;
  744. y+= _yDatum;
  745. // Range checking
  746. if ((x < _vpX) || (y < _vpY) ||(x >= _vpW) || (y >= _vpH)) return 0xFF;
  747. if (_bpp == 16)
  748. {
  749. // Return the pixel colour
  750. return readPixel(x - _xDatum, y - _yDatum);
  751. }
  752. if (_bpp == 8)
  753. {
  754. // Return the pixel byte value
  755. return _img8[x + y * _iwidth];
  756. }
  757. if (_bpp == 4)
  758. {
  759. if (x >= _dwidth) return 0xFF;
  760. if ((x & 0x01) == 0)
  761. return _img4[((x+y*_iwidth)>>1)] >> 4; // even index = bits 7 .. 4
  762. else
  763. return _img4[((x+y*_iwidth)>>1)] & 0x0F; // odd index = bits 3 .. 0.
  764. }
  765. if (_bpp == 1)
  766. {
  767. // Note: _dwidth and _dheight bounds not checked (rounded up -iwidth and _iheight used)
  768. if (rotation == 1)
  769. {
  770. uint16_t tx = x;
  771. x = _dheight - y - 1;
  772. y = tx;
  773. }
  774. else if (rotation == 2)
  775. {
  776. x = _dwidth - x - 1;
  777. y = _dheight - y - 1;
  778. }
  779. else if (rotation == 3)
  780. {
  781. uint16_t tx = x;
  782. x = y;
  783. y = _dwidth - tx - 1;
  784. }
  785. // Return 1 or 0
  786. return (_img8[(x + y * _bitwidth)>>3] >> (7-(x & 0x7))) & 0x01;
  787. }
  788. return 0;
  789. }
  790. /***************************************************************************************
  791. ** Function name: readPixel
  792. ** Description: Read 565 colour of a pixel at defined coordinates
  793. ***************************************************************************************/
  794. uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y)
  795. {
  796. if (_vpOoB || !_created) return 0xFFFF;
  797. x+= _xDatum;
  798. y+= _yDatum;
  799. // Range checking
  800. if ((x < _vpX) || (y < _vpY) ||(x >= _vpW) || (y >= _vpH)) return 0xFFFF;
  801. if (_bpp == 16)
  802. {
  803. uint16_t color = _img[x + y * _iwidth];
  804. return (color >> 8) | (color << 8);
  805. }
  806. if (_bpp == 8)
  807. {
  808. uint16_t color = _img8[x + y * _iwidth];
  809. if (color != 0)
  810. {
  811. uint8_t blue[] = {0, 11, 21, 31};
  812. color = (color & 0xE0)<<8 | (color & 0xC0)<<5
  813. | (color & 0x1C)<<6 | (color & 0x1C)<<3
  814. | blue[color & 0x03];
  815. }
  816. return color;
  817. }
  818. if (_bpp == 4)
  819. {
  820. if (x >= _dwidth) return 0xFFFF;
  821. uint16_t color;
  822. if ((x & 0x01) == 0)
  823. color = _colorMap[_img4[((x+y*_iwidth)>>1)] >> 4]; // even index = bits 7 .. 4
  824. else
  825. color = _colorMap[_img4[((x+y*_iwidth)>>1)] & 0x0F]; // odd index = bits 3 .. 0.
  826. return color;
  827. }
  828. // Note: Must be 1bpp
  829. // _dwidth and _dheight bounds not checked (rounded up -iwidth and _iheight used)
  830. if (rotation == 1)
  831. {
  832. uint16_t tx = x;
  833. x = _dheight - y - 1;
  834. y = tx;
  835. }
  836. else if (rotation == 2)
  837. {
  838. x = _dwidth - x - 1;
  839. y = _dheight - y - 1;
  840. }
  841. else if (rotation == 3)
  842. {
  843. uint16_t tx = x;
  844. x = y;
  845. y = _dwidth - tx - 1;
  846. }
  847. uint16_t color = (_img8[(x + y * _bitwidth)>>3] << (x & 0x7)) & 0x80;
  848. if (color) return _tft->bitmap_fg;
  849. else return _tft->bitmap_bg;
  850. }
  851. /***************************************************************************************
  852. ** Function name: pushImage
  853. ** Description: push image into a defined area of a sprite
  854. ***************************************************************************************/
  855. void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data, uint8_t sbpp)
  856. {
  857. if (data == nullptr || !_created) return;
  858. PI_CLIP;
  859. if (_bpp == 16) // Plot a 16 bpp image into a 16 bpp Sprite
  860. {
  861. // Pointer within original image
  862. uint8_t *ptro = (uint8_t *)data + ((dx + dy * w) << 1);
  863. // Pointer within sprite image
  864. uint8_t *ptrs = (uint8_t *)_img + ((x + y * _iwidth) << 1);
  865. if(_swapBytes)
  866. {
  867. while (dh--)
  868. {
  869. // Fast copy with a 1 byte shift
  870. memcpy(ptrs+1, ptro, (dw<<1) - 1);
  871. // Now correct just the even numbered bytes
  872. for (int32_t xp = 0; xp < (dw<<1); xp+=2)
  873. {
  874. ptrs[xp] = ptro[xp+1];;
  875. }
  876. ptro += w<<1;
  877. ptrs += _iwidth<<1;
  878. }
  879. }
  880. else
  881. {
  882. while (dh--)
  883. {
  884. memcpy(ptrs, ptro, dw<<1);
  885. ptro += w << 1;
  886. ptrs += _iwidth << 1;
  887. }
  888. }
  889. }
  890. else if (_bpp == 8 && sbpp == 8) // Plot a 8 bpp image into a 8 bpp Sprite
  891. {
  892. // Pointer within original image
  893. uint8_t *ptro = (uint8_t *)data + (dx + dy * w);
  894. // Pointer within sprite image
  895. uint8_t *ptrs = (uint8_t *)_img + (x + y * _iwidth);
  896. while (dh--)
  897. {
  898. memcpy(ptrs, ptro, dw);
  899. ptro += w;
  900. ptrs += _iwidth;
  901. }
  902. }
  903. else if (_bpp == 8) // Plot a 16 bpp image into a 8 bpp Sprite
  904. {
  905. uint16_t lastColor = 0;
  906. uint8_t color8 = 0;
  907. for (int32_t yp = dy; yp < dy + dh; yp++)
  908. {
  909. int32_t xyw = x + y * _iwidth;
  910. int32_t dxypw = dx + yp * w;
  911. for (int32_t xp = dx; xp < dx + dw; xp++)
  912. {
  913. uint16_t color = data[dxypw++];
  914. if (color != lastColor) {
  915. // When data source is a sprite, the bytes are already swapped
  916. if(!_swapBytes) color8 = (uint8_t)((color & 0xE0) | (color & 0x07)<<2 | (color & 0x1800)>>11);
  917. else color8 = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3);
  918. }
  919. lastColor = color;
  920. _img8[xyw++] = color8;
  921. }
  922. y++;
  923. }
  924. }
  925. else if (_bpp == 4)
  926. {
  927. // The image is assumed to be 4 bit, where each byte corresponds to two pixels.
  928. // much faster when aligned to a byte boundary, because the alternative is slower, requiring
  929. // tedious bit operations.
  930. int sWidth = (_iwidth >> 1);
  931. uint8_t *ptr = (uint8_t *)data;
  932. if ((x & 0x01) == 0 && (dx & 0x01) == 0 && (dw & 0x01) == 0)
  933. {
  934. x = (x >> 1) + y * sWidth;
  935. dw = (dw >> 1);
  936. dx = (dx >> 1) + dy * (w>>1);
  937. while (dh--)
  938. {
  939. memcpy(_img4 + x, ptr + dx, dw);
  940. dx += (w >> 1);
  941. x += sWidth;
  942. }
  943. }
  944. else // not optimized
  945. {
  946. for (int32_t yp = dy; yp < dy + dh; yp++)
  947. {
  948. int32_t ox = x;
  949. for (int32_t xp = dx; xp < dx + dw; xp++)
  950. {
  951. uint32_t color;
  952. if ((xp & 0x01) == 0)
  953. color = (ptr[((xp+yp*w)>>1)] & 0xF0) >> 4; // even index = bits 7 .. 4
  954. else
  955. color = ptr[((xp-1+yp*w)>>1)] & 0x0F; // odd index = bits 3 .. 0.
  956. drawPixel(ox, y, color);
  957. ox++;
  958. }
  959. y++;
  960. }
  961. }
  962. }
  963. else // 1bpp
  964. {
  965. // Plot a 1bpp image into a 1bpp Sprite
  966. uint32_t ww = (w+7)>>3; // Width of source image line in bytes
  967. uint8_t *ptr = (uint8_t *)data;
  968. for (int32_t yp = dy; yp < dy + dh; yp++)
  969. {
  970. uint32_t yw = yp * ww; // Byte starting the line containing source pixel
  971. int32_t ox = x;
  972. for (int32_t xp = dx; xp < dx + dw; xp++)
  973. {
  974. uint16_t readPixel = (ptr[(xp>>3) + yw] & (0x80 >> (xp & 0x7)) );
  975. drawPixel(ox++, y, readPixel);
  976. }
  977. y++;
  978. }
  979. }
  980. }
  981. /***************************************************************************************
  982. ** Function name: pushImage
  983. ** Description: push 565 colour FLASH (PROGMEM) image into a defined area
  984. ***************************************************************************************/
  985. void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data)
  986. {
  987. #ifdef ESP32
  988. pushImage(x, y, w, h, (uint16_t*) data);
  989. #else
  990. // Partitioned memory FLASH processor
  991. if (data == nullptr || !_created) return;
  992. PI_CLIP;
  993. if (_bpp == 16) // Plot a 16 bpp image into a 16 bpp Sprite
  994. {
  995. for (int32_t yp = dy; yp < dy + dh; yp++)
  996. {
  997. int32_t ox = x;
  998. for (int32_t xp = dx; xp < dx + dw; xp++)
  999. {
  1000. uint16_t color = pgm_read_word(data + xp + yp * w);
  1001. if(_swapBytes) color = color<<8 | color>>8;
  1002. _img[ox + y * _iwidth] = color;
  1003. ox++;
  1004. }
  1005. y++;
  1006. }
  1007. }
  1008. else if (_bpp == 8) // Plot a 16 bpp image into a 8 bpp Sprite
  1009. {
  1010. for (int32_t yp = dy; yp < dy + dh; yp++)
  1011. {
  1012. int32_t ox = x;
  1013. for (int32_t xp = dx; xp < dx + dw; xp++)
  1014. {
  1015. uint16_t color = pgm_read_word(data + xp + yp * w);
  1016. if(_swapBytes) color = color<<8 | color>>8;
  1017. _img8[ox + y * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3);
  1018. ox++;
  1019. }
  1020. y++;
  1021. }
  1022. }
  1023. else if (_bpp == 4)
  1024. {
  1025. #ifdef TFT_eSPI_DEBUG
  1026. Serial.println("TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data) not implemented");
  1027. #endif
  1028. return;
  1029. }
  1030. else // Plot a 1bpp image into a 1bpp Sprite
  1031. {
  1032. x-= _xDatum; // Remove offsets, drawPixel will add
  1033. y-= _yDatum;
  1034. uint16_t bsw = (w+7) >> 3; // Width in bytes of source image line
  1035. uint8_t *ptr = ((uint8_t*)data) + dy * bsw;
  1036. while (dh--) {
  1037. int32_t odx = dx;
  1038. int32_t ox = x;
  1039. while (odx < dx + dw) {
  1040. uint8_t pbyte = pgm_read_byte(ptr + (odx>>3));
  1041. uint8_t mask = 0x80 >> (odx & 7);
  1042. while (mask) {
  1043. uint8_t p = pbyte & mask;
  1044. mask = mask >> 1;
  1045. drawPixel(ox++, y, p);
  1046. odx++;
  1047. }
  1048. }
  1049. ptr += bsw;
  1050. y++;
  1051. }
  1052. }
  1053. #endif // if ESP32 check
  1054. }
  1055. /***************************************************************************************
  1056. ** Function name: setWindow
  1057. ** Description: Set the bounds of a window in the sprite
  1058. ***************************************************************************************/
  1059. // Intentionally not constrained to viewport area, does not manage 1bpp rotations
  1060. void TFT_eSprite::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1)
  1061. {
  1062. if (x0 > x1) swap_coord(x0, x1);
  1063. if (y0 > y1) swap_coord(y0, y1);
  1064. int32_t w = width();
  1065. int32_t h = height();
  1066. if ((x0 >= w) || (x1 < 0) || (y0 >= h) || (y1 < 0))
  1067. { // Point to that extra "off screen" pixel
  1068. _xs = 0;
  1069. _ys = _dheight;
  1070. _xe = 0;
  1071. _ye = _dheight;
  1072. }
  1073. else
  1074. {
  1075. if (x0 < 0) x0 = 0;
  1076. if (x1 >= w) x1 = w - 1;
  1077. if (y0 < 0) y0 = 0;
  1078. if (y1 >= h) y1 = h - 1;
  1079. _xs = x0;
  1080. _ys = y0;
  1081. _xe = x1;
  1082. _ye = y1;
  1083. }
  1084. _xptr = _xs;
  1085. _yptr = _ys;
  1086. }
  1087. /***************************************************************************************
  1088. ** Function name: pushColor
  1089. ** Description: Send a new pixel to the set window
  1090. ***************************************************************************************/
  1091. void TFT_eSprite::pushColor(uint32_t color)
  1092. {
  1093. if (!_created ) return;
  1094. // Write the colour to RAM in set window
  1095. if (_bpp == 16)
  1096. _img [_xptr + _yptr * _iwidth] = (uint16_t) (color >> 8) | (color << 8);
  1097. else if (_bpp == 8)
  1098. _img8[_xptr + _yptr * _iwidth] = (uint8_t )((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3);
  1099. else if (_bpp == 4)
  1100. {
  1101. uint8_t c = (uint8_t)color & 0x0F;
  1102. if ((_xptr & 0x01) == 0) {
  1103. _img4[(_xptr + _yptr * _iwidth)>>1] = (c << 4) | (_img4[(_xptr + _yptr * _iwidth)>>1] & 0x0F); // new color is in bits 7 .. 4
  1104. }
  1105. else {
  1106. _img4[(_xptr + _yptr * _iwidth)>>1] = (_img4[(_xptr + _yptr * _iwidth)>>1] & 0xF0) | c; // new color is the low bits
  1107. }
  1108. }
  1109. else drawPixel(_xptr, _yptr, color);
  1110. // Increment x
  1111. _xptr++;
  1112. // Wrap on x and y to start, increment y if needed
  1113. if (_xptr > _xe)
  1114. {
  1115. _xptr = _xs;
  1116. _yptr++;
  1117. if (_yptr > _ye) _yptr = _ys;
  1118. }
  1119. }
  1120. /***************************************************************************************
  1121. ** Function name: pushColor
  1122. ** Description: Send a "len" new pixels to the set window
  1123. ***************************************************************************************/
  1124. void TFT_eSprite::pushColor(uint32_t color, uint16_t len)
  1125. {
  1126. if (!_created ) return;
  1127. uint16_t pixelColor;
  1128. if (_bpp == 16)
  1129. pixelColor = (uint16_t) (color >> 8) | (color << 8);
  1130. else if (_bpp == 8)
  1131. pixelColor = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3;
  1132. else pixelColor = (uint16_t) color; // for 1bpp or 4bpp
  1133. while(len--) writeColor(pixelColor);
  1134. }
  1135. /***************************************************************************************
  1136. ** Function name: writeColor
  1137. ** Description: Write a pixel with pre-formatted colour to the set window
  1138. ***************************************************************************************/
  1139. void TFT_eSprite::writeColor(uint16_t color)
  1140. {
  1141. if (!_created ) return;
  1142. // Write 16 bit RGB 565 encoded colour to RAM
  1143. if (_bpp == 16) _img [_xptr + _yptr * _iwidth] = color;
  1144. // Write 8 bit RGB 332 encoded colour to RAM
  1145. else if (_bpp == 8) _img8[_xptr + _yptr * _iwidth] = (uint8_t) color;
  1146. else if (_bpp == 4)
  1147. {
  1148. uint8_t c = (uint8_t)color & 0x0F;
  1149. if ((_xptr & 0x01) == 0)
  1150. _img4[(_xptr + _yptr * _iwidth)>>1] = (c << 4) | (_img4[(_xptr + _yptr * _iwidth)>>1] & 0x0F); // new color is in bits 7 .. 4
  1151. else
  1152. _img4[(_xptr + _yptr * _iwidth)>>1] = (_img4[(_xptr + _yptr * _iwidth)>>1] & 0xF0) | c; // new color is the low bits (x is odd)
  1153. }
  1154. else drawPixel(_xptr, _yptr, color);
  1155. // Increment x
  1156. _xptr++;
  1157. // Wrap on x and y to start, increment y if needed
  1158. if (_xptr > _xe)
  1159. {
  1160. _xptr = _xs;
  1161. _yptr++;
  1162. if (_yptr > _ye) _yptr = _ys;
  1163. }
  1164. }
  1165. /***************************************************************************************
  1166. ** Function name: setScrollRect
  1167. ** Description: Set scroll area within the sprite and the gap fill colour
  1168. ***************************************************************************************/
  1169. // Intentionally not constrained to viewport area
  1170. void TFT_eSprite::setScrollRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t color)
  1171. {
  1172. if ((x >= _iwidth) || (y >= _iheight) || !_created ) return;
  1173. if (x < 0) { w += x; x = 0; }
  1174. if (y < 0) { h += y; y = 0; }
  1175. if ((x + w) > _iwidth ) w = _iwidth - x;
  1176. if ((y + h) > _iheight) h = _iheight - y;
  1177. if ( w < 1 || h < 1) return;
  1178. _sx = x;
  1179. _sy = y;
  1180. _sw = w;
  1181. _sh = h;
  1182. _scolor = color;
  1183. }
  1184. /***************************************************************************************
  1185. ** Function name: scroll
  1186. ** Description: Scroll dx,dy pixels, positive right,down, negative left,up
  1187. ***************************************************************************************/
  1188. void TFT_eSprite::scroll(int16_t dx, int16_t dy)
  1189. {
  1190. if (abs(dx) >= _sw || abs(dy) >= _sh)
  1191. {
  1192. fillRect (_sx, _sy, _sw, _sh, _scolor);
  1193. return;
  1194. }
  1195. // Fetch the scroll area width and height set by setScrollRect()
  1196. uint32_t w = _sw - abs(dx); // line width to copy
  1197. uint32_t h = _sh - abs(dy); // lines to copy
  1198. int32_t iw = _iwidth; // rounded up width of sprite
  1199. // Fetch the x,y origin set by setScrollRect()
  1200. uint32_t tx = _sx; // to x
  1201. uint32_t fx = _sx; // from x
  1202. uint32_t ty = _sy; // to y
  1203. uint32_t fy = _sy; // from y
  1204. // Adjust for x delta
  1205. if (dx <= 0) fx -= dx;
  1206. else tx += dx;
  1207. // Adjust for y delta
  1208. if (dy <= 0) fy -= dy;
  1209. else
  1210. { // Scrolling down so start copy from bottom
  1211. ty = ty + _sh - 1; // "To" pointer
  1212. iw = -iw; // Pointer moves backwards
  1213. fy = ty - dy; // "From" pointer
  1214. }
  1215. // Calculate "from y" and "to y" pointers in RAM
  1216. uint32_t fyp = fx + fy * _iwidth;
  1217. uint32_t typ = tx + ty * _iwidth;
  1218. // Now move the pixels in RAM
  1219. if (_bpp == 16)
  1220. {
  1221. while (h--)
  1222. { // move pixel lines (to, from, byte count)
  1223. memmove( _img + typ, _img + fyp, w<<1);
  1224. typ += iw;
  1225. fyp += iw;
  1226. }
  1227. }
  1228. else if (_bpp == 8)
  1229. {
  1230. while (h--)
  1231. { // move pixel lines (to, from, byte count)
  1232. memmove( _img8 + typ, _img8 + fyp, w);
  1233. typ += iw;
  1234. fyp += iw;
  1235. }
  1236. }
  1237. else if (_bpp == 4)
  1238. {
  1239. // could optimize for scrolling by even # pixels using memove (later)
  1240. if (dx > 0) { tx += w; fx += w; } // Start from right edge
  1241. while (h--)
  1242. { // move pixels one by one
  1243. for (uint16_t xp = 0; xp < w; xp++)
  1244. {
  1245. if (dx <= 0) drawPixel(tx + xp, ty, readPixelValue(fx + xp, fy));
  1246. if (dx > 0) drawPixel(tx - xp, ty, readPixelValue(fx - xp, fy));
  1247. }
  1248. if (dy <= 0) { ty++; fy++; }
  1249. else { ty--; fy--; }
  1250. }
  1251. }
  1252. else if (_bpp == 1 )
  1253. {
  1254. if (dx > 0) { tx += w; fx += w; } // Start from right edge
  1255. while (h--)
  1256. { // move pixels one by one
  1257. for (uint16_t xp = 0; xp < w; xp++)
  1258. {
  1259. if (dx <= 0) drawPixel(tx + xp, ty, readPixelValue(fx + xp, fy));
  1260. if (dx > 0) drawPixel(tx - xp, ty, readPixelValue(fx - xp, fy));
  1261. }
  1262. if (dy <= 0) { ty++; fy++; }
  1263. else { ty--; fy--; }
  1264. }
  1265. }
  1266. else return; // Not 1, 4, 8 or 16 bpp
  1267. // Fill the gap left by the scrolling
  1268. if (dx > 0) fillRect(_sx, _sy, dx, _sh, _scolor);
  1269. if (dx < 0) fillRect(_sx + _sw + dx, _sy, -dx, _sh, _scolor);
  1270. if (dy > 0) fillRect(_sx, _sy, _sw, dy, _scolor);
  1271. if (dy < 0) fillRect(_sx, _sy + _sh + dy, _sw, -dy, _scolor);
  1272. }
  1273. /***************************************************************************************
  1274. ** Function name: fillSprite
  1275. ** Description: Fill the whole sprite with defined colour
  1276. ***************************************************************************************/
  1277. void TFT_eSprite::fillSprite(uint32_t color)
  1278. {
  1279. if (!_created || _vpOoB) return;
  1280. // Use memset if possible as it is super fast
  1281. if(_xDatum == 0 && _yDatum == 0 && _xWidth == width())
  1282. {
  1283. if(_bpp == 16) {
  1284. if ( (uint8_t)color == (uint8_t)(color>>8) ) {
  1285. memset(_img, (uint8_t)color, _iwidth * _yHeight * 2);
  1286. }
  1287. else fillRect(_vpX, _vpY, _xWidth, _yHeight, color);
  1288. }
  1289. else if (_bpp == 8)
  1290. {
  1291. color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3;
  1292. memset(_img8, (uint8_t)color, _iwidth * _yHeight);
  1293. }
  1294. else if (_bpp == 4)
  1295. {
  1296. uint8_t c = ((color & 0x0F) | (((color & 0x0F) << 4) & 0xF0));
  1297. memset(_img4, c, (_iwidth * _yHeight) >> 1);
  1298. }
  1299. else if (_bpp == 1)
  1300. {
  1301. if(color) memset(_img8, 0xFF, (_bitwidth>>3) * _dheight + 1);
  1302. else memset(_img8, 0x00, (_bitwidth>>3) * _dheight + 1);
  1303. }
  1304. }
  1305. else fillRect(_vpX - _xDatum, _vpY - _yDatum, _xWidth, _yHeight, color);
  1306. }
  1307. /***************************************************************************************
  1308. ** Function name: width
  1309. ** Description: Return the width of sprite
  1310. ***************************************************************************************/
  1311. // Return the size of the display
  1312. int16_t TFT_eSprite::width(void)
  1313. {
  1314. if (!_created ) return 0;
  1315. if (_bpp > 1) {
  1316. if (_vpDatum) return _xWidth;
  1317. return _dwidth;
  1318. }
  1319. if (rotation & 1) {
  1320. if (_vpDatum) return _xWidth;
  1321. return _dheight;
  1322. }
  1323. if (_vpDatum) return _xWidth;
  1324. return _dwidth;
  1325. }
  1326. /***************************************************************************************
  1327. ** Function name: height
  1328. ** Description: Return the height of sprite
  1329. ***************************************************************************************/
  1330. int16_t TFT_eSprite::height(void)
  1331. {
  1332. if (!_created ) return 0;
  1333. if (_bpp > 1) {
  1334. if (_vpDatum) return _yHeight;
  1335. return _dheight;
  1336. }
  1337. if (rotation & 1) {
  1338. if (_vpDatum) return _yHeight;
  1339. return _dwidth;
  1340. }
  1341. if (_vpDatum) return _yHeight;
  1342. return _dheight;
  1343. }
  1344. /***************************************************************************************
  1345. ** Function name: setRotation
  1346. ** Description: Rotate coordinate frame for 1bpp sprite
  1347. ***************************************************************************************/
  1348. // Does nothing for 4, 8 and 16 bpp sprites.
  1349. void TFT_eSprite::setRotation(uint8_t r)
  1350. {
  1351. if (_bpp != 1) return;
  1352. rotation = r;
  1353. if (rotation&1) {
  1354. resetViewport();
  1355. }
  1356. else {
  1357. resetViewport();
  1358. }
  1359. }
  1360. /***************************************************************************************
  1361. ** Function name: getRotation
  1362. ** Description: Get rotation for 1bpp sprite
  1363. ***************************************************************************************/
  1364. uint8_t TFT_eSprite::getRotation(void)
  1365. {
  1366. return rotation;
  1367. }
  1368. /***************************************************************************************
  1369. ** Function name: drawPixel
  1370. ** Description: push a single pixel at an arbitrary position
  1371. ***************************************************************************************/
  1372. void TFT_eSprite::drawPixel(int32_t x, int32_t y, uint32_t color)
  1373. {
  1374. if (!_created || _vpOoB) return;
  1375. x+= _xDatum;
  1376. y+= _yDatum;
  1377. // Range checking
  1378. if ((x < _vpX) || (y < _vpY) ||(x >= _vpW) || (y >= _vpH)) return;
  1379. if (_bpp == 16)
  1380. {
  1381. color = (color >> 8) | (color << 8);
  1382. _img[x+y*_iwidth] = (uint16_t) color;
  1383. }
  1384. else if (_bpp == 8)
  1385. {
  1386. _img8[x+y*_iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3);
  1387. }
  1388. else if (_bpp == 4)
  1389. {
  1390. uint8_t c = color & 0x0F;
  1391. int index = (x+y*_iwidth)>>1;;
  1392. if ((x & 0x01) == 0) {
  1393. _img4[index] = (uint8_t)((c << 4) | (_img4[index] & 0x0F));
  1394. }
  1395. else {
  1396. _img4[index] = (uint8_t)(c | (_img4[index] & 0xF0));
  1397. }
  1398. }
  1399. else // 1 bpp
  1400. {
  1401. if (rotation == 1)
  1402. {
  1403. uint16_t tx = x;
  1404. x = _dwidth - y - 1;
  1405. y = tx;
  1406. }
  1407. else if (rotation == 2)
  1408. {
  1409. x = _dwidth - x - 1;
  1410. y = _dheight - y - 1;
  1411. }
  1412. else if (rotation == 3)
  1413. {
  1414. uint16_t tx = x;
  1415. x = y;
  1416. y = _dheight - tx - 1;
  1417. }
  1418. if (color) _img8[(x + y * _bitwidth)>>3] |= (0x80 >> (x & 0x7));
  1419. else _img8[(x + y * _bitwidth)>>3] &= ~(0x80 >> (x & 0x7));
  1420. }
  1421. }
  1422. /***************************************************************************************
  1423. ** Function name: drawLine
  1424. ** Description: draw a line between 2 arbitrary points
  1425. ***************************************************************************************/
  1426. void TFT_eSprite::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color)
  1427. {
  1428. if (!_created || _vpOoB) return;
  1429. //_xDatum and _yDatum Not added here, it is added by drawPixel & drawFastxLine
  1430. bool steep = abs(y1 - y0) > abs(x1 - x0);
  1431. if (steep) {
  1432. swap_coord(x0, y0);
  1433. swap_coord(x1, y1);
  1434. }
  1435. if (x0 > x1) {
  1436. swap_coord(x0, x1);
  1437. swap_coord(y0, y1);
  1438. }
  1439. int32_t dx = x1 - x0, dy = abs(y1 - y0);;
  1440. int32_t err = dx >> 1, ystep = -1, xs = x0, dlen = 0;
  1441. if (y0 < y1) ystep = 1;
  1442. // Split into steep and not steep for FastH/V separation
  1443. if (steep) {
  1444. for (; x0 <= x1; x0++) {
  1445. dlen++;
  1446. err -= dy;
  1447. if (err < 0) {
  1448. err += dx;
  1449. if (dlen == 1) drawPixel(y0, xs, color);
  1450. else drawFastVLine(y0, xs, dlen, color);
  1451. dlen = 0; y0 += ystep; xs = x0 + 1;
  1452. }
  1453. }
  1454. if (dlen) drawFastVLine(y0, xs, dlen, color);
  1455. }
  1456. else
  1457. {
  1458. for (; x0 <= x1; x0++) {
  1459. dlen++;
  1460. err -= dy;
  1461. if (err < 0) {
  1462. err += dx;
  1463. if (dlen == 1) drawPixel(xs, y0, color);
  1464. else drawFastHLine(xs, y0, dlen, color);
  1465. dlen = 0; y0 += ystep; xs = x0 + 1;
  1466. }
  1467. }
  1468. if (dlen) drawFastHLine(xs, y0, dlen, color);
  1469. }
  1470. }
  1471. /***************************************************************************************
  1472. ** Function name: drawFastVLine
  1473. ** Description: draw a vertical line
  1474. ***************************************************************************************/
  1475. void TFT_eSprite::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color)
  1476. {
  1477. if (!_created || _vpOoB) return;
  1478. x+= _xDatum;
  1479. y+= _yDatum;
  1480. // Clipping
  1481. if ((x < _vpX) || (x >= _vpW) || (y >= _vpH)) return;
  1482. if (y < _vpY) { h += y - _vpY; y = _vpY; }
  1483. if ((y + h) > _vpH) h = _vpH - y;
  1484. if (h < 1) return;
  1485. if (_bpp == 16)
  1486. {
  1487. color = (color >> 8) | (color << 8);
  1488. int32_t yp = x + _iwidth * y;
  1489. while (h--) {_img[yp] = (uint16_t) color; yp += _iwidth;}
  1490. }
  1491. else if (_bpp == 8)
  1492. {
  1493. color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3;
  1494. while (h--) _img8[x + _iwidth * y++] = (uint8_t) color;
  1495. }
  1496. else if (_bpp == 4)
  1497. {
  1498. if ((x & 0x01) == 0)
  1499. {
  1500. uint8_t c = (uint8_t) (color & 0xF) << 4;
  1501. while (h--) {
  1502. _img4[(x + _iwidth * y)>>1] = (uint8_t) (c | (_img4[(x + _iwidth * y)>>1] & 0x0F));
  1503. y++;
  1504. }
  1505. }
  1506. else {
  1507. uint8_t c = (uint8_t)color & 0xF;
  1508. while (h--) {
  1509. _img4[(x + _iwidth * y)>>1] = (uint8_t) (c | (_img4[(x + _iwidth * y)>>1] & 0xF0)); // x is odd; new color goes into the low bits.
  1510. y++;
  1511. }
  1512. }
  1513. }
  1514. else
  1515. {
  1516. x -= _xDatum; // Remove any offset as it will be added by drawPixel
  1517. y -= _yDatum;
  1518. while (h--)
  1519. {
  1520. drawPixel(x, y++, color);
  1521. }
  1522. }
  1523. }
  1524. /***************************************************************************************
  1525. ** Function name: drawFastHLine
  1526. ** Description: draw a horizontal line
  1527. ***************************************************************************************/
  1528. void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color)
  1529. {
  1530. if (!_created || _vpOoB) return;
  1531. x+= _xDatum;
  1532. y+= _yDatum;
  1533. // Clipping
  1534. if ((y < _vpY) || (x >= _vpW) || (y >= _vpH)) return;
  1535. if (x < _vpX) { w += x - _vpX; x = _vpX; }
  1536. if ((x + w) > _vpW) w = _vpW - x;
  1537. if (w < 1) return;
  1538. if (_bpp == 16)
  1539. {
  1540. color = (color >> 8) | (color << 8);
  1541. while (w--) _img[_iwidth * y + x++] = (uint16_t) color;
  1542. }
  1543. else if (_bpp == 8)
  1544. {
  1545. color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3;
  1546. memset(_img8+_iwidth * y + x, (uint8_t)color, w);
  1547. }
  1548. else if (_bpp == 4)
  1549. {
  1550. uint8_t c = (uint8_t)color & 0x0F;
  1551. uint8_t c2 = (c | ((c << 4) & 0xF0));
  1552. if ((x & 0x01) == 1)
  1553. {
  1554. drawPixel(x - _xDatum, y - _yDatum, color);
  1555. x++; w--;
  1556. if (w < 1)
  1557. return;
  1558. }
  1559. if (((w + x) & 0x01) == 1)
  1560. {
  1561. // handle the extra one at the other end
  1562. drawPixel(x - _xDatum + w - 1, y - _yDatum, color);
  1563. w--;
  1564. if (w < 1) return;
  1565. }
  1566. memset(_img4 + ((_iwidth * y + x) >> 1), c2, (w >> 1));
  1567. }
  1568. else {
  1569. x -= _xDatum; // Remove any offset as it will be added by drawPixel
  1570. y -= _yDatum;
  1571. while (w--)
  1572. {
  1573. drawPixel(x++, y, color);
  1574. }
  1575. }
  1576. }
  1577. /***************************************************************************************
  1578. ** Function name: fillRect
  1579. ** Description: draw a filled rectangle
  1580. ***************************************************************************************/
  1581. void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color)
  1582. {
  1583. if (!_created || _vpOoB) return;
  1584. x+= _xDatum;
  1585. y+= _yDatum;
  1586. // Clipping
  1587. if ((x >= _vpW) || (y >= _vpH)) return;
  1588. if (x < _vpX) { w += x - _vpX; x = _vpX; }
  1589. if (y < _vpY) { h += y - _vpY; y = _vpY; }
  1590. if ((x + w) > _vpW) w = _vpW - x;
  1591. if ((y + h) > _vpH) h = _vpH - y;
  1592. if ((w < 1) || (h < 1)) return;
  1593. int32_t yp = _iwidth * y + x;
  1594. if (_bpp == 16)
  1595. {
  1596. color = (color >> 8) | (color << 8);
  1597. uint32_t iw = w;
  1598. int32_t ys = yp;
  1599. if(h--) {while (iw--) _img[yp++] = (uint16_t) color;}
  1600. yp = ys;
  1601. while (h--)
  1602. {
  1603. yp += _iwidth;
  1604. memcpy( _img+yp, _img+ys, w<<1);
  1605. }
  1606. }
  1607. else if (_bpp == 8)
  1608. {
  1609. color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3;
  1610. while (h--)
  1611. {
  1612. memset(_img8 + yp, (uint8_t)color, w);
  1613. yp += _iwidth;
  1614. }
  1615. }
  1616. else if (_bpp == 4)
  1617. {
  1618. uint8_t c1 = (uint8_t)color & 0x0F;
  1619. uint8_t c2 = c1 | ((c1 << 4) & 0xF0);
  1620. if ((x & 0x01) == 0 && (w & 0x01) == 0)
  1621. {
  1622. yp = (yp >> 1);
  1623. while (h--)
  1624. {
  1625. memset(_img4 + yp, c2, (w>>1));
  1626. yp += (_iwidth >> 1);
  1627. }
  1628. }
  1629. else if ((x & 0x01) == 0)
  1630. {
  1631. // same as above but you have a hangover on the right.
  1632. yp = (yp >> 1);
  1633. while (h--)
  1634. {
  1635. if (w > 1)
  1636. memset(_img4 + yp, c2, (w-1)>>1);
  1637. // handle the rightmost pixel by calling drawPixel
  1638. drawPixel(x+w-1-_xDatum, y+h-_yDatum, c1);
  1639. yp += (_iwidth >> 1);
  1640. }
  1641. }
  1642. else if ((w & 0x01) == 1)
  1643. {
  1644. yp = (yp + 1) >> 1;
  1645. while (h--) {
  1646. drawPixel(x-_xDatum, y+h-_yDatum, color & 0x0F);
  1647. if (w > 1)
  1648. memset(_img4 + yp, c2, (w-1)>>1);
  1649. // same as above but you have a hangover on the left instead
  1650. yp += (_iwidth >> 1);
  1651. }
  1652. }
  1653. else
  1654. {
  1655. yp = (yp + 1) >> 1;
  1656. while (h--) {
  1657. drawPixel(x-_xDatum, y+h-_yDatum, color & 0x0F);
  1658. if (w > 1) drawPixel(x+w-1-_xDatum, y+h-_yDatum, color & 0x0F);
  1659. if (w > 2)
  1660. memset(_img4 + yp, c2, (w-2)>>1);
  1661. // maximal hacking, single pixels on left and right.
  1662. yp += (_iwidth >> 1);
  1663. }
  1664. }
  1665. }
  1666. else
  1667. {
  1668. x -= _xDatum;
  1669. y -= _yDatum;
  1670. while (h--)
  1671. {
  1672. int32_t ww = w;
  1673. int32_t xx = x;
  1674. while (ww--) drawPixel(xx++, y, color);
  1675. y++;
  1676. }
  1677. }
  1678. }
  1679. /***************************************************************************************
  1680. ** Function name: drawChar
  1681. ** Description: draw a single character in the Adafruit GLCD or freefont
  1682. ***************************************************************************************/
  1683. void TFT_eSprite::drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32_t bg, uint8_t size)
  1684. {
  1685. if ( _vpOoB || !_created ) return;
  1686. if ((x >= _vpW - _xDatum) || // Clip right
  1687. (y >= _vpH - _yDatum) || // Clip bottom
  1688. ((x + 6 * size - 1) < (_vpX - _xDatum)) || // Clip left
  1689. ((y + 8 * size - 1) < (_vpY - _yDatum))) // Clip top
  1690. return;
  1691. if (c < 32) return;
  1692. #ifdef LOAD_GLCD
  1693. //>>>>>>>>>>>>>>>>>>
  1694. #ifdef LOAD_GFXFF
  1695. if(!gfxFont) { // 'Classic' built-in font
  1696. #endif
  1697. //>>>>>>>>>>>>>>>>>>
  1698. bool fillbg = (bg != color);
  1699. if ((size==1) && fillbg)
  1700. {
  1701. uint8_t column[6];
  1702. uint8_t mask = 0x1;
  1703. for (int8_t i = 0; i < 5; i++ ) column[i] = pgm_read_byte(font + (c * 5) + i);
  1704. column[5] = 0;
  1705. int8_t j, k;
  1706. for (j = 0; j < 8; j++) {
  1707. for (k = 0; k < 5; k++ ) {
  1708. if (column[k] & mask) {
  1709. drawPixel(x + k, y + j, color);
  1710. }
  1711. else {
  1712. drawPixel(x + k, y + j, bg);
  1713. }
  1714. }
  1715. mask <<= 1;
  1716. drawPixel(x + k, y + j, bg);
  1717. }
  1718. }
  1719. else
  1720. {
  1721. for (int8_t i = 0; i < 6; i++ ) {
  1722. uint8_t line;
  1723. if (i == 5)
  1724. line = 0x0;
  1725. else
  1726. line = pgm_read_byte(font + (c * 5) + i);
  1727. if (size == 1) // default size
  1728. {
  1729. for (int8_t j = 0; j < 8; j++) {
  1730. if (line & 0x1) drawPixel(x + i, y + j, color);
  1731. line >>= 1;
  1732. }
  1733. }
  1734. else { // big size
  1735. for (int8_t j = 0; j < 8; j++) {
  1736. if (line & 0x1) fillRect(x + (i * size), y + (j * size), size, size, color);
  1737. else if (fillbg) fillRect(x + i * size, y + j * size, size, size, bg);
  1738. line >>= 1;
  1739. }
  1740. }
  1741. }
  1742. }
  1743. //>>>>>>>>>>>>>>>>>>>>>>>>>>>
  1744. #ifdef LOAD_GFXFF
  1745. } else { // Custom font
  1746. #endif
  1747. //>>>>>>>>>>>>>>>>>>>>>>>>>>>
  1748. #endif // LOAD_GLCD
  1749. #ifdef LOAD_GFXFF
  1750. // Filter out bad characters not present in font
  1751. if ((c >= pgm_read_word(&gfxFont->first)) && (c <= pgm_read_word(&gfxFont->last )))
  1752. {
  1753. //>>>>>>>>>>>>>>>>>>>>>>>>>>>
  1754. c -= pgm_read_word(&gfxFont->first);
  1755. GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]);
  1756. uint8_t *bitmap = (uint8_t *)pgm_read_dword(&gfxFont->bitmap);
  1757. uint32_t bo = pgm_read_word(&glyph->bitmapOffset);
  1758. uint8_t w = pgm_read_byte(&glyph->width),
  1759. h = pgm_read_byte(&glyph->height);
  1760. //xa = pgm_read_byte(&glyph->xAdvance);
  1761. int8_t xo = pgm_read_byte(&glyph->xOffset),
  1762. yo = pgm_read_byte(&glyph->yOffset);
  1763. uint8_t xx, yy, bits=0, bit=0;
  1764. int16_t xo16 = 0, yo16 = 0;
  1765. if(size > 1) {
  1766. xo16 = xo;
  1767. yo16 = yo;
  1768. }
  1769. uint16_t hpc = 0; // Horizontal foreground pixel count
  1770. for(yy=0; yy<h; yy++) {
  1771. for(xx=0; xx<w; xx++) {
  1772. if(bit == 0) {
  1773. bits = pgm_read_byte(&bitmap[bo++]);
  1774. bit = 0x80;
  1775. }
  1776. if(bits & bit) hpc++;
  1777. else {
  1778. if (hpc) {
  1779. if(size == 1) drawFastHLine(x+xo+xx-hpc, y+yo+yy, hpc, color);
  1780. else fillRect(x+(xo16+xx-hpc)*size, y+(yo16+yy)*size, size*hpc, size, color);
  1781. hpc=0;
  1782. }
  1783. }
  1784. bit >>= 1;
  1785. }
  1786. // Draw pixels for this line as we are about to increment yy
  1787. if (hpc) {
  1788. if(size == 1) drawFastHLine(x+xo+xx-hpc, y+yo+yy, hpc, color);
  1789. else fillRect(x+(xo16+xx-hpc)*size, y+(yo16+yy)*size, size*hpc, size, color);
  1790. hpc=0;
  1791. }
  1792. }
  1793. }
  1794. #endif
  1795. #ifdef LOAD_GLCD
  1796. #ifdef LOAD_GFXFF
  1797. } // End classic vs custom font
  1798. #endif
  1799. #endif
  1800. }
  1801. /***************************************************************************************
  1802. ** Function name: drawChar
  1803. ** Description: draw a unicode glyph onto the screen
  1804. ***************************************************************************************/
  1805. // TODO: Rationalise with TFT_eSPI
  1806. // Any UTF-8 decoding must be done before calling drawChar()
  1807. int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y)
  1808. {
  1809. return drawChar(uniCode, x, y, textfont);
  1810. }
  1811. // Any UTF-8 decoding must be done before calling drawChar()
  1812. int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font)
  1813. {
  1814. if (_vpOoB || !uniCode) return 0;
  1815. if (font==1) {
  1816. #ifdef LOAD_GLCD
  1817. #ifndef LOAD_GFXFF
  1818. drawChar(x, y, uniCode, textcolor, textbgcolor, textsize);
  1819. return 6 * textsize;
  1820. #endif
  1821. #else
  1822. #ifndef LOAD_GFXFF
  1823. return 0;
  1824. #endif
  1825. #endif
  1826. #ifdef LOAD_GFXFF
  1827. drawChar(x, y, uniCode, textcolor, textbgcolor, textsize);
  1828. if(!gfxFont) { // 'Classic' built-in font
  1829. #ifdef LOAD_GLCD
  1830. return 6 * textsize;
  1831. #else
  1832. return 0;
  1833. #endif
  1834. }
  1835. else {
  1836. if((uniCode >= pgm_read_word(&gfxFont->first)) && (uniCode <= pgm_read_word(&gfxFont->last) )) {
  1837. uint16_t c2 = uniCode - pgm_read_word(&gfxFont->first);
  1838. GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]);
  1839. return pgm_read_byte(&glyph->xAdvance) * textsize;
  1840. }
  1841. else {
  1842. return 0;
  1843. }
  1844. }
  1845. #endif
  1846. }
  1847. if ((font>1) && (font<9) && ((uniCode < 32) || (uniCode > 127))) return 0;
  1848. int32_t width = 0;
  1849. int32_t height = 0;
  1850. uint32_t flash_address = 0;
  1851. uniCode -= 32;
  1852. #ifdef LOAD_FONT2
  1853. if (font == 2) {
  1854. flash_address = pgm_read_dword(&chrtbl_f16[uniCode]);
  1855. width = pgm_read_byte(widtbl_f16 + uniCode);
  1856. height = chr_hgt_f16;
  1857. }
  1858. #ifdef LOAD_RLE
  1859. else
  1860. #endif
  1861. #endif
  1862. #ifdef LOAD_RLE
  1863. {
  1864. if ((font>2) && (font<9)) {
  1865. flash_address = pgm_read_dword( (const void*)(pgm_read_dword( &(fontdata[font].chartbl ) ) + uniCode*sizeof(void *)) );
  1866. width = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[font].widthtbl ) ) + uniCode );
  1867. height= pgm_read_byte( &fontdata[font].height );
  1868. }
  1869. }
  1870. #endif
  1871. int32_t xd = x + _xDatum;
  1872. int32_t yd = y + _yDatum;
  1873. if ((xd + width * textsize < _vpX || xd >= _vpW) && (yd + height * textsize < _vpY || yd >= _vpH)) return width * textsize ;
  1874. int32_t w = width;
  1875. int32_t pX = 0;
  1876. int32_t pY = y;
  1877. uint8_t line = 0;
  1878. bool clip = xd < _vpX || xd + width * textsize >= _vpW || yd < _vpY || yd + height * textsize >= _vpH;
  1879. #ifdef LOAD_FONT2 // chop out code if we do not need it
  1880. if (font == 2) {
  1881. w = w + 6; // Should be + 7 but we need to compensate for width increment
  1882. w = w / 8;
  1883. for (int32_t i = 0; i < height; i++)
  1884. {
  1885. if (textcolor != textbgcolor) fillRect(x, pY, width * textsize, textsize, textbgcolor);
  1886. for (int32_t k = 0; k < w; k++)
  1887. {
  1888. line = pgm_read_byte((uint8_t *)flash_address + w * i + k);
  1889. if (line) {
  1890. if (textsize == 1) {
  1891. pX = x + k * 8;
  1892. if (line & 0x80) drawPixel(pX, pY, textcolor);
  1893. if (line & 0x40) drawPixel(pX + 1, pY, textcolor);
  1894. if (line & 0x20) drawPixel(pX + 2, pY, textcolor);
  1895. if (line & 0x10) drawPixel(pX + 3, pY, textcolor);
  1896. if (line & 0x08) drawPixel(pX + 4, pY, textcolor);
  1897. if (line & 0x04) drawPixel(pX + 5, pY, textcolor);
  1898. if (line & 0x02) drawPixel(pX + 6, pY, textcolor);
  1899. if (line & 0x01) drawPixel(pX + 7, pY, textcolor);
  1900. }
  1901. else {
  1902. pX = x + k * 8 * textsize;
  1903. if (line & 0x80) fillRect(pX, pY, textsize, textsize, textcolor);
  1904. if (line & 0x40) fillRect(pX + textsize, pY, textsize, textsize, textcolor);
  1905. if (line & 0x20) fillRect(pX + 2 * textsize, pY, textsize, textsize, textcolor);
  1906. if (line & 0x10) fillRect(pX + 3 * textsize, pY, textsize, textsize, textcolor);
  1907. if (line & 0x08) fillRect(pX + 4 * textsize, pY, textsize, textsize, textcolor);
  1908. if (line & 0x04) fillRect(pX + 5 * textsize, pY, textsize, textsize, textcolor);
  1909. if (line & 0x02) fillRect(pX + 6 * textsize, pY, textsize, textsize, textcolor);
  1910. if (line & 0x01) fillRect(pX + 7 * textsize, pY, textsize, textsize, textcolor);
  1911. }
  1912. }
  1913. }
  1914. pY += textsize;
  1915. }
  1916. }
  1917. #ifdef LOAD_RLE
  1918. else
  1919. #endif
  1920. #endif //FONT2
  1921. #ifdef LOAD_RLE //674 bytes of code
  1922. // Font is not 2 and hence is RLE encoded
  1923. {
  1924. w *= height; // Now w is total number of pixels in the character
  1925. int16_t color = textcolor;
  1926. if (_bpp == 16) color = (textcolor >> 8) | (textcolor << 8);
  1927. else if (_bpp == 8) color = ((textcolor & 0xE000)>>8 | (textcolor & 0x0700)>>6 | (textcolor & 0x0018)>>3);
  1928. int16_t bgcolor = textbgcolor;
  1929. if (_bpp == 16) bgcolor = (textbgcolor >> 8) | (textbgcolor << 8);
  1930. else if (_bpp == 8) bgcolor = ((textbgcolor & 0xE000)>>8 | (textbgcolor & 0x0700)>>6 | (textbgcolor & 0x0018)>>3);
  1931. if (textcolor == textbgcolor && !clip && _bpp != 1) {
  1932. int32_t px = 0, py = pY; // To hold character block start and end column and row values
  1933. int32_t pc = 0; // Pixel count
  1934. uint8_t np = textsize * textsize; // Number of pixels in a drawn pixel
  1935. uint8_t tnp = 0; // Temporary copy of np for while loop
  1936. uint8_t ts = textsize - 1; // Temporary copy of textsize
  1937. // 16 bit pixel count so maximum font size is equivalent to 180x180 pixels in area
  1938. // w is total number of pixels to plot to fill character block
  1939. while (pc < w) {
  1940. line = pgm_read_byte((uint8_t *)flash_address);
  1941. flash_address++;
  1942. if (line & 0x80) {
  1943. line &= 0x7F;
  1944. line++;
  1945. if (ts) {
  1946. px = xd + textsize * (pc % width); // Keep these px and py calculations outside the loop as they are slow
  1947. py = yd + textsize * (pc / width);
  1948. }
  1949. else {
  1950. px = xd + pc % width; // Keep these px and py calculations outside the loop as they are slow
  1951. py = yd + pc / width;
  1952. }
  1953. while (line--) { // In this case the while(line--) is faster
  1954. pc++; // This is faster than putting pc+=line before while()?
  1955. setWindow(px, py, px + ts, py + ts);
  1956. if (ts) {
  1957. tnp = np;
  1958. while (tnp--) writeColor(color);
  1959. }
  1960. else writeColor(color);
  1961. px += textsize;
  1962. if (px >= (xd + width * textsize)) {
  1963. px = xd;
  1964. py += textsize;
  1965. }
  1966. }
  1967. }
  1968. else {
  1969. line++;
  1970. pc += line;
  1971. }
  1972. }
  1973. }
  1974. else {
  1975. // Text colour != background and textsize = 1 and character is within viewport area
  1976. // so use faster drawing of characters and background using block write
  1977. if (textcolor != textbgcolor && textsize == 1 && !clip && _bpp != 1)
  1978. {
  1979. setWindow(xd, yd, xd + width - 1, yd + height - 1);
  1980. // Maximum font size is equivalent to 180x180 pixels in area
  1981. while (w > 0) {
  1982. line = pgm_read_byte((uint8_t *)flash_address++); // 8 bytes smaller when incrementing here
  1983. if (line & 0x80) {
  1984. line &= 0x7F;
  1985. line++; w -= line;
  1986. while (line--) writeColor(color);
  1987. }
  1988. else {
  1989. line++; w -= line;
  1990. while (line--) writeColor(bgcolor);
  1991. }
  1992. }
  1993. }
  1994. else
  1995. {
  1996. int32_t px = 0, py = 0; // To hold character pixel coords
  1997. int32_t tx = 0, ty = 0; // To hold character TFT pixel coords
  1998. int32_t pc = 0; // Pixel count
  1999. int32_t pl = 0; // Pixel line length
  2000. uint16_t pcol = 0; // Pixel color
  2001. bool pf = true; // Flag for plotting
  2002. while (pc < w) {
  2003. line = pgm_read_byte((uint8_t *)flash_address);
  2004. flash_address++;
  2005. if (line & 0x80) { pcol = textcolor; line &= 0x7F; pf = true;}
  2006. else { pcol = textbgcolor; if (textcolor == textbgcolor) pf = false;}
  2007. line++;
  2008. px = pc % width;
  2009. tx = x + textsize * px;
  2010. py = pc / width;
  2011. ty = y + textsize * py;
  2012. pl = 0;
  2013. pc += line;
  2014. while (line--) {
  2015. pl++;
  2016. if ((px+pl) >= width) {
  2017. if (pf) fillRect(tx, ty, pl * textsize, textsize, pcol);
  2018. pl = 0;
  2019. px = 0;
  2020. tx = x;
  2021. py ++;
  2022. ty += textsize;
  2023. }
  2024. }
  2025. if (pl && pf) fillRect(tx, ty, pl * textsize, textsize, pcol);
  2026. }
  2027. }
  2028. }
  2029. }
  2030. // End of RLE font rendering
  2031. #endif
  2032. return width * textsize; // x +
  2033. }
  2034. #ifdef SMOOTH_FONT
  2035. /***************************************************************************************
  2036. ** Function name: drawGlyph
  2037. ** Description: Write a character to the sprite cursor position
  2038. ***************************************************************************************/
  2039. //
  2040. void TFT_eSprite::drawGlyph(uint16_t code)
  2041. {
  2042. uint16_t fg = textcolor;
  2043. uint16_t bg = textbgcolor;
  2044. if (code < 0x21)
  2045. {
  2046. if (code == 0x20) {
  2047. cursor_x += gFont.spaceWidth;
  2048. return;
  2049. }
  2050. if (code == '\n') {
  2051. cursor_x = 0;
  2052. cursor_y += gFont.yAdvance;
  2053. if (textwrapY && (cursor_y >= height())) cursor_y = 0;
  2054. return;
  2055. }
  2056. }
  2057. uint16_t gNum = 0;
  2058. bool found = getUnicodeIndex(code, &gNum);
  2059. if (found)
  2060. {
  2061. bool newSprite = !_created;
  2062. if (newSprite)
  2063. {
  2064. createSprite(gWidth[gNum], gFont.yAdvance);
  2065. if(fg != bg) fillSprite(bg);
  2066. cursor_x = -gdX[gNum];
  2067. cursor_y = 0;
  2068. }
  2069. else
  2070. {
  2071. if( textwrapX && ((cursor_x + gWidth[gNum] + gdX[gNum]) > width())) {
  2072. cursor_y += gFont.yAdvance;
  2073. cursor_x = 0;
  2074. }
  2075. if( textwrapY && ((cursor_y + gFont.yAdvance) > height())) cursor_y = 0;
  2076. if ( cursor_x == 0) cursor_x -= gdX[gNum];
  2077. }
  2078. uint8_t* pbuffer = nullptr;
  2079. const uint8_t* gPtr = (const uint8_t*) gFont.gArray;
  2080. #ifdef FONT_FS_AVAILABLE
  2081. if (fs_font) {
  2082. fontFile.seek(gBitmap[gNum], fs::SeekSet); // This is slow for a significant position shift!
  2083. pbuffer = (uint8_t*)malloc(gWidth[gNum]);
  2084. }
  2085. #endif
  2086. int16_t xs = 0;
  2087. uint16_t dl = 0;
  2088. uint8_t pixel = 0;
  2089. int32_t cgy = cursor_y + gFont.maxAscent - gdY[gNum];
  2090. int32_t cgx = cursor_x + gdX[gNum];
  2091. for (int32_t y = 0; y < gHeight[gNum]; y++)
  2092. {
  2093. #ifdef FONT_FS_AVAILABLE
  2094. if (fs_font) {
  2095. fontFile.read(pbuffer, gWidth[gNum]);
  2096. }
  2097. #endif
  2098. for (int32_t x = 0; x < gWidth[gNum]; x++)
  2099. {
  2100. #ifdef FONT_FS_AVAILABLE
  2101. if (fs_font) {
  2102. pixel = pbuffer[x];
  2103. }
  2104. else
  2105. #endif
  2106. pixel = pgm_read_byte(gPtr + gBitmap[gNum] + x + gWidth[gNum] * y);
  2107. if (pixel)
  2108. {
  2109. if (pixel != 0xFF)
  2110. {
  2111. if (dl) { drawFastHLine( xs, y + cgy, dl, fg); dl = 0; }
  2112. if (_bpp != 1) {
  2113. if (fg == bg) drawPixel(x + cgx, y + cgy, alphaBlend(pixel, fg, readPixel(x + cgx, y + cgy)));
  2114. else drawPixel(x + cgx, y + cgy, alphaBlend(pixel, fg, bg));
  2115. }
  2116. else if (pixel>127) drawPixel(x + cgx, y + cgy, fg);
  2117. }
  2118. else
  2119. {
  2120. if (dl==0) xs = x + cgx;
  2121. dl++;
  2122. }
  2123. }
  2124. else
  2125. {
  2126. if (dl) { drawFastHLine( xs, y + cgy, dl, fg); dl = 0; }
  2127. }
  2128. }
  2129. if (dl) { drawFastHLine( xs, y + cgy, dl, fg); dl = 0; }
  2130. }
  2131. if (pbuffer) free(pbuffer);
  2132. if (newSprite)
  2133. {
  2134. pushSprite(cgx, cursor_y);
  2135. deleteSprite();
  2136. }
  2137. cursor_x += gxAdvance[gNum];
  2138. }
  2139. else
  2140. {
  2141. // Not a Unicode in font so draw a rectangle and move on cursor
  2142. drawRect(cursor_x, cursor_y + gFont.maxAscent - gFont.ascent, gFont.spaceWidth, gFont.ascent, fg);
  2143. cursor_x += gFont.spaceWidth + 1;
  2144. }
  2145. }
  2146. /***************************************************************************************
  2147. ** Function name: printToSprite
  2148. ** Description: Write a string to the sprite cursor position
  2149. ***************************************************************************************/
  2150. void TFT_eSprite::printToSprite(String string)
  2151. {
  2152. if(!fontLoaded) return;
  2153. printToSprite((char*)string.c_str(), string.length());
  2154. }
  2155. /***************************************************************************************
  2156. ** Function name: printToSprite
  2157. ** Description: Write a string to the sprite cursor position
  2158. ***************************************************************************************/
  2159. void TFT_eSprite::printToSprite(char *cbuffer, uint16_t len) //String string)
  2160. {
  2161. if(!fontLoaded) return;
  2162. uint16_t n = 0;
  2163. bool newSprite = !_created;
  2164. if (newSprite)
  2165. {
  2166. int16_t sWidth = 1;
  2167. uint16_t index = 0;
  2168. while (n < len)
  2169. {
  2170. uint16_t unicode = decodeUTF8((uint8_t*)cbuffer, &n, len - n);
  2171. if (getUnicodeIndex(unicode, &index))
  2172. {
  2173. if (n == 0) sWidth -= gdX[index];
  2174. if (n == len-1) sWidth += ( gWidth[index] + gdX[index]);
  2175. else sWidth += gxAdvance[index];
  2176. }
  2177. else sWidth += gFont.spaceWidth + 1;
  2178. }
  2179. createSprite(sWidth, gFont.yAdvance);
  2180. if (textcolor != textbgcolor) fillSprite(textbgcolor);
  2181. }
  2182. n = 0;
  2183. while (n < len)
  2184. {
  2185. uint16_t unicode = decodeUTF8((uint8_t*)cbuffer, &n, len - n);
  2186. //Serial.print("Decoded Unicode = 0x");Serial.println(unicode,HEX);
  2187. //Serial.print("n = ");Serial.println(n);
  2188. drawGlyph(unicode);
  2189. }
  2190. if (newSprite)
  2191. { // The sprite had to be created so place at TFT cursor
  2192. pushSprite(_tft->cursor_x, _tft->cursor_y);
  2193. deleteSprite();
  2194. }
  2195. }
  2196. /***************************************************************************************
  2197. ** Function name: printToSprite
  2198. ** Description: Print character in a Sprite, create sprite if needed
  2199. ***************************************************************************************/
  2200. int16_t TFT_eSprite::printToSprite(int16_t x, int16_t y, uint16_t index)
  2201. {
  2202. bool newSprite = !_created;
  2203. int16_t sWidth = gWidth[index];
  2204. if (newSprite)
  2205. {
  2206. createSprite(sWidth, gFont.yAdvance);
  2207. if (textcolor != textbgcolor) fillSprite(textbgcolor);
  2208. drawGlyph(gUnicode[index]);
  2209. pushSprite(x + gdX[index], y, textbgcolor);
  2210. deleteSprite();
  2211. }
  2212. else drawGlyph(gUnicode[index]);
  2213. return gxAdvance[index];
  2214. }
  2215. #endif