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_layer.h"
25 
26 /*********************
27  *      DEFINES
28  *********************/
29 
30 #define FRAG_SPACING 3
31 
32 /**********************
33  *      TYPEDEFS
34  **********************/
35 
36 typedef struct {
37     lv_sdl_cache_key_magic_t magic;
38     lv_coord_t radius;
39     lv_coord_t size;
40 } lv_draw_rect_bg_key_t;
41 
42 typedef struct {
43     lv_sdl_cache_key_magic_t magic;
44     lv_gradient_stop_t stops[LV_GRADIENT_MAX_STOPS];
45     uint8_t stops_count;
46     lv_grad_dir_t dir;
47 } lv_draw_rect_grad_strip_key_t;
48 
49 typedef struct {
50     lv_sdl_cache_key_magic_t magic;
51     lv_gradient_stop_t stops[LV_GRADIENT_MAX_STOPS];
52     uint8_t stops_count;
53     lv_grad_dir_t dir;
54     lv_coord_t w;
55     lv_coord_t h;
56     lv_coord_t radius;
57 } lv_draw_rect_grad_frag_key_t;
58 
59 typedef struct {
60     lv_sdl_cache_key_magic_t magic;
61     lv_coord_t radius;
62     lv_coord_t size;
63     lv_coord_t blur;
64 } lv_draw_rect_shadow_key_t;
65 
66 typedef struct {
67     lv_sdl_cache_key_magic_t magic;
68     lv_coord_t rout, rin;
69     lv_area_t offsets;
70 } lv_draw_rect_border_key_t;
71 
72 /**********************
73  *  STATIC PROTOTYPES
74  **********************/
75 
76 static void draw_bg_color(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
77                           const lv_draw_rect_dsc_t * dsc);
78 
79 static void draw_bg_grad_simple(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
80                                 const lv_grad_dsc_t * grad, bool blend_mod);
81 
82 static void draw_bg_grad_radius(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
83                                 const lv_draw_rect_dsc_t * dsc);
84 
85 static void draw_bg_img(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
86                         const lv_draw_rect_dsc_t * dsc);
87 
88 static void draw_border(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
89                         const lv_draw_rect_dsc_t * dsc);
90 
91 static void draw_shadow(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip,
92                         const lv_draw_rect_dsc_t * dsc);
93 
94 static void draw_outline(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip,
95                          const lv_draw_rect_dsc_t * dsc);
96 
97 static void draw_border_generic(lv_draw_sdl_ctx_t * ctx, const lv_area_t * outer_area, const lv_area_t * inner_area,
98                                 const lv_area_t * clip, lv_coord_t rout, lv_coord_t rin, lv_color_t color, lv_opa_t opa,
99                                 lv_blend_mode_t blend_mode);
100 
101 static void frag_render_borders(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size,
102                                 const lv_area_t * coords, const lv_area_t * clipped, bool full);
103 
104 static void frag_render_center(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size,
105                                const lv_area_t * coords, const lv_area_t * clipped, bool full);
106 
107 static lv_draw_rect_bg_key_t rect_bg_key_create(lv_coord_t radius, lv_coord_t size);
108 
109 static lv_draw_rect_grad_frag_key_t rect_grad_frag_key_create(const lv_grad_dsc_t * grad, lv_coord_t w, lv_coord_t h,
110                                                               lv_coord_t radius);
111 
112 static lv_draw_rect_grad_strip_key_t rect_grad_strip_key_create(const lv_grad_dsc_t * grad);
113 
114 static lv_draw_rect_shadow_key_t rect_shadow_key_create(lv_coord_t radius, lv_coord_t size, lv_coord_t blur);
115 
116 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,
117                                                         const lv_area_t * inner_area);
118 
119 /**********************
120  *  STATIC VARIABLES
121  **********************/
122 
123 /**********************
124  *      MACROS
125  **********************/
126 #define SKIP_BORDER(dsc) ((dsc)->border_opa <= LV_OPA_MIN || (dsc)->border_width == 0 || (dsc)->border_side == LV_BORDER_SIDE_NONE || (dsc)->border_post)
127 #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))
128 #define SKIP_IMAGE(dsc) ((dsc)->bg_img_src == NULL || (dsc)->bg_img_opa <= LV_OPA_MIN)
129 #define SKIP_OUTLINE(dsc) ((dsc)->outline_opa <= LV_OPA_MIN || (dsc)->outline_width == 0)
130 
131 /**********************
132  *   GLOBAL FUNCTIONS
133  **********************/
134 
lv_draw_sdl_draw_rect(lv_draw_ctx_t * draw_ctx,const lv_draw_rect_dsc_t * dsc,const lv_area_t * coords)135 void lv_draw_sdl_draw_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
136 {
137     const lv_area_t * clip = draw_ctx->clip_area;
138     lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
139 
140     lv_area_t extension = {0, 0, 0, 0};
141     if(!SKIP_SHADOW(dsc)) {
142         lv_coord_t ext = (lv_coord_t)(dsc->shadow_spread - dsc->shadow_width / 2 + 1);
143         extension.x1 = LV_MAX(extension.x1, -dsc->shadow_ofs_x + ext);
144         extension.x2 = LV_MAX(extension.x2, dsc->shadow_ofs_x + ext);
145         extension.y1 = LV_MAX(extension.y1, -dsc->shadow_ofs_y + ext);
146         extension.y2 = LV_MAX(extension.y2, dsc->shadow_ofs_y + ext);
147     }
148     if(!SKIP_OUTLINE(dsc)) {
149         lv_coord_t ext = (lv_coord_t)(dsc->outline_pad - 1 + dsc->outline_width);
150         extension.x1 = LV_MAX(extension.x1, ext);
151         extension.x2 = LV_MAX(extension.x2, ext);
152         extension.y1 = LV_MAX(extension.y1, ext);
153         extension.y2 = LV_MAX(extension.y2, ext);
154     }
155     /* Coords will be translated so coords will start at (0,0) */
156     lv_area_t t_coords = *coords, t_clip = *clip, apply_area, t_area;
157     bool has_composite = lv_draw_sdl_composite_begin(ctx, coords, clip, &extension, dsc->blend_mode, &t_coords, &t_clip,
158                                                      &apply_area);
159 
160     lv_draw_sdl_transform_areas_offset(ctx, has_composite, &apply_area, &t_coords, &t_clip);
161 
162     bool has_content = _lv_area_intersect(&t_area, &t_coords, &t_clip);
163 
164     SDL_Rect clip_rect;
165     lv_area_to_sdl_rect(&t_clip, &clip_rect);
166     draw_shadow(ctx, &t_coords, &t_clip, dsc);
167     /* Shadows and outlines will also draw in extended area */
168     if(has_content) {
169         draw_bg_color(ctx, &t_coords, &t_area, dsc);
170         draw_bg_img(ctx, &t_coords, &t_area, dsc);
171         draw_border(ctx, &t_coords, &t_area, dsc);
172     }
173     draw_outline(ctx, &t_coords, &t_clip, dsc);
174 
175     lv_draw_sdl_composite_end(ctx, &apply_area, dsc->blend_mode);
176 }
177 
lv_draw_sdl_rect_bg_frag_obtain(lv_draw_sdl_ctx_t * ctx,lv_coord_t radius,bool * in_cache)178 SDL_Texture * lv_draw_sdl_rect_bg_frag_obtain(lv_draw_sdl_ctx_t * ctx, lv_coord_t radius, bool * in_cache)
179 {
180     lv_draw_rect_bg_key_t key = rect_bg_key_create(radius, radius);
181     SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL);
182     if(texture == NULL) {
183         lv_area_t coords = {0, 0, radius * 2 - 1, radius * 2 - 1};
184         lv_area_t coords_frag = {0, 0, radius - 1, radius - 1};
185         lv_draw_mask_radius_param_t mask_rout_param;
186         lv_draw_mask_radius_init(&mask_rout_param, &coords, radius, false);
187         int16_t mask_id = lv_draw_mask_add(&mask_rout_param, NULL);
188         texture = lv_draw_sdl_mask_dump_texture(ctx->renderer, &coords_frag, &mask_id, 1);
189         SDL_assert(texture != NULL);
190         lv_draw_mask_remove_id(mask_id);
191         *in_cache = lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), texture);
192     }
193     else {
194         *in_cache = true;
195     }
196     return texture;
197 }
198 
lv_draw_sdl_rect_grad_frag_obtain(lv_draw_sdl_ctx_t * ctx,const lv_grad_dsc_t * grad,lv_coord_t w,lv_coord_t h,lv_coord_t radius,bool * in_cache)199 SDL_Texture * lv_draw_sdl_rect_grad_frag_obtain(lv_draw_sdl_ctx_t * ctx, const lv_grad_dsc_t * grad, lv_coord_t w,
200                                                 lv_coord_t h, lv_coord_t radius, bool * in_cache)
201 {
202     lv_draw_rect_grad_frag_key_t key = rect_grad_frag_key_create(grad, w, h, radius);
203     SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL);
204     if(texture == NULL) {
205         lv_area_t coords = {0, 0, radius * 2 + FRAG_SPACING - 1, radius * 2 + FRAG_SPACING - 1};
206         texture = SDL_CreateTexture(ctx->renderer, LV_DRAW_SDL_TEXTURE_FORMAT, SDL_TEXTUREACCESS_TARGET,
207                                     lv_area_get_width(&coords), lv_area_get_height(&coords));
208         SDL_assert(texture != NULL);
209 
210         lv_draw_mask_radius_param_t mask_rout_param;
211         lv_draw_mask_radius_init(&mask_rout_param, &coords, radius, false);
212         int16_t mask_id = lv_draw_mask_add(&mask_rout_param, NULL);
213         SDL_Texture * mask = lv_draw_sdl_mask_dump_texture(ctx->renderer, &coords, &mask_id, 1);
214         SDL_assert(mask != NULL);
215         SDL_SetTextureBlendMode(mask, SDL_BLENDMODE_NONE);
216         lv_draw_mask_remove_id(mask_id);
217 
218         SDL_Texture * target_backup = SDL_GetRenderTarget(ctx->renderer);
219         SDL_SetRenderTarget(ctx->renderer, texture);
220         SDL_RenderCopy(ctx->renderer, mask, NULL, NULL);
221         SDL_DestroyTexture(mask);
222 
223         lv_area_t blend_coords = {.x1 = 0, .y1 = 0, .x2 = w - 1, .y2 = h - 1};
224         lv_area_t draw_area = {.x1 = 0, .y1 = 0, .x2 = radius - 1, .y2 = radius - 1};
225         /* Align to top left */
226         lv_area_align(&coords, &draw_area, LV_ALIGN_TOP_LEFT, 0, 0);
227         lv_area_align(&coords, &blend_coords, LV_ALIGN_TOP_LEFT, 0, 0);
228         draw_bg_grad_simple(ctx, &blend_coords, &draw_area, grad, true);
229 
230         /* Align to top right */
231         lv_area_align(&coords, &draw_area, LV_ALIGN_TOP_RIGHT, 0, 0);
232         lv_area_align(&coords, &blend_coords, LV_ALIGN_TOP_RIGHT, 0, 0);
233         draw_bg_grad_simple(ctx, &blend_coords, &draw_area, grad, true);
234 
235         /* Align to bottom right */
236         lv_area_align(&coords, &draw_area, LV_ALIGN_BOTTOM_RIGHT, 0, 0);
237         lv_area_align(&coords, &blend_coords, LV_ALIGN_BOTTOM_RIGHT, 0, 0);
238         draw_bg_grad_simple(ctx, &blend_coords, &draw_area, grad, true);
239 
240         /* Align to bottom left */
241         lv_area_align(&coords, &draw_area, LV_ALIGN_BOTTOM_LEFT, 0, 0);
242         lv_area_align(&coords, &blend_coords, LV_ALIGN_BOTTOM_LEFT, 0, 0);
243         draw_bg_grad_simple(ctx, &blend_coords, &draw_area, grad, true);
244 
245         SDL_SetRenderTarget(ctx->renderer, target_backup);
246         *in_cache = lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), texture);
247     }
248     else {
249         *in_cache = true;
250     }
251     return texture;
252 }
253 
lv_draw_sdl_rect_grad_strip_obtain(lv_draw_sdl_ctx_t * ctx,const lv_grad_dsc_t * grad,bool * in_cache)254 SDL_Texture * lv_draw_sdl_rect_grad_strip_obtain(lv_draw_sdl_ctx_t * ctx, const lv_grad_dsc_t * grad, bool * in_cache)
255 {
256     lv_draw_rect_grad_strip_key_t key = rect_grad_strip_key_create(grad);
257     SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL);
258     if(texture == NULL) {
259         Uint32 amask = 0xFF000000;
260         Uint32 rmask = 0x00FF0000;
261         Uint32 gmask = 0x0000FF00;
262         Uint32 bmask = 0x000000FF;
263         lv_color_t pixels[256];
264         for(int i = 0; i < 256; i++) {
265             pixels[i] = lv_gradient_calculate(grad, 256, i);
266         }
267         int width = grad->dir == LV_GRAD_DIR_VER ? 1 : 256;
268         int height = grad->dir == LV_GRAD_DIR_VER ? 256 : 1;
269         SDL_Surface * surface = SDL_CreateRGBSurfaceFrom(pixels, width, height, LV_COLOR_DEPTH, width * LV_COLOR_DEPTH / 8,
270                                                          rmask, gmask, bmask, amask);
271         texture = SDL_CreateTextureFromSurface(ctx->renderer, surface);
272         SDL_assert(texture != NULL);
273         SDL_FreeSurface(surface);
274         *in_cache = lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), texture);
275     }
276     else {
277         *in_cache = true;
278     }
279     return texture;
280 }
281 
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)282 void lv_draw_sdl_rect_bg_frag_draw_corners(lv_draw_sdl_ctx_t * ctx, SDL_Texture * frag, lv_coord_t frag_size,
283                                            const lv_area_t * coords, const lv_area_t * clip, bool full)
284 {
285     if(!clip) clip = coords;
286     lv_area_t corner_area, dst_area;
287     /* Upper left */
288     corner_area.x1 = coords->x1;
289     corner_area.y1 = coords->y1;
290     corner_area.x2 = coords->x1 + frag_size - 1;
291     corner_area.y2 = coords->y1 + frag_size - 1;
292     if(_lv_area_intersect(&dst_area, &corner_area, clip)) {
293         SDL_Rect dst_rect;
294         lv_area_to_sdl_rect(&dst_area, &dst_rect);
295 
296         lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area);
297         lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1), sy = (lv_coord_t)(dst_area.y1 - corner_area.y1);
298         SDL_Rect src_rect = {sx, sy, dw, dh};
299         SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect);
300     }
301     /* Upper right, clip right edge if too big */
302     corner_area.x1 = LV_MAX(coords->x2 - frag_size + 1, coords->x1 + frag_size);
303     corner_area.x2 = coords->x2;
304     if(_lv_area_intersect(&dst_area, &corner_area, clip)) {
305         SDL_Rect dst_rect;
306         lv_area_to_sdl_rect(&dst_area, &dst_rect);
307 
308         lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area);
309         if(full) {
310             lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1),
311                        sy = (lv_coord_t)(dst_area.y1 - corner_area.y1);
312             SDL_Rect src_rect = {frag_size + FRAG_SPACING + sx, sy, dw, dh};
313             SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect);
314         }
315         else {
316             SDL_Rect src_rect = {corner_area.x2 - dst_area.x2, dst_area.y1 - corner_area.y1, dw, dh};
317             SDL_RenderCopyEx(ctx->renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_HORIZONTAL);
318         }
319     }
320     /* Lower right, clip bottom edge if too big */
321     corner_area.y1 = LV_MAX(coords->y2 - frag_size + 1, coords->y1 + frag_size);
322     corner_area.y2 = coords->y2;
323     if(_lv_area_intersect(&dst_area, &corner_area, clip)) {
324         SDL_Rect dst_rect;
325         lv_area_to_sdl_rect(&dst_area, &dst_rect);
326 
327         lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area);
328         if(full) {
329             lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1),
330                        sy = (lv_coord_t)(dst_area.y1 - corner_area.y1);
331             SDL_Rect src_rect = {frag_size + FRAG_SPACING + sx, frag_size + FRAG_SPACING + sy, dw, dh};
332             SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect);
333         }
334         else {
335             SDL_Rect src_rect = {corner_area.x2 - dst_area.x2, corner_area.y2 - dst_area.y2, dw, dh};
336             SDL_RenderCopyEx(ctx->renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_HORIZONTAL | SDL_FLIP_VERTICAL);
337         }
338     }
339     /* Lower left, right edge should not be clipped */
340     corner_area.x1 = coords->x1;
341     corner_area.x2 = coords->x1 + frag_size - 1;
342     if(_lv_area_intersect(&dst_area, &corner_area, clip)) {
343         SDL_Rect dst_rect;
344         lv_area_to_sdl_rect(&dst_area, &dst_rect);
345 
346         lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area);
347         if(full) {
348             lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1),
349                        sy = (lv_coord_t)(dst_area.y1 - corner_area.y1);
350             SDL_Rect src_rect = {sx, frag_size + FRAG_SPACING + sy, dw, dh};
351             SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect);
352         }
353         else {
354             SDL_Rect src_rect = {dst_area.x1 - corner_area.x1, corner_area.y2 - dst_area.y2, dw, dh};
355             SDL_RenderCopyEx(ctx->renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_VERTICAL);
356         }
357     }
358 }
359 
360 /**********************
361  *   STATIC FUNCTIONS
362  **********************/
363 
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)364 static void draw_bg_color(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
365                           const lv_draw_rect_dsc_t * dsc)
366 {
367     if(dsc->bg_opa == 0) {
368         return;
369     }
370     lv_coord_t radius = dsc->radius;
371     SDL_Color bg_color;
372     if(dsc->bg_grad.dir == LV_GRAD_DIR_NONE) {
373         lv_color_to_sdl_color(&dsc->bg_color, &bg_color);
374     }
375     else if(dsc->bg_grad.stops_count == 1) {
376         lv_color_to_sdl_color(&dsc->bg_grad.stops[0].color, &bg_color);
377     }
378     else {
379         if(radius <= 0) {
380             draw_bg_grad_simple(ctx, coords, draw_area, &dsc->bg_grad, false);
381         }
382         else {
383             draw_bg_grad_radius(ctx, coords, draw_area, dsc);
384         }
385         return;
386     }
387     if(radius <= 0) {
388         SDL_Rect rect;
389         lv_area_to_sdl_rect(draw_area, &rect);
390         SDL_SetRenderDrawColor(ctx->renderer, bg_color.r, bg_color.g, bg_color.b, dsc->bg_opa);
391         SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_BLEND);
392         SDL_RenderFillRect(ctx->renderer, &rect);
393         return;
394     }
395 
396     /*A small texture with a quarter of the rect is enough*/
397     lv_coord_t bg_w = lv_area_get_width(coords), bg_h = lv_area_get_height(coords);
398     lv_coord_t real_radius = LV_MIN3(bg_w / 2, bg_h / 2, radius);
399     bool texture_in_cache = false;
400     SDL_Texture * texture = lv_draw_sdl_rect_bg_frag_obtain(ctx, real_radius, &texture_in_cache);
401 
402     SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
403     SDL_SetTextureAlphaMod(texture, dsc->bg_opa);
404     SDL_SetTextureColorMod(texture, bg_color.r, bg_color.g, bg_color.b);
405     lv_draw_sdl_rect_bg_frag_draw_corners(ctx, texture, real_radius, coords, draw_area, false);
406     frag_render_borders(ctx->renderer, texture, real_radius, coords, draw_area, false);
407     frag_render_center(ctx->renderer, texture, real_radius, coords, draw_area, false);
408 
409     if(!texture_in_cache) {
410         LV_LOG_WARN("Texture is not cached, this will impact performance.");
411         SDL_DestroyTexture(texture);
412     }
413 }
414 
draw_bg_grad_simple(lv_draw_sdl_ctx_t * ctx,const lv_area_t * coords,const lv_area_t * draw_area,const lv_grad_dsc_t * grad,bool blend_mod)415 static void draw_bg_grad_simple(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
416                                 const lv_grad_dsc_t * grad, bool blend_mod)
417 {
418     SDL_Rect dstrect;
419     lv_area_to_sdl_rect(draw_area, &dstrect);
420     SDL_Rect srcrect;
421     if(grad->dir == LV_GRAD_DIR_VER) {
422         lv_coord_t coords_h = lv_area_get_height(coords);
423         srcrect.x = 0;
424         srcrect.y = (draw_area->y1 - coords->y1) * 255 / coords_h;
425         srcrect.w = 1;
426         srcrect.h = dstrect.h * 256 / coords_h;
427 
428         if(srcrect.y < 0 || srcrect.y > 255) {
429             return;
430         }
431     }
432     else {
433         lv_coord_t coords_w = lv_area_get_width(coords);
434         srcrect.x = (draw_area->x1 - coords->x1) * 255 / coords_w;
435         srcrect.y = 0;
436         srcrect.w = dstrect.w * 256 / coords_w;
437         srcrect.h = 1;
438 
439         if(srcrect.x < 0 || srcrect.x > 255) {
440             return;
441         }
442     }
443 
444     bool grad_texture_in_cache = false;
445     SDL_Texture * grad_texture = lv_draw_sdl_rect_grad_strip_obtain(ctx, grad, &grad_texture_in_cache);
446     if(blend_mod) {
447         SDL_SetTextureBlendMode(grad_texture, SDL_BLENDMODE_MOD);
448     }
449     else {
450         SDL_SetTextureBlendMode(grad_texture, SDL_BLENDMODE_BLEND);
451     }
452 
453     SDL_RenderCopy(ctx->renderer, grad_texture, &srcrect, &dstrect);
454 
455     if(!grad_texture_in_cache) {
456         LV_LOG_WARN("Texture is not cached, this will impact performance.");
457         SDL_DestroyTexture(grad_texture);
458     }
459 }
460 
draw_bg_grad_radius(lv_draw_sdl_ctx_t * ctx,const lv_area_t * coords,const lv_area_t * draw_area,const lv_draw_rect_dsc_t * dsc)461 static void draw_bg_grad_radius(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
462                                 const lv_draw_rect_dsc_t * dsc)
463 {
464     lv_coord_t radius = dsc->radius;
465     /*A small texture with a quarter of the rect is enough*/
466     lv_coord_t bg_w = lv_area_get_width(coords), bg_h = lv_area_get_height(coords);
467     lv_coord_t real_radius = LV_MIN3(bg_w / 2, bg_h / 2, radius);
468     bool grad_texture_in_cache = false;
469     SDL_Texture * grad_texture = lv_draw_sdl_rect_grad_frag_obtain(ctx, &dsc->bg_grad, bg_w, bg_h, radius,
470                                                                    &grad_texture_in_cache);
471     SDL_SetTextureBlendMode(grad_texture, SDL_BLENDMODE_BLEND);
472 
473     lv_draw_sdl_rect_bg_frag_draw_corners(ctx, grad_texture, real_radius, coords, draw_area, true);
474     lv_area_t part_coords;
475     lv_area_t part_area;
476     if(bg_w > radius * 2) {
477         /*Draw left, middle, right*/
478         part_coords.x1 = 0;
479         part_coords.x2 = radius - 1;
480         part_coords.y1 = radius;
481         part_coords.y2 = bg_h - radius - 1;
482 
483         lv_area_align(coords, &part_coords, LV_ALIGN_LEFT_MID, 0, 0);
484         _lv_area_intersect(&part_area, &part_coords, draw_area);
485         draw_bg_grad_simple(ctx, coords, &part_area, &dsc->bg_grad, false);
486 
487         lv_area_align(coords, &part_coords, LV_ALIGN_RIGHT_MID, 0, 0);
488         _lv_area_intersect(&part_area, &part_coords, draw_area);
489         draw_bg_grad_simple(ctx, coords, &part_area, &dsc->bg_grad, false);
490 
491         part_coords.x1 = radius;
492         part_coords.x2 = bg_w - radius - 1;
493         part_coords.y1 = 0;
494         part_coords.y2 = bg_h - 1;
495         lv_area_align(coords, &part_coords, LV_ALIGN_CENTER, 0, 0);
496         _lv_area_intersect(&part_area, &part_coords, draw_area);
497         draw_bg_grad_simple(ctx, coords, &part_area, &dsc->bg_grad, false);
498     }
499     else if(bg_h > radius * 2) {
500         /*Draw top, middle, bottom*/
501         part_coords.x1 = radius;
502         part_coords.x2 = bg_w - radius - 1;
503         part_coords.y1 = 0;
504         part_coords.y2 = radius - 1;
505 
506         lv_area_align(coords, &part_coords, LV_ALIGN_TOP_MID, 0, 0);
507         _lv_area_intersect(&part_area, &part_coords, draw_area);
508         draw_bg_grad_simple(ctx, coords, &part_area, &dsc->bg_grad, false);
509 
510         lv_area_align(coords, &part_coords, LV_ALIGN_BOTTOM_MID, 0, 0);
511         _lv_area_intersect(&part_area, &part_coords, draw_area);
512         draw_bg_grad_simple(ctx, coords, &part_area, &dsc->bg_grad, false);
513 
514         part_coords.x1 = 0;
515         part_coords.x2 = bg_w - 1;
516         part_coords.y1 = radius;
517         part_coords.y2 = bg_h - radius - 1;
518         lv_area_align(coords, &part_coords, LV_ALIGN_CENTER, 0, 0);
519         _lv_area_intersect(&part_area, &part_coords, draw_area);
520         draw_bg_grad_simple(ctx, coords, &part_area, &dsc->bg_grad, false);
521     }
522 
523     if(!grad_texture_in_cache) {
524         LV_LOG_WARN("Texture is not cached, this will impact performance.");
525         SDL_DestroyTexture(grad_texture);
526     }
527 }
528 
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)529 static void draw_bg_img(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
530                         const lv_draw_rect_dsc_t * dsc)
531 {
532     LV_UNUSED(draw_area);
533     if(SKIP_IMAGE(dsc)) return;
534 
535     lv_img_src_t src_type = lv_img_src_get_type(dsc->bg_img_src);
536     if(src_type == LV_IMG_SRC_SYMBOL) {
537         lv_point_t size;
538         lv_txt_get_size(&size, dsc->bg_img_src, dsc->bg_img_symbol_font, 0, 0, LV_COORD_MAX, LV_TEXT_FLAG_NONE);
539         lv_area_t a;
540         a.x1 = coords->x1 + lv_area_get_width(coords) / 2 - size.x / 2;
541         a.x2 = a.x1 + size.x - 1;
542         a.y1 = coords->y1 + lv_area_get_height(coords) / 2 - size.y / 2;
543         a.y2 = a.y1 + size.y - 1;
544 
545         lv_draw_label_dsc_t label_draw_dsc;
546         lv_draw_label_dsc_init(&label_draw_dsc);
547         label_draw_dsc.font = dsc->bg_img_symbol_font;
548         label_draw_dsc.color = dsc->bg_img_recolor;
549         label_draw_dsc.opa = dsc->bg_img_opa;
550         lv_draw_label((lv_draw_ctx_t *) ctx, &label_draw_dsc, &a, dsc->bg_img_src, NULL);
551     }
552     else {
553         lv_img_header_t header;
554         size_t key_size;
555         lv_draw_sdl_cache_key_head_img_t * key = lv_draw_sdl_texture_img_key_create(dsc->bg_img_src, 0, &key_size);
556         bool key_found;
557         lv_img_header_t * cache_header = NULL;
558         SDL_Texture * texture = lv_draw_sdl_texture_cache_get_with_userdata(ctx, key, key_size, &key_found,
559                                                                             (void **) &cache_header);
560         SDL_free(key);
561         if(texture) {
562             header = *cache_header;
563         }
564         else if(key_found || lv_img_decoder_get_info(dsc->bg_img_src, &header) != LV_RES_OK) {
565             /* When cache hit but with negative result, use default decoder. If still fail, return.*/
566             LV_LOG_WARN("Couldn't read the background image");
567             return;
568         }
569 
570         lv_draw_img_dsc_t img_dsc;
571         lv_draw_img_dsc_init(&img_dsc);
572         img_dsc.blend_mode = dsc->blend_mode;
573         img_dsc.recolor = dsc->bg_img_recolor;
574         img_dsc.recolor_opa = dsc->bg_img_recolor_opa;
575         img_dsc.opa = dsc->bg_img_opa;
576         img_dsc.frame_id = 0;
577 
578         int16_t radius_mask_id = LV_MASK_ID_INV;
579         lv_draw_mask_radius_param_t radius_param;
580         if(dsc->radius > 0) {
581             lv_draw_mask_radius_init(&radius_param, coords, dsc->radius, false);
582             radius_mask_id = lv_draw_mask_add(&radius_param, NULL);
583         }
584 
585         /*Center align*/
586         if(dsc->bg_img_tiled == false) {
587             lv_area_t area;
588             area.x1 = coords->x1 + lv_area_get_width(coords) / 2 - header.w / 2;
589             area.y1 = coords->y1 + lv_area_get_height(coords) / 2 - header.h / 2;
590             area.x2 = area.x1 + header.w - 1;
591             area.y2 = area.y1 + header.h - 1;
592 
593             lv_draw_img((lv_draw_ctx_t *) ctx, &img_dsc, &area, dsc->bg_img_src);
594         }
595         else {
596             lv_area_t area;
597             area.y1 = coords->y1;
598             area.y2 = area.y1 + header.h - 1;
599 
600             for(; area.y1 <= coords->y2; area.y1 += header.h, area.y2 += header.h) {
601 
602                 area.x1 = coords->x1;
603                 area.x2 = area.x1 + header.w - 1;
604                 for(; area.x1 <= coords->x2; area.x1 += header.w, area.x2 += header.w) {
605                     lv_draw_img((lv_draw_ctx_t *) ctx, &img_dsc, &area, dsc->bg_img_src);
606                 }
607             }
608         }
609 
610         if(radius_mask_id != LV_MASK_ID_INV) {
611             lv_draw_mask_remove_id(radius_mask_id);
612             lv_draw_mask_free_param(&radius_param);
613         }
614     }
615 }
616 
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)617 static void draw_shadow(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip,
618                         const lv_draw_rect_dsc_t * dsc)
619 {
620     /*Check whether the shadow is visible*/
621     if(SKIP_SHADOW(dsc)) return;
622 
623     lv_coord_t sw = dsc->shadow_width;
624 
625     lv_area_t core_area;
626     core_area.x1 = coords->x1 + dsc->shadow_ofs_x - dsc->shadow_spread;
627     core_area.x2 = coords->x2 + dsc->shadow_ofs_x + dsc->shadow_spread;
628     core_area.y1 = coords->y1 + dsc->shadow_ofs_y - dsc->shadow_spread;
629     core_area.y2 = coords->y2 + dsc->shadow_ofs_y + dsc->shadow_spread;
630 
631     lv_area_t shadow_area;
632     shadow_area.x1 = core_area.x1 - sw / 2 - 1;
633     shadow_area.x2 = core_area.x2 + sw / 2 + 1;
634     shadow_area.y1 = core_area.y1 - sw / 2 - 1;
635     shadow_area.y2 = core_area.y2 + sw / 2 + 1;
636 
637     lv_opa_t opa = dsc->shadow_opa;
638 
639     if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
640 
641     /*Get clipped draw area which is the real draw area.
642      *It is always the same or inside `shadow_area`*/
643     lv_area_t draw_area;
644     if(!_lv_area_intersect(&draw_area, &shadow_area, clip)) return;
645 
646     SDL_Rect core_area_rect;
647     lv_area_to_sdl_rect(&shadow_area, &core_area_rect);
648 
649     lv_coord_t radius = dsc->radius;
650     /* No matter how big the shadow is, what we need is just a corner */
651     lv_coord_t frag_size = LV_MIN3(lv_area_get_width(&core_area) / 2, lv_area_get_height(&core_area) / 2,
652                                    LV_MAX(sw / 2, radius));
653 
654     /* This is how big the corner is after blurring */
655     lv_coord_t blur_growth = (lv_coord_t)(sw / 2 + 1);
656 
657     lv_coord_t blur_frag_size = (lv_coord_t)(frag_size + blur_growth);
658 
659     lv_draw_rect_shadow_key_t key = rect_shadow_key_create(radius, frag_size, sw);
660 
661     SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL);
662     bool texture_in_cache = false;
663     if(texture == NULL) {
664         lv_area_t mask_area = {blur_growth, blur_growth}, mask_area_blurred = {0, 0};
665         lv_area_set_width(&mask_area, frag_size * 2);
666         lv_area_set_height(&mask_area, frag_size * 2);
667         lv_area_set_width(&mask_area_blurred, blur_frag_size * 2);
668         lv_area_set_height(&mask_area_blurred, blur_frag_size * 2);
669 
670         lv_draw_mask_radius_param_t mask_rout_param;
671         lv_draw_mask_radius_init(&mask_rout_param, &mask_area, radius, false);
672         int16_t mask_id = lv_draw_mask_add(&mask_rout_param, NULL);
673         lv_opa_t * mask_buf = lv_draw_sdl_mask_dump_opa(&mask_area_blurred, &mask_id, 1);
674         lv_stack_blur_grayscale(mask_buf, lv_area_get_width(&mask_area_blurred), lv_area_get_height(&mask_area_blurred),
675                                 sw / 2 + sw % 2);
676         texture = lv_sdl_create_opa_texture(ctx->renderer, mask_buf, blur_frag_size, blur_frag_size,
677                                             lv_area_get_width(&mask_area_blurred));
678         lv_mem_buf_release(mask_buf);
679         lv_draw_mask_remove_id(mask_id);
680         SDL_assert(texture);
681         texture_in_cache = lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), texture);
682     }
683     else {
684         texture_in_cache = true;
685     }
686 
687     SDL_Color shadow_color;
688     lv_color_to_sdl_color(&dsc->shadow_color, &shadow_color);
689     SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
690     SDL_SetTextureAlphaMod(texture, opa);
691     SDL_SetTextureColorMod(texture, shadow_color.r, shadow_color.g, shadow_color.b);
692 
693     lv_draw_sdl_rect_bg_frag_draw_corners(ctx, texture, blur_frag_size, &shadow_area, clip, false);
694     frag_render_borders(ctx->renderer, texture, blur_frag_size, &shadow_area, clip, false);
695     frag_render_center(ctx->renderer, texture, blur_frag_size, &shadow_area, clip, false);
696 
697     if(!texture_in_cache) {
698         LV_LOG_WARN("Texture is not cached, this will impact performance.");
699         SDL_DestroyTexture(texture);
700     }
701 }
702 
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)703 static void draw_border(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
704                         const lv_draw_rect_dsc_t * dsc)
705 {
706     if(SKIP_BORDER(dsc)) return;
707 
708     SDL_Color border_color;
709     lv_color_to_sdl_color(&dsc->border_color, &border_color);
710 
711     lv_coord_t coords_w = lv_area_get_width(coords), coords_h = lv_area_get_height(coords);
712     lv_coord_t short_side = LV_MIN(coords_w, coords_h);
713     lv_coord_t rout = LV_MIN(dsc->radius, short_side / 2);/*Get the inner area*/
714     lv_area_t area_inner;
715     lv_area_copy(&area_inner, coords);//        lv_area_increase(&area_inner, 1, 1);
716     area_inner.x1 += ((dsc->border_side & LV_BORDER_SIDE_LEFT) ? dsc->border_width : -(dsc->border_width + rout));
717     area_inner.x2 -= ((dsc->border_side & LV_BORDER_SIDE_RIGHT) ? dsc->border_width : -(dsc->border_width + rout));
718     area_inner.y1 += ((dsc->border_side & LV_BORDER_SIDE_TOP) ? dsc->border_width : -(dsc->border_width + rout));
719     area_inner.y2 -= ((dsc->border_side & LV_BORDER_SIDE_BOTTOM) ? dsc->border_width : -(dsc->border_width + rout));
720     lv_coord_t rin = LV_MAX(rout - dsc->border_width, 0);
721     draw_border_generic(ctx, coords, &area_inner, draw_area, rout, rin, dsc->border_color, dsc->border_opa,
722                         dsc->blend_mode);
723 }
724 
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)725 static void draw_outline(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip,
726                          const lv_draw_rect_dsc_t * dsc)
727 {
728     if(SKIP_OUTLINE(dsc)) return;
729 
730     lv_opa_t opa = dsc->outline_opa;
731 
732     if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
733 
734     /*Get the inner radius*/
735     lv_area_t area_inner;
736     lv_area_copy(&area_inner, coords);
737 
738     /*Bring the outline closer to make sure there is no color bleeding with pad=0*/
739     lv_coord_t pad = dsc->outline_pad - 1;
740     area_inner.x1 -= pad;
741     area_inner.y1 -= pad;
742     area_inner.x2 += pad;
743     area_inner.y2 += pad;
744 
745     lv_area_t area_outer;
746     lv_area_copy(&area_outer, &area_inner);
747 
748     area_outer.x1 -= dsc->outline_width;
749     area_outer.x2 += dsc->outline_width;
750     area_outer.y1 -= dsc->outline_width;
751     area_outer.y2 += dsc->outline_width;
752 
753     lv_area_t draw_area;
754     if(!_lv_area_intersect(&draw_area, &area_outer, clip)) return;
755 
756     int32_t inner_w = lv_area_get_width(&area_inner);
757     int32_t inner_h = lv_area_get_height(&area_inner);
758     lv_coord_t rin = dsc->radius;
759     int32_t short_side = LV_MIN(inner_w, inner_h);
760     if(rin > short_side >> 1) rin = short_side >> 1;
761 
762     lv_coord_t rout = rin + dsc->outline_width;
763 
764     draw_border_generic(ctx, &area_outer, &area_inner, clip, rout, rin, dsc->outline_color, dsc->outline_opa,
765                         dsc->blend_mode);
766 }
767 
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)768 static void draw_border_generic(lv_draw_sdl_ctx_t * ctx, const lv_area_t * outer_area, const lv_area_t * inner_area,
769                                 const lv_area_t * clip, lv_coord_t rout, lv_coord_t rin, lv_color_t color, lv_opa_t opa,
770                                 lv_blend_mode_t blend_mode)
771 {
772     opa = opa >= LV_OPA_COVER ? LV_OPA_COVER : opa;
773 
774     SDL_Renderer * renderer = ctx->renderer;
775 
776     lv_draw_rect_border_key_t key = rect_border_key_create(rout, rin, outer_area, inner_area);
777     lv_coord_t radius = LV_MIN3(rout, lv_area_get_width(outer_area) / 2, lv_area_get_height(outer_area) / 2);
778     lv_coord_t max_side = LV_MAX4(key.offsets.x1, key.offsets.y1, -key.offsets.x2, -key.offsets.y2);
779     lv_coord_t frag_size = LV_MAX(radius, max_side);
780     SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL);
781     bool texture_in_cache;
782     if(texture == NULL) {
783         /* Create a mask texture with size of (frag_size * 2 + FRAG_SPACING) */
784         const lv_area_t frag_area = {0, 0, frag_size * 2 + FRAG_SPACING - 1, frag_size * 2 + FRAG_SPACING - 1};
785 
786         /*Create mask for the outer area*/
787         int16_t mask_ids[2] = {LV_MASK_ID_INV, LV_MASK_ID_INV};
788         lv_draw_mask_radius_param_t mask_rout_param;
789         if(rout > 0) {
790             lv_draw_mask_radius_init(&mask_rout_param, &frag_area, rout, false);
791             mask_ids[0] = lv_draw_mask_add(&mask_rout_param, NULL);
792         }
793 
794         /*Create mask for the inner mask*/
795         if(rin < 0) rin = 0;
796         const lv_area_t frag_inner_area = {frag_area.x1 + key.offsets.x1, frag_area.y1 + key.offsets.y1,
797                                            frag_area.x2 + key.offsets.x2, frag_area.y2 + key.offsets.y2
798                                           };
799         lv_draw_mask_radius_param_t mask_rin_param;
800         lv_draw_mask_radius_init(&mask_rin_param, &frag_inner_area, rin, true);
801         mask_ids[1] = lv_draw_mask_add(&mask_rin_param, NULL);
802 
803         texture = lv_draw_sdl_mask_dump_texture(renderer, &frag_area, mask_ids, 2);
804 
805         lv_draw_mask_remove_id(mask_ids[1]);
806         lv_draw_mask_remove_id(mask_ids[0]);
807         SDL_assert(texture);
808         texture_in_cache = lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), texture);
809     }
810     else {
811         texture_in_cache = true;
812     }
813 
814     SDL_Rect outer_rect;
815     lv_area_to_sdl_rect(outer_area, &outer_rect);
816     SDL_Color color_sdl;
817     lv_color_to_sdl_color(&color, &color_sdl);
818 
819     SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
820     SDL_SetTextureAlphaMod(texture, opa);
821     SDL_SetTextureColorMod(texture, color_sdl.r, color_sdl.g, color_sdl.b);
822 
823     lv_draw_sdl_rect_bg_frag_draw_corners(ctx, texture, frag_size, outer_area, clip, true);
824     frag_render_borders(renderer, texture, frag_size, outer_area, clip, true);
825 
826     if(!texture_in_cache) {
827         LV_LOG_WARN("Texture is not cached, this will impact performance.");
828         SDL_DestroyTexture(texture);
829     }
830 }
831 
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)832 static void frag_render_borders(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size,
833                                 const lv_area_t * coords, const lv_area_t * clipped, bool full)
834 {
835     lv_area_t border_area, dst_area;
836     /* Top border */
837     border_area.x1 = coords->x1 + frag_size;
838     border_area.y1 = coords->y1;
839     border_area.x2 = coords->x2 - frag_size;
840     border_area.y2 = coords->y1 + frag_size - 1;
841     if(_lv_area_intersect(&dst_area, &border_area, clipped)) {
842         SDL_Rect dst_rect;
843         lv_area_to_sdl_rect(&dst_area, &dst_rect);
844 
845         lv_coord_t sy = (lv_coord_t)(dst_area.y1 - border_area.y1);
846         if(full) {
847             SDL_Rect src_rect = {frag_size + 1, sy, 1, lv_area_get_height(&dst_area)};
848             SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
849         }
850         else {
851             SDL_Rect src_rect = {frag_size - 1, sy, 1, lv_area_get_height(&dst_area)};
852             SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
853         }
854     }
855     /* Bottom border */
856     border_area.y1 = LV_MAX(coords->y2 - frag_size + 1, coords->y1 + frag_size);
857     border_area.y2 = coords->y2;
858     if(_lv_area_intersect(&dst_area, &border_area, clipped)) {
859         SDL_Rect dst_rect;
860         lv_area_to_sdl_rect(&dst_area, &dst_rect);
861 
862         lv_coord_t dh = lv_area_get_height(&dst_area);
863         if(full) {
864             lv_coord_t sy = (lv_coord_t)(dst_area.y1 - border_area.y1);
865             SDL_Rect src_rect = {frag_size + 1, frag_size + FRAG_SPACING + sy, 1, dh};
866             SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
867         }
868         else {
869             lv_coord_t sy = (lv_coord_t)(border_area.y2 - dst_area.y2);
870             SDL_Rect src_rect = {frag_size - 1, sy, 1, dh};
871             SDL_RenderCopyEx(renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_VERTICAL);
872         }
873     }
874     /* Left border */
875     border_area.x1 = coords->x1;
876     border_area.y1 = coords->y1 + frag_size;
877     border_area.x2 = coords->x1 + frag_size - 1;
878     border_area.y2 = coords->y2 - frag_size;
879     if(_lv_area_intersect(&dst_area, &border_area, clipped)) {
880         SDL_Rect dst_rect;
881         lv_area_to_sdl_rect(&dst_area, &dst_rect);
882 
883         lv_coord_t dw = lv_area_get_width(&dst_area);
884         lv_coord_t sx = (lv_coord_t)(dst_area.x1 - border_area.x1);
885         if(full) {
886             SDL_Rect src_rect = {sx, frag_size + 1, dw, 1};
887             SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
888         }
889         else {
890             SDL_Rect src_rect = {sx, frag_size - 1, dw, 1};
891             SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
892         }
893     }
894     /* Right border */
895     border_area.x1 = LV_MAX(coords->x2 - frag_size + 1, coords->x1 + frag_size);
896     border_area.x2 = coords->x2;
897     if(_lv_area_intersect(&dst_area, &border_area, clipped)) {
898         SDL_Rect dst_rect;
899         lv_area_to_sdl_rect(&dst_area, &dst_rect);
900 
901         lv_coord_t dw = lv_area_get_width(&dst_area);
902         if(full) {
903             lv_coord_t sx = (lv_coord_t)(dst_area.x1 - border_area.x1);
904             SDL_Rect src_rect = {frag_size + FRAG_SPACING + sx, frag_size + 1, dw, 1};
905             SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
906         }
907         else {
908             lv_coord_t sx = (lv_coord_t)(border_area.x2 - dst_area.x2);
909             SDL_Rect src_rect = {sx, frag_size - 1, dw, 1};
910             SDL_RenderCopyEx(renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_HORIZONTAL);
911         }
912     }
913 }
914 
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)915 static void frag_render_center(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size,
916                                const lv_area_t * coords,
917                                const lv_area_t * clipped, bool full)
918 {
919     lv_area_t center_area = {
920         coords->x1 + frag_size,
921         coords->y1 + frag_size,
922         coords->x2 - frag_size,
923         coords->y2 - frag_size,
924     };
925     if(center_area.x2 < center_area.x1 || center_area.y2 < center_area.y1) return;
926     lv_area_t draw_area;
927     if(!_lv_area_intersect(&draw_area, &center_area, clipped)) {
928         return;
929     }
930     SDL_Rect dst_rect;
931     lv_area_to_sdl_rect(&draw_area, &dst_rect);
932     if(full) {
933         SDL_Rect src_rect = {frag_size, frag_size, 1, 1};
934         SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
935     }
936     else {
937         SDL_Rect src_rect = {frag_size - 1, frag_size - 1, 1, 1};
938         SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
939     }
940 }
941 
rect_bg_key_create(lv_coord_t radius,lv_coord_t size)942 static lv_draw_rect_bg_key_t rect_bg_key_create(lv_coord_t radius, lv_coord_t size)
943 {
944     lv_draw_rect_bg_key_t key;
945     SDL_memset(&key, 0, sizeof(key));
946     key.magic = LV_GPU_CACHE_KEY_MAGIC_RECT_BG;
947     key.radius = radius;
948     key.size = size;
949     return key;
950 }
951 
rect_grad_frag_key_create(const lv_grad_dsc_t * grad,lv_coord_t w,lv_coord_t h,lv_coord_t radius)952 static lv_draw_rect_grad_frag_key_t rect_grad_frag_key_create(const lv_grad_dsc_t * grad, lv_coord_t w, lv_coord_t h,
953                                                               lv_coord_t radius)
954 {
955     lv_draw_rect_grad_frag_key_t key;
956     SDL_memset(&key, 0, sizeof(key));
957     key.magic = LV_GPU_CACHE_KEY_MAGIC_RECT_GRAD;
958     key.stops_count = grad->stops_count;
959     key.dir = grad->dir;
960     for(uint8_t i = 0; i < grad->stops_count; i++) {
961         key.stops[i].frac = grad->stops[i].frac;
962         key.stops[i].color = grad->stops[i].color;
963     }
964     key.w = w;
965     key.h = h;
966     key.radius = radius;
967     return key;
968 }
969 
rect_grad_strip_key_create(const lv_grad_dsc_t * grad)970 static lv_draw_rect_grad_strip_key_t rect_grad_strip_key_create(const lv_grad_dsc_t * grad)
971 {
972     lv_draw_rect_grad_strip_key_t key;
973     SDL_memset(&key, 0, sizeof(key));
974     key.magic = LV_GPU_CACHE_KEY_MAGIC_RECT_GRAD;
975     key.stops_count = grad->stops_count;
976     key.dir = grad->dir;
977     for(uint8_t i = 0; i < grad->stops_count; i++) {
978         key.stops[i].frac = grad->stops[i].frac;
979         key.stops[i].color = grad->stops[i].color;
980     }
981     return key;
982 }
983 
rect_shadow_key_create(lv_coord_t radius,lv_coord_t size,lv_coord_t blur)984 static lv_draw_rect_shadow_key_t rect_shadow_key_create(lv_coord_t radius, lv_coord_t size, lv_coord_t blur)
985 {
986     lv_draw_rect_shadow_key_t key;
987     SDL_memset(&key, 0, sizeof(key));
988     key.magic = LV_GPU_CACHE_KEY_MAGIC_RECT_SHADOW;
989     key.radius = radius;
990     key.size = size;
991     key.blur = blur;
992     return key;
993 }
994 
rect_border_key_create(lv_coord_t rout,lv_coord_t rin,const lv_area_t * outer_area,const lv_area_t * inner_area)995 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,
996                                                         const lv_area_t * inner_area)
997 {
998     lv_draw_rect_border_key_t key;
999     /* VERY IMPORTANT! Padding between members is uninitialized, so we have to wipe them manually */
1000     SDL_memset(&key, 0, sizeof(key));
1001     key.magic = LV_GPU_CACHE_KEY_MAGIC_RECT_BORDER;
1002     key.rout = rout;
1003     key.rin = rin;
1004     key.offsets.x1 = inner_area->x1 - outer_area->x1;
1005     key.offsets.x2 = inner_area->x2 - outer_area->x2;
1006     key.offsets.y1 = inner_area->y1 - outer_area->y1;
1007     key.offsets.y2 = inner_area->y2 - outer_area->y2;
1008     return key;
1009 }
1010 
1011 #endif /*LV_USE_GPU_SDL*/
1012