49 static const PN_stdfloat small_accent_scale = 0.6f;
52 static const PN_stdfloat tiny_accent_scale = 0.4;
55 static const PN_stdfloat squash_accent_scale_x = 0.8f;
56 static const PN_stdfloat squash_accent_scale_y = 0.5f;
60 static const PN_stdfloat small_squash_accent_scale_x = 0.6f;
61 static const PN_stdfloat small_squash_accent_scale_y = 0.3;
65 static const PN_stdfloat ligature_advance_scale = 0.6f;
73 isspacew(
unsigned int ch) {
74 return isascii(ch) && isspace(ch);
82 isbreakpoint(
unsigned int ch) {
83 return (ch ==
' ' || ch ==
'\t' ||
84 ch == (
unsigned int)text_soft_hyphen_key ||
85 ch == (
unsigned int)text_soft_break_key);
95 _usage_hint(
Geom::UH_static),
97 _dynamic_merge(text_dynamic_merge),
109 _initial_cprops(copy._initial_cprops),
110 _text_string(copy._text_string),
111 _text_block(copy._text_block),
114 _next_row_ypos(copy._next_row_ypos),
115 _encoder(copy._encoder),
116 _usage_hint(copy._usage_hint),
117 _max_rows(copy._max_rows),
118 _dynamic_merge(copy._dynamic_merge),
119 _multiline_mode(copy._multiline_mode)
128 _initial_cprops = copy._initial_cprops;
129 _text_string = copy._text_string;
130 _text_block = copy._text_block;
133 _next_row_ypos = copy._next_row_ypos;
134 _encoder = copy._encoder;
135 _usage_hint = copy._usage_hint;
136 _max_rows = copy._max_rows;
137 _dynamic_merge = copy._dynamic_merge;
138 _multiline_mode = copy._multiline_mode;
155 _next_row_ypos = 0.0f;
157 _text_string.clear();
175 wstring::const_iterator si = wtext.begin();
176 scan_wtext(_text_string, si, wtext.end(), _initial_cprops);
178 while (si != wtext.end()) {
183 <<
"pop_properties encountered without preceding push_properties.\n";
184 scan_wtext(_text_string, si, wtext.end(), _initial_cprops);
188 return wordwrap_text();
205 set_wsubstr(
const wstring &wtext,
int start,
int count) {
206 nassertr(start >= 0 && start <= (
int)_text_string.size(),
false);
207 nassertr(count >= 0 && start + count <= (
int)_text_string.size(),
false);
212 wstring::const_iterator si = wtext.begin();
213 scan_wtext(substr, si, wtext.end(), _initial_cprops);
214 while (si != wtext.end()) {
216 <<
"pop_properties encountered without preceding push_properties.\n";
217 scan_wtext(substr, si, wtext.end(), _initial_cprops);
220 _text_string.erase(_text_string.begin() + start, _text_string.begin() + start + count);
221 _text_string.insert(_text_string.begin() + start, substr.begin(), substr.end());
223 return wordwrap_text();
239 TextString::const_iterator si;
240 for (si = _text_string.begin(); si != _text_string.end(); ++si) {
241 const TextCharacter &tch = (*si);
242 if (tch._graphic ==
nullptr) {
243 wtext += tch._character;
266 TextBlock::const_iterator bi;
267 for (bi = _text_block.begin(); bi != _text_block.end(); ++bi) {
268 const TextRow &row = (*bi);
269 if (bi != _text_block.begin()) {
273 TextString::const_iterator si;
274 for (si = row._string.begin(); si != row._string.end(); ++si) {
275 const TextCharacter &tch = (*si);
276 if (tch._graphic ==
nullptr) {
277 wtext += tch._character;
297 PT(ComputedProperties) current_cprops = _initial_cprops;
299 TextString::const_iterator si;
300 for (si = _text_string.begin(); si != _text_string.end(); ++si) {
301 const TextCharacter &tch = (*si);
302 current_cprops->append_delta(wtext, tch._cprops);
303 if (tch._graphic ==
nullptr) {
304 wtext += tch._character;
306 wtext.push_back(text_embed_graphic_key);
307 wtext += tch._graphic_wname;
308 wtext.push_back(text_embed_graphic_key);
310 current_cprops = tch._cprops;
312 current_cprops->append_delta(wtext, _initial_cprops);
334 PT(ComputedProperties) current_cprops = _initial_cprops;
336 TextBlock::const_iterator bi;
337 for (bi = _text_block.begin(); bi != _text_block.end(); ++bi) {
338 const TextRow &row = (*bi);
339 if (bi != _text_block.begin()) {
340 current_cprops->append_delta(wtext, _initial_cprops);
341 current_cprops = _initial_cprops;
345 TextString::const_iterator si;
346 for (si = row._string.begin(); si != row._string.end(); ++si) {
347 const TextCharacter &tch = (*si);
348 current_cprops->append_delta(wtext, tch._cprops);
349 if (tch._graphic ==
nullptr) {
350 wtext += tch._character;
352 wtext.push_back(text_embed_graphic_key);
353 wtext += tch._graphic_wname;
354 wtext.push_back(text_embed_graphic_key);
356 current_cprops = tch._cprops;
359 current_cprops->append_delta(wtext, _initial_cprops);
374 calc_r_c(
int &r,
int &c,
int n)
const {
375 nassertr(n >= 0 && n <= (
int)_text_string.size(),
false);
377 if (n == (
int)_text_string.size()) {
379 if (_text_string.empty()) {
383 r = _text_block.size() - 1;
384 c = _text_block[r]._string.size();
396 while (r + 1 < (
int)_text_block.size() &&
397 _text_block[r + 1]._row_start < n) {
401 const TextRow &row = _text_block[r];
402 bool is_real_char =
true;
404 nassertr(n > 0,
false);
405 if (row._got_soft_hyphens) {
409 int i = row._row_start;
411 if (_text_string[i]._character != text_soft_hyphen_key &&
412 _text_string[i]._character != text_soft_break_key) {
417 if (_text_string[n - 1]._character != text_soft_hyphen_key &&
418 _text_string[n - 1]._character != text_soft_break_key) {
420 if (_text_string[n - 1]._character ==
'\n') {
421 is_real_char =
false;
424 is_real_char =
false;
429 c = min(n - row._row_start, (
int)row._string.size());
430 if (_text_string[n - 1]._character ==
'\n') {
431 is_real_char =
false;
448 nassertr(r >= 0 && r <= (
int)_text_block.size(), 0);
449 if (r == (
int)_text_block.size()) {
451 return _text_string.size();
454 nassertr(c >= 0 && c <= (
int)_text_block[r]._string.size(), 0);
455 const TextRow &row = _text_block[r];
457 if (row._got_soft_hyphens) {
460 int n = row._row_start;
462 if (_text_string[n]._character != text_soft_hyphen_key &&
463 _text_string[n]._character != text_soft_break_key) {
472 return row._row_start + c;
487 nassertr(r >= 0 && r <= (
int)_text_block.size(), 0.0f);
488 if (r == (
int)_text_block.size()) {
489 nassertr(c == 0, 0.0f);
493 nassertr(c >= 0 && c <= (
int)_text_block[r]._string.size(), 0.0f);
494 const TextRow &row = _text_block[r];
495 PN_stdfloat xpos = row._xpos;
496 for (
int i = 0; i < c; ++i) {
512 PlacedGlyphs placed_glyphs;
513 assemble_paragraph(placed_glyphs);
521 shadow_node->add_child(shadow_geom_node);
525 text_node->add_child(text_geom_node);
532 bool any_shadow =
false;
534 GeomCollectorMap geom_collector_map;
535 GeomCollectorMap geom_shadow_collector_map;
537 QuadMap quad_shadow_map;
539 PlacedGlyphs::const_iterator pgi;
540 for (pgi = placed_glyphs.begin(); pgi != placed_glyphs.end(); ++pgi) {
541 const GlyphPlacement &placement = (*pgi);
543 if (placement._properties != properties) {
545 properties = placement._properties;
548 if (properties->has_shadow()) {
553 shadow_state.clear();
557 if (!placement._glyph.is_null()) {
558 if (properties->has_shadow()) {
559 if (_dynamic_merge) {
560 if (placement._glyph->has_quad()) {
561 placement.assign_quad_to(quad_shadow_map, shadow_state, shadow);
563 placement.assign_append_to(geom_shadow_collector_map, shadow_state, shadow);
566 placement.assign_to(shadow_geom_node, shadow_state, shadow);
576 if (_dynamic_merge) {
577 if (placement._glyph->has_quad()) {
578 placement.assign_quad_to(quad_map, text_state);
580 placement.assign_append_to(geom_collector_map, text_state);
583 placement.assign_to(text_geom_node, text_state);
586 placement.copy_graphic_to(text_node, text_state);
588 placed_glyphs.clear();
593 parent_node->add_child(shadow_node);
596 GeomCollectorMap::iterator gc;
597 for (gc = geom_collector_map.begin(); gc != geom_collector_map.end(); ++gc) {
598 (*gc).second.append_geom(text_geom_node, (*gc).first._state);
601 generate_quads(text_geom_node, quad_map);
604 for (gc = geom_shadow_collector_map.begin();
605 gc != geom_shadow_collector_map.end();
607 (*gc).second.append_geom(shadow_geom_node, (*gc).first._state);
610 generate_quads(shadow_geom_node, quad_shadow_map);
613 parent_node->add_child(text_node);
627 if (character ==
' ') {
630 nassertr(font !=
nullptr, 0.0f);
637 UnicodeLatinMap::AccentType accent_type;
638 int additional_flags;
639 PN_stdfloat glyph_scale;
640 PN_stdfloat advance_scale;
641 get_character_glyphs(character, &properties,
642 got_glyph, first_glyph, second_glyph, accent_type,
643 additional_flags, glyph_scale, advance_scale);
645 PN_stdfloat advance = 0.0f;
647 if (first_glyph !=
nullptr) {
648 advance = first_glyph->get_advance() * advance_scale;
650 if (second_glyph !=
nullptr) {
651 advance += second_glyph->get_advance();
656 return advance * glyph_scale;
682 if (character ==
' ' || character ==
'\n') {
689 nassertr(font !=
nullptr,
false);
692 return font->get_glyph(character, glyph);
706 if (character ==
' ' || character ==
'\n') {
715 UnicodeLatinMap::AccentType accent_type;
716 int additional_flags;
717 PN_stdfloat glyph_scale;
718 PN_stdfloat advance_scale;
719 get_character_glyphs(character, &properties,
720 got_glyph, first_glyph, second_glyph, accent_type,
721 additional_flags, glyph_scale, advance_scale);
741 if (character ==
' ' || character ==
'\n') {
748 nassertr(font !=
nullptr,
false);
751 if (!font->get_glyph(character, glyph)) {
755 return glyph->is_whitespace();
765 wstring::const_iterator &si,
766 const wstring::const_iterator &send,
767 TextAssembler::ComputedProperties *current_cprops) {
769 if ((*si) == text_push_properties_key) {
775 while (si != send && (*si) != text_push_properties_key) {
783 <<
"Unclosed push_properties in text.\n";
790 PT(ComputedProperties) new_cprops =
791 new ComputedProperties(current_cprops, wname, _encoder);
794 scan_wtext(output_string, si, send, new_cprops);
796 if (text_cat.is_debug()) {
802 <<
"push_properties not matched by pop_properties.\n";
806 }
else if ((*si) == text_pop_properties_key) {
812 }
else if ((*si) == text_embed_graphic_key) {
817 wstring graphic_wname;
819 while (si != send && (*si) != text_embed_graphic_key) {
820 graphic_wname += (*si);
827 <<
"Unclosed embed_graphic in text.\n";
835 std::string graphic_name = _encoder->
encode_wtext(graphic_wname);
842 if (named_graphic !=
nullptr) {
843 output_string.push_back(TextCharacter(named_graphic, graphic_wname, current_cprops));
847 <<
"Unknown TextGraphic: " << graphic_name <<
"\n";
852 output_string.push_back(TextCharacter(*si, current_cprops));
874 if (_text_string.empty()) {
881 _text_block.push_back(TextRow(p));
884 PN_stdfloat initial_width = 0.0f;
885 while (p < _text_string.size() && isspacew(_text_string[p]._character)) {
886 if (_text_string[p]._character ==
'\n') {
887 initial_width = 0.0f;
888 if (_max_rows > 0 && (
int)_text_block.size() >= _max_rows) {
892 _text_block.back()._eol_cprops = _text_string[p]._cprops;
893 _text_block.push_back(TextRow(p + 1));
896 _text_block.back()._string.push_back(_text_string[p]);
900 bool needs_newline =
false;
902 while (p < _text_string.size()) {
903 nassertr(!isspacew(_text_string[p]._character),
false);
909 bool any_spaces =
false;
910 size_t last_space = 0;
911 PN_stdfloat last_space_width = 0.0f;
913 bool any_hyphens =
false;
914 size_t last_hyphen = 0;
915 bool output_hyphen =
false;
917 bool overflow =
false;
918 PN_stdfloat wordwrap_width = -1.0f;
920 bool last_was_space =
false;
921 PN_stdfloat width = initial_width;
922 while (q < _text_string.size() && _text_string[q]._character !=
'\n') {
923 if (_text_string[q]._cprops->_properties.has_wordwrap()) {
924 wordwrap_width = _text_string[q]._cprops->_properties.get_wordwrap();
926 wordwrap_width = -1.0f;
929 if (isspacew(_text_string[q]._character) ||
930 _text_string[q]._character == text_soft_break_key) {
931 if (!last_was_space) {
938 last_space_width = width;
939 last_was_space =
true;
942 last_was_space =
false;
947 if (_text_string[q]._character == text_soft_hyphen_key) {
948 if (wordwrap_width > 0.0f) {
952 if (q != p && width + calc_hyphen_width(_text_string[q]) <= wordwrap_width) {
964 if (wordwrap_width > 0.0f && width > wordwrap_width) {
976 nassertr(wordwrap_width > 0.0f,
false);
978 if (any_spaces && last_space_width / wordwrap_width >= text_hyphen_ratio) {
987 output_hyphen =
true;
989 }
else if (any_spaces) {
998 while ((
int)i < text_max_never_break && q - i > p &&
999 get_text_never_break_before().find(_text_string[q - i]._character) != wstring::npos) {
1002 if ((
int)i < text_max_never_break) {
1009 size_t next_start = q;
1010 while (next_start < _text_string.size() &&
1011 isbreakpoint(_text_string[next_start]._character)) {
1016 while (q > p && isspacew(_text_string[q - 1]._character)) {
1020 if (next_start == p) {
1025 if (initial_width == 0.0f) {
1031 while (next_start < _text_string.size() &&
1032 isbreakpoint(_text_string[next_start]._character)) {
1038 if (needs_newline) {
1039 if (_max_rows > 0 && (
int)_text_block.size() >= _max_rows) {
1043 _text_block.push_back(TextRow(p));
1046 needs_newline =
true;
1049 if (_text_string[next_start - 1]._cprops->_properties.get_preserve_trailing_whitespace()) {
1053 for (
size_t pi = p; pi < q; pi++) {
1054 if (_text_string[pi]._character != text_soft_hyphen_key &&
1055 _text_string[pi]._character != text_soft_break_key) {
1056 _text_block.back()._string.push_back(_text_string[pi]);
1058 _text_block.back()._got_soft_hyphens =
true;
1061 if (output_hyphen) {
1062 wstring text_soft_hyphen_output = get_text_soft_hyphen_output();
1063 wstring::const_iterator wi;
1064 for (wi = text_soft_hyphen_output.begin();
1065 wi != text_soft_hyphen_output.end();
1067 _text_block.back()._string.push_back(TextCharacter(*wi, _text_string[last_hyphen]._cprops));
1073 if (next_start < _text_string.size() && _text_string[next_start]._character ==
'\n') {
1075 if (_max_rows > 0 && (
int)_text_block.size() >= _max_rows) {
1079 _text_block.back()._eol_cprops = _text_string[next_start]._cprops;
1081 _text_block.push_back(TextRow(next_start));
1082 needs_newline =
false;
1087 initial_width = 0.0f;
1088 while (p < _text_string.size() && isspacew(_text_string[p]._character)) {
1089 if (_text_string[p]._character ==
'\n') {
1090 initial_width = 0.0f;
1091 if (_max_rows > 0 && (
int)_text_block.size() >= _max_rows) {
1095 _text_block.back()._eol_cprops = _text_string[p]._cprops;
1096 _text_block.push_back(TextRow(p + 1));
1098 initial_width +=
calc_width(_text_string[p]);
1099 _text_block.back()._string.push_back(_text_string[p]);
1112 PN_stdfloat TextAssembler::
1113 calc_hyphen_width(
const TextCharacter &tch) {
1114 TextFont *font = tch._cprops->_properties.get_font();
1115 nassertr(font !=
nullptr, 0.0f);
1117 PN_stdfloat hyphen_width = 0.0f;
1118 wstring text_soft_hyphen_output = get_text_soft_hyphen_output();
1119 wstring::const_iterator wi;
1120 for (wi = text_soft_hyphen_output.begin();
1121 wi != text_soft_hyphen_output.end();
1123 hyphen_width +=
calc_width(*wi, tch._cprops->_properties);
1126 return hyphen_width;
1132 void TextAssembler::
1133 generate_quads(
GeomNode *geom_node,
const QuadMap &quad_map) {
1134 QuadMap::const_iterator qmi;
1135 for (qmi = quad_map.begin(); qmi != quad_map.end(); ++qmi) {
1136 const QuadDefs &quads = qmi->second;
1138 glyphs.reserve(quads.size());
1150 vtx_handle->unclean_set_num_rows(quads.size() * 4);
1152 unsigned char *write_ptr = vtx_handle->get_write_pointer();
1156 size_t stride = format->
get_array(0)->get_stride() /
sizeof(PN_float32);
1158 PN_float32 *vtx_ptr = (PN_float32 *)
1159 (write_ptr + format->
get_column(InternalName::get_vertex())->get_start());
1160 PN_float32 *tex_ptr = (PN_float32 *)
1161 (write_ptr + format->
get_column(InternalName::get_texcoord())->get_start());
1163 for (
const QuadDef &quad : quads) {
1164 vtx_ptr[0] = quad._dimensions[0] + quad._slanth;
1166 vtx_ptr[2] = quad._dimensions[3];
1169 tex_ptr[0] = quad._uvs[0];
1170 tex_ptr[1] = quad._uvs[3];
1173 vtx_ptr[0] = quad._dimensions[0] + quad._slantl;
1175 vtx_ptr[2] = quad._dimensions[1];
1178 tex_ptr[0] = quad._uvs[0];
1179 tex_ptr[1] = quad._uvs[1];
1182 vtx_ptr[0] = quad._dimensions[2] + quad._slanth;
1184 vtx_ptr[2] = quad._dimensions[3];
1187 tex_ptr[0] = quad._uvs[2];
1188 tex_ptr[1] = quad._uvs[3];
1191 vtx_ptr[0] = quad._dimensions[2] + quad._slantl;
1193 vtx_ptr[2] = quad._dimensions[1];
1196 tex_ptr[0] = quad._uvs[2];
1197 tex_ptr[1] = quad._uvs[1];
1200 glyphs.push_back(move(quad._glyph));
1204 size_t stride = format->
get_array(0)->get_stride() /
sizeof(PN_float64);
1206 PN_float64 *vtx_ptr = (PN_float64 *)
1207 (write_ptr + format->
get_column(InternalName::get_vertex())->get_start());
1208 PN_float64 *tex_ptr = (PN_float64 *)
1209 (write_ptr + format->
get_column(InternalName::get_texcoord())->get_start());
1211 for (
const QuadDef &quad : quads) {
1212 vtx_ptr[0] = quad._dimensions[0] + quad._slanth;
1214 vtx_ptr[2] = quad._dimensions[3];
1217 tex_ptr[0] = quad._uvs[0];
1218 tex_ptr[1] = quad._uvs[3];
1221 vtx_ptr[0] = quad._dimensions[0] + quad._slantl;
1223 vtx_ptr[2] = quad._dimensions[1];
1226 tex_ptr[0] = quad._uvs[0];
1227 tex_ptr[1] = quad._uvs[1];
1230 vtx_ptr[0] = quad._dimensions[2] + quad._slanth;
1232 vtx_ptr[2] = quad._dimensions[3];
1235 tex_ptr[0] = quad._uvs[2];
1236 tex_ptr[1] = quad._uvs[3];
1239 vtx_ptr[0] = quad._dimensions[2] + quad._slantl;
1241 vtx_ptr[2] = quad._dimensions[1];
1244 tex_ptr[0] = quad._uvs[2];
1245 tex_ptr[1] = quad._uvs[1];
1248 glyphs.push_back(move(quad._glyph));
1254 int vtx_count = quads.size() * 4;
1256 if (vtx_count > 65535) {
1257 tris->set_index_type(GeomEnums::NT_uint32);
1259 tris->set_index_type(GeomEnums::NT_uint16);
1263 idx_handle->unclean_set_num_rows(quads.size() * 6);
1264 if (tris->get_index_type() == GeomEnums::NT_uint16) {
1266 uint16_t *idx_ptr = (uint16_t *)idx_handle->get_write_pointer();
1268 for (
int i = 0; i < vtx_count; i += 4) {
1269 *(idx_ptr++) = i + 0;
1270 *(idx_ptr++) = i + 1;
1271 *(idx_ptr++) = i + 2;
1272 *(idx_ptr++) = i + 2;
1273 *(idx_ptr++) = i + 1;
1274 *(idx_ptr++) = i + 3;
1278 uint32_t *idx_ptr = (uint32_t *)idx_handle->get_write_pointer();
1280 for (
int i = 0; i < vtx_count; i += 4) {
1281 *(idx_ptr++) = i + 0;
1282 *(idx_ptr++) = i + 1;
1283 *(idx_ptr++) = i + 2;
1284 *(idx_ptr++) = i + 2;
1285 *(idx_ptr++) = i + 1;
1286 *(idx_ptr++) = i + 3;
1292 tris->set_minmax(0, vtx_count - 1,
nullptr,
nullptr);
1295 geom->_glyphs.swap(glyphs);
1296 geom->add_primitive(tris);
1297 geom_node->
add_geom(geom, qmi->first);
1305 void TextAssembler::
1307 _ul.set(0.0f, 0.0f);
1308 _lr.set(0.0f, 0.0f);
1311 PN_stdfloat ypos = 0.0f;
1312 _next_row_ypos = 0.0f;
1313 TextBlock::iterator bi;
1314 for (bi = _text_block.begin(); bi != _text_block.end(); ++bi) {
1315 TextRow &row = (*bi);
1318 size_t first_glyph = placed_glyphs.size();
1321 PN_stdfloat row_width, line_height, wordwrap;
1322 TextProperties::Alignment align;
1323 assemble_row(row, placed_glyphs,
1324 row_width, line_height, align, wordwrap);
1328 if (num_rows == 0) {
1330 _ul[1] = 0.8f * line_height;
1335 ypos -= line_height;
1337 _lr[1] = ypos - 0.2 * line_height;
1344 PN_stdfloat xpos = 0;
1346 case TextProperties::A_left:
1347 _lr[0] = max(_lr[0], row_width);
1350 case TextProperties::A_right:
1352 _ul[0] = min(_ul[0], xpos);
1355 case TextProperties::A_center:
1356 xpos = -0.5f * row_width;
1357 _ul[0] = min(_ul[0], xpos);
1358 _lr[0] = max(_lr[0], -xpos);
1361 case TextProperties::A_boxed_left:
1362 _lr[0] = max(_lr[0], max(row_width, wordwrap));
1365 case TextProperties::A_boxed_right:
1366 xpos = wordwrap - row_width;
1367 _ul[0] = min(_ul[0], xpos);
1370 case TextProperties::A_boxed_center:
1371 xpos = -0.5f * row_width;
1372 if (wordwrap > row_width) xpos += (wordwrap * 0.5f);
1373 _ul[0] = min(_ul[0], max(xpos,(wordwrap * 0.5f)));
1374 _lr[0] = max(_lr[0], min(-xpos,-(wordwrap * 0.5f)));
1382 for (
size_t i = first_glyph; i < placed_glyphs.size(); ++i) {
1383 placed_glyphs[i]._xpos += xpos;
1384 placed_glyphs[i]._ypos += ypos;
1389 _next_row_ypos = ypos - line_height;
1403 void TextAssembler::
1404 assemble_row(TextAssembler::TextRow &row,
1406 PN_stdfloat &row_width, PN_stdfloat &line_height,
1407 TextProperties::Alignment &align, PN_stdfloat &wordwrap) {
1411 PN_stdfloat xpos = 0.0f;
1412 align = TextProperties::A_left;
1417 bool underscore =
false;
1418 PN_stdfloat underscore_start = 0.0f;
1421 #ifdef HAVE_HARFBUZZ
1422 const ComputedProperties *prev_cprops =
nullptr;
1423 hb_buffer_t *harfbuff =
nullptr;
1426 TextString::const_iterator si;
1427 for (si = row._string.begin(); si != row._string.end(); ++si) {
1428 const TextCharacter &tch = (*si);
1429 wchar_t character = tch._character;
1434 (underscore && (properties->get_text_color() != underscore_properties->get_text_color() ||
1437 if (underscore && underscore_start != xpos) {
1438 draw_underscore(placed_glyphs, underscore_start, xpos,
1439 underscore_properties);
1442 underscore_start = xpos;
1443 underscore_properties = properties;
1447 nassertv(font !=
nullptr);
1450 if ((align == TextProperties::A_left) &&
1451 (properties->get_align() != TextProperties::A_left)) {
1452 align = properties->get_align();
1456 if (properties->get_wordwrap() > 0.0f) {
1457 wordwrap = properties->get_wordwrap();
1462 if (graphic !=
nullptr) {
1464 line_height = max(line_height, frame[3] - frame[2]);
1469 #ifdef HAVE_HARFBUZZ
1470 if (tch._cprops != prev_cprops || graphic !=
nullptr) {
1471 if (harfbuff !=
nullptr && hb_buffer_get_length(harfbuff) > 0) {
1473 shape_buffer(harfbuff, placed_glyphs, xpos, prev_cprops->_properties);
1474 hb_buffer_reset(harfbuff);
1476 }
else if (harfbuff ==
nullptr && text_use_harfbuzz &&
1477 font->
is_of_type(DynamicTextFont::get_class_type())) {
1478 harfbuff = hb_buffer_create();
1480 prev_cprops = tch._cprops;
1483 if (graphic ==
nullptr && harfbuff !=
nullptr) {
1484 hb_buffer_add(harfbuff, character, character);
1489 if (character ==
' ') {
1494 }
else if (character ==
'\t') {
1497 xpos = (floor(xpos / tab_width) + 1.0f) * tab_width;
1500 }
else if (character == text_soft_hyphen_key) {
1503 }
else if (graphic !=
nullptr) {
1505 GlyphPlacement placement;
1512 model_node->set_preserve_transform(ModelNode::PT_no_touch);
1513 model_node->add_child(model);
1514 placement._graphic_model = model_node.p();
1518 placement._graphic_model = model->copy_subgraph();
1524 PN_stdfloat advance = (frame[1] - frame[0]);
1529 placement._xpos = (xpos - frame[0]);
1531 placement._slant = properties->
get_slant();
1532 placement._properties = properties;
1534 placed_glyphs.push_back(placement);
1536 xpos += advance * glyph_scale;
1544 UnicodeLatinMap::AccentType accent_type;
1545 int additional_flags;
1546 PN_stdfloat glyph_scale;
1547 PN_stdfloat advance_scale;
1548 get_character_glyphs(character, properties,
1549 got_glyph, first_glyph, second_glyph, accent_type,
1550 additional_flags, glyph_scale, advance_scale);
1554 sprintf(buffer,
"U+%04x", character);
1556 <<
"No definition in " << font->get_name()
1557 <<
" for character " << buffer;
1558 if (character < 128 && isprint((
unsigned int)character)) {
1559 text_cat.warning(
false)
1560 <<
" ('" << (char)character <<
"')";
1562 text_cat.warning(
false)
1570 if (prev_char != -1) {
1571 xpos += font->
get_kerning(prev_char, character) * glyph_scale;
1573 prev_char = character;
1580 GlyphPlacement placement;
1582 placement._glyph =
nullptr;
1583 placement._scale = glyph_scale;
1584 placement._xpos = xpos;
1586 placement._slant = properties->
get_slant();
1587 placement._properties = properties;
1589 PN_stdfloat advance = 0.0f;
1591 if (accent_type != UnicodeLatinMap::AT_none || additional_flags != 0) {
1594 LPoint3 min_vert, max_vert;
1595 bool found_any =
false;
1596 if (first_glyph !=
nullptr) {
1597 first_glyph->calc_tight_bounds(min_vert, max_vert, found_any,
1600 if (second_glyph !=
nullptr) {
1601 second_glyph->calc_tight_bounds(min_vert, max_vert, found_any,
1606 LPoint3 centroid = (min_vert + max_vert) / 2.0f;
1608 if ((additional_flags & UnicodeLatinMap::AF_turned) != 0) {
1619 placement._scale *= -1;
1620 placement._xpos += centroid[0] * 2;
1621 placement._ypos += centroid[2] * 2;
1624 if (accent_type != UnicodeLatinMap::AT_none) {
1625 GlyphPlacement accent_placement(placement);
1626 tack_on_accent(accent_type, min_vert, max_vert, centroid,
1627 properties, accent_placement);
1628 placed_glyphs.push_back(accent_placement);
1633 if (first_glyph !=
nullptr) {
1634 advance = first_glyph->get_advance() * advance_scale;
1635 if (!first_glyph->is_whitespace()) {
1636 std::swap(placement._glyph, first_glyph);
1637 placed_glyphs.push_back(placement);
1643 if (second_glyph !=
nullptr) {
1644 placement._xpos += advance * glyph_scale;
1645 advance += second_glyph->get_advance();
1646 std::swap(placement._glyph, second_glyph);
1647 placed_glyphs.push_back(placement);
1650 xpos += advance * glyph_scale;
1654 #ifdef HAVE_HARFBUZZ
1655 if (harfbuff !=
nullptr && hb_buffer_get_length(harfbuff) > 0) {
1656 shape_buffer(harfbuff, placed_glyphs, xpos, prev_cprops->_properties);
1658 hb_buffer_destroy(harfbuff);
1661 if (underscore && underscore_start != xpos) {
1662 draw_underscore(placed_glyphs, underscore_start, xpos,
1663 underscore_properties);
1668 if (row._eol_cprops !=
nullptr) {
1673 const TextProperties *properties = &(row._eol_cprops->_properties);
1675 nassertv(font !=
nullptr);
1677 if (line_height == 0.0f) {
1679 line_height = max(line_height, font->
get_line_height() * glyph_scale);
1687 void TextAssembler::
1688 shape_buffer(hb_buffer_t *buf, PlacedGlyphs &placed_glyphs, PN_stdfloat &xpos,
1691 #ifdef HAVE_HARFBUZZ
1694 hb_direction_t direction = HB_DIRECTION_INVALID;
1697 case TextProperties::D_ltr:
1698 direction = HB_DIRECTION_LTR;
1700 case TextProperties::D_rtl:
1701 direction = HB_DIRECTION_RTL;
1705 hb_buffer_set_content_type(buf, HB_BUFFER_CONTENT_TYPE_UNICODE);
1706 hb_buffer_set_direction(buf, direction);
1707 hb_buffer_guess_segment_properties(buf);
1709 DynamicTextFont *font = DCAST(DynamicTextFont, properties.
get_font());
1710 hb_font_t *hb_font = font->get_hb_font();
1711 hb_shape(hb_font, buf,
nullptr, 0);
1714 PN_stdfloat scale = glyph_scale / (font->get_pixels_per_unit() * font->get_scale_factor() * 64.0);
1716 unsigned int glyph_count;
1717 hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count);
1718 hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count);
1720 for (
unsigned int i = 0; i < glyph_count; ++i) {
1721 int character = glyph_info[i].cluster;
1722 int glyph_index = glyph_info[i].codepoint;
1725 if (!font->get_glyph_by_index(character, glyph_index, glyph)) {
1727 sprintf(buffer,
"U+%04x", character);
1729 <<
"No definition in " << font->get_name()
1730 <<
" for character " << buffer;
1731 if (character < 128 && isprint((
unsigned int)character)) {
1732 text_cat.warning(
false)
1733 <<
" ('" << (char)character <<
"')";
1735 text_cat.warning(
false)
1739 PN_stdfloat advance = glyph_pos[i].x_advance * scale;
1740 if (glyph->is_whitespace()) {
1746 PN_stdfloat x_offset = glyph_pos[i].x_offset * scale;
1747 PN_stdfloat y_offset = glyph_pos[i].y_offset * scale;
1753 GlyphPlacement placement;
1754 placement._glyph = move(glyph);
1755 placement._scale = glyph_scale;
1756 placement._xpos = xpos + x_offset;
1758 placement._slant = properties.
get_slant();
1759 placement._properties = &properties;
1760 placed_glyphs.push_back(placement);
1771 void TextAssembler::
1773 PN_stdfloat underscore_start, PN_stdfloat underscore_end,
1779 vdata->unclean_set_num_rows(2);
1784 vertex.set_data3(underscore_start, 0.0f, y);
1785 color.set_data4(underscore_properties->get_text_color());
1786 vertex.set_data3(underscore_end, 0.0f, y);
1787 color.set_data4(underscore_properties->get_text_color());
1790 lines->add_next_vertices(2);
1791 lines->close_primitive();
1794 geom->add_primitive(lines);
1803 GlyphPlacement placement;
1804 placement._glyph = move(glyph);
1805 placement._xpos = 0;
1806 placement._ypos = 0;
1807 placement._scale = 1;
1808 placement._slant = 0;
1809 placement._properties = underscore_properties;
1810 placed_glyphs.push_back(placement);
1825 void TextAssembler::
1826 get_character_glyphs(
int character,
const TextProperties *properties,
1829 UnicodeLatinMap::AccentType &accent_type,
1830 int &additional_flags,
1831 PN_stdfloat &glyph_scale, PN_stdfloat &advance_scale) {
1833 nassertv_always(font !=
nullptr);
1837 second_glyph =
nullptr;
1838 accent_type = UnicodeLatinMap::AT_none;
1839 additional_flags = 0;
1841 advance_scale = 1.0f;
1847 if (map_entry !=
nullptr) {
1849 map_entry->_toupper_character != (char32_t)character) {
1850 character = map_entry->_toupper_character;
1856 got_glyph = font->get_glyph(character, glyph);
1857 if (!got_glyph && map_entry !=
nullptr && map_entry->_ascii_equiv != 0) {
1860 if (map_entry->_ascii_equiv ==
'i') {
1862 got_glyph = font->get_glyph(0x0131, glyph) ||
1863 font->get_glyph(
'i', glyph);
1865 }
else if (map_entry->_ascii_equiv ==
'j') {
1867 got_glyph = font->get_glyph(0x0237, glyph) ||
1868 font->get_glyph(
'j', glyph);
1871 got_glyph = font->get_glyph(map_entry->_ascii_equiv, glyph);
1874 if (!got_glyph && map_entry->_toupper_character != (char32_t)character) {
1876 character = map_entry->_toupper_character;
1878 if (map_entry !=
nullptr) {
1879 got_glyph = font->get_glyph(map_entry->_ascii_equiv, glyph);
1884 accent_type = map_entry->_accent_type;
1885 additional_flags = map_entry->_additional_flags;
1887 bool got_second_glyph =
false;
1888 if (map_entry->_ascii_additional != 0) {
1891 font->get_glyph(map_entry->_ascii_additional, second_glyph);
1894 if ((additional_flags & UnicodeLatinMap::AF_ligature) != 0 &&
1899 advance_scale = ligature_advance_scale;
1902 if ((additional_flags & UnicodeLatinMap::AF_smallcap) != 0) {
1914 void TextAssembler::
1915 tack_on_accent(UnicodeLatinMap::AccentType accent_type,
1916 const LPoint3 &min_vert,
const LPoint3 &max_vert,
1917 const LPoint3 ¢roid,
1919 TextAssembler::GlyphPlacement &placement)
const {
1923 if (combine_char != 0 &&
1924 tack_on_accent(combine_char, CP_above, CT_none, min_vert, max_vert,
1925 centroid, properties, placement)) {
1930 switch (accent_type) {
1931 case UnicodeLatinMap::AT_grave:
1936 tack_on_accent(
'/', CP_above, CT_small_squash_mirror_y, min_vert, max_vert, centroid,
1937 properties, placement);
1940 case UnicodeLatinMap::AT_acute:
1941 tack_on_accent(
'/', CP_above, CT_small_squash, min_vert, max_vert, centroid,
1942 properties, placement);
1945 case UnicodeLatinMap::AT_breve:
1946 tack_on_accent(
')', CP_above, CT_tiny_rotate_270, min_vert, max_vert, centroid,
1947 properties, placement);
1950 case UnicodeLatinMap::AT_inverted_breve:
1951 tack_on_accent(
'(', CP_above, CT_tiny_rotate_270, min_vert, max_vert, centroid,
1952 properties, placement);
1955 case UnicodeLatinMap::AT_circumflex:
1956 tack_on_accent(
'^', CP_above, CT_none, min_vert, max_vert, centroid,
1957 properties, placement) ||
1958 tack_on_accent(
'v', CP_above, CT_squash_mirror_y, min_vert, max_vert, centroid,
1959 properties, placement);
1962 case UnicodeLatinMap::AT_circumflex_below:
1963 tack_on_accent(
'^', CP_below, CT_none, min_vert, max_vert, centroid,
1964 properties, placement) ||
1965 tack_on_accent(
'v', CP_below, CT_squash_mirror_y, min_vert, max_vert, centroid,
1966 properties, placement);
1969 case UnicodeLatinMap::AT_caron:
1970 tack_on_accent(
'^', CP_above, CT_mirror_y, min_vert, max_vert, centroid,
1971 properties, placement) ||
1972 tack_on_accent(
'v', CP_above, CT_squash, min_vert, max_vert, centroid,
1973 properties, placement);
1977 case UnicodeLatinMap::AT_tilde:
1978 tack_on_accent(
'~', CP_above, CT_none, min_vert, max_vert, centroid,
1979 properties, placement) ||
1980 tack_on_accent(
's', CP_above, CT_squash_mirror_diag, min_vert, max_vert, centroid,
1981 properties, placement);
1985 case UnicodeLatinMap::AT_tilde_below:
1986 tack_on_accent(
'~', CP_below, CT_none, min_vert, max_vert, centroid,
1987 properties, placement) ||
1988 tack_on_accent(
's', CP_below, CT_squash_mirror_diag, min_vert, max_vert, centroid,
1989 properties, placement);
1992 case UnicodeLatinMap::AT_diaeresis:
1993 tack_on_accent(
':', CP_above, CT_small_rotate_270, min_vert, max_vert, centroid,
1994 properties, placement);
1997 case UnicodeLatinMap::AT_diaeresis_below:
1998 tack_on_accent(
':', CP_below, CT_small_rotate_270, min_vert, max_vert, centroid,
1999 properties, placement);
2002 case UnicodeLatinMap::AT_dot_above:
2003 tack_on_accent(
'.', CP_above, CT_none, min_vert, max_vert, centroid,
2004 properties, placement);
2007 case UnicodeLatinMap::AT_dot_below:
2008 tack_on_accent(
'.', CP_below, CT_none, min_vert, max_vert, centroid,
2009 properties, placement);
2012 case UnicodeLatinMap::AT_macron:
2013 tack_on_accent(
'-', CP_above, CT_none, min_vert, max_vert, centroid,
2014 properties, placement);
2017 case UnicodeLatinMap::AT_line_below:
2018 tack_on_accent(
'-', CP_below, CT_none, min_vert, max_vert, centroid,
2019 properties, placement);
2022 case UnicodeLatinMap::AT_ring_above:
2023 tack_on_accent(
'o', CP_top, CT_tiny, min_vert, max_vert, centroid,
2024 properties, placement);
2027 case UnicodeLatinMap::AT_ring_below:
2028 tack_on_accent(
'o', CP_bottom, CT_tiny, min_vert, max_vert, centroid,
2029 properties, placement);
2032 case UnicodeLatinMap::AT_cedilla:
2033 tack_on_accent(0xb8, CP_below, CT_none, min_vert, max_vert, centroid,
2034 properties, placement) ||
2035 tack_on_accent(
'c', CP_bottom, CT_tiny_mirror_x, min_vert, max_vert, centroid,
2036 properties, placement);
2041 case UnicodeLatinMap::AT_comma_below:
2042 tack_on_accent(
',', CP_below, CT_none, min_vert, max_vert, centroid,
2043 properties, placement);
2046 case UnicodeLatinMap::AT_ogonek:
2047 tack_on_accent(
',', CP_bottom, CT_mirror_x, min_vert, max_vert, centroid,
2048 properties, placement);
2051 case UnicodeLatinMap::AT_stroke:
2052 tack_on_accent(
'/', CP_within, CT_none, min_vert, max_vert, centroid,
2053 properties, placement);
2067 bool TextAssembler::
2068 tack_on_accent(
wchar_t accent_mark, TextAssembler::CheesyPosition position,
2069 TextAssembler::CheesyTransform transform,
2070 const LPoint3 &min_vert,
const LPoint3 &max_vert,
2071 const LPoint3 ¢roid,
2073 TextAssembler::GlyphPlacement &placement)
const {
2075 nassertr(font !=
nullptr,
false);
2080 if (font->get_glyph(accent_mark, accent_glyph) ||
2081 font->get_glyph(toupper(accent_mark), accent_glyph)) {
2082 if (!accent_glyph->is_whitespace()) {
2083 LPoint3 min_accent, max_accent;
2084 bool found_any =
false;
2085 accent_glyph->calc_tight_bounds(min_accent, max_accent, found_any,
2089 LMatrix4 accent_mat;
2090 bool has_mat =
true;
2092 switch (transform) {
2098 accent_mat = LMatrix4::scale_mat(-1.0f, -1.0f, 1.0f);
2100 min_accent[0] = -max_accent[0];
2105 accent_mat = LMatrix4::scale_mat(1.0f, -1.0f, -1.0f);
2107 min_accent[2] = -max_accent[2];
2112 accent_mat.set_rotate_mat_normaxis(90.0f, LVecBase3(0.0f, -1.0f, 0.0f));
2116 max_accent[0] = -min_accent[2];
2117 min_accent[0] = -max_accent[2];
2124 placement._scale *= -1;
2126 min_accent[0] = -max_accent[0];
2129 min_accent[2] = -max_accent[2];
2134 accent_mat.set_rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f));
2138 min_accent[0] = min_accent[2];
2139 max_accent[0] = max_accent[2];
2145 accent_mat = LMatrix4::scale_mat(squash_accent_scale_x, 1.0f, squash_accent_scale_y);
2146 min_accent[0] *= squash_accent_scale_x;
2147 max_accent[0] *= squash_accent_scale_x;
2148 min_accent[2] *= squash_accent_scale_y;
2149 max_accent[2] *= squash_accent_scale_y;
2152 case CT_squash_mirror_y:
2153 accent_mat = LMatrix4::scale_mat(squash_accent_scale_x, -1.0f, -squash_accent_scale_y);
2154 min_accent[0] *= squash_accent_scale_x;
2155 max_accent[0] *= squash_accent_scale_x;
2157 min_accent[2] = -max_accent[2] * squash_accent_scale_y;
2158 max_accent[2] = -t * squash_accent_scale_y;
2161 case CT_squash_mirror_diag:
2163 LMatrix4::rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f)) *
2164 LMatrix4::scale_mat(-squash_accent_scale_x, -1.0f, squash_accent_scale_y);
2169 min_accent[0] = min_accent[2] * -squash_accent_scale_x;
2170 max_accent[0] = max_accent[2] * -squash_accent_scale_x;
2171 min_accent[2] = -u * squash_accent_scale_y;
2172 max_accent[2] = -t * squash_accent_scale_y;
2175 case CT_small_squash:
2176 accent_mat = LMatrix4::scale_mat(small_squash_accent_scale_x, 1.0f, small_squash_accent_scale_y);
2177 min_accent[0] *= small_squash_accent_scale_x;
2178 max_accent[0] *= small_squash_accent_scale_x;
2179 min_accent[2] *= small_squash_accent_scale_y;
2180 max_accent[2] *= small_squash_accent_scale_y;
2183 case CT_small_squash_mirror_y:
2184 accent_mat = LMatrix4::scale_mat(small_squash_accent_scale_x, -1.0f, -small_squash_accent_scale_y);
2185 min_accent[0] *= small_squash_accent_scale_x;
2186 max_accent[0] *= small_squash_accent_scale_x;
2188 min_accent[2] = -max_accent[2] * small_squash_accent_scale_y;
2189 max_accent[2] = -t * small_squash_accent_scale_y;
2192 case CT_small_squash_mirror_diag:
2194 LMatrix4::rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f)) *
2195 LMatrix4::scale_mat(-small_squash_accent_scale_x, -1.0f, small_squash_accent_scale_y);
2200 min_accent[0] = min_accent[2] * -small_squash_accent_scale_x;
2201 max_accent[0] = max_accent[2] * -small_squash_accent_scale_x;
2202 min_accent[2] = -u * small_squash_accent_scale_y;
2203 max_accent[2] = -t * small_squash_accent_scale_y;
2208 placement._scale *= small_accent_scale;
2209 min_accent *= small_accent_scale;
2210 max_accent *= small_accent_scale;
2213 case CT_small_rotate_270:
2215 LMatrix4::rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f)) *
2216 LMatrix4::scale_mat(small_accent_scale);
2221 min_accent[0] = min_accent[2] * small_accent_scale;
2222 max_accent[0] = max_accent[2] * small_accent_scale;
2223 min_accent[2] = -u * small_accent_scale;
2224 max_accent[2] = -t * small_accent_scale;
2229 placement._scale *= tiny_accent_scale;
2230 min_accent *= tiny_accent_scale;
2231 max_accent *= tiny_accent_scale;
2234 case CT_tiny_mirror_x:
2235 accent_mat = LMatrix4::scale_mat(-tiny_accent_scale, -1.0f, tiny_accent_scale);
2238 min_accent[0] = -max_accent[0] * tiny_accent_scale;
2239 max_accent[0] = -t * tiny_accent_scale;
2240 min_accent[2] *= tiny_accent_scale;
2241 max_accent[2] *= tiny_accent_scale;
2244 case CT_tiny_rotate_270:
2246 LMatrix4::rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f)) *
2247 LMatrix4::scale_mat(tiny_accent_scale);
2252 min_accent[0] = min_accent[2] * tiny_accent_scale;
2253 max_accent[0] = max_accent[2] * tiny_accent_scale;
2254 min_accent[2] = -u * tiny_accent_scale;
2255 max_accent[2] = -t * tiny_accent_scale;
2264 LPoint3 accent_centroid = (min_accent + max_accent) / 2.0f;
2265 PN_stdfloat accent_height = max_accent[2] - min_accent[2] - total_margin * 2;
2266 PN_stdfloat accent_x = centroid[0] - accent_centroid[0];
2267 PN_stdfloat accent_y = 0;
2268 PN_stdfloat min_y = min_vert[2] + total_margin;
2269 PN_stdfloat max_y = max_vert[2] - total_margin;
2274 accent_y = max_y - accent_centroid[2] + accent_height * 0.75f;
2279 accent_y = min_y - accent_centroid[2] - accent_height * 0.75f;
2284 accent_y = max_y - accent_centroid[2];
2289 accent_y = min_y - accent_centroid[2];
2294 accent_y = centroid[2] - accent_centroid[2];
2298 placement._xpos += placement._scale * (accent_x + placement._slant * accent_y);
2299 placement._ypos += placement._scale * accent_y;
2303 PT(
Geom) accent_geom = accent_glyph->get_geom(_usage_hint);
2304 accent_geom->transform_vertices(accent_mat);
2305 placement._glyph =
new TextGlyph(0, accent_geom, accent_glyph->get_state(), 0);
2308 placement._glyph = accent_glyph;
2322 void TextAssembler::ComputedProperties::
2323 append_delta(wstring &wtext, TextAssembler::ComputedProperties *other) {
2324 if (
this != other) {
2325 if (_depth > other->_depth) {
2327 nassertv(_based_on !=
nullptr);
2329 wtext.push_back(text_pop_properties_key);
2330 _based_on->append_delta(wtext, other);
2332 }
else if (other->_depth > _depth) {
2334 nassertv(other->_based_on !=
nullptr);
2336 append_delta(wtext, other->_based_on);
2337 wtext.push_back(text_push_properties_key);
2338 wtext += other->_wname;
2339 wtext.push_back(text_push_properties_key);
2341 }
else if (_depth != 0) {
2343 nassertv(_based_on !=
nullptr && other->_based_on !=
nullptr);
2345 wtext.push_back(text_pop_properties_key);
2346 _based_on->append_delta(wtext, other->_based_on);
2347 wtext.push_back(text_push_properties_key);
2348 wtext += other->_wname;
2349 wtext.push_back(text_push_properties_key);
2358 void TextAssembler::GlyphPlacement::
2360 const LVector2 &offset)
const {
2362 LMatrix4 xform(_scale, 0.0f, 0.0f, 0.0f,
2363 0.0f, 1.0f, 0.0f, 0.0f,
2364 _slant * _scale, 0.0f, _scale, 0.0f,
2365 _xpos + offset[0], 0.0f, _ypos - offset[1], 1.0f);
2367 PT(
Geom) geom = _glyph->get_geom(GeomEnums::UH_static);
2368 geom->transform_vertices(xform);
2369 geom_node->
add_geom(geom, state->compose(_glyph->get_state()));
2377 void TextAssembler::GlyphPlacement::
2378 assign_append_to(GeomCollectorMap &geom_collector_map,
2380 const LVector2 &offset)
const {
2382 LMatrix4 xform(_scale, 0.0f, 0.0f, 0.0f,
2383 0.0f, 1.0f, 0.0f, 0.0f,
2384 _slant * _scale, 0.0f, _scale, 0.0f,
2385 _xpos + offset[0], 0.0f, _ypos - offset[1], 1.0f);
2387 PT(
Geom) geom = _glyph->get_geom(GeomEnums::UH_static);
2392 CPT(
RenderState) rs = _glyph->get_state()->compose(state);
2393 GeomCollectorKey key(rs, vdata->
get_format());
2395 GeomCollectorMap::iterator mi = geom_collector_map.find(key);
2396 if (mi == geom_collector_map.end()) {
2397 mi = geom_collector_map.insert(GeomCollectorMap::value_type(key, GeomCollector(vdata->
get_format()))).first;
2399 GeomCollector &geom_collector = (*mi).second;
2400 geom_collector.count_geom(geom);
2404 VertexIndexMap vimap;
2406 for (
size_t p = 0; p < geom->get_num_primitives(); ++p) {
2407 CPT(
GeomPrimitive) primitive = geom->get_primitive(p)->decompose();
2410 GeomPrimitive *new_prim = geom_collector.get_primitive(primitive->get_type());
2413 for (sp = 0; sp < primitive->get_num_primitives(); sp++) {
2415 e = primitive->get_primitive_end(sp);
2418 for (i = s; i < e; i++) {
2419 int vi = primitive->get_vertex(i);
2422 std::pair<VertexIndexMap::iterator, bool> added = vimap.insert(VertexIndexMap::value_type(vi, 0));
2427 new_vertex = geom_collector.append_vertex(vdata, vi, xform);
2429 (*(added.first)).second = new_vertex;
2435 new_vertex = (*(added.first)).second;
2448 void TextAssembler::GlyphPlacement::
2449 assign_quad_to(QuadMap &quad_map,
const RenderState *state,
2450 const LVector2 &offset)
const {
2453 if (_glyph->get_quad(quad._dimensions, quad._uvs)) {
2454 quad._dimensions *= _scale;
2455 quad._slantl = quad._dimensions[1] * _slant;
2456 quad._slanth = quad._dimensions[3] * _slant;
2457 quad._dimensions += LVecBase4(_xpos, _ypos, _xpos, _ypos);
2458 quad._dimensions += LVecBase4(offset[0], -offset[1], offset[0], -offset[1]);
2459 quad._glyph = _glyph;
2461 quad_map[state->compose(_glyph->get_state())].push_back(move(quad));
2469 void TextAssembler::GlyphPlacement::
2471 if (_graphic_model !=
nullptr) {
2474 node->add_child(intermediate_node);
2476 intermediate_node->set_transform(
2477 TransformState::make_pos_hpr_scale_shear(
2478 LVecBase3(_xpos, 0, _ypos),
2480 LVecBase3(_scale, 1, _scale),
2481 LVecBase3(0, _slant, 0)
2484 intermediate_node->set_state(state);
2485 intermediate_node->add_child(_graphic_model);
2493 TextAssembler::GeomCollector::
2503 TextAssembler::GeomCollector::
2504 GeomCollector(
const TextAssembler::GeomCollector ©) :
2505 _vdata(copy._vdata),
2517 if (prim_type == GeomTriangles::get_class_type()) {
2518 if (_triangles ==
nullptr) {
2520 _geom->add_primitive(_triangles);
2524 }
else if (prim_type == GeomLines::get_class_type()) {
2525 if (_lines ==
nullptr) {
2526 _lines =
new GeomLines(Geom::UH_static);
2527 _geom->add_primitive(_lines);
2531 }
else if (prim_type == GeomPoints::get_class_type()) {
2532 if (_points ==
nullptr) {
2534 _geom->add_primitive(_points);
2539 nassert_raise(
"unexpected primitive type");
2547 int TextAssembler::GeomCollector::
2549 const LMatrix4 &xform) {
2550 int new_row = _vdata->get_num_rows();
2554 vertex_rewriter.set_row_unsafe(new_row);
2555 LPoint3 point = vertex_rewriter.get_data3();
2556 vertex_rewriter.set_data3(point * xform);
2565 void TextAssembler::GeomCollector::
2567 if (_geom->get_num_primitives() > 0) {