diff --git a/src/core/lv_refr.c b/src/core/lv_refr.c index 5ebca8e189..1f88205a48 100644 --- a/src/core/lv_refr.c +++ b/src/core/lv_refr.c @@ -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)) { diff --git a/src/display/lv_display.c b/src/display/lv_display.c index 01ab7bbf66..57129f141c 100644 --- a/src/display/lv_display.c +++ b/src/display/lv_display.c @@ -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; } } diff --git a/src/display/lv_display.h b/src/display/lv_display.h index 5e6bd5aeec..2f00b4f5b8 100644 --- a/src/display/lv_display.h +++ b/src/display/lv_display.h @@ -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) diff --git a/src/display/lv_display_private.h b/src/display/lv_display_private.h index ac7cb37c6a..5b67b820e6 100644 --- a/src/display/lv_display_private.h +++ b/src/display/lv_display_private.h @@ -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*/ diff --git a/src/others/test/lv_test_display.c b/src/others/test/lv_test_display.c index f3aa892f62..b6c00708f6 100644 --- a/src/others/test/lv_test_display.c +++ b/src/others/test/lv_test_display.c @@ -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); diff --git a/tests/ref_imgs_vg_lite/display_matrix_rotation_0.png b/tests/ref_imgs_vg_lite/display_matrix_rotation_0.png new file mode 100644 index 0000000000..10581692a2 Binary files /dev/null and b/tests/ref_imgs_vg_lite/display_matrix_rotation_0.png differ diff --git a/tests/ref_imgs_vg_lite/display_matrix_rotation_180.png b/tests/ref_imgs_vg_lite/display_matrix_rotation_180.png new file mode 100644 index 0000000000..b93e886829 Binary files /dev/null and b/tests/ref_imgs_vg_lite/display_matrix_rotation_180.png differ diff --git a/tests/ref_imgs_vg_lite/display_matrix_rotation_270.png b/tests/ref_imgs_vg_lite/display_matrix_rotation_270.png new file mode 100644 index 0000000000..ab56163be4 Binary files /dev/null and b/tests/ref_imgs_vg_lite/display_matrix_rotation_270.png differ diff --git a/tests/ref_imgs_vg_lite/display_matrix_rotation_90.png b/tests/ref_imgs_vg_lite/display_matrix_rotation_90.png new file mode 100644 index 0000000000..48fa92117d Binary files /dev/null and b/tests/ref_imgs_vg_lite/display_matrix_rotation_90.png differ diff --git a/tests/src/test_cases/test_display.c b/tests/src/test_cases/test_display.c index bd952f8725..2439a7ce0d 100644 --- a/tests/src/test_cases/test_display.c +++ b/tests/src/test_cases/test_display.c @@ -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