canvas.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. #include "canvas_i.h"
  2. #include "icon_i.h"
  3. #include "icon_animation_i.h"
  4. #include <furi.h>
  5. #include <furi_hal.h>
  6. #include <furi_hal_rtc.h>
  7. #include <stdint.h>
  8. #include <u8g2_glue.h>
  9. const CanvasFontParameters canvas_font_params[FontTotalNumber] = {
  10. [FontPrimary] = {.leading_default = 12, .leading_min = 11, .height = 8, .descender = 2},
  11. [FontSecondary] = {.leading_default = 11, .leading_min = 9, .height = 7, .descender = 2},
  12. [FontKeyboard] = {.leading_default = 11, .leading_min = 9, .height = 7, .descender = 2},
  13. [FontBigNumbers] = {.leading_default = 18, .leading_min = 16, .height = 15, .descender = 0},
  14. };
  15. Canvas* canvas_init() {
  16. Canvas* canvas = malloc(sizeof(Canvas));
  17. canvas->compress_icon = compress_icon_alloc();
  18. // Setup u8g2
  19. u8g2_Setup_st756x_flipper(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
  20. canvas->orientation = CanvasOrientationHorizontal;
  21. // Initialize display
  22. u8g2_InitDisplay(&canvas->fb);
  23. // Wake up display
  24. u8g2_SetPowerSave(&canvas->fb, 0);
  25. // Clear buffer and send to device
  26. canvas_clear(canvas);
  27. canvas_commit(canvas);
  28. return canvas;
  29. }
  30. void canvas_free(Canvas* canvas) {
  31. furi_assert(canvas);
  32. compress_icon_free(canvas->compress_icon);
  33. free(canvas);
  34. }
  35. void canvas_reset(Canvas* canvas) {
  36. furi_assert(canvas);
  37. canvas_clear(canvas);
  38. canvas_set_color(canvas, ColorBlack);
  39. canvas_set_font(canvas, FontSecondary);
  40. canvas_set_font_direction(canvas, CanvasDirectionLeftToRight);
  41. }
  42. void canvas_commit(Canvas* canvas) {
  43. furi_assert(canvas);
  44. u8g2_SendBuffer(&canvas->fb);
  45. }
  46. uint8_t* canvas_get_buffer(Canvas* canvas) {
  47. furi_assert(canvas);
  48. return u8g2_GetBufferPtr(&canvas->fb);
  49. }
  50. size_t canvas_get_buffer_size(const Canvas* canvas) {
  51. furi_assert(canvas);
  52. return u8g2_GetBufferTileWidth(&canvas->fb) * u8g2_GetBufferTileHeight(&canvas->fb) * 8;
  53. }
  54. void canvas_frame_set(
  55. Canvas* canvas,
  56. uint8_t offset_x,
  57. uint8_t offset_y,
  58. uint8_t width,
  59. uint8_t height) {
  60. furi_assert(canvas);
  61. canvas->offset_x = offset_x;
  62. canvas->offset_y = offset_y;
  63. canvas->width = width;
  64. canvas->height = height;
  65. }
  66. uint8_t canvas_width(const Canvas* canvas) {
  67. furi_assert(canvas);
  68. return canvas->width;
  69. }
  70. uint8_t canvas_height(const Canvas* canvas) {
  71. furi_assert(canvas);
  72. return canvas->height;
  73. }
  74. uint8_t canvas_current_font_height(const Canvas* canvas) {
  75. furi_assert(canvas);
  76. uint8_t font_height = u8g2_GetMaxCharHeight(&canvas->fb);
  77. if(canvas->fb.font == u8g2_font_haxrcorp4089_tr) {
  78. font_height += 1;
  79. }
  80. return font_height;
  81. }
  82. const CanvasFontParameters* canvas_get_font_params(const Canvas* canvas, Font font) {
  83. furi_assert(canvas);
  84. furi_assert(font < FontTotalNumber);
  85. return &canvas_font_params[font];
  86. }
  87. void canvas_clear(Canvas* canvas) {
  88. furi_assert(canvas);
  89. u8g2_ClearBuffer(&canvas->fb);
  90. }
  91. void canvas_set_color(Canvas* canvas, Color color) {
  92. furi_assert(canvas);
  93. u8g2_SetDrawColor(&canvas->fb, color);
  94. }
  95. void canvas_set_font_direction(Canvas* canvas, CanvasDirection dir) {
  96. furi_assert(canvas);
  97. u8g2_SetFontDirection(&canvas->fb, dir);
  98. }
  99. void canvas_invert_color(Canvas* canvas) {
  100. canvas->fb.draw_color = !canvas->fb.draw_color;
  101. }
  102. void canvas_set_font(Canvas* canvas, Font font) {
  103. furi_assert(canvas);
  104. u8g2_SetFontMode(&canvas->fb, 1);
  105. if(font == FontPrimary) {
  106. u8g2_SetFont(&canvas->fb, u8g2_font_helvB08_tr);
  107. } else if(font == FontSecondary) {
  108. u8g2_SetFont(&canvas->fb, u8g2_font_haxrcorp4089_tr);
  109. } else if(font == FontKeyboard) {
  110. u8g2_SetFont(&canvas->fb, u8g2_font_profont11_mr);
  111. } else if(font == FontBigNumbers) {
  112. u8g2_SetFont(&canvas->fb, u8g2_font_profont22_tn);
  113. } else {
  114. furi_crash(NULL);
  115. }
  116. }
  117. void canvas_set_custom_u8g2_font(Canvas* canvas, const uint8_t* font) {
  118. furi_assert(canvas);
  119. u8g2_SetFontMode(&canvas->fb, 1);
  120. u8g2_SetFont(&canvas->fb, font);
  121. }
  122. void canvas_draw_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str) {
  123. furi_assert(canvas);
  124. if(!str) return;
  125. x += canvas->offset_x;
  126. y += canvas->offset_y;
  127. u8g2_DrawStr(&canvas->fb, x, y, str);
  128. }
  129. void canvas_draw_str_aligned(
  130. Canvas* canvas,
  131. uint8_t x,
  132. uint8_t y,
  133. Align horizontal,
  134. Align vertical,
  135. const char* str) {
  136. furi_assert(canvas);
  137. if(!str) return;
  138. x += canvas->offset_x;
  139. y += canvas->offset_y;
  140. switch(horizontal) {
  141. case AlignLeft:
  142. break;
  143. case AlignRight:
  144. x -= u8g2_GetStrWidth(&canvas->fb, str);
  145. break;
  146. case AlignCenter:
  147. x -= (u8g2_GetStrWidth(&canvas->fb, str) / 2);
  148. break;
  149. default:
  150. furi_crash(NULL);
  151. break;
  152. }
  153. switch(vertical) {
  154. case AlignTop:
  155. y += u8g2_GetAscent(&canvas->fb);
  156. break;
  157. case AlignBottom:
  158. break;
  159. case AlignCenter:
  160. y += (u8g2_GetAscent(&canvas->fb) / 2);
  161. break;
  162. default:
  163. furi_crash(NULL);
  164. break;
  165. }
  166. u8g2_DrawStr(&canvas->fb, x, y, str);
  167. }
  168. uint16_t canvas_string_width(Canvas* canvas, const char* str) {
  169. furi_assert(canvas);
  170. if(!str) return 0;
  171. return u8g2_GetStrWidth(&canvas->fb, str);
  172. }
  173. uint8_t canvas_glyph_width(Canvas* canvas, char symbol) {
  174. furi_assert(canvas);
  175. return u8g2_GetGlyphWidth(&canvas->fb, symbol);
  176. }
  177. void canvas_draw_bitmap(
  178. Canvas* canvas,
  179. uint8_t x,
  180. uint8_t y,
  181. uint8_t width,
  182. uint8_t height,
  183. const uint8_t* compressed_bitmap_data) {
  184. furi_assert(canvas);
  185. x += canvas->offset_x;
  186. y += canvas->offset_y;
  187. uint8_t* bitmap_data = NULL;
  188. compress_icon_decode(canvas->compress_icon, compressed_bitmap_data, &bitmap_data);
  189. canvas_draw_u8g2_bitmap(&canvas->fb, x, y, width, height, bitmap_data, IconRotation0);
  190. }
  191. void canvas_draw_icon_animation(
  192. Canvas* canvas,
  193. uint8_t x,
  194. uint8_t y,
  195. IconAnimation* icon_animation) {
  196. furi_assert(canvas);
  197. furi_assert(icon_animation);
  198. x += canvas->offset_x;
  199. y += canvas->offset_y;
  200. uint8_t* icon_data = NULL;
  201. compress_icon_decode(
  202. canvas->compress_icon, icon_animation_get_data(icon_animation), &icon_data);
  203. canvas_draw_u8g2_bitmap(
  204. &canvas->fb,
  205. x,
  206. y,
  207. icon_animation_get_width(icon_animation),
  208. icon_animation_get_height(icon_animation),
  209. icon_data,
  210. IconRotation0);
  211. }
  212. static void canvas_draw_u8g2_bitmap_int(
  213. u8g2_t* u8g2,
  214. u8g2_uint_t x,
  215. u8g2_uint_t y,
  216. u8g2_uint_t w,
  217. u8g2_uint_t h,
  218. bool mirror,
  219. bool rotation,
  220. const uint8_t* bitmap) {
  221. u8g2_uint_t blen;
  222. blen = w;
  223. blen += 7;
  224. blen >>= 3;
  225. if(rotation && !mirror) {
  226. x += w + 1;
  227. } else if(mirror && !rotation) {
  228. y += h - 1;
  229. }
  230. while(h > 0) {
  231. const uint8_t* b = bitmap;
  232. uint16_t len = w;
  233. uint16_t x0 = x;
  234. uint16_t y0 = y;
  235. uint8_t mask;
  236. uint8_t color = u8g2->draw_color;
  237. uint8_t ncolor = (color == 0 ? 1 : 0);
  238. mask = 1;
  239. while(len > 0) {
  240. if(u8x8_pgm_read(b) & mask) {
  241. u8g2->draw_color = color;
  242. u8g2_DrawHVLine(u8g2, x0, y0, 1, 0);
  243. } else if(u8g2->bitmap_transparency == 0) {
  244. u8g2->draw_color = ncolor;
  245. u8g2_DrawHVLine(u8g2, x0, y0, 1, 0);
  246. }
  247. if(rotation) {
  248. y0++;
  249. } else {
  250. x0++;
  251. }
  252. mask <<= 1;
  253. if(mask == 0) {
  254. mask = 1;
  255. b++;
  256. }
  257. len--;
  258. }
  259. u8g2->draw_color = color;
  260. bitmap += blen;
  261. if(mirror) {
  262. if(rotation) {
  263. x++;
  264. } else {
  265. y--;
  266. }
  267. } else {
  268. if(rotation) {
  269. x--;
  270. } else {
  271. y++;
  272. }
  273. }
  274. h--;
  275. }
  276. }
  277. void canvas_draw_u8g2_bitmap(
  278. u8g2_t* u8g2,
  279. u8g2_uint_t x,
  280. u8g2_uint_t y,
  281. u8g2_uint_t w,
  282. u8g2_uint_t h,
  283. const uint8_t* bitmap,
  284. IconRotation rotation) {
  285. u8g2_uint_t blen;
  286. blen = w;
  287. blen += 7;
  288. blen >>= 3;
  289. #ifdef U8G2_WITH_INTERSECTION
  290. if(u8g2_IsIntersection(u8g2, x, y, x + w, y + h) == 0) return;
  291. #endif /* U8G2_WITH_INTERSECTION */
  292. switch(rotation) {
  293. case IconRotation0:
  294. canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 0, 0, bitmap);
  295. break;
  296. case IconRotation90:
  297. canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 0, 1, bitmap);
  298. break;
  299. case IconRotation180:
  300. canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 1, 0, bitmap);
  301. break;
  302. case IconRotation270:
  303. canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 1, 1, bitmap);
  304. break;
  305. default:
  306. break;
  307. }
  308. }
  309. void canvas_draw_icon_ex(
  310. Canvas* canvas,
  311. uint8_t x,
  312. uint8_t y,
  313. const Icon* icon,
  314. IconRotation rotation) {
  315. furi_assert(canvas);
  316. furi_assert(icon);
  317. x += canvas->offset_x;
  318. y += canvas->offset_y;
  319. uint8_t* icon_data = NULL;
  320. compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data);
  321. canvas_draw_u8g2_bitmap(
  322. &canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data, rotation);
  323. }
  324. void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon) {
  325. furi_assert(canvas);
  326. furi_assert(icon);
  327. x += canvas->offset_x;
  328. y += canvas->offset_y;
  329. uint8_t* icon_data = NULL;
  330. compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data);
  331. canvas_draw_u8g2_bitmap(
  332. &canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data, IconRotation0);
  333. }
  334. void canvas_draw_dot(Canvas* canvas, uint8_t x, uint8_t y) {
  335. furi_assert(canvas);
  336. x += canvas->offset_x;
  337. y += canvas->offset_y;
  338. u8g2_DrawPixel(&canvas->fb, x, y);
  339. }
  340. void canvas_draw_box(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height) {
  341. furi_assert(canvas);
  342. x += canvas->offset_x;
  343. y += canvas->offset_y;
  344. u8g2_DrawBox(&canvas->fb, x, y, width, height);
  345. }
  346. void canvas_draw_rbox(
  347. Canvas* canvas,
  348. uint8_t x,
  349. uint8_t y,
  350. uint8_t width,
  351. uint8_t height,
  352. uint8_t radius) {
  353. furi_assert(canvas);
  354. x += canvas->offset_x;
  355. y += canvas->offset_y;
  356. u8g2_DrawRBox(&canvas->fb, x, y, width, height, radius);
  357. }
  358. void canvas_draw_frame(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height) {
  359. furi_assert(canvas);
  360. x += canvas->offset_x;
  361. y += canvas->offset_y;
  362. u8g2_DrawFrame(&canvas->fb, x, y, width, height);
  363. }
  364. void canvas_draw_rframe(
  365. Canvas* canvas,
  366. uint8_t x,
  367. uint8_t y,
  368. uint8_t width,
  369. uint8_t height,
  370. uint8_t radius) {
  371. furi_assert(canvas);
  372. x += canvas->offset_x;
  373. y += canvas->offset_y;
  374. u8g2_DrawRFrame(&canvas->fb, x, y, width, height, radius);
  375. }
  376. void canvas_draw_line(Canvas* canvas, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) {
  377. furi_assert(canvas);
  378. x1 += canvas->offset_x;
  379. y1 += canvas->offset_y;
  380. x2 += canvas->offset_x;
  381. y2 += canvas->offset_y;
  382. u8g2_DrawLine(&canvas->fb, x1, y1, x2, y2);
  383. }
  384. void canvas_draw_circle(Canvas* canvas, uint8_t x, uint8_t y, uint8_t radius) {
  385. furi_assert(canvas);
  386. x += canvas->offset_x;
  387. y += canvas->offset_y;
  388. u8g2_DrawCircle(&canvas->fb, x, y, radius, U8G2_DRAW_ALL);
  389. }
  390. void canvas_draw_disc(Canvas* canvas, uint8_t x, uint8_t y, uint8_t radius) {
  391. furi_assert(canvas);
  392. x += canvas->offset_x;
  393. y += canvas->offset_y;
  394. u8g2_DrawDisc(&canvas->fb, x, y, radius, U8G2_DRAW_ALL);
  395. }
  396. void canvas_draw_triangle(
  397. Canvas* canvas,
  398. uint8_t x,
  399. uint8_t y,
  400. uint8_t base,
  401. uint8_t height,
  402. CanvasDirection dir) {
  403. furi_assert(canvas);
  404. if(dir == CanvasDirectionBottomToTop) {
  405. canvas_draw_line(canvas, x - base / 2, y, x + base / 2, y);
  406. canvas_draw_line(canvas, x - base / 2, y, x, y - height + 1);
  407. canvas_draw_line(canvas, x, y - height + 1, x + base / 2, y);
  408. } else if(dir == CanvasDirectionTopToBottom) {
  409. canvas_draw_line(canvas, x - base / 2, y, x + base / 2, y);
  410. canvas_draw_line(canvas, x - base / 2, y, x, y + height - 1);
  411. canvas_draw_line(canvas, x, y + height - 1, x + base / 2, y);
  412. } else if(dir == CanvasDirectionRightToLeft) {
  413. canvas_draw_line(canvas, x, y - base / 2, x, y + base / 2);
  414. canvas_draw_line(canvas, x, y - base / 2, x - height + 1, y);
  415. canvas_draw_line(canvas, x - height + 1, y, x, y + base / 2);
  416. } else if(dir == CanvasDirectionLeftToRight) {
  417. canvas_draw_line(canvas, x, y - base / 2, x, y + base / 2);
  418. canvas_draw_line(canvas, x, y - base / 2, x + height - 1, y);
  419. canvas_draw_line(canvas, x + height - 1, y, x, y + base / 2);
  420. }
  421. }
  422. void canvas_draw_xbm(
  423. Canvas* canvas,
  424. uint8_t x,
  425. uint8_t y,
  426. uint8_t w,
  427. uint8_t h,
  428. const uint8_t* bitmap) {
  429. furi_assert(canvas);
  430. x += canvas->offset_x;
  431. y += canvas->offset_y;
  432. canvas_draw_u8g2_bitmap(&canvas->fb, x, y, w, h, bitmap, IconRotation0);
  433. }
  434. void canvas_draw_glyph(Canvas* canvas, uint8_t x, uint8_t y, uint16_t ch) {
  435. furi_assert(canvas);
  436. x += canvas->offset_x;
  437. y += canvas->offset_y;
  438. u8g2_DrawGlyph(&canvas->fb, x, y, ch);
  439. }
  440. void canvas_set_bitmap_mode(Canvas* canvas, bool alpha) {
  441. u8g2_SetBitmapMode(&canvas->fb, alpha ? 1 : 0);
  442. }
  443. void canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation) {
  444. furi_assert(canvas);
  445. const u8g2_cb_t* rotate_cb = NULL;
  446. bool need_swap = false;
  447. if(canvas->orientation != orientation) {
  448. switch(orientation) {
  449. case CanvasOrientationHorizontal:
  450. need_swap = canvas->orientation == CanvasOrientationVertical ||
  451. canvas->orientation == CanvasOrientationVerticalFlip;
  452. rotate_cb = U8G2_R0;
  453. break;
  454. case CanvasOrientationHorizontalFlip:
  455. need_swap = canvas->orientation == CanvasOrientationVertical ||
  456. canvas->orientation == CanvasOrientationVerticalFlip;
  457. rotate_cb = U8G2_R2;
  458. break;
  459. case CanvasOrientationVertical:
  460. need_swap = canvas->orientation == CanvasOrientationHorizontal ||
  461. canvas->orientation == CanvasOrientationHorizontalFlip;
  462. rotate_cb = U8G2_R3;
  463. break;
  464. case CanvasOrientationVerticalFlip:
  465. need_swap = canvas->orientation == CanvasOrientationHorizontal ||
  466. canvas->orientation == CanvasOrientationHorizontalFlip;
  467. rotate_cb = U8G2_R1;
  468. break;
  469. default:
  470. furi_assert(0);
  471. }
  472. if(need_swap) FURI_SWAP(canvas->width, canvas->height);
  473. u8g2_SetDisplayRotation(&canvas->fb, rotate_cb);
  474. canvas->orientation = orientation;
  475. }
  476. }
  477. CanvasOrientation canvas_get_orientation(const Canvas* canvas) {
  478. return canvas->orientation;
  479. }