Skip to content

Add basic OSD support #188

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 135 additions & 3 deletions components/retro-go/rg_display.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@
static rg_task_t *display_task_queue;
static rg_display_counters_t counters;
static rg_display_config_t config;
static rg_surface_t *osd;
static rg_surface_t *border;
static rg_display_t display;
static int16_t map_viewport_to_source_x[RG_SCREEN_WIDTH + 1];
static int16_t map_viewport_to_source_y[RG_SCREEN_HEIGHT + 1];
static uint32_t screen_line_checksum[RG_SCREEN_HEIGHT + 1];

// OSD Surface Management
static bool osd_enabled = false;
static rg_osd_t osd;

#define LINE_IS_REPEATED(Y) (map_viewport_to_source_y[(Y)] == map_viewport_to_source_y[(Y) - 1])
// This is to avoid flooring a number that is approximated to .9999999 and be explicit about it
#define FLOAT_TO_INT(x) ((int)((x) + 0.1f))
Expand All @@ -36,6 +39,74 @@ static const char *SETTING_CUSTOM_ZOOM = "DispCustomZoom";
#include "drivers/display/dummy.h"
#endif

// TODO : make it more user-friendly -> instead of specifying a rect and a surface
// just specify the corner and the dimensions (width and height)
rg_surface_t *rg_display_init_osd(rg_corner_t corner, int width, int height, bool has_transparency)
{
// clipping if osd > display
width = RG_MIN(width, display.screen.width);
height = RG_MIN(height, display.screen.height);

osd.has_transparency = has_transparency;

int left, top;
switch (corner)
{
case CORNER_TOP_LEFT:
left = 0;
top = 0;
break;
case CORNER_TOP_RIGHT:
left = display.screen.width - width;
top = 0;
break;
case CORNER_BOTTOM_LEFT:
left = 0;
top = display.screen.height - height;
break;
case CORNER_BOTTOM_RIGHT:
left = display.screen.width - width;
top = display.screen.height - height;
break;
default:
left = 0;
top = 0;
break;
}

osd.surface = rg_surface_create(width, height, RG_PIXEL_565_LE, MEM_SLOW);

osd.left = left;
osd.top = top;

if (has_transparency)
{ // we fill the background with the "transparent color"
uint16_t *buffer = osd.surface->data; // Treat the buffer as 16-bit values
for (int i = 0; i < osd.surface->width * osd.surface->height; i++)
buffer[i] = C_TRANSPARENT; // Assign the C_TRANSPARENT color directly to the background
Comment on lines +84 to +86
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have finally implemented rg_surface_fill, you should now be able to do rg_surface_fill(osd.surface, NULL, C_TRANSPARENT);

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although I'm not able to test my own code right now so maybe best don't waste your time...

}

osd_enabled = 1; // Enable OSD if surface is valid
return osd.surface;
}

void rg_display_set_osd_enabled(bool enabled)
{
osd_enabled = enabled;
rg_display_force_redraw();
}

bool rg_display_is_osd_enabled()
{
return osd_enabled;
}

void deinit_osd()
{
rg_surface_free(osd.surface); // Free the OSD surface
osd_enabled = false;
}

static inline unsigned blend_pixels(unsigned a, unsigned b)
{
// Fast path (taken 80-90% of the time)
Expand Down Expand Up @@ -205,10 +276,71 @@ static inline void write_update(const rg_surface_t *update)
lines_remaining -= lines_to_copy;
}

if (osd != NULL)
if (osd_enabled)
{
// TODO: Draw on screen display. By default it should be bottom left which is fine
// for both virtual keyboard and info labels. Maybe make it configurable later...
const int *buffer = osd.surface->data;
RG_ASSERT_ARG(buffer);

const int width = osd.surface->width;
const int height = osd.surface->height;

const int min_width = RG_MIN(draw_width, osd.surface->width);
const int min_height = RG_MIN(draw_height, osd.surface->height);

const int x0 = RG_MAX(draw_left, osd.left);
const int y0 = RG_MAX(draw_top, osd.top);

lcd_set_window(osd.left + display.screen.margin_left, osd.top + display.screen.margin_top, width, height);

// FIXME: only redraw when background has changed
for (size_t y = 0; y < height;)
{
uint16_t *line_buffer = lcd_get_buffer(LCD_BUFFER_LENGTH);
size_t num_lines = RG_MIN(LCD_BUFFER_LENGTH / width, height - y);

// Copy line by line because stride may not match width
for (size_t line = 0; line < num_lines; ++line)
{
uint16_t *osd_buffer = (void *)buffer + (y + line) * osd.surface->width * 2;
uint16_t *dst = line_buffer + (line * width);

#define OSD_TO_SCREEN(PTR_TYPE, BACKGROUND_PIXEL) { \
PTR_TYPE *viewport = (PTR_TYPE *)data; \
if(y + line >= draw_top){ \
viewport += ((map_viewport_to_source_y[y + line - draw_top] * stride) >> 1); \
} \
else{ \
viewport += ((map_viewport_to_source_y[y + line] * stride) >> 1); \
} \
for (size_t x = 0; x < width; ++x){ \
if(osd.has_transparency && osd_buffer[x] == C_TRANSPARENT){ \
if((y+line)>=y0 && (x-draw_left)>=x0 && (y+line)<=min_height && (x-draw_left)<=min_width){\
dst[x] = BACKGROUND_PIXEL; \
} \
else{ \
dst[x] = 0x0000; \
} \
} \
else{ \
dst[x] = (osd_buffer[x] >> 8) | (osd_buffer[x] << 8); \
} \
} \
}

if (format & RG_PIXEL_PALETTE)
OSD_TO_SCREEN(uint8_t, palette[viewport[map_viewport_to_source_x[x - draw_left]]])
else if (format == RG_PIXEL_565_LE)
OSD_TO_SCREEN(uint16_t, (viewport[map_viewport_to_source_x[x - draw_left]] >> 8) | (viewport[map_viewport_to_source_x[x - draw_left]] << 8))
else
OSD_TO_SCREEN(uint16_t, viewport[map_viewport_to_source_x[x - draw_left]])
}

lcd_send_buffer(line_buffer, width * num_lines);
y += num_lines;
}
lcd_sync();
}

if (lines_updated > draw_height * 0.80f)
Expand All @@ -230,7 +362,7 @@ static void update_viewport_scaling(void)
if (config.scaling == RG_DISPLAY_SCALING_FULL)
{
new_width = screen_width;
new_height = screen_height;
new_height = display.screen.height;
}
else if (config.scaling == RG_DISPLAY_SCALING_FIT)
{
Expand Down
20 changes: 20 additions & 0 deletions components/retro-go/rg_display.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,26 @@ typedef struct

#include "rg_surface.h"

typedef struct
{
rg_surface_t *surface;
bool has_transparency;
int top, left;
} rg_osd_t;

typedef enum
{
CORNER_TOP_LEFT = 0,
CORNER_TOP_RIGHT,
CORNER_BOTTOM_LEFT,
CORNER_BOTTOM_RIGHT
} rg_corner_t;

rg_surface_t *rg_display_init_osd(rg_corner_t corner, int width, int height, bool has_transparency);
void rg_display_set_osd_enabled(bool enabled);
bool rg_display_is_osd_enabled();
void deinit_osd();

void rg_display_init(void);
void rg_display_deinit(void);
void rg_display_write_rect(int left, int top, int width, int height, int stride, const uint16_t *buffer, uint32_t flags);
Expand Down
Loading