From 348b45b436ffd8eb2df2cd27b60570273d0df211 Mon Sep 17 00:00:00 2001 From: Matt Borgerson Date: Sat, 3 Jun 2023 22:22:24 -0700 Subject: [PATCH] ui: Support widescreen thumbnails --- ui/xemu-snapshots.h | 4 +- ui/xemu-thumbnail.cc | 15 ++---- ui/xui/gl-helpers.cc | 106 +++++++++++++++++++++++++------------------ ui/xui/gl-helpers.hh | 6 ++- ui/xui/main-menu.cc | 24 +++++++++- 5 files changed, 93 insertions(+), 62 deletions(-) diff --git a/ui/xemu-snapshots.h b/ui/xemu-snapshots.h index 2f6dc4182f..792b9d3c4f 100644 --- a/ui/xemu-snapshots.h +++ b/ui/xemu-snapshots.h @@ -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[]; diff --git a/ui/xemu-thumbnail.cc b/ui/xemu-thumbnail.cc index 968d1c8c2b..86df3ef2b4 100644 --- a/ui/xemu-thumbnail.cc +++ b/ui/xemu-thumbnail.cc @@ -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 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); diff --git a/ui/xui/gl-helpers.cc b/ui/xui/gl-helpers.cc index a7ce17261f..996f263726 100644 --- a/ui/xui/gl-helpers.cc +++ b/ui/xui/gl-helpers.cc @@ -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 proportionally to fit in +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 &png, int width, int height) +bool RenderFramebufferToPng(GLuint tex, bool flip, std::vector &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 pixels; pixels.resize(width * height * 3); @@ -757,7 +773,8 @@ bool ExtractFramebufferPixels(GLuint tex, bool flip, std::vector &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 png; - if (ExtractFramebufferPixels(tex, flip, png)) { + + if (RenderFramebufferToPng(tex, flip, png)) { time_t t = time(NULL); struct tm *tmp = localtime(&t); if (tmp) { diff --git a/ui/xui/gl-helpers.hh b/ui/xui/gl-helpers.hh index f881f9fd5b..ce5c00b6a4 100644 --- a/ui/xui/gl-helpers.hh +++ b/ui/xui/gl-helpers.hh @@ -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 &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 &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); \ No newline at end of file diff --git a/ui/xui/main-menu.cc b/ui/xui/main-menu.cc index b011a4cb43..715b585cfd 100644 --- a/ui/xui/main-menu.cc +++ b/ui/xui/main-menu.cc @@ -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); }