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 #ifndef ARCHON_X3D_FONT_H
00034 #define ARCHON_X3D_FONT_H
00035
00036 #include <string>
00037 #include <vector>
00038
00039 #include <archon/util/ref.H>
00040
00041 namespace Archon
00042 {
00043 namespace X3D
00044 {
00045 using namespace std;
00046 using namespace Utilities;
00047
00048 struct Server;
00049
00056 struct FontServer: virtual RefObjectBase
00057 {
00058 FontServer(Ref<Server>, vector<string> fontDirs);
00059
00060 const BackRef<Server> server;
00061
00062 private:
00063 vector<string> fontDirs;
00064 };
00065
00066
00067
00068
00069
00070
00071 namespace
00072 {
00073 struct Glyph
00074 {
00078 Vector2 size;
00079
00085 Vector2 horizontalLowerLeft;
00086
00090 double horizontalAdvance;
00091
00097 Vector2 verticalLowerLeft;
00098
00102 double verticalAdvance;
00103
00104 Vector2 textureLowerLeft;
00105 Vector2 textureUpperRight;
00106
00107 Glyph(Vector2 size,
00108 Vector2 horizontalLowerLeft,
00109 double horizontalAdvance,
00110 Vector2 verticalUpperRight,
00111 double verticalAdvance,
00112 Vector2 textureLowerLeft,
00113 Vector2 textureUpperRight):
00114 size(size),
00115 horizontalLowerLeft(horizontalLowerLeft),
00116 horizontalAdvance(horizontalAdvance),
00117 verticalLowerLeft(verticalLowerLeft),
00118 verticalAdvance(verticalAdvance),
00119 textureLowerLeft(textureLowerLeft),
00120 textureUpperRight(textureUpperRight) {}
00121 };
00122
00123 struct FontFace;
00124
00125 struct FontList
00126 {
00127 Mutex mutex;
00128
00129 static vector<string> fontPaths;
00130 static int pixelSize;
00131
00132 struct Entry
00133 {
00134 string plainFilePath;
00135 int plainFileIndex;
00136 FontFace *plainFace;
00137
00138 string boldFilePath;
00139 int boldFileIndex;
00140 FontFace *boldFace;
00141
00142 string italicFilePath;
00143 int italicFileIndex;
00144 FontFace *italicFace;
00145
00146 string bolditalicFilePath;
00147 int bolditalicFileIndex;
00148 FontFace *bolditalicFace;
00149
00150 Entry():
00151 plainFace(0), boldFace(0),
00152 italicFace(0), bolditalicFace(0) {}
00153 };
00154
00155 map<string, Entry> fontFamilies;
00156
00157 FT_Library library;
00158
00159 bool addFace(string path, int index, FT_Face face)
00160 {
00161 string family = face->family_name;
00162
00163 bool bold = face->style_flags&FT_STYLE_FLAG_BOLD;
00164 bool italic = face->style_flags&FT_STYLE_FLAG_ITALIC;
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202 Entry &entry = fontFamilies[family];
00203
00204 if(!bold && !italic)
00205 {
00206 if(entry.plainFilePath.size()) return false;
00207 entry.plainFilePath = path;
00208 entry.plainFileIndex = index;
00209 }
00210 else if(bold && !italic)
00211 {
00212 if(entry.boldFilePath.size()) return false;
00213 entry.boldFilePath = path;
00214 entry.boldFileIndex = index;
00215 }
00216 else if(!bold && italic)
00217 {
00218 if(entry.italicFilePath.size()) return false;
00219 entry.italicFilePath = path;
00220 entry.italicFileIndex = index;
00221 }
00222 else if(bold && italic)
00223 {
00224 if(entry.bolditalicFilePath.size()) return false;
00225 entry.bolditalicFilePath = path;
00226 entry.bolditalicFileIndex = index;
00227 }
00228
00229 return true;
00230 }
00231
00232 FontList()
00233 {
00234 if(FT_Init_FreeType(&library))
00235 ARCHON_THROW1(ResourceException,
00236 "Error initializing FreeType library");
00237
00238 int n = 0;
00239
00240 for(unsigned i=0; i<fontPaths.size(); ++i)
00241 {
00242
00243 vector<string> dirNames;
00244 try
00245 {
00246 dirNames = File::getDirNames(fontPaths[i]);
00247 }
00248 catch(File::ArgumentException &)
00249 {
00250
00251 continue;
00252 }
00253 for(unsigned j=0; j<dirNames.size(); ++j)
00254 {
00255 string name = dirNames[j];
00256 if(name == "." || name == "..") continue;
00257 string p = fontPaths[i]+"/"+name;
00258 if(File::Stat(p).getType() != File::Stat::type_regular) continue;
00259
00260 FT_Face face;
00261 if(FT_New_Face(library, p.c_str(), 0, &face)) continue;
00262
00263
00264
00265 if(addFace(p, 0, face)) ++n;
00266
00267 unsigned m = face->num_faces;
00268 FT_Done_Face(face);
00269
00270 for(unsigned k=1; k<m; ++k)
00271 {
00272 if(FT_New_Face(library, p.c_str(), k, &face)) break;
00273 if(addFace(p, k, face)) ++n;
00274 FT_Done_Face(face);
00275 }
00276 }
00277 }
00278
00279 cerr << "Found " + Archon::Utilities::Text::toString(n) +
00280 " font faces in " + Archon::Utilities::Text::toString(fontPaths.size()) +
00281 " directories\n";
00282 }
00283
00284 FontFace *getFace(string familyName, bool bold, bool italic);
00285 FontFace *getDefaultFace();
00286 };
00287
00288 vector<string> FontList::fontPaths;
00289 int FontList::pixelSize = 32;
00290
00291 struct FontFace
00292 {
00293 Mutex mutex;
00294
00295 double horizontalBottom;
00296 double horizontalTop;
00297
00298 double verticalLeft;
00299 double verticalRight;
00300
00301 vector<const Glyph *> glyphs;
00302
00303 GLuint textureId;
00304
00305 FontFace(string filePath, int faceIndex, int pixelSize)
00306 {
00307 const FontList *fontList = getFontList();
00308
00309 FT_Face face;
00310 if(FT_New_Face(fontList->library, filePath.c_str(), faceIndex, &face))
00311 ARCHON_THROW1(ResourceException,
00312 "Font file \"" + filePath + "\" is no longer available");
00313
00314 if(FT_Set_Pixel_Sizes(face, 0, pixelSize))
00315 ARCHON_THROW1(InternalException,
00316 "Error setting pixel size of font face");
00317
00318 FT_GlyphSlot slot = face->glyph;
00319
00320 glyphs.resize(256);
00321
00322
00323 int imageWidth = 16*pixelSize;
00324 int imageHeight = 16*pixelSize;
00325 Image texture(imageWidth, imageHeight, Image::components_la, 8);
00326
00327
00328 for(int x=0; x<imageWidth; ++x)
00329 for(int y=0; y<imageHeight; ++y)
00330 texture.setPixel(x, y, 255u, 0u);
00331
00332
00333
00334
00335
00336
00337
00338
00339 const int textureSpacing = 4;
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352 const double texturePadding = textureSpacing/2.0;
00353
00354 int xPos = textureSpacing;
00355 int yPos = textureSpacing;
00356 int maxHeight = 0;
00357
00358
00359 for(unsigned i=0; i<256; ++i)
00360 {
00361 if(FT_Load_Char(face, static_cast<unsigned char>(i), FT_LOAD_RENDER))
00362 continue;
00363 if(int(slot->bitmap.pixel_mode) != 2)
00364 ARCHON_THROW1(InternalException,
00365 "Unsupported pixel data encoding in font");
00366
00367
00368 if(slot->bitmap.width+textureSpacing > imageWidth - xPos)
00369 {
00370 yPos += maxHeight+textureSpacing;
00371 xPos = textureSpacing;
00372 maxHeight = 0;
00373 }
00374
00375 if(slot->bitmap.width+textureSpacing > imageWidth - xPos ||
00376 slot->bitmap.rows+textureSpacing > imageHeight - yPos)
00377 ARCHON_THROW1(InternalException,
00378 "All glyphs won't fit inside texture");
00379
00380
00381 for(int x=0; x<slot->bitmap.width; ++x)
00382 for(int y=0; y<slot->bitmap.rows; ++y)
00383 {
00384 unsigned l = (static_cast<unsigned char *>(slot->bitmap.buffer))
00385 [x + y * slot->bitmap.width];
00386 texture.setPixel(xPos+x, imageHeight-1-yPos-y, 255, l);
00387 }
00388
00389 double linesPerTexel = face->units_per_EM/double(face->height*pixelSize);
00390
00391 horizontalBottom = 0;
00392 horizontalTop = pixelSize*linesPerTexel;
00393 verticalLeft = 0;
00394 verticalRight = pixelSize*linesPerTexel;
00395
00396 Vector2 horizontalLowerLeft;
00397 double horizontalAdvance;
00398 Vector2 verticalLowerLeft;
00399 double verticalAdvance;
00400
00401 if(face->face_flags&FT_FACE_FLAG_HORIZONTAL)
00402 {
00403 horizontalLowerLeft = verticalLowerLeft =
00404 Vector2(slot->bitmap_left-texturePadding,
00405 slot->bitmap_top-slot->bitmap.rows-texturePadding)*
00406 linesPerTexel;
00407
00408 horizontalAdvance = slot->advance.x/64 * linesPerTexel;
00409 verticalAdvance = 1;
00410 }
00411 else
00412 {
00413 horizontalLowerLeft = verticalLowerLeft =
00414 Vector2(slot->bitmap_left-texturePadding,
00415 slot->bitmap_top-slot->bitmap.rows-texturePadding)*
00416 linesPerTexel;
00417
00418 horizontalAdvance = 1;
00419 verticalAdvance = slot->advance.y/64 * linesPerTexel;
00420 }
00421
00422 glyphs[i] =
00423 new Glyph(Vector2(slot->bitmap.width + 2*texturePadding,
00424 slot->bitmap.rows + 2*texturePadding) *
00425 linesPerTexel,
00426 horizontalLowerLeft, horizontalAdvance,
00427 verticalLowerLeft, verticalAdvance,
00428 Vector2(double(xPos-texturePadding)/imageWidth,
00429 double(imageHeight-1-yPos-slot->bitmap.rows-texturePadding)/imageHeight),
00430 Vector2(double(xPos+slot->bitmap.width+texturePadding)/imageWidth,
00431 double(imageHeight-1-yPos+texturePadding)/imageHeight));
00432
00433 xPos += slot->bitmap.width+textureSpacing;
00434 if(slot->bitmap.rows > maxHeight) maxHeight = slot->bitmap.rows;
00435 }
00436
00437 {
00438 string family = face->family_name;
00439 bool bold = face->style_flags&FT_STYLE_FLAG_BOLD;
00440 bool italic = face->style_flags&FT_STYLE_FLAG_ITALIC;
00441 string style = bold ? italic ? "bolditalic" : "bold" : italic ? "italic" : "plain";
00442 texture.save(family + " " + style + ".png");
00443 }
00444
00445 FT_Done_Face(face);
00446
00447 {
00448 int colorMode;
00449 switch(texture.getComponentSpecifier())
00450 {
00451 case Utilities::Image::components_l: colorMode = GL_LUMINANCE; break;
00452 case Utilities::Image::components_la: colorMode = GL_LUMINANCE_ALPHA; break;
00453 case Utilities::Image::components_rgb: colorMode = GL_RGB; break;
00454 case Utilities::Image::components_rgba: colorMode = GL_RGBA; break;
00455 default:
00456 ARCHON_THROW1(InternalException,
00457 "Unsuported color components");
00458 }
00459
00460 int componentType;
00461 switch(texture.getBitsPerComponent())
00462 {
00463 case 8: componentType = GL_UNSIGNED_BYTE; break;
00464 case 16: componentType = GL_UNSIGNED_SHORT; break;
00465 default:
00466 ARCHON_THROW1(InternalException,
00467 "Unsuported color component width");
00468 }
00469
00470 glGenTextures(1, &textureId);
00471 glBindTexture(GL_TEXTURE_2D, textureId);
00472 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
00473 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_CLAMP);
00474 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
00475 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
00476 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
00477
00478 glTexImage2D(GL_TEXTURE_2D, 0, colorMode, texture.getWidth(),
00479 texture.getHeight(), 0, colorMode, componentType,
00480 texture.getDataPtr());
00481 }
00482 }
00483
00488 const Glyph *getGlyph(uchar i)
00489 {
00490 Mutex::Lock l(mutex);
00491 if(i > 255) return 0;
00492 return glyphs[i];
00493 }
00494
00500 static FontFace *get()
00501 {
00502 FontList *fontList = getFontList();
00503 return fontList->getDefaultFace();
00504 }
00505
00511 static FontFace *get(string familyName, bool bold, bool italic)
00512 {
00513 FontList *fontList = getFontList();
00514 return fontList->getFace(familyName, bold, italic);
00515 }
00516
00517 private:
00518 static FontList *getFontList()
00519 {
00520 static FontList fontList;
00521 return &fontList;
00522 }
00523 };
00524 }
00525
00526 FontFace *FontList::getDefaultFace()
00527 {
00528 FontFace *f = getFace("Times New Roman", false, false);
00529 if(f) return f;
00530 ARCHON_THROW1(InternalException,
00531 "Could not find default font face");
00532 }
00533
00534 FontFace *FontList::getFace(string familyName, bool bold, bool italic)
00535 {
00536 Mutex::Lock l(mutex);
00537
00538 map<string, Entry>::iterator i = fontFamilies.find(familyName);
00539 if(i == fontFamilies.end()) return 0;
00540
00541 if(!bold && !italic)
00542 {
00543 if(!i->second.plainFilePath.size()) return 0;
00544 if(!i->second.plainFace)
00545 i->second.plainFace =
00546 new FontFace(i->second.plainFilePath, i->second.plainFileIndex, pixelSize);
00547 return i->second.plainFace;
00548 }
00549
00550 if(bold && !italic)
00551 {
00552 if(!i->second.boldFilePath.size()) return 0;
00553 if(!i->second.boldFace)
00554 i->second.boldFace =
00555 new FontFace(i->second.boldFilePath, i->second.boldFileIndex, pixelSize);
00556 return i->second.boldFace;
00557 }
00558
00559 if(!bold && italic)
00560 {
00561 if(!i->second.italicFilePath.size()) return 0;
00562 if(!i->second.italicFace)
00563 i->second.italicFace =
00564 new FontFace(i->second.italicFilePath, i->second.italicFileIndex, pixelSize);
00565 return i->second.italicFace;
00566 }
00567
00568 if(!i->second.bolditalicFilePath.size()) return 0;
00569 if(!i->second.bolditalicFace)
00570 i->second.bolditalicFace =
00571 new FontFace(i->second.bolditalicFilePath, i->second.bolditalicFileIndex, pixelSize);
00572 return i->second.bolditalicFace;
00573 }
00574
00575
00576
00577 }
00578 }
00579
00580 #endif // ARCHON_X3D_FONT_H