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