glut-font.cpp

Go to the documentation of this file.
00001 /*
00002  * glut-font.cpp
00003  *
00004  * Copyright (C) 2008,2010  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  * Basic font support.  See glut-font.h
00031  */
00032 
00033 // includes --------------------------------------------------------------------
00034 #include "glut-font.h"          // always include our own header first
00035 
00036 // define these to force static linking
00037 #define FREETYPE2_STATIC
00038 #define FTGL_LIBRARY_STATIC
00039 //#define FT2_BUILD_LIBRARY
00040 
00041 #include "ftgl.h"
00042 
00043 #include "perf/perf.h"
00044 #include "wave-glut/wave-glut.h"
00045 
00046 
00047 namespace glut {
00048 
00049 
00050 // interface destructors
00051 Font::~Font(void) throw() { }
00052 FontManager::~FontManager(void) throw() { }
00053 
00054 
00055 ////////////////////////////////////////////////////////////////////////////////
00056 //
00057 //      static helper methods
00058 //
00059 ////////////////////////////////////////////////////////////////////////////////
00060 
00061 
00062 // used when path=NULL
00063 class DefaultFont : public Font {
00064 public:
00065         ~DefaultFont(void) throw() { }
00066 
00067         // glut::Font class interface methods ----------------------------------
00068         const char * getName(void) const throw() { return "(default font)"; }
00069         font_rect_t getBoundingRect(IN const char * text) {
00070                         ASSERT(text, "null");
00071 
00072                         int lh = this->getLineHeight();
00073 
00074                         int drop = 3;   // letters hang 3 pixels below baseline
00075 
00076                         font_rect_t box;
00077                         box.lead = 0;
00078                         box.drop = drop;
00079                         box.rise = lh - drop;
00080                         box.trail = glutBitmapLength(GLUT_BITMAP_9_BY_15,
00081                             (const byte_t *) text);
00082 
00083                         return box;
00084                 }
00085 
00086         void display(IN float x, IN float y, IN float z, IN const char * text) {
00087                         ASSERT(text, "null");
00088 
00089                         glRasterPos3f(x, y, z);
00090                         for (; *text; ++text) {
00091                                 glutBitmapCharacter(GLUT_BITMAP_9_BY_15, *text);
00092                         }
00093                 }
00094 
00095         bool canScale(void) const throw() { return false; }
00096         bool setFaceSize(IN float size) { return false; }
00097         float getFaceSize(void) const throw() { return -1; }
00098         int getLineHeight(void) const throw() { return 15; }
00099 
00100 private:
00101 };
00102 
00103 
00104 
00105 class FtglFont : public Font {
00106 public:
00107         ~FtglFont(void) throw() {
00108                         if (m_data) {
00109                                 delete[] m_data;
00110                         }
00111                 }
00112 
00113         // glut::Font class interface methods ----------------------------------
00114         const char * getName(void) const throw() { return m_name.c_str(); }
00115         font_rect_t getBoundingRect(IN const char * text) {
00116                         ASSERT(text, "null");
00117 
00118                         //DPRINTF("Sizing: '%s'", text);
00119                         FTBBox box = m_font->BBox(text);
00120                         font_rect_t fr;
00121                         FTPoint u = box.Upper();
00122                         FTPoint l = box.Lower();
00123                         //DPRINTF("  l: (%f, %f, %f)", l.X(), l.Y(), l.Z());
00124                         //DPRINTF("  u: (%f, %f, %f)", u.X(), u.Y(), u.Z());
00125 
00126                         fr.lead = -l.X();
00127                         fr.drop = -l.Y() + 1;
00128 
00129                         fr.trail = u.X();
00130                         fr.rise = u.Y();
00131 
00132                         //DPRINTF("  lead:%d  trail:%d", fr.lead, fr.trail);
00133                         //DPRINTF("  rise:%d  drop:%d", fr.rise, fr.drop);
00134 
00135                         return fr;
00136                 }
00137         void display(IN float x, IN float y, IN float z, IN const char * text) {
00138                         ASSERT(text, "null");
00139 
00140                         glMatrixMode(GL_MODELVIEW);
00141                         glPushMatrix();
00142                         glTranslatef(x, y, z);
00143                         glScalef(1, -1, 1);
00144                         m_font->Render(text);
00145                         glPopMatrix();
00146                 }
00147 
00148         bool canScale(void) const throw() { return true; }
00149         bool setFaceSize(IN float pointSize) {
00150                         if (pointSize < 0.5) {
00151                                 return false;
00152                         }
00153                         m_font->FaceSize(pointSize);
00154                         return true;
00155                 }
00156         float getFaceSize(void) const throw() {
00157                         return m_font->FaceSize();
00158                 }
00159         int getLineHeight(void) const throw() { return m_font->LineHeight(); }
00160 
00161         // static factory methods ----------------------------------------------
00162         static smart_ptr<FtglFont> create(IN const char * name,
00163                                         IN byte_t * data,
00164                                         IN smart_ptr<FTFont>& font) {
00165                         ASSERT(name, "null");
00166                         ASSERT(font, "null");
00167                         // ASSERT(data) -- optional
00168 
00169                         smart_ptr<FtglFont> local = new FtglFont;
00170                         ASSERT(local, "out of memory");
00171 
00172                         local->m_name = name;
00173                         local->m_font = font;
00174                         local->m_data = data;
00175 
00176                         return local;
00177                 }
00178 
00179 private:
00180         // private constructor -------------------------------------------------
00181         FtglFont(void) throw() { m_data = NULL; }
00182 
00183         // private helper methods ----------------------------------------------
00184         // private member data -------------------------------------------------
00185         byte_t *                m_data;
00186         std::string             m_name;
00187         smart_ptr<FTFont>       m_font;
00188 };
00189 
00190 
00191 
00192 class Mgr : public FontManager {
00193 public:
00194         ~Mgr(void) throw() { }
00195 
00196         // public class methods ------------------------------------------------
00197         void initialize(void);
00198 
00199         // glut::FontManager class interface methods ---------------------------
00200         smart_ptr<Font> getFont(IN nstream::Stream * stream);
00201 
00202 private:
00203         // private typedefs ----------------------------------------------------
00204         typedef std::map<std::string, smart_ptr<FtglFont> > font_map_t;
00205 
00206         // private helper methods ----------------------------------------------
00207         // private member data -------------------------------------------------
00208         font_map_t                      m_fonts;
00209 };
00210 
00211 
00212 void
00213 Mgr::initialize
00214 (
00215 void
00216 )
00217 {
00218 }
00219 
00220 
00221 
00222 smart_ptr<Font>
00223 Mgr::getFont
00224 (
00225 IN nstream::Stream * stream
00226 )
00227 {
00228         // ASSERT(stream) -- can be null!
00229         if (!stream) {
00230                 return getDefaultFont();
00231         }
00232 
00233         // get full name to use as cache key
00234         smart_ptr<nstream::File> file = stream->getFile();
00235         ASSERT(file, "null");
00236         const char * path = file->getName();
00237         ASSERT(path, "null");
00238         smart_ptr<nstream::Manager> mgr = file->getManager();
00239         ASSERT(mgr, "null");
00240         std::string name = mgr->getFullName(path);
00241         //DPRINTF("path '%s' --> name '%s'", path, name.c_str());
00242 
00243         // already have this font?
00244         font_map_t::iterator i = m_fonts.find(name);
00245         if (m_fonts.end() != i) {
00246                 smart_ptr<Font> font = i->second;
00247                 ASSERT(font, "null font in map?");
00248                 return font;
00249         }
00250 
00251         // don't have this loaded yet
00252         perf::Timer timer("loadFont");
00253         DPRINTF("Need to load font: %s", name.c_str());
00254 
00255         // FTGL can't read from streams, have to populate buffer and hold on
00256         std::istream& in = stream->getStream();
00257         ASSERT_THROW(in.good(), "bad font stream? " << name);
00258         std::streampos currPos = in.tellg();
00259         in.seekg(0, std::ios::end);
00260         std::streampos endPos = in.tellg();
00261         long bytes = (long) (endPos - currPos);
00262         //DPRINTF("Stream is %ld bytes", bytes);
00263         in.seekg(currPos, std::ios::beg);
00264 
00265         // allocate big data array and copy stream data into it
00266         byte_t * data = new byte_t[bytes];
00267         ASSERT(data, "out of memory");
00268         in.read((char *) data, bytes);
00269         ASSERT_THROW(!in.fail(), "failed to read all bytes: " << bytes);
00270 
00271         smart_ptr<FTGLTextureFont> texFont = new FTGLTextureFont(data, bytes);
00272         ASSERT_THROW(texFont, "failed to create texture font: " << name);
00273         texFont->FaceSize(40);
00274         smart_ptr<FTFont> ftglFont = texFont;   // downcast
00275         ASSERT(ftglFont, "can't downcast to FTFont from FTGLTextureFont?");
00276         smart_ptr<FtglFont> font = FtglFont::create(path, data, ftglFont);
00277         ASSERT_THROW(font, "failed to create glut::Font: " << path);
00278 
00279         // cache font and return
00280         m_fonts[name] = font;
00281         return font;
00282 }
00283 
00284 
00285 
00286 ////////////////////////////////////////////////////////////////////////////////
00287 //
00288 //      public API
00289 //
00290 ////////////////////////////////////////////////////////////////////////////////
00291 
00292 smart_ptr<FontManager>
00293 FontManager::create
00294 (
00295 void
00296 )
00297 {
00298         smart_ptr<Mgr> local = new Mgr;
00299         ASSERT(local, "out of memory");
00300 
00301         local->initialize();
00302 
00303         return local;
00304 }
00305 
00306 
00307 
00308 smart_ptr<Font>
00309 getDefaultFont
00310 (
00311 void
00312 )
00313 {
00314         static smart_ptr<Font> g_defaultFont = NULL;
00315         if (!g_defaultFont) {
00316                 g_defaultFont = new DefaultFont;
00317                 ASSERT(g_defaultFont, "out of memory");
00318         }
00319         return g_defaultFont;
00320 }
00321 
00322 
00323 
00324 bool
00325 scaleFontToPixelHeight
00326 (
00327 IO Font * font,
00328 IN const char * text,
00329 IN int H
00330 )
00331 {
00332         perf::Timer timer("scaleFont");
00333         ASSERT(font, "null");
00334         ASSERT(text, "null");
00335         ASSERT(H > 0, "Bad desired pixel height: %d", H);
00336 
00337         if (!font->canScale()) {
00338                 return false;
00339         }
00340 
00341         float startScale = font->getFaceSize();
00342 
00343         float leftSize = 0.0;
00344         float rightSize = 72.0;
00345         while (true) {
00346                 ASSERT_THROW(font->setFaceSize(rightSize),
00347                     "Font is supposed to be scalable");
00348                 font_rect_t fr = font->getBoundingRect(text);
00349                 int y = fr.rise + fr.drop;
00350 //              DPRINTF("Point size %f --> line height %d", rightSize, y);
00351                 if (rightSize > 200 && y < 2) {
00352                         // font isn't scaling!
00353                         font->setFaceSize(startScale);
00354                         return false;
00355                 }
00356                 if (y > H) {
00357                         break;
00358                 }
00359                 rightSize *= 2;
00360         }
00361 
00362         // okay, binary search between left and right
00363         while (true) {
00364                 float midSize = 0.5 * (leftSize + rightSize);
00365                 ASSERT_THROW(font->setFaceSize(midSize),
00366                     "font is supposed to be scalable");
00367                 font_rect_t fr = font->getBoundingRect(text);
00368                 int y = fr.rise + fr.drop;
00369         //      DPRINTF("Point size %f --> line height %d", midSize, y);
00370                 if (rightSize - leftSize < 0.25)
00371                         return true;
00372                 if (y == H)
00373                         return true;
00374                 if (y < H) {
00375                         // mid is too small --> go right
00376                         leftSize = midSize;
00377                 } else {
00378                         // mid is too big --> go left
00379                         rightSize = midSize;
00380                 }
00381         }
00382 
00383         return false;
00384 }
00385 
00386 
00387 
00388 };      // glut namespace
00389