Browse Source

[TextServer] Fix text buffer not processing strings added after `shape`.

Pāvels Nadtočajevs 7 months ago
parent
commit
43bc44e3b0
2 changed files with 62 additions and 11 deletions
  1. 20 10
      modules/text_server_adv/text_server_adv.cpp
  2. 42 1
      tests/servers/test_text_server.h

+ 20 - 10
modules/text_server_adv/text_server_adv.cpp

@@ -4773,17 +4773,24 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S
 			}
 		}
 
+		Vector<Vector3i> bidi_ranges;
+		if (p_sd->bidi_override.is_empty()) {
+			bidi_ranges.push_back(Vector3i(p_sd->start, p_sd->end, DIRECTION_INHERITED));
+		} else {
+			bidi_ranges = p_sd->bidi_override;
+		}
+
 		int sd_size = p_sd->glyphs.size();
 		const Glyph *sd_glyphs = p_sd->glyphs.ptr();
-		for (int ov = 0; ov < p_sd->bidi_override.size(); ov++) {
+		for (int ov = 0; ov < bidi_ranges.size(); ov++) {
 			UErrorCode err = U_ZERO_ERROR;
 
-			if (p_sd->bidi_override[ov].x >= p_start + p_length || p_sd->bidi_override[ov].y <= p_start) {
+			if (bidi_ranges[ov].x >= p_start + p_length || bidi_ranges[ov].y <= p_start) {
 				continue;
 			}
-			int ov_start = _convert_pos_inv(p_sd, p_sd->bidi_override[ov].x);
+			int ov_start = _convert_pos_inv(p_sd, bidi_ranges[ov].x);
 			int start = MAX(0, _convert_pos_inv(p_sd, p_start) - ov_start);
-			int end = MIN(_convert_pos_inv(p_sd, p_start + p_length), _convert_pos_inv(p_sd, p_sd->bidi_override[ov].y)) - ov_start;
+			int end = MIN(_convert_pos_inv(p_sd, p_start + p_length), _convert_pos_inv(p_sd, bidi_ranges[ov].y)) - ov_start;
 
 			ERR_FAIL_COND_V_MSG((start < 0 || end - start > p_new_sd->utf16.length()), false, "Invalid BiDi override range.");
 
@@ -4797,7 +4804,7 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S
 						// Line BiDi failed (string contains incompatible control characters), try full paragraph BiDi instead.
 						err = U_ZERO_ERROR;
 						const UChar *data = p_sd->utf16.get_data();
-						switch (static_cast<TextServer::Direction>(p_sd->bidi_override[ov].z)) {
+						switch (static_cast<TextServer::Direction>(bidi_ranges[ov].z)) {
 							case DIRECTION_LTR: {
 								ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_LTR, nullptr, &err);
 							} break;
@@ -6490,14 +6497,17 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) {
 		} break;
 	}
 
+	Vector<Vector3i> bidi_ranges;
 	if (sd->bidi_override.is_empty()) {
-		sd->bidi_override.push_back(Vector3i(sd->start, sd->end, DIRECTION_INHERITED));
+		bidi_ranges.push_back(Vector3i(sd->start, sd->end, DIRECTION_INHERITED));
+	} else {
+		bidi_ranges = sd->bidi_override;
 	}
 
-	for (int ov = 0; ov < sd->bidi_override.size(); ov++) {
+	for (int ov = 0; ov < bidi_ranges.size(); ov++) {
 		// Create BiDi iterator.
-		int start = _convert_pos_inv(sd, sd->bidi_override[ov].x - sd->start);
-		int end = _convert_pos_inv(sd, sd->bidi_override[ov].y - sd->start);
+		int start = _convert_pos_inv(sd, bidi_ranges[ov].x - sd->start);
+		int end = _convert_pos_inv(sd, bidi_ranges[ov].y - sd->start);
 
 		if (start < 0 || end - start > sd->utf16.length()) {
 			continue;
@@ -6506,7 +6516,7 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) {
 		UErrorCode err = U_ZERO_ERROR;
 		UBiDi *bidi_iter = ubidi_openSized(end - start, 0, &err);
 		if (U_SUCCESS(err)) {
-			switch (static_cast<TextServer::Direction>(sd->bidi_override[ov].z)) {
+			switch (static_cast<TextServer::Direction>(bidi_ranges[ov].z)) {
 				case DIRECTION_LTR: {
 					ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_LTR, nullptr, &err);
 				} break;

+ 42 - 1
tests/servers/test_text_server.h

@@ -815,12 +815,12 @@ TEST_SUITE("[TextServer]") {
 		SUBCASE("[TextServer] Word break") {
 			for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
 				Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
+				CHECK_FALSE_MESSAGE(ts.is_null(), "Invalid TS interface.");
 
 				if (!ts->has_feature(TextServer::FEATURE_SIMPLE_LAYOUT)) {
 					continue;
 				}
 
-				CHECK_FALSE_MESSAGE(ts.is_null(), "Invalid TS interface.");
 				{
 					String text1 = U"linguistically similar and effectively form";
 					//                           14^     22^ 26^         38^
@@ -918,6 +918,47 @@ TEST_SUITE("[TextServer]") {
 				}
 			}
 		}
+
+		SUBCASE("[TextServer] Buffer invalidation") {
+			for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
+				Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
+				CHECK_FALSE_MESSAGE(ts.is_null(), "Invalid TS interface.");
+
+				if (!ts->has_feature(TextServer::FEATURE_SIMPLE_LAYOUT)) {
+					continue;
+				}
+
+				RID font1 = ts->create_font();
+				ts->font_set_data_ptr(font1, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
+
+				Array font;
+				font.push_back(font1);
+
+				RID ctx = ts->create_shaped_text();
+				CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed.");
+				bool ok = ts->shaped_text_add_string(ctx, "T", font, 16);
+				CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
+				int gl_size = ts->shaped_text_get_glyph_count(ctx);
+				CHECK_MESSAGE(gl_size == 1, "Shaping failed, invalid glyph count");
+
+				ok = ts->shaped_text_add_object(ctx, "key", Size2(20, 20), INLINE_ALIGNMENT_CENTER, 1, 0.0);
+				CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
+				gl_size = ts->shaped_text_get_glyph_count(ctx);
+				CHECK_MESSAGE(gl_size == 2, "Shaping failed, invalid glyph count");
+
+				ok = ts->shaped_text_add_string(ctx, "B", font, 16);
+				CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
+				gl_size = ts->shaped_text_get_glyph_count(ctx);
+				CHECK_MESSAGE(gl_size == 3, "Shaping failed, invalid glyph count");
+
+				ts->free_rid(ctx);
+
+				for (int j = 0; j < font.size(); j++) {
+					ts->free_rid(font[j]);
+				}
+				font.clear();
+			}
+		}
 	}
 }
 }; // namespace TestTextServer