acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

lv_draw_sdl_rect.c (29873B)

      1 /**
      2  * @file lv_draw_sdl_rect.c
      3  *
      4  */
      5 
      6 /*********************
      7  *      INCLUDES
      8  *********************/
      9 
     10 #include "../../lv_conf_internal.h"
     11 
     12 #if LV_USE_GPU_SDL
     13 
     14 #include "../lv_draw_rect.h"
     15 #include "../lv_draw_img.h"
     16 #include "../lv_draw_label.h"
     17 #include "../lv_draw_mask.h"
     18 #include "../../core/lv_refr.h"
     19 #include "lv_draw_sdl_utils.h"
     20 #include "lv_draw_sdl_texture_cache.h"
     21 #include "lv_draw_sdl_composite.h"
     22 #include "lv_draw_sdl_mask.h"
     23 #include "lv_draw_sdl_stack_blur.h"
     24 #include "lv_draw_sdl_img.h"
     25 
     26 /*********************
     27  *      DEFINES
     28  *********************/
     29 
     30 /**********************
     31  *      TYPEDEFS
     32  **********************/
     33 
     34 typedef struct {
     35     lv_sdl_cache_key_magic_t magic;
     36     lv_coord_t radius;
     37     lv_coord_t size;
     38 } lv_draw_rect_bg_key_t;
     39 
     40 typedef struct {
     41     lv_sdl_cache_key_magic_t magic;
     42     lv_coord_t radius;
     43     lv_coord_t size;
     44     lv_coord_t blur;
     45 } lv_draw_rect_shadow_key_t;
     46 
     47 typedef struct {
     48     lv_sdl_cache_key_magic_t magic;
     49     lv_coord_t rout, rin;
     50     lv_area_t offsets;
     51 } lv_draw_rect_border_key_t;
     52 
     53 /**********************
     54  *  STATIC PROTOTYPES
     55  **********************/
     56 
     57 static void draw_bg_color(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
     58                           const lv_draw_rect_dsc_t * dsc);
     59 
     60 static void draw_bg_img(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
     61                         const lv_draw_rect_dsc_t * dsc);
     62 
     63 static void draw_border(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
     64                         const lv_draw_rect_dsc_t * dsc);
     65 
     66 static void draw_shadow(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip,
     67                         const lv_draw_rect_dsc_t * dsc);
     68 
     69 static void draw_outline(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip,
     70                          const lv_draw_rect_dsc_t * dsc);
     71 
     72 static void draw_border_generic(lv_draw_sdl_ctx_t * ctx, const lv_area_t * outer_area, const lv_area_t * inner_area,
     73                                 const lv_area_t * clip, lv_coord_t rout, lv_coord_t rin, lv_color_t color, lv_opa_t opa,
     74                                 lv_blend_mode_t blend_mode);
     75 
     76 static void frag_render_borders(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size,
     77                                 const lv_area_t * coords, const lv_area_t * clipped, bool full);
     78 
     79 static void frag_render_center(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size,
     80                                const lv_area_t * coords, const lv_area_t * clipped, bool full);
     81 
     82 static lv_draw_rect_bg_key_t rect_bg_key_create(lv_coord_t radius, lv_coord_t size);
     83 
     84 static lv_draw_rect_shadow_key_t rect_shadow_key_create(lv_coord_t radius, lv_coord_t size, lv_coord_t blur);
     85 
     86 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,
     87                                                         const lv_area_t * inner_area);
     88 
     89 /**********************
     90  *  STATIC VARIABLES
     91  **********************/
     92 
     93 /**********************
     94  *      MACROS
     95  **********************/
     96 #define SKIP_BORDER(dsc) ((dsc)->border_opa <= LV_OPA_MIN || (dsc)->border_width == 0 || (dsc)->border_side == LV_BORDER_SIDE_NONE || (dsc)->border_post)
     97 #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))
     98 #define SKIP_IMAGE(dsc) ((dsc)->bg_img_src == NULL || (dsc)->bg_img_opa <= LV_OPA_MIN)
     99 #define SKIP_OUTLINE(dsc) ((dsc)->outline_opa <= LV_OPA_MIN || (dsc)->outline_width == 0)
    100 
    101 /**********************
    102  *   GLOBAL FUNCTIONS
    103  **********************/
    104 
    105 void lv_draw_sdl_draw_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
    106 {
    107     const lv_area_t * clip = draw_ctx->clip_area;
    108     lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
    109 
    110     lv_area_t extension = {0, 0, 0, 0};
    111     if(!SKIP_SHADOW(dsc)) {
    112         lv_coord_t ext = (lv_coord_t)(dsc->shadow_spread - dsc->shadow_width / 2 + 1);
    113         extension.x1 = LV_MAX(extension.x1, -dsc->shadow_ofs_x + ext);
    114         extension.x2 = LV_MAX(extension.x2, dsc->shadow_ofs_x + ext);
    115         extension.y1 = LV_MAX(extension.y1, -dsc->shadow_ofs_y + ext);
    116         extension.y2 = LV_MAX(extension.y2, dsc->shadow_ofs_y + ext);
    117     }
    118     if(!SKIP_OUTLINE(dsc)) {
    119         lv_coord_t ext = (lv_coord_t)(dsc->outline_pad - 1 + dsc->outline_width);
    120         extension.x1 = LV_MAX(extension.x1, ext);
    121         extension.x2 = LV_MAX(extension.x2, ext);
    122         extension.y1 = LV_MAX(extension.y1, ext);
    123         extension.y2 = LV_MAX(extension.y2, ext);
    124     }
    125     /* Coords will be translated so coords will start at (0,0) */
    126     lv_area_t t_coords = *coords, t_clip = *clip, apply_area, t_area;
    127     lv_draw_sdl_composite_begin(ctx, coords, clip, &extension, dsc->blend_mode, &t_coords, &t_clip, &apply_area);
    128     bool has_content = _lv_area_intersect(&t_area, &t_coords, &t_clip);
    129 
    130     SDL_Rect clip_rect;
    131     lv_area_to_sdl_rect(&t_clip, &clip_rect);
    132     draw_shadow(ctx, &t_coords, &t_clip, dsc);
    133     /* Shadows and outlines will also draw in extended area */
    134     if(has_content) {
    135         draw_bg_color(ctx, &t_coords, &t_area, dsc);
    136         draw_bg_img(ctx, &t_coords, &t_area, dsc);
    137         draw_border(ctx, &t_coords, &t_area, dsc);
    138     }
    139     draw_outline(ctx, &t_coords, &t_clip, dsc);
    140 
    141     lv_draw_sdl_composite_end(ctx, &apply_area, dsc->blend_mode);
    142 }
    143 
    144 SDL_Texture * lv_draw_sdl_rect_bg_frag_obtain(lv_draw_sdl_ctx_t * ctx, lv_coord_t radius)
    145 {
    146     lv_draw_rect_bg_key_t key = rect_bg_key_create(radius, radius);
    147     lv_area_t coords = {0, 0, radius * 2 - 1, radius * 2 - 1};
    148     lv_area_t coords_frag = {0, 0, radius - 1, radius - 1};
    149     SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL);
    150     if(texture == NULL) {
    151         lv_draw_mask_radius_param_t mask_rout_param;
    152         lv_draw_mask_radius_init(&mask_rout_param, &coords, radius, false);
    153         int16_t mask_id = lv_draw_mask_add(&mask_rout_param, NULL);
    154         texture = lv_draw_sdl_mask_dump_texture(ctx->renderer, &coords_frag, &mask_id, 1);
    155         lv_draw_mask_remove_id(mask_id);
    156         SDL_assert(texture);
    157         lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), texture);
    158     }
    159     return texture;
    160 }
    161 
    162 void lv_draw_sdl_rect_bg_frag_draw_corners(lv_draw_sdl_ctx_t * ctx, SDL_Texture * frag, lv_coord_t frag_size,
    163                                            const lv_area_t * coords, const lv_area_t * clip, bool full)
    164 {
    165     if(!clip) clip = coords;
    166     lv_area_t corner_area, dst_area;
    167     /* Upper left */
    168     corner_area.x1 = coords->x1;
    169     corner_area.y1 = coords->y1;
    170     corner_area.x2 = coords->x1 + frag_size - 1;
    171     corner_area.y2 = coords->y1 + frag_size - 1;
    172     if(_lv_area_intersect(&dst_area, &corner_area, clip)) {
    173         SDL_Rect dst_rect;
    174         lv_area_to_sdl_rect(&dst_area, &dst_rect);
    175 
    176         lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area);
    177         lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1), sy = (lv_coord_t)(dst_area.y1 - corner_area.y1);
    178         SDL_Rect src_rect = {sx, sy, dw, dh};
    179         SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect);
    180     }
    181     /* Upper right, clip right edge if too big */
    182     corner_area.x1 = LV_MAX(coords->x2 - frag_size + 1, coords->x1 + frag_size);
    183     corner_area.x2 = coords->x2;
    184     if(_lv_area_intersect(&dst_area, &corner_area, clip)) {
    185         SDL_Rect dst_rect;
    186         lv_area_to_sdl_rect(&dst_area, &dst_rect);
    187 
    188         lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area);
    189         if(full) {
    190             lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1),
    191                        sy = (lv_coord_t)(dst_area.y1 - corner_area.y1);
    192             SDL_Rect src_rect = {frag_size + 3 + sx, sy, dw, dh};
    193             SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect);
    194         }
    195         else {
    196             SDL_Rect src_rect = {corner_area.x2 - dst_area.x2, dst_area.y1 - corner_area.y1, dw, dh};
    197             SDL_RenderCopyEx(ctx->renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_HORIZONTAL);
    198         }
    199     }
    200     /* Lower right, clip bottom edge if too big */
    201     corner_area.y1 = LV_MAX(coords->y2 - frag_size + 1, coords->y1 + frag_size);
    202     corner_area.y2 = coords->y2;
    203     if(_lv_area_intersect(&dst_area, &corner_area, clip)) {
    204         SDL_Rect dst_rect;
    205         lv_area_to_sdl_rect(&dst_area, &dst_rect);
    206 
    207         lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area);
    208         if(full) {
    209             lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1),
    210                        sy = (lv_coord_t)(dst_area.y1 - corner_area.y1);
    211             SDL_Rect src_rect = {frag_size + 3 + sx, frag_size + 3 + sy, dw, dh};
    212             SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect);
    213         }
    214         else {
    215             SDL_Rect src_rect = {corner_area.x2 - dst_area.x2, corner_area.y2 - dst_area.y2, dw, dh};
    216             SDL_RenderCopyEx(ctx->renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_HORIZONTAL | SDL_FLIP_VERTICAL);
    217         }
    218     }
    219     /* Lower left, right edge should not be clip */
    220     corner_area.x1 = coords->x1;
    221     corner_area.x2 = coords->x1 + frag_size - 1;
    222     if(_lv_area_intersect(&dst_area, &corner_area, clip)) {
    223         SDL_Rect dst_rect;
    224         lv_area_to_sdl_rect(&dst_area, &dst_rect);
    225 
    226         lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area);
    227         if(full) {
    228             lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1),
    229                        sy = (lv_coord_t)(dst_area.y1 - corner_area.y1);
    230             SDL_Rect src_rect = {sx, frag_size + 3 + sy, dw, dh};
    231             SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect);
    232         }
    233         else {
    234             SDL_Rect src_rect = {dst_area.x1 - corner_area.x1, corner_area.y2 - dst_area.y2, dw, dh};
    235             SDL_RenderCopyEx(ctx->renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_VERTICAL);
    236         }
    237     }
    238 }
    239 
    240 
    241 /**********************
    242  *   STATIC FUNCTIONS
    243  **********************/
    244 
    245 static void draw_bg_color(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
    246                           const lv_draw_rect_dsc_t * dsc)
    247 {
    248     if(dsc->bg_opa == 0) {
    249         return;
    250     }
    251     SDL_Color bg_color;
    252     lv_color_to_sdl_color(&dsc->bg_color, &bg_color);
    253     lv_coord_t radius = dsc->radius;
    254     if(radius <= 0) {
    255         SDL_Rect rect;
    256         lv_area_to_sdl_rect(draw_area, &rect);
    257         SDL_SetRenderDrawColor(ctx->renderer, bg_color.r, bg_color.g, bg_color.b, dsc->bg_opa);
    258         SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_BLEND);
    259         SDL_RenderFillRect(ctx->renderer, &rect);
    260         return;
    261     }
    262 
    263     /*A small texture with a quarter of the rect is enough*/
    264     lv_coord_t bg_w = lv_area_get_width(coords), bg_h = lv_area_get_height(coords);
    265     lv_coord_t real_radius = LV_MIN3(bg_w / 2, bg_h / 2, radius);
    266     SDL_Texture * texture = lv_draw_sdl_rect_bg_frag_obtain(ctx, real_radius);
    267 
    268     SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
    269     SDL_SetTextureAlphaMod(texture, dsc->bg_opa);
    270     SDL_SetTextureColorMod(texture, bg_color.r, bg_color.g, bg_color.b);
    271     lv_draw_sdl_rect_bg_frag_draw_corners(ctx, texture, real_radius, coords, draw_area, false);
    272     frag_render_borders(ctx->renderer, texture, real_radius, coords, draw_area, false);
    273     frag_render_center(ctx->renderer, texture, real_radius, coords, draw_area, false);
    274 }
    275 
    276 static void draw_bg_img(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
    277                         const lv_draw_rect_dsc_t * dsc)
    278 {
    279     if(SKIP_IMAGE(dsc)) return;
    280 
    281     lv_img_src_t src_type = lv_img_src_get_type(dsc->bg_img_src);
    282     if(src_type == LV_IMG_SRC_SYMBOL) {
    283         lv_point_t size;
    284         lv_txt_get_size(&size, dsc->bg_img_src, dsc->bg_img_symbol_font, 0, 0, LV_COORD_MAX, LV_TEXT_FLAG_NONE);
    285         lv_area_t a;
    286         a.x1 = coords->x1 + lv_area_get_width(coords) / 2 - size.x / 2;
    287         a.x2 = a.x1 + size.x - 1;
    288         a.y1 = coords->y1 + lv_area_get_height(coords) / 2 - size.y / 2;
    289         a.y2 = a.y1 + size.y - 1;
    290 
    291         lv_draw_label_dsc_t label_draw_dsc;
    292         lv_draw_label_dsc_init(&label_draw_dsc);
    293         label_draw_dsc.font = dsc->bg_img_symbol_font;
    294         label_draw_dsc.color = dsc->bg_img_recolor;
    295         label_draw_dsc.opa = dsc->bg_img_opa;
    296         lv_draw_label((lv_draw_ctx_t *) ctx, &label_draw_dsc, &a, dsc->bg_img_src, NULL);
    297     }
    298     else {
    299         lv_img_header_t header;
    300         size_t key_size;
    301         lv_draw_sdl_cache_key_head_img_t * key = lv_draw_sdl_texture_img_key_create(dsc->bg_img_src, 0, &key_size);
    302         bool key_found;
    303         lv_img_header_t * cache_header = NULL;
    304         SDL_Texture * texture = lv_draw_sdl_texture_cache_get_with_userdata(ctx, key, key_size, &key_found,
    305                                                                             (void **) &cache_header);
    306         SDL_free(key);
    307         if(texture) {
    308             header = *cache_header;
    309         }
    310         else if(key_found || lv_img_decoder_get_info(dsc->bg_img_src, &header) != LV_RES_OK) {
    311             /* When cache hit but with negative result, use default decoder. If still fail, return.*/
    312             LV_LOG_WARN("Couldn't read the background image");
    313             return;
    314         }
    315 
    316         lv_draw_img_dsc_t img_dsc;
    317         lv_draw_img_dsc_init(&img_dsc);
    318         img_dsc.blend_mode = dsc->blend_mode;
    319         img_dsc.recolor = dsc->bg_img_recolor;
    320         img_dsc.recolor_opa = dsc->bg_img_recolor_opa;
    321         img_dsc.opa = dsc->bg_img_opa;
    322         img_dsc.frame_id = 0;
    323 
    324         int16_t radius_mask_id = LV_MASK_ID_INV;
    325         lv_draw_mask_radius_param_t radius_param;
    326         if(dsc->radius > 0) {
    327             lv_draw_mask_radius_init(&radius_param, coords, dsc->radius, false);
    328             radius_mask_id = lv_draw_mask_add(&radius_param, NULL);
    329         }
    330 
    331         /*Center align*/
    332         if(dsc->bg_img_tiled == false) {
    333             lv_area_t area;
    334             area.x1 = coords->x1 + lv_area_get_width(coords) / 2 - header.w / 2;
    335             area.y1 = coords->y1 + lv_area_get_height(coords) / 2 - header.h / 2;
    336             area.x2 = area.x1 + header.w - 1;
    337             area.y2 = area.y1 + header.h - 1;
    338 
    339             lv_draw_img((lv_draw_ctx_t *) ctx, &img_dsc, &area, dsc->bg_img_src);
    340         }
    341         else {
    342             lv_area_t area;
    343             area.y1 = coords->y1;
    344             area.y2 = area.y1 + header.h - 1;
    345 
    346             for(; area.y1 <= coords->y2; area.y1 += header.h, area.y2 += header.h) {
    347 
    348                 area.x1 = coords->x1;
    349                 area.x2 = area.x1 + header.w - 1;
    350                 for(; area.x1 <= coords->x2; area.x1 += header.w, area.x2 += header.w) {
    351                     lv_draw_img((lv_draw_ctx_t *) ctx, &img_dsc, &area, dsc->bg_img_src);
    352                 }
    353             }
    354         }
    355 
    356         if(radius_mask_id != LV_MASK_ID_INV) {
    357             lv_draw_mask_remove_id(radius_mask_id);
    358             lv_draw_mask_free_param(&radius_param);
    359         }
    360     }
    361 }
    362 
    363 static void draw_shadow(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip,
    364                         const lv_draw_rect_dsc_t * dsc)
    365 {
    366     /*Check whether the shadow is visible*/
    367     if(SKIP_SHADOW(dsc)) return;
    368 
    369     lv_coord_t sw = dsc->shadow_width;
    370 
    371     lv_area_t core_area;
    372     core_area.x1 = coords->x1 + dsc->shadow_ofs_x - dsc->shadow_spread;
    373     core_area.x2 = coords->x2 + dsc->shadow_ofs_x + dsc->shadow_spread;
    374     core_area.y1 = coords->y1 + dsc->shadow_ofs_y - dsc->shadow_spread;
    375     core_area.y2 = coords->y2 + dsc->shadow_ofs_y + dsc->shadow_spread;
    376 
    377     lv_area_t shadow_area;
    378     shadow_area.x1 = core_area.x1 - sw / 2 - 1;
    379     shadow_area.x2 = core_area.x2 + sw / 2 + 1;
    380     shadow_area.y1 = core_area.y1 - sw / 2 - 1;
    381     shadow_area.y2 = core_area.y2 + sw / 2 + 1;
    382 
    383     lv_opa_t opa = dsc->shadow_opa;
    384 
    385     if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
    386 
    387     /*Get clipped draw area which is the real draw area.
    388      *It is always the same or inside `shadow_area`*/
    389     lv_area_t draw_area;
    390     if(!_lv_area_intersect(&draw_area, &shadow_area, clip)) return;
    391 
    392     SDL_Rect core_area_rect;
    393     lv_area_to_sdl_rect(&shadow_area, &core_area_rect);
    394 
    395     lv_coord_t radius = dsc->radius;
    396     /* No matter how big the shadow is, what we need is just a corner */
    397     lv_coord_t frag_size = LV_MIN3(lv_area_get_width(&core_area) / 2, lv_area_get_height(&core_area) / 2,
    398                                    LV_MAX(sw / 2, radius));
    399 
    400     /* This is how big the corner is after blurring */
    401     lv_coord_t blur_growth = (lv_coord_t)(sw / 2 + 1);
    402 
    403     lv_coord_t blur_frag_size = (lv_coord_t)(frag_size + blur_growth);
    404 
    405     lv_draw_rect_shadow_key_t key = rect_shadow_key_create(radius, frag_size, sw);
    406 
    407     SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL);
    408     if(texture == NULL) {
    409         lv_area_t mask_area = {blur_growth, blur_growth}, mask_area_blurred = {0, 0};
    410         lv_area_set_width(&mask_area, frag_size * 2);
    411         lv_area_set_height(&mask_area, frag_size * 2);
    412         lv_area_set_width(&mask_area_blurred, blur_frag_size * 2);
    413         lv_area_set_height(&mask_area_blurred, blur_frag_size * 2);
    414 
    415         lv_draw_mask_radius_param_t mask_rout_param;
    416         lv_draw_mask_radius_init(&mask_rout_param, &mask_area, radius, false);
    417         int16_t mask_id = lv_draw_mask_add(&mask_rout_param, NULL);
    418         lv_opa_t * mask_buf = lv_draw_sdl_mask_dump_opa(&mask_area_blurred, &mask_id, 1);
    419         lv_stack_blur_grayscale(mask_buf, lv_area_get_width(&mask_area_blurred), lv_area_get_height(&mask_area_blurred),
    420                                 sw / 2 + sw % 2);
    421         texture = lv_sdl_create_opa_texture(ctx->renderer, mask_buf, blur_frag_size, blur_frag_size,
    422                                             lv_area_get_width(&mask_area_blurred));
    423         lv_mem_buf_release(mask_buf);
    424         lv_draw_mask_remove_id(mask_id);
    425         SDL_assert(texture);
    426         lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), texture);
    427     }
    428 
    429     SDL_Color shadow_color;
    430     lv_color_to_sdl_color(&dsc->shadow_color, &shadow_color);
    431     SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
    432     SDL_SetTextureAlphaMod(texture, opa);
    433     SDL_SetTextureColorMod(texture, shadow_color.r, shadow_color.g, shadow_color.b);
    434 
    435     lv_draw_sdl_rect_bg_frag_draw_corners(ctx, texture, blur_frag_size, &shadow_area, clip, false);
    436     frag_render_borders(ctx->renderer, texture, blur_frag_size, &shadow_area, clip, false);
    437     frag_render_center(ctx->renderer, texture, blur_frag_size, &shadow_area, clip, false);
    438 }
    439 
    440 
    441 static void draw_border(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
    442                         const lv_draw_rect_dsc_t * dsc)
    443 {
    444     if(SKIP_BORDER(dsc)) return;
    445 
    446     SDL_Color border_color;
    447     lv_color_to_sdl_color(&dsc->border_color, &border_color);
    448 
    449     lv_coord_t coords_w = lv_area_get_width(coords), coords_h = lv_area_get_height(coords);
    450     lv_coord_t short_side = LV_MIN(coords_w, coords_h);
    451     lv_coord_t rout = LV_MIN(dsc->radius, short_side / 2);/*Get the inner area*/
    452     lv_area_t area_inner;
    453     lv_area_copy(&area_inner, coords);//        lv_area_increase(&area_inner, 1, 1);
    454     area_inner.x1 += ((dsc->border_side & LV_BORDER_SIDE_LEFT) ? dsc->border_width : -(dsc->border_width + rout));
    455     area_inner.x2 -= ((dsc->border_side & LV_BORDER_SIDE_RIGHT) ? dsc->border_width : -(dsc->border_width + rout));
    456     area_inner.y1 += ((dsc->border_side & LV_BORDER_SIDE_TOP) ? dsc->border_width : -(dsc->border_width + rout));
    457     area_inner.y2 -= ((dsc->border_side & LV_BORDER_SIDE_BOTTOM) ? dsc->border_width : -(dsc->border_width + rout));
    458     lv_coord_t rin = LV_MAX(rout - dsc->border_width, 0);
    459     draw_border_generic(ctx, coords, &area_inner, draw_area, rout, rin, dsc->border_color, dsc->border_opa,
    460                         dsc->blend_mode);
    461 }
    462 
    463 static void draw_outline(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip,
    464                          const lv_draw_rect_dsc_t * dsc)
    465 {
    466     if(SKIP_OUTLINE(dsc)) return;
    467 
    468     lv_opa_t opa = dsc->outline_opa;
    469 
    470     if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
    471 
    472     /*Get the inner radius*/
    473     lv_area_t area_inner;
    474     lv_area_copy(&area_inner, coords);
    475 
    476     /*Bring the outline closer to make sure there is no color bleeding with pad=0*/
    477     lv_coord_t pad = dsc->outline_pad - 1;
    478     area_inner.x1 -= pad;
    479     area_inner.y1 -= pad;
    480     area_inner.x2 += pad;
    481     area_inner.y2 += pad;
    482 
    483     lv_area_t area_outer;
    484     lv_area_copy(&area_outer, &area_inner);
    485 
    486     area_outer.x1 -= dsc->outline_width;
    487     area_outer.x2 += dsc->outline_width;
    488     area_outer.y1 -= dsc->outline_width;
    489     area_outer.y2 += dsc->outline_width;
    490 
    491     lv_area_t draw_area;
    492     if(!_lv_area_intersect(&draw_area, &area_outer, clip)) return;
    493 
    494     int32_t inner_w = lv_area_get_width(&area_inner);
    495     int32_t inner_h = lv_area_get_height(&area_inner);
    496     lv_coord_t rin = dsc->radius;
    497     int32_t short_side = LV_MIN(inner_w, inner_h);
    498     if(rin > short_side >> 1) rin = short_side >> 1;
    499 
    500     lv_coord_t rout = rin + dsc->outline_width;
    501 
    502     draw_border_generic(ctx, &area_outer, &area_inner, clip, rout, rin, dsc->outline_color, dsc->outline_opa,
    503                         dsc->blend_mode);
    504 }
    505 
    506 static void draw_border_generic(lv_draw_sdl_ctx_t * ctx, const lv_area_t * outer_area, const lv_area_t * inner_area,
    507                                 const lv_area_t * clip, lv_coord_t rout, lv_coord_t rin, lv_color_t color, lv_opa_t opa,
    508                                 lv_blend_mode_t blend_mode)
    509 {
    510     opa = opa >= LV_OPA_COVER ? LV_OPA_COVER : opa;
    511 
    512     SDL_Renderer * renderer = ctx->renderer;
    513 
    514     lv_draw_rect_border_key_t key = rect_border_key_create(rout, rin, outer_area, inner_area);
    515     lv_coord_t radius = LV_MIN3(rout, lv_area_get_width(outer_area) / 2, lv_area_get_height(outer_area) / 2);
    516     lv_coord_t max_side = LV_MAX4(key.offsets.x1, key.offsets.y1, -key.offsets.x2, -key.offsets.y2);
    517     lv_coord_t frag_size = LV_MAX(radius, max_side);
    518     SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL);
    519     if(texture == NULL) {
    520         /* Create a mask texture with size of (frag_size * 2 + 3) */
    521         const lv_area_t frag_area = {0, 0, frag_size * 2 + 2, frag_size * 2 + 2};
    522 
    523         /*Create mask for the outer area*/
    524         int16_t mask_ids[2] = {LV_MASK_ID_INV, LV_MASK_ID_INV};
    525         lv_draw_mask_radius_param_t mask_rout_param;
    526         if(rout > 0) {
    527             lv_draw_mask_radius_init(&mask_rout_param, &frag_area, rout, false);
    528             mask_ids[0] = lv_draw_mask_add(&mask_rout_param, NULL);
    529         }
    530 
    531         /*Create mask for the inner mask*/
    532         if(rin < 0) rin = 0;
    533         const lv_area_t frag_inner_area = {frag_area.x1 + key.offsets.x1, frag_area.y1 + key.offsets.y1,
    534                                            frag_area.x2 + key.offsets.x2, frag_area.y2 + key.offsets.y2
    535                                           };
    536         lv_draw_mask_radius_param_t mask_rin_param;
    537         lv_draw_mask_radius_init(&mask_rin_param, &frag_inner_area, rin, true);
    538         mask_ids[1] = lv_draw_mask_add(&mask_rin_param, NULL);
    539 
    540         texture = lv_draw_sdl_mask_dump_texture(renderer, &frag_area, mask_ids, 2);
    541 
    542         lv_draw_mask_remove_id(mask_ids[1]);
    543         lv_draw_mask_remove_id(mask_ids[0]);
    544         SDL_assert(texture);
    545         lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), texture);
    546     }
    547 
    548     SDL_Rect outer_rect;
    549     lv_area_to_sdl_rect(outer_area, &outer_rect);
    550     SDL_Color color_sdl;
    551     lv_color_to_sdl_color(&color, &color_sdl);
    552 
    553     SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
    554     SDL_SetTextureAlphaMod(texture, opa);
    555     SDL_SetTextureColorMod(texture, color_sdl.r, color_sdl.g, color_sdl.b);
    556 
    557     lv_draw_sdl_rect_bg_frag_draw_corners(ctx, texture, frag_size, outer_area, clip, true);
    558     frag_render_borders(renderer, texture, frag_size, outer_area, clip, true);
    559 }
    560 
    561 static void frag_render_borders(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size,
    562                                 const lv_area_t * coords, const lv_area_t * clipped, bool full)
    563 {
    564     lv_area_t border_area, dst_area;
    565     /* Top border */
    566     border_area.x1 = coords->x1 + frag_size;
    567     border_area.y1 = coords->y1;
    568     border_area.x2 = coords->x2 - frag_size;
    569     border_area.y2 = coords->y1 + frag_size - 1;
    570     if(_lv_area_intersect(&dst_area, &border_area, clipped)) {
    571         SDL_Rect dst_rect;
    572         lv_area_to_sdl_rect(&dst_area, &dst_rect);
    573 
    574         lv_coord_t sy = (lv_coord_t)(dst_area.y1 - border_area.y1);
    575         if(full) {
    576             SDL_Rect src_rect = {frag_size + 1, sy, 1, lv_area_get_height(&dst_area)};
    577             SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
    578         }
    579         else {
    580             SDL_Rect src_rect = {frag_size - 1, sy, 1, lv_area_get_height(&dst_area)};
    581             SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
    582         }
    583     }
    584     /* Bottom border */
    585     border_area.y1 = LV_MAX(coords->y2 - frag_size + 1, coords->y1 + frag_size);
    586     border_area.y2 = coords->y2;
    587     if(_lv_area_intersect(&dst_area, &border_area, clipped)) {
    588         SDL_Rect dst_rect;
    589         lv_area_to_sdl_rect(&dst_area, &dst_rect);
    590 
    591         lv_coord_t dh = lv_area_get_height(&dst_area);
    592         if(full) {
    593             lv_coord_t sy = (lv_coord_t)(dst_area.y1 - border_area.y1);
    594             SDL_Rect src_rect = {frag_size + 1, frag_size + 3 + sy, 1, dh};
    595             SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
    596         }
    597         else {
    598             lv_coord_t sy = (lv_coord_t)(border_area.y2 - dst_area.y2);
    599             SDL_Rect src_rect = {frag_size - 1, sy, 1, dh};
    600             SDL_RenderCopyEx(renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_VERTICAL);
    601         }
    602     }
    603     /* Left border */
    604     border_area.x1 = coords->x1;
    605     border_area.y1 = coords->y1 + frag_size;
    606     border_area.x2 = coords->x1 + frag_size - 1;
    607     border_area.y2 = coords->y2 - frag_size;
    608     if(_lv_area_intersect(&dst_area, &border_area, clipped)) {
    609         SDL_Rect dst_rect;
    610         lv_area_to_sdl_rect(&dst_area, &dst_rect);
    611 
    612         lv_coord_t dw = lv_area_get_width(&dst_area);
    613         lv_coord_t sx = (lv_coord_t)(dst_area.x1 - border_area.x1);
    614         if(full) {
    615             SDL_Rect src_rect = {sx, frag_size + 1, dw, 1};
    616             SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
    617         }
    618         else {
    619             SDL_Rect src_rect = {sx, frag_size - 1, dw, 1};
    620             SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
    621         }
    622     }
    623     /* Right border */
    624     border_area.x1 = LV_MAX(coords->x2 - frag_size + 1, coords->x1 + frag_size);
    625     border_area.x2 = coords->x2;
    626     if(_lv_area_intersect(&dst_area, &border_area, clipped)) {
    627         SDL_Rect dst_rect;
    628         lv_area_to_sdl_rect(&dst_area, &dst_rect);
    629 
    630         lv_coord_t dw = lv_area_get_width(&dst_area);
    631         if(full) {
    632             lv_coord_t sx = (lv_coord_t)(dst_area.x1 - border_area.x1);
    633             SDL_Rect src_rect = {frag_size + 3 + sx, frag_size + 1, dw, 1};
    634             SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
    635         }
    636         else {
    637             lv_coord_t sx = (lv_coord_t)(border_area.x2 - dst_area.x2);
    638             SDL_Rect src_rect = {sx, frag_size - 1, dw, 1};
    639             SDL_RenderCopyEx(renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_HORIZONTAL);
    640         }
    641     }
    642 }
    643 
    644 static void frag_render_center(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size,
    645                                const lv_area_t * coords,
    646                                const lv_area_t * clipped, bool full)
    647 {
    648     lv_area_t center_area = {
    649         coords->x1 + frag_size,
    650         coords->y1 + frag_size,
    651         coords->x2 - frag_size,
    652         coords->y2 - frag_size,
    653     };
    654     if(center_area.x2 < center_area.x1 || center_area.y2 < center_area.y1) return;
    655     lv_area_t draw_area;
    656     if(!_lv_area_intersect(&draw_area, &center_area, clipped)) {
    657         return;
    658     }
    659     SDL_Rect dst_rect;
    660     lv_area_to_sdl_rect(&draw_area, &dst_rect);
    661     if(full) {
    662         SDL_Rect src_rect = {frag_size, frag_size, 1, 1};
    663         SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
    664     }
    665     else {
    666         SDL_Rect src_rect = {frag_size - 1, frag_size - 1, 1, 1};
    667         SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
    668     }
    669 }
    670 
    671 static lv_draw_rect_bg_key_t rect_bg_key_create(lv_coord_t radius, lv_coord_t size)
    672 {
    673     lv_draw_rect_bg_key_t key;
    674     SDL_memset(&key, 0, sizeof(key));
    675     key.magic = LV_GPU_CACHE_KEY_MAGIC_RECT_BG;
    676     key.radius = radius;
    677     key.size = size;
    678     return key;
    679 }
    680 
    681 static lv_draw_rect_shadow_key_t rect_shadow_key_create(lv_coord_t radius, lv_coord_t size, lv_coord_t blur)
    682 {
    683     lv_draw_rect_shadow_key_t key;
    684     SDL_memset(&key, 0, sizeof(key));
    685     key.magic = LV_GPU_CACHE_KEY_MAGIC_RECT_SHADOW;
    686     key.radius = radius;
    687     key.size = size;
    688     key.blur = blur;
    689     return key;
    690 }
    691 
    692 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,
    693                                                         const lv_area_t * inner_area)
    694 {
    695     lv_draw_rect_border_key_t key;
    696     /* VERY IMPORTANT! Padding between members is uninitialized, so we have to wipe them manually */
    697     SDL_memset(&key, 0, sizeof(key));
    698     key.magic = LV_GPU_CACHE_KEY_MAGIC_RECT_BORDER;
    699     key.rout = rout;
    700     key.rin = rin;
    701     key.offsets.x1 = inner_area->x1 - outer_area->x1;
    702     key.offsets.x2 = inner_area->x2 - outer_area->x2;
    703     key.offsets.y1 = inner_area->y1 - outer_area->y1;
    704     key.offsets.y2 = inner_area->y2 - outer_area->y2;
    705     return key;
    706 }
    707 
    708 #endif /*LV_USE_GPU_SDL*/