00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00026 #include <iostream>
00027
00028 #include <ft2build.h>
00029 #include FT_FREETYPE_H
00030
00031 #include <GL/gl.h>
00032
00033 #include <archon/util/file.H>
00034 #include <archon/util/unicode.H>
00035 #include <archon/util/image.H>
00036
00037 #include <archon/x3d/server/field_type.H>
00038 #include <archon/x3d/server/field.H>
00039 #include <archon/x3d/server/text.H>
00040
00041 namespace Archon
00042 {
00043 namespace X3D
00044 {
00045 const NodeType *FontStyleNode::type = 0;
00046 const NodeType *FontStyle::type = 0;
00047 const NodeType *TextNode::type = 0;
00048 const NodeType *Text::type = 0;
00049
00050 void initializeTextComponent()
00051 {
00052 vector<const FieldBase *> fields;
00053
00054
00055
00056 FontStyleNode::type =
00057 NodeType::newAbstract("FontStyleNode", false, 0,
00058 GeometricPropertyNode::type);
00059
00060
00061
00062
00063 fields.push_back(newPrivateField("family", MFString::type,
00064 &FontStyle::family));
00065 fields.push_back(newPrivateField("horizontal", SFBool::type,
00066 &FontStyle::horizontal));
00067 fields.push_back(newPrivateField("justify", MFString::type,
00068 &FontStyle::justify));
00069 fields.push_back(newPrivateField("language", SFString::type,
00070 &FontStyle::language));
00071 fields.push_back(newPrivateField("leftToRight", SFBool::type,
00072 &FontStyle::leftToRight));
00073 fields.push_back(newPrivateField("size", SFFloat::type,
00074 &FontStyle::size));
00075 fields.push_back(newPrivateField("spacing", SFFloat::type,
00076 &FontStyle::spacing));
00077 fields.push_back(newPrivateField("style", SFString::type,
00078 &FontStyle::style));
00079 fields.push_back(newPrivateField("topToBottom", SFBool::type,
00080 &FontStyle::topToBottom));
00081 FontStyle::type =
00082 NodeType::newConcrete("FontStyle", "fontStyle",
00083 FontStyle::instantiate, &fields,
00084 FontStyleNode::type);
00085 fields.clear();
00086
00087
00088
00089
00090 TextNode::type =
00091 NodeType::newAbstract("TextNode", false, 0,
00092 GeometryNode::type);
00093
00094
00095
00096
00097 fields.push_back(newExposedField("string", MFString::type,
00098 &Text::_string,
00099 &Text::stringChanged,
00100 &Text::stringStamp));
00101 fields.push_back(newExposedField("fontStyle",
00102 &Text::fontStyle,
00103 &Text::fontStyleChanged,
00104 &Text::fontStyleStamp));
00105 fields.push_back(newExposedField("length", MFFloat::type,
00106 &Text::length,
00107 &Text::lengthChanged,
00108 &Text::lengthStamp));
00109 fields.push_back(newExposedField("maxExtent", SFFloat::type,
00110 &Text::maxExtent,
00111 &Text::maxExtentChanged,
00112 &Text::maxExtentStamp));
00113 fields.push_back(newPrivateField("solid", SFBool::type,
00114 &Text::solid));
00115
00116 Text::type =
00117 NodeType::newConcrete("Text", "geometry",
00118 Text::instantiate, &fields,
00119 TextNode::type);
00120 fields.clear();
00121 }
00122
00132 int Text::intersect(const Math::Ray3 &, double &dist) const
00133 {
00134 return 0;
00135 }
00136
00137 void Text::getNormalAndTexCoord(Vector3 hitPoint, int where,
00138 const Shape *,
00139 Vector3 *hitNormal,
00140 Vector2 *hitTexCoord) const
00141 {
00142 }
00143
00144
00145 namespace
00146 {
00147 struct Glyph
00148 {
00152 Vector2 size;
00153
00159 Vector2 horizontalLowerLeft;
00160
00164 double horizontalAdvance;
00165
00171 Vector2 verticalLowerLeft;
00172
00176 double verticalAdvance;
00177
00178 Vector2 textureLowerLeft;
00179 Vector2 textureUpperRight;
00180
00181 Glyph(Vector2 size,
00182 Vector2 horizontalLowerLeft,
00183 double horizontalAdvance,
00184 Vector2 verticalUpperRight,
00185 double verticalAdvance,
00186 Vector2 textureLowerLeft,
00187 Vector2 textureUpperRight):
00188 size(size),
00189 horizontalLowerLeft(horizontalLowerLeft),
00190 horizontalAdvance(horizontalAdvance),
00191 verticalLowerLeft(verticalLowerLeft),
00192 verticalAdvance(verticalAdvance),
00193 textureLowerLeft(textureLowerLeft),
00194 textureUpperRight(textureUpperRight) {}
00195 };
00196
00197 struct FontFace;
00198
00199 struct FontList
00200 {
00201 Mutex mutex;
00202
00203 static vector<string> fontPaths;
00204 static int pixelSize;
00205
00206 struct Entry
00207 {
00208 string plainFilePath;
00209 int plainFileIndex;
00210 FontFace *plainFace;
00211
00212 string boldFilePath;
00213 int boldFileIndex;
00214 FontFace *boldFace;
00215
00216 string italicFilePath;
00217 int italicFileIndex;
00218 FontFace *italicFace;
00219
00220 string bolditalicFilePath;
00221 int bolditalicFileIndex;
00222 FontFace *bolditalicFace;
00223
00224 Entry():
00225 plainFace(0), boldFace(0),
00226 italicFace(0), bolditalicFace(0) {}
00227 };
00228
00229 map<string, Entry> fontFamilies;
00230
00231 FT_Library library;
00232
00233 bool addFace(string path, int index, FT_Face face)
00234 {
00235 string family = face->family_name;
00236
00237 bool bold = face->style_flags&FT_STYLE_FLAG_BOLD;
00238 bool italic = face->style_flags&FT_STYLE_FLAG_ITALIC;
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276 Entry &entry = fontFamilies[family];
00277
00278 if(!bold && !italic)
00279 {
00280 if(entry.plainFilePath.size()) return false;
00281 entry.plainFilePath = path;
00282 entry.plainFileIndex = index;
00283 }
00284 else if(bold && !italic)
00285 {
00286 if(entry.boldFilePath.size()) return false;
00287 entry.boldFilePath = path;
00288 entry.boldFileIndex = index;
00289 }
00290 else if(!bold && italic)
00291 {
00292 if(entry.italicFilePath.size()) return false;
00293 entry.italicFilePath = path;
00294 entry.italicFileIndex = index;
00295 }
00296 else if(bold && italic)
00297 {
00298 if(entry.bolditalicFilePath.size()) return false;
00299 entry.bolditalicFilePath = path;
00300 entry.bolditalicFileIndex = index;
00301 }
00302
00303 return true;
00304 }
00305
00306 FontList()
00307 {
00308 if(FT_Init_FreeType(&library))
00309 ARCHON_THROW1(ResourceException,
00310 "Error initializing FreeType library");
00311
00312 int n = 0;
00313
00314 for(unsigned i=0; i<fontPaths.size(); ++i)
00315 {
00316
00317 vector<string> dirNames = File::getDirNames(fontPaths[i]);
00318 for(unsigned j=0; j<dirNames.size(); ++j)
00319 {
00320 string name = dirNames[j];
00321 if(name == "." || name == "..") continue;
00322 string p = fontPaths[i]+"/"+name;
00323 if(File::Stat(p).getType() != File::Stat::type_regular) continue;
00324
00325 FT_Face face;
00326 if(FT_New_Face(library, p.c_str(), 0, &face)) continue;
00327
00328
00329
00330 if(addFace(p, 0, face)) ++n;
00331
00332 unsigned m = face->num_faces;
00333 FT_Done_Face(face);
00334
00335 for(unsigned k=1; k<m; ++k)
00336 {
00337 if(FT_New_Face(library, p.c_str(), k, &face)) break;
00338 if(addFace(p, k, face)) ++n;
00339 FT_Done_Face(face);
00340 }
00341 }
00342 }
00343
00344 cerr << "Found " + Archon::Utilities::Text::toString(n) +
00345 " font faces in " + Archon::Utilities::Text::toString(fontPaths.size()) +
00346 " directories\n";
00347 }
00348
00349 FontFace *getFace(string familyName, bool bold, bool italic);
00350 FontFace *getDefaultFace();
00351 };
00352
00353 vector<string> FontList::fontPaths;
00354 int FontList::pixelSize = 32;
00355
00356 struct FontFace
00357 {
00358 Mutex mutex;
00359
00360 double horizontalBottom;
00361 double horizontalTop;
00362
00363 double verticalLeft;
00364 double verticalRight;
00365
00366 vector<const Glyph *> glyphs;
00367
00368 GLuint textureId;
00369
00370 FontFace(string filePath, int faceIndex, int pixelSize)
00371 {
00372 const FontList *fontList = getFontList();
00373
00374 FT_Face face;
00375 if(FT_New_Face(fontList->library, filePath.c_str(), faceIndex, &face))
00376 ARCHON_THROW1(ResourceException,
00377 "Font file \"" + filePath + "\" is no longer available");
00378
00379 if(FT_Set_Pixel_Sizes(face, 0, pixelSize))
00380 ARCHON_THROW1(InternalException,
00381 "Error setting pixel size of font face");
00382
00383 FT_GlyphSlot slot = face->glyph;
00384
00385 glyphs.resize(256);
00386
00387 int imageWidth = 16*pixelSize;
00388 int imageHeight = 16*pixelSize;
00389 Image texture(imageWidth, imageHeight, Image::components_la, 8);
00390
00391 for(int x=0; x<imageWidth; ++x)
00392 for(int y=0; y<imageHeight; ++y)
00393 texture.setPixel(x, y, 255u, 0u);
00394
00395
00396
00397
00398
00399
00400
00401
00402 const int textureSpacing = 4;
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415 const double texturePadding = textureSpacing/2.0;
00416
00417 int xPos = textureSpacing;
00418 int yPos = textureSpacing;
00419 int maxHeight = 0;
00420
00421
00422 for(unsigned i=0; i<256; ++i)
00423 {
00424 if(FT_Load_Char(face, static_cast<unsigned char>(i), FT_LOAD_RENDER))
00425 continue;
00426 if(int(slot->bitmap.pixel_mode) != 2)
00427 ARCHON_THROW1(InternalException,
00428 "Unsupported pixel data encoding in font");
00429
00430 if(slot->bitmap.width+textureSpacing > imageWidth - xPos)
00431 {
00432 yPos += maxHeight+textureSpacing;
00433 xPos = textureSpacing;
00434 maxHeight = 0;
00435 }
00436
00437 if(slot->bitmap.width+textureSpacing > imageWidth - xPos ||
00438 slot->bitmap.rows+textureSpacing > imageHeight - yPos)
00439 ARCHON_THROW1(InternalException,
00440 "All glyphs won't fit inside texture");
00441
00442 for(int x=0; x<slot->bitmap.width; ++x)
00443 for(int y=0; y<slot->bitmap.rows; ++y)
00444 {
00445 unsigned l = (static_cast<unsigned char *>(slot->bitmap.buffer))
00446 [x + y * slot->bitmap.width];
00447 texture.setPixel(xPos+x, imageHeight-1-yPos-y, 255, l);
00448 }
00449
00450 double linesPerTexel = face->units_per_EM/double(face->height*pixelSize);
00451
00452 horizontalBottom = 0;
00453 horizontalTop = pixelSize*linesPerTexel;
00454 verticalLeft = 0;
00455 verticalRight = pixelSize*linesPerTexel;
00456
00457 Vector2 horizontalLowerLeft;
00458 double horizontalAdvance;
00459 Vector2 verticalLowerLeft;
00460 double verticalAdvance;
00461
00462 if(face->face_flags&FT_FACE_FLAG_HORIZONTAL)
00463 {
00464 horizontalLowerLeft = verticalLowerLeft =
00465 Vector2(slot->bitmap_left-texturePadding,
00466 slot->bitmap_top-slot->bitmap.rows-texturePadding)*
00467 linesPerTexel;
00468
00469 horizontalAdvance = slot->advance.x/64 * linesPerTexel;
00470 verticalAdvance = 1;
00471 }
00472 else
00473 {
00474 horizontalLowerLeft = verticalLowerLeft =
00475 Vector2(slot->bitmap_left-texturePadding,
00476 slot->bitmap_top-slot->bitmap.rows-texturePadding)*
00477 linesPerTexel;
00478
00479 horizontalAdvance = 1;
00480 verticalAdvance = slot->advance.y/64 * linesPerTexel;
00481 }
00482
00483 glyphs[i] =
00484 new Glyph(Vector2(slot->bitmap.width + 2*texturePadding,
00485 slot->bitmap.rows + 2*texturePadding) *
00486 linesPerTexel,
00487 horizontalLowerLeft, horizontalAdvance,
00488 verticalLowerLeft, verticalAdvance,
00489 Vector2(double(xPos-texturePadding)/imageWidth,
00490 double(imageHeight-1-yPos-slot->bitmap.rows-texturePadding)/imageHeight),
00491 Vector2(double(xPos+slot->bitmap.width+texturePadding)/imageWidth,
00492 double(imageHeight-1-yPos+texturePadding)/imageHeight));
00493
00494 xPos += slot->bitmap.width+textureSpacing;
00495 if(slot->bitmap.rows > maxHeight) maxHeight = slot->bitmap.rows;
00496 }
00497
00498 {
00499 string family = face->family_name;
00500 bool bold = face->style_flags&FT_STYLE_FLAG_BOLD;
00501 bool italic = face->style_flags&FT_STYLE_FLAG_ITALIC;
00502 string style = bold ? italic ? "bolditalic" : "bold" : italic ? "italic" : "plain";
00503
00504 }
00505
00506 FT_Done_Face(face);
00507
00508 {
00509 int colorMode;
00510 switch(texture.getComponentSpecifier())
00511 {
00512 case Utilities::Image::components_l: colorMode = GL_LUMINANCE; break;
00513 case Utilities::Image::components_la: colorMode = GL_LUMINANCE_ALPHA; break;
00514 case Utilities::Image::components_rgb: colorMode = GL_RGB; break;
00515 case Utilities::Image::components_rgba: colorMode = GL_RGBA; break;
00516 default:
00517 ARCHON_THROW1(InternalException,
00518 "Unsuported color components");
00519 }
00520
00521 int componentType;
00522 switch(texture.getBitsPerComponent())
00523 {
00524 case 8: componentType = GL_UNSIGNED_BYTE; break;
00525 case 16: componentType = GL_UNSIGNED_SHORT; break;
00526 default:
00527 ARCHON_THROW1(InternalException,
00528 "Unsuported color component width");
00529 }
00530
00531 glGenTextures(1, &textureId);
00532 glBindTexture(GL_TEXTURE_2D, textureId);
00533 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
00534 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_CLAMP);
00535 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
00536 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
00537 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
00538
00539 glTexImage2D(GL_TEXTURE_2D, 0, colorMode, texture.getWidth(),
00540 texture.getHeight(), 0, colorMode, componentType,
00541 texture.getPixelBuffer());
00542 }
00543 }
00544
00549 const Glyph *getGlyph(uchar i)
00550 {
00551 Mutex::Lock l(mutex);
00552 if(i > 255) return 0;
00553 return glyphs[i];
00554 }
00555
00561 static FontFace *get()
00562 {
00563 FontList *fontList = getFontList();
00564 return fontList->getDefaultFace();
00565 }
00566
00572 static FontFace *get(string familyName, bool bold, bool italic)
00573 {
00574 FontList *fontList = getFontList();
00575 return fontList->getFace(familyName, bold, italic);
00576 }
00577
00578 private:
00579 static FontList *getFontList()
00580 {
00581 static FontList fontList;
00582 return &fontList;
00583 }
00584 };
00585 }
00586
00587 FontFace *FontList::getDefaultFace()
00588 {
00589 FontFace *f = getFace("Times New Roman", false, false);
00590 if(f) return f;
00591 ARCHON_THROW1(InternalException,
00592 "Could not find default font face");
00593 }
00594
00595 FontFace *FontList::getFace(string familyName, bool bold, bool italic)
00596 {
00597 Mutex::Lock l(mutex);
00598
00599 map<string, Entry>::iterator i = fontFamilies.find(familyName);
00600 if(i == fontFamilies.end()) return 0;
00601
00602 if(!bold && !italic)
00603 {
00604 if(!i->second.plainFilePath.size()) return 0;
00605 if(!i->second.plainFace)
00606 i->second.plainFace =
00607 new FontFace(i->second.plainFilePath, i->second.plainFileIndex, pixelSize);
00608 return i->second.plainFace;
00609 }
00610
00611 if(bold && !italic)
00612 {
00613 if(!i->second.boldFilePath.size()) return 0;
00614 if(!i->second.boldFace)
00615 i->second.boldFace =
00616 new FontFace(i->second.boldFilePath, i->second.boldFileIndex, pixelSize);
00617 return i->second.boldFace;
00618 }
00619
00620 if(!bold && italic)
00621 {
00622 if(!i->second.italicFilePath.size()) return 0;
00623 if(!i->second.italicFace)
00624 i->second.italicFace =
00625 new FontFace(i->second.italicFilePath, i->second.italicFileIndex, pixelSize);
00626 return i->second.italicFace;
00627 }
00628
00629 if(!i->second.bolditalicFilePath.size()) return 0;
00630 if(!i->second.bolditalicFace)
00631 i->second.bolditalicFace =
00632 new FontFace(i->second.bolditalicFilePath, i->second.bolditalicFileIndex, pixelSize);
00633 return i->second.bolditalicFace;
00634 }
00635
00636
00637
00716 void Text::render(bool texture,
00717 const Shape *shape,
00718 const RenderConfig *renderConfig)
00719 {
00720 const FontStyle *f = dynamic_cast<const FontStyle *>(fontStyle.get());
00721
00722 if(!f && fontStyle)
00723 ARCHON_THROW1(InternalException,
00724 "'" + fontStyle->getType()->getName() +
00725 "' is not supported yet");
00726
00727 FontFace *face = 0;
00728
00729 if(f) for(unsigned i=0; i<f->getFamily().size(); ++i)
00730 {
00731 bool bold = false;
00732 bool italic = false;
00733 if(f->getStyle() == "BOLD") bold = true;
00734 else if(f->getStyle() == "ITALIC") italic = true;
00735 else if(f->getStyle() == "BOLDITALIC") bold = italic = true;
00736
00737 face = FontFace::get(f->getFamily()[i], bold, italic);
00738 if(face) break;
00739
00740 if(f->getFamily()[i] == "SERIF")
00741 {
00742 face = FontFace::get("Times New Roman", bold, italic);
00743 if(face) break;
00744 }
00745 else if(f->getFamily()[i] == "SANS")
00746 {
00747 face = FontFace::get("Arial", bold, italic);
00748 if(face) break;
00749 }
00750 else if(f->getFamily()[i] == "TYPEWRITER")
00751 {
00752 face = FontFace::get("Courier New", bold, italic);
00753 if(face) break;
00754 }
00755 }
00756
00757 if(!face) face = FontFace::get();
00758
00759 bool horizontal = f ? f->getHorizontal() : true;
00760 bool leftToRight = f ? f->getLeftToRight() : true;
00761 bool topToBottom = f ? f->getTopToBottom() : true;
00762
00763 const double size = f ? f->getSize() : 1;
00764 double lineAdvance = f ? f->getSpacing()*size : 1;
00765 if(topToBottom) lineAdvance = -lineAdvance;
00766
00767 vector<vector<const Glyph *> > stringGlyphs;
00768 vector<double> stringExtents;
00769 vector<double> stringAjusts;
00770
00771 stringGlyphs.resize(_string.size());
00772 stringExtents.resize(_string.size());
00773 stringAjusts.resize(_string.size());
00774
00775
00776 double max = 0;
00777 for(unsigned i=0; i<_string.size(); ++i)
00778 {
00779 ustring s = Unicode::decodeUtf8(_string[i]);
00780
00781 stringGlyphs[i].reserve(s.size());
00782 double extent = 0;
00783
00784 for(unsigned j=0; j<s.size(); ++j)
00785 {
00786 const Glyph *g = face->getGlyph(s[j]);
00787 if(!g) continue;
00788 stringGlyphs[i].push_back(g);
00789 extent += (horizontal ? g->horizontalAdvance : g->verticalAdvance)*size;
00790 }
00791
00792 if(i >= length.size() || length[i] == 0)
00793 {
00794 stringExtents[i] = extent;
00795 stringAjusts[i] = 0;
00796 }
00797 else
00798 {
00799 stringExtents[i] = length[i];
00800 if(stringGlyphs[i].size() > 1)
00801 stringAjusts[i] = (length[i] - extent)/(stringGlyphs[i].size()-1);
00802 }
00803
00804 if(stringExtents[i] > max) max = stringExtents[i];
00805 }
00806
00807 double compress = maxExtent > 0 && max > maxExtent ? maxExtent/max : 1;
00808
00809
00810
00811
00812
00813
00814 double primaryAlignment = 0;
00815 if(f && f->getJustify().size())
00816 {
00817 string s = f->getJustify()[0];
00818 if(s == "MIDDLE") primaryAlignment = -0.5;
00819 else if(s == "END") primaryAlignment = -1;
00820 }
00821 const bool primaryDirection = horizontal ? leftToRight : topToBottom;
00822 if(!primaryDirection) primaryAlignment = - primaryAlignment;
00823
00824 double totalSecondaryExtent =
00825 (horizontal ? face->horizontalTop - face->horizontalBottom :
00826 face->verticalRight - face->verticalLeft)+_string.size()-1;
00827
00828 double lineOrigin = 0;
00829 if(f && f->getJustify().size()>1)
00830 {
00831 string s = f->getJustify()[1];
00832 if(s == "BEGIN") lineOrigin = topToBottom ? -face->horizontalTop : 0;
00833 else if(s == "MIDDLE")
00834 lineOrigin =
00835 topToBottom ? totalSecondaryExtent/2 - face->horizontalTop :
00836 -totalSecondaryExtent/2;
00837 else if(s == "END")
00838 lineOrigin =
00839 topToBottom ? totalSecondaryExtent - face->horizontalTop :
00840 -totalSecondaryExtent;
00841 }
00842
00843 glFrontFace(GL_CCW);
00844 glDisable(GL_COLOR_MATERIAL);
00845 if(solid)
00846 {
00847 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0);
00848 glEnable(GL_CULL_FACE);
00849 }
00850 else
00851 {
00852 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
00853 glDisable(GL_CULL_FACE);
00854 }
00855 glNormal3d(0, 0, 1);
00856
00857 if(renderConfig->textAsQuadsMode || renderConfig->wireframeMode)
00858 {
00859 glDisable(GL_TEXTURE_2D);
00860 }
00861 else
00862 {
00863 glAlphaFunc(GL_GREATER, 0);
00864 glEnable(GL_ALPHA_TEST);
00865 glEnable(GL_TEXTURE_2D);
00866 glBindTexture(GL_TEXTURE_2D, face->textureId);
00867 }
00868
00869 glBegin(GL_QUADS);
00870
00871 for(unsigned i=0; i<_string.size(); ++i)
00872 {
00873 Vector2 pen(stringExtents[i] * primaryAlignment, lineOrigin);
00874 const double ajust = stringAjusts[i];
00875
00876 for(unsigned j=0; j<stringGlyphs[i].size(); ++j)
00877 {
00878 const Glyph *g = stringGlyphs[i][j];
00879
00880 if(!leftToRight)
00881 pen[0] -= g->horizontalAdvance*size+ajust;
00882
00883 Vector2 layoutLowerLeft = pen + g->horizontalLowerLeft * size;
00884 Vector2 layoutUpperRight = layoutLowerLeft + g->size * size;
00885
00886 layoutLowerLeft[0] *= compress;
00887 layoutUpperRight[0] *= compress;
00888
00889 glTexCoord2d(g->textureUpperRight[0], g->textureUpperRight[1]);
00890 glVertex2d(layoutUpperRight[0], layoutUpperRight[1]);
00891
00892 glTexCoord2d(g->textureLowerLeft[0], g->textureUpperRight[1]);
00893 glVertex2d(layoutLowerLeft[0], layoutUpperRight[1]);
00894
00895 glTexCoord2d(g->textureLowerLeft[0], g->textureLowerLeft[1]);
00896 glVertex2d(layoutLowerLeft[0], layoutLowerLeft[1]);
00897
00898 glTexCoord2d(g->textureUpperRight[0], g->textureLowerLeft[1]);
00899 glVertex2d(layoutUpperRight[0], layoutLowerLeft[1]);
00900
00901 if(leftToRight)
00902 pen[0] += g->horizontalAdvance*size+ajust;
00903 }
00904
00905 lineOrigin += lineAdvance;
00906 }
00907 glEnd();
00908
00909 glDisable(GL_ALPHA_TEST);
00910 }
00911
00912 void addFontPath(string p)
00913 {
00914 FontList::fontPaths.push_back(p);
00915 }
00916 }
00917 }