00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 
00034 #include "platform/font.hpp"
00035 #include "platform/texture.hpp"
00036 
00037 #include "data/shared.hpp"
00038 #include "data/pointer.hpp"
00039 #include "data/dictionary.hpp"
00040 #include "data/cache.hpp"
00041 #include "data/directory.hpp"
00042 #include "data/file.hpp"
00043 
00044 #include "platform/display.hpp"
00045 #include "platform/lowlevel.hpp"
00046 
00047 namespace gsgl
00048 {
00049 
00050     using namespace data;
00051 
00052     namespace platform
00053     {
00054 
00055         static void clear_pixel_alpha(SDL_Surface *surface)
00056         {
00057             SDL_LockSurface(surface);
00058 
00059             unsigned char *pixels = static_cast<unsigned char *>(surface->pixels);
00060 
00061             for (int v = 0; v < surface->h; ++v)
00062             {
00063                 for (int u = 0; u < surface->w; ++u)
00064                 {
00065                     int index = u * surface->format->BytesPerPixel + v * surface->pitch;
00066                     unsigned int *pixel = reinterpret_cast<unsigned int *>(&pixels[index]);
00067 
00068                     *pixel &= ~surface->format->Amask;
00069                 }
00070             }
00071 
00072             SDL_UnlockSurface(surface);
00073         } 
00074 
00075 
00076         static void src_alpha_blit(SDL_Surface *src, SDL_Surface *dest, int dest_x, int dest_y)
00077         {
00078             SDL_LockSurface(src);
00079             SDL_LockSurface(dest);
00080 
00081             unsigned char *src_pixels = static_cast<unsigned char *>(src->pixels);
00082             unsigned char *dest_pixels = static_cast<unsigned char *>(dest->pixels);
00083 
00084             for (int v = 0; v < src->h; ++v)
00085             {
00086                 for (int u = 0; u < src->w; ++u)
00087                 {
00088                     int src_index = u * src->format->BytesPerPixel + v * src->pitch;
00089                     int dest_index = (u + dest_x) * src->format->BytesPerPixel + (v + dest_y) * dest->pitch;
00090 
00091                     unsigned int *src_pixel = reinterpret_cast<unsigned int *>(&src_pixels[src_index]);
00092                     unsigned int *dest_pixel = reinterpret_cast<unsigned int *>(&dest_pixels[dest_index]);
00093 
00094                     unsigned int sp = *src_pixel & (src->format->Rmask | src->format->Gmask | src->format->Bmask | src->format->Amask);
00095                     *dest_pixel |= sp;
00096                 }
00097             }
00098             
00099             SDL_UnlockSurface(dest);
00100             SDL_UnlockSurface(src);
00101         } 
00102 
00103 
00104         
00105 
00106         static int nearest_power_2(int n)
00107         {
00108             int res = 1;
00109             while (res < n)
00110                 res <<= 1;
00111             return res;
00112         } 
00113             
00114         
00115 
00116         class font_impl
00117             : public shared_object
00118         {
00119             const string face;
00120             const int size;
00121 
00122             int font_height;
00123             int font_ascent;
00124             int texture_height; 
00125 
00126             dictionary<float, wchar_t> glyph_pct_x, glyph_pct_y;
00127             dictionary<float, wchar_t> glyph_widths;
00128 
00129             TTF_Font *font;
00130             color fg;
00131             dictionary<texture *, wchar_t> glyph_textures;
00132         public:
00133             font_impl(const string &, int size, const color & fg);
00134             virtual ~font_impl();
00135 
00136             const string & get_face() const { return face; }
00137             const int get_size() const { return size; }
00138             const color & get_fg() const { return fg; }
00139 
00140             float calc_height(const string &);
00141             float calc_width(const string &);
00142 
00143             void draw(const string &);
00144 
00145         private:
00146             texture *get_glyph(const wchar_t);
00147 
00148             void get_font_dir();
00149         }; 
00150 
00151         
00152 
00153         static const string FONT_DIR;
00154 
00155         
00156 
00157         font_impl::font_impl(const string & face, int size, const color & fg)
00158             : shared_object(), face(face), size(size), font(0), fg(fg)
00159         {
00160             
00161 
00162             if (FONT_DIR.is_empty())
00163                 get_font_dir();
00164 
00165             
00166             string fname = FONT_DIR;
00167 
00168 #ifdef WIN32
00169             if (face == L"Mono")
00170             {
00171                 fname += L"cour.ttf";
00172             }
00173             else if (face == L"Sans")
00174             {
00175                 fname += L"arial.ttf";
00176             }
00177             else
00178             {
00179                 throw runtime_exception(L"Unable to determine font file name for '%ls'", face.w_string());
00180             }
00181 #else
00182 #error Font name lookup unimplemented!
00183 #endif
00184 
00185             
00186             font = TTF_OpenFont(fname.c_string(), size);
00187 
00188             if (!font)
00189                 throw runtime_exception(L"Unable to load font '%ls': %hs", face.w_string(), TTF_GetError());
00190 
00191             
00192             font_height = TTF_FontHeight(font);
00193             font_ascent = TTF_FontAscent(font);
00194             texture_height = nearest_power_2(font_height);
00195         } 
00196 
00197 
00198         font_impl::~font_impl()
00199         {
00200             
00201 
00202             for (dictionary<texture *, wchar_t>::iterator i = glyph_textures.iter(); i.is_valid(); ++i)
00203                 delete *i;
00204 
00205             TTF_CloseFont(font);
00206         } 
00207 
00208 
00209         float font_impl::calc_height(const string & str)
00210         {
00211             int w, h;
00212 
00213             if (TTF_SizeUTF8(font, str.c_string(), &w, &h) != -1)
00214                 return static_cast<float>(h);
00215             else
00216                 throw runtime_exception(L"%hs", TTF_GetError());
00217         } 
00218 
00219 
00220         float font_impl::calc_width(const string & str)
00221         {
00222             int w, h;
00223 
00224             if (TTF_SizeUTF8(font, str.c_string(), &w, &h) != -1)
00225                 return static_cast<float>(w);
00226             else
00227                 throw runtime_exception(L"%hs", TTF_GetError());
00228         } 
00229 
00230 
00231         void font_impl::draw(const string & str)
00232         {
00233             glPushAttrib(GL_ALL_ATTRIB_BITS);                                                                       CHECK_GL_ERRORS();
00234             glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);                                                          CHECK_GL_ERRORS();
00235 
00236             glPolygonMode(GL_FRONT, GL_FILL);                                                                       CHECK_GL_ERRORS();
00237 
00238             glEnable(GL_BLEND);                                                                                     CHECK_GL_ERRORS();
00239             glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);                                                      CHECK_GL_ERRORS();
00240 
00241             glEnable(GL_TEXTURE_2D);                                                                                CHECK_GL_ERRORS();
00242 
00243             const gsgl::index_t len = str.size();
00244             for (gsgl::index_t i = 0; i < len; ++i)
00245             {
00246                 const wchar_t ch = str[i];
00247 
00248                 texture *t = get_glyph(ch);
00249                 if (t)
00250                 {
00251                     t->bind();
00252                     
00253                     float pct_y = glyph_pct_y[ch];
00254                     float pct_x = glyph_pct_x[ch];
00255 
00256                     glBegin(GL_TRIANGLE_STRIP);
00257 
00258                     
00259                     glTexCoord2f(0.0f, pct_y);
00260                     glVertex2f(0.0f, static_cast<GLfloat>(font_height));
00261 
00262                     
00263                     glTexCoord2f(0.0f, 0.0f);
00264                     glVertex2f(0.0f, 0.0f);
00265 
00266                     
00267                     glTexCoord2f(pct_x, pct_y);
00268                     glVertex2f(glyph_widths[ch], static_cast<GLfloat>(font_height));
00269 
00270                     
00271                     glTexCoord2f(pct_x, 0.0f);
00272                     glVertex2f(glyph_widths[ch], 0.0f);
00273 
00274                     glEnd();                                                                                            CHECK_GL_ERRORS();
00275 
00276                     
00277                 }
00278 
00279                 glMatrixMode(GL_MODELVIEW);                                                                         CHECK_GL_ERRORS();
00280                 glTranslatef(glyph_widths[ch], 0.0f, 0.0f);                                                         CHECK_GL_ERRORS();
00281             }
00282 
00283             glPopClientAttrib();                                                                                    CHECK_GL_ERRORS();
00284             glPopAttrib();                                                                                          CHECK_GL_ERRORS();
00285         } 
00286 
00287 
00288         texture *font_impl::get_glyph(const wchar_t ch)
00289         {
00290             texture *tex = glyph_textures[ch];
00291 
00292             if (!tex)
00293             {
00294                 int minx, maxx, miny, maxy, advance;
00295                 if (TTF_GlyphMetrics(font, ch, &minx, &maxx, &miny, &maxy, &advance) == -1)
00296                     throw runtime_exception(L"error getting glyph metrics: %hs", TTF_GetError());
00297 
00298                 SDL_Color c;
00299                 c.r = static_cast<Uint8>(fg[color::COMPONENT_RED] * 255.0f);
00300                 c.g = static_cast<Uint8>(fg[color::COMPONENT_GREEN] * 255.0f);
00301                 c.b = static_cast<Uint8>(fg[color::COMPONENT_BLUE] * 255.0f);
00302 
00303                 SDL_Surface *surf1 = TTF_RenderGlyph_Blended(font, ch, c);
00304 
00305                 SDL_Surface *surf2 = SDL_CreateRGBSurface(SDL_SWSURFACE, nearest_power_2(minx + surf1->w), texture_height, 32, surf1->format->Rmask, surf1->format->Gmask, surf1->format->Bmask, surf1->format->Amask);
00306 
00307                 if (!surf2)
00308                     throw runtime_exception(L"unable to create SDL surface: %hs", SDL_GetError());
00309 
00310                 SDL_Rect dest;
00311 
00312                 dest.x = minx > 0 ? minx : 0;
00313                 dest.y = (surf2->h - font_height) + (font_ascent - maxy);
00314                 dest.w = surf1->w;
00315                 dest.h = surf1->h;
00316 
00317                 clear_pixel_alpha(surf2);
00318                 src_alpha_blit(surf1, surf2, dest.x, dest.y);
00319 
00320                 string glyph_id = string::format(L"%ls %d: %lc", face.w_string(), size, ch);
00321                 glyph_textures[ch] = tex = new texture(surf2, TEXTURE_ENV_REPLACE | TEXTURE_WRAP_CLAMP | TEXTURE_FILTER_LINEAR, TEXTURE_COLORMAP, glyph_id.w_string());
00322 
00323                 glyph_pct_x[ch] = static_cast<float>(advance) / static_cast<float>(surf2->w);
00324                 glyph_pct_y[ch] = static_cast<float>(font_height) / static_cast<float>(surf2->h);
00325                 glyph_widths[ch] = static_cast<float>(advance);
00326 
00327                 SDL_FreeSurface(surf2);
00328                 SDL_FreeSurface(surf1);
00329             }
00330 
00331             return tex;
00332         } 
00333 
00334 
00335         void font_impl::get_font_dir()
00336         {
00337 #ifdef WIN32
00338             smart_pointer<wchar_t, true> buf(new wchar_t[MAX_PATH]);
00339             
00340             if (SUCCEEDED(SHGetFolderPath(0, CSIDL_WINDOWS, 0, 0, buf)))
00341             {
00342                 string path(buf);
00343                 io::directory font_dir(path);
00344                 const_cast<string &>(FONT_DIR) = font_dir.get_full_path() + L"Fonts" + io::directory::SEPARATOR;
00345             }
00346             else
00347             {
00348                 throw io_exception(L"unable to find system font directory");
00349             }
00350 #endif
00351         } 
00352 
00353 
00354         
00355 
00356         typedef data::cache<font_impl> font_cache;
00357 
00358         
00359         
00360         font::font(const string & face, int size, const color & fg)
00361         {
00362             
00363             
00364             font_cache & global_fonts = *font_cache::global_instance();
00365 
00366             string cache_name = string::format(L"%s %d %f %f %f %f", face.w_string(), size, fg[color::COMPONENT_RED], fg[color::COMPONENT_GREEN], fg[color::COMPONENT_BLUE], fg[color::COMPONENT_ALPHA]);
00367             
00368             if (global_fonts.contains_index(cache_name))
00369             {
00370                 impl = global_fonts[cache_name];
00371             }
00372             else
00373             {
00374                 impl = new font_impl(face, size, fg);
00375                 global_fonts[cache_name] = impl;
00376                 impl->attach();
00377             }
00378             impl->attach();
00379         } 
00380         
00381 
00382         font::~font()
00383         {
00384             assert(impl);
00385 
00386             
00387             impl->detach();
00388         } 
00389         
00390 
00391         const string & font::get_face() const
00392         {
00393             assert(impl);
00394             return impl->get_face();
00395         } 
00396 
00397 
00398         const int font::get_size() const
00399         {
00400             assert(impl);
00401             return impl->get_size();
00402         } 
00403 
00404 
00405         float font::calc_height(const string & str) const
00406         {
00407             assert(impl);
00408             return impl->calc_height(str);
00409         } 
00410 
00411 
00412         float font::calc_width(const string & str) const
00413         {
00414             assert(impl);
00415             return impl->calc_width(str);
00416         } 
00417 
00418 
00419         void font::draw(const string & str) const
00420         {
00421             assert(impl);
00422             impl->draw(str);
00423         } 
00424 
00425 
00426         gsgl::data_object *font::create_global_font_cache()
00427         {
00428             return new font_cache(L"font cache");
00429         } 
00430 
00431 
00432     } 
00433 
00434 
00435     platform::font_cache *platform::font_cache::instance = 0; 
00436 
00437 }