Skip to content

Commit

Permalink
drivers: display: sdl: Correcting display_read()
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
soburi committed Jan 11, 2024
1 parent b4d014a commit 90082c8
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 22 deletions.
168 changes: 157 additions & 11 deletions drivers/display/display_sdl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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)
Expand Down Expand Up @@ -66,10 +70,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_software_renderer, hide_window,
&disp_data->window, &disp_data->renderer,
&disp_data->texture);
int rc = sdl_display_init_bottom(config->height, config->width, sdl_display_zoom_pct,
use_software_renderer, &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");
Expand Down Expand Up @@ -250,28 +254,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");

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;
}

return sdl_display_read_bottom(desc->height, desc->width, x, y,
disp_data->renderer, buf, desc->pitch);
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)
Expand Down Expand Up @@ -341,7 +483,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 = {
Expand All @@ -361,8 +504,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, \
Expand Down
66 changes: 59 additions & 7 deletions drivers/display/display_sdl_bottom.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@

int sdl_display_init_bottom(uint16_t height, uint16_t width, uint16_t zoom_pct,
bool use_software_renderer, bool hide_window,
void **window, void **renderer, void **texture)
void **window, void **renderer, void **mutex,
void **texture, void **read_texture)
{
SDL_WindowFlags window_flag = hide_window ? SDL_WINDOW_HIDDEN : SDL_WINDOW_SHOWN;
*window = SDL_CreateWindow("Zephyr Display", SDL_WINDOWPOS_UNDEFINED,
Expand All @@ -36,15 +37,28 @@ 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,
SDL_TEXTUREACCESS_STATIC, width, height);
*texture = SDL_CreateTexture(*renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC,
width, height);
if (*texture == NULL) {
nsi_print_warning("Failed to create SDL texture: %s", SDL_GetError());
return -1;
}

*read_texture = SDL_CreateTexture(*renderer, SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_TARGET, width, height);
if (*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);
Expand All @@ -54,37 +68,64 @@ 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) {
SDL_RenderClear(renderer);
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)
Expand All @@ -100,13 +141,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;
Expand Down
11 changes: 7 additions & 4 deletions drivers/display/display_sdl_bottom.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,20 @@ extern "C" {

int sdl_display_init_bottom(uint16_t height, uint16_t width, uint16_t zoom_pct,
bool use_software_renderer, bool hide_window,
void **window, void **renderer, void **texture);
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
}
Expand Down

0 comments on commit 90082c8

Please sign in to comment.