From efc5bb40d9868e01616f20160a83e77ae104fe2c Mon Sep 17 00:00:00 2001 From: Mariotaku Date: Fri, 17 Sep 2021 01:20:32 +0900 Subject: [PATCH] feat(drawing) hardware accelerated rendering by SDL2 (#2484) * Hardware accelerated SDL render WIP * Rect drawing code cleanup * Fixed arc drawing angle Fixed compact rect drawing * Refactoring Fixed cache deinit order * Image recolor Drawing left and right border * Math problems * Improving draw cache * Improving line drawing logic * Improving arc drawing quality * Added round function * Attempts to render properly on Pi * Updated lruc function names * Updated symbol names * Saved ram by reusing part of background rect * Added outline drawing Removed arc texture caching * rect drawing cache rendering * high efficiency rect shadow texture caching * fixed simple borders drawing * fixed generic borders drawing * Simplified text atlas caching logic Supports chroma keyed image * Color palette WIP * Improved draw cache * Updated defines for better build * renaming files for better consistency * fix includes * Updated lv_conf_internal with generator * Fixing build issues * fixed img rotation pivot * better font atlas caching WIP * fix includes * full font atlas support improved gpu caching for rect * update conf header * fix palette creation * Fixed font atlas key matching * fixed chroma key image background * added 3bpp mask palette (WIP) * fixed caching key initialization * disabled gpu arc drawing for now updated naming convention * updated naming convention * fix makefile and include path * improved rect drawing when having masks * improved rect drawing when having masks * reduced texture allocation while drawing with mask * accurate clipping for rects and texts * fixed build error * fixed build error * line drawing WIP * updated imports * fixed freezes if LRU has smaller new item * fixed clipping rect color * fixed build error * using built-in free function for LRU key * Added custom background drawing function * fixing imports * fixed shadow bitmap on old SDL libs * improved draw_img compatibility * fixing font baking * fixed font rendering * fixed lv_draw_line check * configurable SDL include path * disabled SDL line drawing implementation * supports screen resize * sdl: Remove duplicated object Observed issue: /usr/bin/ld: lv_gpu_sdl_texture_cache.o: \ in function `_lv_gpu_sdl_texture_cache_init': lv_gpu_sdl_texture_cache.c:(.text+0x30): \ multiple definition of `_lv_gpu_sdl_texture_cache_init'; \ lv_gpu_sdl_texture_cache.o:lv_gpu_sdl_texture_cache.c:(.text+0x30): \ first defined here Forwarded: https://github.com/mariotaku/lvgl/pull/1 Relate-to: https://github.com/lvgl/lvgl/pull/2484 Signed-off-by: Philippe Coval * Reduced opening of image * Closes image resource properly * improved draw image logic * Moved driver related code to lv_drivers * fixed lv_deinit implicit sdl code invocation * improved shadow clipping * fixed outline area invalidation * updated comments * formatted code using astyle * Updated README * formatted code * fixed typo * fixed static declaration Co-authored-by: Gabor Kiss-Vamosi Co-authored-by: Philippe Coval --- lv_conf_template.h | 21 +- src/core/lv_obj.c | 12 +- src/core/lv_refr.c | 4 +- src/draw/lv_draw_blend.c | 7 + src/draw/lv_draw_img.c | 7 + src/draw/lv_draw_label.c | 9 + src/draw/lv_draw_line.c | 2 +- src/draw/lv_draw_mask.c | 33 ++ src/draw/lv_draw_mask.h | 16 + src/draw/lv_draw_rect.c | 5 + src/draw/lv_draw_rect.h | 4 +- src/gpu/lv_gpu.mk | 3 + src/gpu/lv_gpu_sdl.c | 59 ++ src/gpu/lv_gpu_sdl.h | 69 +++ src/gpu/sdl/README.md | 28 + src/gpu/sdl/lv_gpu_sdl.mk | 16 + src/gpu/sdl/lv_gpu_sdl_draw_arc.c | 163 ++++++ src/gpu/sdl/lv_gpu_sdl_draw_blend.c | 95 ++++ src/gpu/sdl/lv_gpu_sdl_draw_img.c | 171 ++++++ src/gpu/sdl/lv_gpu_sdl_draw_label.c | 332 +++++++++++ src/gpu/sdl/lv_gpu_sdl_draw_line.c | 158 ++++++ src/gpu/sdl/lv_gpu_sdl_draw_rect.c | 733 +++++++++++++++++++++++++ src/gpu/sdl/lv_gpu_sdl_lru.c | 375 +++++++++++++ src/gpu/sdl/lv_gpu_sdl_lru.h | 90 +++ src/gpu/sdl/lv_gpu_sdl_mask.c | 110 ++++ src/gpu/sdl/lv_gpu_sdl_mask.h | 57 ++ src/gpu/sdl/lv_gpu_sdl_stack_blur.c | 247 +++++++++ src/gpu/sdl/lv_gpu_sdl_stack_blur.h | 42 ++ src/gpu/sdl/lv_gpu_sdl_texture_cache.c | 197 +++++++ src/gpu/sdl/lv_gpu_sdl_texture_cache.h | 90 +++ src/gpu/sdl/lv_gpu_sdl_utils.c | 179 ++++++ src/gpu/sdl/lv_gpu_sdl_utils.h | 61 ++ src/hal/lv_hal_disp.h | 1 + src/lv_conf_internal.h | 62 ++- 34 files changed, 3445 insertions(+), 13 deletions(-) create mode 100644 src/gpu/lv_gpu_sdl.c create mode 100644 src/gpu/lv_gpu_sdl.h create mode 100644 src/gpu/sdl/README.md create mode 100644 src/gpu/sdl/lv_gpu_sdl.mk create mode 100644 src/gpu/sdl/lv_gpu_sdl_draw_arc.c create mode 100644 src/gpu/sdl/lv_gpu_sdl_draw_blend.c create mode 100644 src/gpu/sdl/lv_gpu_sdl_draw_img.c create mode 100644 src/gpu/sdl/lv_gpu_sdl_draw_label.c create mode 100644 src/gpu/sdl/lv_gpu_sdl_draw_line.c create mode 100644 src/gpu/sdl/lv_gpu_sdl_draw_rect.c create mode 100644 src/gpu/sdl/lv_gpu_sdl_lru.c create mode 100644 src/gpu/sdl/lv_gpu_sdl_lru.h create mode 100644 src/gpu/sdl/lv_gpu_sdl_mask.c create mode 100644 src/gpu/sdl/lv_gpu_sdl_mask.h create mode 100644 src/gpu/sdl/lv_gpu_sdl_stack_blur.c create mode 100644 src/gpu/sdl/lv_gpu_sdl_stack_blur.h create mode 100644 src/gpu/sdl/lv_gpu_sdl_texture_cache.c create mode 100644 src/gpu/sdl/lv_gpu_sdl_texture_cache.h create mode 100644 src/gpu/sdl/lv_gpu_sdl_utils.c create mode 100644 src/gpu/sdl/lv_gpu_sdl_utils.h diff --git a/lv_conf_template.h b/lv_conf_template.h index 289a50bcf..ceedb0415 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -149,6 +149,25 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ /*Use NXP's VG-Lite GPU iMX RTxxx platforms*/ #define LV_USE_GPU_NXP_VG_LITE 0 +/*Use SDL renderer API*/ +#ifndef LV_USE_GPU_SDL +# ifdef CONFIG_LV_USE_GPU_SDL +# define LV_USE_GPU_SDL CONFIG_LV_USE_GPU_SDL +# else +# define LV_USE_GPU_SDL 0 +# endif +#endif +#if LV_USE_GPU_SDL +# define LV_USE_EXTERNAL_RENDERER 1 +# ifndef LV_GPU_SDL_INCLUDE +# define LV_GPU_SDL_INCLUDE_PATH +# endif +#endif + +#ifndef LV_USE_EXTERNAL_RENDERER +# define LV_USE_EXTERNAL_RENDERER 0 +#endif + /*------------- * Logging *-----------*/ @@ -225,7 +244,7 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ #define LV_USE_USER_DATA 1 /*Garbage Collector settings - *Used if lvgl is binded to higher level language and the memory is managed by that language*/ + *Used if lvgl is bound to higher level language and the memory is managed by that language*/ #define LV_ENABLE_GC 0 #if LV_ENABLE_GC != 0 # define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/ diff --git a/src/core/lv_obj.c b/src/core/lv_obj.c index b14b574c4..3a81dee3e 100644 --- a/src/core/lv_obj.c +++ b/src/core/lv_obj.c @@ -35,6 +35,10 @@ #include "../gpu/lv_gpu_nxp_pxp_osa.h" #endif +#if LV_USE_GPU_SDL + #include "../gpu/lv_gpu_sdl.h" +#endif + /********************* * DEFINES *********************/ @@ -121,6 +125,9 @@ void lv_init(void) for(; ;) ; } #endif +#if LV_USE_GPU_SDL + lv_gpu_sdl_init(); +#endif _lv_obj_style_init(); _lv_ll_init(&LV_GC_ROOT(_lv_disp_ll), sizeof(lv_disp_t)); @@ -177,10 +184,13 @@ void lv_init(void) LV_LOG_TRACE("finished"); } -#if LV_ENABLE_GC || !LV_MEM_CUSTOM +#if LV_ENABLE_GC || !LV_MEM_CUSTOM || LV_USE_GPU_SDL void lv_deinit(void) { +#if LV_USE_GPU_SDL + lv_gpu_sdl_deinit(); +#endif _lv_gc_clear_roots(); lv_disp_set_default(NULL); diff --git a/src/core/lv_refr.c b/src/core/lv_refr.c index cc6d7e7b9..07c1306e0 100644 --- a/src/core/lv_refr.c +++ b/src/core/lv_refr.c @@ -517,7 +517,9 @@ static void lv_refr_area_part(const lv_area_t * area_p) /*Draw a display background if there is no top object*/ if(top_act_scr == NULL && top_prev_scr == NULL) { - if(disp_refr->bg_img) { + if(disp_refr->bg_fn) { + disp_refr->bg_fn(&start_mask); + } else if(disp_refr->bg_img) { lv_draw_img_dsc_t dsc; lv_draw_img_dsc_init(&dsc); dsc.opa = disp_refr->bg_opa; diff --git a/src/draw/lv_draw_blend.c b/src/draw/lv_draw_blend.c index 8f8decdc2..cef175d7e 100644 --- a/src/draw/lv_draw_blend.c +++ b/src/draw/lv_draw_blend.c @@ -33,6 +33,7 @@ * STATIC PROTOTYPES **********************/ +#if LV_USE_EXTERNAL_RENDERER == 0 static void fill_set_px(const lv_area_t * disp_area, lv_color_t * disp_buf, const lv_area_t * draw_area, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_draw_mask_res_t mask_res); @@ -65,6 +66,7 @@ static void map_blended(const lv_area_t * disp_area, lv_color_t * disp_buf, con static inline lv_color_t color_blend_true_color_additive(lv_color_t fg, lv_color_t bg, lv_opa_t opa); static inline lv_color_t color_blend_true_color_subtractive(lv_color_t fg, lv_color_t bg, lv_opa_t opa); #endif +#endif //LV_USE_GPU_SDL_RENDER /********************** * STATIC VARIABLES @@ -109,6 +111,7 @@ static inline lv_color_t color_blend_true_color_subtractive(lv_color_t fg, lv_co * GLOBAL FUNCTIONS **********************/ +#if LV_USE_EXTERNAL_RENDERER == 0 /** * Fill and area in the display buffer. * @param clip_area clip the fill to this area (absolute coordinates) @@ -273,6 +276,7 @@ static void fill_set_px(const lv_area_t * disp_area, lv_color_t * disp_buf, con } } +#if LV_USE_EXTERNAL_RENDERER == 0 /** * Fill an area with a color * @param disp_area the current display area (destination area) @@ -572,6 +576,7 @@ static void fill_blended(const lv_area_t * disp_area, lv_color_t * disp_buf, co } } #endif +#endif // LV_USE_GPU_SDL_RENDER static void map_set_px(const lv_area_t * disp_area, lv_color_t * disp_buf, const lv_area_t * draw_area, const lv_area_t * map_area, const lv_color_t * map_buf, lv_opa_t opa, @@ -1022,3 +1027,5 @@ static inline lv_color_t color_blend_true_color_subtractive(lv_color_t fg, lv_co return lv_color_mix(fg, bg, opa); } #endif + +#endif // LV_USE_GPU_SDL_RENDER \ No newline at end of file diff --git a/src/draw/lv_draw_img.c b/src/draw/lv_draw_img.c index 0bd1118dd..c9d484829 100644 --- a/src/draw/lv_draw_img.c +++ b/src/draw/lv_draw_img.c @@ -30,6 +30,7 @@ /********************** * STATIC PROTOTYPES **********************/ +#if LV_USE_EXTERNAL_RENDERER == 0 LV_ATTRIBUTE_FAST_MEM static lv_res_t lv_img_draw_core(const lv_area_t * coords, const lv_area_t * clip_area, const void * src, const lv_draw_img_dsc_t * draw_dsc); @@ -41,6 +42,7 @@ LV_ATTRIBUTE_FAST_MEM static void lv_draw_map(const lv_area_t * map_area, const static void show_error(const lv_area_t * coords, const lv_area_t * clip_area, const char * msg); static void draw_cleanup(_lv_img_cache_entry_t * cache); +#endif /********************** * STATIC VARIABLES @@ -63,6 +65,7 @@ void lv_draw_img_dsc_init(lv_draw_img_dsc_t * dsc) dsc->antialias = LV_COLOR_DEPTH > 8 ? 1 : 0; } +#if LV_USE_EXTERNAL_RENDERER == 0 /** * Draw an image * @param coords the coordinates of the image @@ -89,6 +92,7 @@ void lv_draw_img(const lv_area_t * coords, const lv_area_t * mask, const void * return; } } +#endif //LV_USE_GPU_SDL_RENDER /** * Get the pixel size of a color format in bits @@ -229,6 +233,7 @@ lv_img_src_t lv_img_src_get_type(const void * src) * STATIC FUNCTIONS **********************/ +#if LV_USE_EXTERNAL_RENDERER == 0 LV_ATTRIBUTE_FAST_MEM static lv_res_t lv_img_draw_core(const lv_area_t * coords, const lv_area_t * clip_area, const void * src, const lv_draw_img_dsc_t * draw_dsc) @@ -659,3 +664,5 @@ static void draw_cleanup(_lv_img_cache_entry_t * cache) LV_UNUSED(cache); #endif } + +#endif //LV_USE_GPU_SDL_RENDER \ No newline at end of file diff --git a/src/draw/lv_draw_label.c b/src/draw/lv_draw_label.c index 1121c5749..ac63bd310 100644 --- a/src/draw/lv_draw_label.c +++ b/src/draw/lv_draw_label.c @@ -13,6 +13,9 @@ #include "../misc/lv_bidi.h" #include "../misc/lv_assert.h" +#if LV_USE_GPU_SDL + #include "../gpu/lv_gpu_sdl.h" +#endif /********************* * DEFINES *********************/ @@ -32,9 +35,13 @@ typedef uint8_t cmd_state_t; /********************** * STATIC PROTOTYPES **********************/ + +#if LV_USE_EXTERNAL_RENDERER == 0 LV_ATTRIBUTE_FAST_MEM static void draw_letter_normal(lv_coord_t pos_x, lv_coord_t pos_y, lv_font_glyph_dsc_t * g, const lv_area_t * clip_area, const uint8_t * map_p, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode); +#endif + #if LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX static void draw_letter_subpx(lv_coord_t pos_x, lv_coord_t pos_y, lv_font_glyph_dsc_t * g, const lv_area_t * clip_area, const uint8_t * map_p, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode); @@ -387,6 +394,7 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_label(const lv_area_t * coords, const lv_area LV_ASSERT_MEM_INTEGRITY(); } +#if LV_USE_EXTERNAL_RENDERER == 0 /********************** * STATIC FUNCTIONS **********************/ @@ -616,6 +624,7 @@ LV_ATTRIBUTE_FAST_MEM static void draw_letter_normal(lv_coord_t pos_x, lv_coord_ lv_mem_buf_release(mask_buf); } +#endif #if LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX static void draw_letter_subpx(lv_coord_t pos_x, lv_coord_t pos_y, lv_font_glyph_dsc_t * g, const lv_area_t * clip_area, diff --git a/src/draw/lv_draw_line.c b/src/draw/lv_draw_line.c index d79c597b8..407a280a7 100644 --- a/src/draw/lv_draw_line.c +++ b/src/draw/lv_draw_line.c @@ -34,7 +34,6 @@ LV_ATTRIBUTE_FAST_MEM static void draw_line_hor(const lv_point_t * point1, const LV_ATTRIBUTE_FAST_MEM static void draw_line_ver(const lv_point_t * point1, const lv_point_t * point2, const lv_area_t * clip, const lv_draw_line_dsc_t * dsc); - /********************** * STATIC VARIABLES **********************/ @@ -117,6 +116,7 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_line(const lv_point_t * point1, const lv_poin * STATIC FUNCTIONS **********************/ + LV_ATTRIBUTE_FAST_MEM static void draw_line_hor(const lv_point_t * point1, const lv_point_t * point2, const lv_area_t * clip, const lv_draw_line_dsc_t * dsc) diff --git a/src/draw/lv_draw_mask.c b/src/draw/lv_draw_mask.c index 9860d4b00..d60e461d9 100644 --- a/src/draw/lv_draw_mask.c +++ b/src/draw/lv_draw_mask.c @@ -126,6 +126,39 @@ LV_ATTRIBUTE_FAST_MEM lv_draw_mask_res_t lv_draw_mask_apply(lv_opa_t * mask_buf, return changed ? LV_DRAW_MASK_RES_CHANGED : LV_DRAW_MASK_RES_FULL_COVER; } +/** + * Apply the specified buffers on a line. Used internally by the library's drawing routines. + * @param mask_buf store the result mask here. Has to be `len` byte long. Should be initialized with `0xFF`. + * @param abs_x absolute X coordinate where the line to calculate start + * @param abs_y absolute Y coordinate where the line to calculate start + * @param len length of the line to calculate (in pixel count) + * @param ids ID array of added buffers + * @param ids_count number of ID array + * @return One of these values: + * - `LV_DRAW_MASK_RES_FULL_TRANSP`: the whole line is transparent. `mask_buf` is not set to zero + * - `LV_DRAW_MASK_RES_FULL_COVER`: the whole line is fully visible. `mask_buf` is unchanged + * - `LV_DRAW_MASK_RES_CHANGED`: `mask_buf` has changed, it shows the desired opacity of each pixel in the given line + */ +LV_ATTRIBUTE_FAST_MEM lv_draw_mask_res_t lv_draw_mask_apply_ids(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y, + lv_coord_t len, const int16_t *ids, int16_t ids_count) +{ + bool changed = false; + _lv_draw_mask_common_dsc_t * dsc; + + for (int i = 0; i < ids_count; i++) { + int16_t id = ids[i]; + if (id == LV_MASK_ID_INV) continue; + dsc = LV_GC_ROOT(_lv_draw_mask_list[id]).param; + if (!dsc) continue; + lv_draw_mask_res_t res = LV_DRAW_MASK_RES_FULL_COVER; + res = dsc->cb(mask_buf, abs_x, abs_y, len, dsc); + if(res == LV_DRAW_MASK_RES_TRANSP) return LV_DRAW_MASK_RES_TRANSP; + else if(res == LV_DRAW_MASK_RES_CHANGED) changed = true; + } + + return changed ? LV_DRAW_MASK_RES_CHANGED : LV_DRAW_MASK_RES_FULL_COVER; +} + /** * Remove a mask with a given ID * @param id the ID of the mask. Returned by `lv_draw_mask_add` diff --git a/src/draw/lv_draw_mask.h b/src/draw/lv_draw_mask.h index c63a7802e..123cd9163 100644 --- a/src/draw/lv_draw_mask.h +++ b/src/draw/lv_draw_mask.h @@ -236,6 +236,22 @@ int16_t lv_draw_mask_add(void * param, void * custom_id); LV_ATTRIBUTE_FAST_MEM lv_draw_mask_res_t lv_draw_mask_apply(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y, lv_coord_t len); +/** + * Apply the specified buffers on a line. Used internally by the library's drawing routines. + * @param mask_buf store the result mask here. Has to be `len` byte long. Should be initialized with `0xFF`. + * @param abs_x absolute X coordinate where the line to calculate start + * @param abs_y absolute Y coordinate where the line to calculate start + * @param len length of the line to calculate (in pixel count) + * @param ids ID array of added buffers + * @param ids_count number of ID array + * @return One of these values: + * - `LV_DRAW_MASK_RES_FULL_TRANSP`: the whole line is transparent. `mask_buf` is not set to zero + * - `LV_DRAW_MASK_RES_FULL_COVER`: the whole line is fully visible. `mask_buf` is unchanged + * - `LV_DRAW_MASK_RES_CHANGED`: `mask_buf` has changed, it shows the desired opacity of each pixel in the given line + */ +LV_ATTRIBUTE_FAST_MEM lv_draw_mask_res_t lv_draw_mask_apply_ids(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y, + lv_coord_t len, const int16_t *ids, int16_t ids_count); + //! @endcond /** diff --git a/src/draw/lv_draw_rect.c b/src/draw/lv_draw_rect.c index 1c0195b93..948c0bb30 100644 --- a/src/draw/lv_draw_rect.c +++ b/src/draw/lv_draw_rect.c @@ -28,6 +28,7 @@ /********************** * STATIC PROTOTYPES **********************/ +#if LV_USE_EXTERNAL_RENDERER == 0 LV_ATTRIBUTE_FAST_MEM static void draw_bg(const lv_area_t * coords, const lv_area_t * clip_area, const lv_draw_rect_dsc_t * dsc); LV_ATTRIBUTE_FAST_MEM static void draw_bg_img(const lv_area_t * coords, const lv_area_t * clip, @@ -54,6 +55,7 @@ static void draw_border_simple(const lv_area_t * clip, const lv_area_t * outer_a #if LV_DRAW_COMPLEX LV_ATTRIBUTE_FAST_MEM static inline lv_color_t grad_get(const lv_draw_rect_dsc_t * dsc, lv_coord_t s, lv_coord_t i); #endif +#endif /********************** * STATIC VARIABLES @@ -89,6 +91,7 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_rect_dsc_init(lv_draw_rect_dsc_t * dsc) dsc->border_side = LV_BORDER_SIDE_FULL; } +#if LV_USE_EXTERNAL_RENDERER == 0 /** * Draw a rectangle * @param coords the coordinates of the rectangle @@ -1331,3 +1334,5 @@ static void draw_border_simple(const lv_area_t * clip, const lv_area_t * outer_a } } + +#endif \ No newline at end of file diff --git a/src/draw/lv_draw_rect.h b/src/draw/lv_draw_rect.h index 5ad45d836..f43a0eafb 100644 --- a/src/draw/lv_draw_rect.h +++ b/src/draw/lv_draw_rect.h @@ -80,10 +80,10 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_rect_dsc_init(lv_draw_rect_dsc_t * dsc); /** * Draw a rectangle * @param coords the coordinates of the rectangle - * @param mask the rectangle will be drawn only in this mask + * @param clip the rectangle will be drawn only in this area * @param dsc pointer to an initialized `lv_draw_rect_dsc_t` variable */ -void lv_draw_rect(const lv_area_t * coords, const lv_area_t * mask, const lv_draw_rect_dsc_t * dsc); +void lv_draw_rect(const lv_area_t * coords, const lv_area_t * clip, const lv_draw_rect_dsc_t * dsc); /** * Draw a pixel diff --git a/src/gpu/lv_gpu.mk b/src/gpu/lv_gpu.mk index d909b0335..e162d79f6 100644 --- a/src/gpu/lv_gpu.mk +++ b/src/gpu/lv_gpu.mk @@ -2,8 +2,11 @@ CSRCS += lv_gpu_nxp_pxp.c CSRCS += lv_gpu_nxp_pxp_osa.c CSRCS += lv_gpu_nxp_vglite.c CSRCS += lv_gpu_stm32_dma2d.c +CSRCS += lv_gpu_sdl.c DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/gpu VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/gpu CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/gpu" + +include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/gpu/sdl/lv_gpu_sdl.mk \ No newline at end of file diff --git a/src/gpu/lv_gpu_sdl.c b/src/gpu/lv_gpu_sdl.c new file mode 100644 index 000000000..2b46d87c9 --- /dev/null +++ b/src/gpu/lv_gpu_sdl.c @@ -0,0 +1,59 @@ +/** + * @file lv_gpu_sdl.c + * + */ + +/********************* + * INCLUDES + *********************/ + + +#include "../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include "lv_gpu_sdl.h" +#include "sdl/lv_gpu_sdl_utils.h" +#include "sdl/lv_gpu_sdl_texture_cache.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_gpu_sdl_init() +{ + _lv_gpu_sdl_utils_init(); + _lv_gpu_sdl_texture_cache_init(); +} + +void lv_gpu_sdl_deinit() +{ + _lv_gpu_sdl_texture_cache_deinit(); + _lv_gpu_sdl_utils_deinit(); +} +/********************** + * STATIC FUNCTIONS + **********************/ + + +#endif /*LV_USE_GPU_SDL*/ diff --git a/src/gpu/lv_gpu_sdl.h b/src/gpu/lv_gpu_sdl.h new file mode 100644 index 000000000..77a9f1b4f --- /dev/null +++ b/src/gpu/lv_gpu_sdl.h @@ -0,0 +1,69 @@ +/** + * @file lv_gpu_sdl.h + * + */ + +#ifndef LV_GPU_SDL_H +#define LV_GPU_SDL_H + + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include LV_GPU_SDL_INCLUDE_PATH + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void lv_gpu_sdl_init(); + +/** + * @brief Free caches + * + */ +void lv_gpu_sdl_deinit(); + +/*====================== + * Add/remove functions + *=====================*/ + +/*===================== + * Setter functions + *====================*/ + +/*===================== + * Getter functions + *====================*/ + +/*===================== + * Other functions + *====================*/ + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_GPU_SDL*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_GPU_SDL_H*/ diff --git a/src/gpu/sdl/README.md b/src/gpu/sdl/README.md new file mode 100644 index 000000000..289fc04e4 --- /dev/null +++ b/src/gpu/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_gpu_sdl_stack_blur.c` + +Contains modified code from [android-stackblur](https://github.com/kikoso/android-stackblur) project. +Apache License 2.0 + +### `lv_gpu_sdl_lru.c`/`lv_gpu_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/src/gpu/sdl/lv_gpu_sdl.mk b/src/gpu/sdl/lv_gpu_sdl.mk new file mode 100644 index 000000000..f258c8ba6 --- /dev/null +++ b/src/gpu/sdl/lv_gpu_sdl.mk @@ -0,0 +1,16 @@ +CSRCS += lv_gpu_sdl_draw_arc.c +CSRCS += lv_gpu_sdl_draw_blend.c +CSRCS += lv_gpu_sdl_draw_img.c +CSRCS += lv_gpu_sdl_draw_label.c +CSRCS += lv_gpu_sdl_draw_line.c +CSRCS += lv_gpu_sdl_draw_rect.c +CSRCS += lv_gpu_sdl_lru.c +CSRCS += lv_gpu_sdl_mask.c +CSRCS += lv_gpu_sdl_stack_blur.c +CSRCS += lv_gpu_sdl_texture_cache.c +CSRCS += lv_gpu_sdl_utils.c + +DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/gpu/sdl +VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/gpu/sdl + +CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/gpu/sdl" diff --git a/src/gpu/sdl/lv_gpu_sdl_draw_arc.c b/src/gpu/sdl/lv_gpu_sdl_draw_arc.c new file mode 100644 index 000000000..21bf9d8fa --- /dev/null +++ b/src/gpu/sdl/lv_gpu_sdl_draw_arc.c @@ -0,0 +1,163 @@ +/** + * @file lv_gpu_sdl_draw_arc.c + * + * This implementation does not functioning properly so it's not enabled + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include "../../hal/lv_hal_disp.h" +#include "../../core/lv_refr.h" +#include "../../draw/lv_draw_arc.h" +#include "lv_gpu_sdl_utils.h" +#include "lv_gpu_sdl_lru.h" +#include "lv_gpu_sdl_texture_cache.h" +#include "lv_gpu_sdl_mask.h" + +#ifndef M_PI + #include +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + lv_gpu_cache_key_magic_t magic; + uint16_t radius; + uint16_t angle; + lv_coord_t width; + uint8_t rounded; +} lv_draw_arc_key_t; + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_arc2(lv_coord_t center_x, lv_coord_t center_y, uint16_t radius, uint16_t start_angle, uint16_t end_angle, + const lv_area_t * clip_area, const lv_draw_arc_dsc_t * dsc) +{ + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + lv_disp_drv_t * driver = disp->driver; + SDL_Renderer * renderer = (SDL_Renderer *) driver->user_data; + + 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 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; + + /*Increase 1 px each side to texture, to have better rotation result*/ + lv_area_t texture_area_out; + lv_area_copy(&texture_area_out, &area_out); + lv_area_increase(&texture_area_out, 1, 1); + + SDL_Rect area_out_rect, clip_rect; + lv_area_to_sdl_rect(&texture_area_out, &area_out_rect); + lv_area_to_sdl_rect(clip_area, &clip_rect); + + lv_draw_arc_key_t key = { + .magic = LV_GPU_CACHE_KEY_MAGIC_ARC, + .radius = radius, + .angle = ((end_angle - start_angle) % 360 + 360) % 360, + .width = dsc->width, + .rounded = dsc->rounded, + }; + // SDL_Texture *texture = lv_gpu_draw_cache_get(&key, sizeof(key)); + SDL_Texture * texture = NULL; + if(texture == NULL) { + /*Create inner the mask*/ + lv_draw_mask_radius_param_t mask_in_param; + lv_draw_mask_radius_init(&mask_in_param, &area_in, LV_RADIUS_CIRCLE, true); + int16_t mask_in_id = lv_draw_mask_add(&mask_in_param, NULL); + + lv_draw_mask_radius_param_t mask_out_param; + lv_draw_mask_radius_init(&mask_out_param, &area_out, LV_RADIUS_CIRCLE, false); + int16_t mask_out_id = lv_draw_mask_add(&mask_out_param, NULL); + + SDL_Surface * ark_mask; + if(key.angle < 360) { + while(start_angle >= 360) start_angle -= 360; + while(end_angle >= 360) end_angle -= 360; + lv_draw_mask_angle_param_t mask_angle_param; + lv_draw_mask_angle_init(&mask_angle_param, center_x, center_y, 0, key.angle); + int16_t mask_angle_id = lv_draw_mask_add(&mask_angle_param, NULL); + ark_mask = lv_sdl_apply_mask_surface(&texture_area_out, NULL, 0); + lv_draw_mask_remove_id(mask_angle_id); + } + else { + ark_mask = lv_sdl_apply_mask_surface(&texture_area_out, NULL, 0); + } + lv_draw_mask_remove_id(mask_out_id); + lv_draw_mask_remove_id(mask_in_id); + + if(dsc->rounded) { + SDL_Renderer * mask_renderer = SDL_CreateSoftwareRenderer(ark_mask); + lv_area_t cap_area = {.x1 = 0, .y1 = 0}; + lv_area_set_width(&cap_area, dsc->width); + lv_area_set_height(&cap_area, dsc->width); + + lv_draw_mask_radius_param_t mask_rout_param; + lv_draw_mask_radius_init(&mask_rout_param, &cap_area, LV_RADIUS_CIRCLE, false); + int16_t mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL); + SDL_Texture * round_texture = lv_sdl_gen_mask_texture(mask_renderer, &cap_area, &mask_rout_id, 1); + lv_draw_mask_remove_id(mask_rout_id); + + SDL_SetTextureBlendMode(round_texture, SDL_BLENDMODE_BLEND); + float mid_point = radius - key.width / 2.0f; + SDL_Rect cap_dst; + cap_dst.w = lv_area_get_width(&cap_area); + cap_dst.h = lv_area_get_height(&cap_area); + cap_dst.x = mid_point + lv_sdl_round(SDL_cos(0) * mid_point); + cap_dst.y = mid_point + lv_sdl_round(SDL_sin(0) * mid_point); + SDL_RenderCopy(mask_renderer, round_texture, NULL, &cap_dst); + cap_dst.x = mid_point + lv_sdl_round(SDL_cos(key.angle * M_PI / 180.0f) * mid_point); + cap_dst.y = mid_point + lv_sdl_round(SDL_sin(key.angle * M_PI / 180.0f) * mid_point); + SDL_RenderCopy(mask_renderer, round_texture, NULL, &cap_dst); + SDL_DestroyTexture(round_texture); + SDL_DestroyRenderer(mask_renderer); + } + texture = SDL_CreateTextureFromSurface(renderer, ark_mask); + SDL_FreeSurface(ark_mask); + + SDL_assert(texture); + // lv_gpu_draw_cache_put(&key, sizeof(key), texture); + } + + SDL_Color arc_color; + lv_color_to_sdl_color(&dsc->color, &arc_color); + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + SDL_SetTextureAlphaMod(texture, dsc->opa); + SDL_SetTextureColorMod(texture, arc_color.r, arc_color.g, arc_color.b); + SDL_RenderSetClipRect(renderer, &clip_rect); + SDL_RenderCopyEx(renderer, texture, NULL, &area_out_rect, start_angle, NULL, SDL_FLIP_NONE); + SDL_DestroyTexture(texture); +} + +#endif /*LV_USE_GPU_SDL*/ diff --git a/src/gpu/sdl/lv_gpu_sdl_draw_blend.c b/src/gpu/sdl/lv_gpu_sdl_draw_blend.c new file mode 100644 index 000000000..33d391366 --- /dev/null +++ b/src/gpu/sdl/lv_gpu_sdl_draw_blend.c @@ -0,0 +1,95 @@ +/** + * @file lv_gpu_sdl_draw_blend.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include "../../draw/lv_draw_blend.h" +#include "../../core/lv_refr.h" +#include "lv_gpu_sdl_texture_cache.h" +#include "lv_gpu_sdl_utils.h" +#include "lv_gpu_sdl_mask.h" + +#include LV_GPU_SDL_INCLUDE_PATH + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void _lv_blend_fill(const lv_area_t * clip_area, const lv_area_t * fill_area, lv_color_t color, + lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_opa_t opa, lv_blend_mode_t mode) +{ + /*Do not draw transparent things*/ + if(opa < LV_OPA_MIN) return; + if(mask_res == LV_DRAW_MASK_RES_TRANSP) return; + + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + SDL_Renderer * renderer = (SDL_Renderer *) disp->driver->user_data; + + /*Get clipped fill area which is the real draw area. + *It is always the same or inside `fill_area`*/ + lv_area_t draw_area; + if(!_lv_area_intersect(&draw_area, clip_area, fill_area)) return; + + SDL_Rect draw_area_rect; + lv_area_to_sdl_rect(&draw_area, &draw_area_rect); + if(mask) { + SDL_Surface * mask_surface = lv_sdl_create_mask_surface(mask, lv_area_get_width(&draw_area), + lv_area_get_height(&draw_area), + lv_area_get_width(&draw_area)); + SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, mask_surface); + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + SDL_SetTextureAlphaMod(texture, opa); + SDL_SetTextureColorMod(texture, color.ch.red, color.ch.green, color.ch.blue); + SDL_RenderSetClipRect(renderer, &draw_area_rect); + SDL_RenderCopy(renderer, texture, NULL, &draw_area_rect); + SDL_DestroyTexture(texture); + SDL_FreeSurface(mask_surface); + } + else { + SDL_SetRenderDrawColor(renderer, color.ch.red, color.ch.green, color.ch.blue, opa); + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + SDL_RenderSetClipRect(renderer, &draw_area_rect); + SDL_RenderFillRect(renderer, &draw_area_rect); + } +} + +void _lv_blend_map(const lv_area_t * clip_area, const lv_area_t * map_area, + const lv_color_t * map_buf, lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_opa_t opa, + lv_blend_mode_t mode) +{ + +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_GPU_SDL*/ diff --git a/src/gpu/sdl/lv_gpu_sdl_draw_img.c b/src/gpu/sdl/lv_gpu_sdl_draw_img.c new file mode 100644 index 000000000..d777b42d1 --- /dev/null +++ b/src/gpu/sdl/lv_gpu_sdl_draw_img.c @@ -0,0 +1,171 @@ +/** + * @file lv_gpu_sdl_draw_img.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include "../../core/lv_refr.h" +#include "lv_gpu_sdl_utils.h" +#include "lv_gpu_sdl_lru.h" +#include "lv_gpu_sdl_texture_cache.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + + +/********************** + * 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 VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_img(const lv_area_t * coords, const lv_area_t * mask, const void * src, + const lv_draw_img_dsc_t * draw_dsc) +{ + if(draw_dsc->opa <= LV_OPA_MIN) return; + + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + SDL_Renderer * renderer = (SDL_Renderer *) disp->driver->user_data; + + size_t key_size; + lv_gpu_sdl_cache_key_head_img_t * key = lv_gpu_sdl_img_cache_key_create(src, draw_dsc->frame_id, &key_size); + bool texture_found = false; + SDL_Texture * texture = lv_gpu_draw_cache_get(key, key_size, &texture_found); + if(!texture_found) { + _lv_img_cache_entry_t * cdsc = _lv_img_cache_open(src, draw_dsc->recolor, draw_dsc->frame_id); + lv_gpu_sdl_cache_flag_t tex_flags = 0; + if(cdsc) { + lv_img_decoder_dsc_t * dsc = &cdsc->dec_dsc; + if(dsc->user_data && SDL_memcmp(dsc->user_data, LV_GPU_SDL_DEC_DSC_TEXTURE_HEAD, 8) == 0) { + texture = ((lv_gpu_sdl_dec_dsc_userdata_t *) dsc->user_data)->texture; + tex_flags |= LV_GPU_SDL_CACHE_FLAG_MANAGED; + } + else { + texture = upload_img_texture(renderer, dsc); + } +#if LV_IMG_CACHE_DEF_SIZE == 0 + lv_img_decoder_close(dsc); +#endif + } + if(texture && cdsc) { + lv_img_header_t * header = SDL_malloc(sizeof(lv_img_header_t)); + SDL_memcpy(header, &cdsc->dec_dsc.header, sizeof(lv_img_header_t)); + lv_gpu_draw_cache_put_advanced(key, key_size, texture, header, SDL_free, tex_flags); + } + else { + lv_gpu_draw_cache_put(key, key_size, NULL); + } + } + SDL_free(key); + if(!texture) { + return; + } + + SDL_Rect mask_rect, coords_rect; + lv_area_to_sdl_rect(mask, &mask_rect); + lv_area_to_sdl_rect(coords, &coords_rect); + lv_area_zoom_to_sdl_rect(coords, &coords_rect, draw_dsc->zoom, &draw_dsc->pivot); + + SDL_Point pivot = {.x = draw_dsc->pivot.x, .y = draw_dsc->pivot.y}; + SDL_SetTextureAlphaMod(texture, draw_dsc->opa); + SDL_SetTextureColorMod(texture, 0xFF, 0xFF, 0xFF); + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + SDL_RenderSetClipRect(renderer, &mask_rect); + + SDL_Color recolor; + lv_color_to_sdl_color(&draw_dsc->recolor, &recolor); + /*Draw original image if not fully recolored*/ + /*TODO: what if the image is translucent as well?*/ + if(draw_dsc->recolor_opa < LV_OPA_MAX) { + SDL_RenderCopyEx(renderer, texture, NULL, &coords_rect, draw_dsc->angle, &pivot, SDL_FLIP_NONE); + } + + SDL_SetTextureColorMod(texture, recolor.r, recolor.g, recolor.b); + if(draw_dsc->recolor_opa >= LV_OPA_MAX) { + /*Draw fully colored image*/ + SDL_SetTextureAlphaMod(texture, draw_dsc->opa); + SDL_RenderCopyEx(renderer, texture, NULL, &coords_rect, draw_dsc->angle, &pivot, SDL_FLIP_NONE); + } + else if(draw_dsc->recolor_opa >= LV_OPA_MIN) { + SDL_SetTextureAlphaMod(texture, draw_dsc->recolor_opa); + SDL_RenderCopyEx(renderer, texture, NULL, &coords_rect, draw_dsc->angle, &pivot, SDL_FLIP_NONE); + } +} + +/********************** + * 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 == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED; + uint32_t h = dsc->header.h; + uint32_t w = 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 = dsc->header.h; + lv_coord_t w = 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; +} + + +#endif /*LV_USE_GPU_SDL*/ diff --git a/src/gpu/sdl/lv_gpu_sdl_draw_label.c b/src/gpu/sdl/lv_gpu_sdl_draw_label.c new file mode 100644 index 000000000..e87773e53 --- /dev/null +++ b/src/gpu/sdl/lv_gpu_sdl_draw_label.c @@ -0,0 +1,332 @@ +/** + * @file lv_gpu_sdl_draw_label.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include "../../draw/lv_draw_label.h" +#include "../../font/lv_font_fmt_txt.h" +#include "../../core/lv_refr.h" +#include "../../misc/lv_utils.h" + +#include LV_GPU_SDL_INCLUDE_PATH + +#include "lv_gpu_sdl_utils.h" +#include "lv_gpu_sdl_texture_cache.h" +#include "lv_gpu_sdl_mask.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct lv_sdl_font_atlas_t { + SDL_Rect * pos; +} lv_sdl_font_atlas_t; + +typedef struct { + lv_gpu_cache_key_magic_t magic; + const lv_font_t * font_p; + uint32_t cmap_index; +} lv_font_key_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void draw_letter_masked(SDL_Renderer * renderer, SDL_Texture * atlas, SDL_Rect * src, SDL_Rect * dst, + SDL_Rect * clip, lv_color_t color, lv_opa_t opa); + +static void font_atlas_free(lv_sdl_font_atlas_t * atlas); + +static SDL_Texture * font_atlas_bake(SDL_Renderer * renderer, const lv_font_t * font_p, uint32_t cmap_idx, + lv_sdl_font_atlas_t * atlas); + +static int32_t unicode_list_compare(const void * ref, const void * element); + +static bool font_cmap_find_index(const lv_font_fmt_txt_dsc_t * dsc, uint32_t letter, uint32_t * cmap_index, + uint32_t * char_index); + +static lv_font_key_t font_key_create(const lv_font_t * font_p, uint32_t cmap_index); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_letter(const lv_point_t * pos_p, const lv_area_t * clip_area, + const lv_font_t * font_p, uint32_t letter, lv_color_t color, lv_opa_t opa, + lv_blend_mode_t blend_mode) +{ + 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); + } + 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; + + /*If the letter is completely out of mask don't draw it*/ + if(pos_x + g.box_w < clip_area->x1 || + pos_x > clip_area->x2 || + pos_y + g.box_h < clip_area->y1 || + pos_y > clip_area->y2) { + return; + } + lv_area_t dst = {pos_x, pos_y, pos_x + g.box_w - 1, pos_y + g.box_h - 1}; + uint32_t atlas_index; + uint32_t cmap_index; + if(!font_cmap_find_index(font_p->dsc, letter, &cmap_index, &atlas_index)) { + return; + } + lv_font_key_t key = font_key_create(font_p, cmap_index); + lv_sdl_font_atlas_t * atlas = NULL; + bool found = false; + SDL_Texture * texture = lv_gpu_draw_cache_get_with_userdata(&key, sizeof(key), &found, (void **) &atlas); + + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + SDL_Renderer * renderer = (SDL_Renderer *) disp->driver->user_data; + + if(!found) { + atlas = SDL_malloc(sizeof(lv_sdl_font_atlas_t)); + texture = font_atlas_bake(renderer, font_p, cmap_index, atlas); + lv_gpu_draw_cache_put_advanced(&key, sizeof(key), texture, atlas, (lv_lru_free_t *) font_atlas_free, 0); + } + if(texture == NULL) return; + SDL_Rect dstrect = {.x = pos_x, .y = pos_y, .w = g.box_w, .h = g.box_h}; + + SDL_Rect clip_area_rect; + lv_area_to_sdl_rect(clip_area, &clip_area_rect); + + if(lv_draw_mask_is_any(&dst)) { + draw_letter_masked(renderer, texture, &atlas->pos[atlas_index], &dstrect, &clip_area_rect, color, opa); + return; + } + + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + SDL_SetTextureAlphaMod(texture, opa); + SDL_SetTextureColorMod(texture, color.ch.red, color.ch.green, color.ch.blue); + SDL_RenderSetClipRect(renderer, &clip_area_rect); + SDL_RenderCopy(renderer, texture, &atlas->pos[atlas_index], &dstrect); +} + + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void draw_letter_masked(SDL_Renderer * renderer, SDL_Texture * atlas, SDL_Rect * src, SDL_Rect * dst, + SDL_Rect * clip, lv_color_t color, lv_opa_t opa) +{ + SDL_Texture * screen = SDL_GetRenderTarget(renderer); + + lv_area_t mask_area = {.x1 = dst->x, .x2 = dst->x + dst->w - 1, .y1 = dst->y, .y2 = dst->y + dst->h - 1}; + SDL_Texture * content = lv_gpu_temp_texture_obtain(renderer, dst->w, dst->h); + SDL_SetTextureBlendMode(content, SDL_BLENDMODE_NONE); + SDL_SetRenderTarget(renderer, content); + SDL_RenderSetClipRect(renderer, NULL); + + /* Replace texture with clip mask */ + SDL_Rect mask_rect = {.w = dst->w, .h = dst->h, .x = 0, .y = 0}; + SDL_Texture * mask = lv_sdl_gen_mask_texture(renderer, &mask_area, NULL, 0); + SDL_SetTextureBlendMode(mask, SDL_BLENDMODE_NONE); + SDL_RenderCopy(renderer, mask, NULL, &mask_rect); + + /* Multiply with font atlas */ + SDL_SetTextureAlphaMod(atlas, 0xFF); + SDL_SetTextureColorMod(atlas, 0xFF, 0xFF, 0xFF); +#if SDL_VERSION_ATLEAST(2, 0, 6) + SDL_BlendMode mode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO, + SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ZERO, + SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDOPERATION_ADD); + SDL_SetTextureBlendMode(atlas, mode); +#else + SDL_SetTextureBlendMode(atlas, SDL_BLENDMODE_BLEND); +#endif + SDL_RenderCopy(renderer, atlas, src, &mask_rect); + + /* Draw composited part on screen */ + SDL_SetTextureBlendMode(content, SDL_BLENDMODE_BLEND); + SDL_SetTextureAlphaMod(content, opa); + SDL_SetTextureColorMod(content, color.ch.red, color.ch.green, color.ch.blue); + + SDL_SetRenderTarget(renderer, screen); + SDL_RenderSetClipRect(renderer, clip); + SDL_RenderCopy(renderer, content, &mask_rect, dst); + SDL_DestroyTexture(mask); +} + +SDL_Texture * font_atlas_bake(SDL_Renderer * renderer, const lv_font_t * font_p, uint32_t cmap_idx, + lv_sdl_font_atlas_t * atlas) +{ + /* Clear atlas struct */ + SDL_memset(atlas, 0, sizeof(lv_sdl_font_atlas_t)); + const lv_font_fmt_txt_dsc_t * dsc = (lv_font_fmt_txt_dsc_t *) font_p->dsc; + if(dsc->bitmap_format != LV_FONT_FMT_TXT_PLAIN) { + /* we don't support compressed font at this moment */ + return NULL; + } + const lv_font_fmt_txt_cmap_t * cmap = &dsc->cmaps[cmap_idx]; + int glyph_count = cmap->type == LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY ? cmap->range_length : cmap->list_length; + int atlas_size = 0; + while(atlas_size * atlas_size < glyph_count) { + atlas_size++; + } + int atlas_w = font_p->line_height * atlas_size; + int atlas_h = font_p->line_height * (glyph_count / atlas_size + 1); + if(atlas_w > 2048 || atlas_h > 2048) { + /*This atlas texture will be too large to load*/ + return NULL; + } + lv_opa_t * s1 = lv_mem_buf_get(atlas_w * atlas_h * sizeof(lv_opa_t)); + atlas->pos = SDL_malloc(sizeof(SDL_Rect) * glyph_count); + int atlas_y = 0; + int atlas_x = 0; + for(int i = 0; i < glyph_count; i++) { + int glyph_idx = cmap->glyph_id_start + i; + switch(cmap->type) { + case LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL: { + const uint8_t * gid_ofs_8 = cmap->glyph_id_ofs_list; + glyph_idx = gid_ofs_8[i]; + break; + } + case LV_FONT_FMT_TXT_CMAP_SPARSE_FULL: { + const uint16_t * gid_ofs_16 = cmap->glyph_id_ofs_list; + glyph_idx = gid_ofs_16[i]; + break; + } + } + const lv_font_fmt_txt_glyph_dsc_t * gd = &dsc->glyph_dsc[glyph_idx]; + if(atlas_x + gd->box_w >= atlas_w) { + atlas_x = 0; + atlas_y += font_p->line_height; + } + SDL_Rect * rect = &atlas->pos[i]; + rect->x = atlas_x; + rect->y = atlas_y; + rect->w = gd->box_w; + rect->h = gd->box_h; + if(gd->box_w <= 0 || gd->box_h <= 0) { + continue; + } + lv_sdl_to_8bpp(&s1[rect->y * atlas_w + rect->x], &dsc->glyph_bitmap[gd->bitmap_index], rect->w, + rect->h, atlas_w, dsc->bpp); + atlas_x += gd->box_w; + } + SDL_Surface * mask = lv_sdl_create_mask_surface(s1, atlas_w, atlas_h, atlas_w); + SDL_Texture * result = SDL_CreateTextureFromSurface(renderer, mask); + SDL_FreeSurface(mask); + lv_mem_buf_release(s1); + + if(!result) { + if(atlas->pos) { + SDL_free(atlas->pos); + } + SDL_memset(atlas, 0, sizeof(lv_sdl_font_atlas_t)); + } + return result; +} + +static void font_atlas_free(lv_sdl_font_atlas_t * atlas) +{ + if(atlas->pos) { + SDL_free(atlas->pos); + } + SDL_free(atlas); +} + +static bool font_cmap_find_index(const lv_font_fmt_txt_dsc_t * dsc, uint32_t letter, uint32_t * cmap_index, + uint32_t * char_index) +{ + for(int i = 0; i < dsc->cmap_num; i++) { + const lv_font_fmt_txt_cmap_t * cmap = &dsc->cmaps[i]; + /*Relative code point*/ + uint32_t rcp = letter - cmap->range_start; + if(rcp > cmap->range_length) continue; + if(cmap->type == LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY) { + *cmap_index = i; + *char_index = rcp; + return true; + } + else if(cmap->type == LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL) { + *cmap_index = i; + *char_index = rcp; + return true; + } + else if(cmap->type == LV_FONT_FMT_TXT_CMAP_SPARSE_TINY) { + uint16_t key = rcp; + uint16_t * p = _lv_utils_bsearch(&key, cmap->unicode_list, cmap->list_length, + sizeof(cmap->unicode_list[0]), unicode_list_compare); + if(!p) continue; + + *cmap_index = i; + *char_index = p - cmap->unicode_list; + return true; + } + else if(cmap->type == LV_FONT_FMT_TXT_CMAP_SPARSE_FULL) { + uint16_t key = rcp; + uint16_t * p = _lv_utils_bsearch(&key, cmap->unicode_list, cmap->list_length, + sizeof(cmap->unicode_list[0]), unicode_list_compare); + if(!p) continue; + + *cmap_index = i; + *char_index = p - cmap->unicode_list; + return true; + } + } + return 0; +} + +static int32_t unicode_list_compare(const void * ref, const void * element) +{ + return ((int32_t)(*(uint16_t *) ref)) - ((int32_t)(*(uint16_t *) element)); +} + +static lv_font_key_t font_key_create(const lv_font_t * font_p, uint32_t cmap_index) +{ + lv_font_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; + key.font_p = font_p; + key.cmap_index = cmap_index; + return key; +} + +#endif /*LV_USE_GPU_SDL*/ diff --git a/src/gpu/sdl/lv_gpu_sdl_draw_line.c b/src/gpu/sdl/lv_gpu_sdl_draw_line.c new file mode 100644 index 000000000..d1b3f362a --- /dev/null +++ b/src/gpu/sdl/lv_gpu_sdl_draw_line.c @@ -0,0 +1,158 @@ +/** + * @file lv_gpu_sdl_draw_line.c + * + * This implementation does not functioning properly so it's not enabled + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include LV_GPU_SDL_INCLUDE_PATH +#include "../../core/lv_refr.h" +#include "lv_gpu_sdl_utils.h" +#include "lv_gpu_sdl_lru.h" +#include "lv_gpu_sdl_texture_cache.h" +#include "lv_gpu_sdl_mask.h" + +#ifndef M_PI + #include +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + lv_gpu_cache_key_magic_t magic; + lv_coord_t length; + lv_coord_t thickness; +} lv_draw_line_key_t; + +static lv_draw_line_key_t line_key_create(lv_coord_t length, lv_coord_t thickness); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_line2(const lv_point_t * point1, const lv_point_t * point2, const lv_area_t * clip, + const lv_draw_line_dsc_t * dsc) +{ + if(dsc->width == 0) return; + if(dsc->opa <= LV_OPA_MIN) return; + + if(point1->x == point2->x && point1->y == point2->y) return; + + lv_area_t clip_line; + clip_line.x1 = LV_MIN(point1->x, point2->x) - dsc->width / 2; + clip_line.x2 = LV_MAX(point1->x, point2->x) + dsc->width / 2; + clip_line.y1 = LV_MIN(point1->y, point2->y) - dsc->width / 2; + clip_line.y2 = LV_MAX(point1->y, point2->y) + dsc->width / 2; + + if(!_lv_area_intersect(&clip_line, &clip_line, clip)) return; + + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + SDL_Renderer * renderer = (SDL_Renderer *) disp->driver->user_data; + + SDL_Color line_color; + lv_color_to_sdl_color(&dsc->color, &line_color); + + int length = lv_sdl_round(SDL_sqrt(SDL_pow(point2->y - point1->y + 1, 2) + SDL_pow(point2->x - point1->x + 1, 2))); + lv_coord_t thickness = dsc->width; + lv_draw_line_key_t key = line_key_create(length, thickness); + lv_area_t coords = {1, 1, length, dsc->width}; + lv_area_t tex_coords; + lv_area_copy(&tex_coords, &coords); + lv_area_increase(&tex_coords, 1, 1); + + SDL_Texture * texture = lv_gpu_draw_cache_get(&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, 0, false); + int16_t mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL); + texture = lv_sdl_gen_mask_texture(renderer, &tex_coords, &mask_rout_id, 1); + lv_draw_mask_remove_id(mask_rout_id); + SDL_assert(texture); + lv_gpu_draw_cache_put(&key, sizeof(key), texture); + } + double angle = SDL_atan2(point2->y - point1->y, point2->x - point1->x) * 180 / M_PI; + + SDL_Rect clip_rect; + lv_area_to_sdl_rect(&clip_line, &clip_rect); + + + SDL_Texture * screen = SDL_GetRenderTarget(renderer); + + SDL_Texture * content = lv_gpu_temp_texture_obtain(renderer, clip_rect.w, clip_rect.h); + SDL_SetTextureBlendMode(content, SDL_BLENDMODE_BLEND); + SDL_SetRenderTarget(renderer, content); + SDL_RenderSetClipRect(renderer, NULL); + + // /* Replace texture with clip mask */ + SDL_Rect mask_rect = {.w = clip_rect.w, .h = clip_rect.h, .x = 0, .y = 0}; + // SDL_Texture *mask = lv_sdl_gen_mask_texture(renderer, &clip_line, NULL, 0); + // SDL_SetTextureBlendMode(mask, SDL_BLENDMODE_NONE); + //// SDL_RenderCopy(renderer, mask, NULL, &mask_rect); + // + // SDL_SetTextureAlphaMod(texture, 0xFF); + // SDL_SetTextureColorMod(texture, 0xFF, 0xFF, 0xFF); + //#if SDL_VERSION_ATLEAST(2, 0, 6) + // SDL_BlendMode mode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO, + // SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ZERO, + // SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDOPERATION_ADD); + // SDL_SetTextureBlendMode(texture, mode); + // SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_NONE); + //#else + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_NONE); + //#endif + + SDL_Rect coords_rect; + lv_area_to_sdl_rect(&tex_coords, &coords_rect); + coords_rect.x = 0 - coords_rect.h / 2; + coords_rect.y = 0 - coords_rect.h / 2; + SDL_Point center = {coords_rect.h / 2, coords_rect.h / 2}; + SDL_RenderCopyEx(renderer, texture, NULL, &coords_rect, angle, ¢er, SDL_FLIP_NONE); + // + // /* Draw composited part on screen */ + SDL_SetTextureBlendMode(content, SDL_BLENDMODE_NONE); + SDL_SetTextureAlphaMod(content, dsc->opa); + SDL_SetTextureColorMod(content, line_color.r, line_color.g, line_color.b); + // + SDL_SetRenderTarget(renderer, screen); + // SDL_RenderSetClipRect(renderer, &clip_rect); + SDL_RenderSetClipRect(renderer, NULL); + SDL_RenderCopy(renderer, content, &mask_rect, &clip_rect); + // SDL_DestroyTexture(mask); +} + +static lv_draw_line_key_t line_key_create(lv_coord_t length, lv_coord_t thickness) +{ + lv_draw_line_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_LINE; + key.length = length; + key.thickness = thickness; + return key; +} + + +#endif /*LV_USE_GPU_SDL*/ diff --git a/src/gpu/sdl/lv_gpu_sdl_draw_rect.c b/src/gpu/sdl/lv_gpu_sdl_draw_rect.c new file mode 100644 index 000000000..689fb3d73 --- /dev/null +++ b/src/gpu/sdl/lv_gpu_sdl_draw_rect.c @@ -0,0 +1,733 @@ +/** + * @file lv_gpu_sdl_draw_rect.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include "../../draw/lv_draw_rect.h" +#include "../../hal/lv_hal_disp.h" +#include "../../core/lv_refr.h" +#include "lv_gpu_sdl_utils.h" +#include "lv_gpu_sdl_lru.h" +#include "lv_gpu_sdl_texture_cache.h" +#include "lv_gpu_sdl_mask.h" +#include "lv_gpu_sdl_stack_blur.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + lv_gpu_cache_key_magic_t magic; + lv_coord_t radius; + lv_coord_t size; +} lv_draw_rect_bg_key_t; + +typedef struct { + lv_gpu_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_gpu_cache_key_magic_t magic; + lv_coord_t rout, rin; + lv_coord_t thickness; + lv_coord_t size; + lv_border_side_t side; +} lv_draw_rect_border_key_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void draw_bg_color(SDL_Renderer * renderer, const lv_area_t * coords, const lv_draw_rect_dsc_t * dsc); + +static void draw_bg_img(const lv_area_t * coords, const lv_area_t * clip, + const lv_draw_rect_dsc_t * dsc); + +static void draw_border(SDL_Renderer * renderer, const lv_area_t * coords, const lv_draw_rect_dsc_t * dsc); + +static void draw_shadow(SDL_Renderer * renderer, const lv_area_t * coords, const lv_area_t * clip, + const lv_draw_rect_dsc_t * dsc); + +static void draw_outline(const lv_area_t * coords, const lv_area_t * clip, const lv_draw_rect_dsc_t * dsc); + +static void draw_border_generic(const lv_area_t * outer_area, const lv_area_t * inner_area, lv_coord_t rout, + lv_coord_t rin, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode); + +static void draw_border_simple(const lv_area_t * outer_area, const lv_area_t * inner_area, lv_color_t color, + lv_opa_t opa); + +static void draw_rect_masked(const lv_area_t * coords, const lv_area_t * clip, const lv_draw_rect_dsc_t * dsc); + +static void draw_rect_masked_simple(const lv_area_t * coords, const lv_area_t * mask, const lv_draw_rect_dsc_t * dsc); + +static void frag_render_corners(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size, + const lv_area_t * coords); + +static void frag_render_borders(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size, + const lv_area_t * coords); + +static void frag_render_center(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size, + const lv_area_t * coords); + +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, lv_coord_t thickness, + lv_coord_t size, lv_border_side_t side); + +/********************** + * 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_rect(const lv_area_t * coords, const lv_area_t * clip, const lv_draw_rect_dsc_t * dsc) +{ + lv_area_t draw_area; + bool has_draw_content = _lv_area_intersect(&draw_area, coords, clip); + + if(lv_draw_mask_is_any(&draw_area)) { + draw_rect_masked(coords, clip, dsc); + return; + } + + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + SDL_Renderer * renderer = (SDL_Renderer *) disp->driver->user_data; + + + SDL_Rect clip_rect; + lv_area_to_sdl_rect(clip, &clip_rect); + SDL_RenderSetClipRect(renderer, &clip_rect); + draw_shadow(renderer, coords, clip, dsc); + /* Shadows and outlines will also draw in extended area */ + if(has_draw_content) { + draw_bg_color(renderer, coords, dsc); + draw_bg_img(coords, clip, dsc); + draw_border(renderer, coords, dsc); + } + draw_outline(coords, clip, dsc); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void draw_bg_color(SDL_Renderer * renderer, const lv_area_t * coords, const lv_draw_rect_dsc_t * dsc) +{ + SDL_Color bg_color; + lv_color_to_sdl_color(&dsc->bg_color, &bg_color); + lv_coord_t radius = dsc->radius; + if(radius > 0) { + /*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), bg_min = LV_MIN(bg_w, bg_h); + /* If size isn't times of 2, increase 1 px */ + lv_coord_t min_half = bg_min % 2 == 0 ? bg_min / 2 : bg_min / 2 + 1; + lv_coord_t frag_size = radius == LV_RADIUS_CIRCLE ? min_half : LV_MIN(radius + 1, min_half); + lv_draw_rect_bg_key_t key = rect_bg_key_create(radius, frag_size); + lv_area_t coords_frag; + lv_area_copy(&coords_frag, coords); + lv_area_set_width(&coords_frag, frag_size); + lv_area_set_height(&coords_frag, frag_size); + SDL_Texture * texture = lv_gpu_draw_cache_get(&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_sdl_gen_mask_texture(renderer, &coords_frag, &mask_id, 1); + lv_draw_mask_remove_id(mask_id); + SDL_assert(texture); + lv_gpu_draw_cache_put(&key, sizeof(key), texture); + } + + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + SDL_SetTextureAlphaMod(texture, dsc->bg_opa); + SDL_SetTextureColorMod(texture, bg_color.r, bg_color.g, bg_color.b); + frag_render_corners(renderer, texture, frag_size, coords); + frag_render_borders(renderer, texture, frag_size, coords); + frag_render_center(renderer, texture, frag_size, coords); + } + else { + SDL_Rect coords_rect; + lv_area_to_sdl_rect(coords, &coords_rect); + SDL_SetRenderDrawColor(renderer, bg_color.r, bg_color.g, bg_color.b, dsc->bg_opa); + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + SDL_RenderFillRect(renderer, &coords_rect); + } +} + +static void draw_bg_img(const lv_area_t * coords, const lv_area_t * clip, 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(&a, clip, &label_draw_dsc, dsc->bg_img_src, NULL); + } + else { + lv_img_header_t header; + size_t key_size; + lv_gpu_sdl_cache_key_head_img_t * key = lv_gpu_sdl_img_cache_key_create(dsc->bg_img_src, 0, &key_size); + bool key_found; + lv_img_header_t * cache_header = NULL; + SDL_Texture * texture = lv_gpu_draw_cache_get_with_userdata(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; + + /*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(&area, clip, dsc->bg_img_src, &img_dsc); + } + 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(&area, clip, dsc->bg_img_src, &img_dsc); + } + } + } + } +} + +static void draw_shadow(SDL_Renderer * renderer, 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; + lv_coord_t sh_width = lv_area_get_width(&core_area); + lv_coord_t sh_height = lv_area_get_height(&core_area); + lv_coord_t sh_min = LV_MIN(sh_width, sh_height); + /* If size isn't times of 2, increase 1 px */ + lv_coord_t min_half = sh_min % 2 == 0 ? sh_min / 2 : sh_min / 2 + 1; + /* No matter how big the shadow is, what we need is just a corner */ + lv_coord_t frag_size = radius == LV_RADIUS_CIRCLE ? min_half : LV_MIN(radius + 1, min_half); + /* This is how big the corner is after blurring */ + lv_coord_t blur_frag_size = frag_size + sw + 2; + + lv_draw_rect_shadow_key_t key = rect_shadow_key_create(radius, frag_size, sw); + + lv_area_t blur_frag; + lv_area_copy(&blur_frag, &shadow_area); + lv_area_set_width(&blur_frag, blur_frag_size * 2); + lv_area_set_height(&blur_frag, blur_frag_size * 2); + SDL_Texture * texture = lv_gpu_draw_cache_get(&key, sizeof(key), NULL); + if(texture == NULL) { + lv_draw_mask_radius_param_t mask_rout_param; + lv_draw_mask_radius_init(&mask_rout_param, &core_area, radius, false); + int16_t mask_id = lv_draw_mask_add(&mask_rout_param, NULL); + lv_opa_t * mask_buf = lv_draw_mask_dump(&blur_frag, &mask_id, 1); + lv_stack_blur_grayscale(mask_buf, lv_area_get_width(&blur_frag), lv_area_get_height(&blur_frag), sw / 2 + 1); + texture = lv_sdl_create_mask_texture(renderer, mask_buf, blur_frag_size, blur_frag_size, + lv_area_get_width(&blur_frag)); + lv_mem_buf_release(mask_buf); + lv_draw_mask_remove_id(mask_id); + SDL_assert(texture); + lv_gpu_draw_cache_put(&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); + + frag_render_corners(renderer, texture, blur_frag_size, &shadow_area); + frag_render_borders(renderer, texture, blur_frag_size, &shadow_area); + frag_render_center(renderer, texture, blur_frag_size, &shadow_area); +} + + +static void draw_border(SDL_Renderer * renderer, const lv_area_t * coords, 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); + + + if(dsc->border_side != LV_BORDER_SIDE_FULL) { + SDL_SetRenderDrawColor(renderer, border_color.r, border_color.g, border_color.b, dsc->border_opa); + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + for(int w = 0; w <= dsc->border_width; w++) { + if(dsc->border_side & LV_BORDER_SIDE_TOP) { + SDL_RenderDrawLine(renderer, coords->x1, coords->y1 + w, coords->x2, coords->y1 + w); + } + if(dsc->border_side & LV_BORDER_SIDE_BOTTOM) { + SDL_RenderDrawLine(renderer, coords->x1, coords->y2 - w, coords->x2, coords->y2 - w); + } + if(dsc->border_side & LV_BORDER_SIDE_LEFT) { + SDL_RenderDrawLine(renderer, coords->x1 + w, coords->y1, coords->x1 + w, coords->y2); + } + if(dsc->border_side & LV_BORDER_SIDE_RIGHT) { + SDL_RenderDrawLine(renderer, coords->x2 - w, coords->y1, coords->x2 - w, coords->y2); + } + } + } + else { + int32_t coords_w = lv_area_get_width(coords); + int32_t coords_h = lv_area_get_height(coords); + int32_t rout = dsc->radius; + int32_t short_side = LV_MIN(coords_w, coords_h); + if(rout > short_side >> 1) rout = short_side >> 1; + + /*Get the inner area*/ + lv_area_t area_inner; + lv_area_copy(&area_inner, coords); + 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 = rout - dsc->border_width; + if(rin < 0) rin = 0; + draw_border_generic(coords, &area_inner, rout, rin, dsc->border_color, dsc->border_opa, + dsc->blend_mode); + } +} + +static void draw_outline(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); + + /*Extend the outline into the background area if it's overlapping the edge*/ + lv_coord_t pad = (dsc->outline_pad == 0 ? (dsc->outline_pad - 1) : dsc->outline_pad); + 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); + int32_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(&area_outer, &area_inner, rout, rin, dsc->outline_color, dsc->outline_opa, + dsc->blend_mode); +} + +static void draw_border_generic(const lv_area_t * outer_area, const lv_area_t * inner_area, 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; + + if(rout == 0 || rin == 0) { + draw_border_simple(outer_area, inner_area, color, opa); + return; + } + + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + SDL_Renderer * renderer = (SDL_Renderer *) disp->driver->user_data; + + lv_coord_t border_width = lv_area_get_width(outer_area); + lv_coord_t border_height = lv_area_get_height(outer_area); + lv_coord_t border_min = LV_MIN(border_width, border_height); + lv_coord_t min_half = border_min % 2 == 0 ? border_min / 2 : border_min / 2 + 1; + lv_coord_t frag_size = rout == LV_RADIUS_CIRCLE ? min_half : LV_MIN(rout + 1, min_half); + lv_draw_rect_border_key_t key = rect_border_key_create(rout, rin, inner_area->x1 - outer_area->x1 + 1, + frag_size, LV_BORDER_SIDE_FULL); + SDL_Texture * texture = lv_gpu_draw_cache_get(&key, sizeof(key), NULL); + if(texture == NULL) { + /*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, outer_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; + lv_draw_mask_radius_param_t mask_rin_param; + lv_draw_mask_radius_init(&mask_rin_param, inner_area, rin, true); + mask_ids[1] = lv_draw_mask_add(&mask_rin_param, NULL); + + lv_area_t frag_area; + lv_area_copy(&frag_area, outer_area); + lv_area_set_width(&frag_area, frag_size); + lv_area_set_height(&frag_area, frag_size); + + texture = lv_sdl_gen_mask_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_gpu_draw_cache_put(&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); + + frag_render_corners(renderer, texture, frag_size, outer_area); + frag_render_borders(renderer, texture, frag_size, outer_area); +} + +static void draw_border_simple(const lv_area_t * outer_area, const lv_area_t * inner_area, lv_color_t color, + lv_opa_t opa) +{ + + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + SDL_Renderer * renderer = (SDL_Renderer *) disp->driver->user_data; + + SDL_Color color_sdl; + lv_color_to_sdl_color(&color, &color_sdl); + + SDL_SetRenderDrawColor(renderer, color_sdl.r, color_sdl.g, color_sdl.b, opa); + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + SDL_Rect simple_rect; + simple_rect.w = inner_area->x2 - outer_area->x1 + 1; + simple_rect.h = inner_area->y1 - outer_area->y1 + 1; + /*Top border*/ + simple_rect.x = outer_area->x1; + simple_rect.y = outer_area->y1; + SDL_RenderFillRect(renderer, &simple_rect); + /*Bottom border*/ + simple_rect.x = inner_area->x1; + simple_rect.y = inner_area->y2; + SDL_RenderFillRect(renderer, &simple_rect); + + simple_rect.w = inner_area->x1 - outer_area->x1 + 1; + simple_rect.h = inner_area->y2 - outer_area->y1 + 1; + /*Left border*/ + simple_rect.x = outer_area->x1; + simple_rect.y = inner_area->y1; + SDL_RenderFillRect(renderer, &simple_rect); + /*Right border*/ + simple_rect.x = inner_area->x2; + simple_rect.y = outer_area->y1; + SDL_RenderFillRect(renderer, &simple_rect); + +} + +static void frag_render_corners(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size, + const lv_area_t * coords) +{ + lv_coord_t bg_w = lv_area_get_width(coords); + lv_coord_t bg_h = lv_area_get_height(coords); + SDL_Rect srcrect = {0, 0, frag_size, frag_size}; + SDL_Rect dstrect = {.x = coords->x1, .y = coords->y1, .w = frag_size, .h = frag_size}; + /* Upper left */ + SDL_RenderCopyEx(renderer, frag, &srcrect, &dstrect, 0, NULL, SDL_FLIP_NONE); + /* Upper right, clip right edge if too big */ + srcrect.w = dstrect.w = LV_MIN(frag_size, bg_w - frag_size); + dstrect.x = coords->x2 - srcrect.w + 1; + SDL_RenderCopyEx(renderer, frag, &srcrect, &dstrect, 0, NULL, SDL_FLIP_HORIZONTAL); + /* Lower right, clip bottom edge if too big */ + srcrect.h = dstrect.h = LV_MIN(frag_size, bg_h - frag_size); + dstrect.y = coords->y2 - srcrect.h + 1; + SDL_RenderCopyEx(renderer, frag, &srcrect, &dstrect, 0, NULL, SDL_FLIP_HORIZONTAL | SDL_FLIP_VERTICAL); + /* Lower left, right edge should not be clipped */ + srcrect.w = dstrect.w = frag_size; + dstrect.x = coords->x1; + SDL_RenderCopyEx(renderer, frag, &srcrect, &dstrect, 0, NULL, SDL_FLIP_VERTICAL); +} + +static void frag_render_borders(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size, + const lv_area_t * coords) +{ + lv_coord_t bg_w = lv_area_get_width(coords); + lv_coord_t bg_h = lv_area_get_height(coords); + SDL_Rect srcrect; + SDL_Rect dstrect; + /* For top/bottom edges, stretch pixels on the right */ + srcrect.h = dstrect.h = frag_size; + dstrect.w = bg_w - frag_size * 2; + /* Has space to fill */ + if(dstrect.w > 0 && dstrect.h > 0) { + srcrect.w = 1; + srcrect.y = 0; + srcrect.x = frag_size - 1; + dstrect.x = coords->x1 + frag_size; + /* Top edge */ + dstrect.y = coords->y1; + SDL_RenderCopy(renderer, frag, &srcrect, &dstrect); + /* Bottom edge */ + dstrect.y = coords->y2 - frag_size + 1; + if(bg_h < frag_size * 2) { + /* Bottom edge will overlap with top, so decrease it by 1 px */ + srcrect.h = dstrect.h = frag_size - 1; + dstrect.y += 1; + } + if(srcrect.h > 0) { + SDL_RenderCopyEx(renderer, frag, &srcrect, &dstrect, 0, NULL, SDL_FLIP_VERTICAL); + } + } + /* For left/right edges, stretch pixels on the bottom */ + srcrect.w = dstrect.w = frag_size; + dstrect.h = bg_h - frag_size * 2; + if(dstrect.w > 0 && dstrect.h > 0) { + srcrect.h = 1; + srcrect.x = 0; + srcrect.y = frag_size - 1; + dstrect.y = coords->y1 + frag_size; + /* Left edge */ + dstrect.x = coords->x1; + SDL_RenderCopy(renderer, frag, &srcrect, &dstrect); + /* Right edge */ + dstrect.x = coords->x2 - frag_size + 1; + if(bg_w < frag_size * 2) { + /* Right edge will overlap with left, so decrease it by 1 px */ + srcrect.w = dstrect.w = frag_size - 1; + dstrect.x += 1; + } + if(srcrect.w > 0) { + SDL_RenderCopyEx(renderer, frag, &srcrect, &dstrect, 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) +{ + lv_coord_t bg_w = lv_area_get_width(coords); + lv_coord_t bg_h = lv_area_get_height(coords); + SDL_Rect dstrect = {coords->x1 + frag_size, coords->y1 + frag_size, bg_w - frag_size * 2, bg_h - frag_size * 2}; + if(dstrect.w > 0 && dstrect.h > 0) { + SDL_Rect srcrect = {frag_size - 1, frag_size - 1, 1, 1}; + SDL_RenderCopy(renderer, frag, &srcrect, &dstrect); + } +} + +static void draw_rect_masked(const lv_area_t * coords, const lv_area_t * clip, const lv_draw_rect_dsc_t * dsc) +{ + if(dsc->radius <= 0 && SKIP_BORDER(dsc) && SKIP_SHADOW(dsc) && SKIP_IMAGE(dsc) && SKIP_OUTLINE(dsc)) { + draw_rect_masked_simple(coords, clip, dsc); + return; + } + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + SDL_Renderer * renderer = (SDL_Renderer *) disp->driver->user_data; + + SDL_Texture * screen = SDL_GetRenderTarget(renderer); + + lv_coord_t sw = dsc->shadow_width; + + lv_area_t sh_area = *coords; + lv_area_increase(&sh_area, dsc->shadow_spread + sw / 2 + 1, dsc->shadow_spread + sw / 2 + 1); + lv_area_move(&sh_area, dsc->shadow_ofs_x, dsc->shadow_ofs_y); + + lv_coord_t draw_w = lv_area_get_width(&sh_area); + lv_coord_t draw_h = lv_area_get_height(&sh_area); + /* Render drawing area to an offscreen texture */ + SDL_Texture * content = lv_gpu_temp_texture_obtain(renderer, draw_w, draw_h); + SDL_assert(content); + SDL_SetTextureColorMod(content, 0xFF, 0xFF, 0xFF); + SDL_SetTextureAlphaMod(content, 0xFF); + SDL_SetTextureBlendMode(content, SDL_BLENDMODE_BLEND); + + SDL_SetRenderTarget(renderer, content); + SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0x00); + SDL_RenderClear(renderer); + SDL_RenderSetClipRect(renderer, NULL); + lv_area_t content_coords; + lv_area_copy(&content_coords, coords); + lv_area_move(&content_coords, -sh_area.x1, -sh_area.y1); + + draw_shadow(renderer, &content_coords, clip, dsc); + draw_bg_color(renderer, &content_coords, dsc); + draw_bg_img(&content_coords, clip, dsc); + draw_border(renderer, &content_coords, dsc); + draw_outline(&content_coords, clip, dsc); + + SDL_Texture * clip_mask = lv_sdl_gen_mask_texture(renderer, &sh_area, NULL, 0); + SDL_Rect src_rect = {.w = draw_w, .h = draw_h, .x = 0, .y = 0}; +#if SDL_VERSION_ATLEAST(2, 0, 6) + 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(clip_mask, mode); + SDL_RenderCopy(renderer, clip_mask, NULL, &src_rect); +#endif + + SDL_Rect mask_rect; + SDL_Rect draw_rect; + lv_area_to_sdl_rect(&sh_area, &draw_rect); + lv_area_to_sdl_rect(clip, &mask_rect); + + SDL_SetRenderTarget(renderer, screen); + SDL_RenderSetClipRect(renderer, &mask_rect); + SDL_RenderCopy(renderer, content, &src_rect, &draw_rect); + SDL_DestroyTexture(clip_mask); +} + +static void draw_rect_masked_simple(const lv_area_t * coords, const lv_area_t * mask, const lv_draw_rect_dsc_t * dsc) +{ + SDL_Color bg_color; + lv_color_to_sdl_color(&dsc->bg_color, &bg_color); + + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + SDL_Renderer * renderer = (SDL_Renderer *) disp->driver->user_data; + + SDL_Surface * indexed = lv_sdl_apply_mask_surface(coords, NULL, 0); + SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, indexed); + + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + SDL_SetTextureAlphaMod(texture, dsc->bg_opa); + SDL_SetTextureColorMod(texture, bg_color.r, bg_color.g, bg_color.b); + + SDL_Rect coords_rect, mask_rect; + lv_area_to_sdl_rect(coords, &coords_rect); + lv_area_to_sdl_rect(mask, &mask_rect); + + SDL_RenderSetClipRect(renderer, &mask_rect); + SDL_RenderCopy(renderer, texture, NULL, &coords_rect); + + SDL_DestroyTexture(texture); + SDL_FreeSurface(indexed); +} + +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, lv_coord_t thickness, + lv_coord_t size, lv_border_side_t side) +{ + 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.thickness = thickness; + key.size = size; + key.side = side; + return key; +} + +#endif /*LV_USE_GPU_SDL*/ diff --git a/src/gpu/sdl/lv_gpu_sdl_lru.c b/src/gpu/sdl/lv_gpu_sdl_lru.c new file mode 100644 index 000000000..0f547fe9e --- /dev/null +++ b/src/gpu/sdl/lv_gpu_sdl_lru.c @@ -0,0 +1,375 @@ +/** + * @file lv_gpu_sdl_lru.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include "lv_gpu_sdl_lru.h" + +#include +#include +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/** + * MurmurHash2 + * @author Austin Appleby + * @see http://sites.google.com/site/murmurhash/ + */ +static uint32_t lv_lru_hash(lv_lru_t * cache, const void * key, uint32_t key_length); + +/** compare a key against an existing item's key */ +static int lv_lru_cmp_keys(lruc_item * item, const void * key, uint32_t key_length); + +/** remove an item and push it to the free items queue */ +static void lv_lru_remove_item(lv_lru_t * cache, lruc_item * prev, lruc_item * item, uint32_t hash_index); + +/** + * remove the least recently used item + * + * @todo we can optimise this by finding the n lru items, where n = required_space / average_length + */ +static void lv_lru_remove_lru_item(lv_lru_t * cache); + +/** pop an existing item off the free queue, or create a new one */ +static lruc_item * lv_lru_pop_or_create_item(lv_lru_t * cache); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/* error helpers */ +#define error_for(conditions, error) if(conditions) {return error;} +#define test_for_missing_cache() error_for(!cache, LV_LRU_MISSING_CACHE) +#define test_for_missing_key() error_for(!key, LV_LRU_MISSING_KEY) +#define test_for_missing_value() error_for(!value || value_length == 0, LV_LRU_MISSING_VALUE) +#define test_for_value_too_large() error_for(value_length > cache->total_memory, LV_LRU_VALUE_TOO_LARGE) + +/* lock helpers */ +#define lock_cache() if(SDL_LockMutex(cache->mutex)) {\ + perror("LRU Cache unable to obtain mutex lock");\ + return LV_LRU_LOCK_ERROR;\ + } + +#define unlock_cache() if(SDL_UnlockMutex(cache->mutex)) {\ + perror("LRU Cache unable to release mutex lock");\ + return LV_LRU_LOCK_ERROR;\ + } + + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_lru_t * lv_lru_new(uint64_t cache_size, uint32_t average_length, lv_lru_free_t * value_free, + lv_lru_free_t * key_free) +{ + // create the cache + lv_lru_t * cache = (lv_lru_t *) calloc(sizeof(lv_lru_t), 1); + if(!cache) { + perror("LRU Cache unable to create cache object"); + return NULL; + } + cache->hash_table_size = cache_size / average_length; + cache->average_item_length = average_length; + cache->free_memory = cache_size; + cache->total_memory = cache_size; + cache->seed = time(NULL); + cache->value_free = value_free ? value_free : free; + cache->key_free = key_free ? key_free : free; + + // size the hash table to a guestimate of the number of slots required (assuming a perfect hash) + cache->items = (lruc_item **) calloc(sizeof(lruc_item *), cache->hash_table_size); + if(!cache->items) { + perror("LRU Cache unable to create cache hash table"); + free(cache); + return NULL; + } + + // all cache calls are guarded by a mutex + cache->mutex = SDL_CreateMutex(); + if(!cache->mutex) { + perror("LRU Cache unable to initialise mutex"); + free(cache->items); + free(cache); + return NULL; + } + return cache; +} + + +lruc_error lv_lru_free(lv_lru_t * cache) +{ + test_for_missing_cache(); + + // free each of the cached items, and the hash table + lruc_item * item = NULL, *next = NULL; + uint32_t i = 0; + if(cache->items) { + for(; i < cache->hash_table_size; i++) { + item = cache->items[i]; + while(item) { + next = (lruc_item *) item->next; + cache->value_free(item->value); + cache->key_free(item->key); + cache->free_memory += item->value_length; + free(item); + item = next; + } + } + free(cache->items); + } + + if(cache->free_items) { + item = cache->free_items; + while(item) { + next = (lruc_item *) item->next; + free(item); + item = next; + } + } + + // free the cache + if(cache->mutex) { + SDL_DestroyMutex(cache->mutex); + } + free(cache); + + return LV_LRU_NO_ERROR; +} + + +lruc_error lv_lru_set(lv_lru_t * cache, const void * key, size_t key_length, void * value, size_t value_length) +{ + test_for_missing_cache(); + test_for_missing_key(); + test_for_missing_value(); + test_for_value_too_large(); + lock_cache(); + + // see if the key already exists + uint32_t hash_index = lv_lru_hash(cache, key, key_length); + int64_t required = 0; + lruc_item * item = NULL, *prev = NULL; + item = cache->items[hash_index]; + + while(item && lv_lru_cmp_keys(item, key, key_length)) { + prev = item; + item = (lruc_item *) item->next; + } + + if(item) { + // update the value and value_lengths + required = (int)(value_length - item->value_length); + cache->value_free(item->value); + item->value = value; + item->value_length = value_length; + + } + else { + // insert a new item + item = lv_lru_pop_or_create_item(cache); + item->value = value; + item->key = malloc(key_length); + memcpy(item->key, key, key_length); + item->value_length = value_length; + item->key_length = key_length; + required = value_length; + + if(prev) + prev->next = item; + else + cache->items[hash_index] = item; + } + item->access_count = ++cache->access_count; + + // remove as many items as necessary to free enough space + if(required > 0 && required > cache->free_memory) { + while(cache->free_memory < required) + lv_lru_remove_lru_item(cache); + } + cache->free_memory -= required; + unlock_cache(); + return LV_LRU_NO_ERROR; +} + + +lruc_error lv_lru_get(lv_lru_t * cache, const void * key, size_t key_size, void ** value) +{ + test_for_missing_cache(); + test_for_missing_key(); + lock_cache(); + + // loop until we find the item, or hit the end of a chain + uint32_t hash_index = lv_lru_hash(cache, key, key_size); + lruc_item * item = cache->items[hash_index]; + + while(item && lv_lru_cmp_keys(item, key, key_size)) + item = (lruc_item *) item->next; + + if(item) { + *value = item->value; + item->access_count = ++cache->access_count; + } + else { + *value = NULL; + } + + unlock_cache(); + return LV_LRU_NO_ERROR; +} + +lruc_error lv_lru_delete(lv_lru_t * cache, const void * key, size_t key_size) +{ + test_for_missing_cache(); + test_for_missing_key(); + lock_cache(); + + // loop until we find the item, or hit the end of a chain + lruc_item * item = NULL, *prev = NULL; + uint32_t hash_index = lv_lru_hash(cache, key, key_size); + item = cache->items[hash_index]; + + while(item && lv_lru_cmp_keys(item, key, key_size)) { + prev = item; + item = (lruc_item *) item->next; + } + + if(item) { + lv_lru_remove_item(cache, prev, item, hash_index); + } + + unlock_cache(); + return LV_LRU_NO_ERROR; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static uint32_t lv_lru_hash(lv_lru_t * cache, const void * key, uint32_t key_length) +{ + uint32_t m = 0x5bd1e995; + uint32_t r = 24; + uint32_t h = cache->seed ^ key_length; + char * data = (char *) key; + + while(key_length >= 4) { + uint32_t k = *(uint32_t *) data; + k *= m; + k ^= k >> r; + k *= m; + h *= m; + h ^= k; + data += 4; + key_length -= 4; + } + + switch(key_length) { + case 3: + h ^= data[2] << 16; + case 2: + h ^= data[1] << 8; + case 1: + h ^= data[0]; + h *= m; + }; + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + return h % cache->hash_table_size; +} + +static int lv_lru_cmp_keys(lruc_item * item, const void * key, uint32_t key_length) +{ + if(key_length != item->key_length) + return 1; + else + return memcmp(key, item->key, key_length); +} + +static void lv_lru_remove_item(lv_lru_t * cache, lruc_item * prev, lruc_item * item, uint32_t hash_index) +{ + if(prev) + prev->next = item->next; + else + cache->items[hash_index] = (lruc_item *) item->next; + + // free memory and update the free memory counter + cache->free_memory += item->value_length; + cache->value_free(item->value); + cache->key_free(item->key); + + // push the item to the free items queue + memset(item, 0, sizeof(lruc_item)); + item->next = cache->free_items; + cache->free_items = item; +} + +static void lv_lru_remove_lru_item(lv_lru_t * cache) +{ + lruc_item * min_item = NULL, *min_prev = NULL; + lruc_item * item = NULL, *prev = NULL; + uint32_t i = 0, min_index = -1; + uint64_t min_access_count = -1; + + for(; i < cache->hash_table_size; i++) { + item = cache->items[i]; + prev = NULL; + + while(item) { + if(item->access_count < min_access_count || min_access_count == -1) { + min_access_count = item->access_count; + min_item = item; + min_prev = prev; + min_index = i; + } + prev = item; + item = item->next; + } + } + + if(min_item) + lv_lru_remove_item(cache, min_prev, min_item, min_index); +} + +static lruc_item * lv_lru_pop_or_create_item(lv_lru_t * cache) +{ + lruc_item * item = NULL; + + if(cache->free_items) { + item = cache->free_items; + cache->free_items = item->next; + memset(item, 0, sizeof(lruc_item)); + } + else { + item = (lruc_item *) calloc(sizeof(lruc_item), 1); + } + + return item; +} + +#endif /*LV_USE_GPU_SDL*/ \ No newline at end of file diff --git a/src/gpu/sdl/lv_gpu_sdl_lru.h b/src/gpu/sdl/lv_gpu_sdl_lru.h new file mode 100644 index 000000000..a74e9eeac --- /dev/null +++ b/src/gpu/sdl/lv_gpu_sdl_lru.h @@ -0,0 +1,90 @@ +/** + * @file lv_gpu_sdl_lru.h + * + */ + +#ifndef LV_LRU_H +#define LV_LRU_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#include LV_GPU_SDL_INCLUDE_PATH + +#include +#include + + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef enum { + LV_LRU_NO_ERROR = 0, + LV_LRU_MISSING_CACHE, + LV_LRU_MISSING_KEY, + LV_LRU_MISSING_VALUE, + LV_LRU_LOCK_ERROR, + LV_LRU_VALUE_TOO_LARGE +} lruc_error; + +typedef void (lv_lru_free_t)(void * v); + +typedef struct lruc_item { + void * value; + void * key; + size_t value_length; + size_t key_length; + uint64_t access_count; + struct lruc_item * next; +} lruc_item; + +typedef struct { + lruc_item ** items; + uint64_t access_count; + uint64_t free_memory; + uint64_t total_memory; + uint64_t average_item_length; + uint32_t hash_table_size; + time_t seed; + lv_lru_free_t * value_free; + lv_lru_free_t * key_free; + lruc_item * free_items; + SDL_mutex * mutex; +} lv_lru_t; + + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +lv_lru_t * lv_lru_new(uint64_t cache_size, uint32_t average_length, lv_lru_free_t * value_free, + lv_lru_free_t * key_free); + +lruc_error lv_lru_free(lv_lru_t * cache); + +lruc_error lv_lru_set(lv_lru_t * cache, const void * key, size_t key_length, void * value, size_t value_length); + +lruc_error lv_lru_get(lv_lru_t * cache, const void * key, size_t key_size, void ** value); + +lruc_error lv_lru_delete(lv_lru_t * cache, const void * key, size_t key_size); + +/********************** + * MACROS + **********************/ +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_LRU_H*/ diff --git a/src/gpu/sdl/lv_gpu_sdl_mask.c b/src/gpu/sdl/lv_gpu_sdl_mask.c new file mode 100644 index 000000000..263c457c5 --- /dev/null +++ b/src/gpu/sdl/lv_gpu_sdl_mask.c @@ -0,0 +1,110 @@ +/** + * @file lv_gpu_sdl_mask.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include "../../draw/lv_draw_mask.h" +#include "../../misc/lv_mem.h" +#include "lv_gpu_sdl_mask.h" +#include "lv_gpu_sdl_utils.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + + +SDL_Surface * lv_sdl_create_mask_surface(lv_opa_t * pixels, lv_coord_t width, lv_coord_t height, lv_coord_t stride) +{ + SDL_Surface * indexed = SDL_CreateRGBSurfaceFrom(pixels, width, height, 8, stride, 0, 0, 0, 0); + SDL_SetSurfacePalette(indexed, lv_sdl_get_grayscale_palette(8)); + SDL_Surface * converted = SDL_ConvertSurfaceFormat(indexed, SDL_PIXELFORMAT_ARGB8888, 0); + SDL_FreeSurface(indexed); + return converted; +} + +SDL_Texture * lv_sdl_create_mask_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_mask_surface(pixels, width, height, stride); + SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, indexed); + SDL_FreeSurface(indexed); + return texture; +} + +lv_opa_t * lv_draw_mask_dump(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_Surface * lv_sdl_apply_mask_surface(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_mask_dump(coords, ids, ids_count); + lv_mem_buf_release(mask_buf); + return lv_sdl_create_mask_surface(mask_buf, w, h, w); +} + +SDL_Texture * lv_sdl_gen_mask_texture(SDL_Renderer * renderer, const lv_area_t * coords, const int16_t * ids, + int16_t ids_count) +{ + SDL_Surface * indexed = lv_sdl_apply_mask_surface(coords, ids, ids_count); + SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, indexed); + SDL_FreeSurface(indexed); + return texture; +} + + +/********************** + * STATIC FUNCTIONS + **********************/ + + +#endif /*LV_USE_GPU_SDL*/ diff --git a/src/gpu/sdl/lv_gpu_sdl_mask.h b/src/gpu/sdl/lv_gpu_sdl_mask.h new file mode 100644 index 000000000..6dbd73b7d --- /dev/null +++ b/src/gpu/sdl/lv_gpu_sdl_mask.h @@ -0,0 +1,57 @@ +/** + * @file lv_gpu_sdl_mask.h + * + */ + +#ifndef LV_GPU_SDL_MASK_H +#define LV_GPU_SDL_MASK_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#include LV_GPU_SDL_INCLUDE_PATH + +#include "../../misc/lv_area.h" +#include "../../misc/lv_color.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + + +lv_opa_t * lv_draw_mask_dump(const lv_area_t * coords, const int16_t * ids, int16_t ids_count); + +SDL_Surface * lv_sdl_create_mask_surface(lv_opa_t * pixels, lv_coord_t width, lv_coord_t height, lv_coord_t stride); + +SDL_Texture * lv_sdl_create_mask_texture(SDL_Renderer * renderer, lv_opa_t * pixels, lv_coord_t width, + lv_coord_t height, lv_coord_t stride); + +SDL_Surface * lv_sdl_apply_mask_surface(const lv_area_t * coords, const int16_t * ids, int16_t ids_count); + +SDL_Texture * +lv_sdl_gen_mask_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_GPU_SDL_MASK_H*/ diff --git a/src/gpu/sdl/lv_gpu_sdl_stack_blur.c b/src/gpu/sdl/lv_gpu_sdl_stack_blur.c new file mode 100644 index 000000000..6d68294c9 --- /dev/null +++ b/src/gpu/sdl/lv_gpu_sdl_stack_blur.c @@ -0,0 +1,247 @@ +/** + * @file lv_gpu_sdl_stack_blur.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_gpu_sdl_stack_blur.h" + +/********************* + * 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 + +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]; + } + } + } +} + diff --git a/src/gpu/sdl/lv_gpu_sdl_stack_blur.h b/src/gpu/sdl/lv_gpu_sdl_stack_blur.h new file mode 100644 index 000000000..5e13192a1 --- /dev/null +++ b/src/gpu/sdl/lv_gpu_sdl_stack_blur.h @@ -0,0 +1,42 @@ +/** + * @file lv_gpu_sdl_stack_blur.h + * + */ +#ifndef LV_GPU_SDL_STACK_BLUR_H +#define LV_GPU_SDL_STACK_BLUR_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#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 + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_GPU_SDL_STACK_BLUR_H*/ diff --git a/src/gpu/sdl/lv_gpu_sdl_texture_cache.c b/src/gpu/sdl/lv_gpu_sdl_texture_cache.c new file mode 100644 index 000000000..96f7d2d5a --- /dev/null +++ b/src/gpu/sdl/lv_gpu_sdl_texture_cache.c @@ -0,0 +1,197 @@ +/** + * @file lv_gpu_sdl_texture_cache.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include "lv_gpu_sdl_texture_cache.h" + +#include "../../misc/lv_log.h" +#include "../../draw/lv_draw_label.h" +#include "../../draw/lv_draw_img.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + SDL_Texture * texture; + void * userdata; + lv_lru_free_t * userdata_free; + lv_gpu_sdl_cache_flag_t flags; +} draw_cache_value_t; + +typedef struct { + lv_gpu_cache_key_magic_t magic; +} temp_texture_key_t; + +typedef struct { + lv_coord_t width, height; +} temp_texture_userdata_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void draw_cache_free_value(draw_cache_value_t *); + +/********************** + * STATIC VARIABLES + **********************/ + +static lv_lru_t * lv_sdl_texture_cache; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void _lv_gpu_sdl_texture_cache_init() +{ + lv_sdl_texture_cache = lv_lru_new(1024 * 1024 * 8, 65536, (lv_lru_free_t *) draw_cache_free_value, + NULL); +} + +void _lv_gpu_sdl_texture_cache_deinit() +{ + lv_lru_free(lv_sdl_texture_cache); +} + +SDL_Texture * lv_gpu_draw_cache_get(const void * key, size_t key_length, bool * found) +{ + return lv_gpu_draw_cache_get_with_userdata(key, key_length, found, NULL); +} + +SDL_Texture * lv_gpu_draw_cache_get_with_userdata(const void * key, size_t key_length, bool * found, void ** userdata) +{ + draw_cache_value_t * value = NULL; + lv_lru_get(lv_sdl_texture_cache, key, key_length, (void **) &value); + if(!value) { + if(found) { + *found = false; + } + return NULL; + } + else { + if(userdata) { + *userdata = value->userdata; + } + } + if(found) { + *found = true; + } + return value->texture; +} + +void lv_gpu_draw_cache_put(const void * key, size_t key_length, SDL_Texture * texture) +{ + lv_gpu_draw_cache_put_advanced(key, key_length, texture, NULL, NULL, 0); +} + +void lv_gpu_draw_cache_put_advanced(const void * key, size_t key_length, SDL_Texture * texture, void * userdata, + lv_lru_free_t userdata_free, lv_gpu_sdl_cache_flag_t flags) +{ + 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(lv_sdl_texture_cache, key, key_length, value, 1); + return; + } + if(flags & LV_GPU_SDL_CACHE_FLAG_MANAGED) { + /* Managed texture doesn't count into cache size */ + LV_LOG_INFO("cache texture %p, %d*%d@%dbpp", texture, width, height, SDL_BITSPERPIXEL(format)); + lv_lru_set(lv_sdl_texture_cache, 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(lv_sdl_texture_cache, key, key_length, value, width * height * SDL_BITSPERPIXEL(format) / 8); +} + +SDL_Texture * lv_gpu_temp_texture_obtain(SDL_Renderer * renderer, lv_coord_t width, lv_coord_t height) +{ + temp_texture_key_t key; + SDL_memset(&key, 0, sizeof(key)); + key.magic = LV_GPU_CACHE_KEY_TEMP; + temp_texture_userdata_t * userdata = NULL; + SDL_Texture * texture = lv_gpu_draw_cache_get_with_userdata(&key, sizeof(key), NULL, (void **) &userdata); + if(texture && userdata->width >= width && userdata->height >= height) { + return texture; + } + texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, width, height); + userdata = SDL_malloc(sizeof(temp_texture_userdata_t)); + userdata->width = width; + userdata->height = height; + lv_gpu_draw_cache_put_advanced(&key, sizeof(key), texture, userdata, SDL_free, 0); + return texture; +} + +lv_gpu_sdl_cache_key_head_img_t * lv_gpu_sdl_img_cache_key_create(const void * src, int32_t frame_id, size_t * size) +{ + lv_gpu_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_gpu_sdl_cache_key_head_img_t *) key; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void draw_cache_free_value(draw_cache_value_t * value) +{ + if(value->texture && !(value->flags & LV_GPU_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); +} + + +#endif /*LV_USE_GPU_SDL*/ + diff --git a/src/gpu/sdl/lv_gpu_sdl_texture_cache.h b/src/gpu/sdl/lv_gpu_sdl_texture_cache.h new file mode 100644 index 000000000..5a20d542d --- /dev/null +++ b/src/gpu/sdl/lv_gpu_sdl_texture_cache.h @@ -0,0 +1,90 @@ +/** + * @file lv_gpu_sdl_texture_cache.h + * + */ + +#ifndef LV_GPU_SDL_TEXTURE_CACHE_H +#define LV_GPU_SDL_TEXTURE_CACHE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#include LV_GPU_SDL_INCLUDE_PATH +#include "../../draw/lv_img_decoder.h" +#include "../../misc/lv_area.h" +#include "lv_gpu_sdl_lru.h" + +/********************* + * DEFINES + *********************/ + +#define LV_GPU_SDL_DEC_DSC_TEXTURE_HEAD "@LVSDLTex" + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + char head[8]; + SDL_Texture * texture; +} lv_gpu_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_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 = 0x41, + LV_GPU_CACHE_KEY_TEMP = 0xFF, +} lv_gpu_cache_key_magic_t; + +typedef enum { + LV_GPU_SDL_CACHE_FLAG_NONE = 0, + LV_GPU_SDL_CACHE_FLAG_MANAGED = 1, +} lv_gpu_sdl_cache_flag_t; + +typedef struct { + lv_gpu_cache_key_magic_t magic; + lv_img_src_t type; + int32_t frame_id; +} lv_gpu_sdl_cache_key_head_img_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void _lv_gpu_sdl_texture_cache_init(); + +void _lv_gpu_sdl_texture_cache_deinit(); + +SDL_Texture * lv_gpu_draw_cache_get(const void * key, size_t key_length, bool * found); + +SDL_Texture * lv_gpu_draw_cache_get_with_userdata(const void * key, size_t key_length, bool * found, void ** userdata); + +void lv_gpu_draw_cache_put(const void * key, size_t key_length, SDL_Texture * texture); + +void lv_gpu_draw_cache_put_advanced(const void * key, size_t key_length, SDL_Texture * texture, void * userdata, + lv_lru_free_t userdata_free, lv_gpu_sdl_cache_flag_t flags); + +SDL_Texture * lv_gpu_temp_texture_obtain(SDL_Renderer * renderer, lv_coord_t width, lv_coord_t height); + +lv_gpu_sdl_cache_key_head_img_t * lv_gpu_sdl_img_cache_key_create(const void * src, int32_t frame_id, size_t * size); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_GPU_SDL_TEXTURE_CACHE_H*/ diff --git a/src/gpu/sdl/lv_gpu_sdl_utils.c b/src/gpu/sdl/lv_gpu_sdl_utils.c new file mode 100644 index 000000000..d6486014a --- /dev/null +++ b/src/gpu/sdl/lv_gpu_sdl_utils.c @@ -0,0 +1,179 @@ +/** + * @file lv_gpu_sdl_utils.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include "lv_gpu_sdl_utils.h" + +#include "../../draw/lv_draw_label.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +static SDL_Palette * lv_sdl_palette_grayscale1 = NULL; +static SDL_Palette * lv_sdl_palette_grayscale2 = NULL; +static SDL_Palette * lv_sdl_palette_grayscale3 = NULL; +static SDL_Palette * lv_sdl_palette_grayscale4 = NULL; +static SDL_Palette * lv_sdl_palette_grayscale8 = NULL; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void _lv_gpu_sdl_utils_init() +{ + lv_sdl_palette_grayscale1 = lv_sdl_alloc_palette_for_bpp(_lv_bpp1_opa_table, 1); + lv_sdl_palette_grayscale2 = lv_sdl_alloc_palette_for_bpp(_lv_bpp2_opa_table, 2); + lv_sdl_palette_grayscale3 = lv_sdl_alloc_palette_for_bpp(_lv_bpp3_opa_table, 3); + lv_sdl_palette_grayscale4 = lv_sdl_alloc_palette_for_bpp(_lv_bpp4_opa_table, 4); + lv_sdl_palette_grayscale8 = lv_sdl_alloc_palette_for_bpp(_lv_bpp8_opa_table, 8); +} + +void _lv_gpu_sdl_utils_deinit() +{ + SDL_FreePalette(lv_sdl_palette_grayscale1); + SDL_FreePalette(lv_sdl_palette_grayscale2); + SDL_FreePalette(lv_sdl_palette_grayscale3); + SDL_FreePalette(lv_sdl_palette_grayscale4); + SDL_FreePalette(lv_sdl_palette_grayscale8); +} + +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) +{ + 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; +} + +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; + } + int h = in->y2 - in->y1 + 1; + int w = in->x2 - in->x1 + 1; + int sh = h * zoom >> 8; + int sw = w * zoom >> 8; + out->x = in->x1 - (sw / 2 - pivot->x); + out->y = in->y1 - (sh / 2 - pivot->y); + out->w = sw; + out->h = sh; +} + +double lv_sdl_round(double d) +{ + return (d - (long) d) < 0.5 ? SDL_floor(d) : SDL_ceil(d); +} + +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_Palette * lv_sdl_get_grayscale_palette(uint8_t bpp) +{ + switch(bpp) { + case 1: + return lv_sdl_palette_grayscale1; + case 2: + return lv_sdl_palette_grayscale2; + case 3: + return lv_sdl_palette_grayscale3; + case 4: + return lv_sdl_palette_grayscale4; + case 8: + return lv_sdl_palette_grayscale8; + } + return NULL; +} + +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) { + 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/src/gpu/sdl/lv_gpu_sdl_utils.h b/src/gpu/sdl/lv_gpu_sdl_utils.h new file mode 100644 index 000000000..c2daeb79b --- /dev/null +++ b/src/gpu/sdl/lv_gpu_sdl_utils.h @@ -0,0 +1,61 @@ +/** + * @file lv_gpu_sdl_utils.h + * + */ +#ifndef LV_GPU_SDL_UTILS_H +#define LV_GPU_SDL_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#include "../../misc/lv_color.h" +#include "../../misc/lv_area.h" + +#include LV_GPU_SDL_INCLUDE_PATH + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void _lv_gpu_sdl_utils_init(); + +void _lv_gpu_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); + +double lv_sdl_round(double d); + +SDL_Palette * lv_sdl_alloc_palette_for_bpp(const uint8_t * mapping, uint8_t bpp); + +SDL_Palette * lv_sdl_get_grayscale_palette(uint8_t bpp); + +void lv_sdl_to_8bpp(uint8_t * dest, const uint8_t * src, int width, int height, int stride, uint8_t bpp); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_GPU_SDL_UTILS_H*/ diff --git a/src/hal/lv_hal_disp.h b/src/hal/lv_hal_disp.h index 1c3d83bcd..96b605adf 100644 --- a/src/hal/lv_hal_disp.h +++ b/src/hal/lv_hal_disp.h @@ -167,6 +167,7 @@ uint8_t del_prev : lv_opa_t bg_opa; /** - /*==================== COLOR SETTINGS *====================*/ @@ -60,7 +58,7 @@ # ifdef CONFIG_LV_COLOR_DEPTH # define LV_COLOR_DEPTH CONFIG_LV_COLOR_DEPTH # else -# define LV_COLOR_DEPTH 32 +# define LV_COLOR_DEPTH 16 # endif #endif @@ -123,6 +121,12 @@ # define LV_MEM_ADR 0 /*0: unused*/ # endif #endif +/*Instead of an address give a memory allocator that will be called to get a memory pool for LVGL. E.g. my_malloc*/ +#if LV_MEM_ADR == 0 +//#define LV_MEM_POOL_INCLUDE your_alloc_library /* Uncomment if using an external allocator*/ +//#define LV_MEM_POOL_ALLOC your_alloc /* Uncomment if using an external allocator*/ +#endif + #else /*LV_MEM_CUSTOM*/ #ifndef LV_MEM_CUSTOM_INCLUDE # ifdef CONFIG_LV_MEM_CUSTOM_INCLUDE @@ -286,6 +290,7 @@ # define LV_DISP_ROT_MAX_BUF (10*1024) # endif #endif + /*------------- * GPU *-----------*/ @@ -342,6 +347,49 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ # endif #endif +/*Use SDL renderer API*/ +#ifndef LV_USE_GPU_SDL +# ifdef CONFIG_LV_USE_GPU_SDL +#ifndef LV_USE_GPU_SDL +# ifdef CONFIG_LV_USE_GPU_SDL +# define LV_USE_GPU_SDL CONFIG_LV_USE_GPU_SDL +# else +# define LV_USE_GPU_SDL CONFIG_LV_USE_GPU_SDL +# endif +#endif +# else +# define LV_USE_GPU_SDL 0 +# endif +#endif +#if LV_USE_GPU_SDL +#ifndef LV_USE_EXTERNAL_RENDERER +# ifdef CONFIG_LV_USE_EXTERNAL_RENDERER +# define LV_USE_EXTERNAL_RENDERER CONFIG_LV_USE_EXTERNAL_RENDERER +# else +# define LV_USE_EXTERNAL_RENDERER 1 +# endif +#endif +# ifndef LV_GPU_SDL_INCLUDE +#ifndef LV_GPU_SDL_INCLUDE_PATH +# ifdef CONFIG_LV_GPU_SDL_INCLUDE_PATH +# define LV_GPU_SDL_INCLUDE_PATH CONFIG_LV_GPU_SDL_INCLUDE_PATH +# else +# define LV_GPU_SDL_INCLUDE_PATH +# endif +#endif +# endif +#endif + +#ifndef LV_USE_EXTERNAL_RENDERER +#ifndef LV_USE_EXTERNAL_RENDERER +# ifdef CONFIG_LV_USE_EXTERNAL_RENDERER +# define LV_USE_EXTERNAL_RENDERER CONFIG_LV_USE_EXTERNAL_RENDERER +# else +# define LV_USE_EXTERNAL_RENDERER 0 +# endif +#endif +#endif + /*------------- * Logging *-----------*/ @@ -675,7 +723,7 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ # endif #endif -/*Compiler prefix for a big array declaration in RAM*/ +/*Complier prefix for a big array declaration in RAM*/ #ifndef LV_ATTRIBUTE_LARGE_RAM_ARRAY # ifdef CONFIG_LV_ATTRIBUTE_LARGE_RAM_ARRAY # define LV_ATTRIBUTE_LARGE_RAM_ARRAY CONFIG_LV_ATTRIBUTE_LARGE_RAM_ARRAY @@ -894,7 +942,7 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ # ifdef CONFIG_LV_FONT_DEJAVU_16_PERSIAN_HEBREW # define LV_FONT_DEJAVU_16_PERSIAN_HEBREW CONFIG_LV_FONT_DEJAVU_16_PERSIAN_HEBREW # else -# define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 0 /*Hebrew, Arabic, Persian letters and all their forms*/ +# define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 0 /*Hebrew, Arabic, Perisan letters and all their forms*/ # endif #endif #ifndef LV_FONT_SIMSUN_16_CJK @@ -1142,7 +1190,6 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ # endif #endif - #ifndef LV_USE_DROPDOWN # ifdef CONFIG_LV_USE_DROPDOWN # define LV_USE_DROPDOWN CONFIG_LV_USE_DROPDOWN @@ -1437,6 +1484,7 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ /*----------- * Themes *----------*/ + /*A simple, impressive and very complete theme*/ #ifndef LV_USE_THEME_DEFAULT # ifdef CONFIG_LV_USE_THEME_DEFAULT @@ -1536,7 +1584,7 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ LV_EXPORT_CONST_INT(LV_DPI_DEF); -/*If running without lv_conf.h add typedefs with default value*/ +/*If running without lv_conf.h add typdesf with default value*/ #if defined(LV_CONF_SKIP) # if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) /*Disable warnings for Visual Studio*/