acid-drop- Hacking the planet from a LilyGo T-Deck using custom firmware |
git clone git://git.acid.vegas/acid-drop.git |
Log | Files | Refs | Archive | README | LICENSE |
lv_draw_label.c (13479B)
1 /** 2 * @file lv_draw_label.c 3 * 4 */ 5 6 /********************* 7 * INCLUDES 8 *********************/ 9 #include "lv_draw.h" 10 #include "lv_draw_label.h" 11 #include "../misc/lv_math.h" 12 #include "../hal/lv_hal_disp.h" 13 #include "../core/lv_refr.h" 14 #include "../misc/lv_bidi.h" 15 #include "../misc/lv_assert.h" 16 17 /********************* 18 * DEFINES 19 *********************/ 20 #define LABEL_RECOLOR_PAR_LENGTH 6 21 #define LV_LABEL_HINT_UPDATE_TH 1024 /*Update the "hint" if the label's y coordinates have changed more then this*/ 22 23 /********************** 24 * TYPEDEFS 25 **********************/ 26 enum { 27 CMD_STATE_WAIT, 28 CMD_STATE_PAR, 29 CMD_STATE_IN, 30 }; 31 typedef uint8_t cmd_state_t; 32 33 /********************** 34 * STATIC PROTOTYPES 35 **********************/ 36 37 static uint8_t hex_char_to_num(char hex); 38 39 /********************** 40 * STATIC VARIABLES 41 **********************/ 42 43 /********************** 44 * GLOBAL VARIABLES 45 **********************/ 46 47 /********************** 48 * MACROS 49 **********************/ 50 51 /********************** 52 * GLOBAL FUNCTIONS 53 **********************/ 54 55 void lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc) 56 { 57 lv_memset_00(dsc, sizeof(lv_draw_label_dsc_t)); 58 dsc->opa = LV_OPA_COVER; 59 dsc->color = lv_color_black(); 60 dsc->font = LV_FONT_DEFAULT; 61 dsc->sel_start = LV_DRAW_LABEL_NO_TXT_SEL; 62 dsc->sel_end = LV_DRAW_LABEL_NO_TXT_SEL; 63 dsc->sel_color = lv_color_black(); 64 dsc->sel_bg_color = lv_palette_main(LV_PALETTE_BLUE); 65 dsc->bidi_dir = LV_BASE_DIR_LTR; 66 } 67 68 /** 69 * Write a text 70 * @param coords coordinates of the label 71 * @param mask the label will be drawn only in this area 72 * @param dsc pointer to draw descriptor 73 * @param txt `\0` terminated text to write 74 * @param hint pointer to a `lv_draw_label_hint_t` variable. 75 * It is managed by the draw to speed up the drawing of very long texts (thousands of lines). 76 */ 77 LV_ATTRIBUTE_FAST_MEM void lv_draw_label(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, 78 const lv_area_t * coords, const char * txt, lv_draw_label_hint_t * hint) 79 { 80 if(dsc->opa <= LV_OPA_MIN) return; 81 if(dsc->font == NULL) { 82 LV_LOG_WARN("dsc->font == NULL"); 83 return; 84 } 85 86 if(draw_ctx->draw_letter == NULL) { 87 LV_LOG_WARN("draw->draw_letter == NULL (there is no function to draw letters)"); 88 return; 89 } 90 91 lv_draw_label_dsc_t dsc_mod = *dsc; 92 93 const lv_font_t * font = dsc->font; 94 int32_t w; 95 96 /*No need to waste processor time if string is empty*/ 97 if(txt == NULL || txt[0] == '\0') 98 return; 99 100 lv_area_t clipped_area; 101 bool clip_ok = _lv_area_intersect(&clipped_area, coords, draw_ctx->clip_area); 102 if(!clip_ok) return; 103 104 lv_text_align_t align = dsc->align; 105 lv_base_dir_t base_dir = dsc->bidi_dir; 106 107 lv_bidi_calculate_align(&align, &base_dir, txt); 108 109 if((dsc->flag & LV_TEXT_FLAG_EXPAND) == 0) { 110 /*Normally use the label's width as width*/ 111 w = lv_area_get_width(coords); 112 } 113 else { 114 /*If EXPAND is enabled then not limit the text's width to the object's width*/ 115 lv_point_t p; 116 lv_txt_get_size(&p, txt, dsc->font, dsc->letter_space, dsc->line_space, LV_COORD_MAX, 117 dsc->flag); 118 w = p.x; 119 } 120 121 int32_t line_height_font = lv_font_get_line_height(font); 122 int32_t line_height = line_height_font + dsc->line_space; 123 124 /*Init variables for the first line*/ 125 int32_t line_width = 0; 126 lv_point_t pos; 127 pos.x = coords->x1; 128 pos.y = coords->y1; 129 130 int32_t x_ofs = 0; 131 int32_t y_ofs = 0; 132 x_ofs = dsc->ofs_x; 133 y_ofs = dsc->ofs_y; 134 pos.y += y_ofs; 135 136 uint32_t line_start = 0; 137 int32_t last_line_start = -1; 138 139 /*Check the hint to use the cached info*/ 140 if(hint && y_ofs == 0 && coords->y1 < 0) { 141 /*If the label changed too much recalculate the hint.*/ 142 if(LV_ABS(hint->coord_y - coords->y1) > LV_LABEL_HINT_UPDATE_TH - 2 * line_height) { 143 hint->line_start = -1; 144 } 145 last_line_start = hint->line_start; 146 } 147 148 /*Use the hint if it's valid*/ 149 if(hint && last_line_start >= 0) { 150 line_start = last_line_start; 151 pos.y += hint->y; 152 } 153 154 uint32_t line_end = line_start + _lv_txt_get_next_line(&txt[line_start], font, dsc->letter_space, w, NULL, dsc->flag); 155 156 /*Go the first visible line*/ 157 while(pos.y + line_height_font < draw_ctx->clip_area->y1) { 158 /*Go to next line*/ 159 line_start = line_end; 160 line_end += _lv_txt_get_next_line(&txt[line_start], font, dsc->letter_space, w, NULL, dsc->flag); 161 pos.y += line_height; 162 163 /*Save at the threshold coordinate*/ 164 if(hint && pos.y >= -LV_LABEL_HINT_UPDATE_TH && hint->line_start < 0) { 165 hint->line_start = line_start; 166 hint->y = pos.y - coords->y1; 167 hint->coord_y = coords->y1; 168 } 169 170 if(txt[line_start] == '\0') return; 171 } 172 173 /*Align to middle*/ 174 if(align == LV_TEXT_ALIGN_CENTER) { 175 line_width = lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag); 176 177 pos.x += (lv_area_get_width(coords) - line_width) / 2; 178 179 } 180 /*Align to the right*/ 181 else if(align == LV_TEXT_ALIGN_RIGHT) { 182 line_width = lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag); 183 pos.x += lv_area_get_width(coords) - line_width; 184 } 185 uint32_t sel_start = dsc->sel_start; 186 uint32_t sel_end = dsc->sel_end; 187 if(sel_start > sel_end) { 188 uint32_t tmp = sel_start; 189 sel_start = sel_end; 190 sel_end = tmp; 191 } 192 lv_draw_line_dsc_t line_dsc; 193 194 if((dsc->decor & LV_TEXT_DECOR_UNDERLINE) || (dsc->decor & LV_TEXT_DECOR_STRIKETHROUGH)) { 195 lv_draw_line_dsc_init(&line_dsc); 196 line_dsc.color = dsc->color; 197 line_dsc.width = font->underline_thickness ? font->underline_thickness : 1; 198 line_dsc.opa = dsc->opa; 199 line_dsc.blend_mode = dsc->blend_mode; 200 } 201 202 cmd_state_t cmd_state = CMD_STATE_WAIT; 203 uint32_t i; 204 uint32_t par_start = 0; 205 lv_color_t recolor; 206 lv_color_t color = lv_color_black(); 207 int32_t letter_w; 208 209 lv_draw_rect_dsc_t draw_dsc_sel; 210 lv_draw_rect_dsc_init(&draw_dsc_sel); 211 draw_dsc_sel.bg_color = dsc->sel_bg_color; 212 213 int32_t pos_x_start = pos.x; 214 /*Write out all lines*/ 215 while(txt[line_start] != '\0') { 216 pos.x += x_ofs; 217 218 /*Write all letter of a line*/ 219 cmd_state = CMD_STATE_WAIT; 220 i = 0; 221 #if LV_USE_BIDI 222 char * bidi_txt = lv_mem_buf_get(line_end - line_start + 1); 223 _lv_bidi_process_paragraph(txt + line_start, bidi_txt, line_end - line_start, base_dir, NULL, 0); 224 #else 225 const char * bidi_txt = txt + line_start; 226 #endif 227 228 while(i < line_end - line_start) { 229 uint32_t logical_char_pos = 0; 230 if(sel_start != 0xFFFF && sel_end != 0xFFFF) { 231 #if LV_USE_BIDI 232 logical_char_pos = _lv_txt_encoded_get_char_id(txt, line_start); 233 uint32_t t = _lv_txt_encoded_get_char_id(bidi_txt, i); 234 logical_char_pos += _lv_bidi_get_logical_pos(bidi_txt, NULL, line_end - line_start, base_dir, t, NULL); 235 #else 236 logical_char_pos = _lv_txt_encoded_get_char_id(txt, line_start + i); 237 #endif 238 } 239 240 uint32_t letter; 241 uint32_t letter_next; 242 _lv_txt_encoded_letter_next_2(bidi_txt, &letter, &letter_next, &i); 243 /*Handle the re-color command*/ 244 if((dsc->flag & LV_TEXT_FLAG_RECOLOR) != 0) { 245 if(letter == (uint32_t)LV_TXT_COLOR_CMD[0]) { 246 if(cmd_state == CMD_STATE_WAIT) { /*Start char*/ 247 par_start = i; 248 cmd_state = CMD_STATE_PAR; 249 continue; 250 } 251 else if(cmd_state == CMD_STATE_PAR) { /*Other start char in parameter escaped cmd. char*/ 252 cmd_state = CMD_STATE_WAIT; 253 } 254 else if(cmd_state == CMD_STATE_IN) { /*Command end*/ 255 cmd_state = CMD_STATE_WAIT; 256 continue; 257 } 258 } 259 260 /*Skip the color parameter and wait the space after it*/ 261 if(cmd_state == CMD_STATE_PAR) { 262 if(letter == ' ') { 263 /*Get the parameter*/ 264 if(i - par_start == LABEL_RECOLOR_PAR_LENGTH + 1) { 265 char buf[LABEL_RECOLOR_PAR_LENGTH + 1]; 266 lv_memcpy_small(buf, &bidi_txt[par_start], LABEL_RECOLOR_PAR_LENGTH); 267 buf[LABEL_RECOLOR_PAR_LENGTH] = '\0'; 268 int r, g, b; 269 r = (hex_char_to_num(buf[0]) << 4) + hex_char_to_num(buf[1]); 270 g = (hex_char_to_num(buf[2]) << 4) + hex_char_to_num(buf[3]); 271 b = (hex_char_to_num(buf[4]) << 4) + hex_char_to_num(buf[5]); 272 recolor = lv_color_make(r, g, b); 273 } 274 else { 275 recolor.full = dsc->color.full; 276 } 277 cmd_state = CMD_STATE_IN; /*After the parameter the text is in the command*/ 278 } 279 continue; 280 } 281 } 282 283 color = dsc->color; 284 285 if(cmd_state == CMD_STATE_IN) color = recolor; 286 287 letter_w = lv_font_get_glyph_width(font, letter, letter_next); 288 289 if(sel_start != 0xFFFF && sel_end != 0xFFFF) { 290 if(logical_char_pos >= sel_start && logical_char_pos < sel_end) { 291 lv_area_t sel_coords; 292 sel_coords.x1 = pos.x; 293 sel_coords.y1 = pos.y; 294 sel_coords.x2 = pos.x + letter_w + dsc->letter_space - 1; 295 sel_coords.y2 = pos.y + line_height - 1; 296 lv_draw_rect(draw_ctx, &draw_dsc_sel, &sel_coords); 297 color = dsc->sel_color; 298 } 299 } 300 301 dsc_mod.color = color; 302 lv_draw_letter(draw_ctx, &dsc_mod, &pos, letter); 303 304 if(letter_w > 0) { 305 pos.x += letter_w + dsc->letter_space; 306 } 307 } 308 309 if(dsc->decor & LV_TEXT_DECOR_STRIKETHROUGH) { 310 lv_point_t p1; 311 lv_point_t p2; 312 p1.x = pos_x_start; 313 p1.y = pos.y + (dsc->font->line_height / 2) + line_dsc.width / 2; 314 p2.x = pos.x; 315 p2.y = p1.y; 316 line_dsc.color = color; 317 lv_draw_line(draw_ctx, &line_dsc, &p1, &p2); 318 } 319 320 if(dsc->decor & LV_TEXT_DECOR_UNDERLINE) { 321 lv_point_t p1; 322 lv_point_t p2; 323 p1.x = pos_x_start; 324 p1.y = pos.y + dsc->font->line_height - dsc->font->base_line - font->underline_position; 325 p2.x = pos.x; 326 p2.y = p1.y; 327 line_dsc.color = color; 328 lv_draw_line(draw_ctx, &line_dsc, &p1, &p2); 329 } 330 331 #if LV_USE_BIDI 332 lv_mem_buf_release(bidi_txt); 333 bidi_txt = NULL; 334 #endif 335 /*Go to next line*/ 336 line_start = line_end; 337 line_end += _lv_txt_get_next_line(&txt[line_start], font, dsc->letter_space, w, NULL, dsc->flag); 338 339 pos.x = coords->x1; 340 /*Align to middle*/ 341 if(align == LV_TEXT_ALIGN_CENTER) { 342 line_width = 343 lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag); 344 345 pos.x += (lv_area_get_width(coords) - line_width) / 2; 346 347 } 348 /*Align to the right*/ 349 else if(align == LV_TEXT_ALIGN_RIGHT) { 350 line_width = 351 lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag); 352 pos.x += lv_area_get_width(coords) - line_width; 353 } 354 355 /*Go the next line position*/ 356 pos.y += line_height; 357 358 if(pos.y > draw_ctx->clip_area->y2) return; 359 } 360 361 LV_ASSERT_MEM_INTEGRITY(); 362 } 363 364 void lv_draw_letter(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p, 365 uint32_t letter) 366 { 367 draw_ctx->draw_letter(draw_ctx, dsc, pos_p, letter); 368 } 369 370 371 /********************** 372 * STATIC FUNCTIONS 373 **********************/ 374 375 /** 376 * Convert a hexadecimal characters to a number (0..15) 377 * @param hex Pointer to a hexadecimal character (0..9, A..F) 378 * @return the numerical value of `hex` or 0 on error 379 */ 380 static uint8_t hex_char_to_num(char hex) 381 { 382 uint8_t result = 0; 383 384 if(hex >= '0' && hex <= '9') { 385 result = hex - '0'; 386 } 387 else { 388 if(hex >= 'a') hex -= 'a' - 'A'; /*Convert to upper case*/ 389 390 switch(hex) { 391 case 'A': 392 result = 10; 393 break; 394 case 'B': 395 result = 11; 396 break; 397 case 'C': 398 result = 12; 399 break; 400 case 'D': 401 result = 13; 402 break; 403 case 'E': 404 result = 14; 405 break; 406 case 'F': 407 result = 15; 408 break; 409 default: 410 result = 0; 411 break; 412 } 413 } 414 415 return result; 416 } 417