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