canvas.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  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. u8g2_DrawXBM(&canvas->fb, x, y, width, height, bitmap_data);
  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. u8g2_DrawXBM(
  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. }
  211. void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon) {
  212. furi_assert(canvas);
  213. furi_assert(icon);
  214. x += canvas->offset_x;
  215. y += canvas->offset_y;
  216. uint8_t* icon_data = NULL;
  217. compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data);
  218. u8g2_DrawXBM(&canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data);
  219. }
  220. void canvas_draw_dot(Canvas* canvas, uint8_t x, uint8_t y) {
  221. furi_assert(canvas);
  222. x += canvas->offset_x;
  223. y += canvas->offset_y;
  224. u8g2_DrawPixel(&canvas->fb, x, y);
  225. }
  226. void canvas_draw_box(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height) {
  227. furi_assert(canvas);
  228. x += canvas->offset_x;
  229. y += canvas->offset_y;
  230. u8g2_DrawBox(&canvas->fb, x, y, width, height);
  231. }
  232. void canvas_draw_rbox(
  233. Canvas* canvas,
  234. uint8_t x,
  235. uint8_t y,
  236. uint8_t width,
  237. uint8_t height,
  238. uint8_t radius) {
  239. furi_assert(canvas);
  240. x += canvas->offset_x;
  241. y += canvas->offset_y;
  242. u8g2_DrawRBox(&canvas->fb, x, y, width, height, radius);
  243. }
  244. void canvas_draw_frame(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height) {
  245. furi_assert(canvas);
  246. x += canvas->offset_x;
  247. y += canvas->offset_y;
  248. u8g2_DrawFrame(&canvas->fb, x, y, width, height);
  249. }
  250. void canvas_draw_rframe(
  251. Canvas* canvas,
  252. uint8_t x,
  253. uint8_t y,
  254. uint8_t width,
  255. uint8_t height,
  256. uint8_t radius) {
  257. furi_assert(canvas);
  258. x += canvas->offset_x;
  259. y += canvas->offset_y;
  260. u8g2_DrawRFrame(&canvas->fb, x, y, width, height, radius);
  261. }
  262. void canvas_draw_line(Canvas* canvas, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) {
  263. furi_assert(canvas);
  264. x1 += canvas->offset_x;
  265. y1 += canvas->offset_y;
  266. x2 += canvas->offset_x;
  267. y2 += canvas->offset_y;
  268. u8g2_DrawLine(&canvas->fb, x1, y1, x2, y2);
  269. }
  270. void canvas_draw_circle(Canvas* canvas, uint8_t x, uint8_t y, uint8_t radius) {
  271. furi_assert(canvas);
  272. x += canvas->offset_x;
  273. y += canvas->offset_y;
  274. u8g2_DrawCircle(&canvas->fb, x, y, radius, U8G2_DRAW_ALL);
  275. }
  276. void canvas_draw_disc(Canvas* canvas, uint8_t x, uint8_t y, uint8_t radius) {
  277. furi_assert(canvas);
  278. x += canvas->offset_x;
  279. y += canvas->offset_y;
  280. u8g2_DrawDisc(&canvas->fb, x, y, radius, U8G2_DRAW_ALL);
  281. }
  282. void canvas_draw_triangle(
  283. Canvas* canvas,
  284. uint8_t x,
  285. uint8_t y,
  286. uint8_t base,
  287. uint8_t height,
  288. CanvasDirection dir) {
  289. furi_assert(canvas);
  290. if(dir == CanvasDirectionBottomToTop) {
  291. canvas_draw_line(canvas, x - base / 2, y, x + base / 2, y);
  292. canvas_draw_line(canvas, x - base / 2, y, x, y - height + 1);
  293. canvas_draw_line(canvas, x, y - height + 1, x + base / 2, y);
  294. } else if(dir == CanvasDirectionTopToBottom) {
  295. canvas_draw_line(canvas, x - base / 2, y, x + base / 2, y);
  296. canvas_draw_line(canvas, x - base / 2, y, x, y + height - 1);
  297. canvas_draw_line(canvas, x, y + height - 1, x + base / 2, y);
  298. } else if(dir == CanvasDirectionRightToLeft) {
  299. canvas_draw_line(canvas, x, y - base / 2, x, y + base / 2);
  300. canvas_draw_line(canvas, x, y - base / 2, x - height + 1, y);
  301. canvas_draw_line(canvas, x - height + 1, y, x, y + base / 2);
  302. } else if(dir == CanvasDirectionLeftToRight) {
  303. canvas_draw_line(canvas, x, y - base / 2, x, y + base / 2);
  304. canvas_draw_line(canvas, x, y - base / 2, x + height - 1, y);
  305. canvas_draw_line(canvas, x + height - 1, y, x, y + base / 2);
  306. }
  307. }
  308. void canvas_draw_xbm(
  309. Canvas* canvas,
  310. uint8_t x,
  311. uint8_t y,
  312. uint8_t w,
  313. uint8_t h,
  314. const uint8_t* bitmap) {
  315. furi_assert(canvas);
  316. x += canvas->offset_x;
  317. y += canvas->offset_y;
  318. u8g2_DrawXBM(&canvas->fb, x, y, w, h, bitmap);
  319. }
  320. void canvas_draw_glyph(Canvas* canvas, uint8_t x, uint8_t y, uint16_t ch) {
  321. furi_assert(canvas);
  322. x += canvas->offset_x;
  323. y += canvas->offset_y;
  324. u8g2_DrawGlyph(&canvas->fb, x, y, ch);
  325. }
  326. void canvas_set_bitmap_mode(Canvas* canvas, bool alpha) {
  327. u8g2_SetBitmapMode(&canvas->fb, alpha ? 1 : 0);
  328. }
  329. void canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation) {
  330. furi_assert(canvas);
  331. const u8g2_cb_t* rotate_cb = NULL;
  332. bool need_swap = false;
  333. if(canvas->orientation != orientation) {
  334. switch(orientation) {
  335. case CanvasOrientationHorizontal:
  336. need_swap = canvas->orientation == CanvasOrientationVertical ||
  337. canvas->orientation == CanvasOrientationVerticalFlip;
  338. rotate_cb = U8G2_R0;
  339. break;
  340. case CanvasOrientationHorizontalFlip:
  341. need_swap = canvas->orientation == CanvasOrientationVertical ||
  342. canvas->orientation == CanvasOrientationVerticalFlip;
  343. rotate_cb = U8G2_R2;
  344. break;
  345. case CanvasOrientationVertical:
  346. need_swap = canvas->orientation == CanvasOrientationHorizontal ||
  347. canvas->orientation == CanvasOrientationHorizontalFlip;
  348. rotate_cb = U8G2_R3;
  349. break;
  350. case CanvasOrientationVerticalFlip:
  351. need_swap = canvas->orientation == CanvasOrientationHorizontal ||
  352. canvas->orientation == CanvasOrientationHorizontalFlip;
  353. rotate_cb = U8G2_R1;
  354. break;
  355. default:
  356. furi_assert(0);
  357. }
  358. if(need_swap) FURI_SWAP(canvas->width, canvas->height);
  359. u8g2_SetDisplayRotation(&canvas->fb, rotate_cb);
  360. canvas->orientation = orientation;
  361. }
  362. }
  363. CanvasOrientation canvas_get_orientation(const Canvas* canvas) {
  364. return canvas->orientation;
  365. }