view engine/core/video/fonts/fontbase.cpp @ 228:756b895e1dab

Merged unicode-support back into trunk. Now all GUI/visible strings should be unicode. Internal strings unchanged. Remember to use a font that actually has the desired codepoints. Current default unicode policiy is 'ignore'.
author phoku@33b003aa-7bff-0310-803a-e67f0ece8222
date Sat, 21 Mar 2009 10:38:11 +0000
parents 9a05ba6735b1
children 7887f3854862
line wrap: on
line source

/***************************************************************************
 *   Copyright (C) 2005-2008 by the FIFE team                              *
 *   http://www.fifengine.de                                               *
 *   This file is part of FIFE.                                            *
 *                                                                         *
 *   FIFE is free software; you can redistribute it and/or                 *
 *   modify it under the terms of the GNU Lesser General Public            *
 *   License as published by the Free Software Foundation; either          *
 *   version 2.1 of the License, or (at your option) any later version.    *
 *                                                                         *
 *   This library 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     *
 *   Lesser General Public License for more details.                       *
 *                                                                         *
 *   You should have received a copy of the GNU Lesser General Public      *
 *   License along with this library; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
 ***************************************************************************/

// Standard C++ library includes
#include <vector>

// Platform specific includes

// 3rd party library includes
#include <boost/filesystem/convenience.hpp>

// FIFE includes
// These includes are split up in two parts, separated by one empty line
// First block: files included from the FIFE root src directory
// Second block: files included from the same folder
#include "util/structures/rect.h"
#include "util/base/exception.h"
#include "util/utf8/utf8.h"
#include "video/image.h"
#include "video/renderbackend.h"

#include "fontbase.h"

namespace FIFE {

	FontBase::FontBase():
			m_pool(),
			mColor(),
			mGlyphSpacing(0),
			mRowSpacing(0),
			mFilename(""),
			m_antiAlias(true) {
	}

	void FontBase::setRowSpacing(int spacing) {
		mRowSpacing = spacing;
	}

	int FontBase::getRowSpacing() const {
		return mRowSpacing;
	}

	void FontBase::setGlyphSpacing(int spacing) {
		mGlyphSpacing = spacing;
	}

	int FontBase::getGlyphSpacing() const {
		return mGlyphSpacing;
	}

	void FontBase::setAntiAlias(bool antiAlias) {
		m_antiAlias = antiAlias;
	}

	bool FontBase::isAntiAlias() {
		return m_antiAlias;
	}

	SDL_Color FontBase::getColor() const {
		return mColor;
	}

	int FontBase::getStringIndexAt(const std::string &text, int x) {
		assert( utf8::is_valid(text.begin(), text.end()) );
		std::string::const_iterator cur;
		if (text.size() == 0) return 0;
		if (x <= 0) return 0;

		cur = text.begin();

		utf8::next(cur, text.end());

		std::string buff;
		while(cur != text.end()) {
			buff = std::string(text.begin(), cur);

			if (getWidth(buff) > x) {
				return buff.size();
			} else {
				utf8::next(cur, text.end());
			}
		}

		if (x > getWidth(text)) {
			return text.size();
		} else {
			return buff.size();
		}
	}

	Image* FontBase::getAsImage(const std::string& text) {
		Image* image = m_pool.getRenderedText(this, text);
		if (!image) {
			SDL_Surface* textSurface = renderString(text);
			image = RenderBackend::instance()->createImage(textSurface);
			m_pool.addRenderedText( this, text, image );
		}
		return image;
	}

	Image* FontBase::getAsImageMultiline(const std::string& text) {
		const uint8_t newline_utf8 = '\n';
		uint32_t newline;
		utf8::utf8to32(&newline_utf8,&newline_utf8 + 1,&newline);
		//std::cout << "Text:" << text << std::endl;
		Image* image = m_pool.getRenderedText(this, text);
		if (!image) {
			std::vector<SDL_Surface*> lines;
			std::string::const_iterator it = text.begin();
			// split text as needed
			int render_width = 0, render_height = 0;
			do {
				uint32_t codepoint = 0;
				std::string line;
				while( codepoint != newline && it != text.end() )
				{
					codepoint = utf8::next(it,text.end());
					if( codepoint != newline )
						utf8::append(codepoint, back_inserter(line));
				}
				//std::cout << "Line:" << line << std::endl;
				SDL_Surface* text_surface = renderString(line);
				if (text_surface->w > render_width) {
					render_width = text_surface->w;
				}
				lines.push_back(text_surface);
			} while (it != text.end());
			
			render_height = (getRowSpacing() + getHeight()) * lines.size();
			SDL_Surface* final_surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
				render_width,render_height,32,
				RMASK, GMASK, BMASK ,AMASK);
			if (!final_surface) {
				throw SDLException(std::string("CreateRGBSurface failed: ") + SDL_GetError());
			}
			SDL_FillRect(final_surface, 0, 0x00000000);
			int ypos = 0;
			for (std::vector<SDL_Surface*>::iterator i = lines.begin(); i != lines.end(); ++i) {
				SDL_Rect dst_rect = { 0, 0, 0, 0 };
				dst_rect.y = ypos;

				SDL_SetAlpha(*i,0,SDL_ALPHA_OPAQUE);
				SDL_BlitSurface(*i,0,final_surface,&dst_rect);
				ypos += getRowSpacing() + getHeight();
				SDL_FreeSurface(*i);
			}
			image = RenderBackend::instance()->createImage(final_surface);
			m_pool.addRenderedText(this, text, image);
		}
		return image;
	}

	std::string FontBase::splitTextToWidth (const std::string& text, int render_width) {
		const uint32_t whitespace = ' ';
		const uint8_t newline_utf8 = '\n';
		uint32_t newline;
		utf8::utf8to32(&newline_utf8,&newline_utf8 + 1,&newline);
		if (render_width <= 0 || text.empty()) { 
			return text;
		}
		std::string output;
		std::string line;
		std::string::const_iterator pos = text.begin();
		std::list<std::pair<size_t,std::string::const_iterator> > break_pos;
		bool firstLine = true;

		while( pos != text.end() )
		{
			break_pos.clear();
			if( !firstLine ) {
				line =  "\n";
			} else {
				firstLine = false;
			}

			bool haveNewLine = false;
			while( getWidth(line) < render_width && pos != text.end() )
			{
				uint32_t codepoint = utf8::next(pos, text.end());
				if (codepoint == whitespace && !line.empty())
					break_pos.push_back( std::make_pair(line.length(),pos) );

				if( codepoint != newline )
					utf8::append(codepoint, back_inserter(line) );

				// Special case: Already newlines in string:
				if( codepoint == newline ) {
					output.append(line);
					line = "";
					haveNewLine = true;
					break;
				}
			}
			if( haveNewLine )
				continue;

			if( pos == text.end() )
				break;

			if( break_pos.empty() ) {
				// No break position and line length smaller than 2
				// means the renderwidth is really screwed. Just continue
				// appending single character lines.
				if( utf8::distance(line.begin(),line.end()) <= 1 ) {
					output.append(line);
					continue;
				}

				// We can't do hyphenation here,
				// so we just retreat one character :-(
				// FIXME
				//line = line.erase(line.length() - 1);
				//--pos;
			} else {
				line = line.substr(0,break_pos.back().first);
				pos = break_pos.back().second;
			}
			output.append(line);
		}
		if( !line.empty() ) {
			output.append(line);
		}
		return output;
	}
}