From 13770a2660256ad3e4e3cdc81e9622635a6552e1 Mon Sep 17 00:00:00 2001 From: zhouli Date: Thu, 14 Jul 2022 20:29:08 +0800 Subject: [PATCH] rgb_lcd: support mirror and swap axis --- components/esp_lcd/src/esp_lcd_rgb_panel.c | 257 +++++++++++++++++- .../test_apps/rgb_lcd/main/test_rgb_panel.c | 31 +++ 2 files changed, 275 insertions(+), 13 deletions(-) diff --git a/components/esp_lcd/src/esp_lcd_rgb_panel.c b/components/esp_lcd/src/esp_lcd_rgb_panel.c index 267bec453d..61abf5f1a9 100644 --- a/components/esp_lcd/src/esp_lcd_rgb_panel.c +++ b/components/esp_lcd/src/esp_lcd_rgb_panel.c @@ -31,6 +31,7 @@ #include "hal/gpio_hal.h" #include "esp_private/gdma.h" #include "driver/gpio.h" +#include "esp_bit_defs.h" #include "esp_private/periph_ctrl.h" #if CONFIG_SPIRAM #include "esp_psram.h" @@ -48,6 +49,16 @@ #define LCD_RGB_INTR_ALLOC_FLAGS ESP_INTR_FLAG_INTRDISABLED #endif +#define RGB_PANEL_SWAP_XY 0 +#define RGB_PANEL_MIRROR_Y 1 +#define RGB_PANEL_MIRROR_X 2 + +typedef enum { + ROTATE_MASK_SWAP_XY = BIT(RGB_PANEL_SWAP_XY), + ROTATE_MASK_MIRROR_Y = BIT(RGB_PANEL_MIRROR_Y), + ROTATE_MASK_MIRROR_X = BIT(RGB_PANEL_MIRROR_X), +} panel_rotate_mask_t; + static const char *TAG = "lcd_panel.rgb"; typedef struct esp_rgb_panel_t esp_rgb_panel_t; @@ -96,6 +107,7 @@ struct esp_rgb_panel_t { int y_gap; // Extra gap in y coordinate, it's used when calculate the flush window portMUX_TYPE spinlock; // to protect panel specific resource from concurrent access (e.g. between task and ISR) int lcd_clk_flags; // LCD clock calculation flags + int rotate_mask; // panel rotate_mask mask, Or'ed of `panel_rotate_mask_t` struct { uint32_t disp_en_level: 1; // The level which can turn on the screen by `disp_gpio_num` uint32_t stream_mode: 1; // If set, the LCD transfers data continuously, otherwise, it stops refreshing the LCD when transaction done @@ -436,6 +448,21 @@ static esp_err_t rgb_panel_init(esp_lcd_panel_t *panel) return ret; } +__attribute__((always_inline)) +static inline void copy_pixel_16bpp(uint8_t *to, const uint8_t *from) +{ + *to++ = *from++; + *to++ = *from++; +} + +__attribute__((always_inline)) +static inline void copy_pixel_24bpp(uint8_t *to, const uint8_t *from) +{ + *to++ = *from++; + *to++ = *from++; + *to++ = *from++; +} + static esp_err_t rgb_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data) { esp_rgb_panel_t *rgb_panel = __containerof(panel, esp_rgb_panel_t, base); @@ -459,33 +486,231 @@ static esp_err_t rgb_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int x_end += rgb_panel->x_gap; y_end += rgb_panel->y_gap; // round the boundary - x_start = MIN(x_start, rgb_panel->timings.h_res); - x_end = MIN(x_end, rgb_panel->timings.h_res); - y_start = MIN(y_start, rgb_panel->timings.v_res); - y_end = MIN(y_end, rgb_panel->timings.v_res); + int h_res = rgb_panel->timings.h_res; + int v_res = rgb_panel->timings.v_res; + if (rgb_panel->rotate_mask & ROTATE_MASK_SWAP_XY) { + x_start = MIN(x_start, v_res); + x_end = MIN(x_end, v_res); + y_start = MIN(y_start, h_res); + y_end = MIN(y_end, h_res); + } else { + x_start = MIN(x_start, h_res); + x_end = MIN(x_end, h_res); + y_start = MIN(y_start, v_res); + y_end = MIN(y_end, v_res); + } int bytes_per_pixel = rgb_panel->bits_per_pixel / 8; int pixels_per_line = rgb_panel->timings.h_res; uint32_t bytes_per_line = bytes_per_pixel * pixels_per_line; uint8_t *fb = rgb_panel->fbs[rgb_panel->cur_fb_index]; + uint32_t bytes_to_flush = v_res * h_res * bytes_per_pixel; + uint8_t *flush_ptr = fb; if (do_copy) { // copy the UI draw buffer into internal frame buffer const uint8_t *from = (const uint8_t *)color_data; uint32_t copy_bytes_per_line = (x_end - x_start) * bytes_per_pixel; - uint8_t *to = fb + (y_start * pixels_per_line + x_start) * bytes_per_pixel; - for (int y = y_start; y < y_end; y++) { - memcpy(to, from, copy_bytes_per_line); - to += bytes_per_line; - from += copy_bytes_per_line; + size_t offset = y_start * copy_bytes_per_line + x_start * bytes_per_pixel; + uint8_t *to = fb; + if (2 == bytes_per_pixel) { + switch (rgb_panel->rotate_mask) { + case 0: { + uint8_t *to = fb + (y_start * h_res + x_start) * bytes_per_pixel; + for (int y = y_start; y < y_end; y++) { + memcpy(to, from, copy_bytes_per_line); + to += bytes_per_line; + from += copy_bytes_per_line; + } + bytes_to_flush = (y_end - y_start) * bytes_per_line; + flush_ptr = fb + y_start * bytes_per_line; + } + break; + case ROTATE_MASK_MIRROR_X: + for (int y = y_start; y < y_end; y++) { + uint32_t index = (y * h_res + (h_res - 1 - x_start)) * bytes_per_pixel; + for (size_t x = x_start; x < x_end; x++) { + copy_pixel_16bpp(to + index, from); + index -= bytes_per_pixel; + from += bytes_per_pixel; + } + } + bytes_to_flush = (y_end - y_start) * bytes_per_line; + flush_ptr = fb + y_start * bytes_per_line; + break; + case ROTATE_MASK_MIRROR_Y: { + uint8_t *to = fb + ((v_res - 1 - y_start) * h_res + x_start) * bytes_per_pixel; + for (int y = y_start; y < y_end; y++) { + memcpy(to, from, copy_bytes_per_line); + to -= bytes_per_line; + from += copy_bytes_per_line; + } + bytes_to_flush = (y_end - y_start) * bytes_per_line; + flush_ptr = fb + (v_res - y_end) * bytes_per_line; + } + break; + case ROTATE_MASK_MIRROR_X | ROTATE_MASK_MIRROR_Y: + for (int y = y_start; y < y_end; y++) { + uint32_t index = ((v_res - 1 - y) * h_res + (h_res - 1 - x_start)) * bytes_per_pixel; + for (size_t x = x_start; x < x_end; x++) { + copy_pixel_16bpp(to + index, from); + index -= bytes_per_pixel; + from += bytes_per_pixel; + } + } + bytes_to_flush = (y_end - y_start) * bytes_per_line; + flush_ptr = fb + (v_res - y_end) * bytes_per_line; + break; + case ROTATE_MASK_SWAP_XY: + for (int y = y_start; y < y_end; y++) { + for (int x = x_start; x < x_end; x++) { + uint32_t j = y * copy_bytes_per_line + x * bytes_per_pixel - offset; + uint32_t i = (x * h_res + y) * bytes_per_pixel; + copy_pixel_16bpp(to + i, from + j); + } + } + bytes_to_flush = (x_end - x_start) * bytes_per_line; + flush_ptr = fb + x_start * bytes_per_line; + break; + case ROTATE_MASK_SWAP_XY | ROTATE_MASK_MIRROR_X: + for (int y = y_start; y < y_end; y++) { + for (int x = x_start; x < x_end; x++) { + uint32_t j = y * copy_bytes_per_line + x * bytes_per_pixel - offset; + uint32_t i = (x * h_res + h_res - 1 - y) * bytes_per_pixel; + copy_pixel_16bpp(to + i, from + j); + } + } + bytes_to_flush = (x_end - x_start) * bytes_per_line; + flush_ptr = fb + x_start * bytes_per_line; + break; + case ROTATE_MASK_SWAP_XY | ROTATE_MASK_MIRROR_Y: + for (int y = y_start; y < y_end; y++) { + for (int x = x_start; x < x_end; x++) { + uint32_t j = y * copy_bytes_per_line + x * bytes_per_pixel - offset; + uint32_t i = ((v_res - 1 - x) * h_res + y) * bytes_per_pixel; + copy_pixel_16bpp(to + i, from + j); + } + } + bytes_to_flush = (x_end - x_start) * bytes_per_line; + flush_ptr = fb + (v_res - x_end) * bytes_per_line; + break; + case ROTATE_MASK_SWAP_XY | ROTATE_MASK_MIRROR_X | ROTATE_MASK_MIRROR_Y: + for (int y = y_start; y < y_end; y++) { + for (int x = x_start; x < x_end; x++) { + uint32_t j = y * copy_bytes_per_line + x * bytes_per_pixel - offset; + uint32_t i = ((v_res - 1 - x) * h_res + h_res - 1 - y) * bytes_per_pixel; + copy_pixel_16bpp(to + i, from + j); + } + } + bytes_to_flush = (x_end - x_start) * bytes_per_line; + flush_ptr = fb + (v_res - x_end) * bytes_per_line; + break; + default: + break; + } + } else if (3 == bytes_per_pixel) { + switch (rgb_panel->rotate_mask) { + case 0: { + uint8_t *to = fb + (y_start * h_res + x_start) * bytes_per_pixel; + for (int y = y_start; y < y_end; y++) { + memcpy(to, from, copy_bytes_per_line); + to += bytes_per_line; + from += copy_bytes_per_line; + } + bytes_to_flush = (y_end - y_start) * bytes_per_line; + flush_ptr = fb + y_start * bytes_per_line; + } + break; + case ROTATE_MASK_MIRROR_X: + for (int y = y_start; y < y_end; y++) { + uint32_t index = (y * h_res + (h_res - 1 - x_start)) * bytes_per_pixel; + for (size_t x = x_start; x < x_end; x++) { + copy_pixel_24bpp(to + index, from); + index -= bytes_per_pixel; + from += bytes_per_pixel; + } + } + bytes_to_flush = (y_end - y_start) * bytes_per_line; + flush_ptr = fb + y_start * bytes_per_line; + break; + case ROTATE_MASK_MIRROR_Y: { + uint8_t *to = fb + ((v_res - 1 - y_start) * h_res + x_start) * bytes_per_pixel; + for (int y = y_start; y < y_end; y++) { + memcpy(to, from, copy_bytes_per_line); + to -= bytes_per_line; + from += copy_bytes_per_line; + } + bytes_to_flush = (y_end - y_start) * bytes_per_line; + flush_ptr = fb + (v_res - y_end) * bytes_per_line; + } + break; + case ROTATE_MASK_MIRROR_X | ROTATE_MASK_MIRROR_Y: + for (int y = y_start; y < y_end; y++) { + uint32_t index = ((v_res - 1 - y) * h_res + (h_res - 1 - x_start)) * bytes_per_pixel; + for (size_t x = x_start; x < x_end; x++) { + copy_pixel_24bpp(to + index, from); + index -= bytes_per_pixel; + from += bytes_per_pixel; + } + } + bytes_to_flush = (y_end - y_start) * bytes_per_line; + flush_ptr = fb + (v_res - y_end) * bytes_per_line; + break; + case ROTATE_MASK_SWAP_XY: + for (int y = y_start; y < y_end; y++) { + for (int x = x_start; x < x_end; x++) { + uint32_t j = y * copy_bytes_per_line + x * bytes_per_pixel - offset; + uint32_t i = (x * h_res + y) * bytes_per_pixel; + copy_pixel_24bpp(to + i, from + j); + } + } + bytes_to_flush = (x_end - x_start) * bytes_per_line; + flush_ptr = fb + x_start * bytes_per_line; + break; + case ROTATE_MASK_SWAP_XY | ROTATE_MASK_MIRROR_X: + for (int y = y_start; y < y_end; y++) { + for (int x = x_start; x < x_end; x++) { + uint32_t j = y * copy_bytes_per_line + x * bytes_per_pixel - offset; + uint32_t i = (x * h_res + h_res - 1 - y) * bytes_per_pixel; + copy_pixel_24bpp(to + i, from + j); + } + } + bytes_to_flush = (x_end - x_start) * bytes_per_line; + flush_ptr = fb + x_start * bytes_per_line; + break; + case ROTATE_MASK_SWAP_XY | ROTATE_MASK_MIRROR_Y: + for (int y = y_start; y < y_end; y++) { + for (int x = x_start; x < x_end; x++) { + uint32_t j = y * copy_bytes_per_line + x * bytes_per_pixel - offset; + uint32_t i = ((v_res - 1 - x) * h_res + y) * bytes_per_pixel; + copy_pixel_24bpp(to + i, from + j); + } + } + bytes_to_flush = (x_end - x_start) * bytes_per_line; + flush_ptr = fb + (v_res - x_end) * bytes_per_line; + break; + case ROTATE_MASK_SWAP_XY | ROTATE_MASK_MIRROR_X | ROTATE_MASK_MIRROR_Y: + for (int y = y_start; y < y_end; y++) { + for (int x = x_start; x < x_end; x++) { + uint32_t j = y * copy_bytes_per_line + x * bytes_per_pixel - offset; + uint32_t i = ((v_res - 1 - x) * h_res + h_res - 1 - y) * bytes_per_pixel; + copy_pixel_24bpp(to + i, from + j); + } + } + bytes_to_flush = (x_end - x_start) * bytes_per_line; + flush_ptr = fb + (v_res - x_end) * bytes_per_line; + break; + default: + break; + } } + } if (rgb_panel->flags.fb_in_psram && !rgb_panel->bb_size) { // CPU writes data to PSRAM through DCache, data in PSRAM might not get updated, so write back // Note that if we use a bounce buffer, the data gets read by the CPU as well so no need to write back - uint32_t bytes_to_flush = (y_end - y_start) * bytes_per_line; - Cache_WriteBack_Addr((uint32_t)(fb + y_start * bytes_per_line), bytes_to_flush); + Cache_WriteBack_Addr((uint32_t)(flush_ptr), bytes_to_flush); } if (!rgb_panel->bb_size) { @@ -513,12 +738,18 @@ static esp_err_t rgb_panel_invert_color(esp_lcd_panel_t *panel, bool invert_colo static esp_err_t rgb_panel_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) { - return ESP_ERR_NOT_SUPPORTED; + esp_rgb_panel_t *rgb_panel = __containerof(panel, esp_rgb_panel_t, base); + rgb_panel->rotate_mask &= ~(ROTATE_MASK_MIRROR_X | ROTATE_MASK_MIRROR_Y); + rgb_panel->rotate_mask |= (mirror_x << RGB_PANEL_MIRROR_X | mirror_y << RGB_PANEL_MIRROR_Y); + return ESP_OK; } static esp_err_t rgb_panel_swap_xy(esp_lcd_panel_t *panel, bool swap_axes) { - return ESP_ERR_NOT_SUPPORTED; + esp_rgb_panel_t *rgb_panel = __containerof(panel, esp_rgb_panel_t, base); + rgb_panel->rotate_mask &= ~(ROTATE_MASK_SWAP_XY); + rgb_panel->rotate_mask |= swap_axes << RGB_PANEL_SWAP_XY; + return ESP_OK; } static esp_err_t rgb_panel_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap) diff --git a/components/esp_lcd/test_apps/rgb_lcd/main/test_rgb_panel.c b/components/esp_lcd/test_apps/rgb_lcd/main/test_rgb_panel.c index 91e0dc41ed..687d5077a5 100644 --- a/components/esp_lcd/test_apps/rgb_lcd/main/test_rgb_panel.c +++ b/components/esp_lcd/test_apps/rgb_lcd/main/test_rgb_panel.c @@ -11,6 +11,7 @@ #include "esp_lcd_panel_rgb.h" #include "esp_lcd_panel_ops.h" #include "esp_random.h" +#include "esp_timer.h" #include "esp_attr.h" #include "spi_flash_mmap.h" #include "test_rgb_board.h" @@ -212,6 +213,36 @@ TEST_CASE("lcd_rgb_panel_update_pclk", "[lcd]") free(img); } +TEST_CASE("lcd_rgb_panel_rotate", "[lcd]") +{ + const int w = 200; + const int h = 100; + uint64_t t = 0; + uint8_t *img = malloc(w * h * sizeof(uint16_t)); + TEST_ASSERT_NOT_NULL(img); + uint8_t color_byte = esp_random() & 0xFF; + memset(img, color_byte, w * h * sizeof(uint16_t)); + + printf("initialize RGB panel with stream mode\r\n"); + esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, 16, 0, false, NULL, NULL); + + printf("Update the rotation of panel\r\n"); + for (size_t i = 0; i < 8; i++) { + esp_lcd_panel_swap_xy(panel_handle, i & 4); + esp_lcd_panel_mirror(panel_handle, i & 2, i & 1); + printf("Panel Rotation=%d\r\n", i); + t = esp_timer_get_time(); + esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, w, h, img); + t = esp_timer_get_time() - t; + printf("@resolution %dx%d time per frame=%.2fMS\r\n", w, h, (float)t / 1000.0f); + vTaskDelay(pdMS_TO_TICKS(1000)); + } + + printf("delete RGB panel\r\n"); + TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); + free(img); +} + #if CONFIG_LCD_RGB_ISR_IRAM_SAFE TEST_LCD_CALLBACK_ATTR static bool test_rgb_panel_count_in_callback(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx) {