Skip to content

Commit 03fb38e

Browse files
author
LocalIdentity
committed
Fix DPI Scaling Issues
Path of Building’s Lua UI always works in “virtual” coordinates, while the renderer operates in physical pixels. After enabling DPI scaling, the text renderer and the Lua-side layout logic started disagreeing about where a glyph ends. The mismatched math had a couple of side effects noticeable at 125 %+ Windows scaling:
1 parent ec19208 commit 03fb38e

File tree

4 files changed

+69
-42
lines changed

4 files changed

+69
-42
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
/build/
1+
/build/
2+
/build-SimpleGraphic
3+
/.idea

engine/render/r_font.cpp

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <iostream>
1111
#include <fstream>
1212
#include <string>
13+
#include <cmath>
1314

1415
// =======
1516
// Classes
@@ -144,12 +145,12 @@ int r_font_c::StringWidthInternal(f_fontHeight_s* fh, std::u32string_view str, i
144145
int heightIdx = (int)(std::find(fontHeights, fontHeights + numFontHeight, fh) - fontHeights);
145146
auto tofuFont = FindSmallerFontHeight(height, heightIdx, tofuSizeReduction);
146147

147-
auto measureCodepoint = [](f_fontHeight_s* fh, char32_t cp) {
148-
auto& glyph = fh->Glyph((char)(unsigned char)cp);
148+
auto measureCodepoint = [](f_fontHeight_s* glyphFh, char32_t cp) {
149+
auto& glyph = glyphFh->Glyph((char)(unsigned char)cp);
149150
return glyph.width + glyph.spLeft + glyph.spRight;
150151
};
151152

152-
int width = 0;
153+
float width = 0.0f;
153154
for (size_t idx = 0; idx < str.size();) {
154155
auto ch = str[idx];
155156
int escLen = IsColorEscape(str.substr(idx));
@@ -158,23 +159,26 @@ int r_font_c::StringWidthInternal(f_fontHeight_s* fh, std::u32string_view str, i
158159
}
159160
else if (ch >= (unsigned)fh->numGlyph) {
160161
auto tofu = BuildTofuString(ch);
161-
for (auto ch : tofu) {
162-
width += measureCodepoint(tofuFont.fh, ch);
162+
for (auto cp : tofu) {
163+
width += (float)measureCodepoint(tofuFont.fh, cp);
164+
width = std::ceil(width);
163165
}
164166
++idx;
165167
}
166168
else if (ch == U'\t') {
167169
auto& glyph = fh->Glyph(' ');
168170
int spWidth = glyph.width + glyph.spLeft + glyph.spRight;
169-
width += (int)(spWidth * 4 * scale);
171+
width += (float)(spWidth << 2) * scale;
172+
width = std::ceil(width);
170173
++idx;
171174
}
172175
else {
173-
width += (int)(measureCodepoint(fh, ch) * scale);
176+
width += (float)measureCodepoint(fh, ch) * scale;
177+
width = std::ceil(width);
174178
++idx;
175179
}
176180
}
177-
return width;
181+
return (int)width;
178182
}
179183

180184
int r_font_c::StringWidth(int height, std::u32string_view str)
@@ -203,12 +207,12 @@ size_t r_font_c::StringCursorInternal(f_fontHeight_s* fh, std::u32string_view st
203207
int heightIdx = (int)(std::find(fontHeights, fontHeights + numFontHeight, fh) - fontHeights);
204208
auto tofuFont = FindSmallerFontHeight(height, heightIdx, tofuSizeReduction);
205209

206-
auto measureCodepoint = [](f_fontHeight_s* fh, char32_t cp) {
207-
auto& glyph = fh->Glyph((char)(unsigned char)cp);
210+
auto measureCodepoint = [](f_fontHeight_s* glyphFh, char32_t cp) {
211+
auto& glyph = glyphFh->Glyph((char)(unsigned char)cp);
208212
return glyph.width + glyph.spLeft + glyph.spRight;
209213
};
210214

211-
int x = 0;
215+
float x = 0.0f;
212216
auto I = str.begin();
213217
auto lineEnd = std::find(I, str.end(), U'\n');
214218
while (I != lineEnd) {
@@ -219,32 +223,34 @@ size_t r_font_c::StringCursorInternal(f_fontHeight_s* fh, std::u32string_view st
219223
}
220224
else if (*I >= (unsigned)fh->numGlyph) {
221225
auto tofu = BuildTofuString(*I);
222-
int tofuWidth = 0;
223-
for (auto ch : tofu) {
224-
tofuWidth += measureCodepoint(tofuFont.fh, ch);
225-
}
226-
int halfWidth = (int)(tofuWidth / 2.0f);
227-
x += halfWidth;
228-
if (curX <= x) {
229-
break;
226+
for (auto cp : tofu) {
227+
x += (float)measureCodepoint(tofuFont.fh, cp);
228+
x = std::ceil(x);
229+
if (curX <= x) {
230+
return std::distance(str.begin(), I);
231+
}
230232
}
231-
x += tofuWidth - halfWidth;
232233
++I;
233234
}
234235
else if (*I == U'\t') {
235236
auto& glyph = fh->Glyph(' ');
236-
int spWidth = glyph.width + glyph.spLeft + glyph.spRight;
237-
int fullWidth = (int)(spWidth * 4 * scale);
238-
int halfWidth = (int)(fullWidth / 2.0f);
237+
float fullWidth = (float)(glyph.width + glyph.spLeft + glyph.spRight) * 4.0f * scale;
238+
float halfWidth = std::ceil(fullWidth * 0.5f);
239239
x += halfWidth;
240+
x = std::ceil(x);
240241
if (curX <= x) {
241242
break;
242243
}
243244
x += fullWidth - halfWidth;
245+
x = std::ceil(x);
246+
if (curX <= x) {
247+
break;
248+
}
244249
++I;
245250
}
246251
else {
247-
x += (int)(measureCodepoint(fh, *I) * scale);
252+
x += (float)measureCodepoint(fh, *I) * scale;
253+
x = std::ceil(x);
248254
if (curX <= x) {
249255
break;
250256
}
@@ -339,7 +345,7 @@ void r_font_c::DrawTextLine(scp_t pos, int align, int height, col4_t col, std::u
339345

340346
// Calculate the string position
341347
float x = pos[X];
342-
float y = pos[Y];
348+
float y = std::floor(pos[Y]);
343349
if (align != F_LEFT) {
344350
// Calculate the real width of the string
345351
float width = (float)StringWidthInternal(fh, str, height, scale);

engine/render/r_main.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1852,11 +1852,13 @@ int r_renderer_c::DrawStringCursorIndex(int height, int font, const char* str, i
18521852
// ==============
18531853

18541854
int r_renderer_c::VirtualScreenWidth() {
1855-
return VirtualMap(sys->video->vid.size[0]);
1855+
int const properWidth = apiDpiAware ? sys->video->vid.fbSize[0] : sys->video->vid.size[0];
1856+
return VirtualMap(properWidth);
18561857
}
18571858

18581859
int r_renderer_c::VirtualScreenHeight() {
1859-
return VirtualMap(sys->video->vid.size[1]);
1860+
int const properHeight = apiDpiAware ? sys->video->vid.fbSize[1] : sys->video->vid.size[1];
1861+
return VirtualMap(properHeight);
18601862
}
18611863

18621864
float r_renderer_c::VirtualScreenScaleFactor() {

ui_api.cpp

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <filesystem>
1010
#include <fstream>
1111
#include <zlib.h>
12+
#include <cmath>
1213

1314
#include "core/core_tex_manipulation.h"
1415

@@ -1068,11 +1069,14 @@ static int l_DrawString(lua_State* L)
10681069
static const char* alignMap[6] = { "LEFT", "CENTER", "RIGHT", "CENTER_X", "RIGHT_X", NULL };
10691070
static const char* fontMap[4] = { "FIXED", "VAR", "VAR BOLD", NULL };
10701071
const float dpiScale = ui->renderer->VirtualScreenScaleFactor();
1072+
const float left = (float)lua_tonumber(L, 1) * dpiScale;
1073+
const float top = (float)lua_tonumber(L, 2) * dpiScale;
1074+
const int scaledHeight = (int)std::lround((double)lua_tointeger(L, 4) * (double)dpiScale);
10711075
ui->renderer->DrawString(
1072-
(float)lua_tonumber(L, 1) * dpiScale,
1073-
(float)lua_tonumber(L, 2) * dpiScale,
1076+
left,
1077+
top,
10741078
luaL_checkoption(L, 3, "LEFT", alignMap),
1075-
(int)lua_tointeger(L, 4) * dpiScale,
1079+
scaledHeight,
10761080
NULL,
10771081
luaL_checkoption(L, 5, "FIXED", fontMap),
10781082
lua_tostring(L, 6)
@@ -1091,9 +1095,13 @@ static int l_DrawStringWidth(lua_State* L)
10911095
ui->LAssert(L, lua_isstring(L, 3), "DrawStringWidth() argument 3: expected string, got %s", luaL_typename(L, 3));
10921096
static const char* fontMap[4] = { "FIXED", "VAR", "VAR BOLD", NULL };
10931097
const float dpiScale = ui->renderer->VirtualScreenScaleFactor();
1094-
lua_pushinteger(L, ui->renderer->DrawStringWidth((int)lua_tointeger(L, 1) * dpiScale,
1095-
luaL_checkoption(L, 2, "FIXED", fontMap),
1096-
lua_tostring(L, 3)));
1098+
const double logicalHeight = lua_tonumber(L, 1);
1099+
const int scaledHeight = (int)std::lround(logicalHeight * dpiScale);
1100+
double const physicalWidth = (double)ui->renderer->DrawStringWidth(
1101+
scaledHeight,
1102+
luaL_checkoption(L, 2, "FIXED", fontMap),
1103+
lua_tostring(L, 3));
1104+
lua_pushnumber(L, physicalWidth / dpiScale);
10971105
return 1;
10981106
}
10991107

@@ -1110,10 +1118,17 @@ static int l_DrawStringCursorIndex(lua_State* L)
11101118
ui->LAssert(L, lua_isnumber(L, 4), "DrawStringCursorIndex() argument 4: expected number, got %s", luaL_typename(L, 4));
11111119
ui->LAssert(L, lua_isnumber(L, 5), "DrawStringCursorIndex() argument 5: expected number, got %s", luaL_typename(L, 5));
11121120
static const char* fontMap[4] = { "FIXED", "VAR", "VAR BOLD", NULL };
1113-
lua_pushinteger(L, ui->renderer->DrawStringCursorIndex((int)lua_tointeger(L, 1) * dpiScale,
1114-
luaL_checkoption(L, 2, "FIXED", fontMap),
1115-
lua_tostring(L, 3),
1116-
(int)lua_tointeger(L, 4) * dpiScale, (int)lua_tointeger(L, 5) * dpiScale) + 1);
1121+
const double logicalHeight = lua_tonumber(L, 1);
1122+
const double logicalCursorX = lua_tonumber(L, 4);
1123+
const double logicalCursorY = lua_tonumber(L, 5);
1124+
const int scaledHeight = (int)std::lround(logicalHeight * dpiScale);
1125+
const int scaledCursorX = (int)std::lround(logicalCursorX * dpiScale);
1126+
const int scaledCursorY = (int)std::lround(logicalCursorY * dpiScale);
1127+
lua_pushinteger(L, ui->renderer->DrawStringCursorIndex(
1128+
scaledHeight,
1129+
luaL_checkoption(L, 2, "FIXED", fontMap),
1130+
lua_tostring(L, 3),
1131+
scaledCursorX, scaledCursorY) + 1);
11171132
return 1;
11181133
}
11191134

@@ -1396,8 +1411,8 @@ static int l_GetCursorPos(lua_State* L)
13961411
{
13971412
ui_main_c* ui = GetUIPtr(L);
13981413
const float dpiScale = ui->renderer->VirtualScreenScaleFactor();
1399-
lua_pushinteger(L, ui->renderer->VirtualMap(ui->cursorX) / dpiScale);
1400-
lua_pushinteger(L, ui->renderer->VirtualMap(ui->cursorY) / dpiScale);
1414+
lua_pushinteger(L, (lua_Integer)std::lround((double)ui->renderer->VirtualMap(ui->cursorX) / (double)dpiScale));
1415+
lua_pushinteger(L, (lua_Integer)std::lround((double)ui->renderer->VirtualMap(ui->cursorY) / (double)dpiScale));
14011416
return 2;
14021417
}
14031418

@@ -1409,8 +1424,10 @@ static int l_SetCursorPos(lua_State* L)
14091424
ui->LAssert(L, n >= 2, "Usage: SetCursorPos(x, y)");
14101425
ui->LAssert(L, lua_isnumber(L, 1), "SetCursorPos() argument 1: expected number, got %s", luaL_typename(L, 1));
14111426
ui->LAssert(L, lua_isnumber(L, 2), "SetCursorPos() argument 2: expected number, got %s", luaL_typename(L, 2));
1412-
int x = ui->renderer->VirtualUnmap((int)lua_tointeger(L, 1) * dpiScale);
1413-
int y = ui->renderer->VirtualUnmap((int)lua_tointeger(L, 2) * dpiScale);
1427+
const int scaledX = (int)std::lround((double)lua_tointeger(L, 1) * (double)dpiScale);
1428+
const int scaledY = (int)std::lround((double)lua_tointeger(L, 2) * (double)dpiScale);
1429+
int x = ui->renderer->VirtualUnmap(scaledX);
1430+
int y = ui->renderer->VirtualUnmap(scaledY);
14141431
ui->sys->video->SetRelativeCursor(x, y);
14151432
return 0;
14161433
}

0 commit comments

Comments
 (0)