diff --git a/docs/src/details/common-widget-features/styles/style-properties.rst b/docs/src/details/common-widget-features/styles/style-properties.rst index 752c24dfda..367efeda8f 100644 --- a/docs/src/details/common-widget-features/styles/style-properties.rst +++ b/docs/src/details/common-widget-features/styles/style-properties.rst @@ -1221,6 +1221,48 @@ Set how to align the lines of the text. Note that it doesn't align the Widget it
  • Ext. draw No
  • +text_outline_stroke_color +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sets the color of letter outline stroke. + +.. raw:: html + + + +text_outline_stroke_width +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Set the letter outline stroke width in pixels. + +.. raw:: html + + + +text_outline_stroke_opa +~~~~~~~~~~~~~~~~~~~~~~~ + +Set the opacity of the letter outline stroke. Value 0, `LV_OPA_0` or `LV_OPA_TRANSP` means fully transparent, 255, `LV_OPA_100` or `LV_OPA_COVER` means fully covering, other values or LV_OPA_10, LV_OPA_20, etc means semi transparency. + +.. raw:: html + + + Miscellaneous ------------- diff --git a/docs/src/details/libs/freetype.rst b/docs/src/details/libs/freetype.rst index 3ad38b6c13..ff37ee2cc8 100644 --- a/docs/src/details/libs/freetype.rst +++ b/docs/src/details/libs/freetype.rst @@ -30,7 +30,7 @@ There are two ways to use FreeType: For UNIX -------- -For UNIX systems, the following is recommended to compile and install FreeType libraries. +For UNIX-like systems, the following is recommended to compile and install FreeType libraries. - Enter the FreeType source code directory - ``make`` @@ -124,6 +124,22 @@ please refer to the example code below. - `FreeType tutorial `__ - LVGL's :ref:`add_font` +Rendering vector fonts is supported with VGLite or ThorVG, +when using vector fonts with ThorVG, it is possible to set a letter outline of a different color. + +This is achieved by setting the style attributes with the +:cpp:func:`lv_style_set_text_outline_width` and :cpp:func:`lv_style_set_text_outline_color` functions + +You will have to account for the increased width and height of letter due to the added letter outline, +to avoid letters overlapping space them out using :cpp:func:`lv_style_set_text_letter_space` + +To use vector fonts with ThorVG, you will have to enable ``LV_USE_VECTOR_GRAPHICS`` in ``lv_conf.h`` + +.. note:: + + This feature is currently experimental, there are clipping issues especially when using large font sizes. + +See the :cpp:func:`lv_example_freetype_2_vector_font` function for a usage example .. _freetype_example: diff --git a/examples/libs/freetype/lv_example_freetype.h b/examples/libs/freetype/lv_example_freetype.h index 28a61c17d0..ca234b2ddc 100644 --- a/examples/libs/freetype/lv_example_freetype.h +++ b/examples/libs/freetype/lv_example_freetype.h @@ -27,6 +27,7 @@ extern "C" { **********************/ void lv_example_freetype_1(void); void lv_example_freetype_2(void); +void lv_example_freetype_2_vector_font(uint32_t font_size, uint32_t border_width); /********************** * MACROS diff --git a/examples/libs/freetype/lv_example_freetype_2.c b/examples/libs/freetype/lv_example_freetype_2.c index 3067fb70b0..095ccc7810 100644 --- a/examples/libs/freetype/lv_example_freetype_2.c +++ b/examples/libs/freetype/lv_example_freetype_2.c @@ -5,9 +5,49 @@ #if LV_FREETYPE_USE_LVGL_PORT #define PATH_PREFIX "A:" #else - #define PATH_PREFIX "../" + #define PATH_PREFIX "./" #endif +/* + * Load a vector font + * ThorVG needs to be enabled, LV_USE_VECTOR_GRAPHICS=1 + */ +void lv_example_freetype_2_vector_font(uint32_t font_size, uint32_t border_width) +{ + lv_font_t * font = lv_freetype_font_create(PATH_PREFIX "lvgl/examples/libs/freetype/Lato-Regular.ttf", + LV_FREETYPE_FONT_RENDER_MODE_OUTLINE, + font_size, + LV_FREETYPE_FONT_STYLE_NORMAL); + + + if(!font) { + LV_LOG_ERROR("Freetype font create failed."); + return; + } + + /*Create style with the new font*/ + static lv_style_t style; + lv_style_init(&style); + lv_style_set_text_font(&style, font); + lv_style_set_text_align(&style, LV_TEXT_ALIGN_CENTER); + lv_style_set_text_color(&style, lv_color_hex(0xFF0000)); + lv_style_set_text_opa(&style, LV_OPA_100); + lv_style_set_text_outline_stroke_opa(&style, LV_OPA_100); + lv_style_set_text_outline_stroke_color(&style, lv_color_hex(0x00FF00)); + lv_style_set_text_outline_stroke_width(&style, border_width); + + /*Avoid overlapping issue when using letter outlines*/ + lv_style_set_text_letter_space(&style, border_width); + lv_style_set_text_line_space(&style, border_width); + + /*Create a label with the new style*/ + lv_obj_t * label = lv_label_create(lv_screen_active()); + lv_obj_add_style(label, &style, 0); + lv_label_set_text(label, "Hello world\nI'm a font created with FreeType"); + lv_obj_center(label); + +} + /** * Load a font with FreeType */ @@ -16,7 +56,7 @@ void lv_example_freetype_2(void) /*Create a font*/ lv_font_t * font = lv_freetype_font_create(PATH_PREFIX "lvgl/examples/libs/freetype/Lato-Regular.ttf", LV_FREETYPE_FONT_RENDER_MODE_BITMAP, - 24, + 400, LV_FREETYPE_FONT_STYLE_NORMAL); /* this font is created from a downscaled NotoColorEmoji to 34x32px @@ -24,7 +64,7 @@ void lv_example_freetype_2(void) * Command: fonttools subset NotoColorEmoji.ttf --text=😀 */ lv_font_t * font_emoji = lv_freetype_font_create(PATH_PREFIX "lvgl/examples/libs/freetype/NotoColorEmoji-32.subset.ttf", LV_FREETYPE_FONT_RENDER_MODE_BITMAP, - 24, + 200, LV_FREETYPE_FONT_STYLE_NORMAL); if(!font || !font_emoji) { diff --git a/scripts/style_api_gen.py b/scripts/style_api_gen.py index 75e108def1..21acadcaf8 100755 --- a/scripts/style_api_gen.py +++ b/scripts/style_api_gen.py @@ -348,6 +348,18 @@ props = [ 'style_type': 'num', 'var_type': 'lv_text_align_t' , 'default':'`LV_TEXT_ALIGN_AUTO`', 'inherited': 1, 'layout': 1, 'ext_draw': 0, 'dsc': "Set how to align the lines of the text. Note that it doesn't align the Widget itself, only the lines inside the Widget. Possible values are `LV_TEXT_ALIGN_LEFT/CENTER/RIGHT/AUTO`. `LV_TEXT_ALIGN_AUTO` detect the text base direction and uses left or right alignment accordingly"}, +{'name': 'TEXT_OUTLINE_STROKE_COLOR', +'style_type': 'color', 'var_type': 'lv_color_t', 'default':'`0x000000`', 'inherited': 1, 'layout': 0, 'ext_draw': 0, 'filtered': 1, + 'dsc': "Sets the color of letter outline stroke."}, + +{'name': 'TEXT_OUTLINE_STROKE_WIDTH', + 'style_type': 'num', 'var_type': 'int32_t' , 'default':0, 'inherited': 1, 'layout': 1, 'ext_draw': 0, + 'dsc': "Set the letter outline stroke width in pixels."}, + +{'name': 'TEXT_OUTLINE_STROKE_OPA', + 'style_type': 'num', 'var_type': 'lv_opa_t', 'default':'`LV_OPA_COVER`', 'inherited': 1, 'layout': 0, 'ext_draw': 0, + 'dsc': "Set the opacity of the letter outline stroke. Value 0, `LV_OPA_0` or `LV_OPA_TRANSP` means fully transparent, 255, `LV_OPA_100` or `LV_OPA_COVER` means fully covering, other values or LV_OPA_10, LV_OPA_20, etc means semi transparency."}, + {'section': 'Miscellaneous', 'dsc':'Mixed properties for various purposes.' }, {'name': 'RADIUS', 'style_type': 'num', 'var_type': 'int32_t', 'default':0, 'inherited': 0, 'layout': 0, 'ext_draw': 0, diff --git a/src/core/lv_obj_style_gen.c b/src/core/lv_obj_style_gen.c index 5fcb31df17..8910d9a0e3 100644 --- a/src/core/lv_obj_style_gen.c +++ b/src/core/lv_obj_style_gen.c @@ -674,6 +674,30 @@ void lv_obj_set_style_text_align(lv_obj_t * obj, lv_text_align_t value, lv_style lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_ALIGN, v, selector); } +void lv_obj_set_style_text_outline_stroke_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) +{ + lv_style_value_t v = { + .color = value + }; + lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_OUTLINE_STROKE_COLOR, v, selector); +} + +void lv_obj_set_style_text_outline_stroke_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_OUTLINE_STROKE_WIDTH, v, selector); +} + +void lv_obj_set_style_text_outline_stroke_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_OUTLINE_STROKE_OPA, v, selector); +} + void lv_obj_set_style_radius(lv_obj_t * obj, int32_t value, lv_style_selector_t selector) { lv_style_value_t v = { diff --git a/src/core/lv_obj_style_gen.h b/src/core/lv_obj_style_gen.h index 0e13447567..ff85e38b92 100644 --- a/src/core/lv_obj_style_gen.h +++ b/src/core/lv_obj_style_gen.h @@ -577,6 +577,30 @@ static inline lv_text_align_t lv_obj_get_style_text_align(const lv_obj_t * obj, return (lv_text_align_t)v.num; } +static inline lv_color_t lv_obj_get_style_text_outline_stroke_color(const lv_obj_t * obj, lv_part_t part) +{ + lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_OUTLINE_STROKE_COLOR); + return v.color; +} + +static inline lv_color_t lv_obj_get_style_text_outline_stroke_color_filtered(const lv_obj_t * obj, lv_part_t part) +{ + lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_OUTLINE_STROKE_COLOR)); + return v.color; +} + +static inline int32_t lv_obj_get_style_text_outline_stroke_width(const lv_obj_t * obj, lv_part_t part) +{ + lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_OUTLINE_STROKE_WIDTH); + return (int32_t)v.num; +} + +static inline lv_opa_t lv_obj_get_style_text_outline_stroke_opa(const lv_obj_t * obj, lv_part_t part) +{ + lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_OUTLINE_STROKE_OPA); + return (lv_opa_t)v.num; +} + static inline int32_t lv_obj_get_style_radius(const lv_obj_t * obj, lv_part_t part) { lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_RADIUS); @@ -858,6 +882,9 @@ void lv_obj_set_style_text_letter_space(lv_obj_t * obj, int32_t value, lv_style_ void lv_obj_set_style_text_line_space(lv_obj_t * obj, int32_t value, lv_style_selector_t selector); void lv_obj_set_style_text_decor(lv_obj_t * obj, lv_text_decor_t value, lv_style_selector_t selector); void lv_obj_set_style_text_align(lv_obj_t * obj, lv_text_align_t value, lv_style_selector_t selector); +void lv_obj_set_style_text_outline_stroke_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector); +void lv_obj_set_style_text_outline_stroke_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector); +void lv_obj_set_style_text_outline_stroke_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector); void lv_obj_set_style_radius(lv_obj_t * obj, int32_t value, lv_style_selector_t selector); void lv_obj_set_style_radial_offset(lv_obj_t * obj, int32_t value, lv_style_selector_t selector); void lv_obj_set_style_clip_corner(lv_obj_t * obj, bool value, lv_style_selector_t selector); diff --git a/src/draw/lv_draw_label.c b/src/draw/lv_draw_label.c index 2959c07e85..7375d06f85 100644 --- a/src/draw/lv_draw_label.c +++ b/src/draw/lv_draw_label.c @@ -315,6 +315,11 @@ void lv_draw_label_iterate_characters(lv_draw_task_t * t, const lv_draw_label_ds draw_letter_dsc.color = dsc->color; draw_letter_dsc.rotation = dsc->rotation; + /* Set letter outline stroke attributes */ + draw_letter_dsc.outline_stroke_width = dsc->outline_stroke_width; + draw_letter_dsc.outline_stroke_opa = dsc->outline_stroke_opa; + draw_letter_dsc.outline_stroke_color = dsc->outline_stroke_color; + lv_draw_fill_dsc_t fill_dsc; lv_draw_fill_dsc_init(&fill_dsc); fill_dsc.opa = dsc->opa; @@ -609,6 +614,14 @@ void lv_draw_unit_draw_letter(lv_draw_task_t * t, lv_draw_glyph_dsc_t * dsc, co } dsc->format = g.format; + + if(g.format == LV_FONT_GLYPH_FORMAT_VECTOR) { + + /*Load the outline of the glyph, even if the function says bitmap*/ + g.outline_stroke_width = dsc->outline_stroke_width; + dsc->glyph_data = (void *) lv_font_get_glyph_bitmap(&g, draw_buf); + dsc->format = dsc->glyph_data ? g.format : LV_FONT_GLYPH_FORMAT_NONE; + } } else { dsc->format = LV_FONT_GLYPH_FORMAT_NONE; diff --git a/src/draw/lv_draw_label.h b/src/draw/lv_draw_label.h index 5d7db01ff5..f2cc6daadb 100644 --- a/src/draw/lv_draw_label.h +++ b/src/draw/lv_draw_label.h @@ -100,6 +100,12 @@ typedef struct { /**Pointer to an externally stored struct where some data can be cached to speed up rendering*/ lv_draw_label_hint_t * hint; + + /* Properties of the letter outlines */ + lv_opa_t outline_stroke_opa; + lv_color_t outline_stroke_color; + int32_t outline_stroke_width; + } lv_draw_label_dsc_t; typedef struct { @@ -119,6 +125,12 @@ typedef struct { lv_opa_t opa; lv_text_decor_t decor : 3; lv_blend_mode_t blend_mode : 3; + + /* Properties of the letter outlines */ + lv_opa_t outline_stroke_opa; + int32_t outline_stroke_width; + lv_color_t outline_stroke_color; + } lv_draw_letter_dsc_t; /** diff --git a/src/draw/lv_draw_label_private.h b/src/draw/lv_draw_label_private.h index 0162e4ad89..f4ee4ea03b 100644 --- a/src/draw/lv_draw_label_private.h +++ b/src/draw/lv_draw_label_private.h @@ -50,6 +50,9 @@ struct _lv_draw_glyph_dsc_t { lv_font_glyph_dsc_t * g; lv_color_t color; lv_opa_t opa; + lv_color_t outline_stroke_color; + lv_opa_t outline_stroke_opa; + int32_t outline_stroke_width; int32_t rotation; lv_point_t pivot; /**< Rotation pivot point associated with total glyph including line_height */ lv_draw_buf_t * _draw_buf; /**< a shared draw buf for get_bitmap, do not use it directly, use glyph_data instead */ diff --git a/src/draw/sw/lv_draw_sw.c b/src/draw/sw/lv_draw_sw.c index 2747a63041..69fcea80b0 100644 --- a/src/draw/sw/lv_draw_sw.c +++ b/src/draw/sw/lv_draw_sw.c @@ -96,7 +96,12 @@ void lv_draw_sw_init(void) #endif #if LV_USE_VECTOR_GRAPHIC && LV_USE_THORVG - tvg_engine_init(TVG_ENGINE_SW, 0); + if(LV_DRAW_SW_DRAW_UNIT_CNT > 1) { + tvg_engine_init(TVG_ENGINE_SW, LV_DRAW_SW_DRAW_UNIT_CNT); + } + else { + tvg_engine_init(TVG_ENGINE_SW, 0); + } #endif lv_ll_init(&LV_GLOBAL_DEFAULT()->draw_sw_blend_handler_ll, sizeof(lv_draw_sw_custom_blend_handler_t)); diff --git a/src/draw/sw/lv_draw_sw_letter.c b/src/draw/sw/lv_draw_sw_letter.c index 740761baa0..911bf3501f 100644 --- a/src/draw/sw/lv_draw_sw_letter.c +++ b/src/draw/sw/lv_draw_sw_letter.c @@ -8,7 +8,15 @@ *********************/ #include "blend/lv_draw_sw_blend_private.h" #include "../lv_draw_label_private.h" +#include "../../draw/lv_draw_private.h" #include "lv_draw_sw.h" + +#if LV_USE_FREETYPE && LV_USE_VECTOR_GRAPHIC + + #include "../../libs/freetype/lv_freetype_private.h" + +#endif + #if LV_USE_DRAW_SW #include "../../display/lv_display.h" @@ -35,6 +43,13 @@ static void /* LV_ATTRIBUTE_FAST_MEM */ draw_letter_cb(lv_draw_task_t * t, lv_draw_glyph_dsc_t * glyph_draw_dsc, lv_draw_fill_dsc_t * fill_draw_dsc, const lv_area_t * fill_area); +#if LV_USE_FREETYPE && LV_USE_VECTOR_GRAPHIC + + static void freetype_outline_event_cb(lv_event_t * e); + static void draw_letter_outline(lv_draw_task_t * t, lv_draw_glyph_dsc_t * dsc); + +#endif + /********************** * STATIC VARIABLES **********************/ @@ -84,6 +99,15 @@ void lv_draw_sw_label(lv_draw_task_t * t, const lv_draw_label_dsc_t * dsc, const if(dsc->opa <= LV_OPA_MIN) return; LV_PROFILER_DRAW_BEGIN; + +#if LV_USE_FREETYPE && LV_USE_VECTOR_GRAPHIC + static bool is_init = false; + if(!is_init) { + lv_freetype_outline_add_event(freetype_outline_event_cb, LV_EVENT_ALL, t); + is_init = true; + } +#endif + lv_draw_label_iterate_characters(t, dsc, coords, draw_letter_cb); LV_PROFILER_DRAW_END; } @@ -154,6 +178,13 @@ static void LV_ATTRIBUTE_FAST_MEM draw_letter_cb(lv_draw_task_t * t, lv_draw_gly } break; } + break; +#if LV_USE_FREETYPE && LV_USE_VECTOR_GRAPHIC + case LV_FONT_GLYPH_FORMAT_VECTOR: { + draw_letter_outline(t, glyph_draw_dsc); + } + break; +#endif default: break; } @@ -164,4 +195,216 @@ static void LV_ATTRIBUTE_FAST_MEM draw_letter_cb(lv_draw_task_t * t, lv_draw_gly } } +#if LV_USE_FREETYPE && LV_USE_VECTOR_GRAPHIC + +typedef struct { + lv_vector_path_t * inside_path; /*The regular glyph*/ + lv_vector_path_t * outside_path; /*A bigger glyph that goes in the background for the letter outline*/ + lv_vector_path_t * cur_path; +} lv_draw_sw_letter_outlines_t; + +/* + * Renders the vectors paths representing a glyph with ThorVG + * the result is then blended into the draw buffer + */ +static void draw_letter_outline(lv_draw_task_t * t, lv_draw_glyph_dsc_t * glyph_dsc) +{ + + lv_draw_sw_letter_outlines_t * glyph_paths; + lv_vector_dsc_t * vector_dsc; + lv_draw_buf_t * draw_buf; + lv_matrix_t matrix; + lv_layer_t layer; + + glyph_paths = (lv_draw_sw_letter_outlines_t *) glyph_dsc->glyph_data; + LV_ASSERT_NULL(glyph_paths); + + int32_t cf; + int32_t w; + int32_t h; + uint32_t stride; + float scale; + lv_area_t buf_area; + + cf = LV_COLOR_FORMAT_ARGB8888; + + scale = LV_FREETYPE_F26DOT6_TO_FLOAT(lv_freetype_outline_get_scale(glyph_dsc->g->resolved_font)); + w = (int32_t)((float) glyph_dsc->g->box_w + glyph_dsc->outline_stroke_width * 2 * scale); + h = (int32_t)((float) glyph_dsc->g->box_h + glyph_dsc->outline_stroke_width * 2 * scale); + buf_area.x1 = 0; + buf_area.y1 = 0; + lv_area_set_width(&buf_area, w); + lv_area_set_height(&buf_area, h); + + stride = lv_draw_buf_width_to_stride(w, cf); + draw_buf = lv_draw_buf_create(w, h, cf, stride); + lv_draw_buf_clear(draw_buf, NULL); + + lv_memzero(&layer, sizeof(lv_layer_t)); + layer.draw_buf = draw_buf; + layer.color_format = cf; + layer.buf_area = buf_area; + layer.phy_clip_area = buf_area; + layer._clip_area = buf_area; + + lv_memzero(&matrix, sizeof(lv_matrix_t)); + + lv_matrix_identity(&matrix); + + vector_dsc = lv_vector_dsc_create(&layer); + + int32_t offset_x; + int32_t offset_y; + + offset_x = (int32_t)((float) glyph_dsc->g->ofs_x - glyph_dsc->outline_stroke_width * scale); + offset_y = (int32_t)((float) glyph_dsc->g->ofs_y - glyph_dsc->outline_stroke_width * scale); + + /*Invert Y-Axis - Freetype's origin point is in the bottom left corner*/ + lv_matrix_scale(&matrix, 1, -1); + lv_matrix_translate(&matrix, -offset_x, -h - offset_y); + lv_matrix_scale(&matrix, scale, scale); + lv_vector_dsc_set_transform(vector_dsc, &matrix); + + /*Set attributes color, line width etc*/ + if(cf == LV_COLOR_FORMAT_ARGB8888) { + + if(glyph_dsc->outline_stroke_width > 0) { + lv_vector_dsc_set_fill_color(vector_dsc, glyph_dsc->outline_stroke_color); + lv_vector_dsc_set_fill_opa(vector_dsc, glyph_dsc->outline_stroke_opa); + lv_vector_dsc_add_path(vector_dsc, glyph_paths->outside_path); + } + + lv_vector_dsc_set_fill_color(vector_dsc, glyph_dsc->color); + lv_vector_dsc_set_fill_opa(vector_dsc, glyph_dsc->opa); + lv_vector_dsc_add_path(vector_dsc, glyph_paths->inside_path); + + } + else { + LV_LOG_ERROR("Unsupported color format: %d", cf); + } + + lv_area_t old_area; + lv_area_t letter_coords; + + /*Render vector path(s) - set the clip area so that it matches + *the size of the temporary buffer used to render the glyph path(s)*/ + lv_memcpy(&old_area, &t->clip_area, sizeof(lv_area_t)); + lv_memcpy(&t->clip_area, &buf_area, sizeof(lv_area_t)); + + lv_draw_vector(vector_dsc); + LV_ASSERT_NULL(layer.draw_task_head); + + lv_draw_sw_vector(t, (lv_draw_vector_task_dsc_t *) layer.draw_task_head->draw_dsc); + + /*Restore previous draw area of the entire text label*/ + lv_memcpy(&t->clip_area, &old_area, sizeof(lv_area_t)); + + lv_memcpy(&letter_coords, glyph_dsc->letter_coords, sizeof(lv_area_t)); + lv_area_set_width(&letter_coords, w); + lv_area_set_height(&letter_coords, h); + + lv_draw_image_dsc_t img_dsc; + + lv_draw_image_dsc_init(&img_dsc); + img_dsc.rotation = 0; + img_dsc.scale_x = LV_SCALE_NONE; + img_dsc.scale_y = LV_SCALE_NONE; + img_dsc.opa = LV_OPA_100; + img_dsc.src = draw_buf; + lv_draw_sw_image(t, &img_dsc, &letter_coords); + + lv_vector_dsc_delete(vector_dsc); + lv_draw_buf_destroy(draw_buf); + +} + +/* Build the inside and outside vector paths for a glyph based + * on the recieved outline events emitted by lv_freetype_outline.c */ +static void freetype_outline_event_cb(lv_event_t * e) +{ + + lv_fpoint_t pnt; + lv_fpoint_t ctrl_pnt1; + lv_fpoint_t ctrl_pnt2; + lv_draw_sw_letter_outlines_t * glyph_paths; + lv_vector_path_t * path; + lv_freetype_outline_event_param_t * outline_event; + + outline_event = lv_event_get_param(e); + pnt.x = LV_FREETYPE_F26DOT6_TO_FLOAT(outline_event->to.x); + pnt.y = LV_FREETYPE_F26DOT6_TO_FLOAT(outline_event->to.y); + glyph_paths = outline_event->outline; + + if(lv_event_get_code(e) == LV_EVENT_CREATE) { + + glyph_paths = lv_malloc(sizeof(lv_draw_sw_letter_outlines_t)); + LV_ASSERT_NULL(glyph_paths); + + glyph_paths->cur_path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_HIGH); + glyph_paths->inside_path = glyph_paths->cur_path; + outline_event->outline = glyph_paths; + return; + + } + else if(lv_event_get_code(e) == LV_EVENT_DELETE) { + + if(glyph_paths->inside_path != NULL) { + lv_vector_path_clear(glyph_paths->inside_path); + lv_vector_path_delete(glyph_paths->inside_path); + } + + if(glyph_paths->outside_path != NULL) { + lv_vector_path_clear(glyph_paths->outside_path); + lv_vector_path_delete(glyph_paths->outside_path); + } + + lv_free(glyph_paths); + return; + + } + else if(outline_event->type == LV_FREETYPE_OUTLINE_BORDER_START) { + + /* Inside path is done - create the border path */ + lv_vector_path_close(glyph_paths->cur_path); + glyph_paths->cur_path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_HIGH); + glyph_paths->outside_path = glyph_paths->cur_path; + return; + } + + path = glyph_paths->cur_path; + + switch(outline_event->type) { + + case LV_FREETYPE_OUTLINE_MOVE_TO: + lv_vector_path_move_to(path, &pnt); + break; + + case LV_FREETYPE_OUTLINE_LINE_TO: + lv_vector_path_line_to(path, &pnt); + break; + + case LV_FREETYPE_OUTLINE_CUBIC_TO: + ctrl_pnt1.x = LV_FREETYPE_F26DOT6_TO_FLOAT(outline_event->control1.x); + ctrl_pnt1.y = LV_FREETYPE_F26DOT6_TO_FLOAT(outline_event->control1.y); + ctrl_pnt2.x = LV_FREETYPE_F26DOT6_TO_FLOAT(outline_event->control2.x); + ctrl_pnt2.y = LV_FREETYPE_F26DOT6_TO_FLOAT(outline_event->control2.y); + lv_vector_path_cubic_to(path, &ctrl_pnt1, &ctrl_pnt2, &pnt); + break; + + case LV_FREETYPE_OUTLINE_CONIC_TO: + ctrl_pnt1.x = LV_FREETYPE_F26DOT6_TO_FLOAT(outline_event->control1.x); + ctrl_pnt1.y = LV_FREETYPE_F26DOT6_TO_FLOAT(outline_event->control1.y); + lv_vector_path_quad_to(path, &ctrl_pnt1, &pnt); + break; + case LV_FREETYPE_OUTLINE_END: + case LV_FREETYPE_OUTLINE_BORDER_START: + /* It's not necessary to close the path and + * border start is handled above + */ + break; + } +} + +#endif /* LV_USE_FREETYPE && LV_USE_VECTOR_GRAPHIC */ + #endif /*LV_USE_DRAW_SW*/ diff --git a/src/draw/sw/lv_draw_sw_vector.c b/src/draw/sw/lv_draw_sw_vector.c index d5b974383d..4a0afb1088 100644 --- a/src/draw/sw/lv_draw_sw_vector.c +++ b/src/draw/sw/lv_draw_sw_vector.c @@ -492,7 +492,6 @@ void lv_draw_sw_vector(lv_draw_task_t * t, const lv_draw_vector_task_dsc_t * dsc buf = new_buf->data; stride = new_buf->header.stride; } - Tvg_Canvas * canvas = tvg_swcanvas_create(); tvg_swcanvas_set_target(canvas, buf, stride / 4, width, height, TVG_COLORSPACE_ARGB8888S); diff --git a/src/font/lv_font.h b/src/font/lv_font.h index fa9f3649f9..15d7a42e32 100644 --- a/src/font/lv_font.h +++ b/src/font/lv_font.h @@ -68,6 +68,7 @@ typedef struct { int16_t ofs_y; /**< y offset of the bounding box*/ lv_font_glyph_format_t format; /**< Font format of the glyph see lv_font_glyph_format_t */ uint8_t is_placeholder: 1; /**< Glyph is missing. But placeholder will still be displayed*/ + int32_t outline_stroke_width; /**< used with freetype vector fonts - width of the letter outline */ /** 0: Get bitmap should return an A8 or ARGB8888 image. * 1: return the bitmap as it is (Maybe A1/2/4 or any proprietary formats). */ diff --git a/src/libs/freetype/lv_freetype.h b/src/libs/freetype/lv_freetype.h index 6fe3a8f940..3bade7dbd6 100755 --- a/src/libs/freetype/lv_freetype.h +++ b/src/libs/freetype/lv_freetype.h @@ -18,6 +18,8 @@ extern "C" { #include "../../misc/lv_types.h" #include "../../misc/lv_event.h" +#include "../../misc/lv_color.h" + #include LV_STDBOOL_INCLUDE /********************* @@ -56,6 +58,7 @@ typedef enum { LV_FREETYPE_OUTLINE_LINE_TO, LV_FREETYPE_OUTLINE_CUBIC_TO, LV_FREETYPE_OUTLINE_CONIC_TO, + LV_FREETYPE_OUTLINE_BORDER_START, /* When line width > 0 the border glyph is drawn after the regular glyph */ } lv_freetype_outline_type_t; /********************** @@ -83,7 +86,6 @@ void lv_freetype_uninit(void); */ lv_font_t * lv_freetype_font_create(const char * pathname, lv_freetype_font_render_mode_t render_mode, uint32_t size, lv_freetype_font_style_t style); - /** * Delete a freetype font. * @param font freetype font to be deleted. diff --git a/src/libs/freetype/lv_freetype_glyph.c b/src/libs/freetype/lv_freetype_glyph.c index 5ecd048959..bd38fb5a8e 100644 --- a/src/libs/freetype/lv_freetype_glyph.c +++ b/src/libs/freetype/lv_freetype_glyph.c @@ -131,10 +131,9 @@ static bool freetype_get_glyph_dsc_cb(const lv_font_t * font, lv_font_glyph_dsc_ static bool freetype_glyph_create_cb(lv_freetype_glyph_cache_data_t * data, void * user_data) { LV_PROFILER_FONT_BEGIN; - lv_freetype_font_dsc_t * dsc = (lv_freetype_font_dsc_t *)user_data; FT_Error error; - + lv_freetype_font_dsc_t * dsc = (lv_freetype_font_dsc_t *)user_data; lv_font_glyph_dsc_t * dsc_out = &data->glyph_dsc; lv_mutex_lock(&dsc->cache_node->face_lock); @@ -169,6 +168,7 @@ static bool freetype_glyph_create_cb(lv_freetype_glyph_cache_data_t * data, void FT_GlyphSlot glyph = face->glyph; if(dsc->render_mode == LV_FREETYPE_FONT_RENDER_MODE_OUTLINE) { + dsc_out->adv_w = FT_F26DOT6_TO_INT(glyph->metrics.horiAdvance); dsc_out->box_h = FT_F26DOT6_TO_INT(glyph->metrics.height); /*Height of the bitmap in [px]*/ dsc_out->box_w = FT_F26DOT6_TO_INT(glyph->metrics.width); /*Width of the bitmap in [px]*/ @@ -177,7 +177,7 @@ static bool freetype_glyph_create_cb(lv_freetype_glyph_cache_data_t * data, void glyph->metrics.height); /*Y offset of the bitmap measured from the as line*/ dsc_out->format = LV_FONT_GLYPH_FORMAT_VECTOR; - /*Transform the glyph to italic if required*/ + /*Transform the glyph to italic if required */ if(dsc->style & LV_FREETYPE_FONT_STYLE_ITALIC) { dsc_out->box_w = lv_freetype_italic_transform_on_pos((lv_point_t) { dsc_out->box_w, dsc_out->box_h diff --git a/src/libs/freetype/lv_freetype_outline.c b/src/libs/freetype/lv_freetype_outline.c index 3f5c978c7c..f7d5edbd0d 100755 --- a/src/libs/freetype/lv_freetype_outline.c +++ b/src/libs/freetype/lv_freetype_outline.c @@ -33,7 +33,7 @@ typedef struct _lv_freetype_outline_node_t { **********************/ static lv_freetype_outline_t outline_create(lv_freetype_context_t * ctx, FT_Face face, FT_UInt glyph_index, - uint32_t size, uint32_t strength); + uint32_t size, uint32_t strength, uint32_t border_width); static lv_result_t outline_delete(lv_freetype_context_t * ctx, lv_freetype_outline_t outline); static const void * freetype_get_glyph_bitmap_cb(lv_font_glyph_dsc_t * g_dsc, lv_draw_buf_t * draw_buf); static void freetype_release_glyph_cb(const lv_font_t * font, lv_font_glyph_dsc_t * g_dsc); @@ -128,7 +128,8 @@ static bool freetype_glyph_outline_create_cb(lv_freetype_outline_node_t * node, dsc->cache_node->face, node->glyph_index, dsc->cache_node->ref_size, - dsc->style & LV_FREETYPE_FONT_STYLE_BOLD ? 1 : 0); + dsc->style & LV_FREETYPE_FONT_STYLE_BOLD ? 1 : 0, + dsc->outline_stroke_width); lv_mutex_unlock(&dsc->cache_node->face_lock); if(!outline) { @@ -168,7 +169,11 @@ static const void * freetype_get_glyph_bitmap_cb(lv_font_glyph_dsc_t * g_dsc, lv const lv_font_t * font = g_dsc->resolved_font; lv_freetype_font_dsc_t * dsc = (lv_freetype_font_dsc_t *)font->dsc; LV_ASSERT_FREETYPE_FONT_DSC(dsc); + + dsc->outline_stroke_width = g_dsc->outline_stroke_width; + lv_cache_entry_t * entry = lv_freetype_outline_lookup(dsc, (FT_UInt)g_dsc->gid.index); + if(entry == NULL) { return NULL; } @@ -300,11 +305,14 @@ static lv_freetype_outline_t outline_create( FT_Face face, FT_UInt glyph_index, uint32_t size, - uint32_t strength) + uint32_t strength, + uint32_t border_width) { LV_PROFILER_FONT_BEGIN; LV_ASSERT_NULL(ctx); FT_Error error; + FT_Glyph glyph; + FT_Stroker stroker; error = FT_Set_Pixel_Sizes(face, 0, size); if(error) { @@ -313,6 +321,7 @@ static lv_freetype_outline_t outline_create( return NULL; } + /** * Disable AUTOHINT(https://freetype.org/autohinting/hinter.html) to avoid display clipping * caused by inconsistent glyph measurement and outline. @@ -331,6 +340,7 @@ static lv_freetype_outline_t outline_create( } } + FT_Outline_Funcs outline_funcs = { .move_to = outline_move_to_cb, .line_to = outline_line_to_cb, @@ -340,42 +350,14 @@ static lv_freetype_outline_t outline_create( .delta = 0 }; - lv_result_t res; lv_freetype_outline_event_param_t param; lv_memzero(¶m, sizeof(param)); - /*Calculate Total Segments Before decompose */ - int32_t tag_size = face->glyph->outline.n_points; - int32_t segments = 0; - int32_t vectors = 0; - - for(int j = 0; j < tag_size; j++) { - if((face->glyph->outline.tags[j] & 0x1) == 0x1) { - segments++; - vectors++; - } - else { - int jj = j + 1 < tag_size ? j + 1 : 0; - if(face->glyph->outline.tags[jj] & 0x1) { - vectors++; - } - else { - segments++; - vectors += 2; - } - } - } - /*Also for every contour we may have a line for close*/ - segments += face->glyph->outline.n_contours; - vectors += face->glyph->outline.n_contours; - - param.sizes.data_size = vectors * 2; - param.sizes.segments_size = segments; + lv_freetype_outline_t outline; res = outline_send_event(ctx, LV_EVENT_CREATE, ¶m); - - lv_freetype_outline_t outline = param.outline; + outline = param.outline; if(res != LV_RESULT_OK || !outline) { LV_LOG_ERROR("Outline object create failed"); @@ -383,22 +365,104 @@ static lv_freetype_outline_t outline_create( return NULL; } - /* Run outline decompose again to fill outline data */ - error = FT_Outline_Decompose(&face->glyph->outline, &outline_funcs, outline); - if(error) { - FT_ERROR_MSG("FT_Outline_Decompose", error); - outline_delete(ctx, outline); - LV_PROFILER_FONT_END; - return NULL; - } + /* 1 iteration if there is no border */ + /* 2 iterations if there is a a border and the glyph itsef */ + for(int i = 0; i < (border_width > 0 ? 2 : 1); i++) { - /* close outline */ - res = outline_push_point(outline, LV_FREETYPE_OUTLINE_END, NULL, NULL, NULL); - if(res != LV_RESULT_OK) { - LV_LOG_ERROR("Outline object close failed"); - outline_delete(ctx, outline); - LV_PROFILER_FONT_END; - return NULL; + FT_Outline glyph_outline; + + if(i == 1) { + + /* decompose the border glyph */ + FT_Stroker_New(ctx->library, &stroker); + FT_Stroker_Set(stroker, border_width * 64, + FT_STROKER_LINECAP_ROUND, + FT_STROKER_LINEJOIN_ROUND, + 0); + + FT_Get_Glyph(face->glyph, &glyph); + FT_Glyph_StrokeBorder(&glyph, stroker, 0, true); + FT_OutlineGlyph g = (FT_OutlineGlyph) glyph; + + FT_Stroker_Done(stroker); + + glyph_outline = g->outline; + + } + else { + + /* decompose glyph */ + glyph_outline = face->glyph->outline; + } + + /*Calculate Total Segments Before decompose */ + int32_t tag_size = glyph_outline.n_points; + int32_t segments = 0; + int32_t vectors = 0; + + for(int j = 0; j < tag_size; j++) { + +#if 0 + if(j == 0 && (glyph_outline.tags[j] & 0x1) == 0) { + /* TODO handle the case where the first point is 'off curve' */ +https://stackoverflow.com/questions/3465809/how-to-interpret-a-freetype-glyph-outline-when-the-first-point-on-the-contour-is + } +#endif + if((glyph_outline.tags[j] & 0x1) == 0x1) { + segments++; + vectors++; + } + else { + int jj = j + 1 < tag_size ? j + 1 : 0; + if(glyph_outline.tags[jj] & 0x1) { + vectors++; + } + else { + segments++; + vectors += 2; + } + } + } + + /*Also for every contour we may have a line for close*/ + segments += glyph_outline.n_contours; + vectors += glyph_outline.n_contours; + + param.sizes.data_size = vectors * 2; + param.sizes.segments_size = segments; + + /* Run outline decompose again to fill outline data */ + error = FT_Outline_Decompose(&glyph_outline, &outline_funcs, outline); + if(error) { + FT_ERROR_MSG("FT_Outline_Decompose", error); + outline_delete(ctx, outline); + LV_PROFILER_FONT_END; + return NULL; + } + + if(i == 0 && border_width > 0) { + + /* Close the border glyph before decomposing the inside glyph */ + res = outline_push_point(outline, LV_FREETYPE_OUTLINE_BORDER_START, NULL, NULL, NULL); + if(res != LV_RESULT_OK) { + LV_LOG_ERROR("Outline object close failed"); + outline_delete(ctx, outline); + LV_PROFILER_FONT_END; + return NULL; + } + + } + else if(i == 0 || (i == 1 && border_width > 0)) { + + /* Close the border glyph or the regular glyph */ + res = outline_push_point(outline, LV_FREETYPE_OUTLINE_END, NULL, NULL, NULL); + if(res != LV_RESULT_OK) { + LV_LOG_ERROR("Outline object close failed"); + outline_delete(ctx, outline); + LV_PROFILER_FONT_END; + return NULL; + } + } } LV_PROFILER_FONT_END; diff --git a/src/libs/freetype/lv_freetype_private.h b/src/libs/freetype/lv_freetype_private.h index 54db336821..f851704564 100755 --- a/src/libs/freetype/lv_freetype_private.h +++ b/src/libs/freetype/lv_freetype_private.h @@ -28,6 +28,7 @@ extern "C" { #include FT_SIZES_H #include FT_IMAGE_H #include FT_OUTLINE_H +#include FT_STROKER_H /********************* * DEFINES @@ -119,6 +120,7 @@ typedef struct _lv_freetype_font_dsc_t { lv_freetype_cache_node_t * cache_node; lv_cache_entry_t * cache_node_entry; FTC_FaceID face_id; + uint32_t outline_stroke_width; } lv_freetype_font_dsc_t; /********************** diff --git a/src/libs/thorvg/config.h b/src/libs/thorvg/config.h index b98d3f35f0..fa32babde9 100644 --- a/src/libs/thorvg/config.h +++ b/src/libs/thorvg/config.h @@ -13,3 +13,4 @@ #define THORVG_VERSION_STRING "0.15.3" +#define THORVG_THREAD_SUPPORT diff --git a/src/misc/lv_style.h b/src/misc/lv_style.h index 7178878035..2da6cd1f8c 100644 --- a/src/misc/lv_style.h +++ b/src/misc/lv_style.h @@ -238,50 +238,53 @@ enum { LV_STYLE_TEXT_LINE_SPACE = 92, LV_STYLE_TEXT_DECOR = 93, LV_STYLE_TEXT_ALIGN = 94, + LV_STYLE_TEXT_OUTLINE_STROKE_WIDTH = 95, + LV_STYLE_TEXT_OUTLINE_STROKE_OPA = 96, + LV_STYLE_TEXT_OUTLINE_STROKE_COLOR = 97, - LV_STYLE_OPA = 95, - LV_STYLE_OPA_LAYERED = 96, - LV_STYLE_COLOR_FILTER_DSC = 97, - LV_STYLE_COLOR_FILTER_OPA = 98, - LV_STYLE_ANIM = 99, - LV_STYLE_ANIM_DURATION = 100, - LV_STYLE_TRANSITION = 102, - LV_STYLE_BLEND_MODE = 103, - LV_STYLE_TRANSFORM_WIDTH = 104, - LV_STYLE_TRANSFORM_HEIGHT = 105, - LV_STYLE_TRANSLATE_X = 106, - LV_STYLE_TRANSLATE_Y = 107, - LV_STYLE_TRANSFORM_SCALE_X = 108, - LV_STYLE_TRANSFORM_SCALE_Y = 109, - LV_STYLE_TRANSFORM_ROTATION = 110, - LV_STYLE_TRANSFORM_PIVOT_X = 111, - LV_STYLE_TRANSFORM_PIVOT_Y = 112, - LV_STYLE_TRANSFORM_SKEW_X = 113, - LV_STYLE_TRANSFORM_SKEW_Y = 114, - LV_STYLE_BITMAP_MASK_SRC = 115, - LV_STYLE_ROTARY_SENSITIVITY = 116, - LV_STYLE_TRANSLATE_RADIAL = 117, - LV_STYLE_RECOLOR = 118, - LV_STYLE_RECOLOR_OPA = 119, + LV_STYLE_OPA = 98, + LV_STYLE_OPA_LAYERED = 99, + LV_STYLE_COLOR_FILTER_DSC = 100, + LV_STYLE_COLOR_FILTER_OPA = 101, + LV_STYLE_ANIM = 102, + LV_STYLE_ANIM_DURATION = 103, + LV_STYLE_TRANSITION = 104, + LV_STYLE_BLEND_MODE = 105, + LV_STYLE_TRANSFORM_WIDTH = 106, + LV_STYLE_TRANSFORM_HEIGHT = 107, + LV_STYLE_TRANSLATE_X = 108, + LV_STYLE_TRANSLATE_Y = 109, + LV_STYLE_TRANSFORM_SCALE_X = 110, + LV_STYLE_TRANSFORM_SCALE_Y = 111, + LV_STYLE_TRANSFORM_ROTATION = 112, + LV_STYLE_TRANSFORM_PIVOT_X = 113, + LV_STYLE_TRANSFORM_PIVOT_Y = 114, + LV_STYLE_TRANSFORM_SKEW_X = 115, + LV_STYLE_TRANSFORM_SKEW_Y = 116, + LV_STYLE_BITMAP_MASK_SRC = 117, + LV_STYLE_ROTARY_SENSITIVITY = 118, + LV_STYLE_TRANSLATE_RADIAL = 119, + LV_STYLE_RECOLOR = 120, + LV_STYLE_RECOLOR_OPA = 121, - LV_STYLE_FLEX_FLOW = 125, - LV_STYLE_FLEX_MAIN_PLACE = 126, - LV_STYLE_FLEX_CROSS_PLACE = 127, - LV_STYLE_FLEX_TRACK_PLACE = 128, - LV_STYLE_FLEX_GROW = 129, + LV_STYLE_FLEX_FLOW = 122, + LV_STYLE_FLEX_MAIN_PLACE = 123, + LV_STYLE_FLEX_CROSS_PLACE = 124, + LV_STYLE_FLEX_TRACK_PLACE = 125, + LV_STYLE_FLEX_GROW = 126, - LV_STYLE_GRID_COLUMN_ALIGN = 130, - LV_STYLE_GRID_ROW_ALIGN = 131, - LV_STYLE_GRID_ROW_DSC_ARRAY = 132, - LV_STYLE_GRID_COLUMN_DSC_ARRAY = 133, - LV_STYLE_GRID_CELL_COLUMN_POS = 134, - LV_STYLE_GRID_CELL_COLUMN_SPAN = 135, - LV_STYLE_GRID_CELL_X_ALIGN = 136, - LV_STYLE_GRID_CELL_ROW_POS = 137, - LV_STYLE_GRID_CELL_ROW_SPAN = 138, - LV_STYLE_GRID_CELL_Y_ALIGN = 139, + LV_STYLE_GRID_COLUMN_ALIGN = 127, + LV_STYLE_GRID_ROW_ALIGN = 128, + LV_STYLE_GRID_ROW_DSC_ARRAY = 129, + LV_STYLE_GRID_COLUMN_DSC_ARRAY = 130, + LV_STYLE_GRID_CELL_COLUMN_POS = 131, + LV_STYLE_GRID_CELL_COLUMN_SPAN = 132, + LV_STYLE_GRID_CELL_X_ALIGN = 133, + LV_STYLE_GRID_CELL_ROW_POS = 134, + LV_STYLE_GRID_CELL_ROW_SPAN = 135, + LV_STYLE_GRID_CELL_Y_ALIGN = 136, - LV_STYLE_LAST_BUILT_IN_PROP = 140, + LV_STYLE_LAST_BUILT_IN_PROP = 137, LV_STYLE_NUM_BUILT_IN_PROPS = LV_STYLE_LAST_BUILT_IN_PROP + 1, diff --git a/src/misc/lv_style_gen.c b/src/misc/lv_style_gen.c index d2d5721406..cd7fe4f477 100644 --- a/src/misc/lv_style_gen.c +++ b/src/misc/lv_style_gen.c @@ -674,6 +674,30 @@ void lv_style_set_text_align(lv_style_t * style, lv_text_align_t value) lv_style_set_prop(style, LV_STYLE_TEXT_ALIGN, v); } +void lv_style_set_text_outline_stroke_color(lv_style_t * style, lv_color_t value) +{ + lv_style_value_t v = { + .color = value + }; + lv_style_set_prop(style, LV_STYLE_TEXT_OUTLINE_STROKE_COLOR, v); +} + +void lv_style_set_text_outline_stroke_width(lv_style_t * style, int32_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_TEXT_OUTLINE_STROKE_WIDTH, v); +} + +void lv_style_set_text_outline_stroke_opa(lv_style_t * style, lv_opa_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_TEXT_OUTLINE_STROKE_OPA, v); +} + void lv_style_set_radius(lv_style_t * style, int32_t value) { lv_style_value_t v = { diff --git a/src/misc/lv_style_gen.h b/src/misc/lv_style_gen.h index c6d2e80850..b5c030dcb9 100644 --- a/src/misc/lv_style_gen.h +++ b/src/misc/lv_style_gen.h @@ -97,6 +97,9 @@ void lv_style_set_text_letter_space(lv_style_t * style, int32_t value); void lv_style_set_text_line_space(lv_style_t * style, int32_t value); void lv_style_set_text_decor(lv_style_t * style, lv_text_decor_t value); void lv_style_set_text_align(lv_style_t * style, lv_text_align_t value); +void lv_style_set_text_outline_stroke_color(lv_style_t * style, lv_color_t value); +void lv_style_set_text_outline_stroke_width(lv_style_t * style, int32_t value); +void lv_style_set_text_outline_stroke_opa(lv_style_t * style, lv_opa_t value); void lv_style_set_radius(lv_style_t * style, int32_t value); void lv_style_set_radial_offset(lv_style_t * style, int32_t value); void lv_style_set_clip_corner(lv_style_t * style, bool value); @@ -551,6 +554,21 @@ void lv_style_set_grid_cell_row_span(lv_style_t * style, int32_t value); .prop = LV_STYLE_TEXT_ALIGN, .value = { .num = (int32_t)val } \ } +#define LV_STYLE_CONST_TEXT_OUTLINE_STROKE_COLOR(val) \ + { \ + .prop = LV_STYLE_TEXT_OUTLINE_STROKE_COLOR, .value = { .color = val } \ + } + +#define LV_STYLE_CONST_TEXT_OUTLINE_STROKE_WIDTH(val) \ + { \ + .prop = LV_STYLE_TEXT_OUTLINE_STROKE_WIDTH, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_TEXT_OUTLINE_STROKE_OPA(val) \ + { \ + .prop = LV_STYLE_TEXT_OUTLINE_STROKE_OPA, .value = { .num = (int32_t)val } \ + } + #define LV_STYLE_CONST_RADIUS(val) \ { \ .prop = LV_STYLE_RADIUS, .value = { .num = (int32_t)val } \ diff --git a/src/widgets/label/lv_label.c b/src/widgets/label/lv_label.c index 46b7e5d001..1747cd2ab0 100644 --- a/src/widgets/label/lv_label.c +++ b/src/widgets/label/lv_label.c @@ -430,6 +430,7 @@ uint32_t lv_label_get_letter_on(const lv_obj_t * obj, lv_point_t * pos_in, bool const int32_t letter_height = lv_font_get_line_height(font); int32_t y = 0; + lv_text_flag_t flag = get_label_flags(label); /*Search the line of the index letter*/; @@ -775,6 +776,7 @@ static void lv_label_event(const lv_obj_class_t * class_p, lv_event_t * e) if(label->invalid_size_cache) { const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN); int32_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_MAIN); + int32_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN); lv_text_flag_t flag = LV_TEXT_FLAG_NONE; if(label->recolor != 0) flag |= LV_TEXT_FLAG_RECOLOR; @@ -840,6 +842,12 @@ static void draw_main(lv_event_t * e) label_draw_dsc.sel_bg_color = lv_obj_get_style_bg_color(obj, LV_PART_SELECTED); } + /* get the style attributes of a letter outline */ + label_draw_dsc.outline_stroke_color = lv_obj_get_style_text_outline_stroke_color(obj, LV_PART_MAIN); + label_draw_dsc.outline_stroke_opa = lv_obj_get_style_text_outline_stroke_opa(obj, LV_PART_MAIN); + label_draw_dsc.outline_stroke_width = lv_obj_get_style_text_outline_stroke_width(obj, LV_PART_MAIN); + + /* In SCROLL and SCROLL_CIRCULAR mode the CENTER and RIGHT are pointless, so remove them. * (In addition, they will create misalignment in this situation)*/ if((label->long_mode == LV_LABEL_LONG_MODE_SCROLL || label->long_mode == LV_LABEL_LONG_MODE_SCROLL_CIRCULAR) && diff --git a/src/widgets/property/lv_obj_property_names.h b/src/widgets/property/lv_obj_property_names.h index f63d12d014..d9a0543212 100644 --- a/src/widgets/property/lv_obj_property_names.h +++ b/src/widgets/property/lv_obj_property_names.h @@ -18,7 +18,7 @@ extern const lv_property_name_t lv_obj_property_names[73]; extern const lv_property_name_t lv_roller_property_names[3]; extern const lv_property_name_t lv_slider_property_names[8]; - extern const lv_property_name_t lv_style_property_names[117]; + extern const lv_property_name_t lv_style_property_names[120]; extern const lv_property_name_t lv_textarea_property_names[15]; #endif #endif diff --git a/src/widgets/property/lv_style_properties.c b/src/widgets/property/lv_style_properties.c index 99a39be12a..61ad99ba14 100644 --- a/src/widgets/property/lv_style_properties.c +++ b/src/widgets/property/lv_style_properties.c @@ -14,7 +14,7 @@ * Generated code from properties.py */ /* *INDENT-OFF* */ -const lv_property_name_t lv_style_property_names[117] = { +const lv_property_name_t lv_style_property_names[120] = { {"align", LV_PROPERTY_STYLE_ALIGN,}, {"anim", LV_PROPERTY_STYLE_ANIM,}, {"anim_duration", LV_PROPERTY_STYLE_ANIM_DURATION,}, @@ -116,6 +116,9 @@ const lv_property_name_t lv_style_property_names[117] = { {"text_letter_space", LV_PROPERTY_STYLE_TEXT_LETTER_SPACE,}, {"text_line_space", LV_PROPERTY_STYLE_TEXT_LINE_SPACE,}, {"text_opa", LV_PROPERTY_STYLE_TEXT_OPA,}, + {"text_outline_stroke_color", LV_PROPERTY_STYLE_TEXT_OUTLINE_STROKE_COLOR,}, + {"text_outline_stroke_opa", LV_PROPERTY_STYLE_TEXT_OUTLINE_STROKE_OPA,}, + {"text_outline_stroke_width", LV_PROPERTY_STYLE_TEXT_OUTLINE_STROKE_WIDTH,}, {"transform_height", LV_PROPERTY_STYLE_TRANSFORM_HEIGHT,}, {"transform_pivot_x", LV_PROPERTY_STYLE_TRANSFORM_PIVOT_X,}, {"transform_pivot_y", LV_PROPERTY_STYLE_TRANSFORM_PIVOT_Y,}, diff --git a/src/widgets/property/lv_style_properties.h b/src/widgets/property/lv_style_properties.h index 0115e855ea..b53c751b7d 100644 --- a/src/widgets/property/lv_style_properties.h +++ b/src/widgets/property/lv_style_properties.h @@ -113,6 +113,9 @@ enum { LV_PROPERTY_ID(STYLE, TEXT_LETTER_SPACE, LV_PROPERTY_TYPE_INT, LV_STYLE_TEXT_LETTER_SPACE), LV_PROPERTY_ID(STYLE, TEXT_LINE_SPACE, LV_PROPERTY_TYPE_INT, LV_STYLE_TEXT_LINE_SPACE), LV_PROPERTY_ID(STYLE, TEXT_OPA, LV_PROPERTY_TYPE_INT, LV_STYLE_TEXT_OPA), + LV_PROPERTY_ID(STYLE, TEXT_OUTLINE_STROKE_COLOR, LV_PROPERTY_TYPE_INT, LV_STYLE_TEXT_OUTLINE_STROKE_COLOR), + LV_PROPERTY_ID(STYLE, TEXT_OUTLINE_STROKE_OPA, LV_PROPERTY_TYPE_INT, LV_STYLE_TEXT_OUTLINE_STROKE_OPA), + LV_PROPERTY_ID(STYLE, TEXT_OUTLINE_STROKE_WIDTH, LV_PROPERTY_TYPE_INT, LV_STYLE_TEXT_OUTLINE_STROKE_WIDTH), LV_PROPERTY_ID(STYLE, TRANSFORM_HEIGHT, LV_PROPERTY_TYPE_INT, LV_STYLE_TRANSFORM_HEIGHT), LV_PROPERTY_ID(STYLE, TRANSFORM_PIVOT_X, LV_PROPERTY_TYPE_INT, LV_STYLE_TRANSFORM_PIVOT_X), LV_PROPERTY_ID(STYLE, TRANSFORM_PIVOT_Y, LV_PROPERTY_TYPE_INT, LV_STYLE_TRANSFORM_PIVOT_Y),