drivers: display: sdl: Correcting display_read()

Fixed an issue where `display_read()` in the SDL driver was not working.

In the current implementation, use texture to represent screen images.
To read this, draw it once on another surface and then read it.

Signed-off-by: TOKITA Hiroshi <tokita.hiroshi@gmail.com>
This commit is contained in:
TOKITA Hiroshi 2024-01-13 06:40:13 +09:00 committed by Fabio Baltieri
parent a4de15ba1c
commit 321389df92
3 changed files with 221 additions and 20 deletions

View file

@ -10,6 +10,7 @@
#include <zephyr/drivers/display.h>
#include <string.h>
#include <stdlib.h>
#include <soc.h>
#include <zephyr/sys/byteorder.h>
#include "display_sdl_bottom.h"
@ -29,10 +30,13 @@ struct sdl_display_config {
struct sdl_display_data {
void *window;
void *renderer;
void *mutex;
void *texture;
void *read_texture;
bool display_on;
enum display_pixel_format current_pixel_format;
uint8_t *buf;
uint8_t *read_buf;
};
static int sdl_display_init(const struct device *dev)
@ -64,10 +68,10 @@ static int sdl_display_init(const struct device *dev)
sdl_display_zoom_pct = CONFIG_SDL_DISPLAY_ZOOM_PCT;
}
int rc = sdl_display_init_bottom(config->height, config->width,
sdl_display_zoom_pct, use_accelerator,
&disp_data->window, &disp_data->renderer,
&disp_data->texture);
int rc = sdl_display_init_bottom(config->height, config->width, sdl_display_zoom_pct,
use_accelerator, &disp_data->window, &disp_data->renderer,
&disp_data->mutex, &disp_data->texture,
&disp_data->read_texture);
if (rc != 0) {
LOG_ERR("Failed to create SDL display");
@ -248,28 +252,166 @@ static int sdl_display_write(const struct device *dev, const uint16_t x,
}
sdl_display_write_bottom(desc->height, desc->width, x, y,
disp_data->renderer, disp_data->texture,
disp_data->renderer, disp_data->mutex, disp_data->texture,
disp_data->buf, disp_data->display_on);
return 0;
}
static void sdl_display_read_argb8888(const uint8_t *read_buf,
const struct display_buffer_descriptor *desc, void *buf)
{
__ASSERT((desc->pitch * 4U * desc->height) <= desc->buf_size, "Read buffer is too small");
memcpy(buf, read_buf, desc->pitch * 4U * desc->height);
}
static void sdl_display_read_rgb888(const uint8_t *read_buf,
const struct display_buffer_descriptor *desc, void *buf)
{
uint32_t w_idx;
uint32_t h_idx;
uint8_t *buf8;
const uint32_t *pix_ptr;
__ASSERT((desc->pitch * 3U * desc->height) <= desc->buf_size, "Read buffer is too small");
for (h_idx = 0U; h_idx < desc->height; ++h_idx) {
buf8 = ((uint8_t *)buf) + desc->pitch * 3U * h_idx;
for (w_idx = 0U; w_idx < desc->width; ++w_idx) {
pix_ptr = (const uint32_t *)read_buf + ((h_idx * desc->pitch) + w_idx);
*buf8 = (*pix_ptr & 0xFF0000) >> 16;
buf8 += 1;
*buf8 = (*pix_ptr & 0xFF00) >> 8;
buf8 += 1;
*buf8 = (*pix_ptr & 0xFF);
buf8 += 1;
}
}
}
static void sdl_display_read_rgb565(const uint8_t *read_buf,
const struct display_buffer_descriptor *desc, void *buf)
{
uint32_t w_idx;
uint32_t h_idx;
uint16_t pixel;
uint16_t *buf16;
const uint32_t *pix_ptr;
__ASSERT((desc->pitch * 2U * desc->height) <= desc->buf_size, "Read buffer is too small");
for (h_idx = 0U; h_idx < desc->height; ++h_idx) {
buf16 = (void *)(((uint8_t *)buf) + desc->pitch * 2U * h_idx);
for (w_idx = 0U; w_idx < desc->width; ++w_idx) {
pix_ptr = (const uint32_t *)read_buf + ((h_idx * desc->pitch) + w_idx);
pixel = (*pix_ptr & 0xF80000) >> 8;
pixel |= (*pix_ptr & 0x00FC00) >> 5;
pixel |= (*pix_ptr & 0x0000F8) >> 3;
*buf16 = sys_be16_to_cpu(pixel);
buf16 += 1;
}
}
}
static void sdl_display_read_bgr565(const uint8_t *read_buf,
const struct display_buffer_descriptor *desc, void *buf)
{
uint32_t w_idx;
uint32_t h_idx;
uint16_t pixel;
uint16_t *buf16;
const uint32_t *pix_ptr;
__ASSERT((desc->pitch * 2U * desc->height) <= desc->buf_size, "Read buffer is too small");
for (h_idx = 0U; h_idx < desc->height; ++h_idx) {
buf16 = (void *)(((uint8_t *)buf) + desc->pitch * 2U * h_idx);
for (w_idx = 0U; w_idx < desc->width; ++w_idx) {
pix_ptr = (const uint32_t *)read_buf + ((h_idx * desc->pitch) + w_idx);
pixel = (*pix_ptr & 0xF80000) >> 8;
pixel |= (*pix_ptr & 0x00FC00) >> 5;
pixel |= (*pix_ptr & 0x0000F8) >> 3;
*buf16 = pixel;
buf16 += 1;
}
}
}
static void sdl_display_read_mono(const uint8_t *read_buf,
const struct display_buffer_descriptor *desc, void *buf,
const bool one_is_black)
{
uint32_t w_idx;
uint32_t h_idx;
uint32_t tile_idx;
uint8_t tile;
const uint32_t *pix_ptr;
uint8_t *buf8;
__ASSERT((desc->pitch * desc->height) <= (desc->buf_size * 8U), "Read buffer is too small");
__ASSERT((desc->height % 8U) == 0U, "Read buffer height not aligned per 8 pixels");
for (tile_idx = 0U; tile_idx < (desc->height / 8U); ++tile_idx) {
buf8 = (void *)(((uint8_t *)buf) + desc->pitch * tile_idx);
for (w_idx = 0U; w_idx < desc->width; ++w_idx) {
tile = 0;
for (h_idx = 0U; h_idx < 8; ++h_idx) {
pix_ptr = (const uint32_t *)read_buf +
((tile_idx * 8 + h_idx) * desc->pitch + w_idx);
if ((*pix_ptr)) {
tile |= BIT(7 - h_idx);
}
}
*buf8 = one_is_black ? ~tile : tile;
buf8 += 1;
}
}
}
static int sdl_display_read(const struct device *dev, const uint16_t x,
const uint16_t y,
const struct display_buffer_descriptor *desc,
void *buf)
{
struct sdl_display_data *disp_data = dev->data;
int err;
LOG_DBG("Reading %dx%d (w,h) bitmap @ %dx%d (x,y)", desc->width,
desc->height, x, y);
__ASSERT(desc->width <= desc->pitch, "Pitch is smaller then width");
__ASSERT((desc->pitch * 3U * desc->height) <= desc->buf_size,
"Input buffer to small");
__ASSERT(desc->width <= desc->pitch, "Pitch is smaller than width");
return sdl_display_read_bottom(desc->height, desc->width, x, y,
disp_data->renderer, buf, desc->pitch);
memset(disp_data->read_buf, 0, desc->pitch * desc->height * 4);
err = sdl_display_read_bottom(desc->height, desc->width, x, y, disp_data->renderer,
disp_data->read_buf, desc->pitch, disp_data->mutex,
disp_data->texture, disp_data->read_texture);
if (err) {
return err;
}
if (disp_data->current_pixel_format == PIXEL_FORMAT_ARGB_8888) {
sdl_display_read_argb8888(disp_data->read_buf, desc, buf);
} else if (disp_data->current_pixel_format == PIXEL_FORMAT_RGB_888) {
sdl_display_read_rgb888(disp_data->read_buf, desc, buf);
} else if (disp_data->current_pixel_format == PIXEL_FORMAT_MONO10) {
sdl_display_read_mono(disp_data->read_buf, desc, buf, true);
} else if (disp_data->current_pixel_format == PIXEL_FORMAT_MONO01) {
sdl_display_read_mono(disp_data->read_buf, desc, buf, false);
} else if (disp_data->current_pixel_format == PIXEL_FORMAT_RGB_565) {
sdl_display_read_rgb565(disp_data->read_buf, desc, buf);
} else if (disp_data->current_pixel_format == PIXEL_FORMAT_BGR_565) {
sdl_display_read_bgr565(disp_data->read_buf, desc, buf);
}
return 0;
}
static int sdl_display_blanking_off(const struct device *dev)
@ -339,7 +481,8 @@ static int sdl_display_set_pixel_format(const struct device *dev,
static void sdl_display_cleanup(struct sdl_display_data *disp_data)
{
sdl_display_cleanup_bottom(&disp_data->window, &disp_data->renderer, &disp_data->texture);
sdl_display_cleanup_bottom(&disp_data->window, &disp_data->renderer, &disp_data->mutex,
&disp_data->texture, &disp_data->read_texture);
}
static const struct display_driver_api sdl_display_api = {
@ -359,8 +502,11 @@ static const struct display_driver_api sdl_display_api = {
\
static uint8_t sdl_buf_##n[4 * DT_INST_PROP(n, height) \
* DT_INST_PROP(n, width)]; \
static uint8_t sdl_read_buf_##n[4 * DT_INST_PROP(n, height) \
* DT_INST_PROP(n, width)]; \
static struct sdl_display_data sdl_data_##n = { \
.buf = sdl_buf_##n, \
.read_buf = sdl_read_buf_##n, \
}; \
\
DEVICE_DT_INST_DEFINE(n, &sdl_display_init, NULL, \

View file

@ -12,7 +12,8 @@
#include "nsi_tracing.h"
int sdl_display_init_bottom(uint16_t height, uint16_t width, uint16_t zoom_pct,
bool use_accelerator, void **window, void **renderer, void **texture)
bool use_accelerator, void **window, void **renderer, void **mutex,
void **texture, void **read_texture)
{
*window = SDL_CreateWindow("Zephyr Display", SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, width * zoom_pct / 100,
@ -34,6 +35,12 @@ int sdl_display_init_bottom(uint16_t height, uint16_t width, uint16_t zoom_pct,
return -1;
}
*mutex = SDL_CreateMutex();
if (*mutex == NULL) {
nsi_print_warning("Failed to create SDL mutex: %s", SDL_GetError());
return -1;
}
SDL_RenderSetLogicalSize(*renderer, width, height);
*texture = SDL_CreateTexture(*renderer, SDL_PIXELFORMAT_ARGB8888,
@ -43,6 +50,13 @@ int sdl_display_init_bottom(uint16_t height, uint16_t width, uint16_t zoom_pct,
return -1;
}
*read_texture = SDL_CreateTexture(*renderer, SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_TARGET, width, height);
if (*read_texture == NULL) {
nsi_print_warning("Failed to create SDL texture for read: %s", SDL_GetError());
return -1;
}
SDL_SetRenderDrawColor(*renderer, 0, 0, 0, 0xFF);
SDL_RenderClear(*renderer);
SDL_RenderPresent(*renderer);
@ -52,16 +66,23 @@ int sdl_display_init_bottom(uint16_t height, uint16_t width, uint16_t zoom_pct,
void sdl_display_write_bottom(const uint16_t height, const uint16_t width,
const uint16_t x, const uint16_t y,
void *renderer, void *texture,
void *renderer, void *mutex, void *texture,
uint8_t *buf, bool display_on)
{
SDL_Rect rect;
int err;
rect.x = x;
rect.y = y;
rect.w = width;
rect.h = height;
err = SDL_TryLockMutex(mutex);
if (err) {
nsi_print_warning("Failed to lock SDL mutex: %s", SDL_GetError());
return;
}
SDL_UpdateTexture(texture, &rect, buf, 4 * rect.w);
if (display_on) {
@ -69,20 +90,40 @@ void sdl_display_write_bottom(const uint16_t height, const uint16_t width,
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
}
SDL_UnlockMutex(mutex);
}
int sdl_display_read_bottom(const uint16_t height, const uint16_t width,
const uint16_t x, const uint16_t y,
void *renderer, void *buf, uint16_t pitch)
void *renderer, void *buf, uint16_t pitch,
void *mutex, void *texture, void *read_texture)
{
SDL_Rect rect;
int err;
rect.x = x;
rect.y = y;
rect.w = width;
rect.h = height;
return SDL_RenderReadPixels(renderer, &rect, 0, buf, pitch * 4U);
err = SDL_TryLockMutex(mutex);
if (err) {
nsi_print_warning("Failed to lock SDL mutex: %s", SDL_GetError());
return -1;
}
SDL_SetRenderTarget(renderer, read_texture);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderReadPixels(renderer, &rect, SDL_PIXELFORMAT_ARGB8888, buf, width * 4);
SDL_SetRenderTarget(renderer, NULL);
SDL_UnlockMutex(mutex);
return err;
}
void sdl_display_blanking_off_bottom(void *renderer, void *texture)
@ -98,13 +139,24 @@ void sdl_display_blanking_on_bottom(void *renderer)
SDL_RenderPresent(renderer);
}
void sdl_display_cleanup_bottom(void **window, void **renderer, void **texture)
void sdl_display_cleanup_bottom(void **window, void **renderer, void **mutex, void **texture,
void **read_texture)
{
if (*read_texture != NULL) {
SDL_DestroyTexture(*read_texture);
*read_texture = NULL;
}
if (*texture != NULL) {
SDL_DestroyTexture(*texture);
*texture = NULL;
}
if (*mutex != NULL) {
SDL_DestroyMutex(*mutex);
*mutex = NULL;
}
if (*renderer != NULL) {
SDL_DestroyRenderer(*renderer);
*renderer = NULL;

View file

@ -21,17 +21,20 @@ extern "C" {
/* Note: None of these functions are public interfaces. But internal to the SDL display driver */
int sdl_display_init_bottom(uint16_t height, uint16_t width, uint16_t zoom_pct,
bool use_accelerator, void **window, void **renderer, void **texture);
bool use_accelerator, void **window, void **renderer, void **mutex,
void **texture, void **read_texture);
void sdl_display_write_bottom(const uint16_t height, const uint16_t width,
const uint16_t x, const uint16_t y,
void *renderer, void *texture,
void *renderer, void *mutex, void *texture,
uint8_t *buf, bool display_on);
int sdl_display_read_bottom(const uint16_t height, const uint16_t width,
const uint16_t x, const uint16_t y,
void *renderer, void *buf, uint16_t pitch);
void *renderer, void *buf, uint16_t pitch,
void *mutex, void *texture, void **read_texture);
void sdl_display_blanking_off_bottom(void *renderer, void *texture);
void sdl_display_blanking_on_bottom(void *renderer);
void sdl_display_cleanup_bottom(void **window, void **renderer, void **texture);
void sdl_display_cleanup_bottom(void **window, void **renderer, void **mutex, void **texture,
void **read_texture);
#ifdef __cplusplus
}