diff --git a/docs/overview/layer.rst b/docs/overview/layer.rst index a5e81672a..5c4099631 100644 --- a/docs/overview/layer.rst +++ b/docs/overview/layer.rst @@ -4,6 +4,12 @@ Layers ====== +In LVGL "layers" can be interpreted in various ways: + +1. The order of widget creation naturally creates a layering of widgets +2. Permanent screen-sized layers can be also used +3. For some draw operations LVGL renders a widget and all its children into a buffer (a.k.a. layer) first + .. _layers_creation: Order of creation @@ -45,7 +51,7 @@ its children. .. _layers_order: Change order -************ +------------ There are four explicit ways to bring an object to the foreground: @@ -60,10 +66,12 @@ There are four explicit ways to bring an object to the foreground: - Use :cpp:expr:`lv_obj_swap(obj1, obj2)` to swap the relative layer position of two objects. - When :cpp:expr:`lv_obj_set_parent(obj, new_parent)` is used, ``obj`` will be on the foreground of the ``new_parent``. +Screen-like layers +****************** .. _layers_top_and_sys: Top and sys layers -****************** +------------------ LVGL uses two special layers named ``layer_top`` and ``layer_sys``. Both are visible and common on all screens of a display. **They are not, @@ -92,7 +100,7 @@ always visible. .. _layers_bottom: Bottom layers -************* +------------- Similarly top and sys. layer bottom layer is also screen size but it's located below the active screen. It's visible only if the active screen's @@ -100,6 +108,45 @@ background opacity is < 255. The get the bottom layer use :cpp:func:`lv_layer_bottom`. +Draw layers +*********** + +Some style properties make LVGL to allocate a buffer and render a widget and its children there first. Later that layer will be merged to the screen or its parent layer after applying some transformations or other modifications. + +Simple layer +------------ + +The following style properties trigger the creation of a "Simple layer": + +- ``opa_layered`` +- ``bitmap_mask_src`` +- ``blend_mode`` + + +In this case widget will be sliced into ``LV_DRAW_SW_LAYER_SIMPLE_BUF_SIZE`` sized chunks. + +If there is no memory for a new chunk, LVGL will try allocating layer when an other chunk is rendered and freed. + + +Transformed layer +--------------- + +When the widget is transformed a larger part of the widget needs to rendered to provide enough data for transformation. LVGL tries to render as small area of the widget as possible, but due to the nature of transformations no slicing is possible in this case. + + +The following style properties trigger the creation of a "Transform layer": + +- ``transform_scale_x`` +- ``transform_scale_y`` +- ``transform_skew_x`` +- ``transform_skew_y`` +- ``transform_rotate`` + +Clip corner +----------- + +The ``clip_corner`` style property also makes LVGL to create a 2 layers with radius height for the top and bottom part of the widget. + .. _layers_api: API diff --git a/docs/overview/style-props.md b/docs/overview/style-props.md index c7a90882b..5b8e51b35 100644 --- a/docs/overview/style-props.md +++ b/docs/overview/style-props.md @@ -873,6 +873,15 @@ Set the base direction of the object. The possible values are `LV_BIDI_DIR_LTR/R
  • Ext. draw No
  • +### bitmap_mask_src +If set a layer will be created for the widget and the layer will be masked with this A8 bitmap mask. + + ## Flex Flex layout properties. diff --git a/examples/widgets/image/lv_example_image_1.c b/examples/widgets/image/lv_example_image_1.c index 5b9ebd853..0e2bd55e1 100644 --- a/examples/widgets/image/lv_example_image_1.c +++ b/examples/widgets/image/lv_example_image_1.c @@ -7,15 +7,10 @@ void lv_example_image_1(void) lv_obj_t * img1 = lv_image_create(lv_screen_active()); lv_image_set_src(img1, &img_cogwheel_argb); lv_obj_align(img1, LV_ALIGN_CENTER, 0, 0); - lv_image_set_scale_x(img1, 512); - lv_image_set_scale_y(img1, 128); - lv_image_set_rotation(img1, 10); lv_obj_t * img2 = lv_image_create(lv_screen_active()); lv_image_set_src(img2, LV_SYMBOL_OK "Accept"); lv_obj_align_to(img2, img1, LV_ALIGN_OUT_BOTTOM_MID, 0, 20); - - lv_obj_set_style_bg_opa(img1, 100, 0); } #endif diff --git a/scripts/style_api_gen.py b/scripts/style_api_gen.py index bd1612271..ad651051f 100755 --- a/scripts/style_api_gen.py +++ b/scripts/style_api_gen.py @@ -389,6 +389,9 @@ props = [ 'style_type': 'num', 'var_type': 'lv_base_dir_t', 'default':'`LV_BASE_DIR_AUTO`', 'inherited': 1, 'layout': 1, 'ext_draw': 0, 'dsc': "Set the base direction of the object. The possible values are `LV_BIDI_DIR_LTR/RTL/AUTO`."}, +{'name': 'BITMAP_MASK_SRC', + 'style_type': 'ptr', 'var_type': 'const lv_image_dsc_t *', 'default':'`NULL`', 'inherited': 0, 'layout': 0, 'ext_draw': 0, + 'dsc': "If set a layer will be created for the widget and the layer will be masked with this A8 bitmap mask."}, {'section': 'Flex', 'dsc':'Flex layout properties.', 'guard':'LV_USE_FLEX'}, diff --git a/src/core/lv_obj_style.c b/src/core/lv_obj_style.c index 03d51c8e9..48681e52b 100644 --- a/src/core/lv_obj_style.c +++ b/src/core/lv_obj_style.c @@ -1016,6 +1016,7 @@ static lv_layer_type_t calculate_layer_type(lv_obj_t * obj) if(lv_obj_get_style_transform_skew_x(obj, 0) != 0) return LV_LAYER_TYPE_TRANSFORM; if(lv_obj_get_style_transform_skew_y(obj, 0) != 0) return LV_LAYER_TYPE_TRANSFORM; if(lv_obj_get_style_opa_layered(obj, 0) != LV_OPA_COVER) return LV_LAYER_TYPE_SIMPLE; + if(lv_obj_get_style_bitmap_mask_src(obj, 0) != NULL) return LV_LAYER_TYPE_SIMPLE; if(lv_obj_get_style_blend_mode(obj, 0) != LV_BLEND_MODE_NORMAL) return LV_LAYER_TYPE_SIMPLE; return LV_LAYER_TYPE_NONE; } diff --git a/src/core/lv_obj_style_gen.c b/src/core/lv_obj_style_gen.c index 489962205..6e7a63c84 100644 --- a/src/core/lv_obj_style_gen.c +++ b/src/core/lv_obj_style_gen.c @@ -690,8 +690,7 @@ 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 @@ -754,6 +753,14 @@ void lv_obj_set_style_base_dir(lv_obj_t * obj, lv_base_dir_t value, lv_style_sel }; lv_obj_set_local_style_prop(obj, LV_STYLE_BASE_DIR, v, selector); } + +void lv_obj_set_style_bitmap_mask_src(lv_obj_t * obj, const lv_image_dsc_t * value, lv_style_selector_t selector) +{ + lv_style_value_t v = { + .ptr = value + }; + lv_obj_set_local_style_prop(obj, LV_STYLE_BITMAP_MASK_SRC, v, selector); +} #if LV_USE_FLEX diff --git a/src/core/lv_obj_style_gen.h b/src/core/lv_obj_style_gen.h index a8d67e837..7f8eb6a08 100644 --- a/src/core/lv_obj_style_gen.h +++ b/src/core/lv_obj_style_gen.h @@ -6,6 +6,7 @@ ********************************************************************** */ + #ifndef LV_OBJ_STYLE_GEN_H #define LV_OBJ_STYLE_GEN_H @@ -225,8 +226,7 @@ static inline lv_color_t lv_obj_get_style_bg_grad_color(const lv_obj_t * obj, ui static inline lv_color_t lv_obj_get_style_bg_grad_color_filtered(const lv_obj_t * obj, uint32_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; } @@ -286,8 +286,7 @@ 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, uint32_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; } @@ -311,8 +310,7 @@ static inline lv_color_t lv_obj_get_style_border_color(const lv_obj_t * obj, uin static inline lv_color_t lv_obj_get_style_border_color_filtered(const lv_obj_t * obj, uint32_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; } @@ -354,8 +352,7 @@ static inline lv_color_t lv_obj_get_style_outline_color(const lv_obj_t * obj, ui static inline lv_color_t lv_obj_get_style_outline_color_filtered(const lv_obj_t * obj, uint32_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; } @@ -403,8 +400,7 @@ static inline lv_color_t lv_obj_get_style_shadow_color(const lv_obj_t * obj, uin static inline lv_color_t lv_obj_get_style_shadow_color_filtered(const lv_obj_t * obj, uint32_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; } @@ -428,8 +424,7 @@ static inline lv_color_t lv_obj_get_style_image_recolor(const lv_obj_t * obj, ui static inline lv_color_t lv_obj_get_style_image_recolor_filtered(const lv_obj_t * obj, uint32_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; } @@ -637,6 +632,12 @@ static inline lv_base_dir_t lv_obj_get_style_base_dir(const lv_obj_t * obj, uint return (lv_base_dir_t)v.num; } +static inline const lv_image_dsc_t * lv_obj_get_style_bitmap_mask_src(const lv_obj_t * obj, uint32_t part) +{ + lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BITMAP_MASK_SRC); + return (const lv_image_dsc_t *)v.ptr; +} + #if LV_USE_FLEX static inline lv_flex_flow_t lv_obj_get_style_flex_flow(const lv_obj_t * obj, uint32_t part) @@ -820,8 +821,7 @@ void lv_obj_set_style_radius(lv_obj_t * obj, int32_t value, lv_style_selector_t 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_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); @@ -829,27 +829,29 @@ void lv_obj_set_style_transition(lv_obj_t * obj, const lv_style_transition_dsc_t void lv_obj_set_style_blend_mode(lv_obj_t * obj, lv_blend_mode_t value, lv_style_selector_t selector); void lv_obj_set_style_layout(lv_obj_t * obj, uint16_t value, lv_style_selector_t selector); void lv_obj_set_style_base_dir(lv_obj_t * obj, lv_base_dir_t value, lv_style_selector_t selector); +void lv_obj_set_style_bitmap_mask_src(lv_obj_t * obj, const lv_image_dsc_t * value, lv_style_selector_t selector); #if LV_USE_FLEX - void lv_obj_set_style_flex_flow(lv_obj_t * obj, lv_flex_flow_t value, lv_style_selector_t selector); - void lv_obj_set_style_flex_main_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector); - void lv_obj_set_style_flex_cross_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector); - void lv_obj_set_style_flex_track_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector); - void lv_obj_set_style_flex_grow(lv_obj_t * obj, uint8_t value, lv_style_selector_t selector); +void lv_obj_set_style_flex_flow(lv_obj_t * obj, lv_flex_flow_t value, lv_style_selector_t selector); +void lv_obj_set_style_flex_main_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector); +void lv_obj_set_style_flex_cross_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector); +void lv_obj_set_style_flex_track_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector); +void lv_obj_set_style_flex_grow(lv_obj_t * obj, uint8_t value, lv_style_selector_t selector); #endif /*LV_USE_FLEX*/ #if LV_USE_GRID - void lv_obj_set_style_grid_column_dsc_array(lv_obj_t * obj, const int32_t * value, lv_style_selector_t selector); - void lv_obj_set_style_grid_column_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector); - void lv_obj_set_style_grid_row_dsc_array(lv_obj_t * obj, const int32_t * value, lv_style_selector_t selector); - void lv_obj_set_style_grid_row_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector); - void lv_obj_set_style_grid_cell_column_pos(lv_obj_t * obj, int32_t value, lv_style_selector_t selector); - void lv_obj_set_style_grid_cell_x_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector); - void lv_obj_set_style_grid_cell_column_span(lv_obj_t * obj, int32_t value, lv_style_selector_t selector); - void lv_obj_set_style_grid_cell_row_pos(lv_obj_t * obj, int32_t value, lv_style_selector_t selector); - void lv_obj_set_style_grid_cell_y_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector); - void lv_obj_set_style_grid_cell_row_span(lv_obj_t * obj, int32_t value, lv_style_selector_t selector); +void lv_obj_set_style_grid_column_dsc_array(lv_obj_t * obj, const int32_t * value, lv_style_selector_t selector); +void lv_obj_set_style_grid_column_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector); +void lv_obj_set_style_grid_row_dsc_array(lv_obj_t * obj, const int32_t * value, lv_style_selector_t selector); +void lv_obj_set_style_grid_row_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector); +void lv_obj_set_style_grid_cell_column_pos(lv_obj_t * obj, int32_t value, lv_style_selector_t selector); +void lv_obj_set_style_grid_cell_x_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector); +void lv_obj_set_style_grid_cell_column_span(lv_obj_t * obj, int32_t value, lv_style_selector_t selector); +void lv_obj_set_style_grid_cell_row_pos(lv_obj_t * obj, int32_t value, lv_style_selector_t selector); +void lv_obj_set_style_grid_cell_y_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector); +void lv_obj_set_style_grid_cell_row_span(lv_obj_t * obj, int32_t value, lv_style_selector_t selector); #endif /*LV_USE_GRID*/ + #endif /* LV_OBJ_STYLE_GEN_H */ diff --git a/src/core/lv_refr.c b/src/core/lv_refr.c index d61e0425b..ad7fd78a6 100644 --- a/src/core/lv_refr.c +++ b/src/core/lv_refr.c @@ -820,18 +820,17 @@ static void refr_obj_and_children(lv_layer_t * layer, lv_obj_t * top_obj) } static lv_result_t layer_get_area(lv_layer_t * layer, lv_obj_t * obj, lv_layer_type_t layer_type, - lv_area_t * layer_area_out) + lv_area_t * layer_area_out, lv_area_t * obj_draw_size_out) { int32_t ext_draw_size = _lv_obj_get_ext_draw_size(obj); - lv_area_t obj_coords_ext; - lv_obj_get_coords(obj, &obj_coords_ext); - lv_area_increase(&obj_coords_ext, ext_draw_size, ext_draw_size); + lv_obj_get_coords(obj, obj_draw_size_out); + lv_area_increase(obj_draw_size_out, ext_draw_size, ext_draw_size); if(layer_type == LV_LAYER_TYPE_TRANSFORM) { /*Get the transformed area and clip it to the current clip area. *This area needs to be updated on the screen.*/ lv_area_t clip_coords_for_obj; - lv_area_t tranf_coords = obj_coords_ext; + lv_area_t tranf_coords = *obj_draw_size_out; lv_obj_get_transformed_area(obj, &tranf_coords, false, false); if(!_lv_area_intersect(&clip_coords_for_obj, &layer->_clip_area, &tranf_coords)) { return LV_RESULT_INVALID; @@ -842,7 +841,7 @@ static lv_result_t layer_get_area(lv_layer_t * layer, lv_obj_t * obj, lv_layer_t *in order to cover transformed area after transformation.*/ lv_area_t inverse_clip_coords_for_obj = clip_coords_for_obj; lv_obj_get_transformed_area(obj, &inverse_clip_coords_for_obj, false, true); - if(!_lv_area_intersect(&inverse_clip_coords_for_obj, &inverse_clip_coords_for_obj, &obj_coords_ext)) { + if(!_lv_area_intersect(&inverse_clip_coords_for_obj, &inverse_clip_coords_for_obj, obj_draw_size_out)) { return LV_RESULT_INVALID; } @@ -851,7 +850,7 @@ static lv_result_t layer_get_area(lv_layer_t * layer, lv_obj_t * obj, lv_layer_t } else if(layer_type == LV_LAYER_TYPE_SIMPLE) { lv_area_t clip_coords_for_obj; - if(!_lv_area_intersect(&clip_coords_for_obj, &layer->_clip_area, &obj_coords_ext)) { + if(!_lv_area_intersect(&clip_coords_for_obj, &layer->_clip_area, obj_draw_size_out)) { return LV_RESULT_INVALID; } *layer_area_out = clip_coords_for_obj; @@ -891,7 +890,8 @@ void refr_obj(lv_layer_t * layer, lv_obj_t * obj) if(opa < LV_OPA_MIN) return; lv_area_t layer_area_full; - lv_result_t res = layer_get_area(layer, obj, layer_type, &layer_area_full); + lv_area_t obj_draw_size; + lv_result_t res = layer_get_area(layer, obj, layer_type, &layer_area_full, &obj_draw_size); if(res != LV_RESULT_OK) return; /*Simple layers can be subdivied into smaller layers*/ @@ -940,6 +940,8 @@ void refr_obj(lv_layer_t * layer, lv_obj_t * obj) layer_draw_dsc.skew_y = lv_obj_get_style_transform_skew_y(obj, 0); layer_draw_dsc.blend_mode = lv_obj_get_style_blend_mode(obj, 0); layer_draw_dsc.antialias = disp_refr->antialiasing; + layer_draw_dsc.bitmap_mask_src = lv_obj_get_style_bitmap_mask_src(obj, 0); + layer_draw_dsc.original_area = obj_draw_size; layer_draw_dsc.src = new_layer; lv_draw_layer(layer, &layer_draw_dsc, &layer_area_act); diff --git a/src/draw/lv_draw_image.c b/src/draw/lv_draw_image.c index aafde5603..1924b9659 100644 --- a/src/draw/lv_draw_image.c +++ b/src/draw/lv_draw_image.c @@ -51,6 +51,7 @@ void lv_draw_image_dsc_init(lv_draw_image_dsc_t * dsc) dsc->scale_x = LV_SCALE_NONE; dsc->scale_y = LV_SCALE_NONE; dsc->antialias = LV_COLOR_DEPTH > 8 ? 1 : 0; + dsc->original_area.x2 = LV_COORD_MIN; /*Indicate invalid area by default by setting a negative size*/ dsc->base.dsc_size = sizeof(lv_draw_image_dsc_t); } diff --git a/src/draw/lv_draw_image.h b/src/draw/lv_draw_image.h index 8a53ae2be..e469527c0 100644 --- a/src/draw/lv_draw_image.h +++ b/src/draw/lv_draw_image.h @@ -55,9 +55,14 @@ typedef struct _lv_draw_image_dsc_t { lv_opa_t opa; lv_blend_mode_t blend_mode : 4; - uint16_t antialias : 1; - uint16_t tile : 1; + uint16_t antialias : 1; + uint16_t tile : 1; lv_draw_image_sup_t * sup; + + /** Might be used to indicate the original size of the image if only a small portion is rendered now. + * Used when a part of a layer is rendered to show the total layer size*/ + lv_area_t original_area; + const lv_image_dsc_t * bitmap_mask_src; } lv_draw_image_dsc_t; /** diff --git a/src/draw/sw/blend/lv_draw_sw_blend.c b/src/draw/sw/blend/lv_draw_sw_blend.c index 0cdbca5a2..9ceee3b1c 100644 --- a/src/draw/sw/blend/lv_draw_sw_blend.c +++ b/src/draw/sw/blend/lv_draw_sw_blend.c @@ -93,6 +93,11 @@ void lv_draw_sw_blend(lv_draw_unit_t * draw_unit, const lv_draw_sw_blend_dsc_t * return; } + if(blend_dsc->mask_area && !_lv_area_intersect(&blend_area, &blend_area, blend_dsc->mask_area)) { + LV_PROFILER_END; + return; + } + _lv_draw_sw_blend_image_dsc_t image_dsc; image_dsc.dest_w = lv_area_get_width(&blend_area); image_dsc.dest_h = lv_area_get_height(&blend_area); diff --git a/src/draw/sw/lv_draw_sw.c b/src/draw/sw/lv_draw_sw.c index 91ca1c9c8..027c17434 100644 --- a/src/draw/sw/lv_draw_sw.c +++ b/src/draw/sw/lv_draw_sw.c @@ -264,6 +264,17 @@ static int32_t evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task) if(draw_dsc->skew_x != 0 || draw_dsc->skew_y != 0) { return 0; } + + bool transformed = draw_dsc->rotation != 0 || draw_dsc->scale_x != LV_SCALE_NONE || + draw_dsc->scale_y != LV_SCALE_NONE ? true : false; + + bool masked = draw_dsc->bitmap_mask_src != NULL; + if(masked && transformed) return 0; + + lv_color_format_t cf = draw_dsc->header.cf; + if(masked && (cf == LV_COLOR_FORMAT_A8 || cf == LV_COLOR_FORMAT_RGB565A8)) { + return 0; + } } break; default: diff --git a/src/draw/sw/lv_draw_sw_img.c b/src/draw/sw/lv_draw_sw_img.c index c5518e88c..2f6dcf34a 100644 --- a/src/draw/sw/lv_draw_sw_img.c +++ b/src/draw/sw/lv_draw_sw_img.c @@ -179,6 +179,8 @@ static void img_draw_core(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t bool transformed = draw_dsc->rotation != 0 || draw_dsc->scale_x != LV_SCALE_NONE || draw_dsc->scale_y != LV_SCALE_NONE ? true : false; + bool masked = draw_dsc->bitmap_mask_src != NULL; + lv_draw_sw_blend_dsc_t blend_dsc; const lv_draw_buf_t * decoded = decoder_dsc->decoded; const uint8_t * src_buf = decoded->data; @@ -191,7 +193,7 @@ static void img_draw_core(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t blend_dsc.blend_mode = draw_dsc->blend_mode; blend_dsc.src_stride = img_stride; - if(!transformed && cf == LV_COLOR_FORMAT_A8) { + if(!transformed && !masked && cf == LV_COLOR_FORMAT_A8) { lv_area_t clipped_coords; if(!_lv_area_intersect(&clipped_coords, img_coords, draw_unit->clip_area)) return; @@ -205,7 +207,7 @@ static void img_draw_core(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t blend_dsc.blend_area = img_coords; lv_draw_sw_blend(draw_unit, &blend_dsc); } - else if(!transformed && cf == LV_COLOR_FORMAT_RGB565A8 && draw_dsc->recolor_opa <= LV_OPA_MIN) { + else if(!transformed && !masked && cf == LV_COLOR_FORMAT_RGB565A8 && draw_dsc->recolor_opa <= LV_OPA_MIN) { int32_t src_h = lv_area_get_height(img_coords); int32_t src_w = lv_area_get_width(img_coords); blend_dsc.src_area = img_coords; @@ -225,14 +227,33 @@ static void img_draw_core(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t lv_draw_sw_blend(draw_unit, &blend_dsc); } /*The simplest case just copy the pixels into the draw_buf. Blending will convert the colors if needed*/ - else if(!transformed && draw_dsc->recolor_opa <= LV_OPA_MIN) { + else if(!transformed && !masked && draw_dsc->recolor_opa <= LV_OPA_MIN) { blend_dsc.src_area = img_coords; blend_dsc.src_buf = src_buf; blend_dsc.blend_area = img_coords; blend_dsc.src_color_format = cf; lv_draw_sw_blend(draw_unit, &blend_dsc); } - /* check whethr it is possible to accelerate the operation in synchronouse mode */ + /*Handle masked RGB565, RGB888, XRGB888, or ARGB8888 images*/ + else if(!transformed && masked && draw_dsc->recolor_opa <= LV_OPA_MIN) { + blend_dsc.src_area = img_coords; + blend_dsc.src_buf = src_buf; + blend_dsc.blend_area = img_coords; + blend_dsc.src_color_format = cf; + blend_dsc.mask_buf = draw_dsc->bitmap_mask_src->data; + blend_dsc.mask_stride = draw_dsc->bitmap_mask_src->header.stride; + + const lv_area_t * original_area; + if(lv_area_get_width(&draw_dsc->original_area) < 0) original_area = img_coords; + else original_area = &draw_dsc->original_area; + + lv_area_t a = {0, 0, draw_dsc->bitmap_mask_src->header.w - 1, draw_dsc->bitmap_mask_src->header.h - 1}; + lv_area_align(original_area, &a, LV_ALIGN_CENTER, 0, 0); + blend_dsc.mask_area = &a; + blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED; + lv_draw_sw_blend(draw_unit, &blend_dsc); + } + /* check whether it is possible to accelerate the operation in synchronouse mode */ else if(LV_RESULT_INVALID == LV_DRAW_SW_IMAGE(transformed, /* whether require transform */ cf, /* image format */ src_buf, /* image buffer */ diff --git a/src/misc/lv_style.c b/src/misc/lv_style.c index 276746160..adfb2521c 100644 --- a/src/misc/lv_style.c +++ b/src/misc/lv_style.c @@ -136,6 +136,7 @@ const uint8_t _lv_style_builtin_prop_flag_lookup_table[_LV_STYLE_NUM_BUILT_IN_PR [LV_STYLE_BLEND_MODE] = LV_STYLE_PROP_FLAG_LAYER_UPDATE, [LV_STYLE_LAYOUT] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE, [LV_STYLE_BASE_DIR] = LV_STYLE_PROP_FLAG_INHERITABLE | LV_STYLE_PROP_FLAG_LAYOUT_UPDATE, + [LV_STYLE_BITMAP_MASK_SRC] = LV_STYLE_PROP_FLAG_LAYER_UPDATE, #if LV_USE_FLEX [LV_STYLE_FLEX_FLOW] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE, diff --git a/src/misc/lv_style.h b/src/misc/lv_style.h index 7e5ed75be..86077987c 100644 --- a/src/misc/lv_style.h +++ b/src/misc/lv_style.h @@ -299,28 +299,30 @@ enum _lv_style_prop_t { LV_STYLE_TRANSFORM_SKEW_X = 113, LV_STYLE_TRANSFORM_SKEW_Y = 114, + LV_STYLE_BITMAP_MASK_SRC = 115, + #if LV_USE_FLEX - LV_STYLE_FLEX_FLOW = 115, - LV_STYLE_FLEX_MAIN_PLACE = 116, - LV_STYLE_FLEX_CROSS_PLACE = 117, - LV_STYLE_FLEX_TRACK_PLACE = 118, - LV_STYLE_FLEX_GROW = 119, + LV_STYLE_FLEX_FLOW = 125, + LV_STYLE_FLEX_MAIN_PLACE = 126, + LV_STYLE_FLEX_CROSS_PLACE = 127, + LV_STYLE_FLEX_TRACK_PLACE = 128, + LV_STYLE_FLEX_GROW = 129, #endif #if LV_USE_GRID - LV_STYLE_GRID_COLUMN_ALIGN = 120, - LV_STYLE_GRID_ROW_ALIGN = 121, - LV_STYLE_GRID_ROW_DSC_ARRAY = 122, - LV_STYLE_GRID_COLUMN_DSC_ARRAY = 123, - LV_STYLE_GRID_CELL_COLUMN_POS = 124, - LV_STYLE_GRID_CELL_COLUMN_SPAN = 125, - LV_STYLE_GRID_CELL_X_ALIGN = 126, - LV_STYLE_GRID_CELL_ROW_POS = 127, - LV_STYLE_GRID_CELL_ROW_SPAN = 128, - LV_STYLE_GRID_CELL_Y_ALIGN = 129, + LV_STYLE_GRID_COLUMN_ALIGN = 130, + LV_STYLE_GRID_ROW_ALIGN = 131, + LV_STYLE_GRID_ROW_DSC_ARRAY = 132, + LV_STYLE_GRID_COLUMN_DSC_ARRAY = 133, + LV_STYLE_GRID_CELL_COLUMN_POS = 134, + LV_STYLE_GRID_CELL_COLUMN_SPAN = 135, + LV_STYLE_GRID_CELL_X_ALIGN = 136, + LV_STYLE_GRID_CELL_ROW_POS = 137, + LV_STYLE_GRID_CELL_ROW_SPAN = 138, + LV_STYLE_GRID_CELL_Y_ALIGN = 139, #endif - _LV_STYLE_LAST_BUILT_IN_PROP = 130, + _LV_STYLE_LAST_BUILT_IN_PROP = 140, _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 c0aacab0b..3ecfde585 100644 --- a/src/misc/lv_style_gen.c +++ b/src/misc/lv_style_gen.c @@ -937,6 +937,16 @@ void lv_style_set_base_dir(lv_style_t * style, lv_base_dir_t value) } const lv_style_prop_t _lv_style_const_prop_id_BASE_DIR = LV_STYLE_BASE_DIR; + +void lv_style_set_bitmap_mask_src(lv_style_t * style, const lv_image_dsc_t * value) +{ + lv_style_value_t v = { + .ptr = value + }; + lv_style_set_prop(style, LV_STYLE_BITMAP_MASK_SRC, v); +} + +const lv_style_prop_t _lv_style_const_prop_id_BITMAP_MASK_SRC = LV_STYLE_BITMAP_MASK_SRC; #if LV_USE_FLEX void lv_style_set_flex_flow(lv_style_t * style, lv_flex_flow_t value) diff --git a/src/misc/lv_style_gen.h b/src/misc/lv_style_gen.h index f02553f87..b66c17436 100644 --- a/src/misc/lv_style_gen.h +++ b/src/misc/lv_style_gen.h @@ -195,6 +195,8 @@ void lv_style_set_layout(lv_style_t * style, uint16_t value); LV_ATTRIBUTE_EXTERN_DATA extern const lv_style_prop_t _lv_style_const_prop_id_LAYOUT; void lv_style_set_base_dir(lv_style_t * style, lv_base_dir_t value); LV_ATTRIBUTE_EXTERN_DATA extern const lv_style_prop_t _lv_style_const_prop_id_BASE_DIR; +void lv_style_set_bitmap_mask_src(lv_style_t * style, const lv_image_dsc_t * value); +LV_ATTRIBUTE_EXTERN_DATA extern const lv_style_prop_t _lv_style_const_prop_id_BITMAP_MASK_SRC; #if LV_USE_FLEX void lv_style_set_flex_flow(lv_style_t * style, lv_flex_flow_t value); @@ -697,6 +699,11 @@ LV_ATTRIBUTE_EXTERN_DATA extern const lv_style_prop_t _lv_style_const_prop_id_BA { \ .prop_ptr = &_lv_style_const_prop_id_BASE_DIR, .value = { .num = (int32_t)val } \ } + +#define LV_STYLE_CONST_BITMAP_MASK_SRC(val) \ + { \ + .prop_ptr = &_lv_style_const_prop_id_BITMAP_MASK_SRC, .value = { .ptr = val } \ + } #if LV_USE_FLEX #define LV_STYLE_CONST_FLEX_FLOW(val) \ diff --git a/src/widgets/image/lv_image.c b/src/widgets/image/lv_image.c index 8d96435da..cb913aa11 100644 --- a/src/widgets/image/lv_image.c +++ b/src/widgets/image/lv_image.c @@ -421,12 +421,19 @@ void lv_image_set_inner_align(lv_obj_t * obj, lv_image_align_t align) if(align == img->align) return; img->align = align; - update_align(obj); lv_obj_invalidate(obj); } +void lv_image_set_bitmap_map_src(lv_obj_t * obj, const lv_image_dsc_t * src) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + lv_image_t * img = (lv_image_t *)obj; + img->bitmap_mask_src = src; + lv_obj_invalidate(obj); +} + /*===================== * Getter functions *====================*/ @@ -531,6 +538,15 @@ lv_image_align_t lv_image_get_inner_align(lv_obj_t * obj) return img->align; } +const lv_image_dsc_t * lv_image_get_bitmap_map_src(lv_obj_t * obj) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + + lv_image_t * img = (lv_image_t *)obj; + + return img->bitmap_mask_src; +} + /********************** * STATIC FUNCTIONS **********************/ @@ -701,6 +717,10 @@ static void draw_image(lv_event_t * e) return; } } + if(img->bitmap_mask_src) { + info->res = LV_COVER_RES_NOT_COVER; + return; + } } else if(code == LV_EVENT_DRAW_MAIN) { @@ -722,6 +742,7 @@ static void draw_image(lv_event_t * e) draw_dsc.rotation = img->rotation; draw_dsc.antialias = img->antialias; draw_dsc.blend_mode = img->blend_mode; + draw_dsc.bitmap_mask_src = img->bitmap_mask_src; draw_dsc.src = img->src; lv_area_t img_area = {obj->coords.x1, obj->coords.y1, diff --git a/src/widgets/image/lv_image.h b/src/widgets/image/lv_image.h index f02696d67..4b58c14ee 100644 --- a/src/widgets/image/lv_image.h +++ b/src/widgets/image/lv_image.h @@ -40,6 +40,7 @@ extern "C" { typedef struct { lv_obj_t obj; const void * src; /**< Image source: Pointer to an array or a file or a symbol*/ + const lv_image_dsc_t * bitmap_mask_src; /**< Pointer to an A8 bitmap mask */ lv_point_t offset; int32_t w; /**< Width of the image (Handled by the library)*/ int32_t h; /**< Height of the image (Handled by the library)*/ @@ -230,6 +231,13 @@ void lv_image_set_antialias(lv_obj_t * obj, bool antialias); */ void lv_image_set_inner_align(lv_obj_t * obj, lv_image_align_t align); +/** + * Set an A8 bitmap mask for the image. + * @param obj pointer to an image object + * @param src an lv_image_dsc_t bitmap mask source. + */ +void lv_image_set_bitmap_map_src(lv_obj_t * obj, const lv_image_dsc_t * src); + /*===================== * Getter functions *====================*/ @@ -314,6 +322,13 @@ bool lv_image_get_antialias(lv_obj_t * obj); */ lv_image_align_t lv_image_get_inner_align(lv_obj_t * obj); +/** + * Get the bitmap mask source. + * @param obj pointer to an image object + * @return an lv_image_dsc_t bitmap mask source. + */ +const lv_image_dsc_t * lv_image_get_bitmap_map_src(lv_obj_t * obj); + /********************** * MACROS **********************/ diff --git a/tests/ref_imgs/clip_corner_1.png b/tests/ref_imgs/clip_corner_1.png deleted file mode 100644 index 7029868fd..000000000 Binary files a/tests/ref_imgs/clip_corner_1.png and /dev/null differ diff --git a/tests/ref_imgs/draw/clip_corner_1.png b/tests/ref_imgs/draw/clip_corner_1.png new file mode 100644 index 000000000..079437b98 Binary files /dev/null and b/tests/ref_imgs/draw/clip_corner_1.png differ diff --git a/tests/ref_imgs/draw/draw_layer_bitmap_mask.png b/tests/ref_imgs/draw/draw_layer_bitmap_mask.png new file mode 100644 index 000000000..b5d4e3479 Binary files /dev/null and b/tests/ref_imgs/draw/draw_layer_bitmap_mask.png differ diff --git a/tests/src/test_cases/draw/test_clip_corner.c b/tests/src/test_cases/draw/test_clip_corner.c index 95c9d0c40..08ba63555 100644 --- a/tests/src/test_cases/draw/test_clip_corner.c +++ b/tests/src/test_cases/draw/test_clip_corner.c @@ -19,6 +19,12 @@ static lv_obj_t * create_panel(int32_t radius, bool transform) lv_obj_set_style_pad_all(parent, 3, 0); lv_obj_set_style_radius(parent, radius, 0); lv_obj_set_style_clip_corner(parent, true, 0); + lv_obj_set_style_shadow_color(parent, lv_color_hex(0x888888), 0); + lv_obj_set_style_shadow_width(parent, 30, 0); + lv_obj_set_style_shadow_spread(parent, 10, 0); + lv_obj_set_style_outline_color(parent, lv_color_hex(0xff0000), 0); + lv_obj_set_style_outline_width(parent, 2, 0); + lv_obj_set_style_outline_pad(parent, 5, 0); if(transform) lv_obj_set_style_transform_rotation(parent, 300, 0); lv_obj_t * label = lv_label_create(parent); @@ -35,7 +41,7 @@ static lv_obj_t * create_panel(int32_t radius, bool transform) return parent; } -void test_func_1(void) +void test_clip_corner_1(void) { lv_obj_set_flex_flow(lv_screen_active(), LV_FLEX_FLOW_ROW_WRAP); lv_obj_set_flex_align(lv_screen_active(), LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_SPACE_EVENLY); @@ -52,7 +58,7 @@ void test_func_1(void) create_panel(30, true); create_panel(100, true); - TEST_ASSERT_EQUAL_SCREENSHOT("clip_corner_1.png"); + TEST_ASSERT_EQUAL_SCREENSHOT("draw/clip_corner_1.png"); } diff --git a/tests/src/test_cases/draw/test_draw_layer.c b/tests/src/test_cases/draw/test_draw_layer.c new file mode 100644 index 000000000..b1b1702ee --- /dev/null +++ b/tests/src/test_cases/draw/test_draw_layer.c @@ -0,0 +1,37 @@ +#if LV_BUILD_TEST +#include "../lvgl.h" + +#include "unity/unity.h" + +void setUp(void) +{ + /* Function run before every test */ +} + +void tearDown(void) +{ + /* Function run after every test */ + lv_obj_clean(lv_screen_active()); +} + +void test_draw_layer_bitmap_mask(void) +{ + LV_IMAGE_DECLARE(test_image_cogwheel_a8); + + lv_obj_t * obj = lv_obj_create(lv_screen_active()); + lv_obj_set_size(obj, 200, 200); + lv_obj_set_style_bg_color(obj, lv_color_hex3(0xf88), 0); + lv_obj_set_style_bitmap_mask_src(obj, &test_image_cogwheel_a8, 0); + lv_obj_center(obj); + + lv_obj_t * label = lv_label_create(obj); + lv_obj_set_width(label, lv_pct(100)); + lv_label_set_text(label, + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque suscipit risus nec pharetra pulvinar. In hac habitasse platea dictumst. Proin placerat congue massa eu luctus. Suspendisse risus nulla, consectetur eget odio ut, mollis sollicitudin magna. Suspendisse volutpat consequat laoreet. Aenean sodales suscipit leo, vitae pulvinar lorem pulvinar eu. Nullam molestie hendrerit est sit amet imperdiet."); + lv_obj_center(label); + + TEST_ASSERT_EQUAL_SCREENSHOT("draw/draw_layer_bitmap_mask.png"); + +} + +#endif