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_img.c (18544B)

      1 /**
      2  * @file lv_draw_sdl_img.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_img.h"
     15 #include "../lv_img_cache.h"
     16 #include "../lv_draw_mask.h"
     17 #include "../../misc/lv_lru.h"
     18 #include "../../misc/lv_gc.h"
     19 
     20 #include "lv_draw_sdl_img.h"
     21 #include "lv_draw_sdl_utils.h"
     22 #include "lv_draw_sdl_texture_cache.h"
     23 #include "lv_draw_sdl_composite.h"
     24 #include "lv_draw_sdl_rect.h"
     25 
     26 /*********************
     27  *      DEFINES
     28  *********************/
     29 
     30 /**********************
     31  *      TYPEDEFS
     32  **********************/
     33 
     34 typedef struct {
     35     lv_sdl_cache_key_magic_t magic;
     36     const SDL_Texture * texture;
     37     lv_coord_t w, h, radius;
     38 } lv_draw_img_rounded_key_t;
     39 
     40 enum {
     41     ROUNDED_IMG_PART_LEFT = 0,
     42     ROUNDED_IMG_PART_HCENTER = 1,
     43     ROUNDED_IMG_PART_RIGHT = 2,
     44     ROUNDED_IMG_PART_TOP = 3,
     45     ROUNDED_IMG_PART_VCENTER = 4,
     46     ROUNDED_IMG_PART_BOTTOM = 5,
     47 };
     48 
     49 enum {
     50     ROUNDED_IMG_CORNER_TOP_LEFT = 0,
     51     ROUNDED_IMG_CORNER_TOP_RIGHT = 1,
     52     ROUNDED_IMG_CORNER_BOTTOM_RIGHT = 2,
     53     ROUNDED_IMG_CORNER_BOTTOM_LEFT = 3,
     54 };
     55 
     56 /**********************
     57  *  STATIC PROTOTYPES
     58  **********************/
     59 
     60 static SDL_Texture * upload_img_texture(SDL_Renderer * renderer, lv_img_decoder_dsc_t * dsc);
     61 
     62 static SDL_Texture * upload_img_texture_fallback(SDL_Renderer * renderer, lv_img_decoder_dsc_t * dsc);
     63 
     64 static bool check_mask_simple_radius(const lv_area_t * coords, lv_coord_t * radius);
     65 
     66 static void draw_img_simple(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture, const lv_draw_sdl_img_header_t * header,
     67                             const lv_draw_img_dsc_t * draw_dsc, const lv_area_t * coords, const lv_area_t * clip);
     68 
     69 static void draw_img_rounded(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture, const lv_draw_sdl_img_header_t * header,
     70                              const lv_draw_img_dsc_t * draw_dsc, const lv_area_t * coords, const lv_area_t * clip,
     71                              lv_coord_t radius);
     72 
     73 static SDL_Texture * img_rounded_frag_obtain(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture,
     74                                              const lv_draw_sdl_img_header_t * header, int w, int h, lv_coord_t radius);
     75 
     76 static lv_draw_img_rounded_key_t rounded_key_create(const SDL_Texture * texture, lv_coord_t w, lv_coord_t h,
     77                                                     lv_coord_t radius);
     78 
     79 static void calc_draw_part(SDL_Texture * texture, const lv_draw_sdl_img_header_t * header, const lv_area_t * coords,
     80                            const lv_area_t * clip, SDL_Rect * clipped_src, SDL_Rect * clipped_dst);
     81 /**********************
     82  *  STATIC VARIABLES
     83  **********************/
     84 
     85 /**********************
     86  *      MACROS
     87  **********************/
     88 
     89 
     90 static void apply_recolor_opa(SDL_Texture * texture, const lv_draw_img_dsc_t * draw_dsc);
     91 
     92 /**********************
     93  *   GLOBAL FUNCTIONS
     94  **********************/
     95 
     96 lv_res_t lv_draw_sdl_img_core(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * draw_dsc,
     97                               const lv_area_t * coords, const void * src)
     98 {
     99     const lv_area_t * clip = draw_ctx->clip_area;
    100     lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
    101 
    102     size_t key_size;
    103     lv_draw_sdl_cache_key_head_img_t * key = lv_draw_sdl_texture_img_key_create(src, draw_dsc->frame_id, &key_size);
    104     bool texture_found = false;
    105     lv_draw_sdl_img_header_t * header = NULL;
    106     SDL_Texture * texture = lv_draw_sdl_texture_cache_get_with_userdata(ctx, key, key_size, &texture_found,
    107                                                                         (void **) &header);
    108     if(!texture_found) {
    109         lv_draw_sdl_img_load_texture(ctx, key, key_size, src, draw_dsc->frame_id, &texture, &header);
    110     }
    111     SDL_free(key);
    112     if(!texture) {
    113         return LV_RES_INV;
    114     }
    115 
    116     lv_area_t zoomed_cords;
    117     _lv_img_buf_get_transformed_area(&zoomed_cords, lv_area_get_width(coords), lv_area_get_height(coords), 0,
    118                                      draw_dsc->zoom, &draw_dsc->pivot);
    119     lv_area_move(&zoomed_cords, coords->x1, coords->y1);
    120 
    121     /* When in > 0, draw simple radius */
    122     lv_coord_t radius = 0;
    123     /* Coords will be translated so coords will start at (0,0) */
    124     lv_area_t t_coords = zoomed_cords, t_clip = *clip, apply_area;
    125 
    126     if(!check_mask_simple_radius(&t_coords, &radius)) {
    127         lv_draw_sdl_composite_begin(ctx, &zoomed_cords, clip, NULL, draw_dsc->blend_mode,
    128                                     &t_coords, &t_clip, &apply_area);
    129     }
    130 
    131     SDL_Rect clip_rect, coords_rect;
    132     lv_area_to_sdl_rect(&t_clip, &clip_rect);
    133     lv_area_to_sdl_rect(&t_coords, &coords_rect);
    134 
    135     SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
    136 
    137     if(radius > 0) {
    138         draw_img_rounded(ctx, texture, header, draw_dsc, &t_coords, &t_clip, radius);
    139     }
    140     else {
    141         draw_img_simple(ctx, texture, header, draw_dsc, &t_coords, &t_clip);
    142     }
    143 
    144     lv_draw_sdl_composite_end(ctx, &apply_area, draw_dsc->blend_mode);
    145 
    146     return LV_RES_OK;
    147 }
    148 
    149 static void calc_draw_part(SDL_Texture * texture, const lv_draw_sdl_img_header_t * header, const lv_area_t * coords,
    150                            const lv_area_t * clip, SDL_Rect * clipped_src, SDL_Rect * clipped_dst)
    151 {
    152     double x = 0, y = 0, w, h;
    153     if(SDL_RectEmpty(&header->rect)) {
    154         Uint32 format = 0;
    155         int access = 0, tw, th;
    156         SDL_QueryTexture(texture, &format, &access, &tw, &th);
    157         w = tw;
    158         h = th;
    159     }
    160     else {
    161         x = header->rect.x;
    162         y = header->rect.y;
    163         w = header->rect.w;
    164         h = header->rect.h;
    165     }
    166     if(clip) {
    167         lv_area_t clipped_area;
    168         _lv_area_intersect(&clipped_area, coords, clip);
    169         lv_area_to_sdl_rect(&clipped_area, clipped_dst);
    170     }
    171     else {
    172         lv_area_to_sdl_rect(coords, clipped_dst);
    173     }
    174     lv_coord_t coords_w = lv_area_get_width(coords), coords_h = lv_area_get_height(coords);
    175     clipped_src->x = (int)(x + (clipped_dst->x - coords->x1) * w / coords_w);
    176     clipped_src->y = (int)(y + (clipped_dst->y - coords->y1) * h / coords_h);
    177     clipped_src->w = (int)(w - (coords_w - clipped_dst->w) * w / coords_w);
    178     clipped_src->h = (int)(h - (coords_h - clipped_dst->h) * h / coords_h);
    179 }
    180 
    181 bool lv_draw_sdl_img_load_texture(lv_draw_sdl_ctx_t * ctx, lv_draw_sdl_cache_key_head_img_t * key, size_t key_size,
    182                                   const void * src, int32_t frame_id, SDL_Texture ** texture,
    183                                   lv_draw_sdl_img_header_t ** header)
    184 {
    185     _lv_img_cache_entry_t * cdsc = _lv_img_cache_open(src, lv_color_white(), frame_id);
    186     lv_draw_sdl_cache_flag_t tex_flags = 0;
    187     SDL_Rect rect;
    188     SDL_memset(&rect, 0, sizeof(SDL_Rect));
    189     if(cdsc) {
    190         lv_img_decoder_dsc_t * dsc = &cdsc->dec_dsc;
    191         if(dsc->user_data && SDL_memcmp(dsc->user_data, LV_DRAW_SDL_DEC_DSC_TEXTURE_HEAD, 8) == 0) {
    192             lv_draw_sdl_dec_dsc_userdata_t * ptr = (lv_draw_sdl_dec_dsc_userdata_t *) dsc->user_data;
    193             *texture = ptr->texture;
    194             rect = ptr->rect;
    195             if(ptr->texture_managed) {
    196                 tex_flags |= LV_DRAW_SDL_CACHE_FLAG_MANAGED;
    197             }
    198             ptr->texture_referenced = true;
    199         }
    200         else {
    201             *texture = upload_img_texture(ctx->renderer, dsc);
    202         }
    203 #if LV_IMG_CACHE_DEF_SIZE == 0
    204         lv_img_decoder_close(dsc);
    205 #endif
    206     }
    207     if(texture && cdsc) {
    208         *header = SDL_malloc(sizeof(lv_draw_sdl_img_header_t));
    209         SDL_memcpy(&(*header)->base, &cdsc->dec_dsc.header, sizeof(lv_img_header_t));
    210         (*header)->rect = rect;
    211         lv_draw_sdl_texture_cache_put_advanced(ctx, key, key_size, *texture, *header, SDL_free, tex_flags);
    212     }
    213     else {
    214         lv_draw_sdl_texture_cache_put(ctx, key, key_size, NULL);
    215         return false;
    216     }
    217     return true;
    218 }
    219 
    220 /**********************
    221  *   STATIC FUNCTIONS
    222  **********************/
    223 
    224 static SDL_Texture * upload_img_texture(SDL_Renderer * renderer, lv_img_decoder_dsc_t * dsc)
    225 {
    226     if(!dsc->img_data) {
    227         return upload_img_texture_fallback(renderer, dsc);
    228     }
    229     bool chroma_keyed = dsc->header.cf == (uint32_t) LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED;
    230     int h = (int) dsc->header.h;
    231     int w = (int) dsc->header.w;
    232     void * data = (void *) dsc->img_data;
    233     Uint32 rmask = 0x00FF0000;
    234     Uint32 gmask = 0x0000FF00;
    235     Uint32 bmask = 0x000000FF;
    236     Uint32 amask = 0xFF000000;
    237     if(chroma_keyed) {
    238         amask = 0x00;
    239     }
    240     SDL_Surface * surface = SDL_CreateRGBSurfaceFrom(data, w, h, LV_COLOR_DEPTH, w * LV_COLOR_DEPTH / 8,
    241                                                      rmask, gmask, bmask, amask);
    242     SDL_SetColorKey(surface, chroma_keyed, lv_color_to32(LV_COLOR_CHROMA_KEY));
    243     SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, surface);
    244     SDL_FreeSurface(surface);
    245     return texture;
    246 }
    247 
    248 static SDL_Texture * upload_img_texture_fallback(SDL_Renderer * renderer, lv_img_decoder_dsc_t * dsc)
    249 {
    250     lv_coord_t h = (lv_coord_t) dsc->header.h;
    251     lv_coord_t w = (lv_coord_t) dsc->header.w;
    252     uint8_t * data = lv_mem_buf_get(w * h * sizeof(lv_color_t));
    253     for(lv_coord_t y = 0; y < h; y++) {
    254         lv_img_decoder_read_line(dsc, 0, y, w, &data[y * w * sizeof(lv_color_t)]);
    255     }
    256     Uint32 rmask = 0x00FF0000;
    257     Uint32 gmask = 0x0000FF00;
    258     Uint32 bmask = 0x000000FF;
    259     Uint32 amask = 0xFF000000;
    260     SDL_Surface * surface = SDL_CreateRGBSurfaceFrom(data, w, h, LV_COLOR_DEPTH, w * LV_COLOR_DEPTH / 8,
    261                                                      rmask, gmask, bmask, amask);
    262     SDL_SetColorKey(surface, SDL_TRUE, lv_color_to32(LV_COLOR_CHROMA_KEY));
    263     SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, surface);
    264     SDL_FreeSurface(surface);
    265     lv_mem_buf_release(data);
    266     return texture;
    267 }
    268 
    269 /**
    270  * Check if there is only one radius mask
    271  * @param radius Set to radius value if the only mask is a radius mask
    272  * @return true if the only mask is a radius mask
    273  */
    274 static bool check_mask_simple_radius(const lv_area_t * coords, lv_coord_t * radius)
    275 {
    276     if(lv_draw_mask_get_cnt() != 1) return false;
    277     for(uint8_t i = 0; i < _LV_MASK_MAX_NUM; i++) {
    278         _lv_draw_mask_common_dsc_t * param = LV_GC_ROOT(_lv_draw_mask_list[i]).param;
    279         if(param->type == LV_DRAW_MASK_TYPE_RADIUS) {
    280             lv_draw_mask_radius_param_t * rparam = (lv_draw_mask_radius_param_t *) param;
    281             if(rparam->cfg.outer) return false;
    282             if(!_lv_area_is_equal(&rparam->cfg.rect, coords)) return false;
    283             *radius = rparam->cfg.radius;
    284             return true;
    285         }
    286     }
    287     return false;
    288 }
    289 
    290 static void draw_img_simple(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture, const lv_draw_sdl_img_header_t * header,
    291                             const lv_draw_img_dsc_t * draw_dsc, const lv_area_t * coords, const lv_area_t * clip)
    292 {
    293     apply_recolor_opa(texture, draw_dsc);
    294     SDL_Point pivot = {.x = draw_dsc->pivot.x, .y = draw_dsc->pivot.y};
    295 
    296     /*Image needs to be rotated, so we have to use clip rect which is slower*/
    297     if(draw_dsc->angle != 0) {
    298         /* No radius, set clip here */
    299         SDL_Rect clip_rect;
    300         lv_area_to_sdl_rect(clip, &clip_rect);
    301         SDL_RenderSetClipRect(ctx->renderer, &clip_rect);
    302     }
    303     SDL_Rect src_rect, dst_rect;
    304     calc_draw_part(texture, header, coords, clip, &src_rect, &dst_rect);
    305     SDL_RenderCopyEx(ctx->renderer, texture, &src_rect, &dst_rect, draw_dsc->angle, &pivot, SDL_FLIP_NONE);
    306     if(draw_dsc->angle != 0) {
    307         SDL_RenderSetClipRect(ctx->renderer, NULL);
    308     }
    309 }
    310 
    311 
    312 static void draw_img_rounded(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture, const lv_draw_sdl_img_header_t * header,
    313                              const lv_draw_img_dsc_t * draw_dsc, const lv_area_t * coords, const lv_area_t * clip,
    314                              lv_coord_t radius)
    315 {
    316     const int w = lv_area_get_width(coords), h = lv_area_get_height(coords);
    317     lv_coord_t real_radius = LV_MIN3(radius, w, h);
    318     SDL_Texture * frag = img_rounded_frag_obtain(ctx, texture, header, w, h, real_radius);
    319     apply_recolor_opa(frag, draw_dsc);
    320     lv_draw_sdl_rect_bg_frag_draw_corners(ctx, frag, real_radius, coords, clip, true);
    321 
    322     apply_recolor_opa(texture, draw_dsc);
    323 
    324     SDL_Rect src_rect, dst_rect;
    325     /* Draw 3 parts */
    326     lv_area_t clip_tmp, part;
    327     calc_draw_part(texture, header, coords, NULL, &src_rect, &dst_rect);
    328     for(int i = w > h ? ROUNDED_IMG_PART_LEFT : ROUNDED_IMG_PART_TOP, j = i + 3; i <= j; i++) {
    329         switch(i) {
    330             case ROUNDED_IMG_PART_LEFT:
    331                 lv_area_set(&part, coords->x1, coords->y1 + radius, coords->x1 + radius - 1, coords->y2 - radius);
    332                 break;
    333             case ROUNDED_IMG_PART_HCENTER:
    334                 lv_area_set(&part, coords->x1 + radius, coords->y1, coords->x2 - radius, coords->y2);
    335                 break;
    336             case ROUNDED_IMG_PART_RIGHT:
    337                 lv_area_set(&part, coords->x2 - radius + 1, coords->y1 + radius, coords->x2, coords->y2 - radius);
    338                 break;
    339             case ROUNDED_IMG_PART_TOP:
    340                 lv_area_set(&part, coords->x1 + radius, coords->y1, coords->x2 - radius, coords->y1 + radius - 1);
    341                 break;
    342             case ROUNDED_IMG_PART_VCENTER:
    343                 lv_area_set(&part, coords->x1 + radius, coords->y2 - radius + 1, coords->x2 - radius, coords->y2);
    344                 break;
    345             case ROUNDED_IMG_PART_BOTTOM:
    346                 lv_area_set(&part, coords->x1, coords->y1 + radius, coords->x2, coords->y2 - radius);
    347                 break;
    348             default:
    349                 break;
    350         }
    351         if(!_lv_area_intersect(&clip_tmp, &part, clip)) continue;
    352         SDL_Rect clip_rect;
    353         lv_area_to_sdl_rect(&clip_tmp, &clip_rect);
    354         SDL_RenderSetClipRect(ctx->renderer, &clip_rect);
    355         SDL_RenderCopy(ctx->renderer, texture, &src_rect, &dst_rect);
    356     }
    357     SDL_RenderSetClipRect(ctx->renderer, NULL);
    358 }
    359 
    360 static void apply_recolor_opa(SDL_Texture * texture, const lv_draw_img_dsc_t * draw_dsc)
    361 {
    362     if(draw_dsc->recolor_opa > LV_OPA_TRANSP) {
    363         /* Draw with mixed recolor */
    364         lv_color_t recolor = lv_color_mix(draw_dsc->recolor, lv_color_white(), draw_dsc->recolor_opa);
    365         SDL_SetTextureColorMod(texture, recolor.ch.red, recolor.ch.green, recolor.ch.blue);
    366     }
    367     else {
    368         /* Draw with no recolor */
    369         SDL_SetTextureColorMod(texture, 0xFF, 0xFF, 0xFF);
    370     }
    371     SDL_SetTextureAlphaMod(texture, draw_dsc->opa);
    372 }
    373 
    374 static SDL_Texture * img_rounded_frag_obtain(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture,
    375                                              const lv_draw_sdl_img_header_t * header, int w, int h, lv_coord_t radius)
    376 {
    377     lv_draw_img_rounded_key_t key = rounded_key_create(texture, w, h, radius);
    378     SDL_Texture * mask_frag = lv_draw_sdl_rect_bg_frag_obtain(ctx, radius);
    379     SDL_Texture * img_frag = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL);
    380     if(img_frag == NULL) {
    381         const lv_coord_t full_frag_size = radius * 2 + 3;
    382         img_frag = SDL_CreateTexture(ctx->renderer, LV_DRAW_SDL_TEXTURE_FORMAT, SDL_TEXTUREACCESS_TARGET,
    383                                      full_frag_size, full_frag_size);
    384         SDL_SetTextureBlendMode(img_frag, SDL_BLENDMODE_BLEND);
    385         SDL_Texture * old_target = SDL_GetRenderTarget(ctx->renderer);
    386         SDL_SetRenderTarget(ctx->renderer, img_frag);
    387         SDL_SetRenderDrawColor(ctx->renderer, 0, 0, 0, 0);
    388         SDL_RenderClear(ctx->renderer);
    389 
    390         lv_area_t coords = {0, 0, w - 1, h - 1}, clip;
    391         lv_area_t frag_coords = {0, 0, full_frag_size - 1, full_frag_size - 1};
    392         lv_draw_sdl_rect_bg_frag_draw_corners(ctx, mask_frag, radius, &frag_coords, NULL, false);
    393 
    394         SDL_SetTextureAlphaMod(texture, 0xFF);
    395         SDL_SetTextureColorMod(texture, 0xFF, 0xFF, 0xFF);
    396 #if LV_GPU_SDL_CUSTOM_BLEND_MODE
    397         SDL_BlendMode blend_mode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO,
    398                                                               SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_DST_ALPHA,
    399                                                               SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD);
    400         SDL_SetTextureBlendMode(texture, blend_mode);
    401 #else
    402         SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_MOD);
    403 #endif
    404         SDL_Rect srcrect, cliprect, dstrect = {0, 0, radius, radius};
    405 
    406         cliprect.w = cliprect.h = radius;
    407         for(int i = 0; i <= ROUNDED_IMG_CORNER_BOTTOM_LEFT; i++) {
    408             switch(i) {
    409                 case ROUNDED_IMG_CORNER_TOP_LEFT:
    410                     cliprect.x = 0;
    411                     cliprect.y = 0;
    412                     lv_area_align(&frag_coords, &coords, LV_ALIGN_TOP_LEFT, 0, 0);
    413                     break;
    414                 case ROUNDED_IMG_CORNER_TOP_RIGHT:
    415                     cliprect.x = full_frag_size - radius;
    416                     cliprect.y = 0;
    417                     lv_area_align(&frag_coords, &coords, LV_ALIGN_TOP_RIGHT, 0, 0);
    418                     break;
    419                 case ROUNDED_IMG_CORNER_BOTTOM_RIGHT:
    420                     cliprect.x = full_frag_size - radius;
    421                     cliprect.y = full_frag_size - radius;
    422                     lv_area_align(&frag_coords, &coords, LV_ALIGN_BOTTOM_RIGHT, 0, 0);
    423                     break;
    424                 case ROUNDED_IMG_CORNER_BOTTOM_LEFT:
    425                     cliprect.x = 0;
    426                     cliprect.y = full_frag_size - radius;
    427                     lv_area_align(&frag_coords, &coords, LV_ALIGN_BOTTOM_LEFT, 0, 0);
    428                     break;
    429                 default:
    430                     break;
    431             }
    432             calc_draw_part(texture, header, &coords, NULL, &srcrect, &dstrect);
    433             SDL_RenderSetClipRect(ctx->renderer, &cliprect);
    434             SDL_RenderCopy(ctx->renderer, texture, &srcrect, &dstrect);
    435         }
    436         SDL_RenderSetClipRect(ctx->renderer, NULL);
    437 
    438         SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
    439 
    440         SDL_SetRenderTarget(ctx->renderer, old_target);
    441         lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), img_frag);
    442     }
    443     return img_frag;
    444 }
    445 
    446 static lv_draw_img_rounded_key_t rounded_key_create(const SDL_Texture * texture, lv_coord_t w, lv_coord_t h,
    447                                                     lv_coord_t radius)
    448 {
    449     lv_draw_img_rounded_key_t key;
    450     SDL_memset(&key, 0, sizeof(key));
    451     key.magic = LV_GPU_CACHE_KEY_MAGIC_IMG_ROUNDED_CORNERS;
    452     key.texture = texture;
    453     key.w = w;
    454     key.h = h;
    455     key.radius = radius;
    456     return key;
    457 }
    458 
    459 #endif /*LV_USE_GPU_SDL*/