Font style rework: Windows part

This commit is contained in:
Henrik Rydgård
2025-11-08 14:47:18 +01:00
parent 980ddb152b
commit 4283e66a12
18 changed files with 212 additions and 119 deletions
+1 -1
View File
@@ -2786,7 +2786,7 @@ set(NativeAssets
assets/shaders
assets/themes
assets/vfpu
assets/Roboto-Condensed.ttf
assets/Roboto_Condensed-Regular.ttf
assets/7z.png
assets/compat.ini
assets/infra-dns.json
+19 -14
View File
@@ -4,31 +4,36 @@
#include <string_view>
#include "Common/Common.h"
#include "Common/CommonTypes.h"
#include "Common/Render/TextureAtlas.h"
#include "Common/Data/Hash/Hash.h"
enum class FontStyleFlags {
enum class FontStyleFlags : u8 {
Default = 0,
Bold = 1,
Light = 2,
Italic = 16,
};
ENUM_CLASS_BITOPS(FontStyleFlags);
enum class FontFamily : u8 {
SansSerif = 1,
Fixed = 2,
};
struct FontStyle {
FontStyle() {}
FontStyle(FontID atlasFnt, std::string_view name, int size, FontStyleFlags _flags = FontStyleFlags::Default) : atlasFont(atlasFnt), fontName(name), sizePts(size), flags(_flags) {}
constexpr FontStyle(FontFamily _family, int size, FontStyleFlags _flags) : family(_family), sizePts(size), flags(_flags) {}
u32 Hash() const {
u32 hash = fontName.empty() ? 0 : hash::Adler32(fontName);
hash ^= sizePts;
hash ^= (int)flags << 10;
return hash;
}
FontID atlasFont{nullptr};
// For native fonts:
std::string fontName;
int sizePts = 0;
u16 sizePts = 0;
FontFamily family = FontFamily::SansSerif;
FontStyleFlags flags = FontStyleFlags::Default;
};
inline constexpr bool operator<(const FontStyle &a, const FontStyle &b) {
if (a.family != b.family)
return a.family < b.family;
if (a.sizePts != b.sizePts)
return a.sizePts < b.sizePts;
return a.flags < b.flags;
}
+83 -3
View File
@@ -1,5 +1,6 @@
#include "ppsspp_config.h"
#include <algorithm>
#include "Common/System/Display.h"
#include "Common/GPU/thin3d.h"
#include "Common/Data/Hash/Hash.h"
@@ -13,6 +14,7 @@
#include "Common/Render/Text/draw_text_qt.h"
#include "Common/Render/Text/draw_text_android.h"
#include "Common/Render/Text/draw_text_sdl.h"
#include "Common/StringUtils.h"
TextDrawer::TextDrawer(Draw::DrawContext *draw) : draw_(draw) {
// These probably shouldn't be state.
@@ -47,7 +49,7 @@ void TextDrawer::DrawString(DrawBuffer &target, std::string_view str, float x, f
return;
}
CacheKey key{ std::string(str), fontHash_ };
const CacheKeyType key{ std::string(str), fontStyle_ };
target.Flush(true);
TextStringEntry *entry;
@@ -124,7 +126,7 @@ void TextDrawer::MeasureString(std::string_view str, float *w, float *h) {
return;
}
CacheKey key{ std::string(str), fontHash_ };
const CacheKeyType key{std::string(str), fontStyle_};
TextMeasureEntry *entry;
auto iter = sizeCache_.find(key);
@@ -204,7 +206,7 @@ void TextDrawer::ClearCache() {
}
cache_.clear();
sizeCache_.clear();
fontHash_ = 0;
fontStyle_ = {};
}
void TextDrawer::OncePerFrame() {
@@ -271,3 +273,81 @@ TextDrawer *TextDrawer::Create(Draw::DrawContext *draw) {
}
return drawer;
}
struct FontDesc {
FontFamily family;
FontStyleFlags flags;
std::string_view fontName;
std::string_view filename;
};
// Append ".ttf" to get the actual filenames.
static const FontDesc g_fontDescs[] = {
{FontFamily::SansSerif, FontStyleFlags::Default, "Roboto Condensed", "Roboto_Condensed-Regular"},
{FontFamily::SansSerif, FontStyleFlags::Bold, "Roboto Condensed", "Roboto_Condensed-Bold"},
{FontFamily::SansSerif, FontStyleFlags::Italic, "Roboto Condensed", "Roboto_Condensed-Italic"},
{FontFamily::SansSerif, FontStyleFlags::Light, "Roboto Condensed", "Roboto_Condensed-Light"},
{FontFamily::Fixed, FontStyleFlags::Default, "Inconsolata", "Inconsolata-Regular"},
};
std::map<FontFamily, std::string> g_fontOverrides;
void SetFontNameOverride(FontFamily family, std::string_view overrideFont) {
g_fontOverrides[family] = std::string(overrideFont);
}
void GetFilenamesForFontStyle(const FontStyle &font, std::vector<std::string> *out, bool all) {
for (const auto &desc : g_fontDescs) {
bool match = all;
if (desc.flags == font.flags && desc.family == font.family) {
match = true;
}
if (match) {
out->push_back(join(desc.filename, ".ttf"));
}
}
if (all) {
// uniquify idiom
std::sort(out->begin(), out->end());
out->erase(std::unique(out->begin(), out->end()), out->end());
return;
}
if (!out->empty()) {
return;
}
// If nothing matched, just return the first font of the requested family.
for (const auto &desc : g_fontDescs) {
if (desc.family == font.family) {
out->push_back(join(desc.filename, ".ttf"));
}
}
}
std::string GetFontNameForFontStyle(const FontStyle &font, FontStyleFlags *outFlags) {
auto override = g_fontOverrides.find(font.family);
if (override != g_fontOverrides.end()) {
*outFlags = font.flags;
return override->second;
}
for (const auto &desc : g_fontDescs) {
if (desc.flags == font.flags && desc.family == font.family) {
*outFlags = font.flags;
return std::string(desc.fontName);
}
}
// If nothing matched, just return the first font of the requested family.
for (const auto &desc : g_fontDescs) {
if (desc.family == font.family) {
// Return the flags of the font we found.
// TODO: Some platforms can actually synthesize traits (bold, italic) if missing.
*outFlags = desc.flags;
return std::string(desc.fontName);
}
}
return "";
}
+14 -17
View File
@@ -46,8 +46,7 @@ public:
virtual ~TextDrawer() = default;
virtual bool IsReady() const { return true; }
virtual uint32_t SetOrCreateFont(const FontStyle &style) = 0;
virtual void SetFont(uint32_t fontHandle) = 0; // Shortcut once you've set the font once.
virtual void SetOrCreateFont(const FontStyle &style) = 0;
void SetFontScale(float xscale, float yscale);
void MeasureString(std::string_view str, float *w, float *h);
@@ -114,17 +113,7 @@ protected:
return (a << 24) | (r << 16) | (g << 8) | b;
}
struct CacheKey {
bool operator < (const CacheKey &other) const {
if (fontHash < other.fontHash)
return true;
if (fontHash > other.fontHash)
return false;
return text < other.text;
}
std::string text;
uint32_t fontHash;
};
typedef std::pair<std::string, FontStyle> CacheKeyType;
Draw::DrawContext *draw_;
@@ -133,11 +122,10 @@ protected:
float fontScaleY_ = 1.0f;
float dpiScale_ = 1.0f;
bool ignoreGlobalDpi_ = false;
FontStyle fontStyle_{};
uint32_t fontHash_ = 0;
std::map<CacheKey, std::unique_ptr<TextStringEntry>> cache_;
std::map<CacheKey, std::unique_ptr<TextMeasureEntry>> sizeCache_;
std::map<CacheKeyType, std::unique_ptr<TextStringEntry>> cache_;
std::map<CacheKeyType, std::unique_ptr<TextMeasureEntry>> sizeCache_;
};
class TextDrawerWordWrapper : public WordWrapper {
@@ -150,3 +138,12 @@ protected:
TextDrawer *drawer_;
};
// The backends can use this to query the filenames of the fonts.
// Some backends want to just load all the fonts, just pass all.
// Note that the ttf file extension is included in the output.
void GetFilenamesForFontStyle(const FontStyle &font, std::vector<std::string> *out, bool all);
std::string GetFontNameForFontStyle(const FontStyle &font, FontStyleFlags *outFlags);
// Some languages use an override font, set via ini file.
void SetFontNameOverride(FontFamily family, std::string_view overrideFont);
+58 -47
View File
@@ -22,11 +22,25 @@ enum {
class TextDrawerFontContext {
public:
~TextDrawerFontContext() {
Destroy();
}
explicit TextDrawerFontContext(const FontStyle &_style, float _dpiScale) : hFont(0), style(_style), dpiScale(_dpiScale) {
FontStyleFlags styleFlags{};
std::string fontName = GetFontNameForFontStyle(style, &styleFlags);
if (fontName.empty()) {
// Shouldn't happen.
fontName = "Tahoma";
}
int weight = FW_NORMAL;
if (styleFlags & FontStyleFlags::Bold) {
weight = FW_BOLD;
}
if (styleFlags & FontStyleFlags::Light) {
weight = FW_LIGHT;
}
bool italic = (styleFlags & FontStyleFlags::Italic);
int height = style.sizePts;
void Create() {
if (hFont) {
Destroy();
}
@@ -35,18 +49,20 @@ public:
hFont = CreateFont(nHeight, 0, 0, 0, weight, italic,
FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, PROOF_QUALITY,
VARIABLE_PITCH, fname.c_str());
VARIABLE_PITCH, ConvertUTF8ToWString(fontName).c_str());
}
~TextDrawerFontContext() {
Destroy();
}
void Destroy() {
DeleteObject(hFont);
hFont = 0;
}
HFONT hFont;
std::wstring fname;
int height;
int weight;
bool italic = false;
HFONT hFont = 0;
FontStyle style;
float dpiScale;
};
@@ -60,8 +76,18 @@ TextDrawerWin32::TextDrawerWin32(Draw::DrawContext *draw) : TextDrawer(draw), ct
ctx_ = new TextDrawerContext();
ctx_->hDC = CreateCompatibleDC(NULL);
BITMAPINFO bmi;
ZeroMemory(&bmi.bmiHeader, sizeof(BITMAPINFOHEADER));
// Load the font files (pass the all flags so we get all filenames);
std::vector<std::string> fonts;
GetFilenamesForFontStyle(FontStyle(FontFamily::SansSerif, 16, FontStyleFlags::Default), &fonts, true);
for (const auto &iter : fonts) {
std::string fn = "assets/" + iter;
int numFontsAdded = AddFontResourceEx(ConvertUTF8ToWString(fn).c_str(), FR_PRIVATE, NULL);
if (numFontsAdded == 0) {
ERROR_LOG(Log::G3D, "Failed to add font resource from %s", fn.c_str());
}
}
BITMAPINFO bmi{};
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = MAX_TEXT_WIDTH;
bmi.bmiHeader.biHeight = -MAX_TEXT_HEIGHT;
@@ -80,53 +106,37 @@ TextDrawerWin32::~TextDrawerWin32() {
ClearCache();
ClearFonts();
// Unload the fonts.
std::vector<std::string> fonts;
GetFilenamesForFontStyle(FontStyle(FontFamily::SansSerif, 16, FontStyleFlags::Default), &fonts, true);
for (const auto &iter : fonts) {
std::string fn = "assets/" + iter + ".ttf";
RemoveFontResourceEx(ConvertUTF8ToWString(fn).c_str(), FR_PRIVATE, NULL);
}
DeleteObject(ctx_->hbmBitmap);
DeleteDC(ctx_->hDC);
delete ctx_;
}
uint32_t TextDrawerWin32::SetOrCreateFont(const FontStyle &style) {
const uint32_t fontHash = style.Hash();
auto iter = fontMap_.find(fontHash);
void TextDrawerWin32::SetOrCreateFont(const FontStyle &style) {
auto iter = fontMap_.find(style);
if (iter != fontMap_.end()) {
fontHash_ = fontHash;
return fontHash;
fontStyle_ = style;
return;
}
std::wstring fname;
if (!style.fontName.empty())
fname = ConvertUTF8ToWString(style.fontName);
else
fname = L"Tahoma";
TextDrawerFontContext *font = new TextDrawerFontContext();
font->weight = FW_LIGHT;
if (style.flags & FontStyleFlags::Bold) {
font->weight = FW_BOLD;
}
font->italic = (style.flags & FontStyleFlags::Italic);
font->height = style.sizePts;
font->fname = fname;
font->dpiScale = dpiScale_;
font->Create();
fontMap_[fontHash] = std::unique_ptr<TextDrawerFontContext>(font);
fontHash_ = fontHash;
return fontHash;
}
void TextDrawerWin32::SetFont(uint32_t fontHandle) {
auto iter = fontMap_.find(fontHandle);
if (iter != fontMap_.end()) {
fontHash_ = fontHandle;
}
fontMap_[style] = std::make_unique<TextDrawerFontContext>(style, dpiScale_);
fontStyle_ = style;
}
void TextDrawerWin32::MeasureStringInternal(std::string_view str, float *w, float *h) {
auto iter = fontMap_.find(fontHash_);
auto iter = fontMap_.find(fontStyle_);
if (iter != fontMap_.end()) {
SelectObject(ctx_->hDC, iter->second->hFont);
} else {
ERROR_LOG(Log::G3D, "Failed to measure string");
return;
}
#if 0 && defined(_DEBUG)
@@ -165,10 +175,11 @@ bool TextDrawerWin32::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStr
}
std::wstring wstr = ConvertUTF8ToWString(ReplaceAll(str, "\n", "\r\n"));
auto iter = fontMap_.find(fontHash_);
auto iter = fontMap_.find(fontStyle_);
if (iter != fontMap_.end()) {
SelectObject(ctx_->hDC, iter->second->hFont);
}
// Set text properties
SetTextColor(ctx_->hDC, 0xFFFFFF);
SetBkColor(ctx_->hDC, 0);
+2 -3
View File
@@ -16,8 +16,7 @@ public:
TextDrawerWin32(Draw::DrawContext *draw);
~TextDrawerWin32();
uint32_t SetOrCreateFont(const FontStyle &style) override;
void SetFont(uint32_t fontHandle) override; // Shortcut once you've set the font once.
void SetOrCreateFont(const FontStyle &style) override;
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override;
protected:
@@ -26,7 +25,7 @@ protected:
void ClearFonts() override;
TextDrawerContext *ctx_;
std::map<uint32_t, std::unique_ptr<TextDrawerFontContext>> fontMap_;
std::map<FontStyle, std::unique_ptr<TextDrawerFontContext>> fontMap_;
};
#endif
+11 -6
View File
@@ -206,12 +206,17 @@ void UIContext::SetFontStyle(const FontStyle &fontStyle) {
}
}
static FontID AtlasFontFromStyle(const FontStyle &style) {
// For now, we only have one font in the atlas.
return FontID("UBUNTU24");
}
void UIContext::MeasureText(const FontStyle &style, float scaleX, float scaleY, std::string_view str, float *x, float *y, int align) const {
_dbg_assert_(str.data() != nullptr);
if (!textDrawer_ || (align & FLAG_DYNAMIC_ASCII)) {
float sizeFactor = (float)style.sizePts / 24.0f;
Draw()->SetFontScale(scaleX * sizeFactor, scaleY * sizeFactor);
Draw()->MeasureText(style.atlasFont, str, x, y);
Draw()->MeasureText(AtlasFontFromStyle(style), str, x, y);
} else {
textDrawer_->SetOrCreateFont(style);
textDrawer_->SetFontScale(scaleX, scaleY);
@@ -225,7 +230,7 @@ void UIContext::MeasureTextRect(const FontStyle &style, float scaleX, float scal
if (!textDrawer_ || (align & FLAG_DYNAMIC_ASCII)) {
float sizeFactor = (float)style.sizePts / 24.0f;
Draw()->SetFontScale(scaleX * sizeFactor, scaleY * sizeFactor);
Draw()->MeasureTextRect(style.atlasFont, str, bounds, x, y, align);
Draw()->MeasureTextRect(AtlasFontFromStyle(style), str, bounds, x, y, align);
} else {
textDrawer_->SetOrCreateFont(style);
textDrawer_->SetFontScale(scaleX, scaleY);
@@ -238,14 +243,14 @@ void UIContext::DrawText(std::string_view str, float x, float y, uint32_t color,
_dbg_assert_(str.data() != nullptr);
if (!textDrawer_ || (align & FLAG_DYNAMIC_ASCII)) {
// Use the font texture if this font is in that texture instead.
bool useFontTexture = Draw()->GetFontAtlas()->getFont(fontStyle_->atlasFont) != nullptr;
bool useFontTexture = Draw()->GetFontAtlas()->getFont(AtlasFontFromStyle(*fontStyle_)) != nullptr;
if (useFontTexture) {
Flush();
BindFontTexture();
}
float sizeFactor = (float)fontStyle_->sizePts / 24.0f;
Draw()->SetFontScale(fontScaleX_ * sizeFactor, fontScaleY_ * sizeFactor);
Draw()->DrawText(fontStyle_->atlasFont, str, x, y, color, align);
Draw()->DrawText(AtlasFontFromStyle(*fontStyle_), str, x, y, color, align);
if (useFontTexture)
Flush();
} else {
@@ -264,14 +269,14 @@ void UIContext::DrawTextShadow(std::string_view str, float x, float y, uint32_t
void UIContext::DrawTextRect(std::string_view str, const Bounds &bounds, uint32_t color, int align) {
if (!textDrawer_ || (align & FLAG_DYNAMIC_ASCII)) {
// Use the font texture if this font is in that texture instead.
bool useFontTexture = Draw()->GetFontAtlas()->getFont(fontStyle_->atlasFont) != nullptr;
bool useFontTexture = Draw()->GetFontAtlas()->getFont(AtlasFontFromStyle(*fontStyle_)) != nullptr;
if (useFontTexture) {
Flush();
BindFontTexture();
}
float sizeFactor = (float)fontStyle_->sizePts / 24.0f;
Draw()->SetFontScale(fontScaleX_ * sizeFactor, fontScaleY_ * sizeFactor);
Draw()->DrawTextRect(fontStyle_->atlasFont, str, bounds.x, bounds.y, bounds.w, bounds.h, color, align);
Draw()->DrawTextRect(AtlasFontFromStyle(*fontStyle_), str, bounds.x, bounds.y, bounds.w, bounds.h, color, align);
if (useFontTexture)
Flush();
} else {
+3 -1
View File
@@ -79,9 +79,11 @@ struct Style {
// To use with an UI atlas.
struct Theme {
FontStyle uiFont;
FontStyle uiFontTiny;
FontStyle uiFontSmall;
FontStyle uiFont;
FontStyle uiFontBig;
FontStyle uiFontCode;
ImageID checkOn;
ImageID checkOff;
-2
View File
@@ -171,8 +171,6 @@ public:
bool bIgnoreWindowsKey;
bool bRestartRequired;
std::string sFont;
bool bPauseWhenMinimized;
bool bPauseExitsEmulator;
+1 -1
View File
@@ -769,7 +769,7 @@ static bool HasTextDrawer() {
if (textDrawer) {
textDrawer->SetFontScale(1.0f, 1.0f);
textDrawer->SetForcedDPIScale(1.0f);
textDrawer->SetOrCreateFont(FontStyle(FontID::invalid(), g_Config.sFont, 18, FontStyleFlags::Default));
textDrawer->SetOrCreateFont(FontStyle(FontFamily::SansSerif, 18, FontStyleFlags::Default));
}
textDrawerInited = true;
return textDrawer != nullptr;
+3 -14
View File
@@ -697,22 +697,10 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch
}
auto des = GetI18NCategory(I18NCat::DESKTOPUI);
// Note to translators: do not translate this/add this to PPSSPP-lang's files.
// It's intended to be custom for every user.
// Only add it to your own personal copies of PPSSPP.
#if PPSSPP_PLATFORM(UWP)
// Roboto font is loaded in TextDrawerUWP.
g_Config.sFont = des->T("Font", "Roboto");
#elif defined(USING_WIN_UI) && !PPSSPP_PLATFORM(UWP)
// TODO: Could allow a setting to specify a font file to load?
// TODO: Make this a constant if we can sanely load the font on other systems?
AddFontResourceEx(L"assets/Roboto-Condensed.ttf", FR_PRIVATE, NULL);
// The font goes by two names, let's allow either one.
if (CheckFontIsUsable(L"Roboto Condensed")) {
g_Config.sFont = des->T("Font", "Roboto Condensed");
} else {
g_Config.sFont = des->T("Font", "Roboto");
}
#elif defined(USING_QT_UI)
size_t fontSize = 0;
uint8_t *fontData = g_VFS.ReadFile("Roboto-Condensed.ttf", &fontSize);
@@ -852,7 +840,8 @@ bool NativeInitGraphics(GraphicsContext *graphicsContext) {
uiContext->Init(g_draw, texColorPipeline, colorPipeline, &ui_draw2d);
if (uiContext->Text()) {
uiContext->Text()->SetOrCreateFont(FontStyle(FontID::invalid(), "Tahoma", 20, FontStyleFlags::Default));
// This seems unnecessary.
// uiContext->Text()->SetOrCreateFont(FontStyle(FontID::invalid(), FontFamily::SansSerif, 20, FontStyleFlags::Default));
}
g_screenManager->setUIContext(uiContext);
+17 -10
View File
@@ -25,7 +25,8 @@
#include "Common/File/DirListing.h"
#include "Common/Log/LogManager.h"
#include "Common/File/VFS/VFS.h"
#include "Common/Data/Text/I18n.h"
#include "Common/Render/Text/draw_text.h"
#include "Core/Config.h"
#include "Common/UI/View.h"
@@ -198,15 +199,21 @@ void UpdateTheme() {
}
}
#if defined(USING_WIN_UI) || PPSSPP_PLATFORM(UWP) || defined(USING_QT_UI)
ui_theme.uiFont = FontStyle(FontID("UBUNTU24"), g_Config.sFont.c_str(), 22);
ui_theme.uiFontSmall = FontStyle(FontID("UBUNTU24"), g_Config.sFont.c_str(), 17);
ui_theme.uiFontBig = FontStyle(FontID("UBUNTU24"), g_Config.sFont.c_str(), 28, FontStyleFlags::Bold);
#else
ui_theme.uiFont = FontStyle(FontID("UBUNTU24"), "", 20);
ui_theme.uiFontSmall = FontStyle(FontID("UBUNTU24"), "", 15);
ui_theme.uiFontBig = FontStyle(FontID("UBUNTU24"), "", 26, FontStyleFlags::Bold);
#endif
// Desktop font override support
auto des = GetI18NCategory(I18NCat::DESKTOPUI);
std::string_view fontOverride = des->T("Font", "");
if (fontOverride == "Font") {
fontOverride = "";
}
if (!fontOverride.empty()) {
SetFontNameOverride(FontFamily::SansSerif, fontOverride);
}
ui_theme.uiFontTiny = FontStyle(FontFamily::SansSerif, 12, FontStyleFlags::Default);
ui_theme.uiFontSmall = FontStyle(FontFamily::SansSerif, 17, FontStyleFlags::Default);
ui_theme.uiFont = FontStyle(FontFamily::SansSerif, 22, FontStyleFlags::Default);
ui_theme.uiFontBig = FontStyle(FontFamily::SansSerif, 28, FontStyleFlags::Bold);
ui_theme.uiFontCode = FontStyle(FontFamily::Fixed, 14, FontStyleFlags::Default);
ui_theme.checkOn = ImageID("I_CHECKEDBOX");
ui_theme.checkOff = ImageID("I_UNCHECKEDBOX");
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.