Visualization Library v1.0.3A lightweight C++ OpenGL middleware for 2D/3D graphics |
[Download] [Tutorials] [All Classes] [Grouped Classes] |
00001 /**************************************************************************************/ 00002 /* */ 00003 /* Visualization Library */ 00004 /* http://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 //-----------------------------------------------------------------------------