topical media & game development

talk show tell print

lib-of-vs-libs-openFrameworks-graphics-ofTrueTypeFont.cpp / cpp



  include <ofTrueTypeFont.h>
  //--------------------------
  
  if defined (TARGET_WIN32) || defined (TARGET_LINUX)
          #include <ft2build.h>
          #include <freetype/freetype.h>
          #include <freetype/ftglyph.h>
          #include <freetype/ftoutln.h>
          #include <freetype/fttrigon.h>
  endif
  
  if defined (TARGET_OSX) || defined (TARGET_OF_IPHONE)
          #include <ft2build.h>
          #include <freetype.h>
          #include <ftglyph.h>
          #include <ftoutln.h>
          #include <fttrigon.h>
  endif
  
  static bool printVectorInfo = false;
  
  //This is for polygon/contour simplification - we use it to reduce the number of points needed in
  //representing the letters as openGL shapes - will soon be moved to ofGraphics.cpp
  
  // From: http://softsurfer.com/Archive/algorithm_0205/algorithm_0205.htm
  // Copyright 2002, softSurfer (www.softsurfer.com)
  // This code may be freely used and modified for any purpose
  // providing that this copyright notice is included with it.
  // SoftSurfer makes no warranty for this code, and cannot be held
  // liable for any real or imagined damage resulting from its use.
  // Users of this code must verify correctness for their application.
  
  typedef struct{
          ofPoint P0;
          ofPoint P1;
  }Segment;
  
  // dot product (3D) which allows vector operations in arguments
  define dot(u,v)   ((u).x * (v).x + (u).y * (v).y + (u).z * (v).z)
  define norm2(v)   dot(v,v)        // norm2 = squared length of vector
  define norm(v)    sqrt(norm2(v))  // norm = length of vector
  define d2(u,v)    norm2(u-v)      // distance squared = norm2 of difference
  define d(u,v)     norm(u-v)       // distance = norm of difference
  
  static void simplifyDP(float tol, ofPoint* v, int j, int k, int* mk ){
      if (k <= j+1) // there is nothing to simplify
          return;
  
      // check for adequate approximation by segment S from v[j] to v[k]
      int     maxi        = j;          // index of vertex farthest from S
      float   maxd2        = 0;         // distance squared of farthest vertex
      float   tol2        = tol * tol;  // tolerance squared
      Segment S                = {v[j], v[k]};  // segment from v[j] to v[k]
      ofPoint u;
          u                                = S.P1 - S.P0;   // segment direction vector
      double  cu                = dot(u,u);     // segment length squared
  
      // test each vertex v[i] for max distance from S
      // compute using the Feb 2001 Algorithm's dist_ofPoint_to_Segment()
      // Note: this works in any dimension (2D, 3D, ...)
      ofPoint  w;
      ofPoint   Pb;                // base of perpendicular from v[i] to S
      float  b, cw, dv2;        // dv2 = distance v[i] to S squared
  
      for (int i=j+1; i<k; i++){
          // compute distance squared
          w = v[i] - S.P0;
          cw = dot(w,u);
          if ( cw <= 0 ) dv2 = d2(v[i], S.P0);
          else if ( cu <= cw ) dv2 = d2(v[i], S.P1);
          else {
              b = (float)(cw / cu);
              Pb = S.P0 + u*b;
              dv2 = d2(v[i], Pb);
          }
          // test with current max distance squared
          if (dv2 <= maxd2) continue;
  
          // v[i] is a new max vertex
          maxi = i;
          maxd2 = dv2;
      }
      if (maxd2 > tol2)        // error is worse than the tolerance
      {
          // split the polyline at the farthest vertex from S
          mk[maxi] = 1;      // mark v[maxi] for the simplified polyline
          // recursively simplify the two subpolylines at v[maxi]
          simplifyDP( tol, v, j, maxi, mk );  // polyline v[j] to v[maxi]
          simplifyDP( tol, v, maxi, k, mk );  // polyline v[maxi] to v[k]
      }
      // else the approximation is OK, so ignore intermediate vertices
      return;
  }
  
  //-------------------------------------------------------------------
  // needs simplifyDP which is above
  static vector <ofPoint> ofSimplifyContour(vector <ofPoint> &V, float tol){
          int n = V.size();
  
          vector <ofPoint> sV;
          sV.assign(n, ofPoint());
  
      int    i, k, m, pv;            // misc counters
      float  tol2 = tol * tol;       // tolerance squared
      ofPoint * vt = new ofPoint[n];
          int * mk = new int[n];
  
          memset(mk, 0, sizeof(int) * n );
  
      // STAGE 1.  Vertex Reduction within tolerance of prior vertex cluster
      vt[0] = V[0];              // start at the beginning
      for (i=k=1, pv=0; i<n; i++) {
          if (d2(V[i], V[pv]) < tol2) continue;
  
          vt[k++] = V[i];
          pv = i;
      }
      if (pv < n-1) vt[k++] = V[n-1];      // finish at the end
  
      // STAGE 2.  Douglas-Peucker polyline simplification
      mk[0] = mk[k-1] = 1;       // mark the first and last vertices
      simplifyDP( tol, vt, 0, k-1, mk );
  
      // copy marked vertices to the output simplified polyline
      for (i=m=0; i<k; i++) {
          if (mk[i]) sV[m++] = vt[i];
      }
  
          //get rid of the unused points
          if( m < (int)sV.size() ) sV.erase( sV.begin()+m, sV.end() );
  
          delete [] vt;
          delete [] mk;
  
          return sV;
  }
  
  //------------------------------------------------------------
  static void quad_bezier(vector <ofPoint> &ptsList, float x1, float y1, float x2, float y2, float x3, float y3, int res){
          for(int i=0; i <= res; i++){
          double t = (double)i / (double)(res);
          double a = pow((1.0 - t), 2.0);
          double b = 2.0 * t * (1.0 - t);
          double c = pow(t, 2.0);
          double x = a * x1 + b * x2 + c * x3;
          double y = a * y1 + b * y2 + c * y3;
                  ptsList.push_back(ofPoint((float)x, (float)y));
      }
  }
  
  //-----------------------------------------------------------
  static void cubic_bezier(vector <ofPoint> &ptsList, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, int res){
          float   ax, bx, cx;
      float   ay, by, cy;
      float   t, t2, t3;
      float   x, y;
  
      // polynomial coefficients
      cx = 3.0f * (x1 - x0);
      bx = 3.0f * (x2 - x1) - cx;
      ax = x3 - x0 - cx - bx;
  
      cy = 3.0f * (y1 - y0);
      by = 3.0f * (y2 - y1) - cy;
      ay = y3 - y0 - cy - by;
  
      int resolution = res;
  
      for (int i = 0; i < resolution; i++){
              t         =  (float)i / (float)(resolution-1);
              t2 = t * t;
              t3 = t2 * t;
                  x = (ax * t3) + (bx * t2) + (cx * t) + x0;
              y = (ay * t3) + (by * t2) + (cy * t) + y0;
              ptsList.push_back(ofPoint(x,y) );
      }
  }
  
  //--------------------------------------------------------
  static ofTTFCharacter makeContoursForCharacter(FT_Face &face);
  static ofTTFCharacter makeContoursForCharacter(FT_Face &face){
  
                  //int num                        = face->glyph->outline.n_points;
                  int nContours        = face->glyph->outline.n_contours;
                  int startPos        = 0;
  
                  char * tags                = face->glyph->outline.tags;
                  FT_Vector * vec = face->glyph->outline.points;
  
                  ofTTFCharacter charOutlines;
  
                  for(int k = 0; k < nContours; k++){
                          if( k > 0 ){
                                  startPos = face->glyph->outline.contours[k-1]+1;
                          }
                          int endPos = face->glyph->outline.contours[k]+1;
  
                          if( printVectorInfo )printf("--NEW CONTOUR\n\n");
  
                          vector <ofPoint> testOutline;
                          ofPoint lastPoint;
  
                          for(int j = startPos; j < endPos; j++){
  
                                  if( FT_CURVE_TAG(tags[j]) == FT_CURVE_TAG_ON ){
                                          lastPoint.set((float)vec[j].x, (float)-vec[j].y, 0);
                                          if( printVectorInfo )printf("flag[\%i] is set to 1 - regular point - \%f \%f \n", j, lastPoint.x, lastPoint.y);
                                          testOutline.push_back(lastPoint);
  
                                  }else{
                                          if( printVectorInfo )printf("flag[\%i] is set to 0 - control point \n", j);
  
                                          if( FT_CURVE_TAG(tags[j]) == FT_CURVE_TAG_CUBIC ){
                                                  if( printVectorInfo )printf("- bit 2 is set to 2 - CUBIC\n");
  
                                                  int prevPoint = j-1;
                                                  if( j == 0){
                                                          prevPoint = endPos-1;
                                                  }
  
                                                  int nextIndex = j+1;
                                                  if( nextIndex >= endPos){
                                                          nextIndex = startPos;
                                                  }
  
                                                  ofPoint nextPoint( (float)vec[nextIndex].x,  -(float)vec[nextIndex].y );
  
                                                  //we need two control points to draw a cubic bezier
                                                  bool lastPointCubic =  ( FT_CURVE_TAG(tags[prevPoint]) != FT_CURVE_TAG_ON ) && ( FT_CURVE_TAG(tags[prevPoint]) == FT_CURVE_TAG_CUBIC);
  
                                                  if( lastPointCubic ){
                                                          ofPoint controlPoint1((float)vec[prevPoint].x,        (float)-vec[prevPoint].y);
                                                          ofPoint controlPoint2((float)vec[j].x, (float)-vec[j].y);
                                                          ofPoint nextPoint((float) vec[nextIndex].x,        -(float) vec[nextIndex].y);
  
                                                          cubic_bezier(testOutline, lastPoint.x, lastPoint.y, controlPoint1.x, controlPoint1.y, controlPoint2.x, controlPoint2.y, nextPoint.x, nextPoint.y, 8);
                                                  }
  
                                          }else{
  
                                                  ofPoint conicPoint( (float)vec[j].x,  -(float)vec[j].y );
  
                                                  if( printVectorInfo )printf("- bit 2 is set to 0 - conic- \n");
                                                  if( printVectorInfo )printf("--- conicPoint point is \%f \%f \n", conicPoint.x, conicPoint.y);
  
                                                  //If the first point is connic and the last point is connic then we need to create a virutal point which acts as a wrap around
                                                  if( j == startPos ){
                                                          bool prevIsConnic = (  FT_CURVE_TAG( tags[endPos-1] ) != FT_CURVE_TAG_ON ) && ( FT_CURVE_TAG( tags[endPos-1]) != FT_CURVE_TAG_CUBIC );
  
                                                          if( prevIsConnic ){
                                                                  ofPoint lastConnic((float)vec[endPos - 1].x, (float)-vec[endPos - 1].y);
                                                                  lastPoint = (conicPoint + lastConnic) / 2;
  
                                                                  if( printVectorInfo )        printf("NEED TO MIX WITH LAST\n");
                                                                  if( printVectorInfo )printf("last is \%f \%f \n", lastPoint.x, lastPoint.y);
                                                          }
                                                  }
  
                                                  //bool doubleConic = false;
  
                                                  int nextIndex = j+1;
                                                  if( nextIndex >= endPos){
                                                          nextIndex = startPos;
                                                  }
  
                                                  ofPoint nextPoint( (float)vec[nextIndex].x,  -(float)vec[nextIndex].y );
  
                                                  if( printVectorInfo )printf("--- last point is \%f \%f \n", lastPoint.x, lastPoint.y);
  
                                                  bool nextIsConnic = (  FT_CURVE_TAG( tags[nextIndex] ) != FT_CURVE_TAG_ON ) && ( FT_CURVE_TAG( tags[nextIndex]) != FT_CURVE_TAG_CUBIC );
  
                                                  //create a 'virtual on point' if we have two connic points
                                                  if( nextIsConnic ){
                                                          nextPoint = (conicPoint + nextPoint) / 2;
                                                          if( printVectorInfo )printf("|_______ double connic!\n");
                                                  }
                                                  if( printVectorInfo )printf("--- next point is \%f \%f \n", nextPoint.x, nextPoint.y);
  
                                                  quad_bezier(testOutline, lastPoint.x, lastPoint.y, conicPoint.x, conicPoint.y, nextPoint.x, nextPoint.y, 8);
  
                                                  if( nextIsConnic ){
                                                          lastPoint = nextPoint;
                                                  }
                                          }
                                  }
  
                          //end for
                          }
  
                          for(int g =0; g < (int)testOutline.size(); g++){
                                  testOutline[g] /= 64.0f;
                          }
  
                          charOutlines.contours.push_back(ofTTFContour());
  
                          if( testOutline.size() ){
                                  charOutlines.contours.back().pts = ofSimplifyContour(testOutline, (float)TTF_SHAPE_SIMPLIFICATION_AMNT);
                          }else{
                                  charOutlines.contours.back().pts = testOutline;
                          }
                  }
  
          return charOutlines;
  }
  
  //------------------------------------------------------------------
  ofTrueTypeFont::ofTrueTypeFont(){
          bLoadedOk                = false;
          bMakeContours        = false;
  }
  
  //------------------------------------------------------------------
  ofTrueTypeFont::~ofTrueTypeFont(){
  
          if (bLoadedOk){
  
                  if (cps != NULL){
                          delete cps;
                  }
  
                  if (texNames != NULL){
                          for (int i = 0; i < nCharacters; i++){
                                  glDeleteTextures(1, &texNames[i]);
                          }
                          delete[] texNames;
                  }
          }
  }
  
  //------------------------------------------------------------------
  void ofTrueTypeFont::loadFont(string filename, int fontsize){
          // load anti-aliased, non-full character set:
          loadFont(filename, fontsize, true, false, false);
  }
  
  //------------------------------------------------------------------
  void ofTrueTypeFont::loadFont(string filename, int fontsize, bool _bAntiAliased, bool _bFullCharacterSet, bool makeContours){
  
          bMakeContours = makeContours;
  
          //------------------------------------------------
          if (bLoadedOk == true){
  
                  // we've already been loaded, try to clean up :
  
                  if (cps != NULL){
                          delete cps;
                  }
                  if (texNames != NULL){
                          for (int i = 0; i < nCharacters; i++){
                                  glDeleteTextures(1, &texNames[i]);
                          }
                          delete texNames;
                  }
                  bLoadedOk = false;
          }
          //------------------------------------------------
  
          filename = ofToDataPath(filename);
  
          bLoadedOk                         = false;
          bAntiAlised                 = _bAntiAliased;
          bFullCharacterSet         = _bFullCharacterSet;
          fontSize                        = fontsize;
  
          //--------------- load the library and typeface
          FT_Library library;
          if (FT_Init_FreeType( &library )){
                  ofLog(OF_LOG_ERROR," PROBLEM WITH FT lib");
                  return;
          }
  
          FT_Face face;
          if (FT_New_Face( library, filename.c_str(), 0, &face )) {
                  return;
          }
  
          FT_Set_Char_Size( face, fontsize << 6, fontsize << 6, 96, 96);
          lineHeight = fontsize * 1.43f;
  
          //------------------------------------------------------
          //kerning would be great to support:
          //ofLog(OF_LOG_NOTICE,"FT_HAS_KERNING ? \%i", FT_HAS_KERNING(face));
          //------------------------------------------------------
  
          nCharacters = bFullCharacterSet ? 256 : 128 - NUM_CHARACTER_TO_START;
  
          //--------------- initialize character info and textures
          cps       = new charProps[nCharacters];
          texNames  = new GLuint[nCharacters];
          glGenTextures(nCharacters, texNames);
  
          if(bMakeContours){
                  charOutlines.clear();
                  charOutlines.assign(nCharacters, ofTTFCharacter());
          }
          //--------------------- load each char -----------------------
          for (int i = 0 ; i < nCharacters; i++){
  
                  //------------------------------------------ anti aliased or not:
                  if(FT_Load_Glyph( face, FT_Get_Char_Index( face, (unsigned char)(i+NUM_CHARACTER_TO_START) ), FT_LOAD_DEFAULT )){
                          ofLog(OF_LOG_ERROR,"error with FT_Load_Glyph \%i", i);
                  }
  
                  if (bAntiAlised == true) FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
                  else FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO);
  
                  //------------------------------------------
                  FT_Bitmap& bitmap= face->glyph->bitmap;
  
                  // 3 pixel border around the glyph
                  // We show 2 pixels of this, so that blending looks good.
                  // 1 pixels is hidden because we don't want to see the real edge of the texture
  
                  border                        = 3;
                  visibleBorder        = 2;
  
                  if(bMakeContours){
                          if( printVectorInfo )printf("\n\ncharacter \%c: \n", char( i+NUM_CHARACTER_TO_START ) );
  
                          //int character = i + NUM_CHARACTER_TO_START;
                          charOutlines[i] = makeContoursForCharacter( face );
                  }
  
                  // prepare the texture:
                  int width  = ofNextPow2( bitmap.width + border*2 );
                  int height = ofNextPow2( bitmap.rows  + border*2 );
  
                  // ------------------------- this is fixing a bug with small type
                  // ------------------------- appearantly, opengl has trouble with
                  // ------------------------- width or height textures of 1, so we
                  // ------------------------- we just set it to 2...
                  if (width == 1) width = 2;
                  if (height == 1) height = 2;
  
                  // -------------------------
                  // info about the character:
                  cps[i].value                         = i;
                  cps[i].height                         = face->glyph->bitmap_top;
                  cps[i].width                         = face->glyph->bitmap.width;
                  cps[i].setWidth                 = face->glyph->advance.x >> 6;
                  cps[i].topExtent                 = face->glyph->bitmap.rows;
                  cps[i].leftExtent                = face->glyph->bitmap_left;
  
                  // texture internals
                  cps[i].tTex             = (float)(bitmap.width + visibleBorder*2)  /  (float)width;
                  cps[i].vTex             = (float)(bitmap.rows +  visibleBorder*2)   /  (float)height;
  
                  cps[i].xOff             = (float)(border - visibleBorder) / (float)width;
                  cps[i].yOff             = (float)(border - visibleBorder) / (float)height;
  
                  /* sanity check:
                  ofLog(OF_LOG_NOTICE,"\%i \%i \%i \%i \%i \%i",
                  cps[i].value ,
                  cps[i].height ,
                  cps[i].width         ,
                  cps[i].setWidth         ,
                  cps[i].topExtent ,
                  cps[i].leftExtent        );
  		*/
  
                  // Allocate Memory For The Texture Data.
                  unsigned char* expanded_data = new unsigned char[ 2 * width * height];
  
                  //-------------------------------- clear data:
                  for(int j=0; j <height;j++) {
                          for(int k=0; k < width; k++){
                                  expanded_data[2*(k+j*width)  ] = 255;   // every luminance pixel = 255
                                  expanded_data[2*(k+j*width)+1] = 0;
                          }
                  }
  
                  if (bAntiAlised == true){
                          //-----------------------------------
                          for(int j=0; j <height; j++) {
                                  for(int k=0; k < width; k++){
                                          if ((k<bitmap.width) && (j<bitmap.rows)){
                                                  expanded_data[2*((k+border)+(j+border)*width)+1] = bitmap.buffer[k + bitmap.width*(j)];
                                          }
                                  }
                          }
                          //-----------------------------------
                  } else {
                          //-----------------------------------
                          // true type packs monochrome info in a
                          // 1-bit format, hella funky
                          // here we unpack it:
                          unsigned char *src =  bitmap.buffer;
                          for(int j=0; j <bitmap.rows;j++) {
                                  unsigned char b=0;
                                  unsigned char *bptr =  src;
                                  for(int k=0; k < bitmap.width ; k++){
                                          expanded_data[2*((k+1)+(j+1)*width)] = 255;
                                          if (k%8==0){ b = (*bptr++);}
                                          expanded_data[2*((k+1)+(j+1)*width) + 1] =
                                                  b&0x80 ? 255 : 0;
                                      b <<= 1;
                                  }
                                  src += bitmap.pitch;
                          }
                          //-----------------------------------
                  }
  
                  //Now we just setup some texture paramaters.
                  glBindTexture( GL_TEXTURE_2D, texNames[i]);
                  #ifndef TARGET_OF_IPHONE
                          glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
                          glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
                  #endif
                     if (bAntiAlised == true){
                          glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
                          glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
                  } else {
                          glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
                          glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
                  }
                  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  
                  //Here we actually create the texture itself, notice
                  //that we are using GL_LUMINANCE_ALPHA to indicate that
                  //we are using 2 channel data.
  
                  #ifndef TARGET_OF_IPHONE // gluBuild2DMipmaps doesn't seem to exist in anything i had in the iphone build... so i commented it out
                          bool b_use_mipmaps = false;  // FOR now this is fixed to false, could be an option, left in for legacy...
                          if (b_use_mipmaps){
                                  gluBuild2DMipmaps(
                                          GL_TEXTURE_2D, GL_LUMINANCE_ALPHA, width, height,
                                          GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded_data);
                          } else
                  #endif
                  {
                      glTexImage2D( GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, width, height,
                             0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded_data );
                  }
  
                  //With the texture created, we don't need to expanded data anymore
  
              delete [] expanded_data;
  
     }
  
          // ------------- close the library and typeface
          FT_Done_Face(face);
          FT_Done_FreeType(library);
            bLoadedOk = true;
  }
  
  //-----------------------------------------------------------
  int ofTrueTypeFont::ofNextPow2 ( int a )
  {
          int rval=1;
          while(rval<a) rval<<=1;
          return rval;
  }
  
  //-----------------------------------------------------------
  void ofTrueTypeFont::setLineHeight(float _newLineHeight) {
          lineHeight = _newLineHeight;
  }
  
  //-----------------------------------------------------------
  float ofTrueTypeFont::getLineHeight(){
          return lineHeight;
  }
  
  //------------------------------------------------------------------
  ofTTFCharacter ofTrueTypeFont::getCharacterAsPoints(int character){
          if( bMakeContours == false ){
                  ofLog(OF_LOG_ERROR, "getCharacterAsPoints: contours not created,  call loadFont with makeContours set to true" );
          }
  
          if( bMakeContours && (int)charOutlines.size() > 0 && character >= NUM_CHARACTER_TO_START && character - NUM_CHARACTER_TO_START < (int)charOutlines.size() ){
                  return charOutlines[character-NUM_CHARACTER_TO_START];
          }else{
                  return ofTTFCharacter();
          }
  }
  
  //-----------------------------------------------------------
  void ofTrueTypeFont::drawChar(int c, float x, float y) {
  
          //----------------------- error checking
          if (!bLoadedOk){
                  ofLog(OF_LOG_ERROR,"Error : font not allocated -- line \%d in \%s", __LINE__,__FILE__);
                  return;
          }
  
          if (c >= nCharacters){
                  //ofLog(OF_LOG_ERROR,"Error : char (\%i) not allocated -- line \%d in \%s", (c + NUM_CHARACTER_TO_START), __LINE__,__FILE__);
                  return;
          }
          //-----------------------
  
          int cu = c;
  
          GLint height        = cps[cu].height;
          GLint bwidth        = cps[cu].width;
          GLint top                = cps[cu].topExtent - cps[cu].height;
          GLint lextent        = cps[cu].leftExtent;
  
          GLfloat        x1, y1, x2, y2, corr, stretch;
          GLfloat t1, v1, t2, v2;
  
          //this accounts for the fact that we are showing 2*visibleBorder extra pixels
          //so we make the size of each char that many pixels bigger
          stretch = (float)(visibleBorder * 2);
  
          t2                = cps[cu].xOff;
          v2                = cps[cu].yOff;
          t1                = cps[cu].tTex + t2;
          v1                = cps[cu].vTex + v2;
  
          corr        = (float)(( (fontSize - height) + top) - fontSize);
  
          x1                = lextent + bwidth + stretch;
          y1                = height + corr + stretch;
          x2                = (float) lextent;
          y2                = -top + corr;
  
          if (glIsTexture(texNames[cu])) {
                  glBindTexture(GL_TEXTURE_2D, texNames[cu]);
                  glNormal3f(0, 0, 1);
  
                  GLfloat verts[] = { x2,y2,
                          x2, y1,
                          x1, y1,
                  x1, y2 };
                  GLfloat tex_coords[] = { t2, v2,
                          t2, v1,
                          t1, v1,
                  t1, v2 };
  
                  glEnableClientState( GL_TEXTURE_COORD_ARRAY );
                  glTexCoordPointer(2, GL_FLOAT, 0, tex_coords );
                  glEnableClientState( GL_VERTEX_ARRAY );
                  glVertexPointer(2, GL_FLOAT, 0, verts );
                  glDrawArrays( GL_TRIANGLE_FAN, 0, 4 );
                  glDisableClientState( GL_TEXTURE_COORD_ARRAY );
  
          } else {
                  //let's add verbosity levels somewhere...
                  //this error, for example, is kind of annoying to see
                  //all the time:
                  ofLog(OF_LOG_WARNING," texture not bound for character -- line \%d in \%s", __LINE__,__FILE__);
          }
  
  }
  
  //-----------------------------------------------------------
  void ofTrueTypeFont::drawCharAsShape(int c, float x, float y) {
  
          //----------------------- error checking
          if (!bLoadedOk){
                  ofLog(OF_LOG_ERROR,"Error : font not allocated -- line \%d in \%s", __LINE__,__FILE__);
                  return;
          }
  
          //----------------------- error checking
          if (!bMakeContours){
                  ofLog(OF_LOG_ERROR,"Error : contours not created for this font - call loadFont with makeContours set to true");
                  return;
          }
  
          if (c >= nCharacters){
                  //ofLog(OF_LOG_ERROR,"Error : char (\%i) not allocated -- line \%d in \%s", (c + NUM_CHARACTER_TO_START), __LINE__,__FILE__);
                  return;
          }
          //-----------------------
  
          int cu = c;
          ofTTFCharacter & charRef = charOutlines[cu];
  
          ofBeginShape();
                  for(int k = 0; k < (int)charRef.contours.size(); k++){
                          if( k!= 0)ofNextContour(true);
                          for(int i = 0; i < (int)charRef.contours[k].pts.size(); i++){
                                  ofVertex(charRef.contours[k].pts[i].x + x, charRef.contours[k].pts[i].y + y);
                          }
                  }
          ofEndShape( true );
  
  }
  
  //-----------------------------------------------------------
  float ofTrueTypeFont::stringWidth(string c) {
      ofRectangle rect = getStringBoundingBox(c, 0,0);
      return rect.width;
  }
  
  ofRectangle ofTrueTypeFont::getStringBoundingBox(string c, float x, float y){
  
      ofRectangle myRect;
  
          GLint                index        = 0;
          GLfloat                xoffset        = 0;
          GLfloat                yoffset        = 0;
      int         len     = (int)c.length();
      float       minx    = -1;
      float       miny    = -1;
      float       maxx    = -1;
      float       maxy    = -1;
  
      if (len < 1){
          myRect.x        = 0;
          myRect.y        = 0;
          myRect.width    = 0;
          myRect.height   = 0;
          return myRect;
      }
  
      bool bFirstCharacter = true;
          while(index < len){
                  int cy = (unsigned char)c[index] - NUM_CHARACTER_TO_START;
               if (cy < nCharacters){                         // full char set or not?
                 if (c[index] == '\n') {
                                  yoffset += (int)lineHeight;
                                  xoffset = 0 ; //reset X Pos back to zero
                } else if (c[index] == ' ') {
                               int cy = (int)'p' - NUM_CHARACTER_TO_START;
                                   xoffset += cps[cy].width;
                                   // zach - this is a bug to fix -- for now, we don't currently deal with ' ' in calculating string bounding box
                    } else {
                  GLint height        = cps[cy].height;
                      GLint bwidth        = cps[cy].width;
                      GLint top                = cps[cy].topExtent - cps[cy].height;
                      GLint lextent        = cps[cy].leftExtent;
                      float        x1, y1, x2, y2, corr, stretch;
                      stretch = (float)visibleBorder * 2;
                                  corr = (float)(((fontSize - height) + top) - fontSize);
                                  x1                = (x + xoffset + lextent + bwidth + stretch);
                      y1                = (y + yoffset + height + corr + stretch);
                      x2                = (x + xoffset + lextent);
                      y2                = (y + yoffset + -top + corr);
                                  xoffset += cps[cy].setWidth;
                                  if (bFirstCharacter == true){
                      minx = x2;
                      miny = y2;
                      maxx = x1;
                      maxy = y1;
                      bFirstCharacter = false;
                  } else {
                      if (x2 < minx) minx = x2;
                      if (y2 < miny) miny = y2;
                      if (x1 > maxx) maxx = x1;
                      if (y1 > maxy) maxy = y1;
              }
                    }
                    }
              index++;
      }
  
      myRect.x        = minx;
      myRect.y        = miny;
      myRect.width    = maxx-minx;
      myRect.height   = maxy-miny;
      return myRect;
  }
  
  //-----------------------------------------------------------
  float ofTrueTypeFont::stringHeight(string c) {
      ofRectangle rect = getStringBoundingBox(c, 0,0);
      return rect.height;
  }
  
  //=====================================================================
  void ofTrueTypeFont::drawString(string c, float x, float y) {
  
      if (!bLoadedOk){
              ofLog(OF_LOG_ERROR,"Error : font not allocated -- line \%d in \%s", __LINE__,__FILE__);
              return;
      };
  
      // we need transparency to draw text, but we don't know
      // if that is set up in outside of this function
      // we "pushAttrib", turn on alpha and "popAttrib"
      // http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/gl/pushattrib.html
  
      // **** note ****
      // I have read that pushAttrib() is slow, if used often,
      // maybe there is a faster way to do this?
      // ie, check if blending is enabled, etc...
      // glIsEnabled().... glGet()...
      // http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/gl/get.html
      // **************
  
          GLint                index        = 0;
          GLfloat                X                = 0;
          GLfloat                Y                = 0;
  
          // (a) record the current "alpha state, blend func, etc"
          #ifndef TARGET_OF_IPHONE
                  glPushAttrib(GL_COLOR_BUFFER_BIT);
          #else
                  GLboolean blend_enabled = glIsEnabled(GL_BLEND);
                  GLboolean texture_2d_enabled = glIsEnabled(GL_TEXTURE_2D);
                  GLint blend_src, blend_dst;
                  glGetIntegerv( GL_BLEND_SRC, &blend_src );
                  glGetIntegerv( GL_BLEND_DST, &blend_dst );
          #endif
  
      // (b) enable our regular ALPHA blending!
      glEnable(GL_BLEND);
          glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
          // (c) enable texture once before we start drawing each char (no point turning it on and off constantly)
          glEnable(GL_TEXTURE_2D);
          // (d) store the current matrix position and do a translation to the drawing position
          glPushMatrix();
          glTranslatef(x, y, 0);
  
          int len = (int)c.length();
  
          while(index < len){
                  int cy = (unsigned char)c[index] - NUM_CHARACTER_TO_START;
                  if (cy < nCharacters){                         // full char set or not?
                    if (c[index] == '\n') {
  
                                  Y = (float) lineHeight;
                                  glTranslatef(-X, Y, 0);
                                  X = 0 ; //reset X Pos back to zero
  
                    }else if (c[index] == ' ') {
                                   int cy = (int)'p' - NUM_CHARACTER_TO_START;
                                   X += cps[cy].width;
                                   glTranslatef((float)cps[cy].width, 0, 0);
                    } else {
                                  drawChar(cy, 0, 0);
                                  X += cps[cy].setWidth;
                                  glTranslatef((float)cps[cy].setWidth, 0, 0);
                    }
                  }
                  index++;
          }
          glPopMatrix();
          glDisable(GL_TEXTURE_2D);
      // (c) return back to the way things were (with blending, blend func, etc)
          #ifndef TARGET_OF_IPHONE
                  glPopAttrib();
          #else
                  if( !blend_enabled )
                          glDisable(GL_BLEND);
                  if( !texture_2d_enabled )
                          glDisable(GL_TEXTURE_2D);
                  glBlendFunc( blend_src, blend_dst );
          #endif
  
  }
  
  //=====================================================================
  void ofTrueTypeFont::drawStringAsShapes(string c, float x, float y) {
  
      if (!bLoadedOk){
              ofLog(OF_LOG_ERROR,"Error : font not allocated -- line \%d in \%s", __LINE__,__FILE__);
              return;
      };
  
          //----------------------- error checking
          if (!bMakeContours){
                  ofLog(OF_LOG_ERROR,"Error : contours not created for this font - call loadFont with makeContours set to true");
                  return;
          }
  
          GLint                index        = 0;
          GLfloat                X                = 0;
          GLfloat                Y                = 0;
  
          glPushMatrix();
          glTranslatef(x, y, 0);
          int len = (int)c.length();
  
          while(index < len){
                  int cy = (unsigned char)c[index] - NUM_CHARACTER_TO_START;
                  if (cy < nCharacters){                         // full char set or not?
                    if (c[index] == '\n') {
  
                                  Y = lineHeight;
                                  X = 0 ; //reset X Pos back to zero
  
                    }else if (c[index] == ' ') {
                                   int cy = (int)'p' - NUM_CHARACTER_TO_START;
                                   X += cps[cy].width;
                                   //glTranslated(cps[cy].width, 0, 0);
                    } else {
                                  drawCharAsShape(cy, X, Y);
                                  X += cps[cy].setWidth;
                                  //glTranslated(cps[cy].setWidth, 0, 0);
                    }
                  }
                  index++;
          }
  
          glPopMatrix();
  
  }
  
  


(C) Æliens 04/09/2009

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.