/* ** font.cpp ** ** This file is part of mkxp. ** ** Copyright (C) 2013 Jonas Kulla ** ** mkxp 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 2 of the License, or ** (at your option) any later version. ** ** mkxp 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 mkxp. If not, see . */ #include "font.h" #include "sharedstate.h" #include "filesystem.h" #include "exception.h" #include "boost-hash.h" #include "util.h" #include "config.h" #include "bundledfont.h" #include #include #include typedef std::pair FontKey; static SDL_RWops *openBundledFont() { return SDL_RWFromConstMem(BNDL_F_D(BUNDLED_FONT), BNDL_F_L(BUNDLED_FONT)); } struct FontSet { /* 'Regular' style */ std::string regular; /* Any other styles (used in case no 'Regular' exists) */ std::string other; }; struct SharedFontStatePrivate { /* Maps: font family name, To: substituted family name, * as specified via configuration file / arguments */ BoostHash subs; /* Maps: font family name, To: set of physical * font filenames located in "Fonts/" */ BoostHash sets; /* Pool of already opened fonts; once opened, they are reused * and never closed until the termination of the program */ BoostHash pool; }; SharedFontState::SharedFontState(const Config &conf) { p = new SharedFontStatePrivate; /* Parse font substitutions */ for (size_t i = 0; i < conf.fontSubs.size(); ++i) { const std::string &raw = conf.fontSubs[i]; size_t sepPos = raw.find_first_of('>'); if (sepPos == std::string::npos) continue; std::string from = raw.substr(0, sepPos); std::string to = raw.substr(sepPos+1); p->subs.insert(from, to); } } SharedFontState::~SharedFontState() { BoostHash::const_iterator iter; for (iter = p->pool.cbegin(); iter != p->pool.cend(); ++iter) TTF_CloseFont(iter->second); delete p; } void SharedFontState::initFontSetCB(SDL_RWops &ops, const std::string &filename) { TTF_Font *font = TTF_OpenFontRW(&ops, 0, 0); if (!font) return; std::string family = TTF_FontFaceFamilyName(font); std::string style = TTF_FontFaceStyleName(font); TTF_CloseFont(font); FontSet &set = p->sets[family]; if (style == "Regular") set.regular = filename; else set.other = filename; } _TTF_Font *SharedFontState::getFont(std::string family, int size) { /* Check for substitutions */ if (p->subs.contains(family)) family = p->subs[family]; /* Find out if the font asset exists */ const FontSet &req = p->sets[family]; if (req.regular.empty() && req.other.empty()) { /* Doesn't exist; use built-in font */ family = ""; } FontKey key(family, size); TTF_Font *font = p->pool.value(key); if (font) return font; /* Not in pool; open new handle */ SDL_RWops *ops; if (family.empty()) { /* Built-in font */ ops = openBundledFont(); } else { /* Use 'other' path as alternative in case * we have no 'regular' styled font asset */ const char *path = !req.regular.empty() ? req.regular.c_str() : req.other.c_str(); ops = SDL_AllocRW(); shState->fileSystem().openReadRaw(*ops, path, true); } // FIXME 0.9 is guesswork at this point // float gamma = (96.0/45.0)*(5.0/14.0)*(size-5); // font = TTF_OpenFontRW(ops, 1, gamma /** .90*/); font = TTF_OpenFontRW(ops, 1, size* .80); if (!font) throw Exception(Exception::SDLError, "%s", SDL_GetError()); p->pool.insert(key, font); return font; } bool SharedFontState::fontPresent(std::string family) { /* Check for substitutions */ if (p->subs.contains(family)) family = p->subs[family]; const FontSet &set = p->sets[family]; return !(set.regular.empty() && set.other.empty()); } _TTF_Font *SharedFontState::openBundled(int size) { SDL_RWops *ops = openBundledFont(); return TTF_OpenFontRW(ops, 1, size); } struct FontPrivate { std::string name; int size; bool bold; bool italic; bool outline; bool shadow; Color *color; Color *outColor; Color colorTmp; Color outColorTmp; static std::string defaultName; static int defaultSize; static bool defaultBold; static bool defaultItalic; static bool defaultOutline; static bool defaultShadow; static Color *defaultColor; static Color *defaultOutColor; static Color defaultColorTmp; static Color defaultOutColorTmp; /* The actual font is opened as late as possible * (when it is queried by a Bitmap), prior it is * set to null */ TTF_Font *sdlFont; FontPrivate(const char *name = 0, int size = 0) : name(name ? std::string(name) : defaultName), size(size ? size : defaultSize), bold(defaultBold), italic(defaultItalic), outline(defaultOutline), shadow(defaultShadow), color(&colorTmp), outColor(&outColorTmp), colorTmp(*defaultColor), outColorTmp(*defaultOutColor), sdlFont(0) {} FontPrivate(const FontPrivate &other) : name(other.name), size(other.size), bold(other.bold), italic(other.italic), outline(other.outline), shadow(other.shadow), color(&colorTmp), outColor(&outColorTmp), colorTmp(*other.color), outColorTmp(*other.outColor), sdlFont(other.sdlFont) {} void operator=(const FontPrivate &o) { name = o.name; size = o.size; bold = o.bold; italic = o.italic; outline = o.outline; shadow = o.shadow; *color = *o.color; *outColor = *o.outColor; sdlFont = 0; } }; std::string FontPrivate::defaultName = "Arial"; int FontPrivate::defaultSize = 22; bool FontPrivate::defaultBold = false; bool FontPrivate::defaultItalic = false; bool FontPrivate::defaultOutline = false; /* Inited at runtime */ bool FontPrivate::defaultShadow = false; /* Inited at runtime */ Color *FontPrivate::defaultColor = &FontPrivate::defaultColorTmp; Color *FontPrivate::defaultOutColor = &FontPrivate::defaultOutColorTmp; Color FontPrivate::defaultColorTmp(255, 255, 255, 255); Color FontPrivate::defaultOutColorTmp(0, 0, 0, 128); bool Font::doesExist(const char *name) { if (!name) return false; return shState->fontState().fontPresent(name); } Font::Font(const char *name, int size) { p = new FontPrivate(name, size); } Font::Font(const Font &other) { p = new FontPrivate(*other.p); } Font::~Font() { delete p; } const Font &Font::operator=(const Font &o) { *p = *o.p; return o; } const char *Font::getName() const { return p->name.c_str(); } void Font::setName(const char *value) { if (p->name == value) return; p->name = value; p->sdlFont = 0; } void Font::setSize(int value) { if (p->size == value) return; /* Catch illegal values (according to RMXP) */ if (value < 6 || value > 96) throw Exception(Exception::ArgumentError, "%s", "bad value for size"); p->size = value; p->sdlFont = 0; } static void guardDisposed() {} DEF_ATTR_RD_SIMPLE(Font, Size, int, p->size) DEF_ATTR_SIMPLE(Font, Bold, bool, p->bold) DEF_ATTR_SIMPLE(Font, Italic, bool, p->italic) DEF_ATTR_SIMPLE(Font, Shadow, bool, p->shadow) DEF_ATTR_SIMPLE(Font, Outline, bool, p->outline) DEF_ATTR_SIMPLE(Font, Color, Color&, *p->color) DEF_ATTR_SIMPLE(Font, OutColor, Color&, *p->outColor) DEF_ATTR_SIMPLE_STATIC(Font, DefaultSize, int, FontPrivate::defaultSize) DEF_ATTR_SIMPLE_STATIC(Font, DefaultBold, bool, FontPrivate::defaultBold) DEF_ATTR_SIMPLE_STATIC(Font, DefaultItalic, bool, FontPrivate::defaultItalic) DEF_ATTR_SIMPLE_STATIC(Font, DefaultShadow, bool, FontPrivate::defaultShadow) DEF_ATTR_SIMPLE_STATIC(Font, DefaultOutline, bool, FontPrivate::defaultOutline) DEF_ATTR_SIMPLE_STATIC(Font, DefaultColor, Color&, *FontPrivate::defaultColor) DEF_ATTR_SIMPLE_STATIC(Font, DefaultOutColor, Color&, *FontPrivate::defaultOutColor) const char *Font::getDefaultName() { return FontPrivate::defaultName.c_str(); } void Font::setDefaultName(const char *value) { FontPrivate::defaultName = value; } void Font::initDynAttribs() { p->color = new Color(p->colorTmp); if (rgssVer >= 3) p->outColor = new Color(p->outColorTmp);; } void Font::initDefaultDynAttribs() { FontPrivate::defaultColor = new Color(FontPrivate::defaultColorTmp); if (rgssVer >= 3) FontPrivate::defaultOutColor = new Color(FontPrivate::defaultOutColorTmp); } void Font::initDefaults() { FontPrivate::defaultOutline = (rgssVer >= 3 ? true : false); FontPrivate::defaultShadow = (rgssVer == 2 ? true : false); } _TTF_Font *Font::getSdlFont() { if (!p->sdlFont) p->sdlFont = shState->fontState().getFont(p->name.c_str(), p->size); int style = TTF_STYLE_NORMAL; if (p->bold) style |= TTF_STYLE_BOLD; if (p->italic) style |= TTF_STYLE_ITALIC; TTF_SetFontStyle(p->sdlFont, style); return p->sdlFont; }