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