Files
scummvm/graphics/macgui/macfontmanager.cpp
T
Harishankar Kumar 9b6d9b0c27 GRAPHICS: Fix WinFont scaling memory leak when scaling fonts.
Memory leak happened because the scaled generated fonts were not
being cached or saved anywhere, thus they were not being destructed
and freed. Added logic to cache generated font in _winFontRegistry
itself.

Leaks observed in `trektech-win` of director engine in first few frames itself.
2023-07-08 22:13:28 +03:00

865 lines
25 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "common/archive.h"
#include "common/stream.h"
#include "common/compression/unzip.h"
#include "common/macresman.h"
#include "graphics/fonts/bdf.h"
#include "graphics/fonts/macfont.h"
#include "graphics/fonts/winfont.h"
#include "graphics/fonts/ttf.h"
#include "graphics/macgui/macwindowmanager.h"
#include "graphics/macgui/macfontmanager.h"
namespace Graphics {
// Source: Apple IIGS Technical Note #41, "Font Family Numbers"
// https://www.1000bit.it/support/manuali/apple/technotes/iigs/tn.iigs.041.html
static struct FontProto {
int id;
Common::Language lang;
Common::CodePage encoding;
const char *name;
} defaultFonts[] = {
{ 2, Common::UNK_LANG, Common::kMacRoman, "New York" },
{ 3, Common::UNK_LANG, Common::kMacRoman, "Geneva" },
{ 4, Common::UNK_LANG, Common::kMacRoman, "Monaco" },
{ 5, Common::UNK_LANG, Common::kMacRoman, "Venice" },
{ 6, Common::UNK_LANG, Common::kMacRoman, "London" },
{ 7, Common::UNK_LANG, Common::kMacRoman, "Athens" },
{ 8, Common::UNK_LANG, Common::kMacRoman, "San Francisco" },
{ 9, Common::UNK_LANG, Common::kMacRoman, "Toronto" },
{ 11, Common::UNK_LANG, Common::kMacRoman, "Cairo" },
{ 12, Common::UNK_LANG, Common::kMacRoman, "Los Angeles" },
{ 13, Common::UNK_LANG, Common::kMacRoman, "Zapf Dingbats" },
{ 14, Common::UNK_LANG, Common::kMacRoman, "Bookman" },
{ 15, Common::UNK_LANG, Common::kMacRoman, "Helvetica Narrow" },
{ 16, Common::UNK_LANG, Common::kMacRoman, "Palatino" },
{ 18, Common::UNK_LANG, Common::kMacRoman, "Zapf Chancery" },
{ 20, Common::UNK_LANG, Common::kMacRoman, "Times" },
{ 21, Common::UNK_LANG, Common::kMacRoman, "Helvetica" },
{ 22, Common::UNK_LANG, Common::kMacRoman, "Courier" },
{ 23, Common::UNK_LANG, Common::kMacRoman, "Symbol" },
{ 24, Common::UNK_LANG, Common::kMacRoman, "Taliesin" }, // mobile?
{ 33, Common::UNK_LANG, Common::kMacRoman, "Avant Garde" },
{ 34, Common::UNK_LANG, Common::kMacRoman, "New Century Schoolbook" },
{ 16383, Common::UNK_LANG, Common::kMacRoman, "Chicago" },
// Japanese (names are Shift JIS encoded)
{ 16384, Common::JA_JPN, Common::kUtf8, "Osaka" },
{ 16436, Common::JA_JPN, Common::kUtf8, "Osaka\x81\x7C\x93\x99\x95\x9D" }, // Osaka Mono
{ -1, Common::UNK_LANG, Common::kCodePageInvalid, NULL }
};
struct AliasProto {
int id;
int aliasForId;
const char *name;
};
static AliasProto defaultAliases[] = {
// English names for Japanese fonts
{ 16436, 16436, "OsakaMono" },
// Missing Japanese fonts
// These technically should be separate fonts, not just aliases for Osaka.
// However, we don't have a free source for these right now.
{ 16396, 16384, "\x96\x7B\x96\xBE\x92\xA9\x81\x7C\x82\x6C" }, // Book Mincho - M
{ 16433, 16436, "\x93\x99\x95\x9D\x83\x53\x83\x56\x83\x62\x83\x4E" }, // Mono Gothic
{ 16435, 16436, "\x93\x99\x95\x9D\x96\xBE\x92\xA9" }, // Mono Ming
{ 16640, 16384, "\x92\x86\x83\x53\x83\x56\x83\x62\x83\x4E\x91\xCC" }, // Medium Gothic
{ 16641, 16384, "\x8D\xD7\x96\xBE\x92\xA9\x91\xCC" }, // Ming
{ 16700, 16384, "\x95\xBD\x90\xAC\x96\xBE\x92\xA9" }, // Heisei Mincho
{ 16701, 16384, "\x95\xBD\x90\xAC\x8A\x70\x83\x53\x83\x56\x83\x62\x83\x4E" }, // Heisei Kaku Gothic
{ -1, -1, NULL }
};
static AliasProto latinModeAliases[] = {
{ 0, 16383, "System" }, // Chicago
{ 1, 3, "Application" }, // Geneva
{ -1, -1, NULL }
};
static AliasProto japaneseModeAliases[] = {
{ 0, 16384, "System" }, // Osaka
{ 1, 16384, "Application" }, // Osaka
{ -1, -1, NULL }
};
static const char *const fontStyleSuffixes[] = {
"Regular",
"Bold",
"Italic",
"Underline",
"Outline",
"Shadow",
"Condense",
"Extend"
};
int parseSlant(const Common::String fontname) {
int res = 0;
for (int i = 1; i < 7; i++)
if (fontname.contains(fontStyleSuffixes[i]))
res |= (1 << (i - 1));
return res;
}
Common::String cleanFontName(const Common::String fontname) {
const char *pos;
Common::String f = fontname;
for (int i = 0; i < 7; i++) {
if ((pos = strstr(f.c_str(), fontStyleSuffixes[i]))) {
while (pos > f.c_str() && *(pos - 1) == '-')
pos--;
f = Common::String(f.c_str(), pos);
}
}
f.trim();
return f;
}
MacFontManager::MacFontManager(uint32 mode, Common::Language language) : _mode(mode),
_language(language), _japaneseFontsLoaded(false) {
for (FontProto *font = defaultFonts; font->name; font++) {
if (!_fontInfo.contains(font->id)) {
FontInfo *info = new FontInfo;
info->lang = font->lang;
info->encoding = font->encoding;
info->name = font->name;
_fontInfo[font->id] = info;
}
if (!_fontIds.contains(font->name)) {
_fontIds[font->name] = font->id;
}
}
for (AliasProto *alias = defaultAliases; alias->name; alias++) {
if (!_fontInfo.contains(alias->id)) {
FontInfo *info = new FontInfo;
info->aliasForId = alias->aliasForId;
info->name = alias->name;
_fontInfo[alias->id] = info;
}
if (!_fontIds.contains(alias->name)) {
_fontIds[alias->name] = alias->id;
}
}
setLocalizedFonts();
if (_mode & MacGUIConstants::kWMModeForceBuiltinFonts) {
_builtInFonts = true;
} else {
loadFonts();
}
}
MacFontManager::~MacFontManager() {
for (auto &it: _fontInfo)
delete it._value;
for (auto &it: _uniFonts)
delete it._value;
for (auto &it: _ttfData)
delete it._value;
for (auto &it: _fontRegistry)
delete it._value;
for (auto &it: _winFontRegistry)
delete it._value;
for (auto &it: _fontFamilies)
delete it;
}
void MacFontManager::setLocalizedFonts() {
AliasProto *aliases = latinModeAliases;
if (_language == Common::JA_JPN) {
aliases = japaneseModeAliases;
loadJapaneseFonts();
}
for (AliasProto *alias = aliases; alias->name; alias++) {
if (_fontInfo.contains(alias->id)) {
// Overwrite the font info that's already registered in case
// we're switching languages or something.
delete _fontInfo[alias->id];
}
FontInfo *info = new FontInfo;
info->aliasForId = alias->aliasForId;
info->name = alias->name;
_fontInfo[alias->id] = info;
_fontIds[alias->name] = alias->id;
}
}
void MacFontManager::loadFontsBDF() {
Common::Archive *dat;
dat = Common::makeZipArchive("classicmacfonts.dat");
if (!dat) {
warning("Could not find classicmacfonts.dat. Falling back to built-in fonts");
_builtInFonts = true;
return;
}
Common::ArchiveMemberList list;
dat->listMembers(list);
for (Common::ArchiveMemberList::iterator it = list.begin(); it != list.end(); ++it) {
Common::SeekableReadStream *stream = dat->createReadStreamForMember((*it)->getName());
Graphics::BdfFont *font = Graphics::BdfFont::loadFont(*stream);
delete stream;
Common::String fontName;
MacFont *macfont;
if (font->getFamilyName() && *font->getFamilyName()) {
fontName = Common::String::format("%s-%s-%d", font->getFamilyName(), font->getFontSlant(), font->getFontSize());
macfont = new MacFont(_fontIds.getValOrDefault(font->getFamilyName(), kMacFontNonStandard), font->getFontSize(), parseFontSlant(font->getFontSlant()));
} else { // Get it from the file name
fontName = (*it)->getName();
// Trim the .bdf extension
for (int i = fontName.size() - 1; i >= 0; --i) {
if (fontName[i] == '.') {
while ((uint)i < fontName.size()) {
fontName.deleteLastChar();
}
break;
}
}
macfont = new MacFont(kMacFontNonStandard);
macfont->setName(fontName);
}
FontMan.assignFontToName(fontName, font);
//macfont->setFont(font);
_fontRegistry.setVal(fontName, macfont);
debug(2, " %s", fontName.c_str());
}
_builtInFonts = false;
delete dat;
}
void MacFontManager::loadFonts() {
Common::Archive *dat;
dat = Common::makeZipArchive("classicmacfonts.dat");
if (!dat) {
warning("Could not find classicmacfonts.dat. Falling back to built-in fonts");
_builtInFonts = true;
return;
}
Common::ArchiveMemberList list;
dat->listMembers(list);
for (Common::ArchiveMemberList::iterator it = list.begin(); it != list.end(); ++it) {
Common::SeekableReadStream *stream = dat->createReadStreamForMember((*it)->getName());
loadFonts(stream);
}
_builtInFonts = false;
delete dat;
}
void MacFontManager::loadJapaneseFonts() {
if (_japaneseFontsLoaded)
return;
#ifdef USE_FREETYPE2
Common::Archive *dat;
dat = Common::makeZipArchive("japanesemacfonts.dat");
if (!dat) {
warning("Could not find japanesemacfonts.dat");
return;
}
Common::ArchiveMemberList list;
dat->listMembers(list);
for (Common::ArchiveMemberList::iterator it = list.begin(); it != list.end(); ++it) {
Common::SeekableReadStream *stream = dat->createReadStreamForMember((*it)->getName());
Common::String fontName = (*it)->getName();
// Trim the .ttf extension
for (int i = fontName.size() - 1; i >= 0; --i) {
if (fontName[i] == '.') {
while ((uint)i < fontName.size()) {
fontName.deleteLastChar();
}
break;
}
}
_ttfData[fontName + "-0-0"] = stream;
}
delete dat;
#else
warning("Japanese fonts require FreeType");
#endif
// Set this to true even if we don't have FreeType so we don't spam warnings.
_japaneseFontsLoaded = true;
}
void MacFontManager::loadFonts(Common::SeekableReadStream *stream) {
Common::MacResManager fontFile;
if (!fontFile.loadFromMacBinary(stream))
return;
loadFonts(&fontFile);
}
void MacFontManager::loadFonts(const Common::String &fileName) {
Common::MacResManager fontFile;
if (!fontFile.open(fileName))
return;
loadFonts(&fontFile);
}
void MacFontManager::loadFonts(Common::MacResManager *fontFile) {
Common::MacResIDArray fonds = fontFile->getResIDArray(MKTAG('F','O','N','D'));
if (fonds.size() > 0) {
for (Common::Array<uint16>::iterator iterator = fonds.begin(); iterator != fonds.end(); ++iterator) {
Common::SeekableReadStream *fond = fontFile->getResource(MKTAG('F', 'O', 'N', 'D'), *iterator);
Common::String familyName = fontFile->getResName(MKTAG('F', 'O', 'N', 'D'), *iterator);
int familySlant = parseSlant(familyName);
familyName = cleanFontName(familyName);
Graphics::MacFontFamily *fontFamily = new MacFontFamily(familyName);
fontFamily->load(*fond);
Common::Array<Graphics::MacFontFamily::AsscEntry> *assoc = fontFamily->getAssocTable();
bool fontFamilyUsed = false;
for (uint i = 0; i < assoc->size(); i++) {
debug(8, "size: %d style: %d id: %d", (*assoc)[i]._fontSize, (*assoc)[i]._fontStyle | familySlant,
(*assoc)[i]._fontID);
Common::SeekableReadStream *fontstream;
MacFont *macfont;
Graphics::MacFONTFont *font;
fontstream = fontFile->getResource(MKTAG('N', 'F', 'N', 'T'), (*assoc)[i]._fontID);
if (!fontstream)
fontstream = fontFile->getResource(MKTAG('F', 'O', 'N', 'T'), (*assoc)[i]._fontID);
#ifdef USE_FREETYPE2
if (!fontstream) {
// The sfnt resource should be just a copy of a TTF
fontstream = fontFile->getResource(MKTAG('s', 'f', 'n', 't'), (*assoc)[i]._fontID);
Common::String fontName = Common::String::format("%s-%d-0", familyName.c_str(), (*assoc)[i]._fontStyle | familySlant);
_ttfData[fontName] = fontstream;
continue;
}
#endif
if (!fontstream) {
if ((*assoc)[i]._fontSize == 0) {
warning("MacFontManager: Detected possible TrueType FontID %d, but no TrueType support detected", (*assoc)[i]._fontID);
} else {
warning("MacFontManager: Unknown FontId: %d", (*assoc)[i]._fontID);
}
continue;
}
fontFamilyUsed = true;
font = new Graphics::MacFONTFont;
font->loadFont(*fontstream, fontFamily, (*assoc)[i]._fontSize, (*assoc)[i]._fontStyle | familySlant);
delete fontstream;
Common::String fontName = Common::String::format("%s-%d-%d", familyName.c_str(), (*assoc)[i]._fontStyle | familySlant, (*assoc)[i]._fontSize);
macfont = new MacFont(_fontIds.getValOrDefault(familyName, kMacFontNonStandard), (*assoc)[i]._fontSize, (*assoc)[i]._fontStyle | familySlant);
FontMan.assignFontToName(fontName, font);
macfont->setFont(font, false);
_fontRegistry.setVal(fontName, macfont);
debug(2, " %s", fontName.c_str());
}
delete fond;
if (fontFamilyUsed)
_fontFamilies.push_back(fontFamily);
else
delete fontFamily;
}
}
}
void MacFontManager::loadWindowsFont(const Common::String fileName) {
Graphics::WinFont *winFont = new Graphics::WinFont();
bool isLoaded = winFont->loadFromFON(fileName);
if (!isLoaded) {
warning("MacFontManager::loadWindowsFont(): Windows Font data from file %s not loaded", fileName.c_str());
delete winFont;
return;
}
Common::String fontName = winFont->getName();
_winFontRegistry.setVal(fontName, winFont);
MacFont *font = new MacFont();
Common::String fullName = Common::String::format("%s-%d-%d", fontName.c_str(), winFont->getStyle(), winFont->getFontHeight());
font->setName(fullName);
font->setFont(winFont, false);
_fontRegistry.setVal(font->getName(), font);
debug(2, "MacFontManager::loadWindowsFont(): Loaded font %s", fullName.c_str());
}
const Font *MacFontManager::getFont(MacFont *macFont) {
Common::String name;
const Font *font = 0;
debug(6, "getFont(%s), id: %d", getFontName(macFont->getId(), macFont->getSize(), macFont->getSlant(), 0).c_str(), macFont->getId());
int aliasForId = getFontAliasForId(macFont->getId());
if (aliasForId > -1) {
macFont->setId(aliasForId);
}
if (!_builtInFonts) {
Common::Language lang = getFontLanguage(macFont->getId());
if (lang == Common::JA_JPN && !_japaneseFontsLoaded) {
loadJapaneseFonts();
}
if (macFont->getName().empty()) {
name = getFontName(macFont->getId(), macFont->getSize(), macFont->getSlant());
macFont->setName(name);
}
if (!_fontRegistry.contains(macFont->getName())) {
// Let's try to generate name
if (macFont->getSlant() != kMacFontRegular) {
name = getFontName(macFont->getId(), macFont->getSize(), macFont->getSlant(), true);
macFont->setName(name);
}
if (!_fontRegistry.contains(macFont->getName()))
generateFontSubstitute(*macFont);
}
font = FontMan.getFontByName(macFont->getName());
if (!font) {
debug(1, "Cannot load font '%s'", macFont->getName().c_str());
font = FontMan.getFontByName(MacFont(kMacFontChicago, 12).getName());
}
}
#ifdef USE_FREETYPE2
if (!font) {
if (_mode & kWMModeUnicode) {
if (macFont->getSize() <= 0) {
debug(1, "MacFontManager::getFont() - Font size <= 0!");
}
Common::HashMap<int, const Graphics::Font *>::iterator pFont = _uniFonts.find(macFont->getSize());
if (pFont != _uniFonts.end()) {
font = pFont->_value;
} else {
font = Graphics::loadTTFFontFromArchive("FreeSans.ttf", macFont->getSize(), Graphics::kTTFSizeModeCharacter, 0, Graphics::kTTFRenderModeMonochrome);
_uniFonts[macFont->getSize()] = font;
}
}
}
#endif
if (!font) {
int id = macFont->getId();
if (_fontInfo.contains(id) && _winFontRegistry.contains(_fontInfo.getVal(id)->name)) {
font = _winFontRegistry.getVal(_fontInfo.getVal(id)->name);
const Graphics::WinFont *winfont = (const Graphics::WinFont *)font;
if (winfont->getFontHeight() != macFont->getSize()) {
debug(5, "MacFontManager::getFont(): For font '%s' windows font '%s' is used of a different size %d", macFont->getName().c_str(), winfont->getName().c_str(), winfont->getFontHeight());
Common::String fullFontName = Common::String::format("%s-%d-%d", winfont->getName().c_str(), winfont->getStyle(), macFont->getSize());
if (_winFontRegistry.contains(fullFontName)) {
// Check if we have generated this earlier, in that case reuse it.
font = _winFontRegistry.getVal(fullFontName);
} else {
// Generate a scaledFont
Graphics::WinFont *scaledWinFont = WinFont::scaleFont(winfont, macFont->getSize());
if (scaledWinFont) {
debug(5, "MacFontManager::getFont(): Generated scaled winFont %s", fullFontName.c_str());
// register font generated for reuse
_winFontRegistry.setVal(fullFontName, scaledWinFont);
font = scaledWinFont;
}
}
}
}
}
if (!font) {
font = macFont->getFallback();
for (auto &it : _fontInfo) {
if (it._value->name == macFont->getFallbackName()) {
macFont->setId(it._key);
}
}
}
return font;
}
const Font *MacFontManager::getFont(MacFont macFont) {
return getFont(&macFont);
}
int MacFontManager::parseFontSlant(Common::String slant) {
slant.toUppercase();
int slantVal = 0;
if (slant == "I")
slantVal |= kMacFontItalic;
if (slant == "B")
slantVal |= kMacFontBold;
if (slant == "R")
slantVal |= kMacFontRegular;
return slantVal;
}
int MacFontManager::parseSlantFromName(const Common::String &name) {
int slantVal = 0;
if (name.contains(" Bold") || name.equalsIgnoreCase("Bold"))
slantVal |= kMacFontBold;
if (name.contains(" Italic") || name.equalsIgnoreCase("Italic"))
slantVal |= kMacFontItalic;
if (name.contains(" Regular") || name.equalsIgnoreCase("Regular"))
slantVal |= kMacFontRegular;
if (name.contains(" Underline") || name.equalsIgnoreCase("Underline"))
slantVal |= kMacFontUnderline;
if (name.contains(" Shadow") || name.equalsIgnoreCase("Shadow"))
slantVal |= kMacFontShadow;
if (name.contains(" Outline") || name.equalsIgnoreCase("Outline"))
slantVal |= kMacFontOutline;
if (name.contains(" Condense") || name.equalsIgnoreCase("Condense"))
slantVal |= kMacFontCondense;
if (name.contains(" Extend") || name.equalsIgnoreCase("Extend"))
slantVal |= kMacFontExtend;
if (name.contains(" Plain") || name.equalsIgnoreCase("Plain"))
slantVal = kMacFontRegular;
return slantVal;
}
int MacFontManager::registerFontName(Common::String name, int preferredId) {
// Don't register an empty font name, just return Geneva's ID.
if (name.empty())
return 1;
if (_fontIds.contains(name))
return _fontIds[name];
int id;
if (preferredId > -1 && !_fontInfo.contains(preferredId)) {
id = preferredId;
} else {
// Preferred ID is already registered, find an unused one.
id = 100;
while (_fontInfo.contains(id))
id++;
}
FontInfo *info = new FontInfo;
info->name = name;
if (preferredId >= 0x4000) {
info->lang = Common::JA_JPN;
info->encoding = Common::kWindows932; // default to Shift JIS
} else {
info->encoding = Common::kMacRoman;
}
_fontInfo[id] = info;
_fontIds[name] = id;
return id;
}
void MacFont::setName(const char *name) {
_name = name;
}
const Common::String MacFontManager::getFontName(uint16 id, int size, int slant, bool tryGen) {
Common::String rawName = getFontName(id);
Common::String n = cleanFontName(rawName);
int extraSlant = parseFontSlant(rawName);
// let's try parse slant from name
if (!extraSlant)
extraSlant = parseSlantFromName(rawName);
if (n.empty()) {
warning("MacFontManager: Requested font ID %d not found. Falling back to Geneva", id);
n = "Geneva";
}
return Common::String::format("%s-%d-%d", n.c_str(), slant | extraSlant, size);
}
const Common::String MacFontManager::getFontName(const MacFont &font) {
return getFontName(font.getId(), font.getSize(), font.getSlant());
}
int MacFontManager::getFontIdByName(Common::String name) {
if (_fontIds.contains(name))
return _fontIds[name];
for (auto it = _fontIds.begin(); it != _fontIds.end(); it++) {
if (it->_key.equalsIgnoreCase(name)) {
return it->_value;
}
}
return 1;
}
Common::Language MacFontManager::getFontLanguage(uint16 id) {
if (!_fontInfo.contains(id)) {
warning("MacFontManager::getFontLanguage: No _fontInfo entry for font %d", id);
return Common::UNK_LANG;
}
if (_fontInfo[id]->aliasForId > -1) {
return getFontLanguage(_fontInfo[id]->aliasForId);
}
return _fontInfo[id]->lang;
}
Common::CodePage MacFontManager::getFontEncoding(uint16 id) {
if (!_fontInfo.contains(id)) {
warning("MacFontManager::getFontEncoding: No _fontInfo entry for font %d", id);
return Common::kCodePageInvalid;
}
if (_fontInfo[id]->aliasForId > -1) {
return getFontEncoding(_fontInfo[id]->aliasForId);
}
return _fontInfo[id]->encoding;
}
int MacFontManager::getFontAliasForId(uint16 id) {
if (!_fontInfo.contains(id)) {
warning("MacFontManager::getFontAliasForId: No _fontInfo entry for font %d", id);
return -1;
}
return _fontInfo[id]->aliasForId;
}
Common::String MacFontManager::getFontName(uint16 id) {
if (!_fontInfo.contains(id)) {
warning("MacFontManager::getFontName: No _fontInfo entry for font %d", id);
return "";
}
if (_fontInfo[id]->aliasForId > -1) {
return getFontName(_fontInfo[id]->aliasForId);
}
return _fontInfo[id]->name;
}
void MacFontManager::generateFontSubstitute(MacFont &macFont) {
Common::String name;
#ifdef USE_FREETYPE2
// Check if we have TTF data for this font.
name = getFontName(macFont.getId(), 0, macFont.getSlant());
if (_ttfData.contains(name)) {
generateTTFFont(macFont, _ttfData[name]);
return;
}
#endif
// Try to see if we have regular font
if (macFont.getSlant() != kMacFontRegular) {
name = getFontName(macFont.getId(), macFont.getSize(), kMacFontRegular);
if (_fontRegistry.contains(name) && !_fontRegistry[name]->isGenerated()) {
generateFONTFont(macFont, *_fontRegistry[name]);
return;
}
}
// Now try twice size
name = getFontName(macFont.getId(), macFont.getSize() * 2, macFont.getSlant());
if (_fontRegistry.contains(name) && !_fontRegistry[name]->isGenerated()) {
generateFONTFont(macFont, *_fontRegistry[name]);
return;
}
// Now half size
name = getFontName(macFont.getId(), macFont.getSize() / 2, macFont.getSlant());
if (_fontRegistry.contains(name) && !_fontRegistry[name]->isGenerated()) {
generateFONTFont(macFont, *_fontRegistry[name]);
return;
}
// No simple substitute was found. Looking for neighborhood fonts
// First we gather all font sizes for this font
Common::Array<MacFont *> sizes;
for (Common::HashMap<Common::String, MacFont *>::iterator i = _fontRegistry.begin(); i != _fontRegistry.end(); ++i) {
if (i->_value->getId() == macFont.getId() && i->_value->getSlant() == macFont.getSlant() && !i->_value->isGenerated())
sizes.push_back(i->_value);
}
if (sizes.empty()) {
if (macFont.getSlant() == kMacFontRegular) {
debug(1, "No viable substitute found (1) for font %s", getFontName(macFont).c_str());
return;
}
// Now let's try to find a regular font
for (Common::HashMap<Common::String, MacFont *>::iterator i = _fontRegistry.begin(); i != _fontRegistry.end(); ++i) {
if (i->_value->getId() == macFont.getId() && i->_value->getSlant() == kMacFontRegular && !i->_value->isGenerated())
sizes.push_back(i->_value);
}
if (sizes.empty()) {
debug(1, "No viable substitute found (2) for font %s", getFontName(macFont).c_str());
return;
}
}
// Now looking for the next larger font, and store the largest one for next check
MacFont *candidate = nullptr;
MacFont *maxSize = sizes[0];
for (uint i = 0; i < sizes.size(); i++) {
if (sizes[i]->getSize() == macFont.getSize()) { // Same size but regular slant
candidate = sizes[i];
break;
}
if ((!candidate && sizes[i]->getSize() > macFont.getSize())
|| (candidate && sizes[i]->getSize() < candidate->getSize()))
candidate = sizes[i];
if (sizes[i]->getSize() > maxSize->getSize())
maxSize = sizes[i];
}
if (candidate) {
generateFONTFont(macFont, *candidate);
return;
}
// Now next smaller font, which is the biggest we have
generateFONTFont(macFont, *maxSize);
}
#ifdef USE_FREETYPE2
void MacFontManager::generateTTFFont(MacFont &toFont, Common::SeekableReadStream *stream) {
debug("Generating TTF font '%s'", getFontName(toFont).c_str());
// TODO: Handle getSlant() flags
stream->seek(0);
Font *font = Graphics::loadTTFFont(*stream, toFont.getSize(), Graphics::kTTFSizeModeCharacter, 0, Graphics::kTTFRenderModeMonochrome);
if (!font) {
warning("Failed to generate font '%s'", getFontName(toFont).c_str());
}
toFont.setGenerated(true);
toFont.setFont(font, true);
FontMan.assignFontToName(getFontName(toFont), font);
_fontRegistry.setVal(getFontName(toFont), new MacFont(toFont));
debug("Generated font '%s'", getFontName(toFont).c_str());
}
#endif
void MacFontManager::generateFONTFont(MacFont &toFont, MacFont &fromFont) {
if (fromFont.isTrueType()) {
warning("Cannot generate FONT font '%s' from TTF font '%s'", getFontName(toFont).c_str(), getFontName(fromFont).c_str());
return;
}
debugN("Found font substitute for font '%s' ", getFontName(toFont).c_str());
debug("as '%s'", getFontName(fromFont).c_str());
int slant = kMacFontRegular;
if (fromFont.getSlant() == kMacFontRegular)
slant = toFont.getSlant();
MacFONTFont *fromFONTFont = static_cast<MacFONTFont *>(fromFont.getFont());
MacFONTFont *font = Graphics::MacFONTFont::scaleFont(fromFONTFont, toFont.getSize(), slant);
if (!font) {
warning("Failed to generate font '%s'", getFontName(toFont).c_str());
}
toFont.setGenerated(true);
toFont.setFont(font, false);
FontMan.assignFontToName(getFontName(toFont), font);
_fontRegistry.setVal(getFontName(toFont), new MacFont(toFont));
debug("Generated font '%s'", getFontName(toFont).c_str());
}
void MacFont::setFallback(const Font *font, Common::String name) {
_fallback = font;
_fallbackName = name;
}
} // End of namespace Graphics