diff options
| author | jacqueline <me@jacqueline.id.au> | 2023-06-01 15:41:47 +1000 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2023-06-01 15:41:47 +1000 |
| commit | dd27c3530432ea0b09f01e604bf577f31d8ef841 (patch) | |
| tree | bbf86cf81a78f0ff0b07f31f1c390db473f26fd3 /lib/lvgl/src/draw/sdl | |
| parent | 6fd588e970470b15936187980829916d0dbe77bb (diff) | |
| download | tangara-fw-dd27c3530432ea0b09f01e604bf577f31d8ef841.tar.gz | |
convert lvgl from submodule to a plain old directory
Diffstat (limited to 'lib/lvgl/src/draw/sdl')
27 files changed, 3943 insertions, 0 deletions
diff --git a/lib/lvgl b/lib/lvgl deleted file mode 160000 -Subproject 0732400e7b564dd0e7dc4a924619d8e19c5b23a diff --git a/lib/lvgl/src/draw/sdl/README.md b/lib/lvgl/src/draw/sdl/README.md new file mode 100644 index 00000000..4415ffac --- /dev/null +++ b/lib/lvgl/src/draw/sdl/README.md @@ -0,0 +1,28 @@ +# SDL_Renderer Based Drawing Functions + +In LVGL, drawing was performed by CPU. To improve drawing performance on platforms with GPU, +we should perform drawing operations on GPU if possible. + +This implementation has moved most bitmap blending and drawing procedures to utilize SDL_Renderer, +which takes advantages of hardware acceleration APIs like DirectX or OpenGL. + +This implementation can be also considered as a reference implementation, for contributors wants to +develop accelerated drawing functions with other APIs such as OpenGL/OpenGL ES. + +## Caveats +`lv_draw_arc`, `lv_draw_line` is not enabled, due to incomplete implementation. So lines and arcs will +have significant impact to drawing performances. + +Performance of this implementation still has room to improve. Or we should use more powerful APIs +such as OpenGL. + +## Notices for files + +### `lv_draw_sdl_stack_blur.c` + +Contains modified code from [android-stackblur](https://github.com/kikoso/android-stackblur) project. +Apache License 2.0 + +### `lv_draw_sdl_lru.c`/`lv_draw_sdl_lru.h` + +Contains modified code from [C-LRU-Cache](https://github.com/willcannings/C-LRU-Cache) project. No license defined.
\ No newline at end of file diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl.c new file mode 100644 index 00000000..e3cdf577 --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl.c @@ -0,0 +1,103 @@ +/** + * @file lv_draw_sdl.c + * + */ + +/********************* + * INCLUDES + *********************/ + + +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#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 + *********************/ +void lv_draw_sdl_draw_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords); + +lv_res_t lv_draw_sdl_img_core(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * draw_dsc, + const lv_area_t * coords, const void * src); + +void lv_draw_sdl_draw_letter(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p, + uint32_t letter); + +void lv_draw_sdl_draw_line(lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc, const lv_point_t * point1, + const lv_point_t * point2); + +void lv_draw_sdl_draw_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center, + uint16_t radius, uint16_t start_angle, uint16_t end_angle); + +void lv_draw_sdl_polygon(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_point_t * points, + uint16_t point_cnt); + +void lv_draw_sdl_draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords); + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_sdl_init_ctx(lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx) +{ + _lv_draw_sdl_utils_init(); + lv_memset_00(draw_ctx, sizeof(lv_draw_sdl_ctx_t)); + draw_ctx->draw_rect = lv_draw_sdl_draw_rect; + draw_ctx->draw_img = lv_draw_sdl_img_core; + draw_ctx->draw_letter = lv_draw_sdl_draw_letter; + draw_ctx->draw_line = lv_draw_sdl_draw_line; + 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)); + lv_memset_00(draw_ctx_sdl->internals, sizeof(lv_draw_sdl_context_internals_t)); + lv_draw_sdl_texture_cache_init(draw_ctx_sdl); +} + +void lv_draw_sdl_deinit_ctx(lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx) +{ + lv_draw_sdl_ctx_t * draw_ctx_sdl = (lv_draw_sdl_ctx_t *) draw_ctx; + lv_draw_sdl_texture_cache_deinit(draw_ctx_sdl); + lv_mem_free(draw_ctx_sdl->internals); + _lv_draw_sdl_utils_deinit(); +} + +SDL_Texture * lv_draw_sdl_create_screen_texture(SDL_Renderer * renderer, lv_coord_t hor, lv_coord_t ver) +{ + SDL_Texture * texture = SDL_CreateTexture(renderer, LV_DRAW_SDL_TEXTURE_FORMAT, SDL_TEXTUREACCESS_TARGET, hor, ver); + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + return texture; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_GPU_SDL*/ diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl.h b/lib/lvgl/src/draw/sdl/lv_draw_sdl.h new file mode 100644 index 00000000..9b44a7b2 --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl.h @@ -0,0 +1,96 @@ +/** + * @file lv_draw_sdl.h + * + */ + +#ifndef LV_DRAW_SDL_H +#define LV_DRAW_SDL_H + + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include LV_GPU_SDL_INCLUDE_PATH + +#include "../lv_draw.h" +#include "../../core/lv_disp.h" + +/********************* + * DEFINES + *********************/ + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define LV_DRAW_SDL_TEXTURE_FORMAT SDL_PIXELFORMAT_ARGB8888 +#else +#define LV_DRAW_SDL_TEXTURE_FORMAT SDL_PIXELFORMAT_RGBA8888 +#endif + +/********************** + * TYPEDEFS + **********************/ + +struct lv_draw_sdl_context_internals_t; + +typedef struct { + /** + * Render for display driver + */ + SDL_Renderer * renderer; + void * user_data; +} lv_draw_sdl_drv_param_t; + +typedef struct { + lv_draw_ctx_t base_draw; + SDL_Renderer * renderer; + struct lv_draw_sdl_context_internals_t * internals; +} lv_draw_sdl_ctx_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void lv_draw_sdl_init_ctx(lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx); + +/** + * @brief Free caches + * + */ +void lv_draw_sdl_deinit_ctx(lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx); + +SDL_Texture * lv_draw_sdl_create_screen_texture(SDL_Renderer * renderer, lv_coord_t hor, lv_coord_t ver); + +/*====================== + * Add/remove functions + *=====================*/ + +/*===================== + * Setter functions + *====================*/ + +/*===================== + * Getter functions + *====================*/ + +/*===================== + * Other functions + *====================*/ + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_GPU_SDL*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_SDL_H*/ diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl.mk b/lib/lvgl/src/draw/sdl/lv_draw_sdl.mk new file mode 100644 index 00000000..c5c28b66 --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl.mk @@ -0,0 +1,19 @@ +CSRCS += lv_draw_sdl.c +CSRCS += lv_draw_sdl_arc.c +CSRCS += lv_draw_sdl_bg.c +CSRCS += lv_draw_sdl_composite.c +CSRCS += lv_draw_sdl_img.c +CSRCS += lv_draw_sdl_label.c +CSRCS += lv_draw_sdl_line.c +CSRCS += lv_draw_sdl_mask.c +CSRCS += lv_draw_sdl_polygon.c +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 + +CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sdl" diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_arc.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_arc.c new file mode 100644 index 00000000..5786eaeb --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_arc.c @@ -0,0 +1,238 @@ +/** + * @file lv_draw_sdl_arc.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include "lv_draw_sdl.h" +#include "lv_draw_sdl_utils.h" +#include "lv_draw_sdl_texture_cache.h" +#include "lv_draw_sdl_composite.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +static void dump_masks(SDL_Texture * texture, const lv_area_t * coords, const int16_t * ids, int16_t ids_count, + const int16_t * caps); + +static void get_cap_area(int16_t angle, lv_coord_t thickness, uint16_t radius, const lv_point_t * center, + lv_area_t * out); + +/********************** + * GLOBAL FUNCTIONS + **********************/ +void lv_draw_sdl_draw_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center, + uint16_t radius, uint16_t start_angle, uint16_t end_angle) +{ + lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx; + + lv_area_t area_out; + area_out.x1 = center->x - radius; + area_out.y1 = center->y - radius; + area_out.x2 = center->x + radius - 1; /*-1 because the center already belongs to the left/bottom part*/ + area_out.y2 = center->y + radius - 1; + + lv_area_t draw_area; + if(!_lv_area_intersect(&draw_area, &area_out, draw_ctx->clip_area)) { + return; + } + + lv_area_t area_in; + lv_area_copy(&area_in, &area_out); + area_in.x1 += dsc->width; + area_in.y1 += dsc->width; + area_in.x2 -= dsc->width; + area_in.y2 -= dsc->width; + + + while(start_angle >= 360) start_angle -= 360; + while(end_angle >= 360) end_angle -= 360; + + int16_t mask_ids[3] = {LV_MASK_ID_INV, LV_MASK_ID_INV, LV_MASK_ID_INV}, mask_ids_count = 1; + int16_t cap_ids[2] = {LV_MASK_ID_INV, LV_MASK_ID_INV}; + + lv_draw_mask_radius_param_t mask_out_param; + lv_draw_mask_radius_init(&mask_out_param, &area_out, LV_RADIUS_CIRCLE, false); + mask_ids[0] = lv_draw_mask_add(&mask_out_param, NULL); + + lv_draw_mask_radius_param_t mask_in_param; + if(lv_area_get_width(&area_in) > 0 && lv_area_get_height(&area_in) > 0) { + lv_draw_mask_radius_init(&mask_in_param, &area_in, LV_RADIUS_CIRCLE, true); + mask_ids[1] = lv_draw_mask_add(&mask_in_param, NULL); + mask_ids_count++; + } + + lv_draw_mask_angle_param_t mask_angle_param; + if((start_angle - end_angle) % 360) { + lv_draw_mask_angle_init(&mask_angle_param, center->x, center->y, start_angle, end_angle); + mask_ids[2] = lv_draw_mask_add(&mask_angle_param, NULL); + mask_ids_count++; + } + + lv_draw_mask_radius_param_t cap_start_param, cap_end_param; + if(mask_ids_count == 3 && dsc->rounded) { + lv_area_t start_area, end_area; + get_cap_area((int16_t) start_angle, dsc->width, radius, center, &start_area); + get_cap_area((int16_t) end_angle, dsc->width, radius, center, &end_area); + lv_draw_mask_radius_init(&cap_start_param, &start_area, dsc->width / 2, false); + cap_ids[0] = lv_draw_mask_add(&cap_start_param, NULL); + lv_draw_mask_radius_init(&cap_end_param, &end_area, dsc->width / 2, false); + cap_ids[1] = lv_draw_mask_add(&cap_end_param, NULL); + } + + lv_coord_t w = lv_area_get_width(&draw_area), h = lv_area_get_height(&draw_area); + SDL_Texture * texture = lv_draw_sdl_composite_texture_obtain(ctx, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_STREAM1, w, h); + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + dump_masks(texture, &draw_area, mask_ids, mask_ids_count, cap_ids[0] != LV_MASK_ID_INV ? cap_ids : NULL); + + lv_draw_mask_remove_id(mask_ids[0]); + lv_draw_mask_free_param(&mask_out_param); + + if(mask_ids_count > 1) { + lv_draw_mask_remove_id(mask_ids[1]); + lv_draw_mask_free_param(&mask_in_param); + } + + if(mask_ids_count > 2) { + lv_draw_mask_remove_id(mask_ids[2]); + lv_draw_mask_free_param(&mask_angle_param); + } + + if(cap_ids[0] != LV_MASK_ID_INV) { + lv_draw_mask_remove_id(cap_ids[0]); + lv_draw_mask_remove_id(cap_ids[1]); + lv_draw_mask_free_param(&cap_start_param); + lv_draw_mask_free_param(&cap_end_param); + } + + SDL_Rect srcrect = {0, 0, w, h}, dstrect; + lv_area_to_sdl_rect(&draw_area, &dstrect); + SDL_Color color; + lv_color_to_sdl_color(&dsc->color, &color); + SDL_SetTextureColorMod(texture, color.r, color.g, color.b); + SDL_SetTextureAlphaMod(texture, dsc->opa); + SDL_RenderCopy(ctx->renderer, texture, &srcrect, &dstrect); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void dump_masks(SDL_Texture * texture, const lv_area_t * coords, const int16_t * ids, int16_t ids_count, + const int16_t * caps) +{ + lv_coord_t w = lv_area_get_width(coords), h = lv_area_get_height(coords); + SDL_assert(w > 0 && h > 0); + SDL_Rect rect = {0, 0, w, h}; + uint8_t * pixels; + int pitch; + if(SDL_LockTexture(texture, &rect, (void **) &pixels, &pitch) != 0) return; + + lv_opa_t * line_buf = lv_mem_buf_get(rect.w); + for(lv_coord_t y = 0; y < rect.h; y++) { + lv_memset_ff(line_buf, rect.w); + lv_coord_t abs_x = (lv_coord_t) coords->x1, abs_y = (lv_coord_t)(y + coords->y1), len = (lv_coord_t) rect.w; + lv_draw_mask_res_t res; + res = lv_draw_mask_apply_ids(line_buf, abs_x, abs_y, len, ids, ids_count); + if(res == LV_DRAW_MASK_RES_TRANSP) { + lv_memset_00(&pixels[y * pitch], 4 * rect.w); + } + else if(res == LV_DRAW_MASK_RES_FULL_COVER) { + lv_memset_ff(&pixels[y * pitch], 4 * rect.w); + } + else { + for(int x = 0; x < rect.w; x++) { + uint8_t * pixel = &pixels[y * pitch + x * 4]; + *pixel = line_buf[x]; + pixel[1] = pixel[2] = pixel[3] = 0xFF; + } + } + if(caps) { + for(int i = 0; i < 2; i++) { + lv_memset_ff(line_buf, rect.w); + res = lv_draw_mask_apply_ids(line_buf, abs_x, abs_y, len, &caps[i], 1); + if(res == LV_DRAW_MASK_RES_TRANSP) { + /* Ignore */ + } + else if(res == LV_DRAW_MASK_RES_FULL_COVER) { + lv_memset_ff(&pixels[y * pitch], 4 * rect.w); + } + else { + for(int x = 0; x < rect.w; x++) { + uint8_t * pixel = &pixels[y * pitch + x * 4]; + uint16_t old_opa = line_buf[x] + *pixel; + *pixel = LV_MIN(old_opa, 0xFF); + pixel[1] = pixel[2] = pixel[3] = 0xFF; + } + } + } + } + } + lv_mem_buf_release(line_buf); + SDL_UnlockTexture(texture); +} + +static void get_cap_area(int16_t angle, lv_coord_t thickness, uint16_t radius, const lv_point_t * center, + lv_area_t * out) +{ + const uint8_t ps = 8; + const uint8_t pa = 127; + + int32_t thick_half = thickness / 2; + uint8_t thick_corr = (thickness & 0x01) ? 0 : 1; + + int32_t cir_x; + int32_t cir_y; + + cir_x = ((radius - thick_half) * lv_trigo_sin((int16_t)(90 - angle))) >> (LV_TRIGO_SHIFT - ps); + cir_y = ((radius - thick_half) * lv_trigo_sin(angle)) >> (LV_TRIGO_SHIFT - ps); + + /*Actually the center of the pixel need to be calculated so apply 1/2 px offset*/ + if(cir_x > 0) { + cir_x = (cir_x - pa) >> ps; + out->x1 = cir_x - thick_half + thick_corr; + out->x2 = cir_x + thick_half; + } + else { + cir_x = (cir_x + pa) >> ps; + out->x1 = cir_x - thick_half; + out->x2 = cir_x + thick_half - thick_corr; + } + + if(cir_y > 0) { + cir_y = (cir_y - pa) >> ps; + out->y1 = cir_y - thick_half + thick_corr; + out->y2 = cir_y + thick_half; + } + else { + cir_y = (cir_y + pa) >> ps; + out->y1 = cir_y - thick_half; + out->y2 = cir_y + thick_half - thick_corr; + } + lv_area_move(out, center->x, center->y); +} + +#endif /*LV_USE_GPU_SDL*/ diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_bg.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_bg.c new file mode 100644 index 00000000..48e0f6b1 --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_bg.c @@ -0,0 +1,106 @@ +/** + * @file lv_draw_sdl_bg.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include "../lv_draw_rect.h" +#include "../lv_draw_img.h" +#include "../lv_draw_label.h" +#include "../lv_draw_mask.h" +#include "../../core/lv_refr.h" +#include "lv_draw_sdl_utils.h" +#include "lv_draw_sdl_texture_cache.h" +#include "lv_draw_sdl_composite.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void draw_bg_color(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area, + const lv_draw_rect_dsc_t * dsc); + +static void draw_bg_img(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area, + const lv_draw_rect_dsc_t * dsc); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_sdl_draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords) +{ + const lv_area_t * clip = draw_ctx->clip_area; + lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx; + /* Coords will be translated so coords will start at (0,0) */ + lv_area_t t_area; + bool has_content = _lv_area_intersect(&t_area, coords, clip); + + /* Shadows and outlines will also draw in extended area */ + if(has_content) { + if(dsc->bg_img_src) { + draw_bg_img(ctx, coords, &t_area, dsc); + } + else { + draw_bg_color(ctx, coords, &t_area, dsc); + } + } + +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void draw_bg_color(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area, + const lv_draw_rect_dsc_t * dsc) +{ + SDL_Color bg_color; + lv_color_to_sdl_color(&dsc->bg_color, &bg_color); + SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_NONE); + SDL_SetRenderDrawColor(ctx->renderer, bg_color.r, bg_color.g, bg_color.b, dsc->bg_opa); + + SDL_Rect rect; + lv_area_to_sdl_rect(draw_area, &rect); + SDL_RenderFillRect(ctx->renderer, &rect); + + SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_BLEND); +} + +static void draw_bg_img(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area, + const lv_draw_rect_dsc_t * dsc) +{ + SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_NONE); + SDL_SetRenderDrawColor(ctx->renderer, 0, 0, 0, 0); + + SDL_Rect rect; + lv_area_to_sdl_rect(draw_area, &rect); + SDL_RenderFillRect(ctx->renderer, &rect); + + SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_BLEND); + lv_draw_rect((lv_draw_ctx_t *) ctx, dsc, coords); +} + +#endif /*LV_USE_GPU_SDL*/ diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_composite.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_composite.c new file mode 100644 index 00000000..4d0603d7 --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_composite.c @@ -0,0 +1,262 @@ +/** + * @file lv_draw_sdl_composite.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include "../../misc/lv_gc.h" +#include "../../core/lv_refr.h" +#include "lv_draw_sdl_composite.h" +#include "lv_draw_sdl_utils.h" +#include "lv_draw_sdl_priv.h" +#include "lv_draw_sdl_texture_cache.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + lv_sdl_cache_key_magic_t magic; + lv_draw_sdl_composite_texture_id_t type; +} composite_key_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static composite_key_t mask_key_create(lv_draw_sdl_composite_texture_id_t type); + +static lv_coord_t next_pow_of_2(lv_coord_t num); + +static void dump_masks(SDL_Texture * texture, const lv_area_t * coords); +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +bool lv_draw_sdl_composite_begin(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords_in, const lv_area_t * clip_in, + const lv_area_t * extension, lv_blend_mode_t blend_mode, lv_area_t * coords_out, + lv_area_t * clip_out, lv_area_t * apply_area) +{ + lv_area_t full_coords = *coords_in; + + /* Normalize full_coords */ + if(full_coords.x1 > full_coords.x2) { + lv_coord_t x2 = full_coords.x2; + full_coords.x2 = full_coords.x1; + full_coords.x1 = x2; + } + if(full_coords.y1 > full_coords.y2) { + lv_coord_t y2 = full_coords.y2; + full_coords.y2 = full_coords.y1; + full_coords.y1 = y2; + } + + if(extension) { + full_coords.x1 -= extension->x1; + full_coords.x2 += extension->x2; + full_coords.y1 -= extension->y1; + full_coords.y2 += extension->y2; + } + + if(!_lv_area_intersect(apply_area, &full_coords, clip_in)) return false; + bool has_mask = lv_draw_mask_is_any(apply_area); + + const bool draw_mask = has_mask && LV_GPU_SDL_CUSTOM_BLEND_MODE; + 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 && 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 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 is not working properly, so we overwrite the target with solid color */ + SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_NONE); + SDL_RenderFillRect(ctx->renderer, NULL); + SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_BLEND); +#if LV_GPU_SDL_CUSTOM_BLEND_MODE + internals->mask = lv_draw_sdl_composite_texture_obtain(ctx, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_STREAM0, w, h); + dump_masks(internals->mask, apply_area); +#endif + } + else if(has_mask) { + /* Fallback mask handling. This will at least make bars looks less bad */ + for(uint8_t i = 0; i < _LV_MASK_MAX_NUM; i++) { + _lv_draw_mask_common_dsc_t * comm_param = LV_GC_ROOT(_lv_draw_mask_list[i]).param; + if(comm_param == NULL) continue; + switch(comm_param->type) { + case LV_DRAW_MASK_TYPE_RADIUS: { + const lv_draw_mask_radius_param_t * param = (const lv_draw_mask_radius_param_t *) comm_param; + if(param->cfg.outer) break; + _lv_area_intersect(clip_out, apply_area, ¶m->cfg.rect); + break; + } + default: + break; + } + } + } + return has_mask; +} + +void lv_draw_sdl_composite_end(lv_draw_sdl_ctx_t * ctx, const lv_area_t * apply_area, lv_blend_mode_t blend_mode) +{ + lv_draw_sdl_context_internals_t * internals = ctx->internals; + SDL_Rect src_rect = {0, 0, lv_area_get_width(apply_area), lv_area_get_height(apply_area)}; +#if LV_GPU_SDL_CUSTOM_BLEND_MODE + if(internals->mask) { + SDL_BlendMode mode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, + SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ZERO, + SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDOPERATION_ADD); + SDL_SetTextureBlendMode(internals->mask, mode); + SDL_RenderCopy(ctx->renderer, internals->mask, &src_rect, &src_rect); + } +#endif + + /* Shapes are drawn on composite layer when mask or blend mode is present */ + if(internals->composition) { + SDL_Rect dst_rect; + lv_area_to_sdl_rect(apply_area, &dst_rect); + + SDL_SetRenderTarget(ctx->renderer, internals->target_backup); + switch(blend_mode) { + case LV_BLEND_MODE_NORMAL: + SDL_SetTextureBlendMode(internals->composition, SDL_BLENDMODE_BLEND); + break; + case LV_BLEND_MODE_ADDITIVE: + SDL_SetTextureBlendMode(internals->composition, SDL_BLENDMODE_ADD); + break; +#if LV_GPU_SDL_CUSTOM_BLEND_MODE + case LV_BLEND_MODE_SUBTRACTIVE: { + SDL_BlendMode mode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE, + SDL_BLENDOPERATION_SUBTRACT, SDL_BLENDFACTOR_ONE, + SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_SUBTRACT); + SDL_SetTextureBlendMode(internals->composition, mode); + break; + } + case LV_BLEND_MODE_MULTIPLY: { + SDL_BlendMode mode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_SRC_COLOR, + SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ZERO, + SDL_BLENDFACTOR_DST_ALPHA, SDL_BLENDOPERATION_ADD); + SDL_SetTextureBlendMode(internals->composition, mode); + break; + } +#endif + default: + LV_LOG_WARN("Doesn't support blend mode %d", blend_mode); + SDL_SetTextureBlendMode(internals->composition, SDL_BLENDMODE_BLEND); + /* Unsupported yet */ + break; + } + SDL_RenderCopy(ctx->renderer, internals->composition, &src_rect, &dst_rect); + } + + 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, + lv_coord_t w, lv_coord_t h) +{ + lv_point_t * tex_size = NULL; + composite_key_t mask_key = mask_key_create(id); + SDL_Texture * result = lv_draw_sdl_texture_cache_get_with_userdata(ctx, &mask_key, sizeof(composite_key_t), NULL, + (void **) &tex_size); + 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_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); + tex_size = lv_mem_alloc(sizeof(lv_point_t)); + tex_size->x = tex_size->y = size; + lv_draw_sdl_texture_cache_put_advanced(ctx, &mask_key, sizeof(composite_key_t), result, tex_size, lv_mem_free, 0); + } + return result; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static composite_key_t mask_key_create(lv_draw_sdl_composite_texture_id_t type) +{ + composite_key_t key; + /* VERY IMPORTANT! Padding between members is uninitialized, so we have to wipe them manually */ + SDL_memset(&key, 0, sizeof(key)); + key.magic = LV_GPU_CACHE_KEY_MAGIC_MASK; + key.type = type; + return key; +} + +static lv_coord_t next_pow_of_2(lv_coord_t num) +{ + lv_coord_t n = 128; + while(n < num && n < 16384) { + n = n << 1; + } + return n; +} + +static void dump_masks(SDL_Texture * texture, const lv_area_t * coords) +{ + lv_coord_t w = lv_area_get_width(coords), h = lv_area_get_height(coords); + SDL_assert(w > 0 && h > 0); + SDL_Rect rect = {0, 0, w, h}; + uint8_t * pixels; + int pitch; + if(SDL_LockTexture(texture, &rect, (void **) &pixels, &pitch) != 0) return; + + lv_opa_t * line_buf = lv_mem_buf_get(rect.w); + for(lv_coord_t y = 0; y < rect.h; y++) { + lv_memset_ff(line_buf, rect.w); + lv_coord_t abs_x = (lv_coord_t) coords->x1, abs_y = (lv_coord_t)(y + coords->y1), len = (lv_coord_t) rect.w; + lv_draw_mask_res_t res; + res = lv_draw_mask_apply(line_buf, abs_x, abs_y, len); + if(res == LV_DRAW_MASK_RES_TRANSP) { + lv_memset_00(&pixels[y * pitch], 4 * rect.w); + } + else if(res == LV_DRAW_MASK_RES_FULL_COVER) { + lv_memset_ff(&pixels[y * pitch], 4 * rect.w); + } + else { + for(int x = 0; x < rect.w; x++) { + const size_t idx = y * pitch + x * 4; + pixels[idx] = line_buf[x]; + pixels[idx + 1] = pixels[idx + 2] = pixels[idx + 3] = 0xFF; + } + } + } + lv_mem_buf_release(line_buf); + SDL_UnlockTexture(texture); +} + +#endif /*LV_USE_GPU_SDL*/ diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_composite.h b/lib/lvgl/src/draw/sdl/lv_draw_sdl_composite.h new file mode 100644 index 00000000..72a2daef --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_composite.h @@ -0,0 +1,73 @@ +/** + * @file lv_draw_sdl_composite.h + * + */ + +#ifndef LV_DRAW_SDL_COMPOSITE_H +#define LV_DRAW_SDL_COMPOSITE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#include LV_GPU_SDL_INCLUDE_PATH + +#include "lv_draw_sdl.h" +#include "../../misc/lv_area.h" +#include "../../misc/lv_color.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef enum lv_draw_sdl_composite_texture_id_t { + LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_STREAM0, + 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; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Begin drawing with mask. Render target will be switched to a temporary texture, + * and drawing coordinates may get clipped or translated + * @param coords_in Original coordinates + * @param clip_in Original clip area + * @param extension Useful for shadows or outlines, can be NULL + * @param coords_out Translated coords + * @param clip_out Translated clip area + * @param apply_area Area of actual composited texture will be drawn + * @return true if there are any mask needs to be drawn, false otherwise + */ +bool lv_draw_sdl_composite_begin(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords_in, const lv_area_t * clip_in, + const lv_area_t * extension, lv_blend_mode_t blend_mode, lv_area_t * coords_out, + lv_area_t * clip_out, lv_area_t * apply_area); + +void lv_draw_sdl_composite_end(lv_draw_sdl_ctx_t * ctx, const lv_area_t * apply_area, lv_blend_mode_t blend_mode); + +SDL_Texture * lv_draw_sdl_composite_texture_obtain(lv_draw_sdl_ctx_t * ctx, lv_draw_sdl_composite_texture_id_t id, + lv_coord_t w, lv_coord_t h); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_SDL_COMPOSITE_H*/ diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_img.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_img.c new file mode 100644 index 00000000..3c955d84 --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_img.c @@ -0,0 +1,467 @@ +/** + * @file lv_draw_sdl_img.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include "../lv_draw_img.h" +#include "../lv_img_cache.h" +#include "../lv_draw_mask.h" +#include "../../misc/lv_lru.h" +#include "../../misc/lv_gc.h" + +#include "lv_draw_sdl_img.h" +#include "lv_draw_sdl_utils.h" +#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 + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + lv_sdl_cache_key_magic_t magic; + const SDL_Texture * texture; + lv_coord_t w, h, radius; +} lv_draw_img_rounded_key_t; + +enum { + ROUNDED_IMG_PART_LEFT = 0, + ROUNDED_IMG_PART_HCENTER = 1, + ROUNDED_IMG_PART_RIGHT = 2, + ROUNDED_IMG_PART_TOP = 3, + ROUNDED_IMG_PART_VCENTER = 4, + ROUNDED_IMG_PART_BOTTOM = 5, +}; + +enum { + ROUNDED_IMG_CORNER_TOP_LEFT = 0, + ROUNDED_IMG_CORNER_TOP_RIGHT = 1, + ROUNDED_IMG_CORNER_BOTTOM_RIGHT = 2, + ROUNDED_IMG_CORNER_BOTTOM_LEFT = 3, +}; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static SDL_Texture * upload_img_texture(SDL_Renderer * renderer, lv_img_decoder_dsc_t * dsc); + +static SDL_Texture * upload_img_texture_fallback(SDL_Renderer * renderer, lv_img_decoder_dsc_t * dsc); + +static bool check_mask_simple_radius(const lv_area_t * coords, lv_coord_t * radius); + +static void draw_img_simple(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture, const lv_draw_sdl_img_header_t * header, + const lv_draw_img_dsc_t * draw_dsc, const lv_area_t * coords, const lv_area_t * clip); + +static void draw_img_rounded(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture, const lv_draw_sdl_img_header_t * header, + const lv_draw_img_dsc_t * draw_dsc, const lv_area_t * coords, const lv_area_t * clip, + lv_coord_t radius); + +static SDL_Texture * img_rounded_frag_obtain(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture, + const lv_draw_sdl_img_header_t * header, int w, int h, lv_coord_t radius); + +static lv_draw_img_rounded_key_t rounded_key_create(const SDL_Texture * texture, lv_coord_t w, lv_coord_t h, + lv_coord_t radius); + +static void calc_draw_part(SDL_Texture * texture, const lv_draw_sdl_img_header_t * header, const lv_area_t * coords, + const lv_area_t * clip, SDL_Rect * clipped_src, SDL_Rect * clipped_dst); +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + + +static void apply_recolor_opa(SDL_Texture * texture, const lv_draw_img_dsc_t * draw_dsc); + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_res_t lv_draw_sdl_img_core(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * draw_dsc, + const lv_area_t * coords, const void * src) +{ + const lv_area_t * clip = draw_ctx->clip_area; + lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx; + + size_t key_size; + lv_draw_sdl_cache_key_head_img_t * key = lv_draw_sdl_texture_img_key_create(src, draw_dsc->frame_id, &key_size); + bool texture_found = false; + lv_draw_sdl_img_header_t * header = NULL; + SDL_Texture * texture = lv_draw_sdl_texture_cache_get_with_userdata(ctx, key, key_size, &texture_found, + (void **) &header); + if(!texture_found) { + lv_draw_sdl_img_load_texture(ctx, key, key_size, src, draw_dsc->frame_id, &texture, &header); + } + SDL_free(key); + if(!texture) { + return LV_RES_INV; + } + + lv_area_t zoomed_cords; + _lv_img_buf_get_transformed_area(&zoomed_cords, lv_area_get_width(coords), lv_area_get_height(coords), 0, + draw_dsc->zoom, &draw_dsc->pivot); + lv_area_move(&zoomed_cords, coords->x1, coords->y1); + + /* When in > 0, draw simple radius */ + lv_coord_t radius = 0; + /* 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)) { + 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); + + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + + if(radius > 0) { + draw_img_rounded(ctx, texture, header, draw_dsc, &t_coords, &t_clip, radius); + } + else { + draw_img_simple(ctx, texture, header, draw_dsc, &t_coords, &t_clip); + } + + lv_draw_sdl_composite_end(ctx, &apply_area, draw_dsc->blend_mode); + + return LV_RES_OK; +} + +static void calc_draw_part(SDL_Texture * texture, const lv_draw_sdl_img_header_t * header, const lv_area_t * coords, + const lv_area_t * clip, SDL_Rect * clipped_src, SDL_Rect * clipped_dst) +{ + double x = 0, y = 0, w, h; + if(SDL_RectEmpty(&header->rect)) { + Uint32 format = 0; + int access = 0, tw, th; + SDL_QueryTexture(texture, &format, &access, &tw, &th); + w = tw; + h = th; + } + else { + x = header->rect.x; + y = header->rect.y; + w = header->rect.w; + h = header->rect.h; + } + if(clip) { + lv_area_t clipped_area; + _lv_area_intersect(&clipped_area, coords, clip); + lv_area_to_sdl_rect(&clipped_area, clipped_dst); + } + else { + lv_area_to_sdl_rect(coords, clipped_dst); + } + lv_coord_t coords_w = lv_area_get_width(coords), coords_h = lv_area_get_height(coords); + clipped_src->x = (int)(x + (clipped_dst->x - coords->x1) * w / coords_w); + clipped_src->y = (int)(y + (clipped_dst->y - coords->y1) * h / coords_h); + clipped_src->w = (int)(w - (coords_w - clipped_dst->w) * w / coords_w); + clipped_src->h = (int)(h - (coords_h - clipped_dst->h) * h / coords_h); +} + +bool lv_draw_sdl_img_load_texture(lv_draw_sdl_ctx_t * ctx, lv_draw_sdl_cache_key_head_img_t * key, size_t key_size, + const void * src, int32_t frame_id, SDL_Texture ** texture, + lv_draw_sdl_img_header_t ** header) +{ + _lv_img_cache_entry_t * cdsc = _lv_img_cache_open(src, lv_color_white(), frame_id); + lv_draw_sdl_cache_flag_t tex_flags = 0; + SDL_Rect rect; + SDL_memset(&rect, 0, sizeof(SDL_Rect)); + if(cdsc) { + lv_img_decoder_dsc_t * dsc = &cdsc->dec_dsc; + if(dsc->user_data && SDL_memcmp(dsc->user_data, LV_DRAW_SDL_DEC_DSC_TEXTURE_HEAD, 8) == 0) { + lv_draw_sdl_dec_dsc_userdata_t * ptr = (lv_draw_sdl_dec_dsc_userdata_t *) dsc->user_data; + *texture = ptr->texture; + rect = ptr->rect; + if(ptr->texture_managed) { + tex_flags |= LV_DRAW_SDL_CACHE_FLAG_MANAGED; + } + ptr->texture_referenced = true; + } + else { + *texture = upload_img_texture(ctx->renderer, dsc); + } +#if LV_IMG_CACHE_DEF_SIZE == 0 + lv_img_decoder_close(dsc); +#endif + } + if(texture && cdsc) { + *header = SDL_malloc(sizeof(lv_draw_sdl_img_header_t)); + SDL_memcpy(&(*header)->base, &cdsc->dec_dsc.header, sizeof(lv_img_header_t)); + (*header)->rect = rect; + lv_draw_sdl_texture_cache_put_advanced(ctx, key, key_size, *texture, *header, SDL_free, tex_flags); + } + else { + lv_draw_sdl_texture_cache_put(ctx, key, key_size, NULL); + return false; + } + return true; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static SDL_Texture * upload_img_texture(SDL_Renderer * renderer, lv_img_decoder_dsc_t * dsc) +{ + if(!dsc->img_data) { + return upload_img_texture_fallback(renderer, dsc); + } + bool chroma_keyed = dsc->header.cf == (uint32_t) LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED; + int h = (int) dsc->header.h; + int w = (int) dsc->header.w; + void * data = (void *) dsc->img_data; + Uint32 rmask = 0x00FF0000; + Uint32 gmask = 0x0000FF00; + Uint32 bmask = 0x000000FF; + Uint32 amask = 0xFF000000; + if(chroma_keyed) { + amask = 0x00; + } + SDL_Surface * surface = SDL_CreateRGBSurfaceFrom(data, w, h, LV_COLOR_DEPTH, w * LV_COLOR_DEPTH / 8, + rmask, gmask, bmask, amask); + SDL_SetColorKey(surface, chroma_keyed, lv_color_to32(LV_COLOR_CHROMA_KEY)); + SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, surface); + SDL_FreeSurface(surface); + return texture; +} + +static SDL_Texture * upload_img_texture_fallback(SDL_Renderer * renderer, lv_img_decoder_dsc_t * dsc) +{ + lv_coord_t h = (lv_coord_t) dsc->header.h; + lv_coord_t w = (lv_coord_t) dsc->header.w; + uint8_t * data = lv_mem_buf_get(w * h * sizeof(lv_color_t)); + for(lv_coord_t y = 0; y < h; y++) { + lv_img_decoder_read_line(dsc, 0, y, w, &data[y * w * sizeof(lv_color_t)]); + } + Uint32 rmask = 0x00FF0000; + Uint32 gmask = 0x0000FF00; + Uint32 bmask = 0x000000FF; + Uint32 amask = 0xFF000000; + SDL_Surface * surface = SDL_CreateRGBSurfaceFrom(data, w, h, LV_COLOR_DEPTH, w * LV_COLOR_DEPTH / 8, + rmask, gmask, bmask, amask); + SDL_SetColorKey(surface, SDL_TRUE, lv_color_to32(LV_COLOR_CHROMA_KEY)); + SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, surface); + SDL_FreeSurface(surface); + lv_mem_buf_release(data); + return texture; +} + +/** + * Check if there is only one radius mask + * @param radius Set to radius value if the only mask is a radius mask + * @return true if the only mask is a radius mask + */ +static bool check_mask_simple_radius(const lv_area_t * coords, lv_coord_t * radius) +{ + if(lv_draw_mask_get_cnt() != 1) return false; + for(uint8_t i = 0; i < _LV_MASK_MAX_NUM; i++) { + _lv_draw_mask_common_dsc_t * param = LV_GC_ROOT(_lv_draw_mask_list[i]).param; + if(param->type == LV_DRAW_MASK_TYPE_RADIUS) { + lv_draw_mask_radius_param_t * rparam = (lv_draw_mask_radius_param_t *) param; + if(rparam->cfg.outer) return false; + if(!_lv_area_is_equal(&rparam->cfg.rect, coords)) return false; + *radius = rparam->cfg.radius; + return true; + } + } + return false; +} + +static void draw_img_simple(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture, const lv_draw_sdl_img_header_t * header, + const lv_draw_img_dsc_t * draw_dsc, const lv_area_t * coords, const lv_area_t * clip) +{ + apply_recolor_opa(texture, draw_dsc); + SDL_Point pivot = {.x = draw_dsc->pivot.x, .y = draw_dsc->pivot.y}; + + /*Image needs to be rotated, so we have to use clip rect which is slower*/ + if(draw_dsc->angle != 0) { + /* No radius, set clip here */ + SDL_Rect clip_rect; + lv_area_to_sdl_rect(clip, &clip_rect); + SDL_RenderSetClipRect(ctx->renderer, &clip_rect); + } + SDL_Rect src_rect, dst_rect; + calc_draw_part(texture, header, coords, clip, &src_rect, &dst_rect); + SDL_RenderCopyEx(ctx->renderer, texture, &src_rect, &dst_rect, draw_dsc->angle, &pivot, SDL_FLIP_NONE); + if(draw_dsc->angle != 0) { + SDL_RenderSetClipRect(ctx->renderer, NULL); + } +} + + +static void draw_img_rounded(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture, const lv_draw_sdl_img_header_t * header, + const lv_draw_img_dsc_t * draw_dsc, const lv_area_t * coords, const lv_area_t * clip, + lv_coord_t radius) +{ + const int w = lv_area_get_width(coords), h = lv_area_get_height(coords); + lv_coord_t real_radius = LV_MIN3(radius, w, h); + SDL_Texture * frag = img_rounded_frag_obtain(ctx, texture, header, w, h, real_radius); + apply_recolor_opa(frag, draw_dsc); + lv_draw_sdl_rect_bg_frag_draw_corners(ctx, frag, real_radius, coords, clip, true); + + apply_recolor_opa(texture, draw_dsc); + + SDL_Rect src_rect, dst_rect; + /* Draw 3 parts */ + lv_area_t clip_tmp, part; + calc_draw_part(texture, header, coords, NULL, &src_rect, &dst_rect); + for(int i = w > h ? ROUNDED_IMG_PART_LEFT : ROUNDED_IMG_PART_TOP, j = i + 3; i <= j; i++) { + switch(i) { + case ROUNDED_IMG_PART_LEFT: + lv_area_set(&part, coords->x1, coords->y1 + radius, coords->x1 + radius - 1, coords->y2 - radius); + break; + case ROUNDED_IMG_PART_HCENTER: + lv_area_set(&part, coords->x1 + radius, coords->y1, coords->x2 - radius, coords->y2); + break; + case ROUNDED_IMG_PART_RIGHT: + lv_area_set(&part, coords->x2 - radius + 1, coords->y1 + radius, coords->x2, coords->y2 - radius); + break; + case ROUNDED_IMG_PART_TOP: + lv_area_set(&part, coords->x1 + radius, coords->y1, coords->x2 - radius, coords->y1 + radius - 1); + break; + case ROUNDED_IMG_PART_VCENTER: + lv_area_set(&part, coords->x1 + radius, coords->y2 - radius + 1, coords->x2 - radius, coords->y2); + break; + case ROUNDED_IMG_PART_BOTTOM: + lv_area_set(&part, coords->x1, coords->y1 + radius, coords->x2, coords->y2 - radius); + break; + default: + break; + } + if(!_lv_area_intersect(&clip_tmp, &part, clip)) continue; + SDL_Rect clip_rect; + lv_area_to_sdl_rect(&clip_tmp, &clip_rect); + SDL_RenderSetClipRect(ctx->renderer, &clip_rect); + SDL_RenderCopy(ctx->renderer, texture, &src_rect, &dst_rect); + } + SDL_RenderSetClipRect(ctx->renderer, NULL); +} + +static void apply_recolor_opa(SDL_Texture * texture, const lv_draw_img_dsc_t * draw_dsc) +{ + if(draw_dsc->recolor_opa > LV_OPA_TRANSP) { + /* Draw with mixed recolor */ + lv_color_t recolor = lv_color_mix(draw_dsc->recolor, lv_color_white(), draw_dsc->recolor_opa); + SDL_SetTextureColorMod(texture, recolor.ch.red, recolor.ch.green, recolor.ch.blue); + } + else { + /* Draw with no recolor */ + SDL_SetTextureColorMod(texture, 0xFF, 0xFF, 0xFF); + } + SDL_SetTextureAlphaMod(texture, draw_dsc->opa); +} + +static SDL_Texture * img_rounded_frag_obtain(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture, + const lv_draw_sdl_img_header_t * header, int w, int h, lv_coord_t radius) +{ + lv_draw_img_rounded_key_t key = rounded_key_create(texture, w, h, radius); + SDL_Texture * mask_frag = lv_draw_sdl_rect_bg_frag_obtain(ctx, radius); + SDL_Texture * img_frag = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL); + if(img_frag == NULL) { + const lv_coord_t full_frag_size = radius * 2 + 3; + img_frag = SDL_CreateTexture(ctx->renderer, LV_DRAW_SDL_TEXTURE_FORMAT, SDL_TEXTUREACCESS_TARGET, + full_frag_size, full_frag_size); + SDL_SetTextureBlendMode(img_frag, SDL_BLENDMODE_BLEND); + SDL_Texture * old_target = SDL_GetRenderTarget(ctx->renderer); + SDL_SetRenderTarget(ctx->renderer, img_frag); + SDL_SetRenderDrawColor(ctx->renderer, 0, 0, 0, 0); + /* SDL_RenderClear is not working properly, so we overwrite the target with solid color */ + SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_NONE); + SDL_RenderFillRect(ctx->renderer, NULL); + SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_BLEND); + + lv_area_t coords = {0, 0, w - 1, h - 1}, clip; + lv_area_t frag_coords = {0, 0, full_frag_size - 1, full_frag_size - 1}; + lv_draw_sdl_rect_bg_frag_draw_corners(ctx, mask_frag, radius, &frag_coords, NULL, false); + + SDL_SetTextureAlphaMod(texture, 0xFF); + SDL_SetTextureColorMod(texture, 0xFF, 0xFF, 0xFF); +#if LV_GPU_SDL_CUSTOM_BLEND_MODE + SDL_BlendMode blend_mode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO, + SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_DST_ALPHA, + SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD); + SDL_SetTextureBlendMode(texture, blend_mode); +#else + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_MOD); +#endif + SDL_Rect srcrect, cliprect, dstrect = {0, 0, radius, radius}; + + cliprect.w = cliprect.h = radius; + for(int i = 0; i <= ROUNDED_IMG_CORNER_BOTTOM_LEFT; i++) { + switch(i) { + case ROUNDED_IMG_CORNER_TOP_LEFT: + cliprect.x = 0; + cliprect.y = 0; + lv_area_align(&frag_coords, &coords, LV_ALIGN_TOP_LEFT, 0, 0); + break; + case ROUNDED_IMG_CORNER_TOP_RIGHT: + cliprect.x = full_frag_size - radius; + cliprect.y = 0; + lv_area_align(&frag_coords, &coords, LV_ALIGN_TOP_RIGHT, 0, 0); + break; + case ROUNDED_IMG_CORNER_BOTTOM_RIGHT: + cliprect.x = full_frag_size - radius; + cliprect.y = full_frag_size - radius; + lv_area_align(&frag_coords, &coords, LV_ALIGN_BOTTOM_RIGHT, 0, 0); + break; + case ROUNDED_IMG_CORNER_BOTTOM_LEFT: + cliprect.x = 0; + cliprect.y = full_frag_size - radius; + lv_area_align(&frag_coords, &coords, LV_ALIGN_BOTTOM_LEFT, 0, 0); + break; + default: + break; + } + calc_draw_part(texture, header, &coords, NULL, &srcrect, &dstrect); + SDL_RenderSetClipRect(ctx->renderer, &cliprect); + SDL_RenderCopy(ctx->renderer, texture, &srcrect, &dstrect); + } + SDL_RenderSetClipRect(ctx->renderer, NULL); + + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + + SDL_SetRenderTarget(ctx->renderer, old_target); + lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), img_frag); + } + return img_frag; +} + +static lv_draw_img_rounded_key_t rounded_key_create(const SDL_Texture * texture, lv_coord_t w, lv_coord_t h, + lv_coord_t radius) +{ + lv_draw_img_rounded_key_t key; + SDL_memset(&key, 0, sizeof(key)); + key.magic = LV_GPU_CACHE_KEY_MAGIC_IMG_ROUNDED_CORNERS; + key.texture = texture; + key.w = w; + key.h = h; + key.radius = radius; + return key; +} + +#endif /*LV_USE_GPU_SDL*/ diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_img.h b/lib/lvgl/src/draw/sdl/lv_draw_sdl_img.h new file mode 100644 index 00000000..0e270277 --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_img.h @@ -0,0 +1,72 @@ +/** + * @file lv_draw_sdl_img.h + * + */ + +#ifndef LV_DRAW_SDL_IMG_H +#define LV_DRAW_SDL_IMG_H + + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include LV_GPU_SDL_INCLUDE_PATH + +#include "../lv_draw.h" + +#include "lv_draw_sdl_texture_cache.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct lv_draw_sdl_img_header_t { + lv_img_header_t base; + SDL_Rect rect; +} lv_draw_sdl_img_header_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/*====================== + * Add/remove functions + *=====================*/ + +/*===================== + * Setter functions + *====================*/ + +/*===================== + * Getter functions + *====================*/ + +/*===================== + * Other functions + *====================*/ +bool lv_draw_sdl_img_load_texture(lv_draw_sdl_ctx_t * ctx, lv_draw_sdl_cache_key_head_img_t * key, size_t key_size, + const void * src, int32_t frame_id, SDL_Texture ** texture, + lv_draw_sdl_img_header_t ** header); +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_GPU_SDL*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_SDL_IMG_H*/ diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_label.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_label.c new file mode 100644 index 00000000..b8c79ae4 --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_label.c @@ -0,0 +1,176 @@ +/** + * @file lv_draw_sdl_label.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include LV_GPU_SDL_INCLUDE_PATH + +#include "../lv_draw_label.h" +#include "../../misc/lv_utils.h" + +#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 + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + lv_sdl_cache_key_magic_t magic; + const lv_font_t * font_p; + uint32_t letter; +} lv_font_glyph_key_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static lv_font_glyph_key_t font_key_glyph_create(const lv_font_t * font_p, uint32_t letter); + + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_sdl_draw_letter(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p, + uint32_t letter) +{ + const lv_area_t * clip_area = draw_ctx->clip_area; + const lv_font_t * font_p = dsc->font; + lv_opa_t opa = dsc->opa; + lv_color_t color = dsc->color; + if(opa < LV_OPA_MIN) return; + if(opa > LV_OPA_MAX) opa = LV_OPA_COVER; + + if(font_p == NULL) { + LV_LOG_WARN("lv_draw_letter: font is NULL"); + return; + } + + lv_font_glyph_dsc_t g; + bool g_ret = lv_font_get_glyph_dsc(font_p, &g, letter, '\0'); + if(g_ret == false) { + /*Add warning if the dsc is not found + *but do not print warning for non printable ASCII chars (e.g. '\n')*/ + if(letter >= 0x20 && + letter != 0xf8ff && /*LV_SYMBOL_DUMMY*/ + letter != 0x200c) { /*ZERO WIDTH NON-JOINER*/ + LV_LOG_WARN("lv_draw_letter: glyph dsc. not found for U+%X", letter); + + /* draw placeholder */ + lv_area_t glyph_coords; + lv_draw_rect_dsc_t glyph_dsc; + lv_coord_t begin_x = pos_p->x + g.ofs_x; + lv_coord_t begin_y = pos_p->y + g.ofs_y; + lv_area_set(&glyph_coords, begin_x, begin_y, begin_x + g.box_w, begin_y + g.box_h); + lv_draw_rect_dsc_init(&glyph_dsc); + glyph_dsc.bg_opa = LV_OPA_MIN; + glyph_dsc.outline_opa = LV_OPA_MIN; + glyph_dsc.shadow_opa = LV_OPA_MIN; + glyph_dsc.bg_img_opa = LV_OPA_MIN; + glyph_dsc.border_color = dsc->color; + glyph_dsc.border_width = 1; + draw_ctx->draw_rect(draw_ctx, &glyph_dsc, &glyph_coords); + } + return; + } + + /*Don't draw anything if the character is empty. E.g. space*/ + if((g.box_h == 0) || (g.box_w == 0)) return; + + int32_t pos_x = pos_p->x + g.ofs_x; + int32_t pos_y = pos_p->y + (font_p->line_height - font_p->base_line) - g.box_h - g.ofs_y; + + const lv_area_t letter_area = {pos_x, pos_y, pos_x + g.box_w - 1, pos_y + g.box_h - 1}; + lv_area_t draw_area; + + /*If the letter is completely out of mask don't draw it*/ + if(!_lv_area_intersect(&draw_area, &letter_area, clip_area)) { + return; + } + + lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx; + SDL_Renderer * renderer = ctx->renderer; + + lv_font_glyph_key_t glyph_key = font_key_glyph_create(font_p, letter); + bool glyph_found = false; + SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &glyph_key, sizeof(glyph_key), &glyph_found); + if(!glyph_found) { + if(g.resolved_font) { + font_p = g.resolved_font; + } + const uint8_t * bmp = lv_font_get_glyph_bitmap(font_p, letter); + uint8_t * buf = lv_mem_alloc(g.box_w * g.box_h); + lv_sdl_to_8bpp(buf, bmp, g.box_w, g.box_h, g.box_w, g.bpp); + SDL_Surface * mask = lv_sdl_create_opa_surface(buf, g.box_w, g.box_h, g.box_w); + texture = SDL_CreateTextureFromSurface(renderer, mask); + SDL_FreeSurface(mask); + lv_mem_free(buf); + lv_draw_sdl_texture_cache_put(ctx, &glyph_key, sizeof(glyph_key), texture); + } + if(!texture) { + return; + } + + lv_area_t t_letter = letter_area, t_clip = *clip_area, 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)) { + return; + } + SDL_Rect srcrect, dstrect; + lv_area_to_sdl_rect(&draw_area, &dstrect); + srcrect.x = draw_area.x1 - t_letter.x1; + srcrect.y = draw_area.y1 - t_letter.y1; + srcrect.w = dstrect.w; + srcrect.h = dstrect.h; + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + SDL_SetTextureAlphaMod(texture, opa); + SDL_SetTextureColorMod(texture, color.ch.red, color.ch.green, color.ch.blue); + SDL_RenderCopy(renderer, texture, &srcrect, &dstrect); + + lv_draw_sdl_composite_end(ctx, &apply_area, dsc->blend_mode); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static lv_font_glyph_key_t font_key_glyph_create(const lv_font_t * font_p, uint32_t letter) +{ + lv_font_glyph_key_t key; + /* VERY IMPORTANT! Padding between members is uninitialized, so we have to wipe them manually */ + SDL_memset(&key, 0, sizeof(key)); + key.magic = LV_GPU_CACHE_KEY_MAGIC_FONT_GLYPH; + key.font_p = font_p; + key.letter = letter; + return key; +} + +#endif /*LV_USE_GPU_SDL*/ diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_layer.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_layer.c new file mode 100644 index 00000000..ae5c327a --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_layer.c @@ -0,0 +1,141 @@ +/** + * @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; + + layer_ctx->max_row_with_alpha = target_h; + layer_ctx->max_row_with_no_alpha = target_h; + + SDL_SetTextureBlendMode(transform_ctx->target, SDL_BLENDMODE_BLEND); + SDL_SetRenderTarget(renderer, transform_ctx->target); + + /* SDL_RenderClear is not working properly, so we overwrite the target with solid color */ + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); + SDL_RenderFillRect(renderer, NULL); + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + + /* 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_SetTextureAlphaMod(transform_ctx->target, draw_dsc->opa); + 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/lib/lvgl/src/draw/sdl/lv_draw_sdl_layer.h b/lib/lvgl/src/draw/sdl/lv_draw_sdl_layer.h new file mode 100644 index 00000000..b60303c4 --- /dev/null +++ b/lib/lvgl/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/lib/lvgl/src/draw/sdl/lv_draw_sdl_line.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_line.c new file mode 100644 index 00000000..3a15d6d3 --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_line.c @@ -0,0 +1,157 @@ +/** + * @file lv_draw_sdl_line.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include "lv_draw_sdl.h" +#include "lv_draw_sdl_utils.h" +#include "lv_draw_sdl_texture_cache.h" +#include "lv_draw_sdl_composite.h" +#include "lv_draw_sdl_mask.h" + +/********************* + * DEFINES + *********************/ +#define ROUND_START 0x01 +#define ROUND_END 0x02 +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + lv_sdl_cache_key_magic_t magic; + lv_coord_t length; + lv_coord_t width; + uint8_t round; +} lv_draw_line_key_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +static lv_draw_line_key_t line_key_create(const lv_draw_line_dsc_t * dsc, lv_coord_t length); + +static SDL_Texture * line_texture_create(lv_draw_sdl_ctx_t * sdl_ctx, const lv_draw_line_dsc_t * dsc, + lv_coord_t length); + +/********************** + * GLOBAL FUNCTIONS + **********************/ +void lv_draw_sdl_draw_line(lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc, const lv_point_t * point1, + const lv_point_t * point2) +{ + lv_draw_sdl_ctx_t * sdl_ctx = (lv_draw_sdl_ctx_t *) draw_ctx; + SDL_Renderer * renderer = sdl_ctx->renderer; + lv_coord_t x1 = point1->x, x2 = point2->x, y1 = point1->y, y2 = point2->y; + double length = SDL_sqrt(SDL_pow(x2 - x1, 2) + SDL_pow(y2 - y1, 2)); + if(length - (long) length > 0.5) { + length = (long) length + 1; + } + + double angle = SDL_atan2(y2 - y1, x2 - x1) * 180 / M_PI; + lv_draw_line_key_t key = line_key_create(dsc, (lv_coord_t) length); + SDL_Texture * texture = lv_draw_sdl_texture_cache_get(sdl_ctx, &key, sizeof(key), NULL); + if(!texture) { + texture = line_texture_create(sdl_ctx, dsc, (lv_coord_t) length); + lv_draw_sdl_texture_cache_put(sdl_ctx, &key, sizeof(key), texture); + } + + lv_area_t coords = {x1, y1, x2, y2}; + const lv_area_t * clip = draw_ctx->clip_area; + + SDL_Rect coords_r, clip_r; + lv_area_to_sdl_rect(&coords, &coords_r); + lv_area_to_sdl_rect(clip, &clip_r); + + lv_area_t t_coords = coords, t_clip = *clip, apply_area; + lv_area_t extension = {dsc->width / 2, dsc->width / 2, dsc->width / 2, dsc->width / 2}; + lv_draw_sdl_composite_begin(sdl_ctx, &coords, clip, &extension, dsc->blend_mode, &t_coords, &t_clip, + &apply_area); + + SDL_Color color; + lv_color_to_sdl_color(&dsc->color, &color); + + SDL_SetTextureColorMod(texture, color.r, color.g, color.b); + SDL_SetTextureAlphaMod(texture, dsc->opa); + SDL_Rect srcrect = {0, 0, (int) length + dsc->width + 2, dsc->width + 2}, + dstrect = {t_coords.x1 - 1 - dsc->width / 2, t_coords.y1 - 1, srcrect.w, srcrect.h}; + SDL_Point center = {1 + dsc->width / 2, 1 + dsc->width / 2}; + + SDL_Rect clip_rect; + lv_area_to_sdl_rect(&t_clip, &clip_rect); + if(!SDL_RectEquals(&clip_rect, &dstrect) || angle != 0) { + SDL_RenderSetClipRect(renderer, &clip_rect); + } + SDL_RenderCopyEx(renderer, texture, &srcrect, &dstrect, angle, ¢er, 0); + SDL_RenderSetClipRect(renderer, NULL); + + lv_draw_sdl_composite_end(sdl_ctx, &apply_area, dsc->blend_mode); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static lv_draw_line_key_t line_key_create(const lv_draw_line_dsc_t * dsc, lv_coord_t length) +{ + lv_draw_line_key_t key; + lv_memset_00(&key, sizeof(lv_draw_line_key_t)); + key.magic = LV_GPU_CACHE_KEY_MAGIC_LINE; + key.length = length; + key.width = dsc->width; + key.round = (dsc->round_start ? ROUND_START : 0) | (dsc->round_end ? ROUND_END : 0); + return key; +} + +static SDL_Texture * line_texture_create(lv_draw_sdl_ctx_t * sdl_ctx, const lv_draw_line_dsc_t * dsc, lv_coord_t length) +{ + SDL_Texture * texture = SDL_CreateTexture(sdl_ctx->renderer, LV_DRAW_SDL_TEXTURE_FORMAT, SDL_TEXTUREACCESS_TARGET, + length + dsc->width + 2, dsc->width + 2); + SDL_Texture * target = SDL_GetRenderTarget(sdl_ctx->renderer); + SDL_SetRenderTarget(sdl_ctx->renderer, texture); + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(sdl_ctx->renderer, 0xFF, 0xFF, 0xFF, 0x0); + /* SDL_RenderClear is not working properly, so we overwrite the target with solid color */ + SDL_SetRenderDrawBlendMode(sdl_ctx->renderer, SDL_BLENDMODE_NONE); + SDL_RenderFillRect(sdl_ctx->renderer, NULL); + SDL_SetRenderDrawBlendMode(sdl_ctx->renderer, SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(sdl_ctx->renderer, 0xFF, 0xFF, 0xFF, 0xFF); + SDL_Rect line_rect = {1 + dsc->width / 2, 1, length, dsc->width}; + SDL_RenderFillRect(sdl_ctx->renderer, &line_rect); + if(dsc->round_start || dsc->round_end) { + lv_draw_mask_radius_param_t param; + lv_area_t round_area = {0, 0, dsc->width - 1, dsc->width - 1}; + lv_draw_mask_radius_init(¶m, &round_area, LV_RADIUS_CIRCLE, false); + + int16_t mask_id = lv_draw_mask_add(¶m, NULL); + SDL_Texture * round_texture = lv_draw_sdl_mask_dump_texture(sdl_ctx->renderer, &round_area, &mask_id, 1); + lv_draw_mask_remove_id(mask_id); + + SDL_Rect round_src = {0, 0, dsc->width, dsc->width}; + SDL_Rect round_dst = {line_rect.x - dsc->width / 2, 1, dsc->width, dsc->width}; + SDL_RenderCopy(sdl_ctx->renderer, round_texture, &round_src, &round_dst); + round_dst.x = line_rect.w + dsc->width / 2; + SDL_RenderCopy(sdl_ctx->renderer, round_texture, &round_src, &round_dst); + SDL_DestroyTexture(round_texture); + } + + SDL_SetRenderTarget(sdl_ctx->renderer, target); + return texture; +} + +#endif /*LV_USE_GPU_SDL*/ diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_mask.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_mask.c new file mode 100644 index 00000000..7bb5bbb3 --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_mask.c @@ -0,0 +1,84 @@ +/** + * @file lv_draw_sdl_mask.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include "../../misc/lv_gc.h" +#include "lv_draw_sdl_mask.h" +#include "lv_draw_sdl_utils.h" + +/********************* + * DEFINES + *********************/ +#ifndef HAVE_SDL_CUSTOM_BLEND_MODE + #define HAVE_SDL_CUSTOM_BLEND_MODE (SDL_VERSION_ATLEAST(2, 0, 6)) +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_opa_t * lv_draw_sdl_mask_dump_opa(const lv_area_t * coords, const int16_t * ids, int16_t ids_count) +{ + SDL_assert(coords->x2 >= coords->x1); + SDL_assert(coords->y2 >= coords->y1); + lv_coord_t w = lv_area_get_width(coords), h = lv_area_get_height(coords); + lv_opa_t * mask_buf = lv_mem_buf_get(w * h); + for(lv_coord_t y = 0; y < h; y++) { + lv_opa_t * line_buf = &mask_buf[y * w]; + lv_memset_ff(line_buf, w); + lv_coord_t abs_x = (lv_coord_t) coords->x1, abs_y = (lv_coord_t)(y + coords->y1), len = (lv_coord_t) w; + lv_draw_mask_res_t res; + if(ids) { + res = lv_draw_mask_apply_ids(line_buf, abs_x, abs_y, len, ids, ids_count); + } + else { + res = lv_draw_mask_apply(line_buf, abs_x, abs_y, len); + } + if(res == LV_DRAW_MASK_RES_TRANSP) { + lv_memset_00(line_buf, w); + } + } + return mask_buf; +} + +SDL_Texture * lv_draw_sdl_mask_dump_texture(SDL_Renderer * renderer, const lv_area_t * coords, const int16_t * ids, + int16_t ids_count) +{ + lv_coord_t w = lv_area_get_width(coords), h = lv_area_get_height(coords); + lv_opa_t * mask_buf = lv_draw_sdl_mask_dump_opa(coords, ids, ids_count); + SDL_Surface * surface = lv_sdl_create_opa_surface(mask_buf, w, h, w); + lv_mem_buf_release(mask_buf); + SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, surface); + SDL_FreeSurface(surface); + return texture; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_GPU_SDL*/ diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_mask.h b/lib/lvgl/src/draw/sdl/lv_draw_sdl_mask.h new file mode 100644 index 00000000..a562d73d --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_mask.h @@ -0,0 +1,51 @@ +/** + * @file lv_draw_sdl_mask.h + * + */ + +#ifndef LV_DRAW_SDL_MASK_H +#define LV_DRAW_SDL_MASK_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#include LV_GPU_SDL_INCLUDE_PATH + +#include "lv_draw_sdl.h" +#include "../../misc/lv_area.h" +#include "../../misc/lv_color.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +lv_opa_t * lv_draw_sdl_mask_dump_opa(const lv_area_t * coords, const int16_t * ids, int16_t ids_count); + +SDL_Texture * lv_draw_sdl_mask_dump_texture(SDL_Renderer * renderer, const lv_area_t * coords, const int16_t * ids, + int16_t ids_count); + + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_SDL_MASK_H*/ diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_polygon.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_polygon.c new file mode 100644 index 00000000..c5df50cb --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_polygon.c @@ -0,0 +1,133 @@ +/** + * @file lv_draw_sdl_polygon.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include "lv_draw_sdl.h" +#include "lv_draw_sdl_utils.h" +#include "lv_draw_sdl_texture_cache.h" +#include "lv_draw_sdl_composite.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +static void dump_masks(SDL_Texture * texture, const lv_area_t * coords); + +/********************** + * GLOBAL FUNCTIONS + **********************/ +void lv_draw_sdl_polygon(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_point_t * points, + uint16_t point_cnt) +{ + if(point_cnt < 3) return; + if(points == NULL) return; + + lv_draw_mask_polygon_param_t polygon_param; + lv_draw_mask_polygon_init(&polygon_param, points, point_cnt); + + if(polygon_param.cfg.point_cnt < 3) { + lv_draw_mask_free_param(&polygon_param); + return; + } + + lv_area_t poly_coords = {.x1 = LV_COORD_MAX, .y1 = LV_COORD_MAX, .x2 = LV_COORD_MIN, .y2 = LV_COORD_MIN}; + + uint16_t i; + for(i = 0; i < point_cnt; i++) { + poly_coords.x1 = LV_MIN(poly_coords.x1, polygon_param.cfg.points[i].x); + poly_coords.y1 = LV_MIN(poly_coords.y1, polygon_param.cfg.points[i].y); + poly_coords.x2 = LV_MAX(poly_coords.x2, polygon_param.cfg.points[i].x); + poly_coords.y2 = LV_MAX(poly_coords.y2, polygon_param.cfg.points[i].y); + } + + bool is_common; + lv_area_t draw_area; + is_common = _lv_area_intersect(&draw_area, &poly_coords, draw_ctx->clip_area); + if(!is_common) { + lv_draw_mask_free_param(&polygon_param); + return; + } + + lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx; + + int16_t mask_id = lv_draw_mask_add(&polygon_param, NULL); + + lv_coord_t w = lv_area_get_width(&draw_area), h = lv_area_get_height(&draw_area); + SDL_Texture * texture = lv_draw_sdl_composite_texture_obtain(ctx, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_STREAM1, w, h); + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + dump_masks(texture, &draw_area); + + lv_draw_mask_remove_id(mask_id); + lv_draw_mask_free_param(&polygon_param); + + SDL_Rect srcrect = {0, 0, w, h}, dstrect; + lv_area_to_sdl_rect(&draw_area, &dstrect); + SDL_Color color; + lv_color_to_sdl_color(&draw_dsc->bg_color, &color); + SDL_SetTextureColorMod(texture, color.r, color.g, color.b); + SDL_SetTextureAlphaMod(texture, draw_dsc->bg_opa); + SDL_RenderCopy(ctx->renderer, texture, &srcrect, &dstrect); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void dump_masks(SDL_Texture * texture, const lv_area_t * coords) +{ + lv_coord_t w = lv_area_get_width(coords), h = lv_area_get_height(coords); + SDL_assert(w > 0 && h > 0); + SDL_Rect rect = {0, 0, w, h}; + uint8_t * pixels; + int pitch; + if(SDL_LockTexture(texture, &rect, (void **) &pixels, &pitch) != 0) return; + + lv_opa_t * line_buf = lv_mem_buf_get(rect.w); + for(lv_coord_t y = 0; y < rect.h; y++) { + lv_memset_ff(line_buf, rect.w); + lv_coord_t abs_x = (lv_coord_t) coords->x1, abs_y = (lv_coord_t)(y + coords->y1), len = (lv_coord_t) rect.w; + lv_draw_mask_res_t res; + res = lv_draw_mask_apply(line_buf, abs_x, abs_y, len); + if(res == LV_DRAW_MASK_RES_TRANSP) { + lv_memset_00(&pixels[y * pitch], 4 * rect.w); + } + else if(res == LV_DRAW_MASK_RES_FULL_COVER) { + lv_memset_ff(&pixels[y * pitch], 4 * rect.w); + } + else { + for(int x = 0; x < rect.w; x++) { + uint8_t * pixel = &pixels[y * pitch + x * 4]; + *pixel = line_buf[x]; + pixel[1] = pixel[2] = pixel[3] = 0xFF; + } + } + } + lv_mem_buf_release(line_buf); + SDL_UnlockTexture(texture); +} + +#endif /*LV_USE_GPU_SDL*/ diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_priv.h b/lib/lvgl/src/draw/sdl/lv_draw_sdl_priv.h new file mode 100644 index 00000000..24a87621 --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_priv.h @@ -0,0 +1,72 @@ +/** + * @file lv_draw_sdl_priv.h + * + */ + +#ifndef LV_DRAW_SDL_PRIV_H +#define LV_DRAW_SDL_PRIV_H + + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include LV_GPU_SDL_INCLUDE_PATH + +#include "../lv_draw.h" +#include "../../misc/lv_lru.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +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; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/*====================== + * Add/remove functions + *=====================*/ + +/*===================== + * Setter functions + *====================*/ + +/*===================== + * Getter functions + *====================*/ + +/*===================== + * Other functions + *====================*/ + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_GPU_SDL*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_SDL_PRIV_H*/ diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_rect.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_rect.c new file mode 100644 index 00000000..a303ac76 --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_rect.c @@ -0,0 +1,712 @@ +/** + * @file lv_draw_sdl_rect.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include "../lv_draw_rect.h" +#include "../lv_draw_img.h" +#include "../lv_draw_label.h" +#include "../lv_draw_mask.h" +#include "../../core/lv_refr.h" +#include "lv_draw_sdl_utils.h" +#include "lv_draw_sdl_texture_cache.h" +#include "lv_draw_sdl_composite.h" +#include "lv_draw_sdl_mask.h" +#include "lv_draw_sdl_stack_blur.h" +#include "lv_draw_sdl_layer.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + lv_sdl_cache_key_magic_t magic; + lv_coord_t radius; + lv_coord_t size; +} lv_draw_rect_bg_key_t; + +typedef struct { + lv_sdl_cache_key_magic_t magic; + lv_coord_t radius; + lv_coord_t size; + lv_coord_t blur; +} lv_draw_rect_shadow_key_t; + +typedef struct { + lv_sdl_cache_key_magic_t magic; + lv_coord_t rout, rin; + lv_area_t offsets; +} lv_draw_rect_border_key_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void draw_bg_color(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area, + const lv_draw_rect_dsc_t * dsc); + +static void draw_bg_img(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area, + const lv_draw_rect_dsc_t * dsc); + +static void draw_border(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area, + const lv_draw_rect_dsc_t * dsc); + +static void draw_shadow(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip, + const lv_draw_rect_dsc_t * dsc); + +static void draw_outline(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip, + const lv_draw_rect_dsc_t * dsc); + +static void draw_border_generic(lv_draw_sdl_ctx_t * ctx, const lv_area_t * outer_area, const lv_area_t * inner_area, + const lv_area_t * clip, lv_coord_t rout, lv_coord_t rin, lv_color_t color, lv_opa_t opa, + lv_blend_mode_t blend_mode); + +static void frag_render_borders(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size, + const lv_area_t * coords, const lv_area_t * clipped, bool full); + +static void frag_render_center(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size, + const lv_area_t * coords, const lv_area_t * clipped, bool full); + +static lv_draw_rect_bg_key_t rect_bg_key_create(lv_coord_t radius, lv_coord_t size); + +static lv_draw_rect_shadow_key_t rect_shadow_key_create(lv_coord_t radius, lv_coord_t size, lv_coord_t blur); + +static lv_draw_rect_border_key_t rect_border_key_create(lv_coord_t rout, lv_coord_t rin, const lv_area_t * outer_area, + const lv_area_t * inner_area); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ +#define SKIP_BORDER(dsc) ((dsc)->border_opa <= LV_OPA_MIN || (dsc)->border_width == 0 || (dsc)->border_side == LV_BORDER_SIDE_NONE || (dsc)->border_post) +#define SKIP_SHADOW(dsc) ((dsc)->shadow_width == 0 || (dsc)->shadow_opa <= LV_OPA_MIN || ((dsc)->shadow_width == 1 && (dsc)->shadow_spread <= 0 && (dsc)->shadow_ofs_x == 0 && (dsc)->shadow_ofs_y == 0)) +#define SKIP_IMAGE(dsc) ((dsc)->bg_img_src == NULL || (dsc)->bg_img_opa <= LV_OPA_MIN) +#define SKIP_OUTLINE(dsc) ((dsc)->outline_opa <= LV_OPA_MIN || (dsc)->outline_width == 0) + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_sdl_draw_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords) +{ + const lv_area_t * clip = draw_ctx->clip_area; + lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx; + + lv_area_t extension = {0, 0, 0, 0}; + if(!SKIP_SHADOW(dsc)) { + lv_coord_t ext = (lv_coord_t)(dsc->shadow_spread - dsc->shadow_width / 2 + 1); + extension.x1 = LV_MAX(extension.x1, -dsc->shadow_ofs_x + ext); + extension.x2 = LV_MAX(extension.x2, dsc->shadow_ofs_x + ext); + extension.y1 = LV_MAX(extension.y1, -dsc->shadow_ofs_y + ext); + extension.y2 = LV_MAX(extension.y2, dsc->shadow_ofs_y + ext); + } + if(!SKIP_OUTLINE(dsc)) { + lv_coord_t ext = (lv_coord_t)(dsc->outline_pad - 1 + dsc->outline_width); + extension.x1 = LV_MAX(extension.x1, ext); + extension.x2 = LV_MAX(extension.x2, ext); + extension.y1 = LV_MAX(extension.y1, ext); + extension.y2 = LV_MAX(extension.y2, ext); + } + /* Coords will be translated so coords will start at (0,0) */ + lv_area_t t_coords = *coords, t_clip = *clip, apply_area, t_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; + lv_area_to_sdl_rect(&t_clip, &clip_rect); + draw_shadow(ctx, &t_coords, &t_clip, dsc); + /* Shadows and outlines will also draw in extended area */ + if(has_content) { + draw_bg_color(ctx, &t_coords, &t_area, dsc); + draw_bg_img(ctx, &t_coords, &t_area, dsc); + draw_border(ctx, &t_coords, &t_area, dsc); + } + draw_outline(ctx, &t_coords, &t_clip, dsc); + + lv_draw_sdl_composite_end(ctx, &apply_area, dsc->blend_mode); +} + +SDL_Texture * lv_draw_sdl_rect_bg_frag_obtain(lv_draw_sdl_ctx_t * ctx, lv_coord_t radius) +{ + lv_draw_rect_bg_key_t key = rect_bg_key_create(radius, radius); + lv_area_t coords = {0, 0, radius * 2 - 1, radius * 2 - 1}; + lv_area_t coords_frag = {0, 0, radius - 1, radius - 1}; + SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL); + if(texture == NULL) { + lv_draw_mask_radius_param_t mask_rout_param; + lv_draw_mask_radius_init(&mask_rout_param, &coords, radius, false); + int16_t mask_id = lv_draw_mask_add(&mask_rout_param, NULL); + texture = lv_draw_sdl_mask_dump_texture(ctx->renderer, &coords_frag, &mask_id, 1); + lv_draw_mask_remove_id(mask_id); + SDL_assert(texture); + lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), texture); + } + return texture; +} + +void lv_draw_sdl_rect_bg_frag_draw_corners(lv_draw_sdl_ctx_t * ctx, SDL_Texture * frag, lv_coord_t frag_size, + const lv_area_t * coords, const lv_area_t * clip, bool full) +{ + if(!clip) clip = coords; + lv_area_t corner_area, dst_area; + /* Upper left */ + corner_area.x1 = coords->x1; + corner_area.y1 = coords->y1; + corner_area.x2 = coords->x1 + frag_size - 1; + corner_area.y2 = coords->y1 + frag_size - 1; + if(_lv_area_intersect(&dst_area, &corner_area, clip)) { + SDL_Rect dst_rect; + lv_area_to_sdl_rect(&dst_area, &dst_rect); + + lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area); + lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1), sy = (lv_coord_t)(dst_area.y1 - corner_area.y1); + SDL_Rect src_rect = {sx, sy, dw, dh}; + SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect); + } + /* Upper right, clip right edge if too big */ + corner_area.x1 = LV_MAX(coords->x2 - frag_size + 1, coords->x1 + frag_size); + corner_area.x2 = coords->x2; + if(_lv_area_intersect(&dst_area, &corner_area, clip)) { + SDL_Rect dst_rect; + lv_area_to_sdl_rect(&dst_area, &dst_rect); + + lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area); + if(full) { + lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1), + sy = (lv_coord_t)(dst_area.y1 - corner_area.y1); + SDL_Rect src_rect = {frag_size + 3 + sx, sy, dw, dh}; + SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect); + } + else { + SDL_Rect src_rect = {corner_area.x2 - dst_area.x2, dst_area.y1 - corner_area.y1, dw, dh}; + SDL_RenderCopyEx(ctx->renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_HORIZONTAL); + } + } + /* Lower right, clip bottom edge if too big */ + corner_area.y1 = LV_MAX(coords->y2 - frag_size + 1, coords->y1 + frag_size); + corner_area.y2 = coords->y2; + if(_lv_area_intersect(&dst_area, &corner_area, clip)) { + SDL_Rect dst_rect; + lv_area_to_sdl_rect(&dst_area, &dst_rect); + + lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area); + if(full) { + lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1), + sy = (lv_coord_t)(dst_area.y1 - corner_area.y1); + SDL_Rect src_rect = {frag_size + 3 + sx, frag_size + 3 + sy, dw, dh}; + SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect); + } + else { + SDL_Rect src_rect = {corner_area.x2 - dst_area.x2, corner_area.y2 - dst_area.y2, dw, dh}; + SDL_RenderCopyEx(ctx->renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_HORIZONTAL | SDL_FLIP_VERTICAL); + } + } + /* Lower left, right edge should not be clip */ + corner_area.x1 = coords->x1; + corner_area.x2 = coords->x1 + frag_size - 1; + if(_lv_area_intersect(&dst_area, &corner_area, clip)) { + SDL_Rect dst_rect; + lv_area_to_sdl_rect(&dst_area, &dst_rect); + + lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area); + if(full) { + lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1), + sy = (lv_coord_t)(dst_area.y1 - corner_area.y1); + SDL_Rect src_rect = {sx, frag_size + 3 + sy, dw, dh}; + SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect); + } + else { + SDL_Rect src_rect = {dst_area.x1 - corner_area.x1, corner_area.y2 - dst_area.y2, dw, dh}; + SDL_RenderCopyEx(ctx->renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_VERTICAL); + } + } +} + + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void draw_bg_color(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area, + const lv_draw_rect_dsc_t * dsc) +{ + if(dsc->bg_opa == 0) { + return; + } + SDL_Color bg_color; + lv_color_to_sdl_color(&dsc->bg_color, &bg_color); + lv_coord_t radius = dsc->radius; + if(radius <= 0) { + SDL_Rect rect; + lv_area_to_sdl_rect(draw_area, &rect); + SDL_SetRenderDrawColor(ctx->renderer, bg_color.r, bg_color.g, bg_color.b, dsc->bg_opa); + SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_BLEND); + SDL_RenderFillRect(ctx->renderer, &rect); + return; + } + + /*A small texture with a quarter of the rect is enough*/ + lv_coord_t bg_w = lv_area_get_width(coords), bg_h = lv_area_get_height(coords); + lv_coord_t real_radius = LV_MIN3(bg_w / 2, bg_h / 2, radius); + SDL_Texture * texture = lv_draw_sdl_rect_bg_frag_obtain(ctx, real_radius); + + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + SDL_SetTextureAlphaMod(texture, dsc->bg_opa); + SDL_SetTextureColorMod(texture, bg_color.r, bg_color.g, bg_color.b); + lv_draw_sdl_rect_bg_frag_draw_corners(ctx, texture, real_radius, coords, draw_area, false); + frag_render_borders(ctx->renderer, texture, real_radius, coords, draw_area, false); + frag_render_center(ctx->renderer, texture, real_radius, coords, draw_area, false); +} + +static void draw_bg_img(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area, + const lv_draw_rect_dsc_t * dsc) +{ + if(SKIP_IMAGE(dsc)) return; + + lv_img_src_t src_type = lv_img_src_get_type(dsc->bg_img_src); + if(src_type == LV_IMG_SRC_SYMBOL) { + lv_point_t size; + lv_txt_get_size(&size, dsc->bg_img_src, dsc->bg_img_symbol_font, 0, 0, LV_COORD_MAX, LV_TEXT_FLAG_NONE); + lv_area_t a; + a.x1 = coords->x1 + lv_area_get_width(coords) / 2 - size.x / 2; + a.x2 = a.x1 + size.x - 1; + a.y1 = coords->y1 + lv_area_get_height(coords) / 2 - size.y / 2; + a.y2 = a.y1 + size.y - 1; + + lv_draw_label_dsc_t label_draw_dsc; + lv_draw_label_dsc_init(&label_draw_dsc); + label_draw_dsc.font = dsc->bg_img_symbol_font; + label_draw_dsc.color = dsc->bg_img_recolor; + label_draw_dsc.opa = dsc->bg_img_opa; + lv_draw_label((lv_draw_ctx_t *) ctx, &label_draw_dsc, &a, dsc->bg_img_src, NULL); + } + else { + lv_img_header_t header; + size_t key_size; + lv_draw_sdl_cache_key_head_img_t * key = lv_draw_sdl_texture_img_key_create(dsc->bg_img_src, 0, &key_size); + bool key_found; + lv_img_header_t * cache_header = NULL; + SDL_Texture * texture = lv_draw_sdl_texture_cache_get_with_userdata(ctx, key, key_size, &key_found, + (void **) &cache_header); + SDL_free(key); + if(texture) { + header = *cache_header; + } + else if(key_found || lv_img_decoder_get_info(dsc->bg_img_src, &header) != LV_RES_OK) { + /* When cache hit but with negative result, use default decoder. If still fail, return.*/ + LV_LOG_WARN("Couldn't read the background image"); + return; + } + + lv_draw_img_dsc_t img_dsc; + lv_draw_img_dsc_init(&img_dsc); + img_dsc.blend_mode = dsc->blend_mode; + img_dsc.recolor = dsc->bg_img_recolor; + img_dsc.recolor_opa = dsc->bg_img_recolor_opa; + img_dsc.opa = dsc->bg_img_opa; + img_dsc.frame_id = 0; + + int16_t radius_mask_id = LV_MASK_ID_INV; + lv_draw_mask_radius_param_t radius_param; + if(dsc->radius > 0) { + lv_draw_mask_radius_init(&radius_param, coords, dsc->radius, false); + radius_mask_id = lv_draw_mask_add(&radius_param, NULL); + } + + /*Center align*/ + if(dsc->bg_img_tiled == false) { + lv_area_t area; + area.x1 = coords->x1 + lv_area_get_width(coords) / 2 - header.w / 2; + area.y1 = coords->y1 + lv_area_get_height(coords) / 2 - header.h / 2; + area.x2 = area.x1 + header.w - 1; + area.y2 = area.y1 + header.h - 1; + + lv_draw_img((lv_draw_ctx_t *) ctx, &img_dsc, &area, dsc->bg_img_src); + } + else { + lv_area_t area; + area.y1 = coords->y1; + area.y2 = area.y1 + header.h - 1; + + for(; area.y1 <= coords->y2; area.y1 += header.h, area.y2 += header.h) { + + area.x1 = coords->x1; + area.x2 = area.x1 + header.w - 1; + for(; area.x1 <= coords->x2; area.x1 += header.w, area.x2 += header.w) { + lv_draw_img((lv_draw_ctx_t *) ctx, &img_dsc, &area, dsc->bg_img_src); + } + } + } + + if(radius_mask_id != LV_MASK_ID_INV) { + lv_draw_mask_remove_id(radius_mask_id); + lv_draw_mask_free_param(&radius_param); + } + } +} + +static void draw_shadow(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip, + const lv_draw_rect_dsc_t * dsc) +{ + /*Check whether the shadow is visible*/ + if(SKIP_SHADOW(dsc)) return; + + lv_coord_t sw = dsc->shadow_width; + + lv_area_t core_area; + core_area.x1 = coords->x1 + dsc->shadow_ofs_x - dsc->shadow_spread; + core_area.x2 = coords->x2 + dsc->shadow_ofs_x + dsc->shadow_spread; + core_area.y1 = coords->y1 + dsc->shadow_ofs_y - dsc->shadow_spread; + core_area.y2 = coords->y2 + dsc->shadow_ofs_y + dsc->shadow_spread; + + lv_area_t shadow_area; + shadow_area.x1 = core_area.x1 - sw / 2 - 1; + shadow_area.x2 = core_area.x2 + sw / 2 + 1; + shadow_area.y1 = core_area.y1 - sw / 2 - 1; + shadow_area.y2 = core_area.y2 + sw / 2 + 1; + + lv_opa_t opa = dsc->shadow_opa; + + if(opa > LV_OPA_MAX) opa = LV_OPA_COVER; + + /*Get clipped draw area which is the real draw area. + *It is always the same or inside `shadow_area`*/ + lv_area_t draw_area; + if(!_lv_area_intersect(&draw_area, &shadow_area, clip)) return; + + SDL_Rect core_area_rect; + lv_area_to_sdl_rect(&shadow_area, &core_area_rect); + + lv_coord_t radius = dsc->radius; + /* No matter how big the shadow is, what we need is just a corner */ + lv_coord_t frag_size = LV_MIN3(lv_area_get_width(&core_area) / 2, lv_area_get_height(&core_area) / 2, + LV_MAX(sw / 2, radius)); + + /* This is how big the corner is after blurring */ + lv_coord_t blur_growth = (lv_coord_t)(sw / 2 + 1); + + lv_coord_t blur_frag_size = (lv_coord_t)(frag_size + blur_growth); + + lv_draw_rect_shadow_key_t key = rect_shadow_key_create(radius, frag_size, sw); + + SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL); + if(texture == NULL) { + lv_area_t mask_area = {blur_growth, blur_growth}, mask_area_blurred = {0, 0}; + lv_area_set_width(&mask_area, frag_size * 2); + lv_area_set_height(&mask_area, frag_size * 2); + lv_area_set_width(&mask_area_blurred, blur_frag_size * 2); + lv_area_set_height(&mask_area_blurred, blur_frag_size * 2); + + lv_draw_mask_radius_param_t mask_rout_param; + lv_draw_mask_radius_init(&mask_rout_param, &mask_area, radius, false); + int16_t mask_id = lv_draw_mask_add(&mask_rout_param, NULL); + lv_opa_t * mask_buf = lv_draw_sdl_mask_dump_opa(&mask_area_blurred, &mask_id, 1); + lv_stack_blur_grayscale(mask_buf, lv_area_get_width(&mask_area_blurred), lv_area_get_height(&mask_area_blurred), + sw / 2 + sw % 2); + texture = lv_sdl_create_opa_texture(ctx->renderer, mask_buf, blur_frag_size, blur_frag_size, + lv_area_get_width(&mask_area_blurred)); + lv_mem_buf_release(mask_buf); + lv_draw_mask_remove_id(mask_id); + SDL_assert(texture); + lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), texture); + } + + SDL_Color shadow_color; + lv_color_to_sdl_color(&dsc->shadow_color, &shadow_color); + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + SDL_SetTextureAlphaMod(texture, opa); + SDL_SetTextureColorMod(texture, shadow_color.r, shadow_color.g, shadow_color.b); + + lv_draw_sdl_rect_bg_frag_draw_corners(ctx, texture, blur_frag_size, &shadow_area, clip, false); + frag_render_borders(ctx->renderer, texture, blur_frag_size, &shadow_area, clip, false); + frag_render_center(ctx->renderer, texture, blur_frag_size, &shadow_area, clip, false); +} + + +static void draw_border(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area, + const lv_draw_rect_dsc_t * dsc) +{ + if(SKIP_BORDER(dsc)) return; + + SDL_Color border_color; + lv_color_to_sdl_color(&dsc->border_color, &border_color); + + lv_coord_t coords_w = lv_area_get_width(coords), coords_h = lv_area_get_height(coords); + lv_coord_t short_side = LV_MIN(coords_w, coords_h); + lv_coord_t rout = LV_MIN(dsc->radius, short_side / 2);/*Get the inner area*/ + lv_area_t area_inner; + lv_area_copy(&area_inner, coords);// lv_area_increase(&area_inner, 1, 1); + area_inner.x1 += ((dsc->border_side & LV_BORDER_SIDE_LEFT) ? dsc->border_width : -(dsc->border_width + rout)); + area_inner.x2 -= ((dsc->border_side & LV_BORDER_SIDE_RIGHT) ? dsc->border_width : -(dsc->border_width + rout)); + area_inner.y1 += ((dsc->border_side & LV_BORDER_SIDE_TOP) ? dsc->border_width : -(dsc->border_width + rout)); + area_inner.y2 -= ((dsc->border_side & LV_BORDER_SIDE_BOTTOM) ? dsc->border_width : -(dsc->border_width + rout)); + lv_coord_t rin = LV_MAX(rout - dsc->border_width, 0); + draw_border_generic(ctx, coords, &area_inner, draw_area, rout, rin, dsc->border_color, dsc->border_opa, + dsc->blend_mode); +} + +static void draw_outline(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip, + const lv_draw_rect_dsc_t * dsc) +{ + if(SKIP_OUTLINE(dsc)) return; + + lv_opa_t opa = dsc->outline_opa; + + if(opa > LV_OPA_MAX) opa = LV_OPA_COVER; + + /*Get the inner radius*/ + lv_area_t area_inner; + lv_area_copy(&area_inner, coords); + + /*Bring the outline closer to make sure there is no color bleeding with pad=0*/ + lv_coord_t pad = dsc->outline_pad - 1; + area_inner.x1 -= pad; + area_inner.y1 -= pad; + area_inner.x2 += pad; + area_inner.y2 += pad; + + lv_area_t area_outer; + lv_area_copy(&area_outer, &area_inner); + + area_outer.x1 -= dsc->outline_width; + area_outer.x2 += dsc->outline_width; + area_outer.y1 -= dsc->outline_width; + area_outer.y2 += dsc->outline_width; + + lv_area_t draw_area; + if(!_lv_area_intersect(&draw_area, &area_outer, clip)) return; + + int32_t inner_w = lv_area_get_width(&area_inner); + int32_t inner_h = lv_area_get_height(&area_inner); + lv_coord_t rin = dsc->radius; + int32_t short_side = LV_MIN(inner_w, inner_h); + if(rin > short_side >> 1) rin = short_side >> 1; + + lv_coord_t rout = rin + dsc->outline_width; + + draw_border_generic(ctx, &area_outer, &area_inner, clip, rout, rin, dsc->outline_color, dsc->outline_opa, + dsc->blend_mode); +} + +static void draw_border_generic(lv_draw_sdl_ctx_t * ctx, const lv_area_t * outer_area, const lv_area_t * inner_area, + const lv_area_t * clip, lv_coord_t rout, lv_coord_t rin, lv_color_t color, lv_opa_t opa, + lv_blend_mode_t blend_mode) +{ + opa = opa >= LV_OPA_COVER ? LV_OPA_COVER : opa; + + SDL_Renderer * renderer = ctx->renderer; + + lv_draw_rect_border_key_t key = rect_border_key_create(rout, rin, outer_area, inner_area); + lv_coord_t radius = LV_MIN3(rout, lv_area_get_width(outer_area) / 2, lv_area_get_height(outer_area) / 2); + lv_coord_t max_side = LV_MAX4(key.offsets.x1, key.offsets.y1, -key.offsets.x2, -key.offsets.y2); + lv_coord_t frag_size = LV_MAX(radius, max_side); + SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL); + if(texture == NULL) { + /* Create a mask texture with size of (frag_size * 2 + 3) */ + const lv_area_t frag_area = {0, 0, frag_size * 2 + 2, frag_size * 2 + 2}; + + /*Create mask for the outer area*/ + int16_t mask_ids[2] = {LV_MASK_ID_INV, LV_MASK_ID_INV}; + lv_draw_mask_radius_param_t mask_rout_param; + if(rout > 0) { + lv_draw_mask_radius_init(&mask_rout_param, &frag_area, rout, false); + mask_ids[0] = lv_draw_mask_add(&mask_rout_param, NULL); + } + + /*Create mask for the inner mask*/ + if(rin < 0) rin = 0; + const lv_area_t frag_inner_area = {frag_area.x1 + key.offsets.x1, frag_area.y1 + key.offsets.y1, + frag_area.x2 + key.offsets.x2, frag_area.y2 + key.offsets.y2 + }; + lv_draw_mask_radius_param_t mask_rin_param; + lv_draw_mask_radius_init(&mask_rin_param, &frag_inner_area, rin, true); + mask_ids[1] = lv_draw_mask_add(&mask_rin_param, NULL); + + texture = lv_draw_sdl_mask_dump_texture(renderer, &frag_area, mask_ids, 2); + + lv_draw_mask_remove_id(mask_ids[1]); + lv_draw_mask_remove_id(mask_ids[0]); + SDL_assert(texture); + lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), texture); + } + + SDL_Rect outer_rect; + lv_area_to_sdl_rect(outer_area, &outer_rect); + SDL_Color color_sdl; + lv_color_to_sdl_color(&color, &color_sdl); + + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + SDL_SetTextureAlphaMod(texture, opa); + SDL_SetTextureColorMod(texture, color_sdl.r, color_sdl.g, color_sdl.b); + + lv_draw_sdl_rect_bg_frag_draw_corners(ctx, texture, frag_size, outer_area, clip, true); + frag_render_borders(renderer, texture, frag_size, outer_area, clip, true); +} + +static void frag_render_borders(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size, + const lv_area_t * coords, const lv_area_t * clipped, bool full) +{ + lv_area_t border_area, dst_area; + /* Top border */ + border_area.x1 = coords->x1 + frag_size; + border_area.y1 = coords->y1; + border_area.x2 = coords->x2 - frag_size; + border_area.y2 = coords->y1 + frag_size - 1; + if(_lv_area_intersect(&dst_area, &border_area, clipped)) { + SDL_Rect dst_rect; + lv_area_to_sdl_rect(&dst_area, &dst_rect); + + lv_coord_t sy = (lv_coord_t)(dst_area.y1 - border_area.y1); + if(full) { + SDL_Rect src_rect = {frag_size + 1, sy, 1, lv_area_get_height(&dst_area)}; + SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect); + } + else { + SDL_Rect src_rect = {frag_size - 1, sy, 1, lv_area_get_height(&dst_area)}; + SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect); + } + } + /* Bottom border */ + border_area.y1 = LV_MAX(coords->y2 - frag_size + 1, coords->y1 + frag_size); + border_area.y2 = coords->y2; + if(_lv_area_intersect(&dst_area, &border_area, clipped)) { + SDL_Rect dst_rect; + lv_area_to_sdl_rect(&dst_area, &dst_rect); + + lv_coord_t dh = lv_area_get_height(&dst_area); + if(full) { + lv_coord_t sy = (lv_coord_t)(dst_area.y1 - border_area.y1); + SDL_Rect src_rect = {frag_size + 1, frag_size + 3 + sy, 1, dh}; + SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect); + } + else { + lv_coord_t sy = (lv_coord_t)(border_area.y2 - dst_area.y2); + SDL_Rect src_rect = {frag_size - 1, sy, 1, dh}; + SDL_RenderCopyEx(renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_VERTICAL); + } + } + /* Left border */ + border_area.x1 = coords->x1; + border_area.y1 = coords->y1 + frag_size; + border_area.x2 = coords->x1 + frag_size - 1; + border_area.y2 = coords->y2 - frag_size; + if(_lv_area_intersect(&dst_area, &border_area, clipped)) { + SDL_Rect dst_rect; + lv_area_to_sdl_rect(&dst_area, &dst_rect); + + lv_coord_t dw = lv_area_get_width(&dst_area); + lv_coord_t sx = (lv_coord_t)(dst_area.x1 - border_area.x1); + if(full) { + SDL_Rect src_rect = {sx, frag_size + 1, dw, 1}; + SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect); + } + else { + SDL_Rect src_rect = {sx, frag_size - 1, dw, 1}; + SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect); + } + } + /* Right border */ + border_area.x1 = LV_MAX(coords->x2 - frag_size + 1, coords->x1 + frag_size); + border_area.x2 = coords->x2; + if(_lv_area_intersect(&dst_area, &border_area, clipped)) { + SDL_Rect dst_rect; + lv_area_to_sdl_rect(&dst_area, &dst_rect); + + lv_coord_t dw = lv_area_get_width(&dst_area); + if(full) { + lv_coord_t sx = (lv_coord_t)(dst_area.x1 - border_area.x1); + SDL_Rect src_rect = {frag_size + 3 + sx, frag_size + 1, dw, 1}; + SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect); + } + else { + lv_coord_t sx = (lv_coord_t)(border_area.x2 - dst_area.x2); + SDL_Rect src_rect = {sx, frag_size - 1, dw, 1}; + SDL_RenderCopyEx(renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_HORIZONTAL); + } + } +} + +static void frag_render_center(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size, + const lv_area_t * coords, + const lv_area_t * clipped, bool full) +{ + lv_area_t center_area = { + coords->x1 + frag_size, + coords->y1 + frag_size, + coords->x2 - frag_size, + coords->y2 - frag_size, + }; + if(center_area.x2 < center_area.x1 || center_area.y2 < center_area.y1) return; + lv_area_t draw_area; + if(!_lv_area_intersect(&draw_area, ¢er_area, clipped)) { + return; + } + SDL_Rect dst_rect; + lv_area_to_sdl_rect(&draw_area, &dst_rect); + if(full) { + SDL_Rect src_rect = {frag_size, frag_size, 1, 1}; + SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect); + } + else { + SDL_Rect src_rect = {frag_size - 1, frag_size - 1, 1, 1}; + SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect); + } +} + +static lv_draw_rect_bg_key_t rect_bg_key_create(lv_coord_t radius, lv_coord_t size) +{ + lv_draw_rect_bg_key_t key; + SDL_memset(&key, 0, sizeof(key)); + key.magic = LV_GPU_CACHE_KEY_MAGIC_RECT_BG; + key.radius = radius; + key.size = size; + return key; +} + +static lv_draw_rect_shadow_key_t rect_shadow_key_create(lv_coord_t radius, lv_coord_t size, lv_coord_t blur) +{ + lv_draw_rect_shadow_key_t key; + SDL_memset(&key, 0, sizeof(key)); + key.magic = LV_GPU_CACHE_KEY_MAGIC_RECT_SHADOW; + key.radius = radius; + key.size = size; + key.blur = blur; + return key; +} + +static lv_draw_rect_border_key_t rect_border_key_create(lv_coord_t rout, lv_coord_t rin, const lv_area_t * outer_area, + const lv_area_t * inner_area) +{ + lv_draw_rect_border_key_t key; + /* VERY IMPORTANT! Padding between members is uninitialized, so we have to wipe them manually */ + SDL_memset(&key, 0, sizeof(key)); + key.magic = LV_GPU_CACHE_KEY_MAGIC_RECT_BORDER; + key.rout = rout; + key.rin = rin; + key.offsets.x1 = inner_area->x1 - outer_area->x1; + key.offsets.x2 = inner_area->x2 - outer_area->x2; + key.offsets.y1 = inner_area->y1 - outer_area->y1; + key.offsets.y2 = inner_area->y2 - outer_area->y2; + return key; +} + +#endif /*LV_USE_GPU_SDL*/ diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_rect.h b/lib/lvgl/src/draw/sdl/lv_draw_sdl_rect.h new file mode 100644 index 00000000..1e9be344 --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_rect.h @@ -0,0 +1,75 @@ +/** + * @file lv_draw_sdl_rect.h + * + */ + +#ifndef LV_DRAW_SDL_RECT_H +#define LV_DRAW_SDL_RECT_H + + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include LV_GPU_SDL_INCLUDE_PATH + +#include "../lv_draw.h" + +#include "lv_draw_sdl_texture_cache.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct lv_draw_sdl_rect_header_t { + lv_img_header_t base; + SDL_Rect rect; +} lv_draw_sdl_rect_header_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/*====================== + * Add/remove functions + *=====================*/ + +/*===================== + * Setter functions + *====================*/ + +/*===================== + * Getter functions + *====================*/ + +/*===================== + * Other functions + *====================*/ + +SDL_Texture * lv_draw_sdl_rect_bg_frag_obtain(lv_draw_sdl_ctx_t * ctx, lv_coord_t radius); + +void lv_draw_sdl_rect_bg_frag_draw_corners(lv_draw_sdl_ctx_t * ctx, SDL_Texture * frag, lv_coord_t frag_size, + const lv_area_t * coords, const lv_area_t * clip, bool full); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_GPU_SDL*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_SDL_RECT_H*/ diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_stack_blur.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_stack_blur.c new file mode 100644 index 00000000..1c411b0e --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_stack_blur.c @@ -0,0 +1,249 @@ +/** + * @file lv_draw_sdl_stack_blur.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_draw_sdl_stack_blur.h" + +#if LV_USE_GPU_SDL +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void +stack_blur_job(lv_opa_t * src, unsigned int w, unsigned int h, unsigned int radius, int cores, int core, int step); + +/********************** + * STATIC VARIABLES + **********************/ + +// Based heavily on http://vitiy.info/Code/stackblur.cpp +// See http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for-cpp/ +// Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com> + +static unsigned short const stackblur_mul[255] = { + 512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512, + 454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512, + 482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456, + 437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512, + 497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328, + 320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456, + 446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335, + 329, 323, 318, 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512, + 505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405, + 399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328, + 324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271, + 268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456, + 451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388, + 385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335, + 332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292, + 289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259 +}; + +static unsigned char const stackblur_shr[255] = { + 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, + 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 +}; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_stack_blur_grayscale(lv_opa_t * buf, uint16_t w, uint16_t h, uint16_t r) +{ + stack_blur_job(buf, w, h, r, 1, 0, 1); + stack_blur_job(buf, w, h, r, 1, 0, 2); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void stack_blur_job(lv_opa_t * src, unsigned int w, unsigned int h, unsigned int radius, int cores, int core, + int step) +{ + if(radius < 2 || radius > 254) { + /* Silently ignore bad radius */ + return; + } + + unsigned int x, y, xp, yp, i; + unsigned int sp; + unsigned int stack_start; + unsigned char * stack_ptr; + + lv_opa_t * src_ptr; + lv_opa_t * dst_ptr; + + unsigned long sum_r; + unsigned long sum_in_r; + unsigned long sum_out_r; + + unsigned int wm = w - 1; + unsigned int hm = h - 1; + unsigned int stride = w; + unsigned int div = (radius * 2) + 1; + unsigned int mul_sum = stackblur_mul[radius]; + unsigned char shr_sum = stackblur_shr[radius]; + unsigned char stack[254 * 2 + 1]; + + if(step == 1) { + unsigned int minY = core * h / cores; + unsigned int maxY = (core + 1) * h / cores; + + for(y = minY; y < maxY; y++) { + sum_r = + sum_in_r = + sum_out_r = 0; + + src_ptr = src + stride * y; // start of line (0,y) + + for(i = 0; i <= radius; i++) { + stack_ptr = &stack[i]; + stack_ptr[0] = src_ptr[0]; + sum_r += src_ptr[0] * (i + 1); + sum_out_r += src_ptr[0]; + } + + + for(i = 1; i <= radius; i++) { + if(i <= wm) src_ptr += 1; + stack_ptr = &stack[i + radius]; + stack_ptr[0] = src_ptr[0]; + sum_r += src_ptr[0] * (radius + 1 - i); + sum_in_r += src_ptr[0]; + } + + + sp = radius; + xp = radius; + if(xp > wm) xp = wm; + src_ptr = src + (xp + y * w); // img.pix_ptr(xp, y); + dst_ptr = src + y * stride; // img.pix_ptr(0, y); + for(x = 0; x < w; x++) { + dst_ptr[0] = LV_CLAMP((sum_r * mul_sum) >> shr_sum, 0, 255); + dst_ptr += 1; + + sum_r -= sum_out_r; + + stack_start = sp + div - radius; + if(stack_start >= div) stack_start -= div; + stack_ptr = &stack[stack_start]; + + sum_out_r -= stack_ptr[0]; + + if(xp < wm) { + src_ptr += 1; + ++xp; + } + + stack_ptr[0] = src_ptr[0]; + + sum_in_r += src_ptr[0]; + sum_r += sum_in_r; + + ++sp; + if(sp >= div) sp = 0; + stack_ptr = &stack[sp]; + + sum_out_r += stack_ptr[0]; + sum_in_r -= stack_ptr[0]; + } + + } + } + + // step 2 + if(step == 2) { + unsigned int minX = core * w / cores; + unsigned int maxX = (core + 1) * w / cores; + + for(x = minX; x < maxX; x++) { + sum_r = + sum_in_r = + sum_out_r = 0; + + src_ptr = src + x; // x,0 + for(i = 0; i <= radius; i++) { + stack_ptr = &stack[i]; + stack_ptr[0] = src_ptr[0]; + sum_r += src_ptr[0] * (i + 1); + sum_out_r += src_ptr[0]; + } + for(i = 1; i <= radius; i++) { + if(i <= hm) src_ptr += stride; // +stride + + stack_ptr = &stack[i + radius]; + stack_ptr[0] = src_ptr[0]; + sum_r += src_ptr[0] * (radius + 1 - i); + sum_in_r += src_ptr[0]; + } + + sp = radius; + yp = radius; + if(yp > hm) yp = hm; + src_ptr = src + (x + yp * w); // img.pix_ptr(x, yp); + dst_ptr = src + x; // img.pix_ptr(x, 0); + for(y = 0; y < h; y++) { + dst_ptr[0] = LV_CLAMP((sum_r * mul_sum) >> shr_sum, 0, 255); + dst_ptr += stride; + + sum_r -= sum_out_r; + + stack_start = sp + div - radius; + if(stack_start >= div) stack_start -= div; + stack_ptr = &stack[stack_start]; + + sum_out_r -= stack_ptr[0]; + + if(yp < hm) { + src_ptr += stride; // stride + ++yp; + } + + stack_ptr[0] = src_ptr[0]; + + sum_in_r += src_ptr[0]; + sum_r += sum_in_r; + + ++sp; + if(sp >= div) sp = 0; + stack_ptr = &stack[sp]; + + sum_out_r += stack_ptr[0]; + sum_in_r -= stack_ptr[0]; + } + } + } +} + +#endif /*LV_USE_GPU_SDL*/ diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_stack_blur.h b/lib/lvgl/src/draw/sdl/lv_draw_sdl_stack_blur.h new file mode 100644 index 00000000..413b1c94 --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_stack_blur.h @@ -0,0 +1,46 @@ +/** + * @file lv_draw_sdl_stack_blur.h + * + */ +#ifndef LV_DRAW_SDL_STACK_BLUR_H +#define LV_DRAW_SDL_STACK_BLUR_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include "../../misc/lv_color.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void lv_stack_blur_grayscale(lv_opa_t * buf, uint16_t w, uint16_t h, uint16_t r); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_GPU_SDL*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_SDL_STACK_BLUR_H*/ diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_texture_cache.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_texture_cache.c new file mode 100644 index 00000000..6845addf --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_texture_cache.c @@ -0,0 +1,178 @@ +/** + * @file lv_draw_sdl_texture_cache.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include "lv_draw_sdl_texture_cache.h" + +#include "lv_draw_sdl_utils.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + SDL_Texture * texture; + void * userdata; + lv_lru_free_t * userdata_free; + lv_draw_sdl_cache_flag_t flags; +} draw_cache_value_t; + +typedef struct { + lv_sdl_cache_key_magic_t magic; +} temp_texture_key_t; + +typedef struct { + lv_coord_t width, height; +} temp_texture_userdata_t; + +static void draw_cache_free_value(draw_cache_value_t *); + +static draw_cache_value_t * draw_cache_get_entry(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length, + bool * found); +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_sdl_texture_cache_init(lv_draw_sdl_ctx_t * ctx) +{ + ctx->internals->texture_cache = lv_lru_create(LV_GPU_SDL_LRU_SIZE, 65536, + (lv_lru_free_t *) draw_cache_free_value, NULL); +} + +void lv_draw_sdl_texture_cache_deinit(lv_draw_sdl_ctx_t * ctx) +{ + lv_lru_del(ctx->internals->texture_cache); +} + +SDL_Texture * lv_draw_sdl_texture_cache_get(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length, bool * found) +{ + return lv_draw_sdl_texture_cache_get_with_userdata(ctx, key, key_length, found, NULL); +} + +SDL_Texture * lv_draw_sdl_texture_cache_get_with_userdata(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length, + bool * found, void ** userdata) +{ + draw_cache_value_t * value = draw_cache_get_entry(ctx, key, key_length, found); + if(!value) return NULL; + if(userdata) { + *userdata = value->userdata; + } + return value->texture; +} + +void lv_draw_sdl_texture_cache_put(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length, SDL_Texture * texture) +{ + lv_draw_sdl_texture_cache_put_advanced(ctx, key, key_length, texture, NULL, NULL, 0); +} + +void lv_draw_sdl_texture_cache_put_advanced(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length, + SDL_Texture * texture, void * userdata, void userdata_free(void *), + lv_draw_sdl_cache_flag_t flags) +{ + lv_lru_t * lru = ctx->internals->texture_cache; + draw_cache_value_t * value = SDL_malloc(sizeof(draw_cache_value_t)); + value->texture = texture; + value->userdata = userdata; + value->userdata_free = userdata_free; + value->flags = flags; + if(!texture) { + lv_lru_set(lru, key, key_length, value, 1); + return; + } + if(flags & LV_DRAW_SDL_CACHE_FLAG_MANAGED) { + /* Managed texture doesn't count into cache size */ + LV_LOG_INFO("cache texture %p", texture); + lv_lru_set(lru, key, key_length, value, 1); + return; + } + Uint32 format; + int access, width, height; + if(SDL_QueryTexture(texture, &format, &access, &width, &height) != 0) { + return; + } + LV_LOG_INFO("cache texture %p, %d*%d@%dbpp", texture, width, height, SDL_BITSPERPIXEL(format)); + lv_lru_set(lru, key, key_length, value, width * height * SDL_BITSPERPIXEL(format) / 8); +} + +lv_draw_sdl_cache_key_head_img_t * lv_draw_sdl_texture_img_key_create(const void * src, int32_t frame_id, size_t * size) +{ + lv_draw_sdl_cache_key_head_img_t header; + /* VERY IMPORTANT! Padding between members is uninitialized, so we have to wipe them manually */ + SDL_memset(&header, 0, sizeof(header)); + header.magic = LV_GPU_CACHE_KEY_MAGIC_IMG; + header.type = lv_img_src_get_type(src); + header.frame_id = frame_id; + void * key; + size_t key_size; + if(header.type == LV_IMG_SRC_FILE || header.type == LV_IMG_SRC_SYMBOL) { + size_t srclen = SDL_strlen(src); + key_size = sizeof(header) + srclen; + key = SDL_malloc(key_size); + SDL_memcpy(key, &header, sizeof(header)); + /*Copy string content as key value*/ + SDL_memcpy(key + sizeof(header), src, srclen); + } + else { + key_size = sizeof(header) + sizeof(void *); + key = SDL_malloc(key_size); + SDL_memcpy(key, &header, sizeof(header)); + /*Copy address number as key value*/ + SDL_memcpy(key + sizeof(header), &src, sizeof(void *)); + } + *size = key_size; + return (lv_draw_sdl_cache_key_head_img_t *) key; +} + +static void draw_cache_free_value(draw_cache_value_t * value) +{ + if(value->texture && !(value->flags & LV_DRAW_SDL_CACHE_FLAG_MANAGED)) { + LV_LOG_INFO("destroy texture %p", value->texture); + SDL_DestroyTexture(value->texture); + } + if(value->userdata_free) { + value->userdata_free(value->userdata); + } + SDL_free(value); +} + +static draw_cache_value_t * draw_cache_get_entry(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length, + bool * found) +{ + lv_lru_t * lru = ctx->internals->texture_cache; + draw_cache_value_t * value = NULL; + lv_lru_get(lru, key, key_length, (void **) &value); + if(!value) { + if(found) { + *found = false; + } + return NULL; + } + if(found) { + *found = true; + } + return value; +} + +#endif /*LV_USE_GPU_SDL*/ + diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_texture_cache.h b/lib/lvgl/src/draw/sdl/lv_draw_sdl_texture_cache.h new file mode 100644 index 00000000..dc8b578e --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_texture_cache.h @@ -0,0 +1,102 @@ +/** + * @file lv_draw_sdl_texture_cache.h + * + */ + +#ifndef LV_DRAW_SDL_TEXTURE_CACHE_H +#define LV_DRAW_SDL_TEXTURE_CACHE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include LV_GPU_SDL_INCLUDE_PATH +#include "lv_draw_sdl.h" +#include "lv_draw_sdl_priv.h" +#include "../../draw/lv_img_decoder.h" +#include "../../misc/lv_area.h" + +/********************* + * DEFINES + *********************/ + +#define LV_DRAW_SDL_DEC_DSC_TEXTURE_HEAD "@LVSDLTex" + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + char head[8]; + SDL_Texture * texture; + SDL_Rect rect; + bool texture_managed; + bool texture_referenced; +} lv_draw_sdl_dec_dsc_userdata_t; + +typedef enum { + LV_GPU_CACHE_KEY_MAGIC_ARC = 0x01, + LV_GPU_CACHE_KEY_MAGIC_IMG = 0x11, + LV_GPU_CACHE_KEY_MAGIC_IMG_ROUNDED_CORNERS = 0x12, + LV_GPU_CACHE_KEY_MAGIC_LINE = 0x21, + LV_GPU_CACHE_KEY_MAGIC_RECT_BG = 0x31, + LV_GPU_CACHE_KEY_MAGIC_RECT_SHADOW = 0x32, + LV_GPU_CACHE_KEY_MAGIC_RECT_BORDER = 0x33, + LV_GPU_CACHE_KEY_MAGIC_FONT_GLYPH = 0x41, + LV_GPU_CACHE_KEY_MAGIC_MASK = 0x51, +} lv_sdl_cache_key_magic_t; + +typedef enum { + LV_DRAW_SDL_CACHE_FLAG_NONE = 0, + LV_DRAW_SDL_CACHE_FLAG_MANAGED = 1, +} lv_draw_sdl_cache_flag_t; + +typedef struct { + lv_sdl_cache_key_magic_t magic; + lv_img_src_t type; + int32_t frame_id; +} lv_draw_sdl_cache_key_head_img_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void lv_draw_sdl_texture_cache_init(lv_draw_sdl_ctx_t * ctx); + +void lv_draw_sdl_texture_cache_deinit(lv_draw_sdl_ctx_t * ctx); + +/** + * Find cached texture by key. The texture can be destroyed during usage. + */ +SDL_Texture * lv_draw_sdl_texture_cache_get(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length, bool * found); + +SDL_Texture * lv_draw_sdl_texture_cache_get_with_userdata(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length, + bool * found, void ** userdata); + +void lv_draw_sdl_texture_cache_put(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length, SDL_Texture * texture); + +void lv_draw_sdl_texture_cache_put_advanced(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length, + SDL_Texture * texture, void * userdata, void userdata_free(void *), + lv_draw_sdl_cache_flag_t flags); + +lv_draw_sdl_cache_key_head_img_t * lv_draw_sdl_texture_img_key_create(const void * src, int32_t frame_id, + size_t * size); + +/********************** + * MACROS + **********************/ +#endif /*LV_USE_GPU_SDL*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_SDL_TEXTURE_CACHE_H*/ diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_utils.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_utils.c new file mode 100644 index 00000000..3ca0fad4 --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_utils.c @@ -0,0 +1,183 @@ +/** + * @file lv_draw_sdl_utils.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include "lv_draw_sdl_utils.h" + +#include "../lv_draw.h" +#include "../lv_draw_label.h" +#include "../../core/lv_refr.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +/********************** + * STATIC VARIABLES + **********************/ +extern const uint8_t _lv_bpp1_opa_table[2]; +extern const uint8_t _lv_bpp2_opa_table[4]; +extern const uint8_t _lv_bpp4_opa_table[16]; +extern const uint8_t _lv_bpp8_opa_table[256]; + +static int utils_init_count = 0; +static SDL_Palette * lv_sdl_palette_grayscale8 = NULL; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void _lv_draw_sdl_utils_init() +{ + utils_init_count++; + if(utils_init_count > 1) { + return; + } + lv_sdl_palette_grayscale8 = lv_sdl_alloc_palette_for_bpp(_lv_bpp8_opa_table, 8); +} + +void _lv_draw_sdl_utils_deinit() +{ + if(utils_init_count == 0) { + return; + } + utils_init_count--; + if(utils_init_count == 0) { + SDL_FreePalette(lv_sdl_palette_grayscale8); + lv_sdl_palette_grayscale8 = NULL; + } +} + +void lv_area_to_sdl_rect(const lv_area_t * in, SDL_Rect * out) +{ + out->x = in->x1; + out->y = in->y1; + out->w = in->x2 - in->x1 + 1; + out->h = in->y2 - in->y1 + 1; +} + +void lv_color_to_sdl_color(const lv_color_t * in, SDL_Color * out) +{ +#if LV_COLOR_DEPTH == 32 + out->a = in->ch.alpha; + out->r = in->ch.red; + out->g = in->ch.green; + out->b = in->ch.blue; +#else + uint32_t color32 = lv_color_to32(*in); + lv_color32_t * color32_t = (lv_color32_t *) &color32; + out->a = color32_t->ch.alpha; + out->r = color32_t->ch.red; + out->g = color32_t->ch.green; + out->b = color32_t->ch.blue; +#endif +} + +void lv_area_zoom_to_sdl_rect(const lv_area_t * in, SDL_Rect * out, uint16_t zoom, const lv_point_t * pivot) +{ + if(zoom == LV_IMG_ZOOM_NONE) { + lv_area_to_sdl_rect(in, out); + return; + } + lv_area_t tmp; + _lv_img_buf_get_transformed_area(&tmp, lv_area_get_width(in), lv_area_get_height(in), 0, zoom, pivot); + lv_area_move(&tmp, in->x1, in->y1); + lv_area_to_sdl_rect(&tmp, out); +} + +SDL_Palette * lv_sdl_alloc_palette_for_bpp(const uint8_t * mapping, uint8_t bpp) +{ + SDL_assert(bpp >= 1 && bpp <= 8); + int color_cnt = 1 << bpp; + SDL_Palette * result = SDL_AllocPalette(color_cnt); + SDL_Color palette[256]; + for(int i = 0; i < color_cnt; i++) { + palette[i].r = palette[i].g = palette[i].b = 0xFF; + palette[i].a = mapping ? mapping[i] : i; + } + SDL_SetPaletteColors(result, palette, 0, color_cnt); + return result; +} + +SDL_Surface * lv_sdl_create_opa_surface(lv_opa_t * opa, lv_coord_t width, lv_coord_t height, lv_coord_t stride) +{ + SDL_Surface * indexed = SDL_CreateRGBSurfaceFrom(opa, width, height, 8, stride, 0, 0, 0, 0); + SDL_SetSurfacePalette(indexed, lv_sdl_palette_grayscale8); + SDL_Surface * converted = SDL_ConvertSurfaceFormat(indexed, LV_DRAW_SDL_TEXTURE_FORMAT, 0); + SDL_FreeSurface(indexed); + return converted; +} + +SDL_Texture * lv_sdl_create_opa_texture(SDL_Renderer * renderer, lv_opa_t * pixels, lv_coord_t width, + lv_coord_t height, lv_coord_t stride) +{ + SDL_Surface * indexed = lv_sdl_create_opa_surface(pixels, width, height, stride); + SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, indexed); + SDL_FreeSurface(indexed); + return texture; +} + +void lv_sdl_to_8bpp(uint8_t * dest, const uint8_t * src, int width, int height, int stride, uint8_t bpp) +{ + int src_len = width * height; + int cur = 0; + int curbit; + uint8_t opa_mask; + const uint8_t * opa_table; + switch(bpp) { + case 1: + opa_mask = 0x1; + opa_table = _lv_bpp1_opa_table; + break; + case 2: + opa_mask = 0x4; + opa_table = _lv_bpp2_opa_table; + break; + case 4: + opa_mask = 0xF; + opa_table = _lv_bpp4_opa_table; + break; + case 8: + opa_mask = 0xFF; + opa_table = _lv_bpp8_opa_table; + break; + default: + return; + } + /* Does this work well on big endian systems? */ + while(cur < src_len) { + curbit = 8 - bpp; + uint8_t src_byte = src[cur * bpp / 8]; + while(curbit >= 0 && cur < src_len) { + uint8_t src_bits = opa_mask & (src_byte >> curbit); + dest[(cur / width * stride) + (cur % width)] = opa_table[src_bits]; + curbit -= bpp; + cur++; + } + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_GPU_SDL*/ diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_utils.h b/lib/lvgl/src/draw/sdl/lv_draw_sdl_utils.h new file mode 100644 index 00000000..9afae687 --- /dev/null +++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_utils.h @@ -0,0 +1,65 @@ +/** + * @file lv_draw_sdl_utils.h + * + */ +#ifndef LV_DRAW_SDL_UTILS_H +#define LV_DRAW_SDL_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" +#if LV_USE_GPU_SDL + +#include "lv_draw_sdl.h" +#include "../../misc/lv_color.h" +#include "../../misc/lv_area.h" + +#include LV_GPU_SDL_INCLUDE_PATH + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void _lv_draw_sdl_utils_init(); + +void _lv_draw_sdl_utils_deinit(); + +void lv_area_to_sdl_rect(const lv_area_t * in, SDL_Rect * out); + +void lv_color_to_sdl_color(const lv_color_t * in, SDL_Color * out); + +void lv_area_zoom_to_sdl_rect(const lv_area_t * in, SDL_Rect * out, uint16_t zoom, const lv_point_t * pivot); + +SDL_Palette * lv_sdl_alloc_palette_for_bpp(const uint8_t * mapping, uint8_t bpp); + +SDL_Surface * lv_sdl_create_opa_surface(lv_opa_t * opa, lv_coord_t width, lv_coord_t height, lv_coord_t stride); + +SDL_Texture * lv_sdl_create_opa_texture(SDL_Renderer * renderer, lv_opa_t * pixels, lv_coord_t width, + lv_coord_t height, lv_coord_t stride); + +void lv_sdl_to_8bpp(uint8_t * dest, const uint8_t * src, int width, int height, int stride, uint8_t bpp); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_GPU_SDL*/ +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_SDL_UTILS_H*/ |
