Panda3D
 All Classes Functions Variables Enumerations
freetypeFont.cxx
00001 // Filename: freetypeFont.cxx
00002 // Created by:  drose (07Sep03)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "freetypeFont.h"
00016 
00017 #ifdef HAVE_FREETYPE
00018 
00019 #include "config_pnmtext.h"
00020 #include "config_util.h"
00021 #include "config_express.h"
00022 #include "virtualFileSystem.h"
00023 
00024 // This constant determines how big a particular point size font
00025 // appears to be.  By convention, 10 points is 1 unit (e.g. 1 foot)
00026 // high.
00027 const PN_stdfloat FreetypeFont::_points_per_unit = 10.0f;
00028 
00029 // A universal typographic convention.
00030 const PN_stdfloat FreetypeFont::_points_per_inch = 72.0f;
00031 
00032 ////////////////////////////////////////////////////////////////////
00033 //     Function: FreetypeFont::Constructor
00034 //       Access: Protected
00035 //  Description: 
00036 ////////////////////////////////////////////////////////////////////
00037 FreetypeFont::
00038 FreetypeFont() {
00039   _face = NULL;
00040 
00041   _point_size = text_point_size;
00042   _requested_pixels_per_unit = text_pixels_per_unit;
00043   _tex_pixels_per_unit = text_pixels_per_unit;
00044   _requested_scale_factor = text_scale_factor;
00045   _scale_factor = text_scale_factor;
00046   _native_antialias = text_native_antialias;
00047 
00048   _line_height = 1.0f;
00049   _space_advance = 0.25f;
00050 
00051   _char_size = 0;
00052   _dpi = 0;
00053   _pixel_width = 0;
00054   _pixel_height = 0;
00055 }
00056 
00057 ////////////////////////////////////////////////////////////////////
00058 //     Function: FreetypeFont::Copy Constructor
00059 //       Access: Protected
00060 //  Description: 
00061 ////////////////////////////////////////////////////////////////////
00062 FreetypeFont::
00063 FreetypeFont(const FreetypeFont &copy) :
00064   Namable(copy),
00065   _point_size(copy._point_size),
00066   _requested_pixels_per_unit(copy._requested_pixels_per_unit),
00067   _tex_pixels_per_unit(copy._tex_pixels_per_unit),
00068   _requested_scale_factor(copy._requested_scale_factor),
00069   _scale_factor(copy._scale_factor),
00070   _native_antialias(copy._native_antialias),
00071   _line_height(copy._line_height),
00072   _space_advance(copy._space_advance),
00073   _face(copy._face),
00074   _char_size(copy._char_size),
00075   _dpi(copy._dpi),
00076   _pixel_width(copy._pixel_width),
00077   _pixel_height(copy._pixel_height)
00078 {
00079 }
00080 
00081 ////////////////////////////////////////////////////////////////////
00082 //     Function: FreetypeFont::load_font
00083 //       Access: Protected
00084 //  Description: This method accepts the name of some font file
00085 //               that FreeType can read, along with face_index,
00086 //               indicating which font within the file to load
00087 //               (usually 0).
00088 ////////////////////////////////////////////////////////////////////
00089 bool FreetypeFont::
00090 load_font(const Filename &font_filename, int face_index) {
00091   unload_font();
00092   _face = new FreetypeFace;
00093   if (!_face->_ft_ok) {
00094     pnmtext_cat.error()
00095       << "Unable to read font " << font_filename
00096       << ": FreeType library not initialized properly.\n";
00097     unload_font();
00098     return false;
00099   }
00100 
00101   bool exists = false;
00102   int error;
00103   Filename path(font_filename);
00104   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00105   vfs->resolve_filename(path, get_model_path());
00106   exists = vfs->read_file(path, _face->_font_data, true);
00107   if (exists) {
00108     FT_Face face;
00109     error = FT_New_Memory_Face(_face->_ft_library, 
00110                                (const FT_Byte *)_face->_font_data.data(),
00111                                _face->_font_data.length(),
00112                                face_index, &face);
00113     _face->set_face(face);
00114   }
00115 
00116   bool okflag = false;
00117   if (!exists) {
00118     pnmtext_cat.error()
00119       << "Unable to find font file " << font_filename << "\n";
00120   } else {
00121     if (error == FT_Err_Unknown_File_Format) {
00122       pnmtext_cat.error()
00123         << "Unable to read font " << font_filename << ": unknown file format.\n";
00124     } else if (error) {
00125       pnmtext_cat.error()
00126         << "Unable to read font " << font_filename << ": invalid.\n";
00127 
00128     } else {
00129       okflag = reset_scale();
00130     }
00131   }
00132 
00133   if (!okflag) {
00134     unload_font();
00135   }
00136   
00137   return okflag;
00138 }
00139 
00140 ////////////////////////////////////////////////////////////////////
00141 //     Function: FreetypeFont::load_font
00142 //       Access: Protected
00143 //  Description: This method accepts a table of data representing
00144 //               the font file, loaded from some source other than a
00145 //               filename on disk.
00146 ////////////////////////////////////////////////////////////////////
00147 bool FreetypeFont::
00148 load_font(const char *font_data, int data_length, int face_index) {
00149   unload_font();
00150   _face = new FreetypeFace;
00151 
00152   if (!_face->_ft_ok) {
00153     pnmtext_cat.error()
00154       << "Unable to read font: FreeType library not initialized properly.\n";
00155     unload_font();
00156     return false;
00157   }
00158 
00159   int error;
00160   FT_Face face;
00161   error = FT_New_Memory_Face(_face->_ft_library, 
00162                              (const FT_Byte *)font_data, data_length,
00163                              face_index, &face);
00164   _face->set_face(face);
00165 
00166   bool okflag = false;
00167   if (error == FT_Err_Unknown_File_Format) {
00168     pnmtext_cat.error()
00169       << "Unable to read font: unknown file format.\n";
00170   } else if (error) {
00171     pnmtext_cat.error()
00172       << "Unable to read font: invalid.\n";
00173     
00174   } else {
00175     okflag = reset_scale();
00176   }
00177 
00178   if (!okflag) {
00179     unload_font();
00180   }
00181   
00182   return okflag;
00183 }
00184 
00185 ////////////////////////////////////////////////////////////////////
00186 //     Function: FreetypeFont::unload_font
00187 //       Access: Protected
00188 //  Description: 
00189 ////////////////////////////////////////////////////////////////////
00190 void FreetypeFont::
00191 unload_font() {
00192   _face = NULL;
00193 }
00194 
00195 ////////////////////////////////////////////////////////////////////
00196 //     Function: FreetypeFont::load_glyph
00197 //       Access: Protected
00198 //  Description: Invokes Freetype to load and render the indicated
00199 //               glyph into a bitmap.  Returns true if successful,
00200 //               false otherwise.
00201 ////////////////////////////////////////////////////////////////////
00202 bool FreetypeFont::
00203 load_glyph(FT_Face face, int glyph_index, bool prerender) {
00204   int flags = FT_LOAD_RENDER;
00205   if (!_native_antialias) { 
00206     flags |= FT_LOAD_MONOCHROME;
00207   }
00208 
00209   if (!prerender) {
00210     // If we want to render as an outline font, don't pre-render it to
00211     // a bitmap.
00212     flags = 0;
00213   }
00214 
00215   int error = FT_Load_Glyph(face, glyph_index, flags);
00216   if (error) {
00217     pnmtext_cat.error()
00218       << "Unable to render glyph " << glyph_index << "\n";
00219     return false;
00220   }
00221   return true;
00222 }
00223 
00224 ////////////////////////////////////////////////////////////////////
00225 //     Function: FreetypeFont::copy_bitmap_to_pnmimage
00226 //       Access: Protected
00227 //  Description: Copies a bitmap as rendered by FreeType into a
00228 //               PNMImage, so it can be rescaled.
00229 ////////////////////////////////////////////////////////////////////
00230 void FreetypeFont::
00231 copy_bitmap_to_pnmimage(const FT_Bitmap &bitmap, PNMImage &image) {
00232   if (bitmap.pixel_mode == ft_pixel_mode_grays && 
00233       bitmap.num_grays == (int)image.get_maxval() + 1) {
00234     // This is the easy case: we can copy the rendered glyph
00235     // directly into our image, one pixel at a time.
00236     unsigned char *buffer_row = bitmap.buffer;
00237     for (int yi = 0; yi < bitmap.rows; yi++) {
00238       for (int xi = 0; xi < bitmap.width; xi++) {
00239         image.set_gray_val(xi, yi, buffer_row[xi]);
00240       }
00241       buffer_row += bitmap.pitch;
00242     }
00243     
00244   } else if (bitmap.pixel_mode == ft_pixel_mode_mono) {
00245     // This is a little bit more work: we have to expand the
00246     // one-bit-per-pixel bitmap into a one-byte-per-pixel image.
00247     unsigned char *buffer_row = bitmap.buffer;
00248     for (int yi = 0; yi < bitmap.rows; yi++) {
00249       xelval maxval = image.get_maxval();
00250       int bit = 0x80;
00251       unsigned char *b = buffer_row;
00252       for (int xi = 0; xi < bitmap.width; xi++) {
00253         if (*b & bit) {
00254           image.set_gray_val(xi, yi, maxval);
00255         } else {
00256           image.set_gray_val(xi, yi, 0);
00257         }
00258         bit >>= 1;
00259         if (bit == 0) {
00260           ++b;
00261           bit = 0x80;
00262         }
00263       }
00264       
00265       buffer_row += bitmap.pitch;
00266     }
00267     
00268     
00269   } else if (bitmap.pixel_mode == ft_pixel_mode_grays) {
00270     // Here we must expand a grayscale pixmap with n levels of gray
00271     // into our 256-level texture.
00272     unsigned char *buffer_row = bitmap.buffer;
00273     for (int yi = 0; yi < bitmap.rows; yi++) {
00274       for (int xi = 0; xi < bitmap.width; xi++) {
00275         image.set_gray(xi, yi, (PN_stdfloat)buffer_row[xi] / (bitmap.num_grays - 1));
00276       }
00277       buffer_row += bitmap.pitch;
00278     }
00279     
00280   } else {
00281     pnmtext_cat.error()
00282       << "Unexpected pixel mode in bitmap: " << (int)bitmap.pixel_mode << "\n";
00283   }
00284 }
00285  
00286 ////////////////////////////////////////////////////////////////////
00287 //     Function: FreetypeFont::reset_scale
00288 //       Access: Private
00289 //  Description: Resets the font based on the current values for
00290 //               _point_size, _tex_pixels_per_unit, and _scale_factor.
00291 //               Returns true if successful, false otherwise.
00292 ////////////////////////////////////////////////////////////////////
00293 bool FreetypeFont::
00294 reset_scale() {
00295   if (_face == NULL) {
00296     return false;
00297   }
00298 
00299   // Get the face, without requesting a particular size yet (we'll
00300   // figure out the size in a second).
00301   FT_Face face = _face->acquire_face(0, 0, 0, 0);
00302 
00303   // The font may be rendered larger (by a factor of _scale_factor),
00304   // and then reduced into the texture.  Hence the difference between
00305   // _font_pixels_per_unit and _tex_pixels_per_unit.
00306   _tex_pixels_per_unit = _requested_pixels_per_unit;
00307   _scale_factor = _requested_scale_factor;
00308   _font_pixels_per_unit = _tex_pixels_per_unit * _scale_factor;
00309 
00310   _pixel_height = 0;
00311   _pixel_width = 0;
00312   PN_stdfloat units_per_inch = (_points_per_inch / _points_per_unit);
00313   _dpi = (int)(_font_pixels_per_unit * units_per_inch);
00314   _char_size = (int)(_point_size * 64);
00315   
00316   int error = FT_Set_Char_Size(face, _char_size, _char_size, _dpi, _dpi);
00317   if (error) {
00318     // If we were unable to set a particular char size, perhaps we
00319     // have a non-scalable font.  Try to figure out the next larger
00320     // available size, or the largest size available if nothing is
00321     // larger.
00322     int desired_height = (int)(_font_pixels_per_unit * _point_size / _points_per_unit + 0.5f);
00323     int best_size = -1;
00324     int largest_size = -1;
00325     if (face->num_fixed_sizes > 0) {
00326       largest_size = 0;
00327       int best_diff = 0;
00328       for (int i = 0; i < face->num_fixed_sizes; i++) {
00329         int diff = face->available_sizes[i].height - desired_height;
00330         if (diff > 0 && (best_size == -1 || diff < best_diff)) {
00331           best_size = i;
00332           best_diff = diff;
00333         }
00334         if (face->available_sizes[i].height > face->available_sizes[largest_size].height) {
00335           largest_size = i;
00336         }
00337       }
00338     }
00339     if (best_size < 0) {
00340       best_size = largest_size;
00341     }
00342 
00343     if (best_size >= 0) {
00344       _pixel_height = face->available_sizes[best_size].height;
00345       _pixel_width = face->available_sizes[best_size].width;
00346       error = FT_Set_Pixel_Sizes(face, _pixel_width, _pixel_height);
00347       if (!error) {
00348         _font_pixels_per_unit = _pixel_height * _points_per_unit / _point_size;
00349         _scale_factor = _font_pixels_per_unit / _tex_pixels_per_unit;
00350 
00351         if (_scale_factor < 1.0) {
00352           // No point in enlarging a fixed-point font.
00353           _scale_factor = 1.0;
00354           _tex_pixels_per_unit = _font_pixels_per_unit;
00355         }
00356       }
00357     }
00358   }
00359 
00360   if (error) {
00361     pnmtext_cat.warning()
00362       << "Unable to set " << get_name() 
00363       << " to " << _point_size << "pt at " << _dpi << " dpi.\n";
00364     _line_height = 1.0f;
00365     _face->release_face(face);
00366     return false;
00367   }
00368 
00369   _line_height = face->size->metrics.height / (_font_pixels_per_unit * 64.0f);
00370 
00371   // Determine the correct width for a space.
00372   error = FT_Load_Char(face, ' ', FT_LOAD_DEFAULT);
00373   if (error) {
00374     // Space isn't defined.  Oh well.
00375     _space_advance = 0.25f * _line_height;
00376 
00377   } else {
00378     _space_advance = face->glyph->advance.x / (_font_pixels_per_unit * 64.0f);
00379   }
00380 
00381   _face->release_face(face);
00382   return true;
00383 }
00384 
00385 #endif  // HAVE_FREETYPE
 All Classes Functions Variables Enumerations