text.cpp

Go to the documentation of this file.
00001 
00002 //            Copyright (C) 2004-2007 by The Allacrost Project
00003 //                         All Rights Reserved
00004 //
00005 // This code is licensed under the GNU GPL version 2. It is free software 
00006 // and you may modify it and/or redistribute it under the terms of this license.
00007 // See http://www.gnu.org/copyleft/gpl.html for details.
00009 
00019 #include <cassert>
00020 #include <cstdarg>
00021 #include <math.h>
00022 #include "utils.h"
00023 #include "video.h"
00024 #include "gui.h"
00025 
00026 using namespace std;
00027 using namespace hoa_utils;
00028 using namespace hoa_video::private_video;
00029 
00030 namespace hoa_video {
00031 
00032 bool GameVideo::LoadFont(const string &filename, const string &name, uint32 size) {
00033   // Return true if the font is already loaded
00034   if (_font_map.find(filename) != _font_map.end()) {
00035     return true;
00036   }
00037 
00038   // Attempt to load the font
00039   TTF_Font *font = TTF_OpenFont(filename.c_str(), size);
00040   
00041   if (!font) {
00042     if (VIDEO_DEBUG)
00043       cerr << "VIDEO ERROR: TTF_OpenFont() failed for font file: " << filename.c_str() << endl;
00044     return false;
00045   }
00046 
00047   // Create a new FontProperties object for the font, and add it to the font map
00048   FontProperties *fp = new FontProperties;
00049   _font_map[name] = fp;
00050 
00051   // Set all of the font's properties
00052   fp->ttf_font = font;
00053   fp->height = TTF_FontHeight(font);
00054   fp->line_skip = TTF_FontLineSkip(font);
00055   fp->ascent = TTF_FontAscent(font);
00056   fp->descent = TTF_FontDescent(font);
00057 
00058   // Set default shadow: x to be 1/8th of the font's height (or 1 pixel), y to be -x
00059   fp->shadow_x = max(fp->height / 8, 1);
00060   fp->shadow_y = -fp->shadow_x;
00061 
00062   // Set default shadow style and create the glyph cache
00063   fp->shadow_style = VIDEO_TEXT_SHADOW_DARK;
00064   fp->glyph_cache = new std::map<uint16, FontGlyph*>;
00065     
00066   return true;
00067 } // bool GameVideo::LoadFont(const string &filename, const string &name, int32 size)
00068 
00069 
00070 
00071 FontProperties* GameVideo::GetFontProperties(const std::string &font_name) {
00072   if (!IsValidFont(font_name)) {
00073     if (VIDEO_DEBUG)
00074       cerr << "VIDEO ERROR: GetFontProperties() failed becase an invalid font name was passed: " << font_name << endl;
00075     return NULL;
00076   }
00077   
00078   return _font_map[font_name];
00079 }
00080 
00081 
00082 
00083 bool GameVideo::SetFont(const std::string &name) {
00084   // check if font is loaded before setting it
00085   if ( _font_map.find(name) == _font_map.end())
00086     return false;
00087     
00088   _current_font = name;
00089   return true;
00090 }
00091 
00092 
00093 //-----------------------------------------------------------------------------
00094 // GetFont: returns the name of the current font (e.g. "verdana18")
00095 //-----------------------------------------------------------------------------
00096 
00097 std::string GameVideo::GetFont() const
00098 {
00099   return _current_font;
00100 }
00101 
00102 
00103 //-----------------------------------------------------------------------------
00104 // GetTextColor: returns the current text color
00105 //-----------------------------------------------------------------------------
00106 
00107 Color GameVideo::GetTextColor () const
00108 {
00109   return _current_text_color;
00110 }
00111 
00112 //-----------------------------------------------------------------------------
00113 // _GenTexLine: renders a text line to a texture
00114 //-----------------------------------------------------------------------------
00115 RenderedLine *GameVideo::_GenTexLine(uint16 *line, FontProperties *fp)
00116 {
00117   if (!fp)
00118     return NULL;
00119 
00120   // Array is { texid, shadowTexid }
00121   GLuint texid[2] = { 0, 0 };
00122 
00123   if (!*line)
00124     return new RenderedLine(texid, 0, 0, 0, 0, 0, 0);
00125   
00126   static const SDL_Color color = { 0xFF, 0xFF, 0xFF, 0xFF };
00127 
00128   TTF_Font * font = fp->ttf_font;
00129 
00130   SDL_Surface *initial      = NULL;
00131   SDL_Surface *intermediary = NULL;
00132   // If we are shadowing, use an array
00133   // to store shadow colored versions
00134   // of text pixels.
00135   uint8       *shadowPixels = NULL;
00136   int32 lineW,    lineH;
00137   int32 textureW, textureH;
00138 
00139   // Number of textures we're rendering
00140   uint8 numTextures = 0;
00141 
00142   // Minimum Y value of the line
00143   int32 minY = 0;
00144   // Calculated line width
00145   int32 calcLineWidth = 0;
00146 
00147   Color shadowColor;
00148 
00149   uint16 *charPtr;
00150 
00151   #if SDL_BYTEORDER == SDL_BIG_ENDIAN
00152     static const int rmask = 0xff000000;
00153     static const int gmask = 0x00ff0000;
00154     static const int bmask = 0x0000ff00;
00155     static const int amask = 0x000000ff;
00156   #else
00157     static const int rmask = 0x000000ff;
00158     static const int gmask = 0x0000ff00;
00159     static const int bmask = 0x00ff0000;
00160     static const int amask = 0xff000000;
00161   #endif  
00162 
00163   if (TTF_SizeUNICODE(font, line, &lineW, &lineH) == -1)
00164   {
00165     if(VIDEO_DEBUG)
00166       cerr << "VIDEO ERROR: TTF_SizeUNICODE() returned NULL in _GenTexLine()!" << endl;
00167     return NULL;
00168   }
00169 
00170   for (charPtr = line; *charPtr; ++charPtr)
00171   {
00172     FontGlyph * glyphinfo = (*fp->glyph_cache)[*charPtr];
00173     int curMinY = glyphinfo->top_y;
00174     if (curMinY < minY)
00175       minY = curMinY;
00176     calcLineWidth += glyphinfo->advance;
00177   }
00178 
00179   lineH -= minY;
00180   // TTF_SizeUNICODE underestimates line width as a 
00181   // result of its micro positioning
00182   if (calcLineWidth > lineW)
00183     lineW = calcLineWidth;
00184 
00185   textureW = RoundUpPow2(lineW + 1);
00186   textureH = RoundUpPow2(lineH + 1);
00187 
00188   intermediary = SDL_CreateRGBSurface(0, textureW, textureH, 32, 
00189       rmask, gmask, bmask, amask);
00190 
00191   if(!intermediary)
00192   {
00193     if(VIDEO_DEBUG)
00194       cerr << "VIDEO ERROR: SDL_CreateRGBSurface() returned NULL in _GenTexLine()!" << endl;
00195     return NULL;
00196   }
00197 
00198   SDL_Rect surfTarget;
00199   int xpos = 0;
00200   int ypos = -minY;
00201   for (charPtr = line; *charPtr; ++charPtr)
00202   {
00203     FontGlyph * glyphinfo = (*fp->glyph_cache)[*charPtr];
00204 
00205     initial = TTF_RenderGlyph_Blended(font, *charPtr, color);
00206 
00207     if(!initial)
00208     {
00209       if(VIDEO_DEBUG)
00210         cerr << "VIDEO ERROR: TTF_RenderGlyph_Blended() returned NULL in _GenTexLine()!" << endl;
00211       return NULL;
00212     }
00213 
00214     surfTarget.x = xpos + glyphinfo->min_x;
00215     surfTarget.y = ypos + glyphinfo->top_y;
00216 
00217     if(SDL_BlitSurface(initial, NULL, intermediary, &surfTarget) < 0)
00218     {
00219       SDL_FreeSurface(initial);
00220       SDL_FreeSurface(intermediary);
00221       if(VIDEO_DEBUG)
00222         cerr << "VIDEO ERROR: SDL_BlitSurface() failed in _GenTexLine()! (" << SDL_GetError() << ")" << endl;
00223       return NULL;
00224     }
00225     SDL_FreeSurface(initial);
00226     xpos += glyphinfo->advance;
00227   }
00228 
00229   if (_text_shadow)
00230   {
00231     shadowPixels = new uint8[intermediary->w * intermediary->h * 4];
00232     shadowColor  = _GetTextShadowColor(fp);
00233   }
00234 
00235   SDL_LockSurface(intermediary);
00236   for(int j = 0; j < intermediary->w * intermediary->h ; ++ j)
00237   {
00238     if (_text_shadow)
00239     {
00240       shadowPixels[j*4+3] = ((uint8*)intermediary->pixels)[j*4+2];
00241       shadowPixels[j*4+0] = (uint8) (shadowColor[0] * 0xFF);
00242       shadowPixels[j*4+1] = (uint8) (shadowColor[1] * 0xFF);
00243       shadowPixels[j*4+2] = (uint8) (shadowColor[2] * 0xFF);
00244     }
00245     ((uint8*)intermediary->pixels)[j*4+3] = ((uint8*)intermediary->pixels)[j*4+2];
00246     ((uint8*)intermediary->pixels)[j*4+0] = (uint8) (_current_text_color[0] * 0xFF);
00247     ((uint8*)intermediary->pixels)[j*4+1] = (uint8) (_current_text_color[1] * 0xFF);
00248     ((uint8*)intermediary->pixels)[j*4+2] = (uint8) (_current_text_color[2] * 0xFF);
00249   }
00250 
00251   numTextures = _text_shadow ? 2 : 1;
00252 
00253   glGenTextures(numTextures, texid);
00254 
00255   uint8 *texturePointers[2] =
00256   { 
00257     (uint8 *)intermediary->pixels, 
00258     shadowPixels 
00259   };
00260 
00261   for (int j = 0; j < numTextures; ++j)
00262   {
00263     _BindTexture(texid[j]);
00264 
00265     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
00266     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
00267 
00268     glTexImage2D(GL_TEXTURE_2D, 0, 4, textureW, textureH, 0, GL_RGBA, 
00269            GL_UNSIGNED_BYTE, texturePointers[j] );
00270 
00271     GLenum err;
00272     if((err = glGetError()) != 0)
00273     {
00274             if(VIDEO_DEBUG)
00275              cerr << "VIDEO ERROR: _GenTexLine: glError found after glTexImage2D (" << gluErrorString(err) << ")" << endl;
00276 
00277       SDL_FreeSurface(intermediary);
00278 
00279       if (shadowPixels)
00280         delete shadowPixels;
00281 
00282             return NULL;
00283     }
00284 
00285   }
00286 
00287   SDL_UnlockSurface(intermediary);
00288   SDL_FreeSurface(intermediary);
00289 
00290   if (shadowPixels)
00291     delete shadowPixels;
00292 
00293   return new RenderedLine(texid, lineW, textureW, lineH, textureH, 0, minY);
00294 }
00295 
00296 //-----------------------------------------------------------------------------
00297 // _CacheGlyphs: renders unicode characters to the internal glyph cache
00298 //-----------------------------------------------------------------------------
00299 
00300 bool GameVideo::_CacheGlyphs
00301 (
00302   const uint16 *uText,
00303   FontProperties *fp 
00304 )
00305 {
00306   if (!fp)
00307     return false;
00308 
00309   static const SDL_Color color = { 0xFF, 0xFF, 0xFF, 0xFF };
00310   static const uint16 fallbackglyph = '?'; // fall back to this glyph if one does not exist
00311 
00312   TTF_Font * font = fp->ttf_font;
00313   
00314   SDL_Surface *initial = NULL;
00315   SDL_Surface *intermediary = NULL;
00316   int32 w,h;
00317   GLuint texture;
00318 
00319   for(const uint16 * character_ptr = uText; *character_ptr != 0; ++character_ptr)
00320   {
00321     // Reference for legibility
00322     const uint16 &character = *character_ptr;
00323 
00324     // Check if glyph already cached
00325     if(fp->glyph_cache->find(character) != fp->glyph_cache->end())
00326       continue;
00327 
00328     initial = TTF_RenderGlyph_Blended(font, character, color);
00329     
00330     if(!initial)
00331     {
00332       if(VIDEO_DEBUG)
00333         cerr << "VIDEO ERROR: TTF_RenderUNICODE_Blended() returned NULL in CacheGlyphs(), resorting to fallback" << endl;
00334       initial = TTF_RenderGlyph_Blended(font, fallbackglyph, color);
00335       if (!fallbackglyph)
00336       {
00337         if (VIDEO_DEBUG)
00338           cerr << "VIDEO ERROR: TTF_RenderUNICODE_Blended() could not render fallback glyph, aborting!" << endl;
00339         return false;
00340       }
00341       // TEMP
00342       //cerr << "VIDEO ERROR (Probably a problem from SDL_ttf): " << TTF_GetError() << endl;
00343       //return false;
00344     }
00345 
00346     w = RoundUpPow2(initial->w + 1);
00347     h = RoundUpPow2(initial->h + 1);  
00348 
00349     uint32 rmask, gmask, bmask, amask;
00350 
00351       // SDL interprets each pixel as a 32-bit number, so our masks must depend
00352       // on the endianness (byte order) of the machine
00353     #if SDL_BYTEORDER == SDL_BIG_ENDIAN
00354       rmask = 0xff000000;
00355       gmask = 0x00ff0000;
00356       bmask = 0x0000ff00;
00357       amask = 0x000000ff;
00358     #else
00359       rmask = 0x000000ff;
00360       gmask = 0x0000ff00;
00361       bmask = 0x00ff0000;
00362       amask = 0xff000000;
00363     #endif  
00364 
00365     intermediary = SDL_CreateRGBSurface(0, w, h, 32, 
00366         rmask, gmask, bmask, amask);
00367 
00368     if(!intermediary)
00369     {
00370       SDL_FreeSurface(initial);
00371       SDL_FreeSurface(intermediary);
00372       if(VIDEO_DEBUG)
00373         cerr << "VIDEO ERROR: SDL_CreateRGBSurface() returned NULL in CacheGlyphs()!" << endl;
00374       return false;
00375     }
00376 
00377 
00378     if(SDL_BlitSurface(initial, 0, intermediary, 0) < 0)
00379     {
00380       SDL_FreeSurface(initial);
00381       SDL_FreeSurface(intermediary);
00382       if(VIDEO_DEBUG)
00383         cerr << "VIDEO ERROR: SDL_BlitSurface() failed in CacheGlyphs()!" << endl;
00384       return false;
00385     }
00386 
00387     glGenTextures(1, &texture);
00388     if(glGetError())
00389     {
00390       SDL_FreeSurface(initial);
00391       SDL_FreeSurface(intermediary);
00392       if(VIDEO_DEBUG)
00393         cerr << "VIDEO ERROR: glGetError() true after glGenTextures() in CacheGlyphs()!" << endl;
00394       return false;
00395     }
00396     
00397     _BindTexture(texture);
00398     if(glGetError())
00399     {
00400       SDL_FreeSurface(initial);
00401       SDL_FreeSurface(intermediary);
00402       if(VIDEO_DEBUG)
00403         cerr << "VIDEO ERROR: glGetError() true after glBindTexture() in CacheGlyphs()!" << endl;
00404       return false;
00405     }
00406 
00407     SDL_LockSurface(intermediary);
00408     for(int j = 0; j < w * h ; ++ j)
00409     {
00410       ((uint8*)intermediary->pixels)[j*4+3] = ((uint8*)intermediary->pixels)[j*4+2];
00411       
00412       ((uint8*)intermediary->pixels)[j*4+0] = 
00413       ((uint8*)intermediary->pixels)[j*4+1] = 
00414       ((uint8*)intermediary->pixels)[j*4+2] = 0xff;
00415     }
00416 
00417     glTexImage2D(GL_TEXTURE_2D, 0, 4, w, h, 0, GL_RGBA, 
00418            GL_UNSIGNED_BYTE, intermediary->pixels );
00419     SDL_UnlockSurface(intermediary);
00420 
00421     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
00422     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
00423 
00424 
00425     if(glGetError())
00426     {
00427       SDL_FreeSurface(initial);
00428       SDL_FreeSurface(intermediary);
00429       if(VIDEO_DEBUG)
00430         cerr << "VIDEO ERROR: glGetError() true after glTexImage2D() in CacheGlyphs()!" << endl;
00431       return false;
00432     }
00433 
00434     int minx, maxx;
00435     int miny, maxy;
00436     int advance;
00437 
00438     if(TTF_GlyphMetrics(font, character, &minx, &maxx, &miny, &maxy, &advance))
00439     {
00440       SDL_FreeSurface(initial);
00441       SDL_FreeSurface(intermediary);
00442       if(VIDEO_DEBUG)
00443         cerr << "VIDEO ERROR: glGetError() true after glTexImage2D() in CacheGlyphs()!" << endl;
00444       return false;
00445     }
00446 
00447     FontGlyph * glyph = new FontGlyph;
00448     glyph->texture = texture;
00449     glyph->min_x = minx;
00450     glyph->min_y = miny;
00451     glyph->top_y = fp->ascent - maxy;
00452     glyph->width = initial->w + 1;
00453     glyph->height = initial->h + 1;
00454     glyph->max_x = (float)(((double)initial->w + 1) / ((double)w));
00455     glyph->max_y = (float)(((double)initial->h + 1) / ((double)h));
00456     glyph->advance = advance;
00457 
00458     fp->glyph_cache->insert(std::pair<uint16, FontGlyph *>(character, glyph));
00459 
00460     SDL_FreeSurface(initial);
00461     SDL_FreeSurface(intermediary);
00462   }
00463   return true;
00464 } // GameVideo::CacheGlyphs()
00465 
00466 //-----------------------------------------------------------------------------
00467 // _DrawTextHelper: since there are two DrawText functions (one for unicode and
00468 //                 one for non-unicode), this private function is used to
00469 //                 do all the work so that code doesn't have to be duplicated.
00470 //                 Either text or uText is valid string and the other is NULL.
00471 //-----------------------------------------------------------------------------
00472 
00473 bool GameVideo::_DrawTextHelper
00474 ( 
00475   const uint16 *uText
00476 )
00477 {
00478 
00479   if(_font_map.empty())
00480     return false;
00481     
00482   // empty string, do nothing
00483   if(*uText == 0)
00484     return true;
00485     
00486   if(_font_map.find(_current_font) == _font_map.end())
00487     return false;
00488   
00489   FontProperties * fp = _font_map[_current_font];
00490   
00491   glBlendFunc(GL_ONE, GL_ONE);
00492   glEnable(GL_BLEND);
00493 
00494   CoordSys &cs = _coord_sys;
00495 
00496   _CacheGlyphs(uText, fp);
00497 
00498   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00499 
00500   glEnable(GL_TEXTURE_2D);
00501   glDisable(GL_FOG);
00502 
00503   glEnable(GL_ALPHA_TEST);
00504   glAlphaFunc(GL_GREATER, 0.1f);
00505 
00506   glPushMatrix();
00507 
00508   int fontwidth;
00509   int fontheight;
00510   if(TTF_SizeUNICODE(fp->ttf_font, uText, &fontwidth, &fontheight) != 0)
00511   {
00512     if(VIDEO_DEBUG)
00513       cerr << "VIDEO ERROR: TTF_SizeUNICODE() returned NULL in _DrawTextHelper()!" << endl;
00514     return false;
00515   }
00516 
00517   float xoff = ((_x_align+1) * fontwidth) * .5f * -cs.GetHorizontalDirection();
00518   float yoff = ((_y_align+1) * fontheight) * .5f * -cs.GetVerticalDirection();
00519 
00520   MoveRelative(xoff, yoff);
00521 
00522   float modulation = _fader.GetFadeModulation();
00523   Color textColor = _current_text_color * modulation;
00524 
00525   int xpos = 0;
00526   
00527   GLfloat tex_coords[8];
00528   GLint vertices[8];
00529   
00530   glEnableClientState ( GL_VERTEX_ARRAY );
00531   glEnableClientState ( GL_TEXTURE_COORD_ARRAY );
00532   
00533   glVertexPointer ( 2, GL_INT, 0, vertices );
00534   glTexCoordPointer ( 2, GL_FLOAT, 0, tex_coords );
00535 
00536   for(const uint16 * glyph = uText; *glyph != 0; glyph++)
00537   {
00538     FontGlyph * glyphinfo = (*fp->glyph_cache)[*glyph];
00539     
00540     int xhi = glyphinfo->width; 
00541     int yhi = glyphinfo->height;
00542     
00543     if(cs.GetHorizontalDirection() < 0.0f)
00544       xhi = -xhi;
00545     if(cs.GetVerticalDirection() < 0.0f)
00546       yhi = -yhi;
00547       
00548     float tx, ty;
00549     tx = glyphinfo->max_x;
00550     ty = glyphinfo->max_y;
00551 
00552     int minx, miny;
00553     minx = glyphinfo->min_x * (int)cs.GetHorizontalDirection() + xpos;
00554     miny = glyphinfo->min_y * (int)cs.GetVerticalDirection();
00555     
00556     _BindTexture(glyphinfo->texture);
00557 
00558     if(glGetError())
00559     {
00560       if(VIDEO_DEBUG)
00561         cerr << "VIDEO ERROR: glGetError() true after 2nd call to glBindTexture() in _DrawTextHelper!" << endl;
00562       return false;
00563     }
00564     
00565     vertices[0] = minx;
00566     vertices[1] = miny;
00567     vertices[2] = minx + xhi;
00568     vertices[3] = miny;
00569     vertices[4] = minx + xhi;
00570     vertices[5] = miny + yhi;
00571     vertices[6] = minx;
00572     vertices[7] = miny + yhi;
00573     tex_coords[0] = 0.0f;
00574     tex_coords[1] = ty;
00575     tex_coords[2] = tx;
00576     tex_coords[3] = ty;
00577     tex_coords[4] = tx;
00578     tex_coords[5] = 0.0f;
00579     tex_coords[6] = 0.0f;
00580     tex_coords[7] = 0.0f;
00581     
00582     glColor4fv((GLfloat*)&textColor);
00583     glDrawArrays(GL_QUADS, 0, 4);
00584     /*glBegin(GL_QUADS);
00585     glColor4fv((GLfloat *)&textColor);
00586 
00587     glTexCoord2f(0.0, ty); 
00588     glVertex2i(minx, miny);
00589 
00590     glTexCoord2f(tx, ty); 
00591     glVertex2i(minx + xhi, miny);
00592 
00593     glTexCoord2f(tx, 0.0); 
00594     glVertex2i(minx + xhi, miny + yhi);
00595 
00596     glTexCoord2f(0.0, 0.0); 
00597     glVertex2i(minx, miny + yhi);
00598 
00599     glEnd();*/
00600 
00601     xpos += glyphinfo->advance;
00602   }
00603 
00604   glDisableClientState ( GL_VERTEX_ARRAY );
00605   glDisableClientState ( GL_TEXTURE_COORD_ARRAY );
00606 
00607   glPopMatrix();
00608   
00609   if(_fog_intensity > 0.0f)
00610     glEnable(GL_FOG);
00611 
00612   glDisable(GL_ALPHA_TEST);
00613 
00614   //glFinish();
00615 
00616   return true;
00617 }
00618 
00619 
00620 //-----------------------------------------------------------------------------
00621 // DrawText: non unicode version
00622 // Note: this simply converts to unicode and calls the unicode version.
00623 //       This performance penalty is acceptable because any text that the player
00624 //       sees will be unicode text anyways. Non-unicode is only used for
00625 //       debugging text.
00626 //-----------------------------------------------------------------------------
00627 
00628 bool GameVideo::DrawText(const string &txt)
00629 {
00630   ustring wstr = MakeUnicodeString(txt);
00631   return DrawText(wstr);
00632 }
00633 
00634 
00635 //-----------------------------------------------------------------------------
00636 // DrawText: unicode version
00637 //-----------------------------------------------------------------------------
00638 
00639 bool GameVideo::DrawText(const ustring &txt)
00640 {
00641   if(txt.empty())
00642   {
00643     // Previously, if an empty string was passed, it was considered an error
00644     // However, it happens often enough in practice that now we just return true
00645     // without doing anything.
00646     
00647     return true;
00648   }
00649 
00650   if(_font_map.find(_current_font) == _font_map.end())
00651   {
00652     if(VIDEO_DEBUG)
00653       cerr << "GameVideo::DrawText() failed because font passed was either not loaded or improperly loaded!\n" <<
00654               "  *fontname: " << _current_font << endl;
00655     return false;
00656   }
00657 
00658   FontProperties * fp = _font_map[_current_font];
00659   TTF_Font * font = fp->ttf_font;
00660   
00661   if(font)
00662   {
00663     _PushContext();
00664     int32 lineSkip = fp->line_skip;
00665     
00666     // Optimization: something seems to be wrong with ustring, using a buffer instead
00667     uint16 buffer[2048];
00668     uint16 newline('\n');
00669 
00670     size_t lastline = 0;
00671     
00672     do
00673     {
00674       size_t nextline;
00675       for(nextline = lastline; nextline < txt.length(); nextline++)
00676       {
00677         if(txt[nextline] == newline)
00678           break;
00679 
00680         buffer[nextline - lastline] = txt[nextline];
00681       }
00682       buffer[nextline - lastline] = 0;
00683       lastline = nextline + 1;
00684 
00685       glPushMatrix();
00686       
00687       Color oldTextColor = _current_text_color;
00688 
00689       // if text shadows are enabled, draw the shadow
00690       if(_text_shadow && fp->shadow_style != VIDEO_TEXT_SHADOW_NONE)
00691       {
00692         Color textColor;
00693         
00694         switch(fp->shadow_style)
00695         {
00696           case VIDEO_TEXT_SHADOW_DARK:
00697             textColor = Color::black;
00698             textColor[3] = oldTextColor[3] * 0.5f;
00699             break;
00700           case VIDEO_TEXT_SHADOW_LIGHT:
00701             textColor = Color::white;
00702             textColor[3] = oldTextColor[3] * 0.5f;
00703             break;
00704           case VIDEO_TEXT_SHADOW_BLACK:
00705             textColor = Color::black;
00706             textColor[3] = oldTextColor[3];
00707             break;
00708           case VIDEO_TEXT_SHADOW_COLOR:
00709             textColor = oldTextColor;
00710             textColor[3] = oldTextColor[3] * 0.5f;
00711             break;
00712           case VIDEO_TEXT_SHADOW_INVCOLOR:
00713             textColor = Color(1.0f - oldTextColor[0], 1.0f - oldTextColor[1], 1.0f - oldTextColor[2], oldTextColor[3] * 0.5f);
00714             break;
00715           default:
00716           {
00717             if(VIDEO_DEBUG)
00718               cerr << "VIDEO ERROR: Unknown text shadow style (" << fp->shadow_style << ") found in GameVideo::DrawText()!" << endl;
00719             break;
00720           }
00721         };
00722         SetTextColor(textColor);
00723         
00724         
00725         glPushMatrix();
00726         MoveRelative(+_coord_sys.GetHorizontalDirection() * fp->shadow_x, 0.0f);
00727         MoveRelative(0.0f, _coord_sys.GetVerticalDirection() * fp->shadow_y);
00728         
00729         if(!_DrawTextHelper(buffer))
00730         {
00731           _PopContext();
00732           return false;
00733         }
00734         glPopMatrix();
00735       }
00736 
00737       SetTextColor(oldTextColor);
00738 
00739       // draw the text itself
00740       if(!_DrawTextHelper(buffer))
00741       {
00742         _PopContext();
00743         return false;
00744       }
00745       
00746       glPopMatrix();
00747       
00748       MoveRelative(0, -lineSkip * _coord_sys.GetVerticalDirection());
00749 
00750     } while(lastline < txt.length());
00751     
00752     _PopContext();
00753   }
00754     
00755   return true;
00756 }
00757 
00758 //-----------------------------------------------------------------------------
00759 // RenderText: Renders the given unicode string.
00760 //-----------------------------------------------------------------------------
00761 RenderedString *GameVideo::RenderText(const ustring &txt)
00762 {
00763   if(txt.empty())
00764   {
00765     // Previously, if an empty string was passed, it was considered an error
00766     // However, it happens often enough in practice that now we just return true
00767     // without doing anything.
00768     
00769     return NULL;
00770   }
00771 
00772   if(_font_map.find(_current_font) == _font_map.end())
00773   {
00774     if(VIDEO_DEBUG)
00775       cerr << "GameVideo::DrawText() failed because font passed was either not loaded or improperly loaded!\n" <<
00776         "  *fontname: " << _current_font << endl;
00777     return NULL;
00778   }
00779 
00780   FontProperties * fp = _font_map[_current_font];
00781   TTF_Font * font = fp->ttf_font;
00782   
00783   if(!font)
00784   {
00785     return NULL;
00786   }
00787 
00788   uint16 newline('\n');
00789   int32 lineSkip = fp->line_skip;
00790   std::vector<uint16 *> lineArray;
00791 
00792   _CacheGlyphs(txt.c_str(), fp);
00793 
00794   const uint16 *charIter;
00795   // Set lineStart value to one additional to total height of line
00796   // a reasonable maximum value.
00797   uint16 *reformattedText = new uint16[txt.size() + 1];
00798   uint16 *reformIter = reformattedText;
00799   uint16 *lastLine = reformattedText;
00800   for (charIter = txt.c_str(); *charIter; ++charIter)
00801   {
00802     if (*charIter == newline)
00803     {
00804       *reformIter++ = '\0';
00805       lineArray.push_back(lastLine);
00806       lastLine = reformIter;
00807     }
00808     else
00809     {
00810       *reformIter++ = *charIter;
00811     }
00812   }
00813   lineArray.push_back(lastLine);
00814   *reformIter = '\0';
00815 
00816   std::vector<uint16 *>::iterator lineIter;
00817 
00818 
00819   Color oldColor = _current_text_color;
00820   int32 shadowOffsetX = 0;
00821   int32 shadowOffsetY = 0;
00822   // if text shadows are enabled, draw the shadow
00823   if(_text_shadow && fp->shadow_style != VIDEO_TEXT_SHADOW_NONE)
00824   {
00825     shadowOffsetX = static_cast<int32>(_coord_sys.GetHorizontalDirection()) * fp->shadow_x;
00826     shadowOffsetY = static_cast<int32>(_coord_sys.GetVerticalDirection()) * fp->shadow_y;
00827   }
00828 
00829   RenderedString *rendStr = new RenderedString(lineSkip, shadowOffsetX, shadowOffsetY);
00830 
00831   for (lineIter = lineArray.begin(); lineIter != lineArray.end(); ++lineIter)
00832   {
00833     RenderedLine *line = NULL;
00834     if ((line = _GenTexLine(*lineIter, fp)) == NULL)
00835     {
00836       if(VIDEO_DEBUG)
00837         cerr << "Failed to generate line texture for " << MakeStandardString(ustring(*lineIter)) << "." << endl;
00838       delete rendStr;
00839       return NULL;
00840     }
00841     rendStr->Add(line);
00842   }
00843   delete[] reformattedText;
00844     
00845   return rendStr;
00846 }
00847 
00848 
00849 //-----------------------------------------------------------------------------
00850 // CalculateTextWidth: return the width of the given text using the given font
00851 //-----------------------------------------------------------------------------
00852 
00853 int32 GameVideo::CalculateTextWidth(const std::string &fontName, const hoa_utils::ustring &text)
00854 {
00855   if(!IsValidFont(fontName))
00856     return -1;
00857     
00858   int32 w;  
00859   if(-1 == TTF_SizeUNICODE(_font_map[fontName]->ttf_font, text.c_str(), &w, NULL))
00860     return -1;
00861     
00862   return w;
00863 }
00864 
00865 
00866 //-----------------------------------------------------------------------------
00867 // CalculateTextWidth: non-unicode version
00868 //-----------------------------------------------------------------------------
00869 
00870 int32 GameVideo::CalculateTextWidth(const std::string &fontName, const std::string  &text)
00871 {
00872   if(!IsValidFont(fontName))
00873     return -1;
00874 
00875   int32 w;  
00876   if(-1 == TTF_SizeText(_font_map[fontName]->ttf_font, text.c_str(), &w, NULL))
00877     return -1;
00878     
00879   return w;
00880 }
00881 
00882 
00883 //-----------------------------------------------------------------------------
00884 // EnableTextShadow: enable/disable text shadow effect
00885 //-----------------------------------------------------------------------------
00886 
00887 void GameVideo::EnableTextShadow(bool enable)
00888 {
00889   _text_shadow = enable;
00890 }
00891 
00892 
00893 //-----------------------------------------------------------------------------
00894 // SetFontShadowXOffset: sets x offset to use for font shadow
00895 //-----------------------------------------------------------------------------
00896 
00897 bool GameVideo::SetFontShadowXOffset(const std::string &fontName, int32 x)
00898 {
00899   if(_font_map.find(fontName) == _font_map.end())
00900   {
00901     if(VIDEO_DEBUG)
00902       cerr << "VIDEO ERROR: GameVideo::SetFontShadowXOffset() failed for font (" << fontName << ") because the font's properties could not be found!" << endl;
00903     return false;
00904   }
00905   
00906   FontProperties *fp = _font_map[fontName];
00907   
00908   if(!fp)
00909   {
00910     if(VIDEO_DEBUG)
00911       cerr << "VIDEO ERROR: GameVideo::SetFontShadowXOffset() failed for font (" << fontName << ") because the FontProperties pointer was NULL!" << endl;
00912     return false;
00913   }
00914 
00915   fp->shadow_x = x;
00916   return true;
00917 }
00918 
00919 
00920 //-----------------------------------------------------------------------------
00921 // SetFontShadowYOffset: sets y offset to use for font shadow
00922 //-----------------------------------------------------------------------------
00923 
00924 bool GameVideo::SetFontShadowYOffset(const std::string &fontName, int32 y)
00925 {
00926   if(_font_map.find(fontName) == _font_map.end())
00927   {
00928     if(VIDEO_DEBUG)
00929       cerr << "VIDEO ERROR: GameVideo::SetFontShadowYOffset() failed for font (" << fontName << ") because the font's properties could not be found!" << endl;
00930     return false;
00931   }
00932 
00933   FontProperties *fp = _font_map[fontName];
00934   
00935   if(!fp)
00936   {
00937     if(VIDEO_DEBUG)
00938       cerr << "VIDEO ERROR: GameVideo::SetFontShadowYOffset() failed for font (" << fontName << ") because the FontProperties pointer was NULL!" << endl;
00939     return false;
00940   }
00941   
00942   fp->shadow_y = y;
00943   return true;
00944 }
00945 
00946 
00947 //-----------------------------------------------------------------------------
00948 // SetFontShadowStyle: sets the shadow style for the given font
00949 //-----------------------------------------------------------------------------
00950 
00951 bool GameVideo::SetFontShadowStyle(const std::string &fontName, TEXT_SHADOW_STYLE style)
00952 {
00953   if(_font_map.find(fontName) == _font_map.end())
00954   {
00955     if(VIDEO_DEBUG)
00956       cerr << "VIDEO ERROR: GameVideo::SetFontShadowYOffset() failed for font (" << fontName << ") because the font's properties could not be found!" << endl;
00957     return false;
00958   }
00959 
00960   FontProperties *fp = _font_map[fontName];
00961   
00962   if(!fp)
00963   {
00964     if(VIDEO_DEBUG)
00965       cerr << "VIDEO ERROR: GameVideo::SetFontShadowYOffset() failed for font (" << fontName << ") because the FontProperties pointer was NULL!" << endl;
00966     return false;
00967   }
00968   
00969   fp->shadow_style = style;
00970   return true;
00971 }
00972 
00973 //-----------------------------------------------------------------------------
00974 // _GetTextShadowColor: gets the current text shadow color
00975 //-----------------------------------------------------------------------------
00976 Color GameVideo::_GetTextShadowColor(FontProperties *fp)
00977 {
00978   Color shadowColor;
00979   if(_text_shadow && fp->shadow_style != VIDEO_TEXT_SHADOW_NONE)
00980   {
00981     switch(fp->shadow_style)
00982     {
00983       case VIDEO_TEXT_SHADOW_DARK:
00984         shadowColor = Color::black;
00985         shadowColor[3] = _current_text_color[3] * 0.5f;
00986         break;
00987       case VIDEO_TEXT_SHADOW_LIGHT:
00988         shadowColor = Color::white;
00989         shadowColor[3] = _current_text_color[3] * 0.5f;
00990         break;
00991       case VIDEO_TEXT_SHADOW_BLACK:
00992         shadowColor = Color::black;
00993         shadowColor[3] = _current_text_color[3];
00994         break;
00995       case VIDEO_TEXT_SHADOW_COLOR:
00996         shadowColor = _current_text_color;
00997         shadowColor[3] = _current_text_color[3] * 0.5f;
00998         break;
00999       case VIDEO_TEXT_SHADOW_INVCOLOR:
01000         shadowColor = Color(1.0f - _current_text_color[0], 1.0f - _current_text_color[1], 1.0f - _current_text_color[2], _current_text_color[3] * 0.5f);
01001         break;
01002       default:
01003       {
01004         if(VIDEO_DEBUG)
01005           cerr << "VIDEO ERROR: Unknown text shadow style (" << fp->shadow_style << ") -  GameVideo::_GetTextShadowColor()" << endl;
01006         break;
01007       }
01008     }
01009   }
01010   return shadowColor;
01011 }
01012 
01013 //-----------------------------------------------------------------------------
01014 // ~RenderedLine: deletes the line
01015 //-----------------------------------------------------------------------------
01016 
01017 RenderedLine::~RenderedLine()
01018 {
01019 }
01020 
01021 //-----------------------------------------------------------------------------
01022 // RenderedLine: constructs a new rendered line
01023 //-----------------------------------------------------------------------------
01024 
01025 RenderedLine::RenderedLine(GLuint *tex, int32 lineWidth, int32 texWidth, int32 lineHeight, int32 texHeight, int32 xOffset, int32 yOffset)
01026   : height(lineHeight), width(lineWidth),
01027     x_offset(xOffset),  y_offset(yOffset)
01028 {
01029   u = ((float)lineWidth  + 1.0f) / (float)texWidth;
01030   v = ((float)lineHeight + 1.0f) / (float)texHeight;
01031   if (tex)
01032     memcpy(texture, tex, sizeof(GLuint) * NUM_TEXTURES);
01033   else
01034     memset(texture, 0,   sizeof(GLuint) * NUM_TEXTURES);
01035 }
01036 
01037 //-----------------------------------------------------------------------------
01038 // RenderedString::Draw: draws a rendered string
01039 //-----------------------------------------------------------------------------
01040 bool RenderedString::Draw() const
01041 {
01042   return VideoManager->Draw(*this);
01043 }
01044 
01045 
01046 //-----------------------------------------------------------------------------
01047 // RenderedLine::Draw: draws a rendered lineline
01048 //-----------------------------------------------------------------------------
01049 bool GameVideo::Draw(const RenderedLine &line, int32 texIndex)
01050 {
01051   if (texIndex >= RenderedLine::NUM_TEXTURES
01052   ||  texIndex < 0)
01053     return false;
01054 
01055   // Empty strings are 0 texture ids
01056   if (!line.texture[texIndex])
01057     return false;
01058 
01059   if (!_BindTexture(line.texture[texIndex]))
01060   {
01061     if ( VIDEO_DEBUG )
01062     {
01063       cerr << "Failed to bind texture for line draw." << endl;
01064     }
01065     return false;
01066   }
01067 
01068   float halfW = line.width  * 0.5f;
01069   float halfH = line.height * 0.5f;
01070 
01071   glBegin(GL_QUADS);
01072 
01073   glTexCoord2f(0.0f, line.v); 
01074   glVertex2f(- halfW, - halfH);
01075 
01076   glTexCoord2f(line.u, line.v); 
01077   glVertex2f(+ halfW, - halfH);
01078 
01079   glTexCoord2f(line.u, 0.0f); 
01080   glVertex2f(+ halfW, + halfH);
01081 
01082   glTexCoord2f(0.0f, 0.0f); 
01083   glVertex2f(- halfW, + halfH);
01084 
01085   glEnd();
01086 
01087   return true;
01088 }
01089 
01090 
01091 //-----------------------------------------------------------------------------
01092 // RenderedString::Add: adds a line to a rendered string
01093 //-----------------------------------------------------------------------------
01094 bool RenderedString::Add(RenderedLine *str)
01095 {
01096   lines.push_back(str);
01097   if (str->width > _width)
01098     _width = str->width;
01099   return true;
01100 }
01101 
01102 //-----------------------------------------------------------------------------
01103 // RenderedString: constructs a new empty rendered string
01104 //-----------------------------------------------------------------------------
01105 RenderedString::RenderedString(int32 line_skip, int32 shadowX, int32 shadowY)
01106   : _width(0), _line_skip(line_skip),
01107     _shadow_xoff(shadowX), _shadow_yoff(shadowY)
01108   {}
01109 
01110 //-----------------------------------------------------------------------------
01111 // ~RenderedString: deletes all contained lines
01112 //-----------------------------------------------------------------------------
01113 RenderedString::~RenderedString()
01114 {
01115   std::vector<RenderedLine *>::iterator line;
01116   for (line = lines.begin(); line != lines.end(); ++line)
01117   {
01118     VideoManager->_DeleteTexture((*line)->texture[RenderedLine::MAIN_TEXTURE]);
01119     VideoManager->_DeleteTexture((*line)->texture[RenderedLine::SHADOW_TEXTURE]);
01120     delete *line;
01121   }
01122 }
01123 
01124 //-----------------------------------------------------------------------------
01125 // Draw: draws a rendered string
01126 //-----------------------------------------------------------------------------
01127 bool GameVideo::Draw(const RenderedString &string)
01128 {
01129   glEnable(GL_BLEND);
01130   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
01131 
01132   glEnable(GL_TEXTURE_2D);
01133   glDisable(GL_FOG);
01134 
01135   glEnable(GL_ALPHA_TEST);
01136   glAlphaFunc(GL_GREATER, 0.1f);
01137 
01138   float xoff = ((_x_align) * string.GetWidth()) * .5f * -_coord_sys.GetHorizontalDirection();
01139 
01140   std::vector<RenderedLine*>::const_iterator it;
01141 
01142   glPushMatrix();
01143   MoveRelative(xoff, 0.0f);
01144   for (it = string.lines.begin(); it != string.lines.end(); ++it)
01145   {
01146     const RenderedLine &line = *(*it);
01147     if (line.texture[RenderedLine::SHADOW_TEXTURE])
01148     {
01149       glPushMatrix();
01150       MoveRelative(string.GetShadowX(), string.GetShadowY());
01151       Draw(line, RenderedLine::SHADOW_TEXTURE);
01152       glPopMatrix();
01153     }
01154     Draw(line, RenderedLine::MAIN_TEXTURE);
01155     MoveRelative(0.0f, -_coord_sys.GetVerticalDirection() * string.GetLineSkip());
01156   }
01157   glPopMatrix();
01158   return true;
01159 }
01160 
01161 }  // namespace hoa_video
01162 

Generated on Fri Jul 6 23:11:24 2007 for Hero of Allacrost by  doxygen 1.5.1