GFX_FUNCTIONS.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. /*
  2. * GFX_FUNCTIONS.c
  3. *
  4. * Created on: 30-Oct-2020
  5. * Author: meh
  6. */
  7. #include <ST7735.h>
  8. #include "stdint.h"
  9. #include "stdlib.h"
  10. extern int16_t _width; ///< Display width as modified by current rotation
  11. extern int16_t _height; ///< Display height as modified by current rotation
  12. extern int16_t cursor_x; ///< x location to start print()ing text
  13. extern int16_t cursor_y; ///< y location to start print()ing text
  14. extern uint8_t rotation; ///< Display rotation (0 thru 3)
  15. extern uint8_t _colstart; ///< Some displays need this changed to offset
  16. extern uint8_t _rowstart; ///< Some displays need this changed to offset
  17. extern uint8_t _xstart;
  18. extern uint8_t _ystart;
  19. void drawPixel(int16_t x, int16_t y, uint16_t color)
  20. {
  21. ST7735_DrawPixel(x, y, color);
  22. }
  23. void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
  24. {
  25. ST7735_FillRectangle(x, y, w, h, color);
  26. }
  27. /***********************************************************************************************************/
  28. #define _swap_int16_t(a, b) \
  29. { \
  30. int16_t t = a; \
  31. a = b; \
  32. b = t; \
  33. }
  34. #define min(a, b) (((a) < (b)) ? (a) : (b))
  35. void writePixel(int16_t x, int16_t y, uint16_t color)
  36. {
  37. drawPixel(x, y, color);
  38. }
  39. void writeLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color)
  40. {
  41. int16_t steep = abs(y1 - y0) > abs(x1 - x0);
  42. if (steep) {
  43. _swap_int16_t(x0, y0);
  44. _swap_int16_t(x1, y1);
  45. }
  46. if (x0 > x1) {
  47. _swap_int16_t(x0, x1);
  48. _swap_int16_t(y0, y1);
  49. }
  50. int16_t dx, dy;
  51. dx = x1 - x0;
  52. dy = abs(y1 - y0);
  53. int16_t err = dx / 2;
  54. int16_t ystep;
  55. if (y0 < y1) {
  56. ystep = 1;
  57. } else {
  58. ystep = -1;
  59. }
  60. for (; x0<=x1; x0++) {
  61. if (steep) {
  62. writePixel(y0, x0, color);
  63. } else {
  64. writePixel(x0, y0, color);
  65. }
  66. err -= dy;
  67. if (err < 0) {
  68. y0 += ystep;
  69. err += dx;
  70. }
  71. }
  72. }
  73. void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color)
  74. {
  75. writeLine(x, y, x, y + h - 1, color);
  76. }
  77. void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color)
  78. {
  79. writeLine(x, y, x + w - 1, y, color);
  80. }
  81. void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color)
  82. {
  83. if(x0 == x1){
  84. if(y0 > y1) _swap_int16_t(y0, y1);
  85. drawFastVLine(x0, y0, y1 - y0 + 1, color);
  86. } else if(y0 == y1){
  87. if(x0 > x1) _swap_int16_t(x0, x1);
  88. drawFastHLine(x0, y0, x1 - x0 + 1, color);
  89. } else {
  90. writeLine(x0, y0, x1, y1, color);
  91. }
  92. }
  93. void drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color)
  94. {
  95. int16_t f = 1 - r;
  96. int16_t ddF_x = 1;
  97. int16_t ddF_y = -2 * r;
  98. int16_t x = 0;
  99. int16_t y = r;
  100. writePixel(x0 , y0+r, color);
  101. writePixel(x0 , y0-r, color);
  102. writePixel(x0+r, y0 , color);
  103. writePixel(x0-r, y0 , color);
  104. while (x<y) {
  105. if (f >= 0) {
  106. y--;
  107. ddF_y += 2;
  108. f += ddF_y;
  109. }
  110. x++;
  111. ddF_x += 2;
  112. f += ddF_x;
  113. writePixel(x0 + x, y0 + y, color);
  114. writePixel(x0 - x, y0 + y, color);
  115. writePixel(x0 + x, y0 - y, color);
  116. writePixel(x0 - x, y0 - y, color);
  117. writePixel(x0 + y, y0 + x, color);
  118. writePixel(x0 - y, y0 + x, color);
  119. writePixel(x0 + y, y0 - x, color);
  120. writePixel(x0 - y, y0 - x, color);
  121. }
  122. }
  123. void drawCircleHelper( int16_t x0, int16_t y0, int16_t r, uint8_t cornername, uint16_t color)
  124. {
  125. int16_t f = 1 - r;
  126. int16_t ddF_x = 1;
  127. int16_t ddF_y = -2 * r;
  128. int16_t x = 0;
  129. int16_t y = r;
  130. while (x<y) {
  131. if (f >= 0) {
  132. y--;
  133. ddF_y += 2;
  134. f += ddF_y;
  135. }
  136. x++;
  137. ddF_x += 2;
  138. f += ddF_x;
  139. if (cornername & 0x4) {
  140. writePixel(x0 + x, y0 + y, color);
  141. writePixel(x0 + y, y0 + x, color);
  142. }
  143. if (cornername & 0x2) {
  144. writePixel(x0 + x, y0 - y, color);
  145. writePixel(x0 + y, y0 - x, color);
  146. }
  147. if (cornername & 0x8) {
  148. writePixel(x0 - y, y0 + x, color);
  149. writePixel(x0 - x, y0 + y, color);
  150. }
  151. if (cornername & 0x1) {
  152. writePixel(x0 - y, y0 - x, color);
  153. writePixel(x0 - x, y0 - y, color);
  154. }
  155. }
  156. }
  157. void fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t corners, int16_t delta, uint16_t color)
  158. {
  159. int16_t f = 1 - r;
  160. int16_t ddF_x = 1;
  161. int16_t ddF_y = -2 * r;
  162. int16_t x = 0;
  163. int16_t y = r;
  164. int16_t px = x;
  165. int16_t py = y;
  166. delta++; // Avoid some +1's in the loop
  167. while(x < y) {
  168. if (f >= 0) {
  169. y--;
  170. ddF_y += 2;
  171. f += ddF_y;
  172. }
  173. x++;
  174. ddF_x += 2;
  175. f += ddF_x;
  176. // These checks avoid double-drawing certain lines, important
  177. // for the SSD1306 library which has an INVERT drawing mode.
  178. if(x < (y + 1)) {
  179. if(corners & 1) drawFastVLine(x0+x, y0-y, 2*y+delta, color);
  180. if(corners & 2) drawFastVLine(x0-x, y0-y, 2*y+delta, color);
  181. }
  182. if(y != py) {
  183. if(corners & 1) drawFastVLine(x0+py, y0-px, 2*px+delta, color);
  184. if(corners & 2) drawFastVLine(x0-py, y0-px, 2*px+delta, color);
  185. py = y;
  186. }
  187. px = x;
  188. }
  189. }
  190. void fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color)
  191. {
  192. drawFastVLine(x0, y0-r, 2*r+1, color);
  193. fillCircleHelper(x0, y0, r, 3, 0, color);
  194. }
  195. void drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
  196. {
  197. drawFastHLine(x, y, w, color);
  198. drawFastHLine(x, y+h-1, w, color);
  199. drawFastVLine(x, y, h, color);
  200. drawFastVLine(x+w-1, y, h, color);
  201. }
  202. void drawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color)
  203. {
  204. int16_t max_radius = ((w < h) ? w : h) / 2; // 1/2 minor axis
  205. if(r > max_radius) r = max_radius;
  206. // smarter version
  207. drawFastHLine(x+r , y , w-2*r, color); // Top
  208. drawFastHLine(x+r , y+h-1, w-2*r, color); // Bottom
  209. drawFastVLine(x , y+r , h-2*r, color); // Left
  210. drawFastVLine(x+w-1, y+r , h-2*r, color); // Right
  211. // draw four corners
  212. drawCircleHelper(x+r , y+r , r, 1, color);
  213. drawCircleHelper(x+w-r-1, y+r , r, 2, color);
  214. drawCircleHelper(x+w-r-1, y+h-r-1, r, 4, color);
  215. drawCircleHelper(x+r , y+h-r-1, r, 8, color);
  216. }
  217. void fillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color)
  218. {
  219. int16_t max_radius = ((w < h) ? w : h) / 2; // 1/2 minor axis
  220. if(r > max_radius) r = max_radius;
  221. // smarter version
  222. fillRect(x+r, y, w-2*r, h, color);
  223. // draw four corners
  224. fillCircleHelper(x+w-r-1, y+r, r, 1, h-2*r-1, color);
  225. fillCircleHelper(x+r , y+r, r, 2, h-2*r-1, color);
  226. }
  227. void drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color)
  228. {
  229. drawLine(x0, y0, x1, y1, color);
  230. drawLine(x1, y1, x2, y2, color);
  231. drawLine(x2, y2, x0, y0, color);
  232. }
  233. void fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color)
  234. {
  235. int16_t a, b, y, last;
  236. // Sort coordinates by Y order (y2 >= y1 >= y0)
  237. if (y0 > y1) {
  238. _swap_int16_t(y0, y1); _swap_int16_t(x0, x1);
  239. }
  240. if (y1 > y2) {
  241. _swap_int16_t(y2, y1); _swap_int16_t(x2, x1);
  242. }
  243. if (y0 > y1) {
  244. _swap_int16_t(y0, y1); _swap_int16_t(x0, x1);
  245. }
  246. if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing
  247. a = b = x0;
  248. if(x1 < a) a = x1;
  249. else if(x1 > b) b = x1;
  250. if(x2 < a) a = x2;
  251. else if(x2 > b) b = x2;
  252. drawFastHLine(a, y0, b-a+1, color);
  253. return;
  254. }
  255. int16_t
  256. dx01 = x1 - x0,
  257. dy01 = y1 - y0,
  258. dx02 = x2 - x0,
  259. dy02 = y2 - y0,
  260. dx12 = x2 - x1,
  261. dy12 = y2 - y1;
  262. int32_t
  263. sa = 0,
  264. sb = 0;
  265. // For upper part of triangle, find scanline crossings for segments
  266. // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1
  267. // is included here (and second loop will be skipped, avoiding a /0
  268. // error there), otherwise scanline y1 is skipped here and handled
  269. // in the second loop...which also avoids a /0 error here if y0=y1
  270. // (flat-topped triangle).
  271. if(y1 == y2) last = y1; // Include y1 scanline
  272. else last = y1-1; // Skip it
  273. for(y=y0; y<=last; y++) {
  274. a = x0 + sa / dy01;
  275. b = x0 + sb / dy02;
  276. sa += dx01;
  277. sb += dx02;
  278. /* longhand:
  279. a = x0 + (x1 - x0) * (y - y0) / (y1 - y0);
  280. b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
  281. */
  282. if(a > b) _swap_int16_t(a,b);
  283. drawFastHLine(a, y, b-a+1, color);
  284. }
  285. // For lower part of triangle, find scanline crossings for segments
  286. // 0-2 and 1-2. This loop is skipped if y1=y2.
  287. sa = (int32_t)dx12 * (y - y1);
  288. sb = (int32_t)dx02 * (y - y0);
  289. for(; y<=y2; y++) {
  290. a = x1 + sa / dy12;
  291. b = x0 + sb / dy02;
  292. sa += dx12;
  293. sb += dx02;
  294. /* longhand:
  295. a = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
  296. b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
  297. */
  298. if(a > b) _swap_int16_t(a,b);
  299. drawFastHLine(a, y, b-a+1, color);
  300. }
  301. }
  302. void fillScreen(uint16_t color) {
  303. fillRect(0, 0, _width, _height, color);
  304. }
  305. void testLines(uint16_t color)
  306. {
  307. int x1, y1, x2, y2,
  308. w = _width,
  309. h = _height;
  310. fillScreen(BLACK);
  311. x1 = y1 = 0;
  312. y2 = h - 1;
  313. for (x2 = 0; x2 < w; x2 += 6) drawLine(x1, y1, x2, y2, color);
  314. x2 = w - 1;
  315. for (y2 = 0; y2 < h; y2 += 6) drawLine(x1, y1, x2, y2, color);
  316. fillScreen(BLACK);
  317. x1 = w - 1;
  318. y1 = 0;
  319. y2 = h - 1;
  320. for (x2 = 0; x2 < w; x2 += 6) drawLine(x1, y1, x2, y2, color);
  321. x2 = 0;
  322. for (y2 = 0; y2 < h; y2 += 6) drawLine(x1, y1, x2, y2, color);
  323. fillScreen(BLACK);
  324. x1 = 0;
  325. y1 = h - 1;
  326. y2 = 0;
  327. for (x2 = 0; x2 < w; x2 += 6) drawLine(x1, y1, x2, y2, color);
  328. x2 = w - 1;
  329. for (y2 = 0; y2 < h; y2 += 6) drawLine(x1, y1, x2, y2, color);
  330. fillScreen(BLACK);
  331. x1 = w - 1;
  332. y1 = h - 1;
  333. y2 = 0;
  334. for (x2 = 0; x2 < w; x2 += 6) drawLine(x1, y1, x2, y2, color);
  335. x2 = 0;
  336. for (y2 = 0; y2 < h; y2 += 6) drawLine(x1, y1, x2, y2, color);
  337. }
  338. void testFastLines(uint16_t color1, uint16_t color2)
  339. {
  340. int x, y, w = _width, h = _height;
  341. fillScreen(BLACK);
  342. for (y = 0; y < h; y += 5) drawFastHLine(0, y, w, color1);
  343. for (x = 0; x < w; x += 5) drawFastVLine(x, 0, h, color2);
  344. }
  345. void testRects(uint16_t color)
  346. {
  347. int n, i, i2,
  348. cx = _width / 2,
  349. cy = _height / 2;
  350. fillScreen(BLACK);
  351. n = min(_width, _height);
  352. for (i = 2; i < n; i += 6) {
  353. i2 = i / 2;
  354. drawRect(cx - i2, cy - i2, i, i, color);
  355. }
  356. }
  357. void testFilledRects(uint16_t color1, uint16_t color2)
  358. {
  359. int n, i, i2,
  360. cx = _width / 2 - 1,
  361. cy = _height / 2 - 1;
  362. fillScreen(BLACK);
  363. n = min(_width, _height);
  364. for (i = n; i > 0; i -= 6) {
  365. i2 = i / 2;
  366. fillRect(cx - i2, cy - i2, i, i, color1);
  367. drawRect(cx - i2, cy - i2, i, i, color2);
  368. }
  369. }
  370. void testFilledCircles(uint8_t radius, uint16_t color)
  371. {
  372. int x, y, w = _width, h = _height, r2 = radius * 2;
  373. fillScreen(BLACK);
  374. for (x = radius; x < w; x += r2) {
  375. for (y = radius; y < h; y += r2) {
  376. fillCircle(x, y, radius, color);
  377. }
  378. }
  379. }
  380. void testCircles(uint8_t radius, uint16_t color)
  381. {
  382. int x, y, r2 = radius * 2,
  383. w = _width + radius,
  384. h = _height + radius;
  385. // Screen is not cleared for this one -- this is
  386. // intentional and does not affect the reported time.
  387. for (x = 0; x < w; x += r2) {
  388. for (y = 0; y < h; y += r2) {
  389. drawCircle(x, y, radius, color);
  390. }
  391. }
  392. }
  393. void testTriangles()
  394. {
  395. int n, i, cx = _width / 2 - 1,
  396. cy = _height / 2 - 1;
  397. fillScreen(BLACK);
  398. n = min(cx, cy);
  399. for (i = 0; i < n; i += 5) {
  400. drawTriangle(
  401. cx , cy - i, // peak
  402. cx - i, cy + i, // bottom left
  403. cx + i, cy + i, // bottom right
  404. color565(0, 0, i));
  405. }
  406. }
  407. void testFilledTriangles() {
  408. int i, cx = _width / 2 - 1,
  409. cy = _height / 2 - 1;
  410. fillScreen(BLACK);
  411. for (i = min(cx, cy); i > 10; i -= 5) {
  412. fillTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i,
  413. color565(0, i, i));
  414. drawTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i,
  415. color565(i, i, 0));
  416. }
  417. }
  418. void testRoundRects() {
  419. int w, i, i2, red, step,
  420. cx = _width / 2 - 1,
  421. cy = _height / 2 - 1;
  422. fillScreen(BLACK);
  423. w = min(_width, _height);
  424. red = 0;
  425. step = (256 * 6) / w;
  426. for (i = 0; i < w; i += 6) {
  427. i2 = i / 2;
  428. red += step;
  429. drawRoundRect(cx - i2, cy - i2, i, i, i / 8, color565(red, 0, 0));
  430. }
  431. }
  432. void testFilledRoundRects() {
  433. int i, i2, green, step,
  434. cx = _width / 2 - 1,
  435. cy = _height / 2 - 1;
  436. fillScreen(BLACK);
  437. green = 256;
  438. step = (256 * 6) / min(_width, _height);
  439. for (i = min(_width, _height); i > 20; i -= 6) {
  440. i2 = i / 2;
  441. green -= step;
  442. fillRoundRect(cx - i2, cy - i2, i, i, i / 8, color565(0, green, 0));
  443. }
  444. }
  445. void testFillScreen()
  446. {
  447. fillScreen(BLACK);
  448. fillScreen(RED);
  449. fillScreen(GREEN);
  450. fillScreen(BLUE);
  451. fillScreen(BLACK);
  452. }
  453. void testAll (void)
  454. {
  455. testFillScreen();
  456. testLines(CYAN);
  457. testFastLines(RED, BLUE);
  458. testRects(GREEN);
  459. testFilledRects(YELLOW, MAGENTA);
  460. testFilledCircles(10, MAGENTA);
  461. testCircles(10, WHITE);
  462. testTriangles();
  463. testFilledTriangles();
  464. testRoundRects();
  465. testFilledRoundRects();
  466. }