00001 #include "gui_gl_font.hpp"
00002 #include "gui_gl_material.hpp"
00003 #include <gui_manager.hpp>
00004 #include <gui_out.hpp>
00005 #include <utils_string.hpp>
00006 #include <utils_filesystem.hpp>
00007
00008 #include <ft2build.h>
00009 #include FT_FREETYPE_H
00010 #include FT_GLYPH_H
00011
00012 namespace gui {
00013 namespace gl
00014 {
00015 font::font(const std::string& sFontFile, uint uiSize) : bKerning_(false)
00016 {
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 if (!utils::file_exists(sFontFile))
00027 throw gui::exception("gui::gl::font", "Couldn't find file \""+sFontFile+"\".");
00028
00029 FT_Library mFT;
00030 if (FT_Init_FreeType(&mFT))
00031 throw gui::exception("gui::gl::font", "Error initializing FreeType !");
00032
00033 FT_Face mFace;
00034
00035
00036 uint uiSpacing = 1;
00037
00038 if (FT_New_Face(mFT, sFontFile.c_str(), 0, &mFace))
00039 {
00040 throw gui::exception("gui::gl::font", "Error loading font : \""+sFontFile+
00041 "\" : couldn't load face."
00042 );
00043 }
00044
00045 if (FT_Set_Char_Size(mFace, uiSize*64, uiSize*64, 96, 96))
00046 {
00047 throw gui::exception("gui::gl::font", "Error loading font : \""+sFontFile+
00048 "\" : couldn't set font size."
00049 );
00050 }
00051
00052 int iMaxHeight = 0, iMaxWidth = 0, iMaxBearingY = 0;
00053
00054 const FT_Int32 iLoadFlags = FT_LOAD_RENDER | FT_LOAD_NO_HINTING;
00055
00056
00057 for (uint cp = 32; cp <= 255; ++cp)
00058 {
00059 if (FT_Load_Char(mFace, cp, iLoadFlags))
00060 continue;
00061
00062 int iCharHeight = 2*(mFace->glyph->bitmap.rows << 6) - mFace->glyph->metrics.horiBearingY;
00063 if (iCharHeight > iMaxHeight)
00064 iMaxHeight = iCharHeight;
00065
00066 if (mFace->glyph->metrics.horiBearingY > iMaxBearingY)
00067 iMaxBearingY = mFace->glyph->metrics.horiBearingY;
00068
00069 int iCharWidth = std::max(
00070 mFace->glyph->bitmap.width + int(mFace->glyph->metrics.horiBearingX >> 6),
00071 int(mFace->glyph->advance.x >> 6)
00072 );
00073
00074 if (iCharWidth > iMaxWidth)
00075 iMaxWidth = iCharWidth;
00076 }
00077
00078 iMaxBearingY = iMaxBearingY >> 6;
00079 iMaxHeight = iMaxHeight >> 6;
00080
00081
00082 size_t uiTexSize = (iMaxWidth + uiSpacing)*(iMaxHeight + uiSpacing)*(255-33);
00083
00084 uint uiTexSide = static_cast<uint>(::sqrt(uiTexSize));
00085 uiTexSide += std::max(iMaxWidth, iMaxHeight);
00086
00087
00088 uint i = 1;
00089 while (uiTexSide > i)
00090 i += i;
00091 uiTexSide = i;
00092
00093 size_t uiFinalWidth, uiFinalHeight;
00094 if (uiTexSide*uiTexSide/2 >= uiTexSize)
00095 uiFinalHeight = uiTexSide/2;
00096 else
00097 uiFinalHeight = uiTexSide;
00098
00099 uiFinalWidth = uiTexSide;
00100
00101 fTextureWidth_ = static_cast<float>(uiFinalWidth);
00102 fTextureHeight_ = static_cast<float>(uiFinalHeight);
00103
00104 pTexture_ = utils::refptr<gl::material>(new material(uiFinalWidth, uiFinalHeight));
00105 std::fill(pTexture_->get_data().begin(), pTexture_->get_data().end(), ub32color({0, 0, 0, 0}));
00106
00107 lCharacterList_.resize(256);
00108
00109 size_t x = 0, y = 0;
00110 character_info mCI;
00111
00112 if (FT_HAS_KERNING(mFace))
00113 bKerning_ = true;
00114
00115 if (bKerning_)
00116 mCI.lKerningInfo.resize(256);
00117
00118 for (uint cp = 32; cp <= 255; ++cp)
00119 {
00120 mCI.uiCodePoint = cp;
00121
00122 if (FT_Load_Char(mFace, cp, iLoadFlags))
00123 {
00124 gui::out << gui::warning << "gui::gl::font : Can't load character " << cp
00125 << " in font \"" << sFontFile << "\"." << std::endl;
00126 continue;
00127 }
00128
00129 size_t uiXBearing = std::max(0, int(mFace->glyph->metrics.horiBearingX >> 6));
00130
00131 ub32color::chanel* sBuffer = mFace->glyph->bitmap.buffer;
00132 if (sBuffer)
00133 {
00134 int iYBearing = iMaxBearingY - (mFace->glyph->metrics.horiBearingY >> 6);
00135
00136 for (int j = 0; j < mFace->glyph->bitmap.rows; ++j)
00137 for (int i = 0; i < mFace->glyph->bitmap.width; ++i, ++sBuffer)
00138 pTexture_->set_pixel(x + i + uiXBearing, y + j + iYBearing, ub32color({255, 255, 255, *sBuffer}));
00139 }
00140
00141 if (bKerning_)
00142 {
00143 FT_Vector kern;
00144 unsigned int prev, next;
00145 for (uint cp2 = 33; cp2 <= 255; ++cp2)
00146 {
00147 prev = FT_Get_Char_Index(mFace, cp);
00148 next = FT_Get_Char_Index(mFace, cp2);
00149 if (!FT_Get_Kerning(mFace, prev, next, FT_KERNING_UNFITTED, &kern))
00150 mCI.lKerningInfo[cp2] = vector2f(kern.x >> 6, kern.y >> 6);
00151 }
00152 }
00153
00154 FT_Int iAdvance = std::max(int(uiXBearing + mFace->glyph->bitmap.width), int(mFace->glyph->advance.x >> 6));
00155
00156 mCI.fU1 = x/float(uiFinalWidth);
00157 mCI.fV1 = y/float(uiFinalHeight);
00158 mCI.fU2 = (x + iAdvance)/float(uiFinalWidth);
00159 mCI.fV2 = (y + iMaxHeight)/float(uiFinalHeight);
00160
00161 lCharacterList_[cp] = mCI;
00162
00163
00164 x += (iAdvance + uiSpacing);
00165
00166
00167 if (x + iAdvance > uiFinalWidth - 1)
00168 {
00169 y += iMaxHeight + uiSpacing;
00170 x = 0;
00171 }
00172 }
00173
00174
00175 if (!FT_Load_Char(mFace, 32, iLoadFlags))
00176 {
00177 lCharacterList_[32].fU1 = 0.0f;
00178 lCharacterList_[32].fU2 = (mFace->glyph->advance.x >> 6)/float(uiFinalWidth);
00179 }
00180
00181 FT_Done_FreeType(mFT);
00182
00183 pTexture_->premultiply_alpha();
00184 pTexture_->update_texture();
00185 pTexture_->clear_cache_data_();
00186 }
00187
00188 font::~font()
00189 {
00190 }
00191
00192 std::array<float,4> font::get_character_uvs(uint uiChar) const
00193 {
00194 const character_info& c = lCharacterList_[uiChar];
00195 return {{c.fU1, c.fV1, c.fU2, c.fV2}};
00196 }
00197
00198 float font::get_character_width(uint uiChar) const
00199 {
00200 const character_info& c = lCharacterList_[uiChar];
00201 return (c.fU2 - c.fU1)*fTextureWidth_;
00202 }
00203
00204 float font::get_character_kerning(uint uiChar1, uint uiChar2) const
00205 {
00206 if (bKerning_)
00207 return lCharacterList_[uiChar1].lKerningInfo[uiChar2].x;
00208 else
00209 return 0.0f;
00210 }
00211
00212 utils::wptr<gui::material> font::get_texture() const
00213 {
00214 return pTexture_;
00215 }
00216 }
00217 }