From f5759351806d8b14537c0488842e3c7132c72fc9 Mon Sep 17 00:00:00 2001 From: Mariotaku Date: Wed, 15 Jun 2022 17:36:47 +0900 Subject: [PATCH] fix(sdl): add transformation support for the SDL backend (#3403) * sdl transform wip * sdl transform wip * working transform (scale, rotate) * fixed transform with masks * fixing includes * removed lv_obj_t references in draw backend * update the API to work with SW layers too * update lv_conf_internal.h * makefile fixes * updated sdl transform implementation Co-authored-by: Gabor Kiss-Vamosi --- lv_conf_template.h | 4 +- src/core/lv_refr.c | 263 +++++++++++---------------- src/draw/lv_draw.c | 5 + src/draw/lv_draw.h | 61 +++++++ src/draw/lv_draw.mk | 1 + src/draw/lv_draw_layer.c | 89 +++++++++ src/draw/lv_draw_layer.h | 83 +++++++++ src/draw/sdl/lv_draw_sdl.c | 5 + src/draw/sdl/lv_draw_sdl.mk | 1 + src/draw/sdl/lv_draw_sdl_composite.c | 15 +- src/draw/sdl/lv_draw_sdl_composite.h | 1 + src/draw/sdl/lv_draw_sdl_img.c | 9 +- src/draw/sdl/lv_draw_sdl_label.c | 7 +- src/draw/sdl/lv_draw_sdl_layer.c | 132 ++++++++++++++ src/draw/sdl/lv_draw_sdl_layer.h | 55 ++++++ src/draw/sdl/lv_draw_sdl_priv.h | 2 + src/draw/sdl/lv_draw_sdl_rect.c | 8 +- src/draw/sw/lv_draw_sw.c | 6 +- src/draw/sw/lv_draw_sw.h | 19 +- src/draw/sw/lv_draw_sw.mk | 1 + src/draw/sw/lv_draw_sw_layer.c | 152 ++++++++++++++++ src/lv_conf_internal.h | 4 +- 22 files changed, 749 insertions(+), 174 deletions(-) create mode 100644 src/draw/lv_draw_layer.c create mode 100644 src/draw/lv_draw_layer.h create mode 100644 src/draw/sdl/lv_draw_sdl_layer.c create mode 100644 src/draw/sdl/lv_draw_sdl_layer.h create mode 100644 src/draw/sw/lv_draw_sw_layer.c diff --git a/lv_conf_template.h b/lv_conf_template.h index 238764ea8..e30407272 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -125,8 +125,6 @@ * and blend it as an image with the given opacity. * Note that `bg_opa`, `text_opa` etc don't require buffering into layer) * The widget can be buffered in smaller chunks to avoid using large buffers. - * `draw_area` (`lv_area_t` meaning the area to draw and `px_size` (size of a pixel in bytes) - * can be used the set the buffer size adaptively. * * - LV_LAYER_SIMPLE_BUF_SIZE: [bytes] the optimal target buffer size. LVGL will try to allocate it * - LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE: [bytes] used if `LV_LAYER_SIMPLE_BUF_SIZE` couldn't be allocated. @@ -136,7 +134,7 @@ * and can't be drawn in chunks. So these settings affects only widgets with opacity. */ #define LV_LAYER_SIMPLE_BUF_SIZE (24 * 1024) -#define LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE LV_MAX(lv_area_get_width(&draw_area) * px_size, 2048) +#define LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE (3 * 1024) /*Default image cache size. Image caching keeps the images opened. *If only the built-in image formats are used there is no real advantage of caching. (I.e. if no new image decoder is added) diff --git a/src/core/lv_refr.c b/src/core/lv_refr.c index f6e3d4169..4c561cce6 100644 --- a/src/core/lv_refr.c +++ b/src/core/lv_refr.c @@ -812,204 +812,159 @@ static void refr_obj_and_children(lv_draw_ctx_t * draw_ctx, lv_obj_t * top_obj) } } + +static lv_res_t layer_get_area(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj, lv_layer_type_t layer_type, + lv_area_t * layer_area_out) +{ + lv_coord_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); + + 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_obj_get_transformed_area(obj, &tranf_coords, false, false); + if(!_lv_area_intersect(&clip_coords_for_obj, draw_ctx->clip_area, &tranf_coords)) { + return LV_RES_INV; + } + + /*Transform back (inverse) the transformed area. + *It will tell which area of the non-transformed widget needs to be redrawn + *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)) { + return LV_RES_INV; + } + + *layer_area_out = inverse_clip_coords_for_obj; + } + else if(layer_type == LV_LAYER_TYPE_SIMPLE) { + lv_area_t clip_coords_for_obj; + if(!_lv_area_intersect(&clip_coords_for_obj, draw_ctx->clip_area, &obj_coords_ext)) { + return LV_RES_INV; + } + *layer_area_out = clip_coords_for_obj; + } + else { + LV_LOG_WARN("Unhandled intermediate layer type"); + return LV_RES_INV; + } + + return LV_RES_OK; +} + +static void layer_alpha_test(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags) +{ + bool has_alpha; + /*If globally the layer has alpha maybe this smaller section has not (e.g. not on a rounded corner) + *If turns out that this section has no alpha renderer can choose faster algorithms*/ + if(flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA) { + /*Test for alpha by assuming there is no alpha. If it fails, fall back to rendering with alpha*/ + has_alpha = true; + if(_lv_area_is_in(&layer_ctx->area_act, &obj->coords, 0)) { + lv_cover_check_info_t info; + info.res = LV_COVER_RES_COVER; + info.area = &layer_ctx->area_act; + lv_event_send(obj, LV_EVENT_COVER_CHECK, &info); + if(info.res == LV_COVER_RES_COVER) has_alpha = false; + } + + if(has_alpha) { + layer_ctx->area_act.y2 = layer_ctx->area_act.y1 + layer_ctx->max_row_with_alpha - 1; + } + } + else { + has_alpha = false; + } + + if(layer_ctx->area_act.y2 > layer_ctx->area_full.y2) layer_ctx->area_act.y2 = layer_ctx->area_full.y2; + lv_draw_layer_adjust(draw_ctx, layer_ctx, has_alpha ? LV_DRAW_LAYER_FLAG_HAS_ALPHA : LV_DRAW_LAYER_FLAG_NONE); +} + + void refr_obj(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj) { /*Do not refresh hidden objects*/ if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return; - lv_layer_type_t inlayer = _lv_obj_get_layer_type(obj); - if(inlayer == LV_LAYER_TYPE_NONE) { + lv_layer_type_t layer_type = _lv_obj_get_layer_type(obj); + if(layer_type == LV_LAYER_TYPE_NONE) { lv_obj_redraw(draw_ctx, obj); } else { lv_opa_t opa = lv_obj_get_style_opa(obj, 0); if(opa < LV_OPA_MIN) return; - lv_area_t draw_area; - const lv_area_t * clip_area_ori = draw_ctx->clip_area; - lv_coord_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); - uint32_t buf_size_sub; + lv_area_t layer_area_full; + lv_res_t res = layer_get_area(draw_ctx, obj, layer_type, &layer_area_full); + if(res != LV_RES_OK) return; - if(inlayer == LV_LAYER_TYPE_TRANSFORM) { - lv_area_t clip_coords_for_obj; - lv_area_t tranf_coords = obj_coords_ext; - lv_obj_get_transformed_area(obj, &tranf_coords, false, false); - if(!_lv_area_intersect(&clip_coords_for_obj, clip_area_ori, &tranf_coords)) { - return; - } + lv_draw_layer_flags_t flags = LV_DRAW_LAYER_FLAG_HAS_ALPHA; - 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)) { - return; - } - - draw_area = inverse_clip_coords_for_obj; - } - else if(inlayer == LV_LAYER_TYPE_SIMPLE) { - lv_area_t clip_coords_for_obj; - if(!_lv_area_intersect(&clip_coords_for_obj, clip_area_ori, &obj_coords_ext)) { - return; - } - draw_area = clip_coords_for_obj; - } - else { - LV_LOG_WARN("Unhandled intermediate layer type"); - return; - } - - bool full_cover = false; - if(_lv_area_is_in(&draw_area, &obj->coords, 0)) { + if(_lv_area_is_in(&layer_area_full, &obj->coords, 0)) { lv_cover_check_info_t info; info.res = LV_COVER_RES_COVER; - info.area = &draw_area; + info.area = &layer_area_full; lv_event_send(obj, LV_EVENT_COVER_CHECK, &info); - if(info.res == LV_COVER_RES_COVER) full_cover = true; + if(info.res == LV_COVER_RES_COVER) flags &= ~LV_DRAW_LAYER_FLAG_HAS_ALPHA; } - if(LV_COLOR_SCREEN_TRANSP == 0 && !full_cover) { - LV_LOG_WARN("Rendering this widget needs LV_COLOR_SCREEN_TRANSP 1"); + if(layer_type == LV_LAYER_TYPE_SIMPLE) flags |= LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE; + + lv_draw_layer_ctx_t * layer_ctx = lv_draw_layer_create(draw_ctx, &layer_area_full, flags); + if(layer_ctx == NULL) { + LV_LOG_WARN("Couldn't create a new layer context"); return; } - - uint32_t px_size = full_cover ? sizeof(lv_color_t) : LV_IMG_PX_SIZE_ALPHA_BYTE; - - if(inlayer == LV_LAYER_TYPE_SIMPLE) { - buf_size_sub = LV_LAYER_SIMPLE_BUF_SIZE; - } - else { - buf_size_sub = lv_area_get_size(&draw_area) * px_size; - } - - uint32_t buf_size_full = lv_area_get_size(&draw_area) * px_size; - if(buf_size_sub > buf_size_full) buf_size_sub = buf_size_full; - - uint8_t * layer_buf = lv_mem_alloc(buf_size_sub); - /*Try again with a smaller buf size*/ - if(inlayer == LV_LAYER_TYPE_SIMPLE) { - if(layer_buf == NULL) { - LV_LOG_WARN("Cannot allocate %"LV_PRIu32" bytes for layer buffer. Allocating %"LV_PRIu32" bytes instead. (Reduced performance)", - (uint32_t)buf_size_sub * px_size, (uint32_t)LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE * px_size); - buf_size_sub = LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE; - if(buf_size_sub > buf_size_full) buf_size_sub = buf_size_full; - layer_buf = lv_mem_alloc(buf_size_sub); - } - } - if(layer_buf == NULL) { - LV_LOG_ERROR("Out of memory: couldn't allocate %"LV_PRIu32" bytes for layer buffer.", (uint32_t)buf_size_sub * px_size); - LV_ASSERT_MALLOC(layer_buf); - return; - } - - int32_t row_cnt = 0; - lv_draw_ctx_t * new_draw_ctx = lv_mem_alloc(disp_refr->driver->draw_ctx_size); - LV_ASSERT_MALLOC(new_draw_ctx); - if(new_draw_ctx == NULL) { - LV_LOG_ERROR("Out of memory: couldn't allocate new draw context."); - lv_mem_free(layer_buf); - return; - } - - lv_area_t draw_area_sub = draw_area; - - /*Set-up a new draw_ctx*/ - bool old_scr_transp = disp_refr->driver->screen_transp; - disp_refr->driver->draw_ctx_init(disp_refr->driver, new_draw_ctx); - new_draw_ctx->clip_area = &draw_area_sub; - new_draw_ctx->buf_area = &draw_area_sub; - new_draw_ctx->buf = (void *)layer_buf; + lv_point_t pivot = { + .x = lv_obj_get_style_transform_pivot_x(obj, 0), + .y = lv_obj_get_style_transform_pivot_y(obj, 0) + }; lv_draw_img_dsc_t draw_dsc; lv_draw_img_dsc_init(&draw_dsc); draw_dsc.opa = opa; draw_dsc.angle = lv_obj_get_style_transform_angle(obj, 0); if(draw_dsc.angle > 3600) draw_dsc.angle -= 3600; - if(draw_dsc.angle < 0) draw_dsc.angle += 3600; + else if(draw_dsc.angle < 0) draw_dsc.angle += 3600; draw_dsc.zoom = lv_obj_get_style_transform_zoom(obj, 0); draw_dsc.blend_mode = lv_obj_get_style_blend_mode(obj, 0); draw_dsc.antialias = disp_refr->driver->antialiasing; - lv_point_t pivot; - pivot.x = lv_obj_get_style_transform_pivot_x(obj, 0); - pivot.y = lv_obj_get_style_transform_pivot_y(obj, 0); - - lv_img_dsc_t img; - img.data = layer_buf; - img.header.always_zero = 0; - img.header.w = lv_area_get_width(&draw_area); - - /*If the whole area is covers calculate the row count only once*/ - if(full_cover) { - row_cnt = buf_size_sub / (sizeof(lv_color_t) * lv_area_get_width(&draw_area)); - draw_area_sub.y2 = draw_area_sub.y1 + row_cnt - 1; - if(draw_area_sub.y2 > draw_area.y2) draw_area_sub.y2 = draw_area.y2; + if(flags & LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE) { + layer_ctx->area_act = layer_ctx->area_full; + layer_ctx->area_act.y2 = layer_ctx->area_act.y1 + layer_ctx->max_row_with_no_alpha - 1; + if(layer_ctx->area_act.y2 > layer_ctx->area_full.y2) layer_ctx->area_act.y2 = layer_ctx->area_full.y2; } - while(draw_area_sub.y1 <= draw_area.y2) { - /* If the widget covers the area of as many rows as an RGB buffer provides, - * draw it as RGB instead of ARGB because it's much faster - * `full_cover_global == true` indicates that the widget covers the whole draw_area anyway*/ - if(!full_cover) { - row_cnt = buf_size_sub / (sizeof(lv_color_t) * lv_area_get_width(&draw_area)); + while(layer_ctx->area_act.y1 <= layer_area_full.y2) { + if(flags & LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE) { + layer_alpha_test(obj, draw_ctx, layer_ctx, flags); } - draw_area_sub.y2 = draw_area_sub.y1 + row_cnt - 1; - if(draw_area_sub.y2 > draw_area.y2) draw_area_sub.y2 = draw_area.y2; + lv_obj_redraw(draw_ctx, obj); - bool full_cover_sub = false; - if(_lv_area_is_in(&draw_area_sub, &obj->coords, 0)) { - lv_cover_check_info_t info; - info.res = LV_COVER_RES_COVER; - info.area = &draw_area_sub; - lv_event_send(obj, LV_EVENT_COVER_CHECK, &info); - if(info.res == LV_COVER_RES_COVER) full_cover_sub = true; - } + draw_dsc.pivot.x = obj->coords.x1 + pivot.x - draw_ctx->buf_area->x1; + draw_dsc.pivot.y = obj->coords.y1 + pivot.y - draw_ctx->buf_area->y1; - /* In the area is not covered by the widget recalculate the row count using ARGB pixel size - * It can happen that this smaller area is already covered by the widget but ignore this case - * in favor of fewer LV_EVENT_COVER_CHECK calls */ - if(!full_cover_sub) { - row_cnt = buf_size_sub / (LV_IMG_PX_SIZE_ALPHA_BYTE * lv_area_get_width(&draw_area)); - draw_area_sub.y2 = draw_area_sub.y1 + row_cnt - 1; - if(draw_area_sub.y2 > draw_area.y2) draw_area_sub.y2 = draw_area.y2; - disp_refr->driver->screen_transp = 1; - img.header.cf = LV_IMG_CF_TRUE_COLOR_ALPHA; - } - else { - disp_refr->driver->screen_transp = 0; - img.header.cf = LV_IMG_CF_TRUE_COLOR; - } + /*With LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE it should also go the next chunk*/ + lv_draw_layer_blend(draw_ctx, layer_ctx, &draw_dsc); - draw_dsc.pivot.x = obj->coords.x1 + pivot.x - draw_area_sub.x1; - draw_dsc.pivot.y = obj->coords.y1 + pivot.y - draw_area_sub.y1; + if((flags & LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE) == 0) break; - if(!full_cover_sub) lv_memset_00(layer_buf, buf_size_sub); - lv_obj_redraw(new_draw_ctx, obj); - - lv_img_cache_invalidate_src(&img); - - img.header.h = lv_area_get_height(&draw_area_sub); - - disp_refr->driver->screen_transp = old_scr_transp; - lv_draw_img(draw_ctx, &draw_dsc, &draw_area_sub, &img); - - draw_area_sub.y1 = draw_area_sub.y2 + 1; - if(draw_area_sub.y2 > draw_area.y2) draw_area_sub.y2 = draw_area.y2; + lv_area_move(&layer_ctx->area_act, 0, layer_ctx->max_row_with_no_alpha); } - disp_refr->driver->draw_ctx_deinit(disp_refr->driver, new_draw_ctx); - lv_mem_free(layer_buf); - lv_mem_free(new_draw_ctx); - disp_refr->driver->screen_transp = old_scr_transp; + lv_draw_layer_destroy(draw_ctx, layer_ctx); } } - - static uint32_t get_max_row(lv_disp_t * disp, lv_coord_t area_w, lv_coord_t area_h) { int32_t max_row = (uint32_t)disp->driver->draw_buf->size / area_w; diff --git a/src/draw/lv_draw.c b/src/draw/lv_draw.c index f538756b3..823f70768 100644 --- a/src/draw/lv_draw.c +++ b/src/draw/lv_draw.c @@ -42,6 +42,11 @@ void lv_draw_init(void) /*Nothing to init now*/ } +void lv_draw_wait_for_finish(lv_draw_ctx_t * draw_ctx) +{ + if(draw_ctx->wait_for_finish) draw_ctx->wait_for_finish(draw_ctx); +} + /********************** * STATIC FUNCTIONS **********************/ diff --git a/src/draw/lv_draw.h b/src/draw/lv_draw.h index 84a3aef1c..80b62e9f0 100644 --- a/src/draw/lv_draw.h +++ b/src/draw/lv_draw.h @@ -28,6 +28,7 @@ extern "C" { #include "lv_draw_arc.h" #include "lv_draw_mask.h" #include "lv_draw_transform.h" +#include "lv_draw_layer.h" /********************* * DEFINES @@ -41,6 +42,19 @@ typedef struct { void * user_data; } lv_draw_mask_t; +typedef struct _lv_draw_layer_ctx_t { + lv_area_t area_full; + lv_area_t area_act; + lv_coord_t max_row_with_alpha; + lv_coord_t max_row_with_no_alpha; + void * buf; + struct { + const lv_area_t * clip_area; + lv_area_t * buf_area; + void * buf; + bool screen_transp; + } original; +} lv_draw_layer_ctx_t; typedef struct _lv_draw_ctx_t { /** @@ -126,6 +140,50 @@ typedef struct _lv_draw_ctx_t { void (*buffer_copy)(struct _lv_draw_ctx_t * draw_ctx, void * dest_buf, lv_coord_t dest_stride, const lv_area_t * dest_area, void * src_buf, lv_coord_t src_stride, const lv_area_t * src_area); + + /** + * Initialize a new layer context. + * The original buffer and area data are already saved from `draw_ctx` to `layer_ctx` + * @param draw_ctx pointer to the current draw context + * @param layer_area the coordinates of the layer + * @param flags OR-ed flags from @lv_draw_layer_flags_t + * @return pointer to the layer context, or NULL on error + */ + struct _lv_draw_layer_ctx_t * (*layer_init)(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags); + + /** + * Adjust the layer_ctx and/or draw_ctx based on the `layer_ctx->area_act`. + * It's called only if flags has `LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE` + * @param draw_ctx pointer to the current draw context + * @param layer_ctx pointer to a layer context + * @param flags OR-ed flags from @lv_draw_layer_flags_t + */ + void (*layer_adjust)(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags); + + /** + * Blend a rendered layer to `layer_ctx->area_act` + * @param draw_ctx pointer to the current draw context + * @param layer_ctx pointer to a layer context + * @param draw_dsc pointer to an image draw descriptor + */ + void (*layer_blend)(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + const lv_draw_img_dsc_t * draw_dsc); + + /** + * Destroy a layer context. The original buffer and area data of the `draw_ctx` will be restored + * and the `layer_ctx` itself will be freed automatically. + * @param draw_ctx pointer to the current draw context + * @param layer_ctx pointer to a layer context + */ + void (*layer_destroy)(struct _lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx); + + /** + * Size of a layer context in bytes. + */ + size_t layer_instance_size; + #if LV_USE_USER_DATA void * user_data; #endif @@ -138,6 +196,9 @@ typedef struct _lv_draw_ctx_t { void lv_draw_init(void); + +void lv_draw_wait_for_finish(lv_draw_ctx_t * draw_ctx); + /********************** * GLOBAL VARIABLES **********************/ diff --git a/src/draw/lv_draw.mk b/src/draw/lv_draw.mk index b95f4548c..f48f48fe0 100644 --- a/src/draw/lv_draw.mk +++ b/src/draw/lv_draw.mk @@ -6,6 +6,7 @@ CSRCS += lv_draw_line.c CSRCS += lv_draw_mask.c CSRCS += lv_draw_rect.c CSRCS += lv_draw_transform.c +CSRCS += lv_draw_layer.c CSRCS += lv_draw_triangle.c CSRCS += lv_img_buf.c CSRCS += lv_img_cache.c diff --git a/src/draw/lv_draw_layer.c b/src/draw/lv_draw_layer.c new file mode 100644 index 000000000..d49a226cd --- /dev/null +++ b/src/draw/lv_draw_layer.c @@ -0,0 +1,89 @@ +/** + * @file lv_draw_layer.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_draw.h" +#include "lv_draw_arc.h" +#include "../core/lv_refr.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_draw_layer_ctx_t * lv_draw_layer_create(lv_draw_ctx_t * draw_ctx, const lv_area_t * layer_area, + lv_draw_layer_flags_t flags) +{ + if(draw_ctx->layer_init == NULL) return NULL; + + lv_draw_layer_ctx_t * layer_ctx = lv_mem_alloc(draw_ctx->layer_instance_size); + LV_ASSERT_MALLOC(layer_ctx); + if(layer_ctx == NULL) { + LV_LOG_WARN("Couldn't allocate a new layer context"); + return NULL; + } + + lv_memset_00(layer_ctx, draw_ctx->layer_instance_size); + + lv_disp_t * disp_refr = _lv_refr_get_disp_refreshing(); + layer_ctx->original.buf = draw_ctx->buf; + layer_ctx->original.buf_area = draw_ctx->buf_area; + layer_ctx->original.clip_area = draw_ctx->clip_area; + layer_ctx->original.screen_transp = disp_refr->driver->screen_transp; + layer_ctx->area_full = *layer_area; + + return draw_ctx->layer_init(draw_ctx, layer_ctx, flags); +} + +void lv_draw_layer_adjust(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags) +{ + if(draw_ctx->layer_adjust) draw_ctx->layer_adjust(draw_ctx, layer_ctx, flags); +} + +void lv_draw_layer_blend(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + lv_draw_img_dsc_t * draw_dsc) +{ + if(draw_ctx->layer_blend) draw_ctx->layer_blend(draw_ctx, layer_ctx, draw_dsc); +} + +void lv_draw_layer_destroy(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx) +{ + + lv_draw_wait_for_finish(draw_ctx); + draw_ctx->buf = layer_ctx->original.buf; + draw_ctx->buf_area = layer_ctx->original.buf_area; + draw_ctx->clip_area = layer_ctx->original.clip_area; + lv_disp_t * disp_refr = _lv_refr_get_disp_refreshing(); + disp_refr->driver->screen_transp = layer_ctx->original.screen_transp; + + if(draw_ctx->layer_destroy) draw_ctx->layer_destroy(draw_ctx, layer_ctx); + lv_mem_free(layer_ctx); +} + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/src/draw/lv_draw_layer.h b/src/draw/lv_draw_layer.h new file mode 100644 index 000000000..cd64149c4 --- /dev/null +++ b/src/draw/lv_draw_layer.h @@ -0,0 +1,83 @@ +/** + * @file lv_draw_layer.h + * + */ + +#ifndef LV_DRAW_LAYER_H +#define LV_DRAW_LAYER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../lv_conf_internal.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +struct _lv_draw_ctx_t; +struct _lv_draw_layer_ctx_t; + +typedef enum { + LV_DRAW_LAYER_FLAG_NONE, + LV_DRAW_LAYER_FLAG_HAS_ALPHA, + LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE, +} lv_draw_layer_flags_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a new layer context. It is used to start and independent rendering session + * with the current draw_ctx + * @param draw_ctx pointer to the current draw context + * @param layer_area the coordinates of the layer + * @param flags OR-ed flags from @lv_draw_layer_flags_t + * @return pointer to the layer context, or NULL on error + */ +struct _lv_draw_layer_ctx_t * lv_draw_layer_create(struct _lv_draw_ctx_t * draw_ctx, const lv_area_t * layer_area, + lv_draw_layer_flags_t flags); + +/** + * Adjust the layer_ctx and/or draw_ctx based on the `layer_ctx->area_act`. + * It's called only if flags has `LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE` + * @param draw_ctx pointer to the current draw context + * @param layer_ctx pointer to a layer context + * @param flags OR-ed flags from @lv_draw_layer_flags_t + */ +void lv_draw_layer_adjust(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags); + +/** + * Blend a rendered layer to `layer_ctx->area_act` + * @param draw_ctx pointer to the current draw context + * @param layer_ctx pointer to a layer context + * @param draw_dsc pointer to an image draw descriptor + */ +void lv_draw_layer_blend(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + lv_draw_img_dsc_t * draw_dsc); + +/** + * Destroy a layer context. + * @param draw_ctx pointer to the current draw context + * @param layer_ctx pointer to a layer context + */ +void lv_draw_layer_destroy(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_LAYER_H*/ diff --git a/src/draw/sdl/lv_draw_sdl.c b/src/draw/sdl/lv_draw_sdl.c index 430f3a23c..e3cdf577a 100644 --- a/src/draw/sdl/lv_draw_sdl.c +++ b/src/draw/sdl/lv_draw_sdl.c @@ -15,6 +15,7 @@ #include "lv_draw_sdl.h" #include "lv_draw_sdl_utils.h" #include "lv_draw_sdl_texture_cache.h" +#include "lv_draw_sdl_layer.h" /********************* * DEFINES @@ -69,6 +70,10 @@ void lv_draw_sdl_init_ctx(lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx) draw_ctx->draw_arc = lv_draw_sdl_draw_arc; draw_ctx->draw_polygon = lv_draw_sdl_polygon; draw_ctx->draw_bg = lv_draw_sdl_draw_bg; + draw_ctx->layer_init = lv_draw_sdl_layer_init; + draw_ctx->layer_blend = lv_draw_sdl_layer_blend; + draw_ctx->layer_destroy = lv_draw_sdl_layer_destroy; + draw_ctx->layer_instance_size = sizeof(lv_draw_sdl_layer_ctx_t); lv_draw_sdl_ctx_t * draw_ctx_sdl = (lv_draw_sdl_ctx_t *) draw_ctx; draw_ctx_sdl->renderer = ((lv_draw_sdl_drv_param_t *) disp_drv->user_data)->renderer; draw_ctx_sdl->internals = lv_mem_alloc(sizeof(lv_draw_sdl_context_internals_t)); diff --git a/src/draw/sdl/lv_draw_sdl.mk b/src/draw/sdl/lv_draw_sdl.mk index 6609c38bc..c5c28b66b 100644 --- a/src/draw/sdl/lv_draw_sdl.mk +++ b/src/draw/sdl/lv_draw_sdl.mk @@ -11,6 +11,7 @@ CSRCS += lv_draw_sdl_rect.c CSRCS += lv_draw_sdl_stack_blur.c CSRCS += lv_draw_sdl_texture_cache.c CSRCS += lv_draw_sdl_utils.c +CSRCS += lv_draw_sdl_layer.c DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sdl VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sdl diff --git a/src/draw/sdl/lv_draw_sdl_composite.c b/src/draw/sdl/lv_draw_sdl_composite.c index 1ee843f7a..e6007d1a5 100644 --- a/src/draw/sdl/lv_draw_sdl_composite.c +++ b/src/draw/sdl/lv_draw_sdl_composite.c @@ -13,7 +13,6 @@ #include "../../misc/lv_gc.h" #include "../../core/lv_refr.h" #include "lv_draw_sdl_composite.h" -#include "lv_draw_sdl_mask.h" #include "lv_draw_sdl_utils.h" #include "lv_draw_sdl_priv.h" #include "lv_draw_sdl_texture_cache.h" @@ -84,15 +83,16 @@ bool lv_draw_sdl_composite_begin(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coor const bool draw_blend = blend_mode != LV_BLEND_MODE_NORMAL; if(draw_mask || draw_blend) { lv_draw_sdl_context_internals_t * internals = ctx->internals; - LV_ASSERT(internals->mask == NULL && internals->composition == NULL); + LV_ASSERT(internals->mask == NULL && internals->composition == NULL && internals->target_backup == NULL); lv_coord_t w = lv_area_get_width(apply_area), h = lv_area_get_height(apply_area); internals->composition = lv_draw_sdl_composite_texture_obtain(ctx, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET0, w, h); - /* Don't need to worry about overflow */ + /* Don't need to worry about integral overflow */ lv_coord_t ofs_x = (lv_coord_t) - apply_area->x1, ofs_y = (lv_coord_t) - apply_area->y1; /* Offset draw area to start with (0,0) of coords */ lv_area_move(coords_out, ofs_x, ofs_y); lv_area_move(clip_out, ofs_x, ofs_y); + internals->target_backup = SDL_GetRenderTarget(ctx->renderer); SDL_SetRenderTarget(ctx->renderer, internals->composition); SDL_SetRenderDrawColor(ctx->renderer, 255, 255, 255, 0); SDL_RenderClear(ctx->renderer); @@ -140,7 +140,7 @@ void lv_draw_sdl_composite_end(lv_draw_sdl_ctx_t * ctx, const lv_area_t * apply_ SDL_Rect dst_rect; lv_area_to_sdl_rect(apply_area, &dst_rect); - SDL_SetRenderTarget(ctx->renderer, ctx->base_draw.buf); + SDL_SetRenderTarget(ctx->renderer, internals->target_backup); switch(blend_mode) { case LV_BLEND_MODE_NORMAL: SDL_SetTextureBlendMode(internals->composition, SDL_BLENDMODE_BLEND); @@ -173,7 +173,7 @@ void lv_draw_sdl_composite_end(lv_draw_sdl_ctx_t * ctx, const lv_area_t * apply_ SDL_RenderCopy(ctx->renderer, internals->composition, &src_rect, &dst_rect); } - internals->mask = internals->composition = NULL; + internals->mask = internals->composition = internals->target_backup = NULL; } SDL_Texture * lv_draw_sdl_composite_texture_obtain(lv_draw_sdl_ctx_t * ctx, lv_draw_sdl_composite_texture_id_t id, @@ -186,7 +186,10 @@ SDL_Texture * lv_draw_sdl_composite_texture_obtain(lv_draw_sdl_ctx_t * ctx, lv_d if(!result || tex_size->x < w || tex_size->y < h) { lv_coord_t size = next_pow_of_2(LV_MAX(w, h)); int access = SDL_TEXTUREACCESS_STREAMING; - if(id >= LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET0) { + if(id >= LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TRANSFORM0) { + access = SDL_TEXTUREACCESS_TARGET; + } + else if(id >= LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET0) { access = SDL_TEXTUREACCESS_TARGET; } result = SDL_CreateTexture(ctx->renderer, LV_DRAW_SDL_TEXTURE_FORMAT, access, size, size); diff --git a/src/draw/sdl/lv_draw_sdl_composite.h b/src/draw/sdl/lv_draw_sdl_composite.h index 3050815d7..72a2daef7 100644 --- a/src/draw/sdl/lv_draw_sdl_composite.h +++ b/src/draw/sdl/lv_draw_sdl_composite.h @@ -35,6 +35,7 @@ typedef enum lv_draw_sdl_composite_texture_id_t { LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_STREAM1, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET0, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET1, + LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TRANSFORM0, } lv_draw_sdl_composite_texture_id_t; /********************** diff --git a/src/draw/sdl/lv_draw_sdl_img.c b/src/draw/sdl/lv_draw_sdl_img.c index c6782386e..59a9fc00f 100644 --- a/src/draw/sdl/lv_draw_sdl_img.c +++ b/src/draw/sdl/lv_draw_sdl_img.c @@ -22,6 +22,7 @@ #include "lv_draw_sdl_texture_cache.h" #include "lv_draw_sdl_composite.h" #include "lv_draw_sdl_rect.h" +#include "lv_draw_sdl_layer.h" /********************* * DEFINES @@ -123,11 +124,15 @@ lv_res_t lv_draw_sdl_img_core(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t /* Coords will be translated so coords will start at (0,0) */ lv_area_t t_coords = zoomed_cords, t_clip = *clip, apply_area; + bool has_composite = false; + if(!check_mask_simple_radius(&t_coords, &radius)) { - lv_draw_sdl_composite_begin(ctx, &zoomed_cords, clip, NULL, draw_dsc->blend_mode, - &t_coords, &t_clip, &apply_area); + has_composite = lv_draw_sdl_composite_begin(ctx, &zoomed_cords, clip, NULL, draw_dsc->blend_mode, + &t_coords, &t_clip, &apply_area); } + lv_draw_sdl_transform_areas_offset(ctx, has_composite, &apply_area, &t_coords, &t_clip); + SDL_Rect clip_rect, coords_rect; lv_area_to_sdl_rect(&t_clip, &clip_rect); lv_area_to_sdl_rect(&t_coords, &coords_rect); diff --git a/src/draw/sdl/lv_draw_sdl_label.c b/src/draw/sdl/lv_draw_sdl_label.c index 344f40aed..b8c79ae4a 100644 --- a/src/draw/sdl/lv_draw_sdl_label.c +++ b/src/draw/sdl/lv_draw_sdl_label.c @@ -19,6 +19,7 @@ #include "lv_draw_sdl_utils.h" #include "lv_draw_sdl_texture_cache.h" #include "lv_draw_sdl_composite.h" +#include "lv_draw_sdl_layer.h" /********************* * DEFINES @@ -134,8 +135,10 @@ void lv_draw_sdl_draw_letter(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t } lv_area_t t_letter = letter_area, t_clip = *clip_area, apply_area; - bool has_mask = lv_draw_sdl_composite_begin(ctx, &letter_area, clip_area, NULL, dsc->blend_mode, &t_letter, &t_clip, - &apply_area); + bool has_composite = lv_draw_sdl_composite_begin(ctx, &letter_area, clip_area, NULL, dsc->blend_mode, &t_letter, + &t_clip, &apply_area); + + lv_draw_sdl_transform_areas_offset(ctx, has_composite, &apply_area, &t_letter, &t_clip); /*If the letter is completely out of mask don't draw it*/ if(!_lv_area_intersect(&draw_area, &t_letter, &t_clip)) { diff --git a/src/draw/sdl/lv_draw_sdl_layer.c b/src/draw/sdl/lv_draw_sdl_layer.c new file mode 100644 index 000000000..48bc1b8f9 --- /dev/null +++ b/src/draw/sdl/lv_draw_sdl_layer.c @@ -0,0 +1,132 @@ +/** + * @file lv_draw_sdl_refr.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include "../../core/lv_refr.h" + +#include "lv_draw_sdl.h" +#include "lv_draw_sdl_priv.h" +#include "lv_draw_sdl_composite.h" +#include "lv_draw_sdl_utils.h" +#include "lv_draw_sdl_layer.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_draw_layer_ctx_t * lv_draw_sdl_layer_init(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags) +{ + lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx; + SDL_Renderer * renderer = ctx->renderer; + + lv_draw_sdl_layer_ctx_t * transform_ctx = (lv_draw_sdl_layer_ctx_t *) layer_ctx; + + transform_ctx->flags = flags; + transform_ctx->orig_target = SDL_GetRenderTarget(renderer); + + lv_coord_t target_w = lv_area_get_width(&layer_ctx->area_full); + lv_coord_t target_h = lv_area_get_height(&layer_ctx->area_full); + + enum lv_draw_sdl_composite_texture_id_t texture_id = LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TRANSFORM0 + + ctx->internals->transform_count; + transform_ctx->target = lv_draw_sdl_composite_texture_obtain(ctx, texture_id, target_w, target_h); + transform_ctx->target_rect.x = 0; + transform_ctx->target_rect.y = 0; + transform_ctx->target_rect.w = target_w; + transform_ctx->target_rect.h = target_h; + + SDL_SetTextureBlendMode(transform_ctx->target, SDL_BLENDMODE_BLEND); + SDL_SetRenderTarget(renderer, transform_ctx->target); + SDL_RenderClear(renderer); + + /* Set proper drawing context for transform layer */ + ctx->internals->transform_count += 1; + draw_ctx->buf_area = &layer_ctx->area_full; + draw_ctx->clip_area = &layer_ctx->area_full; + + return layer_ctx; +} + +void lv_draw_sdl_layer_blend(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx, + const lv_draw_img_dsc_t * draw_dsc) +{ + lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx; + lv_draw_sdl_layer_ctx_t * transform_ctx = (lv_draw_sdl_layer_ctx_t *) layer_ctx; + + SDL_Renderer * renderer = ctx->renderer; + + SDL_Rect trans_rect; + + if(transform_ctx->flags & LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE) { + lv_area_zoom_to_sdl_rect(&layer_ctx->area_act, &trans_rect, draw_dsc->zoom, &draw_dsc->pivot); + } + else { + lv_area_zoom_to_sdl_rect(&layer_ctx->area_full, &trans_rect, draw_dsc->zoom, &draw_dsc->pivot); + } + + SDL_SetRenderTarget(renderer, transform_ctx->orig_target); + + /*Render off-screen texture, transformed*/ + SDL_Rect clip_rect; + lv_area_to_sdl_rect(layer_ctx->original.clip_area, &clip_rect); + SDL_Point center = {.x = draw_dsc->pivot.x, .y = draw_dsc->pivot.y}; + SDL_RenderSetClipRect(renderer, &clip_rect); + SDL_RenderCopyEx(renderer, transform_ctx->target, &transform_ctx->target_rect, &trans_rect, + draw_dsc->angle, ¢er, SDL_FLIP_NONE); + SDL_RenderSetClipRect(renderer, NULL); +} + +void lv_draw_sdl_layer_destroy(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx) +{ + lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx; + ctx->internals->transform_count -= 1; +} + +void lv_draw_sdl_transform_areas_offset(lv_draw_sdl_ctx_t * ctx, bool has_composite, lv_area_t * apply_area, + lv_area_t * coords, lv_area_t * clip) +{ + if(ctx->internals->transform_count == 0) { + return; + } + lv_area_t * area = ctx->base_draw.buf_area; + lv_area_move(coords, -area->x1, -area->y1); + lv_area_move(clip, -area->x1, -area->y1); + if(has_composite) { + lv_area_move(apply_area, -area->x1, -area->y1); + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_GPU_SDL*/ diff --git a/src/draw/sdl/lv_draw_sdl_layer.h b/src/draw/sdl/lv_draw_sdl_layer.h new file mode 100644 index 000000000..b60303c47 --- /dev/null +++ b/src/draw/sdl/lv_draw_sdl_layer.h @@ -0,0 +1,55 @@ +/** + * @file lv_draw_sdl_refr.h + * + */ + +#ifndef LV_TEMPL_H +#define LV_TEMPL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "lv_draw_sdl.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +typedef struct _lv_draw_sdl_layer_ctx_t { + lv_draw_layer_ctx_t base; + + SDL_Texture * orig_target; + SDL_Texture * target; + SDL_Rect target_rect; + lv_draw_layer_flags_t flags; +} lv_draw_sdl_layer_ctx_t; +/********************** + * GLOBAL PROTOTYPES + **********************/ + +lv_draw_layer_ctx_t * lv_draw_sdl_layer_init(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags); + +void lv_draw_sdl_layer_blend(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * transform_ctx, + const lv_draw_img_dsc_t * draw_dsc); + +void lv_draw_sdl_layer_destroy(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx); + +void lv_draw_sdl_transform_areas_offset(lv_draw_sdl_ctx_t * ctx, bool has_composite, lv_area_t * apply_area, + lv_area_t * coords, lv_area_t * clip); +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_TEMPL_H*/ diff --git a/src/draw/sdl/lv_draw_sdl_priv.h b/src/draw/sdl/lv_draw_sdl_priv.h index 1f44c22ac..24a876218 100644 --- a/src/draw/sdl/lv_draw_sdl_priv.h +++ b/src/draw/sdl/lv_draw_sdl_priv.h @@ -35,6 +35,8 @@ typedef struct lv_draw_sdl_context_internals_t { lv_lru_t * texture_cache; SDL_Texture * mask; SDL_Texture * composition; + SDL_Texture * target_backup; + uint8_t transform_count; } lv_draw_sdl_context_internals_t; /********************** diff --git a/src/draw/sdl/lv_draw_sdl_rect.c b/src/draw/sdl/lv_draw_sdl_rect.c index 4df113efb..a303ac764 100644 --- a/src/draw/sdl/lv_draw_sdl_rect.c +++ b/src/draw/sdl/lv_draw_sdl_rect.c @@ -21,7 +21,7 @@ #include "lv_draw_sdl_composite.h" #include "lv_draw_sdl_mask.h" #include "lv_draw_sdl_stack_blur.h" -#include "lv_draw_sdl_img.h" +#include "lv_draw_sdl_layer.h" /********************* * DEFINES @@ -124,7 +124,11 @@ void lv_draw_sdl_draw_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * } /* Coords will be translated so coords will start at (0,0) */ lv_area_t t_coords = *coords, t_clip = *clip, apply_area, t_area; - lv_draw_sdl_composite_begin(ctx, coords, clip, &extension, dsc->blend_mode, &t_coords, &t_clip, &apply_area); + bool has_composite = lv_draw_sdl_composite_begin(ctx, coords, clip, &extension, dsc->blend_mode, &t_coords, &t_clip, + &apply_area); + + lv_draw_sdl_transform_areas_offset(ctx, has_composite, &apply_area, &t_coords, &t_clip); + bool has_content = _lv_area_intersect(&t_area, &t_coords, &t_clip); SDL_Rect clip_rect; diff --git a/src/draw/sw/lv_draw_sw.c b/src/draw/sw/lv_draw_sw.c index 376ec4d23..1c0c6d4a2 100644 --- a/src/draw/sw/lv_draw_sw.c +++ b/src/draw/sw/lv_draw_sw.c @@ -56,7 +56,12 @@ void lv_draw_sw_init_ctx(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx) #endif draw_sw_ctx->base_draw.wait_for_finish = lv_draw_sw_wait_for_finish; draw_sw_ctx->base_draw.buffer_copy = lv_draw_sw_buffer_copy; + draw_sw_ctx->base_draw.layer_init = lv_draw_sw_layer_create; + draw_sw_ctx->base_draw.layer_adjust = lv_draw_sw_layer_adjust; + draw_sw_ctx->base_draw.layer_blend = lv_draw_sw_layer_blend; + draw_sw_ctx->base_draw.layer_destroy = lv_draw_sw_layer_destroy; draw_sw_ctx->blend = lv_draw_sw_blend_basic; + draw_ctx->layer_instance_size = sizeof(lv_draw_sw_layer_ctx_t); } void lv_draw_sw_deinit_ctx(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx) @@ -96,7 +101,6 @@ void lv_draw_sw_buffer_copy(lv_draw_ctx_t * draw_ctx, dest_bufc += dest_stride; src_bufc += src_stride; } - } /********************** diff --git a/src/draw/sw/lv_draw_sw.h b/src/draw/sw/lv_draw_sw.h index 521deba7e..1618649cf 100644 --- a/src/draw/sw/lv_draw_sw.h +++ b/src/draw/sw/lv_draw_sw.h @@ -36,6 +36,13 @@ typedef struct { void (*blend)(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc); } lv_draw_sw_ctx_t; +typedef struct { + lv_draw_layer_ctx_t base_draw; + + uint32_t buf_size_bytes: 31; + uint32_t has_alpha : 1; +} lv_draw_sw_layer_ctx_t; + /********************** * GLOBAL PROTOTYPES **********************/ @@ -67,11 +74,21 @@ void lv_draw_sw_buffer_copy(lv_draw_ctx_t * draw_ctx, void * dest_buf, lv_coord_t dest_stride, const lv_area_t * dest_area, void * src_buf, lv_coord_t src_stride, const lv_area_t * src_area); - void lv_draw_sw_transform(lv_draw_ctx_t * draw_ctx, const lv_area_t * dest_area, const void * src_buf, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, const lv_draw_img_dsc_t * draw_dsc, lv_img_cf_t cf, lv_color_t * cbuf, lv_opa_t * abuf); +struct _lv_draw_layer_ctx_t * lv_draw_sw_layer_create(struct _lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags); + +void lv_draw_sw_layer_adjust(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags); + +void lv_draw_sw_layer_blend(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + const lv_draw_img_dsc_t * draw_dsc); + +void lv_draw_sw_layer_destroy(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx); + /*********************** * GLOBAL VARIABLES ***********************/ diff --git a/src/draw/sw/lv_draw_sw.mk b/src/draw/sw/lv_draw_sw.mk index 73dbac6b3..4625cbcfc 100644 --- a/src/draw/sw/lv_draw_sw.mk +++ b/src/draw/sw/lv_draw_sw.mk @@ -9,6 +9,7 @@ CSRCS += lv_draw_sw_line.c CSRCS += lv_draw_sw_polygon.c CSRCS += lv_draw_sw_rect.c CSRCS += lv_draw_sw_transform.c +CSRCS += lv_draw_sw_layer.c DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw diff --git a/src/draw/sw/lv_draw_sw_layer.c b/src/draw/sw/lv_draw_sw_layer.c new file mode 100644 index 000000000..a9461b51f --- /dev/null +++ b/src/draw/sw/lv_draw_sw_layer.c @@ -0,0 +1,152 @@ +/** + * @file lv_draw_sw_layer.h + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_draw_sw.h" +#include "../../hal/lv_hal_disp.h" +#include "../../misc/lv_area.h" +#include "../../core/lv_refr.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * GLOBAL VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + + +struct _lv_draw_layer_ctx_t * lv_draw_sw_layer_create(struct _lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags) +{ + if(LV_COLOR_SCREEN_TRANSP == 0 && (flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA)) { + LV_LOG_WARN("Rendering this widget needs LV_COLOR_SCREEN_TRANSP 1"); + return NULL; + } + + lv_draw_sw_layer_ctx_t * layer_sw_ctx = (lv_draw_sw_layer_ctx_t *) layer_ctx; + uint32_t px_size = flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA ? LV_IMG_PX_SIZE_ALPHA_BYTE : sizeof(lv_color_t); + if(flags & LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE) { + layer_sw_ctx->buf_size_bytes = LV_LAYER_SIMPLE_BUF_SIZE; + uint32_t full_size = lv_area_get_size(&layer_sw_ctx->base_draw.area_full) * px_size; + if(layer_sw_ctx->buf_size_bytes > full_size) layer_sw_ctx->buf_size_bytes = full_size; + layer_sw_ctx->base_draw.buf = lv_mem_alloc(layer_sw_ctx->buf_size_bytes); + if(layer_sw_ctx->base_draw.buf == NULL) { + LV_LOG_WARN("Cannot allocate %"LV_PRIu32" bytes for layer buffer. Allocating %"LV_PRIu32" bytes instead. (Reduced performance)", + (uint32_t)layer_sw_ctx->buf_size_bytes, (uint32_t)LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE * px_size); + layer_sw_ctx->buf_size_bytes = LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE; + layer_sw_ctx->base_draw.buf = lv_mem_alloc(layer_sw_ctx->buf_size_bytes); + if(layer_sw_ctx->base_draw.buf == NULL) { + lv_mem_free(layer_ctx); + return NULL; + } + } + layer_sw_ctx->base_draw.area_act = layer_sw_ctx->base_draw.area_full; + layer_sw_ctx->base_draw.area_act.y2 = layer_sw_ctx->base_draw.area_full.y1; + lv_coord_t w = lv_area_get_width(&layer_sw_ctx->base_draw.area_act); + layer_sw_ctx->base_draw.max_row_with_alpha = layer_sw_ctx->buf_size_bytes / w / LV_IMG_PX_SIZE_ALPHA_BYTE; + layer_sw_ctx->base_draw.max_row_with_no_alpha = layer_sw_ctx->buf_size_bytes / w / sizeof(lv_color_t); + } + else { + layer_sw_ctx->base_draw.area_act = layer_sw_ctx->base_draw.area_full; + layer_sw_ctx->buf_size_bytes = lv_area_get_size(&layer_sw_ctx->base_draw.area_full) * px_size; + layer_sw_ctx->base_draw.buf = lv_mem_alloc(layer_sw_ctx->buf_size_bytes); + lv_memset_00(layer_sw_ctx->base_draw.buf, layer_sw_ctx->buf_size_bytes); + layer_sw_ctx->has_alpha = flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA ? 1 : 0; + if(layer_sw_ctx->base_draw.buf == NULL) { + lv_mem_free(layer_ctx); + return NULL; + } + + draw_ctx->buf = layer_sw_ctx->base_draw.buf; + draw_ctx->buf_area = &layer_sw_ctx->base_draw.area_act; + draw_ctx->clip_area = &layer_sw_ctx->base_draw.area_act; + + lv_disp_t * disp_refr = _lv_refr_get_disp_refreshing(); + disp_refr->driver->screen_transp = flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA ? 1 : 0; + } + + return layer_ctx; +} + +void lv_draw_sw_layer_adjust(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags) +{ + + lv_draw_sw_layer_ctx_t * layer_sw_ctx = (lv_draw_sw_layer_ctx_t *) layer_ctx; + lv_disp_t * disp_refr = _lv_refr_get_disp_refreshing(); + if(flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA) { + lv_memset_00(layer_ctx->buf, layer_sw_ctx->buf_size_bytes); + layer_sw_ctx->has_alpha = 1; + disp_refr->driver->screen_transp = 1; + } + else { + layer_sw_ctx->has_alpha = 0; + disp_refr->driver->screen_transp = 0; + } + + draw_ctx->buf = layer_ctx->buf; + draw_ctx->buf_area = &layer_ctx->area_act; + draw_ctx->clip_area = &layer_ctx->area_act; +} + +void lv_draw_sw_layer_blend(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + const lv_draw_img_dsc_t * draw_dsc) +{ + lv_draw_sw_layer_ctx_t * layer_sw_ctx = (lv_draw_sw_layer_ctx_t *) layer_ctx; + + lv_img_dsc_t img; + img.data = draw_ctx->buf; + img.header.always_zero = 0; + img.header.w = lv_area_get_width(draw_ctx->buf_area); + img.header.h = lv_area_get_height(draw_ctx->buf_area); + img.header.cf = layer_sw_ctx->has_alpha ? LV_IMG_CF_TRUE_COLOR_ALPHA : LV_IMG_CF_TRUE_COLOR; + lv_img_cache_invalidate_src(&img); + + /*Restore the original draw_ctx*/ + draw_ctx->buf = layer_ctx->original.buf; + draw_ctx->buf_area = layer_ctx->original.buf_area; + draw_ctx->clip_area = layer_ctx->original.clip_area; + lv_disp_t * disp_refr = _lv_refr_get_disp_refreshing(); + disp_refr->driver->screen_transp = layer_ctx->original.screen_transp; + + /*Blend the layer*/ + lv_draw_img(draw_ctx, draw_dsc, &layer_ctx->area_act, &img); + lv_draw_wait_for_finish(draw_ctx); +} + +void lv_draw_sw_layer_destroy(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx) +{ + LV_UNUSED(draw_ctx); + + lv_mem_free(layer_ctx->buf); +} + + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index 83633506f..8e778efcd 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -317,8 +317,6 @@ * and blend it as an image with the given opacity. * Note that `bg_opa`, `text_opa` etc don't require buffering into layer) * The widget can be buffered in smaller chunks to avoid using large buffers. - * `draw_area` (`lv_area_t` meaning the area to draw and `px_size` (size of a pixel in bytes) - * can be used the set the buffer size adaptively. * * - LV_LAYER_SIMPLE_BUF_SIZE: [bytes] the optimal target buffer size. LVGL will try to allocate it * - LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE: [bytes] used if `LV_LAYER_SIMPLE_BUF_SIZE` couldn't be allocated. @@ -338,7 +336,7 @@ #ifdef CONFIG_LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE #define LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE CONFIG_LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE #else - #define LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE LV_MAX(lv_area_get_width(&draw_area) * px_size, 2048) + #define LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE (3 * 1024) #endif #endif