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.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  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 "config.h"
  19. #include "log.h"
  20. #include "lcd.h"
  21. #include "menu.h"
  22. #include "text.h"
  23. typedef struct {
  24. struct text_conf *options;
  25. uint16_t anchor;
  26. } state_t;
  27. static uint32_t blend(uint32_t fg_c, uint32_t bg_c, uint8_t alpha) {
  28. float bg[4] = { RGB_565_REV(bg_c), 1.0f };
  29. float fg[4] = { RGB_565_REV(fg_c), alpha / 255.0f };
  30. float r[4];
  31. r[3] = 1.0f - (1.0f - fg[3]) * (1.0f - bg[3]);
  32. if (r[3] < 1.0e-6f) {
  33. r[0] = 0.0f;
  34. r[1] = 0.0f;
  35. r[2] = 0.0f;
  36. } else {
  37. r[0] = fg[0] * fg[3] / r[3] + bg[0] * bg[3] * (1.0f - fg[3]) / r[3];
  38. r[1] = fg[1] * fg[3] / r[3] + bg[1] * bg[3] * (1.0f - fg[3]) / r[3];
  39. r[2] = fg[2] * fg[3] / r[3] + bg[2] * bg[3] * (1.0f - fg[3]) / r[3];
  40. }
  41. return RGB_565((uint32_t)(r[0] * 255.0f),
  42. (uint32_t)(r[1] * 255.0f),
  43. (uint32_t)(r[2] * 255.0f));
  44. }
  45. static void pixel_callback(int16_t x, int16_t y, uint8_t count, uint8_t alpha,
  46. void *state) {
  47. state_t *s = (state_t*)state;
  48. if ((y < 0) || (y >= (s->options->y + s->options->height))
  49. || (x < 0) || ((x + count) >= (s->options->x + s->options->width))) {
  50. return;
  51. }
  52. while (count--) {
  53. lcd_write_point(x, y,
  54. blend(s->options->fg, s->options->bg, alpha));
  55. x++;
  56. }
  57. }
  58. static uint8_t character_callback(int16_t x, int16_t y, mf_char character,
  59. void *state) {
  60. state_t *s = (state_t*)state;
  61. uint8_t w = mf_render_character(s->options->font->font, x, y, character, pixel_callback, state);
  62. return w;
  63. }
  64. static bool line_callback(const char *line, uint16_t count, void *state) {
  65. state_t *s = (state_t*)state;
  66. if (s->options->bg != TEXT_BG_NONE) {
  67. int16_t width = mf_get_string_width(s->options->font->font, line, count, false) + 2 * s->options->margin;
  68. int16_t line_height = s->options->font->font->line_height;
  69. if (s->options->alignment == MF_ALIGN_LEFT) {
  70. lcd_write_rect(s->options->x,
  71. s->options->y,
  72. s->options->x + width,
  73. s->options->y + line_height,
  74. s->options->bg);
  75. } else if (s->options->alignment == MF_ALIGN_CENTER) {
  76. lcd_write_rect(s->options->x + s->options->width / 2 - width / 2,
  77. s->options->y,
  78. s->options->x + s->options->width / 2 + width / 2,
  79. s->options->y + line_height,
  80. s->options->bg);
  81. } else if (s->options->alignment == MF_ALIGN_RIGHT) {
  82. lcd_write_rect(s->options->x + s->options->width - width,
  83. s->options->y,
  84. s->options->x + s->options->width,
  85. s->options->y + line_height,
  86. s->options->bg);
  87. }
  88. }
  89. if (s->options->justify) {
  90. mf_render_justified(s->options->font->font, s->anchor + s->options->x, s->options->y,
  91. s->options->width - s->options->margin * 2,
  92. line, count, character_callback, state);
  93. } else {
  94. mf_render_aligned(s->options->font->font, s->anchor + s->options->x, s->options->y,
  95. s->options->alignment, line, count,
  96. character_callback, state);
  97. }
  98. s->options->y += s->options->font->font->line_height;
  99. return true;
  100. }
  101. void text_prepare_font(struct text_font *tf) {
  102. if (!tf) {
  103. debug("invalid param");
  104. return;
  105. }
  106. debug("searching for \"%s\"", tf->fontname);
  107. const struct mf_font_s *font = mf_find_font(tf->fontname);
  108. if (!font) {
  109. debug("No such font: %s", tf->fontname);
  110. return;
  111. }
  112. tf->font = font;
  113. // TODO
  114. //struct mf_scaledfont_s scaledfont;
  115. //if (tf->scale > 1) {
  116. // mf_scale_font(&scaledfont, font, tf->scale, tf->scale);
  117. // tf->font = scaledfont.font;
  118. //}
  119. }
  120. void text_draw(struct text_conf *tc) {
  121. if ((!tc) || (!tc->font) || (!tc->font->font)) {
  122. debug("invalid param");
  123. return;
  124. }
  125. state_t state;
  126. state.options = tc;
  127. if (tc->alignment == MF_ALIGN_LEFT) {
  128. state.anchor = tc->margin;
  129. } else if (tc->alignment == MF_ALIGN_CENTER) {
  130. state.anchor = tc->width / 2;
  131. } else if (tc->alignment == MF_ALIGN_RIGHT) {
  132. state.anchor = tc->width - tc->margin;
  133. }
  134. mf_wordwrap(tc->font->font, tc->width - 2 * tc->margin,
  135. tc->text, line_callback, &state);
  136. }
  137. void text_box(const char *s, bool centered) {
  138. static struct text_font font = {
  139. .fontname = "fixed_10x20",
  140. .font = NULL,
  141. };
  142. if (font.font == NULL) {
  143. text_prepare_font(&font);
  144. }
  145. int x_off = 0;
  146. int width = LCD_WIDTH;
  147. int y_off = 50;
  148. int height = (MENU_MAX_LINES * 20) + ((MENU_MAX_LINES - 1) * 2);
  149. struct text_conf text = {
  150. .text = "",
  151. .x = x_off,
  152. .y = y_off,
  153. .justify = false,
  154. .alignment = centered ? MF_ALIGN_CENTER : MF_ALIGN_LEFT,
  155. .width = width,
  156. .height = height,
  157. .margin = 2,
  158. .fg = RGB_565(0xFF, 0xFF, 0xFF),
  159. .bg = TEXT_BG_NONE,
  160. .font = &font,
  161. };
  162. lcd_write_rect(x_off, y_off,
  163. x_off + width - 1,
  164. y_off + height - 1,
  165. RGB_565(0x00, 0x00, 0x00));
  166. text.text = s;
  167. text_draw(&text);
  168. }