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.
 
 
 

2353 line
66 KiB

  1. /* EPD library
  2. *
  3. * Author: LoBo (loboris@gmail.com, loboris.github)
  4. *
  5. * Module supporting SPI ePaper displays
  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 "freertos/task.h"
  13. #include "esp_system.h"
  14. #include "time.h"
  15. #include <math.h>
  16. #include "rom/tjpgd.h"
  17. #include "esp_heap_alloc_caps.h"
  18. #include "EPD.h"
  19. #include "EPDspi.h"
  20. #include "rom/tjpgd.h"
  21. #define DEG_TO_RAD 0.01745329252
  22. #define RAD_TO_DEG 57.295779513
  23. #define deg_to_rad 0.01745329252 + 3.14159265359
  24. #define swap(a, b) { int16_t t = a; a = b; b = t; }
  25. #define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt)))
  26. #if !defined(max)
  27. #define max(A,B) ( (A) > (B) ? (A):(B))
  28. #endif
  29. #if !defined(min)
  30. #define min(A,B) ( (A) < (B) ? (A):(B))
  31. #endif
  32. //==========================================================================================
  33. // ==== Global variables ===================================================================
  34. //==========================================================================================
  35. uint8_t orientation; // current screen orientation
  36. uint16_t font_rotate; // current font font_rotate angle (0~395)
  37. uint8_t font_transparent; // if not 0 draw fonts transparent
  38. uint8_t font_forceFixed; // if not zero force drawing proportional fonts with fixed width
  39. uint8_t font_buffered_char;
  40. uint8_t font_line_space; // additional spacing between text lines; added to font height
  41. uint8_t font_x_space; // additional spacing between characters in x axis
  42. uint8_t text_wrap; // if not 0 wrap long text to the new line, else clip
  43. color_t _fg; // current foreground color for fonts
  44. color_t _bg; // current background for non transparent fonts
  45. dispWin_t dispWin; // display clip window
  46. float _angleOffset; // angle offset for arc, polygon and line by angle functions
  47. Font_t cfont; // Current font structure
  48. uint8_t image_debug;
  49. int EPD_X; // X position of the next character after EPD_print() function
  50. int EPD_Y; // Y position of the next character after EPD_print() function
  51. // =========================================================================================
  52. // Embedded fonts
  53. extern uint8_t tft_SmallFont[];
  54. extern uint8_t tft_DefaultFont[];
  55. extern uint8_t tft_Dejavu18[];
  56. extern uint8_t tft_Dejavu24[];
  57. extern uint8_t tft_Ubuntu16[];
  58. extern uint8_t tft_Comic24[];
  59. extern uint8_t tft_minya24[];
  60. extern uint8_t tft_tooney32[];
  61. // ==============================================================
  62. // ==== Set default values of global variables ==================
  63. uint8_t orientation = LANDSCAPE_0; // screen orientation
  64. uint16_t font_rotate = 0; // font rotation
  65. uint8_t font_transparent = 0;
  66. uint8_t font_forceFixed = 0;
  67. uint8_t text_wrap = 0; // character wrapping to new line
  68. color_t _fg = EPD_BLACK;
  69. color_t _bg = EPD_WHITE;
  70. float _angleOffset = DEFAULT_ANGLE_OFFSET;
  71. int EPD_X = 0;
  72. int EPD_Y = 0;
  73. dispWin_t dispWin = {
  74. .x1 = 0,
  75. .y1 = 0,
  76. .x2 = EPD_DISPLAY_WIDTH-1,
  77. .y2 = EPD_DISPLAY_HEIGHT-1,
  78. };
  79. Font_t cfont = {
  80. .font = tft_DefaultFont,
  81. .x_size = 0,
  82. .y_size = 0x0B,
  83. .offset = 0,
  84. .numchars = 95,
  85. .bitmap = 1,
  86. };
  87. uint8_t font_line_space = 0;
  88. uint8_t image_debug = 0;
  89. // ==============================================================
  90. typedef struct {
  91. uint8_t charCode;
  92. int adjYOffset;
  93. int width;
  94. int height;
  95. int xOffset;
  96. int xDelta;
  97. uint16_t dataPtr;
  98. } propFont;
  99. static dispWin_t dispWinTemp;
  100. static uint8_t *userfont = NULL;
  101. static int EPD_OFFSET = 0;
  102. static propFont fontChar;
  103. static float _arcAngleMax = DEFAULT_ARC_ANGLE_MAX;
  104. // ==============================================================================================================
  105. //----------------------------------------------
  106. static void drawPixel(int x, int y, uint8_t val)
  107. {
  108. if (orientation == LANDSCAPE_180) {
  109. x = _width - x - 1;
  110. y = _height - y - 1;
  111. }
  112. if (_gs) {
  113. val &= 0x0F;
  114. //if (gs_drawBuff[(y * _width) + x] != val) {
  115. gs_drawBuff[(y * _width) + x] = val;
  116. gs_used_shades |= (1<<val);
  117. //}
  118. }
  119. else {
  120. val &= 0x01;
  121. uint8_t buf_val = drawBuff[(x * (_height >> 3)) + (y>>3)];
  122. uint8_t new_val = buf_val;
  123. if (val) new_val &= (0x80 >> (y % 8)) ^ 0xFF;
  124. else new_val |= (0x80 >> (y % 8));
  125. //if (new_val != buf_val) drawBuff[(x * (_height>>3)) + (y>>3)] = new_val;
  126. drawBuff[(x * (_height>>3)) + (y>>3)] = new_val;
  127. }
  128. }
  129. //-------------------------------------------------------------------------
  130. static void EPD_pushColorRep(int x1, int y1, int x2, int y2, color_t color)
  131. {
  132. if (_gs == 0) color &= 0x01;
  133. else color &= 0x0F;
  134. for (int y=y1; y<=y2; y++) {
  135. for (int x = x1; x<=x2; x++){
  136. drawPixel(x, y, color);
  137. }
  138. }
  139. }
  140. /*
  141. //---------------------------------------------------------------------------------------
  142. static void copyBuff(int x1, int y1, int x2, int y2, uint8_t *src_buf, uint8_t *dest_buf)
  143. {
  144. if
  145. uint8_t buf_val1 = (src_buf[(x1 * (EPD_DISPLAY_HEIGHT>>3)) + (y1>>3)]) ;
  146. uint8_t val = 0x80 >> (y1 % 8);
  147. if (val) buf_val &= (0x80 >> (y % 8)) ^ 0xFF;
  148. else buf_val |= (0x80 >> (y % 8));
  149. drawBuff[(x * (EPD_DISPLAY_HEIGHT>>3)) + (y>>3)] = buf_val;
  150. }
  151. */
  152. // ==============================================================================================================
  153. // =========================================================================
  154. // ** All drawings are clipped to 'dispWin' **
  155. // ** All x,y coordinates in public functions are relative to clip window **
  156. // =========== : Public functions
  157. // ----------- : Local functions
  158. // =========================================================================
  159. // Compare two colors; return 0 if equal
  160. //============================================
  161. int EPD_compare_colors(color_t c1, color_t c2)
  162. {
  163. if (c1 != c2) return 1;
  164. return 0;
  165. }
  166. // draw color pixel on screen
  167. //-----------------------------------------------------------
  168. static void _drawPixel(int16_t x, int16_t y, color_t color) {
  169. if ((x < dispWin.x1) || (y < dispWin.y1) || (x > dispWin.x2) || (y > dispWin.y2)) return;
  170. drawPixel(x, y, color);
  171. }
  172. //=======================================================
  173. void EPD_drawPixel(int16_t x, int16_t y, color_t color) {
  174. _drawPixel(x+dispWin.x1, y+dispWin.y1, color);
  175. }
  176. //--------------------------------------------------------------------------
  177. static void _drawFastVLine(int16_t x, int16_t y, int16_t h, color_t color) {
  178. // clipping
  179. if ((x < dispWin.x1) || (x > dispWin.x2) || (y > dispWin.y2)) return;
  180. if (y < dispWin.y1) {
  181. h -= (dispWin.y1 - y);
  182. y = dispWin.y1;
  183. }
  184. if (h < 0) h = 0;
  185. if ((y + h) > (dispWin.y2+1)) h = dispWin.y2 - y + 1;
  186. if (h == 0) h = 1;
  187. EPD_pushColorRep(x, y, x, y+h-1, color);
  188. }
  189. //--------------------------------------------------------------------------
  190. static void _drawFastHLine(int16_t x, int16_t y, int16_t w, color_t color) {
  191. // clipping
  192. if ((y < dispWin.y1) || (x > dispWin.x2) || (y > dispWin.y2)) return;
  193. if (x < dispWin.x1) {
  194. w -= (dispWin.x1 - x);
  195. x = dispWin.x1;
  196. }
  197. if (w < 0) w = 0;
  198. if ((x + w) > (dispWin.x2+1)) w = dispWin.x2 - x + 1;
  199. if (w == 0) w = 1;
  200. EPD_pushColorRep(x, y, x+w-1, y, color);
  201. }
  202. //======================================================================
  203. void EPD_drawFastVLine(int16_t x, int16_t y, int16_t h, color_t color) {
  204. _drawFastVLine(x+dispWin.x1, y+dispWin.y1, h, color);
  205. }
  206. //======================================================================
  207. void EPD_drawFastHLine(int16_t x, int16_t y, int16_t w, color_t color) {
  208. _drawFastHLine(x+dispWin.x1, y+dispWin.y1, w, color);
  209. }
  210. // Bresenham's algorithm - thx wikipedia - speed enhanced by Bodmer this uses
  211. // the eficient FastH/V Line draw routine for segments of 2 pixels or more
  212. //----------------------------------------------------------------------------------
  213. static void _drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, color_t color)
  214. {
  215. if (x0 == x1) {
  216. if (y0 <= y1) _drawFastVLine(x0, y0, y1-y0, color);
  217. else _drawFastVLine(x0, y1, y0-y1, color);
  218. return;
  219. }
  220. if (y0 == y1) {
  221. if (x0 <= x1) _drawFastHLine(x0, y0, x1-x0, color);
  222. else _drawFastHLine(x1, y0, x0-x1, color);
  223. return;
  224. }
  225. int steep = 0;
  226. if (abs(y1 - y0) > abs(x1 - x0)) steep = 1;
  227. if (steep) {
  228. swap(x0, y0);
  229. swap(x1, y1);
  230. }
  231. if (x0 > x1) {
  232. swap(x0, x1);
  233. swap(y0, y1);
  234. }
  235. int16_t dx = x1 - x0, dy = abs(y1 - y0);;
  236. int16_t err = dx >> 1, ystep = -1, xs = x0, dlen = 0;
  237. if (y0 < y1) ystep = 1;
  238. // Split into steep and not steep for FastH/V separation
  239. if (steep) {
  240. for (; x0 <= x1; x0++) {
  241. dlen++;
  242. err -= dy;
  243. if (err < 0) {
  244. err += dx;
  245. if (dlen == 1) _drawPixel(y0, xs, color);
  246. else _drawFastVLine(y0, xs, dlen, color);
  247. dlen = 0; y0 += ystep; xs = x0 + 1;
  248. }
  249. }
  250. if (dlen) _drawFastVLine(y0, xs, dlen, color);
  251. }
  252. else
  253. {
  254. for (; x0 <= x1; x0++) {
  255. dlen++;
  256. err -= dy;
  257. if (err < 0) {
  258. err += dx;
  259. if (dlen == 1) _drawPixel(xs, y0, color);
  260. else _drawFastHLine(xs, y0, dlen, color);
  261. dlen = 0; y0 += ystep; xs = x0 + 1;
  262. }
  263. }
  264. if (dlen) _drawFastHLine(xs, y0, dlen, color);
  265. }
  266. }
  267. //==============================================================================
  268. void EPD_drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, color_t color)
  269. {
  270. _drawLine(x0+dispWin.x1, y0+dispWin.y1, x1+dispWin.x1, y1+dispWin.y1, color);
  271. }
  272. // fill a rectangle
  273. //--------------------------------------------------------------------------------
  274. static void _fillRect(int16_t x, int16_t y, int16_t w, int16_t h, color_t color) {
  275. // clipping
  276. if ((x >= dispWin.x2) || (y > dispWin.y2)) return;
  277. if (x < dispWin.x1) {
  278. w -= (dispWin.x1 - x);
  279. x = dispWin.x1;
  280. }
  281. if (y < dispWin.y1) {
  282. h -= (dispWin.y1 - y);
  283. y = dispWin.y1;
  284. }
  285. if (w < 0) w = 0;
  286. if (h < 0) h = 0;
  287. if ((x + w) > (dispWin.x2+1)) w = dispWin.x2 - x + 1;
  288. if ((y + h) > (dispWin.y2+1)) h = dispWin.y2 - y + 1;
  289. if (w == 0) w = 1;
  290. if (h == 0) h = 1;
  291. EPD_pushColorRep(x, y, x+w-1, y+h-1, color);
  292. }
  293. //============================================================================
  294. void EPD_fillRect(int16_t x, int16_t y, int16_t w, int16_t h, color_t color) {
  295. _fillRect(x+dispWin.x1, y+dispWin.y1, w, h, color);
  296. }
  297. //==================================
  298. void EPD_fillScreen(color_t color) {
  299. color &= 0x0F;
  300. memset(disp_buffer, ((color&1) ? 0 : 0xFF), _width * (_height/8));
  301. memset(gs_disp_buffer, color, _width * _height);
  302. gs_used_shades = 0;
  303. }
  304. //==================================
  305. void EPD_fillWindow(color_t color) {
  306. EPD_pushColorRep(dispWin.x1, dispWin.y1, dispWin.x2, dispWin.y2, 0xFF);
  307. }
  308. // ^^^============= Basics drawing functions ================================^^^
  309. // ================ Graphics drawing functions ==================================
  310. //-----------------------------------------------------------------------------------
  311. static void _drawRect(uint16_t x1,uint16_t y1,uint16_t w,uint16_t h, color_t color) {
  312. _drawFastHLine(x1,y1,w, color);
  313. _drawFastVLine(x1+w-1,y1,h, color);
  314. _drawFastHLine(x1,y1+h-1,w, color);
  315. _drawFastVLine(x1,y1,h, color);
  316. }
  317. //===============================================================================
  318. void EPD_drawRect(uint16_t x1,uint16_t y1,uint16_t w,uint16_t h, color_t color) {
  319. _drawRect(x1+dispWin.x1, y1+dispWin.y1, w, h, color);
  320. }
  321. //-------------------------------------------------------------------------------------------------
  322. static void drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, color_t color)
  323. {
  324. int16_t f = 1 - r;
  325. int16_t ddF_x = 1;
  326. int16_t ddF_y = -2 * r;
  327. int16_t x = 0;
  328. int16_t y = r;
  329. while (x < y) {
  330. if (f >= 0) {
  331. y--;
  332. ddF_y += 2;
  333. f += ddF_y;
  334. }
  335. x++;
  336. ddF_x += 2;
  337. f += ddF_x;
  338. if (cornername & 0x4) {
  339. _drawPixel(x0 + x, y0 + y, color);
  340. _drawPixel(x0 + y, y0 + x, color);
  341. }
  342. if (cornername & 0x2) {
  343. _drawPixel(x0 + x, y0 - y, color);
  344. _drawPixel(x0 + y, y0 - x, color);
  345. }
  346. if (cornername & 0x8) {
  347. _drawPixel(x0 - y, y0 + x, color);
  348. _drawPixel(x0 - x, y0 + y, color);
  349. }
  350. if (cornername & 0x1) {
  351. _drawPixel(x0 - y, y0 - x, color);
  352. _drawPixel(x0 - x, y0 - y, color);
  353. }
  354. }
  355. }
  356. // Used to do circles and roundrects
  357. //----------------------------------------------------------------------------------------------------------------
  358. static void fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, color_t color)
  359. {
  360. int16_t f = 1 - r;
  361. int16_t ddF_x = 1;
  362. int16_t ddF_y = -2 * r;
  363. int16_t x = 0;
  364. int16_t y = r;
  365. int16_t ylm = x0 - r;
  366. while (x < y) {
  367. if (f >= 0) {
  368. if (cornername & 0x1) _drawFastVLine(x0 + y, y0 - x, 2 * x + 1 + delta, color);
  369. if (cornername & 0x2) _drawFastVLine(x0 - y, y0 - x, 2 * x + 1 + delta, color);
  370. ylm = x0 - y;
  371. y--;
  372. ddF_y += 2;
  373. f += ddF_y;
  374. }
  375. x++;
  376. ddF_x += 2;
  377. f += ddF_x;
  378. if ((x0 - x) > ylm) {
  379. if (cornername & 0x1) _drawFastVLine(x0 + x, y0 - y, 2 * y + 1 + delta, color);
  380. if (cornername & 0x2) _drawFastVLine(x0 - x, y0 - y, 2 * y + 1 + delta, color);
  381. }
  382. }
  383. }
  384. // Draw a rounded rectangle
  385. //=============================================================================================
  386. void EPD_drawRoundRect(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t r, color_t color)
  387. {
  388. x += dispWin.x1;
  389. y += dispWin.y1;
  390. // smarter version
  391. _drawFastHLine(x + r, y, w - 2 * r, color); // Top
  392. _drawFastHLine(x + r, y + h - 1, w - 2 * r, color); // Bottom
  393. _drawFastVLine(x, y + r, h - 2 * r, color); // Left
  394. _drawFastVLine(x + w - 1, y + r, h - 2 * r, color); // Right
  395. // draw four corners
  396. drawCircleHelper(x + r, y + r, r, 1, color);
  397. drawCircleHelper(x + w - r - 1, y + r, r, 2, color);
  398. drawCircleHelper(x + w - r - 1, y + h - r - 1, r, 4, color);
  399. drawCircleHelper(x + r, y + h - r - 1, r, 8, color);
  400. }
  401. // Fill a rounded rectangle
  402. //=============================================================================================
  403. void EPD_fillRoundRect(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t r, color_t color)
  404. {
  405. x += dispWin.x1;
  406. y += dispWin.y1;
  407. // smarter version
  408. _fillRect(x + r, y, w - 2 * r, h, color);
  409. // draw four corners
  410. fillCircleHelper(x + w - r - 1, y + r, r, 1, h - 2 * r - 1, color);
  411. fillCircleHelper(x + r, y + r, r, 2, h - 2 * r - 1, color);
  412. }
  413. //-----------------------------------------------------------------------------------------------
  414. static void _drawLineByAngle(int16_t x, int16_t y, int16_t angle, uint16_t length, color_t color)
  415. {
  416. _drawLine(
  417. x,
  418. y,
  419. x + length * cos((angle + _angleOffset) * DEG_TO_RAD),
  420. y + length * sin((angle + _angleOffset) * DEG_TO_RAD), color);
  421. }
  422. //---------------------------------------------------------------------------------------------------------------
  423. static void _DrawLineByAngle(int16_t x, int16_t y, int16_t angle, uint16_t start, uint16_t length, color_t color)
  424. {
  425. _drawLine(
  426. x + start * cos((angle + _angleOffset) * DEG_TO_RAD),
  427. y + start * sin((angle + _angleOffset) * DEG_TO_RAD),
  428. x + (start + length) * cos((angle + _angleOffset) * DEG_TO_RAD),
  429. y + (start + length) * sin((angle + _angleOffset) * DEG_TO_RAD), color);
  430. }
  431. //===========================================================================================================
  432. void EPD_drawLineByAngle(uint16_t x, uint16_t y, uint16_t start, uint16_t len, uint16_t angle, color_t color)
  433. {
  434. x += dispWin.x1;
  435. y += dispWin.y1;
  436. if (start == 0) _drawLineByAngle(x, y, angle, len, color);
  437. else _DrawLineByAngle(x, y, angle, start, len, color);
  438. }
  439. // Draw a triangle
  440. //--------------------------------------------------------------------------------------------------------------------
  441. static void _drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
  442. {
  443. _drawLine(x0, y0, x1, y1, color);
  444. _drawLine(x1, y1, x2, y2, color);
  445. _drawLine(x2, y2, x0, y0, color);
  446. }
  447. //================================================================================================================
  448. void EPD_drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
  449. {
  450. x0 += dispWin.x1;
  451. y0 += dispWin.y1;
  452. x1 += dispWin.x1;
  453. y1 += dispWin.y1;
  454. x2 += dispWin.x1;
  455. y2 += dispWin.y1;
  456. _drawLine(x0, y0, x1, y1, color);
  457. _drawLine(x1, y1, x2, y2, color);
  458. _drawLine(x2, y2, x0, y0, color);
  459. }
  460. // Fill a triangle
  461. //--------------------------------------------------------------------------------------------------------------------
  462. static void _fillTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
  463. {
  464. int16_t a, b, y, last;
  465. // Sort coordinates by Y order (y2 >= y1 >= y0)
  466. if (y0 > y1) {
  467. swap(y0, y1); swap(x0, x1);
  468. }
  469. if (y1 > y2) {
  470. swap(y2, y1); swap(x2, x1);
  471. }
  472. if (y0 > y1) {
  473. swap(y0, y1); swap(x0, x1);
  474. }
  475. if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing
  476. a = b = x0;
  477. if(x1 < a) a = x1;
  478. else if(x1 > b) b = x1;
  479. if(x2 < a) a = x2;
  480. else if(x2 > b) b = x2;
  481. _drawFastHLine(a, y0, b-a+1, color);
  482. return;
  483. }
  484. int16_t
  485. dx01 = x1 - x0,
  486. dy01 = y1 - y0,
  487. dx02 = x2 - x0,
  488. dy02 = y2 - y0,
  489. dx12 = x2 - x1,
  490. dy12 = y2 - y1;
  491. int32_t
  492. sa = 0,
  493. sb = 0;
  494. // For upper part of triangle, find scanline crossings for segments
  495. // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1
  496. // is included here (and second loop will be skipped, avoiding a /0
  497. // error there), otherwise scanline y1 is skipped here and handled
  498. // in the second loop...which also avoids a /0 error here if y0=y1
  499. // (flat-topped triangle).
  500. if(y1 == y2) last = y1; // Include y1 scanline
  501. else last = y1-1; // Skip it
  502. for(y=y0; y<=last; y++) {
  503. a = x0 + sa / dy01;
  504. b = x0 + sb / dy02;
  505. sa += dx01;
  506. sb += dx02;
  507. /* longhand:
  508. a = x0 + (x1 - x0) * (y - y0) / (y1 - y0);
  509. b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
  510. */
  511. if(a > b) swap(a,b);
  512. _drawFastHLine(a, y, b-a+1, color);
  513. }
  514. // For lower part of triangle, find scanline crossings for segments
  515. // 0-2 and 1-2. This loop is skipped if y1=y2.
  516. sa = dx12 * (y - y1);
  517. sb = dx02 * (y - y0);
  518. for(; y<=y2; y++) {
  519. a = x1 + sa / dy12;
  520. b = x0 + sb / dy02;
  521. sa += dx12;
  522. sb += dx02;
  523. /* longhand:
  524. a = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
  525. b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
  526. */
  527. if(a > b) swap(a,b);
  528. _drawFastHLine(a, y, b-a+1, color);
  529. }
  530. }
  531. //================================================================================================================
  532. void EPD_fillTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
  533. {
  534. _fillTriangle(
  535. x0 + dispWin.x1, y0 + dispWin.y1,
  536. x1 + dispWin.x1, y1 + dispWin.y1,
  537. x2 + dispWin.x1, y2 + dispWin.y1,
  538. color);
  539. }
  540. //====================================================================
  541. void EPD_drawCircle(int16_t x, int16_t y, int radius, color_t color) {
  542. x += dispWin.x1;
  543. y += dispWin.y1;
  544. int f = 1 - radius;
  545. int ddF_x = 1;
  546. int ddF_y = -2 * radius;
  547. int x1 = 0;
  548. int y1 = radius;
  549. _drawPixel(x, y + radius, color);
  550. _drawPixel(x, y - radius, color);
  551. _drawPixel(x + radius, y, color);
  552. _drawPixel(x - radius, y, color);
  553. while(x1 < y1) {
  554. if (f >= 0) {
  555. y1--;
  556. ddF_y += 2;
  557. f += ddF_y;
  558. }
  559. x1++;
  560. ddF_x += 2;
  561. f += ddF_x;
  562. _drawPixel(x + x1, y + y1, color);
  563. _drawPixel(x - x1, y + y1, color);
  564. _drawPixel(x + x1, y - y1, color);
  565. _drawPixel(x - x1, y - y1, color);
  566. _drawPixel(x + y1, y + x1, color);
  567. _drawPixel(x - y1, y + x1, color);
  568. _drawPixel(x + y1, y - x1, color);
  569. _drawPixel(x - y1, y - x1, color);
  570. }
  571. }
  572. //====================================================================
  573. void EPD_fillCircle(int16_t x, int16_t y, int radius, color_t color) {
  574. x += dispWin.x1;
  575. y += dispWin.y1;
  576. _drawFastVLine(x, y-radius, 2*radius+1, color);
  577. fillCircleHelper(x, y, radius, 3, 0, color);
  578. }
  579. //----------------------------------------------------------------------------------------------------------------
  580. static void _draw_ellipse_section(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, color_t color, uint8_t option)
  581. {
  582. // upper right
  583. if ( option & EPD_ELLIPSE_UPPER_RIGHT ) _drawPixel(x0 + x, y0 - y, color);
  584. // upper left
  585. if ( option & EPD_ELLIPSE_UPPER_LEFT ) _drawPixel(x0 - x, y0 - y, color);
  586. // lower right
  587. if ( option & EPD_ELLIPSE_LOWER_RIGHT ) _drawPixel(x0 + x, y0 + y, color);
  588. // lower left
  589. if ( option & EPD_ELLIPSE_LOWER_LEFT ) _drawPixel(x0 - x, y0 + y, color);
  590. }
  591. //=====================================================================================================
  592. void EPD_drawEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, color_t color, uint8_t option)
  593. {
  594. x0 += dispWin.x1;
  595. y0 += dispWin.y1;
  596. uint16_t x, y;
  597. int32_t xchg, ychg;
  598. int32_t err;
  599. int32_t rxrx2;
  600. int32_t ryry2;
  601. int32_t stopx, stopy;
  602. rxrx2 = rx;
  603. rxrx2 *= rx;
  604. rxrx2 *= 2;
  605. ryry2 = ry;
  606. ryry2 *= ry;
  607. ryry2 *= 2;
  608. x = rx;
  609. y = 0;
  610. xchg = 1;
  611. xchg -= rx;
  612. xchg -= rx;
  613. xchg *= ry;
  614. xchg *= ry;
  615. ychg = rx;
  616. ychg *= rx;
  617. err = 0;
  618. stopx = ryry2;
  619. stopx *= rx;
  620. stopy = 0;
  621. while( stopx >= stopy ) {
  622. _draw_ellipse_section(x, y, x0, y0, color, option);
  623. y++;
  624. stopy += rxrx2;
  625. err += ychg;
  626. ychg += rxrx2;
  627. if ( 2*err+xchg > 0 ) {
  628. x--;
  629. stopx -= ryry2;
  630. err += xchg;
  631. xchg += ryry2;
  632. }
  633. }
  634. x = 0;
  635. y = ry;
  636. xchg = ry;
  637. xchg *= ry;
  638. ychg = 1;
  639. ychg -= ry;
  640. ychg -= ry;
  641. ychg *= rx;
  642. ychg *= rx;
  643. err = 0;
  644. stopx = 0;
  645. stopy = rxrx2;
  646. stopy *= ry;
  647. while( stopx <= stopy ) {
  648. _draw_ellipse_section(x, y, x0, y0, color, option);
  649. x++;
  650. stopx += ryry2;
  651. err += xchg;
  652. xchg += ryry2;
  653. if ( 2*err+ychg > 0 ) {
  654. y--;
  655. stopy -= rxrx2;
  656. err += ychg;
  657. ychg += rxrx2;
  658. }
  659. }
  660. }
  661. //-----------------------------------------------------------------------------------------------------------------------
  662. static void _draw_filled_ellipse_section(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, color_t color, uint8_t option)
  663. {
  664. // upper right
  665. if ( option & EPD_ELLIPSE_UPPER_RIGHT ) _drawFastVLine(x0+x, y0-y, y+1, color);
  666. // upper left
  667. if ( option & EPD_ELLIPSE_UPPER_LEFT ) _drawFastVLine(x0-x, y0-y, y+1, color);
  668. // lower right
  669. if ( option & EPD_ELLIPSE_LOWER_RIGHT ) _drawFastVLine(x0+x, y0, y+1, color);
  670. // lower left
  671. if ( option & EPD_ELLIPSE_LOWER_LEFT ) _drawFastVLine(x0-x, y0, y+1, color);
  672. }
  673. //=====================================================================================================
  674. void EPD_fillEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, color_t color, uint8_t option)
  675. {
  676. x0 += dispWin.x1;
  677. y0 += dispWin.y1;
  678. uint16_t x, y;
  679. int32_t xchg, ychg;
  680. int32_t err;
  681. int32_t rxrx2;
  682. int32_t ryry2;
  683. int32_t stopx, stopy;
  684. rxrx2 = rx;
  685. rxrx2 *= rx;
  686. rxrx2 *= 2;
  687. ryry2 = ry;
  688. ryry2 *= ry;
  689. ryry2 *= 2;
  690. x = rx;
  691. y = 0;
  692. xchg = 1;
  693. xchg -= rx;
  694. xchg -= rx;
  695. xchg *= ry;
  696. xchg *= ry;
  697. ychg = rx;
  698. ychg *= rx;
  699. err = 0;
  700. stopx = ryry2;
  701. stopx *= rx;
  702. stopy = 0;
  703. while( stopx >= stopy ) {
  704. _draw_filled_ellipse_section(x, y, x0, y0, color, option);
  705. y++;
  706. stopy += rxrx2;
  707. err += ychg;
  708. ychg += rxrx2;
  709. if ( 2*err+xchg > 0 ) {
  710. x--;
  711. stopx -= ryry2;
  712. err += xchg;
  713. xchg += ryry2;
  714. }
  715. }
  716. x = 0;
  717. y = ry;
  718. xchg = ry;
  719. xchg *= ry;
  720. ychg = 1;
  721. ychg -= ry;
  722. ychg -= ry;
  723. ychg *= rx;
  724. ychg *= rx;
  725. err = 0;
  726. stopx = 0;
  727. stopy = rxrx2;
  728. stopy *= ry;
  729. while( stopx <= stopy ) {
  730. _draw_filled_ellipse_section(x, y, x0, y0, color, option);
  731. x++;
  732. stopx += ryry2;
  733. err += xchg;
  734. xchg += ryry2;
  735. if ( 2*err+ychg > 0 ) {
  736. y--;
  737. stopy -= rxrx2;
  738. err += ychg;
  739. ychg += rxrx2;
  740. }
  741. }
  742. }
  743. // ==== ARC DRAWING ===================================================================
  744. //---------------------------------------------------------------------------------------------------------------------------------
  745. static void _fillArcOffsetted(uint16_t cx, uint16_t cy, uint16_t radius, uint16_t thickness, float start, float end, color_t color)
  746. {
  747. //float sslope = (float)cos_lookup(start) / (float)sin_lookup(start);
  748. //float eslope = (float)cos_lookup(end) / (float)sin_lookup(end);
  749. float sslope = (cos(start/_arcAngleMax * 2 * PI) * _arcAngleMax) / (sin(start/_arcAngleMax * 2 * PI) * _arcAngleMax) ;
  750. float eslope = (cos(end/_arcAngleMax * 2 * PI) * _arcAngleMax) / (sin(end/_arcAngleMax * 2 * PI) * _arcAngleMax);
  751. if (end == 360) eslope = -1000000;
  752. int ir2 = (radius - thickness) * (radius - thickness);
  753. int or2 = radius * radius;
  754. for (int x = -radius; x <= radius; x++) {
  755. for (int y = -radius; y <= radius; y++) {
  756. int x2 = x * x;
  757. int y2 = y * y;
  758. if (
  759. (x2 + y2 < or2 && x2 + y2 >= ir2) &&
  760. (
  761. (y > 0 && start < 180 && x <= y * sslope) ||
  762. (y < 0 && start > 180 && x >= y * sslope) ||
  763. (y < 0 && start <= 180) ||
  764. (y == 0 && start <= 180 && x < 0) ||
  765. (y == 0 && start == 0 && x > 0)
  766. ) &&
  767. (
  768. (y > 0 && end < 180 && x >= y * eslope) ||
  769. (y < 0 && end > 180 && x <= y * eslope) ||
  770. (y > 0 && end >= 180) ||
  771. (y == 0 && end >= 180 && x < 0) ||
  772. (y == 0 && start == 0 && x > 0)
  773. )
  774. )
  775. _drawPixel(cx+x, cy+y, color);
  776. }
  777. }
  778. }
  779. //===========================================================================================================================
  780. void EPD_drawArc(uint16_t cx, uint16_t cy, uint16_t r, uint16_t th, float start, float end, color_t color, color_t fillcolor)
  781. {
  782. cx += dispWin.x1;
  783. cy += dispWin.y1;
  784. if (th < 1) th = 1;
  785. if (th > r) th = r;
  786. int f = EPD_compare_colors(fillcolor, color);
  787. float astart = fmodf(start, _arcAngleMax);
  788. float aend = fmodf(end, _arcAngleMax);
  789. astart += _angleOffset;
  790. aend += _angleOffset;
  791. if (astart < 0) astart += (float)360;
  792. if (aend < 0) aend += (float)360;
  793. if (aend == 0) aend = (float)360;
  794. if (astart > aend) {
  795. _fillArcOffsetted(cx, cy, r, th, astart, _arcAngleMax, fillcolor);
  796. _fillArcOffsetted(cx, cy, r, th, 0, aend, fillcolor);
  797. if (f) {
  798. _fillArcOffsetted(cx, cy, r, 1, astart, _arcAngleMax, color);
  799. _fillArcOffsetted(cx, cy, r, 1, 0, aend, color);
  800. _fillArcOffsetted(cx, cy, r-th, 1, astart, _arcAngleMax, color);
  801. _fillArcOffsetted(cx, cy, r-th, 1, 0, aend, color);
  802. }
  803. }
  804. else {
  805. _fillArcOffsetted(cx, cy, r, th, astart, aend, fillcolor);
  806. if (f) {
  807. _fillArcOffsetted(cx, cy, r, 1, astart, aend, color);
  808. _fillArcOffsetted(cx, cy, r-th, 1, astart, aend, color);
  809. }
  810. }
  811. if (f) {
  812. _drawLine(cx + (r-th) * cos(astart * DEG_TO_RAD), cy + (r-th) * sin(astart * DEG_TO_RAD),
  813. cx + (r-1) * cos(astart * DEG_TO_RAD), cy + (r-1) * sin(astart * DEG_TO_RAD), color);
  814. _drawLine(cx + (r-th) * cos(aend * DEG_TO_RAD), cy + (r-th) * sin(aend * DEG_TO_RAD),
  815. cx + (r-1) * cos(aend * DEG_TO_RAD), cy + (r-1) * sin(aend * DEG_TO_RAD), color);
  816. }
  817. }
  818. //=============================================================================================================
  819. void EPD_drawPolygon(int cx, int cy, int sides, int diameter, color_t color, color_t fill, int rot, uint8_t th)
  820. {
  821. cx += dispWin.x1;
  822. cy += dispWin.y1;
  823. int deg = rot - _angleOffset;
  824. int f = EPD_compare_colors(fill, color);
  825. if (sides < MIN_POLIGON_SIDES) sides = MIN_POLIGON_SIDES; // This ensures the minimum side number
  826. if (sides > MAX_POLIGON_SIDES) sides = MAX_POLIGON_SIDES; // This ensures the maximum side number
  827. int Xpoints[sides], Ypoints[sides]; // Set the arrays based on the number of sides entered
  828. int rads = 360 / sides; // This equally spaces the points.
  829. for (int idx = 0; idx < sides; idx++) {
  830. Xpoints[idx] = cx + sin((float)(idx*rads + deg) * deg_to_rad) * diameter;
  831. Ypoints[idx] = cy + cos((float)(idx*rads + deg) * deg_to_rad) * diameter;
  832. }
  833. // Draw the polygon on the screen.
  834. if (f) {
  835. for(int idx = 0; idx < sides; idx++) {
  836. if((idx+1) < sides) _fillTriangle(cx,cy,Xpoints[idx],Ypoints[idx],Xpoints[idx+1],Ypoints[idx+1], fill);
  837. else _fillTriangle(cx,cy,Xpoints[idx],Ypoints[idx],Xpoints[0],Ypoints[0], fill);
  838. }
  839. }
  840. if (th) {
  841. for (int n=0; n<th; n++) {
  842. if (n > 0) {
  843. for (int idx = 0; idx < sides; idx++) {
  844. Xpoints[idx] = cx + sin((float)(idx*rads + deg) * deg_to_rad) * (diameter-n);
  845. Ypoints[idx] = cy + cos((float)(idx*rads + deg) * deg_to_rad) * (diameter-n);
  846. }
  847. }
  848. for(int idx = 0; idx < sides; idx++) {
  849. if( (idx+1) < sides)
  850. _drawLine(Xpoints[idx],Ypoints[idx],Xpoints[idx+1],Ypoints[idx+1], color); // draw the lines
  851. else
  852. _drawLine(Xpoints[idx],Ypoints[idx],Xpoints[0],Ypoints[0], color); // finishes the last line to close up the polygon.
  853. }
  854. }
  855. }
  856. }
  857. /*
  858. // Similar to the Polygon function.
  859. //=====================================================================================
  860. void EPD_drawStar(int cx, int cy, int diameter, color_t color, bool fill, float factor)
  861. {
  862. cx += dispWin.x1;
  863. cy += dispWin.y1;
  864. factor = constrain(factor, 1.0, 4.0);
  865. uint8_t sides = 5;
  866. uint8_t rads = 360 / sides;
  867. int Xpoints_O[sides], Ypoints_O[sides], Xpoints_I[sides], Ypoints_I[sides];//Xpoints_T[5], Ypoints_T[5];
  868. for(int idx = 0; idx < sides; idx++) {
  869. // makes the outer points
  870. Xpoints_O[idx] = cx + sin((float)(idx*rads + 72) * deg_to_rad) * diameter;
  871. Ypoints_O[idx] = cy + cos((float)(idx*rads + 72) * deg_to_rad) * diameter;
  872. // makes the inner points
  873. Xpoints_I[idx] = cx + sin((float)(idx*rads + 36) * deg_to_rad) * ((float)(diameter)/factor);
  874. // 36 is half of 72, and this will allow the inner and outer points to line up like a triangle.
  875. Ypoints_I[idx] = cy + cos((float)(idx*rads + 36) * deg_to_rad) * ((float)(diameter)/factor);
  876. }
  877. for(int idx = 0; idx < sides; idx++) {
  878. if((idx+1) < sides) {
  879. if(fill) {// this part below should be self explanatory. It fills in the star.
  880. _fillTriangle(cx,cy,Xpoints_I[idx],Ypoints_I[idx],Xpoints_O[idx],Ypoints_O[idx], color);
  881. _fillTriangle(cx,cy,Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx+1],Ypoints_I[idx+1], color);
  882. }
  883. else {
  884. _drawLine(Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx+1],Ypoints_I[idx+1], color);
  885. _drawLine(Xpoints_I[idx],Ypoints_I[idx],Xpoints_O[idx],Ypoints_O[idx], color);
  886. }
  887. }
  888. else {
  889. if(fill) {
  890. _fillTriangle(cx,cy,Xpoints_I[0],Ypoints_I[0],Xpoints_O[idx],Ypoints_O[idx], color);
  891. _fillTriangle(cx,cy,Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx],Ypoints_I[idx], color);
  892. }
  893. else {
  894. _drawLine(Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx],Ypoints_I[idx], color);
  895. _drawLine(Xpoints_I[0],Ypoints_I[0],Xpoints_O[idx],Ypoints_O[idx], color);
  896. }
  897. }
  898. }
  899. }
  900. */
  901. // ================ Font and string functions ==================================
  902. //--------------------------------------------------------
  903. static int load_file_font(const char * fontfile, int info)
  904. {
  905. int err = 0;
  906. char err_msg[256] = {'\0'};
  907. if (userfont != NULL) {
  908. free(userfont);
  909. userfont = NULL;
  910. }
  911. struct stat sb;
  912. // Open the file
  913. FILE *fhndl = fopen(fontfile, "r");
  914. if (!fhndl) {
  915. sprintf(err_msg, "Error opening font file '%s'", fontfile);
  916. err = 1;
  917. goto exit;
  918. }
  919. // Get file size
  920. if (stat(fontfile, &sb) != 0) {
  921. sprintf(err_msg, "Error getting font file size");
  922. err = 2;
  923. goto exit;
  924. }
  925. int fsize = sb.st_size;
  926. if (fsize < 30) {
  927. sprintf(err_msg, "Error getting font file size");
  928. err = 3;
  929. goto exit;
  930. }
  931. userfont = malloc(fsize+4);
  932. if (userfont == NULL) {
  933. sprintf(err_msg, "Font memory allocation error");
  934. fclose(fhndl);
  935. err = 4;
  936. goto exit;
  937. }
  938. int read = fread(userfont, 1, fsize, fhndl);
  939. fclose(fhndl);
  940. if (read != fsize) {
  941. sprintf(err_msg, "Font read error");
  942. err = 5;
  943. goto exit;
  944. }
  945. userfont[read] = 0;
  946. if (strstr((char *)(userfont+read-8), "RPH_font") == NULL) {
  947. sprintf(err_msg, "Font ID not found");
  948. err = 6;
  949. goto exit;
  950. }
  951. // Check size
  952. int size = 0;
  953. int numchar = 0;
  954. int width = userfont[0];
  955. int height = userfont[1];
  956. uint8_t first = 255;
  957. uint8_t last = 0;
  958. //int offst = 0;
  959. int pminwidth = 255;
  960. int pmaxwidth = 0;
  961. if (width != 0) {
  962. // Fixed font
  963. numchar = userfont[3];
  964. first = userfont[2];
  965. last = first + numchar - 1;
  966. size = ((width * height * numchar) / 8) + 4;
  967. }
  968. else {
  969. // Proportional font
  970. size = 4; // point at first char data
  971. uint8_t charCode;
  972. int charwidth;
  973. do {
  974. charCode = userfont[size];
  975. charwidth = userfont[size+2];
  976. if (charCode != 0xFF) {
  977. numchar++;
  978. if (charwidth != 0) size += ((((charwidth * userfont[size+3])-1) / 8) + 7);
  979. else size += 6;
  980. if (info) {
  981. if (charwidth > pmaxwidth) pmaxwidth = charwidth;
  982. if (charwidth < pminwidth) pminwidth = charwidth;
  983. if (charCode < first) first = charCode;
  984. if (charCode > last) last = charCode;
  985. }
  986. }
  987. else size++;
  988. } while ((size < (read-8)) && (charCode != 0xFF));
  989. }
  990. if (size != (read-8)) {
  991. sprintf(err_msg, "Font size error: found %d expected %d)", size, (read-8));
  992. err = 7;
  993. goto exit;
  994. }
  995. if (info) {
  996. if (width != 0) {
  997. printf("Fixed width font:\r\n size: %d width: %d height: %d characters: %d (%d~%d)",
  998. size, width, height, numchar, first, last);
  999. }
  1000. else {
  1001. printf("Proportional font:\r\n size: %d width: %d~%d height: %d characters: %d (%d~%d)\n",
  1002. size, pminwidth, pmaxwidth, height, numchar, first, last);
  1003. }
  1004. }
  1005. exit:
  1006. if (err) {
  1007. if (userfont) {
  1008. free(userfont);
  1009. userfont = NULL;
  1010. }
  1011. if (info) printf("Error: %d [%s]\r\n", err, err_msg);
  1012. }
  1013. return err;
  1014. }
  1015. //------------------------------------------------
  1016. int compile_font_file(char *fontfile, uint8_t dbg)
  1017. {
  1018. int err = 0;
  1019. char err_msg[128] = {'\0'};
  1020. char outfile[128] = {'\0'};
  1021. size_t len;
  1022. struct stat sb;
  1023. FILE *ffd = NULL;
  1024. FILE *ffd_out = NULL;
  1025. char *sourcebuf = NULL;
  1026. len = strlen(fontfile);
  1027. // check here that filename end with ".c".
  1028. if ((len < 3) || (len > 125) || (strcmp(fontfile + len - 2, ".c") != 0)) {
  1029. sprintf(err_msg, "not a .c file");
  1030. err = 1;
  1031. goto exit;
  1032. }
  1033. sprintf(outfile, "%s", fontfile);
  1034. sprintf(outfile+strlen(outfile)-1, "fon");
  1035. // Open the source file
  1036. if (stat(fontfile, &sb) != 0) {
  1037. sprintf(err_msg, "Error opening source file '%s'", fontfile);
  1038. err = 2;
  1039. goto exit;
  1040. }
  1041. // Open the file
  1042. ffd = fopen(fontfile, "rb");
  1043. if (!ffd) {
  1044. sprintf(err_msg, "Error opening source file '%s'", fontfile);
  1045. err = 3;
  1046. goto exit;
  1047. }
  1048. // Open the font file
  1049. ffd_out= fopen(outfile, "wb");
  1050. if (!ffd_out) {
  1051. sprintf(err_msg, "error opening destination file");
  1052. err = 4;
  1053. goto exit;
  1054. }
  1055. // Get file size
  1056. int fsize = sb.st_size;
  1057. if (fsize <= 0) {
  1058. sprintf(err_msg, "source file size error");
  1059. err = 5;
  1060. goto exit;
  1061. }
  1062. sourcebuf = malloc(fsize+4);
  1063. if (sourcebuf == NULL) {
  1064. sprintf(err_msg, "memory allocation error");
  1065. err = 6;
  1066. goto exit;
  1067. }
  1068. char *fbuf = sourcebuf;
  1069. int rdsize = fread(fbuf, 1, fsize, ffd);
  1070. fclose(ffd);
  1071. ffd = NULL;
  1072. if (rdsize != fsize) {
  1073. sprintf(err_msg, "error reading from source file");
  1074. err = 7;
  1075. goto exit;
  1076. }
  1077. *(fbuf+rdsize) = '\0';
  1078. fbuf = strchr(fbuf, '{'); // beginning of font data
  1079. char *fend = strstr(fbuf, "};"); // end of font data
  1080. if ((fbuf == NULL) || (fend == NULL) || ((fend-fbuf) < 22)) {
  1081. sprintf(err_msg, "wrong source file format");
  1082. err = 8;
  1083. goto exit;
  1084. }
  1085. fbuf++;
  1086. *fend = '\0';
  1087. char hexstr[5] = {'\0'};
  1088. int lastline = 0;
  1089. fbuf = strstr(fbuf, "0x");
  1090. int size = 0;
  1091. char *nextline;
  1092. char *numptr;
  1093. int bptr = 0;
  1094. while ((fbuf != NULL) && (fbuf < fend) && (lastline == 0)) {
  1095. nextline = strchr(fbuf, '\n'); // beginning of the next line
  1096. if (nextline == NULL) {
  1097. nextline = fend-1;
  1098. lastline++;
  1099. }
  1100. else nextline++;
  1101. while (fbuf < nextline) {
  1102. numptr = strstr(fbuf, "0x");
  1103. if ((numptr == NULL) || ((fbuf+4) > nextline)) numptr = strstr(fbuf, "0X");
  1104. if ((numptr != NULL) && ((numptr+4) <= nextline)) {
  1105. fbuf = numptr;
  1106. if (bptr >= 128) {
  1107. // buffer full, write to file
  1108. if (fwrite(outfile, 1, 128, ffd_out) != 128) goto error;
  1109. bptr = 0;
  1110. size += 128;
  1111. }
  1112. memcpy(hexstr, fbuf, 4);
  1113. hexstr[4] = 0;
  1114. outfile[bptr++] = (uint8_t)strtol(hexstr, NULL, 0);
  1115. fbuf += 4;
  1116. }
  1117. else fbuf = nextline;
  1118. }
  1119. fbuf = nextline;
  1120. }
  1121. if (bptr > 0) {
  1122. size += bptr;
  1123. if (fwrite(outfile, 1, bptr, ffd_out) != bptr) goto error;
  1124. }
  1125. // write font ID
  1126. sprintf(outfile, "RPH_font");
  1127. if (fwrite(outfile, 1, 8, ffd_out) != 8) goto error;
  1128. // === Test compiled font ===
  1129. sprintf(outfile, "%s", fontfile);
  1130. sprintf(outfile+strlen(outfile)-1, "fon");
  1131. uint8_t *uf = userfont; // save userfont pointer
  1132. userfont = NULL;
  1133. if (load_file_font(outfile, 1) == 0) {
  1134. sprintf(err_msg, "Error compiling file!");
  1135. }
  1136. else {
  1137. free(userfont);
  1138. sprintf(err_msg, "File compiled successfully.");
  1139. }
  1140. userfont = uf; // restore userfont
  1141. goto exit;
  1142. error:
  1143. sprintf(err_msg, "error writing to destination file");
  1144. err = 9;
  1145. exit:
  1146. if (sourcebuf) free(sourcebuf);
  1147. if (ffd) fclose(ffd);
  1148. if (ffd_out) fclose(ffd_out);
  1149. if (dbg) printf("%s\r\n", err_msg);
  1150. return err;
  1151. }
  1152. // -----------------------------------------------------------------------------------------
  1153. // Individual Proportional Font Character Format:
  1154. // -----------------------------------------------------------------------------------------
  1155. // Character Code
  1156. // yOffset (start Y of visible pixels)
  1157. // Width (width of the visible pixels)
  1158. // Height (height of the visible pixels)
  1159. // xOffset (start X of visible pixels)
  1160. // xDelta (the distance to move the cursor. Effective width of the character.)
  1161. // Data[n]
  1162. // -----------------------------------------------------------------------------------------
  1163. //---------------------------------------------------------------------------------------------
  1164. // Character drawing rectangle is (0, 0) (xDelta-1, cfont.y_size-1)
  1165. // Character visible pixels rectangle is (xOffset, yOffset) (xOffset+Width-1, yOffset+Height-1)
  1166. //---------------------------------------------------------------------------------------------
  1167. //----------------------------------
  1168. void getFontCharacters(uint8_t *buf)
  1169. {
  1170. if (cfont.bitmap == 2) {
  1171. //For 7 segment font only characters 0,1,2,3,4,5,6,7,8,9, . , - , : , / are available.
  1172. for (uint8_t n=0; n < 11; n++) {
  1173. buf[n] = n + 0x30;
  1174. }
  1175. buf[11] = '.';
  1176. buf[12] = '-';
  1177. buf[13] = '/';
  1178. buf[14] = '\0';
  1179. return;
  1180. }
  1181. if (cfont.x_size > 0) {
  1182. for (uint8_t n=0; n < cfont.numchars; n++) {
  1183. buf[n] = cfont.offset + n;
  1184. }
  1185. buf[cfont.numchars] = '\0';
  1186. return;
  1187. }
  1188. uint16_t tempPtr = 4; // point at first char data
  1189. uint8_t cc, cw, ch, n;
  1190. n = 0;
  1191. cc = cfont.font[tempPtr++];
  1192. while (cc != 0xFF) {
  1193. cfont.numchars++;
  1194. tempPtr++;
  1195. cw = cfont.font[tempPtr++];
  1196. ch = cfont.font[tempPtr++];
  1197. tempPtr++;
  1198. tempPtr++;
  1199. if (cw != 0) {
  1200. // packed bits
  1201. tempPtr += (((cw * ch)-1) / 8) + 1;
  1202. }
  1203. buf[n++] = cc;
  1204. cc = cfont.font[tempPtr++];
  1205. }
  1206. buf[n] = '\0';
  1207. }
  1208. // Set max width & height of the proportional font
  1209. //-----------------------------
  1210. static void getMaxWidthHeight()
  1211. {
  1212. uint16_t tempPtr = 4; // point at first char data
  1213. uint8_t cc, cw, ch, cd, cy;
  1214. cfont.numchars = 0;
  1215. cfont.max_x_size = 0;
  1216. cc = cfont.font[tempPtr++];
  1217. while (cc != 0xFF) {
  1218. cfont.numchars++;
  1219. cy = cfont.font[tempPtr++];
  1220. cw = cfont.font[tempPtr++];
  1221. ch = cfont.font[tempPtr++];
  1222. tempPtr++;
  1223. cd = cfont.font[tempPtr++];
  1224. cy += ch;
  1225. if (cw > cfont.max_x_size) cfont.max_x_size = cw;
  1226. if (cd > cfont.max_x_size) cfont.max_x_size = cd;
  1227. if (ch > cfont.y_size) cfont.y_size = ch;
  1228. if (cy > cfont.y_size) cfont.y_size = cy;
  1229. if (cw != 0) {
  1230. // packed bits
  1231. tempPtr += (((cw * ch)-1) / 8) + 1;
  1232. }
  1233. cc = cfont.font[tempPtr++];
  1234. }
  1235. cfont.size = tempPtr;
  1236. }
  1237. // Return the Glyph data for an individual character in the proportional font
  1238. //------------------------------------
  1239. static uint8_t getCharPtr(uint8_t c) {
  1240. uint16_t tempPtr = 4; // point at first char data
  1241. do {
  1242. fontChar.charCode = cfont.font[tempPtr++];
  1243. if (fontChar.charCode == 0xFF) return 0;
  1244. fontChar.adjYOffset = cfont.font[tempPtr++];
  1245. fontChar.width = cfont.font[tempPtr++];
  1246. fontChar.height = cfont.font[tempPtr++];
  1247. fontChar.xOffset = cfont.font[tempPtr++];
  1248. fontChar.xOffset = fontChar.xOffset < 0x80 ? fontChar.xOffset : -(0xFF - fontChar.xOffset);
  1249. fontChar.xDelta = cfont.font[tempPtr++];
  1250. if (c != fontChar.charCode && fontChar.charCode != 0xFF) {
  1251. if (fontChar.width != 0) {
  1252. // packed bits
  1253. tempPtr += (((fontChar.width * fontChar.height)-1) / 8) + 1;
  1254. }
  1255. }
  1256. } while ((c != fontChar.charCode) && (fontChar.charCode != 0xFF));
  1257. fontChar.dataPtr = tempPtr;
  1258. if (c == fontChar.charCode) {
  1259. if (font_forceFixed > 0) {
  1260. // fix width & offset for forced fixed width
  1261. fontChar.xDelta = cfont.max_x_size;
  1262. fontChar.xOffset = (fontChar.xDelta - fontChar.width) / 2;
  1263. }
  1264. }
  1265. else return 0;
  1266. return 1;
  1267. }
  1268. /*
  1269. //-----------------------
  1270. static void _testFont() {
  1271. if (cfont.x_size) {
  1272. printf("FONT TEST: fixed font\r\n");
  1273. return;
  1274. }
  1275. uint16_t tempPtr = 4; // point at first char data
  1276. uint8_t c = 0x20;
  1277. for (c=0x20; c <0xFF; c++) {
  1278. fontChar.charCode = cfont.font[tempPtr++];
  1279. if (fontChar.charCode == 0xFF) break;
  1280. if (fontChar.charCode != c) {
  1281. printf("FONT TEST: last sequential char: %d, expected %d\r\n", fontChar.charCode, c);
  1282. break;
  1283. }
  1284. c = fontChar.charCode;
  1285. fontChar.adjYOffset = cfont.font[tempPtr++];
  1286. fontChar.width = cfont.font[tempPtr++];
  1287. fontChar.height = cfont.font[tempPtr++];
  1288. fontChar.xOffset = cfont.font[tempPtr++];
  1289. fontChar.xOffset = fontChar.xOffset < 0x80 ? fontChar.xOffset : -(0xFF - fontChar.xOffset);
  1290. fontChar.xDelta = cfont.font[tempPtr++];
  1291. if (fontChar.charCode != 0xFF) {
  1292. if (fontChar.width != 0) {
  1293. // packed bits
  1294. tempPtr += (((fontChar.width * fontChar.height)-1) / 8) + 1;
  1295. }
  1296. }
  1297. }
  1298. printf("FONT TEST: W=%d H=%d last char: %d [%c]; length: %d\r\n", cfont.max_x_size, cfont.y_size, c, c, tempPtr);
  1299. }
  1300. */
  1301. //===================================================
  1302. void EPD_setFont(uint8_t font, const char *font_file)
  1303. {
  1304. cfont.font = NULL;
  1305. if (font == FONT_7SEG) {
  1306. cfont.bitmap = 2;
  1307. cfont.x_size = 24;
  1308. cfont.y_size = 6;
  1309. cfont.offset = 0;
  1310. cfont.color = _fg;
  1311. }
  1312. else {
  1313. if (font == USER_FONT) {
  1314. if (load_file_font(font_file, 0) != 0) cfont.font = tft_DefaultFont;
  1315. else cfont.font = userfont;
  1316. }
  1317. else if (font == DEJAVU18_FONT) cfont.font = tft_Dejavu18;
  1318. else if (font == DEJAVU24_FONT) cfont.font = tft_Dejavu24;
  1319. else if (font == UBUNTU16_FONT) cfont.font = tft_Ubuntu16;
  1320. else if (font == COMIC24_FONT) cfont.font = tft_Comic24;
  1321. else if (font == MINYA24_FONT) cfont.font = tft_minya24;
  1322. else if (font == TOONEY32_FONT) cfont.font = tft_tooney32;
  1323. else if (font == SMALL_FONT) cfont.font = tft_SmallFont;
  1324. else cfont.font = tft_DefaultFont;
  1325. cfont.bitmap = 1;
  1326. cfont.x_size = cfont.font[0];
  1327. cfont.y_size = cfont.font[1];
  1328. if (cfont.x_size > 0) {
  1329. cfont.offset = cfont.font[2];
  1330. cfont.numchars = cfont.font[3];
  1331. cfont.size = cfont.x_size * cfont.y_size * cfont.numchars;
  1332. }
  1333. else {
  1334. cfont.offset = 4;
  1335. getMaxWidthHeight();
  1336. }
  1337. //_testFont();
  1338. }
  1339. }
  1340. // -----------------------------------------------------------------------------------------
  1341. // Individual Proportional Font Character Format:
  1342. // -----------------------------------------------------------------------------------------
  1343. // Character Code
  1344. // yOffset (start Y of visible pixels)
  1345. // Width (width of the visible pixels)
  1346. // Height (height of the visible pixels)
  1347. // xOffset (start X of visible pixels)
  1348. // xDelta (the distance to move the cursor. Effective width of the character.)
  1349. // Data[n]
  1350. // -----------------------------------------------------------------------------------------
  1351. //---------------------------------------------------------------------------------------------
  1352. // Character drawing rectangle is (0, 0) (xDelta-1, cfont.y_size-1)
  1353. // Character visible pixels rectangle is (xOffset, yOffset) (xOffset+Width-1, yOffset+Height-1)
  1354. //---------------------------------------------------------------------------------------------
  1355. // print non-rotated proportional character
  1356. // character is already in fontChar
  1357. //----------------------------------------------
  1358. static int printProportionalChar(int x, int y) {
  1359. uint8_t ch = 0;
  1360. int i, j, char_width;
  1361. char_width = ((fontChar.width > fontChar.xDelta) ? fontChar.width : fontChar.xDelta);
  1362. int cx, cy;
  1363. if (!font_transparent) _fillRect(x, y, char_width+1, cfont.y_size, _bg);
  1364. // draw Glyph
  1365. uint8_t mask = 0x80;
  1366. for (j=0; j < fontChar.height; j++) {
  1367. for (i=0; i < fontChar.width; i++) {
  1368. if (((i + (j*fontChar.width)) % 8) == 0) {
  1369. mask = 0x80;
  1370. ch = cfont.font[fontChar.dataPtr++];
  1371. }
  1372. if ((ch & mask) !=0) {
  1373. cx = (uint16_t)(x+fontChar.xOffset+i);
  1374. cy = (uint16_t)(y+j+fontChar.adjYOffset);
  1375. _drawPixel(cx, cy, _fg);
  1376. }
  1377. mask >>= 1;
  1378. }
  1379. }
  1380. return char_width;
  1381. }
  1382. // non-rotated fixed width character
  1383. //----------------------------------------------
  1384. static void printChar(uint8_t c, int x, int y) {
  1385. uint8_t i, j, ch, fz, mask;
  1386. uint16_t k, temp, cx, cy;
  1387. // fz = bytes per char row
  1388. fz = cfont.x_size/8;
  1389. if (cfont.x_size % 8) fz++;
  1390. // get character position in buffer
  1391. temp = ((c-cfont.offset)*((fz)*cfont.y_size))+4;
  1392. if (!font_transparent) _fillRect(x, y, cfont.x_size, cfont.y_size, _bg);
  1393. for (j=0; j<cfont.y_size; j++) {
  1394. for (k=0; k < fz; k++) {
  1395. ch = cfont.font[temp+k];
  1396. mask=0x80;
  1397. for (i=0; i<8; i++) {
  1398. if ((ch & mask) !=0) {
  1399. cx = (uint16_t)(x+i+(k*8));
  1400. cy = (uint16_t)(y+j);
  1401. _drawPixel(cx, cy, _fg);
  1402. }
  1403. mask >>= 1;
  1404. }
  1405. }
  1406. temp += (fz);
  1407. }
  1408. }
  1409. // print rotated proportional character
  1410. // character is already in fontChar
  1411. //---------------------------------------------------
  1412. static int rotatePropChar(int x, int y, int offset) {
  1413. uint8_t ch = 0;
  1414. double radian = font_rotate * DEG_TO_RAD;
  1415. float cos_radian = cos(radian);
  1416. float sin_radian = sin(radian);
  1417. uint8_t mask = 0x80;
  1418. for (int j=0; j < fontChar.height; j++) {
  1419. for (int i=0; i < fontChar.width; i++) {
  1420. if (((i + (j*fontChar.width)) % 8) == 0) {
  1421. mask = 0x80;
  1422. ch = cfont.font[fontChar.dataPtr++];
  1423. }
  1424. int newX = (int)(x + (((offset + i) * cos_radian) - ((j+fontChar.adjYOffset)*sin_radian)));
  1425. int newY = (int)(y + (((j+fontChar.adjYOffset) * cos_radian) + ((offset + i) * sin_radian)));
  1426. if ((ch & mask) != 0) _drawPixel(newX,newY,_fg);
  1427. else if (!font_transparent) _drawPixel(newX,newY,_bg);
  1428. mask >>= 1;
  1429. }
  1430. }
  1431. return fontChar.xDelta+1;
  1432. }
  1433. // rotated fixed width character
  1434. //--------------------------------------------------------
  1435. static void rotateChar(uint8_t c, int x, int y, int pos) {
  1436. uint8_t i,j,ch,fz,mask;
  1437. uint16_t temp;
  1438. int newx,newy;
  1439. double radian = font_rotate*0.0175;
  1440. float cos_radian = cos(radian);
  1441. float sin_radian = sin(radian);
  1442. int zz;
  1443. if( cfont.x_size < 8 ) fz = cfont.x_size;
  1444. else fz = cfont.x_size/8;
  1445. temp=((c-cfont.offset)*((fz)*cfont.y_size))+4;
  1446. for (j=0; j<cfont.y_size; j++) {
  1447. for (zz=0; zz<(fz); zz++) {
  1448. ch = cfont.font[temp+zz];
  1449. mask = 0x80;
  1450. for (i=0; i<8; i++) {
  1451. newx=(int)(x+(((i+(zz*8)+(pos*cfont.x_size))*cos_radian)-((j)*sin_radian)));
  1452. newy=(int)(y+(((j)*cos_radian)+((i+(zz*8)+(pos*cfont.x_size))*sin_radian)));
  1453. if ((ch & mask) != 0) _drawPixel(newx,newy,_fg);
  1454. else if (!font_transparent) _drawPixel(newx,newy,_bg);
  1455. mask >>= 1;
  1456. }
  1457. }
  1458. temp+=(fz);
  1459. }
  1460. // calculate x,y for the next char
  1461. EPD_X = (int)(x + ((pos+1) * cfont.x_size * cos_radian));
  1462. EPD_Y = (int)(y + ((pos+1) * cfont.x_size * sin_radian));
  1463. }
  1464. //----------------------
  1465. static int _7seg_width()
  1466. {
  1467. return (2 * (2 * cfont.y_size + 1)) + cfont.x_size;
  1468. }
  1469. //-----------------------
  1470. static int _7seg_height()
  1471. {
  1472. return (3 * (2 * cfont.y_size + 1)) + (2 * cfont.x_size);
  1473. }
  1474. // Returns the string width in pixels.
  1475. // Useful for positions strings on the screen.
  1476. //===============================
  1477. int EPD_getStringWidth(char* str)
  1478. {
  1479. int strWidth = 0;
  1480. if (cfont.bitmap == 2) strWidth = ((_7seg_width()+2) * strlen(str)) - 2; // 7-segment font
  1481. else if (cfont.x_size != 0) strWidth = strlen(str) * cfont.x_size; // fixed width font
  1482. else {
  1483. // calculate the width of the string of proportional characters
  1484. char* tempStrptr = str;
  1485. while (*tempStrptr != 0) {
  1486. if (getCharPtr(*tempStrptr++)) {
  1487. strWidth += (((fontChar.width > fontChar.xDelta) ? fontChar.width : fontChar.xDelta) + font_x_space);
  1488. }
  1489. }
  1490. strWidth -= font_x_space;
  1491. }
  1492. return strWidth;
  1493. }
  1494. //===============================================
  1495. void EPD_clearStringRect(int x, int y, char *str)
  1496. {
  1497. int w = EPD_getStringWidth(str);
  1498. int h = EPD_getfontheight();
  1499. EPD_fillRect(x+dispWin.x1, y+dispWin.y1, w, h, _bg);
  1500. }
  1501. void EPD_frameStringRect(int x, int y, char *str)
  1502. {
  1503. int w = EPD_getStringWidth(str);
  1504. int h = EPD_getfontheight();
  1505. EPD_drawRect(x+dispWin.x1, y+dispWin.y1, w, h, _fg);
  1506. }
  1507. //==============================================================================
  1508. /**
  1509. * bit-encoded bar position of all digits' bcd segments
  1510. *
  1511. * 6
  1512. * +-----+
  1513. * 3 | . | 2
  1514. * +--5--+
  1515. * 1 | . | 0
  1516. * +--.--+
  1517. * 4
  1518. */
  1519. static const uint16_t font_bcd[] = {
  1520. 0x200, // 0010 0000 0000 // -
  1521. 0x080, // 0000 1000 0000 // .
  1522. 0x06C, // 0100 0110 1100 // /, degree
  1523. 0x05f, // 0000 0101 1111, // 0
  1524. 0x005, // 0000 0000 0101, // 1
  1525. 0x076, // 0000 0111 0110, // 2
  1526. 0x075, // 0000 0111 0101, // 3
  1527. 0x02d, // 0000 0010 1101, // 4
  1528. 0x079, // 0000 0111 1001, // 5
  1529. 0x07b, // 0000 0111 1011, // 6
  1530. 0x045, // 0000 0100 0101, // 7
  1531. 0x07f, // 0000 0111 1111, // 8
  1532. 0x07d, // 0000 0111 1101 // 9
  1533. 0x900 // 1001 0000 0000 // :
  1534. };
  1535. //-----------------------------------------------------------------------------------------------
  1536. static void barVert(int16_t x, int16_t y, int16_t w, int16_t l, color_t color, color_t outline) {
  1537. _fillTriangle(x+1, y+2*w, x+w, y+w+1, x+2*w-1, y+2*w, color);
  1538. _fillTriangle(x+1, y+2*w+l+1, x+w, y+3*w+l, x+2*w-1, y+2*w+l+1, color);
  1539. _fillRect(x, y+2*w+1, 2*w+1, l, color);
  1540. if (cfont.offset) {
  1541. _drawTriangle(x+1, y+2*w, x+w, y+w+1, x+2*w-1, y+2*w, outline);
  1542. _drawTriangle(x+1, y+2*w+l+1, x+w, y+3*w+l, x+2*w-1, y+2*w+l+1, outline);
  1543. _drawRect(x, y+2*w+1, 2*w+1, l, outline);
  1544. }
  1545. }
  1546. //----------------------------------------------------------------------------------------------
  1547. static void barHor(int16_t x, int16_t y, int16_t w, int16_t l, color_t color, color_t outline) {
  1548. _fillTriangle(x+2*w, y+2*w-1, x+w+1, y+w, x+2*w, y+1, color);
  1549. _fillTriangle(x+2*w+l+1, y+2*w-1, x+3*w+l, y+w, x+2*w+l+1, y+1, color);
  1550. _fillRect(x+2*w+1, y, l, 2*w+1, color);
  1551. if (cfont.offset) {
  1552. _drawTriangle(x+2*w, y+2*w-1, x+w+1, y+w, x+2*w, y+1, outline);
  1553. _drawTriangle(x+2*w+l+1, y+2*w-1, x+3*w+l, y+w, x+2*w+l+1, y+1, outline);
  1554. _drawRect(x+2*w+1, y, l, 2*w+1, outline);
  1555. }
  1556. }
  1557. //--------------------------------------------------------------------------------------------
  1558. static void _draw7seg(int16_t x, int16_t y, int8_t num, int16_t w, int16_t l, color_t color) {
  1559. /* TODO: clipping */
  1560. if (num < 0x2D || num > 0x3A) return;
  1561. int16_t c = font_bcd[num-0x2D];
  1562. int16_t d = 2*w+l+1;
  1563. // === Clear unused segments ===
  1564. /*
  1565. if (!(c & 0x001)) barVert(x+d, y+d, w, l, _bg, _bg);
  1566. if (!(c & 0x002)) barVert(x, y+d, w, l, _bg, _bg);
  1567. if (!(c & 0x004)) barVert(x+d, y, w, l, _bg, _bg);
  1568. if (!(c & 0x008)) barVert(x, y, w, l, _bg, _bg);
  1569. if (!(c & 0x010)) barHor(x, y+2*d, w, l, _bg, _bg);
  1570. if (!(c & 0x020)) barHor(x, y+d, w, l, _bg, _bg);
  1571. if (!(c & 0x040)) barHor(x, y, w, l, _bg, _bg);
  1572. if (!(c & 0x080)) {
  1573. // low point
  1574. _fillRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, _bg);
  1575. if (cfont.offset) _drawRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, _bg);
  1576. }
  1577. if (!(c & 0x100)) {
  1578. // down middle point
  1579. _fillRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, _bg);
  1580. if (cfont.offset) _drawRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, _bg);
  1581. }
  1582. if (!(c & 0x800)) {
  1583. // up middle point
  1584. _fillRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, _bg);
  1585. if (cfont.offset) _drawRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, _bg);
  1586. }
  1587. if (!(c & 0x200)) {
  1588. // middle, minus
  1589. _fillRect(x+2*w+1, y+d, l, 2*w+1, _bg);
  1590. if (cfont.offset) _drawRect(x+2*w+1, y+d, l, 2*w+1, _bg);
  1591. }
  1592. */
  1593. barVert(x+d, y+d, w, l, _bg, _bg);
  1594. barVert(x, y+d, w, l, _bg, _bg);
  1595. barVert(x+d, y, w, l, _bg, _bg);
  1596. barVert(x, y, w, l, _bg, _bg);
  1597. barHor(x, y+2*d, w, l, _bg, _bg);
  1598. barHor(x, y+d, w, l, _bg, _bg);
  1599. barHor(x, y, w, l, _bg, _bg);
  1600. _fillRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, _bg);
  1601. _drawRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, _bg);
  1602. _fillRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, _bg);
  1603. _drawRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, _bg);
  1604. _fillRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, _bg);
  1605. _drawRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, _bg);
  1606. _fillRect(x+2*w+1, y+d, l, 2*w+1, _bg);
  1607. _drawRect(x+2*w+1, y+d, l, 2*w+1, _bg);
  1608. // === Draw used segments ===
  1609. if (c & 0x001) barVert(x+d, y+d, w, l, color, cfont.color); // down right
  1610. if (c & 0x002) barVert(x, y+d, w, l, color, cfont.color); // down left
  1611. if (c & 0x004) barVert(x+d, y, w, l, color, cfont.color); // up right
  1612. if (c & 0x008) barVert(x, y, w, l, color, cfont.color); // up left
  1613. if (c & 0x010) barHor(x, y+2*d, w, l, color, cfont.color); // down
  1614. if (c & 0x020) barHor(x, y+d, w, l, color, cfont.color); // middle
  1615. if (c & 0x040) barHor(x, y, w, l, color, cfont.color); // up
  1616. if (c & 0x080) {
  1617. // low point
  1618. _fillRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, color);
  1619. if (cfont.offset) _drawRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, cfont.color);
  1620. }
  1621. if (c & 0x100) {
  1622. // down middle point
  1623. _fillRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, color);
  1624. if (cfont.offset) _drawRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, cfont.color);
  1625. }
  1626. if (c & 0x800) {
  1627. // up middle point
  1628. _fillRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, color);
  1629. if (cfont.offset) _drawRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, cfont.color);
  1630. }
  1631. if (c & 0x200) {
  1632. // middle, minus
  1633. _fillRect(x+2*w+1, y+d, l, 2*w+1, color);
  1634. if (cfont.offset) _drawRect(x+2*w+1, y+d, l, 2*w+1, cfont.color);
  1635. }
  1636. }
  1637. //==============================================================================
  1638. //======================================
  1639. void EPD_print(char *st, int x, int y) {
  1640. int stl, i, tmpw, tmph, fh;
  1641. uint8_t ch;
  1642. if (cfont.bitmap == 0) return; // wrong font selected
  1643. // ** Rotated strings cannot be aligned
  1644. if ((font_rotate != 0) && ((x <= CENTER) || (y <= CENTER))) return;
  1645. if ((x < LASTX) || (font_rotate == 0)) EPD_OFFSET = 0;
  1646. if ((x >= LASTX) && (x < LASTY)) x = EPD_X + (x-LASTX);
  1647. else if (x > CENTER) x += dispWin.x1;
  1648. if (y >= LASTY) y = EPD_Y + (y-LASTY);
  1649. else if (y > CENTER) y += dispWin.y1;
  1650. // ** Get number of characters in string to print
  1651. stl = strlen(st);
  1652. // ** Calculate CENTER, RIGHT or BOTTOM position
  1653. tmpw = EPD_getStringWidth(st); // string width in pixels
  1654. fh = cfont.y_size; // font height
  1655. if ((cfont.x_size != 0) && (cfont.bitmap == 2)) {
  1656. // 7-segment font
  1657. fh = (3 * (2 * cfont.y_size + 1)) + (2 * cfont.x_size); // 7-seg character height
  1658. }
  1659. if (x == RIGHT) x = dispWin.x2 - tmpw + dispWin.x1;
  1660. else if (x == CENTER) x = (((dispWin.x2 - dispWin.x1 + 1) - tmpw) / 2) + dispWin.x1;
  1661. if (y == BOTTOM) y = dispWin.y2 - fh + dispWin.y1;
  1662. else if (y==CENTER) y = (((dispWin.y2 - dispWin.y1 + 1) - (fh/2)) / 2) + dispWin.y1;
  1663. if (x < dispWin.x1) x = dispWin.x1;
  1664. if (y < dispWin.y1) y = dispWin.y1;
  1665. if ((x > dispWin.x2) || (y > dispWin.y2)) return;
  1666. EPD_X = x;
  1667. EPD_Y = y;
  1668. // ** Adjust y position
  1669. tmph = cfont.y_size; // font height
  1670. // for non-proportional fonts, char width is the same for all chars
  1671. tmpw = cfont.x_size;
  1672. if (cfont.x_size != 0) {
  1673. if (cfont.bitmap == 2) { // 7-segment font
  1674. tmpw = _7seg_width(); // character width
  1675. tmph = _7seg_height(); // character height
  1676. }
  1677. }
  1678. else EPD_OFFSET = 0; // fixed font; offset not needed
  1679. if (( EPD_Y + tmph - 1) > dispWin.y2) return;
  1680. int offset = EPD_OFFSET;
  1681. for (i=0; i<stl; i++) {
  1682. ch = st[i]; // get string character
  1683. if (ch == 0x0D) { // === '\r', erase to eol ====
  1684. if ((!font_transparent) && (font_rotate==0)) _fillRect( EPD_X, EPD_Y, dispWin.x2+1- EPD_X, tmph, _bg);
  1685. }
  1686. else if (ch == 0x0A) { // ==== '\n', new line ====
  1687. if (cfont.bitmap == 1) {
  1688. EPD_Y += tmph + font_line_space;
  1689. if ( EPD_Y > (dispWin.y2-tmph)) break;
  1690. EPD_X = dispWin.x1;
  1691. }
  1692. }
  1693. else { // ==== other characters ====
  1694. if (cfont.x_size == 0) {
  1695. // for proportional font get character data to 'fontChar'
  1696. if (getCharPtr(ch)) tmpw = fontChar.xDelta;
  1697. else continue;
  1698. }
  1699. // check if character can be displayed in the current line
  1700. if (( EPD_X+tmpw) > (dispWin.x2)) {
  1701. if (text_wrap == 0) break;
  1702. EPD_Y += tmph + font_line_space;
  1703. if ( EPD_Y > (dispWin.y2-tmph)) break;
  1704. EPD_X = dispWin.x1;
  1705. }
  1706. // Let's print the character
  1707. if (cfont.x_size == 0) {
  1708. // == proportional font
  1709. if (font_rotate == 0) EPD_X += printProportionalChar( EPD_X, EPD_Y) + font_x_space;
  1710. else {
  1711. // rotated proportional font
  1712. offset += rotatePropChar(x, y, offset);
  1713. EPD_OFFSET = offset;
  1714. }
  1715. }
  1716. else {
  1717. if (cfont.bitmap == 1) {
  1718. // == fixed font
  1719. if ((ch < cfont.offset) || ((ch-cfont.offset) > cfont.numchars)) ch = cfont.offset;
  1720. if (font_rotate == 0) {
  1721. printChar(ch, EPD_X, EPD_Y);
  1722. EPD_X += tmpw;
  1723. }
  1724. else rotateChar(ch, x, y, i);
  1725. }
  1726. else if (cfont.bitmap == 2) {
  1727. // == 7-segment font ==
  1728. _draw7seg( EPD_X, EPD_Y, ch, cfont.y_size, cfont.x_size, _fg);
  1729. EPD_X += (tmpw + 2);
  1730. }
  1731. }
  1732. }
  1733. }
  1734. }
  1735. // ================ Service functions ==========================================
  1736. //=====================================================================
  1737. void EPD_setclipwin(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
  1738. {
  1739. dispWin.x1 = x1;
  1740. dispWin.y1 = y1;
  1741. dispWin.x2 = x2;
  1742. dispWin.y2 = y2;
  1743. if (dispWin.x2 >= EPD_DISPLAY_WIDTH) dispWin.x2 = EPD_DISPLAY_WIDTH-1;
  1744. if (dispWin.y2 >= EPD_DISPLAY_HEIGHT) dispWin.y2 = EPD_DISPLAY_HEIGHT-1;
  1745. if (dispWin.x1 > dispWin.x2) dispWin.x1 = dispWin.x2;
  1746. if (dispWin.y1 > dispWin.y2) dispWin.y1 = dispWin.y2;
  1747. }
  1748. //=====================
  1749. void EPD_resetclipwin()
  1750. {
  1751. dispWin.x2 = EPD_DISPLAY_WIDTH-1;
  1752. dispWin.y2 = EPD_DISPLAY_HEIGHT-1;
  1753. dispWin.x1 = 0;
  1754. dispWin.y1 = 0;
  1755. }
  1756. //==========================================================================
  1757. void set_7seg_font_atrib(uint8_t l, uint8_t w, int outline, color_t color) {
  1758. if (cfont.bitmap != 2) return;
  1759. if (l < 6) l = 6;
  1760. if (l > 40) l = 40;
  1761. if (w < 1) w = 1;
  1762. if (w > (l/2)) w = l/2;
  1763. if (w > 12) w = 12;
  1764. cfont.x_size = l;
  1765. cfont.y_size = w;
  1766. cfont.offset = outline;
  1767. cfont.color = color;
  1768. }
  1769. //==========================================
  1770. int EPD_getfontsize(int *width, int* height)
  1771. {
  1772. if (cfont.bitmap == 1) {
  1773. if (cfont.x_size != 0) *width = cfont.x_size; // fixed width font
  1774. else *width = cfont.max_x_size; // proportional font
  1775. *height = cfont.y_size;
  1776. }
  1777. else if (cfont.bitmap == 2) {
  1778. // 7-segment font
  1779. *width = _7seg_width();
  1780. *height = _7seg_height();
  1781. }
  1782. else {
  1783. *width = 0;
  1784. *height = 0;
  1785. return 0;
  1786. }
  1787. return 1;
  1788. }
  1789. //=====================
  1790. int EPD_getfontheight()
  1791. {
  1792. if (cfont.bitmap == 1) return cfont.y_size; // Bitmap font
  1793. else if (cfont.bitmap == 2) return _7seg_height(); // 7-segment font
  1794. return 0;
  1795. }
  1796. //====================
  1797. void EPD_saveClipWin()
  1798. {
  1799. dispWinTemp.x1 = dispWin.x1;
  1800. dispWinTemp.y1 = dispWin.y1;
  1801. dispWinTemp.x2 = dispWin.x2;
  1802. dispWinTemp.y2 = dispWin.y2;
  1803. }
  1804. //=======================
  1805. void EPD_restoreClipWin()
  1806. {
  1807. dispWin.x1 = dispWinTemp.x1;
  1808. dispWin.y1 = dispWinTemp.y1;
  1809. dispWin.x2 = dispWinTemp.x2;
  1810. dispWin.y2 = dispWinTemp.y2;
  1811. }
  1812. // ================ JPG SUPPORT ================================================
  1813. // RGB to GRAYSCALE constants
  1814. // 0.2989 0.5870 0.1140
  1815. #define GS_FACT_R 0.2989
  1816. #define GS_FACT_G 0.4870
  1817. #define GS_FACT_B 0.2140
  1818. // User defined device identifier
  1819. typedef struct {
  1820. FILE *fhndl; // File handler for input function
  1821. int x; // image top left point X position
  1822. int y; // image top left point Y position
  1823. uint8_t *membuff; // memory buffer containing the image
  1824. uint32_t bufsize; // size of the memory buffer
  1825. uint32_t bufptr; // memory buffer current position
  1826. } JPGIODEV;
  1827. // User defined call-back function to input JPEG data from file
  1828. //---------------------
  1829. static UINT tjd_input (
  1830. JDEC* jd, // Decompression object
  1831. BYTE* buff, // Pointer to the read buffer (NULL:skip)
  1832. UINT nd // Number of bytes to read/skip from input stream
  1833. )
  1834. {
  1835. int rb = 0;
  1836. // Device identifier for the session (5th argument of jd_prepare function)
  1837. JPGIODEV *dev = (JPGIODEV*)jd->device;
  1838. if (buff) { // Read nd bytes from the input strem
  1839. rb = fread(buff, 1, nd, dev->fhndl);
  1840. return rb; // Returns actual number of bytes read
  1841. }
  1842. else { // Remove nd bytes from the input stream
  1843. if (fseek(dev->fhndl, nd, SEEK_CUR) >= 0) return nd;
  1844. else return 0;
  1845. }
  1846. }
  1847. // User defined call-back function to input JPEG data from memory buffer
  1848. //-------------------------
  1849. static UINT tjd_buf_input (
  1850. JDEC* jd, // Decompression object
  1851. BYTE* buff, // Pointer to the read buffer (NULL:skip)
  1852. UINT nd // Number of bytes to read/skip from input stream
  1853. )
  1854. {
  1855. // Device identifier for the session (5th argument of jd_prepare function)
  1856. JPGIODEV *dev = (JPGIODEV*)jd->device;
  1857. if (!dev->membuff) return 0;
  1858. if (dev->bufptr >= (dev->bufsize + 2)) return 0; // end of stream
  1859. if ((dev->bufptr + nd) > (dev->bufsize + 2)) nd = (dev->bufsize + 2) - dev->bufptr;
  1860. if (buff) { // Read nd bytes from the input strem
  1861. memcpy(buff, dev->membuff + dev->bufptr, nd);
  1862. dev->bufptr += nd;
  1863. return nd; // Returns number of bytes read
  1864. }
  1865. else { // Remove nd bytes from the input stream
  1866. dev->bufptr += nd;
  1867. return nd;
  1868. }
  1869. }
  1870. // User defined call-back function to output RGB bitmap to display device
  1871. //----------------------
  1872. static UINT tjd_output (
  1873. JDEC* jd, // Decompression object of current session
  1874. void* bitmap, // Bitmap data to be output
  1875. JRECT* rect // Rectangular region to output
  1876. )
  1877. {
  1878. // Device identifier for the session (5th argument of jd_prepare function)
  1879. JPGIODEV *dev = (JPGIODEV*)jd->device;
  1880. // ** Put the rectangular into the display device **
  1881. int x;
  1882. int y;
  1883. int dleft, dtop, dright, dbottom;
  1884. BYTE *src = (BYTE*)bitmap;
  1885. int left = rect->left + dev->x;
  1886. int top = rect->top + dev->y;
  1887. int right = rect->right + dev->x;
  1888. int bottom = rect->bottom + dev->y;
  1889. if ((left > dispWin.x2) || (top > dispWin.y2)) return 1; // out of screen area, return
  1890. if ((right < dispWin.x1) || (bottom < dispWin.y1)) return 1;// out of screen area, return
  1891. if (left < dispWin.x1) dleft = dispWin.x1;
  1892. else dleft = left;
  1893. if (top < dispWin.y1) dtop = dispWin.y1;
  1894. else dtop = top;
  1895. if (right > dispWin.x2) dright = dispWin.x2;
  1896. else dright = right;
  1897. if (bottom > dispWin.y2) dbottom = dispWin.y2;
  1898. else dbottom = bottom;
  1899. if ((dleft > dispWin.x2) || (dtop > dispWin.y2)) return 1; // out of screen area, return
  1900. if ((dright < dispWin.x1) || (dbottom < dispWin.y1)) return 1; // out of screen area, return
  1901. uint32_t len = ((dright-dleft+1) * (dbottom-dtop+1)); // calculate length of data
  1902. float gs_clr = 0;
  1903. uint8_t rgb_color[3];
  1904. uint8_t last_lvl, i;
  1905. uint8_t pix;
  1906. if ((len > 0) && (len <= JPG_IMAGE_LINE_BUF_SIZE)) {
  1907. for (y = top; y <= bottom; y++) {
  1908. for (x = left; x <= right; x++) {
  1909. // Clip to display area
  1910. if ((x >= dleft) && (y >= dtop) && (x <= dright) && (y <= dbottom)) {
  1911. // Directly convert color to 4-bit gray scale
  1912. pix = 0;
  1913. pix |= ((*src++) >> 4) & 0x08;
  1914. pix |= ((*src++) >> 5) & 0x06;
  1915. pix |= ((*src++) >> 7);
  1916. pix ^= 0x0F;
  1917. /* Convert rgb color to gray scale
  1918. memcpy(rgb_color, src, 3);
  1919. src += 3;
  1920. gs_clr = (GS_FACT_R * rgb_color[0]) + (GS_FACT_G * rgb_color[1]) + (GS_FACT_B * rgb_color[2]);
  1921. if (gs_clr > 255) gs_clr = 255;
  1922. // Use only 4 bits & invert
  1923. //pix = ((uint8_t)gs_clr >> 4) ^ 0x0F;
  1924. pix = (uint8_t)gs_clr;
  1925. // Using gray scale lookup table
  1926. last_lvl = 0;
  1927. i = 0;
  1928. for (i=0; i<16; i++) {
  1929. if ((pix > last_lvl) && (pix <= lvl_buf_jpg[i])) {
  1930. pix = 15 - i;
  1931. last_lvl = lvl_buf[i];
  1932. break;
  1933. }
  1934. last_lvl = lvl_buf[i];
  1935. }
  1936. */
  1937. gs_disp_buffer[(y * EPD_DISPLAY_WIDTH) + x] = pix;
  1938. gs_used_shades |= (1 << pix);
  1939. }
  1940. else src += 3; // skip
  1941. }
  1942. }
  1943. }
  1944. else {
  1945. 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);
  1946. return 0; // stop decompression
  1947. }
  1948. return 1; // Continue to decompression
  1949. }
  1950. // X & Y can be < 0 !
  1951. //=================================================================================
  1952. int EPD_jpg_image(int x, int y, uint8_t scale, char *fname, uint8_t *buf, int size)
  1953. {
  1954. JPGIODEV dev;
  1955. struct stat sb;
  1956. char *work = NULL; // Pointer to the working buffer (must be 4-byte aligned)
  1957. UINT sz_work = 3800; // Size of the working buffer (must be power of 2)
  1958. JDEC jd; // Decompression object (70 bytes)
  1959. JRESULT rc;
  1960. int res = -10;
  1961. if (fname == NULL) {
  1962. // image from buffer
  1963. dev.fhndl = NULL;
  1964. dev.membuff = buf;
  1965. dev.bufsize = size;
  1966. dev.bufptr = 0;
  1967. }
  1968. else {
  1969. // image from file
  1970. dev.membuff = NULL;
  1971. dev.bufsize = 0;
  1972. dev.bufptr = 0;
  1973. if (stat(fname, &sb) != 0) {
  1974. if (image_debug) printf("File error: %ss\r\n", strerror(errno));
  1975. res = -11;
  1976. goto exit;
  1977. }
  1978. dev.fhndl = fopen(fname, "r");
  1979. if (!dev.fhndl) {
  1980. if (image_debug) printf("Error opening file: %s\r\n", strerror(errno));
  1981. res = -12;
  1982. goto exit;
  1983. }
  1984. }
  1985. if (scale > 3) scale = 3;
  1986. work = malloc(sz_work);
  1987. if (work) {
  1988. if (dev.membuff) rc = jd_prepare(&jd, tjd_buf_input, (void *)work, sz_work, &dev);
  1989. else rc = jd_prepare(&jd, tjd_input, (void *)work, sz_work, &dev);
  1990. if (rc == JDR_OK) {
  1991. if (x == CENTER) x = ((dispWin.x2 - dispWin.x1 + 1 - (int)(jd.width >> scale)) / 2) + dispWin.x1;
  1992. else if (x == RIGHT) x = dispWin.x2 + 1 - (int)(jd.width >> scale);
  1993. if (y == CENTER) y = ((dispWin.y2 - dispWin.y1 + 1 - (int)(jd.height >> scale)) / 2) + dispWin.y1;
  1994. else if (y == BOTTOM) y = dispWin.y2 + 1 - (int)(jd.height >> scale);
  1995. if (x < ((dispWin.x2-1) * -1)) x = (dispWin.x2-1) * -1;
  1996. if (y < ((dispWin.y2-1)) * -1) y = (dispWin.y2-1) * -1;
  1997. if (x > (dispWin.x2-1)) x = dispWin.x2 - 1;
  1998. if (y > (dispWin.y2-1)) y = dispWin.y2-1;
  1999. dev.x = x;
  2000. dev.y = y;
  2001. // Start to decode the JPEG file
  2002. rc = jd_decomp(&jd, tjd_output, scale);
  2003. if (rc != JDR_OK) {
  2004. if (image_debug) printf("jpg decompression error %d\r\n", rc);
  2005. res = rc * -1;
  2006. }
  2007. res = 0;
  2008. if (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);
  2009. }
  2010. else {
  2011. if (image_debug) printf("jpg prepare error %d\r\n", rc);
  2012. res = rc * -1;
  2013. }
  2014. }
  2015. else {
  2016. if (image_debug) printf("work buffer allocation error\r\n");
  2017. res = -13;
  2018. }
  2019. exit:
  2020. if (work) free(work); // free work buffer
  2021. if (dev.fhndl) fclose(dev.fhndl); // close input file
  2022. return res;
  2023. }