Repo for ESP32 Weather Station Development
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2951 lines
84KB

  1. /* TFT module
  2. *
  3. * Author: LoBo (loboris@gmail.com, loboris.github)
  4. *
  5. * Module supporting SPI TFT displays based on ILI9341 & ILI9488 controllers
  6. */
  7. #include <stdio.h>
  8. #include <errno.h>
  9. #include <sys/stat.h>
  10. #include <string.h>
  11. #include "freertos/FreeRTOS.h"
  12. #include "tft.h"
  13. #include <math.h>
  14. #include "esp32/rom/tjpgd.h"
  15. #define DEG_TO_RAD 0.01745329252
  16. #define deg_to_rad 0.01745329252 + 3.14159265359
  17. #define swap(a, b) { int16_t t = a; a = b; b = t; }
  18. #if !defined(max)
  19. #define max(A,B) ( (A) > (B) ? (A):(B))
  20. #endif
  21. #if !defined(min)
  22. #define min(A,B) ( (A) < (B) ? (A):(B))
  23. #endif
  24. // Embedded fonts
  25. extern uint8_t tft_SmallFont[];
  26. extern uint8_t tft_DefaultFont[];
  27. extern uint8_t tft_Dejavu18[];
  28. extern uint8_t tft_Dejavu24[];
  29. extern uint8_t tft_Ubuntu16[];
  30. extern uint8_t tft_Comic24[];
  31. extern uint8_t tft_minya24[];
  32. extern uint8_t tft_tooney32[];
  33. extern uint8_t tft_def_small[];
  34. // ==== Color definitions constants ==============
  35. const color_t TFT_BLACK = { 0, 0, 0 };
  36. const color_t TFT_NAVY = { 0, 0, 128 };
  37. const color_t TFT_DARKGREEN = { 0, 128, 0 };
  38. const color_t TFT_DARKCYAN = { 0, 128, 128 };
  39. const color_t TFT_MAROON = { 128, 0, 0 };
  40. const color_t TFT_PURPLE = { 128, 0, 128 };
  41. const color_t TFT_OLIVE = { 128, 128, 0 };
  42. const color_t TFT_LIGHTGREY = { 192, 192, 192 };
  43. const color_t TFT_DARKGREY = { 128, 128, 128 };
  44. const color_t TFT_BLUE = { 0, 0, 255 };
  45. const color_t TFT_GREEN = { 0, 255, 0 };
  46. const color_t TFT_CYAN = { 0, 255, 255 };
  47. const color_t TFT_RED = { 252, 0, 0 };
  48. const color_t TFT_MAGENTA = { 252, 0, 255 };
  49. const color_t TFT_YELLOW = { 252, 252, 0 };
  50. const color_t TFT_WHITE = { 252, 252, 252 };
  51. const color_t TFT_ORANGE = { 252, 164, 0 };
  52. const color_t TFT_GREENYELLOW = { 172, 252, 44 };
  53. const color_t TFT_PINK = { 252, 192, 202 };
  54. // ===============================================
  55. // ==============================================================
  56. // ==== Set default values of global variables ==================
  57. uint8_t tft_orientation = LANDSCAPE;// screen tft_orientation
  58. uint16_t tft_font_rotate = 0; // font rotation
  59. uint8_t tft_font_transparent = 0;
  60. uint8_t tft_font_forceFixed = 0;
  61. uint8_t tft_text_wrap = 0; // character wrapping to new line
  62. color_t tft_fg = { 0, 255, 0};
  63. color_t tft_bg = { 0, 0, 0};
  64. uint8_t tft_image_debug = 0;
  65. float tft_angleOffset = DEFAULT_ANGLE_OFFSET;
  66. int tft_x = 0;
  67. int tft_y = 0;
  68. uint32_t tft_tp_calx = 7472920;
  69. uint32_t tft_tp_caly = 122224794;
  70. dispWin_t tft_dispWin = {
  71. .x1 = TFT_STATIC_WIDTH_OFFSET,
  72. .y1 = TFT_STATIC_HEIGHT_OFFSET,
  73. .x2 = DEFAULT_TFT_DISPLAY_WIDTH + TFT_STATIC_WIDTH_OFFSET,
  74. .y2 = DEFAULT_TFT_DISPLAY_HEIGHT + TFT_STATIC_HEIGHT_OFFSET,
  75. };
  76. Font tft_cfont = {
  77. .font = tft_DefaultFont,
  78. .x_size = 0,
  79. .y_size = 0x0B,
  80. .offset = 0,
  81. .numchars = 95,
  82. .bitmap = 1,
  83. };
  84. uint8_t tft_font_buffered_char = 1;
  85. uint8_t tft_font_line_space = 0;
  86. // ==============================================================
  87. typedef struct {
  88. uint8_t charCode;
  89. int adjYOffset;
  90. int width;
  91. int height;
  92. int xOffset;
  93. int xDelta;
  94. uint16_t dataPtr;
  95. } propFont;
  96. static dispWin_t dispWinTemp;
  97. static uint8_t *userfont = NULL;
  98. static int TFT_OFFSET = 0;
  99. static propFont fontChar;
  100. static float _arcAngleMax = DEFAULT_ARC_ANGLE_MAX;
  101. // =========================================================================
  102. // ** All drawings are clipped to 'tft_dispWin' **
  103. // ** All x,y coordinates in public functions are relative to clip window **
  104. // =========== : Public functions
  105. // ----------- : Local functions
  106. // =========================================================================
  107. // Compare two colors; return 0 if equal
  108. //============================================
  109. int TFT_compare_colors(color_t c1, color_t c2)
  110. {
  111. if ((c1.r & 0xFC) != (c2.r & 0xFC)) return 1;
  112. if ((c1.g & 0xFC) != (c2.g & 0xFC)) return 1;
  113. if ((c1.b & 0xFC) != (c2.b & 0xFC)) return 1;
  114. return 0;
  115. }
  116. // draw color pixel on screen
  117. //------------------------------------------------------------------------
  118. static void _drawPixel(int16_t x, int16_t y, color_t color, uint8_t sel) {
  119. if ((x < tft_dispWin.x1) || (y < tft_dispWin.y1) || (x > tft_dispWin.x2) || (y > tft_dispWin.y2)) return;
  120. drawPixel(x, y, color, sel);
  121. }
  122. //====================================================================
  123. void TFT_drawPixel(int16_t x, int16_t y, color_t color, uint8_t sel) {
  124. _drawPixel(x+tft_dispWin.x1, y+tft_dispWin.y1, color, sel);
  125. }
  126. //===========================================
  127. color_t TFT_readPixel(int16_t x, int16_t y) {
  128. if ((x < tft_dispWin.x1) || (y < tft_dispWin.y1) || (x > tft_dispWin.x2) || (y > tft_dispWin.y2)) return TFT_BLACK;
  129. return readPixel(x, y);
  130. }
  131. //--------------------------------------------------------------------------
  132. static void _drawFastVLine(int16_t x, int16_t y, int16_t h, color_t color) {
  133. // clipping
  134. if ((x < tft_dispWin.x1) || (x > tft_dispWin.x2) || (y > tft_dispWin.y2)) return;
  135. if (y < tft_dispWin.y1) {
  136. h -= (tft_dispWin.y1 - y);
  137. y = tft_dispWin.y1;
  138. }
  139. if (h < 0) h = 0;
  140. if ((y + h) > (tft_dispWin.y2+1)) h = tft_dispWin.y2 - y + 1;
  141. if (h == 0) h = 1;
  142. TFT_pushColorRep(x, y, x, y+h-1, color, (uint32_t)h);
  143. }
  144. //--------------------------------------------------------------------------
  145. static void _drawFastHLine(int16_t x, int16_t y, int16_t w, color_t color) {
  146. // clipping
  147. if ((y < tft_dispWin.y1) || (x > tft_dispWin.x2) || (y > tft_dispWin.y2)) return;
  148. if (x < tft_dispWin.x1) {
  149. w -= (tft_dispWin.x1 - x);
  150. x = tft_dispWin.x1;
  151. }
  152. if (w < 0) w = 0;
  153. if ((x + w) > (tft_dispWin.x2+1)) w = tft_dispWin.x2 - x + 1;
  154. if (w == 0) w = 1;
  155. TFT_pushColorRep(x, y, x+w-1, y, color, (uint32_t)w);
  156. }
  157. //======================================================================
  158. void TFT_drawFastVLine(int16_t x, int16_t y, int16_t h, color_t color) {
  159. _drawFastVLine(x+tft_dispWin.x1, y+tft_dispWin.y1, h, color);
  160. }
  161. //======================================================================
  162. void TFT_drawFastHLine(int16_t x, int16_t y, int16_t w, color_t color) {
  163. _drawFastHLine(x+tft_dispWin.x1, y+tft_dispWin.y1, w, color);
  164. }
  165. // Bresenham's algorithm - thx wikipedia - speed enhanced by Bodmer this uses
  166. // the eficient FastH/V Line draw routine for segments of 2 pixels or more
  167. //----------------------------------------------------------------------------------
  168. static void _drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, color_t color)
  169. {
  170. if (x0 == x1) {
  171. if (y0 <= y1) _drawFastVLine(x0, y0, y1-y0, color);
  172. else _drawFastVLine(x0, y1, y0-y1, color);
  173. return;
  174. }
  175. if (y0 == y1) {
  176. if (x0 <= x1) _drawFastHLine(x0, y0, x1-x0, color);
  177. else _drawFastHLine(x1, y0, x0-x1, color);
  178. return;
  179. }
  180. int steep = 0;
  181. if (abs(y1 - y0) > abs(x1 - x0)) steep = 1;
  182. if (steep) {
  183. swap(x0, y0);
  184. swap(x1, y1);
  185. }
  186. if (x0 > x1) {
  187. swap(x0, x1);
  188. swap(y0, y1);
  189. }
  190. int16_t dx = x1 - x0, dy = abs(y1 - y0);
  191. int16_t err = dx >> 1, ystep = -1, xs = x0, dlen = 0;
  192. if (y0 < y1) ystep = 1;
  193. // Split into steep and not steep for FastH/V separation
  194. if (steep) {
  195. for (; x0 <= x1; x0++) {
  196. dlen++;
  197. err -= dy;
  198. if (err < 0) {
  199. err += dx;
  200. if (dlen == 1) _drawPixel(y0, xs, color, 1);
  201. else _drawFastVLine(y0, xs, dlen, color);
  202. dlen = 0; y0 += ystep; xs = x0 + 1;
  203. }
  204. }
  205. if (dlen) _drawFastVLine(y0, xs, dlen, color);
  206. }
  207. else
  208. {
  209. for (; x0 <= x1; x0++) {
  210. dlen++;
  211. err -= dy;
  212. if (err < 0) {
  213. err += dx;
  214. if (dlen == 1) _drawPixel(xs, y0, color, 1);
  215. else _drawFastHLine(xs, y0, dlen, color);
  216. dlen = 0; y0 += ystep; xs = x0 + 1;
  217. }
  218. }
  219. if (dlen) _drawFastHLine(xs, y0, dlen, color);
  220. }
  221. }
  222. //==============================================================================
  223. void TFT_drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, color_t color)
  224. {
  225. _drawLine(x0+tft_dispWin.x1, y0+tft_dispWin.y1, x1+tft_dispWin.x1, y1+tft_dispWin.y1, color);
  226. }
  227. // fill a rectangle
  228. //--------------------------------------------------------------------------------
  229. static void _fillRect(int16_t x, int16_t y, int16_t w, int16_t h, color_t color) {
  230. // clipping
  231. if ((x >= tft_dispWin.x2) || (y > tft_dispWin.y2)) return;
  232. if (x < tft_dispWin.x1) {
  233. w -= (tft_dispWin.x1 - x);
  234. x = tft_dispWin.x1;
  235. }
  236. if (y < tft_dispWin.y1) {
  237. h -= (tft_dispWin.y1 - y);
  238. y = tft_dispWin.y1;
  239. }
  240. if (w < 0) w = 0;
  241. if (h < 0) h = 0;
  242. if ((x + w) > (tft_dispWin.x2+1)) w = tft_dispWin.x2 - x + 1;
  243. if ((y + h) > (tft_dispWin.y2+1)) h = tft_dispWin.y2 - y + 1;
  244. if (w == 0) w = 1;
  245. if (h == 0) h = 1;
  246. TFT_pushColorRep(x, y, x+w-1, y+h-1, color, (uint32_t)(h*w));
  247. }
  248. //============================================================================
  249. void TFT_fillRect(int16_t x, int16_t y, int16_t w, int16_t h, color_t color) {
  250. _fillRect(x+tft_dispWin.x1, y+tft_dispWin.y1, w, h, color);
  251. }
  252. //==================================
  253. void TFT_fillScreen(color_t color) {
  254. TFT_pushColorRep(TFT_STATIC_X_OFFSET, TFT_STATIC_Y_OFFSET, tft_width + TFT_STATIC_X_OFFSET -1, tft_height + TFT_STATIC_Y_OFFSET -1, color, (uint32_t)(tft_height*tft_width));
  255. }
  256. //==================================
  257. void TFT_fillWindow(color_t color) {
  258. TFT_pushColorRep(tft_dispWin.x1, tft_dispWin.y1, tft_dispWin.x2, tft_dispWin.y2,
  259. color, (uint32_t)((tft_dispWin.x2-tft_dispWin.x1+1) * (tft_dispWin.y2-tft_dispWin.y1+1)));
  260. }
  261. // ^^^============= Basics drawing functions ================================^^^
  262. // ================ Graphics drawing functions ==================================
  263. //-----------------------------------------------------------------------------------
  264. static void _drawRect(uint16_t x1,uint16_t y1,uint16_t w,uint16_t h, color_t color) {
  265. _drawFastHLine(x1,y1,w, color);
  266. _drawFastVLine(x1+w-1,y1,h, color);
  267. _drawFastHLine(x1,y1+h-1,w, color);
  268. _drawFastVLine(x1,y1,h, color);
  269. }
  270. //===============================================================================
  271. void TFT_drawRect(uint16_t x1,uint16_t y1,uint16_t w,uint16_t h, color_t color) {
  272. _drawRect(x1+tft_dispWin.x1, y1+tft_dispWin.y1, w, h, color);
  273. }
  274. //-------------------------------------------------------------------------------------------------
  275. static void drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, color_t color)
  276. {
  277. int16_t f = 1 - r;
  278. int16_t ddF_x = 1;
  279. int16_t ddF_y = -2 * r;
  280. int16_t x = 0;
  281. int16_t y = r;
  282. disp_select();
  283. while (x < y) {
  284. if (f >= 0) {
  285. y--;
  286. ddF_y += 2;
  287. f += ddF_y;
  288. }
  289. x++;
  290. ddF_x += 2;
  291. f += ddF_x;
  292. if (cornername & 0x4) {
  293. _drawPixel(x0 + x, y0 + y, color, 0);
  294. _drawPixel(x0 + y, y0 + x, color, 0);
  295. }
  296. if (cornername & 0x2) {
  297. _drawPixel(x0 + x, y0 - y, color, 0);
  298. _drawPixel(x0 + y, y0 - x, color, 0);
  299. }
  300. if (cornername & 0x8) {
  301. _drawPixel(x0 - y, y0 + x, color, 0);
  302. _drawPixel(x0 - x, y0 + y, color, 0);
  303. }
  304. if (cornername & 0x1) {
  305. _drawPixel(x0 - y, y0 - x, color, 0);
  306. _drawPixel(x0 - x, y0 - y, color, 0);
  307. }
  308. }
  309. disp_deselect();
  310. }
  311. // Used to do circles and roundrects
  312. //----------------------------------------------------------------------------------------------------------------
  313. static void fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, color_t color)
  314. {
  315. int16_t f = 1 - r;
  316. int16_t ddF_x = 1;
  317. int16_t ddF_y = -2 * r;
  318. int16_t x = 0;
  319. int16_t y = r;
  320. int16_t ylm = x0 - r;
  321. while (x < y) {
  322. if (f >= 0) {
  323. if (cornername & 0x1) _drawFastVLine(x0 + y, y0 - x, 2 * x + 1 + delta, color);
  324. if (cornername & 0x2) _drawFastVLine(x0 - y, y0 - x, 2 * x + 1 + delta, color);
  325. ylm = x0 - y;
  326. y--;
  327. ddF_y += 2;
  328. f += ddF_y;
  329. }
  330. x++;
  331. ddF_x += 2;
  332. f += ddF_x;
  333. if ((x0 - x) > ylm) {
  334. if (cornername & 0x1) _drawFastVLine(x0 + x, y0 - y, 2 * y + 1 + delta, color);
  335. if (cornername & 0x2) _drawFastVLine(x0 - x, y0 - y, 2 * y + 1 + delta, color);
  336. }
  337. }
  338. }
  339. // Draw a rounded rectangle
  340. //=============================================================================================
  341. void TFT_drawRoundRect(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t r, color_t color)
  342. {
  343. x += tft_dispWin.x1;
  344. y += tft_dispWin.y1;
  345. // smarter version
  346. _drawFastHLine(x + r, y, w - 2 * r, color); // Top
  347. _drawFastHLine(x + r, y + h - 1, w - 2 * r, color); // Bottom
  348. _drawFastVLine(x, y + r, h - 2 * r, color); // Left
  349. _drawFastVLine(x + w - 1, y + r, h - 2 * r, color); // Right
  350. // draw four corners
  351. drawCircleHelper(x + r, y + r, r, 1, color);
  352. drawCircleHelper(x + w - r - 1, y + r, r, 2, color);
  353. drawCircleHelper(x + w - r - 1, y + h - r - 1, r, 4, color);
  354. drawCircleHelper(x + r, y + h - r - 1, r, 8, color);
  355. }
  356. // Fill a rounded rectangle
  357. //=============================================================================================
  358. void TFT_fillRoundRect(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t r, color_t color)
  359. {
  360. x += tft_dispWin.x1;
  361. y += tft_dispWin.y1;
  362. // smarter version
  363. _fillRect(x + r, y, w - 2 * r, h, color);
  364. // draw four corners
  365. fillCircleHelper(x + w - r - 1, y + r, r, 1, h - 2 * r - 1, color);
  366. fillCircleHelper(x + r, y + r, r, 2, h - 2 * r - 1, color);
  367. }
  368. //-----------------------------------------------------------------------------------------------
  369. static void _drawLineByAngle(int16_t x, int16_t y, int16_t angle, uint16_t length, color_t color)
  370. {
  371. _drawLine(
  372. x,
  373. y,
  374. x + length * cos((angle + tft_angleOffset) * DEG_TO_RAD),
  375. y + length * sin((angle + tft_angleOffset) * DEG_TO_RAD), color);
  376. }
  377. //---------------------------------------------------------------------------------------------------------------
  378. static void _DrawLineByAngle(int16_t x, int16_t y, int16_t angle, uint16_t start, uint16_t length, color_t color)
  379. {
  380. _drawLine(
  381. x + start * cos((angle + tft_angleOffset) * DEG_TO_RAD),
  382. y + start * sin((angle + tft_angleOffset) * DEG_TO_RAD),
  383. x + (start + length) * cos((angle + tft_angleOffset) * DEG_TO_RAD),
  384. y + (start + length) * sin((angle + tft_angleOffset) * DEG_TO_RAD), color);
  385. }
  386. //===========================================================================================================
  387. void TFT_drawLineByAngle(uint16_t x, uint16_t y, uint16_t start, uint16_t len, uint16_t angle, color_t color)
  388. {
  389. x += tft_dispWin.x1;
  390. y += tft_dispWin.y1;
  391. if (start == 0) _drawLineByAngle(x, y, angle, len, color);
  392. else _DrawLineByAngle(x, y, angle, start, len, color);
  393. }
  394. // Draw a triangle
  395. //--------------------------------------------------------------------------------------------------------------------
  396. static void _drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
  397. {
  398. _drawLine(x0, y0, x1, y1, color);
  399. _drawLine(x1, y1, x2, y2, color);
  400. _drawLine(x2, y2, x0, y0, color);
  401. }
  402. //================================================================================================================
  403. void TFT_drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
  404. {
  405. x0 += tft_dispWin.x1;
  406. y0 += tft_dispWin.y1;
  407. x1 += tft_dispWin.x1;
  408. y1 += tft_dispWin.y1;
  409. x2 += tft_dispWin.x1;
  410. y2 += tft_dispWin.y1;
  411. _drawLine(x0, y0, x1, y1, color);
  412. _drawLine(x1, y1, x2, y2, color);
  413. _drawLine(x2, y2, x0, y0, color);
  414. }
  415. // Fill a triangle
  416. //--------------------------------------------------------------------------------------------------------------------
  417. static void _fillTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
  418. {
  419. int16_t a, b, y, last;
  420. // Sort coordinates by Y order (y2 >= y1 >= y0)
  421. if (y0 > y1) {
  422. swap(y0, y1); swap(x0, x1);
  423. }
  424. if (y1 > y2) {
  425. swap(y2, y1); swap(x2, x1);
  426. }
  427. if (y0 > y1) {
  428. swap(y0, y1); swap(x0, x1);
  429. }
  430. if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing
  431. a = b = x0;
  432. if(x1 < a) a = x1;
  433. else if(x1 > b) b = x1;
  434. if(x2 < a) a = x2;
  435. else if(x2 > b) b = x2;
  436. _drawFastHLine(a, y0, b-a+1, color);
  437. return;
  438. }
  439. int16_t
  440. dx01 = x1 - x0,
  441. dy01 = y1 - y0,
  442. dx02 = x2 - x0,
  443. dy02 = y2 - y0,
  444. dx12 = x2 - x1,
  445. dy12 = y2 - y1;
  446. int32_t
  447. sa = 0,
  448. sb = 0;
  449. // For upper part of triangle, find scanline crossings for segments
  450. // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1
  451. // is included here (and second loop will be skipped, avoiding a /0
  452. // error there), otherwise scanline y1 is skipped here and handled
  453. // in the second loop...which also avoids a /0 error here if y0=y1
  454. // (flat-topped triangle).
  455. if(y1 == y2) last = y1; // Include y1 scanline
  456. else last = y1-1; // Skip it
  457. for(y=y0; y<=last; y++) {
  458. a = x0 + sa / dy01;
  459. b = x0 + sb / dy02;
  460. sa += dx01;
  461. sb += dx02;
  462. /* longhand:
  463. a = x0 + (x1 - x0) * (y - y0) / (y1 - y0);
  464. b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
  465. */
  466. if(a > b) swap(a,b);
  467. _drawFastHLine(a, y, b-a+1, color);
  468. }
  469. // For lower part of triangle, find scanline crossings for segments
  470. // 0-2 and 1-2. This loop is skipped if y1=y2.
  471. sa = dx12 * (y - y1);
  472. sb = dx02 * (y - y0);
  473. for(; y<=y2; y++) {
  474. a = x1 + sa / dy12;
  475. b = x0 + sb / dy02;
  476. sa += dx12;
  477. sb += dx02;
  478. /* longhand:
  479. a = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
  480. b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
  481. */
  482. if(a > b) swap(a,b);
  483. _drawFastHLine(a, y, b-a+1, color);
  484. }
  485. }
  486. //================================================================================================================
  487. void TFT_fillTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
  488. {
  489. _fillTriangle(
  490. x0 + tft_dispWin.x1, y0 + tft_dispWin.y1,
  491. x1 + tft_dispWin.x1, y1 + tft_dispWin.y1,
  492. x2 + tft_dispWin.x1, y2 + tft_dispWin.y1,
  493. color);
  494. }
  495. //====================================================================
  496. void TFT_drawCircle(int16_t x, int16_t y, int radius, color_t color) {
  497. x += tft_dispWin.x1;
  498. y += tft_dispWin.y1;
  499. int f = 1 - radius;
  500. int ddF_x = 1;
  501. int ddF_y = -2 * radius;
  502. int x1 = 0;
  503. int y1 = radius;
  504. disp_select();
  505. _drawPixel(x, y + radius, color, 0);
  506. _drawPixel(x, y - radius, color, 0);
  507. _drawPixel(x + radius, y, color, 0);
  508. _drawPixel(x - radius, y, color, 0);
  509. while(x1 < y1) {
  510. if (f >= 0) {
  511. y1--;
  512. ddF_y += 2;
  513. f += ddF_y;
  514. }
  515. x1++;
  516. ddF_x += 2;
  517. f += ddF_x;
  518. _drawPixel(x + x1, y + y1, color, 0);
  519. _drawPixel(x - x1, y + y1, color, 0);
  520. _drawPixel(x + x1, y - y1, color, 0);
  521. _drawPixel(x - x1, y - y1, color, 0);
  522. _drawPixel(x + y1, y + x1, color, 0);
  523. _drawPixel(x - y1, y + x1, color, 0);
  524. _drawPixel(x + y1, y - x1, color, 0);
  525. _drawPixel(x - y1, y - x1, color, 0);
  526. }
  527. disp_deselect();
  528. }
  529. //====================================================================
  530. void TFT_fillCircle(int16_t x, int16_t y, int radius, color_t color) {
  531. x += tft_dispWin.x1;
  532. y += tft_dispWin.y1;
  533. _drawFastVLine(x, y-radius, 2*radius+1, color);
  534. fillCircleHelper(x, y, radius, 3, 0, color);
  535. }
  536. //----------------------------------------------------------------------------------------------------------------
  537. static void _draw_ellipse_section(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, color_t color, uint8_t option)
  538. {
  539. disp_select();
  540. // upper right
  541. if ( option & TFT_ELLIPSE_UPPER_RIGHT ) _drawPixel(x0 + x, y0 - y, color, 0);
  542. // upper left
  543. if ( option & TFT_ELLIPSE_UPPER_LEFT ) _drawPixel(x0 - x, y0 - y, color, 0);
  544. // lower right
  545. if ( option & TFT_ELLIPSE_LOWER_RIGHT ) _drawPixel(x0 + x, y0 + y, color, 0);
  546. // lower left
  547. if ( option & TFT_ELLIPSE_LOWER_LEFT ) _drawPixel(x0 - x, y0 + y, color, 0);
  548. disp_deselect();
  549. }
  550. //=====================================================================================================
  551. void TFT_drawEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, color_t color, uint8_t option)
  552. {
  553. x0 += tft_dispWin.x1;
  554. y0 += tft_dispWin.y1;
  555. uint16_t x, y;
  556. int32_t xchg, ychg;
  557. int32_t err;
  558. int32_t rxrx2;
  559. int32_t ryry2;
  560. int32_t stopx, stopy;
  561. rxrx2 = rx;
  562. rxrx2 *= rx;
  563. rxrx2 *= 2;
  564. ryry2 = ry;
  565. ryry2 *= ry;
  566. ryry2 *= 2;
  567. x = rx;
  568. y = 0;
  569. xchg = 1;
  570. xchg -= rx;
  571. xchg -= rx;
  572. xchg *= ry;
  573. xchg *= ry;
  574. ychg = rx;
  575. ychg *= rx;
  576. err = 0;
  577. stopx = ryry2;
  578. stopx *= rx;
  579. stopy = 0;
  580. while( stopx >= stopy ) {
  581. _draw_ellipse_section(x, y, x0, y0, color, option);
  582. y++;
  583. stopy += rxrx2;
  584. err += ychg;
  585. ychg += rxrx2;
  586. if ( 2*err+xchg > 0 ) {
  587. x--;
  588. stopx -= ryry2;
  589. err += xchg;
  590. xchg += ryry2;
  591. }
  592. }
  593. x = 0;
  594. y = ry;
  595. xchg = ry;
  596. xchg *= ry;
  597. ychg = 1;
  598. ychg -= ry;
  599. ychg -= ry;
  600. ychg *= rx;
  601. ychg *= rx;
  602. err = 0;
  603. stopx = 0;
  604. stopy = rxrx2;
  605. stopy *= ry;
  606. while( stopx <= stopy ) {
  607. _draw_ellipse_section(x, y, x0, y0, color, option);
  608. x++;
  609. stopx += ryry2;
  610. err += xchg;
  611. xchg += ryry2;
  612. if ( 2*err+ychg > 0 ) {
  613. y--;
  614. stopy -= rxrx2;
  615. err += ychg;
  616. ychg += rxrx2;
  617. }
  618. }
  619. }
  620. //-----------------------------------------------------------------------------------------------------------------------
  621. static void _draw_filled_ellipse_section(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, color_t color, uint8_t option)
  622. {
  623. // upper right
  624. if ( option & TFT_ELLIPSE_UPPER_RIGHT ) _drawFastVLine(x0+x, y0-y, y+1, color);
  625. // upper left
  626. if ( option & TFT_ELLIPSE_UPPER_LEFT ) _drawFastVLine(x0-x, y0-y, y+1, color);
  627. // lower right
  628. if ( option & TFT_ELLIPSE_LOWER_RIGHT ) _drawFastVLine(x0+x, y0, y+1, color);
  629. // lower left
  630. if ( option & TFT_ELLIPSE_LOWER_LEFT ) _drawFastVLine(x0-x, y0, y+1, color);
  631. }
  632. //=====================================================================================================
  633. void TFT_fillEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, color_t color, uint8_t option)
  634. {
  635. x0 += tft_dispWin.x1;
  636. y0 += tft_dispWin.y1;
  637. uint16_t x, y;
  638. int32_t xchg, ychg;
  639. int32_t err;
  640. int32_t rxrx2;
  641. int32_t ryry2;
  642. int32_t stopx, stopy;
  643. rxrx2 = rx;
  644. rxrx2 *= rx;
  645. rxrx2 *= 2;
  646. ryry2 = ry;
  647. ryry2 *= ry;
  648. ryry2 *= 2;
  649. x = rx;
  650. y = 0;
  651. xchg = 1;
  652. xchg -= rx;
  653. xchg -= rx;
  654. xchg *= ry;
  655. xchg *= ry;
  656. ychg = rx;
  657. ychg *= rx;
  658. err = 0;
  659. stopx = ryry2;
  660. stopx *= rx;
  661. stopy = 0;
  662. while( stopx >= stopy ) {
  663. _draw_filled_ellipse_section(x, y, x0, y0, color, option);
  664. y++;
  665. stopy += rxrx2;
  666. err += ychg;
  667. ychg += rxrx2;
  668. if ( 2*err+xchg > 0 ) {
  669. x--;
  670. stopx -= ryry2;
  671. err += xchg;
  672. xchg += ryry2;
  673. }
  674. }
  675. x = 0;
  676. y = ry;
  677. xchg = ry;
  678. xchg *= ry;
  679. ychg = 1;
  680. ychg -= ry;
  681. ychg -= ry;
  682. ychg *= rx;
  683. ychg *= rx;
  684. err = 0;
  685. stopx = 0;
  686. stopy = rxrx2;
  687. stopy *= ry;
  688. while( stopx <= stopy ) {
  689. _draw_filled_ellipse_section(x, y, x0, y0, color, option);
  690. x++;
  691. stopx += ryry2;
  692. err += xchg;
  693. xchg += ryry2;
  694. if ( 2*err+ychg > 0 ) {
  695. y--;
  696. stopy -= rxrx2;
  697. err += ychg;
  698. ychg += rxrx2;
  699. }
  700. }
  701. }
  702. // ==== ARC DRAWING ===================================================================
  703. //---------------------------------------------------------------------------------------------------------------------------------
  704. static void _fillArcOffsetted(uint16_t cx, uint16_t cy, uint16_t radius, uint16_t thickness, float start, float end, color_t color)
  705. {
  706. //float sslope = (float)cos_lookup(start) / (float)sin_lookup(start);
  707. //float eslope = (float)cos_lookup(end) / (float)sin_lookup(end);
  708. float sslope = (cos(start/_arcAngleMax * 2 * PI) * _arcAngleMax) / (sin(start/_arcAngleMax * 2 * PI) * _arcAngleMax) ;
  709. float eslope = (cos(end/_arcAngleMax * 2 * PI) * _arcAngleMax) / (sin(end/_arcAngleMax * 2 * PI) * _arcAngleMax);
  710. if (end == 360) eslope = -1000000;
  711. int ir2 = (radius - thickness) * (radius - thickness);
  712. int or2 = radius * radius;
  713. disp_select();
  714. for (int x = -radius; x <= radius; x++) {
  715. for (int y = -radius; y <= radius; y++) {
  716. int x2 = x * x;
  717. int y2 = y * y;
  718. if (
  719. (x2 + y2 < or2 && x2 + y2 >= ir2) &&
  720. (
  721. (y > 0 && start < 180 && x <= y * sslope) ||
  722. (y < 0 && start > 180 && x >= y * sslope) ||
  723. (y < 0 && start <= 180) ||
  724. (y == 0 && start <= 180 && x < 0) ||
  725. (y == 0 && start == 0 && x > 0)
  726. ) &&
  727. (
  728. (y > 0 && end < 180 && x >= y * eslope) ||
  729. (y < 0 && end > 180 && x <= y * eslope) ||
  730. (y > 0 && end >= 180) ||
  731. (y == 0 && end >= 180 && x < 0) ||
  732. (y == 0 && start == 0 && x > 0)
  733. )
  734. )
  735. _drawPixel(cx+x, cy+y, color, 0);
  736. }
  737. }
  738. disp_deselect();
  739. }
  740. //===========================================================================================================================
  741. void TFT_drawArc(uint16_t cx, uint16_t cy, uint16_t r, uint16_t th, float start, float end, color_t color, color_t fillcolor)
  742. {
  743. cx += tft_dispWin.x1;
  744. cy += tft_dispWin.y1;
  745. if (th < 1) th = 1;
  746. if (th > r) th = r;
  747. int f = TFT_compare_colors(fillcolor, color);
  748. float astart = fmodf(start, _arcAngleMax);
  749. float aend = fmodf(end, _arcAngleMax);
  750. astart += tft_angleOffset;
  751. aend += tft_angleOffset;
  752. if (astart < 0) astart += (float)360;
  753. if (aend < 0) aend += (float)360;
  754. if (aend == 0) aend = (float)360;
  755. if (astart > aend) {
  756. _fillArcOffsetted(cx, cy, r, th, astart, _arcAngleMax, fillcolor);
  757. _fillArcOffsetted(cx, cy, r, th, 0, aend, fillcolor);
  758. if (f) {
  759. _fillArcOffsetted(cx, cy, r, 1, astart, _arcAngleMax, color);
  760. _fillArcOffsetted(cx, cy, r, 1, 0, aend, color);
  761. _fillArcOffsetted(cx, cy, r-th, 1, astart, _arcAngleMax, color);
  762. _fillArcOffsetted(cx, cy, r-th, 1, 0, aend, color);
  763. }
  764. }
  765. else {
  766. _fillArcOffsetted(cx, cy, r, th, astart, aend, fillcolor);
  767. if (f) {
  768. _fillArcOffsetted(cx, cy, r, 1, astart, aend, color);
  769. _fillArcOffsetted(cx, cy, r-th, 1, astart, aend, color);
  770. }
  771. }
  772. if (f) {
  773. _drawLine(cx + (r-th) * cos(astart * DEG_TO_RAD), cy + (r-th) * sin(astart * DEG_TO_RAD),
  774. cx + (r-1) * cos(astart * DEG_TO_RAD), cy + (r-1) * sin(astart * DEG_TO_RAD), color);
  775. _drawLine(cx + (r-th) * cos(aend * DEG_TO_RAD), cy + (r-th) * sin(aend * DEG_TO_RAD),
  776. cx + (r-1) * cos(aend * DEG_TO_RAD), cy + (r-1) * sin(aend * DEG_TO_RAD), color);
  777. }
  778. }
  779. //=============================================================================================================
  780. void TFT_drawPolygon(int cx, int cy, int sides, int diameter, color_t color, color_t fill, int rot, uint8_t th)
  781. {
  782. cx += tft_dispWin.x1;
  783. cy += tft_dispWin.y1;
  784. int deg = rot - tft_angleOffset;
  785. int f = TFT_compare_colors(fill, color);
  786. if (sides < MIN_POLIGON_SIDES) sides = MIN_POLIGON_SIDES; // This ensures the minimum side number
  787. if (sides > MAX_POLIGON_SIDES) sides = MAX_POLIGON_SIDES; // This ensures the maximum side number
  788. int Xpoints[sides], Ypoints[sides]; // Set the arrays based on the number of sides entered
  789. int rads = 360 / sides; // This equally spaces the points.
  790. for (int idx = 0; idx < sides; idx++) {
  791. Xpoints[idx] = cx + sin((float)(idx*rads + deg) * deg_to_rad) * diameter;
  792. Ypoints[idx] = cy + cos((float)(idx*rads + deg) * deg_to_rad) * diameter;
  793. }
  794. // Draw the polygon on the screen.
  795. if (f) {
  796. for(int idx = 0; idx < sides; idx++) {
  797. if((idx+1) < sides) _fillTriangle(cx,cy,Xpoints[idx],Ypoints[idx],Xpoints[idx+1],Ypoints[idx+1], fill);
  798. else _fillTriangle(cx,cy,Xpoints[idx],Ypoints[idx],Xpoints[0],Ypoints[0], fill);
  799. }
  800. }
  801. if (th) {
  802. for (int n=0; n<th; n++) {
  803. if (n > 0) {
  804. for (int idx = 0; idx < sides; idx++) {
  805. Xpoints[idx] = cx + sin((float)(idx*rads + deg) * deg_to_rad) * (diameter-n);
  806. Ypoints[idx] = cy + cos((float)(idx*rads + deg) * deg_to_rad) * (diameter-n);
  807. }
  808. }
  809. for(int idx = 0; idx < sides; idx++) {
  810. if( (idx+1) < sides)
  811. _drawLine(Xpoints[idx],Ypoints[idx],Xpoints[idx+1],Ypoints[idx+1], color); // draw the lines
  812. else
  813. _drawLine(Xpoints[idx],Ypoints[idx],Xpoints[0],Ypoints[0], color); // finishes the last line to close up the polygon.
  814. }
  815. }
  816. }
  817. }
  818. /*
  819. // Similar to the Polygon function.
  820. //=====================================================================================
  821. void TFT_drawStar(int cx, int cy, int diameter, color_t color, bool fill, float factor)
  822. {
  823. cx += tft_dispWin.x1;
  824. cy += tft_dispWin.y1;
  825. factor = constrain(factor, 1.0, 4.0);
  826. uint8_t sides = 5;
  827. uint8_t rads = 360 / sides;
  828. int Xpoints_O[sides], Ypoints_O[sides], Xpoints_I[sides], Ypoints_I[sides];//Xpoints_T[5], Ypoints_T[5];
  829. for(int idx = 0; idx < sides; idx++) {
  830. // makes the outer points
  831. Xpoints_O[idx] = cx + sin((float)(idx*rads + 72) * deg_to_rad) * diameter;
  832. Ypoints_O[idx] = cy + cos((float)(idx*rads + 72) * deg_to_rad) * diameter;
  833. // makes the inner points
  834. Xpoints_I[idx] = cx + sin((float)(idx*rads + 36) * deg_to_rad) * ((float)(diameter)/factor);
  835. // 36 is half of 72, and this will allow the inner and outer points to line up like a triangle.
  836. Ypoints_I[idx] = cy + cos((float)(idx*rads + 36) * deg_to_rad) * ((float)(diameter)/factor);
  837. }
  838. for(int idx = 0; idx < sides; idx++) {
  839. if((idx+1) < sides) {
  840. if(fill) {// this part below should be self explanatory. It fills in the star.
  841. _fillTriangle(cx,cy,Xpoints_I[idx],Ypoints_I[idx],Xpoints_O[idx],Ypoints_O[idx], color);
  842. _fillTriangle(cx,cy,Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx+1],Ypoints_I[idx+1], color);
  843. }
  844. else {
  845. _drawLine(Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx+1],Ypoints_I[idx+1], color);
  846. _drawLine(Xpoints_I[idx],Ypoints_I[idx],Xpoints_O[idx],Ypoints_O[idx], color);
  847. }
  848. }
  849. else {
  850. if(fill) {
  851. _fillTriangle(cx,cy,Xpoints_I[0],Ypoints_I[0],Xpoints_O[idx],Ypoints_O[idx], color);
  852. _fillTriangle(cx,cy,Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx],Ypoints_I[idx], color);
  853. }
  854. else {
  855. _drawLine(Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx],Ypoints_I[idx], color);
  856. _drawLine(Xpoints_I[0],Ypoints_I[0],Xpoints_O[idx],Ypoints_O[idx], color);
  857. }
  858. }
  859. }
  860. }
  861. */
  862. // ================ Font and string functions ==================================
  863. //--------------------------------------------------------
  864. static int load_file_font(const char * fontfile, int info)
  865. {
  866. int err = 0;
  867. char err_msg[256] = {'\0'};
  868. if (userfont != NULL) {
  869. free(userfont);
  870. userfont = NULL;
  871. }
  872. struct stat sb;
  873. // Open the file
  874. FILE *fhndl = fopen(fontfile, "r");
  875. if (!fhndl) {
  876. sprintf(err_msg, "Error opening font file '%s'", fontfile);
  877. err = 1;
  878. goto exit;
  879. }
  880. // Get file size
  881. if (stat(fontfile, &sb) != 0) {
  882. sprintf(err_msg, "Error getting font file size");
  883. err = 2;
  884. goto exit;
  885. }
  886. int fsize = sb.st_size;
  887. if (fsize < 30) {
  888. sprintf(err_msg, "Error getting font file size");
  889. err = 3;
  890. goto exit;
  891. }
  892. userfont = malloc(fsize+4);
  893. if (userfont == NULL) {
  894. sprintf(err_msg, "Font memory allocation error");
  895. fclose(fhndl);
  896. err = 4;
  897. goto exit;
  898. }
  899. int read = fread(userfont, 1, fsize, fhndl);
  900. fclose(fhndl);
  901. if (read != fsize) {
  902. sprintf(err_msg, "Font read error");
  903. err = 5;
  904. goto exit;
  905. }
  906. userfont[read] = 0;
  907. if (strstr((char *)(userfont+read-8), "RPH_font") == NULL) {
  908. sprintf(err_msg, "Font ID not found");
  909. err = 6;
  910. goto exit;
  911. }
  912. // Check size
  913. int size = 0;
  914. int numchar = 0;
  915. int width = userfont[0];
  916. int height = userfont[1];
  917. uint8_t first = 255;
  918. uint8_t last = 0;
  919. //int offst = 0;
  920. int pminwidth = 255;
  921. int pmaxwidth = 0;
  922. if (width != 0) {
  923. // Fixed font
  924. numchar = userfont[3];
  925. first = userfont[2];
  926. last = first + numchar - 1;
  927. size = ((width * height * numchar) / 8) + 4;
  928. }
  929. else {
  930. // Proportional font
  931. size = 4; // point at first char data
  932. uint8_t charCode;
  933. int charwidth;
  934. do {
  935. charCode = userfont[size];
  936. charwidth = userfont[size+2];
  937. if (charCode != 0xFF) {
  938. numchar++;
  939. if (charwidth != 0) size += ((((charwidth * userfont[size+3])-1) / 8) + 7);
  940. else size += 6;
  941. if (info) {
  942. if (charwidth > pmaxwidth) pmaxwidth = charwidth;
  943. if (charwidth < pminwidth) pminwidth = charwidth;
  944. if (charCode < first) first = charCode;
  945. if (charCode > last) last = charCode;
  946. }
  947. }
  948. else size++;
  949. } while ((size < (read-8)) && (charCode != 0xFF));
  950. }
  951. if (size != (read-8)) {
  952. sprintf(err_msg, "Font size error: found %d expected %d)", size, (read-8));
  953. err = 7;
  954. goto exit;
  955. }
  956. if (info) {
  957. if (width != 0) {
  958. printf("Fixed width font:\r\n size: %d width: %d height: %d characters: %d (%d~%d)\n",
  959. size, width, height, numchar, first, last);
  960. }
  961. else {
  962. printf("Proportional font:\r\n size: %d width: %d~%d height: %d characters: %d (%d~%d)\n",
  963. size, pminwidth, pmaxwidth, height, numchar, first, last);
  964. }
  965. }
  966. exit:
  967. if (err) {
  968. if (userfont) {
  969. free(userfont);
  970. userfont = NULL;
  971. }
  972. if (info) printf("Error: %d [%s]\r\n", err, err_msg);
  973. }
  974. return err;
  975. }
  976. //------------------------------------------------
  977. int compile_font_file(char *fontfile, uint8_t dbg)
  978. {
  979. int err = 0;
  980. char err_msg[128] = {'\0'};
  981. char outfile[128] = {'\0'};
  982. size_t len;
  983. struct stat sb;
  984. FILE *ffd = NULL;
  985. FILE *ffd_out = NULL;
  986. char *sourcebuf = NULL;
  987. len = strlen(fontfile);
  988. // check here that filename end with ".c".
  989. if ((len < 3) || (len > 125) || (strcmp(fontfile + len - 2, ".c") != 0)) {
  990. sprintf(err_msg, "not a .c file");
  991. err = 1;
  992. goto exit;
  993. }
  994. sprintf(outfile, "%s", fontfile);
  995. sprintf(outfile+strlen(outfile)-1, "fon");
  996. // Open the source file
  997. if (stat(fontfile, &sb) != 0) {
  998. sprintf(err_msg, "Error opening source file '%s'", fontfile);
  999. err = 2;
  1000. goto exit;
  1001. }
  1002. // Open the file
  1003. ffd = fopen(fontfile, "rb");
  1004. if (!ffd) {
  1005. sprintf(err_msg, "Error opening source file '%s'", fontfile);
  1006. err = 3;
  1007. goto exit;
  1008. }
  1009. // Open the font file
  1010. ffd_out= fopen(outfile, "wb");
  1011. if (!ffd_out) {
  1012. sprintf(err_msg, "error opening destination file");
  1013. err = 4;
  1014. goto exit;
  1015. }
  1016. // Get file size
  1017. int fsize = sb.st_size;
  1018. if (fsize <= 0) {
  1019. sprintf(err_msg, "source file size error");
  1020. err = 5;
  1021. goto exit;
  1022. }
  1023. sourcebuf = malloc(fsize+4);
  1024. if (sourcebuf == NULL) {
  1025. sprintf(err_msg, "memory allocation error");
  1026. err = 6;
  1027. goto exit;
  1028. }
  1029. char *fbuf = sourcebuf;
  1030. int rdsize = fread(fbuf, 1, fsize, ffd);
  1031. fclose(ffd);
  1032. ffd = NULL;
  1033. if (rdsize != fsize) {
  1034. sprintf(err_msg, "error reading from source file");
  1035. err = 7;
  1036. goto exit;
  1037. }
  1038. *(fbuf+rdsize) = '\0';
  1039. fbuf = strchr(fbuf, '{'); // beginning of font data
  1040. char *fend = strstr(fbuf, "};"); // end of font data
  1041. if ((fbuf == NULL) || (fend == NULL) || ((fend-fbuf) < 22)) {
  1042. sprintf(err_msg, "wrong source file format");
  1043. err = 8;
  1044. goto exit;
  1045. }
  1046. fbuf++;
  1047. *fend = '\0';
  1048. char hexstr[5] = {'\0'};
  1049. int lastline = 0;
  1050. fbuf = strstr(fbuf, "0x");
  1051. int size = 0;
  1052. char *nextline;
  1053. char *numptr;
  1054. int bptr = 0;
  1055. while ((fbuf != NULL) && (fbuf < fend) && (lastline == 0)) {
  1056. nextline = strchr(fbuf, '\n'); // beginning of the next line
  1057. if (nextline == NULL) {
  1058. nextline = fend-1;
  1059. lastline++;
  1060. }
  1061. else nextline++;
  1062. while (fbuf < nextline) {
  1063. numptr = strstr(fbuf, "0x");
  1064. if ((numptr == NULL) || ((fbuf+4) > nextline)) numptr = strstr(fbuf, "0X");
  1065. if ((numptr != NULL) && ((numptr+4) <= nextline)) {
  1066. fbuf = numptr;
  1067. if (bptr >= 128) {
  1068. // buffer full, write to file
  1069. if (fwrite(outfile, 1, 128, ffd_out) != 128) goto error;
  1070. bptr = 0;
  1071. size += 128;
  1072. }
  1073. memcpy(hexstr, fbuf, 4);
  1074. hexstr[4] = 0;
  1075. outfile[bptr++] = (uint8_t)strtol(hexstr, NULL, 0);
  1076. fbuf += 4;
  1077. }
  1078. else fbuf = nextline;
  1079. }
  1080. fbuf = nextline;
  1081. }
  1082. if (bptr > 0) {
  1083. size += bptr;
  1084. if (fwrite(outfile, 1, bptr, ffd_out) != bptr) goto error;
  1085. }
  1086. // write font ID
  1087. sprintf(outfile, "RPH_font");
  1088. if (fwrite(outfile, 1, 8, ffd_out) != 8) goto error;
  1089. fclose(ffd_out);
  1090. ffd_out = NULL;
  1091. // === Test compiled font ===
  1092. sprintf(outfile, "%s", fontfile);
  1093. sprintf(outfile+strlen(outfile)-1, "fon");
  1094. uint8_t *uf = userfont; // save userfont pointer
  1095. userfont = NULL;
  1096. if (load_file_font(outfile, 1) != 0) {
  1097. sprintf(err_msg, "Error compiling file!");
  1098. err = 10;
  1099. }
  1100. else {
  1101. free(userfont);
  1102. sprintf(err_msg, "File compiled successfully.");
  1103. }
  1104. userfont = uf; // restore userfont
  1105. goto exit;
  1106. error:
  1107. sprintf(err_msg, "error writing to destination file");
  1108. err = 9;
  1109. exit:
  1110. if (sourcebuf) free(sourcebuf);
  1111. if (ffd) fclose(ffd);
  1112. if (ffd_out) fclose(ffd_out);
  1113. if (dbg) printf("%s\r\n", err_msg);
  1114. return err;
  1115. }
  1116. // -----------------------------------------------------------------------------------------
  1117. // Individual Proportional Font Character Format:
  1118. // -----------------------------------------------------------------------------------------
  1119. // Character Code
  1120. // yOffset (start Y of visible pixels)
  1121. // Width (width of the visible pixels)
  1122. // Height (height of the visible pixels)
  1123. // xOffset (start X of visible pixels)
  1124. // xDelta (the distance to move the cursor. Effective width of the character.)
  1125. // Data[n]
  1126. // -----------------------------------------------------------------------------------------
  1127. //---------------------------------------------------------------------------------------------
  1128. // Character drawing rectangle is (0, 0) (xDelta-1, tft_cfont.y_size-1)
  1129. // Character visible pixels rectangle is (xOffset, yOffset) (xOffset+Width-1, yOffset+Height-1)
  1130. //---------------------------------------------------------------------------------------------
  1131. //----------------------------------
  1132. void getFontCharacters(uint8_t *buf)
  1133. {
  1134. if (tft_cfont.bitmap == 2) {
  1135. //For 7 segment font only characters 0,1,2,3,4,5,6,7,8,9, . , - , : , / are available.
  1136. for (uint8_t n=0; n < 11; n++) {
  1137. buf[n] = n + 0x30;
  1138. }
  1139. buf[11] = '.';
  1140. buf[12] = '-';
  1141. buf[13] = '/';
  1142. buf[14] = '\0';
  1143. return;
  1144. }
  1145. if (tft_cfont.x_size > 0) {
  1146. for (uint8_t n=0; n < tft_cfont.numchars; n++) {
  1147. buf[n] = tft_cfont.offset + n;
  1148. }
  1149. buf[tft_cfont.numchars] = '\0';
  1150. return;
  1151. }
  1152. uint16_t tempPtr = 4; // point at first char data
  1153. uint8_t cc, cw, ch, n;
  1154. n = 0;
  1155. cc = tft_cfont.font[tempPtr++];
  1156. while (cc != 0xFF) {
  1157. tft_cfont.numchars++;
  1158. tempPtr++;
  1159. cw = tft_cfont.font[tempPtr++];
  1160. ch = tft_cfont.font[tempPtr++];
  1161. tempPtr++;
  1162. tempPtr++;
  1163. if (cw != 0) {
  1164. // packed bits
  1165. tempPtr += (((cw * ch)-1) / 8) + 1;
  1166. }
  1167. buf[n++] = cc;
  1168. cc = tft_cfont.font[tempPtr++];
  1169. }
  1170. buf[n] = '\0';
  1171. }
  1172. // Set max width & height of the proportional font
  1173. //-----------------------------
  1174. static void getMaxWidthHeight()
  1175. {
  1176. uint16_t tempPtr = 4; // point at first char data
  1177. uint8_t cc, cw, ch, cd, cy;
  1178. tft_cfont.numchars = 0;
  1179. tft_cfont.max_x_size = 0;
  1180. cc = tft_cfont.font[tempPtr++];
  1181. while (cc != 0xFF) {
  1182. tft_cfont.numchars++;
  1183. cy = tft_cfont.font[tempPtr++];
  1184. cw = tft_cfont.font[tempPtr++];
  1185. ch = tft_cfont.font[tempPtr++];
  1186. tempPtr++;
  1187. cd = tft_cfont.font[tempPtr++];
  1188. cy += ch;
  1189. if (cw > tft_cfont.max_x_size) tft_cfont.max_x_size = cw;
  1190. if (cd > tft_cfont.max_x_size) tft_cfont.max_x_size = cd;
  1191. if (ch > tft_cfont.y_size) tft_cfont.y_size = ch;
  1192. if (cy > tft_cfont.y_size) tft_cfont.y_size = cy;
  1193. if (cw != 0) {
  1194. // packed bits
  1195. tempPtr += (((cw * ch)-1) / 8) + 1;
  1196. }
  1197. cc = tft_cfont.font[tempPtr++];
  1198. }
  1199. tft_cfont.size = tempPtr;
  1200. }
  1201. // Return the Glyph data for an individual character in the proportional font
  1202. //------------------------------------
  1203. static uint8_t getCharPtr(uint8_t c) {
  1204. uint16_t tempPtr = 4; // point at first char data
  1205. do {
  1206. fontChar.charCode = tft_cfont.font[tempPtr++];
  1207. if (fontChar.charCode == 0xFF) return 0;
  1208. fontChar.adjYOffset = tft_cfont.font[tempPtr++];
  1209. fontChar.width = tft_cfont.font[tempPtr++];
  1210. fontChar.height = tft_cfont.font[tempPtr++];
  1211. fontChar.xOffset = tft_cfont.font[tempPtr++];
  1212. fontChar.xOffset = fontChar.xOffset < 0x80 ? fontChar.xOffset : -(0xFF - fontChar.xOffset);
  1213. fontChar.xDelta = tft_cfont.font[tempPtr++];
  1214. if (c != fontChar.charCode && fontChar.charCode != 0xFF) {
  1215. if (fontChar.width != 0) {
  1216. // packed bits
  1217. tempPtr += (((fontChar.width * fontChar.height)-1) / 8) + 1;
  1218. }
  1219. }
  1220. } while ((c != fontChar.charCode) && (fontChar.charCode != 0xFF));
  1221. fontChar.dataPtr = tempPtr;
  1222. if (c == fontChar.charCode) {
  1223. if (tft_font_forceFixed > 0) {
  1224. // fix width & offset for forced fixed width
  1225. fontChar.xDelta = tft_cfont.max_x_size;
  1226. fontChar.xOffset = (fontChar.xDelta - fontChar.width) / 2;
  1227. }
  1228. }
  1229. else return 0;
  1230. return 1;
  1231. }
  1232. /*
  1233. //-----------------------
  1234. static void _testFont() {
  1235. if (tft_cfont.x_size) {
  1236. printf("FONT TEST: fixed font\r\n");
  1237. return;
  1238. }
  1239. uint16_t tempPtr = 4; // point at first char data
  1240. uint8_t c = 0x20;
  1241. for (c=0x20; c <0xFF; c++) {
  1242. fontChar.charCode = tft_cfont.font[tempPtr++];
  1243. if (fontChar.charCode == 0xFF) break;
  1244. if (fontChar.charCode != c) {
  1245. printf("FONT TEST: last sequential char: %d, expected %d\r\n", fontChar.charCode, c);
  1246. break;
  1247. }
  1248. c = fontChar.charCode;
  1249. fontChar.adjYOffset = tft_cfont.font[tempPtr++];
  1250. fontChar.width = tft_cfont.font[tempPtr++];
  1251. fontChar.height = tft_cfont.font[tempPtr++];
  1252. fontChar.xOffset = tft_cfont.font[tempPtr++];
  1253. fontChar.xOffset = fontChar.xOffset < 0x80 ? fontChar.xOffset : -(0xFF - fontChar.xOffset);
  1254. fontChar.xDelta = tft_cfont.font[tempPtr++];
  1255. if (fontChar.charCode != 0xFF) {
  1256. if (fontChar.width != 0) {
  1257. // packed bits
  1258. tempPtr += (((fontChar.width * fontChar.height)-1) / 8) + 1;
  1259. }
  1260. }
  1261. }
  1262. printf("FONT TEST: W=%d H=%d last char: %d [%c]; length: %d\r\n", tft_cfont.max_x_size, tft_cfont.y_size, c, c, tempPtr);
  1263. }
  1264. */
  1265. //===================================================
  1266. void TFT_setFont(uint8_t font, const char *font_file)
  1267. {
  1268. tft_cfont.font = NULL;
  1269. if (font == FONT_7SEG) {
  1270. tft_cfont.bitmap = 2;
  1271. tft_cfont.x_size = 24;
  1272. tft_cfont.y_size = 6;
  1273. tft_cfont.offset = 0;
  1274. tft_cfont.color = tft_fg;
  1275. }
  1276. else {
  1277. if (font == USER_FONT) {
  1278. if (load_file_font(font_file, 0) != 0) tft_cfont.font = tft_DefaultFont;
  1279. else tft_cfont.font = userfont;
  1280. }
  1281. else if (font == DEJAVU18_FONT) tft_cfont.font = tft_Dejavu18;
  1282. else if (font == DEJAVU24_FONT) tft_cfont.font = tft_Dejavu24;
  1283. else if (font == UBUNTU16_FONT) tft_cfont.font = tft_Ubuntu16;
  1284. else if (font == COMIC24_FONT) tft_cfont.font = tft_Comic24;
  1285. else if (font == MINYA24_FONT) tft_cfont.font = tft_minya24;
  1286. else if (font == TOONEY32_FONT) tft_cfont.font = tft_tooney32;
  1287. else if (font == SMALL_FONT) tft_cfont.font = tft_SmallFont;
  1288. else if (font == DEF_SMALL_FONT) tft_cfont.font = tft_def_small;
  1289. else tft_cfont.font = tft_DefaultFont;
  1290. tft_cfont.bitmap = 1;
  1291. tft_cfont.x_size = tft_cfont.font[0];
  1292. tft_cfont.y_size = tft_cfont.font[1];
  1293. if (tft_cfont.x_size > 0) {
  1294. tft_cfont.offset = tft_cfont.font[2];
  1295. tft_cfont.numchars = tft_cfont.font[3];
  1296. tft_cfont.size = tft_cfont.x_size * tft_cfont.y_size * tft_cfont.numchars;
  1297. }
  1298. else {
  1299. tft_cfont.offset = 4;
  1300. getMaxWidthHeight();
  1301. }
  1302. //_testFont();
  1303. }
  1304. }
  1305. // -----------------------------------------------------------------------------------------
  1306. // Individual Proportional Font Character Format:
  1307. // -----------------------------------------------------------------------------------------
  1308. // Character Code
  1309. // yOffset (start Y of visible pixels)
  1310. // Width (width of the visible pixels)
  1311. // Height (height of the visible pixels)
  1312. // xOffset (start X of visible pixels)
  1313. // xDelta (the distance to move the cursor. Effective width of the character.)
  1314. // Data[n]
  1315. // -----------------------------------------------------------------------------------------
  1316. //---------------------------------------------------------------------------------------------
  1317. // Character drawing rectangle is (0, 0) (xDelta-1, tft_cfont.y_size-1)
  1318. // Character visible pixels rectangle is (xOffset, yOffset) (xOffset+Width-1, yOffset+Height-1)
  1319. //---------------------------------------------------------------------------------------------
  1320. // print non-rotated proportional character
  1321. // character is already in fontChar
  1322. //----------------------------------------------
  1323. static int printProportionalChar(int x, int y) {
  1324. uint8_t ch = 0;
  1325. int i, j, char_width;
  1326. char_width = ((fontChar.width > fontChar.xDelta) ? fontChar.width : fontChar.xDelta);
  1327. if ((tft_font_buffered_char) && (!tft_font_transparent)) {
  1328. int len, bufPos;
  1329. // === buffer Glyph data for faster sending ===
  1330. len = char_width * tft_cfont.y_size;
  1331. color_t *color_line = heap_caps_malloc(len*3, MALLOC_CAP_DMA);
  1332. if (color_line) {
  1333. // fill with background color
  1334. for (int n = 0; n < len; n++) {
  1335. color_line[n] = tft_bg;
  1336. }
  1337. // set character pixels to foreground color
  1338. uint8_t mask = 0x80;
  1339. for (j=0; j < fontChar.height; j++) {
  1340. for (i=0; i < fontChar.width; i++) {
  1341. if (((i + (j*fontChar.width)) % 8) == 0) {
  1342. mask = 0x80;
  1343. ch = tft_cfont.font[fontChar.dataPtr++];
  1344. }
  1345. if ((ch & mask) != 0) {
  1346. // visible pixel
  1347. bufPos = ((j + fontChar.adjYOffset) * char_width) + (fontChar.xOffset + i); // bufY + bufX
  1348. color_line[bufPos] = tft_fg;
  1349. /*
  1350. bufY = (j + fontChar.adjYOffset) * char_width;
  1351. bufX = fontChar.xOffset + i;
  1352. if ((bufX < 0) || (bufX > char_width)) {
  1353. printf("[%c] X ERR: %d\r\n", fontChar.charCode, bufX);
  1354. }
  1355. bufPos = bufY + bufX;
  1356. if ((bufPos < len) && (bufPos > 0)) color_line[bufPos] = tft_fg;
  1357. else printf("[%c] ERR: %d > %d W=%d H=%d bufX=%d bufY=%d X=%d Y=%d\r\n",
  1358. fontChar.charCode, bufPos, len, char_width, tft_cfont.y_size, bufX, bufY, fontChar.xOffset + i, j + fontChar.adjYOffset);
  1359. */
  1360. }
  1361. mask >>= 1;
  1362. }
  1363. }
  1364. // send to display in one transaction
  1365. disp_select();
  1366. send_data(x, y, x+char_width-1, y+tft_cfont.y_size-1, len, color_line);
  1367. disp_deselect();
  1368. free(color_line);
  1369. return char_width;
  1370. }
  1371. }
  1372. int cx, cy;
  1373. if (!tft_font_transparent) _fillRect(x, y, char_width+1, tft_cfont.y_size, tft_bg);
  1374. // draw Glyph
  1375. uint8_t mask = 0x80;
  1376. disp_select();
  1377. for (j=0; j < fontChar.height; j++) {
  1378. for (i=0; i < fontChar.width; i++) {
  1379. if (((i + (j*fontChar.width)) % 8) == 0) {
  1380. mask = 0x80;
  1381. ch = tft_cfont.font[fontChar.dataPtr++];
  1382. }
  1383. if ((ch & mask) !=0) {
  1384. cx = (uint16_t)(x+fontChar.xOffset+i);
  1385. cy = (uint16_t)(y+j+fontChar.adjYOffset);
  1386. _drawPixel(cx, cy, tft_fg, 0);
  1387. }
  1388. mask >>= 1;
  1389. }
  1390. }
  1391. disp_deselect();
  1392. return char_width;
  1393. }
  1394. // non-rotated fixed width character
  1395. //----------------------------------------------
  1396. static void printChar(uint8_t c, int x, int y) {
  1397. uint8_t i, j, ch, fz, mask;
  1398. uint16_t k, temp, cx, cy, len;
  1399. // fz = bytes per char row
  1400. fz = tft_cfont.x_size/8;
  1401. if (tft_cfont.x_size % 8) fz++;
  1402. // get character position in buffer
  1403. temp = ((c-tft_cfont.offset)*((fz)*tft_cfont.y_size))+4;
  1404. if ((tft_font_buffered_char) && (!tft_font_transparent)) {
  1405. // === buffer Glyph data for faster sending ===
  1406. len = tft_cfont.x_size * tft_cfont.y_size;
  1407. color_t *color_line = heap_caps_malloc(len*3, MALLOC_CAP_DMA);
  1408. if (color_line) {
  1409. // fill with background color
  1410. for (int n = 0; n < len; n++) {
  1411. color_line[n] = tft_bg;
  1412. }
  1413. // set character pixels to foreground color
  1414. for (j=0; j<tft_cfont.y_size; j++) {
  1415. for (k=0; k < fz; k++) {
  1416. ch = tft_cfont.font[temp+k];
  1417. mask=0x80;
  1418. for (i=0; i<8; i++) {
  1419. if ((ch & mask) !=0) color_line[(j*tft_cfont.x_size) + (i+(k*8))] = tft_fg;
  1420. mask >>= 1;
  1421. }
  1422. }
  1423. temp += (fz);
  1424. }
  1425. // send to display in one transaction
  1426. disp_select();
  1427. send_data(x, y, x+tft_cfont.x_size-1, y+tft_cfont.y_size-1, len, color_line);
  1428. disp_deselect();
  1429. free(color_line);
  1430. return;
  1431. }
  1432. }
  1433. if (!tft_font_transparent) _fillRect(x, y, tft_cfont.x_size, tft_cfont.y_size, tft_bg);
  1434. disp_select();
  1435. for (j=0; j<tft_cfont.y_size; j++) {
  1436. for (k=0; k < fz; k++) {
  1437. ch = tft_cfont.font[temp+k];
  1438. mask=0x80;
  1439. for (i=0; i<8; i++) {
  1440. if ((ch & mask) !=0) {
  1441. cx = (uint16_t)(x+i+(k*8));
  1442. cy = (uint16_t)(y+j);
  1443. _drawPixel(cx, cy, tft_fg, 0);
  1444. }
  1445. mask >>= 1;
  1446. }
  1447. }
  1448. temp += (fz);
  1449. }
  1450. disp_deselect();
  1451. }
  1452. // print rotated proportional character
  1453. // character is already in fontChar
  1454. //---------------------------------------------------
  1455. static int rotatePropChar(int x, int y, int offset) {
  1456. uint8_t ch = 0;
  1457. double radian = tft_font_rotate * DEG_TO_RAD;
  1458. float cos_radian = cos(radian);
  1459. float sin_radian = sin(radian);
  1460. uint8_t mask = 0x80;
  1461. disp_select();
  1462. for (int j=0; j < fontChar.height; j++) {
  1463. for (int i=0; i < fontChar.width; i++) {
  1464. if (((i + (j*fontChar.width)) % 8) == 0) {
  1465. mask = 0x80;
  1466. ch = tft_cfont.font[fontChar.dataPtr++];
  1467. }
  1468. int newX = (int)(x + (((offset + i) * cos_radian) - ((j+fontChar.adjYOffset)*sin_radian)));
  1469. int newY = (int)(y + (((j+fontChar.adjYOffset) * cos_radian) + ((offset + i) * sin_radian)));
  1470. if ((ch & mask) != 0) _drawPixel(newX,newY,tft_fg, 0);
  1471. else if (!tft_font_transparent) _drawPixel(newX,newY,tft_bg, 0);
  1472. mask >>= 1;
  1473. }
  1474. }
  1475. disp_deselect();
  1476. return fontChar.xDelta+1;
  1477. }
  1478. // rotated fixed width character
  1479. //--------------------------------------------------------
  1480. static void rotateChar(uint8_t c, int x, int y, int pos) {
  1481. uint8_t i,j,ch,fz,mask;
  1482. uint16_t temp;
  1483. int newx,newy;
  1484. double radian = tft_font_rotate*0.0175;
  1485. float cos_radian = cos(radian);
  1486. float sin_radian = sin(radian);
  1487. int zz;
  1488. if( tft_cfont.x_size < 8 ) fz = tft_cfont.x_size;
  1489. else fz = tft_cfont.x_size/8;
  1490. temp=((c-tft_cfont.offset)*((fz)*tft_cfont.y_size))+4;
  1491. disp_select();
  1492. for (j=0; j<tft_cfont.y_size; j++) {
  1493. for (zz=0; zz<(fz); zz++) {
  1494. ch = tft_cfont.font[temp+zz];
  1495. mask = 0x80;
  1496. for (i=0; i<8; i++) {
  1497. newx=(int)(x+(((i+(zz*8)+(pos*tft_cfont.x_size))*cos_radian)-((j)*sin_radian)));
  1498. newy=(int)(y+(((j)*cos_radian)+((i+(zz*8)+(pos*tft_cfont.x_size))*sin_radian)));
  1499. if ((ch & mask) != 0) _drawPixel(newx,newy,tft_fg, 0);
  1500. else if (!tft_font_transparent) _drawPixel(newx,newy,tft_bg, 0);
  1501. mask >>= 1;
  1502. }
  1503. }
  1504. temp+=(fz);
  1505. }
  1506. disp_deselect();
  1507. // calculate x,y for the next char
  1508. tft_x = (int)(x + ((pos+1) * tft_cfont.x_size * cos_radian));
  1509. tft_y = (int)(y + ((pos+1) * tft_cfont.x_size * sin_radian));
  1510. }
  1511. //----------------------
  1512. static int _7seg_width()
  1513. {
  1514. return (2 * (2 * tft_cfont.y_size + 1)) + tft_cfont.x_size;
  1515. }
  1516. //-----------------------
  1517. static int _7seg_height()
  1518. {
  1519. return (3 * (2 * tft_cfont.y_size + 1)) + (2 * tft_cfont.x_size);
  1520. }
  1521. // Returns the string width in pixels.
  1522. // Useful for positions strings on the screen.
  1523. //===============================
  1524. int TFT_getStringWidth(char* str)
  1525. {
  1526. int strWidth = 0;
  1527. if (tft_cfont.bitmap == 2) strWidth = ((_7seg_width()+2) * strlen(str)) - 2; // 7-segment font
  1528. else if (tft_cfont.x_size != 0) strWidth = strlen(str) * tft_cfont.x_size; // fixed width font
  1529. else {
  1530. // calculate the width of the string of proportional characters
  1531. char* tempStrptr = str;
  1532. while (*tempStrptr != 0) {
  1533. if (getCharPtr(*tempStrptr++)) {
  1534. strWidth += (((fontChar.width > fontChar.xDelta) ? fontChar.width : fontChar.xDelta) + 1);
  1535. }
  1536. }
  1537. strWidth--;
  1538. }
  1539. return strWidth;
  1540. }
  1541. //===============================================
  1542. void TFT_clearStringRect(int x, int y, char *str)
  1543. {
  1544. int w = TFT_getStringWidth(str);
  1545. int h = TFT_getfontheight();
  1546. TFT_fillRect(x+tft_dispWin.x1, y+tft_dispWin.y1, w, h, tft_bg);
  1547. }
  1548. //==============================================================================
  1549. /**
  1550. * bit-encoded bar position of all digits' bcd segments
  1551. *
  1552. * 6
  1553. * +-----+
  1554. * 3 | . | 2
  1555. * +--5--+
  1556. * 1 | . | 0
  1557. * +--.--+
  1558. * 4
  1559. */
  1560. static const uint16_t font_bcd[] = {
  1561. 0x200, // 0010 0000 0000 // -
  1562. 0x080, // 0000 1000 0000 // .
  1563. 0x06C, // 0100 0110 1100 // /, degree
  1564. 0x05f, // 0000 0101 1111, // 0
  1565. 0x005, // 0000 0000 0101, // 1
  1566. 0x076, // 0000 0111 0110, // 2
  1567. 0x075, // 0000 0111 0101, // 3
  1568. 0x02d, // 0000 0010 1101, // 4
  1569. 0x079, // 0000 0111 1001, // 5
  1570. 0x07b, // 0000 0111 1011, // 6
  1571. 0x045, // 0000 0100 0101, // 7
  1572. 0x07f, // 0000 0111 1111, // 8
  1573. 0x07d, // 0000 0111 1101 // 9
  1574. 0x900 // 1001 0000 0000 // :
  1575. };
  1576. //-----------------------------------------------------------------------------------------------
  1577. static void barVert(int16_t x, int16_t y, int16_t w, int16_t l, color_t color, color_t outline) {
  1578. _fillTriangle(x+1, y+2*w, x+w, y+w+1, x+2*w-1, y+2*w, color);
  1579. _fillTriangle(x+1, y+2*w+l+1, x+w, y+3*w+l, x+2*w-1, y+2*w+l+1, color);
  1580. _fillRect(x, y+2*w+1, 2*w+1, l, color);
  1581. if (tft_cfont.offset) {
  1582. _drawTriangle(x+1, y+2*w, x+w, y+w+1, x+2*w-1, y+2*w, outline);
  1583. _drawTriangle(x+1, y+2*w+l+1, x+w, y+3*w+l, x+2*w-1, y+2*w+l+1, outline);
  1584. _drawRect(x, y+2*w+1, 2*w+1, l, outline);
  1585. }
  1586. }
  1587. //----------------------------------------------------------------------------------------------
  1588. static void barHor(int16_t x, int16_t y, int16_t w, int16_t l, color_t color, color_t outline) {
  1589. _fillTriangle(x+2*w, y+2*w-1, x+w+1, y+w, x+2*w, y+1, color);
  1590. _fillTriangle(x+2*w+l+1, y+2*w-1, x+3*w+l, y+w, x+2*w+l+1, y+1, color);
  1591. _fillRect(x+2*w+1, y, l, 2*w+1, color);
  1592. if (tft_cfont.offset) {
  1593. _drawTriangle(x+2*w, y+2*w-1, x+w+1, y+w, x+2*w, y+1, outline);
  1594. _drawTriangle(x+2*w+l+1, y+2*w-1, x+3*w+l, y+w, x+2*w+l+1, y+1, outline);
  1595. _drawRect(x+2*w+1, y, l, 2*w+1, outline);
  1596. }
  1597. }
  1598. //--------------------------------------------------------------------------------------------
  1599. static void _draw7seg(int16_t x, int16_t y, int8_t num, int16_t w, int16_t l, color_t color) {
  1600. /* TODO: clipping */
  1601. if (num < 0x2D || num > 0x3A) return;
  1602. int16_t c = font_bcd[num-0x2D];
  1603. int16_t d = 2*w+l+1;
  1604. // === Clear unused segments ===
  1605. if (!(c & 0x001)) barVert(x+d, y+d, w, l, tft_bg, tft_bg);
  1606. if (!(c & 0x002)) barVert(x, y+d, w, l, tft_bg, tft_bg);
  1607. if (!(c & 0x004)) barVert(x+d, y, w, l, tft_bg, tft_bg);
  1608. if (!(c & 0x008)) barVert(x, y, w, l, tft_bg, tft_bg);
  1609. if (!(c & 0x010)) barHor(x, y+2*d, w, l, tft_bg, tft_bg);
  1610. if (!(c & 0x020)) barHor(x, y+d, w, l, tft_bg, tft_bg);
  1611. if (!(c & 0x040)) barHor(x, y, w, l, tft_bg, tft_bg);
  1612. if (!(c & 0x080)) {
  1613. // low point
  1614. _fillRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, tft_bg);
  1615. if (tft_cfont.offset) _drawRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, tft_bg);
  1616. }
  1617. if (!(c & 0x100)) {
  1618. // down middle point
  1619. _fillRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, tft_bg);
  1620. if (tft_cfont.offset) _drawRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, tft_bg);
  1621. }
  1622. if (!(c & 0x800)) {
  1623. // up middle point
  1624. _fillRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, tft_bg);
  1625. if (tft_cfont.offset) _drawRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, tft_bg);
  1626. }
  1627. if (!(c & 0x200)) {
  1628. // middle, minus
  1629. _fillRect(x+2*w+1, y+d, l, 2*w+1, tft_bg);
  1630. if (tft_cfont.offset) _drawRect(x+2*w+1, y+d, l, 2*w+1, tft_bg);
  1631. }
  1632. // === Draw used segments ===
  1633. if (c & 0x001) barVert(x+d, y+d, w, l, color, tft_cfont.color); // down right
  1634. if (c & 0x002) barVert(x, y+d, w, l, color, tft_cfont.color); // down left
  1635. if (c & 0x004) barVert(x+d, y, w, l, color, tft_cfont.color); // up right
  1636. if (c & 0x008) barVert(x, y, w, l, color, tft_cfont.color); // up left
  1637. if (c & 0x010) barHor(x, y+2*d, w, l, color, tft_cfont.color); // down
  1638. if (c & 0x020) barHor(x, y+d, w, l, color, tft_cfont.color); // middle
  1639. if (c & 0x040) barHor(x, y, w, l, color, tft_cfont.color); // up
  1640. if (c & 0x080) {
  1641. // low point
  1642. _fillRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, color);
  1643. if (tft_cfont.offset) _drawRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, tft_cfont.color);
  1644. }
  1645. if (c & 0x100) {
  1646. // down middle point
  1647. _fillRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, color);
  1648. if (tft_cfont.offset) _drawRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, tft_cfont.color);
  1649. }
  1650. if (c & 0x800) {
  1651. // up middle point
  1652. _fillRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, color);
  1653. if (tft_cfont.offset) _drawRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, tft_cfont.color);
  1654. }
  1655. if (c & 0x200) {
  1656. // middle, minus
  1657. _fillRect(x+2*w+1, y+d, l, 2*w+1, color);
  1658. if (tft_cfont.offset) _drawRect(x+2*w+1, y+d, l, 2*w+1, tft_cfont.color);
  1659. }
  1660. }
  1661. //==============================================================================
  1662. //======================================
  1663. void TFT_print(char *st, int x, int y) {
  1664. int stl, i, tmpw, tmph, fh;
  1665. uint8_t ch;
  1666. if (tft_cfont.bitmap == 0) return; // wrong font selected
  1667. // ** Rotated strings cannot be aligned
  1668. if ((tft_font_rotate != 0) && ((x <= CENTER) || (y <= CENTER))) return;
  1669. if ((x < LASTX) || (tft_font_rotate == 0)) TFT_OFFSET = 0;
  1670. if ((x >= LASTX) && (x < LASTY)) x = tft_x + (x-LASTX);
  1671. else if (x > CENTER) x += tft_dispWin.x1;
  1672. if (y >= LASTY) y = tft_y + (y-LASTY);
  1673. else if (y > CENTER) y += tft_dispWin.y1;
  1674. // ** Get number of characters in string to print
  1675. stl = strlen(st);
  1676. // ** Calculate CENTER, RIGHT or BOTTOM position
  1677. tmpw = TFT_getStringWidth(st); // string width in pixels
  1678. fh = tft_cfont.y_size; // font height
  1679. if ((tft_cfont.x_size != 0) && (tft_cfont.bitmap == 2)) {
  1680. // 7-segment font
  1681. fh = (3 * (2 * tft_cfont.y_size + 1)) + (2 * tft_cfont.x_size); // 7-seg character height
  1682. }
  1683. if (x == RIGHT) x = tft_dispWin.x2 - tmpw + tft_dispWin.x1;
  1684. else if (x == CENTER) x = (((tft_dispWin.x2 - tft_dispWin.x1 + 1) - tmpw) / 2) + tft_dispWin.x1;
  1685. if (y == BOTTOM) y = tft_dispWin.y2 - fh + tft_dispWin.y1;
  1686. else if (y==CENTER) y = (((tft_dispWin.y2 - tft_dispWin.y1 + 1) - (fh/2)) / 2) + tft_dispWin.y1;
  1687. if (x < tft_dispWin.x1) x = tft_dispWin.x1;
  1688. if (y < tft_dispWin.y1) y = tft_dispWin.y1;
  1689. if ((x > tft_dispWin.x2) || (y > tft_dispWin.y2)) return;
  1690. tft_x = x;
  1691. tft_y = y;
  1692. // ** Adjust y position
  1693. tmph = tft_cfont.y_size; // font height
  1694. // for non-proportional fonts, char width is the same for all chars
  1695. tmpw = tft_cfont.x_size;
  1696. if (tft_cfont.x_size != 0) {
  1697. if (tft_cfont.bitmap == 2) { // 7-segment font
  1698. tmpw = _7seg_width(); // character width
  1699. tmph = _7seg_height(); // character height
  1700. }
  1701. }
  1702. else TFT_OFFSET = 0; // fixed font; offset not needed
  1703. if ((tft_y + tmph - 1) > tft_dispWin.y2) return;
  1704. int offset = TFT_OFFSET;
  1705. for (i=0; i<stl; i++) {
  1706. ch = st[i]; // get string character
  1707. if (ch == 0x0D) { // === '\r', erase to eol ====
  1708. if ((!tft_font_transparent) && (tft_font_rotate==0)) _fillRect(tft_x, tft_y, tft_dispWin.x2+1-tft_x, tmph, tft_bg);
  1709. }
  1710. else if (ch == 0x0A) { // ==== '\n', new line ====
  1711. if (tft_cfont.bitmap == 1) {
  1712. tft_y += tmph + tft_font_line_space;
  1713. if (tft_y > (tft_dispWin.y2-tmph)) break;
  1714. tft_x = tft_dispWin.x1;
  1715. }
  1716. }
  1717. else { // ==== other characters ====
  1718. if (tft_cfont.x_size == 0) {
  1719. // for proportional font get character data to 'fontChar'
  1720. if (getCharPtr(ch)) tmpw = fontChar.xDelta;
  1721. else continue;
  1722. }
  1723. // check if character can be displayed in the current line
  1724. if ((tft_x+tmpw) > (tft_dispWin.x2)) {
  1725. if (tft_text_wrap == 0) break;
  1726. tft_y += tmph + tft_font_line_space;
  1727. if (tft_y > (tft_dispWin.y2-tmph)) break;
  1728. tft_x = tft_dispWin.x1;
  1729. }
  1730. // Let's print the character
  1731. if (tft_cfont.x_size == 0) {
  1732. // == proportional font
  1733. if (tft_font_rotate == 0) tft_x += printProportionalChar(tft_x, tft_y) + 1;
  1734. else {
  1735. // rotated proportional font
  1736. offset += rotatePropChar(x, y, offset);
  1737. TFT_OFFSET = offset;
  1738. }
  1739. }
  1740. else {
  1741. if (tft_cfont.bitmap == 1) {
  1742. // == fixed font
  1743. if ((ch < tft_cfont.offset) || ((ch-tft_cfont.offset) > tft_cfont.numchars)) ch = tft_cfont.offset;
  1744. if (tft_font_rotate == 0) {
  1745. printChar(ch, tft_x, tft_y);
  1746. tft_x += tmpw;
  1747. }
  1748. else rotateChar(ch, x, y, i);
  1749. }
  1750. else if (tft_cfont.bitmap == 2) {
  1751. // == 7-segment font ==
  1752. _draw7seg(tft_x, tft_y, ch, tft_cfont.y_size, tft_cfont.x_size, tft_fg);
  1753. tft_x += (tmpw + 2);
  1754. }
  1755. }
  1756. }
  1757. }
  1758. }
  1759. // ================ Service functions ==========================================
  1760. // Change the screen rotation.
  1761. // Input: m new rotation value (0 to 3)
  1762. //=================================
  1763. void TFT_setRotation(uint8_t rot) {
  1764. if (rot > 3) {
  1765. uint8_t madctl = (rot & 0xF8); // for testing, manually set MADCTL register
  1766. if (disp_select() == ESP_OK) {
  1767. disp_spi_transfer_cmd_data(TFT_MADCTL, &madctl, 1);
  1768. disp_deselect();
  1769. }
  1770. }
  1771. else {
  1772. tft_orientation = rot;
  1773. _tft_setRotation(rot);
  1774. }
  1775. tft_dispWin.x1 = TFT_STATIC_X_OFFSET;
  1776. tft_dispWin.y1 = TFT_STATIC_Y_OFFSET;
  1777. tft_dispWin.x2 = tft_width + TFT_STATIC_X_OFFSET -1;
  1778. tft_dispWin.y2 = tft_height + TFT_STATIC_Y_OFFSET -1;
  1779. TFT_fillScreen(tft_bg);
  1780. }
  1781. // Send the command to invert all of the colors.
  1782. // Input: i 0 to disable inversion; non-zero to enable inversion
  1783. //==========================================
  1784. void TFT_invertDisplay(const uint8_t mode) {
  1785. if (disp_select() == ESP_OK) {
  1786. if ( mode == INVERT_ON ) disp_spi_transfer_cmd(TFT_INVONN);
  1787. else disp_spi_transfer_cmd(TFT_INVOFF);
  1788. disp_deselect();
  1789. }
  1790. }
  1791. // Select gamma curve
  1792. // Input: gamma = 0~3
  1793. //==================================
  1794. void TFT_setGammaCurve(uint8_t gm) {
  1795. uint8_t gamma_curve = (uint8_t)1 << (gm & (uint8_t)0x03);
  1796. if (disp_select() == ESP_OK) {
  1797. disp_spi_transfer_cmd_data(TFT_CMD_GAMMASET, &gamma_curve, 1);
  1798. disp_deselect();
  1799. }
  1800. }
  1801. //===========================================================
  1802. color_t HSBtoRGB(float _hue, float _sat, float _brightness) {
  1803. float red = 0.0;
  1804. float green = 0.0;
  1805. float blue = 0.0;
  1806. if (_sat == 0.0) {
  1807. red = _brightness;
  1808. green = _brightness;
  1809. blue = _brightness;
  1810. } else {
  1811. if (_hue == 360.0) {
  1812. _hue = 0;
  1813. }
  1814. int slice = (int)(_hue / 60.0);
  1815. float hue_frac = (_hue / 60.0) - slice;
  1816. float aa = _brightness * (1.0 - _sat);
  1817. float bb = _brightness * (1.0 - _sat * hue_frac);
  1818. float cc = _brightness * (1.0 - _sat * (1.0 - hue_frac));
  1819. switch(slice) {
  1820. case 0:
  1821. red = _brightness;
  1822. green = cc;
  1823. blue = aa;
  1824. break;
  1825. case 1:
  1826. red = bb;
  1827. green = _brightness;
  1828. blue = aa;
  1829. break;
  1830. case 2:
  1831. red = aa;
  1832. green = _brightness;
  1833. blue = cc;
  1834. break;
  1835. case 3:
  1836. red = aa;
  1837. green = bb;
  1838. blue = _brightness;
  1839. break;
  1840. case 4:
  1841. red = cc;
  1842. green = aa;
  1843. blue = _brightness;
  1844. break;
  1845. case 5:
  1846. red = _brightness;
  1847. green = aa;
  1848. blue = bb;
  1849. break;
  1850. default:
  1851. red = 0.0;
  1852. green = 0.0;
  1853. blue = 0.0;
  1854. break;
  1855. }
  1856. }
  1857. color_t color;
  1858. color.r = ((uint8_t)(red * 255.0)) & 0xFC;
  1859. color.g = ((uint8_t)(green * 255.0)) & 0xFC;
  1860. color.b = ((uint8_t)(blue * 255.0)) & 0xFC;
  1861. return color;
  1862. }
  1863. //=====================================================================
  1864. void TFT_setclipwin(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
  1865. {
  1866. tft_dispWin.x1 = x1 + TFT_STATIC_X_OFFSET;
  1867. tft_dispWin.y1 = y1 + TFT_STATIC_Y_OFFSET;
  1868. tft_dispWin.x2 = x2 + TFT_STATIC_X_OFFSET;
  1869. tft_dispWin.y2 = y2 + TFT_STATIC_Y_OFFSET;
  1870. if (tft_dispWin.x2 >= tft_width + TFT_STATIC_X_OFFSET) tft_dispWin.x2 = tft_width + TFT_STATIC_X_OFFSET -1;
  1871. if (tft_dispWin.y2 >= tft_height + TFT_STATIC_Y_OFFSET) tft_dispWin.y2 = tft_height + TFT_STATIC_Y_OFFSET -1;
  1872. if (tft_dispWin.x1 > tft_dispWin.x2) tft_dispWin.x1 = tft_dispWin.x2;
  1873. if (tft_dispWin.y1 > tft_dispWin.y2) tft_dispWin.y1 = tft_dispWin.y2;
  1874. }
  1875. //=====================
  1876. void TFT_resetclipwin()
  1877. {
  1878. tft_dispWin.x2 = tft_width + TFT_STATIC_X_OFFSET -1;
  1879. tft_dispWin.y2 = tft_height + TFT_STATIC_Y_OFFSET -1;
  1880. tft_dispWin.x1 = TFT_STATIC_X_OFFSET;
  1881. tft_dispWin.y1 = TFT_STATIC_Y_OFFSET;
  1882. }
  1883. //==========================================================================
  1884. void set_7seg_font_atrib(uint8_t l, uint8_t w, int outline, color_t color) {
  1885. if (tft_cfont.bitmap != 2) return;
  1886. if (l < 6) l = 6;
  1887. if (l > 40) l = 40;
  1888. if (w < 1) w = 1;
  1889. if (w > (l/2)) w = l/2;
  1890. if (w > 12) w = 12;
  1891. tft_cfont.x_size = l;
  1892. tft_cfont.y_size = w;
  1893. tft_cfont.offset = outline;
  1894. tft_cfont.color = color;
  1895. }
  1896. //==========================================
  1897. int TFT_getfontsize(int *width, int* height)
  1898. {
  1899. if (tft_cfont.bitmap == 1) {
  1900. if (tft_cfont.x_size != 0) *width = tft_cfont.x_size; // fixed width font
  1901. else *width = tft_cfont.max_x_size; // proportional font
  1902. *height = tft_cfont.y_size;
  1903. }
  1904. else if (tft_cfont.bitmap == 2) {
  1905. // 7-segment font
  1906. *width = _7seg_width();
  1907. *height = _7seg_height();
  1908. }
  1909. else {
  1910. *width = 0;
  1911. *height = 0;
  1912. return 0;
  1913. }
  1914. return 1;
  1915. }
  1916. //=====================
  1917. int TFT_getfontheight()
  1918. {
  1919. if (tft_cfont.bitmap == 1) return tft_cfont.y_size; // Bitmap font
  1920. else if (tft_cfont.bitmap == 2) return _7seg_height(); // 7-segment font
  1921. return 0;
  1922. }
  1923. //====================
  1924. void TFT_saveClipWin()
  1925. {
  1926. dispWinTemp.x1 = tft_dispWin.x1;
  1927. dispWinTemp.y1 = tft_dispWin.y1;
  1928. dispWinTemp.x2 = tft_dispWin.x2;
  1929. dispWinTemp.y2 = tft_dispWin.y2;
  1930. }
  1931. //=======================
  1932. void TFT_restoreClipWin()
  1933. {
  1934. tft_dispWin.x1 = dispWinTemp.x1;
  1935. tft_dispWin.y1 = dispWinTemp.y1;
  1936. tft_dispWin.x2 = dispWinTemp.x2;
  1937. tft_dispWin.y2 = dispWinTemp.y2;
  1938. }
  1939. // ================ JPG SUPPORT ================================================
  1940. // User defined device identifier
  1941. typedef struct {
  1942. FILE *fhndl; // File handler for input function
  1943. int x; // image top left point X position
  1944. int y; // image top left point Y position
  1945. uint8_t *membuff; // memory buffer containing the image
  1946. uint32_t bufsize; // size of the memory buffer
  1947. uint32_t bufptr; // memory buffer current position
  1948. color_t *linbuf[2]; // memory buffer used for display output
  1949. uint8_t linbuf_idx;
  1950. } JPGIODEV;
  1951. // User defined call-back function to input JPEG data from file
  1952. //---------------------
  1953. static UINT tjd_input (
  1954. JDEC* jd, // Decompression object
  1955. BYTE* buff, // Pointer to the read buffer (NULL:skip)
  1956. UINT nd // Number of bytes to read/skip from input stream
  1957. )
  1958. {
  1959. int rb = 0;
  1960. // Device identifier for the session (5th argument of jd_prepare function)
  1961. JPGIODEV *dev = (JPGIODEV*)jd->device;
  1962. if (buff) { // Read nd bytes from the input strem
  1963. rb = fread(buff, 1, nd, dev->fhndl);
  1964. return rb; // Returns actual number of bytes read
  1965. }
  1966. else { // Remove nd bytes from the input stream
  1967. if (fseek(dev->fhndl, nd, SEEK_CUR) >= 0) return nd;
  1968. else return 0;
  1969. }
  1970. }
  1971. // User defined call-back function to input JPEG data from memory buffer
  1972. //-------------------------
  1973. static UINT tjd_buf_input (
  1974. JDEC* jd, // Decompression object
  1975. BYTE* buff, // Pointer to the read buffer (NULL:skip)
  1976. UINT nd // Number of bytes to read/skip from input stream
  1977. )
  1978. {
  1979. // Device identifier for the session (5th argument of jd_prepare function)
  1980. JPGIODEV *dev = (JPGIODEV*)jd->device;
  1981. if (!dev->membuff) return 0;
  1982. if (dev->bufptr >= (dev->bufsize + 2)) return 0; // end of stream
  1983. if ((dev->bufptr + nd) > (dev->bufsize + 2)) nd = (dev->bufsize + 2) - dev->bufptr;
  1984. if (buff) { // Read nd bytes from the input strem
  1985. memcpy(buff, dev->membuff + dev->bufptr, nd);
  1986. dev->bufptr += nd;
  1987. return nd; // Returns number of bytes read
  1988. }
  1989. else { // Remove nd bytes from the input stream
  1990. dev->bufptr += nd;
  1991. return nd;
  1992. }
  1993. }
  1994. // User defined call-back function to output RGB bitmap to display device
  1995. //----------------------
  1996. static UINT tjd_output (
  1997. JDEC* jd, // Decompression object of current session
  1998. void* bitmap, // Bitmap data to be output
  1999. JRECT* rect // Rectangular region to output
  2000. )
  2001. {
  2002. // Device identifier for the session (5th argument of jd_prepare function)
  2003. JPGIODEV *dev = (JPGIODEV*)jd->device;
  2004. // ** Put the rectangular into the display device **
  2005. int x;
  2006. int y;
  2007. int dleft, dtop, dright, dbottom;
  2008. BYTE *src = (BYTE*)bitmap;
  2009. int left = rect->left + dev->x;
  2010. int top = rect->top + dev->y;
  2011. int right = rect->right + dev->x;
  2012. int bottom = rect->bottom + dev->y;
  2013. if ((left > tft_dispWin.x2) || (top > tft_dispWin.y2)) return 1; // out of screen area, return
  2014. if ((right < tft_dispWin.x1) || (bottom < tft_dispWin.y1)) return 1;// out of screen area, return
  2015. if (left < tft_dispWin.x1) dleft = tft_dispWin.x1;
  2016. else dleft = left;
  2017. if (top < tft_dispWin.y1) dtop = tft_dispWin.y1;
  2018. else dtop = top;
  2019. if (right > tft_dispWin.x2) dright = tft_dispWin.x2;
  2020. else dright = right;
  2021. if (bottom > tft_dispWin.y2) dbottom = tft_dispWin.y2;
  2022. else dbottom = bottom;
  2023. if ((dleft > tft_dispWin.x2) || (dtop > tft_dispWin.y2)) return 1; // out of screen area, return
  2024. if ((dright < tft_dispWin.x1) || (dbottom < tft_dispWin.y1)) return 1; // out of screen area, return
  2025. uint32_t len = ((dright-dleft+1) * (dbottom-dtop+1)); // calculate length of data
  2026. if ((len > 0) && (len <= JPG_IMAGE_LINE_BUF_SIZE)) {
  2027. uint8_t *dest = (uint8_t *)(dev->linbuf[dev->linbuf_idx]);
  2028. for (y = top; y <= bottom; y++) {
  2029. for (x = left; x <= right; x++) {
  2030. // Clip to display area
  2031. if ((x >= dleft) && (y >= dtop) && (x <= dright) && (y <= dbottom)) {
  2032. *dest++ = (*src++) & 0xFC;
  2033. *dest++ = (*src++) & 0xFC;
  2034. *dest++ = (*src++) & 0xFC;
  2035. }
  2036. else src += 3; // skip
  2037. }
  2038. }
  2039. wait_trans_finish(1);
  2040. send_data(dleft, dtop, dright, dbottom, len, dev->linbuf[dev->linbuf_idx]);
  2041. dev->linbuf_idx = ((dev->linbuf_idx + 1) & 1);
  2042. }
  2043. else {
  2044. wait_trans_finish(1);
  2045. printf("Data size error: %d jpg: (%d,%d,%d,%d) disp: (%d,%d,%d,%d)\r\n", len, left,top,right,bottom, dleft,dtop,dright,dbottom);
  2046. return 0; // stop decompression
  2047. }
  2048. return 1; // Continue to decompression
  2049. }
  2050. // tft.jpgimage(X, Y, scale, file_name, buf, size]
  2051. // X & Y can be < 0 !
  2052. //==================================================================================
  2053. void TFT_jpg_image(int x, int y, uint8_t scale, char *fname, uint8_t *buf, int size)
  2054. {
  2055. JPGIODEV dev;
  2056. struct stat sb;
  2057. char *work = NULL; // Pointer to the working buffer (must be 4-byte aligned)
  2058. UINT sz_work = 3800; // Size of the working buffer (must be power of 2)
  2059. JDEC jd; // Decompression object (70 bytes)
  2060. JRESULT rc;
  2061. dev.linbuf[0] = NULL;
  2062. dev.linbuf[1] = NULL;
  2063. dev.linbuf_idx = 0;
  2064. dev.fhndl = NULL;
  2065. if (fname == NULL) {
  2066. // image from buffer
  2067. dev.membuff = buf;
  2068. dev.bufsize = size;
  2069. dev.bufptr = 0;
  2070. }
  2071. else {
  2072. // image from file
  2073. dev.membuff = NULL;
  2074. dev.bufsize = 0;
  2075. dev.bufptr = 0;
  2076. if (stat(fname, &sb) != 0) {
  2077. if (tft_image_debug) printf("File error: %ss\r\n", strerror(errno));
  2078. goto exit;
  2079. }
  2080. dev.fhndl = fopen(fname, "r");
  2081. if (!dev.fhndl) {
  2082. if (tft_image_debug) printf("Error opening file: %s\r\n", strerror(errno));
  2083. goto exit;
  2084. }
  2085. }
  2086. if (scale > 3) scale = 3;
  2087. work = malloc(sz_work);
  2088. if (work) {
  2089. if (dev.membuff) rc = jd_prepare(&jd, tjd_buf_input, (void *)work, sz_work, &dev);
  2090. else rc = jd_prepare(&jd, tjd_input, (void *)work, sz_work, &dev);
  2091. if (rc == JDR_OK) {
  2092. if (x == CENTER) x = ((tft_dispWin.x2 - tft_dispWin.x1 + 1 - (int)(jd.width >> scale)) / 2) + tft_dispWin.x1;
  2093. else if (x == RIGHT) x = tft_dispWin.x2 + 1 - (int)(jd.width >> scale);
  2094. if (y == CENTER) y = ((tft_dispWin.y2 - tft_dispWin.y1 + 1 - (int)(jd.height >> scale)) / 2) + tft_dispWin.y1;
  2095. else if (y == BOTTOM) y = tft_dispWin.y2 + 1 - (int)(jd.height >> scale);
  2096. if (x < ((tft_dispWin.x2-1) * -1)) x = (tft_dispWin.x2-1) * -1;
  2097. if (y < ((tft_dispWin.y2-1)) * -1) y = (tft_dispWin.y2-1) * -1;
  2098. if (x > (tft_dispWin.x2-1)) x = tft_dispWin.x2 - 1;
  2099. if (y > (tft_dispWin.y2-1)) y = tft_dispWin.y2-1;
  2100. dev.x = x;
  2101. dev.y = y;
  2102. dev.linbuf[0] = heap_caps_malloc(JPG_IMAGE_LINE_BUF_SIZE*3, MALLOC_CAP_DMA);
  2103. if (dev.linbuf[0] == NULL) {
  2104. if (tft_image_debug) printf("Error allocating line buffer #0\r\n");
  2105. goto exit;
  2106. }
  2107. dev.linbuf[1] = heap_caps_malloc(JPG_IMAGE_LINE_BUF_SIZE*3, MALLOC_CAP_DMA);
  2108. if (dev.linbuf[1] == NULL) {
  2109. if (tft_image_debug) printf("Error allocating line buffer #1\r\n");
  2110. goto exit;
  2111. }
  2112. // Start to decode the JPEG file
  2113. disp_select();
  2114. rc = jd_decomp(&jd, tjd_output, scale);
  2115. disp_deselect();
  2116. if (rc != JDR_OK) {
  2117. if (tft_image_debug) printf("jpg decompression error %d\r\n", rc);
  2118. }
  2119. if (tft_image_debug) printf("Jpg size: %dx%d, position; %d,%d, scale: %d, bytes used: %d\r\n", jd.width, jd.height, x, y, scale, jd.sz_pool);
  2120. }
  2121. else {
  2122. if (tft_image_debug) printf("jpg prepare error %d\r\n", rc);
  2123. }
  2124. }
  2125. else {
  2126. if (tft_image_debug) printf("work buffer allocation error\r\n");
  2127. }
  2128. exit:
  2129. if (work) free(work); // free work buffer
  2130. if (dev.linbuf[0]) free(dev.linbuf[0]);
  2131. if (dev.linbuf[1]) free(dev.linbuf[1]);
  2132. if (dev.fhndl) fclose(dev.fhndl); // close input file
  2133. }
  2134. //====================================================================================
  2135. int TFT_bmp_image(int x, int y, uint8_t scale, char *fname, uint8_t *imgbuf, int size)
  2136. {
  2137. FILE *fhndl = NULL;
  2138. struct stat sb;
  2139. int i, err=0;
  2140. int img_xsize, img_ysize, img_xstart, img_xlen, img_ystart, img_ylen;
  2141. int img_pos, img_pix_pos, scan_lines, rd_len;
  2142. uint8_t tmpc;
  2143. uint16_t wtemp;
  2144. uint32_t temp;
  2145. int disp_xstart, disp_xend, disp_ystart, disp_yend;
  2146. uint8_t buf[56];
  2147. char err_buf[64];
  2148. uint8_t *line_buf[2] = {NULL,NULL};
  2149. uint8_t lb_idx = 0;
  2150. uint8_t *scale_buf = NULL;
  2151. uint8_t scale_pix;
  2152. uint16_t co[3] = {0,0,0}; // RGB sum
  2153. uint8_t npix;
  2154. if (scale > 7) scale = 7;
  2155. scale_pix = scale+1; // scale factor ( 1~8 )
  2156. if (fname) {
  2157. // * File name is given, reading image from file
  2158. if (stat(fname, &sb) != 0) {
  2159. sprintf(err_buf, "opening file");
  2160. err = -1;
  2161. goto exit;
  2162. }
  2163. size = sb.st_size;
  2164. fhndl = fopen(fname, "r");
  2165. if (!fhndl) {
  2166. sprintf(err_buf, "opening file");
  2167. err = -2;
  2168. goto exit;
  2169. }
  2170. i = fread(buf, 1, 54, fhndl); // read header
  2171. }
  2172. else {
  2173. // * Reading image from buffer
  2174. if ((imgbuf) && (size > 54)) {
  2175. memcpy(buf, imgbuf, 54);
  2176. i = 54;
  2177. }
  2178. else i = 0;
  2179. }
  2180. sprintf(err_buf, "reading header");
  2181. if (i != 54) {err = -3; goto exit;}
  2182. // ** Check image header and get image properties
  2183. if ((buf[0] != 'B') || (buf[1] != 'M')) {err=-4; goto exit;} // accept only images with 'BM' id
  2184. memcpy(&temp, buf+2, 4); // file size
  2185. if (temp != size) {err=-5; goto exit;}
  2186. memcpy(&img_pos, buf+10, 4); // start of pixel data
  2187. memcpy(&temp, buf+14, 4); // BMP header size
  2188. if (temp != 40) {err=-6; goto exit;}
  2189. memcpy(&wtemp, buf+26, 2); // the number of color planes
  2190. if (wtemp != 1) {err=-7; goto exit;}
  2191. memcpy(&wtemp, buf+28, 2); // the number of bits per pixel
  2192. if (wtemp != 24) {err=-8; goto exit;}
  2193. memcpy(&temp, buf+30, 4); // the compression method being used
  2194. if (temp != 0) {err=-9; goto exit;}
  2195. memcpy(&img_xsize, buf+18, 4); // the bitmap width in pixels
  2196. memcpy(&img_ysize, buf+22, 4); // the bitmap height in pixels
  2197. // * scale image dimensions
  2198. img_xlen = img_xsize / scale_pix; // image display horizontal size
  2199. img_ylen = img_ysize / scale_pix; // image display vertical size
  2200. if (x == CENTER) x = ((tft_dispWin.x2 - tft_dispWin.x1 + 1 - img_xlen) / 2) + tft_dispWin.x1;
  2201. else if (x == RIGHT) x = tft_dispWin.x2 + 1 - img_xlen;
  2202. if (y == CENTER) y = ((tft_dispWin.y2 - tft_dispWin.y1 + 1 - img_ylen) / 2) + tft_dispWin.y1;
  2203. else if (y == BOTTOM) y = tft_dispWin.y2 + 1 - img_ylen;
  2204. if ((x < ((tft_dispWin.x2 + 1) * -1)) || (x > (tft_dispWin.x2 + 1)) || (y < ((tft_dispWin.y2 + 1) * -1)) || (y > (tft_dispWin.y2 + 1))) {
  2205. sprintf(err_buf, "out of display area (%d,%d", x, y);
  2206. err = -10;
  2207. goto exit;
  2208. }
  2209. // ** set display and image areas
  2210. if (x < tft_dispWin.x1) {
  2211. disp_xstart = tft_dispWin.x1;
  2212. img_xstart = -x; // image pixel line X offset
  2213. img_xlen += x;
  2214. }
  2215. else {
  2216. disp_xstart = x;
  2217. img_xstart = 0;
  2218. }
  2219. if (y < tft_dispWin.y1) {
  2220. disp_ystart = tft_dispWin.y1;
  2221. img_ystart = -y; // image pixel line Y offset
  2222. img_ylen += y;
  2223. }
  2224. else {
  2225. disp_ystart = y;
  2226. img_ystart = 0;
  2227. }
  2228. disp_xend = disp_xstart + img_xlen - 1;
  2229. disp_yend = disp_ystart + img_ylen - 1;
  2230. if (disp_xend > tft_dispWin.x2) {
  2231. disp_xend = tft_dispWin.x2;
  2232. img_xlen = disp_xend - disp_xstart + 1;
  2233. }
  2234. if (disp_yend > tft_dispWin.y2) {
  2235. disp_yend = tft_dispWin.y2;
  2236. img_ylen = disp_yend - disp_ystart + 1;
  2237. }
  2238. if ((img_xlen < 8) || (img_ylen < 8) || (img_xstart >= (img_xsize-2)) || ((img_ysize - img_ystart) < 2)) {
  2239. sprintf(err_buf, "image too small");
  2240. err = -11;
  2241. goto exit;
  2242. }
  2243. // ** Allocate memory for 2 lines of image pixels
  2244. line_buf[0] = heap_caps_malloc(img_xsize*3, MALLOC_CAP_DMA);
  2245. if (line_buf[0] == NULL) {
  2246. sprintf(err_buf, "allocating line buffer #1");
  2247. err=-12;
  2248. goto exit;
  2249. }
  2250. line_buf[1] = heap_caps_malloc(img_xsize*3, MALLOC_CAP_DMA);
  2251. if (line_buf[1] == NULL) {
  2252. sprintf(err_buf, "allocating line buffer #2");
  2253. err=-13;
  2254. goto exit;
  2255. }
  2256. if (scale) {
  2257. // Allocate memory for scale buffer
  2258. rd_len = img_xlen * 3 * scale_pix;
  2259. scale_buf = malloc(rd_len*scale_pix);
  2260. if (scale_buf == NULL) {
  2261. sprintf(err_buf, "allocating scale buffer");
  2262. err=-14;
  2263. goto exit;
  2264. }
  2265. }
  2266. else rd_len = img_xlen * 3;
  2267. // ** ***************************************************** **
  2268. // ** BMP images are stored in file from LAST to FIRST line **
  2269. // ** ***************************************************** **
  2270. /* Used variables:
  2271. img_xsize horizontal image size in pixels
  2272. img_ysize number of image lines
  2273. img_xlen image display horizontal scaled size in pixels
  2274. img_ylen image display vertical scaled size in pixels
  2275. img_xstart first pixel in line to be displayed
  2276. img_ystart first image line to be displayed
  2277. img_xlen number of pixels in image line to be displayed, starting with 'img_xstart'
  2278. img_ylen number of lines in image to be displayed, starting with 'img_ystart'
  2279. rd_len length of color data which are read from image line in bytes
  2280. */
  2281. // Set position in image to the first color data (beginning of the LAST line)
  2282. img_pos += (img_ystart * (img_xsize*3));
  2283. if (fhndl) {
  2284. if (fseek(fhndl, img_pos, SEEK_SET) != 0) {
  2285. sprintf(err_buf, "file seek at %d", img_pos);
  2286. err = -15;
  2287. goto exit;
  2288. }
  2289. }
  2290. if (tft_image_debug) printf("BMP: image size: (%d,%d) scale: %d disp size: (%d,%d) img xofs: %d img yofs: %d at: %d,%d; line buf: 2* %d scale buf: %d\r\n",
  2291. img_xsize, img_ysize, scale_pix, img_xlen, img_ylen, img_xstart, img_ystart, disp_xstart, disp_ystart, img_xsize*3, ((scale) ? (rd_len*scale_pix) : 0));
  2292. // * Select the display
  2293. disp_select();
  2294. while ((disp_yend >= disp_ystart) && ((img_pos + (img_xsize*3)) <= size)) {
  2295. if (img_pos > size) {
  2296. sprintf(err_buf, "EOF reached: %d > %d", img_pos, size);
  2297. err = -16;
  2298. goto exit1;
  2299. }
  2300. if (scale == 0) {
  2301. // Read the line of color data into color buffer
  2302. if (fhndl) {
  2303. i = fread(line_buf[lb_idx], 1, img_xsize*3, fhndl); // read line from file
  2304. if (i != (img_xsize*3)) {
  2305. sprintf(err_buf, "file read at %d (%d<>%d)", img_pos, i, img_xsize*3);
  2306. err = -16;
  2307. goto exit1;
  2308. }
  2309. }
  2310. else memcpy(line_buf[lb_idx], imgbuf+img_pos, img_xsize*3);
  2311. if (img_xstart > 0) memmove(line_buf[lb_idx], line_buf[lb_idx]+(img_xstart*3), rd_len);
  2312. // Convert colors BGR-888 (BMP) -> RGB-888 (DISPLAY) ===
  2313. for (i=0; i < rd_len; i += 3) {
  2314. tmpc = line_buf[lb_idx][i+2] & 0xfc; // save R
  2315. line_buf[lb_idx][i+2] = line_buf[lb_idx][i] & 0xfc; // B -> R
  2316. line_buf[lb_idx][i] = tmpc; // R -> B
  2317. line_buf[lb_idx][i+1] &= 0xfc; // G
  2318. }
  2319. img_pos += (img_xsize*3);
  2320. }
  2321. else {
  2322. // scale image, read 'scale_pix' lines and find the average color
  2323. for (scan_lines=0; scan_lines<scale_pix; scan_lines++) {
  2324. if (img_pos > size) break;
  2325. if (fhndl) {
  2326. i = fread(line_buf[lb_idx], 1, img_xsize*3, fhndl); // read line from file
  2327. if (i != (img_xsize*3)) {
  2328. sprintf(err_buf, "file read at %d (%d<>%d)", img_pos, i, img_xsize*3);
  2329. err = -17;
  2330. goto exit1;
  2331. }
  2332. }
  2333. else memcpy(line_buf[lb_idx], imgbuf+img_pos, img_xsize*3);
  2334. img_pos += (img_xsize*3);
  2335. // copy only data which are displayed to scale buffer
  2336. memcpy(scale_buf + (rd_len * scan_lines), line_buf[lb_idx]+img_xstart, rd_len);
  2337. }
  2338. // Populate display line buffer
  2339. for (int n=0;n<(img_xlen*3);n += 3) {
  2340. memset(co, 0, sizeof(co)); // initialize color sum
  2341. npix = 0; // initialize number of pixels in scale rectangle
  2342. // sum all pixels in scale rectangle
  2343. for (int sc_line=0; sc_line<scan_lines; sc_line++) {
  2344. // Get colors position in scale buffer
  2345. img_pix_pos = (rd_len * sc_line) + (n * scale_pix);
  2346. for (int sc_col=0; sc_col<scale_pix; sc_col++) {
  2347. co[0] += scale_buf[img_pix_pos];
  2348. co[1] += scale_buf[img_pix_pos + 1];
  2349. co[2] += scale_buf[img_pix_pos + 2];
  2350. npix++;
  2351. }
  2352. }
  2353. // Place the average in display buffer, convert BGR-888 (BMP) -> RGB-888 (DISPLAY)
  2354. line_buf[lb_idx][n+2] = (uint8_t)(co[0] / npix); // B
  2355. line_buf[lb_idx][n+1] = (uint8_t)(co[1] / npix); // G
  2356. line_buf[lb_idx][n] = (uint8_t)(co[2] / npix); // R
  2357. }
  2358. }
  2359. wait_trans_finish(1);
  2360. send_data(disp_xstart, disp_yend, disp_xend, disp_yend, img_xlen, (color_t *)line_buf[lb_idx]);
  2361. lb_idx = (lb_idx + 1) & 1; // change buffer
  2362. disp_yend--;
  2363. }
  2364. err = 0;
  2365. exit1:
  2366. disp_deselect();
  2367. exit:
  2368. if (scale_buf) free(scale_buf);
  2369. if (line_buf[0]) free(line_buf[0]);
  2370. if (line_buf[1]) free(line_buf[1]);
  2371. if (fhndl) fclose(fhndl);
  2372. if ((err) && (tft_image_debug)) printf("Error: %d [%s]\r\n", err, err_buf);
  2373. return err;
  2374. }
  2375. // ============= Touch panel functions =========================================
  2376. #if USE_TOUCH == TOUCH_TYPE_XPT2046
  2377. //-------------------------------------------------------
  2378. static int tp_get_data_xpt2046(uint8_t type, int samples)
  2379. {
  2380. if (tft_ts_spi == NULL) return 0;
  2381. int n, result, val = 0;
  2382. uint32_t i = 0;
  2383. uint32_t vbuf[18];
  2384. uint32_t minval, maxval, dif;
  2385. if (samples < 3) samples = 1;
  2386. if (samples > 18) samples = 18;
  2387. // one dummy read
  2388. result = touch_get_data(type);
  2389. // read data
  2390. while (i < 10) {
  2391. minval = 5000;
  2392. maxval = 0;
  2393. // get values
  2394. for (n=0;n<samples;n++) {
  2395. result = touch_get_data(type);
  2396. if (result < 0) break;
  2397. vbuf[n] = result;
  2398. if (result < minval) minval = result;
  2399. if (result > maxval) maxval = result;
  2400. }
  2401. if (result < 0) break;
  2402. dif = maxval - minval;
  2403. if (dif < 40) break;
  2404. i++;
  2405. }
  2406. if (result < 0) return -1;
  2407. if (samples > 2) {
  2408. // remove one min value
  2409. for (n = 0; n < samples; n++) {
  2410. if (vbuf[n] == minval) {
  2411. vbuf[n] = 5000;
  2412. break;
  2413. }
  2414. }
  2415. // remove one max value
  2416. for (n = 0; n < samples; n++) {
  2417. if (vbuf[n] == maxval) {
  2418. vbuf[n] = 5000;
  2419. break;
  2420. }
  2421. }
  2422. for (n = 0; n < samples; n++) {
  2423. if (vbuf[n] < 5000) val += vbuf[n];
  2424. }
  2425. val /= (samples-2);
  2426. }
  2427. else val = vbuf[0];
  2428. return val;
  2429. }
  2430. //-----------------------------------------------
  2431. static int TFT_read_touch_xpt2046(int *x, int* y)
  2432. {
  2433. int res = 0, result = -1;
  2434. if (spi_lobo_device_select(tft_ts_spi, 0) != ESP_OK) return 0;
  2435. result = tp_get_data_xpt2046(0xB0, 3); // Z; pressure; touch detect
  2436. if (result <= 50) goto exit;
  2437. // touch panel pressed
  2438. result = tp_get_data_xpt2046(0xD0, 10);
  2439. if (result < 0) goto exit;
  2440. *x = result;
  2441. result = tp_get_data_xpt2046(0x90, 10);
  2442. if (result < 0) goto exit;
  2443. *y = result;
  2444. res = 1;
  2445. exit:
  2446. spi_lobo_device_deselect(tft_ts_spi);
  2447. return res;
  2448. }
  2449. #endif
  2450. //=============================================
  2451. int TFT_read_touch(int *x, int* y, uint8_t raw)
  2452. {
  2453. *x = 0;
  2454. *y = 0;
  2455. if (tft_ts_spi == NULL) return 0;
  2456. #if USE_TOUCH == TOUCH_TYPE_NONE
  2457. return 0;
  2458. #else
  2459. int result = -1;
  2460. int X=0, Y=0;
  2461. #if USE_TOUCH == TOUCH_TYPE_XPT2046
  2462. uint32_t tft_tp_calx = TP_CALX_XPT2046;
  2463. uint32_t tft_tp_caly = TP_CALY_XPT2046;
  2464. result = TFT_read_touch_xpt2046(&X, &Y);
  2465. if (result == 0) return 0;
  2466. #elif USE_TOUCH == TOUCH_TYPE_STMPE610
  2467. uint32_t tft_tp_calx = TP_CALX_STMPE610;
  2468. uint32_t tft_tp_caly = TP_CALY_STMPE610;
  2469. uint16_t Xx, Yy, Z=0;
  2470. result = stmpe610_get_touch(&Xx, &Yy, &Z);
  2471. if (result == 0) return 0;
  2472. X = Xx;
  2473. Y = Yy;
  2474. #else
  2475. return 0;
  2476. #endif
  2477. if (raw) {
  2478. *x = X;
  2479. *y = Y;
  2480. return 1;
  2481. }
  2482. // Calibrate the result
  2483. int tmp;
  2484. int xleft = (tft_tp_calx >> 16) & 0x3FFF;
  2485. int xright = tft_tp_calx & 0x3FFF;
  2486. int ytop = (tft_tp_caly >> 16) & 0x3FFF;
  2487. int ybottom = tft_tp_caly & 0x3FFF;
  2488. if (((xright - xleft) <= 0) || ((ybottom - ytop) <= 0)) return 0;
  2489. #if USE_TOUCH == TOUCH_TYPE_XPT2046
  2490. int width = tft_width;
  2491. int height = tft_height;
  2492. X = ((X - xleft) * height) / (xright - xleft);
  2493. Y = ((Y - ytop) * width) / (ybottom - ytop);
  2494. if (X < 0) X = 0;
  2495. if (X > height-1) X = height-1;
  2496. if (Y < 0) Y = 0;
  2497. if (Y > width-1) Y = width-1;
  2498. switch (tft_orientation) {
  2499. case PORTRAIT:
  2500. tmp = X;
  2501. X = width - Y - 1;
  2502. Y = tmp;
  2503. break;
  2504. case PORTRAIT_FLIP:
  2505. tmp = X;
  2506. X = Y;
  2507. Y = height - tmp - 1;
  2508. break;
  2509. case LANDSCAPE_FLIP:
  2510. X = height - X - 1;
  2511. Y = width - Y - 1;
  2512. break;
  2513. }
  2514. #elif USE_TOUCH == TOUCH_TYPE_STMPE610
  2515. int width = tft_width;
  2516. int height = tft_height;
  2517. if (tft_width > tft_height) {
  2518. width = tft_height;
  2519. height = tft_width;
  2520. }
  2521. X = ((X - xleft) * width) / (xright - xleft);
  2522. Y = ((Y - ytop) * height) / (ybottom - ytop);
  2523. if (X < 0) X = 0;
  2524. if (X > width-1) X = width-1;
  2525. if (Y < 0) Y = 0;
  2526. if (Y > height-1) Y = height-1;
  2527. switch (tft_orientation) {
  2528. case PORTRAIT_FLIP:
  2529. X = width - X - 1;
  2530. Y = height - Y - 1;
  2531. break;
  2532. case LANDSCAPE:
  2533. tmp = X;
  2534. X = Y;
  2535. Y = width - tmp -1;
  2536. break;
  2537. case LANDSCAPE_FLIP:
  2538. tmp = X;
  2539. X = height - Y -1;
  2540. Y = tmp;
  2541. break;
  2542. }
  2543. #endif
  2544. *x = X;
  2545. *y = Y;
  2546. return 1;
  2547. #endif
  2548. }