diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml index 3cf96b01449d..677d46a9cd6f 100644 --- a/doc/classes/TextEdit.xml +++ b/doc/classes/TextEdit.xml @@ -1053,7 +1053,7 @@ - Set a custom draw callback for the gutter at the given index. [param draw_callback] must take the following arguments: A line index [int], a gutter index [int], and an area [Rect2]. This callback only works when the gutter type is [constant GUTTER_TYPE_CUSTOM] (see [method set_gutter_type]). + Set a custom draw callback for the gutter at the given index. [param draw_callback] must take the following arguments: A canvas item [RID], a line index [int], a gutter index [int], and an area [Rect2]. This callback only works when the gutter type is [constant GUTTER_TYPE_CUSTOM] (see [method set_gutter_type]). diff --git a/doc/classes/TextLine.xml b/doc/classes/TextLine.xml index c52552f71d80..05e93afc443b 100644 --- a/doc/classes/TextLine.xml +++ b/doc/classes/TextLine.xml @@ -20,6 +20,19 @@ Adds inline object to the text buffer, [param key] must be unique. In the text, object is represented as [param length] object replacement characters. + + + + + + + + + + + Adds draw calls for glyph outlines to the draw list [param list]. + + @@ -31,6 +44,18 @@ Adds text span and font to draw it. + + + + + + + + + + Adds draw calls for glyphs to the draw list [param list]. + + diff --git a/doc/classes/TextParagraph.xml b/doc/classes/TextParagraph.xml index 756efa0529f5..01a65a6ed52f 100644 --- a/doc/classes/TextParagraph.xml +++ b/doc/classes/TextParagraph.xml @@ -9,6 +9,58 @@ + + + + + + + + + + + Adds draw calls for dropcap glyph outlines to the draw list [param list]. + + + + + + + + + + + + Adds draw calls for dropcap glyphs to the draw list [param list]. + + + + + + + + + + + + + + Adds draw calls for line [param line] glyph outlines to the draw list [param list]. + + + + + + + + + + + + + Adds draw calls for line [param line] glyphs to the draw list [param list]. + + @@ -20,6 +72,20 @@ Adds inline object to the text buffer, [param key] must be unique. In the text, object is represented as [param length] object replacement characters. + + + + + + + + + + + + Adds draw calls for paragraph glyph outlines to the draw list [param list]. + + @@ -31,6 +97,19 @@ Adds text span and font to draw it. + + + + + + + + + + + Adds draw calls for paragraph glyphs to the draw list [param list]. + + diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index 6ec7bf77ffca..cdd2dfedcc25 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -19,6 +19,12 @@ + + + + Creates a new, empty draw list resource. + + @@ -53,6 +59,122 @@ Draws box displaying character hexadecimal code. Used for replacing missing characters. + + + + + + + + Adds custom draw call to a draw list [param list]. [param callback] is called with one argument, [RID] of canvas item to draw it to. + + + + + + + + + + + + + Adds draw call for box displaying character hexadecimal code to a draw list [param list]. + + + + + + + + + + + + + + Adds draw call for a line to a draw list [param list]. + + + + + + + + + + + + Adds draw call for a rectangle to a draw list [param list]. + + + + + + + + + + + + Adds draw call for a texture to a draw list [param list]. + + + + + + + + + Processes all draw calls in the draw list [param list] to the canvas item [param ci]. If [param free] is [code]true[/code], the draw list is also deleted. + + + + + + + + Reserves additional space in the draw list [param list] for a [param count] draw calls. + + + + + + + Sorts draw calls in the draw list using to layer index and texture id. + + + + + + + + + + + + + + + + Adds draw call for a single glyph outline to a draw list [param list]. + + + + + + + + + + + + + + + Adds draw call for a single glyph to a draw list [param list]. + + @@ -1363,6 +1485,22 @@ Adds inline object to the text buffer, [param key] must be unique. In the text, object is represented as [param length] object replacement characters. + + + + + + + + + + + + + + Adds draw calls for text buffer glyph outlines to the draw list [param list]. + + @@ -1376,6 +1514,21 @@ Adds text span and font to draw it to the text buffer. + + + + + + + + + + + + + Adds draw calls for text buffer glyphs to the draw list [param list]. + + @@ -2216,6 +2369,33 @@ Maximum font size which will use "one quarter of the pixel" subpixel positioning in [constant SUBPIXEL_POSITIONING_AUTO] mode. + + Draw call that does nothing. This is used to initialize new [code]TextServer::DrawCallType[/code] native structures. + + + Draw call for a normal glyph or glyph outline. + + + Draw call for a MSDF glyph or glyph outline. + + + Draw call for a LCD anti-aliased glyph or glyph outline using. + + + Draw call for a box displaying character hexadecimal code. + + + Draw call for a line. + + + Draw call for a rectangle. + + + Draw call for a texture. + + + Draw call using a custom draw callback. + TextServer supports simple text layouts. diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml index 5ceb3a194838..8c13aa54dd6d 100644 --- a/doc/classes/TextServerExtension.xml +++ b/doc/classes/TextServerExtension.xml @@ -15,6 +15,12 @@ This method is called before text server is unregistered. + + + + Creates a new, empty draw list resource. + + @@ -48,6 +54,122 @@ Draws box displaying character hexadecimal code. + + + + + + + + Adds custom draw call to the draw list [param list]. [param callback] is called with one argument, [RID] of canvas item to draw it to. + + + + + + + + + + + + + Adds draw call for box displaying character hexadecimal code to a draw list [param list]. + + + + + + + + + + + + + + Adds draw call for a line to a draw list [param list]. + + + + + + + + + + + + Adds draw call for a rectangle to a draw list [param list]. + + + + + + + + + + + + Adds draw call for a texture to a draw list [param list]. + + + + + + + + + Processes all draw calls in the draw list [param list] to the canvas item [param ci]. If [param free] is [code]true[/code], the draw list is also deleted. + + + + + + + + Reserves additional space in the draw list [param list] for a [param count] draw calls. + + + + + + + Sorts draw calls in the draw list using to layer index and texture id. + + + + + + + + + + + + + + + + Adds draw call for a single glyph outline to a draw list [param list]. + + + + + + + + + + + + + + + Adds draw call for a single glyph to a draw list [param list]. + + @@ -1335,6 +1457,22 @@ Adds inline object to the text buffer, [param key] must be unique. In the text, object is represented as [param length] object replacement characters. + + + + + + + + + + + + + + Adds draw calls for text buffer glyph outlines to the draw list [param list]. + + @@ -1348,6 +1486,21 @@ Adds text span and font to draw it to the text buffer. + + + + + + + + + + + + + Adds draw calls for text buffer glyphs to the draw list [param list]. + + diff --git a/editor/script/script_text_editor.cpp b/editor/script/script_text_editor.cpp index 48a49ebaa68d..ad0e3426a3bb 100644 --- a/editor/script/script_text_editor.cpp +++ b/editor/script/script_text_editor.cpp @@ -526,16 +526,15 @@ Array ScriptTextEditor::_inline_object_parse(const String &p_text) { return result; } -void ScriptTextEditor::_inline_object_draw(const Dictionary &p_info, const Rect2 &p_rect) { +void ScriptTextEditor::_inline_object_draw(RID p_ci, const Dictionary &p_info, const Rect2 &p_rect) { if (_is_valid_color_info(p_info)) { Rect2 col_rect = p_rect.grow(-4); if (color_alpha_texture.is_null()) { color_alpha_texture = inline_color_picker->get_theme_icon("sample_bg", "ColorPicker"); } - RID text_ci = code_editor->get_text_editor()->get_text_canvas_item(); - RS::get_singleton()->canvas_item_add_rect(text_ci, p_rect.grow(-3), Color(1, 1, 1)); - color_alpha_texture->draw_rect(text_ci, col_rect); - RS::get_singleton()->canvas_item_add_rect(text_ci, col_rect, Color(p_info["color"])); + RS::get_singleton()->canvas_item_add_rect(p_ci, p_rect.grow(-3), Color(1, 1, 1)); + color_alpha_texture->draw_rect(p_ci, col_rect); + RS::get_singleton()->canvas_item_add_rect(p_ci, col_rect, Color(p_info["color"])); } } diff --git a/editor/script/script_text_editor.h b/editor/script/script_text_editor.h index 24b73d6deb69..bf3f2e3b2357 100644 --- a/editor/script/script_text_editor.h +++ b/editor/script/script_text_editor.h @@ -218,7 +218,7 @@ class ScriptTextEditor : public ScriptEditorBase { bool _is_valid_color_info(const Dictionary &p_info); Array _inline_object_parse(const String &p_text); - void _inline_object_draw(const Dictionary &p_info, const Rect2 &p_rect); + void _inline_object_draw(RID p_ci, const Dictionary &p_info, const Rect2 &p_rect); void _inline_object_handle_click(const Dictionary &p_info, const Rect2 &p_rect); String _picker_color_stringify(const Color &p_color, COLOR_MODE p_mode); void _picker_color_changed(const Color &p_color); diff --git a/misc/extension_api_validation/4.5-stable.expected b/misc/extension_api_validation/4.5-stable.expected index 3cb31355e7fd..16ceeae84fc2 100644 --- a/misc/extension_api_validation/4.5-stable.expected +++ b/misc/extension_api_validation/4.5-stable.expected @@ -99,3 +99,11 @@ Validate extension JSON: Error: Field 'builtin_classes/PackedVector3Array/method Validate extension JSON: Error: Field 'builtin_classes/PackedVector4Array/methods/duplicate': is_const changed value in new API, from false to true. Duplicate method made const. Compatibility methods registered. + + +GH-108369 +--------- +Validate extension JSON: Error: Field 'native_structures/CaretInfo': format changed value in new API, from "Rect2 leading_caret;Rect2 trailing_caret;TextServer::Direction leading_direction;TextServer::Direction trailing_direction" to "Rect2 leading_caret;Rect2 trailing_caret;TextServer::Direction leading_direction = TextServer::DIRECTION_AUTO;TextServer::Direction trailing_direction = TextServer::DIRECTION_AUTO". +Validate extension JSON: Error: Field 'native_structures/Glyph': format changed value in new API, from "int start = -1;int end = -1;uint8_t count = 0;uint8_t repeat = 1;uint16_t flags = 0;float x_off = 0.f;float y_off = 0.f;float advance = 0.f;RID font_rid;int font_size = 0;int32_t index = 0" to "int start = -1;int end = -1;uint8_t count = 0;uint8_t repeat = 1;uint16_t flags = 0;float x_off = 0.f;float y_off = 0.f;float advance = 0.f;RID font_rid;int font_size = 0;int32_t index = 0;int span_index = -1". + +Updated existing native structures to match actual definitions. diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 4c211dc64cf7..788be590ead6 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -431,6 +431,10 @@ void TextServerAdvanced::_free_rid(const RID &p_rid) { shaped_owner.free(p_rid); } memdelete(sd); + } else if (list_owner.owns(p_rid)) { + DrawList *dc = list_owner.get_or_null(p_rid); + list_owner.free(p_rid); + memdelete(dc); } } @@ -3928,11 +3932,18 @@ void TextServerAdvanced::_font_render_glyph(const RID &p_font_rid, const Vector2 #endif } -void TextServerAdvanced::_font_draw_glyph(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const { +void TextServerAdvanced::_font_add_glyph_to_draw_list(const RID &p_font, int64_t p_layer, const RID &p_list, const Transform2D &p_transform, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const { + DrawList *list = list_owner.get_or_null(p_list); + ERR_FAIL_NULL(list); + + _imp_font_add_glyph_to_draw_list(p_font, p_layer, list, p_transform, p_size, p_pos, p_index, p_color, p_oversampling); +} + +void TextServerAdvanced::_imp_font_add_glyph_to_draw_list(const RID &p_font, int64_t p_layer, TextServerAdvanced::DrawList *p_list, const Transform2D &p_transform, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const { if (p_index == 0) { return; // Non visual character, skip. } - FontAdvanced *fd = _get_font_data(p_font_rid); + FontAdvanced *fd = _get_font_data(p_font); ERR_FAIL_NULL(fd); MutexLock lock(fd->mutex); @@ -4031,10 +4042,19 @@ void TextServerAdvanced::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca Point2 cpos = p_pos; cpos += fgl.rect.position * (double)p_size / (double)fd->msdf_source_size; Size2 csize = fgl.rect.size * (double)p_size / (double)fd->msdf_source_size; - RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate, 0, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size); + GlyphDrawCall ret; + ret.layer = p_layer; + ret.texture = texture; + ret.dst_rect = Rect2(cpos, csize); + ret.src_rect = fgl.uv_rect; + ret.modulate = modulate; + ret.transform = p_transform; + ret.data = Vector3i(0, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size); + ret.draw_type = DRAW_CALL_MSDF; + p_list->calls.push_back(ret); } else { Point2 cpos = p_pos; - double scale = _font_get_scale(p_font_rid, p_size) / oversampling_factor; + double scale = _font_get_scale(p_font, p_size) / oversampling_factor; if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE * 64)) { cpos.x = cpos.x + 0.125; } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE * 64)) { @@ -4063,22 +4083,33 @@ void TextServerAdvanced::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca csize /= oversampling_factor; } cpos += gpos; - if (lcd_aa) { - RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate); - } else { - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate, false, false); - } + GlyphDrawCall ret; + ret.layer = p_layer; + ret.texture = texture; + ret.dst_rect = Rect2(cpos, csize); + ret.src_rect = fgl.uv_rect; + ret.modulate = modulate; + ret.transform = p_transform; + ret.draw_type = lcd_aa ? DRAW_CALL_LCD : DRAW_CALL_NORMAL; + p_list->calls.push_back(ret); } } } } } -void TextServerAdvanced::_font_draw_glyph_outline(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const { +void TextServerAdvanced::_font_add_glyph_outline_to_draw_list(const RID &p_font, int64_t p_layer, const RID &p_list, const Transform2D &p_transform, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const { + DrawList *list = list_owner.get_or_null(p_list); + ERR_FAIL_NULL(list); + + _imp_font_add_glyph_outline_to_draw_list(p_font, p_layer, list, p_transform, p_size, p_outline_size, p_pos, p_index, p_color, p_oversampling); +} + +void TextServerAdvanced::_imp_font_add_glyph_outline_to_draw_list(const RID &p_font, int64_t p_layer, TextServerAdvanced::DrawList *p_list, const Transform2D &p_transform, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const { if (p_index == 0) { return; // Non visual character, skip. } - FontAdvanced *fd = _get_font_data(p_font_rid); + FontAdvanced *fd = _get_font_data(p_font); ERR_FAIL_NULL(fd); MutexLock lock(fd->mutex); @@ -4173,10 +4204,19 @@ void TextServerAdvanced::_font_draw_glyph_outline(const RID &p_font_rid, const R Point2 cpos = p_pos; cpos += fgl.rect.position * (double)p_size / (double)fd->msdf_source_size; Size2 csize = fgl.rect.size * (double)p_size / (double)fd->msdf_source_size; - RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate, p_outline_size, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size); + GlyphDrawCall ret; + ret.layer = p_layer; + ret.texture = texture; + ret.dst_rect = Rect2(cpos, csize); + ret.src_rect = fgl.uv_rect; + ret.modulate = modulate; + ret.transform = p_transform; + ret.data = Vector3i(p_outline_size, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size); + ret.draw_type = DRAW_CALL_MSDF; + p_list->calls.push_back(ret); } else { Point2 cpos = p_pos; - double scale = _font_get_scale(p_font_rid, p_size) / oversampling_factor; + double scale = _font_get_scale(p_font, p_size) / oversampling_factor; if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE * 64)) { cpos.x = cpos.x + 0.125; } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE * 64)) { @@ -4205,17 +4245,37 @@ void TextServerAdvanced::_font_draw_glyph_outline(const RID &p_font_rid, const R csize /= oversampling_factor; } cpos += gpos; - if (lcd_aa) { - RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate); - } else { - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate, false, false); - } + GlyphDrawCall ret; + ret.layer = p_layer; + ret.texture = texture; + ret.dst_rect = Rect2(cpos, csize); + ret.src_rect = fgl.uv_rect; + ret.modulate = modulate; + ret.transform = p_transform; + ret.draw_type = lcd_aa ? DRAW_CALL_LCD : DRAW_CALL_NORMAL; + p_list->calls.push_back(ret); } } } } } +void TextServerAdvanced::_font_draw_glyph(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const { + if (RenderingServer::get_singleton() != nullptr) { + DrawList dl; + _imp_font_add_glyph_to_draw_list(p_font_rid, 0, &dl, Transform2D(), p_size, p_pos, p_index, p_color, p_oversampling); + _imp_draw_list_draw(&dl, p_canvas); + } +} + +void TextServerAdvanced::_font_draw_glyph_outline(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const { + if (RenderingServer::get_singleton() != nullptr) { + DrawList dl; + _imp_font_add_glyph_outline_to_draw_list(p_font_rid, 0, &dl, Transform2D(), p_size, p_outline_size, p_pos, p_index, p_color, p_oversampling); + _imp_draw_list_draw(&dl, p_canvas); + } +} + bool TextServerAdvanced::_font_is_language_supported(const RID &p_font_rid, const String &p_language) const { FontAdvanced *fd = _get_font_data(p_font_rid); ERR_FAIL_NULL_V(fd, false); @@ -4362,6 +4422,187 @@ Dictionary TextServerAdvanced::_font_supported_variation_list(const RID &p_font_ return fd->supported_varaitions; } +/*************************************************************************/ +/* Draw list interface */ +/*************************************************************************/ + +RID TextServerAdvanced::_create_draw_list() { + _THREAD_SAFE_METHOD_ + DrawList *list = memnew(DrawList); + return list_owner.make_rid(list); +} + +void TextServerAdvanced::_draw_list_sort(const RID &p_dc) { + DrawList *list = list_owner.get_or_null(p_dc); + ERR_FAIL_NULL(list); + list->calls.sort_custom(); +} + +void TextServerAdvanced::_draw_list_reserve(const RID &p_dc, int64_t p_new_items) { + DrawList *list = list_owner.get_or_null(p_dc); + ERR_FAIL_NULL(list); + list->calls.reserve(list->calls.size() + p_new_items); +} + +void TextServerAdvanced::_draw_list_add_hexbox(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, const Vector2 &p_pos, int64_t p_index, int64_t p_size, const Color &p_modulate) const { + DrawList *list = list_owner.get_or_null(p_dc); + ERR_FAIL_NULL(list); + + GlyphDrawCall dc; + dc.draw_type = TextServer::DRAW_CALL_HEX; + dc.layer = p_layer; + dc.transform = p_transform; + dc.data = Vector2i(p_index, p_size); + dc.modulate = p_modulate; + dc.dst_rect.position = p_pos; + + list->calls.push_back(dc); +} + +void TextServerAdvanced::_draw_list_add_rect(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, const Rect2 &p_rect, bool p_filled, const Color &p_modulate) const { + DrawList *list = list_owner.get_or_null(p_dc); + ERR_FAIL_NULL(list); + + GlyphDrawCall dc; + dc.draw_type = TextServer::DRAW_CALL_RECT; + dc.layer = p_layer; + dc.transform = p_transform; + dc.data = p_filled; + dc.modulate = p_modulate; + dc.dst_rect = p_rect; + + list->calls.push_back(dc); +} + +void TextServerAdvanced::_draw_list_add_line(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, const Point2 &p_start, const Point2 &p_end, float p_width, float p_dash, const Color &p_modulate) const { + DrawList *list = list_owner.get_or_null(p_dc); + ERR_FAIL_NULL(list); + + GlyphDrawCall dc; + dc.draw_type = TextServer::DRAW_CALL_LINE; + dc.layer = p_layer; + dc.transform = p_transform; + dc.data = Vector2(p_dash, p_width); + dc.modulate = p_modulate; + dc.dst_rect.position = p_start; + dc.src_rect.position = p_end; + + list->calls.push_back(dc); +} + +void TextServerAdvanced::_draw_list_add_texture(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, RID p_texture, const Rect2 &p_dst_rect, const Color &p_modulate) const { + DrawList *list = list_owner.get_or_null(p_dc); + ERR_FAIL_NULL(list); + + GlyphDrawCall dc; + dc.draw_type = TextServer::DRAW_CALL_IMAGE; + dc.layer = p_layer; + dc.texture = p_texture; + dc.transform = p_transform; + dc.modulate = p_modulate; + dc.dst_rect = p_dst_rect; + + list->calls.push_back(dc); +} + +void TextServerAdvanced::_draw_list_add_custom(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, const Callable &p_callback) const { + DrawList *list = list_owner.get_or_null(p_dc); + ERR_FAIL_NULL(list); + + GlyphDrawCall dc; + dc.draw_type = TextServer::DRAW_CALL_CUSTOM; + dc.layer = p_layer; + dc.data = p_callback; + dc.transform = p_transform; + + list->calls.push_back(dc); +} + +void TextServerAdvanced::_imp_draw_list_draw(TextServerAdvanced::DrawList *p_list, const RID &p_ci) const { + for (const GlyphDrawCall &dc : p_list->calls) { + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_ci, dc.transform); + if (dc.draw_type == TextServer::DRAW_CALL_MSDF) { + RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_ci, dc.dst_rect, dc.texture, dc.src_rect, dc.modulate, dc.data.operator Vector3().x, dc.data.operator Vector3().y, dc.data.operator Vector3().z); + } else if (dc.draw_type == TextServer::DRAW_CALL_LCD) { + RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_ci, dc.dst_rect, dc.texture, dc.src_rect, dc.modulate); + } else if (dc.draw_type == TextServer::DRAW_CALL_NORMAL) { + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_ci, dc.dst_rect, dc.texture, dc.src_rect, dc.modulate, false, false); + } else if (dc.draw_type == TextServer::DRAW_CALL_CUSTOM) { + const Callable &cb = dc.data.operator Callable(); + if (cb.is_valid()) { + if (cb.get_argument_count() == 1) { + cb.call(p_ci); +#ifndef DISABLE_DEPRECATED + } else if (cb.get_argument_count() == 0) { + cb.call(); +#endif + } + } + } else if (dc.draw_type == TextServer::DRAW_CALL_IMAGE) { + RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_ci, dc.dst_rect, dc.texture, false, dc.modulate, false); + } else if (dc.draw_type == TextServer::DRAW_CALL_HEX) { + TS->draw_hex_code_box(p_ci, dc.data.operator Vector2i().y, dc.dst_rect.position, dc.data.operator Vector2i().x, dc.modulate); + } else if (dc.draw_type == TextServer::DRAW_CALL_RECT) { + if (dc.data.operator bool()) { + RenderingServer::get_singleton()->canvas_item_add_rect(p_ci, dc.dst_rect, dc.modulate, false); + } else { + Vector points; + points.resize(5); + points.write[0] = dc.dst_rect.position; + points.write[1] = dc.dst_rect.position + Vector2(dc.dst_rect.size.x, 0); + points.write[2] = dc.dst_rect.position + dc.dst_rect.size; + points.write[3] = dc.dst_rect.position + Vector2(0, dc.dst_rect.size.y); + points.write[4] = dc.dst_rect.position; + + Vector colors = { dc.modulate }; + + RenderingServer::get_singleton()->canvas_item_add_polyline(p_ci, points, colors, -1.0, false); + } + } else if (dc.draw_type == TextServer::DRAW_CALL_LINE) { + float dash = dc.data.operator Vector2().x; + if (dash <= 0.0) { + RenderingServer::get_singleton()->canvas_item_add_line(p_ci, dc.dst_rect.position, dc.src_rect.position, dc.modulate, dc.data.operator Vector2().y, false); + } else { + float length = (dc.src_rect.position - dc.dst_rect.position).length(); + Vector2 step = dash * (dc.src_rect.position - dc.dst_rect.position).normalized(); + + if (length < dash || step == Vector2()) { + RenderingServer::get_singleton()->canvas_item_add_line(p_ci, dc.dst_rect.position, dc.src_rect.position, dc.modulate, dc.data.operator Vector2().y, false); + return; + } + + int steps = Math::ceil(length / dash); + if (steps % 2 == 0) { + steps--; + } + + Point2 off = dc.dst_rect.position + (dc.src_rect.position - dc.dst_rect.position).normalized() * (length - steps * dash) / 2.0; + + Vector points; + points.resize(steps + 1); + for (int i = 0; i < steps; i += 2) { + points.write[i] = (i == 0) ? dc.dst_rect.position : off; + points.write[i + 1] = (i == steps - 1) ? dc.src_rect.position : (off + step); + off += step * 2; + } + + Vector colors = { dc.modulate }; + + RenderingServer::get_singleton()->canvas_item_add_multiline(p_ci, points, colors, dc.data.operator Vector2().y, false); + } + } + } +} + +void TextServerAdvanced::_draw_list_draw(const RID &p_dc, const RID &p_ci, bool p_free) { + DrawList *list = list_owner.get_or_null(p_dc); + ERR_FAIL_NULL(list); + _imp_draw_list_draw(list, p_ci); + if (p_free) { + _free_rid(p_dc); + } +} + /*************************************************************************/ /* Shaped text buffer interface */ /*************************************************************************/ diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index 49a0e2512cb6..e0fd70dd3c35 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -584,11 +584,16 @@ class TextServerAdvanced : public TextServerExtension { } }; + struct DrawList { + LocalVector calls; + }; + // Common data. mutable RID_PtrOwner font_var_owner; mutable RID_PtrOwner font_owner; mutable RID_PtrOwner shaped_owner; + mutable RID_PtrOwner list_owner; _FORCE_INLINE_ FontAdvanced *_get_font_data(const RID &p_font_rid) const { RID rid = p_font_rid; @@ -768,6 +773,10 @@ class TextServerAdvanced : public TextServerExtension { _FORCE_INLINE_ void _add_features(const Dictionary &p_source, Vector &r_ftrs); + void _imp_font_add_glyph_to_draw_list(const RID &p_font, int64_t p_layer, TextServerAdvanced::DrawList *p_list, const Transform2D &p_transform, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const; + void _imp_font_add_glyph_outline_to_draw_list(const RID &p_font, int64_t p_layer, TextServerAdvanced::DrawList *p_list, const Transform2D &p_transform, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const; + void _imp_draw_list_draw(TextServerAdvanced::DrawList *p_list, const RID &p_ci) const; + Mutex ft_mutex; // HarfBuzz bitmap font interface. @@ -997,6 +1006,9 @@ class TextServerAdvanced : public TextServerExtension { MODBIND7C(font_draw_glyph, const RID &, const RID &, int64_t, const Vector2 &, int64_t, const Color &, float); MODBIND8C(font_draw_glyph_outline, const RID &, const RID &, int64_t, int64_t, const Vector2 &, int64_t, const Color &, float); + MODBIND9C(font_add_glyph_to_draw_list, const RID &, int64_t, const RID &, const Transform2D &, int64_t, const Vector2 &, int64_t, const Color &, float); + MODBIND10C(font_add_glyph_outline_to_draw_list, const RID &, int64_t, const RID &, const Transform2D &, int64_t, int64_t, const Vector2 &, int64_t, const Color &, float); + MODBIND2RC(bool, font_is_language_supported, const RID &, const String &); MODBIND3(font_set_language_support_override, const RID &, const String &, bool); MODBIND2R(bool, font_get_language_support_override, const RID &, const String &); @@ -1018,6 +1030,18 @@ class TextServerAdvanced : public TextServerExtension { MODBIND1(reference_oversampling_level, double); MODBIND1(unreference_oversampling_level, double); + /* Draw list interface */ + + MODBIND0R(RID, create_draw_list); + MODBIND1(draw_list_sort, const RID &); + MODBIND2(draw_list_reserve, const RID &, int64_t); + MODBIND7C(draw_list_add_hexbox, const RID &, int64_t, const Transform2D &, const Vector2 &, int64_t, int64_t, const Color &); + MODBIND6C(draw_list_add_rect, const RID &, int64_t, const Transform2D &, const Rect2 &, bool, const Color &); + MODBIND8C(draw_list_add_line, const RID &, int64_t, const Transform2D &, const Point2 &, const Point2 &, float, float, const Color &); + MODBIND6C(draw_list_add_texture, const RID &, int64_t, const Transform2D &, RID, const Rect2 &, const Color &); + MODBIND4C(draw_list_add_custom, const RID &, int64_t, const Transform2D &, const Callable &); + MODBIND3(draw_list_draw, const RID &, const RID &, bool); + /* Shaped text buffer interface */ MODBIND2R(RID, create_shaped_text, Direction, Orientation); diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index 554108faf1ce..f64b0b69d143 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -155,6 +155,10 @@ void TextServerFallback::_free_rid(const RID &p_rid) { shaped_owner.free(p_rid); } memdelete(sd); + } else if (list_owner.owns(p_rid)) { + DrawList *dc = list_owner.get_or_null(p_rid); + list_owner.free(p_rid); + memdelete(dc); } } @@ -2835,11 +2839,18 @@ void TextServerFallback::_font_render_glyph(const RID &p_font_rid, const Vector2 #endif } -void TextServerFallback::_font_draw_glyph(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const { +void TextServerFallback::_font_add_glyph_to_draw_list(const RID &p_font, int64_t p_layer, const RID &p_list, const Transform2D &p_transform, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const { + DrawList *list = list_owner.get_or_null(p_list); + ERR_FAIL_NULL(list); + + _imp_font_add_glyph_to_draw_list(p_font, p_layer, list, p_transform, p_size, p_pos, p_index, p_color, p_oversampling); +} + +void TextServerFallback::_imp_font_add_glyph_to_draw_list(const RID &p_font, int64_t p_layer, TextServerFallback::DrawList *p_list, const Transform2D &p_transform, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const { if (p_index == 0) { return; // Non visual character, skip. } - FontFallback *fd = _get_font_data(p_font_rid); + FontFallback *fd = _get_font_data(p_font); ERR_FAIL_NULL(fd); MutexLock lock(fd->mutex); @@ -2938,10 +2949,19 @@ void TextServerFallback::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca Point2 cpos = p_pos; cpos += fgl.rect.position * (double)p_size / (double)fd->msdf_source_size; Size2 csize = fgl.rect.size * (double)p_size / (double)fd->msdf_source_size; - RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate, 0, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size); + GlyphDrawCall ret; + ret.layer = p_layer; + ret.texture = texture; + ret.dst_rect = Rect2(cpos, csize); + ret.src_rect = fgl.uv_rect; + ret.modulate = modulate; + ret.transform = p_transform; + ret.data = Vector3i(0, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size); + ret.draw_type = DRAW_CALL_MSDF; + p_list->calls.push_back(ret); } else { Point2 cpos = p_pos; - double scale = _font_get_scale(p_font_rid, p_size) / oversampling_factor; + double scale = _font_get_scale(p_font, p_size) / oversampling_factor; if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { cpos.x = cpos.x + 0.125; } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { @@ -2970,22 +2990,33 @@ void TextServerFallback::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca csize /= oversampling_factor; } cpos += gpos; - if (lcd_aa) { - RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate); - } else { - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate, false, false); - } + GlyphDrawCall ret; + ret.layer = p_layer; + ret.texture = texture; + ret.dst_rect = Rect2(cpos, csize); + ret.src_rect = fgl.uv_rect; + ret.modulate = modulate; + ret.transform = p_transform; + ret.draw_type = lcd_aa ? DRAW_CALL_LCD : DRAW_CALL_NORMAL; + p_list->calls.push_back(ret); } } } } } -void TextServerFallback::_font_draw_glyph_outline(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const { +void TextServerFallback::_font_add_glyph_outline_to_draw_list(const RID &p_font, int64_t p_layer, const RID &p_list, const Transform2D &p_transform, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const { + DrawList *list = list_owner.get_or_null(p_list); + ERR_FAIL_NULL(list); + + _imp_font_add_glyph_outline_to_draw_list(p_font, p_layer, list, p_transform, p_size, p_outline_size, p_pos, p_index, p_color, p_oversampling); +} + +void TextServerFallback::_imp_font_add_glyph_outline_to_draw_list(const RID &p_font, int64_t p_layer, TextServerFallback::DrawList *p_list, const Transform2D &p_transform, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const { if (p_index == 0) { return; // Non visual character, skip. } - FontFallback *fd = _get_font_data(p_font_rid); + FontFallback *fd = _get_font_data(p_font); ERR_FAIL_NULL(fd); MutexLock lock(fd->mutex); @@ -3080,10 +3111,19 @@ void TextServerFallback::_font_draw_glyph_outline(const RID &p_font_rid, const R Point2 cpos = p_pos; cpos += fgl.rect.position * (double)p_size / (double)fd->msdf_source_size; Size2 csize = fgl.rect.size * (double)p_size / (double)fd->msdf_source_size; - RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate, p_outline_size, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size); + GlyphDrawCall ret; + ret.layer = p_layer; + ret.texture = texture; + ret.dst_rect = Rect2(cpos, csize); + ret.src_rect = fgl.uv_rect; + ret.modulate = modulate; + ret.transform = p_transform; + ret.data = Vector3i(p_outline_size, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size); + ret.draw_type = DRAW_CALL_MSDF; + p_list->calls.push_back(ret); } else { Point2 cpos = p_pos; - double scale = _font_get_scale(p_font_rid, p_size) / oversampling_factor; + double scale = _font_get_scale(p_font, p_size) / oversampling_factor; if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { cpos.x = cpos.x + 0.125; } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { @@ -3112,17 +3152,37 @@ void TextServerFallback::_font_draw_glyph_outline(const RID &p_font_rid, const R csize /= oversampling_factor; } cpos += gpos; - if (lcd_aa) { - RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate); - } else { - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate, false, false); - } + GlyphDrawCall ret; + ret.layer = p_layer; + ret.texture = texture; + ret.dst_rect = Rect2(cpos, csize); + ret.src_rect = fgl.uv_rect; + ret.modulate = modulate; + ret.transform = p_transform; + ret.draw_type = lcd_aa ? DRAW_CALL_LCD : DRAW_CALL_NORMAL; + p_list->calls.push_back(ret); } } } } } +void TextServerFallback::_font_draw_glyph(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const { + if (RenderingServer::get_singleton() != nullptr) { + DrawList dl; + _imp_font_add_glyph_to_draw_list(p_font_rid, 0, &dl, Transform2D(), p_size, p_pos, p_index, p_color, p_oversampling); + _imp_draw_list_draw(&dl, p_canvas); + } +} + +void TextServerFallback::_font_draw_glyph_outline(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const { + if (RenderingServer::get_singleton() != nullptr) { + DrawList dl; + _imp_font_add_glyph_outline_to_draw_list(p_font_rid, 0, &dl, Transform2D(), p_size, p_outline_size, p_pos, p_index, p_color, p_oversampling); + _imp_draw_list_draw(&dl, p_canvas); + } +} + bool TextServerFallback::_font_is_language_supported(const RID &p_font_rid, const String &p_language) const { FontFallback *fd = _get_font_data(p_font_rid); ERR_FAIL_NULL_V(fd, false); @@ -3256,6 +3316,187 @@ Dictionary TextServerFallback::_font_supported_variation_list(const RID &p_font_ return fd->supported_varaitions; } +/*************************************************************************/ +/* Draw list interface */ +/*************************************************************************/ + +RID TextServerFallback::_create_draw_list() { + _THREAD_SAFE_METHOD_ + DrawList *list = memnew(DrawList); + return list_owner.make_rid(list); +} + +void TextServerFallback::_draw_list_sort(const RID &p_dc) { + DrawList *list = list_owner.get_or_null(p_dc); + ERR_FAIL_NULL(list); + list->calls.sort_custom(); +} + +void TextServerFallback::_draw_list_reserve(const RID &p_dc, int64_t p_new_items) { + DrawList *list = list_owner.get_or_null(p_dc); + ERR_FAIL_NULL(list); + list->calls.reserve(list->calls.size() + p_new_items); +} + +void TextServerFallback::_draw_list_add_hexbox(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, const Vector2 &p_pos, int64_t p_index, int64_t p_size, const Color &p_modulate) const { + DrawList *list = list_owner.get_or_null(p_dc); + ERR_FAIL_NULL(list); + + GlyphDrawCall dc; + dc.draw_type = TextServer::DRAW_CALL_HEX; + dc.layer = p_layer; + dc.transform = p_transform; + dc.data = Vector2i(p_index, p_size); + dc.modulate = p_modulate; + dc.dst_rect.position = p_pos; + + list->calls.push_back(dc); +} + +void TextServerFallback::_draw_list_add_rect(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, const Rect2 &p_rect, bool p_filled, const Color &p_modulate) const { + DrawList *list = list_owner.get_or_null(p_dc); + ERR_FAIL_NULL(list); + + GlyphDrawCall dc; + dc.draw_type = TextServer::DRAW_CALL_RECT; + dc.layer = p_layer; + dc.transform = p_transform; + dc.data = p_filled; + dc.modulate = p_modulate; + dc.dst_rect = p_rect; + + list->calls.push_back(dc); +} + +void TextServerFallback::_draw_list_add_line(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, const Point2 &p_start, const Point2 &p_end, float p_width, float p_dash, const Color &p_modulate) const { + DrawList *list = list_owner.get_or_null(p_dc); + ERR_FAIL_NULL(list); + + GlyphDrawCall dc; + dc.draw_type = TextServer::DRAW_CALL_LINE; + dc.layer = p_layer; + dc.transform = p_transform; + dc.data = Vector2(p_dash, p_width); + dc.modulate = p_modulate; + dc.dst_rect.position = p_start; + dc.src_rect.position = p_end; + + list->calls.push_back(dc); +} + +void TextServerFallback::_draw_list_add_texture(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, RID p_texture, const Rect2 &p_dst_rect, const Color &p_modulate) const { + DrawList *list = list_owner.get_or_null(p_dc); + ERR_FAIL_NULL(list); + + GlyphDrawCall dc; + dc.draw_type = TextServer::DRAW_CALL_IMAGE; + dc.layer = p_layer; + dc.texture = p_texture; + dc.transform = p_transform; + dc.modulate = p_modulate; + dc.dst_rect = p_dst_rect; + + list->calls.push_back(dc); +} + +void TextServerFallback::_draw_list_add_custom(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, const Callable &p_callback) const { + DrawList *list = list_owner.get_or_null(p_dc); + ERR_FAIL_NULL(list); + + GlyphDrawCall dc; + dc.draw_type = TextServer::DRAW_CALL_CUSTOM; + dc.layer = p_layer; + dc.data = p_callback; + dc.transform = p_transform; + + list->calls.push_back(dc); +} + +void TextServerFallback::_imp_draw_list_draw(TextServerFallback::DrawList *p_list, const RID &p_ci) const { + for (const GlyphDrawCall &dc : p_list->calls) { + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_ci, dc.transform); + if (dc.draw_type == TextServer::DRAW_CALL_MSDF) { + RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_ci, dc.dst_rect, dc.texture, dc.src_rect, dc.modulate, dc.data.operator Vector3().x, dc.data.operator Vector3().y, dc.data.operator Vector3().z); + } else if (dc.draw_type == TextServer::DRAW_CALL_LCD) { + RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_ci, dc.dst_rect, dc.texture, dc.src_rect, dc.modulate); + } else if (dc.draw_type == TextServer::DRAW_CALL_NORMAL) { + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_ci, dc.dst_rect, dc.texture, dc.src_rect, dc.modulate, false, false); + } else if (dc.draw_type == TextServer::DRAW_CALL_CUSTOM) { + const Callable &cb = dc.data.operator Callable(); + if (cb.is_valid()) { + if (cb.get_argument_count() == 1) { + cb.call(p_ci); +#ifndef DISABLE_DEPRECATED + } else if (cb.get_argument_count() == 0) { + cb.call(); +#endif + } + } + } else if (dc.draw_type == TextServer::DRAW_CALL_IMAGE) { + RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_ci, dc.dst_rect, dc.texture, false, dc.modulate, false); + } else if (dc.draw_type == TextServer::DRAW_CALL_HEX) { + TS->draw_hex_code_box(p_ci, dc.data.operator Vector2i().y, dc.dst_rect.position, dc.data.operator Vector2i().x, dc.modulate); + } else if (dc.draw_type == TextServer::DRAW_CALL_RECT) { + if (dc.data.operator bool()) { + RenderingServer::get_singleton()->canvas_item_add_rect(p_ci, dc.dst_rect, dc.modulate, false); + } else { + Vector points; + points.resize(5); + points.write[0] = dc.dst_rect.position; + points.write[1] = dc.dst_rect.position + Vector2(dc.dst_rect.size.x, 0); + points.write[2] = dc.dst_rect.position + dc.dst_rect.size; + points.write[3] = dc.dst_rect.position + Vector2(0, dc.dst_rect.size.y); + points.write[4] = dc.dst_rect.position; + + Vector colors = { dc.modulate }; + + RenderingServer::get_singleton()->canvas_item_add_polyline(p_ci, points, colors, -1.0, false); + } + } else if (dc.draw_type == TextServer::DRAW_CALL_LINE) { + float dash = dc.data.operator Vector2().x; + if (dash <= 0.0) { + RenderingServer::get_singleton()->canvas_item_add_line(p_ci, dc.dst_rect.position, dc.src_rect.position, dc.modulate, dc.data.operator Vector2().y, false); + } else { + float length = (dc.src_rect.position - dc.dst_rect.position).length(); + Vector2 step = dash * (dc.src_rect.position - dc.dst_rect.position).normalized(); + + if (length < dash || step == Vector2()) { + RenderingServer::get_singleton()->canvas_item_add_line(p_ci, dc.dst_rect.position, dc.src_rect.position, dc.modulate, dc.data.operator Vector2().y, false); + return; + } + + int steps = Math::ceil(length / dash); + if (steps % 2 == 0) { + steps--; + } + + Point2 off = dc.dst_rect.position + (dc.src_rect.position - dc.dst_rect.position).normalized() * (length - steps * dash) / 2.0; + + Vector points; + points.resize(steps + 1); + for (int i = 0; i < steps; i += 2) { + points.write[i] = (i == 0) ? dc.dst_rect.position : off; + points.write[i + 1] = (i == steps - 1) ? dc.src_rect.position : (off + step); + off += step * 2; + } + + Vector colors = { dc.modulate }; + + RenderingServer::get_singleton()->canvas_item_add_multiline(p_ci, points, colors, dc.data.operator Vector2().y, false); + } + } + } +} + +void TextServerFallback::_draw_list_draw(const RID &p_dc, const RID &p_ci, bool p_free) { + DrawList *list = list_owner.get_or_null(p_dc); + ERR_FAIL_NULL(list); + _imp_draw_list_draw(list, p_ci); + if (p_free) { + _free_rid(p_dc); + } +} + /*************************************************************************/ /* Shaped text buffer interface */ /*************************************************************************/ diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index 7cc029853305..84f72d3f31bf 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -488,11 +488,16 @@ class TextServerFallback : public TextServerExtension { Vector glyphs_logical; }; + struct DrawList { + LocalVector calls; + }; + // Common data. mutable RID_PtrOwner font_var_owner; mutable RID_PtrOwner font_owner; mutable RID_PtrOwner shaped_owner; + mutable RID_PtrOwner list_owner; _FORCE_INLINE_ FontFallback *_get_font_data(const RID &p_font_rid) const { RID rid = p_font_rid; @@ -595,6 +600,10 @@ class TextServerFallback : public TextServerExtension { void _realign(ShapedTextDataFallback *p_sd) const; _FORCE_INLINE_ RID _find_sys_font_for_text(const RID &p_fdef, const String &p_script_code, const String &p_language, const String &p_text); + void _imp_font_add_glyph_to_draw_list(const RID &p_font, int64_t p_layer, TextServerFallback::DrawList *p_list, const Transform2D &p_transform, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const; + void _imp_font_add_glyph_outline_to_draw_list(const RID &p_font, int64_t p_layer, TextServerFallback::DrawList *p_list, const Transform2D &p_transform, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const; + void _imp_draw_list_draw(TextServerFallback::DrawList *p_list, const RID &p_ci) const; + Mutex ft_mutex; protected: @@ -785,6 +794,9 @@ class TextServerFallback : public TextServerExtension { MODBIND7C(font_draw_glyph, const RID &, const RID &, int64_t, const Vector2 &, int64_t, const Color &, float); MODBIND8C(font_draw_glyph_outline, const RID &, const RID &, int64_t, int64_t, const Vector2 &, int64_t, const Color &, float); + MODBIND9C(font_add_glyph_to_draw_list, const RID &, int64_t, const RID &, const Transform2D &, int64_t, const Vector2 &, int64_t, const Color &, float); + MODBIND10C(font_add_glyph_outline_to_draw_list, const RID &, int64_t, const RID &, const Transform2D &, int64_t, int64_t, const Vector2 &, int64_t, const Color &, float); + MODBIND2RC(bool, font_is_language_supported, const RID &, const String &); MODBIND3(font_set_language_support_override, const RID &, const String &, bool); MODBIND2R(bool, font_get_language_support_override, const RID &, const String &); @@ -806,6 +818,18 @@ class TextServerFallback : public TextServerExtension { MODBIND1(reference_oversampling_level, double); MODBIND1(unreference_oversampling_level, double); + /* Draw list interface */ + + MODBIND0R(RID, create_draw_list); + MODBIND1(draw_list_sort, const RID &); + MODBIND2(draw_list_reserve, const RID &, int64_t); + MODBIND7C(draw_list_add_hexbox, const RID &, int64_t, const Transform2D &, const Vector2 &, int64_t, int64_t, const Color &); + MODBIND6C(draw_list_add_rect, const RID &, int64_t, const Transform2D &, const Rect2 &, bool, const Color &); + MODBIND8C(draw_list_add_line, const RID &, int64_t, const Transform2D &, const Point2 &, const Point2 &, float, float, const Color &); + MODBIND6C(draw_list_add_texture, const RID &, int64_t, const Transform2D &, RID, const Rect2 &, const Color &); + MODBIND4C(draw_list_add_custom, const RID &, int64_t, const Transform2D &, const Callable &); + MODBIND3(draw_list_draw, const RID &, const RID &, bool); + /* Shaped text buffer interface */ MODBIND2R(RID, create_shaped_text, Direction, Orientation); diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index de5173d0ea19..32d9d561b6c3 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -1342,9 +1342,8 @@ bool CodeEdit::is_drawing_executing_lines_gutter() const { return draw_executing_lines; } -void CodeEdit::_main_gutter_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) { +void CodeEdit::_main_gutter_draw_callback(RID p_ci, int p_line, int p_gutter, const Rect2 &p_region) { bool hovering = get_hovered_gutter() == Vector2i(main_gutter, p_line); - RID ci = get_text_canvas_item(); if (draw_breakpoints && theme_cache.breakpoint_icon.is_valid()) { bool breakpointed = is_line_breakpointed(p_line); bool shift_pressed = Input::get_singleton()->is_key_pressed(Key::SHIFT); @@ -1359,7 +1358,7 @@ void CodeEdit::_main_gutter_draw_callback(int p_line, int p_gutter, const Rect2 Rect2 icon_region = p_region; icon_region.position += Point2(padding, padding); icon_region.size -= Point2(padding, padding) * 2; - theme_cache.breakpoint_icon->draw_rect(ci, icon_region, false, use_color); + theme_cache.breakpoint_icon->draw_rect(p_ci, icon_region, false, use_color); } } @@ -1378,7 +1377,7 @@ void CodeEdit::_main_gutter_draw_callback(int p_line, int p_gutter, const Rect2 Rect2 icon_region = p_region; icon_region.position += Point2(horizontal_padding, 0); icon_region.size -= Point2(horizontal_padding * 1.1, vertical_padding); - theme_cache.bookmark_icon->draw_rect(ci, icon_region, false, use_color); + theme_cache.bookmark_icon->draw_rect(p_ci, icon_region, false, use_color); } } @@ -1389,7 +1388,7 @@ void CodeEdit::_main_gutter_draw_callback(int p_line, int p_gutter, const Rect2 Rect2 icon_region = p_region; icon_region.position += Point2(horizontal_padding, vertical_padding); icon_region.size -= Point2(horizontal_padding, vertical_padding) * 2; - theme_cache.executing_line_icon->draw_rect(ci, icon_region, false, theme_cache.executing_line_color); + theme_cache.executing_line_icon->draw_rect(p_ci, icon_region, false, theme_cache.executing_line_color); } } @@ -1532,7 +1531,7 @@ int CodeEdit::get_line_numbers_min_digits() const { return line_numbers_min_digits; } -void CodeEdit::_line_number_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) { +void CodeEdit::_line_number_draw_callback(RID p_ci, int p_line, int p_gutter, const Rect2 &p_region) { if (!Rect2(Vector2(0, 0), get_size()).intersects(p_region)) { return; } @@ -1571,7 +1570,7 @@ void CodeEdit::_line_number_draw_callback(int p_line, int p_gutter, const Rect2 number_color = theme_cache.line_number_color; } - TS->shaped_text_draw(text_rid, get_text_canvas_item(), ofs, -1, -1, number_color); + TS->shaped_text_draw(text_rid, p_ci, ofs, -1, -1, number_color); } void CodeEdit::_clear_line_number_text_cache() { @@ -1594,13 +1593,12 @@ bool CodeEdit::is_drawing_fold_gutter() const { return is_gutter_drawn(fold_gutter); } -void CodeEdit::_fold_gutter_draw_callback(int p_line, int p_gutter, Rect2 p_region) { +void CodeEdit::_fold_gutter_draw_callback(RID p_ci, int p_line, int p_gutter, Rect2 p_region) { if (!can_fold_line(p_line) && !is_line_folded(p_line)) { set_line_gutter_clickable(p_line, fold_gutter, false); return; } set_line_gutter_clickable(p_line, fold_gutter, true); - RID ci = get_text_canvas_item(); int horizontal_padding = p_region.size.x / 10; int vertical_padding = p_region.size.y / 6; @@ -1614,17 +1612,17 @@ void CodeEdit::_fold_gutter_draw_callback(int p_line, int p_gutter, Rect2 p_regi Color region_icon_color = theme_cache.folded_code_region_color; region_icon_color.a = MAX(region_icon_color.a, 0.4f); if (can_fold) { - theme_cache.can_fold_code_region_icon->draw_rect(ci, p_region, false, region_icon_color); + theme_cache.can_fold_code_region_icon->draw_rect(p_ci, p_region, false, region_icon_color); } else { - theme_cache.folded_code_region_icon->draw_rect(ci, p_region, false, region_icon_color); + theme_cache.folded_code_region_icon->draw_rect(p_ci, p_region, false, region_icon_color); } return; } if (can_fold) { - theme_cache.can_fold_icon->draw_rect(ci, p_region, false, theme_cache.code_folding_color); + theme_cache.can_fold_icon->draw_rect(p_ci, p_region, false, theme_cache.code_folding_color); return; } - theme_cache.folded_icon->draw_rect(ci, p_region, false, theme_cache.code_folding_color); + theme_cache.folded_icon->draw_rect(p_ci, p_region, false, theme_cache.code_folding_color); } /* Line Folding */ diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h index bda57af261c0..c9fee6b57442 100644 --- a/scene/gui/code_edit.h +++ b/scene/gui/code_edit.h @@ -96,7 +96,7 @@ class CodeEdit : public TextEdit { int main_gutter = -1; void _update_draw_main_gutter(); - void _main_gutter_draw_callback(int p_line, int p_gutter, const Rect2 &p_region); + void _main_gutter_draw_callback(RID p_ci, int p_line, int p_gutter, const Rect2 &p_region); // breakpoints HashMap breakpointed_lines; @@ -116,12 +116,12 @@ class CodeEdit : public TextEdit { HashMap line_number_text_cache; void _clear_line_number_text_cache(); void _update_line_number_gutter_width(); - void _line_number_draw_callback(int p_line, int p_gutter, const Rect2 &p_region); + void _line_number_draw_callback(RID p_ci, int p_line, int p_gutter, const Rect2 &p_region); /* Fold Gutter */ int fold_gutter = -1; bool draw_fold_gutter = false; - void _fold_gutter_draw_callback(int p_line, int p_gutter, Rect2 p_region); + void _fold_gutter_draw_callback(RID p_ci, int p_line, int p_gutter, Rect2 p_region); void _gutter_clicked(int p_line, int p_gutter); void _update_gutter_indexes(); diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 04387dcc99dd..625a19080ad7 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -388,30 +388,30 @@ void Label::_update_visible() const { } } -inline void draw_glyph(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_color, const Vector2 &p_ofs) { +inline void draw_glyph(const Glyph &p_gl, int p_layer, RID p_list, const Color &p_font_color, const Vector2 &p_ofs) { if (p_gl.font_rid != RID()) { - TS->font_draw_glyph(p_gl.font_rid, p_canvas, p_gl.font_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off), p_gl.index, p_font_color); + TS->font_add_glyph_to_draw_list(p_gl.font_rid, p_layer, p_list, Transform2D(), p_gl.font_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off), p_gl.index, p_font_color); } else if (((p_gl.flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) && ((p_gl.flags & TextServer::GRAPHEME_IS_EMBEDDED_OBJECT) != TextServer::GRAPHEME_IS_EMBEDDED_OBJECT)) { - TS->draw_hex_code_box(p_canvas, p_gl.font_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off), p_gl.index, p_font_color); + TS->draw_list_add_hexbox(p_list, -1, Transform2D(), p_ofs + Vector2(p_gl.x_off, p_gl.y_off), p_gl.index, p_gl.font_size, p_font_color); } } -inline void draw_glyph_shadow(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_shadow_color, const Vector2 &p_ofs, const Vector2 &shadow_ofs) { +inline void draw_glyph_shadow(const Glyph &p_gl, int p_layer, RID p_list, const Color &p_font_shadow_color, const Vector2 &p_ofs, const Vector2 &shadow_ofs) { if (p_gl.font_rid != RID()) { - TS->font_draw_glyph(p_gl.font_rid, p_canvas, p_gl.font_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + shadow_ofs, p_gl.index, p_font_shadow_color); + TS->font_add_glyph_to_draw_list(p_gl.font_rid, p_layer, p_list, Transform2D(), p_gl.font_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + shadow_ofs, p_gl.index, p_font_shadow_color); } } -inline void draw_glyph_shadow_outline(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_shadow_color, const Vector2 &p_ofs, int p_shadow_outline_size, const Vector2 &shadow_ofs) { +inline void draw_glyph_shadow_outline(const Glyph &p_gl, int p_layer, RID p_list, const Color &p_font_shadow_color, const Vector2 &p_ofs, int p_shadow_outline_size, const Vector2 &shadow_ofs) { if (p_gl.font_rid != RID()) { - TS->font_draw_glyph_outline(p_gl.font_rid, p_canvas, p_gl.font_size, p_shadow_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + shadow_ofs, p_gl.index, p_font_shadow_color); + TS->font_add_glyph_outline_to_draw_list(p_gl.font_rid, p_layer, p_list, Transform2D(), p_gl.font_size, p_shadow_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + shadow_ofs, p_gl.index, p_font_shadow_color); } } -inline void draw_glyph_outline(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_outline_color, const Vector2 &p_ofs, int p_outline_size) { +inline void draw_glyph_outline(const Glyph &p_gl, int p_layer, RID p_list, const Color &p_font_outline_color, const Vector2 &p_ofs, int p_outline_size) { if (p_gl.font_rid != RID()) { if (p_font_outline_color.a != 0.0 && p_outline_size > 0) { - TS->font_draw_glyph_outline(p_gl.font_rid, p_canvas, p_gl.font_size, p_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off), p_gl.index, p_font_outline_color); + TS->font_add_glyph_outline_to_draw_list(p_gl.font_rid, p_layer, p_list, Transform2D(), p_gl.font_size, p_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off), p_gl.index, p_font_outline_color); } } } @@ -787,6 +787,38 @@ void Label::_notification(int p_what) { int visible_glyphs = total_glyphs * visible_ratio; int line_index = 0; + int dc_call_count = 1; + if (font_shadow_color.a != 0 && shadow_outline_size > 0) { + dc_call_count++; + } + if (font_shadow_color.a > 0) { + dc_call_count++; + } + if (stacked_outline_datas.size() != 0) { + int draw_iterations = stacked_shadow_datas.size(); + for (int draw_iteration_index = draw_iterations - 1; draw_iteration_index >= 0; --draw_iteration_index) { + LabelSettings::StackedShadowData stacked_shadow_data = stacked_shadow_datas[draw_iteration_index]; + if (stacked_shadow_data.outline_size > 0) { + dc_call_count++; + } + dc_call_count++; + } + } + if (stacked_outline_datas.size() != 0) { + int draw_iterations = stacked_outline_datas.size(); + for (int draw_iteration_index = draw_iterations - 1; draw_iteration_index >= 0; --draw_iteration_index) { + LabelSettings::StackedOutlineData stacked_outline_data = stacked_outline_datas[draw_iteration_index]; + if (stacked_outline_data.size <= 0) { + continue; + } + dc_call_count++; + } + } + if (outline_size > 0 && font_outline_color.a != 0) { + dc_call_count++; + } + + RID draw_list = TS->create_draw_list(); for (int p = 0; p < paragraphs.size(); p++) { const Paragraph ¶ = paragraphs[p]; if (line_index + para.lines_rid.size() <= lines_skipped) { @@ -799,6 +831,8 @@ void Label::_notification(int p_what) { } bool rtl = (TS->shaped_text_get_inferred_direction(para.text_rid) == TextServer::DIRECTION_RTL); for (int i = start; i < end; i++) { + TS->draw_list_reserve(draw_list, +dc_call_count * (end - start)); + int priority = 0; RID line_rid = para.lines_rid[i]; Vector2 line_offset = _get_line_rect(p, i).position; ofs.x = line_offset.x; @@ -827,12 +861,12 @@ void Label::_notification(int p_what) { // Draw shadow outline. if (font_shadow_color.a != 0 && shadow_outline_size > 0) { - draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, ci, ofs, gl_size, trim_pos, glyphs, font_shadow_color, draw_glyph_shadow_outline, shadow_outline_size, shadow_ofs); + draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, priority++, draw_list, ofs, gl_size, trim_pos, glyphs, font_shadow_color, draw_glyph_shadow_outline, shadow_outline_size, shadow_ofs); } // Draw shadow. if (font_shadow_color.a > 0) { - draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, ci, ofs, gl_size, trim_pos, glyphs, font_shadow_color, draw_glyph_shadow, shadow_ofs); + draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, priority++, draw_list, ofs, gl_size, trim_pos, glyphs, font_shadow_color, draw_glyph_shadow, shadow_ofs); } // Draw stacked shadow. @@ -842,10 +876,10 @@ void Label::_notification(int p_what) { for (int draw_iteration_index = draw_iterations - 1; draw_iteration_index >= 0; --draw_iteration_index) { LabelSettings::StackedShadowData stacked_shadow_data = stacked_shadow_datas[draw_iteration_index]; if (stacked_shadow_data.outline_size > 0) { - draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, ci, ofs, gl_size, trim_pos, glyphs, stacked_shadow_data.color, draw_glyph_shadow_outline, stacked_shadow_data.outline_size, stacked_shadow_data.offset); + draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, priority++, draw_list, ofs, gl_size, trim_pos, glyphs, stacked_shadow_data.color, draw_glyph_shadow_outline, stacked_shadow_data.outline_size, stacked_shadow_data.offset); } - draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, ci, ofs, gl_size, trim_pos, glyphs, stacked_shadow_data.color, draw_glyph_shadow, stacked_shadow_data.offset); + draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, priority++, draw_list, ofs, gl_size, trim_pos, glyphs, stacked_shadow_data.color, draw_glyph_shadow, stacked_shadow_data.offset); } } @@ -868,19 +902,19 @@ void Label::_notification(int p_what) { if (stacked_outline_data.size <= 0) { continue; } - draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, ci, ofs, gl_size, trim_pos, glyphs, stacked_outline_data.color, draw_glyph_outline, stacked_outline_draw_size); + draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, priority++, draw_list, ofs, gl_size, trim_pos, glyphs, stacked_outline_data.color, draw_glyph_outline, stacked_outline_draw_size); stacked_outline_draw_size -= stacked_outline_data.size; } } // Draw outline. if (outline_size > 0 && font_outline_color.a != 0) { - draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, ci, ofs, gl_size, trim_pos, glyphs, font_outline_color, draw_glyph_outline, outline_size); + draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, priority++, draw_list, ofs, gl_size, trim_pos, glyphs, font_outline_color, draw_glyph_outline, outline_size); } // Draw text. { - draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, ci, ofs, gl_size, trim_pos, glyphs, font_color, draw_glyph); + draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, priority++, draw_list, ofs, gl_size, trim_pos, glyphs, font_color, draw_glyph); } processed_glyphs = processed_glyphs_step; @@ -890,6 +924,8 @@ void Label::_notification(int p_what) { line_index += para.lines_rid.size(); } } + TS->draw_list_sort(draw_list); + TS->draw_list_draw(draw_list, ci, true); } break; case NOTIFICATION_THEME_CHANGED: { diff --git a/scene/gui/label.h b/scene/gui/label.h index be05c98e0eaa..950ebd02b29d 100644 --- a/scene/gui/label.h +++ b/scene/gui/label.h @@ -194,7 +194,7 @@ class Label : public Control { ~Label(); template - void draw_text(bool p_rtl, int p_ellipsis_pos, int p_ellipsis_gl_size, const Glyph *p_ellipsis_glyphs, bool p_trim_chars, int p_para_start, int p_visible_chars, bool p_trim_glyphs_ltr, int &p_processed_glyphs_step, int p_processed_glyphs, int p_visible_glyphs, bool p_trim_glyphs_rtl, int p_total_glyphs, const RID &p_ci, const Vector2 &p_ofs, int p_gl_size, int p_trim_pos, const Glyph *p_glyphs, const Color &p_color, void (*p_draw_func)(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_outline_color, const Vector2 &p_ofs, VarArgsFunc... p_args), VarArgs &&...p_args) { + void draw_text(bool p_rtl, int p_ellipsis_pos, int p_ellipsis_gl_size, const Glyph *p_ellipsis_glyphs, bool p_trim_chars, int p_para_start, int p_visible_chars, bool p_trim_glyphs_ltr, int &p_processed_glyphs_step, int p_processed_glyphs, int p_visible_glyphs, bool p_trim_glyphs_rtl, int p_total_glyphs, int p_layer, RID p_list, const Vector2 &p_ofs, int p_gl_size, int p_trim_pos, const Glyph *p_glyphs, const Color &p_color, void (*p_draw_func)(const Glyph &p_gl, int p_layer, RID p_list, const Color &p_font_outline_color, const Vector2 &p_ofs, VarArgsFunc... p_args), VarArgs &&...p_args) { p_processed_glyphs_step = p_processed_glyphs; Vector2 offset_step = p_ofs; /* Draw RTL ellipsis string when necessary. */ if (p_rtl && p_ellipsis_pos >= 0) { @@ -202,7 +202,7 @@ class Label : public Control { for (int j = 0; j < p_ellipsis_glyphs[gl_idx].repeat; j++) { bool skip = (p_trim_chars && p_ellipsis_glyphs[gl_idx].end + p_para_start > p_visible_chars) || (p_trim_glyphs_ltr && (p_processed_glyphs_step >= p_visible_glyphs)) || (p_trim_glyphs_rtl && (p_processed_glyphs_step < p_total_glyphs - p_visible_glyphs)); if (!skip) { - p_draw_func(p_ellipsis_glyphs[gl_idx], p_ci, p_color, offset_step, std::forward(p_args)...); + p_draw_func(p_ellipsis_glyphs[gl_idx], p_layer, p_list, p_color, offset_step, std::forward(p_args)...); } p_processed_glyphs_step++; offset_step.x += p_ellipsis_glyphs[gl_idx].advance; @@ -224,7 +224,7 @@ class Label : public Control { for (int k = 0; k < p_glyphs[j].repeat; k++) { bool skip = (p_trim_chars && p_glyphs[j].end + p_para_start > p_visible_chars) || (p_trim_glyphs_ltr && (p_processed_glyphs_step >= p_visible_glyphs)) || (p_trim_glyphs_rtl && (p_processed_glyphs_step < p_total_glyphs - p_visible_glyphs)); if (!skip) { - p_draw_func(p_glyphs[j], p_ci, p_color, offset_step, std::forward(p_args)...); + p_draw_func(p_glyphs[j], p_layer, p_list, p_color, offset_step, std::forward(p_args)...); } p_processed_glyphs_step++; offset_step.x += p_glyphs[j].advance; @@ -235,7 +235,7 @@ class Label : public Control { for (int j = 0; j < p_ellipsis_glyphs[gl_idx].repeat; j++) { bool skip = (p_trim_chars && p_ellipsis_glyphs[gl_idx].end + p_para_start > p_visible_chars) || (p_trim_glyphs_ltr && (p_processed_glyphs_step >= p_visible_glyphs)) || (p_trim_glyphs_rtl && (p_processed_glyphs_step < p_total_glyphs - p_visible_glyphs)); if (!skip) { - p_draw_func(p_ellipsis_glyphs[gl_idx], p_ci, p_color, offset_step, std::forward(p_args)...); + p_draw_func(p_ellipsis_glyphs[gl_idx], p_layer, p_list, p_color, offset_step, std::forward(p_args)...); } p_processed_glyphs_step++; offset_step.x += p_ellipsis_glyphs[gl_idx].advance; diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 69ef0bde1e45..e3989c2032c1 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -1470,33 +1470,24 @@ void LineEdit::_notification(int p_what) { int gl_size = TS->shaped_text_get_glyph_count(text_rid); // Draw text. + RID draw_list = TS->create_draw_list(); + ofs.y += TS->shaped_text_get_ascent(text_rid); Color font_outline_color = theme_cache.font_outline_color; int outline_size = theme_cache.font_outline_size; - if (outline_size > 0 && font_outline_color.a > 0) { - Vector2 oofs = ofs; - for (int i = 0; i < gl_size; i++) { - for (int j = 0; j < glyphs[i].repeat; j++) { - if (std::ceil(oofs.x) >= x_ofs && (oofs.x + glyphs[i].advance) <= ofs_max) { - if (glyphs[i].font_rid != RID()) { - TS->font_draw_glyph_outline(glyphs[i].font_rid, ci, glyphs[i].font_size, outline_size, oofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, font_outline_color); - } - } - oofs.x += glyphs[i].advance; - } - if (oofs.x >= ofs_max) { - break; - } - } - } + + TS->draw_list_reserve(draw_list, gl_size * (outline_size > 0 && font_outline_color.a > 0 ? 2 : 1)); for (int i = 0; i < gl_size; i++) { bool selected = selection.enabled && glyphs[i].start >= selection.begin && glyphs[i].end <= selection.end; for (int j = 0; j < glyphs[i].repeat; j++) { if (std::ceil(ofs.x) >= x_ofs && (ofs.x + glyphs[i].advance) <= ofs_max) { if (glyphs[i].font_rid != RID()) { - TS->font_draw_glyph(glyphs[i].font_rid, ci, glyphs[i].font_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, selected ? font_selected_color : font_color); + if (outline_size > 0 && font_outline_color.a > 0) { + TS->font_add_glyph_outline_to_draw_list(glyphs[i].font_rid, DRAW_STEP_OUTLINE, draw_list, Transform2D(), glyphs[i].font_size, outline_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, font_outline_color); + } + TS->font_add_glyph_to_draw_list(glyphs[i].font_rid, DRAW_STEP_TEXT, draw_list, Transform2D(), glyphs[i].font_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, selected ? font_selected_color : font_color); } else if (((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) && ((glyphs[i].flags & TextServer::GRAPHEME_IS_EMBEDDED_OBJECT) != TextServer::GRAPHEME_IS_EMBEDDED_OBJECT)) { - TS->draw_hex_code_box(ci, glyphs[i].font_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, selected ? font_selected_color : font_color); + TS->draw_list_add_hexbox(draw_list, DRAW_STEP_TEXT, Transform2D(), ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, glyphs[i].font_size, selected ? font_selected_color : font_color); } } ofs.x += glyphs[i].advance; @@ -1505,6 +1496,8 @@ void LineEdit::_notification(int p_what) { break; } } + TS->draw_list_sort(draw_list); + TS->draw_list_draw(draw_list, ci, true); // Draw carets. ofs.x = x_ofs + scroll_offset; diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index c8ca6c7e3bbf..1018acf9e502 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -84,6 +84,11 @@ class LineEdit : public Control { }; private: + enum LEDrawStep { + DRAW_STEP_OUTLINE, + DRAW_STEP_TEXT, + }; + HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT; bool editing = false; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index dd66583459d3..d7d7864b9cfd 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -919,10 +919,12 @@ void RichTextLabel::_set_table_size(ItemTable *p_table, int p_available_width) { } } -int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, float p_vsep, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs, int &r_processed_glyphs) { +int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, float p_vsep, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs, int &r_processed_glyphs, RID p_list, int p_layer) { ERR_FAIL_NULL_V(p_frame, 0); ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)p_frame->lines.size(), 0); + int first_layer = DRAW_STEP_MAX * p_layer; + Vector2 off; Line &l = p_frame->lines[p_line]; @@ -934,7 +936,6 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o return 0; } - RID ci = get_canvas_item(); bool rtl = (l.text_buf->get_direction() == TextServer::DIRECTION_RTL); bool lrtl = is_layout_rtl(); @@ -950,9 +951,9 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o bool skip_dc = (trim_chars && l.char_offset > visible_characters) || (trim_glyphs_ltr && (r_processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (r_processed_glyphs < total_glyphs - visible_glyphs)); if (!skip_dc) { if (l.dc_ol_size > 0) { - l.text_buf->draw_dropcap_outline(ci, p_ofs + ((rtl) ? Vector2() : Vector2(l.offset.x, 0)), l.dc_ol_size, l.dc_ol_color); + l.text_buf->add_dropcap_outline_to_draw_list(DRAW_STEP_OUTLINE, p_list, Transform2D(), p_ofs + ((rtl) ? Vector2() : Vector2(l.offset.x, 0)), l.dc_ol_size, l.dc_ol_color); } - l.text_buf->draw_dropcap(ci, p_ofs + ((rtl) ? Vector2() : Vector2(l.offset.x, 0)), l.dc_color); + l.text_buf->add_dropcap_to_draw_list(DRAW_STEP_TEXT, p_list, Transform2D(), p_ofs + ((rtl) ? Vector2() : Vector2(l.offset.x, 0)), l.dc_color); } const Ref &text_buf = l.text_buf_disp.is_valid() ? l.text_buf_disp : l.text_buf; @@ -1020,20 +1021,20 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o Color font_shadow_color = p_font_shadow_color * Color(1, 1, 1, font_color.a); if (rtl) { if (p_shadow_outline_size > 0 && font_shadow_color.a != 0.0) { - l.text_prefix->draw_outline(ci, p_ofs + Vector2(off.x + length, 0) + p_shadow_ofs, p_shadow_outline_size, font_shadow_color); + l.text_prefix->add_outline_to_draw_list(DRAW_STEP_SHADOW_OUTLINE, p_list, Transform2D(), p_ofs + Vector2(off.x + length, 0) + p_shadow_ofs, p_shadow_outline_size, font_shadow_color); } if (outline_size > 0 && font_outline_color.a != 0.0) { - l.text_prefix->draw_outline(ci, p_ofs + Vector2(off.x + length, 0), outline_size, font_outline_color); + l.text_prefix->add_outline_to_draw_list(DRAW_STEP_OUTLINE, p_list, Transform2D(), p_ofs + Vector2(off.x + length, 0), outline_size, font_outline_color); } - l.text_prefix->draw(ci, p_ofs + Vector2(off.x + length, 0), font_color); + l.text_prefix->add_to_draw_list(DRAW_STEP_TEXT, p_list, Transform2D(), p_ofs + Vector2(off.x + length, 0), font_color); } else { if (p_shadow_outline_size > 0 && font_shadow_color.a != 0.0) { - l.text_prefix->draw_outline(ci, p_ofs + Vector2(off.x - l.text_prefix->get_size().x, 0) + p_shadow_ofs, p_shadow_outline_size, font_shadow_color); + l.text_prefix->add_outline_to_draw_list(DRAW_STEP_SHADOW_OUTLINE, p_list, Transform2D(), p_ofs + Vector2(off.x - l.text_prefix->get_size().x, 0) + p_shadow_ofs, p_shadow_outline_size, font_shadow_color); } if (outline_size > 0 && font_outline_color.a != 0.0) { - l.text_prefix->draw_outline(ci, p_ofs + Vector2(off.x - l.text_prefix->get_size().x, 0), outline_size, font_outline_color); + l.text_prefix->add_outline_to_draw_list(DRAW_STEP_OUTLINE, p_list, Transform2D(), p_ofs + Vector2(off.x - l.text_prefix->get_size().x, 0), outline_size, font_outline_color); } - l.text_prefix->draw(ci, p_ofs + Vector2(off.x - l.text_prefix->get_size().x, 0), font_color); + l.text_prefix->add_to_draw_list(DRAW_STEP_TEXT, p_list, Transform2D(), p_ofs + Vector2(off.x - l.text_prefix->get_size().x, 0), font_color); } } @@ -1090,10 +1091,10 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o if (img->pad) { Size2 pad_size = rect.size.min(img->image->get_size()); Vector2 pad_off = (rect.size - pad_size) / 2; - img->image->draw_rect(ci, Rect2(p_ofs + rect.position + off + pad_off, pad_size), false, img->color); + TS->draw_list_add_texture(p_list, first_layer + DRAW_STEP_TEXT, Transform2D(), img->image->get_rid(), Rect2(p_ofs + rect.position + off + pad_off, pad_size), img->color); visible_rect = _merge_or_copy_rect(visible_rect, Rect2(p_ofs + rect.position + off + pad_off, pad_size)); } else { - img->image->draw_rect(ci, Rect2(p_ofs + rect.position + off, rect.size), false, img->color); + TS->draw_list_add_texture(p_list, first_layer + DRAW_STEP_TEXT, Transform2D(), img->image->get_rid(), Rect2(p_ofs + rect.position + off, rect.size), img->color); visible_rect = _merge_or_copy_rect(visible_rect, Rect2(p_ofs + rect.position + off, rect.size)); } } break; @@ -1124,22 +1125,22 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o if (row % 2 == 0) { Color c = frame->odd_row_bg != Color(0, 0, 0, 0) ? frame->odd_row_bg : odd_row_bg; if (c.a > 0.0) { - draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position - Vector2(h_separation * 0.5, v_separation * 0.5).floor(), Size2(table->columns[col].width + h_separation + frame->padding.position.x + frame->padding.size.x, table->rows_no_padding[row] + frame->padding.position.y + frame->padding.size.y)), c, true); + TS->draw_list_add_rect(p_list, first_layer + DRAW_STEP_BACKGROUND, Transform2D(), Rect2(p_ofs + rect.position + off + coff - frame->padding.position - Vector2(h_separation * 0.5, v_separation * 0.5).floor(), Size2(table->columns[col].width + h_separation + frame->padding.position.x + frame->padding.size.x, table->rows_no_padding[row] + frame->padding.position.y + frame->padding.size.y)), true, c); } } else { Color c = frame->even_row_bg != Color(0, 0, 0, 0) ? frame->even_row_bg : even_row_bg; if (c.a > 0.0) { - draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position - Vector2(h_separation * 0.5, v_separation * 0.5).floor(), Size2(table->columns[col].width + h_separation + frame->padding.position.x + frame->padding.size.x, table->rows_no_padding[row] + frame->padding.position.y + frame->padding.size.y)), c, true); + TS->draw_list_add_rect(p_list, first_layer + DRAW_STEP_BACKGROUND, Transform2D(), Rect2(p_ofs + rect.position + off + coff - frame->padding.position - Vector2(h_separation * 0.5, v_separation * 0.5).floor(), Size2(table->columns[col].width + h_separation + frame->padding.position.x + frame->padding.size.x, table->rows_no_padding[row] + frame->padding.position.y + frame->padding.size.y)), true, c); } } Color bc = frame->border != Color(0, 0, 0, 0) ? frame->border : border; if (bc.a > 0.0) { - draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position - Vector2(h_separation * 0.5, v_separation * 0.5).floor(), Size2(table->columns[col].width + h_separation + frame->padding.position.x + frame->padding.size.x, table->rows_no_padding[row] + frame->padding.position.y + frame->padding.size.y)), bc, false); + TS->draw_list_add_rect(p_list, first_layer + DRAW_STEP_BACKGROUND, Transform2D(), Rect2(p_ofs + rect.position + off + coff - frame->padding.position - Vector2(h_separation * 0.5, v_separation * 0.5).floor(), Size2(table->columns[col].width + h_separation + frame->padding.position.x + frame->padding.size.x, table->rows_no_padding[row] + frame->padding.position.y + frame->padding.size.y)), false, bc); } } for (int j = 0; j < (int)frame->lines.size(); j++) { - _draw_line(frame, j, p_ofs + rect.position + off + Vector2(0, frame->lines[j].offset.y), rect.size.x, 0, p_base_color, p_outline_size, p_outline_color, p_font_shadow_color, p_shadow_outline_size, p_shadow_ofs, r_processed_glyphs); + _draw_line(frame, j, p_ofs + rect.position + off + Vector2(0, frame->lines[j].offset.y), rect.size.x, 0, p_base_color, p_outline_size, p_outline_color, p_font_shadow_color, p_shadow_outline_size, p_shadow_ofs, r_processed_glyphs, p_list, p_layer + 1); } idx++; } @@ -1239,7 +1240,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o if (ul_started && new_ul_color != ul_color_prev) { float y_off = upos; float underline_width = MAX(1.0, uth * theme_cache.base_scale); - draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), ul_color, underline_width); + TS->draw_list_add_line(p_list, first_layer + DRAW_STEP_BACKGROUND, Transform2D(), ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), underline_width, 0.0, ul_color); ul_start = p_ofs + Vector2(off_step.x, off_step.y); ul_color = new_ul_color; ul_color_prev = new_ul_color; @@ -1253,13 +1254,13 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o ul_started = false; float y_off = upos; float underline_width = MAX(1.0, uth * theme_cache.base_scale); - draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), ul_color, underline_width); + TS->draw_list_add_line(p_list, first_layer + DRAW_STEP_BACKGROUND, Transform2D(), ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), underline_width, 0.0, ul_color); } if (_find_hint(it, nullptr) && underline_hint) { if (dot_ul_started && font_color != dot_ul_color_prev) { float y_off = upos; float underline_width = MAX(1.0, uth * theme_cache.base_scale); - draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2)); + TS->draw_list_add_line(p_list, first_layer + DRAW_STEP_BACKGROUND, Transform2D(), dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), underline_width, MAX(2.0, underline_width * 2), dot_ul_color); dot_ul_start = p_ofs + Vector2(off_step.x, off_step.y); dot_ul_color_prev = font_color; dot_ul_color = font_color; @@ -1275,7 +1276,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o dot_ul_started = false; float y_off = upos; float underline_width = MAX(1.0, uth * theme_cache.base_scale); - draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2)); + TS->draw_list_add_line(p_list, first_layer + DRAW_STEP_BACKGROUND, Transform2D(), dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), underline_width, MAX(2.0, underline_width * 2), dot_ul_color); } Color user_st_color = Color(0, 0, 0, 0); if (_find_strikethrough(it, &user_st_color)) { @@ -1289,7 +1290,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o if (st_started && new_st_color != st_color_prev) { float y_off = -l_ascent + l_size.y / 2; float underline_width = MAX(1.0, uth * theme_cache.base_scale); - draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), st_color, underline_width); + TS->draw_list_add_line(p_list, first_layer + DRAW_STEP_FOREGROUND, Transform2D(), st_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), underline_width, 0.0, st_color); st_start = p_ofs + Vector2(off_step.x, off_step.y); st_color = new_st_color; st_color_prev = new_st_color; @@ -1303,7 +1304,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o st_started = false; float y_off = -l_ascent + l_size.y / 2; float underline_width = MAX(1.0, uth * theme_cache.base_scale); - draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), st_color, underline_width); + TS->draw_list_add_line(p_list, first_layer + DRAW_STEP_FOREGROUND, Transform2D(), st_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), underline_width, 0.0, st_color); } } if (step == DRAW_STEP_SHADOW_OUTLINE || step == DRAW_STEP_SHADOW || step == DRAW_STEP_OUTLINE || step == DRAW_STEP_TEXT) { @@ -1436,27 +1437,25 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o Vector2 char_off = char_xform.get_origin(); Transform2D char_reverse_xform; + Transform2D char_final_xform; if (step == DRAW_STEP_TEXT) { if (selected && use_selected_font_color) { font_color = theme_cache.font_selected_color; } char_reverse_xform.set_origin(-char_off); - Transform2D char_final_xform = char_xform * char_reverse_xform; - draw_set_transform_matrix(char_final_xform); + char_final_xform = char_xform * char_reverse_xform; } else if (step == DRAW_STEP_SHADOW_OUTLINE || step == DRAW_STEP_SHADOW) { font_color = font_shadow_color * Color(1, 1, 1, font_color.a); char_reverse_xform.set_origin(-char_off - p_shadow_ofs); - Transform2D char_final_xform = char_xform * char_reverse_xform; + char_final_xform = char_xform * char_reverse_xform; char_final_xform.columns[2] += p_shadow_ofs; - draw_set_transform_matrix(char_final_xform); } else if (step == DRAW_STEP_OUTLINE) { font_color = font_outline_color * Color(1, 1, 1, font_color.a); char_reverse_xform.set_origin(-char_off); - Transform2D char_final_xform = char_xform * char_reverse_xform; - draw_set_transform_matrix(char_final_xform); + char_final_xform = char_xform * char_reverse_xform; } // Draw glyphs. @@ -1468,16 +1467,16 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o visible_rect = _merge_or_copy_rect(visible_rect, Rect2i(fx_offset + char_off - Vector2i(0, l_ascent), Point2i(glyphs[i].advance, l_size.y))); if (step == DRAW_STEP_TEXT) { if (frid != RID()) { - TS->font_draw_glyph(frid, ci, glyphs[i].font_size, fx_offset + char_off, gl, font_color); + TS->font_add_glyph_to_draw_list(frid, step, p_list, char_final_xform, glyphs[i].font_size, fx_offset + char_off, gl, font_color); } else if (((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) && ((glyphs[i].flags & TextServer::GRAPHEME_IS_EMBEDDED_OBJECT) != TextServer::GRAPHEME_IS_EMBEDDED_OBJECT)) { - TS->draw_hex_code_box(ci, glyphs[i].font_size, fx_offset + char_off, gl, font_color); + TS->draw_list_add_hexbox(p_list, first_layer + DRAW_STEP_TEXT, char_final_xform, fx_offset + char_off, gl, glyphs[i].font_size, font_color); } } else if (step == DRAW_STEP_SHADOW_OUTLINE && frid != RID()) { - TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, p_shadow_outline_size, fx_offset + char_off + p_shadow_ofs, gl, font_color); + TS->font_add_glyph_outline_to_draw_list(frid, step, p_list, char_final_xform, glyphs[i].font_size, p_shadow_outline_size, fx_offset + char_off + p_shadow_ofs, gl, font_color); } else if (step == DRAW_STEP_SHADOW && frid != RID()) { - TS->font_draw_glyph(frid, ci, glyphs[i].font_size, fx_offset + char_off + p_shadow_ofs, gl, font_color); + TS->font_add_glyph_to_draw_list(frid, step, p_list, char_final_xform, glyphs[i].font_size, fx_offset + char_off + p_shadow_ofs, gl, font_color); } else if (step == DRAW_STEP_OUTLINE && frid != RID()) { - TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, outline_size, fx_offset + char_off, gl, font_color); + TS->font_add_glyph_outline_to_draw_list(frid, step, p_list, char_final_xform, glyphs[i].font_size, outline_size, fx_offset + char_off, gl, font_color); } } } @@ -1488,24 +1487,23 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o ul_started = false; float y_off = upos; float underline_width = MAX(1.0, uth * theme_cache.base_scale); - draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), ul_color, underline_width); + TS->draw_list_add_line(p_list, first_layer + DRAW_STEP_BACKGROUND, Transform2D(), ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), underline_width, 0.0, ul_color); } if (dot_ul_started) { dot_ul_started = false; float y_off = upos; float underline_width = MAX(1.0, uth * theme_cache.base_scale); - draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2)); + TS->draw_list_add_line(p_list, first_layer + DRAW_STEP_BACKGROUND, Transform2D(), dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), underline_width, MAX(2.0, underline_width * 2), dot_ul_color); } if (st_started) { st_started = false; float y_off = -l_ascent + l_size.y / 2; float underline_width = MAX(1.0, uth * theme_cache.base_scale); - draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), st_color, underline_width); + TS->draw_list_add_line(p_list, first_layer + DRAW_STEP_FOREGROUND, Transform2D(), st_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), underline_width, 0.0, st_color); } } off_step.x += glyphs[i].advance; } - draw_set_transform_matrix(Transform2D()); } // Draw boxes. if (step == DRAW_STEP_BACKGROUND || step == DRAW_STEP_FOREGROUND) { @@ -1522,7 +1520,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o if (last_color.a > 0.0) { Vector2 rect_off = p_ofs + Vector2(box_start - theme_cache.text_highlight_h_padding, off_step.y - l_ascent - theme_cache.text_highlight_v_padding); Vector2 rect_size = Vector2(off_step.x - box_start + 2 * theme_cache.text_highlight_h_padding, l_size.y + 2 * theme_cache.text_highlight_v_padding); - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(rect_off, rect_size), last_color); + TS->draw_list_add_rect(p_list, first_layer + step, Transform2D(), Rect2(rect_off, rect_size), true, last_color); } if (color.a > 0.0) { box_start = off_step.x; @@ -1534,7 +1532,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o if (last_color.a > 0.0) { Vector2 rect_off = p_ofs + Vector2(box_start - theme_cache.text_highlight_h_padding, off_step.y - l_ascent - theme_cache.text_highlight_v_padding); Vector2 rect_size = Vector2(off_step.x - box_start + 2 * theme_cache.text_highlight_h_padding, l_size.y + 2 * theme_cache.text_highlight_v_padding); - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(rect_off, rect_size), last_color); + TS->draw_list_add_rect(p_list, first_layer + step, Transform2D(), Rect2(rect_off, rect_size), true, last_color); } last_color = Color(0, 0, 0, 0); } @@ -1548,7 +1546,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o if (last_color.a > 0.0) { Vector2 rect_off = p_ofs + Vector2(box_start - theme_cache.text_highlight_h_padding, off_step.y - l_ascent - theme_cache.text_highlight_v_padding); Vector2 rect_size = Vector2(off_step.x - box_start + 2 * theme_cache.text_highlight_h_padding, l_size.y + 2 * theme_cache.text_highlight_v_padding); - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(rect_off, rect_size), last_color); + TS->draw_list_add_rect(p_list, first_layer + step, Transform2D(), Rect2(rect_off, rect_size), true, last_color); } } if (step == DRAW_STEP_BACKGROUND) { @@ -1557,7 +1555,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o Vector sel = TS->shaped_text_get_selection(rid, sel_start, sel_end); for (int i = 0; i < sel.size(); i++) { Rect2 rect = Rect2(sel[i].x + p_ofs.x + off.x, p_ofs.y + off.y - l_ascent, sel[i].y - sel[i].x, l_size.y); // Note: use "off" not "off_step", selection is relative to the line start. - RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, selection_bg); + TS->draw_list_add_rect(p_list, first_layer + step, Transform2D(), rect, true, selection_bg); } } } @@ -1566,19 +1564,19 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o ul_started = false; float y_off = upos; float underline_width = MAX(1.0, uth * theme_cache.base_scale); - draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), ul_color, underline_width); + TS->draw_list_add_line(p_list, first_layer + DRAW_STEP_BACKGROUND, Transform2D(), ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), underline_width, 0.0, ul_color); } if (dot_ul_started) { dot_ul_started = false; float y_off = upos; float underline_width = MAX(1.0, uth * theme_cache.base_scale); - draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2)); + TS->draw_list_add_line(p_list, first_layer + DRAW_STEP_BACKGROUND, Transform2D(), dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), underline_width, MAX(2.0, underline_width * 2), dot_ul_color); } if (st_started) { st_started = false; float y_off = -l_ascent + l_size.y / 2; float underline_width = MAX(1.0, uth * theme_cache.base_scale); - draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), st_color, underline_width); + TS->draw_list_add_line(p_list, first_layer + DRAW_STEP_FOREGROUND, Transform2D(), st_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), underline_width, 0.0, st_color); } } } @@ -2647,10 +2645,11 @@ void RichTextLabel::_notification(int p_what) { // New cache draw. Point2 ofs = text_rect.get_position() + Vector2(0, vbegin + main->lines[from_line].offset.y - vofs); int processed_glyphs = 0; + RID draw_list = TS->create_draw_list(); while (ofs.y < size.height - v_limit && from_line < to_line) { MutexLock lock(main->lines[from_line].text_buf->get_mutex()); - int drawn_lines = _draw_line(main, from_line, ofs, text_rect.size.x, vsep, theme_cache.default_color, theme_cache.outline_size, theme_cache.font_outline_color, theme_cache.font_shadow_color, theme_cache.shadow_outline_size, shadow_ofs, processed_glyphs); + int drawn_lines = _draw_line(main, from_line, ofs, text_rect.size.x, vsep, theme_cache.default_color, theme_cache.outline_size, theme_cache.font_outline_color, theme_cache.font_shadow_color, theme_cache.shadow_outline_size, shadow_ofs, processed_glyphs, draw_list, 0); visible_line_count += drawn_lines; if (drawn_lines > 0) { visible_paragraph_count++; @@ -2662,6 +2661,9 @@ void RichTextLabel::_notification(int p_what) { scroll_visible = follow_vc_pos > 0; vscroll->set_visible(follow_vc_pos > 0); } + TS->draw_list_sort(draw_list); + TS->draw_list_draw(draw_list, ci, true); + if (has_focus() && get_tree()->is_accessibility_enabled()) { RID ae; if (keyboard_focus_frame && keyboard_focus_item) { diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 07fc3961da98..88ba3932e7a0 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -674,7 +674,7 @@ class RichTextLabel : public Control { void _set_table_size(ItemTable *p_table, int p_available_width); void _update_line_font(ItemFrame *p_frame, int p_line, const Ref &p_base_font, int p_base_font_size); - int _draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, float p_vsep, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs, int &r_processed_glyphs); + int _draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, float p_vsep, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs, int &r_processed_glyphs, RID p_list, int p_layer); float _find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, float p_vsep, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool p_table = false, bool p_meta = false); void _accessibility_update_line(RID p_id, ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, float p_vsep); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 5d247e2f9c4c..810280bf7c86 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1324,6 +1324,8 @@ void TextEdit::_notification(int p_what) { _draw_guidelines(); // Draw main text. + RID draw_list = TS->create_draw_list(); + line_drawing_cache.clear(); int row_height = draw_placeholder ? placeholder_line_height + theme_cache.line_spacing : get_line_height(); int line = first_vis_line; @@ -1398,18 +1400,18 @@ void TextEdit::_notification(int p_what) { if (text.get_line_background_color(line).a > 0.0) { if (rtl) { - RS::get_singleton()->canvas_item_add_rect(text_ci, Rect2(size.width - xmargin_end, ofs_y, xmargin_end - xmargin_beg, row_height), text.get_line_background_color(line)); + TS->draw_list_add_rect(draw_list, DRAW_STEP_BACKGROUND, Transform2D(), Rect2(size.width - xmargin_end, ofs_y, xmargin_end - xmargin_beg, row_height), true, text.get_line_background_color(line)); } else { - RS::get_singleton()->canvas_item_add_rect(text_ci, Rect2(xmargin_beg, ofs_y, xmargin_end - xmargin_beg, row_height), text.get_line_background_color(line)); + TS->draw_list_add_rect(draw_list, DRAW_STEP_BACKGROUND, Transform2D(), Rect2(xmargin_beg, ofs_y, xmargin_end - xmargin_beg, row_height), true, text.get_line_background_color(line)); } } // Draw current line highlight. if (highlight_current_line && highlighted_lines.has(Pair(line, line_wrap_index))) { if (rtl) { - RS::get_singleton()->canvas_item_add_rect(text_ci, Rect2(size.width - xmargin_end, ofs_y, xmargin_end - xmargin_beg, row_height), theme_cache.current_line_color); + TS->draw_list_add_rect(draw_list, DRAW_STEP_BACKGROUND, Transform2D(), Rect2(size.width - xmargin_end, ofs_y, xmargin_end - xmargin_beg, row_height), true, theme_cache.current_line_color); } else { - RS::get_singleton()->canvas_item_add_rect(text_ci, Rect2(xmargin_beg, ofs_y, xmargin_end - xmargin_beg, row_height), theme_cache.current_line_color); + TS->draw_list_add_rect(draw_list, DRAW_STEP_BACKGROUND, Transform2D(), Rect2(xmargin_beg, ofs_y, xmargin_end - xmargin_beg, row_height), true, theme_cache.current_line_color); } } @@ -1439,9 +1441,9 @@ void TextEdit::_notification(int p_what) { int yofs = ofs_y + (row_height - tl->get_size().y) / 2; if (theme_cache.outline_size > 0 && theme_cache.outline_color.a > 0) { - tl->draw_outline(text_ci, Point2(gutter_offset, yofs), theme_cache.outline_size, theme_cache.outline_color); + tl->add_outline_to_draw_list(DRAW_STEP_TEXT_OUTLINE, draw_list, Transform2D(), Point2(gutter_offset, yofs), theme_cache.outline_size, theme_cache.outline_color); } - tl->draw(text_ci, Point2(gutter_offset, yofs), get_line_gutter_item_color(line, g)); + tl->add_to_draw_list(DRAW_STEP_TEXT, draw_list, Transform2D(), Point2(gutter_offset, yofs), get_line_gutter_item_color(line, g)); } break; case GUTTER_TYPE_ICON: { const Ref icon = get_line_gutter_icon(line, g); @@ -1469,7 +1471,7 @@ void TextEdit::_notification(int p_what) { gutter_rect.position.x = size.width - gutter_rect.position.x - gutter_rect.size.x; } - icon->draw_rect(text_ci, gutter_rect, false, get_line_gutter_item_color(line, g)); + TS->draw_list_add_texture(draw_list, DRAW_STEP_TEXT, Transform2D(), icon->get_rid(), gutter_rect, get_line_gutter_item_color(line, g)); } break; case GUTTER_TYPE_CUSTOM: { if (gutter.custom_draw_callback.is_valid()) { @@ -1477,7 +1479,7 @@ void TextEdit::_notification(int p_what) { if (rtl) { gutter_rect.position.x = size.width - gutter_rect.position.x - gutter_rect.size.x; } - gutter.custom_draw_callback.call(line, g, Rect2(gutter_rect)); + TS->draw_list_add_custom(draw_list, DRAW_STEP_TEXT, Transform2D(), gutter.custom_draw_callback.bind(line, g, Rect2(gutter_rect))); } } break; } @@ -1546,7 +1548,7 @@ void TextEdit::_notification(int p_what) { if (rect.position.x + rect.size.x > xmargin_end) { rect.size.x = xmargin_end - rect.position.x; } - RS::get_singleton()->canvas_item_add_rect(text_ci, rect, theme_cache.selection_color); + TS->draw_list_add_rect(draw_list, DRAW_STEP_BACKGROUND, Transform2D(), rect, true, theme_cache.selection_color); } } } @@ -1568,8 +1570,8 @@ void TextEdit::_notification(int p_what) { } else if (rect.position.x + rect.size.x > xmargin_end) { rect.size.x = xmargin_end - rect.position.x; } - RS::get_singleton()->canvas_item_add_rect(text_ci, rect, theme_cache.search_result_color); - _draw_rect_unfilled(text_ci, rect, theme_cache.search_result_border_color); + TS->draw_list_add_rect(draw_list, DRAW_STEP_BACKGROUND, Transform2D(), rect, true, theme_cache.search_result_color); + TS->draw_list_add_rect(draw_list, DRAW_STEP_BACKGROUND_BORDER, Transform2D(), rect, false, theme_cache.search_result_border_color); } search_text_col = _get_column_pos_of_word(search_text, str, search_flags, search_text_col + search_text_len); @@ -1592,7 +1594,7 @@ void TextEdit::_notification(int p_what) { } else if (rect.position.x + rect.size.x > xmargin_end) { rect.size.x = xmargin_end - rect.position.x; } - RS::get_singleton()->canvas_item_add_rect(text_ci, rect, theme_cache.word_highlighted_color); + TS->draw_list_add_rect(draw_list, DRAW_STEP_BACKGROUND, Transform2D(), rect, true, theme_cache.word_highlighted_color); } highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, highlighted_text_col + highlighted_text_len); @@ -1619,7 +1621,7 @@ void TextEdit::_notification(int p_what) { } rect.position.y += std::ceil(TS->shaped_text_get_ascent(rid)) + std::ceil(theme_cache.font->get_underline_position(theme_cache.font_size)); rect.size.y = MAX(1, theme_cache.font->get_underline_thickness(theme_cache.font_size)); - RS::get_singleton()->canvas_item_add_rect(text_ci, rect, highlight_underline_color); + TS->draw_list_add_rect(draw_list, DRAW_STEP_BACKGROUND, Transform2D(), rect, true, highlight_underline_color); } lookup_symbol_word_col = _get_column_pos_of_word(lookup_symbol_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, lookup_symbol_word_col + lookup_symbol_word_len); @@ -1637,33 +1639,16 @@ void TextEdit::_notification(int p_what) { int first_visible_char = TS->shaped_text_get_range(rid).y; int last_visible_char = TS->shaped_text_get_range(rid).x; - float char_ofs = 0; - if (theme_cache.outline_size > 0 && theme_cache.outline_color.a > 0) { - for (int j = 0; j < gl_size; j++) { - for (int k = 0; k < glyphs[j].repeat; k++) { - if ((char_ofs + char_margin) >= xmargin_beg && (char_ofs + glyphs[j].advance + char_margin) <= xmargin_end) { - if (glyphs[j].font_rid != RID()) { - TS->font_draw_glyph_outline(glyphs[j].font_rid, text_ci, glyphs[j].font_size, theme_cache.outline_size, Vector2(char_margin + char_ofs + glyphs[j].x_off, ofs_y + glyphs[j].y_off), glyphs[j].index, theme_cache.outline_color); - } - } - char_ofs += glyphs[j].advance; - } - if ((char_ofs + char_margin) >= xmargin_end) { - break; - } - } - char_ofs = 0; - } - // Draw inline objects. for (Dictionary k : object_keys) { Rect2 col_rect = TS->shaped_text_get_object_rect(rid, k); col_rect.position += Vector2(char_margin, ofs_y); if (!clipped && (col_rect.position.x) >= xmargin_beg && (col_rect.position.x + col_rect.size.x) <= xmargin_end) { - inline_object_drawer.call(k, col_rect); + TS->draw_list_add_custom(draw_list, DRAW_STEP_TEXT, Transform2D(), inline_object_drawer.bind(k, col_rect)); } } + float char_ofs = 0; for (int j = 0; j < gl_size; j++) { for (const Pair &color_data : color_map) { if (color_data.first <= glyphs[j].start) { @@ -1699,7 +1684,7 @@ void TextEdit::_notification(int p_what) { gl_color = _get_brace_mismatch_color(); } Rect2 rect = Rect2(char_pos, ofs_y + theme_cache.font->get_underline_position(theme_cache.font_size), glyphs[j].advance * glyphs[j].repeat, MAX(theme_cache.font->get_underline_thickness(theme_cache.font_size) * theme_cache.base_scale, 1)); - RS::get_singleton()->canvas_item_add_rect(text_ci, rect, gl_color); + TS->draw_list_add_rect(draw_list, DRAW_STEP_BACKGROUND, Transform2D(), rect, true, gl_color); } if ((brace_match.close_match_line == line && brace_match.close_match_column == glyphs[j].start) || @@ -1708,18 +1693,18 @@ void TextEdit::_notification(int p_what) { gl_color = _get_brace_mismatch_color(); } Rect2 rect = Rect2(char_pos, ofs_y + theme_cache.font->get_underline_position(theme_cache.font_size), glyphs[j].advance * glyphs[j].repeat, MAX(theme_cache.font->get_underline_thickness(theme_cache.font_size) * theme_cache.base_scale, 1)); - RS::get_singleton()->canvas_item_add_rect(text_ci, rect, gl_color); + TS->draw_list_add_rect(draw_list, DRAW_STEP_BACKGROUND, Transform2D(), rect, true, gl_color); } } } if (draw_tabs && ((glyphs[j].flags & TextServer::GRAPHEME_IS_TAB) == TextServer::GRAPHEME_IS_TAB)) { int yofs = (text_height - theme_cache.tab_icon->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index); - theme_cache.tab_icon->draw(text_ci, Point2(char_pos, ofs_y + yofs), gl_color); + TS->draw_list_add_texture(draw_list, DRAW_STEP_TEXT, Transform2D(), theme_cache.tab_icon->get_rid(), Rect2(Point2(char_pos, ofs_y + yofs), theme_cache.tab_icon->get_size()), gl_color); } else if (draw_spaces && ((glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE) && ((glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL)) { int yofs = (text_height - theme_cache.space_icon->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index); int xofs = (glyphs[j].advance * glyphs[j].repeat - theme_cache.space_icon->get_width()) / 2; - theme_cache.space_icon->draw(text_ci, Point2(char_pos + xofs, ofs_y + yofs), gl_color); + TS->draw_list_add_texture(draw_list, DRAW_STEP_TEXT, Transform2D(), theme_cache.space_icon->get_rid(), Rect2(Point2(char_pos + xofs, ofs_y + yofs), theme_cache.space_icon->get_size()), gl_color); } } @@ -1727,10 +1712,13 @@ void TextEdit::_notification(int p_what) { for (int k = 0; k < glyphs[j].repeat; k++) { if (!clipped && (char_ofs + char_margin) >= xmargin_beg && (char_ofs + glyphs[j].advance + char_margin) <= xmargin_end) { if (glyphs[j].font_rid != RID()) { - TS->font_draw_glyph(glyphs[j].font_rid, text_ci, glyphs[j].font_size, Vector2(char_margin + char_ofs + glyphs[j].x_off, ofs_y + glyphs[j].y_off), glyphs[j].index, gl_color); + if (theme_cache.outline_size > 0 && theme_cache.outline_color.a > 0) { + TS->font_add_glyph_outline_to_draw_list(glyphs[j].font_rid, DRAW_STEP_TEXT_OUTLINE, draw_list, Transform2D(), glyphs[j].font_size, theme_cache.outline_size, Vector2(char_margin + char_ofs + glyphs[j].x_off, ofs_y + glyphs[j].y_off), glyphs[j].index, theme_cache.outline_color); + } + TS->font_add_glyph_to_draw_list(glyphs[j].font_rid, DRAW_STEP_TEXT, draw_list, Transform2D(), glyphs[j].font_size, Vector2(char_margin + char_ofs + glyphs[j].x_off, ofs_y + glyphs[j].y_off), glyphs[j].index, gl_color); had_glyphs_drawn = true; } else if (((glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) && ((glyphs[j].flags & TextServer::GRAPHEME_IS_EMBEDDED_OBJECT) != TextServer::GRAPHEME_IS_EMBEDDED_OBJECT)) { - TS->draw_hex_code_box(text_ci, glyphs[j].font_size, Vector2(char_margin + char_ofs + glyphs[j].x_off, ofs_y + glyphs[j].y_off), glyphs[j].index, gl_color); + TS->draw_list_add_hexbox(draw_list, DRAW_STEP_TEXT, Transform2D(), Vector2(char_margin + char_ofs + glyphs[j].x_off, ofs_y + glyphs[j].y_off), glyphs[j].index, glyphs[j].font_size, gl_color); had_glyphs_drawn = true; } } @@ -1761,7 +1749,7 @@ void TextEdit::_notification(int p_what) { int yofs = (text_height - _get_folded_eol_icon()->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index); Color eol_color = _get_code_folding_color(); eol_color.a = 1; - _get_folded_eol_icon()->draw(text_ci, Point2(xofs, ofs_y + yofs), eol_color); + TS->draw_list_add_texture(draw_list, DRAW_STEP_TEXT, Transform2D(), _get_folded_eol_icon()->get_rid(), Rect2(Point2(xofs, ofs_y + yofs), _get_folded_eol_icon()->get_size()), eol_color); } } @@ -1814,20 +1802,21 @@ void TextEdit::_notification(int p_what) { } ts_caret.t_caret.position += Vector2(char_margin, ofs_y); if (overtype_mode) { - RS::get_singleton()->canvas_item_add_rect(text_ci, ts_caret.t_caret, theme_cache.caret_color); + TS->draw_list_add_rect(draw_list, DRAW_STEP_FOREGROUND, Transform2D(), ts_caret.t_caret, true, theme_cache.caret_color); + } else { - _draw_rect_unfilled(text_ci, ts_caret.t_caret, theme_cache.caret_color); + TS->draw_list_add_rect(draw_list, DRAW_STEP_FOREGROUND, Transform2D(), ts_caret.t_caret, false, theme_cache.caret_color); } if (ts_caret.l_caret != Rect2() && ts_caret.l_dir != ts_caret.t_dir) { // Draw split caret (leading part). ts_caret.l_caret.position += Vector2(char_margin, ofs_y); ts_caret.l_caret.size.x = caret_width; - RS::get_singleton()->canvas_item_add_rect(text_ci, ts_caret.l_caret, theme_cache.caret_color); + TS->draw_list_add_rect(draw_list, DRAW_STEP_FOREGROUND, Transform2D(), ts_caret.l_caret, true, theme_cache.caret_color); // Draw extra direction marker on top of split caret. float d = (ts_caret.l_dir == TextServer::DIRECTION_LTR) ? 0.5 : -3; Rect2 trect = Rect2(ts_caret.l_caret.position.x + d * caret_width, ts_caret.l_caret.position.y + ts_caret.l_caret.size.y - caret_width, 3 * caret_width, caret_width); - RS::get_singleton()->canvas_item_add_rect(text_ci, trect, theme_cache.caret_color); + TS->draw_list_add_rect(draw_list, DRAW_STEP_FOREGROUND, Transform2D(), trect, true, theme_cache.caret_color); } } else { // End of the line. if (gl_size > 0) { @@ -1853,9 +1842,9 @@ void TextEdit::_notification(int p_what) { ts_caret.l_caret.position.x -= ts_caret.l_caret.size.x; } if (overtype_mode) { - RS::get_singleton()->canvas_item_add_rect(text_ci, ts_caret.l_caret, theme_cache.caret_color); + TS->draw_list_add_rect(draw_list, DRAW_STEP_FOREGROUND, Transform2D(), ts_caret.l_caret, true, theme_cache.caret_color); } else { - _draw_rect_unfilled(text_ci, ts_caret.l_caret, theme_cache.caret_color); + TS->draw_list_add_rect(draw_list, DRAW_STEP_FOREGROUND, Transform2D(), ts_caret.l_caret, false, theme_cache.caret_color); } } } else { @@ -1864,28 +1853,28 @@ void TextEdit::_notification(int p_what) { // Draw extra marker on top of mid caret. Rect2 trect = Rect2(ts_caret.l_caret.position.x - 2.5 * caret_width, ts_caret.l_caret.position.y, 6 * caret_width, caret_width); trect.position += Vector2(char_margin, ofs_y); - RS::get_singleton()->canvas_item_add_rect(text_ci, trect, theme_cache.caret_color); + TS->draw_list_add_rect(draw_list, DRAW_STEP_FOREGROUND, Transform2D(), trect, true, theme_cache.caret_color); } else if (ts_caret.l_caret != Rect2() && ts_caret.t_caret != Rect2() && ts_caret.l_dir != ts_caret.t_dir) { // Draw extra direction marker on top of split caret. float d = (ts_caret.l_dir == TextServer::DIRECTION_LTR) ? 0.5 : -3; Rect2 trect = Rect2(ts_caret.l_caret.position.x + d * caret_width, ts_caret.l_caret.position.y + ts_caret.l_caret.size.y - caret_width, 3 * caret_width, caret_width); trect.position += Vector2(char_margin, ofs_y); - RS::get_singleton()->canvas_item_add_rect(text_ci, trect, theme_cache.caret_color); + TS->draw_list_add_rect(draw_list, DRAW_STEP_FOREGROUND, Transform2D(), trect, true, theme_cache.caret_color); d = (ts_caret.t_dir == TextServer::DIRECTION_LTR) ? 0.5 : -3; trect = Rect2(ts_caret.t_caret.position.x + d * caret_width, ts_caret.t_caret.position.y, 3 * caret_width, caret_width); trect.position += Vector2(char_margin, ofs_y); - RS::get_singleton()->canvas_item_add_rect(text_ci, trect, theme_cache.caret_color); + TS->draw_list_add_rect(draw_list, DRAW_STEP_FOREGROUND, Transform2D(), trect, true, theme_cache.caret_color); } ts_caret.l_caret.position += Vector2(char_margin, ofs_y); ts_caret.l_caret.size.x = caret_width; - RS::get_singleton()->canvas_item_add_rect(text_ci, ts_caret.l_caret, theme_cache.caret_color); + TS->draw_list_add_rect(draw_list, DRAW_STEP_FOREGROUND, Transform2D(), ts_caret.l_caret, true, theme_cache.caret_color); ts_caret.t_caret.position += Vector2(char_margin, ofs_y); ts_caret.t_caret.size.x = caret_width; - RS::get_singleton()->canvas_item_add_rect(text_ci, ts_caret.t_caret, theme_cache.caret_color); + TS->draw_list_add_rect(draw_list, DRAW_STEP_FOREGROUND, Transform2D(), ts_caret.t_caret, true, theme_cache.caret_color); } } } @@ -1906,7 +1895,7 @@ void TextEdit::_notification(int p_what) { rect.size.x = xmargin_end - rect.position.x; } rect.size.y = caret_width; - RS::get_singleton()->canvas_item_add_rect(text_ci, rect, theme_cache.caret_color); + TS->draw_list_add_rect(draw_list, DRAW_STEP_FOREGROUND, Transform2D(), rect, true, theme_cache.caret_color); carets.write[c].draw_pos.x = rect.position.x; } } @@ -1925,7 +1914,7 @@ void TextEdit::_notification(int p_what) { rect.size.x = xmargin_end - rect.position.x; } rect.size.y = caret_width * 3; - RS::get_singleton()->canvas_item_add_rect(text_ci, rect, theme_cache.caret_color); + TS->draw_list_add_rect(draw_list, DRAW_STEP_FOREGROUND, Transform2D(), rect, true, theme_cache.caret_color); carets.write[c].draw_pos.x = rect.position.x; } } @@ -1938,6 +1927,8 @@ void TextEdit::_notification(int p_what) { line_drawing_cache[line] = cache_entry; } } + TS->draw_list_sort(draw_list); + TS->draw_list_draw(draw_list, text_ci, true); if (has_focus()) { _update_ime_window_position(); @@ -9242,26 +9233,6 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li emit_signal(SNAME("lines_edited_from"), p_to_line, p_from_line); } -void TextEdit::_draw_rect_unfilled(RID p_canvas_item, const Rect2 &p_rect, const Color &p_color, real_t p_width, bool p_antialiased) const { - Rect2 rect = p_rect.abs(); - - if (p_width >= rect.size.width || p_width >= rect.size.height) { - RS::get_singleton()->canvas_item_add_rect(p_canvas_item, rect.grow(0.5f * p_width), p_color, p_antialiased); - } else { - Vector points; - points.resize(5); - points.write[0] = rect.position; - points.write[1] = rect.position + Vector2(rect.size.x, 0); - points.write[2] = rect.position + rect.size; - points.write[3] = rect.position + Vector2(0, rect.size.y); - points.write[4] = rect.position; - - Vector colors = { p_color }; - - RS::get_singleton()->canvas_item_add_polyline(p_canvas_item, points, colors, p_width, p_antialiased); - } -} - TextEdit::TextEdit(const String &p_placeholder) { placeholder_data_buf.instantiate(); carets.push_back(Caret()); diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 5517290224c7..12902e43e2e5 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -122,6 +122,15 @@ class TextEdit : public Control { }; private: + enum TEDrawStep { + DRAW_STEP_BACKGROUND, + DRAW_STEP_BACKGROUND_BORDER, + DRAW_STEP_TEXT_OUTLINE, + DRAW_STEP_TEXT, + DRAW_STEP_FOREGROUND, + DRAW_STEP_MAX, + }; + struct GutterInfo { GutterType type = GutterType::GUTTER_TYPE_STRING; String name = ""; @@ -658,9 +667,6 @@ class TextEdit : public Control { RID accessibility_text_root_element_nl; - // FIXME: Helper method to draw unfilled rects, should be moved to RenderingServer. - void _draw_rect_unfilled(RID p_canvas_item, const Rect2 &p_rect, const Color &p_color, real_t p_width = -1.0, bool p_antialiased = false) const; - /* Theme. */ Ref _get_current_stylebox() const; diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp index a74359e25fd4..a7dc2b416023 100644 --- a/scene/resources/text_line.cpp +++ b/scene/resources/text_line.cpp @@ -109,6 +109,9 @@ void TextLine::_bind_methods() { ClassDB::bind_method(D_METHOD("draw", "canvas", "pos", "color", "oversampling"), &TextLine::draw, DEFVAL(Color(1, 1, 1)), DEFVAL(0.0)); ClassDB::bind_method(D_METHOD("draw_outline", "canvas", "pos", "outline_size", "color", "oversampling"), &TextLine::draw_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1)), DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("add_to_draw_list", "layer", "list", "transform", "pos", "color", "oversampling"), &TextLine::add_to_draw_list, DEFVAL(Color(1, 1, 1)), DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("add_outline_to_draw_list", "layer", "list", "transform", "pos", "outline_size", "color", "oversampling"), &TextLine::add_outline_to_draw_list, DEFVAL(1), DEFVAL(Color(1, 1, 1)), DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("hit_test", "coords"), &TextLine::hit_test); } @@ -432,7 +435,7 @@ void TextLine::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color, fl ofs.x += TS->shaped_text_get_ascent(rid); clip_l = MAX(0, p_pos.y - ofs.y); } - return TS->shaped_text_draw(rid, p_canvas, ofs, clip_l, clip_l + width, p_color, p_oversampling); + TS->shaped_text_draw(rid, p_canvas, ofs, clip_l, clip_l + width, p_color, p_oversampling); } void TextLine::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size, const Color &p_color, float p_oversampling) const { @@ -479,7 +482,101 @@ void TextLine::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_si ofs.x += TS->shaped_text_get_ascent(rid); clip_l = MAX(0, p_pos.y - ofs.y); } - return TS->shaped_text_draw_outline(rid, p_canvas, ofs, clip_l, clip_l + width, p_outline_size, p_color, p_oversampling); + TS->shaped_text_draw_outline(rid, p_canvas, ofs, clip_l, clip_l + width, p_outline_size, p_color, p_oversampling); +} + +void TextLine::add_to_draw_list(int p_layer, RID p_list, const Transform2D &p_transform, const Vector2 &p_pos, const Color &p_color, float p_oversampling) const { + _shape(); + + Vector2 ofs = p_pos; + + float length = TS->shaped_text_get_width(rid); + if (width > 0) { + switch (alignment) { + case HORIZONTAL_ALIGNMENT_FILL: + case HORIZONTAL_ALIGNMENT_LEFT: + break; + case HORIZONTAL_ALIGNMENT_CENTER: { + if (length <= width) { + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += Math::floor((width - length) / 2.0); + } else { + ofs.y += Math::floor((width - length) / 2.0); + } + } else if (TS->shaped_text_get_inferred_direction(rid) == TextServer::DIRECTION_RTL) { + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += width - length; + } else { + ofs.y += width - length; + } + } + } break; + case HORIZONTAL_ALIGNMENT_RIGHT: { + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += width - length; + } else { + ofs.y += width - length; + } + } break; + } + } + + float clip_l; + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.y += TS->shaped_text_get_ascent(rid); + clip_l = MAX(0, p_pos.x - ofs.x); + } else { + ofs.x += TS->shaped_text_get_ascent(rid); + clip_l = MAX(0, p_pos.y - ofs.y); + } + TS->shaped_text_add_to_draw_list(rid, p_layer, p_list, p_transform, ofs, clip_l, clip_l + width, p_color, p_oversampling); +} + +void TextLine::add_outline_to_draw_list(int p_layer, RID p_list, const Transform2D &p_transform, const Vector2 &p_pos, int p_outline_size, const Color &p_color, float p_oversampling) const { + _shape(); + + Vector2 ofs = p_pos; + + float length = TS->shaped_text_get_width(rid); + if (width > 0) { + switch (alignment) { + case HORIZONTAL_ALIGNMENT_FILL: + case HORIZONTAL_ALIGNMENT_LEFT: + break; + case HORIZONTAL_ALIGNMENT_CENTER: { + if (length <= width) { + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += Math::floor((width - length) / 2.0); + } else { + ofs.y += Math::floor((width - length) / 2.0); + } + } else if (TS->shaped_text_get_inferred_direction(rid) == TextServer::DIRECTION_RTL) { + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += width - length; + } else { + ofs.y += width - length; + } + } + } break; + case HORIZONTAL_ALIGNMENT_RIGHT: { + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += width - length; + } else { + ofs.y += width - length; + } + } break; + } + } + + float clip_l; + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.y += TS->shaped_text_get_ascent(rid); + clip_l = MAX(0, p_pos.x - ofs.x); + } else { + ofs.x += TS->shaped_text_get_ascent(rid); + clip_l = MAX(0, p_pos.y - ofs.y); + } + TS->shaped_text_add_outline_to_draw_list(rid, p_layer, p_list, p_transform, ofs, clip_l, clip_l + width, p_outline_size, p_color, p_oversampling); } int TextLine::hit_test(float p_coords) const { diff --git a/scene/resources/text_line.h b/scene/resources/text_line.h index 6c9f548d9ddb..3dec85eac8ff 100644 --- a/scene/resources/text_line.h +++ b/scene/resources/text_line.h @@ -119,6 +119,9 @@ class TextLine : public RefCounted { void draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color = Color(1, 1, 1), float p_oversampling = 0.0) const; void draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1), float p_oversampling = 0.0) const; + void add_to_draw_list(int p_layer, RID p_list, const Transform2D &p_transform, const Vector2 &p_pos, const Color &p_color = Color(1, 1, 1), float p_oversampling = 0.0) const; + void add_outline_to_draw_list(int p_layer, RID p_list, const Transform2D &p_transform, const Vector2 &p_pos, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1), float p_oversampling = 0.0) const; + int hit_test(float p_coords) const; TextLine(const String &p_text, const Ref &p_font, int p_font_size, const String &p_language = "", TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL); diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp index 35d8af36cb3e..f22bbe8eabbf 100644 --- a/scene/resources/text_paragraph.cpp +++ b/scene/resources/text_paragraph.cpp @@ -149,6 +149,15 @@ void TextParagraph::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_dropcap", "canvas", "pos", "color", "oversampling"), &TextParagraph::draw_dropcap, DEFVAL(Color(1, 1, 1)), DEFVAL(0.0)); ClassDB::bind_method(D_METHOD("draw_dropcap_outline", "canvas", "pos", "outline_size", "color", "oversampling"), &TextParagraph::draw_dropcap_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1)), DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("add_to_draw_list", "layer", "list", "transform", "pos", "color", "dc_color", "oversampling"), &TextParagraph::add_to_draw_list, DEFVAL(Color(1, 1, 1)), DEFVAL(Color(1, 1, 1)), DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("add_outline_to_draw_list", "layer", "list", "transform", "pos", "outline_size", "color", "dc_color", "oversampling"), &TextParagraph::add_outline_to_draw_list, DEFVAL(1), DEFVAL(Color(1, 1, 1)), DEFVAL(Color(1, 1, 1)), DEFVAL(0.0)); + + ClassDB::bind_method(D_METHOD("add_line_to_draw_list", "layer", "list", "transform", "pos", "line", "color", "oversampling"), &TextParagraph::add_line_to_draw_list, DEFVAL(Color(1, 1, 1)), DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("add_line_outline_to_draw_list", "layer", "list", "transform", "pos", "line", "outline_size", "color", "oversampling"), &TextParagraph::add_line_outline_to_draw_list, DEFVAL(1), DEFVAL(Color(1, 1, 1)), DEFVAL(0.0)); + + ClassDB::bind_method(D_METHOD("add_dropcap_to_draw_list", "layer", "list", "transform", "pos", "color", "oversampling"), &TextParagraph::add_dropcap_to_draw_list, DEFVAL(Color(1, 1, 1)), DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("add_dropcap_outline_to_draw_list", "layer", "list", "transform", "pos", "outline_size", "color", "oversampling"), &TextParagraph::add_dropcap_outline_to_draw_list, DEFVAL(1), DEFVAL(Color(1, 1, 1)), DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("hit_test", "coords"), &TextParagraph::hit_test); } @@ -1039,6 +1048,211 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli } } +void TextParagraph::add_to_draw_list(int p_layer, RID p_list, const Transform2D &p_transform, const Vector2 &p_pos, const Color &p_color, const Color &p_dc_color, float p_oversampling) const { + _THREAD_SAFE_METHOD_ + + _shape_lines(); + Vector2 ofs = p_pos; + float h_offset = 0.f; + if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) { + h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x; + } else { + h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y; + } + + if (h_offset > 0) { + // Draw dropcap. + Vector2 dc_off = ofs; + if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_RTL) { + if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) { + dc_off.x += width - h_offset; + } else { + dc_off.y += width - h_offset; + } + } + TS->shaped_text_add_to_draw_list(dropcap_rid, p_layer, p_list, p_transform, dc_off + Vector2(0, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.size.y + dropcap_margins.position.y / 2), -1, -1, p_dc_color, p_oversampling); + } + + int lines_visible = (max_lines_visible >= 0) ? MIN(max_lines_visible, (int)lines_rid.size()) : (int)lines_rid.size(); + + for (int i = 0; i < lines_visible; i++) { + float l_width = width; + if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x = p_pos.x; + ofs.y += TS->shaped_text_get_ascent(lines_rid[i]); + if (i <= dropcap_lines) { + if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) { + ofs.x -= h_offset; + } + l_width -= h_offset; + } + } else { + ofs.y = p_pos.y; + ofs.x += TS->shaped_text_get_ascent(lines_rid[i]); + if (i <= dropcap_lines) { + if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) { + ofs.y -= h_offset; + } + l_width -= h_offset; + } + } + float line_width = TS->shaped_text_get_width(lines_rid[i]); + if (width > 0) { + switch (alignment) { + case HORIZONTAL_ALIGNMENT_FILL: + if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) { + if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += l_width - line_width; + } else { + ofs.y += l_width - line_width; + } + } + break; + case HORIZONTAL_ALIGNMENT_LEFT: + break; + case HORIZONTAL_ALIGNMENT_CENTER: { + if (line_width <= l_width) { + if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += Math::floor((l_width - line_width) / 2.0); + } else { + ofs.y += Math::floor((l_width - line_width) / 2.0); + } + } else if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) { + if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += l_width - line_width; + } else { + ofs.y += l_width - line_width; + } + } + } break; + case HORIZONTAL_ALIGNMENT_RIGHT: { + if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += l_width - line_width; + } else { + ofs.y += l_width - line_width; + } + } break; + } + } + float clip_l; + if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { + clip_l = MAX(0, p_pos.x - ofs.x); + } else { + clip_l = MAX(0, p_pos.y - ofs.y); + } + TS->shaped_text_add_to_draw_list(lines_rid[i], p_layer, p_list, p_transform, ofs, clip_l, clip_l + l_width, p_color, p_oversampling); + if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x = p_pos.x; + ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + line_spacing; + } else { + ofs.y = p_pos.y; + ofs.x += TS->shaped_text_get_descent(lines_rid[i]) + line_spacing; + } + } +} + +void TextParagraph::add_outline_to_draw_list(int p_layer, RID p_list, const Transform2D &p_transform, const Vector2 &p_pos, int p_outline_size, const Color &p_color, const Color &p_dc_color, float p_oversampling) const { + _THREAD_SAFE_METHOD_ + + _shape_lines(); + Vector2 ofs = p_pos; + + float h_offset = 0.f; + if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) { + h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x; + } else { + h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y; + } + + if (h_offset > 0) { + // Draw dropcap. + Vector2 dc_off = ofs; + if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_RTL) { + if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) { + dc_off.x += width - h_offset; + } else { + dc_off.y += width - h_offset; + } + } + TS->shaped_text_add_outline_to_draw_list(dropcap_rid, p_layer, p_list, p_transform, dc_off + Vector2(dropcap_margins.position.x, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.position.y), -1, -1, p_outline_size, p_dc_color, p_oversampling); + } + + for (int i = 0; i < (int)lines_rid.size(); i++) { + float l_width = width; + if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x = p_pos.x; + ofs.y += TS->shaped_text_get_ascent(lines_rid[i]); + if (i <= dropcap_lines) { + if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) { + ofs.x -= h_offset; + } + l_width -= h_offset; + } + } else { + ofs.y = p_pos.y; + ofs.x += TS->shaped_text_get_ascent(lines_rid[i]); + if (i <= dropcap_lines) { + if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) { + ofs.y -= h_offset; + } + l_width -= h_offset; + } + } + float length = TS->shaped_text_get_width(lines_rid[i]); + if (width > 0) { + switch (alignment) { + case HORIZONTAL_ALIGNMENT_FILL: + if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) { + if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += l_width - length; + } else { + ofs.y += l_width - length; + } + } + break; + case HORIZONTAL_ALIGNMENT_LEFT: + break; + case HORIZONTAL_ALIGNMENT_CENTER: { + if (length <= l_width) { + if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += Math::floor((l_width - length) / 2.0); + } else { + ofs.y += Math::floor((l_width - length) / 2.0); + } + } else if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) { + if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += l_width - length; + } else { + ofs.y += l_width - length; + } + } + } break; + case HORIZONTAL_ALIGNMENT_RIGHT: { + if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += l_width - length; + } else { + ofs.y += l_width - length; + } + } break; + } + } + float clip_l; + if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { + clip_l = MAX(0, p_pos.x - ofs.x); + } else { + clip_l = MAX(0, p_pos.y - ofs.y); + } + TS->shaped_text_add_outline_to_draw_list(lines_rid[i], p_layer, p_list, p_transform, ofs, clip_l, clip_l + l_width, p_outline_size, p_color, p_oversampling); + if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x = p_pos.x; + ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + line_spacing; + } else { + ofs.y = p_pos.y; + ofs.x += TS->shaped_text_get_descent(lines_rid[i]) + line_spacing; + } + } +} + int TextParagraph::hit_test(const Point2 &p_coords) const { _THREAD_SAFE_METHOD_ @@ -1121,6 +1335,54 @@ void TextParagraph::draw_dropcap_outline(RID p_canvas, const Vector2 &p_pos, int } } +void TextParagraph::add_dropcap_to_draw_list(int p_layer, RID p_list, const Transform2D &p_transform, const Vector2 &p_pos, const Color &p_color, float p_oversampling) const { + _THREAD_SAFE_METHOD_ + + Vector2 ofs = p_pos; + float h_offset = 0.f; + if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) { + h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x; + } else { + h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y; + } + + if (h_offset > 0) { + // Draw dropcap. + if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_RTL) { + if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += width - h_offset; + } else { + ofs.y += width - h_offset; + } + } + TS->shaped_text_add_to_draw_list(dropcap_rid, p_layer, p_list, p_transform, ofs + Vector2(dropcap_margins.position.x, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.position.y), -1, -1, p_color, p_oversampling); + } +} + +void TextParagraph::add_dropcap_outline_to_draw_list(int p_layer, RID p_list, const Transform2D &p_transform, const Vector2 &p_pos, int p_outline_size, const Color &p_color, float p_oversampling) const { + _THREAD_SAFE_METHOD_ + + Vector2 ofs = p_pos; + float h_offset = 0.f; + if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) { + h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x; + } else { + h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y; + } + + if (h_offset > 0) { + // Draw dropcap. + if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_RTL) { + if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += width - h_offset; + } else { + ofs.y += width - h_offset; + } + } + TS->shaped_text_add_outline_to_draw_list(dropcap_rid, p_layer, p_list, p_transform, ofs + Vector2(dropcap_margins.position.x, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.position.y), -1, -1, p_outline_size, p_color, p_oversampling); + } +} + void TextParagraph::draw_line(RID p_canvas, const Vector2 &p_pos, int p_line, const Color &p_color, float p_oversampling) const { _THREAD_SAFE_METHOD_ @@ -1152,6 +1414,37 @@ void TextParagraph::draw_line_outline(RID p_canvas, const Vector2 &p_pos, int p_ return TS->shaped_text_draw_outline(lines_rid[p_line], p_canvas, ofs, -1, -1, p_outline_size, p_color, p_oversampling); } +void TextParagraph::add_line_to_draw_list(int p_layer, RID p_list, const Transform2D &p_transform, const Vector2 &p_pos, int p_line, const Color &p_color, float p_oversampling) const { + _THREAD_SAFE_METHOD_ + + _shape_lines(); + ERR_FAIL_COND(p_line < 0 || p_line >= (int)lines_rid.size()); + + Vector2 ofs = p_pos; + + if (TS->shaped_text_get_orientation(lines_rid[p_line]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.y += TS->shaped_text_get_ascent(lines_rid[p_line]); + } else { + ofs.x += TS->shaped_text_get_ascent(lines_rid[p_line]); + } + return TS->shaped_text_add_to_draw_list(lines_rid[p_line], p_layer, p_list, p_transform, ofs, -1, -1, p_color, p_oversampling); +} + +void TextParagraph::add_line_outline_to_draw_list(int p_layer, RID p_list, const Transform2D &p_transform, const Vector2 &p_pos, int p_line, int p_outline_size, const Color &p_color, float p_oversampling) const { + _THREAD_SAFE_METHOD_ + + _shape_lines(); + ERR_FAIL_COND(p_line < 0 || p_line >= (int)lines_rid.size()); + + Vector2 ofs = p_pos; + if (TS->shaped_text_get_orientation(lines_rid[p_line]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.y += TS->shaped_text_get_ascent(lines_rid[p_line]); + } else { + ofs.x += TS->shaped_text_get_ascent(lines_rid[p_line]); + } + return TS->shaped_text_add_outline_to_draw_list(lines_rid[p_line], p_layer, p_list, p_transform, ofs, -1, -1, p_outline_size, p_color, p_oversampling); +} + TextParagraph::TextParagraph(const String &p_text, const Ref &p_font, int p_font_size, const String &p_language, float p_width, TextServer::Direction p_direction, TextServer::Orientation p_orientation) { rid = TS->create_shaped_text(p_direction, p_orientation); if (p_font.is_valid()) { diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h index 5a5a2c6d2e79..e4425f4f62be 100644 --- a/scene/resources/text_paragraph.h +++ b/scene/resources/text_paragraph.h @@ -169,6 +169,15 @@ class TextParagraph : public RefCounted { void draw_dropcap(RID p_canvas, const Vector2 &p_pos, const Color &p_color = Color(1, 1, 1), float p_oversampling = 0.0) const; void draw_dropcap_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1), float p_oversampling = 0.0) const; + void add_to_draw_list(int p_layer, RID p_list, const Transform2D &p_transform, const Vector2 &p_pos, const Color &p_color = Color(1, 1, 1), const Color &p_dc_color = Color(1, 1, 1), float p_oversampling = 0.0) const; + void add_outline_to_draw_list(int p_layer, RID p_list, const Transform2D &p_transform, const Vector2 &p_pos, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1), const Color &p_dc_color = Color(1, 1, 1), float p_oversampling = 0.0) const; + + void add_line_to_draw_list(int p_layer, RID p_list, const Transform2D &p_transform, const Vector2 &p_pos, int p_line, const Color &p_color = Color(1, 1, 1), float p_oversampling = 0.0) const; + void add_line_outline_to_draw_list(int p_layer, RID p_list, const Transform2D &p_transform, const Vector2 &p_pos, int p_line, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1), float p_oversampling = 0.0) const; + + void add_dropcap_to_draw_list(int p_layer, RID p_list, const Transform2D &p_transform, const Vector2 &p_pos, const Color &p_color = Color(1, 1, 1), float p_oversampling = 0.0) const; + void add_dropcap_outline_to_draw_list(int p_layer, RID p_list, const Transform2D &p_transform, const Vector2 &p_pos, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1), float p_oversampling = 0.0) const; + int hit_test(const Point2 &p_coords) const; bool is_dirty(); diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp index bee17012578f..af660f0f515b 100644 --- a/servers/register_server_types.cpp +++ b/servers/register_server_types.cpp @@ -146,8 +146,9 @@ void register_server_types() { GDREGISTER_CLASS(TextServerExtension); GDREGISTER_CLASS(TextServerDummy); - GDREGISTER_NATIVE_STRUCT(Glyph, "int start = -1;int end = -1;uint8_t count = 0;uint8_t repeat = 1;uint16_t flags = 0;float x_off = 0.f;float y_off = 0.f;float advance = 0.f;RID font_rid;int font_size = 0;int32_t index = 0"); - GDREGISTER_NATIVE_STRUCT(CaretInfo, "Rect2 leading_caret;Rect2 trailing_caret;TextServer::Direction leading_direction;TextServer::Direction trailing_direction"); + GDREGISTER_NATIVE_STRUCT(Glyph, "int start = -1;int end = -1;uint8_t count = 0;uint8_t repeat = 1;uint16_t flags = 0;float x_off = 0.f;float y_off = 0.f;float advance = 0.f;RID font_rid;int font_size = 0;int32_t index = 0;int span_index = -1"); + GDREGISTER_NATIVE_STRUCT(CaretInfo, "Rect2 leading_caret;Rect2 trailing_caret;TextServer::Direction leading_direction = TextServer::DIRECTION_AUTO;TextServer::Direction trailing_direction = TextServer::DIRECTION_AUTO"); + GDREGISTER_NATIVE_STRUCT(GlyphDrawCall, "TextServer::DrawCallType draw_type = TextServer::DRAW_CALL_NULL;int16_t layer = 0;Transform2D transform;RID texture;Rect2 dst_rect;Rect2 src_rect;Color modulate;Variant data;"); Engine::get_singleton()->add_singleton(Engine::Singleton("TextServerManager", TextServerManager::get_singleton(), "TextServerManager")); diff --git a/servers/text/text_server.cpp b/servers/text/text_server.cpp index 117dc5073fe0..194fd8d0cb3a 100644 --- a/servers/text/text_server.cpp +++ b/servers/text/text_server.cpp @@ -375,6 +375,9 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("font_draw_glyph", "font_rid", "canvas", "size", "pos", "index", "color", "oversampling"), &TextServer::font_draw_glyph, DEFVAL(Color(1, 1, 1)), DEFVAL(0.0)); ClassDB::bind_method(D_METHOD("font_draw_glyph_outline", "font_rid", "canvas", "size", "outline_size", "pos", "index", "color", "oversampling"), &TextServer::font_draw_glyph_outline, DEFVAL(Color(1, 1, 1)), DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("font_add_glyph_to_draw_list", "font_rid", "layer", "list", "transform", "size", "pos", "index", "color", "oversampling"), &TextServer::font_add_glyph_to_draw_list, DEFVAL(Color(1, 1, 1)), DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("font_add_glyph_outline_to_draw_list", "font_rid", "layer", "list", "transform", "size", "outline_size", "pos", "index", "color", "oversampling"), &TextServer::font_add_glyph_outline_to_draw_list, DEFVAL(Color(1, 1, 1)), DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("font_is_language_supported", "font_rid", "language"), &TextServer::font_is_language_supported); ClassDB::bind_method(D_METHOD("font_set_language_support_override", "font_rid", "language", "supported"), &TextServer::font_set_language_support_override); ClassDB::bind_method(D_METHOD("font_get_language_support_override", "font_rid", "language"), &TextServer::font_get_language_support_override); @@ -401,6 +404,20 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_hex_code_box_size", "size", "index"), &TextServer::get_hex_code_box_size); ClassDB::bind_method(D_METHOD("draw_hex_code_box", "canvas", "size", "pos", "index", "color"), &TextServer::draw_hex_code_box); + /* Draw list interface */ + + ClassDB::bind_method(D_METHOD("create_draw_list"), &TextServer::create_draw_list); + ClassDB::bind_method(D_METHOD("draw_list_sort", "list"), &TextServer::draw_list_sort); + ClassDB::bind_method(D_METHOD("draw_list_reserve", "list", "count"), &TextServer::draw_list_reserve); + + ClassDB::bind_method(D_METHOD("draw_list_add_hexbox", "list", "layer", "transform", "pos", "index", "size", "modulate"), &TextServer::draw_list_add_hexbox); + ClassDB::bind_method(D_METHOD("draw_list_add_rect", "list", "layer", "transform", "rect", "filled", "modulate"), &TextServer::draw_list_add_rect); + ClassDB::bind_method(D_METHOD("draw_list_add_line", "list", "layer", "transform", "start", "end", "width", "dash", "modulate"), &TextServer::draw_list_add_line); + ClassDB::bind_method(D_METHOD("draw_list_add_texture", "list", "layer", "transform", "texture", "rect", "modulate"), &TextServer::draw_list_add_texture); + ClassDB::bind_method(D_METHOD("draw_list_add_custom", "list", "layer", "transform", "callback"), &TextServer::draw_list_add_custom); + + ClassDB::bind_method(D_METHOD("draw_list_draw", "list", "ci", "free"), &TextServer::draw_list_draw, DEFVAL(false)); + /* Shaped text buffer interface */ ClassDB::bind_method(D_METHOD("create_shaped_text", "direction", "orientation"), &TextServer::create_shaped_text, DEFVAL(DIRECTION_AUTO), DEFVAL(ORIENTATION_HORIZONTAL)); @@ -509,6 +526,9 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("shaped_text_draw", "shaped", "canvas", "pos", "clip_l", "clip_r", "color", "oversampling"), &TextServer::shaped_text_draw, DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0.0)); ClassDB::bind_method(D_METHOD("shaped_text_draw_outline", "shaped", "canvas", "pos", "clip_l", "clip_r", "outline_size", "color", "oversampling"), &TextServer::shaped_text_draw_outline, DEFVAL(-1), DEFVAL(-1), DEFVAL(1), DEFVAL(Color(1, 1, 1)), DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("shaped_text_add_to_draw_list", "shaped", "layer", "list", "transform", "pos", "clip_l", "clip_r", "color", "oversampling"), &TextServer::shaped_text_add_to_draw_list, DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("shaped_text_add_outline_to_draw_list", "shaped", "layer", "list", "transform", "pos", "clip_l", "clip_r", "outline_size", "color", "oversampling"), &TextServer::shaped_text_add_outline_to_draw_list, DEFVAL(-1), DEFVAL(-1), DEFVAL(1), DEFVAL(Color(1, 1, 1)), DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("shaped_text_get_dominant_direction_in_range", "shaped", "start", "end"), &TextServer::shaped_text_get_dominant_direction_in_range); #ifndef DISABLE_DEPRECATED @@ -638,6 +658,17 @@ void TextServer::_bind_methods() { BIND_ENUM_CONSTANT(SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE); BIND_ENUM_CONSTANT(SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE); + /* DrawCallType */ + BIND_ENUM_CONSTANT(DRAW_CALL_NULL); + BIND_ENUM_CONSTANT(DRAW_CALL_NORMAL); + BIND_ENUM_CONSTANT(DRAW_CALL_MSDF); + BIND_ENUM_CONSTANT(DRAW_CALL_LCD); + BIND_ENUM_CONSTANT(DRAW_CALL_HEX); + BIND_ENUM_CONSTANT(DRAW_CALL_LINE); + BIND_ENUM_CONSTANT(DRAW_CALL_RECT); + BIND_ENUM_CONSTANT(DRAW_CALL_IMAGE); + BIND_ENUM_CONSTANT(DRAW_CALL_CUSTOM); + /* Feature */ BIND_ENUM_CONSTANT(FEATURE_SIMPLE_LAYOUT); BIND_ENUM_CONSTANT(FEATURE_BIDI_LAYOUT); @@ -1922,6 +1953,196 @@ void TextServer::shaped_text_draw_outline(const RID &p_shaped, const RID &p_canv } } +void TextServer::shaped_text_add_to_draw_list(const RID &p_shaped, int64_t p_layer, const RID &p_list, const Transform2D &p_transform, const Vector2 &p_pos, double p_clip_l, double p_clip_r, const Color &p_color, float p_oversampling) const { + TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped); + bool hex_codes = shaped_text_get_preserve_control(p_shaped) || shaped_text_get_preserve_invalid(p_shaped); + + bool rtl = shaped_text_get_direction(p_shaped) == DIRECTION_RTL; + + int ellipsis_pos = shaped_text_get_ellipsis_pos(p_shaped); + int trim_pos = shaped_text_get_trim_pos(p_shaped); + + const Glyph *ellipsis_glyphs = shaped_text_get_ellipsis_glyphs(p_shaped); + int ellipsis_gl_size = shaped_text_get_ellipsis_glyph_count(p_shaped); + + int v_size = shaped_text_get_glyph_count(p_shaped); + const Glyph *glyphs = shaped_text_get_glyphs(p_shaped); + + Vector2 ofs; + // Draw RTL ellipsis string when needed. + if (rtl && ellipsis_pos >= 0) { + for (int i = ellipsis_gl_size - 1; i >= 0; i--) { + for (int j = 0; j < ellipsis_glyphs[i].repeat; j++) { + font_add_glyph_to_draw_list(ellipsis_glyphs[i].font_rid, p_layer, p_list, p_transform, ellipsis_glyphs[i].font_size, ofs + p_pos + Vector2(ellipsis_glyphs[i].x_off, ellipsis_glyphs[i].y_off), ellipsis_glyphs[i].index, p_color, p_oversampling); + if (orientation == ORIENTATION_HORIZONTAL) { + ofs.x += ellipsis_glyphs[i].advance; + } else { + ofs.y += ellipsis_glyphs[i].advance; + } + } + } + } + // Draw at the baseline. + for (int i = 0; i < v_size; i++) { + if (trim_pos >= 0) { + if (rtl) { + if (i < trim_pos) { + continue; + } + } else { + if (i >= trim_pos) { + break; + } + } + } + for (int j = 0; j < glyphs[i].repeat; j++) { + if (p_clip_r > 0) { + // Clip right / bottom. + if (orientation == ORIENTATION_HORIZONTAL) { + if (ofs.x + glyphs[i].advance > p_clip_r) { + return; + } + } else { + if (ofs.y + glyphs[i].advance > p_clip_r) { + return; + } + } + } + if (p_clip_l > 0) { + // Clip left / top. + if (orientation == ORIENTATION_HORIZONTAL) { + if (ofs.x < p_clip_l) { + ofs.x += glyphs[i].advance; + continue; + } + } else { + if (ofs.y < p_clip_l) { + ofs.y += glyphs[i].advance; + continue; + } + } + } + + if (glyphs[i].font_rid != RID()) { + font_add_glyph_to_draw_list(glyphs[i].font_rid, p_layer, p_list, p_transform, glyphs[i].font_size, ofs + p_pos + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, p_color, p_oversampling); + } else if (hex_codes && ((glyphs[i].flags & GRAPHEME_IS_VIRTUAL) != GRAPHEME_IS_VIRTUAL) && ((glyphs[i].flags & GRAPHEME_IS_EMBEDDED_OBJECT) != GRAPHEME_IS_EMBEDDED_OBJECT)) { + draw_list_add_hexbox(p_list, p_layer, p_transform, ofs + p_pos + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, glyphs[i].font_size, p_color); + } + if (orientation == ORIENTATION_HORIZONTAL) { + ofs.x += glyphs[i].advance; + } else { + ofs.y += glyphs[i].advance; + } + } + } + // Draw LTR ellipsis string when needed. + if (!rtl && ellipsis_pos >= 0) { + for (int i = 0; i < ellipsis_gl_size; i++) { + for (int j = 0; j < ellipsis_glyphs[i].repeat; j++) { + font_add_glyph_to_draw_list(ellipsis_glyphs[i].font_rid, p_layer, p_list, p_transform, ellipsis_glyphs[i].font_size, ofs + p_pos + Vector2(ellipsis_glyphs[i].x_off, ellipsis_glyphs[i].y_off), ellipsis_glyphs[i].index, p_color, p_oversampling); + if (orientation == ORIENTATION_HORIZONTAL) { + ofs.x += ellipsis_glyphs[i].advance; + } else { + ofs.y += ellipsis_glyphs[i].advance; + } + } + } + } +} + +void TextServer::shaped_text_add_outline_to_draw_list(const RID &p_shaped, int64_t p_layer, const RID &p_list, const Transform2D &p_transform, const Vector2 &p_pos, double p_clip_l, double p_clip_r, int64_t p_outline_size, const Color &p_color, float p_oversampling) const { + TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped); + + bool rtl = (shaped_text_get_inferred_direction(p_shaped) == DIRECTION_RTL); + + int ellipsis_pos = shaped_text_get_ellipsis_pos(p_shaped); + int trim_pos = shaped_text_get_trim_pos(p_shaped); + + const Glyph *ellipsis_glyphs = shaped_text_get_ellipsis_glyphs(p_shaped); + int ellipsis_gl_size = shaped_text_get_ellipsis_glyph_count(p_shaped); + + int v_size = shaped_text_get_glyph_count(p_shaped); + const Glyph *glyphs = shaped_text_get_glyphs(p_shaped); + + Vector2 ofs; + // Draw RTL ellipsis string when needed. + if (rtl && ellipsis_pos >= 0) { + for (int i = ellipsis_gl_size - 1; i >= 0; i--) { + for (int j = 0; j < ellipsis_glyphs[i].repeat; j++) { + font_add_glyph_outline_to_draw_list(ellipsis_glyphs[i].font_rid, p_layer, p_list, p_transform, ellipsis_glyphs[i].font_size, p_outline_size, ofs + p_pos + Vector2(ellipsis_glyphs[i].x_off, ellipsis_glyphs[i].y_off), ellipsis_glyphs[i].index, p_color, p_oversampling); + if (orientation == ORIENTATION_HORIZONTAL) { + ofs.x += ellipsis_glyphs[i].advance; + } else { + ofs.y += ellipsis_glyphs[i].advance; + } + } + } + } + // Draw at the baseline. + for (int i = 0; i < v_size; i++) { + if (trim_pos >= 0) { + if (rtl) { + if (i < trim_pos) { + continue; + } + } else { + if (i >= trim_pos) { + break; + } + } + } + for (int j = 0; j < glyphs[i].repeat; j++) { + if (p_clip_r > 0) { + // Clip right / bottom. + if (orientation == ORIENTATION_HORIZONTAL) { + if (ofs.x + glyphs[i].advance > p_clip_r) { + return; + } + } else { + if (ofs.y + glyphs[i].advance > p_clip_r) { + return; + } + } + } + if (p_clip_l > 0) { + // Clip left / top. + if (orientation == ORIENTATION_HORIZONTAL) { + if (ofs.x < p_clip_l) { + ofs.x += glyphs[i].advance; + continue; + } + } else { + if (ofs.y < p_clip_l) { + ofs.y += glyphs[i].advance; + continue; + } + } + } + if (glyphs[i].font_rid != RID()) { + font_add_glyph_outline_to_draw_list(glyphs[i].font_rid, p_layer, p_list, p_transform, glyphs[i].font_size, p_outline_size, ofs + p_pos + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, p_color, p_oversampling); + } + if (orientation == ORIENTATION_HORIZONTAL) { + ofs.x += glyphs[i].advance; + } else { + ofs.y += glyphs[i].advance; + } + } + } + // Draw LTR ellipsis string when needed. + if (!rtl && ellipsis_pos >= 0) { + for (int i = 0; i < ellipsis_gl_size; i++) { + for (int j = 0; j < ellipsis_glyphs[i].repeat; j++) { + font_add_glyph_outline_to_draw_list(ellipsis_glyphs[i].font_rid, p_layer, p_list, p_transform, ellipsis_glyphs[i].font_size, p_outline_size, ofs + p_pos + Vector2(ellipsis_glyphs[i].x_off, ellipsis_glyphs[i].y_off), ellipsis_glyphs[i].index, p_color, p_oversampling); + if (orientation == ORIENTATION_HORIZONTAL) { + ofs.x += ellipsis_glyphs[i].advance; + } else { + ofs.y += ellipsis_glyphs[i].advance; + } + } + } + } +} + #ifdef DEBUG_ENABLED void TextServer::debug_print_glyph(int p_idx, const Glyph &p_glyph) const { diff --git a/servers/text/text_server.h b/servers/text/text_server.h index 64e8a6bc80f5..4a0fa7675976 100644 --- a/servers/text/text_server.h +++ b/servers/text/text_server.h @@ -41,6 +41,7 @@ class TypedArray; struct Glyph; struct CaretInfo; +struct GlyphDrawCall; #define OT_TAG(m_c1, m_c2, m_c3, m_c4) ((int32_t)((((uint32_t)(m_c1) & 0xff) << 24) | (((uint32_t)(m_c2) & 0xff) << 16) | (((uint32_t)(m_c3) & 0xff) << 8) | ((uint32_t)(m_c4) & 0xff))) @@ -172,6 +173,18 @@ class TextServer : public RefCounted { SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE = 16, }; + enum DrawCallType { + DRAW_CALL_NULL, + DRAW_CALL_NORMAL, + DRAW_CALL_MSDF, + DRAW_CALL_LCD, + DRAW_CALL_HEX, + DRAW_CALL_LINE, + DRAW_CALL_RECT, + DRAW_CALL_IMAGE, + DRAW_CALL_CUSTOM, + }; + enum Feature { FEATURE_SIMPLE_LAYOUT = 1 << 0, FEATURE_BIDI_LAYOUT = 1 << 1, @@ -430,6 +443,9 @@ class TextServer : public RefCounted { virtual void font_draw_glyph(const RID &p_font, const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color = Color(1, 1, 1), float p_oversampling = 0.0) const = 0; virtual void font_draw_glyph_outline(const RID &p_font, const RID &p_canvas, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color = Color(1, 1, 1), float p_oversampling = 0.0) const = 0; + virtual void font_add_glyph_to_draw_list(const RID &p_font, int64_t p_layer, const RID &p_list, const Transform2D &p_transform, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color = Color(1, 1, 1), float p_oversampling = 0.0) const = 0; + virtual void font_add_glyph_outline_to_draw_list(const RID &p_font, int64_t p_layer, const RID &p_list, const Transform2D &p_transform, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color = Color(1, 1, 1), float p_oversampling = 0.0) const = 0; + virtual bool font_is_language_supported(const RID &p_font_rid, const String &p_language) const = 0; virtual void font_set_language_support_override(const RID &p_font_rid, const String &p_language, bool p_supported) = 0; virtual bool font_get_language_support_override(const RID &p_font_rid, const String &p_language) = 0; @@ -458,6 +474,20 @@ class TextServer : public RefCounted { virtual Vector2 get_hex_code_box_size(int64_t p_size, int64_t p_index) const; virtual void draw_hex_code_box(const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const; + /* Draw list interface */ + + virtual RID create_draw_list() = 0; + virtual void draw_list_sort(const RID &p_dc) = 0; + virtual void draw_list_reserve(const RID &p_dc, int64_t p_new_items) = 0; + + virtual void draw_list_add_hexbox(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, const Vector2 &p_pos, int64_t p_index, int64_t p_size, const Color &p_modulate) const = 0; + virtual void draw_list_add_rect(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, const Rect2 &p_rect, bool p_filled, const Color &p_modulate) const = 0; + virtual void draw_list_add_line(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, const Point2 &p_start, const Point2 &p_end, float p_width, float p_dash, const Color &p_modulate) const = 0; + virtual void draw_list_add_texture(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, RID p_texture, const Rect2 &p_dst_rect, const Color &p_modulate) const = 0; + virtual void draw_list_add_custom(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, const Callable &p_callback) const = 0; + + virtual void draw_list_draw(const RID &p_dc, const RID &p_ci, bool p_free = true) = 0; + /* Shaped text buffer interface */ virtual RID create_shaped_text(Direction p_direction = DIRECTION_AUTO, Orientation p_orientation = ORIENTATION_HORIZONTAL) = 0; @@ -579,6 +609,9 @@ class TextServer : public RefCounted { virtual void shaped_text_draw(const RID &p_shaped, const RID &p_canvas, const Vector2 &p_pos, double p_clip_l = -1.0, double p_clip_r = -1.0, const Color &p_color = Color(1, 1, 1), float p_oversampling = 0.0) const; virtual void shaped_text_draw_outline(const RID &p_shaped, const RID &p_canvas, const Vector2 &p_pos, double p_clip_l = -1.0, double p_clip_r = -1.0, int64_t p_outline_size = 1, const Color &p_color = Color(1, 1, 1), float p_oversampling = 0.0) const; + virtual void shaped_text_add_to_draw_list(const RID &p_shaped, int64_t p_layer, const RID &p_list, const Transform2D &p_transform, const Vector2 &p_pos, double p_clip_l = -1.0, double p_clip_r = -1.0, const Color &p_color = Color(1, 1, 1), float p_oversampling = 0.0) const; + virtual void shaped_text_add_outline_to_draw_list(const RID &p_shaped, int64_t p_layer, const RID &p_list, const Transform2D &p_transform, const Vector2 &p_pos, double p_clip_l = -1.0, double p_clip_r = -1.0, int64_t p_outline_size = 1, const Color &p_color = Color(1, 1, 1), float p_oversampling = 0.0) const; + #ifdef DEBUG_ENABLED void debug_print_glyph(int p_idx, const Glyph &p_glyph) const; void shaped_text_debug_print(const RID &p_shaped) const; @@ -619,6 +652,8 @@ class TextServer : public RefCounted { /*************************************************************************/ +// Native structures, sync with `register_server_types()` if definition is changed. + struct Glyph { int start = -1; // Start offset in the source string. int end = -1; // End offset in the source string. @@ -647,8 +682,30 @@ struct Glyph { struct CaretInfo { Rect2 l_caret; Rect2 t_caret; - TextServer::Direction l_dir; - TextServer::Direction t_dir; + TextServer::Direction l_dir = TextServer::DIRECTION_AUTO; + TextServer::Direction t_dir = TextServer::DIRECTION_AUTO; +}; + +struct GlyphDrawCall { + TextServer::DrawCallType draw_type = TextServer::DRAW_CALL_NULL; + int16_t layer = 0; + Transform2D transform; + + RID texture; + Rect2 dst_rect; + Rect2 src_rect; + Color modulate; + Variant data; +}; + +struct GlyphDrawCallCompare { + _FORCE_INLINE_ bool operator()(const GlyphDrawCall &l, const GlyphDrawCall &r) const { + if (l.layer == r.layer) { + return l.texture.get_id() < r.texture.get_id(); + } else { + return l.layer < r.layer; + } + } }; /*************************************************************************/ @@ -709,6 +766,8 @@ VARIANT_ENUM_CAST(TextServer::StructuredTextParser); VARIANT_ENUM_CAST(TextServer::FontAntialiasing); VARIANT_ENUM_CAST(TextServer::FontLCDSubpixelLayout); VARIANT_ENUM_CAST(TextServer::FixedSizeScaleMode); +VARIANT_ENUM_CAST(TextServer::DrawCallType); GDVIRTUAL_NATIVE_PTR(Glyph); GDVIRTUAL_NATIVE_PTR(CaretInfo); +GDVIRTUAL_NATIVE_PTR(GlyphDrawCall); diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp index 9882800f9a25..292312840b60 100644 --- a/servers/text/text_server_extension.cpp +++ b/servers/text/text_server_extension.cpp @@ -218,6 +218,8 @@ void TextServerExtension::_bind_methods() { GDVIRTUAL_BIND_COMPAT(_font_draw_glyph_bind_compat_104872, "font_rid", "canvas", "size", "pos", "index", "color"); GDVIRTUAL_BIND_COMPAT(_font_draw_glyph_outline_bind_compat_104872, "font_rid", "canvas", "size", "outline_size", "pos", "index", "color"); #endif + GDVIRTUAL_BIND(_font_add_glyph_to_draw_list, "font_rid", "layer", "list", "transform", "size", "pos", "index", "color", "oversampling"); + GDVIRTUAL_BIND(_font_add_glyph_outline_to_draw_list, "font_rid", "layer", "list", "transform", "size", "outline_size", "pos", "index", "color", "oversampling"); GDVIRTUAL_BIND(_font_is_language_supported, "font_rid", "language"); GDVIRTUAL_BIND(_font_set_language_support_override, "font_rid", "language", "supported"); @@ -247,6 +249,18 @@ void TextServerExtension::_bind_methods() { GDVIRTUAL_BIND(_get_hex_code_box_size, "size", "index"); GDVIRTUAL_BIND(_draw_hex_code_box, "canvas", "size", "pos", "index", "color"); + /* Draw list interface */ + + GDVIRTUAL_BIND(_create_draw_list); + GDVIRTUAL_BIND(_draw_list_sort, "list"); + GDVIRTUAL_BIND(_draw_list_reserve, "list", "count"); + GDVIRTUAL_BIND(_draw_list_add_hexbox, "list", "layer", "transform", "pos", "index", "size", "modulate"); + GDVIRTUAL_BIND(_draw_list_add_rect, "list", "layer", "transform", "rect", "filled", "modulate"); + GDVIRTUAL_BIND(_draw_list_add_line, "list", "layer", "transform", "start", "end", "width", "dash", "modulate"); + GDVIRTUAL_BIND(_draw_list_add_texture, "list", "layer", "transform", "texture", "rect", "modulate"); + GDVIRTUAL_BIND(_draw_list_add_custom, "list", "layer", "transform", "callback"); + GDVIRTUAL_BIND(_draw_list_draw, "list", "ci", "free"); + /* Shaped text buffer interface */ GDVIRTUAL_BIND(_create_shaped_text, "direction", "orientation"); @@ -355,6 +369,8 @@ void TextServerExtension::_bind_methods() { GDVIRTUAL_BIND_COMPAT(_shaped_text_draw_bind_compat_104872, "shaped", "canvas", "pos", "clip_l", "clip_r", "color"); GDVIRTUAL_BIND_COMPAT(_shaped_text_draw_outline_bind_compat_104872, "shaped", "canvas", "pos", "clip_l", "clip_r", "outline_size", "color"); #endif + GDVIRTUAL_BIND(_shaped_text_add_to_draw_list, "shaped", "layer", "list", "transform", "pos", "clip_l", "clip_r", "color", "oversampling"); + GDVIRTUAL_BIND(_shaped_text_add_outline_to_draw_list, "shaped", "layer", "list", "transform", "pos", "clip_l", "clip_r", "outline_size", "color", "oversampling"); GDVIRTUAL_BIND(_shaped_text_get_grapheme_bounds, "shaped", "pos"); GDVIRTUAL_BIND(_shaped_text_next_grapheme_pos, "shaped", "pos"); @@ -1032,6 +1048,14 @@ void TextServerExtension::font_draw_glyph_outline(const RID &p_font_rid, const R #endif } +void TextServerExtension::font_add_glyph_to_draw_list(const RID &p_font_rid, int64_t p_layer, const RID &p_list, const Transform2D &p_transform, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const { + GDVIRTUAL_CALL(_font_add_glyph_to_draw_list, p_font_rid, p_layer, p_list, p_transform, p_size, p_pos, p_index, p_color, p_oversampling); +} + +void TextServerExtension::font_add_glyph_outline_to_draw_list(const RID &p_font_rid, int64_t p_layer, const RID &p_list, const Transform2D &p_transform, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const { + GDVIRTUAL_CALL(_font_add_glyph_outline_to_draw_list, p_font_rid, p_layer, p_list, p_transform, p_size, p_outline_size, p_pos, p_index, p_color, p_oversampling); +} + bool TextServerExtension::font_is_language_supported(const RID &p_font_rid, const String &p_language) const { bool ret = false; GDVIRTUAL_CALL(_font_is_language_supported, p_font_rid, p_language, ret); @@ -1137,6 +1161,47 @@ void TextServerExtension::draw_hex_code_box(const RID &p_canvas, int64_t p_size, TextServer::draw_hex_code_box(p_canvas, p_size, p_pos, p_index, p_color); } } +/*************************************************************************/ +/* Draw list interface */ +/*************************************************************************/ + +RID TextServerExtension::create_draw_list() { + RID ret; + GDVIRTUAL_CALL(_create_draw_list, ret); + return ret; +} + +void TextServerExtension::draw_list_sort(const RID &p_dc) { + GDVIRTUAL_CALL(_draw_list_sort, p_dc); +} + +void TextServerExtension::draw_list_reserve(const RID &p_dc, int64_t p_new_items) { + GDVIRTUAL_CALL(_draw_list_reserve, p_dc, p_new_items); +} + +void TextServerExtension::draw_list_add_hexbox(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, const Vector2 &p_pos, int64_t p_index, int64_t p_size, const Color &p_modulate) const { + GDVIRTUAL_CALL(_draw_list_add_hexbox, p_dc, p_layer, p_transform, p_pos, p_index, p_size, p_modulate); +} + +void TextServerExtension::draw_list_add_rect(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, const Rect2 &p_rect, bool p_filled, const Color &p_modulate) const { + GDVIRTUAL_CALL(_draw_list_add_rect, p_dc, p_layer, p_transform, p_rect, p_filled, p_modulate); +} + +void TextServerExtension::draw_list_add_line(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, const Point2 &p_start, const Point2 &p_end, float p_width, float p_dash, const Color &p_modulate) const { + GDVIRTUAL_CALL(_draw_list_add_line, p_dc, p_layer, p_transform, p_start, p_end, p_width, p_dash, p_modulate); +} + +void TextServerExtension::draw_list_add_texture(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, RID p_texture, const Rect2 &p_dst_rect, const Color &p_modulate) const { + GDVIRTUAL_CALL(_draw_list_add_texture, p_dc, p_layer, p_transform, p_texture, p_dst_rect, p_modulate); +} + +void TextServerExtension::draw_list_add_custom(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, const Callable &p_callback) const { + GDVIRTUAL_CALL(_draw_list_add_custom, p_dc, p_layer, p_transform, p_callback); +} + +void TextServerExtension::draw_list_draw(const RID &p_dc, const RID &p_ci, bool p_free) { + GDVIRTUAL_CALL(_draw_list_draw, p_dc, p_ci, p_free); +} /*************************************************************************/ /* Shaped text buffer interface */ @@ -1598,6 +1663,20 @@ void TextServerExtension::shaped_text_draw_outline(const RID &p_shaped, const RI TextServer::shaped_text_draw_outline(p_shaped, p_canvas, p_pos, p_clip_l, p_clip_r, p_outline_size, p_color, p_oversampling); } +void TextServerExtension::shaped_text_add_to_draw_list(const RID &p_shaped, int64_t p_layer, const RID &p_list, const Transform2D &p_transform, const Vector2 &p_pos, double p_clip_l, double p_clip_r, const Color &p_color, float p_oversampling) const { + if (GDVIRTUAL_CALL(_shaped_text_add_to_draw_list, p_shaped, p_layer, p_list, p_transform, p_pos, p_clip_l, p_clip_r, p_color, p_oversampling)) { + return; + } + TextServer::shaped_text_add_to_draw_list(p_shaped, p_layer, p_list, p_transform, p_pos, p_clip_l, p_clip_r, p_color, p_oversampling); +} + +void TextServerExtension::shaped_text_add_outline_to_draw_list(const RID &p_shaped, int64_t p_layer, const RID &p_list, const Transform2D &p_transform, const Vector2 &p_pos, double p_clip_l, double p_clip_r, int64_t p_outline_size, const Color &p_color, float p_oversampling) const { + if (GDVIRTUAL_CALL(_shaped_text_add_outline_to_draw_list, p_shaped, p_layer, p_list, p_transform, p_pos, p_clip_l, p_clip_r, p_outline_size, p_color, p_oversampling)) { + return; + } + TextServer::shaped_text_add_outline_to_draw_list(p_shaped, p_layer, p_list, p_transform, p_pos, p_clip_l, p_clip_r, p_outline_size, p_color, p_oversampling); +} + Vector2 TextServerExtension::shaped_text_get_grapheme_bounds(const RID &p_shaped, int64_t p_pos) const { Vector2 ret; if (GDVIRTUAL_CALL(_shaped_text_get_grapheme_bounds, p_shaped, p_pos, ret)) { diff --git a/servers/text/text_server_extension.h b/servers/text/text_server_extension.h index b764addcf837..a1b066aac605 100644 --- a/servers/text/text_server_extension.h +++ b/servers/text/text_server_extension.h @@ -356,6 +356,10 @@ class TextServerExtension : public TextServer { GDVIRTUAL6C_COMPAT(_font_draw_glyph_bind_compat_104872, _font_draw_glyph, RID, RID, int64_t, const Vector2 &, int64_t, const Color &); GDVIRTUAL7C_COMPAT(_font_draw_glyph_outline_bind_compat_104872, _font_draw_glyph_outline, RID, RID, int64_t, int64_t, const Vector2 &, int64_t, const Color &); #endif + virtual void font_add_glyph_to_draw_list(const RID &p_font, int64_t p_layer, const RID &p_list, const Transform2D &p_transform, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color = Color(1, 1, 1), float p_oversampling = 0.0) const override; + virtual void font_add_glyph_outline_to_draw_list(const RID &p_font, int64_t p_layer, const RID &p_list, const Transform2D &p_transform, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color = Color(1, 1, 1), float p_oversampling = 0.0) const override; + GDVIRTUAL9C_REQUIRED(_font_add_glyph_to_draw_list, const RID &, int64_t, const RID &, const Transform2D &, int64_t, const Vector2 &, int64_t, const Color &, float); + GDVIRTUAL10C_REQUIRED(_font_add_glyph_outline_to_draw_list, const RID &, int64_t, const RID &, const Transform2D &, int64_t, int64_t, const Vector2 &, int64_t, const Color &, float); virtual bool font_is_language_supported(const RID &p_font_rid, const String &p_language) const override; virtual void font_set_language_support_override(const RID &p_font_rid, const String &p_language, bool p_supported) override; @@ -405,6 +409,27 @@ class TextServerExtension : public TextServer { GDVIRTUAL2RC(Vector2, _get_hex_code_box_size, int64_t, int64_t); GDVIRTUAL5C(_draw_hex_code_box, RID, int64_t, const Vector2 &, int64_t, const Color &); + /* Draw list interface */ + + virtual RID create_draw_list() override; + virtual void draw_list_sort(const RID &p_dc) override; + virtual void draw_list_reserve(const RID &p_dc, int64_t p_new_items) override; + virtual void draw_list_add_hexbox(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, const Vector2 &p_pos, int64_t p_index, int64_t p_size, const Color &p_modulate) const override; + virtual void draw_list_add_rect(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, const Rect2 &p_rect, bool p_filled, const Color &p_modulate) const override; + virtual void draw_list_add_line(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, const Point2 &p_start, const Point2 &p_end, float p_width, float p_dash, const Color &p_modulate) const override; + virtual void draw_list_add_texture(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, RID p_texture, const Rect2 &p_dst_rect, const Color &p_modulate) const override; + virtual void draw_list_add_custom(const RID &p_dc, int64_t p_layer, const Transform2D &p_transform, const Callable &p_callback) const override; + virtual void draw_list_draw(const RID &p_dc, const RID &p_ci, bool p_free = true) override; + GDVIRTUAL0R_REQUIRED(RID, _create_draw_list); + GDVIRTUAL1_REQUIRED(_draw_list_sort, const RID &); + GDVIRTUAL2_REQUIRED(_draw_list_reserve, const RID &, int64_t); + GDVIRTUAL7C_REQUIRED(_draw_list_add_hexbox, const RID &, int64_t, const Transform2D &, const Vector2 &, int64_t, int64_t, const Color &); + GDVIRTUAL6C_REQUIRED(_draw_list_add_rect, const RID &, int64_t, const Transform2D &, const Rect2 &, bool, const Color &); + GDVIRTUAL8C_REQUIRED(_draw_list_add_line, const RID &, int64_t, const Transform2D &, const Point2 &, const Point2 &, float, float, const Color &); + GDVIRTUAL6C_REQUIRED(_draw_list_add_texture, const RID &, int64_t, const Transform2D &, RID, const Rect2 &, const Color &); + GDVIRTUAL4C_REQUIRED(_draw_list_add_custom, const RID &, int64_t, const Transform2D &, const Callable &); + GDVIRTUAL3_REQUIRED(_draw_list_draw, const RID &, const RID &, bool); + /* Shaped text buffer interface */ virtual RID create_shaped_text(Direction p_direction = DIRECTION_AUTO, Orientation p_orientation = ORIENTATION_HORIZONTAL) override; @@ -590,6 +615,10 @@ class TextServerExtension : public TextServer { GDVIRTUAL6C_COMPAT(_shaped_text_draw_bind_compat_104872, _shaped_text_draw, RID, RID, const Vector2 &, double, double, const Color &); GDVIRTUAL7C_COMPAT(_shaped_text_draw_outline_bind_compat_104872, _shaped_text_draw_outline, RID, RID, const Vector2 &, double, double, int64_t, const Color &); #endif + virtual void shaped_text_add_to_draw_list(const RID &p_shaped, int64_t p_layer, const RID &p_list, const Transform2D &p_transform, const Vector2 &p_pos, double p_clip_l = -1.0, double p_clip_r = -1.0, const Color &p_color = Color(1, 1, 1), float p_oversampling = 0.0) const override; + virtual void shaped_text_add_outline_to_draw_list(const RID &p_shaped, int64_t p_layer, const RID &p_list, const Transform2D &p_transform, const Vector2 &p_pos, double p_clip_l = -1.0, double p_clip_r = -1.0, int64_t p_outline_size = 1, const Color &p_color = Color(1, 1, 1), float p_oversampling = 0.0) const override; + GDVIRTUAL9C(_shaped_text_add_to_draw_list, RID, int64_t, RID, const Transform2D &, const Vector2 &, double, double, const Color &, float); + GDVIRTUAL10C(_shaped_text_add_outline_to_draw_list, RID, int64_t, RID, const Transform2D &, const Vector2 &, double, double, int64_t, const Color &, float); virtual Vector2 shaped_text_get_grapheme_bounds(const RID &p_shaped, int64_t p_pos) const override; virtual int64_t shaped_text_next_grapheme_pos(const RID &p_shaped, int64_t p_pos) const override;