text.C

Go to the documentation of this file.
00001 /*
00002  * This file is part of the "Archon" framework.
00003  * (http://files3d.sourceforge.net)
00004  *
00005  * Copyright © 2002 by Kristian Spangsege and Brian Kristiansen.
00006  *
00007  * Permission to use, copy, modify, and distribute this software and
00008  * its documentation under the terms of the GNU General Public License is
00009  * hereby granted. No representations are made about the suitability of
00010  * this software for any purpose. It is provided "as is" without express
00011  * or implied warranty. See the GNU General Public License
00012  * (http://www.gnu.org/copyleft/gpl.html) for more details.
00013  *
00014  * The characters in this file are ISO8859-1 encoded.
00015  *
00016  * The documentation in this file is in "Doxygen" style
00017  * (http://www.doxygen.org).
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       // FontStyleNode
00055 
00056       FontStyleNode::type =
00057         NodeType::newAbstract("FontStyleNode", false, 0,
00058                               GeometricPropertyNode::type);
00059 
00060 
00061       // FontStyle
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       // TextNode
00089 
00090       TextNode::type =
00091         NodeType::newAbstract("TextNode", false, 0,
00092                               GeometryNode::type);
00093 
00094 
00095       // Text
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           //string style = bold ? italic ? "bolditalic" : "bold" : italic ? "italic" : "plain";
00241           //cerr << "Found \"" << family << "\" " << style << "\n";
00242 
00243           /*
00244           cerr << "Style name       = " << face->style_name << "\n";
00245           cerr << "Number of glyphs = " << face->num_faces << "\n";
00246           cerr << "Units per EM     = " << face->units_per_EM << "\n";
00247           cerr << "Height           = " << face->height << "\n";
00248           cerr << "Ascender         = " << face->ascender << "\n";
00249           cerr << "Descender        = " << face->descender << "\n";
00250           cerr << "Num fixed sizes  = " << face->num_fixed_sizes << "\n";
00251           cerr << "Num charmaps     = " << face->num_charmaps << "\n";
00252           if(face->face_flags&FT_FACE_FLAG_SCALABLE)
00253             cerr << "                   FT_FACE_FLAG_SCALABLE\n";
00254           if(face->face_flags&FT_FACE_FLAG_FIXED_SIZES)
00255             cerr << "                   FT_FACE_FLAG_FIXED_SIZES\n";
00256           if(face->face_flags&FT_FACE_FLAG_FIXED_WIDTH)
00257             cerr << "                   FT_FACE_FLAG_FIXED_WIDTH\n";
00258           if(face->face_flags&FT_FACE_FLAG_HORIZONTAL)
00259             cerr << "                   FT_FACE_FLAG_HORIZONTAL\n";
00260           if(face->face_flags&FT_FACE_FLAG_VERTICAL)
00261             cerr << "                   FT_FACE_FLAG_VERTICAL\n";
00262           if(face->face_flags&FT_FACE_FLAG_SFNT)
00263             cerr << "                   FT_FACE_FLAG_SFNT\n";
00264           if(face->face_flags&FT_FACE_FLAG_KERNING)
00265             cerr << "                   FT_FACE_FLAG_KERNING\n";
00266           if(face->face_flags&FT_FACE_FLAG_MULTIPLE_MASTERS)
00267             cerr << "                   FT_FACE_FLAG_MULTIPLE_MASTERS\n";
00268           if(face->face_flags&FT_FACE_FLAG_GLYPH_NAMES)
00269             cerr << "                   FT_FACE_FLAG_GLYPH_NAMES\n";
00270           if(face->face_flags&FT_FACE_FLAG_EXTERNAL_STREAM)
00271             cerr << "                   FT_FACE_FLAG_EXTERNAL_STREAM\n";
00272           if(face->face_flags&FT_FACE_FLAG_FAST_GLYPHS)
00273             cerr << "                   FT_FACE_FLAG_FAST_GLYPHS\n";
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             //cerr << "Searching for fonts in \"" << fontPaths[i] << "\"\n";
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               //cerr << "Found font file \"" << p << "\"\n";
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); // Could be reduced to 1 bit per component
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            * Minimum vertical and horizontal distance between glyphs
00397            * in texture mesured in number of texels. This spacing is
00398            * introduced to prevent the texels from one glyph to
00399            * "bleed" into the bbox of an adjacent glyph due to the
00400            * filtering done by OpenGL.
00401            */
00402           const int textureSpacing = 4;
00403 
00404           /*
00405            * Number of texels to expand the minimum bbox of the glyph
00406            * in the texture by to get the bbox to use for the actual
00407            * texture mapping. This of cause increases the size of the
00408            * rendered quad for the glyph. The reason for expanding the
00409            * minimum bbox is to allow the filtering done by OpenGL to
00410            * work correctly at the edges of the glyph. If there were
00411            * no padding then otherwise smooth edges of the glyph would
00412            * be cut off too sharply. Note that half the size of
00413            * 'textureSpacing' will usually be optimal.
00414            */
00415           const double texturePadding = textureSpacing/2.0;
00416 
00417           int xPos = textureSpacing;
00418           int yPos = textureSpacing;
00419           int maxHeight = 0;
00420 
00421           // Load first Unicode page into texture
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             //texture.save(family + " " + style + ".png");
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; // Inter character ajustment
00770 
00771       stringGlyphs.resize(_string.size());
00772       stringExtents.resize(_string.size());
00773       stringAjusts.resize(_string.size());
00774 
00775       // Find glyphs and calculate extents of strings
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        * The displacement of the lines in the primary direction of the
00811        * lines and relative the the effective length of each line.
00812        * before the origin of alignment
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); // Pass if incoming alpha is greater that 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 }

Generated on Sun Jul 30 22:55:45 2006 for Archon by  doxygen 1.4.4