00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
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 
00038 
00039 
00040 static const float points_per_unit = 10.0f;
00041 
00042 
00043 static const float points_per_inch = 72.0f;
00044 
00045 
00046 
00047 
00048 
00049 
00050 
00051 
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         
00123         
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 
00144 
00145 
00146 
00147 
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 
00188 
00189 
00190 
00191 DynamicTextFont::
00192 ~DynamicTextFont() {
00193   if (_is_valid) {
00194     FT_Done_Face(_face);
00195     _is_valid = false;
00196   }
00197 }
00198 
00199 
00200 
00201 
00202 
00203 
00204 
00205 
00206 
00207 
00208 
00209 int DynamicTextFont::
00210 get_num_pages() const {
00211   return _pages.size();
00212 }
00213 
00214 
00215 
00216 
00217 
00218 
00219 
00220 
00221 
00222 
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 
00232 
00233 
00234 
00235 
00236 
00237 int DynamicTextFont::
00238 garbage_collect() {
00239   int removed_count = 0;
00240 
00241   
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       
00248       new_cache.insert(new_cache.end(), (*ci));
00249     } else {
00250       
00251       removed_count++;
00252     }
00253   }
00254   _cache.swap(new_cache);
00255 
00256   
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 
00268 
00269 
00270 
00271 
00272 
00273 
00274 
00275 
00276 
00277 
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 
00290 
00291 
00292 
00293 
00294 
00295 
00296 
00297 
00298 
00299 
00300 
00301 void DynamicTextFont::
00302 clear() {
00303   _cache.clear();
00304   _pages.clear();
00305   _empty_glyphs.clear();
00306 }
00307 
00308 
00309 
00310 
00311 
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       
00334       
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 
00346 
00347 
00348 
00349 
00350 
00351 
00352 
00353 
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 
00382 
00383 
00384 
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   
00397   
00398   
00399   
00400   _minfilter = text_minfilter;
00401   _magfilter = text_magfilter;
00402 
00403   
00404   
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 
00417 
00418 
00419 
00420 
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 
00435 
00436 
00437 
00438 
00439 
00440 bool DynamicTextFont::
00441 reset_scale() {
00442   
00443   
00444   
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     
00455     
00456     
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   
00496   error = FT_Load_Char(_face, ' ', FT_LOAD_DEFAULT);
00497   if (error) {
00498     
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 
00510 
00511 
00512 
00513 
00514 
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     
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       
00545       
00546       
00547       glyph = slot_glyph(bitmap.width, bitmap.rows);
00548       copy_bitmap_to_texture(bitmap, glyph);
00549 
00550     } else {
00551       
00552       
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 
00581 
00582 
00583 
00584 
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     
00590     
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     
00602     
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     
00629     
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 
00648 
00649 
00650 
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     
00657     
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     
00668     
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     
00693     
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 
00710 
00711 
00712 
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 
00727 
00728 
00729 
00730 
00731 
00732 
00733 
00734 DynamicTextGlyph *DynamicTextFont::
00735 slot_glyph(int x_size, int y_size) {
00736   
00737   x_size += _texture_margin * 2;
00738   y_size += _texture_margin * 2;
00739 
00740   if (!_pages.empty()) {
00741     
00742     
00743     
00744     
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         
00753         
00754         _preferred_page = pi;
00755         return glyph;
00756       }
00757 
00758       if (page->is_empty()) {
00759         
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   
00771   
00772   if (garbage_collect() != 0) {
00773     
00774     return slot_glyph(x_size, y_size);
00775 
00776   } else {
00777     
00778     
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 
00789 
00790 
00791 
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