15 #include "dynamicTextFont.h" 19 #undef interface // I don't know where this symbol is defined, but it interferes with FreeType. 31 #include "config_text.h" 32 #include "config_util.h" 33 #include "config_express.h" 34 #include "virtualFileSystem.h" 35 #include "geomVertexData.h" 36 #include "geomVertexFormat.h" 37 #include "geomVertexWriter.h" 38 #include "geomLinestrips.h" 39 #include "geomTriangles.h" 40 #include "renderState.h" 41 #include "string_utils.h" 42 #include "triangulator.h" 43 #include "nurbsCurveEvaluator.h" 44 #include "nurbsCurveResult.h" 60 DynamicTextFont(
const Filename &font_filename,
int face_index) {
62 _is_valid = load_font(font_filename, face_index);
63 TextFont::set_name(FreetypeFont::get_name());
64 TextFont::_line_height = FreetypeFont::get_line_height();
65 TextFont::_space_advance = FreetypeFont::get_space_advance();
67 _fg.set(1.0f, 1.0f, 1.0f, 1.0f);
68 _bg.set(1.0f, 1.0f, 1.0f, 0.0f);
69 _outline_color.set(1.0f, 1.0f, 1.0f, 0.0f);
70 _outline_width = 0.0f;
71 _outline_feather = 0.0f;
73 _tex_format = Texture::F_alpha;
74 _needs_image_processing =
false;
85 DynamicTextFont(
const char *font_data,
int data_length,
int face_index) {
87 _is_valid = load_font(font_data, data_length, face_index);
88 TextFont::set_name(FreetypeFont::get_name());
89 TextFont::_line_height = FreetypeFont::_line_height;
90 TextFont::_space_advance = FreetypeFont::_space_advance;
92 _fg.set(1.0f, 1.0f, 1.0f, 1.0f);
93 _bg.set(1.0f, 1.0f, 1.0f, 0.0f);
94 _outline_color.set(1.0f, 1.0f, 1.0f, 0.0f);
95 _outline_width = 0.0f;
96 _outline_feather = 0.0f;
98 _tex_format = Texture::F_alpha;
99 _needs_image_processing =
false;
108 DynamicTextFont(
const DynamicTextFont ©) :
111 _texture_margin(copy._texture_margin),
112 _poly_margin(copy._poly_margin),
113 _page_x_size(copy._page_x_size),
114 _page_y_size(copy._page_y_size),
115 _minfilter(copy._minfilter),
116 _magfilter(copy._magfilter),
117 _anisotropic_degree(copy._anisotropic_degree),
118 _render_mode(copy._render_mode),
119 _winding_order(copy._winding_order),
122 _outline_color(copy._outline_color),
123 _outline_width(copy._outline_width),
124 _outline_feather(copy._outline_feather),
125 _has_outline(copy._has_outline),
126 _tex_format(copy._tex_format),
127 _needs_image_processing(copy._needs_image_processing),
148 return new DynamicTextFont(*
this);
161 int DynamicTextFont::
162 get_num_pages()
const {
163 return _pages.size();
176 DynamicTextPage *DynamicTextFont::
177 get_page(
int n)
const {
178 nassertr(n >= 0 && n < (
int)_pages.size(), (DynamicTextPage *)NULL);
189 int DynamicTextFont::
191 int removed_count = 0;
196 for (ci = _cache.begin(); ci != _cache.end(); ++ci) {
197 DynamicTextGlyph *glyph = (*ci).second;
198 if (glyph == (DynamicTextGlyph *)NULL || glyph->_geom_count != 0) {
200 new_cache.insert(new_cache.end(), (*ci));
206 _cache.swap(new_cache);
210 for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
211 DynamicTextPage *page = (*pi);
212 page->garbage_collect(
this);
215 return removed_count;
231 void DynamicTextFont::
235 _empty_glyphs.clear();
243 void DynamicTextFont::
244 write(ostream &out,
int indent_level)
const {
245 static const int max_glyph_name = 1024;
246 char glyph_name[max_glyph_name];
248 indent(out, indent_level)
249 <<
"DynamicTextFont " << get_name() <<
", " 250 << get_num_pages() <<
" pages, " 251 << _cache.size() <<
" glyphs:\n";
252 Cache::const_iterator ci;
253 for (ci = _cache.begin(); ci != _cache.end(); ++ci) {
254 int glyph_index = (*ci).first;
255 DynamicTextGlyph *glyph = (*ci).second;
256 indent(out, indent_level + 2)
259 FT_Face face = acquire_face();
260 if (FT_HAS_GLYPH_NAMES(face)) {
261 int error = FT_Get_Glyph_Name(face, glyph_index,
262 glyph_name, max_glyph_name);
266 if (!error && strcmp(glyph_name,
".notdef") != 0) {
267 out <<
" (" << glyph_name <<
")";
272 out <<
", count = " << glyph->_geom_count <<
"\n";
287 bool DynamicTextFont::
288 get_glyph(
int character,
const TextGlyph *&glyph) {
294 FT_Face face = acquire_face();
295 int glyph_index = FT_Get_Char_Index(face, character);
296 if (text_cat.is_spam()) {
298 << *
this <<
" maps " << character <<
" to glyph " << glyph_index <<
"\n";
301 Cache::iterator ci = _cache.find(glyph_index);
302 if (ci != _cache.end()) {
303 glyph = (*ci).second;
305 DynamicTextGlyph *dynamic_glyph = make_glyph(character, face, glyph_index);
306 _cache.insert(Cache::value_type(glyph_index, dynamic_glyph));
307 glyph = dynamic_glyph;
310 if (glyph == (DynamicTextGlyph *)NULL) {
311 glyph = get_invalid_glyph();
316 return (glyph_index != 0);
326 void DynamicTextFont::
328 _texture_margin = text_texture_margin;
329 _poly_margin = text_poly_margin;
330 _page_x_size = text_page_size[0];
331 _page_y_size = text_page_size[1];
337 _minfilter = text_minfilter;
338 _magfilter = text_magfilter;
342 _anisotropic_degree = text_anisotropic_degree;
344 _render_mode = text_render_mode;
345 _winding_order = WO_default;
357 void DynamicTextFont::
360 for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
361 DynamicTextPage *page = (*pi);
362 page->set_minfilter(_minfilter);
363 page->set_magfilter(_magfilter);
364 page->set_anisotropic_degree(_anisotropic_degree);
375 void DynamicTextFont::
376 determine_tex_format() {
377 nassertv(get_num_pages() == 0);
379 _has_outline = (_outline_color != _bg && _outline_width > 0.0f);
380 _needs_image_processing =
true;
382 bool needs_color =
false;
383 bool needs_grayscale =
false;
384 bool needs_alpha =
false;
386 if (_fg[1] != _fg[0] || _fg[2] != _fg[0] ||
387 _bg[1] != _bg[0] || _bg[2] != _bg[0] ||
388 (_has_outline && (_outline_color[1] != _outline_color[0] || _outline_color[2] != _outline_color[0]))) {
393 }
else if (_fg[0] != 1.0f || _fg[1] != 1.0f || _fg[2] != 1.0f ||
394 _bg[0] != 1.0f || _bg[1] != 1.0f || _bg[2] != 1.0f ||
395 (_has_outline && (_outline_color[0] != 1.0f || _outline_color[1] != 1.0f || _outline_color[2] != 1.0f))) {
397 needs_grayscale =
true;
400 if (_fg[3] != 1.0f || _bg[3] != 1.0f ||
401 (_has_outline && (_outline_color[3] != 1.0f))) {
408 _tex_format = Texture::F_rgba;
410 _tex_format = Texture::F_rgb;
412 }
else if (needs_grayscale) {
414 _tex_format = Texture::F_luminance_alpha;
416 _tex_format = Texture::F_luminance;
420 _tex_format = Texture::F_alpha;
423 _fg ==
LColor(1.0f, 1.0f, 1.0f, 1.0f) &&
424 _bg ==
LColor(1.0f, 1.0f, 1.0f, 0.0f)) {
427 _needs_image_processing =
false;
432 _tex_format = Texture::F_luminance;
445 DynamicTextGlyph *DynamicTextFont::
446 make_glyph(
int character, FT_Face face,
int glyph_index) {
447 if (!load_glyph(face, glyph_index,
false)) {
448 return (DynamicTextGlyph *)NULL;
451 FT_GlyphSlot slot = face->glyph;
452 FT_Bitmap &bitmap = slot->bitmap;
454 if ((bitmap.width == 0 || bitmap.rows == 0) && (glyph_index == 0)) {
463 PN_stdfloat advance = slot->advance.x / 64.0;
465 if (_render_mode != RM_texture &&
466 slot->format == ft_glyph_format_outline) {
492 FT_Outline_Funcs funcs;
493 memset(&funcs, 0,
sizeof(funcs));
494 funcs.move_to = (FT_Outline_MoveTo_Func)outline_move_to;
495 funcs.line_to = (FT_Outline_LineTo_Func)outline_line_to;
496 funcs.conic_to = (FT_Outline_ConicTo_Func)outline_conic_to;
497 funcs.cubic_to = (FT_Outline_CubicTo_Func)outline_cubic_to;
499 WindingOrder wo = _winding_order;
500 if (wo == WO_default) {
503 #ifdef FT_ORIENTATION_FILL_RIGHT 504 if (FT_Outline_Get_Orientation(&slot->outline) == FT_ORIENTATION_FILL_RIGHT) {
513 #endif // FT_ORIENTATION_FILL_RIGHT 517 FT_Outline_Reverse(&slot->outline);
521 FT_Outline_Decompose(&slot->outline, &funcs, (
void *)
this);
523 PT(DynamicTextGlyph) glyph =
524 new DynamicTextGlyph(character, advance / _font_pixels_per_unit);
525 switch (_render_mode) {
527 render_wireframe_contours(glyph);
531 render_polygon_contours(glyph,
true,
false);
535 render_polygon_contours(glyph,
false,
true);
539 render_polygon_contours(glyph,
true,
true);
549 if (slot->format != ft_glyph_format_bitmap) {
550 FT_Render_Glyph(slot, ft_render_mode_normal);
553 if (bitmap.width == 0 || bitmap.rows == 0) {
556 PT(DynamicTextGlyph) glyph =
557 new DynamicTextGlyph(character, advance / _font_pixels_per_unit);
558 _empty_glyphs.push_back(glyph);
562 DynamicTextGlyph *glyph;
564 PN_stdfloat tex_x_size = bitmap.width;
565 PN_stdfloat tex_y_size = bitmap.rows;
569 if (_tex_pixels_per_unit == _font_pixels_per_unit &&
570 !_needs_image_processing) {
574 glyph = slot_glyph(character, bitmap.width, bitmap.rows);
575 copy_bitmap_to_texture(bitmap, glyph);
581 tex_x_size /= _scale_factor;
582 tex_y_size /= _scale_factor;
583 int int_x_size = (int)ceil(tex_x_size);
584 int int_y_size = (int)ceil(tex_y_size);
585 int bmp_x_size = (int)(int_x_size * _scale_factor + 0.5f);
586 int bmp_y_size = (int)(int_y_size * _scale_factor + 0.5f);
588 PNMImage image(bmp_x_size, bmp_y_size, PNMImage::CT_grayscale);
589 copy_bitmap_to_pnmimage(bitmap, image);
591 PNMImage reduced(int_x_size, int_y_size, PNMImage::CT_grayscale);
592 reduced.quick_filter_from(image);
595 PN_stdfloat outline_pixels = _outline_width / _points_per_unit * _tex_pixels_per_unit;
596 outline = (int)ceil(outline_pixels);
598 int_x_size += outline * 2;
599 int_y_size += outline * 2;
600 tex_x_size += outline * 2;
601 tex_y_size += outline * 2;
602 glyph = slot_glyph(character, int_x_size, int_y_size);
606 PNMImage padded(int_x_size, int_y_size, PNMImage::CT_grayscale);
607 padded.copy_sub_image(reduced, outline, outline);
608 copy_pnmimage_to_texture(padded, glyph);
611 copy_pnmimage_to_texture(reduced, glyph);
615 glyph->make_geom((
int)floor(slot->bitmap_top + outline * _scale_factor + 0.5f),
616 (
int)floor(slot->bitmap_left - outline * _scale_factor + 0.5f),
617 advance, _poly_margin,
618 tex_x_size, tex_y_size,
619 _font_pixels_per_unit, _tex_pixels_per_unit);
631 void DynamicTextFont::
632 copy_bitmap_to_texture(
const FT_Bitmap &bitmap, DynamicTextGlyph *glyph) {
633 if (bitmap.pixel_mode == ft_pixel_mode_grays && bitmap.num_grays == 256) {
636 unsigned char *buffer_row = bitmap.buffer;
637 for (
int yi = 0; yi < bitmap.rows; yi++) {
639 unsigned char *texture_row = glyph->get_row(yi);
640 nassertv(texture_row != (
unsigned char *)NULL);
641 memcpy(texture_row, buffer_row, bitmap.width);
642 buffer_row += bitmap.pitch;
645 }
else if (bitmap.pixel_mode == ft_pixel_mode_mono) {
648 unsigned char *buffer_row = bitmap.buffer;
649 for (
int yi = 0; yi < bitmap.rows; yi++) {
650 unsigned char *texture_row = glyph->get_row(yi);
651 nassertv(texture_row != (
unsigned char *)NULL);
654 unsigned char *b = buffer_row;
655 for (
int xi = 0; xi < bitmap.width; xi++) {
657 texture_row[xi] = 0xff;
659 texture_row[xi] = 0x00;
668 buffer_row += bitmap.pitch;
672 }
else if (bitmap.pixel_mode == ft_pixel_mode_grays) {
675 unsigned char *buffer_row = bitmap.buffer;
676 for (
int yi = 0; yi < bitmap.rows; yi++) {
677 unsigned char *texture_row = glyph->get_row(yi);
678 nassertv(texture_row != (
unsigned char *)NULL);
679 for (
int xi = 0; xi < bitmap.width; xi++) {
680 texture_row[xi] = (int)(buffer_row[xi] * 255) / (bitmap.num_grays - 1);
682 buffer_row += bitmap.pitch;
687 <<
"Unexpected pixel mode in bitmap: " << (int)bitmap.pixel_mode <<
"\n";
697 void DynamicTextFont::
698 copy_pnmimage_to_texture(
const PNMImage &image, DynamicTextGlyph *glyph) {
699 if (!_needs_image_processing) {
702 nassertv(glyph->_page->get_num_components() == 1);
703 for (
int yi = 0; yi < image.
get_y_size(); yi++) {
704 unsigned char *texture_row = glyph->get_row(yi);
705 nassertv(texture_row != (
unsigned char *)NULL);
706 for (
int xi = 0; xi < image.
get_x_size(); xi++) {
715 PN_stdfloat outline_pixels = _outline_width / _points_per_unit * _tex_pixels_per_unit;
722 PN_stdfloat f = _outline_feather * _outline_feather;
724 for (
int yi = 0; yi < outline.get_y_size(); yi++) {
725 for (
int xi = 0; xi < outline.get_x_size(); xi++) {
726 PN_stdfloat v = outline.get_gray(xi, yi);
731 outline.set_gray(xi, yi, 1.0);
734 outline.set_gray(xi, yi, v / f);
740 blend_pnmimage_to_texture(outline, glyph, _outline_color);
746 blend_pnmimage_to_texture(image, glyph, _fg);
758 void DynamicTextFont::
759 blend_pnmimage_to_texture(
const PNMImage &image, DynamicTextGlyph *glyph,
764 if (num_components == 1) {
767 if (glyph->_page->get_format() != Texture::F_alpha) {
771 for (
int yi = 0; yi < image.
get_y_size(); yi++) {
772 unsigned char *texture_row = glyph->get_row(yi);
773 nassertv(texture_row != (
unsigned char *)NULL);
774 for (
int xi = 0; xi < image.
get_x_size(); xi++) {
775 unsigned char *tr = texture_row + xi;
776 PN_stdfloat t = (PN_stdfloat)image.
get_gray(xi, yi);
777 tr[0] = (
unsigned char)(tr[0] + t * (fgv[ci] - tr[0]));
781 }
else if (num_components == 2) {
784 for (
int yi = 0; yi < image.
get_y_size(); yi++) {
785 unsigned char *texture_row = glyph->get_row(yi);
786 nassertv(texture_row != (
unsigned char *)NULL);
787 for (
int xi = 0; xi < image.
get_x_size(); xi++) {
788 unsigned char *tr = texture_row + xi * 2;
789 PN_stdfloat t = (PN_stdfloat)image.
get_gray(xi, yi);
790 tr[0] = (
unsigned char)(tr[0] + t * (fgv[0] - tr[0]));
791 tr[1] = (
unsigned char)(tr[1] + t * (fgv[3] - tr[1]));
795 }
else if (num_components == 3) {
798 for (
int yi = 0; yi < image.
get_y_size(); yi++) {
799 unsigned char *texture_row = glyph->get_row(yi);
800 nassertv(texture_row != (
unsigned char *)NULL);
801 for (
int xi = 0; xi < image.
get_x_size(); xi++) {
802 unsigned char *tr = texture_row + xi * 3;
803 PN_stdfloat t = (PN_stdfloat)image.
get_gray(xi, yi);
804 tr[0] = (
unsigned char)(tr[0] + t * (fgv[2] - tr[0]));
805 tr[1] = (
unsigned char)(tr[1] + t * (fgv[1] - tr[1]));
806 tr[2] = (
unsigned char)(tr[2] + t * (fgv[0] - tr[2]));
813 for (
int yi = 0; yi < image.
get_y_size(); yi++) {
814 unsigned char *texture_row = glyph->get_row(yi);
815 nassertv(texture_row != (
unsigned char *)NULL);
816 for (
int xi = 0; xi < image.
get_x_size(); xi++) {
817 unsigned char *tr = texture_row + xi * 4;
818 PN_stdfloat t = (PN_stdfloat)image.
get_gray(xi, yi);
819 tr[0] = (
unsigned char)(tr[0] + t * (fgv[2] - tr[0]));
820 tr[1] = (
unsigned char)(tr[1] + t * (fgv[1] - tr[1]));
821 tr[2] = (
unsigned char)(tr[2] + t * (fgv[0] - tr[2]));
822 tr[3] = (
unsigned char)(tr[3] + t * (fgv[3] - tr[3]));
837 DynamicTextGlyph *DynamicTextFont::
838 slot_glyph(
int character,
int x_size,
int y_size) {
840 x_size += _texture_margin * 2;
841 y_size += _texture_margin * 2;
843 if (!_pages.empty()) {
848 _preferred_page = _preferred_page % _pages.size();
849 int pi = _preferred_page;
852 DynamicTextPage *page = _pages[pi];
853 DynamicTextGlyph *glyph = page->slot_glyph(character, x_size, y_size, _texture_margin);
854 if (glyph != (DynamicTextGlyph *)NULL) {
857 _preferred_page = pi;
861 if (page->is_empty()) {
864 <<
"Glyph of size " << x_size <<
" by " << y_size
865 <<
" pixels won't fit on an empty page.\n";
866 return (DynamicTextGlyph *)NULL;
869 pi = (pi + 1) % _pages.size();
870 }
while (pi != _preferred_page);
875 if (garbage_collect() != 0) {
877 return slot_glyph(character, x_size, y_size);
882 _preferred_page = _pages.size();
883 PT(DynamicTextPage) page =
new DynamicTextPage(
this, _preferred_page);
884 _pages.push_back(page);
885 return page->slot_glyph(character, x_size, y_size, _texture_margin);
895 void DynamicTextFont::
896 render_wireframe_contours(DynamicTextGlyph *glyph) {
898 (
string(), GeomVertexFormat::get_v3(),
904 Contours::const_iterator ci;
905 for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
906 const Contour &contour = (*ci);
907 Points::const_iterator pi;
909 for (pi = contour._points.begin(); pi != contour._points.end(); ++pi) {
911 vertex.add_data3(p[0], 0.0f, p[1]);
914 lines->add_next_vertices(contour._points.size());
915 lines->close_primitive();
918 glyph->set_geom(vdata, lines, RenderState::make_empty());
928 void DynamicTextFont::
929 render_polygon_contours(DynamicTextGlyph *glyph,
bool face,
bool extrude) {
931 (
string(), GeomVertexFormat::get_v3n3(),
939 Contours::iterator ci;
944 for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
945 Contour &contour = (*ci);
949 for (
size_t i = 0; i < contour._points.size() - 1; ++i) {
950 const LPoint2 &p = contour._points[i]._p;
951 vertex.add_data3(p[0], 0.0f, p[1]);
952 normal.add_data3(0.0f, -1.0f, 0.0f);
961 for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
962 const Contour &contour = (*ci);
964 if (contour._is_solid && !contour._points.empty()) {
966 for (
size_t i = 0; i < contour._points.size() - 1; ++i) {
971 Contours::iterator cj;
972 for (cj = _contours.begin(); cj != _contours.end(); ++cj) {
973 Contour &hole = (*cj);
974 if (!hole._is_solid && !hole._points.empty()) {
976 for (
size_t j = 0; j < hole._points.size() - 1; ++j) {
984 for (
int ti = 0; ti < num_triangles; ++ti) {
988 tris->close_primitive();
999 for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
1000 const Contour &contour = (*ci);
1001 Points::const_iterator pi;
1003 for (
size_t i = 0; i < contour._points.size(); ++i) {
1004 const ContourPoint &cp = contour._points[i];
1009 LVector3 n_in(t_in[1], 0.0f, -t_in[0]);
1010 vertex.add_data3(p[0], 1.0f, p[1]);
1011 vertex.add_data3(p[0], 0.0f, p[1]);
1012 normal.add_data3(n_in);
1013 normal.add_data3(n_in);
1016 int vi = vertex.get_write_row();
1017 tris->add_vertex(vi - 4);
1018 tris->add_vertex(vi - 2);
1019 tris->add_vertex(vi - 1);
1020 tris->close_primitive();
1021 tris->add_vertex(vi - 1);
1022 tris->add_vertex(vi - 3);
1023 tris->add_vertex(vi - 4);
1024 tris->close_primitive();
1027 if (i != contour._points.size() - 1 && !t_in.
almost_equal(t_out)) {
1030 LVector3 n_out(t_out[1], 0.0f, -t_out[0]);
1031 vertex.add_data3(p[0], 1.0f, p[1]);
1032 vertex.add_data3(p[0], 0.0f, p[1]);
1033 normal.add_data3(n_out);
1034 normal.add_data3(n_out);
1041 int back_start = vertex.get_write_row();
1043 for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
1044 Contour &contour = (*ci);
1045 for (
size_t i = 0; i < contour._points.size() - 1; ++i) {
1046 const LPoint2 &p = contour._points[i]._p;
1047 vertex.add_data3(p[0], 1.0f, p[1]);
1048 normal.add_data3(0.0f, 1.0f, 0.0f);
1053 for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
1054 const Contour &contour = (*ci);
1056 if (contour._is_solid && !contour._points.empty()) {
1058 for (
size_t i = 0; i < contour._points.size() - 1; ++i) {
1063 Contours::iterator cj;
1064 for (cj = _contours.begin(); cj != _contours.end(); ++cj) {
1065 Contour &hole = (*cj);
1066 if (!hole._is_solid && !hole._points.empty()) {
1068 for (
size_t j = 0; j < hole._points.size() - 1; ++j) {
1076 for (
int ti = 0; ti < num_triangles; ++ti) {
1080 tris->close_primitive();
1087 glyph->set_geom(vdata, tris, RenderState::make_empty());
1099 int DynamicTextFont::
1100 outline_move_to(
const FT_Vector *to,
void *user) {
1101 DynamicTextFont *
self = (DynamicTextFont *)user;
1104 PN_stdfloat scale = 1.0f / (64.0f *
self->_font_pixels_per_unit);
1107 if (self->_contours.empty() ||
1108 !
self->_contours.back()._points.empty()) {
1109 self->_contours.push_back(Contour());
1121 int DynamicTextFont::
1122 outline_line_to(
const FT_Vector *to,
void *user) {
1123 DynamicTextFont *
self = (DynamicTextFont *)user;
1124 nassertr(!self->_contours.empty(), 1);
1127 PN_stdfloat scale = 1.0f / (64.0f *
self->_font_pixels_per_unit);
1134 if (self->_contours.back()._points.empty()) {
1135 self->_contours.back()._points.push_back(ContourPoint(self->_q,
LVector2::zero(), t));
1137 self->_contours.back()._points.back().connect_to(t);
1140 self->_contours.back()._points.push_back(ContourPoint(p, t,
LVector2::zero()));
1151 int DynamicTextFont::
1152 outline_conic_to(
const FT_Vector *control,
1153 const FT_Vector *to,
void *user) {
1154 DynamicTextFont *
self = (DynamicTextFont *)user;
1155 nassertr(!self->_contours.empty(), 1);
1158 PN_stdfloat scale = 1.0f / (64.0f *
self->_font_pixels_per_unit);
1175 return self->outline_nurbs(ncr);
1184 int DynamicTextFont::
1185 outline_cubic_to(
const FT_Vector *control1,
const FT_Vector *control2,
1186 const FT_Vector *to,
void *user) {
1187 DynamicTextFont *
self = (DynamicTextFont *)user;
1188 nassertr(!self->_contours.empty(), 1);
1191 PN_stdfloat scale = 1.0f / (64.0f *
self->_font_pixels_per_unit);
1210 return self->outline_nurbs(ncr);
1219 int DynamicTextFont::
1227 bool needs_connect =
false;
1229 if (_contours.back()._points.empty()) {
1234 needs_connect =
true;
1237 for (
int i = start; i < num_samples; ++i) {
1241 PN_stdfloat st0 = st, st1 = st;
1245 if (i < num_samples - 1) {
1257 if (needs_connect) {
1258 _contours.back()._points.back().connect_to(
LVector2(t[0], t[1]));
1259 needs_connect =
false;
1262 _contours.back()._points.push_back(ContourPoint(p[0], p[1], t[0], t[1]));
1268 #endif // HAVE_FREETYPE This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
bool almost_equal(const LVecBase2f &other, float threshold) const
Returns true if two vectors are memberwise equal within a specified tolerance.
This is the base class for all three-component vectors and points.
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
int get_num_components() const
Returns the number of elements in the vector, four.
void reset(int num_vertices)
Resets all the vertices and knots to their default values, and sets the curve up with the indicated n...
int get_num_vertices() const
Returns the number of vertices in the pool.
xelval get_gray_val(int x, int y) const
Returns the gray component color at the indicated pixel.
void triangulate()
Does the work of triangulating the specified polygon.
float get_gray(int x, int y) const
Returns the gray component color at the indicated pixel.
const LPoint3 & get_sample_point(int n) const
Returns the point on the curve of the nth sample point generated by the previous call to adaptive_sam...
This class is an abstraction for evaluating NURBS curves.
int get_triangle_v0(int n) const
Returns vertex 0 of the nth triangle generated by the previous call to triangulate().
bool normalize()
Normalizes the vector in place.
void begin_hole()
Finishes the previous hole, if any, and prepares to add a new hole.
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
void set_order(int order)
Sets the order of the curve.
int get_triangle_v2(int n) const
Returns vertex 2 of the nth triangle generated by the previous call to triangulate().
void add_hole_vertex(int index)
Adds the next consecutive vertex of the current hole.
An encapsulation of a font; i.e.
PN_stdfloat get_sample_t(int n) const
Returns the t value of the nth sample point generated by the previous call to adaptive_sample().
void clear_polygon()
Removes the current polygon definition (and its set of holes), but does not clear the vertex pool...
int get_num_samples() const
Returns the number of sample points generated by the previous call to adaptive_sample().
void set_vertex(int i, const LVecBase4 &vertex)
Sets the nth control vertex of the curve, as a vertex in 4-d homogeneous space.
The name of a file, such as a texture file or an Egg file.
This class can triangulate a convex or concave polygon, even one with holes.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
A representation of a single glyph (character) from a font.
bool eval_point(PN_stdfloat t, LVecBase3 &point)
Computes the point on the curve corresponding to the indicated value in parametric time...
void gaussian_filter_from(float radius, const PNMImage ©)
Makes a resized copy of the indicated image into this one using the indicated filter.
bool is_left_winding() const
Returns true if the polygon vertices are listed in counterclockwise order, or false if they appear to...
int get_num_triangles() const
Returns the number of triangles generated by the previous call to triangulate().
static const LVector2f & zero()
Returns a zero-length vector.
void add_polygon_vertex(int index)
Adds the next consecutive vertex of the polygon.
void local_object()
This function should be called, once, immediately after creating a new instance of some ReferenceCoun...
This is the base class for all three-component vectors and points.
This is a two-component vector offset.
Defines a series of line strips.
Defines a series of disconnected triangles.
void adaptive_sample(PN_stdfloat tolerance)
Determines the set of subdivisions necessary to approximate the curve with a set of linear segments...
This is a two-component point in space.
TypeHandle is the identifier used to differentiate C++ class types.
int get_triangle_v1(int n) const
Returns vertex 1 of the nth triangle generated by the previous call to triangulate().
int add_vertex(const LPoint2d &point)
Adds a new vertex to the vertex pool.
bool normalize()
Normalizes the vector in place.
The result of a NurbsCurveEvaluator.