FIFE  2008.0
 All Classes Namespaces Functions Variables Enumerations Enumerator
fontbase.cpp
00001 /***************************************************************************
00002  *   Copyright (C) 2005-2008 by the FIFE team                              *
00003  *   http://www.fifengine.de                                               *
00004  *   This file is part of FIFE.                                            *
00005  *                                                                         *
00006  *   FIFE is free software; you can redistribute it and/or                 *
00007  *   modify it under the terms of the GNU Lesser General Public            *
00008  *   License as published by the Free Software Foundation; either          *
00009  *   version 2.1 of the License, or (at your option) any later version.    *
00010  *                                                                         *
00011  *   This library is distributed in the hope that it will be useful,       *
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
00014  *   Lesser General Public License for more details.                       *
00015  *                                                                         *
00016  *   You should have received a copy of the GNU Lesser General Public      *
00017  *   License along with this library; if not, write to the                 *
00018  *   Free Software Foundation, Inc.,                                       *
00019  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
00020  ***************************************************************************/
00021 
00022 // Standard C++ library includes
00023 #include <vector>
00024 
00025 // Platform specific includes
00026 
00027 // 3rd party library includes
00028 #include <boost/filesystem/convenience.hpp>
00029 
00030 // FIFE includes
00031 // These includes are split up in two parts, separated by one empty line
00032 // First block: files included from the FIFE root src directory
00033 // Second block: files included from the same folder
00034 #include "util/structures/rect.h"
00035 #include "util/base/exception.h"
00036 #include "util/utf8/utf8.h"
00037 #include "video/image.h"
00038 #include "video/renderbackend.h"
00039 
00040 #include "fontbase.h"
00041 
00042 namespace FIFE {
00043 
00044     FontBase::FontBase():
00045             m_pool(),
00046             mColor(),
00047             mGlyphSpacing(0),
00048             mRowSpacing(0),
00049             mFilename(""),
00050             m_antiAlias(true) {
00051     }
00052 
00053     void FontBase::invalidate() {
00054         m_pool.invalidateCachedText();
00055     }
00056 
00057     void FontBase::setRowSpacing(int spacing) {
00058         mRowSpacing = spacing;
00059     }
00060 
00061     int FontBase::getRowSpacing() const {
00062         return mRowSpacing;
00063     }
00064 
00065     void FontBase::setGlyphSpacing(int spacing) {
00066         mGlyphSpacing = spacing;
00067     }
00068 
00069     int FontBase::getGlyphSpacing() const {
00070         return mGlyphSpacing;
00071     }
00072 
00073     void FontBase::setAntiAlias(bool antiAlias) {
00074         m_antiAlias = antiAlias;
00075     }
00076 
00077     bool FontBase::isAntiAlias() {
00078         return m_antiAlias;
00079     }
00080 
00081     SDL_Color FontBase::getColor() const {
00082         return mColor;
00083     }
00084 
00085     int FontBase::getStringIndexAt(const std::string &text, int x) const {
00086         assert( utf8::is_valid(text.begin(), text.end()) );
00087         std::string::const_iterator cur;
00088         if (text.size() == 0) return 0;
00089         if (x <= 0) return 0;
00090 
00091         cur = text.begin();
00092 
00093         utf8::next(cur, text.end());
00094 
00095         std::string buff;
00096         while(cur != text.end()) {
00097             buff = std::string(text.begin(), cur);
00098 
00099             if (getWidth(buff) > x) {
00100                 return buff.size();
00101             } else {
00102                 utf8::next(cur, text.end());
00103             }
00104         }
00105 
00106         if (x > getWidth(text)) {
00107             return text.size();
00108         } else {
00109             return buff.size();
00110         }
00111     }
00112 
00113     Image* FontBase::getAsImage(const std::string& text) {
00114         Image* image = m_pool.getRenderedText(this, text);
00115         if (!image) {
00116             SDL_Surface* textSurface = renderString(text);
00117             image = RenderBackend::instance()->createImage(textSurface);
00118             m_pool.addRenderedText( this, text, image );
00119         }
00120         return image;
00121     }
00122 
00123     Image* FontBase::getAsImageMultiline(const std::string& text) {
00124         const uint8_t newline_utf8 = '\n';
00125         uint32_t newline;
00126         utf8::utf8to32(&newline_utf8,&newline_utf8 + 1,&newline);
00127         //std::cout << "Text:" << text << std::endl;
00128         Image* image = m_pool.getRenderedText(this, text);
00129         if (!image) {
00130             std::vector<SDL_Surface*> lines;
00131             std::string::const_iterator it = text.begin();
00132             // split text as needed
00133             int render_width = 0, render_height = 0;
00134             do {
00135                 uint32_t codepoint = 0;
00136                 std::string line;
00137                 while( codepoint != newline && it != text.end() )
00138                 {
00139                     codepoint = utf8::next(it,text.end());
00140                     if( codepoint != newline )
00141                         utf8::append(codepoint, back_inserter(line));
00142                 }
00143                 //std::cout << "Line:" << line << std::endl;
00144                 SDL_Surface* text_surface = renderString(line);
00145                 if (text_surface->w > render_width) {
00146                     render_width = text_surface->w;
00147                 }
00148                 lines.push_back(text_surface);
00149             } while (it != text.end());
00150 
00151             render_height = (getRowSpacing() + getHeight()) * lines.size();
00152             SDL_Surface* final_surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
00153                 render_width,render_height,32,
00154                 RMASK, GMASK, BMASK ,AMASK);
00155             if (!final_surface) {
00156                 throw SDLException(std::string("CreateRGBSurface failed: ") + SDL_GetError());
00157             }
00158             SDL_FillRect(final_surface, 0, 0x00000000);
00159             int ypos = 0;
00160             for (std::vector<SDL_Surface*>::iterator i = lines.begin(); i != lines.end(); ++i) {
00161                 SDL_Rect dst_rect = { 0, 0, 0, 0 };
00162                 dst_rect.y = ypos;
00163 
00164                 SDL_SetAlpha(*i,0,SDL_ALPHA_OPAQUE);
00165                 SDL_BlitSurface(*i,0,final_surface,&dst_rect);
00166                 ypos += getRowSpacing() + getHeight();
00167                 SDL_FreeSurface(*i);
00168             }
00169             image = RenderBackend::instance()->createImage(final_surface);
00170             m_pool.addRenderedText(this, text, image);
00171         }
00172         return image;
00173     }
00174 
00175     std::string FontBase::splitTextToWidth (const std::string& text, int render_width) {
00176         const uint32_t whitespace = ' ';
00177         const uint8_t newline_utf8 = '\n';
00178         uint32_t newline;
00179         utf8::utf8to32(&newline_utf8,&newline_utf8 + 1,&newline);
00180         if (render_width <= 0 || text.empty()) {
00181             return text;
00182         }
00183         std::string output;
00184         std::string line;
00185         std::string::const_iterator pos = text.begin();
00186         std::list<std::pair<size_t,std::string::const_iterator> > break_pos;
00187         bool firstLine = true;
00188 
00189         while( pos != text.end())
00190         {
00191             break_pos.clear();
00192             if( !firstLine ) {
00193                 line =  "\n";
00194             } else {
00195                 firstLine = false;
00196             }
00197 
00198             bool haveNewLine = false;
00199             while( getWidth(line) < render_width && pos != text.end() )
00200             {
00201                 uint32_t codepoint = utf8::next(pos, text.end());
00202                 if (codepoint == whitespace && !line.empty())
00203                     break_pos.push_back( std::make_pair(line.length(),pos) );
00204 
00205                 if( codepoint != newline )
00206                     utf8::append(codepoint, back_inserter(line) );
00207 
00208                 // Special case: Already newlines in string:
00209                 if( codepoint == newline ) {
00210                     output.append(line);
00211                     line = "";
00212                     haveNewLine = true;
00213                     break;
00214                 }
00215             }
00216             if( haveNewLine )
00217                 continue;
00218 
00219             if( pos == text.end() )
00220                 break;
00221 
00222             if( break_pos.empty() ) {
00223                 // No break position and line length smaller than 2
00224                 // means the renderwidth is really screwed. Just continue
00225                 // appending single character lines.
00226                 if( utf8::distance(line.begin(),line.end()) <= 1 && line != "\n") {
00227                     output.append(line);
00228                     continue;
00229                 }
00230 
00231                 if (line == "\n") {
00232                     ++pos;
00233                 }
00234 
00235                 // We can't do hyphenation here,
00236                 // so we just retreat one character :-(
00237                 // FIXME
00238                 //line = line.erase(line.length() - 1);
00239                 //--pos;
00240             } else {
00241                 line = line.substr(0,break_pos.back().first);
00242                 pos = break_pos.back().second;
00243             }
00244             output.append(line);
00245         }
00246         if( !line.empty() ) {
00247             output.append(line);
00248         }
00249         return output;
00250     }
00251 }