feat(refr): use transform matrix to realize display rotation (#6911)

Signed-off-by: pengyiqiang <pengyiqiang@xiaomi.com>
Co-authored-by: pengyiqiang <pengyiqiang@xiaomi.com>
This commit is contained in:
VIFEX
2025-04-16 01:31:09 +08:00
committed by GitHub
parent dc8c529b7a
commit c08a7a1066
10 changed files with 171 additions and 21 deletions
+42 -11
View File
@@ -547,6 +547,11 @@ static void refr_sync_areas(void)
* @todo Resize SDL window will trigger crash because of sync_area is larger than disp_area
*/
lv_area_intersect(sync_area, sync_area, &disp_area);
#if LV_DRAW_TRANSFORM_USE_MATRIX
if(lv_display_get_matrix_rotation(disp_refr)) {
lv_display_rotate_area(disp_refr, sync_area);
}
#endif
lv_draw_buf_copy(off_screen, sync_area, on_screen, sync_area);
}
@@ -666,23 +671,18 @@ static void refr_area(const lv_area_t * area_p, int32_t y_offset)
layer->phy_clip_area = *area_p;
layer->partial_y_offset = y_offset;
if(disp_refr->render_mode == LV_DISPLAY_RENDER_MODE_FULL) {
/*In full mode the area is always the full screen, so the buffer area to it too*/
layer->buf_area = *area_p;
layer_reshape_draw_buf(layer, layer->draw_buf->header.stride);
}
else if(disp_refr->render_mode == LV_DISPLAY_RENDER_MODE_PARTIAL) {
if(disp_refr->render_mode == LV_DISPLAY_RENDER_MODE_PARTIAL) {
/*In partial mode render this area to the buffer*/
layer->buf_area = *area_p;
layer_reshape_draw_buf(layer, LV_STRIDE_AUTO);
}
else if(disp_refr->render_mode == LV_DISPLAY_RENDER_MODE_DIRECT) {
/*In direct mode the the buffer area is always the whole screen*/
else if(disp_refr->render_mode == LV_DISPLAY_RENDER_MODE_DIRECT ||
disp_refr->render_mode == LV_DISPLAY_RENDER_MODE_FULL) {
/*In direct mode and full mode the the buffer area is always the whole screen, not considering rotation*/
layer->buf_area.x1 = 0;
layer->buf_area.y1 = 0;
layer->buf_area.x2 = lv_display_get_horizontal_resolution(disp_refr) - 1;
layer->buf_area.y2 = lv_display_get_vertical_resolution(disp_refr) - 1;
layer->buf_area.x2 = lv_display_get_original_horizontal_resolution(disp_refr) - 1;
layer->buf_area.y2 = lv_display_get_original_vertical_resolution(disp_refr) - 1;
layer_reshape_draw_buf(layer, layer->draw_buf->header.stride);
}
@@ -767,6 +767,37 @@ static void refr_configured_layer(lv_layer_t * layer)
lv_layer_reset(layer);
#if LV_DRAW_TRANSFORM_USE_MATRIX
if(lv_display_get_matrix_rotation(disp_refr)) {
const lv_display_rotation_t rotation = lv_display_get_rotation(disp_refr);
if(rotation != LV_DISPLAY_ROTATION_0) {
lv_display_rotate_area(disp_refr, &layer->phy_clip_area);
/* The screen rotation direction defined by LVGL is opposite to the drawing angle */
switch(rotation) {
case LV_DISPLAY_ROTATION_90:
lv_matrix_rotate(&layer->matrix, 270);
lv_matrix_translate(&layer->matrix, -disp_refr->ver_res, 0);
break;
case LV_DISPLAY_ROTATION_180:
lv_matrix_rotate(&layer->matrix, 180);
lv_matrix_translate(&layer->matrix, -disp_refr->hor_res, -disp_refr->ver_res);
break;
case LV_DISPLAY_ROTATION_270:
lv_matrix_rotate(&layer->matrix, 90);
lv_matrix_translate(&layer->matrix, 0, -disp_refr->hor_res);
break;
default:
LV_LOG_WARN("Invalid rotation: %d", rotation);
break;
}
}
}
#endif /* LV_DRAW_TRANSFORM_USE_MATRIX */
/* In single buffered mode wait here until the buffer is freed.
* Else we would draw into the buffer while it's still being transferred to the display*/
if(!lv_display_is_double_buffered(disp_refr)) {
+54 -7
View File
@@ -322,6 +322,26 @@ int32_t lv_display_get_vertical_resolution(const lv_display_t * disp)
}
}
int32_t lv_display_get_original_horizontal_resolution(const lv_display_t * disp)
{
if(disp == NULL) disp = lv_display_get_default();
if(disp == NULL) {
return 0;
}
return disp->hor_res;
}
int32_t lv_display_get_original_vertical_resolution(const lv_display_t * disp)
{
if(disp == NULL) disp = lv_display_get_default();
if(disp == NULL) {
return 0;
}
return disp->ver_res;
}
int32_t lv_display_get_physical_horizontal_resolution(const lv_display_t * disp)
{
if(disp == NULL) disp = lv_display_get_default();
@@ -426,9 +446,8 @@ void lv_display_set_buffers(lv_display_t * disp, void * buf1, void * buf2, uint3
{
LV_ASSERT_MSG(buf1 != NULL, "Null buffer");
lv_color_format_t cf = lv_display_get_color_format(disp);
uint32_t w = lv_display_get_horizontal_resolution(disp);
uint32_t h = lv_display_get_vertical_resolution(disp);
uint32_t w = lv_display_get_original_horizontal_resolution(disp);
uint32_t h = lv_display_get_original_vertical_resolution(disp);
LV_ASSERT_MSG(w != 0 && h != 0, "display resolution is 0");
/* buf1 or buf2 is not aligned according to LV_DRAW_BUF_ALIGN */
@@ -457,8 +476,8 @@ void lv_display_set_buffers_with_stride(lv_display_t * disp, void * buf1, void *
{
LV_ASSERT_MSG(buf1 != NULL, "Null buffer");
lv_color_format_t cf = lv_display_get_color_format(disp);
uint32_t w = lv_display_get_horizontal_resolution(disp);
uint32_t h = lv_display_get_vertical_resolution(disp);
uint32_t w = lv_display_get_original_horizontal_resolution(disp);
uint32_t h = lv_display_get_original_vertical_resolution(disp);
LV_ASSERT_MSG(w != 0 && h != 0, "display resolution is 0");
if(render_mode == LV_DISPLAY_RENDER_MODE_PARTIAL) {
@@ -874,6 +893,32 @@ lv_display_rotation_t lv_display_get_rotation(lv_display_t * disp)
return disp->rotation;
}
void lv_display_set_matrix_rotation(lv_display_t * disp, bool enable)
{
#if LV_DRAW_TRANSFORM_USE_MATRIX
if(disp == NULL) disp = lv_display_get_default();
if(disp == NULL) return;
if(!(disp->render_mode == LV_DISPLAY_RENDER_MODE_DIRECT || disp->render_mode == LV_DISPLAY_RENDER_MODE_FULL)) {
LV_LOG_WARN("Unsupported rendering mode: %d", disp->render_mode);
return;
}
disp->matrix_rotation = enable;
#else
(void)disp;
(void)enable;
LV_LOG_WARN("LV_DRAW_TRANSFORM_USE_MATRIX was not enabled");
#endif
}
bool lv_display_get_matrix_rotation(lv_display_t * disp)
{
if(disp == NULL) disp = lv_display_get_default();
if(disp == NULL) return false;
return disp->matrix_rotation;
}
void lv_display_set_theme(lv_display_t * disp, lv_theme_t * th)
{
if(!disp) disp = lv_display_get_default();
@@ -1048,12 +1093,12 @@ void lv_display_rotate_area(lv_display_t * disp, lv_area_t * area)
{
lv_display_rotation_t rotation = lv_display_get_rotation(disp);
if(rotation == LV_DISPLAY_ROTATION_0) return;
int32_t w = lv_area_get_width(area);
int32_t h = lv_area_get_height(area);
switch(rotation) {
case LV_DISPLAY_ROTATION_0:
return;
case LV_DISPLAY_ROTATION_90:
area->y2 = disp->ver_res - area->x1 - 1;
area->x1 = area->y1;
@@ -1072,6 +1117,8 @@ void lv_display_rotate_area(lv_display_t * disp, lv_area_t * area)
area->x2 = area->x1 + h - 1;
area->y1 = area->y2 - w + 1;
break;
default:
break;
}
}
+28
View File
@@ -158,6 +158,13 @@ void lv_display_set_offset(lv_display_t * disp, int32_t x, int32_t y);
*/
void lv_display_set_rotation(lv_display_t * disp, lv_display_rotation_t rotation);
/**
* Use matrix rotation for the display. This function is depended on `LV_DRAW_TRANSFORM_USE_MATRIX`
* @param disp pointer to a display (NULL to use the default display)
* @param enable true: enable matrix rotation, false: disable
*/
void lv_display_set_matrix_rotation(lv_display_t * disp, bool enable);
/**
* Set the DPI (dot per inch) of the display.
* dpi = sqrt(hor_res^2 + ver_res^2) / diagonal"
@@ -180,6 +187,20 @@ int32_t lv_display_get_horizontal_resolution(const lv_display_t * disp);
*/
int32_t lv_display_get_vertical_resolution(const lv_display_t * disp);
/**
* Get the original horizontal resolution of a display without considering rotation
* @param disp pointer to a display (NULL to use the default display)
* @return the horizontal resolution of the display.
*/
int32_t lv_display_get_original_horizontal_resolution(const lv_display_t * disp);
/**
* Get the original vertical resolution of a display without considering rotation
* @param disp pointer to a display (NULL to use the default display)
* @return the vertical resolution of the display
*/
int32_t lv_display_get_original_vertical_resolution(const lv_display_t * disp);
/**
* Get the physical horizontal resolution of a display
* @param disp pointer to a display (NULL to use the default display)
@@ -215,6 +236,13 @@ int32_t lv_display_get_offset_y(const lv_display_t * disp);
*/
lv_display_rotation_t lv_display_get_rotation(lv_display_t * disp);
/**
* Get if matrix rotation is enabled for a display or not
* @param disp pointer to a display (NULL to use the default display)
* @return true: matrix rotation is enabled; false: disabled
*/
bool lv_display_get_matrix_rotation(lv_display_t * disp);
/**
* Get the DPI of the display
* @param disp pointer to a display (NULL to use the default display)
+2
View File
@@ -145,6 +145,8 @@ struct _lv_display_t {
uint32_t rotation : 3; /**< Element of lv_display_rotation_t*/
uint32_t matrix_rotation : 1; /**< 1: Use matrix for display rotation*/
lv_theme_t * theme; /**< The theme assigned to the screen*/
/** A timer which periodically checks the dirty areas and refreshes them*/
+2 -2
View File
@@ -75,8 +75,8 @@ static void buf_changed_event_cb(lv_event_t * e)
{
lv_display_t * disp = lv_event_get_target(e);
lv_color_format_t cf = lv_display_get_color_format(disp);
int32_t hor_res = lv_display_get_horizontal_resolution(disp);
int32_t ver_res = lv_display_get_vertical_resolution(disp);
int32_t hor_res = lv_display_get_original_horizontal_resolution(disp);
int32_t ver_res = lv_display_get_original_vertical_resolution(disp);
free(_state.draw_buf.unaligned_data);
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

+43 -1
View File
@@ -2,6 +2,9 @@
#include "../lvgl.h"
#include "unity/unity.h"
/*Bypassing resolution check*/
#define TEST_DISPLAY_ASSERT_EQUAL_SCREENSHOT(path) TEST_ASSERT_MESSAGE(lv_test_screenshot_compare(path), path);
void setUp(void)
{
/* Function run before every test */
@@ -9,7 +12,8 @@ void setUp(void)
void tearDown(void)
{
/* Function run after every test */
lv_display_set_matrix_rotation(NULL, false);
lv_obj_clean(lv_screen_active());
}
struct display_area_test_set {
@@ -36,6 +40,7 @@ void test_get_drawbuf_size_double_buffered(void)
LV_COLOR_FORMAT_RGB888), 200, LV_DISPLAY_RENDER_MODE_PARTIAL);
TEST_ASSERT_EQUAL(200, lv_display_get_draw_buf_size(disp));
lv_display_delete(disp);
}
void test_get_drawbuf_size_single_buffered(void)
@@ -49,6 +54,7 @@ void test_get_drawbuf_size_single_buffered(void)
LV_DISPLAY_RENDER_MODE_PARTIAL);
TEST_ASSERT_EQUAL(200, lv_display_get_draw_buf_size(disp));
lv_display_delete(disp);
}
static void exec_invalidated_drawbuf_size_test(const struct display_area_test_set * test_set)
@@ -126,5 +132,41 @@ void test_get_invalidated_drawbuf_size_i1_partial()
exec_invalidated_drawbuf_size_test(&test_set);
}
void test_display_matrix_rotation(void)
{
#if LV_DRAW_TRANSFORM_USE_MATRIX
lv_obj_t * obj = lv_obj_create(lv_screen_active());
lv_obj_set_size(obj, 300, 200);
lv_obj_set_pos(obj, 30, 20);
lv_obj_t * label = lv_label_create(obj);
lv_display_t * disp = lv_obj_get_display(obj);
lv_display_set_matrix_rotation(disp, true);
TEST_ASSERT_TRUE(lv_display_get_matrix_rotation(disp));
lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_0);
lv_label_set_text(label, "Rotation: 0 degrees");
TEST_DISPLAY_ASSERT_EQUAL_SCREENSHOT("display_matrix_rotation_0.png");
lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_90);
lv_label_set_text(label, "Rotation: 90 degrees");
TEST_DISPLAY_ASSERT_EQUAL_SCREENSHOT("display_matrix_rotation_90.png");
lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_180);
lv_label_set_text(label, "Rotation: 180 degrees");
TEST_DISPLAY_ASSERT_EQUAL_SCREENSHOT("display_matrix_rotation_180.png");
lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_270);
lv_label_set_text(label, "Rotation: 270 degrees");
TEST_DISPLAY_ASSERT_EQUAL_SCREENSHOT("display_matrix_rotation_270.png");
lv_display_set_matrix_rotation(disp, false);
lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_0);
lv_label_set_text(label, "Rotation: 0 degrees");
TEST_DISPLAY_ASSERT_EQUAL_SCREENSHOT("display_matrix_rotation_0.png");
#else
TEST_PASS();
#endif
}
#endif