diff --git a/openVulkanoCpp/Base/Utils.hpp b/openVulkanoCpp/Base/Utils.hpp index 7654bc9..1e56708 100644 --- a/openVulkanoCpp/Base/Utils.hpp +++ b/openVulkanoCpp/Base/Utils.hpp @@ -187,11 +187,11 @@ namespace OpenVulkano } template - static Array ReadFile(const T& filePath, bool emptyOnMissing = false, + static Array ReadFile(const T& filePath, bool emptyOnMissing = true, bool nullTerminateString = false); template - static Array ReadFile(const char (&filePath)[N], bool emptyOnMissing = false, + static Array ReadFile(const char (&filePath)[N], bool emptyOnMissing = true, bool nullTerminateString = false) { return ReadFile(std::string(filePath), emptyOnMissing, nullTerminateString); diff --git a/openVulkanoCpp/Host/SystemFontResolver.hpp b/openVulkanoCpp/Host/SystemFontResolver.hpp new file mode 100644 index 0000000..eb4c8b3 --- /dev/null +++ b/openVulkanoCpp/Host/SystemFontResolver.hpp @@ -0,0 +1,22 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include +#include + +namespace OpenVulkano +{ + class SystemFontResolver + { + public: + static std::string GetSystemFontPath(const std::string& fontName); + + private: + static std::map ReadSystemFonts(); + }; +} diff --git a/openVulkanoCpp/Host/SystemInfo.hpp b/openVulkanoCpp/Host/SystemInfo.hpp index f3a0b3e..7ab324c 100644 --- a/openVulkanoCpp/Host/SystemInfo.hpp +++ b/openVulkanoCpp/Host/SystemInfo.hpp @@ -69,7 +69,6 @@ namespace OpenVulkano static DeviceOrientation GetDeviceOrientation(); static void EnableDeviceOrientationEvents(); static InterfaceOrientation GetInterfaceOrientation(); - static std::string GetSystemFontPath(const std::string& fontName); static Event<> OnLowPowerModeChanged; static Event<> OnBatteryStateChanged; diff --git a/openVulkanoCpp/Host/Windows/SystemFontResolver.cpp b/openVulkanoCpp/Host/Windows/SystemFontResolver.cpp new file mode 100644 index 0000000..2d59940 --- /dev/null +++ b/openVulkanoCpp/Host/Windows/SystemFontResolver.cpp @@ -0,0 +1,66 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "Host/SystemFontResolver.hpp" +#include +#include + +#define QFR_DESCRIPTION 1 + +namespace OpenVulkano +{ + std::string SystemFontResolver::GetSystemFontPath(const std::string& fontName) + { + // font name -> filename + static std::map fontFileMapping = ReadSystemFonts(); + // maybe check everything in lower case ? so that we can use Arial/arial as input parameter + auto it = fontFileMapping.find(fontName); + return it == fontFileMapping.end() ? "" : it->second; + } + + std::map SystemFontResolver::ReadSystemFonts() + { + std::map fontFileMapping; + // thank you Microsoft for function that is not even documented, but exists and it's the only function that + // can return real font name from font filename (e.g. font filename is times.ttf that corresponds to Times New Roman) + int(WINAPI* GetFontResourceInfoW)(wchar_t*, unsigned long*, void*, unsigned long); + *(FARPROC*) &GetFontResourceInfoW = GetProcAddress(GetModuleHandleA("gdi32"), "GetFontResourceInfoW"); + + std::wstring winDir; + winDir.resize(MAX_PATH); + UINT len = GetWindowsDirectoryW(winDir.data(), MAX_PATH); + winDir.resize(len); + std::filesystem::path fontsDir = std::filesystem::path(winDir) / "Fonts"; + for (const auto& fontFilename : std::filesystem::directory_iterator(fontsDir)) + { + unsigned long size = 0; + std::wstring ws = fontFilename.path().wstring(); + if (!GetFontResourceInfoW(ws.data(), &size, NULL, QFR_DESCRIPTION)) + { + continue; + } + std::wstring fontName; + fontName.resize(size); + if (GetFontResourceInfoW(ws.data(), &size, fontName.data(), QFR_DESCRIPTION)) + { + // remove null-terminated characters since size is always bigger than needed + std::string fontNameCropped(fontName.begin(), fontName.end()); + size_t realSize = 0; + for (; realSize < fontNameCropped.size(); realSize++) + { + if (fontNameCropped[realSize] == '\0') + { + break; + } + } + fontNameCropped.resize(realSize); + fontFileMapping[std::move(fontNameCropped)] = fontFilename.path().string(); + } + } + return fontFileMapping; + } + +} diff --git a/openVulkanoCpp/Host/Windows/SystemInfo.cpp b/openVulkanoCpp/Host/Windows/SystemInfo.cpp index 3366009..bea3c6b 100644 --- a/openVulkanoCpp/Host/Windows/SystemInfo.cpp +++ b/openVulkanoCpp/Host/Windows/SystemInfo.cpp @@ -19,9 +19,6 @@ #include #include #include -#include -#include -#include // NOTE(vb): Windows defines macros like GetUserName that are used to automatically select the appropriate function version (GetUserNameA for ANSI and GetUserNameW for Unicode) // based on whether the _UNICODE macro is defined, so we manually undefine these macros to avoid naming collisions. @@ -31,8 +28,6 @@ #pragma comment(lib, "PowrProf.lib") #pragma comment(lib, "wbemuuid.lib") -#define QFR_DESCRIPTION 1 - namespace OpenVulkano { namespace @@ -590,56 +585,4 @@ namespace OpenVulkano return InterfaceOrientation::Landscape; } } - - std::string SystemInfo::GetSystemFontPath(const std::string& fontName) - { - // font name -> filename - static std::map fontFileMapping; - if (fontFileMapping.empty()) - { - // thank you Microsoft for function that is not even documented, but exists and it's the only function that - // can return real font name from font filename (e.g. font filename is times.ttf that corresponds to Times New Roman) - int(WINAPI * GetFontResourceInfoW)(wchar_t*, unsigned long*, void*, unsigned long); - *(FARPROC*) &GetFontResourceInfoW = GetProcAddress(GetModuleHandleA("gdi32"), "GetFontResourceInfoW"); - - std::wstring winDir; - winDir.resize(MAX_PATH); - UINT len = GetWindowsDirectoryW(winDir.data(), MAX_PATH); - winDir.resize(len); - std::filesystem::path fontsDir = std::filesystem::path(winDir) / "Fonts"; - for (const auto& fontFilename : std::filesystem::directory_iterator(fontsDir)) - { - unsigned long size = 0; - std::wstring ws = fontFilename.path().wstring(); - if (!GetFontResourceInfoW(ws.data(), &size, NULL, QFR_DESCRIPTION)) - { - continue; - } - std::wstring fontName; - fontName.resize(size); - if (GetFontResourceInfoW(ws.data(), &size, fontName.data(), QFR_DESCRIPTION)) - { - // remove null-terminated characters since size is always bigger than needed - std::string fontNameCropped(fontName.begin(), fontName.end()); - size_t realSize = 0; - for (; realSize < fontNameCropped.size(); realSize++) - { - if (fontNameCropped[realSize] == '\0') - { - break; - } - } - fontNameCropped.resize(realSize); - fontFileMapping[std::move(fontNameCropped)] = fontFilename.path().string(); - } - } - } - - // maybe check everything in lower case ? so that we can use Arial/arial as input parameter - if (fontFileMapping.contains(fontName)) - { - return fontFileMapping.at(fontName); - } - return ""; - } } \ No newline at end of file diff --git a/openVulkanoCpp/Scene/BitmapFontAtlasGenerator.cpp b/openVulkanoCpp/Scene/BitmapFontAtlasGenerator.cpp index d94d884..634600b 100644 --- a/openVulkanoCpp/Scene/BitmapFontAtlasGenerator.cpp +++ b/openVulkanoCpp/Scene/BitmapFontAtlasGenerator.cpp @@ -157,9 +157,8 @@ namespace OpenVulkano::Scene + glyph.firstGlyphByteInAtlas; for (int row = 0; row < slot->bitmap.rows; row++) { - std::memcpy(baseAddress + row * m_atlasData->GetTexture()->resolution.x, - &slot->bitmap.buffer[(slot->bitmap.rows - 1 - row) * slot->bitmap.pitch], - slot->bitmap.width); + std::memcpy(baseAddress - row * m_atlasData->GetTexture()->resolution.x, + &slot->bitmap.buffer[row * slot->bitmap.pitch], slot->bitmap.width); } } else diff --git a/openVulkanoCpp/Scene/Text/FontAtlasFactory.cpp b/openVulkanoCpp/Scene/Text/FontAtlasFactory.cpp index fd0427d..a6f4ba2 100644 --- a/openVulkanoCpp/Scene/Text/FontAtlasFactory.cpp +++ b/openVulkanoCpp/Scene/Text/FontAtlasFactory.cpp @@ -7,73 +7,100 @@ #include "FontAtlasFactory.hpp" #include "Scene/SdfFontAtlasGenerator.hpp" #include "Scene/BitmapFontAtlasGenerator.hpp" -#include "Host/SystemInfo.hpp" +#include "Host/SystemFontResolver.hpp" +#include "Base/Logger.hpp" +#include "Host/ResourceLoader.hpp" +#include namespace OpenVulkano::Scene { - FontAtlas::Ptr FontAtlasFactory::GetFontAtlasScalable(const std::string& fontIdentifier, - const std::set& charset, bool msdf) const + FontAtlasFactory::FontIdentifier::FontIdentifier(const std::string& font_, const std::set& charset, + SubpixelLayout subpixelLayout_, float ptSize_, + FontAtlasType atlasType_) + : font(font_), subpixelLayout(subpixelLayout_), ptSize(ptSize_), atlasType(atlasType_) { - const std::string fileName = FindFont(fontIdentifier); - if (fileName.empty()) + std::for_each(charset.begin(), charset.end(), [&](uint32_t c) { charsetHash ^= c; }); + } + + bool FontAtlasFactory::FontIdentifier::FontIdentifier::operator<(const FontIdentifier& other) const + { + return std::tie(atlasType, charsetHash, ptSize, subpixelLayout, font) + < std::tie(other.atlasType, other.charsetHash, other.ptSize, other.subpixelLayout, other.font); + } + + FontAtlas::Ptr FontAtlasFactory::GetFontAtlasScalable(const std::string& fontIdentifier, bool msdf, + const std::set& charset) const + { + const auto& fontData = FindFont(fontIdentifier); + if (fontData.Empty()) { + Logger::DATA->warn("Could not find font {}", fontIdentifier); return nullptr; } - const std::set& setRef = (charset.empty() ? FontAtlasGeneratorBase::LoadAllGlyphs(fontIdentifier) : charset); - FontIdentifier id(fontIdentifier, setRef, SubpixelLayout::UNKNOWN, 0, msdf); - if (m_atlasesCache.contains(id)) + const std::set& setRef = (charset.empty() ? FontAtlasGeneratorBase::LoadAllGlyphs(fontData) : charset); + FontIdentifier id(fontIdentifier, setRef, SubpixelLayout::UNKNOWN, 0, + msdf ? FontAtlasType::MSDF : FontAtlasType::SDF); + + auto it = m_atlasesCache.find(id); + if (it != m_atlasesCache.end()) { - return m_atlasesCache.at(id); + return it->second; } if (msdf) { MsdfFontAtlasGenerator msdfGen; - msdfGen.GenerateAtlas(fileName, setRef); - m_atlasesCache[id] = msdfGen.GetAtlas(); - return m_atlasesCache.at(id); + msdfGen.GenerateAtlas(fontData, setRef); + return m_atlasesCache.insert({ id, msdfGen.GetAtlas() }).first->second; } SdfFontAtlasGenerator sdfGen; - sdfGen.GenerateAtlas(fileName, setRef); - m_atlasesCache[id] = sdfGen.GetAtlas(); - return m_atlasesCache.at(id); + sdfGen.GenerateAtlas(fontData, setRef); + return m_atlasesCache.insert({ id, sdfGen.GetAtlas() }).first->second; } FontAtlas::Ptr FontAtlasFactory::GetFontAtlas(const std::string& fontIdentifier, float ptSize, - const std::set& charset, - std::optional subpixelLayout) const + std::optional subpixelLayout, + const std::set& charset) const { - const std::string fileName = FindFont(fontIdentifier); - if (fileName.empty()) + const auto& fontData = FindFont(fontIdentifier); + if (fontData.Empty()) { + Logger::DATA->warn("Could not find font {}", fontIdentifier); return nullptr; } - const std::set& setRef = (charset.empty() ? FontAtlasGeneratorBase::LoadAllGlyphs(fontIdentifier) : charset); - FontIdentifier id(fontIdentifier, setRef, subpixelLayout.value_or(SubpixelLayout::UNKNOWN), ptSize, false); - if (m_atlasesCache.contains(id)) + const std::set& setRef = (charset.empty() ? FontAtlasGeneratorBase::LoadAllGlyphs(fontData) : charset); + FontIdentifier id(fontIdentifier, setRef, subpixelLayout.value_or(SubpixelLayout::UNKNOWN), ptSize, + subpixelLayout ? FontAtlasType::BITMAP_SUBPIXEL : FontAtlasType::BITMAP); + + auto it = m_atlasesCache.find(id); + if (it != m_atlasesCache.end()) { - return m_atlasesCache.at(id); + return it->second; } FontPixelSizeConfig cfg(ptSize); BitmapFontAtlasGenerator bitmapGen(cfg, subpixelLayout); - bitmapGen.GenerateAtlas(fileName, setRef); - m_atlasesCache[id] = bitmapGen.GetAtlas(); - return m_atlasesCache.at(id); + bitmapGen.GenerateAtlas(fontData, setRef); + return m_atlasesCache.insert({ id, bitmapGen.GetAtlas() }).first->second; } - std::string FontAtlasFactory::FindFont(const std::string& fontIdentifier) const + Array FontAtlasFactory::FindFont(const std::string& fontIdentifier) const { - if (!std::filesystem::exists(fontIdentifier)) + Array resource = ResourceLoader::GetInstance().GetResource(fontIdentifier); + if (resource.Empty()) { - if (!m_allowSystemFonts) + if (!std::filesystem::exists(fontIdentifier)) { - return ""; + if (!m_allowSystemFonts) + { + return {}; + } + return Utils::ReadFile(SystemFontResolver::GetSystemFontPath(fontIdentifier)); } - return SystemInfo::GetSystemFontPath(fontIdentifier); + return Utils::ReadFile(fontIdentifier); } - return fontIdentifier; + return resource; } } diff --git a/openVulkanoCpp/Scene/Text/FontAtlasFactory.hpp b/openVulkanoCpp/Scene/Text/FontAtlasFactory.hpp index 31ed708..22a8e53 100644 --- a/openVulkanoCpp/Scene/Text/FontAtlasFactory.hpp +++ b/openVulkanoCpp/Scene/Text/FontAtlasFactory.hpp @@ -8,6 +8,7 @@ #include "FontAtlas.hpp" #include "Scene/SubpixelLayout.hpp" +#include "Data/Containers/Array.hpp" namespace OpenVulkano::Scene { @@ -16,54 +17,28 @@ namespace OpenVulkano::Scene struct FontIdentifier { FontIdentifier(const std::string& font_, const std::set& charset, SubpixelLayout subpixelLayout_, - float ptSize_, bool msdf_) - : font(font_), subpixelLayout(subpixelLayout_), ptSize(ptSize_), msdf(msdf_) - { - std::for_each(charset.begin(), charset.end(), [&](uint32_t c) { charsetHash ^= c; }); - } - + float ptSize_, FontAtlasType atlasType_); + bool operator<(const FontIdentifier& other) const; + std::string font; - size_t charsetHash = 0; + uint32_t charsetHash = 0; SubpixelLayout subpixelLayout = SubpixelLayout::UNKNOWN; float ptSize = 0; - bool msdf = true; - - bool operator<(const FontIdentifier& other) const - { - if (font != other.font) - { - return font < other.font; - } - if (charsetHash != other.charsetHash) - { - return charsetHash < other.charsetHash; - } - if (subpixelLayout != other.subpixelLayout) - { - return subpixelLayout < other.subpixelLayout; - } - if (ptSize != other.ptSize) - { - return ptSize < other.ptSize; - } - return msdf < other.msdf; - } - + FontAtlasType atlasType; }; public: FontAtlasFactory(bool allowSystemFonts = true) : m_allowSystemFonts(allowSystemFonts) {} - [[nodiscard]] FontAtlas::Ptr GetFontAtlasScalable(const std::string& fontIdentifier, - const std::set& charset = {}, - bool msdf = true) const; + [[nodiscard]] FontAtlas::Ptr GetFontAtlasScalable(const std::string& fontIdentifier, bool msdf = true, + const std::set& charset = {}) const; [[nodiscard]] FontAtlas::Ptr GetFontAtlas(const std::string& fontIdentifier, float ptSize, - const std::set& charset = {}, - std::optional subpixelLayout = std::nullopt) const; + std::optional subpixelLayout = std::nullopt, + const std::set& charset = {}) const; private: - std::string FindFont(const std::string& fontFile) const; + Array FindFont(const std::string& fontFile) const; private: - bool m_allowSystemFonts; + const bool m_allowSystemFonts; mutable std::map m_atlasesCache; }; } diff --git a/tests/Host/Windows/SystemFontsSearchTests.cpp b/tests/Host/Windows/SystemFontsSearchTests.cpp index c299d48..e478e57 100644 --- a/tests/Host/Windows/SystemFontsSearchTests.cpp +++ b/tests/Host/Windows/SystemFontsSearchTests.cpp @@ -6,7 +6,7 @@ #include #include "Base/Logger.hpp" -#include "Host/SystemInfo.hpp" +#include "Host/SystemFontResolver.hpp" #include #include "Scene/Text/FontAtlasFactory.hpp" @@ -18,16 +18,16 @@ TEST_CASE("Search system fonts") Logger::SetupLogger("", "tests.log"); // assume these fonts are present since they are default - std::filesystem::path path = SystemInfo::GetSystemFontPath("Arial"); + std::filesystem::path path = SystemFontResolver::GetSystemFontPath("Arial"); REQUIRE(path.filename() == "arial.ttf"); // assume these fonts are present since they are default - path = SystemInfo::GetSystemFontPath("Times New Roman"); + path = SystemFontResolver::GetSystemFontPath("Times New Roman"); REQUIRE(path.filename() == "times.ttf"); - path = SystemInfo::GetSystemFontPath("Arial Bold Italic"); + path = SystemFontResolver::GetSystemFontPath("Arial Bold Italic"); REQUIRE(path.filename() == "arialbi.ttf"); - path = SystemInfo::GetSystemFontPath("NON-EXISTING Font"); + path = SystemFontResolver::GetSystemFontPath("NON-EXISTING Font"); REQUIRE(path.empty()); }