265 lines
8.0 KiB
C
265 lines
8.0 KiB
C
|
/**
|
||
|
* @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;
|
||
|
}
|