In this article, we'll trying to overlay a UI layer onto a video layer, which like OSD. Here is my platform information.
- System: kernel 4.19
- LVGL Version: 8.3
- Display Driver: DRM
- CPU: RV1126, which have two layers.
§1 Check Plane Info
If your CPU's VOP support multiple layer, then you could draw UI and video on difference layer. In this article, I use RV1126 to implement this function. If you use framebuffer
as display interface, then you can directly draw image or UI on different fb device. Such as, draw video on fb1
, UI on fb0
. If you use DRM, using modetest
to check whether planes support overlay.
[root@DCIR:/]# modetest
Encoders:
id crtc type possible crtcs possible clones
58 56 DSI 0x00000001 0x00000000
Connectors:
id encoder status name size (mm) modes encoders
59 58 connected DSI-1 68x121 1 58
modes:
name refresh (Hz) hdisp hss hse htot vdisp vss vse vtot)
800x1280 57 800 818 836 882 1280 1310 1314 1328 67000 flags: nhsync, nvsync; type: preferred, driver
props:
1 EDID:
flags: immutable blob
blobs:
value:
2 DPMS:
flags: enum
enums: On=0 Standby=1 Suspend=2 Off=3
value: 0
5 link-status:
flags: enum
enums: Good=0 Bad=1
value: 0
6 non-desktop:
flags: immutable range
values: 0 1
value: 0
46 brightness:
flags: range
values: 0 100
value: 50
47 contrast:
flags: range
values: 0 100
value: 50
50 saturation:
flags: range
values: 0 100
value: 50
51 hue:
flags: range
values: 0 100
value: 50
CRTCs:
id fb pos size
56 60 (0,0) (800x1280)
800x1280 57 800 818 836 882 1280 1310 1314 1328 67000 flags: nhsync, nvsync; type: preferred, driver
props:
41 left margin:
flags: range
values: 0 100
value: 100
42 right margin:
flags: range
values: 0 100
value: 100
43 top margin:
flags: range
values: 0 100
value: 100
44 bottom margin:
flags: range
values: 0 100
value: 100
54 FEATURE:
flags: immutable bitmask
values: afbdc=0x1
value: 0
26 GAMMA_LUT:
flags: blob
blobs:
value:
27 GAMMA_LUT_SIZE:
flags: immutable range
values: 0 4294967295
value: 256
Planes:
id crtc fb CRTC x,y x,y gamma size possible crtcs
55 56 60 0,0 0,0 0 0x00000001
formats: XR24 AR24 XB24 AB24 RG24 BG24 RG16 BG16
props:
8 type:
flags: immutable enum
enums: Overlay=0 Primary=1 Cursor=2
value: 1
53 FEATURE:
flags: immutable bitmask
values: scale=0x1 alpha=0x2 hdr2sdr=0x4 sdr2hdr=0x8 afbdc=0x10
value: 2
57 0 0 0,0 0,0 0 0x00000001
formats: XR24 AR24 XB24 AB24 RG24 BG24 RG16 BG16 NV12 NV16 NV24
props:
8 type:
flags: immutable enum
enums: Overlay=0 Primary=1 Cursor=2
value: 0
53 FEATURE:
flags: immutable bitmask
values: scale=0x1 alpha=0x2 hdr2sdr=0x4 sdr2hdr=0x8 afbdc=0x10
value: 3
Frame buffers:
id size pitch
1.1 Connectors
Connected output port via CPU, here we only have one MIPI-DSI output which id equals 59.
1.2 CRTCs
Video Output Processor, for RV1126, we only have one VOP which id equals 56.
1.3 Planes
i.e. layer, for RV1126, we have two layers. plane-55's props specify it's a primary layer, and plane-57 is a overlay layer. so, we should draw video on primary player, and draw UI on overlay layer.
§2 LVGL config
Here are several steps to enable transparent background:
2.1 LV_COLOR_DEPTH & LV_COLOR_SCREEN_TRANSP
- In
lv_conf.h
, make sure color depth is 32, eg.#define LV_COLOR_DEPTH 32
, which allows alpha channel. - In
lv_conf.h
, enable LV_COLOR_SCREEN_TRANSP, eg.#define LV_COLOR_SCREEN_TRANSP 1
.
2.2 Specify planes
Here I manually specify LVGL's plane id as 57. In lv_drivers/display/drm.c
, for function drm_setup
, we modify:
// ret = find_plane(fourcc, &drm_dev.plane_id, drm_dev.crtc_id, drm_dev.crtc_idx);
// if (ret) {
// err("Cannot find plane");
// goto err;
// }
drm_dev.plane_id = 57;
2.3 Set background opa
In main function, we add:
lv_style_t style_scr_act;
lv_style_init(&style_scr_act);
lv_style_set_bg_opa(&style_scr_act, LV_OPA_TRANSP);
lv_obj_add_style(lv_scr_act(), &style_scr_act, 0);
// lv_disp_set_bg_opa(lv_scr_act(), LV_OPA_TRANSP);
The complete main:
int main(void)
{
DRM_Init();
// capture and draw video
pthread_t pth_capt;
pthread_create(&pth_capt, NULL, (void*)V4L2_SP_Streaming, NULL);
#if 1
/* ================ */
/* ===== LVGL ===== */
/* ================ */
/*LittlevGL init*/
lv_init();
/*Linux frame buffer device init*/
drm_init();
/*A small buffer for LittlevGL to draw the screen's content*/
static lv_color_t buf[DISP_BUF_SIZE], buf1[DISP_BUF_SIZE];
/*Initialize a descriptor for the buffer*/
static lv_disp_draw_buf_t disp_buf;
// lv_disp_draw_buf_init(&disp_buf, buf, NULL, DISP_BUF_SIZE);
lv_disp_draw_buf_init(&disp_buf, buf, buf1, DISP_BUF_SIZE);
/*Initialize and register a display driver*/
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.draw_buf = &disp_buf;
disp_drv.flush_cb = drm_flush;
disp_drv.hor_res = 800;
disp_drv.ver_res = 1280;
disp_drv.screen_transp = 1;
lv_disp_drv_register(&disp_drv);
#if 1
evdev_init();
static lv_indev_drv_t indev_drv_1;
lv_indev_drv_init(&indev_drv_1); /*Basic initialization*/
indev_drv_1.type = LV_INDEV_TYPE_POINTER;
/*This function will be called periodically (by the library) to get the mouse position and state*/
indev_drv_1.read_cb = evdev_read;
lv_indev_t *mouse_indev = lv_indev_drv_register(&indev_drv_1);
#endif
#if 1
/*Set a cursor for the mouse*/
LV_IMG_DECLARE(mouse_cursor_icon)
lv_obj_t * cursor_obj = lv_img_create(lv_scr_act()); /*Create an image object for the cursor */
lv_img_set_src(cursor_obj, &mouse_cursor_icon); /*Set the image source*/
lv_indev_set_cursor(mouse_indev, cursor_obj); /*Connect the image object to the driver*/
#endif
#if 1
lv_style_t style_scr_act;
lv_style_init(&style_scr_act);
lv_style_set_bg_opa(&style_scr_act, LV_OPA_TRANSP);
lv_obj_add_style(lv_scr_act(), &style_scr_act, 0);
// lv_disp_set_bg_opa(lv_scr_act(), LV_OPA_TRANSP);
#endif
mybtn();
/*Handle LitlevGL tasks (tickless mode)*/
while(1) {
lv_timer_handler();
usleep(5000);
}
#endif
pthread_exit(NULL);
return 0;
}