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