123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- // Description : AtomFont class.
- #if !defined(USE_NULLFONT_ALWAYS)
- #include <AtomLyIntegration/AtomFont/AtomFont.h>
- #include <AtomLyIntegration/AtomFont/FFont.h>
- #include <AtomLyIntegration/AtomFont/FontTexture.h>
- #include <AtomLyIntegration/AtomFont/FontRenderer.h>
- #include <CryCommon/CryPath.h>
- #include <CryCommon/ILocalizationManager.h>
- #include <AzCore/std/string/conversions.h>
- #include <AzCore/std/string/string_view.h>
- #include <AzCore/std/parallel/lock.h>
- #include <AzCore/Interface/Interface.h>
- #include <AzFramework/Archive/IArchive.h>
- #include <Atom/RPI.Public/RPIUtils.h>
- #include <Atom/RPI.Public/DynamicDraw/DynamicDrawInterface.h>
- #include <Atom/RPI.Reflect/Asset/AssetUtils.h>
- // Static member definitions
- const AZ::AtomFont::GlyphSize AZ::AtomFont::defaultGlyphSize = AZ::AtomFont::GlyphSize(ICryFont::defaultGlyphSizeX, ICryFont::defaultGlyphSizeY);
- #if !defined(_RELEASE)
- static void DumfontTexture(IConsoleCmdArgs* cmdArgs)
- {
- if (cmdArgs->GetArgCount() != 2)
- {
- return;
- }
- const char* fontName = cmdArgs->GetArg(1);
- if (fontName && *fontName && *fontName != '0')
- {
- AZStd::string fontFilePath("@engroot@/");
- fontFilePath += fontName;
- fontFilePath += ".bmp";
- AZ::FFont* font = (AZ::FFont*) gEnv->pCryFont->GetFont(fontName);
- if (font)
- {
- font->GetFontTexture()->WriteToFile(fontFilePath.c_str());
- gEnv->pLog->LogWithType(IMiniLog::eInputResponse, "Dumped \"%s\" texture to \"%s\"!", fontName, fontFilePath.c_str());
- }
- }
- }
- static void DumfontNames([[maybe_unused]] IConsoleCmdArgs* cmdArgs)
- {
- AZStd::string names = gEnv->pCryFont->GetLoadedFontNames();
- gEnv->pLog->LogWithType(IMiniLog::eInputResponse, "Currently loaded fonts: %s", names.c_str());
- }
- static void ReloadFonts([[maybe_unused]] IConsoleCmdArgs* cmdArgs)
- {
- gEnv->pCryFont->ReloadAllFonts();
- }
- #endif
- namespace
- {
- //! Stores paths to styled font assets for a given set of languages
- //! This struct stores the XML data contained within the <font> tag of
- //! an enclosing <fontfamily> definition:
- //!
- //! <fontfamily name="FontFamilyName">
- //! <font lang="Language1, Language2">
- //! <file path="regular.font" />
- //! <file path="bold.font" tags="b" />
- //! <file path="italic.font" tags="i" />
- //! <file path="bolditalic.font" tags="b,i" />
- //! </font>
- //! </fontfamily>
- struct FontTagXml
- {
- //! \return True if all font asset paths are non-empty, false otherwise
- bool IsValid() const
- {
- // Note that "lang" can be empty
- return !m_fontFilename.empty()
- && !m_boldFontFilename.empty()
- && !m_italicFontFilename.empty()
- && !m_boldItalicFontFilename.empty();
- }
- AZStd::string m_lang; //!< Stores a comma-separated list of languages this collection of fonts applies to.
- //!< If this is an empty string, it implies that these set of fonts will be applied
- //!< by default (when a language is being used but no fonts in the font family are
- //!< mapped to that language).
- AZStd::string m_fontFilename; //!< Font used when no styling is applied.
- AZStd::string m_boldFontFilename; //!< Bold-styled font
- AZStd::string m_italicFontFilename; //!< Italic-styled font
- AZStd::string m_boldItalicFontFilename; //!< Bold-italic-styled font
- };
- //! Stores parsed font family XML data.
- //! This struct contains the name of the font family and a list of font
- //! file XML data for all the language-specific mappings of this
- //! font family.
- //!
- //! Example XML:
- //!
- //! <fontfamily name="FontFamilyName">
- //! <font>
- //! <file path="regular.font" />
- //! <file path="bold.font" tags="b" />
- //! <file path="italic.font" tags="i" />
- //! <file path="bolditalic.font" tags="b,i" />
- //! </font>
- //! <font lang="korean">
- //! <file path="../korean/korean-regular.font" />
- //! <file path="../korean/korean-italic.font" tags="b" />
- //! <file path="../korean/korean-bold.font" tags="i" />
- //! <file path="../korean/korean-bolditalic.font" tags="b,i" />
- //! </font>
- //! <font lang="chinesesimplified">
- //! <file path="../chinesesimplified/chinesesimplified-regular.font" />
- //! <file path="../chinesesimplified/chinesesimplified-bold.font" tags="b" />
- //! <file path="../chinesesimplified/chinesesimplified-italic.font" tags="i" />
- //! <file path="../chinesesimplified/chinesesimplified-bolditalic.font" tags="b,i" />
- //! </font>
- //! </fontfamily>
- struct FontFamilyTagXml
- {
- //! Returns true if all font file fields were parsed, false otherwise.
- bool IsValid() const
- {
- for (const FontTagXml& fontTagXml : m_fontTagsXml)
- {
- if (!fontTagXml.IsValid())
- {
- return false;
- }
- }
- // Every font family must have a name
- return !m_fontFamilyName.empty();
- }
- AZStd::string m_fontFamilyName; //!< Value of the "name" font-family tag attribute
- AZStd::list<FontTagXml> m_fontTagsXml; //!< List of child <font> tag data.
- };
- //! Returns true if the XML tree was traversed successfully, false otherwise.
- //!
- //! Note that, if this function returns true, it simply means that there were
- //! no unexpected structure issues with the given XML tree, it doesn't
- //! necessarily mean that all the required fields were parsed.
- bool ParseFontFamilyXml(const XmlNodeRef& node, FontFamilyTagXml& xmlData)
- {
- if (!node)
- {
- return false;
- }
- // <fontfamily>
- if (AZStd::string(node->getTag()) == "fontfamily")
- {
- const int numAttributes = node->getNumAttributes();
- if (numAttributes <= 0)
- {
- // Expecting at least one attribute
- return false;
- }
- AZStd::string name;
- for (int i = 0, count = node->getNumAttributes(); i < count; ++i)
- {
- const char* key = "";
- const char* value = "";
- if (node->getAttributeByIndex(i, &key, &value))
- {
- if (AZStd::string(key) == "name")
- {
- name = value;
- }
- else
- {
- // Unexpected font tag attribute
- return false;
- }
- }
- }
- AZ::StringFunc::TrimWhiteSpace(name, true, true);
- if (!name.empty())
- {
- xmlData.m_fontFamilyName = name;
- }
- else
- {
- // Font family must have a name
- return false;
- }
- }
- // <font>
- if (AZStd::string(node->getTag()) == "font")
- {
- xmlData.m_fontTagsXml.push_back(FontTagXml());
- AZStd::string lang;
- for (int i = 0, count = node->getNumAttributes(); i < count; ++i)
- {
- const char* key = "";
- const char* value = "";
- if (node->getAttributeByIndex(i, &key, &value))
- {
- if (AZStd::string(key) == "lang")
- {
- lang = value;
- }
- else
- {
- // Unexpected font tag attribute
- return false;
- }
- }
- }
- AZ::StringFunc::TrimWhiteSpace(lang, true, true);
- if (!lang.empty())
- {
- xmlData.m_fontTagsXml.back().m_lang = lang;
- }
- }
- // <file>
- else if (AZStd::string(node->getTag()) == "file")
- {
- const int numAttributes = node->getNumAttributes();
- if (numAttributes <= 0)
- {
- // Expecting at least one attribute
- return false;
- }
- AZStd::string path;
- AZStd::string tags;
- for (int i = 0, count = node->getNumAttributes(); i < count; ++i)
- {
- const char* key = "";
- const char* value = "";
- if (node->getAttributeByIndex(i, &key, &value))
- {
- if (AZStd::string(key) == "path")
- {
- path = value;
- }
- else if (AZStd::string(key) == "tags")
- {
- tags = value;
- }
- else
- {
- // Unexpected font tag attribute
- return false;
- }
- }
- }
- AZ::StringFunc::TrimWhiteSpace(tags, true, true);
- if (tags.empty())
- {
- xmlData.m_fontTagsXml.back().m_fontFilename = path;
- }
- else if (tags == "b")
- {
- xmlData.m_fontTagsXml.back().m_boldFontFilename = path;
- }
- else if (tags == "i")
- {
- xmlData.m_fontTagsXml.back().m_italicFontFilename = path;
- }
- else
- {
- // We'll just assume any other tag indicates bold italic
- xmlData.m_fontTagsXml.back().m_boldItalicFontFilename = path;
- }
- }
- for (int i = 0, count = node->getChildCount(); i < count; ++i)
- {
- XmlNodeRef child = node->getChild(i);
- if (!ParseFontFamilyXml(child, xmlData))
- {
- return false;
- }
- }
- return true;
- }
- //! Only attempt XML file load if file exists.
- //! There are use-cases where the XML path is not fully known (such as
- //! when referencing font family names from font family XML files), and
- //! attempting to load the XML files directly via ISystem() methods can
- //! produce a lot of warning noise.
- XmlNodeRef SafeLoadXmlFromFile(const AZStd::string& xmlPath)
- {
- if (gEnv->pCryPak->IsFileExist(xmlPath.c_str()))
- {
- return GetISystem()->LoadXmlFromFile(xmlPath.c_str());
- }
- return XmlNodeRef();
- }
- }
- AZ::AtomFont::AtomFont([[maybe_unused]] ISystem* system)
- {
- CryLogAlways("Using FreeType %d.%d.%d", FREETYPE_MAJOR, FREETYPE_MINOR, FREETYPE_PATCH);
- // Persist fonts for application lifetime to prevent unnecessary work
- REGISTER_CVAR(r_persistFontFamilies, r_persistFontFamilies, VF_NULL, "Persist loaded font families for lifetime of application.");
- #if !defined(_RELEASE)
- REGISTER_COMMAND("r_DumfontTexture", DumfontTexture, 0,
- "Dumps the specified font's texture to a bitmap file\n"
- "Use r_DumfontTexture to get the loaded font names\n"
- "Usage: r_DumfontTexture <fontname>");
- REGISTER_COMMAND("r_DumfontNames", DumfontNames, 0,
- "Logs a list of fonts currently loaded");
- REGISTER_COMMAND("r_ReloadFonts", ReloadFonts, VF_NULL,
- "Reload all fonts");
- #endif
- AZ::Interface<AzFramework::FontQueryInterface>::Register(this);
- // Queue a load for the font per viewport dynamic draw context shader, and wait for it to load
- static const char* shaderFilepath = "Shaders/SimpleTextured.azshader";
- Data::Asset<RPI::ShaderAsset> shaderAsset = RPI::AssetUtils::GetAssetByProductPath<RPI::ShaderAsset>(shaderFilepath, RPI::AssetUtils::TraceLevel::Assert);
- shaderAsset.QueueLoad();
- Data::AssetBus::Handler::BusConnect(shaderAsset.GetId());
- }
- AZ::AtomFont::~AtomFont()
- {
- Data::AssetBus::Handler::BusDisconnect();
- AZ::Interface<AzFramework::FontQueryInterface>::Unregister(this);
- m_defaultFontDrawInterface = nullptr;
- // Persist fonts for application lifetime to prevent unnecessary work
- m_persistedFontFamilies.clear();
- for (FontMapItor it = m_fonts.begin(), itEnd = m_fonts.end(); it != itEnd; )
- {
- FFont* font = it->second;
- ++it; // iterate as Release() below will remove font from the map
- SAFE_RELEASE(font);
- }
- }
- void AZ::AtomFont::Release()
- {
- delete this;
- }
- IFFont* AZ::AtomFont::NewFont(const char* fontName)
- {
- AZStd::string name = fontName;
- AZStd::to_lower(name.begin(), name.end());
- AzFramework::FontId fontId = GetFontId(name.c_str());
- FontMapItor it = m_fonts.find(fontId);
- if (it != m_fonts.end())
- {
- return it->second;
- }
- FFont* font = new FFont(this, name.c_str());
- m_fonts.insert(FontMapItor::value_type(fontId, font));
- if(!m_defaultFontDrawInterface)
- {
- m_defaultFontDrawInterface = static_cast<AzFramework::FontDrawInterface*>(font);
- }
- return font;
- }
- IFFont* AZ::AtomFont::GetFont(const char* fontName) const
- {
- AZStd::string name = fontName;
- AZStd::to_lower(name.begin(), name.end());
- AzFramework::FontId fontId = GetFontId(name.c_str());
- FontMapConstItor it = m_fonts.find(fontId);
- return it != m_fonts.end() ? it->second : 0;
- }
- AzFramework::FontDrawInterface* AZ::AtomFont::GetFontDrawInterface(AzFramework::FontId fontId) const
- {
- FontMapConstItor it = m_fonts.find(fontId);
- return (it != m_fonts.end()) ? it->second : nullptr;
- }
- AzFramework::FontDrawInterface* AZ::AtomFont::GetDefaultFontDrawInterface() const
- {
- return m_defaultFontDrawInterface;
- }
- FontFamilyPtr AZ::AtomFont::LoadFontFamily(const char* fontFamilyName)
- {
- FontFamilyPtr fontFamily(nullptr);
- AZStd::string fontFamilyPath;
- AZStd::string fontFamilyFullPath;
-
- XmlNodeRef root = LoadFontFamilyXml(fontFamilyName, fontFamilyPath, fontFamilyFullPath);
- if (root)
- {
- FontFamilyTagXml xmlData;
- const bool parseSuccess = ParseFontFamilyXml(root, xmlData);
- if (parseSuccess && xmlData.IsValid())
- {
- const char* currentLanguage = gEnv->pSystem->GetLocalizationManager()->GetLanguage();
- FontTagXml* defaultFont = nullptr;
- FontTagXml* langSpecificFont = nullptr;
- // Note that we don't break out of this for-loop early because we
- // want to find both the default font family and the
- // language-specific font family. We prefer the lang-specific
- // family but will fall back on the default if it doesn't exist.
- for (FontTagXml& fontTagXml : xmlData.m_fontTagsXml)
- {
- if (fontTagXml.m_lang.empty())
- {
- defaultFont = &fontTagXml;
- }
- else
- {
- // "lang" font-tag attribute could be comma-separated
- AZStd::vector<AZStd::string> tokens;
- AZ::StringFunc::Tokenize(fontTagXml.m_lang, tokens, ',');
- for(AZStd::string& langToken : tokens)
- {
- AZ::StringFunc::TrimWhiteSpace(langToken, true, true);
- if (langToken == currentLanguage)
- {
- langSpecificFont = &fontTagXml;
- break;
- }
- }
- }
- }
- if (langSpecificFont || defaultFont)
- {
- // Prefer lang-specific font-family over default, if it exists
- FontTagXml* fontTagXml = langSpecificFont ? langSpecificFont : defaultFont;
- // Pre-pend font family's path to make font family XML paths
- // relative to font family file
- fontTagXml->m_fontFilename = fontFamilyPath + fontTagXml->m_fontFilename;
- fontTagXml->m_boldFontFilename = fontFamilyPath + fontTagXml->m_boldFontFilename;
- fontTagXml->m_italicFontFilename = fontFamilyPath + fontTagXml->m_italicFontFilename;
- fontTagXml->m_boldItalicFontFilename = fontFamilyPath + fontTagXml->m_boldItalicFontFilename;
- IFFont* normal = LoadFont(fontTagXml->m_fontFilename.c_str());
- IFFont* bold = LoadFont(fontTagXml->m_boldFontFilename.c_str());
- IFFont* italic = LoadFont(fontTagXml->m_italicFontFilename.c_str());
- IFFont* boldItalic = LoadFont(fontTagXml->m_boldItalicFontFilename.c_str());
- // Only continue if all fonts were created successfully
- if (normal && bold && italic && boldItalic)
- {
- fontFamily.reset(new FontFamily(),
- [this](FontFamily* fontFamily)
- {
- if (AZ::Interface<AzFramework::FontQueryInterface>::Get())
- {
- ReleaseFontFamily(fontFamily);
- }
- });
- // Map the font family name both by path and by name defined
- // within the Font Family XML itself. This allows font
- // families to also be referenced simply by name.
- if (!AddFontFamilyToMaps(fontFamilyFullPath.c_str(), xmlData.m_fontFamilyName.c_str(), fontFamily))
- {
- SAFE_RELEASE(normal);
- SAFE_RELEASE(bold);
- SAFE_RELEASE(italic);
- SAFE_RELEASE(boldItalic);
- return nullptr;
- }
- fontFamily->familyName = xmlData.m_fontFamilyName;
- fontFamily->normal = normal;
- fontFamily->bold = bold;
- fontFamily->italic = italic;
- fontFamily->boldItalic = boldItalic;
- }
- else
- {
- SAFE_RELEASE(normal);
- SAFE_RELEASE(bold);
- SAFE_RELEASE(italic);
- SAFE_RELEASE(boldItalic);
- }
- }
- }
- }
- if (!fontFamily)
- {
- // Unable to load font family XML, so load font normally and associate
- // it with a font family
- IFFont* font = LoadFont(fontFamilyName);
- if (font)
- {
- // Create a font family from a single font by assigning all the
- // font family stylings to the same font
- fontFamily.reset(new FontFamily(),
- [this](FontFamily* fontFamily)
- {
- if (AZ::Interface<AzFramework::FontQueryInterface>::Get())
- {
- ReleaseFontFamily(fontFamily);
- }
- });
- // Use filepath as familyName so font loading/unloading doesn't break with duplicate file names
- fontFamily->familyName = fontFamilyName;
- if (!AddFontFamilyToMaps(fontFamilyName, fontFamily->familyName.c_str(), fontFamily))
- {
- SAFE_RELEASE(font);
- return nullptr;
- }
- // Assign all stylings to the same font
- fontFamily->normal = font;
- fontFamily->bold = font;
- fontFamily->italic = font;
- fontFamily->boldItalic = font;
- // The other three stylings need to have their ref count
- // incremented (even though in this particular case its all the
- // same font) because when ReleaseFontFamily executes all fonts
- // in the family will be (corresondingly) Release'd.
- fontFamily->bold->AddRef();
- fontFamily->italic->AddRef();
- fontFamily->boldItalic->AddRef();
- }
- }
- // Persist fonts for application lifetime to prevent unnecessary work
- if (r_persistFontFamilies > 0)
- {
- m_persistedFontFamilies.emplace_back(FontFamilyPtr(fontFamily));
- }
- return fontFamily;
- }
- FontFamilyPtr AZ::AtomFont::GetFontFamily(const char* fontFamilyName)
- {
- FontFamilyPtr fontFamily = nullptr;
- // The given string could either be: a font family name (defined in font
- // family XML), a file path (for regular fonts mapped as font families),
- // or just the filename of a font itself. Fonts are mapped by font family
- // name or by filepath, so attempt lookup using the map first since it's
- // the fastest.
- AZStd::string loweredName = AZStd::string(fontFamilyName);
- AZ::StringFunc::TrimWhiteSpace(loweredName, true, true);
- AZStd::to_lower(loweredName.begin(), loweredName.end());
- auto it = m_fontFamilies.find(PathUtil::MakeGamePath(loweredName).c_str());
- if (it != m_fontFamilies.end())
- {
- fontFamily = FontFamilyPtr(it->second);
- }
- else
- {
- // Iterate through all fonts, returning the first match where simply
- // the filename of a font could be a match. This case will likely be
- // hit when text markup references a font that doesn't belong to a
- // font family.
- for (const auto& fontFamilyIter : m_fontFamilies)
- {
- const AZStd::string& mappedFontFamilyName = fontFamilyIter.first;
- AZStd::string mappedFilenameNoExtension = PathUtil::GetFileName(mappedFontFamilyName.c_str());
- AZStd::string searchStringFilenameNoExtension = PathUtil::GetFileName(loweredName);
- if (mappedFilenameNoExtension == searchStringFilenameNoExtension)
- {
- fontFamily = FontFamilyPtr(fontFamilyIter.second);
- break;
- }
- }
- }
- return fontFamily;
- }
- void AZ::AtomFont::AddCharsToFontTextures(FontFamilyPtr fontFamily, const char* chars, int glyphSizeX, int glyphSizeY)
- {
- fontFamily->normal->AddCharsToFontTexture(chars, glyphSizeX, glyphSizeY);
- fontFamily->bold->AddCharsToFontTexture(chars, glyphSizeX, glyphSizeY);
- fontFamily->italic->AddCharsToFontTexture(chars, glyphSizeX, glyphSizeY);
- fontFamily->boldItalic->AddCharsToFontTexture(chars, glyphSizeX, glyphSizeY);
- }
- AZStd::string AZ::AtomFont::GetLoadedFontNames() const
- {
- AZStd::string ret;
- for (FontMapConstItor it = m_fonts.begin(), itEnd = m_fonts.end(); it != itEnd; ++it)
- {
- FFont* font = it->second;
- if (font)
- {
- if (!ret.empty())
- {
- ret += ",";
- }
- ret += font->GetName();
- }
- }
- return ret;
- }
- void AZ::AtomFont::OnLanguageChanged()
- {
- ReloadAllFonts();
- LanguageChangeNotificationBus::Broadcast(&LanguageChangeNotificationBus::Events::LanguageChanged);
- }
- void AZ::AtomFont::ReloadAllFonts()
- {
- // Persist fonts for application lifetime to prevent unnecessary work
- m_persistedFontFamilies.clear();
- AZStd::list<AZStd::string> fontFamilyFilenames;
- AZStd::list<FontFamily*> fontFamilyWeakPtrs;
- // Iterate through all currently loaded font families
- for (auto it : m_fontFamilyReverseLookup)
- {
- fontFamilyWeakPtrs.push_back(it.first);
- fontFamilyFilenames.push_back(it.second->first);
- }
- // Release font-family resources and unmap them
- for (auto fontFamily : fontFamilyWeakPtrs)
- {
- ReleaseFontFamily(fontFamily);
- }
- // Reload the font families
- for (auto familyFilename : fontFamilyFilenames)
- {
- LoadFontFamily(familyFilename.c_str());
- }
- // All UI text components need to reload their font assets (both in-game
- // and in-editor).
- FontNotificationBus::Broadcast(&FontNotificationBus::Events::OnFontsReloaded);
- }
- void AZ::AtomFont::UnregisterFont(const char* fontName)
- {
- AZStd::string name(fontName);
- AZStd::to_lower(name.begin(), name.end());
- AzFramework::FontId fontId = GetFontId(name.c_str());
- FontMapItor it = m_fonts.find(fontId);
- #if defined(AZ_ENABLE_TRACING)
- IFFont* fontPtr = it->second;
- #endif
- if (it != m_fonts.end())
- {
- m_fonts.erase(it);
- }
- #if defined(AZ_ENABLE_TRACING)
- // Make sure the font being released isn't currently in use by a font family.
- // If it is, the FontFamily will have a dangling pointer and will cause a
- // crash when the FontFamily eventually gets released.
- for (auto reverseMapEntry : m_fontFamilyReverseLookup)
- {
- FontFamily* fontFamily = reverseMapEntry.first;
- AZ_Assert(fontFamily->normal != fontPtr,
- "The following font is being freed but still in use by a FontFamily: %s",
- fontName);
- AZ_Assert(fontFamily->italic != fontPtr,
- "The following font is being freed but still in use by a FontFamily: %s",
- fontName);
- AZ_Assert(fontFamily->bold != fontPtr,
- "The following font is being freed but still in use by a FontFamily: %s",
- fontName);
- AZ_Assert(fontFamily->boldItalic != fontPtr,
- "The following font is being freed but still in use by a FontFamily: %s",
- fontName);
- }
- #endif
- }
- IFFont* AZ::AtomFont::LoadFont(const char* fontName)
- {
- AZStd::string fontNameLower = fontName;
- AZStd::to_lower(fontNameLower.begin(), fontNameLower.end());
- IFFont* font = GetFont(fontNameLower.c_str());
- if (font)
- {
- font->AddRef(); // use existing loaded font
- }
- else
- {
- // attempt to create and load a new font, use the font pathname as the font name
- font = NewFont(fontNameLower.c_str());
- if (!font)
- {
- CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Error creating a new font named %s.", fontNameLower.c_str());
- }
- else
- {
- // creating font adds one to its refcount so no need for AddRef here
- if (!font->Load(fontNameLower.c_str()))
- {
- CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Error loading a font from %s.", fontNameLower.c_str());
- font->Release();
- font = nullptr;
- }
- }
- }
- return font;
- }
- void AZ::AtomFont::ReleaseFontFamily(FontFamily* fontFamily)
- {
- // Ensure that Font Family was mapped prior to destruction
- const bool isMapped = m_fontFamilyReverseLookup.find(fontFamily) != m_fontFamilyReverseLookup.end();
- if (!isMapped)
- {
- return;
- }
- // Note that the FontFamily is mapped both by filename and by "family name"
- auto it = m_fontFamilyReverseLookup[fontFamily];
- m_fontFamilies.erase(it);
- AZStd::string familyName(fontFamily->familyName);
- AZStd::to_lower(familyName.begin(), familyName.end());
- m_fontFamilies.erase(familyName.c_str());
- // Reverse lookup is used to avoid needing to store filename path with
- // the font family, so we need to remove that entry also.
- m_fontFamilyReverseLookup.erase(fontFamily);
- SAFE_RELEASE(fontFamily->normal);
- SAFE_RELEASE(fontFamily->bold);
- SAFE_RELEASE(fontFamily->italic);
- SAFE_RELEASE(fontFamily->boldItalic);
- }
- bool AZ::AtomFont::AddFontFamilyToMaps(const char* fontFamilyFilename, const char* fontFamilyName, FontFamilyPtr fontFamily)
- {
- if (!fontFamilyFilename || !fontFamilyName || !fontFamily.get())
- {
- return false;
- }
- // We don't support "updating" mapped values.
- AZStd::string loweredFilename(PathUtil::MakeGamePath(AZStd::string(fontFamilyFilename)).c_str());
- AZStd::to_lower<AZStd::string::iterator>(loweredFilename.begin(), loweredFilename.end());
- if (m_fontFamilies.find(loweredFilename) != m_fontFamilies.end())
- {
- CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "Couldn't load Font Family '%s': already loaded", fontFamilyFilename);
- return false;
- }
- // Similarly, we don't support Font Family XMLs that have the same font
- // family name (we assume all Font Family names are unique).
- AZStd::string loweredFontFamilyName(fontFamilyName);
- AZStd::to_lower<AZStd::string::iterator>(loweredFontFamilyName.begin(), loweredFontFamilyName.end());
- if (m_fontFamilies.find(loweredFontFamilyName) != m_fontFamilies.end())
- {
- CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "Couldn't load Font Family '%s': already loaded", fontFamilyName);
- return false;
- }
- // First, insert by filename
- AZStd::pair<AZStd::string, AZStd::weak_ptr<FontFamily>> insertPair(loweredFilename, fontFamily);
- auto iterPosition = m_fontFamilies.insert(insertPair).first;
- m_fontFamilyReverseLookup[fontFamily.get()] = iterPosition;
-
- // Then, by Font Family name
- AZStd::pair<AZStd::string, AZStd::weak_ptr<FontFamily>> nameInsertPair(loweredFontFamilyName, fontFamily);
- m_fontFamilies.insert(nameInsertPair);
- return true;
- }
- XmlNodeRef AZ::AtomFont::LoadFontFamilyXml(const char* fontFamilyName, AZStd::string& outputDirectory, AZStd::string& outputFullPath)
- {
- outputFullPath = fontFamilyName;
- outputDirectory = PathUtil::GetPath(fontFamilyName);
- XmlNodeRef root = SafeLoadXmlFromFile(outputFullPath);
- // When parsing a <font> tag in markup, only the font name is given and
- // not a path, so we try to build a "best guess" path from the name.
- if (!root)
- {
- AZStd::string fileNoExtension(PathUtil::GetFileName(fontFamilyName));
- AZStd::string fileExtension(PathUtil::GetExt(fontFamilyName));
- if (fileExtension.empty())
- {
- fileExtension = ".fontfamily";
- }
- // Try: "fonts/fontName.fontfamily"
- outputDirectory = AZStd::string("fonts/");
- outputFullPath = outputDirectory + fileNoExtension + fileExtension;
- root = SafeLoadXmlFromFile(outputFullPath);
- // Finally, try: "fonts/fontName/fontName.fontfamily"
- if (!root)
- {
- outputDirectory = AZStd::string("fonts/") + fileNoExtension + "/";
- outputFullPath = outputDirectory + fileNoExtension + fileExtension;
- root = SafeLoadXmlFromFile(outputFullPath);
- }
- }
- return root;
- }
- void AZ::AtomFont::OnAssetReady(Data::Asset<Data::AssetData> asset)
- {
- Data::Asset<RPI::ShaderAsset> shaderAsset = asset;
- AZ::AtomBridge::PerViewportDynamicDraw::Get()->RegisterDynamicDrawContext(
- AZ::Name(AZ::AtomFontDynamicDrawContextName),
- [shaderAsset](RPI::Ptr<RPI::DynamicDrawContext> drawContext)
- {
- AZ_Assert(shaderAsset->IsReady(), "Attempting to register the AtomFont"
- " dynamic draw context before the shader asset is loaded. The shader should be loaded first"
- " to avoid a blocking asset load and potential deadlock, since the DynamicDrawContext lambda"
- " will be executed during scene processing and there may be multiple scenes executing in parallel.");
- Data::Instance<RPI::Shader> shader = RPI::Shader::FindOrCreate(shaderAsset);
- AZ::RPI::ShaderOptionList shaderOptions;
- shaderOptions.push_back(AZ::RPI::ShaderOption(AZ::Name("o_useColorChannels"), AZ::Name("false")));
- shaderOptions.push_back(AZ::RPI::ShaderOption(AZ::Name("o_clamp"), AZ::Name("true")));
- drawContext->InitShaderWithVariant(shader, &shaderOptions);
- drawContext->InitVertexFormat(
- {
- {"POSITION", RHI::Format::R32G32B32_FLOAT},
- {"COLOR", RHI::Format::B8G8R8A8_UNORM},
- {"TEXCOORD0", RHI::Format::R32G32_FLOAT}
- });
- drawContext->EndInit();
- });
- Data::AssetBus::Handler::BusDisconnect();
- }
- #endif
|