709 lines
29 KiB
C
709 lines
29 KiB
C
|
/**
|
||
|
* @file lv_draw_sdl_rect.c
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
/*********************
|
||
|
* INCLUDES
|
||
|
*********************/
|
||
|
|
||
|
#include "../../lv_conf_internal.h"
|
||
|
|
||
|
#if LV_USE_GPU_SDL
|
||
|
|
||
|
#include "../lv_draw_rect.h"
|
||
|
#include "../lv_draw_img.h"
|
||
|
#include "../lv_draw_label.h"
|
||
|
#include "../lv_draw_mask.h"
|
||
|
#include "../../core/lv_refr.h"
|
||
|
#include "lv_draw_sdl_utils.h"
|
||
|
#include "lv_draw_sdl_texture_cache.h"
|
||
|
#include "lv_draw_sdl_composite.h"
|
||
|
#include "lv_draw_sdl_mask.h"
|
||
|
#include "lv_draw_sdl_stack_blur.h"
|
||
|
#include "lv_draw_sdl_img.h"
|
||
|
|
||
|
/*********************
|
||
|
* DEFINES
|
||
|
*********************/
|
||
|
|
||
|
/**********************
|
||
|
* TYPEDEFS
|
||
|
**********************/
|
||
|
|
||
|
typedef struct {
|
||
|
lv_sdl_cache_key_magic_t magic;
|
||
|
lv_coord_t radius;
|
||
|
lv_coord_t size;
|
||
|
} lv_draw_rect_bg_key_t;
|
||
|
|
||
|
typedef struct {
|
||
|
lv_sdl_cache_key_magic_t magic;
|
||
|
lv_coord_t radius;
|
||
|
lv_coord_t size;
|
||
|
lv_coord_t blur;
|
||
|
} lv_draw_rect_shadow_key_t;
|
||
|
|
||
|
typedef struct {
|
||
|
lv_sdl_cache_key_magic_t magic;
|
||
|
lv_coord_t rout, rin;
|
||
|
lv_area_t offsets;
|
||
|
} lv_draw_rect_border_key_t;
|
||
|
|
||
|
/**********************
|
||
|
* STATIC PROTOTYPES
|
||
|
**********************/
|
||
|
|
||
|
static void draw_bg_color(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
|
||
|
const lv_draw_rect_dsc_t * dsc);
|
||
|
|
||
|
static void draw_bg_img(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
|
||
|
const lv_draw_rect_dsc_t * dsc);
|
||
|
|
||
|
static void draw_border(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
|
||
|
const lv_draw_rect_dsc_t * dsc);
|
||
|
|
||
|
static void draw_shadow(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip,
|
||
|
const lv_draw_rect_dsc_t * dsc);
|
||
|
|
||
|
static void draw_outline(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip,
|
||
|
const lv_draw_rect_dsc_t * dsc);
|
||
|
|
||
|
static void draw_border_generic(lv_draw_sdl_ctx_t * ctx, const lv_area_t * outer_area, const lv_area_t * inner_area,
|
||
|
const lv_area_t * clip, lv_coord_t rout, lv_coord_t rin, lv_color_t color, lv_opa_t opa,
|
||
|
lv_blend_mode_t blend_mode);
|
||
|
|
||
|
static void frag_render_borders(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size,
|
||
|
const lv_area_t * coords, const lv_area_t * clipped, bool full);
|
||
|
|
||
|
static void frag_render_center(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size,
|
||
|
const lv_area_t * coords, const lv_area_t * clipped, bool full);
|
||
|
|
||
|
static lv_draw_rect_bg_key_t rect_bg_key_create(lv_coord_t radius, lv_coord_t size);
|
||
|
|
||
|
static lv_draw_rect_shadow_key_t rect_shadow_key_create(lv_coord_t radius, lv_coord_t size, lv_coord_t blur);
|
||
|
|
||
|
static lv_draw_rect_border_key_t rect_border_key_create(lv_coord_t rout, lv_coord_t rin, const lv_area_t * outer_area,
|
||
|
const lv_area_t * inner_area);
|
||
|
|
||
|
/**********************
|
||
|
* STATIC VARIABLES
|
||
|
**********************/
|
||
|
|
||
|
/**********************
|
||
|
* MACROS
|
||
|
**********************/
|
||
|
#define SKIP_BORDER(dsc) ((dsc)->border_opa <= LV_OPA_MIN || (dsc)->border_width == 0 || (dsc)->border_side == LV_BORDER_SIDE_NONE || (dsc)->border_post)
|
||
|
#define SKIP_SHADOW(dsc) ((dsc)->shadow_width == 0 || (dsc)->shadow_opa <= LV_OPA_MIN || ((dsc)->shadow_width == 1 && (dsc)->shadow_spread <= 0 && (dsc)->shadow_ofs_x == 0 && (dsc)->shadow_ofs_y == 0))
|
||
|
#define SKIP_IMAGE(dsc) ((dsc)->bg_img_src == NULL || (dsc)->bg_img_opa <= LV_OPA_MIN)
|
||
|
#define SKIP_OUTLINE(dsc) ((dsc)->outline_opa <= LV_OPA_MIN || (dsc)->outline_width == 0)
|
||
|
|
||
|
/**********************
|
||
|
* GLOBAL FUNCTIONS
|
||
|
**********************/
|
||
|
|
||
|
void lv_draw_sdl_draw_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
|
||
|
{
|
||
|
const lv_area_t * clip = draw_ctx->clip_area;
|
||
|
lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
|
||
|
|
||
|
lv_area_t extension = {0, 0, 0, 0};
|
||
|
if(!SKIP_SHADOW(dsc)) {
|
||
|
lv_coord_t ext = (lv_coord_t)(dsc->shadow_spread - dsc->shadow_width / 2 + 1);
|
||
|
extension.x1 = LV_MAX(extension.x1, -dsc->shadow_ofs_x + ext);
|
||
|
extension.x2 = LV_MAX(extension.x2, dsc->shadow_ofs_x + ext);
|
||
|
extension.y1 = LV_MAX(extension.y1, -dsc->shadow_ofs_y + ext);
|
||
|
extension.y2 = LV_MAX(extension.y2, dsc->shadow_ofs_y + ext);
|
||
|
}
|
||
|
if(!SKIP_OUTLINE(dsc)) {
|
||
|
lv_coord_t ext = (lv_coord_t)(dsc->outline_pad - 1 + dsc->outline_width);
|
||
|
extension.x1 = LV_MAX(extension.x1, ext);
|
||
|
extension.x2 = LV_MAX(extension.x2, ext);
|
||
|
extension.y1 = LV_MAX(extension.y1, ext);
|
||
|
extension.y2 = LV_MAX(extension.y2, ext);
|
||
|
}
|
||
|
/* Coords will be translated so coords will start at (0,0) */
|
||
|
lv_area_t t_coords = *coords, t_clip = *clip, apply_area, t_area;
|
||
|
lv_draw_sdl_composite_begin(ctx, coords, clip, &extension, dsc->blend_mode, &t_coords, &t_clip, &apply_area);
|
||
|
bool has_content = _lv_area_intersect(&t_area, &t_coords, &t_clip);
|
||
|
|
||
|
SDL_Rect clip_rect;
|
||
|
lv_area_to_sdl_rect(&t_clip, &clip_rect);
|
||
|
draw_shadow(ctx, &t_coords, &t_clip, dsc);
|
||
|
/* Shadows and outlines will also draw in extended area */
|
||
|
if(has_content) {
|
||
|
draw_bg_color(ctx, &t_coords, &t_area, dsc);
|
||
|
draw_bg_img(ctx, &t_coords, &t_area, dsc);
|
||
|
draw_border(ctx, &t_coords, &t_area, dsc);
|
||
|
}
|
||
|
draw_outline(ctx, &t_coords, &t_clip, dsc);
|
||
|
|
||
|
lv_draw_sdl_composite_end(ctx, &apply_area, dsc->blend_mode);
|
||
|
}
|
||
|
|
||
|
SDL_Texture * lv_draw_sdl_rect_bg_frag_obtain(lv_draw_sdl_ctx_t * ctx, lv_coord_t radius)
|
||
|
{
|
||
|
lv_draw_rect_bg_key_t key = rect_bg_key_create(radius, radius);
|
||
|
lv_area_t coords = {0, 0, radius * 2 - 1, radius * 2 - 1};
|
||
|
lv_area_t coords_frag = {0, 0, radius - 1, radius - 1};
|
||
|
SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL);
|
||
|
if(texture == NULL) {
|
||
|
lv_draw_mask_radius_param_t mask_rout_param;
|
||
|
lv_draw_mask_radius_init(&mask_rout_param, &coords, radius, false);
|
||
|
int16_t mask_id = lv_draw_mask_add(&mask_rout_param, NULL);
|
||
|
texture = lv_draw_sdl_mask_dump_texture(ctx->renderer, &coords_frag, &mask_id, 1);
|
||
|
lv_draw_mask_remove_id(mask_id);
|
||
|
SDL_assert(texture);
|
||
|
lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), texture);
|
||
|
}
|
||
|
return texture;
|
||
|
}
|
||
|
|
||
|
void lv_draw_sdl_rect_bg_frag_draw_corners(lv_draw_sdl_ctx_t * ctx, SDL_Texture * frag, lv_coord_t frag_size,
|
||
|
const lv_area_t * coords, const lv_area_t * clip, bool full)
|
||
|
{
|
||
|
if(!clip) clip = coords;
|
||
|
lv_area_t corner_area, dst_area;
|
||
|
/* Upper left */
|
||
|
corner_area.x1 = coords->x1;
|
||
|
corner_area.y1 = coords->y1;
|
||
|
corner_area.x2 = coords->x1 + frag_size - 1;
|
||
|
corner_area.y2 = coords->y1 + frag_size - 1;
|
||
|
if(_lv_area_intersect(&dst_area, &corner_area, clip)) {
|
||
|
SDL_Rect dst_rect;
|
||
|
lv_area_to_sdl_rect(&dst_area, &dst_rect);
|
||
|
|
||
|
lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area);
|
||
|
lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1), sy = (lv_coord_t)(dst_area.y1 - corner_area.y1);
|
||
|
SDL_Rect src_rect = {sx, sy, dw, dh};
|
||
|
SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect);
|
||
|
}
|
||
|
/* Upper right, clip right edge if too big */
|
||
|
corner_area.x1 = LV_MAX(coords->x2 - frag_size + 1, coords->x1 + frag_size);
|
||
|
corner_area.x2 = coords->x2;
|
||
|
if(_lv_area_intersect(&dst_area, &corner_area, clip)) {
|
||
|
SDL_Rect dst_rect;
|
||
|
lv_area_to_sdl_rect(&dst_area, &dst_rect);
|
||
|
|
||
|
lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area);
|
||
|
if(full) {
|
||
|
lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1),
|
||
|
sy = (lv_coord_t)(dst_area.y1 - corner_area.y1);
|
||
|
SDL_Rect src_rect = {frag_size + 3 + sx, sy, dw, dh};
|
||
|
SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect);
|
||
|
}
|
||
|
else {
|
||
|
SDL_Rect src_rect = {corner_area.x2 - dst_area.x2, dst_area.y1 - corner_area.y1, dw, dh};
|
||
|
SDL_RenderCopyEx(ctx->renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_HORIZONTAL);
|
||
|
}
|
||
|
}
|
||
|
/* Lower right, clip bottom edge if too big */
|
||
|
corner_area.y1 = LV_MAX(coords->y2 - frag_size + 1, coords->y1 + frag_size);
|
||
|
corner_area.y2 = coords->y2;
|
||
|
if(_lv_area_intersect(&dst_area, &corner_area, clip)) {
|
||
|
SDL_Rect dst_rect;
|
||
|
lv_area_to_sdl_rect(&dst_area, &dst_rect);
|
||
|
|
||
|
lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area);
|
||
|
if(full) {
|
||
|
lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1),
|
||
|
sy = (lv_coord_t)(dst_area.y1 - corner_area.y1);
|
||
|
SDL_Rect src_rect = {frag_size + 3 + sx, frag_size + 3 + sy, dw, dh};
|
||
|
SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect);
|
||
|
}
|
||
|
else {
|
||
|
SDL_Rect src_rect = {corner_area.x2 - dst_area.x2, corner_area.y2 - dst_area.y2, dw, dh};
|
||
|
SDL_RenderCopyEx(ctx->renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_HORIZONTAL | SDL_FLIP_VERTICAL);
|
||
|
}
|
||
|
}
|
||
|
/* Lower left, right edge should not be clip */
|
||
|
corner_area.x1 = coords->x1;
|
||
|
corner_area.x2 = coords->x1 + frag_size - 1;
|
||
|
if(_lv_area_intersect(&dst_area, &corner_area, clip)) {
|
||
|
SDL_Rect dst_rect;
|
||
|
lv_area_to_sdl_rect(&dst_area, &dst_rect);
|
||
|
|
||
|
lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area);
|
||
|
if(full) {
|
||
|
lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1),
|
||
|
sy = (lv_coord_t)(dst_area.y1 - corner_area.y1);
|
||
|
SDL_Rect src_rect = {sx, frag_size + 3 + sy, dw, dh};
|
||
|
SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect);
|
||
|
}
|
||
|
else {
|
||
|
SDL_Rect src_rect = {dst_area.x1 - corner_area.x1, corner_area.y2 - dst_area.y2, dw, dh};
|
||
|
SDL_RenderCopyEx(ctx->renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_VERTICAL);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**********************
|
||
|
* STATIC FUNCTIONS
|
||
|
**********************/
|
||
|
|
||
|
static void draw_bg_color(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
|
||
|
const lv_draw_rect_dsc_t * dsc)
|
||
|
{
|
||
|
if(dsc->bg_opa == 0) {
|
||
|
return;
|
||
|
}
|
||
|
SDL_Color bg_color;
|
||
|
lv_color_to_sdl_color(&dsc->bg_color, &bg_color);
|
||
|
lv_coord_t radius = dsc->radius;
|
||
|
if(radius <= 0) {
|
||
|
SDL_Rect rect;
|
||
|
lv_area_to_sdl_rect(draw_area, &rect);
|
||
|
SDL_SetRenderDrawColor(ctx->renderer, bg_color.r, bg_color.g, bg_color.b, dsc->bg_opa);
|
||
|
SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_BLEND);
|
||
|
SDL_RenderFillRect(ctx->renderer, &rect);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*A small texture with a quarter of the rect is enough*/
|
||
|
lv_coord_t bg_w = lv_area_get_width(coords), bg_h = lv_area_get_height(coords);
|
||
|
lv_coord_t real_radius = LV_MIN3(bg_w / 2, bg_h / 2, radius);
|
||
|
SDL_Texture * texture = lv_draw_sdl_rect_bg_frag_obtain(ctx, real_radius);
|
||
|
|
||
|
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
|
||
|
SDL_SetTextureAlphaMod(texture, dsc->bg_opa);
|
||
|
SDL_SetTextureColorMod(texture, bg_color.r, bg_color.g, bg_color.b);
|
||
|
lv_draw_sdl_rect_bg_frag_draw_corners(ctx, texture, real_radius, coords, draw_area, false);
|
||
|
frag_render_borders(ctx->renderer, texture, real_radius, coords, draw_area, false);
|
||
|
frag_render_center(ctx->renderer, texture, real_radius, coords, draw_area, false);
|
||
|
}
|
||
|
|
||
|
static void draw_bg_img(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
|
||
|
const lv_draw_rect_dsc_t * dsc)
|
||
|
{
|
||
|
if(SKIP_IMAGE(dsc)) return;
|
||
|
|
||
|
lv_img_src_t src_type = lv_img_src_get_type(dsc->bg_img_src);
|
||
|
if(src_type == LV_IMG_SRC_SYMBOL) {
|
||
|
lv_point_t size;
|
||
|
lv_txt_get_size(&size, dsc->bg_img_src, dsc->bg_img_symbol_font, 0, 0, LV_COORD_MAX, LV_TEXT_FLAG_NONE);
|
||
|
lv_area_t a;
|
||
|
a.x1 = coords->x1 + lv_area_get_width(coords) / 2 - size.x / 2;
|
||
|
a.x2 = a.x1 + size.x - 1;
|
||
|
a.y1 = coords->y1 + lv_area_get_height(coords) / 2 - size.y / 2;
|
||
|
a.y2 = a.y1 + size.y - 1;
|
||
|
|
||
|
lv_draw_label_dsc_t label_draw_dsc;
|
||
|
lv_draw_label_dsc_init(&label_draw_dsc);
|
||
|
label_draw_dsc.font = dsc->bg_img_symbol_font;
|
||
|
label_draw_dsc.color = dsc->bg_img_recolor;
|
||
|
label_draw_dsc.opa = dsc->bg_img_opa;
|
||
|
lv_draw_label((lv_draw_ctx_t *) ctx, &label_draw_dsc, &a, dsc->bg_img_src, NULL);
|
||
|
}
|
||
|
else {
|
||
|
lv_img_header_t header;
|
||
|
size_t key_size;
|
||
|
lv_draw_sdl_cache_key_head_img_t * key = lv_draw_sdl_texture_img_key_create(dsc->bg_img_src, 0, &key_size);
|
||
|
bool key_found;
|
||
|
lv_img_header_t * cache_header = NULL;
|
||
|
SDL_Texture * texture = lv_draw_sdl_texture_cache_get_with_userdata(ctx, key, key_size, &key_found,
|
||
|
(void **) &cache_header);
|
||
|
SDL_free(key);
|
||
|
if(texture) {
|
||
|
header = *cache_header;
|
||
|
}
|
||
|
else if(key_found || lv_img_decoder_get_info(dsc->bg_img_src, &header) != LV_RES_OK) {
|
||
|
/* When cache hit but with negative result, use default decoder. If still fail, return.*/
|
||
|
LV_LOG_WARN("Couldn't read the background image");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
lv_draw_img_dsc_t img_dsc;
|
||
|
lv_draw_img_dsc_init(&img_dsc);
|
||
|
img_dsc.blend_mode = dsc->blend_mode;
|
||
|
img_dsc.recolor = dsc->bg_img_recolor;
|
||
|
img_dsc.recolor_opa = dsc->bg_img_recolor_opa;
|
||
|
img_dsc.opa = dsc->bg_img_opa;
|
||
|
img_dsc.frame_id = 0;
|
||
|
|
||
|
int16_t radius_mask_id = LV_MASK_ID_INV;
|
||
|
lv_draw_mask_radius_param_t radius_param;
|
||
|
if(dsc->radius > 0) {
|
||
|
lv_draw_mask_radius_init(&radius_param, coords, dsc->radius, false);
|
||
|
radius_mask_id = lv_draw_mask_add(&radius_param, NULL);
|
||
|
}
|
||
|
|
||
|
/*Center align*/
|
||
|
if(dsc->bg_img_tiled == false) {
|
||
|
lv_area_t area;
|
||
|
area.x1 = coords->x1 + lv_area_get_width(coords) / 2 - header.w / 2;
|
||
|
area.y1 = coords->y1 + lv_area_get_height(coords) / 2 - header.h / 2;
|
||
|
area.x2 = area.x1 + header.w - 1;
|
||
|
area.y2 = area.y1 + header.h - 1;
|
||
|
|
||
|
lv_draw_img((lv_draw_ctx_t *) ctx, &img_dsc, &area, dsc->bg_img_src);
|
||
|
}
|
||
|
else {
|
||
|
lv_area_t area;
|
||
|
area.y1 = coords->y1;
|
||
|
area.y2 = area.y1 + header.h - 1;
|
||
|
|
||
|
for(; area.y1 <= coords->y2; area.y1 += header.h, area.y2 += header.h) {
|
||
|
|
||
|
area.x1 = coords->x1;
|
||
|
area.x2 = area.x1 + header.w - 1;
|
||
|
for(; area.x1 <= coords->x2; area.x1 += header.w, area.x2 += header.w) {
|
||
|
lv_draw_img((lv_draw_ctx_t *) ctx, &img_dsc, &area, dsc->bg_img_src);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(radius_mask_id != LV_MASK_ID_INV) {
|
||
|
lv_draw_mask_remove_id(radius_mask_id);
|
||
|
lv_draw_mask_free_param(&radius_param);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void draw_shadow(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip,
|
||
|
const lv_draw_rect_dsc_t * dsc)
|
||
|
{
|
||
|
/*Check whether the shadow is visible*/
|
||
|
if(SKIP_SHADOW(dsc)) return;
|
||
|
|
||
|
lv_coord_t sw = dsc->shadow_width;
|
||
|
|
||
|
lv_area_t core_area;
|
||
|
core_area.x1 = coords->x1 + dsc->shadow_ofs_x - dsc->shadow_spread;
|
||
|
core_area.x2 = coords->x2 + dsc->shadow_ofs_x + dsc->shadow_spread;
|
||
|
core_area.y1 = coords->y1 + dsc->shadow_ofs_y - dsc->shadow_spread;
|
||
|
core_area.y2 = coords->y2 + dsc->shadow_ofs_y + dsc->shadow_spread;
|
||
|
|
||
|
lv_area_t shadow_area;
|
||
|
shadow_area.x1 = core_area.x1 - sw / 2 - 1;
|
||
|
shadow_area.x2 = core_area.x2 + sw / 2 + 1;
|
||
|
shadow_area.y1 = core_area.y1 - sw / 2 - 1;
|
||
|
shadow_area.y2 = core_area.y2 + sw / 2 + 1;
|
||
|
|
||
|
lv_opa_t opa = dsc->shadow_opa;
|
||
|
|
||
|
if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
|
||
|
|
||
|
/*Get clipped draw area which is the real draw area.
|
||
|
*It is always the same or inside `shadow_area`*/
|
||
|
lv_area_t draw_area;
|
||
|
if(!_lv_area_intersect(&draw_area, &shadow_area, clip)) return;
|
||
|
|
||
|
SDL_Rect core_area_rect;
|
||
|
lv_area_to_sdl_rect(&shadow_area, &core_area_rect);
|
||
|
|
||
|
lv_coord_t radius = dsc->radius;
|
||
|
/* No matter how big the shadow is, what we need is just a corner */
|
||
|
lv_coord_t frag_size = LV_MIN3(lv_area_get_width(&core_area) / 2, lv_area_get_height(&core_area) / 2,
|
||
|
LV_MAX(sw / 2, radius));
|
||
|
|
||
|
/* This is how big the corner is after blurring */
|
||
|
lv_coord_t blur_growth = (lv_coord_t)(sw / 2 + 1);
|
||
|
|
||
|
lv_coord_t blur_frag_size = (lv_coord_t)(frag_size + blur_growth);
|
||
|
|
||
|
lv_draw_rect_shadow_key_t key = rect_shadow_key_create(radius, frag_size, sw);
|
||
|
|
||
|
SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL);
|
||
|
if(texture == NULL) {
|
||
|
lv_area_t mask_area = {blur_growth, blur_growth}, mask_area_blurred = {0, 0};
|
||
|
lv_area_set_width(&mask_area, frag_size * 2);
|
||
|
lv_area_set_height(&mask_area, frag_size * 2);
|
||
|
lv_area_set_width(&mask_area_blurred, blur_frag_size * 2);
|
||
|
lv_area_set_height(&mask_area_blurred, blur_frag_size * 2);
|
||
|
|
||
|
lv_draw_mask_radius_param_t mask_rout_param;
|
||
|
lv_draw_mask_radius_init(&mask_rout_param, &mask_area, radius, false);
|
||
|
int16_t mask_id = lv_draw_mask_add(&mask_rout_param, NULL);
|
||
|
lv_opa_t * mask_buf = lv_draw_sdl_mask_dump_opa(&mask_area_blurred, &mask_id, 1);
|
||
|
lv_stack_blur_grayscale(mask_buf, lv_area_get_width(&mask_area_blurred), lv_area_get_height(&mask_area_blurred),
|
||
|
sw / 2 + sw % 2);
|
||
|
texture = lv_sdl_create_opa_texture(ctx->renderer, mask_buf, blur_frag_size, blur_frag_size,
|
||
|
lv_area_get_width(&mask_area_blurred));
|
||
|
lv_mem_buf_release(mask_buf);
|
||
|
lv_draw_mask_remove_id(mask_id);
|
||
|
SDL_assert(texture);
|
||
|
lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), texture);
|
||
|
}
|
||
|
|
||
|
SDL_Color shadow_color;
|
||
|
lv_color_to_sdl_color(&dsc->shadow_color, &shadow_color);
|
||
|
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
|
||
|
SDL_SetTextureAlphaMod(texture, opa);
|
||
|
SDL_SetTextureColorMod(texture, shadow_color.r, shadow_color.g, shadow_color.b);
|
||
|
|
||
|
lv_draw_sdl_rect_bg_frag_draw_corners(ctx, texture, blur_frag_size, &shadow_area, clip, false);
|
||
|
frag_render_borders(ctx->renderer, texture, blur_frag_size, &shadow_area, clip, false);
|
||
|
frag_render_center(ctx->renderer, texture, blur_frag_size, &shadow_area, clip, false);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void draw_border(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
|
||
|
const lv_draw_rect_dsc_t * dsc)
|
||
|
{
|
||
|
if(SKIP_BORDER(dsc)) return;
|
||
|
|
||
|
SDL_Color border_color;
|
||
|
lv_color_to_sdl_color(&dsc->border_color, &border_color);
|
||
|
|
||
|
lv_coord_t coords_w = lv_area_get_width(coords), coords_h = lv_area_get_height(coords);
|
||
|
lv_coord_t short_side = LV_MIN(coords_w, coords_h);
|
||
|
lv_coord_t rout = LV_MIN(dsc->radius, short_side / 2);/*Get the inner area*/
|
||
|
lv_area_t area_inner;
|
||
|
lv_area_copy(&area_inner, coords);// lv_area_increase(&area_inner, 1, 1);
|
||
|
area_inner.x1 += ((dsc->border_side & LV_BORDER_SIDE_LEFT) ? dsc->border_width : -(dsc->border_width + rout));
|
||
|
area_inner.x2 -= ((dsc->border_side & LV_BORDER_SIDE_RIGHT) ? dsc->border_width : -(dsc->border_width + rout));
|
||
|
area_inner.y1 += ((dsc->border_side & LV_BORDER_SIDE_TOP) ? dsc->border_width : -(dsc->border_width + rout));
|
||
|
area_inner.y2 -= ((dsc->border_side & LV_BORDER_SIDE_BOTTOM) ? dsc->border_width : -(dsc->border_width + rout));
|
||
|
lv_coord_t rin = LV_MAX(rout - dsc->border_width, 0);
|
||
|
draw_border_generic(ctx, coords, &area_inner, draw_area, rout, rin, dsc->border_color, dsc->border_opa,
|
||
|
dsc->blend_mode);
|
||
|
}
|
||
|
|
||
|
static void draw_outline(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip,
|
||
|
const lv_draw_rect_dsc_t * dsc)
|
||
|
{
|
||
|
if(SKIP_OUTLINE(dsc)) return;
|
||
|
|
||
|
lv_opa_t opa = dsc->outline_opa;
|
||
|
|
||
|
if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
|
||
|
|
||
|
/*Get the inner radius*/
|
||
|
lv_area_t area_inner;
|
||
|
lv_area_copy(&area_inner, coords);
|
||
|
|
||
|
/*Bring the outline closer to make sure there is no color bleeding with pad=0*/
|
||
|
lv_coord_t pad = dsc->outline_pad - 1;
|
||
|
area_inner.x1 -= pad;
|
||
|
area_inner.y1 -= pad;
|
||
|
area_inner.x2 += pad;
|
||
|
area_inner.y2 += pad;
|
||
|
|
||
|
lv_area_t area_outer;
|
||
|
lv_area_copy(&area_outer, &area_inner);
|
||
|
|
||
|
area_outer.x1 -= dsc->outline_width;
|
||
|
area_outer.x2 += dsc->outline_width;
|
||
|
area_outer.y1 -= dsc->outline_width;
|
||
|
area_outer.y2 += dsc->outline_width;
|
||
|
|
||
|
lv_area_t draw_area;
|
||
|
if(!_lv_area_intersect(&draw_area, &area_outer, clip)) return;
|
||
|
|
||
|
int32_t inner_w = lv_area_get_width(&area_inner);
|
||
|
int32_t inner_h = lv_area_get_height(&area_inner);
|
||
|
lv_coord_t rin = dsc->radius;
|
||
|
int32_t short_side = LV_MIN(inner_w, inner_h);
|
||
|
if(rin > short_side >> 1) rin = short_side >> 1;
|
||
|
|
||
|
lv_coord_t rout = rin + dsc->outline_width;
|
||
|
|
||
|
draw_border_generic(ctx, &area_outer, &area_inner, clip, rout, rin, dsc->outline_color, dsc->outline_opa,
|
||
|
dsc->blend_mode);
|
||
|
}
|
||
|
|
||
|
static void draw_border_generic(lv_draw_sdl_ctx_t * ctx, const lv_area_t * outer_area, const lv_area_t * inner_area,
|
||
|
const lv_area_t * clip, lv_coord_t rout, lv_coord_t rin, lv_color_t color, lv_opa_t opa,
|
||
|
lv_blend_mode_t blend_mode)
|
||
|
{
|
||
|
opa = opa >= LV_OPA_COVER ? LV_OPA_COVER : opa;
|
||
|
|
||
|
SDL_Renderer * renderer = ctx->renderer;
|
||
|
|
||
|
lv_draw_rect_border_key_t key = rect_border_key_create(rout, rin, outer_area, inner_area);
|
||
|
lv_coord_t radius = LV_MIN3(rout, lv_area_get_width(outer_area) / 2, lv_area_get_height(outer_area) / 2);
|
||
|
lv_coord_t max_side = LV_MAX4(key.offsets.x1, key.offsets.y1, -key.offsets.x2, -key.offsets.y2);
|
||
|
lv_coord_t frag_size = LV_MAX(radius, max_side);
|
||
|
SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL);
|
||
|
if(texture == NULL) {
|
||
|
/* Create a mask texture with size of (frag_size * 2 + 3) */
|
||
|
const lv_area_t frag_area = {0, 0, frag_size * 2 + 2, frag_size * 2 + 2};
|
||
|
|
||
|
/*Create mask for the outer area*/
|
||
|
int16_t mask_ids[2] = {LV_MASK_ID_INV, LV_MASK_ID_INV};
|
||
|
lv_draw_mask_radius_param_t mask_rout_param;
|
||
|
if(rout > 0) {
|
||
|
lv_draw_mask_radius_init(&mask_rout_param, &frag_area, rout, false);
|
||
|
mask_ids[0] = lv_draw_mask_add(&mask_rout_param, NULL);
|
||
|
}
|
||
|
|
||
|
/*Create mask for the inner mask*/
|
||
|
if(rin < 0) rin = 0;
|
||
|
const lv_area_t frag_inner_area = {frag_area.x1 + key.offsets.x1, frag_area.y1 + key.offsets.y1,
|
||
|
frag_area.x2 + key.offsets.x2, frag_area.y2 + key.offsets.y2
|
||
|
};
|
||
|
lv_draw_mask_radius_param_t mask_rin_param;
|
||
|
lv_draw_mask_radius_init(&mask_rin_param, &frag_inner_area, rin, true);
|
||
|
mask_ids[1] = lv_draw_mask_add(&mask_rin_param, NULL);
|
||
|
|
||
|
texture = lv_draw_sdl_mask_dump_texture(renderer, &frag_area, mask_ids, 2);
|
||
|
|
||
|
lv_draw_mask_remove_id(mask_ids[1]);
|
||
|
lv_draw_mask_remove_id(mask_ids[0]);
|
||
|
SDL_assert(texture);
|
||
|
lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), texture);
|
||
|
}
|
||
|
|
||
|
SDL_Rect outer_rect;
|
||
|
lv_area_to_sdl_rect(outer_area, &outer_rect);
|
||
|
SDL_Color color_sdl;
|
||
|
lv_color_to_sdl_color(&color, &color_sdl);
|
||
|
|
||
|
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
|
||
|
SDL_SetTextureAlphaMod(texture, opa);
|
||
|
SDL_SetTextureColorMod(texture, color_sdl.r, color_sdl.g, color_sdl.b);
|
||
|
|
||
|
lv_draw_sdl_rect_bg_frag_draw_corners(ctx, texture, frag_size, outer_area, clip, true);
|
||
|
frag_render_borders(renderer, texture, frag_size, outer_area, clip, true);
|
||
|
}
|
||
|
|
||
|
static void frag_render_borders(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size,
|
||
|
const lv_area_t * coords, const lv_area_t * clipped, bool full)
|
||
|
{
|
||
|
lv_area_t border_area, dst_area;
|
||
|
/* Top border */
|
||
|
border_area.x1 = coords->x1 + frag_size;
|
||
|
border_area.y1 = coords->y1;
|
||
|
border_area.x2 = coords->x2 - frag_size;
|
||
|
border_area.y2 = coords->y1 + frag_size - 1;
|
||
|
if(_lv_area_intersect(&dst_area, &border_area, clipped)) {
|
||
|
SDL_Rect dst_rect;
|
||
|
lv_area_to_sdl_rect(&dst_area, &dst_rect);
|
||
|
|
||
|
lv_coord_t sy = (lv_coord_t)(dst_area.y1 - border_area.y1);
|
||
|
if(full) {
|
||
|
SDL_Rect src_rect = {frag_size + 1, sy, 1, lv_area_get_height(&dst_area)};
|
||
|
SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
|
||
|
}
|
||
|
else {
|
||
|
SDL_Rect src_rect = {frag_size - 1, sy, 1, lv_area_get_height(&dst_area)};
|
||
|
SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
|
||
|
}
|
||
|
}
|
||
|
/* Bottom border */
|
||
|
border_area.y1 = LV_MAX(coords->y2 - frag_size + 1, coords->y1 + frag_size);
|
||
|
border_area.y2 = coords->y2;
|
||
|
if(_lv_area_intersect(&dst_area, &border_area, clipped)) {
|
||
|
SDL_Rect dst_rect;
|
||
|
lv_area_to_sdl_rect(&dst_area, &dst_rect);
|
||
|
|
||
|
lv_coord_t dh = lv_area_get_height(&dst_area);
|
||
|
if(full) {
|
||
|
lv_coord_t sy = (lv_coord_t)(dst_area.y1 - border_area.y1);
|
||
|
SDL_Rect src_rect = {frag_size + 1, frag_size + 3 + sy, 1, dh};
|
||
|
SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
|
||
|
}
|
||
|
else {
|
||
|
lv_coord_t sy = (lv_coord_t)(border_area.y2 - dst_area.y2);
|
||
|
SDL_Rect src_rect = {frag_size - 1, sy, 1, dh};
|
||
|
SDL_RenderCopyEx(renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_VERTICAL);
|
||
|
}
|
||
|
}
|
||
|
/* Left border */
|
||
|
border_area.x1 = coords->x1;
|
||
|
border_area.y1 = coords->y1 + frag_size;
|
||
|
border_area.x2 = coords->x1 + frag_size - 1;
|
||
|
border_area.y2 = coords->y2 - frag_size;
|
||
|
if(_lv_area_intersect(&dst_area, &border_area, clipped)) {
|
||
|
SDL_Rect dst_rect;
|
||
|
lv_area_to_sdl_rect(&dst_area, &dst_rect);
|
||
|
|
||
|
lv_coord_t dw = lv_area_get_width(&dst_area);
|
||
|
lv_coord_t sx = (lv_coord_t)(dst_area.x1 - border_area.x1);
|
||
|
if(full) {
|
||
|
SDL_Rect src_rect = {sx, frag_size + 1, dw, 1};
|
||
|
SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
|
||
|
}
|
||
|
else {
|
||
|
SDL_Rect src_rect = {sx, frag_size - 1, dw, 1};
|
||
|
SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
|
||
|
}
|
||
|
}
|
||
|
/* Right border */
|
||
|
border_area.x1 = LV_MAX(coords->x2 - frag_size + 1, coords->x1 + frag_size);
|
||
|
border_area.x2 = coords->x2;
|
||
|
if(_lv_area_intersect(&dst_area, &border_area, clipped)) {
|
||
|
SDL_Rect dst_rect;
|
||
|
lv_area_to_sdl_rect(&dst_area, &dst_rect);
|
||
|
|
||
|
lv_coord_t dw = lv_area_get_width(&dst_area);
|
||
|
if(full) {
|
||
|
lv_coord_t sx = (lv_coord_t)(dst_area.x1 - border_area.x1);
|
||
|
SDL_Rect src_rect = {frag_size + 3 + sx, frag_size + 1, dw, 1};
|
||
|
SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
|
||
|
}
|
||
|
else {
|
||
|
lv_coord_t sx = (lv_coord_t)(border_area.x2 - dst_area.x2);
|
||
|
SDL_Rect src_rect = {sx, frag_size - 1, dw, 1};
|
||
|
SDL_RenderCopyEx(renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_HORIZONTAL);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void frag_render_center(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size,
|
||
|
const lv_area_t * coords,
|
||
|
const lv_area_t * clipped, bool full)
|
||
|
{
|
||
|
lv_area_t center_area = {
|
||
|
coords->x1 + frag_size,
|
||
|
coords->y1 + frag_size,
|
||
|
coords->x2 - frag_size,
|
||
|
coords->y2 - frag_size,
|
||
|
};
|
||
|
if(center_area.x2 < center_area.x1 || center_area.y2 < center_area.y1) return;
|
||
|
lv_area_t draw_area;
|
||
|
if(!_lv_area_intersect(&draw_area, ¢er_area, clipped)) {
|
||
|
return;
|
||
|
}
|
||
|
SDL_Rect dst_rect;
|
||
|
lv_area_to_sdl_rect(&draw_area, &dst_rect);
|
||
|
if(full) {
|
||
|
SDL_Rect src_rect = {frag_size, frag_size, 1, 1};
|
||
|
SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
|
||
|
}
|
||
|
else {
|
||
|
SDL_Rect src_rect = {frag_size - 1, frag_size - 1, 1, 1};
|
||
|
SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static lv_draw_rect_bg_key_t rect_bg_key_create(lv_coord_t radius, lv_coord_t size)
|
||
|
{
|
||
|
lv_draw_rect_bg_key_t key;
|
||
|
SDL_memset(&key, 0, sizeof(key));
|
||
|
key.magic = LV_GPU_CACHE_KEY_MAGIC_RECT_BG;
|
||
|
key.radius = radius;
|
||
|
key.size = size;
|
||
|
return key;
|
||
|
}
|
||
|
|
||
|
static lv_draw_rect_shadow_key_t rect_shadow_key_create(lv_coord_t radius, lv_coord_t size, lv_coord_t blur)
|
||
|
{
|
||
|
lv_draw_rect_shadow_key_t key;
|
||
|
SDL_memset(&key, 0, sizeof(key));
|
||
|
key.magic = LV_GPU_CACHE_KEY_MAGIC_RECT_SHADOW;
|
||
|
key.radius = radius;
|
||
|
key.size = size;
|
||
|
key.blur = blur;
|
||
|
return key;
|
||
|
}
|
||
|
|
||
|
static lv_draw_rect_border_key_t rect_border_key_create(lv_coord_t rout, lv_coord_t rin, const lv_area_t * outer_area,
|
||
|
const lv_area_t * inner_area)
|
||
|
{
|
||
|
lv_draw_rect_border_key_t key;
|
||
|
/* VERY IMPORTANT! Padding between members is uninitialized, so we have to wipe them manually */
|
||
|
SDL_memset(&key, 0, sizeof(key));
|
||
|
key.magic = LV_GPU_CACHE_KEY_MAGIC_RECT_BORDER;
|
||
|
key.rout = rout;
|
||
|
key.rin = rin;
|
||
|
key.offsets.x1 = inner_area->x1 - outer_area->x1;
|
||
|
key.offsets.x2 = inner_area->x2 - outer_area->x2;
|
||
|
key.offsets.y1 = inner_area->y1 - outer_area->y1;
|
||
|
key.offsets.y2 = inner_area->y2 - outer_area->y2;
|
||
|
return key;
|
||
|
}
|
||
|
|
||
|
#endif /*LV_USE_GPU_SDL*/
|