/** * @file lv_draw_label.c * */ /********************* * INCLUDES *********************/ #include "lv_draw_label.h" #include "lv_draw_rbasic.h" #include "../lv_misc/lv_math.h" /********************* * DEFINES *********************/ #define LABEL_RECOLOR_PAR_LENGTH 6 /********************** * TYPEDEFS **********************/ enum { CMD_STATE_WAIT, CMD_STATE_PAR, CMD_STATE_IN, }; typedef uint8_t cmd_state_t; /********************** * STATIC PROTOTYPES **********************/ static uint8_t hex_char_to_num(char hex); /********************** * STATIC VARIABLES **********************/ /********************** * MACROS **********************/ /********************** * GLOBAL FUNCTIONS **********************/ /** * Write a text * @param coords coordinates of the label * @param mask the label will be drawn only in this area * @param style pointer to a style * @param opa_scale scale down all opacities by the factor * @param txt 0 terminated text to write * @param flag settings for the text from 'txt_flag_t' enum * @param offset text offset in x and y direction (NULL if unused) * */ void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale, const char * txt, lv_txt_flag_t flag, lv_point_t * offset) { const lv_font_t * font = style->text.font; lv_coord_t w; if((flag & LV_TXT_FLAG_EXPAND) == 0) { /*Normally use the label's width as width*/ w = lv_area_get_width(coords); } else { /*If EXAPND is enabled then not limit the text's width to the object's width*/ lv_point_t p; lv_txt_get_size(&p, txt, style->text.font, style->text.letter_space, style->text.line_space, LV_COORD_MAX, flag); w = p.x; } lv_coord_t line_height = lv_font_get_height(font) + style->text.line_space; /*Init variables for the first line*/ lv_coord_t line_width = 0; lv_point_t pos; pos.x = coords->x1; pos.y = coords->y1; lv_coord_t x_ofs = 0; lv_coord_t y_ofs = 0; if(offset != NULL) { x_ofs = offset->x; y_ofs = offset->y; pos.y += y_ofs; } uint32_t line_start = 0; uint32_t line_end = lv_txt_get_next_line(txt, font, style->text.letter_space, w, flag); /*Go the first visible line*/ while(pos.y + line_height < mask->y1) { /*Go to next line*/ line_start = line_end; line_end += lv_txt_get_next_line(&txt[line_start], font, style->text.letter_space, w, flag); pos.y += line_height; if(txt[line_start] == '\0') return; } /*Align to middle*/ if(flag & LV_TXT_FLAG_CENTER) { line_width = lv_txt_get_width(&txt[line_start], line_end - line_start, font, style->text.letter_space, flag); pos.x += (lv_area_get_width(coords) - line_width) / 2; } /*Align to the right*/ else if(flag & LV_TXT_FLAG_RIGHT) { line_width = lv_txt_get_width(&txt[line_start], line_end - line_start, font, style->text.letter_space, flag); pos.x += lv_area_get_width(coords) - line_width; } lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->text.opa : (uint16_t)((uint16_t) style->text.opa * opa_scale) >> 8; cmd_state_t cmd_state = CMD_STATE_WAIT; uint32_t i; uint16_t par_start = 0; lv_color_t recolor; lv_coord_t letter_w; /*Real draw need a background color for higher bpp letter*/ #if LV_VDB_SIZE == 0 lv_rletter_set_background(style->body.main_color); #endif /*Write out all lines*/ while(txt[line_start] != '\0') { if(offset != NULL) { pos.x += x_ofs; } /*Write all letter of a line*/ cmd_state = CMD_STATE_WAIT; i = line_start; uint32_t letter; while(i < line_end) { letter = lv_txt_encoded_next(txt, &i); /*Handle the re-color command*/ if((flag & LV_TXT_FLAG_RECOLOR) != 0) { if(letter == (uint32_t)LV_TXT_COLOR_CMD[0]) { if(cmd_state == CMD_STATE_WAIT) { /*Start char*/ par_start = i; cmd_state = CMD_STATE_PAR; continue; } else if(cmd_state == CMD_STATE_PAR) { /*Other start char in parameter escaped cmd. char */ cmd_state = CMD_STATE_WAIT; } else if(cmd_state == CMD_STATE_IN) { /*Command end */ cmd_state = CMD_STATE_WAIT; continue; } } /*Skip the color parameter and wait the space after it*/ if(cmd_state == CMD_STATE_PAR) { if(letter == ' ') { /*Get the parameter*/ if(i - par_start == LABEL_RECOLOR_PAR_LENGTH + 1) { char buf[LABEL_RECOLOR_PAR_LENGTH + 1]; memcpy(buf, &txt[par_start], LABEL_RECOLOR_PAR_LENGTH); buf[LABEL_RECOLOR_PAR_LENGTH] = '\0'; int r, g, b; r = (hex_char_to_num(buf[0]) << 4) + hex_char_to_num(buf[1]); g = (hex_char_to_num(buf[2]) << 4) + hex_char_to_num(buf[3]); b = (hex_char_to_num(buf[4]) << 4) + hex_char_to_num(buf[5]); recolor = LV_COLOR_MAKE(r, g, b); } else { recolor.full = style->text.color.full; } cmd_state = CMD_STATE_IN; /*After the parameter the text is in the command*/ } continue; } } lv_color_t color = style->text.color; if(cmd_state == CMD_STATE_IN) color = recolor; letter_fp(&pos, mask, font, letter, color, opa); letter_w = lv_font_get_width(font, letter); if(letter_w > 0){ pos.x += letter_w + style->text.letter_space; } } /*Go to next line*/ line_start = line_end; line_end += lv_txt_get_next_line(&txt[line_start], font, style->text.letter_space, w, flag); pos.x = coords->x1; /*Align to middle*/ if(flag & LV_TXT_FLAG_CENTER) { line_width = lv_txt_get_width(&txt[line_start], line_end - line_start, font, style->text.letter_space, flag); pos.x += (lv_area_get_width(coords) - line_width) / 2; } /*Align to the right*/ else if(flag & LV_TXT_FLAG_RIGHT) { line_width = lv_txt_get_width(&txt[line_start], line_end - line_start, font, style->text.letter_space, flag); pos.x += lv_area_get_width(coords) - line_width; } /*Go the next line position*/ pos.y += line_height; if(pos.y > mask->y2) return; } } /********************** * STATIC FUNCTIONS **********************/ /** * Convert a hexadecimal characters to a number (0..15) * @param hex Pointer to a hexadecimal character (0..9, A..F) * @return the numerical value of `hex` or 0 on error */ static uint8_t hex_char_to_num(char hex) { uint8_t result = 0; if(hex >= '0' && hex <= '9') { result = hex - '0'; } else { if(hex >= 'a') hex -= 'a' - 'A'; /*Convert to upper case*/ switch(hex) { case 'A': result = 10; break; case 'B': result = 11; break; case 'C': result = 12; break; case 'D': result = 13; break; case 'E': result = 14; break; case 'F': result = 15; break; default: result = 0; break; } } return result; }