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
+
+
+ - Default `0`
+ - Inherited No
+ - Layout No
+ - Ext. draw No
+
+
+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
+
+
+ - Default `false`
+ - Inherited No
+ - Layout No
+ - Ext. draw No
+
+
+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
+
+
+ - Default `LV_BLUR_QUALITY_AUTO`
+ - Inherited No
+ - Layout No
+ - Ext. draw No
+
+
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
+
+
+