OSDLFixedFont.cc

Go to the documentation of this file.
00001 #include "OSDLFixedFont.h"
00002 
00003 #include "OSDLSurface.h"         // for Surface
00004 #include "OSDLPixel.h"           // for ColorDefinition
00005 #include "OSDLVideoTypes.h"      // for Length, SignedLength, etc.
00006 #include "OSDLUtils.h"           // for getBackendLastError.
00007 
00008 #include "SDL_gfxPrimitives.h"   // for stringColor
00009 
00010 #include "SDL.h"                 // for SDL_Surface
00011 
00012 #include <list>
00013  
00014 #include <ctype.h>               // for isdigit
00015 
00016 
00017 #ifdef OSDL_USES_CONFIG_H
00018 #include <OSDLConfig.h>          // for OSDL_DEBUG_FONT and al 
00019 #endif // OSDL_USES_CONFIG_H
00020 
00021 
00022 
00023 using std::string ;
00024 
00025 using namespace Ceylan ;
00026 using namespace Ceylan::Log ;
00027 
00028 using namespace OSDL::Video ;
00029 using namespace OSDL::Video::TwoDimensional ;
00030 using namespace OSDL::Video::TwoDimensional::Text ;
00031 
00032 
00033 Ceylan::System::FileLocator Text::FixedFont::FixedFontFileLocator ;
00034 
00035 
00036 const OSDL::Video::Length
00037     OSDL::Video::TwoDimensional::Text::BasicFontCharacterWidth  = 8 ;
00038     
00039 const OSDL::Video::Length
00040     OSDL::Video::TwoDimensional::Text::BasicFontCharacterHeight = 8 ;
00041 
00042 
00043 
00044 string Text::FixedFont::FontFileExtension = ".fnt" ;
00045 
00046 
00047 const Ceylan::Float32 Text::FixedFont::SpaceWidthFactor = 0.7 ;
00048 
00049 
00050 const Ceylan::Uint16 Text::FixedFont::FontCharacterCount = 256 ;
00051 
00052 
00053 
00054 // First, the two basic fixed font primitives.
00055 
00056 
00057 bool Text::printBasic( const std::string & text, Surface & targetSurface,
00058     Coordinate x, Coordinate y, Pixels::ColorDefinition colorDef ) throw()
00059 {   
00060 
00061     FixedFont::SetFontSettings( /* no font data : built-in */ 0, /* width */ 8, 
00062         /* height */ 8 ) ;
00063             
00064     bool result = ::stringColor( & targetSurface.getSDLSurface(), 
00065         x, y, text.c_str(),
00066         Pixels::convertColorDefinitionToRawPixelColor( colorDef ) ) == 0 ;
00067         
00068     return result ;
00069     
00070 }
00071 
00072 
00073 bool Text::printBasic( const std::string & text, Surface & targetSurface,
00074     Coordinate x, Coordinate y, 
00075     Pixels::ColorElement red, Pixels::ColorElement blue,
00076     Pixels::ColorElement green, Pixels::ColorElement alpha ) throw()
00077 {   
00078 
00079     
00080     FixedFont::SetFontSettings( /* no font data : built-in */ 0, /* width */ 8, 
00081         /* height */ 8 ) ;
00082         
00083     /*
00084      * Color should not be mapped to target surface, since it will be 
00085      * done thanks to the SDL_gfx blit of the result.
00086      *
00087      */
00088         
00089     bool result = ::stringRGBA( & targetSurface.getSDLSurface(), x, y,
00090         text.c_str(), red, green, blue, alpha ) == 0 ;
00091         
00092     return result ;
00093     
00094 }
00095 
00096 
00097 
00098 /*
00099  * The problem of the SDL_gfx fixed fonts is that the glyph caching 
00100  * feature works only for one font and one color.
00101  * If multiple fixed fonts are used, as they share the same cache,
00102  * their glyphs are regularly overwritten by the same character rendered 
00103  * with another font, and, even worse, switching fonts leads to flushing 
00104  * all the glyph cache.
00105  *
00106  * To avoid that, OSDL uses one cache by fixed font. 
00107  * Besides, the cached glyph can be directly converted to display, which,
00108  * together with a RLE encoding based on the color key, should be more
00109  * efficient.
00110  *
00111  */
00112 
00113 FixedFont::FixedFont( 
00114         Length characterWidth, 
00115         Length characterHeight, 
00116         RenderingStyle renderingStyle, 
00117         bool convertToDisplay, 
00118         RenderCache cacheSettings,
00119         AllowedCachePolicy cachePolicy, 
00120         Ceylan::System::Size quota 
00121             ) throw( TextException ) :
00122     Font( convertToDisplay, cacheSettings, cachePolicy, quota ),
00123     _width( characterWidth ),
00124     _height( characterHeight ),
00125     _fontData( 0 )
00126 {
00127 
00128     // Space and alinea widths are set as well by :
00129     loadFontFrom( BuildFontFilenameFor( characterWidth, characterHeight,
00130         renderingStyle ) ) ;
00131     
00132 }
00133 
00134                               
00135 FixedFont::FixedFont( 
00136         const std::string & fontFilename, 
00137         bool convertToDisplay,
00138         RenderCache cacheSettings, 
00139         AllowedCachePolicy cachePolicy, 
00140         Ceylan::System::Size quota 
00141             ) throw( TextException ) :
00142     Font( convertToDisplay, cacheSettings, cachePolicy, quota ),
00143     _width( 0 ),
00144     _height( 0 ),
00145     _fontData( 0 )
00146 {
00147 
00148     GetFontAttributesFrom( fontFilename, _width, _height, _renderingStyle ) ;
00149 
00150     // Space and alinea widths are set as well by :
00151     loadFontFrom( fontFilename ) ;
00152     
00153 }
00154 
00155 
00156 FixedFont::~FixedFont() throw()
00157 {       
00158         
00159     if ( _fontData != 0 )
00160         delete [] _fontData ;   
00161                     
00162 }
00163 
00164 
00165 
00166 Width FixedFont::getWidth() const throw()
00167 {
00168 
00169     return _width ;
00170     
00171 }
00172 
00173 
00174 Width FixedFont::getWidth( Ceylan::Latin1Char character ) const throw()
00175 {
00176 
00177     return _width ;
00178     
00179 }
00180 
00181 
00182 SignedWidth FixedFont::getWidthOffset( Ceylan::Latin1Char character ) 
00183     const throw( TextException )
00184 {
00185 
00186     return 0 ;
00187     
00188 }
00189 
00190 
00191 
00192 SignedHeight FixedFont::getHeightAboveBaseline( Ceylan::Latin1Char character ) 
00193     const throw( TextException )
00194 {
00195 
00196     return _height ;
00197     
00198 }
00199 
00200 
00201 SignedLength FixedFont::getAdvance() const throw( TextException )
00202 {
00203 
00204     return _width ;
00205     
00206 }
00207 
00208 
00209 SignedLength FixedFont::getAdvance( Ceylan::Latin1Char character ) 
00210     const throw( TextException )
00211 {
00212 
00213     // As getInterGlyphWidth returns 0 :
00214     return _width ;
00215     
00216 }
00217 
00218 
00219 Width FixedFont::getInterGlyphWidth() const throw()
00220 {
00221 
00222     return 0 ;
00223     
00224 }
00225 
00226 
00227 Text::Height FixedFont::getHeight() const throw()
00228 {
00229 
00230     return _height ;
00231     
00232 }
00233 
00234 
00235 SignedHeight FixedFont::getAscent() const throw()
00236 {
00237     return _height ;
00238 }
00239 
00240 
00241 SignedHeight FixedFont::getDescent() const throw()
00242 {
00243 
00244     return 0 ;
00245     
00246 }
00247 
00248 
00249 Text::Height FixedFont::getLineSkip() const throw()
00250 {
00251 
00252     return _height + static_cast<Text::Height>( 
00253         Ceylan::Maths::Max<Ceylan::Float32>( 2 , 0.1 * _height ) ) ;
00254         
00255 }
00256 
00257 
00258 
00259 // Bounding boxes section.
00260 
00261 
00262 UprightRectangle & FixedFont::getBoundingBox() const throw( TextException )
00263 {
00264 
00265     return * new UprightRectangle( 0, 0, getAdvance(), getLineSkip() ) ;
00266     
00267 }
00268 
00269 
00270 UprightRectangle & FixedFont::getBoundingBoxFor( const std::string & word )
00271     const throw( TextException )
00272 {
00273 
00274     if ( word.empty() )
00275         throw TextException( 
00276             "FixedFont::getBoundingBoxFor (word) : empty word specified." ) ;
00277             
00278     return * new UprightRectangle( 0, 0, 
00279         static_cast<Length>( getAdvance() * ( word.size() - 1 ) + _width ), 
00280         static_cast<Length>( _height ) ) ;
00281         
00282 }
00283 
00284 
00285 
00286 
00287 // Render section.
00288 
00289 
00290 
00291 OSDL::Video::Surface & FixedFont::renderLatin1Glyph( 
00292     Ceylan::Latin1Char character, RenderQuality quality, 
00293     Pixels::ColorDefinition glyphColor ) throw( TextException )
00294 {
00295 
00296     // Two different cases, depending on a glyph cache being used or not :
00297     
00298     if ( _cacheSettings == GlyphCached )
00299     {
00300         
00301         /*
00302          * First check that the character-quality-color combination is not
00303          * already available in cache :
00304          *
00305          */
00306         
00307         CharColorQualityKey renderKey( character, glyphColor, 
00308             /* quality ignored */ DefaultQuality ) ;
00309             
00310         SmartResource * res = _glyphCache->getClone( renderKey ) ;
00311         
00312         if ( res != 0 )
00313         {
00314         
00315             Surface * toReturn = dynamic_cast<Surface *>( res ) ;
00316             
00317 #if OSDL_DEBUG_FONT
00318 
00319             LogPlug::debug( "FixedFont::renderLatin1Glyph : cache hit, "
00320                 "returning clone of prerendered glyph." ) ;
00321             
00322             if ( toReturn == 0 )
00323                 Ceylan::emergencyShutdown( "FixedFont::renderLatin1Glyph : "
00324                     "cache did not return a Surface." ) ;
00325                                 
00326 #endif // OSDL_DEBUG_FONT
00327             
00328             return *toReturn ;
00329             
00330         }   
00331         
00332         
00333 #if OSDL_DEBUG_FONT
00334 
00335         LogPlug::debug( "FixedFont::renderLatin1Glyph : "
00336             "cache miss, creating new glyph rendering." ) ;
00337             
00338 #endif // OSDL_DEBUG_FONT
00339 
00340         // Here it its a cache miss, we therefore have to generate the glyph :
00341         Surface & newSurface = basicRenderLatin1Glyph( character, glyphColor ) ;
00342         
00343         // Give the cache a chance of being fed :       
00344         _glyphCache->scanForAddition( renderKey, newSurface ) ;
00345         
00346         
00347         return newSurface ;
00348                     
00349     }
00350 
00351     /*
00352      * Here we are not using a glyph cache, we have simply to generate 
00353      * the glyph :  
00354      *
00355      */
00356     return basicRenderLatin1Glyph( character, glyphColor ) ;    
00357 
00358 }
00359 
00360 
00361 void FixedFont::blitLatin1Glyph( Surface & targetSurface, 
00362     Coordinate x, Coordinate y, 
00363     Ceylan::Latin1Char character, RenderQuality quality, 
00364     Pixels::ColorDefinition glyphColor ) throw( TextException )
00365 {
00366 
00367     // Here the 'quality' attribute is ignored.
00368     
00369     // Two different cases : either we are glyph-cached, or not.
00370     
00371     
00372     if ( _cacheSettings == GlyphCached )
00373     {
00374         
00375         /*
00376          * First check that the character-(quality)-color combination 
00377          * is not already available in cache :
00378          *
00379          */
00380          
00381         CharColorQualityKey renderKey( character, glyphColor, 
00382             /* quality ignored */ DefaultQuality ) ;
00383             
00384         const Resource * cacheEntry = _glyphCache->get( renderKey ) ;
00385         
00386         if ( cacheEntry != 0 )
00387         {
00388         
00389             // Already available ? Get it and blit it !
00390             const Surface * cachedSurface = 
00391                 dynamic_cast<const Surface *>( cacheEntry ) ;
00392             
00393 #if OSDL_DEBUG_FONT
00394 
00395             LogPlug::debug( "FixedFont::blitLatin1Glyph : "
00396                 "cache hit, blitting prerendered glyph." ) ;
00397             
00398             if ( cachedSurface == 0 )
00399                 Ceylan::emergencyShutdown( "FixedFont::blitLatin1Glyph : "
00400                     "cache did not return a Surface." ) ;
00401                     
00402 #endif // OSDL_DEBUG_FONT
00403             
00404             try
00405             {
00406                 cachedSurface->blitTo( targetSurface, x, y ) ;
00407             }
00408             catch( const VideoException & e )
00409             {
00410                 throw TextException( "FixedFont::blitLatin1Glyph : "
00411                     "blit of cloned glyph failed : " + e.toString() ) ;
00412             }
00413             
00414         }
00415         else
00416         {
00417         
00418 #if OSDL_DEBUG_FONT
00419 
00420             LogPlug::debug( "FixedFont::blitLatin1Glyph : cache miss, "
00421                 "blitting newly rendered glyph." ) ;
00422                 
00423 #endif // OSDL_DEBUG_FONT
00424         
00425             /*
00426              * Not found in cache, hence ask a new rendering 
00427              * (and submit it to the glyph cache) :
00428              *
00429              * (the surface returned by 'submitLatin1GlyphToCache' is 
00430              * owned in all cases by the cache, it should not be 
00431              * deallocated here)
00432              *
00433              */ 
00434             submitLatin1GlyphToCache( character, glyphColor ).blitTo(
00435                 targetSurface, x, y ) ;
00436                             
00437         }
00438                     
00439     }
00440     
00441     /*
00442      * Here we are not glyph-cached, we blit the glyph directly :
00443      * (no quality managed) :
00444      *
00445      */
00446     basicBlitLatin1Glyph( targetSurface, x, y, character, glyphColor ) ;
00447     
00448 }
00449 
00450             
00451                 
00452 OSDL::Video::Surface & FixedFont::renderLatin1GlyphAlpha( 
00453     Ceylan::Latin1Char character, RenderQuality quality,
00454     Pixels::ColorDefinition glyphColor ) throw( TextException )
00455 {
00456 
00457     /*
00458      * This method should not be used, since it does not do what was expected.
00459      *
00460      * This is not really annoying since using colorkey is indeed better 
00461      * than using alphablending in the case of fonts.
00462      *
00463      */
00464     
00465     /*
00466      * With testOSDLFixedFont.cc, one can see that :
00467      *   - if the alpha mask for the created surface is the expected
00468      * '0x000000FF', then one can see only a grid, no character is to be seen
00469      *   - if the alpha mask for the created surface is '0x00000FF0', then 
00470      * one can see that the characters are indeed available but that their 
00471      * alpha coordinate is null, whereas we would want to have it set to 
00472      * the one of the surface generated by SDL_gfx : the blit does not do
00473      * what we would want, we tried many settings with no luck for the moment.
00474      *
00475      */
00476      
00477     Pixels::PixelColor color = Pixels::convertColorDefinitionToRawPixelColor(
00478         glyphColor ) ;
00479     
00480     /*
00481     if ( _cacheSettings == Font::GlyphCached )
00482     {
00483 
00484         unsigned char charIndex = static_cast<unsigned char>( character ) ;
00485         Surface * inCache = _cachedGlyphs[ charIndex ] ;
00486                 
00487         if ( inCache != 0  && color == _cachedColors[ charIndex ] )
00488                 return inCache->clone() ;
00489     }
00490     */
00491     
00492     // Here we have to generate the glyph.
00493 
00494     /*
00495      * Some explanations are needed to understand how alpha blending is 
00496      * managed :
00497      * 1. an empty RGBA surface, with source alpha blending disabled, is
00498      * created.
00499      * It will hold a copy of the surface put in cache and blit by SDL_gfx
00500      * (characterColor).
00501      * The first setAlpha disables the source alpha blending on this surface, 
00502      * to prepare for the SDL_gfx operated blit. 
00503      * Otherwise we would be in the case of (RGBA with SRCALPHA) -> RBGA 
00504      * blit which, according to
00505      * http://www.libsdl.org/cgi/docwiki.cgi/SDL_5fSetAlpha, would result
00506      * to our new surface having unchanged alpha coordinates, hence uniform :
00507      * the alpha-coded shape of letters would be lost. 
00508      * Instead, this first setAlpha allows to have a
00509      * (RGBA with SRCALPHA) -> RBG blit, which preserves the alpha 
00510      * coordinates from SDL_gfx 
00511      * 2. then the blit is performed thanks to characterColor
00512      * 3. the source alpha attribute of the result is restored thanks 
00513      * to a second call to setAlpha.
00514      * This way, when our surface will be blitted (for example to the 
00515      * screen surface), it will be blitted as a surface directly generated 
00516      * by SDL_gfx would be blitted, i.e. with respect to the alpha 
00517      * coordinates we managed painfully to preserve.
00518      *
00519      * I found this alpha-blending subject rather dull and awfully explained 
00520      * by the SDL doc, the result does not seem to work correctly.
00521      *
00522      */
00523      
00524      
00525     /*
00526      * Creates a new back-buffer surface to which the glyph will be blitted :
00527      *
00528      * (beware to endianness)
00529      *
00530      */
00531         
00532     Surface & res = * new Surface( * SDL_CreateRGBSurface( 
00533         Surface::Hardware, _width, _height, 32, 
00534         0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF0
00535         /* replace 0x000000FF by 0x00000FF0 to see that this could work */
00536           ) ) ;
00537     
00538     // Clears source alpha bit for our surface to prepare for next blit :
00539     res.setAlpha( 0 /* Surface::AlphaBlendingBlit disabled */,
00540         Pixels::AlphaOpaque ) ;     
00541     
00542     // Blits the SDL_gfx-generated character to our surface :
00543     if ( ::characterColor( & res.getSDLSurface(), 0, 0, 
00544             static_cast<char>( character ), color ) != 0 )
00545         throw TextException( "FixedFont::renderLatin1Glyph : blit failed, " 
00546             + Utils::getBackendLastError() ) ;
00547 
00548     // Restores source alpha attributes for our surface :
00549     res.setAlpha( Surface::AlphaBlendingBlit, Pixels::AlphaOpaque ) ;       
00550         
00551     //if ( _convertToDisplay )
00552     //  res.convertToDisplay( /* alphaChannelWanted */ true ) ;
00553 
00554     /*
00555     if ( _cacheSettings == Font::GlyphCached )
00556         updateCache( res.clone(), character, color ) ;
00557     */
00558     return res ;    
00559 }
00560 
00561 
00562 const string FixedFont::toString( Ceylan::VerbosityLevels level ) const throw()
00563 {
00564 
00565     string res = "Fixed font, whose dimensions are " 
00566         + Ceylan::toString( _width ) 
00567         + "x" + Ceylan::toString( _height ) ;
00568         
00569     if ( level == Ceylan::low )
00570         return res ;
00571     
00572     res += ". " + Font::toString( level ) ;
00573     
00574     return res ;
00575         
00576 }
00577 
00578 
00579 
00580 
00581 // Static public section.
00582 
00583 
00584 
00585 // Protected section.
00586 
00587     
00588 Font::RenderQuality FixedFont::GetObtainedQualityFor( 
00589     Font::RenderQuality targetedQuality ) throw()   
00590 {
00591 
00592     return Solid ;
00593     
00594 }
00595 
00596 
00597 void FixedFont::SetFontSettings( const Ceylan::Byte * fontData, 
00598     Length characterWidth, Length characterHeight ) throw() 
00599 {
00600 
00601     static const char * lastFontData  = 0 ;
00602     static Length lastCharacterWidth  = 0 ;
00603     static Length lastCharacterHeight = 0 ;
00604     
00605     /*
00606      * No other code should use directly 'gfxPrimitivesSetFont' since it 
00607      * would change SDL_gfx state.
00608      *
00609      */
00610     if ( lastFontData != fontData || lastCharacterWidth != characterWidth 
00611         || lastCharacterHeight != characterHeight )
00612     {
00613     
00614         // We have indeed to reset SDL_gfx state :
00615                 
00616 #if OSDL_DEBUG_FONT
00617 
00618         LogPlug::debug( 
00619             "FixedFont::SetFontSettings : having to reset SDL_gfx state" ) ;
00620             
00621 #endif // OSDL_DEBUG_FONT
00622         
00623         lastFontData        = fontData ;
00624         lastCharacterWidth  = characterWidth ;
00625         lastCharacterHeight = characterHeight ;
00626 
00627         gfxPrimitivesSetFont( fontData, characterWidth, characterHeight ) ;
00628     }   
00629     else
00630     {
00631     
00632 #if OSDL_DEBUG_FONT
00633 
00634         LogPlug::debug( "FixedFont::SetFontSettings : saved a SDL_gfx reset" ) ;
00635         
00636 #endif // OSDL_DEBUG_FONT
00637 
00638     }
00639 
00640 }                           
00641 
00642 
00643 
00644                             
00645 // Protected section.
00646 
00647                             
00648 void FixedFont::loadFontFrom( const std::string & fontFilename ) 
00649     throw( TextException )
00650 {
00651 
00652 
00653 #if OSDL_DEBUG_FONT
00654 
00655     LogPlug::trace( "FixedFont::loadFontFrom : trying to load font file '" 
00656         + fontFilename + "'." ) ;
00657     
00658 #endif // OSDL_DEBUG_FONT
00659 
00660     string fontFullPath = fontFilename ;
00661     
00662     // Search directly in current working directory :
00663     if ( ! System::File::ExistsAsFileOrSymbolicLink( fontFilename ) )
00664     {
00665         
00666         try
00667         {
00668             // On failure use the dedicated TrueType font locator :
00669             fontFullPath = FixedFontFileLocator.find( fontFilename ) ;
00670         }
00671         catch( const System::FileLocatorException & e )
00672         {
00673         
00674             // Last hope : general font locator :
00675             try
00676             {
00677                 fontFullPath = Font::FontFileLocator.find( fontFilename ) ;
00678             }   
00679             catch( const System::FileLocatorException & ex )
00680             {
00681                 
00682                 // Not found at all, let's raise an exception.
00683                 
00684                 string currentDir ;
00685                 
00686                 try
00687                 {
00688                     currentDir =
00689                         System::Directory::GetCurrentWorkingDirectoryName() ;
00690                 }
00691                 catch( const System::Directory::DirectoryException & exc )
00692                 {
00693                 
00694                     throw TextException( 
00695                         "FixedFont::loadFontFrom : unable to load '" 
00696                         + fontFilename 
00697                         + "', exception generation triggered another failure : "
00698                         + exc.toString() + "." ) ;
00699                         
00700                 }
00701                 
00702                 throw TextException( "FixedFont::loadFontFrom : '" 
00703                     + fontFilename 
00704                     + "' is not a regular file or a symbolic link "
00705                     "relative to the current directory (" + currentDir
00706                     + ") and cannot be found through Fixed font locator ("
00707                     + FixedFont::FixedFontFileLocator.toString() 
00708                     + ") nor through general font locator "
00709                     "based on font path environment variable ("
00710                     + Font::FontPathEnvironmentVariable + ") : " 
00711                     + Font::FontFileLocator.toString() + "." ) ;
00712                     
00713             }       
00714         }       
00715     }       
00716     
00717     
00718     // Here fontFullPath should be OK.
00719 
00720 #if OSDL_DEBUG_FONT
00721 
00722     LogPlug::debug( "FixedFont::loadFontFrom : full font filename is '" 
00723         + fontFullPath + "'." ) ;
00724         
00725 #endif // OSDL_DEBUG_FONT
00726     
00727     try
00728     {
00729     
00730         System::File fontFile( fontFullPath, System::File::Read ) ; 
00731         
00732         System::Size dataSize = fontFile.size() ;
00733         
00734         if ( _fontData != 0 )
00735             delete [] _fontData ;
00736             
00737         _fontData = new char[ dataSize ] ;
00738         
00739         if ( _fontData == 0 )
00740             throw TextException( 
00741                 "FixedFont::loadFontFrom : not enough memory." ) ;
00742         
00743         if ( fontFile.read( _fontData, dataSize ) != dataSize )
00744             throw TextException( 
00745                 "FixedFont::loadFontFrom : error while reading font data." ) ;
00746         
00747     }
00748     catch( const System::SystemException & e )
00749     {
00750     
00751         throw TextException( "FixedFont::loadFontFrom : "
00752             "error while loading font data file : " + e.toString() ) ;
00753     }
00754     
00755     _spaceWidth = static_cast<Width>( SpaceWidthFactor * 
00756         getWidth( /* all character have the same width here */ ) ) ;
00757 
00758     // By default, the width of an alinea is a multiple of a space width :
00759     _alineaWidth = DefaultSpaceBasedAlineaWidth * _spaceWidth ; 
00760 
00761     // Here the font is loaded. 
00762             
00763 }
00764 
00765 
00766 
00767 const Surface & FixedFont::submitLatin1GlyphToCache( 
00768         Ceylan::Latin1Char character, Pixels::ColorDefinition glyphColor ) 
00769     throw( TextException )
00770 {
00771     
00772     /*
00773      * The method contract tells us this character is not in cache, we 
00774      * have to render it :
00775      *
00776      */
00777     Surface & glyphSurface = basicRenderLatin1Glyph( character, glyphColor ) ;
00778 
00779     CharColorQualityKey renderKey( character, glyphColor, 
00780         /* quality not managed */ DefaultQuality ) ;
00781     
00782     bool takenByCache ; 
00783     
00784     try
00785     { 
00786         takenByCache = _glyphCache->takeOwnershipOf( renderKey, glyphSurface ) ;
00787     }
00788     catch( const ResourceManagerException & e )
00789     {
00790     
00791         /*
00792          * This should never happen since this method is meant to be 
00793          * called only when the cache could not respond : the key should
00794          * therefore not be already associated.
00795          *
00796          */
00797          throw TextException( 
00798             "FixedFont::submitLatin1GlyphToCache : cache submitting failed : "
00799             + e.toString() ) ;
00800 
00801     }
00802     
00803     if ( ! takenByCache )
00804         throw TextException( "FixedFont::submitLatin1GlyphToCache : "
00805             "cache did not accept rendering for '" 
00806             + Ceylan::toString( character ) + "'." ) ;
00807     
00808     // Cache still owns that (const) surface :      
00809     return glyphSurface ;
00810     
00811 }
00812 
00813 
00814 
00815 Surface & FixedFont::basicRenderLatin1Glyph( Ceylan::Latin1Char character, 
00816     Pixels::ColorDefinition glyphColor ) throw( TextException )
00817 {
00818 
00819     ColorMask redMask, greenMask, blueMask ;
00820     
00821     Pixels::getRecommendedColorMasks( redMask, greenMask, blueMask ) ;
00822     
00823     // Creates a back-buffer surface with no source alpha blending requested :
00824     Surface & res = * new Surface( Surface::Hardware | Surface::ColorkeyBlit,
00825         _width, _height, /* bpp */ 32, redMask, greenMask, blueMask, 
00826         /* no alpha wanted */ 0 ) ;
00827 
00828     // Avoid messing text color with color key :
00829     Pixels::ColorDefinition colorKey ;
00830     
00831     if ( Pixels::areEqual( glyphColor, Pixels::Black, /* use alpha */ false ) )
00832     {
00833         colorKey = Pixels::White ;
00834         res.fill( colorKey ) ;
00835     }   
00836     else
00837     {
00838     
00839         colorKey = Pixels::Black ;
00840         
00841         /*
00842          * No need to fill 'res' with black, since new RGB surfaces come 
00843          * all black already.
00844          *
00845          */
00846          
00847     }   
00848         
00849     basicBlitLatin1Glyph( res, 0, 0, character, glyphColor ) ;
00850 
00851     // Comment out following two lines to see blit blocks (as black rectangles):
00852     res.setColorKey( Surface::ColorkeyBlit | Surface::RLEColorkeyBlit, 
00853         convertColorDefinitionToPixelColor( res.getPixelFormat(), colorKey ) ) ;
00854             
00855     if ( _convertToDisplay )
00856     {
00857     
00858         /*
00859          * We want to keep our colorkey, so we do not choose to add alpha.
00860          * Surface will be RLE encoded here :
00861          *  
00862          */
00863         res.convertToDisplay( /* alphaChannelWanted */ false ) ;
00864     }   
00865     
00866     return res ;
00867     
00868 }   
00869                                 
00870                             
00871 void FixedFont::basicBlitLatin1Glyph( Surface & targetSurface,
00872      Coordinate x, Coordinate y, Ceylan::Latin1Char character,
00873      Pixels::ColorDefinition glyphColor ) throw( TextException )
00874 {
00875 
00876     /*
00877      * SDL_gfx internal cache may have to be reset, since another fixed 
00878      * font may be the current one.
00879      *
00880      * This is not that annoying since glyphs already can be cached at 
00881      * the OSDL level, and the actual reset is only triggered when necessary.
00882      *
00883      */
00884     SetFontSettings( _fontData, _width, _height ) ; 
00885 
00886     Pixels::PixelColor color = Pixels::convertColorDefinitionToRawPixelColor(
00887         glyphColor ) ;
00888     
00889     if ( ::characterColor( & targetSurface.getSDLSurface(), x, y, 
00890             static_cast<char>( character ), color ) != 0 )
00891         throw TextException( 
00892             "FixedFont::basicBlitLatin1Glyph : blit of glyph failed, " 
00893             + Utils::getBackendLastError() ) ;  
00894 
00895 }
00896     
00897                             
00898                             
00899 string FixedFont::BuildFontFilenameFor( 
00900     Length characterWidth, Length characterHeight,
00901     RenderingStyle renderingStyle ) throw( TextException )
00902 {
00903 
00904     string attribute ;
00905     
00906     if ( renderingStyle & Bold )
00907     {
00908     
00909         if ( renderingStyle & ~Bold )
00910             throw TextException( "FixedFont::buildFontFilenameFor : "
00911                 "too many rendering styles selected : " 
00912                 + Ceylan::toString( renderingStyle ) + "." ) ;
00913                 
00914         attribute += "B" ;
00915         
00916     }
00917     
00918     if ( renderingStyle & Italic )
00919     {
00920     
00921         if ( renderingStyle & ~Italic )
00922             throw TextException( "FixedFont::buildFontFilenameFor : "
00923                 "too many rendering styles selected : " 
00924                 + Ceylan::toString( renderingStyle ) + "." ) ;
00925                 
00926         attribute += "O" ;
00927         
00928     }
00929     
00930     if ( renderingStyle & Underline )
00931     {
00932     
00933         if ( renderingStyle & ~Underline )
00934             throw TextException( "FixedFont::buildFontFilenameFor : "
00935                 "too many rendering styles selected : " 
00936                 + Ceylan::toString( renderingStyle ) + "." ) ;
00937                 
00938         attribute += "U" ;
00939         
00940     }
00941     
00942     
00943     return Ceylan::toString( characterWidth ) + "x" 
00944         + Ceylan::toString( characterHeight ) + attribute + FontFileExtension ;
00945 
00946 }
00947 
00948 
00949 void FixedFont::GetFontAttributesFrom( const string & filename, 
00950     Length & characterWidth, Length & characterHeight, 
00951     RenderingStyle & renderingStyle ) throw( TextException )
00952 {
00953 
00954     // Some examples : '10x20.fnt', '8x13B.fnt'
00955     System::Size size = filename.size() ;
00956 
00957     if ( filename.substr( size - 4 ) != FontFileExtension )
00958         throw TextException( 
00959             "FixedFont::getFontAttributesFrom : expected extension ("
00960             + FontFileExtension + "), not found in '" + filename + "'." ) ;
00961     
00962     string width, height ;
00963     
00964     Ceylan::Uint16 index = 0 ;
00965     
00966     while ( filename[index] != 'x' && index < size - 4 )
00967     {
00968         width += filename[index] ;
00969     }       
00970     
00971     try
00972     {   
00973         characterWidth = static_cast<Length>( stringToUnsignedLong( width ) ) ;
00974     }
00975     catch( const Ceylan::Exception & e )
00976     {
00977     
00978         throw TextException( 
00979             "FixedFont::getFontAttributesFrom : unable to guess width from '"
00980             + filename + "' : " + e.toString() ) ;
00981     }
00982     
00983     index++ ;
00984     
00985     while ( ::isdigit( filename[index] ) 
00986         && index < size - 3 /* to include the '.' */ )
00987     {
00988         height += filename[index] ;
00989     }       
00990     
00991     
00992     try
00993     {   
00994         characterHeight = 
00995             static_cast<Length>( stringToUnsignedLong( height ) ) ;
00996     }
00997     catch( const Ceylan::Exception & e )
00998     {
00999         throw TextException( 
01000             "FixedFont::getFontAttributesFrom : unable to guess height from '"
01001             + filename + "' : " + e.toString() ) ;
01002     }
01003     
01004     
01005     switch( filename[index] )
01006     {
01007     
01008         case '.':
01009             // We are already in the extension.
01010             renderingStyle = Normal ;
01011             break ;
01012 
01013         case 'B':
01014             renderingStyle = Bold ;
01015             break ;
01016     
01017         case 'O':
01018             renderingStyle = Italic ;
01019             break ;
01020     
01021         case 'U':
01022             renderingStyle = Underline ;
01023             break ;
01024                     
01025         default:
01026             throw TextException( "FixedFont::getFontAttributesFrom : "
01027                 "unable to guess rendering style from '" + filename + "'." ) ;
01028             break ;
01029             
01030     }
01031     
01032 }
01033 

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