00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 #include "OSDLFont.h"
00028 
00029 #include "OSDLSurface.h"         
00030 #include "OSDLPixel.h"           
00031 
00032 
00033 #include <list>
00034 
00035 
00036 #ifdef OSDL_USES_CONFIG_H
00037 #include <OSDLConfig.h>              
00038 #endif // OSDL_USES_CONFIG_H
00039 
00040 
00041 #if OSDL_ARCH_NINTENDO_DS
00042 #include "OSDLConfigForNintendoDS.h" 
00043 #endif // OSDL_ARCH_NINTENDO_DS
00044 
00045 
00046 
00047 
00048 
00049 #if OSDL_USES_SDL_TTF
00050 
00051 #include "SDL_ttf.h"             
00052 
00053 #endif // OSDL_USES_SDL_TTF
00054 
00055 
00056 using std::list ;
00057 using std::pair ;
00058 
00059 using std::string ;
00060 
00061 
00062 using namespace Ceylan ;
00063 using namespace Ceylan::Log ;
00064 
00065 using namespace OSDL::Video ;
00066 using namespace OSDL::Video::Pixels ;
00067 using namespace OSDL::Video::TwoDimensional ;
00068 
00069 
00070 
00071 string Text::Font::FontPathEnvironmentVariable = "FONT_PATH" ;
00072 
00073 
00074 Ceylan::System::FileLocator Text::Font::FontFileLocator(
00075   FontPathEnvironmentVariable ) ;
00076 
00077 
00078 
00079 
00080 FontException::FontException( const string & reason ) :
00081   VideoException( "FontException: " + reason )
00082 {
00083 
00084 }
00085 
00086 
00087 
00088 FontException::~FontException() throw()
00089 {
00090 
00091 }
00092 
00093 
00094 
00095 
00096 using namespace OSDL::Video::TwoDimensional::Text ;
00097 
00098 
00099 
00100 
00101 
00102 
00103 
00104 
00105 #if OSDL_USES_SDL_TTF
00106 
00107 const RenderingStyle Font::Normal    = TTF_STYLE_NORMAL    ;
00108 const RenderingStyle Font::Bold      = TTF_STYLE_BOLD      ;
00109 const RenderingStyle Font::Italic    = TTF_STYLE_ITALIC    ;
00110 const RenderingStyle Font::Underline = TTF_STYLE_UNDERLINE ;
00111 
00112 #else // OSDL_USES_SDL_TTF
00113 
00114 
00115 
00116 
00117 const RenderingStyle Font::Normal    = 0x00 ;
00118 const RenderingStyle Font::Bold      = 0x01 ;
00119 const RenderingStyle Font::Italic    = 0x02 ;
00120 const RenderingStyle Font::Underline = 0x04 ;
00121 
00122 #endif // OSDL_USES_SDL_TTF
00123 
00124 
00125 
00126 
00127 const Ceylan::System::Size Font::DefaultGlyphCachedQuota = 4 * 1024 * 1024 ;
00128 const Ceylan::System::Size Font::DefaultWordCachedQuota  = 6 * 1024 * 1024 ;
00129 const Ceylan::System::Size Font::DefaultTextCachedQuota  = 8 * 1024 * 1024 ;
00130 
00131 const Ceylan::Uint8 Font::DefaultSpaceBasedAlineaWidth = 6 ;
00132 
00133 
00134 
00135 
00136 Font::Font(
00137   bool convertToDisplay,
00138   RenderCache cacheSettings,
00139   AllowedCachePolicy cachePolicy,
00140   Ceylan::System::Size quota ) :
00141   _renderingStyle( Normal ),
00142   _convertToDisplay( convertToDisplay ),
00143   _cacheSettings( cacheSettings ),
00144   _glyphCache( 0 ),
00145   _textCache( 0 ),
00146   _backgroundColor( Pixels::Black ),
00147   _spaceWidth( 0 ),
00148   _alineaWidth( 0 )
00149 {
00150 
00151   
00152 
00153 
00154 
00155 
00156 
00157 
00158   switch( _cacheSettings )
00159   {
00160 
00161 
00162     
00163 
00164 
00165 
00166 
00167 
00168 
00169 
00170 
00171 
00172 
00173 
00174 
00175   case None:
00176 
00177 #if OSDL_DEBUG_FONT
00178     LogPlug::debug( "Font created with no rendering cache." ) ;
00179 #endif // OSDL_DEBUG_FONT
00180 
00181     
00182     break ;
00183 
00184 
00185   case GlyphCached:
00186 
00187 #if OSDL_DEBUG_FONT
00188     LogPlug::debug( "Font created with glyph rendering cache." ) ;
00189 #endif // OSDL_DEBUG_FONT
00190 
00191     if ( quota == 0 )
00192       quota = DefaultGlyphCachedQuota ;
00193 
00194     SmartResourceManager<CharColorQualityKey>::CachePolicy
00195       actualGlyphPolicy ;
00196 
00197     
00198 
00199 
00200 
00201 
00202     switch( cachePolicy )
00203     {
00204 
00205     case NeverDrop:
00206       actualGlyphPolicy =
00207         SmartResourceManager<CharColorQualityKey>::NeverDrop ;
00208       break ;
00209 
00210     case DropLessRequestedFirst:
00211       actualGlyphPolicy =
00212         SmartResourceManager<CharColorQualityKey>::DropLessRequestedFirst ;
00213       break ;
00214 
00215     default:
00216       Ceylan::emergencyShutdown(
00217         "OSDL::Video::TwoDimensional::Font constructor "
00218         "with glych cache: forbidden cache settings" ) ;
00219 
00220       break ;
00221 
00222     }
00223 
00224     _glyphCache = new SmartResourceManager<CharColorQualityKey>( quota,
00225       actualGlyphPolicy ) ;
00226     break ;
00227 
00228 
00229   case WordCached:
00230 
00231 #if OSDL_DEBUG_FONT
00232     LogPlug::debug( "Font created with word rendering cache." ) ;
00233 #endif // OSDL_DEBUG_FONT
00234 
00235     if ( quota == 0 )
00236       quota = DefaultWordCachedQuota ;
00237 
00238     SmartResourceManager<StringColorQualityKey>::CachePolicy
00239       actualWordPolicy ;
00240 
00241     
00242 
00243 
00244 
00245 
00246     switch( cachePolicy )
00247     {
00248 
00249     case NeverDrop:
00250       actualWordPolicy =
00251         SmartResourceManager<StringColorQualityKey>::NeverDrop ;
00252       break ;
00253 
00254     case DropLessRequestedFirst:
00255       actualWordPolicy =
00256         SmartResourceManager<StringColorQualityKey>::DropLessRequestedFirst ;
00257       break ;
00258 
00259     default:
00260       Ceylan::emergencyShutdown(
00261         "OSDL::Video::TwoDimensional::Font constructor "
00262         "with word cache: forbidden cache settings" ) ;
00263 
00264       break ;
00265     }
00266 
00267     _textCache = new SmartResourceManager<StringColorQualityKey>(
00268       quota, actualWordPolicy ) ;
00269     break ;
00270 
00271 
00272   case TextCached:
00273 
00274 #if OSDL_DEBUG_FONT
00275     LogPlug::debug( "Font created with text rendering cache." ) ;
00276 #endif // OSDL_DEBUG_FONT
00277 
00278     if ( quota == 0 )
00279       quota = DefaultTextCachedQuota ;
00280 
00281     SmartResourceManager<StringColorQualityKey>::CachePolicy
00282       actualTextPolicy ;
00283 
00284     
00285 
00286 
00287 
00288 
00289     switch( cachePolicy )
00290     {
00291 
00292     case NeverDrop:
00293       actualTextPolicy =
00294         SmartResourceManager<StringColorQualityKey>::NeverDrop ;
00295       break ;
00296 
00297     case DropLessRequestedFirst:
00298       actualTextPolicy =
00299         SmartResourceManager<StringColorQualityKey>::DropLessRequestedFirst ;
00300       break ;
00301 
00302     default:
00303       Ceylan::emergencyShutdown(
00304         "OSDL::Video::TwoDimensional::Font constructor "
00305         "with text cache: forbidden cache settings" ) ;
00306       break ;
00307 
00308     }
00309 
00310     _textCache = new SmartResourceManager<StringColorQualityKey>(
00311       quota, actualTextPolicy ) ;
00312     break ;
00313 
00314 
00315   default:
00316     Ceylan::emergencyShutdown(
00317       "OSDL::Video::TwoDimensional::Font constructor: "
00318       "unexpected cache settings" ) ;
00319     break ;
00320 
00321   }
00322 
00323   
00324 
00325 }
00326 
00327 
00328 
00329 Font::~Font()  throw()
00330 {
00331 
00332   if ( _glyphCache != 0 )
00333     delete _glyphCache ;
00334 
00335   if ( _textCache != 0 )
00336     delete _textCache ;
00337 
00338 }
00339 
00340 
00341 
00342 RenderingStyle Font::getRenderingStyle() const
00343 {
00344 
00345   return _renderingStyle ;
00346 
00347 }
00348 
00349 
00350 
00351 void Font::setRenderingStyle( RenderingStyle newStyle )
00352 {
00353 
00354   _renderingStyle = newStyle ;
00355 
00356 }
00357 
00358 
00359 
00360 
00361 void Font::setBackgroundColor( Pixels::ColorDefinition newBackgroundColor )
00362 {
00363 
00364   _backgroundColor = newBackgroundColor ;
00365 
00366 }
00367 
00368 
00369 
00370 OSDL::Video::Pixels::ColorDefinition Font::getBackgroundColor() const
00371 {
00372 
00373   return _backgroundColor ;
00374 
00375 }
00376 
00377 
00378 
00379 Width Font::getAlineaWidth() const
00380 {
00381   return _alineaWidth ;
00382 
00383 }
00384 
00385 
00386 void Font::setAlineaWidth( Width newAlineaWidth )
00387 {
00388 
00389   _alineaWidth = newAlineaWidth ;
00390 
00391 }
00392 
00393 
00394 
00395 std::string Font::describeGlyphFor( Ceylan::Latin1Char character ) const
00396 {
00397 
00398   list<string> res ;
00399 
00400   res.push_back( "Advance is "
00401     + Ceylan::toString( getAdvance( character ) ) ) ;
00402 
00403   res.push_back( "Width is " + Ceylan::toString( getWidth( character ) ) ) ;
00404 
00405   res.push_back( "Width offset is "
00406     + Ceylan::toString( getWidthOffset( character ) ) ) ;
00407 
00408   res.push_back( "Height above baseline is "
00409     + Ceylan::toString( getHeightAboveBaseline( character ) ) ) ;
00410 
00411   return "Informations about the glyph corresponding to the character '"
00412     + Ceylan::toString( character ) + "': "
00413     + Ceylan::formatStringList( res ) ;
00414 
00415 }
00416 
00417 
00418 
00419 Width Font::getInterGlyphWidth() const
00420 {
00421 
00422   
00423   static Width inter = static_cast<Width>(
00424     Ceylan::Maths::Max<Ceylan::Float32>( 1, 0.1f * getWidth( 'a' ) ) ) ;
00425 
00426   return inter ;
00427 
00428 }
00429 
00430 
00431 
00432 OSDL::Video::Surface & Font::renderLatin1Text( const std::string & text,
00433   RenderQuality quality, Pixels::ColorDefinition textColor )
00434 {
00435 
00436   
00437 
00438   switch( _cacheSettings )
00439   {
00440 
00441   case None:
00442   case GlyphCached:
00443     
00444     
00445 
00446 
00447 
00448 
00449 
00450     return basicRenderLatin1Text( text, quality, textColor ) ;
00451     break ;
00452 
00453 
00454   case WordCached:
00455     
00456     return renderLatin1TextWithWordCached( text, quality, textColor ) ;
00457     break ;
00458 
00459 
00460   case TextCached:
00461     
00462     return renderLatin1TextWithTextCached( text, quality, textColor ) ;
00463     break ;
00464 
00465 
00466   default:
00467     Ceylan::emergencyShutdown(
00468       "OSDL::Video::TwoDimensional::Font::renderLatin1Text: "
00469       "unexpected cache settings" ) ;
00470     break ;
00471 
00472   }
00473 
00474   
00475   throw FontException( "Font::renderLatin1Text: unexpected end of method" ) ;
00476 
00477 }
00478 
00479 
00480 
00481 void Font::blitLatin1Text( Surface & targetSurface, Coordinate x, Coordinate y,
00482   const std::string & text, RenderQuality quality,
00483   Pixels::ColorDefinition textColor )
00484 {
00485 
00486   
00487 
00488 
00489 
00490 
00491 
00492 
00493 
00494 
00495   if ( _cacheSettings == TextCached )
00496   {
00497 
00498     StringColorQualityKey renderKey( text, textColor, quality ) ;
00499 
00500     const Resource * res = _textCache->get( renderKey ) ;
00501 
00502 
00503     if ( res != 0 )
00504     {
00505 
00506       const Surface * toReturn = dynamic_cast<const Surface *>( res ) ;
00507 
00508 #if OSDL_DEBUG_FONT
00509 
00510       LogPlug::debug( "Font::blitLatin1Text: cache hit, "
00511         "returning clone of prerendered text." ) ;
00512 
00513       if ( toReturn == 0 )
00514         Ceylan::emergencyShutdown( "Font::blitLatin1Text: "
00515           "clone is not a Surface." ) ;
00516 
00517 #endif // OSDL_DEBUG_FONT
00518 
00519       toReturn->blitTo( targetSurface, x, y ) ;
00520 
00521       return ;
00522 
00523     }
00524 
00525   }
00526 
00527 
00528   
00529 
00530 
00531 
00532 
00533   Surface & res = renderLatin1Text( text, quality, textColor ) ;
00534 
00535   
00536   
00537 
00538   try
00539   {
00540     res.blitTo( targetSurface, x, y ) ;
00541   }
00542   catch( const VideoException & e )
00543   {
00544     
00545     throw FontException( "Font::blitLatin1Text: blit failed: "
00546       + e.toString() ) ;
00547   }
00548 
00549   delete & res ;
00550 
00551 }
00552 
00553 
00554 
00555 OSDL::Video::Surface & Font::renderLatin1MultiLineText(
00556   Length width, Length height, const std::string & text,
00557   TextIndex & renderIndex, Coordinate & lastOrdinateUsed,
00558   RenderQuality quality, Pixels::ColorDefinition textColor, bool justified )
00559 {
00560 
00561   
00562 
00563 
00564 
00565 
00566   ColorMask redMask, greenMask, blueMask ;
00567 
00568   Pixels::getRecommendedColorMasks( redMask, greenMask, blueMask ) ;
00569 
00570   Surface & res = * new Surface( Surface::Hardware | Surface::ColorkeyBlit,
00571     width, height,  32, redMask, greenMask, blueMask,
00572      0 ) ;
00573 
00574   
00575 
00576 
00577 
00578 
00579 
00580 
00581   Pixels::ColorDefinition colorKey ;
00582 
00583   if ( Pixels::areEqual( textColor, Pixels::Black,  false ) )
00584   {
00585 
00586     colorKey = Pixels::White ;
00587     res.fill( colorKey ) ;
00588 
00589   }
00590   else
00591   {
00592 
00593     colorKey = Pixels::Black ;
00594     
00595 
00596 
00597 
00598 
00599 
00600   }
00601 
00602   Length lineSkip = getLineSkip() ;
00603 
00604   
00605 
00606 
00607 
00608 
00609   LineNumber maxLines = height / lineSkip ;
00610 
00611   if ( maxLines == 0 )
00612     throw FontException( "Font::renderLatin1MultiLineText: box height ("
00613       + Ceylan::toString( height )
00614       + ") is not enough even for one line of text, whose height is "
00615       + Ceylan::toString( lineSkip )  + "." ) ;
00616 
00617   renderIndex = 0 ;
00618 
00619 #if OSDL_DEBUG_FONT
00620 
00621   LogPlug::debug( "Font::renderLatin1MultiLineText: "
00622     + Ceylan::toString( maxLines )
00623     + " line(s) available to render following text: '" + text + "'." ) ;
00624 
00625 #endif // OSDL_DEBUG_FONT
00626 
00627 
00628   
00629 
00630 
00631 
00632 
00633 
00634 
00635 
00636 
00637   Height lineHeight = 0 ;
00638 
00639   
00640   list<string> words ;
00641 
00642   string currentWord ;
00643 
00644   Length currentWidth, storedWidth ;
00645 
00646   Width wordWidth ;
00647   const Surface * wordSurface ;
00648 
00649 
00650   
00651 
00652 
00653 
00654 
00655 
00656 
00657 
00658 
00659 
00660 
00661 
00662 
00663 
00664 
00665 
00666 
00667   bool createTemporaryWordCache =
00668     ( _cacheSettings == GlyphCached || _cacheSettings == None ) ;
00669 
00670   if ( createTemporaryWordCache )
00671   {
00672 
00673 #if OSDL_DEBUG_FONT
00674 
00675     if ( _textCache != 0 )
00676       throw FontException( "Font::renderLatin1MultiLineText: "
00677         "unable to create temporary word cache "
00678         "since already existing." ) ;
00679 
00680     LogPlug::debug( "Font::renderLatin1MultiLineText: "
00681       "creating a temporary word cache" ) ;
00682 
00683 #endif // OSDL_DEBUG_FONT
00684 
00685     _textCache = new SmartResourceManager<StringColorQualityKey>(
00686       DefaultTextCachedQuota,
00687       SmartResourceManager<StringColorQualityKey>::DropLessRequestedFirst ) ;
00688 
00689   }
00690 
00691   
00692 
00693 
00694 
00695 
00696 
00697   list<string> paragraphs = Ceylan::splitIntoParagraphs( text ) ;
00698 
00699   words = Ceylan::splitIntoWords( paragraphs.front() ) ;
00700   paragraphs.pop_front() ;
00701 
00702   currentWidth = _alineaWidth ;
00703 
00704   list<string> wordsOnTheLine ;
00705 
00706   Width totalWordWidth ;
00707 
00708   bool lineFull ;
00709 
00710   
00711   for ( TextIndex currentLine = 0; currentLine < maxLines; currentLine++ )
00712   {
00713 
00714     
00715     storedWidth = currentWidth ;
00716 
00717     
00718 
00719 
00720 
00721 
00722     totalWordWidth = 0 ;
00723 
00724     lineFull = false ;
00725     wordsOnTheLine.clear() ;
00726 
00727     while ( ! words.empty() && ! lineFull )
00728     {
00729 
00730       currentWord = words.front() ;
00731 
00732 
00733       
00734       if ( currentWord.empty() )
00735       {
00736 
00737         wordSurface = 0 ;
00738         wordWidth = 0 ;
00739 
00740       }
00741       else
00742       {
00743 
00744         wordSurface = & getConstLatin1WordFromCache( currentWord,
00745           quality, textColor ) ;
00746         wordWidth = wordSurface->getWidth() ;
00747 
00748         
00749 
00750 
00751 
00752 
00753 
00754 
00755         if ( wordWidth > width )
00756           LogPlug::error( "Font::renderLatin1MultiLineText: "
00757             "the rendering of word '" + currentWord
00758             + "' cannot fit in one line." ) ;
00759 
00760       }
00761 
00762       if ( currentWidth + wordWidth <= width )
00763       {
00764 
00765         
00766         renderIndex +=
00767           static_cast<Text::TextIndex>( currentWord.size() )
00768           +  1 ;
00769 
00770         totalWordWidth += wordWidth ;
00771 
00772         if ( justified )
00773           wordsOnTheLine.push_back( currentWord ) ;
00774         else
00775           if ( wordSurface != 0 )
00776             wordSurface->blitTo( res, currentWidth, lineHeight ) ;
00777 
00778         currentWidth += wordWidth + _spaceWidth ;
00779         words.pop_front() ;
00780       }
00781       else
00782       {
00783 
00784         
00785         lineFull = true ;
00786       }
00787 
00788     }
00789 
00790     
00791 
00792     System::Size wordCount = wordsOnTheLine.size() ;
00793     currentWidth = storedWidth ;
00794 
00795     
00796 
00797 
00798 
00799 
00800 
00801 
00802 
00803 
00804     if ( justified && ! words.empty() && wordCount > 1 )
00805     {
00806 
00807       for ( list<string>::const_iterator it = wordsOnTheLine.begin();
00808             it != wordsOnTheLine.end(); it++ )
00809       {
00810 
00811         if ( (*it ).empty() )
00812         {
00813           wordWidth = 0 ;
00814         }
00815         else
00816         {
00817 
00818           wordSurface = & getConstLatin1WordFromCache( (*it),
00819             quality, textColor ) ;
00820 
00821           wordSurface->blitTo( res, currentWidth, lineHeight ) ;
00822 
00823           wordWidth = wordSurface->getWidth() ;
00824 
00825         }
00826 
00827         
00828 
00829 
00830 
00831 
00832 
00833 
00834 
00835 
00836 
00837 
00838 
00839 
00840 
00841 
00842 
00843 
00844 
00845 
00846 
00847 
00848 
00849 
00850 
00851 
00852 
00853 
00854 
00855 
00856 
00857 
00858         wordCount-- ;
00859 
00860         currentWidth += wordWidth + 
00861           static_cast<Width>( Maths::Round(
00862               static_cast<Ceylan::Float32>(
00863                 width - currentWidth - totalWordWidth )
00864               / wordCount ) ) ;
00865 
00866         totalWordWidth -= wordWidth ;
00867 
00868       }
00869 
00870     }
00871     else
00872     {
00873 
00874       
00875 
00876       for ( list<string>::const_iterator it = wordsOnTheLine.begin();
00877             it != wordsOnTheLine.end(); it++ )
00878       {
00879 
00880         if ( ! (*it).empty() )
00881         {
00882 
00883           wordSurface = & getConstLatin1WordFromCache( (*it),
00884             quality, textColor ) ;
00885           wordSurface->blitTo( res, currentWidth, lineHeight ) ;
00886           currentWidth += wordSurface->getWidth() + _spaceWidth ;
00887 
00888         }
00889 
00890       }
00891 
00892     }
00893 
00894     lineHeight += lineSkip ;
00895 
00896 
00897     if ( words.empty() )
00898     {
00899 
00900       if ( paragraphs.empty() )
00901         break ;
00902       words = Ceylan::splitIntoWords( paragraphs.front() ) ;
00903       paragraphs.pop_front() ;
00904 
00905       
00906       currentLine++ ;
00907       lineHeight += lineSkip ;
00908       currentWidth = _alineaWidth ;
00909 
00910     }
00911     else
00912     {
00913       currentWidth = 0 ;
00914     }
00915 
00916   }
00917 
00918 
00919   if ( createTemporaryWordCache )
00920   {
00921 
00922 
00923 #if OSDL_DEBUG_FONT
00924 
00925     LogPlug::debug( "Font::renderLatin1MultiLineText: "
00926       "deleting temporary word cache: " + _textCache->toString() ) ;
00927 
00928 #endif // OSDL_DEBUG_FONT
00929 
00930     delete _textCache ;
00931     _textCache = 0 ;
00932 
00933   }
00934 
00935 
00936   
00937   lastOrdinateUsed = lineHeight ;
00938 
00939   
00940   
00941 
00942 #if OSDL_DEBUG_FONT
00943 
00944   if ( renderIndex == text.size() )
00945     LogPlug::debug(
00946       "Font::renderLatin1MultiLineText: full text fit in box." ) ;
00947   else
00948     LogPlug::debug( "Font::renderLatin1MultiLineText: only "
00949       + Ceylan::toString( renderIndex ) + " characters out of "
00950       + Ceylan::toString( text.size() )
00951       + " characters of the full text could be rendered in the box." ) ;
00952 
00953 #endif // OSDL_DEBUG_FONT
00954 
00955 
00956   
00957 
00958 
00959 
00960 
00961   res.setColorKey( Surface::ColorkeyBlit | Surface::RLEColorkeyBlit,
00962     convertColorDefinitionToPixelColor( res.getPixelFormat(),
00963       colorKey ) ) ;
00964 
00965 
00966   if ( _convertToDisplay )
00967   {
00968 
00969     
00970 
00971 
00972 
00973 
00974     res.convertToDisplay(  false ) ;
00975   }
00976 
00977   return res ;
00978 
00979 }
00980 
00981 
00982 
00983 void Font::blitLatin1MultiLineText( Surface & targetSurface,
00984   const UprightRectangle & clientArea, const std::string & text,
00985   TextIndex & renderIndex, RenderQuality quality,
00986   Pixels::ColorDefinition textColor, bool justified )
00987 {
00988 
00989 #if OSDL_DEBUG_FONT
00990 
00991   LogPlug::debug( "Font::blitLatin1MultiLineText: rendering multiline text '"
00992     + text + "' on location " + clientArea.toString() + " of target "
00993     + targetSurface.toString( Ceylan::low ) + "." ) ;
00994 
00995 #endif // OSDL_DEBUG_FONT
00996 
00997   blitLatin1MultiLineText( targetSurface, clientArea.getUpperLeftAbscissa(),
00998     clientArea.getUpperLeftOrdinate(), clientArea.getWidth(),
00999     clientArea.getHeight(), text, renderIndex, quality,
01000     textColor, justified ) ;
01001 
01002 }
01003 
01004 
01005 
01006 void Font::blitLatin1MultiLineText( Surface & targetSurface,
01007   Coordinate x, Coordinate y, Length width, Length height,
01008   const std::string & text, TextIndex & renderIndex,
01009   RenderQuality quality, Pixels::ColorDefinition textColor,
01010   bool justified )
01011 {
01012 
01013   
01014   Coordinate lastOrdinateUsed ;
01015 
01016   
01017 
01018 
01019 
01020 
01021   Surface * res = & renderLatin1MultiLineText( width, height,
01022     text, renderIndex, lastOrdinateUsed, quality, textColor, justified ) ;
01023 
01024   res->blitTo( targetSurface, x, y ) ;
01025 
01026   delete res ;
01027 
01028 }
01029 
01030 
01031 
01032 const string Font::toString( Ceylan::VerbosityLevels level ) const
01033 {
01034 
01035   string res = "Rendering style: " ;
01036 
01037   if ( _renderingStyle == Normal )
01038     res += "normal" ;
01039   else
01040   {
01041     std::list<string> listRes ;
01042 
01043     if ( _renderingStyle & Bold )
01044       listRes.push_back( "bold" ) ;
01045 
01046     if ( _renderingStyle & Italic )
01047       listRes.push_back( "italic" ) ;
01048 
01049     if ( _renderingStyle & Underline )
01050       listRes.push_back( "underline" ) ;
01051 
01052     res += Ceylan::join( listRes, ", " ) ;
01053 
01054   }
01055 
01056   res += ". Renderings (and caches if activated) are " ;
01057 
01058   if ( ! _convertToDisplay )
01059     res += "not " ;
01060 
01061   res += "automatically converted to display. " ;
01062 
01063   switch( _cacheSettings )
01064   {
01065 
01066   case None:
01067     res += "No render cache used" ;
01068     break ;
01069 
01070   case GlyphCached:
01071     res += "Glyph renderings are cached" ;
01072     break ;
01073 
01074   case WordCached:
01075     res += "Word renderings are cached" ;
01076     break ;
01077 
01078   case TextCached:
01079     res += "Text renderings are cached" ;
01080     break ;
01081 
01082   default:
01083     res += "Unknown policy for render cache (abnormal)" ;
01084     break ;
01085 
01086   }
01087 
01088 
01089   if ( level == Ceylan::low )
01090     return res ;
01091 
01092 
01093   
01094   switch( _cacheSettings )
01095   {
01096 
01097   case None:
01098     break ;
01099 
01100   case GlyphCached:
01101     res += ". Glyph cache state is: "
01102       + _glyphCache->toString( level ) ;
01103     break ;
01104 
01105   case WordCached:
01106     res += ". Word cache state is: " + _textCache->toString( level )  ;
01107     break ;
01108 
01109   case TextCached:
01110     res += ". Text cache state is: " + _textCache->toString( level )  ;
01111     break ;
01112 
01113   default:
01114     break ;
01115 
01116   }
01117 
01118   return res ;
01119 
01120 }
01121 
01122 
01123 
01124 string Font::InterpretRenderingStyle( RenderingStyle style )
01125 {
01126 
01127   if ( style == Normal )
01128     return "normal" ;
01129 
01130   std::list<string> res ;
01131 
01132   if ( style & Bold )
01133     res.push_back( "bold" ) ;
01134 
01135   if ( style & Italic )
01136     res.push_back( "italic" ) ;
01137 
01138   if ( style & Underline )
01139     res.push_back( "underline" ) ;
01140 
01141   return Ceylan::join( res, ", " ) ;
01142 
01143 }
01144 
01145 
01146 OSDL::Video::Surface & Font::renderLatin1TextWithWordCached(
01147   const string & text, RenderQuality quality,
01148   Pixels::ColorDefinition textColor )
01149 {
01150 
01151   
01152 
01153 
01154 
01155 
01156 
01157 
01158 
01159   
01160   list<string> words = Ceylan::split( text, ' ' ) ;
01161 
01162 
01163   Length currentWidth = 0 ;
01164 
01165 #if OSDL_DEBUG_FONT
01166 
01167   LogPlug::debug( "Font::renderLatin1TextWithWordCached: will render '"
01168     + text + "', space width is " + Ceylan::toString( _spaceWidth ) ) ;
01169 
01170 #endif // OSDL_DEBUG_FONT
01171 
01172   const Surface * wordRendered ;
01173 
01174   
01175 
01176 
01177 
01178 
01179   for ( list<string>::const_iterator it = words.begin();
01180         it != words.end(); it++ )
01181   {
01182 
01183     if ( (*it).empty() )
01184     {
01185 
01186 #if OSDL_DEBUG_FONT
01187 
01188       LogPlug::debug(
01189         "Font::renderLatin1TextWithWordCached: jumping a space." ) ;
01190 
01191 #endif // OSDL_DEBUG_FONT
01192 
01193       currentWidth += _spaceWidth ;
01194       continue ;
01195     }
01196 
01197 #if OSDL_DEBUG_FONT
01198 
01199     LogPlug::debug( "Font::renderLatin1TextWithWordCached: examining '"
01200       + (*it) + "'." ) ;
01201 
01202 #endif // OSDL_DEBUG_FONT
01203 
01204     
01205 
01206 
01207 
01208 
01209     wordRendered = & getConstLatin1WordFromCache( (*it), quality,
01210       textColor ) ;
01211 
01212     currentWidth += wordRendered->getWidth() + _spaceWidth ;
01213 
01214   }
01215 
01216 
01217   
01218 
01219   ColorMask redMask, greenMask, blueMask ;
01220   Pixels::getRecommendedColorMasks( redMask, greenMask, blueMask ) ;
01221 
01222   Surface & res = * new Surface( Surface::Hardware | Surface::ColorkeyBlit,
01223     currentWidth, getLineSkip() - getDescent(), 32,
01224     redMask, greenMask, blueMask,  0 ) ;
01225 
01226   
01227   Pixels::ColorDefinition colorKey ;
01228 
01229   if ( Pixels::areEqual( textColor, Pixels::Black,  false ) )
01230   {
01231 
01232     colorKey = Pixels::White ;
01233     res.fill( colorKey ) ;
01234 
01235   }
01236   else
01237   {
01238 
01239     colorKey = Pixels::Black ;
01240     
01241 
01242 
01243 
01244 
01245 
01246   }
01247 
01248   currentWidth = 0 ;
01249 
01250   
01251 
01252 
01253 
01254 
01255 
01256 
01257 
01258 
01259   for ( list<string>::const_iterator it = words.begin();
01260         it != words.end(); it++ )
01261   {
01262 
01263     if ( (*it).empty() )
01264     {
01265       currentWidth += _spaceWidth ;
01266       continue ;
01267     }
01268 
01269 #if OSDL_DEBUG_FONT
01270 
01271     LogPlug::debug( "Font::renderLatin1TextWithWordCached: blitting '"
01272       + (*it) + "'." ) ;
01273 
01274 #endif // OSDL_DEBUG_FONT
01275 
01276     wordRendered = & getConstLatin1WordFromCache( (*it),
01277       quality, textColor ) ;
01278     wordRendered->blitTo( res, currentWidth, 0 ) ;
01279     currentWidth += wordRendered->getWidth() + _spaceWidth ;
01280 
01281   }
01282 
01283 
01284   
01285   res.setColorKey( Surface::ColorkeyBlit | Surface::RLEColorkeyBlit,
01286     convertColorDefinitionToPixelColor( res.getPixelFormat(), colorKey ) ) ;
01287 
01288   
01289 
01290 
01291 
01292 
01293   
01294 
01295   if ( _convertToDisplay )
01296   {
01297 
01298     
01299 
01300 
01301 
01302 
01303     res.convertToDisplay(  false ) ;
01304   }
01305 
01306   return res ;
01307 
01308 }
01309 
01310 
01311 
01312 OSDL::Video::Surface & Font::renderLatin1TextWithTextCached(
01313   const string & text, RenderQuality quality,
01314   Pixels::ColorDefinition textColor )
01315 {
01316 
01317 #if OSDL_DEBUG_FONT
01318 
01319   LogPlug::trace( "Font::renderLatin1TextWithTextCached" ) ;
01320 
01321 #endif // OSDL_DEBUG_FONT
01322 
01323   
01324 
01325 
01326 
01327 
01328 
01329   StringColorQualityKey renderKey( text, textColor, quality ) ;
01330 
01331   SmartResource * res = _textCache->getClone( renderKey ) ;
01332 
01333 
01334   if ( res != 0 )
01335   {
01336 
01337     Surface * returned = dynamic_cast<Surface *>( res ) ;
01338 
01339 #if OSDL_DEBUG_FONT
01340 
01341     LogPlug::debug( "Font::renderLatin1TextWithTextCached: cache hit, "
01342       "returning clone of prerendered text." ) ;
01343 
01344     if ( returned == 0 )
01345       Ceylan::emergencyShutdown( "Font::renderLatin1TextWithTextCached: "
01346         "clone is not a Surface." ) ;
01347 
01348 #endif // OSDL_DEBUG_FONT
01349 
01350     return * returned ;
01351 
01352   }
01353 
01354 #if OSDL_DEBUG_FONT
01355   LogPlug::debug( "Font::renderLatin1TextWithTextCached: "
01356     "cache miss, creating new text rendering." ) ;
01357 #endif // OSDL_DEBUG_FONT
01358 
01359   
01360   Surface & newSurface = basicRenderLatin1Text( text, quality, textColor ) ;
01361 
01362   
01363   _textCache->scanForAddition( renderKey, newSurface ) ;
01364 
01365   return newSurface  ;
01366 
01367 }
01368 
01369 
01370 
01371 void Font::blitLatin1Word( Surface & targetSurface, Coordinate x, Coordinate y,
01372   const std::string & word, RenderQuality quality,
01373   Pixels::ColorDefinition wordColor )
01374 {
01375 
01376 
01377   
01378 
01379 
01380 
01381 
01382 
01383 
01384 
01385 
01386   
01387 
01388 
01389 
01390 
01391   
01392 
01393 #ifdef OSDL_WORD_LOOKUP_IN_TEXT_CACHE
01394   if ( _cacheSettings == WordCached || _cacheSettings == TextCached )
01395 #else // OSDL_WORD_LOOKUP_IN_TEXT_CACHE
01396     if ( _cacheSettings == WordCached )
01397 #endif // OSDL_WORD_LOOKUP_IN_TEXT_CACHE
01398     {
01399 
01400       getConstLatin1WordFromCache( word, quality, wordColor ).blitTo(
01401         targetSurface, x, y ) ;
01402 
01403     }
01404     else
01405     {
01406       
01407       basicRenderLatin1Text( word, quality, wordColor ).blitTo(
01408         targetSurface, x, y ) ;
01409     }
01410 
01411 }
01412 
01413 
01414 
01415 
01416 
01417 
01418 
01419 
01420 
01421 
01422 
01423 
01424 
01425 
01426 
01427 
01428 
01429 
01430 
01431 
01432 
01433 
01434 
01435 
01436 
01437 
01438 
01439 const OSDL::Video::Surface & Font::getConstLatin1WordFromCache(
01440   const std::string & word, RenderQuality quality,
01441   Pixels::ColorDefinition wordColor )
01442 {
01443 
01444   
01445 
01446   StringColorQualityKey renderKey( word, wordColor, quality ) ;
01447 
01448   const Resource * inCache = _textCache->get( renderKey ) ;
01449 
01450   if ( inCache != 0 )
01451   {
01452 
01453     
01454     const Surface * wordSurface = dynamic_cast<const Surface *>( inCache ) ;
01455 
01456 #if OSDL_DEBUG_FONT
01457 
01458     LogPlug::debug( "Font::getConstLatin1WordFromCache: "
01459       "cache hit for '" + word
01460       + "', returning 'const' prerendered word." ) ;
01461 
01462     if ( wordSurface == 0 )
01463       Ceylan::emergencyShutdown( "Font::getConstLatin1WordFromCache: "
01464         "cache did not return a Surface." ) ;
01465 
01466 #endif // OSDL_DEBUG_FONT
01467 
01468     return * wordSurface ;
01469 
01470   }
01471 
01472 #if OSDL_DEBUG_FONT
01473 
01474   LogPlug::debug( "Font::getConstLatin1WordFromCache: "
01475     "cache miss for '" + word
01476     + "', rendering it and submitting it to cache." ) ;
01477 
01478 #endif // OSDL_DEBUG_FONT
01479 
01480   
01481   Surface & wordSurface = basicRenderLatin1Text( word, quality, wordColor ) ;
01482 
01483   try
01484   {
01485     _textCache->takeOwnershipOf( renderKey, wordSurface ) ;
01486   }
01487   catch( const ResourceManagerException & e )
01488   {
01489 
01490     
01491 
01492 
01493 
01494 
01495     throw FontException( "Font::getConstLatin1WordFromCache: "
01496       "cache submitting failed (abnormal): " + e.toString() ) ;
01497 
01498   }
01499 
01500   
01501   return wordSurface ;
01502 
01503 }
01504 
01505 
01506 
01507 OSDL::Video::Surface & Font::basicRenderLatin1Text( const std::string & text,
01508   RenderQuality quality, Pixels::ColorDefinition textColor )
01509 {
01510 
01511   Length lineSkip = getLineSkip() ;
01512   Length ascent   = getAscent() ;
01513 
01514   System::Size textSize = text.size() ;
01515 
01516 
01517 #if OSDL_DEBUG_FONT
01518 
01519   LogPlug::debug( "Font::basicRenderLatin1Text: rendering '" + text + "'." ) ;
01520 
01521   LogPlug::debug( "Font::basicRenderLatin1Text: line skip is "
01522     + Ceylan::toString( lineSkip ) ) ;
01523 
01524   LogPlug::debug( "Font::basicRenderLatin1Text: ascent is "
01525     + Ceylan::toString( ascent ) ) ;
01526 
01527   LogPlug::debug( "Font::basicRenderLatin1Text: descent is "
01528     + Ceylan::toString( getDescent() ) ) ;
01529 
01530   LogPlug::debug( "Font::basicRenderLatin1Text: height is "
01531     + Ceylan::toString( getHeight() ) ) ;
01532 
01533 #endif // OSDL_DEBUG_FONT
01534 
01535   Length * horizSteps = new Length[ textSize ] ;
01536 
01537 
01538   
01539 
01540 
01541 
01542 
01543 
01544 
01545 
01546 
01547 
01548 
01549 
01550 
01551 
01552 
01553 
01554   Length width = 0 ;
01555   Length maxWidth = 0 ;
01556   System::Size charCount = 0 ;
01557 
01558   bool firstLetter = true ;
01559 
01560   Ceylan::Latin1Char currentChar = 0 ;
01561   SignedLength       currentOffset = 0 ;
01562   Length             currentAdvance ;
01563 
01564   
01565 
01566 
01567 
01568 
01569   for ( string::const_iterator it = text.begin(); it != text.end(); it++ )
01570   {
01571 
01572     currentChar    = static_cast<Ceylan::Latin1Char>(*it) ;
01573     currentOffset  = getWidthOffset( currentChar ) ;
01574     currentAdvance = getAdvance( currentChar ) ;
01575 
01576     
01577 
01578 
01579 
01580 
01581 
01582     if ( firstLetter )
01583     {
01584 
01585       firstLetter = false ;
01586 
01587       
01588       horizSteps[charCount] = 0 ;
01589 
01590     }
01591     else
01592     {
01593 
01594       
01595 
01596 
01597 
01598 
01599       horizSteps[charCount] = width + currentOffset ;
01600 
01601     }
01602 
01603     charCount++ ;
01604 
01605 #if OSDL_DEBUG_FONT
01606 
01607     LogPlug::debug( "Font::basicRenderLatin1Text: adding "
01608       + Ceylan::toString( currentAdvance )
01609       + " width for char '" +  Ceylan::toString( currentChar ) + "'" ) ;
01610 
01611 #endif // OSDL_DEBUG_FONT
01612 
01613     
01614     width += currentAdvance ;
01615 
01616     
01617 
01618 
01619 
01620 
01621 
01622 
01623 
01624     
01625 
01626   }
01627 
01628   
01629 
01630 
01631 
01632 
01633 
01634 
01635   maxWidth = horizSteps[ textSize - 1 ] + currentOffset
01636     + Ceylan::Maths::Max<Ceylan::Sint32>( getWidth( currentChar ),
01637       getAdvance( currentChar ) ) ;
01638 
01639 #if OSDL_DEBUG_FONT
01640 
01641   LogPlug::debug( "Font::basicRenderLatin1Text: text width will be "
01642     + Ceylan::toString( maxWidth )
01643     + ", height will be " + Ceylan::toString( lineSkip ) + "." ) ;
01644 
01645 #endif // OSDL_DEBUG_FONT
01646 
01647   
01648 
01649 
01650 
01651 
01652 
01653   ColorMask redMask, greenMask, blueMask ;
01654   Pixels::getRecommendedColorMasks( redMask, greenMask, blueMask ) ;
01655 
01656   Surface * res ;
01657 
01658   try
01659   {
01660     res = new Surface( Surface::Hardware | Surface::ColorkeyBlit,
01661       maxWidth, lineSkip - getDescent(), 32,
01662       redMask, greenMask, blueMask,  0 ) ;
01663   }
01664   catch( const VideoException & e )
01665   {
01666     delete [] horizSteps ;
01667     throw FontException(
01668       "Font::basicRenderLatin1Text: surface creation failed: "
01669       + e.toString() ) ;
01670   }
01671 
01672   
01673   Pixels::ColorDefinition colorKey ;
01674 
01675   if ( Pixels::areEqual( textColor, Pixels::Black,  false ) )
01676   {
01677     colorKey = Pixels::White ;
01678     res->fill( colorKey ) ;
01679   }
01680   else
01681   {
01682     colorKey = Pixels::Black ;
01683     
01684 
01685 
01686 
01687 
01688   }
01689 
01690 
01691   
01692   charCount = 0 ;
01693 
01694   for ( string::const_iterator it = text.begin(); it != text.end(); it++ )
01695   {
01696 
01697     currentChar = static_cast<Ceylan::Latin1Char>(*it) ;
01698 
01699 #if OSDL_DEBUG_FONT
01700 
01701     LogPlug::debug( "Font::basicRenderLatin1Text: rendering '"
01702       + Ceylan::toString( currentChar )
01703       + "' at width "  + Ceylan::toString( horizSteps[charCount] )
01704       + ", at height "
01705       + Ceylan::toString( ascent - getHeightAboveBaseline( currentChar ) )
01706                     ) ;
01707 
01708 #endif // OSDL_DEBUG_FONT
01709 
01710     blitLatin1Glyph( *res, horizSteps[charCount],
01711       ascent - getHeightAboveBaseline( currentChar ),
01712       currentChar, quality, textColor ) ;
01713 
01714 #if OSDL_DEBUG_FONT
01715 
01716     LogPlug::debug( "Font::basicRenderLatin1Text: rendered '"
01717       + Ceylan::toString( currentChar )
01718       + "' at width "  + Ceylan::toString( horizSteps[charCount] )
01719       + ", at height "
01720       + Ceylan::toString( ascent - getHeightAboveBaseline( currentChar ) )
01721                     ) ;
01722 
01723 #endif // OSDL_DEBUG_FONT
01724 
01725     charCount++ ;
01726 
01727   }
01728 
01729   
01730   res->setColorKey( Surface::ColorkeyBlit | Surface::RLEColorkeyBlit,
01731     convertColorDefinitionToPixelColor( res->getPixelFormat(), colorKey )
01732                     ) ;
01733 
01734   if ( _convertToDisplay )
01735   {
01736 
01737     
01738 
01739 
01740 
01741 
01742     res->convertToDisplay(  false ) ;
01743   }
01744 
01745   
01746 
01747 
01748 
01749 
01750   
01751 
01752   delete [] horizSteps ;
01753 
01754   return *res ;
01755 
01756 }