S&B Volcano vaporizer remote control with Pi Pico W
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.

text.c 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. /*
  2. * text.c
  3. *
  4. * Copyright (c) 2023 Thomas Buck (thomas@xythobuz.de)
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * See <http://www.gnu.org/licenses/>.
  17. */
  18. #include <string.h>
  19. #include "config.h"
  20. #include "log.h"
  21. #include "lcd.h"
  22. #include "menu.h"
  23. #include "text.h"
  24. typedef struct {
  25. struct text_conf *options;
  26. uint16_t anchor;
  27. int y;
  28. } state_t;
  29. static uint32_t blend(uint32_t fg_c, uint32_t bg_c, uint8_t alpha) {
  30. float bg[4] = { RGB_565_REV(bg_c), 1.0f };
  31. float fg[4] = { RGB_565_REV(fg_c), alpha / 255.0f };
  32. float r[4];
  33. r[3] = 1.0f - (1.0f - fg[3]) * (1.0f - bg[3]);
  34. if (r[3] < 1.0e-6f) {
  35. r[0] = 0.0f;
  36. r[1] = 0.0f;
  37. r[2] = 0.0f;
  38. } else {
  39. r[0] = fg[0] * fg[3] / r[3] + bg[0] * bg[3] * (1.0f - fg[3]) / r[3];
  40. r[1] = fg[1] * fg[3] / r[3] + bg[1] * bg[3] * (1.0f - fg[3]) / r[3];
  41. r[2] = fg[2] * fg[3] / r[3] + bg[2] * bg[3] * (1.0f - fg[3]) / r[3];
  42. }
  43. return RGB_565((uint32_t)(r[0] * 255.0f),
  44. (uint32_t)(r[1] * 255.0f),
  45. (uint32_t)(r[2] * 255.0f));
  46. }
  47. static void pixel_callback(int16_t x, int16_t y, uint8_t count, uint8_t alpha,
  48. void *state) {
  49. state_t *s = (state_t*)state;
  50. if ((y < 0) || (y >= (s->options->y + s->options->height)
  51. || (y < s->options->y)
  52. || (y >= LCD_HEIGHT)) || (x >= LCD_WIDTH)
  53. || (x < 0) || ((x + count) >= (s->options->x + s->options->width))) {
  54. return;
  55. }
  56. while (count--) {
  57. lcd_write_point(x, y,
  58. blend(s->options->fg, s->options->bg, alpha));
  59. x++;
  60. }
  61. }
  62. static uint8_t character_callback(int16_t x, int16_t y, mf_char character,
  63. void *state) {
  64. state_t *s = (state_t*)state;
  65. uint8_t w = mf_render_character(s->options->font->font, x, y, character, pixel_callback, state);
  66. return w;
  67. }
  68. static bool line_callback(const char *line, uint16_t count, void *state) {
  69. state_t *s = (state_t*)state;
  70. if (s->y < (s->options->y - s->options->font->font->line_height)) {
  71. s->y += s->options->font->font->line_height;
  72. return true;
  73. }
  74. if (s->options->bg != TEXT_BG_NONE) {
  75. int16_t width = mf_get_string_width(s->options->font->font, line, count, false) + 2 * s->options->margin;
  76. int16_t line_height = s->options->font->font->line_height;
  77. if (s->options->alignment == MF_ALIGN_LEFT) {
  78. lcd_write_rect(s->options->x,
  79. MAX(s->y, s->options->y),
  80. s->options->x + width,
  81. MIN(s->y + line_height, s->options->y + s->options->height),
  82. s->options->bg);
  83. } else if (s->options->alignment == MF_ALIGN_CENTER) {
  84. lcd_write_rect(s->options->x + s->options->width / 2 - width / 2,
  85. MAX(s->y, s->options->y),
  86. s->options->x + s->options->width / 2 + width / 2,
  87. MIN(s->y + line_height, s->options->y + s->options->height),
  88. s->options->bg);
  89. } else if (s->options->alignment == MF_ALIGN_RIGHT) {
  90. lcd_write_rect(s->options->x + s->options->width - width,
  91. MAX(s->y, s->options->y),
  92. s->options->x + s->options->width,
  93. MIN(s->y + line_height, s->options->y + s->options->height),
  94. s->options->bg);
  95. }
  96. }
  97. if (s->options->justify) {
  98. mf_render_justified(s->options->font->font, s->anchor + s->options->x, s->y,
  99. s->options->width - s->options->margin * 2,
  100. line, count, character_callback, state);
  101. } else {
  102. mf_render_aligned(s->options->font->font, s->anchor + s->options->x, s->y,
  103. s->options->alignment, line, count,
  104. character_callback, state);
  105. }
  106. s->y += s->options->font->font->line_height;
  107. return (s->y < (s->options->y + s->options->height))
  108. && (s->y < LCD_HEIGHT);
  109. }
  110. void text_prepare_font(struct text_font *tf) {
  111. if (!tf) {
  112. debug("invalid param");
  113. return;
  114. }
  115. debug("searching for \"%s\"", tf->fontname);
  116. const struct mf_font_s *font = mf_find_font(tf->fontname);
  117. if (!font) {
  118. debug("No such font: %s", tf->fontname);
  119. return;
  120. }
  121. tf->font = font;
  122. // TODO
  123. //struct mf_scaledfont_s scaledfont;
  124. //if (tf->scale > 1) {
  125. // mf_scale_font(&scaledfont, font, tf->scale, tf->scale);
  126. // tf->font = scaledfont.font;
  127. //}
  128. }
  129. int16_t text_draw(struct text_conf *tc) {
  130. if ((!tc) || (!tc->font) || (!tc->font->font)) {
  131. debug("invalid param");
  132. return 0;
  133. }
  134. state_t state;
  135. state.options = tc;
  136. state.y = tc->y + tc->y_text_off;
  137. if (tc->alignment == MF_ALIGN_LEFT) {
  138. state.anchor = tc->margin;
  139. } else if (tc->alignment == MF_ALIGN_CENTER) {
  140. state.anchor = tc->width / 2;
  141. } else if (tc->alignment == MF_ALIGN_RIGHT) {
  142. state.anchor = tc->width - tc->margin;
  143. }
  144. mf_wordwrap(tc->font->font, tc->width - 2 * tc->margin,
  145. tc->text, line_callback, &state);
  146. return state.y;
  147. }
  148. int16_t text_box(const char *s, bool centered,
  149. const char *fontname,
  150. uint16_t x_off, uint16_t width,
  151. uint16_t y_off, uint16_t height,
  152. int16_t y_text_off) {
  153. static struct text_font font = {
  154. .fontname = "",
  155. .font = NULL,
  156. };
  157. if ((font.font == NULL) || (strcmp(font.fontname, fontname) != 0)) {
  158. font.fontname = fontname;
  159. text_prepare_font(&font);
  160. }
  161. struct text_conf text = {
  162. .text = s,
  163. .x = x_off,
  164. .y = y_off,
  165. .y_text_off = y_text_off,
  166. .justify = false,
  167. .alignment = centered ? MF_ALIGN_CENTER : MF_ALIGN_LEFT,
  168. .width = width,
  169. .height = height,
  170. .margin = 2,
  171. .fg = RGB_565(0xFF, 0xFF, 0xFF),
  172. .bg = TEXT_BG_NONE,
  173. .font = &font,
  174. };
  175. lcd_write_rect(x_off, y_off,
  176. x_off + width - 1,
  177. y_off + height - 1,
  178. RGB_565(0x00, 0x00, 0x00));
  179. return text_draw(&text);
  180. }