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_composite.c (9959B)

      1 /**
      2  * @file lv_draw_sdl_composite.c
      3  *
      4  */
      5 
      6 /*********************
      7  *      INCLUDES
      8  *********************/
      9 #include "../../lv_conf_internal.h"
     10 
     11 #if LV_USE_GPU_SDL
     12 
     13 #include "../../misc/lv_gc.h"
     14 #include "../../core/lv_refr.h"
     15 #include "lv_draw_sdl_composite.h"
     16 #include "lv_draw_sdl_mask.h"
     17 #include "lv_draw_sdl_utils.h"
     18 #include "lv_draw_sdl_priv.h"
     19 #include "lv_draw_sdl_texture_cache.h"
     20 
     21 /*********************
     22  *      DEFINES
     23  *********************/
     24 
     25 /**********************
     26  *      TYPEDEFS
     27  **********************/
     28 
     29 typedef struct {
     30     lv_sdl_cache_key_magic_t magic;
     31     lv_draw_sdl_composite_texture_id_t type;
     32 } composite_key_t;
     33 
     34 /**********************
     35  *  STATIC PROTOTYPES
     36  **********************/
     37 
     38 static composite_key_t mask_key_create(lv_draw_sdl_composite_texture_id_t type);
     39 
     40 static lv_coord_t next_pow_of_2(lv_coord_t num);
     41 
     42 static void dump_masks(SDL_Texture * texture, const lv_area_t * coords);
     43 /**********************
     44  *  STATIC VARIABLES
     45  **********************/
     46 
     47 /**********************
     48  *      MACROS
     49  **********************/
     50 
     51 /**********************
     52  *   GLOBAL FUNCTIONS
     53  **********************/
     54 
     55 bool lv_draw_sdl_composite_begin(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords_in, const lv_area_t * clip_in,
     56                                  const lv_area_t * extension, lv_blend_mode_t blend_mode, lv_area_t * coords_out,
     57                                  lv_area_t * clip_out, lv_area_t * apply_area)
     58 {
     59     lv_area_t full_coords = *coords_in;
     60 
     61     /* Normalize full_coords */
     62     if(full_coords.x1 > full_coords.x2) {
     63         lv_coord_t x2 = full_coords.x2;
     64         full_coords.x2 = full_coords.x1;
     65         full_coords.x1 = x2;
     66     }
     67     if(full_coords.y1 > full_coords.y2) {
     68         lv_coord_t y2 = full_coords.y2;
     69         full_coords.y2 = full_coords.y1;
     70         full_coords.y1 = y2;
     71     }
     72 
     73     if(extension) {
     74         full_coords.x1 -= extension->x1;
     75         full_coords.x2 += extension->x2;
     76         full_coords.y1 -= extension->y1;
     77         full_coords.y2 += extension->y2;
     78     }
     79 
     80     if(!_lv_area_intersect(apply_area, &full_coords, clip_in)) return false;
     81     bool has_mask = lv_draw_mask_is_any(apply_area);
     82 
     83     const bool draw_mask = has_mask && LV_GPU_SDL_CUSTOM_BLEND_MODE;
     84     const bool draw_blend = blend_mode != LV_BLEND_MODE_NORMAL;
     85     if(draw_mask || draw_blend) {
     86         lv_draw_sdl_context_internals_t * internals = ctx->internals;
     87         LV_ASSERT(internals->mask == NULL && internals->composition == NULL);
     88 
     89         lv_coord_t w = lv_area_get_width(apply_area), h = lv_area_get_height(apply_area);
     90         internals->composition = lv_draw_sdl_composite_texture_obtain(ctx, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET0, w, h);
     91         /* Don't need to worry about overflow */
     92         lv_coord_t ofs_x = (lv_coord_t) - apply_area->x1, ofs_y = (lv_coord_t) - apply_area->y1;
     93         /* Offset draw area to start with (0,0) of coords */
     94         lv_area_move(coords_out, ofs_x, ofs_y);
     95         lv_area_move(clip_out, ofs_x, ofs_y);
     96         SDL_SetRenderTarget(ctx->renderer, internals->composition);
     97         SDL_SetRenderDrawColor(ctx->renderer, 255, 255, 255, 0);
     98         SDL_RenderClear(ctx->renderer);
     99 #if LV_GPU_SDL_CUSTOM_BLEND_MODE
    100         internals->mask = lv_draw_sdl_composite_texture_obtain(ctx, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_STREAM0, w, h);
    101         dump_masks(internals->mask, apply_area);
    102 #endif
    103     }
    104     else if(has_mask) {
    105         /* Fallback mask handling. This will at least make bars looks less bad */
    106         for(uint8_t i = 0; i < _LV_MASK_MAX_NUM; i++) {
    107             _lv_draw_mask_common_dsc_t * comm_param = LV_GC_ROOT(_lv_draw_mask_list[i]).param;
    108             if(comm_param == NULL) continue;
    109             switch(comm_param->type) {
    110                 case LV_DRAW_MASK_TYPE_RADIUS: {
    111                         const lv_draw_mask_radius_param_t * param = (const lv_draw_mask_radius_param_t *) comm_param;
    112                         if(param->cfg.outer) break;
    113                         _lv_area_intersect(clip_out, apply_area, &param->cfg.rect);
    114                         break;
    115                     }
    116                 default:
    117                     break;
    118             }
    119         }
    120     }
    121     return has_mask;
    122 }
    123 
    124 void lv_draw_sdl_composite_end(lv_draw_sdl_ctx_t * ctx, const lv_area_t * apply_area, lv_blend_mode_t blend_mode)
    125 {
    126     lv_draw_sdl_context_internals_t * internals = ctx->internals;
    127     SDL_Rect src_rect = {0, 0, lv_area_get_width(apply_area), lv_area_get_height(apply_area)};
    128 #if LV_GPU_SDL_CUSTOM_BLEND_MODE
    129     if(internals->mask) {
    130         SDL_BlendMode mode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE,
    131                                                         SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ZERO,
    132                                                         SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDOPERATION_ADD);
    133         SDL_SetTextureBlendMode(internals->mask, mode);
    134         SDL_RenderCopy(ctx->renderer, internals->mask, &src_rect, &src_rect);
    135     }
    136 #endif
    137 
    138     /* Shapes are drawn on composite layer when mask or blend mode is present */
    139     if(internals->composition) {
    140         SDL_Rect dst_rect;
    141         lv_area_to_sdl_rect(apply_area, &dst_rect);
    142 
    143         SDL_SetRenderTarget(ctx->renderer, ctx->base_draw.buf);
    144         switch(blend_mode) {
    145             case LV_BLEND_MODE_NORMAL:
    146                 SDL_SetTextureBlendMode(internals->composition, SDL_BLENDMODE_BLEND);
    147                 break;
    148             case LV_BLEND_MODE_ADDITIVE:
    149                 SDL_SetTextureBlendMode(internals->composition, SDL_BLENDMODE_ADD);
    150                 break;
    151 #if LV_GPU_SDL_CUSTOM_BLEND_MODE
    152             case LV_BLEND_MODE_SUBTRACTIVE: {
    153                     SDL_BlendMode mode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE,
    154                                                                     SDL_BLENDOPERATION_SUBTRACT, SDL_BLENDFACTOR_ONE,
    155                                                                     SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_SUBTRACT);
    156                     SDL_SetTextureBlendMode(internals->composition, mode);
    157                     break;
    158                 }
    159             case LV_BLEND_MODE_MULTIPLY: {
    160                     SDL_BlendMode mode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_SRC_COLOR,
    161                                                                     SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ZERO,
    162                                                                     SDL_BLENDFACTOR_DST_ALPHA, SDL_BLENDOPERATION_ADD);
    163                     SDL_SetTextureBlendMode(internals->composition, mode);
    164                     break;
    165                 }
    166 #endif
    167             default:
    168                 LV_LOG_WARN("Doesn't support blend mode %d", blend_mode);
    169                 SDL_SetTextureBlendMode(internals->composition, SDL_BLENDMODE_BLEND);
    170                 /* Unsupported yet */
    171                 break;
    172         }
    173         SDL_RenderCopy(ctx->renderer, internals->composition, &src_rect, &dst_rect);
    174     }
    175 
    176     internals->mask = internals->composition = NULL;
    177 }
    178 
    179 SDL_Texture * lv_draw_sdl_composite_texture_obtain(lv_draw_sdl_ctx_t * ctx, lv_draw_sdl_composite_texture_id_t id,
    180                                                    lv_coord_t w, lv_coord_t h)
    181 {
    182     lv_point_t * tex_size = NULL;
    183     composite_key_t mask_key = mask_key_create(id);
    184     SDL_Texture * result = lv_draw_sdl_texture_cache_get_with_userdata(ctx, &mask_key, sizeof(composite_key_t), NULL,
    185                                                                        (void **) &tex_size);
    186     if(!result || tex_size->x < w || tex_size->y < h) {
    187         lv_coord_t size = next_pow_of_2(LV_MAX(w, h));
    188         int access = SDL_TEXTUREACCESS_STREAMING;
    189         if(id >= LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET0) {
    190             access = SDL_TEXTUREACCESS_TARGET;
    191         }
    192         result = SDL_CreateTexture(ctx->renderer, LV_DRAW_SDL_TEXTURE_FORMAT, access, size, size);
    193         tex_size = lv_mem_alloc(sizeof(lv_point_t));
    194         tex_size->x = tex_size->y = size;
    195         lv_draw_sdl_texture_cache_put_advanced(ctx, &mask_key, sizeof(composite_key_t), result, tex_size, lv_mem_free, 0);
    196     }
    197     return result;
    198 }
    199 
    200 /**********************
    201  *   STATIC FUNCTIONS
    202  **********************/
    203 
    204 static composite_key_t mask_key_create(lv_draw_sdl_composite_texture_id_t type)
    205 {
    206     composite_key_t key;
    207     /* VERY IMPORTANT! Padding between members is uninitialized, so we have to wipe them manually */
    208     SDL_memset(&key, 0, sizeof(key));
    209     key.magic = LV_GPU_CACHE_KEY_MAGIC_MASK;
    210     key.type = type;
    211     return key;
    212 }
    213 
    214 static lv_coord_t next_pow_of_2(lv_coord_t num)
    215 {
    216     lv_coord_t n = 128;
    217     while(n < num && n < 16384) {
    218         n = n << 1;
    219     }
    220     return n;
    221 }
    222 
    223 static void dump_masks(SDL_Texture * texture, const lv_area_t * coords)
    224 {
    225     lv_coord_t w = lv_area_get_width(coords), h = lv_area_get_height(coords);
    226     SDL_assert(w > 0 && h > 0);
    227     SDL_Rect rect = {0, 0, w, h};
    228     uint8_t * pixels;
    229     int pitch;
    230     if(SDL_LockTexture(texture, &rect, (void **) &pixels, &pitch) != 0) return;
    231 
    232     lv_opa_t * line_buf = lv_mem_buf_get(rect.w);
    233     for(lv_coord_t y = 0; y < rect.h; y++) {
    234         lv_memset_ff(line_buf, rect.w);
    235         lv_coord_t abs_x = (lv_coord_t) coords->x1, abs_y = (lv_coord_t)(y + coords->y1), len = (lv_coord_t) rect.w;
    236         lv_draw_mask_res_t res;
    237         res = lv_draw_mask_apply(line_buf, abs_x, abs_y, len);
    238         if(res == LV_DRAW_MASK_RES_TRANSP) {
    239             lv_memset_00(&pixels[y * pitch], 4 * rect.w);
    240         }
    241         else if(res == LV_DRAW_MASK_RES_FULL_COVER) {
    242             lv_memset_ff(&pixels[y * pitch], 4 * rect.w);
    243         }
    244         else {
    245             for(int x = 0; x < rect.w; x++) {
    246                 const size_t idx = y * pitch + x * 4;
    247                 pixels[idx] = line_buf[x];
    248                 pixels[idx + 1] = pixels[idx + 2] = pixels[idx + 3] = 0xFF;
    249             }
    250         }
    251     }
    252     lv_mem_buf_release(line_buf);
    253     SDL_UnlockTexture(texture);
    254 }
    255 
    256 #endif /*LV_USE_GPU_SDL*/