wasm: improve HiDPI support
This commit is contained in:
@@ -26,6 +26,13 @@ EM_JS(float, js_devicePixelRatio, (), {
|
|||||||
return window.devicePixelRatio || 1.0;
|
return window.devicePixelRatio || 1.0;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// SDL_CreateWindow sets inline width/height on the canvas which overrides
|
||||||
|
// the stylesheet's 100vw/100vh. Clear them once so CSS stays in control.
|
||||||
|
EM_JS(void, js_clearCanvasInlineSize, (), {
|
||||||
|
var c = document.getElementById('canvas');
|
||||||
|
if (c) { c.style.width = ''; c.style.height = ''; }
|
||||||
|
});
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#include <GL/gl.h>
|
#include <GL/gl.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -38,6 +45,30 @@ namespace baudmine {
|
|||||||
|
|
||||||
Application::Application() = default;
|
Application::Application() = default;
|
||||||
|
|
||||||
|
void Application::syncCanvasSize() {
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
double cssW, cssH;
|
||||||
|
emscripten_get_element_css_size("#canvas", &cssW, &cssH);
|
||||||
|
float dpr = js_devicePixelRatio();
|
||||||
|
int targetW = static_cast<int>(cssW * dpr + 0.5);
|
||||||
|
int targetH = static_cast<int>(cssH * dpr + 0.5);
|
||||||
|
int curW, curH;
|
||||||
|
emscripten_get_canvas_element_size("#canvas", &curW, &curH);
|
||||||
|
if (curW != targetW || curH != targetH) {
|
||||||
|
// Set backing store + viewport to physical pixels.
|
||||||
|
// CSS display size is handled by the stylesheet (100vw × 100vh).
|
||||||
|
emscripten_set_canvas_element_size("#canvas", targetW, targetH);
|
||||||
|
glViewport(0, 0, targetW, targetH);
|
||||||
|
}
|
||||||
|
// Re-apply UI scale if devicePixelRatio changed (orientation, zoom, etc.)
|
||||||
|
if (std::abs(dpr - lastDpr_) > 0.01f) {
|
||||||
|
lastDpr_ = dpr;
|
||||||
|
float scale = (uiScale_ > 0.0f) ? uiScale_ : dpr;
|
||||||
|
applyUIScale(scale);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void Application::applyUIScale(float scale) {
|
void Application::applyUIScale(float scale) {
|
||||||
scale = std::clamp(scale, 0.5f, 4.0f);
|
scale = std::clamp(scale, 0.5f, 4.0f);
|
||||||
if (std::abs(scale - appliedScale_) < 0.01f) return;
|
if (std::abs(scale - appliedScale_) < 0.01f) return;
|
||||||
@@ -53,17 +84,35 @@ void Application::applyUIScale(float scale) {
|
|||||||
return s;
|
return s;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
|
// Determine framebuffer scale (e.g. 2x–3x on HiDPI phones).
|
||||||
|
float fbScale = 1.0f;
|
||||||
|
int winW, winH, drawW, drawH;
|
||||||
|
SDL_GetWindowSize(window_, &winW, &winH);
|
||||||
|
SDL_GL_GetDrawableSize(window_, &drawW, &drawH);
|
||||||
|
if (winW > 0) fbScale = static_cast<float>(drawW) / winW;
|
||||||
|
|
||||||
|
logicalScale_ = scale / fbScale;
|
||||||
|
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
// Rasterize the font at full physical resolution so the atlas has
|
||||||
|
// crisp glyphs, then tell ImGui to scale them back to logical size.
|
||||||
|
// Without this the 13px atlas gets bilinear-stretched to 13*dpr px.
|
||||||
io.Fonts->Clear();
|
io.Fonts->Clear();
|
||||||
ImFontConfig fc;
|
ImFontConfig fc;
|
||||||
fc.SizePixels = 13.0f * scale;
|
fc.SizePixels = std::max(8.0f, 13.0f * scale); // physical pixels
|
||||||
io.Fonts->AddFontDefault(&fc);
|
io.Fonts->AddFontDefault(&fc);
|
||||||
io.Fonts->Build();
|
io.Fonts->Build();
|
||||||
ImGui_ImplOpenGL3_DestroyFontsTexture();
|
ImGui_ImplOpenGL3_DestroyFontsTexture();
|
||||||
|
io.FontGlobalScale = 1.0f / fbScale; // display at logical size
|
||||||
|
|
||||||
// Restore base style, then scale from 1x.
|
// Restore base style, then scale from 1x.
|
||||||
ImGui::GetStyle() = baseStyle;
|
ImGui::GetStyle() = baseStyle;
|
||||||
ImGui::GetStyle().ScaleAllSizes(scale);
|
ImGui::GetStyle().ScaleAllSizes(logicalScale_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::requestUIScale(float scale) {
|
||||||
|
pendingScale_ = scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::~Application() {
|
Application::~Application() {
|
||||||
@@ -115,6 +164,13 @@ bool Application::init(int argc, char** argv) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
// SDL_CreateWindow sets inline width/height on the canvas element,
|
||||||
|
// overriding the stylesheet's 100vw/100vh. Clear them so CSS stays
|
||||||
|
// in control of display size while we manage the backing store.
|
||||||
|
js_clearCanvasInlineSize();
|
||||||
|
#endif
|
||||||
|
|
||||||
glContext_ = SDL_GL_CreateContext(window_);
|
glContext_ = SDL_GL_CreateContext(window_);
|
||||||
SDL_GL_MakeCurrent(window_, glContext_);
|
SDL_GL_MakeCurrent(window_, glContext_);
|
||||||
SDL_GL_SetSwapInterval(1); // vsync
|
SDL_GL_SetSwapInterval(1); // vsync
|
||||||
@@ -144,11 +200,15 @@ bool Application::init(int argc, char** argv) {
|
|||||||
// Load saved config (overwrites defaults for FFT size, overlap, window, etc.)
|
// Load saved config (overwrites defaults for FFT size, overlap, window, etc.)
|
||||||
loadConfig();
|
loadConfig();
|
||||||
|
|
||||||
|
// Sync canvas to physical pixels before first frame (WASM)
|
||||||
|
syncCanvasSize();
|
||||||
|
|
||||||
// Apply DPI-aware UI scaling
|
// Apply DPI-aware UI scaling
|
||||||
{
|
{
|
||||||
float dpiScale = 1.0f;
|
float dpiScale = 1.0f;
|
||||||
#ifdef __EMSCRIPTEN__
|
#ifdef __EMSCRIPTEN__
|
||||||
dpiScale = js_devicePixelRatio();
|
dpiScale = js_devicePixelRatio();
|
||||||
|
lastDpr_ = dpiScale;
|
||||||
#else
|
#else
|
||||||
float ddpi = 0;
|
float ddpi = 0;
|
||||||
if (SDL_GetDisplayDPI(0, &ddpi, nullptr, nullptr) == 0 && ddpi > 0)
|
if (SDL_GetDisplayDPI(0, &ddpi, nullptr, nullptr) == 0 && ddpi > 0)
|
||||||
@@ -186,6 +246,14 @@ bool Application::init(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Application::mainLoopStep() {
|
void Application::mainLoopStep() {
|
||||||
|
syncCanvasSize();
|
||||||
|
|
||||||
|
// Apply deferred UI scale (must happen outside the ImGui frame).
|
||||||
|
if (pendingScale_ > 0.0f) {
|
||||||
|
applyUIScale(pendingScale_);
|
||||||
|
pendingScale_ = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
while (SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
ImGui_ImplSDL2_ProcessEvent(&event);
|
ImGui_ImplSDL2_ProcessEvent(&event);
|
||||||
@@ -429,7 +497,7 @@ void Application::render() {
|
|||||||
if (SDL_GetDisplayDPI(0, &ddpi, nullptr, nullptr) == 0 && ddpi > 0)
|
if (SDL_GetDisplayDPI(0, &ddpi, nullptr, nullptr) == 0 && ddpi > 0)
|
||||||
dpiScale = ddpi / 96.0f;
|
dpiScale = ddpi / 96.0f;
|
||||||
#endif
|
#endif
|
||||||
applyUIScale(dpiScale);
|
requestUIScale(dpiScale);
|
||||||
saveConfig();
|
saveConfig();
|
||||||
}
|
}
|
||||||
for (int s : kScales) {
|
for (int s : kScales) {
|
||||||
@@ -437,7 +505,7 @@ void Application::render() {
|
|||||||
std::snprintf(label, sizeof(label), "%d%%", s);
|
std::snprintf(label, sizeof(label), "%d%%", s);
|
||||||
if (ImGui::MenuItem(label, nullptr, uiScale_ > 0.0f && std::abs(curPct - s) <= 2)) {
|
if (ImGui::MenuItem(label, nullptr, uiScale_ > 0.0f && std::abs(curPct - s) <= 2)) {
|
||||||
uiScale_ = s / 100.0f;
|
uiScale_ = s / 100.0f;
|
||||||
applyUIScale(uiScale_);
|
requestUIScale(uiScale_);
|
||||||
saveConfig();
|
saveConfig();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -485,7 +553,7 @@ void Application::render() {
|
|||||||
// Layout
|
// Layout
|
||||||
float totalW = ImGui::GetContentRegionAvail().x;
|
float totalW = ImGui::GetContentRegionAvail().x;
|
||||||
float contentH = ImGui::GetContentRegionAvail().y;
|
float contentH = ImGui::GetContentRegionAvail().y;
|
||||||
float controlW = showSidebar_ ? 270.0f * appliedScale_ : 0.0f;
|
float controlW = showSidebar_ ? 270.0f * logicalScale_ : 0.0f;
|
||||||
float contentW = totalW - (showSidebar_ ? controlW + 8 : 0);
|
float contentW = totalW - (showSidebar_ ? controlW + 8 : 0);
|
||||||
|
|
||||||
// Control panel (sidebar)
|
// Control panel (sidebar)
|
||||||
|
|||||||
@@ -121,9 +121,14 @@ private:
|
|||||||
FreqScale freqScale_ = FreqScale::Linear;
|
FreqScale freqScale_ = FreqScale::Linear;
|
||||||
bool paused_ = false;
|
bool paused_ = false;
|
||||||
bool vsync_ = true;
|
bool vsync_ = true;
|
||||||
float uiScale_ = 0.0f; // 0 = auto (use DPI), >0 = manual override
|
float uiScale_ = 0.0f; // 0 = auto (use DPI), >0 = manual override
|
||||||
float appliedScale_ = 0.0f; // currently applied scale (0 = not yet applied)
|
float appliedScale_ = 0.0f; // currently applied user-facing scale
|
||||||
|
float pendingScale_ = 0.0f; // deferred scale (applied before next frame)
|
||||||
|
float logicalScale_ = 1.0f; // scale after compensating for framebuffer DPI
|
||||||
|
float lastDpr_ = 0.0f; // last devicePixelRatio (to detect changes)
|
||||||
void applyUIScale(float scale);
|
void applyUIScale(float scale);
|
||||||
|
void requestUIScale(float scale); // safe to call mid-frame
|
||||||
|
void syncCanvasSize();
|
||||||
// (waterfallW_ removed — texture width tracks bin count automatically)
|
// (waterfallW_ removed — texture width tracks bin count automatically)
|
||||||
// (waterfallH_ removed — fixed history depth of 1024 rows)
|
// (waterfallH_ removed — fixed history depth of 1024 rows)
|
||||||
|
|
||||||
|
|||||||
@@ -48,17 +48,9 @@
|
|||||||
setStatus: function(text) {
|
setStatus: function(text) {
|
||||||
document.getElementById('status').textContent = text;
|
document.getElementById('status').textContent = text;
|
||||||
},
|
},
|
||||||
// Auto-resize canvas at full device resolution.
|
// Canvas backing-store sizing is handled in C++ via syncCanvasSize()
|
||||||
preRun: [function() {
|
// each frame, so no JS resize handler is needed.
|
||||||
function resize() {
|
preRun: []
|
||||||
var canvas = document.getElementById('canvas');
|
|
||||||
var dpr = window.devicePixelRatio || 1;
|
|
||||||
canvas.width = Math.round(window.innerWidth * dpr);
|
|
||||||
canvas.height = Math.round(window.innerHeight * dpr);
|
|
||||||
}
|
|
||||||
resize();
|
|
||||||
window.addEventListener('resize', resize);
|
|
||||||
}]
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
{{{ SCRIPT }}}
|
{{{ SCRIPT }}}
|
||||||
|
|||||||
Reference in New Issue
Block a user