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