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 270562dad2..5ee76333e0 100644 --- a/docs/src/details/common-widget-features/styles/style-properties.rst +++ b/docs/src/details/common-widget-features/styles/style-properties.rst @@ -1408,6 +1408,48 @@ Sets the intensity of color mixing. Value 0, `LV_OPA_0` or `LV_OPA_TRANSP` means
  • Ext. draw No
  • +blur_radius +~~~~~~~~~~~ + +Sets the intensity of blurring. Applied on each lv_part separately before the children are rendered. + +.. raw:: html + + + +blur_backdrop +~~~~~~~~~~~~~ + +If `true` the background of the widget will be blurred. The part should have < 100% opacity to make it visible. If `false` the given part will be blurred when it's rendered but before drawing the children. + +.. raw:: html + + + +blur_quality +~~~~~~~~~~~~ + +Setting to `LV_BLUR_QUALITY_SPEED` the blurring algorithm will prefer speed over quality. `LV_BLUR_QUALITY_PRECISION` will force using higher quality but slower blur. With `LV_BLUR_QUALITY_AUTO` the quality will be selected automatically. + +.. raw:: html + + + anim ~~~~ diff --git a/docs/src/details/main-modules/draw/draw_descriptors.rst b/docs/src/details/main-modules/draw/draw_descriptors.rst index 6021e33a8a..886fa4c179 100644 --- a/docs/src/details/main-modules/draw/draw_descriptors.rst +++ b/docs/src/details/main-modules/draw/draw_descriptors.rst @@ -617,6 +617,25 @@ Functions for triangle drawing: +Blur Draw Descriptor +******************** + +Blur is defined by :cpp:type:`lv_draw_blur_dsc_t`, which includes: + +:blur_radius: The radius of the blur. +:corner_radius: The radius of the corners. +:quality: Tells whether to prefer speed or quality. + +Functions for blur drawing: + +- :cpp:expr:`lv_draw_blur_dsc_init(&dsc)` initializes a blur descriptor. +- :cpp:expr:`lv_draw_blur(layer, &dsc, area)` creates a task to blur an area. +- :cpp:expr:`lv_draw_task_get_blur_dsc(draw_task)` retrieves blur descriptor. + +.. lv_example:: widgets/canvas/lv_example_canvas_10 + :language: c + + Vector Draw Descriptor ********************** diff --git a/examples/widgets/canvas/index.rst b/examples/widgets/canvas/index.rst index d4d210f4be..2ed84bc79a 100644 --- a/examples/widgets/canvas/index.rst +++ b/examples/widgets/canvas/index.rst @@ -57,14 +57,22 @@ Draw a triangle to the canvas .. lv_example:: widgets/canvas/lv_example_canvas_9 :language: c -Draw Fancy Letter Effects -------------------------- +Blur an area on the canvas +----------------------------- .. lv_example:: widgets/canvas/lv_example_canvas_10 :language: c -Draw Fancy Letter Effects 2 ---------------------------- +Draw Fancy Letter Effects +------------------------- + .. lv_example:: widgets/canvas/lv_example_canvas_11 :language: c + + +Draw Fancy Letter Effects 2 +--------------------------- +.. lv_example:: widgets/canvas/lv_example_canvas_12 + :language: c + diff --git a/examples/widgets/canvas/lv_example_canvas_10.c b/examples/widgets/canvas/lv_example_canvas_10.c index 29b4729aac..4775be0110 100644 --- a/examples/widgets/canvas/lv_example_canvas_10.c +++ b/examples/widgets/canvas/lv_example_canvas_10.c @@ -1,67 +1,69 @@ #include "../../lv_examples.h" -#if LV_USE_CANVAS && LV_BUILD_EXAMPLES +#if LV_USE_CANVAS && LV_FONT_MONTSERRAT_18 && LV_BUILD_EXAMPLES -#define CANVAS_WIDTH 300 -#define CANVAS_HEIGHT 200 - -static void timer_cb(lv_timer_t * timer) -{ - static int16_t counter = 0; - const char * string = "lol~ I'm wavvvvvvving~>>>"; - const int16_t string_len = lv_strlen(string); - - lv_obj_t * canvas = (lv_obj_t *)lv_timer_get_user_data(timer); - lv_layer_t layer; - lv_canvas_init_layer(canvas, &layer); - - lv_canvas_fill_bg(canvas, lv_color_white(), LV_OPA_COVER); - - lv_draw_letter_dsc_t letter_dsc; - lv_draw_letter_dsc_init(&letter_dsc); - letter_dsc.color = lv_color_hex(0xff0000); - letter_dsc.font = lv_font_get_default(); - - { -#define CURVE2_X(t) (t * 2 + 10) -#define CURVE2_Y(t) (lv_trigo_sin((t) * 5) * 40 / 32767 + CANVAS_HEIGHT / 2) - - int32_t pre_x = CURVE2_X(-1); - int32_t pre_y = CURVE2_Y(-1); - for(int16_t i = 0; i < string_len; i++) { - const int16_t angle = (int16_t)(i * 5); - const int32_t x = CURVE2_X(angle); - const int32_t y = CURVE2_Y(angle + counter / 2); - const lv_point_t point = { .x = x, .y = y }; - - letter_dsc.unicode = (uint32_t)string[i % string_len]; - letter_dsc.rotation = lv_atan2(y - pre_y, x - pre_x) * 10; - letter_dsc.color = lv_color_hsv_to_rgb(i * 10, 100, 100); - lv_draw_letter(&layer, &letter_dsc, &point); - - pre_x = x; - pre_y = y; - } - } - - lv_canvas_finish_layer(canvas, &layer); - - counter++; -} +#define CANVAS_WIDTH 100 +#define CANVAS_HEIGHT 100 +/** + *Blur an area on the canvas + */ void lv_example_canvas_10(void) { /*Create a buffer for the canvas*/ - LV_DRAW_BUF_DEFINE_STATIC(draw_buf, CANVAS_WIDTH, CANVAS_HEIGHT, LV_COLOR_FORMAT_ARGB8888); + LV_DRAW_BUF_DEFINE_STATIC(draw_buf, CANVAS_WIDTH, CANVAS_HEIGHT, LV_COLOR_FORMAT_RGB565); LV_DRAW_BUF_INIT_STATIC(draw_buf); + /*Create a canvas and initialize its palette*/ lv_obj_t * canvas = lv_canvas_create(lv_screen_active()); - lv_obj_set_size(canvas, CANVAS_WIDTH, CANVAS_HEIGHT); - + lv_canvas_set_draw_buf(canvas, &draw_buf); + lv_canvas_fill_bg(canvas, lv_color_hex3(0xccc), LV_OPA_COVER); lv_obj_center(canvas); - lv_canvas_set_draw_buf(canvas, &draw_buf); + lv_layer_t layer; + lv_canvas_init_layer(canvas, &layer); - lv_timer_create(timer_cb, 16, canvas); + /*A label in the background*/ + lv_draw_label_dsc_t label_dsc; + lv_draw_label_dsc_init(&label_dsc); + label_dsc.color = lv_palette_main(LV_PALETTE_RED); + label_dsc.font = &lv_font_montserrat_14; + label_dsc.decor = LV_TEXT_DECOR_UNDERLINE; + label_dsc.align = LV_TEXT_ALIGN_CENTER; + label_dsc.text = "Some parts of\nthis canvas is blurred"; + + lv_area_t label1_coords = {10, 10, 90, 90}; + + lv_draw_label(&layer, &label_dsc, &label1_coords); + + /*Blur the middle of the canvas*/ + lv_draw_blur_dsc_t blur_dsc; + lv_draw_blur_dsc_init(&blur_dsc); + blur_dsc.corner_radius = 10; + blur_dsc.blur_radius = 8; + + lv_area_t fill_coords = {20, 30, 80, 70}; + lv_draw_blur(&layer, &blur_dsc, &fill_coords); + + /*Draw a semi-transparent rectangle on the blurred area*/ + lv_draw_fill_dsc_t fill_dsc; + lv_draw_fill_dsc_init(&fill_dsc); + fill_dsc.color = lv_palette_lighten(LV_PALETTE_BLUE, 1); + fill_dsc.radius = 10; + fill_dsc.opa = LV_OPA_30; + + lv_draw_fill(&layer, &fill_dsc, &fill_coords); + + /*Add label on the blurred area*/ + lv_draw_label_dsc_init(&label_dsc); + label_dsc.color = lv_color_black(); + label_dsc.font = &lv_font_montserrat_14; + label_dsc.align = LV_TEXT_ALIGN_CENTER; + label_dsc.text = "Hello world"; + + lv_area_t label2_coords = {20, 35, 80, 60}; + lv_draw_label(&layer, &label_dsc, &label2_coords); + + lv_canvas_finish_layer(canvas, &layer); } #endif diff --git a/examples/widgets/canvas/lv_example_canvas_11.c b/examples/widgets/canvas/lv_example_canvas_11.c index 6677af71e1..528d5a7c21 100644 --- a/examples/widgets/canvas/lv_example_canvas_11.c +++ b/examples/widgets/canvas/lv_example_canvas_11.c @@ -7,10 +7,10 @@ static void timer_cb(lv_timer_t * timer) { static int32_t counter = 0; - const char * string = "windstorrrrrrrrrrrrrrrrm~>>>"; + const char * string = "lol~ I'm wavvvvvvving~>>>"; const int16_t string_len = lv_strlen(string); - lv_obj_t * canvas = (lv_obj_t *) lv_timer_get_user_data(timer); + lv_obj_t * canvas = (lv_obj_t *)lv_timer_get_user_data(timer); lv_layer_t layer; lv_canvas_init_layer(canvas, &layer); @@ -22,23 +22,21 @@ static void timer_cb(lv_timer_t * timer) letter_dsc.font = lv_font_get_default(); { -#define CURVE2_X(t) ((t) * 2 + lv_trigo_cos((t) * 5) * 40 / 32767 - 10) -#define CURVE2_Y(t, T) ((t) * lv_trigo_sin(((t) + (T)) * 5) * 40 / 32767 / 80 + CANVAS_HEIGHT / 2) +#define CURVE2_X(t) (t * 2 + 10) +#define CURVE2_Y(t) (lv_trigo_sin((t) * 5) * 40 / 32767 + CANVAS_HEIGHT / 2) int32_t pre_x = CURVE2_X(-1); - int32_t pre_y = CURVE2_Y(-1, 0); + int32_t pre_y = CURVE2_Y(-1); for(int16_t i = 0; i < string_len; i++) { - const int32_t angle = i * 5; + const int16_t angle = (int16_t)(i * 5); const int32_t x = CURVE2_X(angle); - const int32_t y = CURVE2_Y(angle + 30, counter / 2); + const int32_t y = CURVE2_Y(angle + counter / 2); + const lv_point_t point = { .x = x, .y = y }; letter_dsc.unicode = (uint32_t)string[i % string_len]; letter_dsc.rotation = lv_atan2(y - pre_y, x - pre_x) * 10; letter_dsc.color = lv_color_hsv_to_rgb(i * 10, 100, 100); - lv_point_t p = (lv_point_t) { - .x = x, .y = y - }; - lv_draw_letter(&layer, &letter_dsc, &p); + lv_draw_letter(&layer, &letter_dsc, &point); pre_x = x; pre_y = y; diff --git a/examples/widgets/canvas/lv_example_canvas_12.c b/examples/widgets/canvas/lv_example_canvas_12.c new file mode 100644 index 0000000000..d732710469 --- /dev/null +++ b/examples/widgets/canvas/lv_example_canvas_12.c @@ -0,0 +1,69 @@ +#include "../../lv_examples.h" +#if LV_USE_CANVAS && LV_BUILD_EXAMPLES + +#define CANVAS_WIDTH 300 +#define CANVAS_HEIGHT 200 + +static void timer_cb(lv_timer_t * timer) +{ + static int32_t counter = 0; + const char * string = "windstorrrrrrrrrrrrrrrrm~>>>"; + const int16_t string_len = lv_strlen(string); + + lv_obj_t * canvas = (lv_obj_t *) lv_timer_get_user_data(timer); + lv_layer_t layer; + lv_canvas_init_layer(canvas, &layer); + + lv_canvas_fill_bg(canvas, lv_color_white(), LV_OPA_COVER); + + lv_draw_letter_dsc_t letter_dsc; + lv_draw_letter_dsc_init(&letter_dsc); + letter_dsc.color = lv_color_hex(0xff0000); + letter_dsc.font = lv_font_get_default(); + + { +#define CURVE2_X(t) ((t) * 2 + lv_trigo_cos((t) * 5) * 40 / 32767 - 10) +#define CURVE2_Y(t, T) ((t) * lv_trigo_sin(((t) + (T)) * 5) * 40 / 32767 / 80 + CANVAS_HEIGHT / 2) + + int32_t pre_x = CURVE2_X(-1); + int32_t pre_y = CURVE2_Y(-1, 0); + for(int16_t i = 0; i < string_len; i++) { + const int32_t angle = i * 5; + const int32_t x = CURVE2_X(angle); + const int32_t y = CURVE2_Y(angle + 30, counter / 2); + + letter_dsc.unicode = (uint32_t)string[i % string_len]; + letter_dsc.rotation = lv_atan2(y - pre_y, x - pre_x) * 10; + letter_dsc.color = lv_color_hsv_to_rgb(i * 10, 100, 100); + lv_point_t p = (lv_point_t) { + .x = x, .y = y + }; + lv_draw_letter(&layer, &letter_dsc, &p); + + pre_x = x; + pre_y = y; + } + } + + lv_canvas_finish_layer(canvas, &layer); + + counter++; +} + +void lv_example_canvas_12(void) +{ + /*Create a buffer for the canvas*/ + LV_DRAW_BUF_DEFINE_STATIC(draw_buf, CANVAS_WIDTH, CANVAS_HEIGHT, LV_COLOR_FORMAT_ARGB8888); + LV_DRAW_BUF_INIT_STATIC(draw_buf); + + lv_obj_t * canvas = lv_canvas_create(lv_screen_active()); + lv_obj_set_size(canvas, CANVAS_WIDTH, CANVAS_HEIGHT); + + lv_obj_center(canvas); + + lv_canvas_set_draw_buf(canvas, &draw_buf); + + lv_timer_create(timer_cb, 16, canvas); +} + +#endif diff --git a/examples/widgets/lv_example_widgets.h b/examples/widgets/lv_example_widgets.h index d8f80a349a..c38132d722 100644 --- a/examples/widgets/lv_example_widgets.h +++ b/examples/widgets/lv_example_widgets.h @@ -63,6 +63,7 @@ void lv_example_canvas_8(void); void lv_example_canvas_9(void); void lv_example_canvas_10(void); void lv_example_canvas_11(void); +void lv_example_canvas_12(void); void lv_example_chart_1(void); void lv_example_chart_2(void); @@ -118,6 +119,7 @@ void lv_example_menu_5(void); void lv_example_msgbox_1(void); void lv_example_msgbox_2(void); +void lv_example_msgbox_3(void); void lv_example_obj_1(void); void lv_example_obj_2(void); diff --git a/examples/widgets/msgbox/index.rst b/examples/widgets/msgbox/index.rst index ece6382695..f0fc6b5f0d 100644 --- a/examples/widgets/msgbox/index.rst +++ b/examples/widgets/msgbox/index.rst @@ -11,4 +11,10 @@ Scrolling and styled Message box .. lv_example:: widgets/msgbox/lv_example_msgbox_2 :language: c +Message box with blurred background +----------------------------------- + +.. lv_example:: widgets/msgbox/lv_example_msgbox_3 + :language: c + diff --git a/examples/widgets/msgbox/lv_example_msgbox_3.c b/examples/widgets/msgbox/lv_example_msgbox_3.c new file mode 100644 index 0000000000..9e5b7407ca --- /dev/null +++ b/examples/widgets/msgbox/lv_example_msgbox_3.c @@ -0,0 +1,75 @@ +#include "../../lv_examples.h" +#if LV_USE_MSGBOX && LV_BUILD_EXAMPLES + +static void dropdown_value_changed_event_cb(lv_event_t * e) +{ + lv_obj_t * dropdown = lv_event_get_target_obj(e); + lv_obj_t * msgbox = (lv_obj_t *)lv_event_get_user_data(e); + lv_obj_t * top_layer = lv_layer_top(); + uint32_t opt = lv_dropdown_get_selected(dropdown); + + /*Blur screen*/ + if(opt == 0) { + lv_obj_set_style_blur_radius(msgbox, 0, 0); + lv_obj_set_style_blur_radius(top_layer, 24, 0); + } + /*Blur Message box*/ + else { + lv_obj_set_style_blur_radius(msgbox, 24, 0); + lv_obj_set_style_blur_radius(top_layer, 0, 0); + } +} + +void lv_example_msgbox_3(void) +{ + + lv_obj_t * label = lv_label_create(lv_screen_active()); + lv_label_set_text(label, "Lorem ipsum dolor sit amet, consectetur adipiscing elit." + " Ut orci mauris, placerat et euismod eu, ullamcorper eget massa. " + "Suspendisse sodales vitae augue ut vestibulum. " + "Nunc fringilla leo ut tellus consectetur tincidunt. " + "Quisque eu tortor semper odio aliquet congue egestas ac massa. " + "Phasellus elit lectus, finibus tempor augue in, elementum lobortis nisl. " + "Donec tristique lorem et tincidunt faucibus.\n\n" + "Lorem ipsum dolor sit amet, consectetur adipiscing elit." + " Ut orci mauris, placerat et euismod eu, ullamcorper eget massa. " + "Suspendisse sodales vitae augue ut vestibulum. " + "Nunc fringilla leo ut tellus consectetur tincidunt. " + "Quisque eu tortor semper odio aliquet congue egestas ac massa. " + "Phasellus elit lectus, finibus tempor augue in, elementum lobortis nisl. " + "Donec tristique lorem et tincidunt faucibus."); + + lv_obj_set_width(label, lv_pct(60)); + lv_obj_center(label); + lv_obj_set_style_text_color(label, lv_palette_main(LV_PALETTE_BLUE), 0); + + lv_obj_t * msgbox1 = lv_msgbox_create(lv_layer_top()); + lv_msgbox_add_title(msgbox1, "Setting"); + lv_msgbox_add_text(msgbox1, "Hello!\n\n" + "Scroll the text in the background to see how it behaves."); + + /*Just a little styling on the message box*/ + lv_obj_set_style_bg_opa(msgbox1, LV_OPA_40, 0); + lv_obj_set_style_bg_opa(lv_msgbox_get_header(msgbox1), LV_OPA_50, 0); + lv_obj_set_style_bg_color(lv_msgbox_get_header(msgbox1), lv_color_black(), 0); + lv_obj_set_style_bg_color(msgbox1, lv_color_black(), 0); + lv_obj_set_style_text_color(msgbox1, lv_color_white(), 0); + lv_obj_set_style_text_color(lv_msgbox_get_header(msgbox1), lv_color_white(), 0); + lv_obj_set_style_blur_backdrop(msgbox1, true, 0); + + /*A dropdown to select what to blur*/ + lv_obj_t * dropdown = lv_dropdown_create(lv_layer_top()); + lv_dropdown_set_options(dropdown, "Blur screen\nBlur msgbox"); + lv_obj_set_pos(dropdown, 5, 5); + lv_obj_add_event_cb(dropdown, dropdown_value_changed_event_cb, LV_EVENT_VALUE_CHANGED, msgbox1); + /*Also make the list blurred*/ + lv_obj_set_style_blur_radius(lv_dropdown_get_list(dropdown), 24, 0); + lv_obj_set_style_blur_backdrop(lv_dropdown_get_list(dropdown), true, 0); + lv_obj_set_style_bg_opa(lv_dropdown_get_list(dropdown), LV_OPA_50, 0); + + /*Send a value changed event to set the initial state*/ + lv_obj_send_event(dropdown, LV_EVENT_VALUE_CHANGED, NULL); + + +} +#endif diff --git a/scripts/style_api_gen.py b/scripts/style_api_gen.py index 7a4006d206..b94690d94f 100755 --- a/scripts/style_api_gen.py +++ b/scripts/style_api_gen.py @@ -401,6 +401,18 @@ props = [ 'style_type': 'num', 'var_type': 'lv_opa_t', 'default':'`LV_OPA_TRANSP`', 'inherited': 0, 'layout': 0, 'ext_draw': 0, 'dsc': "Sets the intensity of color mixing. Value 0, `LV_OPA_0` or `LV_OPA_TRANSP` means fully transparent. A value of 255, `LV_OPA_100` or `LV_OPA_COVER` means fully opaque. Intermediate values like LV_OPA_10, LV_OPA_20, etc result in semi-transparency."}, +{'name': 'BLUR_RADIUS', + 'style_type': 'num', 'var_type': 'int32_t', 'default':'`0`', 'inherited': 0, 'layout': 0, 'ext_draw': 0, + 'dsc': "Sets the intensity of blurring. Applied on each lv_part separately before the children are rendered."}, + +{'name': 'BLUR_BACKDROP', + 'style_type': 'num', 'var_type': 'bool', 'default':'`false`', 'inherited': 0, 'layout': 0, 'ext_draw': 0, + 'dsc': "If `true` the background of the widget will be blurred. The part should have < 100% opacity to make it visible. If `false` the given part will be blurred when it's rendered but before drawing the children."}, + +{'name': 'BLUR_QUALITY', + 'style_type': 'num', 'var_type': 'lv_blur_quality_t', 'default':'`LV_BLUR_QUALITY_AUTO`', 'inherited': 0, 'layout': 0, 'ext_draw': 0, + 'dsc': "Setting to `LV_BLUR_QUALITY_SPEED` the blurring algorithm will prefer speed over quality. `LV_BLUR_QUALITY_PRECISION` will force using higher quality but slower blur. With `LV_BLUR_QUALITY_AUTO` the quality will be selected automatically. "}, + {'name': 'ANIM', 'style_type': 'ptr', 'var_type': 'const lv_anim_t *', 'default':'`NULL`', 'inherited': 0, 'layout': 0, 'ext_draw': 0, 'dsc': "Animation template for Widget's animation. Should be a pointer to `lv_anim_t`. The animation parameters are widget specific, e.g. animation time could be the E.g. blink time of the cursor on the Text Area or scroll time of a roller. See Widgets' documentation to learn more."}, diff --git a/src/core/lv_obj.c b/src/core/lv_obj.c index 34a4b0fc0b..cfecadb9ce 100644 --- a/src/core/lv_obj.c +++ b/src/core/lv_obj.c @@ -693,10 +693,20 @@ static void lv_obj_draw(lv_event_t * e) } else if(code == LV_EVENT_DRAW_MAIN) { lv_layer_t * layer = lv_event_get_layer(e); + lv_draw_rect_dsc_t draw_dsc; lv_draw_rect_dsc_init(&draw_dsc); draw_dsc.base.layer = layer; + lv_draw_blur_dsc_t blur_dsc; + lv_draw_blur_dsc_init(&blur_dsc); + blur_dsc.corner_radius = draw_dsc.radius; + + bool backdrop_blur = lv_obj_get_style_blur_backdrop(obj, LV_PART_INDICATOR); + lv_obj_init_draw_blur_dsc(obj, LV_PART_MAIN, &blur_dsc); + blur_dsc.base.layer = layer; + if(backdrop_blur) lv_draw_blur(layer, &blur_dsc, &obj->coords); + lv_obj_init_draw_rect_dsc(obj, LV_PART_MAIN, &draw_dsc); /*If the border is drawn later disable loading its properties*/ if(lv_obj_get_style_border_post(obj, LV_PART_MAIN)) { @@ -710,6 +720,9 @@ static void lv_obj_draw(lv_event_t * e) lv_area_increase(&coords, w, h); lv_draw_rect(layer, &draw_dsc, &coords); + + blur_dsc.blur_radius = lv_obj_get_style_blur_radius(obj, LV_PART_MAIN); + if(!backdrop_blur) lv_draw_blur(layer, &blur_dsc, &coords); } else if(code == LV_EVENT_DRAW_POST) { lv_layer_t * layer = lv_event_get_layer(e); @@ -747,17 +760,34 @@ static void draw_scrollbar(lv_obj_t * obj, lv_layer_t * layer) if(lv_area_get_size(&hor_area) <= 0 && lv_area_get_size(&ver_area) <= 0) return; - lv_draw_rect_dsc_t draw_dsc; - lv_result_t sb_res = scrollbar_init_draw_dsc(obj, &draw_dsc); + lv_draw_rect_dsc_t rect_dsc; + lv_result_t sb_res = scrollbar_init_draw_dsc(obj, &rect_dsc); if(sb_res != LV_RESULT_OK) return; + bool backdrop_blur = lv_obj_get_style_blur_backdrop(obj, LV_PART_SCROLLBAR); + lv_draw_blur_dsc_t blur_dsc; + lv_draw_blur_dsc_init(&blur_dsc); + blur_dsc.corner_radius = rect_dsc.radius; + blur_dsc.blur_radius = lv_obj_get_style_blur_radius(obj, LV_PART_SCROLLBAR); + if(lv_area_get_size(&hor_area) > 0) { - draw_dsc.base.id1 = 0; - lv_draw_rect(layer, &draw_dsc, &hor_area); + if(backdrop_blur) lv_obj_init_draw_blur_dsc(obj, LV_PART_SCROLLBAR, &blur_dsc); + blur_dsc.base.id1 = 0; + lv_draw_blur(layer, &blur_dsc, &hor_area); + + rect_dsc.base.id1 = 0; + lv_draw_rect(layer, &rect_dsc, &hor_area); + + if(!backdrop_blur) lv_draw_blur(layer, &blur_dsc, &hor_area); } if(lv_area_get_size(&ver_area) > 0) { - draw_dsc.base.id1 = 1; - lv_draw_rect(layer, &draw_dsc, &ver_area); + blur_dsc.base.id1 = 1; + if(backdrop_blur) lv_draw_blur(layer, &blur_dsc, &ver_area); + + rect_dsc.base.id1 = 1; + lv_draw_rect(layer, &rect_dsc, &ver_area); + + if(!backdrop_blur) lv_draw_blur(layer, &blur_dsc, &ver_area); } } diff --git a/src/core/lv_obj_draw.c b/src/core/lv_obj_draw.c index 03f10aad4e..1f1908e8a3 100644 --- a/src/core/lv_obj_draw.c +++ b/src/core/lv_obj_draw.c @@ -12,7 +12,6 @@ #include "../display/lv_display.h" #include "../indev/lv_indev.h" #include "../stdlib/lv_string.h" -#include "../draw/lv_draw_arc.h" /********************* * DEFINES @@ -319,6 +318,24 @@ void lv_obj_init_draw_arc_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_arc_dsc_t LV_PROFILER_DRAW_END; } +void lv_obj_init_draw_blur_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_blur_dsc_t * draw_dsc) +{ + LV_PROFILER_DRAW_BEGIN; + draw_dsc->base.obj = obj; + draw_dsc->base.part = part; + + draw_dsc->blur_radius = lv_obj_get_style_blur_radius(obj, part); + draw_dsc->quality = lv_obj_get_style_blur_quality(obj, part); + + /*Radius might be set earlier as it's already known*/ + if(draw_dsc->corner_radius == 0) { + draw_dsc->corner_radius = lv_obj_get_style_radius(obj, part); + } + + LV_PROFILER_DRAW_END; +} + + int32_t lv_obj_calculate_ext_draw_size(lv_obj_t * obj, lv_part_t part) { LV_PROFILER_DRAW_BEGIN; diff --git a/src/core/lv_obj_draw.h b/src/core/lv_obj_draw.h index 4ad447c970..1f6c287af8 100644 --- a/src/core/lv_obj_draw.h +++ b/src/core/lv_obj_draw.h @@ -20,6 +20,7 @@ extern "C" { #include "../draw/lv_draw_line.h" #include "../draw/lv_draw_arc.h" #include "../draw/lv_draw_triangle.h" +#include "../draw/lv_draw_blur.h" #include "lv_obj_style.h" /********************* @@ -103,6 +104,18 @@ void lv_obj_init_draw_line_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_line_dsc_ */ void lv_obj_init_draw_arc_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_arc_dsc_t * draw_dsc); + +/** + * Initialize a blur draw descriptor from an object's styles in its current state. + * draw_dsc->radius will only be calculated if it's 0 initially. Radius can be set before calling this function + * to avoid getting it twice. + * @param obj pointer to an object + * @param part part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc + * @param draw_dsc the descriptor to initialize. + * Should be initialized with `lv_draw_blur_dsc_init(draw_dsc)`. + */ +void lv_obj_init_draw_blur_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_blur_dsc_t * draw_dsc); + /** * Get the required extra size (around the object's part) to draw shadow, outline, value etc. * @param obj pointer to an object diff --git a/src/core/lv_obj_pos.c b/src/core/lv_obj_pos.c index e5463f78c2..c0112707e6 100644 --- a/src/core/lv_obj_pos.c +++ b/src/core/lv_obj_pos.c @@ -36,6 +36,8 @@ static void layout_update_core(lv_obj_t * obj); static void transform_point_array(const lv_obj_t * obj, lv_point_t * p, size_t p_count, bool inv); static bool is_transformed(const lv_obj_t * obj); static lv_obj_tree_walk_res_t update_layout_completed_cb(lv_obj_t * obj, void * user_data); +static lv_result_t invalidate_area_core(const lv_obj_t * obj, lv_area_t * area_tmp); + /********************** * STATIC VARIABLES **********************/ @@ -860,35 +862,94 @@ void lv_obj_get_transformed_area(const lv_obj_t * obj, lv_area_t * area, lv_obj_ area->y2 = LV_MAX4(p[0].y, p[1].y, p[2].y, p[3].y); } -void lv_obj_invalidate_area(const lv_obj_t * obj, const lv_area_t * area) +typedef struct { + const lv_obj_t * requester_obj; + const lv_area_t * inv_area; +} blur_walk_data_t; + +static lv_obj_tree_walk_res_t blur_walk_cb(lv_obj_t * obj, void * user_data) +{ + blur_walk_data_t * blur_data = user_data; + if(obj == blur_data->requester_obj) return LV_OBJ_TREE_WALK_SKIP_CHILDREN; + + /*Truncate the area to the object*/ + lv_area_t obj_coords; + int32_t ext_size = lv_obj_get_ext_draw_size(obj); + lv_area_copy(&obj_coords, &obj->coords); + lv_area_increase(&obj_coords, ext_size, ext_size); + + if(is_transformed(obj)) { + lv_obj_get_transformed_area(obj, &obj_coords, LV_OBJ_POINT_TRANSFORM_FLAG_RECURSIVE); + } + + /*If the widget has blur set, invalidate it*/ + if(lv_area_is_on(blur_data->inv_area, &obj_coords)) { + const uint32_t group = (uint32_t)1 << lv_style_get_prop_group(LV_STYLE_BLUR_RADIUS); + const lv_state_t state = lv_obj_style_get_selector_state(lv_obj_get_state(obj)); + const lv_state_t state_inv = ~state; + lv_style_value_t v; + uint32_t i; + for(i = 0; i < obj->style_cnt; i++) { + lv_obj_style_t * obj_style = &obj->styles[i]; + if(obj_style->is_disabled) continue; + if((obj_style->style->has_group & group) == 0) continue; + lv_state_t state_style = lv_obj_style_get_selector_state(obj->styles[i].selector); + if((state_style & state_inv)) continue; + if(lv_style_get_prop(obj_style->style, LV_STYLE_BLUR_RADIUS, &v)) { + /*Truncate the area to the object*/ + ext_size = lv_obj_get_ext_draw_size(obj); + lv_area_copy(&obj_coords, &obj->coords); + obj_coords.x1 -= ext_size; + obj_coords.y1 -= ext_size; + obj_coords.x2 += ext_size; + obj_coords.y2 += ext_size; + + invalidate_area_core(obj, &obj_coords); + + /*No need to check the children as the widget is already invalidated + *which will redraw the children too*/ + return LV_OBJ_TREE_WALK_SKIP_CHILDREN; + } + } + + /*Check the next child, maybe it's blurred*/ + return LV_OBJ_TREE_WALK_NEXT; + } + else { + /*Not on the area of interest, skip it*/ + return LV_OBJ_TREE_WALK_SKIP_CHILDREN; + } + +} + +lv_result_t lv_obj_invalidate_area(const lv_obj_t * obj, const lv_area_t * area) { LV_ASSERT_OBJ(obj, MY_CLASS); lv_display_t * disp = lv_obj_get_display(obj); - if(!lv_display_is_invalidation_enabled(disp)) return; + if(!lv_display_is_invalidation_enabled(disp)) return LV_RESULT_INVALID; lv_area_t area_tmp; lv_area_copy(&area_tmp, area); - if(!lv_obj_area_is_visible(obj, &area_tmp)) return; -#if LV_DRAW_TRANSFORM_USE_MATRIX - /** - * When using the global matrix, the vertex coordinates of clip_area lose precision after transformation, - * which can be solved by expanding the redrawing area. - */ - lv_area_increase(&area_tmp, 5, 5); -#else - if(obj->spec_attr && obj->spec_attr->layer_type == LV_LAYER_TYPE_TRANSFORM) { - /*Make the area slightly larger to avoid rounding errors. - *5 is an empirical value*/ - lv_area_increase(&area_tmp, 5, 5); - } -#endif + lv_result_t res = invalidate_area_core(obj, &area_tmp); + if(res == LV_RESULT_INVALID) return res; - lv_inv_area(lv_obj_get_display(obj), &area_tmp); + /*If this area is on a blurred widget, invalidate that widget too*/ + blur_walk_data_t blur_walk_data; + blur_walk_data.requester_obj = obj; + blur_walk_data.inv_area = &area_tmp; + lv_obj_tree_walk(disp->act_scr, blur_walk_cb, &blur_walk_data); + if(disp->prev_scr) lv_obj_tree_walk(disp->prev_scr, blur_walk_cb, &blur_walk_data); + lv_obj_tree_walk(disp->sys_layer, blur_walk_cb, &blur_walk_data); + lv_obj_tree_walk(disp->top_layer, blur_walk_cb, &blur_walk_data); + lv_obj_tree_walk(disp->bottom_layer, blur_walk_cb, &blur_walk_data); + + return res; } -void lv_obj_invalidate(const lv_obj_t * obj) + +lv_result_t lv_obj_invalidate(const lv_obj_t * obj) { LV_ASSERT_OBJ(obj, MY_CLASS); @@ -901,7 +962,9 @@ void lv_obj_invalidate(const lv_obj_t * obj) obj_coords.x2 += ext_size; obj_coords.y2 += ext_size; - lv_obj_invalidate_area(obj, &obj_coords); + lv_result_t res = lv_obj_invalidate_area(obj, &obj_coords); + + return res; } bool lv_obj_area_is_visible(const lv_obj_t * obj, lv_area_t * area) @@ -1351,4 +1414,26 @@ static lv_obj_tree_walk_res_t update_layout_completed_cb(lv_obj_t * obj, void * LV_UNUSED(user_data); lv_obj_send_event(obj, LV_EVENT_UPDATE_LAYOUT_COMPLETED, NULL); return LV_OBJ_TREE_WALK_NEXT; -} \ No newline at end of file +} + +static lv_result_t invalidate_area_core(const lv_obj_t * obj, lv_area_t * area_tmp) +{ + if(!lv_obj_area_is_visible(obj, area_tmp)) return LV_RESULT_INVALID; +#if LV_DRAW_TRANSFORM_USE_MATRIX + /** + * When using the global matrix, the vertex coordinates of clip_area lose precision after transformation, + * which can be solved by expanding the redrawing area. + */ + lv_area_increase(area_tmp, 5, 5); +#else + if(obj->spec_attr && obj->spec_attr->layer_type == LV_LAYER_TYPE_TRANSFORM) { + /*Make the area slightly larger to avoid rounding errors. + *5 is an empirical value*/ + lv_area_increase(area_tmp, 5, 5); + } +#endif + + lv_result_t res = lv_inv_area(lv_obj_get_display(obj), area_tmp); + return res; +} + diff --git a/src/core/lv_obj_pos.h b/src/core/lv_obj_pos.h index 1401abf5d1..a7967d950d 100644 --- a/src/core/lv_obj_pos.h +++ b/src/core/lv_obj_pos.h @@ -401,14 +401,18 @@ void lv_obj_get_transformed_area(const lv_obj_t * obj, lv_area_t * area, lv_obj_ * The area will be truncated to the object's area and marked for redraw. * @param obj pointer to an object * @param area the area to redraw + * @return LV_RESULT_OK: the area is invalidated; LV_RESULT_INVALID: the area wasn't invalidated. + * (maybe it was off-screen or fully clipped) */ -void lv_obj_invalidate_area(const lv_obj_t * obj, const lv_area_t * area); +lv_result_t lv_obj_invalidate_area(const lv_obj_t * obj, const lv_area_t * area); /** * Mark the object as invalid to redrawn its area * @param obj pointer to an object + * @return LV_RESULT_OK: the area is invalidated; LV_RESULT_INVALID: the area wasn't invalidated. + * (maybe it was off-screen or fully clipped) */ -void lv_obj_invalidate(const lv_obj_t * obj); +lv_result_t lv_obj_invalidate(const lv_obj_t * obj); /** * Tell whether an area of an object is visible (even partially) now or not diff --git a/src/core/lv_obj_style_gen.c b/src/core/lv_obj_style_gen.c index 4b73f5c721..e7237ce177 100644 --- a/src/core/lv_obj_style_gen.c +++ b/src/core/lv_obj_style_gen.c @@ -746,7 +746,8 @@ void lv_obj_set_style_opa_layered(lv_obj_t * obj, lv_opa_t value, lv_style_selec lv_obj_set_local_style_prop(obj, LV_STYLE_OPA_LAYERED, v, selector); } -void lv_obj_set_style_color_filter_dsc(lv_obj_t * obj, const lv_color_filter_dsc_t * value, lv_style_selector_t selector) +void lv_obj_set_style_color_filter_dsc(lv_obj_t * obj, const lv_color_filter_dsc_t * value, + lv_style_selector_t selector) { lv_style_value_t v = { .ptr = value @@ -778,6 +779,30 @@ void lv_obj_set_style_recolor_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selec lv_obj_set_local_style_prop(obj, LV_STYLE_RECOLOR_OPA, v, selector); } +void lv_obj_set_style_blur_radius(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_BLUR_RADIUS, v, selector); +} + +void lv_obj_set_style_blur_backdrop(lv_obj_t * obj, bool value, lv_style_selector_t selector) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_obj_set_local_style_prop(obj, LV_STYLE_BLUR_BACKDROP, v, selector); +} + +void lv_obj_set_style_blur_quality(lv_obj_t * obj, lv_blur_quality_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_BLUR_QUALITY, v, selector); +} + void lv_obj_set_style_anim(lv_obj_t * obj, const lv_anim_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 5be2b89401..420983b9fa 100644 --- a/src/core/lv_obj_style_gen.h +++ b/src/core/lv_obj_style_gen.h @@ -243,7 +243,8 @@ static inline lv_color_t lv_obj_get_style_bg_grad_color(const lv_obj_t * obj, lv static inline lv_color_t lv_obj_get_style_bg_grad_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_BG_GRAD_COLOR)); + lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, + LV_STYLE_BG_GRAD_COLOR)); return v.color; } @@ -303,7 +304,8 @@ static inline lv_color_t lv_obj_get_style_bg_image_recolor(const lv_obj_t * obj, static inline lv_color_t lv_obj_get_style_bg_image_recolor_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_BG_IMAGE_RECOLOR)); + lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, + LV_STYLE_BG_IMAGE_RECOLOR)); return v.color; } @@ -327,7 +329,8 @@ static inline lv_color_t lv_obj_get_style_border_color(const lv_obj_t * obj, lv_ static inline lv_color_t lv_obj_get_style_border_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_BORDER_COLOR)); + lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, + LV_STYLE_BORDER_COLOR)); return v.color; } @@ -369,7 +372,8 @@ static inline lv_color_t lv_obj_get_style_outline_color(const lv_obj_t * obj, lv static inline lv_color_t lv_obj_get_style_outline_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_OUTLINE_COLOR)); + lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, + LV_STYLE_OUTLINE_COLOR)); return v.color; } @@ -417,7 +421,8 @@ static inline lv_color_t lv_obj_get_style_shadow_color(const lv_obj_t * obj, lv_ static inline lv_color_t lv_obj_get_style_shadow_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_SHADOW_COLOR)); + lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, + LV_STYLE_SHADOW_COLOR)); return v.color; } @@ -441,7 +446,8 @@ static inline lv_color_t lv_obj_get_style_image_recolor(const lv_obj_t * obj, lv static inline lv_color_t lv_obj_get_style_image_recolor_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_IMAGE_RECOLOR)); + lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, + LV_STYLE_IMAGE_RECOLOR)); return v.color; } @@ -591,7 +597,8 @@ static inline lv_color_t lv_obj_get_style_text_outline_stroke_color(const lv_obj 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)); + 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; } @@ -661,6 +668,24 @@ static inline lv_opa_t lv_obj_get_style_recolor_opa(const lv_obj_t * obj, lv_par return (lv_opa_t)v.num; } +static inline int32_t lv_obj_get_style_blur_radius(const lv_obj_t * obj, lv_part_t part) +{ + lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BLUR_RADIUS); + return (int32_t)v.num; +} + +static inline bool lv_obj_get_style_blur_backdrop(const lv_obj_t * obj, lv_part_t part) +{ + lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BLUR_BACKDROP); + return (bool)v.num; +} + +static inline lv_blur_quality_t lv_obj_get_style_blur_quality(const lv_obj_t * obj, lv_part_t part) +{ + lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BLUR_QUALITY); + return (lv_blur_quality_t)v.num; +} + static inline const lv_anim_t * lv_obj_get_style_anim(const lv_obj_t * obj, lv_part_t part) { lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ANIM); @@ -897,10 +922,14 @@ void lv_obj_set_style_radial_offset(lv_obj_t * obj, int32_t value, lv_style_sele void lv_obj_set_style_clip_corner(lv_obj_t * obj, bool value, lv_style_selector_t selector); void lv_obj_set_style_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector); void lv_obj_set_style_opa_layered(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector); -void lv_obj_set_style_color_filter_dsc(lv_obj_t * obj, const lv_color_filter_dsc_t * value, lv_style_selector_t selector); +void lv_obj_set_style_color_filter_dsc(lv_obj_t * obj, const lv_color_filter_dsc_t * value, + lv_style_selector_t selector); void lv_obj_set_style_color_filter_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector); void lv_obj_set_style_recolor(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector); void lv_obj_set_style_recolor_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector); +void lv_obj_set_style_blur_radius(lv_obj_t * obj, int32_t value, lv_style_selector_t selector); +void lv_obj_set_style_blur_backdrop(lv_obj_t * obj, bool value, lv_style_selector_t selector); +void lv_obj_set_style_blur_quality(lv_obj_t * obj, lv_blur_quality_t value, lv_style_selector_t selector); void lv_obj_set_style_anim(lv_obj_t * obj, const lv_anim_t * value, lv_style_selector_t selector); void lv_obj_set_style_anim_duration(lv_obj_t * obj, uint32_t value, lv_style_selector_t selector); void lv_obj_set_style_transition(lv_obj_t * obj, const lv_style_transition_dsc_t * value, lv_style_selector_t selector); diff --git a/src/core/lv_refr.c b/src/core/lv_refr.c index e02e1384a1..21ba22c783 100644 --- a/src/core/lv_refr.c +++ b/src/core/lv_refr.c @@ -266,11 +266,11 @@ void lv_obj_redraw(lv_layer_t * layer, lv_obj_t * obj) LV_PROFILER_REFR_END; } -void lv_inv_area(lv_display_t * disp, const lv_area_t * area_p) +lv_result_t lv_inv_area(lv_display_t * disp, const lv_area_t * area_p) { if(!disp) disp = lv_display_get_default(); - if(!disp) return; - if(!lv_display_is_invalidation_enabled(disp)) return; + if(!disp) return LV_RESULT_INVALID; + if(!lv_display_is_invalidation_enabled(disp)) return LV_RESULT_INVALID; /** * There are two reasons for this issue: @@ -288,7 +288,7 @@ void lv_inv_area(lv_display_t * disp, const lv_area_t * area_p) /*Clear the invalidate buffer if the parameter is NULL*/ if(area_p == NULL) { disp->inv_p = 0; - return; + return LV_RESULT_OK; } lv_area_t scr_area; @@ -301,7 +301,7 @@ void lv_inv_area(lv_display_t * disp, const lv_area_t * area_p) bool suc; suc = lv_area_intersect(&com_area, area_p, &scr_area); - if(suc == false) return; /*Out of the screen*/ + if(suc == false) return LV_RESULT_INVALID; /*Out of the screen*/ if(disp->color_format == LV_COLOR_FORMAT_I1) { /*Make sure that the X coordinates start and end on byte boundary. @@ -315,16 +315,16 @@ void lv_inv_area(lv_display_t * disp, const lv_area_t * area_p) disp->inv_areas[0] = scr_area; disp->inv_p = 1; lv_display_send_event(disp, LV_EVENT_REFR_REQUEST, NULL); - return; + return LV_RESULT_OK; } lv_result_t res = lv_display_send_event(disp, LV_EVENT_INVALIDATE_AREA, &com_area); - if(res != LV_RESULT_OK) return; + if(res != LV_RESULT_OK) return LV_RESULT_INVALID; /*Save only if this area is not in one of the saved areas*/ uint16_t i; for(i = 0; i < disp->inv_p; i++) { - if(lv_area_is_in(&com_area, &disp->inv_areas[i], 0) != false) return; + if(lv_area_is_in(&com_area, &disp->inv_areas[i], 0) != false) return LV_RESULT_OK; } /*Save the area*/ @@ -337,6 +337,8 @@ void lv_inv_area(lv_display_t * disp, const lv_area_t * area_p) disp->inv_p++; lv_display_send_event(disp, LV_EVENT_REFR_REQUEST, NULL); + + return LV_RESULT_OK; } /** diff --git a/src/core/lv_refr_private.h b/src/core/lv_refr_private.h index 0519d65292..79c2ede962 100644 --- a/src/core/lv_refr_private.h +++ b/src/core/lv_refr_private.h @@ -43,8 +43,9 @@ void lv_refr_deinit(void); * @param area_p pointer to area which should be invalidated (NULL: delete the invalidated areas) * @param disp pointer to display where the area should be invalidated (NULL can be used if there is * only one display) + * @return LV_RESULT_OK: the area is invalidated; LV_RESULT_INVALID: the area wasn't invalidated. */ -void lv_inv_area(lv_display_t * disp, const lv_area_t * area_p); +lv_result_t lv_inv_area(lv_display_t * disp, const lv_area_t * area_p); /** * Get the display which is being refreshed diff --git a/src/draw/lv_draw.c b/src/draw/lv_draw.c index 24eecdadb6..fc6fdd3338 100644 --- a/src/draw/lv_draw.c +++ b/src/draw/lv_draw.c @@ -605,6 +605,8 @@ static inline size_t get_draw_dsc_size(lv_draw_task_type_t type) return sizeof(lv_draw_arc_dsc_t); case LV_DRAW_TASK_TYPE_TRIANGLE: return sizeof(lv_draw_triangle_dsc_t); + case LV_DRAW_TASK_TYPE_BLUR: + return sizeof(lv_draw_blur_dsc_t); case LV_DRAW_TASK_TYPE_MASK_RECTANGLE: return sizeof(lv_draw_mask_rect_dsc_t); diff --git a/src/draw/lv_draw.h b/src/draw/lv_draw.h index dd8291da5c..c112bbab09 100644 --- a/src/draw/lv_draw.h +++ b/src/draw/lv_draw.h @@ -58,6 +58,7 @@ typedef enum { LV_DRAW_TASK_TYPE_TRIANGLE, LV_DRAW_TASK_TYPE_MASK_RECTANGLE, LV_DRAW_TASK_TYPE_MASK_BITMAP, + LV_DRAW_TASK_TYPE_BLUR, #if LV_USE_VECTOR_GRAPHIC LV_DRAW_TASK_TYPE_VECTOR, #endif diff --git a/src/draw/lv_draw_blur.c b/src/draw/lv_draw_blur.c new file mode 100644 index 0000000000..c09d59d375 --- /dev/null +++ b/src/draw/lv_draw_blur.c @@ -0,0 +1,65 @@ +/** + * @file lv_draw_blur.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_draw_private.h" +#include "lv_draw_blur.h" +#include "../misc/lv_types.h" +#include "../stdlib/lv_string.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void LV_ATTRIBUTE_FAST_MEM lv_draw_blur_dsc_init(lv_draw_blur_dsc_t * dsc) +{ + lv_memzero(dsc, sizeof(lv_draw_blur_dsc_t)); + dsc->base.dsc_size = sizeof(lv_draw_blur_dsc_t); +} + +lv_draw_blur_dsc_t * lv_draw_task_get_blur_dsc(lv_draw_task_t * task) +{ + return task->type == LV_DRAW_TASK_TYPE_BLUR ? (lv_draw_blur_dsc_t *)task->draw_dsc : NULL; +} + +void LV_ATTRIBUTE_FAST_MEM lv_draw_blur(lv_layer_t * layer, const lv_draw_blur_dsc_t * dsc, const lv_area_t * coords) +{ + if(dsc->blur_radius <= 0) return; + + LV_PROFILER_DRAW_BEGIN; + + lv_draw_task_t * t = lv_draw_add_task(layer, coords, LV_DRAW_TASK_TYPE_BLUR); + + lv_memcpy(t->draw_dsc, dsc, sizeof(*dsc)); + + lv_draw_finalize_task_creation(layer, t); + LV_PROFILER_DRAW_END; +} + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/src/draw/lv_draw_blur.h b/src/draw/lv_draw_blur.h new file mode 100644 index 0000000000..acbb556502 --- /dev/null +++ b/src/draw/lv_draw_blur.h @@ -0,0 +1,82 @@ +/** + * @file lv_draw_blur.h + * + */ + +#ifndef LV_DRAW_BLUR_H +#define LV_DRAW_BLUR_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../lv_conf_internal.h" +#include "../misc/lv_color.h" +#include "../misc/lv_area.h" +#include "../misc/lv_style.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + lv_draw_dsc_base_t base; + + /** + * The intensity of blur. + */ + int32_t blur_radius; + + /** + * The corner radius of the blurred area + */ + int32_t corner_radius; + + /** + * Sets whether to prefer speed or precision + */ + lv_blur_quality_t quality; + +} lv_draw_blur_dsc_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Initialize a blur draw descriptor + * @param dsc pointer to a draw descriptor + */ +void lv_draw_blur_dsc_init(lv_draw_blur_dsc_t * dsc); + +/** + * Try to get a blur draw descriptor from a draw task. + * @param task draw task + * @return the task's draw descriptor or NULL if the task is not of type LV_DRAW_TASK_TYPE_BLUR + */ +lv_draw_blur_dsc_t * lv_draw_task_get_blur_dsc(lv_draw_task_t * task); + +/** + * Create a blur draw task + * @param layer pointer to a layer + * @param dsc pointer to an initialized `lv_draw_blur_dsc_t` variable + * @param coords coordinates of the character + */ +void lv_draw_blur(lv_layer_t * layer, const lv_draw_blur_dsc_t * dsc, const lv_area_t * coords); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_BLUR_H*/ diff --git a/src/draw/sw/lv_draw_sw.c b/src/draw/sw/lv_draw_sw.c index c62b6c6706..31c52a2f64 100644 --- a/src/draw/sw/lv_draw_sw.c +++ b/src/draw/sw/lv_draw_sw.c @@ -409,6 +409,9 @@ static void execute_drawing(lv_draw_task_t * t) case LV_DRAW_TASK_TYPE_LINE: lv_draw_sw_line(t, t->draw_dsc); break; + case LV_DRAW_TASK_TYPE_BLUR: + lv_draw_sw_blur(t, t->draw_dsc, &t->area); + break; case LV_DRAW_TASK_TYPE_TRIANGLE: lv_draw_sw_triangle(t, t->draw_dsc); break; diff --git a/src/draw/sw/lv_draw_sw.h b/src/draw/sw/lv_draw_sw.h index aae25c6d18..b39351c91d 100644 --- a/src/draw/sw/lv_draw_sw.h +++ b/src/draw/sw/lv_draw_sw.h @@ -26,6 +26,7 @@ extern "C" { #include "../lv_draw_image.h" #include "../lv_draw_line.h" #include "../lv_draw_arc.h" +#include "../lv_draw_blur.h" #include "lv_draw_sw_utils.h" #include "blend/lv_draw_sw_blend.h" @@ -121,6 +122,15 @@ void lv_draw_sw_layer(lv_draw_task_t * t, const lv_draw_image_dsc_t * draw_dsc, */ void lv_draw_sw_triangle(lv_draw_task_t * t, const lv_draw_triangle_dsc_t * dsc); + +/** + * Blur an area with SW render + * @param t pointer to a draw task + * @param dsc the draw descriptor + * @param coords the area to blur + */ +void lv_draw_sw_blur(lv_draw_task_t * t, const lv_draw_blur_dsc_t * dsc, const lv_area_t * coords); + /** * Mask out a rectangle with radius from a current layer * @param t pointer to a draw task diff --git a/src/draw/sw/lv_draw_sw_blur.c b/src/draw/sw/lv_draw_sw_blur.c new file mode 100644 index 0000000000..f38d2a4473 --- /dev/null +++ b/src/draw/sw/lv_draw_sw_blur.c @@ -0,0 +1,395 @@ +/** + * @file lv_draw_sw_blur.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "../../misc/lv_area_private.h" +#include "lv_draw_sw_mask_private.h" +#include "../lv_draw_private.h" +#include "lv_draw_sw.h" + +#if LV_USE_DRAW_SW + +#include "../../misc/lv_math.h" +#include "../../misc/lv_types.h" +#include "../../core/lv_refr_private.h" +#include "../../stdlib/lv_string.h" + +/********************* + * DEFINES + *********************/ +#define BLUR_INTENSITY_BITS 12 +#define BLUR_INTENSITY_MAX (1 << 12) +#define BLUR_INTENSITY_HALF ((1 << 12) / 2) + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void blur_2_bytes_init(uint32_t * sum, lv_color16_t * buf, uint32_t sample_len, int32_t stride); +static inline uint16_t blur_2_bytes(uint32_t * sum, uint16_t px, uint32_t intensity); + +static void blur_3_bytes_init(uint32_t * sum, volatile uint8_t * buf, uint32_t sample_len, int32_t stride); +static inline void blur_3_bytes(uint32_t * sum, volatile uint8_t * buf, uint32_t intensity); + +static int32_t get_rounded_edge_point(int32_t p_start, int32_t p_end, int32_t p, int32_t r); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_sw_blur(lv_draw_task_t * t, const lv_draw_blur_dsc_t * dsc, const lv_area_t * coords) +{ + if(dsc->blur_radius == 0) return; + LV_PROFILER_DRAW_BEGIN; + + lv_area_t clipped_coords; + if(!lv_area_intersect(&clipped_coords, coords, &t->clip_area)) return; + lv_area_move(&clipped_coords, -t->target_layer->buf_area.x1, -t->target_layer->buf_area.y1); + + uint32_t blur_radius = dsc->blur_radius; + + /*On larger radius skip some pixels as the result is a blob anyways, so not all pixels matter + *This only every 2nd or 3rd px will be blurred, the result will be stored in the layers buffers, + *and finally the missing pixels are set to nearest blurred pixel. We loose precision but it looks ok + *and and it's very fast. + */ + int32_t skip_cnt = 1; + if(dsc->quality == LV_BLUR_QUALITY_AUTO) { + if(blur_radius >= 32 && dsc->corner_radius == 0) skip_cnt = 3; + else if(blur_radius >= 8) skip_cnt = 2; + } + else if(dsc->quality == LV_BLUR_QUALITY_SPEED) { + if(blur_radius >= 24) skip_cnt = 3; + else skip_cnt = 2; + } + + /*The blurring are must be multiples of skip_cnt so the blurring is all directions + * blur the same pixels if some pixels are skipped*/ + clipped_coords.x1 = ((clipped_coords.x1 + (skip_cnt - 1)) / skip_cnt) * skip_cnt; + clipped_coords.x2 = ((clipped_coords.x2 - (skip_cnt - 1)) / skip_cnt) * skip_cnt; + clipped_coords.y1 = ((clipped_coords.y1 + (skip_cnt - 1)) / skip_cnt) * skip_cnt; + clipped_coords.y2 = ((clipped_coords.y2 - (skip_cnt - 1)) / skip_cnt) * skip_cnt; + if(lv_area_get_width(&clipped_coords) < 0) return; + if(lv_area_get_height(&clipped_coords) < 0) return; + + blur_radius = blur_radius / skip_cnt; + + /*We will use an IIR low pass filer in all 4 direction: top to bottom, bottom to top, left to right, right to left. + *Approximate the the filter coefficient from the radius. + *The filter is like: this_px = mix(prev_px, this_px, intensity) + */ + uint32_t intensity = (BLUR_INTENSITY_MAX * blur_radius) / (blur_radius + 4); + + int32_t sample_len = LV_MAX(blur_radius / 2, 1); + int32_t radius = dsc->corner_radius; + int32_t w = lv_area_get_width(coords); + int32_t h = lv_area_get_height(coords); + int32_t short_side = LV_MIN(w, h); + if(radius > short_side >> 1) radius = short_side >> 1; + + uint32_t px_size = lv_color_format_get_size(t->target_layer->draw_buf->header.cf); + int32_t stride_byte = t->target_layer->draw_buf->header.stride; + int32_t stride_px = stride_byte / px_size; + int32_t next_px_ofs_byte = px_size * skip_cnt; + + uint32_t sum[3]; + int32_t y; + int32_t x; + + /*Blur each column top to bottom and bottom to top.*/ + for(x = clipped_coords.x1; x <= clipped_coords.x2; x += skip_cnt) { + int32_t cir_y = get_rounded_edge_point(coords->x1, coords->x2, x, radius); + int32_t y_start = LV_CLAMP(clipped_coords.y1, coords->y1 + cir_y, clipped_coords.y2); + int32_t y_end = LV_CLAMP(clipped_coords.y1, coords->y2 - cir_y, clipped_coords.y2); + + /*Make sure that the width and height is a multiple of skip_cnt so that back and forth blurring + *surely affects the same pixels */ + y_start = (y_start / skip_cnt) * skip_cnt; + y_end = (y_end / skip_cnt) * skip_cnt; + if(y_start > y_end) continue; + + uint32_t sample_len_limited = LV_MIN((y_end - y_start) / skip_cnt + 1, sample_len); + + if(px_size >= 3) { + /*Compiler optimization might mishandle it, so add volatile*/ + volatile uint8_t * buf_column = lv_draw_buf_goto_xy(t->target_layer->draw_buf, x, y_start); + blur_3_bytes_init(sum, buf_column, sample_len_limited, stride_byte * skip_cnt); + + for(y = y_start; y <= y_end; y += skip_cnt) { + blur_3_bytes(sum, buf_column, intensity); + buf_column += stride_byte * skip_cnt; + } + + buf_column = lv_draw_buf_goto_xy(t->target_layer->draw_buf, x, y_end); + blur_3_bytes_init(sum, buf_column, sample_len_limited, -stride_byte * skip_cnt); + for(y = y_start; y <= y_end; y += skip_cnt) { + blur_3_bytes(sum, buf_column, intensity); + buf_column -= stride_byte * skip_cnt; + } + } + else if(px_size == 2) { + uint16_t * buf16_column = lv_draw_buf_goto_xy(t->target_layer->draw_buf, x, y_start); + blur_2_bytes_init(sum, (lv_color16_t *)buf16_column, sample_len_limited, stride_px * skip_cnt); + uint16_t buf16_prev = buf16_column[0] + 1; /*Make sure that it's not equal in the first round*/ + + for(y = y_start; y <= y_end; y += skip_cnt) { + if(buf16_prev != *buf16_column) { + *buf16_column = blur_2_bytes(sum, *buf16_column, intensity); + buf16_prev = *buf16_column; + } + buf16_column += stride_px * skip_cnt; + } + + buf16_column = lv_draw_buf_goto_xy(t->target_layer->draw_buf, x, y_end); + blur_2_bytes_init(sum, (lv_color16_t *)buf16_column, sample_len_limited, -stride_px * skip_cnt); + buf16_prev = buf16_column[0] + 1; /*Make sure that it's not equal in the first round*/ + + for(y = y_start; y <= y_end; y += skip_cnt) { + if(buf16_prev != *buf16_column) { + *buf16_column = blur_2_bytes(sum, *buf16_column, intensity); + buf16_prev = *buf16_column; + } + buf16_column -= stride_px * skip_cnt; + } + } + } + + /*Blur each line from left to right and right to left. + *Also fill the gap in each line because of skipped pixels*/ + for(y = clipped_coords.y1; y <= clipped_coords.y2; y += skip_cnt) { + int32_t cir_x = get_rounded_edge_point(coords->y1, coords->y2, y, radius); + int32_t x_start = LV_CLAMP(clipped_coords.x1, coords->x1 + cir_x, clipped_coords.x2); + int32_t x_end = LV_CLAMP(clipped_coords.x1, coords->x2 - cir_x, clipped_coords.x2); + + /*Make sure that the width and height is a multiple of skip_cnt so that back and forth blurring + *surely affects the same pixels */ + x_start = (x_start / skip_cnt) * skip_cnt; + x_end = (x_end / skip_cnt) * skip_cnt; + + if(x_start > x_end) continue; + uint32_t line_len_byte = (x_end - x_start + skip_cnt) * px_size; + uint32_t sample_len_limited = LV_MIN((x_end - x_start) / skip_cnt + 1, sample_len); + + if(px_size >= 3) { + /*Compiler optimization might mishandle it, so add volatile*/ + volatile uint8_t * buf_line = lv_draw_buf_goto_xy(t->target_layer->draw_buf, x_start, y); + + blur_3_bytes_init(sum, buf_line, sample_len_limited, px_size * skip_cnt); + buf_line += px_size * skip_cnt; + for(x = x_start + skip_cnt; x <= x_end; x += skip_cnt) { + blur_3_bytes(sum, buf_line, intensity); + buf_line += next_px_ofs_byte; + } + + buf_line = lv_draw_buf_goto_xy(t->target_layer->draw_buf, x_end, y); + blur_3_bytes_init(sum, buf_line, sample_len_limited, - px_size * skip_cnt); + for(x = x_start; x <= x_end; x += skip_cnt) { + blur_3_bytes(sum, buf_line, intensity); + + /*This is the final pixel, fill the gaps in the line by just repeating the pixel (simple upscale)*/ + if(skip_cnt == 2) { + buf_line[px_size + 0] = buf_line[0]; + buf_line[px_size + 1] = buf_line[1]; + buf_line[px_size + 2] = buf_line[2]; + } + else if(skip_cnt == 3) { + buf_line[px_size + 0] = buf_line[0]; + buf_line[px_size + 1] = buf_line[1]; + buf_line[px_size + 2] = buf_line[2]; + buf_line[px_size * 2 + 0] = buf_line[0]; + buf_line[px_size * 2 + 1] = buf_line[1]; + buf_line[px_size * 2 + 2] = buf_line[2]; + } + + /*Fill the empty lines by duplicating a the finished filled lines to the gaps*/ + if(skip_cnt > 1 && x + skip_cnt > x_end) { + uint8_t * buf_copy_from = lv_draw_buf_goto_xy(t->target_layer->draw_buf, x_start, y); + lv_memcpy(buf_copy_from + stride_byte, buf_copy_from, line_len_byte); + if(skip_cnt == 3) { + lv_memcpy(buf_copy_from + stride_byte * 2, buf_copy_from, line_len_byte); + } + } + + buf_line -= next_px_ofs_byte; + } + + } + else if(px_size == 2) { + uint16_t * buf16_line = lv_draw_buf_goto_xy(t->target_layer->draw_buf, x_start, y); + blur_2_bytes_init(sum, (lv_color16_t *)buf16_line, sample_len_limited, skip_cnt); + uint16_t buf16_prev = buf16_line[0] + 1; /*Make sure that it's not equal in the first round*/ + + for(x = x_start; x <= x_end; x += skip_cnt) { + if(buf16_prev != *buf16_line) { + *buf16_line = blur_2_bytes(sum, *buf16_line, intensity); + buf16_prev = *buf16_line; + } + buf16_line += skip_cnt; + } + + buf16_line = lv_draw_buf_goto_xy(t->target_layer->draw_buf, x_end, y); + blur_2_bytes_init(sum, (lv_color16_t *)buf16_line, sample_len_limited, - skip_cnt); + buf16_prev = buf16_line[0] + 1; /*Make sure that it's not equal in the first round*/ + + for(x = x_start; x <= x_end; x += skip_cnt) { + if(buf16_prev != *buf16_line) { + *buf16_line = blur_2_bytes(sum, *buf16_line, intensity); + buf16_prev = *buf16_line; + } + + /*This is the final pixel, fill the gaps in the line by just repeating the pixel (simple upscale)*/ + if(skip_cnt == 2) { + /*Fill the empty lines by duplicating a the finished filled lines to the gaps*/ + buf16_line[1] = buf16_line[0]; + } + else if(skip_cnt == 3) { + /*Fill the empty lines by duplicating a the finished filled lines to the gaps*/ + buf16_line[1] = buf16_line[0]; + buf16_line[2] = buf16_line[0]; + } + + /*Fill the empty lines by duplicating a the finished filled lines to the gaps*/ + if(skip_cnt > 1 && x + skip_cnt > x_end) { + uint8_t * buf_copy_from = lv_draw_buf_goto_xy(t->target_layer->draw_buf, x_start, y); + lv_memcpy(buf_copy_from + stride_byte, buf_copy_from, line_len_byte); + if(skip_cnt == 3) { + lv_memcpy(buf_copy_from + stride_byte * 2, buf_copy_from, line_len_byte); + } + } + + buf16_line -= skip_cnt; + } + } + } + + LV_PROFILER_DRAW_END; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void blur_3_bytes_init(uint32_t * sum, volatile uint8_t * buf, uint32_t sample_len, int32_t stride) +{ + uint32_t s; + + sum[0] = 0; + sum[1] = 0; + sum[2] = 0; + + for(s = 0; s < sample_len; s++) { + sum[0] += buf[0]; + sum[1] += buf[1]; + sum[2] += buf[2]; + buf += stride; + } + sum[0] = (sum[0] << BLUR_INTENSITY_BITS) / sample_len; + sum[1] = (sum[1] << BLUR_INTENSITY_BITS) / sample_len; + sum[2] = (sum[2] << BLUR_INTENSITY_BITS) / sample_len; +} + +static void blur_2_bytes_init(uint32_t * sum, lv_color16_t * buf, uint32_t sample_len, int32_t stride) +{ + uint32_t s; + + sum[0] = 0; + sum[1] = 0; + sum[2] = 0; + for(s = 0; s < sample_len; s++) { + sum[0] += buf->red; + sum[1] += buf->green; + sum[2] += buf->blue; + buf += stride; + } + + sum[0] = (sum[0] << BLUR_INTENSITY_BITS) / sample_len; + sum[1] = (sum[1] << BLUR_INTENSITY_BITS) / sample_len; + sum[2] = (sum[2] << BLUR_INTENSITY_BITS) / sample_len; +} + +static inline uint16_t blur_2_bytes(uint32_t * sum, uint16_t px, uint32_t intensity) +{ + const uint32_t inv = BLUR_INTENSITY_MAX - intensity; + const uint32_t half = BLUR_INTENSITY_MAX >> 1; + const uint32_t shift = BLUR_INTENSITY_BITS; + + /* unpack */ + uint32_t r = px >> 11; + uint32_t g = (px >> 5) & 0x3F; + uint32_t b = px & 0x1F; + + uint32_t s0 = sum[0]; + uint32_t s1 = sum[1]; + uint32_t s2 = sum[2]; + + /* fused multiply-accumulate pattern */ + s0 = (s0 * intensity >> shift) + (r * inv); + s1 = (s1 * intensity >> shift) + (g * inv); + s2 = (s2 * intensity >> shift) + (b * inv); + + sum[0] = s0; + sum[1] = s1; + sum[2] = s2; + + /* final */ + r = (s0 + half) >> shift; + g = (s1 + half) >> shift; + b = (s2 + half) >> shift; + + return (uint16_t)((r << 11) | (g << 5) | b); +} + + + +static inline void blur_3_bytes(uint32_t * sum, volatile uint8_t * buf, uint32_t intensity) +{ + uint32_t intensity_inv = BLUR_INTENSITY_MAX - intensity; + + sum[0] = ((sum[0] * intensity) >> BLUR_INTENSITY_BITS) + ((buf[0] * intensity_inv)); + buf[0] = sum[0] >> BLUR_INTENSITY_BITS; + + sum[1] = ((sum[1] * intensity) >> BLUR_INTENSITY_BITS) + ((buf[1] * intensity_inv)); + buf[1] = sum[1] >> BLUR_INTENSITY_BITS; + + sum[2] = ((sum[2] * intensity) >> BLUR_INTENSITY_BITS) + ((buf[2] * intensity_inv)); + buf[2] = sum[2] >> BLUR_INTENSITY_BITS; +} + +/** + * Get the X or Y point for a rounded edge. + * If the X coordinates are used Y will be returned and vice versa + * Calculates the left or top edge + * @param p_start the edge's X1 or Y1 coordinate + * @param p_end the edge's X2 or Y2 coordinate + * @param p the X or Y coordinate on the edge for which the related X or X should be returned + * @param r the radius of the corner + * @return the X or Y coordinate corresponding to the provided coordinates + */ +static int32_t get_rounded_edge_point(int32_t p_start, int32_t p_end, int32_t p, int32_t r) +{ + if(p < p_start + r) p = r - (p - p_start); + else if(p > p_end - r) p = r - (p_end - p); + else return 0; + + uint32_t res = lv_sqrt32(r * r - p * p); + return r - res; +} + + +#endif /*LV_USE_DRAW_SW*/ diff --git a/src/misc/lv_style.h b/src/misc/lv_style.h index 30c67e63fb..78c41ca100 100644 --- a/src/misc/lv_style.h +++ b/src/misc/lv_style.h @@ -118,6 +118,12 @@ typedef enum { LV_BORDER_SIDE_INTERNAL = 0x10, /**< FOR matrix-like objects (e.g. Button matrix)*/ } lv_border_side_t; +typedef enum { + LV_BLUR_QUALITY_AUTO = 0, /**< Set the quality automatically */ + LV_BLUR_QUALITY_SPEED, /**< Prefer speed over precision */ + LV_BLUR_QUALITY_PRECISION, /**< Prefer precision over speed*/ +} lv_blur_quality_t; + /** A image colorkey definition. * The transparency within the color range of [low, high] will be set to LV_OPA_TRANSP If the "enable" flag is set to true. */ @@ -293,7 +299,11 @@ enum _lv_style_id_t { LV_STYLE_GRID_CELL_Y_ALIGN = 136, LV_STYLE_IMAGE_COLORKEY = 137, - LV_STYLE_LAST_BUILT_IN_PROP = 138, + LV_STYLE_BLUR_RADIUS = 138, + LV_STYLE_BLUR_BACKDROP = 139, + LV_STYLE_BLUR_QUALITY = 140, + + LV_STYLE_LAST_BUILT_IN_PROP, 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 4f560d3fdc..3d5f105fd6 100644 --- a/src/misc/lv_style_gen.c +++ b/src/misc/lv_style_gen.c @@ -778,6 +778,30 @@ void lv_style_set_recolor_opa(lv_style_t * style, lv_opa_t value) lv_style_set_prop(style, LV_STYLE_RECOLOR_OPA, v); } +void lv_style_set_blur_radius(lv_style_t * style, int32_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_BLUR_RADIUS, v); +} + +void lv_style_set_blur_backdrop(lv_style_t * style, bool value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_BLUR_BACKDROP, v); +} + +void lv_style_set_blur_quality(lv_style_t * style, lv_blur_quality_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_BLUR_QUALITY, v); +} + void lv_style_set_anim(lv_style_t * style, const lv_anim_t * value) { lv_style_value_t v = { diff --git a/src/misc/lv_style_gen.h b/src/misc/lv_style_gen.h index 1bbded9e7e..0d68a78178 100644 --- a/src/misc/lv_style_gen.h +++ b/src/misc/lv_style_gen.h @@ -110,6 +110,9 @@ void lv_style_set_color_filter_dsc(lv_style_t * style, const lv_color_filter_dsc void lv_style_set_color_filter_opa(lv_style_t * style, lv_opa_t value); void lv_style_set_recolor(lv_style_t * style, lv_color_t value); void lv_style_set_recolor_opa(lv_style_t * style, lv_opa_t value); +void lv_style_set_blur_radius(lv_style_t * style, int32_t value); +void lv_style_set_blur_backdrop(lv_style_t * style, bool value); +void lv_style_set_blur_quality(lv_style_t * style, lv_blur_quality_t value); void lv_style_set_anim(lv_style_t * style, const lv_anim_t * value); void lv_style_set_anim_duration(lv_style_t * style, uint32_t value); void lv_style_set_transition(lv_style_t * style, const lv_style_transition_dsc_t * value); @@ -620,6 +623,21 @@ void lv_style_set_grid_cell_row_span(lv_style_t * style, int32_t value); .prop = LV_STYLE_RECOLOR_OPA, .value = { .num = (int32_t)val } \ } +#define LV_STYLE_CONST_BLUR_RADIUS(val) \ + { \ + .prop = LV_STYLE_BLUR_RADIUS, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_BLUR_BACKDROP(val) \ + { \ + .prop = LV_STYLE_BLUR_BACKDROP, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_BLUR_QUALITY(val) \ + { \ + .prop = LV_STYLE_BLUR_QUALITY, .value = { .num = (int32_t)val } \ + } + #define LV_STYLE_CONST_ANIM(val) \ { \ .prop = LV_STYLE_ANIM, .value = { .ptr = val } \ diff --git a/src/themes/default/lv_theme_default.c b/src/themes/default/lv_theme_default.c index 8db4f3f0b3..e0fed7f685 100644 --- a/src/themes/default/lv_theme_default.c +++ b/src/themes/default/lv_theme_default.c @@ -978,7 +978,6 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj) } else if(lv_obj_check_type(obj, &lv_dropdownlist_class)) { lv_obj_add_style(obj, &theme->styles.card, 0); - lv_obj_add_style(obj, &theme->styles.clip_corner, 0); lv_obj_add_style(obj, &theme->styles.line_space_large, 0); lv_obj_add_style(obj, &theme->styles.dropdown_list, 0); lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); @@ -1133,18 +1132,18 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj) return; } else if(lv_obj_check_type(obj, &lv_msgbox_header_class)) { - lv_obj_add_style(obj, &theme->styles.pad_tiny, 0); + lv_obj_add_style(obj, &theme->styles.pad_small, 0); lv_obj_add_style(obj, &theme->styles.bg_color_grey, 0); return; } else if(lv_obj_check_type(obj, &lv_msgbox_footer_class)) { - lv_obj_add_style(obj, &theme->styles.pad_tiny, 0); + lv_obj_add_style(obj, &theme->styles.pad_small, 0); return; } else if(lv_obj_check_type(obj, &lv_msgbox_content_class)) { lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); lv_obj_add_style(obj, &theme->styles.scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED); - lv_obj_add_style(obj, &theme->styles.pad_tiny, 0); + lv_obj_add_style(obj, &theme->styles.pad_small, 0); return; } else if(lv_obj_check_type(obj, &lv_msgbox_header_button_class) || diff --git a/src/widgets/bar/lv_bar.c b/src/widgets/bar/lv_bar.c index ae724e50bf..6e58b0cadb 100644 --- a/src/widgets/bar/lv_bar.c +++ b/src/widgets/bar/lv_bar.c @@ -509,10 +509,18 @@ static void draw_indic(lv_event_t * e) draw_rect_dsc.base.layer = layer; lv_obj_init_draw_rect_dsc(obj, LV_PART_INDICATOR, &draw_rect_dsc); + int32_t bg_radius = lv_obj_get_style_radius(obj, LV_PART_MAIN); int32_t short_side = LV_MIN(barw, barh); if(bg_radius > short_side >> 1) bg_radius = short_side >> 1; + bool backdrop_blur = lv_obj_get_style_blur_backdrop(obj, LV_PART_INDICATOR); + lv_draw_blur_dsc_t draw_blur_dsc; + lv_draw_blur_dsc_init(&draw_blur_dsc); + draw_blur_dsc.corner_radius = draw_rect_dsc.radius; + lv_obj_init_draw_blur_dsc(obj, LV_PART_INDICATOR, &draw_blur_dsc); + if(backdrop_blur) lv_draw_blur(layer, &draw_blur_dsc, &indic_area); + int32_t indic_radius = draw_rect_dsc.radius; short_side = LV_MIN(lv_area_get_width(&bar->indic_area), lv_area_get_height(&bar->indic_area)); if(indic_radius > short_side >> 1) indic_radius = short_side >> 1; @@ -556,12 +564,12 @@ static void draw_indic(lv_event_t * e) if(radius_issue || mask_needed) { if(!radius_issue) { /*Draw only the shadow*/ - lv_draw_rect_dsc_t draw_tmp_dsc = draw_rect_dsc; - draw_tmp_dsc.border_opa = 0; - draw_tmp_dsc.outline_opa = 0; - draw_tmp_dsc.bg_opa = 0; - draw_tmp_dsc.bg_image_opa = 0; - lv_draw_rect(layer, &draw_tmp_dsc, &indic_area); + lv_draw_rect_dsc_t draw_rect_tmp_dsc = draw_rect_dsc; + draw_rect_tmp_dsc.border_opa = 0; + draw_rect_tmp_dsc.outline_opa = 0; + draw_rect_tmp_dsc.bg_opa = 0; + draw_rect_tmp_dsc.bg_image_opa = 0; + lv_draw_rect(layer, &draw_rect_tmp_dsc, &indic_area); } else { draw_rect_dsc.border_opa = 0; @@ -617,10 +625,13 @@ static void draw_indic(lv_event_t * e) draw_tmp_dsc.bg_opa = 0; draw_tmp_dsc.bg_image_opa = 0; lv_draw_rect(layer, &draw_tmp_dsc, &indic_area); + } else { lv_draw_rect(layer, &draw_rect_dsc, &indic_area); } + + if(!backdrop_blur) lv_draw_blur(layer, &draw_blur_dsc, &indic_area); } static void lv_bar_event(const lv_obj_class_t * class_p, lv_event_t * e) diff --git a/src/widgets/property/lv_obj_property_names.h b/src/widgets/property/lv_obj_property_names.h index e8bb947589..1dc1762afc 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[75]; 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[121]; + extern const lv_property_name_t lv_style_property_names[123]; 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 88181de18d..eaec6f9e1e 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[121] = { +const lv_property_name_t lv_style_property_names[123] = { {"align", LV_PROPERTY_STYLE_ALIGN,}, {"anim", LV_PROPERTY_STYLE_ANIM,}, {"anim_duration", LV_PROPERTY_STYLE_ANIM_DURATION,}, @@ -40,6 +40,9 @@ const lv_property_name_t lv_style_property_names[121] = { {"bg_opa", LV_PROPERTY_STYLE_BG_OPA,}, {"bitmap_mask_src", LV_PROPERTY_STYLE_BITMAP_MASK_SRC,}, {"blend_mode", LV_PROPERTY_STYLE_BLEND_MODE,}, + {"blur_backdrop", LV_PROPERTY_STYLE_BLUR_BACKDROP,}, + {"blur_quality", LV_PROPERTY_STYLE_BLUR_QUALITY,}, + {"blur_radius", LV_PROPERTY_STYLE_BLUR_RADIUS,}, {"border_color", LV_PROPERTY_STYLE_BORDER_COLOR,}, {"border_opa", LV_PROPERTY_STYLE_BORDER_OPA,}, {"border_post", LV_PROPERTY_STYLE_BORDER_POST,}, @@ -68,7 +71,6 @@ const lv_property_name_t lv_style_property_names[121] = { {"image_opa", LV_PROPERTY_STYLE_IMAGE_OPA,}, {"image_recolor", LV_PROPERTY_STYLE_IMAGE_RECOLOR,}, {"image_recolor_opa", LV_PROPERTY_STYLE_IMAGE_RECOLOR_OPA,}, - {"last_built_in_prop", LV_PROPERTY_STYLE_LAST_BUILT_IN_PROP,}, {"layout", LV_PROPERTY_STYLE_LAYOUT,}, {"length", LV_PROPERTY_STYLE_LENGTH,}, {"line_color", LV_PROPERTY_STYLE_LINE_COLOR,}, diff --git a/src/widgets/property/lv_style_properties.h b/src/widgets/property/lv_style_properties.h index 4847032080..a0f3c00597 100644 --- a/src/widgets/property/lv_style_properties.h +++ b/src/widgets/property/lv_style_properties.h @@ -37,6 +37,9 @@ enum _lv_property_style_id_t { LV_PROPERTY_ID(STYLE, BG_OPA, LV_PROPERTY_TYPE_INT, LV_STYLE_BG_OPA), LV_PROPERTY_ID(STYLE, BITMAP_MASK_SRC, LV_PROPERTY_TYPE_POINTER, LV_STYLE_BITMAP_MASK_SRC), LV_PROPERTY_ID(STYLE, BLEND_MODE, LV_PROPERTY_TYPE_INT, LV_STYLE_BLEND_MODE), + LV_PROPERTY_ID(STYLE, BLUR_BACKDROP, LV_PROPERTY_TYPE_INT, LV_STYLE_BLUR_BACKDROP), + LV_PROPERTY_ID(STYLE, BLUR_QUALITY, LV_PROPERTY_TYPE_INT, LV_STYLE_BLUR_QUALITY), + LV_PROPERTY_ID(STYLE, BLUR_RADIUS, LV_PROPERTY_TYPE_INT, LV_STYLE_BLUR_RADIUS), LV_PROPERTY_ID(STYLE, BORDER_COLOR, LV_PROPERTY_TYPE_COLOR, LV_STYLE_BORDER_COLOR), LV_PROPERTY_ID(STYLE, BORDER_OPA, LV_PROPERTY_TYPE_INT, LV_STYLE_BORDER_OPA), LV_PROPERTY_ID(STYLE, BORDER_POST, LV_PROPERTY_TYPE_INT, LV_STYLE_BORDER_POST), @@ -65,7 +68,6 @@ enum _lv_property_style_id_t { LV_PROPERTY_ID(STYLE, IMAGE_OPA, LV_PROPERTY_TYPE_INT, LV_STYLE_IMAGE_OPA), LV_PROPERTY_ID(STYLE, IMAGE_RECOLOR, LV_PROPERTY_TYPE_COLOR, LV_STYLE_IMAGE_RECOLOR), LV_PROPERTY_ID(STYLE, IMAGE_RECOLOR_OPA, LV_PROPERTY_TYPE_INT, LV_STYLE_IMAGE_RECOLOR_OPA), - LV_PROPERTY_ID(STYLE, LAST_BUILT_IN_PROP, LV_PROPERTY_TYPE_INVALID, LV_STYLE_LAST_BUILT_IN_PROP), LV_PROPERTY_ID(STYLE, LAYOUT, LV_PROPERTY_TYPE_INT, LV_STYLE_LAYOUT), LV_PROPERTY_ID(STYLE, LENGTH, LV_PROPERTY_TYPE_INT, LV_STYLE_LENGTH), LV_PROPERTY_ID(STYLE, LINE_COLOR, LV_PROPERTY_TYPE_COLOR, LV_STYLE_LINE_COLOR), diff --git a/src/xml/lv_xml_base_types.c b/src/xml/lv_xml_base_types.c index 5c4a906c6c..4286349d04 100644 --- a/src/xml/lv_xml_base_types.c +++ b/src/xml/lv_xml_base_types.c @@ -238,6 +238,16 @@ lv_blend_mode_t lv_xml_blend_mode_to_enum(const char * txt) return 0; /*Return 0 in lack of a better option. */ } +lv_blur_quality_t lv_xml_blur_quality_to_enum(const char * txt) +{ + if(lv_streq("auto", txt)) return LV_BLUR_QUALITY_AUTO; + if(lv_streq("speed", txt)) return LV_BLUR_QUALITY_SPEED; + if(lv_streq("precision", txt)) return LV_BLUR_QUALITY_PRECISION; + + LV_LOG_WARN("%s is an unknown value for blur_quality", txt); + return 0; /*Return 0 in lack of a better option. */ +} + lv_event_code_t lv_xml_trigger_text_to_enum_value(const char * txt) { if(lv_streq("all", txt)) return LV_EVENT_ALL; diff --git a/src/xml/lv_xml_base_types.h b/src/xml/lv_xml_base_types.h index 5bfe941902..85a64e8bbb 100644 --- a/src/xml/lv_xml_base_types.h +++ b/src/xml/lv_xml_base_types.h @@ -139,6 +139,13 @@ lv_layout_t lv_xml_layout_to_enum(const char * txt); */ lv_blend_mode_t lv_xml_blend_mode_to_enum(const char * txt); +/** + * Convert a blur quality string to enum + * @param txt e.g. "auto" + * @return the related enum, e.g. `LV_BLUR_QUALITY_AUTO` + */ +lv_blur_quality_t lv_xml_blur_quality_to_enum(const char * txt); + /** * Convert an event trigger string to enum * @param txt e.g. "clicked" diff --git a/src/xml/lv_xml_style.c b/src/xml/lv_xml_style.c index d45b55a148..dd60a7ccf9 100644 --- a/src/xml/lv_xml_style.c +++ b/src/xml/lv_xml_style.c @@ -267,6 +267,10 @@ lv_result_t lv_xml_register_style(lv_xml_component_scope_t * scope, const char * else SET_STYLE_IF(recolor, lv_xml_to_color(value)); else SET_STYLE_IF(recolor_opa, lv_xml_to_opa(value)); + else SET_STYLE_IF(blur_radius, lv_xml_atoi(value)); + else SET_STYLE_IF(blur_backdrop, lv_xml_to_bool(value)); + else SET_STYLE_IF(blur_quality, lv_xml_blur_quality_to_enum(value)); + else SET_STYLE_IF(layout, lv_xml_layout_to_enum(value)); else SET_STYLE_IF(flex_flow, lv_xml_flex_flow_to_enum(value)); diff --git a/src/xml/parsers/lv_xml_obj_parser.c b/src/xml/parsers/lv_xml_obj_parser.c index 7bd497a671..f72c2b3ac8 100644 --- a/src/xml/parsers/lv_xml_obj_parser.c +++ b/src/xml/parsers/lv_xml_obj_parser.c @@ -953,6 +953,9 @@ static void apply_styles(lv_xml_parser_state_t * state, lv_obj_t * obj, const ch else SET_STYLE_IF(rotary_sensitivity, lv_xml_atoi(value)); else SET_STYLE_IF(recolor, lv_xml_to_color(value)); else SET_STYLE_IF(recolor_opa, lv_xml_to_opa(value)); + else SET_STYLE_IF(blur_radius, lv_xml_atoi(value)); + else SET_STYLE_IF(blur_backdrop, lv_xml_to_bool(value)); + else SET_STYLE_IF(blur_quality, lv_xml_blur_quality_to_enum(value)); else SET_STYLE_IF(layout, lv_xml_layout_to_enum(value)); diff --git a/tests/ref_imgs/draw/draw_blur_corner_0.png b/tests/ref_imgs/draw/draw_blur_corner_0.png new file mode 100644 index 0000000000..c0902a74fb Binary files /dev/null and b/tests/ref_imgs/draw/draw_blur_corner_0.png differ diff --git a/tests/ref_imgs/draw/draw_blur_corner_16.png b/tests/ref_imgs/draw/draw_blur_corner_16.png new file mode 100644 index 0000000000..adc50fa785 Binary files /dev/null and b/tests/ref_imgs/draw/draw_blur_corner_16.png differ diff --git a/tests/ref_imgs/draw/draw_blur_corner_32.png b/tests/ref_imgs/draw/draw_blur_corner_32.png new file mode 100644 index 0000000000..067113c9a3 Binary files /dev/null and b/tests/ref_imgs/draw/draw_blur_corner_32.png differ diff --git a/tests/ref_imgs/draw/draw_blur_corner_4.png b/tests/ref_imgs/draw/draw_blur_corner_4.png new file mode 100644 index 0000000000..b333253642 Binary files /dev/null and b/tests/ref_imgs/draw/draw_blur_corner_4.png differ diff --git a/tests/ref_imgs/theme_default_dark.png b/tests/ref_imgs/theme_default_dark.png index 39ba2db49f..c9ee220a4d 100644 Binary files a/tests/ref_imgs/theme_default_dark.png and b/tests/ref_imgs/theme_default_dark.png differ diff --git a/tests/ref_imgs/theme_default_light.png b/tests/ref_imgs/theme_default_light.png index 2a48238c04..36429ffaa4 100644 Binary files a/tests/ref_imgs/theme_default_light.png and b/tests/ref_imgs/theme_default_light.png differ diff --git a/tests/ref_imgs/widgets/dropdown_1.png b/tests/ref_imgs/widgets/dropdown_1.png index c774bbd000..bde5e83315 100644 Binary files a/tests/ref_imgs/widgets/dropdown_1.png and b/tests/ref_imgs/widgets/dropdown_1.png differ diff --git a/tests/ref_imgs/widgets/dropdown_2.png b/tests/ref_imgs/widgets/dropdown_2.png index c4b8573dd3..1db0f78cca 100644 Binary files a/tests/ref_imgs/widgets/dropdown_2.png and b/tests/ref_imgs/widgets/dropdown_2.png differ diff --git a/tests/ref_imgs/widgets/msgbox_ok_no_close_btn.png b/tests/ref_imgs/widgets/msgbox_ok_no_close_btn.png index 6da9cccdac..bb5384d13e 100644 Binary files a/tests/ref_imgs/widgets/msgbox_ok_no_close_btn.png and b/tests/ref_imgs/widgets/msgbox_ok_no_close_btn.png differ diff --git a/tests/ref_imgs/widgets/msgbox_ok_with_close_btn.png b/tests/ref_imgs/widgets/msgbox_ok_with_close_btn.png index 9489bf3098..59584b2f08 100644 Binary files a/tests/ref_imgs/widgets/msgbox_ok_with_close_btn.png and b/tests/ref_imgs/widgets/msgbox_ok_with_close_btn.png differ diff --git a/tests/ref_imgs/xml/lv_dropdown.png b/tests/ref_imgs/xml/lv_dropdown.png index bf89f7c9bc..4d3194244c 100644 Binary files a/tests/ref_imgs/xml/lv_dropdown.png and b/tests/ref_imgs/xml/lv_dropdown.png differ diff --git a/tests/ref_imgs_vg_lite/draw/draw_blur_corner_0.png b/tests/ref_imgs_vg_lite/draw/draw_blur_corner_0.png new file mode 100644 index 0000000000..563e22de4f Binary files /dev/null and b/tests/ref_imgs_vg_lite/draw/draw_blur_corner_0.png differ diff --git a/tests/ref_imgs_vg_lite/draw/draw_blur_corner_16.png b/tests/ref_imgs_vg_lite/draw/draw_blur_corner_16.png new file mode 100644 index 0000000000..7697ea16bb Binary files /dev/null and b/tests/ref_imgs_vg_lite/draw/draw_blur_corner_16.png differ diff --git a/tests/ref_imgs_vg_lite/draw/draw_blur_corner_32.png b/tests/ref_imgs_vg_lite/draw/draw_blur_corner_32.png new file mode 100644 index 0000000000..33f5b941c3 Binary files /dev/null and b/tests/ref_imgs_vg_lite/draw/draw_blur_corner_32.png differ diff --git a/tests/ref_imgs_vg_lite/draw/draw_blur_corner_4.png b/tests/ref_imgs_vg_lite/draw/draw_blur_corner_4.png new file mode 100644 index 0000000000..d436fae6ce Binary files /dev/null and b/tests/ref_imgs_vg_lite/draw/draw_blur_corner_4.png differ diff --git a/tests/ref_imgs_vg_lite/theme_default_dark.png b/tests/ref_imgs_vg_lite/theme_default_dark.png index 2396922e31..5d57a6a23a 100644 Binary files a/tests/ref_imgs_vg_lite/theme_default_dark.png and b/tests/ref_imgs_vg_lite/theme_default_dark.png differ diff --git a/tests/ref_imgs_vg_lite/theme_default_light.png b/tests/ref_imgs_vg_lite/theme_default_light.png index 6b8068c758..fb2284aa0a 100644 Binary files a/tests/ref_imgs_vg_lite/theme_default_light.png and b/tests/ref_imgs_vg_lite/theme_default_light.png differ diff --git a/tests/ref_imgs_vg_lite/widgets/dropdown_1.png b/tests/ref_imgs_vg_lite/widgets/dropdown_1.png index 3cbd05deff..45719eead7 100644 Binary files a/tests/ref_imgs_vg_lite/widgets/dropdown_1.png and b/tests/ref_imgs_vg_lite/widgets/dropdown_1.png differ diff --git a/tests/ref_imgs_vg_lite/widgets/dropdown_2.png b/tests/ref_imgs_vg_lite/widgets/dropdown_2.png index b350ec4cbb..3ded40ae97 100644 Binary files a/tests/ref_imgs_vg_lite/widgets/dropdown_2.png and b/tests/ref_imgs_vg_lite/widgets/dropdown_2.png differ diff --git a/tests/ref_imgs_vg_lite/widgets/msgbox_ok_no_close_btn.png b/tests/ref_imgs_vg_lite/widgets/msgbox_ok_no_close_btn.png index f921a36406..4a8fbf47ab 100644 Binary files a/tests/ref_imgs_vg_lite/widgets/msgbox_ok_no_close_btn.png and b/tests/ref_imgs_vg_lite/widgets/msgbox_ok_no_close_btn.png differ diff --git a/tests/ref_imgs_vg_lite/widgets/msgbox_ok_with_close_btn.png b/tests/ref_imgs_vg_lite/widgets/msgbox_ok_with_close_btn.png index 5da5836f0e..66279f35f6 100644 Binary files a/tests/ref_imgs_vg_lite/widgets/msgbox_ok_with_close_btn.png and b/tests/ref_imgs_vg_lite/widgets/msgbox_ok_with_close_btn.png differ diff --git a/tests/ref_imgs_vg_lite/xml/lv_dropdown.png b/tests/ref_imgs_vg_lite/xml/lv_dropdown.png index b3dce7077c..bc94db624f 100644 Binary files a/tests/ref_imgs_vg_lite/xml/lv_dropdown.png and b/tests/ref_imgs_vg_lite/xml/lv_dropdown.png differ diff --git a/tests/src/test_cases/draw/test_draw_blur.c b/tests/src/test_cases/draw/test_draw_blur.c new file mode 100644 index 0000000000..e9c6210806 --- /dev/null +++ b/tests/src/test_cases/draw/test_draw_blur.c @@ -0,0 +1,120 @@ +#if LV_BUILD_TEST +#include "../lvgl.h" +#include "../../lvgl_private.h" + + +#include "unity/unity.h" + +void setUp(void) +{ + /* Function run before every test */ + lv_obj_set_flex_flow(lv_screen_active(), LV_FLEX_FLOW_ROW_WRAP); + lv_obj_set_flex_align(lv_screen_active(), LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_SPACE_EVENLY); +} + +void tearDown(void) +{ + /* Function run after every test */ + lv_obj_clean(lv_screen_active()); +} + +#define CANVAS_WIDTH 180 +#define CANVAS_HEIGHT 100 + + +static void small_canvas_render(const char * name_sub, lv_color_format_t cf, void * canvas_buf, int32_t blur_radius, + uint32_t corner_radius) +{ + lv_obj_t * canvas = lv_canvas_create(lv_screen_active()); + lv_canvas_set_buffer(canvas, canvas_buf, CANVAS_WIDTH, CANVAS_HEIGHT, cf); + lv_canvas_fill_bg(canvas, lv_color_hex3(0xccc), LV_OPA_COVER); + + lv_layer_t layer; + lv_canvas_init_layer(canvas, &layer); + + /*A label in the background*/ + lv_draw_label_dsc_t label_dsc; + lv_draw_label_dsc_init(&label_dsc); + label_dsc.color = lv_palette_main(LV_PALETTE_RED); + label_dsc.font = &lv_font_montserrat_14; + label_dsc.decor = LV_TEXT_DECOR_UNDERLINE; + label_dsc.align = LV_TEXT_ALIGN_CENTER; + label_dsc.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " + "Curabitur sed velit sed neque tristique sagittis vel et sapien."; + + lv_area_t label1_coords = {10, 10, CANVAS_WIDTH - 10, CANVAS_HEIGHT - 10}; + lv_draw_label(&layer, &label_dsc, &label1_coords); + + /*Blur the middle of the canvas*/ + lv_draw_blur_dsc_t blur_dsc; + lv_draw_blur_dsc_init(&blur_dsc); + blur_dsc.corner_radius = corner_radius; + blur_dsc.blur_radius = blur_radius; + + lv_area_t fill_coords = {40, 20, CANVAS_WIDTH - 40, CANVAS_HEIGHT - 20}; + lv_draw_blur(&layer, &blur_dsc, &fill_coords); + + /*Draw a semi-transparent rectangle on the blurred area*/ + lv_draw_fill_dsc_t fill_dsc; + lv_draw_fill_dsc_init(&fill_dsc); + fill_dsc.color = lv_palette_lighten(LV_PALETTE_BLUE, 1); + fill_dsc.radius = corner_radius; + fill_dsc.opa = LV_OPA_30; + + lv_draw_fill(&layer, &fill_dsc, &fill_coords); + + /*Add label on the blurred area*/ + char buf[128]; + lv_snprintf(buf, sizeof(buf), "%s\nblur:%d", name_sub, blur_radius); + + lv_draw_label_dsc_init(&label_dsc); + label_dsc.color = lv_color_black(); + label_dsc.font = &lv_font_montserrat_14; + label_dsc.align = LV_TEXT_ALIGN_CENTER; + label_dsc.text = buf; + + lv_area_t label2_coords = {50, 30, CANVAS_WIDTH - 50, CANVAS_HEIGHT - 30}; + lv_draw_label(&layer, &label_dsc, &label2_coords); + + lv_canvas_finish_layer(canvas, &layer); +} + +void test_blur(void) +{ + static LV_ATTRIBUTE_MEM_ALIGN uint8_t canvas_buf[16][LV_TEST_WIDTH_TO_STRIDE(CANVAS_WIDTH, + 4) * CANVAS_HEIGHT + LV_DRAW_BUF_ALIGN]; + + uint32_t corner_radius_options[4] = {0, 4, 16, 32}; + uint32_t r; + + for(r = 0; r < 4; r++) { + lv_obj_clean(lv_screen_active()); + + uint32_t radius_current = corner_radius_options[r]; + small_canvas_render("rgb565", LV_COLOR_FORMAT_RGB565, canvas_buf[0], 6, radius_current); + small_canvas_render("rgb888", LV_COLOR_FORMAT_RGB888, canvas_buf[1], 6, radius_current); + small_canvas_render("xrgb8888", LV_COLOR_FORMAT_XRGB8888, canvas_buf[2], 6, radius_current); + small_canvas_render("argb8888", LV_COLOR_FORMAT_ARGB8888, canvas_buf[3], 6, radius_current); + + small_canvas_render("rgb565", LV_COLOR_FORMAT_RGB565, canvas_buf[4], 12, radius_current); + small_canvas_render("rgb888", LV_COLOR_FORMAT_RGB888, canvas_buf[5], 12, radius_current); + small_canvas_render("xrgb8888", LV_COLOR_FORMAT_XRGB8888, canvas_buf[6], 12, radius_current); + small_canvas_render("argb8888", LV_COLOR_FORMAT_ARGB8888, canvas_buf[7], 12, radius_current); + + small_canvas_render("rgb565", LV_COLOR_FORMAT_RGB565, canvas_buf[8], 24, radius_current); + small_canvas_render("rgb888", LV_COLOR_FORMAT_RGB888, canvas_buf[9], 24, radius_current); + small_canvas_render("xrgb8888", LV_COLOR_FORMAT_XRGB8888, canvas_buf[10], 24, radius_current); + small_canvas_render("argb8888", LV_COLOR_FORMAT_ARGB8888, canvas_buf[11], 24, radius_current); + + small_canvas_render("rgb565", LV_COLOR_FORMAT_RGB565, canvas_buf[12], 60, radius_current); + small_canvas_render("rgb888", LV_COLOR_FORMAT_RGB888, canvas_buf[13], 60, radius_current); + small_canvas_render("xrgb8888", LV_COLOR_FORMAT_XRGB8888, canvas_buf[14], 60, radius_current); + small_canvas_render("argb8888", LV_COLOR_FORMAT_ARGB8888, canvas_buf[15], 60, radius_current); + + char buf[128]; + lv_snprintf(buf, sizeof(buf), "draw/draw_blur_corner_%u.png", radius_current); + TEST_ASSERT_EQUAL_SCREENSHOT(buf); + } +} + +#endif diff --git a/tests/src/test_cases/test_style.c b/tests/src/test_cases/test_style.c index c2de69e339..5dcb9b735a 100644 --- a/tests/src/test_cases/test_style.c +++ b/tests/src/test_cases/test_style.c @@ -114,6 +114,8 @@ void test_style_replacement(void) TEST_ASSERT_EQUAL(false, replaced); TEST_ASSERT_EQUAL_COLOR(lv_color_hex(0x0000ff), lv_obj_get_style_bg_color(obj, LV_PART_MAIN)); + lv_obj_delete(obj); + lv_style_reset(&style_red); lv_style_reset(&style_blue); } @@ -206,7 +208,8 @@ void test_style_has_prop(void) lv_style_set_outline_color(&style, lv_color_white()); /*Create object with style*/ - lv_obj_t * obj = lv_obj_create(lv_screen_active()); + lv_obj_t * obj = + lv_obj_create(lv_screen_active()); TEST_ASSERT_EQUAL(false, lv_obj_has_style_prop(obj, LV_PART_MAIN, LV_STYLE_OUTLINE_COLOR)); TEST_ASSERT_EQUAL(false, lv_obj_has_style_prop(obj, LV_PART_MAIN, LV_STYLE_OUTLINE_WIDTH)); @@ -219,6 +222,7 @@ void test_style_has_prop(void) TEST_ASSERT_EQUAL(true, lv_obj_has_style_prop(obj, LV_PART_MAIN, LV_STYLE_OUTLINE_WIDTH)); TEST_ASSERT_EQUAL(false, lv_obj_has_style_prop(obj, LV_PART_INDICATOR, LV_STYLE_OUTLINE_COLOR)); + lv_obj_delete(obj); lv_style_reset(&style); } diff --git a/xmls/globals.xml b/xmls/globals.xml index c3cb3b46ac..2e6935179c 100644 --- a/xmls/globals.xml +++ b/xmls/globals.xml @@ -388,6 +388,10 @@ + + + + diff --git a/xmls/lv_obj.xml b/xmls/lv_obj.xml index ba203966b3..f51fe43695 100644 --- a/xmls/lv_obj.xml +++ b/xmls/lv_obj.xml @@ -342,6 +342,9 @@ Example + + +