OSDLTrueTypeFont.cc

Go to the documentation of this file.
00001 #include "OSDLTrueTypeFont.h"
00002 
00003 #include "OSDLSurface.h"         // for Surface
00004 #include "OSDLPixel.h"           // for ColorDefinition
00005 
00006 #include "Ceylan.h"              // for Uint32, inheritance
00007 
00008 #include "SDL_gfxPrimitives.h"   // for stringColor
00009 #include "SDL.h"                 // for SDL_Surface
00010 
00011 #include <list>
00012  
00013  
00014 
00015 
00016 using namespace OSDL::Video::TwoDimensional ;
00017 using namespace OSDL::Video::TwoDimensional::Text ;
00018 
00019 using namespace Ceylan ;
00020 using namespace Ceylan::Log ;
00021 
00022 using std::string ;
00023 
00024 
00025 #ifdef OSDL_USES_CONFIG_H
00026 #include <OSDLConfig.h>          // for OSDL_DEBUG_FONT and al 
00027 #endif // OSDL_USES_CONFIG_H
00028 
00029 
00030 Ceylan::System::FileLocator Text::TrueTypeFont::TrueTypeFontFileLocator ;
00031 
00032 string Text::TrueTypeFont::TrueTypeFontFileExtension = ".ttf" ;
00033 
00034 
00035 const Ceylan::Float32 Text::TrueTypeFont::SpaceWidthFactor = 1 ;
00036 
00037 
00038 // Used to know when the SDL_TTF module can be stopped.
00039 Ceylan::Uint32 TrueTypeFont::FontCounter = 0 ;
00040 
00041 
00042 
00043 TrueTypeFont::TrueTypeFont( 
00044     const std::string & fontFilename, 
00045     PointSize pointSize, 
00046     FontIndex index, 
00047     bool convertToDisplay, 
00048     RenderCache cacheSettings ) 
00049             throw( TextException ) :
00050     Font( convertToDisplay, cacheSettings ),    
00051     _pointSize( pointSize ),
00052     _actualFont( 0 )
00053 {
00054     
00055     string fontFullPath = fontFilename ;
00056     
00057     // Search directly in current working directory :
00058     if ( ! System::File::ExistsAsFileOrSymbolicLink( fontFilename ) )
00059     {
00060         
00061         // On failure use the dedicated TrueType font locator :
00062         try
00063         {
00064         
00065             fontFullPath = TrueTypeFont::TrueTypeFontFileLocator.find(
00066                 fontFilename ) ;
00067                 
00068         }
00069         catch( const System::FileLocatorException & e )
00070         {
00071                 
00072             // Last hope is general font locator :
00073             try
00074             {
00075                 fontFullPath = Font::FontFileLocator.find( fontFilename ) ;
00076             }
00077             catch( const System::FileLocatorException & ex )
00078             {
00079                 
00080                 // Not found !
00081                 
00082                 string currentDir ;
00083                 
00084                 try
00085                 {
00086                     currentDir =
00087                         System::Directory::GetCurrentWorkingDirectoryName() ;
00088                 }
00089                 catch( const System::Directory::DirectoryException & exc )
00090                 {
00091                     throw TextException( 
00092                         "TrueTypeFont constructor : unable to load '" 
00093                         + fontFilename 
00094                         + "', exception generation triggered another failure : "
00095                         + exc.toString() + "." ) ;
00096                 }
00097                 
00098                 throw TextException( "TrueTypeFont constructor : '" 
00099                     + fontFilename 
00100                     + "' is not a regular file or a symbolic link "
00101                     "relative to the current directory (" + currentDir
00102                     + ") and cannot be found through TrueType font locator ("
00103                     + TrueTypeFont::TrueTypeFontFileLocator.toString() 
00104                     + ") nor through general font locator based on "
00105                     "font path environment variable ("
00106                     + Font::FontPathEnvironmentVariable + ") : " 
00107                     + Font::FontFileLocator.toString() + "." ) ;
00108                     
00109             }       
00110         }       
00111     }
00112     
00113     
00114     if ( TTF_WasInit() == 0 )
00115     {
00116     
00117         if ( TTF_Init()== -1 )
00118             throw TextException( 
00119                 "TrueTypeFont constructor : unable to init font library : "
00120                 + DescribeLastError() ) ;
00121                 
00122     }   
00123     
00124     _actualFont = TTF_OpenFont( fontFullPath.c_str(), pointSize ) ;
00125     
00126     if ( _actualFont == 0 )
00127         throw TextException( "TrueTypeFont constructor : unable to open '" 
00128             + fontFullPath 
00129             + "' with a point size of " 
00130             + Ceylan::toString( pointSize ) + " dots per inch : "
00131             + DescribeLastError() ) ;
00132     
00133     _spaceWidth = static_cast<Width>( SpaceWidthFactor * getWidth( ' ' ) ) ;
00134 
00135     // By default, the width of an alinea is a multiple of a space width :
00136     _alineaWidth = DefaultSpaceBasedAlineaWidth * _spaceWidth ; 
00137     
00138     FontCounter++ ;
00139     
00140 }
00141 
00142 
00143 TrueTypeFont::~TrueTypeFont() throw()
00144 {
00145 
00146     if ( _actualFont != 0 )
00147         TTF_CloseFont( _actualFont ) ;
00148         
00149     FontCounter-- ;
00150     
00151     if ( FontCounter == 0 && TTF_WasInit() != 0 )
00152         TTF_Quit() ;    
00153         
00154 }
00155 
00156 
00157 
00158 PointSize TrueTypeFont::getPointSize() const throw()
00159 {
00160 
00161     return _pointSize ;
00162     
00163 }
00164 
00165 
00166 RenderingStyle TrueTypeFont::getRenderingStyle() const throw()
00167 {
00168 
00169     return TTF_GetFontStyle( _actualFont ) ;
00170     
00171 }
00172 
00173 
00174 void TrueTypeFont::setRenderingStyle( RenderingStyle newStyle ) 
00175     throw( TextException )
00176 {
00177 
00178 
00179     // Cannot guess whether the specified style is supported.
00180 
00181     /* 
00182      * It would flush the internal cache of previously rendered glyphs, 
00183      * even if there is no change in style otherwise :
00184      *
00185      */
00186      
00187     if ( newStyle != getRenderingStyle() )
00188         TTF_SetFontStyle( _actualFont, newStyle ) ;
00189         
00190 }   
00191 
00192      
00193 Width TrueTypeFont::getWidth( Ceylan::Latin1Char character ) 
00194     const throw( TextException )
00195 {
00196 
00197     /*
00198      * Not using the advance parameter except for space, whose width 
00199      * would be zero otherwise.
00200      *
00201      */
00202  
00203     if ( character == ' ' )
00204         return getAdvance( ' ' ) ;
00205         
00206     int minX, maxX ;
00207     
00208     if ( TTF_GlyphMetrics( _actualFont,
00209             Ceylan::UnicodeString::ConvertFromLatin1( character), 
00210             & minX, & maxX, 0, 0, 0 )  != 0 )
00211         throw TextException( "TrueTypeFont::getWidth : " 
00212             + DescribeLastError() ) ;
00213 
00214     return static_cast<Width>( maxX - minX ) ;  
00215     
00216 }
00217 
00218 
00219 SignedWidth TrueTypeFont::getWidthOffset( Ceylan::Latin1Char character ) 
00220     const throw( TextException )
00221 {
00222 
00223     int minX ;
00224     
00225     if ( TTF_GlyphMetrics( _actualFont,
00226             Ceylan::UnicodeString::ConvertFromLatin1( character), 
00227             & minX, 0, 0, 0, 0 )  != 0 )
00228         throw TextException( "TrueTypeFont::getWidthOffset : " 
00229             + DescribeLastError() ) ;
00230             
00231     return static_cast<SignedWidth>( minX ) ;   
00232 
00233 }
00234 
00235                             
00236 SignedHeight TrueTypeFont::getHeightAboveBaseline( 
00237     Ceylan::Latin1Char character ) const throw( TextException )
00238 {
00239 
00240     int maxY ;
00241     
00242     if ( TTF_GlyphMetrics( _actualFont,
00243             Ceylan::UnicodeString::ConvertFromLatin1( character), 
00244             0, 0, 0, & maxY, 0 )  != 0 )
00245         throw TextException( "TrueTypeFont::getHeightAboveBaseline : " 
00246             + DescribeLastError() ) ;
00247     
00248     return static_cast<SignedHeight>( maxY ) ;
00249     
00250 }
00251 
00252 
00253 OSDL::Video::SignedLength TrueTypeFont::getAdvance( 
00254     Ceylan::Latin1Char character ) const throw( TextException )
00255 {
00256 
00257     int advance ;
00258     
00259     if ( TTF_GlyphMetrics( _actualFont,
00260         Ceylan::UnicodeString::ConvertFromLatin1( character), 
00261             0, 0, 0, 0, & advance ) != 0 )
00262         throw TextException( "TrueTypeFont::getAdvance : "
00263             + DescribeLastError() ) ;
00264     
00265     return static_cast<SignedLength>( advance ) ;
00266     
00267 }
00268 
00269 
00270 Text::Height TrueTypeFont::getHeight() const throw()
00271 {
00272 
00273     return TTF_FontHeight( _actualFont ) ;
00274     
00275 }
00276 
00277 
00278 Text::SignedHeight TrueTypeFont::getAscent() const throw()
00279 {
00280 
00281     return TTF_FontAscent( _actualFont ) ;
00282     
00283 }
00284 
00285 
00286 Text::SignedHeight TrueTypeFont::getDescent() const throw()
00287 {
00288 
00289     return TTF_FontDescent( _actualFont ) ;
00290     
00291 }
00292 
00293 
00294 Text::Height TrueTypeFont::getLineSkip() const throw()
00295 {
00296 
00297     return TTF_FontLineSkip( _actualFont ) ;
00298     
00299 }
00300 
00301 
00302 Ceylan::Uint16 TrueTypeFont::getFacesCount() const throw()
00303 {
00304 
00305     return TTF_FontFaces( _actualFont ) ;
00306     
00307 }
00308 
00309 
00310 bool TrueTypeFont::isFixedWidth() const throw()
00311 {
00312 
00313     return ( TTF_FontFaceIsFixedWidth( _actualFont ) > 0 ) ;
00314     
00315 }
00316 
00317 
00318 string TrueTypeFont::getFaceFamilyName() const throw()
00319 {
00320 
00321     return TTF_FontFaceFamilyName( _actualFont ) ;
00322     
00323 }
00324 
00325 
00326 string TrueTypeFont::getFaceStyleName() const throw()
00327 {
00328 
00329     return TTF_FontFaceStyleName( _actualFont ) ;
00330     
00331 }
00332 
00333 
00334 
00335 // Bounding boxes section.
00336 
00337 
00338 UprightRectangle & TrueTypeFont::getBoundingBoxFor( 
00339         Ceylan::Unicode glyph, SignedLength & advance ) 
00340     const throw( TextException )
00341 {
00342 
00343     int minX, maxX, minY, maxY, intAdvance ;
00344 
00345     if ( TTF_GlyphMetrics( _actualFont, glyph, & minX, & maxX, 
00346             & minY, & maxY, & intAdvance ) != 0 )
00347         throw TextException( "TrueTypeFont::getClippingBoxFor (glyph) : " 
00348             + DescribeLastError() ) ;
00349     
00350     advance = static_cast<SignedLength>( intAdvance ) ;
00351     
00352     return * new UprightRectangle( 
00353         static_cast<Coordinate>( minX ), 
00354         static_cast<Coordinate>( maxY ),
00355         static_cast<Length>( maxX - minX ), 
00356         static_cast<Length>( maxY - minY ) ) ;
00357          
00358 }
00359 
00360 
00361 UprightRectangle & TrueTypeFont::getBoundingBoxFor( const std::string & text )
00362     const throw( TextException )
00363 {
00364 
00365     int width, height ;
00366     
00367     if ( TTF_SizeText( _actualFont, text.c_str(), & width, & height ) != 0 )
00368         throw TextException( 
00369             "TrueTypeFont::getBoundingBoxFor (Latin-1 string) : " 
00370             + DescribeLastError() ) ;
00371     
00372     return * new UprightRectangle( 0, 0, static_cast<Length>( width ), 
00373         static_cast<Length>( height ) ) ;   
00374         
00375 }
00376 
00377 
00378 UprightRectangle & TrueTypeFont::getBoundingBoxForUTF8( 
00379     const std::string & text ) const throw( TextException )
00380 {
00381 
00382     int width, height ;
00383     
00384     if ( TTF_SizeUTF8( _actualFont, text.c_str(), & width, & height ) != 0 )
00385         throw TextException( 
00386             "TrueTypeFont::getBoundingBoxFor (UTF-8 string) : " 
00387             + DescribeLastError() ) ;
00388     
00389     return * new UprightRectangle( 0, 0, static_cast<Length>( width ), 
00390         static_cast<Length>( height ) ) ;   
00391         
00392 }
00393 
00394 
00395 UprightRectangle & TrueTypeFont::getBoundingBoxForUnicode( 
00396     const Ceylan::Unicode * text ) const throw( TextException )
00397 {
00398 
00399     if ( text == 0 )
00400         throw TextException( "TrueTypeFont::getBoundingBoxForUnicode : "
00401             "null pointer for Unicode string." ) ;
00402 
00403     int width, height ;
00404     
00405     if ( TTF_SizeUNICODE( _actualFont, text, & width, & height ) != 0 )
00406         throw TextException( 
00407             "TrueTypeFont::getBoundingBoxFor (Unicode string) : " 
00408             + DescribeLastError() ) ;
00409     
00410     return * new UprightRectangle( 0, 0, static_cast<Length>( width ), 
00411         static_cast<Length>( height ) ) ;   
00412         
00413 }
00414 
00415 
00416 
00417 // Render section.
00418 
00419 
00420 OSDL::Video::Surface & TrueTypeFont::renderLatin1Glyph( 
00421         Ceylan::Latin1Char character, 
00422         RenderQuality quality, 
00423         Pixels::ColorDefinition glyphColor ) 
00424     throw( TextException )
00425 {
00426     
00427     return renderUnicodeGlyph( 
00428         Ceylan::UnicodeString::ConvertFromLatin1( character ),
00429         quality, glyphColor ) ;
00430         
00431 }
00432 
00433 
00434 void TrueTypeFont::blitLatin1Glyph( 
00435     Surface & targetSurface, Coordinate x, Coordinate y, 
00436     Ceylan::Latin1Char character, RenderQuality quality, 
00437     Pixels::ColorDefinition glyphColor ) throw( TextException )
00438 {
00439 
00440     // Could be easily optimized (even though Freetype has a cache feature) :
00441     
00442     Surface & res = renderUnicodeGlyph(
00443         Ceylan::UnicodeString::ConvertFromLatin1( character ),
00444         quality, glyphColor ) ;
00445         
00446     res.blitTo( targetSurface, x, y ) ;
00447     
00448     delete & res ;
00449         
00450 }
00451 
00452 
00453 OSDL::Video::Surface & TrueTypeFont::renderUnicodeGlyph( 
00454     Ceylan::Unicode character, 
00455     RenderQuality quality, 
00456     Pixels::ColorDefinition glyphColor ) throw( TextException )
00457 {
00458 
00459     // Two different cases, depending on a glyph cache being used or not :
00460     
00461     if ( _cacheSettings == GlyphCached )
00462     {
00463         
00464         /*
00465          * First check that the character-quality-color combination is 
00466          * not already available in cache :
00467          *
00468          */
00469         
00470         CharColorQualityKey renderKey( character, glyphColor, quality ) ;
00471             
00472         SmartResource * res = _glyphCache->getClone( renderKey ) ;
00473         
00474         if ( res != 0 )
00475         {
00476         
00477             Surface * returned = dynamic_cast<Surface *>( res ) ;
00478             
00479 #if OSDL_DEBUG_FONT
00480 
00481             LogPlug::debug( "TrueTypeFont::renderUnicodeGlyph : cache hit, "
00482                 "returning clone of prerendered glyph." ) ;
00483             
00484             if ( returned == 0 )
00485                 Ceylan::emergencyShutdown( "TrueTypeFont::renderUnicodeGlyph : "
00486                     "clone is not a Surface." ) ;
00487                     
00488 #endif // OSDL_DEBUG_FONT
00489             
00490             return * returned ;
00491             
00492         }   
00493         
00494 #if OSDL_DEBUG_FONT
00495 
00496         LogPlug::debug( "TrueTypeFont::renderUnicodeGlyph : "
00497             "cache miss, creating new glyph rendering." ) ;
00498             
00499 #endif // OSDL_DEBUG_FONT
00500 
00501         // Here it its a cache miss, we therefore have to generate the glyph :
00502         Surface & newSurface = basicRenderUnicodeGlyph( character, quality,
00503             glyphColor ) ;
00504         
00505         // Give the cache a chance of being fed :       
00506         _glyphCache->scanForAddition( renderKey, newSurface ) ;
00507         
00508         
00509         return newSurface ;
00510                     
00511     }
00512 
00513     /*
00514      * Here we are not using a glyph cache, we have simply to generate 
00515      * the glyph :  
00516      *
00517      */
00518     return basicRenderUnicodeGlyph( character, quality, glyphColor ) ;  
00519 
00520 }
00521 
00522 
00523 
00524 
00525 OSDL::Video::Surface & TrueTypeFont::renderLatin1Text( 
00526         const std::string & text, 
00527         RenderQuality quality, Pixels::ColorDefinition textColor )
00528     throw( TextException )
00529 {
00530 
00531 
00532     /*
00533      * Test to compare both implementations 
00534      * (OSDL glyph-based versus SDL_ttf whole string) : 
00535      *  - next line uncommented : OSDL glyph-based 
00536      *  - next line commented : SDL_ttf whole string
00537      *
00538      * Whole string (this actual method) may be preferred since rendering 
00539      * is maybe a bit better (maybe kerning better supported, alpha 
00540      * specifically managed for each quality, etc.).
00541      *
00542      * However both methods look most of the time the same, pixel-perfect 
00543      * wise.
00544      *
00545      * Default selected implementation (when next line not commented) is 
00546      * the OSDL's one :
00547      *
00548      */  
00549     return Font::renderLatin1Text( text, quality, textColor ) ;
00550     
00551     
00552     /*
00553      * Following SDL_ttf whole string rendering deactivated if previous 
00554      * line not commented :
00555      *
00556      */
00557     
00558     SDL_Surface * textSurface ;
00559     Surface * res ;
00560     
00561     switch( quality )
00562     {   
00563     
00564         case Solid:
00565             textSurface = TTF_RenderText_Solid( _actualFont, 
00566                 text.c_str(), textColor ) ;
00567             if ( textSurface == 0 )
00568                 throw TextException( 
00569                     "TrueTypeFont::renderLatin1Text (solid) : " 
00570                     "unable to render text '" + text
00571                     + "' : " + DescribeLastError() ) ; 
00572             res = new Surface( * textSurface, 
00573                 /* display type */ Surface::BackBuffer ) ;  
00574             // No colorkey added, since already there. 
00575             break ;
00576     
00577     
00578         case Shaded:
00579             textSurface = TTF_RenderText_Shaded( _actualFont, 
00580                 text.c_str(), textColor, _backgroundColor ) ;
00581             if ( textSurface == 0 )
00582                 throw TextException( 
00583                     "TrueTypeFont::renderLatin1Text (shaded) : " 
00584                     "unable to render text '" + text
00585                     + "' : " + DescribeLastError() ) ; 
00586                     
00587             /*
00588              * We have to create a new surface so that the surface returned by
00589              * 'TTF_RenderText_Shaded' will no more be palettized : we need a
00590              * color key to have transparent blits.
00591              *
00592              */  
00593             ColorMask redMask, greenMask, blueMask ;
00594             Pixels::getRecommendedColorMasks( redMask, greenMask, blueMask ) ;
00595     
00596             res = new Surface( Surface::Hardware | Surface::ColorkeyBlit, 
00597                 textSurface->w, textSurface->h, /* bpp */ 32, 
00598                 redMask, greenMask, blueMask, /* no alpha wanted */ 0 ) ;
00599 
00600             // Avoid messing text color with color key :
00601             Pixels::ColorDefinition colorKey ;
00602     
00603             if ( Pixels::areEqual( textColor, Pixels::Black, 
00604                 /* use alpha */ false ) )
00605             {
00606                 colorKey = Pixels::White ;
00607                 res->fill( colorKey ) ;
00608             }   
00609             else
00610             {
00611                 colorKey = Pixels::Black ;
00612                 /*
00613                  * No need to fill 'res' with black, since new RGB 
00614                  * surfaces come all black already.
00615                  *
00616                  */
00617             }   
00618                 
00619             try
00620             {
00621                 res->setColorKey( 
00622                     Surface::ColorkeyBlit | Surface::RLEColorkeyBlit, 
00623                     Pixels::convertColorDefinitionToPixelColor(
00624                         res->getPixelFormat(), colorKey ) ) ;
00625             }
00626             catch( const Video::VideoException & e )
00627             {
00628                 throw TextException( 
00629                     "TrueTypeFont::renderLatin1Text (shaded) : "
00630                     "color keying failed : " + e.toString() ) ; 
00631             }
00632             SDL_BlitSurface( textSurface, 0, & res->getSDLSurface(), 0 ) ;
00633             break ;
00634 
00635 
00636         case Blended:
00637             textSurface = TTF_RenderText_Blended( _actualFont, 
00638                 text.c_str(), textColor ) ;
00639             if ( textSurface == 0 )
00640                 throw TextException( 
00641                     "TrueTypeFont::renderLatin1Text (blended) : " 
00642                     "unable to render text '" + text
00643                     + "' : " + DescribeLastError() ) ; 
00644                     
00645             res = new Surface( * textSurface, 
00646                 /* display type */ Surface::BackBuffer ) ;  
00647 
00648             // Not defined by default :
00649 #ifdef OSDL_ADDS_COLOR_KEY
00650 
00651             /*
00652              * Alpha should be preserved since the blending is the 
00653              * point of this quality, but adding a color key should not harm :
00654              *
00655              */
00656              
00657             // Avoid messing text color with color key :
00658             Pixels::ColorDefinition colorKey ;
00659     
00660             if ( Pixels::areEqual( textColor, Pixels::Black, 
00661                 /* use alpha */ false ) )
00662             {
00663                 colorKey = Pixels::White ;
00664                 res->fill( colorKey ) ;
00665             }   
00666             else
00667             {
00668                 colorKey = Pixels::Black ;
00669                 
00670                 /*
00671                  * No need to fill 'res' with black, since new RGB 
00672                  * surfaces come all black already.
00673                  *
00674                  */
00675             }   
00676             
00677             try
00678             {
00679                 res->setColorKey( 
00680                     Surface::ColorkeyBlit | Surface::RLEColorkeyBlit, 
00681                     Pixels::convertColorDefinitionToPixelColor(
00682                         res->getPixelFormat(), colorKey ) ) ;
00683             }
00684             catch( const Video::VideoException & e )
00685             {
00686                 throw TextException( 
00687                     "TrueTypeFont::renderLatin1Text (blended) : "
00688                     "color keying failed : " + e.toString() ) ; 
00689             }
00690             
00691 #endif // OSDL_ADDS_COLOR_KEY
00692             
00693             break ;
00694     
00695     
00696         default:
00697             throw TextException( "TrueTypeFont::renderLatin1Text : "
00698                 "unknown quality requested : " 
00699                 + Ceylan::toString( quality ) + "." ) ; 
00700             break ; 
00701             
00702     }
00703     
00704 
00705     if ( _convertToDisplay )
00706     {
00707     
00708         /*
00709          * We want to keep our colorkey (if any), so we choose to add 
00710          * alpha only if no colorkey is used.
00711          *
00712          * Surface will be RLE encoded here :
00713          *  
00714          */
00715         if ( quality == Blended )
00716             res->convertToDisplay( /* alphaChannelWanted */ true ) ;
00717         else    
00718             res->convertToDisplay( /* alphaChannelWanted */ false ) ;
00719             
00720     }   
00721 
00722     return * res ;  
00723             
00724 }
00725 
00726 
00727 
00728 
00729 OSDL::Video::Surface & TrueTypeFont::renderUTF8Text( 
00730         const std::string & text, 
00731         RenderQuality quality, 
00732         Pixels::ColorDefinition textColor )
00733     throw( TextException )
00734 {
00735 
00736     SDL_Surface * textSurface ;
00737     Surface * res ;
00738     
00739     switch( quality )
00740     {
00741     
00742     
00743         case Solid:
00744             textSurface = TTF_RenderUTF8_Solid( _actualFont, text.c_str(),
00745                 textColor ) ;
00746                 
00747             if ( textSurface == 0 )
00748                 throw TextException( "TrueTypeFont::renderUTF8Text (solid) : " 
00749                     "unable to render text '" + text
00750                     + "' : " + DescribeLastError() ) ; 
00751                     
00752             res = new Surface( * textSurface, 
00753                 /* display type */ Surface::BackBuffer ) ;  
00754             break ;
00755     
00756     
00757         case Shaded:
00758             textSurface = TTF_RenderUTF8_Shaded( _actualFont, text.c_str(),
00759                 textColor, _backgroundColor ) ;
00760                 
00761             if ( textSurface == 0 )
00762                 throw TextException( "TrueTypeFont::renderUTF8Text (shaded) : " 
00763                     "unable to render text '" + text
00764                     + "' : " + DescribeLastError() ) ; 
00765                     
00766             /*
00767              * We have to create a new surface so that the surface returned by
00768              * 'TTF_RenderUTF8_Shaded' will no more be palettized : 
00769              * we need a color key to have transparent blits.
00770              *
00771              */
00772             ColorMask redMask, greenMask, blueMask ;
00773             Pixels::getRecommendedColorMasks( redMask, greenMask, blueMask ) ;
00774                  
00775             res = new Surface( Surface::Hardware | Surface::ColorkeyBlit, 
00776                 textSurface->w, textSurface->h, 32, 
00777                 redMask, greenMask, blueMask, /* no alpha wanted */ 0 ) ;
00778                 
00779             // Avoid messing text color with color key :
00780             Pixels::ColorDefinition colorKey ;
00781     
00782             if ( Pixels::areEqual( textColor, Pixels::Black, 
00783                 /* use alpha */ false ) )
00784             {
00785             
00786                 colorKey = Pixels::White ;
00787                 res->fill( colorKey ) ;
00788                 
00789             }   
00790             else
00791             {
00792             
00793                 colorKey = Pixels::Black ;
00794                 /*
00795                  * No need to fill 'res' with black, since new RGB surfaces 
00796                  * come all black already.
00797                  *
00798                  */
00799                  
00800             }   
00801                 
00802             try
00803             {
00804             
00805                 res->setColorKey( 
00806                     Surface::ColorkeyBlit | Surface::RLEColorkeyBlit, 
00807                     Pixels::convertColorDefinitionToPixelColor(
00808                         res->getPixelFormat(), colorKey ) ) ;
00809                         
00810             }
00811             catch( const Video::VideoException & e )
00812             {
00813                 throw TextException( "TrueTypeFont::renderUTF8Text (shaded) : "
00814                     "color keying failed : " + e.toString() ) ; 
00815             }
00816             
00817             SDL_BlitSurface( textSurface, 0, & res->getSDLSurface(), 0 ) ;
00818             break ;
00819 
00820 
00821         case Blended:
00822             textSurface = TTF_RenderUTF8_Blended( _actualFont, text.c_str(),
00823                 textColor ) ;
00824                 
00825             if ( textSurface == 0 )
00826                 throw TextException( 
00827                     "TrueTypeFont::renderUTF8Text (blended) : " 
00828                     "unable to render text '" + text
00829                     + "' : " + DescribeLastError() ) ; 
00830                     
00831             res = new Surface( * textSurface, 
00832                 /* display type */ Surface::BackBuffer ) ;  
00833 
00834 
00835             // Not defined by default :
00836 #ifdef OSDL_ADDS_COLOR_KEY
00837 
00838             /*
00839              * Alpha should be preserved since the blending is the 
00840              * point of this quality, but adding a color key should not harm :
00841              *
00842              */
00843              
00844             // Avoid messing text color with color key :
00845             Pixels::ColorDefinition colorKey ;
00846     
00847             if ( Pixels::areEqual( textColor, Pixels::Black, 
00848                 /* use alpha */ false ) )
00849             {
00850             
00851                 colorKey = Pixels::White ;
00852                 res->fill( colorKey ) ;
00853                 
00854             }   
00855             else
00856             {
00857             
00858                 colorKey = Pixels::Black ;
00859                 /*
00860                  * No need to fill 'res' with black, since new RGB 
00861                  * surfaces come all black already.
00862                  *
00863                  */
00864             }   
00865             
00866             try
00867             {
00868                 res->setColorKey( 
00869                     Surface::ColorkeyBlit | Surface::RLEColorkeyBlit, 
00870                     Pixels::convertColorDefinitionToPixelColor(
00871                         res->getPixelFormat(), colorKey ) ) ;
00872             }
00873             catch( const Video::VideoException & e )
00874             {
00875             
00876                 throw TextException( "TrueTypeFont::renderUTF8Text (blended) : "
00877                     "color keying failed : " + e.toString() ) ;
00878                      
00879             }
00880             
00881 #endif // OSDL_ADDS_COLOR_KEY
00882 
00883             break ;
00884     
00885     
00886         default:
00887             throw TextException( "TrueTypeFont::renderUTF8Text : "
00888                 "unknown quality requested : " 
00889                 + Ceylan::toString( quality ) + "." ) ; 
00890             break ; 
00891             
00892     }
00893     
00894     
00895     if ( _convertToDisplay )
00896     {
00897     
00898         /*
00899          * We want to keep our colorkey (if any), so we choose to add 
00900          * alpha only if no colorkey is used.
00901          *
00902          * Surface will be RLE encoded here :
00903          *  
00904          */
00905         if ( quality == Blended )
00906             res->convertToDisplay( /* alphaChannelWanted */ true ) ;
00907         else    
00908             res->convertToDisplay( /* alphaChannelWanted */ false ) ;
00909             
00910     }   
00911 
00912     return * res ;  
00913     
00914 }
00915 
00916 
00917 
00918 OSDL::Video::Surface & TrueTypeFont::renderUnicodeText( 
00919         const Ceylan::Unicode * text, 
00920         RenderQuality quality, 
00921         Pixels::ColorDefinition textColor ) 
00922     throw( TextException )
00923 {
00924 
00925     if ( text == 0 )
00926         throw TextException( 
00927             "TrueTypeFont::renderUnicode : null pointer for Unicode string." ) ;
00928         
00929     SDL_Surface * textSurface ;
00930     Surface * res ;
00931     
00932     switch( quality )
00933     {
00934     
00935         case Solid:
00936             textSurface = TTF_RenderUNICODE_Solid( _actualFont, text, 
00937                 textColor ) ;
00938                 
00939             if ( textSurface == 0 )
00940                 throw TextException( 
00941                     "TrueTypeFont::renderUnicodeText (solid) : " 
00942                     "unable to render text : " + DescribeLastError() ) ; 
00943             res = new Surface( * textSurface, 
00944                 /* display type */ Surface::BackBuffer ) ;  
00945             break ;
00946     
00947     
00948         case Shaded:
00949             textSurface = TTF_RenderUNICODE_Shaded( _actualFont, text,
00950                 textColor, _backgroundColor ) ;
00951                 
00952             if ( textSurface == 0 )
00953                 throw TextException( 
00954                     "TrueTypeFont::renderUnicodeText (shaded) : " 
00955                     "unable to render text : " + DescribeLastError() ) ; 
00956                     
00957             /*
00958              * We have to create a new surface so that the surface returned by
00959              * 'TTF_RenderUNICODE_Shaded' will no more be palettized : 
00960              * we need a color key to have transparent blits.
00961              *
00962              */              
00963             ColorMask redMask, greenMask, blueMask ;
00964             Pixels::getRecommendedColorMasks( redMask, greenMask, blueMask ) ;
00965             
00966             res = new Surface( Surface::Hardware | Surface::ColorkeyBlit, 
00967                 textSurface->w, textSurface->h, 32, 
00968                 redMask, greenMask, blueMask, /* no alpha wanted */ 0 ) ;
00969 
00970             // Avoid messing text color with color key :
00971             Pixels::ColorDefinition colorKey ;
00972     
00973             if ( Pixels::areEqual( textColor, Pixels::Black, 
00974                 /* use alpha */ false ) )
00975             {
00976             
00977                 colorKey = Pixels::White ;
00978                 res->fill( colorKey ) ;
00979                 
00980             }   
00981             else
00982             {
00983             
00984                 colorKey = Pixels::Black ;
00985                 
00986                 /*
00987                  * No need to fill 'res' with black, since new RGB surfaces 
00988                  * come all black already.
00989                  *
00990                  */
00991             }   
00992                 
00993             try
00994             {
00995                 res->setColorKey( 
00996                     Surface::ColorkeyBlit | Surface::RLEColorkeyBlit, 
00997                     Pixels::convertColorDefinitionToPixelColor(
00998                         res->getPixelFormat(), colorKey ) ) ;
00999             }
01000             catch( const Video::VideoException & e )
01001             {
01002                 throw TextException( 
01003                     "TrueTypeFont::renderUnicodeText (shaded) : "
01004                     "color keying failed : " + e.toString() ) ; 
01005             }
01006             SDL_BlitSurface( textSurface, 0, & res->getSDLSurface(), 0 ) ;
01007             break ;
01008 
01009 
01010         case Blended:
01011             textSurface = TTF_RenderUNICODE_Blended( _actualFont, text,
01012                 textColor ) ;
01013                 
01014             if ( textSurface == 0 )
01015                 throw TextException( 
01016                     "TrueTypeFont::renderUnicodeText (blended) : " 
01017                     "unable to render text : " + DescribeLastError() ) ; 
01018                      
01019             res = new Surface( * textSurface, 
01020                 /* display type */ Surface::BackBuffer ) ;
01021 
01022 
01023             // Not defined by default :
01024 #ifdef OSDL_ADDS_COLOR_KEY
01025 
01026             /*
01027              * Alpha should be preserved since the blending is the 
01028              * point of this quality, but adding a color key should not harm :
01029              *
01030              */
01031              
01032             // Avoid messing text color with color key :
01033             Pixels::ColorDefinition colorKey ;
01034     
01035             if ( Pixels::areEqual( textColor, Pixels::Black,
01036                  /* use alpha */ false ) )
01037             {
01038             
01039                 colorKey = Pixels::White ;
01040                 res->fill( colorKey ) ;
01041                 
01042             }   
01043             else
01044             {
01045             
01046                 colorKey = Pixels::Black ;
01047                 
01048                 /*
01049                  * No need to fill 'res' with black, since new RGB 
01050                  * surfaces come all black already.
01051                  *
01052                  */
01053             }   
01054             
01055                 
01056             /*
01057              * Alpha should be preserved since the blending is the point 
01058              * of this quality, but adding a color key should not harm :
01059              *
01060              */
01061             try
01062             {
01063             
01064                 res->setColorKey( 
01065                     Surface::ColorkeyBlit | Surface::RLEColorkeyBlit, 
01066                     Pixels::convertColorDefinitionToPixelColor(
01067                         res->getPixelFormat(), colorKey ) ) ;
01068                         
01069             }
01070             catch( const Video::VideoException & e )
01071             {
01072                 throw TextException( 
01073                     "TrueTypeFont::renderUnicodeText (blended) : "
01074                     "color keying failed : " + e.toString() ) ; 
01075             }
01076             
01077 #endif // OSDL_ADDS_COLOR_KEY
01078 
01079             break ;
01080     
01081     
01082         default:
01083             throw TextException( "TrueTypeFont::renderUnicodeText : "
01084                 "unknown quality requested : " 
01085                 + Ceylan::toString( quality ) + "." ) ; 
01086             break ; 
01087 
01088     }
01089 
01090     
01091     if ( _convertToDisplay )
01092     {
01093     
01094         /*
01095          * We want to keep our colorkey (if any), so we choose to add
01096          *  alpha only if no colorkey is used.
01097          *
01098          * Surface will be RLE encoded here :
01099          *  
01100          */
01101         if ( quality == Blended )
01102             res->convertToDisplay( /* alphaChannelWanted */ true ) ;
01103         else    
01104             res->convertToDisplay( /* alphaChannelWanted */ false ) ;
01105             
01106     }   
01107 
01108     return * res ;  
01109     
01110 }
01111 
01112 
01113 
01114 const string TrueTypeFont::toString( Ceylan::VerbosityLevels level ) 
01115     const throw()
01116 {
01117 
01118     string res = "Truetype font, whose point size is " 
01119         + Ceylan::toString( _pointSize ) + " dots per inch" ;
01120         
01121     if ( level == Ceylan::low )
01122         return res ;
01123     
01124     Ceylan::Uint16 faceCount = getFacesCount() ;
01125     
01126     res += ". This font has " ;
01127     
01128     switch( faceCount )
01129     {
01130     
01131         case 0:
01132             res += "no available face (abnormal)" ;
01133             break ;
01134             
01135         case 1:
01136             res += "exactly one face" ;
01137             break ;
01138             
01139         default:
01140             res += Ceylan::toString( faceCount ) + " faces" ;
01141             break ;         
01142     
01143     }
01144     
01145     if ( isFixedWidth() )
01146         res +=", and is fixed width" ;
01147     else
01148         res +=", and is not fixed width" ;
01149     
01150     res +=". The family name of the current face is '" + getFaceFamilyName() 
01151         + "', and its style name is '" + getFaceStyleName() + "'" ;
01152     
01153     if  ( level == Ceylan::medium )
01154         return res ;        
01155             
01156     SDL_version compileVersion ;
01157     
01158     TTF_VERSION( & compileVersion ) ;   
01159     
01160     const SDL_version * linkedVersion = TTF_Linked_Version() ;
01161     
01162     res += ". The OSDL font module is compiled with SDL_ttf version " 
01163         + Ceylan::toNumericalString( compileVersion.major ) + "."
01164         + Ceylan::toNumericalString( compileVersion.minor ) + "."
01165         + Ceylan::toNumericalString( compileVersion.patch ) 
01166         + ", linked with version "
01167         + Ceylan::toNumericalString( linkedVersion->major ) + "."
01168         + Ceylan::toNumericalString( linkedVersion->minor ) + "."
01169         + Ceylan::toNumericalString( linkedVersion->patch ) ;
01170         + ". Rendering style is " 
01171         + InterpretRenderingStyle( TTF_GetFontStyle( _actualFont ) ) ;
01172         
01173         
01174     return res ;
01175         
01176         
01177 }
01178 
01179 
01180 
01181 // Static section.
01182 
01183 
01184 Font::RenderQuality TrueTypeFont::GetObtainedQualityFor( 
01185     Font::RenderQuality targetedQuality ) throw()
01186 {
01187 
01188     return targetedQuality ;
01189     
01190 }
01191 
01192 
01193 void TrueTypeFont::SetUnicodeSwapStatus( bool newStatus ) throw()
01194 {
01195 
01196     if ( newStatus )
01197         TTF_ByteSwappedUNICODE( 1 ) ;
01198     else    
01199         TTF_ByteSwappedUNICODE( 0 ) ;
01200         
01201 }
01202 
01203 
01204 string TrueTypeFont::DescribeLastError() throw() 
01205 {
01206 
01207     return TTF_GetError() ;
01208     
01209 }
01210 
01211 
01212 OSDL::Video::Surface & TrueTypeFont::basicRenderUnicodeGlyph( 
01213     Ceylan::Unicode character, RenderQuality quality, 
01214     Pixels::ColorDefinition glyphColor ) throw( TextException )
01215 {
01216 
01217     // Render unconditionnally here :
01218     
01219     SDL_Surface * textSurface ;
01220     Surface * res ;
01221     
01222     switch( quality )
01223     {
01224     
01225     
01226         case Solid:
01227             textSurface = TTF_RenderGlyph_Solid( _actualFont, character,
01228                 glyphColor ) ;
01229                 
01230             if ( textSurface == 0 )
01231                 throw TextException( 
01232                     "TrueTypeFont::basicRenderUnicodeGlyph (solid) : "
01233                     "unable to render character '" 
01234                     + Ceylan::toString( character )
01235                     + "' : " + DescribeLastError() ) ; 
01236                     
01237             res = new Surface( * textSurface, 
01238                 /* display type */ Surface::BackBuffer ) ;  
01239                 
01240             // No colorkey added, since already there. 
01241             break ;
01242     
01243     
01244         case Shaded:
01245             textSurface = TTF_RenderGlyph_Shaded( _actualFont, character,
01246                 glyphColor, _backgroundColor ) ;
01247                 
01248             if ( textSurface == 0 )
01249                 throw TextException( 
01250                     "TrueTypeFont::basicRenderUnicodeGlyph (shaded) : " 
01251                     "unable to render character '" 
01252                     + Ceylan::toString( character )
01253                     + "' : " + DescribeLastError() ) ; 
01254                      
01255             /*
01256              * We have to create a new surface so that the surface returned by
01257              * 'TTF_RenderGlyph_Shaded' will no more be palettized : 
01258              * we need a color key to have transparent blits.
01259              *
01260              */  
01261             
01262             ColorMask redMask, greenMask, blueMask ;
01263             Pixels::getRecommendedColorMasks( redMask, greenMask, blueMask ) ;
01264             
01265             res = new Surface( Surface::Hardware | Surface::ColorkeyBlit,
01266                 textSurface->w, textSurface->h, /* bpp */ 32, 
01267                 redMask, greenMask, blueMask, /* no alpha wanted */ 0 ) ;
01268 
01269             // Avoid messing text color with color key :
01270             Pixels::ColorDefinition colorKey ;
01271     
01272             if ( Pixels::areEqual( glyphColor, Pixels::Black, 
01273                 /* use alpha */ false ) )
01274             {
01275             
01276                 colorKey = Pixels::White ;
01277                 res->fill( colorKey ) ;
01278                 
01279             }   
01280             else
01281             {
01282             
01283                 colorKey = Pixels::Black ;
01284                 
01285                 /*
01286                  * No need to fill 'res' with black, since new RGB 
01287                  * surfaces come all black already.
01288                  *
01289                  */
01290                  
01291             }   
01292             
01293             try
01294             {
01295                 res->setColorKey( 
01296                     Surface::ColorkeyBlit | Surface::RLEColorkeyBlit, 
01297                     Pixels::convertColorDefinitionToPixelColor(
01298                         res->getPixelFormat(), colorKey ) ) ;
01299             }
01300             catch( const Video::VideoException & e )
01301             {
01302             
01303                 throw TextException( 
01304                     "TrueTypeFont::basicRenderUnicodeGlyph (shaded) : "
01305                     "color keying failed : " + e.toString() ) ; 
01306                     
01307             }
01308             SDL_BlitSurface( textSurface, 0, & res->getSDLSurface(), 0 ) ;
01309             break ;
01310 
01311 
01312         case Blended:
01313             textSurface = TTF_RenderGlyph_Blended( _actualFont, character,
01314                 glyphColor ) ;
01315                 
01316             if ( textSurface == 0 )
01317                 throw TextException( 
01318                     "TrueTypeFont::basicRenderUnicodeGlyph (blended) : " 
01319                     "unable to render character '" 
01320                     + Ceylan::toString( character )
01321                     + "' : " + DescribeLastError() ) ; 
01322             
01323             res = new Surface( * textSurface, 
01324                 /* display type */ Surface::BackBuffer ) ;  
01325 
01326             // No color key enforced.
01327             
01328             break ;
01329     
01330     
01331         default:
01332             throw TextException( "TrueTypeFont::basicRenderUnicodeGlyph : "
01333                 "unknown quality requested : " 
01334                 + Ceylan::toString( quality ) + "." ) ; 
01335             break ; 
01336             
01337     }
01338     
01339     
01340     if ( _convertToDisplay )
01341     {
01342     
01343         /*
01344          * We want to keep our colorkey (if any), so we choose to add 
01345          * alpha only if no colorkey is used.
01346          *
01347          * Surface will be RLE encoded here :
01348          *  
01349          */
01350         if ( quality == Blended )
01351             res->convertToDisplay( /* alphaChannelWanted */ true ) ;
01352         else    
01353             res->convertToDisplay( /* alphaChannelWanted */ false ) ;
01354             
01355     }   
01356     
01357     // Uncomment next line to debug character bounding-box :
01358     //res->drawEdges() ;
01359         
01360     return * res ;  
01361     
01362 }
01363 

Generated on Fri Mar 30 14:47:00 2007 for OSDL by  doxygen 1.5.1