Fix font loading for the UWP backend, although wacky rendering issues remain

This commit is contained in:
Henrik Rydgård
2025-11-08 15:46:00 +01:00
parent 4283e66a12
commit bf5ded9c02
5 changed files with 126 additions and 84 deletions
+94 -73
View File
@@ -1,4 +1,5 @@
#include "ppsspp_config.h"
#include "Common/System/Display.h"
#include "Common/GPU/thin3d.h"
#include "Common/Data/Hash/Hash.h"
@@ -6,17 +7,21 @@
#include "Common/Data/Encoding/Utf8.h"
#include "Common/Render/Text/draw_text.h"
#include "Common/Render/Text/draw_text_uwp.h"
#include "Common/File/VFS/VFS.h"
#include "Common/Log.h"
#include "Common/StringUtils.h"
#include "Common/File/Path.h"
#if PPSSPP_PLATFORM(UWP)
#include <string>
#include <d3d11.h>
#include <dxgi1_3.h>
#include <d2d1_3.h>
#include <dwrite_3.h>
using namespace Microsoft::WRL;
enum {
MAX_TEXT_WIDTH = 4096,
MAX_TEXT_HEIGHT = 512
@@ -24,47 +29,43 @@ enum {
class TextDrawerFontContext {
public:
~TextDrawerFontContext() {
Destroy();
}
void Create() {
if (textFmt) {
Destroy();
TextDrawerFontContext(const FontStyle &_style, float _dpiScale, IDWriteFactory4 *factory, IDWriteFontCollection1 *fontCollection) : style(_style), dpiScale(_dpiScale) {
DWRITE_FONT_STYLE fontStyle = DWRITE_FONT_STYLE_NORMAL;
DWRITE_FONT_WEIGHT weight = DWRITE_FONT_WEIGHT_NORMAL;
int height = style.sizePts;
FontStyleFlags styleFlags = FontStyleFlags::Default;
std::string fontName = GetFontNameForFontStyle(style, &styleFlags);
if (styleFlags & FontStyleFlags::Bold) {
weight = DWRITE_FONT_WEIGHT_BOLD;
}
if (!factory) {
return;
if (styleFlags & FontStyleFlags::Italic) {
fontStyle = DWRITE_FONT_STYLE_ITALIC;
}
HRESULT hr = factory->CreateTextFormat(
fname.c_str(),
ConvertUTF8ToWString(fontName).c_str(),
fontCollection,
weight,
italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL,
fontStyle,
DWRITE_FONT_STRETCH_NORMAL,
(float)MulDiv(height, (int)(96.0f * (1.0f / dpiScale)), 72),
L"",
L"en-us",
&textFmt
);
if (FAILED(hr)) {
ERROR_LOG(Log::G3D, "Failed creating font %s", fname.c_str());
ERROR_LOG(Log::G3D, "Failed creating text format for font %s", fontName.c_str());
}
}
void Destroy() {
~TextDrawerFontContext() {
if (textFmt) {
textFmt->Release();
}
textFmt = nullptr;
}
IDWriteFactory4 *factory = nullptr;
IDWriteFontCollection1 *fontCollection = nullptr;
IDWriteTextFormat *textFmt = nullptr;
std::wstring fname;
int height;
DWRITE_FONT_WEIGHT weight;
bool italic = false;
FontStyle style;
float dpiScale;
};
@@ -105,18 +106,65 @@ TextDrawerUWP::TextDrawerUWP(Draw::DrawContext *draw) : TextDrawer(draw), ctx_(n
hr = m_d2dDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &m_d2dContext);
if (FAILED(hr)) _assert_msg_(false, "D2D CreateDeviceContext failed");
// Load the Roboto font
hr = m_dwriteFactory->CreateFontFileReference(L"Content/Roboto-Condensed.ttf", nullptr, &m_fontFile);
if (FAILED(hr)) ERROR_LOG(Log::System, "CreateFontFileReference failed");
hr = m_dwriteFactory->CreateFontSetBuilder(&m_fontSetBuilder);
if (FAILED(hr)) ERROR_LOG(Log::System, "CreateFontSetBuilder failed");
hr = m_fontSetBuilder->AddFontFile(m_fontFile);
hr = m_dwriteFactory->CreateInMemoryFontFileLoader(&m_inMemoryLoader);
if (FAILED(hr)) {
// handle error
}
m_dwriteFactory->RegisterFontFileLoader(m_inMemoryLoader);
// Load our fonts.
std::vector<std::string> fontFilenames;
GetFilenamesForFontStyle(FontStyle{}, &fontFilenames, true);
for (const auto &fname : fontFilenames) {
size_t size;
uint8_t *data = g_VFS.ReadFile(fname.c_str(), &size);
ComPtr<IDWriteFontFile> fontFile;
hr = m_inMemoryLoader->CreateInMemoryFontFileReference(
m_dwriteFactory,
data,
static_cast<UINT32>(size),
nullptr, // optional last write time
&fontFile
);
m_fontSetBuilder->AddFontFile(fontFile.Get());
delete[] data;
m_fontFiles.push_back(fontFile);
}
hr = m_fontSetBuilder->CreateFontSet(&m_fontSet);
hr = m_dwriteFactory->CreateFontCollectionFromFontSet(m_fontSet, &m_fontCollection);
UINT32 familyCount = m_fontCollection->GetFontFamilyCount();
for (UINT32 i = 0; i < familyCount; ++i) {
IDWriteFontFamily *family;
m_fontCollection->GetFontFamily(i, &family);
IDWriteLocalizedStrings *names;
family->GetFamilyNames(&names);
UINT32 length = 0;
names->GetStringLength(0, &length);
std::wstring name(length + 1, L'\0');
names->GetString(0, &name[0], length + 1);
std::string nname = ConvertWStringToUTF8(name);
NOTICE_LOG(Log::G3D, "Font family: %s", nname.c_str());
names->Release();
family->Release();
}
ctx_ = new TextDrawerContext();
D2D1_BITMAP_PROPERTIES1 properties;
D2D1_BITMAP_PROPERTIES1 properties{};
properties.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
properties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
properties.dpiX = 96.0f;
@@ -154,64 +202,38 @@ TextDrawerUWP::~TextDrawerUWP() {
ctx_->bitmap->Release();
ctx_->mirror_bmp->Release();
m_dwriteFactory->UnregisterFontFileLoader(m_inMemoryLoader);
m_fontCollection->Release();
m_fontSet->Release();
for (auto file : m_fontFiles) {
file->Release();
}
m_fontFiles.clear();
m_fontSetBuilder->Release();
m_dwriteFactory->Release();
m_inMemoryLoader->Release();
m_d2dWhiteBrush->Release();
m_d2dContext->Release();
m_d2dDevice->Release();
m_d2dFactory->Release();
m_fontCollection->Release();
m_fontSet->Release();
m_fontFile->Release();
m_fontSetBuilder->Release();
m_dwriteFactory->Release();
delete ctx_;
}
uint32_t TextDrawerUWP::SetOrCreateFont(const FontStyle &style) {
const uint32_t fontHash = style.Hash();
auto iter = fontMap_.find(fontHash);
void TextDrawerUWP::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 = DWRITE_FONT_WEIGHT_NORMAL;
if (style.flags & FontStyleFlags::Bold) {
font->weight = DWRITE_FONT_WEIGHT_BOLD;
}
if (style.flags & FontStyleFlags::Italic) {
font->italic = true;
}
font->height = style.sizePts;
font->fname = fname;
font->dpiScale = dpiScale_;
font->factory = m_dwriteFactory;
font->fontCollection = m_fontCollection;
font->Create();
fontMap_[fontHash] = std::unique_ptr<TextDrawerFontContext>(font);
fontHash_ = fontHash;
return fontHash;
}
void TextDrawerUWP::SetFont(uint32_t fontHandle) {
auto iter = fontMap_.find(fontHandle);
if (iter != fontMap_.end()) {
fontHash_ = fontHandle;
}
fontMap_[style] = std::make_unique<TextDrawerFontContext>(style, dpiScale_, m_dwriteFactory, m_fontCollection);
fontStyle_ = style;
}
void TextDrawerUWP::MeasureStringInternal(std::string_view str, float *w, float *h) {
IDWriteTextFormat* format = nullptr;
auto iter = fontMap_.find(fontHash_);
auto iter = fontMap_.find(fontStyle_);
if (iter != fontMap_.end()) {
format = iter->second->textFmt;
}
@@ -249,7 +271,7 @@ bool TextDrawerUWP::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStrin
SIZE size;
IDWriteTextFormat *format = nullptr;
auto iter = fontMap_.find(fontHash_);
auto iter = fontMap_.find(fontStyle_);
if (iter != fontMap_.end()) {
format = iter->second->textFmt;
}
@@ -377,7 +399,6 @@ bool TextDrawerUWP::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStrin
void TextDrawerUWP::ClearFonts() {
for (auto &iter : fontMap_) {
iter.second->dpiScale = dpiScale_;
iter.second->Destroy();
}
fontMap_.clear();
}
+5 -5
View File
@@ -9,6 +9,7 @@
#include <d2d1_3.h>
#include <dwrite_3.h>
#include <wrl/client.h>
struct TextDrawerContext;
// Internal struct but all details in .cpp file (pimpl to avoid pulling in excessive headers here)
@@ -19,8 +20,7 @@ public:
TextDrawerUWP(Draw::DrawContext *draw);
~TextDrawerUWP();
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:
@@ -29,7 +29,7 @@ protected:
void ClearFonts() override;
TextDrawerContext *ctx_;
std::map<uint32_t, std::unique_ptr<TextDrawerFontContext>> fontMap_;
std::map<FontStyle, std::unique_ptr<TextDrawerFontContext>> fontMap_;
// Direct2D drawing components.
ID2D1Factory5* m_d2dFactory;
@@ -39,11 +39,11 @@ protected:
// DirectWrite drawing components.
IDWriteFactory5* m_dwriteFactory;
IDWriteFontFile* m_fontFile;
IDWriteFontSet* m_fontSet;
IDWriteFontSetBuilder1* m_fontSetBuilder;
IDWriteFontCollection1* m_fontCollection;
IDWriteInMemoryFontFileLoader *m_inMemoryLoader;
std::vector<Microsoft::WRL::ComPtr<IDWriteFontFile>> m_fontFiles;
};
#endif
+1 -4
View File
@@ -698,10 +698,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch
auto des = GetI18NCategory(I18NCat::DESKTOPUI);
#if PPSSPP_PLATFORM(UWP)
// Roboto font is loaded in TextDrawerUWP.
g_Config.sFont = des->T("Font", "Roboto");
#elif defined(USING_QT_UI)
#if defined(USING_QT_UI)
size_t fontSize = 0;
uint8_t *fontData = g_VFS.ReadFile("Roboto-Condensed.ttf", &fontSize);
if (fontData) {
+13 -1
View File
@@ -451,7 +451,19 @@
</Text>
</ItemGroup>
<ItemGroup>
<Font Include="..\Assets\Roboto-Condensed.ttf">
<Font Include="..\Assets\Roboto_Condensed-Regular.ttf">
<Link>Content\%(Filename)%(Extension)</Link>
</Font>
<Font Include="..\Assets\Roboto_Condensed-Bold.ttf">
<Link>Content\%(Filename)%(Extension)</Link>
</Font>
<Font Include="..\Assets\Roboto_Condensed-Italic.ttf">
<Link>Content\%(Filename)%(Extension)</Link>
</Font>
<Font Include="..\Assets\Roboto_Condensed-Light.ttf">
<Link>Content\%(Filename)%(Extension)</Link>
</Font>
<Font Include="..\Assets\Inconsolata-Regular.ttf">
<Link>Content\%(Filename)%(Extension)</Link>
</Font>
</ItemGroup>
+13 -1
View File
@@ -290,7 +290,19 @@
</Text>
</ItemGroup>
<ItemGroup>
<Font Include="..\Assets\Roboto-Condensed.ttf">
<Font Include="..\Assets\Roboto_Condensed-Regular.ttf">
<Filter>Content</Filter>
</Font>
<Font Include="..\Assets\Roboto_Condensed-Bold.ttf">
<Filter>Content</Filter>
</Font>
<Font Include="..\Assets\Roboto_Condensed-Light.ttf">
<Filter>Content</Filter>
</Font>
<Font Include="..\Assets\Roboto_Condensed-Italic.ttf">
<Filter>Content</Filter>
</Font>
<Font Include="..\Assets\Inconsolata-Regular.ttf">
<Filter>Content</Filter>
</Font>
</ItemGroup>