diff options
Diffstat (limited to 'modules')
141 files changed, 7037 insertions, 7190 deletions
diff --git a/modules/bullet/SCsub b/modules/bullet/SCsub index bfac0df5b0..ba57de303e 100644 --- a/modules/bullet/SCsub +++ b/modules/bullet/SCsub @@ -12,6 +12,8 @@ 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. + if env["float"] == "64": + env.Append(CPPDEFINES=["BT_USE_DOUBLE_PRECISION=1"]) thirdparty_dir = "#thirdparty/bullet/" bullet2_src = [ diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp index e07be14c5b..fc876a81cf 100644 --- a/modules/bullet/bullet_physics_server.cpp +++ b/modules/bullet/bullet_physics_server.cpp @@ -842,12 +842,12 @@ PhysicsDirectBodyState3D *BulletPhysicsServer3D::body_get_direct_state(RID p_bod return BulletPhysicsDirectBodyState3D::get_singleton(body); } -bool BulletPhysicsServer3D::body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result, bool p_exclude_raycast_shapes) { +bool BulletPhysicsServer3D::body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result, bool p_exclude_raycast_shapes, const Set<RID> &p_exclude) { RigidBodyBullet *body = rigid_body_owner.getornull(p_body); ERR_FAIL_COND_V(!body, false); ERR_FAIL_COND_V(!body->get_space(), false); - return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, r_result, p_exclude_raycast_shapes); + return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, r_result, p_exclude_raycast_shapes, p_exclude); } int BulletPhysicsServer3D::body_test_ray_separation(RID p_body, const Transform3D &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, SeparationResult *r_results, int p_result_max, real_t p_margin) { diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h index d34d619ba2..7f0934e679 100644 --- a/modules/bullet/bullet_physics_server.h +++ b/modules/bullet/bullet_physics_server.h @@ -253,7 +253,7 @@ public: // this function only works on physics process, errors and returns null otherwise virtual PhysicsDirectBodyState3D *body_get_direct_state(RID p_body) override; - virtual bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true) override; + virtual bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true, const Set<RID> &p_exclude = Set<RID>()) override; virtual int body_test_ray_separation(RID p_body, const Transform3D &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, SeparationResult *r_results, int p_result_max, real_t p_margin = 0.001) override; /* SOFT BODY API */ diff --git a/modules/bullet/godot_result_callbacks.cpp b/modules/bullet/godot_result_callbacks.cpp index 399b102284..1f962772e7 100644 --- a/modules/bullet/godot_result_callbacks.cpp +++ b/modules/bullet/godot_result_callbacks.cpp @@ -135,6 +135,10 @@ bool GodotKinClosestConvexResultCallback::needsCollision(btBroadphaseProxy *prox if (m_self_object->has_collision_exception(gObj) || gObj->has_collision_exception(m_self_object)) { return false; } + + if (m_exclude->has(gObj->get_self())) { + return false; + } } return true; } else { diff --git a/modules/bullet/godot_result_callbacks.h b/modules/bullet/godot_result_callbacks.h index 9216322108..96a649d77a 100644 --- a/modules/bullet/godot_result_callbacks.h +++ b/modules/bullet/godot_result_callbacks.h @@ -100,11 +100,13 @@ public: struct GodotKinClosestConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback { public: const RigidBodyBullet *m_self_object; + const Set<RID> *m_exclude; const bool m_infinite_inertia; - GodotKinClosestConvexResultCallback(const btVector3 &convexFromWorld, const btVector3 &convexToWorld, const RigidBodyBullet *p_self_object, bool p_infinite_inertia) : + GodotKinClosestConvexResultCallback(const btVector3 &convexFromWorld, const btVector3 &convexToWorld, const RigidBodyBullet *p_self_object, bool p_infinite_inertia, const Set<RID> *p_exclude) : btCollisionWorld::ClosestConvexResultCallback(convexFromWorld, convexToWorld), m_self_object(p_self_object), + m_exclude(p_exclude), m_infinite_inertia(p_infinite_inertia) {} virtual bool needsCollision(btBroadphaseProxy *proxy0) const; diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp index ce39d4f0df..0d2cd1f5a0 100644 --- a/modules/bullet/rigid_body_bullet.cpp +++ b/modules/bullet/rigid_body_bullet.cpp @@ -114,6 +114,16 @@ Transform3D BulletPhysicsDirectBodyState3D::get_transform() const { return body->get_transform(); } +Vector3 BulletPhysicsDirectBodyState3D::get_velocity_at_local_position(const Vector3 &p_position) const { + btVector3 local_position; + G_TO_B(p_position, local_position); + + Vector3 velocity; + B_TO_G(body->btBody->getVelocityInLocalPoint(local_position), velocity); + + return velocity; +} + void BulletPhysicsDirectBodyState3D::add_central_force(const Vector3 &p_force) { body->apply_central_force(p_force); } diff --git a/modules/bullet/rigid_body_bullet.h b/modules/bullet/rigid_body_bullet.h index 606df7134b..01ac1e4836 100644 --- a/modules/bullet/rigid_body_bullet.h +++ b/modules/bullet/rigid_body_bullet.h @@ -110,6 +110,8 @@ public: virtual void set_transform(const Transform3D &p_transform) override; virtual Transform3D get_transform() const override; + virtual Vector3 get_velocity_at_local_position(const Vector3 &p_position) const override; + virtual void add_central_force(const Vector3 &p_force) override; virtual void add_force(const Vector3 &p_force, const Vector3 &p_position = Vector3()) override; virtual void add_torque(const Vector3 &p_torque) override; @@ -299,7 +301,7 @@ public: void reload_axis_lock(); /// Doc: - /// https://web.archive.org/web/20180404091446/http://www.bulletphysics.org/mediawiki-1.5.8/index.php/Anti_tunneling_by_Motion_Clamping + /// https://web.archive.org/web/20180404091446/https://www.bulletphysics.org/mediawiki-1.5.8/index.php/Anti_tunneling_by_Motion_Clamping void set_continuous_collision_detection(bool p_enable); bool is_continuous_collision_detection_enabled() const; diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp index 583900e6bc..a9a811c445 100644 --- a/modules/bullet/space_bullet.cpp +++ b/modules/bullet/space_bullet.cpp @@ -908,7 +908,7 @@ static Ref<StandardMaterial3D> red_mat; static Ref<StandardMaterial3D> blue_mat; #endif -bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer3D::MotionResult *r_result, bool p_exclude_raycast_shapes) { +bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer3D::MotionResult *r_result, bool p_exclude_raycast_shapes, const Set<RID> &p_exclude) { #if debug_test_motion /// Yes I know this is not good, but I've used it as fast debugging hack. /// I'm leaving it here just for speedup the other eventual debugs @@ -945,10 +945,15 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform3D &p G_TO_B(p_from, body_transform); UNSCALE_BT_BASIS(body_transform); + if (!p_body->get_kinematic_utilities()) { + p_body->init_kinematic_utilities(); + p_body->reload_kinematic_shapes(); + } + btVector3 initial_recover_motion(0, 0, 0); { /// Phase one - multi shapes depenetration using margin for (int t(RECOVERING_MOVEMENT_CYCLES); 0 < t; --t) { - if (!recover_from_penetration(p_body, body_transform, RECOVERING_MOVEMENT_SCALE, p_infinite_inertia, initial_recover_motion)) { + if (!recover_from_penetration(p_body, body_transform, RECOVERING_MOVEMENT_SCALE, p_infinite_inertia, initial_recover_motion, nullptr, p_exclude)) { break; } } @@ -958,6 +963,9 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform3D &p btVector3 motion; G_TO_B(p_motion, motion); + real_t total_length = motion.length(); + real_t unsafe_fraction = 1.0; + real_t safe_fraction = 1.0; { // Phase two - sweep test, from a secure position without margin @@ -1000,13 +1008,22 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform3D &p break; } - GodotKinClosestConvexResultCallback btResult(shape_world_from.getOrigin(), shape_world_to.getOrigin(), p_body, p_infinite_inertia); + GodotKinClosestConvexResultCallback btResult(shape_world_from.getOrigin(), shape_world_to.getOrigin(), p_body, p_infinite_inertia, &p_exclude); btResult.m_collisionFilterGroup = p_body->get_collision_layer(); btResult.m_collisionFilterMask = p_body->get_collision_mask(); dynamicsWorld->convexSweepTest(convex_shape_test, shape_world_from, shape_world_to, btResult, dynamicsWorld->getDispatchInfo().m_allowedCcdPenetration); if (btResult.hasHit()) { + if (total_length > CMP_EPSILON) { + real_t hit_fraction = btResult.m_closestHitFraction * motion.length() / total_length; + if (hit_fraction < unsafe_fraction) { + unsafe_fraction = hit_fraction; + real_t margin = p_body->get_kinematic_utilities()->safe_margin; + safe_fraction = MAX(hit_fraction - (1 - ((total_length - margin) / total_length)), 0); + } + } + /// Since for each sweep test I fix the motion of new shapes in base the recover result, /// if another shape will hit something it means that has a deepest penetration respect the previous shape motion *= btResult.m_closestHitFraction; @@ -1023,7 +1040,7 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform3D &p btVector3 __rec(0, 0, 0); RecoverResult r_recover_result; - has_penetration = recover_from_penetration(p_body, body_transform, 1, p_infinite_inertia, __rec, &r_recover_result); + has_penetration = recover_from_penetration(p_body, body_transform, 1, p_infinite_inertia, __rec, &r_recover_result, p_exclude); // Parse results if (r_result) { @@ -1043,6 +1060,9 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform3D &p r_result->collider_id = collisionObject->get_instance_id(); r_result->collider_shape = r_recover_result.other_compound_shape_index; r_result->collision_local_shape = r_recover_result.local_shape_most_recovered; + r_result->collision_depth = Math::abs(r_recover_result.penetration_distance); + r_result->collision_safe_fraction = safe_fraction; + r_result->collision_unsafe_fraction = unsafe_fraction; #if debug_test_motion Vector3 sup_line2; @@ -1067,6 +1087,11 @@ int SpaceBullet::test_ray_separation(RigidBodyBullet *p_body, const Transform3D G_TO_B(p_transform, body_transform); UNSCALE_BT_BASIS(body_transform); + if (!p_body->get_kinematic_utilities()) { + p_body->init_kinematic_utilities(); + p_body->reload_kinematic_shapes(); + } + btVector3 recover_motion(0, 0, 0); int rays_found = 0; @@ -1173,7 +1198,7 @@ public: } }; -bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result) { +bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result, const Set<RID> &p_exclude) { // Calculate the cumulative AABB of all shapes of the kinematic body btVector3 aabb_min, aabb_max; bool shapes_found = false; @@ -1242,6 +1267,12 @@ bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTran for (int i = recover_broad_result.results.size() - 1; 0 <= i; --i) { btCollisionObject *otherObject = recover_broad_result.results[i].collision_object; + + CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(otherObject->getUserPointer()); + if (p_exclude.has(gObj->get_self())) { + continue; + } + if (p_infinite_inertia && !otherObject->isStaticOrKinematicObject()) { otherObject->activate(); // Force activation of hitten rigid, soft body continue; diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h index 36d0538e6b..2070e0e633 100644 --- a/modules/bullet/space_bullet.h +++ b/modules/bullet/space_bullet.h @@ -188,7 +188,7 @@ public: real_t get_linear_damp() const { return linear_damp; } real_t get_angular_damp() const { return angular_damp; } - bool test_body_motion(RigidBodyBullet *p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer3D::MotionResult *r_result, bool p_exclude_raycast_shapes); + bool test_body_motion(RigidBodyBullet *p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer3D::MotionResult *r_result, bool p_exclude_raycast_shapes, const Set<RID> &p_exclude = Set<RID>()); int test_ray_separation(RigidBodyBullet *p_body, const Transform3D &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, PhysicsServer3D::SeparationResult *r_results, int p_result_max, real_t p_margin); private: @@ -209,7 +209,7 @@ private: RecoverResult() {} }; - bool recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = nullptr); + bool recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = nullptr, const Set<RID> &p_exclude = Set<RID>()); /// This is an API that recover a kinematic object from penetration /// This allow only Convex Convex test and it always use GJK algorithm, With this API we don't benefit of Bullet special accelerated functions bool RFP_convex_convex_test(const btConvexShape *p_shapeA, const btConvexShape *p_shapeB, btCollisionObject *p_objectB, int p_shapeId_A, int p_shapeId_B, const btTransform &p_transformA, const btTransform &p_transformB, btScalar p_recover_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = nullptr); diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index b47fa35f1a..bf11cc7f68 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "csg_shape.h" + #include "core/math/geometry_2d.h" -#include "scene/3d/path_3d.h" void CSGShape3D::set_use_collision(bool p_enable) { if (use_collision == p_enable) { @@ -88,36 +88,40 @@ uint32_t CSGShape3D::get_collision_mask() const { return collision_mask; } -void CSGShape3D::set_collision_mask_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); - uint32_t mask = get_collision_mask(); +void CSGShape3D::set_collision_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); + uint32_t collision_layer = get_collision_layer(); if (p_value) { - mask |= 1 << p_bit; + collision_layer |= 1 << (p_layer_number - 1); } else { - mask &= ~(1 << p_bit); + collision_layer &= ~(1 << (p_layer_number - 1)); } - set_collision_mask(mask); + set_collision_layer(collision_layer); } -bool CSGShape3D::get_collision_mask_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); - return get_collision_mask() & (1 << p_bit); +bool CSGShape3D::get_collision_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_layer() & (1 << (p_layer_number - 1)); } -void CSGShape3D::set_collision_layer_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); - uint32_t layer = get_collision_layer(); +void CSGShape3D::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); + uint32_t mask = get_collision_mask(); if (p_value) { - layer |= 1 << p_bit; + mask |= 1 << (p_layer_number - 1); } else { - layer &= ~(1 << p_bit); + mask &= ~(1 << (p_layer_number - 1)); } - set_collision_layer(layer); + set_collision_mask(mask); } -bool CSGShape3D::get_collision_layer_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision layer bit must be between 0 and 31 inclusive."); - return get_collision_layer() & (1 << p_bit); +bool CSGShape3D::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); } bool CSGShape3D::is_root_shape() const { @@ -605,11 +609,11 @@ void CSGShape3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &CSGShape3D::set_collision_mask); ClassDB::bind_method(D_METHOD("get_collision_mask"), &CSGShape3D::get_collision_mask); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &CSGShape3D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &CSGShape3D::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &CSGShape3D::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &CSGShape3D::get_collision_mask_value); - ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &CSGShape3D::set_collision_layer_bit); - ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &CSGShape3D::get_collision_layer_bit); + ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &CSGShape3D::set_collision_layer_value); + ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &CSGShape3D::get_collision_layer_value); ClassDB::bind_method(D_METHOD("set_calculate_tangents", "enabled"), &CSGShape3D::set_calculate_tangents); ClassDB::bind_method(D_METHOD("is_calculating_tangents"), &CSGShape3D::is_calculating_tangents); @@ -1676,109 +1680,80 @@ CSGTorus3D::CSGTorus3D() { /////////////// CSGBrush *CSGPolygon3D::_build_brush() { - // set our bounding box + CSGBrush *brush = memnew(CSGBrush); if (polygon.size() < 3) { - return memnew(CSGBrush); + return brush; } - Vector<Point2> final_polygon = polygon; - - if (Triangulate::get_area(final_polygon) > 0) { - final_polygon.reverse(); + // Triangulate polygon shape. + Vector<Point2> shape_polygon = polygon; + if (Triangulate::get_area(shape_polygon) > 0) { + shape_polygon.reverse(); } - - Vector<int> triangles = Geometry2D::triangulate_polygon(final_polygon); - - if (triangles.size() < 3) { - return memnew(CSGBrush); + int shape_sides = shape_polygon.size(); + Vector<int> shape_faces = Geometry2D::triangulate_polygon(shape_polygon); + ERR_FAIL_COND_V_MSG(shape_faces.size() < 3, brush, "Failed to triangulate CSGPolygon"); + + // Get polygon enclosing Rect2. + Rect2 shape_rect(shape_polygon[0], Vector2()); + for (int i = 1; i < shape_sides; i++) { + shape_rect.expand_to(shape_polygon[i]); } - Path3D *path = nullptr; + // If MODE_PATH, check if curve has changed. Ref<Curve3D> curve; - - // get bounds for our polygon - Vector2 final_polygon_min; - Vector2 final_polygon_max; - for (int i = 0; i < final_polygon.size(); i++) { - Vector2 p = final_polygon[i]; - if (i == 0) { - final_polygon_min = p; - final_polygon_max = final_polygon_min; - } else { - if (p.x < final_polygon_min.x) { - final_polygon_min.x = p.x; - } - if (p.y < final_polygon_min.y) { - final_polygon_min.y = p.y; - } - - if (p.x > final_polygon_max.x) { - final_polygon_max.x = p.x; + if (mode == MODE_PATH) { + Path3D *current_path = Object::cast_to<Path3D>(get_node_or_null(path_node)); + if (path != current_path) { + if (path) { + path->disconnect("tree_exited", callable_mp(this, &CSGPolygon3D::_path_exited)); + path->disconnect("curve_changed", callable_mp(this, &CSGPolygon3D::_path_changed)); } - if (p.y > final_polygon_max.y) { - final_polygon_max.y = p.y; + path = current_path; + if (path) { + path->connect("tree_exited", callable_mp(this, &CSGPolygon3D::_path_exited)); + path->connect("curve_changed", callable_mp(this, &CSGPolygon3D::_path_changed)); } } - } - Vector2 final_polygon_size = final_polygon_max - final_polygon_min; - if (mode == MODE_PATH) { - if (!has_node(path_node)) { - return memnew(CSGBrush); - } - Node *n = get_node(path_node); - if (!n) { - return memnew(CSGBrush); - } - path = Object::cast_to<Path3D>(n); if (!path) { - return memnew(CSGBrush); + return brush; } - if (path != path_cache) { - if (path_cache) { - path_cache->disconnect("tree_exited", callable_mp(this, &CSGPolygon3D::_path_exited)); - path_cache->disconnect("curve_changed", callable_mp(this, &CSGPolygon3D::_path_changed)); - path_cache = nullptr; - } - - path_cache = path; - - path_cache->connect("tree_exited", callable_mp(this, &CSGPolygon3D::_path_exited)); - path_cache->connect("curve_changed", callable_mp(this, &CSGPolygon3D::_path_changed)); - } curve = path->get_curve(); - if (curve.is_null()) { - return memnew(CSGBrush); - } - if (curve->get_baked_length() <= 0) { - return memnew(CSGBrush); + if (curve.is_null() || curve->get_point_count() < 2) { + return brush; } } - CSGBrush *brush = memnew(CSGBrush); - - int face_count = 0; + // Calculate the number extrusions, ends and faces. + int extrusions = 0; + int extrusion_face_count = shape_sides * 2; + int end_count = 0; + int shape_face_count = shape_faces.size() / 3; switch (mode) { case MODE_DEPTH: - face_count = triangles.size() * 2 / 3 + (final_polygon.size()) * 2; + extrusions = 1; + end_count = 2; break; case MODE_SPIN: - face_count = (spin_degrees < 360 ? triangles.size() * 2 / 3 : 0) + (final_polygon.size()) * 2 * spin_sides; + extrusions = spin_sides; + if (spin_degrees < 360) { + end_count = 2; + } break; case MODE_PATH: { - float bl = curve->get_baked_length(); - int splits = MAX(2, Math::ceil(bl / path_interval)); - if (path_joined) { - face_count = splits * final_polygon.size() * 2; - } else { - face_count = triangles.size() * 2 / 3 + splits * final_polygon.size() * 2; + extrusions = Math::ceil(1.0 * curve->get_point_count() / path_interval); + if (!path_joined) { + end_count = 2; + extrusions -= 1; } } break; } + int face_count = extrusions * extrusion_face_count + end_count * shape_face_count; - bool invert_val = is_inverting_faces(); + // Intialize variables used to create the mesh. Ref<Material> material = get_material(); Vector<Vector3> faces; @@ -1789,362 +1764,216 @@ CSGBrush *CSGPolygon3D::_build_brush() { faces.resize(face_count * 3); uvs.resize(face_count * 3); - smooth.resize(face_count); materials.resize(face_count); invert.resize(face_count); - AABB aabb; //must be computed - { - Vector3 *facesw = faces.ptrw(); - Vector2 *uvsw = uvs.ptrw(); - bool *smoothw = smooth.ptrw(); - Ref<Material> *materialsw = materials.ptrw(); - bool *invertw = invert.ptrw(); - - int face = 0; - - switch (mode) { - case MODE_DEPTH: { - //add triangles, front and back - for (int i = 0; i < 2; i++) { - for (int j = 0; j < triangles.size(); j += 3) { - for (int k = 0; k < 3; k++) { - int src[3] = { 0, i == 0 ? 1 : 2, i == 0 ? 2 : 1 }; - Vector2 p = final_polygon[triangles[j + src[k]]]; - Vector3 v = Vector3(p.x, p.y, 0); - if (i == 0) { - v.z -= depth; - } - facesw[face * 3 + k] = v; - uvsw[face * 3 + k] = (p - final_polygon_min) / final_polygon_size; - if (i == 0) { - uvsw[face * 3 + k].x = 1.0 - uvsw[face * 3 + k].x; /* flip x */ - } - } - - smoothw[face] = false; - materialsw[face] = material; - invertw[face] = invert_val; - face++; - } - } - - //add triangles for depth - for (int i = 0; i < final_polygon.size(); i++) { - int i_n = (i + 1) % final_polygon.size(); - - Vector3 v[4] = { - Vector3(final_polygon[i].x, final_polygon[i].y, -depth), - Vector3(final_polygon[i_n].x, final_polygon[i_n].y, -depth), - Vector3(final_polygon[i_n].x, final_polygon[i_n].y, 0), - Vector3(final_polygon[i].x, final_polygon[i].y, 0), - }; - - Vector2 u[4] = { - Vector2(0, 0), - Vector2(0, 1), - Vector2(1, 1), - Vector2(1, 0) - }; + Vector3 *facesw = faces.ptrw(); + Vector2 *uvsw = uvs.ptrw(); + bool *smoothw = smooth.ptrw(); + Ref<Material> *materialsw = materials.ptrw(); + bool *invertw = invert.ptrw(); + + int face = 0; + Transform3D base_xform; + Transform3D current_xform; + Transform3D previous_xform; + double u_step = 1.0 / extrusions; + double v_step = 1.0 / shape_sides; + double spin_step = Math::deg2rad(spin_degrees / spin_sides); + double extrusion_step = 1.0 / extrusions; + if (mode == MODE_PATH) { + if (path_joined) { + extrusion_step = 1.0 / (extrusions - 1); + } + extrusion_step *= curve->get_baked_length(); + } - // face 1 - facesw[face * 3 + 0] = v[0]; - facesw[face * 3 + 1] = v[1]; - facesw[face * 3 + 2] = v[2]; + if (mode == MODE_PATH) { + if (!path_local) { + base_xform = path->get_global_transform(); + } - uvsw[face * 3 + 0] = u[0]; - uvsw[face * 3 + 1] = u[1]; - uvsw[face * 3 + 2] = u[2]; + Vector3 current_point = curve->interpolate_baked(0); + Vector3 next_point = curve->interpolate_baked(extrusion_step); + Vector3 current_up = Vector3(0, 1, 0); + Vector3 direction = next_point - current_point; - smoothw[face] = smooth_faces; - invertw[face] = invert_val; - materialsw[face] = material; + if (path_joined) { + Vector3 last_point = curve->interpolate_baked(curve->get_baked_length()); + direction = next_point - last_point; + } - face++; + switch (path_rotation) { + case PATH_ROTATION_POLYGON: + direction = Vector3(0, 0, -1); + break; + case PATH_ROTATION_PATH: + break; + case PATH_ROTATION_PATH_FOLLOW: + current_up = curve->interpolate_baked_up_vector(0); + break; + } - // face 2 - facesw[face * 3 + 0] = v[2]; - facesw[face * 3 + 1] = v[3]; - facesw[face * 3 + 2] = v[0]; + Transform3D facing = Transform3D().looking_at(direction, current_up); + current_xform = base_xform.translated(current_point) * facing; + } - uvsw[face * 3 + 0] = u[2]; - uvsw[face * 3 + 1] = u[3]; - uvsw[face * 3 + 2] = u[0]; + // Create the mesh. + if (end_count > 0) { + // Add front end face. + for (int face_idx = 0; face_idx < shape_face_count; face_idx++) { + for (int face_vertex_idx = 0; face_vertex_idx < 3; face_vertex_idx++) { + // We need to reverse the rotation of the shape face vertices. + int index = shape_faces[face_idx * 3 + 2 - face_vertex_idx]; + Point2 p = shape_polygon[index]; + Point2 uv = (p - shape_rect.position) / shape_rect.size; + + // Use the left side of the bottom half of the y-inverted texture. + uv.x = uv.x / 2; + uv.y = 1 - (uv.y / 2); + + facesw[face * 3 + face_vertex_idx] = current_xform.xform(Vector3(p.x, p.y, 0)); + uvsw[face * 3 + face_vertex_idx] = uv; + } - smoothw[face] = smooth_faces; - invertw[face] = invert_val; - materialsw[face] = material; + smoothw[face] = false; + materialsw[face] = material; + invertw[face] = invert_faces; + face++; + } + } - face++; - } + // Add extrusion faces. + for (int x0 = 0; x0 < extrusions; x0++) { + previous_xform = current_xform; + switch (mode) { + case MODE_DEPTH: { + current_xform.translate(Vector3(0, 0, -depth)); } break; case MODE_SPIN: { - for (int i = 0; i < spin_sides; i++) { - float inci = float(i) / spin_sides; - float inci_n = float((i + 1)) / spin_sides; - - float angi = -Math::deg2rad(inci * spin_degrees); - float angi_n = -Math::deg2rad(inci_n * spin_degrees); - - Vector3 normali = Vector3(Math::cos(angi), 0, Math::sin(angi)); - Vector3 normali_n = Vector3(Math::cos(angi_n), 0, Math::sin(angi_n)); - - //add triangles for depth - for (int j = 0; j < final_polygon.size(); j++) { - int j_n = (j + 1) % final_polygon.size(); - - Vector3 v[4] = { - Vector3(normali.x * final_polygon[j].x, final_polygon[j].y, normali.z * final_polygon[j].x), - Vector3(normali.x * final_polygon[j_n].x, final_polygon[j_n].y, normali.z * final_polygon[j_n].x), - Vector3(normali_n.x * final_polygon[j_n].x, final_polygon[j_n].y, normali_n.z * final_polygon[j_n].x), - Vector3(normali_n.x * final_polygon[j].x, final_polygon[j].y, normali_n.z * final_polygon[j].x), - }; - - Vector2 u[4] = { - Vector2(0, 0), - Vector2(0, 1), - Vector2(1, 1), - Vector2(1, 0) - }; - - // face 1 - facesw[face * 3 + 0] = v[0]; - facesw[face * 3 + 1] = v[2]; - facesw[face * 3 + 2] = v[1]; - - uvsw[face * 3 + 0] = u[0]; - uvsw[face * 3 + 1] = u[2]; - uvsw[face * 3 + 2] = u[1]; - - smoothw[face] = smooth_faces; - invertw[face] = invert_val; - materialsw[face] = material; - - face++; - - // face 2 - facesw[face * 3 + 0] = v[2]; - facesw[face * 3 + 1] = v[0]; - facesw[face * 3 + 2] = v[3]; - - uvsw[face * 3 + 0] = u[2]; - uvsw[face * 3 + 1] = u[0]; - uvsw[face * 3 + 2] = u[3]; - - smoothw[face] = smooth_faces; - invertw[face] = invert_val; - materialsw[face] = material; - - face++; - } - - if (i == 0 && spin_degrees < 360) { - for (int j = 0; j < triangles.size(); j += 3) { - for (int k = 0; k < 3; k++) { - int src[3] = { 0, 2, 1 }; - Vector2 p = final_polygon[triangles[j + src[k]]]; - Vector3 v = Vector3(p.x, p.y, 0); - facesw[face * 3 + k] = v; - uvsw[face * 3 + k] = (p - final_polygon_min) / final_polygon_size; - } - - smoothw[face] = false; - materialsw[face] = material; - invertw[face] = invert_val; - face++; - } - } - - if (i == spin_sides - 1 && spin_degrees < 360) { - for (int j = 0; j < triangles.size(); j += 3) { - for (int k = 0; k < 3; k++) { - int src[3] = { 0, 1, 2 }; - Vector2 p = final_polygon[triangles[j + src[k]]]; - Vector3 v = Vector3(normali_n.x * p.x, p.y, normali_n.z * p.x); - facesw[face * 3 + k] = v; - uvsw[face * 3 + k] = (p - final_polygon_min) / final_polygon_size; - uvsw[face * 3 + k].x = 1.0 - uvsw[face * 3 + k].x; /* flip x */ - } - - smoothw[face] = false; - materialsw[face] = material; - invertw[face] = invert_val; - face++; - } - } - } + current_xform.rotate(Vector3(0, 1, 0), spin_step); } break; case MODE_PATH: { - float bl = curve->get_baked_length(); - int splits = MAX(2, Math::ceil(bl / path_interval)); - float u1 = 0.0; - float u2 = path_continuous_u ? 0.0 : 1.0; - - Transform3D path_to_this; - if (!path_local) { - // center on paths origin - path_to_this = get_global_transform().affine_inverse() * path->get_global_transform(); - } - - Transform3D prev_xf; - - Vector3 lookat_dir; - - if (path_rotation == PATH_ROTATION_POLYGON) { - lookat_dir = (path->get_global_transform().affine_inverse() * get_global_transform()).xform(Vector3(0, 0, -1)); - } else { - Vector3 p1, p2; - p1 = curve->interpolate_baked(0); - p2 = curve->interpolate_baked(0.1); - lookat_dir = (p2 - p1).normalized(); - } - - for (int i = 0; i <= splits; i++) { - float ofs = i * path_interval; - if (ofs > bl) { - ofs = bl; - } - if (i == splits && path_joined) { - ofs = 0.0; - } - - Transform3D xf; - xf.origin = curve->interpolate_baked(ofs); - - Vector3 local_dir; - - if (path_rotation == PATH_ROTATION_PATH_FOLLOW && ofs > 0) { - //before end - Vector3 p1 = curve->interpolate_baked(ofs - 0.1); - Vector3 p2 = curve->interpolate_baked(ofs); - local_dir = (p2 - p1).normalized(); - + double previous_offset = x0 * extrusion_step; + double current_offset = (x0 + 1) * extrusion_step; + double next_offset = (x0 + 2) * extrusion_step; + if (x0 == extrusions - 1) { + if (path_joined) { + current_offset = 0; + next_offset = extrusion_step; } else { - local_dir = lookat_dir; - } - - xf = xf.looking_at(xf.origin + local_dir, Vector3(0, 1, 0)); - Basis rot(Vector3(0, 0, 1), curve->interpolate_baked_tilt(ofs)); - - xf = xf * rot; //post mult - - xf = path_to_this * xf; - - if (i > 0) { - if (path_continuous_u) { - u1 = u2; - u2 += (prev_xf.origin - xf.origin).length(); - }; - - //put triangles where they belong - //add triangles for depth - for (int j = 0; j < final_polygon.size(); j++) { - int j_n = (j + 1) % final_polygon.size(); - - Vector3 v[4] = { - prev_xf.xform(Vector3(final_polygon[j].x, final_polygon[j].y, 0)), - prev_xf.xform(Vector3(final_polygon[j_n].x, final_polygon[j_n].y, 0)), - xf.xform(Vector3(final_polygon[j_n].x, final_polygon[j_n].y, 0)), - xf.xform(Vector3(final_polygon[j].x, final_polygon[j].y, 0)), - }; - - Vector2 u[4] = { - Vector2(u1, 1), - Vector2(u1, 0), - Vector2(u2, 0), - Vector2(u2, 1) - }; - - // face 1 - facesw[face * 3 + 0] = v[0]; - facesw[face * 3 + 1] = v[1]; - facesw[face * 3 + 2] = v[2]; - - uvsw[face * 3 + 0] = u[0]; - uvsw[face * 3 + 1] = u[1]; - uvsw[face * 3 + 2] = u[2]; - - smoothw[face] = smooth_faces; - invertw[face] = invert_val; - materialsw[face] = material; - - face++; - - // face 2 - facesw[face * 3 + 0] = v[2]; - facesw[face * 3 + 1] = v[3]; - facesw[face * 3 + 2] = v[0]; - - uvsw[face * 3 + 0] = u[2]; - uvsw[face * 3 + 1] = u[3]; - uvsw[face * 3 + 2] = u[0]; - - smoothw[face] = smooth_faces; - invertw[face] = invert_val; - materialsw[face] = material; - - face++; - } - } - - if (i == 0 && !path_joined) { - for (int j = 0; j < triangles.size(); j += 3) { - for (int k = 0; k < 3; k++) { - int src[3] = { 0, 1, 2 }; - Vector2 p = final_polygon[triangles[j + src[k]]]; - Vector3 v = Vector3(p.x, p.y, 0); - facesw[face * 3 + k] = xf.xform(v); - uvsw[face * 3 + k] = (p - final_polygon_min) / final_polygon_size; - } - - smoothw[face] = false; - materialsw[face] = material; - invertw[face] = invert_val; - face++; - } + next_offset = current_offset; } + } - if (i == splits && !path_joined) { - for (int j = 0; j < triangles.size(); j += 3) { - for (int k = 0; k < 3; k++) { - int src[3] = { 0, 2, 1 }; - Vector2 p = final_polygon[triangles[j + src[k]]]; - Vector3 v = Vector3(p.x, p.y, 0); - facesw[face * 3 + k] = xf.xform(v); - uvsw[face * 3 + k] = (p - final_polygon_min) / final_polygon_size; - uvsw[face * 3 + k].x = 1.0 - uvsw[face * 3 + k].x; /* flip x */ - } - - smoothw[face] = false; - materialsw[face] = material; - invertw[face] = invert_val; - face++; - } - } + Vector3 previous_point = curve->interpolate_baked(previous_offset); + Vector3 current_point = curve->interpolate_baked(current_offset); + Vector3 next_point = curve->interpolate_baked(next_offset); + Vector3 current_up = Vector3(0, 1, 0); + Vector3 direction = next_point - previous_point; - prev_xf = xf; + switch (path_rotation) { + case PATH_ROTATION_POLYGON: + direction = Vector3(0, 0, -1); + break; + case PATH_ROTATION_PATH: + break; + case PATH_ROTATION_PATH_FOLLOW: + current_up = curve->interpolate_baked_up_vector(current_offset); + break; } + Transform3D facing = Transform3D().looking_at(direction, current_up); + current_xform = base_xform.translated(current_point) * facing; } break; } - if (face != face_count) { - ERR_PRINT("Face mismatch bug! fix code"); + double u0 = x0 * u_step; + double u1 = ((x0 + 1) * u_step); + if (mode == MODE_PATH && !path_continuous_u) { + u0 = 0.0; + u1 = 1.0; } - for (int i = 0; i < face_count * 3; i++) { - if (i == 0) { - aabb.position = facesw[i]; - } else { - aabb.expand_to(facesw[i]); + + for (int y0 = 0; y0 < shape_sides; y0++) { + int y1 = (y0 + 1) % shape_sides; + // Use the top half of the texture. + double v0 = (y0 * v_step) / 2; + double v1 = ((y0 + 1) * v_step) / 2; + + Vector3 v[4] = { + previous_xform.xform(Vector3(shape_polygon[y0].x, shape_polygon[y0].y, 0)), + current_xform.xform(Vector3(shape_polygon[y0].x, shape_polygon[y0].y, 0)), + current_xform.xform(Vector3(shape_polygon[y1].x, shape_polygon[y1].y, 0)), + previous_xform.xform(Vector3(shape_polygon[y1].x, shape_polygon[y1].y, 0)), + }; + + Vector2 u[4] = { + Vector2(u0, v0), + Vector2(u1, v0), + Vector2(u1, v1), + Vector2(u0, v1), + }; + + // Face 1 + facesw[face * 3 + 0] = v[0]; + facesw[face * 3 + 1] = v[1]; + facesw[face * 3 + 2] = v[2]; + + uvsw[face * 3 + 0] = u[0]; + uvsw[face * 3 + 1] = u[1]; + uvsw[face * 3 + 2] = u[2]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_faces; + materialsw[face] = material; + + face++; + + // Face 2 + facesw[face * 3 + 0] = v[2]; + facesw[face * 3 + 1] = v[3]; + facesw[face * 3 + 2] = v[0]; + + uvsw[face * 3 + 0] = u[2]; + uvsw[face * 3 + 1] = u[3]; + uvsw[face * 3 + 2] = u[0]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_faces; + materialsw[face] = material; + + face++; + } + } + + if (end_count > 1) { + // Add back end face. + for (int face_idx = 0; face_idx < shape_face_count; face_idx++) { + for (int face_vertex_idx = 0; face_vertex_idx < 3; face_vertex_idx++) { + int index = shape_faces[face_idx * 3 + face_vertex_idx]; + Point2 p = shape_polygon[index]; + Point2 uv = (p - shape_rect.position) / shape_rect.size; + + // Use the x-inverted ride side of the bottom half of the y-inverted texture. + uv.x = 1 - uv.x / 2; + uv.y = 1 - (uv.y / 2); + + facesw[face * 3 + face_vertex_idx] = current_xform.xform(Vector3(p.x, p.y, 0)); + uvsw[face * 3 + face_vertex_idx] = uv; } - // invert UVs on the Y-axis OpenGL = upside down - uvsw[i].y = 1.0 - uvsw[i].y; + smoothw[face] = false; + materialsw[face] = material; + invertw[face] = invert_faces; + face++; } } + ERR_FAIL_COND_V_MSG(face != face_count, brush, "Bug: Failed to create the CSGPolygon mesh correctly."); + brush->build_from_faces(faces, uvs, smooth, materials, invert); return brush; @@ -2152,10 +1981,10 @@ CSGBrush *CSGPolygon3D::_build_brush() { void CSGPolygon3D::_notification(int p_what) { if (p_what == NOTIFICATION_EXIT_TREE) { - if (path_cache) { - path_cache->disconnect("tree_exited", callable_mp(this, &CSGPolygon3D::_path_exited)); - path_cache->disconnect("curve_changed", callable_mp(this, &CSGPolygon3D::_path_changed)); - path_cache = nullptr; + if (path) { + path->disconnect("tree_exited", callable_mp(this, &CSGPolygon3D::_path_exited)); + path->disconnect("curve_changed", callable_mp(this, &CSGPolygon3D::_path_changed)); + path = nullptr; } } } @@ -2180,7 +2009,7 @@ void CSGPolygon3D::_path_changed() { } void CSGPolygon3D::_path_exited() { - path_cache = nullptr; + path = nullptr; } void CSGPolygon3D::_bind_methods() { @@ -2202,10 +2031,10 @@ void CSGPolygon3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_path_node", "path"), &CSGPolygon3D::set_path_node); ClassDB::bind_method(D_METHOD("get_path_node"), &CSGPolygon3D::get_path_node); - ClassDB::bind_method(D_METHOD("set_path_interval", "distance"), &CSGPolygon3D::set_path_interval); + ClassDB::bind_method(D_METHOD("set_path_interval", "interval"), &CSGPolygon3D::set_path_interval); ClassDB::bind_method(D_METHOD("get_path_interval"), &CSGPolygon3D::get_path_interval); - ClassDB::bind_method(D_METHOD("set_path_rotation", "mode"), &CSGPolygon3D::set_path_rotation); + ClassDB::bind_method(D_METHOD("set_path_rotation", "path_rotation"), &CSGPolygon3D::set_path_rotation); ClassDB::bind_method(D_METHOD("get_path_rotation"), &CSGPolygon3D::get_path_rotation); ClassDB::bind_method(D_METHOD("set_path_local", "enable"), &CSGPolygon3D::set_path_local); @@ -2228,11 +2057,11 @@ void CSGPolygon3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon"); ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Depth,Spin,Path"), "set_mode", "get_mode"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_depth", "get_depth"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_RANGE, "0.01,100.0,0.01,or_greater,exp"), "set_depth", "get_depth"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "spin_degrees", PROPERTY_HINT_RANGE, "1,360,0.1"), "set_spin_degrees", "get_spin_degrees"); ADD_PROPERTY(PropertyInfo(Variant::INT, "spin_sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_spin_sides", "get_spin_sides"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "path_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Path3D"), "set_path_node", "get_path_node"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_interval", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_path_interval", "get_path_interval"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_interval", PROPERTY_HINT_RANGE, "0.1,1.0,0.05,exp"), "set_path_interval", "get_path_interval"); ADD_PROPERTY(PropertyInfo(Variant::INT, "path_rotation", PROPERTY_HINT_ENUM, "Polygon,Path,PathFollow"), "set_path_rotation", "get_path_rotation"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_local"), "set_path_local", "is_path_local"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_continuous_u"), "set_path_continuous_u", "is_path_continuous_u"); @@ -2301,7 +2130,7 @@ float CSGPolygon3D::get_spin_degrees() const { return spin_degrees; } -void CSGPolygon3D::set_spin_sides(const int p_spin_sides) { +void CSGPolygon3D::set_spin_sides(int p_spin_sides) { ERR_FAIL_COND(p_spin_sides < 3); spin_sides = p_spin_sides; _make_dirty(); @@ -2323,7 +2152,7 @@ NodePath CSGPolygon3D::get_path_node() const { } void CSGPolygon3D::set_path_interval(float p_interval) { - ERR_FAIL_COND_MSG(p_interval < 0.001, "Path interval cannot be smaller than 0.001."); + ERR_FAIL_COND_MSG(p_interval <= 0 || p_interval > 1, "Path interval must be greater than 0 and less than or equal to 1.0."); path_interval = p_interval; _make_dirty(); update_gizmos(); @@ -2400,10 +2229,10 @@ CSGPolygon3D::CSGPolygon3D() { spin_degrees = 360; spin_sides = 8; smooth_faces = false; - path_interval = 1; - path_rotation = PATH_ROTATION_PATH; + path_interval = 1.0; + path_rotation = PATH_ROTATION_PATH_FOLLOW; path_local = false; - path_continuous_u = false; + path_continuous_u = true; path_joined = false; - path_cache = nullptr; + path = nullptr; } diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h index de7de09f00..5cf371665e 100644 --- a/modules/csg/csg_shape.h +++ b/modules/csg/csg_shape.h @@ -34,6 +34,7 @@ #define CSGJS_HEADER_ONLY #include "csg.h" +#include "scene/3d/path_3d.h" #include "scene/3d/visual_instance_3d.h" #include "scene/resources/concave_polygon_shape_3d.h" #include "thirdparty/misc/mikktspace.h" @@ -83,14 +84,14 @@ private: Vector<Vector3> vertices; Vector<Vector3> normals; Vector<Vector2> uvs; - Vector<float> tans; + Vector<real_t> tans; Ref<Material> material; int last_added = 0; Vector3 *verticesw = nullptr; Vector3 *normalsw = nullptr; Vector2 *uvsw = nullptr; - float *tansw = nullptr; + real_t *tansw = nullptr; }; //mikktspace callbacks @@ -136,11 +137,11 @@ public: void set_collision_mask(uint32_t p_mask); uint32_t get_collision_mask() const; - void set_collision_layer_bit(int p_bit, bool p_value); - bool get_collision_layer_bit(int p_bit) const; + void set_collision_layer_value(int p_layer_number, bool p_value); + bool get_collision_layer_value(int p_layer_number) const; - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) const; void set_snap(float p_snap); float get_snap() const; @@ -168,10 +169,8 @@ public: class CSGPrimitive3D : public CSGShape3D { GDCLASS(CSGPrimitive3D, CSGShape3D); -private: - bool invert_faces; - protected: + bool invert_faces; CSGBrush *_create_brush_from_arrays(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uv, const Vector<bool> &p_smooth, const Vector<Ref<Material>> &p_materials); static void _bind_methods(); @@ -361,7 +360,7 @@ private: PathRotation path_rotation; bool path_local; - Node *path_cache; + Path3D *path; bool smooth_faces; bool path_continuous_u; diff --git a/modules/csg/doc_classes/CSGPolygon3D.xml b/modules/csg/doc_classes/CSGPolygon3D.xml index 4f29786779..5309cde956 100644 --- a/modules/csg/doc_classes/CSGPolygon3D.xml +++ b/modules/csg/doc_classes/CSGPolygon3D.xml @@ -4,7 +4,7 @@ Extrudes a 2D polygon shape to create a 3D mesh. </brief_description> <description> - This node takes a 2D polygon shape and extrudes it to create a 3D mesh. + An array of 2D points is extruded to quickly and easily create a variety of 3D meshes. </description> <tutorials> </tutorials> @@ -12,63 +12,65 @@ </methods> <members> <member name="depth" type="float" setter="set_depth" getter="get_depth" default="1.0"> - Extrusion depth when [member mode] is [constant MODE_DEPTH]. + When [member mode] is [constant MODE_DEPTH], the depth of the extrusion. </member> <member name="material" type="Material" setter="set_material" getter="get_material"> - Material to use for the resulting mesh. + Material to use for the resulting mesh. The UV maps the top half of the material to the extruded shape (U along the length of the extrusions and V around the outline of the [member polygon]), the bottom-left quarter to the front end face, and the bottom-right quarter to the back end face. </member> <member name="mode" type="int" setter="set_mode" getter="get_mode" enum="CSGPolygon3D.Mode" default="0"> - Extrusion mode. + The [member mode] used to extrude the [member polygon]. </member> <member name="path_continuous_u" type="bool" setter="set_path_continuous_u" getter="is_path_continuous_u"> - If [code]true[/code] the u component of our uv will continuously increase in unison with the distance traveled along our path when [member mode] is [constant MODE_PATH]. + When [member mode] is [constant MODE_PATH], by default, the top half of the [member material] is stretched along the entire length of the extruded shape. If [code]false[/code] the top half of the material is repeated every step of the extrusion. </member> <member name="path_interval" type="float" setter="set_path_interval" getter="get_path_interval"> - Interval at which a new extrusion slice is added along the path when [member mode] is [constant MODE_PATH]. + When [member mode] is [constant MODE_PATH], the path interval or ratio of path points to extrusions. </member> <member name="path_joined" type="bool" setter="set_path_joined" getter="is_path_joined"> - If [code]true[/code] the start and end of our path are joined together ensuring there is no seam when [member mode] is [constant MODE_PATH]. + When [member mode] is [constant MODE_PATH], if [code]true[/code] the ends of the path are joined, by adding an extrusion between the last and first points of the path. </member> <member name="path_local" type="bool" setter="set_path_local" getter="is_path_local"> - If [code]false[/code] we extrude centered on our path, if [code]true[/code] we extrude in relation to the position of our CSGPolygon3D when [member mode] is [constant MODE_PATH]. + When [member mode] is [constant MODE_PATH], if [code]true[/code] the [Transform3D] of the [CSGPolygon3D] is used as the starting point for the extrusions, not the [Transform3D] of the [member path_node]. </member> <member name="path_node" type="NodePath" setter="set_path_node" getter="get_path_node"> - The [Shape3D] object containing the path along which we extrude when [member mode] is [constant MODE_PATH]. + When [member mode] is [constant MODE_PATH], the location of the [Path3D] object used to extrude the [member polygon]. </member> <member name="path_rotation" type="int" setter="set_path_rotation" getter="get_path_rotation" enum="CSGPolygon3D.PathRotation"> - The method by which each slice is rotated along the path when [member mode] is [constant MODE_PATH]. + When [member mode] is [constant MODE_PATH], the [enum PathRotation] method used to rotate the [member polygon] as it is extruded. </member> <member name="polygon" type="PackedVector2Array" setter="set_polygon" getter="get_polygon" default="PackedVector2Array(0, 0, 0, 1, 1, 1, 1, 0)"> - Point array that defines the shape that we'll extrude. + The point array that defines the 2D polygon that is extruded. </member> <member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces" default="false"> - Generates smooth normals so smooth shading is applied to our mesh. + If [code]true[/code], applies smooth shading to the extrusions. </member> <member name="spin_degrees" type="float" setter="set_spin_degrees" getter="get_spin_degrees"> - Degrees to rotate our extrusion for each slice when [member mode] is [constant MODE_SPIN]. + When [member mode] is [constant MODE_SPIN], the total number of degrees the [member polygon] is rotated when extruding. </member> <member name="spin_sides" type="int" setter="set_spin_sides" getter="get_spin_sides"> - Number of extrusion when [member mode] is [constant MODE_SPIN]. + When [member mode] is [constant MODE_SPIN], the number of extrusions made. </member> </members> <constants> <constant name="MODE_DEPTH" value="0" enum="Mode"> - Shape3D is extruded to [member depth]. + The [member polygon] shape is extruded along the negative Z axis. </constant> <constant name="MODE_SPIN" value="1" enum="Mode"> - Shape3D is extruded by rotating it around an axis. + The [member polygon] shape is extruded by rotating it around the Y axis. </constant> <constant name="MODE_PATH" value="2" enum="Mode"> - Shape3D is extruded along a path set by a [Shape3D] set in [member path_node]. + The [member polygon] shape is extruded along the [Path3D] specified in [member path_node]. </constant> <constant name="PATH_ROTATION_POLYGON" value="0" enum="PathRotation"> - Slice is not rotated. + The [member polygon] shape is not rotated. + [b]Note:[/b] Requires the path Z coordinates to continually decrease to ensure viable shapes. </constant> <constant name="PATH_ROTATION_PATH" value="1" enum="PathRotation"> - Slice is rotated around the up vector of the path. + The [member polygon] shape is rotated along the path, but it is not rotated around the path axis. + [b]Note:[/b] Requires the path Z coordinates to continually decrease to ensure viable shapes. </constant> <constant name="PATH_ROTATION_PATH_FOLLOW" value="2" enum="PathRotation"> - Slice is rotate to match the path exactly. + The [member polygon] shape follows the path and its rotations around the path axis. </constant> </constants> </class> diff --git a/modules/csg/doc_classes/CSGShape3D.xml b/modules/csg/doc_classes/CSGShape3D.xml index f42ce8c379..446269f3f0 100644 --- a/modules/csg/doc_classes/CSGShape3D.xml +++ b/modules/csg/doc_classes/CSGShape3D.xml @@ -9,18 +9,18 @@ <tutorials> </tutorials> <methods> - <method name="get_collision_layer_bit" qualifiers="const"> + <method name="get_collision_layer_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns an individual bit on the collision mask. + Returns whether or not the specified layer of the [member collision_layer] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="get_collision_mask_bit" qualifiers="const"> + <method name="get_collision_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns an individual bit on the collision mask. + Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="get_meshes" qualifiers="const"> @@ -35,20 +35,20 @@ Returns [code]true[/code] if this is a root shape and is thus the object that is rendered. </description> </method> - <method name="set_collision_layer_bit"> + <method name="set_collision_layer_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - Sets individual bits on the layer mask. Use this if you only need to change one layer's value. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_layer], given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="set_collision_mask_bit"> + <method name="set_collision_mask_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - Sets individual bits on the collision mask. Use this if you only need to change one layer's value. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32. </description> </method> </methods> diff --git a/modules/denoise/lightmap_denoiser.cpp b/modules/denoise/lightmap_denoiser.cpp index 003bc832b0..71dcc1d75f 100644 --- a/modules/denoise/lightmap_denoiser.cpp +++ b/modules/denoise/lightmap_denoiser.cpp @@ -31,6 +31,8 @@ #include "lightmap_denoiser.h" #include "denoise_wrapper.h" +#include "core/io/image.h" + LightmapDenoiser *LightmapDenoiserOIDN::create_oidn_denoiser() { return memnew(LightmapDenoiserOIDN); } diff --git a/modules/enet/doc_classes/ENetConnection.xml b/modules/enet/doc_classes/ENetConnection.xml index c2a85ffdf8..00469ab44c 100644 --- a/modules/enet/doc_classes/ENetConnection.xml +++ b/modules/enet/doc_classes/ENetConnection.xml @@ -40,6 +40,7 @@ <description> Sets the compression method used for network packets. These have different tradeoffs of compression speed versus bandwidth, you may need to test which one works best for your use case if you use compression at all. [b]Note:[/b] Most games' network design involve sending many small packets frequently (smaller than 4 KB each). If in doubt, it is recommended to keep the default compression algorithm as it works best on these small packets. + [b]Note:[/b] The compression mode must be set to the same value on both the server and all its clients. Clients will fail to connect if the compression mode set on the client differs from the one set on the server. </description> </method> <method name="connect_to_host"> @@ -155,7 +156,7 @@ ENet's built-in range encoding. Works well on small packets, but is not the most efficient algorithm on packets larger than 4 KB. </constant> <constant name="COMPRESS_FASTLZ" value="2" enum="CompressionMode"> - [url=http://fastlz.org/]FastLZ[/url] compression. This option uses less CPU resources compared to [constant COMPRESS_ZLIB], at the expense of using more bandwidth. + [url=https://fastlz.org/]FastLZ[/url] compression. This option uses less CPU resources compared to [constant COMPRESS_ZLIB], at the expense of using more bandwidth. </constant> <constant name="COMPRESS_ZLIB" value="3" enum="CompressionMode"> [url=https://www.zlib.net/]Zlib[/url] compression. This option uses less bandwidth compared to [constant COMPRESS_FASTLZ], at the expense of using more CPU resources. diff --git a/modules/fbx/data/fbx_material.cpp b/modules/fbx/data/fbx_material.cpp index fb6c67f7b9..86baec4244 100644 --- a/modules/fbx/data/fbx_material.cpp +++ b/modules/fbx/data/fbx_material.cpp @@ -31,7 +31,7 @@ #include "fbx_material.h" // FIXME: Shouldn't depend on core_bind.h! Use DirAccessRef like the rest of -// the engine instead of _Directory. +// the engine instead of core_bind::Directory. #include "core/core_bind.h" #include "scene/resources/material.h" #include "scene/resources/texture.h" @@ -55,7 +55,7 @@ void FBXMaterial::add_search_string(String p_filename, String p_current_director } String find_file(const String &p_base, const String &p_file_to_find) { - _Directory dir; + core_bind::Directory dir; dir.open(p_base); dir.list_dir_begin(); @@ -84,7 +84,7 @@ String find_file(const String &p_base, const String &p_file_to_find) { // fbx will not give us good path information and let's not regex them to fix them // no relative paths are in fbx generally they have a rel field but it's populated incorrectly by the SDK. String FBXMaterial::find_texture_path_by_filename(const String p_filename, const String p_current_directory) { - _Directory dir; + core_bind::Directory dir; Vector<String> paths; add_search_string(p_filename, p_current_directory, "", paths); add_search_string(p_filename, p_current_directory, "texture", paths); diff --git a/modules/fbx/data/fbx_mesh_data.cpp b/modules/fbx/data/fbx_mesh_data.cpp index 8b4b1e08b3..dcea476275 100644 --- a/modules/fbx/data/fbx_mesh_data.cpp +++ b/modules/fbx/data/fbx_mesh_data.cpp @@ -433,7 +433,7 @@ void FBXMeshData::sanitize_vertex_weights(const ImportState &state) { { // Sort - real_t *weights_ptr = vm->weights.ptrw(); + float *weights_ptr = vm->weights.ptrw(); int *bones_ptr = vm->bones.ptrw(); for (int i = 0; i < vm->weights.size(); i += 1) { for (int x = i + 1; x < vm->weights.size(); x += 1) { @@ -449,7 +449,7 @@ void FBXMeshData::sanitize_vertex_weights(const ImportState &state) { // Resize vm->weights.resize(max_vertex_influence_count); vm->bones.resize(max_vertex_influence_count); - real_t *weights_ptr = vm->weights.ptrw(); + float *weights_ptr = vm->weights.ptrw(); int *bones_ptr = vm->bones.ptrw(); for (int i = initial_size; i < max_vertex_influence_count; i += 1) { weights_ptr[i] = 0.0; diff --git a/modules/fbx/data/fbx_mesh_data.h b/modules/fbx/data/fbx_mesh_data.h index 7486c5c59c..24db4a5469 100644 --- a/modules/fbx/data/fbx_mesh_data.h +++ b/modules/fbx/data/fbx_mesh_data.h @@ -64,7 +64,7 @@ struct SurfaceData { }; struct VertexWeightMapping { - Vector<real_t> weights; + Vector<float> weights; Vector<int> bones; // This extra vector is used because the bone id is computed in a second step. // TODO Get rid of this extra step is a good idea. diff --git a/modules/fbx/fbx_parser/FBXParser.cpp b/modules/fbx/fbx_parser/FBXParser.cpp index a92b23f4ee..d9ef025a16 100644 --- a/modules/fbx/fbx_parser/FBXParser.cpp +++ b/modules/fbx/fbx_parser/FBXParser.cpp @@ -575,7 +575,7 @@ void ReadBinaryDataArray(char type, uint32_t count, const char *&data, const cha std::copy(data, end, buff.begin()); } else if (encmode == 1) { // zlib/deflate, next comes ZIP head (0x78 0x01) - // see http://www.ietf.org/rfc/rfc1950.txt + // see https://www.ietf.org/rfc/rfc1950.txt z_stream zstream; zstream.opaque = Z_NULL; diff --git a/modules/gdnative/config.py b/modules/gdnative/config.py index fd860e9763..fa985501b5 100644 --- a/modules/gdnative/config.py +++ b/modules/gdnative/config.py @@ -8,7 +8,6 @@ def configure(env): def get_doc_classes(): return [ - "XRInterfaceGDNative", "GDNative", "GDNativeLibrary", "MultiplayerPeerGDNative", diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml index f84d4e60f3..94eae3cd06 100644 --- a/modules/gdnative/doc_classes/GDNativeLibrary.xml +++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml @@ -4,7 +4,7 @@ An external library containing functions or script classes to use in Godot. </brief_description> <description> - A GDNative library can implement [NativeScript]s, global functions to call with the [GDNative] class, or low-level engine extensions through interfaces such as [XRInterfaceGDNative]. The library must be compiled for each platform and architecture that the project will run on. + A GDNative library can implement [NativeScript]s, global functions to call with the [GDNative] class, or low-level engine extensions through interfaces such as XRInterfaceGDNative. The library must be compiled for each platform and architecture that the project will run on. </description> <tutorials> <link title="GDNative C example">https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-c-example.html</link> diff --git a/modules/gdnative/doc_classes/XRInterfaceGDNative.xml b/modules/gdnative/doc_classes/XRInterfaceGDNative.xml deleted file mode 100644 index 13de815793..0000000000 --- a/modules/gdnative/doc_classes/XRInterfaceGDNative.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<class name="XRInterfaceGDNative" inherits="XRInterface" version="4.0"> - <brief_description> - GDNative wrapper for an XR interface. - </brief_description> - <description> - This is a wrapper class for GDNative implementations of the XR interface. To use a GDNative XR interface, simply instantiate this object and set your GDNative library containing the XR interface implementation. - </description> - <tutorials> - </tutorials> - <methods> - </methods> - <constants> - </constants> -</class> diff --git a/modules/gdnative/gdnative/packed_arrays.cpp b/modules/gdnative/gdnative/packed_arrays.cpp index 396109e576..f03c94aeb8 100644 --- a/modules/gdnative/gdnative/packed_arrays.cpp +++ b/modules/gdnative/gdnative/packed_arrays.cpp @@ -51,8 +51,6 @@ static_assert(sizeof(godot_packed_color_array) == sizeof(PackedColorArray), "Pac extern "C" { #endif -#define memnew_placement_custom(m_placement, m_class, m_constr) _post_initialize(new (m_placement, sizeof(m_class), "") m_constr) - // byte void GDAPI godot_packed_byte_array_new(godot_packed_byte_array *p_self) { diff --git a/modules/gdnative/gdnative/variant.cpp b/modules/gdnative/gdnative/variant.cpp index 1d75ea206c..ec9aaa0a55 100644 --- a/modules/gdnative/gdnative/variant.cpp +++ b/modules/gdnative/gdnative/variant.cpp @@ -48,8 +48,6 @@ static_assert(sizeof(godot_variant) == sizeof(Variant), "Variant size mismatch") #pragma GCC optimize("-O0") #endif -#define memnew_placement_custom(m_placement, m_class, m_constr) _post_initialize(new (m_placement, sizeof(m_class), "") m_constr) - #if defined(__arm__) && defined(__GNUC__) && !defined(__clang__) && \ (__GNUC__ == 6 || (__GNUC__ == 7 && __GNUC_MINOR__ < 4) || (__GNUC__ == 8 && __GNUC_MINOR__ < 1)) #pragma GCC pop_options @@ -70,131 +68,131 @@ void GDAPI godot_variant_new_nil(godot_variant *r_dest) { void GDAPI godot_variant_new_bool(godot_variant *r_dest, const godot_bool p_b) { Variant *dest = (Variant *)r_dest; - memnew_placement_custom(dest, Variant, Variant(p_b)); + memnew_placement(dest, Variant(p_b)); } void GDAPI godot_variant_new_int(godot_variant *r_dest, const godot_int p_i) { Variant *dest = (Variant *)r_dest; - memnew_placement_custom(dest, Variant, Variant(p_i)); + memnew_placement(dest, Variant(p_i)); } void GDAPI godot_variant_new_float(godot_variant *r_dest, const godot_float p_r) { Variant *dest = (Variant *)r_dest; - memnew_placement_custom(dest, Variant, Variant(p_r)); + memnew_placement(dest, Variant(p_r)); } void GDAPI godot_variant_new_string(godot_variant *r_dest, const godot_string *p_s) { Variant *dest = (Variant *)r_dest; const String *s = (const String *)p_s; - memnew_placement_custom(dest, Variant, Variant(*s)); + memnew_placement(dest, Variant(*s)); } void GDAPI godot_variant_new_string_name(godot_variant *r_dest, const godot_string_name *p_s) { Variant *dest = (Variant *)r_dest; const StringName *s = (const StringName *)p_s; - memnew_placement_custom(dest, Variant, Variant(*s)); + memnew_placement(dest, Variant(*s)); } void GDAPI godot_variant_new_vector2(godot_variant *r_dest, const godot_vector2 *p_v2) { Variant *dest = (Variant *)r_dest; const Vector2 *v2 = (const Vector2 *)p_v2; - memnew_placement_custom(dest, Variant, Variant(*v2)); + memnew_placement(dest, Variant(*v2)); } void GDAPI godot_variant_new_vector2i(godot_variant *r_dest, const godot_vector2i *p_v2) { Variant *dest = (Variant *)r_dest; const Vector2i *v2 = (const Vector2i *)p_v2; - memnew_placement_custom(dest, Variant, Variant(*v2)); + memnew_placement(dest, Variant(*v2)); } void GDAPI godot_variant_new_rect2(godot_variant *r_dest, const godot_rect2 *p_rect2) { Variant *dest = (Variant *)r_dest; const Rect2 *rect2 = (const Rect2 *)p_rect2; - memnew_placement_custom(dest, Variant, Variant(*rect2)); + memnew_placement(dest, Variant(*rect2)); } void GDAPI godot_variant_new_rect2i(godot_variant *r_dest, const godot_rect2i *p_rect2) { Variant *dest = (Variant *)r_dest; const Rect2i *rect2 = (const Rect2i *)p_rect2; - memnew_placement_custom(dest, Variant, Variant(*rect2)); + memnew_placement(dest, Variant(*rect2)); } void GDAPI godot_variant_new_vector3(godot_variant *r_dest, const godot_vector3 *p_v3) { Variant *dest = (Variant *)r_dest; const Vector3 *v3 = (const Vector3 *)p_v3; - memnew_placement_custom(dest, Variant, Variant(*v3)); + memnew_placement(dest, Variant(*v3)); } void GDAPI godot_variant_new_vector3i(godot_variant *r_dest, const godot_vector3i *p_v3) { Variant *dest = (Variant *)r_dest; const Vector3i *v3 = (const Vector3i *)p_v3; - memnew_placement_custom(dest, Variant, Variant(*v3)); + memnew_placement(dest, Variant(*v3)); } void GDAPI godot_variant_new_transform2d(godot_variant *r_dest, const godot_transform2d *p_t2d) { Variant *dest = (Variant *)r_dest; const Transform2D *t2d = (const Transform2D *)p_t2d; - memnew_placement_custom(dest, Variant, Variant(*t2d)); + memnew_placement(dest, Variant(*t2d)); } void GDAPI godot_variant_new_plane(godot_variant *r_dest, const godot_plane *p_plane) { Variant *dest = (Variant *)r_dest; const Plane *plane = (const Plane *)p_plane; - memnew_placement_custom(dest, Variant, Variant(*plane)); + memnew_placement(dest, Variant(*plane)); } void GDAPI godot_variant_new_quaternion(godot_variant *r_dest, const godot_quaternion *p_quaternion) { Variant *dest = (Variant *)r_dest; const Quaternion *quaternion = (const Quaternion *)p_quaternion; - memnew_placement_custom(dest, Variant, Variant(*quaternion)); + memnew_placement(dest, Variant(*quaternion)); } void GDAPI godot_variant_new_aabb(godot_variant *r_dest, const godot_aabb *p_aabb) { Variant *dest = (Variant *)r_dest; const AABB *aabb = (const AABB *)p_aabb; - memnew_placement_custom(dest, Variant, Variant(*aabb)); + memnew_placement(dest, Variant(*aabb)); } void GDAPI godot_variant_new_basis(godot_variant *r_dest, const godot_basis *p_basis) { Variant *dest = (Variant *)r_dest; const Basis *basis = (const Basis *)p_basis; - memnew_placement_custom(dest, Variant, Variant(*basis)); + memnew_placement(dest, Variant(*basis)); } void GDAPI godot_variant_new_transform3d(godot_variant *r_dest, const godot_transform3d *p_trans) { Variant *dest = (Variant *)r_dest; const Transform3D *trans = (const Transform3D *)p_trans; - memnew_placement_custom(dest, Variant, Variant(*trans)); + memnew_placement(dest, Variant(*trans)); } void GDAPI godot_variant_new_color(godot_variant *r_dest, const godot_color *p_color) { Variant *dest = (Variant *)r_dest; const Color *color = (const Color *)p_color; - memnew_placement_custom(dest, Variant, Variant(*color)); + memnew_placement(dest, Variant(*color)); } void GDAPI godot_variant_new_node_path(godot_variant *r_dest, const godot_node_path *p_np) { Variant *dest = (Variant *)r_dest; const NodePath *np = (const NodePath *)p_np; - memnew_placement_custom(dest, Variant, Variant(*np)); + memnew_placement(dest, Variant(*np)); } void GDAPI godot_variant_new_rid(godot_variant *r_dest, const godot_rid *p_rid) { Variant *dest = (Variant *)r_dest; const RID *rid = (const RID *)p_rid; - memnew_placement_custom(dest, Variant, Variant(*rid)); + memnew_placement(dest, Variant(*rid)); } void GDAPI godot_variant_new_callable(godot_variant *r_dest, const godot_callable *p_cb) { Variant *dest = (Variant *)r_dest; const Callable *cb = (const Callable *)p_cb; - memnew_placement_custom(dest, Variant, Variant(*cb)); + memnew_placement(dest, Variant(*cb)); } void GDAPI godot_variant_new_signal(godot_variant *r_dest, const godot_signal *p_signal) { Variant *dest = (Variant *)r_dest; const Signal *signal = (const Signal *)p_signal; - memnew_placement_custom(dest, Variant, Variant(*signal)); + memnew_placement(dest, Variant(*signal)); } void GDAPI godot_variant_new_object(godot_variant *r_dest, const godot_object *p_obj) { @@ -206,81 +204,81 @@ void GDAPI godot_variant_new_object(godot_variant *r_dest, const godot_object *p ref = REF(ref_counted); } if (!ref.is_null()) { - memnew_placement_custom(dest, Variant, Variant(ref)); + memnew_placement(dest, Variant(ref)); } else { #if defined(DEBUG_METHODS_ENABLED) if (ref_counted) { ERR_PRINT("RefCounted object has 0 refcount in godot_variant_new_object - you lost it somewhere."); } #endif - memnew_placement_custom(dest, Variant, Variant(obj)); + memnew_placement(dest, Variant(obj)); } } void GDAPI godot_variant_new_dictionary(godot_variant *r_dest, const godot_dictionary *p_dict) { Variant *dest = (Variant *)r_dest; const Dictionary *dict = (const Dictionary *)p_dict; - memnew_placement_custom(dest, Variant, Variant(*dict)); + memnew_placement(dest, Variant(*dict)); } void GDAPI godot_variant_new_array(godot_variant *r_dest, const godot_array *p_arr) { Variant *dest = (Variant *)r_dest; const Array *arr = (const Array *)p_arr; - memnew_placement_custom(dest, Variant, Variant(*arr)); + memnew_placement(dest, Variant(*arr)); } void GDAPI godot_variant_new_packed_byte_array(godot_variant *r_dest, const godot_packed_byte_array *p_pba) { Variant *dest = (Variant *)r_dest; const PackedByteArray *pba = (const PackedByteArray *)p_pba; - memnew_placement_custom(dest, Variant, Variant(*pba)); + memnew_placement(dest, Variant(*pba)); } void GDAPI godot_variant_new_packed_int32_array(godot_variant *r_dest, const godot_packed_int32_array *p_pia) { Variant *dest = (Variant *)r_dest; const PackedInt32Array *pia = (const PackedInt32Array *)p_pia; - memnew_placement_custom(dest, Variant, Variant(*pia)); + memnew_placement(dest, Variant(*pia)); } void GDAPI godot_variant_new_packed_int64_array(godot_variant *r_dest, const godot_packed_int64_array *p_pia) { Variant *dest = (Variant *)r_dest; const PackedInt64Array *pia = (const PackedInt64Array *)p_pia; - memnew_placement_custom(dest, Variant, Variant(*pia)); + memnew_placement(dest, Variant(*pia)); } void GDAPI godot_variant_new_packed_float32_array(godot_variant *r_dest, const godot_packed_float32_array *p_pra) { Variant *dest = (Variant *)r_dest; const PackedFloat32Array *pra = (const PackedFloat32Array *)p_pra; - memnew_placement_custom(dest, Variant, Variant(*pra)); + memnew_placement(dest, Variant(*pra)); } void GDAPI godot_variant_new_packed_float64_array(godot_variant *r_dest, const godot_packed_float64_array *p_pra) { Variant *dest = (Variant *)r_dest; const PackedFloat64Array *pra = (const PackedFloat64Array *)p_pra; - memnew_placement_custom(dest, Variant, Variant(*pra)); + memnew_placement(dest, Variant(*pra)); } void GDAPI godot_variant_new_packed_string_array(godot_variant *r_dest, const godot_packed_string_array *p_psa) { Variant *dest = (Variant *)r_dest; const PackedStringArray *psa = (const PackedStringArray *)p_psa; - memnew_placement_custom(dest, Variant, Variant(*psa)); + memnew_placement(dest, Variant(*psa)); } void GDAPI godot_variant_new_packed_vector2_array(godot_variant *r_dest, const godot_packed_vector2_array *p_pv2a) { Variant *dest = (Variant *)r_dest; const PackedVector2Array *pv2a = (const PackedVector2Array *)p_pv2a; - memnew_placement_custom(dest, Variant, Variant(*pv2a)); + memnew_placement(dest, Variant(*pv2a)); } void GDAPI godot_variant_new_packed_vector3_array(godot_variant *r_dest, const godot_packed_vector3_array *p_pv3a) { Variant *dest = (Variant *)r_dest; const PackedVector3Array *pv3a = (const PackedVector3Array *)p_pv3a; - memnew_placement_custom(dest, Variant, Variant(*pv3a)); + memnew_placement(dest, Variant(*pv3a)); } void GDAPI godot_variant_new_packed_color_array(godot_variant *r_dest, const godot_packed_color_array *p_pca) { Variant *dest = (Variant *)r_dest; const PackedColorArray *pca = (const PackedColorArray *)p_pca; - memnew_placement_custom(dest, Variant, Variant(*pca)); + memnew_placement(dest, Variant(*pca)); } godot_bool GDAPI godot_variant_as_bool(const godot_variant *p_self) { @@ -568,7 +566,7 @@ void GDAPI godot_variant_call(godot_variant *p_self, const godot_string_name *p_ Variant ret; Callable::CallError error; self->call(*method, args, p_argcount, ret, error); - memnew_placement_custom(r_return, Variant, Variant(ret)); + memnew_placement(r_return, Variant(ret)); if (r_error) { r_error->error = (godot_variant_call_error_error)error.error; @@ -584,7 +582,7 @@ void GDAPI godot_variant_call_with_cstring(godot_variant *p_self, const char *p_ Variant ret; Callable::CallError error; self->call(method, args, p_argcount, ret, error); - memnew_placement_custom(r_return, Variant, Variant(ret)); + memnew_placement(r_return, Variant(ret)); if (r_error) { r_error->error = (godot_variant_call_error_error)error.error; @@ -600,7 +598,7 @@ void GDAPI godot_variant_call_static(godot_variant_type p_type, const godot_stri Variant ret; Callable::CallError error; Variant::call_static(type, *method, args, p_argcount, ret, error); - memnew_placement_custom(r_return, Variant, Variant(ret)); + memnew_placement(r_return, Variant(ret)); if (r_error) { r_error->error = (godot_variant_call_error_error)error.error; @@ -616,7 +614,7 @@ void GDAPI godot_variant_call_static_with_cstring(godot_variant_type p_type, con Variant ret; Callable::CallError error; Variant::call_static(type, method, args, p_argcount, ret, error); - memnew_placement_custom(r_return, Variant, Variant(ret)); + memnew_placement(r_return, Variant(ret)); if (r_error) { r_error->error = (godot_variant_call_error_error)error.error; @@ -679,7 +677,7 @@ godot_variant GDAPI godot_variant_get(const godot_variant *p_self, const godot_v ret = self->get(*key, r_valid); godot_variant result; - memnew_placement_custom(&result, Variant, Variant(ret)); + memnew_placement(&result, Variant(ret)); return result; } @@ -690,7 +688,7 @@ godot_variant GDAPI godot_variant_get_named(const godot_variant *p_self, const g ret = self->get_named(*key, *r_valid); godot_variant result; - memnew_placement_custom(&result, Variant, Variant(ret)); + memnew_placement(&result, Variant(ret)); return result; } @@ -701,7 +699,7 @@ godot_variant GDAPI godot_variant_get_named_with_cstring(const godot_variant *p_ ret = self->get_named(*key, *r_valid); godot_variant result; - memnew_placement_custom(&result, Variant, Variant(ret)); + memnew_placement(&result, Variant(ret)); return result; } @@ -712,7 +710,7 @@ godot_variant GDAPI godot_variant_get_keyed(const godot_variant *p_self, const g ret = self->get_keyed(*key, *r_valid); godot_variant result; - memnew_placement_custom(&result, Variant, Variant(ret)); + memnew_placement(&result, Variant(ret)); return result; } @@ -722,7 +720,7 @@ godot_variant GDAPI godot_variant_get_indexed(const godot_variant *p_self, godot ret = self->get_indexed(p_index, *r_valid, *r_oob); godot_variant result; - memnew_placement_custom(&result, Variant, Variant(ret)); + memnew_placement(&result, Variant(ret)); return result; } @@ -747,7 +745,7 @@ godot_variant GDAPI godot_variant_iter_get(const godot_variant *p_self, godot_va Variant result = self->iter_next(*iter, *r_valid); godot_variant ret; - memnew_placement_custom(&ret, Variant, Variant(result)); + memnew_placement(&ret, Variant(result)); return ret; } @@ -781,7 +779,7 @@ godot_variant GDAPI godot_variant_duplicate(const godot_variant *p_self, godot_b const Variant *self = (const Variant *)p_self; Variant result = self->duplicate(p_deep); godot_variant ret; - memnew_placement_custom(&ret, Variant, Variant(result)); + memnew_placement(&ret, Variant(result)); return ret; } @@ -789,7 +787,7 @@ godot_string GDAPI godot_variant_stringify(const godot_variant *p_self) { const Variant *self = (const Variant *)p_self; String result = *self; godot_string ret; - memnew_placement_custom(&ret, String, String(result)); + memnew_placement(&ret, String(result)); return ret; } @@ -811,7 +809,7 @@ godot_variant_type GDAPI godot_variant_get_operator_return_type(godot_variant_op godot_string GDAPI godot_variant_get_operator_name(godot_variant_operator p_operator) { String op_name = Variant::get_operator_name((Variant::Operator)p_operator); godot_string ret; - memnew_placement_custom(&ret, String, String(op_name)); + memnew_placement(&ret, String(op_name)); return ret; } @@ -916,7 +914,7 @@ void GDAPI godot_variant_get_builtin_method_list(godot_variant_type p_type, godo Variant::get_builtin_method_list((Variant::Type)p_type, &list); int i = 0; for (const StringName &E : list) { - memnew_placement_custom(&r_list[i], StringName, StringName(E)); + memnew_placement(&r_list[i], StringName(E)); } } @@ -971,7 +969,7 @@ void GDAPI godot_variant_get_member_list(godot_variant_type p_type, godot_string Variant::get_member_list((Variant::Type)p_type, &members); int i = 0; for (const StringName &E : members) { - memnew_placement_custom(&r_list[i++], StringName, StringName(E)); + memnew_placement(&r_list[i++], StringName(E)); } } @@ -1076,7 +1074,7 @@ void GDAPI godot_variant_get_constants_list(godot_variant_type p_type, godot_str int i = 0; Variant::get_constants_for_type((Variant::Type)p_type, &constants); for (const StringName &E : constants) { - memnew_placement_custom(&r_list[i++], StringName, StringName(E)); + memnew_placement(&r_list[i++], StringName(E)); } } @@ -1091,14 +1089,14 @@ bool GDAPI godot_variant_has_constant_with_cstring(godot_variant_type p_type, co godot_variant GDAPI godot_variant_get_constant_value(godot_variant_type p_type, const godot_string_name *p_constant) { Variant constant = Variant::get_constant_value((Variant::Type)p_type, *((const StringName *)p_constant)); godot_variant ret; - memnew_placement_custom(&ret, Variant, Variant(constant)); + memnew_placement(&ret, Variant(constant)); return ret; } godot_variant GDAPI godot_variant_get_constant_value_with_cstring(godot_variant_type p_type, const char *p_constant) { Variant constant = Variant::get_constant_value((Variant::Type)p_type, StringName(p_constant)); godot_variant ret; - memnew_placement_custom(&ret, Variant, Variant(constant)); + memnew_placement(&ret, Variant(constant)); return ret; } @@ -1183,14 +1181,14 @@ godot_variant_type GDAPI godot_variant_get_utility_function_argument_type_with_c godot_string GDAPI godot_variant_get_utility_function_argument_name(const godot_string_name *p_function, int p_argument) { String argument_name = Variant::get_utility_function_argument_name(*((const StringName *)p_function), p_argument); godot_string ret; - memnew_placement_custom(&ret, String, String(argument_name)); + memnew_placement(&ret, String(argument_name)); return ret; } godot_string GDAPI godot_variant_get_utility_function_argument_name_with_cstring(const char *p_function, int p_argument) { String argument_name = Variant::get_utility_function_argument_name(StringName(p_function), p_argument); godot_string ret; - memnew_placement_custom(&ret, String, String(argument_name)); + memnew_placement(&ret, String(argument_name)); return ret; } @@ -1228,7 +1226,7 @@ void GDAPI godot_variant_get_utility_function_list(godot_string_name *r_function Variant::get_utility_function_list(&functions); for (const StringName &E : functions) { - memnew_placement_custom(func++, StringName, StringName(E)); + memnew_placement(func++, StringName(E)); } } @@ -1258,7 +1256,7 @@ bool GDAPI godot_variant_has_key(const godot_variant *p_self, const godot_varian godot_string GDAPI godot_variant_get_type_name(godot_variant_type p_type) { String name = Variant::get_type_name((Variant::Type)p_type); godot_string ret; - memnew_placement_custom(&ret, String, String(name)); + memnew_placement(&ret, String(name)); return ret; } diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json index 8c65447e5d..66d2dc267d 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -5048,169 +5048,6 @@ ] }, { - "name": "xr", - "type": "XR", - "version": { - "major": 1, - "minor": 1 - }, - "next": null, - "api": [ - { - "name": "godot_xr_register_interface", - "return_type": "void", - "arguments": [ - [ - "const godot_xr_interface_gdnative *", - "p_interface" - ] - ] - }, - { - "name": "godot_xr_get_worldscale", - "return_type": "godot_real_t", - "arguments": [] - }, - { - "name": "godot_xr_get_reference_frame", - "return_type": "godot_transform3d", - "arguments": [] - }, - { - "name": "godot_xr_blit", - "return_type": "void", - "arguments": [ - [ - "godot_int", - "p_eye" - ], - [ - "godot_rid *", - "p_render_target" - ], - [ - "godot_rect2 *", - "p_screen_rect" - ] - ] - }, - { - "name": "godot_xr_get_texid", - "return_type": "godot_int", - "arguments": [ - [ - "godot_rid *", - "p_render_target" - ] - ] - }, - { - "name": "godot_xr_add_controller", - "return_type": "godot_int", - "arguments": [ - [ - "char *", - "p_device_name" - ], - [ - "godot_int", - "p_hand" - ], - [ - "godot_bool", - "p_tracks_orientation" - ], - [ - "godot_bool", - "p_tracks_position" - ] - ] - }, - { - "name": "godot_xr_remove_controller", - "return_type": "void", - "arguments": [ - [ - "godot_int", - "p_controller_id" - ] - ] - }, - { - "name": "godot_xr_set_controller_transform", - "return_type": "void", - "arguments": [ - [ - "godot_int", - "p_controller_id" - ], - [ - "godot_transform3d *", - "p_transform" - ], - [ - "godot_bool", - "p_tracks_orientation" - ], - [ - "godot_bool", - "p_tracks_position" - ] - ] - }, - { - "name": "godot_xr_set_controller_button", - "return_type": "void", - "arguments": [ - [ - "godot_int", - "p_controller_id" - ], - [ - "godot_int", - "p_button" - ], - [ - "godot_bool", - "p_is_pressed" - ] - ] - }, - { - "name": "godot_xr_set_controller_axis", - "return_type": "void", - "arguments": [ - [ - "godot_int", - "p_controller_id" - ], - [ - "godot_int", - "p_exis" - ], - [ - "godot_real_t", - "p_value" - ], - [ - "godot_bool", - "p_can_be_negative" - ] - ] - }, - { - "name": "godot_xr_get_controller_rumble", - "return_type": "godot_real_t", - "arguments": [ - [ - "godot_int", - "p_controller_id" - ] - ] - } - ] - }, - { "name": "videodecoder", "type": "VIDEODECODER", "version": { @@ -5517,7 +5354,7 @@ }, { "name": "godot_glyph_get_advance", - "return_type": "godot_float", + "return_type": "godot_real_t", "arguments": [ [ "const godot_glyph *", @@ -5534,7 +5371,7 @@ "p_self" ], [ - "godot_float", + "godot_real_t", "p_advance" ] ] diff --git a/modules/gdnative/gdnative_builders.py b/modules/gdnative/gdnative_builders.py index d03298d7a9..181fd71b82 100644 --- a/modules/gdnative/gdnative_builders.py +++ b/modules/gdnative/gdnative_builders.py @@ -19,7 +19,6 @@ def _build_gdnative_api_struct_header(api): "", "#include <gdnative/gdnative.h>", "#include <android/godot_android.h>", - "#include <xr/godot_xr.h>", "#include <nativescript/godot_nativescript.h>", "#include <net/godot_net.h>", "#include <pluginscript/godot_pluginscript.h>", diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h index c97f5f0389..09eac2492f 100644 --- a/modules/gdnative/include/nativescript/godot_nativescript.h +++ b/modules/gdnative/include/nativescript/godot_nativescript.h @@ -39,12 +39,8 @@ extern "C" { typedef enum { GODOT_METHOD_RPC_MODE_DISABLED, - GODOT_METHOD_RPC_MODE_REMOTE, - GODOT_METHOD_RPC_MODE_MASTER, - GODOT_METHOD_RPC_MODE_PUPPET, - GODOT_METHOD_RPC_MODE_REMOTESYNC, - GODOT_METHOD_RPC_MODE_MASTERSYNC, - GODOT_METHOD_RPC_MODE_PUPPETSYNC, + GODOT_METHOD_RPC_MODE_ANY, + GODOT_METHOD_RPC_MODE_AUTHORITY, } godot_nativescript_method_rpc_mode; typedef enum { diff --git a/modules/gdnative/include/text/godot_text.h b/modules/gdnative/include/text/godot_text.h index 6428f2f149..940cfd11f8 100644 --- a/modules/gdnative/include/text/godot_text.h +++ b/modules/gdnative/include/text/godot_text.h @@ -60,68 +60,109 @@ typedef struct { 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); + + void (*free)(void *, godot_rid *); + bool (*has)(void *, godot_rid *); + 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); - godot_rid (*create_font_bitmap)(void *, float, float, int); - void (*font_bitmap_add_texture)(void *, godot_rid *, const godot_object *); - void (*font_bitmap_add_char)(void *, godot_rid *, char32_t, int, const godot_rect2 *, const godot_vector2 *, float); - void (*font_bitmap_add_kerning_pair)(void *, godot_rid *, char32_t, char32_t, 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); - int (*font_get_spacing_space)(void *, godot_rid *); - void (*font_set_spacing_space)(void *, godot_rid *, int); - int (*font_get_spacing_glyph)(void *, godot_rid *); - void (*font_set_spacing_glyph)(void *, godot_rid *, int); + int32_t (*name_to_tag)(const void *, const godot_string *); + godot_string (*tag_to_name)(const void *, int32_t); + + godot_rid (*create_font)(void *); + void (*font_set_data)(void *, godot_rid *, const godot_packed_byte_array *); + void (*font_set_data_ptr)(void *, godot_rid *, const uint8_t *, size_t); 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 *); + bool (*font_is_antialiased)(const void *, godot_rid *); + void (*font_set_multichannel_signed_distance_field)(void *, godot_rid *, bool); + bool (*font_is_multichannel_signed_distance_field)(const void *, godot_rid *); + void (*font_set_msdf_pixel_range)(void *, godot_rid *, godot_int); + godot_int (*font_get_msdf_pixel_range)(const void *, godot_rid *); + void (*font_set_msdf_size)(void *, godot_rid *, godot_int); + godot_int (*font_get_msdf_size)(const void *, godot_rid *); + void (*font_set_fixed_size)(void *, godot_rid *, godot_int); + godot_int (*font_get_fixed_size)(const 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 *); + bool (*font_is_force_autohinter)(const void *, godot_rid *); + void (*font_set_hinting)(void *, godot_rid *, godot_int); + godot_int (*font_get_hinting)(const void *, godot_rid *); + void (*font_set_variation_coordinates)(void *, godot_rid *, const godot_dictionary *); + godot_dictionary (*font_get_variation_coordinates)(const void *, godot_rid *); + void (*font_set_oversampling)(void *, godot_rid *, godot_real_t); + godot_real_t (*font_get_oversampling)(const void *, godot_rid *); + godot_array (*font_get_size_cache_list)(const void *, godot_rid *); + void (*font_clear_size_cache)(void *, godot_rid *); + void (*font_remove_size_cache)(void *, godot_rid *, const godot_vector2i *); + void (*font_set_ascent)(void *, godot_rid *, godot_int, godot_real_t); + godot_real_t (*font_get_ascent)(const void *, godot_rid *, godot_int); + void (*font_set_descent)(void *, godot_rid *, godot_int, godot_real_t); + godot_real_t (*font_get_descent)(const void *, godot_rid *, godot_int); + void (*font_set_underline_position)(void *, godot_rid *, godot_int, godot_real_t); + godot_real_t (*font_get_underline_position)(const void *, godot_rid *, godot_int); + void (*font_set_underline_thickness)(void *, godot_rid *, godot_int, godot_real_t); + godot_real_t (*font_get_underline_thickness)(const void *, godot_rid *, godot_int); + void (*font_set_scale)(void *, godot_rid *, godot_int, godot_real_t); + godot_real_t (*font_get_scale)(const void *, godot_rid *, godot_int); + void (*font_set_spacing)(void *, godot_rid *, godot_int, godot_int, godot_int); + godot_int (*font_get_spacing)(const void *, godot_rid *, godot_int, godot_int); + godot_int (*font_get_texture_count)(const void *, godot_rid *, const godot_vector2i *); + void (*font_clear_textures)(void *, godot_rid *, const godot_vector2i *); + void (*font_remove_texture)(void *, godot_rid *, const godot_vector2i *, godot_int); + void (*font_set_texture_image)(void *, godot_rid *, const godot_vector2i *, godot_int, const godot_object *); + godot_object *(*font_get_texture_image)(const void *, godot_rid *, const godot_vector2i *, godot_int); + void (*font_set_texture_offsets)(void *, godot_rid *, const godot_vector2i *, godot_int, const godot_packed_int32_array *); + godot_packed_int32_array (*font_get_texture_offsets)(const void *, godot_rid *, const godot_vector2i *, godot_int); + godot_array (*font_get_glyph_list)(const void *, godot_rid *, const godot_vector2i *); + void (*font_clear_glyphs)(void *, godot_rid *, const godot_vector2i *); + void (*font_remove_glyph)(void *, godot_rid *, const godot_vector2i *, int32_t); + godot_vector2 (*font_get_glyph_advance)(const void *, godot_rid *, godot_int, int32_t); + void (*font_set_glyph_advance)(void *, godot_rid *, godot_int, int32_t, const godot_vector2 *); + godot_vector2 (*font_get_glyph_offset)(const void *, godot_rid *, const godot_vector2i *, int32_t); + void (*font_set_glyph_offset)(void *, godot_rid *, const godot_vector2i *, int32_t, const godot_vector2 *); + godot_vector2 (*font_get_glyph_size)(const void *, godot_rid *, const godot_vector2i *, int32_t); + void (*font_set_glyph_size)(void *, godot_rid *, const godot_vector2i *, int32_t, const godot_vector2 *); + godot_rect2 (*font_get_glyph_uv_rect)(const void *, godot_rid *, const godot_vector2i *, int32_t); + void (*font_set_glyph_uv_rect)(void *, godot_rid *, const godot_vector2i *, int32_t, const godot_rect2 *); + godot_int (*font_get_glyph_texture_idx)(const void *, godot_rid *, const godot_vector2i *, int32_t); + void (*font_set_glyph_texture_idx)(void *, godot_rid *, const godot_vector2i *, int32_t, godot_int); + bool (*font_get_glyph_contours)(const void *, godot_rid *, godot_int, int32_t, godot_packed_vector3_array *, godot_packed_int32_array *, bool *); + godot_array (*font_get_kerning_list)(const void *, godot_rid *, godot_int); + void (*font_clear_kerning_map)(void *, godot_rid *, godot_int); + void (*font_remove_kerning)(void *, godot_rid *, godot_int, const godot_vector2i *); + void (*font_set_kerning)(void *, godot_rid *, godot_int, const godot_vector2i *, const godot_vector2 *); + godot_vector2 (*font_get_kerning)(const void *, godot_rid *, godot_int, const godot_vector2i *); + int32_t (*font_get_glyph_index)(const void *, godot_rid *, godot_int, char32_t, char32_t); + bool (*font_has_char)(const void *, godot_rid *, char32_t); + godot_string (*font_get_supported_chars)(const void *, godot_rid *); + void (*font_render_range)(void *, godot_rid *, const godot_vector2i *, char32_t, char32_t); + void (*font_render_glyph)(void *, godot_rid *, const godot_vector2i *, int32_t); + void (*font_draw_glyph)(const void *, godot_rid *, godot_rid *, godot_int, const godot_vector2 *, int32_t, const godot_color *); + void (*font_draw_glyph_outline)(const void *, godot_rid *, godot_rid *, godot_int, godot_int, const godot_vector2 *, int32_t, const godot_color *); + bool (*font_is_language_supported)(const 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 *); + bool (*font_get_language_support_override)(const 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 *); + godot_packed_string_array (*font_get_language_support_overrides)(const void *, godot_rid *); + bool (*font_is_script_supported)(const 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 *); + bool (*font_get_script_support_override)(const 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 *); - bool (*font_get_glyph_contours)(void *, godot_rid *, int, uint32_t, godot_packed_vector3_array *, godot_packed_int32_array *, bool *); - float (*font_get_oversampling)(void *); - void (*font_set_oversampling)(void *, float); - godot_packed_string_array (*get_system_fonts)(void *); + godot_packed_string_array (*font_get_script_support_overrides)(const void *, godot_rid *); + godot_dictionary (*font_supported_feature_list)(const void *, godot_rid *); + godot_dictionary (*font_supported_variation_list)(const void *, godot_rid *); + godot_real_t (*font_get_global_oversampling)(const void *); + void (*font_set_global_oversampling)(void *, godot_real_t); + 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); @@ -138,27 +179,28 @@ typedef struct { 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 *); + godot_real_t (*shaped_text_fit_to_width)(void *, godot_rid *, godot_real_t, uint8_t); + godot_real_t (*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 *); - void (*shaped_text_overrun_trim_to_width)(void *, godot_rid *, float, uint8_t); + void (*shaped_text_overrun_trim_to_width)(void *, godot_rid *, godot_real_t, uint8_t); 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_line_breaks)(void *, godot_rid *, godot_real_t, int, uint8_t); godot_packed_vector2i_array (*shaped_text_get_word_breaks)(void *, godot_rid *, int); 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_real_t (*shaped_text_get_ascent)(void *, godot_rid *); + godot_real_t (*shaped_text_get_descent)(void *, godot_rid *); + godot_real_t (*shaped_text_get_width)(void *, godot_rid *); + godot_real_t (*shaped_text_get_underline_position)(void *, godot_rid *); + godot_real_t (*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 *); @@ -185,8 +227,8 @@ 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_float GDAPI godot_glyph_get_advance(const godot_glyph *p_self); -void GDAPI godot_glyph_set_advance(godot_glyph *p_self, godot_float p_advance); +godot_real_t GDAPI godot_glyph_get_advance(const godot_glyph *p_self); +void GDAPI godot_glyph_set_advance(godot_glyph *p_self, godot_real_t 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); diff --git a/modules/gdnative/include/xr/godot_xr.h b/modules/gdnative/include/xr/godot_xr.h deleted file mode 100644 index 53cb830cbb..0000000000 --- a/modules/gdnative/include/xr/godot_xr.h +++ /dev/null @@ -1,98 +0,0 @@ -/*************************************************************************/ -/* godot_xr.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 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_NATIVEXR_H -#define GODOT_NATIVEXR_H - -#include <gdnative/gdnative.h> - -#ifdef __cplusplus -extern "C" { -#endif - -// For future versions of the API we should only add new functions at the end of the structure and use the -// version info to detect whether a call is available - -// Use these to populate version in your plugin -#define GODOTVR_API_MAJOR 4 -#define GODOTVR_API_MINOR 0 - -typedef struct { - godot_gdnative_api_version version; /* version of our API */ - void *(*constructor)(godot_object *); - void (*destructor)(void *); - godot_string (*get_name)(const void *); - godot_int (*get_capabilities)(const void *); - godot_bool (*get_anchor_detection_is_enabled)(const void *); - void (*set_anchor_detection_is_enabled)(void *, godot_bool); - godot_int (*get_view_count)(const void *); - godot_bool (*is_initialized)(const void *); - godot_bool (*initialize)(void *); - void (*uninitialize)(void *); - godot_vector2 (*get_render_targetsize)(const void *); - - godot_transform3d (*get_camera_transform)(void *); - godot_transform3d (*get_transform_for_view)(void *, godot_int, godot_transform3d *); - void (*fill_projection_for_view)(void *, godot_real_t *, godot_int, godot_real_t, godot_real_t, godot_real_t); - void (*commit_views)(void *, godot_rid *, godot_rect2 *); - - void (*process)(void *); - void (*notification)(void *, godot_int); - godot_int (*get_camera_feed_id)(void *); - - // possibly deprecate but adding/keeping as a reminder these are in Godot 3 - void (*commit_for_eye)(void *, godot_int, godot_rid *, godot_rect2 *); - godot_int (*get_external_texture_for_eye)(void *, godot_int); - godot_int (*get_external_depth_for_eye)(void *, godot_int); -} godot_xr_interface_gdnative; - -void GDAPI godot_xr_register_interface(const godot_xr_interface_gdnative *p_interface); - -// helper functions to access XRServer data -godot_real_t GDAPI godot_xr_get_worldscale(); -godot_transform3d GDAPI godot_xr_get_reference_frame(); - -// helper functions for rendering -void GDAPI godot_xr_blit(godot_int p_eye, godot_rid *p_render_target, godot_rect2 *p_rect); -godot_int GDAPI godot_xr_get_texid(godot_rid *p_render_target); - -// helper functions for updating XR controllers -godot_int GDAPI godot_xr_add_controller(char *p_device_name, godot_int p_hand, godot_bool p_tracks_orientation, godot_bool p_tracks_position); -void GDAPI godot_xr_remove_controller(godot_int p_controller_id); -void GDAPI godot_xr_set_controller_transform(godot_int p_controller_id, godot_transform3d *p_transform, godot_bool p_tracks_orientation, godot_bool p_tracks_position); -void GDAPI godot_xr_set_controller_button(godot_int p_controller_id, godot_int p_button, godot_bool p_is_pressed); -void GDAPI godot_xr_set_controller_axis(godot_int p_controller_id, godot_int p_axis, godot_real_t p_value, godot_bool p_can_be_negative); -godot_real_t GDAPI godot_xr_get_controller_rumble(godot_int p_controller_id); - -#ifdef __cplusplus -} -#endif - -#endif /* !GODOT_NATIVEXR_H */ diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp index df0f29277e..2d9b45cb07 100644 --- a/modules/gdnative/nativescript/api_generator.cpp +++ b/modules/gdnative/nativescript/api_generator.cpp @@ -242,13 +242,9 @@ List<ClassAPI> generate_c_api_classes() { class_api.class_name = class_name; class_api.super_class_name = ClassDB::get_parent_class(class_name); { - String name = class_name; - if (name.begins_with("_")) { - name.remove(0); - } - class_api.is_singleton = Engine::get_singleton()->has_singleton(name); + class_api.is_singleton = Engine::get_singleton()->has_singleton(class_name); if (class_api.is_singleton) { - class_api.singleton_name = name; + class_api.singleton_name = class_name; } } class_api.is_instantiable = !class_api.is_singleton && ClassDB::can_instantiate(class_name); diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp index f3a0e9603f..26d3aed702 100644 --- a/modules/gdnative/nativescript/nativescript.cpp +++ b/modules/gdnative/nativescript/nativescript.cpp @@ -114,9 +114,25 @@ void NativeScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) #endif bool NativeScript::inherits_script(const Ref<Script> &p_script) const { -#ifndef _MSC_VER -#warning inheritance needs to be implemented in NativeScript -#endif + Ref<NativeScript> ns = p_script; + if (ns.is_null()) { + return false; + } + + const NativeScriptDesc *other_s = ns->get_script_desc(); + if (!other_s) { + return false; + } + + const NativeScriptDesc *s = get_script_desc(); + + while (s) { + if (s == other_s) { + return true; + } + s = s->base_data; + } + return false; } diff --git a/modules/gdnative/pluginscript/pluginscript_script.cpp b/modules/gdnative/pluginscript/pluginscript_script.cpp index 7fc8178e34..5380858582 100644 --- a/modules/gdnative/pluginscript/pluginscript_script.cpp +++ b/modules/gdnative/pluginscript/pluginscript_script.cpp @@ -139,9 +139,20 @@ bool PluginScript::can_instantiate() const { } bool PluginScript::inherits_script(const Ref<Script> &p_script) const { -#ifndef _MSC_VER -#warning inheritance needs to be implemented in PluginScript -#endif + Ref<PluginScript> ps = p_script; + if (ps.is_null()) { + return false; + } + + const PluginScript *s = this; + + while (s) { + if (s == p_script.ptr()) { + return true; + } + s = Object::cast_to<PluginScript>(s->_ref_base_parent.ptr()); + } + return false; } diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp index a41c4f7b19..e4c2b20224 100644 --- a/modules/gdnative/register_types.cpp +++ b/modules/gdnative/register_types.cpp @@ -38,7 +38,6 @@ #include "net/register_types.h" #include "pluginscript/register_types.h" #include "videodecoder/register_types.h" -#include "xr/register_types.h" #include "core/config/engine.h" #include "core/config/project_settings.h" @@ -267,7 +266,6 @@ void register_gdnative_types() { GDNativeCallRegistry::singleton->register_native_call_type("standard_varcall", cb_standard_varcall); register_net_types(); - register_xr_types(); register_nativescript_types(); register_pluginscript_types(); register_videodecoder_types(); @@ -331,7 +329,6 @@ void unregister_gdnative_types() { unregister_videodecoder_types(); unregister_pluginscript_types(); unregister_nativescript_types(); - unregister_xr_types(); unregister_net_types(); memdelete(GDNativeCallRegistry::singleton); diff --git a/modules/gdnative/text/text_server_gdnative.cpp b/modules/gdnative/text/text_server_gdnative.cpp index 81dd570bcb..39db8ae636 100644 --- a/modules/gdnative/text/text_server_gdnative.cpp +++ b/modules/gdnative/text/text_server_gdnative.cpp @@ -88,299 +88,479 @@ bool TextServerGDNative::is_locale_right_to_left(const String &p_locale) { return interface->is_locale_right_to_left(data, (godot_string *)&p_locale); } +int32_t TextServerGDNative::name_to_tag(const String &p_name) const { + ERR_FAIL_COND_V(interface == nullptr, 0); + return interface->name_to_tag(data, (godot_string *)&p_name); +} + +String TextServerGDNative::tag_to_name(int32_t p_tag) const { + ERR_FAIL_COND_V(interface == nullptr, String()); + godot_string result = interface->tag_to_name(data, p_tag); + String name = *(String *)&result; + godot_string_destroy(&result); + return name; +} + /*************************************************************************/ -/* Font interface */ +/* Font */ /*************************************************************************/ -RID TextServerGDNative::create_font_system(const String &p_name, int p_base_size) { +RID TextServerGDNative::create_font() { ERR_FAIL_COND_V(interface == nullptr, RID()); - godot_rid result = interface->create_font_system(data, (const godot_string *)&p_name, p_base_size); + godot_rid result = interface->create_font(data); 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; +void TextServerGDNative::font_set_data(RID p_font_rid, const PackedByteArray &p_data) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_data(data, (godot_rid *)&p_font_rid, (const godot_packed_byte_array *)&p_data); } -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; +void TextServerGDNative::font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_data_ptr(data, (godot_rid *)&p_font_rid, p_data_ptr, p_data_size); } -RID TextServerGDNative::create_font_bitmap(float p_height, float p_ascent, int p_base_size) { - ERR_FAIL_COND_V(interface == nullptr, RID()); - godot_rid result = interface->create_font_bitmap(data, p_height, p_ascent, p_base_size); - RID rid = *(RID *)&result; - return rid; +void TextServerGDNative::font_set_antialiased(RID p_font_rid, bool p_antialiased) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_antialiased(data, (godot_rid *)&p_font_rid, p_antialiased); } -void TextServerGDNative::font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) { +bool TextServerGDNative::font_is_antialiased(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, false); + return interface->font_is_antialiased(data, (godot_rid *)&p_font_rid); +} + +void TextServerGDNative::font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) { ERR_FAIL_COND(interface == nullptr); - interface->font_bitmap_add_texture(data, (godot_rid *)&p_font, (const godot_object *)p_texture.ptr()); + interface->font_set_multichannel_signed_distance_field(data, (godot_rid *)&p_font_rid, p_msdf); +} + +bool TextServerGDNative::font_is_multichannel_signed_distance_field(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, false); + return interface->font_is_multichannel_signed_distance_field(data, (godot_rid *)&p_font_rid); } -void TextServerGDNative::font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { +void TextServerGDNative::font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) { ERR_FAIL_COND(interface == nullptr); - interface->font_bitmap_add_char(data, (godot_rid *)&p_font, p_char, p_texture_idx, (const godot_rect2 *)&p_rect, (const godot_vector2 *)&p_align, p_advance); + interface->font_set_msdf_pixel_range(data, (godot_rid *)&p_font_rid, p_msdf_pixel_range); } -void TextServerGDNative::font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) { +int TextServerGDNative::font_get_msdf_pixel_range(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, 0); + return interface->font_get_msdf_pixel_range(data, (godot_rid *)&p_font_rid); +} + +void TextServerGDNative::font_set_msdf_size(RID p_font_rid, int p_msdf_size) { ERR_FAIL_COND(interface == nullptr); - interface->font_bitmap_add_kerning_pair(data, (godot_rid *)&p_font, p_A, p_B, p_kerning); + interface->font_set_msdf_size(data, (godot_rid *)&p_font_rid, p_msdf_size); } -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); +int TextServerGDNative::font_get_msdf_size(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, 0); + return interface->font_get_msdf_size(data, (godot_rid *)&p_font_rid); } -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); +void TextServerGDNative::font_set_fixed_size(RID p_font_rid, int p_fixed_size) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_fixed_size(data, (godot_rid *)&p_font_rid, p_fixed_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); +int TextServerGDNative::font_get_fixed_size(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, 0); + return interface->font_get_fixed_size(data, (godot_rid *)&p_font_rid); } -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); +void TextServerGDNative::font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_force_autohinter(data, (godot_rid *)&p_font_rid, p_force_autohinter); } -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); +bool TextServerGDNative::font_is_force_autohinter(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, false); + return interface->font_is_force_autohinter(data, (godot_rid *)&p_font_rid); } -int TextServerGDNative::font_get_spacing_space(RID p_font) const { - ERR_FAIL_COND_V(interface == nullptr, 0); - return interface->font_get_spacing_space(data, (godot_rid *)&p_font); +void TextServerGDNative::font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_hinting(data, (godot_rid *)&p_font_rid, (godot_int)p_hinting); } -void TextServerGDNative::font_set_spacing_space(RID p_font, int p_value) { +TextServer::Hinting TextServerGDNative::font_get_hinting(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, TextServer::HINTING_NONE); + return (TextServer::Hinting)interface->font_get_hinting(data, (godot_rid *)&p_font_rid); +} + +void TextServerGDNative::font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_variation_coordinates(data, (godot_rid *)&p_font_rid, (const godot_dictionary *)&p_variation_coordinates); +} + +Dictionary TextServerGDNative::font_get_variation_coordinates(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, Dictionary()); + godot_dictionary result = interface->font_get_variation_coordinates(data, (godot_rid *)&p_font_rid); + Dictionary dict = *(Dictionary *)&result; + godot_dictionary_destroy(&result); + return dict; +} + +void TextServerGDNative::font_set_oversampling(RID p_font_rid, real_t p_oversampling) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_oversampling(data, (godot_rid *)&p_font_rid, p_oversampling); +} + +real_t TextServerGDNative::font_get_oversampling(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, 0.0f); + return interface->font_get_oversampling(data, (godot_rid *)&p_font_rid); +} + +Array TextServerGDNative::font_get_size_cache_list(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, Array()); + godot_array result = interface->font_get_size_cache_list(data, (godot_rid *)&p_font_rid); + Array list = *(Array *)&result; + godot_array_destroy(&result); + return list; +} + +void TextServerGDNative::font_clear_size_cache(RID p_font_rid) { + ERR_FAIL_COND(interface == nullptr); + interface->font_clear_size_cache(data, (godot_rid *)&p_font_rid); +} + +void TextServerGDNative::font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) { + ERR_FAIL_COND(interface == nullptr); + interface->font_remove_size_cache(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size); +} + +void TextServerGDNative::font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) { ERR_FAIL_COND(interface == nullptr); - interface->font_set_spacing_space(data, (godot_rid *)&p_font, p_value); + interface->font_set_ascent(data, (godot_rid *)&p_font_rid, p_size, p_ascent); +} + +real_t TextServerGDNative::font_get_ascent(RID p_font_rid, int p_size) const { + ERR_FAIL_COND_V(interface == nullptr, 0.0f); + return interface->font_get_ascent(data, (godot_rid *)&p_font_rid, p_size); } -int TextServerGDNative::font_get_spacing_glyph(RID p_font) const { +void TextServerGDNative::font_set_descent(RID p_font_rid, int p_size, real_t p_descent) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_descent(data, (godot_rid *)&p_font_rid, p_size, p_descent); +} + +real_t TextServerGDNative::font_get_descent(RID p_font_rid, int p_size) const { + ERR_FAIL_COND_V(interface == nullptr, 0.0f); + return interface->font_get_descent(data, (godot_rid *)&p_font_rid, p_size); +} + +void TextServerGDNative::font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_underline_position(data, (godot_rid *)&p_font_rid, p_size, p_underline_position); +} + +real_t TextServerGDNative::font_get_underline_position(RID p_font_rid, int p_size) const { + ERR_FAIL_COND_V(interface == nullptr, 0.0f); + return interface->font_get_underline_position(data, (godot_rid *)&p_font_rid, p_size); +} + +void TextServerGDNative::font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_underline_thickness(data, (godot_rid *)&p_font_rid, p_size, p_underline_thickness); +} + +real_t TextServerGDNative::font_get_underline_thickness(RID p_font_rid, int p_size) const { + ERR_FAIL_COND_V(interface == nullptr, 0.0f); + return interface->font_get_underline_thickness(data, (godot_rid *)&p_font_rid, p_size); +} + +void TextServerGDNative::font_set_scale(RID p_font_rid, int p_size, real_t p_scale) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_scale(data, (godot_rid *)&p_font_rid, p_size, p_scale); +} + +real_t TextServerGDNative::font_get_scale(RID p_font_rid, int p_size) const { + ERR_FAIL_COND_V(interface == nullptr, 0.0f); + return interface->font_get_scale(data, (godot_rid *)&p_font_rid, p_size); +} + +void TextServerGDNative::font_set_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing, int p_value) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_spacing(data, (godot_rid *)&p_font_rid, p_size, (godot_int)p_spacing, p_value); +} + +int TextServerGDNative::font_get_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing) const { ERR_FAIL_COND_V(interface == nullptr, 0); - return interface->font_get_spacing_glyph(data, (godot_rid *)&p_font); + return interface->font_get_spacing(data, (godot_rid *)&p_font_rid, p_size, (godot_int)p_spacing); } -void TextServerGDNative::font_set_spacing_glyph(RID p_font, int p_value) { +int TextServerGDNative::font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const { + ERR_FAIL_COND_V(interface == nullptr, -1); + return interface->font_get_texture_count(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size); +} + +void TextServerGDNative::font_clear_textures(RID p_font_rid, const Vector2i &p_size) { ERR_FAIL_COND(interface == nullptr); - interface->font_set_spacing_glyph(data, (godot_rid *)&p_font, p_value); + interface->font_clear_textures(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size); } -void TextServerGDNative::font_set_antialiased(RID p_font, bool p_antialiased) { +void TextServerGDNative::font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) { ERR_FAIL_COND(interface == nullptr); - interface->font_set_antialiased(data, (godot_rid *)&p_font, p_antialiased); + interface->font_remove_texture(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_texture_index); } -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); +void TextServerGDNative::font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_texture_image(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_texture_index, (const godot_object *)p_image.ptr()); } -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); +Ref<Image> TextServerGDNative::font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const { + ERR_FAIL_COND_V(interface == nullptr, Ref<Image>()); + godot_object *result = interface->font_get_texture_image(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_texture_index); + return Ref<Image>((Image *)result); +} - return info; +void TextServerGDNative::font_set_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_texture_offsets(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_texture_index, (const godot_packed_int32_array *)&p_offset); +} + +PackedInt32Array TextServerGDNative::font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const { + ERR_FAIL_COND_V(interface == nullptr, PackedInt32Array()); + godot_packed_int32_array result = interface->font_get_texture_offsets(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_texture_index); + PackedInt32Array offset = *(PackedInt32Array *)&result; + godot_packed_int32_array_destroy(&result); + return offset; +} + +Array TextServerGDNative::font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const { + ERR_FAIL_COND_V(interface == nullptr, Array()); + godot_array result = interface->font_get_glyph_list(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size); + Array list = *(Array *)&result; + godot_array_destroy(&result); + return list; +} + +void TextServerGDNative::font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) { + ERR_FAIL_COND(interface == nullptr); + interface->font_clear_glyphs(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size); } -void TextServerGDNative::font_set_variation(RID p_font, const String &p_name, double p_value) { +void TextServerGDNative::font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) { ERR_FAIL_COND(interface == nullptr); - interface->font_set_variation(data, (godot_rid *)&p_font, (godot_string *)&p_name, p_value); + interface->font_remove_glyph(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph); } -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); +Vector2 TextServerGDNative::font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(interface == nullptr, Vector2()); + godot_vector2 result = interface->font_get_glyph_advance(data, (godot_rid *)&p_font_rid, p_size, p_glyph); + Vector2 adv = *(Vector2 *)&result; + return adv; } -void TextServerGDNative::font_set_hinting(RID p_font, TextServer::Hinting p_hinting) { +void TextServerGDNative::font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) { ERR_FAIL_COND(interface == nullptr); - interface->font_set_hinting(data, (godot_rid *)&p_font, (godot_int)p_hinting); + interface->font_set_glyph_advance(data, (godot_rid *)&p_font_rid, p_size, p_glyph, (const godot_vector2 *)&p_advance); } -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); +Vector2 TextServerGDNative::font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(interface == nullptr, Vector2()); + godot_vector2 result = interface->font_get_glyph_offset(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph); + Vector2 off = *(Vector2 *)&result; + return off; } -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); +void TextServerGDNative::font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_glyph_offset(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph, (const godot_vector2 *)&p_offset); +} - return info; +Vector2 TextServerGDNative::font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(interface == nullptr, Vector2()); + godot_vector2 result = interface->font_get_glyph_size(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph); + Vector2 sz = *(Vector2 *)&result; + return sz; } -void TextServerGDNative::font_set_distance_field_hint(RID p_font, bool p_distance_field) { +void TextServerGDNative::font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) { ERR_FAIL_COND(interface == nullptr); - interface->font_set_distance_field_hint(data, (godot_rid *)&p_font, p_distance_field); + interface->font_set_glyph_size(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph, (const godot_vector2 *)&p_gl_size); } -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); +Rect2 TextServerGDNative::font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(interface == nullptr, Rect2()); + godot_rect2 result = interface->font_get_glyph_uv_rect(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph); + Rect2 uv = *(Rect2 *)&result; + return uv; } -void TextServerGDNative::font_set_force_autohinter(RID p_font, bool p_enabeld) { +void TextServerGDNative::font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) { ERR_FAIL_COND(interface == nullptr); - interface->font_set_force_autohinter(data, (godot_rid *)&p_font, p_enabeld); + interface->font_set_glyph_uv_rect(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph, (const godot_rect2 *)&p_uv_rect); } -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); +int TextServerGDNative::font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(interface == nullptr, -1); + return interface->font_get_glyph_texture_idx(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph); +} + +void TextServerGDNative::font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_glyph_texture_idx(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph, p_texture_idx); } -bool TextServerGDNative::font_has_char(RID p_font, char32_t p_char) const { +bool TextServerGDNative::font_get_glyph_contours(RID p_font_rid, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const { ERR_FAIL_COND_V(interface == nullptr, false); - return interface->font_has_char(data, (godot_rid *)&p_font, p_char); + return interface->font_get_glyph_contours(data, (godot_rid *)&p_font_rid, p_size, p_index, (godot_packed_vector3_array *)&r_points, (godot_packed_int32_array *)&r_contours, &r_orientation); } -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; +Array TextServerGDNative::font_get_kerning_list(RID p_font_rid, int p_size) const { + ERR_FAIL_COND_V(interface == nullptr, Array()); + godot_array result = interface->font_get_kerning_list(data, (godot_rid *)&p_font_rid, p_size); + Array list = *(Array *)&result; + godot_array_destroy(&result); + return list; } -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); +void TextServerGDNative::font_clear_kerning_map(RID p_font_rid, int p_size) { + ERR_FAIL_COND(interface == nullptr); + interface->font_clear_kerning_map(data, (godot_rid *)&p_font_rid, p_size); } -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); +void TextServerGDNative::font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) { + ERR_FAIL_COND(interface == nullptr); + interface->font_remove_kerning(data, (godot_rid *)&p_font_rid, p_size, (const godot_vector2i *)&p_glyph_pair); +} + +void TextServerGDNative::font_set_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_kerning(data, (godot_rid *)&p_font_rid, p_size, (const godot_vector2i *)&p_glyph_pair, (const godot_vector2 *)&p_kerning); +} + +Vector2 TextServerGDNative::font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const { + ERR_FAIL_COND_V(interface == nullptr, Vector2()); + godot_vector2 result = interface->font_get_kerning(data, (godot_rid *)&p_font_rid, p_size, (const godot_vector2i *)&p_glyph_pair); + Vector2 kern = *(Vector2 *)&result; + return kern; } -bool TextServerGDNative::font_is_language_supported(RID p_font, const String &p_language) const { +int32_t TextServerGDNative::font_get_glyph_index(RID p_font_rid, int p_size, 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_rid, p_size, p_char, p_variation_selector); +} + +bool TextServerGDNative::font_has_char(RID p_font_rid, char32_t p_char) const { ERR_FAIL_COND_V(interface == nullptr, false); - return interface->font_is_language_supported(data, (godot_rid *)&p_font, (godot_string *)&p_language); + return interface->font_has_char(data, (godot_rid *)&p_font_rid, p_char); +} + +String TextServerGDNative::font_get_supported_chars(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, String()); + godot_string result = interface->font_get_supported_chars(data, (godot_rid *)&p_font_rid); + String chars = *(String *)&result; + godot_string_destroy(&result); + return chars; } -void TextServerGDNative::font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) { +void TextServerGDNative::font_render_range(RID p_font_rid, const Vector2i &p_size, char32_t p_start, char32_t p_end) { ERR_FAIL_COND(interface == nullptr); - return interface->font_set_language_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_language, p_supported); + interface->font_render_range(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_start, p_end); } -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_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) { + ERR_FAIL_COND(interface == nullptr); + interface->font_render_glyph(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_index); } -void TextServerGDNative::font_remove_language_support_override(RID p_font, const String &p_language) { +void TextServerGDNative::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const { ERR_FAIL_COND(interface == nullptr); - interface->font_remove_language_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_language); + interface->font_draw_glyph(data, (godot_rid *)&p_font_rid, (godot_rid *)&p_canvas, p_size, (const godot_vector2 *)&p_pos, p_index, (const godot_color *)&p_color); } -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; +void TextServerGDNative::font_draw_glyph_outline(RID p_font_rid, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const { + ERR_FAIL_COND(interface == nullptr); + interface->font_draw_glyph_outline(data, (godot_rid *)&p_font_rid, (godot_rid *)&p_canvas, p_size, p_outline_size, (const godot_vector2 *)&p_pos, p_index, (const godot_color *)&p_color); } -bool TextServerGDNative::font_is_script_supported(RID p_font, const String &p_script) const { +bool TextServerGDNative::font_is_language_supported(RID p_font_rid, const String &p_language) const { ERR_FAIL_COND_V(interface == nullptr, false); - return interface->font_is_script_supported(data, (godot_rid *)&p_font, (godot_string *)&p_script); + return interface->font_is_language_supported(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_language); } -void TextServerGDNative::font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) { +void TextServerGDNative::font_set_language_support_override(RID p_font_rid, const String &p_language, 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); + interface->font_set_language_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_language, p_supported); } -bool TextServerGDNative::font_get_script_support_override(RID p_font, const String &p_script) { +bool TextServerGDNative::font_get_language_support_override(RID p_font_rid, const String &p_language) { ERR_FAIL_COND_V(interface == nullptr, false); - return interface->font_get_script_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_script); + return interface->font_get_language_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_language); } -void TextServerGDNative::font_remove_script_support_override(RID p_font, const String &p_script) { +void TextServerGDNative::font_remove_language_support_override(RID p_font_rid, const String &p_language) { ERR_FAIL_COND(interface == nullptr); - interface->font_remove_script_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_script); + interface->font_remove_language_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_language); } -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; +Vector<String> TextServerGDNative::font_get_language_support_overrides(RID p_font_rid) { + ERR_FAIL_COND_V(interface == nullptr, PackedStringArray()); + godot_packed_string_array result = interface->font_get_language_support_overrides(data, (godot_rid *)&p_font_rid); + PackedStringArray list = *(PackedStringArray *)&result; godot_packed_string_array_destroy(&result); - return ret; + return list; } -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); +bool TextServerGDNative::font_is_script_supported(RID p_font_rid, const String &p_script) const { + ERR_FAIL_COND_V(interface == nullptr, false); + return interface->font_is_script_supported(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_script); } -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; +void TextServerGDNative::font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_script_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_script, p_supported); } -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; +bool TextServerGDNative::font_get_script_support_override(RID p_font_rid, const String &p_script) { + ERR_FAIL_COND_V(interface == nullptr, false); + return interface->font_get_script_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_script); } -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; +void TextServerGDNative::font_remove_script_support_override(RID p_font_rid, const String &p_script) { + ERR_FAIL_COND(interface == nullptr); + interface->font_remove_script_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_script); } -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; +Vector<String> TextServerGDNative::font_get_script_support_overrides(RID p_font_rid) { + ERR_FAIL_COND_V(interface == nullptr, PackedStringArray()); + godot_packed_string_array result = interface->font_get_script_support_overrides(data, (godot_rid *)&p_font_rid); + PackedStringArray list = *(PackedStringArray *)&result; + godot_packed_string_array_destroy(&result); + return list; } -bool TextServerGDNative::font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const { - ERR_FAIL_COND_V(interface == nullptr, false); - ERR_FAIL_COND_V(interface->font_get_glyph_contours == nullptr, false); - return interface->font_get_glyph_contours(data, (godot_rid *)&p_font, p_size, p_index, (godot_packed_vector3_array *)&r_points, (godot_packed_int32_array *)&r_contours, (bool *)&r_orientation); +Dictionary TextServerGDNative::font_supported_feature_list(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, Dictionary()); + godot_dictionary result = interface->font_supported_feature_list(data, (godot_rid *)&p_font_rid); + Dictionary dict = *(Dictionary *)&result; + godot_dictionary_destroy(&result); + return dict; } -float TextServerGDNative::font_get_oversampling() const { - ERR_FAIL_COND_V(interface == nullptr, 1.f); - return interface->font_get_oversampling(data); +Dictionary TextServerGDNative::font_supported_variation_list(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, Dictionary()); + godot_dictionary result = interface->font_supported_variation_list(data, (godot_rid *)&p_font_rid); + Dictionary dict = *(Dictionary *)&result; + godot_dictionary_destroy(&result); + return dict; } -void TextServerGDNative::font_set_oversampling(float p_oversampling) { - ERR_FAIL_COND(interface == nullptr); - return interface->font_set_oversampling(data, p_oversampling); +real_t TextServerGDNative::font_get_global_oversampling() const { + ERR_FAIL_COND_V(interface == nullptr, 0.0f); + return interface->font_get_global_oversampling(data); } -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; +void TextServerGDNative::font_set_global_oversampling(real_t p_oversampling) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_global_oversampling(data, p_oversampling); } /*************************************************************************/ @@ -449,12 +629,12 @@ bool TextServerGDNative::shaped_text_add_string(RID p_shaped, const String &p_te 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) { +bool TextServerGDNative::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign 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) { +bool TextServerGDNative::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign 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); } @@ -473,12 +653,12 @@ RID TextServerGDNative::shaped_text_get_parent(RID p_shaped) const { return rid; } -float TextServerGDNative::shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t p_jst_flags) { +real_t TextServerGDNative::shaped_text_fit_to_width(RID p_shaped, real_t 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) { +real_t TextServerGDNative::shaped_text_tab_align(RID p_shaped, const Vector<real_t> &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); } @@ -498,9 +678,9 @@ bool TextServerGDNative::shaped_text_update_justification_ops(RID p_shaped) { return interface->shaped_text_update_justification_ops(data, (godot_rid *)&p_shaped); } -void TextServerGDNative::shaped_text_overrun_trim_to_width(RID p_shaped_line, float p_width, uint8_t p_clip_flags) { +void TextServerGDNative::shaped_text_overrun_trim_to_width(RID p_shaped_line, real_t p_width, uint8_t p_trim_flags) { ERR_FAIL_COND(interface == nullptr); - interface->shaped_text_overrun_trim_to_width(data, (godot_rid *)&p_shaped_line, p_width, p_clip_flags); + interface->shaped_text_overrun_trim_to_width(data, (godot_rid *)&p_shaped_line, p_width, p_trim_flags); }; bool TextServerGDNative::shaped_text_is_ready(RID p_shaped) const { @@ -531,7 +711,7 @@ Vector<TextServer::Glyph> TextServerGDNative::shaped_text_sort_logical(RID p_sha 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 { +Vector<Vector2i> TextServerGDNative::shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<real_t> &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); @@ -543,7 +723,7 @@ Vector<Vector2i> TextServerGDNative::shaped_text_get_line_breaks_adv(RID p_shape } } -Vector<Vector2i> TextServerGDNative::shaped_text_get_line_breaks(RID p_shaped, float p_width, int p_start, uint8_t p_break_flags) const { +Vector<Vector2i> TextServerGDNative::shaped_text_get_line_breaks(RID p_shaped, real_t 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); @@ -588,27 +768,27 @@ Size2 TextServerGDNative::shaped_text_get_size(RID p_shaped) const { return size; } -float TextServerGDNative::shaped_text_get_ascent(RID p_shaped) const { +real_t 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 { +real_t 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 { +real_t 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 { +real_t 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 { +real_t 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); } @@ -756,12 +936,12 @@ void GDAPI godot_glyph_set_offset(godot_glyph *p_self, const godot_vector2 *p_of self->y_off = offset->y; } -godot_float GDAPI godot_glyph_get_advance(const godot_glyph *p_self) { +godot_real_t 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_float p_advance) { +void GDAPI godot_glyph_set_advance(godot_glyph *p_self, godot_real_t p_advance) { TextServer::Glyph *self = (TextServer::Glyph *)p_self; self->advance = p_advance; } diff --git a/modules/gdnative/text/text_server_gdnative.h b/modules/gdnative/text/text_server_gdnative.h index 7a0725f3d9..f081637e23 100644 --- a/modules/gdnative/text/text_server_gdnative.h +++ b/modules/gdnative/text/text_server_gdnative.h @@ -60,78 +60,130 @@ public: virtual bool is_locale_right_to_left(const String &p_locale) override; + virtual int32_t name_to_tag(const String &p_name) const override; + virtual String tag_to_name(int32_t p_tag) const 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 RID create_font_bitmap(float p_height, float p_ascent, int p_base_size = 16) override; + virtual RID create_font() override; + + virtual void font_set_data(RID p_font_rid, const PackedByteArray &p_data) override; + virtual void font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) override; + + virtual void font_set_antialiased(RID p_font_rid, bool p_antialiased) override; + virtual bool font_is_antialiased(RID p_font_rid) const override; + + virtual void font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) override; + virtual bool font_is_multichannel_signed_distance_field(RID p_font_rid) const override; + + virtual void font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) override; + virtual int font_get_msdf_pixel_range(RID p_font_rid) const override; + + virtual void font_set_msdf_size(RID p_font_rid, int p_msdf_size) override; + virtual int font_get_msdf_size(RID p_font_rid) const override; + + virtual void font_set_fixed_size(RID p_font_rid, int p_fixed_size) override; + virtual int font_get_fixed_size(RID p_font_rid) const override; + + virtual void font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) override; + virtual bool font_is_force_autohinter(RID p_font_rid) const override; + + virtual void font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) override; + virtual TextServer::Hinting font_get_hinting(RID p_font_rid) const override; + + virtual void font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) override; + virtual Dictionary font_get_variation_coordinates(RID p_font_rid) const override; + + virtual void font_set_oversampling(RID p_font_rid, real_t p_oversampling) override; + virtual real_t font_get_oversampling(RID p_font_rid) const override; + + virtual Array font_get_size_cache_list(RID p_font_rid) const override; + virtual void font_clear_size_cache(RID p_font_rid) override; + virtual void font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) override; + + virtual void font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) override; + virtual real_t font_get_ascent(RID p_font_rid, int p_size) const override; + + virtual void font_set_descent(RID p_font_rid, int p_size, real_t p_descent) override; + virtual real_t font_get_descent(RID p_font_rid, int p_size) const override; + + virtual void font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) override; + virtual real_t font_get_underline_position(RID p_font_rid, int p_size) const override; + + virtual void font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) override; + virtual real_t font_get_underline_thickness(RID p_font_rid, int p_size) const override; + + virtual void font_set_scale(RID p_font_rid, int p_size, real_t p_scale) override; + virtual real_t font_get_scale(RID p_font_rid, int p_size) const override; + + virtual void font_set_spacing(RID p_font_rid, int p_size, SpacingType p_spacing, int p_value) override; + virtual int font_get_spacing(RID p_font_rid, int p_size, SpacingType p_spacing) const override; - virtual void font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) override; - virtual void font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override; - virtual void font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) override; + virtual int font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const override; + virtual void font_clear_textures(RID p_font_rid, const Vector2i &p_size) override; + virtual void font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) 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 void font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) override; + virtual Ref<Image> font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) 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_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) override; + virtual PackedInt32Array font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const override; - virtual int font_get_spacing_space(RID p_font) const override; - virtual void font_set_spacing_space(RID p_font, int p_value) override; + virtual Array font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const override; + virtual void font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) override; + virtual void font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) override; - virtual int font_get_spacing_glyph(RID p_font) const override; - virtual void font_set_spacing_glyph(RID p_font, int p_value) override; + virtual Vector2 font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) override; - virtual void font_set_antialiased(RID p_font, bool p_antialiased) override; - virtual bool font_get_antialiased(RID p_font) const override; + virtual Vector2 font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) override; - virtual Dictionary font_get_feature_list(RID p_font) const override; - virtual Dictionary font_get_variation_list(RID p_font) const override; + virtual Vector2 font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) 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 Rect2 font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) override; - virtual void font_set_hinting(RID p_font, Hinting p_hinting) override; - virtual Hinting font_get_hinting(RID p_font) const override; + virtual int font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) 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_get_glyph_contours(RID p_font, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) 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 Array font_get_kerning_list(RID p_font_rid, int p_size) const override; + virtual void font_clear_kerning_map(RID p_font_rid, int p_size) override; + virtual void font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) 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_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) override; + virtual Vector2 font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const override; - virtual bool font_has_outline(RID p_font) const override; - virtual float font_get_base_size(RID p_font) const override; + virtual int32_t font_get_glyph_index(RID p_font_rid, int p_size, char32_t p_char, char32_t p_variation_selector = 0) 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_has_char(RID p_font_rid, char32_t p_char) const override; + virtual String font_get_supported_chars(RID p_font_rid) const 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 void font_render_range(RID p_font, const Vector2i &p_size, char32_t p_start, char32_t p_end) override; + virtual void font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) 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 void font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) const override; + virtual void font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) 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 bool font_is_language_supported(RID p_font_rid, const String &p_language) const override; + virtual void font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) override; + virtual bool font_get_language_support_override(RID p_font_rid, const String &p_language) override; + virtual void font_remove_language_support_override(RID p_font_rid, const String &p_language) override; + virtual Vector<String> font_get_language_support_overrides(RID p_font_rid) override; - virtual bool font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override; + virtual bool font_is_script_supported(RID p_font_rid, const String &p_script) const override; + virtual void font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) override; + virtual bool font_get_script_support_override(RID p_font_rid, const String &p_script) override; + virtual void font_remove_script_support_override(RID p_font_rid, const String &p_script) override; + virtual Vector<String> font_get_script_support_overrides(RID p_font_rid) override; - virtual float font_get_oversampling() const override; - virtual void font_set_oversampling(float p_oversampling) override; + virtual Dictionary font_supported_feature_list(RID p_font_rid) const override; + virtual Dictionary font_supported_variation_list(RID p_font_rid) const override; - virtual Vector<String> get_system_fonts() const override; + virtual real_t font_get_global_oversampling() const override; + virtual void font_set_global_oversampling(real_t p_oversampling) override; /* Shaped text buffer interface */ @@ -154,20 +206,20 @@ public: 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 bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER, int p_length = 1) override; + virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_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 real_t shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override; + virtual real_t shaped_text_tab_align(RID p_shaped, const Vector<real_t> &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 void shaped_text_overrun_trim_to_width(RID p_shaped, float p_width, uint8_t p_clip_flags) override; + virtual void shaped_text_overrun_trim_to_width(RID p_shaped, real_t p_width, uint8_t p_trim_flags) override; virtual bool shaped_text_is_ready(RID p_shaped) const override; @@ -176,18 +228,18 @@ public: 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_line_breaks_adv(RID p_shaped, const Vector<real_t> &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, real_t 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, int p_grapheme_flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_PUNCTUATION) 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 real_t shaped_text_get_ascent(RID p_shaped) const override; + virtual real_t shaped_text_get_descent(RID p_shaped) const override; + virtual real_t shaped_text_get_width(RID p_shaped) const override; + virtual real_t shaped_text_get_underline_position(RID p_shaped) const override; + virtual real_t 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; diff --git a/modules/gdnative/xr/SCsub b/modules/gdnative/xr/SCsub deleted file mode 100644 index 0b2db3b504..0000000000 --- a/modules/gdnative/xr/SCsub +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env python - -Import("env") -Import("env_gdnative") - -env_gdnative.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/gdnative/xr/xr_interface_gdnative.cpp b/modules/gdnative/xr/xr_interface_gdnative.cpp deleted file mode 100644 index e51542e23d..0000000000 --- a/modules/gdnative/xr/xr_interface_gdnative.cpp +++ /dev/null @@ -1,450 +0,0 @@ -/*************************************************************************/ -/* xr_interface_gdnative.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 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 "xr_interface_gdnative.h" -#include "core/input/input.h" -#include "servers/rendering/rendering_server_globals.h" -#include "servers/xr/xr_positional_tracker.h" - -void XRInterfaceGDNative::_bind_methods() { - ADD_PROPERTY_DEFAULT("interface_is_initialized", false); - ADD_PROPERTY_DEFAULT("ar_is_anchor_detection_enabled", false); -} - -XRInterfaceGDNative::XRInterfaceGDNative() { - print_verbose("Construct gdnative interface\n"); - - // we won't have our data pointer until our library gets set - data = nullptr; - - interface = nullptr; -} - -XRInterfaceGDNative::~XRInterfaceGDNative() { - print_verbose("Destruct gdnative interface\n"); - - if (interface != nullptr && is_initialized()) { - uninitialize(); - }; - - // cleanup after ourselves - cleanup(); -} - -void XRInterfaceGDNative::cleanup() { - if (interface != nullptr) { - interface->destructor(data); - data = nullptr; - interface = nullptr; - } -} - -void XRInterfaceGDNative::set_interface(const godot_xr_interface_gdnative *p_interface) { - // this should only be called once, just being paranoid.. - if (interface) { - cleanup(); - interface = NULL; - } - - // validate - ERR_FAIL_NULL(p_interface); - ERR_FAIL_COND_MSG(p_interface->version.major < 4, "This is an incompatible GDNative XR plugin."); - - // bind to our interface - interface = p_interface; - - // Now we do our constructing... - data = interface->constructor((godot_object *)this); -} - -StringName XRInterfaceGDNative::get_name() const { - ERR_FAIL_COND_V(interface == nullptr, StringName()); - - godot_string result = interface->get_name(data); - - StringName name = *(String *)&result; - - godot_string_destroy(&result); - - return name; -} - -int XRInterfaceGDNative::get_capabilities() const { - int capabilities; - - ERR_FAIL_COND_V(interface == nullptr, 0); // 0 = None - - capabilities = interface->get_capabilities(data); - - return capabilities; -} - -bool XRInterfaceGDNative::get_anchor_detection_is_enabled() const { - ERR_FAIL_COND_V(interface == nullptr, false); - - return interface->get_anchor_detection_is_enabled(data); -} - -void XRInterfaceGDNative::set_anchor_detection_is_enabled(bool p_enable) { - ERR_FAIL_COND(interface == nullptr); - - interface->set_anchor_detection_is_enabled(data, p_enable); -} - -int XRInterfaceGDNative::get_camera_feed_id() { - ERR_FAIL_COND_V(interface == nullptr, 0); - - return (unsigned int)interface->get_camera_feed_id(data); -} - -uint32_t XRInterfaceGDNative::get_view_count() { - uint32_t view_count; - - ERR_FAIL_COND_V(interface == nullptr, 1); - - view_count = interface->get_view_count(data); - - return view_count; -} - -bool XRInterfaceGDNative::is_initialized() const { - ERR_FAIL_COND_V(interface == nullptr, false); - - return interface->is_initialized(data); -} - -bool XRInterfaceGDNative::initialize() { - ERR_FAIL_COND_V(interface == nullptr, false); - - bool initialized = interface->initialize(data); - - if (initialized) { - // if we successfully initialize our interface and we don't have a primary interface yet, this becomes our primary interface - - XRServer *xr_server = XRServer::get_singleton(); - if ((xr_server != nullptr) && (xr_server->get_primary_interface() == nullptr)) { - xr_server->set_primary_interface(this); - }; - }; - - return initialized; -} - -void XRInterfaceGDNative::uninitialize() { - ERR_FAIL_COND(interface == nullptr); - - XRServer *xr_server = XRServer::get_singleton(); - if (xr_server != nullptr) { - // Whatever happens, make sure this is no longer our primary interface - xr_server->clear_primary_interface_if(this); - } - - interface->uninitialize(data); -} - -Size2 XRInterfaceGDNative::get_render_targetsize() { - ERR_FAIL_COND_V(interface == nullptr, Size2()); - - godot_vector2 result = interface->get_render_targetsize(data); - Vector2 *vec = (Vector2 *)&result; - - return *vec; -} - -Transform3D XRInterfaceGDNative::get_camera_transform() { - Transform3D *ret; - - ERR_FAIL_COND_V(interface == nullptr, Transform3D()); - - godot_transform3d t = interface->get_camera_transform(data); - - ret = (Transform3D *)&t; - - return *ret; -} - -Transform3D XRInterfaceGDNative::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) { - Transform3D *ret; - - ERR_FAIL_COND_V(interface == nullptr, Transform3D()); - - godot_transform3d t = interface->get_transform_for_view(data, (int)p_view, (godot_transform3d *)&p_cam_transform); - - ret = (Transform3D *)&t; - - return *ret; -} - -CameraMatrix XRInterfaceGDNative::get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) { - CameraMatrix cm; - - ERR_FAIL_COND_V(interface == nullptr, CameraMatrix()); - - interface->fill_projection_for_view(data, (godot_real_t *)cm.matrix, (godot_int)p_view, p_aspect, p_z_near, p_z_far); - - return cm; -} - -Vector<BlitToScreen> XRInterfaceGDNative::commit_views(RID p_render_target, const Rect2 &p_screen_rect) { - // possibly move this as a member variable and add a callback to populate? - Vector<BlitToScreen> blit_to_screen; - - ERR_FAIL_COND_V(interface == nullptr, blit_to_screen); - - // must implement - interface->commit_views(data, (godot_rid *)&p_render_target, (godot_rect2 *)&p_screen_rect); - - return blit_to_screen; -} - -unsigned int XRInterfaceGDNative::get_external_texture_for_eye(XRInterface::Eyes p_eye) { - ERR_FAIL_COND_V(interface == nullptr, 0); - - return (unsigned int)interface->get_external_texture_for_eye(data, (godot_int)p_eye); -} - -void XRInterfaceGDNative::commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) { - ERR_FAIL_COND(interface == nullptr); - - interface->commit_for_eye(data, (godot_int)p_eye, (godot_rid *)&p_render_target, (godot_rect2 *)&p_screen_rect); -} - -void XRInterfaceGDNative::process() { - ERR_FAIL_COND(interface == nullptr); - - interface->process(data); -} - -void XRInterfaceGDNative::notification(int p_what) { - ERR_FAIL_COND(interface == nullptr); - - interface->notification(data, p_what); -} - -///////////////////////////////////////////////////////////////////////////////////// -// some helper callbacks - -extern "C" { - -void GDAPI godot_xr_register_interface(const godot_xr_interface_gdnative *p_interface) { - // Must be on a version 4 plugin - ERR_FAIL_COND_MSG(p_interface->version.major < 4, "GDNative XR interfaces build for Godot 3.x are not supported."); - - Ref<XRInterfaceGDNative> new_interface; - new_interface.instantiate(); - new_interface->set_interface((const godot_xr_interface_gdnative *)p_interface); - XRServer::get_singleton()->add_interface(new_interface); -} - -godot_real_t GDAPI godot_xr_get_worldscale() { - XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL_V(xr_server, 1.0); - - return xr_server->get_world_scale(); -} - -godot_transform3d GDAPI godot_xr_get_reference_frame() { - godot_transform3d reference_frame; - Transform3D *reference_frame_ptr = (Transform3D *)&reference_frame; - - XRServer *xr_server = XRServer::get_singleton(); - if (xr_server != nullptr) { - *reference_frame_ptr = xr_server->get_reference_frame(); - } else { - memnew_placement(&reference_frame, Transform3D); - } - - return reference_frame; -} - -void GDAPI godot_xr_blit(godot_int p_eye, godot_rid *p_render_target, godot_rect2 *p_rect) { - // blits out our texture as is, handy for preview display of one of the eyes that is already rendered with lens distortion on an external HMD - XRInterface::Eyes eye = (XRInterface::Eyes)p_eye; -#if 0 - RID *render_target = (RID *)p_render_target; -#endif - Rect2 screen_rect = *(Rect2 *)p_rect; - - if (eye == XRInterface::EYE_LEFT) { - screen_rect.size.x /= 2.0; - } else if (p_eye == XRInterface::EYE_RIGHT) { - screen_rect.size.x /= 2.0; - screen_rect.position.x += screen_rect.size.x; - } -#ifndef _MSC_VER -#warning this needs to be redone -#endif -#if 0 - RSG::rasterizer->blit_render_target_to_screen(*render_target, screen_rect, 0); -#endif -} - -godot_int GDAPI godot_xr_get_texid(godot_rid *p_render_target) { - // In order to send off our textures to display on our hardware we need the opengl texture ID instead of the render target RID - // This is a handy function to expose that. -#if 0 - RID *render_target = (RID *)p_render_target; - - RID eye_texture = RSG::storage->render_target_get_texture(*render_target); -#endif - -#ifndef _MSC_VER -#warning need to obtain this ID again -#endif - uint32_t texid = 0; //RS::get_singleton()->texture_get_texid(eye_texture); - - return texid; -} - -godot_int GDAPI godot_xr_add_controller(char *p_device_name, godot_int p_hand, godot_bool p_tracks_orientation, godot_bool p_tracks_position) { - XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL_V(xr_server, 0); - - Input *input = Input::get_singleton(); - ERR_FAIL_NULL_V(input, 0); - - Ref<XRPositionalTracker> new_tracker; - new_tracker.instantiate(); - new_tracker->set_tracker_name(p_device_name); - new_tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER); - if (p_hand == 1) { - new_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_LEFT); - } else if (p_hand == 2) { - new_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_RIGHT); - } - - // also register as joystick... - int joyid = input->get_unused_joy_id(); - if (joyid != -1) { - new_tracker->set_joy_id(joyid); - input->joy_connection_changed(joyid, true, p_device_name, ""); - } - - if (p_tracks_orientation) { - Basis orientation; - new_tracker->set_orientation(orientation); - } - if (p_tracks_position) { - Vector3 position; - new_tracker->set_position(position); - } - - // add our tracker to our server and remember its pointer - xr_server->add_tracker(new_tracker); - - // note, this ID is only unique within controllers! - return new_tracker->get_tracker_id(); -} - -void GDAPI godot_xr_remove_controller(godot_int p_controller_id) { - XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL(xr_server); - - Input *input = Input::get_singleton(); - ERR_FAIL_NULL(input); - - Ref<XRPositionalTracker> remove_tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); - if (remove_tracker.is_valid()) { - // unset our joystick if applicable - int joyid = remove_tracker->get_joy_id(); - if (joyid != -1) { - input->joy_connection_changed(joyid, false, "", ""); - remove_tracker->set_joy_id(-1); - } - - // remove our tracker from our server - xr_server->remove_tracker(remove_tracker); - remove_tracker.unref(); - } -} - -void GDAPI godot_xr_set_controller_transform(godot_int p_controller_id, godot_transform3d *p_transform, godot_bool p_tracks_orientation, godot_bool p_tracks_position) { - XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL(xr_server); - - Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); - if (tracker.is_valid()) { - Transform3D *transform = (Transform3D *)p_transform; - if (p_tracks_orientation) { - tracker->set_orientation(transform->basis); - } - if (p_tracks_position) { - tracker->set_rw_position(transform->origin); - } - } -} - -void GDAPI godot_xr_set_controller_button(godot_int p_controller_id, godot_int p_button, godot_bool p_is_pressed) { - XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL(xr_server); - - Input *input = Input::get_singleton(); - ERR_FAIL_NULL(input); - - Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); - if (tracker.is_valid()) { - int joyid = tracker->get_joy_id(); - if (joyid != -1) { - input->joy_button(joyid, (JoyButton)p_button, p_is_pressed); - } - } -} - -void GDAPI godot_xr_set_controller_axis(godot_int p_controller_id, godot_int p_axis, godot_real_t p_value, godot_bool p_can_be_negative) { - XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL(xr_server); - - Input *input = Input::get_singleton(); - ERR_FAIL_NULL(input); - - Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); - if (tracker.is_valid()) { - int joyid = tracker->get_joy_id(); - if (joyid != -1) { - Input::JoyAxisValue jx; - jx.min = p_can_be_negative ? -1 : 0; - jx.value = p_value; - input->joy_axis(joyid, (JoyAxis)p_axis, jx); - } - } -} - -godot_real_t GDAPI godot_xr_get_controller_rumble(godot_int p_controller_id) { - XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL_V(xr_server, 0.0); - - Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); - if (tracker.is_valid()) { - return tracker->get_rumble(); - } - - return 0.0; -} -} diff --git a/modules/gdnative/xr/xr_interface_gdnative.h b/modules/gdnative/xr/xr_interface_gdnative.h deleted file mode 100644 index 42e9206c1f..0000000000 --- a/modules/gdnative/xr/xr_interface_gdnative.h +++ /dev/null @@ -1,95 +0,0 @@ -/*************************************************************************/ -/* xr_interface_gdnative.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 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 XR_INTERFACE_GDNATIVE_H -#define XR_INTERFACE_GDNATIVE_H - -#include "modules/gdnative/gdnative.h" -#include "servers/xr/xr_interface.h" - -/** - @authors Hinsbart & Karroffel & Mux213 - - This subclass of our AR/VR interface forms a bridge to GDNative. -*/ - -class XRInterfaceGDNative : public XRInterface { - GDCLASS(XRInterfaceGDNative, XRInterface); - - void cleanup(); - -protected: - const godot_xr_interface_gdnative *interface; - void *data; - - static void _bind_methods(); - -public: - /** general interface information **/ - XRInterfaceGDNative(); - ~XRInterfaceGDNative(); - - void set_interface(const godot_xr_interface_gdnative *p_interface); - - virtual StringName get_name() const override; - virtual int get_capabilities() const override; - - virtual bool is_initialized() const override; - virtual bool initialize() override; - virtual void uninitialize() override; - - /** specific to AR **/ - virtual bool get_anchor_detection_is_enabled() const override; - virtual void set_anchor_detection_is_enabled(bool p_enable) override; - virtual int get_camera_feed_id() override; - - /** rendering and internal **/ - virtual Size2 get_render_targetsize() override; - virtual uint32_t get_view_count() override; - virtual Transform3D get_camera_transform() override; - virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override; - - // we expose a Vector<float> version of this function to GDNative - Vector<float> _get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far); - - // and a CameraMatrix version to XRServer - virtual CameraMatrix get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) override; - - virtual Vector<BlitToScreen> commit_views(RID p_render_target, const Rect2 &p_screen_rect) override; - - virtual void process() override; - virtual void notification(int p_what) override; - - // deprecated - virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override; - virtual unsigned int get_external_texture_for_eye(XRInterface::Eyes p_eye) override; -}; - -#endif // XR_INTERFACE_GDNATIVE_H diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index ed8b0a4690..ab441d194a 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -45,7 +45,7 @@ static bool _is_bin_symbol(char32_t c) { return (c == '0' || c == '1'); } -Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line) { +Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_line) { Dictionary color_map; Type next_type = NONE; @@ -449,25 +449,21 @@ void GDScriptSyntaxHighlighter::_update_cache() { color_region_cache.clear(); font_color = text_edit->get_theme_color(SNAME("font_color")); - symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color"); - function_color = EDITOR_GET("text_editor/highlighting/function_color"); - number_color = EDITOR_GET("text_editor/highlighting/number_color"); - member_color = EDITOR_GET("text_editor/highlighting/member_variable_color"); + symbol_color = EDITOR_GET("text_editor/theme/highlighting/symbol_color"); + function_color = EDITOR_GET("text_editor/theme/highlighting/function_color"); + number_color = EDITOR_GET("text_editor/theme/highlighting/number_color"); + member_color = EDITOR_GET("text_editor/theme/highlighting/member_variable_color"); /* Engine types. */ - const Color types_color = EDITOR_GET("text_editor/highlighting/engine_type_color"); + const Color types_color = EDITOR_GET("text_editor/theme/highlighting/engine_type_color"); List<StringName> types; ClassDB::get_class_list(&types); for (const StringName &E : types) { - String n = E; - if (n.begins_with("_")) { - n = n.substr(1, n.length()); - } - keywords[n] = types_color; + keywords[E] = types_color; } /* User types. */ - const Color usertype_color = EDITOR_GET("text_editor/highlighting/user_type_color"); + const Color usertype_color = EDITOR_GET("text_editor/theme/highlighting/user_type_color"); List<StringName> global_classes; ScriptServer::get_global_class_list(&global_classes); for (const StringName &E : global_classes) { @@ -475,9 +471,9 @@ void GDScriptSyntaxHighlighter::_update_cache() { } /* Autoloads. */ - Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); - for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) { - const ProjectSettings::AutoloadInfo &info = E->value(); + OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); + for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { + const ProjectSettings::AutoloadInfo &info = E.value(); if (info.is_singleton) { keywords[info.name] = usertype_color; } @@ -486,7 +482,7 @@ void GDScriptSyntaxHighlighter::_update_cache() { const GDScriptLanguage *gdscript = GDScriptLanguage::get_singleton(); /* Core types. */ - const Color basetype_color = EDITOR_GET("text_editor/highlighting/base_type_color"); + const Color basetype_color = EDITOR_GET("text_editor/theme/highlighting/base_type_color"); List<String> core_types; gdscript->get_core_type_words(&core_types); for (const String &E : core_types) { @@ -494,8 +490,8 @@ void GDScriptSyntaxHighlighter::_update_cache() { } /* Reserved words. */ - const Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color"); - const Color control_flow_keyword_color = EDITOR_GET("text_editor/highlighting/control_flow_keyword_color"); + const Color keyword_color = EDITOR_GET("text_editor/theme/highlighting/keyword_color"); + const Color control_flow_keyword_color = EDITOR_GET("text_editor/theme/highlighting/control_flow_keyword_color"); List<String> keyword_list; gdscript->get_reserved_words(&keyword_list); for (const String &E : keyword_list) { @@ -507,7 +503,7 @@ void GDScriptSyntaxHighlighter::_update_cache() { } /* Comments */ - const Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color"); + const Color comment_color = EDITOR_GET("text_editor/theme/highlighting/comment_color"); List<String> comments; gdscript->get_comment_delimiters(&comments); for (const String &comment : comments) { @@ -517,7 +513,7 @@ void GDScriptSyntaxHighlighter::_update_cache() { } /* Strings */ - const Color string_color = EDITOR_GET("text_editor/highlighting/string_color"); + const Color string_color = EDITOR_GET("text_editor/theme/highlighting/string_color"); List<String> strings; gdscript->get_string_delimiters(&strings); for (const String &string : strings) { @@ -529,7 +525,7 @@ void GDScriptSyntaxHighlighter::_update_cache() { const Ref<Script> script = _get_edited_resource(); if (script.is_valid()) { /* Member types. */ - const Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color"); + const Color member_variable_color = EDITOR_GET("text_editor/theme/highlighting/member_variable_color"); StringName instance_base = script->get_instance_base_type(); if (instance_base != StringName()) { List<PropertyInfo> plist; @@ -566,28 +562,28 @@ void GDScriptSyntaxHighlighter::_update_cache() { annotation_color = Color(0.8, 0.5, 0.25); } - EDITOR_DEF("text_editor/highlighting/gdscript/function_definition_color", function_definition_color); - EDITOR_DEF("text_editor/highlighting/gdscript/node_path_color", node_path_color); - EDITOR_DEF("text_editor/highlighting/gdscript/annotation_color", annotation_color); + EDITOR_DEF("text_editor/theme/highlighting/gdscript/function_definition_color", function_definition_color); + EDITOR_DEF("text_editor/theme/highlighting/gdscript/node_path_color", node_path_color); + EDITOR_DEF("text_editor/theme/highlighting/gdscript/annotation_color", annotation_color); if (text_edit_color_theme == "Default" || godot_2_theme) { EditorSettings::get_singleton()->set_initial_value( - "text_editor/highlighting/gdscript/function_definition_color", + "text_editor/theme/highlighting/gdscript/function_definition_color", function_definition_color, true); EditorSettings::get_singleton()->set_initial_value( - "text_editor/highlighting/gdscript/node_path_color", + "text_editor/theme/highlighting/gdscript/node_path_color", node_path_color, true); EditorSettings::get_singleton()->set_initial_value( - "text_editor/highlighting/gdscript/annotation_color", + "text_editor/theme/highlighting/gdscript/annotation_color", annotation_color, true); } - function_definition_color = EDITOR_GET("text_editor/highlighting/gdscript/function_definition_color"); - node_path_color = EDITOR_GET("text_editor/highlighting/gdscript/node_path_color"); - annotation_color = EDITOR_GET("text_editor/highlighting/gdscript/annotation_color"); - type_color = EDITOR_GET("text_editor/highlighting/base_type_color"); + function_definition_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/function_definition_color"); + node_path_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/node_path_color"); + annotation_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/annotation_color"); + type_color = EDITOR_GET("text_editor/theme/highlighting/base_type_color"); } void GDScriptSyntaxHighlighter::add_color_region(const String &p_start_key, const String &p_end_key, const Color &p_color, bool p_line_only) { diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h index d07c182aa6..fabd64dab8 100644 --- a/modules/gdscript/editor/gdscript_highlighter.h +++ b/modules/gdscript/editor/gdscript_highlighter.h @@ -80,7 +80,7 @@ private: public: virtual void _update_cache() override; - virtual Dictionary _get_line_syntax_highlighting(int p_line) override; + virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) override; virtual String _get_name() const override; virtual Array _get_supported_languages() const override; diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 8957b00a1b..7943cf7469 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -625,9 +625,9 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc String path = ""; if (String(c->extends_path) != "" && String(c->extends_path) != get_path()) { path = c->extends_path; - if (path.is_rel_path()) { + if (path.is_relative_path()) { String base = get_path(); - if (base == "" || base.is_rel_path()) { + if (base == "" || base.is_relative_path()) { ERR_PRINT(("Could not resolve relative path for parent class: " + path).utf8().get_data()); } else { path = base.get_base_dir().plus_file(path); @@ -1644,16 +1644,11 @@ void GDScriptLanguage::init() { List<StringName> class_list; ClassDB::get_class_list(&class_list); for (const StringName &n : class_list) { - String s = String(n); - if (s.begins_with("_")) { - s = s.substr(1, s.length()); - } - - if (globals.has(s)) { + if (globals.has(n)) { continue; } Ref<GDScriptNativeClass> nc = memnew(GDScriptNativeClass(n)); - _add_global(s, nc); + _add_global(n, nc); } //populate singletons @@ -2064,7 +2059,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b if (r_icon_path) { if (c->icon_path.is_empty() || c->icon_path.is_absolute_path()) { *r_icon_path = c->icon_path; - } else if (c->icon_path.is_rel_path()) { + } else if (c->icon_path.is_relative_path()) { *r_icon_path = p_path.get_base_dir().plus_file(c->icon_path).simplify_path(); } } @@ -2092,7 +2087,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b break; } String subpath = subclass->extends_path; - if (subpath.is_rel_path()) { + if (subpath.is_relative_path()) { subpath = path.get_base_dir().plus_file(subpath).simplify_path(); } diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index ab37e54cf1..6f76ca05dc 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -30,6 +30,7 @@ #include "gdscript_analyzer.h" +#include "core/config/engine.h" #include "core/config/project_settings.h" #include "core/io/file_access.h" #include "core/io/resource_loader.h" @@ -112,11 +113,10 @@ static GDScriptParser::DataType make_native_enum_type(const StringName &p_native type.is_meta_type = true; List<StringName> enum_values; - StringName real_native_name = GDScriptParser::get_real_class_name(p_native_class); - ClassDB::get_enum_constants(real_native_name, p_enum_name, &enum_values); + ClassDB::get_enum_constants(p_native_class, p_enum_name, &enum_values); for (const StringName &E : enum_values) { - type.enum_values[E] = ClassDB::get_integer_constant(real_native_name, E); + type.enum_values[E] = ClassDB::get_integer_constant(p_native_class, E); } return type; @@ -229,7 +229,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), p_class); return err; } - } else if (class_exists(name) && ClassDB::can_instantiate(GDScriptParser::get_real_class_name(name))) { + } else if (class_exists(name) && ClassDB::can_instantiate(name)) { base.kind = GDScriptParser::DataType::NATIVE; base.native_type = name; } else { @@ -406,7 +406,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type return GDScriptParser::DataType(); } result = ref->get_parser()->head->get_datatype(); - } else if (ClassDB::has_enum(GDScriptParser::get_real_class_name(parser->current_class->base_type.native_type), first)) { + } else if (ClassDB::has_enum(parser->current_class->base_type.native_type, first)) { // Native enum in current class. result = make_native_enum_type(parser->current_class->base_type.native_type, first); } else { @@ -433,8 +433,28 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type case GDScriptParser::ClassNode::Member::CONSTANT: if (member.constant->get_datatype().is_meta_type) { result = member.constant->get_datatype(); + result.is_meta_type = false; found = true; break; + } else if (Ref<Script>(member.constant->initializer->reduced_value).is_valid()) { + Ref<GDScript> gdscript = member.constant->initializer->reduced_value; + if (gdscript.is_valid()) { + Ref<GDScriptParserRef> ref = get_parser_for(gdscript->get_path()); + if (ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED) != OK) { + push_error(vformat(R"(Could not parse script from "%s".)", gdscript->get_path()), p_type); + return GDScriptParser::DataType(); + } + result = ref->get_parser()->head->get_datatype(); + result.is_meta_type = false; + } else { + Ref<GDScript> script = member.constant->initializer->reduced_value; + result.kind = GDScriptParser::DataType::SCRIPT; + result.builtin_type = Variant::OBJECT; + result.script_type = script; + result.script_path = script->get_path(); + result.native_type = script->get_instance_base_type(); + } + break; } [[fallthrough]]; default: @@ -469,7 +489,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type } } else if (result.kind == GDScriptParser::DataType::NATIVE) { // Only enums allowed for native. - if (ClassDB::has_enum(GDScriptParser::get_real_class_name(result.native_type), p_type->type_chain[1]->name)) { + if (ClassDB::has_enum(result.native_type, p_type->type_chain[1]->name)) { if (p_type->type_chain.size() > 2) { push_error(R"(Enums cannot contain nested types.)", p_type->type_chain[2]); } else { @@ -2130,6 +2150,9 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa if (is_self && parser->current_function != nullptr && parser->current_function->is_static && !is_static) { push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parser->current_function->identifier->name), p_call->callee); + } else if (!is_self && base_type.is_meta_type && !is_static) { + base_type.is_meta_type = false; // For `to_string()`. + push_error(vformat(R"*(Cannot call non-static function "%s()" on the class "%s" directly. Make an instance instead.)*", p_call->function_name, base_type.to_string()), p_call->callee); } else if (is_self && !is_static && !lambda_stack.is_empty()) { push_error(vformat(R"*(Cannot call non-static function "%s()" from a lambda function.)*", p_call->function_name), p_call->callee); } @@ -2252,7 +2275,7 @@ void GDScriptAnalyzer::reduce_get_node(GDScriptParser::GetNodeNode *p_get_node) result.native_type = "Node"; result.builtin_type = Variant::OBJECT; - if (!ClassDB::is_parent_class(GDScriptParser::get_real_class_name(parser->current_class->base_type.native_type), result.native_type)) { + if (!ClassDB::is_parent_class(parser->current_class->base_type.native_type, result.native_type)) { push_error(R"*(Cannot use shorthand "get_node()" notation ("$") on a class that isn't a node.)*", p_get_node); } else if (!lambda_stack.is_empty()) { push_error(R"*(Cannot use shorthand "get_node()" notation ("$") inside a lambda. Use a captured variable instead.)*", p_get_node); @@ -2393,6 +2416,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod resolve_function_signature(member.function); p_identifier->set_datatype(make_callable_type(member.function->info)); break; + case GDScriptParser::ClassNode::Member::CLASS: + // For out-of-order resolution: + resolve_class_interface(member.m_class); + p_identifier->set_datatype(member.m_class->get_datatype()); + break; default: break; // Type already set. } @@ -2421,7 +2449,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod } // Check native members. - const StringName &native = GDScriptParser::get_real_class_name(base.native_type); + const StringName &native = base.native_type; if (class_exists(native)) { PropertyInfo prop_info; @@ -2699,7 +2727,7 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) { } else { p_preload->resolved_path = p_preload->path->reduced_value; // TODO: Save this as script dependency. - if (p_preload->resolved_path.is_rel_path()) { + if (p_preload->resolved_path.is_relative_path()) { p_preload->resolved_path = parser->script_path.get_base_dir().plus_file(p_preload->resolved_path); } p_preload->resolved_path = p_preload->resolved_path.simplify_path(); @@ -3202,7 +3230,7 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo return result; } -bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, GDScriptParser::DataType p_base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) { +bool GDScriptAnalyzer::get_function_signature(GDScriptParser::CallNode *p_source, GDScriptParser::DataType p_base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) { r_static = false; r_vararg = false; r_default_arg_count = 0; @@ -3221,14 +3249,16 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, GD for (const MethodInfo &E : methods) { if (E.name == p_function) { - return function_signature_from_info(E, r_return_type, r_par_types, r_default_arg_count, r_static, r_vararg); + function_signature_from_info(E, r_return_type, r_par_types, r_default_arg_count, r_static, r_vararg); + r_static = Variant::is_builtin_method_static(p_base_type.builtin_type, function_name); + return true; } } return false; } - bool is_constructor = p_base_type.is_meta_type && p_function == "new"; + bool is_constructor = (p_base_type.is_meta_type || (p_source->callee && p_source->callee->type == GDScriptParser::Node::IDENTIFIER)) && p_function == StaticCString::create("new"); if (is_constructor) { function_name = "_init"; r_static = true; @@ -3258,6 +3288,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, GD } } r_return_type = found_function->get_datatype(); + r_return_type.is_meta_type = false; r_return_type.is_coroutine = found_function->is_coroutine; return true; @@ -3303,11 +3334,13 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, GD return true; } - StringName real_native = GDScriptParser::get_real_class_name(base_native); - MethodInfo info; - if (ClassDB::get_method_info(real_native, function_name, &info)) { - return function_signature_from_info(info, r_return_type, r_par_types, r_default_arg_count, r_static, r_vararg); + if (ClassDB::get_method_info(base_native, function_name, &info)) { + bool valid = function_signature_from_info(info, r_return_type, r_par_types, r_default_arg_count, r_static, r_vararg); + if (valid && Engine::get_singleton()->has_singleton(base_native)) { + r_static = true; + } + return valid; } return false; @@ -3398,24 +3431,23 @@ bool GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_local, con StringName parent = base_native; while (parent != StringName()) { - StringName real_class_name = GDScriptParser::get_real_class_name(parent); - if (ClassDB::has_method(real_class_name, name, true)) { + if (ClassDB::has_method(parent, name, true)) { parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "method", parent); return true; - } else if (ClassDB::has_signal(real_class_name, name, true)) { + } else if (ClassDB::has_signal(parent, name, true)) { parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "signal", parent); return true; - } else if (ClassDB::has_property(real_class_name, name, true)) { + } else if (ClassDB::has_property(parent, name, true)) { parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "property", parent); return true; - } else if (ClassDB::has_integer_constant(real_class_name, name, true)) { + } else if (ClassDB::has_integer_constant(parent, name, true)) { parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "constant", parent); return true; - } else if (ClassDB::has_enum(real_class_name, name, true)) { + } else if (ClassDB::has_enum(parent, name, true)) { parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "enum", parent); return true; } - parent = ClassDB::get_parent_class(real_class_name); + parent = ClassDB::get_parent_class(parent); } return false; @@ -3560,16 +3592,12 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ break; // Already solved before. } - // Get underscore-prefixed version for some classes. - src_native = GDScriptParser::get_real_class_name(src_native); - switch (p_target.kind) { case GDScriptParser::DataType::NATIVE: { if (p_target.is_meta_type) { return ClassDB::is_parent_class(src_native, GDScriptNativeClass::get_class_static()); } - StringName tgt_native = GDScriptParser::get_real_class_name(p_target.native_type); - return ClassDB::is_parent_class(src_native, tgt_native); + return ClassDB::is_parent_class(src_native, p_target.native_type); } case GDScriptParser::DataType::SCRIPT: if (p_target.is_meta_type) { @@ -3618,8 +3646,7 @@ void GDScriptAnalyzer::mark_node_unsafe(const GDScriptParser::Node *p_node) { } bool GDScriptAnalyzer::class_exists(const StringName &p_class) const { - StringName real_name = GDScriptParser::get_real_class_name(p_class); - return ClassDB::class_exists(real_name) && ClassDB::is_class_exposed(real_name); + return ClassDB::class_exists(p_class) && ClassDB::is_class_exposed(p_class); } Ref<GDScriptParserRef> GDScriptAnalyzer::get_parser_for(const String &p_path) { diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index 8cd3fcf837..32bf049fa1 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -99,7 +99,7 @@ class GDScriptAnalyzer { GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type) const; GDScriptParser::DataType type_from_property(const PropertyInfo &p_property) const; GDScriptParser::DataType make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source); - bool get_function_signature(GDScriptParser::Node *p_source, GDScriptParser::DataType base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg); + bool get_function_signature(GDScriptParser::CallNode *p_source, GDScriptParser::DataType base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg); bool function_signature_from_info(const MethodInfo &p_info, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg); 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); diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp index 1a844bf241..07f50d14dc 100644 --- a/modules/gdscript/gdscript_cache.cpp +++ b/modules/gdscript/gdscript_cache.cpp @@ -200,7 +200,9 @@ Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_erro if (singleton->full_gdscript_cache.has(p_path)) { return singleton->full_gdscript_cache[p_path]; } + Ref<GDScript> script = get_shallow_script(p_path); + ERR_FAIL_COND_V(script.is_null(), Ref<GDScript>()); r_error = script->load_source_code(p_path); diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 7c9d08b782..ddf4f281b4 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -107,7 +107,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D // Locate class by constructing the path to it and following that path GDScriptParser::ClassNode *class_type = p_datatype.class_type; if (class_type) { - if (class_type->fqcn.begins_with(main_script->path) || (!main_script->name.is_empty() && class_type->fqcn.begins_with(main_script->name))) { + if ((!main_script->path.is_empty() && class_type->fqcn.begins_with(main_script->path)) || (!main_script->name.is_empty() && class_type->fqcn.begins_with(main_script->name))) { // Local class. List<StringName> names; while (class_type->outer) { @@ -126,8 +126,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D names.pop_back(); } result.kind = GDScriptDataType::GDSCRIPT; - result.script_type_ref = script; - result.script_type = result.script_type_ref.ptr(); + result.script_type = script.ptr(); result.native_type = script->get_instance_base_type(); } else { result.kind = GDScriptDataType::GDSCRIPT; diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 2e570d5a5b..70e18c6e6c 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -623,11 +623,11 @@ static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_functio } static void _get_directory_contents(EditorFileSystemDirectory *p_dir, Map<String, ScriptCodeCompletionOption> &r_list) { - const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\""; + const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\""; for (int i = 0; i < p_dir->get_file_count(); i++) { ScriptCodeCompletionOption option(p_dir->get_file_path(i), ScriptCodeCompletionOption::KIND_FILE_PATH); - option.insert_text = quote_style + option.display + quote_style; + option.insert_text = option.display.quote(quote_style); r_list.insert(option.display, option); } @@ -641,20 +641,20 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a if (p_argument == 3 || p_argument == 4) { // Slider hint. ScriptCodeCompletionOption slider1("or_greater", ScriptCodeCompletionOption::KIND_PLAIN_TEXT); - slider1.insert_text = p_quote_style + slider1.display + p_quote_style; + slider1.insert_text = slider1.display.quote(p_quote_style); r_result.insert(slider1.display, slider1); ScriptCodeCompletionOption slider2("or_lesser", ScriptCodeCompletionOption::KIND_PLAIN_TEXT); - slider2.insert_text = p_quote_style + slider2.display + p_quote_style; + slider2.insert_text = slider2.display.quote(p_quote_style); r_result.insert(slider2.display, slider2); } } else if (p_annotation->name == "@export_exp_easing") { if (p_argument == 0 || p_argument == 1) { // Easing hint. ScriptCodeCompletionOption hint1("attenuation", ScriptCodeCompletionOption::KIND_PLAIN_TEXT); - hint1.insert_text = p_quote_style + hint1.display + p_quote_style; + hint1.insert_text = hint1.display.quote(p_quote_style); r_result.insert(hint1.display, hint1); ScriptCodeCompletionOption hint2("inout", ScriptCodeCompletionOption::KIND_PLAIN_TEXT); - hint2.insert_text = p_quote_style + hint2.display + p_quote_style; + hint2.insert_text = hint2.display.quote(p_quote_style); r_result.insert(hint2.display, hint2); } } else if (p_annotation->name == "@export_node_path") { @@ -731,9 +731,9 @@ static void _list_available_types(bool p_inherit_only, GDScriptParser::Completio } // Autoload singletons - 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()) { - const ProjectSettings::AutoloadInfo &info = E->get(); + OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); + for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { + const ProjectSettings::AutoloadInfo &info = E.get(); if (!info.is_singleton || info.path.get_extension().to_lower() != "gd") { continue; } @@ -912,7 +912,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base } } break; case GDScriptParser::DataType::NATIVE: { - StringName type = GDScriptParser::get_real_class_name(base_type.native_type); + StringName type = base_type.native_type; if (!ClassDB::class_exists(type)) { return; } @@ -1086,12 +1086,12 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool 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) { + OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); + for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { + if (!E.value().is_singleton) { continue; } - ScriptCodeCompletionOption option(E->key(), ScriptCodeCompletionOption::KIND_CONSTANT); + ScriptCodeCompletionOption option(E.key(), ScriptCodeCompletionOption::KIND_CONSTANT); r_result.insert(option.display, option); } @@ -1326,10 +1326,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, native_type.kind = GDScriptParser::DataType::NATIVE; native_type.native_type = native_type.script_type->get_instance_base_type(); if (!ClassDB::class_exists(native_type.native_type)) { - native_type.native_type = String("_") + native_type.native_type; - if (!ClassDB::class_exists(native_type.native_type)) { - native_type.kind = GDScriptParser::DataType::UNRESOLVED; - } + native_type.kind = GDScriptParser::DataType::UNRESOLVED; } } } @@ -1362,12 +1359,12 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[which]); found = true; } else { - Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); + OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); - for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) { - String name = E->key(); + for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { + String name = E.key(); if (name == which) { - String script = E->value().path; + String script = E.value().path; if (!script.begins_with("res://")) { script = "res://" + script; @@ -1765,9 +1762,8 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, base_type = GDScriptParser::DataType(); break; } - StringName real_native = GDScriptParser::get_real_class_name(base_type.native_type); MethodInfo info; - if (ClassDB::get_method_info(real_native, p_context.current_function->identifier->name, &info)) { + if (ClassDB::get_method_info(base_type.native_type, p_context.current_function->identifier->name, &info)) { for (const PropertyInfo &E : info.arguments) { if (E.name == p_identifier) { r_type = _type_from_property(E); @@ -1836,13 +1832,12 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, } // Check ClassDB. - StringName class_name = GDScriptParser::get_real_class_name(p_identifier); - if (ClassDB::class_exists(class_name) && ClassDB::is_class_exposed(class_name)) { + if (ClassDB::class_exists(p_identifier) && ClassDB::is_class_exposed(p_identifier)) { r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; r_type.type.kind = GDScriptParser::DataType::NATIVE; r_type.type.native_type = p_identifier; r_type.type.is_constant = true; - r_type.type.is_meta_type = !Engine::get_singleton()->has_singleton(class_name); + r_type.type.is_meta_type = !Engine::get_singleton()->has_singleton(p_identifier); r_type.value = Variant(); } @@ -1951,7 +1946,7 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext & } } break; case GDScriptParser::DataType::NATIVE: { - StringName class_name = GDScriptParser::get_real_class_name(base_type.native_type); + StringName class_name = base_type.native_type; if (!ClassDB::class_exists(class_name)) { return false; } @@ -2113,11 +2108,10 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex } } break; case GDScriptParser::DataType::NATIVE: { - StringName native = GDScriptParser::get_real_class_name(base_type.native_type); - if (!ClassDB::class_exists(native)) { + if (!ClassDB::class_exists(base_type.native_type)) { return false; } - MethodBind *mb = ClassDB::get_method(native, p_method); + MethodBind *mb = ClassDB::get_method(base_type.native_type, p_method); if (mb) { r_type = _type_from_property(mb->get_return_info()); return true; @@ -2192,7 +2186,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c Variant base = p_base.value; GDScriptParser::DataType base_type = p_base.type; - const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\""; + const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\""; while (base_type.is_set() && !base_type.is_variant()) { switch (base_type.kind) { @@ -2209,7 +2203,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c base_type = base_type.class_type->base_type; } break; case GDScriptParser::DataType::NATIVE: { - StringName class_name = GDScriptParser::get_real_class_name(base_type.native_type); + StringName class_name = base_type.native_type; if (!ClassDB::class_exists(class_name)) { base_type.kind = GDScriptParser::DataType::UNRESOLVED; break; @@ -2254,7 +2248,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c } String name = s.get_slice("/", 1); ScriptCodeCompletionOption option("/root/" + name, ScriptCodeCompletionOption::KIND_NODE_PATH); - option.insert_text = quote_style + option.display + quote_style; + option.insert_text = option.display.quote(quote_style); r_result.insert(option.display, option); } } @@ -2270,7 +2264,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c } String name = s.get_slice("/", 1); ScriptCodeCompletionOption option(name, ScriptCodeCompletionOption::KIND_CONSTANT); - option.insert_text = quote_style + option.display + quote_style; + option.insert_text = option.display.quote(quote_style); r_result.insert(option.display, option); } } @@ -2305,8 +2299,6 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c } static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, const GDScriptParser::Node *p_call, int p_argidx, Map<String, ScriptCodeCompletionOption> &r_result, bool &r_forced, String &r_arghint) { - const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\""; - if (p_call->type == GDScriptParser::Node::PRELOAD) { if (p_argidx == 0 && bool(EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths"))) { _get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), r_result); @@ -2382,7 +2374,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c } ::Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint) { - const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\""; + const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\""; GDScriptParser parser; GDScriptAnalyzer analyzer(&parser); @@ -2594,7 +2586,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c break; } - StringName class_name = GDScriptParser::get_real_class_name(native_type.native_type); + StringName class_name = native_type.native_type; if (!ClassDB::class_exists(class_name)) { break; } @@ -2668,11 +2660,11 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c } // Get autoloads. - Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); + OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); - for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) { - String name = E->key(); - ScriptCodeCompletionOption option(quote_style + "/root/" + name + quote_style, ScriptCodeCompletionOption::KIND_NODE_PATH); + for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { + String path = "/root/" + E.key(); + ScriptCodeCompletionOption option(path.quote(quote_style), ScriptCodeCompletionOption::KIND_NODE_PATH); options.insert(option.display, option); } } @@ -2705,10 +2697,10 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path String GDScriptLanguage::_get_indentation() const { #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { - bool use_space_indentation = EDITOR_DEF("text_editor/indent/type", false); + bool use_space_indentation = EDITOR_DEF("text_editor/behavior/indent/type", false); if (use_space_indentation) { - int indent_size = EDITOR_DEF("text_editor/indent/size", 4); + int indent_size = EDITOR_DEF("text_editor/behavior/indent/size", 4); String space_indent = ""; for (int i = 0; i < indent_size; i++) { @@ -2823,7 +2815,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co } } break; case GDScriptParser::DataType::NATIVE: { - StringName class_name = GDScriptParser::get_real_class_name(base_type.native_type); + StringName class_name = base_type.native_type; if (!ClassDB::class_exists(class_name)) { base_type.kind = GDScriptParser::DataType::UNRESOLVED; break; @@ -2875,11 +2867,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co StringName parent = ClassDB::get_parent_class(class_name); if (parent != StringName()) { - if (String(parent).begins_with("_")) { - base_type.native_type = String(parent).substr(1); - } else { - base_type.native_type = parent; - } + base_type.native_type = parent; } else { base_type.kind = GDScriptParser::DataType::UNRESOLVED; } @@ -2933,18 +2921,11 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co } ::Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) { - //before parsing, try the usual stuff + // Before parsing, try the usual stuff if (ClassDB::class_exists(p_symbol)) { r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS; r_result.class_name = p_symbol; return OK; - } else { - String under_prefix = "_" + p_symbol; - if (ClassDB::class_exists(under_prefix)) { - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS; - r_result.class_name = p_symbol; - return OK; - } } for (int i = 0; i < Variant::VARIANT_MAX; i++) { diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index 9e5ef0f632..87d8c03494 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -111,11 +111,7 @@ public: } if (!ClassDB::is_parent_class(obj->get_class_name(), native_type)) { - // Try with underscore prefix - StringName underscore_native_type = "_" + native_type; - if (!ClassDB::is_parent_class(obj->get_class_name(), underscore_native_type)) { - return false; - } + return false; } return true; } break; diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index a500dfd51a..f4c721e00a 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -94,43 +94,8 @@ Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) { return Variant::VARIANT_MAX; } -// TODO: Move this to a central location (maybe core?). -static HashMap<StringName, StringName> underscore_map; -static const char *underscore_classes[] = { - "ClassDB", - "Directory", - "Engine", - "File", - "Geometry", - "GodotSharp", - "JSON", - "Marshalls", - "Mutex", - "OS", - "ResourceLoader", - "ResourceSaver", - "Semaphore", - "Thread", - "VisualScriptEditor", - nullptr, -}; -StringName GDScriptParser::get_real_class_name(const StringName &p_source) { - if (underscore_map.is_empty()) { - const char **class_name = underscore_classes; - while (*class_name != nullptr) { - underscore_map[*class_name] = String("_") + *class_name; - class_name++; - } - } - if (underscore_map.has(p_source)) { - return underscore_map[p_source]; - } - return p_source; -} - void GDScriptParser::cleanup() { builtin_types.clear(); - underscore_map.clear(); } void GDScriptParser::get_annotation_list(List<MethodInfo> *r_annotations) const { @@ -168,7 +133,7 @@ GDScriptParser::GDScriptParser() { register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>); register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>); // Networking. - register_annotation(MethodInfo("@rpc", { Variant::STRING, "mode" }, { Variant::STRING, "sync" }, { Variant::STRING, "transfer_mode" }, { Variant::INT, "transfer_channel" }), AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_PUPPET>, 4, true); + register_annotation(MethodInfo("@rpc", { Variant::STRING, "mode" }, { Variant::STRING, "sync" }, { Variant::STRING, "transfer_mode" }, { Variant::INT, "transfer_channel" }), AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_AUTHORITY>, 4, true); // TODO: Warning annotations. } @@ -337,7 +302,7 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_ int tab_size = 4; #ifdef TOOLS_ENABLED if (EditorSettings::get_singleton()) { - tab_size = EditorSettings::get_singleton()->get_setting("text_editor/indent/size"); + tab_size = EditorSettings::get_singleton()->get_setting("text_editor/behavior/indent/size"); } #endif // TOOLS_ENABLED @@ -1152,7 +1117,6 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() { } item.custom_value = value; } - item.rightmost_column = previous.rightmost_column; item.index = enum_node->values.size(); enum_node->values.push_back(item); @@ -2401,6 +2365,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode } assignment->assignee = p_previous_operand; assignment->assigned_value = parse_expression(false); + if (assignment->assigned_value == nullptr) { + push_error(R"(Expected an expression after "=".)"); + } #ifdef DEBUG_ENABLED if (has_operator && source_variable != nullptr && source_variable->assignments == 0) { @@ -2413,7 +2380,11 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode GDScriptParser::ExpressionNode *GDScriptParser::parse_await(ExpressionNode *p_previous_operand, bool p_can_assign) { AwaitNode *await = alloc_node<AwaitNode>(); - await->to_await = parse_precedence(PREC_AWAIT, false); + ExpressionNode *element = parse_precedence(PREC_AWAIT, false); + if (element == nullptr) { + push_error(R"(Expected signal or coroutine after "await".)"); + } + await->to_await = element; current_function->is_coroutine = true; @@ -2480,7 +2451,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_dictionary(ExpressionNode switch (dictionary->style) { case DictionaryNode::LUA_TABLE: if (key != nullptr && key->type != Node::IDENTIFIER) { - push_error("Expected identifier as dictionary key."); + push_error("Expected identifier as LUA-style dictionary key."); + advance(); + break; } if (!match(GDScriptTokenizer::Token::EQUAL)) { if (match(GDScriptTokenizer::Token::COLON)) { @@ -2984,7 +2957,7 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String & } else { /* Syntax: - @tutorial ( The Title Here ) : http://the.url/ + @tutorial ( The Title Here ) : https://the.url/ ^ open ^ close ^ colon ^ url */ int open_bracket_pos = begin_scan, close_bracket_pos = 0; @@ -3361,10 +3334,10 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node variable->export_info.hint_string = Variant::get_type_name(export_type.builtin_type); break; case GDScriptParser::DataType::NATIVE: - if (ClassDB::is_parent_class(get_real_class_name(export_type.native_type), "Resource")) { + if (ClassDB::is_parent_class(export_type.native_type, "Resource")) { variable->export_info.type = Variant::OBJECT; variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE; - variable->export_info.hint_string = get_real_class_name(export_type.native_type); + variable->export_info.hint_string = export_type.native_type; } else { push_error(R"(Export type can only be built-in, a resource, or an enum.)", variable); return false; @@ -3426,43 +3399,35 @@ bool GDScriptParser::network_annotations(const AnnotationNode *p_annotation, Nod MultiplayerAPI::RPCConfig rpc_config; rpc_config.rpc_mode = t_mode; - for (int i = 0; i < p_annotation->resolved_arguments.size(); i++) { - if (i == 0) { + if (p_annotation->resolved_arguments.size()) { + int last = p_annotation->resolved_arguments.size() - 1; + if (p_annotation->resolved_arguments[last].get_type() == Variant::INT) { + rpc_config.channel = p_annotation->resolved_arguments[last].operator int(); + last -= 1; + } + if (last > 3) { + push_error(R"(Invalid RPC arguments. At most 4 arguments are allowed, where only the last argument can be an integer to specify the channel.')", p_annotation); + return false; + } + for (int i = last; i >= 0; i--) { String mode = p_annotation->resolved_arguments[i].operator String(); if (mode == "any") { - rpc_config.rpc_mode = MultiplayerAPI::RPC_MODE_REMOTE; - } else if (mode == "master") { - rpc_config.rpc_mode = MultiplayerAPI::RPC_MODE_MASTER; - } else if (mode == "puppet") { - rpc_config.rpc_mode = MultiplayerAPI::RPC_MODE_PUPPET; - } else { - push_error(R"(Invalid RPC mode. Must be one of: 'any', 'master', or 'puppet')", p_annotation); - return false; - } - } else if (i == 1) { - String sync = p_annotation->resolved_arguments[i].operator String(); - if (sync == "sync") { + rpc_config.rpc_mode = MultiplayerAPI::RPC_MODE_ANY; + } else if (mode == "auth") { + rpc_config.rpc_mode = MultiplayerAPI::RPC_MODE_AUTHORITY; + } else if (mode == "sync") { rpc_config.sync = true; - } else if (sync == "nosync") { + } else if (mode == "nosync") { rpc_config.sync = false; - } else { - push_error(R"(Invalid RPC sync mode. Must be one of: 'sync' or 'nosync')", p_annotation); - return false; - } - } else if (i == 2) { - String mode = p_annotation->resolved_arguments[i].operator String(); - if (mode == "reliable") { + } else if (mode == "reliable") { rpc_config.transfer_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE; } else if (mode == "unreliable") { rpc_config.transfer_mode = MultiplayerPeer::TRANSFER_MODE_UNRELIABLE; } else if (mode == "ordered") { rpc_config.transfer_mode = MultiplayerPeer::TRANSFER_MODE_UNRELIABLE_ORDERED; } else { - push_error(R"(Invalid RPC transfer mode. Must be one of: 'reliable', 'unreliable', 'ordered')", p_annotation); - return false; + push_error(R"(Invalid RPC argument. Must be one of: 'sync'/'nosync' (local calls), 'any'/'auth' (permission), 'reliable'/'unreliable'/'ordered' (transfer mode).)", p_annotation); } - } else if (i == 3) { - rpc_config.channel = p_annotation->resolved_arguments[i].operator int(); } } switch (p_node->type) { diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 6a227a55e5..0bce8d7ddd 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -1399,7 +1399,6 @@ public: ClassNode *get_tree() const { return head; } bool is_tool() const { return _is_tool; } static Variant::Type get_builtin_type(const StringName &p_type); - static StringName get_real_class_name(const StringName &p_source); 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 3f14156dfa..d4a098811a 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -1440,7 +1440,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() { GDScriptTokenizer::GDScriptTokenizer() { #ifdef TOOLS_ENABLED if (EditorSettings::get_singleton()) { - tab_size = EditorSettings::get_singleton()->get_setting("text_editor/indent/size"); + tab_size = EditorSettings::get_singleton()->get_setting("text_editor/behavior/indent/size"); } #endif // TOOLS_ENABLED } diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 8a261a88e3..882256b7e3 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -174,7 +174,7 @@ void (*type_init_function_table[])(Variant *) = { &VariantInitializer<StringName>::init, // STRING_NAME. &VariantInitializer<NodePath>::init, // NODE_PATH. &VariantInitializer<RID>::init, // RID. - &VariantTypeAdjust<Object *>::adjust, // OBJECT. + &VariantInitializer<Object *>::init, // OBJECT. &VariantInitializer<Callable>::init, // CALLABLE. &VariantInitializer<Signal>::init, // SIGNAL. &VariantInitializer<Dictionary>::init, // DICTIONARY. diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp index 9574c765bc..03b1e3fa44 100644 --- a/modules/gdscript/language_server/gdscript_text_document.cpp +++ b/modules/gdscript/language_server/gdscript_text_document.cpp @@ -291,8 +291,8 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) { } } else if (item.kind == lsp::CompletionItemKind::Event) { if (params.context.triggerKind == lsp::CompletionTriggerKind::TriggerCharacter && (params.context.triggerCharacter == "(")) { - const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\""; - item.insertText = quote_style + item.label + quote_style; + const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\""; + item.insertText = item.label.quote(quote_style); } } diff --git a/modules/gdscript/tests/README.md b/modules/gdscript/tests/README.md new file mode 100644 index 0000000000..6e54085962 --- /dev/null +++ b/modules/gdscript/tests/README.md @@ -0,0 +1,8 @@ +# GDScript integration tests + +The `scripts/` folder contains integration tests in the form of GDScript files +and output files. + +See the +[Integration tests for GDScript documentation](https://docs.godotengine.org/en/latest/development/cpp/unit_testing.html#integration-tests-for-gdscript) +for information about creating and running GDScript integration tests. diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp index 03a48bf071..6225e5d1eb 100644 --- a/modules/gdscript/tests/gdscript_test_runner.cpp +++ b/modules/gdscript/tests/gdscript_test_runner.cpp @@ -48,11 +48,11 @@ namespace GDScriptTests { void init_autoloads() { - Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); + OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); // First pass, add the constants so they exist before any script is loaded. - for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) { - const ProjectSettings::AutoloadInfo &info = E->get(); + for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { + const ProjectSettings::AutoloadInfo &info = E.get(); if (info.is_singleton) { for (int i = 0; i < ScriptServer::get_language_count(); i++) { @@ -62,8 +62,8 @@ void init_autoloads() { } // Second pass, load into global constants. - for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) { - const ProjectSettings::AutoloadInfo &info = E->get(); + for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { + const ProjectSettings::AutoloadInfo &info = E.get(); if (!info.is_singleton) { // Skip non-singletons since we don't have a scene tree here anyway. diff --git a/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.out b/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.out index dc4348d9c3..2c8cc9c03f 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.out +++ b/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.out @@ -1,3 +1,3 @@ GDTEST_OK Name is equal -True +true diff --git a/modules/gdscript/tests/scripts/analyzer/features/call_static_builtin_function.gd b/modules/gdscript/tests/scripts/analyzer/features/call_static_builtin_function.gd new file mode 100644 index 0000000000..ac66b78220 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/call_static_builtin_function.gd @@ -0,0 +1,3 @@ +func test(): + print(Color.html_is_valid("00ffff")) + print("OK") diff --git a/modules/gdscript/tests/scripts/analyzer/features/call_static_builtin_function.out b/modules/gdscript/tests/scripts/analyzer/features/call_static_builtin_function.out new file mode 100644 index 0000000000..ad6beeb646 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/call_static_builtin_function.out @@ -0,0 +1,3 @@ +GDTEST_OK +true +OK diff --git a/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.gd b/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.gd new file mode 100644 index 0000000000..fb0ace6a90 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.gd @@ -0,0 +1,5 @@ +func test(): + pass + +func something(): + return "OK" diff --git a/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.out b/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.out new file mode 100644 index 0000000000..d73c5eb7cd --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.out @@ -0,0 +1 @@ +GDTEST_OK diff --git a/modules/gdscript/tests/scripts/analyzer/features/inner_class_as_return_type.gd b/modules/gdscript/tests/scripts/analyzer/features/inner_class_as_return_type.gd new file mode 100644 index 0000000000..4f4b7a4897 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/inner_class_as_return_type.gd @@ -0,0 +1,11 @@ +class InnerClass: + var val := "OK" + static func create_instance() -> InnerClass: + return new() + +func create_inner_instance() -> InnerClass: + return InnerClass.create_instance() + +func test(): + var instance = create_inner_instance() + print(instance.val) diff --git a/modules/gdscript/tests/scripts/analyzer/features/inner_class_as_return_type.out b/modules/gdscript/tests/scripts/analyzer/features/inner_class_as_return_type.out new file mode 100644 index 0000000000..1ccb591560 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/inner_class_as_return_type.out @@ -0,0 +1,2 @@ +GDTEST_OK +OK diff --git a/modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.gd b/modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.gd new file mode 100644 index 0000000000..5f73064cc0 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.gd @@ -0,0 +1,5 @@ +const preloaded : GDScript = preload("gdscript_to_preload.gd") + +func test(): + var preloaded_instance: preloaded = preloaded.new() + print(preloaded_instance.something()) diff --git a/modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.out b/modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.out new file mode 100644 index 0000000000..1ccb591560 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.out @@ -0,0 +1,2 @@ +GDTEST_OK +OK diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_empty_assignee.gd b/modules/gdscript/tests/scripts/parser/errors/assignment_empty_assignee.gd new file mode 100644 index 0000000000..17c65ad60a --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/assignment_empty_assignee.gd @@ -0,0 +1,3 @@ +func test(): + var a = 0 + a = diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_empty_assignee.out b/modules/gdscript/tests/scripts/parser/errors/assignment_empty_assignee.out new file mode 100644 index 0000000000..1369a7a0dc --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/assignment_empty_assignee.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Expected an expression after "=". diff --git a/modules/gdscript/tests/scripts/parser/features/str_preserves_case.gd b/modules/gdscript/tests/scripts/parser/features/str_preserves_case.gd new file mode 100644 index 0000000000..9e48a7f0da --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/str_preserves_case.gd @@ -0,0 +1,7 @@ +func test(): + var null_var = null + var true_var: bool = true + var false_var: bool = false + print(str(null_var)) + print(str(true_var)) + print(str(false_var)) diff --git a/modules/gdscript/tests/scripts/parser/features/str_preserves_case.out b/modules/gdscript/tests/scripts/parser/features/str_preserves_case.out new file mode 100644 index 0000000000..abba38e87c --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/str_preserves_case.out @@ -0,0 +1,4 @@ +GDTEST_OK +null +true +false diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp index 52e9d92223..e54f055f2b 100644 --- a/modules/gdscript/tests/test_gdscript.cpp +++ b/modules/gdscript/tests/test_gdscript.cpp @@ -56,7 +56,7 @@ static void test_tokenizer(const String &p_code, const Vector<String> &p_lines) int tab_size = 4; #ifdef TOOLS_ENABLED if (EditorSettings::get_singleton()) { - tab_size = EditorSettings::get_singleton()->get_setting("text_editor/indent/size"); + tab_size = EditorSettings::get_singleton()->get_setting("text_editor/behavior/indent/size"); } #endif // TOOLS_ENABLED String tab = String(" ").repeat(tab_size); diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index be44f66423..ff0579a11c 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -43,6 +43,7 @@ #include "gltf_texture.h" #include "core/crypto/crypto_core.h" +#include "core/error/error_macros.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" #include "core/io/json.h" @@ -4469,9 +4470,9 @@ Error GLTFDocument::_parse_lights(Ref<GLTFState> state) { const Dictionary &spot = d["spot"]; light->inner_cone_angle = spot["innerConeAngle"]; light->outer_cone_angle = spot["outerConeAngle"]; - ERR_FAIL_COND_V_MSG(light->inner_cone_angle >= light->outer_cone_angle, ERR_PARSE_ERROR, "The inner angle must be smaller than the outer angle."); + ERR_CONTINUE_MSG(light->inner_cone_angle >= light->outer_cone_angle, "The inner angle must be smaller than the outer angle."); } else if (type != "point" && type != "directional") { - ERR_FAIL_V_MSG(ERR_PARSE_ERROR, "Light type is unknown."); + ERR_CONTINUE_MSG(ERR_PARSE_ERROR, "Light type is unknown."); } state->lights.push_back(light); @@ -5380,15 +5381,16 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> state, Node *scene_parent // and attach it to the bone_attachment scene_parent = bone_attachment; } - - // We still have not managed to make a node if (gltf_node->mesh >= 0) { current_node = _generate_mesh_instance(state, scene_parent, node_index); } else if (gltf_node->camera >= 0) { current_node = _generate_camera(state, scene_parent, node_index); } else if (gltf_node->light >= 0) { current_node = _generate_light(state, scene_parent, node_index); - } else { + } + + // We still have not managed to make a node. + if (!current_node) { current_node = _generate_spatial(state, scene_parent, node_index); } diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index 3e8ea19045..7a826897a9 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -346,8 +346,8 @@ private: Error _serialize_extensions(Ref<GLTFState> state) const; public: - // http://www.itu.int/rec/R-REC-BT.601 - // http://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.601-7-201103-I!!PDF-E.pdf + // https://www.itu.int/rec/R-REC-BT.601 + // https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.601-7-201103-I!!PDF-E.pdf static constexpr float R_BRIGHTNESS_COEFF = 0.299f; static constexpr float G_BRIGHTNESS_COEFF = 0.587f; static constexpr float B_BRIGHTNESS_COEFF = 0.114f; diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml index 8ea7384658..c1dbe63628 100644 --- a/modules/gridmap/doc_classes/GridMap.xml +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -53,18 +53,18 @@ The orientation of the cell at the given grid coordinates. [code]-1[/code] is returned if the cell is empty. </description> </method> - <method name="get_collision_layer_bit" qualifiers="const"> + <method name="get_collision_layer_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns an individual bit on the [member collision_layer]. + Returns whether or not the specified layer of the [member collision_layer] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="get_collision_mask_bit" qualifiers="const"> + <method name="get_collision_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns an individual bit on the [member collision_mask]. + Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="get_meshes"> @@ -119,20 +119,20 @@ <description> </description> </method> - <method name="set_collision_layer_bit"> + <method name="set_collision_layer_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - Sets an individual bit on the [member collision_layer]. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_layer], given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="set_collision_mask_bit"> + <method name="set_collision_mask_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - Sets an individual bit on the [member collision_mask]. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="world_to_map" qualifiers="const"> diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index fea513c820..8e8b6f14ad 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -151,36 +151,40 @@ uint32_t GridMap::get_collision_mask() const { return collision_mask; } -void GridMap::set_collision_mask_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); - uint32_t mask = get_collision_mask(); +void GridMap::set_collision_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); + uint32_t collision_layer = get_collision_layer(); if (p_value) { - mask |= 1 << p_bit; + collision_layer |= 1 << (p_layer_number - 1); } else { - mask &= ~(1 << p_bit); + collision_layer &= ~(1 << (p_layer_number - 1)); } - set_collision_mask(mask); + set_collision_layer(collision_layer); } -bool GridMap::get_collision_mask_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); - return get_collision_mask() & (1 << p_bit); +bool GridMap::get_collision_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_layer() & (1 << (p_layer_number - 1)); } -void GridMap::set_collision_layer_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); - uint32_t layer = get_collision_layer(); +void GridMap::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); + uint32_t mask = get_collision_mask(); if (p_value) { - layer |= 1 << p_bit; + mask |= 1 << (p_layer_number - 1); } else { - layer &= ~(1 << p_bit); + mask &= ~(1 << (p_layer_number - 1)); } - set_collision_layer(layer); + set_collision_mask(mask); } -bool GridMap::get_collision_layer_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision layer bit must be between 0 and 31 inclusive."); - return get_collision_layer() & (1 << p_bit); +bool GridMap::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); } void GridMap::set_bake_navigation(bool p_bake_navigation) { @@ -794,11 +798,11 @@ void GridMap::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &GridMap::set_collision_mask); ClassDB::bind_method(D_METHOD("get_collision_mask"), &GridMap::get_collision_mask); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &GridMap::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &GridMap::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &GridMap::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &GridMap::get_collision_mask_value); - ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &GridMap::set_collision_layer_bit); - ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &GridMap::get_collision_layer_bit); + ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &GridMap::set_collision_layer_value); + ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &GridMap::get_collision_layer_value); ClassDB::bind_method(D_METHOD("set_bake_navigation", "bake_navigation"), &GridMap::set_bake_navigation); ClassDB::bind_method(D_METHOD("is_baking_navigation"), &GridMap::is_baking_navigation); diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h index 8cd82e1f4c..879489fc70 100644 --- a/modules/gridmap/grid_map.h +++ b/modules/gridmap/grid_map.h @@ -217,11 +217,11 @@ public: void set_collision_mask(uint32_t p_mask); uint32_t get_collision_mask() const; - void set_collision_layer_bit(int p_bit, bool p_value); - bool get_collision_layer_bit(int p_bit) const; + void set_collision_layer_value(int p_layer_number, bool p_value); + bool get_collision_layer_value(int p_layer_number) const; - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) const; void set_bake_navigation(bool p_bake_navigation); bool is_baking_navigation(); diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index f101c43e89..2331a12d0f 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -790,7 +790,7 @@ void GridMapEditor::_sbox_input(const Ref<InputEvent> &p_ie) { if (k.is_valid() && (k->get_keycode() == KEY_UP || k->get_keycode() == KEY_DOWN || k->get_keycode() == KEY_PAGEUP || k->get_keycode() == KEY_PAGEDOWN)) { // Forward the key input to the ItemList so it can be scrolled - mesh_library_palette->call("_gui_input", k); + mesh_library_palette->gui_input(k); search_box->accept_event(); } } diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp index 2cc974322d..fb741f6266 100644 --- a/modules/minimp3/audio_stream_mp3.cpp +++ b/modules/minimp3/audio_stream_mp3.cpp @@ -37,11 +37,13 @@ #include "core/io/file_access.h" -void AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) { - ERR_FAIL_COND(!active); +int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) { + ERR_FAIL_COND_V(!active, 0); int todo = p_frames; + int frames_mixed_this_step = p_frames; + while (todo && active) { mp3dec_frame_info_t frame_info; mp3d_sample_t *buf_frame = nullptr; @@ -60,6 +62,7 @@ void AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) { seek(mp3_stream->loop_offset); loops++; } else { + frames_mixed_this_step = p_frames - todo; //fill remainder with silence for (int i = p_frames - todo; i < p_frames; i++) { p_buffer[i] = AudioFrame(0, 0); @@ -69,6 +72,7 @@ void AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) { } } } + return frames_mixed_this_step; } float AudioStreamPlaybackMP3::get_stream_sampling_rate() { diff --git a/modules/minimp3/audio_stream_mp3.h b/modules/minimp3/audio_stream_mp3.h index ce001fc418..5dd88779f8 100644 --- a/modules/minimp3/audio_stream_mp3.h +++ b/modules/minimp3/audio_stream_mp3.h @@ -51,7 +51,7 @@ class AudioStreamPlaybackMP3 : public AudioStreamPlaybackResampled { Ref<AudioStreamMP3> mp3_stream; protected: - virtual void _mix_internal(AudioFrame *p_buffer, int p_frames) override; + virtual int _mix_internal(AudioFrame *p_buffer, int p_frames) override; virtual float get_stream_sampling_rate() override; public: diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp index 590b95ab79..12bb0e5b44 100644 --- a/modules/mobile_vr/mobile_vr_interface.cpp +++ b/modules/mobile_vr/mobile_vr_interface.cpp @@ -39,7 +39,7 @@ StringName MobileVRInterface::get_name() const { return "Native mobile"; }; -int MobileVRInterface::get_capabilities() const { +uint32_t MobileVRInterface::get_capabilities() const { return XRInterface::XR_STEREO; }; @@ -305,6 +305,10 @@ uint32_t MobileVRInterface::get_view_count() { return 2; }; +XRInterface::TrackingStatus MobileVRInterface::get_tracking_status() const { + return tracking_state; +} + bool MobileVRInterface::is_initialized() const { return (initialized); }; @@ -340,16 +344,16 @@ bool MobileVRInterface::initialize() { void MobileVRInterface::uninitialize() { if (initialized) { XRServer *xr_server = XRServer::get_singleton(); - if (xr_server != nullptr) { + if (xr_server != nullptr && xr_server->get_primary_interface() == this) { // no longer our primary interface - xr_server->clear_primary_interface_if(this); + xr_server->set_primary_interface(nullptr); } initialized = false; }; }; -Size2 MobileVRInterface::get_render_targetsize() { +Size2 MobileVRInterface::get_render_target_size() { _THREAD_SAFE_METHOD_ // we use half our window size @@ -429,31 +433,6 @@ CameraMatrix MobileVRInterface::get_projection_for_view(uint32_t p_view, real_t return eye; }; -void MobileVRInterface::commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) { - _THREAD_SAFE_METHOD_ - - // We must have a valid render target - ERR_FAIL_COND(!p_render_target.is_valid()); - - // Because we are rendering to our device we must use our main viewport! - ERR_FAIL_COND(p_screen_rect == Rect2()); - - Rect2 dest = p_screen_rect; - Vector2 eye_center; - - // we output half a screen - dest.size.x *= 0.5; - - if (p_eye == XRInterface::EYE_LEFT) { - eye_center.x = ((-intraocular_dist / 2.0) + (display_width / 4.0)) / (display_width / 2.0); - } else if (p_eye == XRInterface::EYE_RIGHT) { - dest.position.x = dest.size.x; - eye_center.x = ((intraocular_dist / 2.0) - (display_width / 4.0)) / (display_width / 2.0); - } - // we don't offset the eye center vertically (yet) - eye_center.y = 0.0; -} - Vector<BlitToScreen> MobileVRInterface::commit_views(RID p_render_target, const Rect2 &p_screen_rect) { _THREAD_SAFE_METHOD_ diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h index 29ce0f92c8..48b76ec187 100644 --- a/modules/mobile_vr/mobile_vr_interface.h +++ b/modules/mobile_vr/mobile_vr_interface.h @@ -52,6 +52,7 @@ class MobileVRInterface : public XRInterface { private: bool initialized = false; + XRInterface::TrackingStatus tracking_state; Basis orientation; // Just set some defaults for these. At some point we need to look at adding a lookup table for common device + headset combos and/or support reading cardboard QR codes @@ -86,20 +87,20 @@ private: Vector3 mag_next_max; ///@TODO a few support functions for trackers, most are math related and should likely be moved elsewhere - float floor_decimals(float p_value, float p_decimals) { + float floor_decimals(const float p_value, const float p_decimals) { float power_of_10 = pow(10.0f, p_decimals); return floor(p_value * power_of_10) / power_of_10; }; - Vector3 floor_decimals(const Vector3 &p_vector, float p_decimals) { + Vector3 floor_decimals(const Vector3 &p_vector, const float p_decimals) { return Vector3(floor_decimals(p_vector.x, p_decimals), floor_decimals(p_vector.y, p_decimals), floor_decimals(p_vector.z, p_decimals)); }; - Vector3 low_pass(const Vector3 &p_vector, const Vector3 &p_last_vector, float p_factor) { + Vector3 low_pass(const Vector3 &p_vector, const Vector3 &p_last_vector, const float p_factor) { return p_vector + (p_factor * (p_last_vector - p_vector)); }; - Vector3 scrub(const Vector3 &p_vector, const Vector3 &p_last_vector, float p_decimals, float p_factor) { + Vector3 scrub(const Vector3 &p_vector, const Vector3 &p_last_vector, const float p_decimals, const float p_factor) { return low_pass(floor_decimals(p_vector, p_decimals), p_last_vector, p_factor); }; @@ -131,13 +132,15 @@ public: real_t get_k2() const; virtual StringName get_name() const override; - virtual int get_capabilities() const override; + virtual uint32_t get_capabilities() const override; + + virtual TrackingStatus get_tracking_status() const override; virtual bool is_initialized() const override; virtual bool initialize() override; virtual void uninitialize() override; - virtual Size2 get_render_targetsize() override; + virtual Size2 get_render_target_size() override; virtual uint32_t get_view_count() override; virtual Transform3D get_camera_transform() override; virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override; @@ -145,13 +148,9 @@ public: virtual Vector<BlitToScreen> commit_views(RID p_render_target, const Rect2 &p_screen_rect) override; virtual void process() override; - virtual void notification(int p_what) override {} MobileVRInterface(); ~MobileVRInterface(); - - // deprecated - virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override; }; #endif // !MOBILE_VR_INTERFACE_H diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 520262c0eb..978093b7d5 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -82,6 +82,12 @@ static bool _create_project_solution_if_needed() { CSharpLanguage *CSharpLanguage::singleton = nullptr; +GDNativeInstanceBindingCallbacks CSharpLanguage::_instance_binding_callbacks = { + &_instance_binding_create_callback, + &_instance_binding_free_callback, + &_instance_binding_reference_callback +}; + String CSharpLanguage::get_name() const { return "C#"; } @@ -542,10 +548,10 @@ String CSharpLanguage::make_function(const String &, const String &, const Packe String CSharpLanguage::_get_indentation() const { #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { - bool use_space_indentation = EDITOR_DEF("text_editor/indent/type", 0); + bool use_space_indentation = EDITOR_DEF("text_editor/behavior/indent/type", 0); if (use_space_indentation) { - int indent_size = EDITOR_DEF("text_editor/indent/size", 4); + int indent_size = EDITOR_DEF("text_editor/behavior/indent/size", 4); String space_indent = ""; for (int i = 0; i < indent_size; i++) { @@ -1444,46 +1450,46 @@ bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_b return true; } -void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) { - MutexLock lock(language_bind_mutex); +Map<Object *, CSharpScriptBinding>::Element *CSharpLanguage::insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding) { + return script_bindings.insert(p_object, p_script_binding); +} + +void *CSharpLanguage::_instance_binding_create_callback(void *, void *p_instance) { + CSharpLanguage *csharp_lang = CSharpLanguage::get_singleton(); - Map<Object *, CSharpScriptBinding>::Element *match = script_bindings.find(p_object); + MutexLock lock(csharp_lang->language_bind_mutex); + + Map<Object *, CSharpScriptBinding>::Element *match = csharp_lang->script_bindings.find((Object *)p_instance); if (match) { return (void *)match; } CSharpScriptBinding script_binding; - if (!setup_csharp_script_binding(script_binding, p_object)) { - return nullptr; - } - - return (void *)insert_script_binding(p_object, script_binding); + return (void *)csharp_lang->insert_script_binding((Object *)p_instance, script_binding); } -Map<Object *, CSharpScriptBinding>::Element *CSharpLanguage::insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding) { - return script_bindings.insert(p_object, p_script_binding); -} +void CSharpLanguage::_instance_binding_free_callback(void *, void *, void *p_binding) { + CSharpLanguage *csharp_lang = CSharpLanguage::get_singleton(); -void CSharpLanguage::free_instance_binding_data(void *p_data) { if (GDMono::get_singleton() == nullptr) { #ifdef DEBUG_ENABLED - CRASH_COND(!script_bindings.is_empty()); + CRASH_COND(!csharp_lang->script_bindings.is_empty()); #endif // Mono runtime finalized, all the gchandle bindings were already released return; } - if (finalizing) { + if (csharp_lang->finalizing) { return; // inside CSharpLanguage::finish(), all the gchandle bindings are released there } GD_MONO_ASSERT_THREAD_ATTACHED; { - MutexLock lock(language_bind_mutex); + MutexLock lock(csharp_lang->language_bind_mutex); - Map<Object *, CSharpScriptBinding>::Element *data = (Map<Object *, CSharpScriptBinding>::Element *)p_data; + Map<Object *, CSharpScriptBinding>::Element *data = (Map<Object *, CSharpScriptBinding>::Element *)p_binding; CSharpScriptBinding &script_binding = data->value(); @@ -1497,92 +1503,110 @@ void CSharpLanguage::free_instance_binding_data(void *p_data) { script_binding.gchandle.release(); } - script_bindings.erase(data); + csharp_lang->script_bindings.erase(data); } } -void CSharpLanguage::refcount_incremented_instance_binding(Object *p_object) { -#if 0 - RefCounted *rc_owner = Object::cast_to<RefCounted>(p_object); +GDNativeBool CSharpLanguage::_instance_binding_reference_callback(void *p_token, void *p_binding, GDNativeBool p_reference) { + CRASH_COND(!p_binding); + + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)p_binding)->get(); + + RefCounted *rc_owner = Object::cast_to<RefCounted>(script_binding.owner); #ifdef DEBUG_ENABLED CRASH_COND(!rc_owner); - CRASH_COND(!p_object->has_script_instance_binding(get_language_index())); #endif - void *data = p_object->get_script_instance_binding(get_language_index()); - CRASH_COND(!data); - - CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); MonoGCHandleData &gchandle = script_binding.gchandle; + int refcount = rc_owner->reference_get_count(); + if (!script_binding.inited) { - return; + return refcount == 0; } - if (rc_owner->reference_get_count() > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 - GD_MONO_SCOPE_THREAD_ATTACH; + if (p_reference) { + // Refcount incremented + if (refcount > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 + GD_MONO_SCOPE_THREAD_ATTACH; - // The reference count was increased after the managed side was the only one referencing our owner. - // This means the owner is being referenced again by the unmanaged side, - // so the owner must hold the managed side alive again to avoid it from being GCed. + // The reference count was increased after the managed side was the only one referencing our owner. + // This means the owner is being referenced again by the unmanaged side, + // so the owner must hold the managed side alive again to avoid it from being GCed. - MonoObject *target = gchandle.get_target(); - if (!target) { - return; // Called after the managed side was collected, so nothing to do here - } + MonoObject *target = gchandle.get_target(); + if (!target) { + return false; // Called after the managed side was collected, so nothing to do here + } - // Release the current weak handle and replace it with a strong handle. - MonoGCHandleData strong_gchandle = MonoGCHandleData::new_strong_handle(target); - gchandle.release(); - gchandle = strong_gchandle; - } -#endif -} + // Release the current weak handle and replace it with a strong handle. + MonoGCHandleData strong_gchandle = MonoGCHandleData::new_strong_handle(target); + gchandle.release(); + gchandle = strong_gchandle; + } -bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) { -#if 0 - RefCounted *rc_owner = Object::cast_to<RefCounted>(p_object); + return false; + } else { + // Refcount decremented + if (refcount == 1 && !gchandle.is_released() && !gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 + GD_MONO_SCOPE_THREAD_ATTACH; -#ifdef DEBUG_ENABLED - CRASH_COND(!rc_owner); - CRASH_COND(!p_object->has_script_instance_binding(get_language_index())); -#endif + // If owner owner is no longer referenced by the unmanaged side, + // the managed instance takes responsibility of deleting the owner when GCed. - void *data = p_object->get_script_instance_binding(get_language_index()); - CRASH_COND(!data); + MonoObject *target = gchandle.get_target(); + if (!target) { + return refcount == 0; // Called after the managed side was collected, so nothing to do here + } - CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); - MonoGCHandleData &gchandle = script_binding.gchandle; + // Release the current strong handle and replace it with a weak handle. + MonoGCHandleData weak_gchandle = MonoGCHandleData::new_weak_handle(target); + gchandle.release(); + gchandle = weak_gchandle; - int refcount = rc_owner->reference_get_count(); + return false; + } - if (!script_binding.inited) { return refcount == 0; } +} - if (refcount == 1 && !gchandle.is_released() && !gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 - GD_MONO_SCOPE_THREAD_ATTACH; +void *CSharpLanguage::get_instance_binding(Object *p_object) { + void *binding = p_object->get_instance_binding(get_singleton(), &_instance_binding_callbacks); - // If owner owner is no longer referenced by the unmanaged side, - // the managed instance takes responsibility of deleting the owner when GCed. + // Initially this was in `_instance_binding_create_callback`. However, after the new instance + // binding re-write it was resulting in a deadlock in `_instance_binding_reference`, as + // `setup_csharp_script_binding` may call `reference()`. It was moved here outside to fix that. - MonoObject *target = gchandle.get_target(); - if (!target) { - return refcount == 0; // Called after the managed side was collected, so nothing to do here - } + if (binding) { + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)binding)->value(); - // Release the current strong handle and replace it with a weak handle. - MonoGCHandleData weak_gchandle = MonoGCHandleData::new_weak_handle(target); - gchandle.release(); - gchandle = weak_gchandle; + if (!script_binding.inited) { + MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex()); - return false; + if (!script_binding.inited) { // Another thread may have set it up + CSharpLanguage::get_singleton()->setup_csharp_script_binding(script_binding, p_object); + } + } } - return refcount == 0; + return binding; +} + +void *CSharpLanguage::get_existing_instance_binding(Object *p_object) { +#ifdef DEBUG_ENABLED + CRASH_COND(p_object->has_instance_binding(p_object)); #endif - return false; + return p_object->get_instance_binding(get_singleton(), &_instance_binding_callbacks); +} + +void CSharpLanguage::set_instance_binding(Object *p_object, void *p_binding) { + p_object->set_instance_binding(get_singleton(), p_binding, &_instance_binding_callbacks); +} + +bool CSharpLanguage::has_instance_binding(Object *p_object) { + return p_object->has_instance_binding(get_singleton()); } CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle) { @@ -2260,29 +2284,16 @@ CSharpInstance::~CSharpInstance() { // Otherwise, the unsafe reference debug checks will incorrectly detect a bug. bool die = _unreference_owner_unsafe(); CRASH_COND(die); // `owner_keep_alive` holds a reference, so it can't die -#if 0 - void *data = owner->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); - + void *data = CSharpLanguage::get_instance_binding(owner); CRASH_COND(data == nullptr); - CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); - - if (!script_binding.inited) { - MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex()); - - if (!script_binding.inited) { // Other thread may have set it up - // Already had a binding that needs to be setup - CSharpLanguage::get_singleton()->setup_csharp_script_binding(script_binding, owner); - CRASH_COND(!script_binding.inited); - } - } + CRASH_COND(!script_binding.inited); #ifdef DEBUG_ENABLED // The "instance binding" holds a reference so the refcount should be at least 2 before `scope_keep_owner_alive` goes out of scope CRASH_COND(rc_owner->reference_get_count() <= 1); #endif -#endif } if (script.is_valid() && owner) { @@ -3100,10 +3111,10 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg // Hold it alive. Important if we have to dispose a script instance binding before creating the CSharpInstance. ref = Ref<RefCounted>(static_cast<RefCounted *>(p_owner)); } -#if 0 + // If the object had a script instance binding, dispose it before adding the CSharpInstance - if (p_owner->has_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index())) { - void *data = p_owner->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); + if (CSharpLanguage::has_instance_binding(p_owner)) { + void *data = CSharpLanguage::get_existing_instance_binding(p_owner); CRASH_COND(data == nullptr); CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); @@ -3122,7 +3133,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg script_binding.inited = false; } } -#endif + CSharpInstance *instance = memnew(CSharpInstance(Ref<CSharpScript>(this))); instance->base_ref_counted = p_is_ref_counted; instance->owner = p_owner; @@ -3192,7 +3203,7 @@ Variant CSharpScript::_new(const Variant **p_args, int p_argcount, Callable::Cal CSharpInstance *instance = _create_instance(p_args, p_argcount, owner, r != nullptr, r_error); if (!instance) { if (ref.is_null()) { - memdelete(owner); //no owner, sorry + memdelete(owner); // no owner, sorry } return Variant(); } @@ -3455,13 +3466,10 @@ int CSharpScript::get_member_line(const StringName &p_member) const { MultiplayerAPI::RPCMode CSharpScript::_member_get_rpc_mode(IMonoClassMember *p_member) const { if (p_member->has_attribute(CACHED_CLASS(RemoteAttribute))) { - return MultiplayerAPI::RPC_MODE_REMOTE; - } - if (p_member->has_attribute(CACHED_CLASS(MasterAttribute))) { - return MultiplayerAPI::RPC_MODE_MASTER; + return MultiplayerAPI::RPC_MODE_ANY; } if (p_member->has_attribute(CACHED_CLASS(PuppetAttribute))) { - return MultiplayerAPI::RPC_MODE_PUPPET; + return MultiplayerAPI::RPC_MODE_AUTHORITY; } return MultiplayerAPI::RPC_MODE_DISABLED; diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index da6b60aee2..4552f376d0 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -401,7 +401,18 @@ class CSharpLanguage : public ScriptLanguage { static void _editor_init_callback(); #endif + static void *_instance_binding_create_callback(void *p_token, void *p_instance); + static void _instance_binding_free_callback(void *p_token, void *p_instance, void *p_binding); + static GDNativeBool _instance_binding_reference_callback(void *p_token, void *p_binding, GDNativeBool p_reference); + + static GDNativeInstanceBindingCallbacks _instance_binding_callbacks; + public: + static void *get_instance_binding(Object *p_object); + static void *get_existing_instance_binding(Object *p_object); + static void set_instance_binding(Object *p_object, void *p_binding); + static bool has_instance_binding(Object *p_object); + StringNameCache string_names; const Mutex &get_language_bind_mutex() { return language_bind_mutex; } @@ -507,12 +518,6 @@ public: void thread_enter() override; void thread_exit() override; - // Don't use these. I'm watching you - void *alloc_instance_binding_data(Object *p_object) override; - void free_instance_binding_data(void *p_data) override; - void refcount_incremented_instance_binding(Object *p_object) override; - bool refcount_decremented_instance_binding(Object *p_object) override; - Map<Object *, CSharpScriptBinding>::Element *insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding); bool setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object); diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs index c380707587..25e260beed 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs @@ -285,7 +285,7 @@ namespace GodotTools.Build break; } - buildLog.CursorSetLine(line); + buildLog.SetCaretLine(line); } public void RestartBuild() @@ -384,7 +384,7 @@ namespace GodotTools.Build buildLog = new TextEdit { - Readonly = true, + Editable = false, SizeFlagsVertical = (int)SizeFlags.ExpandFill, SizeFlagsHorizontal = (int)SizeFlags.ExpandFill // Avoid being squashed by the issues list }; diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs index 897f1b2822..5f35d506de 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs @@ -73,7 +73,7 @@ namespace GodotTools.Build GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message); } - if (!BuildManager.BuildProjectBlocking("Debug", targets: new[] {"Rebuild"})) + if (!BuildManager.BuildProjectBlocking("Debug", targets: new[] { "Rebuild" })) return; // Build failed // Notify running game for hot-reload @@ -92,7 +92,7 @@ namespace GodotTools.Build if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) return; // No solution to build - BuildManager.BuildProjectBlocking("Debug", targets: new[] {"Clean"}); + BuildManager.BuildProjectBlocking("Debug", targets: new[] { "Clean" }); } private void ViewLogToggled(bool pressed) => BuildOutputView.LogVisible = pressed; @@ -129,10 +129,10 @@ namespace GodotTools.Build RectMinSize = new Vector2(0, 228) * EditorScale; SizeFlagsVertical = (int)SizeFlags.ExpandFill; - var toolBarHBox = new HBoxContainer {SizeFlagsHorizontal = (int)SizeFlags.ExpandFill}; + var toolBarHBox = new HBoxContainer { SizeFlagsHorizontal = (int)SizeFlags.ExpandFill }; AddChild(toolBarHBox); - buildMenuBtn = new MenuButton {Text = "Build", Icon = GetThemeIcon("Play", "EditorIcons")}; + buildMenuBtn = new MenuButton { Text = "Build", Icon = GetThemeIcon("Play", "EditorIcons") }; toolBarHBox.AddChild(buildMenuBtn); var buildMenu = buildMenuBtn.GetPopup(); @@ -183,10 +183,14 @@ namespace GodotTools.Build { base._Notification(what); - if (what == NotificationThemeChanged) { - buildMenuBtn.Icon = GetThemeIcon("Play", "EditorIcons"); - errorsBtn.Icon = GetThemeIcon("StatusError", "EditorIcons"); - warningsBtn.Icon = GetThemeIcon("NodeWarning", "EditorIcons"); + if (what == NotificationThemeChanged) + { + if (buildMenuBtn != null) + buildMenuBtn.Icon = GetThemeIcon("Play", "EditorIcons"); + if (errorsBtn != null) + errorsBtn.Icon = GetThemeIcon("StatusError", "EditorIcons"); + if (warningsBtn != null) + warningsBtn.Icon = GetThemeIcon("NodeWarning", "EditorIcons"); } } } diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 632f7d61cc..7fdef8ff45 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -3130,8 +3130,18 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; } break; case Variant::CALLABLE: + ERR_FAIL_COND_V_MSG(r_iarg.type.cname != name_cache.type_Callable, false, + "Parameter of type '" + String(r_iarg.type.cname) + "' cannot have a default value of type '" + String(name_cache.type_Callable) + "'."); + ERR_FAIL_COND_V_MSG(!p_val.is_zero(), false, + "Parameter of type '" + String(r_iarg.type.cname) + "' can only have null/zero as the default value."); + r_iarg.default_argument = "default"; + break; case Variant::SIGNAL: - CRASH_NOW_MSG("Parameter of type '" + String(r_iarg.type.cname) + "' cannot have a default value."); + ERR_FAIL_COND_V_MSG(r_iarg.type.cname != name_cache.type_Signal, false, + "Parameter of type '" + String(r_iarg.type.cname) + "' cannot have a default value of type '" + String(name_cache.type_Signal) + "'."); + ERR_FAIL_COND_V_MSG(!p_val.is_zero(), false, + "Parameter of type '" + String(r_iarg.type.cname) + "' can only have null/zero as the default value."); + r_iarg.default_argument = "default"; break; default: CRASH_NOW_MSG("Unexpected Variant type: " + itos(p_val.get_type())); diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index a649181b20..8a85a1acbd 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -536,6 +536,8 @@ class BindingsGenerator { StringName type_Object = StaticCString::create("Object"); StringName type_RefCounted = StaticCString::create("RefCounted"); StringName type_RID = StaticCString::create("RID"); + StringName type_Callable = StaticCString::create("Callable"); + StringName type_Signal = StaticCString::create("Signal"); StringName type_String = StaticCString::create("String"); StringName type_StringName = StaticCString::create("StringName"); StringName type_NodePath = StaticCString::create("NodePath"); diff --git a/modules/mono/editor/code_completion.cpp b/modules/mono/editor/code_completion.cpp index b7b36a92d8..d911f6461c 100644 --- a/modules/mono/editor/code_completion.cpp +++ b/modules/mono/editor/code_completion.cpp @@ -121,7 +121,7 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr case CompletionKind::NODE_PATHS: { { // AutoLoads - Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); + OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) { const ProjectSettings::AutoloadInfo &info = E.value; diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp index 8164f459ca..6692a6efec 100644 --- a/modules/mono/editor/editor_internal_calls.cpp +++ b/modules/mono/editor/editor_internal_calls.cpp @@ -241,7 +241,7 @@ MonoBoolean godot_icall_Internal_IsAssembliesReloadingNeeded() { void godot_icall_Internal_ReloadAssemblies(MonoBoolean p_soft_reload) { #ifdef GD_MONO_HOT_RELOAD - _GodotSharp::get_singleton()->call_deferred(SNAME("_reload_assemblies"), (bool)p_soft_reload); + mono_bind::GodotSharp::get_singleton()->call_deferred(SNAME("_reload_assemblies"), (bool)p_soft_reload); #endif } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs index 6cec8773b2..8aaa9e849d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs @@ -6,8 +6,5 @@ namespace Godot public class RemoteAttribute : Attribute {} [AttributeUsage(AttributeTargets.Method)] - public class MasterAttribute : Attribute {} - - [AttributeUsage(AttributeTargets.Method)] public class PuppetAttribute : Attribute {} } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs index d9665cbf2b..bc598248bc 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs @@ -64,7 +64,7 @@ namespace Godot /// <summary> /// If the string is a path to a file, return the path to the file without the extension. /// </summary> - public static string BaseName(this string instance) + public static string GetBaseName(this string instance) { int index = instance.LastIndexOf('.'); @@ -339,7 +339,7 @@ namespace Godot /// <summary> /// If the string is a path to a file, return the extension. /// </summary> - public static string Extension(this string instance) + public static string GetExtension(this string instance) { int pos = instance.FindLast("."); @@ -575,7 +575,7 @@ namespace Godot /// <summary> /// If the string is a path to a file or directory, return <see langword="true"/> if the path is absolute. /// </summary> - public static bool IsAbsPath(this string instance) + public static bool IsAbsolutePath(this string instance) { if (string.IsNullOrEmpty(instance)) return false; @@ -588,9 +588,9 @@ namespace Godot /// <summary> /// If the string is a path to a file or directory, return <see langword="true"/> if the path is relative. /// </summary> - public static bool IsRelPath(this string instance) + public static bool IsRelativePath(this string instance) { - return !IsAbsPath(instance); + return !IsAbsolutePath(instance); } /// <summary> @@ -1103,6 +1103,17 @@ namespace Godot } /// <summary> + /// Returns a simplified canonical path. + /// </summary> + public static string SimplifyPath(this string instance) + { + return godot_icall_String_simplify_path(instance); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_String_simplify_path(string str); + + /// <summary> /// Split the string by a divisor string, return an array of the substrings. /// Example "One,Two,Three" will return ["One","Two","Three"] if split by ",". /// </summary> diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp index a99dff8432..6c5503a3bd 100644 --- a/modules/mono/glue/base_object_glue.cpp +++ b/modules/mono/glue/base_object_glue.cpp @@ -64,8 +64,8 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) { return; } } -#if 0 - void *data = p_ptr->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); + + void *data = CSharpLanguage::get_existing_instance_binding(p_ptr); if (data) { CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); @@ -76,7 +76,6 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) { } } } -#endif } void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolean p_is_finalizer) { @@ -85,7 +84,7 @@ void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoole // This is only called with RefCounted derived classes CRASH_COND(!Object::cast_to<RefCounted>(p_ptr)); #endif -#if 0 + RefCounted *rc = static_cast<RefCounted *>(p_ptr); if (rc->get_script_instance()) { @@ -113,7 +112,7 @@ void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoole if (rc->unreference()) { memdelete(rc); } else { - void *data = rc->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); + void *data = CSharpLanguage::get_existing_instance_binding(rc); if (data) { CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); @@ -125,7 +124,6 @@ void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoole } } } -#endif } void godot_icall_Object_ConnectEventSignals(Object *p_ptr) { diff --git a/modules/mono/glue/string_glue.cpp b/modules/mono/glue/string_glue.cpp index 18a9221f89..bb80a836ad 100644 --- a/modules/mono/glue/string_glue.cpp +++ b/modules/mono/glue/string_glue.cpp @@ -67,6 +67,11 @@ MonoString *godot_icall_String_sha256_text(MonoString *p_str) { return GDMonoMarshal::mono_string_from_godot(ret); } +MonoString *godot_icall_String_simplify_path(MonoString *p_str) { + String ret = GDMonoMarshal::mono_string_to_godot(p_str).simplify_path(); + return GDMonoMarshal::mono_string_from_godot(ret); +} + void godot_register_string_icalls() { 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); @@ -74,6 +79,7 @@ void godot_register_string_icalls() { 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); + GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_simplify_path", godot_icall_String_simplify_path); } #endif // MONO_GLUE_ENABLED diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 299344bb93..1b1349a3a3 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -73,7 +73,7 @@ #endif // TODO: -// This has turn into a gigantic mess. There's too much going on here. Too much #ifdef as well. +// This has turned into a gigantic mess. There's too much going on here. Too much #ifdef as well. // It's just painful to read... It needs to be re-structured. Please, clean this up, future me. GDMono *GDMono::singleton = nullptr; @@ -1335,23 +1335,25 @@ GDMono::~GDMono() { singleton = nullptr; } -_GodotSharp *_GodotSharp::singleton = nullptr; +namespace mono_bind { -void _GodotSharp::attach_thread() { +GodotSharp *GodotSharp::singleton = nullptr; + +void GodotSharp::attach_thread() { GDMonoUtils::attach_current_thread(); } -void _GodotSharp::detach_thread() { +void GodotSharp::detach_thread() { GDMonoUtils::detach_current_thread(); } -int32_t _GodotSharp::get_domain_id() { +int32_t GodotSharp::get_domain_id() { MonoDomain *domain = mono_domain_get(); ERR_FAIL_NULL_V(domain, -1); return mono_domain_get_id(domain); } -int32_t _GodotSharp::get_scripts_domain_id() { +int32_t GodotSharp::get_scripts_domain_id() { ERR_FAIL_NULL_V_MSG(GDMono::get_singleton(), -1, "The Mono runtime is not initialized"); MonoDomain *domain = GDMono::get_singleton()->get_scripts_domain(); @@ -1359,21 +1361,21 @@ int32_t _GodotSharp::get_scripts_domain_id() { return mono_domain_get_id(domain); } -bool _GodotSharp::is_scripts_domain_loaded() { +bool GodotSharp::is_scripts_domain_loaded() { return GDMono::get_singleton() != nullptr && GDMono::get_singleton()->is_runtime_initialized() && GDMono::get_singleton()->get_scripts_domain() != nullptr; } -bool _GodotSharp::_is_domain_finalizing_for_unload(int32_t p_domain_id) { +bool GodotSharp::_is_domain_finalizing_for_unload(int32_t p_domain_id) { return is_domain_finalizing_for_unload(p_domain_id); } -bool _GodotSharp::is_domain_finalizing_for_unload(int32_t p_domain_id) { +bool GodotSharp::is_domain_finalizing_for_unload(int32_t p_domain_id) { return is_domain_finalizing_for_unload(mono_domain_get_by_id(p_domain_id)); } -bool _GodotSharp::is_domain_finalizing_for_unload(MonoDomain *p_domain) { +bool GodotSharp::is_domain_finalizing_for_unload(MonoDomain *p_domain) { GDMono *gd_mono = GDMono::get_singleton(); ERR_FAIL_COND_V_MSG(!gd_mono || !gd_mono->is_runtime_initialized(), @@ -1388,15 +1390,15 @@ bool _GodotSharp::is_domain_finalizing_for_unload(MonoDomain *p_domain) { return mono_domain_is_unloading(p_domain); } -bool _GodotSharp::is_runtime_shutting_down() { +bool GodotSharp::is_runtime_shutting_down() { return mono_runtime_is_shutting_down(); } -bool _GodotSharp::is_runtime_initialized() { +bool GodotSharp::is_runtime_initialized() { return GDMono::get_singleton() != nullptr && GDMono::get_singleton()->is_runtime_initialized(); } -void _GodotSharp::_reload_assemblies(bool p_soft_reload) { +void GodotSharp::_reload_assemblies(bool p_soft_reload) { #ifdef GD_MONO_HOT_RELOAD CRASH_COND(CSharpLanguage::get_singleton() == nullptr); // This method may be called more than once with `call_deferred`, so we need to check @@ -1407,24 +1409,26 @@ void _GodotSharp::_reload_assemblies(bool p_soft_reload) { #endif } -void _GodotSharp::_bind_methods() { - ClassDB::bind_method(D_METHOD("attach_thread"), &_GodotSharp::attach_thread); - ClassDB::bind_method(D_METHOD("detach_thread"), &_GodotSharp::detach_thread); +void GodotSharp::_bind_methods() { + ClassDB::bind_method(D_METHOD("attach_thread"), &GodotSharp::attach_thread); + ClassDB::bind_method(D_METHOD("detach_thread"), &GodotSharp::detach_thread); - ClassDB::bind_method(D_METHOD("get_domain_id"), &_GodotSharp::get_domain_id); - ClassDB::bind_method(D_METHOD("get_scripts_domain_id"), &_GodotSharp::get_scripts_domain_id); - ClassDB::bind_method(D_METHOD("is_scripts_domain_loaded"), &_GodotSharp::is_scripts_domain_loaded); - ClassDB::bind_method(D_METHOD("is_domain_finalizing_for_unload", "domain_id"), &_GodotSharp::_is_domain_finalizing_for_unload); + ClassDB::bind_method(D_METHOD("get_domain_id"), &GodotSharp::get_domain_id); + ClassDB::bind_method(D_METHOD("get_scripts_domain_id"), &GodotSharp::get_scripts_domain_id); + ClassDB::bind_method(D_METHOD("is_scripts_domain_loaded"), &GodotSharp::is_scripts_domain_loaded); + ClassDB::bind_method(D_METHOD("is_domain_finalizing_for_unload", "domain_id"), &GodotSharp::_is_domain_finalizing_for_unload); - ClassDB::bind_method(D_METHOD("is_runtime_shutting_down"), &_GodotSharp::is_runtime_shutting_down); - ClassDB::bind_method(D_METHOD("is_runtime_initialized"), &_GodotSharp::is_runtime_initialized); - ClassDB::bind_method(D_METHOD("_reload_assemblies"), &_GodotSharp::_reload_assemblies); + ClassDB::bind_method(D_METHOD("is_runtime_shutting_down"), &GodotSharp::is_runtime_shutting_down); + ClassDB::bind_method(D_METHOD("is_runtime_initialized"), &GodotSharp::is_runtime_initialized); + ClassDB::bind_method(D_METHOD("_reload_assemblies"), &GodotSharp::_reload_assemblies); } -_GodotSharp::_GodotSharp() { +GodotSharp::GodotSharp() { singleton = this; } -_GodotSharp::~_GodotSharp() { +GodotSharp::~GodotSharp() { singleton = nullptr; } + +} // namespace mono_bind diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index 5accc21f8e..4170e5053f 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -293,8 +293,10 @@ public: gdmono::ScopeExitDomainUnload __gdmono__scope__exit__domain__unload__(m_mono_domain); \ (void)__gdmono__scope__exit__domain__unload__; -class _GodotSharp : public Object { - GDCLASS(_GodotSharp, Object); +namespace mono_bind { + +class GodotSharp : public Object { + GDCLASS(GodotSharp, Object); friend class GDMono; @@ -303,11 +305,11 @@ class _GodotSharp : public Object { void _reload_assemblies(bool p_soft_reload); protected: - static _GodotSharp *singleton; + static GodotSharp *singleton; static void _bind_methods(); public: - static _GodotSharp *get_singleton() { return singleton; } + static GodotSharp *get_singleton() { return singleton; } void attach_thread(); void detach_thread(); @@ -323,8 +325,10 @@ public: bool is_runtime_shutting_down(); bool is_runtime_initialized(); - _GodotSharp(); - ~_GodotSharp(); + GodotSharp(); + ~GodotSharp(); }; +} // namespace mono_bind + #endif // GD_MONO_H diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp index 0b36796d98..8b215a66c2 100644 --- a/modules/mono/mono_gd/gd_mono_cache.cpp +++ b/modules/mono/mono_gd/gd_mono_cache.cpp @@ -141,7 +141,6 @@ void CachedData::clear_godot_api_cache() { class_SignalAttribute = nullptr; class_ToolAttribute = nullptr; class_RemoteAttribute = nullptr; - class_MasterAttribute = nullptr; class_PuppetAttribute = nullptr; class_GodotMethodAttribute = nullptr; field_GodotMethodAttribute_methodName = nullptr; @@ -267,7 +266,6 @@ void update_godot_api_cache() { CACHE_CLASS_AND_CHECK(SignalAttribute, GODOT_API_CLASS(SignalAttribute)); CACHE_CLASS_AND_CHECK(ToolAttribute, GODOT_API_CLASS(ToolAttribute)); CACHE_CLASS_AND_CHECK(RemoteAttribute, GODOT_API_CLASS(RemoteAttribute)); - CACHE_CLASS_AND_CHECK(MasterAttribute, GODOT_API_CLASS(MasterAttribute)); CACHE_CLASS_AND_CHECK(PuppetAttribute, GODOT_API_CLASS(PuppetAttribute)); CACHE_CLASS_AND_CHECK(GodotMethodAttribute, GODOT_API_CLASS(GodotMethodAttribute)); CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName")); diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h index 6d49da0af3..fd28bbda14 100644 --- a/modules/mono/mono_gd/gd_mono_cache.h +++ b/modules/mono/mono_gd/gd_mono_cache.h @@ -112,7 +112,6 @@ struct CachedData { GDMonoClass *class_SignalAttribute; GDMonoClass *class_ToolAttribute; GDMonoClass *class_RemoteAttribute; - GDMonoClass *class_MasterAttribute; GDMonoClass *class_PuppetAttribute; GDMonoClass *class_GodotMethodAttribute; GDMonoField *field_GodotMethodAttribute_methodName; diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp index d6545d50ec..cf76c23926 100644 --- a/modules/mono/mono_gd/gd_mono_internals.cpp +++ b/modules/mono/mono_gd/gd_mono_internals.cpp @@ -45,7 +45,7 @@ namespace GDMonoInternals { void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { // This method should not fail -#if 0 + CRASH_COND(!unmanaged); // All mono objects created from the managed world (e.g.: 'new Player()') @@ -89,12 +89,16 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { } // The object was just created, no script instance binding should have been attached - CRASH_COND(unmanaged->has_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index())); + CRASH_COND(CSharpLanguage::has_instance_binding(unmanaged)); - void *data = (void *)CSharpLanguage::get_singleton()->insert_script_binding(unmanaged, script_binding); + void *data; + { + MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex()); + data = (void *)CSharpLanguage::get_singleton()->insert_script_binding(unmanaged, script_binding); + } // Should be thread safe because the object was just created and nothing else should be referencing it - unmanaged->set_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index(), data); + CSharpLanguage::set_instance_binding(unmanaged, data); return; } @@ -108,7 +112,6 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { CSharpInstance *csharp_instance = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle); unmanaged->set_script_and_instance(script, csharp_instance); -#endif } void unhandled_exception(MonoException *p_exc) { diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index 9f2977bfa2..c9789bf270 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -1686,7 +1686,9 @@ Callable managed_to_callable(const M_Callable &p_managed_callable) { Object *target = p_managed_callable.target ? unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_callable.target)) : nullptr; - StringName *method_ptr = unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_callable.method_string_name)); + StringName *method_ptr = p_managed_callable.method_string_name ? + unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_callable.method_string_name)) : + nullptr; StringName method = method_ptr ? *method_ptr : StringName(); return Callable(target, method); } @@ -1732,7 +1734,9 @@ Signal managed_to_signal_info(const M_SignalInfo &p_managed_signal) { Object *owner = p_managed_signal.owner ? unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_signal.owner)) : nullptr; - StringName *name_ptr = unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_signal.name_string_name)); + StringName *name_ptr = p_managed_signal.name_string_name ? + unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_signal.name_string_name)) : + nullptr; StringName name = name_ptr ? *name_ptr : StringName(); return Signal(owner, name); } diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 080398c997..13939bd014 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -54,7 +54,6 @@ namespace GDMonoUtils { MonoObject *unmanaged_get_managed(Object *unmanaged) { -#if 0 if (!unmanaged) { return nullptr; } @@ -69,22 +68,10 @@ MonoObject *unmanaged_get_managed(Object *unmanaged) { // If the owner does not have a CSharpInstance... - void *data = unmanaged->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); - + void *data = CSharpLanguage::get_instance_binding(unmanaged); ERR_FAIL_NULL_V(data, nullptr); - CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->value(); - - if (!script_binding.inited) { - MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex()); - - if (!script_binding.inited) { // Other thread may have set it up - // Already had a binding that needs to be setup - CSharpLanguage::get_singleton()->setup_csharp_script_binding(script_binding, unmanaged); - - ERR_FAIL_COND_V(!script_binding.inited, nullptr); - } - } + ERR_FAIL_COND_V(!script_binding.inited, nullptr); MonoGCHandleData &gchandle = script_binding.gchandle; @@ -121,8 +108,6 @@ MonoObject *unmanaged_get_managed(Object *unmanaged) { } return mono_object; -#endif - return nullptr; } void set_main_thread(MonoThread *p_thread) { diff --git a/modules/mono/register_types.cpp b/modules/mono/register_types.cpp index 2ba89eac55..98e83335e9 100644 --- a/modules/mono/register_types.cpp +++ b/modules/mono/register_types.cpp @@ -38,15 +38,15 @@ CSharpLanguage *script_language_cs = nullptr; Ref<ResourceFormatLoaderCSharpScript> resource_loader_cs; Ref<ResourceFormatSaverCSharpScript> resource_saver_cs; -_GodotSharp *_godotsharp = nullptr; +mono_bind::GodotSharp *_godotsharp = nullptr; void register_mono_types() { GDREGISTER_CLASS(CSharpScript); - _godotsharp = memnew(_GodotSharp); + _godotsharp = memnew(mono_bind::GodotSharp); - GDREGISTER_CLASS(_GodotSharp); - Engine::get_singleton()->add_singleton(Engine::Singleton("GodotSharp", _GodotSharp::get_singleton())); + GDREGISTER_CLASS(mono_bind::GodotSharp); + Engine::get_singleton()->add_singleton(Engine::Singleton("GodotSharp", mono_bind::GodotSharp::get_singleton())); script_language_cs = memnew(CSharpLanguage); script_language_cs->set_language_index(ScriptServer::get_language_count()); diff --git a/modules/msdfgen/SCsub b/modules/msdfgen/SCsub new file mode 100644 index 0000000000..227369f4bd --- /dev/null +++ b/modules/msdfgen/SCsub @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +Import("env") +Import("env_modules") + +env_msdfgen = env_modules.Clone() + +# Thirdparty source files + +thirdparty_obj = [] + +if env["builtin_msdfgen"]: + env_msdfgen.disable_warnings() + + thirdparty_dir = "#thirdparty/msdfgen/" + thirdparty_sources = [ + "core/Contour.cpp", + "core/EdgeHolder.cpp", + "core/MSDFErrorCorrection.cpp", + "core/Projection.cpp", + "core/Scanline.cpp", + "core/Shape.cpp", + "core/SignedDistance.cpp", + "core/Vector2.cpp", + "core/contour-combiners.cpp", + "core/edge-coloring.cpp", + "core/edge-segments.cpp", + "core/edge-selectors.cpp", + "core/equation-solver.cpp", + "core/msdf-error-correction.cpp", + "core/msdfgen.cpp", + "core/rasterization.cpp", + "core/render-sdf.cpp", + "core/sdf-error-estimation.cpp", + "core/shape-description.cpp", + ] + thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + + env_msdfgen.Append(CPPPATH=["#thirdparty/freetype/include", "#thirdparty/msdfgen", "#thirdparty/nanosvg"]) + + lib = env_msdfgen.add_library("msdfgen_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 = [] + +env_msdfgen.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/msdfgen/config.py b/modules/msdfgen/config.py new file mode 100644 index 0000000000..653e466a74 --- /dev/null +++ b/modules/msdfgen/config.py @@ -0,0 +1,6 @@ +def can_build(env, platform): + return env.module_check_dependencies("msdfgen", ["freetype"]) + + +def configure(env): + pass diff --git a/modules/gdnative/xr/register_types.cpp b/modules/msdfgen/register_types.cpp index cb043debc5..ad60a66d2d 100644 --- a/modules/gdnative/xr/register_types.cpp +++ b/modules/msdfgen/register_types.cpp @@ -29,12 +29,7 @@ /*************************************************************************/ #include "register_types.h" -#include "xr_interface_gdnative.h" -void register_xr_types() { - GDREGISTER_CLASS(XRInterfaceGDNative); - ClassDB::add_compatibility_class("ARVRInterfaceGDNative", "XRInterfaceGDNative"); -} +void register_msdfgen_types() {} -void unregister_xr_types() { -} +void unregister_msdfgen_types() {} diff --git a/modules/gdnative/xr/register_types.h b/modules/msdfgen/register_types.h index 4e7469abe9..fb776c14ed 100644 --- a/modules/gdnative/xr/register_types.h +++ b/modules/msdfgen/register_types.h @@ -28,10 +28,10 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef XR_REGISTER_TYPES_H -#define XR_REGISTER_TYPES_H +#ifndef MSDFGEN_REGISTER_TYPES_H +#define MSDFGEN_REGISTER_TYPES_H -void register_xr_types(); -void unregister_xr_types(); +void register_msdfgen_types(); +void unregister_msdfgen_types(); -#endif // XR_REGISTER_TYPES_H +#endif // MSDFGEN_REGISTER_TYPES_H diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp index 41cd75fd22..905d10c9d4 100644 --- a/modules/navigation/navigation_mesh_generator.cpp +++ b/modules/navigation/navigation_mesh_generator.cpp @@ -138,7 +138,7 @@ void NavigationMeshGenerator::_add_faces(const PackedVector3Array &p_faces, cons } } -void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, int p_generate_from, uint32_t p_collision_mask, bool p_recurse_children) { +void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children) { if (Object::cast_to<MeshInstance3D>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) { MeshInstance3D *mesh_instance = Object::cast_to<MeshInstance3D>(p_node); Ref<Mesh> mesh = mesh_instance->get_mesh(); @@ -187,7 +187,7 @@ void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transfor Ref<CapsuleMesh> capsule_mesh; capsule_mesh.instantiate(); capsule_mesh->set_radius(capsule->get_radius()); - capsule_mesh->set_mid_height(capsule->get_height() / 2.0); + capsule_mesh->set_height(capsule->get_height()); mesh = capsule_mesh; } @@ -515,7 +515,7 @@ void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) Transform3D navmesh_xform = Object::cast_to<Node3D>(p_node)->get_transform().affine_inverse(); for (Node *E : parse_nodes) { - int geometry_type = p_nav_mesh->get_parsed_geometry_type(); + NavigationMesh::ParsedGeometryType geometry_type = p_nav_mesh->get_parsed_geometry_type(); uint32_t collision_mask = p_nav_mesh->get_collision_mask(); bool recurse_children = p_nav_mesh->get_source_geometry_mode() != NavigationMesh::SOURCE_GEOMETRY_GROUPS_EXPLICIT; _parse_geometry(navmesh_xform, E, vertices, indices, geometry_type, collision_mask, recurse_children); diff --git a/modules/navigation/navigation_mesh_generator.h b/modules/navigation/navigation_mesh_generator.h index 847c7d097b..78f1329e3f 100644 --- a/modules/navigation/navigation_mesh_generator.h +++ b/modules/navigation/navigation_mesh_generator.h @@ -52,7 +52,7 @@ protected: static void _add_vertex(const Vector3 &p_vec3, Vector<float> &p_verticies); static void _add_mesh(const Ref<Mesh> &p_mesh, const Transform3D &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices); static void _add_faces(const PackedVector3Array &p_faces, const Transform3D &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices); - static void _parse_geometry(Transform3D p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, int p_generate_from, uint32_t p_collision_mask, bool p_recurse_children); + static void _parse_geometry(Transform3D p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children); static void _convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh); static void _build_recast_navigation_mesh( diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp index 768b419348..3a938200e9 100644 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp @@ -32,13 +32,15 @@ #include "core/io/file_access.h" -void AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) { - ERR_FAIL_COND(!active); +int AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) { + ERR_FAIL_COND_V(!active, 0); int todo = p_frames; int start_buffer = 0; + int frames_mixed_this_step = p_frames; + while (todo && active) { float *buffer = (float *)p_buffer; if (start_buffer > 0) { @@ -64,6 +66,7 @@ void AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_fra // we still have buffer to fill, start from this element in the next iteration. start_buffer = p_frames - todo; } else { + frames_mixed_this_step = p_frames - todo; for (int i = p_frames - todo; i < p_frames; i++) { p_buffer[i] = AudioFrame(0, 0); } @@ -72,6 +75,7 @@ void AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_fra } } } + return frames_mixed_this_step; } float AudioStreamPlaybackOGGVorbis::get_stream_sampling_rate() { diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.h b/modules/stb_vorbis/audio_stream_ogg_vorbis.h index 2bd70a2722..756c241d1f 100644 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.h +++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.h @@ -52,7 +52,7 @@ class AudioStreamPlaybackOGGVorbis : public AudioStreamPlaybackResampled { Ref<AudioStreamOGGVorbis> vorbis_stream; protected: - virtual void _mix_internal(AudioFrame *p_buffer, int p_frames) override; + virtual int _mix_internal(AudioFrame *p_buffer, int p_frames) override; virtual float get_stream_sampling_rate() override; public: diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub index d06c5c2f14..6691f86e60 100644 --- a/modules/text_server_adv/SCsub +++ b/modules/text_server_adv/SCsub @@ -15,7 +15,7 @@ def make_icu_data(target, source, env): 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("/* License & terms of use: https://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') @@ -38,7 +38,8 @@ def make_icu_data(target, source, env): # Thirdparty source files thirdparty_obj = [] -freetype_enabled = env.module_check_dependencies("text_server_adv", ["freetype"]) +freetype_enabled = env.module_check_dependencies("text_server_adv", ["freetype"], True) +msdngen_enabled = env.module_check_dependencies("text_server_adv", ["msdfgen"], True) if env["builtin_harfbuzz"]: env_harfbuzz = env_modules.Clone() @@ -509,6 +510,13 @@ env_text_server_adv.Append( ] ) +if msdngen_enabled: + env_text_server_adv.Append( + CPPPATH=[ + "#thirdparty/msdfgen", + ] + ) + if freetype_enabled: env_text_server_adv.Append( CPPPATH=[ diff --git a/modules/text_server_adv/bitmap_font_adv.cpp b/modules/text_server_adv/bitmap_font_adv.cpp deleted file mode 100644 index df7b42eac6..0000000000 --- a/modules/text_server_adv/bitmap_font_adv.cpp +++ /dev/null @@ -1,585 +0,0 @@ -/*************************************************************************/ -/* bitmap_font_adv.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 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.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.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; - } - - if (hb_handle) { - hb_font_destroy(hb_handle); - } - hb_handle = hb_bmp_font_create(this, base_size, nullptr); - valid = true; - - memdelete(f); - return OK; -} - -Error BitmapFontDataAdvanced::bitmap_new(float p_height, float p_ascent, int p_base_size) { - height = p_height; - ascent = p_ascent; - - base_size = p_base_size; - if (base_size == 0) { - base_size = height; - } - - char_map.clear(); - textures.clear(); - kerning_map.clear(); - if (hb_handle) { - hb_font_destroy(hb_handle); - } - hb_handle = hb_bmp_font_create(this, base_size, nullptr); - valid = true; - - return OK; -} - -void BitmapFontDataAdvanced::bitmap_add_texture(const Ref<Texture> &p_texture) { - ERR_FAIL_COND(!valid); - ERR_FAIL_COND_MSG(p_texture.is_null(), "It's not a reference to a valid Texture object."); - - textures.push_back(p_texture); -} - -void BitmapFontDataAdvanced::bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { - ERR_FAIL_COND(!valid); - - Character chr; - chr.rect = p_rect; - chr.texture_idx = p_texture_idx; - if (p_advance < 0) { - chr.advance.x = chr.rect.size.x; - } else { - chr.advance.x = p_advance; - } - chr.align = p_align; - char_map[p_char] = chr; -} - -void BitmapFontDataAdvanced::bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) { - ERR_FAIL_COND(!valid); - - KerningPairKey kpk; - kpk.A = p_A; - kpk.B = p_B; - - if (p_kerning == 0 && kerning_map.has(kpk)) { - kerning_map.erase(kpk); - } else { - kerning_map[kpk] = p_kerning; - } -} - -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); - return hb_handle; -} - -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)); -} - -float BitmapFontDataAdvanced::get_font_scale(int p_size) const { - return 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) { - Point2i cpos = p_pos; - cpos += (c->align + Vector2(0, -ascent)) * (float(p_size) / float(base_size)); - Size2i csize = c->rect.size * (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, csize), 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() { - if (hb_handle) { - hb_font_destroy(hb_handle); - } -} diff --git a/modules/text_server_adv/bitmap_font_adv.h b/modules/text_server_adv/bitmap_font_adv.h deleted file mode 100644 index 7b620021e1..0000000000 --- a/modules/text_server_adv/bitmap_font_adv.h +++ /dev/null @@ -1,123 +0,0 @@ -/*************************************************************************/ -/* bitmap_font_adv.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 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; - hb_font_t *hb_handle = nullptr; - - 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 bitmap_new(float p_height, float p_ascent, int p_base_size) override; - - virtual void bitmap_add_texture(const Ref<Texture> &p_texture) override; - virtual void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override; - virtual void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) 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 float get_font_scale(int p_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/dynamic_font_adv.cpp b/modules/text_server_adv/dynamic_font_adv.cpp deleted file mode 100644 index 62eedebb59..0000000000 --- a/modules/text_server_adv/dynamic_font_adv.cpp +++ /dev/null @@ -1,1030 +0,0 @@ -/*************************************************************************/ -/* dynamic_font_adv.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 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" - -#ifdef MODULE_FREETYPE_ENABLED - -#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.is_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 + "'."); - } - - uint64_t len = f->get_length(); - 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 = (-FT_MulFix(fds->face->underline_position, fds->face->size->metrics.y_scale) / 64.0) / oversampling * fds->scale_color_font; - fds->underline_thickness = (FT_MulFix(fds->face->underline_thickness, fds->face->size->metrics.y_scale) / 64.0) / oversampling * fds->scale_color_font; - - //Load os2 TTF table - 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.is_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, nullptr, nullptr); - 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, nullptr, nullptr); - 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.instantiate(); - 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).round(); - chr.advance = (advance * p_data->scale_color_font / oversampling).round(); - 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, nullptr, nullptr); - 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, nullptr, nullptr); - 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) { - Point2i 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) { - Point2i 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; -} - -bool DynamicFontDataAdvanced::get_glyph_contours(int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, false); - - int error = FT_Load_Glyph(fds->face, p_index, FT_LOAD_NO_BITMAP | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)); - ERR_FAIL_COND_V(error, false); - - r_points.clear(); - r_contours.clear(); - - float h = fds->ascent; - float scale = (1.0 / 64.0) / oversampling * fds->scale_color_font; - for (short i = 0; i < fds->face->glyph->outline.n_points; i++) { - r_points.push_back(Vector3(fds->face->glyph->outline.points[i].x * scale, h - fds->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fds->face->glyph->outline.tags[i]))); - } - for (short i = 0; i < fds->face->glyph->outline.n_contours; i++) { - r_contours.push_back(fds->face->glyph->outline.contours[i]); - } - r_orientation = (FT_Outline_Get_Orientation(&fds->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT); - return true; -} - -DynamicFontDataAdvanced::~DynamicFontDataAdvanced() { - clear_cache(); - if (library != nullptr) { - FT_Done_FreeType(library); - } -} - -#endif // MODULE_FREETYPE_ENABLED diff --git a/modules/text_server_adv/dynamic_font_adv.h b/modules/text_server_adv/dynamic_font_adv.h deleted file mode 100644 index b3f97bb029..0000000000 --- a/modules/text_server_adv/dynamic_font_adv.h +++ /dev/null @@ -1,195 +0,0 @@ -/*************************************************************************/ -/* dynamic_font_adv.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 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 "modules/modules_enabled.gen.h" -#ifdef MODULE_FREETYPE_ENABLED - -#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 = 0; - }; - bool operator<(CacheID right) const { - return key < right.key; - } - }; - - 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.0; - float descent = 0.0; - float underline_position = 0.0; - float underline_thickness = 0.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 bool get_glyph_contours(int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override; - - virtual ~DynamicFontDataAdvanced() override; -}; - -#endif // MODULE_FREETYPE_ENABLED - -#endif // DYNAMIC_FONT_ADV_H diff --git a/modules/text_server_adv/font_adv.h b/modules/text_server_adv/font_adv.h deleted file mode 100644 index 4fadefc569..0000000000 --- a/modules/text_server_adv/font_adv.h +++ /dev/null @@ -1,115 +0,0 @@ -/*************************************************************************/ -/* font_adv.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 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; - int spacing_space = 0; - int spacing_glyph = 0; - - virtual void clear_cache() = 0; - - virtual Error load_from_file(const String &p_filename, int p_base_size) { return ERR_CANT_CREATE; }; - virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) { return ERR_CANT_CREATE; }; - virtual Error bitmap_new(float p_height, float p_ascent, int p_base_size) { return ERR_CANT_CREATE; }; - - virtual void bitmap_add_texture(const Ref<Texture> &p_texture) { ERR_FAIL_MSG("Not supported."); }; - virtual void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { ERR_FAIL_MSG("Not supported."); }; - virtual void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) { ERR_FAIL_MSG("Not supported."); }; - - 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 int get_spacing_space() const { return spacing_space; }; - virtual void set_spacing_space(int p_value) { - spacing_space = p_value; - clear_cache(); - }; - - virtual int get_spacing_glyph() const { return spacing_glyph; }; - virtual void set_spacing_glyph(int p_value) { - spacing_glyph = p_value; - clear_cache(); - }; - - 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 true; }; - virtual bool is_script_supported(uint32_t p_script) const { return true; }; - - 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 bool get_glyph_contours(int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const { return false; }; - - virtual ~FontDataAdvanced(){}; -}; - -#endif // FONT_ADV_H diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index fa4888f843..78a87be971 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -29,15 +29,213 @@ /*************************************************************************/ #include "text_server_adv.h" -#include "bitmap_font_adv.h" -#include "dynamic_font_adv.h" +#include "core/string/print_string.h" #include "core/string/translation.h" #ifdef ICU_STATIC_DATA #include "thirdparty/icu4c/icudata.gen.h" #endif +#ifdef MODULE_MSDFGEN_ENABLED +#include "core/ShapeDistanceFinder.h" +#include "core/contour-combiners.h" +#include "core/edge-selectors.h" +#include "msdfgen.h" +#endif + +/*************************************************************************/ +/* hb_bmp_font_t HarfBuzz Bitmap font interface */ +/*************************************************************************/ + +hb_font_funcs_t *TextServerAdvanced::funcs = nullptr; + +TextServerAdvanced::hb_bmp_font_t *TextServerAdvanced::_hb_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref) { + hb_bmp_font_t *bm_font = memnew(hb_bmp_font_t); + + if (!bm_font) { + return nullptr; + } + + bm_font->face = p_face; + bm_font->unref = p_unref; + + return bm_font; +} + +void TextServerAdvanced::_hb_bmp_font_destroy(void *p_data) { + hb_bmp_font_t *bm_font = reinterpret_cast<hb_bmp_font_t *>(p_data); + memdelete(bm_font); +} + +hb_bool_t TextServerAdvanced::hb_bmp_get_nominal_glyph(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_unicode, hb_codepoint_t *r_glyph, void *p_user_data) { + const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data); + + if (!bm_font->face) { + return false; + } + + if (!bm_font->face->glyph_map.has(p_unicode)) { + if (bm_font->face->glyph_map.has(0xF000u + p_unicode)) { + *r_glyph = 0xF000u + p_unicode; + return true; + } else { + return false; + } + } + + *r_glyph = p_unicode; + return true; +} + +hb_position_t TextServerAdvanced::hb_bmp_get_glyph_h_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data) { + const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data); + + if (!bm_font->face) { + return 0; + } + + if (!bm_font->face->glyph_map.has(p_glyph)) { + return 0; + } + + return bm_font->face->glyph_map[p_glyph].advance.x * 64; +} + +hb_position_t TextServerAdvanced::hb_bmp_get_glyph_v_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data) { + const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data); + + if (!bm_font->face) { + return 0; + } + + if (!bm_font->face->glyph_map.has(p_glyph)) { + return 0; + } + + return -bm_font->face->glyph_map[p_glyph].advance.y * 64; +} + +hb_position_t TextServerAdvanced::hb_bmp_get_glyph_h_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data) { + const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data); + + if (!bm_font->face) { + return 0; + } + + if (!bm_font->face->kerning_map.has(Vector2i(p_left_glyph, p_right_glyph))) { + return 0; + } + + return bm_font->face->kerning_map[Vector2i(p_left_glyph, p_right_glyph)].x * 64; +} + +hb_position_t TextServerAdvanced::hb_bmp_get_glyph_v_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data) { + const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data); + + if (!bm_font->face) { + return 0; + } + + if (!bm_font->face->kerning_map.has(Vector2i(p_left_glyph, p_right_glyph))) { + return 0; + } + + return bm_font->face->kerning_map[Vector2i(p_left_glyph, p_right_glyph)].y * 64; +} + +hb_bool_t TextServerAdvanced::hb_bmp_get_glyph_v_origin(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_position_t *r_x, hb_position_t *r_y, void *p_user_data) { + const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data); + + if (!bm_font->face) { + return false; + } + + if (!bm_font->face->glyph_map.has(p_glyph)) { + return false; + } + + *r_x = bm_font->face->glyph_map[p_glyph].advance.x * 32; + *r_y = -bm_font->face->ascent * 64; + + return true; +} + +hb_bool_t TextServerAdvanced::hb_bmp_get_glyph_extents(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_glyph_extents_t *r_extents, void *p_user_data) { + const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data); + + if (!bm_font->face) { + return false; + } + + if (!bm_font->face->glyph_map.has(p_glyph)) { + return false; + } + + r_extents->x_bearing = 0; + r_extents->y_bearing = 0; + r_extents->width = bm_font->face->glyph_map[p_glyph].rect.size.x * 64; + r_extents->height = bm_font->face->glyph_map[p_glyph].rect.size.y * 64; + + return true; +} + +hb_bool_t TextServerAdvanced::hb_bmp_get_font_h_extents(hb_font_t *p_font, void *p_font_data, hb_font_extents_t *r_metrics, void *p_user_data) { + const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data); + + if (!bm_font->face) { + return false; + } + + r_metrics->ascender = bm_font->face->ascent; + r_metrics->descender = bm_font->face->descent; + r_metrics->line_gap = 0; + + return true; +} + +void TextServerAdvanced::hb_bmp_create_font_funcs() { + if (funcs == nullptr) { + 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_nominal_glyph_func(funcs, hb_bmp_get_nominal_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_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_make_immutable(funcs); + } +} + +void TextServerAdvanced::hb_bmp_free_font_funcs() { + if (funcs != nullptr) { + hb_font_funcs_destroy(funcs); + funcs = nullptr; + } +} + +void TextServerAdvanced::_hb_bmp_font_set_funcs(hb_font_t *p_font, TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref) { + hb_font_set_funcs(p_font, funcs, _hb_bmp_font_create(p_face, p_unref), _hb_bmp_font_destroy); +} + +hb_font_t *TextServerAdvanced::hb_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, 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, false); + return font; +} + +/*************************************************************************/ +/* Character properties. */ +/*************************************************************************/ + _FORCE_INLINE_ bool is_ain(char32_t p_chr) { return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_AIN; } @@ -497,7 +695,7 @@ static FeatureInfo feature_set[] = { { 0, String() }, }; -int32_t TextServerAdvanced::name_to_tag(const String &p_name) { +int32_t TextServerAdvanced::name_to_tag(const String &p_name) const { for (int i = 0; feature_set[i].tag != 0; i++) { if (feature_set[i].name == p_name) { return feature_set[i].tag; @@ -508,7 +706,7 @@ int32_t TextServerAdvanced::name_to_tag(const String &p_name) { return hb_tag_from_string(p_name.replace("custom_", "").ascii().get_data(), -1); } -String TextServerAdvanced::tag_to_name(int32_t p_tag) { +String TextServerAdvanced::tag_to_name(int32_t p_tag) const { for (int i = 0; feature_set[i].tag != 0; i++) { if (feature_set[i].tag == p_tag) { return feature_set[i].name; @@ -523,426 +721,2094 @@ String TextServerAdvanced::tag_to_name(int32_t p_tag) { } /*************************************************************************/ -/* Font interface */ +/* Font Glyph Rendering */ /*************************************************************************/ -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."); +_FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::find_texture_pos_for_glyph(FontDataForSizeAdvanced *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) const { + FontTexturePosition ret; + ret.index = -1; + + int mw = p_width; + int mh = p_height; + + for (int i = 0; i < p_data->textures.size(); i++) { + const FontTexture &ct = p_data->textures[i]; + + if (RenderingServer::get_singleton() != nullptr) { + if (ct.texture->get_format() != p_image_format) { + continue; + } + } + + if (mw > ct.texture_w || mh > ct.texture_h) { // Too big for this texture. + continue; + } + + ret.y = 0x7FFFFFFF; + ret.x = 0; + + for (int j = 0; j < ct.texture_w - 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_h) { + 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.x * p_data->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); + + FontTexture tex; + tex.texture_w = texsize; + tex.texture_h = texsize; + tex.format = p_image_format; + tex.imgdata.resize(texsize * texsize * p_color_size); + + { + // 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, BW font. + 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, Color font, Multichannel(+True) SDF. + 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; } -RID TextServerAdvanced::create_font_resource(const String &p_filename, int p_base_size) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = nullptr; - if (p_filename.get_extension() == "fnt" || p_filename.get_extension() == "font") { - fd = memnew(BitmapFontDataAdvanced); +#ifdef MODULE_MSDFGEN_ENABLED + +struct MSContext { + msdfgen::Point2 position; + msdfgen::Shape *shape; + msdfgen::Contour *contour; +}; + +class DistancePixelConversion { + double invRange; + +public: + _FORCE_INLINE_ explicit DistancePixelConversion(double range) : + invRange(1 / range) {} + _FORCE_INLINE_ void operator()(float *pixels, const msdfgen::MultiAndTrueDistance &distance) const { + pixels[0] = float(invRange * distance.r + .5); + pixels[1] = float(invRange * distance.g + .5); + pixels[2] = float(invRange * distance.b + .5); + pixels[3] = float(invRange * distance.a + .5); + } +}; + +struct MSDFThreadData { + msdfgen::Bitmap<float, 4> *output; + msdfgen::Shape *shape; + msdfgen::Projection *projection; + DistancePixelConversion *distancePixelConversion; +}; + +static msdfgen::Point2 ft_point2(const FT_Vector &vector) { + return msdfgen::Point2(vector.x / 60.0f, vector.y / 60.0f); +} + +static int ft_move_to(const FT_Vector *to, void *user) { + MSContext *context = reinterpret_cast<MSContext *>(user); + if (!(context->contour && context->contour->edges.empty())) { + context->contour = &context->shape->addContour(); + } + context->position = ft_point2(*to); + return 0; +} + +static int ft_line_to(const FT_Vector *to, void *user) { + MSContext *context = reinterpret_cast<MSContext *>(user); + msdfgen::Point2 endpoint = ft_point2(*to); + if (endpoint != context->position) { + context->contour->addEdge(new msdfgen::LinearSegment(context->position, endpoint)); + context->position = endpoint; + } + return 0; +} + +static int ft_conic_to(const FT_Vector *control, const FT_Vector *to, void *user) { + MSContext *context = reinterpret_cast<MSContext *>(user); + context->contour->addEdge(new msdfgen::QuadraticSegment(context->position, ft_point2(*control), ft_point2(*to))); + context->position = ft_point2(*to); + return 0; +} + +static int ft_cubic_to(const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to, void *user) { + MSContext *context = reinterpret_cast<MSContext *>(user); + context->contour->addEdge(new msdfgen::CubicSegment(context->position, ft_point2(*control1), ft_point2(*control2), ft_point2(*to))); + context->position = ft_point2(*to); + return 0; +} + +void TextServerAdvanced::_generateMTSDF_threaded(uint32_t y, void *p_td) const { + MSDFThreadData *td = (MSDFThreadData *)p_td; + + msdfgen::ShapeDistanceFinder<msdfgen::OverlappingContourCombiner<msdfgen::MultiAndTrueDistanceSelector>> distanceFinder(*td->shape); + int row = td->shape->inverseYAxis ? td->output->height() - y - 1 : y; + for (int col = 0; col < td->output->width(); ++col) { + int x = (y % 2) ? td->output->width() - col - 1 : col; + msdfgen::Point2 p = td->projection->unproject(msdfgen::Point2(x + .5, y + .5)); + msdfgen::MultiAndTrueDistance distance = distanceFinder.distance(p); + td->distancePixelConversion->operator()(td->output->operator()(x, row), distance); + } +} + +_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(FontDataAdvanced *p_font_data, FontDataForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const { + msdfgen::Shape shape; + + shape.contours.clear(); + shape.inverseYAxis = false; + + MSContext context = {}; + context.shape = &shape; + FT_Outline_Funcs ft_functions; + ft_functions.move_to = &ft_move_to; + ft_functions.line_to = &ft_line_to; + ft_functions.conic_to = &ft_conic_to; + ft_functions.cubic_to = &ft_cubic_to; + ft_functions.shift = 0; + ft_functions.delta = 0; + + int error = FT_Outline_Decompose(outline, &ft_functions, &context); + ERR_FAIL_COND_V_MSG(error, FontGlyph(), "FreeType: Outline decomposition error: '" + String(FT_Error_String(error)) + "'."); + if (!shape.contours.empty() && shape.contours.back().edges.empty()) { + shape.contours.pop_back(); + } + + if (FT_Outline_Get_Orientation(outline) == 1) { + for (int i = 0; i < (int)shape.contours.size(); ++i) { + shape.contours[i].reverse(); + } + } + + shape.inverseYAxis = true; + shape.normalize(); + + msdfgen::Shape::Bounds bounds = shape.getBounds(p_pixel_range); + + FontGlyph chr; + chr.found = true; + chr.advance = advance.round(); + + if (shape.validate() && shape.contours.size() > 0) { + int w = (bounds.r - bounds.l); + int h = (bounds.t - bounds.b); + + int mw = w + p_rect_margin * 2; + int mh = h + p_rect_margin * 2; + + ERR_FAIL_COND_V(mw > 4096, FontGlyph()); + ERR_FAIL_COND_V(mh > 4096, FontGlyph()); + + FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, 4, Image::FORMAT_RGBA8, mw, mh); + ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph()); + FontTexture &tex = p_data->textures.write[tex_pos.index]; + + edgeColoringSimple(shape, 3.0); // Max. angle. + msdfgen::Bitmap<float, 4> image(w, h); // Texture size. + //msdfgen::generateMTSDF(image, shape, p_pixel_range, 1.0, msdfgen::Vector2(-bounds.l, -bounds.b)); // Range, scale, translation. + + DistancePixelConversion distancePixelConversion(p_pixel_range); + msdfgen::Projection projection(msdfgen::Vector2(1.0, 1.0), msdfgen::Vector2(-bounds.l, -bounds.b)); + msdfgen::MSDFGeneratorConfig config(true, msdfgen::ErrorCorrectionConfig()); + + MSDFThreadData td; + td.output = ℑ + td.shape = &shape; + td.projection = &projection; + td.distancePixelConversion = &distancePixelConversion; + + if (p_font_data->work_pool.get_thread_count() == 0) { + p_font_data->work_pool.init(); + } + p_font_data->work_pool.do_work(h, this, &TextServerAdvanced::_generateMTSDF_threaded, &td); + + msdfgen::msdfErrorCorrection(image, shape, projection, p_pixel_range, config); + + { + 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 + p_rect_margin) * tex.texture_w + j + tex_pos.x + p_rect_margin) * 4; + ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), FontGlyph()); + wr[ofs + 0] = (uint8_t)(CLAMP(image(j, i)[0] * 256.f, 0.f, 255.f)); + wr[ofs + 1] = (uint8_t)(CLAMP(image(j, i)[1] * 256.f, 0.f, 255.f)); + wr[ofs + 2] = (uint8_t)(CLAMP(image(j, i)[2] * 256.f, 0.f, 255.f)); + wr[ofs + 3] = (uint8_t)(CLAMP(image(j, i)[3] * 256.f, 0.f, 255.f)); + //wr[ofs + 0] = 100; + //wr[ofs + 1] = 100; + //wr[ofs + 2] = 100; + //wr[ofs + 3] = 100; + } + } + } + + // Blit to image and texture. + { + if (RenderingServer::get_singleton() != nullptr) { + Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, Image::FORMAT_RGBA8, tex.imgdata)); + if (tex.texture.is_null()) { + tex.texture.instantiate(); + tex.texture->create_from_image(img); + } else { + tex.texture->update(img); + } + } + } + + // Update height array. + for (int k = tex_pos.x; k < tex_pos.x + mw; k++) { + tex.offsets.write[k] = tex_pos.y + mh; + } + + chr.texture_idx = tex_pos.index; + + chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w, h); + chr.rect.position = Vector2(bounds.l, -bounds.t); + chr.rect.size = chr.uv_rect.size; + } + return chr; +} +#endif + #ifdef MODULE_FREETYPE_ENABLED - } else if (p_filename.get_extension() == "ttf" || p_filename.get_extension() == "otf" || p_filename.get_extension() == "woff") { - fd = memnew(DynamicFontDataAdvanced); +_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitmap(FontDataForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const { + int w = bitmap.width; + int h = bitmap.rows; + + int mw = w + p_rect_margin * 2; + int mh = h + p_rect_margin * 2; + + ERR_FAIL_COND_V(mw > 4096, FontGlyph()); + ERR_FAIL_COND_V(mh > 4096, FontGlyph()); + + 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; + + FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, color_size, require_format, mw, mh); + ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph()); + + // Fit character in char texture. + + FontTexture &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 + p_rect_margin) * tex.texture_w + j + tex_pos.x + p_rect_margin) * color_size; + ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), FontGlyph()); + 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]; + //wr[ofs + 1] = 100; + 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; + default: + ERR_FAIL_V_MSG(FontGlyph(), "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_w, tex.texture_h, 0, require_format, tex.imgdata)); + + if (tex.texture.is_null()) { + tex.texture.instantiate(); + tex.texture->create_from_image(img); + } else { + tex.texture->update(img); + } + } + } + + // Update height array. + for (int k = tex_pos.x; k < tex_pos.x + mw; k++) { + tex.offsets.write[k] = tex_pos.y + mh; + } + + FontGlyph chr; + chr.advance = (advance * p_data->scale / p_data->oversampling).round(); + chr.texture_idx = tex_pos.index; + chr.found = true; + + chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w, h); + chr.rect.position = (Vector2(xofs, -yofs) * p_data->scale / p_data->oversampling).round(); + chr.rect.size = chr.uv_rect.size * p_data->scale / p_data->oversampling; + return chr; +} #endif - } else { - return RID(); + +/*************************************************************************/ +/* Font Cache */ +/*************************************************************************/ + +_FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontDataAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(!_ensure_cache_for_size(p_font_data, p_size), false); + + FontDataForSizeAdvanced *fd = p_font_data->cache[p_size]; + if (fd->glyph_map.has(p_glyph)) { + return fd->glyph_map[p_glyph].found; } - Error err = fd->load_from_file(p_filename, p_base_size); - if (err != OK) { - memdelete(fd); - return RID(); + if (p_glyph == 0) { // Non graphical or invalid glyph, do not render. + fd->glyph_map[p_glyph] = FontGlyph(); + return true; } - return font_owner.make_rid(fd); +#ifdef MODULE_FREETYPE_ENABLED + FontGlyph gl; + if (fd->face) { + FT_Int32 flags = FT_LOAD_DEFAULT; + + bool outline = p_size.y > 0; + switch (p_font_data->hinting) { + case TextServer::HINTING_NONE: + flags |= FT_LOAD_NO_HINTING; + break; + case TextServer::HINTING_LIGHT: + flags |= FT_LOAD_TARGET_LIGHT; + break; + default: + flags |= FT_LOAD_TARGET_NORMAL; + break; + } + if (p_font_data->force_autohinter) { + flags |= FT_LOAD_FORCE_AUTOHINT; + } + if (outline) { + flags |= FT_LOAD_NO_BITMAP; + } else if (FT_HAS_COLOR(fd->face)) { + flags |= FT_LOAD_COLOR; + } + + FT_Fixed v, h; + FT_Get_Advance(fd->face, p_glyph, flags, &h); + FT_Get_Advance(fd->face, p_glyph, flags | FT_LOAD_VERTICAL_LAYOUT, &v); + + int error = FT_Load_Glyph(fd->face, p_glyph, flags); + if (error) { + fd->glyph_map[p_glyph] = FontGlyph(); + ERR_FAIL_V_MSG(false, "FreeType: Failed to load glyph."); + } + + if (!outline) { + if (!p_font_data->msdf) { + error = FT_Render_Glyph(fd->face->glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO); + } + FT_GlyphSlot slot = fd->face->glyph; + if (!error) { + if (p_font_data->msdf) { +#ifdef MODULE_MSDFGEN_ENABLED + gl = rasterize_msdf(p_font_data, fd, p_font_data->msdf_range, rect_range, &slot->outline, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0); +#else + fd->glyph_map[p_glyph] = FontGlyph(); + ERR_FAIL_V_MSG(false, "Compiled without MSDFGEN support!"); +#endif + } else { + gl = rasterize_bitmap(fd, rect_range, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0); + } + } + } else { + FT_Stroker stroker; + if (FT_Stroker_New(library, &stroker) != 0) { + fd->glyph_map[p_glyph] = FontGlyph(); + ERR_FAIL_V_MSG(false, "FreeType: Failed to load glyph stroker."); + } + + FT_Stroker_Set(stroker, (int)(fd->size.y * fd->oversampling * 16.0), FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0); + FT_Glyph glyph; + FT_BitmapGlyph glyph_bitmap; + + if (FT_Get_Glyph(fd->face->glyph, &glyph) != 0) { + goto cleanup_stroker; + } + if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) { + goto cleanup_glyph; + } + if (FT_Glyph_To_Bitmap(&glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1) != 0) { + goto cleanup_glyph; + } + glyph_bitmap = (FT_BitmapGlyph)glyph; + gl = rasterize_bitmap(fd, rect_range, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2()); + + cleanup_glyph: + FT_Done_Glyph(glyph); + cleanup_stroker: + FT_Stroker_Done(stroker); + } + fd->glyph_map[p_glyph] = gl; + return gl.found; + } +#endif + fd->glyph_map[p_glyph] = FontGlyph(); + return false; } -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 == "fnt" || p_type == "font") { - fd = memnew(BitmapFontDataAdvanced); +_FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced *p_font_data, const Vector2i &p_size) const { + if (p_font_data->cache.has(p_size)) { + return true; + } + + FontDataForSizeAdvanced *fd = memnew(FontDataForSizeAdvanced); + fd->size = p_size; + if (p_font_data->data_ptr) { + // Init dynamic font. #ifdef MODULE_FREETYPE_ENABLED - } else if (p_type == "ttf" || p_type == "otf" || p_type == "woff") { - fd = memnew(DynamicFontDataAdvanced); + int error = 0; + if (!library) { + error = FT_Init_FreeType(&library); + ERR_FAIL_COND_V_MSG(error != 0, false, TTR("FreeType: Error initializing library:") + " '" + String(FT_Error_String(error)) + "'."); + } + + memset(&fd->stream, 0, sizeof(FT_StreamRec)); + fd->stream.base = (unsigned char *)p_font_data->data_ptr; + fd->stream.size = p_font_data->data_size; + fd->stream.pos = 0; + + FT_Open_Args fargs; + memset(&fargs, 0, sizeof(FT_Open_Args)); + fargs.memory_base = (unsigned char *)p_font_data->data_ptr; + fargs.memory_size = p_font_data->data_size; + fargs.flags = FT_OPEN_MEMORY; + fargs.stream = &fd->stream; + error = FT_Open_Face(library, &fargs, 0, &fd->face); + if (error) { + FT_Done_Face(fd->face); + fd->face = nullptr; + ERR_FAIL_V_MSG(false, TTR("FreeType: Error loading font:") + " '" + String(FT_Error_String(error)) + "'."); + } + fd->hb_handle = hb_ft_font_create(fd->face, nullptr); + if (fd->hb_handle == nullptr) { + FT_Done_Face(fd->face); + fd->face = nullptr; + ERR_FAIL_V_MSG(false, TTR("HarfBuzz: Error creating FreeType font object.")); + } + + if (p_font_data->msdf) { + fd->oversampling = 1.0f; + fd->size.x = p_font_data->msdf_source_size; + } else if (p_font_data->oversampling <= 0.0f) { + fd->oversampling = TS->font_get_global_oversampling(); + } else { + fd->oversampling = p_font_data->oversampling; + } + + if (FT_HAS_COLOR(fd->face) && fd->face->num_fixed_sizes > 0) { + int best_match = 0; + int diff = ABS(fd->size.x - ((int64_t)fd->face->available_sizes[0].width)); + fd->scale = real_t(fd->size.x * fd->oversampling) / fd->face->available_sizes[0].width; + for (int i = 1; i < fd->face->num_fixed_sizes; i++) { + int ndiff = ABS(fd->size.x - ((int64_t)fd->face->available_sizes[i].width)); + if (ndiff < diff) { + best_match = i; + diff = ndiff; + fd->scale = real_t(fd->size.x * fd->oversampling) / fd->face->available_sizes[i].width; + } + } + FT_Select_Size(fd->face, best_match); + } else { + FT_Set_Pixel_Sizes(fd->face, 0, fd->size.x * fd->oversampling); + } + + fd->ascent = (fd->face->size->metrics.ascender / 64.0) / fd->oversampling * fd->scale; + fd->descent = (-fd->face->size->metrics.descender / 64.0) / fd->oversampling * fd->scale; + fd->underline_position = (-FT_MulFix(fd->face->underline_position, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale; + fd->underline_thickness = (FT_MulFix(fd->face->underline_thickness, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale; + + if (!p_font_data->face_init) { + // Get supported scripts from OpenType font data. + p_font_data->supported_scripts.clear(); + unsigned int count = hb_ot_layout_table_get_script_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GSUB, 0, nullptr, nullptr); + 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(fd->hb_handle), HB_OT_TAG_GSUB, 0, &count, script_tags); + for (unsigned int i = 0; i < count; i++) { + p_font_data->supported_scripts.insert(script_tags[i]); + } + memfree(script_tags); + } + count = hb_ot_layout_table_get_script_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GPOS, 0, nullptr, nullptr); + 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(fd->hb_handle), HB_OT_TAG_GPOS, 0, &count, script_tags); + for (unsigned int i = 0; i < count; i++) { + p_font_data->supported_scripts.insert(script_tags[i]); + } + memfree(script_tags); + } + + // Get supported scripts from OS2 table. + TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(fd->face, FT_SFNT_OS2); + if (os2) { + if ((os2->ulUnicodeRange1 & 1L << 4) || (os2->ulUnicodeRange1 & 1L << 5) || (os2->ulUnicodeRange1 & 1L << 6) || (os2->ulUnicodeRange1 & 1L << 31) || (os2->ulUnicodeRange2 & 1L << 0) || (os2->ulUnicodeRange2 & 1L << 1) || (os2->ulUnicodeRange2 & 1L << 2) || (os2->ulUnicodeRange2 & 1L << 3) || (os2->ulUnicodeRange2 & 1L << 4) || (os2->ulUnicodeRange2 & 1L << 5) || (os2->ulUnicodeRange2 & 1L << 6) || (os2->ulUnicodeRange2 & 1L << 7) || (os2->ulUnicodeRange2 & 1L << 8) || (os2->ulUnicodeRange2 & 1L << 9) || (os2->ulUnicodeRange2 & 1L << 10) || (os2->ulUnicodeRange2 & 1L << 11) || (os2->ulUnicodeRange2 & 1L << 12) || (os2->ulUnicodeRange2 & 1L << 13) || (os2->ulUnicodeRange2 & 1L << 14) || (os2->ulUnicodeRange2 & 1L << 15) || (os2->ulUnicodeRange2 & 1L << 30) || (os2->ulUnicodeRange3 & 1L << 0) || (os2->ulUnicodeRange3 & 1L << 1) || (os2->ulUnicodeRange3 & 1L << 2) || (os2->ulUnicodeRange3 & 1L << 4) || (os2->ulUnicodeRange3 & 1L << 5) || (os2->ulUnicodeRange3 & 1L << 18) || (os2->ulUnicodeRange3 & 1L << 24) || (os2->ulUnicodeRange3 & 1L << 25) || (os2->ulUnicodeRange3 & 1L << 26) || (os2->ulUnicodeRange3 & 1L << 27) || (os2->ulUnicodeRange3 & 1L << 28) || (os2->ulUnicodeRange4 & 1L << 3) || (os2->ulUnicodeRange4 & 1L << 6) || (os2->ulUnicodeRange4 & 1L << 15) || (os2->ulUnicodeRange4 & 1L << 23) || (os2->ulUnicodeRange4 & 1L << 24) || (os2->ulUnicodeRange4 & 1L << 26)) { + p_font_data->supported_scripts.insert(HB_SCRIPT_COMMON); + } + if ((os2->ulUnicodeRange1 & 1L << 0) || (os2->ulUnicodeRange1 & 1L << 1) || (os2->ulUnicodeRange1 & 1L << 2) || (os2->ulUnicodeRange1 & 1L << 3) || (os2->ulUnicodeRange1 & 1L << 29)) { + p_font_data->supported_scripts.insert(HB_SCRIPT_LATIN); + } + if ((os2->ulUnicodeRange1 & 1L << 7) || (os2->ulUnicodeRange1 & 1L << 30)) { + p_font_data->supported_scripts.insert(HB_SCRIPT_GREEK); + } + if (os2->ulUnicodeRange1 & 1L << 8) { + p_font_data->supported_scripts.insert(HB_SCRIPT_COPTIC); + } + if (os2->ulUnicodeRange1 & 1L << 9) { + p_font_data->supported_scripts.insert(HB_SCRIPT_CYRILLIC); + } + if (os2->ulUnicodeRange1 & 1L << 10) { + p_font_data->supported_scripts.insert(HB_SCRIPT_ARMENIAN); + } + if (os2->ulUnicodeRange1 & 1L << 11) { + p_font_data->supported_scripts.insert(HB_SCRIPT_HEBREW); + } + if (os2->ulUnicodeRange1 & 1L << 12) { + p_font_data->supported_scripts.insert(HB_SCRIPT_VAI); + } + if ((os2->ulUnicodeRange1 & 1L << 13) || (os2->ulUnicodeRange2 & 1L << 31) || (os2->ulUnicodeRange3 & 1L << 3)) { + p_font_data->supported_scripts.insert(HB_SCRIPT_ARABIC); + } + if (os2->ulUnicodeRange1 & 1L << 14) { + p_font_data->supported_scripts.insert(HB_SCRIPT_NKO); + } + if (os2->ulUnicodeRange1 & 1L << 15) { + p_font_data->supported_scripts.insert(HB_SCRIPT_DEVANAGARI); + } + if (os2->ulUnicodeRange1 & 1L << 16) { + p_font_data->supported_scripts.insert(HB_SCRIPT_BENGALI); + } + if (os2->ulUnicodeRange1 & 1L << 17) { + p_font_data->supported_scripts.insert(HB_SCRIPT_GURMUKHI); + } + if (os2->ulUnicodeRange1 & 1L << 18) { + p_font_data->supported_scripts.insert(HB_SCRIPT_GUJARATI); + } + if (os2->ulUnicodeRange1 & 1L << 19) { + p_font_data->supported_scripts.insert(HB_SCRIPT_ORIYA); + } + if (os2->ulUnicodeRange1 & 1L << 20) { + p_font_data->supported_scripts.insert(HB_SCRIPT_TAMIL); + } + if (os2->ulUnicodeRange1 & 1L << 21) { + p_font_data->supported_scripts.insert(HB_SCRIPT_TELUGU); + } + if (os2->ulUnicodeRange1 & 1L << 22) { + p_font_data->supported_scripts.insert(HB_SCRIPT_KANNADA); + } + if (os2->ulUnicodeRange1 & 1L << 23) { + p_font_data->supported_scripts.insert(HB_SCRIPT_MALAYALAM); + } + if (os2->ulUnicodeRange1 & 1L << 24) { + p_font_data->supported_scripts.insert(HB_SCRIPT_THAI); + } + if (os2->ulUnicodeRange1 & 1L << 25) { + p_font_data->supported_scripts.insert(HB_SCRIPT_LAO); + } + if (os2->ulUnicodeRange1 & 1L << 26) { + p_font_data->supported_scripts.insert(HB_SCRIPT_GEORGIAN); + } + if (os2->ulUnicodeRange1 & 1L << 27) { + p_font_data->supported_scripts.insert(HB_SCRIPT_BALINESE); + } + if ((os2->ulUnicodeRange1 & 1L << 28) || (os2->ulUnicodeRange2 & 1L << 20) || (os2->ulUnicodeRange2 & 1L << 24)) { + p_font_data->supported_scripts.insert(HB_SCRIPT_HANGUL); + } + if ((os2->ulUnicodeRange2 & 1L << 21) || (os2->ulUnicodeRange2 & 1L << 22) || (os2->ulUnicodeRange2 & 1L << 23) || (os2->ulUnicodeRange2 & 1L << 26) || (os2->ulUnicodeRange2 & 1L << 27) || (os2->ulUnicodeRange2 & 1L << 29)) { + p_font_data->supported_scripts.insert(HB_SCRIPT_HAN); + } + if (os2->ulUnicodeRange2 & 1L << 17) { + p_font_data->supported_scripts.insert(HB_SCRIPT_HIRAGANA); + } + if (os2->ulUnicodeRange2 & 1L << 18) { + p_font_data->supported_scripts.insert(HB_SCRIPT_KATAKANA); + } + if (os2->ulUnicodeRange2 & 1L << 19) { + p_font_data->supported_scripts.insert(HB_SCRIPT_BOPOMOFO); + } + if (os2->ulUnicodeRange3 & 1L << 6) { + p_font_data->supported_scripts.insert(HB_SCRIPT_TIBETAN); + } + if (os2->ulUnicodeRange3 & 1L << 7) { + p_font_data->supported_scripts.insert(HB_SCRIPT_SYRIAC); + } + if (os2->ulUnicodeRange3 & 1L << 8) { + p_font_data->supported_scripts.insert(HB_SCRIPT_THAANA); + } + if (os2->ulUnicodeRange3 & 1L << 9) { + p_font_data->supported_scripts.insert(HB_SCRIPT_SINHALA); + } + if (os2->ulUnicodeRange3 & 1L << 10) { + p_font_data->supported_scripts.insert(HB_SCRIPT_MYANMAR); + } + if (os2->ulUnicodeRange3 & 1L << 11) { + p_font_data->supported_scripts.insert(HB_SCRIPT_ETHIOPIC); + } + if (os2->ulUnicodeRange3 & 1L << 12) { + p_font_data->supported_scripts.insert(HB_SCRIPT_CHEROKEE); + } + if (os2->ulUnicodeRange3 & 1L << 13) { + p_font_data->supported_scripts.insert(HB_SCRIPT_CANADIAN_SYLLABICS); + } + if (os2->ulUnicodeRange3 & 1L << 14) { + p_font_data->supported_scripts.insert(HB_SCRIPT_OGHAM); + } + if (os2->ulUnicodeRange3 & 1L << 15) { + p_font_data->supported_scripts.insert(HB_SCRIPT_RUNIC); + } + if (os2->ulUnicodeRange3 & 1L << 16) { + p_font_data->supported_scripts.insert(HB_SCRIPT_KHMER); + } + if (os2->ulUnicodeRange3 & 1L << 17) { + p_font_data->supported_scripts.insert(HB_SCRIPT_MONGOLIAN); + } + if (os2->ulUnicodeRange3 & 1L << 19) { + p_font_data->supported_scripts.insert(HB_SCRIPT_YI); + } + if (os2->ulUnicodeRange3 & 1L << 20) { + p_font_data->supported_scripts.insert(HB_SCRIPT_HANUNOO); + p_font_data->supported_scripts.insert(HB_SCRIPT_TAGBANWA); + p_font_data->supported_scripts.insert(HB_SCRIPT_BUHID); + p_font_data->supported_scripts.insert(HB_SCRIPT_TAGALOG); + } + if (os2->ulUnicodeRange3 & 1L << 21) { + p_font_data->supported_scripts.insert(HB_SCRIPT_OLD_ITALIC); + } + if (os2->ulUnicodeRange3 & 1L << 22) { + p_font_data->supported_scripts.insert(HB_SCRIPT_GOTHIC); + } + if (os2->ulUnicodeRange3 & 1L << 23) { + p_font_data->supported_scripts.insert(HB_SCRIPT_DESERET); + } + if (os2->ulUnicodeRange3 & 1L << 29) { + p_font_data->supported_scripts.insert(HB_SCRIPT_LIMBU); + } + if (os2->ulUnicodeRange3 & 1L << 30) { + p_font_data->supported_scripts.insert(HB_SCRIPT_TAI_LE); + } + if (os2->ulUnicodeRange3 & 1L << 31) { + p_font_data->supported_scripts.insert(HB_SCRIPT_NEW_TAI_LUE); + } + if (os2->ulUnicodeRange4 & 1L << 0) { + p_font_data->supported_scripts.insert(HB_SCRIPT_BUGINESE); + } + if (os2->ulUnicodeRange4 & 1L << 1) { + p_font_data->supported_scripts.insert(HB_SCRIPT_GLAGOLITIC); + } + if (os2->ulUnicodeRange4 & 1L << 2) { + p_font_data->supported_scripts.insert(HB_SCRIPT_TIFINAGH); + } + if (os2->ulUnicodeRange4 & 1L << 4) { + p_font_data->supported_scripts.insert(HB_SCRIPT_SYLOTI_NAGRI); + } + if (os2->ulUnicodeRange4 & 1L << 5) { + p_font_data->supported_scripts.insert(HB_SCRIPT_LINEAR_B); + } + if (os2->ulUnicodeRange4 & 1L << 7) { + p_font_data->supported_scripts.insert(HB_SCRIPT_UGARITIC); + } + if (os2->ulUnicodeRange4 & 1L << 8) { + p_font_data->supported_scripts.insert(HB_SCRIPT_OLD_PERSIAN); + } + if (os2->ulUnicodeRange4 & 1L << 9) { + p_font_data->supported_scripts.insert(HB_SCRIPT_SHAVIAN); + } + if (os2->ulUnicodeRange4 & 1L << 10) { + p_font_data->supported_scripts.insert(HB_SCRIPT_OSMANYA); + } + if (os2->ulUnicodeRange4 & 1L << 11) { + p_font_data->supported_scripts.insert(HB_SCRIPT_CYPRIOT); + } + if (os2->ulUnicodeRange4 & 1L << 12) { + p_font_data->supported_scripts.insert(HB_SCRIPT_KHAROSHTHI); + } + if (os2->ulUnicodeRange4 & 1L << 13) { + p_font_data->supported_scripts.insert(HB_SCRIPT_TAI_VIET); + } + if (os2->ulUnicodeRange4 & 1L << 14) { + p_font_data->supported_scripts.insert(HB_SCRIPT_CUNEIFORM); + } + if (os2->ulUnicodeRange4 & 1L << 16) { + p_font_data->supported_scripts.insert(HB_SCRIPT_SUNDANESE); + } + if (os2->ulUnicodeRange4 & 1L << 17) { + p_font_data->supported_scripts.insert(HB_SCRIPT_LEPCHA); + } + if (os2->ulUnicodeRange4 & 1L << 18) { + p_font_data->supported_scripts.insert(HB_SCRIPT_OL_CHIKI); + } + if (os2->ulUnicodeRange4 & 1L << 19) { + p_font_data->supported_scripts.insert(HB_SCRIPT_SAURASHTRA); + } + if (os2->ulUnicodeRange4 & 1L << 20) { + p_font_data->supported_scripts.insert(HB_SCRIPT_KAYAH_LI); + } + if (os2->ulUnicodeRange4 & 1L << 21) { + p_font_data->supported_scripts.insert(HB_SCRIPT_REJANG); + } + if (os2->ulUnicodeRange4 & 1L << 22) { + p_font_data->supported_scripts.insert(HB_SCRIPT_CHAM); + } + if (os2->ulUnicodeRange4 & 1L << 25) { + p_font_data->supported_scripts.insert(HB_SCRIPT_ANATOLIAN_HIEROGLYPHS); + } + } + + // Read OpenType feature tags. + p_font_data->supported_features.clear(); + count = hb_ot_layout_table_get_feature_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GSUB, 0, nullptr, nullptr); + 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(fd->hb_handle), HB_OT_TAG_GSUB, 0, &count, feature_tags); + for (unsigned int i = 0; i < count; i++) { + p_font_data->supported_features[feature_tags[i]] = 1; + } + memfree(feature_tags); + } + count = hb_ot_layout_table_get_feature_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GPOS, 0, nullptr, nullptr); + 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(fd->hb_handle), HB_OT_TAG_GPOS, 0, &count, feature_tags); + for (unsigned int i = 0; i < count; i++) { + p_font_data->supported_features[feature_tags[i]] = 1; + } + memfree(feature_tags); + } + + // Read OpenType variations. + p_font_data->supported_varaitions.clear(); + if (fd->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) { + FT_MM_Var *amaster; + FT_Get_MM_Var(fd->face, &amaster); + for (FT_UInt i = 0; i < amaster->num_axis; i++) { + p_font_data->supported_varaitions[(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); + } + p_font_data->face_init = true; + } + + // Write variations. + if (fd->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) { + FT_MM_Var *amaster; + + FT_Get_MM_Var(fd->face, &amaster); + + Vector<hb_variation_t> hb_vars; + Vector<FT_Fixed> coords; + coords.resize(amaster->num_axis); + + FT_Get_Var_Design_Coordinates(fd->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 (p_font_data->variation_coordinates.has(var.tag)) { + var.value = p_font_data->variation_coordinates[var.tag]; + coords.write[i] = CLAMP(var.value * 65536.f, amaster->axis[i].minimum, amaster->axis[i].maximum); + } + + if (p_font_data->variation_coordinates.has(tag_to_name(var.tag))) { + var.value = p_font_data->variation_coordinates[tag_to_name(var.tag)]; + coords.write[i] = CLAMP(var.value * 65536.f, amaster->axis[i].minimum, amaster->axis[i].maximum); + } + + hb_vars.push_back(var); + } + + FT_Set_Var_Design_Coordinates(fd->face, coords.size(), coords.ptrw()); + hb_font_set_variations(fd->hb_handle, hb_vars.is_empty() ? nullptr : &hb_vars[0], hb_vars.size()); + FT_Done_MM_Var(library, amaster); + } +#else + ERR_FAIL_V_MSG(false, TTR("FreeType: Can't load dynamic font, engine is compiled without FreeType support!"); #endif } else { - return RID(); + // Init bitmap font. + fd->hb_handle = hb_bmp_font_create(fd, nullptr); + if (!fd->hb_handle) { + ERR_FAIL_V_MSG(false, TTR("HarfBuzz: Error creating bitmap font object.")); + } } + p_font_data->cache[p_size] = fd; + return true; +} - Error err = fd->load_from_memory(p_data, p_size, p_base_size); - if (err != OK) { - memdelete(fd); - return RID(); +_FORCE_INLINE_ void TextServerAdvanced::_font_clear_cache(FontDataAdvanced *p_font_data) { + for (const Map<Vector2i, FontDataForSizeAdvanced *>::Element *E = p_font_data->cache.front(); E; E = E->next()) { + memdelete(E->get()); } + p_font_data->cache.clear(); + p_font_data->face_init = false; + p_font_data->supported_features.clear(); + p_font_data->supported_varaitions.clear(); + p_font_data->supported_scripts.clear(); +} + +hb_font_t *TextServerAdvanced::_font_get_hb_handle(RID p_font_rid, int p_size) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, nullptr); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), nullptr); + + return fd->cache[size]->hb_handle; +} + +RID TextServerAdvanced::create_font() { + FontDataAdvanced *fd = memnew(FontDataAdvanced); return font_owner.make_rid(fd); } -RID TextServerAdvanced::create_font_bitmap(float p_height, float p_ascent, int p_base_size) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = memnew(BitmapFontDataAdvanced); - Error err = fd->bitmap_new(p_height, p_ascent, p_base_size); - if (err != OK) { - memdelete(fd); - return RID(); +void TextServerAdvanced::font_set_data(RID p_font_rid, const PackedByteArray &p_data) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + _font_clear_cache(fd); + fd->data = p_data; + fd->data_ptr = fd->data.ptr(); + fd->data_size = fd->data.size(); +} + +void TextServerAdvanced::font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + _font_clear_cache(fd); + fd->data.clear(); + fd->data_ptr = p_data_ptr; + fd->data_size = p_data_size; +} + +void TextServerAdvanced::font_set_antialiased(RID p_font_rid, bool p_antialiased) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->antialiased != p_antialiased) { + _font_clear_cache(fd); + fd->antialiased = p_antialiased; } +} - return font_owner.make_rid(fd); +bool TextServerAdvanced::font_is_antialiased(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->antialiased; } -void TextServerAdvanced::font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +void TextServerAdvanced::font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->bitmap_add_texture(p_texture); + + MutexLock lock(fd->mutex); + if (fd->msdf != p_msdf) { + _font_clear_cache(fd); + fd->msdf = p_msdf; + } } -void TextServerAdvanced::font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +bool TextServerAdvanced::font_is_multichannel_signed_distance_field(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->msdf; +} + +void TextServerAdvanced::font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->bitmap_add_char(p_char, p_texture_idx, p_rect, p_align, p_advance); + + MutexLock lock(fd->mutex); + if (fd->msdf_range != p_msdf_pixel_range) { + _font_clear_cache(fd); + fd->msdf_range = p_msdf_pixel_range; + } } -void TextServerAdvanced::font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +int TextServerAdvanced::font_get_msdf_pixel_range(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->msdf_range; +} + +void TextServerAdvanced::font_set_msdf_size(RID p_font_rid, int p_msdf_size) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->bitmap_add_kerning_pair(p_A, p_B, p_kerning); + + MutexLock lock(fd->mutex); + if (fd->msdf_source_size != p_msdf_size) { + _font_clear_cache(fd); + fd->msdf_source_size = p_msdf_size; + } } -float TextServerAdvanced::font_get_height(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); +int TextServerAdvanced::font_get_msdf_size(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->msdf_source_size; +} + +void TextServerAdvanced::font_set_fixed_size(RID p_font_rid, int p_fixed_size) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->fixed_size != p_fixed_size) { + fd->fixed_size = p_fixed_size; + } +} + +int TextServerAdvanced::font_get_fixed_size(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->fixed_size; +} + +void TextServerAdvanced::font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->force_autohinter != p_force_autohinter) { + _font_clear_cache(fd); + fd->force_autohinter = p_force_autohinter; + } +} + +bool TextServerAdvanced::font_is_force_autohinter(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->force_autohinter; +} + +void TextServerAdvanced::font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->hinting != p_hinting) { + _font_clear_cache(fd); + fd->hinting = p_hinting; + } +} + +TextServer::Hinting TextServerAdvanced::font_get_hinting(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, HINTING_NONE); + + MutexLock lock(fd->mutex); + return fd->hinting; +} + +void TextServerAdvanced::font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->variation_coordinates != p_variation_coordinates) { + _font_clear_cache(fd); + fd->variation_coordinates = p_variation_coordinates; + } +} + +Dictionary TextServerAdvanced::font_get_variation_coordinates(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Dictionary()); + + MutexLock lock(fd->mutex); + return fd->variation_coordinates; +} + +void TextServerAdvanced::font_set_oversampling(RID p_font_rid, real_t p_oversampling) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->oversampling != p_oversampling) { + _font_clear_cache(fd); + fd->oversampling = p_oversampling; + } +} + +real_t TextServerAdvanced::font_get_oversampling(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_height(p_size); + + MutexLock lock(fd->mutex); + return fd->oversampling; } -float TextServerAdvanced::font_get_ascent(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); +Array TextServerAdvanced::font_get_size_cache_list(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Array()); + + MutexLock lock(fd->mutex); + Array ret; + for (const Map<Vector2i, FontDataForSizeAdvanced *>::Element *E = fd->cache.front(); E; E = E->next()) { + ret.push_back(E->key()); + } + return ret; +} + +void TextServerAdvanced::font_clear_size_cache(RID p_font_rid) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + for (const Map<Vector2i, FontDataForSizeAdvanced *>::Element *E = fd->cache.front(); E; E = E->next()) { + memdelete(E->get()); + } + fd->cache.clear(); +} + +void TextServerAdvanced::font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->cache.has(p_size)) { + memdelete(fd->cache[p_size]); + fd->cache.erase(p_size); + } +} + +void TextServerAdvanced::font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->ascent = p_ascent; +} + +real_t TextServerAdvanced::font_get_ascent(RID p_font_rid, int p_size) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_ascent(p_size); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f); + + if (fd->msdf) { + return fd->cache[size]->ascent * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->ascent; + } } -float TextServerAdvanced::font_get_descent(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); +void TextServerAdvanced::font_set_descent(RID p_font_rid, int p_size, real_t p_descent) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->descent = p_descent; +} + +real_t TextServerAdvanced::font_get_descent(RID p_font_rid, int p_size) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_descent(p_size); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f); + + if (fd->msdf) { + return fd->cache[size]->descent * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->descent; + } } -float TextServerAdvanced::font_get_underline_position(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); +void TextServerAdvanced::font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->underline_position = p_underline_position; +} + +real_t TextServerAdvanced::font_get_underline_position(RID p_font_rid, int p_size) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_underline_position(p_size); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f); + + if (fd->msdf) { + return fd->cache[size]->underline_position * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->underline_position; + } } -float TextServerAdvanced::font_get_underline_thickness(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); +void TextServerAdvanced::font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->underline_thickness = p_underline_thickness; +} + +real_t TextServerAdvanced::font_get_underline_thickness(RID p_font_rid, int p_size) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_underline_thickness(p_size); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f); + + if (fd->msdf) { + return fd->cache[size]->underline_thickness * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->underline_thickness; + } } -int TextServerAdvanced::font_get_spacing_space(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, 0); - return fd->get_spacing_space(); +void TextServerAdvanced::font_set_scale(RID p_font_rid, int p_size, real_t p_scale) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->scale = p_scale; } -void TextServerAdvanced::font_set_spacing_space(RID p_font, int p_value) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +real_t TextServerAdvanced::font_get_scale(RID p_font_rid, int p_size) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, 0.f); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f); + + if (fd->msdf) { + return fd->cache[size]->scale * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->scale / fd->cache[size]->oversampling; + } +} + +void TextServerAdvanced::font_set_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing, int p_value) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_spacing_space(p_value); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + switch (p_spacing) { + case TextServer::SPACING_GLYPH: { + fd->cache[size]->spacing_glyph = p_value; + } break; + case TextServer::SPACING_SPACE: { + fd->cache[size]->spacing_space = p_value; + } break; + default: { + ERR_FAIL_MSG("Invalid spacing type: " + itos(p_spacing)); + } break; + } } -int TextServerAdvanced::font_get_spacing_glyph(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); +int TextServerAdvanced::font_get_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0); - return fd->get_spacing_glyph(); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0); + + switch (p_spacing) { + case TextServer::SPACING_GLYPH: { + if (fd->msdf) { + return fd->cache[size]->spacing_glyph * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->spacing_glyph; + } + } break; + case TextServer::SPACING_SPACE: { + if (fd->msdf) { + return fd->cache[size]->spacing_space * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->spacing_space; + } + } break; + default: { + ERR_FAIL_V_MSG(0, "Invalid spacing type: " + itos(p_spacing)); + } break; + } + return 0; } -void TextServerAdvanced::font_set_spacing_glyph(RID p_font, int p_value) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +int TextServerAdvanced::font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, 0); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0); + + return fd->cache[size]->textures.size(); +} + +void TextServerAdvanced::font_clear_textures(RID p_font_rid, const Vector2i &p_size) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_spacing_glyph(p_value); + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->textures.clear(); } -void TextServerAdvanced::font_set_antialiased(RID p_font, bool p_antialiased) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +void TextServerAdvanced::font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_antialiased(p_antialiased); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + ERR_FAIL_INDEX(p_texture_index, fd->cache[size]->textures.size()); + + fd->cache[size]->textures.remove(p_texture_index); } -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(); +void TextServerAdvanced::font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + ERR_FAIL_COND(p_image.is_null()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + ERR_FAIL_COND(p_texture_index < 0); + if (p_texture_index >= fd->cache[size]->textures.size()) { + fd->cache[size]->textures.resize(p_texture_index + 1); + } + + FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + + tex.imgdata = p_image->get_data(); + tex.texture_w = p_image->get_width(); + tex.texture_h = p_image->get_height(); + tex.format = p_image->get_format(); + + Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, tex.format, tex.imgdata)); + tex.texture = Ref<ImageTexture>(); + tex.texture.instantiate(); + tex.texture->create_from_image(img); } -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(); +Ref<Image> TextServerAdvanced::font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Ref<Image>()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Ref<Image>()); + ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), Ref<Image>()); + + const FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, tex.format, tex.imgdata)); + + return img; } -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_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + if (p_texture_index >= fd->cache[size]->textures.size()) { + fd->cache[size]->textures.resize(p_texture_index + 1); + } + + FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + tex.offsets = p_offset; } -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); +PackedInt32Array TextServerAdvanced::font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, PackedInt32Array()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), PackedInt32Array()); + ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), PackedInt32Array()); + + const FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + return tex.offsets; +} + +Array TextServerAdvanced::font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Array()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array()); + + Array ret; + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + const int32_t *E = nullptr; + while ((E = gl.next(E))) { + ret.push_back(*E); + } + return ret; +} + +void TextServerAdvanced::font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_variation(p_name, p_value); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + fd->cache[size]->glyph_map.clear(); } -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_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + fd->cache[size]->glyph_map.erase(p_glyph); } -void TextServerAdvanced::font_set_distance_field_hint(RID p_font, bool p_distance_field) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +Vector2 TextServerAdvanced::font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Vector2()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + if (!_ensure_glyph(fd, size, p_glyph)) { + return Vector2(); // Invalid or non graphicl glyph, do not display errors. + } + + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + if (fd->msdf) { + return gl[p_glyph].advance * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return gl[p_glyph].advance; + } +} + +void TextServerAdvanced::font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_distance_field_hint(p_distance_field); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].advance = p_advance; + gl[p_glyph].found = true; } -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(); +Vector2 TextServerAdvanced::font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Vector2()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + if (!_ensure_glyph(fd, size, p_glyph)) { + return Vector2(); // Invalid or non graphicl glyph, do not display errors. + } + + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + if (fd->msdf) { + return gl[p_glyph].rect.position * (real_t)p_size.x / (real_t)fd->msdf_source_size; + } else { + return gl[p_glyph].rect.position; + } } -void TextServerAdvanced::font_set_hinting(RID p_font, TextServer::Hinting p_hinting) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +void TextServerAdvanced::font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_hinting(p_hinting); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].rect.position = p_offset; + gl[p_glyph].found = true; } -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(); +Vector2 TextServerAdvanced::font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Vector2()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + if (!_ensure_glyph(fd, size, p_glyph)) { + return Vector2(); // Invalid or non graphicl glyph, do not display errors. + } + + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + if (fd->msdf) { + return gl[p_glyph].rect.size * (real_t)p_size.x / (real_t)fd->msdf_source_size; + } else { + return gl[p_glyph].rect.size; + } } -void TextServerAdvanced::font_set_force_autohinter(RID p_font, bool p_enabeld) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +void TextServerAdvanced::font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_force_autohinter(p_enabeld); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].rect.size = p_gl_size; + gl[p_glyph].found = true; } -bool TextServerAdvanced::font_get_force_autohinter(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); +Rect2 TextServerAdvanced::font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Rect2()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Rect2()); + if (!_ensure_glyph(fd, size, p_glyph)) { + return Rect2(); // Invalid or non graphicl glyph, do not display errors. + } + + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + return gl[p_glyph].uv_rect; +} + +void TextServerAdvanced::font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].uv_rect = p_uv_rect; + gl[p_glyph].found = true; +} + +int TextServerAdvanced::font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, -1); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), -1); + if (!_ensure_glyph(fd, size, p_glyph)) { + return -1; // Invalid or non graphicl glyph, do not display errors. + } + + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + return gl[p_glyph].texture_idx; +} + +void TextServerAdvanced::font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].texture_idx = p_texture_idx; + gl[p_glyph].found = true; +} + +bool TextServerAdvanced::font_get_glyph_contours(RID p_font_rid, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, false); - return fd->get_force_autohinter(); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), false); + +#ifdef MODULE_FREETYPE_ENABLED + int error = FT_Load_Glyph(fd->cache[size]->face, p_index, FT_LOAD_NO_BITMAP | (fd->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)); + ERR_FAIL_COND_V(error, false); + + r_points.clear(); + r_contours.clear(); + + real_t h = fd->cache[size]->ascent; + real_t scale = (1.0 / 64.0) / fd->cache[size]->oversampling * fd->cache[size]->scale; + if (fd->msdf) { + scale = scale * (real_t)p_size / (real_t)fd->msdf_source_size; + } + for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_points; i++) { + r_points.push_back(Vector3(fd->cache[size]->face->glyph->outline.points[i].x * scale, h - fd->cache[size]->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fd->cache[size]->face->glyph->outline.tags[i]))); + } + for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_contours; i++) { + r_contours.push_back(fd->cache[size]->face->glyph->outline.contours[i]); + } + r_orientation = (FT_Outline_Get_Orientation(&fd->cache[size]->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT); +#else + return false; +#endif + return true; } -bool TextServerAdvanced::font_has_char(RID p_font, char32_t p_char) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); +Array TextServerAdvanced::font_get_kerning_list(RID p_font_rid, int p_size) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Array()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array()); + + Array ret; + for (const Map<Vector2i, Vector2>::Element *E = fd->cache[size]->kerning_map.front(); E; E = E->next()) { + ret.push_back(E->key()); + } + return ret; +} + +void TextServerAdvanced::font_clear_kerning_map(RID p_font_rid, int p_size) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->kerning_map.clear(); +} + +void TextServerAdvanced::font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->kerning_map.erase(p_glyph_pair); +} + +void TextServerAdvanced::font_set_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->kerning_map[p_glyph_pair] = p_kerning; +} + +Vector2 TextServerAdvanced::font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Vector2()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + + const Map<Vector2i, Vector2> &kern = fd->cache[size]->kerning_map; + + if (kern.has(p_glyph_pair)) { + if (fd->msdf) { + return kern[p_glyph_pair] * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return kern[p_glyph_pair]; + } + } else { +#ifdef MODULE_FREETYPE_ENABLED + if (fd->cache[size]->face) { + FT_Vector delta; + FT_Get_Kerning(fd->cache[size]->face, p_glyph_pair.x, p_glyph_pair.y, FT_KERNING_DEFAULT, &delta); + if (fd->msdf) { + return Vector2(delta.x, delta.y) * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return Vector2(delta.x, delta.y); + } + } +#endif + } + return Vector2(); +} + +int32_t TextServerAdvanced::font_get_glyph_index(RID p_font_rid, int p_size, char32_t p_char, char32_t p_variation_selector) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, 0); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0); + +#ifdef MODULE_FREETYPE_ENABLED + if (fd->cache[size]->face) { + if (p_variation_selector) { + return FT_Face_GetCharVariantIndex(fd->cache[size]->face, p_char, p_variation_selector); + } else { + return FT_Get_Char_Index(fd->cache[size]->face, p_char); + } + } else { + return 0; + } +#else + return (int32_t)p_char; +#endif +} + +bool TextServerAdvanced::font_has_char(RID p_font_rid, char32_t p_char) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, false); - return fd->has_char(p_char); + + MutexLock lock(fd->mutex); + if (fd->cache.is_empty()) { + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), false); + } + FontDataForSizeAdvanced *at_size = fd->cache.front()->get(); + +#ifdef MODULE_FREETYPE_ENABLED + if (at_size && at_size->face) { + return FT_Get_Char_Index(at_size->face, p_char) != 0; + } +#endif + return (at_size) ? at_size->glyph_map.has((int32_t)p_char) : false; } -String TextServerAdvanced::font_get_supported_chars(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); +String TextServerAdvanced::font_get_supported_chars(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, String()); - return fd->get_supported_chars(); + + MutexLock lock(fd->mutex); + if (fd->cache.is_empty()) { + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), String()); + } + FontDataForSizeAdvanced *at_size = fd->cache.front()->get(); + + String chars; +#ifdef MODULE_FREETYPE_ENABLED + if (at_size && at_size->face) { + FT_UInt gindex; + FT_ULong charcode = FT_Get_First_Char(at_size->face, &gindex); + while (gindex != 0) { + if (charcode != 0) { + chars += char32_t(charcode); + } + charcode = FT_Get_Next_Char(at_size->face, charcode, &gindex); + } + return chars; + } +#endif + if (at_size) { + const HashMap<int32_t, FontGlyph> &gl = at_size->glyph_map; + const int32_t *E = nullptr; + while ((E = gl.next(E))) { + chars += char32_t(*E); + } + } + return 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(); +void TextServerAdvanced::font_render_range(RID p_font_rid, const Vector2i &p_size, char32_t p_start, char32_t p_end) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + for (char32_t i = p_start; i <= p_end; i++) { +#ifdef MODULE_FREETYPE_ENABLED + if (fd->cache[size]->face) { + _ensure_glyph(fd, size, FT_Get_Char_Index(fd->cache[size]->face, i)); + continue; + } +#endif + _ensure_glyph(fd, size, (int32_t)i); + } } -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(); +void TextServerAdvanced::font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + ERR_FAIL_COND(!_ensure_glyph(fd, size, p_index)); } -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]]; +void TextServerAdvanced::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + if (!_ensure_glyph(fd, size, p_index)) { + return; // // Invalid or non graphicl glyph, do not display errors, nothing to draw. + } + + const FontGlyph &gl = fd->cache[size]->glyph_map[p_index]; + if (gl.found) { + ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size()); + + if (gl.texture_idx != -1) { + Color modulate = p_color; +#ifdef MODULE_FREETYPE_ENABLED + if (fd->cache[size]->face && FT_HAS_COLOR(fd->cache[size]->face)) { + modulate.r = modulate.g = modulate.b = 1.0; + } +#endif + if (RenderingServer::get_singleton() != nullptr) { + RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid(); + if (fd->msdf) { + Point2 cpos = p_pos; + cpos += gl.rect.position * (real_t)p_size / (real_t)fd->msdf_source_size; + Size2 csize = gl.rect.size * (real_t)p_size / (real_t)fd->msdf_source_size; + RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, 0, fd->msdf_range); + } else { + Point2i cpos = p_pos; + cpos += gl.rect.position; + Size2i csize = gl.rect.size; + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + } } } - 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); +void TextServerAdvanced::font_draw_glyph_outline(RID p_font_rid, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->lang_support_overrides[p_language] = p_supported; + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, Vector2i(p_size, p_outline_size)); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + if (!_ensure_glyph(fd, size, p_index)) { + return; // // Invalid or non graphicl glyph, do not display errors, nothing to draw. + } + + const FontGlyph &gl = fd->cache[size]->glyph_map[p_index]; + if (gl.found) { + ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size()); + + if (gl.texture_idx != -1) { + Color modulate = p_color; +#ifdef MODULE_FREETYPE_ENABLED + if (fd->cache[size]->face && FT_HAS_COLOR(fd->cache[size]->face)) { + modulate.r = modulate.g = modulate.b = 1.0; + } +#endif + if (RenderingServer::get_singleton() != nullptr) { + RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid(); + if (fd->msdf) { + Point2 cpos = p_pos; + cpos += gl.rect.position * (real_t)p_size / (real_t)fd->msdf_source_size; + Size2 csize = gl.rect.size * (real_t)p_size / (real_t)fd->msdf_source_size; + RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, p_outline_size * 2, fd->msdf_range); + } else { + Point2i cpos = p_pos; + cpos += gl.rect.position; + Size2i csize = gl.rect.size; + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + } + } + } + } } -bool TextServerAdvanced::font_get_language_support_override(RID p_font, const String &p_language) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +bool TextServerAdvanced::font_is_language_supported(RID p_font_rid, const String &p_language) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, false); - return fd->lang_support_overrides[p_language]; + + MutexLock lock(fd->mutex); + if (fd->language_support_overrides.has(p_language)) { + return fd->language_support_overrides[p_language]; + } else { + return true; + } } -void TextServerAdvanced::font_remove_language_support_override(RID p_font, const String &p_language) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +void TextServerAdvanced::font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->lang_support_overrides.erase(p_language); + + MutexLock lock(fd->mutex); + fd->language_support_overrides[p_language] = p_supported; } -Vector<String> TextServerAdvanced::font_get_language_support_overrides(RID p_font) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +bool TextServerAdvanced::font_get_language_support_override(RID p_font_rid, const String &p_language) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->language_support_overrides[p_language]; +} + +void TextServerAdvanced::font_remove_language_support_override(RID p_font_rid, const String &p_language) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + fd->language_support_overrides.erase(p_language); +} + +Vector<String> TextServerAdvanced::font_get_language_support_overrides(RID p_font_rid) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); 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()); + + MutexLock lock(fd->mutex); + Vector<String> out; + for (const Map<String, bool>::Element *E = fd->language_support_overrides.front(); E; E = E->next()) { + out.push_back(E->key()); } - return ret; + return out; } -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); +bool TextServerAdvanced::font_is_script_supported(RID p_font_rid, const String &p_script) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); 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); + Vector2i size = _get_size(fd, 16); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), false); + return fd->supported_scripts.has(TS->name_to_tag(p_script)); } } -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); +void TextServerAdvanced::font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); 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); +bool TextServerAdvanced::font_get_script_support_override(RID p_font_rid, const String &p_script) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); 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); +void TextServerAdvanced::font_remove_script_support_override(RID p_font_rid, const String &p_script) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); 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); +Vector<String> TextServerAdvanced::font_get_script_support_overrides(RID p_font_rid) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); 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); + MutexLock lock(fd->mutex); + Vector<String> out; + for (const Map<String, bool>::Element *E = fd->script_support_overrides.front(); E; E = E->next()) { + out.push_back(E->key()); + } + return out; } -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); -} +Dictionary TextServerAdvanced::font_supported_feature_list(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Dictionary()); -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); + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, 16); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Dictionary()); + return fd->supported_features; } -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); -} +Dictionary TextServerAdvanced::font_supported_variation_list(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Dictionary()); -bool TextServerAdvanced::font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, false); - return fd->get_glyph_contours(p_size, p_index, r_points, r_contours, r_orientation); + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, 16); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Dictionary()); + return fd->supported_varaitions; } -float TextServerAdvanced::font_get_oversampling() const { +real_t TextServerAdvanced::font_get_global_oversampling() const { return oversampling; } -void TextServerAdvanced::font_set_oversampling(float p_oversampling) { +void TextServerAdvanced::font_set_global_oversampling(real_t p_oversampling) { _THREAD_SAFE_METHOD_ if (oversampling != p_oversampling) { oversampling = p_oversampling; List<RID> fonts; font_owner.get_owned_list(&fonts); + bool font_cleared = false; for (const RID &E : fonts) { - font_owner.getornull(E)->clear_cache(); + if (!font_is_multichannel_signed_distance_field(E) && font_get_oversampling(E) <= 0) { + font_clear_size_cache(E); + font_cleared = true; + } } - List<RID> text_bufs; - shaped_owner.get_owned_list(&text_bufs); - for (const RID &E : text_bufs) { - invalidate(shaped_owner.getornull(E)); + if (font_cleared) { + List<RID> text_bufs; + shaped_owner.get_owned_list(&text_bufs); + for (const RID &E : text_bufs) { + invalidate(shaped_owner.getornull(E)); + } } } } -Vector<String> TextServerAdvanced::get_system_fonts() const { - return Vector<String>(); -} - /*************************************************************************/ /* Shaped text buffer interface */ /*************************************************************************/ @@ -977,6 +2843,7 @@ void TextServerAdvanced::invalidate(TextServerAdvanced::ShapedTextDataAdvanced * p_shaped->sort_valid = false; p_shaped->line_breaks_valid = false; p_shaped->justification_ops_valid = false; + p_shaped->text_trimmed = false; p_shaped->ascent = 0.f; p_shaped->descent = 0.f; p_shaped->width = 0.f; @@ -984,6 +2851,7 @@ void TextServerAdvanced::invalidate(TextServerAdvanced::ShapedTextDataAdvanced * p_shaped->uthk = 0.f; p_shaped->glyphs.clear(); p_shaped->glyphs_logical.clear(); + p_shaped->overrun_trim_data = TrimData(); p_shaped->utf16 = Char16String(); if (p_shaped->script_iter != nullptr) { memdelete(p_shaped->script_iter); @@ -1027,10 +2895,10 @@ RID TextServerAdvanced::create_shaped_text(TextServer::Direction p_direction, Te } void TextServerAdvanced::shaped_text_clear(RID p_shaped) { - _THREAD_SAFE_METHOD_ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND(!sd); + MutexLock lock(sd->mutex); sd->parent = RID(); sd->start = 0; sd->end = 0; @@ -1042,10 +2910,10 @@ void TextServerAdvanced::shaped_text_clear(RID p_shaped) { } 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); + MutexLock lock(sd->mutex); if (sd->direction != p_direction) { if (sd->parent != RID()) { full_copy(sd); @@ -1056,16 +2924,18 @@ void TextServerAdvanced::shaped_text_set_direction(RID p_shaped, TextServer::Dir } 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); + + MutexLock lock(sd->mutex); 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); + + MutexLock lock(sd->mutex); if (sd->parent != RID()) { full_copy(sd); } @@ -1074,9 +2944,10 @@ void TextServerAdvanced::shaped_text_set_bidi_override(RID p_shaped, const Vecto } 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); + + MutexLock lock(sd->mutex); if (sd->orientation != p_orientation) { if (sd->parent != RID()) { full_copy(sd); @@ -1087,9 +2958,10 @@ void TextServerAdvanced::shaped_text_set_orientation(RID p_shaped, TextServer::O } 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); + + MutexLock lock(sd->mutex); ERR_FAIL_COND(sd->parent != RID()); if (sd->preserve_invalid != p_enabled) { sd->preserve_invalid = p_enabled; @@ -1098,16 +2970,18 @@ void TextServerAdvanced::shaped_text_set_preserve_invalid(RID p_shaped, bool p_e } 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); + + MutexLock lock(sd->mutex); 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); + + MutexLock lock(sd->mutex); if (sd->preserve_control != p_enabled) { if (sd->parent != RID()) { full_copy(sd); @@ -1118,25 +2992,31 @@ void TextServerAdvanced::shaped_text_set_preserve_control(RID p_shaped, bool p_e } 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); + + MutexLock lock(sd->mutex); 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); + + MutexLock lock(sd->mutex); 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); + MutexLock lock(sd->mutex); + for (int i = 0; i < p_fonts.size(); i++) { + ERR_FAIL_COND_V(!font_owner.getornull(p_fonts[i]), false); + } + if (p_text.is_empty()) { return true; } @@ -1161,7 +3041,7 @@ bool TextServerAdvanced::shaped_text_add_string(RID p_shaped, const String &p_te 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) { +bool TextServerAdvanced::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align, int p_length) { _THREAD_SAFE_METHOD_ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); @@ -1191,10 +3071,11 @@ bool TextServerAdvanced::shaped_text_add_object(RID p_shaped, Variant p_key, con 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_ +bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align) { ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); + + MutexLock lock(sd->mutex); 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; @@ -1222,45 +3103,20 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, 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 (gl.font_rid.is_valid()) { 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)); + sd->ascent = MAX(sd->ascent, MAX(font_get_ascent(gl.font_rid, gl.font_size), -gl.y_off)); + sd->descent = MAX(sd->descent, MAX(font_get_descent(gl.font_rid, 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->ascent = MAX(sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); + sd->descent = MAX(sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).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)); @@ -1279,43 +3135,80 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, } // Align embedded objects to baseline. + real_t full_ascent = sd->ascent; + real_t full_descent = sd->descent; 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: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.y = -sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.y = -(E->get().rect.size.y / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.y = (-sd->ascent + sd->descent) / 2; + } break; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.y = 0; + } break; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.y = sd->descent; + } break; + } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.y -= E->get().rect.size.y; + } break; + case INLINE_ALIGN_CENTER_TO: { + 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; + case INLINE_ALIGN_TOP_TO: { + //NOP } break; } + full_ascent = MAX(full_ascent, -E->get().rect.position.y); + full_descent = MAX(full_descent, E->get().rect.position.y + E->get().rect.size.y); } else { - switch (E->get().inline_align) { - case VALIGN_TOP: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.x = -sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.x = -(E->get().rect.size.x / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.x = (-sd->ascent + sd->descent) / 2; } break; - case VALIGN_BOTTOM: { - E->get().rect.position.x = sd->descent - E->get().rect.size.x; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.x = 0; + } break; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.x = sd->descent; + } break; + } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.x -= E->get().rect.size.x; + } break; + case INLINE_ALIGN_CENTER_TO: { + E->get().rect.position.x -= E->get().rect.size.x / 2; + } break; + case INLINE_ALIGN_TOP_TO: { + //NOP } break; } + full_ascent = MAX(full_ascent, -E->get().rect.position.x); + full_descent = MAX(full_descent, E->get().rect.position.x + E->get().rect.size.x); } } } + sd->ascent = full_ascent; + sd->descent = full_descent; } 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()); + + MutexLock lock(sd->mutex); if (sd->parent != RID()) { return shaped_text_substr(sd->parent, p_start, p_length); } @@ -1349,9 +3242,6 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng 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; @@ -1363,7 +3253,7 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng ERR_FAIL_COND_V_MSG((start < 0 || end - start > new_sd->utf16.length()), RID(), "Invalid BiDi override range."); - //Create temporary line bidi & shape + // 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); @@ -1404,46 +3294,18 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng 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 (gl.font_rid.is_valid()) { 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)); + new_sd->ascent = MAX(new_sd->ascent, MAX(font_get_ascent(gl.font_rid, gl.font_size), -gl.y_off)); + new_sd->descent = MAX(new_sd->descent, MAX(font_get_descent(gl.font_rid, 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)); + new_sd->ascent = MAX(new_sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); + new_sd->descent = MAX(new_sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).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. @@ -1464,53 +3326,90 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng } // Align embedded objects to baseline. + real_t full_ascent = new_sd->ascent; + real_t full_descent = new_sd->descent; 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: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.y = -new_sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.y = -(E->get().rect.size.y / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.y = (-new_sd->ascent + new_sd->descent) / 2; + } break; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.y = 0; + } break; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.y = new_sd->descent; + } break; + } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.y -= E->get().rect.size.y; + } break; + case INLINE_ALIGN_CENTER_TO: { + 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; + case INLINE_ALIGN_TOP_TO: { + //NOP } break; } + full_ascent = MAX(full_ascent, -E->get().rect.position.y); + full_descent = MAX(full_descent, E->get().rect.position.y + E->get().rect.size.y); } else { - switch (E->get().inline_align) { - case VALIGN_TOP: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.x = -new_sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.x = -(E->get().rect.size.x / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.x = (-new_sd->ascent + new_sd->descent) / 2; } break; - case VALIGN_BOTTOM: { - E->get().rect.position.x = new_sd->descent - E->get().rect.size.x; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.x = 0; + } break; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.x = new_sd->descent; + } break; + } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.x -= E->get().rect.size.x; + } break; + case INLINE_ALIGN_CENTER_TO: { + E->get().rect.position.x -= E->get().rect.size.x / 2; + } break; + case INLINE_ALIGN_TOP_TO: { + //NOP } break; } + full_ascent = MAX(full_ascent, -E->get().rect.position.x); + full_descent = MAX(full_descent, E->get().rect.position.x + E->get().rect.size.x); } } } + new_sd->ascent = full_ascent; + new_sd->descent = full_descent; } - 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()); + + MutexLock lock(sd->mutex); 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_ +real_t TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t /*JustificationFlag*/ p_jst_flags) { ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); } @@ -1518,6 +3417,7 @@ float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width, const_cast<TextServerAdvanced *>(this)->shaped_text_update_justification_ops(p_shaped); } + sd->fit_width_minimum_reached = false; int start_pos = 0; int end_pos = sd->glyphs.size() - 1; @@ -1546,14 +3446,26 @@ float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width, } } + real_t justification_width; + if ((p_jst_flags & JUSTIFICATION_CONSTRAIN_ELLIPSIS) == JUSTIFICATION_CONSTRAIN_ELLIPSIS) { + if (sd->overrun_trim_data.trim_pos >= 0) { + start_pos = sd->overrun_trim_data.trim_pos; + justification_width = sd->width_trimmed; + } else { + return sd->width; + } + } else { + justification_width = sd->width; + } + 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; + justification_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; + justification_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; } @@ -1574,7 +3486,7 @@ float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width, } if ((elongation_count > 0) && ((p_jst_flags & JUSTIFICATION_KASHIDA) == JUSTIFICATION_KASHIDA)) { - float delta_width_per_kashida = (p_width - sd->width) / elongation_count; + real_t delta_width_per_kashida = (p_width - justification_width) / elongation_count; for (int i = start_pos; i <= end_pos; i++) { Glyph &gl = sd->glyphs.write[i]; if (gl.count > 0) { @@ -1582,41 +3494,58 @@ float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width, 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; + gl.repeat = MAX(count, 0); } - sd->width += (gl.repeat - prev_count) * gl.advance; + justification_width += (gl.repeat - prev_count) * gl.advance; } } } } - + real_t adv_remain = 0; if ((space_count > 0) && ((p_jst_flags & JUSTIFICATION_WORD_BOUND) == JUSTIFICATION_WORD_BOUND)) { - float delta_width_per_space = (p_width - sd->width) / space_count; + real_t delta_width_per_space = (p_width - justification_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; + real_t old_adv = gl.advance; + real_t new_advance; if ((gl.flags & GRAPHEME_IS_VIRTUAL) == GRAPHEME_IS_VIRTUAL) { - gl.advance = Math::round(MAX(gl.advance + delta_width_per_space, 0.f)); + new_advance = 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)); + new_advance = MAX(gl.advance + delta_width_per_space, 0.1 * gl.font_size); } - sd->width += (gl.advance - old_adv); + gl.advance = new_advance; + adv_remain += (new_advance - gl.advance); + if (adv_remain >= 1.0) { + gl.advance++; + adv_remain -= 1.0; + } else if (adv_remain <= -1.0) { + gl.advance = MAX(gl.advance - 1, 0); + adv_remain -= 1.0; + } + justification_width += (gl.advance - old_adv); } } } } + if (Math::floor(p_width) < Math::floor(justification_width)) { + sd->fit_width_minimum_reached = true; + } + + if ((p_jst_flags & JUSTIFICATION_CONSTRAIN_ELLIPSIS) != JUSTIFICATION_CONSTRAIN_ELLIPSIS) { + sd->width = justification_width; + } + return sd->width; } -float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) { - _THREAD_SAFE_METHOD_ +real_t TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<real_t> &p_tab_stops) { ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); } @@ -1625,7 +3554,7 @@ float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<float } int tab_index = 0; - float off = 0.f; + real_t off = 0.f; int start, end, delta; if (sd->para_direction == DIRECTION_LTR) { @@ -1642,7 +3571,7 @@ float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<float for (int i = start; i != end; i += delta) { if ((gl[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) { - float tab_off = 0.f; + real_t tab_off = 0.f; while (tab_off <= off) { tab_off += p_tab_stops[tab_index]; tab_index++; @@ -1650,7 +3579,7 @@ float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<float tab_index = 0; } } - float old_adv = gl[i].advance; + real_t old_adv = gl[i].advance; gl[i].advance = tab_off - off; sd->width += gl[i].advance - old_adv; off = 0; @@ -1662,39 +3591,49 @@ float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<float return 0.f; } -void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, float p_width, uint8_t p_clip_flags) { - _THREAD_SAFE_METHOD_ +void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, real_t p_width, uint8_t p_trim_flags) { ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped_line); ERR_FAIL_COND_MSG(!sd, "ShapedTextDataAdvanced invalid."); + + MutexLock lock(sd->mutex); if (!sd->valid) { shaped_text_shape(p_shaped_line); } - bool add_ellipsis = (p_clip_flags & OVERRUN_ADD_ELLIPSIS) == OVERRUN_ADD_ELLIPSIS; - bool cut_per_word = (p_clip_flags & OVERRUN_TRIM_WORD_ONLY) == OVERRUN_TRIM_WORD_ONLY; - bool enforce_ellipsis = (p_clip_flags & OVERRUN_ENFORCE_ELLIPSIS) == OVERRUN_ENFORCE_ELLIPSIS; + sd->overrun_trim_data.ellipsis_glyph_buf.clear(); + + bool add_ellipsis = (p_trim_flags & OVERRUN_ADD_ELLIPSIS) == OVERRUN_ADD_ELLIPSIS; + bool cut_per_word = (p_trim_flags & OVERRUN_TRIM_WORD_ONLY) == OVERRUN_TRIM_WORD_ONLY; + bool enforce_ellipsis = (p_trim_flags & OVERRUN_ENFORCE_ELLIPSIS) == OVERRUN_ENFORCE_ELLIPSIS; + bool justification_aware = (p_trim_flags & OVERRUN_JUSTIFICATION_AWARE) == OVERRUN_JUSTIFICATION_AWARE; Glyph *sd_glyphs = sd->glyphs.ptrw(); - if ((p_clip_flags & OVERRUN_TRIM) == OVERRUN_NO_TRIMMING || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) { + if ((p_trim_flags & OVERRUN_TRIM) == OVERRUN_NO_TRIMMING || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) { + sd->overrun_trim_data.trim_pos = -1; + sd->overrun_trim_data.ellipsis_pos = -1; + return; + } + + if (justification_aware && !sd->fit_width_minimum_reached) { return; } int sd_size = sd->glyphs.size(); RID last_gl_font_rid = sd_glyphs[sd_size - 1].font_rid; int last_gl_font_size = sd_glyphs[sd_size - 1].font_size; - uint32_t dot_gl_idx = font_get_glyph_index(last_gl_font_rid, '.'); - Vector2 dot_adv = font_get_glyph_advance(last_gl_font_rid, dot_gl_idx, last_gl_font_size); - uint32_t whitespace_gl_idx = font_get_glyph_index(last_gl_font_rid, ' '); - Vector2 whitespace_adv = font_get_glyph_advance(last_gl_font_rid, whitespace_gl_idx, last_gl_font_size); + int32_t dot_gl_idx = font_get_glyph_index(last_gl_font_rid, last_gl_font_size, '.'); + Vector2 dot_adv = font_get_glyph_advance(last_gl_font_rid, last_gl_font_size, dot_gl_idx); + int32_t whitespace_gl_idx = font_get_glyph_index(last_gl_font_rid, last_gl_font_size, ' '); + Vector2 whitespace_adv = font_get_glyph_advance(last_gl_font_rid, last_gl_font_size, whitespace_gl_idx); - int ellipsis_advance = 0; + int ellipsis_width = 0; if (add_ellipsis) { - ellipsis_advance = 3 * dot_adv.x + font_get_spacing_glyph(last_gl_font_rid) + (cut_per_word ? whitespace_adv.x : 0); + ellipsis_width = 3 * dot_adv.x + font_get_spacing(last_gl_font_rid, last_gl_font_size, SPACING_GLYPH) + (cut_per_word ? whitespace_adv.x : 0); } int ell_min_characters = 6; - float width = sd->width; + real_t width = sd->width; bool is_rtl = sd->direction == DIRECTION_RTL || (sd->direction == DIRECTION_AUTO && sd->para_direction == DIRECTION_RTL); @@ -1710,12 +3649,12 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl for (int i = glyphs_from; i != glyphs_to; i += glyphs_delta) { if (!is_rtl) { - width -= sd_glyphs[i].advance; + width -= sd_glyphs[i].advance * sd_glyphs[i].repeat; } if (sd_glyphs[i].count > 0) { bool above_min_char_treshold = ((is_rtl) ? sd_size - 1 - i : i) >= ell_min_characters; - if (width + (((above_min_char_treshold && add_ellipsis) || enforce_ellipsis) ? ellipsis_advance : 0) <= p_width) { + if (width + (((above_min_char_treshold && add_ellipsis) || enforce_ellipsis) ? ellipsis_width : 0) <= p_width) { if (cut_per_word && above_min_char_treshold) { if ((sd_glyphs[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) { last_valid_cut = i; @@ -1728,7 +3667,7 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl if (found) { trim_pos = last_valid_cut; - if (above_min_char_treshold && width - ellipsis_advance <= p_width) { + if (add_ellipsis && (above_min_char_treshold || enforce_ellipsis) && width - ellipsis_width <= p_width) { ellipsis_pos = trim_pos; } break; @@ -1736,18 +3675,21 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl } } if (is_rtl) { - width -= sd_glyphs[i].advance; + width -= sd_glyphs[i].advance * sd_glyphs[i].repeat; } } + sd->overrun_trim_data.trim_pos = trim_pos; + sd->overrun_trim_data.ellipsis_pos = ellipsis_pos; + if (trim_pos == 0 && enforce_ellipsis && add_ellipsis) { + sd->overrun_trim_data.ellipsis_pos = 0; + } + if ((trim_pos >= 0 && sd->width > p_width) || enforce_ellipsis) { - int added_glyphs = 0; if (add_ellipsis && (ellipsis_pos > 0 || enforce_ellipsis)) { // Insert an additional space when cutting word bound for aesthetics. if (cut_per_word && (ellipsis_pos > 0)) { TextServer::Glyph gl; - gl.start = sd_glyphs[ellipsis_pos].start; - gl.end = sd_glyphs[ellipsis_pos].end; gl.count = 1; gl.advance = whitespace_adv.x; gl.index = whitespace_gl_idx; @@ -1755,72 +3697,39 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl gl.font_size = last_gl_font_size; gl.flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT | GRAPHEME_IS_VIRTUAL | (is_rtl ? GRAPHEME_IS_RTL : 0); - // Optimized glyph insertion by replacing a glyph whenever possible. - int glyph_idx = trim_pos + ((is_rtl) ? (-added_glyphs - 1) : added_glyphs); - if (is_rtl) { - if (glyph_idx < 0) { - sd->glyphs.insert(0, gl); - } else { - sd->glyphs.set(glyph_idx, gl); - } - } else { - if (glyph_idx > (sd_size - 1)) { - sd->glyphs.append(gl); - } else { - sd->glyphs.set(glyph_idx, gl); - } - } - added_glyphs++; + sd->overrun_trim_data.ellipsis_glyph_buf.append(gl); } // Add ellipsis dots. - for (int d = 0; d < 3; d++) { - TextServer::Glyph gl; - gl.start = sd_glyphs[ellipsis_pos].start; - gl.end = sd_glyphs[ellipsis_pos].end; - gl.count = 1; - gl.advance = dot_adv.x; - gl.index = dot_gl_idx; - gl.font_rid = last_gl_font_rid; - gl.font_size = last_gl_font_size; - gl.flags = GRAPHEME_IS_PUNCTUATION | GRAPHEME_IS_VIRTUAL | (is_rtl ? GRAPHEME_IS_RTL : 0); - - // Optimized glyph insertion by replacing a glyph whenever possible. - int glyph_idx = trim_pos + ((is_rtl) ? (-added_glyphs - 1) : added_glyphs); - if (is_rtl) { - if (glyph_idx < 0) { - sd->glyphs.insert(0, gl); - } else { - sd->glyphs.set(glyph_idx, gl); - } - } else { - if (glyph_idx > (sd_size - 1)) { - sd->glyphs.append(gl); - } else { - sd->glyphs.set(glyph_idx, gl); - } - } - added_glyphs++; - } - } - - // Cut the remaining glyphs off. - if (!is_rtl) { - sd->glyphs.resize(trim_pos + added_glyphs); - } else { - if (trim_pos - added_glyphs >= 0) { - sd->glyphs = sd->glyphs.subarray(trim_pos - added_glyphs, sd->glyphs.size() - 1); - } + TextServer::Glyph gl; + gl.count = 1; + gl.repeat = 3; + gl.advance = dot_adv.x; + gl.index = dot_gl_idx; + gl.font_rid = last_gl_font_rid; + gl.font_size = last_gl_font_size; + gl.flags = GRAPHEME_IS_PUNCTUATION | GRAPHEME_IS_VIRTUAL | (is_rtl ? GRAPHEME_IS_RTL : 0); + + sd->overrun_trim_data.ellipsis_glyph_buf.append(gl); } - // Update to correct width. - sd->width = width + ((ellipsis_pos != -1) ? ellipsis_advance : 0); + sd->text_trimmed = true; + sd->width_trimmed = width + ((ellipsis_pos != -1) ? ellipsis_width : 0); } } +TextServer::TrimData TextServerAdvanced::shaped_text_get_trim_data(RID p_shaped) const { + ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); + ERR_FAIL_COND_V_MSG(!sd, TrimData(), "ShapedTextDataAdvanced invalid."); + + MutexLock lock(sd->mutex); + return sd->overrun_trim_data; +} + 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); + + MutexLock lock(sd->mutex); if (!sd->valid) { shaped_text_shape(p_shaped); } @@ -1843,7 +3752,7 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) { int r_end = sd->spans[i].end; UBreakIterator *bi = ubrk_open(UBRK_LINE, language.ascii().get_data(), data + _convert_pos_inv(sd, r_start), _convert_pos_inv(sd, r_end - r_start), &err); if (U_FAILURE(err)) { - //No data loaded - use fallback. + // No data loaded - use fallback. for (int j = r_start; j < r_end; j++) { char32_t c = sd->text[j - sd->start]; if (is_whitespace(c)) { @@ -1907,7 +3816,7 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) { 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 + sd->glyphs.insert(i + sd_glyphs[i].count, gl); // Insert after. // Update write pointer and size. sd_size = sd->glyphs.size(); @@ -2004,9 +3913,10 @@ _FORCE_INLINE_ int _generate_kashida_justification_opportunies(const String &p_d } 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); + + MutexLock lock(sd->mutex); if (!sd->valid) { shaped_text_shape(p_shaped); } @@ -2027,7 +3937,7 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) { UErrorCode err = U_ZERO_ERROR; UBreakIterator *bi = ubrk_open(UBRK_WORD, "", data, data_size, &err); if (U_FAILURE(err)) { - // No data - use fallback + // No data - use fallback. int limit = 0; for (int i = 0; i < sd->text.length(); i++) { if (is_whitespace(data[i])) { @@ -2100,7 +4010,7 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) { 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 + sd->glyphs.insert(i + sd->glyphs[i].count, gl); // Insert after. i += sd->glyphs[i].count; continue; } @@ -2114,8 +4024,8 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) { } 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_font_t *hb_font = _font_get_hb_handle(p_font, p_font_size); + ERR_FAIL_COND_V(hb_font == nullptr, TextServer::Glyph()); hb_buffer_clear_contents(p_sd->hb_buffer); hb_buffer_set_direction(p_sd->hb_buffer, p_direction); @@ -2140,16 +4050,17 @@ TextServer::Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced gl.font_size = p_font_size; if (glyph_count > 0) { + real_t scale = font_get_scale(p_font, p_font_size); if (p_sd->orientation == ORIENTATION_HORIZONTAL) { - gl.advance = Math::round(glyph_pos[0].x_advance / (64.0 / fd->get_font_scale(p_font_size))); + gl.advance = Math::round(glyph_pos[0].x_advance / (64.0 / scale)); } else { - gl.advance = -Math::round(glyph_pos[0].y_advance / (64.0 / fd->get_font_scale(p_font_size))); + gl.advance = -Math::round(glyph_pos[0].y_advance / (64.0 / scale)); } 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))); + gl.x_off = Math::round(glyph_pos[0].x_offset / (64.0 / scale)); + gl.y_off = -Math::round(glyph_pos[0].y_offset / (64.0 / scale)); if ((glyph_info[0].codepoint != 0) || !u_isgraph(p_char)) { gl.flags |= GRAPHEME_IS_VALID; @@ -2159,14 +4070,9 @@ TextServer::Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced } 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 + if (p_fb_index >= p_fonts.size()) { + // 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; @@ -2196,7 +4102,8 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star return; } - hb_font_t *hb_font = fd->get_hb_handle(fs); + RID f = p_fonts[p_fb_index]; + hb_font_t *hb_font = _font_get_hb_handle(f, fs); ERR_FAIL_COND(hb_font == nullptr); hb_buffer_clear_contents(p_sd->hb_buffer); @@ -2276,18 +4183,19 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star gl.index = glyph_info[i].codepoint; if (gl.index != 0) { + real_t scale = font_get_scale(f, fs); if (p_sd->orientation == ORIENTATION_HORIZONTAL) { - gl.advance = Math::round(glyph_pos[i].x_advance / (64.0 / fd->get_font_scale(fs))); + gl.advance = Math::round(glyph_pos[i].x_advance / (64.0 / scale)); } else { - gl.advance = -Math::round(glyph_pos[i].y_advance / (64.0 / fd->get_font_scale(fs))); + gl.advance = -Math::round(glyph_pos[i].y_advance / (64.0 / scale)); } - 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))); + gl.x_off = Math::round(glyph_pos[i].x_offset / (64.0 / scale)); + gl.y_off = -Math::round(glyph_pos[i].y_offset / (64.0 / scale)); } - if (fd->get_spacing_space() && is_whitespace(p_sd->text[glyph_info[i].cluster])) { - gl.advance += fd->get_spacing_space(); + if (font_get_spacing(f, fs, SPACING_SPACE) && is_whitespace(p_sd->text[glyph_info[i].cluster])) { + gl.advance += font_get_spacing(f, fs, SPACING_SPACE); } else { - gl.advance += fd->get_spacing_glyph(); + gl.advance += font_get_spacing(f, fs, SPACING_GLYPH); } if (p_sd->preserve_control) { @@ -2309,7 +4217,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star w[last_cluster_index].flags |= GRAPHEME_IS_VALID; } - //Fallback. + // Fallback. int failed_subrun_start = p_end + 1; int failed_subrun_end = p_start; @@ -2325,8 +4233,8 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star 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->ascent = MAX(p_sd->ascent, Math::round(font_get_glyph_advance(f, fs, w[i + j].index).x * 0.5)); + p_sd->descent = MAX(p_sd->descent, Math::round(font_get_glyph_advance(f, fs, w[i + j].index).x * 0.5)); } p_sd->width += w[i + j].advance; p_sd->glyphs.push_back(w[i + j]); @@ -2345,18 +4253,18 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star 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)); + p_sd->ascent = MAX(p_sd->ascent, font_get_ascent(f, fs)); + p_sd->descent = MAX(p_sd->descent, font_get_descent(f, fs)); + p_sd->upos = MAX(p_sd->upos, font_get_underline_position(f, fs)); + p_sd->uthk = MAX(p_sd->uthk, font_get_underline_thickness(f, 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); + MutexLock lock(sd->mutex); if (sd->valid) { return true; } @@ -2473,33 +4381,9 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) { 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; @@ -2539,49 +4423,86 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) { } // Align embedded objects to baseline. + real_t full_ascent = sd->ascent; + real_t full_descent = sd->descent; 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: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.y = -sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.y = -(E->get().rect.size.y / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.y = (-sd->ascent + sd->descent) / 2; } break; - case VALIGN_BOTTOM: { - E->get().rect.position.y = sd->descent - E->get().rect.size.y; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.y = 0; + } break; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.y = sd->descent; } break; } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.y -= E->get().rect.size.y; + } break; + case INLINE_ALIGN_CENTER_TO: { + E->get().rect.position.y -= E->get().rect.size.y / 2; + } break; + case INLINE_ALIGN_TOP_TO: { + //NOP + } break; + } + full_ascent = MAX(full_ascent, -E->get().rect.position.y); + full_descent = MAX(full_descent, E->get().rect.position.y + E->get().rect.size.y); } else { - switch (E->get().inline_align) { - case VALIGN_TOP: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.x = -sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.x = -(E->get().rect.size.x / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.x = (-sd->ascent + sd->descent) / 2; + } break; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.x = 0; + } break; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.x = sd->descent; + } break; + } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.x -= E->get().rect.size.x; + } break; + case INLINE_ALIGN_CENTER_TO: { + 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; + case INLINE_ALIGN_TOP_TO: { + //NOP } break; } + full_ascent = MAX(full_ascent, -E->get().rect.position.x); + full_descent = MAX(full_descent, E->get().rect.position.x + E->get().rect.size.x); } } - + sd->ascent = full_ascent; + sd->descent = full_descent; 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); + + MutexLock lock(sd->mutex); 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>()); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); } @@ -2589,16 +4510,18 @@ Vector<TextServer::Glyph> TextServerAdvanced::shaped_text_get_glyphs(RID p_shape } 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()); + + MutexLock lock(sd->mutex); 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>()); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); } @@ -2613,10 +4536,11 @@ Vector<TextServer::Glyph> TextServerAdvanced::shaped_text_sort_logical(RID p_sha } 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); + + MutexLock lock(sd->mutex); for (const Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) { ret.push_back(E->key()); } @@ -2625,9 +4549,10 @@ Array TextServerAdvanced::shaped_text_get_objects(RID p_shaped) const { } 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()); + + MutexLock lock(sd->mutex); ERR_FAIL_COND_V(!sd->objects.has(p_key), Rect2()); if (!sd->valid) { const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); @@ -2636,53 +4561,58 @@ Rect2 TextServerAdvanced::shaped_text_get_object_rect(RID p_shaped, Variant p_ke } 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()); + + MutexLock lock(sd->mutex); 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); + return Size2((sd->text_trimmed ? sd->width_trimmed : sd->width), sd->ascent + sd->descent); } else { - return Size2(sd->ascent + sd->descent, sd->width); + return Size2(sd->ascent + sd->descent, (sd->text_trimmed ? sd->width_trimmed : sd->width)); } } -float TextServerAdvanced::shaped_text_get_ascent(RID p_shaped) const { - _THREAD_SAFE_METHOD_ +real_t TextServerAdvanced::shaped_text_get_ascent(RID p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); 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_ +real_t TextServerAdvanced::shaped_text_get_descent(RID p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); 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_ +real_t TextServerAdvanced::shaped_text_get_width(RID p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); } - return sd->width; + return (sd->text_trimmed ? sd->width_trimmed : sd->width); } -float TextServerAdvanced::shaped_text_get_underline_position(RID p_shaped) const { - _THREAD_SAFE_METHOD_ +real_t TextServerAdvanced::shaped_text_get_underline_position(RID p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); } @@ -2690,10 +4620,11 @@ float TextServerAdvanced::shaped_text_get_underline_position(RID p_shaped) const return sd->upos; } -float TextServerAdvanced::shaped_text_get_underline_thickness(RID p_shaped) const { - _THREAD_SAFE_METHOD_ +real_t TextServerAdvanced::shaped_text_get_underline_thickness(RID p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); } @@ -2720,7 +4651,6 @@ static num_system_data num_systems[]{ }; 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; @@ -2747,7 +4677,6 @@ String TextServerAdvanced::format_number(const String &p_string, const String &p } 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; @@ -2777,7 +4706,6 @@ String TextServerAdvanced::parse_number(const String &p_string, const String &p_ } 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++) { @@ -2807,6 +4735,9 @@ TextServerAdvanced::TextServerAdvanced() { TextServerAdvanced::~TextServerAdvanced() { hb_bmp_free_font_funcs(); + if (library != nullptr) { + FT_Done_FreeType(library); + } u_cleanup(); #ifndef ICU_STATIC_DATA if (icu_data != nullptr) { diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index 3c4f840bfd..5989035800 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -39,6 +39,7 @@ #include "servers/text_server.h" #include "core/templates/rid_owner.h" +#include "core/templates/thread_work_pool.h" #include "scene/resources/texture.h" #include "script_iterator.h" @@ -53,11 +54,24 @@ #include <unicode/ustring.h> #include <unicode/utypes.h> +#include "modules/modules_enabled.gen.h" + +#ifdef MODULE_FREETYPE_ENABLED +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_TRUETYPE_TABLES_H +#include FT_STROKER_H +#include FT_ADVANCES_H +#include FT_MULTIPLE_MASTERS_H +#include FT_BBOX_H + +#include <hb-ft.h> +#include <hb-ot.h> +#endif + #include <hb-icu.h> #include <hb.h> -#include "font_adv.h" - class TextServerAdvanced : public TextServer { GDCLASS(TextServerAdvanced, TextServer); _THREAD_SAFE_CLASS_ @@ -65,8 +79,149 @@ class TextServerAdvanced : public TextServer { static String interface_name; static uint32_t interface_features; + // ICU support data. + uint8_t *icu_data = nullptr; + // Font cache data. + +#ifdef MODULE_FREETYPE_ENABLED + mutable FT_Library library = nullptr; +#endif + + const int rect_range = 2; + + struct FontTexture { + Image::Format format; + PackedByteArray imgdata; + int texture_w = 0; + int texture_h = 0; + PackedInt32Array offsets; + Ref<ImageTexture> texture; + }; + + struct FontTexturePosition { + int index = 0; + int x = 0; + int y = 0; + }; + + struct FontGlyph { + bool found = false; + int texture_idx = -1; + Rect2 rect; + Rect2 uv_rect; + Vector2 advance; + }; + + struct FontDataForSizeAdvanced { + real_t ascent = 0.f; + real_t descent = 0.f; + real_t underline_position = 0.f; + real_t underline_thickness = 0.f; + real_t scale = 1.f; + real_t oversampling = 1.f; + + int spacing_glyph = 0; + int spacing_space = 0; + + Vector2i size; + + Vector<FontTexture> textures; + HashMap<int32_t, FontGlyph> glyph_map; + Map<Vector2i, Vector2> kerning_map; + + hb_font_t *hb_handle = nullptr; + +#ifdef MODULE_FREETYPE_ENABLED + FT_Face face = nullptr; + FT_StreamRec stream; +#endif + + ~FontDataForSizeAdvanced() { + if (hb_handle != nullptr) { + hb_font_destroy(hb_handle); + } +#ifdef MODULE_FREETYPE_ENABLED + if (face != nullptr) { + FT_Done_Face(face); + } +#endif + } + }; + + struct FontDataAdvanced { + Mutex mutex; + + bool antialiased = true; + bool msdf = false; + int msdf_range = 14; + int msdf_source_size = 48; + int fixed_size = 0; + bool force_autohinter = false; + TextServer::Hinting hinting = TextServer::HINTING_LIGHT; + Dictionary variation_coordinates; + real_t oversampling = 0.f; + + Map<Vector2i, FontDataForSizeAdvanced *> cache; + + bool face_init = false; + Set<uint32_t> supported_scripts; + Dictionary supported_features; + Dictionary supported_varaitions; + + // Language/script support override. + Map<String, bool> language_support_overrides; + Map<String, bool> script_support_overrides; + + PackedByteArray data; + const uint8_t *data_ptr; + size_t data_size; + mutable ThreadWorkPool work_pool; + + ~FontDataAdvanced() { + work_pool.finish(); + for (const Map<Vector2i, FontDataForSizeAdvanced *>::Element *E = cache.front(); E; E = E->next()) { + memdelete(E->get()); + } + cache.clear(); + } + }; + + _FORCE_INLINE_ FontTexturePosition find_texture_pos_for_glyph(FontDataForSizeAdvanced *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) const; +#ifdef MODULE_MSDFGEN_ENABLED + _FORCE_INLINE_ FontGlyph rasterize_msdf(FontDataAdvanced *p_font_data, FontDataForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const; +#endif +#ifdef MODULE_FREETYPE_ENABLED + _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontDataForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const; +#endif + _FORCE_INLINE_ bool _ensure_glyph(FontDataAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const; + _FORCE_INLINE_ bool _ensure_cache_for_size(FontDataAdvanced *p_font_data, const Vector2i &p_size) const; + _FORCE_INLINE_ void _font_clear_cache(FontDataAdvanced *p_font_data); + void _generateMTSDF_threaded(uint32_t y, void *p_td) const; + + _FORCE_INLINE_ Vector2i _get_size(const FontDataAdvanced *p_font_data, int p_size) const { + if (p_font_data->msdf) { + return Vector2i(p_font_data->msdf_source_size, 0); + } else if (p_font_data->fixed_size > 0) { + return Vector2i(p_font_data->fixed_size, 0); + } else { + return Vector2i(p_size, 0); + } + } + + _FORCE_INLINE_ Vector2i _get_size_outline(const FontDataAdvanced *p_font_data, const Vector2i &p_size) const { + if (p_font_data->msdf) { + return Vector2i(p_font_data->msdf_source_size, 0); + } else if (p_font_data->fixed_size > 0) { + return Vector2i(p_font_data->fixed_size, MIN(p_size.y, 1)); + } else { + return p_size; + } + } + + // Shaped text cache data. + struct ShapedTextDataAdvanced : public ShapedTextData { /* Intermediate data */ Char16String utf16; @@ -88,7 +243,9 @@ class TextServerAdvanced : public TextServer { } }; - float oversampling = 1.f; + // Common data. + + real_t oversampling = 1.f; mutable RID_PtrOwner<FontDataAdvanced> font_owner; mutable RID_PtrOwner<ShapedTextDataAdvanced> shaped_owner; @@ -97,6 +254,30 @@ class TextServerAdvanced : public TextServer { 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); + // HarfBuzz bitmap font interface. + + static hb_font_funcs_t *funcs; + + struct hb_bmp_font_t { + TextServerAdvanced::FontDataForSizeAdvanced *face = nullptr; + bool unref = false; /* Whether to destroy bm_face when done. */ + }; + + static hb_bmp_font_t *_hb_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref); + static void _hb_bmp_font_destroy(void *p_data); + static hb_bool_t hb_bmp_get_nominal_glyph(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_unicode, hb_codepoint_t *r_glyph, void *p_user_data); + static hb_position_t hb_bmp_get_glyph_h_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data); + static hb_position_t hb_bmp_get_glyph_v_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data); + static hb_position_t hb_bmp_get_glyph_h_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data); + static hb_position_t hb_bmp_get_glyph_v_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data); + static hb_bool_t hb_bmp_get_glyph_v_origin(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_position_t *r_x, hb_position_t *r_y, void *p_user_data); + static hb_bool_t hb_bmp_get_glyph_extents(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_glyph_extents_t *r_extents, void *p_user_data); + static hb_bool_t hb_bmp_get_font_h_extents(hb_font_t *p_font, void *p_font_data, hb_font_extents_t *r_metrics, void *p_user_data); + static void hb_bmp_create_font_funcs(); + static void hb_bmp_free_font_funcs(); + static void _hb_bmp_font_set_funcs(hb_font_t *p_font, TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref); + static hb_font_t *hb_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, hb_destroy_func_t p_destroy); + protected: static void _bind_methods(){}; @@ -119,81 +300,132 @@ public: 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; + virtual int32_t name_to_tag(const String &p_name) const override; + virtual String tag_to_name(int32_t p_tag) const 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 RID create_font_bitmap(float p_height, float p_ascent, int p_base_size = 16) override; + virtual RID create_font() override; + + virtual void font_set_data(RID p_font_rid, const PackedByteArray &p_data) override; + virtual void font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) override; + + virtual void font_set_antialiased(RID p_font_rid, bool p_antialiased) override; + virtual bool font_is_antialiased(RID p_font_rid) const override; + + virtual void font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) override; + virtual bool font_is_multichannel_signed_distance_field(RID p_font_rid) const override; + + virtual void font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) override; + virtual int font_get_msdf_pixel_range(RID p_font_rid) const override; + + virtual void font_set_msdf_size(RID p_font_rid, int p_msdf_size) override; + virtual int font_get_msdf_size(RID p_font_rid) const override; + + virtual void font_set_fixed_size(RID p_font_rid, int p_fixed_size) override; + virtual int font_get_fixed_size(RID p_font_rid) const override; + + virtual void font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) override; + virtual bool font_is_force_autohinter(RID p_font_rid) const override; + + virtual void font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) override; + virtual TextServer::Hinting font_get_hinting(RID p_font_rid) const override; + + virtual void font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) override; + virtual Dictionary font_get_variation_coordinates(RID p_font_rid) const override; + + virtual void font_set_oversampling(RID p_font_rid, real_t p_oversampling) override; + virtual real_t font_get_oversampling(RID p_font_rid) const override; + + virtual Array font_get_size_cache_list(RID p_font_rid) const override; + virtual void font_clear_size_cache(RID p_font_rid) override; + virtual void font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) override; + + hb_font_t *_font_get_hb_handle(RID p_font, int p_font_size) const; + + virtual void font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) override; + virtual real_t font_get_ascent(RID p_font_rid, int p_size) const override; + + virtual void font_set_descent(RID p_font_rid, int p_size, real_t p_descent) override; + virtual real_t font_get_descent(RID p_font_rid, int p_size) const override; + + virtual void font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) override; + virtual real_t font_get_underline_position(RID p_font_rid, int p_size) const override; + + virtual void font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) override; + virtual real_t font_get_underline_thickness(RID p_font_rid, int p_size) const override; + + virtual void font_set_scale(RID p_font_rid, int p_size, real_t p_scale) override; + virtual real_t font_get_scale(RID p_font_rid, int p_size) const override; + + virtual void font_set_spacing(RID p_font_rid, int p_size, SpacingType p_spacing, int p_value) override; + virtual int font_get_spacing(RID p_font_rid, int p_size, SpacingType p_spacing) const override; - virtual void font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) override; - virtual void font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override; - virtual void font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) override; + virtual int font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const override; + virtual void font_clear_textures(RID p_font_rid, const Vector2i &p_size) override; + virtual void font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) 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 void font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) override; + virtual Ref<Image> font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) 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_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) override; + virtual PackedInt32Array font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const override; - virtual int font_get_spacing_space(RID p_font) const override; - virtual void font_set_spacing_space(RID p_font, int p_value) override; + virtual Array font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const override; + virtual void font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) override; + virtual void font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) override; - virtual int font_get_spacing_glyph(RID p_font) const override; - virtual void font_set_spacing_glyph(RID p_font, int p_value) override; + virtual Vector2 font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) override; - virtual void font_set_antialiased(RID p_font, bool p_antialiased) override; - virtual bool font_get_antialiased(RID p_font) const override; + virtual Vector2 font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) override; - virtual Dictionary font_get_feature_list(RID p_font) const override; - virtual Dictionary font_get_variation_list(RID p_font) const override; + virtual Vector2 font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) 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 Rect2 font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) override; - virtual void font_set_hinting(RID p_font, Hinting p_hinting) override; - virtual Hinting font_get_hinting(RID p_font) const override; + virtual int font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) 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_get_glyph_contours(RID p_font, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) 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 Array font_get_kerning_list(RID p_font_rid, int p_size) const override; + virtual void font_clear_kerning_map(RID p_font_rid, int p_size) override; + virtual void font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) 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_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) override; + virtual Vector2 font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const override; - virtual bool font_has_outline(RID p_font) const override; - virtual float font_get_base_size(RID p_font) const override; + virtual int32_t font_get_glyph_index(RID p_font_rid, int p_size, char32_t p_char, char32_t p_variation_selector = 0) 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_has_char(RID p_font_rid, char32_t p_char) const override; + virtual String font_get_supported_chars(RID p_font_rid) const 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 void font_render_range(RID p_font, const Vector2i &p_size, char32_t p_start, char32_t p_end) override; + virtual void font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) 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 void font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) const override; + virtual void font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) 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 bool font_is_language_supported(RID p_font_rid, const String &p_language) const override; + virtual void font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) override; + virtual bool font_get_language_support_override(RID p_font_rid, const String &p_language) override; + virtual void font_remove_language_support_override(RID p_font_rid, const String &p_language) override; + virtual Vector<String> font_get_language_support_overrides(RID p_font_rid) override; - virtual bool font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override; + virtual bool font_is_script_supported(RID p_font_rid, const String &p_script) const override; + virtual void font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) override; + virtual bool font_get_script_support_override(RID p_font_rid, const String &p_script) override; + virtual void font_remove_script_support_override(RID p_font_rid, const String &p_script) override; + virtual Vector<String> font_get_script_support_overrides(RID p_font_rid) override; - virtual float font_get_oversampling() const override; - virtual void font_set_oversampling(float p_oversampling) override; + virtual Dictionary font_supported_feature_list(RID p_font_rid) const override; + virtual Dictionary font_supported_variation_list(RID p_font_rid) const override; - virtual Vector<String> get_system_fonts() const override; + virtual real_t font_get_global_oversampling() const override; + virtual void font_set_global_oversampling(real_t p_oversampling) override; /* Shaped text buffer interface */ @@ -216,20 +448,21 @@ public: 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 bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER, int p_length = 1) override; + virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_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 real_t shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override; + virtual real_t shaped_text_tab_align(RID p_shaped, const Vector<real_t> &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 void shaped_text_overrun_trim_to_width(RID p_shaped, float p_width, uint8_t p_clip_flags) override; + virtual void shaped_text_overrun_trim_to_width(RID p_shaped, real_t p_width, uint8_t p_trim_flags) override; + virtual TrimData shaped_text_get_trim_data(RID p_shaped) const override; virtual bool shaped_text_is_ready(RID p_shaped) const override; @@ -243,11 +476,11 @@ public: 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 real_t shaped_text_get_ascent(RID p_shaped) const override; + virtual real_t shaped_text_get_descent(RID p_shaped) const override; + virtual real_t shaped_text_get_width(RID p_shaped) const override; + virtual real_t shaped_text_get_underline_position(RID p_shaped) const override; + virtual real_t 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; diff --git a/modules/text_server_fb/SCsub b/modules/text_server_fb/SCsub index 03eccbe7bd..31d1db6167 100644 --- a/modules/text_server_fb/SCsub +++ b/modules/text_server_fb/SCsub @@ -3,11 +3,23 @@ Import("env") Import("env_modules") +freetype_enabled = env.module_check_dependencies("text_server_fb", ["freetype"], True) +msdngen_enabled = env.module_check_dependencies("text_server_fb", ["msdfgen"], True) + env_text_server_fb = env_modules.Clone() -env_text_server_fb.Append( - CPPPATH=[ - "#thirdparty/freetype/include", - ] -) + +if msdngen_enabled: + env_text_server_fb.Append( + CPPPATH=[ + "#thirdparty/msdfgen", + ] + ) + +if freetype_enabled: + 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 deleted file mode 100644 index 313f170f04..0000000000 --- a/modules/text_server_fb/bitmap_font_fb.cpp +++ /dev/null @@ -1,352 +0,0 @@ -/*************************************************************************/ -/* bitmap_font_fb.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 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.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::bitmap_new(float p_height, float p_ascent, int p_base_size) { - height = p_height; - ascent = p_ascent; - - base_size = p_base_size; - if (base_size == 0) { - base_size = height; - } - - char_map.clear(); - textures.clear(); - kerning_map.clear(); - - valid = true; - - return OK; -} - -void BitmapFontDataFallback::bitmap_add_texture(const Ref<Texture> &p_texture) { - ERR_FAIL_COND(!valid); - ERR_FAIL_COND_MSG(p_texture.is_null(), "It's not a reference to a valid Texture object."); - - textures.push_back(p_texture); -} - -void BitmapFontDataFallback::bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { - ERR_FAIL_COND(!valid); - - Character chr; - chr.rect = p_rect; - chr.texture_idx = p_texture_idx; - if (p_advance < 0) { - chr.advance.x = chr.rect.size.x; - } else { - chr.advance.x = p_advance; - } - chr.align = p_align; - char_map[p_char] = chr; -} - -void BitmapFontDataFallback::bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) { - ERR_FAIL_COND(!valid); - - KerningPairKey kpk; - kpk.A = p_A; - kpk.B = p_B; - - if (p_kerning == 0 && kerning_map.has(kpk)) { - kerning_map.erase(kpk); - } else { - kerning_map[kpk] = p_kerning; - } -} - -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) { - Point2i cpos = p_pos; - cpos += (c->align + Vector2(0, -ascent)) * (float(p_size) / float(base_size)); - Size2i csize = c->rect.size * (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, csize), 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 deleted file mode 100644 index 7cd7507ebc..0000000000 --- a/modules/text_server_fb/bitmap_font_fb.h +++ /dev/null @@ -1,111 +0,0 @@ -/*************************************************************************/ -/* bitmap_font_fb.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 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 bitmap_new(float p_height, float p_ascent, int p_base_size) override; - - virtual void bitmap_add_texture(const Ref<Texture> &p_texture) override; - virtual void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override; - virtual void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) 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/dynamic_font_fb.cpp b/modules/text_server_fb/dynamic_font_fb.cpp deleted file mode 100644 index 7e77987074..0000000000 --- a/modules/text_server_fb/dynamic_font_fb.cpp +++ /dev/null @@ -1,713 +0,0 @@ -/*************************************************************************/ -/* dynamic_font_fb.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 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" - -#ifdef MODULE_FREETYPE_ENABLED - -#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.is_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 + "'."); - } - - uint64_t len = f->get_length(); - 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 = (-FT_MulFix(fds->face->underline_position, fds->face->size->metrics.y_scale) / 64.0) / oversampling * fds->scale_color_font; - fds->underline_thickness = (FT_MulFix(fds->face->underline_thickness, fds->face->size->metrics.y_scale) / 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.instantiate(); - 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).round(); - chr.advance = (advance * p_data->scale_color_font / oversampling).round(); - 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) { - Point2i 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) { - Point2i 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; -} - -bool DynamicFontDataFallback::get_glyph_contours(int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, false); - - int error = FT_Load_Glyph(fds->face, p_index, FT_LOAD_NO_BITMAP | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)); - ERR_FAIL_COND_V(error, false); - - r_points.clear(); - r_contours.clear(); - - float h = fds->ascent; - float scale = (1.0 / 64.0) / oversampling * fds->scale_color_font; - for (short i = 0; i < fds->face->glyph->outline.n_points; i++) { - r_points.push_back(Vector3(fds->face->glyph->outline.points[i].x * scale, h - fds->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fds->face->glyph->outline.tags[i]))); - } - for (short i = 0; i < fds->face->glyph->outline.n_contours; i++) { - r_contours.push_back(fds->face->glyph->outline.contours[i]); - } - r_orientation = (FT_Outline_Get_Orientation(&fds->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT); - return true; -} - -DynamicFontDataFallback::~DynamicFontDataFallback() { - clear_cache(); - if (library != nullptr) { - FT_Done_FreeType(library); - } -} - -#endif // MODULE_FREETYPE_ENABLED diff --git a/modules/text_server_fb/dynamic_font_fb.h b/modules/text_server_fb/dynamic_font_fb.h deleted file mode 100644 index 82e59fa607..0000000000 --- a/modules/text_server_fb/dynamic_font_fb.h +++ /dev/null @@ -1,173 +0,0 @@ -/*************************************************************************/ -/* dynamic_font_fb.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 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 "modules/modules_enabled.gen.h" -#ifdef MODULE_FREETYPE_ENABLED - -#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 = 0; - }; - bool operator<(CacheID right) const { - return key < right.key; - } - }; - - struct DataAtSize { - FT_Face face = nullptr; - FT_StreamRec stream; - - int size = 0; - float scale_color_font = 1.f; - float ascent = 0.0; - float descent = 0.0; - float underline_position = 0.0; - float underline_thickness = 0.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 bool get_glyph_contours(int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override; - - virtual ~DynamicFontDataFallback() override; -}; - -#endif // MODULE_FREETYPE_ENABLED - -#endif // DYNAMIC_FONT_FALLBACK_H diff --git a/modules/text_server_fb/font_fb.h b/modules/text_server_fb/font_fb.h deleted file mode 100644 index fe9888b7f4..0000000000 --- a/modules/text_server_fb/font_fb.h +++ /dev/null @@ -1,101 +0,0 @@ -/*************************************************************************/ -/* font_fb.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 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; - int spacing_space = 0; - int spacing_glyph = 0; - - virtual void clear_cache() = 0; - - virtual Error load_from_file(const String &p_filename, int p_base_size) { return ERR_CANT_CREATE; }; - virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) { return ERR_CANT_CREATE; }; - virtual Error bitmap_new(float p_height, float p_ascent, int p_base_size) { return ERR_CANT_CREATE; }; - - virtual void bitmap_add_texture(const Ref<Texture> &p_texture) { ERR_FAIL_MSG("Not supported."); }; - virtual void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { ERR_FAIL_MSG("Not supported."); }; - virtual void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) { ERR_FAIL_MSG("Not supported."); }; - - 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 int get_spacing_space() const { return spacing_space; }; - virtual void set_spacing_space(int p_value) { - spacing_space = p_value; - clear_cache(); - }; - - virtual int get_spacing_glyph() const { return spacing_glyph; }; - virtual void set_spacing_glyph(int p_value) { - spacing_glyph = p_value; - clear_cache(); - }; - - 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 bool get_glyph_contours(int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const { return false; }; - - virtual ~FontDataFallback(){}; -}; - -#endif // FONT_FALLBACK_H diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index 004cbc2bb3..e4e6797f92 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -30,8 +30,18 @@ #include "text_server_fb.h" -#include "bitmap_font_fb.h" -#include "dynamic_font_fb.h" +#include "core/string/print_string.h" + +#ifdef MODULE_MSDFGEN_ENABLED +#include "core/ShapeDistanceFinder.h" +#include "core/contour-combiners.h" +#include "core/edge-selectors.h" +#include "msdfgen.h" +#endif + +/*************************************************************************/ +/* Character properties. */ +/*************************************************************************/ _FORCE_INLINE_ bool is_control(char32_t p_char) { return (p_char <= 0x001f) || (p_char >= 0x007f && p_char <= 0x009F); @@ -100,291 +110,1779 @@ bool TextServerFallback::is_locale_right_to_left(const String &p_locale) { return false; // No RTL support. } +#define OT_TAG(c1, c2, c3, c4) ((int32_t)((((uint32_t)(c1)&0xFF) << 24) | (((uint32_t)(c2)&0xFF) << 16) | (((uint32_t)(c3)&0xFF) << 8) | ((uint32_t)(c4)&0xFF))) + +struct FeatureInfo { + int32_t tag; + String name; +}; + +static FeatureInfo feature_set[] = { + // Registered OpenType variation tags. + { OT_TAG('i', 't', 'a', 'l'), "italic" }, + { OT_TAG('o', 'p', 's', 'z'), "optical_size" }, + { OT_TAG('s', 'l', 'n', 't'), "slant" }, + { OT_TAG('w', 'd', 't', 'h'), "width" }, + { OT_TAG('w', 'g', 'h', 't'), "weight" }, + { 0, String() }, +}; + +_FORCE_INLINE_ int32_t ot_tag_from_string(const char *p_str, int p_len) { + char tag[4]; + uint32_t i; + + if (!p_str || !p_len || !*p_str) + return OT_TAG(0, 0, 0, 0); + + if (p_len < 0 || p_len > 4) { + p_len = 4; + } + for (i = 0; i < (uint32_t)p_len && p_str[i]; i++) { + tag[i] = p_str[i]; + } + + for (; i < 4; i++) { + tag[i] = ' '; + } + + return OT_TAG(tag[0], tag[1], tag[2], tag[3]); +} + +int32_t TextServerFallback::name_to_tag(const String &p_name) const { + 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 ot_tag_from_string(p_name.replace("custom_", "").ascii().get_data(), -1); +} + +_FORCE_INLINE_ void ot_tag_to_string(int32_t p_tag, char *p_buf) { + p_buf[0] = (char)(uint8_t)(p_tag >> 24); + p_buf[1] = (char)(uint8_t)(p_tag >> 16); + p_buf[2] = (char)(uint8_t)(p_tag >> 8); + p_buf[3] = (char)(uint8_t)(p_tag >> 0); +} + +String TextServerFallback::tag_to_name(int32_t p_tag) const { + 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); + ot_tag_to_string(p_tag, name); + return String("custom_") + String(name); +} + /*************************************************************************/ -/* Font interface */ +/* Font Glyph Rendering */ /*************************************************************************/ -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."); +_FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_texture_pos_for_glyph(FontDataForSizeFallback *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) const { + FontTexturePosition ret; + ret.index = -1; + + int mw = p_width; + int mh = p_height; + + for (int i = 0; i < p_data->textures.size(); i++) { + const FontTexture &ct = p_data->textures[i]; + + if (RenderingServer::get_singleton() != nullptr) { + if (ct.texture->get_format() != p_image_format) { + continue; + } + } + + if (mw > ct.texture_w || mh > ct.texture_h) { // Too big for this texture. + continue; + } + + ret.y = 0x7FFFFFFF; + ret.x = 0; + + for (int j = 0; j < ct.texture_w - 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_h) { + 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.x * p_data->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); + + FontTexture tex; + tex.texture_w = texsize; + tex.texture_h = texsize; + tex.format = p_image_format; + tex.imgdata.resize(texsize * texsize * p_color_size); + + { + // 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, BW font. + 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, Color font, Multichannel(+True) SDF. + 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; } -RID TextServerFallback::create_font_resource(const String &p_filename, int p_base_size) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = nullptr; - if (p_filename.get_extension() == "fnt" || p_filename.get_extension() == "font") { - fd = memnew(BitmapFontDataFallback); +#ifdef MODULE_MSDFGEN_ENABLED + +struct MSContext { + msdfgen::Point2 position; + msdfgen::Shape *shape; + msdfgen::Contour *contour; +}; + +class DistancePixelConversion { + double invRange; + +public: + _FORCE_INLINE_ explicit DistancePixelConversion(double range) : + invRange(1 / range) {} + _FORCE_INLINE_ void operator()(float *pixels, const msdfgen::MultiAndTrueDistance &distance) const { + pixels[0] = float(invRange * distance.r + .5); + pixels[1] = float(invRange * distance.g + .5); + pixels[2] = float(invRange * distance.b + .5); + pixels[3] = float(invRange * distance.a + .5); + } +}; + +struct MSDFThreadData { + msdfgen::Bitmap<float, 4> *output; + msdfgen::Shape *shape; + msdfgen::Projection *projection; + DistancePixelConversion *distancePixelConversion; +}; + +static msdfgen::Point2 ft_point2(const FT_Vector &vector) { + return msdfgen::Point2(vector.x / 60.0f, vector.y / 60.0f); +} + +static int ft_move_to(const FT_Vector *to, void *user) { + MSContext *context = reinterpret_cast<MSContext *>(user); + if (!(context->contour && context->contour->edges.empty())) { + context->contour = &context->shape->addContour(); + } + context->position = ft_point2(*to); + return 0; +} + +static int ft_line_to(const FT_Vector *to, void *user) { + MSContext *context = reinterpret_cast<MSContext *>(user); + msdfgen::Point2 endpoint = ft_point2(*to); + if (endpoint != context->position) { + context->contour->addEdge(new msdfgen::LinearSegment(context->position, endpoint)); + context->position = endpoint; + } + return 0; +} + +static int ft_conic_to(const FT_Vector *control, const FT_Vector *to, void *user) { + MSContext *context = reinterpret_cast<MSContext *>(user); + context->contour->addEdge(new msdfgen::QuadraticSegment(context->position, ft_point2(*control), ft_point2(*to))); + context->position = ft_point2(*to); + return 0; +} + +static int ft_cubic_to(const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to, void *user) { + MSContext *context = reinterpret_cast<MSContext *>(user); + context->contour->addEdge(new msdfgen::CubicSegment(context->position, ft_point2(*control1), ft_point2(*control2), ft_point2(*to))); + context->position = ft_point2(*to); + return 0; +} + +void TextServerFallback::_generateMTSDF_threaded(uint32_t y, void *p_td) const { + MSDFThreadData *td = (MSDFThreadData *)p_td; + + msdfgen::ShapeDistanceFinder<msdfgen::OverlappingContourCombiner<msdfgen::MultiAndTrueDistanceSelector>> distanceFinder(*td->shape); + int row = td->shape->inverseYAxis ? td->output->height() - y - 1 : y; + for (int col = 0; col < td->output->width(); ++col) { + int x = (y % 2) ? td->output->width() - col - 1 : col; + msdfgen::Point2 p = td->projection->unproject(msdfgen::Point2(x + .5, y + .5)); + msdfgen::MultiAndTrueDistance distance = distanceFinder.distance(p); + td->distancePixelConversion->operator()(td->output->operator()(x, row), distance); + } +} + +_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(FontDataFallback *p_font_data, FontDataForSizeFallback *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const { + msdfgen::Shape shape; + + shape.contours.clear(); + shape.inverseYAxis = false; + + MSContext context = {}; + context.shape = &shape; + FT_Outline_Funcs ft_functions; + ft_functions.move_to = &ft_move_to; + ft_functions.line_to = &ft_line_to; + ft_functions.conic_to = &ft_conic_to; + ft_functions.cubic_to = &ft_cubic_to; + ft_functions.shift = 0; + ft_functions.delta = 0; + + int error = FT_Outline_Decompose(outline, &ft_functions, &context); + ERR_FAIL_COND_V_MSG(error, FontGlyph(), "FreeType: Outline decomposition error: '" + String(FT_Error_String(error)) + "'."); + if (!shape.contours.empty() && shape.contours.back().edges.empty()) { + shape.contours.pop_back(); + } + + if (FT_Outline_Get_Orientation(outline) == 1) { + for (int i = 0; i < (int)shape.contours.size(); ++i) { + shape.contours[i].reverse(); + } + } + + shape.inverseYAxis = true; + shape.normalize(); + + msdfgen::Shape::Bounds bounds = shape.getBounds(p_pixel_range); + + FontGlyph chr; + chr.found = true; + chr.advance = advance.round(); + + if (shape.validate() && shape.contours.size() > 0) { + int w = (bounds.r - bounds.l); + int h = (bounds.t - bounds.b); + + int mw = w + p_rect_margin * 2; + int mh = h + p_rect_margin * 2; + + ERR_FAIL_COND_V(mw > 4096, FontGlyph()); + ERR_FAIL_COND_V(mh > 4096, FontGlyph()); + + FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, 4, Image::FORMAT_RGBA8, mw, mh); + ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph()); + FontTexture &tex = p_data->textures.write[tex_pos.index]; + + edgeColoringSimple(shape, 3.0); // Max. angle. + msdfgen::Bitmap<real_t, 4> image(w, h); // Texture size. + //msdfgen::generateMTSDF(image, shape, p_pixel_range, 1.0, msdfgen::Vector2(-bounds.l, -bounds.b)); // Range, scale, translation. + + DistancePixelConversion distancePixelConversion(p_pixel_range); + msdfgen::Projection projection(msdfgen::Vector2(1.0, 1.0), msdfgen::Vector2(-bounds.l, -bounds.b)); + msdfgen::MSDFGeneratorConfig config(true, msdfgen::ErrorCorrectionConfig()); + + MSDFThreadData td; + td.output = ℑ + td.shape = &shape; + td.projection = &projection; + td.distancePixelConversion = &distancePixelConversion; + + if (p_font_data->work_pool.get_thread_count() == 0) { + p_font_data->work_pool.init(); + } + p_font_data->work_pool.do_work(h, this, &TextServerFallback::_generateMTSDF_threaded, &td); + + msdfgen::msdfErrorCorrection(image, shape, projection, p_pixel_range, config); + + { + 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 + p_rect_margin) * tex.texture_w + j + tex_pos.x + p_rect_margin) * 4; + ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), FontGlyph()); + wr[ofs + 0] = (uint8_t)(CLAMP(image(j, i)[0] * 256.f, 0.f, 255.f)); + wr[ofs + 1] = (uint8_t)(CLAMP(image(j, i)[1] * 256.f, 0.f, 255.f)); + wr[ofs + 2] = (uint8_t)(CLAMP(image(j, i)[2] * 256.f, 0.f, 255.f)); + wr[ofs + 3] = (uint8_t)(CLAMP(image(j, i)[3] * 256.f, 0.f, 255.f)); + } + } + } + + // Blit to image and texture. + { + if (RenderingServer::get_singleton() != nullptr) { + Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, Image::FORMAT_RGBA8, tex.imgdata)); + if (tex.texture.is_null()) { + tex.texture.instantiate(); + tex.texture->create_from_image(img); + } else { + tex.texture->update(img); + } + } + } + + // Update height array. + for (int k = tex_pos.x; k < tex_pos.x + mw; k++) { + tex.offsets.write[k] = tex_pos.y + mh; + } + + chr.texture_idx = tex_pos.index; + + chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w, h); + chr.rect.position = Vector2(bounds.l, -bounds.t); + chr.rect.size = chr.uv_rect.size; + } + return chr; +} +#endif + #ifdef MODULE_FREETYPE_ENABLED - } else if (p_filename.get_extension() == "ttf" || p_filename.get_extension() == "otf" || p_filename.get_extension() == "woff") { - fd = memnew(DynamicFontDataFallback); +_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitmap(FontDataForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const { + int w = bitmap.width; + int h = bitmap.rows; + + int mw = w + p_rect_margin * 2; + int mh = h + p_rect_margin * 2; + + ERR_FAIL_COND_V(mw > 4096, FontGlyph()); + ERR_FAIL_COND_V(mh > 4096, FontGlyph()); + + 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; + + FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, color_size, require_format, mw, mh); + ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph()); + + // Fit character in char texture. + + FontTexture &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 + p_rect_margin) * tex.texture_w + j + tex_pos.x + p_rect_margin) * color_size; + ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), FontGlyph()); + 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; + default: + ERR_FAIL_V_MSG(FontGlyph(), "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_w, tex.texture_h, 0, require_format, tex.imgdata)); + + if (tex.texture.is_null()) { + tex.texture.instantiate(); + tex.texture->create_from_image(img); + } else { + tex.texture->update(img); + } + } + } + + // Update height array. + for (int k = tex_pos.x; k < tex_pos.x + mw; k++) { + tex.offsets.write[k] = tex_pos.y + mh; + } + + FontGlyph chr; + chr.advance = (advance * p_data->scale / p_data->oversampling).round(); + chr.texture_idx = tex_pos.index; + chr.found = true; + + chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w, h); + chr.rect.position = (Vector2(xofs, -yofs) * p_data->scale / p_data->oversampling).round(); + chr.rect.size = chr.uv_rect.size * p_data->scale / p_data->oversampling; + return chr; +} #endif - } else { - return RID(); + +/*************************************************************************/ +/* Font Cache */ +/*************************************************************************/ + +_FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontDataFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(!_ensure_cache_for_size(p_font_data, p_size), false); + + FontDataForSizeFallback *fd = p_font_data->cache[p_size]; + if (fd->glyph_map.has(p_glyph)) { + return fd->glyph_map[p_glyph].found; } - Error err = fd->load_from_file(p_filename, p_base_size); - if (err != OK) { - memdelete(fd); - return RID(); + if (p_glyph == 0) { // Non graphical or invalid glyph, do not render. + fd->glyph_map[p_glyph] = FontGlyph(); + return true; } - return font_owner.make_rid(fd); +#ifdef MODULE_FREETYPE_ENABLED + FontGlyph gl; + if (fd->face) { + FT_Int32 flags = FT_LOAD_DEFAULT; + + bool outline = p_size.y > 0; + switch (p_font_data->hinting) { + case TextServer::HINTING_NONE: + flags |= FT_LOAD_NO_HINTING; + break; + case TextServer::HINTING_LIGHT: + flags |= FT_LOAD_TARGET_LIGHT; + break; + default: + flags |= FT_LOAD_TARGET_NORMAL; + break; + } + if (p_font_data->force_autohinter) { + flags |= FT_LOAD_FORCE_AUTOHINT; + } + if (outline) { + flags |= FT_LOAD_NO_BITMAP; + } else if (FT_HAS_COLOR(fd->face)) { + flags |= FT_LOAD_COLOR; + } + + FT_Fixed v, h; + FT_Get_Advance(fd->face, p_glyph, flags, &h); + FT_Get_Advance(fd->face, p_glyph, flags | FT_LOAD_VERTICAL_LAYOUT, &v); + + int error = FT_Load_Glyph(fd->face, p_glyph, flags); + if (error) { + fd->glyph_map[p_glyph] = FontGlyph(); + ERR_FAIL_V_MSG(false, "FreeType: Failed to load glyph."); + } + + if (!outline) { + if (!p_font_data->msdf) { + error = FT_Render_Glyph(fd->face->glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO); + } + FT_GlyphSlot slot = fd->face->glyph; + if (!error) { + if (p_font_data->msdf) { +#ifdef MODULE_MSDFGEN_ENABLED + gl = rasterize_msdf(p_font_data, fd, p_font_data->msdf_range, rect_range, &slot->outline, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0); +#else + fd->glyph_map[p_glyph] = FontGlyph(); + ERR_FAIL_V_MSG(false, "Compiled without MSDFGEN support!"); +#endif + } else { + gl = rasterize_bitmap(fd, rect_range, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0); + } + } + } else { + FT_Stroker stroker; + if (FT_Stroker_New(library, &stroker) != 0) { + fd->glyph_map[p_glyph] = FontGlyph(); + ERR_FAIL_V_MSG(false, "FreeType: Failed to load glyph stroker."); + } + + FT_Stroker_Set(stroker, (int)(fd->size.y * fd->oversampling * 16.0), FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0); + FT_Glyph glyph; + FT_BitmapGlyph glyph_bitmap; + + if (FT_Get_Glyph(fd->face->glyph, &glyph) != 0) { + goto cleanup_stroker; + } + if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) { + goto cleanup_glyph; + } + if (FT_Glyph_To_Bitmap(&glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1) != 0) { + goto cleanup_glyph; + } + glyph_bitmap = (FT_BitmapGlyph)glyph; + gl = rasterize_bitmap(fd, rect_range, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2()); + + cleanup_glyph: + FT_Done_Glyph(glyph); + cleanup_stroker: + FT_Stroker_Done(stroker); + } + fd->glyph_map[p_glyph] = gl; + return gl.found; + } +#endif + fd->glyph_map[p_glyph] = FontGlyph(); + return false; } -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 == "fnt" || p_type == "font") { - fd = memnew(BitmapFontDataFallback); +_FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback *p_font_data, const Vector2i &p_size) const { + if (p_font_data->cache.has(p_size)) { + return true; + } + + FontDataForSizeFallback *fd = memnew(FontDataForSizeFallback); + fd->size = p_size; + if (p_font_data->data_ptr) { + // Init dynamic font. #ifdef MODULE_FREETYPE_ENABLED - } else if (p_type == "ttf" || p_type == "otf" || p_type == "woff") { - fd = memnew(DynamicFontDataFallback); + int error = 0; + if (!library) { + error = FT_Init_FreeType(&library); + ERR_FAIL_COND_V_MSG(error != 0, false, TTR("FreeType: Error initializing library:") + " '" + String(FT_Error_String(error)) + "'."); + } + + memset(&fd->stream, 0, sizeof(FT_StreamRec)); + fd->stream.base = (unsigned char *)p_font_data->data_ptr; + fd->stream.size = p_font_data->data_size; + fd->stream.pos = 0; + + FT_Open_Args fargs; + memset(&fargs, 0, sizeof(FT_Open_Args)); + fargs.memory_base = (unsigned char *)p_font_data->data_ptr; + fargs.memory_size = p_font_data->data_size; + fargs.flags = FT_OPEN_MEMORY; + fargs.stream = &fd->stream; + error = FT_Open_Face(library, &fargs, 0, &fd->face); + if (error) { + FT_Done_Face(fd->face); + fd->face = nullptr; + ERR_FAIL_V_MSG(false, TTR("FreeType: Error loading font:") + " '" + String(FT_Error_String(error)) + "'."); + } + + if (p_font_data->msdf) { + fd->oversampling = 1.0f; + fd->size.x = p_font_data->msdf_source_size; + } else if (p_font_data->oversampling <= 0.0f) { + fd->oversampling = TS->font_get_global_oversampling(); + } else { + fd->oversampling = p_font_data->oversampling; + } + + if (FT_HAS_COLOR(fd->face) && fd->face->num_fixed_sizes > 0) { + int best_match = 0; + int diff = ABS(fd->size.x - ((int64_t)fd->face->available_sizes[0].width)); + fd->scale = real_t(fd->size.x * fd->oversampling) / fd->face->available_sizes[0].width; + for (int i = 1; i < fd->face->num_fixed_sizes; i++) { + int ndiff = ABS(fd->size.x - ((int64_t)fd->face->available_sizes[i].width)); + if (ndiff < diff) { + best_match = i; + diff = ndiff; + fd->scale = real_t(fd->size.x * fd->oversampling) / fd->face->available_sizes[i].width; + } + } + FT_Select_Size(fd->face, best_match); + } else { + FT_Set_Pixel_Sizes(fd->face, 0, fd->size.x * fd->oversampling); + } + + fd->ascent = (fd->face->size->metrics.ascender / 64.0) / fd->oversampling * fd->scale; + fd->descent = (-fd->face->size->metrics.descender / 64.0) / fd->oversampling * fd->scale; + fd->underline_position = (-FT_MulFix(fd->face->underline_position, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale; + fd->underline_thickness = (FT_MulFix(fd->face->underline_thickness, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale; + + if (!p_font_data->face_init) { + // Read OpenType variations. + p_font_data->supported_varaitions.clear(); + if (fd->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) { + FT_MM_Var *amaster; + FT_Get_MM_Var(fd->face, &amaster); + for (FT_UInt i = 0; i < amaster->num_axis; i++) { + p_font_data->supported_varaitions[(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); + } + p_font_data->face_init = true; + } + + // Write variations. + if (fd->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) { + FT_MM_Var *amaster; + + FT_Get_MM_Var(fd->face, &amaster); + + Vector<FT_Fixed> coords; + coords.resize(amaster->num_axis); + + FT_Get_Var_Design_Coordinates(fd->face, coords.size(), coords.ptrw()); + + for (FT_UInt i = 0; i < amaster->num_axis; i++) { + // Reset to default. + int32_t var_tag = amaster->axis[i].tag; + real_t var_value = (double)amaster->axis[i].def / 65536.f; + coords.write[i] = amaster->axis[i].def; + + if (p_font_data->variation_coordinates.has(var_tag)) { + var_value = p_font_data->variation_coordinates[var_tag]; + coords.write[i] = CLAMP(var_value * 65536.f, amaster->axis[i].minimum, amaster->axis[i].maximum); + } + + if (p_font_data->variation_coordinates.has(tag_to_name(var_tag))) { + var_value = p_font_data->variation_coordinates[tag_to_name(var_tag)]; + coords.write[i] = CLAMP(var_value * 65536.f, amaster->axis[i].minimum, amaster->axis[i].maximum); + } + } + + FT_Set_Var_Design_Coordinates(fd->face, coords.size(), coords.ptrw()); + FT_Done_MM_Var(library, amaster); + } +#else + ERR_FAIL_V_MSG(false, TTR("FreeType: Can't load dynamic font, engine is compiled without FreeType support!"); #endif - } else { - return RID(); } + p_font_data->cache[p_size] = fd; + return true; +} - Error err = fd->load_from_memory(p_data, p_size, p_base_size); - if (err != OK) { - memdelete(fd); - return RID(); +_FORCE_INLINE_ void TextServerFallback::_font_clear_cache(FontDataFallback *p_font_data) { + for (const Map<Vector2i, FontDataForSizeFallback *>::Element *E = p_font_data->cache.front(); E; E = E->next()) { + memdelete(E->get()); } + p_font_data->cache.clear(); + p_font_data->face_init = false; + p_font_data->supported_varaitions.clear(); +} + +RID TextServerFallback::create_font() { + FontDataFallback *fd = memnew(FontDataFallback); + return font_owner.make_rid(fd); } -RID TextServerFallback::create_font_bitmap(float p_height, float p_ascent, int p_base_size) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = memnew(BitmapFontDataFallback); - Error err = fd->bitmap_new(p_height, p_ascent, p_base_size); - if (err != OK) { - memdelete(fd); - return RID(); +void TextServerFallback::font_set_data(RID p_font_rid, const PackedByteArray &p_data) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + _font_clear_cache(fd); + fd->data = p_data; + fd->data_ptr = fd->data.ptr(); + fd->data_size = fd->data.size(); +} + +void TextServerFallback::font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + _font_clear_cache(fd); + fd->data.clear(); + fd->data_ptr = p_data_ptr; + fd->data_size = p_data_size; +} + +void TextServerFallback::font_set_antialiased(RID p_font_rid, bool p_antialiased) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->antialiased != p_antialiased) { + _font_clear_cache(fd); + fd->antialiased = p_antialiased; } +} - return font_owner.make_rid(fd); +bool TextServerFallback::font_is_antialiased(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->antialiased; } -void TextServerFallback::font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +void TextServerFallback::font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->bitmap_add_texture(p_texture); + + MutexLock lock(fd->mutex); + if (fd->msdf != p_msdf) { + _font_clear_cache(fd); + fd->msdf = p_msdf; + } } -void TextServerFallback::font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +bool TextServerFallback::font_is_multichannel_signed_distance_field(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->msdf; +} + +void TextServerFallback::font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->bitmap_add_char(p_char, p_texture_idx, p_rect, p_align, p_advance); + + MutexLock lock(fd->mutex); + if (fd->msdf_range != p_msdf_pixel_range) { + _font_clear_cache(fd); + fd->msdf_range = p_msdf_pixel_range; + } } -void TextServerFallback::font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +int TextServerFallback::font_get_msdf_pixel_range(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->msdf_range; +} + +void TextServerFallback::font_set_msdf_size(RID p_font_rid, int p_msdf_size) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->bitmap_add_kerning_pair(p_A, p_B, p_kerning); + + MutexLock lock(fd->mutex); + if (fd->msdf_source_size != p_msdf_size) { + _font_clear_cache(fd); + fd->msdf_source_size = p_msdf_size; + } } -float TextServerFallback::font_get_height(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); +int TextServerFallback::font_get_msdf_size(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->msdf_source_size; +} + +void TextServerFallback::font_set_fixed_size(RID p_font_rid, int p_fixed_size) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->fixed_size != p_fixed_size) { + fd->fixed_size = p_fixed_size; + } +} + +int TextServerFallback::font_get_fixed_size(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->fixed_size; +} + +void TextServerFallback::font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->force_autohinter != p_force_autohinter) { + _font_clear_cache(fd); + fd->force_autohinter = p_force_autohinter; + } +} + +bool TextServerFallback::font_is_force_autohinter(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->force_autohinter; +} + +void TextServerFallback::font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->hinting != p_hinting) { + _font_clear_cache(fd); + fd->hinting = p_hinting; + } +} + +TextServer::Hinting TextServerFallback::font_get_hinting(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, HINTING_NONE); + + MutexLock lock(fd->mutex); + return fd->hinting; +} + +void TextServerFallback::font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->variation_coordinates != p_variation_coordinates) { + _font_clear_cache(fd); + fd->variation_coordinates = p_variation_coordinates; + } +} + +Dictionary TextServerFallback::font_get_variation_coordinates(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Dictionary()); + + MutexLock lock(fd->mutex); + return fd->variation_coordinates; +} + +void TextServerFallback::font_set_oversampling(RID p_font_rid, real_t p_oversampling) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->oversampling != p_oversampling) { + _font_clear_cache(fd); + fd->oversampling = p_oversampling; + } +} + +real_t TextServerFallback::font_get_oversampling(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_height(p_size); + + MutexLock lock(fd->mutex); + return fd->oversampling; } -float TextServerFallback::font_get_ascent(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); +Array TextServerFallback::font_get_size_cache_list(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Array()); + + MutexLock lock(fd->mutex); + Array ret; + for (const Map<Vector2i, FontDataForSizeFallback *>::Element *E = fd->cache.front(); E; E = E->next()) { + ret.push_back(E->key()); + } + return ret; +} + +void TextServerFallback::font_clear_size_cache(RID p_font_rid) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + for (const Map<Vector2i, FontDataForSizeFallback *>::Element *E = fd->cache.front(); E; E = E->next()) { + memdelete(E->get()); + } + fd->cache.clear(); +} + +void TextServerFallback::font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->cache.has(p_size)) { + memdelete(fd->cache[p_size]); + fd->cache.erase(p_size); + } +} + +void TextServerFallback::font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->ascent = p_ascent; +} + +real_t TextServerFallback::font_get_ascent(RID p_font_rid, int p_size) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_ascent(p_size); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f); + + if (fd->msdf) { + return fd->cache[size]->ascent * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->ascent; + } } -float TextServerFallback::font_get_descent(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); +void TextServerFallback::font_set_descent(RID p_font_rid, int p_size, real_t p_descent) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->descent = p_descent; +} + +real_t TextServerFallback::font_get_descent(RID p_font_rid, int p_size) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_descent(p_size); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f); + + if (fd->msdf) { + return fd->cache[size]->descent * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->descent; + } } -float TextServerFallback::font_get_underline_position(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); +void TextServerFallback::font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->underline_position = p_underline_position; +} + +real_t TextServerFallback::font_get_underline_position(RID p_font_rid, int p_size) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_underline_position(p_size); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f); + + if (fd->msdf) { + return fd->cache[size]->underline_position * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->underline_position; + } } -float TextServerFallback::font_get_underline_thickness(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); +void TextServerFallback::font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->underline_thickness = p_underline_thickness; +} + +real_t TextServerFallback::font_get_underline_thickness(RID p_font_rid, int p_size) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_underline_thickness(p_size); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f); + + if (fd->msdf) { + return fd->cache[size]->underline_thickness * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->underline_thickness; + } } -int TextServerFallback::font_get_spacing_space(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, 0); - return fd->get_spacing_space(); +void TextServerFallback::font_set_scale(RID p_font_rid, int p_size, real_t p_scale) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->scale = p_scale; } -void TextServerFallback::font_set_spacing_space(RID p_font, int p_value) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +real_t TextServerFallback::font_get_scale(RID p_font_rid, int p_size) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, 0.f); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f); + + if (fd->msdf) { + return fd->cache[size]->scale * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->scale / fd->cache[size]->oversampling; + } +} + +void TextServerFallback::font_set_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing, int p_value) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_spacing_space(p_value); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + switch (p_spacing) { + case TextServer::SPACING_GLYPH: { + fd->cache[size]->spacing_glyph = p_value; + } break; + case TextServer::SPACING_SPACE: { + fd->cache[size]->spacing_space = p_value; + } break; + default: { + ERR_FAIL_MSG("Invalid spacing type: " + itos(p_spacing)); + } break; + } } -int TextServerFallback::font_get_spacing_glyph(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); +int TextServerFallback::font_get_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0); - return fd->get_spacing_glyph(); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0); + + switch (p_spacing) { + case TextServer::SPACING_GLYPH: { + if (fd->msdf) { + return fd->cache[size]->spacing_glyph * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->spacing_glyph; + } + } break; + case TextServer::SPACING_SPACE: { + if (fd->msdf) { + return fd->cache[size]->spacing_space * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->spacing_space; + } + } break; + default: { + ERR_FAIL_V_MSG(0, "Invalid spacing type: " + itos(p_spacing)); + } break; + } + return 0; } -void TextServerFallback::font_set_spacing_glyph(RID p_font, int p_value) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +int TextServerFallback::font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, 0); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0); + + return fd->cache[size]->textures.size(); +} + +void TextServerFallback::font_clear_textures(RID p_font_rid, const Vector2i &p_size) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_spacing_glyph(p_value); + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->textures.clear(); } -void TextServerFallback::font_set_antialiased(RID p_font, bool p_antialiased) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +void TextServerFallback::font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_antialiased(p_antialiased); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + ERR_FAIL_INDEX(p_texture_index, fd->cache[size]->textures.size()); + + fd->cache[size]->textures.remove(p_texture_index); } -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_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + ERR_FAIL_COND(p_image.is_null()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + ERR_FAIL_COND(p_texture_index < 0); + if (p_texture_index >= fd->cache[size]->textures.size()) { + fd->cache[size]->textures.resize(p_texture_index + 1); + } + + FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + + tex.imgdata = p_image->get_data(); + tex.texture_w = p_image->get_width(); + tex.texture_h = p_image->get_height(); + tex.format = p_image->get_format(); + + Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, tex.format, tex.imgdata)); + tex.texture = Ref<ImageTexture>(); + tex.texture.instantiate(); + tex.texture->create_from_image(img); } -void TextServerFallback::font_set_distance_field_hint(RID p_font, bool p_distance_field) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +Ref<Image> TextServerFallback::font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Ref<Image>()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Ref<Image>()); + ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), Ref<Image>()); + + const FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, tex.format, tex.imgdata)); + + return img; +} + +void TextServerFallback::font_set_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_distance_field_hint(p_distance_field); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + if (p_texture_index >= fd->cache[size]->textures.size()) { + fd->cache[size]->textures.resize(p_texture_index + 1); + } + + FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + tex.offsets = p_offset; } -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(); +PackedInt32Array TextServerFallback::font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, PackedInt32Array()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), PackedInt32Array()); + ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), PackedInt32Array()); + + const FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + return tex.offsets; } -void TextServerFallback::font_set_hinting(RID p_font, TextServer::Hinting p_hinting) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +Array TextServerFallback::font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Array()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array()); + + Array ret; + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + const int32_t *E = nullptr; + while ((E = gl.next(E))) { + ret.push_back(*E); + } + return ret; +} + +void TextServerFallback::font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_hinting(p_hinting); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + fd->cache[size]->glyph_map.clear(); } -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_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + fd->cache[size]->glyph_map.erase(p_glyph); } -void TextServerFallback::font_set_force_autohinter(RID p_font, bool p_enabeld) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +Vector2 TextServerFallback::font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Vector2()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + if (!_ensure_glyph(fd, size, p_glyph)) { + return Vector2(); // Invalid or non graphicl glyph, do not display errors. + } + + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + if (fd->msdf) { + return gl[p_glyph].advance * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return gl[p_glyph].advance; + } +} + +void TextServerFallback::font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_force_autohinter(p_enabeld); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].advance = p_advance; + gl[p_glyph].found = true; } -bool TextServerFallback::font_get_force_autohinter(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); +Vector2 TextServerFallback::font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Vector2()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + if (!_ensure_glyph(fd, size, p_glyph)) { + return Vector2(); // Invalid or non graphicl glyph, do not display errors. + } + + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + if (fd->msdf) { + return gl[p_glyph].rect.position * (real_t)p_size.x / (real_t)fd->msdf_source_size; + } else { + return gl[p_glyph].rect.position; + } +} + +void TextServerFallback::font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].rect.position = p_offset; + gl[p_glyph].found = true; +} + +Vector2 TextServerFallback::font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Vector2()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + if (!_ensure_glyph(fd, size, p_glyph)) { + return Vector2(); // Invalid or non graphicl glyph, do not display errors. + } + + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + if (fd->msdf) { + return gl[p_glyph].rect.size * (real_t)p_size.x / (real_t)fd->msdf_source_size; + } else { + return gl[p_glyph].rect.size; + } +} + +void TextServerFallback::font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].rect.size = p_gl_size; + gl[p_glyph].found = true; +} + +Rect2 TextServerFallback::font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Rect2()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Rect2()); + if (!_ensure_glyph(fd, size, p_glyph)) { + return Rect2(); // Invalid or non graphicl glyph, do not display errors. + } + + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + return gl[p_glyph].uv_rect; +} + +void TextServerFallback::font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].uv_rect = p_uv_rect; + gl[p_glyph].found = true; +} + +int TextServerFallback::font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, -1); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), -1); + if (!_ensure_glyph(fd, size, p_glyph)) { + return -1; // Invalid or non graphicl glyph, do not display errors. + } + + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + return gl[p_glyph].texture_idx; +} + +void TextServerFallback::font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].texture_idx = p_texture_idx; + gl[p_glyph].found = true; +} + +bool TextServerFallback::font_get_glyph_contours(RID p_font_rid, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, false); - return fd->get_force_autohinter(); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), false); + +#ifdef MODULE_FREETYPE_ENABLED + int error = FT_Load_Glyph(fd->cache[size]->face, p_index, FT_LOAD_NO_BITMAP | (fd->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)); + ERR_FAIL_COND_V(error, false); + + r_points.clear(); + r_contours.clear(); + + real_t h = fd->cache[size]->ascent; + real_t scale = (1.0 / 64.0) / fd->cache[size]->oversampling * fd->cache[size]->scale; + if (fd->msdf) { + scale = scale * (real_t)p_size / (real_t)fd->msdf_source_size; + } + for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_points; i++) { + r_points.push_back(Vector3(fd->cache[size]->face->glyph->outline.points[i].x * scale, h - fd->cache[size]->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fd->cache[size]->face->glyph->outline.tags[i]))); + } + for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_contours; i++) { + r_contours.push_back(fd->cache[size]->face->glyph->outline.contours[i]); + } + r_orientation = (FT_Outline_Get_Orientation(&fd->cache[size]->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT); +#else + return false; +#endif + return true; } -bool TextServerFallback::font_has_char(RID p_font, char32_t p_char) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); +Array TextServerFallback::font_get_kerning_list(RID p_font_rid, int p_size) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Array()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array()); + + Array ret; + for (const Map<Vector2i, Vector2>::Element *E = fd->cache[size]->kerning_map.front(); E; E = E->next()) { + ret.push_back(E->key()); + } + return ret; +} + +void TextServerFallback::font_clear_kerning_map(RID p_font_rid, int p_size) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->kerning_map.clear(); +} + +void TextServerFallback::font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->kerning_map.erase(p_glyph_pair); +} + +void TextServerFallback::font_set_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->kerning_map[p_glyph_pair] = p_kerning; +} + +Vector2 TextServerFallback::font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Vector2()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + + const Map<Vector2i, Vector2> &kern = fd->cache[size]->kerning_map; + + if (kern.has(p_glyph_pair)) { + if (fd->msdf) { + return kern[p_glyph_pair] * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return kern[p_glyph_pair]; + } + } else { +#ifdef MODULE_FREETYPE_ENABLED + if (fd->cache[size]->face) { + FT_Vector delta; + FT_Get_Kerning(fd->cache[size]->face, p_glyph_pair.x, p_glyph_pair.y, FT_KERNING_DEFAULT, &delta); + if (fd->msdf) { + return Vector2(delta.x, delta.y) * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return Vector2(delta.x, delta.y); + } + } +#endif + } + return Vector2(); +} + +int32_t TextServerFallback::font_get_glyph_index(RID p_font_rid, int p_size, char32_t p_char, char32_t p_variation_selector) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, 0); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0); + +#ifdef MODULE_FREETYPE_ENABLED + if (fd->cache[size]->face) { + if (p_variation_selector) { + return FT_Face_GetCharVariantIndex(fd->cache[size]->face, p_char, p_variation_selector); + } else { + return FT_Get_Char_Index(fd->cache[size]->face, p_char); + } + } else { + return 0; + } +#else + return (int32_t)p_char; +#endif +} + +bool TextServerFallback::font_has_char(RID p_font_rid, char32_t p_char) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, false); - return fd->has_char(p_char); + + MutexLock lock(fd->mutex); + if (fd->cache.is_empty()) { + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), false); + } + FontDataForSizeFallback *at_size = fd->cache.front()->get(); + +#ifdef MODULE_FREETYPE_ENABLED + if (at_size && at_size->face) { + return FT_Get_Char_Index(at_size->face, p_char) != 0; + } +#endif + return (at_size) ? at_size->glyph_map.has((int32_t)p_char) : false; } -String TextServerFallback::font_get_supported_chars(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); +String TextServerFallback::font_get_supported_chars(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, String()); - return fd->get_supported_chars(); + + MutexLock lock(fd->mutex); + if (fd->cache.is_empty()) { + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), String()); + } + FontDataForSizeFallback *at_size = fd->cache.front()->get(); + + String chars; +#ifdef MODULE_FREETYPE_ENABLED + if (at_size && at_size->face) { + FT_UInt gindex; + FT_ULong charcode = FT_Get_First_Char(at_size->face, &gindex); + while (gindex != 0) { + if (charcode != 0) { + chars += char32_t(charcode); + } + charcode = FT_Get_Next_Char(at_size->face, charcode, &gindex); + } + return chars; + } +#endif + if (at_size) { + const HashMap<int32_t, FontGlyph> &gl = at_size->glyph_map; + const int32_t *E = nullptr; + while ((E = gl.next(E))) { + chars += char32_t(*E); + } + } + return 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(); +void TextServerFallback::font_render_range(RID p_font_rid, const Vector2i &p_size, char32_t p_start, char32_t p_end) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + for (char32_t i = p_start; i <= p_end; i++) { +#ifdef MODULE_FREETYPE_ENABLED + if (fd->cache[size]->face) { + _ensure_glyph(fd, size, FT_Get_Char_Index(fd->cache[size]->face, i)); + continue; + } +#endif + _ensure_glyph(fd, size, (int32_t)i); + } } -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(); +void TextServerFallback::font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + ERR_FAIL_COND(!_ensure_glyph(fd, size, p_index)); } -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]]; +void TextServerFallback::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + if (!_ensure_glyph(fd, size, p_index)) { + return; // // Invalid or non graphicl glyph, do not display errors, nothing to draw. + } + + const FontGlyph &gl = fd->cache[size]->glyph_map[p_index]; + if (gl.found) { + ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size()); + + if (gl.texture_idx != -1) { + Color modulate = p_color; +#ifdef MODULE_FREETYPE_ENABLED + if (fd->cache[size]->face && FT_HAS_COLOR(fd->cache[size]->face)) { + modulate.r = modulate.g = modulate.b = 1.0; + } +#endif + if (RenderingServer::get_singleton() != nullptr) { + RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid(); + if (fd->msdf) { + Point2 cpos = p_pos; + cpos += gl.rect.position * (real_t)p_size / (real_t)fd->msdf_source_size; + Size2 csize = gl.rect.size * (real_t)p_size / (real_t)fd->msdf_source_size; + RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, 0, fd->msdf_range); + } else { + Point2i cpos = p_pos; + cpos += gl.rect.position; + Size2i csize = gl.rect.size; + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + } } } - 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); +void TextServerFallback::font_draw_glyph_outline(RID p_font_rid, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, Vector2i(p_size, p_outline_size)); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + if (!_ensure_glyph(fd, size, p_index)) { + return; // // Invalid or non graphicl glyph, do not display errors, nothing to draw. + } + + const FontGlyph &gl = fd->cache[size]->glyph_map[p_index]; + if (gl.found) { + ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size()); + + if (gl.texture_idx != -1) { + Color modulate = p_color; +#ifdef MODULE_FREETYPE_ENABLED + if (fd->cache[size]->face && FT_HAS_COLOR(fd->cache[size]->face)) { + modulate.r = modulate.g = modulate.b = 1.0; + } +#endif + if (RenderingServer::get_singleton() != nullptr) { + RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid(); + if (fd->msdf) { + Point2 cpos = p_pos; + cpos += gl.rect.position * (real_t)p_size / (real_t)fd->msdf_source_size; + Size2 csize = gl.rect.size * (real_t)p_size / (real_t)fd->msdf_source_size; + RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, p_outline_size * 2, fd->msdf_range); + } else { + Point2i cpos = p_pos; + cpos += gl.rect.position; + Size2i csize = gl.rect.size; + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + } + } + } + } +} + +bool TextServerFallback::font_is_language_supported(RID p_font_rid, const String &p_language) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + if (fd->language_support_overrides.has(p_language)) { + return fd->language_support_overrides[p_language]; + } else { + return true; + } +} + +void TextServerFallback::font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->lang_support_overrides[p_language] = p_supported; + + MutexLock lock(fd->mutex); + fd->language_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); +bool TextServerFallback::font_get_language_support_override(RID p_font_rid, const String &p_language) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, false); - return fd->lang_support_overrides[p_language]; + + MutexLock lock(fd->mutex); + return fd->language_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); +void TextServerFallback::font_remove_language_support_override(RID p_font_rid, const String &p_language) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->lang_support_overrides.erase(p_language); + + MutexLock lock(fd->mutex); + fd->language_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); +Vector<String> TextServerFallback::font_get_language_support_overrides(RID p_font_rid) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); 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()); + + MutexLock lock(fd->mutex); + Vector<String> out; + for (const Map<String, bool>::Element *E = fd->language_support_overrides.front(); E; E = E->next()) { + out.push_back(E->key()); } - return ret; + return out; } -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); +bool TextServerFallback::font_is_script_supported(RID p_font_rid, const String &p_script) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); if (fd->script_support_overrides.has(p_script)) { return fd->script_support_overrides[p_script]; } else { @@ -392,95 +1890,84 @@ bool TextServerFallback::font_is_script_supported(RID p_font, const String &p_sc } } -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); +void TextServerFallback::font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); 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); +bool TextServerFallback::font_get_script_support_override(RID p_font_rid, const String &p_script) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); 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); +void TextServerFallback::font_remove_script_support_override(RID p_font_rid, const String &p_script) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, 16); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); 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); +Vector<String> TextServerFallback::font_get_script_support_overrides(RID p_font_rid) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); 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); + MutexLock lock(fd->mutex); + Vector<String> out; + for (const Map<String, bool>::Element *E = fd->script_support_overrides.front(); E; E = E->next()) { + out.push_back(E->key()); + } + return out; } -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); +Dictionary TextServerFallback::font_supported_feature_list(RID p_font_rid) const { + return Dictionary(); } -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); -} +Dictionary TextServerFallback::font_supported_variation_list(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Dictionary()); -bool TextServerFallback::font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, false); - return fd->get_glyph_contours(p_size, p_index, r_points, r_contours, r_orientation); + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, 16); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Dictionary()); + return fd->supported_varaitions; } -float TextServerFallback::font_get_oversampling() const { +real_t TextServerFallback::font_get_global_oversampling() const { return oversampling; } -void TextServerFallback::font_set_oversampling(float p_oversampling) { +void TextServerFallback::font_set_global_oversampling(real_t p_oversampling) { _THREAD_SAFE_METHOD_ if (oversampling != p_oversampling) { oversampling = p_oversampling; List<RID> fonts; font_owner.get_owned_list(&fonts); + bool font_cleared = false; for (const RID &E : fonts) { - font_owner.getornull(E)->clear_cache(); + if (!font_is_multichannel_signed_distance_field(E) && font_get_oversampling(E) <= 0) { + font_clear_size_cache(E); + font_cleared = true; + } } - } -} -Vector<String> TextServerFallback::get_system_fonts() const { - return Vector<String>(); + if (font_cleared) { + List<RID> text_bufs; + shaped_owner.get_owned_list(&text_bufs); + for (const RID &E : text_bufs) { + invalidate(shaped_owner.getornull(E)); + } + } + } } /*************************************************************************/ @@ -533,10 +2020,10 @@ RID TextServerFallback::create_shaped_text(TextServer::Direction p_direction, Te } void TextServerFallback::shaped_text_clear(RID p_shaped) { - _THREAD_SAFE_METHOD_ ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND(!sd); + MutexLock lock(sd->mutex); sd->parent = RID(); sd->start = 0; sd->end = 0; @@ -557,9 +2044,10 @@ TextServer::Direction TextServerFallback::shaped_text_get_direction(RID p_shaped } 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); + + MutexLock lock(sd->mutex); if (sd->orientation != p_orientation) { if (sd->parent != RID()) { full_copy(sd); @@ -570,19 +2058,21 @@ void TextServerFallback::shaped_text_set_orientation(RID p_shaped, TextServer::O } void TextServerFallback::shaped_text_set_bidi_override(RID p_shaped, const Vector<Vector2i> &p_override) { - //No BiDi support, ignore. + // 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); + + MutexLock lock(sd->mutex); 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); + + MutexLock lock(sd->mutex); ERR_FAIL_COND(!sd); if (sd->preserve_invalid != p_enabled) { if (sd->parent != RID()) { @@ -594,16 +2084,18 @@ void TextServerFallback::shaped_text_set_preserve_invalid(RID p_shaped, bool p_e } 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); + + MutexLock lock(sd->mutex); 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); + + MutexLock lock(sd->mutex); if (sd->preserve_control != p_enabled) { if (sd->parent != RID()) { full_copy(sd); @@ -614,18 +2106,24 @@ void TextServerFallback::shaped_text_set_preserve_control(RID p_shaped, bool p_e } 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); + + MutexLock lock(sd->mutex); 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); + + MutexLock lock(sd->mutex); ERR_FAIL_COND_V(p_size <= 0, false); + for (int i = 0; i < p_fonts.size(); i++) { + ERR_FAIL_COND_V(!font_owner.getornull(p_fonts[i]), false); + } + if (p_text.is_empty()) { return true; } @@ -637,6 +2135,7 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te 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. Vector<RID> fonts_no_match; int font_count = p_fonts.size(); @@ -661,10 +2160,11 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te 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_ +bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align, int p_length) { ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); + + MutexLock lock(sd->mutex); ERR_FAIL_COND_V(p_key == Variant(), false); ERR_FAIL_COND_V(sd->objects.has(p_key), false); @@ -691,10 +2191,11 @@ bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, con 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_ +bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align) { ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); + + MutexLock lock(sd->mutex); 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; @@ -706,8 +2207,6 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, 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]; @@ -724,48 +2223,20 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, 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 (gl.font_rid.is_valid()) { 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)); + sd->ascent = MAX(sd->ascent, font_get_ascent(gl.font_rid, gl.font_size)); + sd->descent = MAX(sd->descent, font_get_descent(gl.font_rid, 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->ascent = MAX(sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); + sd->descent = MAX(sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).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)); @@ -784,43 +2255,80 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, } // Align embedded objects to baseline. + real_t full_ascent = sd->ascent; + real_t full_descent = sd->descent; 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: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.y = -sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.y = -(E->get().rect.size.y / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.y = (-sd->ascent + sd->descent) / 2; + } break; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.y = 0; } break; - case VALIGN_BOTTOM: { - E->get().rect.position.y = sd->descent - E->get().rect.size.y; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.y = sd->descent; } break; } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.y -= E->get().rect.size.y; + } break; + case INLINE_ALIGN_CENTER_TO: { + E->get().rect.position.y -= E->get().rect.size.y / 2; + } break; + case INLINE_ALIGN_TOP_TO: { + //NOP + } break; + } + full_ascent = MAX(full_ascent, -E->get().rect.position.y); + full_descent = MAX(full_descent, E->get().rect.position.y + E->get().rect.size.y); } else { - switch (E->get().inline_align) { - case VALIGN_TOP: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.x = -sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.x = -(E->get().rect.size.x / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.x = (-sd->ascent + sd->descent) / 2; + } break; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.x = 0; + } break; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.x = sd->descent; } break; - case VALIGN_BOTTOM: { - E->get().rect.position.x = sd->descent - E->get().rect.size.x; + } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.x -= E->get().rect.size.x; + } break; + case INLINE_ALIGN_CENTER_TO: { + E->get().rect.position.x -= E->get().rect.size.x / 2; + } break; + case INLINE_ALIGN_TOP_TO: { + //NOP } break; } + full_ascent = MAX(full_ascent, -E->get().rect.position.x); + full_descent = MAX(full_descent, E->get().rect.position.x + E->get().rect.size.x); } } } + sd->ascent = full_ascent; + sd->descent = full_descent; } 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()); + + MutexLock lock(sd->mutex); if (sd->parent != RID()) { return shaped_text_substr(sd->parent, p_start, p_length); } @@ -869,43 +2377,18 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng 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 (gl.font_rid.is_valid()) { 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)); + new_sd->ascent = MAX(new_sd->ascent, font_get_ascent(gl.font_rid, gl.font_size)); + new_sd->descent = MAX(new_sd->descent, font_get_descent(gl.font_rid, 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)); + new_sd->ascent = MAX(new_sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); + new_sd->descent = MAX(new_sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).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. @@ -923,35 +2406,72 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng } } + // Align embedded objects to baseline. + real_t full_ascent = new_sd->ascent; + real_t full_descent = new_sd->descent; 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: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.y = -new_sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.y = -(E->get().rect.size.y / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.y = (-new_sd->ascent + new_sd->descent) / 2; + } break; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.y = 0; + } break; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.y = new_sd->descent; + } break; + } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.y -= E->get().rect.size.y; + } break; + case INLINE_ALIGN_CENTER_TO: { + 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; + case INLINE_ALIGN_TOP_TO: { + //NOP } break; } + full_ascent = MAX(full_ascent, -E->get().rect.position.y); + full_descent = MAX(full_descent, E->get().rect.position.y + E->get().rect.size.y); } else { - switch (E->get().inline_align) { - case VALIGN_TOP: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.x = -new_sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.x = -(E->get().rect.size.x / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.x = (-new_sd->ascent + new_sd->descent) / 2; } break; - case VALIGN_BOTTOM: { - E->get().rect.position.x = new_sd->descent - E->get().rect.size.x; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.x = 0; + } break; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.x = new_sd->descent; } break; } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.x -= E->get().rect.size.x; + } break; + case INLINE_ALIGN_CENTER_TO: { + E->get().rect.position.x -= E->get().rect.size.x / 2; + } break; + case INLINE_ALIGN_TOP_TO: { + //NOP + } break; + } + full_ascent = MAX(full_ascent, -E->get().rect.position.x); + full_descent = MAX(full_descent, E->get().rect.position.x + E->get().rect.size.x); } } } + new_sd->ascent = full_ascent; + new_sd->descent = full_descent; } new_sd->valid = true; @@ -959,16 +2479,18 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng } 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()); + + MutexLock lock(sd->mutex); 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_ +real_t TextServerFallback::shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t /*JustificationFlag*/ p_jst_flags) { ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped); } @@ -1028,13 +2550,13 @@ float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width, } if ((space_count > 0) && ((p_jst_flags & JUSTIFICATION_WORD_BOUND) == JUSTIFICATION_WORD_BOUND)) { - float delta_width_per_space = (p_width - sd->width) / space_count; + real_t 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)); + real_t old_adv = gl.advance; + gl.advance = MAX(gl.advance + delta_width_per_space, Math::round(0.1 * gl.font_size)); sd->width += (gl.advance - old_adv); } } @@ -1044,10 +2566,11 @@ float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width, return sd->width; } -float TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) { - _THREAD_SAFE_METHOD_ +real_t TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<real_t> &p_tab_stops) { ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped); } @@ -1056,7 +2579,7 @@ float TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<float } int tab_index = 0; - float off = 0.f; + real_t off = 0.f; int start, end, delta; if (sd->para_direction == DIRECTION_LTR) { @@ -1073,7 +2596,7 @@ float TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<float for (int i = start; i != end; i += delta) { if ((gl[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) { - float tab_off = 0.f; + real_t tab_off = 0.f; while (tab_off <= off) { tab_off += p_tab_stops[tab_index]; tab_index++; @@ -1081,7 +2604,7 @@ float TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<float tab_index = 0; } } - float old_adv = gl[i].advance; + real_t old_adv = gl[i].advance; gl[i].advance = tab_off - off; sd->width += gl[i].advance - old_adv; off = 0; @@ -1094,9 +2617,10 @@ float TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<float } 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); + + MutexLock lock(sd->mutex); if (!sd->valid) { shaped_text_shape(p_shaped); } @@ -1134,9 +2658,10 @@ bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) { } 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); + + MutexLock lock(sd->mutex); if (!sd->valid) { shaped_text_shape(p_shaped); } @@ -1148,60 +2673,63 @@ bool TextServerFallback::shaped_text_update_justification_ops(RID p_shaped) { return true; } -void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, float p_width, uint8_t p_clip_flags) { - _THREAD_SAFE_METHOD_ +void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, real_t p_width, uint8_t p_trim_flags) { ShapedTextData *sd = shaped_owner.getornull(p_shaped_line); - ERR_FAIL_COND_MSG(!sd, "ShapedTextDataAdvanced invalid."); + ERR_FAIL_COND_MSG(!sd, "ShapedTextDataFallback invalid."); + + MutexLock lock(sd->mutex); if (!sd->valid) { shaped_text_shape(p_shaped_line); } - bool add_ellipsis = (p_clip_flags & OVERRUN_ADD_ELLIPSIS) == OVERRUN_ADD_ELLIPSIS; - bool cut_per_word = (p_clip_flags & OVERRUN_TRIM_WORD_ONLY) == OVERRUN_TRIM_WORD_ONLY; - bool enforce_ellipsis = (p_clip_flags & OVERRUN_ENFORCE_ELLIPSIS) == OVERRUN_ENFORCE_ELLIPSIS; + sd->overrun_trim_data.ellipsis_glyph_buf.clear(); + + bool add_ellipsis = (p_trim_flags & OVERRUN_ADD_ELLIPSIS) == OVERRUN_ADD_ELLIPSIS; + bool cut_per_word = (p_trim_flags & OVERRUN_TRIM_WORD_ONLY) == OVERRUN_TRIM_WORD_ONLY; + bool enforce_ellipsis = (p_trim_flags & OVERRUN_ENFORCE_ELLIPSIS) == OVERRUN_ENFORCE_ELLIPSIS; + bool justification_aware = (p_trim_flags & OVERRUN_JUSTIFICATION_AWARE) == OVERRUN_JUSTIFICATION_AWARE; Glyph *sd_glyphs = sd->glyphs.ptrw(); - if ((p_clip_flags & OVERRUN_TRIM) == OVERRUN_NO_TRIMMING || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) { + if ((p_trim_flags & OVERRUN_TRIM) == OVERRUN_NO_TRIMMING || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) { + sd->overrun_trim_data.trim_pos = -1; + sd->overrun_trim_data.ellipsis_pos = -1; + return; + } + + if (justification_aware && !sd->fit_width_minimum_reached) { return; } int sd_size = sd->glyphs.size(); RID last_gl_font_rid = sd_glyphs[sd_size - 1].font_rid; int last_gl_font_size = sd_glyphs[sd_size - 1].font_size; - uint32_t dot_gl_idx = font_get_glyph_index(last_gl_font_rid, '.'); - Vector2 dot_adv = font_get_glyph_advance(last_gl_font_rid, dot_gl_idx, last_gl_font_size); - uint32_t whitespace_gl_idx = font_get_glyph_index(last_gl_font_rid, ' '); - Vector2 whitespace_adv = font_get_glyph_advance(last_gl_font_rid, whitespace_gl_idx, last_gl_font_size); + int32_t dot_gl_idx = font_get_glyph_index(last_gl_font_rid, '.', 0); + Vector2 dot_adv = font_get_glyph_advance(last_gl_font_rid, last_gl_font_size, dot_gl_idx); + int32_t whitespace_gl_idx = font_get_glyph_index(last_gl_font_rid, ' ', 0); + Vector2 whitespace_adv = font_get_glyph_advance(last_gl_font_rid, last_gl_font_size, whitespace_gl_idx); - int ellipsis_advance = 0; + int ellipsis_width = 0; if (add_ellipsis) { - ellipsis_advance = 3 * dot_adv.x + font_get_spacing_glyph(last_gl_font_rid) + (cut_per_word ? whitespace_adv.x : 0); + ellipsis_width = 3 * dot_adv.x + font_get_spacing(last_gl_font_rid, last_gl_font_size, TextServer::SPACING_GLYPH) + (cut_per_word ? whitespace_adv.x : 0); } int ell_min_characters = 6; - float width = sd->width; - - bool is_rtl = sd->direction == DIRECTION_RTL || (sd->direction == DIRECTION_AUTO && sd->para_direction == DIRECTION_RTL); + real_t width = sd->width; - int trim_pos = (is_rtl) ? sd_size : 0; + int trim_pos = 0; int ellipsis_pos = (enforce_ellipsis) ? 0 : -1; int last_valid_cut = 0; bool found = false; - int glyphs_from = (is_rtl) ? 0 : sd_size - 1; - int glyphs_to = (is_rtl) ? sd_size - 1 : -1; - int glyphs_delta = (is_rtl) ? +1 : -1; + for (int i = sd_size - 1; i != -1; i--) { + width -= sd_glyphs[i].advance * sd_glyphs[i].repeat; - for (int i = glyphs_from; i != glyphs_to; i += glyphs_delta) { - if (!is_rtl) { - width -= sd_glyphs[i].advance; - } if (sd_glyphs[i].count > 0) { - bool above_min_char_treshold = ((is_rtl) ? sd_size - 1 - i : i) >= ell_min_characters; + bool above_min_char_treshold = (i >= ell_min_characters); - if (width + (((above_min_char_treshold && add_ellipsis) || enforce_ellipsis) ? ellipsis_advance : 0) <= p_width) { + if (width + (((above_min_char_treshold && add_ellipsis) || enforce_ellipsis) ? ellipsis_width : 0) <= p_width) { if (cut_per_word && above_min_char_treshold) { if ((sd_glyphs[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) { last_valid_cut = i; @@ -1214,99 +2742,66 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl if (found) { trim_pos = last_valid_cut; - if (above_min_char_treshold && width - ellipsis_advance <= p_width) { + if (add_ellipsis && (above_min_char_treshold || enforce_ellipsis) && width - ellipsis_width <= p_width) { ellipsis_pos = trim_pos; } break; } } } - if (is_rtl) { - width -= sd_glyphs[i].advance; - } + } + + sd->overrun_trim_data.trim_pos = trim_pos; + sd->overrun_trim_data.ellipsis_pos = ellipsis_pos; + if (trim_pos == 0 && enforce_ellipsis && add_ellipsis) { + sd->overrun_trim_data.ellipsis_pos = 0; } if ((trim_pos >= 0 && sd->width > p_width) || enforce_ellipsis) { - int added_glyphs = 0; if (add_ellipsis && (ellipsis_pos > 0 || enforce_ellipsis)) { // Insert an additional space when cutting word bound for aesthetics. if (cut_per_word && (ellipsis_pos > 0)) { TextServer::Glyph gl; - gl.start = sd_glyphs[ellipsis_pos].start; - gl.end = sd_glyphs[ellipsis_pos].end; gl.count = 1; gl.advance = whitespace_adv.x; gl.index = whitespace_gl_idx; gl.font_rid = last_gl_font_rid; gl.font_size = last_gl_font_size; - gl.flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT | GRAPHEME_IS_VIRTUAL | (is_rtl ? GRAPHEME_IS_RTL : 0); + gl.flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT | GRAPHEME_IS_VIRTUAL; - // Optimized glyph insertion by replacing a glyph whenever possible. - int glyph_idx = trim_pos + ((is_rtl) ? -added_glyphs : added_glyphs); - if (is_rtl) { - if (glyph_idx < 0) { - sd->glyphs.insert(0, gl); - } else { - sd->glyphs.set(glyph_idx, gl); - } - } else { - if (glyph_idx > (sd_size - 1)) { - sd->glyphs.append(gl); - } else { - sd->glyphs.set(glyph_idx, gl); - } - } - added_glyphs++; + sd->overrun_trim_data.ellipsis_glyph_buf.append(gl); } // Add ellipsis dots. - for (int d = 0; d < 3; d++) { - TextServer::Glyph gl; - gl.start = sd_glyphs[ellipsis_pos].start; - gl.end = sd_glyphs[ellipsis_pos].end; - gl.count = 1; - gl.advance = dot_adv.x; - gl.index = dot_gl_idx; - gl.font_rid = last_gl_font_rid; - gl.font_size = last_gl_font_size; - gl.flags = GRAPHEME_IS_PUNCTUATION | GRAPHEME_IS_VIRTUAL | (is_rtl ? GRAPHEME_IS_RTL : 0); - - // Optimized glyph insertion by replacing a glyph whenever possible. - int glyph_idx = trim_pos + ((is_rtl) ? -added_glyphs : added_glyphs); - if (is_rtl) { - if (glyph_idx < 0) { - sd->glyphs.insert(0, gl); - } else { - sd->glyphs.set(glyph_idx, gl); - } - } else { - if (glyph_idx > (sd_size - 1)) { - sd->glyphs.append(gl); - } else { - sd->glyphs.set(glyph_idx, gl); - } - } - added_glyphs++; - } - } - - // Cut the remaining glyphs off. - if (!is_rtl) { - sd->glyphs.resize(trim_pos + added_glyphs); - } else { - for (int ridx = 0; ridx <= trim_pos - added_glyphs; ridx++) { - sd->glyphs.remove(0); - } + TextServer::Glyph gl; + gl.count = 1; + gl.repeat = 3; + gl.advance = dot_adv.x; + gl.index = dot_gl_idx; + gl.font_rid = last_gl_font_rid; + gl.font_size = last_gl_font_size; + gl.flags = GRAPHEME_IS_PUNCTUATION | GRAPHEME_IS_VIRTUAL; + + sd->overrun_trim_data.ellipsis_glyph_buf.append(gl); } - // Update to correct width. - sd->width = width + ((ellipsis_pos != -1) ? ellipsis_advance : 0); + sd->text_trimmed = true; + sd->width_trimmed = width + ((ellipsis_pos != -1) ? ellipsis_width : 0); } } +TextServer::TrimData TextServerFallback::shaped_text_get_trim_data(RID p_shaped) const { + ShapedTextData *sd = shaped_owner.getornull(p_shaped); + ERR_FAIL_COND_V_MSG(!sd, TrimData(), "ShapedTextDataFallback invalid."); + + MutexLock lock(sd->mutex); + return sd->overrun_trim_data; +} + bool TextServerFallback::shaped_text_shape(RID p_shaped) { - _THREAD_SAFE_METHOD_ ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); + + MutexLock lock(sd->mutex); if (sd->valid) { return true; } @@ -1336,33 +2831,9 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) { 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; @@ -1379,14 +2850,12 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) { } 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. + gl.index = (int32_t)sd->text[j]; // Use codepoint. if (gl.index == 0x0009 || gl.index == 0x000b) { gl.index = 0x0020; } @@ -1395,45 +2864,44 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) { } // 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)) { + if (font_has_char(span.fonts[k], gl.index)) { gl.font_rid = span.fonts[k]; break; } } - if (gl.font_rid != RID()) { + if (gl.font_rid.is_valid()) { 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.advance = font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).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)); + sd->ascent = MAX(sd->ascent, font_get_ascent(gl.font_rid, gl.font_size)); + sd->descent = MAX(sd->descent, font_get_descent(gl.font_rid, 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)); + gl.advance = font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).y; + gl.x_off = -Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5); + gl.y_off = font_get_ascent(gl.font_rid, gl.font_size); + sd->ascent = MAX(sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); + sd->descent = MAX(sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); } } - if (fd->get_spacing_space() && is_whitespace(sd->text[j])) { - gl.advance += fd->get_spacing_space(); + if (font_get_spacing(gl.font_rid, gl.font_size, TextServer::SPACING_SPACE) && is_whitespace(sd->text[j])) { + gl.advance += font_get_spacing(gl.font_rid, gl.font_size, TextServer::SPACING_SPACE); } else { - gl.advance += fd->get_spacing_glyph(); + gl.advance += font_get_spacing(gl.font_rid, gl.font_size, TextServer::SPACING_GLYPH); } - sd->upos = MAX(sd->upos, fd->get_underline_position(gl.font_size)); - sd->uthk = MAX(sd->uthk, fd->get_underline_thickness(gl.font_size)); + 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)); // 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; + prev_gl.advance += font_get_kerning(gl.font_rid, gl.font_size, Vector2i(prev_gl.index, gl.index)).x; } else { - prev_gl.advance += fd->get_kerning(prev_gl.index, gl.index, gl.font_size).y; + prev_gl.advance += font_get_kerning(gl.font_rid, gl.font_size, Vector2i(prev_gl.index, gl.index)).y; } } } @@ -1456,49 +2924,86 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) { } // Align embedded objects to baseline. + real_t full_ascent = sd->ascent; + real_t full_descent = sd->descent; 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: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.y = -sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.y = -(E->get().rect.size.y / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.y = (-sd->ascent + sd->descent) / 2; + } break; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.y = 0; } break; - case VALIGN_BOTTOM: { - E->get().rect.position.y = sd->descent - E->get().rect.size.y; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.y = sd->descent; } break; } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.y -= E->get().rect.size.y; + } break; + case INLINE_ALIGN_CENTER_TO: { + E->get().rect.position.y -= E->get().rect.size.y / 2; + } break; + case INLINE_ALIGN_TOP_TO: { + //NOP + } break; + } + full_ascent = MAX(full_ascent, -E->get().rect.position.y); + full_descent = MAX(full_descent, E->get().rect.position.y + E->get().rect.size.y); } else { - switch (E->get().inline_align) { - case VALIGN_TOP: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.x = -sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.x = -(E->get().rect.size.x / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.x = (-sd->ascent + sd->descent) / 2; + } break; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.x = 0; } break; - case VALIGN_BOTTOM: { - E->get().rect.position.x = sd->descent - E->get().rect.size.x; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.x = sd->descent; } break; } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.x -= E->get().rect.size.x; + } break; + case INLINE_ALIGN_CENTER_TO: { + E->get().rect.position.x -= E->get().rect.size.x / 2; + } break; + case INLINE_ALIGN_TOP_TO: { + //NOP + } break; + } + full_ascent = MAX(full_ascent, -E->get().rect.position.x); + full_descent = MAX(full_descent, E->get().rect.position.x + E->get().rect.size.x); } } - + sd->ascent = full_ascent; + sd->descent = full_descent; 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); + + MutexLock lock(sd->mutex); 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>()); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped); } @@ -1506,16 +3011,18 @@ Vector<TextServer::Glyph> TextServerFallback::shaped_text_get_glyphs(RID p_shape } 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()); + + MutexLock lock(sd->mutex); 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>()); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped); } @@ -1524,10 +3031,11 @@ Vector<TextServer::Glyph> TextServerFallback::shaped_text_sort_logical(RID p_sha } 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); + + MutexLock lock(sd->mutex); for (const Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) { ret.push_back(E->key()); } @@ -1536,9 +3044,10 @@ Array TextServerFallback::shaped_text_get_objects(RID p_shaped) const { } 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()); + + MutexLock lock(sd->mutex); ERR_FAIL_COND_V(!sd->objects.has(p_key), Rect2()); if (!sd->valid) { const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped); @@ -1547,9 +3056,10 @@ Rect2 TextServerFallback::shaped_text_get_object_rect(RID p_shaped, Variant p_ke } 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()); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped); } @@ -1560,40 +3070,44 @@ Size2 TextServerFallback::shaped_text_get_size(RID p_shaped) const { } } -float TextServerFallback::shaped_text_get_ascent(RID p_shaped) const { - _THREAD_SAFE_METHOD_ +real_t TextServerFallback::shaped_text_get_ascent(RID p_shaped) const { const ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); 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_ +real_t TextServerFallback::shaped_text_get_descent(RID p_shaped) const { const ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); 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_ +real_t TextServerFallback::shaped_text_get_width(RID p_shaped) const { const ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); 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_ +real_t TextServerFallback::shaped_text_get_underline_position(RID p_shaped) const { const ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped); } @@ -1601,10 +3115,11 @@ float TextServerFallback::shaped_text_get_underline_position(RID p_shaped) const return sd->upos; } -float TextServerFallback::shaped_text_get_underline_thickness(RID p_shaped) const { - _THREAD_SAFE_METHOD_ +real_t TextServerFallback::shaped_text_get_underline_thickness(RID p_shaped) const { const ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped); } @@ -1620,3 +3135,11 @@ TextServer *TextServerFallback::create_func(Error &r_error, void *p_user_data) { void TextServerFallback::register_server() { TextServerManager::register_create_function(interface_name, interface_features, create_func, nullptr); } + +TextServerFallback::TextServerFallback(){}; + +TextServerFallback::~TextServerFallback() { + if (library != nullptr) { + FT_Done_FreeType(library); + } +}; diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index b70c8f4ec0..fde75e7135 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -39,22 +39,165 @@ #include "servers/text_server.h" #include "core/templates/rid_owner.h" - +#include "core/templates/thread_work_pool.h" #include "scene/resources/texture.h" -#include "font_fb.h" +#include "modules/modules_enabled.gen.h" + +#ifdef MODULE_FREETYPE_ENABLED +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_TRUETYPE_TABLES_H +#include FT_STROKER_H +#include FT_ADVANCES_H +#include FT_MULTIPLE_MASTERS_H +#include FT_BBOX_H +#endif 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; + // Font cache data. + +#ifdef MODULE_FREETYPE_ENABLED + mutable FT_Library library = nullptr; +#endif + + const int rect_range = 2; + + struct FontTexture { + Image::Format format; + PackedByteArray imgdata; + int texture_w = 0; + int texture_h = 0; + PackedInt32Array offsets; + Ref<ImageTexture> texture; + }; + + struct FontTexturePosition { + int index = 0; + int x = 0; + int y = 0; + }; + + struct FontGlyph { + bool found = false; + int texture_idx = -1; + Rect2 rect; + Rect2 uv_rect; + Vector2 advance; + }; + + struct FontDataForSizeFallback { + real_t ascent = 0.f; + real_t descent = 0.f; + real_t underline_position = 0.f; + real_t underline_thickness = 0.f; + real_t scale = 1.f; + real_t oversampling = 1.f; + + int spacing_glyph = 0; + int spacing_space = 0; + + Vector2i size; + + Vector<FontTexture> textures; + HashMap<int32_t, FontGlyph> glyph_map; + Map<Vector2i, Vector2> kerning_map; + +#ifdef MODULE_FREETYPE_ENABLED + FT_Face face = nullptr; + FT_StreamRec stream; +#endif + + ~FontDataForSizeFallback() { +#ifdef MODULE_FREETYPE_ENABLED + if (face != nullptr) { + FT_Done_Face(face); + } +#endif + } + }; + + struct FontDataFallback { + Mutex mutex; + + bool antialiased = true; + bool msdf = false; + int msdf_range = 14; + int msdf_source_size = 48; + int fixed_size = 0; + bool force_autohinter = false; + TextServer::Hinting hinting = TextServer::HINTING_LIGHT; + Dictionary variation_coordinates; + real_t oversampling = 0.f; + + Map<Vector2i, FontDataForSizeFallback *> cache; + + bool face_init = false; + Dictionary supported_varaitions; + + // Language/script support override. + Map<String, bool> language_support_overrides; + Map<String, bool> script_support_overrides; + + PackedByteArray data; + const uint8_t *data_ptr; + size_t data_size; + + mutable ThreadWorkPool work_pool; + + ~FontDataFallback() { + work_pool.finish(); + for (const Map<Vector2i, FontDataForSizeFallback *>::Element *E = cache.front(); E; E = E->next()) { + memdelete(E->get()); + } + cache.clear(); + } + }; + + _FORCE_INLINE_ FontTexturePosition find_texture_pos_for_glyph(FontDataForSizeFallback *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) const; +#ifdef MODULE_MSDFGEN_ENABLED + _FORCE_INLINE_ FontGlyph rasterize_msdf(FontDataFallback *p_font_data, FontDataForSizeFallback *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const; +#endif +#ifdef MODULE_FREETYPE_ENABLED + _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontDataForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const; +#endif + _FORCE_INLINE_ bool _ensure_glyph(FontDataFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const; + _FORCE_INLINE_ bool _ensure_cache_for_size(FontDataFallback *p_font_data, const Vector2i &p_size) const; + _FORCE_INLINE_ void _font_clear_cache(FontDataFallback *p_font_data); + void _generateMTSDF_threaded(uint32_t y, void *p_td) const; + + _FORCE_INLINE_ Vector2i _get_size(const FontDataFallback *p_font_data, int p_size) const { + if (p_font_data->msdf) { + return Vector2i(p_font_data->msdf_source_size, 0); + } else if (p_font_data->fixed_size > 0) { + return Vector2i(p_font_data->fixed_size, 0); + } else { + return Vector2i(p_size, 0); + } + } + + _FORCE_INLINE_ Vector2i _get_size_outline(const FontDataFallback *p_font_data, const Vector2i &p_size) const { + if (p_font_data->msdf) { + return Vector2i(p_font_data->msdf_source_size, 0); + } else if (p_font_data->fixed_size > 0) { + return Vector2i(p_font_data->fixed_size, MIN(p_size.y, 1)); + } else { + return p_size; + } + } + + // Common data. + + real_t oversampling = 1.f; + mutable RID_PtrOwner<FontDataFallback> font_owner; + mutable RID_PtrOwner<ShapedTextData> shaped_owner; + protected: static void _bind_methods(){}; @@ -77,72 +220,130 @@ public: virtual bool is_locale_right_to_left(const String &p_locale) override; + virtual int32_t name_to_tag(const String &p_name) const override; + virtual String tag_to_name(int32_t p_tag) const 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 RID create_font_bitmap(float p_height, float p_ascent, int p_base_size = 16) override; + virtual RID create_font() override; + + virtual void font_set_data(RID p_font_rid, const PackedByteArray &p_data) override; + virtual void font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) override; + + virtual void font_set_antialiased(RID p_font_rid, bool p_antialiased) override; + virtual bool font_is_antialiased(RID p_font_rid) const override; + + virtual void font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) override; + virtual bool font_is_multichannel_signed_distance_field(RID p_font_rid) const override; + + virtual void font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) override; + virtual int font_get_msdf_pixel_range(RID p_font_rid) const override; + + virtual void font_set_msdf_size(RID p_font_rid, int p_msdf_size) override; + virtual int font_get_msdf_size(RID p_font_rid) const override; + + virtual void font_set_fixed_size(RID p_font_rid, int p_fixed_size) override; + virtual int font_get_fixed_size(RID p_font_rid) const override; + + virtual void font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) override; + virtual bool font_is_force_autohinter(RID p_font_rid) const override; + + virtual void font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) override; + virtual TextServer::Hinting font_get_hinting(RID p_font_rid) const override; + + virtual void font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) override; + virtual Dictionary font_get_variation_coordinates(RID p_font_rid) const override; + + virtual void font_set_oversampling(RID p_font_rid, real_t p_oversampling) override; + virtual real_t font_get_oversampling(RID p_font_rid) const override; + + virtual Array font_get_size_cache_list(RID p_font_rid) const override; + virtual void font_clear_size_cache(RID p_font_rid) override; + virtual void font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) override; + + virtual void font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) override; + virtual real_t font_get_ascent(RID p_font_rid, int p_size) const override; + + virtual void font_set_descent(RID p_font_rid, int p_size, real_t p_descent) override; + virtual real_t font_get_descent(RID p_font_rid, int p_size) const override; + + virtual void font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) override; + virtual real_t font_get_underline_position(RID p_font_rid, int p_size) const override; + + virtual void font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) override; + virtual real_t font_get_underline_thickness(RID p_font_rid, int p_size) const override; + + virtual void font_set_scale(RID p_font_rid, int p_size, real_t p_scale) override; + virtual real_t font_get_scale(RID p_font_rid, int p_size) const override; + + virtual void font_set_spacing(RID p_font_rid, int p_size, SpacingType p_spacing, int p_value) override; + virtual int font_get_spacing(RID p_font_rid, int p_size, SpacingType p_spacing) const override; + + virtual int font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const override; + virtual void font_clear_textures(RID p_font_rid, const Vector2i &p_size) override; + virtual void font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) override; + + virtual void font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) override; + virtual Ref<Image> font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const override; - virtual void font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) override; - virtual void font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override; - virtual void font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) override; + virtual void font_set_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) override; + virtual PackedInt32Array font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const 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 Array font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const override; + virtual void font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) override; + virtual void font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) 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 Vector2 font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) override; - virtual int font_get_spacing_space(RID p_font) const override; - virtual void font_set_spacing_space(RID p_font, int p_value) override; + virtual Vector2 font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) override; - virtual int font_get_spacing_glyph(RID p_font) const override; - virtual void font_set_spacing_glyph(RID p_font, int p_value) override; + virtual Vector2 font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) override; - virtual void font_set_antialiased(RID p_font, bool p_antialiased) override; - virtual bool font_get_antialiased(RID p_font) const override; + virtual Rect2 font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) override; - virtual void font_set_hinting(RID p_font, Hinting p_hinting) override; - virtual Hinting font_get_hinting(RID p_font) const override; + virtual int font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) 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_get_glyph_contours(RID p_font, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) 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 Array font_get_kerning_list(RID p_font_rid, int p_size) const override; + virtual void font_clear_kerning_map(RID p_font_rid, int p_size) override; + virtual void font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) 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_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) override; + virtual Vector2 font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const override; - virtual bool font_has_outline(RID p_font) const override; - virtual float font_get_base_size(RID p_font) const override; + virtual int32_t font_get_glyph_index(RID p_font_rid, int p_size, char32_t p_char, char32_t p_variation_selector = 0) 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_has_char(RID p_font_rid, char32_t p_char) const override; + virtual String font_get_supported_chars(RID p_font_rid) const 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 void font_render_range(RID p_font, const Vector2i &p_size, char32_t p_start, char32_t p_end) override; + virtual void font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) 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 void font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) const override; + virtual void font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) 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 bool font_is_language_supported(RID p_font_rid, const String &p_language) const override; + virtual void font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) override; + virtual bool font_get_language_support_override(RID p_font_rid, const String &p_language) override; + virtual void font_remove_language_support_override(RID p_font_rid, const String &p_language) override; + virtual Vector<String> font_get_language_support_overrides(RID p_font_rid) override; - virtual bool font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override; + virtual bool font_is_script_supported(RID p_font_rid, const String &p_script) const override; + virtual void font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) override; + virtual bool font_get_script_support_override(RID p_font_rid, const String &p_script) override; + virtual void font_remove_script_support_override(RID p_font_rid, const String &p_script) override; + virtual Vector<String> font_get_script_support_overrides(RID p_font_rid) override; - virtual float font_get_oversampling() const override; - virtual void font_set_oversampling(float p_oversampling) override; + virtual Dictionary font_supported_feature_list(RID p_font_rid) const override; + virtual Dictionary font_supported_variation_list(RID p_font_rid) const override; - virtual Vector<String> get_system_fonts() const override; + virtual real_t font_get_global_oversampling() const override; + virtual void font_set_global_oversampling(real_t p_oversampling) override; /* Shaped text buffer interface */ @@ -165,20 +366,21 @@ public: 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 bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER, int p_length = 1) override; + virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_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 real_t shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override; + virtual real_t shaped_text_tab_align(RID p_shaped, const Vector<real_t> &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 void shaped_text_overrun_trim_to_width(RID p_shaped, float p_width, uint8_t p_clip_flags) override; + virtual void shaped_text_overrun_trim_to_width(RID p_shaped, real_t p_width, uint8_t p_trim_flags) override; + virtual TrimData shaped_text_get_trim_data(RID p_shaped) const override; virtual bool shaped_text_is_ready(RID p_shaped) const override; @@ -192,17 +394,17 @@ public: 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 real_t shaped_text_get_ascent(RID p_shaped) const override; + virtual real_t shaped_text_get_descent(RID p_shaped) const override; + virtual real_t shaped_text_get_width(RID p_shaped) const override; + virtual real_t shaped_text_get_underline_position(RID p_shaped) const override; + virtual real_t 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(){}; + TextServerFallback(); + ~TextServerFallback(); }; #endif // TEXT_SERVER_FALLBACK_H diff --git a/modules/vhacd/register_types.cpp b/modules/vhacd/register_types.cpp index 3d7aaee921..2b48e94604 100644 --- a/modules/vhacd/register_types.cpp +++ b/modules/vhacd/register_types.cpp @@ -33,7 +33,7 @@ #include "thirdparty/vhacd/public/VHACD.h" static Vector<Vector<Face3>> convex_decompose(const Vector<Face3> &p_faces, int p_max_convex_hulls = -1) { - Vector<float> vertices; + Vector<real_t> vertices; vertices.resize(p_faces.size() * 9); Vector<uint32_t> indices; indices.resize(p_faces.size() * 3); diff --git a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml index 8aa34f8cae..b574576856 100644 --- a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml @@ -9,118 +9,118 @@ <tutorials> </tutorials> <methods> - <method name="_get_caption" qualifiers="virtual"> + <method name="_get_caption" qualifiers="virtual const"> <return type="String" /> <description> Return the node's title. </description> </method> - <method name="_get_category" qualifiers="virtual"> + <method name="_get_category" qualifiers="virtual const"> <return type="String" /> <description> Return the node's category. </description> </method> - <method name="_get_input_value_port_count" qualifiers="virtual"> + <method name="_get_input_value_port_count" qualifiers="virtual const"> <return type="int" /> <description> Return the count of input value ports. </description> </method> - <method name="_get_input_value_port_hint" qualifiers="virtual"> + <method name="_get_input_value_port_hint" qualifiers="virtual const"> <return type="int" /> - <argument index="0" name="idx" type="int" /> + <argument index="0" name="input_idx" type="int" /> <description> Return the specified input port's hint. See the [enum @GlobalScope.PropertyHint] hints. </description> </method> - <method name="_get_input_value_port_hint_string" qualifiers="virtual"> + <method name="_get_input_value_port_hint_string" qualifiers="virtual const"> <return type="String" /> - <argument index="0" name="idx" type="int" /> + <argument index="0" name="input_idx" type="int" /> <description> Return the specified input port's hint string. </description> </method> - <method name="_get_input_value_port_name" qualifiers="virtual"> + <method name="_get_input_value_port_name" qualifiers="virtual const"> <return type="String" /> - <argument index="0" name="idx" type="int" /> + <argument index="0" name="input_idx" type="int" /> <description> Return the specified input port's name. </description> </method> - <method name="_get_input_value_port_type" qualifiers="virtual"> + <method name="_get_input_value_port_type" qualifiers="virtual const"> <return type="int" /> - <argument index="0" name="idx" type="int" /> + <argument index="0" name="input_idx" type="int" /> <description> Return the specified input port's type. See the [enum Variant.Type] values. </description> </method> - <method name="_get_output_sequence_port_count" qualifiers="virtual"> + <method name="_get_output_sequence_port_count" qualifiers="virtual const"> <return type="int" /> <description> Return the amount of output [b]sequence[/b] ports. </description> </method> - <method name="_get_output_sequence_port_text" qualifiers="virtual"> + <method name="_get_output_sequence_port_text" qualifiers="virtual const"> <return type="String" /> - <argument index="0" name="idx" type="int" /> + <argument index="0" name="seq_idx" type="int" /> <description> Return the specified [b]sequence[/b] output's name. </description> </method> - <method name="_get_output_value_port_count" qualifiers="virtual"> + <method name="_get_output_value_port_count" qualifiers="virtual const"> <return type="int" /> <description> Return the amount of output value ports. </description> </method> - <method name="_get_output_value_port_hint" qualifiers="virtual"> + <method name="_get_output_value_port_hint" qualifiers="virtual const"> <return type="int" /> - <argument index="0" name="idx" type="int" /> + <argument index="0" name="output_idx" type="int" /> <description> Return the specified output port's hint. See the [enum @GlobalScope.PropertyHint] hints. </description> </method> - <method name="_get_output_value_port_hint_string" qualifiers="virtual"> + <method name="_get_output_value_port_hint_string" qualifiers="virtual const"> <return type="String" /> - <argument index="0" name="idx" type="int" /> + <argument index="0" name="output_idx" type="int" /> <description> Return the specified output port's hint string. </description> </method> - <method name="_get_output_value_port_name" qualifiers="virtual"> + <method name="_get_output_value_port_name" qualifiers="virtual const"> <return type="String" /> - <argument index="0" name="idx" type="int" /> + <argument index="0" name="output_idx" type="int" /> <description> Return the specified output port's name. </description> </method> - <method name="_get_output_value_port_type" qualifiers="virtual"> + <method name="_get_output_value_port_type" qualifiers="virtual const"> <return type="int" /> - <argument index="0" name="idx" type="int" /> + <argument index="0" name="output_idx" type="int" /> <description> Return the specified output port's type. See the [enum Variant.Type] values. </description> </method> - <method name="_get_text" qualifiers="virtual"> + <method name="_get_text" qualifiers="virtual const"> <return type="String" /> <description> Return the custom node's text, which is shown right next to the input [b]sequence[/b] port (if there is none, on the place that is usually taken by it). </description> </method> - <method name="_get_working_memory_size" qualifiers="virtual"> + <method name="_get_working_memory_size" qualifiers="virtual const"> <return type="int" /> <description> Return the size of the custom node's working memory. See [method _step] for more details. </description> </method> - <method name="_has_input_sequence_port" qualifiers="virtual"> + <method name="_has_input_sequence_port" qualifiers="virtual const"> <return type="bool" /> <description> Return whether the custom node has an input [b]sequence[/b] port. </description> </method> - <method name="_step" qualifiers="virtual"> + <method name="_step" qualifiers="virtual const"> <return type="Variant" /> <argument index="0" name="inputs" type="Array" /> <argument index="1" name="outputs" type="Array" /> diff --git a/modules/visual_script/doc_classes/VisualScriptEditor.xml b/modules/visual_script/doc_classes/VisualScriptEditor.xml deleted file mode 100644 index 9ea889c77b..0000000000 --- a/modules/visual_script/doc_classes/VisualScriptEditor.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptEditor" inherits="Object" version="4.0"> - <brief_description> - </brief_description> - <description> - </description> - <tutorials> - </tutorials> - <methods> - <method name="add_custom_node"> - <return type="void" /> - <argument index="0" name="name" type="String" /> - <argument index="1" name="category" type="String" /> - <argument index="2" name="script" type="Script" /> - <description> - Add a custom Visual Script node to the editor. It'll be placed under "Custom Nodes" with the [code]category[/code] as the parameter. - </description> - </method> - <method name="remove_custom_node"> - <return type="void" /> - <argument index="0" name="name" type="String" /> - <argument index="1" name="category" type="String" /> - <description> - Remove a custom Visual Script node from the editor. Custom nodes already placed on scripts won't be removed. - </description> - </method> - </methods> - <signals> - <signal name="custom_nodes_updated"> - <description> - Emitted when a custom Visual Script node is added or removed. - </description> - </signal> - </signals> - <constants> - </constants> -</class> diff --git a/modules/visual_script/doc_classes/VisualScriptSubCall.xml b/modules/visual_script/doc_classes/VisualScriptSubCall.xml index 374e7d0f35..f54887b09c 100644 --- a/modules/visual_script/doc_classes/VisualScriptSubCall.xml +++ b/modules/visual_script/doc_classes/VisualScriptSubCall.xml @@ -9,13 +9,6 @@ <tutorials> </tutorials> <methods> - <method name="_subcall" qualifiers="virtual"> - <return type="Variant" /> - <argument index="0" name="arguments" type="Variant" /> - <description> - Called by this node. - </description> - </method> </methods> <constants> </constants> diff --git a/modules/visual_script/register_types.cpp b/modules/visual_script/register_types.cpp index f20ef046a3..7fb9707fce 100644 --- a/modules/visual_script/register_types.cpp +++ b/modules/visual_script/register_types.cpp @@ -43,7 +43,7 @@ VisualScriptLanguage *visual_script_language = nullptr; #ifdef TOOLS_ENABLED -static _VisualScriptEditor *vs_editor_singleton = nullptr; +static VisualScriptCustomNodes *vs_custom_nodes_singleton = nullptr; #endif void register_visual_script_types() { @@ -114,10 +114,10 @@ void register_visual_script_types() { #ifdef TOOLS_ENABLED ClassDB::set_current_api(ClassDB::API_EDITOR); - GDREGISTER_CLASS(_VisualScriptEditor); + GDREGISTER_CLASS(VisualScriptCustomNodes); ClassDB::set_current_api(ClassDB::API_CORE); - vs_editor_singleton = memnew(_VisualScriptEditor); - Engine::get_singleton()->add_singleton(Engine::Singleton("VisualScriptEditor", _VisualScriptEditor::get_singleton())); + vs_custom_nodes_singleton = memnew(VisualScriptCustomNodes); + Engine::get_singleton()->add_singleton(Engine::Singleton("VisualScriptEditor", VisualScriptCustomNodes::get_singleton())); VisualScriptEditor::register_editor(); #endif @@ -130,8 +130,8 @@ void unregister_visual_script_types() { #ifdef TOOLS_ENABLED VisualScriptEditor::free_clipboard(); - if (vs_editor_singleton) { - memdelete(vs_editor_singleton); + if (vs_custom_nodes_singleton) { + memdelete(vs_custom_nodes_singleton); } #endif if (visual_script_language) { diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index 86793af77f..142419f90a 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -1845,26 +1845,6 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o max_input_args = 0; max_output_args = 0; - if (Object::cast_to<Node>(p_owner)) { - // Turn on these if they exist and base is a node. - Node *node = Object::cast_to<Node>(p_owner); - if (p_script->functions.has("_process")) { - node->set_process(true); - } - if (p_script->functions.has("_physics_process")) { - node->set_physics_process(true); - } - if (p_script->functions.has("_input")) { - node->set_process_input(true); - } - if (p_script->functions.has("_unhandled_input")) { - node->set_process_unhandled_input(true); - } - if (p_script->functions.has("_unhandled_key_input")) { - node->set_process_unhandled_key_input(true); - } - } - // Setup variables. { List<StringName> keys; diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index a802e8022d..eee9e8f32b 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -1816,7 +1816,7 @@ void VisualScriptEditor::_generic_search(String p_base_type, Vector2 pos, bool n } } -void VisualScriptEditor::_input(const Ref<InputEvent> &p_event) { +void VisualScriptEditor::input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); // GUI input for VS Editor Plugin @@ -2323,10 +2323,6 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da pset.instantiate(); pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE); pset->set_base_type(obj->get_class()); - /*if (use_value) { - pset->set_use_builtin_value(true); - pset->set_builtin_value(d["value"]); - }*/ vnode = pset; } else { Ref<VisualScriptPropertyGet> pget; @@ -2903,9 +2899,8 @@ void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot, if (!converted) { undo_redo->add_do_method(script.ptr(), "data_connect", p_from.to_int(), from_port, p_to.to_int(), to_port); undo_redo->add_undo_method(script.ptr(), "data_disconnect", p_from.to_int(), from_port, p_to.to_int(), to_port); - } - // Update nodes in graph - if (!converted) { + + // Update nodes in graph undo_redo->add_do_method(this, "_update_graph", p_from.to_int()); undo_redo->add_do_method(this, "_update_graph", p_to.to_int()); undo_redo->add_undo_method(this, "_update_graph", p_from.to_int()); @@ -4251,8 +4246,6 @@ void VisualScriptEditor::_bind_methods() { ClassDB::bind_method("_can_drop_data_fw", &VisualScriptEditor::can_drop_data_fw); ClassDB::bind_method("_drop_data_fw", &VisualScriptEditor::drop_data_fw); - ClassDB::bind_method("_input", &VisualScriptEditor::_input); - ClassDB::bind_method("_update_graph_connections", &VisualScriptEditor::_update_graph_connections); ClassDB::bind_method("_update_members", &VisualScriptEditor::_update_members); @@ -4526,44 +4519,47 @@ void VisualScriptEditor::register_editor() { EditorNode::add_plugin_init_callback(register_editor_callback); } -Ref<VisualScriptNode> _VisualScriptEditor::create_node_custom(const String &p_name) { +void VisualScriptEditor::validate() { +} + +// VisualScriptCustomNodes + +Ref<VisualScriptNode> VisualScriptCustomNodes::create_node_custom(const String &p_name) { Ref<VisualScriptCustomNode> node; node.instantiate(); node->set_script(singleton->custom_nodes[p_name]); return node; } -_VisualScriptEditor *_VisualScriptEditor::singleton = nullptr; -Map<String, REF> _VisualScriptEditor::custom_nodes; +VisualScriptCustomNodes *VisualScriptCustomNodes::singleton = nullptr; +Map<String, REF> VisualScriptCustomNodes::custom_nodes; -_VisualScriptEditor::_VisualScriptEditor() { +VisualScriptCustomNodes::VisualScriptCustomNodes() { singleton = this; } -_VisualScriptEditor::~_VisualScriptEditor() { +VisualScriptCustomNodes::~VisualScriptCustomNodes() { custom_nodes.clear(); } -void _VisualScriptEditor::add_custom_node(const String &p_name, const String &p_category, const Ref<Script> &p_script) { +void VisualScriptCustomNodes::add_custom_node(const String &p_name, const String &p_category, const Ref<Script> &p_script) { String node_name = "custom/" + p_category + "/" + p_name; custom_nodes.insert(node_name, p_script); - VisualScriptLanguage::singleton->add_register_func(node_name, &_VisualScriptEditor::create_node_custom); + VisualScriptLanguage::singleton->add_register_func(node_name, &VisualScriptCustomNodes::create_node_custom); emit_signal(SNAME("custom_nodes_updated")); } -void _VisualScriptEditor::remove_custom_node(const String &p_name, const String &p_category) { +void VisualScriptCustomNodes::remove_custom_node(const String &p_name, const String &p_category) { String node_name = "custom/" + p_category + "/" + p_name; custom_nodes.erase(node_name); VisualScriptLanguage::singleton->remove_register_func(node_name); emit_signal(SNAME("custom_nodes_updated")); } -void _VisualScriptEditor::_bind_methods() { - ClassDB::bind_method(D_METHOD("add_custom_node", "name", "category", "script"), &_VisualScriptEditor::add_custom_node); - ClassDB::bind_method(D_METHOD("remove_custom_node", "name", "category"), &_VisualScriptEditor::remove_custom_node); +void VisualScriptCustomNodes::_bind_methods() { + ClassDB::bind_method(D_METHOD("add_custom_node", "name", "category", "script"), &VisualScriptCustomNodes::add_custom_node); + ClassDB::bind_method(D_METHOD("remove_custom_node", "name", "category"), &VisualScriptCustomNodes::remove_custom_node); ADD_SIGNAL(MethodInfo("custom_nodes_updated")); } -void VisualScriptEditor::validate() { -} #endif diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index 3b7ed3dba6..7dfb4fa270 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -43,13 +43,14 @@ class VisualScriptEditorVariableEdit; #ifdef TOOLS_ENABLED +// TODO: Maybe this class should be refactored. +// See https://github.com/godotengine/godot/issues/51913 class VisualScriptEditor : public ScriptEditorBase { GDCLASS(VisualScriptEditor, ScriptEditorBase); enum { TYPE_SEQUENCE = 1000, INDEX_BASE_SEQUENCE = 1024 - }; enum { @@ -60,7 +61,7 @@ class VisualScriptEditor : public ScriptEditorBase { EDIT_CUT_NODES, EDIT_PASTE_NODES, EDIT_CREATE_FUNCTION, - REFRESH_GRAPH + REFRESH_GRAPH, }; enum PortAction { @@ -71,7 +72,6 @@ class VisualScriptEditor : public ScriptEditorBase { enum MemberAction { MEMBER_EDIT, MEMBER_REMOVE - }; enum MemberType { @@ -236,7 +236,7 @@ class VisualScriptEditor : public ScriptEditorBase { void _generic_search(String p_base_type = "", Vector2 pos = Vector2(), bool node_centered = false); - void _input(const Ref<InputEvent> &p_event); + virtual void input(const Ref<InputEvent> &p_event) override; void _graph_gui_input(const Ref<InputEvent> &p_event); void _members_gui_input(const Ref<InputEvent> &p_event); void _fn_name_box_input(const Ref<InputEvent> &p_event); @@ -333,27 +333,28 @@ public: }; // Singleton -class _VisualScriptEditor : public Object { - GDCLASS(_VisualScriptEditor, Object); +class VisualScriptCustomNodes : public Object { + GDCLASS(VisualScriptCustomNodes, Object); friend class VisualScriptLanguage; protected: static void _bind_methods(); - static _VisualScriptEditor *singleton; + static VisualScriptCustomNodes *singleton; static Map<String, REF> custom_nodes; static Ref<VisualScriptNode> create_node_custom(const String &p_name); public: - static _VisualScriptEditor *get_singleton() { return singleton; } + static VisualScriptCustomNodes *get_singleton() { return singleton; } void add_custom_node(const String &p_name, const String &p_category, const Ref<Script> &p_script); void remove_custom_node(const String &p_name, const String &p_category); - _VisualScriptEditor(); - ~_VisualScriptEditor(); + VisualScriptCustomNodes(); + ~VisualScriptCustomNodes(); }; + #endif #endif // VISUALSCRIPT_EDITOR_H diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp index c517d89aa5..44fc91d8f5 100644 --- a/modules/visual_script/visual_script_nodes.cpp +++ b/modules/visual_script/visual_script_nodes.cpp @@ -163,7 +163,7 @@ void VisualScriptFunction::_get_property_list(List<PropertyInfo> *p_list) const p_list->push_back(PropertyInfo(Variant::INT, "stack/size", PROPERTY_HINT_RANGE, "1,100000")); } p_list->push_back(PropertyInfo(Variant::BOOL, "stack/stackless")); - p_list->push_back(PropertyInfo(Variant::INT, "rpc/mode", PROPERTY_HINT_ENUM, "Disabled,Remote,Master,Puppet,Remote Sync,Master Sync,Puppet Sync")); + p_list->push_back(PropertyInfo(Variant::INT, "rpc/mode", PROPERTY_HINT_ENUM, "Disabled,Any,Authority")); } int VisualScriptFunction::get_output_sequence_port_count() const { @@ -2825,36 +2825,41 @@ VisualScriptSelf::VisualScriptSelf() { ////////////////////////////////////////// int VisualScriptCustomNode::get_output_sequence_port_count() const { - if (get_script_instance() && get_script_instance()->has_method("_get_output_sequence_port_count")) { - return get_script_instance()->call("_get_output_sequence_port_count"); + int ret; + if (GDVIRTUAL_CALL(_get_output_sequence_port_count, ret)) { + return ret; } return 0; } bool VisualScriptCustomNode::has_input_sequence_port() const { - if (get_script_instance() && get_script_instance()->has_method("_has_input_sequence_port")) { - return get_script_instance()->call("_has_input_sequence_port"); + bool ret; + if (GDVIRTUAL_CALL(_has_input_sequence_port, ret)) { + return ret; } return false; } int VisualScriptCustomNode::get_input_value_port_count() const { - if (get_script_instance() && get_script_instance()->has_method("_get_input_value_port_count")) { - return get_script_instance()->call("_get_input_value_port_count"); + int ret; + if (GDVIRTUAL_CALL(_get_input_value_port_count, ret)) { + return ret; } return 0; } int VisualScriptCustomNode::get_output_value_port_count() const { - if (get_script_instance() && get_script_instance()->has_method("_get_output_value_port_count")) { - return get_script_instance()->call("_get_output_value_port_count"); + int ret; + if (GDVIRTUAL_CALL(_get_output_value_port_count, ret)) { + return ret; } return 0; } String VisualScriptCustomNode::get_output_sequence_port_text(int p_port) const { - if (get_script_instance() && get_script_instance()->has_method("_get_output_sequence_port_text")) { - return get_script_instance()->call("_get_output_sequence_port_text", p_port); + String ret; + if (GDVIRTUAL_CALL(_get_output_sequence_port_text, p_port, ret)) { + return ret; } return String(); @@ -2862,34 +2867,61 @@ String VisualScriptCustomNode::get_output_sequence_port_text(int p_port) const { PropertyInfo VisualScriptCustomNode::get_input_value_port_info(int p_idx) const { PropertyInfo info; - if (get_script_instance() && get_script_instance()->has_method("_get_input_value_port_type")) { - info.type = Variant::Type(int(get_script_instance()->call("_get_input_value_port_type", p_idx))); + { + int type; + if (GDVIRTUAL_CALL(_get_input_value_port_type, p_idx, type)) { + info.type = Variant::Type(type); + } } - if (get_script_instance() && get_script_instance()->has_method("_get_input_value_port_name")) { - info.name = get_script_instance()->call("_get_input_value_port_name", p_idx); + { + String name; + if (GDVIRTUAL_CALL(_get_input_value_port_name, p_idx, name)) { + info.name = name; + } } - if (get_script_instance() && get_script_instance()->has_method("_get_input_value_port_hint")) { - info.hint = PropertyHint(int(get_script_instance()->call("_get_input_value_port_hint", p_idx))); + { + int hint; + if (GDVIRTUAL_CALL(_get_input_value_port_hint, p_idx, hint)) { + info.hint = PropertyHint(hint); + } } - if (get_script_instance() && get_script_instance()->has_method("_get_input_value_port_hint_string")) { - info.hint_string = get_script_instance()->call("_get_input_value_port_hint_string", p_idx); + + { + String hint_string; + if (GDVIRTUAL_CALL(_get_input_value_port_hint_string, p_idx, hint_string)) { + info.hint_string = hint_string; + } } + return info; } PropertyInfo VisualScriptCustomNode::get_output_value_port_info(int p_idx) const { PropertyInfo info; - if (get_script_instance() && get_script_instance()->has_method("_get_output_value_port_type")) { - info.type = Variant::Type(int(get_script_instance()->call("_get_output_value_port_type", p_idx))); + { + int type; + if (GDVIRTUAL_CALL(_get_output_value_port_type, p_idx, type)) { + info.type = Variant::Type(type); + } } - if (get_script_instance() && get_script_instance()->has_method("_get_output_value_port_name")) { - info.name = get_script_instance()->call("_get_output_value_port_name", p_idx); + { + String name; + if (GDVIRTUAL_CALL(_get_output_value_port_name, p_idx, name)) { + info.name = name; + } } - if (get_script_instance() && get_script_instance()->has_method("_get_output_value_port_hint")) { - info.hint = PropertyHint(int(get_script_instance()->call("_get_output_value_port_hint", p_idx))); + { + int hint; + if (GDVIRTUAL_CALL(_get_output_value_port_hint, p_idx, hint)) { + info.hint = PropertyHint(hint); + } } - if (get_script_instance() && get_script_instance()->has_method("_get_output_value_port_hint_string")) { - info.hint_string = get_script_instance()->call("_get_output_value_port_hint_string", p_idx); + + { + String hint_string; + if (GDVIRTUAL_CALL(_get_output_value_port_hint_string, p_idx, hint_string)) { + info.hint_string = hint_string; + } } return info; } @@ -2911,22 +2943,25 @@ VisualScriptCustomNode::TypeGuess VisualScriptCustomNode::guess_output_type(Type } String VisualScriptCustomNode::get_caption() const { - if (get_script_instance() && get_script_instance()->has_method("_get_caption")) { - return get_script_instance()->call("_get_caption"); + String ret; + if (GDVIRTUAL_CALL(_get_caption, ret)) { + return ret; } return "CustomNode"; } String VisualScriptCustomNode::get_text() const { - if (get_script_instance() && get_script_instance()->has_method("_get_text")) { - return get_script_instance()->call("_get_text"); + String ret; + if (GDVIRTUAL_CALL(_get_text, ret)) { + return ret; } return ""; } String VisualScriptCustomNode::get_category() const { - if (get_script_instance() && get_script_instance()->has_method("_get_category")) { - return get_script_instance()->call("_get_category"); + String ret; + if (GDVIRTUAL_CALL(_get_category, ret)) { + return ret; } return "Custom"; } @@ -2941,14 +2976,7 @@ public: virtual int get_working_memory_size() const { return work_mem_size; } virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) { - if (node->get_script_instance()) { -#ifdef DEBUG_ENABLED - if (!node->get_script_instance()->has_method(VisualScriptLanguage::singleton->_step)) { - r_error_str = RTR("Custom node has no _step() method, can't process graph."); - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return 0; - } -#endif + if (GDVIRTUAL_IS_OVERRIDDEN_PTR(node, _step)) { Array in_values; Array out_values; Array work_mem; @@ -2969,7 +2997,8 @@ public: int ret_out; - Variant ret = node->get_script_instance()->call(VisualScriptLanguage::singleton->_step, in_values, out_values, p_start_mode, work_mem); + Variant ret; + GDVIRTUAL_CALL_PTR(node, _step, in_values, out_values, p_start_mode, work_mem, ret); if (ret.get_type() == Variant::STRING) { r_error_str = ret; r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; @@ -2995,6 +3024,9 @@ public: } return ret_out; + } else { + r_error_str = RTR("Custom node has no _step() method, can't process graph."); + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; } return 0; @@ -3008,11 +3040,8 @@ VisualScriptNodeInstance *VisualScriptCustomNode::instantiate(VisualScriptInstan instance->in_count = get_input_value_port_count(); instance->out_count = get_output_value_port_count(); - if (get_script_instance() && get_script_instance()->has_method("_get_working_memory_size")) { - instance->work_mem_size = get_script_instance()->call("_get_working_memory_size"); - } else { - instance->work_mem_size = 0; - } + instance->work_mem_size = 0; + GDVIRTUAL_CALL(_get_working_memory_size, instance->work_mem_size); return instance; } @@ -3022,32 +3051,29 @@ void VisualScriptCustomNode::_script_changed() { } void VisualScriptCustomNode::_bind_methods() { - BIND_VMETHOD(MethodInfo(Variant::INT, "_get_output_sequence_port_count")); - BIND_VMETHOD(MethodInfo(Variant::BOOL, "_has_input_sequence_port")); - - BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_output_sequence_port_text", PropertyInfo(Variant::INT, "idx"))); - BIND_VMETHOD(MethodInfo(Variant::INT, "_get_input_value_port_count")); - BIND_VMETHOD(MethodInfo(Variant::INT, "_get_output_value_port_count")); + GDVIRTUAL_BIND(_get_output_sequence_port_count); + GDVIRTUAL_BIND(_has_input_sequence_port); + GDVIRTUAL_BIND(_get_output_sequence_port_text, "seq_idx"); - BIND_VMETHOD(MethodInfo(Variant::INT, "_get_input_value_port_type", PropertyInfo(Variant::INT, "idx"))); - BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_input_value_port_name", PropertyInfo(Variant::INT, "idx"))); - BIND_VMETHOD(MethodInfo(Variant::INT, "_get_input_value_port_hint", PropertyInfo(Variant::INT, "idx"))); - BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_input_value_port_hint_string", PropertyInfo(Variant::INT, "idx"))); + GDVIRTUAL_BIND(_get_input_value_port_count); + GDVIRTUAL_BIND(_get_input_value_port_type, "input_idx"); + GDVIRTUAL_BIND(_get_input_value_port_name, "input_idx"); + GDVIRTUAL_BIND(_get_input_value_port_hint, "input_idx"); + GDVIRTUAL_BIND(_get_input_value_port_hint_string, "input_idx"); - BIND_VMETHOD(MethodInfo(Variant::INT, "_get_output_value_port_type", PropertyInfo(Variant::INT, "idx"))); - BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_output_value_port_name", PropertyInfo(Variant::INT, "idx"))); - BIND_VMETHOD(MethodInfo(Variant::INT, "_get_output_value_port_hint", PropertyInfo(Variant::INT, "idx"))); - BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_output_value_port_hint_string", PropertyInfo(Variant::INT, "idx"))); + GDVIRTUAL_BIND(_get_output_value_port_count); + GDVIRTUAL_BIND(_get_output_value_port_type, "output_idx"); + GDVIRTUAL_BIND(_get_output_value_port_name, "output_idx"); + GDVIRTUAL_BIND(_get_output_value_port_hint, "output_idx"); + GDVIRTUAL_BIND(_get_output_value_port_hint_string, "output_idx"); - BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_caption")); - BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_text")); - BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_category")); + GDVIRTUAL_BIND(_get_caption); + GDVIRTUAL_BIND(_get_text); + GDVIRTUAL_BIND(_get_category); - BIND_VMETHOD(MethodInfo(Variant::INT, "_get_working_memory_size")); + GDVIRTUAL_BIND(_get_working_memory_size); - MethodInfo stepmi(Variant::NIL, "_step", PropertyInfo(Variant::ARRAY, "inputs"), PropertyInfo(Variant::ARRAY, "outputs"), PropertyInfo(Variant::INT, "start_mode"), PropertyInfo(Variant::ARRAY, "working_mem")); - stepmi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; - BIND_VMETHOD(stepmi); + GDVIRTUAL_BIND(_step, "inputs", "outputs", "start_mode", "working_mem"); BIND_ENUM_CONSTANT(START_MODE_BEGIN_SEQUENCE); BIND_ENUM_CONSTANT(START_MODE_CONTINUE_SEQUENCE); @@ -3170,9 +3196,7 @@ VisualScriptNodeInstance *VisualScriptSubCall::instantiate(VisualScriptInstance } void VisualScriptSubCall::_bind_methods() { - MethodInfo scmi(Variant::NIL, "_subcall", PropertyInfo(Variant::NIL, "arguments")); - scmi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; - BIND_VMETHOD(scmi); + // Since this is script only, registering virtual function is no longer valid. Will have to go in docs. } VisualScriptSubCall::VisualScriptSubCall() { diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h index 2390e5c7bc..35d9b0b4fe 100644 --- a/modules/visual_script/visual_script_nodes.h +++ b/modules/visual_script/visual_script_nodes.h @@ -31,6 +31,8 @@ #ifndef VISUAL_SCRIPT_NODES_H #define VISUAL_SCRIPT_NODES_H +#include "core/object/gdvirtual.gen.inc" +#include "core/object/script_language.h" #include "visual_script.h" class VisualScriptFunction : public VisualScriptNode { @@ -757,6 +759,30 @@ class VisualScriptCustomNode : public VisualScriptNode { protected: static void _bind_methods(); + friend class VisualScriptNodeInstanceCustomNode; + GDVIRTUAL0RC(int, _get_output_sequence_port_count) + GDVIRTUAL0RC(bool, _has_input_sequence_port) + GDVIRTUAL1RC(String, _get_output_sequence_port_text, int) + + GDVIRTUAL0RC(int, _get_input_value_port_count) + GDVIRTUAL1RC(int, _get_input_value_port_type, int) + GDVIRTUAL1RC(String, _get_input_value_port_name, int) + GDVIRTUAL1RC(int, _get_input_value_port_hint, int) + GDVIRTUAL1RC(String, _get_input_value_port_hint_string, int) + + GDVIRTUAL0RC(int, _get_output_value_port_count) + GDVIRTUAL1RC(int, _get_output_value_port_type, int) + GDVIRTUAL1RC(String, _get_output_value_port_name, int) + GDVIRTUAL1RC(int, _get_output_value_port_hint, int) + GDVIRTUAL1RC(String, _get_output_value_port_hint_string, int) + + GDVIRTUAL0RC(String, _get_caption) + GDVIRTUAL0RC(String, _get_text) + GDVIRTUAL0RC(String, _get_category) + + GDVIRTUAL0RC(int, _get_working_memory_size) + + GDVIRTUAL4RC(Variant, _step, Array, Array, int, Array) public: enum StartMode { //replicated for step diff --git a/modules/visual_script/visual_script_property_selector.cpp b/modules/visual_script/visual_script_property_selector.cpp index 8bf1c6cbfa..d8b88d6cd3 100644 --- a/modules/visual_script/visual_script_property_selector.cpp +++ b/modules/visual_script/visual_script_property_selector.cpp @@ -55,7 +55,7 @@ void VisualScriptPropertySelector::_sbox_input(const Ref<InputEvent> &p_ie) { case KEY_DOWN: case KEY_PAGEUP: case KEY_PAGEDOWN: { - search_options->call("_gui_input", k); + search_options->gui_input(k); search_box->accept_event(); TreeItem *root = search_options->get_root(); @@ -74,6 +74,8 @@ void VisualScriptPropertySelector::_sbox_input(const Ref<InputEvent> &p_ie) { current->select(0); } break; + default: + break; } } } diff --git a/modules/visual_script/visual_script_yield_nodes.cpp b/modules/visual_script/visual_script_yield_nodes.cpp index 9fa49b8a1d..cded1e587c 100644 --- a/modules/visual_script/visual_script_yield_nodes.cpp +++ b/modules/visual_script/visual_script_yield_nodes.cpp @@ -93,7 +93,7 @@ String VisualScriptYield::get_text() const { class VisualScriptNodeInstanceYield : public VisualScriptNodeInstance { public: VisualScriptYield::YieldMode mode; - float wait_time; + double wait_time; virtual int get_working_memory_size() const { return 1; } //yield needs at least 1 //virtual bool is_output_port_unsequenced(int p_idx) const { return false; } @@ -159,7 +159,7 @@ VisualScriptYield::YieldMode VisualScriptYield::get_yield_mode() { return yield_mode; } -void VisualScriptYield::set_wait_time(float p_time) { +void VisualScriptYield::set_wait_time(double p_time) { if (wait_time == p_time) { return; } @@ -167,7 +167,7 @@ void VisualScriptYield::set_wait_time(float p_time) { ports_changed_notify(); } -float VisualScriptYield::get_wait_time() { +double VisualScriptYield::get_wait_time() { return wait_time; } diff --git a/modules/visual_script/visual_script_yield_nodes.h b/modules/visual_script/visual_script_yield_nodes.h index fa596173a6..6005ff30b0 100644 --- a/modules/visual_script/visual_script_yield_nodes.h +++ b/modules/visual_script/visual_script_yield_nodes.h @@ -47,7 +47,7 @@ public: private: YieldMode yield_mode; - float wait_time; + double wait_time; protected: virtual void _validate_property(PropertyInfo &property) const override; @@ -73,8 +73,8 @@ public: void set_yield_mode(YieldMode p_mode); YieldMode get_yield_mode(); - void set_wait_time(float p_time); - float get_wait_time(); + void set_wait_time(double p_time); + double get_wait_time(); virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override; diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp index 49997b42d3..26c0176ea4 100644 --- a/modules/websocket/wsl_client.cpp +++ b/modules/websocket/wsl_client.cpp @@ -161,22 +161,28 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, ERR_FAIL_COND_V(p_path.is_empty(), ERR_INVALID_PARAMETER); _peer = Ref<WSLPeer>(memnew(WSLPeer)); - IPAddress addr; - if (!p_host.is_valid_ip_address()) { - addr = IP::get_singleton()->resolve_hostname(p_host); + if (p_host.is_valid_ip_address()) { + ip_candidates.clear(); + ip_candidates.push_back(IPAddress(p_host)); } else { - addr = p_host; + ip_candidates = IP::get_singleton()->resolve_hostname_addresses(p_host); } - ERR_FAIL_COND_V(!addr.is_valid(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(ip_candidates.is_empty(), ERR_INVALID_PARAMETER); String port = ""; if ((p_port != 80 && !p_ssl) || (p_port != 443 && p_ssl)) { port = ":" + itos(p_port); } - Error err = _tcp->connect_to_host(addr, p_port); + Error err = ERR_BUG; // Should be at least one entry. + while (ip_candidates.size() > 0) { + err = _tcp->connect_to_host(ip_candidates.pop_front(), p_port); + if (err == OK) { + break; + } + } if (err != OK) { _tcp->disconnect_from_host(); _on_error(); @@ -185,6 +191,7 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, _connection = _tcp; _use_ssl = p_ssl; _host = p_host; + _port = p_port; // Strip edges from protocols. _protocols.resize(p_protocols.size()); String *pw = _protocols.ptrw(); @@ -244,6 +251,7 @@ void WSLClient::poll() { _on_error(); break; case StreamPeerTCP::STATUS_CONNECTED: { + ip_candidates.clear(); Ref<StreamPeerSSL> ssl; if (_use_ssl) { if (_connection == _tcp) { @@ -274,6 +282,12 @@ void WSLClient::poll() { _do_handshake(); } break; case StreamPeerTCP::STATUS_ERROR: + while (ip_candidates.size() > 0) { + _tcp->disconnect_from_host(); + if (_tcp->connect_to_host(ip_candidates.pop_front(), _port) == OK) { + return; + } + } disconnect_from_host(); _on_error(); break; @@ -315,6 +329,8 @@ void WSLClient::disconnect_from_host(int p_code, String p_reason) { memset(_resp_buf, 0, sizeof(_resp_buf)); _resp_pos = 0; + + ip_candidates.clear(); } IPAddress WSLClient::get_connected_host() const { diff --git a/modules/websocket/wsl_client.h b/modules/websocket/wsl_client.h index 849639ee8b..3972977910 100644 --- a/modules/websocket/wsl_client.h +++ b/modules/websocket/wsl_client.h @@ -63,6 +63,8 @@ private: String _key; String _host; + int _port; + Array ip_candidates; Vector<String> _protocols; bool _use_ssl = false; diff --git a/modules/webxr/godot_webxr.h b/modules/webxr/godot_webxr.h index 41a690f473..7aac0a6508 100644 --- a/modules/webxr/godot_webxr.h +++ b/modules/webxr/godot_webxr.h @@ -62,7 +62,7 @@ extern void godot_webxr_initialize( extern void godot_webxr_uninitialize(); extern int godot_webxr_get_view_count(); -extern int *godot_webxr_get_render_targetsize(); +extern int *godot_webxr_get_render_target_size(); extern float *godot_webxr_get_transform_for_eye(int p_eye); extern float *godot_webxr_get_projection_for_eye(int p_eye); extern int godot_webxr_get_external_texture_for_eye(int p_eye); diff --git a/modules/webxr/native/library_godot_webxr.js b/modules/webxr/native/library_godot_webxr.js index 6e19a8ac6e..c4b21defce 100644 --- a/modules/webxr/native/library_godot_webxr.js +++ b/modules/webxr/native/library_godot_webxr.js @@ -406,9 +406,9 @@ const GodotWebXR = { return GodotWebXR.pose.views.length; }, - godot_webxr_get_render_targetsize__proxy: 'sync', - godot_webxr_get_render_targetsize__sig: 'i', - godot_webxr_get_render_targetsize: function () { + godot_webxr_get_render_target_size__proxy: 'sync', + godot_webxr_get_render_target_size__sig: 'i', + godot_webxr_get_render_target_size: function () { if (!GodotWebXR.session || !GodotWebXR.pose) { return 0; } diff --git a/modules/webxr/webxr_interface_js.cpp b/modules/webxr/webxr_interface_js.cpp index 099e769303..2d699961ae 100644 --- a/modules/webxr/webxr_interface_js.cpp +++ b/modules/webxr/webxr_interface_js.cpp @@ -199,7 +199,7 @@ StringName WebXRInterfaceJS::get_name() const { return "WebXR"; }; -int WebXRInterfaceJS::get_capabilities() const { +uint32_t WebXRInterfaceJS::get_capabilities() const { return XRInterface::XR_STEREO | XRInterface::XR_MONO; }; @@ -254,9 +254,9 @@ bool WebXRInterfaceJS::initialize() { void WebXRInterfaceJS::uninitialize() { if (initialized) { XRServer *xr_server = XRServer::get_singleton(); - if (xr_server != nullptr) { + if (xr_server != nullptr && xr_server->get_primary_interface() == this) { // no longer our primary interface - xr_server->clear_primary_interface_if(this); + xr_server->set_primary_interface(nullptr); } godot_webxr_uninitialize(); @@ -285,12 +285,12 @@ Transform3D WebXRInterfaceJS::_js_matrix_to_transform(float *p_js_matrix) { return transform; } -Size2 WebXRInterfaceJS::get_render_targetsize() { +Size2 WebXRInterfaceJS::get_render_target_size() { if (render_targetsize.width != 0 && render_targetsize.height != 0) { return render_targetsize; } - int *js_size = godot_webxr_get_render_targetsize(); + int *js_size = godot_webxr_get_render_target_size(); if (!initialized || js_size == nullptr) { // As a temporary default (until WebXR is fully initialized), use half the window size. Size2 temp = DisplayServer::get_singleton()->window_get_size(); @@ -365,20 +365,6 @@ CameraMatrix WebXRInterfaceJS::get_projection_for_view(uint32_t p_view, real_t p return eye; } -unsigned int WebXRInterfaceJS::get_external_texture_for_eye(XRInterface::Eyes p_eye) { - if (!initialized) { - return 0; - } - return godot_webxr_get_external_texture_for_eye(p_eye); -} - -void WebXRInterfaceJS::commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) { - if (!initialized) { - return; - } - godot_webxr_commit_for_eye(p_eye); -} - Vector<BlitToScreen> WebXRInterfaceJS::commit_views(RID p_render_target, const Rect2 &p_screen_rect) { Vector<BlitToScreen> blit_to_screen; @@ -474,10 +460,6 @@ void WebXRInterfaceJS::_on_controller_changed() { } } -void WebXRInterfaceJS::notification(int p_what) { - // Nothing to do here. -} - WebXRInterfaceJS::WebXRInterfaceJS() { initialized = false; session_mode = "inline"; diff --git a/modules/webxr/webxr_interface_js.h b/modules/webxr/webxr_interface_js.h index f9368582b7..82307190db 100644 --- a/modules/webxr/webxr_interface_js.h +++ b/modules/webxr/webxr_interface_js.h @@ -76,23 +76,20 @@ public: virtual PackedVector3Array get_bounds_geometry() const override; virtual StringName get_name() const override; - virtual int get_capabilities() const override; + virtual uint32_t get_capabilities() const override; virtual bool is_initialized() const override; virtual bool initialize() override; virtual void uninitialize() override; - virtual Size2 get_render_targetsize() override; + virtual Size2 get_render_target_size() override; virtual uint32_t get_view_count() override; virtual Transform3D get_camera_transform() override; virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override; virtual CameraMatrix get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) override; - virtual unsigned int get_external_texture_for_eye(XRInterface::Eyes p_eye) override; - virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override; virtual Vector<BlitToScreen> commit_views(RID p_render_target, const Rect2 &p_screen_rect) override; virtual void process() override; - virtual void notification(int p_what) override; void _on_controller_changed(); |