00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "dynamicTextFont.h"
00016
00017 #ifdef HAVE_FREETYPE
00018
00019 #undef interface // I don't know where this symbol is defined, but it interferes with FreeType.
00020 #include FT_OUTLINE_H
00021 #ifdef FT_BBOX_H
00022 #include FT_BBOX_H
00023 #endif
00024 #ifdef FT_BITMAP_H
00025 #include FT_BITMAP_H
00026 #endif
00027 #ifdef FT_STROKER_H
00028 #include FT_STROKER_H
00029 #endif
00030
00031 #include "config_text.h"
00032 #include "config_util.h"
00033 #include "config_express.h"
00034 #include "virtualFileSystem.h"
00035 #include "geomVertexData.h"
00036 #include "geomVertexFormat.h"
00037 #include "geomVertexWriter.h"
00038 #include "geomLinestrips.h"
00039 #include "geomTriangles.h"
00040 #include "renderState.h"
00041 #include "string_utils.h"
00042 #include "triangulator.h"
00043 #include "nurbsCurveEvaluator.h"
00044 #include "nurbsCurveResult.h"
00045
00046
00047
00048 TypeHandle DynamicTextFont::_type_handle;
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059 DynamicTextFont::
00060 DynamicTextFont(const Filename &font_filename, int face_index) {
00061 initialize();
00062 _is_valid = load_font(font_filename, face_index);
00063 TextFont::set_name(FreetypeFont::get_name());
00064 TextFont::_line_height = FreetypeFont::get_line_height();
00065 TextFont::_space_advance = FreetypeFont::get_space_advance();
00066
00067 _fg.set(1.0f, 1.0f, 1.0f, 1.0f);
00068 _bg.set(1.0f, 1.0f, 1.0f, 0.0f);
00069 _outline_color.set(1.0f, 1.0f, 1.0f, 0.0f);
00070 _outline_width = 0.0f;
00071 _outline_feather = 0.0f;
00072 _has_outline = false;
00073 _tex_format = Texture::F_alpha;
00074 _needs_image_processing = false;
00075 }
00076
00077
00078
00079
00080
00081
00082
00083
00084 DynamicTextFont::
00085 DynamicTextFont(const char *font_data, int data_length, int face_index) {
00086 initialize();
00087 _is_valid = load_font(font_data, data_length, face_index);
00088 TextFont::set_name(FreetypeFont::get_name());
00089 TextFont::_line_height = FreetypeFont::_line_height;
00090 TextFont::_space_advance = FreetypeFont::_space_advance;
00091
00092 _fg.set(1.0f, 1.0f, 1.0f, 1.0f);
00093 _bg.set(1.0f, 1.0f, 1.0f, 0.0f);
00094 _outline_color.set(1.0f, 1.0f, 1.0f, 0.0f);
00095 _outline_width = 0.0f;
00096 _outline_feather = 0.0f;
00097 _has_outline = false;
00098 _tex_format = Texture::F_alpha;
00099 _needs_image_processing = false;
00100 }
00101
00102
00103
00104
00105
00106
00107 DynamicTextFont::
00108 DynamicTextFont(const DynamicTextFont ©) :
00109 TextFont(copy),
00110 FreetypeFont(copy),
00111 _texture_margin(copy._texture_margin),
00112 _poly_margin(copy._poly_margin),
00113 _page_x_size(copy._page_x_size),
00114 _page_y_size(copy._page_y_size),
00115 _minfilter(copy._minfilter),
00116 _magfilter(copy._magfilter),
00117 _anisotropic_degree(copy._anisotropic_degree),
00118 _render_mode(copy._render_mode),
00119 _winding_order(copy._winding_order),
00120 _fg(copy._fg),
00121 _bg(copy._bg),
00122 _outline_color(copy._outline_color),
00123 _outline_width(copy._outline_width),
00124 _outline_feather(copy._outline_feather),
00125 _has_outline(copy._has_outline),
00126 _tex_format(copy._tex_format),
00127 _needs_image_processing(copy._needs_image_processing),
00128 _preferred_page(0)
00129 {
00130 }
00131
00132
00133
00134
00135
00136
00137 DynamicTextFont::
00138 ~DynamicTextFont() {
00139 }
00140
00141
00142
00143
00144
00145
00146 PT(TextFont) DynamicTextFont::
00147 make_copy() const {
00148 return new DynamicTextFont(*this);
00149 }
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161 int DynamicTextFont::
00162 get_num_pages() const {
00163 return _pages.size();
00164 }
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176 DynamicTextPage *DynamicTextFont::
00177 get_page(int n) const {
00178 nassertr(n >= 0 && n < (int)_pages.size(), (DynamicTextPage *)NULL);
00179 return _pages[n];
00180 }
00181
00182
00183
00184
00185
00186
00187
00188
00189 int DynamicTextFont::
00190 garbage_collect() {
00191 int removed_count = 0;
00192
00193
00194 Cache new_cache;
00195 Cache::iterator ci;
00196 for (ci = _cache.begin(); ci != _cache.end(); ++ci) {
00197 DynamicTextGlyph *glyph = (*ci).second;
00198 if (glyph == (DynamicTextGlyph *)NULL || glyph->_geom_count != 0) {
00199
00200 new_cache.insert(new_cache.end(), (*ci));
00201 } else {
00202
00203 removed_count++;
00204 }
00205 }
00206 _cache.swap(new_cache);
00207
00208
00209 Pages::iterator pi;
00210 for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
00211 DynamicTextPage *page = (*pi);
00212 page->garbage_collect(this);
00213 }
00214
00215 return removed_count;
00216 }
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231 void DynamicTextFont::
00232 clear() {
00233 _cache.clear();
00234 _pages.clear();
00235 _empty_glyphs.clear();
00236 }
00237
00238
00239
00240
00241
00242
00243 void DynamicTextFont::
00244 write(ostream &out, int indent_level) const {
00245 static const int max_glyph_name = 1024;
00246 char glyph_name[max_glyph_name];
00247
00248 indent(out, indent_level)
00249 << "DynamicTextFont " << get_name() << ", "
00250 << get_num_pages() << " pages, "
00251 << _cache.size() << " glyphs:\n";
00252 Cache::const_iterator ci;
00253 for (ci = _cache.begin(); ci != _cache.end(); ++ci) {
00254 int glyph_index = (*ci).first;
00255 DynamicTextGlyph *glyph = (*ci).second;
00256 indent(out, indent_level + 2)
00257 << glyph_index;
00258
00259 FT_Face face = acquire_face();
00260 if (FT_HAS_GLYPH_NAMES(face)) {
00261 int error = FT_Get_Glyph_Name(face, glyph_index,
00262 glyph_name, max_glyph_name);
00263
00264
00265
00266 if (!error && strcmp(glyph_name, ".notdef") != 0) {
00267 out << " (" << glyph_name << ")";
00268 }
00269 }
00270 release_face(face);
00271
00272 out << ", count = " << glyph->_geom_count << "\n";
00273 }
00274 }
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287 bool DynamicTextFont::
00288 get_glyph(int character, const TextGlyph *&glyph) {
00289 if (!_is_valid) {
00290 glyph = (TextGlyph *)NULL;
00291 return false;
00292 }
00293
00294 FT_Face face = acquire_face();
00295 int glyph_index = FT_Get_Char_Index(face, character);
00296 if (text_cat.is_spam()) {
00297 text_cat.spam()
00298 << *this << " maps " << character << " to glyph " << glyph_index << "\n";
00299 }
00300
00301 Cache::iterator ci = _cache.find(glyph_index);
00302 if (ci != _cache.end()) {
00303 glyph = (*ci).second;
00304 } else {
00305 DynamicTextGlyph *dynamic_glyph = make_glyph(character, face, glyph_index);
00306 _cache.insert(Cache::value_type(glyph_index, dynamic_glyph));
00307 glyph = dynamic_glyph;
00308 }
00309
00310 if (glyph == (DynamicTextGlyph *)NULL) {
00311 glyph = get_invalid_glyph();
00312 glyph_index = 0;
00313 }
00314
00315 release_face(face);
00316 return (glyph_index != 0);
00317 }
00318
00319
00320
00321
00322
00323
00324
00325
00326 void DynamicTextFont::
00327 initialize() {
00328 _texture_margin = text_texture_margin;
00329 _poly_margin = text_poly_margin;
00330 _page_x_size = text_page_size[0];
00331 _page_y_size = text_page_size[1];
00332
00333
00334
00335
00336
00337 _minfilter = text_minfilter;
00338 _magfilter = text_magfilter;
00339
00340
00341
00342 _anisotropic_degree = text_anisotropic_degree;
00343
00344 _render_mode = text_render_mode;
00345 _winding_order = WO_default;
00346
00347 _preferred_page = 0;
00348 }
00349
00350
00351
00352
00353
00354
00355
00356
00357 void DynamicTextFont::
00358 update_filters() {
00359 Pages::iterator pi;
00360 for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
00361 DynamicTextPage *page = (*pi);
00362 page->set_minfilter(_minfilter);
00363 page->set_magfilter(_magfilter);
00364 page->set_anisotropic_degree(_anisotropic_degree);
00365 }
00366 }
00367
00368
00369
00370
00371
00372
00373
00374
00375 void DynamicTextFont::
00376 determine_tex_format() {
00377 nassertv(get_num_pages() == 0);
00378
00379 _has_outline = (_outline_color != _bg && _outline_width > 0.0f);
00380 _needs_image_processing = true;
00381
00382 bool needs_color = false;
00383 bool needs_grayscale = false;
00384 bool needs_alpha = false;
00385
00386 if (_fg[1] != _fg[0] || _fg[2] != _fg[0] ||
00387 _bg[1] != _bg[0] || _bg[2] != _bg[0] ||
00388 (_has_outline && (_outline_color[1] != _outline_color[0] || _outline_color[2] != _outline_color[0]))) {
00389
00390
00391 needs_color = true;
00392
00393 } else if (_fg[0] != 1.0f || _fg[1] != 1.0f || _fg[2] != 1.0f ||
00394 _bg[0] != 1.0f || _bg[1] != 1.0f || _bg[2] != 1.0f ||
00395 (_has_outline && (_outline_color[0] != 1.0f || _outline_color[1] != 1.0f || _outline_color[2] != 1.0f))) {
00396
00397 needs_grayscale = true;
00398 }
00399
00400 if (_fg[3] != 1.0f || _bg[3] != 1.0f ||
00401 (_has_outline && (_outline_color[3] != 1.0f))) {
00402
00403 needs_alpha = true;
00404 }
00405
00406 if (needs_color) {
00407 if (needs_alpha) {
00408 _tex_format = Texture::F_rgba;
00409 } else {
00410 _tex_format = Texture::F_rgb;
00411 }
00412 } else if (needs_grayscale) {
00413 if (needs_alpha) {
00414 _tex_format = Texture::F_luminance_alpha;
00415 } else {
00416 _tex_format = Texture::F_luminance;
00417 }
00418 } else {
00419 if (needs_alpha) {
00420 _tex_format = Texture::F_alpha;
00421
00422 if (!_has_outline &&
00423 _fg == LColor(1.0f, 1.0f, 1.0f, 1.0f) &&
00424 _bg == LColor(1.0f, 1.0f, 1.0f, 0.0f)) {
00425
00426
00427 _needs_image_processing = false;
00428 }
00429
00430 } else {
00431
00432 _tex_format = Texture::F_luminance;
00433 }
00434 }
00435 }
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445 DynamicTextGlyph *DynamicTextFont::
00446 make_glyph(int character, FT_Face face, int glyph_index) {
00447 if (!load_glyph(face, glyph_index, false)) {
00448 return (DynamicTextGlyph *)NULL;
00449 }
00450
00451 FT_GlyphSlot slot = face->glyph;
00452 FT_Bitmap &bitmap = slot->bitmap;
00453
00454 if ((bitmap.width == 0 || bitmap.rows == 0) && (glyph_index == 0)) {
00455
00456
00457
00458
00459
00460 return NULL;
00461 }
00462
00463 PN_stdfloat advance = slot->advance.x / 64.0;
00464
00465 if (_render_mode != RM_texture &&
00466 slot->format == ft_glyph_format_outline) {
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492 FT_Outline_Funcs funcs;
00493 memset(&funcs, 0, sizeof(funcs));
00494 funcs.move_to = (FT_Outline_MoveTo_Func)outline_move_to;
00495 funcs.line_to = (FT_Outline_LineTo_Func)outline_line_to;
00496 funcs.conic_to = (FT_Outline_ConicTo_Func)outline_conic_to;
00497 funcs.cubic_to = (FT_Outline_CubicTo_Func)outline_cubic_to;
00498
00499 WindingOrder wo = _winding_order;
00500 if (wo == WO_default) {
00501
00502
00503 #ifdef FT_ORIENTATION_FILL_RIGHT
00504 if (FT_Outline_Get_Orientation(&slot->outline) == FT_ORIENTATION_FILL_RIGHT) {
00505 wo = WO_right;
00506 } else {
00507 wo = WO_left;
00508 }
00509 #else
00510
00511
00512 wo = WO_right;
00513 #endif // FT_ORIENTATION_FILL_RIGHT
00514 }
00515
00516 if (wo != WO_left) {
00517 FT_Outline_Reverse(&slot->outline);
00518 }
00519
00520 _contours.clear();
00521 FT_Outline_Decompose(&slot->outline, &funcs, (void *)this);
00522
00523 PT(DynamicTextGlyph) glyph =
00524 new DynamicTextGlyph(character, advance / _font_pixels_per_unit);
00525 switch (_render_mode) {
00526 case RM_wireframe:
00527 render_wireframe_contours(glyph);
00528 return glyph;
00529
00530 case RM_polygon:
00531 render_polygon_contours(glyph, true, false);
00532 return glyph;
00533
00534 case RM_extruded:
00535 render_polygon_contours(glyph, false, true);
00536 return glyph;
00537
00538 case RM_solid:
00539 render_polygon_contours(glyph, true, true);
00540 return glyph;
00541
00542 default:
00543 break;
00544 }
00545 }
00546
00547
00548 if (slot->format != ft_glyph_format_bitmap) {
00549 FT_Render_Glyph(slot, ft_render_mode_normal);
00550 }
00551
00552 if (bitmap.width == 0 || bitmap.rows == 0) {
00553
00554
00555 PT(DynamicTextGlyph) glyph =
00556 new DynamicTextGlyph(character, advance / _font_pixels_per_unit);
00557 _empty_glyphs.push_back(glyph);
00558 return glyph;
00559
00560 } else {
00561 DynamicTextGlyph *glyph;
00562
00563 PN_stdfloat tex_x_size = bitmap.width;
00564 PN_stdfloat tex_y_size = bitmap.rows;
00565
00566 int outline = 0;
00567
00568 if (_tex_pixels_per_unit == _font_pixels_per_unit &&
00569 !_needs_image_processing) {
00570
00571
00572
00573 glyph = slot_glyph(character, bitmap.width, bitmap.rows);
00574 copy_bitmap_to_texture(bitmap, glyph);
00575
00576 } else {
00577
00578
00579
00580 tex_x_size /= _scale_factor;
00581 tex_y_size /= _scale_factor;
00582 int int_x_size = (int)ceil(tex_x_size);
00583 int int_y_size = (int)ceil(tex_y_size);
00584 int bmp_x_size = (int)(int_x_size * _scale_factor + 0.5f);
00585 int bmp_y_size = (int)(int_y_size * _scale_factor + 0.5f);
00586
00587 PNMImage image(bmp_x_size, bmp_y_size, PNMImage::CT_grayscale);
00588 copy_bitmap_to_pnmimage(bitmap, image);
00589
00590 PNMImage reduced(int_x_size, int_y_size, PNMImage::CT_grayscale);
00591 reduced.quick_filter_from(image);
00592
00593
00594 PN_stdfloat outline_pixels = _outline_width / _points_per_unit * _tex_pixels_per_unit;
00595 outline = (int)ceil(outline_pixels);
00596
00597 int_x_size += outline * 2;
00598 int_y_size += outline * 2;
00599 tex_x_size += outline * 2;
00600 tex_y_size += outline * 2;
00601 glyph = slot_glyph(character, int_x_size, int_y_size);
00602
00603 if (outline != 0) {
00604
00605 PNMImage padded(int_x_size, int_y_size, PNMImage::CT_grayscale);
00606 padded.copy_sub_image(reduced, outline, outline);
00607 copy_pnmimage_to_texture(padded, glyph);
00608
00609 } else {
00610 copy_pnmimage_to_texture(reduced, glyph);
00611 }
00612 }
00613
00614 glyph->make_geom((int)floor(slot->bitmap_top + outline * _scale_factor + 0.5f),
00615 (int)floor(slot->bitmap_left - outline * _scale_factor + 0.5f),
00616 advance, _poly_margin,
00617 tex_x_size, tex_y_size,
00618 _font_pixels_per_unit, _tex_pixels_per_unit);
00619 return glyph;
00620 }
00621 }
00622
00623
00624
00625
00626
00627
00628
00629
00630 void DynamicTextFont::
00631 copy_bitmap_to_texture(const FT_Bitmap &bitmap, DynamicTextGlyph *glyph) {
00632 if (bitmap.pixel_mode == ft_pixel_mode_grays && bitmap.num_grays == 256) {
00633
00634
00635 unsigned char *buffer_row = bitmap.buffer;
00636 for (int yi = 0; yi < bitmap.rows; yi++) {
00637
00638 unsigned char *texture_row = glyph->get_row(yi);
00639 nassertv(texture_row != (unsigned char *)NULL);
00640 memcpy(texture_row, buffer_row, bitmap.width);
00641 buffer_row += bitmap.pitch;
00642 }
00643
00644 } else if (bitmap.pixel_mode == ft_pixel_mode_mono) {
00645
00646
00647 unsigned char *buffer_row = bitmap.buffer;
00648 for (int yi = 0; yi < bitmap.rows; yi++) {
00649 unsigned char *texture_row = glyph->get_row(yi);
00650 nassertv(texture_row != (unsigned char *)NULL);
00651
00652 int bit = 0x80;
00653 unsigned char *b = buffer_row;
00654 for (int xi = 0; xi < bitmap.width; xi++) {
00655 if (*b & bit) {
00656 texture_row[xi] = 0xff;
00657 } else {
00658 texture_row[xi] = 0x00;
00659 }
00660 bit >>= 1;
00661 if (bit == 0) {
00662 ++b;
00663 bit = 0x80;
00664 }
00665 }
00666
00667 buffer_row += bitmap.pitch;
00668 }
00669
00670
00671 } else if (bitmap.pixel_mode == ft_pixel_mode_grays) {
00672
00673
00674 unsigned char *buffer_row = bitmap.buffer;
00675 for (int yi = 0; yi < bitmap.rows; yi++) {
00676 unsigned char *texture_row = glyph->get_row(yi);
00677 nassertv(texture_row != (unsigned char *)NULL);
00678 for (int xi = 0; xi < bitmap.width; xi++) {
00679 texture_row[xi] = (int)(buffer_row[xi] * 255) / (bitmap.num_grays - 1);
00680 }
00681 buffer_row += bitmap.pitch;
00682 }
00683
00684 } else {
00685 text_cat.error()
00686 << "Unexpected pixel mode in bitmap: " << (int)bitmap.pixel_mode << "\n";
00687 }
00688 }
00689
00690
00691
00692
00693
00694
00695
00696 void DynamicTextFont::
00697 copy_pnmimage_to_texture(const PNMImage &image, DynamicTextGlyph *glyph) {
00698 if (!_needs_image_processing) {
00699
00700
00701 nassertv(glyph->_page->get_num_components() == 1);
00702 for (int yi = 0; yi < image.get_y_size(); yi++) {
00703 unsigned char *texture_row = glyph->get_row(yi);
00704 nassertv(texture_row != (unsigned char *)NULL);
00705 for (int xi = 0; xi < image.get_x_size(); xi++) {
00706 texture_row[xi] = image.get_gray_val(xi, yi);
00707 }
00708 }
00709
00710 } else {
00711 if (_has_outline) {
00712
00713 PNMImage outline(image.get_x_size(), image.get_y_size(), PNMImage::CT_grayscale);
00714 PN_stdfloat outline_pixels = _outline_width / _points_per_unit * _tex_pixels_per_unit;
00715 outline.gaussian_filter_from(outline_pixels * 0.707, image);
00716
00717
00718
00719
00720
00721 PN_stdfloat f = _outline_feather * _outline_feather;
00722
00723 for (int yi = 0; yi < outline.get_y_size(); yi++) {
00724 for (int xi = 0; xi < outline.get_x_size(); xi++) {
00725 PN_stdfloat v = outline.get_gray(xi, yi);
00726 if (v == 0.0f) {
00727
00728 } else if (v >= f) {
00729
00730 outline.set_gray(xi, yi, 1.0);
00731 } else {
00732
00733 outline.set_gray(xi, yi, v / f);
00734 }
00735 }
00736 }
00737
00738
00739 blend_pnmimage_to_texture(outline, glyph, _outline_color);
00740 }
00741
00742
00743
00744
00745 blend_pnmimage_to_texture(image, glyph, _fg);
00746 }
00747 }
00748
00749
00750
00751
00752
00753
00754
00755
00756
00757 void DynamicTextFont::
00758 blend_pnmimage_to_texture(const PNMImage &image, DynamicTextGlyph *glyph,
00759 const LColor &fg) {
00760 LColor fgv = fg * 255.0f;
00761
00762 int num_components = glyph->_page->get_num_components();
00763 if (num_components == 1) {
00764
00765 int ci = 3;
00766 if (glyph->_page->get_format() != Texture::F_alpha) {
00767 ci = 0;
00768 }
00769
00770 for (int yi = 0; yi < image.get_y_size(); yi++) {
00771 unsigned char *texture_row = glyph->get_row(yi);
00772 nassertv(texture_row != (unsigned char *)NULL);
00773 for (int xi = 0; xi < image.get_x_size(); xi++) {
00774 unsigned char *tr = texture_row + xi;
00775 PN_stdfloat t = (PN_stdfloat)image.get_gray(xi, yi);
00776 tr[0] = (unsigned char)(tr[0] + t * (fgv[ci] - tr[0]));
00777 }
00778 }
00779
00780 } else if (num_components == 2) {
00781
00782
00783 for (int yi = 0; yi < image.get_y_size(); yi++) {
00784 unsigned char *texture_row = glyph->get_row(yi);
00785 nassertv(texture_row != (unsigned char *)NULL);
00786 for (int xi = 0; xi < image.get_x_size(); xi++) {
00787 unsigned char *tr = texture_row + xi * 2;
00788 PN_stdfloat t = (PN_stdfloat)image.get_gray(xi, yi);
00789 tr[0] = (unsigned char)(tr[0] + t * (fgv[0] - tr[0]));
00790 tr[1] = (unsigned char)(tr[1] + t * (fgv[3] - tr[1]));
00791 }
00792 }
00793
00794 } else if (num_components == 3) {
00795
00796
00797 for (int yi = 0; yi < image.get_y_size(); yi++) {
00798 unsigned char *texture_row = glyph->get_row(yi);
00799 nassertv(texture_row != (unsigned char *)NULL);
00800 for (int xi = 0; xi < image.get_x_size(); xi++) {
00801 unsigned char *tr = texture_row + xi * 3;
00802 PN_stdfloat t = (PN_stdfloat)image.get_gray(xi, yi);
00803 tr[0] = (unsigned char)(tr[0] + t * (fgv[2] - tr[0]));
00804 tr[1] = (unsigned char)(tr[1] + t * (fgv[1] - tr[1]));
00805 tr[2] = (unsigned char)(tr[2] + t * (fgv[0] - tr[2]));
00806 }
00807 }
00808
00809 } else {
00810
00811
00812 for (int yi = 0; yi < image.get_y_size(); yi++) {
00813 unsigned char *texture_row = glyph->get_row(yi);
00814 nassertv(texture_row != (unsigned char *)NULL);
00815 for (int xi = 0; xi < image.get_x_size(); xi++) {
00816 unsigned char *tr = texture_row + xi * 4;
00817 PN_stdfloat t = (PN_stdfloat)image.get_gray(xi, yi);
00818 tr[0] = (unsigned char)(tr[0] + t * (fgv[2] - tr[0]));
00819 tr[1] = (unsigned char)(tr[1] + t * (fgv[1] - tr[1]));
00820 tr[2] = (unsigned char)(tr[2] + t * (fgv[0] - tr[2]));
00821 tr[3] = (unsigned char)(tr[3] + t * (fgv[3] - tr[3]));
00822 }
00823 }
00824 }
00825 }
00826
00827
00828
00829
00830
00831
00832
00833
00834
00835
00836 DynamicTextGlyph *DynamicTextFont::
00837 slot_glyph(int character, int x_size, int y_size) {
00838
00839 x_size += _texture_margin * 2;
00840 y_size += _texture_margin * 2;
00841
00842 if (!_pages.empty()) {
00843
00844
00845
00846
00847 _preferred_page = _preferred_page % _pages.size();
00848 int pi = _preferred_page;
00849
00850 do {
00851 DynamicTextPage *page = _pages[pi];
00852 DynamicTextGlyph *glyph = page->slot_glyph(character, x_size, y_size, _texture_margin);
00853 if (glyph != (DynamicTextGlyph *)NULL) {
00854
00855
00856 _preferred_page = pi;
00857 return glyph;
00858 }
00859
00860 if (page->is_empty()) {
00861
00862 text_cat.error()
00863 << "Glyph of size " << x_size << " by " << y_size
00864 << " pixels won't fit on an empty page.\n";
00865 return (DynamicTextGlyph *)NULL;
00866 }
00867
00868 pi = (pi + 1) % _pages.size();
00869 } while (pi != _preferred_page);
00870 }
00871
00872
00873
00874 if (garbage_collect() != 0) {
00875
00876 return slot_glyph(character, x_size, y_size);
00877
00878 } else {
00879
00880
00881 _preferred_page = _pages.size();
00882 PT(DynamicTextPage) page = new DynamicTextPage(this, _preferred_page);
00883 _pages.push_back(page);
00884 return page->slot_glyph(character, x_size, y_size, _texture_margin);
00885 }
00886 }
00887
00888
00889
00890
00891
00892
00893
00894 void DynamicTextFont::
00895 render_wireframe_contours(DynamicTextGlyph *glyph) {
00896 PT(GeomVertexData) vdata = new GeomVertexData
00897 (string(), GeomVertexFormat::get_v3(),
00898 Geom::UH_static);
00899 GeomVertexWriter vertex(vdata, InternalName::get_vertex());
00900
00901 PT(GeomLinestrips) lines = new GeomLinestrips(Geom::UH_static);
00902
00903 Contours::const_iterator ci;
00904 for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
00905 const Contour &contour = (*ci);
00906 Points::const_iterator pi;
00907
00908 for (pi = contour._points.begin(); pi != contour._points.end(); ++pi) {
00909 const LPoint2 &p = (*pi)._p;
00910 vertex.add_data3(p[0], 0.0f, p[1]);
00911 }
00912
00913 lines->add_next_vertices(contour._points.size());
00914 lines->close_primitive();
00915 }
00916
00917 glyph->set_geom(vdata, lines, RenderState::make_empty());
00918 _contours.clear();
00919 }
00920
00921
00922
00923
00924
00925
00926
00927 void DynamicTextFont::
00928 render_polygon_contours(DynamicTextGlyph *glyph, bool face, bool extrude) {
00929 PT(GeomVertexData) vdata = new GeomVertexData
00930 (string(), GeomVertexFormat::get_v3n3(),
00931 Geom::UH_static);
00932 GeomVertexWriter vertex(vdata, InternalName::get_vertex());
00933 GeomVertexWriter normal(vdata, InternalName::get_normal());
00934
00935 PT(GeomTriangles) tris = new GeomTriangles(Geom::UH_static);
00936 Triangulator t;
00937
00938 Contours::iterator ci;
00939
00940 if (face) {
00941
00942
00943 for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
00944 Contour &contour = (*ci);
00945
00946 t.clear_polygon();
00947 contour._start_vertex = t.get_num_vertices();
00948 for (size_t i = 0; i < contour._points.size() - 1; ++i) {
00949 const LPoint2 &p = contour._points[i]._p;
00950 vertex.add_data3(p[0], 0.0f, p[1]);
00951 normal.add_data3(0.0f, -1.0f, 0.0f);
00952 int vi = t.add_vertex(p[0], p[1]);
00953 t.add_polygon_vertex(vi);
00954 }
00955
00956 contour._is_solid = t.is_left_winding();
00957 }
00958
00959
00960 for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
00961 const Contour &contour = (*ci);
00962
00963 if (contour._is_solid && !contour._points.empty()) {
00964 t.clear_polygon();
00965 for (size_t i = 0; i < contour._points.size() - 1; ++i) {
00966 t.add_polygon_vertex(contour._start_vertex + i);
00967 }
00968
00969
00970 Contours::iterator cj;
00971 for (cj = _contours.begin(); cj != _contours.end(); ++cj) {
00972 Contour &hole = (*cj);
00973 if (!hole._is_solid && !hole._points.empty()) {
00974 t.begin_hole();
00975 for (size_t j = 0; j < hole._points.size() - 1; ++j) {
00976 t.add_hole_vertex(hole._start_vertex + j);
00977 }
00978 }
00979 }
00980
00981 t.triangulate();
00982 int num_triangles = t.get_num_triangles();
00983 for (int ti = 0; ti < num_triangles; ++ti) {
00984 tris->add_vertex(t.get_triangle_v0(ti));
00985 tris->add_vertex(t.get_triangle_v1(ti));
00986 tris->add_vertex(t.get_triangle_v2(ti));
00987 tris->close_primitive();
00988 }
00989 }
00990 }
00991 }
00992
00993 if (extrude) {
00994
00995
00996
00997
00998 for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
00999 const Contour &contour = (*ci);
01000 Points::const_iterator pi;
01001
01002 for (size_t i = 0; i < contour._points.size(); ++i) {
01003 const ContourPoint &cp = contour._points[i];
01004 const LPoint2 &p = cp._p;
01005 const LVector2 &t_in = cp._in;
01006 const LVector2 &t_out = cp._out;
01007
01008 LVector3 n_in(t_in[1], 0.0f, -t_in[0]);
01009 vertex.add_data3(p[0], 1.0f, p[1]);
01010 vertex.add_data3(p[0], 0.0f, p[1]);
01011 normal.add_data3(n_in);
01012 normal.add_data3(n_in);
01013
01014 if (i != 0) {
01015 int vi = vertex.get_write_row();
01016 tris->add_vertex(vi - 4);
01017 tris->add_vertex(vi - 2);
01018 tris->add_vertex(vi - 1);
01019 tris->close_primitive();
01020 tris->add_vertex(vi - 1);
01021 tris->add_vertex(vi - 3);
01022 tris->add_vertex(vi - 4);
01023 tris->close_primitive();
01024 }
01025
01026 if (i != contour._points.size() - 1 && !t_in.almost_equal(t_out)) {
01027
01028
01029 LVector3 n_out(t_out[1], 0.0f, -t_out[0]);
01030 vertex.add_data3(p[0], 1.0f, p[1]);
01031 vertex.add_data3(p[0], 0.0f, p[1]);
01032 normal.add_data3(n_out);
01033 normal.add_data3(n_out);
01034 }
01035 }
01036 }
01037
01038 if (face) {
01039
01040 int back_start = vertex.get_write_row();
01041
01042 for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
01043 Contour &contour = (*ci);
01044 for (size_t i = 0; i < contour._points.size() - 1; ++i) {
01045 const LPoint2 &p = contour._points[i]._p;
01046 vertex.add_data3(p[0], 1.0f, p[1]);
01047 normal.add_data3(0.0f, 1.0f, 0.0f);
01048 }
01049 }
01050
01051
01052 for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
01053 const Contour &contour = (*ci);
01054
01055 if (contour._is_solid && !contour._points.empty()) {
01056 t.clear_polygon();
01057 for (size_t i = 0; i < contour._points.size() - 1; ++i) {
01058 t.add_polygon_vertex(contour._start_vertex + i);
01059 }
01060
01061
01062 Contours::iterator cj;
01063 for (cj = _contours.begin(); cj != _contours.end(); ++cj) {
01064 Contour &hole = (*cj);
01065 if (!hole._is_solid && !hole._points.empty()) {
01066 t.begin_hole();
01067 for (size_t j = 0; j < hole._points.size() - 1; ++j) {
01068 t.add_hole_vertex(hole._start_vertex + j);
01069 }
01070 }
01071 }
01072
01073 t.triangulate();
01074 int num_triangles = t.get_num_triangles();
01075 for (int ti = 0; ti < num_triangles; ++ti) {
01076 tris->add_vertex(t.get_triangle_v2(ti) + back_start);
01077 tris->add_vertex(t.get_triangle_v1(ti) + back_start);
01078 tris->add_vertex(t.get_triangle_v0(ti) + back_start);
01079 tris->close_primitive();
01080 }
01081 }
01082 }
01083 }
01084 }
01085
01086 glyph->set_geom(vdata, tris, RenderState::make_empty());
01087
01088
01089 _contours.clear();
01090 }
01091
01092
01093
01094
01095
01096
01097
01098 int DynamicTextFont::
01099 outline_move_to(const FT_Vector *to, void *user) {
01100 DynamicTextFont *self = (DynamicTextFont *)user;
01101
01102
01103 PN_stdfloat scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
01104 LPoint2 p = LPoint2(to->x, to->y) * scale;
01105
01106 if (self->_contours.empty() ||
01107 !self->_contours.back()._points.empty()) {
01108 self->_contours.push_back(Contour());
01109 }
01110 self->_q = p;
01111 return 0;
01112 }
01113
01114
01115
01116
01117
01118
01119
01120 int DynamicTextFont::
01121 outline_line_to(const FT_Vector *to, void *user) {
01122 DynamicTextFont *self = (DynamicTextFont *)user;
01123 nassertr(!self->_contours.empty(), 1);
01124
01125
01126 PN_stdfloat scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
01127 LPoint2 p = LPoint2(to->x, to->y) * scale;
01128
01129
01130 LVector2 t = (p - self->_q);
01131 t.normalize();
01132
01133 if (self->_contours.back()._points.empty()) {
01134 self->_contours.back()._points.push_back(ContourPoint(self->_q, LVector2::zero(), t));
01135 } else {
01136 self->_contours.back()._points.back().connect_to(t);
01137 }
01138
01139 self->_contours.back()._points.push_back(ContourPoint(p, t, LVector2::zero()));
01140 self->_q = p;
01141 return 0;
01142 }
01143
01144
01145
01146
01147
01148
01149
01150 int DynamicTextFont::
01151 outline_conic_to(const FT_Vector *control,
01152 const FT_Vector *to, void *user) {
01153 DynamicTextFont *self = (DynamicTextFont *)user;
01154 nassertr(!self->_contours.empty(), 1);
01155
01156
01157 PN_stdfloat scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
01158
01159 LPoint2 c = LPoint2(control->x, control->y) * scale;
01160 LPoint2 p = LPoint2(to->x, to->y) * scale;
01161
01162
01163 NurbsCurveEvaluator nce;
01164 nce.local_object();
01165 nce.set_order(3);
01166 nce.reset(3);
01167 nce.set_vertex(0, LVecBase3(self->_q[0], self->_q[1], 0.0f));
01168 nce.set_vertex(1, LVecBase3(c[0], c[1], 0.0f));
01169 nce.set_vertex(2, LVecBase3(p[0], p[1], 0.0f));
01170
01171 self->_q = p;
01172
01173 PT(NurbsCurveResult) ncr = nce.evaluate();
01174 return self->outline_nurbs(ncr);
01175 }
01176
01177
01178
01179
01180
01181
01182
01183 int DynamicTextFont::
01184 outline_cubic_to(const FT_Vector *control1, const FT_Vector *control2,
01185 const FT_Vector *to, void *user) {
01186 DynamicTextFont *self = (DynamicTextFont *)user;
01187 nassertr(!self->_contours.empty(), 1);
01188
01189
01190 PN_stdfloat scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
01191
01192 LPoint2 c1 = LPoint2(control1->x, control1->y) * scale;
01193 LPoint2 c2 = LPoint2(control2->x, control2->y) * scale;
01194 LPoint2 p = LPoint2(to->x, to->y) * scale;
01195
01196
01197 NurbsCurveEvaluator nce;
01198 nce.local_object();
01199 nce.set_order(4);
01200 nce.reset(4);
01201 nce.set_vertex(0, LVecBase3(self->_q[0], self->_q[1], 0.0f));
01202 nce.set_vertex(1, LVecBase3(c1[0], c1[1], 0.0f));
01203 nce.set_vertex(2, LVecBase3(c2[0], c2[1], 0.0f));
01204 nce.set_vertex(3, LVecBase3(p[0], p[1], 0.0f));
01205
01206 self->_q = p;
01207
01208 PT(NurbsCurveResult) ncr = nce.evaluate();
01209 return self->outline_nurbs(ncr);
01210 }
01211
01212
01213
01214
01215
01216
01217
01218 int DynamicTextFont::
01219 outline_nurbs(NurbsCurveResult *ncr) {
01220
01221
01222 ncr->adaptive_sample(1.0f / _font_pixels_per_unit);
01223
01224 int num_samples = ncr->get_num_samples();
01225
01226 bool needs_connect = false;
01227 int start = 1;
01228 if (_contours.back()._points.empty()) {
01229
01230
01231 start = 0;
01232 } else {
01233 needs_connect = true;
01234 }
01235
01236 for (int i = start; i < num_samples; ++i) {
01237 PN_stdfloat st = ncr->get_sample_t(i);
01238 const LPoint3 &p = ncr->get_sample_point(i);
01239
01240 PN_stdfloat st0 = st, st1 = st;
01241 if (i > 0) {
01242 st0 = ncr->get_sample_t(i - 1) * 0.1f + st * 0.9f;
01243 }
01244 if (i < num_samples - 1) {
01245 st1 = ncr->get_sample_t(i + 1) * 0.1f + st * 0.9f;
01246 }
01247
01248
01249
01250 LPoint3 p0, p1;
01251 ncr->eval_point(st0, p0);
01252 ncr->eval_point(st1, p1);
01253 LVector3 t = p1 - p0;
01254 t.normalize();
01255
01256 if (needs_connect) {
01257 _contours.back()._points.back().connect_to(LVector2(t[0], t[1]));
01258 needs_connect = false;
01259 }
01260
01261 _contours.back()._points.push_back(ContourPoint(p[0], p[1], t[0], t[1]));
01262 }
01263
01264 return 0;
01265 }
01266
01267 #endif // HAVE_FREETYPE