diff options
Diffstat (limited to 'src/gui/sdlfont.cpp')
-rw-r--r-- | src/gui/sdlfont.cpp | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/src/gui/sdlfont.cpp b/src/gui/sdlfont.cpp new file mode 100644 index 000000000..ab20eee97 --- /dev/null +++ b/src/gui/sdlfont.cpp @@ -0,0 +1,340 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2009 Aethyra Development Team + * Copyright (C) 2011 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * 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 2 of the License, or + * 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 "gui/sdlfont.h" + +#include "client.h" +#include "graphics.h" +#include "log.h" +#include "main.h" + +#include "resources/image.h" +#include "resources/resourcemanager.h" + +#include "utils/stringutils.h" + +#include <guichan/exception.hpp> + +const unsigned int CACHE_SIZE = 256; +const unsigned int CACHE_SIZE_SMALL1 = 90; +const unsigned int CACHE_SIZE_SMALL2 = 150; + +char *strBuf; + +class SDLTextChunk +{ + public: + SDLTextChunk(const std::string &text, const gcn::Color &color) : + img(0), text(text), color(color) + { + } + + ~SDLTextChunk() + { + delete img; + img = 0; + } + + bool operator==(const SDLTextChunk &chunk) const + { + return (chunk.text == text && chunk.color == color); + } + + void generate(TTF_Font *font) + { + SDL_Color sdlCol; + sdlCol.b = static_cast<Uint8>(color.b); + sdlCol.r = static_cast<Uint8>(color.r); + sdlCol.g = static_cast<Uint8>(color.g); + + getSafeUtf8String(text, strBuf); + +// SDL_Surface *surface = TTF_RenderUTF8_Solid( + SDL_Surface *surface = TTF_RenderUTF8_Blended( + font, strBuf, sdlCol); + + if (!surface) + { + img = 0; + return; + } + + img = Image::load(surface); + + SDL_FreeSurface(surface); + } + + Image *img; + std::string text; + gcn::Color color; +}; + +typedef std::list<SDLTextChunk>::iterator CacheIterator; + +static int fontCounter; + +SDLFont::SDLFont(const std::string &filename, int size, int style) : + mCreateCounter(0), + mDeleteCounter(0) +{ + ResourceManager *resman = ResourceManager::getInstance(); + + if (fontCounter == 0 && TTF_Init() == -1) + { + throw GCN_EXCEPTION("Unable to initialize SDL_ttf: " + + std::string(TTF_GetError())); + } + + if (!fontCounter) + { + strBuf = new char[65535]; + memset(strBuf, 0, 65535); + } + + ++fontCounter; + mFont = TTF_OpenFont(resman->getPath(filename).c_str(), size); + + if (!mFont) + { + logger->log("Error finding font " + filename); + mFont = TTF_OpenFont(resman->getPath( + "fonts/dejavusans.ttf").c_str(), size); + if (!mFont) + { + throw GCN_EXCEPTION("SDLSDLFont::SDLSDLFont: " + + std::string(TTF_GetError())); + } + } + + TTF_SetFontStyle(mFont, style); + mCleanTime = cur_time + 120; +} + +SDLFont::~SDLFont() +{ + TTF_CloseFont(mFont); + mFont = 0; + --fontCounter; + + if (fontCounter == 0) + { + TTF_Quit(); + delete []strBuf; + } +} + +void SDLFont::loadFont(const std::string &filename, int size, int style) +{ + ResourceManager *resman = ResourceManager::getInstance(); + + if (fontCounter == 0 && TTF_Init() == -1) + { + logger->log("Unable to initialize SDL_ttf: " + + std::string(TTF_GetError())); + return; + } + + TTF_Font *font = TTF_OpenFont(resman->getPath(filename).c_str(), size); + + if (!font) + { + logger->log("SDLSDLFont::SDLSDLFont: " + + std::string(TTF_GetError())); + return; + } + + if (mFont) + TTF_CloseFont(mFont); + + mFont = font; + TTF_SetFontStyle(mFont, style); + clear(); +} + +void SDLFont::clear() +{ + for (unsigned short f = 0; f < static_cast<unsigned short>( + CACHES_NUMBER); f ++) + { + mCache[static_cast<unsigned short>(f)].clear(); + } +} + +void SDLFont::drawString(gcn::Graphics *graphics, + const std::string &text, + int x, int y) +{ + if (text.empty()) + return; + + Graphics *g = dynamic_cast<Graphics *>(graphics); + + gcn::Color col = g->getColor(); + const float alpha = static_cast<float>(col.a) / 255.0f; + + /* The alpha value is ignored at string generation so avoid caching the + * same text with different alpha values. + */ + col.a = 255; + + SDLTextChunk chunk(text, col); + + unsigned char chr = text[0]; + std::list<SDLTextChunk> *cache = &mCache[chr]; + + bool found = false; + +#ifdef DEBUG_FONT + int cnt = 0; +#endif + + for (CacheIterator i = cache->begin(); i != cache->end(); ++i) + { + if (chunk == (*i)) + { + // Raise priority: move it to front + cache->splice(cache->begin(), *cache, i); + found = true; + break; + } +#ifdef DEBUG_FONT + cnt ++; +#endif + } +#ifdef DEBUG_FONT + logger->log("drawString: " + text + ", iterations: " + toString(cnt)); +#endif + + // Surface not found + if (!found) + { + if (cache->size() >= CACHE_SIZE) + { +#ifdef DEBUG_FONT_COUNTERS + mDeleteCounter ++; +#endif + cache->pop_back(); + } +#ifdef DEBUG_FONT_COUNTERS + mCreateCounter ++; +#endif + cache->push_front(chunk); + cache->front().generate(mFont); + + if (!mCleanTime) + { + mCleanTime = cur_time + 120; + } + else if (mCleanTime < cur_time) + { + doClean(); + mCleanTime = cur_time + 120; + } + } + + if (cache->front().img) + { + cache->front().img->setAlpha(alpha); + g->drawImage(cache->front().img, x, y); + } + +} + +void SDLFont::createSDLTextChunk(SDLTextChunk *chunk) +{ + if (!chunk || chunk->text.empty()) + return; + + const float alpha = static_cast<float>(chunk->color.a) / 255.0f; + chunk->color.a = 255; + chunk->generate(mFont); + if (chunk->img) + chunk->img->setAlpha(alpha); +} + +int SDLFont::getWidth(const std::string &text) const +{ + if (text.empty()) + return 0; + + unsigned char chr = text[0]; + std::list<SDLTextChunk> *cache = &mCache[chr]; + +#ifdef DEBUG_FONT + int cnt = 0; +#endif + + for (CacheIterator i = cache->begin(); i != cache->end(); ++i) + { + if (i->text == text) + { + // Raise priority: move it to front + // Assumption is that TTF::draw will be called next + cache->splice(cache->begin(), *cache, i); + if (i->img) + return i->img->getWidth(); + else + return 0; + } +#ifdef DEBUG_FONT + cnt ++; +#endif + } + +#ifdef DEBUG_FONT + logger->log("getWidth: " + text + ", iterations: " + toString(cnt)); +#endif + + int w, h; + getSafeUtf8String(text, strBuf); + TTF_SizeUTF8(mFont, strBuf, &w, &h); + return w; +} + +int SDLFont::getHeight() const +{ + return TTF_FontHeight(mFont); +} + +void SDLFont::doClean() +{ + for (int f = 0; f < CACHES_NUMBER; f ++) + { + std::list<SDLTextChunk> *cache = &mCache[f]; + if (cache->size() > CACHE_SIZE_SMALL2) + { +#ifdef DEBUG_FONT_COUNTERS + mDeleteCounter += 10; +#endif + for (int d = 0; d < 10; d ++) + cache->pop_back(); + } + else if (cache->size() > CACHE_SIZE_SMALL1) + { +#ifdef DEBUG_FONT_COUNTERS + mDeleteCounter ++; +#endif + cache->pop_back(); + } + } +}
\ No newline at end of file |