Index: source/graphics/Font.h =================================================================== --- source/graphics/Font.h +++ source/graphics/Font.h @@ -19,12 +19,17 @@ #define INCLUDED_FONT #include "graphics/Texture.h" +#include "ps/CStrIntern.h" + +#include +#include /** * Storage for a bitmap font. Loaded by CFontManager. */ class CFont { + struct PrivateTag{}; public: struct GlyphData { @@ -61,6 +66,11 @@ GlyphData* m_Data[256]; }; + CFont(PrivateTag){} + + static std::shared_ptr Create(std::istream& fontStream, const CStrIntern fontName, + CTexturePtr(*loadTexture)(bool, CStrIntern)); + bool HasRGB() const { return m_HasRGB; } int GetLineSpacing() const { return m_LineSpacing; } int GetHeight() const { return m_Height; } @@ -77,10 +87,6 @@ CTexturePtr GetTexture() const { return m_Texture; } private: - friend class CFontManager; - - CFont() = default; - CTexturePtr m_Texture; bool m_HasRGB; // true if RGBA, false if ALPHA Index: source/graphics/Font.cpp =================================================================== --- source/graphics/Font.cpp +++ source/graphics/Font.cpp @@ -23,9 +23,13 @@ #include "ps/CLogger.h" #include "renderer/Renderer.h" +#include #include +#include #include +constexpr unsigned int REQUIRED_VERSION = 101; + CFont::GlyphMap::GlyphMap() { memset(m_Data, 0, sizeof(m_Data)); @@ -45,6 +49,90 @@ m_Data[i >> 8][i & 0xff].defined = 1; } +std::shared_ptr CFont::Create(std::istream& fontStream, const CStrIntern fontName, + CTexturePtr(*loadTexture)(bool, CStrIntern)) +{ + auto ret = std::make_shared(PrivateTag()); + + unsigned int version; + fontStream >> version; + // Make sure this is from a recent version of the font builder. + if (version != REQUIRED_VERSION) + { + LOGERROR("Font %s has invalid version: %u should be %u", fontName.c_str(), version, + REQUIRED_VERSION); + return {}; + } + + float textureWidth, textureHeight; + fontStream >> textureWidth >> textureHeight; + + std::string format; + fontStream >> format; + if (format == "rgba") + ret->m_HasRGB = true; + else if (format == "a") + ret->m_HasRGB = false; + else + { + LOGWARNING("Invalid .fnt format string"); + return {}; + } + + u16 numberOfGlyphs; + fontStream >> numberOfGlyphs; + + fontStream >> ret->m_LineSpacing; + fontStream >> ret->m_Height; + + ret->m_BoundsX0 = std::numeric_limits::max(); + ret->m_BoundsY0 = std::numeric_limits::max(); + ret->m_BoundsX1 = -std::numeric_limits::max(); + ret->m_BoundsY1 = -std::numeric_limits::max(); + + for (u16 i = 0; i < numberOfGlyphs; ++i) + { + // Bigger than u16 to detect overflow errors. + i32 codepoint; + float textureX, textureY; + i16 width, height, offsetX, offsetY, advance; + fontStream >> codepoint + >> textureX >> textureY >> width >> height + >> offsetX >> offsetY >> advance; + + if (codepoint < 0 || codepoint > 0xFFFF) + { + LOGWARNING("Font %s has invalid codepoint 0x%x", fontName.c_str(), codepoint); + continue; + } + + const float u = textureX / textureWidth; + const float v = textureY / textureHeight; + const float w = static_cast(width) / textureWidth; + const float h = static_cast(height) / textureHeight; + + CFont::GlyphData g = + { + u, -v, u + w, -v + h, + offsetX, static_cast(-offsetY), static_cast(offsetX + width), + static_cast(-offsetY + height), advance + }; + ret->m_Glyphs.set(static_cast(codepoint), g); + + ret->m_BoundsX0 = std::min(ret->m_BoundsX0, static_cast(g.x0)); + ret->m_BoundsY0 = std::min(ret->m_BoundsY0, static_cast(g.y0)); + ret->m_BoundsX1 = std::max(ret->m_BoundsX1, static_cast(g.x1)); + ret->m_BoundsY1 = std::max(ret->m_BoundsY1, static_cast(g.y1)); + } + + // Ensure the height has been found (which should always happen if the font includes an 'I'). + if(!ret->m_Height) + return {}; + ret->m_Texture = loadTexture(ret->m_HasRGB, fontName); + + return ret; +} + int CFont::GetCharacterWidth(wchar_t c) const { const GlyphData* g = m_Glyphs.get(c); Index: source/graphics/FontManager.h =================================================================== --- source/graphics/FontManager.h +++ source/graphics/FontManager.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -20,6 +20,7 @@ #include "ps/CStrIntern.h" +#include #include class CFont; @@ -30,12 +31,10 @@ class CFontManager { public: - std::shared_ptr LoadFont(CStrIntern fontName); + std::shared_ptr LoadFont(CStrIntern fontName); private: - bool ReadFont(CFont* font, CStrIntern fontName); - - using FontsMap = std::unordered_map>; + using FontsMap = std::unordered_map>; FontsMap m_Fonts; }; Index: source/graphics/FontManager.cpp =================================================================== --- source/graphics/FontManager.cpp +++ source/graphics/FontManager.cpp @@ -27,122 +27,49 @@ #include "ps/Filesystem.h" #include "renderer/Renderer.h" -#include +const VfsPath PATH = L"fonts/"; -std::shared_ptr CFontManager::LoadFont(CStrIntern fontName) +static CTexturePtr LoadTexture(bool hasRGB, const CStrIntern name) { - FontsMap::iterator it = m_Fonts.find(fontName); - if (it != m_Fonts.end()) - return it->second; - - std::shared_ptr font(new CFont()); - - if (!ReadFont(font.get(), fontName)) - { - // Fall back to default font (unless this is the default font) - if (fontName == str_sans_10) - font.reset(); - else - font = LoadFont(str_sans_10); - } - - m_Fonts[fontName] = font; - return font; -} + const VfsPath imageName(name.string() + ".png"); + CTextureProperties textureProps(PATH / imageName, + hasRGB ? Renderer::Backend::Format::R8G8B8A8_UNORM : + Renderer::Backend::Format::A8_UNORM); + textureProps.SetIgnoreQuality(true); + return g_Renderer.GetTextureManager().CreateTexture(textureProps); +}; -bool CFontManager::ReadFont(CFont* font, CStrIntern fontName) +static std::shared_ptr ReadFont(CStrIntern fontName) { - const VfsPath path(L"fonts/"); - // Read font definition file into a stringstream std::shared_ptr buffer; size_t size; const VfsPath fntName(fontName.string() + ".fnt"); - if (g_VFS->LoadFile(path / fntName, buffer, size) < 0) + if (g_VFS->LoadFile(PATH / fntName, buffer, size) < 0) { - LOGERROR("Failed to open font file %s", (path / fntName).string8()); - return false; + LOGERROR("Failed to open font file %s", (PATH / fntName).string8()); + return {}; } std::istringstream fontStream( std::string(reinterpret_cast(buffer.get()), size)); - int version; - fontStream >> version; - // Make sure this is from a recent version of the font builder. - if (version != 101) - { - LOGERROR("Font %s has invalid version", fontName.c_str()); - return false; - } - - int textureWidth, textureHeight; - fontStream >> textureWidth >> textureHeight; - - std::string format; - fontStream >> format; - if (format == "rgba") - font->m_HasRGB = true; - else if (format == "a") - font->m_HasRGB = false; - else - { - LOGWARNING("Invalid .fnt format string"); - return false; - } - - int mumberOfGlyphs; - fontStream >> mumberOfGlyphs; - - fontStream >> font->m_LineSpacing; - fontStream >> font->m_Height; + return CFont::Create(fontStream, fontName, &LoadTexture); +} - font->m_BoundsX0 = std::numeric_limits::max(); - font->m_BoundsY0 = std::numeric_limits::max(); - font->m_BoundsX1 = -std::numeric_limits::max(); - font->m_BoundsY1 = -std::numeric_limits::max(); +std::shared_ptr CFontManager::LoadFont(CStrIntern fontName) +{ + FontsMap::iterator it = m_Fonts.find(fontName); + if (it != m_Fonts.end()) + return it->second; - for (int i = 0; i < mumberOfGlyphs; ++i) + if (const auto font = ReadFont(fontName)) { - int codepoint, textureX, textureY, width, height, offsetX, offsetY, advance; - fontStream >> codepoint - >> textureX >> textureY >> width >> height - >> offsetX >> offsetY >> advance; - - if (codepoint < 0 || codepoint > 0xFFFF) - { - LOGWARNING("Font %s has invalid codepoint 0x%x", fontName.c_str(), codepoint); - continue; - } - - const float u = static_cast(textureX) / textureWidth; - const float v = static_cast(textureY) / textureHeight; - const float w = static_cast(width) / textureWidth; - const float h = static_cast(height) / textureHeight; - - CFont::GlyphData g = - { - u, -v, u + w, -v + h, - static_cast(offsetX), static_cast(-offsetY), - static_cast(offsetX + width), static_cast(-offsetY + height), - static_cast(advance) - }; - font->m_Glyphs.set(static_cast(codepoint), g); - - font->m_BoundsX0 = std::min(font->m_BoundsX0, static_cast(g.x0)); - font->m_BoundsY0 = std::min(font->m_BoundsY0, static_cast(g.y0)); - font->m_BoundsX1 = std::max(font->m_BoundsX1, static_cast(g.x1)); - font->m_BoundsY1 = std::max(font->m_BoundsY1, static_cast(g.y1)); + m_Fonts[fontName] = font; + return font; } - // Ensure the height has been found (which should always happen if the font includes an 'I'). - ENSURE(font->m_Height); - - // Load glyph texture - const VfsPath imageName(fontName.string() + ".png"); - CTextureProperties textureProps(path / imageName, - font->m_HasRGB ? Renderer::Backend::Format::R8G8B8A8_UNORM : Renderer::Backend::Format::A8_UNORM); - textureProps.SetIgnoreQuality(true); - font->m_Texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); - - return true; + // Fall back to default font (unless this is the default font) + const auto fallbackFont = (fontName == str_sans_10) ? nullptr : LoadFont(str_sans_10); + m_Fonts[fontName] = fallbackFont; + return fallbackFont; } Index: source/graphics/FontMetrics.h =================================================================== --- source/graphics/FontMetrics.h +++ source/graphics/FontMetrics.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -18,6 +18,8 @@ #ifndef INCLUDED_FONTMETRICS #define INCLUDED_FONTMETRICS +#include + class CFont; class CStrIntern; @@ -37,7 +39,7 @@ void CalculateStringSize(const wchar_t* string, int& w, int& h) const; private: - std::shared_ptr m_Font; + std::shared_ptr m_Font; }; #endif // INCLUDED_FONTMETRICS Index: source/graphics/TextRenderer.h =================================================================== --- source/graphics/TextRenderer.h +++ source/graphics/TextRenderer.h @@ -26,6 +26,8 @@ #include "renderer/backend/IDeviceCommandContext.h" #include +#include +#include class CFont; class CMatrix3D; @@ -154,7 +156,7 @@ size_t chars; // sum of runs[i].text->size() CVector2D translate; CColor color; - std::shared_ptr font; + std::shared_ptr font; std::list runs; }; @@ -165,7 +167,7 @@ CColor m_Color; CStrIntern m_FontName; - std::shared_ptr m_Font; + std::shared_ptr m_Font; bool m_Dirty = true;