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 <philippe.coval@huawei.com>

* 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 <kisvegabor@gmail.com>
Co-authored-by: Philippe Coval <philippe.coval@huawei.com>
This commit is contained in:
Mariotaku 2021-09-17 01:20:32 +09:00 committed by GitHub
parent 71e6f65d51
commit efc5bb40d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 3445 additions and 13 deletions

View File

@ -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 <SDL2/SDL.h>
# 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*/

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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)

View File

@ -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`

View File

@ -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
/**

View File

@ -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

View File

@ -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

View File

@ -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

59
src/gpu/lv_gpu_sdl.c Normal file
View File

@ -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*/

69
src/gpu/lv_gpu_sdl.h Normal file
View File

@ -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*/

28
src/gpu/sdl/README.md Normal file
View File

@ -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.

16
src/gpu/sdl/lv_gpu_sdl.mk Normal file
View File

@ -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"

View File

@ -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 <math.h>
#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*/

View File

@ -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*/

View File

@ -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*/

View File

@ -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*/

View File

@ -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 <math.h>
#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, &center, 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*/

View File

@ -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*/

View File

@ -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 <stdlib.h>
#include <string.h>
#include <stdio.h>
/*********************
* 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*/

View File

@ -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 <stdint.h>
#include <time.h>
/*********************
* 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*/

View File

@ -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*/

View File

@ -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*/

View File

@ -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 <mario@quasimondo.com>
static unsigned short const stackblur_mul[255] = {
512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512,
454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512,
482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456,
437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512,
497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328,
320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456,
446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335,
329, 323, 318, 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512,
505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405,
399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328,
324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271,
268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456,
451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388,
385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335,
332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292,
289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259
};
static unsigned char const stackblur_shr[255] = {
9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_stack_blur_grayscale(lv_opa_t * buf, uint16_t w, uint16_t h, uint16_t r)
{
stack_blur_job(buf, w, h, r, 1, 0, 1);
stack_blur_job(buf, w, h, r, 1, 0, 2);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void stack_blur_job(lv_opa_t * src, unsigned int w, unsigned int h, unsigned int radius, int cores, int core,
int step)
{
if(radius < 2 || radius > 254) {
/* Silently ignore bad radius */
return;
}
unsigned int x, y, xp, yp, i;
unsigned int sp;
unsigned int stack_start;
unsigned char * stack_ptr;
lv_opa_t * src_ptr;
lv_opa_t * dst_ptr;
unsigned long sum_r;
unsigned long sum_in_r;
unsigned long sum_out_r;
unsigned int wm = w - 1;
unsigned int hm = h - 1;
unsigned int stride = w;
unsigned int div = (radius * 2) + 1;
unsigned int mul_sum = stackblur_mul[radius];
unsigned char shr_sum = stackblur_shr[radius];
unsigned char stack[254 * 2 + 1];
if(step == 1) {
unsigned int minY = core * h / cores;
unsigned int maxY = (core + 1) * h / cores;
for(y = minY; y < maxY; y++) {
sum_r =
sum_in_r =
sum_out_r = 0;
src_ptr = src + stride * y; // start of line (0,y)
for(i = 0; i <= radius; i++) {
stack_ptr = &stack[i];
stack_ptr[0] = src_ptr[0];
sum_r += src_ptr[0] * (i + 1);
sum_out_r += src_ptr[0];
}
for(i = 1; i <= radius; i++) {
if(i <= wm) src_ptr += 1;
stack_ptr = &stack[i + radius];
stack_ptr[0] = src_ptr[0];
sum_r += src_ptr[0] * (radius + 1 - i);
sum_in_r += src_ptr[0];
}
sp = radius;
xp = radius;
if(xp > wm) xp = wm;
src_ptr = src + (xp + y * w); // img.pix_ptr(xp, y);
dst_ptr = src + y * stride; // img.pix_ptr(0, y);
for(x = 0; x < w; x++) {
dst_ptr[0] = LV_CLAMP((sum_r * mul_sum) >> shr_sum, 0, 255);
dst_ptr += 1;
sum_r -= sum_out_r;
stack_start = sp + div - radius;
if(stack_start >= div) stack_start -= div;
stack_ptr = &stack[stack_start];
sum_out_r -= stack_ptr[0];
if(xp < wm) {
src_ptr += 1;
++xp;
}
stack_ptr[0] = src_ptr[0];
sum_in_r += src_ptr[0];
sum_r += sum_in_r;
++sp;
if(sp >= div) sp = 0;
stack_ptr = &stack[sp];
sum_out_r += stack_ptr[0];
sum_in_r -= stack_ptr[0];
}
}
}
// step 2
if(step == 2) {
unsigned int minX = core * w / cores;
unsigned int maxX = (core + 1) * w / cores;
for(x = minX; x < maxX; x++) {
sum_r =
sum_in_r =
sum_out_r = 0;
src_ptr = src + x; // x,0
for(i = 0; i <= radius; i++) {
stack_ptr = &stack[i];
stack_ptr[0] = src_ptr[0];
sum_r += src_ptr[0] * (i + 1);
sum_out_r += src_ptr[0];
}
for(i = 1; i <= radius; i++) {
if(i <= hm) src_ptr += stride; // +stride
stack_ptr = &stack[i + radius];
stack_ptr[0] = src_ptr[0];
sum_r += src_ptr[0] * (radius + 1 - i);
sum_in_r += src_ptr[0];
}
sp = radius;
yp = radius;
if(yp > hm) yp = hm;
src_ptr = src + (x + yp * w); // img.pix_ptr(x, yp);
dst_ptr = src + x; // img.pix_ptr(x, 0);
for(y = 0; y < h; y++) {
dst_ptr[0] = LV_CLAMP((sum_r * mul_sum) >> shr_sum, 0, 255);
dst_ptr += stride;
sum_r -= sum_out_r;
stack_start = sp + div - radius;
if(stack_start >= div) stack_start -= div;
stack_ptr = &stack[stack_start];
sum_out_r -= stack_ptr[0];
if(yp < hm) {
src_ptr += stride; // stride
++yp;
}
stack_ptr[0] = src_ptr[0];
sum_in_r += src_ptr[0];
sum_r += sum_in_r;
++sp;
if(sp >= div) sp = 0;
stack_ptr = &stack[sp];
sum_out_r += stack_ptr[0];
sum_in_r -= stack_ptr[0];
}
}
}
}

View File

@ -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*/

View File

@ -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*/

View File

@ -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*/

View File

@ -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*/

View File

@ -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*/

View File

@ -167,6 +167,7 @@ uint8_t del_prev :
lv_opa_t bg_opa; /**<Opacity of the background color or wallpaper*/
lv_color_t bg_color; /**< Default display color when screens are transparent*/
const void * bg_img; /**< An image source to display as wallpaper*/
void (*bg_fn)(lv_area_t*);/**< A function to handle drawing*/
/** Invalidated (marked to redraw) areas*/
lv_area_t inv_areas[LV_INV_BUF_SIZE];

View File

@ -46,11 +46,9 @@
/*----------------------------------
* Start parsing lv_conf_template.h
-----------------------------------*/
/* clang-format off */
#include <stdint.h>
/*====================
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 <SDL2/SDL.h>
# 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*/