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 }