rg_surface: Replaced custom scaling by a zoom factor that preserves aspect ratio and can go over 1x
This commit is contained in:
parent
444521f41c
commit
9eb04c1efc
@ -24,15 +24,18 @@ 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];
|
||||
|
||||
#define LINE_IS_REPEATED(Y) (map_viewport_to_source_y[(Y)] == map_viewport_to_source_y[(Y) - 1])
|
||||
|
||||
static const char *SETTING_BACKLIGHT = "DispBacklight";
|
||||
static const char *SETTING_SCALING = "DispScaling";
|
||||
static const char *SETTING_FILTER = "DispFilter";
|
||||
static const char *SETTING_ROTATION = "DispRotation";
|
||||
static const char *SETTING_BORDER = "DispBorder";
|
||||
static const char *SETTING_CUSTOM_WIDTH = "DispCustomWidth";
|
||||
static const char *SETTING_CUSTOM_HEIGHT = "DispCustomHeight";
|
||||
static const char *SETTING_CUSTOM_ZOOM = "DispCustomZoom";
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
static spi_device_handle_t spi_dev;
|
||||
@ -284,78 +287,68 @@ static void lcd_deinit(void)
|
||||
|
||||
static inline unsigned blend_pixels(unsigned a, unsigned b)
|
||||
{
|
||||
// Fast path
|
||||
// Fast path (taken 80-90% of the time)
|
||||
if (a == b)
|
||||
return a;
|
||||
|
||||
// Input in Big-Endian, swap to Little Endian
|
||||
// Not the original author, but a good explanation is found at:
|
||||
// https://medium.com/@luc.trudeau/fast-averaging-of-high-color-16-bit-pixels-cb4ac7fd1488
|
||||
a = (a << 8) | (a >> 8);
|
||||
b = (b << 8) | (b >> 8);
|
||||
|
||||
// Signed arithmetic is deliberate here
|
||||
int r0 = (a >> 11) & 0x1F;
|
||||
int g0 = (a >> 5) & 0x3F;
|
||||
int b0 = (a) & 0x1F;
|
||||
|
||||
int r1 = (b >> 11) & 0x1F;
|
||||
int g1 = (b >> 5) & 0x3F;
|
||||
int b1 = (b) & 0x1F;
|
||||
|
||||
int rv = (((r1 - r0) >> 1) + r0);
|
||||
int gv = (((g1 - g0) >> 1) + g0);
|
||||
int bv = (((b1 - b0) >> 1) + b0);
|
||||
|
||||
unsigned v = (rv << 11) | (gv << 5) | (bv);
|
||||
|
||||
// Back to Big-Endian
|
||||
unsigned s = a ^ b;
|
||||
unsigned v = ((s & 0xF7DEU) >> 1) + (a & b) + (s & 0x0821U);
|
||||
return (v << 8) | (v >> 8);
|
||||
|
||||
// This is my attempt at averaging two 565BE values without swapping bytes (3x the speed of the code above)
|
||||
// return (((a ^ b) & 0b1101111011110110U) >> 1) + (a & b);
|
||||
}
|
||||
|
||||
static inline void write_update(const rg_surface_t *update)
|
||||
{
|
||||
const int64_t time_start = rg_system_timer();
|
||||
|
||||
const int draw_left = display.viewport.left;
|
||||
const int draw_top = display.viewport.top;
|
||||
const int draw_width = display.viewport.width;
|
||||
const int draw_height = display.viewport.height;
|
||||
bool filter_x = display.viewport.filter_x;
|
||||
bool filter_y = display.viewport.filter_y;
|
||||
int draw_left = display.viewport.left;
|
||||
int draw_top = display.viewport.top;
|
||||
int draw_width = display.viewport.width;
|
||||
int draw_height = display.viewport.height;
|
||||
|
||||
const bool filter_y = (config.filter == RG_DISPLAY_FILTER_VERT || config.filter == RG_DISPLAY_FILTER_BOTH) &&
|
||||
(config.scaling && (display.viewport.height % display.source.height) != 0);
|
||||
const bool filter_x = (config.filter == RG_DISPLAY_FILTER_HORIZ || config.filter == RG_DISPLAY_FILTER_BOTH) &&
|
||||
(config.scaling && (display.viewport.width % display.source.width) != 0);
|
||||
int crop_left = 0;
|
||||
int crop_top = 0;
|
||||
|
||||
if (draw_left < 0)
|
||||
{
|
||||
crop_left += -draw_left * display.viewport.step_x;
|
||||
draw_width += draw_left * 2;
|
||||
draw_left = 0;
|
||||
}
|
||||
|
||||
if (draw_top < 0)
|
||||
{
|
||||
crop_top += -draw_top * display.viewport.step_y;
|
||||
draw_height += draw_top * 2;
|
||||
draw_top = 0;
|
||||
}
|
||||
|
||||
const int format = update->format;
|
||||
const int stride = update->stride;
|
||||
const void *data = update->data + (display.source.crop_v * stride) + (display.source.crop_h * RG_PIXEL_GET_SIZE(format));
|
||||
const void *data = update->data + update->offset + (crop_top * stride) + (crop_left * RG_PIXEL_GET_SIZE(format));
|
||||
const uint16_t *palette = update->palette;
|
||||
|
||||
bool partial = true; // config.update_mode == RG_DISPLAY_UPDATE_PARTIAL;
|
||||
|
||||
int lines_per_buffer = LCD_BUFFER_LENGTH / draw_width;
|
||||
int lines_remaining = draw_height;
|
||||
int lines_updated = 0;
|
||||
int window_top = -1;
|
||||
|
||||
float step_x = (float)display.source.width / draw_width;
|
||||
float step_y = (float)display.source.height / draw_height;
|
||||
|
||||
// Maybe it's worth using regular ints here? It's just 1KB more on the stack...
|
||||
int16_t map_viewport_to_source_x[draw_width];
|
||||
int16_t map_viewport_to_source_y[draw_height];
|
||||
|
||||
for (int x = 0; x < draw_width; ++x)
|
||||
map_viewport_to_source_x[x] = x * step_x + 0.1f;
|
||||
|
||||
for (int y = 0; y < draw_height; ++y)
|
||||
map_viewport_to_source_y[y] = y * step_y + 0.1f;
|
||||
|
||||
#define LINE_IS_REPEATED(Y) (map_viewport_to_source_y[(Y)] == map_viewport_to_source_y[(Y) - 1])
|
||||
bool partial = true;
|
||||
|
||||
for (int y = 0; y < draw_height;)
|
||||
{
|
||||
int lines_to_copy = RG_MIN(lines_per_buffer, lines_remaining);
|
||||
|
||||
if (lines_to_copy < 1)
|
||||
break;
|
||||
|
||||
// The vertical filter requires a block to start and end with unscaled lines
|
||||
if (filter_y)
|
||||
{
|
||||
@ -364,11 +357,6 @@ static inline void write_update(const rg_surface_t *update)
|
||||
--lines_to_copy;
|
||||
}
|
||||
|
||||
if (lines_to_copy < 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t *line_buffer = lcd_get_buffer();
|
||||
uint16_t *line_buffer_ptr = line_buffer;
|
||||
|
||||
@ -484,52 +472,55 @@ static void update_viewport_scaling(void)
|
||||
int src_height = display.source.height;
|
||||
int new_width = src_width;
|
||||
int new_height = src_height;
|
||||
double new_ratio = 0.0;
|
||||
|
||||
if (config.scaling == RG_DISPLAY_SCALING_FULL)
|
||||
{
|
||||
new_ratio = display.screen.width / (double)display.screen.height;
|
||||
new_width = display.screen.height * new_ratio;
|
||||
new_width = display.screen.width;
|
||||
new_height = display.screen.height;
|
||||
}
|
||||
else if (config.scaling == RG_DISPLAY_SCALING_FIT)
|
||||
{
|
||||
new_ratio = src_width / (double)src_height;
|
||||
new_width = display.screen.height * new_ratio;
|
||||
new_width = display.screen.height * ((double)src_width / src_height);
|
||||
new_height = display.screen.height;
|
||||
if (new_width > display.screen.width) {
|
||||
new_width = display.screen.width;
|
||||
new_height = display.screen.width * ((double)src_height / src_width);
|
||||
}
|
||||
}
|
||||
else if (config.scaling == RG_DISPLAY_SCALING_CUSTOM)
|
||||
else if (config.scaling == RG_DISPLAY_SCALING_ZOOM)
|
||||
{
|
||||
new_ratio = config.custom_width / (double)config.custom_height;
|
||||
new_width = config.custom_width;
|
||||
new_height = config.custom_height;
|
||||
new_width = src_width * config.custom_zoom;
|
||||
new_height = src_height * config.custom_zoom;
|
||||
}
|
||||
|
||||
// Everything works better when we use even dimensions!
|
||||
new_width &= ~1;
|
||||
new_height &= ~1;
|
||||
|
||||
if (new_width > display.screen.width)
|
||||
{
|
||||
RG_LOGW("new_width too large: %d, cropping to %d", new_width, display.screen.width);
|
||||
new_width = display.screen.width;
|
||||
}
|
||||
|
||||
if (new_height > display.screen.height)
|
||||
{
|
||||
RG_LOGW("new_height too large: %d, cropping to %d", new_height, display.screen.height);
|
||||
new_height = display.screen.height;
|
||||
}
|
||||
|
||||
display.viewport.left = (display.screen.width - new_width) / 2;
|
||||
display.viewport.top = (display.screen.height - new_height) / 2;
|
||||
display.viewport.width = new_width;
|
||||
display.viewport.height = new_height;
|
||||
|
||||
display.viewport.step_x = (float)src_width / display.viewport.width;
|
||||
display.viewport.step_y = (float)src_height / display.viewport.height;
|
||||
|
||||
display.viewport.filter_x = (config.filter == RG_DISPLAY_FILTER_HORIZ || config.filter == RG_DISPLAY_FILTER_BOTH) &&
|
||||
(config.scaling && (display.viewport.width % src_width) != 0);
|
||||
display.viewport.filter_y = (config.filter == RG_DISPLAY_FILTER_VERT || config.filter == RG_DISPLAY_FILTER_BOTH) &&
|
||||
(config.scaling && (display.viewport.height % src_height) != 0);
|
||||
|
||||
memset(screen_line_checksum, 0, sizeof(screen_line_checksum));
|
||||
|
||||
RG_LOGI("%dx%d@%.3f => %dx%d@%.3f x_pos:%d y_pos:%d\n", src_width, src_height, src_width / (double)src_height,
|
||||
new_width, new_height, new_ratio, display.viewport.left, display.viewport.top);
|
||||
// The 0.1f addition is to ceil the number in case we hit a .999999 approximation
|
||||
for (int x = 0; x < display.screen.width; ++x)
|
||||
map_viewport_to_source_x[x] = x * display.viewport.step_x + 0.1f;
|
||||
for (int y = 0; y < display.screen.height; ++y)
|
||||
map_viewport_to_source_y[y] = y * display.viewport.step_y + 0.1f;
|
||||
|
||||
RG_LOGI("%dx%d@%.3f => %dx%d@%.3f left:%d top:%d step_x:%.2f step_y:%.2f", src_width, src_height,
|
||||
(double)src_width / src_height, new_width, new_height, (double)new_width / new_height,
|
||||
display.viewport.left, display.viewport.top, display.viewport.step_x, display.viewport.step_y);
|
||||
}
|
||||
|
||||
static bool load_border_file(const char *filename)
|
||||
@ -625,28 +616,16 @@ display_scaling_t rg_display_get_scaling(void)
|
||||
return config.scaling;
|
||||
}
|
||||
|
||||
void rg_display_set_custom_width(int width)
|
||||
void rg_display_set_custom_zoom(double factor)
|
||||
{
|
||||
config.custom_width = RG_MIN(RG_MAX(64, width), display.screen.width);
|
||||
rg_settings_set_number(NS_APP, SETTING_CUSTOM_WIDTH, config.custom_width);
|
||||
config.custom_zoom = RG_MIN(RG_MAX(0.1, factor), 2.0);
|
||||
rg_settings_set_number(NS_APP, SETTING_CUSTOM_ZOOM, config.custom_zoom);
|
||||
display.changed = true;
|
||||
}
|
||||
|
||||
int rg_display_get_custom_width(void)
|
||||
double rg_display_get_custom_zoom(void)
|
||||
{
|
||||
return config.custom_width;
|
||||
}
|
||||
|
||||
void rg_display_set_custom_height(int height)
|
||||
{
|
||||
config.custom_height = RG_MIN(RG_MAX(64, height), display.screen.height);
|
||||
rg_settings_set_number(NS_APP, SETTING_CUSTOM_HEIGHT, config.custom_height);
|
||||
display.changed = true;
|
||||
}
|
||||
|
||||
int rg_display_get_custom_height(void)
|
||||
{
|
||||
return config.custom_height;
|
||||
return config.custom_zoom;
|
||||
}
|
||||
|
||||
void rg_display_set_filter(display_filter_t filter)
|
||||
@ -708,16 +687,6 @@ char *rg_display_get_border(void)
|
||||
return rg_settings_get_string(NS_APP, SETTING_BORDER, NULL);
|
||||
}
|
||||
|
||||
bool rg_display_save_frame(const char *filename, const rg_surface_t *frame, int width, int height)
|
||||
{
|
||||
// Ugly hack because the frame we receive doesn't have crop info yet
|
||||
rg_surface_t temp = *frame;
|
||||
temp.width = display.source.width;
|
||||
temp.height = display.source.height;
|
||||
temp.data += (display.source.crop_v * temp.stride) + (display.source.crop_h * RG_PIXEL_GET_SIZE(frame->format));
|
||||
return rg_surface_save_image_file(&temp, filename, width, height);
|
||||
}
|
||||
|
||||
void rg_display_submit(const rg_surface_t *update, uint32_t flags)
|
||||
{
|
||||
const int64_t time_start = rg_system_timer();
|
||||
@ -726,8 +695,13 @@ void rg_display_submit(const rg_surface_t *update, uint32_t flags)
|
||||
if (!update || !update->data)
|
||||
return;
|
||||
|
||||
if (!display.source.defined)
|
||||
rg_display_set_source_viewport(update->width, update->height, 0, 0);
|
||||
if (display.source.width != update->width || display.source.height != update->height)
|
||||
{
|
||||
rg_display_sync(true);
|
||||
display.source.width = update->width;
|
||||
display.source.height = update->height;
|
||||
display.changed = true;
|
||||
}
|
||||
|
||||
xQueueSend(display_task_queue, &update, portMAX_DELAY);
|
||||
|
||||
@ -735,17 +709,6 @@ void rg_display_submit(const rg_surface_t *update, uint32_t flags)
|
||||
counters.totalFrames++;
|
||||
}
|
||||
|
||||
void rg_display_set_source_viewport(int width, int height, int crop_h, int crop_v)
|
||||
{
|
||||
rg_display_sync(true);
|
||||
display.source.crop_h = RG_MAX(RG_MAX(0, width - display.screen.width) / 2, crop_h);
|
||||
display.source.crop_v = RG_MAX(RG_MAX(0, height - display.screen.height) / 2, crop_v);
|
||||
display.source.width = width - display.source.crop_h * 2;
|
||||
display.source.height = height - display.source.crop_v * 2;
|
||||
display.source.defined = true;
|
||||
display.changed = true;
|
||||
}
|
||||
|
||||
bool rg_display_sync(bool block)
|
||||
{
|
||||
#ifdef ESP_PLATFORM
|
||||
@ -854,12 +817,11 @@ void rg_display_init(void)
|
||||
// TO DO: We probably should call the setters to ensure valid values...
|
||||
config = (rg_display_config_t){
|
||||
.backlight = rg_settings_get_number(NS_GLOBAL, SETTING_BACKLIGHT, 80),
|
||||
.scaling = rg_settings_get_number(NS_APP, SETTING_SCALING, RG_DISPLAY_SCALING_FULL),
|
||||
.scaling = rg_settings_get_number(NS_APP, SETTING_SCALING, RG_DISPLAY_SCALING_OFF),
|
||||
.filter = rg_settings_get_number(NS_APP, SETTING_FILTER, RG_DISPLAY_FILTER_BOTH),
|
||||
.rotation = rg_settings_get_number(NS_APP, SETTING_ROTATION, RG_DISPLAY_ROTATION_AUTO),
|
||||
.border_file = rg_settings_get_string(NS_APP, SETTING_BORDER, NULL),
|
||||
.custom_width = rg_settings_get_number(NS_APP, SETTING_CUSTOM_WIDTH, 240),
|
||||
.custom_height = rg_settings_get_number(NS_APP, SETTING_CUSTOM_HEIGHT, 240),
|
||||
.custom_zoom = rg_settings_get_number(NS_APP, SETTING_CUSTOM_ZOOM, 1.0),
|
||||
};
|
||||
display = (rg_display_t){
|
||||
.screen.width = RG_SCREEN_WIDTH - RG_SCREEN_MARGIN_LEFT - RG_SCREEN_MARGIN_RIGHT,
|
||||
|
||||
@ -8,7 +8,7 @@ typedef enum
|
||||
RG_DISPLAY_SCALING_OFF = 0, // No scaling, center image on screen
|
||||
RG_DISPLAY_SCALING_FIT, // Scale and preserve aspect ratio
|
||||
RG_DISPLAY_SCALING_FULL, // Scale and stretch to fill screen
|
||||
RG_DISPLAY_SCALING_CUSTOM, // Custom width and height
|
||||
RG_DISPLAY_SCALING_ZOOM, // Custom zoom and preserve aspect ratio
|
||||
RG_DISPLAY_SCALING_COUNT
|
||||
} display_scaling_t;
|
||||
|
||||
@ -49,7 +49,7 @@ typedef struct
|
||||
display_filter_t filter;
|
||||
display_backlight_t backlight;
|
||||
char *border_file;
|
||||
int custom_width, custom_height;
|
||||
double custom_zoom;
|
||||
} rg_display_config_t;
|
||||
|
||||
typedef struct
|
||||
@ -72,12 +72,12 @@ typedef struct
|
||||
{
|
||||
int top, left;
|
||||
int width, height;
|
||||
float step_x, step_y;
|
||||
bool filter_x, filter_y;
|
||||
} viewport;
|
||||
struct
|
||||
{
|
||||
int crop_h, crop_v;
|
||||
int width, height;
|
||||
bool defined;
|
||||
} source;
|
||||
bool changed;
|
||||
} rg_display_t;
|
||||
@ -90,8 +90,6 @@ void rg_display_write(int left, int top, int width, int height, int stride, cons
|
||||
void rg_display_clear(uint16_t color_le);
|
||||
bool rg_display_sync(bool block);
|
||||
void rg_display_force_redraw(void);
|
||||
bool rg_display_save_frame(const char *filename, const rg_surface_t *frame, int width, int height);
|
||||
void rg_display_set_source_viewport(int width, int height, int crop_h, int crop_v);
|
||||
void rg_display_submit(const rg_surface_t *update, uint32_t flags);
|
||||
|
||||
rg_display_counters_t rg_display_get_counters(void);
|
||||
@ -107,7 +105,5 @@ void rg_display_set_backlight(display_backlight_t percent);
|
||||
display_backlight_t rg_display_get_backlight(void);
|
||||
void rg_display_set_border(const char *filename);
|
||||
char *rg_display_get_border(void);
|
||||
void rg_display_set_custom_width(int width);
|
||||
int rg_display_get_custom_width(void);
|
||||
void rg_display_set_custom_height(int height);
|
||||
int rg_display_get_custom_height(void);
|
||||
void rg_display_set_custom_zoom(double factor);
|
||||
double rg_display_get_custom_zoom(void);
|
||||
|
||||
@ -1150,51 +1150,30 @@ static rg_gui_event_t scaling_update_cb(rg_gui_option_t *option, rg_gui_event_t
|
||||
|
||||
if (mode == RG_DISPLAY_SCALING_OFF)
|
||||
strcpy(option->value, "Off ");
|
||||
if (mode == RG_DISPLAY_SCALING_FIT)
|
||||
else if (mode == RG_DISPLAY_SCALING_FIT)
|
||||
strcpy(option->value, "Fit ");
|
||||
if (mode == RG_DISPLAY_SCALING_FULL)
|
||||
else if (mode == RG_DISPLAY_SCALING_FULL)
|
||||
strcpy(option->value, "Full ");
|
||||
if (mode == RG_DISPLAY_SCALING_CUSTOM)
|
||||
strcpy(option->value, "Custom");
|
||||
else if (mode == RG_DISPLAY_SCALING_ZOOM)
|
||||
strcpy(option->value, "Zoom");
|
||||
|
||||
return RG_DIALOG_VOID;
|
||||
}
|
||||
|
||||
static rg_gui_event_t custom_width_cb(rg_gui_option_t *option, rg_gui_event_t event)
|
||||
static rg_gui_event_t custom_zoom_cb(rg_gui_option_t *option, rg_gui_event_t event)
|
||||
{
|
||||
if (rg_display_get_scaling() != RG_DISPLAY_SCALING_CUSTOM)
|
||||
if (rg_display_get_scaling() != RG_DISPLAY_SCALING_ZOOM)
|
||||
{
|
||||
option->flags = RG_DIALOG_FLAG_HIDDEN;
|
||||
return RG_DIALOG_VOID;
|
||||
}
|
||||
|
||||
if (event == RG_DIALOG_PREV)
|
||||
rg_display_set_custom_width(rg_display_get_custom_width() - 4);
|
||||
rg_display_set_custom_zoom(rg_display_get_custom_zoom() - 0.05);
|
||||
if (event == RG_DIALOG_NEXT)
|
||||
rg_display_set_custom_width(rg_display_get_custom_width() + 4);
|
||||
rg_display_set_custom_zoom(rg_display_get_custom_zoom() + 0.05);
|
||||
|
||||
sprintf(option->value, "%d", rg_display_get_custom_width());
|
||||
option->flags = RG_DIALOG_FLAG_NORMAL;
|
||||
|
||||
if (event == RG_DIALOG_PREV || event == RG_DIALOG_NEXT)
|
||||
return RG_DIALOG_REDRAW;
|
||||
return RG_DIALOG_VOID;
|
||||
}
|
||||
|
||||
static rg_gui_event_t custom_height_cb(rg_gui_option_t *option, rg_gui_event_t event)
|
||||
{
|
||||
if (rg_display_get_scaling() != RG_DISPLAY_SCALING_CUSTOM)
|
||||
{
|
||||
option->flags = RG_DIALOG_FLAG_HIDDEN;
|
||||
return RG_DIALOG_VOID;
|
||||
}
|
||||
|
||||
if (event == RG_DIALOG_PREV)
|
||||
rg_display_set_custom_height(rg_display_get_custom_height() - 4);
|
||||
if (event == RG_DIALOG_NEXT)
|
||||
rg_display_set_custom_height(rg_display_get_custom_height() + 4);
|
||||
|
||||
sprintf(option->value, "%d", rg_display_get_custom_height());
|
||||
sprintf(option->value, "%.2f", rg_display_get_custom_zoom());
|
||||
option->flags = RG_DIALOG_FLAG_NORMAL;
|
||||
|
||||
if (event == RG_DIALOG_PREV || event == RG_DIALOG_NEXT)
|
||||
@ -1346,8 +1325,7 @@ void rg_gui_options_menu(void)
|
||||
else
|
||||
{
|
||||
*opt++ = (rg_gui_option_t){0, "Scaling", "-", RG_DIALOG_FLAG_NORMAL, &scaling_update_cb};
|
||||
*opt++ = (rg_gui_option_t){0, " Width", "-", RG_DIALOG_FLAG_HIDDEN, &custom_width_cb};
|
||||
*opt++ = (rg_gui_option_t){0, " Height", "-", RG_DIALOG_FLAG_HIDDEN, &custom_height_cb};
|
||||
*opt++ = (rg_gui_option_t){0, " Factor", "-", RG_DIALOG_FLAG_HIDDEN, &custom_zoom_cb};
|
||||
*opt++ = (rg_gui_option_t){0, "Filter", "-", RG_DIALOG_FLAG_NORMAL, &filter_update_cb};
|
||||
*opt++ = (rg_gui_option_t){0, "Border", "-", RG_DIALOG_FLAG_NORMAL, &border_update_cb};
|
||||
*opt++ = (rg_gui_option_t){0, "Speed", "-", RG_DIALOG_FLAG_NORMAL, &speedup_update_cb};
|
||||
|
||||
@ -38,6 +38,7 @@ rg_surface_t *rg_surface_create(int width, int height, int format, uint32_t allo
|
||||
surface->width = width;
|
||||
surface->height = height;
|
||||
surface->stride = width * pixel_size;
|
||||
surface->offset = 0;
|
||||
surface->format = format;
|
||||
surface->data = data_size ? ((void *)surface + sizeof(rg_surface_t)) : NULL;
|
||||
surface->palette = palette_size ? ((void *)surface + sizeof(rg_surface_t) + data_size) : NULL;
|
||||
@ -87,20 +88,20 @@ bool rg_surface_copy(const rg_surface_t *source, const rg_rect_t *source_rect, r
|
||||
for (int x = 0; x < copy_width; ++x)
|
||||
src_x_map[x] = scale ? (int)(x * step_x) : x;
|
||||
|
||||
#define COPY_PIXELS_1(SRC_PIXEL, DST_PIXEL) \
|
||||
for (int y = 0; y < copy_height; ++y) \
|
||||
{ \
|
||||
int src_y = scale ? (y * step_y) : y; \
|
||||
const uint8_t *src = source->data + (src_y * source->stride); \
|
||||
uint8_t *dst = dest->data + (y * dest->stride); \
|
||||
for (int x = 0; x < copy_width; ++x) \
|
||||
{ \
|
||||
int src_x = (int)src_x_map[x]; \
|
||||
uint16_t pixel = SRC_PIXEL; \
|
||||
if ((int)pixel == transparency) \
|
||||
continue; \
|
||||
DST_PIXEL; \
|
||||
} \
|
||||
#define COPY_PIXELS_1(SRC_PIXEL, DST_PIXEL) \
|
||||
for (int y = 0; y < copy_height; ++y) \
|
||||
{ \
|
||||
int src_y = scale ? (y * step_y) : y; \
|
||||
const uint8_t *src = source->data + source->offset + (src_y * source->stride); \
|
||||
uint8_t *dst = dest->data + dest->offset + (y * dest->stride); \
|
||||
for (int x = 0; x < copy_width; ++x) \
|
||||
{ \
|
||||
int src_x = (int)src_x_map[x]; \
|
||||
uint16_t pixel = SRC_PIXEL; \
|
||||
if ((int)pixel == transparency) \
|
||||
continue; \
|
||||
DST_PIXEL; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define COPY_PIXELS(SRC_PIXEL) \
|
||||
@ -122,8 +123,8 @@ bool rg_surface_copy(const rg_surface_t *source, const rg_rect_t *source_rect, r
|
||||
{
|
||||
for (int y = 0; y < copy_height; ++y)
|
||||
{
|
||||
const uint8_t *src = source->data + (y * source->stride); // + source_rect left
|
||||
uint8_t *dst = dest->data + (y * dest->stride); // + dest_rect left
|
||||
const uint8_t *src = source->data + source->offset + (y * source->stride); // + source_rect left
|
||||
uint8_t *dst = dest->data + dest->offset + (y * dest->stride); // + dest_rect left
|
||||
memcpy(dst, src, copy_width * RG_PIXEL_GET_SIZE(dest->format));
|
||||
}
|
||||
}
|
||||
@ -273,7 +274,7 @@ bool rg_surface_save_image_file(const rg_surface_t *source, const char *filename
|
||||
source = temp;
|
||||
}
|
||||
|
||||
error = lodepng_encode24_file(filename, source->data, width, height);
|
||||
error = lodepng_encode24_file(filename, source->data + source->offset, width, height);
|
||||
rg_surface_free(temp);
|
||||
|
||||
if (error == 0)
|
||||
|
||||
@ -30,7 +30,7 @@ typedef struct
|
||||
typedef struct
|
||||
{
|
||||
int width, height;
|
||||
int stride; // offset, pixlen
|
||||
int stride, offset; // , pixlen
|
||||
int format;
|
||||
uint16_t *palette;
|
||||
void *data;
|
||||
|
||||
@ -13,7 +13,6 @@ static int KeyboardCol, KeyboardRow, KeyboardKey;
|
||||
static int64_t KeyboardDebounce = 0;
|
||||
static int FrameStartTime;
|
||||
static int KeyboardEmulation, CropPicture;
|
||||
static int PrevScanLines212 = -1;
|
||||
static char *PendingLoadSTA = NULL;
|
||||
|
||||
#define BPS16
|
||||
@ -55,6 +54,14 @@ static const unsigned char KBDKeys[YKEYS][XKEYS] = {
|
||||
{'Z', 'X', 'C', 'V', 'B', 'N', 'M', ',', '.', '/', 0, 0},
|
||||
{'[', ']', ' ', ' ', ' ', ' ', ' ', '\\', '\'', 0, 0, 0}};
|
||||
|
||||
static inline void SubmitFrame(void)
|
||||
{
|
||||
int crop_v = CropPicture ? (ScanLines212 ? 8 : 18) : 0;
|
||||
currentUpdate->offset = crop_v * currentUpdate->stride;
|
||||
currentUpdate->height = HEIGHT - crop_v * 2;
|
||||
rg_display_submit(currentUpdate, 0);
|
||||
}
|
||||
|
||||
int ProcessEvents(int Wait)
|
||||
{
|
||||
for (int i = 0; i < 16; ++i)
|
||||
@ -217,26 +224,15 @@ void SetColor(byte N, byte R, byte G, byte B)
|
||||
XPal0 = color;
|
||||
}
|
||||
|
||||
void UpdateViewport(void)
|
||||
{
|
||||
int crop_v = CropPicture ? (ScanLines212 ? 8 : 18) : 0;
|
||||
rg_display_set_source_viewport(WIDTH, HEIGHT, 0, crop_v);
|
||||
}
|
||||
|
||||
void PutImage(void)
|
||||
{
|
||||
if (CropPicture && ScanLines212 != PrevScanLines212)
|
||||
{
|
||||
UpdateViewport();
|
||||
PrevScanLines212 = ScanLines212;
|
||||
}
|
||||
|
||||
if (InKeyboard)
|
||||
DrawKeyboard(&NormScreen, KBDKeys[KeyboardRow][KeyboardCol]);
|
||||
rg_display_submit(currentUpdate, 0);
|
||||
|
||||
SubmitFrame();
|
||||
currentUpdate = updates[currentUpdate == updates[0]];
|
||||
NormScreen.Data = currentUpdate->data;
|
||||
XBuf = currentUpdate->data;
|
||||
XBuf = NormScreen.Data;
|
||||
}
|
||||
|
||||
unsigned int Joystick(void)
|
||||
@ -266,7 +262,7 @@ unsigned int Mouse(byte N)
|
||||
|
||||
int ShowVideo(void)
|
||||
{
|
||||
rg_display_submit(currentUpdate, 0);
|
||||
SubmitFrame();
|
||||
rg_system_tick(0);
|
||||
return 1;
|
||||
}
|
||||
@ -353,14 +349,14 @@ static bool reset_handler(bool hard)
|
||||
|
||||
static bool screenshot_handler(const char *filename, int width, int height)
|
||||
{
|
||||
return rg_display_save_frame(filename, currentUpdate, width, height);
|
||||
return rg_surface_save_image_file(currentUpdate, filename, width, height);
|
||||
}
|
||||
|
||||
static void event_handler(int event, void *arg)
|
||||
{
|
||||
if (event == RG_EVENT_REDRAW)
|
||||
{
|
||||
rg_display_submit(currentUpdate, 0);
|
||||
SubmitFrame();
|
||||
}
|
||||
}
|
||||
|
||||
@ -369,7 +365,6 @@ static rg_gui_event_t crop_select_cb(rg_gui_option_t *option, rg_gui_event_t eve
|
||||
if (event == RG_DIALOG_PREV || event == RG_DIALOG_NEXT)
|
||||
{
|
||||
CropPicture = !CropPicture;
|
||||
UpdateViewport();
|
||||
rg_settings_set_number(NS_APP, "Crop", CropPicture);
|
||||
return RG_DIALOG_REDRAW;
|
||||
}
|
||||
|
||||
@ -154,7 +154,7 @@ static rg_gui_event_t z80_update_cb(rg_gui_option_t *option, rg_gui_event_t even
|
||||
|
||||
static bool screenshot_handler(const char *filename, int width, int height)
|
||||
{
|
||||
return rg_display_save_frame(filename, currentUpdate, width, height);
|
||||
return rg_surface_save_image_file(currentUpdate, filename, width, height);
|
||||
}
|
||||
|
||||
static bool save_state_handler(const char *filename)
|
||||
@ -267,8 +267,6 @@ void app_main(void)
|
||||
extern unsigned int screen_width, screen_height;
|
||||
extern int hint_pending;
|
||||
|
||||
unsigned int prev_screen_height = 0, prev_screen_width = 0;
|
||||
|
||||
uint32_t keymap[8] = {RG_KEY_UP, RG_KEY_DOWN, RG_KEY_LEFT, RG_KEY_RIGHT, RG_KEY_A, RG_KEY_B, RG_KEY_SELECT, RG_KEY_START};
|
||||
uint32_t joystick = 0, joystick_old;
|
||||
|
||||
@ -308,13 +306,6 @@ void app_main(void)
|
||||
screen_width = REG12_MODE_H40 ? 320 : 256;
|
||||
screen_height = REG1_PAL ? 240 : 224;
|
||||
|
||||
if (screen_width != prev_screen_width || prev_screen_height != screen_height)
|
||||
{
|
||||
rg_display_set_source_viewport(screen_width, screen_height, 0, 0);
|
||||
prev_screen_width = screen_width;
|
||||
prev_screen_height = screen_height;
|
||||
}
|
||||
|
||||
gwenesis_vdp_set_buffer(currentUpdate->data);
|
||||
gwenesis_vdp_render_config();
|
||||
|
||||
@ -402,6 +393,8 @@ void app_main(void)
|
||||
for (int i = 0; i < 256; ++i)
|
||||
currentUpdate->palette[i] = (CRAM565[i] << 8) | (CRAM565[i] >> 8);
|
||||
slowFrame = !rg_display_sync(false);
|
||||
currentUpdate->width = screen_width;
|
||||
currentUpdate->height = screen_height;
|
||||
rg_display_submit(currentUpdate, 0);
|
||||
}
|
||||
|
||||
|
||||
@ -472,7 +472,7 @@ void I_Init(void)
|
||||
static bool screenshot_handler(const char *filename, int width, int height)
|
||||
{
|
||||
Z_FreeTags(PU_CACHE, PU_CACHE); // At this point the heap is usually full. Let's reclaim some!
|
||||
return rg_display_save_frame(filename, update, width, height);
|
||||
return rg_surface_save_image_file(update, filename, width, height);
|
||||
}
|
||||
|
||||
static bool save_state_handler(const char *filename)
|
||||
|
||||
@ -69,7 +69,7 @@
|
||||
// We need 16 bytes of scratch area on both side of each line. The 16 bytes can be shared by adjacent lines.
|
||||
// The buffer should look like [16 bytes] [line 1] [16 bytes] ... [16 bytes] [line 242] [16 bytes]
|
||||
#define XBUF_WIDTH (352 + 16)
|
||||
#define XBUF_HEIGHT (242 + 4)
|
||||
#define XBUF_HEIGHT (242)
|
||||
|
||||
int LoadState(const char *name);
|
||||
int SaveState(const char *name);
|
||||
|
||||
@ -42,7 +42,7 @@ static void event_handler(int event, void *arg)
|
||||
|
||||
static bool screenshot_handler(const char *filename, int width, int height)
|
||||
{
|
||||
return rg_display_save_frame(filename, currentUpdate, width, height);
|
||||
return rg_surface_save_image_file(currentUpdate, filename, width, height);
|
||||
}
|
||||
|
||||
static bool save_state_handler(const char *filename)
|
||||
|
||||
@ -123,7 +123,7 @@ static void event_handler(int event, void *arg)
|
||||
|
||||
static bool screenshot_handler(const char *filename, int width, int height)
|
||||
{
|
||||
return rg_display_save_frame(filename, currentUpdate, width, height);
|
||||
return rg_surface_save_image_file(currentUpdate, filename, width, height);
|
||||
}
|
||||
|
||||
void gw_main(void)
|
||||
|
||||
@ -80,7 +80,10 @@ static void set_display_mode(void)
|
||||
break;
|
||||
}
|
||||
|
||||
rg_display_set_source_viewport(width, height, 0, 0);
|
||||
updates[0]->width = width;
|
||||
updates[0]->height = height;
|
||||
updates[1]->width = width;
|
||||
updates[1]->height = height;
|
||||
}
|
||||
|
||||
|
||||
@ -92,11 +95,13 @@ static rg_gui_event_t rotation_cb(rg_gui_option_t *option, rg_gui_event_t event)
|
||||
if (--rotation < 0) rotation = RG_DISPLAY_ROTATION_COUNT - 1;
|
||||
rg_display_set_rotation((display_rotation_t)rotation);
|
||||
set_display_mode();
|
||||
return RG_DIALOG_REDRAW;
|
||||
}
|
||||
if (event == RG_DIALOG_NEXT) {
|
||||
if (++rotation > RG_DISPLAY_ROTATION_COUNT - 1) rotation = 0;
|
||||
rg_display_set_rotation((display_rotation_t)rotation);
|
||||
set_display_mode();
|
||||
return RG_DIALOG_REDRAW;
|
||||
}
|
||||
|
||||
strcpy(option->value, "Off ");
|
||||
@ -117,7 +122,7 @@ static void event_handler(int event, void *arg)
|
||||
|
||||
static bool screenshot_handler(const char *filename, int width, int height)
|
||||
{
|
||||
return rg_display_save_frame(filename, currentUpdate, width, height);
|
||||
return rg_surface_save_image_file(currentUpdate, filename, width, height);
|
||||
}
|
||||
|
||||
static bool save_state_handler(const char *filename)
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
static int overscan = true;
|
||||
static int autocrop = 0;
|
||||
static int palette = 0;
|
||||
static int crop_h, crop_v;
|
||||
static bool slowFrame = false;
|
||||
static bool nsfPlayer = false;
|
||||
static nes_t *nes;
|
||||
@ -27,14 +26,14 @@ static void event_handler(int event, void *arg)
|
||||
{
|
||||
if (nsfPlayer)
|
||||
rg_display_clear(C_BLACK);
|
||||
else
|
||||
rg_display_submit(currentUpdate, 0);
|
||||
else if (nes)
|
||||
(nes->blit_func)(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static bool screenshot_handler(const char *filename, int width, int height)
|
||||
{
|
||||
return rg_display_save_frame(filename, currentUpdate, width, height);
|
||||
return rg_surface_save_image_file(currentUpdate, filename, width, height);
|
||||
}
|
||||
|
||||
static bool save_state_handler(const char *filename)
|
||||
@ -58,16 +57,6 @@ static bool reset_handler(bool hard)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void set_display_mode(void)
|
||||
{
|
||||
crop_v = (overscan) ? nes->overscan : 0;
|
||||
crop_h = (autocrop) ? 8 : 0;
|
||||
// crop_h = (autocrop == 2) || (autocrop == 1 && nes->ppu->left_bg_counter > 210) ? 8 : 0;
|
||||
rg_display_set_source_viewport(NES_SCREEN_WIDTH, NES_SCREEN_HEIGHT, crop_h, crop_v);
|
||||
nes_setvidbuf(currentUpdate->data - 8);
|
||||
}
|
||||
|
||||
static void build_palette(int n)
|
||||
{
|
||||
uint16_t *pal = nofrendo_buildpalette(n, 16);
|
||||
@ -102,7 +91,6 @@ static rg_gui_event_t overscan_update_cb(rg_gui_option_t *option, rg_gui_event_t
|
||||
{
|
||||
overscan = !overscan;
|
||||
rg_settings_set_number(NS_APP, SETTING_OVERSCAN, overscan);
|
||||
set_display_mode();
|
||||
return RG_DIALOG_REDRAW;
|
||||
}
|
||||
|
||||
@ -123,7 +111,6 @@ static rg_gui_event_t autocrop_update_cb(rg_gui_option_t *option, rg_gui_event_t
|
||||
{
|
||||
autocrop = val;
|
||||
rg_settings_set_number(NS_APP, SETTING_AUTOCROP, val);
|
||||
set_display_mode();
|
||||
return RG_DIALOG_REDRAW;
|
||||
}
|
||||
|
||||
@ -163,9 +150,15 @@ static rg_gui_event_t palette_update_cb(rg_gui_option_t *option, rg_gui_event_t
|
||||
|
||||
static void blit_screen(uint8 *bmp)
|
||||
{
|
||||
slowFrame = bmp && !rg_display_sync(false);
|
||||
// A rolling average should be used for autocrop == 1, it causes jitter in some games...
|
||||
// int crop_h = (autocrop == 2) || (autocrop == 1 && nes->ppu->left_bg_counter > 210) ? 8 : 0;
|
||||
slowFrame = !rg_display_sync(false);
|
||||
int crop_v = (overscan) ? nes->overscan : 0;
|
||||
int crop_h = (autocrop) ? 16 : 8;
|
||||
// crop_h = (autocrop == 2) || (autocrop == 1 && nes->ppu->left_bg_counter > 210) ? 8 : 0;
|
||||
currentUpdate->width = NES_SCREEN_WIDTH - crop_h * 2;
|
||||
currentUpdate->height = NES_SCREEN_HEIGHT - crop_v * 2;
|
||||
currentUpdate->offset = crop_v * currentUpdate->stride + crop_h;
|
||||
rg_display_submit(currentUpdate, 0);
|
||||
}
|
||||
|
||||
@ -213,9 +206,6 @@ void nes_main(void)
|
||||
updates[1] = rg_surface_create(NES_SCREEN_PITCH, NES_SCREEN_HEIGHT, RG_PIXEL_PAL565_BE, MEM_FAST);
|
||||
currentUpdate = updates[0];
|
||||
|
||||
updates[0]->data += 8;
|
||||
updates[1]->data += 8;
|
||||
|
||||
nes = nes_init(SYS_DETECT, app->sampleRate, true);
|
||||
if (!nes)
|
||||
{
|
||||
@ -240,7 +230,6 @@ void nes_main(void)
|
||||
ppu_setopt(PPU_LIMIT_SPRITES, rg_settings_get_number(NS_APP, SETTING_SPRITELIMIT, 1));
|
||||
|
||||
build_palette(palette);
|
||||
set_display_mode();
|
||||
|
||||
// This is necessary for successful state restoration
|
||||
// I have not yet investigated why that is...
|
||||
@ -282,7 +271,7 @@ void nes_main(void)
|
||||
if (drawFrame)
|
||||
{
|
||||
currentUpdate = updates[currentUpdate == updates[0]];
|
||||
nes_setvidbuf(currentUpdate->data - 8);
|
||||
nes_setvidbuf(currentUpdate->data);
|
||||
}
|
||||
|
||||
input_update(0, buttons);
|
||||
|
||||
@ -13,13 +13,10 @@
|
||||
#define AUDIO_SAMPLE_RATE 22050
|
||||
|
||||
static bool emulationPaused = false; // This should probably be a mutex
|
||||
static int current_height = 0;
|
||||
static int current_width = 0;
|
||||
static int overscan = false;
|
||||
static int skipFrames = 0;
|
||||
static bool drawFrame = true;
|
||||
static bool slowFrame = false;
|
||||
static uint8_t *framebuffers[2];
|
||||
|
||||
static rg_surface_t *updates[2];
|
||||
static rg_surface_t *currentUpdate;
|
||||
@ -34,7 +31,6 @@ static rg_gui_event_t overscan_update_cb(rg_gui_option_t *option, rg_gui_event_t
|
||||
{
|
||||
overscan = !overscan;
|
||||
rg_settings_set_number(NS_APP, SETTING_OVERSCAN, overscan);
|
||||
current_width = current_height = 0;
|
||||
return RG_DIALOG_REDRAW;
|
||||
}
|
||||
|
||||
@ -45,19 +41,10 @@ static rg_gui_event_t overscan_update_cb(rg_gui_option_t *option, rg_gui_event_t
|
||||
|
||||
uint8_t *osd_gfx_framebuffer(int width, int height)
|
||||
{
|
||||
if (width != current_width || height != current_height)
|
||||
if (width > 0 && height > 0)
|
||||
{
|
||||
RG_LOGI("Resolution changed to: %dx%d", width, height);
|
||||
|
||||
// PCE-GO needs 16 columns of scratch space + horizontally center
|
||||
int offset_center = 16 + ((XBUF_WIDTH - width) / 2);
|
||||
updates[0]->data = framebuffers[0] + offset_center;
|
||||
updates[1]->data = framebuffers[1] + offset_center;
|
||||
|
||||
rg_display_set_source_viewport(width, height, 0, 0);
|
||||
|
||||
current_width = width;
|
||||
current_height = height;
|
||||
currentUpdate->width = width;
|
||||
currentUpdate->height = height;
|
||||
}
|
||||
return drawFrame ? currentUpdate->data : NULL;
|
||||
}
|
||||
@ -168,7 +155,7 @@ static void event_handler(int event, void *arg)
|
||||
static bool screenshot_handler(const char *filename, int width, int height)
|
||||
{
|
||||
// We must use previous update because at this point current has been wiped.
|
||||
return rg_display_save_frame(filename, updates[currentUpdate == updates[0]], width, height);
|
||||
return rg_surface_save_image_file(updates[currentUpdate == updates[0]], filename, width, height);
|
||||
}
|
||||
|
||||
static bool save_state_handler(const char *filename)
|
||||
@ -213,8 +200,10 @@ void pce_main(void)
|
||||
updates[1] = rg_surface_create(XBUF_WIDTH, XBUF_HEIGHT, RG_PIXEL_PAL565_BE, MEM_FAST);
|
||||
currentUpdate = updates[0];
|
||||
|
||||
framebuffers[0] = updates[0]->data;
|
||||
framebuffers[1] = updates[1]->data;
|
||||
updates[0]->data += 16;
|
||||
updates[0]->width -= 16;
|
||||
updates[1]->data += 16;
|
||||
updates[1]->width -= 16;
|
||||
|
||||
uint16_t *palette = PalettePCE(16);
|
||||
for (int i = 0; i < 256; i++)
|
||||
|
||||
@ -30,7 +30,7 @@ static void event_handler(int event, void *arg)
|
||||
|
||||
static bool screenshot_handler(const char *filename, int width, int height)
|
||||
{
|
||||
return rg_display_save_frame(filename, currentUpdate, width, height);
|
||||
return rg_surface_save_image_file(currentUpdate, filename, width, height);
|
||||
}
|
||||
|
||||
static bool save_state_handler(const char *filename)
|
||||
@ -137,10 +137,12 @@ void sms_main(void)
|
||||
|
||||
app->tickRate = (sms.display == DISPLAY_NTSC) ? FPS_NTSC : FPS_PAL;
|
||||
|
||||
updates[0]->data += bitmap.viewport.x;
|
||||
updates[1]->data += bitmap.viewport.x;
|
||||
|
||||
rg_display_set_source_viewport(bitmap.viewport.w, bitmap.viewport.h, 0, 0);
|
||||
updates[0]->offset = bitmap.viewport.x;
|
||||
updates[0]->width = bitmap.viewport.w;
|
||||
updates[0]->height = bitmap.viewport.h;
|
||||
updates[1]->offset = bitmap.viewport.x;
|
||||
updates[1]->width = bitmap.viewport.w;
|
||||
updates[1]->height = bitmap.viewport.h;
|
||||
|
||||
if (app->bootFlags & RG_BOOT_RESUME)
|
||||
{
|
||||
@ -232,7 +234,7 @@ void sms_main(void)
|
||||
slowFrame = !rg_display_sync(false);
|
||||
rg_display_submit(currentUpdate, 0);
|
||||
currentUpdate = updates[currentUpdate == updates[0]]; // Swap
|
||||
bitmap.data = currentUpdate->data - bitmap.viewport.x;
|
||||
bitmap.data = currentUpdate->data;
|
||||
}
|
||||
|
||||
// The emulator's sound buffer isn't in a very convenient format, we must remix it.
|
||||
|
||||
@ -46,7 +46,7 @@ static void update_keymap(int id)
|
||||
|
||||
static bool screenshot_handler(const char *filename, int width, int height)
|
||||
{
|
||||
return rg_display_save_frame(filename, currentUpdate, width, height);
|
||||
return rg_surface_save_image_file(currentUpdate, filename, width, height);
|
||||
}
|
||||
|
||||
static bool save_state_handler(const char *filename)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user