Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/text/dynamicTextFont.cxx

Go to the documentation of this file.
00001 // Filename: dynamicTextFont.cxx
00002 // Created by:  drose (08Feb02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "dynamicTextFont.h"
00020 
00021 #ifdef HAVE_FREETYPE
00022 
00023 #include "config_text.h"
00024 #include "config_util.h"
00025 #include "config_express.h"
00026 #include "virtualFileSystem.h"
00027 
00028 bool DynamicTextFont::_update_cleared_glyphs = text_update_cleared_glyphs;
00029 
00030 FT_Library DynamicTextFont::_ft_library;
00031 bool DynamicTextFont::_ft_initialized = false;
00032 bool DynamicTextFont::_ft_ok = false;
00033 
00034 TypeHandle DynamicTextFont::_type_handle;
00035 
00036 
00037 // This constant determines how big a particular point size font
00038 // appears to be.  By convention, 10 points is 1 unit (e.g. 1 foot)
00039 // high.
00040 static const float points_per_unit = 10.0f;
00041 
00042 // A universal typographic convention.
00043 static const float points_per_inch = 72.0f;
00044 
00045 ////////////////////////////////////////////////////////////////////
00046 //     Function: DynamicTextFont::Constructor
00047 //       Access: Published
00048 //  Description: The constructor expects the name of some font file
00049 //               that FreeType can read, along with face_index,
00050 //               indicating which font within the file to load
00051 //               (usually 0).
00052 ////////////////////////////////////////////////////////////////////
00053 DynamicTextFont::
00054 DynamicTextFont(const Filename &font_filename, int face_index) {
00055   initialize();
00056 
00057   if (!_ft_ok) {
00058     text_cat.error()
00059       << "Unable to read font " << font_filename
00060       << ": FreeType library not initialized properly.\n";
00061     return;
00062   }
00063 
00064   bool exists = false;
00065   int error;
00066   Filename path(font_filename);
00067   if (use_vfs) {
00068     VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00069     vfs->resolve_filename(path, get_model_path());
00070     exists = vfs->read_file(path, _raw_font_data);
00071     if (exists) {
00072       error = FT_New_Memory_Face(_ft_library, 
00073                                  (const FT_Byte *)_raw_font_data.data(),
00074                                  _raw_font_data.length(),
00075                                  face_index, &_face);
00076     }
00077   } else {
00078     path.resolve_filename(get_model_path());
00079     exists = path.exists();
00080     if (exists) {
00081       string os_specific = path.to_os_specific();
00082       error = FT_New_Face(_ft_library, os_specific.c_str(),
00083                           face_index, &_face);
00084     }
00085   }
00086 
00087   if (!exists) {
00088     text_cat.error()
00089       << "Unable to find font file " << font_filename << "\n";
00090   } else {
00091     if (error == FT_Err_Unknown_File_Format) {
00092       text_cat.error()
00093         << "Unable to read font " << font_filename << ": unknown file format.\n";
00094     } else if (error) {
00095       text_cat.error()
00096         << "Unable to read font " << font_filename << ": invalid.\n";
00097 
00098     } else {
00099       string name = _face->family_name;
00100       if (_face->style_name != NULL) {
00101         name += " ";
00102         name += _face->style_name;
00103       }
00104       set_name(name);
00105 
00106       text_cat.info()
00107         << "Loaded font " << get_name() << "\n";
00108       _is_valid = true;
00109       reset_scale();
00110 
00111       if (text_cat.is_debug()) {
00112         text_cat.debug()
00113           << *this << " has " << _face->num_charmaps << " charmaps:\n";
00114         for (int i = 0; i < _face->num_charmaps; i++) {
00115           text_cat.debug(false) << " " << (void *)_face->charmaps[i];
00116         }
00117         text_cat.debug(false) << "\n";
00118         text_cat.debug()
00119           << "default charmap is " << (void *)_face->charmap << "\n";
00120       }
00121       if (_face->charmap == NULL) {
00122         // If for some reason FreeType didn't set us up a charmap,
00123         // then set it up ourselves.
00124         if (_face->num_charmaps == 0) {
00125           text_cat.warning()
00126             << *this << " has no charmaps available.\n";
00127         } else {
00128           text_cat.warning()
00129             << *this << " has no default Unicode charmap.\n";
00130           if (_face->num_charmaps > 1) {
00131             text_cat.warning()
00132               << "Choosing arbitrary charmap of " << _face->num_charmaps 
00133               << ".\n";
00134           }
00135           FT_Set_Charmap(_face, _face->charmaps[0]);
00136         }
00137       }
00138     }
00139   }
00140 }
00141 
00142 ////////////////////////////////////////////////////////////////////
00143 //     Function: DynamicTextFont::Constructor
00144 //       Access: Published
00145 //  Description: This constructor accepts a table of data representing
00146 //               the font file, loaded from some source other than a
00147 //               filename on disk.
00148 ////////////////////////////////////////////////////////////////////
00149 DynamicTextFont::
00150 DynamicTextFont(const char *font_data, int data_length, int face_index) {
00151   initialize();
00152 
00153   if (!_ft_ok) {
00154     text_cat.error()
00155       << "Unable to read font: FreeType library not initialized properly.\n";
00156     return;
00157   }
00158 
00159   int error;
00160   error = FT_New_Memory_Face(_ft_library, 
00161                              (const FT_Byte *)font_data, data_length,
00162                              face_index, &_face);
00163 
00164   if (error == FT_Err_Unknown_File_Format) {
00165     text_cat.error()
00166       << "Unable to read font: unknown file format.\n";
00167   } else if (error) {
00168     text_cat.error()
00169       << "Unable to read font: invalid.\n";
00170     
00171   } else {
00172     string name = _face->family_name;
00173     if (_face->style_name != NULL) {
00174       name += " ";
00175       name += _face->style_name;
00176     }
00177     set_name(name);
00178     
00179     text_cat.info()
00180       << "Loaded font " << get_name() << "\n";
00181     _is_valid = true;
00182     reset_scale();
00183   }
00184 }
00185 
00186 ////////////////////////////////////////////////////////////////////
00187 //     Function: DynamicTextFont::Constructor
00188 //       Access: Published, Virtual
00189 //  Description: 
00190 ////////////////////////////////////////////////////////////////////
00191 DynamicTextFont::
00192 ~DynamicTextFont() {
00193   if (_is_valid) {
00194     FT_Done_Face(_face);
00195     _is_valid = false;
00196   }
00197 }
00198 
00199 ////////////////////////////////////////////////////////////////////
00200 //     Function: DynamicTextFont::get_num_pages
00201 //       Access: Published
00202 //  Description: Returns the number of pages associated with the font.
00203 //               Initially, the font has zero pages; when the first
00204 //               piece of text is rendered with the font, it will add
00205 //               additional pages as needed.  Each page is a Texture
00206 //               object that contains the images for each of the
00207 //               glyphs currently in use somewhere.
00208 ////////////////////////////////////////////////////////////////////
00209 int DynamicTextFont::
00210 get_num_pages() const {
00211   return _pages.size();
00212 }
00213 
00214 ////////////////////////////////////////////////////////////////////
00215 //     Function: DynamicTextFont::get_page
00216 //       Access: Published
00217 //  Description: Returns the nth page associated with the font.
00218 //               Initially, the font has zero pages; when the first
00219 //               piece of text is rendered with the font, it will add
00220 //               additional pages as needed.  Each page is a Texture
00221 //               object that contains the images for each of the
00222 //               glyphs currently in use somewhere.
00223 ////////////////////////////////////////////////////////////////////
00224 DynamicTextPage *DynamicTextFont::
00225 get_page(int n) const {
00226   nassertr(n >= 0 && n < (int)_pages.size(), (DynamicTextPage *)NULL);
00227   return _pages[n];
00228 }
00229 
00230 ////////////////////////////////////////////////////////////////////
00231 //     Function: DynamicTextFont::garbage_collect
00232 //       Access: Published
00233 //  Description: Removes all of the glyphs from the font that are no
00234 //               longer being used by any Geoms.  Returns the number
00235 //               of glyphs removed.
00236 ////////////////////////////////////////////////////////////////////
00237 int DynamicTextFont::
00238 garbage_collect() {
00239   int removed_count = 0;
00240 
00241   // First, remove all the old entries from our cache index.
00242   Cache new_cache;
00243   Cache::iterator ci;
00244   for (ci = _cache.begin(); ci != _cache.end(); ++ci) {
00245     DynamicTextGlyph *glyph = (*ci).second;
00246     if (glyph->_geom_count != 0) {
00247       // Keep this one.
00248       new_cache.insert(new_cache.end(), (*ci));
00249     } else {
00250       // Drop this one.
00251       removed_count++;
00252     }
00253   }
00254   _cache.swap(new_cache);
00255 
00256   // Now, go through each page and do the same thing.
00257   Pages::iterator pi;
00258   for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
00259     DynamicTextPage *page = (*pi);
00260     page->garbage_collect();
00261   }
00262 
00263   return removed_count;
00264 }
00265 
00266 ////////////////////////////////////////////////////////////////////
00267 //     Function: DynamicTextFont::update_texture_memory
00268 //       Access: Published
00269 //  Description: Marks all of the pages dirty so they will be reloaded
00270 //               into texture memory.  This is necessary only if
00271 //               set_update_cleared_glyphs() is false, and some
00272 //               textures have recently been removed from the pages
00273 //               (for instance, after a call to garbage_collect()).
00274 //
00275 //               Calling this just ensures that what you see when you
00276 //               apply the texture page to a polygon represents what
00277 //               is actually stored on the page.
00278 ////////////////////////////////////////////////////////////////////
00279 void DynamicTextFont::
00280 update_texture_memory() {
00281   Pages::iterator pi;
00282   for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
00283     DynamicTextPage *page = (*pi);
00284     page->mark_dirty(Texture::DF_image);
00285   }
00286 }
00287 
00288 ////////////////////////////////////////////////////////////////////
00289 //     Function: DynamicTextFont::clear
00290 //       Access: Published
00291 //  Description: Drops all the glyphs out of the cache and frees any
00292 //               association with any previously-generated pages.
00293 //
00294 //               Calling this frequently can result in wasted texture
00295 //               memory, as any previously rendered text will still
00296 //               keep a pointer to the old, previously-generated
00297 //               pages.  As long as the previously rendered text
00298 //               remains around, the old pages will also remain
00299 //               around.
00300 ////////////////////////////////////////////////////////////////////
00301 void DynamicTextFont::
00302 clear() {
00303   _cache.clear();
00304   _pages.clear();
00305   _empty_glyphs.clear();
00306 }
00307 
00308 ////////////////////////////////////////////////////////////////////
00309 //     Function: DynamicTextFont::write
00310 //       Access: Published, Virtual
00311 //  Description:
00312 ////////////////////////////////////////////////////////////////////
00313 void DynamicTextFont::
00314 write(ostream &out, int indent_level) const {
00315   static const int max_glyph_name = 1024;
00316   char glyph_name[max_glyph_name];
00317 
00318   indent(out, indent_level)
00319     << "DynamicTextFont " << get_name() << ", " 
00320     << get_num_pages() << " pages, "
00321     << _cache.size() << " glyphs:\n";
00322   Cache::const_iterator ci;
00323   for (ci = _cache.begin(); ci != _cache.end(); ++ci) {
00324     int glyph_index = (*ci).first;
00325     DynamicTextGlyph *glyph = (*ci).second;
00326     indent(out, indent_level + 2) 
00327       << glyph_index;
00328 
00329     if (FT_HAS_GLYPH_NAMES(_face)) {
00330       int error = FT_Get_Glyph_Name(_face, glyph_index, 
00331                                     glyph_name, max_glyph_name);
00332 
00333       // Some fonts, notably MS Mincho, claim to have glyph names but
00334       // only report ".notdef" as the name of each glyph.  Thanks.
00335       if (!error && strcmp(glyph_name, ".notdef") != 0) {
00336         out << " (" << glyph_name << ")";
00337       }
00338     }
00339 
00340     out << ", count = " << glyph->_geom_count << "\n";
00341   }
00342 }
00343 
00344 ////////////////////////////////////////////////////////////////////
00345 //     Function: DynamicTextFont::get_glyph
00346 //       Access: Public, Virtual
00347 //  Description: Gets the glyph associated with the given character
00348 //               code, as well as an optional scaling parameter that
00349 //               should be applied to the glyph's geometry and advance
00350 //               parameters.  Returns true if the glyph exists, false
00351 //               if it does not.  Even if the return value is false,
00352 //               the value for glyph might be filled in with a
00353 //               printable glyph.
00354 ////////////////////////////////////////////////////////////////////
00355 bool DynamicTextFont::
00356 get_glyph(int character, const TextGlyph *&glyph) {
00357   if (!_is_valid) {
00358     glyph = (TextGlyph *)NULL;
00359     return false;
00360   }
00361 
00362   int glyph_index = FT_Get_Char_Index(_face, character);
00363   if (text_cat.is_spam()) {
00364     text_cat.spam()
00365       << *this << " maps " << character << " to glyph " << glyph_index << "\n";
00366   }
00367 
00368   Cache::iterator ci = _cache.find(glyph_index);
00369   if (ci != _cache.end()) {
00370     glyph = (*ci).second;
00371   } else {
00372     DynamicTextGlyph *dynamic_glyph = make_glyph(glyph_index);
00373     _cache.insert(Cache::value_type(glyph_index, dynamic_glyph));
00374     glyph = dynamic_glyph;
00375   }
00376 
00377   return (glyph_index != 0 && glyph != (DynamicTextGlyph *)NULL);
00378 }
00379 
00380 ////////////////////////////////////////////////////////////////////
00381 //     Function: DynamicTextFont::initialize
00382 //       Access: Private
00383 //  Description: Called from both constructors to set up some internal
00384 //               structures.
00385 ////////////////////////////////////////////////////////////////////
00386 void DynamicTextFont::
00387 initialize() {
00388   _texture_margin = text_texture_margin;
00389   _poly_margin = text_poly_margin;
00390   _page_x_size = text_page_x_size;
00391   _page_y_size = text_page_y_size;
00392   _point_size = text_point_size;
00393   _tex_pixels_per_unit = text_pixels_per_unit;
00394   _scale_factor = text_scale_factor;
00395 
00396   // We don't necessarily want to use mipmaps, since we don't want to
00397   // regenerate those every time the texture changes, but we probably
00398   // do want at least linear filtering.  Use whatever the Configrc
00399   // file suggests.
00400   _minfilter = text_minfilter;
00401   _magfilter = text_magfilter;
00402 
00403   // Anisotropic filtering can help the look of the text, and doesn't
00404   // require generating mipmaps, but does require hardware support.
00405   _anisotropic_degree = text_anisotropic_degree;
00406 
00407 
00408   _preferred_page = 0;
00409 
00410   if (!_ft_initialized) {
00411     initialize_ft_library();
00412   }
00413 }
00414 
00415 ////////////////////////////////////////////////////////////////////
00416 //     Function: DynamicTextFont::update_filters
00417 //       Access: Private
00418 //  Description: Reapplies all current filter settings to all of the
00419 //               pages.  This is normally called whenever the filter
00420 //               settings change.
00421 ////////////////////////////////////////////////////////////////////
00422 void DynamicTextFont::
00423 update_filters() {
00424   Pages::iterator pi;
00425   for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
00426     DynamicTextPage *page = (*pi);
00427     page->set_minfilter(_minfilter);
00428     page->set_magfilter(_magfilter);
00429     page->set_anisotropic_degree(_anisotropic_degree);
00430   }
00431 }
00432  
00433 ////////////////////////////////////////////////////////////////////
00434 //     Function: DynamicTextFont::reset_scale
00435 //       Access: Private
00436 //  Description: Resets the font based on the current values for
00437 //               _point_size, _tex_pixels_per_unit, and _scale_factor.
00438 //               Returns true if successful, false otherwise.
00439 ////////////////////////////////////////////////////////////////////
00440 bool DynamicTextFont::
00441 reset_scale() {
00442   // The font may be rendered larger (by a factor of _scale_factor),
00443   // and then reduced into the texture.  Hence the difference between
00444   // _font_pixels_per_unit aned _tex_pixels_per_unit.
00445   _font_pixels_per_unit = _tex_pixels_per_unit * _scale_factor;
00446 
00447   float units_per_inch = (points_per_inch / points_per_unit);
00448   int dpi = (int)(_font_pixels_per_unit * units_per_inch);
00449   
00450   int error = FT_Set_Char_Size(_face,
00451                                (int)(_point_size * 64), (int)(_point_size * 64),
00452                                dpi, dpi);
00453   if (error) {
00454     // If we were unable to set a particular char size, perhaps we
00455     // have a non-scalable font.  Try to figure out the closest
00456     // available size.
00457     int desired_height = (int)(_font_pixels_per_unit * _point_size / points_per_unit + 0.5f);
00458     int best_size = -1;
00459     if (_face->num_fixed_sizes > 0) {
00460       best_size = 0;
00461       int best_diff = abs(desired_height - _face->available_sizes[0].height);
00462       for (int i = 1; i < _face->num_fixed_sizes; i++) {
00463         int diff = abs(desired_height - _face->available_sizes[i].height);
00464         if (diff < best_diff) {
00465           best_size = i;
00466           best_diff = diff;
00467         }
00468       }
00469     }
00470     if (best_size >= 0) {
00471       int pixel_height = _face->available_sizes[best_size].height;
00472       int pixel_width = _face->available_sizes[best_size].width;
00473       error = FT_Set_Pixel_Sizes(_face, pixel_width, pixel_height);
00474       if (!error) {
00475         text_cat.info()
00476           << "Using " << pixel_height << "-pixel font for "
00477           << get_name() << "\n";
00478 
00479         _font_pixels_per_unit = pixel_height * points_per_unit / _point_size;
00480         _tex_pixels_per_unit = _font_pixels_per_unit;
00481       }
00482     }
00483   }
00484 
00485   if (error) {
00486     text_cat.warning()
00487       << "Unable to set " << get_name() 
00488       << " to " << _point_size << "pt at " << dpi << " dpi.\n";
00489     _line_height = 1.0f;
00490     return false;
00491   }
00492 
00493   _line_height = _face->size->metrics.height / (_font_pixels_per_unit * 64.0f);
00494 
00495   // Determine the correct width for a space.
00496   error = FT_Load_Char(_face, ' ', FT_LOAD_DEFAULT);
00497   if (error) {
00498     // Space isn't defined.  Oh well.
00499     _space_advance = 0.25f * _line_height;
00500 
00501   } else {
00502     _space_advance = _face->glyph->advance.x / (_font_pixels_per_unit * 64.0f);
00503   }
00504 
00505   return true;
00506 }
00507 
00508 ////////////////////////////////////////////////////////////////////
00509 //     Function: DynamicTextFont::make_glyph
00510 //       Access: Private
00511 //  Description: Slots a space in the texture map for the new
00512 //               character and renders the glyph, returning the
00513 //               newly-created TextGlyph object, or NULL if the
00514 //               glyph cannot be created for some reason.
00515 ////////////////////////////////////////////////////////////////////
00516 DynamicTextGlyph *DynamicTextFont::
00517 make_glyph(int glyph_index) {
00518   int error = FT_Load_Glyph(_face, glyph_index, FT_LOAD_RENDER);
00519   if (error) {
00520     text_cat.error()
00521       << "Unable to render glyph " << glyph_index << "\n";
00522     return (DynamicTextGlyph *)NULL;
00523   }
00524 
00525   FT_GlyphSlot slot = _face->glyph;
00526   FT_Bitmap &bitmap = slot->bitmap;
00527 
00528   float advance = slot->advance.x / 64.0;
00529 
00530   if (bitmap.width == 0 || bitmap.rows == 0) {
00531     // If we got an empty bitmap, it's a special case.
00532     PT(DynamicTextGlyph) glyph = 
00533       new DynamicTextGlyph(advance / _font_pixels_per_unit);
00534     _empty_glyphs.push_back(glyph);
00535     return glyph;
00536 
00537   } else {
00538     DynamicTextGlyph *glyph;
00539 
00540     float tex_x_size = bitmap.width;
00541     float tex_y_size = bitmap.rows;
00542 
00543     if (_tex_pixels_per_unit == _font_pixels_per_unit) {
00544       // If the bitmap produced from the font doesn't require scaling
00545       // before it goes to the texture, we can just copy it directly
00546       // into the texture.
00547       glyph = slot_glyph(bitmap.width, bitmap.rows);
00548       copy_bitmap_to_texture(bitmap, glyph);
00549 
00550     } else {
00551       // Otherwise, we need to copy to a PNMImage first, so we can
00552       // scale it; and then copy it to the texture from there.
00553       tex_x_size /= _scale_factor;
00554       tex_y_size /= _scale_factor;
00555       int int_x_size = (int)ceil(tex_x_size);
00556       int int_y_size = (int)ceil(tex_y_size);
00557       int bmp_x_size = (int)(int_x_size * _scale_factor + 0.5f);
00558       int bmp_y_size = (int)(int_y_size * _scale_factor + 0.5f);
00559       glyph = slot_glyph(int_x_size, int_y_size);
00560       
00561       PNMImage image(bmp_x_size, bmp_y_size, PNMImage::CT_grayscale);
00562       copy_bitmap_to_pnmimage(bitmap, image);
00563 
00564       PNMImage reduced(int_x_size, int_y_size, PNMImage::CT_grayscale);
00565       reduced.quick_filter_from(image);
00566       copy_pnmimage_to_texture(reduced, glyph);
00567     }
00568       
00569     glyph->_page->mark_dirty(Texture::DF_image);
00570     
00571     glyph->make_geom(slot->bitmap_top, slot->bitmap_left,
00572                      advance, _poly_margin,
00573                      tex_x_size, tex_y_size,
00574                      _font_pixels_per_unit, _tex_pixels_per_unit);
00575     return glyph;
00576   }
00577 }
00578 
00579 ////////////////////////////////////////////////////////////////////
00580 //     Function: DynamicTextFont::copy_bitmap_to_texture
00581 //       Access: Private
00582 //  Description: Copies a bitmap as rendered by FreeType directly into
00583 //               the texture memory image for the indicated glyph,
00584 //               without any scaling of pixels.
00585 ////////////////////////////////////////////////////////////////////
00586 void DynamicTextFont::
00587 copy_bitmap_to_texture(const FT_Bitmap &bitmap, DynamicTextGlyph *glyph) {
00588   if (bitmap.pixel_mode == ft_pixel_mode_grays && bitmap.num_grays == 256) {
00589     // This is the easy case: we can memcpy the rendered glyph
00590     // directly into our texture image, one row at a time.
00591     unsigned char *buffer_row = bitmap.buffer;
00592     for (int yi = 0; yi < bitmap.rows; yi++) {
00593       
00594       unsigned char *texture_row = glyph->get_row(yi);
00595       nassertv(texture_row != (unsigned char *)NULL);
00596       memcpy(texture_row, buffer_row, bitmap.width);
00597       buffer_row += bitmap.pitch;
00598     }
00599     
00600   } else if (bitmap.pixel_mode == ft_pixel_mode_mono) {
00601     // This is a little bit more work: we have to expand the
00602     // one-bit-per-pixel bitmap into a one-byte-per-pixel texture.
00603     unsigned char *buffer_row = bitmap.buffer;
00604     for (int yi = 0; yi < bitmap.rows; yi++) {
00605       unsigned char *texture_row = glyph->get_row(yi);
00606       nassertv(texture_row != (unsigned char *)NULL);
00607       
00608       int bit = 0x80;
00609       unsigned char *b = buffer_row;
00610       for (int xi = 0; xi < bitmap.width; xi++) {
00611         if (*b & bit) {
00612           texture_row[xi] = 0xff;
00613         } else {
00614           texture_row[xi] = 0x00;
00615         }
00616         bit >>= 1;
00617         if (bit == 0) {
00618           ++b;
00619           bit = 0x80;
00620         }
00621       }
00622       
00623       buffer_row += bitmap.pitch;
00624     }
00625     
00626     
00627   } else if (bitmap.pixel_mode == ft_pixel_mode_grays) {
00628     // Here we must expand a grayscale pixmap with n levels of gray
00629     // into our 256-level texture.
00630     unsigned char *buffer_row = bitmap.buffer;
00631     for (int yi = 0; yi < bitmap.rows; yi++) {
00632       unsigned char *texture_row = glyph->get_row(yi);
00633       nassertv(texture_row != (unsigned char *)NULL);
00634       for (int xi = 0; xi < bitmap.width; xi++) {
00635         texture_row[xi] = (int)(buffer_row[xi] * 255) / (bitmap.num_grays - 1);
00636       }
00637       buffer_row += bitmap.pitch;
00638     }
00639     
00640   } else {
00641     text_cat.error()
00642       << "Unexpected pixel mode in bitmap: " << (int)bitmap.pixel_mode << "\n";
00643   }
00644 }
00645 
00646 ////////////////////////////////////////////////////////////////////
00647 //     Function: DynamicTextFont::copy_bitmap_to_pnmimage
00648 //       Access: Private
00649 //  Description: Copies a bitmap as rendered by FreeType into a
00650 //               PNMImage, so it can be rescaled.
00651 ////////////////////////////////////////////////////////////////////
00652 void DynamicTextFont::
00653 copy_bitmap_to_pnmimage(const FT_Bitmap &bitmap, PNMImage &image) {
00654   if (bitmap.pixel_mode == ft_pixel_mode_grays && 
00655       bitmap.num_grays == (int)image.get_maxval() + 1) {
00656     // This is the easy case: we can copy the rendered glyph
00657     // directly into our image, one pixel at a time.
00658     unsigned char *buffer_row = bitmap.buffer;
00659     for (int yi = 0; yi < bitmap.rows; yi++) {
00660       for (int xi = 0; xi < bitmap.width; xi++) {
00661         image.set_gray_val(xi, yi, buffer_row[xi]);
00662       }
00663       buffer_row += bitmap.pitch;
00664     }
00665     
00666   } else if (bitmap.pixel_mode == ft_pixel_mode_mono) {
00667     // This is a little bit more work: we have to expand the
00668     // one-bit-per-pixel bitmap into a one-byte-per-pixel image.
00669     unsigned char *buffer_row = bitmap.buffer;
00670     for (int yi = 0; yi < bitmap.rows; yi++) {
00671       xelval maxval = image.get_maxval();
00672       int bit = 0x80;
00673       unsigned char *b = buffer_row;
00674       for (int xi = 0; xi < bitmap.width; xi++) {
00675         if (*b & bit) {
00676           image.set_gray_val(xi, yi, maxval);
00677         } else {
00678           image.set_gray_val(xi, yi, 0);
00679         }
00680         bit >>= 1;
00681         if (bit == 0) {
00682           ++b;
00683           bit = 0x80;
00684         }
00685       }
00686       
00687       buffer_row += bitmap.pitch;
00688     }
00689     
00690     
00691   } else if (bitmap.pixel_mode == ft_pixel_mode_grays) {
00692     // Here we must expand a grayscale pixmap with n levels of gray
00693     // into our 256-level texture.
00694     unsigned char *buffer_row = bitmap.buffer;
00695     for (int yi = 0; yi < bitmap.rows; yi++) {
00696       for (int xi = 0; xi < bitmap.width; xi++) {
00697         image.set_gray(xi, yi, (float)buffer_row[xi] / (bitmap.num_grays - 1));
00698       }
00699       buffer_row += bitmap.pitch;
00700     }
00701     
00702   } else {
00703     text_cat.error()
00704       << "Unexpected pixel mode in bitmap: " << (int)bitmap.pixel_mode << "\n";
00705   }
00706 }
00707 
00708 ////////////////////////////////////////////////////////////////////
00709 //     Function: DynamicTextFont::copy_pnmimage_to_texture
00710 //       Access: Private
00711 //  Description: Copies a bitmap stored in a PNMImage into
00712 //               the texture memory image for the indicated glyph.
00713 ////////////////////////////////////////////////////////////////////
00714 void DynamicTextFont::
00715 copy_pnmimage_to_texture(const PNMImage &image, DynamicTextGlyph *glyph) {
00716   for (int yi = 0; yi < image.get_y_size(); yi++) {
00717     unsigned char *texture_row = glyph->get_row(yi);
00718     nassertv(texture_row != (unsigned char *)NULL);
00719     for (int xi = 0; xi < image.get_x_size(); xi++) {
00720       texture_row[xi] = image.get_gray_val(xi, yi);
00721     }
00722   }
00723 }
00724 
00725 ////////////////////////////////////////////////////////////////////
00726 //     Function: DynamicTextFont::slot_glyph
00727 //       Access: Private
00728 //  Description: Chooses a page that will have room for a glyph of the
00729 //               indicated size (after expanding the indicated size by
00730 //               the current margin).  Returns the newly-allocated
00731 //               glyph on the chosen page; the glyph has not been
00732 //               filled in yet except with its size.
00733 ////////////////////////////////////////////////////////////////////
00734 DynamicTextGlyph *DynamicTextFont::
00735 slot_glyph(int x_size, int y_size) {
00736   // Increase the indicated size by the current margin.
00737   x_size += _texture_margin * 2;
00738   y_size += _texture_margin * 2;
00739 
00740   if (!_pages.empty()) {
00741     // Start searching on the preferred page.  That way, we'll fill up
00742     // the preferred page first, and we can gradually rotate this page
00743     // around; it keeps us from spending too much time checking
00744     // already-filled pages for space.
00745     _preferred_page = _preferred_page % _pages.size();
00746     int pi = _preferred_page;
00747 
00748     do {
00749       DynamicTextPage *page = _pages[pi];
00750       DynamicTextGlyph *glyph = page->slot_glyph(x_size, y_size, _texture_margin);
00751       if (glyph != (DynamicTextGlyph *)NULL) {
00752         // Once we found a page to hold the glyph, that becomes our
00753         // new preferred page.
00754         _preferred_page = pi;
00755         return glyph;
00756       }
00757 
00758       if (page->is_empty()) {
00759         // If we couldn't even put it on an empty page, we're screwed.
00760         text_cat.error()
00761           << "Glyph of size " << x_size << " by " << y_size
00762           << " pixels won't fit on an empty page.\n";
00763         return (DynamicTextGlyph *)NULL;
00764       }
00765 
00766       pi = (pi + 1) % _pages.size();
00767     } while (pi != _preferred_page);
00768   }
00769 
00770   // All pages are filled.  Can we free up space by removing some old
00771   // glyphs?
00772   if (garbage_collect() != 0) {
00773     // Yes, we just freed up some space.  Try once more, recursively.
00774     return slot_glyph(x_size, y_size);
00775 
00776   } else {
00777     // No good; all recorded glyphs are actually in use.  We need to
00778     // make a new page.
00779     _preferred_page = _pages.size();
00780     PT(DynamicTextPage) page = new DynamicTextPage(this);
00781     _pages.push_back(page);
00782     return page->slot_glyph(x_size, y_size, _texture_margin);
00783   }
00784 }
00785 
00786 
00787 ////////////////////////////////////////////////////////////////////
00788 //     Function: DynamicTextFont::initialize_ft_library
00789 //       Access: Private, Static
00790 //  Description: Should be called exactly once to initialize the
00791 //               FreeType library.
00792 ////////////////////////////////////////////////////////////////////
00793 void DynamicTextFont::
00794 initialize_ft_library() {
00795   if (!_ft_initialized) {
00796     int error = FT_Init_FreeType(&_ft_library);
00797     _ft_initialized = true;
00798     if (error) {
00799       text_cat.error()
00800         << "Unable to initialize FreeType; DynamicTextFonts will not load.\n";
00801     } else {
00802       _ft_ok = true;
00803     }
00804   }
00805 }
00806 
00807 #endif  // HAVE_FREETYPE

Generated on Fri May 2 00:44:14 2003 for Panda by doxygen1.3