OSDLFont.cc

Go to the documentation of this file.
00001 #include "OSDLFont.h"
00002 
00003 #include "OSDLSurface.h"         // for Surface
00004 #include "OSDLPixel.h"           // for ColorDefinition
00005 
00006 #include "SDL_gfxPrimitives.h"   // for stringColor
00007 
00008 #include "SDL_ttf.h"             // for TTF_STYLE_NORMAL and others
00009 
00010 #include <list>
00011 
00012 
00013 #ifdef OSDL_USES_CONFIG_H
00014 #include <OSDLConfig.h>          // for OSDL_DEBUG_FONT and al 
00015 #endif // OSDL_USES_CONFIG_H
00016 
00017 
00018 using std::list ;
00019 using std::pair ;
00020 
00021 using std::string ;
00022 
00023 
00024 using namespace Ceylan ;
00025 using namespace Ceylan::Log ;
00026 
00027 using namespace OSDL::Video::TwoDimensional ;
00028 
00029 
00030 
00031 string Text::Font::FontPathEnvironmentVariable = "FONT_PATH" ;
00032 
00033 
00034 Ceylan::System::FileLocator Text::Font::FontFileLocator(
00035     FontPathEnvironmentVariable ) ;
00036 
00037 
00038 
00039 TextException::TextException( const string & reason ) throw() :
00040     VideoException( "TextException : " + reason ) 
00041 {
00042 
00043 }
00044     
00045     
00046 TextException::~TextException() throw()
00047 {
00048 
00049 }
00050 
00051 
00052 
00053 
00054 using namespace OSDL::Video::TwoDimensional::Text ;
00055 
00056 
00057 
00058 
00059 // Font section.
00060 
00061 
00062 const RenderingStyle Font::Normal    = TTF_STYLE_NORMAL    ;
00063 const RenderingStyle Font::Bold      = TTF_STYLE_BOLD      ;
00064 const RenderingStyle Font::Italic    = TTF_STYLE_ITALIC    ;
00065 const RenderingStyle Font::Underline = TTF_STYLE_UNDERLINE ;
00066 
00067 
00068 const Ceylan::System::Size Font::DefaultGlyphCachedQuota = 4 * 1024 * 1024 ;
00069 const Ceylan::System::Size Font::DefaultWordCachedQuota  = 6 * 1024 * 1024 ;
00070 const Ceylan::System::Size Font::DefaultTextCachedQuota  = 8 * 1024 * 1024 ;
00071 
00072 const Ceylan::Uint8 Font::DefaultSpaceBasedAlineaWidth = 6 ;
00073 
00074 
00075 
00076 Font::Font( 
00077         bool convertToDisplay, 
00078         RenderCache cacheSettings, 
00079         AllowedCachePolicy cachePolicy, 
00080         Ceylan::System::Size quota 
00081             ) throw() :         
00082     _renderingStyle( Normal ),
00083     _convertToDisplay( convertToDisplay ),
00084     _cacheSettings( cacheSettings ),
00085     _glyphCache( 0 ),
00086     _textCache( 0 ),
00087     _backgroundColor( Pixels::Black ),
00088     _spaceWidth( 0 ),
00089     _alineaWidth( 0 )
00090 {
00091     
00092     /*
00093      * _spaceWidth and _alineaWidth cannot be initialized in this Font
00094      *  mother class since getWidth is abstract for it (pure virtual). 
00095      * Therefore each child class should initialize it.
00096      *
00097      */
00098     
00099     switch( _cacheSettings )
00100     {
00101     
00102     
00103         /*
00104          * The several sub-switches allow to get rid of following parameter 
00105          * in signature :
00106          * 'Ceylan::SmartResourceManager<CharColorQualityKey>::CachePolicy
00107          * cachePolicy = Ceylan::SmartResourceManager<CharColorQualityKey>::
00108          * DropLessRequestedFirst' which would not be compliant with a
00109          * SmartResourceManager<StringColorQualityKey>, which is also needed. 
00110          *
00111          * The abstract AllowedCachePolicy is therefore to be converted 
00112          * to its templated version.
00113          *
00114          */
00115                  
00116         case None:
00117         
00118 #if OSDL_DEBUG_FONT
00119             LogPlug::debug( "Font created with no rendering cache." ) ;
00120 #endif // OSDL_DEBUG_FONT
00121 
00122             // Nothing to do.
00123             break ;
00124             
00125             
00126         case GlyphCached:
00127 
00128 #if OSDL_DEBUG_FONT
00129             LogPlug::debug( "Font created with glyph rendering cache." ) ;
00130 #endif // OSDL_DEBUG_FONT
00131 
00132             if ( quota == 0 )
00133                 quota = DefaultGlyphCachedQuota ;
00134             SmartResourceManager<CharColorQualityKey>::CachePolicy
00135                 actualGlyphPolicy ;
00136                 
00137             /*
00138              * Let's convert our allowed cache policy into a 
00139              * templated-ready cache policy :
00140              *
00141              */
00142             switch( cachePolicy )
00143             {   
00144                 case NeverDrop:
00145                     actualGlyphPolicy =
00146                         SmartResourceManager<CharColorQualityKey>::NeverDrop ;
00147                     break ;
00148                 
00149                 case DropLessRequestedFirst:
00150                     actualGlyphPolicy =
00151             SmartResourceManager<CharColorQualityKey>::DropLessRequestedFirst ;
00152                     break ;
00153                 
00154                 default:
00155                     Ceylan::emergencyShutdown(
00156                         "OSDL::Video::TwoDimensional::Font constructor "
00157                         "with glych cache : forbidden cache settings" ) ;       
00158                         
00159                     break ; 
00160             }       
00161             
00162             _glyphCache = new SmartResourceManager<CharColorQualityKey>( quota, 
00163                 actualGlyphPolicy ) ;
00164             break ;
00165         
00166         
00167         case WordCached:
00168 
00169 #if OSDL_DEBUG_FONT
00170             LogPlug::debug( "Font created with word rendering cache." ) ;
00171 #endif // OSDL_DEBUG_FONT
00172 
00173             if ( quota == 0 )
00174                 quota = DefaultWordCachedQuota ;
00175                 
00176             SmartResourceManager<StringColorQualityKey>::CachePolicy
00177                 actualWordPolicy ;
00178                 
00179             /*
00180              * Let's convert our allowed cache policy into a 
00181              * templated-ready cache policy :
00182              *
00183              */
00184             switch( cachePolicy )
00185             {   
00186             
00187                 case NeverDrop:
00188                     actualWordPolicy =
00189                         SmartResourceManager<StringColorQualityKey>::NeverDrop ;
00190                     break ;
00191                 
00192                 case DropLessRequestedFirst:
00193                     actualWordPolicy =
00194         SmartResourceManager<StringColorQualityKey>::DropLessRequestedFirst ;
00195                     break ;
00196                 
00197                 default:
00198                     Ceylan::emergencyShutdown(
00199                         "OSDL::Video::TwoDimensional::Font constructor "
00200                         "with word cache : forbidden cache settings" ) ;        
00201                         
00202                     break ; 
00203             }       
00204             
00205             _textCache = new SmartResourceManager<StringColorQualityKey>( 
00206                 quota, actualWordPolicy ) ;
00207             break ;
00208             
00209             
00210         case TextCached:
00211 
00212 #if OSDL_DEBUG_FONT
00213             LogPlug::debug( "Font created with text rendering cache." ) ;
00214 #endif // OSDL_DEBUG_FONT
00215 
00216             if ( quota == 0 )
00217                 quota = DefaultTextCachedQuota ;
00218                 
00219             SmartResourceManager<StringColorQualityKey>::CachePolicy
00220                 actualTextPolicy ;
00221                 
00222             /*
00223              * Let's convert our allowed cache policy into a 
00224              * templated-ready cache policy :
00225              *
00226              */
00227             switch( cachePolicy )
00228             {   
00229                 case NeverDrop:
00230                     actualTextPolicy =
00231                         SmartResourceManager<StringColorQualityKey>::NeverDrop ;
00232                     break ;
00233                 
00234                 case DropLessRequestedFirst:
00235                     actualTextPolicy =
00236         SmartResourceManager<StringColorQualityKey>::DropLessRequestedFirst ;
00237                     break ;
00238                 
00239                 default:
00240                     Ceylan::emergencyShutdown(
00241                         "OSDL::Video::TwoDimensional::Font constructor "
00242                         "with text cache : forbidden cache settings" ) ;        
00243                     break ; 
00244             }   
00245                 
00246             _textCache = new SmartResourceManager<StringColorQualityKey>( 
00247                 quota, actualTextPolicy ) ;     
00248             break ;
00249             
00250             
00251         default:
00252             Ceylan::emergencyShutdown( 
00253                 "OSDL::Video::TwoDimensional::Font constructor : "
00254                 "unexpected cache settings" ) ;
00255             break ;
00256                     
00257     }
00258     
00259     // _spaceWidth and _alineaWidth to be initialized in child classes.
00260     
00261 }
00262 
00263 
00264 
00265 Font::~Font() throw()
00266 {
00267 
00268     if ( _glyphCache != 0 )
00269         delete _glyphCache ;
00270         
00271     if ( _textCache != 0 )
00272         delete _textCache ;
00273         
00274 }
00275 
00276 
00277 
00278 RenderingStyle Font::getRenderingStyle() const throw()
00279 {
00280 
00281     return _renderingStyle ;
00282     
00283 }
00284 
00285 
00286 
00287 void Font::setRenderingStyle( RenderingStyle newStyle ) throw( TextException )
00288 {
00289 
00290     _renderingStyle = newStyle ;
00291     
00292 }
00293 
00294 
00295 
00296 
00297 void Font::setBackgroundColor( Pixels::ColorDefinition newBackgroundColor )
00298     throw()
00299 {
00300 
00301     _backgroundColor = newBackgroundColor ;
00302     
00303 }
00304 
00305 
00306 OSDL::Video::Pixels::ColorDefinition Font::getBackgroundColor() const throw()
00307 {
00308 
00309     return _backgroundColor ;
00310     
00311 }
00312 
00313 
00314 
00315 Width Font::getAlineaWidth() const throw()
00316 
00317 {
00318     return _alineaWidth ;
00319     
00320 }
00321 
00322 
00323 void Font::setAlineaWidth( Width newAlineaWidth ) throw()
00324 {
00325 
00326     _alineaWidth = newAlineaWidth ;
00327     
00328 }
00329 
00330 
00331 
00332 std::string Font::describeGlyphFor( Ceylan::Latin1Char character ) const throw()
00333 {
00334 
00335     list<string> res ;
00336 
00337     res.push_back( "Advance is " 
00338         + Ceylan::toString( getAdvance( character ) ) ) ;
00339     
00340     res.push_back( "Width is " + Ceylan::toString( getWidth( character ) ) ) ;
00341     
00342     res.push_back( "Width offset is " 
00343         + Ceylan::toString( getWidthOffset( character ) ) ) ;
00344     
00345     res.push_back( "Height above baseline is " 
00346         + Ceylan::toString( getHeightAboveBaseline( character ) ) ) ;
00347         
00348     return "Informations about the glyph corresponding to the character '" 
00349         + Ceylan::toString( character ) + "' : " 
00350         + Ceylan::formatStringList( res ) ;
00351         
00352 }
00353 
00354 
00355     
00356 Width Font::getInterGlyphWidth() const throw()
00357 {
00358 
00359     // Scales with the letter width, minimum is one :
00360     static Width inter = static_cast<Width>( 
00361         Ceylan::Maths::Max<float>( 1, 0.1 * getWidth( 'a' ) ) ) ;
00362 
00363     return inter ;
00364 
00365 }
00366 
00367 
00368 
00369 OSDL::Video::Surface & Font::renderLatin1Text( const std::string & text,
00370         RenderQuality quality, Pixels::ColorDefinition textColor ) 
00371     throw( TextException )
00372 {
00373 
00374     
00375     // Different cases, depending on cache settings :
00376 
00377     switch( _cacheSettings )
00378     {
00379     
00380         case None:
00381         case GlyphCached:
00382         
00383             /*
00384              * Nothing else to do than full rendering (fire and forget),
00385              * glyph that are cached or not are managed directly at the 
00386              * glyph level.
00387              *
00388              */
00389             return basicRenderLatin1Text( text, quality, textColor ) ;
00390             break ;
00391         
00392         
00393         case WordCached:
00394             return renderLatin1TextWithWordCached( text, quality, textColor ) ;
00395             break ;
00396             
00397             
00398         case TextCached:
00399             return renderLatin1TextWithTextCached( text, quality, textColor ) ;
00400             break ;
00401             
00402             
00403         default:
00404             Ceylan::emergencyShutdown(
00405                 "OSDL::Video::TwoDimensional::Font::renderLatin1Text : "
00406                 "unexpected cache settings" ) ;
00407             break ;
00408                     
00409     }
00410     
00411     // Avoid a warning :
00412     throw TextException( "Font::renderLatin1Text : unexpected end of method" ) ;
00413         
00414 }
00415 
00416 
00417 
00418 void Font::blitLatin1Text( Surface & targetSurface, Coordinate x, Coordinate y, 
00419         const std::string & text, RenderQuality quality, 
00420         Pixels::ColorDefinition textColor ) throw( TextException )          
00421 {
00422         
00423     /*
00424      * Shortcut :
00425      * Tests if one can retrieve and blit its target from the text cache 
00426      * instead of creating, blitting and destroying a Surface just for
00427      * this blit (if the text cache is used; word cache has far
00428      * too few chances of having 'text', it is not interrogated here as a
00429      * shortcut) :
00430      *
00431      */ 
00432     if ( _cacheSettings == TextCached )
00433     {
00434     
00435         StringColorQualityKey renderKey( text, textColor, quality ) ;
00436             
00437         const Resource * res = _textCache->get( renderKey ) ;
00438 
00439         
00440         if ( res != 0 )
00441         {
00442         
00443             const Surface * toReturn = dynamic_cast<const Surface *>( res ) ;
00444             
00445 #if OSDL_DEBUG_FONT
00446 
00447             LogPlug::debug( "Font::blitLatin1Text : cache hit, "
00448                 "returning clone of prerendered text." ) ;
00449             
00450             if ( toReturn == 0 )
00451                 Ceylan::emergencyShutdown( "Font::blitLatin1Text : "
00452                     "clone is not a Surface." ) ;
00453                     
00454 #endif // OSDL_DEBUG_FONT
00455             
00456             toReturn->blitTo( targetSurface, x, y ) ;
00457             
00458             return ;
00459 
00460         }
00461     
00462     }
00463     
00464     
00465     /*
00466      * Here we have to ask a full one-shot rendered surface 
00467      * (possibly with cache help) :
00468      *
00469      */
00470     Surface & res = renderLatin1Text( text, quality, textColor ) ;
00471     
00472     // Useful for testing : 
00473     // res.savePNG( Ceylan::toString( y ) + ".png" ) ;
00474     
00475     try
00476     {
00477         res.blitTo( targetSurface, x, y ) ;
00478     }
00479     catch( const VideoException & e )
00480     {
00481         // Would not be safe : delete & res ;
00482         throw TextException( "Font::blitLatin1Text : blit failed : " 
00483             + e.toString() ) ;
00484     }   
00485     
00486     delete & res ;
00487     
00488 }
00489 
00490 
00491         
00492 OSDL::Video::Surface & Font::renderLatin1MultiLineText( 
00493     Length width, Length height, const std::string & text, 
00494     TextIndex & renderIndex, Coordinate & lastOrdinateUsed,
00495     RenderQuality quality, Pixels::ColorDefinition textColor, bool justified ) 
00496         throw( TextException )
00497 {
00498 
00499     ColorMask redMask, greenMask, blueMask ;
00500     
00501     Pixels::getRecommendedColorMasks( redMask, greenMask, blueMask ) ;
00502     
00503     Surface & res = * new Surface( Surface::Hardware | Surface::ColorkeyBlit, 
00504         width, height, /* bpp */ 32, redMask, greenMask, blueMask, 
00505         /* no alpha wanted */ 0 ) ;
00506 
00507     /*
00508      * Avoid messing text color with color key.
00509      * 'Pixels::selectColorDifferentFrom' could be used but here a full 
00510      * surface fill can be saved in most cases (as long as the text color 
00511      * is not pure black).
00512      *
00513      */
00514     Pixels::ColorDefinition colorKey ;
00515     
00516     if ( Pixels::areEqual( textColor, Pixels::Black, /* use alpha */ false ) )
00517     {
00518         colorKey = Pixels::White ;
00519         res.fill( colorKey ) ;
00520     }   
00521     else
00522     {
00523     
00524         colorKey = Pixels::Black ;
00525         /*
00526          * No need to fill 'res' with black, since new RGB surfaces come 
00527          * all black already.
00528          *
00529          */
00530          
00531     }   
00532         
00533     Length lineSkip = getLineSkip() ;
00534     
00535     /*
00536      * This integer division returns the maximum usable number of lines 
00537      * in this box :
00538      *
00539      */
00540     LineNumber maxLines = height / lineSkip ;
00541     
00542     if ( maxLines == 0 )
00543         throw TextException( "Font::renderLatin1MultiLineText : box height ("
00544             + Ceylan::toString( height ) 
00545             + ") is not enough even for one line of text, whose height is "
00546             + Ceylan::toString( lineSkip )  + "." ) ;
00547 
00548     renderIndex = 0 ;
00549     
00550 #if OSDL_DEBUG_FONT
00551 
00552     LogPlug::debug( "Font::renderLatin1MultiLineText : " 
00553         + Ceylan::toString( maxLines )
00554         + " line(s) available to render following text : '" + text + "'." ) ;
00555         
00556 #endif // OSDL_DEBUG_FONT
00557     
00558     
00559     /*
00560      * Auto-centering vertically the usable area for text in its surface 
00561      * should not be enforced here since it would hinder placing the 
00562      * returned surface exactly where appropriate in its container.
00563      *
00564      * Hence disabled : 
00565      * 'Height lineHeight = ( height - maxLines * lineSkip ) / 2 ;'
00566      *
00567      */
00568     Height lineHeight = 0 ; 
00569 
00570     // Each paragraph in turn will be split into a list of words :
00571     list<string> words ;
00572     
00573     string currentWord ;
00574     
00575     Length currentWidth, storedWidth ;
00576     
00577     Width wordWidth ;
00578     const Surface * wordSurface ;
00579     
00580     Width totalWordWidth ;
00581     
00582     list<string> wordsOnTheLine ;
00583 
00584     bool lineFull ;
00585     
00586     /*
00587      * Having a justified text implies that all words in a line of text 
00588      * must be rendered before starting the blits for this line. 
00589      *
00590      * If word or text caches are used, then word renderings are directly
00591      * taken from them : it prevents from creating, rendering and 
00592      * destroying uselessly as many surfaces as words.
00593      *
00594      * If no word or text cache is used, these renderings have to be kept
00595      * nonetheless, whether only a glyph cache is used or not. 
00596      * Therefore a word cache is temporarily used to speed up this 
00597      * two-pass line rendering. 
00598      * Glyph cache, if any, is used as well nonetheless and will be fed,
00599      * whereas word cache will be deallocated after this rendering. 
00600      *
00601      */
00602     
00603     bool createTemporaryWordCache = 
00604         ( _cacheSettings == GlyphCached || _cacheSettings == None ) ;
00605     
00606     if ( createTemporaryWordCache )
00607     {
00608     
00609 #if OSDL_DEBUG_FONT
00610 
00611         if ( _textCache != 0 )
00612             throw TextException( "Font::renderLatin1MultiLineText : "
00613                 "unable to create temporary word cache 
00614                 "since already existing." ) ;
00615 
00616         LogPlug::debug( "Font::renderLatin1MultiLineText : "
00617             "creating a temporary word cache" ) ;
00618 
00619 #endif // OSDL_DEBUG_FONT
00620 
00621         _textCache = new SmartResourceManager<StringColorQualityKey>(
00622             DefaultTextCachedQuota, 
00623     SmartResourceManager<StringColorQualityKey>::DropLessRequestedFirst ) ;
00624 
00625     }
00626 
00627     /*
00628      * Hence in all cases we can rely on having a word cache 
00629      * (even though it starts empty).
00630      *
00631      */
00632 
00633     list<string> paragraphs = Ceylan::splitIntoParagraphs( text ) ;
00634 
00635     words = Ceylan::splitIntoWords( paragraphs.front() ) ;
00636     paragraphs.pop_front() ;
00637     currentWidth = _alineaWidth ;
00638 
00639 
00640     // Here we use a (word) cache, and we draw the lines one by one :
00641     for ( TextIndex currentLine = 0; currentLine < maxLines; currentLine++ )
00642     {
00643 
00644         // Save current width for later restore :
00645         storedWidth = currentWidth ;
00646     
00647         /*
00648          * Start from the left edge, and select as many words as possible 
00649          * within this line :
00650          *
00651          */
00652         totalWordWidth = 0 ;
00653 
00654         lineFull = false ;
00655         wordsOnTheLine.clear() ;
00656            
00657         while ( ! words.empty() && ! lineFull )
00658         {
00659        
00660             currentWord = words.front() ;
00661            
00662             // Multiple whitespaces in a row can lead to empty words :
00663             if ( currentWord.empty() )
00664             {
00665                 wordSurface = 0 ;
00666                 wordWidth = 0 ;
00667             }
00668             else
00669             {   
00670                 wordSurface = & getConstLatin1WordFromCache( currentWord,
00671                     quality, textColor ) ;
00672                 wordWidth   = wordSurface->getWidth() ;
00673             }
00674            
00675             if ( currentWidth + wordWidth <= width ) 
00676             {
00677            
00678                 // Word accepted for this line :
00679                 renderIndex += currentWord.size() + /* trailing space */ 1 ;
00680                 totalWordWidth += wordWidth ;
00681                
00682                 if ( justified )
00683                     wordsOnTheLine.push_back( currentWord ) ;
00684                 else 
00685                     if ( wordSurface != 0 )
00686                         wordSurface->blitTo( res, currentWidth, lineHeight ) ;
00687                        
00688                 currentWidth += wordWidth + _spaceWidth ;
00689                 words.pop_front() ;
00690             }
00691             else
00692             {
00693                 // With this last word, the line would be too long :
00694                 lineFull = true ;
00695             }
00696                                    
00697         }
00698        
00699         // Words are selected for the current line, now time to render them.
00700 
00701         System::Size wordCount = wordsOnTheLine.size() ;
00702         currentWidth = storedWidth ;
00703        
00704         /*
00705          * Last part of a paragraph should not be justified : it would 
00706          * result in huge inter-word spaces, instead the text can stop 
00707          * anywhere before the line's end.
00708          * Hence we check that 'words' is not empty.
00709          *
00710          * Zero word or only one word ? Do nothing special to justify text.
00711          * 
00712          */
00713         if ( justified && ! words.empty() && wordCount > 1 )
00714         {
00715                                                
00716             for ( list<string>::const_iterator it = wordsOnTheLine.begin();
00717                 it != wordsOnTheLine.end(); it++ ) 
00718             {
00719 
00720                 if ( (*it ).empty() )
00721                 {
00722                     wordWidth = 0 ;
00723                 }
00724                 else
00725                 {
00726                 
00727                     wordSurface = & getConstLatin1WordFromCache( (*it), 
00728                         quality, textColor ) ;
00729                        
00730                     wordSurface->blitTo( res, currentWidth, lineHeight ) ;
00731                    
00732                     wordWidth = wordSurface->getWidth() ;
00733                     
00734                 }   
00735                    
00736                 /*
00737                  * For justified text, space width is computed with 
00738                  * pixel-perfect accuracy.
00739                  * Knowing exactly what words fit with normal space width, 
00740                  * a new space width is computed so that these words are
00741                  * dispatched regularly on the line, and begin and end with 
00742                  * it, provided it is not a paragraph end.
00743                  *
00744                  * As this space width has to be an integer, round off errors
00745                  * would accumulate if a constant corrected space width 
00746                  * was used, and the last word of the line would not end
00747                  * perfectly at the end of it, which would lead to a rather
00748                  * unpleasant visual effect : the right edge of the text 
00749                  * would not be vertically aligned.
00750                  *
00751                  * To correct that, after each word the perfect space width 
00752                  * for this step is computed, considering only what remains 
00753                  * to be written. 
00754                  * Hence the space width is adapted and the text fit 
00755                  * perfectly on the line.
00756                  * 
00757                  * Better round to lowest integer (ceil or static_cast) than 
00758                  * to nearest, since if space width is rounded up (floor) 
00759                  * the text might be clipped by the line edge.
00760                  *
00761                  * Number of spaces is equal to number of remaining words 
00762                  * minus one, the width of the current justified space is 
00763                  * the one that would be chosen if it was divided equally 
00764                  * for all remaining spaces.
00765                  *
00766                  */
00767                 wordCount-- ;
00768                
00769                 currentWidth += wordWidth + /* justified space */ 
00770                     static_cast<Width>( Maths::Round( 
00771                         static_cast<Ceylan::Float32>( 
00772                             width - currentWidth - totalWordWidth ) 
00773                                 / wordCount ) ) ;
00774                    
00775                 totalWordWidth -= wordWidth ;
00776                                    
00777             }
00778        
00779         }   
00780         else 
00781         {   
00782        
00783             // We do not justify text here :
00784            
00785             for ( list<string>::const_iterator it = wordsOnTheLine.begin();
00786                 it != wordsOnTheLine.end(); it++ ) 
00787             {
00788                
00789                 if ( ! (*it).empty() )
00790                 {
00791                 
00792                     wordSurface = & getConstLatin1WordFromCache( (*it), 
00793                         quality, textColor ) ;
00794                     wordSurface->blitTo( res, currentWidth, lineHeight ) ;
00795                     currentWidth += wordSurface->getWidth() + _spaceWidth ;
00796                     
00797                 }   
00798                                    
00799             }                          
00800 
00801         }
00802 
00803         lineHeight += lineSkip ;
00804 
00805        
00806         if ( words.empty() )
00807         {
00808             
00809             if ( paragraphs.empty() )
00810                 break ;
00811             words = Ceylan::splitIntoWords( paragraphs.front() ) ;
00812             paragraphs.pop_front() ;
00813            
00814             // One empty line between paragraphs :
00815             currentLine++ ;
00816             lineHeight += lineSkip ;
00817             currentWidth = _alineaWidth ;
00818            
00819         }   
00820         else
00821         {
00822             currentWidth = 0 ;
00823         }   
00824                    
00825     }
00826 
00827 
00828     if ( createTemporaryWordCache )
00829     {
00830         
00831 
00832 #if OSDL_DEBUG_FONT
00833 
00834         LogPlug::debug( "Font::renderLatin1MultiLineText : "
00835             "deleting temporary word cache : " + _textCache->toString() ) ;
00836             
00837 #endif // OSDL_DEBUG_FONT
00838         
00839         delete _textCache ;
00840         _textCache = 0 ;
00841 
00842     }
00843 
00844     
00845     // No ordinate beyond container height should be retured :
00846     lastOrdinateUsed = lineHeight ;
00847     
00848     // To inspect justified text :
00849     //res.drawEdges() ;
00850     
00851 #if OSDL_DEBUG_FONT
00852 
00853     if ( renderIndex == text.size() )
00854         LogPlug::debug( 
00855             "Font::renderLatin1MultiLineText : full text fit in box." ) ;
00856     else    
00857         LogPlug::debug( "Font::renderLatin1MultiLineText : only " 
00858             + Ceylan::toString( renderIndex ) + " characters out of "
00859             + Ceylan::toString( text.size() ) 
00860             + " characters of the full text could be rendered in the box." ) ;
00861             
00862 #endif // OSDL_DEBUG_FONT
00863 
00864 
00865     /*
00866      * Comment out following two lines to see blit blocks 
00867      * (as black rectangles):
00868      *
00869      */
00870     res.setColorKey( Surface::ColorkeyBlit | Surface::RLEColorkeyBlit, 
00871         convertColorDefinitionToPixelColor( res.getPixelFormat(), 
00872         colorKey ) ) ;
00873 
00874 
00875     if ( _convertToDisplay )
00876     {
00877     
00878         /*
00879          * We want to keep our colorkey, so we do not choose to add alpha.
00880          * Surface will be RLE encoded here :
00881          *  
00882          */
00883         res.convertToDisplay( /* alphaChannelWanted */ false ) ;
00884     }   
00885     
00886     return res ;
00887     
00888 }
00889 
00890 
00891 
00892 void Font::blitLatin1MultiLineText( Surface & targetSurface, 
00893         const UprightRectangle & clientArea, const std::string & text,
00894         TextIndex & renderIndex, RenderQuality quality, 
00895         Pixels::ColorDefinition textColor, bool justified ) 
00896     throw( TextException )
00897 {
00898 
00899 #if OSDL_DEBUG_FONT
00900 
00901     LogPlug::debug( "Font::blitLatin1MultiLineText : rendering multiline text '"
00902         + text + "' on location " + clientArea.toString() + " of target " 
00903         + targetSurface.toString( Ceylan::low ) + "." ) ;
00904         
00905 #endif // OSDL_DEBUG_FONT
00906     
00907     blitLatin1MultiLineText( targetSurface, clientArea.getUpperLeftAbscissa(),
00908         clientArea.getUpperLeftOrdinate(), clientArea.getWidth(),
00909         clientArea.getHeight(), text, renderIndex, quality, 
00910         textColor, justified ) ;
00911 
00912 }
00913 
00914 
00915 
00916 void Font::blitLatin1MultiLineText( Surface & targetSurface, 
00917         Coordinate x, Coordinate y, Length width, Length height, 
00918         const std::string & text, TextIndex & renderIndex,
00919         RenderQuality quality, Pixels::ColorDefinition textColor, 
00920         bool justified ) 
00921     throw( TextException )
00922 {
00923     
00924     // Not used here :
00925     Coordinate lastOrdinateUsed ;
00926     
00927     /*
00928      * Nothing to optimize at this level, user ought cache multiline 
00929      * renderings if appropriate.
00930      *
00931      */
00932     Surface * res = & renderLatin1MultiLineText( width, height, 
00933         text, renderIndex, lastOrdinateUsed, quality, textColor, justified ) ;
00934     
00935     res->blitTo( targetSurface, x, y ) ;
00936     
00937     delete res ;
00938 
00939 }   
00940                             
00941         
00942                                                                         
00943 const string Font::toString( Ceylan::VerbosityLevels level ) const throw()
00944 { 
00945 
00946     string res = "Rendering style : " ;
00947     
00948     if ( _renderingStyle == Normal )
00949         res += "normal" ;
00950     else
00951     {   
00952         std::list<string> listRes ;
00953     
00954         if ( _renderingStyle & Bold )
00955             listRes.push_back( "bold" ) ;
00956         
00957         if ( _renderingStyle & Italic )
00958             listRes.push_back( "italic" ) ;
00959         
00960         if ( _renderingStyle & Underline )
00961             listRes.push_back( "underline" ) ;
00962             
00963         res += Ceylan::join( listRes, ", " ) ;
00964         
00965     }
00966     
00967     res += ". Renderings (and caches if activated) are " ;
00968     
00969     if ( ! _convertToDisplay )
00970         res += "not " ; 
00971     
00972     res += "automatically converted to display. " ;
00973     
00974     switch( _cacheSettings )
00975     {
00976         
00977         case None:
00978             res += "No render cache used" ;
00979             break ;
00980             
00981         case GlyphCached:
00982             res += "Glyph renderings are cached" ;
00983             break ;
00984     
00985         case WordCached:
00986             res += "Word renderings are cached" ;
00987             break ;
00988     
00989         case TextCached:
00990             res += "Text renderings are cached" ;
00991             break ;
00992     
00993         default:
00994             res += "Unknown policy for render cache (abnormal)" ;
00995             break ;
00996         
00997     }
00998 
00999 
01000     if ( level == Ceylan::low )
01001         return res ;    
01002         
01003         
01004     // Cache pointers should be ok, hence are not tested beforehand :
01005     switch( _cacheSettings )
01006     {
01007         
01008         case None:
01009             break ;
01010             
01011         case GlyphCached:
01012             res += ". Glyph cache state is : " 
01013                 + _glyphCache->toString( level ) ;
01014             break ;
01015     
01016         case WordCached:
01017             res += ". Word cache state is : " + _textCache->toString( level )  ;
01018             break ;
01019     
01020         case TextCached:
01021             res += ". Text cache state is : " + _textCache->toString( level )  ;
01022             break ;
01023     
01024         default:
01025             break ;
01026         
01027     }
01028 
01029     return res ;
01030     
01031 }
01032 
01033 
01034 
01035 string Font::InterpretRenderingStyle( RenderingStyle style ) throw()
01036 {
01037 
01038     if ( style == Normal )
01039         return "normal" ;
01040 
01041     std::list<string> res ;
01042     
01043     if ( style & Bold )
01044         res.push_back( "bold" ) ;
01045                 
01046     if ( style & Italic )
01047         res.push_back( "italic" ) ;
01048                 
01049     if ( style & Underline )
01050         res.push_back( "underline" ) ;
01051     
01052     return Ceylan::join( res, ", " ) ;
01053                     
01054 }
01055 
01056 
01057 
01058 OSDL::Video::Surface & Font::renderLatin1TextWithWordCached( 
01059     const string & text, RenderQuality quality, 
01060     Pixels::ColorDefinition textColor ) throw( TextException )
01061 {
01062 
01063     /*
01064      * The difficulty here is that we cannot know the total width a priori.
01065      *
01066      * Two passes are therefore needed : one to know the size and create 
01067      * the overall surface, the other to blit words onto it.
01068      *
01069      */
01070     
01071     // Splits text into a list of words :
01072     list<string> words = Ceylan::split( text, ' ' ) ;
01073     
01074     
01075     Length currentWidth = 0 ;
01076     
01077 #if OSDL_DEBUG_FONT
01078 
01079     LogPlug::debug( "Font::renderLatin1TextWithWordCached : will render '"
01080         + text + "', space width is " + Ceylan::toString( _spaceWidth ) ) ;
01081         
01082 #endif // OSDL_DEBUG_FONT
01083     
01084     const Surface * wordRendered ;
01085         
01086     /*
01087      * First iteration : feeds the cache with word renderings and 
01088      * computes the total width :
01089      *
01090      */
01091     for ( list<string>::const_iterator it = words.begin(); 
01092         it != words.end(); it++ )
01093     {
01094     
01095         if ( (*it).empty() )
01096         {
01097 
01098 #if OSDL_DEBUG_FONT
01099 
01100             LogPlug::debug( 
01101                 "Font::renderLatin1TextWithWordCached : jumping a space." ) ;
01102                 
01103 #endif // OSDL_DEBUG_FONT
01104 
01105             currentWidth += _spaceWidth ;
01106             continue ;
01107         }   
01108         
01109 #if OSDL_DEBUG_FONT
01110 
01111         LogPlug::debug( "Font::renderLatin1TextWithWordCached : examining '" 
01112             + (*it) + "'." ) ;
01113             
01114 #endif // OSDL_DEBUG_FONT
01115         
01116         /*
01117          * Note that this method is used in the 'word cached' case.
01118          * In all cases the returned 'const Surface' is still owned 
01119          * by the cache.
01120          *
01121          */
01122         wordRendered = & getConstLatin1WordFromCache( (*it), quality, 
01123             textColor ) ;
01124         currentWidth += wordRendered->getWidth() + _spaceWidth ;
01125                 
01126     }
01127     
01128     
01129     // We have the final width, let's create the overall surface :
01130     
01131     ColorMask redMask, greenMask, blueMask ;
01132     Pixels::getRecommendedColorMasks( redMask, greenMask, blueMask ) ;
01133     
01134     Surface & res = * new Surface( Surface::Hardware | Surface::ColorkeyBlit, 
01135         currentWidth, getLineSkip() - getDescent(), 32, 
01136         redMask, greenMask, blueMask, /* no alpha wanted */ 0 ) ;
01137 
01138     // Avoid messing text color with color key :
01139     Pixels::ColorDefinition colorKey ;
01140     
01141     if ( Pixels::areEqual( textColor, Pixels::Black, /* use alpha */ false ) )
01142     {
01143     
01144         colorKey = Pixels::White ;
01145         res.fill( colorKey ) ;
01146         
01147     }   
01148     else
01149     {
01150     
01151         colorKey = Pixels::Black ;
01152         /*
01153          * No need to fill 'res' with black, since new RGB surfaces 
01154          * come all black already.
01155          *
01156          */
01157          
01158     }   
01159             
01160     currentWidth = 0 ;
01161         
01162     /*
01163      * Second iteration : blit the word renderings. 
01164      *
01165      * Surfaces from first iteration could have been stored to save 
01166      * the efforts needed to find them in cache, but depending on the 
01167      * cache quota and policy, it cannot be assumed they are 
01168      * are all still available (the last could make the first go out). 
01169      *
01170      */
01171     for ( list<string>::const_iterator it = words.begin(); 
01172         it != words.end(); it++ )
01173     {
01174     
01175         if ( (*it).empty() )
01176         {
01177             currentWidth += _spaceWidth ;
01178             continue ;
01179         }   
01180         
01181 #if OSDL_DEBUG_FONT
01182 
01183         LogPlug::debug( "Font::renderLatin1TextWithWordCached : blitting '" 
01184             + (*it) + "'." ) ;
01185             
01186 #endif // OSDL_DEBUG_FONT
01187         
01188         wordRendered = & getConstLatin1WordFromCache( (*it), 
01189             quality, textColor ) ;
01190         wordRendered->blitTo( res, currentWidth, 0 ) ;
01191         currentWidth += wordRendered->getWidth() + _spaceWidth ;
01192                 
01193     }
01194     
01195 
01196     // Comment out following two lines to see blit blocks (as black rectangles):
01197     res.setColorKey( Surface::ColorkeyBlit | Surface::RLEColorkeyBlit, 
01198         convertColorDefinitionToPixelColor( res.getPixelFormat(), colorKey ) ) ;
01199     
01200     /*
01201      * Uncomment next line to debug computation of bounding boxes for 
01202      * renderings :
01203      *
01204      */
01205     //res.drawEdges() ;
01206     
01207     if ( _convertToDisplay )
01208     {
01209     
01210         /*
01211          * We want to keep our colorkey, so we do not choose to add alpha.
01212          * Surface will be RLE encoded here :
01213          *  
01214          */
01215         res.convertToDisplay( /* alphaChannelWanted */ false ) ;
01216     }   
01217     
01218     return res ;
01219     
01220 }
01221 
01222 
01223     
01224 OSDL::Video::Surface & Font::renderLatin1TextWithTextCached( 
01225     const string & text, RenderQuality quality, 
01226     Pixels::ColorDefinition textColor ) throw( TextException )
01227 {
01228 
01229 #if OSDL_DEBUG_FONT
01230 
01231     LogPlug::trace( "Font::renderLatin1TextWithTextCached" ) ;
01232     
01233 #endif // OSDL_DEBUG_FONT
01234     
01235     /*
01236      * First check that the text-quality-color combination is not already
01237      * available in cache :
01238      *
01239      */
01240         
01241     StringColorQualityKey renderKey( text, textColor, quality ) ;
01242             
01243     SmartResource * res = _textCache->getClone( renderKey ) ;
01244 
01245         
01246     if ( res != 0 )
01247     {
01248         
01249          Surface * returned = dynamic_cast<Surface *>( res ) ;
01250          
01251 #if OSDL_DEBUG_FONT
01252 
01253         LogPlug::debug( "Font::renderLatin1TextWithTextCached : cache hit, "
01254             "returning clone of prerendered text." ) ;
01255             
01256         if ( returned == 0 )
01257             Ceylan::emergencyShutdown( "Font::renderLatin1TextWithTextCached : "
01258                 "clone is not a Surface." ) ;
01259                 
01260 #endif // OSDL_DEBUG_FONT
01261             
01262         return * returned ;
01263             
01264     }   
01265     
01266 #if OSDL_DEBUG_FONT
01267     LogPlug::debug( "Font::renderLatin1TextWithTextCached : "
01268         "cache miss, creating new text rendering." ) ;
01269 #endif // OSDL_DEBUG_FONT
01270         
01271     // Here it its a cache miss, we therefore have to generate the text :
01272     Surface & newSurface = basicRenderLatin1Text( text, quality, textColor ) ;
01273         
01274     // Give the cache a chance of being fed :       
01275     _textCache->scanForAddition( renderKey, newSurface ) ;
01276         
01277     return newSurface  ; 
01278     
01279 }   
01280 
01281 
01282 
01283 void Font::blitLatin1Word( Surface & targetSurface, Coordinate x, Coordinate y, 
01284         const std::string & word, RenderQuality quality, 
01285         Pixels::ColorDefinition wordColor ) throw( TextException )
01286 {
01287 
01288 
01289     /*
01290      * We do not expect a given word to be already in cache if text-cached : 
01291      * OSDL_WORD_LOOKUP_IN_TEXT_CACHE is not defined by default.
01292      *
01293      * Hence there are two different cases : either we are word-cached, 
01294      * and we may have a rendering in cache, or not.
01295      *
01296      */
01297      
01298     /*
01299      * Uncomment to search in text cache too 
01300      * (not recommended since not more efficient) : 
01301      *
01302      */
01303     //#define OSDL_WORD_LOOKUP_IN_TEXT_CACHE
01304     
01305 #ifdef OSDL_WORD_LOOKUP_IN_TEXT_CACHE
01306     if ( _cacheSettings == WordCached || _cacheSettings == TextCached )
01307 #else // OSDL_WORD_LOOKUP_IN_TEXT_CACHE
01308     if ( _cacheSettings == WordCached )
01309 #endif // OSDL_WORD_LOOKUP_IN_TEXT_CACHE
01310     {
01311         
01312         getConstLatin1WordFromCache( word, quality, wordColor ).blitTo(
01313             targetSurface, x, y ) ;
01314                                         
01315     }
01316     else
01317     {
01318         // Here we cannot cache words, we blit it as directly as possible :
01319         basicRenderLatin1Text( word, quality, wordColor ).blitTo( 
01320             targetSurface, x, y ) ;
01321     }
01322     
01323 }
01324 
01325 
01326 /*
01327  * This method cannot exist since the caller should never have to deallocate 
01328  * the returned surface :
01329  *
01330 const OSDL::Video::Surface & Font::getConstRenderingForLatin1Word( 
01331     const std::string & word,
01332     RenderQuality quality, Pixels::ColorDefinition wordColor ) 
01333         throw( TextException )
01334 {
01335     
01336     // 
01337     
01338     if ( _cacheSettings == WordCached || _cacheSettings == TextCached )
01339     {
01340         return getConstLatin1WordFromCache( word, quality, wordColor ) ;    
01341     }
01342     else
01343     {
01344         
01345     }
01346     
01347 }
01348 */
01349         
01350                             
01351 const OSDL::Video::Surface & Font::getConstLatin1WordFromCache( 
01352     const std::string & word, RenderQuality quality, 
01353     Pixels::ColorDefinition wordColor ) throw( TextException )
01354 {
01355     
01356     // If not in cache, render it and put it in.
01357 
01358     StringColorQualityKey renderKey( word, wordColor, quality ) ;
01359         
01360     const Resource * inCache = _textCache->get( renderKey ) ;   
01361     
01362     if ( inCache != 0 )
01363     {
01364     
01365         // Found in cache !
01366         const Surface * wordSurface = dynamic_cast<const Surface *>( inCache ) ;
01367         
01368 #if OSDL_DEBUG_FONT
01369 
01370         LogPlug::debug( "Font::getConstLatin1WordFromCache : "
01371             "cache hit for '" + word 
01372             + "', returning 'const' prerendered word." ) ;
01373             
01374         if ( wordSurface == 0 )
01375             Ceylan::emergencyShutdown( "Font::getConstLatin1WordFromCache : "
01376                 "cache did not return a Surface." ) ;
01377                 
01378 #endif // OSDL_DEBUG_FONT
01379     
01380         return * wordSurface ;
01381         
01382     }
01383 
01384 #if OSDL_DEBUG_FONT
01385 
01386     LogPlug::debug( "Font::getConstLatin1WordFromCache : "
01387         "cache miss for '" + word 
01388         + "', rendering it and submitting it to cache." ) ;
01389         
01390 #endif // OSDL_DEBUG_FONT
01391     
01392     // Here we know the word rendering is not in cache, we need to put it in :  
01393     Surface & wordSurface = basicRenderLatin1Text( word, quality, wordColor ) ;
01394         
01395     try
01396     { 
01397         _textCache->takeOwnershipOf( renderKey, wordSurface ) ;
01398     }
01399     catch( const ResourceManagerException & e )
01400     {
01401     
01402         /*
01403          * This really should never happen : we know this word rendering 
01404          * is not in cache.
01405          *
01406          */
01407         throw TextException( "Font::getConstLatin1WordFromCache : "
01408             "cache submitting failed (abnormal) : " + e.toString() ) ;
01409 
01410     }
01411     
01412     // Cache still owns that (const) surface :      
01413     return wordSurface ;
01414 
01415 }   
01416                             
01417                             
01418 
01419 OSDL::Video::Surface & Font::basicRenderLatin1Text( const std::string & text,
01420         RenderQuality quality, Pixels::ColorDefinition textColor ) 
01421     throw( TextException )
01422 {
01423 
01424     Length lineSkip = getLineSkip() ;
01425     Length ascent   = getAscent() ;
01426     
01427     System::Size textSize = text.size() ;
01428 
01429 #if OSDL_DEBUG_FONT
01430 
01431     LogPlug::debug( "Font::basicRenderLatin1Text : rendering '" 
01432         + text + "'." ) ;
01433         
01434     LogPlug::debug( "Font::basicRenderLatin1Text : line skip is " 
01435         + Ceylan::toString( lineSkip ) ) ;
01436         
01437     LogPlug::debug( "Font::basicRenderLatin1Text : ascent is " 
01438         + Ceylan::toString( ascent ) ) ;
01439         
01440 #endif // OSDL_DEBUG_FONT
01441         
01442     Length * horizSteps = new Length[ textSize ] ;
01443 
01444 
01445     /*
01446      * When adding a letter y after a letter x, y has to be drawn at : 
01447      * abscissa of x plus x's advance, so that if x leaves room in the 
01448      * baseline, y can start as left as possible, even if x spreads on the 
01449      * right of the position where y starts (ex : if x is a capital 'L' whose
01450      * bottom line goes under the baseline).
01451      *
01452      * However when a letter is the last in a surface, one should use its 
01453      $ width instead of its advance, so that the letter is not truncated. 
01454      * Moreover, the (n-1) letter might spread more to the right than the 
01455      * n one (ex : 'y.', the dot can be placed at the left of the rightmost
01456      * branch of the 'y'), so the best solution is to record max width 
01457      * at each step (instead of replacing the advance by the width for the 
01458      * last glyph).
01459      *
01460      */
01461     Length width = 0 ;
01462     Length maxWidth = 0 ;
01463     System::Size charCount = 0 ;
01464             
01465     bool firstLetter = true ;
01466     
01467     Ceylan::Latin1Char currentChar ;        
01468     SignedLength       currentOffset ;
01469     Length             currentAdvance ;
01470     
01471     /*
01472      * First iteration : guess width and height of the resulting surface :
01473      * (widths are stored for later use)
01474      *
01475      */
01476     for ( string::const_iterator it = text.begin(); it != text.end(); it++ )
01477     {
01478     
01479         currentChar    = static_cast<Ceylan::Latin1Char>(*it) ;
01480         currentOffset  = getWidthOffset( currentChar ) ;
01481         currentAdvance = getAdvance( currentChar ) ;
01482         
01483         /*
01484          * First letters may have a negative offset that should be corrected :
01485          * (otherwise leftmost part of first letter could be truncated)
01486          *
01487          */
01488         
01489         if ( firstLetter )
01490         {
01491             
01492             firstLetter = false ;
01493             
01494             // Uses 0 instead of currentOffset :            
01495             horizSteps[charCount] = 0 ;
01496             
01497         }
01498         else
01499         {   
01500         
01501             /*
01502              * The offset corresponds to the abscissa of the leftmost 
01503              * part of the glyph in its local referential.
01504              *
01505              */         
01506             horizSteps[charCount] = width + currentOffset ;
01507         
01508         }
01509         
01510         charCount++ ;
01511                 
01512 #if OSDL_DEBUG_FONT
01513 
01514         LogPlug::debug( "Font::basicRenderLatin1Text : adding " 
01515             + Ceylan::toString( currentAdvance ) 
01516             + " width for char '" +  Ceylan::toString( currentChar ) + "'" ) ;
01517             
01518 #endif // OSDL_DEBUG_FONT
01519                 
01520         // The new real abscissa than can be written is : 
01521         width += currentAdvance ;
01522                     
01523         /*
01524          * Previously using next addition, which leads to too much space 
01525          * between letters and not to compact uppercase letters (ex : 'OSDL')
01526          * whereas they should be 
01527          * (see with cursive fonts with really wide uppercase glyphs, such 
01528          * as 'cretino.ttf').
01529          *
01530          */
01531         //width += getWidth( currentChar ) + currentOffset ;
01532 
01533     }
01534     
01535     /*
01536      * Retrieves the rightmost abscissa that could be drawn :  
01537      * (character width should be used instead of advance so that its 
01538      * rightmost part is not truncated).
01539      *
01540      */
01541     maxWidth = horizSteps[ textSize - 1 ] + currentOffset 
01542         + getWidth( currentChar ) ;
01543             
01544 #if OSDL_DEBUG_FONT
01545 
01546     LogPlug::debug( "Font::basicRenderLatin1Text : text width will be " 
01547         + Ceylan::toString( maxWidth ) 
01548         + ", height will be " + Ceylan::toString( lineSkip ) + "." ) ;
01549          
01550 #endif // OSDL_DEBUG_FONT
01551     
01552     /*
01553      * Must be filled with glyphs now 
01554      * (lineskip is used to avoid complex blits of lines) :
01555      *
01556      */
01557 
01558     ColorMask redMask, greenMask, blueMask ;
01559     Pixels::getRecommendedColorMasks( redMask, greenMask, blueMask ) ;
01560     
01561     Surface * res ;
01562     
01563     try
01564     {
01565         res = new Surface( Surface::Hardware | Surface::ColorkeyBlit, 
01566             maxWidth, lineSkip - getDescent(), 32, 
01567             redMask, greenMask, blueMask, /* no alpha wanted */ 0 ) ;
01568     }
01569     catch( const VideoException & e )
01570     {
01571         delete [] horizSteps ;
01572         throw TextException( 
01573             "Font::basicRenderLatin1Text : surface creation failed : "
01574             + e.toString() ) ;
01575     }
01576     
01577     // Avoid messing text color with color key :
01578     Pixels::ColorDefinition colorKey ;
01579     
01580     if ( Pixels::areEqual( textColor, Pixels::Black, /* use alpha */ false ) )
01581     {
01582         colorKey = Pixels::White ;
01583         res->fill( colorKey ) ;
01584     }   
01585     else
01586     {
01587         colorKey = Pixels::Black ;
01588         /*
01589          * No need to fill 'res' with black, since new RGB surfaces come 
01590          * all black already.
01591          *
01592          */
01593     }   
01594 
01595     
01596     // Second pass : actual rendering :
01597     charCount = 0 ;
01598     
01599     for ( string::const_iterator it = text.begin(); it != text.end(); it++ )
01600     {
01601         currentChar = static_cast<Ceylan::Latin1Char>(*it) ;
01602         
01603 #if OSDL_DEBUG_FONT
01604 
01605         LogPlug::debug( "Font::basicRenderLatin1Text : rendering '" 
01606             + Ceylan::toString( currentChar )
01607             + "' at width "  + Ceylan::toString( horizSteps[charCount] )
01608             + ", at height " 
01609             + Ceylan::toString( ascent - getHeightAboveBaseline( currentChar ) )
01610         ) ;
01611         
01612 #endif // OSDL_DEBUG_FONT
01613             
01614         blitLatin1Glyph( *res, horizSteps[charCount], 
01615             ascent - getHeightAboveBaseline( currentChar ), 
01616             currentChar, quality, textColor ) ;
01617             
01618         charCount++ ;
01619     }
01620     
01621     // Comment out following two lines to see blit blocks (as black rectangles):
01622     res->setColorKey( Surface::ColorkeyBlit | Surface::RLEColorkeyBlit, 
01623         convertColorDefinitionToPixelColor( res->getPixelFormat(), colorKey ) 
01624     ) ;
01625     
01626     if ( _convertToDisplay )
01627     {
01628     
01629         /*
01630          * We want to keep our colorkey, so we do not choose to add alpha.
01631          * Surface will be RLE encoded here :
01632          *  
01633          */
01634         res->convertToDisplay( /* alphaChannelWanted */ false ) ;
01635     }   
01636     
01637     /*
01638      * Uncomment next line to debug computation of bounding boxes for 
01639      * renderings :
01640      *
01641      */
01642     //res->drawEdges() ;
01643     
01644     delete [] horizSteps ;
01645     
01646     return *res ;
01647     
01648 }
01649 

Generated on Fri Mar 30 14:46:59 2007 for OSDL by  doxygen 1.5.1