diff --git a/.gitignore b/.gitignore index f3d6549..f7bd29a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -/build/ \ No newline at end of file +/build/ +/build-SimpleGraphic +/.idea \ No newline at end of file diff --git a/engine/render.h b/engine/render.h index 9eb2c5d..3506504 100644 --- a/engine/render.h +++ b/engine/render.h @@ -102,6 +102,8 @@ class r_IRenderer { virtual int VirtualScreenWidth() = 0; virtual int VirtualScreenHeight() = 0; virtual float VirtualScreenScaleFactor() = 0; + virtual void SetDpiScaleOverridePercent(int percent) = 0; + virtual int DpiScaleOverridePercent() const = 0; virtual int VirtualMap(int properValue) = 0; virtual int VirtualUnmap(int mappedValue) = 0; diff --git a/engine/render/r_font.cpp b/engine/render/r_font.cpp index 81f35a7..90375b3 100644 --- a/engine/render/r_font.cpp +++ b/engine/render/r_font.cpp @@ -10,6 +10,7 @@ #include #include #include +#include // ======= // Classes @@ -144,12 +145,12 @@ int r_font_c::StringWidthInternal(f_fontHeight_s* fh, std::u32string_view str, i int heightIdx = (int)(std::find(fontHeights, fontHeights + numFontHeight, fh) - fontHeights); auto tofuFont = FindSmallerFontHeight(height, heightIdx, tofuSizeReduction); - auto measureCodepoint = [](f_fontHeight_s* fh, char32_t cp) { - auto& glyph = fh->Glyph((char)(unsigned char)cp); + auto measureCodepoint = [](f_fontHeight_s* glyphFh, char32_t cp) { + auto& glyph = glyphFh->Glyph((char)(unsigned char)cp); return glyph.width + glyph.spLeft + glyph.spRight; }; - int width = 0; + float width = 0.0f; for (size_t idx = 0; idx < str.size();) { auto ch = str[idx]; int escLen = IsColorEscape(str.substr(idx)); @@ -158,23 +159,26 @@ int r_font_c::StringWidthInternal(f_fontHeight_s* fh, std::u32string_view str, i } else if (ch >= (unsigned)fh->numGlyph) { auto tofu = BuildTofuString(ch); - for (auto ch : tofu) { - width += measureCodepoint(tofuFont.fh, ch); + for (auto cp : tofu) { + width += measureCodepoint(tofuFont.fh, cp); + width = std::ceil(width); } ++idx; } else if (ch == U'\t') { auto& glyph = fh->Glyph(' '); int spWidth = glyph.width + glyph.spLeft + glyph.spRight; - width += (int)(spWidth * 4 * scale); + width += spWidth * 4 * scale; + width = std::ceil(width); ++idx; } else { - width += (int)(measureCodepoint(fh, ch) * scale); + width += measureCodepoint(fh, ch) * scale; + width = std::ceil(width); ++idx; } } - return width; + return static_cast(width); } int r_font_c::StringWidth(int height, std::u32string_view str) @@ -187,7 +191,7 @@ int r_font_c::StringWidth(int height, std::u32string_view str) auto lineEnd = std::find(I, str.end(), U'\n'); if (I != lineEnd) { std::u32string_view line(&*I, std::distance(I, lineEnd)); - int lw = (int)StringWidthInternal(fh, line, height, scale); + int lw = StringWidthInternal(fh, line, height, scale); max = (std::max)(max, lw); } if (lineEnd == str.end()) { @@ -203,12 +207,12 @@ size_t r_font_c::StringCursorInternal(f_fontHeight_s* fh, std::u32string_view st int heightIdx = (int)(std::find(fontHeights, fontHeights + numFontHeight, fh) - fontHeights); auto tofuFont = FindSmallerFontHeight(height, heightIdx, tofuSizeReduction); - auto measureCodepoint = [](f_fontHeight_s* fh, char32_t cp) { - auto& glyph = fh->Glyph((char)(unsigned char)cp); + auto measureCodepoint = [](f_fontHeight_s* glyphFh, char32_t cp) { + auto& glyph = glyphFh->Glyph((char)(unsigned char)cp); return glyph.width + glyph.spLeft + glyph.spRight; }; - int x = 0; + float x = 0.0f; auto I = str.begin(); auto lineEnd = std::find(I, str.end(), U'\n'); while (I != lineEnd) { @@ -219,32 +223,34 @@ size_t r_font_c::StringCursorInternal(f_fontHeight_s* fh, std::u32string_view st } else if (*I >= (unsigned)fh->numGlyph) { auto tofu = BuildTofuString(*I); - int tofuWidth = 0; - for (auto ch : tofu) { - tofuWidth += measureCodepoint(tofuFont.fh, ch); + for (auto cp : tofu) { + x += measureCodepoint(tofuFont.fh, cp); + x = std::ceil(x); + if (curX <= x) { + return std::distance(str.begin(), I); + } } - int halfWidth = (int)(tofuWidth / 2.0f); - x += halfWidth; - if (curX <= x) { - break; - } - x += tofuWidth - halfWidth; ++I; } else if (*I == U'\t') { auto& glyph = fh->Glyph(' '); - int spWidth = glyph.width + glyph.spLeft + glyph.spRight; - int fullWidth = (int)(spWidth * 4 * scale); - int halfWidth = (int)(fullWidth / 2.0f); + float fullWidth = (glyph.width + glyph.spLeft + glyph.spRight) * 4.0f * scale; + float halfWidth = std::ceil(fullWidth / 2.0f); x += halfWidth; + x = std::ceil(x); if (curX <= x) { break; } x += fullWidth - halfWidth; + x = std::ceil(x); + if (curX <= x) { + break; + } ++I; } else { - x += (int)(measureCodepoint(fh, *I) * scale); + x += measureCodepoint(fh, *I) * scale; + x = std::ceil(x); if (curX <= x) { break; } @@ -339,10 +345,10 @@ void r_font_c::DrawTextLine(scp_t pos, int align, int height, col4_t col, std::u // Calculate the string position float x = pos[X]; - float y = pos[Y]; + float y = std::floor(pos[Y]); if (align != F_LEFT) { // Calculate the real width of the string - float width = (float)StringWidthInternal(fh, str, height, scale); + float width = StringWidthInternal(fh, str, height, scale); switch (align) { case F_CENTRE: x = floor((renderer->VirtualScreenWidth() - width) / 2.0f + pos[X]); @@ -359,6 +365,9 @@ void r_font_c::DrawTextLine(scp_t pos, int align, int height, col4_t col, std::u } } + // Snap the starting x position to the pixel grid so the leading glyph isn't blurred. + x = std::round(x); + r_tex_c* curTex{}; auto drawCodepoint = [this, &curTex, &x, y](f_fontHeight_s* fh, int height, float scale, int yShift, char32_t cp) { diff --git a/engine/render/r_main.cpp b/engine/render/r_main.cpp index b08e1bc..480c323 100644 --- a/engine/render/r_main.cpp +++ b/engine/render/r_main.cpp @@ -1852,32 +1852,45 @@ int r_renderer_c::DrawStringCursorIndex(int height, int font, const char* str, i // ============== int r_renderer_c::VirtualScreenWidth() { - return VirtualMap(sys->video->vid.size[0]); + int const properWidth = apiDpiAware ? sys->video->vid.fbSize[0] : sys->video->vid.size[0]; + return VirtualMap(properWidth); } int r_renderer_c::VirtualScreenHeight() { - return VirtualMap(sys->video->vid.size[1]); + int const properHeight = apiDpiAware ? sys->video->vid.fbSize[1] : sys->video->vid.size[1]; + return VirtualMap(properHeight); } float r_renderer_c::VirtualScreenScaleFactor() { if (apiDpiAware) { + if (dpiScaleOverridePercent > 0) { + return dpiScaleOverridePercent / 100.0f; + } return sys->video->vid.dpiScale; } return 1.0f; } +void r_renderer_c::SetDpiScaleOverridePercent(int percent) { + dpiScaleOverridePercent = percent; +} + +int r_renderer_c::DpiScaleOverridePercent() const { + return dpiScaleOverridePercent; +} + int r_renderer_c::VirtualMap(int properValue) { if (apiDpiAware) { return properValue; } - return (int)(properValue / sys->video->vid.dpiScale); + return static_cast(properValue / sys->video->vid.dpiScale); } int r_renderer_c::VirtualUnmap(int mappedValue) { if (apiDpiAware) { return mappedValue; } - return (int)(mappedValue * sys->video->vid.dpiScale); + return static_cast(mappedValue * sys->video->vid.dpiScale); } // ===== diff --git a/engine/render/r_main.h b/engine/render/r_main.h index 991fdb2..366750c 100644 --- a/engine/render/r_main.h +++ b/engine/render/r_main.h @@ -99,6 +99,8 @@ class r_renderer_c: public r_IRenderer, public conCmdHandler_c { int VirtualScreenWidth(); int VirtualScreenHeight(); float VirtualScreenScaleFactor(); + void SetDpiScaleOverridePercent(int percent); + int DpiScaleOverridePercent() const; int VirtualMap(int properValue); int VirtualUnmap(int mappedValue); @@ -126,7 +128,7 @@ class r_renderer_c: public r_IRenderer, public conCmdHandler_c { PFNGLINSERTEVENTMARKEREXTPROC glInsertEventMarkerEXT = nullptr; PFNGLPUSHGROUPMARKEREXTPROC glPushGroupMarkerEXT = nullptr; PFNGLPOPGROUPMARKEREXTPROC glPopGroupMarkerEXT = nullptr; - + conVar_c* r_compress = nullptr; conVar_c* r_screenshotFormat = nullptr; conVar_c* r_layerDebug = nullptr; @@ -173,6 +175,7 @@ class r_renderer_c: public r_IRenderer, public conCmdHandler_c { }; bool apiDpiAware{}; + int dpiScaleOverridePercent = 0; RenderTarget rttMain[2]; int presentRtt = 0; diff --git a/ui_api.cpp b/ui_api.cpp index 233cdcb..74fe193 100644 --- a/ui_api.cpp +++ b/ui_api.cpp @@ -6,9 +6,11 @@ #include "ui_local.h" +#include #include #include #include +#include #include "core/core_tex_manipulation.h" @@ -798,10 +800,11 @@ static int l_SetViewport(lua_State* L) for (int i = 1; i <= 4; i++) { ui->LAssert(L, lua_isnumber(L, i), "SetViewport() argument %d: expected number, got %s", i, luaL_typename(L, i)); } - ui->renderer->SetViewport((int)(lua_tointeger(L, 1) * dpiScale), - (int)(lua_tointeger(L, 2) * dpiScale), - (int)(lua_tointeger(L, 3) * dpiScale), - (int)(lua_tointeger(L, 4) * dpiScale)); + int vpX = static_cast(std::lround(lua_tonumber(L, 1) * dpiScale)); + int vpY = static_cast(std::lround(lua_tonumber(L, 2) * dpiScale)); + int vpWidth = static_cast(std::ceil(lua_tonumber(L, 3) * dpiScale)); + int vpHeight = static_cast(std::ceil(lua_tonumber(L, 4) * dpiScale)); + ui->renderer->SetViewport(vpX, vpY, vpWidth, vpHeight); } else { ui->renderer->SetViewport(); @@ -1068,13 +1071,23 @@ static int l_DrawString(lua_State* L) static const char* alignMap[6] = { "LEFT", "CENTER", "RIGHT", "CENTER_X", "RIGHT_X", NULL }; static const char* fontMap[4] = { "FIXED", "VAR", "VAR BOLD", NULL }; const float dpiScale = ui->renderer->VirtualScreenScaleFactor(); + const float left = lua_tonumber(L, 1) * dpiScale; + const float top = lua_tonumber(L, 2) * dpiScale; + const lua_Number logicalHeight = lua_tonumber(L, 4); + int scaledHeight = (int)std::lround(logicalHeight * dpiScale); + if (scaledHeight <= 1) { + scaledHeight = std::max(1, scaledHeight); + } + else { + scaledHeight = (scaledHeight + 1) & ~1; + } ui->renderer->DrawString( - (float)lua_tonumber(L, 1) * dpiScale, - (float)lua_tonumber(L, 2) * dpiScale, + left, + top, luaL_checkoption(L, 3, "LEFT", alignMap), - (int)lua_tointeger(L, 4) * dpiScale, - NULL, - luaL_checkoption(L, 5, "FIXED", fontMap), + scaledHeight, + NULL, + luaL_checkoption(L, 5, "FIXED", fontMap), lua_tostring(L, 6) ); return 0; @@ -1091,9 +1104,19 @@ static int l_DrawStringWidth(lua_State* L) ui->LAssert(L, lua_isstring(L, 3), "DrawStringWidth() argument 3: expected string, got %s", luaL_typename(L, 3)); static const char* fontMap[4] = { "FIXED", "VAR", "VAR BOLD", NULL }; const float dpiScale = ui->renderer->VirtualScreenScaleFactor(); - lua_pushinteger(L, ui->renderer->DrawStringWidth((int)lua_tointeger(L, 1) * dpiScale, - luaL_checkoption(L, 2, "FIXED", fontMap), - lua_tostring(L, 3))); + const lua_Number logicalHeight = lua_tonumber(L, 1); + int scaledHeight = static_cast(std::lround(logicalHeight * dpiScale)); + if (scaledHeight <= 1) { + scaledHeight = std::max(1, scaledHeight); + } + else { + scaledHeight = (scaledHeight + 1) & ~1; + } + double const physicalWidth = ui->renderer->DrawStringWidth( + scaledHeight, + luaL_checkoption(L, 2, "FIXED", fontMap), + lua_tostring(L, 3)); + lua_pushnumber(L, physicalWidth / dpiScale); return 1; } @@ -1110,10 +1133,43 @@ static int l_DrawStringCursorIndex(lua_State* L) ui->LAssert(L, lua_isnumber(L, 4), "DrawStringCursorIndex() argument 4: expected number, got %s", luaL_typename(L, 4)); ui->LAssert(L, lua_isnumber(L, 5), "DrawStringCursorIndex() argument 5: expected number, got %s", luaL_typename(L, 5)); static const char* fontMap[4] = { "FIXED", "VAR", "VAR BOLD", NULL }; - lua_pushinteger(L, ui->renderer->DrawStringCursorIndex((int)lua_tointeger(L, 1) * dpiScale, - luaL_checkoption(L, 2, "FIXED", fontMap), - lua_tostring(L, 3), - (int)lua_tointeger(L, 4) * dpiScale, (int)lua_tointeger(L, 5) * dpiScale) + 1); + const lua_Number logicalHeight = lua_tonumber(L, 1); + const lua_Number logicalCursorX = lua_tonumber(L, 4); + const lua_Number logicalCursorY = lua_tonumber(L, 5); + int scaledHeight = static_cast(std::lround(logicalHeight * dpiScale)); + if (scaledHeight <= 1) { + scaledHeight = std::max(1, scaledHeight); + } + else { + scaledHeight = (scaledHeight + 1) & ~1; + } + const int scaledCursorX = static_cast(std::lround(logicalCursorX * dpiScale)); + const int scaledCursorY = static_cast(std::lround(logicalCursorY * dpiScale)); + lua_pushinteger(L, static_cast(ui->renderer->DrawStringCursorIndex( + scaledHeight, + luaL_checkoption(L, 2, "FIXED", fontMap), + lua_tostring(L, 3), + scaledCursorX, scaledCursorY) + 1)); + return 1; +} + +static int l_SetDPIScaleOverridePercent(lua_State* L) +{ + ui_main_c* ui = GetUIPtr(L); + ui->LAssert(L, ui->renderer != NULL, "Renderer is not initialised"); + int n = lua_gettop(L); + ui->LAssert(L, n >= 1, "Usage: SetDPIScaleOverridePercent(percent)"); + ui->LAssert(L, lua_isnumber(L, 1), "SetDPIScaleOverridePercent() argument 1: expected number, got %s", luaL_typename(L, 1)); + int percent = (int)lua_tointeger(L, 1); + ui->renderer->SetDpiScaleOverridePercent(percent); + return 0; +} + +static int l_GetDPIScaleOverridePercent(lua_State* L) +{ + ui_main_c* ui = GetUIPtr(L); + ui->LAssert(L, ui->renderer != NULL, "Renderer is not initialised"); + lua_pushinteger(L, ui->renderer->DpiScaleOverridePercent()); return 1; } @@ -1396,8 +1452,8 @@ static int l_GetCursorPos(lua_State* L) { ui_main_c* ui = GetUIPtr(L); const float dpiScale = ui->renderer->VirtualScreenScaleFactor(); - lua_pushinteger(L, ui->renderer->VirtualMap(ui->cursorX) / dpiScale); - lua_pushinteger(L, ui->renderer->VirtualMap(ui->cursorY) / dpiScale); + lua_pushinteger(L, (lua_Integer)std::lround(ui->renderer->VirtualMap(ui->cursorX) / dpiScale)); + lua_pushinteger(L, (lua_Integer)std::lround(ui->renderer->VirtualMap(ui->cursorY) / dpiScale)); return 2; } @@ -1409,8 +1465,10 @@ static int l_SetCursorPos(lua_State* L) ui->LAssert(L, n >= 2, "Usage: SetCursorPos(x, y)"); ui->LAssert(L, lua_isnumber(L, 1), "SetCursorPos() argument 1: expected number, got %s", luaL_typename(L, 1)); ui->LAssert(L, lua_isnumber(L, 2), "SetCursorPos() argument 2: expected number, got %s", luaL_typename(L, 2)); - int x = ui->renderer->VirtualUnmap((int)lua_tointeger(L, 1) * dpiScale); - int y = ui->renderer->VirtualUnmap((int)lua_tointeger(L, 2) * dpiScale); + const int scaledX = (int)std::lround(lua_tonumber(L, 1) * dpiScale); + const int scaledY = (int)std::lround(lua_tonumber(L, 2) * dpiScale); + int x = ui->renderer->VirtualUnmap(scaledX); + int y = ui->renderer->VirtualUnmap(scaledY); ui->sys->video->SetRelativeCursor(x, y); return 0; } @@ -2129,6 +2187,8 @@ int ui_main_c::InitAPI(lua_State* L) ADDFUNC(SetViewport); ADDFUNC(SetBlendMode); ADDFUNC(SetDrawColor); + ADDFUNC(SetDPIScaleOverridePercent); + ADDFUNC(GetDPIScaleOverridePercent); ADDFUNC(DrawImage); ADDFUNC(DrawImageQuad); ADDFUNC(DrawString);