feat(fbdev,sdl): support display rotation (#5703)

Co-authored-by: Gabor Kiss-Vamosi <kisvegabor@gmail.com>
This commit is contained in:
Johannes Marbach
2024-03-21 12:36:38 +01:00
committed by GitHub
parent 910257fa8a
commit 1b97f0f2c0
7 changed files with 205 additions and 64 deletions

View File

@@ -152,6 +152,17 @@ reconfiguring the hardware. In lack of hardware display rotation support
:cpp:expr:`lv_draw_sw_rotate` can be used to rotate the buffer in the
``flush_cb``.
:cpp:expr:`lv_display_rotate_area(display, &area)` rotates the rendered area
according to the current rotation settings of the display.
Note that in :cpp:enumerator:`LV_DISPLAY_RENDER_MODE_DIRECT` the small changed areas
are rendered directly in the frame buffer so they cannot be
rotated later. Therefore in direct mode only the whole frame buffer can be rotated.
The same is true for :cpp:enumerator:`LV_DISPLAY_RENDER_MODE_FULL`.
In the case of :cpp:enumerator:`LV_DISPLAY_RENDER_MODE_PARTIAL`the small rendered areas
can be rotated on their own before flushing to the frame buffer.
Color format
------------

View File

@@ -924,6 +924,37 @@ lv_draw_buf_t * lv_display_get_buf_active(lv_display_t * disp)
return disp->buf_act;
}
void lv_display_rotate_area(lv_display_t * disp, lv_area_t * area)
{
lv_display_rotation_t rotation = lv_display_get_rotation(disp);
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;
area->x2 = area->x1 + h - 1;
area->y1 = area->y2 - w + 1;
break;
case LV_DISPLAY_ROTATION_180:
area->y2 = disp->ver_res - area->y1 - 1;
area->y1 = area->y2 - h + 1;
area->x2 = disp->hor_res - area->x1 - 1;
area->x1 = area->x2 - w + 1;
break;
case LV_DISPLAY_ROTATION_270:
area->x1 = disp->hor_res - area->y2 - 1;
area->y2 = area->x2;
area->x2 = area->x1 + h - 1;
area->y1 = area->y2 - w + 1;
break;
}
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -536,6 +536,13 @@ void * lv_display_get_user_data(lv_display_t * disp);
void * lv_display_get_driver_data(lv_display_t * disp);
lv_draw_buf_t * lv_display_get_buf_active(lv_display_t * disp);
/**
* Rotate an area in-place according to the display's rotation
* @param disp pointer to a display
* @param area pointer to an area to rotate
*/
void lv_display_rotate_area(lv_display_t * disp, lv_area_t * area);
/**********************
* MACROS
**********************/

View File

