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