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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  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 "text.h"
  22. typedef struct {
  23. struct text_conf *options;
  24. uint16_t anchor;
  25. int y;
  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. || (y < s->options->y)
  50. || (y >= LCD_HEIGHT)) || (x >= LCD_WIDTH)
  51. || (x < 0) || ((x + count) >= (s->options->x + s->options->width))) {
  52. return;
  53. }
  54. while (count--) {
  55. lcd_write_point(x, y,
  56. blend(s->options->fg, s->options->bg, alpha));
  57. x++;
  58. }
  59. }
  60. static uint8_t character_callback(int16_t x, int16_t y, mf_char character,
  61. void *state) {
  62. state_t *s = (state_t*)state;
  63. uint8_t w = mf_render_character(s->options->font->font, x, y, character, pixel_callback, state);
  64. return w;
  65. }
  66. static bool line_callback(const char *line, uint16_t count, void *state) {
  67. state_t *s = (state_t*)state;
  68. if (s->y < (s->options->y - s->options->font->font->line_height)) {
  69. s->y += s->options->font->font->line_height;
  70. return true;
  71. }
  72. if (s->options->bg != TEXT_BG_NONE) {
  73. int16_t width = mf_get_string_width(s->options->font->font, line, count, false) + 2 * s->options->margin;
  74. int16_t line_height = s->options->font->font->line_height;
  75. if (s->options->alignment == MF_ALIGN_LEFT) {
  76. lcd_write_rect(s->options->x,
  77. MAX(s->y, s->options->y),
  78. s->options->x + width,
  79. MIN(s->y + line_height, s->options->y + s->options->height),
  80. s->options->bg);
  81. } else if (s->options->alignment == MF_ALIGN_CENTER) {
  82. lcd_write_rect(s->options->x + s->options->width / 2 - width / 2,
  83. MAX(s->y, s->options->y),
  84. s->options->x + s->options->width / 2 + width / 2,
  85. MIN(s->y + line_height, s->options->y + s->options->height),
  86. s->options->bg);
  87. } else if (s->options->alignment == MF_ALIGN_RIGHT) {
  88. lcd_write_rect(s->options->x + s->options->width - width,
  89. MAX(s->y, s->options->y),
  90. s->options->x + s->options->width,
  91. MIN(s->y + line_height, s->options->y + s->options->height),
  92. s->options->bg);
  93. }
  94. }
  95. if (s->options->justify) {
  96. mf_render_justified(s->options->font->font, s->anchor + s->options->x, s->y,
  97. s->options->width - s->options->margin * 2,
  98. line, count, character_callback, state);
  99. } else {
  100. mf_render_aligned(s->options->font->font, s->anchor + s->options->x, s->y,
  101. s->options->alignment, line, count,
  102. character_callback, state);
  103. }
  104. s->y += s->options->font->font->line_height;
  105. return (s->y < (s->options->y + s->options->height))
  106. && (s->y < LCD_HEIGHT);
  107. }
  108. void text_prepare_font(struct text_font *tf) {
  109. if (!tf) {
  110. debug("invalid param");
  111. return;
  112. }
  113. debug("searching for \"%s\"", tf->fontname);
  114. const struct mf_font_s *font = mf_find_font(tf->fontname);
  115. if (!font) {
  116. debug("No such font: %s", tf->fontname);
  117. return;
  118. }
  119. tf->font = font;
  120. // TODO
  121. //struct mf_scaledfont_s scaledfont;
  122. //if (tf->scale > 1) {
  123. // mf_scale_font(&scaledfont, font, tf->scale, tf->scale);
  124. // tf->font = scaledfont.font;
  125. //}
  126. }
  127. int16_t text_draw(struct text_conf *tc) {
  128. if ((!tc) || (!tc->font) || (!tc->font->font)) {
  129. debug("invalid param");
  130. return 0;
  131. }
  132. state_t state;
  133. state.options = tc;
  134. state.y = tc->y + tc->y_text_off;
  135. if (tc->alignment == MF_ALIGN_LEFT) {
  136. state.anchor = tc->margin;
  137. } else if (tc->alignment == MF_ALIGN_CENTER) {
  138. state.anchor = tc->width / 2;
  139. } else if (tc->alignment == MF_ALIGN_RIGHT) {
  140. state.anchor = tc->width - tc->margin;
  141. }
  142. mf_wordwrap(tc->font->font, tc->width - 2 * tc->margin,
  143. tc->text, line_callback, &state);
  144. return state.y;
  145. }