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::
1223 ncr->adaptive_sample(1.0f / _font_pixels_per_unit);
1225 int num_samples = ncr->get_num_samples();
1227 bool needs_connect =
false;
1229 if (_contours.back()._points.empty()) {
1234 needs_connect =
true;
1237 for (
int i = start; i < num_samples; ++i) {
1238 PN_stdfloat st = ncr->get_sample_t(i);
1239 const LPoint3 &p = ncr->get_sample_point(i);
1241 PN_stdfloat st0 = st, st1 = st;
1243 st0 = ncr->get_sample_t(i - 1) * 0.1f + st * 0.9f;
1245 if (i < num_samples - 1) {
1246 st1 = ncr->get_sample_t(i + 1) * 0.1f + st * 0.9f;
1252 ncr->eval_point(st0, p0);
1253 ncr->eval_point(st1, p1);
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
int get_triangle_v0(int n) const
Returns vertex 0 of the nth triangle generated by the previous call to triangulate().
bool is_left_winding() const
Returns true if the polygon vertices are listed in counterclockwise order, or false if they appear to...
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
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_vertices() const
Returns the number of vertices in the pool.
void reset(int num_vertices)
Resets all the vertices and knots to their default values, and sets the curve up with the indicated n...
void triangulate()
Does the work of triangulating the specified polygon.
This class is an abstraction for evaluating NURBS curves.
float get_gray(int x, int y) const
Returns the gray component color at the indicated pixel.
bool normalize()
Normalizes the vector in place.
int get_triangle_v1(int n) const
Returns vertex 1 of the nth triangle generated by the previous call to triangulate().
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().
int get_num_triangles() const
Returns the number of triangles 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.
void clear_polygon()
Removes the current polygon definition (and its set of holes), but does not clear the vertex pool...
bool almost_equal(const LVecBase2f &other, float threshold) const
Returns true if two vectors are memberwise equal within a specified tolerance.
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 container for geometry primitives.
A representation of a single glyph (character) from a font.
void gaussian_filter_from(float radius, const PNMImage ©)
Makes a resized copy of the indicated image into this one using the indicated filter.
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.
This is a two-component point in space.
TypeHandle is the identifier used to differentiate C++ class types.
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.
xelval get_gray_val(int x, int y) const
Returns the gray component color at the indicated pixel.
int get_num_components() const
Returns the number of elements in the vector, four.