mirror of https://github.com/xemu-project/xemu.git
ui: Support widescreen thumbnails
This commit is contained in:
parent
9c9f1e83eb
commit
348b45b436
|
@ -28,8 +28,8 @@ extern "C" {
|
|||
#include "block/snapshot.h"
|
||||
|
||||
#define XEMU_SNAPSHOT_DATA_MAGIC 0x78656d75
|
||||
#define XEMU_SNAPSHOT_HEIGHT 120
|
||||
#define XEMU_SNAPSHOT_WIDTH 160
|
||||
#define XEMU_SNAPSHOT_THUMBNAIL_WIDTH 160
|
||||
#define XEMU_SNAPSHOT_THUMBNAIL_HEIGHT 120
|
||||
|
||||
extern const char **g_snapshot_shortcut_index_key_map[];
|
||||
|
||||
|
|
|
@ -67,24 +67,15 @@ TextureBuffer *xemu_snapshots_extract_thumbnail()
|
|||
return NULL;
|
||||
}
|
||||
|
||||
// Render at 2x the base size to account for potential UI scaling
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, display_tex);
|
||||
int thumbnail_width = XEMU_SNAPSHOT_WIDTH * 2;
|
||||
int tex_width;
|
||||
int tex_height;
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &tex_width);
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &tex_height);
|
||||
int thumbnail_height = (int)(((float)tex_height / (float)tex_width) * (float)thumbnail_width);
|
||||
|
||||
std::vector<uint8_t> png;
|
||||
if (!ExtractFramebufferPixels(display_tex, display_flip, png, thumbnail_width, thumbnail_height)) {
|
||||
if (!RenderFramebufferToPng(display_tex, display_flip, png,
|
||||
2 * XEMU_SNAPSHOT_THUMBNAIL_WIDTH,
|
||||
2 * XEMU_SNAPSHOT_THUMBNAIL_HEIGHT)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TextureBuffer *thumbnail = (TextureBuffer *)g_malloc(sizeof(TextureBuffer));
|
||||
thumbnail->buffer = g_malloc(png.size() * sizeof(uint8_t));
|
||||
|
||||
thumbnail->channels = 3;
|
||||
thumbnail->size = png.size() * sizeof(uint8_t);
|
||||
memcpy(thumbnail->buffer, png.data(), thumbnail->size);
|
||||
|
|
|
@ -661,21 +661,60 @@ void RenderLogo(uint32_t time)
|
|||
glUseProgram(0);
|
||||
}
|
||||
|
||||
void RenderFramebuffer(GLint tex, int width, int height, bool flip, bool apply_scaling_factor)
|
||||
// Scale <src> proportionally to fit in <max>
|
||||
void ScaleDimensions(int src_width, int src_height, int max_width, int max_height, int *out_width, int *out_height)
|
||||
{
|
||||
float w_ratio = (float)max_width/(float)max_height;
|
||||
float t_ratio = (float)src_width/(float)src_height;
|
||||
|
||||
if (w_ratio >= t_ratio) {
|
||||
*out_width = (float)max_width * t_ratio/w_ratio;
|
||||
*out_height = max_height;
|
||||
} else {
|
||||
*out_width = max_width;
|
||||
*out_height = (float)max_height * w_ratio/t_ratio;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderFramebuffer(GLint tex, int width, int height, bool flip, float scale[2])
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
|
||||
DecalShader *s = g_framebuffer_shader;
|
||||
s->flip = flip;
|
||||
glViewport(0, 0, width, height);
|
||||
glUseProgram(s->prog);
|
||||
glBindVertexArray(s->vao);
|
||||
glUniform1i(s->flipy_loc, s->flip);
|
||||
glUniform4f(s->scale_offset_loc, scale[0], scale[1], 0, 0);
|
||||
glUniform4f(s->tex_scale_offset_loc, 1.0, 1.0, 0, 0);
|
||||
glUniform1i(s->tex_loc, 0);
|
||||
|
||||
const uint8_t *palette = nv2a_get_dac_palette();
|
||||
for (int i = 0; i < 256; i++) {
|
||||
uint32_t e = (palette[i * 3 + 2] << 16) | (palette[i * 3 + 1] << 8) |
|
||||
palette[i * 3];
|
||||
glUniform1ui(s->palette_loc[i], e);
|
||||
}
|
||||
|
||||
glClearColor(0, 0, 0, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL);
|
||||
}
|
||||
|
||||
void RenderFramebuffer(GLint tex, int width, int height, bool flip)
|
||||
{
|
||||
int tw, th;
|
||||
float scale[2];
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &tw);
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &th);
|
||||
|
||||
// Calculate scaling factors
|
||||
float scale[2];
|
||||
if (!apply_scaling_factor) {
|
||||
scale[0] = 1.0;
|
||||
scale[1] = 1.0;
|
||||
} else if (g_config.display.ui.fit == CONFIG_DISPLAY_UI_FIT_STRETCH) {
|
||||
if (g_config.display.ui.fit == CONFIG_DISPLAY_UI_FIT_STRETCH) {
|
||||
// Stretch to fit
|
||||
scale[0] = 1.0;
|
||||
scale[1] = 1.0;
|
||||
|
@ -705,50 +744,27 @@ void RenderFramebuffer(GLint tex, int width, int height, bool flip, bool apply_s
|
|||
}
|
||||
}
|
||||
|
||||
DecalShader *s = g_framebuffer_shader;
|
||||
s->flip = flip;
|
||||
glViewport(0, 0, width, height);
|
||||
glUseProgram(s->prog);
|
||||
glBindVertexArray(s->vao);
|
||||
glUniform1i(s->flipy_loc, s->flip);
|
||||
glUniform4f(s->scale_offset_loc, scale[0], scale[1], 0, 0);
|
||||
glUniform4f(s->tex_scale_offset_loc, 1.0, 1.0, 0, 0);
|
||||
glUniform1i(s->tex_loc, 0);
|
||||
|
||||
const uint8_t *palette = nv2a_get_dac_palette();
|
||||
for (int i = 0; i < 256; i++) {
|
||||
uint32_t e = (palette[i * 3 + 2] << 16) | (palette[i * 3 + 1] << 8) |
|
||||
palette[i * 3];
|
||||
glUniform1ui(s->palette_loc[i], e);
|
||||
}
|
||||
|
||||
glClearColor(0, 0, 0, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL);
|
||||
RenderFramebuffer(tex, width, height, flip, scale);
|
||||
}
|
||||
|
||||
bool ExtractFramebufferPixels(GLuint tex, bool flip, std::vector<uint8_t> &png, int width, int height)
|
||||
bool RenderFramebufferToPng(GLuint tex, bool flip, std::vector<uint8_t> &png, int max_width, int max_height)
|
||||
{
|
||||
int width, height;
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
assert((width == 0 && height == 0) || (width > 0 && height > 0));
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);
|
||||
|
||||
bool params_from_tex = false;
|
||||
if (width <= 0 && height <= 0) {
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);
|
||||
params_from_tex = true;
|
||||
if (g_config.display.ui.fit == CONFIG_DISPLAY_UI_FIT_SCALE_16_9) {
|
||||
width = height * (16.0f / 9.0f);
|
||||
} else if (g_config.display.ui.fit == CONFIG_DISPLAY_UI_FIT_SCALE_4_3) {
|
||||
width = height * (4.0f / 3.0f);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
assert(width > 0 && height > 0);
|
||||
|
||||
if (params_from_tex) {
|
||||
if (g_config.display.ui.fit == CONFIG_DISPLAY_UI_FIT_SCALE_16_9) {
|
||||
width = height * (16.0f / 9.0f);
|
||||
} else if (g_config.display.ui.fit == CONFIG_DISPLAY_UI_FIT_SCALE_4_3) {
|
||||
width = height * (4.0f / 3.0f);
|
||||
}
|
||||
}
|
||||
if (!max_width) max_width = width;
|
||||
if (!max_height) max_height = height;
|
||||
ScaleDimensions(width, height, max_width, max_height, &width, &height);
|
||||
|
||||
std::vector<uint8_t> pixels;
|
||||
pixels.resize(width * height * 3);
|
||||
|
@ -757,7 +773,8 @@ bool ExtractFramebufferPixels(GLuint tex, bool flip, std::vector<uint8_t> &png,
|
|||
fbo.Target();
|
||||
bool blend = glIsEnabled(GL_BLEND);
|
||||
if (blend) glDisable(GL_BLEND);
|
||||
RenderFramebuffer(tex, width, height, !flip, params_from_tex);
|
||||
float scale[2] = {1.0, 1.0};
|
||||
RenderFramebuffer(tex, width, height, !flip, scale);
|
||||
if (blend) glEnable(GL_BLEND);
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, width);
|
||||
glPixelStorei(GL_PACK_IMAGE_HEIGHT, height);
|
||||
|
@ -773,7 +790,8 @@ void SaveScreenshot(GLuint tex, bool flip)
|
|||
Error *err = NULL;
|
||||
char fname[128];
|
||||
std::vector<uint8_t> png;
|
||||
if (ExtractFramebufferPixels(tex, flip, png)) {
|
||||
|
||||
if (RenderFramebufferToPng(tex, flip, png)) {
|
||||
time_t t = time(NULL);
|
||||
struct tm *tmp = localtime(&t);
|
||||
if (tmp) {
|
||||
|
|
|
@ -47,6 +47,8 @@ void RenderController(float frame_x, float frame_y, uint32_t primary_color,
|
|||
uint32_t secondary_color, ControllerState *state);
|
||||
void RenderControllerPort(float frame_x, float frame_y, int i,
|
||||
uint32_t port_color);
|
||||
void RenderFramebuffer(GLint tex, int width, int height, bool flip, bool apply_scaling_factor = true);
|
||||
bool ExtractFramebufferPixels(GLuint tex, bool flip, std::vector<uint8_t> &png, int width = 0, int height = 0);
|
||||
void RenderFramebuffer(GLint tex, int width, int height, bool flip);
|
||||
void RenderFramebuffer(GLint tex, int width, int height, bool flip, float scale[2]);
|
||||
bool RenderFramebufferToPng(GLuint tex, bool flip, std::vector<uint8_t> &png, int max_width = 0, int max_height = 0);
|
||||
void SaveScreenshot(GLuint tex, bool flip);
|
||||
void ScaleDimensions(int src_width, int src_height, int max_width, int max_height, int *out_width, int *out_height);
|
|
@ -727,7 +727,7 @@ void MainMenuSnapshotsView::SnapshotBigButton(QEMUSnapshotInfo *snapshot, const
|
|||
ImGui::PushFont(g_font_mgr.m_menu_font_medium);
|
||||
|
||||
ImVec2 ts_title = ImGui::CalcTextSize(snapshot->name);
|
||||
ImVec2 thumbnail_size = g_viewport_mgr.Scale(ImVec2(XEMU_SNAPSHOT_WIDTH, XEMU_SNAPSHOT_HEIGHT));
|
||||
ImVec2 thumbnail_size = g_viewport_mgr.Scale(ImVec2(XEMU_SNAPSHOT_THUMBNAIL_WIDTH, XEMU_SNAPSHOT_THUMBNAIL_HEIGHT));
|
||||
ImVec2 thumbnail_pos(style.FramePadding.x, style.FramePadding.y);
|
||||
ImVec2 name_pos(thumbnail_pos.x + thumbnail_size.x + style.FramePadding.x * 2, thumbnail_pos.y);
|
||||
ImVec2 date_pos(name_pos.x, name_pos.y + ts_title.y + style.FramePadding.x);
|
||||
|
@ -758,7 +758,27 @@ void MainMenuSnapshotsView::SnapshotBigButton(QEMUSnapshotInfo *snapshot, const
|
|||
ImGui::SetCursorPosX(pos.x + thumbnail_pos.x);
|
||||
ImGui::SetCursorPosY(pos.y + thumbnail_pos.y);
|
||||
if (screenshot > 0) {
|
||||
ImGui::Image((ImTextureID)(uint64_t)screenshot, thumbnail_size);
|
||||
int thumbnail_width, thumbnail_height;
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, screenshot);
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &thumbnail_width);
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &thumbnail_height);
|
||||
|
||||
// Draw black background behind thumbnail
|
||||
draw_list->AddRectFilled(ImVec2(p0.x + thumbnail_pos.x, p0.y + thumbnail_pos.y),
|
||||
ImVec2(p0.x + thumbnail_pos.x + thumbnail_size.x, p0.y + thumbnail_pos.y + thumbnail_size.y),
|
||||
IM_COL32_BLACK);
|
||||
|
||||
// Center the thumbnail
|
||||
int scaled_width, scaled_height;
|
||||
ScaleDimensions(thumbnail_width, thumbnail_height, thumbnail_size.x, thumbnail_size.y, &scaled_width, &scaled_height);
|
||||
ImVec2 img_pos = ImGui::GetCursorPos();
|
||||
img_pos.x += (thumbnail_size.x - scaled_width) / 2;
|
||||
img_pos.y += (thumbnail_size.y - scaled_height) / 2;
|
||||
ImGui::SetCursorPos(img_pos);
|
||||
|
||||
ImGui::Image((ImTextureID)(uint64_t)screenshot, ImVec2(scaled_width, scaled_height));
|
||||
} else {
|
||||
ImGui::Image((ImTextureID)(uint64_t)g_icon_tex, thumbnail_size);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue