Visualization Library

A lightweight C++ OpenGL middleware for 2D/3D graphics
[Home] [Tutorials] [All Classes] [Grouped Classes]

X:/dropbox/visualizationlibrary/src/vlGraphics/Font.cpp

Go to the documentation of this file.
00001 /**************************************************************************************/
00002 /*                                                                                    */
00003 /*  Visualization Library                                                             */
00004 /*  http://www.visualizationlibrary.org                                               */
00005 /*                                                                                    */
00006 /*  Copyright (c) 2005-2010, Michele Bosi                                             */
00007 /*  All rights reserved.                                                              */
00008 /*                                                                                    */
00009 /*  Redistribution and use in source and binary forms, with or without modification,  */
00010 /*  are permitted provided that the following conditions are met:                     */
00011 /*                                                                                    */
00012 /*  - Redistributions of source code must retain the above copyright notice, this     */
00013 /*  list of conditions and the following disclaimer.                                  */
00014 /*                                                                                    */
00015 /*  - Redistributions in binary form must reproduce the above copyright notice, this  */
00016 /*  list of conditions and the following disclaimer in the documentation and/or       */
00017 /*  other materials provided with the distribution.                                   */
00018 /*                                                                                    */
00019 /*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND   */
00020 /*  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED     */
00021 /*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE            */
00022 /*  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR  */
00023 /*  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES    */
00024 /*  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;      */
00025 /*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON    */
00026 /*  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT           */
00027 /*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS     */
00028 /*  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                      */
00029 /*                                                                                    */
00030 /**************************************************************************************/
00031 
00032 #include <vlGraphics/Font.hpp>
00033 #include <vlGraphics/OpenGL.hpp>
00034 #include <vlGraphics/FontManager.hpp>
00035 #include <vlCore/Log.hpp>
00036 #include <vlCore/Say.hpp>
00037 #include <vlCore/FileSystem.hpp>
00038 #include <vlCore/Image.hpp>
00039 
00040 #include <ft2build.h>
00041 #include FT_FREETYPE_H
00042 
00043 using namespace vl;
00044 
00045 // FreeType error table construction start ------------------------------------------
00046 // taken from "fterrors.h" example
00047 #undef __FTERRORS_H__
00048 #define FT_ERRORDEF( e, v, s )  { e, s },
00049 #define FT_ERROR_START_LIST     {
00050 #define FT_ERROR_END_LIST       { 0, 0 } };
00051 
00052 const struct
00053 {
00054  int          err_code;
00055  const char*  err_msg;
00056 } ft_errors[] =
00057 
00058 #include FT_ERRORS_H
00059 // FreeType error table construction end ------------------------------------------
00060 
00061 const char* get_ft_error_message(int error)
00062 {
00063   int i=0;
00064   while( ft_errors[i].err_msg && ft_errors[i].err_code != error )
00065     ++i;
00066   return ft_errors[i].err_msg;
00067 }
00068 
00069 //-----------------------------------------------------------------------------
00070 // Glyph
00071 //-----------------------------------------------------------------------------
00072 Glyph::~Glyph()
00073 {
00074   if (mTextureHandle)
00075   {
00076     glDeleteTextures(1, &mTextureHandle);
00077     mTextureHandle = 0;
00078   }
00079 }
00080 //-----------------------------------------------------------------------------
00081 // Font
00082 //-----------------------------------------------------------------------------
00083 Font::Font(FontManager* fm)
00084 {
00085   VL_DEBUG_SET_OBJECT_NAME()
00086   mFontManager = fm;
00087   mHeight  = 0;
00088   mFT_Face = NULL;
00089   mSmooth  = false;
00090   mFreeTypeLoadForceAutoHint = true;
00091   setSize(14);
00092 }
00093 //-----------------------------------------------------------------------------
00094 Font::Font(FontManager* fm, const String& font_file, int size)
00095 {
00096   VL_DEBUG_SET_OBJECT_NAME()
00097   mFontManager = fm;
00098   mHeight  = 0;
00099   mFT_Face = NULL;
00100   mSmooth  = false;
00101   mFreeTypeLoadForceAutoHint = true;
00102   loadFont(font_file);
00103   setSize(size);
00104 }
00105 //-----------------------------------------------------------------------------
00106 Font::~Font()
00107 {
00108   releaseFreeTypeData();
00109 }
00110 //-----------------------------------------------------------------------------
00111 void Font::releaseFreeTypeData()
00112 {
00113   if (mFT_Face)
00114   {
00115     if (!mFontManager->freeTypeLibrary())
00116     {
00117       vl::Log::error("Font::releaseFreeTypeData(): mFontManager->freeTypeLibrary() is NULL!\n");
00118       VL_TRAP()
00119     }
00120     else
00121     {
00122       FT_Done_Face(mFT_Face);
00123     }
00124     mFT_Face = NULL;
00125   }
00126 }
00127 //-----------------------------------------------------------------------------
00128 void Font::setSize(int size)
00129 {
00130   if(mSize != size)
00131   {
00132     mSize = size;
00133     // removes all the cached glyphs
00134     mGlyphMap.clear();
00135   }
00136 }
00137 //-----------------------------------------------------------------------------
00138 void Font::loadFont(const String& path)
00139 {
00140   if(path == mFilePath)
00141     return;
00142 
00143   mFilePath = path;
00144   // removes all the cached glyphs
00145   mGlyphMap.clear();
00146 
00147   // remove FreeType font face object
00148   if (mFT_Face)
00149   {
00150     FT_Done_Face(mFT_Face);
00151     mFT_Face = NULL;
00152   }
00153 
00154   FT_Error error = 0;
00155 
00156   ref<VirtualFile> font_file = defFileSystem()->locateFile( filePath() );
00157 
00158   if (!font_file)
00159     Log::error( Say("Font::loadFont('%s'): font file not found.\n") << filePath() );
00160 
00161   if ( font_file && font_file->load(mMemoryFile) )
00162   {
00163     if ( (int)mMemoryFile.size() == font_file->size() )
00164     {
00165       error = FT_New_Memory_Face( (FT_Library)mFontManager->freeTypeLibrary(),
00166                                   (FT_Byte*)&mMemoryFile[0],
00167                                   (int)mMemoryFile.size(),
00168                                   0,
00169                                   &mFT_Face );
00170     }
00171     else
00172       Log::error( Say("Font::loadFont('%s'): could not read file.\n") << filePath() );
00173   }
00174 
00175   if (error)
00176   {
00177     Log::error(Say("FT_New_Face error (%s): %s\n") << filePath() << get_ft_error_message(error) );
00178     VL_TRAP()
00179     return;
00180   }
00181 }
00182 //-----------------------------------------------------------------------------
00183 Glyph* Font::glyph(int character)
00184 {
00185   ref<Glyph>& glyph = mGlyphMap[character];
00186 
00187   if (glyph.get() == NULL)
00188   {
00189     glyph = new Glyph;
00190     glyph->setFont(this);
00191 
00192     // create the glyph
00193 
00194     FT_Error error = 0;
00195 
00196     error = FT_Set_Char_Size(
00197               mFT_Face, /* handle to face object           */
00198               0,        /* char_width in 1/64th of points  */
00199               mSize*64, /* char_height in 1/64th of points */
00200               96,       /* horizontal device resolution    */
00201               96 );     /* vertical device resolution      */
00202 
00203     if(error)
00204     {
00205       // Log::error(Say("FT_Set_Char_Size error: %s\n") << get_ft_error_message(error) );
00206       if ( (mFT_Face->face_flags & FT_FACE_FLAG_SCALABLE) == 0 && mFT_Face->num_fixed_sizes)
00207       {
00208         // look for the size which is less or equal to the given size
00209 
00210         int best_match_index = -1;
00211         int best_match_size  = 0;
00212         for( int i=0; i < mFT_Face->num_fixed_sizes; ++i )
00213         {
00214           int size = mFT_Face->available_sizes[i].y_ppem/64;
00215           // skip bigger characters
00216           if (size <= mSize)
00217           {
00218             if (best_match_index == -1 || (mSize - size) < (mSize - best_match_size) )
00219             {
00220               best_match_index = i;
00221               best_match_size  = size;
00222             }
00223           }
00224         }
00225 
00226         if (best_match_index == -1)
00227           best_match_index = 0;
00228 
00229         error = FT_Select_Size(mFT_Face, best_match_index);
00230         if (error)
00231           Log::error(Say("FT_Select_Size error (%s): %s\n") << filePath() << get_ft_error_message(error) );
00232         VL_CHECK(!error)
00233       }
00234       // else
00235       {
00236         Log::error(Say("FT_Set_Char_Size error (%s): %s\n") << filePath() << get_ft_error_message(error) );
00237         VL_TRAP()
00238         return glyph.get();
00239       }
00240     }
00241 
00242     mHeight = mFT_Face->size->metrics.height / 64.0f;
00243 
00244     // using FT_Load_Char instead of FT_Get_Char_Index + FT_Load_Glyph works better, probably
00245     // FreeType performs some extra tricks internally to better support less reliable fonts...
00246 
00247     // FT_UInt glyph_index = FT_Load_Char( mFT_Face, character, FT_LOAD_DEFAULT );
00248     // glyph->glyphIndex() = glyph_index;
00249     //FT_UInt glyph_index = FT_Get_Char_Index( mFT_Face, character );
00250     //glyph->glyphIndex() = glyph_index;
00252     //FT_Int32 load_flags = FT_LOAD_DEFAULT;
00253     //error = FT_Load_Glyph(
00254     //          mFT_Face,         /* handle to face object */
00255     //          glyph_index,  /* glyph index           */
00256     //          load_flags ); /* load flags, see below */
00257     //if(error)
00258     //{
00259     //  Log::error(Say("FT_Load_Glyph error: %s") << get_ft_error_message(error) );
00260     //  VL_TRAP()
00261     //  return glyph;
00262     //}
00263 
00264     // Note with FT 2.3.9 FT_LOAD_DEFAULT worked well, with FT 2.4 instead we ned FT_LOAD_FORCE_AUTOHINT
00265     // This might work well with VL's font but it might be suboptimal for other fonts.
00266 
00267     error = FT_Load_Char( mFT_Face, character, freeTypeLoadForceAutoHint() ? FT_LOAD_FORCE_AUTOHINT : FT_LOAD_DEFAULT );
00268 
00269     if(error)
00270     {
00271       Log::error(Say("FT_Load_Char error (%s): %s\n") << filePath() << get_ft_error_message(error) );
00272       VL_TRAP()
00273       glyph = NULL;
00274       return glyph.get();
00275     }
00276 
00277     glyph->setGlyphIndex( FT_Get_Char_Index( mFT_Face, character ) );
00278 
00279     error = FT_Render_Glyph(
00280               mFT_Face->glyph,  /* glyph slot */
00281               FT_RENDER_MODE_NORMAL ); /* render mode: FT_RENDER_MODE_MONO or FT_RENDER_MODE_NORMAL */
00282 
00283     // fonts like webdings.ttf generate an error when an unsupported char code is requested instead of
00284     // reverting to char code 0, so we have to do it by hand...
00285 
00286     if(error)
00287     {
00288       // Log::error(Say("FT_Render_Glyph error: %s") << get_ft_error_message(error) );
00289       // VL_TRAP()
00290       error = FT_Load_Glyph(
00291                 mFT_Face,/* handle to face object */
00292                 0,       /* glyph index           */
00293                 FT_LOAD_DEFAULT ); /* load flags, see below */
00294       glyph->setGlyphIndex(0);
00295 
00296       error = FT_Render_Glyph(
00297                 mFT_Face->glyph,  /* glyph slot */
00298                 FT_RENDER_MODE_NORMAL ); /* render mode: FT_RENDER_MODE_MONO or FT_RENDER_MODE_NORMAL */
00299     }
00300 
00301     if(error)
00302     {
00303       Log::error(Say("FT_Render_Glyph error (%s): %s\n") << filePath() << get_ft_error_message(error) );
00304       VL_TRAP()
00305       glyph = NULL;
00306       return glyph.get();
00307     }
00308 
00309     bool ok_format = mFT_Face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY || mFT_Face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO;
00310     ok_format &= mFT_Face->glyph->bitmap.palette_mode == 0;
00311     ok_format &= mFT_Face->glyph->bitmap.pitch > 0 || mFT_Face->glyph->bitmap.buffer == NULL;
00312 
00313     if (!ok_format)
00314     {
00315       Log::error( Say("Font::glyph() error (%s): glyph format not supported. Visualization Library currently supports only FT_PIXEL_MODE_GRAY and FT_PIXEL_MODE_MONO.\n") << filePath() );
00316       VL_TRAP()
00317       return glyph.get();
00318     }
00319 
00320     if ( mFT_Face->glyph->bitmap.buffer )
00321     {
00322       if (mHeight == 0)
00323         mHeight = (float)mFT_Face->glyph->bitmap.rows;
00324 
00325       glyph->setWidth ( mFT_Face->glyph->bitmap.width);
00326       glyph->setHeight( mFT_Face->glyph->bitmap.rows);
00327       glyph->setLeft  ( mFT_Face->glyph->bitmap_left);
00328       glyph->setTop   ( mFT_Face->glyph->bitmap_top);
00329 
00330       VL_CHECK( mFT_Face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY || mFT_Face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO )
00331       VL_CHECK( mFT_Face->glyph->bitmap.palette_mode == 0 )
00332       VL_CHECK( mFT_Face->glyph->bitmap.pitch > 0 )
00333 
00334       unsigned int texhdl;
00335       glGenTextures( 1, &texhdl );
00336       glyph->setTextureHandle(texhdl);
00337       VL_glActiveTexture(GL_TEXTURE0);
00338       glBindTexture( GL_TEXTURE_2D, glyph->textureHandle() );
00339 
00340       int texsize[] = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 0 };
00341       int max_tex_size = 0;
00342       glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
00343 
00344       int w=0, h=0, margin = 1;
00345 
00346       for(int i=0; texsize[i]; ++i)
00347       {
00348         if ( (texsize[i] >= glyph->width() + margin*2 && texsize[i] >= glyph->height() + margin*2) || texsize[i+1] > max_tex_size )
00349         {
00350           w = texsize[i];
00351           h = texsize[i];
00352           break;
00353         }
00354       }
00355       VL_CHECK(w)
00356       VL_CHECK(h)
00357 
00358 #if(1)
00359       // tex coords DO include the border
00360       glyph->setS0( 0 );
00361       glyph->setT0( 1 );
00362       glyph->setS1((margin*2 + glyph->width() ) /(float)(w-1) );
00363       glyph->setT1( 1 -(margin*2 + glyph->height() ) /(float)(h-1) );
00364 
00365       // tex coords DO NOT include the border
00366       //glyph->setS0((float)margin /(float)(w-1));
00367       //glyph->setT0( 1 -(float)margin /(float)(h-1));
00368       //glyph->setS1(((float)margin + glyph->width() ) /(float)(w-1));
00369       //glyph->setT1( 1 -((float)margin + glyph->height() ) /(float)(h-1));
00370 #else
00371       glyph->setS0((float)margin /(float)w);
00372       glyph->setT0( 1 -(float)margin /(float)h);
00373       glyph->setS1(((float)margin + glyph->width() ) /(float)w);
00374       glyph->setT1( 1 -((float)margin + glyph->height() ) /(float)h);
00375 #endif
00376 
00377       ref<Image> img = new Image;
00378       img->allocate2D(w, h, 1, IF_RGBA, IT_UNSIGNED_BYTE);
00379 
00380       // init to all transparent white
00381       for(unsigned char *px = img->pixels(), *end = px + img->requiredMemory(); px<end; px+=4)
00382       {
00383         px[0] = 0xFF;
00384         px[1] = 0xFF;
00385         px[2] = 0xFF;
00386         px[3] = 0x0;
00387       }
00388 
00389       // maps the glyph on the texture leaving a 1px margin
00390 
00391       for(int y=0; y<glyph->height(); y++)
00392       {
00393         for(int x=0; x<glyph->width(); x++)
00394         {
00395           int offset_1 = (x+margin) * 4 + (w-1-y-margin) * img->pitch();
00396           int offset_2 = 0;
00397           if (mFT_Face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
00398             offset_2 = x / 8 + y * ::abs(mFT_Face->glyph->bitmap.pitch);
00399           else
00400             offset_2 = x + y * mFT_Face->glyph->bitmap.pitch;
00401 
00402 #if (1)
00403           if (mFT_Face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
00404             img->pixels()[ offset_1+3 ] = (mFT_Face->glyph->bitmap.buffer[ offset_2 ] >> (7-x%8)) & 0x1 ? 0xFF : 0x0;
00405           else
00406             img->pixels()[ offset_1+3 ] = mFT_Face->glyph->bitmap.buffer[ offset_2 ];
00407 #else
00408           // debug code
00409           img->pixels()[ offset_1+0 ] = face->glyph->bitmap.buffer[ offset_2 ]; // 0xFF;
00410           img->pixels()[ offset_1+1 ] = face->glyph->bitmap.buffer[ offset_2 ]; // 0xFF;
00411           img->pixels()[ offset_1+2 ] = face->glyph->bitmap.buffer[ offset_2 ]; // 0xFF;
00412           img->pixels()[ offset_1+3 ] = 0xFF; // face->glyph->bitmap.buffer[ offset_2 ];
00413 #endif
00414         }
00415       }
00416 
00417       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img->width(), img->height(), 0, img->format(), img->type(), img->pixels() ); VL_CHECK_OGL();
00418 
00419       if ( smooth() )
00420       {
00421         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
00422         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
00423         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
00424         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
00425       }
00426       else
00427       {
00428         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
00429         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
00430         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
00431         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
00432       }
00433 
00434       // sets anisotropy to the maximum supported
00435       if (Has_GL_EXT_texture_filter_anisotropic)
00436       {
00437         float max_anisotropy;
00438         glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy);
00439         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy);
00440       }
00441 
00442       VL_CHECK_OGL();
00443       glBindTexture( GL_TEXTURE_2D, 0 );
00444     }
00445 
00446     glyph->setAdvance( fvec2( (float)mFT_Face->glyph->advance.x / 64.0f, (float)mFT_Face->glyph->advance.y / 64.0f ) );
00447   }
00448 
00449   return glyph.get();
00450 }
00451 //-----------------------------------------------------------------------------
00452 void Font::setSmooth(bool smooth)
00453 {
00454   mSmooth = smooth;
00455   std::map<int, ref<Glyph> >::iterator it = mGlyphMap.begin();
00456   for(; it != mGlyphMap.end(); ++it )
00457   {
00458     const ref<Glyph>& glyph = it->second;
00459     if (glyph->textureHandle() == 0)
00460       continue;
00461 
00462     glBindTexture( GL_TEXTURE_2D, glyph->textureHandle() );
00463     if (smooth)
00464     {
00465       glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
00466       glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
00467       glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
00468       glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
00469     }
00470     else
00471     {
00472       glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
00473       glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
00474       glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
00475       glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
00476     }
00477   }
00478   glBindTexture( GL_TEXTURE_2D, 0 );
00479 }
00480 //-----------------------------------------------------------------------------

Visualization Library 2011.09.1160 Reference Documentation
Copyright 2005-2011 Michele Bosi. All rights reserved.
Updated on Thu May 2 2013 13:40:36.
Permission is granted to use this page to write and publish articles regarding Visualization Library.