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