@@ -449,9 +449,9 @@ static void execute_drawing(lv_draw_sw_unit_t * u)
LV_PROFILER_END;
}
static void rotate90_argb8888(const uint32_t * src, uint32_t * dst, int32_t srcWidth, int32_t srcHeight,
int32_t srcStride,
int32_t dstStride)
static void rotate270_argb8888(const uint32_t * src, uint32_t * dst, int32_t srcWidth, int32_t srcHeight,
int32_t srcStride,
int32_t dstStride)
{
if(LV_RESULT_OK == LV_DRAW_SW_ROTATE90_ARGB8888(src, dst, srcWidth, srcHeight, srcStride, dstStride)) {
return ;
@@ -489,9 +489,9 @@ static void rotate180_argb8888(const uint32_t * src, uint32_t * dst, int32_t wid
}
}
static void rotate270_argb8888(const uint32_t * src, uint32_t * dst, int32_t srcWidth, int32_t srcHeight,
int32_t srcStride,
int32_t dstStride)
static void rotate90_argb8888(const uint32_t * src, uint32_t * dst, int32_t srcWidth, int32_t srcHeight,
int32_t srcStride,
int32_t dstStride)
{
if(LV_RESULT_OK == LV_DRAW_SW_ROTATE270_ARGB8888(src, dst, srcWidth, srcHeight, srcStride, dstStride)) {
return ;
@@ -510,8 +510,8 @@ static void rotate270_argb8888(const uint32_t * src, uint32_t * dst, int32_t src
}
}
static void rotate90_rgb888(const uint8_t * src, uint8_t * dst, int32_t srcWidth, int32_t srcHeight, int32_t srcStride,
int32_t dstStride)
static void rotate270_rgb888(const uint8_t * src, uint8_t * dst, int32_t srcWidth, int32_t srcHeight, int32_t srcStride,
int32_t dstStride)
{
if(LV_RESULT_OK == LV_DRAW_SW_ROTATE90_RGB888(src, dst, srcWidth, srcHeight, srcStride, dstStride)) {
return ;
@@ -546,8 +546,8 @@ static void rotate180_rgb888(const uint8_t * src, uint8_t * dst, int32_t width,
}
}
static void rotate270_rgb888(const uint8_t * src, uint8_t * dst, int32_t width, int32_t height, int32_t srcStride,
int32_t dstStride)
static void rotate90_rgb888(const uint8_t * src, uint8_t * dst, int32_t width, int32_t height, int32_t srcStride,
int32_t dstStride)
{
if(LV_RESULT_OK == LV_DRAW_SW_ROTATE270_RGB888(src, dst, srcWidth, srcHeight, srcStride, dstStride)) {
return ;
@@ -564,9 +564,9 @@ static void rotate270_rgb888(const uint8_t * src, uint8_t * dst, int32_t width,
}
}
static void rotate90_rgb565(const uint16_t * src, uint16_t * dst, int32_t srcWidth, int32_t srcHeight,
int32_t srcStride,
int32_t dstStride)
static void rotate270_rgb565(const uint16_t * src, uint16_t * dst, int32_t srcWidth, int32_t srcHeight,
int32_t srcStride,
int32_t dstStride)
{
if(LV_RESULT_OK == LV_DRAW_SW_ROTATE90_RGB565(src, dst, srcWidth, srcHeight, srcStride, dstStride)) {
return ;
@@ -604,9 +604,9 @@ static void rotate180_rgb565(const uint16_t * src, uint16_t * dst, int32_t width
}
}
static void rotate270_rgb565(const uint16_t * src, uint16_t * dst, int32_t srcWidth, int32_t srcHeight,
int32_t srcStride,
int32_t dstStride)
static void rotate90_rgb565(const uint16_t * src, uint16_t * dst, int32_t srcWidth, int32_t srcHeight,
int32_t srcStride,
int32_t dstStride)
{
if(LV_RESULT_OK == LV_DRAW_SW_ROTATE270_RGB565(src, dst, srcWidth, srcHeight, srcStride, dstStride)) {
return ;

View File

@@ -26,6 +26,9 @@
#include <linux/fb.h>
#endif /* LV_LINUX_FBDEV_BSD */
#include "../../../display/lv_display_private.h"
#include "../../../draw/sw/lv_draw_sw.h"
/*********************
* DEFINES
*********************/
@@ -57,6 +60,8 @@ typedef struct {
struct fb_fix_screeninfo finfo;
#endif /* LV_LINUX_FBDEV_BSD */
char * fbp;
uint8_t * rotated_buf;
size_t rotated_buf_size;
long int screensize;
int fbfd;
bool force_refresh;
@@ -253,15 +258,60 @@ static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * colo
{
lv_linux_fb_t * dsc = lv_display_get_driver_data(disp);
if(dsc->fbp == NULL ||
area->x2 < 0 || area->y2 < 0 ||
area->x1 > (int32_t)dsc->vinfo.xres - 1 || area->y1 > (int32_t)dsc->vinfo.yres - 1) {
if(dsc->fbp == NULL) {
lv_display_flush_ready(disp);
return;
}
int32_t w = lv_area_get_width(area);
uint32_t px_size = lv_color_format_get_size(lv_display_get_color_format(disp));
int32_t h = lv_area_get_height(area);
lv_color_format_t cf = lv_display_get_color_format(disp);
uint32_t px_size = lv_color_format_get_size(cf);
lv_display_rotation_t rotation = lv_display_get_rotation(disp);
/* Not all framebuffer kernel drivers support hardware rotation, so we need to handle it in software here */
if(rotation != LV_DISPLAY_ROTATION_0 && LV_LINUX_FBDEV_RENDER_MODE == LV_DISPLAY_RENDER_MODE_PARTIAL) {
/* (Re)allocate temporary buffer if needed */
size_t buf_size = w * h * px_size;
if(!dsc->rotated_buf || dsc->rotated_buf_size != buf_size) {
dsc->rotated_buf = realloc(dsc->rotated_buf, buf_size);
dsc->rotated_buf_size = buf_size;
}
/* Rotate the pixel buffer */
uint32_t w_stride = lv_draw_buf_width_to_stride(w, cf);
uint32_t h_stride = lv_draw_buf_width_to_stride(h, cf);
switch(rotation) {
case LV_DISPLAY_ROTATION_0:
break;
case LV_DISPLAY_ROTATION_90:
lv_draw_sw_rotate(color_p, dsc->rotated_buf, w, h, w_stride, h_stride, rotation, cf);
break;
case LV_DISPLAY_ROTATION_180:
lv_draw_sw_rotate(color_p, dsc->rotated_buf, w, h, w_stride, w_stride, rotation, cf);
break;
case LV_DISPLAY_ROTATION_270:
lv_draw_sw_rotate(color_p, dsc->rotated_buf, w, h, w_stride, h_stride, rotation, cf);
break;
}
color_p = dsc->rotated_buf;
/* Rotate the area */
lv_display_rotate_area(disp, (lv_area_t *)area);
if(rotation != LV_DISPLAY_ROTATION_180) {
w = lv_area_get_width(area);
h = lv_area_get_height(area);
}
}
/* Ensure that we're within the framebuffer's bounds */
if(area->x2 < 0 || area->y2 < 0 || area->x1 > (int32_t)dsc->vinfo.xres - 1 || area->y1 > (int32_t)dsc->vinfo.yres - 1) {
lv_display_flush_ready(disp);
return;
}
uint32_t color_pos = (area->x1 + dsc->vinfo.xoffset) * px_size + area->y1 * dsc->finfo.line_length;
uint32_t fb_pos = color_pos + dsc->vinfo.yoffset * dsc->finfo.line_length;
@@ -275,6 +325,7 @@ static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * colo
}
}
else {
w = lv_area_get_width(area);
for(y = area->y1; y <= area->y2; y++) {
lv_memcpy(&fbp[fb_pos], color_p, w * px_size);
fb_pos += dsc->finfo.line_length;

View File

@@ -12,6 +12,7 @@
#include "../../core/lv_refr.h"
#include "../../stdlib/lv_string.h"
#include "../../core/lv_global.h"
#include "../../display/lv_display_private.h"
#include "../../lv_init.h"
#define SDL_MAIN_HANDLED /*To fix SDL's "undefined reference to WinMain" issue*/
@@ -38,6 +39,8 @@ typedef struct {
uint8_t * fb_act;
uint8_t * buf1;
uint8_t * buf2;
uint8_t * rotated_buf;
size_t rotated_buf_size;
#endif
uint8_t zoom;
uint8_t ignore_size_chg;
@@ -125,17 +128,17 @@ lv_display_t * lv_sdl_window_create(int32_t hor_res, int32_t ver_res)
}
/*LV_DISPLAY_RENDER_MODE_DIRECT or FULL */
else {
uint32_t stride = lv_draw_buf_width_to_stride(lv_display_get_horizontal_resolution(disp),
uint32_t stride = lv_draw_buf_width_to_stride(disp->hor_res,
lv_display_get_color_format(disp));
lv_display_set_buffers(disp, dsc->fb1, dsc->fb2, stride * lv_display_get_vertical_resolution(disp),
lv_display_set_buffers(disp, dsc->fb1, dsc->fb2, stride * disp->ver_res,
LV_SDL_RENDER_MODE);
}
#else /*/*LV_USE_DRAW_SDL == 1*/
uint32_t stride = lv_draw_buf_width_to_stride(lv_display_get_horizontal_resolution(disp),
uint32_t stride = lv_draw_buf_width_to_stride(disp->hor_res,
lv_display_get_color_format(disp));
/*It will render directly to default Texture, so the buffer is not used, so just set something*/
static uint8_t dummy_buf[1];
lv_display_set_buffers(disp, dummy_buf, NULL, stride * lv_display_get_vertical_resolution(disp),
lv_display_set_buffers(disp, dummy_buf, NULL, stride * disp->ver_res,
LV_SDL_RENDER_MODE);
#endif /*LV_USE_DRAW_SDL == 0*/
lv_display_add_event_cb(disp, res_chg_event_cb, LV_EVENT_RESOLUTION_CHANGED, NULL);
@@ -208,21 +211,60 @@ static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_m
{
#if LV_USE_DRAW_SDL == 0
lv_sdl_window_t * dsc = lv_display_get_driver_data(disp);
lv_color_format_t cf = lv_display_get_color_format(disp);
if(LV_SDL_RENDER_MODE == LV_DISPLAY_RENDER_MODE_PARTIAL) {
int32_t y;
lv_display_rotation_t rotation = lv_display_get_rotation(disp);
uint32_t px_size = lv_color_format_get_size(cf);
if(rotation != LV_DISPLAY_ROTATION_0) {
int32_t w = lv_area_get_width(area);
int32_t h = lv_area_get_height(area);
uint32_t w_stride = lv_draw_buf_width_to_stride(w, cf);
uint32_t h_stride = lv_draw_buf_width_to_stride(h, cf);
size_t buf_size = w * h * px_size;
/* (Re)allocate temporary buffer if needed */
if(!dsc->rotated_buf || dsc->rotated_buf_size != buf_size) {
dsc->rotated_buf = realloc(dsc->rotated_buf, buf_size);
dsc->rotated_buf_size = buf_size;
}
switch(rotation) {
case LV_DISPLAY_ROTATION_0:
break;
case LV_DISPLAY_ROTATION_90:
lv_draw_sw_rotate(px_map, dsc->rotated_buf, w, h, w_stride, h_stride, rotation, cf);
break;
case LV_DISPLAY_ROTATION_180:
lv_draw_sw_rotate(px_map, dsc->rotated_buf, w, h, w_stride, w_stride, rotation, cf);
break;
case LV_DISPLAY_ROTATION_270:
lv_draw_sw_rotate(px_map, dsc->rotated_buf, w, h, w_stride, h_stride, rotation, cf);
break;
}
px_map = dsc->rotated_buf;
lv_display_rotate_area(disp, (lv_area_t *)area);
}
uint32_t px_map_stride = lv_draw_buf_width_to_stride(lv_area_get_width(area), cf);
uint32_t px_map_line_bytes = lv_area_get_width(area) * px_size;
uint8_t * fb_tmp = dsc->fb_act;
uint32_t px_size = lv_color_format_get_size(lv_display_get_color_format(disp));
uint32_t px_map_stride = lv_draw_buf_width_to_stride(lv_area_get_width(area), lv_display_get_color_format(disp));
uint32_t data_size = lv_area_get_width(area) * px_size;
int32_t fb_stride = lv_display_get_horizontal_resolution(disp) * px_size;
uint32_t fb_stride = disp->hor_res * px_size;
fb_tmp += area->y1 * fb_stride;
fb_tmp += area->x1 * px_size;
int32_t y;
for(y = area->y1; y <= area->y2; y++) {
lv_memcpy(fb_tmp, px_map, data_size);
lv_memcpy(fb_tmp, px_map, px_map_line_bytes);
px_map += px_map_stride;
fb_tmp += fb_stride;
}
}
/* TYPICALLY YOU DO NOT NEED THIS
* If it was the last part to refresh update the texture of the window.*/
if(lv_display_flush_is_last(disp)) {
@@ -304,8 +346,8 @@ static void window_create(lv_display_t * disp)
flag |= SDL_WINDOW_FULLSCREEN;
#endif
int32_t hor_res = lv_display_get_horizontal_resolution(disp);
int32_t ver_res = lv_display_get_vertical_resolution(disp);
int32_t hor_res = disp->hor_res;
int32_t ver_res = disp->ver_res;
dsc->window = SDL_CreateWindow("LVGL Simulator",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
hor_res * dsc->zoom, ver_res * dsc->zoom, flag); /*last param. SDL_WINDOW_BORDERLESS to hide borders*/
@@ -331,7 +373,7 @@ static void window_update(lv_display_t * disp)
{
lv_sdl_window_t * dsc = lv_display_get_driver_data(disp);
#if LV_USE_DRAW_SDL == 0
int32_t hor_res = lv_display_get_horizontal_resolution(disp);
int32_t hor_res = disp->hor_res;
uint32_t stride = lv_draw_buf_width_to_stride(hor_res, lv_display_get_color_format(disp));
SDL_UpdateTexture(dsc->texture, NULL, dsc->fb_act, stride);
@@ -346,13 +388,11 @@ static void window_update(lv_display_t * disp)
#if LV_USE_DRAW_SDL == 0
static void texture_resize(lv_display_t * disp)
{
int32_t hor_res = lv_display_get_horizontal_resolution(disp);
int32_t ver_res = lv_display_get_vertical_resolution(disp);
uint32_t stride = lv_draw_buf_width_to_stride(hor_res, lv_display_get_color_format(disp));
uint32_t stride = lv_draw_buf_width_to_stride(disp->hor_res, lv_display_get_color_format(disp));
lv_sdl_window_t * dsc = lv_display_get_driver_data(disp);
dsc->fb1 = realloc(dsc->fb1, stride * ver_res);
memset(dsc->fb1, 0x00, stride * ver_res);
dsc->fb1 = realloc(dsc->fb1, stride * disp->ver_res);
lv_memzero(dsc->fb1, stride * disp->ver_res);
if(LV_SDL_RENDER_MODE == LV_DISPLAY_RENDER_MODE_PARTIAL) {
dsc->fb_act = dsc->fb1;
@@ -362,7 +402,7 @@ static void texture_resize(lv_display_t * disp)
dsc->fb2 = realloc(dsc->fb2, stride * ver_res);
memset(dsc->fb2, 0x00, stride * ver_res);
#endif
lv_display_set_buffers(disp, dsc->fb1, dsc->fb2, stride * ver_res, LV_SDL_RENDER_MODE);
lv_display_set_buffers(disp, dsc->fb1, dsc->fb2, stride * disp->ver_res, LV_SDL_RENDER_MODE);
}
if(dsc->texture) SDL_DestroyTexture(dsc->texture);
@@ -379,7 +419,7 @@ static void texture_resize(lv_display_t * disp)
// px_format = SDL_PIXELFORMAT_BGR24;
dsc->texture = SDL_CreateTexture(dsc->renderer, px_format,
SDL_TEXTUREACCESS_STATIC, hor_res, ver_res);
SDL_TEXTUREACCESS_STATIC, disp->hor_res, disp->ver_res);
SDL_SetTextureBlendMode(dsc->texture, SDL_BLENDMODE_BLEND);
}
#endif
@@ -388,11 +428,9 @@ static void res_chg_event_cb(lv_event_t * e)
{
lv_display_t * disp = lv_event_get_current_target(e);
int32_t hor_res = lv_display_get_horizontal_resolution(disp);
int32_t ver_res = lv_display_get_vertical_resolution(disp);
lv_sdl_window_t * dsc = lv_display_get_driver_data(disp);
if(dsc->ignore_size_chg == false) {
SDL_SetWindowSize(dsc->window, hor_res * dsc->zoom, ver_res * dsc->zoom);
SDL_SetWindowSize(dsc->window, disp->hor_res * dsc->zoom, disp->ver_res * dsc->zoom);
}
#if LV_USE_DRAW_SDL == 0

View File

@@ -22,9 +22,9 @@ void test_rotate90_RGB565(void)
uint16_t dstArray[2 * 3] = {0};
uint16_t expectedArray[2 * 3] = {
0x4440, 0x1110,
0x5550, 0x2220,
0x6660, 0x3330
0x3330, 0x6660,
0x2220, 0x5550,
0x1110, 0x4440,
};
lv_draw_sw_rotate(srcArray, dstArray,
@@ -66,12 +66,12 @@ void test_rotate270_RGB565(void)
};
uint16_t dstArray[2 * 3] = {0};
uint16_t expectedArray[2 * 3] = {
0x3330, 0x6660,
0x2220, 0x5550,
0x1110, 0x4440,
};
uint16_t expectedArray[2 * 3] = {
0x4440, 0x1110,
0x5550, 0x2220,
0x6660, 0x3330
};
lv_draw_sw_rotate(srcArray, dstArray,
3, 2,
3 * sizeof(uint16_t),
@@ -89,10 +89,11 @@ void test_rotate90_RGB888(void)
0x44, 0x4A, 0x4B, 0x55, 0x5A, 0x5B, 0x66, 0x6A, 0x6B
};
uint8_t dstArray[2 * 3 * 3] = {0};
uint8_t expectedArray[2 * 3 * 3] = {
0x33, 0x3A, 0x3B, 0x66, 0x6A, 0x6B,
0x22, 0x2A, 0x2B, 0x55, 0x5A, 0x5B,
0x11, 0x1A, 0x1B, 0x44, 0x4A, 0x4B,
0x44, 0x4A, 0x4B, 0x11, 0x1A, 0x1B,
0x55, 0x5A, 0x5B, 0x22, 0x2A, 0x2B,
0x66, 0x6A, 0x6B, 0x33, 0x3A, 0x3B,
};
lv_draw_sw_rotate(srcArray, dstArray,
@@ -112,6 +113,7 @@ void test_rotate180_RGB888(void)
0x44, 0x4A, 0x4B, 0x55, 0x5A, 0x5B, 0x66, 0x6A, 0x6B
};
uint8_t dstArray[3 * 2 * 3] = {0};
uint8_t expectedArray[3 * 2 * 3] = {
0x66, 0x6A, 0x6B, 0x55, 0x5A, 0x5B, 0x44, 0x4A, 0x4B,
0x33, 0x3A, 0x3B, 0x22, 0x2A, 0x2B, 0x11, 0x1A, 0x1B
@@ -134,10 +136,11 @@ void test_rotate270_RGB888(void)
0x44, 0x4A, 0x4B, 0x55, 0x5A, 0x5B, 0x66, 0x6A, 0x6B
};
uint8_t dstArray[2 * 3 * 3] = {0};
uint8_t expectedArray[2 * 3 * 3] = {
0x44, 0x4A, 0x4B, 0x11, 0x1A, 0x1B,
0x55, 0x5A, 0x5B, 0x22, 0x2A, 0x2B,
0x66, 0x6A, 0x6B, 0x33, 0x3A, 0x3B,
0x33, 0x3A, 0x3B, 0x66, 0x6A, 0x6B,
0x22, 0x2A, 0x2B, 0x55, 0x5A, 0x5B,
0x11, 0x1A, 0x1B, 0x44, 0x4A, 0x4B,
};
lv_draw_sw_rotate(srcArray, dstArray,
@@ -158,10 +161,11 @@ void test_rotate90_ARGB8888(void)
};
uint32_t dstArray[2 * 3] = {0};
uint32_t expectedArray[2 * 3] = {
0x444A4B4C, 0x111A1B1C,
0x555A5B5C, 0x222A2B2C,
0x666A6B6C, 0x333A3B3C
0x333A3B3C, 0x666A6B6C,
0x222A2B2C, 0x555A5B5C,
0x111A1B1C, 0x444A4B4C,
};
lv_draw_sw_rotate(srcArray, dstArray,
@@ -206,11 +210,10 @@ void test_rotate270_ARGB8888(void)
};
uint32_t dstArray[2 * 3] = {0};
uint32_t expectedArray[2 * 3] = {
0x333A3B3C, 0x666A6B6C,
0x222A2B2C, 0x555A5B5C,
0x111A1B1C, 0x444A4B4C,
0x444A4B4C, 0x111A1B1C,
0x555A5B5C, 0x222A2B2C,
0x666A6B6C, 0x333A3B3C
};
lv_draw_sw_rotate(srcArray, dstArray,
3, 2,
3 * sizeof(uint32_t),