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