Browse Source

text: add thread safety to TextNode

This does not 100% cover all the base class TextProperties, however, so you still need to be careful not to access those from two threads at once.
rdb 7 years ago
parent
commit
1e084e0b2b
3 changed files with 414 additions and 265 deletions
  1. 140 30
      panda/src/text/textNode.I
  2. 266 233
      panda/src/text/textNode.cxx
  3. 8 2
      panda/src/text/textNode.h

+ 140 - 30
panda/src/text/textNode.I

@@ -33,6 +33,7 @@ get_line_height() const {
  */
 INLINE void TextNode::
 set_max_rows(int max_rows) {
+  MutexHolder holder(_lock);
   _max_rows = max_rows;
   invalidate_with_measure();
 }
@@ -43,6 +44,7 @@ set_max_rows(int max_rows) {
  */
 INLINE void TextNode::
 clear_max_rows() {
+  MutexHolder holder(_lock);
   _max_rows = 0;
   invalidate_with_measure();
 }
@@ -53,6 +55,7 @@ clear_max_rows() {
  */
 INLINE bool TextNode::
 has_max_rows() const {
+  MutexHolder holder(_lock);
   return _max_rows > 0;
 }
 
@@ -62,6 +65,7 @@ has_max_rows() const {
  */
 INLINE int TextNode::
 get_max_rows() const {
+  MutexHolder holder(_lock);
   return _max_rows;
 }
 
@@ -71,6 +75,7 @@ get_max_rows() const {
  */
 INLINE bool TextNode::
 has_overflow() const {
+  MutexHolder holder(_lock);
   check_measure();
   return (_flags & F_has_overflow) != 0;
 }
@@ -88,6 +93,7 @@ set_frame_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b, PN_stdfloat a) {
  */
 INLINE void TextNode::
 set_frame_color(const LColor &frame_color) {
+  MutexHolder holder(_lock);
   if (_frame_color != frame_color) {
     _frame_color = frame_color;
     invalidate_no_measure();
@@ -99,6 +105,7 @@ set_frame_color(const LColor &frame_color) {
  */
 INLINE LColor TextNode::
 get_frame_color() const {
+  MutexHolder holder(_lock);
   return _frame_color;
 }
 
@@ -107,7 +114,8 @@ get_frame_color() const {
  */
 INLINE void TextNode::
 set_card_border(PN_stdfloat size, PN_stdfloat uv_portion) {
-  if (!has_card_border() || _card_border_size != size || _card_border_uv_portion != uv_portion) {
+  MutexHolder holder(_lock);
+  if ((_flags & F_has_card_border) == 0 || _card_border_size != size || _card_border_uv_portion != uv_portion) {
     _flags |= F_has_card_border;
     _card_border_size = size;
     _card_border_uv_portion = uv_portion;
@@ -120,7 +128,8 @@ set_card_border(PN_stdfloat size, PN_stdfloat uv_portion) {
  */
 INLINE void TextNode::
 clear_card_border() {
-  if (has_card_border()) {
+  MutexHolder holder(_lock);
+  if (_flags & F_has_card_border) {
     _flags &= ~F_has_card_border;
     invalidate_no_measure();
   }
@@ -131,6 +140,7 @@ clear_card_border() {
  */
 INLINE PN_stdfloat TextNode::
 get_card_border_size() const {
+  MutexHolder holder(_lock);
   return _card_border_size;
 }
 
@@ -139,6 +149,7 @@ get_card_border_size() const {
  */
 INLINE PN_stdfloat TextNode::
 get_card_border_uv_portion() const {
+  MutexHolder holder(_lock);
   return _card_border_uv_portion;
 }
 
@@ -147,6 +158,7 @@ get_card_border_uv_portion() const {
  */
 INLINE bool TextNode::
 has_card_border() const {
+  MutexHolder holder(_lock);
   return (_flags & F_has_card_border) != 0;
 }
 
@@ -163,6 +175,7 @@ set_card_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b, PN_stdfloat a) {
  */
 INLINE void TextNode::
 set_card_color(const LColor &card_color) {
+  MutexHolder holder(_lock);
   if (_card_color != card_color) {
     _card_color = card_color;
     invalidate_no_measure();
@@ -174,6 +187,7 @@ set_card_color(const LColor &card_color) {
  */
 INLINE LColor TextNode::
 get_card_color() const {
+  MutexHolder holder(_lock);
   return _card_color;
 }
 
@@ -185,7 +199,8 @@ set_card_texture(Texture *card_texture) {
   if (card_texture == nullptr) {
     clear_card_texture();
   } else {
-    if (!has_card_texture() || _card_texture != card_texture) {
+    MutexHolder holder(_lock);
+    if ((_flags & F_has_card_texture) == 0 || _card_texture != card_texture) {
       _flags |= F_has_card_texture;
       _card_texture = card_texture;
       invalidate_no_measure();
@@ -198,7 +213,8 @@ set_card_texture(Texture *card_texture) {
  */
 INLINE void TextNode::
 clear_card_texture() {
-  if (has_card_texture()) {
+  MutexHolder holder(_lock);
+  if (_flags & F_has_card_texture) {
     _flags &= ~F_has_card_texture;
     _card_texture = nullptr;
     invalidate_no_measure();
@@ -210,6 +226,7 @@ clear_card_texture() {
  */
 INLINE bool TextNode::
 has_card_texture() const {
+  MutexHolder holder(_lock);
   return (_flags & F_has_card_texture) != 0;
 }
 
@@ -218,6 +235,7 @@ has_card_texture() const {
  */
 INLINE Texture *TextNode::
 get_card_texture() const {
+  MutexHolder holder(_lock);
   return _card_texture;
 }
 
@@ -229,6 +247,7 @@ get_card_texture() const {
  */
 INLINE void TextNode::
 set_frame_as_margin(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top) {
+  MutexHolder holder(_lock);
   _flags |= (F_has_frame | F_frame_as_margin);
   _frame_ul.set(left, top);
   _frame_lr.set(right, bottom);
@@ -243,6 +262,7 @@ set_frame_as_margin(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_
  */
 INLINE void TextNode::
 set_frame_actual(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top) {
+  MutexHolder holder(_lock);
   _flags |= F_has_frame;
   _flags &= ~F_frame_as_margin;
   _frame_ul.set(left, top);
@@ -255,6 +275,7 @@ set_frame_actual(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_std
  */
 INLINE void TextNode::
 clear_frame() {
+  MutexHolder holder(_lock);
   _flags &= ~F_has_frame;
   invalidate_no_measure();
 }
@@ -264,6 +285,7 @@ clear_frame() {
  */
 INLINE bool TextNode::
 has_frame() const {
+  MutexHolder holder(_lock);
   return (_flags & F_has_frame) != 0;
 }
 
@@ -276,7 +298,8 @@ has_frame() const {
  */
 INLINE bool TextNode::
 is_frame_as_margin() const {
-  nassertr(has_frame(), false);
+  MutexHolder holder(_lock);
+  nassertr((_flags & F_has_frame) != 0, false);
   return (_flags & F_frame_as_margin) != 0;
 }
 
@@ -288,7 +311,8 @@ is_frame_as_margin() const {
  */
 INLINE LVecBase4 TextNode::
 get_frame_as_set() const {
-  nassertr(has_frame(), LVecBase4(0.0, 0.0, 0.0, 0.0));
+  MutexHolder holder(_lock);
+  nassertr((_flags & F_has_frame) != 0, LVecBase4(0.0, 0.0, 0.0, 0.0));
   return LVecBase4(_frame_ul[0], _frame_lr[0], _frame_lr[1], _frame_ul[1]);
 }
 
@@ -303,18 +327,20 @@ get_frame_as_set() const {
  */
 INLINE LVecBase4 TextNode::
 get_frame_actual() const {
-  if (!has_frame()) {
+  MutexHolder holder(_lock);
+  if (_flags & F_has_frame) {
+    if (_flags & F_frame_as_margin) {
+      check_measure();
+      return LVecBase4(_text_ul[0] - _frame_ul[0],
+                       _text_lr[0] + _frame_lr[0],
+                       _text_lr[1] - _frame_lr[1],
+                       _text_ul[1] + _frame_ul[1]);
+    } else {
+      return LVecBase4(_frame_ul[0], _frame_lr[0], _frame_lr[1], _frame_ul[1]);
+    }
+  } else {
     check_measure();
     return LVecBase4(_text_ul[0], _text_lr[0], _text_lr[1], _text_ul[1]);
-
-  } else if (is_frame_as_margin()) {
-    check_measure();
-    return LVecBase4(_text_ul[0] - _frame_ul[0],
-                      _text_lr[0] + _frame_lr[0],
-                      _text_lr[1] - _frame_lr[1],
-                      _text_ul[1] + _frame_ul[1]);
-  } else {
-    return get_frame_as_set();
   }
 }
 
@@ -323,6 +349,7 @@ get_frame_actual() const {
  */
 INLINE void TextNode::
 set_frame_line_width(PN_stdfloat frame_width) {
+  MutexHolder holder(_lock);
   _frame_width = frame_width;
   invalidate_no_measure();
 }
@@ -332,6 +359,7 @@ set_frame_line_width(PN_stdfloat frame_width) {
  */
 INLINE PN_stdfloat TextNode::
 get_frame_line_width() const {
+  MutexHolder holder(_lock);
   return _frame_width;
 }
 
@@ -342,6 +370,7 @@ get_frame_line_width() const {
  */
 INLINE void TextNode::
 set_frame_corners(bool corners) {
+  MutexHolder holder(_lock);
   if (corners) {
     _flags |= F_frame_corners;
   } else {
@@ -355,6 +384,7 @@ set_frame_corners(bool corners) {
  */
 INLINE bool TextNode::
 get_frame_corners() const {
+  MutexHolder holder(_lock);
   return (_flags & F_frame_corners) != 0;
 }
 
@@ -366,6 +396,7 @@ get_frame_corners() const {
  */
 INLINE void TextNode::
 set_card_as_margin(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top) {
+  MutexHolder holder(_lock);
   _flags |= (F_has_card | F_card_as_margin);
   _card_ul.set(left, top);
   _card_lr.set(right, bottom);
@@ -380,6 +411,7 @@ set_card_as_margin(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_s
  */
 INLINE void TextNode::
 set_card_actual(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top) {
+  MutexHolder holder(_lock);
   _flags |= F_has_card;
   _flags &= ~F_card_as_margin;
   _card_ul.set(left, top);
@@ -394,6 +426,7 @@ set_card_actual(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdf
  */
 INLINE void TextNode::
 set_card_decal(bool card_decal) {
+  MutexHolder holder(_lock);
   if (card_decal) {
     _flags |= F_card_decal;
   } else {
@@ -407,6 +440,7 @@ set_card_decal(bool card_decal) {
  */
 INLINE void TextNode::
 clear_card() {
+  MutexHolder holder(_lock);
   _flags &= ~F_has_card;
   invalidate_no_measure();
 }
@@ -416,6 +450,7 @@ clear_card() {
  */
 INLINE bool TextNode::
 has_card() const {
+  MutexHolder holder(_lock);
   return (_flags & F_has_card) != 0;
 }
 
@@ -424,6 +459,7 @@ has_card() const {
  */
 INLINE bool TextNode::
 get_card_decal() const {
+  MutexHolder holder(_lock);
   return (_flags & F_card_decal) != 0;
 }
 
@@ -436,7 +472,8 @@ get_card_decal() const {
  */
 INLINE bool TextNode::
 is_card_as_margin() const {
-  nassertr(has_card(), false);
+  MutexHolder holder(_lock);
+  nassertr((_flags & F_has_card) != 0, false);
   return (_flags & F_card_as_margin) != 0;
 }
 
@@ -448,7 +485,8 @@ is_card_as_margin() const {
  */
 INLINE LVecBase4 TextNode::
 get_card_as_set() const {
-  nassertr(has_card(), LVecBase4(0.0, 0.0, 0.0, 0.0));
+  MutexHolder holder(_lock);
+  nassertr((_flags & F_has_card) != 0, LVecBase4(0.0, 0.0, 0.0, 0.0));
   return LVecBase4(_card_ul[0], _card_lr[0], _card_lr[1], _card_ul[1]);
 }
 
@@ -463,18 +501,20 @@ get_card_as_set() const {
  */
 INLINE LVecBase4 TextNode::
 get_card_actual() const {
-  if (!has_card()) {
+  MutexHolder holder(_lock);
+  if (_flags & F_has_card) {
+    if (_flags & F_card_as_margin) {
+      check_measure();
+      return LVecBase4(_text_ul[0] - _card_ul[0],
+                       _text_lr[0] + _card_lr[0],
+                       _text_lr[1] - _card_lr[1],
+                       _text_ul[1] + _card_ul[1]);
+    } else {
+      return LVecBase4(_card_ul[0], _card_lr[0], _card_lr[1], _card_ul[1]);
+    }
+  } else {
     check_measure();
     return LVecBase4(_text_ul[0], _text_lr[0], _text_lr[1], _text_ul[1]);
-
-  } else if (is_card_as_margin()) {
-    check_measure();
-    return LVecBase4(_text_ul[0] - _card_ul[0],
-                      _text_lr[0] + _card_lr[0],
-                      _text_lr[1] - _card_lr[1],
-                      _text_ul[1] + _card_ul[1]);
-  } else {
-    return get_card_as_set();
   }
 }
 
@@ -487,6 +527,8 @@ get_card_actual() const {
 INLINE LVecBase4 TextNode::
 get_card_transformed() const {
   LVecBase4 card = get_card_actual();
+
+  MutexHolder holder(_lock);
   LPoint3 ul = LPoint3(card[0], 0.0, card[3]) * _transform;
   LPoint3 lr = LPoint3(card[1], 0.0, card[2]) * _transform;
 
@@ -498,6 +540,7 @@ get_card_transformed() const {
  */
 INLINE void TextNode::
 set_transform(const LMatrix4 &transform) {
+  MutexHolder holder(_lock);
   _transform = transform;
   invalidate_with_measure();
 }
@@ -507,6 +550,7 @@ set_transform(const LMatrix4 &transform) {
  */
 INLINE LMatrix4 TextNode::
 get_transform() const {
+  MutexHolder holder(_lock);
   return _transform;
 }
 
@@ -515,6 +559,7 @@ get_transform() const {
  */
 INLINE void TextNode::
 set_coordinate_system(CoordinateSystem coordinate_system) {
+  MutexHolder holder(_lock);
   _coordinate_system = coordinate_system;
   invalidate_with_measure();
 }
@@ -524,6 +569,7 @@ set_coordinate_system(CoordinateSystem coordinate_system) {
  */
 INLINE CoordinateSystem TextNode::
 get_coordinate_system() const {
+  MutexHolder holder(_lock);
   return _coordinate_system;
 }
 
@@ -535,6 +581,7 @@ get_coordinate_system() const {
  */
 INLINE void TextNode::
 set_usage_hint(Geom::UsageHint usage_hint) {
+  MutexHolder holder(_lock);
   _usage_hint = usage_hint;
   invalidate_no_measure();
 }
@@ -545,6 +592,7 @@ set_usage_hint(Geom::UsageHint usage_hint) {
  */
 INLINE Geom::UsageHint TextNode::
 get_usage_hint() const {
+  MutexHolder holder(_lock);
   return _usage_hint;
 }
 
@@ -585,6 +633,7 @@ get_usage_hint() const {
  */
 INLINE void TextNode::
 set_flatten_flags(int flatten_flags) {
+  MutexHolder holder(_lock);
   _flatten_flags = flatten_flags;
 }
 
@@ -593,6 +642,7 @@ set_flatten_flags(int flatten_flags) {
  */
 INLINE int TextNode::
 get_flatten_flags() const {
+  MutexHolder holder(_lock);
   return _flatten_flags;
 }
 
@@ -602,6 +652,7 @@ get_flatten_flags() const {
  */
 INLINE void TextNode::
 set_font(TextFont *font) {
+  MutexHolder holder(_lock);
   TextProperties::set_font(font);
   invalidate_with_measure();
 }
@@ -611,6 +662,7 @@ set_font(TextFont *font) {
  */
 INLINE void TextNode::
 clear_font() {
+  MutexHolder holder(_lock);
   TextProperties::clear_font();
   invalidate_with_measure();
 }
@@ -631,6 +683,7 @@ clear_font() {
  */
 INLINE void TextNode::
 set_small_caps(bool small_caps) {
+  MutexHolder holder(_lock);
   TextProperties::set_small_caps(small_caps);
   invalidate_with_measure();
 }
@@ -640,6 +693,7 @@ set_small_caps(bool small_caps) {
  */
 INLINE void TextNode::
 clear_small_caps() {
+  MutexHolder holder(_lock);
   TextProperties::clear_small_caps();
   invalidate_with_measure();
 }
@@ -651,6 +705,7 @@ clear_small_caps() {
  */
 INLINE void TextNode::
 set_small_caps_scale(PN_stdfloat small_caps_scale) {
+  MutexHolder holder(_lock);
   TextProperties::set_small_caps_scale(small_caps_scale);
   invalidate_with_measure();
 }
@@ -660,6 +715,7 @@ set_small_caps_scale(PN_stdfloat small_caps_scale) {
  */
 INLINE void TextNode::
 clear_small_caps_scale() {
+  MutexHolder holder(_lock);
   TextProperties::clear_small_caps_scale();
   invalidate_with_measure();
 }
@@ -669,6 +725,7 @@ clear_small_caps_scale() {
  */
 INLINE void TextNode::
 set_slant(PN_stdfloat slant) {
+  MutexHolder holder(_lock);
   TextProperties::set_slant(slant);
   invalidate_with_measure();
 }
@@ -678,6 +735,7 @@ set_slant(PN_stdfloat slant) {
  */
 INLINE void TextNode::
 clear_slant() {
+  MutexHolder holder(_lock);
   TextProperties::clear_slant();
   invalidate_with_measure();
 }
@@ -687,6 +745,7 @@ clear_slant() {
  */
 INLINE void TextNode::
 set_align(TextNode::Alignment align_type) {
+  MutexHolder holder(_lock);
   TextProperties::set_align(align_type);
   invalidate_with_measure();
 }
@@ -696,6 +755,7 @@ set_align(TextNode::Alignment align_type) {
  */
 INLINE void TextNode::
 clear_align() {
+  MutexHolder holder(_lock);
   TextProperties::clear_align();
   invalidate_with_measure();
 }
@@ -706,6 +766,7 @@ clear_align() {
  */
 INLINE void TextNode::
 set_indent(PN_stdfloat indent) {
+  MutexHolder holder(_lock);
   TextProperties::set_indent(indent);
   invalidate_with_measure();
 }
@@ -715,6 +776,7 @@ set_indent(PN_stdfloat indent) {
  */
 INLINE void TextNode::
 clear_indent() {
+  MutexHolder holder(_lock);
   TextProperties::clear_indent();
   invalidate_with_measure();
 }
@@ -725,6 +787,7 @@ clear_indent() {
  */
 INLINE void TextNode::
 set_wordwrap(PN_stdfloat wordwrap) {
+  MutexHolder holder(_lock);
   TextProperties::set_wordwrap(wordwrap);
   invalidate_with_measure();
 }
@@ -735,6 +798,7 @@ set_wordwrap(PN_stdfloat wordwrap) {
  */
 INLINE void TextNode::
 clear_wordwrap() {
+  MutexHolder holder(_lock);
   TextProperties::clear_wordwrap();
   invalidate_with_measure();
 }
@@ -744,6 +808,7 @@ clear_wordwrap() {
  */
 INLINE void TextNode::
 set_text_color(const LColor &text_color) {
+  MutexHolder holder(_lock);
   TextProperties::set_text_color(text_color);
   invalidate_no_measure();
 }
@@ -762,6 +827,7 @@ set_text_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b, PN_stdfloat a) {
  */
 INLINE void TextNode::
 clear_text_color() {
+  MutexHolder holder(_lock);
   TextProperties::clear_text_color();
   invalidate_no_measure();
 }
@@ -779,6 +845,7 @@ set_shadow_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b, PN_stdfloat a) {
  */
 INLINE void TextNode::
 set_shadow_color(const LColor &shadow_color) {
+  MutexHolder holder(_lock);
   TextProperties::set_shadow_color(shadow_color);
   invalidate_no_measure();
 }
@@ -788,6 +855,7 @@ set_shadow_color(const LColor &shadow_color) {
  */
 INLINE void TextNode::
 clear_shadow_color() {
+  MutexHolder holder(_lock);
   TextProperties::clear_shadow_color();
   invalidate_with_measure();
 }
@@ -807,6 +875,7 @@ set_shadow(PN_stdfloat xoffset, PN_stdfloat yoffset) {
  */
 INLINE void TextNode::
 set_shadow(const LVecBase2 &shadow_offset) {
+  MutexHolder holder(_lock);
   TextProperties::set_shadow(shadow_offset);
   invalidate_no_measure();
 }
@@ -816,6 +885,7 @@ set_shadow(const LVecBase2 &shadow_offset) {
  */
 INLINE void TextNode::
 clear_shadow() {
+  MutexHolder holder(_lock);
   TextProperties::clear_shadow();
   invalidate_no_measure();
 }
@@ -831,6 +901,7 @@ clear_shadow() {
  */
 INLINE void TextNode::
 set_bin(const std::string &bin) {
+  MutexHolder holder(_lock);
   TextProperties::set_bin(bin);
   invalidate_no_measure();
 }
@@ -841,6 +912,7 @@ set_bin(const std::string &bin) {
  */
 INLINE void TextNode::
 clear_bin() {
+  MutexHolder holder(_lock);
   TextProperties::clear_bin();
   invalidate_no_measure();
 }
@@ -858,6 +930,7 @@ clear_bin() {
  */
 INLINE int TextNode::
 set_draw_order(int draw_order) {
+  MutexHolder holder(_lock);
   invalidate_no_measure();
   return TextProperties::set_draw_order(draw_order);
 }
@@ -867,6 +940,7 @@ set_draw_order(int draw_order) {
  */
 INLINE void TextNode::
 clear_draw_order() {
+  MutexHolder holder(_lock);
   TextProperties::clear_draw_order();
   invalidate_with_measure();
 }
@@ -877,6 +951,7 @@ clear_draw_order() {
  */
 INLINE void TextNode::
 set_tab_width(PN_stdfloat tab_width) {
+  MutexHolder holder(_lock);
   TextProperties::set_tab_width(tab_width);
   invalidate_with_measure();
 }
@@ -886,6 +961,7 @@ set_tab_width(PN_stdfloat tab_width) {
  */
 INLINE void TextNode::
 clear_tab_width() {
+  MutexHolder holder(_lock);
   TextProperties::clear_tab_width();
   invalidate_with_measure();
 }
@@ -897,6 +973,7 @@ clear_tab_width() {
  */
 INLINE void TextNode::
 set_glyph_scale(PN_stdfloat glyph_scale) {
+  MutexHolder holder(_lock);
   TextProperties::set_glyph_scale(glyph_scale);
   invalidate_with_measure();
 }
@@ -906,6 +983,7 @@ set_glyph_scale(PN_stdfloat glyph_scale) {
  */
 INLINE void TextNode::
 clear_glyph_scale() {
+  MutexHolder holder(_lock);
   TextProperties::clear_glyph_scale();
   invalidate_with_measure();
 }
@@ -917,6 +995,7 @@ clear_glyph_scale() {
  */
 INLINE void TextNode::
 set_glyph_shift(PN_stdfloat glyph_shift) {
+  MutexHolder holder(_lock);
   TextProperties::set_glyph_shift(glyph_shift);
   invalidate_with_measure();
 }
@@ -926,6 +1005,7 @@ set_glyph_shift(PN_stdfloat glyph_shift) {
  */
 INLINE void TextNode::
 clear_glyph_shift() {
+  MutexHolder holder(_lock);
   TextProperties::clear_glyph_shift();
   invalidate_with_measure();
 }
@@ -936,6 +1016,7 @@ clear_glyph_shift() {
  */
 INLINE void TextNode::
 set_text(const std::string &text) {
+  MutexHolder holder(_lock);
   TextEncoder::set_text(text);
   invalidate_with_measure();
 }
@@ -948,6 +1029,7 @@ set_text(const std::string &text) {
  */
 INLINE void TextNode::
 set_text(const std::string &text, TextNode::Encoding encoding) {
+  MutexHolder holder(_lock);
   TextEncoder::set_text(text, encoding);
   invalidate_with_measure();
 }
@@ -957,6 +1039,7 @@ set_text(const std::string &text, TextNode::Encoding encoding) {
  */
 INLINE void TextNode::
 clear_text() {
+  MutexHolder holder(_lock);
   TextEncoder::clear_text();
   invalidate_with_measure();
 }
@@ -966,6 +1049,7 @@ clear_text() {
  */
 INLINE void TextNode::
 append_text(const std::string &text) {
+  MutexHolder holder(_lock);
   TextEncoder::append_text(text);
   invalidate_with_measure();
 }
@@ -976,6 +1060,7 @@ append_text(const std::string &text) {
  */
 INLINE void TextNode::
 append_unicode_char(wchar_t character) {
+  MutexHolder holder(_lock);
   TextEncoder::append_unicode_char(character);
   invalidate_with_measure();
 }
@@ -1008,6 +1093,7 @@ calc_width(const std::string &line) const {
  */
 INLINE void TextNode::
 set_wtext(const std::wstring &wtext) {
+  MutexHolder holder(_lock);
   TextEncoder::set_wtext(wtext);
   invalidate_with_measure();
 }
@@ -1017,6 +1103,7 @@ set_wtext(const std::wstring &wtext) {
  */
 INLINE void TextNode::
 append_wtext(const std::wstring &wtext) {
+  MutexHolder holder(_lock);
   TextEncoder::append_wtext(wtext);
   invalidate_with_measure();
 }
@@ -1030,6 +1117,7 @@ append_wtext(const std::wstring &wtext) {
  */
 INLINE std::wstring TextNode::
 get_wordwrapped_wtext() const {
+  MutexHolder holder(_lock);
   check_measure();
   return _wordwrapped_wtext;
 }
@@ -1040,6 +1128,7 @@ get_wordwrapped_wtext() const {
  */
 INLINE PN_stdfloat TextNode::
 get_left() const {
+  MutexHolder holder(_lock);
   check_measure();
   return _text_ul[0];
 }
@@ -1050,6 +1139,7 @@ get_left() const {
  */
 INLINE PN_stdfloat TextNode::
 get_right() const {
+  MutexHolder holder(_lock);
   check_measure();
   return _text_lr[0];
 }
@@ -1060,6 +1150,7 @@ get_right() const {
  */
 INLINE PN_stdfloat TextNode::
 get_bottom() const {
+  MutexHolder holder(_lock);
   check_measure();
   return _text_lr[1];
 }
@@ -1070,6 +1161,7 @@ get_bottom() const {
  */
 INLINE PN_stdfloat TextNode::
 get_top() const {
+  MutexHolder holder(_lock);
   check_measure();
   return _text_ul[1];
 }
@@ -1079,6 +1171,7 @@ get_top() const {
  */
 INLINE PN_stdfloat TextNode::
 get_height() const {
+  MutexHolder holder(_lock);
   check_measure();
   return _text_ul[1] - _text_lr[1];
 }
@@ -1088,6 +1181,7 @@ get_height() const {
  */
 INLINE PN_stdfloat TextNode::
 get_width() const {
+  MutexHolder holder(_lock);
   check_measure();
   return _text_lr[0] - _text_ul[0];
 }
@@ -1098,6 +1192,7 @@ get_width() const {
  */
 INLINE LPoint3 TextNode::
 get_upper_left_3d() const {
+  MutexHolder holder(_lock);
   check_measure();
   return _ul3d;
 }
@@ -1108,6 +1203,7 @@ get_upper_left_3d() const {
  */
 INLINE LPoint3 TextNode::
 get_lower_right_3d() const {
+  MutexHolder holder(_lock);
   check_measure();
   return _lr3d;
 }
@@ -1118,10 +1214,22 @@ get_lower_right_3d() const {
  */
 INLINE int TextNode::
 get_num_rows() const {
+  MutexHolder holder(_lock);
   check_measure();
   return _num_rows;
 }
 
+/**
+ * Generates the text, according to the parameters indicated within the
+ * TextNode, and returns a Node that may be parented within the tree to
+ * represent it.
+ */
+PT(PandaNode) TextNode::
+generate() {
+  MutexHolder holder(_lock);
+  return do_generate();
+}
+
 /**
  * Can be called after the TextNode has been fully configured, to force the
  * node to recompute its text immediately, rather than waiting for it to be
@@ -1129,6 +1237,7 @@ get_num_rows() const {
  */
 INLINE void TextNode::
 update() {
+  MutexHolder holder(_lock);
   check_rebuild();
 }
 
@@ -1140,8 +1249,9 @@ update() {
  */
 INLINE void TextNode::
 force_update() {
-  invalidate_with_measure();
-  check_rebuild();
+  MutexHolder holder(_lock);
+  mark_internal_bounds_stale();
+  do_rebuild();
 }
 
 /**

+ 266 - 233
panda/src/text/textNode.cxx

@@ -74,7 +74,7 @@ TextNode(const string &name) : PandaNode(name) {
   }
 
   if (text_small_caps) {
-    set_small_caps(true);
+    TextProperties::set_small_caps(true);
   }
 
   _frame_color.set(1.0f, 1.0f, 1.0f, 1.0f);
@@ -277,10 +277,10 @@ void TextNode::
 output(std::ostream &out) const {
   PandaNode::output(out);
 
-  check_rebuild();
+  PT(PandaNode) internal_geom = do_get_internal_geom();
   int geom_count = 0;
-  if (_internal_geom != nullptr) {
-    geom_count = count_geoms(_internal_geom);
+  if (internal_geom != nullptr) {
+    geom_count = count_geoms(internal_geom);
   }
 
   out << " (" << geom_count << " geoms)";
@@ -291,6 +291,7 @@ output(std::ostream &out) const {
  */
 void TextNode::
 write(std::ostream &out, int indent_level) const {
+  MutexHolder holder(_lock);
   PandaNode::write(out, indent_level);
   TextProperties::write(out, indent_level + 2);
   indent(out, indent_level + 2)
@@ -301,167 +302,6 @@ write(std::ostream &out, int indent_level) const {
     << "text is " << get_text() << "\n";
 }
 
-/**
- * Generates the text, according to the parameters indicated within the
- * TextNode, and returns a Node that may be parented within the tree to
- * represent it.
- */
-PT(PandaNode) TextNode::
-generate() {
-  PStatTimer timer(_text_generate_pcollector);
-  if (text_cat.is_debug()) {
-    text_cat.debug()
-      << "Rebuilding " << get_type() << " " << get_name()
-      << " with '" << get_text() << "'\n";
-  }
-
-  // The strategy here will be to assemble together a bunch of letters,
-  // instanced from the letter hierarchy of font_def, into our own little
-  // hierarchy.
-
-  // There will be one root over the whole text block, that contains the
-  // transform passed in.  Under this root there will be another node for each
-  // row, that moves the row into the right place horizontally and vertically,
-  // and for each row, there is another node for each character.
-
-  _ul3d.set(0.0f, 0.0f, 0.0f);
-  _lr3d.set(0.0f, 0.0f, 0.0f);
-
-  // Now build a new sub-tree for all the text components.
-  string name = get_text();
-  size_t newline = name.find('\n');
-  if (newline != string::npos) {
-    name = name.substr(0, newline);
-  }
-  PT(PandaNode) root = new PandaNode(name);
-
-  if (!has_text()) {
-    return root;
-  }
-
-  TextFont *font = get_font();
-  if (font == nullptr) {
-    return root;
-  }
-
-  // Compute the overall text transform matrix.  We build the text in a Z-up
-  // coordinate system and then convert it to whatever the user asked for.
-  LMatrix4 mat =
-    LMatrix4::convert_mat(CS_zup_right, _coordinate_system) *
-    _transform;
-
-  CPT(TransformState) transform = TransformState::make_mat(mat);
-  root->set_transform(transform);
-
-  std::wstring wtext = get_wtext();
-
-  // Assemble the text.
-  TextAssembler assembler(this);
-  assembler.set_properties(*this);
-  assembler.set_max_rows(_max_rows);
-  assembler.set_usage_hint(_usage_hint);
-  assembler.set_dynamic_merge((_flatten_flags & FF_dynamic_merge) != 0);
-  bool all_set = assembler.set_wtext(wtext);
-  if (all_set) {
-    // No overflow.
-    _flags &= ~F_has_overflow;
-  } else {
-    // Overflow.
-    _flags |= F_has_overflow;
-  }
-
-  PT(PandaNode) text_root = assembler.assemble_text();
-  _text_ul = assembler.get_ul();
-  _text_lr = assembler.get_lr();
-  _num_rows = assembler.get_num_rows();
-  _wordwrapped_wtext = assembler.get_wordwrapped_wtext();
-
-  // Parent the text in.
-  PT(PandaNode) text = new PandaNode("text");
-  root->add_child(text, get_draw_order() + 2);
-  text->add_child(text_root);
-
-  // Save the bounding-box information about the text in a form friendly to
-  // the user.
-  const LVector2 &ul = assembler.get_ul();
-  const LVector2 &lr = assembler.get_lr();
-  _ul3d.set(ul[0], 0.0f, ul[1]);
-  _lr3d.set(lr[0], 0.0f, lr[1]);
-
-  _ul3d = _ul3d * _transform;
-  _lr3d = _lr3d * _transform;
-
-  // Incidentally, that means we don't need to measure the text now.
-  _flags &= ~F_needs_measure;
-
-  // Now flatten our hierarchy to get rid of the transforms we put in,
-  // applying them to the vertices.
-
-  NodePath root_np(root);
-  if (_flatten_flags & FF_strong) {
-    root_np.flatten_strong();
-  } else if (_flatten_flags & FF_medium) {
-    root_np.flatten_medium();
-  } else if (_flatten_flags & FF_light) {
-    root_np.flatten_light();
-  }
-
-  // Now deal with the decorations.
-
-  if (has_card()) {
-    PT(PandaNode) card_root;
-    if (has_card_border()) {
-      card_root = make_card_with_border();
-    } else {
-      card_root = make_card();
-    }
-    card_root->set_transform(transform);
-    card_root->set_attrib(ColorAttrib::make_flat(get_card_color()));
-    if (get_card_color()[3] != 1.0f) {
-      card_root->set_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
-    }
-    if (has_card_texture()) {
-      card_root->set_attrib(TextureAttrib::make(get_card_texture()));
-    }
-
-    if (has_bin()) {
-      card_root->set_attrib(CullBinAttrib::make(get_bin(), get_draw_order()));
-    }
-
-    // We always apply attribs down to the card vertices.
-    SceneGraphReducer gr;
-    gr.apply_attribs(card_root);
-
-    // In order to decal the text onto the card, the card must become the
-    // parent of the text.
-    card_root->add_child(root);
-    root = card_root;
-
-    if (get_card_decal()) {
-      card_root->set_effect(DecalEffect::make());
-    }
-  }
-
-  if (has_frame()) {
-    PT(PandaNode) frame_root = make_frame();
-    frame_root->set_transform(transform);
-    root->add_child(frame_root, get_draw_order() + 1);
-    frame_root->set_attrib(ColorAttrib::make_flat(get_frame_color()));
-    if (get_frame_color()[3] != 1.0f) {
-      frame_root->set_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
-    }
-
-    if (has_bin()) {
-      frame_root->set_attrib(CullBinAttrib::make(get_bin(), get_draw_order() + 1));
-    }
-
-    SceneGraphReducer gr;
-    gr.apply_attribs(frame_root);
-  }
-
-  return root;
-}
-
 /**
  * Returns the actual node that is used internally to render the text, if the
  * TextNode is parented within the scene graph.
@@ -470,14 +310,13 @@ generate() {
  * you want to get a handle to geometry that represents the text.  This method
  * is provided as a debugging aid only.
  */
-PandaNode *TextNode::
+PT(PandaNode) TextNode::
 get_internal_geom() const {
   // Output a nuisance warning to discourage the naive from calling this
   // method accidentally.
   text_cat.info()
     << "TextNode::get_internal_geom() called.\n";
-  check_rebuild();
-  return _internal_geom;
+  return do_get_internal_geom();
 }
 
 /**
@@ -508,6 +347,7 @@ get_unsafe_to_apply_attribs() const {
 void TextNode::
 apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
                           GeomTransformer &transformer) {
+  MutexHolder holder(_lock);
   if ((attrib_types & SceneGraphReducer::TT_transform) != 0) {
     const LMatrix4 &mat = attribs._transform->get_mat();
     _transform *= mat;
@@ -525,10 +365,11 @@ apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
       const ColorAttrib *ca = DCAST(ColorAttrib, attribs._color);
       if (ca->get_color_type() == ColorAttrib::T_flat) {
         const LColor &c = ca->get_color();
-        set_text_color(c);
-        set_frame_color(c);
-        set_card_color(c);
-        set_shadow_color(c);
+        TextProperties::set_text_color(c);
+        TextProperties::set_shadow_color(c);
+        _frame_color = c;
+        _card_color = c;
+        invalidate_no_measure();
       }
     }
   }
@@ -538,29 +379,17 @@ apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
       const LVecBase4 &s = csa->get_scale();
       if (s != LVecBase4(1.0f, 1.0f, 1.0f, 1.0f)) {
         LVecBase4 tc = get_text_color();
-        tc[0] *= s[0];
-        tc[1] *= s[1];
-        tc[2] *= s[2];
-        tc[3] *= s[3];
-        set_text_color(tc);
+        tc.componentwise_mult(s);
+        TextProperties::set_text_color(tc);
+
         LVecBase4 sc = get_shadow_color();
-        sc[0] *= s[0];
-        sc[1] *= s[1];
-        sc[2] *= s[2];
-        sc[3] *= s[3];
-        set_shadow_color(sc);
-        LVecBase4 fc = get_frame_color();
-        fc[0] *= s[0];
-        fc[1] *= s[1];
-        fc[2] *= s[2];
-        fc[3] *= s[3];
-        set_frame_color(fc);
-        LVecBase4 cc = get_card_color();
-        cc[0] *= s[0];
-        cc[1] *= s[1];
-        cc[2] *= s[2];
-        cc[3] *= s[3];
-        set_card_color(cc);
+        sc.componentwise_mult(s);
+        TextProperties::set_shadow_color(sc);
+
+        _frame_color.componentwise_mult(s);
+        _card_color.componentwise_mult(s);
+
+        invalidate_no_measure();
       }
     }
   }
@@ -592,11 +421,10 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, bool &found_any,
     PandaNode::calc_tight_bounds(min_point, max_point, found_any, transform,
                                  current_thread);
 
-  check_rebuild();
-
-  if (_internal_geom != nullptr) {
-    _internal_geom->calc_tight_bounds(min_point, max_point,
-                                      found_any, next_transform, current_thread);
+  PT(PandaNode) geom = do_get_internal_geom();
+  if (geom != nullptr) {
+    geom->calc_tight_bounds(min_point, max_point,
+                            found_any, next_transform, current_thread);
   }
 
   return next_transform;
@@ -622,10 +450,11 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, bool &found_any,
  */
 bool TextNode::
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
-  check_rebuild();
-  if (_internal_geom != nullptr) {
+
+  PT(PandaNode) internal_geom = do_get_internal_geom();
+  if (internal_geom != nullptr) {
     // Render the text with this node.
-    CullTraverserData next_data(data, _internal_geom);
+    CullTraverserData next_data(data, internal_geom);
     trav->traverse(next_data);
   }
 
@@ -661,17 +490,20 @@ compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
 
   // Now enclose the bounding box around the text.  We can do this without
   // actually generating the text, if we have at least measured it.
-  check_measure();
-
   LPoint3 vertices[8];
-  vertices[0].set(_ul3d[0], _ul3d[1], _ul3d[2]);
-  vertices[1].set(_ul3d[0], _ul3d[1], _lr3d[2]);
-  vertices[2].set(_ul3d[0], _lr3d[1], _ul3d[2]);
-  vertices[3].set(_ul3d[0], _lr3d[1], _lr3d[2]);
-  vertices[4].set(_lr3d[0], _ul3d[1], _ul3d[2]);
-  vertices[5].set(_lr3d[0], _ul3d[1], _lr3d[2]);
-  vertices[6].set(_lr3d[0], _lr3d[1], _ul3d[2]);
-  vertices[7].set(_lr3d[0], _lr3d[1], _lr3d[2]);
+  {
+    MutexHolder holder(_lock);
+    check_measure();
+
+    vertices[0].set(_ul3d[0], _ul3d[1], _ul3d[2]);
+    vertices[1].set(_ul3d[0], _ul3d[1], _lr3d[2]);
+    vertices[2].set(_ul3d[0], _lr3d[1], _ul3d[2]);
+    vertices[3].set(_ul3d[0], _lr3d[1], _lr3d[2]);
+    vertices[4].set(_lr3d[0], _ul3d[1], _ul3d[2]);
+    vertices[5].set(_lr3d[0], _ul3d[1], _lr3d[2]);
+    vertices[6].set(_lr3d[0], _lr3d[1], _ul3d[2]);
+    vertices[7].set(_lr3d[0], _lr3d[1], _lr3d[2]);
+  }
 
   gbv->around(vertices, vertices + 8);
 
@@ -686,9 +518,8 @@ compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
 void TextNode::
 r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state,
                 GeomTransformer &transformer, Thread *current_thread) {
-  check_rebuild();
 
-  PandaNode *child = _internal_geom;
+  PT(PandaNode) child = do_get_internal_geom();
   if (child != nullptr) {
     CPT(RenderState) child_state = node_state->compose(child->get_state());
     child->r_prepare_scene(gsg, child_state, transformer, current_thread);
@@ -703,8 +534,9 @@ r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state,
  */
 void TextNode::
 do_rebuild() {
+  nassertv(_lock.debug_is_locked());
   _flags &= ~(F_needs_rebuild | F_needs_measure);
-  _internal_geom = generate();
+  _internal_geom = do_generate();
 }
 
 
@@ -718,18 +550,201 @@ do_measure() {
   do_rebuild();
 }
 
+/**
+ * Generates the text, according to the parameters indicated within the
+ * TextNode, and returns a Node that may be parented within the tree to
+ * represent it.
+ */
+PT(PandaNode) TextNode::
+do_generate() {
+  nassertr(_lock.debug_is_locked(), nullptr);
+
+  PStatTimer timer(_text_generate_pcollector);
+  if (text_cat.is_debug()) {
+    text_cat.debug()
+      << "Rebuilding " << get_type() << " " << get_name()
+      << " with '" << get_text() << "'\n";
+  }
+
+  // The strategy here will be to assemble together a bunch of letters,
+  // instanced from the letter hierarchy of font_def, into our own little
+  // hierarchy.
+
+  // There will be one root over the whole text block, that contains the
+  // transform passed in.  Under this root there will be another node for each
+  // row, that moves the row into the right place horizontally and vertically,
+  // and for each row, there is another node for each character.
+
+  _ul3d.set(0.0f, 0.0f, 0.0f);
+  _lr3d.set(0.0f, 0.0f, 0.0f);
+
+  // Now build a new sub-tree for all the text components.
+  string name = get_text();
+  size_t newline = name.find('\n');
+  if (newline != string::npos) {
+    name = name.substr(0, newline);
+  }
+  PT(PandaNode) root = new PandaNode(name);
+
+  if (!has_text()) {
+    return root;
+  }
+
+  TextFont *font = get_font();
+  if (font == nullptr) {
+    return root;
+  }
+
+  // Compute the overall text transform matrix.  We build the text in a Z-up
+  // coordinate system and then convert it to whatever the user asked for.
+  LMatrix4 mat =
+    LMatrix4::convert_mat(CS_zup_right, _coordinate_system) *
+    _transform;
+
+  CPT(TransformState) transform = TransformState::make_mat(mat);
+  root->set_transform(transform);
+
+  std::wstring wtext = get_wtext();
+
+  // Assemble the text.
+  TextAssembler assembler(this);
+  assembler.set_properties(*this);
+  assembler.set_max_rows(_max_rows);
+  assembler.set_usage_hint(_usage_hint);
+  assembler.set_dynamic_merge((_flatten_flags & FF_dynamic_merge) != 0);
+  bool all_set = assembler.set_wtext(wtext);
+  if (all_set) {
+    // No overflow.
+    _flags &= ~F_has_overflow;
+  } else {
+    // Overflow.
+    _flags |= F_has_overflow;
+  }
+
+  PT(PandaNode) text_root = assembler.assemble_text();
+  _text_ul = assembler.get_ul();
+  _text_lr = assembler.get_lr();
+  _num_rows = assembler.get_num_rows();
+  _wordwrapped_wtext = assembler.get_wordwrapped_wtext();
+
+  // Parent the text in.
+  PT(PandaNode) text = new PandaNode("text");
+  root->add_child(text, get_draw_order() + 2);
+  text->add_child(text_root);
+
+  // Save the bounding-box information about the text in a form friendly to
+  // the user.
+  const LVector2 &ul = assembler.get_ul();
+  const LVector2 &lr = assembler.get_lr();
+  _ul3d.set(ul[0], 0.0f, ul[1]);
+  _lr3d.set(lr[0], 0.0f, lr[1]);
+
+  _ul3d = _ul3d * _transform;
+  _lr3d = _lr3d * _transform;
+
+  // Incidentally, that means we don't need to measure the text now.
+  _flags &= ~F_needs_measure;
+
+  // Now flatten our hierarchy to get rid of the transforms we put in,
+  // applying them to the vertices.
+
+  NodePath root_np(root);
+  if (_flatten_flags & FF_strong) {
+    root_np.flatten_strong();
+  } else if (_flatten_flags & FF_medium) {
+    root_np.flatten_medium();
+  } else if (_flatten_flags & FF_light) {
+    root_np.flatten_light();
+  }
+
+  // Now deal with the decorations.
+
+  if (_flags & F_has_card) {
+    PT(PandaNode) card_root;
+    if (_flags & F_has_card_border) {
+      card_root = make_card_with_border();
+    } else {
+      card_root = make_card();
+    }
+    card_root->set_transform(transform);
+    card_root->set_attrib(ColorAttrib::make_flat(_card_color));
+    if (_card_color[3] != 1.0f) {
+      card_root->set_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
+    }
+    if (_flags & F_has_card_texture) {
+      card_root->set_attrib(TextureAttrib::make(_card_texture));
+    }
+
+    if (has_bin()) {
+      card_root->set_attrib(CullBinAttrib::make(get_bin(), get_draw_order()));
+    }
+
+    // We always apply attribs down to the card vertices.
+    SceneGraphReducer gr;
+    gr.apply_attribs(card_root);
+
+    // In order to decal the text onto the card, the card must become the
+    // parent of the text.
+    card_root->add_child(root);
+    root = card_root;
+
+    if (_flags & F_card_decal) {
+      card_root->set_effect(DecalEffect::make());
+    }
+  }
+
+  if (_flags & F_has_frame) {
+    PT(PandaNode) frame_root = make_frame();
+    frame_root->set_transform(transform);
+    root->add_child(frame_root, get_draw_order() + 1);
+    frame_root->set_attrib(ColorAttrib::make_flat(_frame_color));
+    if (_frame_color[3] != 1.0f) {
+      frame_root->set_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
+    }
+
+    if (has_bin()) {
+      frame_root->set_attrib(CullBinAttrib::make(get_bin(), get_draw_order() + 1));
+    }
+
+    SceneGraphReducer gr;
+    gr.apply_attribs(frame_root);
+  }
+
+  return root;
+}
+
+/**
+ * Returns the actual node that is used internally to render the text, if the
+ * TextNode is parented within the scene graph.
+ */
+PT(PandaNode) TextNode::
+do_get_internal_geom() const {
+  MutexHolder holder(_lock);
+  check_rebuild();
+  return _internal_geom;
+}
+
 /**
  * Creates a frame around the text.
  */
 PT(PandaNode) TextNode::
 make_frame() {
+  nassertr(_lock.debug_is_locked(), nullptr);
+  nassertr((_flags & F_needs_measure) == 0, nullptr);
+
   PT(GeomNode) frame_node = new GeomNode("frame");
 
-  LVector4 dimensions = get_frame_actual();
-  PN_stdfloat left = dimensions[0];
-  PN_stdfloat right = dimensions[1];
-  PN_stdfloat bottom = dimensions[2];
-  PN_stdfloat top = dimensions[3];
+  PN_stdfloat left = _frame_ul[0];
+  PN_stdfloat right = _frame_lr[0];
+  PN_stdfloat bottom = _frame_lr[1];
+  PN_stdfloat top = _frame_ul[1];
+
+  if (_flags & F_frame_as_margin) {
+    left = _text_ul[0] - left;
+    right = _text_lr[0] + right;
+    bottom = _text_lr[1] - bottom;
+    top = _text_ul[1] + top;
+  }
 
   CPT(RenderAttrib) thick = RenderModeAttrib::make(RenderModeAttrib::M_unchanged, _frame_width);
   CPT(RenderState) state = RenderState::make(thick);
@@ -753,8 +768,8 @@ make_frame() {
   geom->add_primitive(frame);
   frame_node->add_geom(geom, state);
 
-  if (get_frame_corners()) {
-    PT(GeomPoints) corners = new GeomPoints(get_usage_hint());
+  if (_flags & F_frame_corners) {
+    PT(GeomPoints) corners = new GeomPoints(_usage_hint);
     corners->add_consecutive_vertices(0, 4);
     PT(Geom) geom2 = new Geom(vdata);
     geom2->add_primitive(corners);
@@ -769,13 +784,22 @@ make_frame() {
  */
 PT(PandaNode) TextNode::
 make_card() {
+  nassertr(_lock.debug_is_locked(), nullptr);
+  nassertr((_flags & F_needs_measure) == 0, nullptr);
+
   PT(GeomNode) card_node = new GeomNode("card");
 
-  LVector4 dimensions = get_card_actual();
-  PN_stdfloat left = dimensions[0];
-  PN_stdfloat right = dimensions[1];
-  PN_stdfloat bottom = dimensions[2];
-  PN_stdfloat top = dimensions[3];
+  PN_stdfloat left = _card_ul[0];
+  PN_stdfloat right = _card_lr[0];
+  PN_stdfloat bottom = _card_lr[1];
+  PN_stdfloat top = _card_ul[1];
+
+  if (_flags & F_card_as_margin) {
+    left = _text_ul[0] - left;
+    right = _text_lr[0] + right;
+    bottom = _text_lr[1] - bottom;
+    top = _text_ul[1] + top;
+  }
 
   PT(GeomVertexData) vdata = new GeomVertexData
     ("text", GeomVertexFormat::get_v3t2(), _usage_hint);
@@ -793,7 +817,7 @@ make_card() {
   texcoord.set_data2(1.0f, 1.0f);
   texcoord.set_data2(1.0f, 0.0f);
 
-  PT(GeomTristrips) card = new GeomTristrips(get_usage_hint());
+  PT(GeomTristrips) card = new GeomTristrips(_usage_hint);
   card->add_consecutive_vertices(0, 4);
   card->close_primitive();
 
@@ -812,13 +836,22 @@ make_card() {
  */
 PT(PandaNode) TextNode::
 make_card_with_border() {
+  nassertr(_lock.debug_is_locked(), nullptr);
+  nassertr((_flags & F_needs_measure) == 0, nullptr);
+
   PT(GeomNode) card_node = new GeomNode("card");
 
-  LVector4 dimensions = get_card_actual();
-  PN_stdfloat left = dimensions[0];
-  PN_stdfloat right = dimensions[1];
-  PN_stdfloat bottom = dimensions[2];
-  PN_stdfloat top = dimensions[3];
+  PN_stdfloat left = _card_ul[0];
+  PN_stdfloat right = _card_lr[0];
+  PN_stdfloat bottom = _card_lr[1];
+  PN_stdfloat top = _card_ul[1];
+
+  if (_flags & F_card_as_margin) {
+    left = _text_ul[0] - left;
+    right = _text_lr[0] + right;
+    bottom = _text_lr[1] - bottom;
+    top = _text_ul[1] + top;
+  }
 
 /*
  * we now create three tri-strips instead of one with vertices arranged as

+ 8 - 2
panda/src/text/textNode.h

@@ -24,6 +24,8 @@
 #include "pandaNode.h"
 #include "luse.h"
 #include "geom.h"
+#include "pmutex.h"
+#include "mutexHolder.h"
 
 /**
  * The primary interface to this module.  This class does basic text assembly;
@@ -225,11 +227,11 @@ PUBLISHED:
 
   INLINE int get_num_rows() const;
 
-  PT(PandaNode) generate();
+  INLINE PT(PandaNode) generate();
   INLINE void update();
   INLINE void force_update();
 
-  PandaNode *get_internal_geom() const;
+  PT(PandaNode) get_internal_geom() const;
 
 PUBLISHED:
   MAKE_PROPERTY(max_rows, get_max_rows, set_max_rows);
@@ -312,12 +314,16 @@ private:
   void do_rebuild();
   void do_measure();
 
+  PT(PandaNode) do_generate();
+  PT(PandaNode) do_get_internal_geom() const;
+
   PT(PandaNode) make_frame();
   PT(PandaNode) make_card();
   PT(PandaNode) make_card_with_border();
 
   static int count_geoms(PandaNode *node);
 
+  Mutex _lock;
   PT(PandaNode) _internal_geom;
 
   PT(Texture) _card_texture;