/app/src/graphics.c

https://github.com/andrei7c4/weatherdisplay · C · 540 lines · 466 code · 61 blank · 13 comment · 76 complexity · c21ba9bc40ad0f02bdac559557bcd1e5 MD5 · raw file

  1. #include <ets_sys.h>
  2. #include <string.h>
  3. #include <osapi.h>
  4. #include <mem.h>
  5. #include "graphics.h"
  6. #include "typedefs.h"
  7. #include "conv.h"
  8. #include "debug.h"
  9. uchar *gfxMem = NULL;
  10. const uint gfxMemSize = (GFXMEM_HEIGHT*GFXMEM_BYTEWIDTH);
  11. int ICACHE_FLASH_ATTR gfxMemAlloc(void)
  12. {
  13. gfxMem = os_zalloc(gfxMemSize);
  14. if (!gfxMem)
  15. {
  16. debug("gfxmem malloc failed\n");
  17. return ERROR;
  18. }
  19. return OK;
  20. }
  21. void ICACHE_FLASH_ATTR gfxMemFill(uchar data)
  22. {
  23. memset(gfxMem, data, gfxMemSize);
  24. }
  25. LOCAL void ICACHE_FLASH_ATTR gfxDrawPixel(int x, int y, int color)
  26. {
  27. if (x >= GFXMEM_WIDTH || y >= GFXMEM_HEIGHT)
  28. {
  29. return;
  30. }
  31. uchar *pBuf = &gfxMem[y*GFXMEM_BYTEWIDTH + x/8];
  32. uchar mask = (0x80 >> (x & 7));
  33. *pBuf = color ? *pBuf | mask : *pBuf & ~mask;
  34. }
  35. LOCAL int ICACHE_FLASH_ATTR gfxGetPixel(int x, int y)
  36. {
  37. uchar *pBuf = &gfxMem[y*GFXMEM_BYTEWIDTH + x/8];
  38. uchar mask = (0x80 >> (x & 7));
  39. return (*pBuf & mask) != 0;
  40. }
  41. // for big bitmaps only -> 'x' is aligned to left 8 pixel boundary
  42. LOCAL void ICACHE_FLASH_ATTR drawBitmapAlign8(int x, int y, int bmWidth, int bmHeight, const uint *bitmap, int bitmapSize)
  43. {
  44. int maxBmHeight = GFXMEM_HEIGHT-y;
  45. if (bmHeight > maxBmHeight)
  46. {
  47. bmHeight = maxBmHeight;
  48. }
  49. if (bmHeight <= 0)
  50. return;
  51. bmWidth /= 8;
  52. int memX = x/8;
  53. int bmWidthCpy = bmWidth;
  54. int maxBmWidth = (GFXMEM_BYTEWIDTH-memX);
  55. if (bmWidthCpy > maxBmWidth)
  56. {
  57. bmWidthCpy = maxBmWidth;
  58. }
  59. if (bmWidthCpy <= 0)
  60. return;
  61. int i;
  62. if ((memX % 4) == 0 && (bmWidthCpy % 4) == 0) // x and width are multiples of 4 -> can access flash directly
  63. {
  64. const uchar *pBitmap = (uchar*)bitmap;
  65. for (i = 0; i < bmHeight; i++, y++)
  66. {
  67. os_memcpy(gfxMem + y*GFXMEM_BYTEWIDTH+memX, pBitmap, bmWidthCpy);
  68. pBitmap += bmWidth;
  69. }
  70. }
  71. else // x or width are not multiples of 4 -> need a temp buffer to avoid alignment issues
  72. {
  73. uchar *temp = (uchar*)os_malloc(bitmapSize);
  74. uchar *pTemp = temp;
  75. if (!temp)
  76. return;
  77. os_memcpy(pTemp, bitmap, bitmapSize);
  78. for (i = 0; i < bmHeight; i++, y++)
  79. {
  80. os_memcpy(gfxMem + y*GFXMEM_BYTEWIDTH+memX, pTemp, bmWidthCpy);
  81. pTemp += bmWidth;
  82. }
  83. os_free(temp);
  84. }
  85. }
  86. // multiple must be power of 2
  87. LOCAL int ICACHE_FLASH_ATTR roundUp(int numToRound, int multiple)
  88. {
  89. return (numToRound + multiple - 1) & -multiple;
  90. }
  91. LOCAL void ICACHE_FLASH_ATTR drawBitmap(int x0, int y0, int bmWidth, int bmHeight, const uint *bitmap, int bitmapSize)
  92. {
  93. if (x0 < 0) x0 = 0;
  94. if (y0 < 0) y0 = 0;
  95. int maxBmHeight = GFXMEM_HEIGHT - y0;
  96. if (bmHeight > maxBmHeight)
  97. {
  98. bmHeight = maxBmHeight;
  99. }
  100. if (bmHeight <= 0)
  101. return;
  102. int bmByteWidth = roundUp(bmWidth,8)/8;
  103. int maxBmWidth = GFXMEM_WIDTH - x0;
  104. if (bmWidth > maxBmWidth)
  105. {
  106. bmWidth = maxBmWidth;
  107. }
  108. if (bmWidth <= 0)
  109. return;
  110. int x1 = x0 + bmWidth - 1;
  111. const int y1 = y0 + bmHeight - 1;
  112. const int idxFirst = x0 / 8;
  113. const int idxLast = x1 / 8;
  114. x0 &= 7;
  115. x1 &= 7;
  116. int y, i, j;
  117. uchar *pBuf;
  118. uchar *pLine;
  119. uchar *pBitmap = (uchar*)os_malloc(bitmapSize*4);
  120. if (!pBitmap)
  121. return;
  122. os_memcpy(pBitmap, bitmap, bitmapSize*4);
  123. if (idxFirst == idxLast) // single column -> special case
  124. {
  125. uchar mask = 0;
  126. for (i = x0; i <= x1; i++)
  127. {
  128. mask |= (0x80 >> i);
  129. }
  130. for (y = y0, pBuf = &gfxMem[y0 * GFXMEM_BYTEWIDTH + idxFirst], pLine = pBitmap;
  131. y <= y1;
  132. y++, pBuf += GFXMEM_BYTEWIDTH, pLine += bmByteWidth)
  133. {
  134. // set/clear bits of single column
  135. *pBuf = (*pBuf & ~mask) | ((*pLine >> x0) & mask);
  136. }
  137. }
  138. else
  139. {
  140. const uchar maskFirst = (0xFF >> x0);
  141. const uchar maskLast = (0xFF << (7 - x1));
  142. const int cntFirst = 8 - x0; // nr. of pixels from first (leftmost) column
  143. const int cntLast = x1 + 1; // nr. of pixels from last (rightmost) column
  144. for (y = y0, pLine = pBitmap; y <= y1; y++, pLine += bmByteWidth)
  145. {
  146. // set/clear LSBits of fist (leftmost) column
  147. pBuf = &gfxMem[y * GFXMEM_BYTEWIDTH + idxFirst];
  148. *pBuf = (*pBuf & ~maskFirst) | (pLine[0] >> x0);
  149. pBuf++;
  150. // set/clear all bits of middle columns
  151. for (i = idxFirst + 1, j = 0; i < idxLast; i++, pBuf++)
  152. {
  153. uchar temp = pLine[j] << cntFirst;
  154. *pBuf = temp | (pLine[++j] >> x0);
  155. }
  156. // set/clear MSBits of last (rightmost) column
  157. uchar temp = pLine[j] << cntFirst;
  158. if(cntLast > x0)
  159. {
  160. temp |= (pLine[++j] >> x0);
  161. }
  162. *pBuf = (*pBuf & ~maskLast) | temp;
  163. }
  164. }
  165. os_free(pBitmap);
  166. }
  167. void ICACHE_FLASH_ATTR gfxDrawImage(int x, int y, const uint *image)
  168. {
  169. int imgWidth = image[0];
  170. int imgHeight = image[1];
  171. int bitmapSize = image[2];
  172. drawBitmapAlign8(x, y, imgWidth, imgHeight, image+4, bitmapSize);
  173. //drawBitmap(x, y, imgWidth, imgHeight, image+4, bitmapSize);
  174. }
  175. int ICACHE_FLASH_ATTR gfxDrawCharBig(const uint **font, int x, int y, uchar ch)
  176. {
  177. int first = (int)font[0];
  178. int last = (int)font[1];
  179. if (ch < first || ch > last)
  180. {
  181. return 0;
  182. }
  183. ch -= (first-2);
  184. const uint *chHeader = font[ch];
  185. if (!chHeader) // char not found
  186. {
  187. return 0;
  188. }
  189. int chWidth = chHeader[0];
  190. int chHeight = chHeader[1];
  191. int bitmapSize = chHeader[2];
  192. int yoffset = chHeader[3];
  193. drawBitmapAlign8(x, y+yoffset, chWidth, chHeight, font[ch]+4, bitmapSize);
  194. //drawBitmap(x, y+yoffset, chWidth, chHeight, font[ch]+4, bitmapSize);
  195. return chWidth;
  196. }
  197. int ICACHE_FLASH_ATTR gfxDrawStrBig(const uint **font, int x, int y, const char *str)
  198. {
  199. int strWidth = 0;
  200. while (*str)
  201. {
  202. char ch = *str;
  203. int chWidth = gfxDrawCharBig(font, x, y, ch);
  204. x += chWidth;
  205. strWidth += chWidth;
  206. str++;
  207. }
  208. return strWidth;
  209. }
  210. LOCAL int ICACHE_FLASH_ATTR gfxCharWidthBig(const uint **font, uchar ch)
  211. {
  212. int first = (int)font[0];
  213. int last = (int)font[1];
  214. if (ch < first || ch > last)
  215. {
  216. return 0;
  217. }
  218. ch -= (first-2);
  219. const uint *chHeader = font[ch];
  220. if (!chHeader)
  221. {
  222. return 0;
  223. }
  224. return chHeader[0];
  225. }
  226. int ICACHE_FLASH_ATTR gfxStrWidthBig(const uint **font, const char *str)
  227. {
  228. int strWidth = 0;
  229. while (*str)
  230. {
  231. char ch = *str;
  232. int chWidth = gfxCharWidthBig(font, ch);
  233. strWidth += chWidth;
  234. str++;
  235. }
  236. return strWidth;
  237. }
  238. int gfxDrawChar(const uint *font, int x, int y, uchar ch)
  239. {
  240. int first = font[0];
  241. int last = font[1];
  242. if (ch < first || ch > last)
  243. {
  244. return 0;
  245. }
  246. int chIdx = ch-(first-2);
  247. int chOffset = font[chIdx];
  248. if (!chOffset) // char not found
  249. {
  250. return 0;
  251. }
  252. volatile uint header = font[chOffset];
  253. uchar chWidth = header>>24;
  254. if (ch == ' ') // skip space
  255. {
  256. return chWidth;
  257. }
  258. uchar chHeight = header>>16;
  259. uchar bitmapSize = header>>8;
  260. uchar yoffset = header;
  261. //debug("0x%08X, %u, %u, %u, %u\n", header, chWidth, chHeight, bitmapSize, yoffset);
  262. drawBitmap(x, y+yoffset, chWidth, chHeight, &font[chOffset+1], bitmapSize);
  263. return chWidth;
  264. }
  265. int gfxDrawStr(const uint *font, int x, int y, const char *str)
  266. {
  267. int strWidth = 0;
  268. while (*str)
  269. {
  270. char ch = *str;
  271. int chWidth = gfxDrawChar(font, x, y, ch);
  272. x += chWidth;
  273. strWidth += chWidth;
  274. str++;
  275. }
  276. return strWidth;
  277. }
  278. LOCAL int ICACHE_FLASH_ATTR gfxCharWidth(const uint *font, uchar ch)
  279. {
  280. int first = font[0];
  281. int last = font[1];
  282. if (ch < first || ch > last)
  283. {
  284. return 0;
  285. }
  286. int chIdx = ch-(first-2);
  287. int chOffset = font[chIdx];
  288. if (!chOffset) // char not found
  289. {
  290. return 0;
  291. }
  292. volatile uint header = font[chOffset];
  293. uchar chWidth = header>>24;
  294. return chWidth;
  295. }
  296. int gfxStrWidth(const uint *font, const char *str)
  297. {
  298. int strWidth = 0;
  299. while (*str)
  300. {
  301. char ch = *str;
  302. int chWidth = gfxCharWidth(font, ch);
  303. strWidth += chWidth;
  304. str++;
  305. }
  306. return strWidth;
  307. }
  308. int ICACHE_FLASH_ATTR gfxDrawStrCentredBig(const uint **font, int centre, int y, const char *str)
  309. {
  310. int strWidth = gfxStrWidthBig(font, str);
  311. int x = centre-(strWidth/2);
  312. x = alignTo8(x); // align to nearest 8 pixel boundary
  313. return gfxDrawStrBig(font, x, y, str);
  314. }
  315. int ICACHE_FLASH_ATTR gfxDrawStrAlignRightBig(const uint **font, int right, int y, const char *str)
  316. {
  317. int strWidth = gfxStrWidthBig(font, str);
  318. int x = right-strWidth;
  319. return gfxDrawStrBig(font, x, y, str);
  320. }
  321. int ICACHE_FLASH_ATTR gfxDrawStrCentred(const uint *font, int centre, int y, const char *str)
  322. {
  323. int strWidth = gfxStrWidth(font, str);
  324. int x = centre-(strWidth/2);
  325. return gfxDrawStr(font, x, y, str);
  326. }
  327. int ICACHE_FLASH_ATTR gfxDrawStrAlignRight(const uint *font, int right, int y, const char *str)
  328. {
  329. int strWidth = gfxStrWidth(font, str);
  330. int x = right-strWidth;
  331. return gfxDrawStr(font, x, y, str);
  332. }
  333. void ICACHE_FLASH_ATTR gfxDrawLine(int x0, int y0, int x1, int y1, char color)
  334. {
  335. int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
  336. int dy = abs(y1-y0), sy = y0<y1 ? 1 : -1;
  337. int err = (dx>dy ? dx : -dy)/2, e2;
  338. for(;;)
  339. {
  340. gfxDrawPixel(x0,y0,color);
  341. if (x0==x1 && y0==y1) break;
  342. e2 = err;
  343. if (e2 >-dx) { err -= dy; x0 += sx; }
  344. if (e2 < dy) { err += dx; y0 += sy; }
  345. }
  346. }
  347. void ICACHE_FLASH_ATTR gfxDrawcircle(int x0, int y0, int radius, char color, char fill)
  348. {
  349. int x = radius;
  350. int y = 0;
  351. int err = 0;
  352. while (x >= y)
  353. {
  354. if (fill)
  355. {
  356. gfxDrawLine(x0 - x, y0 + y, x0 + x, y0 + y, color);
  357. gfxDrawLine(x0 - y, y0 + x, x0 + y, y0 + x, color);
  358. gfxDrawLine(x0 - x, y0 - y, x0 + x, y0 - y, color);
  359. gfxDrawLine(x0 - y, y0 - x, x0 + y, y0 - x, color);
  360. }
  361. else
  362. {
  363. gfxDrawPixel(x0 + x, y0 + y, color);
  364. gfxDrawPixel(x0 + y, y0 + x, color);
  365. gfxDrawPixel(x0 - y, y0 + x, color);
  366. gfxDrawPixel(x0 - x, y0 + y, color);
  367. gfxDrawPixel(x0 - x, y0 - y, color);
  368. gfxDrawPixel(x0 - y, y0 - x, color);
  369. gfxDrawPixel(x0 + y, y0 - x, color);
  370. gfxDrawPixel(x0 + x, y0 - y, color);
  371. }
  372. if (err <= 0)
  373. {
  374. y += 1;
  375. err += 2*y + 1;
  376. }
  377. if (err > 0)
  378. {
  379. x -= 1;
  380. err -= 2*x + 1;
  381. }
  382. }
  383. }
  384. void ICACHE_FLASH_ATTR gfxDrawLineBold(int x0, int y0, int x1, int y1, char color, char boldX, char boldY)
  385. {
  386. gfxDrawLine(x0, y0, x1, y1, color);
  387. if (boldX)
  388. {
  389. gfxDrawLine(x0-1, y0, x1-1, y1, color);
  390. gfxDrawLine(x0+1, y0, x1+1, y1, color);
  391. }
  392. if (boldY)
  393. {
  394. gfxDrawLine(x0, y0-1, x1, y1-1, color);
  395. gfxDrawLine(x0, y0+1, x1, y1+1, color);
  396. }
  397. }
  398. void ICACHE_FLASH_ATTR gfxDrawLineDotted(int x0, int y0, int x1, int y1, int space, char color)
  399. {
  400. int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
  401. int dy = abs(y1-y0), sy = y0<y1 ? 1 : -1;
  402. int err = (dx>dy ? dx : -dy)/2, e2;
  403. int i = 0;
  404. if (space < 1)
  405. {
  406. space = 1;
  407. }
  408. for(;;)
  409. {
  410. if (i == space)
  411. {
  412. i = 0;
  413. }
  414. else
  415. {
  416. if (i == 0)
  417. {
  418. gfxDrawPixel(x0,y0,color);
  419. }
  420. i++;
  421. }
  422. if (x0==x1 && y0==y1) break;
  423. e2 = err;
  424. if (e2 >-dx) { err -= dy; x0 += sx; }
  425. if (e2 < dy) { err += dx; y0 += sy; }
  426. }
  427. }
  428. void ICACHE_FLASH_ATTR gfxDrawRectDotted(int x0, int y0, int x1, int y1, int space, char color)
  429. {
  430. int x;
  431. for (x = x0; x <= x1; x+=(space+1))
  432. {
  433. gfxDrawLineDotted(x, y0, x, y1, space, color);
  434. }
  435. }
  436. void ICACHE_FLASH_ATTR gfxDrawRectFill(int x0, int y0, int x1, int y1, char color)
  437. {
  438. if (x0 > x1) swapInt(&x0, &x1);
  439. if (y0 > y1) swapInt(&y0, &y1);
  440. const int idxFirst = x0 / 8;
  441. const int idxLast = x1 / 8;
  442. x0 &= 7;
  443. x1 &= 7;
  444. int y, yOffset;
  445. int i;
  446. uchar *pBuf;
  447. if (idxFirst == idxLast) // single column -> special case
  448. {
  449. uchar mask = 0;
  450. for (i = x0; i <= x1; i++)
  451. {
  452. mask |= (0x80 >> i);
  453. }
  454. for (y = y0; y <= y1; y++)
  455. {
  456. // set/clear bits of single column
  457. pBuf = &gfxMem[y * GFXMEM_BYTEWIDTH + idxFirst];
  458. *pBuf = color ? *pBuf | mask : *pBuf & ~mask;
  459. }
  460. }
  461. else
  462. {
  463. const uchar maskFirst = (0xFF >> x0);
  464. const uchar maskLast = (0xFF << (7 - x1));
  465. const uchar maskMid = color * 0xFF;
  466. for (y = y0; y <= y1; y++)
  467. {
  468. yOffset = y * GFXMEM_BYTEWIDTH;
  469. // set/clear LSBits of fist column
  470. pBuf = &gfxMem[yOffset + idxFirst];
  471. *pBuf = color ? *pBuf | maskFirst : *pBuf & ~maskFirst;
  472. // set/clear all bits of middle columns
  473. memset(&gfxMem[yOffset + idxFirst + 1], maskMid, idxLast - (idxFirst + 1));
  474. // set/clear MSBits of last column
  475. pBuf = &gfxMem[yOffset + idxLast];
  476. *pBuf = color ? *pBuf | maskLast : *pBuf & ~maskLast;
  477. }
  478. }
  479. }