summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/arkit/arkit_interface.mm4
-rw-r--r--modules/assimp/SCsub30
-rw-r--r--modules/assimp/editor_scene_importer_assimp.cpp19
-rw-r--r--modules/basis_universal/SCsub17
-rw-r--r--modules/bullet/SCsub14
-rw-r--r--modules/bullet/bullet_physics_server.cpp16
-rw-r--r--modules/bullet/bullet_physics_server.h3
-rw-r--r--modules/bullet/generic_6dof_joint_bullet.cpp8
-rw-r--r--modules/bullet/generic_6dof_joint_bullet.h3
-rw-r--r--modules/bullet/rigid_body_bullet.cpp4
-rw-r--r--modules/csg/csg_gizmos.cpp2
-rw-r--r--modules/csg/csg_gizmos.h2
-rw-r--r--modules/cvtt/SCsub15
-rw-r--r--modules/denoise/SCsub17
-rw-r--r--modules/enet/SCsub16
-rw-r--r--modules/etc/SCsub15
-rw-r--r--modules/etc/image_compress_etc.cpp (renamed from modules/etc/image_etc.cpp)10
-rw-r--r--modules/etc/image_compress_etc.h (renamed from modules/etc/image_etc.h)8
-rw-r--r--modules/etc/register_types.cpp2
-rw-r--r--modules/freetype/SCsub14
-rw-r--r--modules/gdnative/SCsub1
-rw-r--r--modules/gdnative/gdnative/array.cpp12
-rw-r--r--modules/gdnative/gdnative/packed_arrays.cpp113
-rw-r--r--modules/gdnative/gdnative_api.json454
-rw-r--r--modules/gdnative/gdnative_builders.py1
-rw-r--r--modules/gdnative/gdnative_library_editor_plugin.cpp12
-rw-r--r--modules/gdnative/include/gdnative/array.h1
-rw-r--r--modules/gdnative/include/gdnative/packed_arrays.h47
-rw-r--r--modules/gdnative/include/gdnative/variant.h2
-rw-r--r--modules/gdnative/include/pluginscript/godot_pluginscript.h2
-rw-r--r--modules/gdnative/include/text/godot_text.h231
-rw-r--r--modules/gdnative/nativescript/nativescript.h8
-rw-r--r--modules/gdnative/pluginscript/pluginscript_language.cpp30
-rw-r--r--modules/gdnative/pluginscript/pluginscript_language.h7
-rw-r--r--modules/gdnative/pluginscript/pluginscript_script.cpp1
-rw-r--r--modules/gdnative/pluginscript/pluginscript_script.h18
-rw-r--r--modules/gdnative/text/SCsub6
-rw-r--r--modules/gdnative/text/config.py6
-rw-r--r--modules/gdnative/text/register_types.cpp36
-rw-r--r--modules/gdnative/text/register_types.h37
-rw-r--r--modules/gdnative/text/text_server_gdnative.cpp853
-rw-r--r--modules/gdnative/text/text_server_gdnative.h187
-rw-r--r--modules/gdnavigation/SCsub31
-rw-r--r--modules/gdnavigation/nav_map.cpp2
-rw-r--r--modules/gdnavigation/navigation_mesh_generator.cpp8
-rw-r--r--modules/gdscript/config.py1
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml86
-rw-r--r--modules/gdscript/doc_classes/GDScriptFunctionState.xml45
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.h2
-rw-r--r--modules/gdscript/gdscript.cpp262
-rw-r--r--modules/gdscript/gdscript.h37
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp220
-rw-r--r--modules/gdscript/gdscript_analyzer.h1
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp108
-rw-r--r--modules/gdscript/gdscript_byte_codegen.h36
-rw-r--r--modules/gdscript/gdscript_codegen.h9
-rw-r--r--modules/gdscript/gdscript_compiler.cpp141
-rw-r--r--modules/gdscript/gdscript_disassembler.cpp48
-rw-r--r--modules/gdscript/gdscript_editor.cpp76
-rw-r--r--modules/gdscript/gdscript_function.h17
-rw-r--r--modules/gdscript/gdscript_functions.cpp1942
-rw-r--r--modules/gdscript/gdscript_parser.cpp292
-rw-r--r--modules/gdscript/gdscript_parser.h40
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp20
-rw-r--r--modules/gdscript/gdscript_tokenizer.h20
-rw-r--r--modules/gdscript/gdscript_utility_functions.cpp718
-rw-r--r--modules/gdscript/gdscript_utility_functions.h (renamed from modules/gdscript/gdscript_functions.h)123
-rw-r--r--modules/gdscript/gdscript_vm.cpp91
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.cpp4
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.cpp3
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp3
-rw-r--r--modules/gdscript/language_server/lsp.hpp2
-rw-r--r--modules/gdscript/register_types.cpp5
-rw-r--r--modules/glslang/SCsub18
-rw-r--r--modules/gridmap/grid_map_editor_plugin.cpp4
-rw-r--r--modules/jpg/SCsub17
-rw-r--r--modules/lightmapper_rd/lightmapper_rd.cpp75
-rwxr-xr-xmodules/mbedtls/SCsub25
-rw-r--r--modules/mbedtls/crypto_mbedtls.cpp71
-rw-r--r--modules/mbedtls/crypto_mbedtls.h22
-rw-r--r--modules/mbedtls/register_types.cpp4
-rw-r--r--modules/mbedtls/tests/test_crypto_mbedtls.cpp62
-rw-r--r--modules/mbedtls/tests/test_crypto_mbedtls.h61
-rw-r--r--modules/meshoptimizer/SCsub45
-rw-r--r--modules/meshoptimizer/config.py7
-rw-r--r--modules/meshoptimizer/register_types.cpp43
-rw-r--r--modules/meshoptimizer/register_types.h37
-rw-r--r--modules/minimp3/SCsub17
-rw-r--r--modules/minimp3/audio_stream_mp3.cpp228
-rw-r--r--modules/minimp3/audio_stream_mp3.h110
-rw-r--r--modules/minimp3/config.py16
-rw-r--r--modules/minimp3/doc_classes/AudioStreamMP3.xml26
-rw-r--r--modules/minimp3/register_types.cpp52
-rw-r--r--modules/minimp3/register_types.h37
-rw-r--r--modules/minimp3/resource_importer_mp3.cpp104
-rw-r--r--modules/minimp3/resource_importer_mp3.h58
-rw-r--r--modules/mono/build_scripts/make_android_mono_config.py4
-rw-r--r--modules/mono/build_scripts/mono_configure.py10
-rw-r--r--modules/mono/config.py8
-rw-r--r--modules/mono/csharp_script.cpp2
-rw-r--r--modules/mono/csharp_script.h9
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs36
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs45
-rw-r--r--modules/mono/editor/bindings_generator.cpp73
-rw-r--r--modules/mono/editor/bindings_generator.h9
-rw-r--r--modules/mono/editor/code_completion.cpp12
-rw-r--r--modules/mono/editor/code_completion.h1
-rw-r--r--modules/mono/editor/editor_internal_calls.cpp139
-rw-r--r--modules/mono/editor/godotsharp_export.cpp8
-rw-r--r--modules/mono/editor/script_class_parser.h10
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs10
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs5
-rw-r--r--modules/mono/glue/base_object_glue.cpp28
-rw-r--r--modules/mono/glue/collections_glue.cpp106
-rw-r--r--modules/mono/glue/gd_glue.cpp50
-rw-r--r--modules/mono/glue/nodepath_glue.cpp22
-rw-r--r--modules/mono/glue/rid_glue.cpp6
-rw-r--r--modules/mono/glue/scene_tree_glue.cpp2
-rw-r--r--modules/mono/glue/string_glue.cpp12
-rw-r--r--modules/mono/glue/string_name_glue.cpp8
-rw-r--r--modules/mono/mono_gd/gd_mono_class.cpp4
-rw-r--r--modules/mono/mono_gd/gd_mono_class.h11
-rw-r--r--modules/mono/mono_gd/gd_mono_field.cpp319
-rw-r--r--modules/mono/mono_gd/gd_mono_log.cpp6
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp952
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.h43
-rw-r--r--modules/mono/mono_gd/gd_mono_method.cpp25
-rw-r--r--modules/mono/mono_gd/gd_mono_method.h11
-rw-r--r--modules/mono/mono_gd/gd_mono_property.cpp5
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp8
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h20
-rw-r--r--modules/mono/mono_gd/gd_mono_wasm_m2n.cpp79
-rw-r--r--modules/mono/mono_gd/gd_mono_wasm_m2n.h263
-rw-r--r--modules/mono/mono_gd/support/android_support.cpp4
-rw-r--r--modules/mono/signal_awaiter_utils.cpp24
-rw-r--r--modules/ogg/SCsub16
-rw-r--r--modules/opensimplex/SCsub17
-rw-r--r--modules/opensimplex/doc_classes/OpenSimplexNoise.xml16
-rw-r--r--modules/opensimplex/open_simplex_noise.cpp14
-rw-r--r--modules/opensimplex/open_simplex_noise.h24
-rw-r--r--modules/opus/SCsub19
-rw-r--r--modules/pvr/SCsub15
-rw-r--r--modules/pvr/image_compress_pvrtc.cpp84
-rw-r--r--modules/pvr/image_compress_pvrtc.h36
-rw-r--r--modules/pvr/register_types.cpp3
-rw-r--r--modules/pvr/texture_loader_pvr.cpp58
-rw-r--r--modules/regex/SCsub18
-rw-r--r--modules/squish/SCsub16
-rw-r--r--modules/stb_vorbis/SCsub17
-rw-r--r--modules/svg/SCsub17
-rw-r--r--modules/text_server_adv/SCsub501
-rw-r--r--modules/text_server_adv/bitmap_font_adv.cpp580
-rw-r--r--modules/text_server_adv/bitmap_font_adv.h118
-rw-r--r--modules/text_server_adv/config.py6
-rw-r--r--modules/text_server_adv/dynamic_font_adv.cpp1003
-rw-r--r--modules/text_server_adv/dynamic_font_adv.h191
-rw-r--r--modules/text_server_adv/font_adv.h94
-rw-r--r--modules/text_server_adv/icu_data/icudata_stub.cpp63
-rw-r--r--modules/text_server_adv/register_types.cpp43
-rw-r--r--modules/text_server_adv/register_types.h40
-rw-r--r--modules/text_server_adv/script_iterator.cpp110
-rw-r--r--modules/text_server_adv/script_iterator.h61
-rw-r--r--modules/text_server_adv/text_server_adv.cpp2571
-rw-r--r--modules/text_server_adv/text_server_adv.h248
-rw-r--r--modules/text_server_fb/SCsub13
-rw-r--r--modules/text_server_fb/bitmap_font_fb.cpp356
-rw-r--r--modules/text_server_fb/bitmap_font_fb.h107
-rw-r--r--modules/text_server_fb/config.py11
-rw-r--r--modules/text_server_fb/dynamic_font_fb.cpp685
-rw-r--r--modules/text_server_fb/dynamic_font_fb.h169
-rw-r--r--modules/text_server_fb/font_fb.h80
-rw-r--r--modules/text_server_fb/register_types.cpp43
-rw-r--r--modules/text_server_fb/register_types.h40
-rw-r--r--modules/text_server_fb/text_server_fb.cpp1383
-rw-r--r--modules/text_server_fb/text_server_fb.h193
-rw-r--r--modules/theora/SCsub16
-rw-r--r--modules/tinyexr/SCsub17
-rw-r--r--modules/upnp/SCsub15
-rw-r--r--modules/vhacd/SCsub17
-rw-r--r--modules/visual_script/visual_script.cpp2
-rw-r--r--modules/visual_script/visual_script.h8
-rw-r--r--modules/visual_script/visual_script_editor.cpp15
-rw-r--r--modules/visual_script/visual_script_property_selector.cpp9
-rw-r--r--modules/vorbis/SCsub20
-rw-r--r--modules/webm/SCsub15
-rw-r--r--modules/webm/libvpx/SCsub2
-rw-r--r--modules/webp/SCsub16
-rw-r--r--modules/webrtc/SCsub2
-rw-r--r--modules/webrtc/library_godot_webrtc.js22
-rw-r--r--modules/websocket/SCsub32
-rw-r--r--modules/websocket/library_godot_websocket.js4
-rw-r--r--modules/xatlas_unwrap/SCsub16
-rw-r--r--modules/xatlas_unwrap/register_types.cpp6
194 files changed, 15889 insertions, 3634 deletions
diff --git a/modules/arkit/arkit_interface.mm b/modules/arkit/arkit_interface.mm
index e8fa023ac7..6d69f4a2f4 100644
--- a/modules/arkit/arkit_interface.mm
+++ b/modules/arkit/arkit_interface.mm
@@ -712,8 +712,8 @@ void ARKitInterface::_add_or_update_anchor(GodotARAnchor *p_anchor) {
int16_t index = planeAnchor.geometry.triangleIndices[j];
simd_float3 vrtx = planeAnchor.geometry.vertices[index];
simd_float2 textcoord = planeAnchor.geometry.textureCoordinates[index];
- surftool->add_uv(Vector2(textcoord[0], textcoord[1]));
- surftool->add_color(Color(0.8, 0.8, 0.8));
+ surftool->set_uv(Vector2(textcoord[0], textcoord[1]));
+ surftool->set_color(Color(0.8, 0.8, 0.8));
surftool->add_vertex(Vector3(vrtx[0], vrtx[1], vrtx[2]));
}
diff --git a/modules/assimp/SCsub b/modules/assimp/SCsub
index f1d0c742b4..7213efb74d 100644
--- a/modules/assimp/SCsub
+++ b/modules/assimp/SCsub
@@ -5,8 +5,13 @@ Import("env_modules")
env_assimp = env_modules.Clone()
+# Thirdparty source files
+
+thirdparty_obj = []
+
# Force bundled version for now, there's no released version of Assimp with
# support for ArmaturePopulate which we use from their master branch.
+
if True: # env['builtin_assimp']:
thirdparty_dir = "#thirdparty/assimp"
@@ -84,11 +89,20 @@ if True: # env['builtin_assimp']:
env_thirdparty = env_assimp.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/CApi/*.cpp"))
- env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/Common/*.cpp"))
- env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/PostProcessing/*.cpp"))
- env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/Material/*.cpp"))
- env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/FBX/*.cpp"))
-
-# Godot's own source files
-env_assimp.add_source_files(env.modules_sources, "*.cpp")
+ env_thirdparty.add_source_files(thirdparty_obj, Glob("#thirdparty/assimp/code/CApi/*.cpp"))
+ env_thirdparty.add_source_files(thirdparty_obj, Glob("#thirdparty/assimp/code/Common/*.cpp"))
+ env_thirdparty.add_source_files(thirdparty_obj, Glob("#thirdparty/assimp/code/PostProcessing/*.cpp"))
+ env_thirdparty.add_source_files(thirdparty_obj, Glob("#thirdparty/assimp/code/Material/*.cpp"))
+ env_thirdparty.add_source_files(thirdparty_obj, Glob("#thirdparty/assimp/code/FBX/*.cpp"))
+ env.modules_sources += thirdparty_obj
+
+
+# Godot source files
+
+module_obj = []
+
+env_assimp.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
+
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/assimp/editor_scene_importer_assimp.cpp b/modules/assimp/editor_scene_importer_assimp.cpp
index e5becfd559..796ee27a7d 100644
--- a/modules/assimp/editor_scene_importer_assimp.cpp
+++ b/modules/assimp/editor_scene_importer_assimp.cpp
@@ -827,8 +827,7 @@ EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportState &stat
Ref<ArrayMesh> mesh;
mesh.instance();
bool has_uvs = false;
- bool compress_vert_data = state.import_flags & IMPORT_USE_COMPRESSION;
- uint32_t mesh_flags = compress_vert_data ? Mesh::ARRAY_COMPRESS_DEFAULT : 0;
+ uint32_t mesh_flags = 0;
Map<String, uint32_t> morph_mesh_string_lookup;
@@ -839,7 +838,7 @@ EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportState &stat
String ai_anim_mesh_name = AssimpUtils::get_assimp_string(ai_mesh->mAnimMeshes[j]->mName);
if (!morph_mesh_string_lookup.has(ai_anim_mesh_name)) {
morph_mesh_string_lookup.insert(ai_anim_mesh_name, j);
- mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED);
+ mesh->set_blend_shape_mode(ArrayMesh::BLEND_SHAPE_MODE_NORMALIZED);
if (ai_anim_mesh_name.empty()) {
ai_anim_mesh_name = String("morph_") + itos(j);
}
@@ -904,33 +903,33 @@ EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportState &stat
// Get the texture coordinates if they exist
if (ai_mesh->HasTextureCoords(0)) {
has_uvs = true;
- st->add_uv(Vector2(ai_mesh->mTextureCoords[0][j].x, 1.0f - ai_mesh->mTextureCoords[0][j].y));
+ st->set_uv(Vector2(ai_mesh->mTextureCoords[0][j].x, 1.0f - ai_mesh->mTextureCoords[0][j].y));
}
if (ai_mesh->HasTextureCoords(1)) {
has_uvs = true;
- st->add_uv2(Vector2(ai_mesh->mTextureCoords[1][j].x, 1.0f - ai_mesh->mTextureCoords[1][j].y));
+ st->set_uv2(Vector2(ai_mesh->mTextureCoords[1][j].x, 1.0f - ai_mesh->mTextureCoords[1][j].y));
}
// Assign vertex colors
if (ai_mesh->HasVertexColors(0)) {
Color color = Color(ai_mesh->mColors[0]->r, ai_mesh->mColors[0]->g, ai_mesh->mColors[0]->b,
ai_mesh->mColors[0]->a);
- st->add_color(color);
+ st->set_color(color);
}
// Work out normal calculations? - this needs work it doesn't work properly on huestos
if (ai_mesh->mNormals != nullptr) {
const aiVector3D normals = ai_mesh->mNormals[j];
const Vector3 godot_normal = Vector3(normals.x, normals.y, normals.z);
- st->add_normal(godot_normal);
+ st->set_normal(godot_normal);
if (ai_mesh->HasTangentsAndBitangents()) {
const aiVector3D tangents = ai_mesh->mTangents[j];
const Vector3 godot_tangent = Vector3(tangents.x, tangents.y, tangents.z);
const aiVector3D bitangent = ai_mesh->mBitangents[j];
const Vector3 godot_bitangent = Vector3(bitangent.x, bitangent.y, bitangent.z);
float d = godot_normal.cross(godot_tangent).dot(godot_bitangent) > 0.0f ? 1.0f : -1.0f;
- st->add_tangent(Plane(tangents.x, tangents.y, tangents.z, d));
+ st->set_tangent(Plane(tangents.x, tangents.y, tangents.z, d));
}
}
@@ -948,8 +947,8 @@ EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportState &stat
weights.write[k] = bone_info[k].weight;
}
- st->add_bones(bones);
- st->add_weights(weights);
+ st->set_bones(bones);
+ st->set_weights(weights);
}
// Assign vertex
diff --git a/modules/basis_universal/SCsub b/modules/basis_universal/SCsub
index dc7b176d24..351628a0e3 100644
--- a/modules/basis_universal/SCsub
+++ b/modules/basis_universal/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_basisu = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
# Not unbundled so far since not widespread as shared library
thirdparty_dir = "#thirdparty/basis_universal/"
tool_sources = [
@@ -41,8 +44,16 @@ if env["target"] == "debug":
env_thirdparty = env_basisu.Clone()
env_thirdparty.disable_warnings()
if env["tools"]:
- env_thirdparty.add_source_files(env.modules_sources, tool_sources)
-env_thirdparty.add_source_files(env.modules_sources, transcoder_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, tool_sources)
+env_thirdparty.add_source_files(thirdparty_obj, transcoder_sources)
+env.modules_sources += thirdparty_obj
# Godot source files
-env_basisu.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_basisu.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
+
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/bullet/SCsub b/modules/bullet/SCsub
index 21bdcca18e..bfac0df5b0 100644
--- a/modules/bullet/SCsub
+++ b/modules/bullet/SCsub
@@ -7,6 +7,8 @@ env_bullet = env_modules.Clone()
# Thirdparty source files
+thirdparty_obj = []
+
if env["builtin_bullet"]:
# Build only version 2 for now (as of 2.89)
# Sync file list with relevant upstream CMakeLists.txt for each folder.
@@ -208,8 +210,16 @@ if env["builtin_bullet"]:
env_thirdparty = env_bullet.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
# Godot source files
-env_bullet.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_bullet.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
+
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp
index 663ad6e3e1..3b548b7faa 100644
--- a/modules/bullet/bullet_physics_server.cpp
+++ b/modules/bullet/bullet_physics_server.cpp
@@ -1463,22 +1463,6 @@ bool BulletPhysicsServer3D::generic_6dof_joint_get_flag(RID p_joint, Vector3::Ax
return generic_6dof_joint->get_flag(p_axis, p_flag);
}
-void BulletPhysicsServer3D::generic_6dof_joint_set_precision(RID p_joint, int p_precision) {
- JointBullet *joint = joint_owner.getornull(p_joint);
- ERR_FAIL_COND(!joint);
- ERR_FAIL_COND(joint->get_type() != JOINT_6DOF);
- Generic6DOFJointBullet *generic_6dof_joint = static_cast<Generic6DOFJointBullet *>(joint);
- generic_6dof_joint->set_precision(p_precision);
-}
-
-int BulletPhysicsServer3D::generic_6dof_joint_get_precision(RID p_joint) {
- JointBullet *joint = joint_owner.getornull(p_joint);
- ERR_FAIL_COND_V(!joint, 0);
- ERR_FAIL_COND_V(joint->get_type() != JOINT_6DOF, 0);
- Generic6DOFJointBullet *generic_6dof_joint = static_cast<Generic6DOFJointBullet *>(joint);
- return generic_6dof_joint->get_precision();
-}
-
void BulletPhysicsServer3D::free(RID p_rid) {
if (shape_owner.owns(p_rid)) {
ShapeBullet *shape = shape_owner.getornull(p_rid);
diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h
index dca9339c44..07a32e510c 100644
--- a/modules/bullet/bullet_physics_server.h
+++ b/modules/bullet/bullet_physics_server.h
@@ -376,9 +376,6 @@ public:
virtual void generic_6dof_joint_set_flag(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisFlag p_flag, bool p_enable) override;
virtual bool generic_6dof_joint_get_flag(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisFlag p_flag) override;
- virtual void generic_6dof_joint_set_precision(RID p_joint, int precision) override;
- virtual int generic_6dof_joint_get_precision(RID p_joint) override;
-
/* MISC */
virtual void free(RID p_rid) override;
diff --git a/modules/bullet/generic_6dof_joint_bullet.cpp b/modules/bullet/generic_6dof_joint_bullet.cpp
index 56a66dba45..d75bf1fb98 100644
--- a/modules/bullet/generic_6dof_joint_bullet.cpp
+++ b/modules/bullet/generic_6dof_joint_bullet.cpp
@@ -273,11 +273,3 @@ bool Generic6DOFJointBullet::get_flag(Vector3::Axis p_axis, PhysicsServer3D::G6D
ERR_FAIL_INDEX_V(p_axis, 3, false);
return flags[p_axis][p_flag];
}
-
-void Generic6DOFJointBullet::set_precision(int p_precision) {
- sixDOFConstraint->setOverrideNumSolverIterations(MAX(1, p_precision));
-}
-
-int Generic6DOFJointBullet::get_precision() const {
- return sixDOFConstraint->getOverrideNumSolverIterations();
-}
diff --git a/modules/bullet/generic_6dof_joint_bullet.h b/modules/bullet/generic_6dof_joint_bullet.h
index 316708bb11..ed25337745 100644
--- a/modules/bullet/generic_6dof_joint_bullet.h
+++ b/modules/bullet/generic_6dof_joint_bullet.h
@@ -68,9 +68,6 @@ public:
void set_flag(Vector3::Axis p_axis, PhysicsServer3D::G6DOFJointAxisFlag p_flag, bool p_value);
bool get_flag(Vector3::Axis p_axis, PhysicsServer3D::G6DOFJointAxisFlag p_flag) const;
-
- void set_precision(int p_precision);
- int get_precision() const;
};
#endif
diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp
index 648acdf70e..284a22717b 100644
--- a/modules/bullet/rigid_body_bullet.cpp
+++ b/modules/bullet/rigid_body_bullet.cpp
@@ -735,7 +735,7 @@ void RigidBodyBullet::set_continuous_collision_detection(bool p_enable) {
}
btBody->setCcdSweptSphereRadius(radius * 0.2);
} else {
- btBody->setCcdMotionThreshold(10000.0);
+ btBody->setCcdMotionThreshold(0.);
btBody->setCcdSweptSphereRadius(0.);
}
}
@@ -815,7 +815,7 @@ void RigidBodyBullet::reload_shapes() {
btBody->updateInertiaTensor();
reload_kinematic_shapes();
- set_continuous_collision_detection(btBody->getCcdMotionThreshold() < 9998.0);
+ set_continuous_collision_detection(is_continuous_collision_detection_enabled());
reload_body();
}
diff --git a/modules/csg/csg_gizmos.cpp b/modules/csg/csg_gizmos.cpp
index cce72770f5..f8c05761bb 100644
--- a/modules/csg/csg_gizmos.cpp
+++ b/modules/csg/csg_gizmos.cpp
@@ -319,7 +319,7 @@ bool CSGShape3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<CSGSphere3D>(p_spatial) || Object::cast_to<CSGBox3D>(p_spatial) || Object::cast_to<CSGCylinder3D>(p_spatial) || Object::cast_to<CSGTorus3D>(p_spatial) || Object::cast_to<CSGMesh3D>(p_spatial) || Object::cast_to<CSGPolygon3D>(p_spatial);
}
-String CSGShape3DGizmoPlugin::get_name() const {
+String CSGShape3DGizmoPlugin::get_gizmo_name() const {
return "CSGShape3D";
}
diff --git a/modules/csg/csg_gizmos.h b/modules/csg/csg_gizmos.h
index 83ee847caf..cf44f76f37 100644
--- a/modules/csg/csg_gizmos.h
+++ b/modules/csg/csg_gizmos.h
@@ -40,7 +40,7 @@ class CSGShape3DGizmoPlugin : public EditorNode3DGizmoPlugin {
public:
bool has_gizmo(Node3D *p_spatial) override;
- String get_name() const override;
+ String get_gizmo_name() const override;
int get_priority() const override;
bool is_selectable_when_hidden() const override;
void redraw(EditorNode3DGizmo *p_gizmo) override;
diff --git a/modules/cvtt/SCsub b/modules/cvtt/SCsub
index 5438f7ebac..e56177d6e9 100644
--- a/modules/cvtt/SCsub
+++ b/modules/cvtt/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_cvtt = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
thirdparty_dir = "#thirdparty/cvtt/"
thirdparty_sources = [
"ConvectionKernels.cpp",
@@ -17,7 +20,15 @@ env_cvtt.Prepend(CPPPATH=[thirdparty_dir])
env_thirdparty = env_cvtt.Clone()
env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.modules_sources += thirdparty_obj
# Godot source files
-env_cvtt.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_cvtt.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
+
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/denoise/SCsub b/modules/denoise/SCsub
index bf3bd7d073..97feea2b44 100644
--- a/modules/denoise/SCsub
+++ b/modules/denoise/SCsub
@@ -8,6 +8,9 @@ Import("env_modules")
env_oidn = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
thirdparty_dir = "#thirdparty/oidn/"
thirdparty_sources = [
"core/api.cpp",
@@ -106,7 +109,8 @@ env_oidn.Append(
env_thirdparty = env_oidn.Clone()
env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.modules_sources += thirdparty_obj
weights_in_path = thirdparty_dir + "weights/rtlightmap_hdr.tza"
weights_out_path = thirdparty_dir + "weights/rtlightmap_hdr.gen.cpp"
@@ -114,5 +118,12 @@ weights_out_path = thirdparty_dir + "weights/rtlightmap_hdr.gen.cpp"
env_thirdparty.Depends(weights_out_path, weights_in_path)
env_thirdparty.CommandNoCache(weights_out_path, weights_in_path, resource_to_cpp.tza_to_cpp)
-env_oidn.add_source_files(env.modules_sources, "denoise_wrapper.cpp")
-env_modules.add_source_files(env.modules_sources, ["register_types.cpp", "lightmap_denoiser.cpp"])
+# Godot source files
+
+module_obj = []
+
+env_oidn.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
+
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/enet/SCsub b/modules/enet/SCsub
index c8f4b3885e..580e5a3eb0 100644
--- a/modules/enet/SCsub
+++ b/modules/enet/SCsub
@@ -7,6 +7,8 @@ env_enet = env_modules.Clone()
# Thirdparty source files
+thirdparty_obj = []
+
if env["builtin_enet"]:
thirdparty_dir = "#thirdparty/enet/"
thirdparty_sources = [
@@ -26,6 +28,16 @@ if env["builtin_enet"]:
env_thirdparty = env_enet.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
+
+
+# Godot source files
+
+module_obj = []
+
+env_enet.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
-env_enet.add_source_files(env.modules_sources, "*.cpp")
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/etc/SCsub b/modules/etc/SCsub
index 383bbf83c3..9b46f17916 100644
--- a/modules/etc/SCsub
+++ b/modules/etc/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_etc = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
# Not unbundled so far since not widespread as shared library
thirdparty_dir = "#thirdparty/etc2comp/"
thirdparty_sources = [
@@ -31,7 +34,15 @@ env_etc.Prepend(CPPPATH=[thirdparty_dir])
env_thirdparty = env_etc.Clone()
env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.modules_sources += thirdparty_obj
# Godot source files
-env_etc.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_etc.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
+
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/etc/image_etc.cpp b/modules/etc/image_compress_etc.cpp
index 4e9e64c6a7..bcdea41b43 100644
--- a/modules/etc/image_etc.cpp
+++ b/modules/etc/image_compress_etc.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* image_etc.cpp */
+/* image_compress_etc.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,14 +28,16 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "image_etc.h"
-#include "Etc.h"
-#include "EtcFilter.h"
+#include "image_compress_etc.h"
+
#include "core/io/image.h"
#include "core/os/copymem.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
+#include <Etc.h>
+#include <EtcFilter.h>
+
static Image::Format _get_etc2_mode(Image::UsedChannels format) {
switch (format) {
case Image::USED_CHANNELS_R:
diff --git a/modules/etc/image_etc.h b/modules/etc/image_compress_etc.h
index 7b4f26e127..016e64e4fc 100644
--- a/modules/etc/image_etc.h
+++ b/modules/etc/image_compress_etc.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* image_etc.h */
+/* image_compress_etc.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,9 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef IMAGE_ETC1_H
-#define IMAGE_ETC1_H
+#ifndef IMAGE_COMPRESS_ETC_H
+#define IMAGE_COMPRESS_ETC_H
void _register_etc_compress_func();
-#endif // IMAGE_ETC_H
+#endif // IMAGE_COMPRESS_ETC_H
diff --git a/modules/etc/register_types.cpp b/modules/etc/register_types.cpp
index 0972857808..225ba6b954 100644
--- a/modules/etc/register_types.cpp
+++ b/modules/etc/register_types.cpp
@@ -30,7 +30,7 @@
#include "register_types.h"
-#include "image_etc.h"
+#include "image_compress_etc.h"
#include "texture_loader_pkm.h"
static Ref<ResourceFormatPKM> resource_loader_pkm;
diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub
index bfc1658bb4..fc2535a6ca 100644
--- a/modules/freetype/SCsub
+++ b/modules/freetype/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_freetype = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
if env["builtin_freetype"]:
thirdparty_dir = "#thirdparty/freetype/"
thirdparty_sources = [
@@ -84,6 +87,7 @@ if env["builtin_freetype"]:
env_thirdparty = env_freetype.Clone()
env_thirdparty.disable_warnings()
lib = env_thirdparty.add_library("freetype_builtin", thirdparty_sources)
+ thirdparty_obj += lib
# Needs to be appended to arrive after libscene in the linker call,
# but we don't want it to arrive *after* system libs, so manual hack
@@ -98,5 +102,13 @@ if env["builtin_freetype"]:
if not inserted:
env.Append(LIBS=[lib])
+
# Godot source files
-env_freetype.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_freetype.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
+
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub
index bd06347afb..45354ce692 100644
--- a/modules/gdnative/SCsub
+++ b/modules/gdnative/SCsub
@@ -20,6 +20,7 @@ SConscript("net/SCsub")
SConscript("xr/SCsub")
SConscript("pluginscript/SCsub")
SConscript("videodecoder/SCsub")
+SConscript("text/SCsub")
import gdnative_builders
diff --git a/modules/gdnative/gdnative/array.cpp b/modules/gdnative/gdnative/array.cpp
index a5567f1e6b..863889acbc 100644
--- a/modules/gdnative/gdnative/array.cpp
+++ b/modules/gdnative/gdnative/array.cpp
@@ -90,6 +90,18 @@ void GDAPI godot_array_new_packed_vector2_array(godot_array *r_dest, const godot
}
}
+void GDAPI godot_array_new_packed_vector2i_array(godot_array *r_dest, const godot_packed_vector2i_array *p_pv2a) {
+ Array *dest = (Array *)r_dest;
+ Vector<Vector2i> *pca = (Vector<Vector2i> *)p_pv2a;
+ memnew_placement(dest, Array);
+ dest->resize(pca->size());
+
+ for (int i = 0; i < dest->size(); i++) {
+ Variant v = pca->operator[](i);
+ dest->operator[](i) = v;
+ }
+}
+
void GDAPI godot_array_new_packed_string_array(godot_array *r_dest, const godot_packed_string_array *p_psa) {
Array *dest = (Array *)r_dest;
Vector<String> *pca = (Vector<String> *)p_psa;
diff --git a/modules/gdnative/gdnative/packed_arrays.cpp b/modules/gdnative/gdnative/packed_arrays.cpp
index 9139fd89a1..cc1e05b8a4 100644
--- a/modules/gdnative/gdnative/packed_arrays.cpp
+++ b/modules/gdnative/gdnative/packed_arrays.cpp
@@ -49,6 +49,7 @@ static_assert(sizeof(godot_packed_float32_array) == sizeof(Vector<float>), "Vect
static_assert(sizeof(godot_packed_float64_array) == sizeof(Vector<double>), "Vector<double> size mismatch");
static_assert(sizeof(godot_packed_string_array) == sizeof(Vector<String>), "Vector<String> size mismatch");
static_assert(sizeof(godot_packed_vector2_array) == sizeof(Vector<Vector2>), "Vector<Vector2> size mismatch");
+static_assert(sizeof(godot_packed_vector2i_array) == sizeof(Vector<Vector2i>), "Vector<Vector2i> size mismatch");
static_assert(sizeof(godot_packed_vector3_array) == sizeof(Vector<Vector3>), "Vector<Vector3> size mismatch");
static_assert(sizeof(godot_packed_color_array) == sizeof(Vector<Color>), "Vector<Color> size mismatch");
@@ -799,6 +800,118 @@ void GDAPI godot_packed_vector2_array_destroy(godot_packed_vector2_array *p_self
((Vector<Vector2> *)p_self)->~Vector();
}
+// vector2i
+
+void GDAPI godot_packed_vector2i_array_new(godot_packed_vector2i_array *r_dest) {
+ Vector<Vector2i> *dest = (Vector<Vector2i> *)r_dest;
+ memnew_placement(dest, Vector<Vector2i>);
+}
+
+void GDAPI godot_packed_vector2i_array_new_copy(godot_packed_vector2i_array *r_dest, const godot_packed_vector2i_array *p_src) {
+ Vector<Vector2i> *dest = (Vector<Vector2i> *)r_dest;
+ const Vector<Vector2i> *src = (const Vector<Vector2i> *)p_src;
+ memnew_placement(dest, Vector<Vector2i>(*src));
+}
+
+void GDAPI godot_packed_vector2i_array_new_with_array(godot_packed_vector2i_array *r_dest, const godot_array *p_a) {
+ Vector<Vector2i> *dest = (Vector<Vector2i> *)r_dest;
+ Array *a = (Array *)p_a;
+ memnew_placement(dest, Vector<Vector2i>);
+
+ dest->resize(a->size());
+ for (int i = 0; i < a->size(); i++) {
+ dest->set(i, (*a)[i]);
+ }
+}
+
+const godot_vector2i GDAPI *godot_packed_vector2i_array_ptr(const godot_packed_vector2i_array *p_self) {
+ const Vector<Vector2i> *self = (const Vector<Vector2i> *)p_self;
+ return (const godot_vector2i *)self->ptr();
+}
+
+godot_vector2i GDAPI *godot_packed_vector2i_array_ptrw(godot_packed_vector2i_array *p_self) {
+ Vector<Vector2i> *self = (Vector<Vector2i> *)p_self;
+ return (godot_vector2i *)self->ptrw();
+}
+
+void GDAPI godot_packed_vector2i_array_append(godot_packed_vector2i_array *p_self, const godot_vector2i *p_data) {
+ Vector<Vector2i> *self = (Vector<Vector2i> *)p_self;
+ Vector2i &s = *(Vector2i *)p_data;
+ self->push_back(s);
+}
+
+void GDAPI godot_packed_vector2i_array_append_array(godot_packed_vector2i_array *p_self, const godot_packed_vector2i_array *p_array) {
+ Vector<Vector2i> *self = (Vector<Vector2i> *)p_self;
+ Vector<Vector2i> *array = (Vector<Vector2i> *)p_array;
+ self->append_array(*array);
+}
+
+godot_error GDAPI godot_packed_vector2i_array_insert(godot_packed_vector2i_array *p_self, const godot_int p_idx, const godot_vector2i *p_data) {
+ Vector<Vector2i> *self = (Vector<Vector2i> *)p_self;
+ Vector2i &s = *(Vector2i *)p_data;
+ return (godot_error)self->insert(p_idx, s);
+}
+
+godot_bool GDAPI godot_packed_vector2i_array_has(godot_packed_vector2i_array *p_self, const godot_vector2i *p_value) {
+ Vector<Vector2i> *self = (Vector<Vector2i> *)p_self;
+ Vector2i &v = *(Vector2i *)p_value;
+ return (godot_bool)self->has(v);
+}
+
+void GDAPI godot_packed_vector2i_array_sort(godot_packed_vector2i_array *p_self) {
+ Vector<Vector2i> *self = (Vector<Vector2i> *)p_self;
+ self->sort();
+}
+
+void GDAPI godot_packed_vector2i_array_invert(godot_packed_vector2i_array *p_self) {
+ Vector<Vector2i> *self = (Vector<Vector2i> *)p_self;
+ self->invert();
+}
+
+void GDAPI godot_packed_vector2i_array_push_back(godot_packed_vector2i_array *p_self, const godot_vector2i *p_data) {
+ Vector<Vector2i> *self = (Vector<Vector2i> *)p_self;
+ Vector2i &s = *(Vector2i *)p_data;
+ self->push_back(s);
+}
+
+void GDAPI godot_packed_vector2i_array_remove(godot_packed_vector2i_array *p_self, const godot_int p_idx) {
+ Vector<Vector2i> *self = (Vector<Vector2i> *)p_self;
+ self->remove(p_idx);
+}
+
+void GDAPI godot_packed_vector2i_array_resize(godot_packed_vector2i_array *p_self, const godot_int p_size) {
+ Vector<Vector2i> *self = (Vector<Vector2i> *)p_self;
+ self->resize(p_size);
+}
+
+void GDAPI godot_packed_vector2i_array_set(godot_packed_vector2i_array *p_self, const godot_int p_idx, const godot_vector2i *p_data) {
+ Vector<Vector2i> *self = (Vector<Vector2i> *)p_self;
+ Vector2i &s = *(Vector2i *)p_data;
+ self->set(p_idx, s);
+}
+
+godot_vector2i GDAPI godot_packed_vector2i_array_get(const godot_packed_vector2i_array *p_self, const godot_int p_idx) {
+ const Vector<Vector2i> *self = (const Vector<Vector2i> *)p_self;
+ godot_vector2i v;
+ Vector2i *s = (Vector2i *)&v;
+ *s = self->get(p_idx);
+ return v;
+}
+
+godot_int GDAPI godot_packed_vector2i_array_size(const godot_packed_vector2i_array *p_self) {
+ const Vector<Vector2i> *self = (const Vector<Vector2i> *)p_self;
+ return self->size();
+}
+
+godot_bool GDAPI godot_packed_vector2i_array_empty(const godot_packed_vector2i_array *p_self) {
+ const Vector<Vector2i> *self = (const Vector<Vector2i> *)p_self;
+ return self->empty();
+}
+
+void GDAPI godot_packed_vector2i_array_destroy(godot_packed_vector2i_array *p_self) {
+ ((Vector<Vector2i> *)p_self)->~Vector();
+}
+
// vector3
void GDAPI godot_packed_vector3_array_new(godot_packed_vector3_array *r_dest) {
diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json
index 40d0f75871..84b36b3745 100644
--- a/modules/gdnative/gdnative_api.json
+++ b/modules/gdnative/gdnative_api.json
@@ -260,6 +260,14 @@
]
},
{
+ "name": "godot_array_new_packed_vector2i_array",
+ "return_type": "void",
+ "arguments": [
+ ["godot_array *", "r_dest"],
+ ["const godot_packed_vector2i_array *", "p_pv2a"]
+ ]
+ },
+ {
"name": "godot_array_new_packed_string_array",
"return_type": "void",
"arguments": [
@@ -2638,6 +2646,152 @@
]
},
{
+ "name": "godot_packed_vector2i_array_new",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_vector2i_array *", "r_dest"]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2i_array_new_copy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_vector2i_array *", "r_dest"],
+ ["const godot_packed_vector2i_array *", "p_src"]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2i_array_new_with_array",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_vector2i_array *", "r_dest"],
+ ["const godot_array *", "p_a"]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2i_array_empty",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_packed_vector2i_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2i_array_append",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_vector2i_array *", "p_self"],
+ ["const godot_vector2i *", "p_data"]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2i_array_append_array",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_vector2i_array *", "p_self"],
+ ["const godot_packed_vector2i_array *", "p_array"]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2i_array_insert",
+ "return_type": "godot_error",
+ "arguments": [
+ ["godot_packed_vector2i_array *", "p_self"],
+ ["const godot_int", "p_idx"],
+ ["const godot_vector2i *", "p_data"]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2i_array_has",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["godot_packed_vector2i_array *", "p_self"],
+ ["const godot_vector2i *", "p_value"]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2i_array_sort",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_vector2i_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2i_array_invert",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_vector2i_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2i_array_push_back",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_vector2i_array *", "p_self"],
+ ["const godot_vector2i *", "p_data"]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2i_array_remove",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_vector2i_array *", "p_self"],
+ ["const godot_int", "p_idx"]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2i_array_resize",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_vector2i_array *", "p_self"],
+ ["const godot_int", "p_size"]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2i_array_ptr",
+ "return_type": "const godot_vector2i *",
+ "arguments": [
+ ["const godot_packed_vector2i_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2i_array_ptrw",
+ "return_type": "godot_vector2i *",
+ "arguments": [
+ ["godot_packed_vector2i_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2i_array_set",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_vector2i_array *", "p_self"],
+ ["const godot_int", "p_idx"],
+ ["const godot_vector2i *", "p_data"]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2i_array_get",
+ "return_type": "godot_vector2i",
+ "arguments": [
+ ["const godot_packed_vector2i_array *", "p_self"],
+ ["const godot_int", "p_idx"]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2i_array_size",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_packed_vector2i_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2i_array_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_vector2i_array *", "p_self"]
+ ]
+ },
+ {
"name": "godot_packed_vector3_array_new",
"return_type": "void",
"arguments": [
@@ -7615,6 +7769,306 @@
]
}
]
+ },
+ {
+ "name": "text",
+ "type": "TEXT",
+ "version": {
+ "major": 1,
+ "minor": 0
+ },
+ "next": null,
+ "api": [
+ {
+ "name": "godot_text_register_interface",
+ "return_type": "void",
+ "arguments": [
+ ["const godot_text_interface_gdnative *", "p_interface"],
+ ["const godot_string *", "p_name"],
+ ["uint32_t", "p_features"]
+ ]
+ },
+ {
+ "name": "godot_glyph_new",
+ "return_type": "void",
+ "arguments": [
+ ["godot_glyph *", "r_dest"]
+ ]
+ },
+ {
+ "name": "godot_glyph_get_range",
+ "return_type": "godot_vector2i",
+ "arguments": [
+ ["const godot_glyph *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_glyph_set_range",
+ "return_type": "void",
+ "arguments": [
+ ["godot_glyph *", "p_self"],
+ ["const godot_vector2i *", "p_range"]
+ ]
+ },
+ {
+ "name": "godot_glyph_get_count",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_glyph *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_glyph_set_count",
+ "return_type": "void",
+ "arguments": [
+ ["godot_glyph *", "p_self"],
+ ["godot_int", "p_count"]
+ ]
+ },
+ {
+ "name": "godot_glyph_get_repeat",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_glyph *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_glyph_set_repeat",
+ "return_type": "void",
+ "arguments": [
+ ["godot_glyph *", "p_self"],
+ ["godot_int", "p_repeat"]
+ ]
+ },
+ {
+ "name": "godot_glyph_get_flags",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_glyph *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_glyph_set_flags",
+ "return_type": "void",
+ "arguments": [
+ ["godot_glyph *", "p_self"],
+ ["godot_int", "p_flags"]
+ ]
+ },
+ {
+ "name": "godot_glyph_get_offset",
+ "return_type": "godot_vector2",
+ "arguments": [
+ ["const godot_glyph *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_glyph_set_offset",
+ "return_type": "void",
+ "arguments": [
+ ["godot_glyph *", "p_self"],
+ ["const godot_vector2 *", "p_offset"]
+ ]
+ },
+ {
+ "name": "godot_glyph_get_advance",
+ "return_type": "godot_real",
+ "arguments": [
+ ["const godot_glyph *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_glyph_set_advance",
+ "return_type": "void",
+ "arguments": [
+ ["godot_glyph *", "p_self"],
+ ["godot_real", "p_advance"]
+ ]
+ },
+ {
+ "name": "godot_glyph_get_font",
+ "return_type": "godot_rid",
+ "arguments": [
+ ["const godot_glyph *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_glyph_set_font",
+ "return_type": "void ",
+ "arguments": [
+ ["godot_glyph *", "p_self"],
+ ["godot_rid *", "p_font"]
+ ]
+ },
+ {
+ "name": "godot_glyph_get_font_size",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_glyph *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_glyph_set_font_size",
+ "return_type": "void",
+ "arguments": [
+ ["godot_glyph *", "p_self"],
+ ["godot_int", "p_size"]
+ ]
+ },
+ {
+ "name": "godot_glyph_get_index",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_glyph *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_glyph_set_index",
+ "return_type": "void",
+ "arguments": [
+ ["godot_glyph *", "p_self"],
+ ["godot_int", "p_index"]
+ ]
+ },
+ {
+ "name": "godot_packed_glyph_array_new",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_glyph_array *", "r_dest"]
+ ]
+ },
+ {
+ "name": "godot_packed_glyph_array_new_copy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_glyph_array *", "r_dest"],
+ ["const godot_packed_glyph_array *", "p_src"]
+ ]
+ },
+ {
+ "name": "godot_packed_glyph_array_empty",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_packed_glyph_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_glyph_array_append",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_glyph_array *", "p_self"],
+ ["const godot_glyph *", "p_data"]
+ ]
+ },
+ {
+ "name": "godot_packed_glyph_array_append_array",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_glyph_array *", "p_self"],
+ ["const godot_packed_glyph_array *", "p_array"]
+ ]
+ },
+ {
+ "name": "godot_packed_glyph_array_insert",
+ "return_type": "godot_error",
+ "arguments": [
+ ["godot_packed_glyph_array *", "p_self"],
+ ["const godot_int", "p_idx"],
+ ["const godot_glyph *", "p_data"]
+ ]
+ },
+ {
+ "name": "godot_packed_glyph_array_has",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["godot_packed_glyph_array *", "p_self"],
+ ["const godot_glyph *", "p_value"]
+ ]
+ },
+ {
+ "name": "godot_packed_glyph_array_sort",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_glyph_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_glyph_array_invert",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_glyph_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_glyph_array_push_back",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_glyph_array *", "p_self"],
+ ["const godot_glyph *", "p_data"]
+ ]
+ },
+ {
+ "name": "godot_packed_glyph_array_remove",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_glyph_array *", "p_self"],
+ ["const godot_int", "p_idx"]
+ ]
+ },
+ {
+ "name": "godot_packed_glyph_array_resize",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_glyph_array *", "p_self"],
+ ["const godot_int", "p_size"]
+ ]
+ },
+ {
+ "name": "godot_packed_glyph_array_ptr",
+ "return_type": "const godot_glyph *",
+ "arguments": [
+ ["const godot_packed_glyph_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_glyph_array_ptrw",
+ "return_type": "godot_glyph *",
+ "arguments": [
+ ["godot_packed_glyph_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_glyph_array_set",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_glyph_array *", "p_self"],
+ ["const godot_int", "p_idx"],
+ ["const godot_glyph *", "p_data"]
+ ]
+ },
+ {
+ "name": "godot_packed_glyph_array_get",
+ "return_type": "godot_glyph",
+ "arguments": [
+ ["const godot_packed_glyph_array *", "p_self"],
+ ["const godot_int", "p_idx"]
+ ]
+ },
+ {
+ "name": "godot_packed_glyph_array_size",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_packed_glyph_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_glyph_array_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_glyph_array *", "p_self"]
+ ]
+ }
+ ]
}
]
}
diff --git a/modules/gdnative/gdnative_builders.py b/modules/gdnative/gdnative_builders.py
index 28e4957b2f..d03298d7a9 100644
--- a/modules/gdnative/gdnative_builders.py
+++ b/modules/gdnative/gdnative_builders.py
@@ -24,6 +24,7 @@ def _build_gdnative_api_struct_header(api):
"#include <net/godot_net.h>",
"#include <pluginscript/godot_pluginscript.h>",
"#include <videodecoder/godot_videodecoder.h>",
+ "#include <text/godot_text.h>",
"",
"#ifdef __cplusplus",
'extern "C" {',
diff --git a/modules/gdnative/gdnative_library_editor_plugin.cpp b/modules/gdnative/gdnative_library_editor_plugin.cpp
index f0f095ddf5..52f8c837c4 100644
--- a/modules/gdnative/gdnative_library_editor_plugin.cpp
+++ b/modules/gdnative/gdnative_library_editor_plugin.cpp
@@ -308,11 +308,11 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() {
platform_android.library_extension = "*.so";
platforms["Android"] = platform_android;
- // TODO: Javascript platform is not supported yet
- // NativePlatformConfig platform_html5;
- // platform_html5.name = "HTML5";
- // platform_html5.library_extension = "*.wasm";
- // platforms["Javascript"] = platform_html5;
+ NativePlatformConfig platform_html5;
+ platform_html5.name = "HTML5";
+ platform_html5.entries.push_back("wasm32");
+ platform_html5.library_extension = "*.wasm";
+ platforms["HTML5"] = platform_html5;
NativePlatformConfig platform_ios;
platform_ios.name = "iOS";
@@ -382,7 +382,7 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() {
new_architecture_dialog->add_child(new_architecture_input);
// new_architecture_dialog->set_custom_minimum_size(Vector2(300, 80) * EDSCALE);
new_architecture_input->set_anchors_and_margins_preset(PRESET_HCENTER_WIDE, PRESET_MODE_MINSIZE, 5 * EDSCALE);
- new_architecture_dialog->get_ok()->connect("pressed", callable_mp(this, &GDNativeLibraryEditor::_on_create_new_entry));
+ new_architecture_dialog->get_ok_button()->connect("pressed", callable_mp(this, &GDNativeLibraryEditor::_on_create_new_entry));
}
void GDNativeLibraryEditorPlugin::edit(Object *p_node) {
diff --git a/modules/gdnative/include/gdnative/array.h b/modules/gdnative/include/gdnative/array.h
index 4db685873f..7a59493b7d 100644
--- a/modules/gdnative/include/gdnative/array.h
+++ b/modules/gdnative/include/gdnative/array.h
@@ -65,6 +65,7 @@ void GDAPI godot_array_new_copy(godot_array *r_dest, const godot_array *p_src);
void GDAPI godot_array_new_packed_color_array(godot_array *r_dest, const godot_packed_color_array *p_pca);
void GDAPI godot_array_new_packed_vector3_array(godot_array *r_dest, const godot_packed_vector3_array *p_pv3a);
void GDAPI godot_array_new_packed_vector2_array(godot_array *r_dest, const godot_packed_vector2_array *p_pv2a);
+void GDAPI godot_array_new_packed_vector2i_array(godot_array *r_dest, const godot_packed_vector2i_array *p_pv2a);
void GDAPI godot_array_new_packed_string_array(godot_array *r_dest, const godot_packed_string_array *p_psa);
void GDAPI godot_array_new_packed_float32_array(godot_array *r_dest, const godot_packed_float32_array *p_pra);
void GDAPI godot_array_new_packed_float64_array(godot_array *r_dest, const godot_packed_float64_array *p_pra);
diff --git a/modules/gdnative/include/gdnative/packed_arrays.h b/modules/gdnative/include/gdnative/packed_arrays.h
index 6a1727d76f..ce9579f307 100644
--- a/modules/gdnative/include/gdnative/packed_arrays.h
+++ b/modules/gdnative/include/gdnative/packed_arrays.h
@@ -114,6 +114,17 @@ typedef struct {
} godot_packed_vector2_array;
#endif
+/////// PackedVector2iArray
+
+#define GODOT_PACKED_VECTOR2I_ARRAY_SIZE (2 * sizeof(void *))
+
+#ifndef GODOT_CORE_API_GODOT_PACKED_VECTOR2I_ARRAY_TYPE_DEFINED
+#define GODOT_CORE_API_GODOT_PACKED_VECTOR2I_ARRAY_TYPE_DEFINED
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_PACKED_VECTOR2I_ARRAY_SIZE];
+} godot_packed_vector2i_array;
+#endif
+
/////// PackedVector3Array
#define GODOT_PACKED_VECTOR3_ARRAY_SIZE (2 * sizeof(void *))
@@ -404,6 +415,42 @@ godot_bool GDAPI godot_packed_vector2_array_empty(const godot_packed_vector2_arr
void GDAPI godot_packed_vector2_array_destroy(godot_packed_vector2_array *p_self);
+// vector2i
+
+void GDAPI godot_packed_vector2i_array_new(godot_packed_vector2i_array *r_dest);
+void GDAPI godot_packed_vector2i_array_new_copy(godot_packed_vector2i_array *r_dest, const godot_packed_vector2i_array *p_src);
+void GDAPI godot_packed_vector2i_array_new_with_array(godot_packed_vector2i_array *r_dest, const godot_array *p_a);
+
+const godot_vector2i GDAPI *godot_packed_vector2i_array_ptr(const godot_packed_vector2i_array *p_self);
+godot_vector2i GDAPI *godot_packed_vector2i_array_ptrw(godot_packed_vector2i_array *p_self);
+
+void GDAPI godot_packed_vector2i_array_append(godot_packed_vector2i_array *p_self, const godot_vector2i *p_data);
+
+void GDAPI godot_packed_vector2i_array_append_array(godot_packed_vector2i_array *p_self, const godot_packed_vector2i_array *p_array);
+
+godot_error GDAPI godot_packed_vector2i_array_insert(godot_packed_vector2i_array *p_self, const godot_int p_idx, const godot_vector2i *p_data);
+
+godot_bool GDAPI godot_packed_vector2i_array_has(godot_packed_vector2i_array *p_self, const godot_vector2i *p_value);
+
+void GDAPI godot_packed_vector2i_array_sort(godot_packed_vector2i_array *p_self);
+
+void GDAPI godot_packed_vector2i_array_invert(godot_packed_vector2i_array *p_self);
+
+void GDAPI godot_packed_vector2i_array_push_back(godot_packed_vector2i_array *p_self, const godot_vector2i *p_data);
+
+void GDAPI godot_packed_vector2i_array_remove(godot_packed_vector2i_array *p_self, const godot_int p_idx);
+
+void GDAPI godot_packed_vector2i_array_resize(godot_packed_vector2i_array *p_self, const godot_int p_size);
+
+void GDAPI godot_packed_vector2i_array_set(godot_packed_vector2i_array *p_self, const godot_int p_idx, const godot_vector2i *p_data);
+godot_vector2i GDAPI godot_packed_vector2i_array_get(const godot_packed_vector2i_array *p_self, const godot_int p_idx);
+
+godot_int GDAPI godot_packed_vector2i_array_size(const godot_packed_vector2i_array *p_self);
+
+godot_bool GDAPI godot_packed_vector2i_array_empty(const godot_packed_vector2i_array *p_self);
+
+void GDAPI godot_packed_vector2i_array_destroy(godot_packed_vector2i_array *p_self);
+
// vector3
void GDAPI godot_packed_vector3_array_new(godot_packed_vector3_array *r_dest);
diff --git a/modules/gdnative/include/gdnative/variant.h b/modules/gdnative/include/gdnative/variant.h
index 0a611b76e9..2e803d602b 100644
--- a/modules/gdnative/include/gdnative/variant.h
+++ b/modules/gdnative/include/gdnative/variant.h
@@ -52,7 +52,7 @@ typedef enum godot_variant_type {
// atomic types
GODOT_VARIANT_TYPE_BOOL,
GODOT_VARIANT_TYPE_INT,
- GODOT_VARIANT_TYPE_REAL,
+ GODOT_VARIANT_TYPE_FLOAT,
GODOT_VARIANT_TYPE_STRING,
// math types
diff --git a/modules/gdnative/include/pluginscript/godot_pluginscript.h b/modules/gdnative/include/pluginscript/godot_pluginscript.h
index 406c3ba663..e4b1fd5eb0 100644
--- a/modules/gdnative/include/pluginscript/godot_pluginscript.h
+++ b/modules/gdnative/include/pluginscript/godot_pluginscript.h
@@ -72,6 +72,7 @@ typedef struct {
godot_string_name name;
godot_bool is_tool;
godot_string_name base;
+ godot_string icon_path;
// Member lines format: {<string>: <int>}
godot_dictionary member_lines;
@@ -127,6 +128,7 @@ typedef struct {
const char **string_delimiters; // nullptr terminated array
godot_bool has_named_classes;
godot_bool supports_builtin_mode;
+ godot_bool can_inherit_from_file;
godot_string (*get_template_source_code)(godot_pluginscript_language_data *p_data, const godot_string *p_class_name, const godot_string *p_base_class_name);
godot_bool (*validate)(godot_pluginscript_language_data *p_data, const godot_string *p_script, int *r_line_error, int *r_col_error, godot_string *r_test_error, const godot_string *p_path, godot_packed_string_array *r_functions);
diff --git a/modules/gdnative/include/text/godot_text.h b/modules/gdnative/include/text/godot_text.h
new file mode 100644
index 0000000000..6885f2463d
--- /dev/null
+++ b/modules/gdnative/include/text/godot_text.h
@@ -0,0 +1,231 @@
+/*************************************************************************/
+/* godot_text.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GODOT_NATIVETEXT_H
+#define GODOT_NATIVETEXT_H
+
+#include <gdnative/gdnative.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GODOT_TEXT_API_MAJOR 1
+#define GODOT_TEXT_API_MINOR 0
+
+#define GODOT_GLYPH_SIZE 40
+
+#ifndef GODOT_TEXT_API_GODOT_GLYPH_TYPE_DEFINED
+#define GODOT_TEXT_API_GODOT_GLYPH_TYPE_DEFINED
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_GLYPH_SIZE];
+} godot_glyph;
+#endif
+
+#define GODOT_PACKED_GLYPH_ARRAY_SIZE (2 * sizeof(void *))
+
+#ifndef GODOT_TEXT_API_GODOT_PACKED_GLYPH_ARRAY_TYPE_DEFINED
+#define GODOT_TEXT_API_GODOT_PACKED_GLYPH_ARRAY_TYPE_DEFINED
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_PACKED_GLYPH_ARRAY_SIZE];
+} godot_packed_glyph_array;
+#endif
+
+typedef struct {
+ godot_gdnative_api_version version;
+ void *(*constructor)(godot_object *);
+ void (*destructor)(void *);
+ godot_string (*get_name)(const void *);
+ godot_bool (*has_feature)(const void *, godot_int);
+ bool (*load_support_data)(void *, const godot_string *);
+ godot_string (*get_support_data_filename)(const void *);
+ godot_string (*get_support_data_info)(const void *);
+ bool (*save_support_data)(void *, const godot_string *);
+ bool (*is_locale_right_to_left)(void *, const godot_string *);
+ void (*free)(void *, godot_rid *);
+ bool (*has)(void *, godot_rid *);
+ godot_rid (*create_font_system)(void *, const godot_string *, int);
+ godot_rid (*create_font_resource)(void *, const godot_string *, int);
+ godot_rid (*create_font_memory)(void *, const uint8_t *, size_t, godot_string *, int);
+ float (*font_get_height)(void *, godot_rid *, int);
+ float (*font_get_ascent)(void *, godot_rid *, int);
+ float (*font_get_descent)(void *, godot_rid *, int);
+ float (*font_get_underline_position)(void *, godot_rid *, int);
+ float (*font_get_underline_thickness)(void *, godot_rid *, int);
+ void (*font_set_antialiased)(void *, godot_rid *, bool);
+ bool (*font_get_antialiased)(void *, godot_rid *);
+ godot_dictionary (*font_get_feature_list)(void *, godot_rid *);
+ godot_dictionary (*font_get_variation_list)(void *, godot_rid *);
+ void (*font_set_variation)(void *, godot_rid *, const godot_string *, double);
+ double (*font_get_variation)(void *, godot_rid *, const godot_string *);
+ void (*font_set_distance_field_hint)(void *, godot_rid *, bool);
+ bool (*font_get_distance_field_hint)(void *, godot_rid *);
+ void (*font_set_hinting)(void *, godot_rid *, godot_int);
+ godot_int (*font_get_hinting)(void *, godot_rid *);
+ void (*font_set_force_autohinter)(void *, godot_rid *, bool);
+ bool (*font_get_force_autohinter)(void *, godot_rid *);
+ bool (*font_has_char)(void *, godot_rid *, char32_t);
+ godot_string (*font_get_supported_chars)(void *, godot_rid *);
+ bool (*font_has_outline)(void *, godot_rid *);
+ int (*font_get_base_size)(void *, godot_rid *);
+ bool (*font_is_language_supported)(void *, godot_rid *, const godot_string *);
+ void (*font_set_language_support_override)(void *, godot_rid *, const godot_string *, bool);
+ bool (*font_get_language_support_override)(void *, godot_rid *, const godot_string *);
+ void (*font_remove_language_support_override)(void *, godot_rid *, const godot_string *);
+ godot_packed_string_array (*font_get_language_support_overrides)(void *, godot_rid *);
+ bool (*font_is_script_supported)(void *, godot_rid *, const godot_string *);
+ void (*font_set_script_support_override)(void *, godot_rid *, const godot_string *, bool);
+ bool (*font_get_script_support_override)(void *, godot_rid *, const godot_string *);
+ void (*font_remove_script_support_override)(void *, godot_rid *, const godot_string *);
+ godot_packed_string_array (*font_get_script_support_overrides)(void *, godot_rid *);
+ uint32_t (*font_get_glyph_index)(void *, godot_rid *, char32_t, char32_t);
+ godot_vector2 (*font_get_glyph_advance)(void *, godot_rid *, uint32_t, int);
+ godot_vector2 (*font_get_glyph_kerning)(void *, godot_rid *, uint32_t, uint32_t, int);
+ godot_vector2 (*font_draw_glyph)(void *, godot_rid *, godot_rid *, int, const godot_vector2 *, uint32_t, const godot_color *);
+ godot_vector2 (*font_draw_glyph_outline)(void *, godot_rid *, godot_rid *, int, int, const godot_vector2 *, uint32_t, const godot_color *);
+ float (*font_get_oversampling)(void *);
+ void (*font_set_oversampling)(void *, float);
+ godot_packed_string_array (*get_system_fonts)(void *);
+ godot_rid (*create_shaped_text)(void *, godot_int, godot_int);
+ void (*shaped_text_clear)(void *, godot_rid *);
+ void (*shaped_text_set_direction)(void *, godot_rid *, godot_int);
+ godot_int (*shaped_text_get_direction)(void *, godot_rid *);
+ void (*shaped_text_set_bidi_override)(void *, godot_rid *, const godot_packed_vector2i_array *);
+ void (*shaped_text_set_orientation)(void *, godot_rid *, godot_int);
+ godot_int (*shaped_text_get_orientation)(void *, godot_rid *);
+ void (*shaped_text_set_preserve_invalid)(void *, godot_rid *, bool);
+ bool (*shaped_text_get_preserve_invalid)(void *, godot_rid *);
+ void (*shaped_text_set_preserve_control)(void *, godot_rid *, bool);
+ bool (*shaped_text_get_preserve_control)(void *, godot_rid *);
+ bool (*shaped_text_add_string)(void *, godot_rid *, const godot_string *, const godot_rid **, int, const godot_dictionary *, const godot_string *);
+ bool (*shaped_text_add_object)(void *, godot_rid *, const godot_variant *, const godot_vector2 *, godot_int, godot_int);
+ bool (*shaped_text_resize_object)(void *, godot_rid *, const godot_variant *, const godot_vector2 *, godot_int);
+ godot_rid (*shaped_text_substr)(void *, godot_rid *, godot_int, godot_int);
+ godot_rid (*shaped_text_get_parent)(void *, godot_rid *);
+ float (*shaped_text_fit_to_width)(void *, godot_rid *, float, uint8_t);
+ float (*shaped_text_tab_align)(void *, godot_rid *, godot_packed_float32_array *);
+ bool (*shaped_text_shape)(void *, godot_rid *);
+ bool (*shaped_text_update_breaks)(void *, godot_rid *);
+ bool (*shaped_text_update_justification_ops)(void *, godot_rid *);
+ bool (*shaped_text_is_ready)(void *, godot_rid *);
+ godot_packed_glyph_array (*shaped_text_get_glyphs)(void *, godot_rid *);
+ godot_vector2i (*shaped_text_get_range)(void *, godot_rid *);
+ godot_packed_glyph_array (*shaped_text_sort_logical)(void *, godot_rid *);
+ godot_packed_vector2i_array (*shaped_text_get_line_breaks_adv)(void *, godot_rid *, godot_packed_float32_array *, int, bool, uint8_t);
+ godot_packed_vector2i_array (*shaped_text_get_line_breaks)(void *, godot_rid *, float, int, uint8_t);
+ godot_packed_vector2i_array (*shaped_text_get_word_breaks)(void *, godot_rid *);
+ godot_array (*shaped_text_get_objects)(void *, godot_rid *);
+ godot_rect2 (*shaped_text_get_object_rect)(void *, godot_rid *, const godot_variant *);
+ godot_vector2 (*shaped_text_get_size)(void *, godot_rid *);
+ float (*shaped_text_get_ascent)(void *, godot_rid *);
+ float (*shaped_text_get_descent)(void *, godot_rid *);
+ float (*shaped_text_get_width)(void *, godot_rid *);
+ float (*shaped_text_get_underline_position)(void *, godot_rid *);
+ float (*shaped_text_get_underline_thickness)(void *, godot_rid *);
+ godot_string (*format_number)(void *, const godot_string *, const godot_string *);
+ godot_string (*parse_number)(void *, const godot_string *, const godot_string *);
+ godot_string (*percent_sign)(void *, const godot_string *);
+} godot_text_interface_gdnative;
+
+void GDAPI godot_text_register_interface(const godot_text_interface_gdnative *p_interface, const godot_string *p_name, uint32_t p_features);
+
+// Glyph
+
+void GDAPI godot_glyph_new(godot_glyph *r_dest);
+
+godot_vector2i GDAPI godot_glyph_get_range(const godot_glyph *p_self);
+void GDAPI godot_glyph_set_range(godot_glyph *p_self, const godot_vector2i *p_range);
+
+godot_int GDAPI godot_glyph_get_count(const godot_glyph *p_self);
+void GDAPI godot_glyph_set_count(godot_glyph *p_self, godot_int p_count);
+
+godot_int GDAPI godot_glyph_get_repeat(const godot_glyph *p_self);
+void GDAPI godot_glyph_set_repeat(godot_glyph *p_self, godot_int p_repeat);
+
+godot_int GDAPI godot_glyph_get_flags(const godot_glyph *p_self);
+void GDAPI godot_glyph_set_flags(godot_glyph *p_self, godot_int p_flags);
+
+godot_vector2 GDAPI godot_glyph_get_offset(const godot_glyph *p_self);
+void GDAPI godot_glyph_set_offset(godot_glyph *p_self, const godot_vector2 *p_offset);
+
+godot_real GDAPI godot_glyph_get_advance(const godot_glyph *p_self);
+void GDAPI godot_glyph_set_advance(godot_glyph *p_self, godot_real p_advance);
+
+godot_rid GDAPI godot_glyph_get_font(const godot_glyph *p_self);
+void GDAPI godot_glyph_set_font(godot_glyph *p_self, godot_rid *p_font);
+
+godot_int GDAPI godot_glyph_get_font_size(const godot_glyph *p_self);
+void GDAPI godot_glyph_set_font_size(godot_glyph *p_self, godot_int p_size);
+
+godot_int GDAPI godot_glyph_get_index(const godot_glyph *p_self);
+void GDAPI godot_glyph_set_index(godot_glyph *p_self, godot_int p_index);
+
+// GlyphArray
+
+void GDAPI godot_packed_glyph_array_new(godot_packed_glyph_array *r_dest);
+void GDAPI godot_packed_glyph_array_new_copy(godot_packed_glyph_array *r_dest, const godot_packed_glyph_array *p_src);
+
+const godot_glyph GDAPI *godot_packed_glyph_array_ptr(const godot_packed_glyph_array *p_self);
+godot_glyph GDAPI *godot_packed_glyph_array_ptrw(godot_packed_glyph_array *p_self);
+
+void GDAPI godot_packed_glyph_array_append(godot_packed_glyph_array *p_self, const godot_glyph *p_data);
+
+void GDAPI godot_packed_glyph_array_append_array(godot_packed_glyph_array *p_self, const godot_packed_glyph_array *p_array);
+
+godot_error GDAPI godot_packed_glyph_array_insert(godot_packed_glyph_array *p_self, const godot_int p_idx, const godot_glyph *p_data);
+
+godot_bool GDAPI godot_packed_glyph_array_has(godot_packed_glyph_array *p_self, const godot_glyph *p_value);
+
+void GDAPI godot_packed_glyph_array_sort(godot_packed_glyph_array *p_self);
+
+void GDAPI godot_packed_glyph_array_invert(godot_packed_glyph_array *p_self);
+
+void GDAPI godot_packed_glyph_array_push_back(godot_packed_glyph_array *p_self, const godot_glyph *p_data);
+
+void GDAPI godot_packed_glyph_array_remove(godot_packed_glyph_array *p_self, godot_int p_idx);
+
+void GDAPI godot_packed_glyph_array_resize(godot_packed_glyph_array *p_self, godot_int p_size);
+
+void GDAPI godot_packed_glyph_array_set(godot_packed_glyph_array *p_self, godot_int p_idx, const godot_glyph *p_data);
+godot_glyph GDAPI godot_packed_glyph_array_get(const godot_packed_glyph_array *p_self, godot_int p_idx);
+
+godot_int GDAPI godot_packed_glyph_array_size(const godot_packed_glyph_array *p_self);
+
+godot_bool GDAPI godot_packed_glyph_array_empty(const godot_packed_glyph_array *p_self);
+
+void GDAPI godot_packed_glyph_array_destroy(godot_packed_glyph_array *p_self);
+
+// Grapheme
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !GODOT_NATIVETEXT_H */
diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h
index 2aaa4be325..e91d9b7bfb 100644
--- a/modules/gdnative/nativescript/nativescript.h
+++ b/modules/gdnative/nativescript/nativescript.h
@@ -31,6 +31,7 @@
#ifndef NATIVE_SCRIPT_H
#define NATIVE_SCRIPT_H
+#include "core/doc_data.h"
#include "core/io/resource.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
@@ -152,6 +153,13 @@ public:
virtual void set_source_code(const String &p_code) override;
virtual Error reload(bool p_keep_state = false) override;
+#ifdef TOOLS_ENABLED
+ virtual const Vector<DocData::ClassDoc> &get_documentation() const override {
+ static Vector<DocData::ClassDoc> docs;
+ return docs;
+ }
+#endif // TOOLS_ENABLED
+
virtual bool has_method(const StringName &p_method) const override;
virtual MethodInfo get_method_info(const StringName &p_method) const override;
diff --git a/modules/gdnative/pluginscript/pluginscript_language.cpp b/modules/gdnative/pluginscript/pluginscript_language.cpp
index fc9c4ebd77..df685e716f 100644
--- a/modules/gdnative/pluginscript/pluginscript_language.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_language.cpp
@@ -142,6 +142,10 @@ bool PluginScriptLanguage::supports_builtin_mode() const {
return _desc.supports_builtin_mode;
}
+bool PluginScriptLanguage::can_inherit_from_file() const {
+ return _desc.can_inherit_from_file;
+}
+
int PluginScriptLanguage::find_function(const String &p_function, const String &p_code) const {
if (_desc.find_function) {
return _desc.find_function(_data, (godot_string *)&p_function, (godot_string *)&p_code);
@@ -398,6 +402,32 @@ void PluginScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool
#endif
}
+bool PluginScriptLanguage::handles_global_class_type(const String &p_type) const {
+ return p_type == "PluginScript";
+}
+
+String PluginScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const {
+ if (!p_path.empty()) {
+ Ref<PluginScript> script = ResourceLoader::load(p_path, "PluginScript");
+ if (script.is_valid()) {
+ if (r_base_type) {
+ *r_base_type = script->get_instance_base_type();
+ }
+ if (r_icon_path) {
+ *r_icon_path = script->get_script_class_icon_path();
+ }
+ return script->get_script_class_name();
+ }
+ if (r_base_type) {
+ *r_base_type = String();
+ }
+ if (r_icon_path) {
+ *r_icon_path = String();
+ }
+ }
+ return String();
+}
+
void PluginScriptLanguage::lock() {
_lock.lock();
}
diff --git a/modules/gdnative/pluginscript/pluginscript_language.h b/modules/gdnative/pluginscript/pluginscript_language.h
index 53e1e3ae9b..7548eba4a0 100644
--- a/modules/gdnative/pluginscript/pluginscript_language.h
+++ b/modules/gdnative/pluginscript/pluginscript_language.h
@@ -78,7 +78,7 @@ public:
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;
- virtual bool can_inherit_from_file() { return true; }
+ virtual bool can_inherit_from_file() const;
virtual int find_function(const String &p_function, const String &p_code) const;
virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const;
virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_force, String &r_call_hint);
@@ -122,6 +122,11 @@ public:
virtual void frame();
+ /* GLOBAL CLASSES */
+
+ virtual bool handles_global_class_type(const String &p_type) const;
+ virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr) const;
+
void lock();
void unlock();
diff --git a/modules/gdnative/pluginscript/pluginscript_script.cpp b/modules/gdnative/pluginscript/pluginscript_script.cpp
index 87c6288806..d69ab2fcb7 100644
--- a/modules/gdnative/pluginscript/pluginscript_script.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_script.cpp
@@ -302,6 +302,7 @@ Error PluginScript::reload(bool p_keep_state) {
_data = manifest.data;
_name = *(StringName *)&manifest.name;
_tool = manifest.is_tool;
+ _icon_path = *(String *)&manifest.icon_path;
Dictionary *members = (Dictionary *)&manifest.member_lines;
for (const Variant *key = members->next(); key != nullptr; key = members->next(key)) {
diff --git a/modules/gdnative/pluginscript/pluginscript_script.h b/modules/gdnative/pluginscript/pluginscript_script.h
index 150de05e74..12d93cc407 100644
--- a/modules/gdnative/pluginscript/pluginscript_script.h
+++ b/modules/gdnative/pluginscript/pluginscript_script.h
@@ -32,6 +32,8 @@
#define PLUGINSCRIPT_SCRIPT_H
// Godot imports
+
+#include "core/doc_data.h"
#include "core/object/script_language.h"
// PluginScript imports
#include "pluginscript_language.h"
@@ -67,6 +69,7 @@ private:
String _source;
String _path;
StringName _name;
+ String _icon_path;
protected:
static void _bind_methods();
@@ -82,6 +85,14 @@ protected:
virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) override;
#endif
public:
+ String get_script_class_name() const {
+ return _name;
+ }
+
+ String get_script_class_icon_path() const {
+ return _icon_path;
+ }
+
virtual bool can_instance() const override;
virtual Ref<Script> get_base_script() const override; //for script inheritance
@@ -97,6 +108,13 @@ public:
// TODO: load_source_code only allow utf-8 file, should handle bytecode as well ?
virtual Error load_source_code(const String &p_path);
+#ifdef TOOLS_ENABLED
+ virtual const Vector<DocData::ClassDoc> &get_documentation() const override {
+ static Vector<DocData::ClassDoc> docs;
+ return docs;
+ }
+#endif // TOOLS_ENABLED
+
virtual bool has_method(const StringName &p_method) const override;
virtual MethodInfo get_method_info(const StringName &p_method) const override;
diff --git a/modules/gdnative/text/SCsub b/modules/gdnative/text/SCsub
new file mode 100644
index 0000000000..0b2db3b504
--- /dev/null
+++ b/modules/gdnative/text/SCsub
@@ -0,0 +1,6 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_gdnative")
+
+env_gdnative.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/gdnative/text/config.py b/modules/gdnative/text/config.py
new file mode 100644
index 0000000000..d22f9454ed
--- /dev/null
+++ b/modules/gdnative/text/config.py
@@ -0,0 +1,6 @@
+def can_build(env, platform):
+ return True
+
+
+def configure(env):
+ pass
diff --git a/modules/gdnative/text/register_types.cpp b/modules/gdnative/text/register_types.cpp
new file mode 100644
index 0000000000..e1d4547aa0
--- /dev/null
+++ b/modules/gdnative/text/register_types.cpp
@@ -0,0 +1,36 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "register_types.h"
+#include "text_server_gdnative.h"
+
+void register_text_server_gdn_types() {}
+
+void unregister_text_server_gdn_types() {}
diff --git a/modules/gdnative/text/register_types.h b/modules/gdnative/text/register_types.h
new file mode 100644
index 0000000000..027653e58e
--- /dev/null
+++ b/modules/gdnative/text/register_types.h
@@ -0,0 +1,37 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TEXT_REGISTER_TYPES_H
+#define TEXT_REGISTER_TYPES_H
+
+void register_text_server_gdn_types();
+void unregister_text_server_gdn_types();
+
+#endif // TEXT_REGISTER_TYPES_H
diff --git a/modules/gdnative/text/text_server_gdnative.cpp b/modules/gdnative/text/text_server_gdnative.cpp
new file mode 100644
index 0000000000..cb87adafe8
--- /dev/null
+++ b/modules/gdnative/text/text_server_gdnative.cpp
@@ -0,0 +1,853 @@
+/*************************************************************************/
+/* text_server_gdnative.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "text_server_gdnative.h"
+
+bool TextServerGDNative::has_feature(Feature p_feature) {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->has_feature(data, (godot_int)p_feature);
+}
+
+String TextServerGDNative::get_name() const {
+ ERR_FAIL_COND_V(interface == nullptr, String());
+ godot_string result = interface->get_name(data);
+ String name = *(String *)&result;
+ godot_string_destroy(&result);
+ return name;
+}
+
+void TextServerGDNative::free(RID p_rid) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->free(data, (godot_rid *)&p_rid);
+}
+
+bool TextServerGDNative::has(RID p_rid) {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->has(data, (godot_rid *)&p_rid);
+}
+
+bool TextServerGDNative::load_support_data(const String &p_filename) {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->load_support_data(data, (godot_string *)&p_filename);
+}
+
+#ifdef TOOLS_ENABLED
+
+String TextServerGDNative::get_support_data_filename() {
+ ERR_FAIL_COND_V(interface == nullptr, String());
+ godot_string result = interface->get_support_data_filename(data);
+ String name = *(String *)&result;
+ godot_string_destroy(&result);
+ return name;
+}
+
+String TextServerGDNative::get_support_data_info() {
+ ERR_FAIL_COND_V(interface == nullptr, String());
+ godot_string result = interface->get_support_data_info(data);
+ String info = *(String *)&result;
+ godot_string_destroy(&result);
+ return info;
+}
+
+bool TextServerGDNative::save_support_data(const String &p_filename) {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->save_support_data(data, (godot_string *)&p_filename);
+}
+
+#endif
+
+bool TextServerGDNative::is_locale_right_to_left(const String &p_locale) {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->is_locale_right_to_left(data, (godot_string *)&p_locale);
+}
+
+/*************************************************************************/
+/* Font interface */
+/*************************************************************************/
+
+RID TextServerGDNative::create_font_system(const String &p_name, int p_base_size) {
+ ERR_FAIL_COND_V(interface == nullptr, RID());
+ godot_rid result = interface->create_font_system(data, (const godot_string *)&p_name, p_base_size);
+ RID rid = *(RID *)&result;
+ return rid;
+}
+
+RID TextServerGDNative::create_font_resource(const String &p_filename, int p_base_size) {
+ ERR_FAIL_COND_V(interface == nullptr, RID());
+ godot_rid result = interface->create_font_resource(data, (const godot_string *)&p_filename, p_base_size);
+ RID rid = *(RID *)&result;
+ return rid;
+}
+
+RID TextServerGDNative::create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size) {
+ ERR_FAIL_COND_V(interface == nullptr, RID());
+ godot_rid result = interface->create_font_memory(data, p_data, p_size, (godot_string *)&p_type, p_base_size);
+ RID rid = *(RID *)&result;
+ return rid;
+}
+
+float TextServerGDNative::font_get_height(RID p_font, int p_size) const {
+ ERR_FAIL_COND_V(interface == nullptr, 0.f);
+ return interface->font_get_height(data, (godot_rid *)&p_font, p_size);
+}
+
+float TextServerGDNative::font_get_ascent(RID p_font, int p_size) const {
+ ERR_FAIL_COND_V(interface == nullptr, 0.f);
+ return interface->font_get_ascent(data, (godot_rid *)&p_font, p_size);
+}
+
+float TextServerGDNative::font_get_descent(RID p_font, int p_size) const {
+ ERR_FAIL_COND_V(interface == nullptr, 0.f);
+ return interface->font_get_descent(data, (godot_rid *)&p_font, p_size);
+}
+
+float TextServerGDNative::font_get_underline_position(RID p_font, int p_size) const {
+ ERR_FAIL_COND_V(interface == nullptr, 0.f);
+ return interface->font_get_underline_position(data, (godot_rid *)&p_font, p_size);
+}
+
+float TextServerGDNative::font_get_underline_thickness(RID p_font, int p_size) const {
+ ERR_FAIL_COND_V(interface == nullptr, 0.f);
+ return interface->font_get_underline_thickness(data, (godot_rid *)&p_font, p_size);
+}
+
+void TextServerGDNative::font_set_antialiased(RID p_font, bool p_antialiased) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_antialiased(data, (godot_rid *)&p_font, p_antialiased);
+}
+
+bool TextServerGDNative::font_get_antialiased(RID p_font) const {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->font_get_antialiased(data, (godot_rid *)&p_font);
+}
+
+Dictionary TextServerGDNative::font_get_variation_list(RID p_font) const {
+ ERR_FAIL_COND_V(interface == nullptr, Dictionary());
+ godot_dictionary result = interface->font_get_variation_list(data, (godot_rid *)&p_font);
+ Dictionary info = *(Dictionary *)&result;
+ godot_dictionary_destroy(&result);
+
+ return info;
+}
+
+void TextServerGDNative::font_set_variation(RID p_font, const String &p_name, double p_value) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_variation(data, (godot_rid *)&p_font, (godot_string *)&p_name, p_value);
+}
+
+double TextServerGDNative::font_get_variation(RID p_font, const String &p_name) const {
+ return interface->font_get_variation(data, (godot_rid *)&p_font, (godot_string *)&p_name);
+}
+
+void TextServerGDNative::font_set_hinting(RID p_font, TextServer::Hinting p_hinting) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_hinting(data, (godot_rid *)&p_font, (godot_int)p_hinting);
+}
+
+TextServer::Hinting TextServerGDNative::font_get_hinting(RID p_font) const {
+ ERR_FAIL_COND_V(interface == nullptr, TextServer::HINTING_NONE);
+ return (TextServer::Hinting)interface->font_get_hinting(data, (godot_rid *)&p_font);
+}
+
+Dictionary TextServerGDNative::font_get_feature_list(RID p_font) const {
+ ERR_FAIL_COND_V(interface == nullptr, Dictionary());
+ godot_dictionary result = interface->font_get_feature_list(data, (godot_rid *)&p_font);
+ Dictionary info = *(Dictionary *)&result;
+ godot_dictionary_destroy(&result);
+
+ return info;
+}
+
+void TextServerGDNative::font_set_distance_field_hint(RID p_font, bool p_distance_field) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_distance_field_hint(data, (godot_rid *)&p_font, p_distance_field);
+}
+
+bool TextServerGDNative::font_get_distance_field_hint(RID p_font) const {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->font_get_distance_field_hint(data, (godot_rid *)&p_font);
+}
+
+void TextServerGDNative::font_set_force_autohinter(RID p_font, bool p_enabeld) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_force_autohinter(data, (godot_rid *)&p_font, p_enabeld);
+}
+
+bool TextServerGDNative::font_get_force_autohinter(RID p_font) const {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->font_get_force_autohinter(data, (godot_rid *)&p_font);
+}
+
+bool TextServerGDNative::font_has_char(RID p_font, char32_t p_char) const {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->font_has_char(data, (godot_rid *)&p_font, p_char);
+}
+
+String TextServerGDNative::font_get_supported_chars(RID p_font) const {
+ ERR_FAIL_COND_V(interface == nullptr, String());
+ godot_string result = interface->font_get_supported_chars(data, (godot_rid *)&p_font);
+ String ret = *(String *)&result;
+ godot_string_destroy(&result);
+ return ret;
+}
+
+bool TextServerGDNative::font_has_outline(RID p_font) const {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->font_has_outline(data, (godot_rid *)&p_font);
+}
+
+float TextServerGDNative::font_get_base_size(RID p_font) const {
+ ERR_FAIL_COND_V(interface == nullptr, 0.f);
+ return interface->font_get_base_size(data, (godot_rid *)&p_font);
+}
+
+bool TextServerGDNative::font_is_language_supported(RID p_font, const String &p_language) const {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->font_is_language_supported(data, (godot_rid *)&p_font, (godot_string *)&p_language);
+}
+
+void TextServerGDNative::font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) {
+ ERR_FAIL_COND(interface == nullptr);
+ return interface->font_set_language_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_language, p_supported);
+}
+
+bool TextServerGDNative::font_get_language_support_override(RID p_font, const String &p_language) {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->font_get_language_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_language);
+}
+
+void TextServerGDNative::font_remove_language_support_override(RID p_font, const String &p_language) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_remove_language_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_language);
+}
+
+Vector<String> TextServerGDNative::font_get_language_support_overrides(RID p_font) {
+ ERR_FAIL_COND_V(interface == nullptr, Vector<String>());
+ godot_packed_string_array result = interface->font_get_language_support_overrides(data, (godot_rid *)&p_font);
+ Vector<String> ret = *(Vector<String> *)&result;
+ godot_packed_string_array_destroy(&result);
+ return ret;
+}
+
+bool TextServerGDNative::font_is_script_supported(RID p_font, const String &p_script) const {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->font_is_script_supported(data, (godot_rid *)&p_font, (godot_string *)&p_script);
+}
+
+void TextServerGDNative::font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) {
+ ERR_FAIL_COND(interface == nullptr);
+ return interface->font_set_script_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_script, p_supported);
+}
+
+bool TextServerGDNative::font_get_script_support_override(RID p_font, const String &p_script) {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->font_get_script_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_script);
+}
+
+void TextServerGDNative::font_remove_script_support_override(RID p_font, const String &p_script) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_remove_script_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_script);
+}
+
+Vector<String> TextServerGDNative::font_get_script_support_overrides(RID p_font) {
+ ERR_FAIL_COND_V(interface == nullptr, Vector<String>());
+ godot_packed_string_array result = interface->font_get_script_support_overrides(data, (godot_rid *)&p_font);
+ Vector<String> ret = *(Vector<String> *)&result;
+ godot_packed_string_array_destroy(&result);
+ return ret;
+}
+
+uint32_t TextServerGDNative::font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector) const {
+ ERR_FAIL_COND_V(interface == nullptr, 0);
+ return interface->font_get_glyph_index(data, (godot_rid *)&p_font, p_char, p_variation_selector);
+}
+
+Vector2 TextServerGDNative::font_get_glyph_advance(RID p_font, uint32_t p_index, int p_size) const {
+ ERR_FAIL_COND_V(interface == nullptr, Vector2());
+ godot_vector2 result = interface->font_get_glyph_advance(data, (godot_rid *)&p_font, p_index, p_size);
+ Vector2 advance = *(Vector2 *)&result;
+ return advance;
+}
+
+Vector2 TextServerGDNative::font_get_glyph_kerning(RID p_font, uint32_t p_index_a, uint32_t p_index_b, int p_size) const {
+ ERR_FAIL_COND_V(interface == nullptr, Vector2());
+ godot_vector2 result = interface->font_get_glyph_kerning(data, (godot_rid *)&p_font, p_index_a, p_index_b, p_size);
+ Vector2 kerning = *(Vector2 *)&result;
+ return kerning;
+}
+
+Vector2 TextServerGDNative::font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
+ ERR_FAIL_COND_V(interface == nullptr, Vector2());
+ godot_vector2 result = interface->font_draw_glyph(data, (godot_rid *)&p_font, (godot_rid *)&p_canvas, p_size, (const godot_vector2 *)&p_pos, p_index, (const godot_color *)&p_color);
+ Vector2 advance = *(Vector2 *)&result;
+ return advance;
+}
+
+Vector2 TextServerGDNative::font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
+ ERR_FAIL_COND_V(interface == nullptr, Vector2());
+ godot_vector2 result = interface->font_draw_glyph_outline(data, (godot_rid *)&p_font, (godot_rid *)&p_canvas, p_size, p_outline_size, (const godot_vector2 *)&p_pos, p_index, (const godot_color *)&p_color);
+ Vector2 advance = *(Vector2 *)&result;
+ return advance;
+}
+
+float TextServerGDNative::font_get_oversampling() const {
+ ERR_FAIL_COND_V(interface == nullptr, 1.f);
+ return interface->font_get_oversampling(data);
+}
+
+void TextServerGDNative::font_set_oversampling(float p_oversampling) {
+ ERR_FAIL_COND(interface == nullptr);
+ return interface->font_set_oversampling(data, p_oversampling);
+}
+
+Vector<String> TextServerGDNative::get_system_fonts() const {
+ ERR_FAIL_COND_V(interface == nullptr, Vector<String>());
+ godot_packed_string_array result = interface->get_system_fonts(data);
+ Vector<String> fonts = *(Vector<String> *)&result;
+ godot_packed_string_array_destroy(&result);
+ return fonts;
+}
+
+/*************************************************************************/
+/* Shaped text buffer interface */
+/*************************************************************************/
+
+RID TextServerGDNative::create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
+ ERR_FAIL_COND_V(interface == nullptr, RID());
+ godot_rid result = interface->create_shaped_text(data, (godot_int)p_direction, (godot_int)p_orientation);
+ RID rid = *(RID *)&result;
+ return rid;
+}
+
+void TextServerGDNative::shaped_text_clear(RID p_shaped) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->shaped_text_clear(data, (godot_rid *)&p_shaped);
+}
+
+void TextServerGDNative::shaped_text_set_direction(RID p_shaped, TextServer::Direction p_direction) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->shaped_text_set_direction(data, (godot_rid *)&p_shaped, (godot_int)p_direction);
+}
+
+TextServer::Direction TextServerGDNative::shaped_text_get_direction(RID p_shaped) const {
+ ERR_FAIL_COND_V(interface == nullptr, TextServer::DIRECTION_LTR);
+ return (TextServer::Direction)interface->shaped_text_get_direction(data, (godot_rid *)&p_shaped);
+}
+
+void TextServerGDNative::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->shaped_text_set_orientation(data, (godot_rid *)&p_shaped, (godot_int)p_orientation);
+}
+
+TextServer::Orientation TextServerGDNative::shaped_text_get_orientation(RID p_shaped) const {
+ ERR_FAIL_COND_V(interface == nullptr, TextServer::ORIENTATION_HORIZONTAL);
+ return (TextServer::Orientation)interface->shaped_text_get_orientation(data, (godot_rid *)&p_shaped);
+}
+
+void TextServerGDNative::shaped_text_set_bidi_override(RID p_shaped, const Vector<Vector2i> &p_override) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->shaped_text_set_bidi_override(data, (godot_rid *)&p_shaped, (const godot_packed_vector2i_array *)&p_override);
+}
+
+void TextServerGDNative::shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->shaped_text_set_preserve_invalid(data, (godot_rid *)&p_shaped, p_enabled);
+}
+
+bool TextServerGDNative::shaped_text_get_preserve_invalid(RID p_shaped) const {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return (TextServer::Orientation)interface->shaped_text_get_preserve_invalid(data, (godot_rid *)&p_shaped);
+}
+
+void TextServerGDNative::shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->shaped_text_set_preserve_control(data, (godot_rid *)&p_shaped, p_enabled);
+}
+
+bool TextServerGDNative::shaped_text_get_preserve_control(RID p_shaped) const {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return (TextServer::Orientation)interface->shaped_text_get_preserve_control(data, (godot_rid *)&p_shaped);
+}
+
+bool TextServerGDNative::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->shaped_text_add_string(data, (godot_rid *)&p_shaped, (const godot_string *)&p_text, (const godot_rid **)p_fonts.ptr(), p_size, (const godot_dictionary *)&p_opentype_features, (const godot_string *)&p_language);
+}
+
+bool TextServerGDNative::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align, int p_length) {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->shaped_text_add_object(data, (godot_rid *)&p_shaped, (const godot_variant *)&p_key, (const godot_vector2 *)&p_size, (godot_int)p_inline_align, p_length);
+}
+
+bool TextServerGDNative::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align) {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->shaped_text_resize_object(data, (godot_rid *)&p_shaped, (const godot_variant *)&p_key, (const godot_vector2 *)&p_size, (godot_int)p_inline_align);
+}
+
+RID TextServerGDNative::shaped_text_substr(RID p_shaped, int p_start, int p_length) const {
+ ERR_FAIL_COND_V(interface == nullptr, RID());
+ godot_rid result = interface->shaped_text_substr(data, (godot_rid *)&p_shaped, (godot_int)p_start, (godot_int)p_length);
+ RID rid = *(RID *)&result;
+ return rid;
+}
+
+RID TextServerGDNative::shaped_text_get_parent(RID p_shaped) const {
+ ERR_FAIL_COND_V(interface == nullptr, RID());
+ godot_rid result = interface->shaped_text_get_parent(data, (godot_rid *)&p_shaped);
+ RID rid = *(RID *)&result;
+ return rid;
+}
+
+float TextServerGDNative::shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t p_jst_flags) {
+ ERR_FAIL_COND_V(interface == nullptr, 0.f);
+ return interface->shaped_text_fit_to_width(data, (godot_rid *)&p_shaped, p_width, p_jst_flags);
+}
+
+float TextServerGDNative::shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) {
+ ERR_FAIL_COND_V(interface == nullptr, 0.f);
+ return interface->shaped_text_tab_align(data, (godot_rid *)&p_shaped, (godot_packed_float32_array *)&p_tab_stops);
+}
+
+bool TextServerGDNative::shaped_text_shape(RID p_shaped) {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->shaped_text_shape(data, (godot_rid *)&p_shaped);
+}
+
+bool TextServerGDNative::shaped_text_update_breaks(RID p_shaped) {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->shaped_text_update_breaks(data, (godot_rid *)&p_shaped);
+}
+
+bool TextServerGDNative::shaped_text_update_justification_ops(RID p_shaped) {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->shaped_text_update_justification_ops(data, (godot_rid *)&p_shaped);
+}
+
+bool TextServerGDNative::shaped_text_is_ready(RID p_shaped) const {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->shaped_text_is_ready(data, (godot_rid *)&p_shaped);
+}
+
+Vector<TextServer::Glyph> TextServerGDNative::shaped_text_get_glyphs(RID p_shaped) const {
+ ERR_FAIL_COND_V(interface == nullptr, Vector<TextServer::Glyph>());
+ godot_packed_glyph_array result = interface->shaped_text_get_glyphs(data, (godot_rid *)&p_shaped);
+ Vector<TextServer::Glyph> glyphs = *(Vector<TextServer::Glyph> *)&result;
+ godot_packed_glyph_array_destroy(&result);
+ return glyphs;
+}
+
+Vector2i TextServerGDNative::shaped_text_get_range(RID p_shaped) const {
+ ERR_FAIL_COND_V(interface == nullptr, Vector2i());
+ godot_vector2i result = interface->shaped_text_get_range(data, (godot_rid *)&p_shaped);
+ Vector2i range = *(Vector2i *)&result;
+ return range;
+}
+
+Vector<TextServer::Glyph> TextServerGDNative::shaped_text_sort_logical(RID p_shaped) {
+ ERR_FAIL_COND_V(interface == nullptr, Vector<TextServer::Glyph>());
+ godot_packed_glyph_array result = interface->shaped_text_sort_logical(data, (godot_rid *)&p_shaped);
+ Vector<TextServer::Glyph> glyphs = *(Vector<TextServer::Glyph> *)&result;
+ godot_packed_glyph_array_destroy(&result);
+ return glyphs;
+}
+
+Vector<Vector2i> TextServerGDNative::shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<float> &p_width, int p_start, bool p_once, uint8_t p_break_flags) const {
+ ERR_FAIL_COND_V(interface == nullptr, Vector<Vector2i>());
+ if (interface->shaped_text_get_line_breaks_adv != nullptr) {
+ godot_packed_vector2i_array result = interface->shaped_text_get_line_breaks_adv(data, (godot_rid *)&p_shaped, (godot_packed_float32_array *)&p_width, p_start, p_once, p_break_flags);
+ Vector<Vector2i> breaks = *(Vector<Vector2i> *)&result;
+ godot_packed_vector2i_array_destroy(&result);
+ return breaks;
+ } else {
+ return TextServer::shaped_text_get_line_breaks_adv(p_shaped, p_width, p_break_flags);
+ }
+}
+
+Vector<Vector2i> TextServerGDNative::shaped_text_get_line_breaks(RID p_shaped, float p_width, int p_start, uint8_t p_break_flags) const {
+ ERR_FAIL_COND_V(interface == nullptr, Vector<Vector2i>());
+ if (interface->shaped_text_get_line_breaks != nullptr) {
+ godot_packed_vector2i_array result = interface->shaped_text_get_line_breaks(data, (godot_rid *)&p_shaped, p_width, p_start, p_break_flags);
+ Vector<Vector2i> breaks = *(Vector<Vector2i> *)&result;
+ godot_packed_vector2i_array_destroy(&result);
+ return breaks;
+ } else {
+ return TextServer::shaped_text_get_line_breaks(p_shaped, p_width, p_break_flags);
+ }
+}
+
+Vector<Vector2i> TextServerGDNative::shaped_text_get_word_breaks(RID p_shaped) const {
+ ERR_FAIL_COND_V(interface == nullptr, Vector<Vector2i>());
+ if (interface->shaped_text_get_word_breaks != nullptr) {
+ godot_packed_vector2i_array result = interface->shaped_text_get_word_breaks(data, (godot_rid *)&p_shaped);
+ Vector<Vector2i> breaks = *(Vector<Vector2i> *)&result;
+ godot_packed_vector2i_array_destroy(&result);
+ return breaks;
+ } else {
+ return TextServer::shaped_text_get_word_breaks(p_shaped);
+ }
+}
+
+Array TextServerGDNative::shaped_text_get_objects(RID p_shaped) const {
+ ERR_FAIL_COND_V(interface == nullptr, Array());
+ godot_array result = interface->shaped_text_get_objects(data, (godot_rid *)&p_shaped);
+ Array rect = *(Array *)&result;
+ return rect;
+}
+
+Rect2 TextServerGDNative::shaped_text_get_object_rect(RID p_shaped, Variant p_key) const {
+ ERR_FAIL_COND_V(interface == nullptr, Rect2());
+ godot_rect2 result = interface->shaped_text_get_object_rect(data, (godot_rid *)&p_shaped, (const godot_variant *)&p_key);
+ Rect2 rect = *(Rect2 *)&result;
+ return rect;
+}
+
+Size2 TextServerGDNative::shaped_text_get_size(RID p_shaped) const {
+ ERR_FAIL_COND_V(interface == nullptr, Size2());
+ godot_vector2 result = interface->shaped_text_get_size(data, (godot_rid *)&p_shaped);
+ Size2 size = *(Size2 *)&result;
+ return size;
+}
+
+float TextServerGDNative::shaped_text_get_ascent(RID p_shaped) const {
+ ERR_FAIL_COND_V(interface == nullptr, 0.f);
+ return interface->shaped_text_get_ascent(data, (godot_rid *)&p_shaped);
+}
+
+float TextServerGDNative::shaped_text_get_descent(RID p_shaped) const {
+ ERR_FAIL_COND_V(interface == nullptr, 0.f);
+ return interface->shaped_text_get_descent(data, (godot_rid *)&p_shaped);
+}
+
+float TextServerGDNative::shaped_text_get_width(RID p_shaped) const {
+ ERR_FAIL_COND_V(interface == nullptr, 0.f);
+ return interface->shaped_text_get_width(data, (godot_rid *)&p_shaped);
+}
+
+float TextServerGDNative::shaped_text_get_underline_position(RID p_shaped) const {
+ ERR_FAIL_COND_V(interface == nullptr, 0.f);
+ return interface->shaped_text_get_underline_position(data, (godot_rid *)&p_shaped);
+}
+
+float TextServerGDNative::shaped_text_get_underline_thickness(RID p_shaped) const {
+ ERR_FAIL_COND_V(interface == nullptr, 0.f);
+ return interface->shaped_text_get_underline_thickness(data, (godot_rid *)&p_shaped);
+}
+
+String TextServerGDNative::format_number(const String &p_string, const String &p_language) const {
+ ERR_FAIL_COND_V(interface == nullptr, String());
+ godot_string result = interface->format_number(data, (const godot_string *)&p_string, (const godot_string *)&p_language);
+ if (interface->format_number == nullptr) {
+ return p_string;
+ }
+ String ret = *(String *)&result;
+ godot_string_destroy(&result);
+ return ret;
+}
+
+String TextServerGDNative::parse_number(const String &p_string, const String &p_language) const {
+ ERR_FAIL_COND_V(interface == nullptr, String());
+ if (interface->parse_number == nullptr) {
+ return p_string;
+ }
+ godot_string result = interface->parse_number(data, (const godot_string *)&p_string, (const godot_string *)&p_language);
+ String ret = *(String *)&result;
+ godot_string_destroy(&result);
+ return ret;
+}
+
+String TextServerGDNative::percent_sign(const String &p_language) const {
+ ERR_FAIL_COND_V(interface == nullptr, String());
+ if (interface->percent_sign == nullptr) {
+ return "%";
+ }
+ godot_string result = interface->percent_sign(data, (const godot_string *)&p_language);
+ String ret = *(String *)&result;
+ godot_string_destroy(&result);
+ return ret;
+}
+
+TextServer *TextServerGDNative::create_func(Error &r_error, void *p_user_data) {
+ const godot_text_interface_gdnative *interface = (const godot_text_interface_gdnative *)p_user_data;
+ r_error = OK;
+
+ TextServerGDNative *server = memnew(TextServerGDNative());
+ server->interface = interface;
+ server->data = interface->constructor((godot_object *)server);
+
+ return server;
+}
+
+TextServerGDNative::TextServerGDNative() {
+ data = nullptr;
+ interface = nullptr;
+}
+
+TextServerGDNative::~TextServerGDNative() {
+ if (interface != nullptr) {
+ interface->destructor(data);
+ data = nullptr;
+ interface = nullptr;
+ }
+}
+
+/*************************************************************************/
+/* GDNative functions */
+/*************************************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static_assert(sizeof(godot_glyph) == sizeof(TextServer::Glyph), "Glyph size mismatch");
+static_assert(sizeof(godot_packed_glyph_array) == sizeof(Vector<TextServer::Glyph>), "Vector<Glyph> size mismatch");
+
+void GDAPI godot_text_register_interface(const godot_text_interface_gdnative *p_interface, const godot_string *p_name, uint32_t p_features) {
+ ERR_FAIL_COND(p_interface->version.major != 1);
+ String name = *(String *)p_name;
+ TextServerManager::register_create_function(name + "(GDNative)", p_features, TextServerGDNative::create_func, (void *)p_interface);
+}
+
+// Glyph
+
+void GDAPI godot_glyph_new(godot_glyph *r_dest) {
+ TextServer::Glyph *dest = (TextServer::Glyph *)r_dest;
+ *dest = TextServer::Glyph();
+}
+
+godot_vector2i GDAPI godot_glyph_get_range(const godot_glyph *p_self) {
+ godot_vector2i dest;
+ Vector2i *d = (Vector2i *)&dest;
+ const TextServer::Glyph *self = (const TextServer::Glyph *)p_self;
+ d->x = self->start;
+ d->y = self->end;
+ return dest;
+}
+
+void GDAPI godot_glyph_set_range(godot_glyph *p_self, const godot_vector2i *p_range) {
+ TextServer::Glyph *self = (TextServer::Glyph *)p_self;
+ const Vector2i *range = (const Vector2i *)p_range;
+ self->start = range->x;
+ self->end = range->y;
+}
+
+godot_int GDAPI godot_glyph_get_count(const godot_glyph *p_self) {
+ const TextServer::Glyph *self = (const TextServer::Glyph *)p_self;
+ return self->count;
+}
+
+void GDAPI godot_glyph_set_count(godot_glyph *p_self, godot_int p_count) {
+ TextServer::Glyph *self = (TextServer::Glyph *)p_self;
+ self->count = p_count;
+}
+
+godot_int GDAPI godot_glyph_get_repeat(const godot_glyph *p_self) {
+ const TextServer::Glyph *self = (const TextServer::Glyph *)p_self;
+ return self->repeat;
+}
+
+void GDAPI godot_glyph_set_repeat(godot_glyph *p_self, godot_int p_repeat) {
+ TextServer::Glyph *self = (TextServer::Glyph *)p_self;
+ self->repeat = p_repeat;
+}
+
+godot_int GDAPI godot_glyph_get_flags(const godot_glyph *p_self) {
+ const TextServer::Glyph *self = (const TextServer::Glyph *)p_self;
+ return self->flags;
+}
+
+void GDAPI godot_glyph_set_flags(godot_glyph *p_self, godot_int p_flags) {
+ TextServer::Glyph *self = (TextServer::Glyph *)p_self;
+ self->flags = p_flags;
+}
+
+godot_vector2 GDAPI godot_glyph_get_offset(const godot_glyph *p_self) {
+ godot_vector2 dest;
+ Vector2 *d = (Vector2 *)&dest;
+ const TextServer::Glyph *self = (const TextServer::Glyph *)p_self;
+ d->x = self->x_off;
+ d->y = self->y_off;
+ return dest;
+}
+
+void GDAPI godot_glyph_set_offset(godot_glyph *p_self, const godot_vector2 *p_offset) {
+ TextServer::Glyph *self = (TextServer::Glyph *)p_self;
+ const Vector2 *offset = (const Vector2 *)p_offset;
+ self->x_off = offset->x;
+ self->y_off = offset->y;
+}
+
+godot_real GDAPI godot_glyph_get_advance(const godot_glyph *p_self) {
+ const TextServer::Glyph *self = (const TextServer::Glyph *)p_self;
+ return self->advance;
+}
+
+void GDAPI godot_glyph_set_advance(godot_glyph *p_self, godot_real p_advance) {
+ TextServer::Glyph *self = (TextServer::Glyph *)p_self;
+ self->advance = p_advance;
+}
+
+godot_rid GDAPI godot_glyph_get_font(const godot_glyph *p_self) {
+ godot_rid dest;
+ RID *d = (RID *)&dest;
+ const TextServer::Glyph *self = (const TextServer::Glyph *)p_self;
+ *d = self->font_rid;
+ return dest;
+}
+
+void GDAPI godot_glyph_set_font(godot_glyph *p_self, godot_rid *p_font) {
+ TextServer::Glyph *self = (TextServer::Glyph *)p_self;
+ const RID *font = (const RID *)p_font;
+ self->font_rid = *font;
+}
+
+godot_int GDAPI godot_glyph_get_font_size(const godot_glyph *p_self) {
+ const TextServer::Glyph *self = (const TextServer::Glyph *)p_self;
+ return self->font_size;
+}
+
+void GDAPI godot_glyph_set_font_size(godot_glyph *p_self, godot_int p_size) {
+ TextServer::Glyph *self = (TextServer::Glyph *)p_self;
+ self->font_size = p_size;
+}
+
+godot_int GDAPI godot_glyph_get_index(const godot_glyph *p_self) {
+ const TextServer::Glyph *self = (const TextServer::Glyph *)p_self;
+ return self->index;
+}
+
+void GDAPI godot_glyph_set_index(godot_glyph *p_self, godot_int p_index) {
+ TextServer::Glyph *self = (TextServer::Glyph *)p_self;
+ self->index = p_index;
+}
+
+// GlyphArray
+
+void GDAPI godot_packed_glyph_array_new(godot_packed_glyph_array *r_dest) {
+ Vector<TextServer::Glyph> *dest = (Vector<TextServer::Glyph> *)r_dest;
+ memnew_placement(dest, Vector<TextServer::Glyph>);
+}
+
+void GDAPI godot_packed_glyph_array_new_copy(godot_packed_glyph_array *r_dest, const godot_packed_glyph_array *p_src) {
+ Vector<TextServer::Glyph> *dest = (Vector<TextServer::Glyph> *)r_dest;
+ const Vector<TextServer::Glyph> *src = (const Vector<TextServer::Glyph> *)p_src;
+ memnew_placement(dest, Vector<TextServer::Glyph>(*src));
+}
+
+const godot_glyph GDAPI *godot_packed_glyph_array_ptr(const godot_packed_glyph_array *p_self) {
+ const Vector<TextServer::Glyph> *self = (const Vector<TextServer::Glyph> *)p_self;
+ return (const godot_glyph *)self->ptr();
+}
+
+godot_glyph GDAPI *godot_packed_glyph_array_ptrw(godot_packed_glyph_array *p_self) {
+ Vector<TextServer::Glyph> *self = (Vector<TextServer::Glyph> *)p_self;
+ return (godot_glyph *)self->ptrw();
+}
+
+void GDAPI godot_packed_glyph_array_append(godot_packed_glyph_array *p_self, const godot_glyph *p_data) {
+ Vector<TextServer::Glyph> *self = (Vector<TextServer::Glyph> *)p_self;
+ TextServer::Glyph &s = *(TextServer::Glyph *)p_data;
+ self->push_back(s);
+}
+
+void GDAPI godot_packed_glyph_array_append_array(godot_packed_glyph_array *p_self, const godot_packed_glyph_array *p_array) {
+ Vector<TextServer::Glyph> *self = (Vector<TextServer::Glyph> *)p_self;
+ Vector<TextServer::Glyph> *array = (Vector<TextServer::Glyph> *)p_array;
+ self->append_array(*array);
+}
+
+godot_error GDAPI godot_packed_glyph_array_insert(godot_packed_glyph_array *p_self, const godot_int p_idx, const godot_glyph *p_data) {
+ Vector<TextServer::Glyph> *self = (Vector<TextServer::Glyph> *)p_self;
+ TextServer::Glyph &s = *(TextServer::Glyph *)p_data;
+ return (godot_error)self->insert(p_idx, s);
+}
+
+godot_bool GDAPI godot_packed_glyph_array_has(godot_packed_glyph_array *p_self, const godot_glyph *p_value) {
+ Vector<TextServer::Glyph> *self = (Vector<TextServer::Glyph> *)p_self;
+ TextServer::Glyph &v = *(TextServer::Glyph *)p_value;
+ return (godot_bool)self->has(v);
+}
+
+void GDAPI godot_packed_glyph_array_sort(godot_packed_glyph_array *p_self) {
+ Vector<TextServer::Glyph> *self = (Vector<TextServer::Glyph> *)p_self;
+ self->sort();
+}
+
+void GDAPI godot_packed_glyph_array_invert(godot_packed_glyph_array *p_self) {
+ Vector<TextServer::Glyph> *self = (Vector<TextServer::Glyph> *)p_self;
+ self->invert();
+}
+
+void GDAPI godot_packed_glyph_array_push_back(godot_packed_glyph_array *p_self, const godot_glyph *p_data) {
+ Vector<TextServer::Glyph> *self = (Vector<TextServer::Glyph> *)p_self;
+ TextServer::Glyph &s = *(TextServer::Glyph *)p_data;
+ self->push_back(s);
+}
+
+void GDAPI godot_packed_glyph_array_remove(godot_packed_glyph_array *p_self, const godot_int p_idx) {
+ Vector<TextServer::Glyph> *self = (Vector<TextServer::Glyph> *)p_self;
+ self->remove(p_idx);
+}
+
+void GDAPI godot_packed_glyph_array_resize(godot_packed_glyph_array *p_self, const godot_int p_size) {
+ Vector<TextServer::Glyph> *self = (Vector<TextServer::Glyph> *)p_self;
+ self->resize(p_size);
+}
+
+void GDAPI godot_packed_glyph_array_set(godot_packed_glyph_array *p_self, const godot_int p_idx, const godot_glyph *p_data) {
+ Vector<TextServer::Glyph> *self = (Vector<TextServer::Glyph> *)p_self;
+ TextServer::Glyph &s = *(TextServer::Glyph *)p_data;
+ self->set(p_idx, s);
+}
+
+godot_glyph GDAPI godot_packed_glyph_array_get(const godot_packed_glyph_array *p_self, const godot_int p_idx) {
+ const Vector<TextServer::Glyph> *self = (const Vector<TextServer::Glyph> *)p_self;
+ godot_glyph v;
+ TextServer::Glyph *s = (TextServer::Glyph *)&v;
+ *s = self->get(p_idx);
+ return v;
+}
+
+godot_int GDAPI godot_packed_glyph_array_size(const godot_packed_glyph_array *p_self) {
+ const Vector<TextServer::Glyph> *self = (const Vector<TextServer::Glyph> *)p_self;
+ return self->size();
+}
+
+godot_bool GDAPI godot_packed_glyph_array_empty(const godot_packed_glyph_array *p_self) {
+ const Vector<TextServer::Glyph> *self = (const Vector<TextServer::Glyph> *)p_self;
+ return self->empty();
+}
+
+void GDAPI godot_packed_glyph_array_destroy(godot_packed_glyph_array *p_self) {
+ ((Vector<TextServer::Glyph> *)p_self)->~Vector();
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/modules/gdnative/text/text_server_gdnative.h b/modules/gdnative/text/text_server_gdnative.h
new file mode 100644
index 0000000000..959302aaf4
--- /dev/null
+++ b/modules/gdnative/text/text_server_gdnative.h
@@ -0,0 +1,187 @@
+/*************************************************************************/
+/* text_server_gdnative.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TEXT_SERVER_GDNATIVE_H
+#define TEXT_SERVER_GDNATIVE_H
+
+#include "modules/gdnative/gdnative.h"
+
+#include "servers/text_server.h"
+
+class TextServerGDNative : public TextServer {
+ GDCLASS(TextServerGDNative, TextServer);
+
+ const godot_text_interface_gdnative *interface = nullptr;
+ void *data = nullptr;
+
+protected:
+ static void _bind_methods(){};
+
+public:
+ virtual bool has_feature(Feature p_feature) override;
+ virtual String get_name() const override;
+
+ virtual void free(RID p_rid) override;
+ virtual bool has(RID p_rid) override;
+ virtual bool load_support_data(const String &p_filename) override;
+
+#ifdef TOOLS_ENABLED
+ virtual String get_support_data_filename() override;
+ virtual String get_support_data_info() override;
+ virtual bool save_support_data(const String &p_filename) override;
+#endif
+
+ virtual bool is_locale_right_to_left(const String &p_locale) override;
+
+ /* Font interface */
+ virtual RID create_font_system(const String &p_name, int p_base_size = 16) override;
+ virtual RID create_font_resource(const String &p_filename, int p_base_size = 16) override;
+ virtual RID create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16) override;
+
+ virtual float font_get_height(RID p_font, int p_size) const override;
+ virtual float font_get_ascent(RID p_font, int p_size) const override;
+ virtual float font_get_descent(RID p_font, int p_size) const override;
+
+ virtual float font_get_underline_position(RID p_font, int p_size) const override;
+ virtual float font_get_underline_thickness(RID p_font, int p_size) const override;
+
+ virtual void font_set_antialiased(RID p_font, bool p_antialiased) override;
+ virtual bool font_get_antialiased(RID p_font) const override;
+
+ virtual Dictionary font_get_feature_list(RID p_font) const override;
+ virtual Dictionary font_get_variation_list(RID p_font) const override;
+
+ virtual void font_set_variation(RID p_font, const String &p_name, double p_value) override;
+ virtual double font_get_variation(RID p_font, const String &p_name) const override;
+
+ virtual void font_set_hinting(RID p_font, Hinting p_hinting) override;
+ virtual Hinting font_get_hinting(RID p_font) const override;
+
+ virtual void font_set_distance_field_hint(RID p_font, bool p_distance_field) override;
+ virtual bool font_get_distance_field_hint(RID p_font) const override;
+
+ virtual void font_set_force_autohinter(RID p_font, bool p_enabeld) override;
+ virtual bool font_get_force_autohinter(RID p_font) const override;
+
+ virtual bool font_has_char(RID p_font, char32_t p_char) const override;
+ virtual String font_get_supported_chars(RID p_font) const override;
+
+ virtual bool font_has_outline(RID p_font) const override;
+ virtual float font_get_base_size(RID p_font) const override;
+
+ virtual bool font_is_language_supported(RID p_font, const String &p_language) const override;
+ virtual void font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) override;
+ virtual bool font_get_language_support_override(RID p_font, const String &p_language) override;
+ virtual void font_remove_language_support_override(RID p_font, const String &p_language) override;
+ Vector<String> font_get_language_support_overrides(RID p_font) override;
+
+ virtual bool font_is_script_supported(RID p_font, const String &p_script) const override;
+ virtual void font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) override;
+ virtual bool font_get_script_support_override(RID p_font, const String &p_script) override;
+ virtual void font_remove_script_support_override(RID p_font, const String &p_script) override;
+ Vector<String> font_get_script_support_overrides(RID p_font) override;
+
+ virtual uint32_t font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector = 0x0000) const override;
+ virtual Vector2 font_get_glyph_advance(RID p_font, uint32_t p_index, int p_size) const override;
+ virtual Vector2 font_get_glyph_kerning(RID p_font, uint32_t p_index_a, uint32_t p_index_b, int p_size) const override;
+
+ virtual Vector2 font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
+ virtual Vector2 font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
+
+ virtual float font_get_oversampling() const override;
+ virtual void font_set_oversampling(float p_oversampling) override;
+
+ virtual Vector<String> get_system_fonts() const override;
+
+ /* Shaped text buffer interface */
+
+ virtual RID create_shaped_text(Direction p_direction = DIRECTION_AUTO, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
+
+ virtual void shaped_text_clear(RID p_shaped) override;
+
+ virtual void shaped_text_set_direction(RID p_shaped, Direction p_direction = DIRECTION_AUTO) override;
+ virtual Direction shaped_text_get_direction(RID p_shaped) const override;
+
+ virtual void shaped_text_set_bidi_override(RID p_shaped, const Vector<Vector2i> &p_override) override;
+
+ virtual void shaped_text_set_orientation(RID p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
+ virtual Orientation shaped_text_get_orientation(RID p_shaped) const override;
+
+ virtual void shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) override;
+ virtual bool shaped_text_get_preserve_invalid(RID p_shaped) const override;
+
+ virtual void shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) override;
+ virtual bool shaped_text_get_preserve_control(RID p_shaped) const override;
+
+ virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") override;
+ virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER, int p_length = 1) override;
+ virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER) override;
+
+ virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override;
+ virtual RID shaped_text_get_parent(RID p_shaped) const override;
+
+ virtual float shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
+ virtual float shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) override;
+
+ virtual bool shaped_text_shape(RID p_shaped) override;
+ virtual bool shaped_text_update_breaks(RID p_shaped) override;
+ virtual bool shaped_text_update_justification_ops(RID p_shaped) override;
+
+ virtual bool shaped_text_is_ready(RID p_shaped) const override;
+
+ virtual Vector<Glyph> shaped_text_get_glyphs(RID p_shaped) const override;
+
+ virtual Vector2i shaped_text_get_range(RID p_shaped) const override;
+
+ virtual Vector<Glyph> shaped_text_sort_logical(RID p_shaped) override;
+ virtual Vector<Vector2i> shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<float> &p_width, int p_start = 0, bool p_once = true, uint8_t /*TextBreakFlag*/ p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const override;
+ virtual Vector<Vector2i> shaped_text_get_line_breaks(RID p_shaped, float p_width, int p_start = 0, uint8_t p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const override;
+ virtual Vector<Vector2i> shaped_text_get_word_breaks(RID p_shaped) const override;
+ virtual Array shaped_text_get_objects(RID p_shaped) const override;
+ virtual Rect2 shaped_text_get_object_rect(RID p_shaped, Variant p_key) const override;
+
+ virtual Size2 shaped_text_get_size(RID p_shaped) const override;
+ virtual float shaped_text_get_ascent(RID p_shaped) const override;
+ virtual float shaped_text_get_descent(RID p_shaped) const override;
+ virtual float shaped_text_get_width(RID p_shaped) const override;
+ virtual float shaped_text_get_underline_position(RID p_shaped) const override;
+ virtual float shaped_text_get_underline_thickness(RID p_shaped) const override;
+
+ virtual String format_number(const String &p_string, const String &p_language = "") const override;
+ virtual String parse_number(const String &p_string, const String &p_language = "") const override;
+ virtual String percent_sign(const String &p_language = "") const override;
+
+ static TextServer *create_func(Error &r_error, void *p_user_data);
+
+ TextServerGDNative();
+ ~TextServerGDNative();
+};
+
+#endif // TEXT_SERVER_GDNATIVE_H
diff --git a/modules/gdnavigation/SCsub b/modules/gdnavigation/SCsub
index 877d601c6a..22b5509b32 100644
--- a/modules/gdnavigation/SCsub
+++ b/modules/gdnavigation/SCsub
@@ -5,6 +5,10 @@ Import("env_modules")
env_navigation = env_modules.Clone()
+# Thirdparty source files
+
+thirdparty_obj = []
+
# Recast Thirdparty source files
if env["builtin_recast"]:
thirdparty_dir = "#thirdparty/recastnavigation/Recast/"
@@ -23,28 +27,37 @@ if env["builtin_recast"]:
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- env_navigation.Prepend(CPPPATH=[thirdparty_dir + "/Include"])
+ env_navigation.Prepend(CPPPATH=[thirdparty_dir + "Include"])
env_thirdparty = env_navigation.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
-
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
# RVO Thirdparty source files
if env["builtin_rvo2"]:
- thirdparty_dir = "#thirdparty/rvo2"
+ thirdparty_dir = "#thirdparty/rvo2/"
thirdparty_sources = [
- "/src/Agent.cpp",
- "/src/KdTree.cpp",
+ "Agent.cpp",
+ "KdTree.cpp",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- env_navigation.Prepend(CPPPATH=[thirdparty_dir + "/src"])
+ env_navigation.Prepend(CPPPATH=[thirdparty_dir])
env_thirdparty = env_navigation.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+
+
+env.modules_sources += thirdparty_obj
# Godot source files
-env_navigation.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_navigation.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
+
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/gdnavigation/nav_map.cpp b/modules/gdnavigation/nav_map.cpp
index 7919e6a01f..c8c1b422e8 100644
--- a/modules/gdnavigation/nav_map.cpp
+++ b/modules/gdnavigation/nav_map.cpp
@@ -707,7 +707,7 @@ void NavMap::sync() {
}
if (regenerate_links) {
- map_update_id = map_update_id + 1 % 9999999;
+ map_update_id = (map_update_id + 1) % 9999999;
}
if (agents_dirty) {
diff --git a/modules/gdnavigation/navigation_mesh_generator.cpp b/modules/gdnavigation/navigation_mesh_generator.cpp
index 5329600e39..3310123ca9 100644
--- a/modules/gdnavigation/navigation_mesh_generator.cpp
+++ b/modules/gdnavigation/navigation_mesh_generator.cpp
@@ -176,10 +176,10 @@ void NavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform,
BoxShape3D *box = Object::cast_to<BoxShape3D>(*s);
if (box) {
- Ref<CubeMesh> cube_mesh;
- cube_mesh.instance();
- cube_mesh->set_size(box->get_extents() * 2.0);
- mesh = cube_mesh;
+ Ref<BoxMesh> box_mesh;
+ box_mesh.instance();
+ box_mesh->set_size(box->get_extents() * 2.0);
+ mesh = box_mesh;
}
CapsuleShape3D *capsule = Object::cast_to<CapsuleShape3D>(*s);
diff --git a/modules/gdscript/config.py b/modules/gdscript/config.py
index 6fc227e7f5..61ce6185a5 100644
--- a/modules/gdscript/config.py
+++ b/modules/gdscript/config.py
@@ -10,7 +10,6 @@ def get_doc_classes():
return [
"@GDScript",
"GDScript",
- "GDScriptFunctionState",
]
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index d90b3e52d0..4ed129b3ff 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -55,8 +55,7 @@
<description>
Returns the absolute value of parameter [code]s[/code] (i.e. positive value).
[codeblock]
- # a is 1
- a = abs(-1)
+ a = abs(-1) # a is 1
[/codeblock]
</description>
</method>
@@ -166,10 +165,10 @@
<description>
Rounds [code]s[/code] upward (towards positive infinity), returning the smallest whole number that is not less than [code]s[/code].
[codeblock]
- i = ceil(1.45) # i is 2
- i = ceil(1.001) # i is 2
+ a = ceil(1.45) # a is 2.0
+ a = ceil(1.001) # a is 2.0
[/codeblock]
- See also [method floor], [method round], and [method stepify].
+ See also [method floor], [method round], [method stepify], and [int].
</description>
</method>
<method name="char">
@@ -199,13 +198,9 @@
<description>
Clamps [code]value[/code] and returns a value not less than [code]min[/code] and not more than [code]max[/code].
[codeblock]
- speed = 1000
- # a is 20
- a = clamp(speed, 1, 20)
-
- speed = -10
- # a is 1
- a = clamp(speed, 1, 20)
+ a = clamp(1000, 1, 20) # a is 20
+ a = clamp(-10, 1, 20) # a is 1
+ a = clamp(15, 1, 20) # a is 15
[/codeblock]
</description>
</method>
@@ -236,9 +231,8 @@
<description>
Returns the cosine of angle [code]s[/code] in radians.
[codeblock]
- # Prints 1 then -1
- print(cos(PI * 2))
- print(cos(PI))
+ a = cos(TAU) # a is 1.0
+ a = cos(PI) # a is -1.0
[/codeblock]
</description>
</method>
@@ -250,8 +244,7 @@
<description>
Returns the hyperbolic cosine of [code]s[/code] in radians.
[codeblock]
- # Prints 1.543081
- print(cosh(1))
+ print(cosh(1)) # Prints 1.543081
[/codeblock]
</description>
</method>
@@ -276,8 +269,7 @@
<description>
Returns the result of [code]value[/code] decreased by [code]step[/code] * [code]amount[/code].
[codeblock]
- # a = 59
- a = dectime(60, 10, 0.1))
+ a = dectime(60, 10, 0.1)) # a is 59.0
[/codeblock]
</description>
</method>
@@ -289,8 +281,7 @@
<description>
Converts an angle expressed in degrees to radians.
[codeblock]
- # r is 3.141593
- r = deg2rad(180)
+ r = deg2rad(180) # r is 3.141593
[/codeblock]
</description>
</method>
@@ -300,7 +291,7 @@
<argument index="0" name="dict" type="Dictionary">
</argument>
<description>
- Converts a previously converted instance to a dictionary, back into an instance. Useful for deserializing.
+ Converts a dictionary (previously created with [method inst2dict]) back to an instance. Useful for deserializing.
</description>
</method>
<method name="ease">
@@ -336,13 +327,12 @@
<description>
Rounds [code]s[/code] downward (towards negative infinity), returning the largest whole number that is not more than [code]s[/code].
[codeblock]
- # a is 2.0
- a = floor(2.99)
- # a is -3.0
- a = floor(-2.99)
+ a = floor(2.45) # a is 2.0
+ a = floor(2.99) # a is 2.0
+ a = floor(-2.99) # a is -3.0
[/codeblock]
- See also [method ceil], [method round], and [method stepify].
- [b]Note:[/b] This method returns a float. If you need an integer, you can use [code]int(s)[/code] directly.
+ See also [method ceil], [method round], [method stepify], and [int].
+ [b]Note:[/b] This method returns a float. If you need an integer and [code]s[/code] is a non-negative number, you can use [code]int(s)[/code] directly.
</description>
</method>
<method name="fmod">
@@ -355,8 +345,7 @@
<description>
Returns the floating-point remainder of [code]a/b[/code], keeping the sign of [code]a[/code].
[codeblock]
- # Remainder is 1.5
- var remainder = fmod(7, 5.5)
+ r = fmod(7, 5.5) # r is 1.5
[/codeblock]
For the integer remainder operation, use the % operator.
</description>
@@ -774,9 +763,9 @@
<argument index="1" name="exp" type="float">
</argument>
<description>
- Returns the result of [code]x[/code] raised to the power of [code]y[/code].
+ Returns the result of [code]base[/code] raised to the power of [code]exp[/code].
[codeblock]
- pow(2, 5) # Returns 32
+ pow(2, 5) # Returns 32.0
[/codeblock]
</description>
</method>
@@ -801,7 +790,7 @@
Converts one or more arguments to strings in the best way possible and prints them to the console.
[codeblock]
a = [1, 2, 3]
- print("a", "b", a) # Prints ab[1, 2, 3]
+ print("a", "=", a) # Prints a=[1, 2, 3]
[/codeblock]
[b]Note:[/b] Consider using [method push_error] and [method push_warning] to print error and warning messages instead of [method print]. This distinguishes them from print messages used for debugging purposes, while also displaying a stack trace when an error or warning is printed.
</description>
@@ -900,7 +889,7 @@
<description>
Converts an angle expressed in radians to degrees.
[codeblock]
- rad2deg(0.523599) # Returns 30
+ rad2deg(0.523599) # Returns 30.0
[/codeblock]
</description>
</method>
@@ -1022,9 +1011,11 @@
<description>
Rounds [code]s[/code] to the nearest whole number, with halfway cases rounded away from zero.
[codeblock]
- round(2.6) # Returns 3
+ a = round(2.49) # a is 2.0
+ a = round(2.5) # a is 3.0
+ a = round(2.51) # a is 3.0
[/codeblock]
- See also [method floor], [method ceil], and [method stepify].
+ See also [method floor], [method ceil], [method stepify], and [int].
</description>
</method>
<method name="seed">
@@ -1094,9 +1085,9 @@
This S-shaped curve is the cubic Hermite interpolator, given by [code]f(s) = 3*s^2 - 2*s^3[/code].
[codeblock]
smoothstep(0, 2, -5.0) # Returns 0.0
- smoothstep(0, 2, 0.5) # Returns 0.15625
- smoothstep(0, 2, 1.0) # Returns 0.5
- smoothstep(0, 2, 2.0) # Returns 1.0
+ smoothstep(0, 2, 0.5) # Returns 0.15625
+ smoothstep(0, 2, 1.0) # Returns 0.5
+ smoothstep(0, 2, 2.0) # Returns 1.0
[/codeblock]
</description>
</method>
@@ -1121,12 +1112,9 @@
<description>
Returns the position of the first non-zero digit, after the decimal point. Note that the maximum return value is 10, which is a design decision in the implementation.
[codeblock]
- # n is 0
- n = step_decimals(5)
- # n is 4
- n = step_decimals(1.0005)
- # n is 9
- n = step_decimals(0.000000005)
+ n = step_decimals(5) # n is 0
+ n = step_decimals(1.0005) # n is 4
+ n = step_decimals(0.000000005) # n is 9
[/codeblock]
</description>
</method>
@@ -1140,10 +1128,10 @@
<description>
Snaps float value [code]s[/code] to a given [code]step[/code]. This can also be used to round a floating point number to an arbitrary number of decimals.
[codeblock]
- stepify(100, 32) # Returns 96
+ stepify(100, 32) # Returns 96.0
stepify(3.14159, 0.01) # Returns 3.14
[/codeblock]
- See also [method ceil], [method floor], and [method round].
+ See also [method ceil], [method floor], [method round], and [int].
</description>
</method>
<method name="str" qualifiers="vararg">
@@ -1193,8 +1181,8 @@
<description>
Returns the hyperbolic tangent of [code]s[/code].
[codeblock]
- a = log(2.0) # Returns 0.693147
- tanh(a) # Returns 0.6
+ a = log(2.0) # a is 0.693147
+ b = tanh(a) # b is 0.6
[/codeblock]
</description>
</method>
diff --git a/modules/gdscript/doc_classes/GDScriptFunctionState.xml b/modules/gdscript/doc_classes/GDScriptFunctionState.xml
deleted file mode 100644
index 5e369b32d9..0000000000
--- a/modules/gdscript/doc_classes/GDScriptFunctionState.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="GDScriptFunctionState" inherits="Reference" version="4.0">
- <brief_description>
- State of a function call after yielding.
- </brief_description>
- <description>
- FIXME: Outdated docs as of GDScript rewrite in 4.0.
- Calling [code]yield[/code] within a function will cause that function to yield and return its current state as an object of this type. The yielded function call can then be resumed later by calling [method resume] on this state object.
- </description>
- <tutorials>
- </tutorials>
- <methods>
- <method name="is_valid" qualifiers="const">
- <return type="bool">
- </return>
- <argument index="0" name="extended_check" type="bool" default="false">
- </argument>
- <description>
- Check whether the function call may be resumed. This is not the case if the function state was already resumed.
- If [code]extended_check[/code] is enabled, it also checks if the associated script and object still exist. The extended check is done in debug mode as part of [method GDScriptFunctionState.resume], but you can use this if you know you may be trying to resume without knowing for sure the object and/or script have survived up to that point.
- </description>
- </method>
- <method name="resume">
- <return type="Variant">
- </return>
- <argument index="0" name="arg" type="Variant" default="null">
- </argument>
- <description>
- Resume execution of the yielded function call.
- If handed an argument, return the argument from the [code]yield[/code] call in the yielded function call. You can pass e.g. an [Array] to hand multiple arguments.
- This function returns what the resumed function call returns, possibly another function state if yielded again.
- </description>
- </method>
- </methods>
- <signals>
- <signal name="completed">
- <argument index="0" name="result" type="Variant">
- </argument>
- <description>
- </description>
- </signal>
- </signals>
- <constants>
- </constants>
-</class>
diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h
index 49357f3d2e..e38647eaab 100644
--- a/modules/gdscript/editor/gdscript_highlighter.h
+++ b/modules/gdscript/editor/gdscript_highlighter.h
@@ -42,7 +42,7 @@ private:
Color color;
String start_key;
String end_key;
- bool line_only;
+ bool line_only = false;
};
Vector<ColorRegion> color_regions;
Map<int, int> color_region_cache;
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 53602f7a9b..8fa2de7063 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -231,7 +231,7 @@ void GDScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) {
}
#endif
-void GDScript::get_script_method_list(List<MethodInfo> *p_list) const {
+void GDScript::_get_script_method_list(List<MethodInfo> *r_list, bool p_include_base) const {
const GDScript *current = this;
while (current) {
for (const Map<StringName, GDScriptFunction *>::Element *E = current->member_functions.front(); E; E = E->next()) {
@@ -239,18 +239,29 @@ void GDScript::get_script_method_list(List<MethodInfo> *p_list) const {
MethodInfo mi;
mi.name = E->key();
for (int i = 0; i < func->get_argument_count(); i++) {
- mi.arguments.push_back(func->get_argument_type(i));
+ PropertyInfo arginfo = func->get_argument_type(i);
+#ifdef TOOLS_ENABLED
+ arginfo.name = func->get_argument_name(i);
+#endif
+ mi.arguments.push_back(arginfo);
}
mi.return_val = func->get_return_type();
- p_list->push_back(mi);
+ r_list->push_back(mi);
+ }
+ if (!p_include_base) {
+ return;
}
current = current->_base;
}
}
-void GDScript::get_script_property_list(List<PropertyInfo> *p_list) const {
+void GDScript::get_script_method_list(List<MethodInfo> *r_list) const {
+ _get_script_method_list(r_list, true);
+}
+
+void GDScript::_get_script_property_list(List<PropertyInfo> *r_list, bool p_include_base) const {
const GDScript *sptr = this;
List<PropertyInfo> props;
@@ -269,15 +280,22 @@ void GDScript::get_script_property_list(List<PropertyInfo> *p_list) const {
for (int i = 0; i < msort.size(); i++) {
props.push_front(sptr->member_info[msort[i].name]);
}
+ if (!p_include_base) {
+ break;
+ }
sptr = sptr->_base;
}
for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- p_list->push_back(E->get());
+ r_list->push_back(E->get());
}
}
+void GDScript::get_script_property_list(List<PropertyInfo> *r_list) const {
+ _get_script_property_list(r_list, true);
+}
+
bool GDScript::has_method(const StringName &p_method) const {
return member_functions.has(p_method);
}
@@ -383,6 +401,183 @@ void GDScript::_update_exports_values(Map<StringName, Variant> &values, List<Pro
propnames.push_back(E->get());
}
}
+
+void GDScript::_add_doc(const DocData::ClassDoc &p_inner_class) {
+ if (_owner) {
+ _owner->_add_doc(p_inner_class);
+ } else {
+ for (int i = 0; i < docs.size(); i++) {
+ if (docs[i].name == p_inner_class.name) {
+ docs.remove(i);
+ break;
+ }
+ }
+ docs.append(p_inner_class);
+ }
+}
+
+void GDScript::_clear_doc() {
+ docs.clear();
+ doc = DocData::ClassDoc();
+}
+
+void GDScript::_update_doc() {
+ _clear_doc();
+
+ doc.script_path = "\"" + get_path().get_slice("://", 1) + "\"";
+ if (!name.empty()) {
+ doc.name = name;
+ } else {
+ doc.name = doc.script_path;
+ }
+
+ if (_owner) {
+ doc.name = _owner->doc.name + "." + doc.name;
+ doc.script_path = doc.script_path + "." + doc.name;
+ }
+
+ doc.is_script_doc = true;
+
+ if (base.is_valid() && base->is_valid()) {
+ if (base->doc.name != String()) {
+ doc.inherits = base->doc.name;
+ } else {
+ doc.inherits = base->get_instance_base_type();
+ }
+ } else if (native.is_valid()) {
+ doc.inherits = native->get_name();
+ }
+
+ doc.brief_description = doc_brief_description;
+ doc.description = doc_description;
+ doc.tutorials = doc_tutorials;
+
+ for (Map<String, DocData::EnumDoc>::Element *E = doc_enums.front(); E; E = E->next()) {
+ if (E->value().description != "") {
+ doc.enums[E->key()] = E->value().description;
+ }
+ }
+
+ List<MethodInfo> methods;
+ _get_script_method_list(&methods, false);
+ for (int i = 0; i < methods.size(); i++) {
+ // Ignore internal methods.
+ if (methods[i].name[0] == '@') {
+ continue;
+ }
+
+ DocData::MethodDoc method_doc;
+ const String &class_name = methods[i].name;
+ if (member_functions.has(class_name)) {
+ GDScriptFunction *fn = member_functions[class_name];
+
+ // Change class name if return type is script reference.
+ GDScriptDataType return_type = fn->get_return_type();
+ if (return_type.kind == GDScriptDataType::GDSCRIPT) {
+ methods[i].return_val.class_name = _get_gdscript_reference_class_name(Object::cast_to<GDScript>(return_type.script_type));
+ }
+
+ // Change class name if argumetn is script reference.
+ for (int j = 0; j < fn->get_argument_count(); j++) {
+ GDScriptDataType arg_type = fn->get_argument_type(j);
+ if (arg_type.kind == GDScriptDataType::GDSCRIPT) {
+ methods[i].arguments[j].class_name = _get_gdscript_reference_class_name(Object::cast_to<GDScript>(arg_type.script_type));
+ }
+ }
+ }
+ if (doc_functions.has(methods[i].name)) {
+ DocData::method_doc_from_methodinfo(method_doc, methods[i], doc_functions[methods[i].name]);
+ } else {
+ DocData::method_doc_from_methodinfo(method_doc, methods[i], String());
+ }
+ doc.methods.push_back(method_doc);
+ }
+
+ List<PropertyInfo> props;
+ _get_script_property_list(&props, false);
+ for (int i = 0; i < props.size(); i++) {
+ ScriptMemberInfo scr_member_info;
+ scr_member_info.propinfo = props[i];
+ scr_member_info.propinfo.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+ if (member_indices.has(props[i].name)) {
+ const MemberInfo &mi = member_indices[props[i].name];
+ scr_member_info.setter = mi.setter;
+ scr_member_info.getter = mi.getter;
+ if (mi.data_type.kind == GDScriptDataType::GDSCRIPT) {
+ scr_member_info.propinfo.class_name = _get_gdscript_reference_class_name(
+ Object::cast_to<GDScript>(mi.data_type.script_type));
+ }
+ }
+ if (member_default_values.has(props[i].name)) {
+ scr_member_info.has_default_value = true;
+ scr_member_info.default_value = member_default_values[props[i].name];
+ }
+ if (doc_variables.has(props[i].name)) {
+ scr_member_info.doc_string = doc_variables[props[i].name];
+ }
+
+ DocData::PropertyDoc prop_doc;
+ DocData::property_doc_from_scriptmemberinfo(prop_doc, scr_member_info);
+ doc.properties.push_back(prop_doc);
+ }
+
+ List<MethodInfo> signals;
+ _get_script_signal_list(&signals, false);
+ for (int i = 0; i < signals.size(); i++) {
+ DocData::MethodDoc signal_doc;
+ if (doc_signals.has(signals[i].name)) {
+ DocData::signal_doc_from_methodinfo(signal_doc, signals[i], signals[i].name);
+ } else {
+ DocData::signal_doc_from_methodinfo(signal_doc, signals[i], String());
+ }
+ doc.signals.push_back(signal_doc);
+ }
+
+ for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) {
+ if (subclasses.has(E->key())) {
+ continue;
+ }
+
+ // Enums.
+ bool is_enum = false;
+ if (E->value().get_type() == Variant::DICTIONARY) {
+ if (doc_enums.has(E->key())) {
+ is_enum = true;
+ for (int i = 0; i < doc_enums[E->key()].values.size(); i++) {
+ doc_enums[E->key()].values.write[i].enumeration = E->key();
+ doc.constants.push_back(doc_enums[E->key()].values[i]);
+ }
+ }
+ }
+ if (!is_enum && doc_enums.has("@unnamed_enums")) {
+ for (int i = 0; i < doc_enums["@unnamed_enums"].values.size(); i++) {
+ if (E->key() == doc_enums["@unnamed_enums"].values[i].name) {
+ is_enum = true;
+ DocData::ConstantDoc constant_doc;
+ constant_doc.enumeration = "@unnamed_enums";
+ DocData::constant_doc_from_variant(constant_doc, E->key(), E->value(), doc_enums["@unnamed_enums"].values[i].description);
+ doc.constants.push_back(constant_doc);
+ break;
+ }
+ }
+ }
+ if (!is_enum) {
+ DocData::ConstantDoc constant_doc;
+ String doc_description;
+ if (doc_constants.has(E->key())) {
+ doc_description = doc_constants[E->key()];
+ }
+ DocData::constant_doc_from_variant(constant_doc, E->key(), E->value(), doc_description);
+ doc.constants.push_back(constant_doc);
+ }
+ }
+
+ for (Map<StringName, Ref<GDScript>>::Element *E = subclasses.front(); E; E = E->next()) {
+ E->get()->_update_doc();
+ }
+
+ _add_doc(doc);
+}
#endif
bool GDScript::_update_exports(bool *r_err, bool p_recursive_call) {
@@ -628,8 +823,12 @@ Error GDScript::reload(bool p_keep_state) {
if (EngineDebugger::is_active()) {
GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message);
}
- // TODO: Show all error messages.
- _err_print_error("GDScript::reload", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), ERR_HANDLER_SCRIPT);
+
+ const List<GDScriptParser::ParserError>::Element *e = parser.get_errors().front();
+ while (e != nullptr) {
+ _err_print_error("GDScript::reload", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), e->get().line, ("Parse Error: " + e->get().message).utf8().get_data(), ERR_HANDLER_SCRIPT);
+ e = e->next();
+ }
ERR_FAIL_V(ERR_PARSE_ERROR);
}
@@ -638,6 +837,10 @@ Error GDScript::reload(bool p_keep_state) {
GDScriptCompiler compiler;
err = compiler.compile(&parser, this, p_keep_state);
+#ifdef TOOLS_ENABLED
+ _update_doc();
+#endif
+
if (err) {
if (can_run) {
if (EngineDebugger::is_active()) {
@@ -911,7 +1114,7 @@ bool GDScript::has_script_signal(const StringName &p_signal) const {
return false;
}
-void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
+void GDScript::_get_script_signal_list(List<MethodInfo> *r_list, bool p_include_base) const {
for (const Map<StringName, Vector<StringName>>::Element *E = _signals.front(); E; E = E->next()) {
MethodInfo mi;
mi.name = E->key();
@@ -920,20 +1123,43 @@ void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
arg.name = E->get()[i];
mi.arguments.push_back(arg);
}
- r_signals->push_back(mi);
+ r_list->push_back(mi);
+ }
+
+ if (!p_include_base) {
+ return;
}
if (base.is_valid()) {
- base->get_script_signal_list(r_signals);
+ base->get_script_signal_list(r_list);
}
#ifdef TOOLS_ENABLED
else if (base_cache.is_valid()) {
- base_cache->get_script_signal_list(r_signals);
+ base_cache->get_script_signal_list(r_list);
}
#endif
}
+void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
+ _get_script_signal_list(r_signals, true);
+}
+
+String GDScript::_get_gdscript_reference_class_name(const GDScript *p_gdscript) {
+ ERR_FAIL_NULL_V(p_gdscript, String());
+
+ String class_name;
+ while (p_gdscript) {
+ if (class_name == "") {
+ class_name = p_gdscript->get_script_class_name();
+ } else {
+ class_name = p_gdscript->get_script_class_name() + "." + class_name;
+ }
+ p_gdscript = p_gdscript->_owner;
+ }
+ return class_name;
+}
+
GDScript::GDScript() :
script_list(this) {
valid = false;
@@ -1061,6 +1287,13 @@ GDScript::~GDScript() {
_save_orphaned_subclasses();
+#ifdef TOOLS_ENABLED
+ // Clearing inner class doc, script doc only cleared when the script source deleted.
+ if (_owner) {
+ _clear_doc();
+ }
+#endif
+
#ifdef DEBUG_ENABLED
{
MutexLock lock(GDScriptLanguage::get_singleton()->lock);
@@ -1889,8 +2122,11 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
w++;
}
- for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
- p_words->push_back(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i)));
+ List<StringName> functions;
+ GDScriptUtilityFunctions::get_function_list(&functions);
+
+ for (const List<StringName>::Element *E = functions.front(); E; E = E->next()) {
+ p_words->push_back(String(E->get()));
}
}
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index b69a6e39c0..11c449c5f2 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -33,6 +33,7 @@
#include "core/debugger/engine_debugger.h"
#include "core/debugger/script_debugger.h"
+#include "core/doc_data.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
#include "core/object/script_language.h"
@@ -71,8 +72,8 @@ class GDScript : public Script {
friend class GDScriptFunction;
friend class GDScriptAnalyzer;
friend class GDScriptCompiler;
- friend class GDScriptFunctions;
friend class GDScriptLanguage;
+ friend struct GDScriptUtilityFunctionsDefinitions;
Ref<GDScriptNativeClass> native;
Ref<GDScript> base;
@@ -91,9 +92,7 @@ class GDScript : public Script {
#ifdef TOOLS_ENABLED
Map<StringName, int> member_lines;
-
Map<StringName, Variant> member_default_values;
-
List<PropertyInfo> members_cache;
Map<StringName, Variant> member_default_values_cache;
Ref<GDScript> base_cache;
@@ -102,6 +101,20 @@ class GDScript : public Script {
bool placeholder_fallback_enabled;
void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames);
+ DocData::ClassDoc doc;
+ Vector<DocData::ClassDoc> docs;
+ String doc_brief_description;
+ String doc_description;
+ Vector<DocData::TutorialDoc> doc_tutorials;
+ Map<String, String> doc_functions;
+ Map<String, String> doc_variables;
+ Map<String, String> doc_constants;
+ Map<String, String> doc_signals;
+ Map<String, DocData::EnumDoc> doc_enums;
+ void _clear_doc();
+ void _update_doc();
+ void _add_doc(const DocData::ClassDoc &p_inner_class);
+
#endif
Map<StringName, PropertyInfo> member_info;
@@ -141,6 +154,13 @@ class GDScript : public Script {
void _save_orphaned_subclasses();
void _init_rpc_methods_properties();
+ void _get_script_property_list(List<PropertyInfo> *r_list, bool p_include_base) const;
+ void _get_script_method_list(List<MethodInfo> *r_list, bool p_include_base) const;
+ void _get_script_signal_list(List<MethodInfo> *r_list, bool p_include_base) const;
+
+ // This method will map the class name from "Reference" to "MyClass.InnerClass".
+ static String _get_gdscript_reference_class_name(const GDScript *p_gdscript);
+
protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
bool _set(const StringName &p_name, const Variant &p_value);
@@ -191,6 +211,12 @@ public:
virtual void set_source_code(const String &p_code) override;
virtual void update_exports() override;
+#ifdef TOOLS_ENABLED
+ virtual const Vector<DocData::ClassDoc> &get_documentation() const override {
+ return docs;
+ }
+#endif // TOOLS_ENABLED
+
virtual Error reload(bool p_keep_state = false) override;
void set_script_path(const String &p_path) { path = p_path; } //because subclasses need a path too...
@@ -244,8 +270,8 @@ public:
class GDScriptInstance : public ScriptInstance {
friend class GDScript;
friend class GDScriptFunction;
- friend class GDScriptFunctions;
friend class GDScriptCompiler;
+ friend struct GDScriptUtilityFunctionsDefinitions;
ObjectID owner_id;
Object *owner;
@@ -444,7 +470,8 @@ public:
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;
- virtual bool can_inherit_from_file() { return true; }
+ virtual bool supports_documentation() const;
+ virtual bool can_inherit_from_file() const { return true; }
virtual int find_function(const String &p_function, const String &p_code) const;
virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const;
virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint);
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 320778c635..19951ff17d 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -37,6 +37,7 @@
#include "core/os/file_access.h"
#include "core/templates/hash_map.h"
#include "gdscript.h"
+#include "gdscript_utility_functions.h"
// TODO: Move this to a central location (maybe core?).
static HashMap<StringName, StringName> underscore_map;
@@ -72,6 +73,39 @@ static StringName get_real_class_name(const StringName &p_source) {
return p_source;
}
+static MethodInfo info_from_utility_func(const StringName &p_function) {
+ ERR_FAIL_COND_V(!Variant::has_utility_function(p_function), MethodInfo());
+
+ MethodInfo info(p_function);
+
+ if (Variant::has_utility_function_return_value(p_function)) {
+ info.return_val.type = Variant::get_utility_function_return_type(p_function);
+ if (info.return_val.type == Variant::NIL) {
+ info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+ }
+ }
+
+ if (Variant::is_utility_function_vararg(p_function)) {
+ info.flags |= METHOD_FLAG_VARARG;
+ } else {
+ for (int i = 0; i < Variant::get_utility_function_argument_count(p_function); i++) {
+ PropertyInfo pi;
+#ifdef DEBUG_METHODS_ENABLED
+ pi.name = Variant::get_utility_function_argument_name(p_function, i);
+#else
+ pi.name = "arg" + itos(i + 1);
+#endif
+ pi.type = Variant::get_utility_function_argument_type(p_function, i);
+ if (pi.type == Variant::NIL) {
+ pi.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+ }
+ info.arguments.push_back(pi);
+ }
+ }
+
+ return info;
+}
+
void GDScriptAnalyzer::cleanup() {
underscore_map.clear();
}
@@ -860,7 +894,12 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
parser->push_warning(p_function->parameters[i]->identifier, GDScriptWarning::UNUSED_PARAMETER, p_function->identifier->name, p_function->parameters[i]->identifier->name);
}
is_shadowing(p_function->parameters[i]->identifier, "function parameter");
-#endif
+#endif // DEBUG_ENABLED
+#ifdef TOOLS_ENABLED
+ if (p_function->parameters[i]->default_value && p_function->parameters[i]->default_value->is_constant) {
+ p_function->default_arg_values.push_back(p_function->parameters[i]->default_value->reduced_value);
+ }
+#endif // TOOLS_ENABLED
}
if (p_function->identifier->name == "_init") {
@@ -983,12 +1022,14 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
}
GDScriptParser::DataType arg_type = call->arguments[i]->get_datatype();
- if (arg_type.kind != GDScriptParser::DataType::BUILTIN) {
- all_is_constant = false;
- push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]);
- } else if (arg_type.builtin_type != Variant::INT && arg_type.builtin_type != Variant::FLOAT) {
- all_is_constant = false;
- push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]);
+ if (!arg_type.is_variant()) {
+ if (arg_type.kind != GDScriptParser::DataType::BUILTIN) {
+ all_is_constant = false;
+ push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]);
+ } else if (arg_type.builtin_type != Variant::INT && arg_type.builtin_type != Variant::FLOAT) {
+ all_is_constant = false;
+ push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]);
+ }
}
}
@@ -1609,7 +1650,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
if (p_binary_op->reduced_value.get_type() == Variant::STRING) {
push_error(vformat(R"(%s in operator %s.)", p_binary_op->reduced_value, Variant::get_operator_name(p_binary_op->variant_op)), p_binary_op);
} else {
- push_error(vformat(R"(Invalid operands to operator %s, %s and %s.".)",
+ push_error(vformat(R"(Invalid operands to operator %s, %s and %s.)",
Variant::get_operator_name(p_binary_op->variant_op),
Variant::get_type_name(p_binary_op->left_operand->reduced_value.get_type()),
Variant::get_type_name(p_binary_op->right_operand->reduced_value.get_type())),
@@ -1694,7 +1735,6 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
// Call to name directly.
StringName function_name = p_call->function_name;
Variant::Type builtin_type = GDScriptParser::get_builtin_type(function_name);
- GDScriptFunctions::Function builtin_function = GDScriptParser::get_builtin_function(function_name);
if (builtin_type < Variant::VARIANT_MAX) {
// Is a builtin constructor.
@@ -1707,7 +1747,27 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
call_type.native_type = function_name; // "Object".
}
- if (all_is_constant) {
+ bool safe_to_fold = true;
+ switch (builtin_type) {
+ // Those are stored by reference so not suited for compile-time construction.
+ // Because in this case they would be the same reference in all constructed values.
+ case Variant::OBJECT:
+ case Variant::PACKED_BYTE_ARRAY:
+ case Variant::PACKED_INT32_ARRAY:
+ case Variant::PACKED_INT64_ARRAY:
+ case Variant::PACKED_FLOAT32_ARRAY:
+ case Variant::PACKED_FLOAT64_ARRAY:
+ case Variant::PACKED_STRING_ARRAY:
+ case Variant::PACKED_VECTOR2_ARRAY:
+ case Variant::PACKED_VECTOR3_ARRAY:
+ case Variant::PACKED_COLOR_ARRAY:
+ safe_to_fold = false;
+ break;
+ default:
+ break;
+ }
+
+ if (all_is_constant && safe_to_fold) {
// Construct here.
Vector<const Variant *> args;
for (int i = 0; i < p_call->arguments.size(); i++) {
@@ -1816,10 +1876,52 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
}
p_call->set_datatype(call_type);
return;
- } else if (builtin_function < GDScriptFunctions::FUNC_MAX) {
- MethodInfo function_info = GDScriptFunctions::get_info(builtin_function);
+ } else if (GDScriptUtilityFunctions::function_exists(function_name)) {
+ MethodInfo function_info = GDScriptUtilityFunctions::get_function_info(function_name);
+
+ if (all_is_constant && GDScriptUtilityFunctions::is_function_constant(function_name)) {
+ // Can call on compilation.
+ Vector<const Variant *> args;
+ for (int i = 0; i < p_call->arguments.size(); i++) {
+ args.push_back(&(p_call->arguments[i]->reduced_value));
+ }
+
+ Variant value;
+ Callable::CallError err;
+ GDScriptUtilityFunctions::get_function(function_name)(&value, (const Variant **)args.ptr(), args.size(), err);
+
+ switch (err.error) {
+ case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
+ PropertyInfo wrong_arg = function_info.arguments[err.argument];
+ push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", function_name, err.argument + 1,
+ type_from_property(wrong_arg).to_string(), p_call->arguments[err.argument]->get_datatype().to_string()),
+ p_call->arguments[err.argument]);
+ } break;
+ case Callable::CallError::CALL_ERROR_INVALID_METHOD:
+ push_error(vformat(R"(Invalid call for function "%s".)", function_name), p_call);
+ break;
+ case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
+ push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
+ break;
+ case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
+ push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
+ break;
+ case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
+ break; // Can't happen in a builtin constructor.
+ case Callable::CallError::CALL_OK:
+ p_call->is_constant = true;
+ p_call->reduced_value = value;
+ break;
+ }
+ } else {
+ validate_call_arg(function_info, p_call);
+ }
+ p_call->set_datatype(type_from_property(function_info.return_val));
+ return;
+ } else if (Variant::has_utility_function(function_name)) {
+ MethodInfo function_info = info_from_utility_func(function_name);
- if (all_is_constant && GDScriptFunctions::is_deterministic(builtin_function)) {
+ if (all_is_constant && Variant::get_utility_function_type(function_name) == Variant::UTILITY_FUNC_TYPE_MATH) {
// Can call on compilation.
Vector<const Variant *> args;
for (int i = 0; i < p_call->arguments.size(); i++) {
@@ -1828,23 +1930,23 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
Variant value;
Callable::CallError err;
- GDScriptFunctions::call(builtin_function, (const Variant **)args.ptr(), args.size(), value, err);
+ Variant::call_utility_function(function_name, &value, (const Variant **)args.ptr(), args.size(), err);
switch (err.error) {
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
PropertyInfo wrong_arg = function_info.arguments[err.argument];
- push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", GDScriptFunctions::get_func_name(builtin_function), err.argument + 1,
+ push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", function_name, err.argument + 1,
type_from_property(wrong_arg).to_string(), p_call->arguments[err.argument]->get_datatype().to_string()),
p_call->arguments[err.argument]);
} break;
case Callable::CallError::CALL_ERROR_INVALID_METHOD:
- push_error(vformat(R"(Invalid call for function "%s".)", GDScriptFunctions::get_func_name(builtin_function)), p_call);
+ push_error(vformat(R"(Invalid call for function "%s".)", function_name), p_call);
break;
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
- push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", GDScriptFunctions::get_func_name(builtin_function), err.expected, p_call->arguments.size()), p_call);
+ push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
break;
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
- push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", GDScriptFunctions::get_func_name(builtin_function), err.expected, p_call->arguments.size()), p_call);
+ push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
break;
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
break; // Can't happen in a builtin constructor.
@@ -2358,7 +2460,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
// Not found.
// Check if it's a builtin function.
- if (parser->get_builtin_function(name) < GDScriptFunctions::FUNC_MAX) {
+ if (GDScriptUtilityFunctions::function_exists(name)) {
push_error(vformat(R"(Built-in function "%s" cannot be used as an identifier.)", name), p_identifier);
} else {
push_error(vformat(R"(Identifier "%s" not declared in the current scope.)", name), p_identifier);
@@ -2728,7 +2830,7 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op)
mark_node_unsafe(p_unary_op);
} else {
bool valid = false;
- result = get_operation_type(p_unary_op->variant_op, p_unary_op->operand->get_datatype(), p_unary_op->operand->get_datatype(), valid, p_unary_op);
+ result = get_operation_type(p_unary_op->variant_op, p_unary_op->operand->get_datatype(), valid, p_unary_op);
if (!valid) {
push_error(vformat(R"(Invalid operand of type "%s" for unary operator "%s".)", p_unary_op->operand->get_datatype().to_string(), Variant::get_operator_name(p_unary_op->variant_op)), p_unary_op->operand);
@@ -3094,81 +3196,31 @@ bool GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_local, con
}
#endif
-GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source) {
- // This function creates dummy variant values and apply the operation to those. Less error-prone than keeping a table of valid operations.
+GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source) {
+ // Unary version.
+ GDScriptParser::DataType nil_type;
+ nil_type.builtin_type = Variant::NIL;
+ return get_operation_type(p_operation, p_a, nil_type, r_valid, p_source);
+}
+GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source) {
GDScriptParser::DataType result;
result.kind = GDScriptParser::DataType::VARIANT;
Variant::Type a_type = p_a.builtin_type;
Variant::Type b_type = p_b.builtin_type;
- Variant a;
- REF a_ref;
- if (a_type == Variant::OBJECT) {
- a_ref.instance();
- a = a_ref;
- } else {
- Callable::CallError err;
- Variant::construct(a_type, a, nullptr, 0, err);
- if (err.error != Callable::CallError::CALL_OK) {
- r_valid = false;
- ERR_FAIL_V_MSG(result, vformat("Could not construct value of type %s", Variant::get_type_name(a_type)));
- }
- }
- Variant b;
- REF b_ref;
- if (b_type == Variant::OBJECT) {
- b_ref.instance();
- b = b_ref;
- } else {
- Callable::CallError err;
- Variant::construct(b_type, b, nullptr, 0, err);
- if (err.error != Callable::CallError::CALL_OK) {
- r_valid = false;
- ERR_FAIL_V_MSG(result, vformat("Could not construct value of type %s", Variant::get_type_name(b_type)));
- }
- }
-
- // Avoid division by zero.
- switch (b_type) {
- case Variant::INT:
- b = 1;
- break;
- case Variant::FLOAT:
- b = 1.0;
- break;
- case Variant::VECTOR2:
- b = Vector2(1.0, 1.0);
- break;
- case Variant::VECTOR2I:
- b = Vector2i(1, 1);
- break;
- case Variant::VECTOR3:
- b = Vector3(1.0, 1.0, 1.0);
- break;
- case Variant::VECTOR3I:
- b = Vector3i(1, 1, 1);
- break;
- case Variant::COLOR:
- b = Color(1.0, 1.0, 1.0, 1.0);
- break;
- default:
- // No change needed.
- break;
- }
+ Variant::ValidatedOperatorEvaluator op_eval = Variant::get_validated_operator_evaluator(p_operation, a_type, b_type);
- // Avoid error in formatting operator (%) where it doesn't find a placeholder.
- if (a_type == Variant::STRING && b_type != Variant::ARRAY) {
- a = String("%s");
+ if (op_eval == nullptr) {
+ r_valid = false;
+ return result;
}
- Variant ret;
- Variant::evaluate(p_operation, a, b, ret, r_valid);
+ r_valid = true;
- if (r_valid) {
- return type_from_variant(ret, p_source);
- }
+ result.kind = GDScriptParser::DataType::BUILTIN;
+ result.builtin_type = Variant::get_operator_return_type(p_operation, a_type, b_type);
return result;
}
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index 4fb9c633f1..9925167856 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -102,6 +102,7 @@ class GDScriptAnalyzer {
bool validate_call_arg(const List<GDScriptParser::DataType> &p_par_types, int p_default_args_count, bool p_is_vararg, const GDScriptParser::CallNode *p_call);
bool validate_call_arg(const MethodInfo &p_method, const GDScriptParser::CallNode *p_call);
GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source);
+ GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source);
bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false) const;
void push_error(const String &p_message, const GDScriptParser::Node *p_origin);
void mark_node_unsafe(const GDScriptParser::Node *p_node);
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index a81b3298ef..a5d96077d9 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -40,10 +40,6 @@ uint32_t GDScriptByteCodeGenerator::add_parameter(const StringName &p_name, bool
function->_argument_count++;
function->argument_types.push_back(p_type);
if (p_is_optional) {
- if (function->_default_arg_count == 0) {
- append(GDScriptFunction::OPCODE_JUMP_TO_DEF_ARGUMENT);
- }
- function->default_arguments.push_back(opcodes.size());
function->_default_arg_count++;
}
@@ -96,7 +92,12 @@ void GDScriptByteCodeGenerator::pop_temporary() {
current_temporaries--;
}
-void GDScriptByteCodeGenerator::start_parameters() {}
+void GDScriptByteCodeGenerator::start_parameters() {
+ if (function->_default_arg_count > 0) {
+ append(GDScriptFunction::OPCODE_JUMP_TO_DEF_ARGUMENT);
+ function->default_arguments.push_back(opcodes.size());
+ }
+}
void GDScriptByteCodeGenerator::end_parameters() {
function->default_arguments.invert();
@@ -167,7 +168,7 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
}
if (function->default_arguments.size()) {
- function->_default_arg_count = function->default_arguments.size();
+ function->_default_arg_count = function->default_arguments.size() - 1;
function->_default_arg_ptr = &function->default_arguments[0];
} else {
function->_default_arg_count = 0;
@@ -282,6 +283,30 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->_constructors_count = 0;
}
+ if (utilities_map.size()) {
+ function->utilities.resize(utilities_map.size());
+ function->_utilities_ptr = function->utilities.ptr();
+ function->_utilities_count = utilities_map.size();
+ for (const Map<Variant::ValidatedUtilityFunction, int>::Element *E = utilities_map.front(); E; E = E->next()) {
+ function->utilities.write[E->get()] = E->key();
+ }
+ } else {
+ function->_utilities_ptr = nullptr;
+ function->_utilities_count = 0;
+ }
+
+ if (gds_utilities_map.size()) {
+ function->gds_utilities.resize(gds_utilities_map.size());
+ function->_gds_utilities_ptr = function->gds_utilities.ptr();
+ function->_gds_utilities_count = gds_utilities_map.size();
+ for (const Map<GDScriptUtilityFunctions::FunctionPtr, int>::Element *E = gds_utilities_map.front(); E; E = E->next()) {
+ function->gds_utilities.write[E->get()] = E->key();
+ }
+ } else {
+ function->_gds_utilities_ptr = nullptr;
+ function->_gds_utilities_count = 0;
+ }
+
if (method_bind_map.size()) {
function->methods.resize(method_bind_map.size());
function->_methods_ptr = function->methods.ptrw();
@@ -321,7 +346,28 @@ void GDScriptByteCodeGenerator::set_initial_line(int p_line) {
#define IS_BUILTIN_TYPE(m_var, m_type) \
(m_var.type.has_type && m_var.type.kind == GDScriptDataType::BUILTIN && m_var.type.builtin_type == m_type)
-void GDScriptByteCodeGenerator::write_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) {
+void GDScriptByteCodeGenerator::write_unary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand) {
+ if (HAS_BUILTIN_TYPE(p_left_operand)) {
+ // Gather specific operator.
+ Variant::ValidatedOperatorEvaluator op_func = Variant::get_validated_operator_evaluator(p_operator, p_left_operand.type.builtin_type, Variant::NIL);
+
+ append(GDScriptFunction::OPCODE_OPERATOR_VALIDATED, 3);
+ append(p_left_operand);
+ append(Address());
+ append(p_target);
+ append(op_func);
+ return;
+ }
+
+ // No specific types, perform variant evaluation.
+ append(GDScriptFunction::OPCODE_OPERATOR, 3);
+ append(p_left_operand);
+ append(Address());
+ append(p_target);
+ append(p_operator);
+}
+
+void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) {
if (HAS_BUILTIN_TYPE(p_left_operand) && HAS_BUILTIN_TYPE(p_right_operand)) {
// Gather specific operator.
Variant::ValidatedOperatorEvaluator op_func = Variant::get_validated_operator_evaluator(p_operator, p_left_operand.type.builtin_type, p_right_operand.type.builtin_type);
@@ -382,7 +428,7 @@ void GDScriptByteCodeGenerator::write_end_and(const Address &p_target) {
patch_jump(logic_op_jump_pos2.back()->get());
logic_op_jump_pos1.pop_back();
logic_op_jump_pos2.pop_back();
- append(GDScriptFunction::OPCODE_ASSIGN_FALSE, 0);
+ append(GDScriptFunction::OPCODE_ASSIGN_FALSE, 1);
append(p_target);
}
@@ -407,7 +453,7 @@ void GDScriptByteCodeGenerator::write_end_or(const Address &p_target) {
// Jump away from the success condition.
append(GDScriptFunction::OPCODE_JUMP, 0);
append(opcodes.size() + 3);
- // Here it means one of operands is false.
+ // Here it means one of operands is true.
patch_jump(logic_op_jump_pos1.back()->get());
patch_jump(logic_op_jump_pos2.back()->get());
logic_op_jump_pos1.pop_back();
@@ -612,6 +658,11 @@ void GDScriptByteCodeGenerator::write_assign_false(const Address &p_target) {
append(p_target);
}
+void GDScriptByteCodeGenerator::write_assign_default_parameter(const Address &p_dst, const Address &p_src) {
+ write_assign(p_dst, p_src);
+ function->default_arguments.push_back(opcodes.size());
+}
+
void GDScriptByteCodeGenerator::write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) {
int index = 0;
@@ -677,8 +728,8 @@ void GDScriptByteCodeGenerator::write_call_async(const Address &p_target, const
append(p_function_name);
}
-void GDScriptByteCodeGenerator::write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CALL_BUILT_IN, 1 + p_arguments.size());
+void GDScriptByteCodeGenerator::write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) {
+ append(GDScriptFunction::OPCODE_CALL_GDSCRIPT_UTILITY, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
@@ -687,6 +738,41 @@ void GDScriptByteCodeGenerator::write_call_builtin(const Address &p_target, GDSc
append(p_function);
}
+void GDScriptByteCodeGenerator::write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) {
+ bool is_validated = true;
+ if (Variant::is_utility_function_vararg(p_function)) {
+ is_validated = true; // Vararg works fine with any argument, since they can be any type.
+ } else if (p_arguments.size() == Variant::get_utility_function_argument_count(p_function)) {
+ bool all_types_exact = true;
+ for (int i = 0; i < p_arguments.size(); i++) {
+ if (!IS_BUILTIN_TYPE(p_arguments[i], Variant::get_utility_function_argument_type(p_function, i))) {
+ all_types_exact = false;
+ break;
+ }
+ }
+
+ is_validated = all_types_exact;
+ }
+
+ if (is_validated) {
+ append(GDScriptFunction::OPCODE_CALL_UTILITY_VALIDATED, 1 + p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ append(p_arguments.size());
+ append(Variant::get_validated_utility_function(p_function));
+ } else {
+ append(GDScriptFunction::OPCODE_CALL_UTILITY, 1 + p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ append(p_arguments.size());
+ append(p_function);
+ }
+}
+
void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) {
bool is_validated = false;
diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h
index 0173b7f820..21576b08a4 100644
--- a/modules/gdscript/gdscript_byte_codegen.h
+++ b/modules/gdscript/gdscript_byte_codegen.h
@@ -34,6 +34,7 @@
#include "gdscript_codegen.h"
#include "gdscript_function.h"
+#include "gdscript_utility_functions.h"
class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
bool ended = false;
@@ -76,6 +77,8 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
Map<Variant::ValidatedIndexedGetter, int> indexed_getters_map;
Map<Variant::ValidatedBuiltInMethod, int> builtin_method_map;
Map<Variant::ValidatedConstructor, int> constructors_map;
+ Map<Variant::ValidatedUtilityFunction, int> utilities_map;
+ Map<GDScriptUtilityFunctions::FunctionPtr, int> gds_utilities_map;
Map<MethodBind *, int> method_bind_map;
// Lists since these can be nested.
@@ -241,6 +244,24 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
return pos;
}
+ int get_utility_pos(const Variant::ValidatedUtilityFunction p_utility) {
+ if (utilities_map.has(p_utility)) {
+ return utilities_map[p_utility];
+ }
+ int pos = utilities_map.size();
+ utilities_map[p_utility] = pos;
+ return pos;
+ }
+
+ int get_gds_utility_pos(const GDScriptUtilityFunctions::FunctionPtr p_gds_utility) {
+ if (gds_utilities_map.has(p_gds_utility)) {
+ return gds_utilities_map[p_gds_utility];
+ }
+ int pos = gds_utilities_map.size();
+ gds_utilities_map[p_gds_utility] = pos;
+ return pos;
+ }
+
int get_method_bind_pos(MethodBind *p_method) {
if (method_bind_map.has(p_method)) {
return method_bind_map[p_method];
@@ -346,6 +367,14 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
opcodes.push_back(get_constructor_pos(p_constructor));
}
+ void append(const Variant::ValidatedUtilityFunction p_utility) {
+ opcodes.push_back(get_utility_pos(p_utility));
+ }
+
+ void append(const GDScriptUtilityFunctions::FunctionPtr p_gds_utility) {
+ opcodes.push_back(get_gds_utility_pos(p_gds_utility));
+ }
+
void append(MethodBind *p_method) {
opcodes.push_back(get_method_bind_pos(p_method));
}
@@ -377,7 +406,8 @@ public:
#endif
virtual void set_initial_line(int p_line) override;
- virtual void write_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) override;
+ virtual void write_unary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand) override;
+ virtual void write_binary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) override;
virtual void write_type_test(const Address &p_target, const Address &p_source, const Address &p_type) override;
virtual void write_type_test_builtin(const Address &p_target, const Address &p_source, Variant::Type p_type) override;
virtual void write_and_left_operand(const Address &p_left_operand) override;
@@ -400,11 +430,13 @@ public:
virtual void write_assign(const Address &p_target, const Address &p_source) override;
virtual void write_assign_true(const Address &p_target) override;
virtual void write_assign_false(const Address &p_target) override;
+ virtual void write_assign_default_parameter(const Address &p_dst, const Address &p_src) override;
virtual void write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) override;
virtual void write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
- virtual void write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) override;
+ virtual void write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) override;
+ virtual void write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) override;
virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override;
virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override;
virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override;
diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h
index decab3df0b..e776007bd7 100644
--- a/modules/gdscript/gdscript_codegen.h
+++ b/modules/gdscript/gdscript_codegen.h
@@ -35,7 +35,7 @@
#include "core/string/string_name.h"
#include "core/variant/variant.h"
#include "gdscript_function.h"
-#include "gdscript_functions.h"
+#include "gdscript_utility_functions.h"
class GDScriptCodeGenerator {
public:
@@ -98,7 +98,8 @@ public:
// virtual void alloc_stack(int p_level) = 0; // Is this needed?
// virtual void alloc_call(int p_arg_count) = 0; // This might be automatic from other functions.
- virtual void write_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) = 0;
+ virtual void write_unary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand) = 0;
+ virtual void write_binary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) = 0;
virtual void write_type_test(const Address &p_target, const Address &p_source, const Address &p_type) = 0;
virtual void write_type_test_builtin(const Address &p_target, const Address &p_source, Variant::Type p_type) = 0;
virtual void write_and_left_operand(const Address &p_left_operand) = 0;
@@ -121,11 +122,13 @@ public:
virtual void write_assign(const Address &p_target, const Address &p_source) = 0;
virtual void write_assign_true(const Address &p_target) = 0;
virtual void write_assign_false(const Address &p_target) = 0;
+ virtual void write_assign_default_parameter(const Address &dst, const Address &src) = 0;
virtual void write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) = 0;
virtual void write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
- virtual void write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) = 0;
virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) = 0;
virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 3d327cebeb..af6991041e 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -33,6 +33,7 @@
#include "gdscript.h"
#include "gdscript_byte_codegen.h"
#include "gdscript_cache.h"
+#include "gdscript_utility_functions.h"
bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringName &p_name) {
if (codegen.function_node && codegen.function_node->is_static) {
@@ -98,7 +99,8 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
} break;
case GDScriptParser::DataType::SCRIPT: {
result.kind = GDScriptDataType::SCRIPT;
- result.script_type = Ref<Script>(p_datatype.script_type).ptr();
+ result.script_type_ref = Ref<Script>(p_datatype.script_type);
+ result.script_type = result.script_type_ref.ptr();
result.native_type = result.script_type->get_instance_base_type();
} break;
case GDScriptParser::DataType::CLASS: {
@@ -124,11 +126,13 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
names.pop_back();
}
result.kind = GDScriptDataType::GDSCRIPT;
- result.script_type = script.ptr();
+ result.script_type_ref = script;
+ result.script_type = result.script_type_ref.ptr();
result.native_type = script->get_instance_base_type();
} else {
result.kind = GDScriptDataType::GDSCRIPT;
- result.script_type = GDScriptCache::get_shallow_script(p_datatype.script_path, main_script->path).ptr();
+ result.script_type_ref = GDScriptCache::get_shallow_script(p_datatype.script_path, main_script->path);
+ result.script_type = result.script_type_ref.ptr();
result.native_type = p_datatype.native_type;
}
}
@@ -151,8 +155,8 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
// Only hold strong reference to the script if it's not the owner of the
// element qualified with this type, to avoid cyclic references (leaks).
- if (result.script_type && result.script_type != p_owner) {
- result.script_type_ref = Ref<Script>(result.script_type);
+ if (result.script_type && result.script_type == p_owner) {
+ result.script_type_ref = Ref<Script>();
}
return result;
@@ -453,15 +457,17 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
arguments.push_back(arg);
}
- if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name) != Variant::VARIANT_MAX) {
+ if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(call->function_name) != Variant::VARIANT_MAX) {
// Construct a built-in type.
Variant::Type vtype = GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
gen->write_construct(result, vtype, arguments);
- } else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_function(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name) != GDScriptFunctions::FUNC_MAX) {
- // Built-in function.
- GDScriptFunctions::Function func = GDScriptParser::get_builtin_function(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
- gen->write_call_builtin(result, func, arguments);
+ } else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && Variant::has_utility_function(call->function_name)) {
+ // Variant utility function.
+ gen->write_call_utility(result, call->function_name, arguments);
+ } else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptUtilityFunctions::function_exists(call->function_name)) {
+ // GDScript utility function.
+ gen->write_call_gdscript_utility(result, GDScriptUtilityFunctions::get_function(call->function_name), arguments);
} else {
// Regular function.
const GDScriptParser::ExpressionNode *callee = call->callee;
@@ -677,7 +683,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
return GDScriptCodeGenerator::Address();
}
- gen->write_operator(result, unary->variant_op, operand, GDScriptCodeGenerator::Address());
+ gen->write_unary_operator(result, unary->variant_op, operand);
if (operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
gen->pop_temporary();
@@ -745,7 +751,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
GDScriptCodeGenerator::Address left_operand = _parse_expression(codegen, r_error, binary->left_operand);
GDScriptCodeGenerator::Address right_operand = _parse_expression(codegen, r_error, binary->right_operand);
- gen->write_operator(result, binary->variant_op, left_operand, right_operand);
+ gen->write_binary_operator(result, binary->variant_op, left_operand, right_operand);
if (right_operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
gen->pop_temporary();
@@ -908,7 +914,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
} else {
gen->write_get(value, key, prev_base);
}
- gen->write_operator(value, assignment->variant_op, value, assigned);
+ gen->write_binary_operator(value, assignment->variant_op, value, assigned);
if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
gen->pop_temporary();
}
@@ -966,7 +972,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
GDScriptCodeGenerator::Address member = codegen.add_temporary();
gen->write_get_member(member, name);
- gen->write_operator(assigned, assignment->variant_op, member, assigned);
+ gen->write_binary_operator(assigned, assignment->variant_op, member, assigned);
gen->pop_temporary();
}
@@ -1016,7 +1022,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
// Perform operation.
op_result = codegen.add_temporary();
- gen->write_operator(op_result, assignment->variant_op, target, assigned);
+ gen->write_binary_operator(op_result, assignment->variant_op, target, assigned);
} else {
op_result = assigned;
assigned = GDScriptCodeGenerator::Address();
@@ -1072,7 +1078,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Check type equality.
GDScriptCodeGenerator::Address type_equality_addr = codegen.add_temporary(equality_type);
- codegen.generator->write_operator(type_equality_addr, Variant::OP_EQUAL, p_type_addr, literal_type_addr);
+ codegen.generator->write_binary_operator(type_equality_addr, Variant::OP_EQUAL, p_type_addr, literal_type_addr);
codegen.generator->write_and_left_operand(type_equality_addr);
// Get literal.
@@ -1083,7 +1089,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Check value equality.
GDScriptCodeGenerator::Address equality_addr = codegen.add_temporary(equality_type);
- codegen.generator->write_operator(equality_addr, Variant::OP_EQUAL, p_value_addr, literal_addr);
+ codegen.generator->write_binary_operator(equality_addr, Variant::OP_EQUAL, p_value_addr, literal_addr);
codegen.generator->write_and_right_operand(equality_addr);
// AND both together (reuse temporary location).
@@ -1132,14 +1138,14 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Evaluate expression type.
Vector<GDScriptCodeGenerator::Address> typeof_args;
typeof_args.push_back(expr_addr);
- codegen.generator->write_call_builtin(result_addr, GDScriptFunctions::TYPE_OF, typeof_args);
+ codegen.generator->write_call_utility(result_addr, "typeof", typeof_args);
// Check type equality.
- codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_type_addr, result_addr);
+ codegen.generator->write_binary_operator(result_addr, Variant::OP_EQUAL, p_type_addr, result_addr);
codegen.generator->write_and_left_operand(result_addr);
// Check value equality.
- codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_value_addr, expr_addr);
+ codegen.generator->write_binary_operator(result_addr, Variant::OP_EQUAL, p_value_addr, expr_addr);
codegen.generator->write_and_right_operand(equality_test_addr);
// AND both type and value equality.
@@ -1185,7 +1191,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Check type equality.
GDScriptCodeGenerator::Address result_addr = codegen.add_temporary(temp_type);
- codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_type_addr, array_type_addr);
+ codegen.generator->write_binary_operator(result_addr, Variant::OP_EQUAL, p_type_addr, array_type_addr);
codegen.generator->write_and_left_operand(result_addr);
// Store pattern length in constant map.
@@ -1196,12 +1202,12 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
GDScriptCodeGenerator::Address value_length_addr = codegen.add_temporary(temp_type);
Vector<GDScriptCodeGenerator::Address> len_args;
len_args.push_back(p_value_addr);
- codegen.generator->write_call_builtin(value_length_addr, GDScriptFunctions::LEN, len_args);
+ codegen.generator->write_call_gdscript_utility(value_length_addr, GDScriptUtilityFunctions::get_function("len"), len_args);
// Test length compatibility.
temp_type.builtin_type = Variant::BOOL;
GDScriptCodeGenerator::Address length_compat_addr = codegen.add_temporary(temp_type);
- codegen.generator->write_operator(length_compat_addr, p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL, value_length_addr, array_length_addr);
+ codegen.generator->write_binary_operator(length_compat_addr, p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL, value_length_addr, array_length_addr);
codegen.generator->write_and_right_operand(length_compat_addr);
// AND type and length check.
@@ -1250,7 +1256,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Also get type of element.
Vector<GDScriptCodeGenerator::Address> typeof_args;
typeof_args.push_back(element_addr);
- codegen.generator->write_call_builtin(element_type_addr, GDScriptFunctions::TYPE_OF, typeof_args);
+ codegen.generator->write_call_utility(element_type_addr, "typeof", typeof_args);
// Try the pattern inside the element.
test_addr = _parse_match_pattern(codegen, r_error, p_pattern->array[i], element_addr, element_type_addr, p_previous_test, false, true);
@@ -1284,7 +1290,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Check type equality.
GDScriptCodeGenerator::Address result_addr = codegen.add_temporary(temp_type);
- codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_type_addr, dict_type_addr);
+ codegen.generator->write_binary_operator(result_addr, Variant::OP_EQUAL, p_type_addr, dict_type_addr);
codegen.generator->write_and_left_operand(result_addr);
// Store pattern length in constant map.
@@ -1295,12 +1301,12 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
GDScriptCodeGenerator::Address value_length_addr = codegen.add_temporary(temp_type);
Vector<GDScriptCodeGenerator::Address> func_args;
func_args.push_back(p_value_addr);
- codegen.generator->write_call_builtin(value_length_addr, GDScriptFunctions::LEN, func_args);
+ codegen.generator->write_call_gdscript_utility(value_length_addr, GDScriptUtilityFunctions::get_function("len"), func_args);
// Test length compatibility.
temp_type.builtin_type = Variant::BOOL;
GDScriptCodeGenerator::Address length_compat_addr = codegen.add_temporary(temp_type);
- codegen.generator->write_operator(length_compat_addr, p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL, value_length_addr, dict_length_addr);
+ codegen.generator->write_binary_operator(length_compat_addr, p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL, value_length_addr, dict_length_addr);
codegen.generator->write_and_right_operand(length_compat_addr);
// AND type and length check.
@@ -1364,7 +1370,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Also get type of value.
func_args.clear();
func_args.push_back(element_addr);
- codegen.generator->write_call_builtin(element_type_addr, GDScriptFunctions::TYPE_OF, func_args);
+ codegen.generator->write_call_utility(element_type_addr, "typeof", func_args);
// Try the pattern inside the value.
test_addr = _parse_match_pattern(codegen, r_error, element.value_pattern, element_addr, element_type_addr, test_addr, false, true);
@@ -1497,7 +1503,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
Vector<GDScriptCodeGenerator::Address> typeof_args;
typeof_args.push_back(value);
- gen->write_call_builtin(type, GDScriptFunctions::TYPE_OF, typeof_args);
+ gen->write_call_utility(type, "typeof", typeof_args);
// Now we can actually start testing.
// For each branch.
@@ -1838,7 +1844,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
return error;
}
GDScriptCodeGenerator::Address dst_addr = codegen.parameters[parameter->identifier->name];
- codegen.generator->write_assign(dst_addr, src_addr);
+ codegen.generator->write_assign_default_parameter(dst_addr, src_addr);
if (src_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
}
@@ -1883,6 +1889,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
codegen.generator->set_initial_line(p_func->start_line);
#ifdef TOOLS_ENABLED
p_script->member_lines[func_name] = p_func->start_line;
+ p_script->doc_functions[func_name] = p_func->doc_description;
#endif
} else {
codegen.generator->set_initial_line(0);
@@ -1896,6 +1903,21 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
p_script->implicit_initializer = gd_function;
}
+ if (p_func) {
+ // if no return statement -> return type is void not unresolved Variant
+ if (p_func->body->has_return) {
+ gd_function->return_type = _gdtype_from_datatype(p_func->get_datatype());
+ } else {
+ gd_function->return_type = GDScriptDataType();
+ gd_function->return_type.has_type = true;
+ gd_function->return_type.kind = GDScriptDataType::BUILTIN;
+ gd_function->return_type.builtin_type = Variant::NIL;
+ }
+#ifdef TOOLS_ENABLED
+ gd_function->default_arg_values = p_func->default_arg_values;
+#endif
+ }
+
p_script->member_functions[func_name] = gd_function;
memdelete(codegen.generator);
@@ -1993,6 +2015,24 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
}
}
+#ifdef TOOLS_ENABLED
+ p_script->doc_functions.clear();
+ p_script->doc_variables.clear();
+ p_script->doc_constants.clear();
+ p_script->doc_enums.clear();
+ p_script->doc_signals.clear();
+ p_script->doc_tutorials.clear();
+
+ p_script->doc_brief_description = p_class->doc_brief_description;
+ p_script->doc_description = p_class->doc_description;
+ for (int i = 0; i < p_class->doc_tutorials.size(); i++) {
+ DocData::TutorialDoc td;
+ td.title = p_class->doc_tutorials[i].first;
+ td.link = p_class->doc_tutorials[i].second;
+ p_script->doc_tutorials.append(td);
+ }
+#endif
+
p_script->native = Ref<GDScriptNativeClass>();
p_script->base = Ref<GDScript>();
p_script->_base = nullptr;
@@ -2105,20 +2145,23 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
prop_info.hint = export_info.hint;
prop_info.hint_string = export_info.hint_string;
prop_info.usage = export_info.usage;
-#ifdef TOOLS_ENABLED
- if (variable->initializer != nullptr && variable->initializer->type == GDScriptParser::Node::LITERAL) {
- p_script->member_default_values[name] = static_cast<const GDScriptParser::LiteralNode *>(variable->initializer)->value;
- }
-#endif
} else {
prop_info.usage = PROPERTY_USAGE_SCRIPT_VARIABLE;
}
+#ifdef TOOLS_ENABLED
+ p_script->doc_variables[name] = variable->doc_description;
+#endif
p_script->member_info[name] = prop_info;
p_script->member_indices[name] = minfo;
p_script->members.insert(name);
#ifdef TOOLS_ENABLED
+ if (variable->initializer != nullptr && variable->initializer->is_constant) {
+ p_script->member_default_values[name] = variable->initializer->reduced_value;
+ } else {
+ p_script->member_default_values.erase(name);
+ }
p_script->member_lines[name] = variable->start_line;
#endif
} break;
@@ -2129,8 +2172,10 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
p_script->constants.insert(name, constant->initializer->reduced_value);
#ifdef TOOLS_ENABLED
-
p_script->member_lines[name] = constant->start_line;
+ if (constant->doc_description != String()) {
+ p_script->doc_constants[name] = constant->doc_description;
+ }
#endif
} break;
@@ -2141,6 +2186,15 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
p_script->constants.insert(name, enum_value.value);
#ifdef TOOLS_ENABLED
p_script->member_lines[name] = enum_value.identifier->start_line;
+ if (!p_script->doc_enums.has("@unnamed_enums")) {
+ p_script->doc_enums["@unnamed_enums"] = DocData::EnumDoc();
+ p_script->doc_enums["@unnamed_enums"].name = "@unnamed_enums";
+ }
+ DocData::ConstantDoc const_doc;
+ const_doc.name = enum_value.identifier->name;
+ const_doc.value = Variant(enum_value.value).operator String(); // TODO-DOC: enum value currently is int.
+ const_doc.description = enum_value.doc_description;
+ p_script->doc_enums["@unnamed_enums"].values.push_back(const_doc);
#endif
} break;
@@ -2176,6 +2230,11 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
parameters_names.write[j] = signal->parameters[j]->identifier->name;
}
p_script->_signals[name] = parameters_names;
+#ifdef TOOLS_ENABLED
+ if (!signal->doc_description.empty()) {
+ p_script->doc_signals[name] = signal->doc_description;
+ }
+#endif
} break;
case GDScriptParser::ClassNode::Member::ENUM: {
@@ -2192,6 +2251,16 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
p_script->constants.insert(enum_n->identifier->name, new_enum);
#ifdef TOOLS_ENABLED
p_script->member_lines[enum_n->identifier->name] = enum_n->start_line;
+ p_script->doc_enums[enum_n->identifier->name] = DocData::EnumDoc();
+ p_script->doc_enums[enum_n->identifier->name].name = enum_n->identifier->name;
+ p_script->doc_enums[enum_n->identifier->name].description = enum_n->doc_description;
+ for (int j = 0; j < enum_n->values.size(); j++) {
+ DocData::ConstantDoc const_doc;
+ const_doc.name = enum_n->values[j].identifier->name;
+ const_doc.value = Variant(enum_n->values[j].value).operator String();
+ const_doc.description = enum_n->values[j].doc_description;
+ p_script->doc_enums[enum_n->identifier->name].values.push_back(const_doc);
+ }
#endif
} break;
default:
diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp
index 92a44c57f8..5938cfd7b2 100644
--- a/modules/gdscript/gdscript_disassembler.cpp
+++ b/modules/gdscript/gdscript_disassembler.cpp
@@ -34,7 +34,6 @@
#include "core/string/string_builder.h"
#include "gdscript.h"
-#include "gdscript_functions.h"
static String _get_variant_string(const Variant &p_variant) {
String txt;
@@ -324,11 +323,8 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr += 4;
} break;
case OPCODE_ASSIGN_TYPED_NATIVE: {
- Variant class_name = _constants_ptr[_code_ptr[ip + 3]];
- GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(class_name.operator Object *());
-
text += "assign typed native (";
- text += nc->get_name().operator String();
+ text += DADDR(3);
text += ") ";
text += DADDR(1);
text += " = ";
@@ -607,13 +603,49 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr = 5 + argc;
} break;
- case OPCODE_CALL_BUILT_IN: {
- text += "call-built-in ";
+ case OPCODE_CALL_UTILITY: {
+ text += "call-utility ";
+
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ text += DADDR(1 + argc) + " = ";
+
+ text += _global_names_ptr[_code_ptr[ip + 2 + instr_var_args]];
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 4 + argc;
+ } break;
+ case OPCODE_CALL_UTILITY_VALIDATED: {
+ text += "call-utility ";
+
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ text += DADDR(1 + argc) + " = ";
+
+ text += "<unkown function>";
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 4 + argc;
+ } break;
+ case OPCODE_CALL_GDSCRIPT_UTILITY: {
+ text += "call-gscript-utility ";
int argc = _code_ptr[ip + 1 + instr_var_args];
text += DADDR(1 + argc) + " = ";
- text += GDScriptFunctions::get_func_name(GDScriptFunctions::Function(_code_ptr[ip + 2 + instr_var_args]));
+ text += "<unknown function>";
text += "(";
for (int i = 0; i < argc; i++) {
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index a426046797..2181d17cf0 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -37,6 +37,7 @@
#include "gdscript_compiler.h"
#include "gdscript_parser.h"
#include "gdscript_tokenizer.h"
+#include "gdscript_utility_functions.h"
#ifdef TOOLS_ENABLED
#include "core/config/project_settings.h"
@@ -193,6 +194,10 @@ bool GDScriptLanguage::supports_builtin_mode() const {
return true;
}
+bool GDScriptLanguage::supports_documentation() const {
+ return true;
+}
+
int GDScriptLanguage::find_function(const String &p_function, const String &p_code) const {
GDScriptTokenizer tokenizer;
tokenizer.set_source_code(p_code);
@@ -407,11 +412,14 @@ void GDScriptLanguage::get_recognized_extensions(List<String> *p_extensions) con
}
void GDScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const {
- for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
- p_functions->push_back(GDScriptFunctions::get_info(GDScriptFunctions::Function(i)));
+ List<StringName> functions;
+ GDScriptUtilityFunctions::get_function_list(&functions);
+
+ for (const List<StringName>::Element *E = functions.front(); E; E = E->next()) {
+ p_functions->push_back(GDScriptUtilityFunctions::get_function_info(E->get()));
}
- //not really "functions", but..
+ // Not really "functions", but show in documentation.
{
MethodInfo mi;
mi.name = "preload";
@@ -1026,9 +1034,12 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool
_find_identifiers_in_class(p_context.current_class, p_only_functions, (!p_context.current_function || p_context.current_function->is_static), false, r_result, p_recursion_depth + 1);
}
- for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
- MethodInfo function = GDScriptFunctions::get_info(GDScriptFunctions::Function(i));
- ScriptCodeCompletionOption option(String(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))), ScriptCodeCompletionOption::KIND_FUNCTION);
+ List<StringName> functions;
+ GDScriptUtilityFunctions::get_function_list(&functions);
+
+ for (const List<StringName>::Element *E = functions.front(); E; E = E->next()) {
+ MethodInfo function = GDScriptUtilityFunctions::get_function_info(E->get());
+ ScriptCodeCompletionOption option(String(E->get()), ScriptCodeCompletionOption::KIND_FUNCTION);
if (function.arguments.size() || (function.flags & METHOD_FLAG_VARARG)) {
option.insert_text += "(";
} else {
@@ -1054,10 +1065,8 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool
}
static const char *_keywords[] = {
- "and", "in", "not", "or", "false", "PI", "TAU", "INF", "NAN", "self", "true", "as", "assert",
- "breakpoint", "class", "extends", "is", "func", "preload", "signal", "tool", "await",
- "const", "enum", "static", "super", "var", "break", "continue", "if", "elif",
- "else", "for", "pass", "return", "match", "while",
+ "false", "PI", "TAU", "INF", "NAN", "self", "true", "breakpoint", "tool", "super",
+ "break", "continue", "pass", "return",
0
};
@@ -1068,6 +1077,33 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool
kw++;
}
+ static const char *_keywords_with_space[] = {
+ "and", "in", "not", "or", "as", "class", "extends", "is", "func", "signal", "await",
+ "const", "enum", "static", "var", "if", "elif", "else", "for", "match", "while",
+ 0
+ };
+
+ const char **kws = _keywords_with_space;
+ while (*kws) {
+ ScriptCodeCompletionOption option(*kws, ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
+ option.insert_text += " ";
+ r_result.insert(option.display, option);
+ kws++;
+ }
+
+ static const char *_keywords_with_args[] = {
+ "assert", "preload",
+ 0
+ };
+
+ const char **kwa = _keywords_with_args;
+ while (*kwa) {
+ ScriptCodeCompletionOption option(*kwa, ScriptCodeCompletionOption::KIND_FUNCTION);
+ option.insert_text += "(";
+ r_result.insert(option.display, option);
+ kwa++;
+ }
+
Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
for (const Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E != nullptr; E = E->next()) {
if (!E->value().is_singleton) {
@@ -1259,8 +1295,8 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
r_type.type.builtin_type = GDScriptParser::get_builtin_type(call->function_name);
found = true;
break;
- } else if (GDScriptParser::get_builtin_function(call->function_name) < GDScriptFunctions::FUNC_MAX) {
- MethodInfo mi = GDScriptFunctions::get_info(GDScriptParser::get_builtin_function(call->function_name));
+ } else if (GDScriptUtilityFunctions::function_exists(call->function_name)) {
+ MethodInfo mi = GDScriptUtilityFunctions::get_function_info(call->function_name);
r_type = _type_from_property(mi.return_val);
found = true;
break;
@@ -2313,8 +2349,8 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
GDScriptCompletionIdentifier connect_base;
- if (GDScriptParser::get_builtin_function(call->function_name) < GDScriptFunctions::FUNC_MAX) {
- MethodInfo info = GDScriptFunctions::get_info(GDScriptParser::get_builtin_function(call->function_name));
+ if (GDScriptUtilityFunctions::function_exists(call->function_name)) {
+ MethodInfo info = GDScriptUtilityFunctions::get_function_info(call->function_name);
r_arghint = _make_arguments_hint(info, p_argidx);
return;
} else if (GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) {
@@ -2942,13 +2978,11 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
}
}
- for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
- if (GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i)) == p_symbol) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
- r_result.class_name = "@GDScript";
- r_result.class_member = p_symbol;
- return OK;
- }
+ if (GDScriptUtilityFunctions::function_exists(p_symbol)) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
+ r_result.class_name = "@GDScript";
+ r_result.class_member = p_symbol;
+ return OK;
}
if ("PI" == p_symbol || "TAU" == p_symbol || "INF" == p_symbol || "NAN" == p_symbol) {
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index bb5cc1284d..b669870b51 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -38,6 +38,7 @@
#include "core/templates/pair.h"
#include "core/templates/self_list.h"
#include "core/variant/variant.h"
+#include "gdscript_utility_functions.h"
class GDScriptInstance;
class GDScript;
@@ -190,7 +191,9 @@ public:
OPCODE_CALL,
OPCODE_CALL_RETURN,
OPCODE_CALL_ASYNC,
- OPCODE_CALL_BUILT_IN,
+ OPCODE_CALL_UTILITY,
+ OPCODE_CALL_UTILITY_VALIDATED,
+ OPCODE_CALL_GDSCRIPT_UTILITY,
OPCODE_CALL_BUILTIN_TYPE_VALIDATED,
OPCODE_CALL_SELF_BASE,
OPCODE_CALL_METHOD_BIND,
@@ -344,6 +347,10 @@ private:
const Variant::ValidatedBuiltInMethod *_builtin_methods_ptr = nullptr;
int _constructors_count = 0;
const Variant::ValidatedConstructor *_constructors_ptr = nullptr;
+ int _utilities_count = 0;
+ const Variant::ValidatedUtilityFunction *_utilities_ptr = nullptr;
+ int _gds_utilities_count = 0;
+ const GDScriptUtilityFunctions::FunctionPtr *_gds_utilities_ptr = nullptr;
int _methods_count = 0;
MethodBind **_methods_ptr = nullptr;
const int *_code_ptr = nullptr;
@@ -372,6 +379,8 @@ private:
Vector<Variant::ValidatedIndexedGetter> indexed_getters;
Vector<Variant::ValidatedBuiltInMethod> builtin_methods;
Vector<Variant::ValidatedConstructor> constructors;
+ Vector<Variant::ValidatedUtilityFunction> utilities;
+ Vector<GDScriptUtilityFunctions::FunctionPtr> gds_utilities;
Vector<MethodBind *> methods;
Vector<int> code;
Vector<GDScriptDataType> argument_types;
@@ -379,6 +388,7 @@ private:
#ifdef TOOLS_ENABLED
Vector<StringName> arg_names;
+ Vector<Variant> default_arg_values;
#endif
List<StackDebug> stack_debug;
@@ -458,6 +468,11 @@ public:
ERR_FAIL_INDEX_V(p_idx, default_arguments.size(), Variant());
return default_arguments[p_idx];
}
+#ifdef TOOLS_ENABLED
+ const Vector<Variant> &get_default_arg_values() const {
+ return default_arg_values;
+ }
+#endif // TOOLS_ENABLED
Variant call(GDScriptInstance *p_instance, const Variant **p_args, int p_argcount, Callable::CallError &r_err, CallState *p_state = nullptr);
diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp
deleted file mode 100644
index 3a7c1a8676..0000000000
--- a/modules/gdscript/gdscript_functions.cpp
+++ /dev/null
@@ -1,1942 +0,0 @@
-/*************************************************************************/
-/* gdscript_functions.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "gdscript_functions.h"
-
-#include "core/io/json.h"
-#include "core/io/marshalls.h"
-#include "core/math/math_funcs.h"
-#include "core/object/class_db.h"
-#include "core/object/reference.h"
-#include "core/os/os.h"
-#include "core/variant/variant_parser.h"
-#include "gdscript.h"
-
-const char *GDScriptFunctions::get_func_name(Function p_func) {
- ERR_FAIL_INDEX_V(p_func, FUNC_MAX, "");
-
- static const char *_names[FUNC_MAX] = {
- "sin",
- "cos",
- "tan",
- "sinh",
- "cosh",
- "tanh",
- "asin",
- "acos",
- "atan",
- "atan2",
- "sqrt",
- "fmod",
- "fposmod",
- "posmod",
- "floor",
- "ceil",
- "round",
- "abs",
- "sign",
- "pow",
- "log",
- "exp",
- "is_nan",
- "is_inf",
- "is_equal_approx",
- "is_zero_approx",
- "ease",
- "step_decimals",
- "stepify",
- "lerp",
- "lerp_angle",
- "inverse_lerp",
- "range_lerp",
- "smoothstep",
- "move_toward",
- "dectime",
- "randomize",
- "randi",
- "randf",
- "randf_range",
- "randi_range",
- "seed",
- "rand_seed",
- "deg2rad",
- "rad2deg",
- "linear2db",
- "db2linear",
- "polar2cartesian",
- "cartesian2polar",
- "wrapi",
- "wrapf",
- "max",
- "min",
- "clamp",
- "nearest_po2",
- "weakref",
- "convert",
- "typeof",
- "type_exists",
- "char",
- "ord",
- "str",
- "print",
- "printt",
- "prints",
- "printerr",
- "printraw",
- "print_debug",
- "push_error",
- "push_warning",
- "var2str",
- "str2var",
- "var2bytes",
- "bytes2var",
- "range",
- "load",
- "inst2dict",
- "dict2inst",
- "validate_json",
- "parse_json",
- "to_json",
- "hash",
- "Color8",
- "ColorN",
- "print_stack",
- "get_stack",
- "instance_from_id",
- "len",
- "is_instance_valid",
- };
-
- return _names[p_func];
-}
-
-void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_count, Variant &r_ret, Callable::CallError &r_error) {
- r_error.error = Callable::CallError::CALL_OK;
-#ifdef DEBUG_ENABLED
-
-#define VALIDATE_ARG_COUNT(m_count) \
- if (p_arg_count < m_count) { \
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; \
- r_error.argument = m_count; \
- r_error.expected = m_count; \
- r_ret = Variant(); \
- return; \
- } \
- if (p_arg_count > m_count) { \
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; \
- r_error.argument = m_count; \
- r_error.expected = m_count; \
- r_ret = Variant(); \
- return; \
- }
-
-#define VALIDATE_ARG_NUM(m_arg) \
- if (!p_args[m_arg]->is_num()) { \
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
- r_error.argument = m_arg; \
- r_error.expected = Variant::FLOAT; \
- r_ret = Variant(); \
- return; \
- }
-
-#else
-
-#define VALIDATE_ARG_COUNT(m_count)
-#define VALIDATE_ARG_NUM(m_arg)
-#endif
-
- //using a switch, so the compiler generates a jumptable
-
- switch (p_func) {
- case MATH_SIN: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::sin((double)*p_args[0]);
- } break;
- case MATH_COS: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::cos((double)*p_args[0]);
- } break;
- case MATH_TAN: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::tan((double)*p_args[0]);
- } break;
- case MATH_SINH: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::sinh((double)*p_args[0]);
- } break;
- case MATH_COSH: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::cosh((double)*p_args[0]);
- } break;
- case MATH_TANH: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::tanh((double)*p_args[0]);
- } break;
- case MATH_ASIN: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::asin((double)*p_args[0]);
- } break;
- case MATH_ACOS: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::acos((double)*p_args[0]);
- } break;
- case MATH_ATAN: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::atan((double)*p_args[0]);
- } break;
- case MATH_ATAN2: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::atan2((double)*p_args[0], (double)*p_args[1]);
- } break;
- case MATH_SQRT: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::sqrt((double)*p_args[0]);
- } break;
- case MATH_FMOD: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::fmod((double)*p_args[0], (double)*p_args[1]);
- } break;
- case MATH_FPOSMOD: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::fposmod((double)*p_args[0], (double)*p_args[1]);
- } break;
- case MATH_POSMOD: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::posmod((int)*p_args[0], (int)*p_args[1]);
- } break;
- case MATH_FLOOR: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::floor((double)*p_args[0]);
- } break;
- case MATH_CEIL: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::ceil((double)*p_args[0]);
- } break;
- case MATH_ROUND: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::round((double)*p_args[0]);
- } break;
- case MATH_ABS: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() == Variant::INT) {
- int64_t i = *p_args[0];
- r_ret = ABS(i);
- } else if (p_args[0]->get_type() == Variant::FLOAT) {
- double r = *p_args[0];
- r_ret = Math::abs(r);
- } else {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::FLOAT;
- r_ret = Variant();
- }
- } break;
- case MATH_SIGN: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() == Variant::INT) {
- int64_t i = *p_args[0];
- r_ret = i < 0 ? -1 : (i > 0 ? +1 : 0);
- } else if (p_args[0]->get_type() == Variant::FLOAT) {
- double r = *p_args[0];
- r_ret = r < 0.0 ? -1.0 : (r > 0.0 ? +1.0 : 0.0);
- } else {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::FLOAT;
- r_ret = Variant();
- }
- } break;
- case MATH_POW: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::pow((double)*p_args[0], (double)*p_args[1]);
- } break;
- case MATH_LOG: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::log((double)*p_args[0]);
- } break;
- case MATH_EXP: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::exp((double)*p_args[0]);
- } break;
- case MATH_ISNAN: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::is_nan((double)*p_args[0]);
- } break;
- case MATH_ISINF: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::is_inf((double)*p_args[0]);
- } break;
- case MATH_ISEQUALAPPROX: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::is_equal_approx((real_t)*p_args[0], (real_t)*p_args[1]);
- } break;
- case MATH_ISZEROAPPROX: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::is_zero_approx((real_t)*p_args[0]);
- } break;
- case MATH_EASE: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::ease((double)*p_args[0], (double)*p_args[1]);
- } break;
- case MATH_STEP_DECIMALS: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::step_decimals((double)*p_args[0]);
- } break;
- case MATH_STEPIFY: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::stepify((double)*p_args[0], (double)*p_args[1]);
- } break;
- case MATH_LERP: {
- VALIDATE_ARG_COUNT(3);
- VALIDATE_ARG_NUM(2);
- const double t = (double)*p_args[2];
- switch (p_args[0]->get_type() == p_args[1]->get_type() ? p_args[0]->get_type() : Variant::FLOAT) {
- case Variant::VECTOR2: {
- r_ret = ((Vector2)*p_args[0]).lerp((Vector2)*p_args[1], t);
- } break;
- case Variant::VECTOR3: {
- r_ret = (p_args[0]->operator Vector3()).lerp(p_args[1]->operator Vector3(), t);
- } break;
- case Variant::COLOR: {
- r_ret = ((Color)*p_args[0]).lerp((Color)*p_args[1], t);
- } break;
- default: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::lerp((double)*p_args[0], (double)*p_args[1], t);
- } break;
- }
- } break;
- case MATH_LERP_ANGLE: {
- VALIDATE_ARG_COUNT(3);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- r_ret = Math::lerp_angle((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
- } break;
- case MATH_INVERSE_LERP: {
- VALIDATE_ARG_COUNT(3);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- r_ret = Math::inverse_lerp((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
- } break;
- case MATH_RANGE_LERP: {
- VALIDATE_ARG_COUNT(5);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- VALIDATE_ARG_NUM(3);
- VALIDATE_ARG_NUM(4);
- r_ret = Math::range_lerp((double)*p_args[0], (double)*p_args[1], (double)*p_args[2], (double)*p_args[3], (double)*p_args[4]);
- } break;
- case MATH_SMOOTHSTEP: {
- VALIDATE_ARG_COUNT(3);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- r_ret = Math::smoothstep((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
- } break;
- case MATH_MOVE_TOWARD: {
- VALIDATE_ARG_COUNT(3);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- r_ret = Math::move_toward((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
- } break;
- case MATH_DECTIME: {
- VALIDATE_ARG_COUNT(3);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- r_ret = Math::dectime((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
- } break;
- case MATH_RANDOMIZE: {
- VALIDATE_ARG_COUNT(0);
- Math::randomize();
- r_ret = Variant();
- } break;
- case MATH_RANDI: {
- VALIDATE_ARG_COUNT(0);
- r_ret = Math::rand();
- } break;
- case MATH_RANDF: {
- VALIDATE_ARG_COUNT(0);
- r_ret = Math::randf();
- } break;
- case MATH_RANDF_RANGE: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::random((double)*p_args[0], (double)*p_args[1]);
- } break;
- case MATH_RANDI_RANGE: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::random((int)*p_args[0], (int)*p_args[1]);
- } break;
- case MATH_SEED: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- uint64_t seed = *p_args[0];
- Math::seed(seed);
- r_ret = Variant();
- } break;
- case MATH_RANDSEED: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- uint64_t seed = *p_args[0];
- int ret = Math::rand_from_seed(&seed);
- Array reta;
- reta.push_back(ret);
- reta.push_back(seed);
- r_ret = reta;
-
- } break;
- case MATH_DEG2RAD: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::deg2rad((double)*p_args[0]);
- } break;
- case MATH_RAD2DEG: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::rad2deg((double)*p_args[0]);
- } break;
- case MATH_LINEAR2DB: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::linear2db((double)*p_args[0]);
- } break;
- case MATH_DB2LINEAR: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::db2linear((double)*p_args[0]);
- } break;
- case MATH_POLAR2CARTESIAN: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- double r = *p_args[0];
- double th = *p_args[1];
- r_ret = Vector2(r * Math::cos(th), r * Math::sin(th));
- } break;
- case MATH_CARTESIAN2POLAR: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- double x = *p_args[0];
- double y = *p_args[1];
- r_ret = Vector2(Math::sqrt(x * x + y * y), Math::atan2(y, x));
- } break;
- case MATH_WRAP: {
- VALIDATE_ARG_COUNT(3);
- r_ret = Math::wrapi((int64_t)*p_args[0], (int64_t)*p_args[1], (int64_t)*p_args[2]);
- } break;
- case MATH_WRAPF: {
- VALIDATE_ARG_COUNT(3);
- r_ret = Math::wrapf((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
- } break;
- case LOGIC_MAX: {
- VALIDATE_ARG_COUNT(2);
- if (p_args[0]->get_type() == Variant::INT && p_args[1]->get_type() == Variant::INT) {
- int64_t a = *p_args[0];
- int64_t b = *p_args[1];
- r_ret = MAX(a, b);
- } else {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
-
- double a = *p_args[0];
- double b = *p_args[1];
-
- r_ret = MAX(a, b);
- }
-
- } break;
- case LOGIC_MIN: {
- VALIDATE_ARG_COUNT(2);
- if (p_args[0]->get_type() == Variant::INT && p_args[1]->get_type() == Variant::INT) {
- int64_t a = *p_args[0];
- int64_t b = *p_args[1];
- r_ret = MIN(a, b);
- } else {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
-
- double a = *p_args[0];
- double b = *p_args[1];
-
- r_ret = MIN(a, b);
- }
- } break;
- case LOGIC_CLAMP: {
- VALIDATE_ARG_COUNT(3);
- if (p_args[0]->get_type() == Variant::INT && p_args[1]->get_type() == Variant::INT && p_args[2]->get_type() == Variant::INT) {
- int64_t a = *p_args[0];
- int64_t b = *p_args[1];
- int64_t c = *p_args[2];
- r_ret = CLAMP(a, b, c);
- } else {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
-
- double a = *p_args[0];
- double b = *p_args[1];
- double c = *p_args[2];
-
- r_ret = CLAMP(a, b, c);
- }
- } break;
- case LOGIC_NEAREST_PO2: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- int64_t num = *p_args[0];
- r_ret = next_power_of_2(num);
- } break;
- case OBJ_WEAKREF: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() == Variant::OBJECT) {
- if (p_args[0]->is_ref()) {
- Ref<WeakRef> wref = memnew(WeakRef);
- REF r = *p_args[0];
- if (r.is_valid()) {
- wref->set_ref(r);
- }
- r_ret = wref;
- } else {
- Ref<WeakRef> wref = memnew(WeakRef);
- Object *obj = *p_args[0];
- if (obj) {
- wref->set_obj(obj);
- }
- r_ret = wref;
- }
- } else if (p_args[0]->get_type() == Variant::NIL) {
- Ref<WeakRef> wref = memnew(WeakRef);
- r_ret = wref;
- } else {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- r_ret = Variant();
- return;
- }
- } break;
- case TYPE_CONVERT: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(1);
- int type = *p_args[1];
- if (type < 0 || type >= Variant::VARIANT_MAX) {
- r_ret = RTR("Invalid type argument to convert(), use TYPE_* constants.");
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::INT;
- return;
-
- } else {
- Variant::construct(Variant::Type(type), r_ret, p_args, 1, r_error);
- }
- } break;
- case TYPE_OF: {
- VALIDATE_ARG_COUNT(1);
- r_ret = p_args[0]->get_type();
-
- } break;
- case TYPE_EXISTS: {
- VALIDATE_ARG_COUNT(1);
- r_ret = ClassDB::class_exists(*p_args[0]);
-
- } break;
- case TEXT_CHAR: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- char32_t result[2] = { *p_args[0], 0 };
- r_ret = String(result);
- } break;
- case TEXT_ORD: {
- VALIDATE_ARG_COUNT(1);
-
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- return;
- }
-
- String str = p_args[0]->operator String();
-
- if (str.length() != 1) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = RTR("Expected a string of length 1 (a character).");
- return;
- }
-
- r_ret = str.get(0);
-
- } break;
- case TEXT_STR: {
- if (p_arg_count < 1) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
- r_ret = Variant();
-
- return;
- }
- String str;
- for (int i = 0; i < p_arg_count; i++) {
- String os = p_args[i]->operator String();
-
- if (i == 0) {
- str = os;
- } else {
- str += os;
- }
- }
-
- r_ret = str;
-
- } break;
- case TEXT_PRINT: {
- String str;
- for (int i = 0; i < p_arg_count; i++) {
- str += p_args[i]->operator String();
- }
-
- print_line(str);
- r_ret = Variant();
-
- } break;
- case TEXT_PRINT_TABBED: {
- String str;
- for (int i = 0; i < p_arg_count; i++) {
- if (i) {
- str += "\t";
- }
- str += p_args[i]->operator String();
- }
-
- print_line(str);
- r_ret = Variant();
-
- } break;
- case TEXT_PRINT_SPACED: {
- String str;
- for (int i = 0; i < p_arg_count; i++) {
- if (i) {
- str += " ";
- }
- str += p_args[i]->operator String();
- }
-
- print_line(str);
- r_ret = Variant();
-
- } break;
-
- case TEXT_PRINTERR: {
- String str;
- for (int i = 0; i < p_arg_count; i++) {
- str += p_args[i]->operator String();
- }
-
- print_error(str);
- r_ret = Variant();
-
- } break;
- case TEXT_PRINTRAW: {
- String str;
- for (int i = 0; i < p_arg_count; i++) {
- str += p_args[i]->operator String();
- }
-
- OS::get_singleton()->print("%s", str.utf8().get_data());
- r_ret = Variant();
-
- } break;
- case TEXT_PRINT_DEBUG: {
- String str;
- for (int i = 0; i < p_arg_count; i++) {
- str += p_args[i]->operator String();
- }
-
- ScriptLanguage *script = GDScriptLanguage::get_singleton();
- if (script->debug_get_stack_level_count() > 0) {
- str += "\n At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)) + ":" + script->debug_get_stack_level_function(0) + "()";
- }
-
- print_line(str);
- r_ret = Variant();
- } break;
- case PUSH_ERROR: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- break;
- }
-
- String message = *p_args[0];
- ERR_PRINT(message);
- r_ret = Variant();
- } break;
- case PUSH_WARNING: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- break;
- }
-
- String message = *p_args[0];
- WARN_PRINT(message);
- r_ret = Variant();
- } break;
- case VAR_TO_STR: {
- VALIDATE_ARG_COUNT(1);
- String vars;
- VariantWriter::write_to_string(*p_args[0], vars);
- r_ret = vars;
- } break;
- case STR_TO_VAR: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- return;
- }
- r_ret = *p_args[0];
-
- VariantParser::StreamString ss;
- ss.s = *p_args[0];
-
- String errs;
- int line;
- (void)VariantParser::parse(&ss, r_ret, errs, line);
- } break;
- case VAR_TO_BYTES: {
- bool full_objects = false;
- if (p_arg_count < 1) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
- r_ret = Variant();
- return;
- } else if (p_arg_count > 2) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = 2;
- r_ret = Variant();
- } else if (p_arg_count == 2) {
- if (p_args[1]->get_type() != Variant::BOOL) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- r_error.expected = Variant::BOOL;
- r_ret = Variant();
- return;
- }
- full_objects = *p_args[1];
- }
-
- PackedByteArray barr;
- int len;
- Error err = encode_variant(*p_args[0], nullptr, len, full_objects);
- if (err) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::NIL;
- r_ret = "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID).";
- return;
- }
-
- barr.resize(len);
- {
- uint8_t *w = barr.ptrw();
- encode_variant(*p_args[0], w, len, full_objects);
- }
- r_ret = barr;
- } break;
- case BYTES_TO_VAR: {
- bool allow_objects = false;
- if (p_arg_count < 1) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
- r_ret = Variant();
- return;
- } else if (p_arg_count > 2) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = 2;
- r_ret = Variant();
- } else if (p_arg_count == 2) {
- if (p_args[1]->get_type() != Variant::BOOL) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- r_error.expected = Variant::BOOL;
- r_ret = Variant();
- return;
- }
- allow_objects = *p_args[1];
- }
-
- if (p_args[0]->get_type() != Variant::PACKED_BYTE_ARRAY) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- r_error.expected = Variant::PACKED_BYTE_ARRAY;
- r_ret = Variant();
- return;
- }
-
- PackedByteArray varr = *p_args[0];
- Variant ret;
- {
- const uint8_t *r = varr.ptr();
- Error err = decode_variant(ret, r, varr.size(), nullptr, allow_objects);
- if (err != OK) {
- r_ret = RTR("Not enough bytes for decoding bytes, or invalid format.");
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::PACKED_BYTE_ARRAY;
- return;
- }
- }
-
- r_ret = ret;
-
- } break;
- case GEN_RANGE: {
- switch (p_arg_count) {
- case 0: {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
- r_error.expected = 1;
- r_ret = Variant();
-
- } break;
- case 1: {
- VALIDATE_ARG_NUM(0);
- int count = *p_args[0];
- Array arr;
- if (count <= 0) {
- r_ret = arr;
- return;
- }
- Error err = arr.resize(count);
- if (err != OK) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_ret = Variant();
- return;
- }
-
- for (int i = 0; i < count; i++) {
- arr[i] = i;
- }
-
- r_ret = arr;
- } break;
- case 2: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
-
- int from = *p_args[0];
- int to = *p_args[1];
-
- Array arr;
- if (from >= to) {
- r_ret = arr;
- return;
- }
- Error err = arr.resize(to - from);
- if (err != OK) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_ret = Variant();
- return;
- }
- for (int i = from; i < to; i++) {
- arr[i - from] = i;
- }
- r_ret = arr;
- } break;
- case 3: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
-
- int from = *p_args[0];
- int to = *p_args[1];
- int incr = *p_args[2];
- if (incr == 0) {
- r_ret = RTR("Step argument is zero!");
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return;
- }
-
- Array arr;
- if (from >= to && incr > 0) {
- r_ret = arr;
- return;
- }
- if (from <= to && incr < 0) {
- r_ret = arr;
- return;
- }
-
- //calculate how many
- int count = 0;
- if (incr > 0) {
- count = ((to - from - 1) / incr) + 1;
- } else {
- count = ((from - to - 1) / -incr) + 1;
- }
-
- Error err = arr.resize(count);
-
- if (err != OK) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_ret = Variant();
- return;
- }
-
- if (incr > 0) {
- int idx = 0;
- for (int i = from; i < to; i += incr) {
- arr[idx++] = i;
- }
- } else {
- int idx = 0;
- for (int i = from; i > to; i += incr) {
- arr[idx++] = i;
- }
- }
-
- r_ret = arr;
- } break;
- default: {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = 3;
- r_error.expected = 3;
- r_ret = Variant();
-
- } break;
- }
-
- } break;
- case RESOURCE_LOAD: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- } else {
- r_ret = ResourceLoader::load(*p_args[0]);
- }
-
- } break;
- case INST2DICT: {
- VALIDATE_ARG_COUNT(1);
-
- if (p_args[0]->get_type() == Variant::NIL) {
- r_ret = Variant();
- } else if (p_args[0]->get_type() != Variant::OBJECT) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_ret = Variant();
- } else {
- Object *obj = *p_args[0];
- if (!obj) {
- r_ret = Variant();
-
- } else if (!obj->get_script_instance() || obj->get_script_instance()->get_language() != GDScriptLanguage::get_singleton()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::DICTIONARY;
- r_ret = RTR("Not a script with an instance");
- return;
- } else {
- GDScriptInstance *ins = static_cast<GDScriptInstance *>(obj->get_script_instance());
- Ref<GDScript> base = ins->get_script();
- if (base.is_null()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::DICTIONARY;
- r_ret = RTR("Not based on a script");
- return;
- }
-
- GDScript *p = base.ptr();
- Vector<StringName> sname;
-
- while (p->_owner) {
- sname.push_back(p->name);
- p = p->_owner;
- }
- sname.invert();
-
- if (!p->path.is_resource_file()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::DICTIONARY;
- r_ret = Variant();
-
- r_ret = RTR("Not based on a resource file");
-
- return;
- }
-
- NodePath cp(sname, Vector<StringName>(), false);
-
- Dictionary d;
- d["@subpath"] = cp;
- d["@path"] = p->get_path();
-
- for (Map<StringName, GDScript::MemberInfo>::Element *E = base->member_indices.front(); E; E = E->next()) {
- if (!d.has(E->key())) {
- d[E->key()] = ins->members[E->get().index];
- }
- }
- r_ret = d;
- }
- }
-
- } break;
- case DICT2INST: {
- VALIDATE_ARG_COUNT(1);
-
- if (p_args[0]->get_type() != Variant::DICTIONARY) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::DICTIONARY;
- r_ret = Variant();
-
- return;
- }
-
- Dictionary d = *p_args[0];
-
- if (!d.has("@path")) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- r_ret = RTR("Invalid instance dictionary format (missing @path)");
-
- return;
- }
-
- Ref<Script> scr = ResourceLoader::load(d["@path"]);
- if (!scr.is_valid()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- r_ret = RTR("Invalid instance dictionary format (can't load script at @path)");
- return;
- }
-
- Ref<GDScript> gdscr = scr;
-
- if (!gdscr.is_valid()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- r_ret = Variant();
- r_ret = RTR("Invalid instance dictionary format (invalid script at @path)");
- return;
- }
-
- NodePath sub;
- if (d.has("@subpath")) {
- sub = d["@subpath"];
- }
-
- for (int i = 0; i < sub.get_name_count(); i++) {
- gdscr = gdscr->subclasses[sub.get_name(i)];
- if (!gdscr.is_valid()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- r_ret = Variant();
- r_ret = RTR("Invalid instance dictionary (invalid subclasses)");
- return;
- }
- }
- r_ret = gdscr->_new(nullptr, -1 /*skip initializer*/, r_error);
-
- if (r_error.error != Callable::CallError::CALL_OK) {
- r_ret = Variant();
- return;
- }
-
- GDScriptInstance *ins = static_cast<GDScriptInstance *>(static_cast<Object *>(r_ret)->get_script_instance());
- Ref<GDScript> gd_ref = ins->get_script();
-
- for (Map<StringName, GDScript::MemberInfo>::Element *E = gd_ref->member_indices.front(); E; E = E->next()) {
- if (d.has(E->key())) {
- ins->members.write[E->get().index] = d[E->key()];
- }
- }
-
- } break;
- case VALIDATE_JSON: {
- VALIDATE_ARG_COUNT(1);
-
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- return;
- }
-
- String errs;
- int errl;
-
- Error err = JSON::parse(*p_args[0], r_ret, errs, errl);
-
- if (err != OK) {
- r_ret = itos(errl) + ":" + errs;
- } else {
- r_ret = "";
- }
-
- } break;
- case PARSE_JSON: {
- VALIDATE_ARG_COUNT(1);
-
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- return;
- }
-
- String errs;
- int errl;
-
- Error err = JSON::parse(*p_args[0], r_ret, errs, errl);
-
- if (err != OK) {
- r_ret = Variant();
- ERR_PRINT(vformat("Error parsing JSON at line %s: %s", errl, errs));
- }
-
- } break;
- case TO_JSON: {
- VALIDATE_ARG_COUNT(1);
-
- r_ret = JSON::print(*p_args[0]);
- } break;
- case HASH: {
- VALIDATE_ARG_COUNT(1);
- r_ret = p_args[0]->hash();
-
- } break;
- case COLOR8: {
- if (p_arg_count < 3) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 3;
- r_ret = Variant();
-
- return;
- }
- if (p_arg_count > 4) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = 4;
- r_ret = Variant();
-
- return;
- }
-
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
-
- Color color((float)*p_args[0] / 255.0f, (float)*p_args[1] / 255.0f, (float)*p_args[2] / 255.0f);
-
- if (p_arg_count == 4) {
- VALIDATE_ARG_NUM(3);
- color.a = (float)*p_args[3] / 255.0f;
- }
-
- r_ret = color;
-
- } break;
- case COLORN: {
- if (p_arg_count < 1) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
- r_ret = Variant();
- return;
- }
-
- if (p_arg_count > 2) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = 2;
- r_ret = Variant();
- return;
- }
-
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_ret = Variant();
- } else {
- Color color = Color::named(*p_args[0]);
- if (p_arg_count == 2) {
- VALIDATE_ARG_NUM(1);
- color.a = *p_args[1];
- }
- r_ret = color;
- }
-
- } break;
-
- case PRINT_STACK: {
- VALIDATE_ARG_COUNT(0);
-
- ScriptLanguage *script = GDScriptLanguage::get_singleton();
- for (int i = 0; i < script->debug_get_stack_level_count(); i++) {
- print_line("Frame " + itos(i) + " - " + script->debug_get_stack_level_source(i) + ":" + itos(script->debug_get_stack_level_line(i)) + " in function '" + script->debug_get_stack_level_function(i) + "'");
- };
- } break;
-
- case GET_STACK: {
- VALIDATE_ARG_COUNT(0);
-
- ScriptLanguage *script = GDScriptLanguage::get_singleton();
- Array ret;
- for (int i = 0; i < script->debug_get_stack_level_count(); i++) {
- Dictionary frame;
- frame["source"] = script->debug_get_stack_level_source(i);
- frame["function"] = script->debug_get_stack_level_function(i);
- frame["line"] = script->debug_get_stack_level_line(i);
- ret.push_back(frame);
- };
- r_ret = ret;
- } break;
-
- case INSTANCE_FROM_ID: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() != Variant::INT && p_args[0]->get_type() != Variant::FLOAT) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::INT;
- r_ret = Variant();
- break;
- }
-
- ObjectID id = *p_args[0];
- r_ret = ObjectDB::get_instance(id);
-
- } break;
- case LEN: {
- VALIDATE_ARG_COUNT(1);
- switch (p_args[0]->get_type()) {
- case Variant::STRING: {
- String d = *p_args[0];
- r_ret = d.length();
- } break;
- case Variant::DICTIONARY: {
- Dictionary d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::ARRAY: {
- Array d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_BYTE_ARRAY: {
- Vector<uint8_t> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_INT32_ARRAY: {
- Vector<int32_t> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_INT64_ARRAY: {
- Vector<int64_t> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_FLOAT32_ARRAY: {
- Vector<float> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_FLOAT64_ARRAY: {
- Vector<double> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_STRING_ARRAY: {
- Vector<String> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_VECTOR2_ARRAY: {
- Vector<Vector2> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_VECTOR3_ARRAY: {
- Vector<Vector3> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_COLOR_ARRAY: {
- Vector<Color> d = *p_args[0];
- r_ret = d.size();
- } break;
- default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- r_ret = Variant();
- r_ret = RTR("Object can't provide a length.");
- }
- }
-
- } break;
- case IS_INSTANCE_VALID: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() != Variant::OBJECT) {
- r_ret = false;
- } else {
- Object *obj = p_args[0]->get_validated_object();
- r_ret = obj != nullptr;
- }
-
- } break;
- case FUNC_MAX: {
- ERR_FAIL();
- } break;
- }
-}
-
-bool GDScriptFunctions::is_deterministic(Function p_func) {
- //man i couldn't have chosen a worse function name,
- //way too controversial..
-
- switch (p_func) {
- case MATH_SIN:
- case MATH_COS:
- case MATH_TAN:
- case MATH_SINH:
- case MATH_COSH:
- case MATH_TANH:
- case MATH_ASIN:
- case MATH_ACOS:
- case MATH_ATAN:
- case MATH_ATAN2:
- case MATH_SQRT:
- case MATH_FMOD:
- case MATH_FPOSMOD:
- case MATH_POSMOD:
- case MATH_FLOOR:
- case MATH_CEIL:
- case MATH_ROUND:
- case MATH_ABS:
- case MATH_SIGN:
- case MATH_POW:
- case MATH_LOG:
- case MATH_EXP:
- case MATH_ISNAN:
- case MATH_ISINF:
- case MATH_EASE:
- case MATH_STEP_DECIMALS:
- case MATH_STEPIFY:
- case MATH_LERP:
- case MATH_INVERSE_LERP:
- case MATH_RANGE_LERP:
- case MATH_SMOOTHSTEP:
- case MATH_MOVE_TOWARD:
- case MATH_DECTIME:
- case MATH_DEG2RAD:
- case MATH_RAD2DEG:
- case MATH_LINEAR2DB:
- case MATH_DB2LINEAR:
- case MATH_POLAR2CARTESIAN:
- case MATH_CARTESIAN2POLAR:
- case MATH_WRAP:
- case MATH_WRAPF:
- case LOGIC_MAX:
- case LOGIC_MIN:
- case LOGIC_CLAMP:
- case LOGIC_NEAREST_PO2:
- case TYPE_CONVERT:
- case TYPE_OF:
- case TYPE_EXISTS:
- case TEXT_CHAR:
- case TEXT_ORD:
- case TEXT_STR:
- case COLOR8:
- case LEN:
- // enable for debug only, otherwise not desirable - case GEN_RANGE:
- return true;
- default:
- return false;
- }
-
- return false;
-}
-
-MethodInfo GDScriptFunctions::get_info(Function p_func) {
-#ifdef DEBUG_ENABLED
- //using a switch, so the compiler generates a jumptable
-
- switch (p_func) {
- case MATH_SIN: {
- MethodInfo mi("sin", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
-
- } break;
- case MATH_COS: {
- MethodInfo mi("cos", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_TAN: {
- MethodInfo mi("tan", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_SINH: {
- MethodInfo mi("sinh", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_COSH: {
- MethodInfo mi("cosh", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_TANH: {
- MethodInfo mi("tanh", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_ASIN: {
- MethodInfo mi("asin", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_ACOS: {
- MethodInfo mi("acos", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_ATAN: {
- MethodInfo mi("atan", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_ATAN2: {
- MethodInfo mi("atan2", PropertyInfo(Variant::FLOAT, "y"), PropertyInfo(Variant::FLOAT, "x"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_SQRT: {
- MethodInfo mi("sqrt", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_FMOD: {
- MethodInfo mi("fmod", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_FPOSMOD: {
- MethodInfo mi("fposmod", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_POSMOD: {
- MethodInfo mi("posmod", PropertyInfo(Variant::INT, "a"), PropertyInfo(Variant::INT, "b"));
- mi.return_val.type = Variant::INT;
- return mi;
- } break;
- case MATH_FLOOR: {
- MethodInfo mi("floor", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_CEIL: {
- MethodInfo mi("ceil", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_ROUND: {
- MethodInfo mi("round", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_ABS: {
- MethodInfo mi("abs", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_SIGN: {
- MethodInfo mi("sign", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_POW: {
- MethodInfo mi("pow", PropertyInfo(Variant::FLOAT, "base"), PropertyInfo(Variant::FLOAT, "exp"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_LOG: {
- MethodInfo mi("log", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_EXP: {
- MethodInfo mi("exp", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_ISNAN: {
- MethodInfo mi("is_nan", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::BOOL;
- return mi;
- } break;
- case MATH_ISINF: {
- MethodInfo mi("is_inf", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::BOOL;
- return mi;
- } break;
- case MATH_ISEQUALAPPROX: {
- MethodInfo mi("is_equal_approx", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b"));
- mi.return_val.type = Variant::BOOL;
- return mi;
- } break;
- case MATH_ISZEROAPPROX: {
- MethodInfo mi("is_zero_approx", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::BOOL;
- return mi;
- } break;
- case MATH_EASE: {
- MethodInfo mi("ease", PropertyInfo(Variant::FLOAT, "s"), PropertyInfo(Variant::FLOAT, "curve"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_STEP_DECIMALS: {
- MethodInfo mi("step_decimals", PropertyInfo(Variant::FLOAT, "step"));
- mi.return_val.type = Variant::INT;
- return mi;
- } break;
- case MATH_STEPIFY: {
- MethodInfo mi("stepify", PropertyInfo(Variant::FLOAT, "s"), PropertyInfo(Variant::FLOAT, "step"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_LERP: {
- MethodInfo mi("lerp", PropertyInfo(Variant::NIL, "from"), PropertyInfo(Variant::NIL, "to"), PropertyInfo(Variant::FLOAT, "weight"));
- mi.return_val.type = Variant::NIL;
- mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- return mi;
- } break;
- case MATH_LERP_ANGLE: {
- MethodInfo mi("lerp_angle", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"), PropertyInfo(Variant::FLOAT, "weight"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_INVERSE_LERP: {
- MethodInfo mi("inverse_lerp", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"), PropertyInfo(Variant::FLOAT, "weight"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_RANGE_LERP: {
- MethodInfo mi("range_lerp", PropertyInfo(Variant::FLOAT, "value"), PropertyInfo(Variant::FLOAT, "istart"), PropertyInfo(Variant::FLOAT, "istop"), PropertyInfo(Variant::FLOAT, "ostart"), PropertyInfo(Variant::FLOAT, "ostop"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_SMOOTHSTEP: {
- MethodInfo mi("smoothstep", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"), PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_MOVE_TOWARD: {
- MethodInfo mi("move_toward", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"), PropertyInfo(Variant::FLOAT, "delta"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_DECTIME: {
- MethodInfo mi("dectime", PropertyInfo(Variant::FLOAT, "value"), PropertyInfo(Variant::FLOAT, "amount"), PropertyInfo(Variant::FLOAT, "step"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_RANDOMIZE: {
- MethodInfo mi("randomize");
- mi.return_val.type = Variant::NIL;
- return mi;
- } break;
- case MATH_RANDI: {
- MethodInfo mi("randi");
- mi.return_val.type = Variant::INT;
- return mi;
- } break;
- case MATH_RANDF: {
- MethodInfo mi("randf");
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_RANDF_RANGE: {
- MethodInfo mi("randf_range", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_RANDI_RANGE: {
- MethodInfo mi("randi_range", PropertyInfo(Variant::INT, "from"), PropertyInfo(Variant::INT, "to"));
- mi.return_val.type = Variant::INT;
- return mi;
- } break;
- case MATH_SEED: {
- MethodInfo mi("seed", PropertyInfo(Variant::INT, "seed"));
- mi.return_val.type = Variant::NIL;
- return mi;
- } break;
- case MATH_RANDSEED: {
- MethodInfo mi("rand_seed", PropertyInfo(Variant::INT, "seed"));
- mi.return_val.type = Variant::ARRAY;
- return mi;
- } break;
- case MATH_DEG2RAD: {
- MethodInfo mi("deg2rad", PropertyInfo(Variant::FLOAT, "deg"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_RAD2DEG: {
- MethodInfo mi("rad2deg", PropertyInfo(Variant::FLOAT, "rad"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_LINEAR2DB: {
- MethodInfo mi("linear2db", PropertyInfo(Variant::FLOAT, "nrg"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_DB2LINEAR: {
- MethodInfo mi("db2linear", PropertyInfo(Variant::FLOAT, "db"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_POLAR2CARTESIAN: {
- MethodInfo mi("polar2cartesian", PropertyInfo(Variant::FLOAT, "r"), PropertyInfo(Variant::FLOAT, "th"));
- mi.return_val.type = Variant::VECTOR2;
- return mi;
- } break;
- case MATH_CARTESIAN2POLAR: {
- MethodInfo mi("cartesian2polar", PropertyInfo(Variant::FLOAT, "x"), PropertyInfo(Variant::FLOAT, "y"));
- mi.return_val.type = Variant::VECTOR2;
- return mi;
- } break;
- case MATH_WRAP: {
- MethodInfo mi("wrapi", PropertyInfo(Variant::INT, "value"), PropertyInfo(Variant::INT, "min"), PropertyInfo(Variant::INT, "max"));
- mi.return_val.type = Variant::INT;
- return mi;
- } break;
- case MATH_WRAPF: {
- MethodInfo mi("wrapf", PropertyInfo(Variant::FLOAT, "value"), PropertyInfo(Variant::FLOAT, "min"), PropertyInfo(Variant::FLOAT, "max"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case LOGIC_MAX: {
- MethodInfo mi("max", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
-
- } break;
- case LOGIC_MIN: {
- MethodInfo mi("min", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case LOGIC_CLAMP: {
- MethodInfo mi("clamp", PropertyInfo(Variant::FLOAT, "value"), PropertyInfo(Variant::FLOAT, "min"), PropertyInfo(Variant::FLOAT, "max"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case LOGIC_NEAREST_PO2: {
- MethodInfo mi("nearest_po2", PropertyInfo(Variant::INT, "value"));
- mi.return_val.type = Variant::INT;
- return mi;
- } break;
- case OBJ_WEAKREF: {
- MethodInfo mi("weakref", PropertyInfo(Variant::OBJECT, "obj"));
- mi.return_val.type = Variant::OBJECT;
- mi.return_val.class_name = "WeakRef";
-
- return mi;
-
- } break;
- case TYPE_CONVERT: {
- MethodInfo mi("convert", PropertyInfo(Variant::NIL, "what", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::INT, "type"));
- mi.return_val.type = Variant::NIL;
- mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- return mi;
- } break;
- case TYPE_OF: {
- MethodInfo mi("typeof", PropertyInfo(Variant::NIL, "what", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
- mi.return_val.type = Variant::INT;
- return mi;
-
- } break;
- case TYPE_EXISTS: {
- MethodInfo mi("type_exists", PropertyInfo(Variant::STRING, "type"));
- mi.return_val.type = Variant::BOOL;
- return mi;
-
- } break;
- case TEXT_CHAR: {
- MethodInfo mi("char", PropertyInfo(Variant::INT, "code"));
- mi.return_val.type = Variant::STRING;
- return mi;
-
- } break;
- case TEXT_ORD: {
- MethodInfo mi("ord", PropertyInfo(Variant::STRING, "char"));
- mi.return_val.type = Variant::INT;
- return mi;
-
- } break;
- case TEXT_STR: {
- MethodInfo mi("str");
- mi.return_val.type = Variant::STRING;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
-
- } break;
- case TEXT_PRINT: {
- MethodInfo mi("print");
- mi.return_val.type = Variant::NIL;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
-
- } break;
- case TEXT_PRINT_TABBED: {
- MethodInfo mi("printt");
- mi.return_val.type = Variant::NIL;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
-
- } break;
- case TEXT_PRINT_SPACED: {
- MethodInfo mi("prints");
- mi.return_val.type = Variant::NIL;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
-
- } break;
- case TEXT_PRINTERR: {
- MethodInfo mi("printerr");
- mi.return_val.type = Variant::NIL;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
-
- } break;
- case TEXT_PRINTRAW: {
- MethodInfo mi("printraw");
- mi.return_val.type = Variant::NIL;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
-
- } break;
- case TEXT_PRINT_DEBUG: {
- MethodInfo mi("print_debug");
- mi.return_val.type = Variant::NIL;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
-
- } break;
- case PUSH_ERROR: {
- MethodInfo mi(Variant::NIL, "push_error", PropertyInfo(Variant::STRING, "message"));
- mi.return_val.type = Variant::NIL;
- return mi;
-
- } break;
- case PUSH_WARNING: {
- MethodInfo mi(Variant::NIL, "push_warning", PropertyInfo(Variant::STRING, "message"));
- mi.return_val.type = Variant::NIL;
- return mi;
-
- } break;
- case VAR_TO_STR: {
- MethodInfo mi("var2str", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
- mi.return_val.type = Variant::STRING;
- return mi;
- } break;
- case STR_TO_VAR: {
- MethodInfo mi(Variant::NIL, "str2var", PropertyInfo(Variant::STRING, "string"));
- mi.return_val.type = Variant::NIL;
- mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- return mi;
- } break;
- case VAR_TO_BYTES: {
- MethodInfo mi("var2bytes", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::BOOL, "full_objects"));
- mi.default_arguments.push_back(false);
- mi.return_val.type = Variant::PACKED_BYTE_ARRAY;
- return mi;
- } break;
- case BYTES_TO_VAR: {
- MethodInfo mi(Variant::NIL, "bytes2var", PropertyInfo(Variant::PACKED_BYTE_ARRAY, "bytes"), PropertyInfo(Variant::BOOL, "allow_objects"));
- mi.default_arguments.push_back(false);
- mi.return_val.type = Variant::NIL;
- mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- return mi;
- } break;
- case GEN_RANGE: {
- MethodInfo mi("range");
- mi.return_val.type = Variant::ARRAY;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
- } break;
- case RESOURCE_LOAD: {
- MethodInfo mi("load", PropertyInfo(Variant::STRING, "path"));
- mi.return_val.type = Variant::OBJECT;
- mi.return_val.class_name = "Resource";
- return mi;
- } break;
- case INST2DICT: {
- MethodInfo mi("inst2dict", PropertyInfo(Variant::OBJECT, "inst"));
- mi.return_val.type = Variant::DICTIONARY;
- return mi;
- } break;
- case DICT2INST: {
- MethodInfo mi("dict2inst", PropertyInfo(Variant::DICTIONARY, "dict"));
- mi.return_val.type = Variant::OBJECT;
- return mi;
- } break;
- case VALIDATE_JSON: {
- MethodInfo mi("validate_json", PropertyInfo(Variant::STRING, "json"));
- mi.return_val.type = Variant::STRING;
- return mi;
- } break;
- case PARSE_JSON: {
- MethodInfo mi(Variant::NIL, "parse_json", PropertyInfo(Variant::STRING, "json"));
- mi.return_val.type = Variant::NIL;
- mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- return mi;
- } break;
- case TO_JSON: {
- MethodInfo mi("to_json", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
- mi.return_val.type = Variant::STRING;
- return mi;
- } break;
- case HASH: {
- MethodInfo mi("hash", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
- mi.return_val.type = Variant::INT;
- return mi;
- } break;
- case COLOR8: {
- MethodInfo mi("Color8", PropertyInfo(Variant::INT, "r8"), PropertyInfo(Variant::INT, "g8"), PropertyInfo(Variant::INT, "b8"), PropertyInfo(Variant::INT, "a8"));
- mi.default_arguments.push_back(255);
- mi.return_val.type = Variant::COLOR;
- return mi;
- } break;
- case COLORN: {
- MethodInfo mi("ColorN", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::FLOAT, "alpha"));
- mi.default_arguments.push_back(1.0f);
- mi.return_val.type = Variant::COLOR;
- return mi;
- } break;
-
- case PRINT_STACK: {
- MethodInfo mi("print_stack");
- mi.return_val.type = Variant::NIL;
- return mi;
- } break;
- case GET_STACK: {
- MethodInfo mi("get_stack");
- mi.return_val.type = Variant::ARRAY;
- return mi;
- } break;
-
- case INSTANCE_FROM_ID: {
- MethodInfo mi("instance_from_id", PropertyInfo(Variant::INT, "instance_id"));
- mi.return_val.type = Variant::OBJECT;
- return mi;
- } break;
- case LEN: {
- MethodInfo mi("len", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
- mi.return_val.type = Variant::INT;
- return mi;
- } break;
- case IS_INSTANCE_VALID: {
- MethodInfo mi("is_instance_valid", PropertyInfo(Variant::OBJECT, "instance"));
- mi.return_val.type = Variant::BOOL;
- return mi;
- } break;
- default: {
- ERR_FAIL_V(MethodInfo());
- } break;
- }
-#endif
- MethodInfo mi;
- mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- return mi;
-}
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index fde3662d66..2c735049b6 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -98,15 +98,6 @@ void GDScriptParser::cleanup() {
builtin_types.clear();
}
-GDScriptFunctions::Function GDScriptParser::get_builtin_function(const StringName &p_name) {
- for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
- if (p_name == GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))) {
- return GDScriptFunctions::Function(i);
- }
- }
- return GDScriptFunctions::FUNC_MAX;
-}
-
void GDScriptParser::get_annotation_list(List<MethodInfo> *r_annotations) const {
List<StringName> keys;
valid_annotations.get_key_list(&keys);
@@ -184,6 +175,7 @@ void GDScriptParser::push_error(const String &p_message, const Node *p_origin) {
#ifdef DEBUG_ENABLED
void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_code, const String &p_symbol1, const String &p_symbol2, const String &p_symbol3, const String &p_symbol4) {
+ ERR_FAIL_COND(p_source == nullptr);
Vector<String> symbols;
if (!p_symbol1.empty()) {
symbols.push_back(p_symbol1);
@@ -201,6 +193,7 @@ void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_
}
void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_code, const Vector<String> &p_symbols) {
+ ERR_FAIL_COND(p_source == nullptr);
if (is_ignoring_warnings) {
return;
}
@@ -556,6 +549,17 @@ void GDScriptParser::parse_program() {
parse_class_body();
+#ifdef TOOLS_ENABLED
+ for (Map<int, GDScriptTokenizer::CommentData>::Element *E = tokenizer.get_comments().front(); E; E = E->next()) {
+ if (E->get().new_line && E->get().comment.begins_with("##")) {
+ class_doc_line = MIN(class_doc_line, E->key());
+ }
+ }
+ if (has_comment(class_doc_line)) {
+ get_class_doc_comment(class_doc_line, head->doc_brief_description, head->doc_description, head->doc_tutorials, false);
+ }
+#endif // TOOLS_ENABLED
+
if (!check(GDScriptTokenizer::Token::TK_EOF)) {
push_error("Expected end of file.");
}
@@ -666,6 +670,10 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)()
if (member == nullptr) {
return;
}
+#ifdef TOOLS_ENABLED
+ int doc_comment_line = member->start_line - 1;
+#endif // TOOLS_ENABLED
+
// Consume annotations.
while (!annotation_stack.empty()) {
AnnotationNode *last_annotation = annotation_stack.back()->get();
@@ -678,7 +686,25 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)()
clear_unused_annotations();
return;
}
+#ifdef TOOLS_ENABLED
+ if (last_annotation->start_line == doc_comment_line) {
+ doc_comment_line--;
+ }
+#endif // TOOLS_ENABLED
+ }
+
+#ifdef TOOLS_ENABLED
+ // Consume doc comments.
+ class_doc_line = MIN(class_doc_line, doc_comment_line - 1);
+ if (has_comment(doc_comment_line)) {
+ if constexpr (std::is_same_v<T, ClassNode>) {
+ get_class_doc_comment(doc_comment_line, member->doc_brief_description, member->doc_description, member->doc_tutorials, true);
+ } else {
+ member->doc_description = get_doc_comment(doc_comment_line);
+ }
}
+#endif // TOOLS_ENABLED
+
if (member->identifier != nullptr) {
// Enums may be unnamed.
// TODO: Consider names in outer scope too, for constants and classes (and static functions?)
@@ -1048,6 +1074,7 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
item.parent_enum = enum_node;
item.line = previous.start_line;
item.leftmost_column = previous.leftmost_column;
+ item.rightmost_column = previous.rightmost_column;
if (elements.has(item.identifier->name)) {
push_error(vformat(R"(Name "%s" was already in this enum (at line %d).)", item.identifier->name, elements[item.identifier->name]), item.identifier);
@@ -1086,6 +1113,31 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
pop_multiline();
consume(GDScriptTokenizer::Token::BRACE_CLOSE, R"(Expected closing "}" for enum.)");
+#ifdef TOOLS_ENABLED
+ // Enum values documentaion.
+ for (int i = 0; i < enum_node->values.size(); i++) {
+ if (i == enum_node->values.size() - 1) {
+ // If close bracket is same line as last value.
+ if (enum_node->values[i].line != previous.start_line && has_comment(enum_node->values[i].line)) {
+ if (named) {
+ enum_node->values.write[i].doc_description = get_doc_comment(enum_node->values[i].line, true);
+ } else {
+ current_class->set_enum_value_doc(enum_node->values[i].identifier->name, get_doc_comment(enum_node->values[i].line, true));
+ }
+ }
+ } else {
+ // If two values are same line.
+ if (enum_node->values[i].line != enum_node->values[i + 1].line && has_comment(enum_node->values[i].line)) {
+ if (named) {
+ enum_node->values.write[i].doc_description = get_doc_comment(enum_node->values[i].line, true);
+ } else {
+ current_class->set_enum_value_doc(enum_node->values[i].identifier->name, get_doc_comment(enum_node->values[i].line, true));
+ }
+ }
+ }
+ }
+#endif // TOOLS_ENABLED
+
end_statement("enum");
return enum_node;
@@ -1419,9 +1471,13 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
}
#ifdef DEBUG_ENABLED
- if (unreachable) {
+ if (unreachable && result != nullptr) {
current_suite->has_unreachable_code = true;
- push_warning(result, GDScriptWarning::UNREACHABLE_CODE, current_function->identifier->name);
+ if (current_function) {
+ push_warning(result, GDScriptWarning::UNREACHABLE_CODE, current_function->identifier->name);
+ } else {
+ // TODO: Properties setters and getters with unreachable code are not being warned
+ }
}
#endif
@@ -2488,7 +2544,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
// Arguments.
CompletionType ct = COMPLETION_CALL_ARGUMENTS;
- if (get_builtin_function(call->function_name) == GDScriptFunctions::RESOURCE_LOAD) {
+ if (call->function_name == "load") {
ct = COMPLETION_RESOURCE_PATH;
}
push_completion_call(call);
@@ -2618,6 +2674,218 @@ GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) {
return type;
}
+#ifdef TOOLS_ENABLED
+static bool _in_codeblock(String p_line, bool p_already_in, int *r_block_begins = nullptr) {
+ int start_block = p_line.rfind("[codeblock]");
+ int end_block = p_line.rfind("[/codeblock]");
+
+ if (start_block != -1 && r_block_begins) {
+ *r_block_begins = start_block;
+ }
+
+ if (p_already_in) {
+ if (end_block == -1) {
+ return true;
+ } else if (start_block == -1) {
+ return false;
+ } else {
+ return start_block > end_block;
+ }
+ } else {
+ if (start_block == -1) {
+ return false;
+ } else if (end_block == -1) {
+ return true;
+ } else {
+ return start_block > end_block;
+ }
+ }
+}
+
+bool GDScriptParser::has_comment(int p_line) {
+ return tokenizer.get_comments().has(p_line);
+}
+
+String GDScriptParser::get_doc_comment(int p_line, bool p_single_line) {
+ const Map<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
+ ERR_FAIL_COND_V(!comments.has(p_line), String());
+
+ if (p_single_line) {
+ if (comments[p_line].comment.begins_with("##")) {
+ return comments[p_line].comment.trim_prefix("##").strip_edges();
+ }
+ return "";
+ }
+
+ String doc;
+
+ int line = p_line;
+ bool in_codeblock = false;
+
+ while (comments.has(line - 1)) {
+ if (!comments[line - 1].new_line || !comments[line - 1].comment.begins_with("##")) {
+ break;
+ }
+ line--;
+ }
+
+ int codeblock_begins = 0;
+ while (comments.has(line)) {
+ if (!comments[line].new_line || !comments[line].comment.begins_with("##")) {
+ break;
+ }
+ String doc_line = comments[line].comment.trim_prefix("##");
+
+ in_codeblock = _in_codeblock(doc_line, in_codeblock, &codeblock_begins);
+
+ if (in_codeblock) {
+ int i = 0;
+ for (; i < codeblock_begins; i++) {
+ if (doc_line[i] != ' ') {
+ break;
+ }
+ }
+ doc_line = doc_line.substr(i);
+ } else {
+ doc_line = doc_line.strip_edges();
+ }
+ String line_join = (in_codeblock) ? "\n" : " ";
+
+ doc = (doc.empty()) ? doc_line : doc + line_join + doc_line;
+ line++;
+ }
+
+ return doc;
+}
+
+void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &p_desc, Vector<Pair<String, String>> &p_tutorials, bool p_inner_class) {
+ const Map<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
+ if (!comments.has(p_line)) {
+ return;
+ }
+ ERR_FAIL_COND(p_brief != "" || p_desc != "" || p_tutorials.size() != 0);
+
+ int line = p_line;
+ bool in_codeblock = false;
+ enum Mode {
+ BRIEF,
+ DESC,
+ TUTORIALS,
+ DONE,
+ };
+ Mode mode = BRIEF;
+
+ if (p_inner_class) {
+ while (comments.has(line - 1)) {
+ if (!comments[line - 1].new_line || !comments[line - 1].comment.begins_with("##")) {
+ break;
+ }
+ line--;
+ }
+ }
+
+ int codeblock_begins = 0;
+ while (comments.has(line)) {
+ if (!comments[line].new_line || !comments[line].comment.begins_with("##")) {
+ break;
+ }
+
+ String title, link; // For tutorials.
+ String doc_line = comments[line++].comment.trim_prefix("##");
+ String striped_line = doc_line.strip_edges();
+
+ // Set the read mode.
+ if (striped_line.begins_with("@desc:") && p_desc == "") {
+ mode = DESC;
+ striped_line = striped_line.trim_prefix("@desc:");
+ in_codeblock = _in_codeblock(doc_line, in_codeblock);
+
+ } else if (striped_line.begins_with("@tutorial")) {
+ int begin_scan = String("@tutorial").length();
+ if (begin_scan >= striped_line.length()) {
+ continue; // invalid syntax.
+ }
+
+ if (striped_line[begin_scan] == ':') { // No title.
+ // Syntax: ## @tutorial: https://godotengine.org/ // The title argument is optional.
+ title = "";
+ link = striped_line.trim_prefix("@tutorial:").strip_edges();
+
+ } else {
+ /* Syntax:
+ @tutorial ( The Title Here ) : http://the.url/
+ ^ open ^ close ^ colon ^ url
+ */
+ int open_bracket_pos = begin_scan, close_bracket_pos = 0;
+ while (open_bracket_pos < striped_line.length() && (striped_line[open_bracket_pos] == ' ' || striped_line[open_bracket_pos] == '\t')) {
+ open_bracket_pos++;
+ }
+ if (open_bracket_pos == striped_line.length() || striped_line[open_bracket_pos++] != '(') {
+ continue; // invalid syntax.
+ }
+ close_bracket_pos = open_bracket_pos;
+ while (close_bracket_pos < striped_line.length() && striped_line[close_bracket_pos] != ')') {
+ close_bracket_pos++;
+ }
+ if (close_bracket_pos == striped_line.length()) {
+ continue; // invalid syntax.
+ }
+
+ int colon_pos = close_bracket_pos + 1;
+ while (colon_pos < striped_line.length() && (striped_line[colon_pos] == ' ' || striped_line[colon_pos] == '\t')) {
+ colon_pos++;
+ }
+ if (colon_pos == striped_line.length() || striped_line[colon_pos++] != ':') {
+ continue; // invalid syntax.
+ }
+
+ title = striped_line.substr(open_bracket_pos, close_bracket_pos - open_bracket_pos).strip_edges();
+ link = striped_line.substr(colon_pos).strip_edges();
+ }
+
+ mode = TUTORIALS;
+ in_codeblock = false;
+ } else if (striped_line.empty()) {
+ continue;
+ } else {
+ // Tutorial docs are single line, we need a @tag after it.
+ if (mode == TUTORIALS) {
+ mode = DONE;
+ }
+
+ in_codeblock = _in_codeblock(doc_line, in_codeblock, &codeblock_begins);
+ }
+
+ if (in_codeblock) {
+ int i = 0;
+ for (; i < codeblock_begins; i++) {
+ if (doc_line[i] != ' ') {
+ break;
+ }
+ }
+ doc_line = doc_line.substr(i);
+ } else {
+ doc_line = striped_line;
+ }
+ String line_join = (in_codeblock) ? "\n" : " ";
+
+ switch (mode) {
+ case BRIEF:
+ p_brief = (p_brief.length() == 0) ? doc_line : p_brief + line_join + doc_line;
+ break;
+ case DESC:
+ p_desc = (p_desc.length() == 0) ? doc_line : p_desc + line_join + doc_line;
+ break;
+ case TUTORIALS:
+ p_tutorials.append(Pair<String, String>(title, link));
+ break;
+ case DONE:
+ return;
+ }
+ }
+}
+#endif // TOOLS_ENABLED
+
GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Type p_token_type) {
// Function table for expression parsing.
// clang-format destroys the alignment here, so turn off for the table.
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index b24acc4778..4cecdc6970 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -43,7 +43,6 @@
#include "core/templates/vector.h"
#include "core/variant/variant.h"
#include "gdscript_cache.h"
-#include "gdscript_functions.h"
#include "gdscript_tokenizer.h"
#ifdef DEBUG_ENABLED
@@ -413,9 +412,16 @@ public:
int line = 0;
int leftmost_column = 0;
int rightmost_column = 0;
+#ifdef TOOLS_ENABLED
+ String doc_description;
+#endif // TOOLS_ENABLED
};
+
IdentifierNode *identifier = nullptr;
Vector<Value> values;
+#ifdef TOOLS_ENABLED
+ String doc_description;
+#endif // TOOLS_ENABLED
EnumNode() {
type = ENUM;
@@ -568,6 +574,17 @@ public:
Vector<StringName> extends; // List for indexing: extends A.B.C
DataType base_type;
String fqcn; // Fully-qualified class name. Identifies uniquely any class in the project.
+#ifdef TOOLS_ENABLED
+ String doc_description;
+ String doc_brief_description;
+ Vector<Pair<String, String>> doc_tutorials;
+
+ // EnumValue docs are parsed after itself, so we need a method to add/modify the doc property later.
+ void set_enum_value_doc(const StringName &p_name, const String &p_doc_description) {
+ ERR_FAIL_INDEX(members_indices[p_name], members.size());
+ members.write[members_indices[p_name]].enum_value.doc_description = p_doc_description;
+ }
+#endif // TOOLS_ENABLED
bool resolved_interface = false;
bool resolved_body = false;
@@ -602,6 +619,9 @@ public:
TypeNode *datatype_specifier = nullptr;
bool infer_datatype = false;
int usages = 0;
+#ifdef TOOLS_ENABLED
+ String doc_description;
+#endif // TOOLS_ENABLED
ConstantNode() {
type = CONSTANT;
@@ -653,6 +673,10 @@ public:
bool is_coroutine = false;
MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
MethodInfo info;
+#ifdef TOOLS_ENABLED
+ Vector<Variant> default_arg_values;
+ String doc_description;
+#endif // TOOLS_ENABLED
bool resolved_signature = false;
bool resolved_body = false;
@@ -820,6 +844,9 @@ public:
IdentifierNode *identifier = nullptr;
Vector<ParameterNode *> parameters;
HashMap<StringName, int> parameters_indices;
+#ifdef TOOLS_ENABLED
+ String doc_description;
+#endif // TOOLS_ENABLED
SignalNode() {
type = SIGNAL;
@@ -1012,6 +1039,9 @@ public:
MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
int assignments = 0;
int usages = 0;
+#ifdef TOOLS_ENABLED
+ String doc_description;
+#endif // TOOLS_ENABLED
VariableNode() {
type = VARIABLE;
@@ -1270,13 +1300,19 @@ private:
ExpressionNode *parse_subscript(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_invalid_token(ExpressionNode *p_previous_operand, bool p_can_assign);
TypeNode *parse_type(bool p_allow_void = false);
+#ifdef TOOLS_ENABLED
+ // Doc comments.
+ int class_doc_line = 0x7FFFFFFF;
+ bool has_comment(int p_line);
+ String get_doc_comment(int p_line, bool p_single_line = false);
+ void get_class_doc_comment(int p_line, String &p_brief, String &p_desc, Vector<Pair<String, String>> &p_tutorials, bool p_inner_class);
+#endif // TOOLS_ENABLED
public:
Error parse(const String &p_source_code, const String &p_script_path, bool p_for_completion);
ClassNode *get_tree() const { return head; }
bool is_tool() const { return _is_tool; }
static Variant::Type get_builtin_type(const StringName &p_type);
- static GDScriptFunctions::Function get_builtin_function(const StringName &p_name);
CompletionContext get_completion_context() const { return completion_context; }
CompletionCall get_completion_call() const { return completion_call; }
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index b91777ede1..ac43105254 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -1014,9 +1014,17 @@ void GDScriptTokenizer::check_indent() {
}
if (_peek() == '#') {
// Comment. Advance to the next line.
+#ifdef TOOLS_ENABLED
+ String comment;
+ while (_peek() != '\n' && !_is_at_end()) {
+ comment += _advance();
+ }
+ comments[line] = CommentData(comment, true);
+#else
while (_peek() != '\n' && !_is_at_end()) {
_advance();
}
+#endif // TOOLS_ENABLED
if (_is_at_end()) {
// Reached the end with an empty line, so just dedent as much as needed.
pending_indents -= indent_level();
@@ -1125,18 +1133,26 @@ void GDScriptTokenizer::_skip_whitespace() {
newline(!is_bol); // Don't create new line token if line is empty.
check_indent();
break;
- case '#':
+ case '#': {
// Comment.
+#ifdef TOOLS_ENABLED
+ String comment;
+ while (_peek() != '\n' && !_is_at_end()) {
+ comment += _advance();
+ }
+ comments[line] = CommentData(comment, is_bol);
+#else
while (_peek() != '\n' && !_is_at_end()) {
_advance();
}
+#endif // TOOLS_ENABLED
if (_is_at_end()) {
return;
}
_advance(); // Consume '\n'
newline(!is_bol);
check_indent();
- break;
+ } break;
default:
return;
}
diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h
index d51f1f250f..f236c86f9f 100644
--- a/modules/gdscript/gdscript_tokenizer.h
+++ b/modules/gdscript/gdscript_tokenizer.h
@@ -32,6 +32,7 @@
#define GDSCRIPT_TOKENIZER_H
#include "core/templates/list.h"
+#include "core/templates/map.h"
#include "core/templates/set.h"
#include "core/templates/vector.h"
#include "core/variant/variant.h"
@@ -181,6 +182,21 @@ public:
}
};
+#ifdef TOOLS_ENABLED
+ struct CommentData {
+ String comment;
+ bool new_line = false;
+ CommentData() {}
+ CommentData(const String &p_comment, bool p_new_line) {
+ comment = p_comment;
+ new_line = p_new_line;
+ }
+ };
+ const Map<int, CommentData> &get_comments() const {
+ return comments;
+ }
+#endif // TOOLS_ENABLED
+
private:
String source;
const char32_t *_source = nullptr;
@@ -207,6 +223,10 @@ private:
int position = 0;
int length = 0;
+#ifdef TOOLS_ENABLED
+ Map<int, CommentData> comments;
+#endif // TOOLS_ENABLED
+
_FORCE_INLINE_ bool _is_at_end() { return position >= length; }
_FORCE_INLINE_ char32_t _peek(int p_offset = 0) { return position + p_offset >= 0 && position + p_offset < length ? _current[p_offset] : '\0'; }
int indent_level() const { return indent_stack.size(); }
diff --git a/modules/gdscript/gdscript_utility_functions.cpp b/modules/gdscript/gdscript_utility_functions.cpp
new file mode 100644
index 0000000000..b1780446d0
--- /dev/null
+++ b/modules/gdscript/gdscript_utility_functions.cpp
@@ -0,0 +1,718 @@
+/*************************************************************************/
+/* gdscript_utility_functions.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "gdscript_utility_functions.h"
+
+#include "core/io/resource_loader.h"
+#include "core/object/class_db.h"
+#include "core/object/method_bind.h"
+#include "core/object/object.h"
+#include "core/templates/oa_hash_map.h"
+#include "core/templates/vector.h"
+#include "gdscript.h"
+
+#ifdef DEBUG_ENABLED
+
+#define VALIDATE_ARG_COUNT(m_count) \
+ if (p_arg_count < m_count) { \
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; \
+ r_error.argument = m_count; \
+ r_error.expected = m_count; \
+ *r_ret = Variant(); \
+ return; \
+ } \
+ if (p_arg_count > m_count) { \
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; \
+ r_error.argument = m_count; \
+ r_error.expected = m_count; \
+ *r_ret = Variant(); \
+ return; \
+ }
+
+#define VALIDATE_ARG_INT(m_arg) \
+ if (p_args[m_arg]->get_type() != Variant::INT) { \
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
+ r_error.argument = m_arg; \
+ r_error.expected = Variant::INT; \
+ *r_ret = Variant(); \
+ return; \
+ }
+
+#define VALIDATE_ARG_NUM(m_arg) \
+ if (!p_args[m_arg]->is_num()) { \
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
+ r_error.argument = m_arg; \
+ r_error.expected = Variant::FLOAT; \
+ *r_ret = Variant(); \
+ return; \
+ }
+
+#else
+
+#define VALIDATE_ARG_COUNT(m_count)
+#define VALIDATE_ARG_INT(m_arg)
+#define VALIDATE_ARG_NUM(m_arg)
+
+#endif
+
+struct GDScriptUtilityFunctionsDefinitions {
+ static inline void convert(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(2);
+ VALIDATE_ARG_INT(1);
+ int type = *p_args[1];
+ if (type < 0 || type >= Variant::VARIANT_MAX) {
+ *r_ret = RTR("Invalid type argument to convert(), use TYPE_* constants.");
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::INT;
+ return;
+
+ } else {
+ Variant::construct(Variant::Type(type), *r_ret, p_args, 1, r_error);
+ }
+ }
+
+ static inline void type_exists(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(1);
+ *r_ret = ClassDB::class_exists(*p_args[0]);
+ }
+
+ static inline void _char(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_INT(0);
+ char32_t result[2] = { *p_args[0], 0 };
+ *r_ret = String(result);
+ }
+
+ static inline void str(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ if (p_arg_count < 1) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 1;
+ *r_ret = Variant();
+ return;
+ }
+
+ String str;
+ for (int i = 0; i < p_arg_count; i++) {
+ String os = p_args[i]->operator String();
+
+ if (i == 0) {
+ str = os;
+ } else {
+ str += os;
+ }
+ }
+ *r_ret = str;
+ }
+
+ static inline void range(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ switch (p_arg_count) {
+ case 0: {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 1;
+ r_error.expected = 1;
+ *r_ret = Variant();
+ } break;
+ case 1: {
+ VALIDATE_ARG_NUM(0);
+ int count = *p_args[0];
+ Array arr;
+ if (count <= 0) {
+ *r_ret = arr;
+ return;
+ }
+ Error err = arr.resize(count);
+ if (err != OK) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ *r_ret = Variant();
+ return;
+ }
+
+ for (int i = 0; i < count; i++) {
+ arr[i] = i;
+ }
+
+ *r_ret = arr;
+ } break;
+ case 2: {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+
+ int from = *p_args[0];
+ int to = *p_args[1];
+
+ Array arr;
+ if (from >= to) {
+ *r_ret = arr;
+ return;
+ }
+ Error err = arr.resize(to - from);
+ if (err != OK) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ *r_ret = Variant();
+ return;
+ }
+ for (int i = from; i < to; i++) {
+ arr[i - from] = i;
+ }
+ *r_ret = arr;
+ } break;
+ case 3: {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ VALIDATE_ARG_NUM(2);
+
+ int from = *p_args[0];
+ int to = *p_args[1];
+ int incr = *p_args[2];
+ if (incr == 0) {
+ *r_ret = RTR("Step argument is zero!");
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ return;
+ }
+
+ Array arr;
+ if (from >= to && incr > 0) {
+ *r_ret = arr;
+ return;
+ }
+ if (from <= to && incr < 0) {
+ *r_ret = arr;
+ return;
+ }
+
+ // Calculate how many.
+ int count = 0;
+ if (incr > 0) {
+ count = ((to - from - 1) / incr) + 1;
+ } else {
+ count = ((from - to - 1) / -incr) + 1;
+ }
+
+ Error err = arr.resize(count);
+
+ if (err != OK) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ *r_ret = Variant();
+ return;
+ }
+
+ if (incr > 0) {
+ int idx = 0;
+ for (int i = from; i < to; i += incr) {
+ arr[idx++] = i;
+ }
+ } else {
+ int idx = 0;
+ for (int i = from; i > to; i += incr) {
+ arr[idx++] = i;
+ }
+ }
+
+ *r_ret = arr;
+ } break;
+ default: {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+ r_error.argument = 3;
+ r_error.expected = 3;
+ *r_ret = Variant();
+
+ } break;
+ }
+ }
+
+ static inline void load(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(1);
+ if (p_args[0]->get_type() != Variant::STRING) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::STRING;
+ *r_ret = Variant();
+ } else {
+ *r_ret = ResourceLoader::load(*p_args[0]);
+ }
+ }
+
+ static inline void inst2dict(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(1);
+
+ if (p_args[0]->get_type() == Variant::NIL) {
+ *r_ret = Variant();
+ } else if (p_args[0]->get_type() != Variant::OBJECT) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ *r_ret = Variant();
+ } else {
+ Object *obj = *p_args[0];
+ if (!obj) {
+ *r_ret = Variant();
+
+ } else if (!obj->get_script_instance() || obj->get_script_instance()->get_language() != GDScriptLanguage::get_singleton()) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::DICTIONARY;
+ *r_ret = RTR("Not a script with an instance");
+ return;
+ } else {
+ GDScriptInstance *ins = static_cast<GDScriptInstance *>(obj->get_script_instance());
+ Ref<GDScript> base = ins->get_script();
+ if (base.is_null()) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::DICTIONARY;
+ *r_ret = RTR("Not based on a script");
+ return;
+ }
+
+ GDScript *p = base.ptr();
+ Vector<StringName> sname;
+
+ while (p->_owner) {
+ sname.push_back(p->name);
+ p = p->_owner;
+ }
+ sname.invert();
+
+ if (!p->path.is_resource_file()) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::DICTIONARY;
+ *r_ret = Variant();
+
+ *r_ret = RTR("Not based on a resource file");
+
+ return;
+ }
+
+ NodePath cp(sname, Vector<StringName>(), false);
+
+ Dictionary d;
+ d["@subpath"] = cp;
+ d["@path"] = p->get_path();
+
+ for (Map<StringName, GDScript::MemberInfo>::Element *E = base->member_indices.front(); E; E = E->next()) {
+ if (!d.has(E->key())) {
+ d[E->key()] = ins->members[E->get().index];
+ }
+ }
+ *r_ret = d;
+ }
+ }
+ }
+
+ static inline void dict2inst(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(1);
+
+ if (p_args[0]->get_type() != Variant::DICTIONARY) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::DICTIONARY;
+ *r_ret = Variant();
+
+ return;
+ }
+
+ Dictionary d = *p_args[0];
+
+ if (!d.has("@path")) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::OBJECT;
+ *r_ret = RTR("Invalid instance dictionary format (missing @path)");
+
+ return;
+ }
+
+ Ref<Script> scr = ResourceLoader::load(d["@path"]);
+ if (!scr.is_valid()) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::OBJECT;
+ *r_ret = RTR("Invalid instance dictionary format (can't load script at @path)");
+ return;
+ }
+
+ Ref<GDScript> gdscr = scr;
+
+ if (!gdscr.is_valid()) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::OBJECT;
+ *r_ret = Variant();
+ *r_ret = RTR("Invalid instance dictionary format (invalid script at @path)");
+ return;
+ }
+
+ NodePath sub;
+ if (d.has("@subpath")) {
+ sub = d["@subpath"];
+ }
+
+ for (int i = 0; i < sub.get_name_count(); i++) {
+ gdscr = gdscr->subclasses[sub.get_name(i)];
+ if (!gdscr.is_valid()) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::OBJECT;
+ *r_ret = Variant();
+ *r_ret = RTR("Invalid instance dictionary (invalid subclasses)");
+ return;
+ }
+ }
+ *r_ret = gdscr->_new(nullptr, -1 /*skip initializer*/, r_error);
+
+ if (r_error.error != Callable::CallError::CALL_OK) {
+ *r_ret = Variant();
+ return;
+ }
+
+ GDScriptInstance *ins = static_cast<GDScriptInstance *>(static_cast<Object *>(*r_ret)->get_script_instance());
+ Ref<GDScript> gd_ref = ins->get_script();
+
+ for (Map<StringName, GDScript::MemberInfo>::Element *E = gd_ref->member_indices.front(); E; E = E->next()) {
+ if (d.has(E->key())) {
+ ins->members.write[E->get().index] = d[E->key()];
+ }
+ }
+ }
+
+ static inline void Color8(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ if (p_arg_count < 3) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 3;
+ *r_ret = Variant();
+ return;
+ }
+ if (p_arg_count > 4) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+ r_error.argument = 4;
+ *r_ret = Variant();
+ return;
+ }
+
+ VALIDATE_ARG_INT(0);
+ VALIDATE_ARG_INT(1);
+ VALIDATE_ARG_INT(2);
+
+ Color color((int64_t)*p_args[0] / 255.0f, (int64_t)*p_args[1] / 255.0f, (int64_t)*p_args[2] / 255.0f);
+
+ if (p_arg_count == 4) {
+ VALIDATE_ARG_INT(3);
+ color.a = (int64_t)*p_args[3] / 255.0f;
+ }
+
+ *r_ret = color;
+ }
+
+ static inline void print_debug(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ String str;
+ for (int i = 0; i < p_arg_count; i++) {
+ str += p_args[i]->operator String();
+ }
+
+ ScriptLanguage *script = GDScriptLanguage::get_singleton();
+ if (script->debug_get_stack_level_count() > 0) {
+ str += "\n At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)) + ":" + script->debug_get_stack_level_function(0) + "()";
+ }
+
+ print_line(str);
+ *r_ret = Variant();
+ }
+
+ static inline void print_stack(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(0);
+
+ ScriptLanguage *script = GDScriptLanguage::get_singleton();
+ for (int i = 0; i < script->debug_get_stack_level_count(); i++) {
+ print_line("Frame " + itos(i) + " - " + script->debug_get_stack_level_source(i) + ":" + itos(script->debug_get_stack_level_line(i)) + " in function '" + script->debug_get_stack_level_function(i) + "'");
+ };
+ }
+
+ static inline void get_stack(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(0);
+
+ ScriptLanguage *script = GDScriptLanguage::get_singleton();
+ Array ret;
+ for (int i = 0; i < script->debug_get_stack_level_count(); i++) {
+ Dictionary frame;
+ frame["source"] = script->debug_get_stack_level_source(i);
+ frame["function"] = script->debug_get_stack_level_function(i);
+ frame["line"] = script->debug_get_stack_level_line(i);
+ ret.push_back(frame);
+ };
+ *r_ret = ret;
+ }
+
+ static inline void len(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(1);
+ switch (p_args[0]->get_type()) {
+ case Variant::STRING: {
+ String d = *p_args[0];
+ *r_ret = d.length();
+ } break;
+ case Variant::DICTIONARY: {
+ Dictionary d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::ARRAY: {
+ Array d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_BYTE_ARRAY: {
+ Vector<uint8_t> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_INT32_ARRAY: {
+ Vector<int32_t> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_INT64_ARRAY: {
+ Vector<int64_t> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_FLOAT32_ARRAY: {
+ Vector<float> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_FLOAT64_ARRAY: {
+ Vector<double> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_STRING_ARRAY: {
+ Vector<String> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_VECTOR2_ARRAY: {
+ Vector<Vector2> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_VECTOR3_ARRAY: {
+ Vector<Vector3> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_COLOR_ARRAY: {
+ Vector<Color> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ default: {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::NIL;
+ *r_ret = vformat(RTR("Value of type '%s' can't provide a length."), Variant::get_type_name(p_args[0]->get_type()));
+ }
+ }
+ }
+};
+
+struct GDScriptUtilityFunctionInfo {
+ GDScriptUtilityFunctions::FunctionPtr function;
+ MethodInfo info;
+ bool is_constant = false;
+};
+
+static OAHashMap<StringName, GDScriptUtilityFunctionInfo> utility_function_table;
+static List<StringName> utility_function_name_table;
+
+static void _register_function(const String &p_name, const MethodInfo &p_method_info, GDScriptUtilityFunctions::FunctionPtr p_function, bool p_is_const) {
+ StringName sname(p_name);
+
+ ERR_FAIL_COND(utility_function_table.has(sname));
+
+ GDScriptUtilityFunctionInfo function;
+ function.function = p_function;
+ function.info = p_method_info;
+ function.is_constant = p_is_const;
+
+ utility_function_table.insert(sname, function);
+ utility_function_name_table.push_back(sname);
+}
+
+#define REGISTER_FUNC(m_func, m_is_const, m_return_type, ...) \
+ { \
+ String name(#m_func); \
+ if (name.begins_with("_")) { \
+ name = name.substr(1, name.length() - 1); \
+ } \
+ MethodInfo info = MethodInfo(name, __VA_ARGS__); \
+ info.return_val.type = m_return_type; \
+ _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+ }
+
+#define REGISTER_FUNC_NO_ARGS(m_func, m_is_const, m_return_type) \
+ { \
+ String name(#m_func); \
+ if (name.begins_with("_")) { \
+ name = name.substr(1, name.length() - 1); \
+ } \
+ MethodInfo info = MethodInfo(name); \
+ info.return_val.type = m_return_type; \
+ _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+ }
+
+#define REGISTER_VARARG_FUNC(m_func, m_is_const, m_return_type) \
+ { \
+ String name(#m_func); \
+ if (name.begins_with("_")) { \
+ name = name.substr(1, name.length() - 1); \
+ } \
+ MethodInfo info = MethodInfo(name); \
+ info.return_val.type = m_return_type; \
+ info.flags |= METHOD_FLAG_VARARG; \
+ _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+ }
+
+#define REGISTER_VARIANT_FUNC(m_func, m_is_const, ...) \
+ { \
+ String name(#m_func); \
+ if (name.begins_with("_")) { \
+ name = name.substr(1, name.length() - 1); \
+ } \
+ MethodInfo info = MethodInfo(name, __VA_ARGS__); \
+ info.return_val.type = Variant::NIL; \
+ info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; \
+ _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+ }
+
+#define REGISTER_CLASS_FUNC(m_func, m_is_const, m_return_type, ...) \
+ { \
+ String name(#m_func); \
+ if (name.begins_with("_")) { \
+ name = name.substr(1, name.length() - 1); \
+ } \
+ MethodInfo info = MethodInfo(name, __VA_ARGS__); \
+ info.return_val.type = Variant::OBJECT; \
+ info.return_val.hint = PROPERTY_HINT_RESOURCE_TYPE; \
+ info.return_val.class_name = m_return_type; \
+ _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+ }
+
+#define REGISTER_FUNC_DEF(m_func, m_is_const, m_default, m_return_type, ...) \
+ { \
+ String name(#m_func); \
+ if (name.begins_with("_")) { \
+ name = name.substr(1, name.length() - 1); \
+ } \
+ MethodInfo info = MethodInfo(name, __VA_ARGS__); \
+ info.return_val.type = m_return_type; \
+ info.default_arguments.push_back(m_default); \
+ _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+ }
+
+#define ARG(m_name, m_type) \
+ PropertyInfo(m_type, m_name)
+
+#define VARARG(m_name) \
+ PropertyInfo(Variant::NIL, m_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT)
+
+void GDScriptUtilityFunctions::register_functions() {
+ REGISTER_VARIANT_FUNC(convert, true, VARARG("what"), ARG("type", Variant::INT));
+ REGISTER_FUNC(type_exists, true, Variant::BOOL, ARG("type", Variant::STRING_NAME));
+ REGISTER_FUNC(_char, true, Variant::STRING, ARG("char", Variant::INT));
+ REGISTER_VARARG_FUNC(str, true, Variant::STRING);
+ REGISTER_VARARG_FUNC(range, false, Variant::ARRAY);
+ REGISTER_CLASS_FUNC(load, false, "Resource", ARG("path", Variant::STRING));
+ REGISTER_FUNC(inst2dict, false, Variant::DICTIONARY, ARG("instance", Variant::OBJECT));
+ REGISTER_FUNC(dict2inst, false, Variant::OBJECT, ARG("dictionary", Variant::DICTIONARY));
+ REGISTER_FUNC_DEF(Color8, true, 255, Variant::COLOR, ARG("r8", Variant::INT), ARG("g8", Variant::INT), ARG("b8", Variant::INT), ARG("a8", Variant::INT));
+ REGISTER_VARARG_FUNC(print_debug, false, Variant::NIL);
+ REGISTER_FUNC_NO_ARGS(print_stack, false, Variant::NIL);
+ REGISTER_FUNC_NO_ARGS(get_stack, false, Variant::ARRAY);
+ REGISTER_FUNC(len, true, Variant::INT, VARARG("var"));
+}
+
+void GDScriptUtilityFunctions::unregister_functions() {
+ utility_function_name_table.clear();
+ utility_function_table.clear();
+}
+
+GDScriptUtilityFunctions::FunctionPtr GDScriptUtilityFunctions::get_function(const StringName &p_function) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, nullptr);
+ return info->function;
+}
+
+bool GDScriptUtilityFunctions::has_function_return_value(const StringName &p_function) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, false);
+ return info->info.return_val.type != Variant::NIL || bool(info->info.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT);
+}
+
+Variant::Type GDScriptUtilityFunctions::get_function_return_type(const StringName &p_function) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, Variant::NIL);
+ return info->info.return_val.type;
+}
+
+StringName GDScriptUtilityFunctions::get_function_return_class(const StringName &p_function) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, StringName());
+ return info->info.return_val.class_name;
+}
+
+Variant::Type GDScriptUtilityFunctions::get_function_argument_type(const StringName &p_function, int p_arg) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, Variant::NIL);
+ ERR_FAIL_COND_V(p_arg >= info->info.arguments.size(), Variant::NIL);
+ return info->info.arguments[p_arg].type;
+}
+
+int GDScriptUtilityFunctions::get_function_argument_count(const StringName &p_function, int p_arg) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, 0);
+ return info->info.arguments.size();
+}
+
+bool GDScriptUtilityFunctions::is_function_vararg(const StringName &p_function) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, false);
+ return (bool)(info->info.flags & METHOD_FLAG_VARARG);
+}
+
+bool GDScriptUtilityFunctions::is_function_constant(const StringName &p_function) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, false);
+ return info->is_constant;
+}
+
+bool GDScriptUtilityFunctions::function_exists(const StringName &p_function) {
+ return utility_function_table.has(p_function);
+}
+
+void GDScriptUtilityFunctions::get_function_list(List<StringName> *r_functions) {
+ for (const List<StringName>::Element *E = utility_function_name_table.front(); E; E = E->next()) {
+ r_functions->push_back(E->get());
+ }
+}
+
+MethodInfo GDScriptUtilityFunctions::get_function_info(const StringName &p_function) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, MethodInfo());
+ return info->info;
+}
diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_utility_functions.h
index 005b49c5da..50867438d9 100644
--- a/modules/gdscript/gdscript_functions.h
+++ b/modules/gdscript/gdscript_utility_functions.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* gdscript_functions.h */
+/* gdscript_utility_functions.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,110 +28,31 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GDSCRIPT_FUNCTIONS_H
-#define GDSCRIPT_FUNCTIONS_H
+#ifndef GDSCRIPT_UTILITY_FUNCTIONS_H
+#define GDSCRIPT_UTILITY_FUNCTIONS_H
+#include "core/string/string_name.h"
#include "core/variant/variant.h"
-class GDScriptFunctions {
+class GDScriptUtilityFunctions {
public:
- enum Function {
- MATH_SIN,
- MATH_COS,
- MATH_TAN,
- MATH_SINH,
- MATH_COSH,
- MATH_TANH,
- MATH_ASIN,
- MATH_ACOS,
- MATH_ATAN,
- MATH_ATAN2,
- MATH_SQRT,
- MATH_FMOD,
- MATH_FPOSMOD,
- MATH_POSMOD,
- MATH_FLOOR,
- MATH_CEIL,
- MATH_ROUND,
- MATH_ABS,
- MATH_SIGN,
- MATH_POW,
- MATH_LOG,
- MATH_EXP,
- MATH_ISNAN,
- MATH_ISINF,
- MATH_ISEQUALAPPROX,
- MATH_ISZEROAPPROX,
- MATH_EASE,
- MATH_STEP_DECIMALS,
- MATH_STEPIFY,
- MATH_LERP,
- MATH_LERP_ANGLE,
- MATH_INVERSE_LERP,
- MATH_RANGE_LERP,
- MATH_SMOOTHSTEP,
- MATH_MOVE_TOWARD,
- MATH_DECTIME,
- MATH_RANDOMIZE,
- MATH_RANDI,
- MATH_RANDF,
- MATH_RANDF_RANGE,
- MATH_RANDI_RANGE,
- MATH_SEED,
- MATH_RANDSEED,
- MATH_DEG2RAD,
- MATH_RAD2DEG,
- MATH_LINEAR2DB,
- MATH_DB2LINEAR,
- MATH_POLAR2CARTESIAN,
- MATH_CARTESIAN2POLAR,
- MATH_WRAP,
- MATH_WRAPF,
- LOGIC_MAX,
- LOGIC_MIN,
- LOGIC_CLAMP,
- LOGIC_NEAREST_PO2,
- OBJ_WEAKREF,
- TYPE_CONVERT,
- TYPE_OF,
- TYPE_EXISTS,
- TEXT_CHAR,
- TEXT_ORD,
- TEXT_STR,
- TEXT_PRINT,
- TEXT_PRINT_TABBED,
- TEXT_PRINT_SPACED,
- TEXT_PRINTERR,
- TEXT_PRINTRAW,
- TEXT_PRINT_DEBUG,
- PUSH_ERROR,
- PUSH_WARNING,
- VAR_TO_STR,
- STR_TO_VAR,
- VAR_TO_BYTES,
- BYTES_TO_VAR,
- GEN_RANGE,
- RESOURCE_LOAD,
- INST2DICT,
- DICT2INST,
- VALIDATE_JSON,
- PARSE_JSON,
- TO_JSON,
- HASH,
- COLOR8,
- COLORN,
- PRINT_STACK,
- GET_STACK,
- INSTANCE_FROM_ID,
- LEN,
- IS_INSTANCE_VALID,
- FUNC_MAX
- };
+ typedef void (*FunctionPtr)(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error);
- static const char *get_func_name(Function p_func);
- static void call(Function p_func, const Variant **p_args, int p_arg_count, Variant &r_ret, Callable::CallError &r_error);
- static bool is_deterministic(Function p_func);
- static MethodInfo get_info(Function p_func);
+ static FunctionPtr get_function(const StringName &p_function);
+ static bool has_function_return_value(const StringName &p_function);
+ static Variant::Type get_function_return_type(const StringName &p_function);
+ static StringName get_function_return_class(const StringName &p_function);
+ static Variant::Type get_function_argument_type(const StringName &p_function, int p_arg);
+ static int get_function_argument_count(const StringName &p_function, int p_arg);
+ static bool is_function_vararg(const StringName &p_function);
+ static bool is_function_constant(const StringName &p_function);
+
+ static bool function_exists(const StringName &p_function);
+ static void get_function_list(List<StringName> *r_functions);
+ static MethodInfo get_function_info(const StringName &p_function);
+
+ static void register_functions();
+ static void unregister_functions();
};
-#endif // GDSCRIPT_FUNCTIONS_H
+#endif // GDSCRIPT_UTILITY_FUNCTIONS_H
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index 7c8bfcd944..b8e1791467 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -33,7 +33,6 @@
#include "core/core_string_names.h"
#include "core/os/os.h"
#include "gdscript.h"
-#include "gdscript_functions.h"
Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, GDScript *p_script, Variant &self, Variant &static_ref, Variant *p_stack, String &r_error) const {
int address = p_address & ADDR_MASK;
@@ -220,7 +219,9 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
&&OPCODE_CALL, \
&&OPCODE_CALL_RETURN, \
&&OPCODE_CALL_ASYNC, \
- &&OPCODE_CALL_BUILT_IN, \
+ &&OPCODE_CALL_UTILITY, \
+ &&OPCODE_CALL_UTILITY_VALIDATED, \
+ &&OPCODE_CALL_GDSCRIPT_UTILITY, \
&&OPCODE_CALL_BUILTIN_TYPE_VALIDATED, \
&&OPCODE_CALL_SELF_BASE, \
&&OPCODE_CALL_METHOD_BIND, \
@@ -738,7 +739,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
const Variant::ValidatedKeyedSetter setter = _keyed_setters_ptr[index_setter];
bool valid;
- setter(dst, index, value, valid);
+ setter(dst, index, value, &valid);
#ifdef DEBUG_ENABLED
if (!valid) {
@@ -770,7 +771,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
int64_t int_index = *VariantInternal::get_int(index);
bool oob;
- setter(dst, int_index, value, oob);
+ setter(dst, int_index, value, &oob);
#ifdef DEBUG_ENABLED
if (oob) {
@@ -835,9 +836,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#ifdef DEBUG_ENABLED
// Allow better error message in cases where src and dst are the same stack position.
Variant ret;
- getter(src, key, &ret, valid);
+ getter(src, key, &ret, &valid);
#else
- getter(src, key, dst, valid);
+ getter(src, key, dst, &valid);
#endif
#ifdef DEBUG_ENABLED
if (!valid) {
@@ -870,7 +871,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
int64_t int_index = *VariantInternal::get_int(index);
bool oob;
- getter(src, int_index, dst, oob);
+ getter(src, int_index, dst, &oob);
#ifdef DEBUG_ENABLED
if (oob) {
@@ -1292,7 +1293,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GET_INSTRUCTION_ARG(dst, argc);
- constructor(*dst, (const Variant **)argptrs);
+ constructor(dst, (const Variant **)argptrs);
ip += 3;
}
@@ -1653,7 +1654,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
VariantInternal::initialize(ret, Variant::OBJECT);
Object **ret_opaque = VariantInternal::get_object(ret);
method->ptrcall(base_obj, argptrs, ret_opaque);
- VariantInternal::set_object(ret, *ret_opaque);
+ VariantInternal::object_assign(ret, *ret_opaque); // Set so ID is correct too.
#ifdef DEBUG_ENABLED
if (GDScriptLanguage::get_singleton()->profiling) {
@@ -1749,7 +1750,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
- OPCODE(OPCODE_CALL_BUILT_IN) {
+ OPCODE(OPCODE_CALL_UTILITY) {
CHECK_SPACE(3 + instr_arg_count);
ip += instr_arg_count;
@@ -1757,22 +1758,80 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
int argc = _code_ptr[ip + 1];
GD_ERR_BREAK(argc < 0);
- GDScriptFunctions::Function func = GDScriptFunctions::Function(_code_ptr[ip + 2]);
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _global_names_count);
+ StringName function = _global_names_ptr[_code_ptr[ip + 2]];
+
+ Variant **argptrs = instruction_args;
+
+ GET_INSTRUCTION_ARG(dst, argc);
+
+ Callable::CallError err;
+ Variant::call_utility_function(function, dst, (const Variant **)argptrs, argc, err);
+
+#ifdef DEBUG_ENABLED
+ if (err.error != Callable::CallError::CALL_OK) {
+ String methodstr = function;
+ if (dst->get_type() == Variant::STRING) {
+ // Call provided error string.
+ err_text = "Error calling utility function '" + methodstr + "': " + String(*dst);
+ } else {
+ err_text = _get_call_error(err, "utility function '" + methodstr + "'", (const Variant **)argptrs);
+ }
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CALL_UTILITY_VALIDATED) {
+ CHECK_SPACE(3 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _utilities_count);
+ Variant::ValidatedUtilityFunction function = _utilities_ptr[_code_ptr[ip + 2]];
+
+ Variant **argptrs = instruction_args;
+
+ GET_INSTRUCTION_ARG(dst, argc);
+
+ function(dst, (const Variant **)argptrs, argc);
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CALL_GDSCRIPT_UTILITY) {
+ CHECK_SPACE(3 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _gds_utilities_count);
+ GDScriptUtilityFunctions::FunctionPtr function = _gds_utilities_ptr[_code_ptr[ip + 2]];
+
Variant **argptrs = instruction_args;
GET_INSTRUCTION_ARG(dst, argc);
Callable::CallError err;
- GDScriptFunctions::call(func, (const Variant **)argptrs, argc, *dst, err);
+ function(dst, (const Variant **)argptrs, argc, err);
#ifdef DEBUG_ENABLED
if (err.error != Callable::CallError::CALL_OK) {
- String methodstr = GDScriptFunctions::get_func_name(func);
+ // TODO: Add this information in debug.
+ String methodstr = "<unkown function>";
if (dst->get_type() == Variant::STRING) {
- //call provided error string
- err_text = "Error calling built-in function '" + methodstr + "': " + String(*dst);
+ // Call provided error string.
+ err_text = "Error calling GDScript utility function '" + methodstr + "': " + String(*dst);
} else {
- err_text = _get_call_error(err, "built-in function '" + methodstr + "'", (const Variant **)argptrs);
+ err_text = _get_call_error(err, "GDScript utility function '" + methodstr + "'", (const Variant **)argptrs);
}
OPCODE_BREAK;
}
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp
index 668dfd4835..bd2d170e52 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.cpp
+++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp
@@ -723,8 +723,8 @@ Dictionary ExtendGDScriptParser::dump_class_api(const GDScriptParser::ClassNode
} break;
case ClassNode::Member::ENUM: {
Dictionary enum_dict;
- for (int j = 0; j < m.m_enum->values.size(); i++) {
- enum_dict[m.m_enum->values[i].identifier->name] = m.m_enum->values[i].value;
+ for (int j = 0; j < m.m_enum->values.size(); j++) {
+ enum_dict[m.m_enum->values[j].identifier->name] = m.m_enum->values[j].value;
}
Dictionary api;
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp
index 6ddb0d149e..729be237ec 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.cpp
+++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp
@@ -33,6 +33,7 @@
#include "core/config/project_settings.h"
#include "core/io/json.h"
#include "core/os/copymem.h"
+#include "editor/doc_tools.h"
#include "editor/editor_log.h"
#include "editor/editor_node.h"
@@ -212,7 +213,7 @@ Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) {
void GDScriptLanguageProtocol::initialized(const Variant &p_params) {
lsp::GodotCapabilities capabilities;
- DocData *doc = EditorHelp::get_doc_data();
+ DocTools *doc = EditorHelp::get_doc_data();
for (Map<String, DocData::ClassDoc>::Element *E = doc->class_list.front(); E; E = E->next()) {
lsp::GodotNativeClassInfo gdclass;
gdclass.name = E->get().name;
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index f6643d07f9..60668e7b31 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -34,6 +34,7 @@
#include "../gdscript_parser.h"
#include "core/config/project_settings.h"
#include "core/object/script_language.h"
+#include "editor/doc_tools.h"
#include "editor/editor_file_system.h"
#include "editor/editor_help.h"
#include "editor/editor_node.h"
@@ -189,7 +190,7 @@ Error GDScriptWorkspace::initialize() {
return OK;
}
- DocData *doc = EditorHelp::get_doc_data();
+ DocTools *doc = EditorHelp::get_doc_data();
for (Map<String, DocData::ClassDoc>::Element *E = doc->class_list.front(); E; E = E->next()) {
const DocData::ClassDoc &class_data = E->value();
lsp::DocumentSymbol class_symbol;
diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp
index 288fd41c87..1029c53bbf 100644
--- a/modules/gdscript/language_server/lsp.hpp
+++ b/modules/gdscript/language_server/lsp.hpp
@@ -31,9 +31,9 @@
#ifndef GODOT_LSP_H
#define GODOT_LSP_H
+#include "core/doc_data.h"
#include "core/object/class_db.h"
#include "core/templates/list.h"
-#include "editor/doc_data.h"
namespace lsp {
diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp
index 065f01e654..0c4996e9bb 100644
--- a/modules/gdscript/register_types.cpp
+++ b/modules/gdscript/register_types.cpp
@@ -38,6 +38,7 @@
#include "gdscript_analyzer.h"
#include "gdscript_cache.h"
#include "gdscript_tokenizer.h"
+#include "gdscript_utility_functions.h"
#ifdef TESTS_ENABLED
#include "tests/test_gdscript.h"
@@ -112,7 +113,6 @@ static void _editor_init() {
void register_gdscript_types() {
ClassDB::register_class<GDScript>();
- ClassDB::register_virtual_class<GDScriptFunctionState>();
script_language_gd = memnew(GDScriptLanguage);
ScriptServer::register_language(script_language_gd);
@@ -131,6 +131,8 @@ void register_gdscript_types() {
gdscript_translation_parser_plugin.instance();
EditorTranslationParser::get_singleton()->add_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD);
#endif // TOOLS_ENABLED
+
+ GDScriptUtilityFunctions::register_functions();
}
void unregister_gdscript_types() {
@@ -157,6 +159,7 @@ void unregister_gdscript_types() {
GDScriptParser::cleanup();
GDScriptAnalyzer::cleanup();
+ GDScriptUtilityFunctions::unregister_functions();
}
#ifdef TESTS_ENABLED
diff --git a/modules/glslang/SCsub b/modules/glslang/SCsub
index 58c033c75d..182272ffc7 100644
--- a/modules/glslang/SCsub
+++ b/modules/glslang/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_glslang = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
if env["builtin_glslang"]:
thirdparty_dir = "#thirdparty/glslang/"
thirdparty_sources = [
@@ -70,7 +73,16 @@ if env["builtin_glslang"]:
env_thirdparty = env_glslang.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
+
+
+# Godot source files
+
+module_obj = []
+
+env_glslang.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
-# Godot's own source files
-env_glslang.add_source_files(env.modules_sources, "*.cpp")
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp
index 0e6ec7f520..cda217acf0 100644
--- a/modules/gridmap/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/grid_map_editor_plugin.cpp
@@ -1030,11 +1030,15 @@ void GridMapEditor::_notification(int p_what) {
for (int i = 0; i < 3; i++) {
grid[i] = RS::get_singleton()->mesh_create();
grid_instance[i] = RS::get_singleton()->instance_create2(grid[i], get_tree()->get_root()->get_world_3d()->get_scenario());
+ RenderingServer::get_singleton()->instance_set_layer_mask(grid_instance[i], 1 << Node3DEditorViewport::MISC_TOOL_LAYER);
selection_level_instance[i] = RenderingServer::get_singleton()->instance_create2(selection_level_mesh[i], get_tree()->get_root()->get_world_3d()->get_scenario());
+ RenderingServer::get_singleton()->instance_set_layer_mask(selection_level_instance[i], 1 << Node3DEditorViewport::MISC_TOOL_LAYER);
}
selection_instance = RenderingServer::get_singleton()->instance_create2(selection_mesh, get_tree()->get_root()->get_world_3d()->get_scenario());
+ RenderingServer::get_singleton()->instance_set_layer_mask(selection_instance, 1 << Node3DEditorViewport::MISC_TOOL_LAYER);
paste_instance = RenderingServer::get_singleton()->instance_create2(paste_mesh, get_tree()->get_root()->get_world_3d()->get_scenario());
+ RenderingServer::get_singleton()->instance_set_layer_mask(paste_instance, 1 << Node3DEditorViewport::MISC_TOOL_LAYER);
_update_selection_transform();
_update_paste_indicator();
diff --git a/modules/jpg/SCsub b/modules/jpg/SCsub
index 8ee8e6dd6e..7c6ceeea29 100644
--- a/modules/jpg/SCsub
+++ b/modules/jpg/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_jpg = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
# Not unbundled for now as they are not commonly available as shared library
thirdparty_dir = "#thirdparty/jpeg-compressor/"
thirdparty_sources = [
@@ -17,7 +20,15 @@ env_jpg.Prepend(CPPPATH=[thirdparty_dir])
env_thirdparty = env_jpg.Clone()
env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.modules_sources += thirdparty_obj
+
+# Godot source files
+
+module_obj = []
+
+env_jpg.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
-# Godot's own source files
-env_jpg.add_source_files(env.modules_sources, "*.cpp")
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp
index 1ef991841b..66995382e7 100644
--- a/modules/lightmapper_rd/lightmapper_rd.cpp
+++ b/modules/lightmapper_rd/lightmapper_rd.cpp
@@ -447,7 +447,6 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i
if (cell != last_cell) {
//cell changed, update pointer to indices
giw[cell * 2 + 1] = i;
- last_cell = cell;
solidw[cell] = true;
}
tiw[i] = triangle_sort[i].triangle_index;
@@ -543,7 +542,7 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i
tf.width = grid_size;
tf.height = grid_size;
tf.depth = grid_size;
- tf.type = RD::TEXTURE_TYPE_3D;
+ tf.texture_type = RD::TEXTURE_TYPE_3D;
tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT;
Vector<Vector<uint8_t>> texdata;
@@ -695,7 +694,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
tf.width = atlas_size.width;
tf.height = atlas_size.height;
tf.array_layers = atlas_slices;
- tf.type = RD::TEXTURE_TYPE_2D_ARRAY;
+ tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT;
tf.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
@@ -826,84 +825,84 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
{
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 1;
u.ids.push_back(vertex_buffer);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 2;
u.ids.push_back(triangle_buffer);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 3;
u.ids.push_back(box_buffer);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 4;
u.ids.push_back(triangle_cell_indices_buffer);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 5;
u.ids.push_back(lights_buffer);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 6;
u.ids.push_back(seams_buffer);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 7;
u.ids.push_back(probe_positions_buffer);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 8;
u.ids.push_back(grid_texture);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 9;
u.ids.push_back(grid_texture_sdf);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 10;
u.ids.push_back(albedo_array_tex);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 11;
u.ids.push_back(emission_array_tex);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_SAMPLER;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
u.binding = 12;
u.ids.push_back(sampler);
base_uniforms.push_back(u);
@@ -917,7 +916,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
tf.width = atlas_size.width;
tf.height = atlas_size.height;
tf.depth = 1;
- tf.type = RD::TEXTURE_TYPE_2D;
+ tf.texture_type = RD::TEXTURE_TYPE_2D;
tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
tf.format = RD::DATA_FORMAT_D32_SFLOAT;
@@ -1049,14 +1048,14 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
{
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_IMAGE;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 0;
u.ids.push_back(position_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_IMAGE;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 1;
u.ids.push_back(unocclude_tex); //will be unused
uniforms.push_back(u);
@@ -1089,42 +1088,42 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
{
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_IMAGE;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 0;
u.ids.push_back(light_source_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 1;
u.ids.push_back(light_dest_tex); //will be unused
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 2;
u.ids.push_back(position_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 3;
u.ids.push_back(normal_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_IMAGE;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 4;
u.ids.push_back(light_accum_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_IMAGE;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 5;
u.ids.push_back(light_primary_dynamic_tex);
uniforms.push_back(u);
@@ -1169,49 +1168,49 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
{
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_IMAGE;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 0;
u.ids.push_back(light_dest_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 1;
u.ids.push_back(light_source_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 2;
u.ids.push_back(position_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 3;
u.ids.push_back(normal_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_IMAGE;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 4;
u.ids.push_back(light_accum_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_IMAGE;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 5;
u.ids.push_back(unocclude_tex); //reuse unocclude tex
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 6;
u.ids.push_back(light_environment_tex); //reuse unocclude tex
uniforms.push_back(u);
@@ -1317,28 +1316,28 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
{
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 0;
u.ids.push_back(light_probe_buffer);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 1;
u.ids.push_back(light_dest_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 2;
u.ids.push_back(light_primary_dynamic_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 3;
u.ids.push_back(light_environment_tex);
uniforms.push_back(u);
@@ -1463,14 +1462,14 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
{
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_IMAGE;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 0;
u.ids.push_back(light_accum_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 1;
u.ids.push_back(light_accum_tex2);
uniforms.push_back(u);
@@ -1554,7 +1553,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
{
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 0;
u.ids.push_back(light_accum_tex2);
uniforms.push_back(u);
diff --git a/modules/mbedtls/SCsub b/modules/mbedtls/SCsub
index 5f5d25a3ee..4fcbe8fb43 100755
--- a/modules/mbedtls/SCsub
+++ b/modules/mbedtls/SCsub
@@ -5,8 +5,11 @@ Import("env_modules")
env_mbed_tls = env_modules.Clone()
+# Thirdparty source files
+
+thirdparty_obj = []
+
if env["builtin_mbedtls"]:
- # Thirdparty source files
thirdparty_sources = [
"aes.c",
"aesni.c",
@@ -96,7 +99,21 @@ if env["builtin_mbedtls"]:
env_thirdparty = env_mbed_tls.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
+
+
+# Godot source files
+
+module_obj = []
+
+env_mbed_tls.add_source_files(module_obj, "*.cpp")
+
+if env["tests"]:
+ env_mbed_tls.Append(CPPDEFINES=["TESTS_ENABLED"])
+ env_mbed_tls.add_source_files(module_obj, "./tests/*.cpp")
+
+env.modules_sources += module_obj
-# Module sources
-env_mbed_tls.add_source_files(env.modules_sources, "*.cpp")
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/mbedtls/crypto_mbedtls.cpp b/modules/mbedtls/crypto_mbedtls.cpp
index bec792450a..4ea38ebd60 100644
--- a/modules/mbedtls/crypto_mbedtls.cpp
+++ b/modules/mbedtls/crypto_mbedtls.cpp
@@ -44,6 +44,7 @@
#define PEM_END_CRT "-----END CERTIFICATE-----\n"
#include <mbedtls/debug.h>
+#include <mbedtls/md.h>
#include <mbedtls/pem.h>
CryptoKey *CryptoKeyMbedTLS::create() {
@@ -186,6 +187,68 @@ Error X509CertificateMbedTLS::save(String p_path) {
return OK;
}
+bool HMACContextMbedTLS::is_md_type_allowed(mbedtls_md_type_t p_md_type) {
+ switch (p_md_type) {
+ case MBEDTLS_MD_SHA1:
+ case MBEDTLS_MD_SHA256:
+ return true;
+ default:
+ return false;
+ }
+}
+
+HMACContext *HMACContextMbedTLS::create() {
+ return memnew(HMACContextMbedTLS);
+}
+
+Error HMACContextMbedTLS::start(HashingContext::HashType p_hash_type, PackedByteArray p_key) {
+ ERR_FAIL_COND_V_MSG(ctx != nullptr, ERR_FILE_ALREADY_IN_USE, "HMACContext already started.");
+
+ // HMAC keys can be any size.
+ ERR_FAIL_COND_V_MSG(p_key.empty(), ERR_INVALID_PARAMETER, "Key must not be empty.");
+
+ hash_type = p_hash_type;
+ mbedtls_md_type_t ht = CryptoMbedTLS::md_type_from_hashtype(p_hash_type, hash_len);
+
+ bool allowed = HMACContextMbedTLS::is_md_type_allowed(ht);
+ ERR_FAIL_COND_V_MSG(!allowed, ERR_INVALID_PARAMETER, "Unsupported hash type.");
+
+ ctx = memalloc(sizeof(mbedtls_md_context_t));
+ mbedtls_md_init((mbedtls_md_context_t *)ctx);
+
+ mbedtls_md_setup((mbedtls_md_context_t *)ctx, mbedtls_md_info_from_type((mbedtls_md_type_t)ht), 1);
+ int ret = mbedtls_md_hmac_starts((mbedtls_md_context_t *)ctx, (const uint8_t *)p_key.ptr(), (size_t)p_key.size());
+ return ret ? FAILED : OK;
+}
+
+Error HMACContextMbedTLS::update(PackedByteArray p_data) {
+ ERR_FAIL_COND_V_MSG(ctx == nullptr, ERR_INVALID_DATA, "Start must be called before update.");
+
+ ERR_FAIL_COND_V_MSG(p_data.empty(), ERR_INVALID_PARAMETER, "Src must not be empty.");
+
+ int ret = mbedtls_md_hmac_update((mbedtls_md_context_t *)ctx, (const uint8_t *)p_data.ptr(), (size_t)p_data.size());
+ return ret ? FAILED : OK;
+}
+
+PackedByteArray HMACContextMbedTLS::finish() {
+ ERR_FAIL_COND_V_MSG(ctx == nullptr, PackedByteArray(), "Start must be called before finish.");
+ ERR_FAIL_COND_V_MSG(hash_len == 0, PackedByteArray(), "Unsupported hash type.");
+
+ PackedByteArray out;
+ out.resize(hash_len);
+
+ unsigned char *out_ptr = (unsigned char *)out.ptrw();
+ int ret = mbedtls_md_hmac_finish((mbedtls_md_context_t *)ctx, out_ptr);
+
+ mbedtls_md_free((mbedtls_md_context_t *)ctx);
+ memfree((mbedtls_md_context_t *)ctx);
+ ctx = nullptr;
+ hash_len = 0;
+
+ ERR_FAIL_COND_V_MSG(ret, PackedByteArray(), "Error received while finishing HMAC");
+ return out;
+}
+
Crypto *CryptoMbedTLS::create() {
return memnew(CryptoMbedTLS);
}
@@ -199,6 +262,7 @@ void CryptoMbedTLS::initialize_crypto() {
Crypto::_load_default_certificates = load_default_certificates;
X509CertificateMbedTLS::make_default();
CryptoKeyMbedTLS::make_default();
+ HMACContextMbedTLS::make_default();
}
void CryptoMbedTLS::finalize_crypto() {
@@ -210,6 +274,7 @@ void CryptoMbedTLS::finalize_crypto() {
}
X509CertificateMbedTLS::finalize();
CryptoKeyMbedTLS::finalize();
+ HMACContextMbedTLS::finalize();
}
CryptoMbedTLS::CryptoMbedTLS() {
@@ -313,7 +378,7 @@ PackedByteArray CryptoMbedTLS::generate_random_bytes(int p_bytes) {
return out;
}
-mbedtls_md_type_t CryptoMbedTLS::_md_type_from_hashtype(HashingContext::HashType p_hash_type, int &r_size) {
+mbedtls_md_type_t CryptoMbedTLS::md_type_from_hashtype(HashingContext::HashType p_hash_type, int &r_size) {
switch (p_hash_type) {
case HashingContext::HASH_MD5:
r_size = 16;
@@ -332,7 +397,7 @@ mbedtls_md_type_t CryptoMbedTLS::_md_type_from_hashtype(HashingContext::HashType
Vector<uint8_t> CryptoMbedTLS::sign(HashingContext::HashType p_hash_type, Vector<uint8_t> p_hash, Ref<CryptoKey> p_key) {
int size;
- mbedtls_md_type_t type = _md_type_from_hashtype(p_hash_type, size);
+ mbedtls_md_type_t type = CryptoMbedTLS::md_type_from_hashtype(p_hash_type, size);
ERR_FAIL_COND_V_MSG(type == MBEDTLS_MD_NONE, Vector<uint8_t>(), "Invalid hash type.");
ERR_FAIL_COND_V_MSG(p_hash.size() != size, Vector<uint8_t>(), "Invalid hash provided. Size must be " + itos(size));
Ref<CryptoKeyMbedTLS> key = static_cast<Ref<CryptoKeyMbedTLS>>(p_key);
@@ -350,7 +415,7 @@ Vector<uint8_t> CryptoMbedTLS::sign(HashingContext::HashType p_hash_type, Vector
bool CryptoMbedTLS::verify(HashingContext::HashType p_hash_type, Vector<uint8_t> p_hash, Vector<uint8_t> p_signature, Ref<CryptoKey> p_key) {
int size;
- mbedtls_md_type_t type = _md_type_from_hashtype(p_hash_type, size);
+ mbedtls_md_type_t type = CryptoMbedTLS::md_type_from_hashtype(p_hash_type, size);
ERR_FAIL_COND_V_MSG(type == MBEDTLS_MD_NONE, false, "Invalid hash type.");
ERR_FAIL_COND_V_MSG(p_hash.size() != size, false, "Invalid hash provided. Size must be " + itos(size));
Ref<CryptoKeyMbedTLS> key = static_cast<Ref<CryptoKeyMbedTLS>>(p_key);
diff --git a/modules/mbedtls/crypto_mbedtls.h b/modules/mbedtls/crypto_mbedtls.h
index e40ca08643..990f8ae578 100644
--- a/modules/mbedtls/crypto_mbedtls.h
+++ b/modules/mbedtls/crypto_mbedtls.h
@@ -101,12 +101,31 @@ public:
friend class SSLContextMbedTLS;
};
+class HMACContextMbedTLS : public HMACContext {
+private:
+ HashingContext::HashType hash_type;
+ int hash_len = 0;
+ void *ctx = nullptr;
+
+public:
+ static HMACContext *create();
+ static void make_default() { HMACContext::_create = create; }
+ static void finalize() { HMACContext::_create = nullptr; }
+
+ static bool is_md_type_allowed(mbedtls_md_type_t p_md_type);
+
+ virtual Error start(HashingContext::HashType p_hash_type, PackedByteArray p_key);
+ virtual Error update(PackedByteArray p_data);
+ virtual PackedByteArray finish();
+
+ HMACContextMbedTLS() {}
+};
+
class CryptoMbedTLS : public Crypto {
private:
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
static X509CertificateMbedTLS *default_certs;
- mbedtls_md_type_t _md_type_from_hashtype(HashingContext::HashType p_hash_type, int &r_size);
public:
static Crypto *create();
@@ -114,6 +133,7 @@ public:
static void finalize_crypto();
static X509CertificateMbedTLS *get_default_certificates();
static void load_default_certificates(String p_path);
+ static mbedtls_md_type_t md_type_from_hashtype(HashingContext::HashType p_hash_type, int &r_size);
virtual PackedByteArray generate_random_bytes(int p_bytes);
virtual Ref<CryptoKey> generate_rsa(int p_bytes);
diff --git a/modules/mbedtls/register_types.cpp b/modules/mbedtls/register_types.cpp
index 84a27c29bd..59abbac8ec 100644
--- a/modules/mbedtls/register_types.cpp
+++ b/modules/mbedtls/register_types.cpp
@@ -35,6 +35,10 @@
#include "packet_peer_mbed_dtls.h"
#include "stream_peer_mbedtls.h"
+#ifdef TESTS_ENABLED
+#include "tests/test_crypto_mbedtls.h"
+#endif
+
void register_mbedtls_types() {
CryptoMbedTLS::initialize_crypto();
StreamPeerMbedTLS::initialize_ssl();
diff --git a/modules/mbedtls/tests/test_crypto_mbedtls.cpp b/modules/mbedtls/tests/test_crypto_mbedtls.cpp
new file mode 100644
index 0000000000..c5a27aa794
--- /dev/null
+++ b/modules/mbedtls/tests/test_crypto_mbedtls.cpp
@@ -0,0 +1,62 @@
+/*************************************************************************/
+/* test_crypto_mbedtls.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "modules/mbedtls/tests/test_crypto_mbedtls.h"
+
+#include "modules/mbedtls/crypto_mbedtls.h"
+#include "tests/test_macros.h"
+
+namespace TestCryptoMbedTLS {
+
+void hmac_digest_test(HashingContext::HashType ht, String expected_hex) {
+ CryptoMbedTLS crypto;
+ PackedByteArray key = String("supersecretkey").to_utf8_buffer();
+ PackedByteArray msg = String("Return of the MAC!").to_utf8_buffer();
+ PackedByteArray digest = crypto.hmac_digest(ht, key, msg);
+ String hex = String::hex_encode_buffer(digest.ptr(), digest.size());
+ CHECK(hex == expected_hex);
+}
+
+void hmac_context_digest_test(HashingContext::HashType ht, String expected_hex) {
+ HMACContextMbedTLS ctx;
+ PackedByteArray key = String("supersecretkey").to_utf8_buffer();
+ PackedByteArray msg1 = String("Return of ").to_utf8_buffer();
+ PackedByteArray msg2 = String("the MAC!").to_utf8_buffer();
+ Error err = ctx.start(ht, key);
+ CHECK(err == OK);
+ err = ctx.update(msg1);
+ CHECK(err == OK);
+ err = ctx.update(msg2);
+ CHECK(err == OK);
+ PackedByteArray digest = ctx.finish();
+ String hex = String::hex_encode_buffer(digest.ptr(), digest.size());
+ CHECK(hex == expected_hex);
+}
+} // namespace TestCryptoMbedTLS
diff --git a/modules/mbedtls/tests/test_crypto_mbedtls.h b/modules/mbedtls/tests/test_crypto_mbedtls.h
new file mode 100644
index 0000000000..7b1e062239
--- /dev/null
+++ b/modules/mbedtls/tests/test_crypto_mbedtls.h
@@ -0,0 +1,61 @@
+/*************************************************************************/
+/* test_crypto_mbedtls.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TEST_CRYPTO_MBEDTLS_H
+#define TEST_CRYPTO_MBEDTLS_H
+
+#include "core/crypto/hashing_context.h"
+
+#include "tests/test_macros.h"
+
+namespace TestCryptoMbedTLS {
+
+void hmac_digest_test(HashingContext::HashType ht, String expected_hex);
+
+TEST_CASE("[CryptoMbedTLS] HMAC digest") {
+ // SHA-256
+ hmac_digest_test(HashingContext::HashType::HASH_SHA256, "fe442023f8a7d36a810e1e7cd8a8e2816457f350a008fbf638296afa12085e59");
+
+ // SHA-1
+ hmac_digest_test(HashingContext::HashType::HASH_SHA1, "a0ac4cd68a2f4812c355983d94e8d025afe7dddf");
+}
+
+void hmac_context_digest_test(HashingContext::HashType ht, String expected_hex);
+
+TEST_CASE("[HMACContext] HMAC digest") {
+ // SHA-256
+ hmac_context_digest_test(HashingContext::HashType::HASH_SHA256, "fe442023f8a7d36a810e1e7cd8a8e2816457f350a008fbf638296afa12085e59");
+
+ // SHA-1
+ hmac_context_digest_test(HashingContext::HashType::HASH_SHA1, "a0ac4cd68a2f4812c355983d94e8d025afe7dddf");
+}
+} // namespace TestCryptoMbedTLS
+
+#endif // TEST_CRYPTO_MBEDTLS_H
diff --git a/modules/meshoptimizer/SCsub b/modules/meshoptimizer/SCsub
new file mode 100644
index 0000000000..3f86bb4f00
--- /dev/null
+++ b/modules/meshoptimizer/SCsub
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_meshoptimizer = env_modules.Clone()
+
+# Thirdparty source files
+
+thirdparty_obj = []
+
+thirdparty_dir = "#thirdparty/meshoptimizer/"
+thirdparty_sources = [
+ "allocator.cpp",
+ "clusterizer.cpp",
+ "indexcodec.cpp",
+ "indexgenerator.cpp",
+ "overdrawanalyzer.cpp",
+ "overdrawoptimizer.cpp",
+ "simplifier.cpp",
+ "spatialorder.cpp",
+ "stripifier.cpp",
+ "vcacheanalyzer.cpp",
+ "vcacheoptimizer.cpp",
+ "vertexcodec.cpp",
+ "vertexfilter.cpp",
+ "vfetchanalyzer.cpp",
+ "vfetchoptimizer.cpp",
+]
+thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+env_thirdparty = env_meshoptimizer.Clone()
+env_thirdparty.disable_warnings()
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.modules_sources += thirdparty_obj
+
+# Godot source files
+
+module_obj = []
+
+env_meshoptimizer.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
+
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/meshoptimizer/config.py b/modules/meshoptimizer/config.py
new file mode 100644
index 0000000000..82e4e43397
--- /dev/null
+++ b/modules/meshoptimizer/config.py
@@ -0,0 +1,7 @@
+def can_build(env, platform):
+ # Having this on release by default, it's small and a lot of users like to do procedural stuff
+ return True
+
+
+def configure(env):
+ pass
diff --git a/modules/meshoptimizer/register_types.cpp b/modules/meshoptimizer/register_types.cpp
new file mode 100644
index 0000000000..26c8c6ab72
--- /dev/null
+++ b/modules/meshoptimizer/register_types.cpp
@@ -0,0 +1,43 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "register_types.h"
+#include "scene/resources/surface_tool.h"
+#include "thirdparty/meshoptimizer/meshoptimizer.h"
+
+void register_meshoptimizer_types() {
+ SurfaceTool::optimize_vertex_cache_func = meshopt_optimizeVertexCache;
+ SurfaceTool::simplify_func = meshopt_simplify;
+}
+
+void unregister_meshoptimizer_types() {
+ SurfaceTool::optimize_vertex_cache_func = nullptr;
+ SurfaceTool::simplify_func = nullptr;
+}
diff --git a/modules/meshoptimizer/register_types.h b/modules/meshoptimizer/register_types.h
new file mode 100644
index 0000000000..42b3a76a85
--- /dev/null
+++ b/modules/meshoptimizer/register_types.h
@@ -0,0 +1,37 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MESHOPTIMIZER_REGISTER_TYPES_H
+#define MESHOPTIMIZER_REGISTER_TYPES_H
+
+void register_meshoptimizer_types();
+void unregister_meshoptimizer_types();
+
+#endif // PVR_REGISTER_TYPES_H
diff --git a/modules/minimp3/SCsub b/modules/minimp3/SCsub
new file mode 100644
index 0000000000..f4d1605d55
--- /dev/null
+++ b/modules/minimp3/SCsub
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_minimp3 = env_modules.Clone()
+
+thirdparty_dir = "#thirdparty/minimp3/"
+
+# Treat minimp3 headers as system headers to avoid raising warnings. Not supported on MSVC.
+if not env.msvc:
+ env_minimp3.Append(CPPFLAGS=["-isystem", Dir(thirdparty_dir).path])
+else:
+ env_minimp3.Prepend(CPPPATH=[thirdparty_dir])
+
+# Godot's own source files
+env_minimp3.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp
new file mode 100644
index 0000000000..b20c043e0c
--- /dev/null
+++ b/modules/minimp3/audio_stream_mp3.cpp
@@ -0,0 +1,228 @@
+/*************************************************************************/
+/* audio_stream_mp3.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#define MINIMP3_ONLY_MP3
+#define MINIMP3_FLOAT_OUTPUT
+#define MINIMP3_IMPLEMENTATION
+#define MINIMP3_NO_STDIO
+
+#include "audio_stream_mp3.h"
+
+#include "core/os/file_access.h"
+
+void AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) {
+ ERR_FAIL_COND(!active);
+
+ int todo = p_frames;
+
+ while (todo && active) {
+ mp3dec_frame_info_t frame_info;
+ mp3d_sample_t *buf_frame = nullptr;
+
+ int samples_mixed = mp3dec_ex_read_frame(mp3d, &buf_frame, &frame_info, mp3_stream->channels);
+
+ if (samples_mixed) {
+ p_buffer[p_frames - todo] = AudioFrame(buf_frame[0], buf_frame[samples_mixed - 1]);
+ --todo;
+ ++frames_mixed;
+ }
+
+ else {
+ //EOF
+ if (mp3_stream->loop) {
+ seek(mp3_stream->loop_offset);
+ loops++;
+ } else {
+ //fill remainder with silence
+ for (int i = p_frames - todo; i < p_frames; i++) {
+ p_buffer[i] = AudioFrame(0, 0);
+ }
+ active = false;
+ todo = 0;
+ }
+ }
+ }
+}
+
+float AudioStreamPlaybackMP3::get_stream_sampling_rate() {
+ return mp3_stream->sample_rate;
+}
+
+void AudioStreamPlaybackMP3::start(float p_from_pos) {
+ active = true;
+ seek(p_from_pos);
+ loops = 0;
+ _begin_resample();
+}
+
+void AudioStreamPlaybackMP3::stop() {
+ active = false;
+}
+
+bool AudioStreamPlaybackMP3::is_playing() const {
+ return active;
+}
+
+int AudioStreamPlaybackMP3::get_loop_count() const {
+ return loops;
+}
+
+float AudioStreamPlaybackMP3::get_playback_position() const {
+ return float(frames_mixed) / mp3_stream->sample_rate;
+}
+
+void AudioStreamPlaybackMP3::seek(float p_time) {
+ if (!active)
+ return;
+
+ if (p_time >= mp3_stream->get_length()) {
+ p_time = 0;
+ }
+
+ frames_mixed = uint32_t(mp3_stream->sample_rate * p_time);
+ mp3dec_ex_seek(mp3d, frames_mixed * mp3_stream->channels);
+}
+
+AudioStreamPlaybackMP3::~AudioStreamPlaybackMP3() {
+ if (mp3d) {
+ mp3dec_ex_close(mp3d);
+ memfree(mp3d);
+ }
+}
+
+Ref<AudioStreamPlayback> AudioStreamMP3::instance_playback() {
+ Ref<AudioStreamPlaybackMP3> mp3s;
+
+ ERR_FAIL_COND_V(data == nullptr, mp3s);
+
+ mp3s.instance();
+ mp3s->mp3_stream = Ref<AudioStreamMP3>(this);
+ mp3s->mp3d = (mp3dec_ex_t *)memalloc(sizeof(mp3dec_ex_t));
+
+ int errorcode = mp3dec_ex_open_buf(mp3s->mp3d, (const uint8_t *)data, data_len, MP3D_SEEK_TO_SAMPLE);
+
+ mp3s->frames_mixed = 0;
+ mp3s->active = false;
+ mp3s->loops = 0;
+
+ if (errorcode) {
+ ERR_FAIL_COND_V(errorcode, Ref<AudioStreamPlaybackMP3>());
+ }
+
+ return mp3s;
+}
+
+String AudioStreamMP3::get_stream_name() const {
+ return ""; //return stream_name;
+}
+
+void AudioStreamMP3::clear_data() {
+ if (data) {
+ memfree(data);
+ data = nullptr;
+ data_len = 0;
+ }
+}
+
+void AudioStreamMP3::set_data(const Vector<uint8_t> &p_data) {
+ int src_data_len = p_data.size();
+ const uint8_t *src_datar = p_data.ptr();
+
+ mp3dec_ex_t mp3d;
+ mp3dec_ex_open_buf(&mp3d, src_datar, src_data_len, MP3D_SEEK_TO_SAMPLE);
+
+ channels = mp3d.info.channels;
+ sample_rate = mp3d.info.hz;
+ length = float(mp3d.samples) / (sample_rate * float(channels));
+
+ mp3dec_ex_close(&mp3d);
+
+ clear_data();
+
+ data = memalloc(src_data_len);
+ copymem(data, src_datar, src_data_len);
+ data_len = src_data_len;
+}
+
+Vector<uint8_t> AudioStreamMP3::get_data() const {
+ Vector<uint8_t> vdata;
+
+ if (data_len && data) {
+ vdata.resize(data_len);
+ {
+ uint8_t *w = vdata.ptrw();
+ copymem(w, data, data_len);
+ }
+ }
+
+ return vdata;
+}
+
+void AudioStreamMP3::set_loop(bool p_enable) {
+ loop = p_enable;
+}
+
+bool AudioStreamMP3::has_loop() const {
+ return loop;
+}
+
+void AudioStreamMP3::set_loop_offset(float p_seconds) {
+ loop_offset = p_seconds;
+}
+
+float AudioStreamMP3::get_loop_offset() const {
+ return loop_offset;
+}
+
+float AudioStreamMP3::get_length() const {
+ return length;
+}
+
+void AudioStreamMP3::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamMP3::set_data);
+ ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamMP3::get_data);
+
+ ClassDB::bind_method(D_METHOD("set_loop", "enable"), &AudioStreamMP3::set_loop);
+ ClassDB::bind_method(D_METHOD("has_loop"), &AudioStreamMP3::has_loop);
+
+ ClassDB::bind_method(D_METHOD("set_loop_offset", "seconds"), &AudioStreamMP3::set_loop_offset);
+ ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamMP3::get_loop_offset);
+
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_data", "get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_loop", "has_loop");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_loop_offset", "get_loop_offset");
+}
+
+AudioStreamMP3::AudioStreamMP3() {
+}
+
+AudioStreamMP3::~AudioStreamMP3() {
+ clear_data();
+}
diff --git a/modules/minimp3/audio_stream_mp3.h b/modules/minimp3/audio_stream_mp3.h
new file mode 100644
index 0000000000..8d67190ac5
--- /dev/null
+++ b/modules/minimp3/audio_stream_mp3.h
@@ -0,0 +1,110 @@
+/*************************************************************************/
+/* audio_stream_mp3.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef AUDIO_STREAM_MP3_H
+#define AUDIO_STREAM_MP3_H
+
+#include "core/io/resource_loader.h"
+#include "servers/audio/audio_stream.h"
+
+#include "minimp3_ex.h"
+
+class AudioStreamMP3;
+
+class AudioStreamPlaybackMP3 : public AudioStreamPlaybackResampled {
+ GDCLASS(AudioStreamPlaybackMP3, AudioStreamPlaybackResampled);
+
+ mp3dec_ex_t *mp3d = nullptr;
+ uint32_t frames_mixed = 0;
+ bool active = false;
+ int loops = 0;
+
+ friend class AudioStreamMP3;
+
+ Ref<AudioStreamMP3> mp3_stream;
+
+protected:
+ virtual void _mix_internal(AudioFrame *p_buffer, int p_frames) override;
+ virtual float get_stream_sampling_rate() override;
+
+public:
+ virtual void start(float p_from_pos = 0.0) override;
+ virtual void stop() override;
+ virtual bool is_playing() const override;
+
+ virtual int get_loop_count() const override; //times it looped
+
+ virtual float get_playback_position() const override;
+ virtual void seek(float p_time) override;
+
+ AudioStreamPlaybackMP3() {}
+ ~AudioStreamPlaybackMP3();
+};
+
+class AudioStreamMP3 : public AudioStream {
+ GDCLASS(AudioStreamMP3, AudioStream);
+ OBJ_SAVE_TYPE(AudioStream) //children are all saved as AudioStream, so they can be exchanged
+ RES_BASE_EXTENSION("mp3str");
+
+ friend class AudioStreamPlaybackMP3;
+
+ void *data = nullptr;
+ uint32_t data_len = 0;
+
+ float sample_rate = 1;
+ int channels = 1;
+ float length = 0;
+ bool loop = false;
+ float loop_offset = 0;
+ void clear_data();
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_loop(bool p_enable);
+ bool has_loop() const;
+
+ void set_loop_offset(float p_seconds);
+ float get_loop_offset() const;
+
+ virtual Ref<AudioStreamPlayback> instance_playback() override;
+ virtual String get_stream_name() const override;
+
+ void set_data(const Vector<uint8_t> &p_data);
+ Vector<uint8_t> get_data() const;
+
+ virtual float get_length() const override;
+
+ AudioStreamMP3();
+ virtual ~AudioStreamMP3();
+};
+
+#endif // AUDIO_STREAM_MP3_H
diff --git a/modules/minimp3/config.py b/modules/minimp3/config.py
new file mode 100644
index 0000000000..bd35d099b9
--- /dev/null
+++ b/modules/minimp3/config.py
@@ -0,0 +1,16 @@
+def can_build(env, platform):
+ return True
+
+
+def configure(env):
+ pass
+
+
+def get_doc_classes():
+ return [
+ "AudioStreamMP3",
+ ]
+
+
+def get_doc_path():
+ return "doc_classes"
diff --git a/modules/minimp3/doc_classes/AudioStreamMP3.xml b/modules/minimp3/doc_classes/AudioStreamMP3.xml
new file mode 100644
index 0000000000..92e777ca0f
--- /dev/null
+++ b/modules/minimp3/doc_classes/AudioStreamMP3.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="AudioStreamMP3" inherits="AudioStream" version="4.0">
+ <brief_description>
+ MP3 audio stream driver.
+ </brief_description>
+ <description>
+ MP3 audio stream driver.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <members>
+ <member name="data" type="PackedByteArray" setter="set_data" getter="get_data" default="PackedByteArray( )">
+ Contains the audio data in bytes.
+ </member>
+ <member name="loop" type="bool" setter="set_loop" getter="has_loop" default="false">
+ If [code]true[/code], the stream will automatically loop when it reaches the end.
+ </member>
+ <member name="loop_offset" type="float" setter="set_loop_offset" getter="get_loop_offset" default="0.0">
+ Time in seconds at which the stream starts after being looped.
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/modules/minimp3/register_types.cpp b/modules/minimp3/register_types.cpp
new file mode 100644
index 0000000000..2c648b8efe
--- /dev/null
+++ b/modules/minimp3/register_types.cpp
@@ -0,0 +1,52 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "register_types.h"
+
+#include "audio_stream_mp3.h"
+
+#ifdef TOOLS_ENABLED
+#include "core/config/engine.h"
+#include "resource_importer_mp3.h"
+#endif
+
+void register_minimp3_types() {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ Ref<ResourceImporterMP3> mp3_import;
+ mp3_import.instance();
+ ResourceFormatImporter::get_singleton()->add_importer(mp3_import);
+ }
+#endif
+ ClassDB::register_class<AudioStreamMP3>();
+}
+
+void unregister_minimp3_types() {
+}
diff --git a/modules/minimp3/register_types.h b/modules/minimp3/register_types.h
new file mode 100644
index 0000000000..0d841e4987
--- /dev/null
+++ b/modules/minimp3/register_types.h
@@ -0,0 +1,37 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MINIMP3_REGISTER_TYPES_H
+#define MINIMP3_REGISTER_TYPES_H
+
+void register_minimp3_types();
+void unregister_minimp3_types();
+
+#endif // MINIMP3_REGISTER_TYPES_H
diff --git a/modules/minimp3/resource_importer_mp3.cpp b/modules/minimp3/resource_importer_mp3.cpp
new file mode 100644
index 0000000000..82e536a755
--- /dev/null
+++ b/modules/minimp3/resource_importer_mp3.cpp
@@ -0,0 +1,104 @@
+/*************************************************************************/
+/* resource_importer_mp3.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "resource_importer_mp3.h"
+
+#include "core/io/resource_saver.h"
+#include "core/os/file_access.h"
+#include "scene/resources/texture.h"
+
+String ResourceImporterMP3::get_importer_name() const {
+ return "mp3";
+}
+
+String ResourceImporterMP3::get_visible_name() const {
+ return "MP3";
+}
+
+void ResourceImporterMP3::get_recognized_extensions(List<String> *p_extensions) const {
+ p_extensions->push_back("mp3");
+}
+
+String ResourceImporterMP3::get_save_extension() const {
+ return "mp3str";
+}
+
+String ResourceImporterMP3::get_resource_type() const {
+ return "AudioStreamMP3";
+}
+
+bool ResourceImporterMP3::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
+ return true;
+}
+
+int ResourceImporterMP3::get_preset_count() const {
+ return 0;
+}
+
+String ResourceImporterMP3::get_preset_name(int p_idx) const {
+ return String();
+}
+
+void ResourceImporterMP3::get_import_options(List<ImportOption> *r_options, int p_preset) const {
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "loop_offset"), 0));
+}
+
+Error ResourceImporterMP3::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
+ bool loop = p_options["loop"];
+ float loop_offset = p_options["loop_offset"];
+
+ FileAccess *f = FileAccess::open(p_source_file, FileAccess::READ);
+
+ ERR_FAIL_COND_V(!f, ERR_CANT_OPEN);
+
+ size_t len = f->get_len();
+
+ Vector<uint8_t> data;
+ data.resize(len);
+ uint8_t *w = data.ptrw();
+
+ f->get_buffer(w, len);
+
+ memdelete(f);
+
+ Ref<AudioStreamMP3> mp3_stream;
+ mp3_stream.instance();
+
+ mp3_stream->set_data(data);
+ ERR_FAIL_COND_V(!mp3_stream->get_data().size(), ERR_FILE_CORRUPT);
+ mp3_stream->set_loop(loop);
+ mp3_stream->set_loop_offset(loop_offset);
+
+ return ResourceSaver::save(p_save_path + ".mp3str", mp3_stream);
+}
+
+ResourceImporterMP3::ResourceImporterMP3() {
+}
diff --git a/modules/minimp3/resource_importer_mp3.h b/modules/minimp3/resource_importer_mp3.h
new file mode 100644
index 0000000000..c1e8315e21
--- /dev/null
+++ b/modules/minimp3/resource_importer_mp3.h
@@ -0,0 +1,58 @@
+/*************************************************************************/
+/* resource_importer_mp3.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef RESOURCE_IMPORTER_MP3_H
+#define RESOURCE_IMPORTER_MP3_H
+
+#include "audio_stream_mp3.h"
+#include "core/io/resource_importer.h"
+
+class ResourceImporterMP3 : public ResourceImporter {
+ GDCLASS(ResourceImporterMP3, ResourceImporter);
+
+public:
+ virtual String get_importer_name() const override;
+ virtual String get_visible_name() const override;
+ virtual void get_recognized_extensions(List<String> *p_extensions) const override;
+ virtual String get_save_extension() const override;
+ virtual String get_resource_type() const override;
+
+ virtual int get_preset_count() const override;
+ virtual String get_preset_name(int p_idx) const override;
+
+ virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const override;
+ virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const override;
+
+ virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+
+ ResourceImporterMP3();
+};
+
+#endif // RESOURCE_IMPORTER_MP3_H
diff --git a/modules/mono/build_scripts/make_android_mono_config.py b/modules/mono/build_scripts/make_android_mono_config.py
index 04f8c80243..28494bff6e 100644
--- a/modules/mono/build_scripts/make_android_mono_config.py
+++ b/modules/mono/build_scripts/make_android_mono_config.py
@@ -38,10 +38,10 @@ String get_godot_android_mono_config() {
Vector<uint8_t> data;
data.resize(config_uncompressed_size);
uint8_t* w = data.ptrw();
- Compression::decompress(w.ptr(), config_uncompressed_size, config_compressed_data,
+ Compression::decompress(w, config_uncompressed_size, config_compressed_data,
config_compressed_size, Compression::MODE_DEFLATE);
String s;
- if (s.parse_utf8((const char *)w.ptr(), data.size())) {
+ if (s.parse_utf8((const char *)w, data.size())) {
ERR_FAIL_V(String());
}
return s;
diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py
index 6057004166..309abfbff7 100644
--- a/modules/mono/build_scripts/mono_configure.py
+++ b/modules/mono/build_scripts/mono_configure.py
@@ -97,6 +97,7 @@ def configure(env, env_mono):
copy_mono_root = env["copy_mono_root"]
mono_prefix = env["mono_prefix"]
+ mono_bcl = env["mono_bcl"]
mono_lib_names = ["mono-2.0-sgen", "monosgen-2.0"]
@@ -262,7 +263,8 @@ def configure(env, env_mono):
env_mono.Append(CPPDEFINES=["_REENTRANT"])
if mono_static:
- env.Append(LINKFLAGS=["-rdynamic"])
+ if not is_javascript:
+ env.Append(LINKFLAGS=["-rdynamic"])
mono_lib_file = os.path.join(mono_lib_path, "lib" + mono_lib + ".a")
@@ -397,7 +399,7 @@ def configure(env, env_mono):
if tools_enabled:
# Only supported for editor builds.
- copy_mono_root_files(env, mono_root)
+ copy_mono_root_files(env, mono_root, mono_bcl)
def make_template_dir(env, mono_root):
@@ -430,7 +432,7 @@ def make_template_dir(env, mono_root):
copy_mono_shared_libs(env, mono_root, template_mono_root_dir)
-def copy_mono_root_files(env, mono_root):
+def copy_mono_root_files(env, mono_root, mono_bcl):
from glob import glob
from shutil import copy
from shutil import rmtree
@@ -455,7 +457,7 @@ def copy_mono_root_files(env, mono_root):
# Copy framework assemblies
- mono_framework_dir = os.path.join(mono_root, "lib", "mono", "4.5")
+ mono_framework_dir = mono_bcl or os.path.join(mono_root, "lib", "mono", "4.5")
mono_framework_facades_dir = os.path.join(mono_framework_dir, "Facades")
editor_mono_framework_dir = os.path.join(editor_mono_root_dir, "lib", "mono", "4.5")
diff --git a/modules/mono/config.py b/modules/mono/config.py
index c4a6b408e6..4c851a2989 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -27,6 +27,14 @@ def configure(env):
PathVariable.PathAccept,
)
)
+ envvars.Add(
+ PathVariable(
+ "mono_bcl",
+ "Path to a custom Mono BCL (Base Class Library) directory for the target platform",
+ "",
+ PathVariable.PathAccept,
+ )
+ )
envvars.Add(BoolVariable("mono_static", "Statically link Mono", default_mono_static))
envvars.Add(BoolVariable("mono_glue", "Build with the Mono glue sources", True))
envvars.Add(BoolVariable("build_cil", "Build C# solutions", True))
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index b4537f531d..63ac0956f4 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -2984,7 +2984,7 @@ void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
p_script->tool = nesting_class && nesting_class->has_attribute(CACHED_CLASS(ToolAttribute));
}
-#if TOOLS_ENABLED
+#ifdef TOOLS_ENABLED
if (!p_script->tool) {
p_script->tool = p_script->script_class->get_assembly() == GDMono::get_singleton()->get_tools_assembly();
}
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 3e4e6c3f86..f482cc21f0 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -31,6 +31,7 @@
#ifndef CSHARP_SCRIPT_H
#define CSHARP_SCRIPT_H
+#include "core/doc_data.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
#include "core/object/script_language.h"
@@ -189,6 +190,14 @@ public:
String get_source_code() const override;
void set_source_code(const String &p_code) override;
+#ifdef TOOLS_ENABLED
+ virtual const Vector<DocData::ClassDoc> &get_documentation() const override {
+ // TODO
+ static Vector<DocData::ClassDoc> docs;
+ return docs;
+ }
+#endif // TOOLS_ENABLED
+
Error reload(bool p_keep_state = false) override;
bool has_script_signal(const StringName &p_signal) const override;
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
index 9514cc9622..1a1639aac7 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
@@ -2,6 +2,7 @@ using Godot;
using System;
using Godot.Collections;
using GodotTools.Internals;
+using JetBrains.Annotations;
using File = GodotTools.Utils.File;
using Path = System.IO.Path;
@@ -26,6 +27,9 @@ namespace GodotTools.Build
private TextEdit buildLog;
private PopupMenu issuesListContextMenu;
+ private readonly object pendingBuildLogTextLock = new object();
+ [NotNull] private string pendingBuildLogText = string.Empty;
+
[Signal] public event Action BuildStateChanged;
public bool HasBuildExited { get; private set; } = false;
@@ -240,16 +244,34 @@ namespace GodotTools.Build
EmitSignal(nameof(BuildStateChanged));
}
+ private void UpdateBuildLogText()
+ {
+ lock (pendingBuildLogTextLock)
+ {
+ buildLog.Text += pendingBuildLogText;
+ pendingBuildLogText = string.Empty;
+ ScrollToLastNonEmptyLogLine();
+ }
+ }
+
private void StdOutputReceived(string text)
{
- buildLog.Text += text + "\n";
- ScrollToLastNonEmptyLogLine();
+ lock (pendingBuildLogTextLock)
+ {
+ if (pendingBuildLogText.Length == 0)
+ CallDeferred(nameof(UpdateBuildLogText));
+ pendingBuildLogText += text + "\n";
+ }
}
private void StdErrorReceived(string text)
{
- buildLog.Text += text + "\n";
- ScrollToLastNonEmptyLogLine();
+ lock (pendingBuildLogTextLock)
+ {
+ if (pendingBuildLogText.Length == 0)
+ CallDeferred(nameof(UpdateBuildLogText));
+ pendingBuildLogText += text + "\n";
+ }
}
private void ScrollToLastNonEmptyLogLine()
@@ -377,12 +399,14 @@ namespace GodotTools.Build
BuildManager.BuildStarted += BuildStarted;
BuildManager.BuildFinished += BuildFinished;
// StdOutput/Error can be received from different threads, so we need to use CallDeferred
- BuildManager.StdOutputReceived += line => CallDeferred(nameof(StdOutputReceived), line);
- BuildManager.StdErrorReceived += line => CallDeferred(nameof(StdErrorReceived), line);
+ BuildManager.StdOutputReceived += StdOutputReceived;
+ BuildManager.StdErrorReceived += StdErrorReceived;
}
public void OnBeforeSerialize()
{
+ // In case it didn't update yet. We don't want to have to serialize any pending output.
+ UpdateBuildLogText();
}
public void OnAfterDeserialize()
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
index e18ed7f107..e9bb701562 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
@@ -173,6 +173,8 @@ namespace GodotTools.Export
assemblies[projectDllName] = projectDllSrcPath;
+ string bclDir = DeterminePlatformBclDir(platform);
+
if (platform == OS.Platforms.Android)
{
string godotAndroidExtProfileDir = GetBclProfileDir("godot_android_ext");
@@ -183,8 +185,49 @@ namespace GodotTools.Export
assemblies["Mono.Android"] = monoAndroidAssemblyPath;
}
+ else if (platform == OS.Platforms.HTML5)
+ {
+ // Ideally these would be added automatically since they're referenced by the wasm BCL assemblies.
+ // However, at least in the case of 'WebAssembly.Net.Http' for some reason the BCL assemblies
+ // reference a different version even though the assembly is the same, for some weird reason.
- string bclDir = DeterminePlatformBclDir(platform);
+ var wasmFrameworkAssemblies = new[] {"WebAssembly.Bindings", "WebAssembly.Net.WebSockets"};
+
+ foreach (string thisWasmFrameworkAssemblyName in wasmFrameworkAssemblies)
+ {
+ string thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName + ".dll");
+ if (!File.Exists(thisWasmFrameworkAssemblyPath))
+ throw new FileNotFoundException($"Assembly not found: '{thisWasmFrameworkAssemblyName}'", thisWasmFrameworkAssemblyPath);
+ assemblies[thisWasmFrameworkAssemblyName] = thisWasmFrameworkAssemblyPath;
+ }
+
+ // Assemblies that can have a different name in a newer version. Newer version must come first and it has priority.
+ (string newName, string oldName)[] wasmFrameworkAssembliesOneOf = new[]
+ {
+ ("System.Net.Http.WebAssemblyHttpHandler", "WebAssembly.Net.Http")
+ };
+
+ foreach (var thisWasmFrameworkAssemblyName in wasmFrameworkAssembliesOneOf)
+ {
+ string thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName.newName + ".dll");
+ if (File.Exists(thisWasmFrameworkAssemblyPath))
+ {
+ assemblies[thisWasmFrameworkAssemblyName.newName] = thisWasmFrameworkAssemblyPath;
+ }
+ else
+ {
+ thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName.oldName + ".dll");
+ if (!File.Exists(thisWasmFrameworkAssemblyPath))
+ {
+ throw new FileNotFoundException("Expected one of the following assemblies but none were found: " +
+ $"'{thisWasmFrameworkAssemblyName.newName}' / '{thisWasmFrameworkAssemblyName.oldName}'",
+ thisWasmFrameworkAssemblyPath);
+ }
+
+ assemblies[thisWasmFrameworkAssemblyName.oldName] = thisWasmFrameworkAssemblyPath;
+ }
+ }
+ }
var initialAssemblies = assemblies.Duplicate();
internal_GetExportedAssemblyDependencies(initialAssemblies, buildConfig, bclDir, assemblies);
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index ff3122a77f..ad7e5d4200 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -97,7 +97,7 @@
#define C_METHOD_MANAGED_TO_SIGNAL C_NS_MONOMARSHAL "::signal_info_to_callable"
#define C_METHOD_MANAGED_FROM_SIGNAL C_NS_MONOMARSHAL "::callable_to_signal_info"
-#define BINDINGS_GENERATOR_VERSION UINT32_C(12)
+#define BINDINGS_GENERATOR_VERSION UINT32_C(13)
const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN("\t%0 %1_in = %1;\n");
@@ -185,7 +185,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
return String();
}
- DocData *doc = EditorHelp::get_doc_data();
+ DocTools *doc = EditorHelp::get_doc_data();
String bbcode = p_bbcode;
@@ -1999,12 +1999,12 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \
{ \
- output.append("\tmono_add_internal_call("); \
+ output.append("\tGDMonoUtils::add_internal_call("); \
output.append("\"" BINDINGS_NAMESPACE "."); \
output.append(m_icall.editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); \
output.append("::"); \
output.append(m_icall.name); \
- output.append("\", (void*)"); \
+ output.append("\", "); \
output.append(m_icall.name); \
output.append(");\n"); \
}
@@ -3100,44 +3100,11 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
INSERT_INT_TYPE("sbyte", int8_t, int64_t);
INSERT_INT_TYPE("short", int16_t, int64_t);
INSERT_INT_TYPE("int", int32_t, int64_t);
+ INSERT_INT_TYPE("long", int64_t, int64_t);
INSERT_INT_TYPE("byte", uint8_t, int64_t);
INSERT_INT_TYPE("ushort", uint16_t, int64_t);
INSERT_INT_TYPE("uint", uint32_t, int64_t);
-
- itype = TypeInterface::create_value_type(String("long"));
- {
- itype.c_out = "\treturn (%0)%1;\n";
- itype.c_in = "\t%0 %1_in = (%0)*%1;\n";
- itype.c_out = "\t*%3 = (%0)%1;\n";
- itype.c_type = "int64_t";
- itype.c_arg_in = "&%s_in";
- }
- itype.c_type_in = "int64_t*";
- itype.c_type_out = "int64_t";
- itype.im_type_in = "ref " + itype.name;
- itype.im_type_out = "out " + itype.name;
- itype.cs_in = "ref %0";
- /* in cs_out, im_type_out (%3) includes the 'out ' part */
- itype.cs_out = "%0(%1, %3 argRet); return argRet;";
- itype.ret_as_byref_arg = true;
- builtin_types.insert(itype.cname, itype);
-
- itype = TypeInterface::create_value_type(String("ulong"));
- {
- itype.c_in = "\t%0 %1_in = (%0)*%1;\n";
- itype.c_out = "\t*%3 = (%0)%1;\n";
- itype.c_type = "int64_t";
- itype.c_arg_in = "&%s_in";
- }
- itype.c_type_in = "uint64_t*";
- itype.c_type_out = "uint64_t";
- itype.im_type_in = "ref " + itype.name;
- itype.im_type_out = "out " + itype.name;
- itype.cs_in = "ref %0";
- /* in cs_out, im_type_out (%3) includes the 'out ' part */
- itype.cs_out = "%0(%1, %3 argRet); return argRet;";
- itype.ret_as_byref_arg = true;
- builtin_types.insert(itype.cname, itype);
+ INSERT_INT_TYPE("ulong", uint64_t, int64_t);
}
// Floating point types
@@ -3149,20 +3116,16 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.proxy_name = "float";
{
// The expected type for 'float' in ptrcall is 'double'
- itype.c_in = "\t%0 %1_in = (%0)*%1;\n";
- itype.c_out = "\t*%3 = (%0)%1;\n";
+ itype.c_in = "\t%0 %1_in = (%0)%1;\n";
+ itype.c_out = "\treturn (%0)%1;\n";
itype.c_type = "double";
- itype.c_type_in = "float*";
+ itype.c_type_in = "float";
itype.c_type_out = "float";
itype.c_arg_in = "&%s_in";
}
itype.cs_type = itype.proxy_name;
- itype.im_type_in = "ref " + itype.proxy_name;
- itype.im_type_out = "out " + itype.proxy_name;
- itype.cs_in = "ref %0";
- /* in cs_out, im_type_out (%3) includes the 'out ' part */
- itype.cs_out = "%0(%1, %3 argRet); return argRet;";
- itype.ret_as_byref_arg = true;
+ itype.im_type_in = itype.proxy_name;
+ itype.im_type_out = itype.proxy_name;
builtin_types.insert(itype.cname, itype);
// double
@@ -3171,20 +3134,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.cname = itype.name;
itype.proxy_name = "double";
{
- itype.c_in = "\t%0 %1_in = (%0)*%1;\n";
- itype.c_out = "\t*%3 = (%0)%1;\n";
itype.c_type = "double";
- itype.c_type_in = "double*";
+ itype.c_type_in = "double";
itype.c_type_out = "double";
- itype.c_arg_in = "&%s_in";
+ itype.c_arg_in = "&%s";
}
itype.cs_type = itype.proxy_name;
- itype.im_type_in = "ref " + itype.proxy_name;
- itype.im_type_out = "out " + itype.proxy_name;
- itype.cs_in = "ref %0";
- /* in cs_out, im_type_out (%3) includes the 'out ' part */
- itype.cs_out = "%0(%1, %3 argRet); return argRet;";
- itype.ret_as_byref_arg = true;
+ itype.im_type_in = itype.proxy_name;
+ itype.im_type_out = itype.proxy_name;
builtin_types.insert(itype.cname, itype);
}
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index eeab518954..0cc1bb5f46 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -31,9 +31,10 @@
#ifndef BINDINGS_GENERATOR_H
#define BINDINGS_GENERATOR_H
+#include "core/doc_data.h"
#include "core/object/class_db.h"
#include "core/string/string_builder.h"
-#include "editor/doc_data.h"
+#include "editor/doc_tools.h"
#include "editor/editor_help.h"
#if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
@@ -44,7 +45,7 @@ class BindingsGenerator {
struct ConstantInterface {
String name;
String proxy_name;
- int value;
+ int value = 0;
const DocData::ConstantDoc *const_doc;
ConstantInterface() {}
@@ -74,7 +75,7 @@ class BindingsGenerator {
struct PropertyInterface {
StringName cname;
String proxy_name;
- int index;
+ int index = 0;
StringName setter;
StringName getter;
@@ -479,7 +480,7 @@ class BindingsGenerator {
String im_type_out; // Return type for the C# method declaration. Also used as companion of [unique_siq]
String im_sig; // Signature for the C# method declaration
String unique_sig; // Unique signature to avoid duplicates in containers
- bool editor_only;
+ bool editor_only = false;
InternalCall() {}
diff --git a/modules/mono/editor/code_completion.cpp b/modules/mono/editor/code_completion.cpp
index f1919c2501..2d37b1306c 100644
--- a/modules/mono/editor/code_completion.cpp
+++ b/modules/mono/editor/code_completion.cpp
@@ -228,6 +228,18 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
}
}
} break;
+ case CompletionKind::THEME_FONT_SIZES: {
+ Ref<Script> script = ResourceLoader::load(p_script_file.simplify_path());
+ Node *base = _try_find_owner_node_in_tree(script);
+ if (base && Object::cast_to<Control>(base)) {
+ List<StringName> sn;
+ Theme::get_default()->get_font_size_list(base->get_class(), &sn);
+
+ for (List<StringName>::Element *E = sn.front(); E; E = E->next()) {
+ suggestions.push_back(quoted(E->get()));
+ }
+ }
+ } break;
case CompletionKind::THEME_STYLES: {
Ref<Script> script = ResourceLoader::load(p_script_file.simplify_path());
Node *base = _try_find_owner_node_in_tree(script);
diff --git a/modules/mono/editor/code_completion.h b/modules/mono/editor/code_completion.h
index c2a33a9133..e38768612b 100644
--- a/modules/mono/editor/code_completion.h
+++ b/modules/mono/editor/code_completion.h
@@ -46,6 +46,7 @@ enum class CompletionKind {
THEME_COLORS,
THEME_CONSTANTS,
THEME_FONTS,
+ THEME_FONT_SIZES,
THEME_STYLES
};
diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp
index 68fc372959..f9be19bbe2 100644
--- a/modules/mono/editor/editor_internal_calls.cpp
+++ b/modules/mono/editor/editor_internal_calls.cpp
@@ -47,7 +47,6 @@
#include "../godotsharp_dirs.h"
#include "../mono_gd/gd_mono_marshal.h"
#include "../utils/osx_utils.h"
-#include "bindings_generator.h"
#include "code_completion.h"
#include "godotsharp_export.h"
#include "script_class_parser.h"
@@ -173,35 +172,6 @@ MonoBoolean godot_icall_EditorProgress_Step(MonoString *p_task, MonoString *p_st
return EditorNode::progress_task_step(task, state, p_step, (bool)p_force_refresh);
}
-BindingsGenerator *godot_icall_BindingsGenerator_Ctor() {
- return memnew(BindingsGenerator);
-}
-
-void godot_icall_BindingsGenerator_Dtor(BindingsGenerator *p_handle) {
- memdelete(p_handle);
-}
-
-MonoBoolean godot_icall_BindingsGenerator_LogPrintEnabled(BindingsGenerator *p_handle) {
- return p_handle->is_log_print_enabled();
-}
-
-void godot_icall_BindingsGenerator_SetLogPrintEnabled(BindingsGenerator p_handle, MonoBoolean p_enabled) {
- p_handle.set_log_print_enabled(p_enabled);
-}
-
-int32_t godot_icall_BindingsGenerator_GenerateCsApi(BindingsGenerator *p_handle, MonoString *p_output_dir) {
- String output_dir = GDMonoMarshal::mono_string_to_godot(p_output_dir);
- return p_handle->generate_cs_api(output_dir);
-}
-
-uint32_t godot_icall_BindingsGenerator_Version() {
- return BindingsGenerator::get_version();
-}
-
-uint32_t godot_icall_BindingsGenerator_CsGlueVersion() {
- return CS_GLUE_VERSION;
-}
-
int32_t godot_icall_ScriptClassParser_ParseFile(MonoString *p_filepath, MonoObject *p_classes, MonoString **r_error_str) {
*r_error_str = nullptr;
@@ -400,75 +370,66 @@ MonoBoolean godot_icall_Utils_OS_UnixFileHasExecutableAccess(MonoString *p_file_
void register_editor_internal_calls() {
// GodotSharpDirs
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResDataDir", (void *)godot_icall_GodotSharpDirs_ResDataDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResMetadataDir", (void *)godot_icall_GodotSharpDirs_ResMetadataDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesBaseDir", (void *)godot_icall_GodotSharpDirs_ResAssembliesBaseDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesDir", (void *)godot_icall_GodotSharpDirs_ResAssembliesDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResConfigDir", (void *)godot_icall_GodotSharpDirs_ResConfigDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempDir", (void *)godot_icall_GodotSharpDirs_ResTempDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesBaseDir", (void *)godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesDir", (void *)godot_icall_GodotSharpDirs_ResTempAssembliesDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoUserDir", (void *)godot_icall_GodotSharpDirs_MonoUserDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoLogsDir", (void *)godot_icall_GodotSharpDirs_MonoLogsDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoSolutionsDir", (void *)godot_icall_GodotSharpDirs_MonoSolutionsDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_BuildLogsDirs", (void *)godot_icall_GodotSharpDirs_BuildLogsDirs);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectSlnPath", (void *)godot_icall_GodotSharpDirs_ProjectSlnPath);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectCsProjPath", (void *)godot_icall_GodotSharpDirs_ProjectCsProjPath);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorToolsDir", (void *)godot_icall_GodotSharpDirs_DataEditorToolsDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorPrebuiltApiDir", (void *)godot_icall_GodotSharpDirs_DataEditorPrebuiltApiDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoEtcDir", (void *)godot_icall_GodotSharpDirs_DataMonoEtcDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoLibDir", (void *)godot_icall_GodotSharpDirs_DataMonoLibDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoBinDir", (void *)godot_icall_GodotSharpDirs_DataMonoBinDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResDataDir", godot_icall_GodotSharpDirs_ResDataDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResMetadataDir", godot_icall_GodotSharpDirs_ResMetadataDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesBaseDir", godot_icall_GodotSharpDirs_ResAssembliesBaseDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesDir", godot_icall_GodotSharpDirs_ResAssembliesDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResConfigDir", godot_icall_GodotSharpDirs_ResConfigDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempDir", godot_icall_GodotSharpDirs_ResTempDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesBaseDir", godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesDir", godot_icall_GodotSharpDirs_ResTempAssembliesDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoUserDir", godot_icall_GodotSharpDirs_MonoUserDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoLogsDir", godot_icall_GodotSharpDirs_MonoLogsDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoSolutionsDir", godot_icall_GodotSharpDirs_MonoSolutionsDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_BuildLogsDirs", godot_icall_GodotSharpDirs_BuildLogsDirs);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectSlnPath", godot_icall_GodotSharpDirs_ProjectSlnPath);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectCsProjPath", godot_icall_GodotSharpDirs_ProjectCsProjPath);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorToolsDir", godot_icall_GodotSharpDirs_DataEditorToolsDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorPrebuiltApiDir", godot_icall_GodotSharpDirs_DataEditorPrebuiltApiDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoEtcDir", godot_icall_GodotSharpDirs_DataMonoEtcDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoLibDir", godot_icall_GodotSharpDirs_DataMonoLibDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoBinDir", godot_icall_GodotSharpDirs_DataMonoBinDir);
// EditorProgress
- mono_add_internal_call("GodotTools.Internals.EditorProgress::internal_Create", (void *)godot_icall_EditorProgress_Create);
- mono_add_internal_call("GodotTools.Internals.EditorProgress::internal_Dispose", (void *)godot_icall_EditorProgress_Dispose);
- mono_add_internal_call("GodotTools.Internals.EditorProgress::internal_Step", (void *)godot_icall_EditorProgress_Step);
-
- // BiningsGenerator
- mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_Ctor", (void *)godot_icall_BindingsGenerator_Ctor);
- mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_Dtor", (void *)godot_icall_BindingsGenerator_Dtor);
- mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_LogPrintEnabled", (void *)godot_icall_BindingsGenerator_LogPrintEnabled);
- mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_SetLogPrintEnabled", (void *)godot_icall_BindingsGenerator_SetLogPrintEnabled);
- mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_GenerateCsApi", (void *)godot_icall_BindingsGenerator_GenerateCsApi);
- mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_Version", (void *)godot_icall_BindingsGenerator_Version);
- mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_CsGlueVersion", (void *)godot_icall_BindingsGenerator_CsGlueVersion);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.EditorProgress::internal_Create", godot_icall_EditorProgress_Create);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.EditorProgress::internal_Dispose", godot_icall_EditorProgress_Dispose);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.EditorProgress::internal_Step", godot_icall_EditorProgress_Step);
// ScriptClassParser
- mono_add_internal_call("GodotTools.Internals.ScriptClassParser::internal_ParseFile", (void *)godot_icall_ScriptClassParser_ParseFile);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.ScriptClassParser::internal_ParseFile", godot_icall_ScriptClassParser_ParseFile);
// ExportPlugin
- mono_add_internal_call("GodotTools.Export.ExportPlugin::internal_GetExportedAssemblyDependencies", (void *)godot_icall_ExportPlugin_GetExportedAssemblyDependencies);
+ GDMonoUtils::add_internal_call("GodotTools.Export.ExportPlugin::internal_GetExportedAssemblyDependencies", godot_icall_ExportPlugin_GetExportedAssemblyDependencies);
// Internals
- mono_add_internal_call("GodotTools.Internals.Internal::internal_UpdateApiAssembliesFromPrebuilt", (void *)godot_icall_Internal_UpdateApiAssembliesFromPrebuilt);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_FullTemplatesDir", (void *)godot_icall_Internal_FullTemplatesDir);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_SimplifyGodotPath", (void *)godot_icall_Internal_SimplifyGodotPath);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_IsOsxAppBundleInstalled", (void *)godot_icall_Internal_IsOsxAppBundleInstalled);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotIs32Bits", (void *)godot_icall_Internal_GodotIs32Bits);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotIsRealTDouble", (void *)godot_icall_Internal_GodotIsRealTDouble);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotMainIteration", (void *)godot_icall_Internal_GodotMainIteration);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_GetCoreApiHash", (void *)godot_icall_Internal_GetCoreApiHash);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_GetEditorApiHash", (void *)godot_icall_Internal_GetEditorApiHash);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_IsAssembliesReloadingNeeded", (void *)godot_icall_Internal_IsAssembliesReloadingNeeded);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_ReloadAssemblies", (void *)godot_icall_Internal_ReloadAssemblies);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorDebuggerNodeReloadScripts", (void *)godot_icall_Internal_EditorDebuggerNodeReloadScripts);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorEdit", (void *)godot_icall_Internal_ScriptEditorEdit);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorNodeShowScriptScreen", (void *)godot_icall_Internal_EditorNodeShowScriptScreen);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_GetScriptsMetadataOrNothing", (void *)godot_icall_Internal_GetScriptsMetadataOrNothing);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_MonoWindowsInstallRoot", (void *)godot_icall_Internal_MonoWindowsInstallRoot);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorRunPlay", (void *)godot_icall_Internal_EditorRunPlay);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorRunStop", (void *)godot_icall_Internal_EditorRunStop);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorDebugger_ReloadScripts", (void *)godot_icall_Internal_ScriptEditorDebugger_ReloadScripts);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_CodeCompletionRequest", (void *)godot_icall_Internal_CodeCompletionRequest);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_UpdateApiAssembliesFromPrebuilt", godot_icall_Internal_UpdateApiAssembliesFromPrebuilt);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_FullTemplatesDir", godot_icall_Internal_FullTemplatesDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_SimplifyGodotPath", godot_icall_Internal_SimplifyGodotPath);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_IsOsxAppBundleInstalled", godot_icall_Internal_IsOsxAppBundleInstalled);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotIs32Bits", godot_icall_Internal_GodotIs32Bits);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotIsRealTDouble", godot_icall_Internal_GodotIsRealTDouble);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotMainIteration", godot_icall_Internal_GodotMainIteration);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GetCoreApiHash", godot_icall_Internal_GetCoreApiHash);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GetEditorApiHash", godot_icall_Internal_GetEditorApiHash);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_IsAssembliesReloadingNeeded", godot_icall_Internal_IsAssembliesReloadingNeeded);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_ReloadAssemblies", godot_icall_Internal_ReloadAssemblies);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorDebuggerNodeReloadScripts", godot_icall_Internal_EditorDebuggerNodeReloadScripts);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorEdit", godot_icall_Internal_ScriptEditorEdit);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorNodeShowScriptScreen", godot_icall_Internal_EditorNodeShowScriptScreen);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GetScriptsMetadataOrNothing", godot_icall_Internal_GetScriptsMetadataOrNothing);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_MonoWindowsInstallRoot", godot_icall_Internal_MonoWindowsInstallRoot);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorRunPlay", godot_icall_Internal_EditorRunPlay);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorRunStop", godot_icall_Internal_EditorRunStop);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorDebugger_ReloadScripts", godot_icall_Internal_ScriptEditorDebugger_ReloadScripts);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_CodeCompletionRequest", godot_icall_Internal_CodeCompletionRequest);
// Globals
- mono_add_internal_call("GodotTools.Internals.Globals::internal_EditorScale", (void *)godot_icall_Globals_EditorScale);
- mono_add_internal_call("GodotTools.Internals.Globals::internal_GlobalDef", (void *)godot_icall_Globals_GlobalDef);
- mono_add_internal_call("GodotTools.Internals.Globals::internal_EditorDef", (void *)godot_icall_Globals_EditorDef);
- mono_add_internal_call("GodotTools.Internals.Globals::internal_TTR", (void *)godot_icall_Globals_TTR);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorScale", godot_icall_Globals_EditorScale);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_GlobalDef", godot_icall_Globals_GlobalDef);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorDef", godot_icall_Globals_EditorDef);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_TTR", godot_icall_Globals_TTR);
// Utils.OS
- mono_add_internal_call("GodotTools.Utils.OS::GetPlatformName", (void *)godot_icall_Utils_OS_GetPlatformName);
- mono_add_internal_call("GodotTools.Utils.OS::UnixFileHasExecutableAccess", (void *)godot_icall_Utils_OS_UnixFileHasExecutableAccess);
+ GDMonoUtils::add_internal_call("GodotTools.Utils.OS::GetPlatformName", godot_icall_Utils_OS_GetPlatformName);
+ GDMonoUtils::add_internal_call("GodotTools.Utils.OS::UnixFileHasExecutableAccess", godot_icall_Utils_OS_UnixFileHasExecutableAccess);
}
diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp
index 1a0d5743ae..b006eed69f 100644
--- a/modules/mono/editor/godotsharp_export.cpp
+++ b/modules/mono/editor/godotsharp_export.cpp
@@ -55,10 +55,10 @@ MonoAssemblyName *new_mono_assembly_name() {
struct AssemblyRefInfo {
String name;
- uint16_t major;
- uint16_t minor;
- uint16_t build;
- uint16_t revision;
+ uint16_t major = 0;
+ uint16_t minor = 0;
+ uint16_t build = 0;
+ uint16_t revision = 0;
};
AssemblyRefInfo get_assemblyref_name(MonoImage *p_image, int index) {
diff --git a/modules/mono/editor/script_class_parser.h b/modules/mono/editor/script_class_parser.h
index 3c55fa07a7..deb6061191 100644
--- a/modules/mono/editor/script_class_parser.h
+++ b/modules/mono/editor/script_class_parser.h
@@ -45,22 +45,22 @@ public:
};
String name;
- Type type;
+ Type type = NAMESPACE_DECL;
};
struct ClassDecl {
String name;
String namespace_;
Vector<String> base;
- bool nested;
+ bool nested = false;
};
private:
String code;
- int idx;
- int line;
+ int idx = 0;
+ int line = 0;
String error_str;
- bool error;
+ bool error = false;
Variant value;
Vector<ClassDecl> classes;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs
index b33490f9cb..bd3bcb0c58 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs
@@ -120,13 +120,13 @@ namespace Godot
/// <param name="b">The destination quaternion.</param>
/// <param name="preA">A quaternion before this quaternion.</param>
/// <param name="postB">A quaternion after `b`.</param>
- /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The interpolated quaternion.</returns>
- public Quat CubicSlerp(Quat b, Quat preA, Quat postB, real_t t)
+ public Quat CubicSlerp(Quat b, Quat preA, Quat postB, real_t weight)
{
- real_t t2 = (1.0f - t) * t * 2f;
- Quat sp = Slerp(b, t);
- Quat sq = preA.Slerpni(postB, t);
+ real_t t2 = (1.0f - weight) * weight * 2f;
+ Quat sp = Slerp(b, weight);
+ Quat sq = preA.Slerpni(postB, weight);
return sp.Slerpni(sq, t2);
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
index d536b14eac..b74dd6f4f4 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
@@ -194,15 +194,16 @@ namespace Godot
/// <param name="b">The destination vector.</param>
/// <param name="preA">A vector before this vector.</param>
/// <param name="postB">A vector after `b`.</param>
- /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The interpolated vector.</returns>
- public Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, real_t t)
+ public Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, real_t weight)
{
Vector2 p0 = preA;
Vector2 p1 = this;
Vector2 p2 = b;
Vector2 p3 = postB;
+ real_t t = weight;
real_t t2 = t * t;
real_t t3 = t2 * t;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
index 4a4a2a43cd..07f5b3c38e 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
@@ -161,15 +161,16 @@ namespace Godot
/// <param name="b">The destination vector.</param>
/// <param name="preA">A vector before this vector.</param>
/// <param name="postB">A vector after `b`.</param>
- /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The interpolated vector.</returns>
- public Vector3 CubicInterpolate(Vector3 b, Vector3 preA, Vector3 postB, real_t t)
+ public Vector3 CubicInterpolate(Vector3 b, Vector3 preA, Vector3 postB, real_t weight)
{
Vector3 p0 = preA;
Vector3 p1 = this;
Vector3 p2 = b;
Vector3 p3 = postB;
+ real_t t = weight;
real_t t2 = t * t;
real_t t3 = t2 * t;
diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp
index 544f414cba..afcc75395b 100644
--- a/modules/mono/glue/base_object_glue.cpp
+++ b/modules/mono/glue/base_object_glue.cpp
@@ -163,9 +163,9 @@ MonoObject *godot_icall_Object_weakref(Object *p_ptr) {
return GDMonoUtils::unmanaged_get_managed(wref.ptr());
}
-Error godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, MonoObject *p_awaiter) {
+int32_t godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, MonoObject *p_awaiter) {
StringName signal = p_signal ? *p_signal : StringName();
- return gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter);
+ return (int32_t)gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter);
}
MonoArray *godot_icall_DynamicGodotObject_SetMemberList(Object *p_ptr) {
@@ -240,18 +240,18 @@ MonoString *godot_icall_Object_ToString(Object *p_ptr) {
}
void godot_register_object_icalls() {
- mono_add_internal_call("Godot.Object::godot_icall_Object_Ctor", (void *)godot_icall_Object_Ctor);
- mono_add_internal_call("Godot.Object::godot_icall_Object_Disposed", (void *)godot_icall_Object_Disposed);
- mono_add_internal_call("Godot.Object::godot_icall_Reference_Disposed", (void *)godot_icall_Reference_Disposed);
- mono_add_internal_call("Godot.Object::godot_icall_Object_ConnectEventSignals", (void *)godot_icall_Object_ConnectEventSignals);
- mono_add_internal_call("Godot.Object::godot_icall_Object_ClassDB_get_method", (void *)godot_icall_Object_ClassDB_get_method);
- mono_add_internal_call("Godot.Object::godot_icall_Object_ToString", (void *)godot_icall_Object_ToString);
- mono_add_internal_call("Godot.Object::godot_icall_Object_weakref", (void *)godot_icall_Object_weakref);
- mono_add_internal_call("Godot.SignalAwaiter::godot_icall_SignalAwaiter_connect", (void *)godot_icall_SignalAwaiter_connect);
- mono_add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMemberList", (void *)godot_icall_DynamicGodotObject_SetMemberList);
- mono_add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_InvokeMember", (void *)godot_icall_DynamicGodotObject_InvokeMember);
- mono_add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_GetMember", (void *)godot_icall_DynamicGodotObject_GetMember);
- mono_add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMember", (void *)godot_icall_DynamicGodotObject_SetMember);
+ GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_Ctor", godot_icall_Object_Ctor);
+ GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_Disposed", godot_icall_Object_Disposed);
+ GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Reference_Disposed", godot_icall_Reference_Disposed);
+ GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_ConnectEventSignals", godot_icall_Object_ConnectEventSignals);
+ GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_ClassDB_get_method", godot_icall_Object_ClassDB_get_method);
+ GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_ToString", godot_icall_Object_ToString);
+ GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_weakref", godot_icall_Object_weakref);
+ GDMonoUtils::add_internal_call("Godot.SignalAwaiter::godot_icall_SignalAwaiter_connect", godot_icall_SignalAwaiter_connect);
+ GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMemberList", godot_icall_DynamicGodotObject_SetMemberList);
+ GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_InvokeMember", godot_icall_DynamicGodotObject_InvokeMember);
+ GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_GetMember", godot_icall_DynamicGodotObject_GetMember);
+ GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMember", godot_icall_DynamicGodotObject_SetMember);
}
#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp
index bb3ea0f730..dedb5b9f75 100644
--- a/modules/mono/glue/collections_glue.cpp
+++ b/modules/mono/glue/collections_glue.cpp
@@ -47,7 +47,7 @@ void godot_icall_Array_Dtor(Array *ptr) {
memdelete(ptr);
}
-MonoObject *godot_icall_Array_At(Array *ptr, int index) {
+MonoObject *godot_icall_Array_At(Array *ptr, int32_t index) {
if (index < 0 || index >= ptr->size()) {
GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
return nullptr;
@@ -55,7 +55,7 @@ MonoObject *godot_icall_Array_At(Array *ptr, int index) {
return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index));
}
-MonoObject *godot_icall_Array_At_Generic(Array *ptr, int index, uint32_t type_encoding, GDMonoClass *type_class) {
+MonoObject *godot_icall_Array_At_Generic(Array *ptr, int32_t index, uint32_t type_encoding, GDMonoClass *type_class) {
if (index < 0 || index >= ptr->size()) {
GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
return nullptr;
@@ -63,7 +63,7 @@ MonoObject *godot_icall_Array_At_Generic(Array *ptr, int index, uint32_t type_en
return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index), ManagedType(type_encoding, type_class));
}
-void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value) {
+void godot_icall_Array_SetAt(Array *ptr, int32_t index, MonoObject *value) {
if (index < 0 || index >= ptr->size()) {
GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
return;
@@ -71,11 +71,11 @@ void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value) {
ptr->operator[](index) = GDMonoMarshal::mono_object_to_variant(value);
}
-int godot_icall_Array_Count(Array *ptr) {
+int32_t godot_icall_Array_Count(Array *ptr) {
return ptr->size();
}
-int godot_icall_Array_Add(Array *ptr, MonoObject *item) {
+int32_t godot_icall_Array_Add(Array *ptr, MonoObject *item) {
ptr->append(GDMonoMarshal::mono_object_to_variant(item));
return ptr->size();
}
@@ -88,7 +88,7 @@ MonoBoolean godot_icall_Array_Contains(Array *ptr, MonoObject *item) {
return ptr->find(GDMonoMarshal::mono_object_to_variant(item)) != -1;
}
-void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index) {
+void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int32_t array_index) {
unsigned int count = ptr->size();
if (mono_array_length(array) < (array_index + count)) {
@@ -129,11 +129,11 @@ Array *godot_icall_Array_Concatenate(Array *left, Array *right) {
return new_array;
}
-int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) {
+int32_t godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) {
return ptr->find(GDMonoMarshal::mono_object_to_variant(item));
}
-void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item) {
+void godot_icall_Array_Insert(Array *ptr, int32_t index, MonoObject *item) {
if (index < 0 || index > ptr->size()) {
GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
return;
@@ -150,7 +150,7 @@ MonoBoolean godot_icall_Array_Remove(Array *ptr, MonoObject *item) {
return false;
}
-void godot_icall_Array_RemoveAt(Array *ptr, int index) {
+void godot_icall_Array_RemoveAt(Array *ptr, int32_t index) {
if (index < 0 || index >= ptr->size()) {
GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
return;
@@ -158,8 +158,8 @@ void godot_icall_Array_RemoveAt(Array *ptr, int index) {
ptr->remove(index);
}
-Error godot_icall_Array_Resize(Array *ptr, int new_size) {
- return ptr->resize(new_size);
+int32_t godot_icall_Array_Resize(Array *ptr, int32_t new_size) {
+ return (int32_t)ptr->resize(new_size);
}
void godot_icall_Array_Shuffle(Array *ptr) {
@@ -226,7 +226,7 @@ Array *godot_icall_Dictionary_Values(Dictionary *ptr) {
return memnew(Array(ptr->values()));
}
-int godot_icall_Dictionary_Count(Dictionary *ptr) {
+int32_t godot_icall_Dictionary_Count(Dictionary *ptr) {
return ptr->size();
}
@@ -308,47 +308,47 @@ MonoString *godot_icall_Dictionary_ToString(Dictionary *ptr) {
}
void godot_register_collections_icalls() {
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor", (void *)godot_icall_Array_Ctor);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor_MonoArray", (void *)godot_icall_Array_Ctor_MonoArray);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Dtor", (void *)godot_icall_Array_Dtor);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_At", (void *)godot_icall_Array_At);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_At_Generic", (void *)godot_icall_Array_At_Generic);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_SetAt", (void *)godot_icall_Array_SetAt);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Count", (void *)godot_icall_Array_Count);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Add", (void *)godot_icall_Array_Add);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Clear", (void *)godot_icall_Array_Clear);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Concatenate", (void *)godot_icall_Array_Concatenate);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Contains", (void *)godot_icall_Array_Contains);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_CopyTo", (void *)godot_icall_Array_CopyTo);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Duplicate", (void *)godot_icall_Array_Duplicate);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_IndexOf", (void *)godot_icall_Array_IndexOf);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Insert", (void *)godot_icall_Array_Insert);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Remove", (void *)godot_icall_Array_Remove);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_RemoveAt", (void *)godot_icall_Array_RemoveAt);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Resize", (void *)godot_icall_Array_Resize);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Shuffle", (void *)godot_icall_Array_Shuffle);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Generic_GetElementTypeInfo", (void *)godot_icall_Array_Generic_GetElementTypeInfo);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_ToString", (void *)godot_icall_Array_ToString);
-
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Ctor", (void *)godot_icall_Dictionary_Ctor);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Dtor", (void *)godot_icall_Dictionary_Dtor);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue", (void *)godot_icall_Dictionary_GetValue);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue_Generic", (void *)godot_icall_Dictionary_GetValue_Generic);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_SetValue", (void *)godot_icall_Dictionary_SetValue);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Keys", (void *)godot_icall_Dictionary_Keys);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Values", (void *)godot_icall_Dictionary_Values);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Count", (void *)godot_icall_Dictionary_Count);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Add", (void *)godot_icall_Dictionary_Add);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Clear", (void *)godot_icall_Dictionary_Clear);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Contains", (void *)godot_icall_Dictionary_Contains);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ContainsKey", (void *)godot_icall_Dictionary_ContainsKey);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Duplicate", (void *)godot_icall_Dictionary_Duplicate);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_RemoveKey", (void *)godot_icall_Dictionary_RemoveKey);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Remove", (void *)godot_icall_Dictionary_Remove);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue", (void *)godot_icall_Dictionary_TryGetValue);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue_Generic", (void *)godot_icall_Dictionary_TryGetValue_Generic);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Generic_GetValueTypeInfo", (void *)godot_icall_Dictionary_Generic_GetValueTypeInfo);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ToString", (void *)godot_icall_Dictionary_ToString);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor", godot_icall_Array_Ctor);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor_MonoArray", godot_icall_Array_Ctor_MonoArray);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Dtor", godot_icall_Array_Dtor);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_At", godot_icall_Array_At);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_At_Generic", godot_icall_Array_At_Generic);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_SetAt", godot_icall_Array_SetAt);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Count", godot_icall_Array_Count);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Add", godot_icall_Array_Add);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Clear", godot_icall_Array_Clear);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Concatenate", godot_icall_Array_Concatenate);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Contains", godot_icall_Array_Contains);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_CopyTo", godot_icall_Array_CopyTo);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Duplicate", godot_icall_Array_Duplicate);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_IndexOf", godot_icall_Array_IndexOf);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Insert", godot_icall_Array_Insert);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Remove", godot_icall_Array_Remove);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_RemoveAt", godot_icall_Array_RemoveAt);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Resize", godot_icall_Array_Resize);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Shuffle", godot_icall_Array_Shuffle);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Generic_GetElementTypeInfo", godot_icall_Array_Generic_GetElementTypeInfo);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_ToString", godot_icall_Array_ToString);
+
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Ctor", godot_icall_Dictionary_Ctor);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Dtor", godot_icall_Dictionary_Dtor);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue", godot_icall_Dictionary_GetValue);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue_Generic", godot_icall_Dictionary_GetValue_Generic);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_SetValue", godot_icall_Dictionary_SetValue);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Keys", godot_icall_Dictionary_Keys);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Values", godot_icall_Dictionary_Values);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Count", godot_icall_Dictionary_Count);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Add", godot_icall_Dictionary_Add);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Clear", godot_icall_Dictionary_Clear);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Contains", godot_icall_Dictionary_Contains);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ContainsKey", godot_icall_Dictionary_ContainsKey);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Duplicate", godot_icall_Dictionary_Duplicate);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_RemoveKey", godot_icall_Dictionary_RemoveKey);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Remove", godot_icall_Dictionary_Remove);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue", godot_icall_Dictionary_TryGetValue);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue_Generic", godot_icall_Dictionary_TryGetValue_Generic);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Generic_GetValueTypeInfo", godot_icall_Dictionary_Generic_GetValueTypeInfo);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ToString", godot_icall_Dictionary_ToString);
}
#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp
index 58d8dceb25..a4566b82fb 100644
--- a/modules/mono/glue/gd_glue.cpp
+++ b/modules/mono/glue/gd_glue.cpp
@@ -289,33 +289,33 @@ MonoObject *godot_icall_DefaultGodotTaskScheduler() {
}
void godot_register_gd_icalls() {
- mono_add_internal_call("Godot.GD::godot_icall_GD_bytes2var", (void *)godot_icall_GD_bytes2var);
- mono_add_internal_call("Godot.GD::godot_icall_GD_convert", (void *)godot_icall_GD_convert);
- mono_add_internal_call("Godot.GD::godot_icall_GD_hash", (void *)godot_icall_GD_hash);
- mono_add_internal_call("Godot.GD::godot_icall_GD_instance_from_id", (void *)godot_icall_GD_instance_from_id);
- mono_add_internal_call("Godot.GD::godot_icall_GD_pusherror", (void *)godot_icall_GD_pusherror);
- mono_add_internal_call("Godot.GD::godot_icall_GD_pushwarning", (void *)godot_icall_GD_pushwarning);
- mono_add_internal_call("Godot.GD::godot_icall_GD_print", (void *)godot_icall_GD_print);
- mono_add_internal_call("Godot.GD::godot_icall_GD_printerr", (void *)godot_icall_GD_printerr);
- mono_add_internal_call("Godot.GD::godot_icall_GD_printraw", (void *)godot_icall_GD_printraw);
- mono_add_internal_call("Godot.GD::godot_icall_GD_prints", (void *)godot_icall_GD_prints);
- mono_add_internal_call("Godot.GD::godot_icall_GD_printt", (void *)godot_icall_GD_printt);
- mono_add_internal_call("Godot.GD::godot_icall_GD_randf", (void *)godot_icall_GD_randf);
- mono_add_internal_call("Godot.GD::godot_icall_GD_randi", (void *)godot_icall_GD_randi);
- mono_add_internal_call("Godot.GD::godot_icall_GD_randomize", (void *)godot_icall_GD_randomize);
- mono_add_internal_call("Godot.GD::godot_icall_GD_randf_range", (void *)godot_icall_GD_randf_range);
- mono_add_internal_call("Godot.GD::godot_icall_GD_randi_range", (void *)godot_icall_GD_randi_range);
- mono_add_internal_call("Godot.GD::godot_icall_GD_rand_seed", (void *)godot_icall_GD_rand_seed);
- mono_add_internal_call("Godot.GD::godot_icall_GD_seed", (void *)godot_icall_GD_seed);
- mono_add_internal_call("Godot.GD::godot_icall_GD_str", (void *)godot_icall_GD_str);
- mono_add_internal_call("Godot.GD::godot_icall_GD_str2var", (void *)godot_icall_GD_str2var);
- mono_add_internal_call("Godot.GD::godot_icall_GD_type_exists", (void *)godot_icall_GD_type_exists);
- mono_add_internal_call("Godot.GD::godot_icall_GD_var2bytes", (void *)godot_icall_GD_var2bytes);
- mono_add_internal_call("Godot.GD::godot_icall_GD_var2str", (void *)godot_icall_GD_var2str);
- mono_add_internal_call("Godot.GD::godot_icall_TypeToVariantType", (void *)godot_icall_TypeToVariantType);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_bytes2var", godot_icall_GD_bytes2var);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_convert", godot_icall_GD_convert);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_hash", godot_icall_GD_hash);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_instance_from_id", godot_icall_GD_instance_from_id);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_pusherror", godot_icall_GD_pusherror);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_pushwarning", godot_icall_GD_pushwarning);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_print", godot_icall_GD_print);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printerr", godot_icall_GD_printerr);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printraw", godot_icall_GD_printraw);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_prints", godot_icall_GD_prints);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printt", godot_icall_GD_printt);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randf", godot_icall_GD_randf);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randi", godot_icall_GD_randi);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randomize", godot_icall_GD_randomize);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randf_range", godot_icall_GD_randf_range);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randi_range", godot_icall_GD_randi_range);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_rand_seed", godot_icall_GD_rand_seed);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_seed", godot_icall_GD_seed);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_str", godot_icall_GD_str);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_str2var", godot_icall_GD_str2var);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_type_exists", godot_icall_GD_type_exists);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_var2bytes", godot_icall_GD_var2bytes);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_var2str", godot_icall_GD_var2str);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_TypeToVariantType", godot_icall_TypeToVariantType);
// Dispatcher
- mono_add_internal_call("Godot.Dispatcher::godot_icall_DefaultGodotTaskScheduler", (void *)godot_icall_DefaultGodotTaskScheduler);
+ GDMonoUtils::add_internal_call("Godot.Dispatcher::godot_icall_DefaultGodotTaskScheduler", godot_icall_DefaultGodotTaskScheduler);
}
#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/nodepath_glue.cpp b/modules/mono/glue/nodepath_glue.cpp
index 09c6d8f482..9405360c6c 100644
--- a/modules/mono/glue/nodepath_glue.cpp
+++ b/modules/mono/glue/nodepath_glue.cpp
@@ -81,17 +81,17 @@ MonoBoolean godot_icall_NodePath_is_empty(NodePath *p_ptr) {
}
void godot_register_nodepath_icalls() {
- mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_Ctor", (void *)godot_icall_NodePath_Ctor);
- mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_Dtor", (void *)godot_icall_NodePath_Dtor);
- mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_operator_String", (void *)godot_icall_NodePath_operator_String);
- mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_as_property_path", (void *)godot_icall_NodePath_get_as_property_path);
- mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_concatenated_subnames", (void *)godot_icall_NodePath_get_concatenated_subnames);
- mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name", (void *)godot_icall_NodePath_get_name);
- mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name_count", (void *)godot_icall_NodePath_get_name_count);
- mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname", (void *)godot_icall_NodePath_get_subname);
- mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname_count", (void *)godot_icall_NodePath_get_subname_count);
- mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_is_absolute", (void *)godot_icall_NodePath_is_absolute);
- mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_is_empty", (void *)godot_icall_NodePath_is_empty);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_Ctor", godot_icall_NodePath_Ctor);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_Dtor", godot_icall_NodePath_Dtor);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_operator_String", godot_icall_NodePath_operator_String);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_as_property_path", godot_icall_NodePath_get_as_property_path);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_concatenated_subnames", godot_icall_NodePath_get_concatenated_subnames);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name", godot_icall_NodePath_get_name);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name_count", godot_icall_NodePath_get_name_count);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname", godot_icall_NodePath_get_subname);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname_count", godot_icall_NodePath_get_subname_count);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_is_absolute", godot_icall_NodePath_is_absolute);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_is_empty", godot_icall_NodePath_is_empty);
}
#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/rid_glue.cpp b/modules/mono/glue/rid_glue.cpp
index cb4f26511f..410a98aa84 100644
--- a/modules/mono/glue/rid_glue.cpp
+++ b/modules/mono/glue/rid_glue.cpp
@@ -56,9 +56,9 @@ uint32_t godot_icall_RID_get_id(RID *p_ptr) {
}
void godot_register_rid_icalls() {
- mono_add_internal_call("Godot.RID::godot_icall_RID_Ctor", (void *)godot_icall_RID_Ctor);
- mono_add_internal_call("Godot.RID::godot_icall_RID_Dtor", (void *)godot_icall_RID_Dtor);
- mono_add_internal_call("Godot.RID::godot_icall_RID_get_id", (void *)godot_icall_RID_get_id);
+ GDMonoUtils::add_internal_call("Godot.RID::godot_icall_RID_Ctor", godot_icall_RID_Ctor);
+ GDMonoUtils::add_internal_call("Godot.RID::godot_icall_RID_Dtor", godot_icall_RID_Dtor);
+ GDMonoUtils::add_internal_call("Godot.RID::godot_icall_RID_get_id", godot_icall_RID_get_id);
}
#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/scene_tree_glue.cpp b/modules/mono/glue/scene_tree_glue.cpp
index 53d6c1436d..88695201a3 100644
--- a/modules/mono/glue/scene_tree_glue.cpp
+++ b/modules/mono/glue/scene_tree_glue.cpp
@@ -80,7 +80,7 @@ Array *godot_icall_SceneTree_get_nodes_in_group_Generic(SceneTree *ptr, StringNa
}
void godot_register_scene_tree_icalls() {
- mono_add_internal_call("Godot.SceneTree::godot_icall_SceneTree_get_nodes_in_group_Generic", (void *)godot_icall_SceneTree_get_nodes_in_group_Generic);
+ GDMonoUtils::add_internal_call("Godot.SceneTree::godot_icall_SceneTree_get_nodes_in_group_Generic", godot_icall_SceneTree_get_nodes_in_group_Generic);
}
#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/string_glue.cpp b/modules/mono/glue/string_glue.cpp
index 9271731573..d71d175418 100644
--- a/modules/mono/glue/string_glue.cpp
+++ b/modules/mono/glue/string_glue.cpp
@@ -68,12 +68,12 @@ MonoString *godot_icall_String_sha256_text(MonoString *p_str) {
}
void godot_register_string_icalls() {
- mono_add_internal_call("Godot.StringExtensions::godot_icall_String_md5_buffer", (void *)godot_icall_String_md5_buffer);
- mono_add_internal_call("Godot.StringExtensions::godot_icall_String_md5_text", (void *)godot_icall_String_md5_text);
- mono_add_internal_call("Godot.StringExtensions::godot_icall_String_rfind", (void *)godot_icall_String_rfind);
- mono_add_internal_call("Godot.StringExtensions::godot_icall_String_rfindn", (void *)godot_icall_String_rfindn);
- mono_add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_buffer", (void *)godot_icall_String_sha256_buffer);
- mono_add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_text", (void *)godot_icall_String_sha256_text);
+ GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_md5_buffer", godot_icall_String_md5_buffer);
+ GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_md5_text", godot_icall_String_md5_text);
+ GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_rfind", godot_icall_String_rfind);
+ GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_rfindn", godot_icall_String_rfindn);
+ GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_buffer", godot_icall_String_sha256_buffer);
+ GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_text", godot_icall_String_sha256_text);
}
#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/string_name_glue.cpp b/modules/mono/glue/string_name_glue.cpp
index 9323e3bbb3..fc2bf32b95 100644
--- a/modules/mono/glue/string_name_glue.cpp
+++ b/modules/mono/glue/string_name_glue.cpp
@@ -53,10 +53,10 @@ MonoBoolean godot_icall_StringName_is_empty(StringName *p_ptr) {
}
void godot_register_string_name_icalls() {
- mono_add_internal_call("Godot.StringName::godot_icall_StringName_Ctor", (void *)godot_icall_StringName_Ctor);
- mono_add_internal_call("Godot.StringName::godot_icall_StringName_Dtor", (void *)godot_icall_StringName_Dtor);
- mono_add_internal_call("Godot.StringName::godot_icall_StringName_operator_String", (void *)godot_icall_StringName_operator_String);
- mono_add_internal_call("Godot.StringName::godot_icall_StringName_is_empty", (void *)godot_icall_StringName_is_empty);
+ GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_Ctor", godot_icall_StringName_Ctor);
+ GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_Dtor", godot_icall_StringName_Dtor);
+ GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_operator_String", godot_icall_StringName_operator_String);
+ GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_is_empty", godot_icall_StringName_is_empty);
}
#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp
index 6575cbc1c8..b734f52e4e 100644
--- a/modules/mono/mono_gd/gd_mono_class.cpp
+++ b/modules/mono/mono_gd/gd_mono_class.cpp
@@ -290,7 +290,7 @@ bool GDMonoClass::has_public_parameterless_ctor() {
return ctor && ctor->get_visibility() == IMonoClassMember::PUBLIC;
}
-GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, int p_params_count) {
+GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, uint16_t p_params_count) {
MethodKey key = MethodKey(p_name, p_params_count);
GDMonoMethod **match = methods.getptr(key);
@@ -330,7 +330,7 @@ GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName
return get_method(p_raw_method, p_name, params_count);
}
-GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count) {
+GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName &p_name, uint16_t p_params_count) {
ERR_FAIL_NULL_V(p_raw_method, nullptr);
MethodKey key = MethodKey(p_name, p_params_count);
diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h
index 87db2fa033..b93dfec30a 100644
--- a/modules/mono/mono_gd/gd_mono_class.h
+++ b/modules/mono/mono_gd/gd_mono_class.h
@@ -59,13 +59,12 @@ class GDMonoClass {
MethodKey() {}
- MethodKey(const StringName &p_name, int p_params_count) {
- name = p_name;
- params_count = p_params_count;
+ MethodKey(const StringName &p_name, uint16_t p_params_count) :
+ name(p_name), params_count(p_params_count) {
}
StringName name;
- int params_count;
+ uint16_t params_count = 0;
};
StringName namespace_name;
@@ -139,10 +138,10 @@ public:
bool implements_interface(GDMonoClass *p_interface);
bool has_public_parameterless_ctor();
- GDMonoMethod *get_method(const StringName &p_name, int p_params_count = 0);
+ GDMonoMethod *get_method(const StringName &p_name, uint16_t p_params_count = 0);
GDMonoMethod *get_method(MonoMethod *p_raw_method);
GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name);
- GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count);
+ GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name, uint16_t p_params_count);
GDMonoMethod *get_method_with_desc(const String &p_description, bool p_include_namespace);
GDMonoField *get_field(const StringName &p_name);
diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp
index 00a1e1e507..61d7f64a2a 100644
--- a/modules/mono/mono_gd/gd_mono_field.cpp
+++ b/modules/mono/mono_gd/gd_mono_field.cpp
@@ -46,29 +46,15 @@ void GDMonoField::set_value_raw(MonoObject *p_object, void *p_ptr) {
}
void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_value) {
-#define SET_FROM_STRUCT(m_type) \
- { \
- GDMonoMarshal::M_##m_type from = MARSHALLED_OUT(m_type, p_value.operator ::m_type()); \
- mono_field_set_value(p_object, mono_field, &from); \
- }
-
-#define SET_FROM_ARRAY(m_type) \
- { \
- MonoArray *managed = GDMonoMarshal::m_type##_to_mono_array(p_value.operator ::m_type()); \
- mono_field_set_value(p_object, mono_field, managed); \
- }
-
switch (type.type_encoding) {
case MONO_TYPE_BOOLEAN: {
MonoBoolean val = p_value.operator bool();
mono_field_set_value(p_object, mono_field, &val);
} break;
-
case MONO_TYPE_CHAR: {
int16_t val = p_value.operator unsigned short();
mono_field_set_value(p_object, mono_field, &val);
} break;
-
case MONO_TYPE_I1: {
int8_t val = p_value.operator signed char();
mono_field_set_value(p_object, mono_field, &val);
@@ -85,7 +71,6 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
int64_t val = p_value.operator int64_t();
mono_field_set_value(p_object, mono_field, &val);
} break;
-
case MONO_TYPE_U1: {
uint8_t val = p_value.operator unsigned char();
mono_field_set_value(p_object, mono_field, &val);
@@ -102,93 +87,92 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
uint64_t val = p_value.operator uint64_t();
mono_field_set_value(p_object, mono_field, &val);
} break;
-
case MONO_TYPE_R4: {
float val = p_value.operator float();
mono_field_set_value(p_object, mono_field, &val);
} break;
-
case MONO_TYPE_R8: {
double val = p_value.operator double();
mono_field_set_value(p_object, mono_field, &val);
} break;
-
- case MONO_TYPE_STRING: {
- if (p_value.get_type() == Variant::NIL) {
- // Otherwise, Variant -> String would return the string "Null"
- MonoString *mono_string = nullptr;
- mono_field_set_value(p_object, mono_field, mono_string);
- } else {
- MonoString *mono_string = GDMonoMarshal::mono_string_from_godot(p_value);
- mono_field_set_value(p_object, mono_field, mono_string);
- }
- } break;
-
case MONO_TYPE_VALUETYPE: {
GDMonoClass *tclass = type.type_class;
if (tclass == CACHED_CLASS(Vector2)) {
- SET_FROM_STRUCT(Vector2);
+ GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_value.operator ::Vector2());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
if (tclass == CACHED_CLASS(Vector2i)) {
- SET_FROM_STRUCT(Vector2i);
+ GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_value.operator ::Vector2i());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
if (tclass == CACHED_CLASS(Rect2)) {
- SET_FROM_STRUCT(Rect2);
+ GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_value.operator ::Rect2());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
if (tclass == CACHED_CLASS(Rect2i)) {
- SET_FROM_STRUCT(Rect2i);
+ GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_value.operator ::Rect2i());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
if (tclass == CACHED_CLASS(Transform2D)) {
- SET_FROM_STRUCT(Transform2D);
+ GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_value.operator ::Transform2D());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
if (tclass == CACHED_CLASS(Vector3)) {
- SET_FROM_STRUCT(Vector3);
+ GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_value.operator ::Vector3());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
if (tclass == CACHED_CLASS(Vector3i)) {
- SET_FROM_STRUCT(Vector3i);
+ GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_value.operator ::Vector3i());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
if (tclass == CACHED_CLASS(Basis)) {
- SET_FROM_STRUCT(Basis);
+ GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_value.operator ::Basis());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
if (tclass == CACHED_CLASS(Quat)) {
- SET_FROM_STRUCT(Quat);
+ GDMonoMarshal::M_Quat from = MARSHALLED_OUT(Quat, p_value.operator ::Quat());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
if (tclass == CACHED_CLASS(Transform)) {
- SET_FROM_STRUCT(Transform);
+ GDMonoMarshal::M_Transform from = MARSHALLED_OUT(Transform, p_value.operator ::Transform());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
if (tclass == CACHED_CLASS(AABB)) {
- SET_FROM_STRUCT(AABB);
+ GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_value.operator ::AABB());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
if (tclass == CACHED_CLASS(Color)) {
- SET_FROM_STRUCT(Color);
+ GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_value.operator ::Color());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
if (tclass == CACHED_CLASS(Plane)) {
- SET_FROM_STRUCT(Plane);
+ GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_value.operator ::Plane());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
@@ -267,118 +251,35 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
ERR_FAIL_MSG("Attempted to set the value of a field of unmarshallable type: '" + tclass->get_name() + "'.");
} break;
-
+ case MONO_TYPE_STRING: {
+ if (p_value.get_type() == Variant::NIL) {
+ // Otherwise, Variant -> String would return the string "Null"
+ MonoString *mono_string = nullptr;
+ mono_field_set_value(p_object, mono_field, mono_string);
+ } else {
+ MonoString *mono_string = GDMonoMarshal::mono_string_from_godot(p_value);
+ mono_field_set_value(p_object, mono_field, mono_string);
+ }
+ } break;
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
- MonoArrayType *array_type = mono_type_get_array_type(type.type_class->get_mono_type());
-
- if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) {
- SET_FROM_ARRAY(Array);
- break;
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) {
- SET_FROM_ARRAY(PackedByteArray);
- break;
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) {
- SET_FROM_ARRAY(PackedInt32Array);
- break;
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) {
- SET_FROM_ARRAY(PackedInt64Array);
- break;
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(float)) {
- SET_FROM_ARRAY(PackedFloat32Array);
- break;
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(double)) {
- SET_FROM_ARRAY(PackedFloat64Array);
- break;
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(String)) {
- SET_FROM_ARRAY(PackedStringArray);
- break;
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) {
- SET_FROM_ARRAY(PackedVector2Array);
- break;
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) {
- SET_FROM_ARRAY(PackedVector3Array);
- break;
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Color)) {
- SET_FROM_ARRAY(PackedColorArray);
- break;
- }
-
- GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass);
- if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) {
- MonoArray *managed = GDMonoMarshal::Array_to_mono_array(p_value.operator ::Array(), array_type_class);
+ MonoArray *managed = GDMonoMarshal::variant_to_mono_array(p_value, type.type_class);
+ if (likely(managed != nullptr)) {
mono_field_set_value(p_object, mono_field, managed);
- break;
}
-
- ERR_FAIL_MSG("Attempted to convert Variant to a managed array of unmarshallable element type.");
} break;
-
case MONO_TYPE_CLASS: {
- GDMonoClass *type_class = type.type_class;
-
- // GodotObject
- if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
- MonoObject *managed = GDMonoUtils::unmanaged_get_managed(p_value.operator Object *());
+ MonoObject *managed = GDMonoMarshal::variant_to_mono_object_of_class(p_value, type.type_class);
+ if (likely(managed != nullptr)) {
mono_field_set_value(p_object, mono_field, managed);
- break;
}
-
- if (CACHED_CLASS(StringName) == type_class) {
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator StringName());
- mono_field_set_value(p_object, mono_field, managed);
- break;
- }
-
- if (CACHED_CLASS(NodePath) == type_class) {
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath());
- mono_field_set_value(p_object, mono_field, managed);
- break;
- }
-
- if (CACHED_CLASS(RID) == type_class) {
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator ::RID());
- mono_field_set_value(p_object, mono_field, managed);
- break;
- }
-
- // Godot.Collections.Dictionary or IDictionary
- if (CACHED_CLASS(Dictionary) == type_class || type_class == CACHED_CLASS(System_Collections_IDictionary)) {
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary));
- mono_field_set_value(p_object, mono_field, managed);
- break;
- }
-
- // Godot.Collections.Array or ICollection or IEnumerable
- if (CACHED_CLASS(Array) == type_class ||
- type_class == CACHED_CLASS(System_Collections_ICollection) ||
- type_class == CACHED_CLASS(System_Collections_IEnumerable)) {
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
+ } break;
+ case MONO_TYPE_GENERICINST: {
+ MonoObject *managed = GDMonoMarshal::variant_to_mono_object_of_genericinst(p_value, type.type_class);
+ if (likely(managed != nullptr)) {
mono_field_set_value(p_object, mono_field, managed);
- break;
}
-
- ERR_FAIL_MSG("Attempted to set the value of a field of unmarshallable type: '" + type_class->get_name() + "'.");
} break;
-
case MONO_TYPE_OBJECT: {
// Variant
switch (p_value.get_type()) {
@@ -404,43 +305,56 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
mono_field_set_value(p_object, mono_field, mono_string);
} break;
case Variant::VECTOR2: {
- SET_FROM_STRUCT(Vector2);
+ GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_value.operator ::Vector2());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::VECTOR2I: {
- SET_FROM_STRUCT(Vector2i);
+ GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_value.operator ::Vector2i());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::RECT2: {
- SET_FROM_STRUCT(Rect2);
+ GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_value.operator ::Rect2());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::RECT2I: {
- SET_FROM_STRUCT(Rect2i);
+ GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_value.operator ::Rect2i());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::VECTOR3: {
- SET_FROM_STRUCT(Vector3);
+ GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_value.operator ::Vector3());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::VECTOR3I: {
- SET_FROM_STRUCT(Vector3i);
+ GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_value.operator ::Vector3i());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::TRANSFORM2D: {
- SET_FROM_STRUCT(Transform2D);
+ GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_value.operator ::Transform2D());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::PLANE: {
- SET_FROM_STRUCT(Plane);
+ GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_value.operator ::Plane());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::QUAT: {
- SET_FROM_STRUCT(Quat);
+ GDMonoMarshal::M_Quat from = MARSHALLED_OUT(Quat, p_value.operator ::Quat());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::AABB: {
- SET_FROM_STRUCT(AABB);
+ GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_value.operator ::AABB());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::BASIS: {
- SET_FROM_STRUCT(Basis);
+ GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_value.operator ::Basis());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::TRANSFORM: {
- SET_FROM_STRUCT(Transform);
+ GDMonoMarshal::M_Transform from = MARSHALLED_OUT(Transform, p_value.operator ::Transform());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::COLOR: {
- SET_FROM_STRUCT(Color);
+ GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_value.operator ::Color());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::STRING_NAME: {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator StringName());
@@ -475,106 +389,49 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
mono_field_set_value(p_object, mono_field, managed);
} break;
case Variant::PACKED_BYTE_ARRAY: {
- SET_FROM_ARRAY(PackedByteArray);
+ MonoArray *managed = GDMonoMarshal::PackedByteArray_to_mono_array(p_value.operator ::PackedByteArray());
+ mono_field_set_value(p_object, mono_field, managed);
} break;
case Variant::PACKED_INT32_ARRAY: {
- SET_FROM_ARRAY(PackedInt32Array);
+ MonoArray *managed = GDMonoMarshal::PackedInt32Array_to_mono_array(p_value.operator ::PackedInt32Array());
+ mono_field_set_value(p_object, mono_field, managed);
} break;
case Variant::PACKED_INT64_ARRAY: {
- SET_FROM_ARRAY(PackedInt64Array);
+ MonoArray *managed = GDMonoMarshal::PackedInt64Array_to_mono_array(p_value.operator ::PackedInt64Array());
+ mono_field_set_value(p_object, mono_field, managed);
} break;
case Variant::PACKED_FLOAT32_ARRAY: {
- SET_FROM_ARRAY(PackedFloat32Array);
+ MonoArray *managed = GDMonoMarshal::PackedFloat32Array_to_mono_array(p_value.operator ::PackedFloat32Array());
+ mono_field_set_value(p_object, mono_field, managed);
} break;
case Variant::PACKED_FLOAT64_ARRAY: {
- SET_FROM_ARRAY(PackedFloat64Array);
+ MonoArray *managed = GDMonoMarshal::PackedFloat64Array_to_mono_array(p_value.operator ::PackedFloat64Array());
+ mono_field_set_value(p_object, mono_field, managed);
} break;
case Variant::PACKED_STRING_ARRAY: {
- SET_FROM_ARRAY(PackedStringArray);
+ MonoArray *managed = GDMonoMarshal::PackedStringArray_to_mono_array(p_value.operator ::PackedStringArray());
+ mono_field_set_value(p_object, mono_field, managed);
} break;
case Variant::PACKED_VECTOR2_ARRAY: {
- SET_FROM_ARRAY(PackedVector2Array);
+ MonoArray *managed = GDMonoMarshal::PackedVector2Array_to_mono_array(p_value.operator ::PackedVector2Array());
+ mono_field_set_value(p_object, mono_field, managed);
} break;
case Variant::PACKED_VECTOR3_ARRAY: {
- SET_FROM_ARRAY(PackedVector3Array);
+ MonoArray *managed = GDMonoMarshal::PackedVector3Array_to_mono_array(p_value.operator ::PackedVector3Array());
+ mono_field_set_value(p_object, mono_field, managed);
} break;
case Variant::PACKED_COLOR_ARRAY: {
- SET_FROM_ARRAY(PackedColorArray);
+ MonoArray *managed = GDMonoMarshal::PackedColorArray_to_mono_array(p_value.operator ::PackedColorArray());
+ mono_field_set_value(p_object, mono_field, managed);
} break;
default:
break;
}
} break;
-
- case MONO_TYPE_GENERICINST: {
- MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type.type_class->get_mono_type());
-
- // Godot.Collections.Dictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), type.type_class);
- mono_field_set_value(p_object, mono_field, managed);
- break;
- }
-
- // Godot.Collections.Array<T>
- if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), type.type_class);
- mono_field_set_value(p_object, mono_field, managed);
- break;
- }
-
- // System.Collections.Generic.Dictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) {
- MonoReflectionType *key_reftype = nullptr;
- MonoReflectionType *value_reftype = nullptr;
- GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype);
- MonoObject *managed = GDMonoMarshal::Dictionary_to_system_generic_dict(p_value.operator Dictionary(),
- type.type_class, key_reftype, value_reftype);
- mono_field_set_value(p_object, mono_field, managed);
- break;
- }
-
- // System.Collections.Generic.List<T>
- if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) {
- MonoReflectionType *elem_reftype = nullptr;
- GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
- MonoObject *managed = GDMonoMarshal::Array_to_system_generic_list(p_value.operator Array(),
- type.type_class, elem_reftype);
- mono_field_set_value(p_object, mono_field, managed);
- break;
- }
-
- // IDictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_generic_idictionary(reftype)) {
- MonoReflectionType *key_reftype;
- MonoReflectionType *value_reftype;
- GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype);
- GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype);
-
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), godot_dict_class);
- mono_field_set_value(p_object, mono_field, managed);
- break;
- }
-
- // ICollection<T> or IEnumerable<T>
- if (GDMonoUtils::Marshal::type_is_generic_icollection(reftype) || GDMonoUtils::Marshal::type_is_generic_ienumerable(reftype)) {
- MonoReflectionType *elem_reftype;
- GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
- GDMonoClass *godot_array_class = GDMonoUtils::Marshal::make_generic_array_type(elem_reftype);
-
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), godot_array_class);
- mono_field_set_value(p_object, mono_field, managed);
- break;
- }
- } break;
-
default: {
ERR_PRINT("Attempted to set the value of a field of unexpected type encoding: " + itos(type.type_encoding) + ".");
} break;
}
-
-#undef SET_FROM_ARRAY_AND_BREAK
-#undef SET_FROM_STRUCT_AND_BREAK
}
MonoObject *GDMonoField::get_value(MonoObject *p_object) {
diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp
index b8ee0204c4..7584e7ff0d 100644
--- a/modules/mono/mono_gd/gd_mono_log.cpp
+++ b/modules/mono/mono_gd/gd_mono_log.cpp
@@ -160,13 +160,13 @@ void GDMonoLog::initialize() {
OS::Date date_now = OS::get_singleton()->get_date();
OS::Time time_now = OS::get_singleton()->get_time();
- String log_file_name = str_format("%d_%02d_%02d %02d.%02d.%02d",
+ String log_file_name = str_format("%04d-%02d-%02d_%02d.%02d.%02d",
date_now.year, date_now.month, date_now.day,
time_now.hour, time_now.min, time_now.sec);
- log_file_name += str_format(" (%d)", OS::get_singleton()->get_process_id());
+ log_file_name += str_format("_%d", OS::get_singleton()->get_process_id());
- log_file_name += ".txt";
+ log_file_name += ".log";
log_file_path = logs_dir.plus_file(log_file_name);
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
index eee880ba60..64b350f270 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -311,152 +311,590 @@ bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_
return false;
}
-MonoObject *variant_to_mono_object(const Variant *p_var) {
- ManagedType type;
+MonoString *variant_to_mono_string(const Variant &p_var) {
+ if (p_var.get_type() == Variant::NIL) {
+ return nullptr; // Otherwise, Variant -> String would return the string "Null"
+ }
+ return mono_string_from_godot(p_var.operator String());
+}
+
+MonoArray *variant_to_mono_array(const Variant &p_var, GDMonoClass *p_type_class) {
+ MonoArrayType *array_type = mono_type_get_array_type(p_type_class->get_mono_type());
+
+ if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) {
+ return Array_to_mono_array(p_var.operator Array());
+ }
+
+ if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) {
+ return PackedByteArray_to_mono_array(p_var.operator PackedByteArray());
+ }
+
+ if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) {
+ return PackedInt32Array_to_mono_array(p_var.operator PackedInt32Array());
+ }
+
+ if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) {
+ return PackedInt64Array_to_mono_array(p_var.operator PackedInt64Array());
+ }
+
+ if (array_type->eklass == CACHED_CLASS_RAW(float)) {
+ return PackedFloat32Array_to_mono_array(p_var.operator PackedFloat32Array());
+ }
+
+ if (array_type->eklass == CACHED_CLASS_RAW(double)) {
+ return PackedFloat64Array_to_mono_array(p_var.operator PackedFloat64Array());
+ }
+
+ if (array_type->eklass == CACHED_CLASS_RAW(String)) {
+ return PackedStringArray_to_mono_array(p_var.operator PackedStringArray());
+ }
+
+ if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) {
+ return PackedVector2Array_to_mono_array(p_var.operator PackedVector2Array());
+ }
+
+ if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) {
+ return PackedVector3Array_to_mono_array(p_var.operator PackedVector3Array());
+ }
+
+ if (array_type->eklass == CACHED_CLASS_RAW(Color)) {
+ return PackedColorArray_to_mono_array(p_var.operator PackedColorArray());
+ }
+
+ if (mono_class_is_assignable_from(CACHED_CLASS(GodotObject)->get_mono_ptr(), array_type->eklass)) {
+ return Array_to_mono_array(p_var.operator ::Array(), array_type->eklass);
+ }
+
+ ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to array of unsupported element type:" +
+ GDMonoClass::get_full_name(array_type->eklass) + "'.");
+}
+
+MonoObject *variant_to_mono_object_of_class(const Variant &p_var, GDMonoClass *p_type_class) {
+ // GodotObject
+ if (CACHED_CLASS(GodotObject)->is_assignable_from(p_type_class)) {
+ return GDMonoUtils::unmanaged_get_managed(p_var.operator Object *());
+ }
+
+ if (CACHED_CLASS(StringName) == p_type_class) {
+ return GDMonoUtils::create_managed_from(p_var.operator StringName());
+ }
+
+ if (CACHED_CLASS(NodePath) == p_type_class) {
+ return GDMonoUtils::create_managed_from(p_var.operator NodePath());
+ }
- type.type_encoding = MONO_TYPE_OBJECT;
- // type.type_class is not needed when we specify the MONO_TYPE_OBJECT encoding
+ if (CACHED_CLASS(RID) == p_type_class) {
+ return GDMonoUtils::create_managed_from(p_var.operator ::RID());
+ }
+
+ // Godot.Collections.Dictionary or IDictionary
+ if (CACHED_CLASS(Dictionary) == p_type_class || CACHED_CLASS(System_Collections_IDictionary) == p_type_class) {
+ return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), CACHED_CLASS(Dictionary));
+ }
- return variant_to_mono_object(p_var, type);
+ // Godot.Collections.Array or ICollection or IEnumerable
+ if (CACHED_CLASS(Array) == p_type_class ||
+ CACHED_CLASS(System_Collections_ICollection) == p_type_class ||
+ CACHED_CLASS(System_Collections_IEnumerable) == p_type_class) {
+ return GDMonoUtils::create_managed_from(p_var.operator Array(), CACHED_CLASS(Array));
+ }
+
+ ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type: '" +
+ p_type_class->get_full_name() + "'.");
}
-MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_type) {
+MonoObject *variant_to_mono_object_of_genericinst(const Variant &p_var, GDMonoClass *p_type_class) {
+ MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type_class->get_mono_type());
+
+ // Godot.Collections.Dictionary<TKey, TValue>
+ if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
+ return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), p_type_class);
+ }
+
+ // Godot.Collections.Array<T>
+ if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
+ return GDMonoUtils::create_managed_from(p_var.operator Array(), p_type_class);
+ }
+
+ // System.Collections.Generic.Dictionary<TKey, TValue>
+ if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) {
+ MonoReflectionType *key_reftype = nullptr;
+ MonoReflectionType *value_reftype = nullptr;
+ GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype);
+ return Dictionary_to_system_generic_dict(p_var.operator Dictionary(), p_type_class, key_reftype, value_reftype);
+ }
+
+ // System.Collections.Generic.List<T>
+ if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) {
+ MonoReflectionType *elem_reftype = nullptr;
+ GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
+ return Array_to_system_generic_list(p_var.operator Array(), p_type_class, elem_reftype);
+ }
+
+ // IDictionary<TKey, TValue>
+ if (GDMonoUtils::Marshal::type_is_generic_idictionary(reftype)) {
+ MonoReflectionType *key_reftype;
+ MonoReflectionType *value_reftype;
+ GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype);
+ GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype);
+
+ return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), godot_dict_class);
+ }
+
+ // ICollection<T> or IEnumerable<T>
+ if (GDMonoUtils::Marshal::type_is_generic_icollection(reftype) || GDMonoUtils::Marshal::type_is_generic_ienumerable(reftype)) {
+ MonoReflectionType *elem_reftype;
+ GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
+ GDMonoClass *godot_array_class = GDMonoUtils::Marshal::make_generic_array_type(elem_reftype);
+
+ return GDMonoUtils::create_managed_from(p_var.operator Array(), godot_array_class);
+ }
+
+ ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported generic type: '" +
+ p_type_class->get_full_name() + "'.");
+}
+
+MonoObject *variant_to_mono_object(const Variant &p_var) {
+ // Variant
+ switch (p_var.get_type()) {
+ case Variant::BOOL: {
+ MonoBoolean val = p_var.operator bool();
+ return BOX_BOOLEAN(val);
+ }
+ case Variant::INT: {
+ int64_t val = p_var.operator int64_t();
+ return BOX_INT64(val);
+ }
+ case Variant::FLOAT: {
+#ifdef REAL_T_IS_DOUBLE
+ double val = p_var.operator double();
+ return BOX_DOUBLE(val);
+#else
+ float val = p_var.operator float();
+ return BOX_FLOAT(val);
+#endif
+ }
+ case Variant::STRING:
+ return (MonoObject *)mono_string_from_godot(p_var.operator String());
+ case Variant::VECTOR2: {
+ GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_var.operator ::Vector2());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2), &from);
+ }
+ case Variant::VECTOR2I: {
+ GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_var.operator ::Vector2i());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2i), &from);
+ }
+ case Variant::RECT2: {
+ GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_var.operator ::Rect2());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2), &from);
+ }
+ case Variant::RECT2I: {
+ GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_var.operator ::Rect2i());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2i), &from);
+ }
+ case Variant::VECTOR3: {
+ GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_var.operator ::Vector3());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3), &from);
+ }
+ case Variant::VECTOR3I: {
+ GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_var.operator ::Vector3i());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3i), &from);
+ }
+ case Variant::TRANSFORM2D: {
+ GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_var.operator ::Transform2D());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform2D), &from);
+ }
+ case Variant::PLANE: {
+ GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_var.operator ::Plane());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from);
+ }
+ case Variant::QUAT: {
+ GDMonoMarshal::M_Quat from = MARSHALLED_OUT(Quat, p_var.operator ::Quat());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Quat), &from);
+ }
+ case Variant::AABB: {
+ GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_var.operator ::AABB());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(AABB), &from);
+ }
+ case Variant::BASIS: {
+ GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_var.operator ::Basis());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Basis), &from);
+ }
+ case Variant::TRANSFORM: {
+ GDMonoMarshal::M_Transform from = MARSHALLED_OUT(Transform, p_var.operator ::Transform());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform), &from);
+ }
+ case Variant::COLOR: {
+ GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var.operator ::Color());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from);
+ }
+ case Variant::STRING_NAME:
+ return GDMonoUtils::create_managed_from(p_var.operator StringName());
+ case Variant::NODE_PATH:
+ return GDMonoUtils::create_managed_from(p_var.operator NodePath());
+ case Variant::RID:
+ return GDMonoUtils::create_managed_from(p_var.operator ::RID());
+ case Variant::OBJECT:
+ return GDMonoUtils::unmanaged_get_managed(p_var.operator Object *());
+ case Variant::CALLABLE: {
+ GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var.operator Callable());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from);
+ }
+ case Variant::SIGNAL: {
+ GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var.operator Signal());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from);
+ }
+ case Variant::DICTIONARY:
+ return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), CACHED_CLASS(Dictionary));
+ case Variant::ARRAY:
+ return GDMonoUtils::create_managed_from(p_var.operator Array(), CACHED_CLASS(Array));
+ case Variant::PACKED_BYTE_ARRAY:
+ return (MonoObject *)PackedByteArray_to_mono_array(p_var.operator PackedByteArray());
+ case Variant::PACKED_INT32_ARRAY:
+ return (MonoObject *)PackedInt32Array_to_mono_array(p_var.operator PackedInt32Array());
+ case Variant::PACKED_INT64_ARRAY:
+ return (MonoObject *)PackedInt64Array_to_mono_array(p_var.operator PackedInt64Array());
+ case Variant::PACKED_FLOAT32_ARRAY:
+ return (MonoObject *)PackedFloat32Array_to_mono_array(p_var.operator PackedFloat32Array());
+ case Variant::PACKED_FLOAT64_ARRAY:
+ return (MonoObject *)PackedFloat64Array_to_mono_array(p_var.operator PackedFloat64Array());
+ case Variant::PACKED_STRING_ARRAY:
+ return (MonoObject *)PackedStringArray_to_mono_array(p_var.operator PackedStringArray());
+ case Variant::PACKED_VECTOR2_ARRAY:
+ return (MonoObject *)PackedVector2Array_to_mono_array(p_var.operator PackedVector2Array());
+ case Variant::PACKED_VECTOR3_ARRAY:
+ return (MonoObject *)PackedVector3Array_to_mono_array(p_var.operator PackedVector3Array());
+ case Variant::PACKED_COLOR_ARRAY:
+ return (MonoObject *)PackedColorArray_to_mono_array(p_var.operator PackedColorArray());
+ default:
+ return nullptr;
+ }
+}
+
+size_t variant_get_managed_unboxed_size(const ManagedType &p_type) {
+ // This method prints no errors for unsupported types. It's called on all methods, not only
+ // those that end up being invoked with Variant parameters.
+
+ // For MonoObject* we return 0, as it doesn't need to be stored.
+ constexpr size_t zero_for_mono_object = 0;
+
+ switch (p_type.type_encoding) {
+ case MONO_TYPE_BOOLEAN:
+ return sizeof(MonoBoolean);
+ case MONO_TYPE_CHAR:
+ return sizeof(uint16_t);
+ case MONO_TYPE_I1:
+ return sizeof(int8_t);
+ case MONO_TYPE_I2:
+ return sizeof(int16_t);
+ case MONO_TYPE_I4:
+ return sizeof(int32_t);
+ case MONO_TYPE_I8:
+ return sizeof(int64_t);
+ case MONO_TYPE_U1:
+ return sizeof(uint8_t);
+ case MONO_TYPE_U2:
+ return sizeof(uint16_t);
+ case MONO_TYPE_U4:
+ return sizeof(uint32_t);
+ case MONO_TYPE_U8:
+ return sizeof(uint64_t);
+ case MONO_TYPE_R4:
+ return sizeof(float);
+ case MONO_TYPE_R8:
+ return sizeof(double);
+ case MONO_TYPE_VALUETYPE: {
+ GDMonoClass *vtclass = p_type.type_class;
+
+#define RETURN_CHECK_FOR_STRUCT(m_struct) \
+ if (vtclass == CACHED_CLASS(m_struct)) { \
+ return sizeof(M_##m_struct); \
+ }
+
+ RETURN_CHECK_FOR_STRUCT(Vector2);
+ RETURN_CHECK_FOR_STRUCT(Vector2i);
+ RETURN_CHECK_FOR_STRUCT(Rect2);
+ RETURN_CHECK_FOR_STRUCT(Rect2i);
+ RETURN_CHECK_FOR_STRUCT(Transform2D);
+ RETURN_CHECK_FOR_STRUCT(Vector3);
+ RETURN_CHECK_FOR_STRUCT(Vector3i);
+ RETURN_CHECK_FOR_STRUCT(Basis);
+ RETURN_CHECK_FOR_STRUCT(Quat);
+ RETURN_CHECK_FOR_STRUCT(Transform);
+ RETURN_CHECK_FOR_STRUCT(AABB);
+ RETURN_CHECK_FOR_STRUCT(Color);
+ RETURN_CHECK_FOR_STRUCT(Plane);
+ RETURN_CHECK_FOR_STRUCT(Callable);
+ RETURN_CHECK_FOR_STRUCT(SignalInfo);
+
+#undef RETURN_CHECK_FOR_STRUCT
+
+ if (mono_class_is_enum(vtclass->get_mono_ptr())) {
+ MonoType *enum_basetype = mono_class_enum_basetype(vtclass->get_mono_ptr());
+ switch (mono_type_get_type(enum_basetype)) {
+ case MONO_TYPE_BOOLEAN:
+ return sizeof(MonoBoolean);
+ case MONO_TYPE_CHAR:
+ return sizeof(uint16_t);
+ case MONO_TYPE_I1:
+ return sizeof(int8_t);
+ case MONO_TYPE_I2:
+ return sizeof(int16_t);
+ case MONO_TYPE_I4:
+ return sizeof(int32_t);
+ case MONO_TYPE_I8:
+ return sizeof(int64_t);
+ case MONO_TYPE_U1:
+ return sizeof(uint8_t);
+ case MONO_TYPE_U2:
+ return sizeof(uint16_t);
+ case MONO_TYPE_U4:
+ return sizeof(uint32_t);
+ case MONO_TYPE_U8:
+ return sizeof(uint64_t);
+ default: {
+ // Enum with unsupported base type. We return nullptr MonoObject* on error.
+ return zero_for_mono_object;
+ }
+ }
+ }
+
+ // Enum with unsupported value type. We return nullptr MonoObject* on error.
+ } break;
+ case MONO_TYPE_STRING:
+ return zero_for_mono_object;
+ case MONO_TYPE_ARRAY:
+ case MONO_TYPE_SZARRAY:
+ case MONO_TYPE_CLASS:
+ case MONO_TYPE_GENERICINST:
+ return zero_for_mono_object;
+ case MONO_TYPE_OBJECT:
+ return zero_for_mono_object;
+ }
+
+ // Unsupported type encoding. We return nullptr MonoObject* on error.
+ return zero_for_mono_object;
+}
+
+void *variant_to_managed_unboxed(const Variant &p_var, const ManagedType &p_type, void *r_buffer, unsigned int &r_offset) {
+#define RETURN_TYPE_VAL(m_type, m_val) \
+ *reinterpret_cast<m_type *>(r_buffer) = m_val; \
+ r_offset += sizeof(m_type); \
+ return r_buffer;
+
+ switch (p_type.type_encoding) {
+ case MONO_TYPE_BOOLEAN:
+ RETURN_TYPE_VAL(MonoBoolean, (MonoBoolean)p_var.operator bool());
+ case MONO_TYPE_CHAR:
+ RETURN_TYPE_VAL(uint16_t, p_var.operator unsigned short());
+ case MONO_TYPE_I1:
+ RETURN_TYPE_VAL(int8_t, p_var.operator signed char());
+ case MONO_TYPE_I2:
+ RETURN_TYPE_VAL(int16_t, p_var.operator signed short());
+ case MONO_TYPE_I4:
+ RETURN_TYPE_VAL(int32_t, p_var.operator signed int());
+ case MONO_TYPE_I8:
+ RETURN_TYPE_VAL(int64_t, p_var.operator int64_t());
+ case MONO_TYPE_U1:
+ RETURN_TYPE_VAL(uint8_t, p_var.operator unsigned char());
+ case MONO_TYPE_U2:
+ RETURN_TYPE_VAL(uint16_t, p_var.operator unsigned short());
+ case MONO_TYPE_U4:
+ RETURN_TYPE_VAL(uint32_t, p_var.operator unsigned int());
+ case MONO_TYPE_U8:
+ RETURN_TYPE_VAL(uint64_t, p_var.operator uint64_t());
+ case MONO_TYPE_R4:
+ RETURN_TYPE_VAL(float, p_var.operator float());
+ case MONO_TYPE_R8:
+ RETURN_TYPE_VAL(double, p_var.operator double());
+ case MONO_TYPE_VALUETYPE: {
+ GDMonoClass *vtclass = p_type.type_class;
+
+#define RETURN_CHECK_FOR_STRUCT(m_struct) \
+ if (vtclass == CACHED_CLASS(m_struct)) { \
+ GDMonoMarshal::M_##m_struct from = MARSHALLED_OUT(m_struct, p_var.operator ::m_struct()); \
+ RETURN_TYPE_VAL(M_##m_struct, from); \
+ }
+
+ RETURN_CHECK_FOR_STRUCT(Vector2);
+ RETURN_CHECK_FOR_STRUCT(Vector2i);
+ RETURN_CHECK_FOR_STRUCT(Rect2);
+ RETURN_CHECK_FOR_STRUCT(Rect2i);
+ RETURN_CHECK_FOR_STRUCT(Transform2D);
+ RETURN_CHECK_FOR_STRUCT(Vector3);
+ RETURN_CHECK_FOR_STRUCT(Vector3i);
+ RETURN_CHECK_FOR_STRUCT(Basis);
+ RETURN_CHECK_FOR_STRUCT(Quat);
+ RETURN_CHECK_FOR_STRUCT(Transform);
+ RETURN_CHECK_FOR_STRUCT(AABB);
+ RETURN_CHECK_FOR_STRUCT(Color);
+ RETURN_CHECK_FOR_STRUCT(Plane);
+
+#undef RETURN_CHECK_FOR_STRUCT
+
+ if (vtclass == CACHED_CLASS(Callable)) {
+ GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var.operator Callable());
+ RETURN_TYPE_VAL(M_Callable, from);
+ }
+
+ if (vtclass == CACHED_CLASS(SignalInfo)) {
+ GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var.operator Signal());
+ RETURN_TYPE_VAL(M_SignalInfo, from);
+ }
+
+ if (mono_class_is_enum(vtclass->get_mono_ptr())) {
+ MonoType *enum_basetype = mono_class_enum_basetype(vtclass->get_mono_ptr());
+ switch (mono_type_get_type(enum_basetype)) {
+ case MONO_TYPE_BOOLEAN: {
+ MonoBoolean val = p_var.operator bool();
+ RETURN_TYPE_VAL(MonoBoolean, val);
+ }
+ case MONO_TYPE_CHAR: {
+ uint16_t val = p_var.operator unsigned short();
+ RETURN_TYPE_VAL(uint16_t, val);
+ }
+ case MONO_TYPE_I1: {
+ int8_t val = p_var.operator signed char();
+ RETURN_TYPE_VAL(int8_t, val);
+ }
+ case MONO_TYPE_I2: {
+ int16_t val = p_var.operator signed short();
+ RETURN_TYPE_VAL(int16_t, val);
+ }
+ case MONO_TYPE_I4: {
+ int32_t val = p_var.operator signed int();
+ RETURN_TYPE_VAL(int32_t, val);
+ }
+ case MONO_TYPE_I8: {
+ int64_t val = p_var.operator int64_t();
+ RETURN_TYPE_VAL(int64_t, val);
+ }
+ case MONO_TYPE_U1: {
+ uint8_t val = p_var.operator unsigned char();
+ RETURN_TYPE_VAL(uint8_t, val);
+ }
+ case MONO_TYPE_U2: {
+ uint16_t val = p_var.operator unsigned short();
+ RETURN_TYPE_VAL(uint16_t, val);
+ }
+ case MONO_TYPE_U4: {
+ uint32_t val = p_var.operator unsigned int();
+ RETURN_TYPE_VAL(uint32_t, val);
+ }
+ case MONO_TYPE_U8: {
+ uint64_t val = p_var.operator uint64_t();
+ RETURN_TYPE_VAL(uint64_t, val);
+ }
+ default: {
+ ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to enum value of unsupported base type: '" +
+ GDMonoClass::get_full_name(mono_class_from_mono_type(enum_basetype)) + "'.");
+ }
+ }
+ }
+
+ ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported value type: '" +
+ p_type.type_class->get_full_name() + "'.");
+ } break;
+#undef RETURN_TYPE_VAL
+ case MONO_TYPE_STRING:
+ return variant_to_mono_string(p_var);
+ case MONO_TYPE_ARRAY:
+ case MONO_TYPE_SZARRAY:
+ return variant_to_mono_array(p_var, p_type.type_class);
+ case MONO_TYPE_CLASS:
+ return variant_to_mono_object_of_class(p_var, p_type.type_class);
+ case MONO_TYPE_GENERICINST:
+ return variant_to_mono_object_of_genericinst(p_var, p_type.type_class);
+ case MONO_TYPE_OBJECT:
+ return variant_to_mono_object(p_var);
+ }
+
+ ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type with encoding: " +
+ itos(p_type.type_encoding) + ".");
+}
+
+MonoObject *variant_to_mono_object(const Variant &p_var, const ManagedType &p_type) {
switch (p_type.type_encoding) {
case MONO_TYPE_BOOLEAN: {
- MonoBoolean val = p_var->operator bool();
+ MonoBoolean val = p_var.operator bool();
return BOX_BOOLEAN(val);
}
-
case MONO_TYPE_CHAR: {
- uint16_t val = p_var->operator unsigned short();
+ uint16_t val = p_var.operator unsigned short();
return BOX_UINT16(val);
}
-
case MONO_TYPE_I1: {
- int8_t val = p_var->operator signed char();
+ int8_t val = p_var.operator signed char();
return BOX_INT8(val);
}
case MONO_TYPE_I2: {
- int16_t val = p_var->operator signed short();
+ int16_t val = p_var.operator signed short();
return BOX_INT16(val);
}
case MONO_TYPE_I4: {
- int32_t val = p_var->operator signed int();
+ int32_t val = p_var.operator signed int();
return BOX_INT32(val);
}
case MONO_TYPE_I8: {
- int64_t val = p_var->operator int64_t();
+ int64_t val = p_var.operator int64_t();
return BOX_INT64(val);
}
-
case MONO_TYPE_U1: {
- uint8_t val = p_var->operator unsigned char();
+ uint8_t val = p_var.operator unsigned char();
return BOX_UINT8(val);
}
case MONO_TYPE_U2: {
- uint16_t val = p_var->operator unsigned short();
+ uint16_t val = p_var.operator unsigned short();
return BOX_UINT16(val);
}
case MONO_TYPE_U4: {
- uint32_t val = p_var->operator unsigned int();
+ uint32_t val = p_var.operator unsigned int();
return BOX_UINT32(val);
}
case MONO_TYPE_U8: {
- uint64_t val = p_var->operator uint64_t();
+ uint64_t val = p_var.operator uint64_t();
return BOX_UINT64(val);
}
-
case MONO_TYPE_R4: {
- float val = p_var->operator float();
+ float val = p_var.operator float();
return BOX_FLOAT(val);
}
case MONO_TYPE_R8: {
- double val = p_var->operator double();
+ double val = p_var.operator double();
return BOX_DOUBLE(val);
}
-
- case MONO_TYPE_STRING: {
- if (p_var->get_type() == Variant::NIL) {
- return nullptr; // Otherwise, Variant -> String would return the string "Null"
- }
- return (MonoObject *)mono_string_from_godot(p_var->operator String());
- } break;
-
case MONO_TYPE_VALUETYPE: {
GDMonoClass *vtclass = p_type.type_class;
- if (vtclass == CACHED_CLASS(Vector2)) {
- GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_var->operator ::Vector2());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2), &from);
- }
-
- if (vtclass == CACHED_CLASS(Vector2i)) {
- GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_var->operator ::Vector2i());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2i), &from);
- }
-
- if (vtclass == CACHED_CLASS(Rect2)) {
- GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_var->operator ::Rect2());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2), &from);
- }
-
- if (vtclass == CACHED_CLASS(Rect2i)) {
- GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_var->operator ::Rect2i());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2i), &from);
- }
-
- if (vtclass == CACHED_CLASS(Transform2D)) {
- GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_var->operator ::Transform2D());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform2D), &from);
- }
-
- if (vtclass == CACHED_CLASS(Vector3)) {
- GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_var->operator ::Vector3());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3), &from);
- }
-
- if (vtclass == CACHED_CLASS(Vector3i)) {
- GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_var->operator ::Vector3i());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3i), &from);
- }
-
- if (vtclass == CACHED_CLASS(Basis)) {
- GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_var->operator ::Basis());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Basis), &from);
- }
-
- if (vtclass == CACHED_CLASS(Quat)) {
- GDMonoMarshal::M_Quat from = MARSHALLED_OUT(Quat, p_var->operator ::Quat());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Quat), &from);
- }
-
- if (vtclass == CACHED_CLASS(Transform)) {
- GDMonoMarshal::M_Transform from = MARSHALLED_OUT(Transform, p_var->operator ::Transform());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform), &from);
- }
-
- if (vtclass == CACHED_CLASS(AABB)) {
- GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_var->operator ::AABB());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(AABB), &from);
- }
-
- if (vtclass == CACHED_CLASS(Color)) {
- GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var->operator ::Color());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from);
- }
+#define RETURN_CHECK_FOR_STRUCT(m_struct) \
+ if (vtclass == CACHED_CLASS(m_struct)) { \
+ GDMonoMarshal::M_##m_struct from = MARSHALLED_OUT(m_struct, p_var.operator ::m_struct()); \
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(m_struct), &from); \
+ }
- if (vtclass == CACHED_CLASS(Plane)) {
- GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_var->operator ::Plane());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from);
- }
+ RETURN_CHECK_FOR_STRUCT(Vector2);
+ RETURN_CHECK_FOR_STRUCT(Vector2i);
+ RETURN_CHECK_FOR_STRUCT(Rect2);
+ RETURN_CHECK_FOR_STRUCT(Rect2i);
+ RETURN_CHECK_FOR_STRUCT(Transform2D);
+ RETURN_CHECK_FOR_STRUCT(Vector3);
+ RETURN_CHECK_FOR_STRUCT(Vector3i);
+ RETURN_CHECK_FOR_STRUCT(Basis);
+ RETURN_CHECK_FOR_STRUCT(Quat);
+ RETURN_CHECK_FOR_STRUCT(Transform);
+ RETURN_CHECK_FOR_STRUCT(AABB);
+ RETURN_CHECK_FOR_STRUCT(Color);
+ RETURN_CHECK_FOR_STRUCT(Plane);
+
+#undef RETURN_CHECK_FOR_STRUCT
if (vtclass == CACHED_CLASS(Callable)) {
- GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var->operator Callable());
+ GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var.operator Callable());
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from);
}
if (vtclass == CACHED_CLASS(SignalInfo)) {
- GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var->operator Signal());
+ GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var.operator Signal());
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from);
}
@@ -465,316 +903,84 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
MonoClass *enum_baseclass = mono_class_from_mono_type(enum_basetype);
switch (mono_type_get_type(enum_basetype)) {
case MONO_TYPE_BOOLEAN: {
- MonoBoolean val = p_var->operator bool();
+ MonoBoolean val = p_var.operator bool();
return BOX_ENUM(enum_baseclass, val);
}
case MONO_TYPE_CHAR: {
- uint16_t val = p_var->operator unsigned short();
+ uint16_t val = p_var.operator unsigned short();
return BOX_ENUM(enum_baseclass, val);
}
case MONO_TYPE_I1: {
- int8_t val = p_var->operator signed char();
+ int8_t val = p_var.operator signed char();
return BOX_ENUM(enum_baseclass, val);
}
case MONO_TYPE_I2: {
- int16_t val = p_var->operator signed short();
+ int16_t val = p_var.operator signed short();
return BOX_ENUM(enum_baseclass, val);
}
case MONO_TYPE_I4: {
- int32_t val = p_var->operator signed int();
+ int32_t val = p_var.operator signed int();
return BOX_ENUM(enum_baseclass, val);
}
case MONO_TYPE_I8: {
- int64_t val = p_var->operator int64_t();
+ int64_t val = p_var.operator int64_t();
return BOX_ENUM(enum_baseclass, val);
}
case MONO_TYPE_U1: {
- uint8_t val = p_var->operator unsigned char();
+ uint8_t val = p_var.operator unsigned char();
return BOX_ENUM(enum_baseclass, val);
}
case MONO_TYPE_U2: {
- uint16_t val = p_var->operator unsigned short();
+ uint16_t val = p_var.operator unsigned short();
return BOX_ENUM(enum_baseclass, val);
}
case MONO_TYPE_U4: {
- uint32_t val = p_var->operator unsigned int();
+ uint32_t val = p_var.operator unsigned int();
return BOX_ENUM(enum_baseclass, val);
}
case MONO_TYPE_U8: {
- uint64_t val = p_var->operator uint64_t();
+ uint64_t val = p_var.operator uint64_t();
return BOX_ENUM(enum_baseclass, val);
}
default: {
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to a managed enum value of unmarshallable base type.");
+ ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to enum value of unsupported base type: '" +
+ GDMonoClass::get_full_name(enum_baseclass) + "'.");
}
}
}
- } break;
-
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_SZARRAY: {
- MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type());
-
- if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) {
- return (MonoObject *)Array_to_mono_array(p_var->operator Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) {
- return (MonoObject *)PackedByteArray_to_mono_array(p_var->operator PackedByteArray());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) {
- return (MonoObject *)PackedInt32Array_to_mono_array(p_var->operator PackedInt32Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) {
- return (MonoObject *)PackedInt64Array_to_mono_array(p_var->operator PackedInt64Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(float)) {
- return (MonoObject *)PackedFloat32Array_to_mono_array(p_var->operator PackedFloat32Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(double)) {
- return (MonoObject *)PackedFloat64Array_to_mono_array(p_var->operator PackedFloat64Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(String)) {
- return (MonoObject *)PackedStringArray_to_mono_array(p_var->operator PackedStringArray());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) {
- return (MonoObject *)PackedVector2Array_to_mono_array(p_var->operator PackedVector2Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) {
- return (MonoObject *)PackedVector3Array_to_mono_array(p_var->operator PackedVector3Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Color)) {
- return (MonoObject *)PackedColorArray_to_mono_array(p_var->operator PackedColorArray());
- }
-
- GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass);
- if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) {
- return (MonoObject *)Array_to_mono_array(p_var->operator Array(), array_type_class);
- }
-
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to a managed array of unmarshallable element type.");
- } break;
-
- case MONO_TYPE_CLASS: {
- GDMonoClass *type_class = p_type.type_class;
-
- // GodotObject
- if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
- return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *());
- }
-
- if (CACHED_CLASS(StringName) == type_class) {
- return GDMonoUtils::create_managed_from(p_var->operator StringName());
- }
-
- if (CACHED_CLASS(NodePath) == type_class) {
- return GDMonoUtils::create_managed_from(p_var->operator NodePath());
- }
-
- if (CACHED_CLASS(RID) == type_class) {
- return GDMonoUtils::create_managed_from(p_var->operator ::RID());
- }
-
- // Godot.Collections.Dictionary or IDictionary
- if (CACHED_CLASS(Dictionary) == type_class || CACHED_CLASS(System_Collections_IDictionary) == type_class) {
- return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
- }
-
- // Godot.Collections.Array or ICollection or IEnumerable
- if (CACHED_CLASS(Array) == type_class ||
- CACHED_CLASS(System_Collections_ICollection) == type_class ||
- CACHED_CLASS(System_Collections_IEnumerable) == type_class) {
- return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
- }
- } break;
- case MONO_TYPE_OBJECT: {
- // Variant
- switch (p_var->get_type()) {
- case Variant::BOOL: {
- MonoBoolean val = p_var->operator bool();
- return BOX_BOOLEAN(val);
- }
- case Variant::INT: {
- int64_t val = p_var->operator int64_t();
- return BOX_INT64(val);
- }
- case Variant::FLOAT: {
-#ifdef REAL_T_IS_DOUBLE
- double val = p_var->operator double();
- return BOX_DOUBLE(val);
-#else
- float val = p_var->operator float();
- return BOX_FLOAT(val);
-#endif
- }
- case Variant::STRING:
- return (MonoObject *)mono_string_from_godot(p_var->operator String());
- case Variant::VECTOR2: {
- GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_var->operator ::Vector2());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2), &from);
- }
- case Variant::VECTOR2I: {
- GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_var->operator ::Vector2i());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2i), &from);
- }
- case Variant::RECT2: {
- GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_var->operator ::Rect2());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2), &from);
- }
- case Variant::RECT2I: {
- GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_var->operator ::Rect2i());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2i), &from);
- }
- case Variant::VECTOR3: {
- GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_var->operator ::Vector3());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3), &from);
- }
- case Variant::VECTOR3I: {
- GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_var->operator ::Vector3i());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3i), &from);
- }
- case Variant::TRANSFORM2D: {
- GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_var->operator ::Transform2D());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform2D), &from);
- }
- case Variant::PLANE: {
- GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_var->operator ::Plane());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from);
- }
- case Variant::QUAT: {
- GDMonoMarshal::M_Quat from = MARSHALLED_OUT(Quat, p_var->operator ::Quat());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Quat), &from);
- }
- case Variant::AABB: {
- GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_var->operator ::AABB());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(AABB), &from);
- }
- case Variant::BASIS: {
- GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_var->operator ::Basis());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Basis), &from);
- }
- case Variant::TRANSFORM: {
- GDMonoMarshal::M_Transform from = MARSHALLED_OUT(Transform, p_var->operator ::Transform());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform), &from);
- }
- case Variant::COLOR: {
- GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var->operator ::Color());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from);
- }
- case Variant::STRING_NAME:
- return GDMonoUtils::create_managed_from(p_var->operator StringName());
- case Variant::NODE_PATH:
- return GDMonoUtils::create_managed_from(p_var->operator NodePath());
- case Variant::RID:
- return GDMonoUtils::create_managed_from(p_var->operator ::RID());
- case Variant::OBJECT:
- return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *());
- case Variant::CALLABLE: {
- GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var->operator Callable());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from);
- }
- case Variant::SIGNAL: {
- GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var->operator Signal());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from);
- }
- case Variant::DICTIONARY:
- return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
- case Variant::ARRAY:
- return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
- case Variant::PACKED_BYTE_ARRAY:
- return (MonoObject *)PackedByteArray_to_mono_array(p_var->operator PackedByteArray());
- case Variant::PACKED_INT32_ARRAY:
- return (MonoObject *)PackedInt32Array_to_mono_array(p_var->operator PackedInt32Array());
- case Variant::PACKED_INT64_ARRAY:
- return (MonoObject *)PackedInt64Array_to_mono_array(p_var->operator PackedInt64Array());
- case Variant::PACKED_FLOAT32_ARRAY:
- return (MonoObject *)PackedFloat32Array_to_mono_array(p_var->operator PackedFloat32Array());
- case Variant::PACKED_FLOAT64_ARRAY:
- return (MonoObject *)PackedFloat64Array_to_mono_array(p_var->operator PackedFloat64Array());
- case Variant::PACKED_STRING_ARRAY:
- return (MonoObject *)PackedStringArray_to_mono_array(p_var->operator PackedStringArray());
- case Variant::PACKED_VECTOR2_ARRAY:
- return (MonoObject *)PackedVector2Array_to_mono_array(p_var->operator PackedVector2Array());
- case Variant::PACKED_VECTOR3_ARRAY:
- return (MonoObject *)PackedVector3Array_to_mono_array(p_var->operator PackedVector3Array());
- case Variant::PACKED_COLOR_ARRAY:
- return (MonoObject *)PackedColorArray_to_mono_array(p_var->operator PackedColorArray());
- default:
- return nullptr;
- }
- break;
- case MONO_TYPE_GENERICINST: {
- MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
-
- // Godot.Collections.Dictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
- return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), p_type.type_class);
- }
-
- // Godot.Collections.Array<T>
- if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
- return GDMonoUtils::create_managed_from(p_var->operator Array(), p_type.type_class);
- }
-
- // System.Collections.Generic.Dictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) {
- MonoReflectionType *key_reftype = nullptr;
- MonoReflectionType *value_reftype = nullptr;
- GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype);
- return Dictionary_to_system_generic_dict(p_var->operator Dictionary(), p_type.type_class, key_reftype, value_reftype);
- }
-
- // System.Collections.Generic.List<T>
- if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) {
- MonoReflectionType *elem_reftype = nullptr;
- GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
- return Array_to_system_generic_list(p_var->operator Array(), p_type.type_class, elem_reftype);
- }
-
- // IDictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_generic_idictionary(reftype)) {
- MonoReflectionType *key_reftype;
- MonoReflectionType *value_reftype;
- GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype);
- GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype);
-
- return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), godot_dict_class);
- }
- // ICollection<T> or IEnumerable<T>
- if (GDMonoUtils::Marshal::type_is_generic_icollection(reftype) || GDMonoUtils::Marshal::type_is_generic_ienumerable(reftype)) {
- MonoReflectionType *elem_reftype;
- GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
- GDMonoClass *godot_array_class = GDMonoUtils::Marshal::make_generic_array_type(elem_reftype);
-
- return GDMonoUtils::create_managed_from(p_var->operator Array(), godot_array_class);
- }
- } break;
+ ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported value type: '" +
+ p_type.type_class->get_full_name() + "'.");
} break;
+ case MONO_TYPE_STRING:
+ return (MonoObject *)variant_to_mono_string(p_var);
+ case MONO_TYPE_ARRAY:
+ case MONO_TYPE_SZARRAY:
+ return (MonoObject *)variant_to_mono_array(p_var, p_type.type_class);
+ case MONO_TYPE_CLASS:
+ return variant_to_mono_object_of_class(p_var, p_type.type_class);
+ case MONO_TYPE_GENERICINST:
+ return variant_to_mono_object_of_genericinst(p_var, p_type.type_class);
+ case MONO_TYPE_OBJECT:
+ return variant_to_mono_object(p_var);
}
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to an unmarshallable managed type. Name: '" +
- p_type.type_class->get_name() + "' Encoding: " + itos(p_type.type_encoding) + ".");
+ ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type with encoding: " +
+ itos(p_type.type_encoding) + ".");
}
Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type, bool p_fail_with_err = true) {
ERR_FAIL_COND_V(!p_type.type_class, Variant());
+#ifdef DEBUG_ENABLED
+ CRASH_COND_MSG(p_type.type_encoding == MONO_TYPE_OBJECT, "Type of object should be known.");
+#endif
+
switch (p_type.type_encoding) {
case MONO_TYPE_BOOLEAN:
return (bool)unbox<MonoBoolean>(p_obj);
-
case MONO_TYPE_CHAR:
return unbox<uint16_t>(p_obj);
-
case MONO_TYPE_I1:
return unbox<int8_t>(p_obj);
case MONO_TYPE_I2:
@@ -783,7 +989,6 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
return unbox<int32_t>(p_obj);
case MONO_TYPE_I8:
return unbox<int64_t>(p_obj);
-
case MONO_TYPE_U1:
return unbox<uint8_t>(p_obj);
case MONO_TYPE_U2:
@@ -792,19 +997,10 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
return unbox<uint32_t>(p_obj);
case MONO_TYPE_U8:
return unbox<uint64_t>(p_obj);
-
case MONO_TYPE_R4:
return unbox<float>(p_obj);
case MONO_TYPE_R8:
return unbox<double>(p_obj);
-
- case MONO_TYPE_STRING: {
- if (p_obj == nullptr) {
- return Variant(); // NIL
- }
- return mono_string_to_godot_not_null((MonoString *)p_obj);
- } break;
-
case MONO_TYPE_VALUETYPE: {
GDMonoClass *vtclass = p_type.type_class;
@@ -872,7 +1068,12 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
return unbox<int32_t>(p_obj);
}
} break;
-
+ case MONO_TYPE_STRING: {
+ if (p_obj == nullptr) {
+ return Variant(); // NIL
+ }
+ return mono_string_to_godot_not_null((MonoString *)p_obj);
+ } break;
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type());
@@ -928,7 +1129,6 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
return Variant();
}
} break;
-
case MONO_TYPE_CLASS: {
GDMonoClass *type_class = p_type.type_class;
@@ -973,7 +1173,6 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
return ptr ? Variant(*ptr) : Variant();
}
} break;
-
case MONO_TYPE_GENERICINST: {
MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
@@ -1052,7 +1251,7 @@ String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc) {
ManagedType type = ManagedType::from_class(mono_object_get_class(p_obj));
Variant var = GDMonoMarshal::mono_object_to_variant_no_err(p_obj, type);
- if (var.get_type() == Variant::NIL && p_obj != nullptr) {
+ if (var.get_type() == Variant::NIL) { // `&& p_obj != nullptr` but omitted because always true
// Cannot convert MonoObject* to Variant; fallback to 'ToString()'.
MonoException *exc = nullptr;
MonoString *mono_str = GDMonoUtils::object_to_string(p_obj, &exc);
@@ -1115,9 +1314,10 @@ Dictionary system_generic_dict_to_Dictionary(MonoObject *p_obj, [[maybe_unused]]
}
MonoObject *Array_to_system_generic_list(const Array &p_array, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype) {
- GDMonoClass *elem_class = ManagedType::from_reftype(p_elem_reftype).type_class;
+ MonoType *elem_type = mono_reflection_type_get_type(p_elem_reftype);
+ MonoClass *elem_class = mono_class_from_mono_type(elem_type);
- String ctor_desc = ":.ctor(System.Collections.Generic.IEnumerable`1<" + elem_class->get_type_desc() + ">)";
+ String ctor_desc = ":.ctor(System.Collections.Generic.IEnumerable`1<" + GDMonoUtils::get_type_desc(elem_type) + ">)";
GDMonoMethod *ctor = p_class->get_method_with_desc(ctor_desc, true);
CRASH_COND(ctor == nullptr);
@@ -1156,9 +1356,9 @@ MonoArray *Array_to_mono_array(const Array &p_array) {
return ret;
}
-MonoArray *Array_to_mono_array(const Array &p_array, GDMonoClass *p_array_type_class) {
+MonoArray *Array_to_mono_array(const Array &p_array, MonoClass *p_array_type_class) {
int length = p_array.size();
- MonoArray *ret = mono_array_new(mono_domain_get(), p_array_type_class->get_mono_ptr(), length);
+ MonoArray *ret = mono_array_new(mono_domain_get(), p_array_type_class, length);
for (int i = 0; i < length; i++) {
MonoObject *boxed = variant_to_mono_object(p_array[i]);
diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h
index d1d5f1f202..6d8227f8b4 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.h
+++ b/modules/mono/mono_gd/gd_mono_marshal.h
@@ -90,15 +90,40 @@ _FORCE_INLINE_ MonoString *mono_string_from_godot(const String &p_string) {
// Variant
-MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_type);
-MonoObject *variant_to_mono_object(const Variant *p_var);
-
-_FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant &p_var) {
- return variant_to_mono_object(&p_var);
+size_t variant_get_managed_unboxed_size(const ManagedType &p_type);
+void *variant_to_managed_unboxed(const Variant &p_var, const ManagedType &p_type, void *r_buffer, unsigned int &r_offset);
+MonoObject *variant_to_mono_object(const Variant &p_var, const ManagedType &p_type);
+
+MonoObject *variant_to_mono_object(const Variant &p_var);
+MonoArray *variant_to_mono_array(const Variant &p_var, GDMonoClass *p_type_class);
+MonoObject *variant_to_mono_object_of_class(const Variant &p_var, GDMonoClass *p_type_class);
+MonoObject *variant_to_mono_object_of_genericinst(const Variant &p_var, GDMonoClass *p_type_class);
+MonoString *variant_to_mono_string(const Variant &p_var);
+
+// These overloads were added to avoid passing a `const Variant *` to the `const Variant &`
+// parameter. That would result in the `Variant(bool)` copy constructor being called as
+// pointers are implicitly converted to bool. Implicit conversions are f-ing evil.
+
+_FORCE_INLINE_ void *variant_to_managed_unboxed(const Variant *p_var, const ManagedType &p_type, void *r_buffer, unsigned int &r_offset) {
+ return variant_to_managed_unboxed(*p_var, p_type, r_buffer, r_offset);
}
-
-_FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant &p_var, const ManagedType &p_type) {
- return variant_to_mono_object(&p_var, p_type);
+_FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_type) {
+ return variant_to_mono_object(*p_var, p_type);
+}
+_FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant *p_var) {
+ return variant_to_mono_object(*p_var);
+}
+_FORCE_INLINE_ MonoArray *variant_to_mono_array(const Variant *p_var, GDMonoClass *p_type_class) {
+ return variant_to_mono_array(*p_var, p_type_class);
+}
+_FORCE_INLINE_ MonoObject *variant_to_mono_object_of_class(const Variant *p_var, GDMonoClass *p_type_class) {
+ return variant_to_mono_object_of_class(*p_var, p_type_class);
+}
+_FORCE_INLINE_ MonoObject *variant_to_mono_object_of_genericinst(const Variant *p_var, GDMonoClass *p_type_class) {
+ return variant_to_mono_object_of_genericinst(*p_var, p_type_class);
+}
+_FORCE_INLINE_ MonoString *variant_to_mono_string(const Variant *p_var) {
+ return variant_to_mono_string(*p_var);
}
Variant mono_object_to_variant(MonoObject *p_obj);
@@ -120,7 +145,7 @@ Array system_generic_list_to_Array(MonoObject *p_obj, GDMonoClass *p_class, Mono
// Array
MonoArray *Array_to_mono_array(const Array &p_array);
-MonoArray *Array_to_mono_array(const Array &p_array, GDMonoClass *p_array_type_class);
+MonoArray *Array_to_mono_array(const Array &p_array, MonoClass *p_array_type_class);
Array mono_array_to_Array(MonoArray *p_array);
// PackedInt32Array
diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp
index 04f3b25a70..1d87726234 100644
--- a/modules/mono/mono_gd/gd_mono_method.cpp
+++ b/modules/mono/mono_gd/gd_mono_method.cpp
@@ -75,6 +75,10 @@ void GDMonoMethod::_update_signature(MonoMethodSignature *p_method_sig) {
// clear the cache
method_info_fetched = false;
method_info = MethodInfo();
+
+ for (int i = 0; i < params_count; i++) {
+ params_buffer_size += GDMonoMarshal::variant_get_managed_unboxed_size(param_types[i]);
+ }
}
GDMonoClass *GDMonoMethod::get_enclosing_class() const {
@@ -107,14 +111,15 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params,
MonoObject *ret;
if (params_count > 0) {
- MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), params_count);
+ void **params = (void **)alloca(params_count * sizeof(void *));
+ uint8_t *buffer = (uint8_t *)alloca(params_buffer_size);
+ unsigned int offset = 0;
for (int i = 0; i < params_count; i++) {
- MonoObject *boxed_param = GDMonoMarshal::variant_to_mono_object(p_params[i], param_types[i]);
- mono_array_setref(params, i, boxed_param);
+ params[i] = GDMonoMarshal::variant_to_managed_unboxed(p_params[i], param_types[i], buffer + offset, offset);
}
- ret = GDMonoUtils::runtime_invoke_array(mono_method, p_object, params, &exc);
+ ret = GDMonoUtils::runtime_invoke(mono_method, p_object, params, &exc);
} else {
ret = GDMonoUtils::runtime_invoke(mono_method, p_object, nullptr, &exc);
}
@@ -279,16 +284,8 @@ const MethodInfo &GDMonoMethod::get_method_info() {
return method_info;
}
-GDMonoMethod::GDMonoMethod(StringName p_name, MonoMethod *p_method) {
- name = p_name;
-
- mono_method = p_method;
-
- method_info_fetched = false;
-
- attrs_fetched = false;
- attributes = nullptr;
-
+GDMonoMethod::GDMonoMethod(StringName p_name, MonoMethod *p_method) :
+ name(p_name), mono_method(p_method) {
_update_signature();
}
diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h
index f78f57dca0..115bd998fa 100644
--- a/modules/mono/mono_gd/gd_mono_method.h
+++ b/modules/mono/mono_gd/gd_mono_method.h
@@ -38,15 +38,16 @@
class GDMonoMethod : public IMonoClassMember {
StringName name;
- int params_count;
+ uint16_t params_count;
+ unsigned int params_buffer_size = 0;
ManagedType return_type;
Vector<ManagedType> param_types;
- bool method_info_fetched;
+ bool method_info_fetched = false;
MethodInfo method_info;
- bool attrs_fetched;
- MonoCustomAttrInfo *attributes;
+ bool attrs_fetched = false;
+ MonoCustomAttrInfo *attributes = nullptr;
void _update_signature();
void _update_signature(MonoMethodSignature *p_method_sig);
@@ -72,7 +73,7 @@ public:
_FORCE_INLINE_ MonoMethod *get_mono_ptr() const { return mono_method; }
- _FORCE_INLINE_ int get_parameters_count() const { return params_count; }
+ _FORCE_INLINE_ uint16_t get_parameters_count() const { return params_count; }
_FORCE_INLINE_ ManagedType get_return_type() const { return return_type; }
MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc = nullptr) const;
diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp
index bc3be97102..1027c08a4a 100644
--- a/modules/mono/mono_gd/gd_mono_property.cpp
+++ b/modules/mono/mono_gd/gd_mono_property.cpp
@@ -149,10 +149,9 @@ bool GDMonoProperty::has_setter() {
void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc) {
MonoMethod *prop_method = mono_property_get_set_method(mono_property);
- MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1);
- mono_array_setref(params, 0, p_value);
+ void *params[1] = { p_value };
MonoException *exc = nullptr;
- GDMonoUtils::runtime_invoke_array(prop_method, p_object, params, &exc);
+ GDMonoUtils::runtime_invoke(prop_method, p_object, params, &exc);
if (exc) {
if (r_exc) {
*r_exc = exc;
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index 97fc4c57f9..05fd57255c 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -480,6 +480,7 @@ void set_pending_exception(MonoException *p_exc) {
#else
if (get_runtime_invoke_count() == 0) {
debug_unhandled_exception(p_exc);
+ return;
}
if (!mono_runtime_set_pending_exception(p_exc, false)) {
@@ -498,13 +499,6 @@ MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, M
return ret;
}
-MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **r_exc) {
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- MonoObject *ret = mono_runtime_invoke_array(p_method, p_obj, p_params, (MonoObject **)r_exc);
- GD_MONO_END_RUNTIME_INVOKE;
- return ret;
-}
-
MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
MonoString *ret = mono_object_to_string(p_obj, (MonoObject **)r_exc);
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index 71c131f77c..5370b1c6fc 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -36,6 +36,9 @@
#include "../mono_gc_handle.h"
#include "../utils/macros.h"
#include "gd_mono_header.h"
+#ifdef JAVASCRIPT_ENABLED
+#include "gd_mono_wasm_m2n.h"
+#endif
#include "core/object/class_db.h"
#include "core/object/reference.h"
@@ -135,7 +138,6 @@ _FORCE_INLINE_ int &get_runtime_invoke_count_ref() {
}
MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc);
-MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **r_exc);
MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc);
@@ -155,6 +157,22 @@ private:
};
StringName get_native_godot_class_name(GDMonoClass *p_class);
+
+template <typename... P>
+void add_internal_call(const char *p_name, void (*p_func)(P...)) {
+#ifdef JAVASCRIPT_ENABLED
+ GDMonoWasmM2n::ICallTrampolines<P...>::add();
+#endif
+ mono_add_internal_call(p_name, (void *)p_func);
+}
+
+template <typename R, typename... P>
+void add_internal_call(const char *p_name, R (*p_func)(P...)) {
+#ifdef JAVASCRIPT_ENABLED
+ GDMonoWasmM2n::ICallTrampolinesR<R, P...>::add();
+#endif
+ mono_add_internal_call(p_name, (void *)p_func);
+}
} // namespace GDMonoUtils
#define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoUtils::get_native_godot_class_name(m_class))
diff --git a/modules/mono/mono_gd/gd_mono_wasm_m2n.cpp b/modules/mono/mono_gd/gd_mono_wasm_m2n.cpp
new file mode 100644
index 0000000000..f4c964c6eb
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_wasm_m2n.cpp
@@ -0,0 +1,79 @@
+/*************************************************************************/
+/* gd_mono_wasm_m2n.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "gd_mono_wasm_m2n.h"
+
+#ifdef JAVASCRIPT_ENABLED
+
+#include "core/templates/oa_hash_map.h"
+
+typedef mono_bool (*GodotMonoM2nIcallTrampolineDispatch)(const char *cookie, void *target_func, Mono_InterpMethodArguments *margs);
+
+// This extern function is implemented in our patched version of Mono
+MONO_API void godot_mono_register_m2n_icall_trampoline_dispatch_hook(GodotMonoM2nIcallTrampolineDispatch hook);
+
+namespace GDMonoWasmM2n {
+
+struct HashMapCookieComparator {
+ static bool compare(const char *p_lhs, const char *p_rhs) {
+ return strcmp(p_lhs, p_rhs) == 0;
+ }
+};
+
+// The default hasher supports 'const char *' C Strings, but we need a custom comparator
+OAHashMap<const char *, TrampolineFunc, HashMapHasherDefault, HashMapCookieComparator> trampolines;
+
+void set_trampoline(const char *cookies, GDMonoWasmM2n::TrampolineFunc trampoline_func) {
+ trampolines.set(cookies, trampoline_func);
+}
+
+mono_bool trampoline_dispatch_hook(const char *cookie, void *target_func, Mono_InterpMethodArguments *margs) {
+ TrampolineFunc *trampoline_func = trampolines.lookup_ptr(cookie);
+
+ if (!trampoline_func) {
+ return false;
+ }
+
+ (*trampoline_func)(target_func, margs);
+ return true;
+}
+
+bool initialized = false;
+
+void lazy_initialize() {
+ // Doesn't need to be thread safe
+ if (!initialized) {
+ initialized = true;
+ godot_mono_register_m2n_icall_trampoline_dispatch_hook(&trampoline_dispatch_hook);
+ }
+}
+} // namespace GDMonoWasmM2n
+
+#endif
diff --git a/modules/mono/mono_gd/gd_mono_wasm_m2n.h b/modules/mono/mono_gd/gd_mono_wasm_m2n.h
new file mode 100644
index 0000000000..68a14f15f4
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_wasm_m2n.h
@@ -0,0 +1,263 @@
+/*************************************************************************/
+/* gd_mono_wasm_m2n.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GD_MONO_WASM_M2N_H
+#define GD_MONO_WASM_M2N_H
+
+#ifdef JAVASCRIPT_ENABLED
+
+#include "core/string/ustring.h"
+#include "core/typedefs.h"
+
+#include <mono/metadata/loader.h>
+#include <mono/utils/mono-publib.h>
+#include <stdexcept>
+#include <type_traits>
+
+extern "C" {
+
+struct Mono_InterpMethodArguments {
+ size_t ilen;
+ void **iargs;
+ size_t flen;
+ double *fargs;
+ void **retval;
+ size_t is_float_ret;
+ //#ifdef TARGET_WASM
+ void *sig;
+ //#endif
+};
+} // extern "C"
+
+namespace GDMonoWasmM2n {
+
+template <typename T, size_t Size>
+struct array {
+ T elems[Size];
+};
+
+template <typename T>
+constexpr char get_m2n_cookie_impl() {
+#define M2N_REG_COOKIE(m_type, m_cookie) \
+ if constexpr (std::is_same_v<m_type, T>) { \
+ return m_cookie; \
+ }
+
+ M2N_REG_COOKIE(MonoBoolean, 'I');
+ M2N_REG_COOKIE(int8_t, 'I');
+ M2N_REG_COOKIE(uint8_t, 'I');
+ M2N_REG_COOKIE(int16_t, 'I');
+ M2N_REG_COOKIE(uint16_t, 'I');
+ M2N_REG_COOKIE(int32_t, 'I');
+ M2N_REG_COOKIE(uint32_t, 'I');
+ M2N_REG_COOKIE(int64_t, 'L');
+ M2N_REG_COOKIE(uint64_t, 'L');
+ M2N_REG_COOKIE(float, 'F');
+ M2N_REG_COOKIE(double, 'D');
+
+ if constexpr (std::is_pointer_v<T>) {
+ if constexpr (sizeof(void *) == 4) {
+ return 'I';
+ } else {
+ return 'L';
+ }
+ }
+
+ if constexpr (std::is_void_v<T>) {
+ return 'V';
+ }
+
+ return 'X';
+
+#undef M2N_REG_COOKIE
+}
+
+template <typename T>
+constexpr char get_m2n_cookie() {
+ constexpr char cookie = get_m2n_cookie_impl<T>();
+ static_assert(cookie != 'X', "Type not supported in internal call signature.");
+ return cookie;
+}
+
+template <typename... T>
+constexpr array<const char, sizeof...(T) + 2> get_m2n_cookies() {
+ return array<const char, sizeof...(T) + 2>{ 'V', get_m2n_cookie<T>()..., '\0' };
+}
+
+template <typename R, typename... T>
+constexpr array<const char, sizeof...(T) + 2> get_m2n_cookies_r() {
+ return array<const char, sizeof...(T) + 2>{ get_m2n_cookie<R>(), get_m2n_cookie<T>()..., '\0' };
+}
+
+template <typename T>
+constexpr size_t calc_m2n_index(size_t &r_int_idx, size_t &r_float_idx) {
+ constexpr char cookie = get_m2n_cookie<T>();
+
+ static_assert(cookie == 'I' || cookie == 'L' || cookie == 'F' || cookie == 'D');
+
+ if constexpr (cookie == 'I' || cookie == 'L') {
+ size_t ret = r_int_idx;
+ r_int_idx += cookie == 'I' ? 1 : 2;
+ return ret;
+ } else {
+ size_t ret = r_float_idx;
+ r_float_idx += cookie == 'F' ? 1 : 2;
+ return ret;
+ }
+}
+
+template <typename... P>
+constexpr array<size_t, sizeof...(P)> get_indices_for_type() {
+ size_t int_idx = 0;
+ size_t float_idx = 0;
+ return array<size_t, sizeof...(P)>{ calc_m2n_index<P>(int_idx, float_idx)... };
+}
+
+constexpr size_t fidx(size_t p_x) {
+ if constexpr (sizeof(void *) == 4) {
+ return p_x * 2;
+ } else {
+ return p_x;
+ }
+}
+
+template <typename T>
+T m2n_arg_cast(Mono_InterpMethodArguments *p_margs, size_t p_idx) {
+ constexpr char cookie = get_m2n_cookie<T>();
+
+ static_assert(cookie == 'I' || cookie == 'L' || cookie == 'F' || cookie == 'D');
+
+ if constexpr (cookie == 'I') {
+ return (T)(size_t)p_margs->iargs[p_idx];
+ } else if constexpr (cookie == 'L') {
+ static_assert(std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t> ||
+ (sizeof(void *) == 8 && std::is_pointer_v<T>),
+ "Invalid type for cookie 'L'.");
+
+ union {
+ T l;
+ struct {
+ int32_t lo;
+ int32_t hi;
+ } pair;
+ } p;
+
+ p.pair.lo = (int32_t)(size_t)p_margs->iargs[p_idx];
+ p.pair.hi = (int32_t)(size_t)p_margs->iargs[p_idx + 1];
+
+ return p.l;
+ } else if constexpr (cookie == 'F') {
+ return *reinterpret_cast<float *>(&p_margs->fargs[fidx(p_idx)]);
+ } else if constexpr (cookie == 'D') {
+ return (T)(size_t)p_margs->fargs[p_idx];
+ }
+}
+
+template <typename... P, size_t... Is>
+void m2n_trampoline_with_idx_seq(void *p_target_func, Mono_InterpMethodArguments *p_margs, IndexSequence<Is...>) {
+ constexpr array<size_t, sizeof...(P)> indices = get_indices_for_type<P...>();
+ typedef void (*Func)(P...);
+ Func func = (Func)p_target_func;
+ func(m2n_arg_cast<P>(p_margs, indices.elems[Is])...);
+}
+
+template <typename R, typename... P, size_t... Is>
+void m2n_trampoline_with_idx_seq_r(void *p_target_func, Mono_InterpMethodArguments *p_margs, IndexSequence<Is...>) {
+ constexpr array<size_t, sizeof...(P)> indices = get_indices_for_type<P...>();
+ typedef R (*Func)(P...);
+ Func func = (Func)p_target_func;
+ R res = func(m2n_arg_cast<P>(p_margs, indices.elems[Is])...);
+ *reinterpret_cast<R *>(p_margs->retval) = res;
+}
+
+inline void m2n_trampoline_with_idx_seq_0(void *p_target_func, Mono_InterpMethodArguments *p_margs) {
+ typedef void (*Func)();
+ Func func = (Func)p_target_func;
+ func();
+}
+
+template <typename R>
+void m2n_trampoline_with_idx_seq_r0(void *p_target_func, Mono_InterpMethodArguments *p_margs) {
+ typedef R (*Func)();
+ Func func = (Func)p_target_func;
+ R res = func();
+ *reinterpret_cast<R *>(p_margs->retval) = res;
+}
+
+template <typename... P>
+void m2n_trampoline(void *p_target_func, Mono_InterpMethodArguments *p_margs) {
+ if constexpr (sizeof...(P) == 0) {
+ m2n_trampoline_with_idx_seq_0(p_target_func, p_margs);
+ } else {
+ m2n_trampoline_with_idx_seq<P...>(p_target_func, p_margs, BuildIndexSequence<sizeof...(P)>{});
+ }
+}
+
+template <typename R, typename... P>
+void m2n_trampoline_r(void *p_target_func, Mono_InterpMethodArguments *p_margs) {
+ if constexpr (sizeof...(P) == 0) {
+ m2n_trampoline_with_idx_seq_r0<R>(p_target_func, p_margs);
+ } else {
+ m2n_trampoline_with_idx_seq_r<R, P...>(p_target_func, p_margs, BuildIndexSequence<sizeof...(P)>{});
+ }
+}
+
+typedef void (*TrampolineFunc)(void *p_target_func, Mono_InterpMethodArguments *p_margs);
+
+void set_trampoline(const char *cookies, TrampolineFunc trampoline_func);
+
+void lazy_initialize();
+
+template <typename... P>
+struct ICallTrampolines {
+ static constexpr auto cookies = get_m2n_cookies<P...>();
+
+ static void add() {
+ lazy_initialize();
+ set_trampoline(cookies.elems, &m2n_trampoline<P...>);
+ }
+};
+
+template <typename R, typename... P>
+struct ICallTrampolinesR {
+ static constexpr auto cookies = get_m2n_cookies_r<R, P...>();
+
+ static void add() {
+ lazy_initialize();
+ set_trampoline(cookies.elems, &m2n_trampoline_r<R, P...>);
+ }
+};
+
+void initialize();
+} // namespace GDMonoWasmM2n
+
+#endif
+
+#endif // GD_MONO_WASM_M2N_H
diff --git a/modules/mono/mono_gd/support/android_support.cpp b/modules/mono/mono_gd/support/android_support.cpp
index 18daf859b5..bc2ae03299 100644
--- a/modules/mono/mono_gd/support/android_support.cpp
+++ b/modules/mono/mono_gd/support/android_support.cpp
@@ -355,8 +355,8 @@ MonoArray *_gd_mono_android_cert_store_lookup(MonoString *p_alias) {
}
void register_internal_calls() {
- mono_add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_init_cert_store", (void *)_gd_mono_init_cert_store);
- mono_add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_android_cert_store_lookup", (void *)_gd_mono_android_cert_store_lookup);
+ GDMonoUtils::add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_init_cert_store", _gd_mono_init_cert_store);
+ GDMonoUtils::add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_android_cert_store_lookup", _gd_mono_android_cert_store_lookup);
}
void initialize() {
diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp
index bd67b03c8e..f220abfb4c 100644
--- a/modules/mono/signal_awaiter_utils.cpp
+++ b/modules/mono/signal_awaiter_utils.cpp
@@ -48,18 +48,10 @@ Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signa
}
bool SignalAwaiterCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
+ // Only called if both instances are of type SignalAwaiterCallable. Static cast is safe.
const SignalAwaiterCallable *a = static_cast<const SignalAwaiterCallable *>(p_a);
const SignalAwaiterCallable *b = static_cast<const SignalAwaiterCallable *>(p_b);
-
- if (a->target_id != b->target_id) {
- return false;
- }
-
- if (a->signal != b->signal) {
- return false;
- }
-
- return true;
+ return a->awaiter_handle.handle == b->awaiter_handle.handle;
}
bool SignalAwaiterCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
@@ -109,11 +101,15 @@ void SignalAwaiterCallable::call(const Variant **p_arguments, int p_argcount, Va
"Resumed after await, but class instance is gone.");
#endif
- MonoArray *signal_args = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_argcount);
+ MonoArray *signal_args = nullptr;
- for (int i = 0; i < p_argcount; i++) {
- MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_arguments[i]);
- mono_array_setref(signal_args, i, boxed);
+ if (p_argcount > 0) {
+ signal_args = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_argcount);
+
+ for (int i = 0; i < p_argcount; i++) {
+ MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_arguments[i]);
+ mono_array_setref(signal_args, i, boxed);
+ }
}
MonoObject *awaiter = awaiter_handle.get_target();
diff --git a/modules/ogg/SCsub b/modules/ogg/SCsub
index e768fb4ae8..e415d92498 100644
--- a/modules/ogg/SCsub
+++ b/modules/ogg/SCsub
@@ -9,6 +9,9 @@ Import("env_modules")
env_ogg = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
if env["builtin_libogg"]:
thirdparty_dir = "#thirdparty/libogg/"
thirdparty_sources = [
@@ -21,7 +24,16 @@ if env["builtin_libogg"]:
env_thirdparty = env_ogg.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
+
# Godot source files
-env_ogg.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_ogg.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
+
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/opensimplex/SCsub b/modules/opensimplex/SCsub
index 52d8b145ef..86d77c3dfb 100644
--- a/modules/opensimplex/SCsub
+++ b/modules/opensimplex/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_opensimplex = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
thirdparty_dir = "#thirdparty/misc/"
thirdparty_sources = [
"open-simplex-noise.c",
@@ -16,7 +19,15 @@ env_opensimplex.Prepend(CPPPATH=[thirdparty_dir])
env_thirdparty = env_opensimplex.Clone()
env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.modules_sources += thirdparty_obj
+
+# Godot source files
+
+module_obj = []
+
+env_opensimplex.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
-# Godot's own source files
-env_opensimplex.add_source_files(env.modules_sources, "*.cpp")
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml
index d89828037f..9fe4c9c249 100644
--- a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml
+++ b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml
@@ -24,7 +24,7 @@
<tutorials>
</tutorials>
<methods>
- <method name="get_image">
+ <method name="get_image" qualifiers="const">
<return type="Image">
</return>
<argument index="0" name="width" type="int">
@@ -35,7 +35,7 @@
Generate a noise image with the requested [code]width[/code] and [code]height[/code], based on the current noise parameters.
</description>
</method>
- <method name="get_noise_1d">
+ <method name="get_noise_1d" qualifiers="const">
<return type="float">
</return>
<argument index="0" name="x" type="float">
@@ -45,7 +45,7 @@
[b]Note:[/b] This method actually returns the 2D noise value [code][-1,1][/code] with fixed y-coordinate value 0.0.
</description>
</method>
- <method name="get_noise_2d">
+ <method name="get_noise_2d" qualifiers="const">
<return type="float">
</return>
<argument index="0" name="x" type="float">
@@ -56,7 +56,7 @@
Returns the 2D noise value [code][-1,1][/code] at the given position.
</description>
</method>
- <method name="get_noise_2dv">
+ <method name="get_noise_2dv" qualifiers="const">
<return type="float">
</return>
<argument index="0" name="pos" type="Vector2">
@@ -65,7 +65,7 @@
Returns the 2D noise value [code][-1,1][/code] at the given position.
</description>
</method>
- <method name="get_noise_3d">
+ <method name="get_noise_3d" qualifiers="const">
<return type="float">
</return>
<argument index="0" name="x" type="float">
@@ -78,7 +78,7 @@
Returns the 3D noise value [code][-1,1][/code] at the given position.
</description>
</method>
- <method name="get_noise_3dv">
+ <method name="get_noise_3dv" qualifiers="const">
<return type="float">
</return>
<argument index="0" name="pos" type="Vector3">
@@ -87,7 +87,7 @@
Returns the 3D noise value [code][-1,1][/code] at the given position.
</description>
</method>
- <method name="get_noise_4d">
+ <method name="get_noise_4d" qualifiers="const">
<return type="float">
</return>
<argument index="0" name="x" type="float">
@@ -102,7 +102,7 @@
Returns the 4D noise value [code][-1,1][/code] at the given position.
</description>
</method>
- <method name="get_seamless_image">
+ <method name="get_seamless_image" qualifiers="const">
<return type="Image">
</return>
<argument index="0" name="size" type="int">
diff --git a/modules/opensimplex/open_simplex_noise.cpp b/modules/opensimplex/open_simplex_noise.cpp
index b08219d258..aded4d2a07 100644
--- a/modules/opensimplex/open_simplex_noise.cpp
+++ b/modules/opensimplex/open_simplex_noise.cpp
@@ -63,7 +63,7 @@ void OpenSimplexNoise::set_seed(int p_seed) {
emit_changed();
}
-int OpenSimplexNoise::get_seed() {
+int OpenSimplexNoise::get_seed() const {
return seed;
}
@@ -102,7 +102,7 @@ void OpenSimplexNoise::set_lacunarity(float p_lacunarity) {
emit_changed();
}
-Ref<Image> OpenSimplexNoise::get_image(int p_width, int p_height) {
+Ref<Image> OpenSimplexNoise::get_image(int p_width, int p_height) const {
Vector<uint8_t> data;
data.resize(p_width * p_height * 4);
@@ -124,7 +124,7 @@ Ref<Image> OpenSimplexNoise::get_image(int p_width, int p_height) {
return image;
}
-Ref<Image> OpenSimplexNoise::get_seamless_image(int p_size) {
+Ref<Image> OpenSimplexNoise::get_seamless_image(int p_size) const {
Vector<uint8_t> data;
data.resize(p_size * p_size * 4);
@@ -193,11 +193,11 @@ void OpenSimplexNoise::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lacunarity", PROPERTY_HINT_RANGE, "0.1,4.0,0.01"), "set_lacunarity", "get_lacunarity");
}
-float OpenSimplexNoise::get_noise_1d(float x) {
+float OpenSimplexNoise::get_noise_1d(float x) const {
return get_noise_2d(x, 1.0);
}
-float OpenSimplexNoise::get_noise_2d(float x, float y) {
+float OpenSimplexNoise::get_noise_2d(float x, float y) const {
x /= period;
y /= period;
@@ -217,7 +217,7 @@ float OpenSimplexNoise::get_noise_2d(float x, float y) {
return sum / max;
}
-float OpenSimplexNoise::get_noise_3d(float x, float y, float z) {
+float OpenSimplexNoise::get_noise_3d(float x, float y, float z) const {
x /= period;
y /= period;
z /= period;
@@ -239,7 +239,7 @@ float OpenSimplexNoise::get_noise_3d(float x, float y, float z) {
return sum / max;
}
-float OpenSimplexNoise::get_noise_4d(float x, float y, float z, float w) {
+float OpenSimplexNoise::get_noise_4d(float x, float y, float z, float w) const {
x /= period;
y /= period;
z /= period;
diff --git a/modules/opensimplex/open_simplex_noise.h b/modules/opensimplex/open_simplex_noise.h
index 835f8ed35e..d9bf05115d 100644
--- a/modules/opensimplex/open_simplex_noise.h
+++ b/modules/opensimplex/open_simplex_noise.h
@@ -61,7 +61,7 @@ public:
void _init_seeds();
void set_seed(int seed);
- int get_seed();
+ int get_seed() const;
void set_octaves(int p_octaves);
int get_octaves() const { return octaves; }
@@ -75,22 +75,22 @@ public:
void set_lacunarity(float p_lacunarity);
float get_lacunarity() const { return lacunarity; }
- Ref<Image> get_image(int p_width, int p_height);
- Ref<Image> get_seamless_image(int p_size);
+ Ref<Image> get_image(int p_width, int p_height) const;
+ Ref<Image> get_seamless_image(int p_size) const;
- float get_noise_1d(float x);
- float get_noise_2d(float x, float y);
- float get_noise_3d(float x, float y, float z);
- float get_noise_4d(float x, float y, float z, float w);
+ float get_noise_1d(float x) const;
+ float get_noise_2d(float x, float y) const;
+ float get_noise_3d(float x, float y, float z) const;
+ float get_noise_4d(float x, float y, float z, float w) const;
- _FORCE_INLINE_ float _get_octave_noise_2d(int octave, float x, float y) { return open_simplex_noise2(&(contexts[octave]), x, y); }
- _FORCE_INLINE_ float _get_octave_noise_3d(int octave, float x, float y, float z) { return open_simplex_noise3(&(contexts[octave]), x, y, z); }
- _FORCE_INLINE_ float _get_octave_noise_4d(int octave, float x, float y, float z, float w) { return open_simplex_noise4(&(contexts[octave]), x, y, z, w); }
+ _FORCE_INLINE_ float _get_octave_noise_2d(int octave, float x, float y) const { return open_simplex_noise2(&(contexts[octave]), x, y); }
+ _FORCE_INLINE_ float _get_octave_noise_3d(int octave, float x, float y, float z) const { return open_simplex_noise3(&(contexts[octave]), x, y, z); }
+ _FORCE_INLINE_ float _get_octave_noise_4d(int octave, float x, float y, float z, float w) const { return open_simplex_noise4(&(contexts[octave]), x, y, z, w); }
// Convenience
- _FORCE_INLINE_ float get_noise_2dv(Vector2 v) { return get_noise_2d(v.x, v.y); }
- _FORCE_INLINE_ float get_noise_3dv(Vector3 v) { return get_noise_3d(v.x, v.y, v.z); }
+ _FORCE_INLINE_ float get_noise_2dv(const Vector2 &v) const { return get_noise_2d(v.x, v.y); }
+ _FORCE_INLINE_ float get_noise_3dv(const Vector3 &v) const { return get_noise_3d(v.x, v.y, v.z); }
protected:
static void _bind_methods();
diff --git a/modules/opus/SCsub b/modules/opus/SCsub
index 52c61fa708..1437cd86df 100644
--- a/modules/opus/SCsub
+++ b/modules/opus/SCsub
@@ -10,6 +10,10 @@ Import("env_modules")
env_opus = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
+# Thirdparty source files
if env["builtin_opus"]:
thirdparty_dir = "#thirdparty/opus/"
@@ -233,7 +237,16 @@ if env["builtin_opus"]:
env_thirdparty = env_opus.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
+
+
+# Godot source files
+
+module_obj = []
+
+env_opus.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
-# Module files
-env_opus.add_source_files(env.modules_sources, "register_types.cpp")
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/pvr/SCsub b/modules/pvr/SCsub
index e0baf851f1..36052cffed 100644
--- a/modules/pvr/SCsub
+++ b/modules/pvr/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_pvr = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
# Not unbundled so far since not widespread as shared library
thirdparty_dir = "#thirdparty/pvrtccompressor/"
thirdparty_sources = [
@@ -21,7 +24,15 @@ env_pvr.Prepend(CPPPATH=[thirdparty_dir])
env_thirdparty = env_pvr.Clone()
env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.modules_sources += thirdparty_obj
# Godot source files
-env_pvr.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_pvr.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
+
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/pvr/image_compress_pvrtc.cpp b/modules/pvr/image_compress_pvrtc.cpp
new file mode 100644
index 0000000000..6695a539d0
--- /dev/null
+++ b/modules/pvr/image_compress_pvrtc.cpp
@@ -0,0 +1,84 @@
+/*************************************************************************/
+/* image_compress_pvrtc.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "image_compress_pvrtc.h"
+
+#include "core/io/image.h"
+#include "core/object/reference.h"
+
+#include <PvrTcEncoder.h>
+#include <RgbaBitmap.h>
+
+static void _compress_pvrtc1_4bpp(Image *p_img) {
+ Ref<Image> img = p_img->duplicate();
+
+ bool make_mipmaps = false;
+ if (!img->is_size_po2() || img->get_width() != img->get_height()) {
+ make_mipmaps = img->has_mipmaps();
+ img->resize_to_po2(true);
+ }
+ img->convert(Image::FORMAT_RGBA8);
+ if (!img->has_mipmaps() && make_mipmaps) {
+ img->generate_mipmaps();
+ }
+
+ bool use_alpha = img->detect_alpha();
+
+ Ref<Image> new_img;
+ new_img.instance();
+ new_img->create(img->get_width(), img->get_height(), img->has_mipmaps(), use_alpha ? Image::FORMAT_PVRTC1_4A : Image::FORMAT_PVRTC1_4);
+
+ Vector<uint8_t> data = new_img->get_data();
+ {
+ uint8_t *wr = data.ptrw();
+ const uint8_t *r = img->get_data().ptr();
+
+ for (int i = 0; i <= new_img->get_mipmap_count(); i++) {
+ int ofs, size, w, h;
+ img->get_mipmap_offset_size_and_dimensions(i, ofs, size, w, h);
+ Javelin::RgbaBitmap bm(w, h);
+ void *dst = (void *)bm.GetData();
+ copymem(dst, &r[ofs], size);
+ Javelin::ColorRgba<unsigned char> *dp = bm.GetData();
+ for (int j = 0; j < size / 4; j++) {
+ // Red and blue colors are swapped.
+ SWAP(dp[j].r, dp[j].b);
+ }
+ new_img->get_mipmap_offset_size_and_dimensions(i, ofs, size, w, h);
+ Javelin::PvrTcEncoder::EncodeRgba4Bpp(&wr[ofs], bm);
+ }
+ }
+
+ p_img->create(new_img->get_width(), new_img->get_height(), new_img->has_mipmaps(), new_img->get_format(), data);
+}
+
+void _register_pvrtc_compress_func() {
+ Image::_image_compress_pvrtc1_4bpp_func = _compress_pvrtc1_4bpp;
+}
diff --git a/modules/pvr/image_compress_pvrtc.h b/modules/pvr/image_compress_pvrtc.h
new file mode 100644
index 0000000000..fde65f4bbe
--- /dev/null
+++ b/modules/pvr/image_compress_pvrtc.h
@@ -0,0 +1,36 @@
+/*************************************************************************/
+/* image_compress_pvrtc.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef IMAGE_COMPRESS_PVRTC_H
+#define IMAGE_COMPRESS_PVRTC_H
+
+void _register_pvrtc_compress_func();
+
+#endif // IMAGE_COMPRESS_PVRTC_H
diff --git a/modules/pvr/register_types.cpp b/modules/pvr/register_types.cpp
index 1eb697bba3..9bfc334d76 100644
--- a/modules/pvr/register_types.cpp
+++ b/modules/pvr/register_types.cpp
@@ -30,6 +30,7 @@
#include "register_types.h"
+#include "image_compress_pvrtc.h"
#include "texture_loader_pvr.h"
static Ref<ResourceFormatPVR> resource_loader_pvr;
@@ -37,6 +38,8 @@ static Ref<ResourceFormatPVR> resource_loader_pvr;
void register_pvr_types() {
resource_loader_pvr.instance();
ResourceLoader::add_resource_format_loader(resource_loader_pvr);
+
+ _register_pvrtc_compress_func();
}
void unregister_pvr_types() {
diff --git a/modules/pvr/texture_loader_pvr.cpp b/modules/pvr/texture_loader_pvr.cpp
index 0923714387..c9cbb1935a 100644
--- a/modules/pvr/texture_loader_pvr.cpp
+++ b/modules/pvr/texture_loader_pvr.cpp
@@ -29,11 +29,8 @@
/*************************************************************************/
#include "texture_loader_pvr.h"
-#include "PvrTcEncoder.h"
-#include "RgbaBitmap.h"
+
#include "core/os/file_access.h"
-#include <string.h>
-#include <new>
static void _pvrtc_decompress(Image *p_img);
@@ -111,11 +108,11 @@ RES ResourceFormatPVR::load(const String &p_path, const String &p_original_path,
switch (flags & 0xFF) {
case 0x18:
case 0xC:
- format = (flags & PVR_HAS_ALPHA) ? Image::FORMAT_PVRTC2A : Image::FORMAT_PVRTC2;
+ format = (flags & PVR_HAS_ALPHA) ? Image::FORMAT_PVRTC1_2A : Image::FORMAT_PVRTC1_2;
break;
case 0x19:
case 0xD:
- format = (flags & PVR_HAS_ALPHA) ? Image::FORMAT_PVRTC4A : Image::FORMAT_PVRTC4;
+ format = (flags & PVR_HAS_ALPHA) ? Image::FORMAT_PVRTC1_4A : Image::FORMAT_PVRTC1_4;
break;
case 0x16:
format = Image::FORMAT_L8;
@@ -183,53 +180,8 @@ String ResourceFormatPVR::get_resource_type(const String &p_path) const {
return "";
}
-static void _compress_pvrtc4(Image *p_img) {
- Ref<Image> img = p_img->duplicate();
-
- bool make_mipmaps = false;
- if (!img->is_size_po2() || img->get_width() != img->get_height()) {
- make_mipmaps = img->has_mipmaps();
- img->resize_to_po2(true);
- }
- img->convert(Image::FORMAT_RGBA8);
- if (!img->has_mipmaps() && make_mipmaps) {
- img->generate_mipmaps();
- }
-
- bool use_alpha = img->detect_alpha();
-
- Ref<Image> new_img;
- new_img.instance();
- new_img->create(img->get_width(), img->get_height(), img->has_mipmaps(), use_alpha ? Image::FORMAT_PVRTC4A : Image::FORMAT_PVRTC4);
-
- Vector<uint8_t> data = new_img->get_data();
- {
- uint8_t *wr = data.ptrw();
- const uint8_t *r = img->get_data().ptr();
-
- for (int i = 0; i <= new_img->get_mipmap_count(); i++) {
- int ofs, size, w, h;
- img->get_mipmap_offset_size_and_dimensions(i, ofs, size, w, h);
- Javelin::RgbaBitmap bm(w, h);
- void *dst = (void *)bm.GetData();
- copymem(dst, &r[ofs], size);
- Javelin::ColorRgba<unsigned char> *dp = bm.GetData();
- for (int j = 0; j < size / 4; j++) {
- /* red and blue colors are swapped. */
- SWAP(dp[j].r, dp[j].b);
- }
- new_img->get_mipmap_offset_size_and_dimensions(i, ofs, size, w, h);
- Javelin::PvrTcEncoder::EncodeRgba4Bpp(&wr[ofs], bm);
- }
- }
-
- p_img->create(new_img->get_width(), new_img->get_height(), new_img->has_mipmaps(), new_img->get_format(), data);
-}
-
ResourceFormatPVR::ResourceFormatPVR() {
Image::_image_decompress_pvrtc = _pvrtc_decompress;
- Image::_image_compress_pvrtc4_func = _compress_pvrtc4;
- Image::_image_compress_pvrtc2_func = _compress_pvrtc4;
}
/////////////////////////////////////////////////////////
@@ -635,9 +587,9 @@ static void decompress_pvrtc(PVRTCBlock *p_comp_img, const int p_2bit, const int
}
static void _pvrtc_decompress(Image *p_img) {
- ERR_FAIL_COND(p_img->get_format() != Image::FORMAT_PVRTC2 && p_img->get_format() != Image::FORMAT_PVRTC2A && p_img->get_format() != Image::FORMAT_PVRTC4 && p_img->get_format() != Image::FORMAT_PVRTC4A);
+ ERR_FAIL_COND(p_img->get_format() != Image::FORMAT_PVRTC1_2 && p_img->get_format() != Image::FORMAT_PVRTC1_2A && p_img->get_format() != Image::FORMAT_PVRTC1_4 && p_img->get_format() != Image::FORMAT_PVRTC1_4A);
- bool _2bit = (p_img->get_format() == Image::FORMAT_PVRTC2 || p_img->get_format() == Image::FORMAT_PVRTC2A);
+ bool _2bit = (p_img->get_format() == Image::FORMAT_PVRTC1_2 || p_img->get_format() == Image::FORMAT_PVRTC1_2A);
Vector<uint8_t> data = p_img->get_data();
const uint8_t *r = data.ptr();
diff --git a/modules/regex/SCsub b/modules/regex/SCsub
index 2afacc1d9c..deb9db7591 100644
--- a/modules/regex/SCsub
+++ b/modules/regex/SCsub
@@ -5,6 +5,10 @@ Import("env_modules")
env_regex = env_modules.Clone()
+# Thirdparty source files
+
+thirdparty_obj = []
+
if env["builtin_pcre2"]:
thirdparty_dir = "#thirdparty/pcre2/src/"
thirdparty_flags = ["PCRE2_STATIC", "HAVE_CONFIG_H", "SUPPORT_UNICODE"]
@@ -52,11 +56,21 @@ if env["builtin_pcre2"]:
env_pcre2 = env_regex.Clone()
env_pcre2.disable_warnings()
env_pcre2["OBJSUFFIX"] = "_" + width + env_pcre2["OBJSUFFIX"]
- env_pcre2.add_source_files(env.modules_sources, thirdparty_sources)
env_pcre2.Append(CPPDEFINES=[("PCRE2_CODE_UNIT_WIDTH", width)])
+ env_pcre2.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
pcre2_builtin("16")
pcre2_builtin("32")
+
+# Godot source files
+
+module_obj = []
+
env_regex.Append(CPPDEFINES=[("PCRE2_CODE_UNIT_WIDTH", 0)])
-env_regex.add_source_files(env.modules_sources, "*.cpp")
+env_regex.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
+
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/squish/SCsub b/modules/squish/SCsub
index b31032403f..c9e29911d8 100644
--- a/modules/squish/SCsub
+++ b/modules/squish/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_squish = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
if env["builtin_squish"]:
thirdparty_dir = "#thirdparty/squish/"
thirdparty_sources = [
@@ -26,7 +29,16 @@ if env["builtin_squish"]:
env_thirdparty = env_squish.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
+
# Godot source files
-env_squish.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_squish.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
+
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/stb_vorbis/SCsub b/modules/stb_vorbis/SCsub
index 266c87c802..8fddb23dc8 100644
--- a/modules/stb_vorbis/SCsub
+++ b/modules/stb_vorbis/SCsub
@@ -6,11 +6,22 @@ Import("env_modules")
env_stb_vorbis = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
thirdparty_sources = ["#thirdparty/misc/stb_vorbis.c"]
env_thirdparty = env_stb_vorbis.Clone()
env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.modules_sources += thirdparty_obj
+
+# Godot source files
+
+module_obj = []
+
+env_stb_vorbis.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
-# Godot's own source files
-env_stb_vorbis.add_source_files(env.modules_sources, "*.cpp")
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/svg/SCsub b/modules/svg/SCsub
index 0bfba34fe5..c7228a8d0b 100644
--- a/modules/svg/SCsub
+++ b/modules/svg/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_svg = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
thirdparty_dir = "#thirdparty/nanosvg/"
thirdparty_sources = [
"nanosvg.cc",
@@ -16,7 +19,15 @@ env_svg.Prepend(CPPPATH=[thirdparty_dir])
env_thirdparty = env_svg.Clone()
env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.modules_sources += thirdparty_obj
+
+# Godot source files
+
+module_obj = []
+
+env_svg.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
-# Godot's own source files
-env_svg.add_source_files(env.modules_sources, "*.cpp")
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub
new file mode 100644
index 0000000000..3589c8546d
--- /dev/null
+++ b/modules/text_server_adv/SCsub
@@ -0,0 +1,501 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_text_server_adv = env_modules.Clone()
+
+
+def make_icu_data(target, source, env):
+ import os
+
+ dst = target[0].srcnode().abspath
+
+ g = open(dst, "w", encoding="utf-8")
+
+ g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
+ g.write("/* (C) 2016 and later: Unicode, Inc. and others. */\n")
+ g.write("/* License & terms of use: http://www.unicode.org/copyright.html */\n")
+ g.write("#ifndef _ICU_DATA_H\n")
+ g.write("#define _ICU_DATA_H\n")
+ g.write('#include "unicode/utypes.h"\n')
+ g.write('#include "unicode/udata.h"\n')
+ g.write('#include "unicode/uversion.h"\n')
+
+ f = open(source[0].srcnode().abspath, "rb")
+ buf = f.read()
+ import os.path
+
+ g.write('extern "C" U_EXPORT const size_t U_ICUDATA_SIZE = ' + str(len(buf)) + ";\n")
+ g.write('extern "C" U_EXPORT const unsigned char U_ICUDATA_ENTRY_POINT[] = {\n')
+ for i in range(len(buf)):
+ g.write("\t" + str(buf[i]) + ",\n")
+
+ g.write("};\n")
+ g.write("#endif")
+
+
+# Thirdparty source files
+
+thirdparty_obj = []
+
+if env["builtin_harfbuzz"]:
+ env_harfbuzz = env_modules.Clone()
+ env_harfbuzz.disable_warnings()
+
+ thirdparty_dir = "#thirdparty/harfbuzz/"
+ thirdparty_sources = [
+ "src/hb-aat-layout.cc",
+ "src/hb-aat-map.cc",
+ "src/hb-blob.cc",
+ "src/hb-buffer-serialize.cc",
+ "src/hb-buffer.cc",
+ "src/hb-common.cc",
+ #'src/hb-coretext.cc',
+ #'src/hb-directwrite.cc',
+ "src/hb-draw.cc",
+ "src/hb-face.cc",
+ "src/hb-fallback-shape.cc",
+ "src/hb-font.cc",
+ "src/hb-ft.cc",
+ #'src/hb-gdi.cc',
+ #'src/hb-glib.cc',
+ #'src/hb-gobject-structs.cc',
+ "src/hb-graphite2.cc",
+ "src/hb-icu.cc",
+ "src/hb-map.cc",
+ "src/hb-number.cc",
+ "src/hb-ot-cff1-table.cc",
+ "src/hb-ot-cff2-table.cc",
+ "src/hb-ot-color.cc",
+ "src/hb-ot-face.cc",
+ "src/hb-ot-font.cc",
+ "src/hb-ot-layout.cc",
+ "src/hb-ot-map.cc",
+ "src/hb-ot-math.cc",
+ "src/hb-ot-meta.cc",
+ "src/hb-ot-metrics.cc",
+ "src/hb-ot-name.cc",
+ "src/hb-ot-shape-complex-arabic.cc",
+ "src/hb-ot-shape-complex-default.cc",
+ "src/hb-ot-shape-complex-hangul.cc",
+ "src/hb-ot-shape-complex-hebrew.cc",
+ "src/hb-ot-shape-complex-indic-table.cc",
+ "src/hb-ot-shape-complex-indic.cc",
+ "src/hb-ot-shape-complex-khmer.cc",
+ "src/hb-ot-shape-complex-myanmar.cc",
+ "src/hb-ot-shape-complex-thai.cc",
+ "src/hb-ot-shape-complex-use-table.cc",
+ "src/hb-ot-shape-complex-use.cc",
+ "src/hb-ot-shape-complex-vowel-constraints.cc",
+ "src/hb-ot-shape-fallback.cc",
+ "src/hb-ot-shape-normalize.cc",
+ "src/hb-ot-shape.cc",
+ "src/hb-ot-tag.cc",
+ "src/hb-ot-var.cc",
+ "src/hb-set.cc",
+ "src/hb-shape-plan.cc",
+ "src/hb-shape.cc",
+ "src/hb-shaper.cc",
+ "src/hb-static.cc",
+ "src/hb-style.cc",
+ "src/hb-subset-cff-common.cc",
+ "src/hb-subset-cff1.cc",
+ "src/hb-subset-cff2.cc",
+ "src/hb-subset-input.cc",
+ "src/hb-subset-plan.cc",
+ "src/hb-subset.cc",
+ "src/hb-ucd.cc",
+ "src/hb-unicode.cc",
+ #'src/hb-uniscribe.cc'
+ ]
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ env_harfbuzz.Append(
+ CPPPATH=[
+ "#thirdparty/harfbuzz/src",
+ "#thirdparty/freetype/include",
+ "#thirdparty/graphite/include",
+ "#thirdparty/icu4c/common/",
+ ]
+ )
+
+ if env["platform"] == "android" or env["platform"] == "linuxbsd" or env["platform"] == "server":
+ env_harfbuzz.Append(CCFLAGS=["-DHAVE_PTHREAD"])
+
+ if env["platform"] == "javascript":
+ if env["threads_enabled"]:
+ env_harfbuzz.Append(CCFLAGS=["-DHAVE_PTHREAD"])
+ else:
+ env_harfbuzz.Append(CCFLAGS=["-DHB_NO_MT"])
+
+ env_harfbuzz.Append(
+ CCFLAGS=[
+ "-DHAVE_ICU_BUILTIN",
+ "-DHAVE_ICU",
+ "-DHAVE_FREETYPE",
+ "-DHAVE_GRAPHITE2",
+ "-DGRAPHITE2_STATIC",
+ ]
+ )
+
+ lib = env_harfbuzz.add_library("harfbuzz_builtin", thirdparty_sources)
+ thirdparty_obj += lib
+
+ # Needs to be appended to arrive after libscene in the linker call,
+ # but we don't want it to arrive *after* system libs, so manual hack
+ # LIBS contains first SCons Library objects ("SCons.Node.FS.File object")
+ # and then plain strings for system library. We insert between the two.
+ inserted = False
+ for idx, linklib in enumerate(env["LIBS"]):
+ if isinstance(linklib, (str, bytes)): # first system lib such as "X11", otherwise SCons lib object
+ env["LIBS"].insert(idx, lib)
+ inserted = True
+ break
+ if not inserted:
+ env.Append(LIBS=[lib])
+
+
+if env["builtin_graphite"]:
+ env_graphite = env_modules.Clone()
+ env_graphite.disable_warnings()
+
+ thirdparty_dir = "#thirdparty/graphite/"
+ thirdparty_sources = [
+ "src/gr_char_info.cpp",
+ "src/gr_face.cpp",
+ "src/gr_features.cpp",
+ "src/gr_font.cpp",
+ "src/gr_logging.cpp",
+ "src/gr_segment.cpp",
+ "src/gr_slot.cpp",
+ "src/CmapCache.cpp",
+ "src/Code.cpp",
+ "src/Collider.cpp",
+ "src/Decompressor.cpp",
+ "src/Face.cpp",
+ #'src/FileFace.cpp',
+ "src/FeatureMap.cpp",
+ "src/Font.cpp",
+ "src/GlyphCache.cpp",
+ "src/GlyphFace.cpp",
+ "src/Intervals.cpp",
+ "src/Justifier.cpp",
+ "src/NameTable.cpp",
+ "src/Pass.cpp",
+ "src/Position.cpp",
+ "src/Segment.cpp",
+ "src/Silf.cpp",
+ "src/Slot.cpp",
+ "src/Sparse.cpp",
+ "src/TtfUtil.cpp",
+ "src/UtfCodec.cpp",
+ "src/FileFace.cpp",
+ "src/json.cpp",
+ ]
+ if not env_graphite.msvc:
+ thirdparty_sources += ["src/direct_machine.cpp"]
+ else:
+ thirdparty_sources += ["src/call_machine.cpp"]
+
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ env_graphite.Append(CPPPATH=["#thirdparty/graphite/src", "#thirdparty/graphite/include"])
+ env_graphite.Append(
+ CCFLAGS=[
+ "-DGRAPHITE2_STATIC",
+ "-DGRAPHITE2_NTRACING",
+ "-DGRAPHITE2_NFILEFACE",
+ ]
+ )
+
+ lib = env_graphite.add_library("graphite_builtin", thirdparty_sources)
+ thirdparty_obj += lib
+
+ # Needs to be appended to arrive after libscene in the linker call,
+ # but we don't want it to arrive *after* system libs, so manual hack
+ # LIBS contains first SCons Library objects ("SCons.Node.FS.File object")
+ # and then plain strings for system library. We insert between the two.
+ inserted = False
+ for idx, linklib in enumerate(env["LIBS"]):
+ if isinstance(linklib, (str, bytes)): # first system lib such as "X11", otherwise SCons lib object
+ env["LIBS"].insert(idx, lib)
+ inserted = True
+ break
+ if not inserted:
+ env.Append(LIBS=[lib])
+
+
+if env["builtin_icu"]:
+ env_icu = env_modules.Clone()
+ env_icu.disable_warnings()
+
+ thirdparty_dir = "#thirdparty/icu4c/"
+ thirdparty_sources = [
+ "common/appendable.cpp",
+ "common/bmpset.cpp",
+ "common/brkeng.cpp",
+ "common/brkiter.cpp",
+ "common/bytesinkutil.cpp",
+ "common/bytestream.cpp",
+ "common/bytestrie.cpp",
+ "common/bytestriebuilder.cpp",
+ "common/bytestrieiterator.cpp",
+ "common/caniter.cpp",
+ "common/characterproperties.cpp",
+ "common/chariter.cpp",
+ "common/charstr.cpp",
+ "common/cmemory.cpp",
+ "common/cstr.cpp",
+ "common/cstring.cpp",
+ "common/cwchar.cpp",
+ "common/dictbe.cpp",
+ "common/dictionarydata.cpp",
+ "common/dtintrv.cpp",
+ "common/edits.cpp",
+ "common/errorcode.cpp",
+ "common/filteredbrk.cpp",
+ "common/filterednormalizer2.cpp",
+ "common/icudataver.cpp",
+ "common/icuplug.cpp",
+ "common/loadednormalizer2impl.cpp",
+ "common/localebuilder.cpp",
+ "common/localematcher.cpp",
+ "common/localeprioritylist.cpp",
+ "common/locavailable.cpp",
+ "common/locbased.cpp",
+ "common/locdispnames.cpp",
+ "common/locdistance.cpp",
+ "common/locdspnm.cpp",
+ "common/locid.cpp",
+ "common/loclikely.cpp",
+ "common/loclikelysubtags.cpp",
+ "common/locmap.cpp",
+ "common/locresdata.cpp",
+ "common/locutil.cpp",
+ "common/lsr.cpp",
+ "common/messagepattern.cpp",
+ "common/normalizer2.cpp",
+ "common/normalizer2impl.cpp",
+ "common/normlzr.cpp",
+ "common/parsepos.cpp",
+ "common/patternprops.cpp",
+ "common/pluralmap.cpp",
+ "common/propname.cpp",
+ "common/propsvec.cpp",
+ "common/punycode.cpp",
+ "common/putil.cpp",
+ "common/rbbi.cpp",
+ "common/rbbi_cache.cpp",
+ "common/rbbidata.cpp",
+ "common/rbbinode.cpp",
+ "common/rbbirb.cpp",
+ "common/rbbiscan.cpp",
+ "common/rbbisetb.cpp",
+ "common/rbbistbl.cpp",
+ "common/rbbitblb.cpp",
+ "common/resbund.cpp",
+ "common/resbund_cnv.cpp",
+ "common/resource.cpp",
+ "common/restrace.cpp",
+ "common/ruleiter.cpp",
+ "common/schriter.cpp",
+ "common/serv.cpp",
+ "common/servlk.cpp",
+ "common/servlkf.cpp",
+ "common/servls.cpp",
+ "common/servnotf.cpp",
+ "common/servrbf.cpp",
+ "common/servslkf.cpp",
+ "common/sharedobject.cpp",
+ "common/simpleformatter.cpp",
+ "common/static_unicode_sets.cpp",
+ "common/stringpiece.cpp",
+ "common/stringtriebuilder.cpp",
+ "common/uarrsort.cpp",
+ "common/ubidi.cpp",
+ "common/ubidi_props.cpp",
+ "common/ubidiln.cpp",
+ "common/ubiditransform.cpp",
+ "common/ubidiwrt.cpp",
+ "common/ubrk.cpp",
+ "common/ucase.cpp",
+ "common/ucasemap.cpp",
+ "common/ucasemap_titlecase_brkiter.cpp",
+ "common/ucat.cpp",
+ "common/uchar.cpp",
+ "common/ucharstrie.cpp",
+ "common/ucharstriebuilder.cpp",
+ "common/ucharstrieiterator.cpp",
+ "common/uchriter.cpp",
+ "common/ucln_cmn.cpp",
+ "common/ucmndata.cpp",
+ "common/ucnv.cpp",
+ "common/ucnv2022.cpp",
+ "common/ucnv_bld.cpp",
+ "common/ucnv_cb.cpp",
+ "common/ucnv_cnv.cpp",
+ "common/ucnv_ct.cpp",
+ "common/ucnv_err.cpp",
+ "common/ucnv_ext.cpp",
+ "common/ucnv_io.cpp",
+ "common/ucnv_lmb.cpp",
+ "common/ucnv_set.cpp",
+ "common/ucnv_u16.cpp",
+ "common/ucnv_u32.cpp",
+ "common/ucnv_u7.cpp",
+ "common/ucnv_u8.cpp",
+ "common/ucnvbocu.cpp",
+ "common/ucnvdisp.cpp",
+ "common/ucnvhz.cpp",
+ "common/ucnvisci.cpp",
+ "common/ucnvlat1.cpp",
+ "common/ucnvmbcs.cpp",
+ "common/ucnvscsu.cpp",
+ "common/ucnvsel.cpp",
+ "common/ucol_swp.cpp",
+ "common/ucptrie.cpp",
+ "common/ucurr.cpp",
+ "common/udata.cpp",
+ "common/udatamem.cpp",
+ "common/udataswp.cpp",
+ "common/uenum.cpp",
+ "common/uhash.cpp",
+ "common/uhash_us.cpp",
+ "common/uidna.cpp",
+ "common/uinit.cpp",
+ "common/uinvchar.cpp",
+ "common/uiter.cpp",
+ "common/ulist.cpp",
+ "common/uloc.cpp",
+ "common/uloc_keytype.cpp",
+ "common/uloc_tag.cpp",
+ "common/umapfile.cpp",
+ "common/umath.cpp",
+ "common/umutablecptrie.cpp",
+ "common/umutex.cpp",
+ "common/unames.cpp",
+ "common/unifiedcache.cpp",
+ "common/unifilt.cpp",
+ "common/unifunct.cpp",
+ "common/uniset.cpp",
+ "common/uniset_closure.cpp",
+ "common/uniset_props.cpp",
+ "common/unisetspan.cpp",
+ "common/unistr.cpp",
+ "common/unistr_case.cpp",
+ "common/unistr_case_locale.cpp",
+ "common/unistr_cnv.cpp",
+ "common/unistr_props.cpp",
+ "common/unistr_titlecase_brkiter.cpp",
+ "common/unorm.cpp",
+ "common/unormcmp.cpp",
+ "common/uobject.cpp",
+ "common/uprops.cpp",
+ "common/ures_cnv.cpp",
+ "common/uresbund.cpp",
+ "common/uresdata.cpp",
+ "common/usc_impl.cpp",
+ "common/uscript.cpp",
+ "common/uscript_props.cpp",
+ "common/uset.cpp",
+ "common/uset_props.cpp",
+ "common/usetiter.cpp",
+ # "common/ushape.cpp",
+ "common/usprep.cpp",
+ "common/ustack.cpp",
+ "common/ustr_cnv.cpp",
+ "common/ustr_titlecase_brkiter.cpp",
+ "common/ustr_wcs.cpp",
+ "common/ustrcase.cpp",
+ "common/ustrcase_locale.cpp",
+ "common/ustrenum.cpp",
+ "common/ustrfmt.cpp",
+ "common/ustring.cpp",
+ "common/ustrtrns.cpp",
+ "common/utext.cpp",
+ "common/utf_impl.cpp",
+ "common/util.cpp",
+ "common/util_props.cpp",
+ "common/utrace.cpp",
+ "common/utrie.cpp",
+ "common/utrie2.cpp",
+ "common/utrie2_builder.cpp",
+ "common/utrie_swap.cpp",
+ "common/uts46.cpp",
+ "common/utypes.cpp",
+ "common/uvector.cpp",
+ "common/uvectr32.cpp",
+ "common/uvectr64.cpp",
+ "common/wintz.cpp",
+ ]
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ icu_data_name = "icudt68l.dat"
+
+ if env_icu["tools"]:
+ env_icu.Depends("#thirdparty/icu4c/icudata.gen.h", "#thirdparty/icu4c/" + icu_data_name)
+ env_icu.Command("#thirdparty/icu4c/icudata.gen.h", "#thirdparty/icu4c/" + icu_data_name, make_icu_data)
+ else:
+ thirdparty_sources += ["icu_data/icudata_stub.cpp"]
+
+ env_icu.Append(CPPPATH=["#thirdparty/icu4c/common/"])
+ env_icu.Append(
+ CXXFLAGS=[
+ "-DU_STATIC_IMPLEMENTATION",
+ "-DU_COMMON_IMPLEMENTATION",
+ "-DUCONFIG_NO_COLLATION",
+ "-DUCONFIG_NO_CONVERSION",
+ "-DUCONFIG_NO_FORMATTING",
+ "-DUCONFIG_NO_SERVICE",
+ "-DUCONFIG_NO_IDNA",
+ "-DUCONFIG_NO_FILE_IO",
+ "-DUCONFIG_NO_TRANSLITERATION",
+ "-DPKGDATA_MODE=static",
+ "-DICU_DATA_NAME=" + icu_data_name,
+ ]
+ )
+ env_text_server_adv.Append(
+ CXXFLAGS=[
+ "-DICU_DATA_NAME=" + icu_data_name,
+ ]
+ )
+
+ lib = env_icu.add_library("icu_builtin", thirdparty_sources)
+ thirdparty_obj += lib
+
+ # Needs to be appended to arrive after libscene in the linker call,
+ # but we don't want it to arrive *after* system libs, so manual hack
+ # LIBS contains first SCons Library objects ("SCons.Node.FS.File object")
+ # and then plain strings for system library. We insert between the two.
+ inserted = False
+ for idx, linklib in enumerate(env["LIBS"]):
+ if isinstance(linklib, (str, bytes)): # first system lib such as "X11", otherwise SCons lib object
+ env["LIBS"].insert(idx, lib)
+ inserted = True
+ break
+ if not inserted:
+ env.Append(LIBS=[lib])
+
+
+# Godot source files
+
+module_obj = []
+
+if env_text_server_adv["tools"]:
+ env_text_server_adv.Append(CXXFLAGS=["-DICU_STATIC_DATA"])
+
+env_text_server_adv.Append(
+ CPPPATH=[
+ "#thirdparty/harfbuzz/src",
+ "#thirdparty/freetype/include",
+ "#thirdparty/graphite/include",
+ "#thirdparty/icu4c/common/",
+ ]
+)
+
+env_text_server_adv.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
+
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/text_server_adv/bitmap_font_adv.cpp b/modules/text_server_adv/bitmap_font_adv.cpp
new file mode 100644
index 0000000000..b905b7dabb
--- /dev/null
+++ b/modules/text_server_adv/bitmap_font_adv.cpp
@@ -0,0 +1,580 @@
+/*************************************************************************/
+/* bitmap_font_adv.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "bitmap_font_adv.h"
+
+/*************************************************************************/
+/* hb_bmp_font_t HarfBuzz Bitmap font interface */
+/*************************************************************************/
+
+struct hb_bmp_font_t {
+ BitmapFontDataAdvanced *face = nullptr;
+ float font_size = 0;
+ bool unref = false; /* Whether to destroy bm_face when done. */
+};
+
+static hb_bmp_font_t *_hb_bmp_font_create(BitmapFontDataAdvanced *p_face, float p_font_size, bool p_unref) {
+ hb_bmp_font_t *bm_font = reinterpret_cast<hb_bmp_font_t *>(calloc(1, sizeof(hb_bmp_font_t)));
+
+ if (!bm_font) {
+ return nullptr;
+ }
+
+ bm_font->face = p_face;
+ bm_font->font_size = p_font_size;
+ bm_font->unref = p_unref;
+
+ return bm_font;
+}
+
+static void _hb_bmp_font_destroy(void *data) {
+ hb_bmp_font_t *bm_font = reinterpret_cast<hb_bmp_font_t *>(data);
+ free(bm_font);
+}
+
+static hb_bool_t hb_bmp_get_nominal_glyph(hb_font_t *font, void *font_data, hb_codepoint_t unicode, hb_codepoint_t *glyph, void *user_data) {
+ const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(font_data);
+
+ if (!bm_font->face) {
+ return false;
+ }
+
+ if (!bm_font->face->has_char(unicode)) {
+ if (bm_font->face->has_char(0xF000u + unicode)) {
+ *glyph = 0xF000u + unicode;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ *glyph = unicode;
+ return true;
+}
+
+static hb_position_t hb_bmp_get_glyph_h_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph, void *user_data) {
+ const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(font_data);
+
+ if (!bm_font->face) {
+ return 0;
+ }
+
+ if (!bm_font->face->has_char(glyph)) {
+ return 0;
+ }
+
+ return bm_font->face->get_advance(glyph, bm_font->font_size).x * 64;
+}
+
+static hb_position_t hb_bmp_get_glyph_v_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph, void *user_data) {
+ const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(font_data);
+
+ if (!bm_font->face) {
+ return 0;
+ }
+
+ if (!bm_font->face->has_char(glyph)) {
+ return 0;
+ }
+
+ return -bm_font->face->get_advance(glyph, bm_font->font_size).y * 64;
+}
+
+static hb_position_t hb_bmp_get_glyph_h_kerning(hb_font_t *font, void *font_data, hb_codepoint_t left_glyph, hb_codepoint_t right_glyph, void *user_data) {
+ const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(font_data);
+
+ if (!bm_font->face) {
+ return 0;
+ }
+
+ if (!bm_font->face->has_char(left_glyph)) {
+ return 0;
+ }
+
+ if (!bm_font->face->has_char(right_glyph)) {
+ return 0;
+ }
+
+ return bm_font->face->get_kerning(left_glyph, right_glyph, bm_font->font_size).x * 64;
+}
+
+static hb_bool_t hb_bmp_get_glyph_v_origin(hb_font_t *font, void *font_data, hb_codepoint_t glyph, hb_position_t *x, hb_position_t *y, void *user_data) {
+ const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(font_data);
+
+ if (!bm_font->face) {
+ return false;
+ }
+
+ if (!bm_font->face->has_char(glyph)) {
+ return false;
+ }
+
+ *x = bm_font->face->get_advance(glyph, bm_font->font_size).x * 32;
+ *y = bm_font->face->get_ascent(bm_font->font_size) * 64;
+
+ return true;
+}
+
+static hb_bool_t hb_bmp_get_glyph_extents(hb_font_t *font, void *font_data, hb_codepoint_t glyph, hb_glyph_extents_t *extents, void *user_data) {
+ const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(font_data);
+
+ if (!bm_font->face) {
+ return false;
+ }
+
+ if (!bm_font->face->has_char(glyph)) {
+ return false;
+ }
+
+ extents->x_bearing = 0;
+ extents->y_bearing = 0;
+ extents->width = bm_font->face->get_size(glyph, bm_font->font_size).x * 64;
+ extents->height = bm_font->face->get_size(glyph, bm_font->font_size).y * 64;
+
+ return true;
+}
+
+static hb_bool_t hb_bmp_get_font_h_extents(hb_font_t *font, void *font_data, hb_font_extents_t *metrics, void *user_data) {
+ const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(font_data);
+
+ if (!bm_font->face) {
+ return false;
+ }
+
+ metrics->ascender = bm_font->face->get_ascent(bm_font->font_size);
+ metrics->descender = bm_font->face->get_descent(bm_font->font_size);
+ metrics->line_gap = 0;
+
+ return true;
+}
+
+static hb_font_funcs_t *funcs = nullptr;
+void hb_bmp_create_font_funcs() {
+ funcs = hb_font_funcs_create();
+
+ hb_font_funcs_set_font_h_extents_func(funcs, hb_bmp_get_font_h_extents, nullptr, nullptr);
+ //hb_font_funcs_set_font_v_extents_func (funcs, hb_bmp_get_font_v_extents, nullptr, nullptr);
+ hb_font_funcs_set_nominal_glyph_func(funcs, hb_bmp_get_nominal_glyph, nullptr, nullptr);
+ //hb_font_funcs_set_variation_glyph_func (funcs, hb_bmp_get_variation_glyph, nullptr, nullptr);
+ hb_font_funcs_set_glyph_h_advance_func(funcs, hb_bmp_get_glyph_h_advance, nullptr, nullptr);
+ hb_font_funcs_set_glyph_v_advance_func(funcs, hb_bmp_get_glyph_v_advance, nullptr, nullptr);
+ //hb_font_funcs_set_glyph_h_origin_func(funcs, hb_bmp_get_glyph_h_origin, nullptr, nullptr);
+ hb_font_funcs_set_glyph_v_origin_func(funcs, hb_bmp_get_glyph_v_origin, nullptr, nullptr);
+ hb_font_funcs_set_glyph_h_kerning_func(funcs, hb_bmp_get_glyph_h_kerning, nullptr, nullptr);
+ //hb_font_funcs_set_glyph_v_kerning_func (funcs, hb_bmp_get_glyph_v_kerning, nullptr, nullptr);
+ hb_font_funcs_set_glyph_extents_func(funcs, hb_bmp_get_glyph_extents, nullptr, nullptr);
+ //hb_font_funcs_set_glyph_contour_point_func (funcs, hb_bmp_get_glyph_contour_point, nullptr, nullptr);
+ //hb_font_funcs_set_glyph_name_func (funcs, hb_bmp_get_glyph_name, nullptr, nullptr);
+ //hb_font_funcs_set_glyph_from_name_func (funcs, hb_bmp_get_glyph_from_name, nullptr, nullptr);
+
+ hb_font_funcs_make_immutable(funcs);
+}
+
+void hb_bmp_free_font_funcs() {
+ if (funcs != nullptr) {
+ hb_font_funcs_destroy(funcs);
+ funcs = nullptr;
+ }
+}
+
+static void _hb_bmp_font_set_funcs(hb_font_t *p_font, BitmapFontDataAdvanced *p_face, int p_size, bool p_unref) {
+ hb_font_set_funcs(p_font, funcs, _hb_bmp_font_create(p_face, p_size, p_unref), _hb_bmp_font_destroy);
+}
+
+hb_font_t *hb_bmp_font_create(BitmapFontDataAdvanced *p_face, int p_size, hb_destroy_func_t p_destroy) {
+ hb_font_t *font;
+ hb_face_t *face = hb_face_create(nullptr, 0);
+
+ font = hb_font_create(face);
+ hb_face_destroy(face);
+ _hb_bmp_font_set_funcs(font, p_face, p_size, false);
+ return font;
+}
+
+/*************************************************************************/
+/* BitmapFontDataAdvanced */
+/*************************************************************************/
+
+Error BitmapFontDataAdvanced::load_from_file(const String &p_filename, int p_base_size) {
+ _THREAD_SAFE_METHOD_
+ //fnt format used by angelcode bmfont
+ //http://www.angelcode.com/products/bmfont/
+
+ FileAccess *f = FileAccess::open(p_filename, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(!f, ERR_FILE_NOT_FOUND, "Can't open font: " + p_filename + ".");
+
+ while (true) {
+ String line = f->get_line();
+
+ int delimiter = line.find(" ");
+ String type = line.substr(0, delimiter);
+ int pos = delimiter + 1;
+ Map<String, String> keys;
+
+ while (pos < line.size() && line[pos] == ' ') {
+ pos++;
+ }
+
+ while (pos < line.size()) {
+ int eq = line.find("=", pos);
+ if (eq == -1) {
+ break;
+ }
+ String key = line.substr(pos, eq - pos);
+ int end = -1;
+ String value;
+ if (line[eq + 1] == '"') {
+ end = line.find("\"", eq + 2);
+ if (end == -1) {
+ break;
+ }
+ value = line.substr(eq + 2, end - 1 - eq - 1);
+ pos = end + 1;
+ } else {
+ end = line.find(" ", eq + 1);
+ if (end == -1) {
+ end = line.size();
+ }
+ value = line.substr(eq + 1, end - eq);
+ pos = end;
+ }
+
+ while (pos < line.size() && line[pos] == ' ') {
+ pos++;
+ }
+
+ keys[key] = value;
+ }
+
+ if (type == "info") {
+ if (keys.has("size")) {
+ base_size = keys["size"].to_int();
+ }
+ } else if (type == "common") {
+ if (keys.has("lineHeight")) {
+ height = keys["lineHeight"].to_int();
+ }
+ if (keys.has("base")) {
+ ascent = keys["base"].to_int();
+ }
+ } else if (type == "page") {
+ if (keys.has("file")) {
+ String base_dir = p_filename.get_base_dir();
+ String file = base_dir.plus_file(keys["file"]);
+ if (RenderingServer::get_singleton() != nullptr) {
+ Ref<Texture2D> tex = ResourceLoader::load(file);
+ if (tex.is_null()) {
+ ERR_PRINT("Can't load font texture!");
+ } else {
+ ERR_FAIL_COND_V_MSG(tex.is_null(), ERR_FILE_CANT_READ, "It's not a reference to a valid Texture object.");
+ textures.push_back(tex);
+ }
+ }
+ }
+ } else if (type == "char") {
+ Character c;
+ char32_t idx = 0;
+ if (keys.has("id")) {
+ idx = keys["id"].to_int();
+ }
+ if (keys.has("x")) {
+ c.rect.position.x = keys["x"].to_int();
+ }
+ if (keys.has("y")) {
+ c.rect.position.y = keys["y"].to_int();
+ }
+ if (keys.has("width")) {
+ c.rect.size.width = keys["width"].to_int();
+ }
+ if (keys.has("height")) {
+ c.rect.size.height = keys["height"].to_int();
+ }
+ if (keys.has("xoffset")) {
+ c.align.x = keys["xoffset"].to_int();
+ }
+ if (keys.has("yoffset")) {
+ c.align.y = keys["yoffset"].to_int();
+ }
+ if (keys.has("page")) {
+ c.texture_idx = keys["page"].to_int();
+ }
+ if (keys.has("xadvance")) {
+ c.advance.x = keys["xadvance"].to_int();
+ }
+ if (keys.has("yadvance")) {
+ c.advance.x = keys["yadvance"].to_int();
+ }
+ if (c.advance.x < 0) {
+ c.advance.x = c.rect.size.width + 1;
+ }
+ if (c.advance.y < 0) {
+ c.advance.y = c.rect.size.height + 1;
+ }
+ char_map[idx] = c;
+ } else if (type == "kerning") {
+ KerningPairKey kpk;
+ float k = 0;
+ if (keys.has("first")) {
+ kpk.A = keys["first"].to_int();
+ }
+ if (keys.has("second")) {
+ kpk.B = keys["second"].to_int();
+ }
+ if (keys.has("amount")) {
+ k = keys["amount"].to_int();
+ }
+ kerning_map[kpk] = k;
+ }
+
+ if (f->eof_reached()) {
+ break;
+ }
+ }
+ if (base_size == 0) {
+ base_size = height;
+ }
+
+ valid = true;
+
+ memdelete(f);
+ return OK;
+}
+
+Error BitmapFontDataAdvanced::load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND_V(p_data == nullptr, ERR_CANT_CREATE);
+ ERR_FAIL_COND_V(p_size != sizeof(TextServer::BitmapFontData), ERR_CANT_CREATE);
+
+ const TextServer::BitmapFontData *data = (const TextServer::BitmapFontData *)p_data;
+
+ if (RenderingServer::get_singleton() != nullptr) {
+ Ref<Image> image = memnew(Image(data->img));
+ Ref<ImageTexture> tex = memnew(ImageTexture);
+ tex->create_from_image(image);
+
+ textures.push_back(tex);
+ }
+
+ for (int i = 0; i < data->charcount; i++) {
+ const int *c = &data->char_rects[i * 8];
+
+ Character chr;
+ chr.rect.position.x = c[1];
+ chr.rect.position.y = c[2];
+ chr.rect.size.x = c[3];
+ chr.rect.size.y = c[4];
+ if (c[7] < 0) {
+ chr.advance.x = c[3];
+ } else {
+ chr.advance.x = c[7];
+ }
+ chr.align = Vector2(c[6], c[5]);
+ char_map[c[0]] = chr;
+ }
+
+ for (int i = 0; i < data->kerning_count; i++) {
+ KerningPairKey kpk;
+ kpk.A = data->kernings[i * 3 + 0];
+ kpk.B = data->kernings[i * 3 + 1];
+
+ if (data->kernings[i * 3 + 2] == 0 && kerning_map.has(kpk)) {
+ kerning_map.erase(kpk);
+ } else {
+ kerning_map[kpk] = data->kernings[i * 3 + 2];
+ }
+ }
+
+ height = data->height;
+ ascent = data->ascent;
+
+ base_size = p_base_size;
+ if (base_size == 0) {
+ base_size = height;
+ }
+
+ valid = true;
+
+ return OK;
+}
+
+float BitmapFontDataAdvanced::get_height(int p_size) const {
+ ERR_FAIL_COND_V(!valid, 0.f);
+ return height * (float(p_size) / float(base_size));
+}
+
+float BitmapFontDataAdvanced::get_ascent(int p_size) const {
+ ERR_FAIL_COND_V(!valid, 0.f);
+ return ascent * (float(p_size) / float(base_size));
+}
+
+float BitmapFontDataAdvanced::get_descent(int p_size) const {
+ ERR_FAIL_COND_V(!valid, 0.f);
+ return (height - ascent) * (float(p_size) / float(base_size));
+}
+
+float BitmapFontDataAdvanced::get_underline_position(int p_size) const {
+ ERR_FAIL_COND_V(!valid, 0.f);
+ return 2 * (float(p_size) / float(base_size));
+}
+
+float BitmapFontDataAdvanced::get_underline_thickness(int p_size) const {
+ ERR_FAIL_COND_V(!valid, 0.f);
+ return 1 * (float(p_size) / float(base_size));
+}
+
+void BitmapFontDataAdvanced::set_distance_field_hint(bool p_distance_field) {
+ distance_field_hint = p_distance_field;
+}
+
+bool BitmapFontDataAdvanced::get_distance_field_hint() const {
+ return distance_field_hint;
+}
+
+float BitmapFontDataAdvanced::get_base_size() const {
+ return base_size;
+}
+
+hb_font_t *BitmapFontDataAdvanced::get_hb_handle(int p_size) {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND_V(!valid, nullptr);
+ if (!cache.has(p_size)) {
+ cache[p_size] = hb_bmp_font_create(this, p_size, nullptr);
+ }
+ return cache[p_size];
+}
+
+bool BitmapFontDataAdvanced::has_char(char32_t p_char) const {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND_V(!valid, false);
+ return char_map.has(p_char);
+}
+
+String BitmapFontDataAdvanced::get_supported_chars() const {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND_V(!valid, String());
+ String chars;
+ const uint32_t *k = nullptr;
+ while ((k = char_map.next(k))) {
+ chars += char32_t(*k);
+ }
+ return chars;
+}
+
+Vector2 BitmapFontDataAdvanced::get_advance(uint32_t p_char, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND_V(!valid, Vector2());
+ const Character *c = char_map.getptr(p_char);
+ ERR_FAIL_COND_V(c == nullptr, Vector2());
+
+ return c->advance * (float(p_size) / float(base_size));
+}
+
+Vector2 BitmapFontDataAdvanced::get_align(uint32_t p_char, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND_V(!valid, Vector2());
+ const Character *c = char_map.getptr(p_char);
+ ERR_FAIL_COND_V(c == nullptr, Vector2());
+
+ return c->align * (float(p_size) / float(base_size));
+}
+
+Vector2 BitmapFontDataAdvanced::get_size(uint32_t p_char, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND_V(!valid, Vector2());
+ const Character *c = char_map.getptr(p_char);
+ ERR_FAIL_COND_V(c == nullptr, Vector2());
+
+ return c->rect.size * (float(p_size) / float(base_size));
+}
+
+Vector2 BitmapFontDataAdvanced::get_kerning(uint32_t p_char, uint32_t p_next, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND_V(!valid, Vector2());
+ KerningPairKey kpk;
+ kpk.A = p_char;
+ kpk.B = p_next;
+
+ const Map<KerningPairKey, int>::Element *E = kerning_map.find(kpk);
+ if (E) {
+ return Vector2(-E->get() * (float(p_size) / float(base_size)), 0.f);
+ } else {
+ return Vector2();
+ }
+}
+
+Vector2 BitmapFontDataAdvanced::draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
+ _THREAD_SAFE_METHOD_
+ if (p_index == 0) {
+ return Vector2();
+ }
+ ERR_FAIL_COND_V(!valid, Vector2());
+ const Character *c = char_map.getptr(p_index);
+
+ ERR_FAIL_COND_V(c == nullptr, Vector2());
+ ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), Vector2());
+ if (c->texture_idx != -1) {
+ Point2 cpos = p_pos;
+ cpos += c->align * (float(p_size) / float(base_size));
+ cpos.y -= ascent * (float(p_size) / float(base_size));
+ if (RenderingServer::get_singleton() != nullptr) {
+ //if (distance_field_hint) { // Not implemented.
+ // RenderingServer::get_singleton()->canvas_item_set_distance_field_mode(p_canvas, true);
+ //}
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, c->rect.size * (float(p_size) / float(base_size))), textures[c->texture_idx]->get_rid(), c->rect, p_color, false, false);
+ //if (distance_field_hint) {
+ // RenderingServer::get_singleton()->canvas_item_set_distance_field_mode(p_canvas, false);
+ //}
+ }
+ }
+
+ return c->advance * (float(p_size) / float(base_size));
+}
+
+Vector2 BitmapFontDataAdvanced::draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
+ _THREAD_SAFE_METHOD_
+ if (p_index == 0) {
+ return Vector2();
+ }
+ ERR_FAIL_COND_V(!valid, Vector2());
+ const Character *c = char_map.getptr(p_index);
+
+ ERR_FAIL_COND_V(c == nullptr, Vector2());
+ ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), Vector2());
+
+ // Not supported, return advance for compatibility.
+
+ return c->advance * (float(p_size) / float(base_size));
+}
+
+BitmapFontDataAdvanced::~BitmapFontDataAdvanced() {
+ for (Map<float, hb_font_t *>::Element *E = cache.front(); E; E = E->next()) {
+ hb_font_destroy(E->get());
+ }
+}
diff --git a/modules/text_server_adv/bitmap_font_adv.h b/modules/text_server_adv/bitmap_font_adv.h
new file mode 100644
index 0000000000..cb1a726f76
--- /dev/null
+++ b/modules/text_server_adv/bitmap_font_adv.h
@@ -0,0 +1,118 @@
+/*************************************************************************/
+/* bitmap_font_adv.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef BITMAP_FONT_ADV_H
+#define BITMAP_FONT_ADV_H
+
+#include "font_adv.h"
+
+void hb_bmp_create_font_funcs();
+void hb_bmp_free_font_funcs();
+
+struct BitmapFontDataAdvanced : public FontDataAdvanced {
+ _THREAD_SAFE_CLASS_
+
+private:
+ Vector<Ref<Texture2D>> textures;
+
+ struct Character {
+ int texture_idx = 0;
+ Rect2 rect;
+ Vector2 align;
+ Vector2 advance = Vector2(-1, -1);
+ };
+
+ struct KerningPairKey {
+ union {
+ struct {
+ uint32_t A, B;
+ };
+
+ uint64_t pair = 0;
+ };
+
+ _FORCE_INLINE_ bool operator<(const KerningPairKey &p_r) const { return pair < p_r.pair; }
+ };
+
+ HashMap<uint32_t, Character> char_map;
+ Map<KerningPairKey, int> kerning_map;
+ Map<float, hb_font_t *> cache;
+
+ float height = 0.f;
+ float ascent = 0.f;
+ float base_size = 0.f;
+ bool distance_field_hint = false;
+
+public:
+ virtual void clear_cache() override{};
+
+ virtual Error load_from_file(const String &p_filename, int p_base_size) override;
+ virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) override;
+
+ virtual float get_height(int p_size) const override;
+ virtual float get_ascent(int p_size) const override;
+ virtual float get_descent(int p_size) const override;
+
+ virtual float get_underline_position(int p_size) const override;
+ virtual float get_underline_thickness(int p_size) const override;
+
+ virtual void set_antialiased(bool p_antialiased) override{};
+ virtual bool get_antialiased() const override { return false; };
+
+ virtual void set_hinting(TextServer::Hinting p_hinting) override{};
+ virtual TextServer::Hinting get_hinting() const override { return TextServer::HINTING_NONE; };
+
+ virtual void set_distance_field_hint(bool p_distance_field) override;
+ virtual bool get_distance_field_hint() const override;
+
+ virtual void set_force_autohinter(bool p_enabeld) override{};
+ virtual bool get_force_autohinter() const override { return false; };
+
+ virtual bool has_outline() const override { return false; };
+ virtual float get_base_size() const override;
+
+ virtual hb_font_t *get_hb_handle(int p_size) override;
+
+ virtual bool has_char(char32_t p_char) const override;
+ virtual String get_supported_chars() const override;
+
+ virtual Vector2 get_advance(uint32_t p_char, int p_size) const override;
+ Vector2 get_align(uint32_t p_char, int p_size) const;
+ Vector2 get_size(uint32_t p_char, int p_size) const;
+ virtual Vector2 get_kerning(uint32_t p_char, uint32_t p_next, int p_size) const override;
+ virtual uint32_t get_glyph_index(char32_t p_char, char32_t p_variation_selector) const override { return (uint32_t)p_char; };
+
+ virtual Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override;
+ virtual Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override;
+
+ virtual ~BitmapFontDataAdvanced();
+};
+
+#endif // BITMAP_FONT_ADV_H
diff --git a/modules/text_server_adv/config.py b/modules/text_server_adv/config.py
new file mode 100644
index 0000000000..22482fce24
--- /dev/null
+++ b/modules/text_server_adv/config.py
@@ -0,0 +1,6 @@
+def can_build(env, platform):
+ return env.module_check_dependencies("text_server_adv", ["freetype"])
+
+
+def configure(env):
+ pass
diff --git a/modules/text_server_adv/dynamic_font_adv.cpp b/modules/text_server_adv/dynamic_font_adv.cpp
new file mode 100644
index 0000000000..08c4ad2727
--- /dev/null
+++ b/modules/text_server_adv/dynamic_font_adv.cpp
@@ -0,0 +1,1003 @@
+/*************************************************************************/
+/* dynamic_font_adv.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "dynamic_font_adv.h"
+
+#include FT_STROKER_H
+#include FT_ADVANCES_H
+#include FT_MULTIPLE_MASTERS_H
+
+DynamicFontDataAdvanced::DataAtSize *DynamicFontDataAdvanced::get_data_for_size(int p_size, int p_outline_size) {
+ ERR_FAIL_COND_V(!valid, nullptr);
+ ERR_FAIL_COND_V(p_size < 0 || p_size > UINT16_MAX, nullptr);
+ ERR_FAIL_COND_V(p_outline_size < 0 || p_outline_size > UINT16_MAX, nullptr);
+
+ CacheID id;
+ id.size = p_size;
+ id.outline_size = p_outline_size;
+
+ DataAtSize *fds = nullptr;
+ Map<CacheID, DataAtSize *>::Element *E = nullptr;
+ if (p_outline_size != 0) {
+ E = size_cache_outline.find(id);
+ } else {
+ E = size_cache.find(id);
+ }
+
+ if (E != nullptr) {
+ fds = E->get();
+ } else {
+ if (font_mem == nullptr && font_path != String()) {
+ if (!font_mem_cache.empty()) {
+ font_mem = font_mem_cache.ptr();
+ font_mem_size = font_mem_cache.size();
+ } else {
+ FileAccess *f = FileAccess::open(font_path, FileAccess::READ);
+ if (!f) {
+ ERR_FAIL_V_MSG(nullptr, "Cannot open font file '" + font_path + "'.");
+ }
+
+ size_t len = f->get_len();
+ font_mem_cache.resize(len);
+ f->get_buffer(font_mem_cache.ptrw(), len);
+ font_mem = font_mem_cache.ptr();
+ font_mem_size = len;
+ f->close();
+ }
+ }
+
+ int error = 0;
+ fds = memnew(DataAtSize);
+ if (font_mem) {
+ memset(&fds->stream, 0, sizeof(FT_StreamRec));
+ fds->stream.base = (unsigned char *)font_mem;
+ fds->stream.size = font_mem_size;
+ fds->stream.pos = 0;
+
+ FT_Open_Args fargs;
+ memset(&fargs, 0, sizeof(FT_Open_Args));
+ fargs.memory_base = (unsigned char *)font_mem;
+ fargs.memory_size = font_mem_size;
+ fargs.flags = FT_OPEN_MEMORY;
+ fargs.stream = &fds->stream;
+ error = FT_Open_Face(library, &fargs, 0, &fds->face);
+
+ } else {
+ memdelete(fds);
+ ERR_FAIL_V_MSG(nullptr, "DynamicFont uninitialized.");
+ }
+
+ if (error == FT_Err_Unknown_File_Format) {
+ memdelete(fds);
+ ERR_FAIL_V_MSG(nullptr, "Unknown font format.");
+ } else if (error) {
+ memdelete(fds);
+ ERR_FAIL_V_MSG(nullptr, "Error loading font.");
+ }
+
+ oversampling = TS->font_get_oversampling();
+
+ if (FT_HAS_COLOR(fds->face) && fds->face->num_fixed_sizes > 0) {
+ int best_match = 0;
+ int diff = ABS(p_size - ((int64_t)fds->face->available_sizes[0].width));
+ fds->scale_color_font = float(p_size * oversampling) / fds->face->available_sizes[0].width;
+ for (int i = 1; i < fds->face->num_fixed_sizes; i++) {
+ int ndiff = ABS(p_size - ((int64_t)fds->face->available_sizes[i].width));
+ if (ndiff < diff) {
+ best_match = i;
+ diff = ndiff;
+ fds->scale_color_font = float(p_size * oversampling) / fds->face->available_sizes[i].width;
+ }
+ }
+ FT_Select_Size(fds->face, best_match);
+ } else {
+ FT_Set_Pixel_Sizes(fds->face, 0, p_size * oversampling);
+ }
+
+ fds->size = p_size;
+ fds->ascent = (fds->face->size->metrics.ascender / 64.0) / oversampling * fds->scale_color_font;
+ fds->descent = (-fds->face->size->metrics.descender / 64.0) / oversampling * fds->scale_color_font;
+ fds->underline_position = -fds->face->underline_position / 64.0 / oversampling * fds->scale_color_font;
+ fds->underline_thickness = fds->face->underline_thickness / 64.0 / oversampling * fds->scale_color_font;
+
+ //Load os2 TTF pable
+ fds->os2 = (TT_OS2 *)FT_Get_Sfnt_Table(fds->face, FT_SFNT_OS2);
+
+ fds->hb_handle = hb_ft_font_create(fds->face, nullptr);
+ if (fds->hb_handle == nullptr) {
+ memdelete(fds);
+ ERR_FAIL_V_MSG(nullptr, "Error loading HB font.");
+ }
+
+ if (p_outline_size != 0) {
+ size_cache_outline[id] = fds;
+ } else {
+ size_cache[id] = fds;
+ }
+
+ // Write variations.
+ if (fds->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
+ FT_MM_Var *amaster;
+
+ FT_Get_MM_Var(fds->face, &amaster);
+
+ Vector<hb_variation_t> hb_vars;
+ Vector<FT_Fixed> coords;
+ coords.resize(amaster->num_axis);
+
+ FT_Get_Var_Design_Coordinates(fds->face, coords.size(), coords.ptrw());
+
+ for (FT_UInt i = 0; i < amaster->num_axis; i++) {
+ hb_variation_t var;
+
+ // Reset to default.
+ var.tag = amaster->axis[i].tag;
+ var.value = (double)amaster->axis[i].def / 65536.f;
+ coords.write[i] = amaster->axis[i].def;
+
+ if (variations.has(var.tag)) {
+ var.value = variations[var.tag];
+ coords.write[i] = CLAMP(variations[var.tag] * 65536.f, amaster->axis[i].minimum, amaster->axis[i].maximum);
+ }
+
+ hb_vars.push_back(var);
+ }
+
+ FT_Set_Var_Design_Coordinates(fds->face, coords.size(), coords.ptrw());
+ hb_font_set_variations(fds->hb_handle, hb_vars.empty() ? nullptr : &hb_vars[0], hb_vars.size());
+
+ FT_Done_MM_Var(library, amaster);
+ }
+ }
+ return fds;
+}
+
+Dictionary DynamicFontDataAdvanced::get_variation_list() const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(base_size);
+ if (fds == nullptr) {
+ return Dictionary();
+ }
+
+ Dictionary ret;
+ // Read variations.
+ if (fds->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
+ FT_MM_Var *amaster;
+
+ FT_Get_MM_Var(fds->face, &amaster);
+
+ for (FT_UInt i = 0; i < amaster->num_axis; i++) {
+ ret[(int32_t)amaster->axis[i].tag] = Vector3i(amaster->axis[i].minimum / 65536, amaster->axis[i].maximum / 65536, amaster->axis[i].def / 65536);
+ }
+
+ FT_Done_MM_Var(library, amaster);
+ }
+ return ret;
+}
+
+void DynamicFontDataAdvanced::set_variation(const String &p_name, double p_value) {
+ _THREAD_SAFE_METHOD_
+ int32_t tag = TS->name_to_tag(p_name);
+ if (!variations.has(tag) || (variations[tag] != p_value)) {
+ variations[tag] = p_value;
+ clear_cache();
+ }
+}
+
+double DynamicFontDataAdvanced::get_variation(const String &p_name) const {
+ _THREAD_SAFE_METHOD_
+ int32_t tag = TS->name_to_tag(p_name);
+ if (!variations.has(tag)) {
+ return 0.f;
+ }
+ return variations[tag];
+}
+
+Dictionary DynamicFontDataAdvanced::get_feature_list() const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(base_size);
+ if (fds == nullptr) {
+ return Dictionary();
+ }
+
+ Dictionary out;
+ // Read feature flags.
+ unsigned int count = hb_ot_layout_table_get_feature_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GSUB, 0, NULL, NULL);
+ if (count != 0) {
+ hb_tag_t *feature_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
+ hb_ot_layout_table_get_feature_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GSUB, 0, &count, feature_tags);
+ for (unsigned int i = 0; i < count; i++) {
+ out[feature_tags[i]] = 1;
+ }
+ memfree(feature_tags);
+ }
+ count = hb_ot_layout_table_get_feature_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GPOS, 0, NULL, NULL);
+ if (count != 0) {
+ hb_tag_t *feature_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
+ hb_ot_layout_table_get_feature_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GPOS, 0, &count, feature_tags);
+ for (unsigned int i = 0; i < count; i++) {
+ out[feature_tags[i]] = 1;
+ }
+ memfree(feature_tags);
+ }
+
+ return out;
+}
+
+DynamicFontDataAdvanced::TexturePosition DynamicFontDataAdvanced::find_texture_pos_for_glyph(DynamicFontDataAdvanced::DataAtSize *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) {
+ TexturePosition ret;
+ ret.index = -1;
+
+ int mw = p_width;
+ int mh = p_height;
+
+ for (int i = 0; i < p_data->textures.size(); i++) {
+ const CharTexture &ct = p_data->textures[i];
+
+ if (RenderingServer::get_singleton() != nullptr) {
+ if (ct.texture->get_format() != p_image_format) {
+ continue;
+ }
+ }
+
+ if (mw > ct.texture_size || mh > ct.texture_size) { //too big for this texture
+ continue;
+ }
+
+ ret.y = 0x7FFFFFFF;
+ ret.x = 0;
+
+ for (int j = 0; j < ct.texture_size - mw; j++) {
+ int max_y = 0;
+
+ for (int k = j; k < j + mw; k++) {
+ int y = ct.offsets[k];
+ if (y > max_y) {
+ max_y = y;
+ }
+ }
+
+ if (max_y < ret.y) {
+ ret.y = max_y;
+ ret.x = j;
+ }
+ }
+
+ if (ret.y == 0x7FFFFFFF || ret.y + mh > ct.texture_size) {
+ continue; //fail, could not fit it here
+ }
+
+ ret.index = i;
+ break;
+ }
+
+ if (ret.index == -1) {
+ //could not find texture to fit, create one
+ ret.x = 0;
+ ret.y = 0;
+
+ int texsize = MAX(p_data->size * oversampling * 8, 256);
+ if (mw > texsize) {
+ texsize = mw; //special case, adapt to it?
+ }
+ if (mh > texsize) {
+ texsize = mh; //special case, adapt to it?
+ }
+
+ texsize = next_power_of_2(texsize);
+
+ texsize = MIN(texsize, 4096);
+
+ CharTexture tex;
+ tex.texture_size = texsize;
+ tex.imgdata.resize(texsize * texsize * p_color_size); //grayscale alpha
+
+ {
+ //zero texture
+ uint8_t *w = tex.imgdata.ptrw();
+ ERR_FAIL_COND_V(texsize * texsize * p_color_size > tex.imgdata.size(), ret);
+ // Initialize the texture to all-white pixels to prevent artifacts when the
+ // font is displayed at a non-default scale with filtering enabled.
+ if (p_color_size == 2) {
+ for (int i = 0; i < texsize * texsize * p_color_size; i += 2) { // FORMAT_LA8
+ w[i + 0] = 255;
+ w[i + 1] = 0;
+ }
+ } else if (p_color_size == 4) {
+ for (int i = 0; i < texsize * texsize * p_color_size; i += 4) { // FORMAT_RGBA8
+ w[i + 0] = 255;
+ w[i + 1] = 255;
+ w[i + 2] = 255;
+ w[i + 3] = 0;
+ }
+ } else {
+ ERR_FAIL_V(ret);
+ }
+ }
+ tex.offsets.resize(texsize);
+ for (int i = 0; i < texsize; i++) { //zero offsets
+ tex.offsets.write[i] = 0;
+ }
+
+ p_data->textures.push_back(tex);
+ ret.index = p_data->textures.size() - 1;
+ }
+
+ return ret;
+}
+
+DynamicFontDataAdvanced::Character DynamicFontDataAdvanced::Character::not_found() {
+ Character ch;
+ return ch;
+}
+
+DynamicFontDataAdvanced::Character DynamicFontDataAdvanced::bitmap_to_character(DynamicFontDataAdvanced::DataAtSize *p_data, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) {
+ int w = bitmap.width;
+ int h = bitmap.rows;
+
+ int mw = w + rect_margin * 2;
+ int mh = h + rect_margin * 2;
+
+ ERR_FAIL_COND_V(mw > 4096, Character::not_found());
+ ERR_FAIL_COND_V(mh > 4096, Character::not_found());
+
+ int color_size = bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 2;
+ Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8;
+
+ TexturePosition tex_pos = find_texture_pos_for_glyph(p_data, color_size, require_format, mw, mh);
+ ERR_FAIL_COND_V(tex_pos.index < 0, Character::not_found());
+
+ //fit character in char texture
+
+ CharTexture &tex = p_data->textures.write[tex_pos.index];
+
+ {
+ uint8_t *wr = tex.imgdata.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs = ((i + tex_pos.y + rect_margin) * tex.texture_size + j + tex_pos.x + rect_margin) * color_size;
+ ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), Character::not_found());
+ switch (bitmap.pixel_mode) {
+ case FT_PIXEL_MODE_MONO: {
+ int byte = i * bitmap.pitch + (j >> 3);
+ int bit = 1 << (7 - (j % 8));
+ wr[ofs + 0] = 255; //grayscale as 1
+ wr[ofs + 1] = (bitmap.buffer[byte] & bit) ? 255 : 0;
+ } break;
+ case FT_PIXEL_MODE_GRAY:
+ wr[ofs + 0] = 255; //grayscale as 1
+ wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j];
+ break;
+ case FT_PIXEL_MODE_BGRA: {
+ int ofs_color = i * bitmap.pitch + (j << 2);
+ wr[ofs + 2] = bitmap.buffer[ofs_color + 0];
+ wr[ofs + 1] = bitmap.buffer[ofs_color + 1];
+ wr[ofs + 0] = bitmap.buffer[ofs_color + 2];
+ wr[ofs + 3] = bitmap.buffer[ofs_color + 3];
+ } break;
+ // TODO: FT_PIXEL_MODE_LCD
+ default:
+ ERR_FAIL_V_MSG(Character::not_found(), "Font uses unsupported pixel format: " + itos(bitmap.pixel_mode) + ".");
+ break;
+ }
+ }
+ }
+ }
+
+ //blit to image and texture
+ {
+ if (RenderingServer::get_singleton() != nullptr) {
+ Ref<Image> img = memnew(Image(tex.texture_size, tex.texture_size, 0, require_format, tex.imgdata));
+
+ if (tex.texture.is_null()) {
+ tex.texture.instance();
+ tex.texture->create_from_image(img);
+ } else {
+ tex.texture->update(img); //update
+ }
+ }
+ }
+
+ // update height array
+ for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
+ tex.offsets.write[k] = tex_pos.y + mh;
+ }
+
+ Character chr;
+ chr.align = Vector2(xofs, -yofs) * p_data->scale_color_font / oversampling;
+ chr.advance = advance * p_data->scale_color_font / oversampling;
+ chr.texture_idx = tex_pos.index;
+ chr.found = true;
+
+ chr.rect_uv = Rect2(tex_pos.x + rect_margin, tex_pos.y + rect_margin, w, h);
+ chr.rect = chr.rect_uv;
+ chr.rect.position /= oversampling;
+ chr.rect.size *= (p_data->scale_color_font / oversampling);
+ return chr;
+}
+
+void DynamicFontDataAdvanced::update_glyph(int p_size, uint32_t p_index) {
+ DataAtSize *fds = get_data_for_size(p_size, false);
+ ERR_FAIL_COND(fds == nullptr);
+
+ if (fds->glyph_map.has(p_index)) {
+ return;
+ }
+
+ Character character = Character::not_found();
+ FT_GlyphSlot slot = fds->face->glyph;
+
+ if (p_index == 0) {
+ fds->glyph_map[p_index] = character;
+ return;
+ }
+
+ int ft_hinting;
+ switch (hinting) {
+ case TextServer::HINTING_NONE:
+ ft_hinting = FT_LOAD_NO_HINTING;
+ break;
+ case TextServer::HINTING_LIGHT:
+ ft_hinting = FT_LOAD_TARGET_LIGHT;
+ break;
+ default:
+ ft_hinting = FT_LOAD_TARGET_NORMAL;
+ break;
+ }
+
+ FT_Fixed v, h;
+ FT_Get_Advance(fds->face, p_index, FT_HAS_COLOR(fds->face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting, &h);
+ FT_Get_Advance(fds->face, p_index, FT_HAS_COLOR(fds->face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting | FT_LOAD_VERTICAL_LAYOUT, &v);
+
+ int error = FT_Load_Glyph(fds->face, p_index, FT_HAS_COLOR(fds->face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting);
+ if (error) {
+ fds->glyph_map[p_index] = character;
+ return;
+ }
+
+ error = FT_Render_Glyph(fds->face->glyph, antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
+ if (!error) {
+ character = bitmap_to_character(fds, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0);
+ }
+
+ fds->glyph_map[p_index] = character;
+}
+
+void DynamicFontDataAdvanced::update_glyph_outline(int p_size, int p_outline_size, uint32_t p_index) {
+ DataAtSize *fds = get_data_for_size(p_size, p_outline_size);
+ ERR_FAIL_COND(fds == nullptr);
+
+ if (fds->glyph_map.has(p_index)) {
+ return;
+ }
+
+ Character character = Character::not_found();
+ if (p_index == 0) {
+ fds->glyph_map[p_index] = character;
+ return;
+ }
+
+ int error = FT_Load_Glyph(fds->face, p_index, FT_LOAD_NO_BITMAP | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0));
+ if (error) {
+ fds->glyph_map[p_index] = character;
+ return;
+ }
+
+ FT_Stroker stroker;
+ if (FT_Stroker_New(library, &stroker) != 0) {
+ fds->glyph_map[p_index] = character;
+ return;
+ }
+
+ FT_Stroker_Set(stroker, (int)(p_outline_size * oversampling * 64.0), FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0);
+ FT_Glyph glyph;
+ FT_BitmapGlyph glyph_bitmap;
+
+ if (FT_Get_Glyph(fds->face->glyph, &glyph) != 0) {
+ goto cleanup_stroker;
+ }
+ if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) {
+ goto cleanup_glyph;
+ }
+ if (FT_Glyph_To_Bitmap(&glyph, antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1) != 0) {
+ goto cleanup_glyph;
+ }
+
+ glyph_bitmap = (FT_BitmapGlyph)glyph;
+ character = bitmap_to_character(fds, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2());
+
+cleanup_glyph:
+ FT_Done_Glyph(glyph);
+cleanup_stroker:
+ FT_Stroker_Done(stroker);
+
+ fds->glyph_map[p_index] = character;
+}
+
+void DynamicFontDataAdvanced::clear_cache() {
+ _THREAD_SAFE_METHOD_
+ for (Map<CacheID, DataAtSize *>::Element *E = size_cache.front(); E; E = E->next()) {
+ memdelete(E->get());
+ }
+ size_cache.clear();
+ for (Map<CacheID, DataAtSize *>::Element *E = size_cache_outline.front(); E; E = E->next()) {
+ memdelete(E->get());
+ }
+ size_cache_outline.clear();
+}
+
+Error DynamicFontDataAdvanced::load_from_file(const String &p_filename, int p_base_size) {
+ _THREAD_SAFE_METHOD_
+ if (library == nullptr) {
+ int error = FT_Init_FreeType(&library);
+ ERR_FAIL_COND_V_MSG(error != 0, ERR_CANT_CREATE, "Error initializing FreeType.");
+ }
+ clear_cache();
+
+ font_path = p_filename;
+ base_size = p_base_size;
+
+ valid = true;
+ DataAtSize *fds = get_data_for_size(base_size); // load base size.
+ if (fds == nullptr) {
+ valid = false;
+ ERR_FAIL_V(ERR_CANT_CREATE);
+ }
+
+ return OK;
+}
+
+Error DynamicFontDataAdvanced::load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) {
+ _THREAD_SAFE_METHOD_
+ if (library == nullptr) {
+ int error = FT_Init_FreeType(&library);
+ ERR_FAIL_COND_V_MSG(error != 0, ERR_CANT_CREATE, "Error initializing FreeType.");
+ }
+ clear_cache();
+
+ font_mem = p_data;
+ font_mem_size = p_size;
+ base_size = p_base_size;
+
+ valid = true;
+ DataAtSize *fds = get_data_for_size(base_size); // load base size.
+ if (fds == nullptr) {
+ valid = false;
+ ERR_FAIL_V(ERR_CANT_CREATE);
+ }
+
+ return OK;
+}
+
+float DynamicFontDataAdvanced::get_height(int p_size) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size);
+ ERR_FAIL_COND_V(fds == nullptr, 0.f);
+ return fds->ascent + fds->descent;
+}
+
+float DynamicFontDataAdvanced::get_ascent(int p_size) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size);
+ ERR_FAIL_COND_V(fds == nullptr, 0.f);
+ return fds->ascent;
+}
+
+float DynamicFontDataAdvanced::get_descent(int p_size) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size);
+ ERR_FAIL_COND_V(fds == nullptr, 0.f);
+ return fds->descent;
+}
+
+float DynamicFontDataAdvanced::get_underline_position(int p_size) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size);
+ ERR_FAIL_COND_V(fds == nullptr, 0.f);
+ return fds->underline_position;
+}
+
+float DynamicFontDataAdvanced::get_underline_thickness(int p_size) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size);
+ ERR_FAIL_COND_V(fds == nullptr, 0.f);
+ return fds->underline_thickness;
+}
+
+bool DynamicFontDataAdvanced::is_script_supported(uint32_t p_script) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(base_size);
+ ERR_FAIL_COND_V(fds == nullptr, false);
+
+ unsigned int count = hb_ot_layout_table_get_script_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GSUB, 0, NULL, NULL);
+ if (count != 0) {
+ hb_tag_t *script_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
+ hb_ot_layout_table_get_script_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GSUB, 0, &count, script_tags);
+ for (unsigned int i = 0; i < count; i++) {
+ if (p_script == script_tags[i]) {
+ memfree(script_tags);
+ return true;
+ }
+ }
+ memfree(script_tags);
+ }
+ count = hb_ot_layout_table_get_script_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GPOS, 0, NULL, NULL);
+ if (count != 0) {
+ hb_tag_t *script_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
+ hb_ot_layout_table_get_script_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GPOS, 0, &count, script_tags);
+ for (unsigned int i = 0; i < count; i++) {
+ if (p_script == script_tags[i]) {
+ memfree(script_tags);
+ return true;
+ }
+ }
+ memfree(script_tags);
+ }
+
+ if (!fds->os2) {
+ return false;
+ }
+
+ switch (p_script) {
+ case HB_SCRIPT_COMMON:
+ return (fds->os2->ulUnicodeRange1 & 1L << 4) || (fds->os2->ulUnicodeRange1 & 1L << 5) || (fds->os2->ulUnicodeRange1 & 1L << 6) || (fds->os2->ulUnicodeRange1 & 1L << 31) || (fds->os2->ulUnicodeRange2 & 1L << 0) || (fds->os2->ulUnicodeRange2 & 1L << 1) || (fds->os2->ulUnicodeRange2 & 1L << 2) || (fds->os2->ulUnicodeRange2 & 1L << 3) || (fds->os2->ulUnicodeRange2 & 1L << 4) || (fds->os2->ulUnicodeRange2 & 1L << 5) || (fds->os2->ulUnicodeRange2 & 1L << 6) || (fds->os2->ulUnicodeRange2 & 1L << 7) || (fds->os2->ulUnicodeRange2 & 1L << 8) || (fds->os2->ulUnicodeRange2 & 1L << 9) || (fds->os2->ulUnicodeRange2 & 1L << 10) || (fds->os2->ulUnicodeRange2 & 1L << 11) || (fds->os2->ulUnicodeRange2 & 1L << 12) || (fds->os2->ulUnicodeRange2 & 1L << 13) || (fds->os2->ulUnicodeRange2 & 1L << 14) || (fds->os2->ulUnicodeRange2 & 1L << 15) || (fds->os2->ulUnicodeRange2 & 1L << 30) || (fds->os2->ulUnicodeRange3 & 1L << 0) || (fds->os2->ulUnicodeRange3 & 1L << 1) || (fds->os2->ulUnicodeRange3 & 1L << 2) || (fds->os2->ulUnicodeRange3 & 1L << 4) || (fds->os2->ulUnicodeRange3 & 1L << 5) || (fds->os2->ulUnicodeRange3 & 1L << 18) || (fds->os2->ulUnicodeRange3 & 1L << 24) || (fds->os2->ulUnicodeRange3 & 1L << 25) || (fds->os2->ulUnicodeRange3 & 1L << 26) || (fds->os2->ulUnicodeRange3 & 1L << 27) || (fds->os2->ulUnicodeRange3 & 1L << 28) || (fds->os2->ulUnicodeRange4 & 1L << 3) || (fds->os2->ulUnicodeRange4 & 1L << 6) || (fds->os2->ulUnicodeRange4 & 1L << 15) || (fds->os2->ulUnicodeRange4 & 1L << 23) || (fds->os2->ulUnicodeRange4 & 1L << 24) || (fds->os2->ulUnicodeRange4 & 1L << 26);
+ case HB_SCRIPT_LATIN:
+ return (fds->os2->ulUnicodeRange1 & 1L << 0) || (fds->os2->ulUnicodeRange1 & 1L << 1) || (fds->os2->ulUnicodeRange1 & 1L << 2) || (fds->os2->ulUnicodeRange1 & 1L << 3) || (fds->os2->ulUnicodeRange1 & 1L << 29);
+ case HB_SCRIPT_GREEK:
+ return (fds->os2->ulUnicodeRange1 & 1L << 7) || (fds->os2->ulUnicodeRange1 & 1L << 30);
+ case HB_SCRIPT_COPTIC:
+ return (fds->os2->ulUnicodeRange1 & 1L << 8);
+ case HB_SCRIPT_CYRILLIC:
+ return (fds->os2->ulUnicodeRange1 & 1L << 9);
+ case HB_SCRIPT_ARMENIAN:
+ return (fds->os2->ulUnicodeRange1 & 1L << 10);
+ case HB_SCRIPT_HEBREW:
+ return (fds->os2->ulUnicodeRange1 & 1L << 11);
+ case HB_SCRIPT_VAI:
+ return (fds->os2->ulUnicodeRange1 & 1L << 12);
+ case HB_SCRIPT_ARABIC:
+ return (fds->os2->ulUnicodeRange1 & 1L << 13) || (fds->os2->ulUnicodeRange2 & 1L << 31) || (fds->os2->ulUnicodeRange3 & 1L << 3);
+ case HB_SCRIPT_NKO:
+ return (fds->os2->ulUnicodeRange1 & 1L << 14);
+ case HB_SCRIPT_DEVANAGARI:
+ return (fds->os2->ulUnicodeRange1 & 1L << 15);
+ case HB_SCRIPT_BENGALI:
+ return (fds->os2->ulUnicodeRange1 & 1L << 16);
+ case HB_SCRIPT_GURMUKHI:
+ return (fds->os2->ulUnicodeRange1 & 1L << 17);
+ case HB_SCRIPT_GUJARATI:
+ return (fds->os2->ulUnicodeRange1 & 1L << 18);
+ case HB_SCRIPT_ORIYA:
+ return (fds->os2->ulUnicodeRange1 & 1L << 19);
+ case HB_SCRIPT_TAMIL:
+ return (fds->os2->ulUnicodeRange1 & 1L << 20);
+ case HB_SCRIPT_TELUGU:
+ return (fds->os2->ulUnicodeRange1 & 1L << 21);
+ case HB_SCRIPT_KANNADA:
+ return (fds->os2->ulUnicodeRange1 & 1L << 22);
+ case HB_SCRIPT_MALAYALAM:
+ return (fds->os2->ulUnicodeRange1 & 1L << 23);
+ case HB_SCRIPT_THAI:
+ return (fds->os2->ulUnicodeRange1 & 1L << 24);
+ case HB_SCRIPT_LAO:
+ return (fds->os2->ulUnicodeRange1 & 1L << 25);
+ case HB_SCRIPT_GEORGIAN:
+ return (fds->os2->ulUnicodeRange1 & 1L << 26);
+ case HB_SCRIPT_BALINESE:
+ return (fds->os2->ulUnicodeRange1 & 1L << 27);
+ case HB_SCRIPT_HANGUL:
+ return (fds->os2->ulUnicodeRange1 & 1L << 28) || (fds->os2->ulUnicodeRange2 & 1L << 20) || (fds->os2->ulUnicodeRange2 & 1L << 24);
+ case HB_SCRIPT_HAN:
+ return (fds->os2->ulUnicodeRange2 & 1L << 21) || (fds->os2->ulUnicodeRange2 & 1L << 22) || (fds->os2->ulUnicodeRange2 & 1L << 23) || (fds->os2->ulUnicodeRange2 & 1L << 26) || (fds->os2->ulUnicodeRange2 & 1L << 27) || (fds->os2->ulUnicodeRange2 & 1L << 29);
+ case HB_SCRIPT_HIRAGANA:
+ return (fds->os2->ulUnicodeRange2 & 1L << 17);
+ case HB_SCRIPT_KATAKANA:
+ return (fds->os2->ulUnicodeRange2 & 1L << 18);
+ case HB_SCRIPT_BOPOMOFO:
+ return (fds->os2->ulUnicodeRange2 & 1L << 19);
+ case HB_SCRIPT_TIBETAN:
+ return (fds->os2->ulUnicodeRange3 & 1L << 6);
+ case HB_SCRIPT_SYRIAC:
+ return (fds->os2->ulUnicodeRange3 & 1L << 7);
+ case HB_SCRIPT_THAANA:
+ return (fds->os2->ulUnicodeRange3 & 1L << 8);
+ case HB_SCRIPT_SINHALA:
+ return (fds->os2->ulUnicodeRange3 & 1L << 9);
+ case HB_SCRIPT_MYANMAR:
+ return (fds->os2->ulUnicodeRange3 & 1L << 10);
+ case HB_SCRIPT_ETHIOPIC:
+ return (fds->os2->ulUnicodeRange3 & 1L << 11);
+ case HB_SCRIPT_CHEROKEE:
+ return (fds->os2->ulUnicodeRange3 & 1L << 12);
+ case HB_SCRIPT_CANADIAN_SYLLABICS:
+ return (fds->os2->ulUnicodeRange3 & 1L << 13);
+ case HB_SCRIPT_OGHAM:
+ return (fds->os2->ulUnicodeRange3 & 1L << 14);
+ case HB_SCRIPT_RUNIC:
+ return (fds->os2->ulUnicodeRange3 & 1L << 15);
+ case HB_SCRIPT_KHMER:
+ return (fds->os2->ulUnicodeRange3 & 1L << 16);
+ case HB_SCRIPT_MONGOLIAN:
+ return (fds->os2->ulUnicodeRange3 & 1L << 17);
+ case HB_SCRIPT_YI:
+ return (fds->os2->ulUnicodeRange3 & 1L << 19);
+ case HB_SCRIPT_HANUNOO:
+ case HB_SCRIPT_TAGBANWA:
+ case HB_SCRIPT_BUHID:
+ case HB_SCRIPT_TAGALOG:
+ return (fds->os2->ulUnicodeRange3 & 1L << 20);
+ case HB_SCRIPT_OLD_ITALIC:
+ return (fds->os2->ulUnicodeRange3 & 1L << 21);
+ case HB_SCRIPT_GOTHIC:
+ return (fds->os2->ulUnicodeRange3 & 1L << 22);
+ case HB_SCRIPT_DESERET:
+ return (fds->os2->ulUnicodeRange3 & 1L << 23);
+ case HB_SCRIPT_LIMBU:
+ return (fds->os2->ulUnicodeRange3 & 1L << 29);
+ case HB_SCRIPT_TAI_LE:
+ return (fds->os2->ulUnicodeRange3 & 1L << 30);
+ case HB_SCRIPT_NEW_TAI_LUE:
+ return (fds->os2->ulUnicodeRange3 & 1L << 31);
+ case HB_SCRIPT_BUGINESE:
+ return (fds->os2->ulUnicodeRange4 & 1L << 0);
+ case HB_SCRIPT_GLAGOLITIC:
+ return (fds->os2->ulUnicodeRange4 & 1L << 1);
+ case HB_SCRIPT_TIFINAGH:
+ return (fds->os2->ulUnicodeRange4 & 1L << 2);
+ case HB_SCRIPT_SYLOTI_NAGRI:
+ return (fds->os2->ulUnicodeRange4 & 1L << 4);
+ case HB_SCRIPT_LINEAR_B:
+ return (fds->os2->ulUnicodeRange4 & 1L << 5);
+ case HB_SCRIPT_UGARITIC:
+ return (fds->os2->ulUnicodeRange4 & 1L << 7);
+ case HB_SCRIPT_OLD_PERSIAN:
+ return (fds->os2->ulUnicodeRange4 & 1L << 8);
+ case HB_SCRIPT_SHAVIAN:
+ return (fds->os2->ulUnicodeRange4 & 1L << 9);
+ case HB_SCRIPT_OSMANYA:
+ return (fds->os2->ulUnicodeRange4 & 1L << 10);
+ case HB_SCRIPT_CYPRIOT:
+ return (fds->os2->ulUnicodeRange4 & 1L << 11);
+ case HB_SCRIPT_KHAROSHTHI:
+ return (fds->os2->ulUnicodeRange4 & 1L << 12);
+ case HB_SCRIPT_TAI_VIET:
+ return (fds->os2->ulUnicodeRange4 & 1L << 13);
+ case HB_SCRIPT_CUNEIFORM:
+ return (fds->os2->ulUnicodeRange4 & 1L << 14);
+ case HB_SCRIPT_SUNDANESE:
+ return (fds->os2->ulUnicodeRange4 & 1L << 16);
+ case HB_SCRIPT_LEPCHA:
+ return (fds->os2->ulUnicodeRange4 & 1L << 17);
+ case HB_SCRIPT_OL_CHIKI:
+ return (fds->os2->ulUnicodeRange4 & 1L << 18);
+ case HB_SCRIPT_SAURASHTRA:
+ return (fds->os2->ulUnicodeRange4 & 1L << 19);
+ case HB_SCRIPT_KAYAH_LI:
+ return (fds->os2->ulUnicodeRange4 & 1L << 20);
+ case HB_SCRIPT_REJANG:
+ return (fds->os2->ulUnicodeRange4 & 1L << 21);
+ case HB_SCRIPT_CHAM:
+ return (fds->os2->ulUnicodeRange4 & 1L << 22);
+ case HB_SCRIPT_ANATOLIAN_HIEROGLYPHS:
+ return (fds->os2->ulUnicodeRange4 & 1L << 25);
+ default:
+ return false;
+ };
+}
+
+void DynamicFontDataAdvanced::set_antialiased(bool p_antialiased) {
+ if (antialiased != p_antialiased) {
+ clear_cache();
+ antialiased = p_antialiased;
+ }
+}
+
+bool DynamicFontDataAdvanced::get_antialiased() const {
+ return antialiased;
+}
+
+void DynamicFontDataAdvanced::set_force_autohinter(bool p_enabled) {
+ if (force_autohinter != p_enabled) {
+ clear_cache();
+ force_autohinter = p_enabled;
+ }
+}
+
+bool DynamicFontDataAdvanced::get_force_autohinter() const {
+ return force_autohinter;
+}
+
+void DynamicFontDataAdvanced::set_hinting(TextServer::Hinting p_hinting) {
+ if (hinting != p_hinting) {
+ clear_cache();
+ hinting = p_hinting;
+ }
+}
+
+TextServer::Hinting DynamicFontDataAdvanced::get_hinting() const {
+ return hinting;
+}
+
+bool DynamicFontDataAdvanced::has_outline() const {
+ return true;
+}
+
+float DynamicFontDataAdvanced::get_base_size() const {
+ return base_size;
+}
+
+String DynamicFontDataAdvanced::get_supported_chars() const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(base_size);
+ ERR_FAIL_COND_V(fds == nullptr, String());
+
+ String chars;
+
+ FT_UInt gindex;
+ FT_ULong charcode = FT_Get_First_Char(fds->face, &gindex);
+ while (gindex != 0) {
+ if (charcode != 0) {
+ chars += char32_t(charcode);
+ }
+ charcode = FT_Get_Next_Char(fds->face, charcode, &gindex);
+ }
+
+ return chars;
+}
+
+float DynamicFontDataAdvanced::get_font_scale(int p_size) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size);
+ ERR_FAIL_COND_V(fds == nullptr, 1.0f);
+
+ return fds->scale_color_font / oversampling;
+}
+
+bool DynamicFontDataAdvanced::has_char(char32_t p_char) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(base_size);
+ ERR_FAIL_COND_V(fds == nullptr, false);
+
+ const_cast<DynamicFontDataAdvanced *>(this)->update_glyph(base_size, FT_Get_Char_Index(fds->face, p_char));
+ Character ch = fds->glyph_map[FT_Get_Char_Index(fds->face, p_char)];
+
+ return (ch.found);
+}
+
+hb_font_t *DynamicFontDataAdvanced::get_hb_handle(int p_size) {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = get_data_for_size(p_size);
+ ERR_FAIL_COND_V(fds == nullptr, nullptr);
+
+ return fds->hb_handle;
+}
+
+uint32_t DynamicFontDataAdvanced::get_glyph_index(char32_t p_char, char32_t p_variation_selector) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(base_size);
+ ERR_FAIL_COND_V(fds == nullptr, 0);
+
+ if (p_variation_selector == 0x0000) {
+ return FT_Get_Char_Index(fds->face, p_char);
+ } else {
+ return FT_Face_GetCharVariantIndex(fds->face, p_char, p_variation_selector);
+ }
+}
+
+Vector2 DynamicFontDataAdvanced::get_advance(uint32_t p_index, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size);
+ ERR_FAIL_COND_V(fds == nullptr, Vector2());
+
+ const_cast<DynamicFontDataAdvanced *>(this)->update_glyph(p_size, p_index);
+ Character ch = fds->glyph_map[p_index];
+
+ return ch.advance;
+}
+
+Vector2 DynamicFontDataAdvanced::get_kerning(uint32_t p_char, uint32_t p_next, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size);
+ ERR_FAIL_COND_V(fds == nullptr, Vector2());
+
+ FT_Vector delta;
+ FT_Get_Kerning(fds->face, p_char, p_next, FT_KERNING_DEFAULT, &delta);
+ return Vector2(delta.x, delta.y);
+}
+
+Vector2 DynamicFontDataAdvanced::draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size);
+ ERR_FAIL_COND_V(fds == nullptr, Vector2());
+
+ const_cast<DynamicFontDataAdvanced *>(this)->update_glyph(p_size, p_index);
+ Character ch = fds->glyph_map[p_index];
+
+ Vector2 advance;
+ if (ch.found) {
+ ERR_FAIL_COND_V(ch.texture_idx < -1 || ch.texture_idx >= fds->textures.size(), Vector2());
+
+ if (ch.texture_idx != -1) {
+ Point2 cpos = p_pos;
+ cpos += ch.align;
+ Color modulate = p_color;
+ if (FT_HAS_COLOR(fds->face)) {
+ modulate.r = modulate.g = modulate.b = 1.0;
+ }
+ if (RenderingServer::get_singleton() != nullptr) {
+ RID texture = fds->textures[ch.texture_idx].texture->get_rid();
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, ch.rect.size), texture, ch.rect_uv, modulate, false, false);
+ }
+ }
+
+ advance = ch.advance;
+ }
+
+ return advance;
+}
+
+Vector2 DynamicFontDataAdvanced::draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size, p_outline_size);
+ ERR_FAIL_COND_V(fds == nullptr, Vector2());
+
+ const_cast<DynamicFontDataAdvanced *>(this)->update_glyph_outline(p_size, p_outline_size, p_index);
+ Character ch = fds->glyph_map[p_index];
+
+ Vector2 advance;
+ if (ch.found) {
+ ERR_FAIL_COND_V(ch.texture_idx < -1 || ch.texture_idx >= fds->textures.size(), Vector2());
+
+ if (ch.texture_idx != -1) {
+ Point2 cpos = p_pos;
+ cpos += ch.align;
+ Color modulate = p_color;
+ if (FT_HAS_COLOR(fds->face)) {
+ modulate.r = modulate.g = modulate.b = 1.0;
+ }
+ if (RenderingServer::get_singleton() != nullptr) {
+ RID texture = fds->textures[ch.texture_idx].texture->get_rid();
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, ch.rect.size), texture, ch.rect_uv, modulate, false, false);
+ }
+ }
+
+ advance = ch.advance;
+ }
+
+ return advance;
+}
+
+DynamicFontDataAdvanced::~DynamicFontDataAdvanced() {
+ clear_cache();
+ if (library != nullptr) {
+ FT_Done_FreeType(library);
+ }
+}
diff --git a/modules/text_server_adv/dynamic_font_adv.h b/modules/text_server_adv/dynamic_font_adv.h
new file mode 100644
index 0000000000..f9d6735c32
--- /dev/null
+++ b/modules/text_server_adv/dynamic_font_adv.h
@@ -0,0 +1,191 @@
+/*************************************************************************/
+/* dynamic_font_adv.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef DYNAMIC_FONT_ADV_H
+#define DYNAMIC_FONT_ADV_H
+
+#include "font_adv.h"
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_TRUETYPE_TABLES_H
+
+#include <hb-ft.h>
+#include <hb-ot.h>
+
+struct DynamicFontDataAdvanced : public FontDataAdvanced {
+ _THREAD_SAFE_CLASS_
+
+private:
+ struct CharTexture {
+ Vector<uint8_t> imgdata;
+ int texture_size = 0;
+ Vector<int> offsets;
+ Ref<ImageTexture> texture;
+ };
+
+ struct Character {
+ bool found = false;
+ int texture_idx = 0;
+ Rect2 rect;
+ Rect2 rect_uv;
+ Vector2 align;
+ Vector2 advance = Vector2(-1, -1);
+
+ static Character not_found();
+ };
+
+ struct TexturePosition {
+ int index = 0;
+ int x = 0;
+ int y = 0;
+ };
+
+ struct CacheID {
+ union {
+ struct {
+ uint32_t size : 16;
+ uint32_t outline_size : 16;
+ };
+ uint32_t key;
+ };
+ bool operator<(CacheID right) const {
+ return key < right.key;
+ }
+ CacheID() {
+ key = 0;
+ }
+ };
+
+ struct DataAtSize {
+ FT_Face face = nullptr;
+ TT_OS2 *os2 = nullptr;
+ FT_StreamRec stream;
+
+ int size = 0;
+ float scale_color_font = 1.f;
+ float ascent = 0;
+ float descent = 0;
+ float underline_position = 0;
+ float underline_thickness = 0;
+
+ Vector<CharTexture> textures;
+ HashMap<uint32_t, Character> glyph_map;
+
+ hb_font_t *hb_handle = nullptr;
+ ~DataAtSize() {
+ if (hb_handle != nullptr) {
+ hb_font_destroy(hb_handle);
+ }
+ if (face != nullptr) {
+ FT_Done_Face(face);
+ }
+ }
+ };
+
+ FT_Library library = nullptr;
+
+ // Source data.
+ const uint8_t *font_mem = nullptr;
+ int font_mem_size = 0;
+ String font_path;
+ Vector<uint8_t> font_mem_cache;
+
+ Map<int32_t, double> variations;
+
+ float rect_margin = 1.f;
+ int base_size = 16;
+ float oversampling = 1.f;
+ bool antialiased = true;
+ bool force_autohinter = false;
+ TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
+
+ Map<CacheID, DataAtSize *> size_cache;
+ Map<CacheID, DataAtSize *> size_cache_outline;
+
+ DataAtSize *get_data_for_size(int p_size, int p_outline_size = 0);
+
+ TexturePosition find_texture_pos_for_glyph(DataAtSize *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height);
+ Character bitmap_to_character(DataAtSize *p_data, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance);
+ _FORCE_INLINE_ void update_glyph(int p_size, uint32_t p_index);
+ _FORCE_INLINE_ void update_glyph_outline(int p_size, int p_outline_size, uint32_t p_index);
+
+public:
+ virtual void clear_cache() override;
+
+ virtual Error load_from_file(const String &p_filename, int p_base_size) override;
+ virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) override;
+
+ virtual float get_height(int p_size) const override;
+ virtual float get_ascent(int p_size) const override;
+ virtual float get_descent(int p_size) const override;
+
+ virtual Dictionary get_feature_list() const override;
+ virtual Dictionary get_variation_list() const override;
+
+ virtual void set_variation(const String &p_name, double p_value) override;
+ virtual double get_variation(const String &p_name) const override;
+
+ virtual float get_underline_position(int p_size) const override;
+ virtual float get_underline_thickness(int p_size) const override;
+
+ virtual void set_antialiased(bool p_antialiased) override;
+ virtual bool get_antialiased() const override;
+
+ virtual void set_hinting(TextServer::Hinting p_hinting) override;
+ virtual TextServer::Hinting get_hinting() const override;
+
+ virtual void set_force_autohinter(bool p_enabled) override;
+ virtual bool get_force_autohinter() const override;
+
+ virtual void set_distance_field_hint(bool p_distance_field) override{};
+ virtual bool get_distance_field_hint() const override { return false; };
+
+ virtual bool has_outline() const override;
+ virtual float get_base_size() const override;
+
+ virtual bool is_script_supported(uint32_t p_script) const override;
+
+ virtual bool has_char(char32_t p_char) const override;
+ virtual String get_supported_chars() const override;
+ virtual float get_font_scale(int p_size) const override;
+
+ virtual hb_font_t *get_hb_handle(int p_size) override;
+ virtual uint32_t get_glyph_index(char32_t p_char, char32_t p_variation_selector) const override;
+ virtual Vector2 get_advance(uint32_t p_index, int p_size) const override;
+ virtual Vector2 get_kerning(uint32_t p_char, uint32_t p_next, int p_size) const override;
+
+ virtual Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override;
+ virtual Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override;
+
+ virtual ~DynamicFontDataAdvanced() override;
+};
+
+#endif // DYNAMIC_FONT_ADV_H
diff --git a/modules/text_server_adv/font_adv.h b/modules/text_server_adv/font_adv.h
new file mode 100644
index 0000000000..88b327f57b
--- /dev/null
+++ b/modules/text_server_adv/font_adv.h
@@ -0,0 +1,94 @@
+/*************************************************************************/
+/* font_adv.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef FONT_ADV_H
+#define FONT_ADV_H
+
+#include "servers/text_server.h"
+
+#include <hb.h>
+
+struct FontDataAdvanced {
+ Map<String, bool> lang_support_overrides;
+ Map<String, bool> script_support_overrides;
+ bool valid = false;
+
+ virtual void clear_cache() = 0;
+
+ virtual Error load_from_file(const String &p_filename, int p_base_size) = 0;
+ virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) = 0;
+
+ virtual float get_height(int p_size) const = 0;
+ virtual float get_ascent(int p_size) const = 0;
+ virtual float get_descent(int p_size) const = 0;
+
+ virtual Dictionary get_feature_list() const { return Dictionary(); };
+ virtual Dictionary get_variation_list() const { return Dictionary(); };
+
+ virtual void set_variation(const String &p_name, double p_value){};
+ virtual double get_variation(const String &p_name) const { return 0; };
+
+ virtual float get_underline_position(int p_size) const = 0;
+ virtual float get_underline_thickness(int p_size) const = 0;
+
+ virtual void set_antialiased(bool p_antialiased) = 0;
+ virtual bool get_antialiased() const = 0;
+
+ virtual void set_hinting(TextServer::Hinting p_hinting) = 0;
+ virtual TextServer::Hinting get_hinting() const = 0;
+
+ virtual void set_distance_field_hint(bool p_distance_field) = 0;
+ virtual bool get_distance_field_hint() const = 0;
+
+ virtual void set_force_autohinter(bool p_enabeld) = 0;
+ virtual bool get_force_autohinter() const = 0;
+
+ virtual bool has_outline() const = 0;
+ virtual float get_base_size() const = 0;
+
+ virtual bool is_lang_supported(const String &p_lang) const { return false; };
+ virtual bool is_script_supported(uint32_t p_script) const { return false; };
+
+ virtual bool has_char(char32_t p_char) const = 0;
+ virtual String get_supported_chars() const = 0;
+ virtual float get_font_scale(int p_size) const { return 1.0f; };
+
+ virtual hb_font_t *get_hb_handle(int p_size) = 0;
+ virtual uint32_t get_glyph_index(char32_t p_char, char32_t p_variation_selector) const = 0;
+ virtual Vector2 get_advance(uint32_t p_char, int p_size) const = 0;
+ virtual Vector2 get_kerning(uint32_t p_char, uint32_t p_next, int p_size) const = 0;
+
+ virtual Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const = 0;
+ virtual Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const = 0;
+
+ virtual ~FontDataAdvanced(){};
+};
+
+#endif // FONT_ADV_H
diff --git a/modules/text_server_adv/icu_data/icudata_stub.cpp b/modules/text_server_adv/icu_data/icudata_stub.cpp
new file mode 100644
index 0000000000..13f0ac0c50
--- /dev/null
+++ b/modules/text_server_adv/icu_data/icudata_stub.cpp
@@ -0,0 +1,63 @@
+/*************************************************************************/
+/* icudata_stub.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "unicode/udata.h"
+#include "unicode/utypes.h"
+#include "unicode/uversion.h"
+
+typedef struct {
+ uint16_t header_size;
+ uint8_t magic_1, magic_2;
+ UDataInfo info;
+ char padding[8];
+ uint32_t count, reserved;
+ int fake_name_and_data[4];
+} ICU_data_header;
+
+extern "C" U_EXPORT const ICU_data_header U_ICUDATA_ENTRY_POINT = {
+ 32,
+ 0xDA, 0x27,
+ { sizeof(UDataInfo),
+ 0,
+#if U_IS_BIG_ENDIAN
+ 1,
+#else
+ 0,
+#endif
+ U_CHARSET_FAMILY,
+ sizeof(UChar),
+ 0,
+ { 0x54, 0x6F, 0x43, 0x50 },
+ { 1, 0, 0, 0 },
+ { 0, 0, 0, 0 } },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ 0, 0,
+ { 0, 0, 0, 0 }
+};
diff --git a/modules/text_server_adv/register_types.cpp b/modules/text_server_adv/register_types.cpp
new file mode 100644
index 0000000000..68117e0380
--- /dev/null
+++ b/modules/text_server_adv/register_types.cpp
@@ -0,0 +1,43 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "register_types.h"
+
+#include "text_server_adv.h"
+
+void preregister_text_server_adv_types() {
+ TextServerAdvanced::register_server();
+}
+
+void register_text_server_adv_types() {
+}
+
+void unregister_text_server_adv_types() {
+}
diff --git a/modules/text_server_adv/register_types.h b/modules/text_server_adv/register_types.h
new file mode 100644
index 0000000000..8319ddfd5d
--- /dev/null
+++ b/modules/text_server_adv/register_types.h
@@ -0,0 +1,40 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TEXT_SERVER_ADV_REGISTER_TYPES_H
+#define TEXT_SERVER_ADV_REGISTER_TYPES_H
+
+#define MODULE_TEXT_SERVER_ADV_HAS_PREREGISTER
+
+void preregister_text_server_adv_types();
+void register_text_server_adv_types();
+void unregister_text_server_adv_types();
+
+#endif // TEXT_SERVER_ADV_REGISTER_TYPES_H
diff --git a/modules/text_server_adv/script_iterator.cpp b/modules/text_server_adv/script_iterator.cpp
new file mode 100644
index 0000000000..60a617c3a7
--- /dev/null
+++ b/modules/text_server_adv/script_iterator.cpp
@@ -0,0 +1,110 @@
+/*************************************************************************/
+/* script_iterator.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "script_iterator.h"
+
+bool ScriptIterator::same_script(int32_t p_script_one, int32_t p_script_two) {
+ return p_script_one <= USCRIPT_INHERITED || p_script_two <= USCRIPT_INHERITED || p_script_one == p_script_two;
+}
+
+ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length) {
+ struct ParenStackEntry {
+ int pair_index;
+ UScriptCode script_code;
+ };
+
+ if (p_start >= p_length) {
+ p_start = p_length - 1;
+ }
+
+ if (p_start < 0) {
+ p_start = 0;
+ }
+
+ ParenStackEntry paren_stack[128];
+
+ int script_start;
+ int script_end = p_start;
+ UScriptCode script_code;
+ int paren_sp = -1;
+ int start_sp = paren_sp;
+ UErrorCode err = U_ZERO_ERROR;
+ const char32_t *str = p_string.ptr();
+
+ do {
+ script_code = USCRIPT_COMMON;
+ for (script_start = script_end; script_end < p_length; script_end++) {
+ UChar32 ch = str[script_end];
+ UScriptCode sc = uscript_getScript(ch, &err);
+ if (U_FAILURE(err)) {
+ ERR_FAIL_MSG(u_errorName(err));
+ }
+ if (u_getIntPropertyValue(ch, UCHAR_BIDI_PAIRED_BRACKET_TYPE) != U_BPT_NONE) {
+ if (u_getIntPropertyValue(ch, UCHAR_BIDI_PAIRED_BRACKET_TYPE) == U_BPT_OPEN) {
+ paren_stack[++paren_sp].pair_index = ch;
+ paren_stack[paren_sp].script_code = script_code;
+ } else if (paren_sp >= 0) {
+ UChar32 paired_ch = u_getBidiPairedBracket(ch);
+ while (paren_sp >= 0 && paren_stack[paren_sp].pair_index != paired_ch) {
+ paren_sp -= 1;
+ }
+ if (paren_sp < start_sp)
+ start_sp = paren_sp;
+ if (paren_sp >= 0)
+ sc = paren_stack[paren_sp].script_code;
+ }
+ }
+
+ if (same_script(script_code, sc)) {
+ if (script_code <= USCRIPT_INHERITED && sc > USCRIPT_INHERITED) {
+ script_code = sc;
+ while (start_sp < paren_sp) {
+ paren_stack[++start_sp].script_code = script_code;
+ }
+ }
+ if ((u_getIntPropertyValue(ch, UCHAR_BIDI_PAIRED_BRACKET_TYPE) == U_BPT_CLOSE) && paren_sp >= 0) {
+ paren_sp -= 1;
+ if (start_sp >= 0) {
+ start_sp -= 1;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+
+ ScriptRange rng;
+ rng.script = hb_icu_script_to_script(script_code);
+ rng.start = script_start;
+ rng.end = script_end;
+
+ script_ranges.push_back(rng);
+ } while (script_end < p_length);
+}
diff --git a/modules/text_server_adv/script_iterator.h b/modules/text_server_adv/script_iterator.h
new file mode 100644
index 0000000000..4523aa2767
--- /dev/null
+++ b/modules/text_server_adv/script_iterator.h
@@ -0,0 +1,61 @@
+/*************************************************************************/
+/* script_iterator.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SCRIPT_ITERATOR_H
+#define SCRIPT_ITERATOR_H
+
+#include "servers/text_server.h"
+
+#include <unicode/uchar.h>
+#include <unicode/uloc.h>
+#include <unicode/uscript.h>
+#include <unicode/ustring.h>
+#include <unicode/utypes.h>
+
+#include <hb-icu.h>
+#include <hb.h>
+
+class ScriptIterator {
+public:
+ struct ScriptRange {
+ int start;
+ int end;
+ hb_script_t script;
+ };
+ Vector<ScriptRange> script_ranges;
+
+private:
+ static bool same_script(int32_t p_script_one, int32_t p_script_two);
+
+public:
+ ScriptIterator(const String &p_string, int p_start, int p_length);
+};
+
+#endif //SCRIPT_ITERATOR_H
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
new file mode 100644
index 0000000000..3a706286e5
--- /dev/null
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -0,0 +1,2571 @@
+/*************************************************************************/
+/* text_server_adv.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "text_server_adv.h"
+#include "bitmap_font_adv.h"
+#include "dynamic_font_adv.h"
+
+#include "core/string/translation.h"
+
+#ifdef ICU_STATIC_DATA
+#include "thirdparty/icu4c/icudata.gen.h"
+#endif
+
+_FORCE_INLINE_ bool is_ain(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_AIN;
+}
+
+_FORCE_INLINE_ bool is_alef(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_ALEF;
+}
+
+_FORCE_INLINE_ bool is_beh(char32_t p_chr) {
+ int32_t prop = u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP);
+ return (prop == U_JG_BEH) || (prop == U_JG_NOON) || (prop == U_JG_AFRICAN_NOON) || (prop == U_JG_NYA) || (prop == U_JG_YEH) || (prop == U_JG_FARSI_YEH);
+}
+
+_FORCE_INLINE_ bool is_dal(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_DAL;
+}
+
+_FORCE_INLINE_ bool is_feh(char32_t p_chr) {
+ return (u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_FEH) || (u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_AFRICAN_FEH);
+}
+
+_FORCE_INLINE_ bool is_gaf(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_GAF;
+}
+
+_FORCE_INLINE_ bool is_heh(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_HEH;
+}
+
+_FORCE_INLINE_ bool is_kaf(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_KAF;
+}
+
+_FORCE_INLINE_ bool is_lam(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_LAM;
+}
+
+_FORCE_INLINE_ bool is_qaf(char32_t p_chr) {
+ return (u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_QAF) || (u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_AFRICAN_QAF);
+}
+
+_FORCE_INLINE_ bool is_reh(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_REH;
+}
+
+_FORCE_INLINE_ bool is_seen_sad(char32_t p_chr) {
+ return (u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_SAD) || (u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_SEEN);
+}
+
+_FORCE_INLINE_ bool is_tah(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_TAH;
+}
+
+_FORCE_INLINE_ bool is_teh_marbuta(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_TEH_MARBUTA;
+}
+
+_FORCE_INLINE_ bool is_yeh(char32_t p_chr) {
+ int32_t prop = u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP);
+ return (prop == U_JG_YEH) || (prop == U_JG_FARSI_YEH) || (prop == U_JG_YEH_BARREE) || (prop == U_JG_BURUSHASKI_YEH_BARREE) || (prop == U_JG_YEH_WITH_TAIL);
+}
+
+_FORCE_INLINE_ bool is_waw(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_WAW;
+}
+
+_FORCE_INLINE_ bool is_transparent(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_TYPE) == U_JT_TRANSPARENT;
+}
+
+_FORCE_INLINE_ bool is_ligature(char32_t p_chr, char32_t p_nchr) {
+ return (is_lam(p_chr) && is_alef(p_nchr));
+}
+
+_FORCE_INLINE_ bool is_connected_to_prev(char32_t p_chr, char32_t p_pchr) {
+ int32_t prop = u_getIntPropertyValue(p_pchr, UCHAR_JOINING_TYPE);
+ return (prop != U_JT_RIGHT_JOINING) && (prop != U_JT_NON_JOINING) ? !is_ligature(p_pchr, p_chr) : false;
+}
+
+_FORCE_INLINE_ bool is_control(char32_t p_char) {
+ return (p_char <= 0x001f) || (p_char >= 0x007f && p_char <= 0x009F);
+}
+
+_FORCE_INLINE_ bool is_whitespace(char32_t p_char) {
+ return (p_char == 0x0020) || (p_char == 0x00A0) || (p_char == 0x1680) || (p_char >= 0x2000 && p_char <= 0x200a) || (p_char == 0x202f) || (p_char == 0x205f) || (p_char == 0x3000) || (p_char == 0x2028) || (p_char == 0x2029) || (p_char >= 0x0009 && p_char <= 0x000d) || (p_char == 0x0085);
+}
+
+_FORCE_INLINE_ bool is_linebreak(char32_t p_char) {
+ return (p_char >= 0x000a && p_char <= 0x000d) || (p_char == 0x0085) || (p_char == 0x2028) || (p_char == 0x2029);
+}
+
+/*************************************************************************/
+
+String TextServerAdvanced::interface_name = "ICU / HarfBuzz / Graphite";
+uint32_t TextServerAdvanced::interface_features = FEATURE_BIDI_LAYOUT | FEATURE_VERTICAL_LAYOUT | FEATURE_SHAPING | FEATURE_KASHIDA_JUSTIFICATION | FEATURE_BREAK_ITERATORS | FEATURE_USE_SUPPORT_DATA | FEATURE_FONT_VARIABLE;
+
+bool TextServerAdvanced::has_feature(Feature p_feature) {
+ return (interface_features & p_feature) == p_feature;
+}
+
+String TextServerAdvanced::get_name() const {
+ return interface_name;
+}
+
+void TextServerAdvanced::free(RID p_rid) {
+ _THREAD_SAFE_METHOD_
+ if (font_owner.owns(p_rid)) {
+ FontDataAdvanced *fd = font_owner.getornull(p_rid);
+ font_owner.free(p_rid);
+ memdelete(fd);
+ } else if (shaped_owner.owns(p_rid)) {
+ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_rid);
+ shaped_owner.free(p_rid);
+ memdelete(sd);
+ }
+}
+
+bool TextServerAdvanced::has(RID p_rid) {
+ _THREAD_SAFE_METHOD_
+ return font_owner.owns(p_rid) || shaped_owner.owns(p_rid);
+}
+
+bool TextServerAdvanced::load_support_data(const String &p_filename) {
+ _THREAD_SAFE_METHOD_
+
+#ifdef ICU_STATIC_DATA
+ if (icu_data == nullptr) {
+ UErrorCode err = U_ZERO_ERROR;
+ u_init(&err); // Do not check for errors, since we only load part the of data.
+ icu_data = (uint8_t *)&U_ICUDATA_ENTRY_POINT;
+ }
+#else
+ if (icu_data == nullptr) {
+ String filename = (p_filename.empty()) ? String("res://") + _MKSTR(ICU_DATA_NAME) : p_filename;
+
+ FileAccess *f = FileAccess::open(filename, FileAccess::READ);
+ if (!f) {
+ return false;
+ }
+
+ UErrorCode err = U_ZERO_ERROR;
+
+ // ICU data found.
+ size_t len = f->get_len();
+ icu_data = (uint8_t *)memalloc(len);
+ f->get_buffer(icu_data, len);
+ f->close();
+ memdelete(f);
+
+ udata_setCommonData(icu_data, &err);
+ if (U_FAILURE(err)) {
+ memfree(icu_data);
+ icu_data = nullptr;
+ ERR_FAIL_V_MSG(false, u_errorName(err));
+ }
+
+ err = U_ZERO_ERROR;
+ u_init(&err);
+ if (U_FAILURE(err)) {
+ memfree(icu_data);
+ icu_data = nullptr;
+ ERR_FAIL_V_MSG(false, u_errorName(err));
+ }
+ }
+#endif
+ return true;
+}
+
+#ifdef TOOLS_ENABLED
+
+bool TextServerAdvanced::save_support_data(const String &p_filename) {
+ _THREAD_SAFE_METHOD_
+#ifdef ICU_STATIC_DATA
+
+ // Store data to the res file if it's available.
+ FileAccess *f = FileAccess::open(p_filename, FileAccess::WRITE);
+ if (!f) {
+ return false;
+ }
+ f->store_buffer(U_ICUDATA_ENTRY_POINT, U_ICUDATA_SIZE);
+ f->close();
+ memdelete(f);
+ return true;
+
+#else
+ return false;
+#endif
+}
+
+#endif
+
+bool TextServerAdvanced::is_locale_right_to_left(const String &p_locale) {
+ String l = p_locale.get_slicec('_', 0);
+ if ((l == "ar") || (l == "dv") || (l == "he") || (l == "fa") || (l == "ff") || (l == "ku") || (l == "ur")) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+struct FeatureInfo {
+ int32_t tag;
+ String name;
+};
+
+static FeatureInfo feature_set[] = {
+ // Registred OpenType feature tags.
+ { HB_TAG('a', 'a', 'l', 't'), "access_all_alternates" },
+ { HB_TAG('a', 'b', 'v', 'f'), "above_base_forms" },
+ { HB_TAG('a', 'b', 'v', 'm'), "above_base_mark_positioning" },
+ { HB_TAG('a', 'b', 'v', 's'), "above_base_substitutions" },
+ { HB_TAG('a', 'f', 'r', 'c'), "alternative_fractions" },
+ { HB_TAG('a', 'k', 'h', 'n'), "akhands" },
+ { HB_TAG('b', 'l', 'w', 'f'), "below_base_forms" },
+ { HB_TAG('b', 'l', 'w', 'm'), "below_base_mark_positioning" },
+ { HB_TAG('b', 'l', 'w', 's'), "below_base_substitutions" },
+ { HB_TAG('c', 'a', 'l', 't'), "contextual_alternates" },
+ { HB_TAG('c', 'a', 's', 'e'), "case_sensitive_forms" },
+ { HB_TAG('c', 'c', 'm', 'p'), "glyph_composition" },
+ { HB_TAG('c', 'f', 'a', 'r'), "conjunct_form_after_ro" },
+ { HB_TAG('c', 'j', 'c', 't'), "conjunct_forms" },
+ { HB_TAG('c', 'l', 'i', 'g'), "contextual_ligatures" },
+ { HB_TAG('c', 'p', 'c', 't'), "centered_cjk_punctuation" },
+ { HB_TAG('c', 'p', 's', 'p'), "capital_spacing" },
+ { HB_TAG('c', 's', 'w', 'h'), "contextual_swash" },
+ { HB_TAG('c', 'u', 'r', 's'), "cursive_positioning" },
+ { HB_TAG('c', 'v', '0', '1'), "character_variant_01" },
+ { HB_TAG('c', 'v', '0', '2'), "character_variant_02" },
+ { HB_TAG('c', 'v', '0', '3'), "character_variant_03" },
+ { HB_TAG('c', 'v', '0', '4'), "character_variant_04" },
+ { HB_TAG('c', 'v', '0', '5'), "character_variant_05" },
+ { HB_TAG('c', 'v', '0', '6'), "character_variant_06" },
+ { HB_TAG('c', 'v', '0', '7'), "character_variant_07" },
+ { HB_TAG('c', 'v', '0', '8'), "character_variant_08" },
+ { HB_TAG('c', 'v', '0', '9'), "character_variant_09" },
+ { HB_TAG('c', 'v', '1', '0'), "character_variant_10" },
+ { HB_TAG('c', 'v', '1', '1'), "character_variant_11" },
+ { HB_TAG('c', 'v', '1', '2'), "character_variant_12" },
+ { HB_TAG('c', 'v', '1', '3'), "character_variant_13" },
+ { HB_TAG('c', 'v', '1', '4'), "character_variant_14" },
+ { HB_TAG('c', 'v', '1', '5'), "character_variant_15" },
+ { HB_TAG('c', 'v', '1', '6'), "character_variant_16" },
+ { HB_TAG('c', 'v', '1', '7'), "character_variant_17" },
+ { HB_TAG('c', 'v', '1', '8'), "character_variant_18" },
+ { HB_TAG('c', 'v', '1', '9'), "character_variant_19" },
+ { HB_TAG('c', 'v', '2', '0'), "character_variant_20" },
+ { HB_TAG('c', 'v', '2', '1'), "character_variant_21" },
+ { HB_TAG('c', 'v', '2', '2'), "character_variant_22" },
+ { HB_TAG('c', 'v', '2', '3'), "character_variant_23" },
+ { HB_TAG('c', 'v', '2', '4'), "character_variant_24" },
+ { HB_TAG('c', 'v', '2', '5'), "character_variant_25" },
+ { HB_TAG('c', 'v', '2', '6'), "character_variant_26" },
+ { HB_TAG('c', 'v', '2', '7'), "character_variant_27" },
+ { HB_TAG('c', 'v', '2', '8'), "character_variant_28" },
+ { HB_TAG('c', 'v', '2', '9'), "character_variant_29" },
+ { HB_TAG('c', 'v', '3', '0'), "character_variant_30" },
+ { HB_TAG('c', 'v', '3', '1'), "character_variant_31" },
+ { HB_TAG('c', 'v', '3', '2'), "character_variant_32" },
+ { HB_TAG('c', 'v', '3', '3'), "character_variant_33" },
+ { HB_TAG('c', 'v', '3', '4'), "character_variant_34" },
+ { HB_TAG('c', 'v', '3', '5'), "character_variant_35" },
+ { HB_TAG('c', 'v', '3', '6'), "character_variant_36" },
+ { HB_TAG('c', 'v', '3', '7'), "character_variant_37" },
+ { HB_TAG('c', 'v', '3', '8'), "character_variant_38" },
+ { HB_TAG('c', 'v', '3', '9'), "character_variant_39" },
+ { HB_TAG('c', 'v', '4', '0'), "character_variant_40" },
+ { HB_TAG('c', 'v', '4', '1'), "character_variant_41" },
+ { HB_TAG('c', 'v', '4', '2'), "character_variant_42" },
+ { HB_TAG('c', 'v', '4', '3'), "character_variant_43" },
+ { HB_TAG('c', 'v', '4', '4'), "character_variant_44" },
+ { HB_TAG('c', 'v', '4', '5'), "character_variant_45" },
+ { HB_TAG('c', 'v', '4', '6'), "character_variant_46" },
+ { HB_TAG('c', 'v', '4', '7'), "character_variant_47" },
+ { HB_TAG('c', 'v', '4', '8'), "character_variant_48" },
+ { HB_TAG('c', 'v', '4', '9'), "character_variant_49" },
+ { HB_TAG('c', 'v', '5', '0'), "character_variant_50" },
+ { HB_TAG('c', 'v', '5', '1'), "character_variant_51" },
+ { HB_TAG('c', 'v', '5', '2'), "character_variant_52" },
+ { HB_TAG('c', 'v', '5', '3'), "character_variant_53" },
+ { HB_TAG('c', 'v', '5', '4'), "character_variant_54" },
+ { HB_TAG('c', 'v', '5', '5'), "character_variant_55" },
+ { HB_TAG('c', 'v', '5', '6'), "character_variant_56" },
+ { HB_TAG('c', 'v', '5', '7'), "character_variant_57" },
+ { HB_TAG('c', 'v', '5', '8'), "character_variant_58" },
+ { HB_TAG('c', 'v', '5', '9'), "character_variant_59" },
+ { HB_TAG('c', 'v', '6', '0'), "character_variant_60" },
+ { HB_TAG('c', 'v', '6', '1'), "character_variant_61" },
+ { HB_TAG('c', 'v', '6', '2'), "character_variant_62" },
+ { HB_TAG('c', 'v', '6', '3'), "character_variant_63" },
+ { HB_TAG('c', 'v', '6', '4'), "character_variant_64" },
+ { HB_TAG('c', 'v', '6', '5'), "character_variant_65" },
+ { HB_TAG('c', 'v', '6', '6'), "character_variant_66" },
+ { HB_TAG('c', 'v', '6', '7'), "character_variant_67" },
+ { HB_TAG('c', 'v', '6', '8'), "character_variant_68" },
+ { HB_TAG('c', 'v', '6', '9'), "character_variant_69" },
+ { HB_TAG('c', 'v', '7', '0'), "character_variant_70" },
+ { HB_TAG('c', 'v', '7', '1'), "character_variant_71" },
+ { HB_TAG('c', 'v', '7', '2'), "character_variant_72" },
+ { HB_TAG('c', 'v', '7', '3'), "character_variant_73" },
+ { HB_TAG('c', 'v', '7', '4'), "character_variant_74" },
+ { HB_TAG('c', 'v', '7', '5'), "character_variant_75" },
+ { HB_TAG('c', 'v', '7', '6'), "character_variant_76" },
+ { HB_TAG('c', 'v', '7', '7'), "character_variant_77" },
+ { HB_TAG('c', 'v', '7', '8'), "character_variant_78" },
+ { HB_TAG('c', 'v', '7', '9'), "character_variant_79" },
+ { HB_TAG('c', 'v', '8', '0'), "character_variant_80" },
+ { HB_TAG('c', 'v', '8', '1'), "character_variant_81" },
+ { HB_TAG('c', 'v', '8', '2'), "character_variant_82" },
+ { HB_TAG('c', 'v', '8', '3'), "character_variant_83" },
+ { HB_TAG('c', 'v', '8', '4'), "character_variant_84" },
+ { HB_TAG('c', 'v', '8', '5'), "character_variant_85" },
+ { HB_TAG('c', 'v', '8', '6'), "character_variant_86" },
+ { HB_TAG('c', 'v', '8', '7'), "character_variant_87" },
+ { HB_TAG('c', 'v', '8', '8'), "character_variant_88" },
+ { HB_TAG('c', 'v', '8', '9'), "character_variant_89" },
+ { HB_TAG('c', 'v', '9', '0'), "character_variant_90" },
+ { HB_TAG('c', 'v', '9', '1'), "character_variant_91" },
+ { HB_TAG('c', 'v', '9', '2'), "character_variant_92" },
+ { HB_TAG('c', 'v', '9', '3'), "character_variant_93" },
+ { HB_TAG('c', 'v', '9', '4'), "character_variant_94" },
+ { HB_TAG('c', 'v', '9', '5'), "character_variant_95" },
+ { HB_TAG('c', 'v', '9', '6'), "character_variant_96" },
+ { HB_TAG('c', 'v', '9', '7'), "character_variant_97" },
+ { HB_TAG('c', 'v', '9', '8'), "character_variant_98" },
+ { HB_TAG('c', 'v', '9', '9'), "character_variant_99" },
+ { HB_TAG('c', '2', 'p', 'c'), "petite_capitals_from_capitals" },
+ { HB_TAG('c', '2', 's', 'c'), "small_capitals_from_capitals" },
+ { HB_TAG('d', 'i', 's', 't'), "distances" },
+ { HB_TAG('d', 'l', 'i', 'g'), "discretionary_ligatures" },
+ { HB_TAG('d', 'n', 'o', 'm'), "denominators" },
+ { HB_TAG('d', 't', 'l', 's'), "dotless_forms" },
+ { HB_TAG('e', 'x', 'p', 't'), "expert_forms" },
+ { HB_TAG('f', 'a', 'l', 't'), "final_glyph_on_line_alternates" },
+ { HB_TAG('f', 'i', 'n', '2'), "terminal_forms_2" },
+ { HB_TAG('f', 'i', 'n', '3'), "terminal_forms_3" },
+ { HB_TAG('f', 'i', 'n', 'a'), "terminal_forms" },
+ { HB_TAG('f', 'l', 'a', 'c'), "flattened_accent_forms" },
+ { HB_TAG('f', 'r', 'a', 'c'), "fractions" },
+ { HB_TAG('f', 'w', 'i', 'd'), "full_widths" },
+ { HB_TAG('h', 'a', 'l', 'f'), "half_forms" },
+ { HB_TAG('h', 'a', 'l', 'n'), "halant_forms" },
+ { HB_TAG('h', 'a', 'l', 't'), "alternate_half_widths" },
+ { HB_TAG('h', 'i', 's', 't'), "historical_forms" },
+ { HB_TAG('h', 'k', 'n', 'a'), "horizontal_kana_alternates" },
+ { HB_TAG('h', 'l', 'i', 'g'), "historical_ligatures" },
+ { HB_TAG('h', 'n', 'g', 'l'), "hangul" },
+ { HB_TAG('h', 'o', 'j', 'o'), "hojo_kanji_forms" },
+ { HB_TAG('h', 'w', 'i', 'd'), "half_widths" },
+ { HB_TAG('i', 'n', 'i', 't'), "initial_forms" },
+ { HB_TAG('i', 's', 'o', 'l'), "isolated_forms" },
+ { HB_TAG('i', 't', 'a', 'l'), "italics" },
+ { HB_TAG('j', 'a', 'l', 't'), "justification_alternates" },
+ { HB_TAG('j', 'p', '7', '8'), "jis78_forms" },
+ { HB_TAG('j', 'p', '8', '3'), "jis83_forms" },
+ { HB_TAG('j', 'p', '9', '0'), "jis90_forms" },
+ { HB_TAG('j', 'p', '0', '4'), "jis2004_forms" },
+ { HB_TAG('k', 'e', 'r', 'n'), "kerning" },
+ { HB_TAG('l', 'f', 'b', 'd'), "left_bounds" },
+ { HB_TAG('l', 'i', 'g', 'a'), "standard_ligatures" },
+ { HB_TAG('l', 'j', 'm', 'o'), "leading_jamo_forms" },
+ { HB_TAG('l', 'n', 'u', 'm'), "lining_figures" },
+ { HB_TAG('l', 'o', 'c', 'l'), "localized_forms" },
+ { HB_TAG('l', 't', 'r', 'a'), "left_to_right_alternates" },
+ { HB_TAG('l', 't', 'r', 'm'), "left_to_right_mirrored_forms" },
+ { HB_TAG('m', 'a', 'r', 'k'), "mark_positioning" },
+ { HB_TAG('m', 'e', 'd', '2'), "medial_forms_2" },
+ { HB_TAG('m', 'e', 'd', 'i'), "medial_forms" },
+ { HB_TAG('m', 'g', 'r', 'k'), "mathematical_greek" },
+ { HB_TAG('m', 'k', 'm', 'k'), "mark_to_mark_positioning" },
+ { HB_TAG('m', 's', 'e', 't'), "mark_positioning_via_substitution" },
+ { HB_TAG('n', 'a', 'l', 't'), "alternate_annotation_forms" },
+ { HB_TAG('n', 'l', 'c', 'k'), "nlc_kanji_forms" },
+ { HB_TAG('n', 'u', 'k', 't'), "nukta_forms" },
+ { HB_TAG('n', 'u', 'm', 'r'), "numerators" },
+ { HB_TAG('o', 'n', 'u', 'm'), "oldstyle_figures" },
+ { HB_TAG('o', 'p', 'b', 'd'), "optical_bounds" },
+ { HB_TAG('o', 'r', 'd', 'n'), "ordinals" },
+ { HB_TAG('o', 'r', 'n', 'm'), "ornaments" },
+ { HB_TAG('p', 'a', 'l', 't'), "proportional_alternate_widths" },
+ { HB_TAG('p', 'c', 'a', 'p'), "petite_capitals" },
+ { HB_TAG('p', 'k', 'n', 'a'), "proportional_kana" },
+ { HB_TAG('p', 'n', 'u', 'm'), "proportional_figures" },
+ { HB_TAG('p', 'r', 'e', 'f'), "pre_base_forms" },
+ { HB_TAG('p', 'r', 'e', 's'), "pre_base_substitutions" },
+ { HB_TAG('p', 's', 't', 'f'), "post_base_forms" },
+ { HB_TAG('p', 's', 't', 's'), "post_base_substitutions" },
+ { HB_TAG('p', 'w', 'i', 'd'), "proportional_widths" },
+ { HB_TAG('q', 'w', 'i', 'd'), "quarter_widths" },
+ { HB_TAG('r', 'a', 'n', 'd'), "randomize" },
+ { HB_TAG('r', 'c', 'l', 't'), "required_contextual_alternates" },
+ { HB_TAG('r', 'k', 'r', 'f'), "rakar_forms" },
+ { HB_TAG('r', 'l', 'i', 'g'), "required_ligatures" },
+ { HB_TAG('r', 'p', 'h', 'f'), "reph_forms" },
+ { HB_TAG('r', 't', 'b', 'd'), "right_bounds" },
+ { HB_TAG('r', 't', 'l', 'a'), "right_to_left_alternates" },
+ { HB_TAG('r', 't', 'l', 'm'), "right_to_left_mirrored_forms" },
+ { HB_TAG('r', 'u', 'b', 'y'), "ruby_notation_forms" },
+ { HB_TAG('r', 'v', 'r', 'n'), "required_variation_alternates" },
+ { HB_TAG('s', 'a', 'l', 't'), "stylistic_alternates" },
+ { HB_TAG('s', 'i', 'n', 'f'), "scientific_inferiors" },
+ { HB_TAG('s', 'i', 'z', 'e'), "optical_size" },
+ { HB_TAG('s', 'm', 'c', 'p'), "small_capitals" },
+ { HB_TAG('s', 'm', 'p', 'l'), "simplified_forms" },
+ { HB_TAG('s', 's', '0', '1'), "stylistic_set_01" },
+ { HB_TAG('s', 's', '0', '2'), "stylistic_set_02" },
+ { HB_TAG('s', 's', '0', '3'), "stylistic_set_03" },
+ { HB_TAG('s', 's', '0', '4'), "stylistic_set_04" },
+ { HB_TAG('s', 's', '0', '5'), "stylistic_set_05" },
+ { HB_TAG('s', 's', '0', '6'), "stylistic_set_06" },
+ { HB_TAG('s', 's', '0', '7'), "stylistic_set_07" },
+ { HB_TAG('s', 's', '0', '8'), "stylistic_set_08" },
+ { HB_TAG('s', 's', '0', '9'), "stylistic_set_09" },
+ { HB_TAG('s', 's', '1', '0'), "stylistic_set_10" },
+ { HB_TAG('s', 's', '1', '1'), "stylistic_set_11" },
+ { HB_TAG('s', 's', '1', '2'), "stylistic_set_12" },
+ { HB_TAG('s', 's', '1', '3'), "stylistic_set_13" },
+ { HB_TAG('s', 's', '1', '4'), "stylistic_set_14" },
+ { HB_TAG('s', 's', '1', '5'), "stylistic_set_15" },
+ { HB_TAG('s', 's', '1', '6'), "stylistic_set_16" },
+ { HB_TAG('s', 's', '1', '7'), "stylistic_set_17" },
+ { HB_TAG('s', 's', '1', '8'), "stylistic_set_18" },
+ { HB_TAG('s', 's', '1', '9'), "stylistic_set_19" },
+ { HB_TAG('s', 's', '2', '0'), "stylistic_set_20" },
+ { HB_TAG('s', 's', 't', 'y'), "math_script_style_alternates" },
+ { HB_TAG('s', 't', 'c', 'h'), "stretching_glyph_decomposition" },
+ { HB_TAG('s', 'u', 'b', 's'), "subscript" },
+ { HB_TAG('s', 'u', 'p', 's'), "superscript" },
+ { HB_TAG('s', 'w', 's', 'h'), "swash" },
+ { HB_TAG('t', 'i', 't', 'l'), "titling" },
+ { HB_TAG('t', 'j', 'm', 'o'), "trailing_jamo_forms" },
+ { HB_TAG('t', 'n', 'a', 'm'), "traditional_name_forms" },
+ { HB_TAG('t', 'n', 'u', 'm'), "tabular_figures" },
+ { HB_TAG('t', 'r', 'a', 'd'), "traditional_forms" },
+ { HB_TAG('t', 'w', 'i', 'd'), "third_widths" },
+ { HB_TAG('u', 'n', 'i', 'c'), "unicase" },
+ { HB_TAG('v', 'a', 'l', 't'), "alternate_vertical_metrics" },
+ { HB_TAG('v', 'a', 't', 'u'), "vattu_variants" },
+ { HB_TAG('v', 'e', 'r', 't'), "vertical_writing" },
+ { HB_TAG('v', 'h', 'a', 'l'), "alternate_vertical_half_metrics" },
+ { HB_TAG('v', 'j', 'm', 'o'), "vowel_jamo_forms" },
+ { HB_TAG('v', 'k', 'n', 'a'), "vertical_kana_alternates" },
+ { HB_TAG('v', 'k', 'r', 'n'), "vertical_kerning" },
+ { HB_TAG('v', 'p', 'a', 'l'), "proportional_alternate_vertical_metrics" },
+ { HB_TAG('v', 'r', 't', '2'), "vertical_alternates_and_rotation" },
+ { HB_TAG('v', 'r', 't', 'r'), "vertical_alternates_for_rotation" },
+ { HB_TAG('z', 'e', 'r', 'o'), "slashed_zero" },
+ // Registred OpenType variation tags.
+ { HB_TAG('i', 't', 'a', 'l'), "italic" },
+ { HB_TAG('o', 'p', 's', 'z'), "optical_size" },
+ { HB_TAG('s', 'l', 'n', 't'), "slant" },
+ { HB_TAG('w', 'd', 't', 'h'), "width" },
+ { HB_TAG('w', 'g', 'h', 't'), "weight" },
+ { 0, String() },
+};
+
+int32_t TextServerAdvanced::name_to_tag(const String &p_name) {
+ for (int i = 0; feature_set[i].tag != 0; i++) {
+ if (feature_set[i].name == p_name) {
+ return feature_set[i].tag;
+ }
+ }
+
+ // No readable name, use tag string.
+ return hb_tag_from_string(p_name.replace("custom_", "").ascii().get_data(), -1);
+}
+
+String TextServerAdvanced::tag_to_name(int32_t p_tag) {
+ for (int i = 0; feature_set[i].tag != 0; i++) {
+ if (feature_set[i].tag == p_tag) {
+ return feature_set[i].name;
+ }
+ }
+
+ // No readable name, use tag string.
+ char name[5];
+ memset(name, 0, 5);
+ hb_tag_to_string(p_tag, name);
+ return String("custom_") + String(name);
+}
+
+/*************************************************************************/
+/* Font interface */
+/*************************************************************************/
+
+RID TextServerAdvanced::create_font_system(const String &p_name, int p_base_size) {
+ ERR_FAIL_V_MSG(RID(), "System fonts are not supported by this text server.");
+}
+
+RID TextServerAdvanced::create_font_resource(const String &p_filename, int p_base_size) {
+ _THREAD_SAFE_METHOD_
+ FontDataAdvanced *fd = nullptr;
+ if (p_filename.get_extension() == "ttf" || p_filename.get_extension() == "otf" || p_filename.get_extension() == "woff") {
+ fd = memnew(DynamicFontDataAdvanced);
+ } else if (p_filename.get_extension() == "fnt" || p_filename.get_extension() == "font") {
+ fd = memnew(BitmapFontDataAdvanced);
+ } else {
+ return RID();
+ }
+
+ Error err = fd->load_from_file(p_filename, p_base_size);
+ if (err != OK) {
+ memdelete(fd);
+ return RID();
+ }
+
+ return font_owner.make_rid(fd);
+}
+
+RID TextServerAdvanced::create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size) {
+ _THREAD_SAFE_METHOD_
+ FontDataAdvanced *fd = nullptr;
+ if (p_type == "ttf" || p_type == "otf" || p_type == "woff") {
+ fd = memnew(DynamicFontDataAdvanced);
+ } else if (p_type == "fnt" || p_type == "font") {
+ fd = memnew(BitmapFontDataAdvanced);
+ } else {
+ return RID();
+ }
+
+ Error err = fd->load_from_memory(p_data, p_size, p_base_size);
+ if (err != OK) {
+ memdelete(fd);
+ return RID();
+ }
+
+ return font_owner.make_rid(fd);
+}
+
+float TextServerAdvanced::font_get_height(RID p_font, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, 0.f);
+ return fd->get_height(p_size);
+}
+
+float TextServerAdvanced::font_get_ascent(RID p_font, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, 0.f);
+ return fd->get_ascent(p_size);
+}
+
+float TextServerAdvanced::font_get_descent(RID p_font, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, 0.f);
+ return fd->get_descent(p_size);
+}
+
+float TextServerAdvanced::font_get_underline_position(RID p_font, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, 0.f);
+ return fd->get_underline_position(p_size);
+}
+
+float TextServerAdvanced::font_get_underline_thickness(RID p_font, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, 0.f);
+ return fd->get_underline_thickness(p_size);
+}
+
+void TextServerAdvanced::font_set_antialiased(RID p_font, bool p_antialiased) {
+ _THREAD_SAFE_METHOD_
+ FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND(!fd);
+ fd->set_antialiased(p_antialiased);
+}
+
+Dictionary TextServerAdvanced::font_get_feature_list(RID p_font) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, Dictionary());
+ return fd->get_feature_list();
+}
+
+bool TextServerAdvanced::font_get_antialiased(RID p_font) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, false);
+ return fd->get_antialiased();
+}
+
+Dictionary TextServerAdvanced::font_get_variation_list(RID p_font) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, Dictionary());
+ return fd->get_variation_list();
+}
+
+void TextServerAdvanced::font_set_variation(RID p_font, const String &p_name, double p_value) {
+ _THREAD_SAFE_METHOD_
+ FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND(!fd);
+ fd->set_variation(p_name, p_value);
+}
+
+double TextServerAdvanced::font_get_variation(RID p_font, const String &p_name) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, 0);
+ return fd->get_variation(p_name);
+}
+
+void TextServerAdvanced::font_set_distance_field_hint(RID p_font, bool p_distance_field) {
+ _THREAD_SAFE_METHOD_
+ FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND(!fd);
+ fd->set_distance_field_hint(p_distance_field);
+}
+
+bool TextServerAdvanced::font_get_distance_field_hint(RID p_font) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, false);
+ return fd->get_distance_field_hint();
+}
+
+void TextServerAdvanced::font_set_hinting(RID p_font, TextServer::Hinting p_hinting) {
+ _THREAD_SAFE_METHOD_
+ FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND(!fd);
+ fd->set_hinting(p_hinting);
+}
+
+TextServer::Hinting TextServerAdvanced::font_get_hinting(RID p_font) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, TextServer::HINTING_NONE);
+ return fd->get_hinting();
+}
+
+void TextServerAdvanced::font_set_force_autohinter(RID p_font, bool p_enabeld) {
+ _THREAD_SAFE_METHOD_
+ FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND(!fd);
+ fd->set_force_autohinter(p_enabeld);
+}
+
+bool TextServerAdvanced::font_get_force_autohinter(RID p_font) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, false);
+ return fd->get_force_autohinter();
+}
+
+bool TextServerAdvanced::font_has_char(RID p_font, char32_t p_char) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, false);
+ return fd->has_char(p_char);
+}
+
+String TextServerAdvanced::font_get_supported_chars(RID p_font) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, String());
+ return fd->get_supported_chars();
+}
+
+bool TextServerAdvanced::font_has_outline(RID p_font) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, false);
+ return fd->has_outline();
+}
+
+float TextServerAdvanced::font_get_base_size(RID p_font) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, 0.f);
+ return fd->get_base_size();
+}
+
+bool TextServerAdvanced::font_is_language_supported(RID p_font, const String &p_language) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, false);
+ if (fd->lang_support_overrides.has(p_language)) {
+ return fd->lang_support_overrides[p_language];
+ } else {
+ Vector<String> tags = p_language.replace("-", "_").split("_");
+ if (tags.size() > 0) {
+ if (fd->lang_support_overrides.has(tags[0])) {
+ return fd->lang_support_overrides[tags[0]];
+ }
+ }
+ return fd->is_lang_supported(p_language);
+ }
+}
+
+void TextServerAdvanced::font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) {
+ _THREAD_SAFE_METHOD_
+ FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND(!fd);
+ fd->lang_support_overrides[p_language] = p_supported;
+}
+
+bool TextServerAdvanced::font_get_language_support_override(RID p_font, const String &p_language) {
+ _THREAD_SAFE_METHOD_
+ FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, false);
+ return fd->lang_support_overrides[p_language];
+}
+
+void TextServerAdvanced::font_remove_language_support_override(RID p_font, const String &p_language) {
+ _THREAD_SAFE_METHOD_
+ FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND(!fd);
+ fd->lang_support_overrides.erase(p_language);
+}
+
+Vector<String> TextServerAdvanced::font_get_language_support_overrides(RID p_font) {
+ _THREAD_SAFE_METHOD_
+ FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, Vector<String>());
+ Vector<String> ret;
+ for (Map<String, bool>::Element *E = fd->lang_support_overrides.front(); E; E = E->next()) {
+ ret.push_back(E->key());
+ }
+ return ret;
+}
+
+bool TextServerAdvanced::font_is_script_supported(RID p_font, const String &p_script) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, false);
+ if (fd->script_support_overrides.has(p_script)) {
+ return fd->script_support_overrides[p_script];
+ } else {
+ hb_script_t scr = hb_script_from_string(p_script.ascii().get_data(), -1);
+ return fd->is_script_supported(scr);
+ }
+}
+
+void TextServerAdvanced::font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) {
+ _THREAD_SAFE_METHOD_
+ FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND(!fd);
+ fd->script_support_overrides[p_script] = p_supported;
+}
+
+bool TextServerAdvanced::font_get_script_support_override(RID p_font, const String &p_script) {
+ _THREAD_SAFE_METHOD_
+ FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, false);
+ return fd->script_support_overrides[p_script];
+}
+
+void TextServerAdvanced::font_remove_script_support_override(RID p_font, const String &p_script) {
+ _THREAD_SAFE_METHOD_
+ FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND(!fd);
+ fd->script_support_overrides.erase(p_script);
+}
+
+Vector<String> TextServerAdvanced::font_get_script_support_overrides(RID p_font) {
+ _THREAD_SAFE_METHOD_
+ FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, Vector<String>());
+ Vector<String> ret;
+ for (Map<String, bool>::Element *E = fd->script_support_overrides.front(); E; E = E->next()) {
+ ret.push_back(E->key());
+ }
+ return ret;
+}
+
+uint32_t TextServerAdvanced::font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, 0);
+ return fd->get_glyph_index(p_char, p_variation_selector);
+}
+
+Vector2 TextServerAdvanced::font_get_glyph_advance(RID p_font, uint32_t p_index, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, Vector2());
+ return fd->get_advance(p_index, p_size);
+}
+
+Vector2 TextServerAdvanced::font_get_glyph_kerning(RID p_font, uint32_t p_index_a, uint32_t p_index_b, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, Vector2());
+ return fd->get_kerning(p_index_a, p_index_b, p_size);
+}
+
+Vector2 TextServerAdvanced::font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, Vector2());
+ return fd->draw_glyph(p_canvas, p_size, p_pos, p_index, p_color);
+}
+
+Vector2 TextServerAdvanced::font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataAdvanced *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, Vector2());
+ return fd->draw_glyph_outline(p_canvas, p_size, p_outline_size, p_pos, p_index, p_color);
+}
+
+float TextServerAdvanced::font_get_oversampling() const {
+ return oversampling;
+}
+
+void TextServerAdvanced::font_set_oversampling(float p_oversampling) {
+ _THREAD_SAFE_METHOD_
+ if (oversampling != p_oversampling) {
+ oversampling = p_oversampling;
+ List<RID> fonts;
+ font_owner.get_owned_list(&fonts);
+ for (List<RID>::Element *E = fonts.front(); E; E = E->next()) {
+ font_owner.getornull(E->get())->clear_cache();
+ }
+
+ List<RID> text_bufs;
+ shaped_owner.get_owned_list(&text_bufs);
+ for (List<RID>::Element *E = text_bufs.front(); E; E = E->next()) {
+ invalidate(shaped_owner.getornull(E->get()));
+ }
+ }
+}
+
+Vector<String> TextServerAdvanced::get_system_fonts() const {
+ return Vector<String>();
+}
+
+/*************************************************************************/
+/* Shaped text buffer interface */
+/*************************************************************************/
+
+int TextServerAdvanced::_convert_pos(const ShapedTextDataAdvanced *p_sd, int p_pos) const {
+ int32_t limit = p_pos;
+ if (p_sd->text.length() != p_sd->utf16.length()) {
+ const UChar *data = p_sd->utf16.ptr();
+ for (int i = 0; i < p_pos; i++) {
+ if (U16_IS_LEAD(data[i])) {
+ limit--;
+ }
+ }
+ }
+ return limit;
+}
+
+int TextServerAdvanced::_convert_pos_inv(const ShapedTextDataAdvanced *p_sd, int p_pos) const {
+ int32_t limit = p_pos;
+ if (p_sd->text.length() != p_sd->utf16.length()) {
+ for (int i = 0; i < p_pos; i++) {
+ if (p_sd->text[i] > 0xFFFF) {
+ limit++;
+ }
+ }
+ }
+ return limit;
+}
+
+void TextServerAdvanced::invalidate(TextServerAdvanced::ShapedTextDataAdvanced *p_shaped) {
+ p_shaped->valid = false;
+ p_shaped->sort_valid = false;
+ p_shaped->line_breaks_valid = false;
+ p_shaped->justification_ops_valid = false;
+ p_shaped->ascent = 0.f;
+ p_shaped->descent = 0.f;
+ p_shaped->width = 0.f;
+ p_shaped->upos = 0.f;
+ p_shaped->uthk = 0.f;
+ p_shaped->glyphs.clear();
+ p_shaped->glyphs_logical.clear();
+ p_shaped->utf16 = Char16String();
+ if (p_shaped->script_iter != nullptr) {
+ memdelete(p_shaped->script_iter);
+ p_shaped->script_iter = nullptr;
+ }
+ for (int i = 0; i < p_shaped->bidi_iter.size(); i++) {
+ ubidi_close(p_shaped->bidi_iter[i]);
+ }
+ p_shaped->bidi_iter.clear();
+}
+
+void TextServerAdvanced::full_copy(ShapedTextDataAdvanced *p_shaped) {
+ ShapedTextDataAdvanced *parent = shaped_owner.getornull(p_shaped->parent);
+
+ for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = parent->objects.front(); E; E = E->next()) {
+ if (E->get().pos >= p_shaped->start && E->get().pos < p_shaped->end) {
+ p_shaped->objects[E->key()] = E->get();
+ }
+ }
+
+ for (int k = 0; k < parent->spans.size(); k++) {
+ ShapedTextDataAdvanced::Span span = parent->spans[k];
+ if (span.start >= p_shaped->end || span.end <= p_shaped->start) {
+ continue;
+ }
+ span.start = MAX(p_shaped->start, span.start);
+ span.end = MIN(p_shaped->end, span.end);
+ p_shaped->spans.push_back(span);
+ }
+
+ p_shaped->parent = RID();
+}
+
+RID TextServerAdvanced::create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextDataAdvanced *sd = memnew(ShapedTextDataAdvanced);
+ sd->hb_buffer = hb_buffer_create();
+ sd->direction = p_direction;
+ sd->orientation = p_orientation;
+ return shaped_owner.make_rid(sd);
+}
+
+void TextServerAdvanced::shaped_text_clear(RID p_shaped) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND(!sd);
+
+ sd->parent = RID();
+ sd->start = 0;
+ sd->end = 0;
+ sd->text = String();
+ sd->spans.clear();
+ sd->objects.clear();
+ sd->bidi_override.clear();
+ invalidate(sd);
+}
+
+void TextServerAdvanced::shaped_text_set_direction(RID p_shaped, TextServer::Direction p_direction) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND(!sd);
+
+ if (sd->direction != p_direction) {
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ sd->direction = p_direction;
+ invalidate(sd);
+ }
+}
+
+TextServer::Direction TextServerAdvanced::shaped_text_get_direction(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, TextServer::DIRECTION_LTR);
+ return sd->direction;
+}
+
+void TextServerAdvanced::shaped_text_set_bidi_override(RID p_shaped, const Vector<Vector2i> &p_override) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND(!sd);
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ sd->bidi_override = p_override;
+ invalidate(sd);
+}
+
+void TextServerAdvanced::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND(!sd);
+ if (sd->orientation != p_orientation) {
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ sd->orientation = p_orientation;
+ invalidate(sd);
+ }
+}
+
+void TextServerAdvanced::shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND(!sd);
+ ERR_FAIL_COND(sd->parent != RID());
+ if (sd->preserve_invalid != p_enabled) {
+ sd->preserve_invalid = p_enabled;
+ invalidate(sd);
+ }
+}
+
+bool TextServerAdvanced::shaped_text_get_preserve_invalid(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+ return sd->preserve_invalid;
+}
+
+void TextServerAdvanced::shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND(!sd);
+ if (sd->preserve_control != p_enabled) {
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ sd->preserve_control = p_enabled;
+ invalidate(sd);
+ }
+}
+
+bool TextServerAdvanced::shaped_text_get_preserve_control(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+ return sd->preserve_control;
+}
+
+TextServer::Orientation TextServerAdvanced::shaped_text_get_orientation(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, TextServer::ORIENTATION_HORIZONTAL);
+ return sd->orientation;
+}
+
+bool TextServerAdvanced::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+ ERR_FAIL_COND_V(p_size <= 0, false);
+
+ if (p_text.empty()) {
+ return true;
+ }
+
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+
+ ShapedTextDataAdvanced::Span span;
+ span.start = sd->text.length();
+ span.end = span.start + p_text.length();
+ span.fonts = p_fonts; // Do not pre-sort, spans will be divided to subruns later.
+ span.font_size = p_size;
+ span.language = p_language;
+ span.features = p_opentype_features;
+
+ sd->spans.push_back(span);
+ sd->text += p_text;
+ sd->end += p_text.length();
+ invalidate(sd);
+
+ return true;
+}
+
+bool TextServerAdvanced::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align, int p_length) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+ ERR_FAIL_COND_V(p_key == Variant(), false);
+ ERR_FAIL_COND_V(sd->objects.has(p_key), false);
+
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+
+ ShapedTextDataAdvanced::Span span;
+ span.start = sd->text.length();
+ span.end = span.start + p_length;
+ span.embedded_key = p_key;
+
+ ShapedTextDataAdvanced::EmbeddedObject obj;
+ obj.inline_align = p_inline_align;
+ obj.rect.size = p_size;
+ obj.pos = span.start;
+
+ sd->spans.push_back(span);
+ sd->text += String::chr(0xfffc).repeat(p_length);
+ sd->end += p_length;
+ sd->objects[p_key] = obj;
+ invalidate(sd);
+
+ return true;
+}
+
+bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+ ERR_FAIL_COND_V(!sd->objects.has(p_key), false);
+ sd->objects[p_key].rect.size = p_size;
+ sd->objects[p_key].inline_align = p_inline_align;
+ if (sd->valid) {
+ // Recalc string metrics.
+ sd->ascent = 0;
+ sd->descent = 0;
+ sd->width = 0;
+ sd->upos = 0;
+ sd->uthk = 0;
+ int sd_size = sd->glyphs.size();
+
+ for (int i = 0; i < sd_size; i++) {
+ Glyph gl = sd->glyphs[i];
+ Variant key;
+ if (gl.count == 1) {
+ for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
+ if (E->get().pos == gl.start) {
+ key = E->key();
+ break;
+ }
+ }
+ }
+ if (key != Variant()) {
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ sd->objects[key].rect.position.x = sd->width;
+ sd->width += sd->objects[key].rect.size.x;
+ switch (sd->objects[key].inline_align) {
+ case VALIGN_TOP: {
+ sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.y);
+ } break;
+ case VALIGN_CENTER: {
+ sd->ascent = MAX(sd->ascent, Math::round(sd->objects[key].rect.size.y / 2));
+ sd->descent = MAX(sd->descent, Math::round(sd->objects[key].rect.size.y / 2));
+ } break;
+ case VALIGN_BOTTOM: {
+ sd->descent = MAX(sd->descent, sd->objects[key].rect.size.y);
+ } break;
+ }
+ sd->glyphs.write[i].advance = sd->objects[key].rect.size.x;
+ } else {
+ sd->objects[key].rect.position.y = sd->width;
+ sd->width += sd->objects[key].rect.size.y;
+ switch (sd->objects[key].inline_align) {
+ case VALIGN_TOP: {
+ sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.x);
+ } break;
+ case VALIGN_CENTER: {
+ sd->ascent = MAX(sd->ascent, Math::round(sd->objects[key].rect.size.x / 2));
+ sd->descent = MAX(sd->descent, Math::round(sd->objects[key].rect.size.x / 2));
+ } break;
+ case VALIGN_BOTTOM: {
+ sd->descent = MAX(sd->descent, sd->objects[key].rect.size.x);
+ } break;
+ }
+ sd->glyphs.write[i].advance = sd->objects[key].rect.size.y;
+ }
+ } else {
+ const FontDataAdvanced *fd = font_owner.getornull(gl.font_rid);
+ if (fd != nullptr) {
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ sd->ascent = MAX(sd->ascent, MAX(fd->get_ascent(gl.font_size), -gl.y_off));
+ sd->descent = MAX(sd->descent, MAX(fd->get_descent(gl.font_size), gl.y_off));
+ } else {
+ sd->ascent = MAX(sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
+ sd->descent = MAX(sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
+ }
+ sd->upos = MAX(sd->upos, font_get_underline_position(gl.font_rid, gl.font_size));
+ sd->uthk = MAX(sd->uthk, font_get_underline_thickness(gl.font_rid, gl.font_size));
+ } else if (sd->preserve_invalid || (sd->preserve_control && is_control(gl.index))) {
+ // Glyph not found, replace with hex code box.
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f));
+ sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f));
+ } else {
+ sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
+ sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
+ }
+ }
+ sd->width += gl.advance * gl.repeat;
+ }
+ }
+
+ // Align embedded objects to baseline.
+ for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
+ if ((E->get().pos >= sd->start) && (E->get().pos < sd->end)) {
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ switch (E->get().inline_align) {
+ case VALIGN_TOP: {
+ E->get().rect.position.y = -sd->ascent;
+ } break;
+ case VALIGN_CENTER: {
+ E->get().rect.position.y = -(E->get().rect.size.y / 2);
+ } break;
+ case VALIGN_BOTTOM: {
+ E->get().rect.position.y = sd->descent - E->get().rect.size.y;
+ } break;
+ }
+ } else {
+ switch (E->get().inline_align) {
+ case VALIGN_TOP: {
+ E->get().rect.position.x = -sd->ascent;
+ } break;
+ case VALIGN_CENTER: {
+ E->get().rect.position.x = -(E->get().rect.size.x / 2);
+ } break;
+ case VALIGN_BOTTOM: {
+ E->get().rect.position.x = sd->descent - E->get().rect.size.x;
+ } break;
+ }
+ }
+ }
+ }
+ }
+ return true;
+}
+
+RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_length) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, RID());
+ if (sd->parent != RID()) {
+ return shaped_text_substr(sd->parent, p_start, p_length);
+ }
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+ ERR_FAIL_COND_V(p_start < 0 || p_length < 0, RID());
+ ERR_FAIL_COND_V(sd->start > p_start || sd->end < p_start, RID());
+ ERR_FAIL_COND_V(sd->end < p_start + p_length, RID());
+
+ ShapedTextDataAdvanced *new_sd = memnew(ShapedTextDataAdvanced);
+
+ new_sd->hb_buffer = hb_buffer_create();
+ new_sd->parent = p_shaped;
+ new_sd->start = p_start;
+ new_sd->end = p_start + p_length;
+
+ new_sd->orientation = sd->orientation;
+ new_sd->direction = sd->direction;
+ new_sd->para_direction = sd->para_direction;
+ new_sd->line_breaks_valid = sd->line_breaks_valid;
+ new_sd->justification_ops_valid = sd->justification_ops_valid;
+ new_sd->sort_valid = false;
+ new_sd->upos = sd->upos;
+ new_sd->uthk = sd->uthk;
+
+ if (p_length > 0) {
+ new_sd->text = sd->text.substr(p_start, p_length);
+ new_sd->utf16 = new_sd->text.utf16();
+ new_sd->script_iter = memnew(ScriptIterator(new_sd->text, 0, new_sd->text.length()));
+
+ int sd_size = sd->glyphs.size();
+ const Glyph *sd_glyphs = sd->glyphs.ptr();
+ const FontDataAdvanced *fd = nullptr;
+ RID prev_rid = RID();
+
+ for (int ov = 0; ov < sd->bidi_override.size(); ov++) {
+ UErrorCode err = U_ZERO_ERROR;
+
+ if (sd->bidi_override[ov].x >= p_start + p_length || sd->bidi_override[ov].y <= p_start) {
+ continue;
+ }
+ int start = _convert_pos_inv(sd, MAX(0, p_start - sd->bidi_override[ov].x));
+ int end = _convert_pos_inv(sd, MIN(p_start + p_length, sd->bidi_override[ov].y) - sd->bidi_override[ov].x);
+
+ ERR_FAIL_COND_V_MSG((start < 0 || end - start > new_sd->utf16.length()), RID(), "Invalid BiDi override range.");
+
+ //Create temporary line bidi & shape
+ UBiDi *bidi_iter = ubidi_openSized(end - start, 0, &err);
+ ERR_FAIL_COND_V_MSG(U_FAILURE(err), RID(), u_errorName(err));
+ ubidi_setLine(sd->bidi_iter[ov], start, end, bidi_iter, &err);
+ if (U_FAILURE(err)) {
+ ubidi_close(bidi_iter);
+ ERR_FAIL_V_MSG(RID(), u_errorName(err));
+ }
+ new_sd->bidi_iter.push_back(bidi_iter);
+
+ err = U_ZERO_ERROR;
+ int bidi_run_count = ubidi_countRuns(bidi_iter, &err);
+ ERR_FAIL_COND_V_MSG(U_FAILURE(err), RID(), u_errorName(err));
+ for (int i = 0; i < bidi_run_count; i++) {
+ int32_t _bidi_run_start = 0;
+ int32_t _bidi_run_length = 0;
+ ubidi_getVisualRun(bidi_iter, i, &_bidi_run_start, &_bidi_run_length);
+
+ int32_t bidi_run_start = _convert_pos(sd, sd->bidi_override[ov].x + start + _bidi_run_start);
+ int32_t bidi_run_end = _convert_pos(sd, sd->bidi_override[ov].x + start + _bidi_run_start + _bidi_run_length);
+
+ for (int j = 0; j < sd_size; j++) {
+ if ((sd_glyphs[j].start >= bidi_run_start) && (sd_glyphs[j].end <= bidi_run_end)) {
+ // Copy glyphs.
+ Glyph gl = sd_glyphs[j];
+ Variant key;
+ bool find_embedded = false;
+ if (gl.count == 1) {
+ for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
+ if (E->get().pos == gl.start) {
+ find_embedded = true;
+ key = E->key();
+ new_sd->objects[key] = E->get();
+ break;
+ }
+ }
+ }
+ if (find_embedded) {
+ if (new_sd->orientation == ORIENTATION_HORIZONTAL) {
+ new_sd->objects[key].rect.position.x = new_sd->width;
+ new_sd->width += new_sd->objects[key].rect.size.x;
+ switch (new_sd->objects[key].inline_align) {
+ case VALIGN_TOP: {
+ new_sd->ascent = MAX(new_sd->ascent, new_sd->objects[key].rect.size.y);
+ } break;
+ case VALIGN_CENTER: {
+ new_sd->ascent = MAX(new_sd->ascent, Math::round(new_sd->objects[key].rect.size.y / 2));
+ new_sd->descent = MAX(new_sd->descent, Math::round(new_sd->objects[key].rect.size.y / 2));
+ } break;
+ case VALIGN_BOTTOM: {
+ new_sd->descent = MAX(new_sd->descent, new_sd->objects[key].rect.size.y);
+ } break;
+ }
+ } else {
+ new_sd->objects[key].rect.position.y = new_sd->width;
+ new_sd->width += new_sd->objects[key].rect.size.y;
+ switch (new_sd->objects[key].inline_align) {
+ case VALIGN_TOP: {
+ new_sd->ascent = MAX(new_sd->ascent, new_sd->objects[key].rect.size.x);
+ } break;
+ case VALIGN_CENTER: {
+ new_sd->ascent = MAX(new_sd->ascent, Math::round(new_sd->objects[key].rect.size.x / 2));
+ new_sd->descent = MAX(new_sd->descent, Math::round(new_sd->objects[key].rect.size.x / 2));
+ } break;
+ case VALIGN_BOTTOM: {
+ new_sd->descent = MAX(new_sd->descent, new_sd->objects[key].rect.size.x);
+ } break;
+ }
+ }
+ } else {
+ if (prev_rid != gl.font_rid) {
+ fd = font_owner.getornull(gl.font_rid);
+ prev_rid = gl.font_rid;
+ }
+ if (fd != nullptr) {
+ if (new_sd->orientation == ORIENTATION_HORIZONTAL) {
+ new_sd->ascent = MAX(new_sd->ascent, MAX(fd->get_ascent(gl.font_size), -gl.y_off));
+ new_sd->descent = MAX(new_sd->descent, MAX(fd->get_descent(gl.font_size), gl.y_off));
+ } else {
+ new_sd->ascent = MAX(new_sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
+ new_sd->descent = MAX(new_sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
+ }
+ } else if (new_sd->preserve_invalid || (new_sd->preserve_control && is_control(gl.index))) {
+ // Glyph not found, replace with hex code box.
+ if (new_sd->orientation == ORIENTATION_HORIZONTAL) {
+ new_sd->ascent = MAX(new_sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f));
+ new_sd->descent = MAX(new_sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f));
+ } else {
+ new_sd->ascent = MAX(new_sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
+ new_sd->descent = MAX(new_sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
+ }
+ }
+ new_sd->width += gl.advance * gl.repeat;
+ }
+ new_sd->glyphs.push_back(gl);
+ }
+ }
+ }
+ }
+
+ // Align embedded objects to baseline.
+ for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = new_sd->objects.front(); E; E = E->next()) {
+ if ((E->get().pos >= new_sd->start) && (E->get().pos < new_sd->end)) {
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ switch (E->get().inline_align) {
+ case VALIGN_TOP: {
+ E->get().rect.position.y = -new_sd->ascent;
+ } break;
+ case VALIGN_CENTER: {
+ E->get().rect.position.y = -(E->get().rect.size.y / 2);
+ } break;
+ case VALIGN_BOTTOM: {
+ E->get().rect.position.y = new_sd->descent - E->get().rect.size.y;
+ } break;
+ }
+ } else {
+ switch (E->get().inline_align) {
+ case VALIGN_TOP: {
+ E->get().rect.position.x = -new_sd->ascent;
+ } break;
+ case VALIGN_CENTER: {
+ E->get().rect.position.x = -(E->get().rect.size.x / 2);
+ } break;
+ case VALIGN_BOTTOM: {
+ E->get().rect.position.x = new_sd->descent - E->get().rect.size.x;
+ } break;
+ }
+ }
+ }
+ }
+ }
+
+ new_sd->valid = true;
+
+ return shaped_owner.make_rid(new_sd);
+}
+
+RID TextServerAdvanced::shaped_text_get_parent(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, RID());
+ return sd->parent;
+}
+
+float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t /*JustificationFlag*/ p_jst_flags) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0.f);
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+ if (!sd->justification_ops_valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_update_justification_ops(p_shaped);
+ }
+
+ int start_pos = 0;
+ int end_pos = sd->glyphs.size() - 1;
+
+ if ((p_jst_flags & JUSTIFICATION_AFTER_LAST_TAB) == JUSTIFICATION_AFTER_LAST_TAB) {
+ int start, end, delta;
+ if (sd->para_direction == DIRECTION_LTR) {
+ start = sd->glyphs.size() - 1;
+ end = -1;
+ delta = -1;
+ } else {
+ start = 0;
+ end = sd->glyphs.size();
+ delta = +1;
+ }
+
+ for (int i = start; i != end; i += delta) {
+ if ((sd->glyphs[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) {
+ if (sd->para_direction == DIRECTION_LTR) {
+ start_pos = i;
+ break;
+ } else {
+ end_pos = i;
+ break;
+ }
+ }
+ }
+ }
+
+ if ((p_jst_flags & JUSTIFICATION_TRIM_EDGE_SPACES) == JUSTIFICATION_TRIM_EDGE_SPACES) {
+ while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ sd->width -= sd->glyphs[start_pos].advance * sd->glyphs[start_pos].repeat;
+ sd->glyphs.write[start_pos].advance = 0;
+ start_pos += sd->glyphs[start_pos].count;
+ }
+ while ((start_pos < end_pos) && ((sd->glyphs[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ sd->width -= sd->glyphs[end_pos].advance * sd->glyphs[end_pos].repeat;
+ sd->glyphs.write[end_pos].advance = 0;
+ end_pos -= sd->glyphs[end_pos].count;
+ }
+ }
+
+ int space_count = 0;
+ int elongation_count = 0;
+ for (int i = start_pos; i <= end_pos; i++) {
+ const Glyph &gl = sd->glyphs[i];
+ if (gl.count > 0) {
+ if ((gl.flags & GRAPHEME_IS_ELONGATION) == GRAPHEME_IS_ELONGATION) {
+ elongation_count++;
+ }
+ if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) {
+ space_count++;
+ }
+ }
+ }
+
+ if ((elongation_count > 0) && ((p_jst_flags & JUSTIFICATION_KASHIDA) == JUSTIFICATION_KASHIDA)) {
+ float delta_width_per_kashida = (p_width - sd->width) / elongation_count;
+ for (int i = start_pos; i <= end_pos; i++) {
+ Glyph &gl = sd->glyphs.write[i];
+ if (gl.count > 0) {
+ if (((gl.flags & GRAPHEME_IS_ELONGATION) == GRAPHEME_IS_ELONGATION) && (gl.advance > 0)) {
+ int count = delta_width_per_kashida / gl.advance;
+ int prev_count = gl.repeat;
+ if ((gl.flags & GRAPHEME_IS_VIRTUAL) == GRAPHEME_IS_VIRTUAL) {
+ gl.repeat = count;
+ } else {
+ gl.repeat = count + 1;
+ }
+ sd->width += (gl.repeat - prev_count) * gl.advance;
+ }
+ }
+ }
+ }
+
+ if ((space_count > 0) && ((p_jst_flags & JUSTIFICATION_WORD_BOUND) == JUSTIFICATION_WORD_BOUND)) {
+ float delta_width_per_space = (p_width - sd->width) / space_count;
+ for (int i = start_pos; i <= end_pos; i++) {
+ Glyph &gl = sd->glyphs.write[i];
+ if (gl.count > 0) {
+ if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) {
+ float old_adv = gl.advance;
+ if ((gl.flags & GRAPHEME_IS_VIRTUAL) == GRAPHEME_IS_VIRTUAL) {
+ gl.advance = Math::round(MAX(gl.advance + delta_width_per_space, 0.f));
+ } else {
+ gl.advance = Math::round(MAX(gl.advance + delta_width_per_space, 0.05 * gl.font_size));
+ }
+ sd->width += (gl.advance - old_adv);
+ }
+ }
+ }
+ }
+
+ return sd->width;
+}
+
+float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0.f);
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+ if (!sd->line_breaks_valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_update_breaks(p_shaped);
+ }
+
+ int tab_index = 0;
+ float off = 0.f;
+
+ int start, end, delta;
+ if (sd->para_direction == DIRECTION_LTR) {
+ start = 0;
+ end = sd->glyphs.size();
+ delta = +1;
+ } else {
+ start = sd->glyphs.size() - 1;
+ end = -1;
+ delta = -1;
+ }
+
+ Glyph *gl = sd->glyphs.ptrw();
+
+ for (int i = start; i != end; i += delta) {
+ if ((gl[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) {
+ float tab_off = 0.f;
+ while (tab_off <= off) {
+ tab_off += p_tab_stops[tab_index];
+ tab_index++;
+ if (tab_index >= p_tab_stops.size()) {
+ tab_index = 0;
+ }
+ }
+ float old_adv = gl[i].advance;
+ gl[i].advance = tab_off - off;
+ sd->width += gl[i].advance - old_adv;
+ off = 0;
+ continue;
+ }
+ off += gl[i].advance * gl[i].repeat;
+ }
+
+ return 0.f;
+}
+
+bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+ if (!sd->valid) {
+ shaped_text_shape(p_shaped);
+ }
+
+ if (sd->line_breaks_valid) {
+ return true; // Noting to do.
+ }
+
+ const UChar *data = sd->utf16.ptr();
+
+ HashMap<int, bool> breaks;
+ UErrorCode err = U_ZERO_ERROR;
+ for (int i = 0; i < sd->spans.size(); i++) {
+ UBreakIterator *bi = ubrk_open(UBRK_LINE, sd->spans[i].language.ascii().get_data(), data + _convert_pos_inv(sd, sd->spans[i].start), _convert_pos_inv(sd, sd->spans[i].end - sd->spans[i].start), &err);
+ if (U_FAILURE(err)) {
+ //No data loaded - use fallback.
+ for (int j = sd->spans[i].start; j < sd->spans[i].end; j++) {
+ char32_t c = sd->text[j - sd->start];
+ if (is_whitespace(c)) {
+ breaks[j] = false;
+ }
+ if (is_linebreak(c)) {
+ breaks[j] = true;
+ }
+ }
+ } else {
+ while (ubrk_next(bi) != UBRK_DONE) {
+ int pos = _convert_pos(sd, ubrk_current(bi)) + sd->spans[i].start - 1;
+ if (pos != sd->spans[i].end) {
+ if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_HARD) && (ubrk_getRuleStatus(bi) < UBRK_LINE_HARD_LIMIT)) {
+ breaks[pos] = true;
+ } else if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_SOFT) && (ubrk_getRuleStatus(bi) < UBRK_LINE_SOFT_LIMIT)) {
+ breaks[pos] = false;
+ }
+ }
+ }
+ }
+ ubrk_close(bi);
+ }
+
+ sd->sort_valid = false;
+ sd->glyphs_logical.clear();
+ int sd_size = sd->glyphs.size();
+ const char32_t *ch = sd->text.ptr();
+ Glyph *sd_glyphs = sd->glyphs.ptrw();
+
+ for (int i = 0; i < sd_size; i++) {
+ if (sd_glyphs[i].count > 0) {
+ char32_t c = ch[sd_glyphs[i].start - sd->start];
+ if (c == 0xfffc) {
+ continue;
+ }
+ if (c == 0x0009 || c == 0x000b) {
+ sd_glyphs[i].flags |= GRAPHEME_IS_TAB;
+ }
+ if (is_whitespace(c)) {
+ sd_glyphs[i].flags |= GRAPHEME_IS_SPACE;
+ }
+ if (u_ispunct(c)) {
+ sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION;
+ }
+ if (breaks.has(sd->glyphs[i].start)) {
+ if (breaks[sd->glyphs[i].start]) {
+ sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD;
+ } else {
+ if (is_whitespace(c)) {
+ sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_SOFT;
+ } else {
+ TextServer::Glyph gl;
+ gl.start = sd_glyphs[i].start;
+ gl.end = sd_glyphs[i].end;
+ gl.count = 1;
+ gl.font_rid = sd_glyphs[i].font_rid;
+ gl.font_size = sd_glyphs[i].font_size;
+ gl.flags = GRAPHEME_IS_BREAK_SOFT | GRAPHEME_IS_VIRTUAL;
+ sd->glyphs.insert(i + sd_glyphs[i].count, gl); // insert after
+
+ // Update write pointer and size.
+ sd_size = sd->glyphs.size();
+ sd_glyphs = sd->glyphs.ptrw();
+
+ i += sd_glyphs[i].count;
+ continue;
+ }
+ }
+ }
+
+ i += (sd_glyphs[i].count - 1);
+ }
+ }
+
+ sd->line_breaks_valid = true;
+
+ return sd->line_breaks_valid;
+}
+
+_FORCE_INLINE_ int _generate_kashida_justification_opportunies(const String &p_data, int p_start, int p_end) {
+ int kashida_pos = -1;
+ int8_t priority = 100;
+ int i = p_start;
+
+ char32_t pc = 0;
+
+ while ((p_end > p_start) && is_transparent(p_data[p_end - 1])) {
+ p_end--;
+ }
+
+ while (i < p_end) {
+ uint32_t c = p_data[i];
+
+ if (c == 0x0640) {
+ kashida_pos = i;
+ priority = 0;
+ }
+ if (priority >= 1 && i < p_end - 1) {
+ if (is_seen_sad(c) && (p_data[i + 1] != 0x200C)) {
+ kashida_pos = i;
+ priority = 1;
+ }
+ }
+ if (priority >= 2 && i > p_start) {
+ if (is_teh_marbuta(c) || is_dal(c) || (is_heh(c) && i == p_end - 1)) {
+ if (is_connected_to_prev(c, pc)) {
+ kashida_pos = i - 1;
+ priority = 2;
+ }
+ }
+ }
+ if (priority >= 3 && i > p_start) {
+ if (is_alef(c) || ((is_lam(c) || is_tah(c) || is_kaf(c) || is_gaf(c)) && i == p_end - 1)) {
+ if (is_connected_to_prev(c, pc)) {
+ kashida_pos = i - 1;
+ priority = 3;
+ }
+ }
+ }
+ if (priority >= 4 && i > p_start && i < p_end - 1) {
+ if (is_beh(c)) {
+ if (is_reh(p_data[i + 1]) || is_yeh(p_data[i + 1])) {
+ if (is_connected_to_prev(c, pc)) {
+ kashida_pos = i - 1;
+ priority = 4;
+ }
+ }
+ }
+ }
+ if (priority >= 5 && i > p_start) {
+ if (is_waw(c) || ((is_ain(c) || is_qaf(c) || is_feh(c)) && i == p_end - 1)) {
+ if (is_connected_to_prev(c, pc)) {
+ kashida_pos = i - 1;
+ priority = 5;
+ }
+ }
+ }
+ if (priority >= 6 && i > p_start) {
+ if (is_reh(c)) {
+ if (is_connected_to_prev(c, pc)) {
+ kashida_pos = i - 1;
+ priority = 6;
+ }
+ }
+ }
+ if (!is_transparent(c))
+ pc = c;
+ i++;
+ }
+
+ return kashida_pos;
+}
+
+bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+ if (!sd->valid) {
+ shaped_text_shape(p_shaped);
+ }
+ if (!sd->line_breaks_valid) {
+ shaped_text_update_breaks(p_shaped);
+ }
+
+ if (sd->justification_ops_valid) {
+ return true; // Noting to do.
+ }
+
+ const UChar *data = sd->utf16.ptr();
+ int32_t data_size = sd->utf16.length();
+
+ Map<int, bool> jstops;
+
+ // Use ICU word iterator and custom kashida detection.
+ UErrorCode err = U_ZERO_ERROR;
+ UBreakIterator *bi = ubrk_open(UBRK_WORD, "", data, data_size, &err);
+ if (U_FAILURE(err)) {
+ // No data - use fallback
+ int limit = 0;
+ for (int i = 0; i < sd->text.length(); i++) {
+ if (is_whitespace(data[i])) {
+ int ks = _generate_kashida_justification_opportunies(sd->text, limit, i) + sd->start;
+ if (ks != -1) {
+ jstops[ks] = true;
+ }
+ limit = i + 1;
+ }
+ }
+ int ks = _generate_kashida_justification_opportunies(sd->text, limit, sd->text.length()) + sd->start;
+ if (ks != -1) {
+ jstops[ks] = true;
+ }
+ } else {
+ int limit = 0;
+ while (ubrk_next(bi) != UBRK_DONE) {
+ if (ubrk_getRuleStatus(bi) != UBRK_WORD_NONE) {
+ int i = _convert_pos(sd, ubrk_current(bi));
+ jstops[i + sd->start] = false;
+ int ks = _generate_kashida_justification_opportunies(sd->text, limit, i);
+ if (ks != -1) {
+ jstops[ks + sd->start] = true;
+ }
+ limit = i;
+ }
+ }
+ ubrk_close(bi);
+ }
+
+ sd->sort_valid = false;
+ sd->glyphs_logical.clear();
+ int sd_size = sd->glyphs.size();
+
+ if (jstops.size() > 0) {
+ for (int i = 0; i < sd_size; i++) {
+ if (sd->glyphs[i].count > 0) {
+ if (jstops.has(sd->glyphs[i].start)) {
+ char32_t c = sd->text[sd->glyphs[i].start - sd->start];
+ if (c == 0xfffc) {
+ continue;
+ }
+ if (jstops[sd->glyphs[i].start]) {
+ if (c == 0x0640) {
+ sd->glyphs.write[i].flags |= GRAPHEME_IS_ELONGATION;
+ } else {
+ if (sd->glyphs[i].font_rid != RID()) {
+ TextServer::Glyph gl = _shape_single_glyph(sd, 0x0640, HB_SCRIPT_ARABIC, HB_DIRECTION_RTL, sd->glyphs[i].font_rid, sd->glyphs[i].font_size);
+ if ((gl.flags & GRAPHEME_IS_VALID) == GRAPHEME_IS_VALID) {
+ gl.start = sd->glyphs[i].start;
+ gl.end = sd->glyphs[i].end;
+ gl.repeat = 0;
+ gl.count = 1;
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ gl.y_off = sd->glyphs[i].y_off;
+ } else {
+ gl.x_off = sd->glyphs[i].x_off;
+ }
+ gl.flags |= GRAPHEME_IS_ELONGATION | GRAPHEME_IS_VIRTUAL;
+ sd->glyphs.insert(i, gl);
+ i++;
+ }
+ }
+ }
+ } else if (!is_whitespace(c)) {
+ TextServer::Glyph gl;
+ gl.start = sd->glyphs[i].start;
+ gl.end = sd->glyphs[i].end;
+ gl.count = 1;
+ gl.font_rid = sd->glyphs[i].font_rid;
+ gl.font_size = sd->glyphs[i].font_size;
+ gl.flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_VIRTUAL;
+ sd->glyphs.insert(i + sd->glyphs[i].count, gl); // insert after
+ i += sd->glyphs[i].count;
+ continue;
+ }
+ }
+ }
+ }
+ }
+
+ sd->justification_ops_valid = true;
+ return sd->justification_ops_valid;
+}
+
+TextServer::Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced *p_sd, char32_t p_char, hb_script_t p_script, hb_direction_t p_direction, RID p_font, int p_font_size) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font);
+ hb_font_t *hb_font = fd->get_hb_handle(p_font_size);
+
+ hb_buffer_clear_contents(p_sd->hb_buffer);
+ hb_buffer_set_direction(p_sd->hb_buffer, p_direction);
+ hb_buffer_set_flags(p_sd->hb_buffer, (hb_buffer_flags_t)(HB_BUFFER_FLAG_DEFAULT));
+ hb_buffer_set_script(p_sd->hb_buffer, p_script);
+ hb_buffer_add_utf32(p_sd->hb_buffer, (const uint32_t *)&p_char, 1, 0, 1);
+
+ hb_shape(hb_font, p_sd->hb_buffer, nullptr, 0);
+
+ unsigned int glyph_count = 0;
+ hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(p_sd->hb_buffer, &glyph_count);
+ hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(p_sd->hb_buffer, &glyph_count);
+
+ // Process glyphs.
+ TextServer::Glyph gl;
+
+ if (p_direction == HB_DIRECTION_RTL || p_direction == HB_DIRECTION_BTT) {
+ gl.flags |= TextServer::GRAPHEME_IS_RTL;
+ }
+
+ gl.font_rid = p_font;
+ gl.font_size = p_font_size;
+
+ if (glyph_count > 0) {
+ if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
+ gl.advance = Math::round(glyph_pos[0].x_advance / (64.0 / fd->get_font_scale(p_font_size)));
+ } else {
+ gl.advance = -Math::round(glyph_pos[0].y_advance / (64.0 / fd->get_font_scale(p_font_size)));
+ }
+ gl.count = 1;
+
+ gl.index = glyph_info[0].codepoint;
+ gl.x_off = Math::round(glyph_pos[0].x_offset / (64.0 / fd->get_font_scale(p_font_size)));
+ gl.y_off = -Math::round(glyph_pos[0].y_offset / (64.0 / fd->get_font_scale(p_font_size)));
+
+ if ((glyph_info[0].codepoint != 0) || !u_isgraph(p_char)) {
+ gl.flags |= GRAPHEME_IS_VALID;
+ }
+ }
+ return gl;
+}
+
+void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_start, int32_t p_end, hb_script_t p_script, hb_direction_t p_direction, Vector<RID> p_fonts, int p_span, int p_fb_index) {
+ FontDataAdvanced *fd = nullptr;
+ if (p_fb_index < p_fonts.size()) {
+ fd = font_owner.getornull(p_fonts[p_fb_index]);
+ }
+
+ int fs = p_sd->spans[p_span].font_size;
+ if (fd == nullptr) {
+ // Add fallback glyphs
+ for (int i = p_start; i < p_end; i++) {
+ if (p_sd->preserve_invalid || (p_sd->preserve_control && is_control(p_sd->text[i]))) {
+ TextServer::Glyph gl;
+ gl.start = i;
+ gl.end = i + 1;
+ gl.count = 1;
+ gl.index = p_sd->text[i];
+ gl.font_size = fs;
+ gl.font_rid = RID();
+ if (p_direction == HB_DIRECTION_RTL || p_direction == HB_DIRECTION_BTT) {
+ gl.flags |= TextServer::GRAPHEME_IS_RTL;
+ }
+ if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
+ gl.advance = get_hex_code_box_size(fs, gl.index).x;
+ p_sd->ascent = MAX(p_sd->ascent, Math::round(get_hex_code_box_size(fs, gl.index).y * 0.75f));
+ p_sd->descent = MAX(p_sd->descent, Math::round(get_hex_code_box_size(fs, gl.index).y * 0.25f));
+ } else {
+ gl.advance = get_hex_code_box_size(fs, gl.index).y;
+ p_sd->ascent = MAX(p_sd->ascent, Math::round(get_hex_code_box_size(fs, gl.index).x * 0.5f));
+ p_sd->descent = MAX(p_sd->descent, Math::round(get_hex_code_box_size(fs, gl.index).x * 0.5f));
+ }
+ p_sd->width += gl.advance;
+
+ p_sd->glyphs.push_back(gl);
+ }
+ }
+ return;
+ }
+
+ hb_font_t *hb_font = fd->get_hb_handle(fs);
+ ERR_FAIL_COND(hb_font == nullptr);
+
+ hb_buffer_clear_contents(p_sd->hb_buffer);
+ hb_buffer_set_direction(p_sd->hb_buffer, p_direction);
+ if (p_sd->preserve_control) {
+ hb_buffer_set_flags(p_sd->hb_buffer, (hb_buffer_flags_t)(HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES | (p_start == 0 ? HB_BUFFER_FLAG_BOT : 0) | (p_end == p_sd->text.length() ? HB_BUFFER_FLAG_EOT : 0)));
+ } else {
+ hb_buffer_set_flags(p_sd->hb_buffer, (hb_buffer_flags_t)(HB_BUFFER_FLAG_DEFAULT | (p_start == 0 ? HB_BUFFER_FLAG_BOT : 0) | (p_end == p_sd->text.length() ? HB_BUFFER_FLAG_EOT : 0)));
+ }
+ hb_buffer_set_script(p_sd->hb_buffer, p_script);
+
+ if (p_sd->spans[p_span].language != String()) {
+ hb_language_t lang = hb_language_from_string(p_sd->spans[p_span].language.ascii().get_data(), -1);
+ hb_buffer_set_language(p_sd->hb_buffer, lang);
+ }
+
+ hb_buffer_add_utf32(p_sd->hb_buffer, (const uint32_t *)p_sd->text.ptr(), p_sd->text.length(), p_start, p_end - p_start);
+
+ Vector<hb_feature_t> ftrs;
+ for (const Variant *ftr = p_sd->spans[p_span].features.next(nullptr); ftr != nullptr; ftr = p_sd->spans[p_span].features.next(ftr)) {
+ double values = p_sd->spans[p_span].features[*ftr];
+ if (values >= 0) {
+ hb_feature_t feature;
+ feature.tag = *ftr;
+ feature.value = values;
+ feature.start = 0;
+ feature.end = -1;
+ ftrs.push_back(feature);
+ }
+ }
+ hb_shape(hb_font, p_sd->hb_buffer, ftrs.empty() ? nullptr : &ftrs[0], ftrs.size());
+
+ unsigned int glyph_count = 0;
+ hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(p_sd->hb_buffer, &glyph_count);
+ hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(p_sd->hb_buffer, &glyph_count);
+
+ // Process glyphs.
+ if (glyph_count > 0) {
+ TextServer::Glyph *w = (TextServer::Glyph *)memalloc(glyph_count * sizeof(TextServer::Glyph));
+
+ int end = (p_direction == HB_DIRECTION_RTL || p_direction == HB_DIRECTION_BTT) ? p_end : 0;
+ uint32_t last_cluster_id = UINT32_MAX;
+ unsigned int last_cluster_index = 0;
+ bool last_cluster_valid = true;
+
+ for (unsigned int i = 0; i < glyph_count; i++) {
+ if ((i > 0) && (last_cluster_id != glyph_info[i].cluster)) {
+ if (p_direction == HB_DIRECTION_RTL || p_direction == HB_DIRECTION_BTT) {
+ end = w[last_cluster_index].start;
+ } else {
+ for (unsigned int j = last_cluster_index; j < i; j++) {
+ w[j].end = glyph_info[i].cluster;
+ }
+ }
+ if (p_direction == HB_DIRECTION_RTL || p_direction == HB_DIRECTION_BTT) {
+ w[last_cluster_index].flags |= TextServer::GRAPHEME_IS_RTL;
+ }
+ if (last_cluster_valid) {
+ w[last_cluster_index].flags |= GRAPHEME_IS_VALID;
+ }
+ w[last_cluster_index].count = i - last_cluster_index;
+ last_cluster_index = i;
+ last_cluster_valid = true;
+ }
+
+ last_cluster_id = glyph_info[i].cluster;
+
+ TextServer::Glyph &gl = w[i];
+ gl = TextServer::Glyph();
+
+ gl.start = glyph_info[i].cluster;
+ gl.end = end;
+ gl.count = 0;
+
+ gl.font_rid = p_fonts[p_fb_index];
+ gl.font_size = fs;
+
+ gl.index = glyph_info[i].codepoint;
+ if (gl.index != 0) {
+ if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
+ gl.advance = Math::round(glyph_pos[i].x_advance / (64.0 / fd->get_font_scale(fs)));
+ } else {
+ gl.advance = -Math::round(glyph_pos[i].y_advance / (64.0 / fd->get_font_scale(fs)));
+ }
+ gl.x_off = Math::round(glyph_pos[i].x_offset / (64.0 / fd->get_font_scale(fs)));
+ gl.y_off = -Math::round(glyph_pos[i].y_offset / (64.0 / fd->get_font_scale(fs)));
+ }
+
+ if (p_sd->preserve_control) {
+ last_cluster_valid = last_cluster_valid && ((glyph_info[i].codepoint != 0) || is_whitespace(p_sd->text[glyph_info[i].cluster]) || is_linebreak(p_sd->text[glyph_info[i].cluster]));
+ } else {
+ last_cluster_valid = last_cluster_valid && ((glyph_info[i].codepoint != 0) || !u_isgraph(p_sd->text[glyph_info[i].cluster]));
+ }
+ }
+ if (p_direction == HB_DIRECTION_LTR || p_direction == HB_DIRECTION_TTB) {
+ for (unsigned int j = last_cluster_index; j < glyph_count; j++) {
+ w[j].end = p_end;
+ }
+ }
+ w[last_cluster_index].count = glyph_count - last_cluster_index;
+ if (p_direction == HB_DIRECTION_RTL || p_direction == HB_DIRECTION_BTT) {
+ w[last_cluster_index].flags |= TextServer::GRAPHEME_IS_RTL;
+ }
+ if (last_cluster_valid) {
+ w[last_cluster_index].flags |= GRAPHEME_IS_VALID;
+ }
+
+ //Fallback.
+ int failed_subrun_start = p_end + 1;
+ int failed_subrun_end = p_start;
+
+ for (unsigned int i = 0; i < glyph_count; i++) {
+ if ((w[i].flags & GRAPHEME_IS_VALID) == GRAPHEME_IS_VALID) {
+ if (failed_subrun_start != p_end + 1) {
+ _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1);
+ failed_subrun_start = p_end + 1;
+ failed_subrun_end = p_start;
+ }
+ for (int j = 0; j < w[i].count; j++) {
+ if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
+ p_sd->ascent = MAX(p_sd->ascent, -w[i + j].y_off);
+ p_sd->descent = MAX(p_sd->descent, w[i + j].y_off);
+ } else {
+ p_sd->ascent = MAX(p_sd->ascent, Math::round(fd->get_advance(w[i + j].index, fs).x * 0.5));
+ p_sd->descent = MAX(p_sd->descent, Math::round(fd->get_advance(w[i + j].index, fs).x * 0.5));
+ }
+ p_sd->width += w[i + j].advance;
+ p_sd->glyphs.push_back(w[i + j]);
+ }
+ } else {
+ if (failed_subrun_start >= w[i].start) {
+ failed_subrun_start = w[i].start;
+ }
+ if (failed_subrun_end <= w[i].end) {
+ failed_subrun_end = w[i].end;
+ }
+ }
+ i += w[i].count - 1;
+ }
+ memfree(w);
+ if (failed_subrun_start != p_end + 1) {
+ _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1);
+ }
+ p_sd->ascent = MAX(p_sd->ascent, fd->get_ascent(fs));
+ p_sd->descent = MAX(p_sd->descent, fd->get_descent(fs));
+ p_sd->upos = MAX(p_sd->upos, fd->get_underline_position(fs));
+ p_sd->uthk = MAX(p_sd->uthk, fd->get_underline_thickness(fs));
+ }
+}
+
+bool TextServerAdvanced::shaped_text_shape(RID p_shaped) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+
+ if (sd->valid) {
+ return true;
+ }
+
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ invalidate(sd);
+
+ if (sd->text.length() == 0) {
+ sd->valid = true;
+ return true;
+ }
+
+ sd->utf16 = sd->text.utf16();
+ const UChar *data = sd->utf16.ptr();
+
+ // Create script iterator.
+ if (sd->script_iter == nullptr) {
+ sd->script_iter = memnew(ScriptIterator(sd->text, 0, sd->text.length()));
+ }
+
+ if (sd->bidi_override.empty()) {
+ sd->bidi_override.push_back(Vector2i(0, sd->end));
+ }
+
+ for (int ov = 0; ov < sd->bidi_override.size(); ov++) {
+ // Create BiDi iterator.
+ int start = _convert_pos_inv(sd, sd->bidi_override[ov].x);
+ int end = _convert_pos_inv(sd, sd->bidi_override[ov].y);
+
+ ERR_FAIL_COND_V_MSG((start < 0 || end - start > sd->utf16.length()), false, "Invalid BiDi override range.");
+
+ UErrorCode err = U_ZERO_ERROR;
+ UBiDi *bidi_iter = ubidi_openSized(end, 0, &err);
+ ERR_FAIL_COND_V_MSG(U_FAILURE(err), false, u_errorName(err));
+
+ switch (sd->direction) {
+ case DIRECTION_LTR: {
+ ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_LTR, nullptr, &err);
+ sd->para_direction = DIRECTION_LTR;
+ } break;
+ case DIRECTION_RTL: {
+ ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_RTL, nullptr, &err);
+ sd->para_direction = DIRECTION_RTL;
+ } break;
+ case DIRECTION_AUTO: {
+ UBiDiDirection direction = ubidi_getBaseDirection(data + start, end - start);
+ if (direction != UBIDI_NEUTRAL) {
+ ubidi_setPara(bidi_iter, data + start, end - start, direction, nullptr, &err);
+ sd->para_direction = (direction == UBIDI_RTL) ? DIRECTION_RTL : DIRECTION_LTR;
+ } else {
+ ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_DEFAULT_LTR, nullptr, &err);
+ sd->para_direction = DIRECTION_LTR;
+ }
+ } break;
+ }
+ ERR_FAIL_COND_V_MSG(U_FAILURE(err), false, u_errorName(err));
+ sd->bidi_iter.push_back(bidi_iter);
+
+ err = U_ZERO_ERROR;
+ int bidi_run_count = ubidi_countRuns(bidi_iter, &err);
+ ERR_FAIL_COND_V_MSG(U_FAILURE(err), false, u_errorName(err));
+ for (int i = 0; i < bidi_run_count; i++) {
+ int32_t _bidi_run_start = 0;
+ int32_t _bidi_run_length = 0;
+ hb_direction_t bidi_run_direction = HB_DIRECTION_INVALID;
+ bool is_rtl = (ubidi_getVisualRun(bidi_iter, i, &_bidi_run_start, &_bidi_run_length) == UBIDI_LTR);
+ switch (sd->orientation) {
+ case ORIENTATION_HORIZONTAL: {
+ if (is_rtl) {
+ bidi_run_direction = HB_DIRECTION_LTR;
+ } else {
+ bidi_run_direction = HB_DIRECTION_RTL;
+ }
+ } break;
+ case ORIENTATION_VERTICAL: {
+ if (is_rtl) {
+ bidi_run_direction = HB_DIRECTION_TTB;
+ } else {
+ bidi_run_direction = HB_DIRECTION_BTT;
+ }
+ }
+ }
+
+ int32_t bidi_run_start = _convert_pos(sd, sd->bidi_override[ov].x + _bidi_run_start);
+ int32_t bidi_run_end = _convert_pos(sd, sd->bidi_override[ov].x + _bidi_run_start + _bidi_run_length);
+
+ // Shape runs.
+
+ int scr_from = (is_rtl) ? 0 : sd->script_iter->script_ranges.size() - 1;
+ int scr_to = (is_rtl) ? sd->script_iter->script_ranges.size() : -1;
+ int scr_delta = (is_rtl) ? +1 : -1;
+
+ for (int j = scr_from; j != scr_to; j += scr_delta) {
+ if ((sd->script_iter->script_ranges[j].start < bidi_run_end) && (sd->script_iter->script_ranges[j].end > bidi_run_start)) {
+ int32_t script_run_start = MAX(sd->script_iter->script_ranges[j].start, bidi_run_start);
+ int32_t script_run_end = MIN(sd->script_iter->script_ranges[j].end, bidi_run_end);
+ char scr_buffer[5] = { 0, 0, 0, 0, 0 };
+ hb_tag_to_string(hb_script_to_iso15924_tag(sd->script_iter->script_ranges[j].script), scr_buffer);
+ String script = String(scr_buffer);
+
+ int spn_from = (is_rtl) ? 0 : sd->spans.size() - 1;
+ int spn_to = (is_rtl) ? sd->spans.size() : -1;
+ int spn_delta = (is_rtl) ? +1 : -1;
+
+ for (int k = spn_from; k != spn_to; k += spn_delta) {
+ const ShapedTextDataAdvanced::Span &span = sd->spans[k];
+ if (span.start >= script_run_end || span.end <= script_run_start) {
+ continue;
+ }
+ if (span.embedded_key != Variant()) {
+ // Embedded object.
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ sd->objects[span.embedded_key].rect.position.x = sd->width;
+ sd->width += sd->objects[span.embedded_key].rect.size.x;
+ switch (sd->objects[span.embedded_key].inline_align) {
+ case VALIGN_TOP: {
+ sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.y);
+ } break;
+ case VALIGN_CENTER: {
+ sd->ascent = MAX(sd->ascent, Math::round(sd->objects[span.embedded_key].rect.size.y / 2));
+ sd->descent = MAX(sd->descent, Math::round(sd->objects[span.embedded_key].rect.size.y / 2));
+ } break;
+ case VALIGN_BOTTOM: {
+ sd->descent = MAX(sd->descent, sd->objects[span.embedded_key].rect.size.y);
+ } break;
+ }
+ } else {
+ sd->objects[span.embedded_key].rect.position.y = sd->width;
+ sd->width += sd->objects[span.embedded_key].rect.size.y;
+ switch (sd->objects[span.embedded_key].inline_align) {
+ case VALIGN_TOP: {
+ sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.x);
+ } break;
+ case VALIGN_CENTER: {
+ sd->ascent = MAX(sd->ascent, Math::round(sd->objects[span.embedded_key].rect.size.x / 2));
+ sd->descent = MAX(sd->descent, Math::round(sd->objects[span.embedded_key].rect.size.x / 2));
+ } break;
+ case VALIGN_BOTTOM: {
+ sd->descent = MAX(sd->descent, sd->objects[span.embedded_key].rect.size.x);
+ } break;
+ }
+ }
+ Glyph gl;
+ gl.start = span.start;
+ gl.end = span.end;
+ gl.count = 1;
+ gl.flags = GRAPHEME_IS_VALID | GRAPHEME_IS_VIRTUAL;
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ gl.advance = sd->objects[span.embedded_key].rect.size.x;
+ } else {
+ gl.advance = sd->objects[span.embedded_key].rect.size.y;
+ }
+ sd->glyphs.push_back(gl);
+ } else {
+ Vector<RID> fonts;
+ // Push fonts with the language and script support first.
+ for (int l = 0; l < span.fonts.size(); l++) {
+ if ((font_is_language_supported(span.fonts[l], span.language)) && (font_is_script_supported(span.fonts[l], script))) {
+ fonts.push_back(sd->spans[k].fonts[l]);
+ }
+ }
+ // Push fonts with the script support.
+ for (int l = 0; l < sd->spans[k].fonts.size(); l++) {
+ if (!(font_is_language_supported(span.fonts[l], span.language)) && (font_is_script_supported(span.fonts[l], script))) {
+ fonts.push_back(sd->spans[k].fonts[l]);
+ }
+ }
+ // Push the rest valid fonts.
+ for (int l = 0; l < sd->spans[k].fonts.size(); l++) {
+ if (!(font_is_language_supported(span.fonts[l], span.language)) && !(font_is_script_supported(span.fonts[l], script))) {
+ fonts.push_back(sd->spans[k].fonts[l]);
+ }
+ }
+ _shape_run(sd, MAX(sd->spans[k].start, script_run_start), MIN(sd->spans[k].end, script_run_end), sd->script_iter->script_ranges[j].script, bidi_run_direction, fonts, k, 0);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Align embedded objects to baseline.
+ for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ switch (E->get().inline_align) {
+ case VALIGN_TOP: {
+ E->get().rect.position.y = -sd->ascent;
+ } break;
+ case VALIGN_CENTER: {
+ E->get().rect.position.y = -(E->get().rect.size.y / 2);
+ } break;
+ case VALIGN_BOTTOM: {
+ E->get().rect.position.y = sd->descent - E->get().rect.size.y;
+ } break;
+ }
+ } else {
+ switch (E->get().inline_align) {
+ case VALIGN_TOP: {
+ E->get().rect.position.x = -sd->ascent;
+ } break;
+ case VALIGN_CENTER: {
+ E->get().rect.position.x = -(E->get().rect.size.x / 2);
+ } break;
+ case VALIGN_BOTTOM: {
+ E->get().rect.position.x = sd->descent - E->get().rect.size.x;
+ } break;
+ }
+ }
+ }
+
+ sd->valid = true;
+ return sd->valid;
+}
+
+bool TextServerAdvanced::shaped_text_is_ready(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+ return sd->valid;
+}
+
+Vector<TextServer::Glyph> TextServerAdvanced::shaped_text_get_glyphs(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, Vector<TextServer::Glyph>());
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+ return sd->glyphs;
+}
+
+Vector2i TextServerAdvanced::shaped_text_get_range(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, Vector2i());
+ return Vector2(sd->start, sd->end);
+}
+
+Vector<TextServer::Glyph> TextServerAdvanced::shaped_text_sort_logical(RID p_shaped) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, Vector<TextServer::Glyph>());
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+
+ if (!sd->sort_valid) {
+ sd->glyphs_logical = sd->glyphs;
+ sd->glyphs_logical.sort_custom<TextServer::GlyphCompare>();
+ sd->sort_valid = true;
+ }
+
+ return sd->glyphs_logical;
+}
+
+Array TextServerAdvanced::shaped_text_get_objects(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ Array ret;
+ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, ret);
+ for (const Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
+ ret.push_back(E->key());
+ }
+
+ return ret;
+}
+
+Rect2 TextServerAdvanced::shaped_text_get_object_rect(RID p_shaped, Variant p_key) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, Rect2());
+ ERR_FAIL_COND_V(!sd->objects.has(p_key), Rect2());
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+ return sd->objects[p_key].rect;
+}
+
+Size2 TextServerAdvanced::shaped_text_get_size(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, Size2());
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+ if (sd->orientation == TextServer::ORIENTATION_HORIZONTAL) {
+ return Size2(sd->width, sd->ascent + sd->descent);
+ } else {
+ return Size2(sd->ascent + sd->descent, sd->width);
+ }
+}
+
+float TextServerAdvanced::shaped_text_get_ascent(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0.f);
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+ return sd->ascent;
+}
+
+float TextServerAdvanced::shaped_text_get_descent(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0.f);
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+ return sd->descent;
+}
+
+float TextServerAdvanced::shaped_text_get_width(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0.f);
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+ return sd->width;
+}
+
+float TextServerAdvanced::shaped_text_get_underline_position(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0.f);
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+
+ return sd->upos;
+}
+
+float TextServerAdvanced::shaped_text_get_underline_thickness(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0.f);
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+
+ return sd->uthk;
+}
+
+struct num_system_data {
+ String lang;
+ String digits;
+ String percent_sign;
+ String exp;
+};
+
+static num_system_data num_systems[]{
+ { "ar,ar_AR,ar_BH,ar_DJ,ar_EG,ar_ER,ar_IL,ar_IQ,ar_JO,ar_KM,ar_KW,ar_LB,ar_MR,ar_OM,ar_PS,ar_QA,ar_SA,ar_SD,ar_SO,ar_SS,ar_SY,ar_TD,ar_YE", U"٠١٢٣٤٥٦٧٨٩٫", U"٪", U"اس" },
+ { "fa,ks,pa_Arab,ps,ug,ur_IN,ur,uz_Arab", U"۰۱۲۳۴۵۶۷۸۹٫", U"٪", U"اس" },
+ { "as,bn,mni", U"০১২৩৪৫৬৭৮৯.", U"%", U"e" },
+ { "mr,ne", U"०१२३४५६७८९.", U"%", U"e" },
+ { "dz", U"༠༡༢༣༤༥༦༧༨༩.", U"%", U"e" },
+ { "sat", U"᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙.", U"%", U"e" },
+ { "my", U"၀၁၂၃၄၅၆၇၈၉.", U"%", U"e" },
+ { String(), String(), String(), String() },
+};
+
+String TextServerAdvanced::format_number(const String &p_string, const String &p_language) const {
+ _THREAD_SAFE_METHOD_
+ String lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
+
+ String res = p_string;
+ for (int i = 0; num_systems[i].lang != String(); i++) {
+ Vector<String> langs = num_systems[i].lang.split(",");
+ if (langs.has(lang)) {
+ if (num_systems[i].digits == String()) {
+ return p_string;
+ }
+ res.replace("e", num_systems[i].exp);
+ res.replace("E", num_systems[i].exp);
+ char32_t *data = res.ptrw();
+ for (int j = 0; j < res.size(); j++) {
+ if (data[j] >= 0x30 && data[j] <= 0x39) {
+ data[j] = num_systems[i].digits[data[j] - 0x30];
+ } else if (data[j] == '.' || data[j] == ',') {
+ data[j] = num_systems[i].digits[10];
+ }
+ }
+ break;
+ }
+ }
+ return res;
+}
+
+String TextServerAdvanced::parse_number(const String &p_string, const String &p_language) const {
+ _THREAD_SAFE_METHOD_
+ String lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
+
+ String res = p_string;
+ for (int i = 0; num_systems[i].lang != String(); i++) {
+ Vector<String> langs = num_systems[i].lang.split(",");
+ if (langs.has(lang)) {
+ if (num_systems[i].digits == String()) {
+ return p_string;
+ }
+ res.replace(num_systems[i].exp, "e");
+ char32_t *data = res.ptrw();
+ for (int j = 0; j < res.size(); j++) {
+ if (data[j] == num_systems[i].digits[10]) {
+ data[j] = '.';
+ } else {
+ for (int k = 0; k < 10; k++) {
+ if (data[j] == num_systems[i].digits[k]) {
+ data[j] = 0x30 + k;
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ return res;
+}
+
+String TextServerAdvanced::percent_sign(const String &p_language) const {
+ _THREAD_SAFE_METHOD_
+ String lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
+
+ for (int i = 0; num_systems[i].lang != String(); i++) {
+ Vector<String> langs = num_systems[i].lang.split(",");
+ if (langs.has(lang)) {
+ if (num_systems[i].percent_sign == String()) {
+ return "%";
+ }
+ return num_systems[i].percent_sign;
+ }
+ }
+ return "%";
+}
+
+TextServer *TextServerAdvanced::create_func(Error &r_error, void *p_user_data) {
+ r_error = OK;
+ return memnew(TextServerAdvanced());
+}
+
+void TextServerAdvanced::register_server() {
+ TextServerManager::register_create_function(interface_name, interface_features, create_func, nullptr);
+}
+
+TextServerAdvanced::TextServerAdvanced() {
+ hb_bmp_create_font_funcs();
+}
+
+TextServerAdvanced::~TextServerAdvanced() {
+ hb_bmp_free_font_funcs();
+ u_cleanup();
+#ifndef ICU_STATIC_DATA
+ if (icu_data != nullptr) {
+ memfree(icu_data);
+ icu_data = nullptr;
+ }
+#endif
+}
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
new file mode 100644
index 0000000000..8c26554158
--- /dev/null
+++ b/modules/text_server_adv/text_server_adv.h
@@ -0,0 +1,248 @@
+/*************************************************************************/
+/* text_server_adv.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TEXT_SERVER_ADV_H
+#define TEXT_SERVER_ADV_H
+
+/*************************************************************************/
+/* ICU/HarfBuzz/Graphite backed Text Server implementation with BiDi, */
+/* shaping and advanced font features support. */
+/*************************************************************************/
+
+#include "servers/text_server.h"
+
+#include "core/templates/rid_owner.h"
+#include "scene/resources/texture.h"
+#include "script_iterator.h"
+
+#include <unicode/ubidi.h>
+#include <unicode/ubrk.h>
+#include <unicode/uchar.h>
+#include <unicode/uclean.h>
+#include <unicode/udata.h>
+#include <unicode/uiter.h>
+#include <unicode/uloc.h>
+#include <unicode/uscript.h>
+#include <unicode/ustring.h>
+#include <unicode/utypes.h>
+
+#include <hb-icu.h>
+#include <hb.h>
+
+#include "font_adv.h"
+
+class TextServerAdvanced : public TextServer {
+ GDCLASS(TextServerAdvanced, TextServer);
+ _THREAD_SAFE_CLASS_
+
+ static String interface_name;
+ static uint32_t interface_features;
+
+ uint8_t *icu_data = nullptr;
+
+ struct ShapedTextDataAdvanced : public ShapedTextData {
+ /* Intermediate data */
+ Char16String utf16;
+ Vector<UBiDi *> bidi_iter;
+ Vector<Vector2i> bidi_override;
+ ScriptIterator *script_iter = nullptr;
+ hb_buffer_t *hb_buffer = nullptr;
+
+ ~ShapedTextDataAdvanced() {
+ for (int i = 0; i < bidi_iter.size(); i++) {
+ ubidi_close(bidi_iter[i]);
+ }
+ if (script_iter) {
+ memdelete(script_iter);
+ }
+ if (hb_buffer) {
+ hb_buffer_destroy(hb_buffer);
+ }
+ }
+ };
+
+ float oversampling = 1.f;
+ mutable RID_PtrOwner<FontDataAdvanced> font_owner;
+ mutable RID_PtrOwner<ShapedTextDataAdvanced> shaped_owner;
+
+ int _convert_pos(const ShapedTextDataAdvanced *p_sd, int p_pos) const;
+ int _convert_pos_inv(const ShapedTextDataAdvanced *p_sd, int p_pos) const;
+ void _shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_start, int32_t p_end, hb_script_t p_script, hb_direction_t p_direction, Vector<RID> p_fonts, int p_span, int p_fb_index);
+ TextServer::Glyph _shape_single_glyph(ShapedTextDataAdvanced *p_sd, char32_t p_char, hb_script_t p_script, hb_direction_t p_direction, RID p_font, int p_font_size);
+
+protected:
+ static void _bind_methods(){};
+
+ void full_copy(ShapedTextDataAdvanced *p_shaped);
+ void invalidate(ShapedTextDataAdvanced *p_shaped);
+
+public:
+ virtual bool has_feature(Feature p_feature) override;
+ virtual String get_name() const override;
+
+ virtual void free(RID p_rid) override;
+ virtual bool has(RID p_rid) override;
+ virtual bool load_support_data(const String &p_filename) override;
+
+#ifdef TOOLS_ENABLED
+ virtual String get_support_data_filename() override { return _MKSTR(ICU_DATA_NAME); };
+ virtual String get_support_data_info() override { return String("ICU break iteration data (") + _MKSTR(ICU_DATA_NAME) + String(")."); };
+ virtual bool save_support_data(const String &p_filename) override;
+#endif
+
+ virtual bool is_locale_right_to_left(const String &p_locale) override;
+
+ virtual int32_t name_to_tag(const String &p_name) override;
+ virtual String tag_to_name(int32_t p_tag) override;
+
+ /* Font interface */
+ virtual RID create_font_system(const String &p_name, int p_base_size = 16) override;
+ virtual RID create_font_resource(const String &p_filename, int p_base_size = 16) override;
+ virtual RID create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16) override;
+
+ virtual float font_get_height(RID p_font, int p_size) const override;
+ virtual float font_get_ascent(RID p_font, int p_size) const override;
+ virtual float font_get_descent(RID p_font, int p_size) const override;
+
+ virtual float font_get_underline_position(RID p_font, int p_size) const override;
+ virtual float font_get_underline_thickness(RID p_font, int p_size) const override;
+
+ virtual void font_set_antialiased(RID p_font, bool p_antialiased) override;
+ virtual bool font_get_antialiased(RID p_font) const override;
+
+ virtual Dictionary font_get_feature_list(RID p_font) const override;
+ virtual Dictionary font_get_variation_list(RID p_font) const override;
+
+ virtual void font_set_variation(RID p_font, const String &p_name, double p_value) override;
+ virtual double font_get_variation(RID p_font, const String &p_name) const override;
+
+ virtual void font_set_hinting(RID p_font, Hinting p_hinting) override;
+ virtual Hinting font_get_hinting(RID p_font) const override;
+
+ virtual void font_set_distance_field_hint(RID p_font, bool p_distance_field) override;
+ virtual bool font_get_distance_field_hint(RID p_font) const override;
+
+ virtual void font_set_force_autohinter(RID p_font, bool p_enabeld) override;
+ virtual bool font_get_force_autohinter(RID p_font) const override;
+
+ virtual bool font_has_char(RID p_font, char32_t p_char) const override;
+ virtual String font_get_supported_chars(RID p_font) const override;
+
+ virtual bool font_has_outline(RID p_font) const override;
+ virtual float font_get_base_size(RID p_font) const override;
+
+ virtual bool font_is_language_supported(RID p_font, const String &p_language) const override;
+ virtual void font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) override;
+ virtual bool font_get_language_support_override(RID p_font, const String &p_language) override;
+ virtual void font_remove_language_support_override(RID p_font, const String &p_language) override;
+ Vector<String> font_get_language_support_overrides(RID p_font) override;
+
+ virtual bool font_is_script_supported(RID p_font, const String &p_script) const override;
+ virtual void font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) override;
+ virtual bool font_get_script_support_override(RID p_font, const String &p_script) override;
+ virtual void font_remove_script_support_override(RID p_font, const String &p_script) override;
+ Vector<String> font_get_script_support_overrides(RID p_font) override;
+
+ virtual uint32_t font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector = 0x0000) const override;
+ virtual Vector2 font_get_glyph_advance(RID p_font, uint32_t p_index, int p_size) const override;
+ virtual Vector2 font_get_glyph_kerning(RID p_font, uint32_t p_index_a, uint32_t p_index_b, int p_size) const override;
+
+ virtual Vector2 font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
+ virtual Vector2 font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
+
+ virtual float font_get_oversampling() const override;
+ virtual void font_set_oversampling(float p_oversampling) override;
+
+ virtual Vector<String> get_system_fonts() const override;
+
+ /* Shaped text buffer interface */
+
+ virtual RID create_shaped_text(Direction p_direction = DIRECTION_AUTO, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
+
+ virtual void shaped_text_clear(RID p_shaped) override;
+
+ virtual void shaped_text_set_direction(RID p_shaped, Direction p_direction = DIRECTION_AUTO) override;
+ virtual Direction shaped_text_get_direction(RID p_shaped) const override;
+
+ virtual void shaped_text_set_bidi_override(RID p_shaped, const Vector<Vector2i> &p_override) override;
+
+ virtual void shaped_text_set_orientation(RID p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
+ virtual Orientation shaped_text_get_orientation(RID p_shaped) const override;
+
+ virtual void shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) override;
+ virtual bool shaped_text_get_preserve_invalid(RID p_shaped) const override;
+
+ virtual void shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) override;
+ virtual bool shaped_text_get_preserve_control(RID p_shaped) const override;
+
+ virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") override;
+ virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER, int p_length = 1) override;
+ virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER) override;
+
+ virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override;
+ virtual RID shaped_text_get_parent(RID p_shaped) const override;
+
+ virtual float shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
+ virtual float shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) override;
+
+ virtual bool shaped_text_shape(RID p_shaped) override;
+ virtual bool shaped_text_update_breaks(RID p_shaped) override;
+ virtual bool shaped_text_update_justification_ops(RID p_shaped) override;
+
+ virtual bool shaped_text_is_ready(RID p_shaped) const override;
+
+ virtual Vector<Glyph> shaped_text_get_glyphs(RID p_shaped) const override;
+
+ virtual Vector2i shaped_text_get_range(RID p_shaped) const override;
+
+ virtual Vector<Glyph> shaped_text_sort_logical(RID p_shaped) override;
+
+ virtual Array shaped_text_get_objects(RID p_shaped) const override;
+ virtual Rect2 shaped_text_get_object_rect(RID p_shaped, Variant p_key) const override;
+
+ virtual Size2 shaped_text_get_size(RID p_shaped) const override;
+ virtual float shaped_text_get_ascent(RID p_shaped) const override;
+ virtual float shaped_text_get_descent(RID p_shaped) const override;
+ virtual float shaped_text_get_width(RID p_shaped) const override;
+ virtual float shaped_text_get_underline_position(RID p_shaped) const override;
+ virtual float shaped_text_get_underline_thickness(RID p_shaped) const override;
+
+ virtual String format_number(const String &p_string, const String &p_language = "") const override;
+ virtual String parse_number(const String &p_string, const String &p_language = "") const override;
+ virtual String percent_sign(const String &p_language = "") const override;
+
+ static TextServer *create_func(Error &r_error, void *p_user_data);
+ static void register_server();
+
+ TextServerAdvanced();
+ ~TextServerAdvanced();
+};
+
+#endif // TEXT_SERVER_ADV_H
diff --git a/modules/text_server_fb/SCsub b/modules/text_server_fb/SCsub
new file mode 100644
index 0000000000..03eccbe7bd
--- /dev/null
+++ b/modules/text_server_fb/SCsub
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_text_server_fb = env_modules.Clone()
+env_text_server_fb.Append(
+ CPPPATH=[
+ "#thirdparty/freetype/include",
+ ]
+)
+
+env_text_server_fb.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/text_server_fb/bitmap_font_fb.cpp b/modules/text_server_fb/bitmap_font_fb.cpp
new file mode 100644
index 0000000000..99cbccb69a
--- /dev/null
+++ b/modules/text_server_fb/bitmap_font_fb.cpp
@@ -0,0 +1,356 @@
+/*************************************************************************/
+/* bitmap_font_fb.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "bitmap_font_fb.h"
+
+Error BitmapFontDataFallback::load_from_file(const String &p_filename, int p_base_size) {
+ _THREAD_SAFE_METHOD_
+ //fnt format used by angelcode bmfont
+ //http://www.angelcode.com/products/bmfont/
+
+ FileAccess *f = FileAccess::open(p_filename, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(!f, ERR_FILE_NOT_FOUND, "Can't open font: " + p_filename + ".");
+
+ while (true) {
+ String line = f->get_line();
+
+ int delimiter = line.find(" ");
+ String type = line.substr(0, delimiter);
+ int pos = delimiter + 1;
+ Map<String, String> keys;
+
+ while (pos < line.size() && line[pos] == ' ') {
+ pos++;
+ }
+
+ while (pos < line.size()) {
+ int eq = line.find("=", pos);
+ if (eq == -1) {
+ break;
+ }
+ String key = line.substr(pos, eq - pos);
+ int end = -1;
+ String value;
+ if (line[eq + 1] == '"') {
+ end = line.find("\"", eq + 2);
+ if (end == -1) {
+ break;
+ }
+ value = line.substr(eq + 2, end - 1 - eq - 1);
+ pos = end + 1;
+ } else {
+ end = line.find(" ", eq + 1);
+ if (end == -1) {
+ end = line.size();
+ }
+ value = line.substr(eq + 1, end - eq);
+ pos = end;
+ }
+
+ while (pos < line.size() && line[pos] == ' ') {
+ pos++;
+ }
+
+ keys[key] = value;
+ }
+
+ if (type == "info") {
+ if (keys.has("size")) {
+ base_size = keys["size"].to_int();
+ }
+ } else if (type == "common") {
+ if (keys.has("lineHeight")) {
+ height = keys["lineHeight"].to_int();
+ }
+ if (keys.has("base")) {
+ ascent = keys["base"].to_int();
+ }
+ } else if (type == "page") {
+ if (keys.has("file")) {
+ String base_dir = p_filename.get_base_dir();
+ String file = base_dir.plus_file(keys["file"]);
+ if (RenderingServer::get_singleton() != nullptr) {
+ Ref<Texture2D> tex = ResourceLoader::load(file);
+ if (tex.is_null()) {
+ ERR_PRINT("Can't load font texture!");
+ } else {
+ ERR_FAIL_COND_V_MSG(tex.is_null(), ERR_FILE_CANT_READ, "It's not a reference to a valid Texture object.");
+ textures.push_back(tex);
+ }
+ }
+ }
+ } else if (type == "char") {
+ Character c;
+ char32_t idx = 0;
+ if (keys.has("id")) {
+ idx = keys["id"].to_int();
+ }
+ if (keys.has("x")) {
+ c.rect.position.x = keys["x"].to_int();
+ }
+ if (keys.has("y")) {
+ c.rect.position.y = keys["y"].to_int();
+ }
+ if (keys.has("width")) {
+ c.rect.size.width = keys["width"].to_int();
+ }
+ if (keys.has("height")) {
+ c.rect.size.height = keys["height"].to_int();
+ }
+ if (keys.has("xoffset")) {
+ c.align.x = keys["xoffset"].to_int();
+ }
+ if (keys.has("yoffset")) {
+ c.align.y = keys["yoffset"].to_int();
+ }
+ if (keys.has("page")) {
+ c.texture_idx = keys["page"].to_int();
+ }
+ if (keys.has("xadvance")) {
+ c.advance.x = keys["xadvance"].to_int();
+ }
+ if (keys.has("yadvance")) {
+ c.advance.y = keys["yadvance"].to_int();
+ }
+ if (c.advance.x < 0) {
+ c.advance.x = c.rect.size.width + 1;
+ }
+ if (c.advance.y < 0) {
+ c.advance.y = c.rect.size.height + 1;
+ }
+ char_map[idx] = c;
+ } else if (type == "kerning") {
+ KerningPairKey kpk;
+ float k = 0;
+ if (keys.has("first")) {
+ kpk.A = keys["first"].to_int();
+ }
+ if (keys.has("second")) {
+ kpk.B = keys["second"].to_int();
+ }
+ if (keys.has("amount")) {
+ k = keys["amount"].to_int();
+ }
+ kerning_map[kpk] = k;
+ }
+
+ if (f->eof_reached()) {
+ break;
+ }
+ }
+ if (base_size == 0) {
+ base_size = height;
+ }
+
+ valid = true;
+
+ memdelete(f);
+ return OK;
+}
+
+Error BitmapFontDataFallback::load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND_V(p_data == nullptr, ERR_CANT_CREATE);
+ ERR_FAIL_COND_V(p_size != sizeof(TextServer::BitmapFontData), ERR_CANT_CREATE);
+
+ const TextServer::BitmapFontData *data = (const TextServer::BitmapFontData *)p_data;
+
+ if (RenderingServer::get_singleton() != nullptr) {
+ Ref<Image> image = memnew(Image(data->img));
+ Ref<ImageTexture> tex = memnew(ImageTexture);
+ tex->create_from_image(image);
+
+ textures.push_back(tex);
+ }
+
+ for (int i = 0; i < data->charcount; i++) {
+ const int *c = &data->char_rects[i * 8];
+
+ Character chr;
+ chr.rect.position.x = c[1];
+ chr.rect.position.y = c[2];
+ chr.rect.size.x = c[3];
+ chr.rect.size.y = c[4];
+ if (c[7] < 0) {
+ chr.advance.x = c[3];
+ } else {
+ chr.advance.x = c[7];
+ }
+ chr.align = Vector2(c[6], c[5]);
+ char_map[c[0]] = chr;
+ }
+
+ for (int i = 0; i < data->kerning_count; i++) {
+ KerningPairKey kpk;
+ kpk.A = data->kernings[i * 3 + 0];
+ kpk.B = data->kernings[i * 3 + 1];
+
+ if (data->kernings[i * 3 + 2] == 0 && kerning_map.has(kpk)) {
+ kerning_map.erase(kpk);
+ } else {
+ kerning_map[kpk] = data->kernings[i * 3 + 2];
+ }
+ }
+
+ height = data->height;
+ ascent = data->ascent;
+
+ base_size = p_base_size;
+ if (base_size == 0) {
+ base_size = height;
+ }
+
+ valid = true;
+
+ return OK;
+}
+
+float BitmapFontDataFallback::get_height(int p_size) const {
+ ERR_FAIL_COND_V(!valid, 0.f);
+ return height * (float(p_size) / float(base_size));
+}
+
+float BitmapFontDataFallback::get_ascent(int p_size) const {
+ ERR_FAIL_COND_V(!valid, 0.f);
+ return ascent * (float(p_size) / float(base_size));
+}
+
+float BitmapFontDataFallback::get_descent(int p_size) const {
+ ERR_FAIL_COND_V(!valid, 0.f);
+ return (height - ascent) * (float(p_size) / float(base_size));
+}
+
+float BitmapFontDataFallback::get_underline_position(int p_size) const {
+ ERR_FAIL_COND_V(!valid, 0.f);
+ return 2 * (float(p_size) / float(base_size));
+}
+
+float BitmapFontDataFallback::get_underline_thickness(int p_size) const {
+ ERR_FAIL_COND_V(!valid, 0.f);
+ return 1 * (float(p_size) / float(base_size));
+}
+
+void BitmapFontDataFallback::set_distance_field_hint(bool p_distance_field) {
+ distance_field_hint = p_distance_field;
+}
+
+bool BitmapFontDataFallback::get_distance_field_hint() const {
+ return distance_field_hint;
+}
+
+float BitmapFontDataFallback::get_base_size() const {
+ return base_size;
+}
+
+bool BitmapFontDataFallback::has_char(char32_t p_char) const {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND_V(!valid, false);
+ return char_map.has(p_char);
+}
+
+String BitmapFontDataFallback::get_supported_chars() const {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND_V(!valid, String());
+ String chars;
+ const char32_t *k = nullptr;
+ while ((k = char_map.next(k))) {
+ chars += char32_t(*k);
+ }
+ return chars;
+}
+
+Vector2 BitmapFontDataFallback::get_advance(char32_t p_char, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND_V(!valid, Vector2());
+ const Character *c = char_map.getptr(p_char);
+ ERR_FAIL_COND_V(c == nullptr, Vector2());
+
+ return c->advance * (float(p_size) / float(base_size));
+}
+
+Vector2 BitmapFontDataFallback::get_kerning(char32_t p_char, char32_t p_next, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND_V(!valid, Vector2());
+ KerningPairKey kpk;
+ kpk.A = p_char;
+ kpk.B = p_next;
+
+ const Map<KerningPairKey, int>::Element *E = kerning_map.find(kpk);
+ if (E) {
+ return Vector2(-E->get() * (float(p_size) / float(base_size)), 0);
+ } else {
+ return Vector2();
+ }
+}
+
+Vector2 BitmapFontDataFallback::draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
+ _THREAD_SAFE_METHOD_
+ if (p_index == 0) {
+ return Vector2();
+ }
+ ERR_FAIL_COND_V(!valid, Vector2());
+ const Character *c = char_map.getptr(p_index);
+
+ ERR_FAIL_COND_V(c == nullptr, Vector2());
+ ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), Vector2());
+ if (c->texture_idx != -1) {
+ Point2 cpos = p_pos;
+ cpos += c->align * (float(p_size) / float(base_size));
+ cpos.y -= ascent * (float(p_size) / float(base_size));
+
+ if (RenderingServer::get_singleton() != nullptr) {
+ //if (distance_field_hint) { // Not implemented.
+ // RenderingServer::get_singleton()->canvas_item_set_distance_field_mode(p_canvas, true);
+ //}
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, c->rect.size * (float(p_size) / float(base_size))), textures[c->texture_idx]->get_rid(), c->rect, p_color, false, false);
+ //if (distance_field_hint) {
+ // RenderingServer::get_singleton()->canvas_item_set_distance_field_mode(p_canvas, false);
+ //}
+ }
+ }
+
+ return c->advance * (float(p_size) / float(base_size));
+}
+
+Vector2 BitmapFontDataFallback::draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
+ _THREAD_SAFE_METHOD_
+ if (p_index == 0) {
+ return Vector2();
+ }
+ ERR_FAIL_COND_V(!valid, Vector2());
+ const Character *c = char_map.getptr(p_index);
+
+ ERR_FAIL_COND_V(c == nullptr, Vector2());
+ ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), Vector2());
+
+ // Not supported, return advance for compatibility.
+
+ return c->advance * (float(p_size) / float(base_size));
+}
diff --git a/modules/text_server_fb/bitmap_font_fb.h b/modules/text_server_fb/bitmap_font_fb.h
new file mode 100644
index 0000000000..73e6d8f791
--- /dev/null
+++ b/modules/text_server_fb/bitmap_font_fb.h
@@ -0,0 +1,107 @@
+/*************************************************************************/
+/* bitmap_font_fb.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef BITMAP_FONT_FALLBACK_H
+#define BITMAP_FONT_FALLBACK_H
+
+#include "font_fb.h"
+
+struct BitmapFontDataFallback : public FontDataFallback {
+ _THREAD_SAFE_CLASS_
+
+private:
+ Vector<Ref<Texture2D>> textures;
+
+ struct Character {
+ int texture_idx = 0;
+ Rect2 rect;
+ Vector2 align;
+ Vector2 advance = Vector2(-1, -1);
+ };
+
+ struct KerningPairKey {
+ union {
+ struct {
+ uint32_t A, B;
+ };
+
+ uint64_t pair = 0;
+ };
+
+ _FORCE_INLINE_ bool operator<(const KerningPairKey &p_r) const { return pair < p_r.pair; }
+ };
+
+ HashMap<char32_t, Character> char_map;
+ Map<KerningPairKey, int> kerning_map;
+
+ float height = 0.f;
+ float ascent = 0.f;
+ int base_size = 0;
+ bool distance_field_hint = false;
+
+public:
+ virtual void clear_cache() override{};
+
+ virtual Error load_from_file(const String &p_filename, int p_base_size) override;
+ virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) override;
+
+ virtual float get_height(int p_size) const override;
+ virtual float get_ascent(int p_size) const override;
+ virtual float get_descent(int p_size) const override;
+
+ virtual float get_underline_position(int p_size) const override;
+ virtual float get_underline_thickness(int p_size) const override;
+
+ virtual void set_antialiased(bool p_antialiased) override{};
+ virtual bool get_antialiased() const override { return false; };
+
+ virtual void set_hinting(TextServer::Hinting p_hinting) override{};
+ virtual TextServer::Hinting get_hinting() const override { return TextServer::HINTING_NONE; };
+
+ virtual void set_distance_field_hint(bool p_distance_field) override;
+ virtual bool get_distance_field_hint() const override;
+
+ virtual void set_force_autohinter(bool p_enabeld) override{};
+ virtual bool get_force_autohinter() const override { return false; };
+
+ virtual bool has_outline() const override { return false; };
+ virtual float get_base_size() const override;
+
+ virtual bool has_char(char32_t p_char) const override;
+ virtual String get_supported_chars() const override;
+
+ virtual Vector2 get_advance(char32_t p_char, int p_size) const override;
+ virtual Vector2 get_kerning(char32_t p_char, char32_t p_next, int p_size) const override;
+
+ virtual Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override;
+ virtual Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override;
+};
+
+#endif // BITMAP_FONT_FALLBACK_H
diff --git a/modules/text_server_fb/config.py b/modules/text_server_fb/config.py
new file mode 100644
index 0000000000..491377a369
--- /dev/null
+++ b/modules/text_server_fb/config.py
@@ -0,0 +1,11 @@
+def can_build(env, platform):
+ return env.module_check_dependencies("text_server_fb", ["freetype"])
+
+
+def configure(env):
+ pass
+
+
+def is_enabled():
+ # The module is disabled by default. Use module_text_server_fb_enabled=yes to enable it.
+ return False
diff --git a/modules/text_server_fb/dynamic_font_fb.cpp b/modules/text_server_fb/dynamic_font_fb.cpp
new file mode 100644
index 0000000000..6731870e8f
--- /dev/null
+++ b/modules/text_server_fb/dynamic_font_fb.cpp
@@ -0,0 +1,685 @@
+/*************************************************************************/
+/* dynamic_font_fb.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "dynamic_font_fb.h"
+
+#include FT_STROKER_H
+#include FT_ADVANCES_H
+
+DynamicFontDataFallback::DataAtSize *DynamicFontDataFallback::get_data_for_size(int p_size, int p_outline_size) {
+ ERR_FAIL_COND_V(!valid, nullptr);
+ ERR_FAIL_COND_V(p_size < 0 || p_size > UINT16_MAX, nullptr);
+ ERR_FAIL_COND_V(p_outline_size < 0 || p_outline_size > UINT16_MAX, nullptr);
+
+ CacheID id;
+ id.size = p_size;
+ id.outline_size = p_outline_size;
+
+ DataAtSize *fds = nullptr;
+ Map<CacheID, DataAtSize *>::Element *E = nullptr;
+ if (p_outline_size != 0) {
+ E = size_cache_outline.find(id);
+ } else {
+ E = size_cache.find(id);
+ }
+
+ if (E != nullptr) {
+ fds = E->get();
+ } else {
+ if (font_mem == nullptr && font_path != String()) {
+ if (!font_mem_cache.empty()) {
+ font_mem = font_mem_cache.ptr();
+ font_mem_size = font_mem_cache.size();
+ } else {
+ FileAccess *f = FileAccess::open(font_path, FileAccess::READ);
+ if (!f) {
+ ERR_FAIL_V_MSG(nullptr, "Cannot open font file '" + font_path + "'.");
+ }
+
+ size_t len = f->get_len();
+ font_mem_cache.resize(len);
+ f->get_buffer(font_mem_cache.ptrw(), len);
+ font_mem = font_mem_cache.ptr();
+ font_mem_size = len;
+ f->close();
+ }
+ }
+
+ int error = 0;
+ fds = memnew(DataAtSize);
+ if (font_mem) {
+ memset(&fds->stream, 0, sizeof(FT_StreamRec));
+ fds->stream.base = (unsigned char *)font_mem;
+ fds->stream.size = font_mem_size;
+ fds->stream.pos = 0;
+
+ FT_Open_Args fargs;
+ memset(&fargs, 0, sizeof(FT_Open_Args));
+ fargs.memory_base = (unsigned char *)font_mem;
+ fargs.memory_size = font_mem_size;
+ fargs.flags = FT_OPEN_MEMORY;
+ fargs.stream = &fds->stream;
+ error = FT_Open_Face(library, &fargs, 0, &fds->face);
+
+ } else {
+ memdelete(fds);
+ ERR_FAIL_V_MSG(nullptr, "DynamicFont uninitialized.");
+ }
+
+ if (error == FT_Err_Unknown_File_Format) {
+ memdelete(fds);
+ ERR_FAIL_V_MSG(nullptr, "Unknown font format.");
+
+ } else if (error) {
+ memdelete(fds);
+ ERR_FAIL_V_MSG(nullptr, "Error loading font.");
+ }
+
+ oversampling = TS->font_get_oversampling();
+
+ if (FT_HAS_COLOR(fds->face) && fds->face->num_fixed_sizes > 0) {
+ int best_match = 0;
+ int diff = ABS(p_size - ((int64_t)fds->face->available_sizes[0].width));
+ fds->scale_color_font = float(p_size * oversampling) / fds->face->available_sizes[0].width;
+ for (int i = 1; i < fds->face->num_fixed_sizes; i++) {
+ int ndiff = ABS(p_size - ((int64_t)fds->face->available_sizes[i].width));
+ if (ndiff < diff) {
+ best_match = i;
+ diff = ndiff;
+ fds->scale_color_font = float(p_size * oversampling) / fds->face->available_sizes[i].width;
+ }
+ }
+ FT_Select_Size(fds->face, best_match);
+ } else {
+ FT_Set_Pixel_Sizes(fds->face, 0, p_size * oversampling);
+ }
+
+ fds->size = p_size;
+ fds->ascent = (fds->face->size->metrics.ascender / 64.0) / oversampling * fds->scale_color_font;
+ fds->descent = (-fds->face->size->metrics.descender / 64.0) / oversampling * fds->scale_color_font;
+ fds->underline_position = -fds->face->underline_position / 64.0 / oversampling * fds->scale_color_font;
+ fds->underline_thickness = fds->face->underline_thickness / 64.0 / oversampling * fds->scale_color_font;
+ if (p_outline_size != 0) {
+ size_cache_outline[id] = fds;
+ } else {
+ size_cache[id] = fds;
+ }
+ }
+
+ return fds;
+}
+
+DynamicFontDataFallback::TexturePosition DynamicFontDataFallback::find_texture_pos_for_glyph(DynamicFontDataFallback::DataAtSize *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) {
+ TexturePosition ret;
+ ret.index = -1;
+
+ int mw = p_width;
+ int mh = p_height;
+
+ for (int i = 0; i < p_data->textures.size(); i++) {
+ const CharTexture &ct = p_data->textures[i];
+
+ if (RenderingServer::get_singleton() != nullptr) {
+ if (ct.texture->get_format() != p_image_format) {
+ continue;
+ }
+ }
+
+ if (mw > ct.texture_size || mh > ct.texture_size) { //too big for this texture
+ continue;
+ }
+
+ ret.y = 0x7FFFFFFF;
+ ret.x = 0;
+
+ for (int j = 0; j < ct.texture_size - mw; j++) {
+ int max_y = 0;
+
+ for (int k = j; k < j + mw; k++) {
+ int y = ct.offsets[k];
+ if (y > max_y) {
+ max_y = y;
+ }
+ }
+
+ if (max_y < ret.y) {
+ ret.y = max_y;
+ ret.x = j;
+ }
+ }
+
+ if (ret.y == 0x7FFFFFFF || ret.y + mh > ct.texture_size) {
+ continue; //fail, could not fit it here
+ }
+
+ ret.index = i;
+ break;
+ }
+
+ if (ret.index == -1) {
+ //could not find texture to fit, create one
+ ret.x = 0;
+ ret.y = 0;
+
+ int texsize = MAX(p_data->size * oversampling * 8, 256);
+ if (mw > texsize) {
+ texsize = mw; //special case, adapt to it?
+ }
+ if (mh > texsize) {
+ texsize = mh; //special case, adapt to it?
+ }
+
+ texsize = next_power_of_2(texsize);
+
+ texsize = MIN(texsize, 4096);
+
+ CharTexture tex;
+ tex.texture_size = texsize;
+ tex.imgdata.resize(texsize * texsize * p_color_size); //grayscale alpha
+
+ {
+ //zero texture
+ uint8_t *w = tex.imgdata.ptrw();
+ ERR_FAIL_COND_V(texsize * texsize * p_color_size > tex.imgdata.size(), ret);
+ // Initialize the texture to all-white pixels to prevent artifacts when the
+ // font is displayed at a non-default scale with filtering enabled.
+ if (p_color_size == 2) {
+ for (int i = 0; i < texsize * texsize * p_color_size; i += 2) { // FORMAT_LA8
+ w[i + 0] = 255;
+ w[i + 1] = 0;
+ }
+ } else if (p_color_size == 4) {
+ for (int i = 0; i < texsize * texsize * p_color_size; i += 4) { // FORMAT_RGBA8
+ w[i + 0] = 255;
+ w[i + 1] = 255;
+ w[i + 2] = 255;
+ w[i + 3] = 0;
+ }
+ } else {
+ ERR_FAIL_V(ret);
+ }
+ }
+ tex.offsets.resize(texsize);
+ for (int i = 0; i < texsize; i++) { //zero offsets
+ tex.offsets.write[i] = 0;
+ }
+
+ p_data->textures.push_back(tex);
+ ret.index = p_data->textures.size() - 1;
+ }
+
+ return ret;
+}
+
+DynamicFontDataFallback::Character DynamicFontDataFallback::Character::not_found() {
+ Character ch;
+ return ch;
+}
+
+DynamicFontDataFallback::Character DynamicFontDataFallback::bitmap_to_character(DynamicFontDataFallback::DataAtSize *p_data, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) {
+ int w = bitmap.width;
+ int h = bitmap.rows;
+
+ int mw = w + rect_margin * 2;
+ int mh = h + rect_margin * 2;
+
+ ERR_FAIL_COND_V(mw > 4096, Character::not_found());
+ ERR_FAIL_COND_V(mh > 4096, Character::not_found());
+
+ int color_size = bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 2;
+ Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8;
+
+ TexturePosition tex_pos = find_texture_pos_for_glyph(p_data, color_size, require_format, mw, mh);
+ ERR_FAIL_COND_V(tex_pos.index < 0, Character::not_found());
+
+ //fit character in char texture
+
+ CharTexture &tex = p_data->textures.write[tex_pos.index];
+
+ {
+ uint8_t *wr = tex.imgdata.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs = ((i + tex_pos.y + rect_margin) * tex.texture_size + j + tex_pos.x + rect_margin) * color_size;
+ ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), Character::not_found());
+ switch (bitmap.pixel_mode) {
+ case FT_PIXEL_MODE_MONO: {
+ int byte = i * bitmap.pitch + (j >> 3);
+ int bit = 1 << (7 - (j % 8));
+ wr[ofs + 0] = 255; //grayscale as 1
+ wr[ofs + 1] = (bitmap.buffer[byte] & bit) ? 255 : 0;
+ } break;
+ case FT_PIXEL_MODE_GRAY:
+ wr[ofs + 0] = 255; //grayscale as 1
+ wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j];
+ break;
+ case FT_PIXEL_MODE_BGRA: {
+ int ofs_color = i * bitmap.pitch + (j << 2);
+ wr[ofs + 2] = bitmap.buffer[ofs_color + 0];
+ wr[ofs + 1] = bitmap.buffer[ofs_color + 1];
+ wr[ofs + 0] = bitmap.buffer[ofs_color + 2];
+ wr[ofs + 3] = bitmap.buffer[ofs_color + 3];
+ } break;
+ // TODO: FT_PIXEL_MODE_LCD
+ default:
+ ERR_FAIL_V_MSG(Character::not_found(), "Font uses unsupported pixel format: " + itos(bitmap.pixel_mode) + ".");
+ break;
+ }
+ }
+ }
+ }
+
+ //blit to image and texture
+ {
+ if (RenderingServer::get_singleton() != nullptr) {
+ Ref<Image> img = memnew(Image(tex.texture_size, tex.texture_size, 0, require_format, tex.imgdata));
+
+ if (tex.texture.is_null()) {
+ tex.texture.instance();
+ tex.texture->create_from_image(img);
+ } else {
+ tex.texture->update(img); //update
+ }
+ }
+ }
+
+ // update height array
+ for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
+ tex.offsets.write[k] = tex_pos.y + mh;
+ }
+
+ Character chr;
+ chr.align = Vector2(xofs, -yofs) * p_data->scale_color_font / oversampling;
+ chr.advance = advance * p_data->scale_color_font / oversampling;
+ chr.texture_idx = tex_pos.index;
+ chr.found = true;
+
+ chr.rect_uv = Rect2(tex_pos.x + rect_margin, tex_pos.y + rect_margin, w, h);
+ chr.rect = chr.rect_uv;
+ chr.rect.position /= oversampling;
+ chr.rect.size *= (p_data->scale_color_font / oversampling);
+ return chr;
+}
+
+void DynamicFontDataFallback::update_char(int p_size, char32_t p_char) {
+ DataAtSize *fds = get_data_for_size(p_size, false);
+ ERR_FAIL_COND(fds == nullptr);
+
+ if (fds->char_map.has(p_char)) {
+ return;
+ }
+
+ Character character = Character::not_found();
+
+ FT_GlyphSlot slot = fds->face->glyph;
+ FT_UInt gl_index = FT_Get_Char_Index(fds->face, p_char);
+
+ if (gl_index == 0) {
+ fds->char_map[p_char] = character;
+ return;
+ }
+
+ int ft_hinting;
+ switch (hinting) {
+ case TextServer::HINTING_NONE:
+ ft_hinting = FT_LOAD_NO_HINTING;
+ break;
+ case TextServer::HINTING_LIGHT:
+ ft_hinting = FT_LOAD_TARGET_LIGHT;
+ break;
+ default:
+ ft_hinting = FT_LOAD_TARGET_NORMAL;
+ break;
+ }
+
+ FT_Fixed v, h;
+ FT_Get_Advance(fds->face, gl_index, FT_HAS_COLOR(fds->face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting, &h);
+ FT_Get_Advance(fds->face, gl_index, FT_HAS_COLOR(fds->face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting | FT_LOAD_VERTICAL_LAYOUT, &v);
+
+ int error = FT_Load_Glyph(fds->face, gl_index, FT_HAS_COLOR(fds->face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting);
+ if (error) {
+ fds->char_map[p_char] = character;
+ return;
+ }
+
+ error = FT_Render_Glyph(fds->face->glyph, antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
+ if (!error) {
+ character = bitmap_to_character(fds, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0);
+ }
+
+ fds->char_map[p_char] = character;
+}
+
+void DynamicFontDataFallback::update_char_outline(int p_size, int p_outline_size, char32_t p_char) {
+ DataAtSize *fds = get_data_for_size(p_size, p_outline_size);
+ ERR_FAIL_COND(fds == nullptr);
+
+ if (fds->char_map.has(p_char)) {
+ return;
+ }
+
+ Character character = Character::not_found();
+ FT_UInt gl_index = FT_Get_Char_Index(fds->face, p_char);
+
+ if (gl_index == 0) {
+ fds->char_map[p_char] = character;
+ return;
+ }
+
+ int error = FT_Load_Glyph(fds->face, gl_index, FT_LOAD_NO_BITMAP | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0));
+ if (error) {
+ fds->char_map[p_char] = character;
+ return;
+ }
+
+ FT_Stroker stroker;
+ if (FT_Stroker_New(library, &stroker) != 0) {
+ fds->char_map[p_char] = character;
+ return;
+ }
+
+ FT_Stroker_Set(stroker, (int)(p_outline_size * oversampling * 64.0), FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0);
+ FT_Glyph glyph;
+ FT_BitmapGlyph glyph_bitmap;
+
+ if (FT_Get_Glyph(fds->face->glyph, &glyph) != 0) {
+ goto cleanup_stroker;
+ }
+ if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) {
+ goto cleanup_glyph;
+ }
+ if (FT_Glyph_To_Bitmap(&glyph, antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1) != 0) {
+ goto cleanup_glyph;
+ }
+
+ glyph_bitmap = (FT_BitmapGlyph)glyph;
+ character = bitmap_to_character(fds, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2());
+
+cleanup_glyph:
+ FT_Done_Glyph(glyph);
+cleanup_stroker:
+ FT_Stroker_Done(stroker);
+
+ fds->char_map[p_char] = character;
+}
+
+void DynamicFontDataFallback::clear_cache() {
+ _THREAD_SAFE_METHOD_
+ for (Map<CacheID, DataAtSize *>::Element *E = size_cache.front(); E; E = E->next()) {
+ memdelete(E->get());
+ }
+ size_cache.clear();
+ for (Map<CacheID, DataAtSize *>::Element *E = size_cache_outline.front(); E; E = E->next()) {
+ memdelete(E->get());
+ }
+ size_cache_outline.clear();
+}
+
+Error DynamicFontDataFallback::load_from_file(const String &p_filename, int p_base_size) {
+ _THREAD_SAFE_METHOD_
+ if (library == nullptr) {
+ int error = FT_Init_FreeType(&library);
+ ERR_FAIL_COND_V_MSG(error != 0, ERR_CANT_CREATE, "Error initializing FreeType.");
+ }
+ clear_cache();
+
+ font_path = p_filename;
+ base_size = p_base_size;
+
+ valid = true;
+ DataAtSize *fds = get_data_for_size(base_size); // load base size.
+ if (fds == nullptr) {
+ valid = false;
+ ERR_FAIL_V(ERR_CANT_CREATE);
+ }
+
+ return OK;
+}
+
+Error DynamicFontDataFallback::load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) {
+ _THREAD_SAFE_METHOD_
+ if (library == nullptr) {
+ int error = FT_Init_FreeType(&library);
+ ERR_FAIL_COND_V_MSG(error != 0, ERR_CANT_CREATE, "Error initializing FreeType.");
+ }
+ clear_cache();
+
+ font_mem = p_data;
+ font_mem_size = p_size;
+ base_size = p_base_size;
+
+ valid = true;
+ DataAtSize *fds = get_data_for_size(base_size); // load base size.
+ if (fds == nullptr) {
+ valid = false;
+ ERR_FAIL_V(ERR_CANT_CREATE);
+ }
+
+ return OK;
+}
+
+float DynamicFontDataFallback::get_height(int p_size) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
+ ERR_FAIL_COND_V(fds == nullptr, 0.f);
+ return fds->ascent + fds->descent;
+}
+
+float DynamicFontDataFallback::get_ascent(int p_size) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
+ ERR_FAIL_COND_V(fds == nullptr, 0.f);
+ return fds->ascent;
+}
+
+float DynamicFontDataFallback::get_descent(int p_size) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
+ ERR_FAIL_COND_V(fds == nullptr, 0.f);
+ return fds->descent;
+}
+
+float DynamicFontDataFallback::get_underline_position(int p_size) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
+ ERR_FAIL_COND_V(fds == nullptr, 0.f);
+ return fds->underline_position;
+}
+
+float DynamicFontDataFallback::get_underline_thickness(int p_size) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
+ ERR_FAIL_COND_V(fds == nullptr, 0.f);
+ return fds->underline_thickness;
+}
+
+void DynamicFontDataFallback::set_antialiased(bool p_antialiased) {
+ if (antialiased != p_antialiased) {
+ clear_cache();
+ antialiased = p_antialiased;
+ }
+}
+
+bool DynamicFontDataFallback::get_antialiased() const {
+ return antialiased;
+}
+
+void DynamicFontDataFallback::set_force_autohinter(bool p_enabled) {
+ if (force_autohinter != p_enabled) {
+ clear_cache();
+ force_autohinter = p_enabled;
+ }
+}
+
+bool DynamicFontDataFallback::get_force_autohinter() const {
+ return force_autohinter;
+}
+
+void DynamicFontDataFallback::set_hinting(TextServer::Hinting p_hinting) {
+ if (hinting != p_hinting) {
+ clear_cache();
+ hinting = p_hinting;
+ }
+}
+
+TextServer::Hinting DynamicFontDataFallback::get_hinting() const {
+ return hinting;
+}
+
+bool DynamicFontDataFallback::has_outline() const {
+ return true;
+}
+
+float DynamicFontDataFallback::get_base_size() const {
+ return base_size;
+}
+
+bool DynamicFontDataFallback::has_char(char32_t p_char) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(base_size);
+ ERR_FAIL_COND_V(fds == nullptr, false);
+
+ const_cast<DynamicFontDataFallback *>(this)->update_char(base_size, p_char);
+ Character ch = fds->char_map[p_char];
+
+ return (ch.found);
+}
+
+String DynamicFontDataFallback::get_supported_chars() const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(base_size);
+ ERR_FAIL_COND_V(fds == nullptr, String());
+
+ String chars;
+
+ FT_UInt gindex;
+ FT_ULong charcode = FT_Get_First_Char(fds->face, &gindex);
+ while (gindex != 0) {
+ if (charcode != 0) {
+ chars += char32_t(charcode);
+ }
+ charcode = FT_Get_Next_Char(fds->face, charcode, &gindex);
+ }
+
+ return chars;
+}
+
+Vector2 DynamicFontDataFallback::get_advance(char32_t p_char, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
+ ERR_FAIL_COND_V(fds == nullptr, Vector2());
+
+ const_cast<DynamicFontDataFallback *>(this)->update_char(p_size, p_char);
+ Character ch = fds->char_map[p_char];
+
+ return ch.advance;
+}
+
+Vector2 DynamicFontDataFallback::get_kerning(char32_t p_char, char32_t p_next, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
+ ERR_FAIL_COND_V(fds == nullptr, Vector2());
+
+ FT_Vector delta;
+ FT_Get_Kerning(fds->face, FT_Get_Char_Index(fds->face, p_char), FT_Get_Char_Index(fds->face, p_next), FT_KERNING_DEFAULT, &delta);
+ return Vector2(delta.x, delta.y);
+}
+
+Vector2 DynamicFontDataFallback::draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
+ ERR_FAIL_COND_V(fds == nullptr, Vector2());
+
+ const_cast<DynamicFontDataFallback *>(this)->update_char(p_size, p_index);
+ Character ch = fds->char_map[p_index];
+
+ Vector2 advance;
+ if (ch.found) {
+ ERR_FAIL_COND_V(ch.texture_idx < -1 || ch.texture_idx >= fds->textures.size(), Vector2());
+
+ if (ch.texture_idx != -1) {
+ Point2 cpos = p_pos;
+ cpos += ch.align;
+
+ Color modulate = p_color;
+ if (FT_HAS_COLOR(fds->face)) {
+ modulate.r = modulate.g = modulate.b = 1.0;
+ }
+ if (RenderingServer::get_singleton() != nullptr) {
+ RID texture = fds->textures[ch.texture_idx].texture->get_rid();
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, ch.rect.size), texture, ch.rect_uv, modulate, false, false);
+ }
+ }
+
+ advance = ch.advance;
+ }
+
+ return advance;
+}
+
+Vector2 DynamicFontDataFallback::draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size, p_outline_size);
+ ERR_FAIL_COND_V(fds == nullptr, Vector2());
+
+ const_cast<DynamicFontDataFallback *>(this)->update_char_outline(p_size, p_outline_size, p_index);
+ Character ch = fds->char_map[p_index];
+
+ Vector2 advance;
+ if (ch.found) {
+ ERR_FAIL_COND_V(ch.texture_idx < -1 || ch.texture_idx >= fds->textures.size(), Vector2());
+
+ if (ch.texture_idx != -1) {
+ Point2 cpos = p_pos;
+ cpos += ch.align;
+
+ Color modulate = p_color;
+ if (FT_HAS_COLOR(fds->face)) {
+ modulate.r = modulate.g = modulate.b = 1.0;
+ }
+ if (RenderingServer::get_singleton() != nullptr) {
+ RID texture = fds->textures[ch.texture_idx].texture->get_rid();
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, ch.rect.size), texture, ch.rect_uv, modulate, false, false);
+ }
+ }
+
+ advance = ch.advance;
+ }
+
+ return advance;
+}
+
+DynamicFontDataFallback::~DynamicFontDataFallback() {
+ clear_cache();
+ if (library != nullptr) {
+ FT_Done_FreeType(library);
+ }
+}
diff --git a/modules/text_server_fb/dynamic_font_fb.h b/modules/text_server_fb/dynamic_font_fb.h
new file mode 100644
index 0000000000..6ac8cb52a8
--- /dev/null
+++ b/modules/text_server_fb/dynamic_font_fb.h
@@ -0,0 +1,169 @@
+/*************************************************************************/
+/* dynamic_font_fb.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef DYNAMIC_FONT_FALLBACK_H
+#define DYNAMIC_FONT_FALLBACK_H
+
+#include "font_fb.h"
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+struct DynamicFontDataFallback : public FontDataFallback {
+ _THREAD_SAFE_CLASS_
+
+private:
+ struct CharTexture {
+ Vector<uint8_t> imgdata;
+ int texture_size = 0;
+ Vector<int> offsets;
+ Ref<ImageTexture> texture;
+ };
+
+ struct Character {
+ bool found = false;
+ int texture_idx = 0;
+ Rect2 rect;
+ Rect2 rect_uv;
+ Vector2 align;
+ Vector2 advance = Vector2(-1, -1);
+
+ static Character not_found();
+ };
+
+ struct TexturePosition {
+ int index = 0;
+ int x = 0;
+ int y = 0;
+ };
+
+ struct CacheID {
+ union {
+ struct {
+ uint32_t size : 16;
+ uint32_t outline_size : 16;
+ };
+ uint32_t key;
+ };
+ bool operator<(CacheID right) const {
+ return key < right.key;
+ }
+ CacheID() {
+ key = 0;
+ }
+ };
+
+ struct DataAtSize {
+ FT_Face face = nullptr;
+ FT_StreamRec stream;
+
+ int size = 0;
+ float scale_color_font = 1.f;
+ float ascent = 0;
+ float descent = 0;
+ float underline_position = 0;
+ float underline_thickness = 0;
+
+ Vector<CharTexture> textures;
+ HashMap<char32_t, Character> char_map;
+
+ ~DataAtSize() {
+ if (face != nullptr) {
+ FT_Done_Face(face);
+ }
+ }
+ };
+
+ FT_Library library = nullptr;
+
+ // Source data.
+ const uint8_t *font_mem = nullptr;
+ int font_mem_size = 0;
+ String font_path;
+ Vector<uint8_t> font_mem_cache;
+
+ float rect_margin = 1.f;
+ int base_size = 16;
+ float oversampling = 1.f;
+ bool antialiased = true;
+ bool force_autohinter = false;
+ TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
+
+ Map<CacheID, DataAtSize *> size_cache;
+ Map<CacheID, DataAtSize *> size_cache_outline;
+
+ DataAtSize *get_data_for_size(int p_size, int p_outline_size = 0);
+
+ TexturePosition find_texture_pos_for_glyph(DataAtSize *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height);
+ Character bitmap_to_character(DataAtSize *p_data, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance);
+ _FORCE_INLINE_ void update_char(int p_size, char32_t p_char);
+ _FORCE_INLINE_ void update_char_outline(int p_size, int p_outline_size, char32_t p_char);
+
+public:
+ virtual void clear_cache() override;
+
+ virtual Error load_from_file(const String &p_filename, int p_base_size) override;
+ virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) override;
+
+ virtual float get_height(int p_size) const override;
+ virtual float get_ascent(int p_size) const override;
+ virtual float get_descent(int p_size) const override;
+
+ virtual float get_underline_position(int p_size) const override;
+ virtual float get_underline_thickness(int p_size) const override;
+
+ virtual void set_antialiased(bool p_antialiased) override;
+ virtual bool get_antialiased() const override;
+
+ virtual void set_hinting(TextServer::Hinting p_hinting) override;
+ virtual TextServer::Hinting get_hinting() const override;
+
+ virtual void set_force_autohinter(bool p_enabeld) override;
+ virtual bool get_force_autohinter() const override;
+
+ virtual void set_distance_field_hint(bool p_distance_field) override{};
+ virtual bool get_distance_field_hint() const override { return false; };
+
+ virtual bool has_outline() const override;
+ virtual float get_base_size() const override;
+
+ virtual bool has_char(char32_t p_char) const override;
+ virtual String get_supported_chars() const override;
+
+ virtual Vector2 get_advance(char32_t p_char, int p_size) const override;
+ virtual Vector2 get_kerning(char32_t p_char, char32_t p_next, int p_size) const override;
+
+ virtual Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override;
+ virtual Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override;
+
+ virtual ~DynamicFontDataFallback() override;
+};
+
+#endif // DYNAMIC_FONT_FALLBACK_H
diff --git a/modules/text_server_fb/font_fb.h b/modules/text_server_fb/font_fb.h
new file mode 100644
index 0000000000..d2ce2661a1
--- /dev/null
+++ b/modules/text_server_fb/font_fb.h
@@ -0,0 +1,80 @@
+/*************************************************************************/
+/* font_fb.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef FONT_FALLBACK_H
+#define FONT_FALLBACK_H
+
+#include "servers/text_server.h"
+
+struct FontDataFallback {
+ Map<String, bool> lang_support_overrides;
+ Map<String, bool> script_support_overrides;
+ bool valid = false;
+
+ virtual void clear_cache() = 0;
+
+ virtual Error load_from_file(const String &p_filename, int p_base_size) = 0;
+ virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) = 0;
+
+ virtual float get_height(int p_size) const = 0;
+ virtual float get_ascent(int p_size) const = 0;
+ virtual float get_descent(int p_size) const = 0;
+
+ virtual float get_underline_position(int p_size) const = 0;
+ virtual float get_underline_thickness(int p_size) const = 0;
+
+ virtual void set_antialiased(bool p_antialiased) = 0;
+ virtual bool get_antialiased() const = 0;
+
+ virtual void set_hinting(TextServer::Hinting p_hinting) = 0;
+ virtual TextServer::Hinting get_hinting() const = 0;
+
+ virtual void set_distance_field_hint(bool p_distance_field) = 0;
+ virtual bool get_distance_field_hint() const = 0;
+
+ virtual void set_force_autohinter(bool p_enabeld) = 0;
+ virtual bool get_force_autohinter() const = 0;
+
+ virtual bool has_outline() const = 0;
+ virtual float get_base_size() const = 0;
+
+ virtual bool has_char(char32_t p_char) const = 0;
+ virtual String get_supported_chars() const = 0;
+
+ virtual Vector2 get_advance(char32_t p_char, int p_size) const = 0;
+ virtual Vector2 get_kerning(char32_t p_char, char32_t p_next, int p_size) const = 0;
+
+ virtual Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const = 0;
+ virtual Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const = 0;
+
+ virtual ~FontDataFallback(){};
+};
+
+#endif // FONT_FALLBACK_H
diff --git a/modules/text_server_fb/register_types.cpp b/modules/text_server_fb/register_types.cpp
new file mode 100644
index 0000000000..ad4d2d47ab
--- /dev/null
+++ b/modules/text_server_fb/register_types.cpp
@@ -0,0 +1,43 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "register_types.h"
+
+#include "text_server_fb.h"
+
+void preregister_text_server_fb_types() {
+ TextServerFallback::register_server();
+}
+
+void register_text_server_fb_types() {
+}
+
+void unregister_text_server_fb_types() {
+}
diff --git a/modules/text_server_fb/register_types.h b/modules/text_server_fb/register_types.h
new file mode 100644
index 0000000000..58f8436c67
--- /dev/null
+++ b/modules/text_server_fb/register_types.h
@@ -0,0 +1,40 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TEXT_SERVER_FB_REGISTER_TYPES_H
+#define TEXT_SERVER_FB_REGISTER_TYPES_H
+
+#define MODULE_TEXT_SERVER_FB_HAS_PREREGISTER
+
+void preregister_text_server_fb_types();
+void register_text_server_fb_types();
+void unregister_text_server_fb_types();
+
+#endif // TEXT_SERVER_FB_REGISTER_TYPES_H
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
new file mode 100644
index 0000000000..675d0e5d4d
--- /dev/null
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -0,0 +1,1383 @@
+/*************************************************************************/
+/* text_server_fb.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "text_server_fb.h"
+
+#include "bitmap_font_fb.h"
+#include "dynamic_font_fb.h"
+
+_FORCE_INLINE_ bool is_control(char32_t p_char) {
+ return (p_char <= 0x001f) || (p_char >= 0x007f && p_char <= 0x009F);
+}
+
+_FORCE_INLINE_ bool is_whitespace(char32_t p_char) {
+ return (p_char == 0x0020) || (p_char == 0x00A0) || (p_char == 0x1680) || (p_char >= 0x2000 && p_char <= 0x200a) || (p_char == 0x202f) || (p_char == 0x205f) || (p_char == 0x3000) || (p_char == 0x2028) || (p_char == 0x2029) || (p_char >= 0x0009 && p_char <= 0x000d) || (p_char == 0x0085);
+}
+
+_FORCE_INLINE_ bool is_linebreak(char32_t p_char) {
+ return (p_char >= 0x000a && p_char <= 0x000d) || (p_char == 0x0085) || (p_char == 0x2028) || (p_char == 0x2029);
+}
+
+_FORCE_INLINE_ bool is_punct(char32_t p_char) {
+ return (p_char >= 0x0020 && p_char <= 0x002F) || (p_char >= 0x003A && p_char <= 0x0040) || (p_char >= 0x005B && p_char <= 0x0060) || (p_char >= 0x007B && p_char <= 0x007E) || (p_char >= 0x2000 && p_char <= 0x206F) || (p_char >= 0x3000 && p_char <= 0x303F);
+}
+
+/*************************************************************************/
+
+String TextServerFallback::interface_name = "Fallback";
+uint32_t TextServerFallback::interface_features = 0; // Nothing is supported.
+
+bool TextServerFallback::has_feature(Feature p_feature) {
+ return (interface_features & p_feature) == p_feature;
+}
+
+String TextServerFallback::get_name() const {
+ return interface_name;
+}
+
+void TextServerFallback::free(RID p_rid) {
+ _THREAD_SAFE_METHOD_
+ if (font_owner.owns(p_rid)) {
+ FontDataFallback *fd = font_owner.getornull(p_rid);
+ font_owner.free(p_rid);
+ memdelete(fd);
+ } else if (shaped_owner.owns(p_rid)) {
+ ShapedTextData *sd = shaped_owner.getornull(p_rid);
+ shaped_owner.free(p_rid);
+ memdelete(sd);
+ }
+}
+
+bool TextServerFallback::has(RID p_rid) {
+ _THREAD_SAFE_METHOD_
+ return font_owner.owns(p_rid) || shaped_owner.owns(p_rid);
+}
+
+bool TextServerFallback::load_support_data(const String &p_filename) {
+ return false; // No extra data used.
+}
+
+#ifdef TOOLS_ENABLED
+
+bool TextServerFallback::save_support_data(const String &p_filename) {
+ return false; // No extra data used.
+}
+
+#endif
+
+bool TextServerFallback::is_locale_right_to_left(const String &p_locale) {
+ return false; // No RTL support.
+}
+
+/*************************************************************************/
+/* Font interface */
+/*************************************************************************/
+
+RID TextServerFallback::create_font_system(const String &p_name, int p_base_size) {
+ ERR_FAIL_V_MSG(RID(), "System fonts are not supported by this text server.");
+}
+
+RID TextServerFallback::create_font_resource(const String &p_filename, int p_base_size) {
+ _THREAD_SAFE_METHOD_
+ FontDataFallback *fd = nullptr;
+ if (p_filename.get_extension() == "ttf" || p_filename.get_extension() == "otf" || p_filename.get_extension() == "woff") {
+ fd = memnew(DynamicFontDataFallback);
+ } else if (p_filename.get_extension() == "fnt" || p_filename.get_extension() == "font") {
+ fd = memnew(BitmapFontDataFallback);
+ } else {
+ return RID();
+ }
+
+ Error err = fd->load_from_file(p_filename, p_base_size);
+ if (err != OK) {
+ memdelete(fd);
+ return RID();
+ }
+
+ return font_owner.make_rid(fd);
+}
+
+RID TextServerFallback::create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size) {
+ _THREAD_SAFE_METHOD_
+ FontDataFallback *fd = nullptr;
+ if (p_type == "ttf" || p_type == "otf" || p_type == "woff") {
+ fd = memnew(DynamicFontDataFallback);
+ } else if (p_type == "fnt" || p_type == "font") {
+ fd = memnew(BitmapFontDataFallback);
+ } else {
+ return RID();
+ }
+
+ Error err = fd->load_from_memory(p_data, p_size, p_base_size);
+ if (err != OK) {
+ memdelete(fd);
+ return RID();
+ }
+
+ return font_owner.make_rid(fd);
+}
+
+float TextServerFallback::font_get_height(RID p_font, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, 0.f);
+ return fd->get_height(p_size);
+}
+
+float TextServerFallback::font_get_ascent(RID p_font, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, 0.f);
+ return fd->get_ascent(p_size);
+}
+
+float TextServerFallback::font_get_descent(RID p_font, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, 0.f);
+ return fd->get_descent(p_size);
+}
+
+float TextServerFallback::font_get_underline_position(RID p_font, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, 0.f);
+ return fd->get_underline_position(p_size);
+}
+
+float TextServerFallback::font_get_underline_thickness(RID p_font, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, 0.f);
+ return fd->get_underline_thickness(p_size);
+}
+
+void TextServerFallback::font_set_antialiased(RID p_font, bool p_antialiased) {
+ _THREAD_SAFE_METHOD_
+ FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND(!fd);
+ fd->set_antialiased(p_antialiased);
+}
+
+bool TextServerFallback::font_get_antialiased(RID p_font) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, false);
+ return fd->get_antialiased();
+}
+
+void TextServerFallback::font_set_distance_field_hint(RID p_font, bool p_distance_field) {
+ _THREAD_SAFE_METHOD_
+ FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND(!fd);
+ fd->set_distance_field_hint(p_distance_field);
+}
+
+bool TextServerFallback::font_get_distance_field_hint(RID p_font) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, false);
+ return fd->get_distance_field_hint();
+}
+
+void TextServerFallback::font_set_hinting(RID p_font, TextServer::Hinting p_hinting) {
+ _THREAD_SAFE_METHOD_
+ FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND(!fd);
+ fd->set_hinting(p_hinting);
+}
+
+TextServer::Hinting TextServerFallback::font_get_hinting(RID p_font) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, TextServer::HINTING_NONE);
+ return fd->get_hinting();
+}
+
+void TextServerFallback::font_set_force_autohinter(RID p_font, bool p_enabeld) {
+ _THREAD_SAFE_METHOD_
+ FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND(!fd);
+ fd->set_force_autohinter(p_enabeld);
+}
+
+bool TextServerFallback::font_get_force_autohinter(RID p_font) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, false);
+ return fd->get_force_autohinter();
+}
+
+bool TextServerFallback::font_has_char(RID p_font, char32_t p_char) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, false);
+ return fd->has_char(p_char);
+}
+
+String TextServerFallback::font_get_supported_chars(RID p_font) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, String());
+ return fd->get_supported_chars();
+}
+
+bool TextServerFallback::font_has_outline(RID p_font) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, false);
+ return fd->has_outline();
+}
+
+float TextServerFallback::font_get_base_size(RID p_font) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, 0.f);
+ return fd->get_base_size();
+}
+
+bool TextServerFallback::font_is_language_supported(RID p_font, const String &p_language) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, false);
+ if (fd->lang_support_overrides.has(p_language)) {
+ return fd->lang_support_overrides[p_language];
+ } else {
+ Vector<String> tags = p_language.replace("-", "_").split("_");
+ if (tags.size() > 0) {
+ if (fd->lang_support_overrides.has(tags[0])) {
+ return fd->lang_support_overrides[tags[0]];
+ }
+ }
+ return false;
+ }
+}
+
+void TextServerFallback::font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) {
+ _THREAD_SAFE_METHOD_
+ FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND(!fd);
+ fd->lang_support_overrides[p_language] = p_supported;
+}
+
+bool TextServerFallback::font_get_language_support_override(RID p_font, const String &p_language) {
+ _THREAD_SAFE_METHOD_
+ FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, false);
+ return fd->lang_support_overrides[p_language];
+}
+
+void TextServerFallback::font_remove_language_support_override(RID p_font, const String &p_language) {
+ _THREAD_SAFE_METHOD_
+ FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND(!fd);
+ fd->lang_support_overrides.erase(p_language);
+}
+
+Vector<String> TextServerFallback::font_get_language_support_overrides(RID p_font) {
+ _THREAD_SAFE_METHOD_
+ FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, Vector<String>());
+ Vector<String> ret;
+ for (Map<String, bool>::Element *E = fd->lang_support_overrides.front(); E; E = E->next()) {
+ ret.push_back(E->key());
+ }
+ return ret;
+}
+
+bool TextServerFallback::font_is_script_supported(RID p_font, const String &p_script) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, false);
+ if (fd->script_support_overrides.has(p_script)) {
+ return fd->script_support_overrides[p_script];
+ } else {
+ return true;
+ }
+}
+
+void TextServerFallback::font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) {
+ _THREAD_SAFE_METHOD_
+ FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND(!fd);
+ fd->script_support_overrides[p_script] = p_supported;
+}
+
+bool TextServerFallback::font_get_script_support_override(RID p_font, const String &p_script) {
+ _THREAD_SAFE_METHOD_
+ FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, false);
+ return fd->script_support_overrides[p_script];
+}
+
+void TextServerFallback::font_remove_script_support_override(RID p_font, const String &p_script) {
+ _THREAD_SAFE_METHOD_
+ FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND(!fd);
+ fd->script_support_overrides.erase(p_script);
+}
+
+Vector<String> TextServerFallback::font_get_script_support_overrides(RID p_font) {
+ _THREAD_SAFE_METHOD_
+ FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, Vector<String>());
+ Vector<String> ret;
+ for (Map<String, bool>::Element *E = fd->script_support_overrides.front(); E; E = E->next()) {
+ ret.push_back(E->key());
+ }
+ return ret;
+}
+
+uint32_t TextServerFallback::font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector) const {
+ return (uint32_t)p_char;
+}
+
+Vector2 TextServerFallback::font_get_glyph_advance(RID p_font, uint32_t p_index, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, Vector2());
+ return fd->get_advance(p_index, p_size);
+}
+
+Vector2 TextServerFallback::font_get_glyph_kerning(RID p_font, uint32_t p_index_a, uint32_t p_index_b, int p_size) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, Vector2());
+ return fd->get_kerning(p_index_a, p_index_b, p_size);
+}
+
+Vector2 TextServerFallback::font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, Vector2());
+ return fd->draw_glyph(p_canvas, p_size, p_pos, p_index, p_color);
+}
+
+Vector2 TextServerFallback::font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, Vector2());
+ return fd->draw_glyph_outline(p_canvas, p_size, p_outline_size, p_pos, p_index, p_color);
+}
+
+float TextServerFallback::font_get_oversampling() const {
+ return oversampling;
+}
+
+void TextServerFallback::font_set_oversampling(float p_oversampling) {
+ _THREAD_SAFE_METHOD_
+ if (oversampling != p_oversampling) {
+ oversampling = p_oversampling;
+ List<RID> fonts;
+ font_owner.get_owned_list(&fonts);
+ for (List<RID>::Element *E = fonts.front(); E; E = E->next()) {
+ font_owner.getornull(E->get())->clear_cache();
+ }
+ }
+}
+
+Vector<String> TextServerFallback::get_system_fonts() const {
+ return Vector<String>();
+}
+
+/*************************************************************************/
+/* Shaped text buffer interface */
+/*************************************************************************/
+
+void TextServerFallback::invalidate(ShapedTextData *p_shaped) {
+ p_shaped->valid = false;
+ p_shaped->sort_valid = false;
+ p_shaped->line_breaks_valid = false;
+ p_shaped->justification_ops_valid = false;
+ p_shaped->ascent = 0.f;
+ p_shaped->descent = 0.f;
+ p_shaped->width = 0.f;
+ p_shaped->upos = 0.f;
+ p_shaped->uthk = 0.f;
+ p_shaped->glyphs.clear();
+ p_shaped->glyphs_logical.clear();
+}
+
+void TextServerFallback::full_copy(ShapedTextData *p_shaped) {
+ ShapedTextData *parent = shaped_owner.getornull(p_shaped->parent);
+
+ for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = parent->objects.front(); E; E = E->next()) {
+ if (E->get().pos >= p_shaped->start && E->get().pos < p_shaped->end) {
+ p_shaped->objects[E->key()] = E->get();
+ }
+ }
+
+ for (int k = 0; k < parent->spans.size(); k++) {
+ ShapedTextData::Span span = parent->spans[k];
+ if (span.start >= p_shaped->end || span.end <= p_shaped->start) {
+ continue;
+ }
+ span.start = MAX(p_shaped->start, span.start);
+ span.end = MIN(p_shaped->end, span.end);
+ p_shaped->spans.push_back(span);
+ }
+
+ p_shaped->parent = RID();
+}
+
+RID TextServerFallback::create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextData *sd = memnew(ShapedTextData);
+ sd->direction = p_direction;
+ sd->orientation = p_orientation;
+
+ return shaped_owner.make_rid(sd);
+}
+
+void TextServerFallback::shaped_text_clear(RID p_shaped) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND(!sd);
+
+ sd->parent = RID();
+ sd->start = 0;
+ sd->end = 0;
+ sd->text = String();
+ sd->spans.clear();
+ sd->objects.clear();
+ invalidate(sd);
+}
+
+void TextServerFallback::shaped_text_set_direction(RID p_shaped, TextServer::Direction p_direction) {
+ if (p_direction == DIRECTION_RTL) {
+ ERR_PRINT_ONCE("Right-to-left layout is not supported by this text server.");
+ }
+}
+
+TextServer::Direction TextServerFallback::shaped_text_get_direction(RID p_shaped) const {
+ return TextServer::DIRECTION_LTR;
+}
+
+void TextServerFallback::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND(!sd);
+ if (sd->orientation != p_orientation) {
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ sd->orientation = p_orientation;
+ invalidate(sd);
+ }
+}
+
+void TextServerFallback::shaped_text_set_bidi_override(RID p_shaped, const Vector<Vector2i> &p_override) {
+ //No BiDi support, ignore.
+}
+
+TextServer::Orientation TextServerFallback::shaped_text_get_orientation(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, TextServer::ORIENTATION_HORIZONTAL);
+ return sd->orientation;
+}
+
+void TextServerFallback::shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND(!sd);
+ if (sd->preserve_invalid != p_enabled) {
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ sd->preserve_invalid = p_enabled;
+ invalidate(sd);
+ }
+}
+
+bool TextServerFallback::shaped_text_get_preserve_invalid(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+ return sd->preserve_invalid;
+}
+
+void TextServerFallback::shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND(!sd);
+ if (sd->preserve_control != p_enabled) {
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ sd->preserve_control = p_enabled;
+ invalidate(sd);
+ }
+}
+
+bool TextServerFallback::shaped_text_get_preserve_control(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+ return sd->preserve_control;
+}
+
+bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+ ERR_FAIL_COND_V(p_size <= 0, false);
+
+ if (p_text.empty()) {
+ return true;
+ }
+
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+
+ ShapedTextData::Span span;
+ span.start = sd->text.length();
+ span.end = span.start + p_text.length();
+ // Pre-sort fonts, push fonts with the language support first.
+ for (int i = 0; i < p_fonts.size(); i++) {
+ if (font_is_language_supported(p_fonts[i], p_language)) {
+ span.fonts.push_back(p_fonts[i]);
+ }
+ }
+ // Push the rest valid fonts.
+ for (int i = 0; i < p_fonts.size(); i++) {
+ if (!font_is_language_supported(p_fonts[i], p_language)) {
+ span.fonts.push_back(p_fonts[i]);
+ }
+ }
+ ERR_FAIL_COND_V(span.fonts.empty(), false);
+ span.font_size = p_size;
+ span.language = p_language;
+
+ sd->spans.push_back(span);
+ sd->text += p_text;
+ sd->end += p_text.length();
+ invalidate(sd);
+
+ return true;
+}
+
+bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align, int p_length) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+ ERR_FAIL_COND_V(p_key == Variant(), false);
+ ERR_FAIL_COND_V(sd->objects.has(p_key), false);
+
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+
+ ShapedTextData::Span span;
+ span.start = sd->text.length();
+ span.end = span.start + p_length;
+ span.embedded_key = p_key;
+
+ ShapedTextData::EmbeddedObject obj;
+ obj.inline_align = p_inline_align;
+ obj.rect.size = p_size;
+ obj.pos = span.start;
+
+ sd->spans.push_back(span);
+ sd->text += String::chr(0xfffc).repeat(p_length);
+ sd->end += p_length;
+ sd->objects[p_key] = obj;
+ invalidate(sd);
+
+ return true;
+}
+
+bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+ ERR_FAIL_COND_V(!sd->objects.has(p_key), false);
+ sd->objects[p_key].rect.size = p_size;
+ sd->objects[p_key].inline_align = p_inline_align;
+ if (sd->valid) {
+ // Recalc string metrics.
+ sd->ascent = 0;
+ sd->descent = 0;
+ sd->width = 0;
+ sd->upos = 0;
+ sd->uthk = 0;
+ int sd_size = sd->glyphs.size();
+ const FontDataFallback *fd = nullptr;
+ RID prev_rid = RID();
+
+ for (int i = 0; i < sd_size; i++) {
+ Glyph gl = sd->glyphs[i];
+ Variant key;
+ if (gl.count == 1) {
+ for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
+ if (E->get().pos == gl.start) {
+ key = E->key();
+ break;
+ }
+ }
+ }
+ if (key != Variant()) {
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ sd->objects[key].rect.position.x = sd->width;
+ sd->width += sd->objects[key].rect.size.x;
+ switch (sd->objects[key].inline_align) {
+ case VALIGN_TOP: {
+ sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.y);
+ } break;
+ case VALIGN_CENTER: {
+ sd->ascent = MAX(sd->ascent, Math::round(sd->objects[key].rect.size.y / 2));
+ sd->descent = MAX(sd->descent, Math::round(sd->objects[key].rect.size.y / 2));
+ } break;
+ case VALIGN_BOTTOM: {
+ sd->descent = MAX(sd->descent, sd->objects[key].rect.size.y);
+ } break;
+ }
+ sd->glyphs.write[i].advance = sd->objects[key].rect.size.x;
+ } else {
+ sd->objects[key].rect.position.y = sd->width;
+ sd->width += sd->objects[key].rect.size.y;
+ switch (sd->objects[key].inline_align) {
+ case VALIGN_TOP: {
+ sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.x);
+ } break;
+ case VALIGN_CENTER: {
+ sd->ascent = MAX(sd->ascent, Math::round(sd->objects[key].rect.size.x / 2));
+ sd->descent = MAX(sd->descent, Math::round(sd->objects[key].rect.size.x / 2));
+ } break;
+ case VALIGN_BOTTOM: {
+ sd->descent = MAX(sd->descent, sd->objects[key].rect.size.x);
+ } break;
+ }
+ sd->glyphs.write[i].advance = sd->objects[key].rect.size.y;
+ }
+ } else {
+ if (prev_rid != gl.font_rid) {
+ fd = font_owner.getornull(gl.font_rid);
+ prev_rid = gl.font_rid;
+ }
+ if (fd != nullptr) {
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ sd->ascent = MAX(sd->ascent, fd->get_ascent(gl.font_size));
+ sd->descent = MAX(sd->descent, fd->get_descent(gl.font_size));
+ } else {
+ sd->ascent = MAX(sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
+ sd->descent = MAX(sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
+ }
+ sd->upos = MAX(sd->upos, font_get_underline_position(gl.font_rid, gl.font_size));
+ sd->uthk = MAX(sd->uthk, font_get_underline_thickness(gl.font_rid, gl.font_size));
+ } else if (sd->preserve_invalid || (sd->preserve_control && is_control(gl.index))) {
+ // Glyph not found, replace with hex code box.
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f));
+ sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f));
+ } else {
+ sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
+ sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
+ }
+ }
+ sd->width += gl.advance * gl.repeat;
+ }
+ }
+
+ // Align embedded objects to baseline.
+ for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
+ if ((E->get().pos >= sd->start) && (E->get().pos < sd->end)) {
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ switch (E->get().inline_align) {
+ case VALIGN_TOP: {
+ E->get().rect.position.y = -sd->ascent;
+ } break;
+ case VALIGN_CENTER: {
+ E->get().rect.position.y = -(E->get().rect.size.y / 2);
+ } break;
+ case VALIGN_BOTTOM: {
+ E->get().rect.position.y = sd->descent - E->get().rect.size.y;
+ } break;
+ }
+ } else {
+ switch (E->get().inline_align) {
+ case VALIGN_TOP: {
+ E->get().rect.position.x = -sd->ascent;
+ } break;
+ case VALIGN_CENTER: {
+ E->get().rect.position.x = -(E->get().rect.size.x / 2);
+ } break;
+ case VALIGN_BOTTOM: {
+ E->get().rect.position.x = sd->descent - E->get().rect.size.x;
+ } break;
+ }
+ }
+ }
+ }
+ }
+ return true;
+}
+
+RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_length) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, RID());
+ if (sd->parent != RID()) {
+ return shaped_text_substr(sd->parent, p_start, p_length);
+ }
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+ ERR_FAIL_COND_V(p_start < 0 || p_length < 0, RID());
+ ERR_FAIL_COND_V(sd->start > p_start || sd->end < p_start, RID());
+ ERR_FAIL_COND_V(sd->end < p_start + p_length, RID());
+
+ ShapedTextData *new_sd = memnew(ShapedTextData);
+ new_sd->parent = p_shaped;
+ new_sd->start = p_start;
+ new_sd->end = p_start + p_length;
+
+ new_sd->orientation = sd->orientation;
+ new_sd->direction = sd->direction;
+ new_sd->para_direction = sd->para_direction;
+ new_sd->line_breaks_valid = sd->line_breaks_valid;
+ new_sd->justification_ops_valid = sd->justification_ops_valid;
+ new_sd->sort_valid = false;
+ new_sd->upos = sd->upos;
+ new_sd->uthk = sd->uthk;
+
+ if (p_length > 0) {
+ new_sd->text = sd->text.substr(p_start, p_length);
+ int sd_size = sd->glyphs.size();
+ const Glyph *sd_glyphs = sd->glyphs.ptr();
+
+ for (int i = 0; i < sd_size; i++) {
+ if ((sd_glyphs[i].start >= new_sd->start) && (sd_glyphs[i].end <= new_sd->end)) {
+ Glyph gl = sd_glyphs[i];
+ Variant key;
+ bool find_embedded = false;
+ if (gl.count == 1) {
+ for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
+ if (E->get().pos == gl.start) {
+ find_embedded = true;
+ key = E->key();
+ new_sd->objects[key] = E->get();
+ break;
+ }
+ }
+ }
+ if (find_embedded) {
+ if (new_sd->orientation == ORIENTATION_HORIZONTAL) {
+ new_sd->objects[key].rect.position.x = new_sd->width;
+ new_sd->width += new_sd->objects[key].rect.size.x;
+ switch (new_sd->objects[key].inline_align) {
+ case VALIGN_TOP: {
+ new_sd->ascent = MAX(new_sd->ascent, new_sd->objects[key].rect.size.y);
+ } break;
+ case VALIGN_CENTER: {
+ new_sd->ascent = MAX(new_sd->ascent, Math::round(new_sd->objects[key].rect.size.y / 2));
+ new_sd->descent = MAX(new_sd->descent, Math::round(new_sd->objects[key].rect.size.y / 2));
+ } break;
+ case VALIGN_BOTTOM: {
+ new_sd->descent = MAX(new_sd->descent, new_sd->objects[key].rect.size.y);
+ } break;
+ }
+ } else {
+ new_sd->objects[key].rect.position.y = new_sd->width;
+ new_sd->width += new_sd->objects[key].rect.size.y;
+ switch (new_sd->objects[key].inline_align) {
+ case VALIGN_TOP: {
+ new_sd->ascent = MAX(new_sd->ascent, new_sd->objects[key].rect.size.x);
+ } break;
+ case VALIGN_CENTER: {
+ new_sd->ascent = MAX(new_sd->ascent, Math::round(new_sd->objects[key].rect.size.x / 2));
+ new_sd->descent = MAX(new_sd->descent, Math::round(new_sd->objects[key].rect.size.x / 2));
+ } break;
+ case VALIGN_BOTTOM: {
+ new_sd->descent = MAX(new_sd->descent, new_sd->objects[key].rect.size.x);
+ } break;
+ }
+ }
+ } else {
+ const FontDataFallback *fd = font_owner.getornull(gl.font_rid);
+ if (fd != nullptr) {
+ if (new_sd->orientation == ORIENTATION_HORIZONTAL) {
+ new_sd->ascent = MAX(new_sd->ascent, fd->get_ascent(gl.font_size));
+ new_sd->descent = MAX(new_sd->descent, fd->get_descent(gl.font_size));
+ } else {
+ new_sd->ascent = MAX(new_sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
+ new_sd->descent = MAX(new_sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
+ }
+ } else if (new_sd->preserve_invalid || (new_sd->preserve_control && is_control(gl.index))) {
+ // Glyph not found, replace with hex code box.
+ if (new_sd->orientation == ORIENTATION_HORIZONTAL) {
+ new_sd->ascent = MAX(new_sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f));
+ new_sd->descent = MAX(new_sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f));
+ } else {
+ new_sd->ascent = MAX(new_sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
+ new_sd->descent = MAX(new_sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
+ }
+ }
+ new_sd->width += gl.advance * gl.repeat;
+ }
+ new_sd->glyphs.push_back(gl);
+ }
+ }
+
+ for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = new_sd->objects.front(); E; E = E->next()) {
+ if ((E->get().pos >= new_sd->start) && (E->get().pos < new_sd->end)) {
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ switch (E->get().inline_align) {
+ case VALIGN_TOP: {
+ E->get().rect.position.y = -new_sd->ascent;
+ } break;
+ case VALIGN_CENTER: {
+ E->get().rect.position.y = -(E->get().rect.size.y / 2);
+ } break;
+ case VALIGN_BOTTOM: {
+ E->get().rect.position.y = new_sd->descent - E->get().rect.size.y;
+ } break;
+ }
+ } else {
+ switch (E->get().inline_align) {
+ case VALIGN_TOP: {
+ E->get().rect.position.x = -new_sd->ascent;
+ } break;
+ case VALIGN_CENTER: {
+ E->get().rect.position.x = -(E->get().rect.size.x / 2);
+ } break;
+ case VALIGN_BOTTOM: {
+ E->get().rect.position.x = new_sd->descent - E->get().rect.size.x;
+ } break;
+ }
+ }
+ }
+ }
+ }
+ new_sd->valid = true;
+
+ return shaped_owner.make_rid(new_sd);
+}
+
+RID TextServerFallback::shaped_text_get_parent(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, RID());
+ return sd->parent;
+}
+
+float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t /*JustificationFlag*/ p_jst_flags) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0.f);
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+ if (!sd->justification_ops_valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_update_justification_ops(p_shaped);
+ }
+
+ int start_pos = 0;
+ int end_pos = sd->glyphs.size() - 1;
+
+ if ((p_jst_flags & JUSTIFICATION_AFTER_LAST_TAB) == JUSTIFICATION_AFTER_LAST_TAB) {
+ int start, end, delta;
+ if (sd->para_direction == DIRECTION_LTR) {
+ start = sd->glyphs.size() - 1;
+ end = -1;
+ delta = -1;
+ } else {
+ start = 0;
+ end = sd->glyphs.size();
+ delta = +1;
+ }
+
+ for (int i = start; i != end; i += delta) {
+ if ((sd->glyphs[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) {
+ if (sd->para_direction == DIRECTION_LTR) {
+ start_pos = i;
+ break;
+ } else {
+ end_pos = i;
+ break;
+ }
+ }
+ }
+ }
+
+ if ((p_jst_flags & JUSTIFICATION_TRIM_EDGE_SPACES) == JUSTIFICATION_TRIM_EDGE_SPACES) {
+ while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ sd->width -= sd->glyphs[start_pos].advance * sd->glyphs[start_pos].repeat;
+ sd->glyphs.write[start_pos].advance = 0;
+ start_pos += sd->glyphs[start_pos].count;
+ }
+ while ((start_pos < end_pos) && ((sd->glyphs[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ sd->width -= sd->glyphs[end_pos].advance * sd->glyphs[end_pos].repeat;
+ sd->glyphs.write[end_pos].advance = 0;
+ end_pos -= sd->glyphs[end_pos].count;
+ }
+ }
+
+ int space_count = 0;
+ for (int i = start_pos; i <= end_pos; i++) {
+ const Glyph &gl = sd->glyphs[i];
+ if (gl.count > 0) {
+ if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) {
+ space_count++;
+ }
+ }
+ }
+
+ if ((space_count > 0) && ((p_jst_flags & JUSTIFICATION_WORD_BOUND) == JUSTIFICATION_WORD_BOUND)) {
+ float delta_width_per_space = (p_width - sd->width) / space_count;
+ for (int i = start_pos; i <= end_pos; i++) {
+ Glyph &gl = sd->glyphs.write[i];
+ if (gl.count > 0) {
+ if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) {
+ float old_adv = gl.advance;
+ gl.advance = Math::round(MAX(gl.advance + delta_width_per_space, 0.05 * gl.font_size));
+ sd->width += (gl.advance - old_adv);
+ }
+ }
+ }
+ }
+
+ return sd->width;
+}
+
+float TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0.f);
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+ if (!sd->line_breaks_valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_update_breaks(p_shaped);
+ }
+
+ int tab_index = 0;
+ float off = 0.f;
+
+ int start, end, delta;
+ if (sd->para_direction == DIRECTION_LTR) {
+ start = 0;
+ end = sd->glyphs.size();
+ delta = +1;
+ } else {
+ start = sd->glyphs.size() - 1;
+ end = -1;
+ delta = -1;
+ }
+
+ Glyph *gl = sd->glyphs.ptrw();
+
+ for (int i = start; i != end; i += delta) {
+ if ((gl[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) {
+ float tab_off = 0.f;
+ while (tab_off <= off) {
+ tab_off += p_tab_stops[tab_index];
+ tab_index++;
+ if (tab_index >= p_tab_stops.size()) {
+ tab_index = 0;
+ }
+ }
+ float old_adv = gl[i].advance;
+ gl[i].advance = tab_off - off;
+ sd->width += gl[i].advance - old_adv;
+ off = 0;
+ continue;
+ }
+ off += gl[i].advance * gl[i].repeat;
+ }
+
+ return 0.f;
+}
+
+bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+ if (!sd->valid) {
+ shaped_text_shape(p_shaped);
+ }
+
+ if (sd->line_breaks_valid) {
+ return true; // Noting to do.
+ }
+
+ int sd_size = sd->glyphs.size();
+ for (int i = 0; i < sd_size; i++) {
+ if (sd->glyphs[i].count > 0) {
+ char32_t c = sd->text[sd->glyphs[i].start];
+ if (is_punct(c)) {
+ sd->glyphs.write[i].flags |= GRAPHEME_IS_PUNCTUATION;
+ }
+ if (is_whitespace(c) && !is_linebreak(c)) {
+ sd->glyphs.write[i].flags |= GRAPHEME_IS_SPACE;
+ sd->glyphs.write[i].flags |= GRAPHEME_IS_BREAK_SOFT;
+ }
+ if (is_linebreak(c)) {
+ sd->glyphs.write[i].flags |= GRAPHEME_IS_BREAK_HARD;
+ }
+ if (c == 0x0009 || c == 0x000b) {
+ sd->glyphs.write[i].flags |= GRAPHEME_IS_TAB;
+ }
+
+ i += (sd->glyphs[i].count - 1);
+ }
+ }
+ sd->line_breaks_valid = true;
+ return sd->line_breaks_valid;
+}
+
+bool TextServerFallback::shaped_text_update_justification_ops(RID p_shaped) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+ if (!sd->valid) {
+ shaped_text_shape(p_shaped);
+ }
+ if (!sd->line_breaks_valid) {
+ shaped_text_update_breaks(p_shaped);
+ }
+
+ sd->justification_ops_valid = true; // Not supported by fallback server.
+ return true;
+}
+
+bool TextServerFallback::shaped_text_shape(RID p_shaped) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+ if (sd->valid) {
+ return true;
+ }
+
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+
+ // Cleanup.
+ sd->justification_ops_valid = false;
+ sd->line_breaks_valid = false;
+ sd->ascent = 0.f;
+ sd->descent = 0.f;
+ sd->width = 0.f;
+ sd->glyphs.clear();
+
+ if (sd->text.length() == 0) {
+ sd->valid = true;
+ return true;
+ }
+
+ // "Shape" string.
+ for (int i = 0; i < sd->spans.size(); i++) {
+ const ShapedTextData::Span &span = sd->spans[i];
+ if (span.embedded_key != Variant()) {
+ // Embedded object.
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ sd->objects[span.embedded_key].rect.position.x = sd->width;
+ sd->width += sd->objects[span.embedded_key].rect.size.x;
+ switch (sd->objects[span.embedded_key].inline_align) {
+ case VALIGN_TOP: {
+ sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.y);
+ } break;
+ case VALIGN_CENTER: {
+ sd->ascent = MAX(sd->ascent, Math::round(sd->objects[span.embedded_key].rect.size.y / 2));
+ sd->descent = MAX(sd->descent, Math::round(sd->objects[span.embedded_key].rect.size.y / 2));
+ } break;
+ case VALIGN_BOTTOM: {
+ sd->descent = MAX(sd->descent, sd->objects[span.embedded_key].rect.size.y);
+ } break;
+ }
+ } else {
+ sd->objects[span.embedded_key].rect.position.y = sd->width;
+ sd->width += sd->objects[span.embedded_key].rect.size.y;
+ switch (sd->objects[span.embedded_key].inline_align) {
+ case VALIGN_TOP: {
+ sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.x);
+ } break;
+ case VALIGN_CENTER: {
+ sd->ascent = MAX(sd->ascent, Math::round(sd->objects[span.embedded_key].rect.size.x / 2));
+ sd->descent = MAX(sd->descent, Math::round(sd->objects[span.embedded_key].rect.size.x / 2));
+ } break;
+ case VALIGN_BOTTOM: {
+ sd->descent = MAX(sd->descent, sd->objects[span.embedded_key].rect.size.x);
+ } break;
+ }
+ }
+ Glyph gl;
+ gl.start = span.start;
+ gl.end = span.end;
+ gl.count = 1;
+ gl.index = 0;
+ gl.flags = GRAPHEME_IS_VALID | GRAPHEME_IS_VIRTUAL;
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ gl.advance = sd->objects[span.embedded_key].rect.size.x;
+ } else {
+ gl.advance = sd->objects[span.embedded_key].rect.size.y;
+ }
+ sd->glyphs.push_back(gl);
+ } else {
+ // Text span.
+ for (int j = span.start; j < span.end; j++) {
+ const FontDataFallback *fd = nullptr;
+
+ Glyph gl;
+ gl.start = j;
+ gl.end = j + 1;
+ gl.count = 1;
+ gl.font_size = span.font_size;
+ gl.index = (uint32_t)sd->text[j]; // Use codepoint.
+ if (gl.index == 0x0009 || gl.index == 0x000b) {
+ gl.index = 0x0020;
+ }
+ if (!sd->preserve_control && is_control(gl.index)) {
+ gl.index = 0x0020;
+ }
+ // Select first font which has character (font are already sorted by span language).
+ for (int k = 0; k < span.fonts.size(); k++) {
+ fd = font_owner.getornull(span.fonts[k]);
+ if (fd != nullptr && fd->has_char(gl.index)) {
+ gl.font_rid = span.fonts[k];
+ break;
+ }
+ }
+
+ if (gl.font_rid != RID()) {
+ if (sd->text[j] != 0 && !is_linebreak(sd->text[j])) {
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ gl.advance = fd->get_advance(gl.index, gl.font_size).x;
+ gl.x_off = 0;
+ gl.y_off = 0;
+ sd->ascent = MAX(sd->ascent, fd->get_ascent(gl.font_size));
+ sd->descent = MAX(sd->descent, fd->get_descent(gl.font_size));
+ } else {
+ gl.advance = fd->get_advance(gl.index, gl.font_size).y;
+ gl.x_off = -Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5);
+ gl.y_off = fd->get_ascent(gl.font_size);
+ sd->ascent = MAX(sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
+ sd->descent = MAX(sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
+ }
+ }
+ sd->upos = MAX(sd->upos, fd->get_underline_position(gl.font_size));
+ sd->uthk = MAX(sd->uthk, fd->get_underline_thickness(gl.font_size));
+
+ // Add kerning to previous glyph.
+ if (sd->glyphs.size() > 0) {
+ Glyph &prev_gl = sd->glyphs.write[sd->glyphs.size() - 1];
+ if (prev_gl.font_rid == gl.font_rid && prev_gl.font_size == gl.font_size) {
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ prev_gl.advance += fd->get_kerning(prev_gl.index, gl.index, gl.font_size).x;
+ } else {
+ prev_gl.advance += fd->get_kerning(prev_gl.index, gl.index, gl.font_size).y;
+ }
+ }
+ }
+ } else if (sd->preserve_invalid || (sd->preserve_control && is_control(gl.index))) {
+ // Glyph not found, replace with hex code box.
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ gl.advance = get_hex_code_box_size(gl.font_size, gl.index).x;
+ sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f));
+ sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f));
+ } else {
+ gl.advance = get_hex_code_box_size(gl.font_size, gl.index).y;
+ sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
+ sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
+ }
+ }
+ sd->width += gl.advance;
+ sd->glyphs.push_back(gl);
+ }
+ }
+ }
+
+ // Align embedded objects to baseline.
+ for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ switch (E->get().inline_align) {
+ case VALIGN_TOP: {
+ E->get().rect.position.y = -sd->ascent;
+ } break;
+ case VALIGN_CENTER: {
+ E->get().rect.position.y = -(E->get().rect.size.y / 2);
+ } break;
+ case VALIGN_BOTTOM: {
+ E->get().rect.position.y = sd->descent - E->get().rect.size.y;
+ } break;
+ }
+ } else {
+ switch (E->get().inline_align) {
+ case VALIGN_TOP: {
+ E->get().rect.position.x = -sd->ascent;
+ } break;
+ case VALIGN_CENTER: {
+ E->get().rect.position.x = -(E->get().rect.size.x / 2);
+ } break;
+ case VALIGN_BOTTOM: {
+ E->get().rect.position.x = sd->descent - E->get().rect.size.x;
+ } break;
+ }
+ }
+ }
+
+ sd->valid = true;
+ return sd->valid;
+}
+
+bool TextServerFallback::shaped_text_is_ready(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+ return sd->valid;
+}
+
+Vector<TextServer::Glyph> TextServerFallback::shaped_text_get_glyphs(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, Vector<TextServer::Glyph>());
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+ return sd->glyphs;
+}
+
+Vector2i TextServerFallback::shaped_text_get_range(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, Vector2i());
+ return Vector2(sd->start, sd->end);
+}
+
+Vector<TextServer::Glyph> TextServerFallback::shaped_text_sort_logical(RID p_shaped) {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, Vector<TextServer::Glyph>());
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+
+ return sd->glyphs; // Already in the logical order, return as is.
+}
+
+Array TextServerFallback::shaped_text_get_objects(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ Array ret;
+ const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, ret);
+ for (const Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
+ ret.push_back(E->key());
+ }
+
+ return ret;
+}
+
+Rect2 TextServerFallback::shaped_text_get_object_rect(RID p_shaped, Variant p_key) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, Rect2());
+ ERR_FAIL_COND_V(!sd->objects.has(p_key), Rect2());
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+ return sd->objects[p_key].rect;
+}
+
+Size2 TextServerFallback::shaped_text_get_size(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, Size2());
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+ if (sd->orientation == TextServer::ORIENTATION_HORIZONTAL) {
+ return Size2(sd->width, sd->ascent + sd->descent);
+ } else {
+ return Size2(sd->ascent + sd->descent, sd->width);
+ }
+}
+
+float TextServerFallback::shaped_text_get_ascent(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0.f);
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+ return sd->ascent;
+}
+
+float TextServerFallback::shaped_text_get_descent(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0.f);
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+ return sd->descent;
+}
+
+float TextServerFallback::shaped_text_get_width(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0.f);
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+ return sd->width;
+}
+
+float TextServerFallback::shaped_text_get_underline_position(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0.f);
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+
+ return sd->upos;
+}
+
+float TextServerFallback::shaped_text_get_underline_thickness(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0.f);
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+
+ return sd->uthk;
+}
+
+TextServer *TextServerFallback::create_func(Error &r_error, void *p_user_data) {
+ r_error = OK;
+ return memnew(TextServerFallback());
+}
+
+void TextServerFallback::register_server() {
+ TextServerManager::register_create_function(interface_name, interface_features, create_func, nullptr);
+}
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
new file mode 100644
index 0000000000..56bb1f7bf9
--- /dev/null
+++ b/modules/text_server_fb/text_server_fb.h
@@ -0,0 +1,193 @@
+/*************************************************************************/
+/* text_server_fb.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TEXT_SERVER_FALLBACK_H
+#define TEXT_SERVER_FALLBACK_H
+
+/*************************************************************************/
+/* Fallback Text Server provides simplified TS functionality, without */
+/* BiDi, shaping and advanced font features support. */
+/*************************************************************************/
+
+#include "servers/text_server.h"
+
+#include "core/templates/rid_owner.h"
+
+#include "scene/resources/texture.h"
+
+#include "font_fb.h"
+
+class TextServerFallback : public TextServer {
+ GDCLASS(TextServerFallback, TextServer);
+ _THREAD_SAFE_CLASS_
+
+ float oversampling = 1.f;
+ mutable RID_PtrOwner<FontDataFallback> font_owner;
+ mutable RID_PtrOwner<ShapedTextData> shaped_owner;
+
+ static String interface_name;
+ static uint32_t interface_features;
+
+protected:
+ static void _bind_methods(){};
+
+ void full_copy(ShapedTextData *p_shaped);
+ void invalidate(ShapedTextData *p_shaped);
+
+public:
+ virtual bool has_feature(Feature p_feature) override;
+ virtual String get_name() const override;
+
+ virtual void free(RID p_rid) override;
+ virtual bool has(RID p_rid) override;
+ virtual bool load_support_data(const String &p_filename) override;
+
+#ifdef TOOLS_ENABLED
+ virtual String get_support_data_filename() override { return ""; };
+ virtual String get_support_data_info() override { return "Not supported"; };
+ virtual bool save_support_data(const String &p_filename) override;
+#endif
+
+ virtual bool is_locale_right_to_left(const String &p_locale) override;
+
+ /* Font interface */
+ virtual RID create_font_system(const String &p_name, int p_base_size = 16) override;
+ virtual RID create_font_resource(const String &p_filename, int p_base_size = 16) override;
+ virtual RID create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16) override;
+
+ virtual float font_get_height(RID p_font, int p_size) const override;
+ virtual float font_get_ascent(RID p_font, int p_size) const override;
+ virtual float font_get_descent(RID p_font, int p_size) const override;
+
+ virtual float font_get_underline_position(RID p_font, int p_size) const override;
+ virtual float font_get_underline_thickness(RID p_font, int p_size) const override;
+
+ virtual void font_set_antialiased(RID p_font, bool p_antialiased) override;
+ virtual bool font_get_antialiased(RID p_font) const override;
+
+ virtual void font_set_hinting(RID p_font, Hinting p_hinting) override;
+ virtual Hinting font_get_hinting(RID p_font) const override;
+
+ virtual void font_set_force_autohinter(RID p_font, bool p_enabeld) override;
+ virtual bool font_get_force_autohinter(RID p_font) const override;
+
+ virtual bool font_has_char(RID p_font, char32_t p_char) const override;
+ virtual String font_get_supported_chars(RID p_font) const override;
+
+ virtual void font_set_distance_field_hint(RID p_font, bool p_distance_field) override;
+ virtual bool font_get_distance_field_hint(RID p_font) const override;
+
+ virtual bool font_has_outline(RID p_font) const override;
+ virtual float font_get_base_size(RID p_font) const override;
+
+ virtual bool font_is_language_supported(RID p_font, const String &p_language) const override;
+ virtual void font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) override;
+ virtual bool font_get_language_support_override(RID p_font, const String &p_language) override;
+ virtual void font_remove_language_support_override(RID p_font, const String &p_language) override;
+ Vector<String> font_get_language_support_overrides(RID p_font) override;
+
+ virtual bool font_is_script_supported(RID p_font, const String &p_script) const override;
+ virtual void font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) override;
+ virtual bool font_get_script_support_override(RID p_font, const String &p_script) override;
+ virtual void font_remove_script_support_override(RID p_font, const String &p_script) override;
+ Vector<String> font_get_script_support_overrides(RID p_font) override;
+
+ virtual uint32_t font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector = 0x0000) const override;
+ virtual Vector2 font_get_glyph_advance(RID p_font, uint32_t p_index, int p_size) const override;
+ virtual Vector2 font_get_glyph_kerning(RID p_font, uint32_t p_index_a, uint32_t p_index_b, int p_size) const override;
+
+ virtual Vector2 font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
+ virtual Vector2 font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
+
+ virtual float font_get_oversampling() const override;
+ virtual void font_set_oversampling(float p_oversampling) override;
+
+ virtual Vector<String> get_system_fonts() const override;
+
+ /* Shaped text buffer interface */
+
+ virtual RID create_shaped_text(Direction p_direction = DIRECTION_AUTO, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
+
+ virtual void shaped_text_clear(RID p_shaped) override;
+
+ virtual void shaped_text_set_direction(RID p_shaped, Direction p_direction = DIRECTION_AUTO) override;
+ virtual Direction shaped_text_get_direction(RID p_shaped) const override;
+
+ virtual void shaped_text_set_bidi_override(RID p_shaped, const Vector<Vector2i> &p_override) override;
+
+ virtual void shaped_text_set_orientation(RID p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
+ virtual Orientation shaped_text_get_orientation(RID p_shaped) const override;
+
+ virtual void shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) override;
+ virtual bool shaped_text_get_preserve_invalid(RID p_shaped) const override;
+
+ virtual void shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) override;
+ virtual bool shaped_text_get_preserve_control(RID p_shaped) const override;
+
+ virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") override;
+ virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER, int p_length = 1) override;
+ virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER) override;
+
+ virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override;
+ virtual RID shaped_text_get_parent(RID p_shaped) const override;
+
+ virtual float shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
+ virtual float shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) override;
+
+ virtual bool shaped_text_shape(RID p_shaped) override;
+ virtual bool shaped_text_update_breaks(RID p_shaped) override;
+ virtual bool shaped_text_update_justification_ops(RID p_shaped) override;
+
+ virtual bool shaped_text_is_ready(RID p_shaped) const override;
+
+ virtual Vector<Glyph> shaped_text_get_glyphs(RID p_shaped) const override;
+
+ virtual Vector2i shaped_text_get_range(RID p_shaped) const override;
+
+ virtual Vector<Glyph> shaped_text_sort_logical(RID p_shaped) override;
+
+ virtual Array shaped_text_get_objects(RID p_shaped) const override;
+ virtual Rect2 shaped_text_get_object_rect(RID p_shaped, Variant p_key) const override;
+
+ virtual Size2 shaped_text_get_size(RID p_shaped) const override;
+ virtual float shaped_text_get_ascent(RID p_shaped) const override;
+ virtual float shaped_text_get_descent(RID p_shaped) const override;
+ virtual float shaped_text_get_width(RID p_shaped) const override;
+ virtual float shaped_text_get_underline_position(RID p_shaped) const override;
+ virtual float shaped_text_get_underline_thickness(RID p_shaped) const override;
+
+ static TextServer *create_func(Error &r_error, void *p_user_data);
+ static void register_server();
+
+ TextServerFallback(){};
+ ~TextServerFallback(){};
+};
+
+#endif // TEXT_SERVER_FALLBACK_H
diff --git a/modules/theora/SCsub b/modules/theora/SCsub
index a01e65b4b0..6038ea086a 100644
--- a/modules/theora/SCsub
+++ b/modules/theora/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_theora = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
if env["builtin_libtheora"]:
thirdparty_dir = "#thirdparty/libtheora/"
thirdparty_sources = [
@@ -80,7 +83,16 @@ if env["builtin_libtheora"]:
env_thirdparty = env_theora.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
+
# Godot source files
-env_theora.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_theora.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
+
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/tinyexr/SCsub b/modules/tinyexr/SCsub
index 84b3b4015b..30bde96fb4 100644
--- a/modules/tinyexr/SCsub
+++ b/modules/tinyexr/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_tinyexr = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
# Not unbundled for now as they are not commonly available as shared library
thirdparty_dir = "#thirdparty/tinyexr/"
thirdparty_sources = [
@@ -20,7 +23,15 @@ env_tinyexr.Append(CPPDEFINES=["TINYEXR_USE_THREAD"])
env_thirdparty = env_tinyexr.Clone()
env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.modules_sources += thirdparty_obj
+
+# Godot source files
+
+module_obj = []
+
+env_tinyexr.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
-# Godot's own source files
-env_tinyexr.add_source_files(env.modules_sources, "*.cpp")
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/upnp/SCsub b/modules/upnp/SCsub
index 2e129e15ca..bc0b215be3 100644
--- a/modules/upnp/SCsub
+++ b/modules/upnp/SCsub
@@ -7,6 +7,8 @@ env_upnp = env_modules.Clone()
# Thirdparty source files
+thirdparty_obj = []
+
if env["builtin_miniupnpc"]:
thirdparty_dir = "#thirdparty/miniupnpc/"
thirdparty_sources = [
@@ -31,7 +33,16 @@ if env["builtin_miniupnpc"]:
env_thirdparty = env_upnp.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
+
# Godot source files
-env_upnp.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_upnp.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
+
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/vhacd/SCsub b/modules/vhacd/SCsub
index ecd432b275..1ff4122114 100644
--- a/modules/vhacd/SCsub
+++ b/modules/vhacd/SCsub
@@ -7,6 +7,8 @@ env_vhacd = env_modules.Clone()
# Thirdparty source files
+thirdparty_obj = []
+
thirdparty_dir = "#thirdparty/vhacd/"
thirdparty_sources = [
@@ -24,10 +26,19 @@ thirdparty_sources = [
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-env_vhacd.Prepend(CPPPATH=[thirdparty_dir + "/inc"])
+env_vhacd.Prepend(CPPPATH=[thirdparty_dir + "inc"])
env_thirdparty = env_vhacd.Clone()
env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.modules_sources += thirdparty_obj
+
+# Godot source files
+
+module_obj = []
+
+env_vhacd.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
-env_vhacd.add_source_files(env.modules_sources, "*.cpp")
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index b10d4523f2..f9ef184579 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -2367,7 +2367,7 @@ void VisualScriptFunctionState::connect_to_signal(Object *p_obj, const String &p
binds.push_back(p_binds[i]);
}
binds.push_back(Ref<VisualScriptFunctionState>(this)); //add myself on the back to avoid dying from unreferencing
- p_obj->connect_compat(p_signal, this, "_signal_callback", binds, CONNECT_ONESHOT);
+ p_obj->connect(p_signal, Callable(this, "_signal_callback"), binds, CONNECT_ONESHOT);
}
bool VisualScriptFunctionState::is_valid() const {
diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h
index 85dab4e6cf..59bdfb2fc3 100644
--- a/modules/visual_script/visual_script.h
+++ b/modules/visual_script/visual_script.h
@@ -33,6 +33,7 @@
#include "core/debugger/engine_debugger.h"
#include "core/debugger/script_debugger.h"
+#include "core/doc_data.h"
#include "core/object/script_language.h"
#include "core/os/thread.h"
@@ -342,6 +343,13 @@ public:
virtual void set_source_code(const String &p_code) override;
virtual Error reload(bool p_keep_state = false) override;
+#ifdef TOOLS_ENABLED
+ virtual const Vector<DocData::ClassDoc> &get_documentation() const override {
+ static Vector<DocData::ClassDoc> docs;
+ return docs;
+ }
+#endif // TOOLS_ENABLED
+
virtual bool is_tool() const override;
virtual bool is_valid() const override;
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp
index 1bb96a47f3..1bd88a2a19 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/visual_script_editor.cpp
@@ -719,6 +719,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
line_edit->set_text(node->get_text());
line_edit->set_expand_to_text_length(true);
line_edit->add_theme_font_override("font", get_theme_font("source", "EditorFonts"));
+ line_edit->add_theme_font_size_override("font_size", get_theme_font_size("source_size", "EditorFonts"));
gnode->add_child(line_edit);
line_edit->connect("text_changed", callable_mp(this, &VisualScriptEditor::_expression_text_changed), varray(E->get()));
} else {
@@ -2546,7 +2547,7 @@ void VisualScriptEditor::set_edited_resource(const RES &p_res) {
}
_update_graph();
- _update_members();
+ call_deferred("_update_members");
}
void VisualScriptEditor::enable_editor() {
@@ -4728,6 +4729,7 @@ VisualScriptEditor::VisualScriptEditor() {
saved_position = Vector2(0, 0);
edit_menu = memnew(MenuButton);
+ edit_menu->set_shortcut_context(this);
edit_menu->set_text(TTR("Edit"));
edit_menu->set_switch_on_hover(true);
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/delete_selected"), EDIT_DELETE_NODES);
@@ -4856,8 +4858,8 @@ VisualScriptEditor::VisualScriptEditor() {
function_create_dialog = memnew(ConfirmationDialog);
function_create_dialog->set_title(TTR("Create Function"));
function_create_dialog->add_child(function_vb);
- function_create_dialog->get_ok()->set_text(TTR("Create"));
- function_create_dialog->get_ok()->connect("pressed", callable_mp(this, &VisualScriptEditor::_create_function));
+ function_create_dialog->get_ok_button()->set_text(TTR("Create"));
+ function_create_dialog->get_ok_button()->connect("pressed", callable_mp(this, &VisualScriptEditor::_create_function));
add_child(function_create_dialog);
select_func_text = memnew(Label);
@@ -4900,7 +4902,7 @@ VisualScriptEditor::VisualScriptEditor() {
graph->connect("connection_to_empty", callable_mp(this, &VisualScriptEditor::_graph_connect_to_empty));
edit_signal_dialog = memnew(AcceptDialog);
- edit_signal_dialog->get_ok()->set_text(TTR("Close"));
+ edit_signal_dialog->get_ok_button()->set_text(TTR("Close"));
add_child(edit_signal_dialog);
signal_editor = memnew(VisualScriptEditorSignalEdit);
@@ -4910,7 +4912,7 @@ VisualScriptEditor::VisualScriptEditor() {
edit_signal_edit->edit(signal_editor);
edit_variable_dialog = memnew(AcceptDialog);
- edit_variable_dialog->get_ok()->set_text(TTR("Close"));
+ edit_variable_dialog->get_ok_button()->set_text(TTR("Close"));
add_child(edit_variable_dialog);
variable_editor = memnew(VisualScriptEditorVariableEdit);
@@ -4929,7 +4931,6 @@ VisualScriptEditor::VisualScriptEditor() {
updating_members = false;
set_process_input(true);
- set_process_unhandled_input(true);
default_value_edit = memnew(CustomPropertyEditor);
add_child(default_value_edit);
@@ -4943,7 +4944,7 @@ VisualScriptEditor::VisualScriptEditor() {
new_connect_node_select = memnew(VisualScriptPropertySelector);
add_child(new_connect_node_select);
new_connect_node_select->connect("selected", callable_mp(this, &VisualScriptEditor::_selected_connect_node));
- new_connect_node_select->get_cancel()->connect("pressed", callable_mp(this, &VisualScriptEditor::_cancel_connect_node));
+ new_connect_node_select->get_cancel_button()->connect("pressed", callable_mp(this, &VisualScriptEditor::_cancel_connect_node));
new_virtual_method_select = memnew(VisualScriptPropertySelector);
add_child(new_virtual_method_select);
diff --git a/modules/visual_script/visual_script_property_selector.cpp b/modules/visual_script/visual_script_property_selector.cpp
index 875270e74f..dbb76e19ac 100644
--- a/modules/visual_script/visual_script_property_selector.cpp
+++ b/modules/visual_script/visual_script_property_selector.cpp
@@ -31,6 +31,7 @@
#include "visual_script_property_selector.h"
#include "core/os/keyboard.h"
+#include "editor/doc_tools.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "modules/visual_script/visual_script.h"
@@ -309,7 +310,7 @@ void VisualScriptPropertySelector::_update_search() {
found = true;
}
- get_ok()->set_disabled(root->get_children() == nullptr);
+ get_ok_button()->set_disabled(root->get_children() == nullptr);
}
void VisualScriptPropertySelector::create_visualscript_item(const String &name, TreeItem *const root, const String &search_input, const String &text) {
@@ -437,7 +438,7 @@ void VisualScriptPropertySelector::_item_selected() {
class_type = base_type;
}
- DocData *dd = EditorHelp::get_doc_data();
+ DocTools *dd = EditorHelp::get_doc_data();
String text;
String at_class = class_type;
@@ -704,8 +705,8 @@ VisualScriptPropertySelector::VisualScriptPropertySelector() {
search_box->connect("gui_input", callable_mp(this, &VisualScriptPropertySelector::_sbox_input));
search_options = memnew(Tree);
vbc->add_margin_child(TTR("Matches:"), search_options, true);
- get_ok()->set_text(TTR("Open"));
- get_ok()->set_disabled(true);
+ get_ok_button()->set_text(TTR("Open"));
+ get_ok_button()->set_disabled(true);
register_text_enter(search_box);
set_hide_on_ok(false);
search_options->connect("item_activated", callable_mp(this, &VisualScriptPropertySelector::_confirmed));
diff --git a/modules/vorbis/SCsub b/modules/vorbis/SCsub
index 05d46757d3..bc31fff066 100644
--- a/modules/vorbis/SCsub
+++ b/modules/vorbis/SCsub
@@ -8,9 +8,10 @@ Import("env_modules")
env_vorbis = env_modules.Clone()
-stub = True
-
# Thirdparty source files
+
+thirdparty_obj = []
+
if env["builtin_libvorbis"]:
thirdparty_dir = "#thirdparty/libvorbis/"
thirdparty_sources = [
@@ -51,7 +52,16 @@ if env["builtin_libvorbis"]:
env_thirdparty = env_vorbis.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
+
+
+# Godot source files
+
+module_obj = []
+
+env_vorbis.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
-# Module files
-env_vorbis.add_source_files(env.modules_sources, "register_types.cpp")
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/webm/SCsub b/modules/webm/SCsub
index 247b4ead37..44e80e2870 100644
--- a/modules/webm/SCsub
+++ b/modules/webm/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_webm = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
thirdparty_dir = "#thirdparty/libsimplewebm/"
thirdparty_sources = [
"libwebm/mkvparser/mkvparser.cc",
@@ -31,7 +34,15 @@ if env["builtin_libvpx"]:
env_thirdparty = env_webm.Clone()
env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.modules_sources += thirdparty_obj
# Godot source files
-env_webm.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_webm.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
+
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/webm/libvpx/SCsub b/modules/webm/libvpx/SCsub
index d0744fa313..67d3f1bebd 100644
--- a/modules/webm/libvpx/SCsub
+++ b/modules/webm/libvpx/SCsub
@@ -278,7 +278,7 @@ if webm_cpu_x86:
try:
yasm_found = True
subprocess.Popen([yasm_path, "--version"], stdout=devnull, stderr=devnull).communicate()
- except:
+ except Exception:
yasm_found = False
if yasm_found:
break
diff --git a/modules/webp/SCsub b/modules/webp/SCsub
index 58f2bb35e6..4c0c2f7893 100644
--- a/modules/webp/SCsub
+++ b/modules/webp/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_webp = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
if env["builtin_libwebp"]:
thirdparty_dir = "#thirdparty/libwebp/"
thirdparty_sources = [
@@ -130,7 +133,16 @@ if env["builtin_libwebp"]:
env_thirdparty = env_webp.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
+
# Godot source files
-env_webp.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_webp.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
+
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/webrtc/SCsub b/modules/webrtc/SCsub
index 4f870ddb2f..31b8a73bf2 100644
--- a/modules/webrtc/SCsub
+++ b/modules/webrtc/SCsub
@@ -3,8 +3,6 @@
Import("env")
Import("env_modules")
-# Thirdparty source files
-
env_webrtc = env_modules.Clone()
use_gdnative = env_webrtc["module_gdnative_enabled"]
diff --git a/modules/webrtc/library_godot_webrtc.js b/modules/webrtc/library_godot_webrtc.js
index f725a10a6f..9f029407d2 100644
--- a/modules/webrtc/library_godot_webrtc.js
+++ b/modules/webrtc/library_godot_webrtc.js
@@ -90,6 +90,7 @@ const GodotRTCDataChannel = {
},
},
+ godot_js_rtc_datachannel_ready_state_get__sig: 'ii',
godot_js_rtc_datachannel_ready_state_get: function (p_id) {
const ref = IDHandler.get(p_id);
if (!ref) {
@@ -109,6 +110,7 @@ const GodotRTCDataChannel = {
}
},
+ godot_js_rtc_datachannel_send__sig: 'iiiii',
godot_js_rtc_datachannel_send: function (p_id, p_buffer, p_length, p_raw) {
const ref = IDHandler.get(p_id);
if (!ref) {
@@ -129,14 +131,17 @@ const GodotRTCDataChannel = {
return 0;
},
+ godot_js_rtc_datachannel_is_ordered__sig: 'ii',
godot_js_rtc_datachannel_is_ordered: function (p_id) {
return IDHandler.get_prop(p_id, 'ordered', true);
},
+ godot_js_rtc_datachannel_id_get__sig: 'ii',
godot_js_rtc_datachannel_id_get: function (p_id) {
return IDHandler.get_prop(p_id, 'id', 65535);
},
+ godot_js_rtc_datachannel_max_packet_lifetime_get__sig: 'ii',
godot_js_rtc_datachannel_max_packet_lifetime_get: function (p_id) {
const ref = IDHandler.get(p_id);
if (!ref) {
@@ -151,14 +156,17 @@ const GodotRTCDataChannel = {
return 65535;
},
+ godot_js_rtc_datachannel_max_retransmits_get__sig: 'ii',
godot_js_rtc_datachannel_max_retransmits_get: function (p_id) {
return IDHandler.get_prop(p_id, 'maxRetransmits', 65535);
},
- godot_js_rtc_datachannel_is_negotiated: function (p_id, p_def) {
+ godot_js_rtc_datachannel_is_negotiated__sig: 'ii',
+ godot_js_rtc_datachannel_is_negotiated: function (p_id) {
return IDHandler.get_prop(p_id, 'negotiated', 65535);
},
+ godot_js_rtc_datachannel_label_get__sig: 'ii',
godot_js_rtc_datachannel_label_get: function (p_id) {
const ref = IDHandler.get(p_id);
if (!ref || !ref.label) {
@@ -167,6 +175,7 @@ const GodotRTCDataChannel = {
return GodotRuntime.allocString(ref.label);
},
+ godot_js_rtc_datachannel_protocol_get__sig: 'ii',
godot_js_rtc_datachannel_protocol_get: function (p_id) {
const ref = IDHandler.get(p_id);
if (!ref || !ref.protocol) {
@@ -175,11 +184,13 @@ const GodotRTCDataChannel = {
return GodotRuntime.allocString(ref.protocol);
},
+ godot_js_rtc_datachannel_destroy__sig: 'vi',
godot_js_rtc_datachannel_destroy: function (p_id) {
GodotRTCDataChannel.close(p_id);
IDHandler.remove(p_id);
},
+ godot_js_rtc_datachannel_connect__sig: 'viiiiii',
godot_js_rtc_datachannel_connect: function (p_id, p_ref, p_on_open, p_on_message, p_on_error, p_on_close) {
const onopen = GodotRuntime.get_func(p_on_open).bind(null, p_ref);
const onmessage = GodotRuntime.get_func(p_on_message).bind(null, p_ref);
@@ -188,6 +199,7 @@ const GodotRTCDataChannel = {
GodotRTCDataChannel.connect(p_id, onopen, onmessage, onerror, onclose);
},
+ godot_js_rtc_datachannel_close__sig: 'vi',
godot_js_rtc_datachannel_close: function (p_id) {
const ref = IDHandler.get(p_id);
if (!ref) {
@@ -280,6 +292,7 @@ const GodotRTCPeerConnection = {
},
},
+ godot_js_rtc_pc_create__sig: 'iiiiii',
godot_js_rtc_pc_create: function (p_config, p_ref, p_on_state_change, p_on_candidate, p_on_datachannel) {
const onstatechange = GodotRuntime.get_func(p_on_state_change).bind(null, p_ref);
const oncandidate = GodotRuntime.get_func(p_on_candidate).bind(null, p_ref);
@@ -302,6 +315,7 @@ const GodotRTCPeerConnection = {
return id;
},
+ godot_js_rtc_pc_close__sig: 'vi',
godot_js_rtc_pc_close: function (p_id) {
const ref = IDHandler.get(p_id);
if (!ref) {
@@ -310,6 +324,7 @@ const GodotRTCPeerConnection = {
ref.close();
},
+ godot_js_rtc_pc_destroy__sig: 'vi',
godot_js_rtc_pc_destroy: function (p_id) {
const ref = IDHandler.get(p_id);
if (!ref) {
@@ -321,6 +336,7 @@ const GodotRTCPeerConnection = {
IDHandler.remove(p_id);
},
+ godot_js_rtc_pc_offer_create__sig: 'viiii',
godot_js_rtc_pc_offer_create: function (p_id, p_obj, p_on_session, p_on_error) {
const ref = IDHandler.get(p_id);
if (!ref) {
@@ -335,6 +351,7 @@ const GodotRTCPeerConnection = {
});
},
+ godot_js_rtc_pc_local_description_set__sig: 'viiiii',
godot_js_rtc_pc_local_description_set: function (p_id, p_type, p_sdp, p_obj, p_on_error) {
const ref = IDHandler.get(p_id);
if (!ref) {
@@ -351,6 +368,7 @@ const GodotRTCPeerConnection = {
});
},
+ godot_js_rtc_pc_remote_description_set__sig: 'viiiiii',
godot_js_rtc_pc_remote_description_set: function (p_id, p_type, p_sdp, p_obj, p_session_created, p_on_error) {
const ref = IDHandler.get(p_id);
if (!ref) {
@@ -375,6 +393,7 @@ const GodotRTCPeerConnection = {
});
},
+ godot_js_rtc_pc_ice_candidate_add__sig: 'viiii',
godot_js_rtc_pc_ice_candidate_add: function (p_id, p_mid_name, p_mline_idx, p_sdp) {
const ref = IDHandler.get(p_id);
if (!ref) {
@@ -390,6 +409,7 @@ const GodotRTCPeerConnection = {
},
godot_js_rtc_pc_datachannel_create__deps: ['$GodotRTCDataChannel'],
+ godot_js_rtc_pc_datachannel_create__sig: 'iiii',
godot_js_rtc_pc_datachannel_create: function (p_id, p_label, p_config) {
try {
const ref = IDHandler.get(p_id);
diff --git a/modules/websocket/SCsub b/modules/websocket/SCsub
index 13e51a39c0..4c022c43cf 100644
--- a/modules/websocket/SCsub
+++ b/modules/websocket/SCsub
@@ -5,28 +5,44 @@ Import("env_modules")
env_ws = env_modules.Clone()
+thirdparty_obj = []
+
if env["platform"] == "javascript":
# Our JavaScript/C++ interface.
env.AddJSLibraries(["library_godot_websocket.js"])
+
elif env["builtin_wslay"]:
# Thirdparty source files
- wslay_dir = "#thirdparty/wslay/"
- wslay_sources = [
+ thirdparty_dir = "#thirdparty/wslay/"
+ thirdparty_sources = [
"wslay_net.c",
"wslay_event.c",
"wslay_queue.c",
"wslay_stack.c",
"wslay_frame.c",
]
- wslay_sources = [wslay_dir + s for s in wslay_sources]
- env_ws.Prepend(CPPPATH=[wslay_dir + "includes/"])
+ thirdparty_sources = [thirdparty_dir + s for s in thirdparty_sources]
+
+ env_ws.Prepend(CPPPATH=[thirdparty_dir + "includes/"])
env_ws.Append(CPPDEFINES=["HAVE_CONFIG_H"])
+
if env["platform"] == "windows" or env["platform"] == "uwp":
env_ws.Append(CPPDEFINES=["HAVE_WINSOCK2_H"])
else:
env_ws.Append(CPPDEFINES=["HAVE_NETINET_IN_H"])
- env_wslay = env_ws.Clone()
- env_wslay.disable_warnings()
- env_wslay.add_source_files(env.modules_sources, wslay_sources)
-env_ws.add_source_files(env.modules_sources, "*.cpp")
+ env_thirdparty = env_ws.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
+
+
+# Godot source files
+
+module_obj = []
+
+env_ws.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
+
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/websocket/library_godot_websocket.js b/modules/websocket/library_godot_websocket.js
index 6ada4e7335..cf2c00a6a6 100644
--- a/modules/websocket/library_godot_websocket.js
+++ b/modules/websocket/library_godot_websocket.js
@@ -135,6 +135,7 @@ const GodotWebSocket = {
},
},
+ godot_js_websocket_create__sig: 'iiiiiiii',
godot_js_websocket_create: function (p_ref, p_url, p_proto, p_on_open, p_on_message, p_on_error, p_on_close) {
const on_open = GodotRuntime.get_func(p_on_open).bind(null, p_ref);
const on_message = GodotRuntime.get_func(p_on_message).bind(null, p_ref);
@@ -156,6 +157,7 @@ const GodotWebSocket = {
return GodotWebSocket.create(socket, on_open, on_message, on_error, on_close);
},
+ godot_js_websocket_send__sig: 'iiiii',
godot_js_websocket_send: function (p_id, p_buf, p_buf_len, p_raw) {
const bytes_array = new Uint8Array(p_buf_len);
let i = 0;
@@ -169,12 +171,14 @@ const GodotWebSocket = {
return GodotWebSocket.send(p_id, out);
},
+ godot_js_websocket_close__sig: 'viii',
godot_js_websocket_close: function (p_id, p_code, p_reason) {
const code = p_code;
const reason = GodotRuntime.parseString(p_reason);
GodotWebSocket.close(p_id, code, reason);
},
+ godot_js_websocket_destroy__sig: 'vi',
godot_js_websocket_destroy: function (p_id) {
GodotWebSocket.destroy(p_id);
},
diff --git a/modules/xatlas_unwrap/SCsub b/modules/xatlas_unwrap/SCsub
index c659349d05..aa6bdaea33 100644
--- a/modules/xatlas_unwrap/SCsub
+++ b/modules/xatlas_unwrap/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_xatlas_unwrap = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
if env["builtin_xatlas"]:
thirdparty_dir = "#thirdparty/xatlas/"
thirdparty_sources = [
@@ -17,7 +20,16 @@ if env["builtin_xatlas"]:
env_thirdparty = env_xatlas_unwrap.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
+
# Godot source files
-env_xatlas_unwrap.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_xatlas_unwrap.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
+
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/xatlas_unwrap/register_types.cpp b/modules/xatlas_unwrap/register_types.cpp
index 65350518c3..224038d604 100644
--- a/modules/xatlas_unwrap/register_types.cpp
+++ b/modules/xatlas_unwrap/register_types.cpp
@@ -141,11 +141,11 @@ bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver
xatlas::Atlas *atlas = xatlas::Create();
printf("Adding mesh..\n");
- xatlas::AddMeshError::Enum err = xatlas::AddMesh(atlas, input_mesh, 1);
- ERR_FAIL_COND_V_MSG(err != xatlas::AddMeshError::Enum::Success, false, xatlas::StringForEnum(err));
+ xatlas::AddMeshError err = xatlas::AddMesh(atlas, input_mesh, 1);
+ ERR_FAIL_COND_V_MSG(err != xatlas::AddMeshError::Success, false, xatlas::StringForEnum(err));
printf("Generate..\n");
- xatlas::Generate(atlas, chart_options, xatlas::ParameterizeOptions(), pack_options);
+ xatlas::Generate(atlas, chart_options, pack_options);
*r_size_hint_x = atlas->width;
*r_size_hint_y = atlas->height;