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_hal_disp.c (22696B)

      1 /**
      2  * @file lv_hal_disp.c
      3  *
      4  * @description HAL layer for display driver
      5  *
      6  */
      7 
      8 /*********************
      9  *      INCLUDES
     10  *********************/
     11 #include <stdint.h>
     12 #include <stddef.h>
     13 #include "lv_hal.h"
     14 #include "../misc/lv_mem.h"
     15 #include "../misc/lv_gc.h"
     16 #include "../misc/lv_assert.h"
     17 #include "../core/lv_obj.h"
     18 #include "../core/lv_refr.h"
     19 #include "../core/lv_theme.h"
     20 #include "../draw/sdl/lv_draw_sdl.h"
     21 #include "../draw/sw/lv_draw_sw.h"
     22 #include "../draw/sdl/lv_draw_sdl.h"
     23 #include "../draw/stm32_dma2d/lv_gpu_stm32_dma2d.h"
     24 #include "../draw/arm2d/lv_gpu_arm2d.h"
     25 
     26 #if LV_USE_THEME_DEFAULT
     27     #include "../extra/themes/default/lv_theme_default.h"
     28 #endif
     29 
     30 /*********************
     31  *      DEFINES
     32  *********************/
     33 
     34 /**********************
     35  *      TYPEDEFS
     36  **********************/
     37 
     38 /**********************
     39  *  STATIC PROTOTYPES
     40  **********************/
     41 static lv_obj_tree_walk_res_t invalidate_layout_cb(lv_obj_t * obj, void * user_data);
     42 
     43 static void set_px_true_color_alpha(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x,
     44                                     lv_coord_t y,
     45                                     lv_color_t color, lv_opa_t opa);
     46 
     47 static void set_px_cb_alpha1(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y,
     48                              lv_color_t color, lv_opa_t opa);
     49 
     50 static void set_px_cb_alpha2(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y,
     51                              lv_color_t color, lv_opa_t opa);
     52 
     53 static void set_px_cb_alpha4(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y,
     54                              lv_color_t color, lv_opa_t opa);
     55 
     56 static void set_px_cb_alpha8(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y,
     57                              lv_color_t color, lv_opa_t opa);
     58 
     59 static void set_px_alpha_generic(lv_img_dsc_t * d, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa);
     60 
     61 /**********************
     62  *  STATIC VARIABLES
     63  **********************/
     64 static lv_disp_t * disp_def;
     65 
     66 /**********************
     67  *      MACROS
     68  **********************/
     69 
     70 /**********************
     71  *   GLOBAL FUNCTIONS
     72  **********************/
     73 
     74 /**
     75  * Initialize a display driver with default values.
     76  * It is used to surly have known values in the fields ant not memory junk.
     77  * After it you can set the fields.
     78  * @param driver pointer to driver variable to initialize
     79  */
     80 void lv_disp_drv_init(lv_disp_drv_t * driver)
     81 {
     82     lv_memset_00(driver, sizeof(lv_disp_drv_t));
     83 
     84     driver->hor_res          = 320;
     85     driver->ver_res          = 240;
     86     driver->physical_hor_res = -1;
     87     driver->physical_ver_res = -1;
     88     driver->offset_x         = 0;
     89     driver->offset_y         = 0;
     90     driver->antialiasing     = LV_COLOR_DEPTH > 8 ? 1 : 0;
     91     driver->screen_transp    = LV_COLOR_SCREEN_TRANSP;
     92     driver->dpi              = LV_DPI_DEF;
     93     driver->color_chroma_key = LV_COLOR_CHROMA_KEY;
     94 
     95 
     96 #if LV_USE_GPU_STM32_DMA2D
     97     driver->draw_ctx_init = lv_draw_stm32_dma2d_ctx_init;
     98     driver->draw_ctx_deinit = lv_draw_stm32_dma2d_ctx_init;
     99     driver->draw_ctx_size = sizeof(lv_draw_stm32_dma2d_ctx_t);
    100 #elif LV_USE_GPU_NXP_PXP
    101     driver->draw_ctx_init = lv_draw_nxp_pxp_init;
    102     driver->draw_ctx_deinit = lv_draw_nxp_pxp_init;
    103     driver->draw_ctx_size = sizeof(lv_draw_nxp_pxp_t);
    104 #elif LV_USE_GPU_NXP_VG_LITE
    105     driver->draw_ctx_init = lv_draw_nxp_vglite_init;
    106     driver->draw_ctx_deinit = lv_draw_nxp_vglite_init;
    107     driver->draw_ctx_size = sizeof(lv_draw_nxp_vglite_t);
    108 #elif LV_USE_GPU_SDL
    109     driver->draw_ctx_init = lv_draw_sdl_init_ctx;
    110     driver->draw_ctx_deinit = lv_draw_sdl_deinit_ctx;
    111     driver->draw_ctx_size = sizeof(lv_draw_sdl_ctx_t);
    112 #elif LV_USE_GPU_ARM2D
    113     driver->draw_ctx_init = lv_draw_arm2d_ctx_init;
    114     driver->draw_ctx_deinit = lv_draw_arm2d_ctx_init;
    115     driver->draw_ctx_size = sizeof(lv_draw_arm2d_ctx_t);
    116 #else
    117     driver->draw_ctx_init = lv_draw_sw_init_ctx;
    118     driver->draw_ctx_deinit = lv_draw_sw_init_ctx;
    119     driver->draw_ctx_size = sizeof(lv_draw_sw_ctx_t);
    120 #endif
    121 
    122 }
    123 
    124 /**
    125  * Initialize a display buffer
    126  * @param draw_buf pointer `lv_disp_draw_buf_t` variable to initialize
    127  * @param buf1 A buffer to be used by LVGL to draw the image.
    128  *             Always has to specified and can't be NULL.
    129  *             Can be an array allocated by the user. E.g. `static lv_color_t disp_buf1[1024 * 10]`
    130  *             Or a memory address e.g. in external SRAM
    131  * @param buf2 Optionally specify a second buffer to make image rendering and image flushing
    132  *             (sending to the display) parallel.
    133  *             In the `disp_drv->flush` you should use DMA or similar hardware to send
    134  *             the image to the display in the background.
    135  *             It lets LVGL to render next frame into the other buffer while previous is being
    136  * sent. Set to `NULL` if unused.
    137  * @param size_in_px_cnt size of the `buf1` and `buf2` in pixel count.
    138  */
    139 void lv_disp_draw_buf_init(lv_disp_draw_buf_t * draw_buf, void * buf1, void * buf2, uint32_t size_in_px_cnt)
    140 {
    141     lv_memset_00(draw_buf, sizeof(lv_disp_draw_buf_t));
    142 
    143     draw_buf->buf1    = buf1;
    144     draw_buf->buf2    = buf2;
    145     draw_buf->buf_act = draw_buf->buf1;
    146     draw_buf->size    = size_in_px_cnt;
    147 }
    148 
    149 /**
    150  * Register an initialized display driver.
    151  * Automatically set the first display as active.
    152  * @param driver pointer to an initialized 'lv_disp_drv_t' variable. Only its pointer is saved!
    153  * @return pointer to the new display or NULL on error
    154  */
    155 lv_disp_t * lv_disp_drv_register(lv_disp_drv_t * driver)
    156 {
    157     lv_disp_t * disp = _lv_ll_ins_head(&LV_GC_ROOT(_lv_disp_ll));
    158     LV_ASSERT_MALLOC(disp);
    159     if(!disp) {
    160         return NULL;
    161     }
    162 
    163     /*Create a draw context if not created yet*/
    164     if(driver->draw_ctx == NULL) {
    165         lv_draw_ctx_t * draw_ctx = lv_mem_alloc(driver->draw_ctx_size);
    166         LV_ASSERT_MALLOC(draw_ctx);
    167         if(draw_ctx == NULL) return NULL;
    168         driver->draw_ctx_init(driver, draw_ctx);
    169         driver->draw_ctx = draw_ctx;
    170     }
    171 
    172     lv_memset_00(disp, sizeof(lv_disp_t));
    173 
    174     disp->driver = driver;
    175 
    176     lv_disp_t * disp_def_tmp = disp_def;
    177     disp_def                 = disp; /*Temporarily change the default screen to create the default screens on the
    178                                         new display*/
    179     /*Create a refresh timer*/
    180     disp->refr_timer = lv_timer_create(_lv_disp_refr_timer, LV_DISP_DEF_REFR_PERIOD, disp);
    181     LV_ASSERT_MALLOC(disp->refr_timer);
    182     if(disp->refr_timer == NULL) {
    183         lv_mem_free(disp);
    184         return NULL;
    185     }
    186 
    187     if(driver->full_refresh && driver->draw_buf->size < (uint32_t)driver->hor_res * driver->ver_res) {
    188         driver->full_refresh = 0;
    189         LV_LOG_WARN("full_refresh requires at least screen sized draw buffer(s)");
    190     }
    191 
    192     disp->bg_color = lv_color_white();
    193 #if LV_COLOR_SCREEN_TRANSP
    194     disp->bg_opa = LV_OPA_TRANSP;
    195 #else
    196     disp->bg_opa = LV_OPA_COVER;
    197 #endif
    198 
    199 #if LV_USE_THEME_DEFAULT
    200     if(lv_theme_default_is_inited() == false) {
    201         disp->theme = lv_theme_default_init(disp, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED),
    202                                             LV_THEME_DEFAULT_DARK, LV_FONT_DEFAULT);
    203     }
    204     else {
    205         disp->theme = lv_theme_default_get();
    206     }
    207 #endif
    208 
    209     disp->act_scr   = lv_obj_create(NULL); /*Create a default screen on the display*/
    210     disp->top_layer = lv_obj_create(NULL); /*Create top layer on the display*/
    211     disp->sys_layer = lv_obj_create(NULL); /*Create sys layer on the display*/
    212     lv_obj_remove_style_all(disp->top_layer);
    213     lv_obj_remove_style_all(disp->sys_layer);
    214     lv_obj_clear_flag(disp->top_layer, LV_OBJ_FLAG_CLICKABLE);
    215     lv_obj_clear_flag(disp->sys_layer, LV_OBJ_FLAG_CLICKABLE);
    216 
    217     lv_obj_set_scrollbar_mode(disp->top_layer, LV_SCROLLBAR_MODE_OFF);
    218     lv_obj_set_scrollbar_mode(disp->sys_layer, LV_SCROLLBAR_MODE_OFF);
    219 
    220     lv_obj_invalidate(disp->act_scr);
    221 
    222     disp_def = disp_def_tmp; /*Revert the default display*/
    223     if(disp_def == NULL) disp_def = disp; /*Initialize the default display*/
    224 
    225     lv_timer_ready(disp->refr_timer); /*Be sure the screen will be refreshed immediately on start up*/
    226 
    227     return disp;
    228 }
    229 
    230 /**
    231  * Update the driver in run time.
    232  * @param disp pointer to a display. (return value of `lv_disp_drv_register`)
    233  * @param new_drv pointer to the new driver
    234  */
    235 void lv_disp_drv_update(lv_disp_t * disp, lv_disp_drv_t * new_drv)
    236 {
    237     disp->driver = new_drv;
    238 
    239     if(disp->driver->full_refresh &&
    240        disp->driver->draw_buf->size < (uint32_t)disp->driver->hor_res * disp->driver->ver_res) {
    241         disp->driver->full_refresh = 0;
    242         LV_LOG_WARN("full_refresh requires at least screen sized draw buffer(s)");
    243     }
    244 
    245     lv_coord_t w = lv_disp_get_hor_res(disp);
    246     lv_coord_t h = lv_disp_get_ver_res(disp);
    247     uint32_t i;
    248     for(i = 0; i < disp->screen_cnt; i++) {
    249         lv_area_t prev_coords;
    250         lv_obj_get_coords(disp->screens[i], &prev_coords);
    251         lv_area_set_width(&disp->screens[i]->coords, w);
    252         lv_area_set_height(&disp->screens[i]->coords, h);
    253         lv_event_send(disp->screens[i], LV_EVENT_SIZE_CHANGED, &prev_coords);
    254     }
    255 
    256     /*
    257      * This method is usually called upon orientation change, thus the screen is now a
    258      * different size.
    259      * The object invalidated its previous area. That area is now out of the screen area
    260      * so we reset all invalidated areas and invalidate the active screen's new area only.
    261      */
    262     lv_memset_00(disp->inv_areas, sizeof(disp->inv_areas));
    263     lv_memset_00(disp->inv_area_joined, sizeof(disp->inv_area_joined));
    264     disp->inv_p = 0;
    265     if(disp->act_scr != NULL) lv_obj_invalidate(disp->act_scr);
    266 
    267     lv_obj_tree_walk(NULL, invalidate_layout_cb, NULL);
    268 
    269     if(disp->driver->drv_update_cb) disp->driver->drv_update_cb(disp->driver);
    270 }
    271 
    272 /**
    273  * Remove a display
    274  * @param disp pointer to display
    275  */
    276 void lv_disp_remove(lv_disp_t * disp)
    277 {
    278     bool was_default = false;
    279     if(disp == lv_disp_get_default()) was_default = true;
    280 
    281     /*Detach the input devices*/
    282     lv_indev_t * indev;
    283     indev = lv_indev_get_next(NULL);
    284     while(indev) {
    285         if(indev->driver->disp == disp) {
    286             indev->driver->disp = NULL;
    287         }
    288         indev = lv_indev_get_next(indev);
    289     }
    290 
    291     /** delete screen and other obj */
    292     if(disp->sys_layer) {
    293         lv_obj_del(disp->sys_layer);
    294         disp->sys_layer = NULL;
    295     }
    296     if(disp->top_layer) {
    297         lv_obj_del(disp->top_layer);
    298         disp->top_layer = NULL;
    299     }
    300     while(disp->screen_cnt != 0) {
    301         /*Delete the screenst*/
    302         lv_obj_del(disp->screens[0]);
    303     }
    304 
    305     _lv_ll_remove(&LV_GC_ROOT(_lv_disp_ll), disp);
    306     if(disp->refr_timer) lv_timer_del(disp->refr_timer);
    307     lv_mem_free(disp);
    308 
    309     if(was_default) lv_disp_set_default(_lv_ll_get_head(&LV_GC_ROOT(_lv_disp_ll)));
    310 }
    311 
    312 /**
    313  * Set a default display. The new screens will be created on it by default.
    314  * @param disp pointer to a display
    315  */
    316 void lv_disp_set_default(lv_disp_t * disp)
    317 {
    318     disp_def = disp;
    319 }
    320 
    321 /**
    322  * Get the default display
    323  * @return pointer to the default display
    324  */
    325 lv_disp_t * lv_disp_get_default(void)
    326 {
    327     return disp_def;
    328 }
    329 
    330 /**
    331  * Get the horizontal resolution of a display
    332  * @param disp pointer to a display (NULL to use the default display)
    333  * @return the horizontal resolution of the display
    334  */
    335 lv_coord_t lv_disp_get_hor_res(lv_disp_t * disp)
    336 {
    337     if(disp == NULL) disp = lv_disp_get_default();
    338 
    339     if(disp == NULL) {
    340         return 0;
    341     }
    342     else {
    343         switch(disp->driver->rotated) {
    344             case LV_DISP_ROT_90:
    345             case LV_DISP_ROT_270:
    346                 return disp->driver->ver_res;
    347             default:
    348                 return disp->driver->hor_res;
    349         }
    350     }
    351 }
    352 
    353 /**
    354  * Get the vertical resolution of a display
    355  * @param disp pointer to a display (NULL to use the default display)
    356  * @return the vertical resolution of the display
    357  */
    358 lv_coord_t lv_disp_get_ver_res(lv_disp_t * disp)
    359 {
    360     if(disp == NULL) disp = lv_disp_get_default();
    361 
    362     if(disp == NULL) {
    363         return 0;
    364     }
    365     else {
    366         switch(disp->driver->rotated) {
    367             case LV_DISP_ROT_90:
    368             case LV_DISP_ROT_270:
    369                 return disp->driver->hor_res;
    370             default:
    371                 return disp->driver->ver_res;
    372         }
    373     }
    374 }
    375 
    376 /**
    377  * Get the full / physical horizontal resolution of a display
    378  * @param disp pointer to a display (NULL to use the default display)
    379  * @return the full / physical horizontal resolution of the display
    380  */
    381 lv_coord_t lv_disp_get_physical_hor_res(lv_disp_t * disp)
    382 {
    383     if(disp == NULL) disp = lv_disp_get_default();
    384 
    385     if(disp == NULL) {
    386         return 0;
    387     }
    388     else {
    389         switch(disp->driver->rotated) {
    390             case LV_DISP_ROT_90:
    391             case LV_DISP_ROT_270:
    392                 return disp->driver->physical_ver_res > 0 ? disp->driver->physical_ver_res : disp->driver->ver_res;
    393             default:
    394                 return disp->driver->physical_hor_res > 0 ? disp->driver->physical_hor_res : disp->driver->hor_res;
    395         }
    396     }
    397 }
    398 
    399 /**
    400  * Get the full / physical vertical resolution of a display
    401  * @param disp pointer to a display (NULL to use the default display)
    402  * @return the full / physical vertical resolution of the display
    403  */
    404 lv_coord_t lv_disp_get_physical_ver_res(lv_disp_t * disp)
    405 {
    406     if(disp == NULL) disp = lv_disp_get_default();
    407 
    408     if(disp == NULL) {
    409         return 0;
    410     }
    411     else {
    412         switch(disp->driver->rotated) {
    413             case LV_DISP_ROT_90:
    414             case LV_DISP_ROT_270:
    415                 return disp->driver->physical_hor_res > 0 ? disp->driver->physical_hor_res : disp->driver->hor_res;
    416             default:
    417                 return disp->driver->physical_ver_res > 0 ? disp->driver->physical_ver_res : disp->driver->ver_res;
    418         }
    419     }
    420 }
    421 
    422 /**
    423  * Get the horizontal offset from the full / physical display
    424  * @param disp pointer to a display (NULL to use the default display)
    425  * @return the horizontal offset from the full / physical display
    426  */
    427 lv_coord_t lv_disp_get_offset_x(lv_disp_t * disp)
    428 {
    429     if(disp == NULL) disp = lv_disp_get_default();
    430 
    431     if(disp == NULL) {
    432         return 0;
    433     }
    434     else {
    435         switch(disp->driver->rotated) {
    436             case LV_DISP_ROT_90:
    437                 return disp->driver->offset_y;
    438             case LV_DISP_ROT_180:
    439                 return lv_disp_get_physical_hor_res(disp) - disp->driver->offset_x;
    440             case LV_DISP_ROT_270:
    441                 return lv_disp_get_physical_hor_res(disp) - disp->driver->offset_y;
    442             default:
    443                 return disp->driver->offset_x;
    444         }
    445     }
    446 }
    447 
    448 /**
    449  * Get the vertical offset from the full / physical display
    450  * @param disp pointer to a display (NULL to use the default display)
    451  * @return the horizontal offset from the full / physical display
    452  */
    453 lv_coord_t lv_disp_get_offset_y(lv_disp_t * disp)
    454 {
    455     if(disp == NULL) disp = lv_disp_get_default();
    456 
    457     if(disp == NULL) {
    458         return 0;
    459     }
    460     else {
    461         switch(disp->driver->rotated) {
    462             case LV_DISP_ROT_90:
    463                 return disp->driver->offset_x;
    464             case LV_DISP_ROT_180:
    465                 return lv_disp_get_physical_ver_res(disp) - disp->driver->offset_y;
    466             case LV_DISP_ROT_270:
    467                 return lv_disp_get_physical_ver_res(disp) - disp->driver->offset_x;
    468             default:
    469                 return disp->driver->offset_y;
    470         }
    471     }
    472 }
    473 
    474 /**
    475  * Get if anti-aliasing is enabled for a display or not
    476  * @param disp pointer to a display (NULL to use the default display)
    477  * @return true: anti-aliasing is enabled; false: disabled
    478  */
    479 bool lv_disp_get_antialiasing(lv_disp_t * disp)
    480 {
    481     if(disp == NULL) disp = lv_disp_get_default();
    482     if(disp == NULL) return false;
    483 
    484     return disp->driver->antialiasing ? true : false;
    485 }
    486 
    487 /**
    488  * Get the DPI of the display
    489  * @param disp pointer to a display (NULL to use the default display)
    490  * @return dpi of the display
    491  */
    492 lv_coord_t lv_disp_get_dpi(const lv_disp_t * disp)
    493 {
    494     if(disp == NULL) disp = lv_disp_get_default();
    495     if(disp == NULL) return LV_DPI_DEF;  /*Do not return 0 because it might be a divider*/
    496     return disp->driver->dpi;
    497 }
    498 
    499 /**
    500  * Call in the display driver's `flush_cb` function when the flushing is finished
    501  * @param disp_drv pointer to display driver in `flush_cb` where this function is called
    502  */
    503 LV_ATTRIBUTE_FLUSH_READY void lv_disp_flush_ready(lv_disp_drv_t * disp_drv)
    504 {
    505     /*If the screen is transparent initialize it when the flushing is ready*/
    506 #if LV_COLOR_SCREEN_TRANSP
    507     if(disp_drv->screen_transp) {
    508         if(disp_drv->clear_cb) {
    509             disp_drv->clear_cb(disp_drv, disp_drv->draw_buf->buf_act, disp_drv->draw_buf->size);
    510         }
    511         else {
    512             lv_memset_00(disp_drv->draw_buf->buf_act, disp_drv->draw_buf->size * sizeof(lv_color32_t));
    513         }
    514     }
    515 #endif
    516 
    517     disp_drv->draw_buf->flushing = 0;
    518     disp_drv->draw_buf->flushing_last = 0;
    519 }
    520 
    521 /**
    522  * Tell if it's the last area of the refreshing process.
    523  * Can be called from `flush_cb` to execute some special display refreshing if needed when all areas area flushed.
    524  * @param disp_drv pointer to display driver
    525  * @return true: it's the last area to flush; false: there are other areas too which will be refreshed soon
    526  */
    527 LV_ATTRIBUTE_FLUSH_READY bool lv_disp_flush_is_last(lv_disp_drv_t * disp_drv)
    528 {
    529     return disp_drv->draw_buf->flushing_last;
    530 }
    531 
    532 /**
    533  * Get the next display.
    534  * @param disp pointer to the current display. NULL to initialize.
    535  * @return the next display or NULL if no more. Give the first display when the parameter is NULL
    536  */
    537 lv_disp_t * lv_disp_get_next(lv_disp_t * disp)
    538 {
    539     if(disp == NULL)
    540         return _lv_ll_get_head(&LV_GC_ROOT(_lv_disp_ll));
    541     else
    542         return _lv_ll_get_next(&LV_GC_ROOT(_lv_disp_ll), disp);
    543 }
    544 
    545 /**
    546  * Get the internal buffer of a display
    547  * @param disp pointer to a display
    548  * @return pointer to the internal buffers
    549  */
    550 lv_disp_draw_buf_t * lv_disp_get_draw_buf(lv_disp_t * disp)
    551 {
    552     return disp->driver->draw_buf;
    553 }
    554 
    555 /**
    556  * Set the rotation of this display.
    557  * @param disp pointer to a display (NULL to use the default display)
    558  * @param rotation rotation angle
    559  */
    560 void lv_disp_set_rotation(lv_disp_t * disp, lv_disp_rot_t rotation)
    561 {
    562     if(disp == NULL) disp = lv_disp_get_default();
    563     if(disp == NULL) return;
    564 
    565     disp->driver->rotated = rotation;
    566     lv_disp_drv_update(disp, disp->driver);
    567 }
    568 
    569 /**
    570  * Get the current rotation of this display.
    571  * @param disp pointer to a display (NULL to use the default display)
    572  * @return rotation angle
    573  */
    574 lv_disp_rot_t lv_disp_get_rotation(lv_disp_t * disp)
    575 {
    576     if(disp == NULL) disp = lv_disp_get_default();
    577     if(disp == NULL) return LV_DISP_ROT_NONE;
    578     return disp->driver->rotated;
    579 }
    580 
    581 void lv_disp_drv_use_generic_set_px_cb(lv_disp_drv_t * disp_drv, lv_img_cf_t cf)
    582 {
    583     switch(cf) {
    584         case LV_IMG_CF_TRUE_COLOR_ALPHA:
    585             disp_drv->set_px_cb = set_px_true_color_alpha;
    586             break;
    587         case LV_IMG_CF_ALPHA_1BIT:
    588             disp_drv->set_px_cb = set_px_cb_alpha1;
    589             break;
    590         case LV_IMG_CF_ALPHA_2BIT:
    591             disp_drv->set_px_cb = set_px_cb_alpha2;
    592             break;
    593         case LV_IMG_CF_ALPHA_4BIT:
    594             disp_drv->set_px_cb = set_px_cb_alpha4;
    595             break;
    596         case LV_IMG_CF_ALPHA_8BIT:
    597             disp_drv->set_px_cb = set_px_cb_alpha8;
    598             break;
    599         default:
    600             disp_drv->set_px_cb = NULL;
    601     }
    602 }
    603 
    604 /**********************
    605  *   STATIC FUNCTIONS
    606  **********************/
    607 
    608 static lv_obj_tree_walk_res_t invalidate_layout_cb(lv_obj_t * obj, void * user_data)
    609 {
    610     LV_UNUSED(user_data);
    611     lv_obj_mark_layout_as_dirty(obj);
    612     return LV_OBJ_TREE_WALK_NEXT;
    613 }
    614 
    615 static void set_px_cb_alpha1(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y,
    616                              lv_color_t color, lv_opa_t opa)
    617 {
    618     (void) disp_drv; /*Unused*/
    619 
    620     if(opa <= LV_OPA_MIN) return;
    621     lv_img_dsc_t d;
    622     d.data = buf;
    623     d.header.w = buf_w;
    624     d.header.cf = LV_IMG_CF_ALPHA_1BIT;
    625 
    626     set_px_alpha_generic(&d, x, y, color, opa);
    627 }
    628 
    629 static void set_px_cb_alpha2(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y,
    630                              lv_color_t color, lv_opa_t opa)
    631 {
    632     (void) disp_drv; /*Unused*/
    633 
    634     if(opa <= LV_OPA_MIN) return;
    635     lv_img_dsc_t d;
    636     d.data = buf;
    637     d.header.w = buf_w;
    638     d.header.cf = LV_IMG_CF_ALPHA_2BIT;
    639 
    640     set_px_alpha_generic(&d, x, y, color, opa);
    641 }
    642 
    643 static void set_px_cb_alpha4(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y,
    644                              lv_color_t color, lv_opa_t opa)
    645 {
    646     (void) disp_drv; /*Unused*/
    647 
    648     if(opa <= LV_OPA_MIN) return;
    649     lv_img_dsc_t d;
    650     d.data = buf;
    651     d.header.w = buf_w;
    652     d.header.cf = LV_IMG_CF_ALPHA_4BIT;
    653 
    654     set_px_alpha_generic(&d, x, y, color, opa);
    655 }
    656 
    657 static void set_px_cb_alpha8(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y,
    658                              lv_color_t color, lv_opa_t opa)
    659 {
    660     (void) disp_drv; /*Unused*/
    661 
    662     if(opa <= LV_OPA_MIN) return;
    663     lv_img_dsc_t d;
    664     d.data = buf;
    665     d.header.w = buf_w;
    666     d.header.cf = LV_IMG_CF_ALPHA_8BIT;
    667 
    668     set_px_alpha_generic(&d, x, y, color, opa);
    669 }
    670 
    671 static void set_px_alpha_generic(lv_img_dsc_t * d, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa)
    672 {
    673     d->header.always_zero = 0;
    674     d->header.h = 1;    /*Doesn't matter*/
    675 
    676     uint8_t br = lv_color_brightness(color);
    677     if(opa < LV_OPA_MAX) {
    678         uint8_t bg = lv_img_buf_get_px_alpha(d, x, y);
    679         br = (uint16_t)((uint16_t)br * opa + (bg * (255 - opa))) >> 8;
    680     }
    681 
    682     lv_img_buf_set_px_alpha(d, x, y, br);
    683 }
    684 
    685 static void set_px_true_color_alpha(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x,
    686                                     lv_coord_t y,
    687                                     lv_color_t color, lv_opa_t opa)
    688 {
    689     (void) disp_drv; /*Unused*/
    690 
    691     if(opa <= LV_OPA_MIN) return;
    692     lv_img_dsc_t d;
    693     d.data = buf;
    694     d.header.always_zero = 0;
    695     d.header.h = 1;    /*Doesn't matter*/;
    696     d.header.w = buf_w;
    697     d.header.cf = LV_IMG_CF_TRUE_COLOR_ALPHA;
    698 
    699     lv_color_t bg_color = lv_img_buf_get_px_color(&d, x, y, lv_color_black());
    700     lv_opa_t bg_opa = lv_img_buf_get_px_alpha(&d, x, y);
    701 
    702     lv_opa_t res_opa;
    703     lv_color_t res_color;
    704 
    705     lv_color_mix_with_alpha(bg_color, bg_opa, color, opa, &res_color, &res_opa);
    706 
    707     lv_img_buf_set_px_alpha(&d, x, y, res_opa);
    708     lv_img_buf_set_px_color(&d, x, y, res_color);
    709 }