Path: blob/master/modules/text_server_fb/text_server_fb.cpp
20957 views
/**************************************************************************/1/* text_server_fb.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "text_server_fb.h"3132#ifdef GDEXTENSION33// Headers for building as GDExtension plug-in.3435#include <godot_cpp/classes/file_access.hpp>36#include <godot_cpp/classes/os.hpp>37#include <godot_cpp/classes/project_settings.hpp>38#include <godot_cpp/classes/rendering_server.hpp>39#include <godot_cpp/classes/translation_server.hpp>40#include <godot_cpp/core/error_macros.hpp>4142#define OT_TAG(m_c1, m_c2, m_c3, m_c4) ((int32_t)((((uint32_t)(m_c1) & 0xff) << 24) | (((uint32_t)(m_c2) & 0xff) << 16) | (((uint32_t)(m_c3) & 0xff) << 8) | ((uint32_t)(m_c4) & 0xff)))4344using namespace godot;4546#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get_setting_with_override(m_var)4748#elif defined(GODOT_MODULE)49// Headers for building as built-in module.5051#include "core/config/project_settings.h"52#include "core/error/error_macros.h"53#include "core/io/file_access.h"54#include "core/string/print_string.h"55#include "core/string/translation_server.h"56#include "servers/rendering/rendering_server.h"5758#include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg.5960#endif6162// Thirdparty headers.6364#ifdef MODULE_MSDFGEN_ENABLED65GODOT_GCC_WARNING_PUSH_AND_IGNORE("-Wshadow")66GODOT_MSVC_WARNING_PUSH_AND_IGNORE(4458) // "Declaration of 'identifier' hides class member".6768#include <core/EdgeHolder.h>69#include <core/ShapeDistanceFinder.h>70#include <core/contour-combiners.h>71#include <core/edge-selectors.h>72#include <msdfgen.h>7374GODOT_GCC_WARNING_POP75GODOT_MSVC_WARNING_POP76#endif7778#ifdef MODULE_FREETYPE_ENABLED79#include FT_SFNT_NAMES_H80#include FT_TRUETYPE_IDS_H81#ifdef MODULE_SVG_ENABLED82#include "thorvg_svg_in_ot.h"83#endif84#endif8586/*************************************************************************/8788bool TextServerFallback::_has_feature(Feature p_feature) const {89switch (p_feature) {90case FEATURE_SIMPLE_LAYOUT:91case FEATURE_FONT_BITMAP:92#ifdef MODULE_FREETYPE_ENABLED93case FEATURE_FONT_DYNAMIC:94#endif95#ifdef MODULE_MSDFGEN_ENABLED96case FEATURE_FONT_MSDF:97#endif98return true;99default: {100}101}102return false;103}104105String TextServerFallback::_get_name() const {106#ifdef GDEXTENSION107return "Fallback (GDExtension)";108#elif defined(GODOT_MODULE)109return "Fallback (Built-in)";110#endif111}112113int64_t TextServerFallback::_get_features() const {114int64_t interface_features = FEATURE_SIMPLE_LAYOUT | FEATURE_FONT_BITMAP;115#ifdef MODULE_FREETYPE_ENABLED116interface_features |= FEATURE_FONT_DYNAMIC;117#endif118#ifdef MODULE_MSDFGEN_ENABLED119interface_features |= FEATURE_FONT_MSDF;120#endif121122return interface_features;123}124125void TextServerFallback::_free_rid(const RID &p_rid) {126_THREAD_SAFE_METHOD_127if (font_owner.owns(p_rid)) {128MutexLock ftlock(ft_mutex);129130FontFallback *fd = font_owner.get_or_null(p_rid);131for (const KeyValue<Vector2i, FontForSizeFallback *> &ffsd : fd->cache) {132OversamplingLevel *ol = oversampling_levels.getptr(ffsd.value->viewport_oversampling);133if (ol != nullptr) {134ol->fonts.erase(ffsd.value);135}136}137{138MutexLock lock(fd->mutex);139font_owner.free(p_rid);140}141memdelete(fd);142} else if (font_var_owner.owns(p_rid)) {143MutexLock ftlock(ft_mutex);144145FontFallbackLinkedVariation *fdv = font_var_owner.get_or_null(p_rid);146{147font_var_owner.free(p_rid);148}149memdelete(fdv);150} else if (shaped_owner.owns(p_rid)) {151ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_rid);152{153MutexLock lock(sd->mutex);154shaped_owner.free(p_rid);155}156memdelete(sd);157}158}159160bool TextServerFallback::_has(const RID &p_rid) {161_THREAD_SAFE_METHOD_162return font_owner.owns(p_rid) || shaped_owner.owns(p_rid);163}164165String TextServerFallback::_get_support_data_filename() const {166return "";167}168169String TextServerFallback::_get_support_data_info() const {170return "Not supported";171}172173bool TextServerFallback::_load_support_data(const String &p_filename) {174return false; // No extra data used.175}176177bool TextServerFallback::_save_support_data(const String &p_filename) const {178return false; // No extra data used.179}180181PackedByteArray TextServerFallback::_get_support_data() const {182return PackedByteArray(); // No extra data used.183}184185bool TextServerFallback::_is_locale_using_support_data(const String &p_locale) const {186return false; // No data support.187}188189bool TextServerFallback::_is_locale_right_to_left(const String &p_locale) const {190return false; // No RTL support.191}192193_FORCE_INLINE_ void TextServerFallback::_insert_feature(const StringName &p_name, int32_t p_tag) {194feature_sets.insert(p_name, p_tag);195feature_sets_inv.insert(p_tag, p_name);196}197198void TextServerFallback::_insert_feature_sets() {199// Registered OpenType variation tag.200_insert_feature("italic", OT_TAG('i', 't', 'a', 'l'));201_insert_feature("optical_size", OT_TAG('o', 'p', 's', 'z'));202_insert_feature("slant", OT_TAG('s', 'l', 'n', 't'));203_insert_feature("width", OT_TAG('w', 'd', 't', 'h'));204_insert_feature("weight", OT_TAG('w', 'g', 'h', 't'));205}206207_FORCE_INLINE_ int32_t ot_tag_from_string(const char *p_str, int p_len) {208char tag[4];209uint32_t i;210211if (!p_str || !p_len || !*p_str) {212return OT_TAG(0, 0, 0, 0);213}214215if (p_len < 0 || p_len > 4) {216p_len = 4;217}218for (i = 0; i < (uint32_t)p_len && p_str[i]; i++) {219tag[i] = p_str[i];220}221222for (; i < 4; i++) {223tag[i] = ' ';224}225226return OT_TAG(tag[0], tag[1], tag[2], tag[3]);227}228229int64_t TextServerFallback::_name_to_tag(const String &p_name) const {230if (feature_sets.has(p_name)) {231return feature_sets[p_name];232}233234// No readable name, use tag string.235return ot_tag_from_string(p_name.replace("custom_", "").ascii().get_data(), -1);236}237238_FORCE_INLINE_ void ot_tag_to_string(int32_t p_tag, char *p_buf) {239p_buf[0] = (char)(uint8_t)(p_tag >> 24);240p_buf[1] = (char)(uint8_t)(p_tag >> 16);241p_buf[2] = (char)(uint8_t)(p_tag >> 8);242p_buf[3] = (char)(uint8_t)(p_tag >> 0);243}244245String TextServerFallback::_tag_to_name(int64_t p_tag) const {246if (feature_sets_inv.has(p_tag)) {247return feature_sets_inv[p_tag];248}249250// No readable name, use tag string.251char name[5];252memset(name, 0, 5);253ot_tag_to_string(p_tag, name);254return String("custom_") + String(name);255}256257/*************************************************************************/258/* Font Glyph Rendering */259/*************************************************************************/260261_FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_texture_pos_for_glyph(FontForSizeFallback *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height, bool p_msdf) const {262FontTexturePosition ret;263264int mw = p_width;265int mh = p_height;266267ShelfPackTexture *ct = p_data->textures.ptrw();268for (int32_t i = 0; i < p_data->textures.size(); i++) {269if (ct[i].image.is_null()) {270continue;271}272if (p_image_format != ct[i].image->get_format()) {273continue;274}275if (mw > ct[i].texture_w || mh > ct[i].texture_h) { // Too big for this texture.276continue;277}278279ret = ct[i].pack_rect(i, mh, mw);280if (ret.index != -1) {281break;282}283}284285if (ret.index == -1) {286// Could not find texture to fit, create one.287int texsize = MAX(p_data->size.x * 0.125, 256);288289texsize = next_power_of_2((uint32_t)texsize);290291if (p_msdf) {292texsize = MIN(texsize, 2048);293} else {294texsize = MIN(texsize, 1024);295}296if (mw > texsize) { // Special case, adapt to it?297texsize = next_power_of_2((uint32_t)mw);298}299if (mh > texsize) { // Special case, adapt to it?300texsize = next_power_of_2((uint32_t)mh);301}302303ShelfPackTexture tex = ShelfPackTexture(texsize, texsize);304tex.image = Image::create_empty(texsize, texsize, false, p_image_format);305{306// Zero texture.307uint8_t *w = tex.image->ptrw();308ERR_FAIL_COND_V(texsize * texsize * p_color_size > tex.image->get_data_size(), ret);309// Initialize the texture to all-white pixels to prevent artifacts when the310// font is displayed at a non-default scale with filtering enabled.311if (p_color_size == 2) {312for (int i = 0; i < texsize * texsize * p_color_size; i += 2) { // FORMAT_LA8, BW font.313w[i + 0] = 255;314w[i + 1] = 0;315}316} else if (p_color_size == 4) {317for (int i = 0; i < texsize * texsize * p_color_size; i += 4) { // FORMAT_RGBA8, Color font, Multichannel(+True) SDF.318if (p_msdf) {319w[i + 0] = 0;320w[i + 1] = 0;321w[i + 2] = 0;322} else {323w[i + 0] = 255;324w[i + 1] = 255;325w[i + 2] = 255;326}327w[i + 3] = 0;328}329} else {330ERR_FAIL_V(ret);331}332}333p_data->textures.push_back(tex);334335int32_t idx = p_data->textures.size() - 1;336ret = p_data->textures.write[idx].pack_rect(idx, mh, mw);337}338339return ret;340}341342#ifdef MODULE_MSDFGEN_ENABLED343344struct MSContext {345msdfgen::Point2 position;346msdfgen::Shape *shape = nullptr;347msdfgen::Contour *contour = nullptr;348};349350class DistancePixelConversion {351double invRange;352353public:354_FORCE_INLINE_ explicit DistancePixelConversion(double range) :355invRange(1 / range) {}356_FORCE_INLINE_ void operator()(float *pixels, const msdfgen::MultiAndTrueDistance &distance) const {357pixels[0] = float(invRange * distance.r + .5);358pixels[1] = float(invRange * distance.g + .5);359pixels[2] = float(invRange * distance.b + .5);360pixels[3] = float(invRange * distance.a + .5);361}362};363364struct MSDFThreadData {365msdfgen::Bitmap<float, 4> *output;366msdfgen::Shape *shape;367msdfgen::Projection *projection;368DistancePixelConversion *distancePixelConversion;369};370371static msdfgen::Point2 ft_point2(const FT_Vector &vector) {372return msdfgen::Point2(vector.x / 60.0f, vector.y / 60.0f);373}374375static int ft_move_to(const FT_Vector *to, void *user) {376MSContext *context = static_cast<MSContext *>(user);377if (!(context->contour && context->contour->edges.empty())) {378context->contour = &context->shape->addContour();379}380context->position = ft_point2(*to);381return 0;382}383384static int ft_line_to(const FT_Vector *to, void *user) {385MSContext *context = static_cast<MSContext *>(user);386msdfgen::Point2 endpoint = ft_point2(*to);387if (endpoint != context->position) {388context->contour->addEdge(new msdfgen::LinearSegment(context->position, endpoint));389context->position = endpoint;390}391return 0;392}393394static int ft_conic_to(const FT_Vector *control, const FT_Vector *to, void *user) {395MSContext *context = static_cast<MSContext *>(user);396context->contour->addEdge(new msdfgen::QuadraticSegment(context->position, ft_point2(*control), ft_point2(*to)));397context->position = ft_point2(*to);398return 0;399}400401static int ft_cubic_to(const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to, void *user) {402MSContext *context = static_cast<MSContext *>(user);403context->contour->addEdge(new msdfgen::CubicSegment(context->position, ft_point2(*control1), ft_point2(*control2), ft_point2(*to)));404context->position = ft_point2(*to);405return 0;406}407408void TextServerFallback::_generateMTSDF_threaded(void *p_td, uint32_t p_y) {409MSDFThreadData *td = static_cast<MSDFThreadData *>(p_td);410411msdfgen::ShapeDistanceFinder<msdfgen::OverlappingContourCombiner<msdfgen::MultiAndTrueDistanceSelector>> distanceFinder(*td->shape);412int row = td->shape->inverseYAxis ? td->output->height() - p_y - 1 : p_y;413for (int col = 0; col < td->output->width(); ++col) {414int x = (p_y % 2) ? td->output->width() - col - 1 : col;415msdfgen::Point2 p = td->projection->unproject(msdfgen::Point2(x + .5, p_y + .5));416msdfgen::MultiAndTrueDistance distance = distanceFinder.distance(p);417td->distancePixelConversion->operator()(td->output->operator()(x, row), distance);418}419}420421_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(FontFallback *p_font_data, FontForSizeFallback *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *p_outline, const Vector2 &p_advance) const {422msdfgen::Shape shape;423424shape.contours.clear();425shape.inverseYAxis = false;426427MSContext context = {};428context.shape = &shape;429FT_Outline_Funcs ft_functions;430ft_functions.move_to = &ft_move_to;431ft_functions.line_to = &ft_line_to;432ft_functions.conic_to = &ft_conic_to;433ft_functions.cubic_to = &ft_cubic_to;434ft_functions.shift = 0;435ft_functions.delta = 0;436437int error = FT_Outline_Decompose(p_outline, &ft_functions, &context);438ERR_FAIL_COND_V_MSG(error, FontGlyph(), "FreeType: Outline decomposition error: '" + String(FT_Error_String(error)) + "'.");439if (!shape.contours.empty() && shape.contours.back().edges.empty()) {440shape.contours.pop_back();441}442443if (FT_Outline_Get_Orientation(p_outline) == 1) {444for (int i = 0; i < (int)shape.contours.size(); ++i) {445shape.contours[i].reverse();446}447}448449shape.inverseYAxis = true;450shape.normalize();451452msdfgen::Shape::Bounds bounds = shape.getBounds(p_pixel_range);453454FontGlyph chr;455chr.found = true;456chr.advance = p_advance;457458if (shape.validate() && shape.contours.size() > 0) {459int w = (bounds.r - bounds.l);460int h = (bounds.t - bounds.b);461462if (w == 0 || h == 0) {463chr.texture_idx = -1;464chr.uv_rect = Rect2();465chr.rect = Rect2();466return chr;467}468int mw = w + p_rect_margin * 4;469int mh = h + p_rect_margin * 4;470471ERR_FAIL_COND_V(mw > 4096, FontGlyph());472ERR_FAIL_COND_V(mh > 4096, FontGlyph());473474FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, 4, Image::FORMAT_RGBA8, mw, mh, true);475ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph());476ShelfPackTexture &tex = p_data->textures.write[tex_pos.index];477478edgeColoringSimple(shape, 3.0); // Max. angle.479msdfgen::Bitmap<float, 4> image(w, h); // Texture size.480481DistancePixelConversion distancePixelConversion(p_pixel_range);482msdfgen::Projection projection(msdfgen::Vector2(1.0, 1.0), msdfgen::Vector2(-bounds.l, -bounds.b));483msdfgen::MSDFGeneratorConfig config(true, msdfgen::ErrorCorrectionConfig());484485MSDFThreadData td;486td.output = ℑ487td.shape = &shape;488td.projection = &projection;489td.distancePixelConversion = &distancePixelConversion;490491WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_native_group_task(&TextServerFallback::_generateMTSDF_threaded, &td, h, -1, true, String("TextServerFBRenderMSDF"));492WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);493494msdfgen::msdfErrorCorrection(image, shape, projection, p_pixel_range, config);495496{497uint8_t *wr = tex.image->ptrw();498499for (int i = 0; i < h; i++) {500for (int j = 0; j < w; j++) {501int ofs = ((i + tex_pos.y + p_rect_margin * 2) * tex.texture_w + j + tex_pos.x + p_rect_margin * 2) * 4;502ERR_FAIL_COND_V(ofs >= tex.image->get_data_size(), FontGlyph());503wr[ofs + 0] = (uint8_t)(CLAMP(image(j, i)[0] * 256.f, 0.f, 255.f));504wr[ofs + 1] = (uint8_t)(CLAMP(image(j, i)[1] * 256.f, 0.f, 255.f));505wr[ofs + 2] = (uint8_t)(CLAMP(image(j, i)[2] * 256.f, 0.f, 255.f));506wr[ofs + 3] = (uint8_t)(CLAMP(image(j, i)[3] * 256.f, 0.f, 255.f));507}508}509}510511tex.dirty = true;512513chr.texture_idx = tex_pos.index;514515chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w + p_rect_margin * 2, h + p_rect_margin * 2);516chr.rect.position = Vector2(bounds.l - p_rect_margin, -bounds.t - p_rect_margin);517chr.rect.size = chr.uv_rect.size;518}519return chr;520}521#endif522523#ifdef MODULE_FREETYPE_ENABLED524_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitmap(FontForSizeFallback *p_data, int p_rect_margin, FT_Bitmap p_bitmap, int p_yofs, int p_xofs, const Vector2 &p_advance, bool p_bgra) const {525FontGlyph chr;526chr.advance = p_advance * p_data->scale;527chr.found = true;528529int w = p_bitmap.width;530int h = p_bitmap.rows;531532if (w == 0 || h == 0) {533chr.texture_idx = -1;534chr.uv_rect = Rect2();535chr.rect = Rect2();536return chr;537}538539int color_size = 2;540541switch (p_bitmap.pixel_mode) {542case FT_PIXEL_MODE_MONO:543case FT_PIXEL_MODE_GRAY: {544color_size = 2;545} break;546case FT_PIXEL_MODE_BGRA: {547color_size = 4;548} break;549case FT_PIXEL_MODE_LCD: {550color_size = 4;551w /= 3;552} break;553case FT_PIXEL_MODE_LCD_V: {554color_size = 4;555h /= 3;556} break;557}558559int mw = w + p_rect_margin * 4;560int mh = h + p_rect_margin * 4;561562ERR_FAIL_COND_V(mw > 4096, FontGlyph());563ERR_FAIL_COND_V(mh > 4096, FontGlyph());564565Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8;566567FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, color_size, require_format, mw, mh, false);568ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph());569570// Fit character in char texture.571ShelfPackTexture &tex = p_data->textures.write[tex_pos.index];572573{574uint8_t *wr = tex.image->ptrw();575576for (int i = 0; i < h; i++) {577for (int j = 0; j < w; j++) {578int ofs = ((i + tex_pos.y + p_rect_margin * 2) * tex.texture_w + j + tex_pos.x + p_rect_margin * 2) * color_size;579ERR_FAIL_COND_V(ofs >= tex.image->get_data_size(), FontGlyph());580switch (p_bitmap.pixel_mode) {581case FT_PIXEL_MODE_MONO: {582int byte = i * p_bitmap.pitch + (j >> 3);583int bit = 1 << (7 - (j % 8));584wr[ofs + 0] = 255; // grayscale as 1585wr[ofs + 1] = (p_bitmap.buffer[byte] & bit) ? 255 : 0;586} break;587case FT_PIXEL_MODE_GRAY:588wr[ofs + 0] = 255; // grayscale as 1589wr[ofs + 1] = p_bitmap.buffer[i * p_bitmap.pitch + j];590break;591case FT_PIXEL_MODE_BGRA: {592int ofs_color = i * p_bitmap.pitch + (j << 2);593wr[ofs + 2] = p_bitmap.buffer[ofs_color + 0];594wr[ofs + 1] = p_bitmap.buffer[ofs_color + 1];595wr[ofs + 0] = p_bitmap.buffer[ofs_color + 2];596wr[ofs + 3] = p_bitmap.buffer[ofs_color + 3];597} break;598case FT_PIXEL_MODE_LCD: {599int ofs_color = i * p_bitmap.pitch + (j * 3);600if (p_bgra) {601wr[ofs + 0] = p_bitmap.buffer[ofs_color + 2];602wr[ofs + 1] = p_bitmap.buffer[ofs_color + 1];603wr[ofs + 2] = p_bitmap.buffer[ofs_color + 0];604wr[ofs + 3] = 255;605} else {606wr[ofs + 0] = p_bitmap.buffer[ofs_color + 0];607wr[ofs + 1] = p_bitmap.buffer[ofs_color + 1];608wr[ofs + 2] = p_bitmap.buffer[ofs_color + 2];609wr[ofs + 3] = 255;610}611} break;612case FT_PIXEL_MODE_LCD_V: {613int ofs_color = i * p_bitmap.pitch * 3 + j;614if (p_bgra) {615wr[ofs + 0] = p_bitmap.buffer[ofs_color + p_bitmap.pitch * 2];616wr[ofs + 1] = p_bitmap.buffer[ofs_color + p_bitmap.pitch];617wr[ofs + 2] = p_bitmap.buffer[ofs_color + 0];618wr[ofs + 3] = 255;619} else {620wr[ofs + 0] = p_bitmap.buffer[ofs_color + 0];621wr[ofs + 1] = p_bitmap.buffer[ofs_color + p_bitmap.pitch];622wr[ofs + 2] = p_bitmap.buffer[ofs_color + p_bitmap.pitch * 2];623wr[ofs + 3] = 255;624}625} break;626default:627ERR_FAIL_V_MSG(FontGlyph(), "Font uses unsupported pixel format: " + String::num_int64(p_bitmap.pixel_mode) + ".");628break;629}630}631}632}633634tex.dirty = true;635636chr.texture_idx = tex_pos.index;637638chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w + p_rect_margin * 2, h + p_rect_margin * 2);639chr.rect.position = Vector2(p_xofs - p_rect_margin, -p_yofs - p_rect_margin) * p_data->scale;640chr.rect.size = chr.uv_rect.size * p_data->scale;641return chr;642}643#endif644645/*************************************************************************/646/* Font Cache */647/*************************************************************************/648649bool TextServerFallback::_ensure_glyph(FontFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph, FontGlyph &r_glyph, uint32_t p_oversampling) const {650FontForSizeFallback *fd = nullptr;651ERR_FAIL_COND_V(!_ensure_cache_for_size(p_font_data, p_size, fd, false, p_oversampling), false);652653int32_t glyph_index = p_glyph & 0xffffff; // Remove subpixel shifts.654655HashMap<int32_t, FontGlyph>::Iterator E = fd->glyph_map.find(p_glyph);656if (E) {657bool tx_valid = true;658if (E->value.texture_idx >= 0) {659if (E->value.texture_idx < fd->textures.size()) {660tx_valid = fd->textures[E->value.texture_idx].image.is_valid();661} else {662tx_valid = false;663}664}665if (tx_valid) {666r_glyph = E->value;667return E->value.found;668#ifdef DEBUG_ENABLED669} else {670WARN_PRINT(vformat("Invalid texture cache for glyph %x in font %s, glyph will be re-rendered. Re-import this font to regenerate textures.", glyph_index, p_font_data->font_name));671#endif672}673}674675if (glyph_index == 0) { // Non graphical or invalid glyph, do not render.676E = fd->glyph_map.insert(p_glyph, FontGlyph());677r_glyph = E->value;678return true;679}680681#ifdef MODULE_FREETYPE_ENABLED682FontGlyph gl;683if (p_font_data->face) {684FT_Int32 flags = FT_LOAD_DEFAULT;685686bool outline = p_size.y > 0;687switch (p_font_data->hinting) {688case TextServer::HINTING_NONE:689flags |= FT_LOAD_NO_HINTING;690break;691case TextServer::HINTING_LIGHT:692flags |= FT_LOAD_TARGET_LIGHT;693break;694default:695flags |= FT_LOAD_TARGET_NORMAL;696break;697}698if (p_font_data->force_autohinter) {699flags |= FT_LOAD_FORCE_AUTOHINT;700}701if (outline || (p_font_data->disable_embedded_bitmaps && !FT_HAS_COLOR(p_font_data->face))) {702flags |= FT_LOAD_NO_BITMAP;703} else if (FT_HAS_COLOR(p_font_data->face)) {704flags |= FT_LOAD_COLOR;705}706707glyph_index = FT_Get_Char_Index(p_font_data->face, glyph_index);708709FT_Fixed v, h;710FT_Get_Advance(p_font_data->face, glyph_index, flags, &h);711FT_Get_Advance(p_font_data->face, glyph_index, flags | FT_LOAD_VERTICAL_LAYOUT, &v);712713int error = FT_Load_Glyph(p_font_data->face, glyph_index, flags);714if (error) {715E = fd->glyph_map.insert(p_glyph, FontGlyph());716r_glyph = E->value;717return false;718}719720if (!p_font_data->msdf) {721if ((p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && p_size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE * 64)) {722FT_Pos xshift = (int)((p_glyph >> 27) & 3) << 4;723FT_Outline_Translate(&p_font_data->face->glyph->outline, xshift, 0);724} else if ((p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && p_size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE * 64)) {725FT_Pos xshift = (int)((p_glyph >> 27) & 3) << 5;726FT_Outline_Translate(&p_font_data->face->glyph->outline, xshift, 0);727}728}729730if (p_font_data->embolden != 0.f) {731FT_Pos strength = p_font_data->embolden * p_size.x / 16; // 26.6 fractional units (1 / 64).732FT_Outline_Embolden(&p_font_data->face->glyph->outline, strength);733}734735if (p_font_data->transform != Transform2D()) {736FT_Matrix mat = { FT_Fixed(p_font_data->transform[0][0] * 65536), FT_Fixed(p_font_data->transform[0][1] * 65536), FT_Fixed(p_font_data->transform[1][0] * 65536), FT_Fixed(p_font_data->transform[1][1] * 65536) }; // 16.16 fractional units (1 / 65536).737FT_Outline_Transform(&p_font_data->face->glyph->outline, &mat);738}739740FT_Render_Mode aa_mode = FT_RENDER_MODE_NORMAL;741bool bgra = false;742switch (p_font_data->antialiasing) {743case FONT_ANTIALIASING_NONE: {744aa_mode = FT_RENDER_MODE_MONO;745} break;746case FONT_ANTIALIASING_GRAY: {747aa_mode = FT_RENDER_MODE_NORMAL;748} break;749case FONT_ANTIALIASING_LCD: {750int aa_layout = (int)((p_glyph >> 24) & 7);751switch (aa_layout) {752case FONT_LCD_SUBPIXEL_LAYOUT_HRGB: {753aa_mode = FT_RENDER_MODE_LCD;754bgra = false;755} break;756case FONT_LCD_SUBPIXEL_LAYOUT_HBGR: {757aa_mode = FT_RENDER_MODE_LCD;758bgra = true;759} break;760case FONT_LCD_SUBPIXEL_LAYOUT_VRGB: {761aa_mode = FT_RENDER_MODE_LCD_V;762bgra = false;763} break;764case FONT_LCD_SUBPIXEL_LAYOUT_VBGR: {765aa_mode = FT_RENDER_MODE_LCD_V;766bgra = true;767} break;768default: {769aa_mode = FT_RENDER_MODE_NORMAL;770} break;771}772} break;773}774775FT_GlyphSlot slot = p_font_data->face->glyph;776bool from_svg = (slot->format == FT_GLYPH_FORMAT_SVG); // Need to check before FT_Render_Glyph as it will change format to bitmap.777if (!outline) {778if (!p_font_data->msdf) {779error = FT_Render_Glyph(slot, aa_mode);780}781if (!error) {782if (p_font_data->msdf) {783#ifdef MODULE_MSDFGEN_ENABLED784gl = 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);785#else786fd->glyph_map[p_glyph] = FontGlyph();787ERR_FAIL_V_MSG(false, "Compiled without MSDFGEN support!");788#endif789} else {790gl = rasterize_bitmap(fd, rect_range, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, bgra);791}792}793} else {794FT_Stroker stroker;795if (FT_Stroker_New(ft_library, &stroker) != 0) {796fd->glyph_map[p_glyph] = FontGlyph();797ERR_FAIL_V_MSG(false, "FreeType: Failed to load glyph stroker.");798}799800FT_Stroker_Set(stroker, (int)(fd->size.y * 16.0), FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0);801FT_Glyph glyph;802FT_BitmapGlyph glyph_bitmap;803804if (FT_Get_Glyph(p_font_data->face->glyph, &glyph) != 0) {805goto cleanup_stroker;806}807if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) {808goto cleanup_glyph;809}810if (FT_Glyph_To_Bitmap(&glyph, aa_mode, nullptr, 1) != 0) {811goto cleanup_glyph;812}813glyph_bitmap = (FT_BitmapGlyph)glyph;814gl = rasterize_bitmap(fd, rect_range, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2(), bgra);815816cleanup_glyph:817FT_Done_Glyph(glyph);818cleanup_stroker:819FT_Stroker_Done(stroker);820}821gl.from_svg = from_svg;822E = fd->glyph_map.insert(p_glyph, gl);823r_glyph = E->value;824return gl.found;825}826#endif827E = fd->glyph_map.insert(p_glyph, FontGlyph());828r_glyph = E->value;829return false;830}831832bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_font_data, const Vector2i &p_size, FontForSizeFallback *&r_cache_for_size, bool p_silent, uint32_t p_oversampling) const {833ERR_FAIL_COND_V(p_size.x <= 0, false);834835HashMap<Vector2i, FontForSizeFallback *>::Iterator E = p_font_data->cache.find(p_size);836if (E) {837#ifdef MODULE_FREETYPE_ENABLED838if (E->value->fsize != nullptr) {839FT_Activate_Size(E->value->fsize);840}841#endif842r_cache_for_size = E->value;843// Size used directly, remove from oversampling list.844if (p_oversampling == 0 && E->value->viewport_oversampling != 0) {845OversamplingLevel *ol = oversampling_levels.getptr(E->value->viewport_oversampling);846if (ol) {847ol->fonts.erase(E->value);848}849}850return true;851}852853r_cache_for_size = nullptr;854FontForSizeFallback *fd = memnew(FontForSizeFallback);855fd->size = p_size;856if (p_font_data->data_ptr && (p_font_data->data_size > 0)) {857// Init dynamic font.858#ifdef MODULE_FREETYPE_ENABLED859int error = 0;860{861MutexLock ftlock(ft_mutex);862if (!ft_library) {863error = FT_Init_FreeType(&ft_library);864if (error != 0) {865memdelete(fd);866if (p_silent) {867return false;868} else {869ERR_FAIL_V_MSG(false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");870}871}872#ifdef MODULE_SVG_ENABLED873FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks());874#endif875}876877if (p_font_data->face == nullptr) {878memset(&p_font_data->stream, 0, sizeof(FT_StreamRec));879p_font_data->stream.base = (unsigned char *)p_font_data->data_ptr;880p_font_data->stream.size = p_font_data->data_size;881p_font_data->stream.pos = 0;882883FT_Open_Args fargs;884memset(&fargs, 0, sizeof(FT_Open_Args));885fargs.memory_base = (unsigned char *)p_font_data->data_ptr;886fargs.memory_size = p_font_data->data_size;887fargs.flags = FT_OPEN_MEMORY;888fargs.stream = &p_font_data->stream;889890int max_index = 0;891FT_Face tmp_face = nullptr;892error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);893if (tmp_face && error == 0) {894max_index = tmp_face->num_faces - 1;895}896if (tmp_face) {897FT_Done_Face(tmp_face);898}899900error = FT_Open_Face(ft_library, &fargs, CLAMP(p_font_data->face_index, 0, max_index), &p_font_data->face);901if (error) {902FT_Done_Face(p_font_data->face);903p_font_data->face = nullptr;904memdelete(fd);905if (p_silent) {906return false;907} else {908ERR_FAIL_V_MSG(false, "FreeType: Error loading font: '" + String(FT_Error_String(error)) + "'.");909}910}911}912913FT_New_Size(p_font_data->face, &fd->fsize);914FT_Activate_Size(fd->fsize);915}916917double sz = double(fd->size.x) / 64.0;918if (p_font_data->msdf) {919sz = p_font_data->msdf_source_size;920}921922if (FT_HAS_COLOR(p_font_data->face) && p_font_data->face->num_fixed_sizes > 0) {923int best_match = 0;924int diff = Math::abs(sz - ((int64_t)p_font_data->face->available_sizes[0].width));925fd->scale = sz / p_font_data->face->available_sizes[0].width;926for (int i = 1; i < p_font_data->face->num_fixed_sizes; i++) {927int ndiff = Math::abs(sz - ((int64_t)p_font_data->face->available_sizes[i].width));928if (ndiff < diff) {929best_match = i;930diff = ndiff;931fd->scale = sz / p_font_data->face->available_sizes[i].width;932}933}934FT_Select_Size(p_font_data->face, best_match);935} else {936FT_Size_RequestRec req;937req.type = FT_SIZE_REQUEST_TYPE_NOMINAL;938req.width = MIN(2048.0, sz) * 64.0;939req.height = MIN(2048.0, sz) * 64.0;940req.horiResolution = 0;941req.vertResolution = 0;942943FT_Request_Size(p_font_data->face, &req);944if (p_font_data->face->size->metrics.y_ppem != 0) {945fd->scale = sz / (double)p_font_data->face->size->metrics.y_ppem;946}947}948949fd->ascent = (p_font_data->face->size->metrics.ascender / 64.0) * fd->scale;950fd->descent = (-p_font_data->face->size->metrics.descender / 64.0) * fd->scale;951fd->underline_position = (-FT_MulFix(p_font_data->face->underline_position, p_font_data->face->size->metrics.y_scale) / 64.0) * fd->scale;952fd->underline_thickness = (FT_MulFix(p_font_data->face->underline_thickness, p_font_data->face->size->metrics.y_scale) / 64.0) * fd->scale;953954if (!p_font_data->face_init) {955// When a font does not provide a `family_name`, FreeType tries to synthesize one based on other names.956// FreeType automatically converts non-ASCII characters to "?" in the synthesized name.957// To avoid that behavior, use the format-specific name directly if available.958if (FT_IS_SFNT(p_font_data->face)) {959int name_count = FT_Get_Sfnt_Name_Count(p_font_data->face);960for (int i = 0; i < name_count; i++) {961FT_SfntName sfnt_name;962if (FT_Get_Sfnt_Name(p_font_data->face, i, &sfnt_name) != 0) {963continue;964}965if (sfnt_name.name_id != TT_NAME_ID_FONT_FAMILY && sfnt_name.name_id != TT_NAME_ID_TYPOGRAPHIC_FAMILY) {966continue;967}968if (!p_font_data->font_name.is_empty() && sfnt_name.language_id != TT_MS_LANGID_ENGLISH_UNITED_STATES) {969continue;970}971972switch (sfnt_name.platform_id) {973case TT_PLATFORM_APPLE_UNICODE: {974p_font_data->font_name.clear();975p_font_data->font_name.append_utf16((const char16_t *)sfnt_name.string, sfnt_name.string_len / 2, false);976} break;977978case TT_PLATFORM_MICROSOFT: {979if (sfnt_name.encoding_id == TT_MS_ID_UNICODE_CS || sfnt_name.encoding_id == TT_MS_ID_UCS_4) {980p_font_data->font_name.clear();981p_font_data->font_name.append_utf16((const char16_t *)sfnt_name.string, sfnt_name.string_len / 2, false);982}983} break;984}985}986}987if (p_font_data->font_name.is_empty() && p_font_data->face->family_name != nullptr) {988p_font_data->font_name = String::utf8((const char *)p_font_data->face->family_name);989}990if (p_font_data->face->style_name != nullptr) {991p_font_data->style_name = String::utf8((const char *)p_font_data->face->style_name);992}993p_font_data->weight = _font_get_weight_by_name(p_font_data->style_name.to_lower());994p_font_data->stretch = _font_get_stretch_by_name(p_font_data->style_name.to_lower());995p_font_data->style_flags = 0;996if ((p_font_data->face->style_flags & FT_STYLE_FLAG_BOLD) || p_font_data->weight >= 700) {997p_font_data->style_flags.set_flag(FONT_BOLD);998}999if ((p_font_data->face->style_flags & FT_STYLE_FLAG_ITALIC) || _is_ital_style(p_font_data->style_name.to_lower())) {1000p_font_data->style_flags.set_flag(FONT_ITALIC);1001}1002if (p_font_data->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) {1003p_font_data->style_flags.set_flag(FONT_FIXED_WIDTH);1004}1005// Read OpenType variations.1006p_font_data->supported_varaitions.clear();1007if (p_font_data->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {1008FT_MM_Var *amaster;1009FT_Get_MM_Var(p_font_data->face, &amaster);1010for (FT_UInt i = 0; i < amaster->num_axis; i++) {1011p_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);1012}1013FT_Done_MM_Var(ft_library, amaster);1014}1015p_font_data->face_init = true;1016}10171018#if defined(MACOS_ENABLED) || defined(IOS_ENABLED)1019if (p_font_data->font_name == ".Apple Color Emoji UI" || p_font_data->font_name == "Apple Color Emoji") {1020// The baseline offset is missing from the Apple Color Emoji UI font data, so add it manually.1021// This issue doesn't occur with other system emoji fonts.1022if (!FT_Load_Glyph(p_font_data->face, FT_Get_Char_Index(p_font_data->face, 0x1F92E), FT_LOAD_DEFAULT | FT_LOAD_COLOR)) {1023if (p_font_data->face->glyph->metrics.horiBearingY == p_font_data->face->glyph->metrics.height) {1024p_font_data->baseline_offset = 0.15;1025}1026}1027}1028#endif10291030// Write variations.1031if (p_font_data->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {1032FT_MM_Var *amaster;10331034FT_Get_MM_Var(p_font_data->face, &amaster);10351036Vector<FT_Fixed> coords;1037coords.resize(amaster->num_axis);10381039FT_Get_Var_Design_Coordinates(p_font_data->face, coords.size(), coords.ptrw());10401041for (FT_UInt i = 0; i < amaster->num_axis; i++) {1042// Reset to default.1043int32_t var_tag = amaster->axis[i].tag;1044double var_value = (double)amaster->axis[i].def / 65536.0;1045coords.write[i] = amaster->axis[i].def;10461047if (p_font_data->variation_coordinates.has(var_tag)) {1048var_value = p_font_data->variation_coordinates[var_tag];1049coords.write[i] = CLAMP(var_value * 65536.0, amaster->axis[i].minimum, amaster->axis[i].maximum);1050}10511052if (p_font_data->variation_coordinates.has(tag_to_name(var_tag))) {1053var_value = p_font_data->variation_coordinates[tag_to_name(var_tag)];1054coords.write[i] = CLAMP(var_value * 65536.0, amaster->axis[i].minimum, amaster->axis[i].maximum);1055}1056}10571058FT_Set_Var_Design_Coordinates(p_font_data->face, coords.size(), coords.ptrw());1059FT_Done_MM_Var(ft_library, amaster);1060}1061#else1062memdelete(fd);1063if (p_silent) {1064return false;1065} else {1066ERR_FAIL_V_MSG(false, "FreeType: Can't load dynamic font, engine is compiled without FreeType support!");1067}1068#endif1069}10701071fd->owner = p_font_data;1072p_font_data->cache.insert(p_size, fd);1073r_cache_for_size = fd;1074if (p_oversampling != 0) {1075OversamplingLevel *ol = oversampling_levels.getptr(p_oversampling);1076if (ol) {1077fd->viewport_oversampling = p_oversampling;1078ol->fonts.insert(fd);1079}1080}1081return true;1082}10831084void TextServerFallback::_reference_oversampling_level(double p_oversampling) {1085uint32_t oversampling = CLAMP(p_oversampling, 0.1, 100.0) * 64;1086if (oversampling == 64) {1087return;1088}1089OversamplingLevel *ol = oversampling_levels.getptr(oversampling);1090if (ol) {1091ol->refcount++;1092} else {1093OversamplingLevel new_ol;1094oversampling_levels.insert(oversampling, new_ol);1095}1096}10971098void TextServerFallback::_unreference_oversampling_level(double p_oversampling) {1099uint32_t oversampling = CLAMP(p_oversampling, 0.1, 100.0) * 64;1100if (oversampling == 64) {1101return;1102}1103OversamplingLevel *ol = oversampling_levels.getptr(oversampling);1104if (ol) {1105ol->refcount--;1106if (ol->refcount == 0) {1107for (FontForSizeFallback *fd : ol->fonts) {1108fd->owner->cache.erase(fd->size);1109memdelete(fd);1110}1111ol->fonts.clear();1112oversampling_levels.erase(oversampling);1113}1114}1115}11161117_FORCE_INLINE_ bool TextServerFallback::_font_validate(const RID &p_font_rid) const {1118FontFallback *fd = _get_font_data(p_font_rid);1119ERR_FAIL_NULL_V(fd, false);11201121MutexLock lock(fd->mutex);1122Vector2i size = _get_size(fd, 16);1123FontForSizeFallback *ffsd = nullptr;1124return _ensure_cache_for_size(fd, size, ffsd, true);1125}11261127_FORCE_INLINE_ void TextServerFallback::_font_clear_cache(FontFallback *p_font_data) {1128MutexLock ftlock(ft_mutex);11291130for (const KeyValue<Vector2i, FontForSizeFallback *> &E : p_font_data->cache) {1131if (E.value->viewport_oversampling != 0) {1132OversamplingLevel *ol = oversampling_levels.getptr(E.value->viewport_oversampling);1133if (ol) {1134ol->fonts.erase(E.value);1135}1136}1137memdelete(E.value);1138}11391140p_font_data->cache.clear();1141p_font_data->face_init = false;1142p_font_data->supported_varaitions.clear();1143}11441145RID TextServerFallback::_create_font() {1146_THREAD_SAFE_METHOD_11471148FontFallback *fd = memnew(FontFallback);11491150return font_owner.make_rid(fd);1151}11521153RID TextServerFallback::_create_font_linked_variation(const RID &p_font_rid) {1154_THREAD_SAFE_METHOD_11551156RID rid = p_font_rid;1157FontFallbackLinkedVariation *fdv = font_var_owner.get_or_null(rid);1158if (unlikely(fdv)) {1159rid = fdv->base_font;1160}1161ERR_FAIL_COND_V(!font_owner.owns(rid), RID());11621163FontFallbackLinkedVariation *new_fdv = memnew(FontFallbackLinkedVariation);1164new_fdv->base_font = rid;11651166return font_var_owner.make_rid(new_fdv);1167}11681169void TextServerFallback::_font_set_data(const RID &p_font_rid, const PackedByteArray &p_data) {1170FontFallback *fd = _get_font_data(p_font_rid);1171ERR_FAIL_NULL(fd);11721173MutexLock lock(fd->mutex);1174_font_clear_cache(fd);1175fd->data = p_data;1176fd->data_ptr = fd->data.ptr();1177fd->data_size = fd->data.size();1178}11791180void TextServerFallback::_font_set_data_ptr(const RID &p_font_rid, const uint8_t *p_data_ptr, int64_t p_data_size) {1181FontFallback *fd = _get_font_data(p_font_rid);1182ERR_FAIL_NULL(fd);11831184MutexLock lock(fd->mutex);1185_font_clear_cache(fd);1186fd->data.resize(0);1187fd->data_ptr = p_data_ptr;1188fd->data_size = p_data_size;1189}11901191void TextServerFallback::_font_set_style(const RID &p_font_rid, BitField<FontStyle> p_style) {1192FontFallback *fd = _get_font_data(p_font_rid);1193ERR_FAIL_NULL(fd);11941195MutexLock lock(fd->mutex);1196Vector2i size = _get_size(fd, 16);1197FontForSizeFallback *ffsd = nullptr;1198ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));1199fd->style_flags = p_style;1200}12011202void TextServerFallback::_font_set_face_index(const RID &p_font_rid, int64_t p_face_index) {1203ERR_FAIL_COND(p_face_index < 0);1204ERR_FAIL_COND(p_face_index >= 0x7FFF);12051206FontFallback *fd = _get_font_data(p_font_rid);1207ERR_FAIL_NULL(fd);12081209MutexLock lock(fd->mutex);1210if (fd->face_index != p_face_index) {1211fd->face_index = p_face_index;1212_font_clear_cache(fd);1213}1214}12151216int64_t TextServerFallback::_font_get_face_index(const RID &p_font_rid) const {1217FontFallback *fd = _get_font_data(p_font_rid);1218ERR_FAIL_NULL_V(fd, 0);12191220MutexLock lock(fd->mutex);1221return fd->face_index;1222}12231224int64_t TextServerFallback::_font_get_face_count(const RID &p_font_rid) const {1225FontFallback *fd = _get_font_data(p_font_rid);1226ERR_FAIL_NULL_V(fd, 0);12271228MutexLock lock(fd->mutex);1229int face_count = 0;12301231if (fd->data_ptr && (fd->data_size > 0)) {1232// Init dynamic font.1233#ifdef MODULE_FREETYPE_ENABLED1234int error = 0;1235if (!ft_library) {1236error = FT_Init_FreeType(&ft_library);1237ERR_FAIL_COND_V_MSG(error != 0, false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");1238#ifdef MODULE_SVG_ENABLED1239FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks());1240#endif1241}12421243FT_StreamRec stream;1244memset(&stream, 0, sizeof(FT_StreamRec));1245stream.base = (unsigned char *)fd->data_ptr;1246stream.size = fd->data_size;1247stream.pos = 0;12481249FT_Open_Args fargs;1250memset(&fargs, 0, sizeof(FT_Open_Args));1251fargs.memory_base = (unsigned char *)fd->data_ptr;1252fargs.memory_size = fd->data_size;1253fargs.flags = FT_OPEN_MEMORY;1254fargs.stream = &stream;12551256MutexLock ftlock(ft_mutex);12571258FT_Face tmp_face = nullptr;1259error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);1260if (error == 0) {1261face_count = tmp_face->num_faces;1262FT_Done_Face(tmp_face);1263}1264#endif1265}12661267return face_count;1268}12691270BitField<TextServer::FontStyle> TextServerFallback::_font_get_style(const RID &p_font_rid) const {1271FontFallback *fd = _get_font_data(p_font_rid);1272ERR_FAIL_NULL_V(fd, 0);12731274MutexLock lock(fd->mutex);1275Vector2i size = _get_size(fd, 16);1276FontForSizeFallback *ffsd = nullptr;1277ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0);1278return fd->style_flags;1279}12801281void TextServerFallback::_font_set_style_name(const RID &p_font_rid, const String &p_name) {1282FontFallback *fd = _get_font_data(p_font_rid);1283ERR_FAIL_NULL(fd);12841285MutexLock lock(fd->mutex);1286Vector2i size = _get_size(fd, 16);1287FontForSizeFallback *ffsd = nullptr;1288ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));1289fd->style_name = p_name;1290}12911292String TextServerFallback::_font_get_style_name(const RID &p_font_rid) const {1293FontFallback *fd = _get_font_data(p_font_rid);1294ERR_FAIL_NULL_V(fd, String());12951296MutexLock lock(fd->mutex);1297Vector2i size = _get_size(fd, 16);1298FontForSizeFallback *ffsd = nullptr;1299ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), String());1300return fd->style_name;1301}13021303void TextServerFallback::_font_set_weight(const RID &p_font_rid, int64_t p_weight) {1304FontFallback *fd = _get_font_data(p_font_rid);1305ERR_FAIL_NULL(fd);13061307MutexLock lock(fd->mutex);1308Vector2i size = _get_size(fd, 16);1309FontForSizeFallback *ffsd = nullptr;1310ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));1311fd->weight = CLAMP(p_weight, 100, 999);1312}13131314int64_t TextServerFallback::_font_get_weight(const RID &p_font_rid) const {1315FontFallback *fd = _get_font_data(p_font_rid);1316ERR_FAIL_NULL_V(fd, 400);13171318MutexLock lock(fd->mutex);1319Vector2i size = _get_size(fd, 16);1320FontForSizeFallback *ffsd = nullptr;1321ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 400);1322return fd->weight;1323}13241325void TextServerFallback::_font_set_stretch(const RID &p_font_rid, int64_t p_stretch) {1326FontFallback *fd = _get_font_data(p_font_rid);1327ERR_FAIL_NULL(fd);13281329MutexLock lock(fd->mutex);1330Vector2i size = _get_size(fd, 16);1331FontForSizeFallback *ffsd = nullptr;1332ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));1333fd->stretch = CLAMP(p_stretch, 50, 200);1334}13351336int64_t TextServerFallback::_font_get_stretch(const RID &p_font_rid) const {1337FontFallback *fd = _get_font_data(p_font_rid);1338ERR_FAIL_NULL_V(fd, 100);13391340MutexLock lock(fd->mutex);1341Vector2i size = _get_size(fd, 16);1342FontForSizeFallback *ffsd = nullptr;1343ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 100);1344return fd->stretch;1345}13461347void TextServerFallback::_font_set_name(const RID &p_font_rid, const String &p_name) {1348FontFallback *fd = _get_font_data(p_font_rid);1349ERR_FAIL_NULL(fd);13501351MutexLock lock(fd->mutex);1352Vector2i size = _get_size(fd, 16);1353FontForSizeFallback *ffsd = nullptr;1354ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));1355fd->font_name = p_name;1356}13571358String TextServerFallback::_font_get_name(const RID &p_font_rid) const {1359FontFallback *fd = _get_font_data(p_font_rid);1360ERR_FAIL_NULL_V(fd, String());13611362MutexLock lock(fd->mutex);1363Vector2i size = _get_size(fd, 16);1364FontForSizeFallback *ffsd = nullptr;1365ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), String());1366return fd->font_name;1367}13681369void TextServerFallback::_font_set_antialiasing(const RID &p_font_rid, TextServer::FontAntialiasing p_antialiasing) {1370FontFallback *fd = _get_font_data(p_font_rid);1371ERR_FAIL_NULL(fd);13721373MutexLock lock(fd->mutex);1374if (fd->antialiasing != p_antialiasing) {1375_font_clear_cache(fd);1376fd->antialiasing = p_antialiasing;1377}1378}13791380TextServer::FontAntialiasing TextServerFallback::_font_get_antialiasing(const RID &p_font_rid) const {1381FontFallback *fd = _get_font_data(p_font_rid);1382ERR_FAIL_NULL_V(fd, TextServer::FONT_ANTIALIASING_NONE);13831384MutexLock lock(fd->mutex);1385return fd->antialiasing;1386}13871388void TextServerFallback::_font_set_disable_embedded_bitmaps(const RID &p_font_rid, bool p_disable_embedded_bitmaps) {1389FontFallback *fd = _get_font_data(p_font_rid);1390ERR_FAIL_NULL(fd);13911392MutexLock lock(fd->mutex);1393if (fd->disable_embedded_bitmaps != p_disable_embedded_bitmaps) {1394_font_clear_cache(fd);1395fd->disable_embedded_bitmaps = p_disable_embedded_bitmaps;1396}1397}13981399bool TextServerFallback::_font_get_disable_embedded_bitmaps(const RID &p_font_rid) const {1400FontFallback *fd = _get_font_data(p_font_rid);1401ERR_FAIL_NULL_V(fd, false);14021403MutexLock lock(fd->mutex);1404return fd->disable_embedded_bitmaps;1405}14061407void TextServerFallback::_font_set_generate_mipmaps(const RID &p_font_rid, bool p_generate_mipmaps) {1408FontFallback *fd = _get_font_data(p_font_rid);1409ERR_FAIL_NULL(fd);14101411MutexLock lock(fd->mutex);1412if (fd->mipmaps != p_generate_mipmaps) {1413for (KeyValue<Vector2i, FontForSizeFallback *> &E : fd->cache) {1414for (int i = 0; i < E.value->textures.size(); i++) {1415E.value->textures.write[i].dirty = true;1416E.value->textures.write[i].texture = Ref<ImageTexture>();1417}1418}1419fd->mipmaps = p_generate_mipmaps;1420}1421}14221423bool TextServerFallback::_font_get_generate_mipmaps(const RID &p_font_rid) const {1424FontFallback *fd = _get_font_data(p_font_rid);1425ERR_FAIL_NULL_V(fd, false);14261427MutexLock lock(fd->mutex);1428return fd->mipmaps;1429}14301431void TextServerFallback::_font_set_multichannel_signed_distance_field(const RID &p_font_rid, bool p_msdf) {1432FontFallback *fd = _get_font_data(p_font_rid);1433ERR_FAIL_NULL(fd);14341435MutexLock lock(fd->mutex);1436if (fd->msdf != p_msdf) {1437_font_clear_cache(fd);1438fd->msdf = p_msdf;1439}1440}14411442bool TextServerFallback::_font_is_multichannel_signed_distance_field(const RID &p_font_rid) const {1443FontFallback *fd = _get_font_data(p_font_rid);1444ERR_FAIL_NULL_V(fd, false);14451446MutexLock lock(fd->mutex);1447return fd->msdf;1448}14491450void TextServerFallback::_font_set_msdf_pixel_range(const RID &p_font_rid, int64_t p_msdf_pixel_range) {1451FontFallback *fd = _get_font_data(p_font_rid);1452ERR_FAIL_NULL(fd);14531454MutexLock lock(fd->mutex);1455if (fd->msdf_range != p_msdf_pixel_range) {1456_font_clear_cache(fd);1457fd->msdf_range = p_msdf_pixel_range;1458}1459}14601461int64_t TextServerFallback::_font_get_msdf_pixel_range(const RID &p_font_rid) const {1462FontFallback *fd = _get_font_data(p_font_rid);1463ERR_FAIL_NULL_V(fd, false);14641465MutexLock lock(fd->mutex);1466return fd->msdf_range;1467}14681469void TextServerFallback::_font_set_msdf_size(const RID &p_font_rid, int64_t p_msdf_size) {1470FontFallback *fd = _get_font_data(p_font_rid);1471ERR_FAIL_NULL(fd);14721473MutexLock lock(fd->mutex);1474if (fd->msdf_source_size != p_msdf_size) {1475_font_clear_cache(fd);1476fd->msdf_source_size = p_msdf_size;1477}1478}14791480int64_t TextServerFallback::_font_get_msdf_size(const RID &p_font_rid) const {1481FontFallback *fd = _get_font_data(p_font_rid);1482ERR_FAIL_NULL_V(fd, 0);14831484MutexLock lock(fd->mutex);1485return fd->msdf_source_size;1486}14871488void TextServerFallback::_font_set_fixed_size(const RID &p_font_rid, int64_t p_fixed_size) {1489FontFallback *fd = _get_font_data(p_font_rid);1490ERR_FAIL_NULL(fd);14911492MutexLock lock(fd->mutex);1493fd->fixed_size = p_fixed_size;1494}14951496int64_t TextServerFallback::_font_get_fixed_size(const RID &p_font_rid) const {1497FontFallback *fd = _get_font_data(p_font_rid);1498ERR_FAIL_NULL_V(fd, 0);14991500MutexLock lock(fd->mutex);1501return fd->fixed_size;1502}15031504void TextServerFallback::_font_set_fixed_size_scale_mode(const RID &p_font_rid, TextServer::FixedSizeScaleMode p_fixed_size_scale_mode) {1505FontFallback *fd = _get_font_data(p_font_rid);1506ERR_FAIL_NULL(fd);15071508MutexLock lock(fd->mutex);1509fd->fixed_size_scale_mode = p_fixed_size_scale_mode;1510}15111512TextServer::FixedSizeScaleMode TextServerFallback::_font_get_fixed_size_scale_mode(const RID &p_font_rid) const {1513FontFallback *fd = _get_font_data(p_font_rid);1514ERR_FAIL_NULL_V(fd, FIXED_SIZE_SCALE_DISABLE);15151516MutexLock lock(fd->mutex);1517return fd->fixed_size_scale_mode;1518}15191520void TextServerFallback::_font_set_allow_system_fallback(const RID &p_font_rid, bool p_allow_system_fallback) {1521FontFallback *fd = _get_font_data(p_font_rid);1522ERR_FAIL_NULL(fd);15231524MutexLock lock(fd->mutex);1525fd->allow_system_fallback = p_allow_system_fallback;1526}15271528bool TextServerFallback::_font_is_allow_system_fallback(const RID &p_font_rid) const {1529FontFallback *fd = _get_font_data(p_font_rid);1530ERR_FAIL_NULL_V(fd, false);15311532MutexLock lock(fd->mutex);1533return fd->allow_system_fallback;1534}15351536void TextServerFallback::_font_set_force_autohinter(const RID &p_font_rid, bool p_force_autohinter) {1537FontFallback *fd = _get_font_data(p_font_rid);1538ERR_FAIL_NULL(fd);15391540MutexLock lock(fd->mutex);1541if (fd->force_autohinter != p_force_autohinter) {1542_font_clear_cache(fd);1543fd->force_autohinter = p_force_autohinter;1544}1545}15461547bool TextServerFallback::_font_is_force_autohinter(const RID &p_font_rid) const {1548FontFallback *fd = _get_font_data(p_font_rid);1549ERR_FAIL_NULL_V(fd, false);15501551MutexLock lock(fd->mutex);1552return fd->force_autohinter;1553}15541555void TextServerFallback::_font_set_modulate_color_glyphs(const RID &p_font_rid, bool p_modulate) {1556FontFallback *fd = _get_font_data(p_font_rid);1557ERR_FAIL_NULL(fd);15581559MutexLock lock(fd->mutex);1560if (fd->modulate_color_glyphs != p_modulate) {1561fd->modulate_color_glyphs = p_modulate;1562}1563}15641565bool TextServerFallback::_font_is_modulate_color_glyphs(const RID &p_font_rid) const {1566FontFallback *fd = _get_font_data(p_font_rid);1567ERR_FAIL_NULL_V(fd, false);15681569MutexLock lock(fd->mutex);1570return fd->modulate_color_glyphs;1571}15721573void TextServerFallback::_font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) {1574FontFallback *fd = _get_font_data(p_font_rid);1575ERR_FAIL_NULL(fd);15761577MutexLock lock(fd->mutex);1578if (fd->hinting != p_hinting) {1579_font_clear_cache(fd);1580fd->hinting = p_hinting;1581}1582}15831584TextServer::Hinting TextServerFallback::_font_get_hinting(const RID &p_font_rid) const {1585FontFallback *fd = _get_font_data(p_font_rid);1586ERR_FAIL_NULL_V(fd, HINTING_NONE);15871588MutexLock lock(fd->mutex);1589return fd->hinting;1590}15911592void TextServerFallback::_font_set_subpixel_positioning(const RID &p_font_rid, TextServer::SubpixelPositioning p_subpixel) {1593FontFallback *fd = _get_font_data(p_font_rid);1594ERR_FAIL_NULL(fd);15951596MutexLock lock(fd->mutex);1597fd->subpixel_positioning = p_subpixel;1598}15991600TextServer::SubpixelPositioning TextServerFallback::_font_get_subpixel_positioning(const RID &p_font_rid) const {1601FontFallback *fd = _get_font_data(p_font_rid);1602ERR_FAIL_NULL_V(fd, SUBPIXEL_POSITIONING_DISABLED);16031604MutexLock lock(fd->mutex);1605return fd->subpixel_positioning;1606}16071608void TextServerFallback::_font_set_keep_rounding_remainders(const RID &p_font_rid, bool p_keep_rounding_remainders) {1609FontFallback *fd = _get_font_data(p_font_rid);1610ERR_FAIL_NULL(fd);16111612MutexLock lock(fd->mutex);1613fd->keep_rounding_remainders = p_keep_rounding_remainders;1614}16151616bool TextServerFallback::_font_get_keep_rounding_remainders(const RID &p_font_rid) const {1617FontFallback *fd = _get_font_data(p_font_rid);1618ERR_FAIL_NULL_V(fd, false);16191620MutexLock lock(fd->mutex);1621return fd->keep_rounding_remainders;1622}16231624void TextServerFallback::_font_set_embolden(const RID &p_font_rid, double p_strength) {1625FontFallback *fd = _get_font_data(p_font_rid);1626ERR_FAIL_NULL(fd);16271628MutexLock lock(fd->mutex);1629if (fd->embolden != p_strength) {1630_font_clear_cache(fd);1631fd->embolden = p_strength;1632}1633}16341635double TextServerFallback::_font_get_embolden(const RID &p_font_rid) const {1636FontFallback *fd = _get_font_data(p_font_rid);1637ERR_FAIL_NULL_V(fd, 0.0);16381639MutexLock lock(fd->mutex);1640return fd->embolden;1641}16421643void TextServerFallback::_font_set_spacing(const RID &p_font_rid, SpacingType p_spacing, int64_t p_value) {1644ERR_FAIL_INDEX((int)p_spacing, 4);1645FontFallbackLinkedVariation *fdv = font_var_owner.get_or_null(p_font_rid);1646if (fdv) {1647if (fdv->extra_spacing[p_spacing] != p_value) {1648fdv->extra_spacing[p_spacing] = p_value;1649}1650} else {1651FontFallback *fd = font_owner.get_or_null(p_font_rid);1652ERR_FAIL_NULL(fd);16531654MutexLock lock(fd->mutex);1655if (fd->extra_spacing[p_spacing] != p_value) {1656_font_clear_cache(fd);1657fd->extra_spacing[p_spacing] = p_value;1658}1659}1660}16611662int64_t TextServerFallback::_font_get_spacing(const RID &p_font_rid, SpacingType p_spacing) const {1663ERR_FAIL_INDEX_V((int)p_spacing, 4, 0);1664FontFallbackLinkedVariation *fdv = font_var_owner.get_or_null(p_font_rid);1665if (fdv) {1666return fdv->extra_spacing[p_spacing];1667} else {1668FontFallback *fd = font_owner.get_or_null(p_font_rid);1669ERR_FAIL_NULL_V(fd, 0);16701671MutexLock lock(fd->mutex);1672return fd->extra_spacing[p_spacing];1673}1674}16751676void TextServerFallback::_font_set_baseline_offset(const RID &p_font_rid, double p_baseline_offset) {1677FontFallbackLinkedVariation *fdv = font_var_owner.get_or_null(p_font_rid);1678if (fdv) {1679if (fdv->baseline_offset != p_baseline_offset) {1680fdv->baseline_offset = p_baseline_offset;1681}1682} else {1683FontFallback *fd = font_owner.get_or_null(p_font_rid);1684ERR_FAIL_NULL(fd);16851686MutexLock lock(fd->mutex);1687if (fd->baseline_offset != p_baseline_offset) {1688_font_clear_cache(fd);1689fd->baseline_offset = p_baseline_offset;1690}1691}1692}16931694double TextServerFallback::_font_get_baseline_offset(const RID &p_font_rid) const {1695FontFallbackLinkedVariation *fdv = font_var_owner.get_or_null(p_font_rid);1696if (fdv) {1697return fdv->baseline_offset;1698} else {1699FontFallback *fd = font_owner.get_or_null(p_font_rid);1700ERR_FAIL_NULL_V(fd, 0.0);17011702MutexLock lock(fd->mutex);1703return fd->baseline_offset;1704}1705}17061707void TextServerFallback::_font_set_transform(const RID &p_font_rid, const Transform2D &p_transform) {1708FontFallback *fd = _get_font_data(p_font_rid);1709ERR_FAIL_NULL(fd);17101711MutexLock lock(fd->mutex);1712if (fd->transform != p_transform) {1713_font_clear_cache(fd);1714fd->transform = p_transform;1715}1716}17171718Transform2D TextServerFallback::_font_get_transform(const RID &p_font_rid) const {1719FontFallback *fd = _get_font_data(p_font_rid);1720ERR_FAIL_NULL_V(fd, Transform2D());17211722MutexLock lock(fd->mutex);1723return fd->transform;1724}17251726void TextServerFallback::_font_set_variation_coordinates(const RID &p_font_rid, const Dictionary &p_variation_coordinates) {1727FontFallback *fd = _get_font_data(p_font_rid);1728ERR_FAIL_NULL(fd);17291730MutexLock lock(fd->mutex);1731if (!fd->variation_coordinates.recursive_equal(p_variation_coordinates, 1)) {1732_font_clear_cache(fd);1733fd->variation_coordinates = p_variation_coordinates.duplicate();1734}1735}17361737double TextServerFallback::_font_get_oversampling(const RID &p_font_rid) const {1738FontFallback *fd = _get_font_data(p_font_rid);1739ERR_FAIL_NULL_V(fd, -1.0);17401741MutexLock lock(fd->mutex);1742return fd->oversampling_override;1743}17441745void TextServerFallback::_font_set_oversampling(const RID &p_font_rid, double p_oversampling) {1746FontFallback *fd = _get_font_data(p_font_rid);1747ERR_FAIL_NULL(fd);17481749MutexLock lock(fd->mutex);1750if (fd->oversampling_override != p_oversampling) {1751_font_clear_cache(fd);1752fd->oversampling_override = p_oversampling;1753}1754}17551756Dictionary TextServerFallback::_font_get_variation_coordinates(const RID &p_font_rid) const {1757FontFallback *fd = _get_font_data(p_font_rid);1758ERR_FAIL_NULL_V(fd, Dictionary());17591760MutexLock lock(fd->mutex);1761return fd->variation_coordinates;1762}17631764TypedArray<Vector2i> TextServerFallback::_font_get_size_cache_list(const RID &p_font_rid) const {1765FontFallback *fd = _get_font_data(p_font_rid);1766ERR_FAIL_NULL_V(fd, TypedArray<Vector2i>());17671768MutexLock lock(fd->mutex);1769TypedArray<Vector2i> ret;1770for (const KeyValue<Vector2i, FontForSizeFallback *> &E : fd->cache) {1771if ((E.key.x % 64 == 0) && (E.value->viewport_oversampling == 0)) {1772ret.push_back(Vector2i(E.key.x / 64, E.key.y));1773}1774}1775return ret;1776}17771778TypedArray<Dictionary> TextServerFallback::_font_get_size_cache_info(const RID &p_font_rid) const {1779FontFallback *fd = _get_font_data(p_font_rid);1780ERR_FAIL_NULL_V(fd, TypedArray<Dictionary>());17811782MutexLock lock(fd->mutex);1783TypedArray<Dictionary> ret;1784for (const KeyValue<Vector2i, FontForSizeFallback *> &E : fd->cache) {1785Dictionary size_info;1786size_info["size_px"] = Vector2i(E.key.x / 64, E.key.y);1787if (E.value->viewport_oversampling) {1788size_info["viewport_oversampling"] = double(E.value->viewport_oversampling) / 64.0;1789}1790size_info["glyphs"] = E.value->glyph_map.size();1791size_info["textures"] = E.value->textures.size();1792uint64_t sz = 0;1793for (const ShelfPackTexture &tx : E.value->textures) {1794sz += tx.image->get_data_size() * 2;1795}1796size_info["textures_size"] = sz;1797ret.push_back(size_info);1798}17991800return ret;1801}18021803void TextServerFallback::_font_clear_size_cache(const RID &p_font_rid) {1804FontFallback *fd = _get_font_data(p_font_rid);1805ERR_FAIL_NULL(fd);18061807MutexLock lock(fd->mutex);1808MutexLock ftlock(ft_mutex);1809for (const KeyValue<Vector2i, FontForSizeFallback *> &E : fd->cache) {1810if (E.value->viewport_oversampling != 0) {1811OversamplingLevel *ol = oversampling_levels.getptr(E.value->viewport_oversampling);1812if (ol) {1813ol->fonts.erase(E.value);1814}1815}1816memdelete(E.value);1817}1818fd->cache.clear();1819}18201821void TextServerFallback::_font_remove_size_cache(const RID &p_font_rid, const Vector2i &p_size) {1822FontFallback *fd = _get_font_data(p_font_rid);1823ERR_FAIL_NULL(fd);18241825MutexLock lock(fd->mutex);1826MutexLock ftlock(ft_mutex);1827Vector2i size = Vector2i(p_size.x * 64, p_size.y);1828if (fd->cache.has(size)) {1829if (fd->cache[size]->viewport_oversampling != 0) {1830OversamplingLevel *ol = oversampling_levels.getptr(fd->cache[size]->viewport_oversampling);1831if (ol) {1832ol->fonts.erase(fd->cache[size]);1833}1834}1835memdelete(fd->cache[size]);1836fd->cache.erase(size);1837}1838}18391840void TextServerFallback::_font_set_ascent(const RID &p_font_rid, int64_t p_size, double p_ascent) {1841FontFallback *fd = _get_font_data(p_font_rid);1842ERR_FAIL_NULL(fd);18431844MutexLock lock(fd->mutex);1845Vector2i size = _get_size(fd, p_size);18461847FontForSizeFallback *ffsd = nullptr;1848ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));1849ffsd->ascent = p_ascent;1850}18511852double TextServerFallback::_font_get_ascent(const RID &p_font_rid, int64_t p_size) const {1853FontFallback *fd = _get_font_data(p_font_rid);1854ERR_FAIL_NULL_V(fd, 0.0);18551856MutexLock lock(fd->mutex);1857Vector2i size = _get_size(fd, p_size);18581859FontForSizeFallback *ffsd = nullptr;1860ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0.0);18611862if (fd->msdf) {1863return ffsd->ascent * (double)p_size / (double)fd->msdf_source_size;1864} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size * 64) {1865if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {1866return ffsd->ascent * (double)p_size / (double)fd->fixed_size;1867} else {1868return ffsd->ascent * Math::round((double)p_size / (double)fd->fixed_size);1869}1870} else {1871return ffsd->ascent;1872}1873}18741875void TextServerFallback::_font_set_descent(const RID &p_font_rid, int64_t p_size, double p_descent) {1876FontFallback *fd = _get_font_data(p_font_rid);1877ERR_FAIL_NULL(fd);18781879Vector2i size = _get_size(fd, p_size);18801881FontForSizeFallback *ffsd = nullptr;1882ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));1883ffsd->descent = p_descent;1884}18851886double TextServerFallback::_font_get_descent(const RID &p_font_rid, int64_t p_size) const {1887FontFallback *fd = _get_font_data(p_font_rid);1888ERR_FAIL_NULL_V(fd, 0.0);18891890MutexLock lock(fd->mutex);1891Vector2i size = _get_size(fd, p_size);18921893FontForSizeFallback *ffsd = nullptr;1894ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0.0);18951896if (fd->msdf) {1897return ffsd->descent * (double)p_size / (double)fd->msdf_source_size;1898} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size * 64) {1899if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {1900return ffsd->descent * (double)p_size / (double)fd->fixed_size;1901} else {1902return ffsd->descent * Math::round((double)p_size / (double)fd->fixed_size);1903}1904} else {1905return ffsd->descent;1906}1907}19081909void TextServerFallback::_font_set_underline_position(const RID &p_font_rid, int64_t p_size, double p_underline_position) {1910FontFallback *fd = _get_font_data(p_font_rid);1911ERR_FAIL_NULL(fd);19121913MutexLock lock(fd->mutex);1914Vector2i size = _get_size(fd, p_size);19151916FontForSizeFallback *ffsd = nullptr;1917ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));1918ffsd->underline_position = p_underline_position;1919}19201921double TextServerFallback::_font_get_underline_position(const RID &p_font_rid, int64_t p_size) const {1922FontFallback *fd = _get_font_data(p_font_rid);1923ERR_FAIL_NULL_V(fd, 0.0);19241925MutexLock lock(fd->mutex);1926Vector2i size = _get_size(fd, p_size);19271928FontForSizeFallback *ffsd = nullptr;1929ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0.0);19301931if (fd->msdf) {1932return ffsd->underline_position * (double)p_size / (double)fd->msdf_source_size;1933} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size * 64) {1934if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {1935return ffsd->underline_position * (double)p_size / (double)fd->fixed_size;1936} else {1937return ffsd->underline_position * Math::round((double)p_size / (double)fd->fixed_size);1938}1939} else {1940return ffsd->underline_position;1941}1942}19431944void TextServerFallback::_font_set_underline_thickness(const RID &p_font_rid, int64_t p_size, double p_underline_thickness) {1945FontFallback *fd = _get_font_data(p_font_rid);1946ERR_FAIL_NULL(fd);19471948MutexLock lock(fd->mutex);1949Vector2i size = _get_size(fd, p_size);19501951FontForSizeFallback *ffsd = nullptr;1952ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));1953ffsd->underline_thickness = p_underline_thickness;1954}19551956double TextServerFallback::_font_get_underline_thickness(const RID &p_font_rid, int64_t p_size) const {1957FontFallback *fd = _get_font_data(p_font_rid);1958ERR_FAIL_NULL_V(fd, 0.0);19591960MutexLock lock(fd->mutex);1961Vector2i size = _get_size(fd, p_size);19621963FontForSizeFallback *ffsd = nullptr;1964ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0.0);19651966if (fd->msdf) {1967return ffsd->underline_thickness * (double)p_size / (double)fd->msdf_source_size;1968} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size * 64) {1969if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {1970return ffsd->underline_thickness * (double)p_size / (double)fd->fixed_size;1971} else {1972return ffsd->underline_thickness * Math::round((double)p_size / (double)fd->fixed_size);1973}1974} else {1975return ffsd->underline_thickness;1976}1977}19781979void TextServerFallback::_font_set_scale(const RID &p_font_rid, int64_t p_size, double p_scale) {1980FontFallback *fd = _get_font_data(p_font_rid);1981ERR_FAIL_NULL(fd);19821983MutexLock lock(fd->mutex);1984Vector2i size = _get_size(fd, p_size);19851986FontForSizeFallback *ffsd = nullptr;1987ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));1988#ifdef MODULE_FREETYPE_ENABLED1989if (fd->face) {1990return; // Do not override scale for dynamic fonts, it's calculated automatically.1991}1992#endif1993ffsd->scale = p_scale;1994}19951996double TextServerFallback::_font_get_scale(const RID &p_font_rid, int64_t p_size) const {1997FontFallback *fd = _get_font_data(p_font_rid);1998ERR_FAIL_NULL_V(fd, 0.0);19992000MutexLock lock(fd->mutex);2001Vector2i size = _get_size(fd, p_size);20022003FontForSizeFallback *ffsd = nullptr;2004ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0.0);20052006if (fd->msdf) {2007return ffsd->scale * (double)p_size / (double)fd->msdf_source_size;2008} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size * 64) {2009if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {2010return ffsd->scale * (double)p_size / (double)fd->fixed_size;2011} else {2012return ffsd->scale * Math::round((double)p_size / (double)fd->fixed_size);2013}2014} else {2015return ffsd->scale;2016}2017}20182019int64_t TextServerFallback::_font_get_texture_count(const RID &p_font_rid, const Vector2i &p_size) const {2020FontFallback *fd = _get_font_data(p_font_rid);2021ERR_FAIL_NULL_V(fd, 0);20222023MutexLock lock(fd->mutex);2024Vector2i size = _get_size_outline(fd, p_size);20252026FontForSizeFallback *ffsd = nullptr;2027ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0);20282029return ffsd->textures.size();2030}20312032void TextServerFallback::_font_clear_textures(const RID &p_font_rid, const Vector2i &p_size) {2033FontFallback *fd = _get_font_data(p_font_rid);2034ERR_FAIL_NULL(fd);2035MutexLock lock(fd->mutex);2036Vector2i size = _get_size_outline(fd, p_size);20372038FontForSizeFallback *ffsd = nullptr;2039ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));2040ffsd->textures.clear();2041}20422043void TextServerFallback::_font_remove_texture(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) {2044FontFallback *fd = _get_font_data(p_font_rid);2045ERR_FAIL_NULL(fd);20462047MutexLock lock(fd->mutex);2048Vector2i size = _get_size_outline(fd, p_size);2049FontForSizeFallback *ffsd = nullptr;2050ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));2051ERR_FAIL_INDEX(p_texture_index, ffsd->textures.size());20522053ffsd->textures.remove_at(p_texture_index);2054}20552056void TextServerFallback::_font_set_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const Ref<Image> &p_image) {2057FontFallback *fd = _get_font_data(p_font_rid);2058ERR_FAIL_NULL(fd);2059ERR_FAIL_COND(p_image.is_null());20602061MutexLock lock(fd->mutex);2062Vector2i size = _get_size_outline(fd, p_size);2063FontForSizeFallback *ffsd = nullptr;2064ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));2065ERR_FAIL_COND(p_texture_index < 0);2066if (p_texture_index >= ffsd->textures.size()) {2067ffsd->textures.resize(p_texture_index + 1);2068}20692070ShelfPackTexture &tex = ffsd->textures.write[p_texture_index];20712072tex.image = p_image;2073tex.texture_w = p_image->get_width();2074tex.texture_h = p_image->get_height();20752076Ref<Image> img = p_image;2077if (fd->mipmaps && !img->has_mipmaps()) {2078img = p_image->duplicate();2079img->generate_mipmaps();2080}2081tex.texture = ImageTexture::create_from_image(img);2082tex.dirty = false;2083}20842085Ref<Image> TextServerFallback::_font_get_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const {2086FontFallback *fd = _get_font_data(p_font_rid);2087ERR_FAIL_NULL_V(fd, Ref<Image>());20882089MutexLock lock(fd->mutex);2090Vector2i size = _get_size_outline(fd, p_size);2091FontForSizeFallback *ffsd = nullptr;2092ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Ref<Image>());2093ERR_FAIL_INDEX_V(p_texture_index, ffsd->textures.size(), Ref<Image>());20942095const ShelfPackTexture &tex = ffsd->textures[p_texture_index];2096return tex.image;2097}20982099void TextServerFallback::_font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offsets) {2100ERR_FAIL_COND(p_offsets.size() % 4 != 0);2101FontFallback *fd = _get_font_data(p_font_rid);2102ERR_FAIL_NULL(fd);21032104MutexLock lock(fd->mutex);2105Vector2i size = _get_size_outline(fd, p_size);2106FontForSizeFallback *ffsd = nullptr;2107ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));2108ERR_FAIL_COND(p_texture_index < 0);2109if (p_texture_index >= ffsd->textures.size()) {2110ffsd->textures.resize(p_texture_index + 1);2111}21122113ShelfPackTexture &tex = ffsd->textures.write[p_texture_index];2114tex.shelves.clear();2115for (int32_t i = 0; i < p_offsets.size(); i += 4) {2116tex.shelves.push_back(Shelf(p_offsets[i], p_offsets[i + 1], p_offsets[i + 2], p_offsets[i + 3]));2117}2118}21192120PackedInt32Array TextServerFallback::_font_get_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const {2121FontFallback *fd = _get_font_data(p_font_rid);2122ERR_FAIL_NULL_V(fd, PackedInt32Array());21232124MutexLock lock(fd->mutex);2125Vector2i size = _get_size_outline(fd, p_size);2126FontForSizeFallback *ffsd = nullptr;2127ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), PackedInt32Array());2128ERR_FAIL_INDEX_V(p_texture_index, ffsd->textures.size(), PackedInt32Array());21292130const ShelfPackTexture &tex = ffsd->textures[p_texture_index];2131PackedInt32Array ret;2132ret.resize(tex.shelves.size() * 4);21332134int32_t *wr = ret.ptrw();2135int32_t i = 0;2136for (const Shelf &E : tex.shelves) {2137wr[i * 4] = E.x;2138wr[i * 4 + 1] = E.y;2139wr[i * 4 + 2] = E.w;2140wr[i * 4 + 3] = E.h;2141i++;2142}2143return ret;2144}21452146PackedInt32Array TextServerFallback::_font_get_glyph_list(const RID &p_font_rid, const Vector2i &p_size) const {2147FontFallback *fd = _get_font_data(p_font_rid);2148ERR_FAIL_NULL_V(fd, PackedInt32Array());21492150MutexLock lock(fd->mutex);2151Vector2i size = _get_size_outline(fd, p_size);2152FontForSizeFallback *ffsd = nullptr;2153ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), PackedInt32Array());21542155PackedInt32Array ret;2156const HashMap<int32_t, FontGlyph> &gl = ffsd->glyph_map;2157for (const KeyValue<int32_t, FontGlyph> &E : gl) {2158ret.push_back(E.key);2159}2160return ret;2161}21622163void TextServerFallback::_font_clear_glyphs(const RID &p_font_rid, const Vector2i &p_size) {2164FontFallback *fd = _get_font_data(p_font_rid);2165ERR_FAIL_NULL(fd);21662167MutexLock lock(fd->mutex);2168Vector2i size = _get_size_outline(fd, p_size);2169FontForSizeFallback *ffsd = nullptr;2170ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));21712172ffsd->glyph_map.clear();2173}21742175void TextServerFallback::_font_remove_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) {2176FontFallback *fd = _get_font_data(p_font_rid);2177ERR_FAIL_NULL(fd);21782179MutexLock lock(fd->mutex);2180Vector2i size = _get_size_outline(fd, p_size);2181FontForSizeFallback *ffsd = nullptr;2182ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));21832184ffsd->glyph_map.erase(p_glyph);2185}21862187Vector2 TextServerFallback::_font_get_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph) const {2188FontFallback *fd = _get_font_data(p_font_rid);2189ERR_FAIL_NULL_V(fd, Vector2());21902191MutexLock lock(fd->mutex);2192Vector2i size = _get_size(fd, p_size);21932194FontForSizeFallback *ffsd = nullptr;2195ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Vector2());21962197int mod = 0;2198if (fd->antialiasing == FONT_ANTIALIASING_LCD) {2199TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get();2200if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {2201mod = (layout << 24);2202}2203}22042205FontGlyph fgl;2206if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) {2207return Vector2(); // Invalid or non graphicl glyph, do not display errors.2208}22092210Vector2 ea;2211if (fd->embolden != 0.0) {2212ea.x = fd->embolden * double(size.x) / 4096.0;2213}22142215double scale = _font_get_scale(p_font_rid, p_size);2216if (fd->msdf) {2217return (fgl.advance + ea) * (double)p_size / (double)fd->msdf_source_size;2218} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size * 64) {2219if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {2220return (fgl.advance + ea) * (double)p_size / (double)fd->fixed_size;2221} else {2222return (fgl.advance + ea) * Math::round((double)p_size / (double)fd->fixed_size);2223}2224} else if ((scale == 1.0) && ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_DISABLED) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x > SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE * 64))) {2225return (fgl.advance + ea).round();2226} else {2227return fgl.advance + ea;2228}2229}22302231void TextServerFallback::_font_set_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph, const Vector2 &p_advance) {2232FontFallback *fd = _get_font_data(p_font_rid);2233ERR_FAIL_NULL(fd);22342235MutexLock lock(fd->mutex);2236Vector2i size = _get_size(fd, p_size);22372238FontForSizeFallback *ffsd = nullptr;2239ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));22402241FontGlyph &fgl = ffsd->glyph_map[p_glyph];22422243fgl.advance = p_advance;2244fgl.found = true;2245}22462247Vector2 TextServerFallback::_font_get_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {2248FontFallback *fd = _get_font_data(p_font_rid);2249ERR_FAIL_NULL_V(fd, Vector2());22502251MutexLock lock(fd->mutex);2252Vector2i size = _get_size_outline(fd, p_size);22532254FontForSizeFallback *ffsd = nullptr;2255ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Vector2());22562257int mod = 0;2258if (fd->antialiasing == FONT_ANTIALIASING_LCD) {2259TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get();2260if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {2261mod = (layout << 24);2262}2263}22642265FontGlyph fgl;2266if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) {2267return Vector2(); // Invalid or non graphicl glyph, do not display errors.2268}22692270if (fd->msdf) {2271return fgl.rect.position * (double)p_size.x / (double)fd->msdf_source_size;2272} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size.x * 64) {2273if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {2274return fgl.rect.position * (double)p_size.x / (double)fd->fixed_size;2275} else {2276return fgl.rect.position * Math::round((double)p_size.x / (double)fd->fixed_size);2277}2278} else {2279return fgl.rect.position;2280}2281}22822283void TextServerFallback::_font_set_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_offset) {2284FontFallback *fd = _get_font_data(p_font_rid);2285ERR_FAIL_NULL(fd);22862287MutexLock lock(fd->mutex);2288Vector2i size = _get_size_outline(fd, p_size);22892290FontForSizeFallback *ffsd = nullptr;2291ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));22922293FontGlyph &fgl = ffsd->glyph_map[p_glyph];22942295fgl.rect.position = p_offset;2296fgl.found = true;2297}22982299Vector2 TextServerFallback::_font_get_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {2300FontFallback *fd = _get_font_data(p_font_rid);2301ERR_FAIL_NULL_V(fd, Vector2());23022303MutexLock lock(fd->mutex);2304Vector2i size = _get_size_outline(fd, p_size);23052306FontForSizeFallback *ffsd = nullptr;2307ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Vector2());23082309int mod = 0;2310if (fd->antialiasing == FONT_ANTIALIASING_LCD) {2311TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get();2312if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {2313mod = (layout << 24);2314}2315}23162317FontGlyph fgl;2318if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) {2319return Vector2(); // Invalid or non graphicl glyph, do not display errors.2320}23212322if (fd->msdf) {2323return fgl.rect.size * (double)p_size.x / (double)fd->msdf_source_size;2324} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size.x * 64) {2325if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {2326return fgl.rect.size * (double)p_size.x / (double)fd->fixed_size;2327} else {2328return fgl.rect.size * Math::round((double)p_size.x / (double)fd->fixed_size);2329}2330} else {2331return fgl.rect.size;2332}2333}23342335void TextServerFallback::_font_set_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_gl_size) {2336FontFallback *fd = _get_font_data(p_font_rid);2337ERR_FAIL_NULL(fd);23382339MutexLock lock(fd->mutex);2340Vector2i size = _get_size_outline(fd, p_size);23412342FontForSizeFallback *ffsd = nullptr;2343ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));23442345FontGlyph &fgl = ffsd->glyph_map[p_glyph];23462347fgl.rect.size = p_gl_size;2348fgl.found = true;2349}23502351Rect2 TextServerFallback::_font_get_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {2352FontFallback *fd = _get_font_data(p_font_rid);2353ERR_FAIL_NULL_V(fd, Rect2());23542355MutexLock lock(fd->mutex);2356Vector2i size = _get_size_outline(fd, p_size);23572358FontForSizeFallback *ffsd = nullptr;2359ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Rect2());23602361int mod = 0;2362if (fd->antialiasing == FONT_ANTIALIASING_LCD) {2363TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get();2364if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {2365mod = (layout << 24);2366}2367}23682369FontGlyph fgl;2370if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) {2371return Rect2(); // Invalid or non graphicl glyph, do not display errors.2372}23732374return fgl.uv_rect;2375}23762377void TextServerFallback::_font_set_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Rect2 &p_uv_rect) {2378FontFallback *fd = _get_font_data(p_font_rid);2379ERR_FAIL_NULL(fd);23802381MutexLock lock(fd->mutex);2382Vector2i size = _get_size_outline(fd, p_size);23832384FontForSizeFallback *ffsd = nullptr;2385ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));23862387FontGlyph &fgl = ffsd->glyph_map[p_glyph];23882389fgl.uv_rect = p_uv_rect;2390fgl.found = true;2391}23922393int64_t TextServerFallback::_font_get_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {2394FontFallback *fd = _get_font_data(p_font_rid);2395ERR_FAIL_NULL_V(fd, -1);23962397MutexLock lock(fd->mutex);2398Vector2i size = _get_size_outline(fd, p_size);23992400FontForSizeFallback *ffsd = nullptr;2401ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), -1);24022403int mod = 0;2404if (fd->antialiasing == FONT_ANTIALIASING_LCD) {2405TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get();2406if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {2407mod = (layout << 24);2408}2409}24102411FontGlyph fgl;2412if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) {2413return -1; // Invalid or non graphicl glyph, do not display errors.2414}24152416return fgl.texture_idx;2417}24182419void TextServerFallback::_font_set_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, int64_t p_texture_idx) {2420FontFallback *fd = _get_font_data(p_font_rid);2421ERR_FAIL_NULL(fd);24222423MutexLock lock(fd->mutex);2424Vector2i size = _get_size_outline(fd, p_size);24252426FontForSizeFallback *ffsd = nullptr;2427ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));24282429FontGlyph &fgl = ffsd->glyph_map[p_glyph];24302431fgl.texture_idx = p_texture_idx;2432fgl.found = true;2433}24342435RID TextServerFallback::_font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {2436FontFallback *fd = _get_font_data(p_font_rid);2437ERR_FAIL_NULL_V(fd, RID());24382439MutexLock lock(fd->mutex);2440Vector2i size = _get_size_outline(fd, p_size);24412442FontForSizeFallback *ffsd = nullptr;2443ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), RID());24442445int mod = 0;2446if (fd->antialiasing == FONT_ANTIALIASING_LCD) {2447TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get();2448if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {2449mod = (layout << 24);2450}2451}24522453FontGlyph fgl;2454if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) {2455return RID(); // Invalid or non graphicl glyph, do not display errors.2456}24572458ERR_FAIL_COND_V(fgl.texture_idx < -1 || fgl.texture_idx >= ffsd->textures.size(), RID());24592460if (RenderingServer::get_singleton() != nullptr) {2461if (fgl.texture_idx != -1) {2462if (ffsd->textures[fgl.texture_idx].dirty) {2463ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx];2464Ref<Image> img = tex.image;2465if (fgl.from_svg) {2466// Same as the "fix alpha border" process option when importing SVGs2467img->fix_alpha_edges();2468}2469if (fd->mipmaps && !img->has_mipmaps()) {2470img = tex.image->duplicate();2471img->generate_mipmaps();2472}2473if (tex.texture.is_null()) {2474tex.texture = ImageTexture::create_from_image(img);2475} else {2476tex.texture->update(img);2477}2478tex.dirty = false;2479}2480return ffsd->textures[fgl.texture_idx].texture->get_rid();2481}2482}24832484return RID();2485}24862487Size2 TextServerFallback::_font_get_glyph_texture_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {2488FontFallback *fd = _get_font_data(p_font_rid);2489ERR_FAIL_NULL_V(fd, Size2());24902491MutexLock lock(fd->mutex);2492Vector2i size = _get_size_outline(fd, p_size);24932494FontForSizeFallback *ffsd = nullptr;2495ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Size2());24962497int mod = 0;2498if (fd->antialiasing == FONT_ANTIALIASING_LCD) {2499TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get();2500if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {2501mod = (layout << 24);2502}2503}25042505FontGlyph fgl;2506if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) {2507return Size2(); // Invalid or non graphicl glyph, do not display errors.2508}25092510ERR_FAIL_COND_V(fgl.texture_idx < -1 || fgl.texture_idx >= ffsd->textures.size(), Size2());25112512if (RenderingServer::get_singleton() != nullptr) {2513if (fgl.texture_idx != -1) {2514if (ffsd->textures[fgl.texture_idx].dirty) {2515ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx];2516Ref<Image> img = tex.image;2517if (fgl.from_svg) {2518// Same as the "fix alpha border" process option when importing SVGs2519img->fix_alpha_edges();2520}2521if (fd->mipmaps && !img->has_mipmaps()) {2522img = tex.image->duplicate();2523img->generate_mipmaps();2524}2525if (tex.texture.is_null()) {2526tex.texture = ImageTexture::create_from_image(img);2527} else {2528tex.texture->update(img);2529}2530tex.dirty = false;2531}2532return ffsd->textures[fgl.texture_idx].texture->get_size();2533}2534}25352536return Size2();2537}25382539Dictionary TextServerFallback::_font_get_glyph_contours(const RID &p_font_rid, int64_t p_size, int64_t p_index) const {2540FontFallback *fd = _get_font_data(p_font_rid);2541ERR_FAIL_NULL_V(fd, Dictionary());25422543MutexLock lock(fd->mutex);2544Vector2i size = _get_size(fd, p_size);25452546FontForSizeFallback *ffsd = nullptr;2547ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Dictionary());25482549#ifdef MODULE_FREETYPE_ENABLED2550PackedVector3Array points;2551PackedInt32Array contours;25522553int32_t index = p_index & 0xffffff; // Remove subpixel shifts.25542555int error = FT_Load_Glyph(fd->face, FT_Get_Char_Index(fd->face, index), FT_LOAD_NO_BITMAP | (fd->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0));2556ERR_FAIL_COND_V(error, Dictionary());25572558if (fd->embolden != 0.f) {2559FT_Pos strength = fd->embolden * size.x / 16; // 26.6 fractional units (1 / 64).2560FT_Outline_Embolden(&fd->face->glyph->outline, strength);2561}25622563if (fd->transform != Transform2D()) {2564FT_Matrix mat = { FT_Fixed(fd->transform[0][0] * 65536), FT_Fixed(fd->transform[0][1] * 65536), FT_Fixed(fd->transform[1][0] * 65536), FT_Fixed(fd->transform[1][1] * 65536) }; // 16.16 fractional units (1 / 65536).2565FT_Outline_Transform(&fd->face->glyph->outline, &mat);2566}25672568double scale = (1.0 / 64.0) * ffsd->scale;2569if (fd->msdf) {2570scale = scale * (double)p_size / (double)fd->msdf_source_size;2571} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size * 64) {2572if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {2573scale = scale * (double)p_size / (double)fd->fixed_size;2574} else {2575scale = scale * Math::round((double)p_size / (double)fd->fixed_size);2576}2577}2578for (short i = 0; i < fd->face->glyph->outline.n_points; i++) {2579points.push_back(Vector3(fd->face->glyph->outline.points[i].x * scale, -fd->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fd->face->glyph->outline.tags[i])));2580}2581for (short i = 0; i < fd->face->glyph->outline.n_contours; i++) {2582contours.push_back(fd->face->glyph->outline.contours[i]);2583}2584bool orientation = (FT_Outline_Get_Orientation(&fd->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT);25852586Dictionary out;2587out["points"] = points;2588out["contours"] = contours;2589out["orientation"] = orientation;2590return out;2591#else2592return Dictionary();2593#endif2594}25952596TypedArray<Vector2i> TextServerFallback::_font_get_kerning_list(const RID &p_font_rid, int64_t p_size) const {2597FontFallback *fd = _get_font_data(p_font_rid);2598ERR_FAIL_NULL_V(fd, TypedArray<Vector2i>());25992600MutexLock lock(fd->mutex);2601Vector2i size = _get_size(fd, p_size);26022603FontForSizeFallback *ffsd = nullptr;2604ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), TypedArray<Vector2i>());26052606TypedArray<Vector2i> ret;2607for (const KeyValue<Vector2i, Vector2> &E : ffsd->kerning_map) {2608ret.push_back(E.key);2609}2610return ret;2611}26122613void TextServerFallback::_font_clear_kerning_map(const RID &p_font_rid, int64_t p_size) {2614FontFallback *fd = _get_font_data(p_font_rid);2615ERR_FAIL_NULL(fd);26162617MutexLock lock(fd->mutex);2618Vector2i size = _get_size(fd, p_size);26192620FontForSizeFallback *ffsd = nullptr;2621ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));2622ffsd->kerning_map.clear();2623}26242625void TextServerFallback::_font_remove_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) {2626FontFallback *fd = _get_font_data(p_font_rid);2627ERR_FAIL_NULL(fd);26282629MutexLock lock(fd->mutex);2630Vector2i size = _get_size(fd, p_size);26312632FontForSizeFallback *ffsd = nullptr;2633ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));2634ffsd->kerning_map.erase(p_glyph_pair);2635}26362637void TextServerFallback::_font_set_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) {2638FontFallback *fd = _get_font_data(p_font_rid);2639ERR_FAIL_NULL(fd);26402641MutexLock lock(fd->mutex);2642Vector2i size = _get_size(fd, p_size);26432644FontForSizeFallback *ffsd = nullptr;2645ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));2646ffsd->kerning_map[p_glyph_pair] = p_kerning;2647}26482649Vector2 TextServerFallback::_font_get_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) const {2650FontFallback *fd = _get_font_data(p_font_rid);2651ERR_FAIL_NULL_V(fd, Vector2());26522653MutexLock lock(fd->mutex);2654Vector2i size = _get_size(fd, p_size);26552656FontForSizeFallback *ffsd = nullptr;2657ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Vector2());26582659const HashMap<Vector2i, Vector2> &kern = ffsd->kerning_map;26602661if (kern.has(p_glyph_pair)) {2662if (fd->msdf) {2663return kern[p_glyph_pair] * (double)p_size / (double)fd->msdf_source_size;2664} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size * 64) {2665if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {2666return kern[p_glyph_pair] * (double)p_size / (double)fd->fixed_size;2667} else {2668return kern[p_glyph_pair] * Math::round((double)p_size / (double)fd->fixed_size);2669}2670} else {2671return kern[p_glyph_pair];2672}2673} else {2674#ifdef MODULE_FREETYPE_ENABLED2675if (fd->face) {2676FT_Vector delta;2677int32_t glyph_a = FT_Get_Char_Index(fd->face, p_glyph_pair.x);2678int32_t glyph_b = FT_Get_Char_Index(fd->face, p_glyph_pair.y);2679FT_Get_Kerning(fd->face, glyph_a, glyph_b, FT_KERNING_DEFAULT, &delta);2680if (fd->msdf) {2681return Vector2(delta.x, delta.y) * (double)p_size / (double)fd->msdf_source_size;2682} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size * 64) {2683if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {2684return Vector2(delta.x, delta.y) * (double)p_size / (double)fd->fixed_size;2685} else {2686return Vector2(delta.x, delta.y) * Math::round((double)p_size / (double)fd->fixed_size);2687}2688} else {2689return Vector2(delta.x, delta.y);2690}2691}2692#endif2693}2694return Vector2();2695}26962697int64_t TextServerFallback::_font_get_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_char, int64_t p_variation_selector) const {2698ERR_FAIL_COND_V_MSG((p_char >= 0xd800 && p_char <= 0xdfff) || (p_char > 0x10ffff), 0, "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_char, 16) + ".");2699return (int64_t)p_char;2700}27012702int64_t TextServerFallback::_font_get_char_from_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_glyph_index) const {2703return p_glyph_index;2704}27052706bool TextServerFallback::_font_has_char(const RID &p_font_rid, int64_t p_char) const {2707FontFallback *fd = _get_font_data(p_font_rid);2708ERR_FAIL_COND_V_MSG((p_char >= 0xd800 && p_char <= 0xdfff) || (p_char > 0x10ffff), false, "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_char, 16) + ".");2709if (!fd) {2710return false;2711}27122713MutexLock lock(fd->mutex);2714FontForSizeFallback *ffsd = nullptr;2715if (fd->cache.is_empty()) {2716ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size * 64, 0) : Vector2i(16 * 64, 0), ffsd), false);2717} else {2718ffsd = fd->cache.begin()->value;2719}27202721#ifdef MODULE_FREETYPE_ENABLED2722if (fd->face) {2723return FT_Get_Char_Index(fd->face, p_char) != 0;2724}2725#endif2726return ffsd->glyph_map.has((int32_t)p_char);2727}27282729String TextServerFallback::_font_get_supported_chars(const RID &p_font_rid) const {2730FontFallback *fd = _get_font_data(p_font_rid);2731ERR_FAIL_NULL_V(fd, String());27322733MutexLock lock(fd->mutex);2734FontForSizeFallback *ffsd = nullptr;2735if (fd->cache.is_empty()) {2736ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size * 64, 0) : Vector2i(16 * 64, 0), ffsd), String());2737} else {2738ffsd = fd->cache.begin()->value;2739}27402741String chars;2742#ifdef MODULE_FREETYPE_ENABLED2743if (fd->face) {2744FT_UInt gindex;2745FT_ULong charcode = FT_Get_First_Char(fd->face, &gindex);2746while (gindex != 0) {2747if (charcode != 0) {2748chars = chars + String::chr(charcode);2749}2750charcode = FT_Get_Next_Char(fd->face, charcode, &gindex);2751}2752return chars;2753}2754#endif2755const HashMap<int32_t, FontGlyph> &gl = ffsd->glyph_map;2756for (const KeyValue<int32_t, FontGlyph> &E : gl) {2757chars = chars + String::chr(E.key);2758}2759return chars;2760}27612762PackedInt32Array TextServerFallback::_font_get_supported_glyphs(const RID &p_font_rid) const {2763FontFallback *fd = _get_font_data(p_font_rid);2764ERR_FAIL_NULL_V(fd, PackedInt32Array());27652766MutexLock lock(fd->mutex);2767FontForSizeFallback *at_size = nullptr;2768if (fd->cache.is_empty()) {2769ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size * 64, 0) : Vector2i(16 * 64, 0), at_size), PackedInt32Array());2770} else {2771at_size = fd->cache.begin()->value;2772}27732774PackedInt32Array glyphs;2775#ifdef MODULE_FREETYPE_ENABLED2776if (fd->face) {2777FT_UInt gindex;2778FT_ULong charcode = FT_Get_First_Char(fd->face, &gindex);2779while (gindex != 0) {2780glyphs.push_back(gindex);2781charcode = FT_Get_Next_Char(fd->face, charcode, &gindex);2782}2783return glyphs;2784}2785#endif2786if (at_size) {2787const HashMap<int32_t, FontGlyph> &gl = at_size->glyph_map;2788for (const KeyValue<int32_t, FontGlyph> &E : gl) {2789glyphs.push_back(E.key);2790}2791}2792return glyphs;2793}27942795void TextServerFallback::_font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) {2796FontFallback *fd = _get_font_data(p_font_rid);2797ERR_FAIL_NULL(fd);2798ERR_FAIL_COND_MSG((p_start >= 0xd800 && p_start <= 0xdfff) || (p_start > 0x10ffff), "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_start, 16) + ".");2799ERR_FAIL_COND_MSG((p_end >= 0xd800 && p_end <= 0xdfff) || (p_end > 0x10ffff), "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_end, 16) + ".");28002801MutexLock lock(fd->mutex);2802Vector2i size = _get_size_outline(fd, p_size);2803FontForSizeFallback *ffsd = nullptr;2804ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));2805for (int64_t i = p_start; i <= p_end; i++) {2806#ifdef MODULE_FREETYPE_ENABLED2807int32_t idx = i;2808if (fd->face) {2809FontGlyph fgl;2810if (fd->msdf) {2811_ensure_glyph(fd, size, (int32_t)idx, fgl);2812} else {2813for (int aa = 0; aa < ((fd->antialiasing == FONT_ANTIALIASING_LCD) ? FONT_LCD_SUBPIXEL_LAYOUT_MAX : 1); aa++) {2814if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE * 64)) {2815_ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24), fgl);2816_ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24), fgl);2817_ensure_glyph(fd, size, (int32_t)idx | (2 << 27) | (aa << 24), fgl);2818_ensure_glyph(fd, size, (int32_t)idx | (3 << 27) | (aa << 24), fgl);2819} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE * 64)) {2820_ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24), fgl);2821_ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24), fgl);2822} else {2823_ensure_glyph(fd, size, (int32_t)idx | (aa << 24), fgl);2824}2825}2826}2827}2828#endif2829}2830}28312832void TextServerFallback::_font_render_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_index) {2833FontFallback *fd = _get_font_data(p_font_rid);2834ERR_FAIL_NULL(fd);28352836MutexLock lock(fd->mutex);2837Vector2i size = _get_size_outline(fd, p_size);2838FontForSizeFallback *ffsd = nullptr;2839ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));2840#ifdef MODULE_FREETYPE_ENABLED2841int32_t idx = p_index & 0xffffff; // Remove subpixel shifts.2842if (fd->face) {2843FontGlyph fgl;2844if (fd->msdf) {2845_ensure_glyph(fd, size, (int32_t)idx, fgl);2846} else {2847for (int aa = 0; aa < ((fd->antialiasing == FONT_ANTIALIASING_LCD) ? FONT_LCD_SUBPIXEL_LAYOUT_MAX : 1); aa++) {2848if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE * 64)) {2849_ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24), fgl);2850_ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24), fgl);2851_ensure_glyph(fd, size, (int32_t)idx | (2 << 27) | (aa << 24), fgl);2852_ensure_glyph(fd, size, (int32_t)idx | (3 << 27) | (aa << 24), fgl);2853} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE * 64)) {2854_ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24), fgl);2855_ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24), fgl);2856} else {2857_ensure_glyph(fd, size, (int32_t)idx | (aa << 24), fgl);2858}2859}2860}2861}2862#endif2863}28642865void TextServerFallback::_font_draw_glyph(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const {2866if (p_index == 0) {2867return; // Non visual character, skip.2868}2869FontFallback *fd = _get_font_data(p_font_rid);2870ERR_FAIL_NULL(fd);28712872MutexLock lock(fd->mutex);28732874// Oversampling.2875bool viewport_oversampling = false;2876float oversampling_factor = p_oversampling;2877if (p_oversampling <= 0.0) {2878if (fd->oversampling_override > 0.0) {2879oversampling_factor = fd->oversampling_override;2880} else if (vp_oversampling > 0.0) {2881oversampling_factor = vp_oversampling;2882viewport_oversampling = true;2883} else {2884oversampling_factor = 1.0;2885}2886}2887bool skip_oversampling = fd->msdf || fd->fixed_size > 0;2888if (skip_oversampling) {2889oversampling_factor = 1.0;2890} else {2891uint64_t oversampling_level = CLAMP(oversampling_factor, 0.1, 100.0) * 64;2892oversampling_factor = double(oversampling_level) / 64.0;2893}28942895Vector2i size;2896if (skip_oversampling) {2897size = _get_size(fd, p_size);2898} else {2899size = Vector2i(p_size * 64 * oversampling_factor, 0);2900}29012902FontForSizeFallback *ffsd = nullptr;2903ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd, false, viewport_oversampling ? 64 * oversampling_factor : 0));29042905int32_t index = p_index & 0xffffff; // Remove subpixel shifts.2906bool lcd_aa = false;29072908#ifdef MODULE_FREETYPE_ENABLED2909if (!fd->msdf && fd->face) {2910// LCD layout, bits 24, 25, 262911if (fd->antialiasing == FONT_ANTIALIASING_LCD) {2912TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get();2913if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {2914lcd_aa = true;2915index = index | (layout << 24);2916}2917}2918// Subpixel X-shift, bits 27, 282919if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE * 64)) {2920int xshift = (int)(Math::floor(4 * (p_pos.x + 0.125)) - 4 * Math::floor(p_pos.x + 0.125));2921index = index | (xshift << 27);2922} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE * 64)) {2923int xshift = (int)(Math::floor(2 * (p_pos.x + 0.25)) - 2 * Math::floor(p_pos.x + 0.25));2924index = index | (xshift << 27);2925}2926}2927#endif29282929FontGlyph fgl;2930if (!_ensure_glyph(fd, size, index, fgl, viewport_oversampling ? 64 * oversampling_factor : 0)) {2931return; // Invalid or non-graphical glyph, do not display errors, nothing to draw.2932}29332934if (fgl.found) {2935ERR_FAIL_COND(fgl.texture_idx < -1 || fgl.texture_idx >= ffsd->textures.size());29362937if (fgl.texture_idx != -1) {2938Color modulate = p_color;2939#ifdef MODULE_FREETYPE_ENABLED2940if (!fd->modulate_color_glyphs && fd->face && ffsd->textures[fgl.texture_idx].image.is_valid() && (ffsd->textures[fgl.texture_idx].image->get_format() == Image::FORMAT_RGBA8) && !lcd_aa && !fd->msdf) {2941modulate.r = modulate.g = modulate.b = 1.0;2942}2943#endif2944if (RenderingServer::get_singleton() != nullptr) {2945if (ffsd->textures[fgl.texture_idx].dirty) {2946ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx];2947Ref<Image> img = tex.image;2948if (fgl.from_svg) {2949// Same as the "fix alpha border" process option when importing SVGs2950img->fix_alpha_edges();2951}2952if (fd->mipmaps && !img->has_mipmaps()) {2953img = tex.image->duplicate();2954img->generate_mipmaps();2955}2956if (tex.texture.is_null()) {2957tex.texture = ImageTexture::create_from_image(img);2958} else {2959tex.texture->update(img);2960}2961tex.dirty = false;2962}2963RID texture = ffsd->textures[fgl.texture_idx].texture->get_rid();2964if (fd->msdf) {2965Point2 cpos = p_pos;2966cpos += fgl.rect.position * (double)p_size / (double)fd->msdf_source_size;2967Size2 csize = fgl.rect.size * (double)p_size / (double)fd->msdf_source_size;2968RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate, 0, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size);2969} else {2970Point2 cpos = p_pos;2971double scale = _font_get_scale(p_font_rid, p_size) / oversampling_factor;2972if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {2973cpos.x = cpos.x + 0.125;2974} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {2975cpos.x = cpos.x + 0.25;2976}2977if (scale == 1.0) {2978cpos.y = Math::floor(cpos.y);2979cpos.x = Math::floor(cpos.x);2980}2981Vector2 gpos = fgl.rect.position;2982Size2 csize = fgl.rect.size;2983if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE) {2984if (size.x != p_size * 64) {2985if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {2986double gl_scale = (double)p_size / (double)fd->fixed_size;2987gpos *= gl_scale;2988csize *= gl_scale;2989} else {2990double gl_scale = Math::round((double)p_size / (double)fd->fixed_size);2991gpos *= gl_scale;2992csize *= gl_scale;2993}2994}2995} else {2996gpos /= oversampling_factor;2997csize /= oversampling_factor;2998}2999cpos += gpos;3000if (lcd_aa) {3001RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate);3002} else {3003RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate, false, false);3004}3005}3006}3007}3008}3009}30103011void TextServerFallback::_font_draw_glyph_outline(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color, float p_oversampling) const {3012if (p_index == 0) {3013return; // Non visual character, skip.3014}3015FontFallback *fd = _get_font_data(p_font_rid);3016ERR_FAIL_NULL(fd);30173018MutexLock lock(fd->mutex);30193020// Oversampling.3021bool viewport_oversampling = false;3022float oversampling_factor = p_oversampling;3023if (p_oversampling <= 0.0) {3024if (fd->oversampling_override > 0.0) {3025oversampling_factor = fd->oversampling_override;3026} else if (vp_oversampling > 0.0) {3027oversampling_factor = vp_oversampling;3028viewport_oversampling = true;3029} else {3030oversampling_factor = 1.0;3031}3032}3033bool skip_oversampling = fd->msdf || fd->fixed_size > 0;3034if (skip_oversampling) {3035oversampling_factor = 1.0;3036} else {3037uint64_t oversampling_level = CLAMP(oversampling_factor, 0.1, 100.0) * 64;3038oversampling_factor = double(oversampling_level) / 64.0;3039}30403041Vector2i size;3042if (skip_oversampling) {3043size = _get_size_outline(fd, Vector2i(p_size, p_outline_size));3044} else {3045size = Vector2i(p_size * 64 * oversampling_factor, p_outline_size * oversampling_factor);3046}30473048FontForSizeFallback *ffsd = nullptr;3049ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd, false, viewport_oversampling ? 64 * oversampling_factor : 0));30503051int32_t index = p_index & 0xffffff; // Remove subpixel shifts.3052bool lcd_aa = false;30533054#ifdef MODULE_FREETYPE_ENABLED3055if (!fd->msdf && fd->face) {3056// LCD layout, bits 24, 25, 263057if (fd->antialiasing == FONT_ANTIALIASING_LCD) {3058TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get();3059if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {3060lcd_aa = true;3061index = index | (layout << 24);3062}3063}3064// Subpixel X-shift, bits 27, 283065if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE * 64)) {3066int xshift = (int)(Math::floor(4 * (p_pos.x + 0.125)) - 4 * Math::floor(p_pos.x + 0.125));3067index = index | (xshift << 27);3068} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE * 64)) {3069int xshift = (int)(Math::floor(2 * (p_pos.x + 0.25)) - 2 * Math::floor(p_pos.x + 0.25));3070index = index | (xshift << 27);3071}3072}3073#endif30743075FontGlyph fgl;3076if (!_ensure_glyph(fd, size, index, fgl, viewport_oversampling ? 64 * oversampling_factor : 0)) {3077return; // Invalid or non-graphical glyph, do not display errors, nothing to draw.3078}30793080if (fgl.found) {3081ERR_FAIL_COND(fgl.texture_idx < -1 || fgl.texture_idx >= ffsd->textures.size());30823083if (fgl.texture_idx != -1) {3084Color modulate = p_color;3085#ifdef MODULE_FREETYPE_ENABLED3086if (fd->face && ffsd->textures[fgl.texture_idx].image.is_valid() && (ffsd->textures[fgl.texture_idx].image->get_format() == Image::FORMAT_RGBA8) && !lcd_aa && !fd->msdf) {3087modulate.r = modulate.g = modulate.b = 1.0;3088}3089#endif3090if (RenderingServer::get_singleton() != nullptr) {3091if (ffsd->textures[fgl.texture_idx].dirty) {3092ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx];3093Ref<Image> img = tex.image;3094if (fd->mipmaps && !img->has_mipmaps()) {3095img = tex.image->duplicate();3096img->generate_mipmaps();3097}3098if (tex.texture.is_null()) {3099tex.texture = ImageTexture::create_from_image(img);3100} else {3101tex.texture->update(img);3102}3103tex.dirty = false;3104}3105RID texture = ffsd->textures[fgl.texture_idx].texture->get_rid();3106if (fd->msdf) {3107Point2 cpos = p_pos;3108cpos += fgl.rect.position * (double)p_size / (double)fd->msdf_source_size;3109Size2 csize = fgl.rect.size * (double)p_size / (double)fd->msdf_source_size;3110RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate, p_outline_size, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size);3111} else {3112Point2 cpos = p_pos;3113double scale = _font_get_scale(p_font_rid, p_size) / oversampling_factor;3114if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {3115cpos.x = cpos.x + 0.125;3116} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {3117cpos.x = cpos.x + 0.25;3118}3119if (scale == 1.0) {3120cpos.y = Math::floor(cpos.y);3121cpos.x = Math::floor(cpos.x);3122}3123Vector2 gpos = fgl.rect.position;3124Size2 csize = fgl.rect.size;3125if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE) {3126if (size.x != p_size * 64) {3127if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {3128double gl_scale = (double)p_size / (double)fd->fixed_size;3129gpos *= gl_scale;3130csize *= gl_scale;3131} else {3132double gl_scale = Math::round((double)p_size / (double)fd->fixed_size);3133gpos *= gl_scale;3134csize *= gl_scale;3135}3136}3137} else {3138gpos /= oversampling_factor;3139csize /= oversampling_factor;3140}3141cpos += gpos;3142if (lcd_aa) {3143RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate);3144} else {3145RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate, false, false);3146}3147}3148}3149}3150}3151}31523153bool TextServerFallback::_font_is_language_supported(const RID &p_font_rid, const String &p_language) const {3154FontFallback *fd = _get_font_data(p_font_rid);3155ERR_FAIL_NULL_V(fd, false);31563157MutexLock lock(fd->mutex);3158if (fd->language_support_overrides.has(p_language)) {3159return fd->language_support_overrides[p_language];3160} else {3161if (fd->language_support_overrides.has("*")) {3162return fd->language_support_overrides["*"];3163}3164return true;3165}3166}31673168void TextServerFallback::_font_set_language_support_override(const RID &p_font_rid, const String &p_language, bool p_supported) {3169FontFallback *fd = _get_font_data(p_font_rid);3170ERR_FAIL_NULL(fd);31713172MutexLock lock(fd->mutex);3173fd->language_support_overrides[p_language] = p_supported;3174}31753176bool TextServerFallback::_font_get_language_support_override(const RID &p_font_rid, const String &p_language) {3177FontFallback *fd = _get_font_data(p_font_rid);3178ERR_FAIL_NULL_V(fd, false);31793180MutexLock lock(fd->mutex);3181return fd->language_support_overrides[p_language];3182}31833184void TextServerFallback::_font_remove_language_support_override(const RID &p_font_rid, const String &p_language) {3185FontFallback *fd = _get_font_data(p_font_rid);3186ERR_FAIL_NULL(fd);31873188MutexLock lock(fd->mutex);3189fd->language_support_overrides.erase(p_language);3190}31913192PackedStringArray TextServerFallback::_font_get_language_support_overrides(const RID &p_font_rid) {3193FontFallback *fd = _get_font_data(p_font_rid);3194ERR_FAIL_NULL_V(fd, PackedStringArray());31953196MutexLock lock(fd->mutex);3197PackedStringArray out;3198for (const KeyValue<String, bool> &E : fd->language_support_overrides) {3199out.push_back(E.key);3200}3201return out;3202}32033204bool TextServerFallback::_font_is_script_supported(const RID &p_font_rid, const String &p_script) const {3205FontFallback *fd = _get_font_data(p_font_rid);3206ERR_FAIL_NULL_V(fd, false);32073208MutexLock lock(fd->mutex);3209if (fd->script_support_overrides.has(p_script)) {3210return fd->script_support_overrides[p_script];3211} else {3212if (fd->script_support_overrides.has("*")) {3213return fd->script_support_overrides["*"];3214}3215return true;3216}3217}32183219void TextServerFallback::_font_set_script_support_override(const RID &p_font_rid, const String &p_script, bool p_supported) {3220FontFallback *fd = _get_font_data(p_font_rid);3221ERR_FAIL_NULL(fd);32223223MutexLock lock(fd->mutex);3224fd->script_support_overrides[p_script] = p_supported;3225}32263227bool TextServerFallback::_font_get_script_support_override(const RID &p_font_rid, const String &p_script) {3228FontFallback *fd = _get_font_data(p_font_rid);3229ERR_FAIL_NULL_V(fd, false);32303231MutexLock lock(fd->mutex);3232return fd->script_support_overrides[p_script];3233}32343235void TextServerFallback::_font_remove_script_support_override(const RID &p_font_rid, const String &p_script) {3236FontFallback *fd = _get_font_data(p_font_rid);3237ERR_FAIL_NULL(fd);32383239MutexLock lock(fd->mutex);3240Vector2i size = _get_size(fd, 16);3241FontForSizeFallback *ffsd = nullptr;3242ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));3243fd->script_support_overrides.erase(p_script);3244}32453246PackedStringArray TextServerFallback::_font_get_script_support_overrides(const RID &p_font_rid) {3247FontFallback *fd = _get_font_data(p_font_rid);3248ERR_FAIL_NULL_V(fd, PackedStringArray());32493250MutexLock lock(fd->mutex);3251PackedStringArray out;3252for (const KeyValue<String, bool> &E : fd->script_support_overrides) {3253out.push_back(E.key);3254}3255return out;3256}32573258void TextServerFallback::_font_set_opentype_feature_overrides(const RID &p_font_rid, const Dictionary &p_overrides) {3259FontFallback *fd = _get_font_data(p_font_rid);3260ERR_FAIL_NULL(fd);32613262MutexLock lock(fd->mutex);3263Vector2i size = _get_size(fd, 16);3264FontForSizeFallback *ffsd = nullptr;3265ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));3266fd->feature_overrides = p_overrides;3267}32683269Dictionary TextServerFallback::_font_get_opentype_feature_overrides(const RID &p_font_rid) const {3270FontFallback *fd = _get_font_data(p_font_rid);3271ERR_FAIL_NULL_V(fd, Dictionary());32723273MutexLock lock(fd->mutex);3274return fd->feature_overrides;3275}32763277Dictionary TextServerFallback::_font_supported_feature_list(const RID &p_font_rid) const {3278return Dictionary();3279}32803281Dictionary TextServerFallback::_font_supported_variation_list(const RID &p_font_rid) const {3282FontFallback *fd = _get_font_data(p_font_rid);3283ERR_FAIL_NULL_V(fd, Dictionary());32843285MutexLock lock(fd->mutex);3286Vector2i size = _get_size(fd, 16);3287FontForSizeFallback *ffsd = nullptr;3288ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Dictionary());3289return fd->supported_varaitions;3290}32913292/*************************************************************************/3293/* Shaped text buffer interface */3294/*************************************************************************/32953296void TextServerFallback::invalidate(ShapedTextDataFallback *p_shaped) {3297p_shaped->valid.clear();3298p_shaped->sort_valid = false;3299p_shaped->line_breaks_valid = false;3300p_shaped->justification_ops_valid = false;3301p_shaped->ascent = 0.0;3302p_shaped->descent = 0.0;3303p_shaped->width = 0.0;3304p_shaped->upos = 0.0;3305p_shaped->uthk = 0.0;3306p_shaped->glyphs.clear();3307p_shaped->glyphs_logical.clear();3308p_shaped->runs.clear();3309p_shaped->runs_dirty = true;3310}33113312void TextServerFallback::full_copy(ShapedTextDataFallback *p_shaped) {3313ShapedTextDataFallback *parent = shaped_owner.get_or_null(p_shaped->parent);33143315for (const KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : parent->objects) {3316if (E.value.start >= p_shaped->start && E.value.start < p_shaped->end) {3317p_shaped->objects[E.key] = E.value;3318}3319}33203321for (int i = MAX(0, p_shaped->first_span); i <= MIN(p_shaped->last_span, parent->spans.size() - 1); i++) {3322ShapedTextDataFallback::Span span = parent->spans[i];3323span.start = MAX(p_shaped->start, span.start);3324span.end = MIN(p_shaped->end, span.end);3325p_shaped->spans.push_back(span);3326}3327p_shaped->first_span = 0;3328p_shaped->last_span = 0;33293330p_shaped->parent = RID();3331}33323333RID TextServerFallback::_create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {3334_THREAD_SAFE_METHOD_3335ERR_FAIL_COND_V_MSG(p_direction == DIRECTION_INHERITED, RID(), "Invalid text direction.");33363337ShapedTextDataFallback *sd = memnew(ShapedTextDataFallback);3338sd->direction = p_direction;3339sd->orientation = p_orientation;33403341return shaped_owner.make_rid(sd);3342}33433344void TextServerFallback::_shaped_text_clear(const RID &p_shaped) {3345ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3346ERR_FAIL_NULL(sd);33473348MutexLock lock(sd->mutex);3349sd->parent = RID();3350sd->start = 0;3351sd->end = 0;3352sd->text = String();3353sd->spans.clear();3354sd->first_span = 0;3355sd->last_span = 0;3356sd->objects.clear();3357invalidate(sd);3358}33593360RID TextServerFallback::_shaped_text_duplicate(const RID &p_shaped) {3361_THREAD_SAFE_METHOD_33623363const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3364ERR_FAIL_NULL_V(sd, RID());33653366MutexLock lock(sd->mutex);33673368ShapedTextDataFallback *new_sd = memnew(ShapedTextDataFallback);3369new_sd->parent = p_shaped;3370new_sd->start = sd->start;3371new_sd->end = sd->end;3372new_sd->first_span = sd->first_span;3373new_sd->last_span = sd->last_span;3374new_sd->text = sd->text;3375new_sd->orientation = sd->orientation;3376new_sd->direction = sd->direction;3377new_sd->custom_punct = sd->custom_punct;3378new_sd->para_direction = sd->para_direction;3379new_sd->line_breaks_valid = sd->line_breaks_valid;3380new_sd->justification_ops_valid = sd->justification_ops_valid;3381new_sd->sort_valid = false;3382new_sd->upos = sd->upos;3383new_sd->uthk = sd->uthk;3384new_sd->runs.clear();3385new_sd->runs_dirty = true;3386for (int i = 0; i < TextServer::SPACING_MAX; i++) {3387new_sd->extra_spacing[i] = sd->extra_spacing[i];3388}3389for (const KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : sd->objects) {3390new_sd->objects[E.key] = E.value;3391}3392for (int i = 0; i < sd->spans.size(); i++) {3393new_sd->spans.push_back(sd->spans[i]);3394}3395new_sd->valid.clear();33963397return shaped_owner.make_rid(new_sd);3398}33993400void TextServerFallback::_shaped_text_set_direction(const RID &p_shaped, TextServer::Direction p_direction) {3401ERR_FAIL_COND_MSG(p_direction == DIRECTION_INHERITED, "Invalid text direction.");3402if (p_direction == DIRECTION_RTL) {3403ERR_PRINT_ONCE("Right-to-left layout is not supported by this text server.");3404}3405}34063407TextServer::Direction TextServerFallback::_shaped_text_get_direction(const RID &p_shaped) const {3408return TextServer::DIRECTION_LTR;3409}34103411TextServer::Direction TextServerFallback::_shaped_text_get_inferred_direction(const RID &p_shaped) const {3412return TextServer::DIRECTION_LTR;3413}34143415void TextServerFallback::_shaped_text_set_custom_punctuation(const RID &p_shaped, const String &p_punct) {3416_THREAD_SAFE_METHOD_3417ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3418ERR_FAIL_NULL(sd);34193420if (sd->custom_punct != p_punct) {3421if (sd->parent != RID()) {3422full_copy(sd);3423}3424sd->custom_punct = p_punct;3425invalidate(sd);3426}3427}34283429String TextServerFallback::_shaped_text_get_custom_punctuation(const RID &p_shaped) const {3430_THREAD_SAFE_METHOD_3431const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3432ERR_FAIL_NULL_V(sd, String());3433return sd->custom_punct;3434}34353436void TextServerFallback::_shaped_text_set_custom_ellipsis(const RID &p_shaped, int64_t p_char) {3437_THREAD_SAFE_METHOD_3438ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3439ERR_FAIL_NULL(sd);3440sd->el_char = p_char;3441}34423443int64_t TextServerFallback::_shaped_text_get_custom_ellipsis(const RID &p_shaped) const {3444_THREAD_SAFE_METHOD_3445const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3446ERR_FAIL_NULL_V(sd, 0);3447return sd->el_char;3448}34493450void TextServerFallback::_shaped_text_set_orientation(const RID &p_shaped, TextServer::Orientation p_orientation) {3451ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3452ERR_FAIL_NULL(sd);34533454MutexLock lock(sd->mutex);3455if (sd->orientation != p_orientation) {3456if (sd->parent != RID()) {3457full_copy(sd);3458}3459sd->orientation = p_orientation;3460invalidate(sd);3461}3462}34633464void TextServerFallback::_shaped_text_set_bidi_override(const RID &p_shaped, const Array &p_override) {3465// No BiDi support, ignore.3466}34673468TextServer::Orientation TextServerFallback::_shaped_text_get_orientation(const RID &p_shaped) const {3469const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3470ERR_FAIL_NULL_V(sd, TextServer::ORIENTATION_HORIZONTAL);34713472MutexLock lock(sd->mutex);3473return sd->orientation;3474}34753476void TextServerFallback::_shaped_text_set_preserve_invalid(const RID &p_shaped, bool p_enabled) {3477ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);34783479MutexLock lock(sd->mutex);3480ERR_FAIL_NULL(sd);3481if (sd->preserve_invalid != p_enabled) {3482if (sd->parent != RID()) {3483full_copy(sd);3484}3485sd->preserve_invalid = p_enabled;3486invalidate(sd);3487}3488}34893490bool TextServerFallback::_shaped_text_get_preserve_invalid(const RID &p_shaped) const {3491const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3492ERR_FAIL_NULL_V(sd, false);34933494MutexLock lock(sd->mutex);3495return sd->preserve_invalid;3496}34973498void TextServerFallback::_shaped_text_set_preserve_control(const RID &p_shaped, bool p_enabled) {3499ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3500ERR_FAIL_NULL(sd);35013502MutexLock lock(sd->mutex);3503if (sd->preserve_control != p_enabled) {3504if (sd->parent != RID()) {3505full_copy(sd);3506}3507sd->preserve_control = p_enabled;3508invalidate(sd);3509}3510}35113512bool TextServerFallback::_shaped_text_get_preserve_control(const RID &p_shaped) const {3513const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3514ERR_FAIL_NULL_V(sd, false);35153516MutexLock lock(sd->mutex);3517return sd->preserve_control;3518}35193520void TextServerFallback::_shaped_text_set_spacing(const RID &p_shaped, SpacingType p_spacing, int64_t p_value) {3521ERR_FAIL_INDEX((int)p_spacing, 4);3522ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3523ERR_FAIL_NULL(sd);35243525MutexLock lock(sd->mutex);3526if (sd->extra_spacing[p_spacing] != p_value) {3527if (sd->parent != RID()) {3528full_copy(sd);3529}3530sd->extra_spacing[p_spacing] = p_value;3531invalidate(sd);3532}3533}35343535int64_t TextServerFallback::_shaped_text_get_spacing(const RID &p_shaped, SpacingType p_spacing) const {3536ERR_FAIL_INDEX_V((int)p_spacing, 4, 0);35373538const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3539ERR_FAIL_NULL_V(sd, 0);35403541MutexLock lock(sd->mutex);3542return sd->extra_spacing[p_spacing];3543}35443545int64_t TextServerFallback::_shaped_get_span_count(const RID &p_shaped) const {3546ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3547ERR_FAIL_NULL_V(sd, 0);35483549if (sd->parent != RID()) {3550return sd->last_span - sd->first_span + 1;3551} else {3552return sd->spans.size();3553}3554}35553556Variant TextServerFallback::_shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const {3557ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3558ERR_FAIL_NULL_V(sd, Variant());3559if (sd->parent != RID()) {3560ShapedTextDataFallback *parent_sd = shaped_owner.get_or_null(sd->parent);3561ERR_FAIL_COND_V(!parent_sd->valid.is_set(), Variant());3562ERR_FAIL_INDEX_V(p_index + sd->first_span, parent_sd->spans.size(), Variant());3563return parent_sd->spans[p_index + sd->first_span].meta;3564} else {3565ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant());3566return sd->spans[p_index].meta;3567}3568}35693570Variant TextServerFallback::_shaped_get_span_embedded_object(const RID &p_shaped, int64_t p_index) const {3571ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3572ERR_FAIL_NULL_V(sd, Variant());3573if (sd->parent != RID()) {3574ShapedTextDataFallback *parent_sd = shaped_owner.get_or_null(sd->parent);3575ERR_FAIL_COND_V(!parent_sd->valid.is_set(), Variant());3576ERR_FAIL_INDEX_V(p_index + sd->first_span, parent_sd->spans.size(), Variant());3577return parent_sd->spans[p_index + sd->first_span].embedded_key;3578} else {3579ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant());3580return sd->spans[p_index].embedded_key;3581}3582}35833584String TextServerFallback::_shaped_get_span_text(const RID &p_shaped, int64_t p_index) const {3585ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3586ERR_FAIL_NULL_V(sd, String());3587ShapedTextDataFallback *span_sd = sd;3588if (sd->parent.is_valid()) {3589span_sd = shaped_owner.get_or_null(sd->parent);3590ERR_FAIL_NULL_V(span_sd, String());3591}3592ERR_FAIL_INDEX_V(p_index, span_sd->spans.size(), String());3593return span_sd->text.substr(span_sd->spans[p_index].start, span_sd->spans[p_index].end - span_sd->spans[p_index].start);3594}35953596Variant TextServerFallback::_shaped_get_span_object(const RID &p_shaped, int64_t p_index) const {3597ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3598ERR_FAIL_NULL_V(sd, Variant());3599ShapedTextDataFallback *span_sd = sd;3600if (sd->parent.is_valid()) {3601span_sd = shaped_owner.get_or_null(sd->parent);3602ERR_FAIL_NULL_V(span_sd, Variant());3603}3604ERR_FAIL_INDEX_V(p_index, span_sd->spans.size(), Variant());3605return span_sd->spans[p_index].embedded_key;3606}36073608void TextServerFallback::_generate_runs(ShapedTextDataFallback *p_sd) const {3609ERR_FAIL_NULL(p_sd);3610p_sd->runs.clear();36113612ShapedTextDataFallback *span_sd = p_sd;3613if (p_sd->parent.is_valid()) {3614span_sd = shaped_owner.get_or_null(p_sd->parent);3615ERR_FAIL_NULL(span_sd);3616}36173618int sd_size = p_sd->glyphs.size();3619Glyph *sd_gl = p_sd->glyphs.ptrw();36203621int span_count = span_sd->spans.size();3622int span = -1;3623int span_start = -1;3624int span_end = -1;36253626TextRun run;3627for (int i = 0; i < sd_size; i += sd_gl[i].count) {3628const Glyph &gl = sd_gl[i];3629if (gl.start < 0 || gl.end < 0) {3630continue;3631}3632if (gl.start < span_start || gl.start >= span_end) {3633span = -1;3634span_start = -1;3635span_end = -1;3636for (int j = 0; j < span_count; j++) {3637if (gl.start >= span_sd->spans[j].start && gl.end <= span_sd->spans[j].end) {3638span = j;3639span_start = span_sd->spans[j].start;3640span_end = span_sd->spans[j].end;3641break;3642}3643}3644}3645if (run.font_rid != gl.font_rid || run.font_size != gl.font_size || run.span_index != span) {3646if (run.span_index >= 0) {3647p_sd->runs.push_back(run);3648}3649run.range = Vector2i(gl.start, gl.end);3650run.font_rid = gl.font_rid;3651run.font_size = gl.font_size;3652run.span_index = span;3653}3654run.range.x = MIN(run.range.x, gl.start);3655run.range.y = MAX(run.range.y, gl.end);3656}3657if (run.span_index >= 0) {3658p_sd->runs.push_back(run);3659}3660p_sd->runs_dirty = false;3661}36623663int64_t TextServerFallback::_shaped_get_run_count(const RID &p_shaped) const {3664ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3665ERR_FAIL_NULL_V(sd, 0);3666MutexLock lock(sd->mutex);3667if (!sd->valid.is_set()) {3668const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);3669}3670if (sd->runs_dirty) {3671_generate_runs(sd);3672}3673return sd->runs.size();3674}36753676String TextServerFallback::_shaped_get_run_text(const RID &p_shaped, int64_t p_index) const {3677ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3678ERR_FAIL_NULL_V(sd, String());3679MutexLock lock(sd->mutex);3680if (!sd->valid.is_set()) {3681const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);3682}3683if (sd->runs_dirty) {3684_generate_runs(sd);3685}3686ERR_FAIL_INDEX_V(p_index, sd->runs.size(), String());3687return sd->text.substr(sd->runs[p_index].range.x - sd->start, sd->runs[p_index].range.y - sd->runs[p_index].range.x);3688}36893690Vector2i TextServerFallback::_shaped_get_run_range(const RID &p_shaped, int64_t p_index) const {3691ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3692ERR_FAIL_NULL_V(sd, Vector2i());3693MutexLock lock(sd->mutex);3694if (!sd->valid.is_set()) {3695const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);3696}3697if (sd->runs_dirty) {3698_generate_runs(sd);3699}3700ERR_FAIL_INDEX_V(p_index, sd->runs.size(), Vector2i());3701return sd->runs[p_index].range;3702}37033704RID TextServerFallback::_shaped_get_run_font_rid(const RID &p_shaped, int64_t p_index) const {3705ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3706ERR_FAIL_NULL_V(sd, RID());3707MutexLock lock(sd->mutex);3708if (!sd->valid.is_set()) {3709const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);3710}3711if (sd->runs_dirty) {3712_generate_runs(sd);3713}3714ERR_FAIL_INDEX_V(p_index, sd->runs.size(), RID());3715return sd->runs[p_index].font_rid;3716}37173718int TextServerFallback::_shaped_get_run_font_size(const RID &p_shaped, int64_t p_index) const {3719ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3720ERR_FAIL_NULL_V(sd, 0);3721MutexLock lock(sd->mutex);3722if (!sd->valid.is_set()) {3723const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);3724}3725if (sd->runs_dirty) {3726_generate_runs(sd);3727}3728ERR_FAIL_INDEX_V(p_index, sd->runs.size(), 0);3729return sd->runs[p_index].font_size;3730}37313732String TextServerFallback::_shaped_get_run_language(const RID &p_shaped, int64_t p_index) const {3733ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3734ERR_FAIL_NULL_V(sd, String());3735MutexLock lock(sd->mutex);3736if (!sd->valid.is_set()) {3737const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);3738}3739if (sd->runs_dirty) {3740_generate_runs(sd);3741}3742ERR_FAIL_INDEX_V(p_index, sd->runs.size(), String());37433744int span_idx = sd->runs[p_index].span_index;3745ShapedTextDataFallback *span_sd = sd;3746if (sd->parent.is_valid()) {3747span_sd = shaped_owner.get_or_null(sd->parent);3748ERR_FAIL_NULL_V(span_sd, String());3749}3750ERR_FAIL_INDEX_V(span_idx, span_sd->spans.size(), String());3751return span_sd->spans[span_idx].language;3752}37533754TextServer::Direction TextServerFallback::_shaped_get_run_direction(const RID &p_shaped, int64_t p_index) const {3755ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3756ERR_FAIL_NULL_V(sd, TextServer::DIRECTION_LTR);3757MutexLock lock(sd->mutex);3758if (!sd->valid.is_set()) {3759const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);3760}3761if (sd->runs_dirty) {3762_generate_runs(sd);3763}3764ERR_FAIL_INDEX_V(p_index, sd->runs.size(), TextServer::DIRECTION_LTR);3765return TextServer::DIRECTION_LTR;3766}37673768Variant TextServerFallback::_shaped_get_run_object(const RID &p_shaped, int64_t p_index) const {3769ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3770ERR_FAIL_NULL_V(sd, Variant());3771MutexLock lock(sd->mutex);3772if (!sd->valid.is_set()) {3773const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);3774}3775if (sd->runs_dirty) {3776_generate_runs(sd);3777}3778ERR_FAIL_INDEX_V(p_index, sd->runs.size(), Variant());37793780int span_idx = sd->runs[p_index].span_index;3781ShapedTextDataFallback *span_sd = sd;3782if (sd->parent.is_valid()) {3783span_sd = shaped_owner.get_or_null(sd->parent);3784ERR_FAIL_NULL_V(span_sd, Variant());3785}3786ERR_FAIL_INDEX_V(span_idx, span_sd->spans.size(), Variant());3787return span_sd->spans[span_idx].embedded_key;3788}37893790void TextServerFallback::_shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features) {3791ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3792ERR_FAIL_NULL(sd);3793if (sd->parent != RID()) {3794full_copy(sd);3795}3796ERR_FAIL_INDEX(p_index, sd->spans.size());37973798ShapedTextDataFallback::Span &span = sd->spans.ptrw()[p_index];3799span.fonts.clear();3800// Pre-sort fonts, push fonts with the language support first.3801Array fonts_no_match;3802int font_count = p_fonts.size();3803for (int i = 0; i < font_count; i++) {3804if (_font_is_language_supported(p_fonts[i], span.language)) {3805span.fonts.push_back(p_fonts[i]);3806} else {3807fonts_no_match.push_back(p_fonts[i]);3808}3809}3810span.fonts.append_array(fonts_no_match);3811span.font_size = p_size;3812span.features = p_opentype_features;38133814sd->valid.clear();3815}38163817bool TextServerFallback::_shaped_text_add_string(const RID &p_shaped, const String &p_text, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) {3818ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3819ERR_FAIL_NULL_V(sd, false);38203821MutexLock lock(sd->mutex);3822ERR_FAIL_COND_V(p_size <= 0, false);38233824for (int i = 0; i < p_fonts.size(); i++) {3825ERR_FAIL_NULL_V(_get_font_data(p_fonts[i]), false);3826}38273828if (p_text.is_empty()) {3829return true;3830}38313832if (sd->parent != RID()) {3833full_copy(sd);3834}38353836ShapedTextDataFallback::Span span;3837span.start = sd->text.length();3838span.end = span.start + p_text.length();38393840// Pre-sort fonts, push fonts with the language support first.3841Array fonts_no_match;3842int font_count = p_fonts.size();3843if (font_count > 0) {3844span.fonts.push_back(p_fonts[0]);3845}3846for (int i = 1; i < font_count; i++) {3847if (_font_is_language_supported(p_fonts[i], p_language)) {3848span.fonts.push_back(p_fonts[i]);3849} else {3850fonts_no_match.push_back(p_fonts[i]);3851}3852}3853span.fonts.append_array(fonts_no_match);38543855ERR_FAIL_COND_V(span.fonts.is_empty(), false);3856span.font_size = p_size;3857span.language = p_language;3858span.meta = p_meta;38593860sd->spans.push_back(span);3861sd->text = sd->text + p_text;3862sd->end += p_text.length();3863invalidate(sd);38643865return true;3866}38673868bool TextServerFallback::_shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, int64_t p_length, double p_baseline) {3869ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3870ERR_FAIL_NULL_V(sd, false);38713872MutexLock lock(sd->mutex);3873ERR_FAIL_COND_V(p_key == Variant(), false);3874ERR_FAIL_COND_V(sd->objects.has(p_key), false);38753876if (sd->parent != RID()) {3877full_copy(sd);3878}38793880ShapedTextDataFallback::Span span;3881span.start = sd->start + sd->text.length();3882span.end = span.start + p_length;3883span.embedded_key = p_key;38843885ShapedTextDataFallback::EmbeddedObject obj;3886obj.inline_align = p_inline_align;3887obj.rect.size = p_size;3888obj.start = span.start;3889obj.end = span.end;3890obj.baseline = p_baseline;38913892sd->spans.push_back(span);3893sd->text = sd->text + String::chr(0xfffc).repeat(p_length);3894sd->end += p_length;3895sd->objects[p_key] = obj;3896invalidate(sd);38973898return true;3899}39003901String TextServerFallback::_shaped_get_text(const RID &p_shaped) const {3902const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3903ERR_FAIL_NULL_V(sd, String());39043905return sd->text;3906}39073908bool TextServerFallback::_shaped_text_has_object(const RID &p_shaped, const Variant &p_key) const {3909ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3910ERR_FAIL_NULL_V(sd, false);39113912MutexLock lock(sd->mutex);3913return sd->objects.has(p_key);3914}39153916bool TextServerFallback::_shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, double p_baseline) {3917ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3918ERR_FAIL_NULL_V(sd, false);39193920MutexLock lock(sd->mutex);3921ERR_FAIL_COND_V(!sd->objects.has(p_key), false);3922sd->objects[p_key].rect.size = p_size;3923sd->objects[p_key].inline_align = p_inline_align;3924sd->objects[p_key].baseline = p_baseline;3925if (sd->valid.is_set()) {3926// Recalc string metrics.3927sd->ascent = 0;3928sd->descent = 0;3929sd->width = 0;3930sd->upos = 0;3931sd->uthk = 0;39323933Vector<ShapedTextDataFallback::Span> &spans = sd->spans;3934if (sd->parent != RID()) {3935ShapedTextDataFallback *parent_sd = shaped_owner.get_or_null(sd->parent);3936ERR_FAIL_COND_V(!parent_sd->valid.is_set(), false);3937spans = parent_sd->spans;3938}39393940int sd_size = sd->glyphs.size();3941int span_size = spans.size();39423943for (int i = 0; i < sd_size; i++) {3944Glyph gl = sd->glyphs[i];3945Variant key;3946if ((gl.flags & GRAPHEME_IS_EMBEDDED_OBJECT) == GRAPHEME_IS_EMBEDDED_OBJECT && gl.span_index + sd->first_span >= 0 && gl.span_index + sd->first_span < span_size) {3947key = spans[gl.span_index + sd->first_span].embedded_key;3948}3949if (key != Variant()) {3950if (sd->orientation == ORIENTATION_HORIZONTAL) {3951sd->objects[key].rect.position.x = sd->width;3952sd->width += sd->objects[key].rect.size.x;3953sd->glyphs.write[i].advance = sd->objects[key].rect.size.x;3954} else {3955sd->objects[key].rect.position.y = sd->width;3956sd->width += sd->objects[key].rect.size.y;3957sd->glyphs.write[i].advance = sd->objects[key].rect.size.y;3958}3959} else {3960if (gl.font_rid.is_valid()) {3961if (sd->orientation == ORIENTATION_HORIZONTAL) {3962sd->ascent = MAX(sd->ascent, _font_get_ascent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_TOP));3963sd->descent = MAX(sd->descent, _font_get_descent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_BOTTOM));3964} else {3965sd->ascent = MAX(sd->ascent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));3966sd->descent = MAX(sd->descent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));3967}3968sd->upos = MAX(sd->upos, _font_get_underline_position(gl.font_rid, gl.font_size));3969sd->uthk = MAX(sd->uthk, _font_get_underline_thickness(gl.font_rid, gl.font_size));3970} else if (sd->preserve_invalid || (sd->preserve_control && is_control(gl.index))) {3971// Glyph not found, replace with hex code box.3972if (sd->orientation == ORIENTATION_HORIZONTAL) {3973sd->ascent = MAX(sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.85);3974sd->descent = MAX(sd->descent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.15);3975} else {3976sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5));3977sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5));3978}3979}3980sd->width += gl.advance * gl.repeat;3981}3982}3983_realign(sd);3984}3985return true;3986}39873988void TextServerFallback::_realign(ShapedTextDataFallback *p_sd) const {3989// Align embedded objects to baseline.3990double full_ascent = p_sd->ascent;3991double full_descent = p_sd->descent;3992for (KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : p_sd->objects) {3993if ((E.value.start >= p_sd->start) && (E.value.start < p_sd->end)) {3994if (p_sd->orientation == ORIENTATION_HORIZONTAL) {3995switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {3996case INLINE_ALIGNMENT_TO_TOP: {3997E.value.rect.position.y = -p_sd->ascent;3998} break;3999case INLINE_ALIGNMENT_TO_CENTER: {4000E.value.rect.position.y = (-p_sd->ascent + p_sd->descent) / 2;4001} break;4002case INLINE_ALIGNMENT_TO_BASELINE: {4003E.value.rect.position.y = 0;4004} break;4005case INLINE_ALIGNMENT_TO_BOTTOM: {4006E.value.rect.position.y = p_sd->descent;4007} break;4008}4009switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {4010case INLINE_ALIGNMENT_BOTTOM_TO: {4011E.value.rect.position.y -= E.value.rect.size.y;4012} break;4013case INLINE_ALIGNMENT_CENTER_TO: {4014E.value.rect.position.y -= E.value.rect.size.y / 2;4015} break;4016case INLINE_ALIGNMENT_BASELINE_TO: {4017E.value.rect.position.y -= E.value.baseline;4018} break;4019case INLINE_ALIGNMENT_TOP_TO: {4020// NOP4021} break;4022}4023full_ascent = MAX(full_ascent, -E.value.rect.position.y);4024full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);4025} else {4026switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {4027case INLINE_ALIGNMENT_TO_TOP: {4028E.value.rect.position.x = -p_sd->ascent;4029} break;4030case INLINE_ALIGNMENT_TO_CENTER: {4031E.value.rect.position.x = (-p_sd->ascent + p_sd->descent) / 2;4032} break;4033case INLINE_ALIGNMENT_TO_BASELINE: {4034E.value.rect.position.x = 0;4035} break;4036case INLINE_ALIGNMENT_TO_BOTTOM: {4037E.value.rect.position.x = p_sd->descent;4038} break;4039}4040switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {4041case INLINE_ALIGNMENT_BOTTOM_TO: {4042E.value.rect.position.x -= E.value.rect.size.x;4043} break;4044case INLINE_ALIGNMENT_CENTER_TO: {4045E.value.rect.position.x -= E.value.rect.size.x / 2;4046} break;4047case INLINE_ALIGNMENT_BASELINE_TO: {4048E.value.rect.position.x -= E.value.baseline;4049} break;4050case INLINE_ALIGNMENT_TOP_TO: {4051// NOP4052} break;4053}4054full_ascent = MAX(full_ascent, -E.value.rect.position.x);4055full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);4056}4057}4058}4059p_sd->ascent = full_ascent;4060p_sd->descent = full_descent;4061}40624063RID TextServerFallback::_shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const {4064_THREAD_SAFE_METHOD_40654066const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4067ERR_FAIL_NULL_V(sd, RID());40684069MutexLock lock(sd->mutex);4070if (sd->parent != RID()) {4071return _shaped_text_substr(sd->parent, p_start, p_length);4072}4073if (!sd->valid.is_set()) {4074const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);4075}4076ERR_FAIL_COND_V(p_start < 0 || p_length < 0, RID());4077ERR_FAIL_COND_V(sd->start > p_start || sd->end < p_start, RID());4078ERR_FAIL_COND_V(sd->end < p_start + p_length, RID());40794080ShapedTextDataFallback *new_sd = memnew(ShapedTextDataFallback);4081new_sd->parent = p_shaped;4082new_sd->start = p_start;4083new_sd->end = p_start + p_length;40844085new_sd->orientation = sd->orientation;4086new_sd->direction = sd->direction;4087new_sd->custom_punct = sd->custom_punct;4088new_sd->para_direction = sd->para_direction;4089new_sd->line_breaks_valid = sd->line_breaks_valid;4090new_sd->justification_ops_valid = sd->justification_ops_valid;4091new_sd->sort_valid = false;4092new_sd->upos = sd->upos;4093new_sd->uthk = sd->uthk;4094for (int i = 0; i < TextServer::SPACING_MAX; i++) {4095new_sd->extra_spacing[i] = sd->extra_spacing[i];4096}40974098if (p_length > 0) {4099new_sd->text = sd->text.substr(p_start - sd->start, p_length);41004101int span_size = sd->spans.size();41024103new_sd->first_span = 0;4104new_sd->last_span = span_size - 1;4105for (int i = 0; i < span_size; i++) {4106const ShapedTextDataFallback::Span &span = sd->spans[i];4107if (span.end <= p_start) {4108new_sd->first_span = i + 1;4109} else if (span.start >= p_start + p_length) {4110new_sd->last_span = i - 1;4111break;4112}4113}41144115int sd_size = sd->glyphs.size();4116const Glyph *sd_glyphs = sd->glyphs.ptr();41174118for (int i = 0; i < sd_size; i++) {4119if ((sd_glyphs[i].start >= new_sd->start) && (sd_glyphs[i].end <= new_sd->end)) {4120Glyph gl = sd_glyphs[i];4121if (gl.span_index >= 0) {4122gl.span_index -= new_sd->first_span;4123}4124if (gl.end == p_start + p_length && ((gl.flags & GRAPHEME_IS_SOFT_HYPHEN) == GRAPHEME_IS_SOFT_HYPHEN)) {4125gl.index = 0x00ad;4126gl.advance = font_get_glyph_advance(gl.font_rid, gl.font_size, 0x00ad).x;4127}4128if ((gl.flags & GRAPHEME_IS_EMBEDDED_OBJECT) == GRAPHEME_IS_EMBEDDED_OBJECT && gl.span_index + new_sd->first_span >= 0 && gl.span_index + new_sd->first_span < span_size) {4129Variant key = sd->spans[gl.span_index + new_sd->first_span].embedded_key;4130if (key != Variant()) {4131ShapedTextDataFallback::EmbeddedObject obj = sd->objects[key];4132if (new_sd->orientation == ORIENTATION_HORIZONTAL) {4133obj.rect.position.x = new_sd->width;4134new_sd->width += obj.rect.size.x;4135} else {4136obj.rect.position.y = new_sd->width;4137new_sd->width += obj.rect.size.y;4138}4139new_sd->objects[key] = obj;4140}4141} else {4142if (gl.font_rid.is_valid()) {4143if (new_sd->orientation == ORIENTATION_HORIZONTAL) {4144new_sd->ascent = MAX(new_sd->ascent, _font_get_ascent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_TOP));4145new_sd->descent = MAX(new_sd->descent, _font_get_descent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_BOTTOM));4146} else {4147new_sd->ascent = MAX(new_sd->ascent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));4148new_sd->descent = MAX(new_sd->descent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));4149}4150} else if (new_sd->preserve_invalid || (new_sd->preserve_control && is_control(gl.index))) {4151// Glyph not found, replace with hex code box.4152if (new_sd->orientation == ORIENTATION_HORIZONTAL) {4153new_sd->ascent = MAX(new_sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.85);4154new_sd->descent = MAX(new_sd->descent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.15);4155} else {4156new_sd->ascent = MAX(new_sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5));4157new_sd->descent = MAX(new_sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5));4158}4159}4160if (new_sd->glyphs.is_empty() && gl.x_off < 0.0) {4161gl.advance += -gl.x_off;4162gl.x_off = 0.0;4163}4164new_sd->width += gl.advance * gl.repeat;4165}4166new_sd->glyphs.push_back(gl);4167}4168}41694170_realign(new_sd);4171}4172new_sd->valid.set();41734174return shaped_owner.make_rid(new_sd);4175}41764177RID TextServerFallback::_shaped_text_get_parent(const RID &p_shaped) const {4178ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4179ERR_FAIL_NULL_V(sd, RID());41804181MutexLock lock(sd->mutex);4182return sd->parent;4183}41844185double TextServerFallback::_shaped_text_fit_to_width(const RID &p_shaped, double p_width, BitField<JustificationFlag> p_jst_flags) {4186ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4187ERR_FAIL_NULL_V(sd, 0.0);41884189MutexLock lock(sd->mutex);4190if (!sd->valid.is_set()) {4191_shaped_text_shape(p_shaped);4192}4193if (!sd->justification_ops_valid) {4194_shaped_text_update_justification_ops(p_shaped);4195}41964197int start_pos = 0;4198int end_pos = sd->glyphs.size() - 1;41994200if (p_jst_flags.has_flag(JUSTIFICATION_AFTER_LAST_TAB)) {4201int start, end, delta;4202if (sd->para_direction == DIRECTION_LTR) {4203start = sd->glyphs.size() - 1;4204end = -1;4205delta = -1;4206} else {4207start = 0;4208end = sd->glyphs.size();4209delta = +1;4210}42114212for (int i = start; i != end; i += delta) {4213if ((sd->glyphs[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) {4214if (sd->para_direction == DIRECTION_LTR) {4215start_pos = i;4216break;4217} else {4218end_pos = i;4219break;4220}4221}4222}4223}42244225double justification_width;4226if (p_jst_flags.has_flag(JUSTIFICATION_CONSTRAIN_ELLIPSIS)) {4227if (sd->overrun_trim_data.trim_pos >= 0) {4228end_pos = sd->overrun_trim_data.trim_pos;4229justification_width = sd->width_trimmed;4230} else {4231return Math::ceil(sd->width);4232}4233} else {4234justification_width = sd->width;4235}42364237if (p_jst_flags.has_flag(JUSTIFICATION_TRIM_EDGE_SPACES)) {4238// Trim spaces.4239while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((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)) {4240justification_width -= sd->glyphs[start_pos].advance * sd->glyphs[start_pos].repeat;4241sd->glyphs.write[start_pos].advance = 0;4242start_pos += sd->glyphs[start_pos].count;4243}4244while ((start_pos < end_pos) && ((sd->glyphs[end_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((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)) {4245justification_width -= sd->glyphs[end_pos].advance * sd->glyphs[end_pos].repeat;4246sd->glyphs.write[end_pos].advance = 0;4247end_pos -= sd->glyphs[end_pos].count;4248}4249} else {4250// Skip breaks, but do not reset size.4251while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((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)) {4252start_pos += sd->glyphs[start_pos].count;4253}4254while ((start_pos < end_pos) && ((sd->glyphs[end_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((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)) {4255end_pos -= sd->glyphs[end_pos].count;4256}4257}42584259int space_count = 0;4260for (int i = start_pos; i <= end_pos; i++) {4261const Glyph &gl = sd->glyphs[i];4262if (gl.count > 0) {4263if ((gl.flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN && (gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE && (gl.flags & GRAPHEME_IS_PUNCTUATION) != GRAPHEME_IS_PUNCTUATION) {4264space_count++;4265}4266}4267}42684269if ((space_count > 0) && p_jst_flags.has_flag(JUSTIFICATION_WORD_BOUND)) {4270double delta_width_per_space = (p_width - justification_width) / space_count;4271for (int i = start_pos; i <= end_pos; i++) {4272Glyph &gl = sd->glyphs.write[i];4273if (gl.count > 0) {4274if ((gl.flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN && (gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE && (gl.flags & GRAPHEME_IS_PUNCTUATION) != GRAPHEME_IS_PUNCTUATION) {4275double old_adv = gl.advance;4276gl.advance = MAX(gl.advance + delta_width_per_space, Math::round(0.1 * gl.font_size));4277justification_width += (gl.advance - old_adv);4278}4279}4280}4281}42824283if (Math::floor(p_width) < Math::floor(justification_width)) {4284sd->fit_width_minimum_reached = true;4285}42864287if (!p_jst_flags.has_flag(JUSTIFICATION_CONSTRAIN_ELLIPSIS)) {4288sd->width = justification_width;4289}42904291return Math::ceil(justification_width);4292}42934294double TextServerFallback::_shaped_text_tab_align(const RID &p_shaped, const PackedFloat32Array &p_tab_stops) {4295ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4296ERR_FAIL_NULL_V(sd, 0.0);42974298MutexLock lock(sd->mutex);4299if (!sd->valid.is_set()) {4300_shaped_text_shape(p_shaped);4301}4302if (!sd->line_breaks_valid) {4303_shaped_text_update_breaks(p_shaped);4304}43054306for (int i = 0; i < p_tab_stops.size(); i++) {4307if (p_tab_stops[i] <= 0) {4308return 0.0;4309}4310}43114312int tab_index = 0;4313double off = 0.0;43144315int start, end, delta;4316if (sd->para_direction == DIRECTION_LTR) {4317start = 0;4318end = sd->glyphs.size();4319delta = +1;4320} else {4321start = sd->glyphs.size() - 1;4322end = -1;4323delta = -1;4324}43254326Glyph *gl = sd->glyphs.ptrw();43274328for (int i = start; i != end; i += delta) {4329if ((gl[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) {4330double tab_off = 0.0;4331while (tab_off <= off) {4332tab_off += p_tab_stops[tab_index];4333tab_index++;4334if (tab_index >= p_tab_stops.size()) {4335tab_index = 0;4336}4337}4338double old_adv = gl[i].advance;4339gl[i].advance = tab_off - off;4340sd->width += gl[i].advance - old_adv;4341off = 0;4342continue;4343}4344off += gl[i].advance * gl[i].repeat;4345}43464347return 0.0;4348}43494350bool TextServerFallback::_shaped_text_update_breaks(const RID &p_shaped) {4351ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4352ERR_FAIL_NULL_V(sd, false);43534354MutexLock lock(sd->mutex);4355if (!sd->valid.is_set()) {4356_shaped_text_shape(p_shaped);4357}43584359if (sd->line_breaks_valid) {4360return true; // Nothing to do.4361}43624363int sd_size = sd->glyphs.size();4364Glyph *sd_glyphs = sd->glyphs.ptrw();43654366int c_punct_size = sd->custom_punct.length();4367const char32_t *c_punct = sd->custom_punct.ptr();43684369for (int i = 0; i < sd_size; i++) {4370if (sd_glyphs[i].count > 0) {4371char32_t c = sd->text[sd_glyphs[i].start - sd->start];4372char32_t c_next = i < sd_size ? sd->text[sd_glyphs[i].start - sd->start + 1] : 0x0000;4373if (c_punct_size == 0) {4374if (is_punct(c) && c != 0x005F && c != ' ') {4375sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION;4376}4377} else {4378for (int j = 0; j < c_punct_size; j++) {4379if (c_punct[j] == c) {4380sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION;4381break;4382}4383}4384}4385if (is_underscore(c)) {4386sd->glyphs.write[i].flags |= GRAPHEME_IS_UNDERSCORE;4387}4388if (is_whitespace(c) && !is_linebreak(c)) {4389sd_glyphs[i].flags |= GRAPHEME_IS_SPACE;4390if (c != 0x00A0 && c != 0x202F && c != 0x2060 && c != 0x2007) { // Skip for non-breaking space variants.4391sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_SOFT;4392}4393}4394if (is_linebreak(c)) {4395sd_glyphs[i].flags |= GRAPHEME_IS_SPACE;4396if (c != 0x000D || c_next != 0x000A) { // Skip first hard break in CR-LF pair.4397sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD;4398}4399}4400if (c == 0x0009 || c == 0x000b) {4401sd_glyphs[i].flags |= GRAPHEME_IS_TAB;4402}4403if (c == 0x00ad) {4404sd_glyphs[i].flags |= GRAPHEME_IS_SOFT_HYPHEN;4405}44064407i += (sd_glyphs[i].count - 1);4408}4409}4410sd->line_breaks_valid = true;4411return sd->line_breaks_valid;4412}44134414bool TextServerFallback::_shaped_text_update_justification_ops(const RID &p_shaped) {4415ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4416ERR_FAIL_NULL_V(sd, false);44174418MutexLock lock(sd->mutex);4419if (!sd->valid.is_set()) {4420_shaped_text_shape(p_shaped);4421}4422if (!sd->line_breaks_valid) {4423_shaped_text_update_breaks(p_shaped);4424}44254426sd->justification_ops_valid = true; // Not supported by fallback server.4427return true;4428}44294430RID TextServerFallback::_find_sys_font_for_text(const RID &p_fdef, const String &p_script_code, const String &p_language, const String &p_text) {4431RID f;4432// Try system fallback.4433if (_font_is_allow_system_fallback(p_fdef)) {4434String font_name = _font_get_name(p_fdef);4435BitField<FontStyle> font_style = _font_get_style(p_fdef);4436int font_weight = _font_get_weight(p_fdef);4437int font_stretch = _font_get_stretch(p_fdef);4438Dictionary dvar = _font_get_variation_coordinates(p_fdef);4439static int64_t wgth_tag = name_to_tag("weight");4440static int64_t wdth_tag = name_to_tag("width");4441static int64_t ital_tag = name_to_tag("italic");4442if (dvar.has(wgth_tag)) {4443font_weight = dvar[wgth_tag].operator int();4444}4445if (dvar.has(wdth_tag)) {4446font_stretch = dvar[wdth_tag].operator int();4447}4448if (dvar.has(ital_tag) && dvar[ital_tag].operator int() == 1) {4449font_style.set_flag(TextServer::FONT_ITALIC);4450}44514452String locale = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language;4453PackedStringArray fallback_font_name = OS::get_singleton()->get_system_font_path_for_text(font_name, p_text, locale, p_script_code, font_weight, font_stretch, font_style & TextServer::FONT_ITALIC);4454#ifdef GDEXTENSION4455for (int fb = 0; fb < fallback_font_name.size(); fb++) {4456const String &E = fallback_font_name[fb];4457#elif defined(GODOT_MODULE)4458for (const String &E : fallback_font_name) {4459#endif4460SystemFontKey key = SystemFontKey(E, font_style & TextServer::FONT_ITALIC, font_weight, font_stretch, p_fdef, this);4461if (system_fonts.has(key)) {4462const SystemFontCache &sysf_cache = system_fonts[key];4463int best_score = 0;4464int best_match = -1;4465for (int face_idx = 0; face_idx < sysf_cache.var.size(); face_idx++) {4466const SystemFontCacheRec &F = sysf_cache.var[face_idx];4467if (unlikely(!_font_has_char(F.rid, p_text[0]))) {4468continue;4469}4470BitField<FontStyle> style = _font_get_style(F.rid);4471int weight = _font_get_weight(F.rid);4472int stretch = _font_get_stretch(F.rid);4473int score = (20 - Math::abs(weight - font_weight) / 50);4474score += (20 - Math::abs(stretch - font_stretch) / 10);4475if (bool(style & TextServer::FONT_ITALIC) == bool(font_style & TextServer::FONT_ITALIC)) {4476score += 30;4477}4478if (score >= best_score) {4479best_score = score;4480best_match = face_idx;4481}4482if (best_score == 70) {4483break;4484}4485}4486if (best_match != -1) {4487f = sysf_cache.var[best_match].rid;4488}4489}4490if (!f.is_valid()) {4491if (system_fonts.has(key)) {4492const SystemFontCache &sysf_cache = system_fonts[key];4493if (sysf_cache.max_var == sysf_cache.var.size()) {4494// All subfonts already tested, skip.4495continue;4496}4497}44984499if (!system_font_data.has(E)) {4500system_font_data[E] = FileAccess::get_file_as_bytes(E);4501}45024503const PackedByteArray &font_data = system_font_data[E];45044505SystemFontCacheRec sysf;4506sysf.rid = _create_font();4507_font_set_data_ptr(sysf.rid, font_data.ptr(), font_data.size());4508if (!_font_validate(sysf.rid)) {4509_free_rid(sysf.rid);4510continue;4511}45124513Dictionary var = dvar;4514// Select matching style from collection.4515int best_score = 0;4516int best_match = -1;4517for (int face_idx = 0; face_idx < _font_get_face_count(sysf.rid); face_idx++) {4518_font_set_face_index(sysf.rid, face_idx);4519if (unlikely(!_font_has_char(sysf.rid, p_text[0]))) {4520continue;4521}4522BitField<FontStyle> style = _font_get_style(sysf.rid);4523int weight = _font_get_weight(sysf.rid);4524int stretch = _font_get_stretch(sysf.rid);4525int score = (20 - Math::abs(weight - font_weight) / 50);4526score += (20 - Math::abs(stretch - font_stretch) / 10);4527if (bool(style & TextServer::FONT_ITALIC) == bool(font_style & TextServer::FONT_ITALIC)) {4528score += 30;4529}4530if (score >= best_score) {4531best_score = score;4532best_match = face_idx;4533}4534if (best_score == 70) {4535break;4536}4537}4538if (best_match == -1) {4539_free_rid(sysf.rid);4540continue;4541} else {4542_font_set_face_index(sysf.rid, best_match);4543}4544sysf.index = best_match;45454546// If it's a variable font, apply weight, stretch and italic coordinates to match requested style.4547if (best_score != 70) {4548Dictionary ftr = _font_supported_variation_list(sysf.rid);4549if (ftr.has(wdth_tag)) {4550var[wdth_tag] = font_stretch;4551_font_set_stretch(sysf.rid, font_stretch);4552}4553if (ftr.has(wgth_tag)) {4554var[wgth_tag] = font_weight;4555_font_set_weight(sysf.rid, font_weight);4556}4557if ((font_style & TextServer::FONT_ITALIC) && ftr.has(ital_tag)) {4558var[ital_tag] = 1;4559_font_set_style(sysf.rid, _font_get_style(sysf.rid) | TextServer::FONT_ITALIC);4560}4561}45624563bool fb_use_msdf = key.msdf;4564#ifdef MODULE_FREETYPE_ENABLED4565if (fb_use_msdf) {4566FontFallback *fd = _get_font_data(sysf.rid);4567if (fd) {4568MutexLock lock(fd->mutex);4569Vector2i size = _get_size(fd, 16);4570FontForSizeFallback *ffsd = nullptr;4571if (_ensure_cache_for_size(fd, size, ffsd)) {4572if (ffsd && (FT_HAS_COLOR(fd->face) || !FT_IS_SCALABLE(fd->face))) {4573fb_use_msdf = false;4574}4575}4576}4577}4578#endif45794580_font_set_antialiasing(sysf.rid, key.antialiasing);4581_font_set_disable_embedded_bitmaps(sysf.rid, key.disable_embedded_bitmaps);4582_font_set_generate_mipmaps(sysf.rid, key.mipmaps);4583_font_set_multichannel_signed_distance_field(sysf.rid, fb_use_msdf);4584_font_set_msdf_pixel_range(sysf.rid, key.msdf_range);4585_font_set_msdf_size(sysf.rid, key.msdf_source_size);4586_font_set_fixed_size(sysf.rid, key.fixed_size);4587_font_set_force_autohinter(sysf.rid, key.force_autohinter);4588_font_set_hinting(sysf.rid, key.hinting);4589_font_set_subpixel_positioning(sysf.rid, key.subpixel_positioning);4590_font_set_keep_rounding_remainders(sysf.rid, key.keep_rounding_remainders);4591_font_set_variation_coordinates(sysf.rid, var);4592_font_set_embolden(sysf.rid, key.embolden);4593_font_set_transform(sysf.rid, key.transform);4594_font_set_spacing(sysf.rid, SPACING_TOP, key.extra_spacing[SPACING_TOP]);4595_font_set_spacing(sysf.rid, SPACING_BOTTOM, key.extra_spacing[SPACING_BOTTOM]);4596_font_set_spacing(sysf.rid, SPACING_SPACE, key.extra_spacing[SPACING_SPACE]);4597_font_set_spacing(sysf.rid, SPACING_GLYPH, key.extra_spacing[SPACING_GLYPH]);45984599if (system_fonts.has(key)) {4600system_fonts[key].var.push_back(sysf);4601} else {4602SystemFontCache &sysf_cache = system_fonts[key];4603sysf_cache.max_var = _font_get_face_count(sysf.rid);4604sysf_cache.var.push_back(sysf);4605}4606f = sysf.rid;4607}4608break;4609}4610}4611return f;4612}46134614void TextServerFallback::_shaped_text_overrun_trim_to_width(const RID &p_shaped_line, double p_width, BitField<TextServer::TextOverrunFlag> p_trim_flags) {4615ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped_line);4616ERR_FAIL_NULL_MSG(sd, "ShapedTextDataFallback invalid.");46174618MutexLock lock(sd->mutex);4619if (!sd->valid.is_set()) {4620_shaped_text_shape(p_shaped_line);4621}46224623sd->text_trimmed = false;4624sd->overrun_trim_data.ellipsis_glyph_buf.clear();46254626bool add_ellipsis = p_trim_flags.has_flag(OVERRUN_ADD_ELLIPSIS);4627bool cut_per_word = p_trim_flags.has_flag(OVERRUN_TRIM_WORD_ONLY);4628bool enforce_ellipsis = p_trim_flags.has_flag(OVERRUN_ENFORCE_ELLIPSIS);4629bool short_string_ellipsis = p_trim_flags.has_flag(OVERRUN_SHORT_STRING_ELLIPSIS);4630bool justification_aware = p_trim_flags.has_flag(OVERRUN_JUSTIFICATION_AWARE);46314632Glyph *sd_glyphs = sd->glyphs.ptrw();46334634if ((p_trim_flags & OVERRUN_TRIM) == OVERRUN_NO_TRIM || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) {4635sd->overrun_trim_data.trim_pos = -1;4636sd->overrun_trim_data.ellipsis_pos = -1;4637return;4638}46394640if (justification_aware && !sd->fit_width_minimum_reached) {4641return;4642}46434644Vector<ShapedTextDataFallback::Span> &spans = sd->spans;4645if (sd->parent != RID()) {4646ShapedTextDataFallback *parent_sd = shaped_owner.get_or_null(sd->parent);4647ERR_FAIL_COND(!parent_sd->valid.is_set());4648spans = parent_sd->spans;4649}46504651int span_size = spans.size();4652if (span_size == 0) {4653return;4654}46554656int sd_size = sd->glyphs.size();4657int last_gl_font_size = sd_glyphs[sd_size - 1].font_size;4658bool found_el_char = false;46594660// Find usable fonts, if fonts from the last glyph do not have required chars.4661RID dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;4662if (add_ellipsis || enforce_ellipsis || short_string_ellipsis) {4663if (!_font_has_char(dot_gl_font_rid, sd->el_char)) {4664const Array &fonts = spans[span_size - 1].fonts;4665for (int i = 0; i < fonts.size(); i++) {4666if (_font_has_char(fonts[i], sd->el_char)) {4667dot_gl_font_rid = fonts[i];4668found_el_char = true;4669break;4670}4671}4672if (!found_el_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {4673const char32_t u32str[] = { sd->el_char, 0 };4674RID rid = _find_sys_font_for_text(fonts[0], String(), spans[span_size - 1].language, u32str);4675if (rid.is_valid()) {4676dot_gl_font_rid = rid;4677found_el_char = true;4678}4679}4680} else {4681found_el_char = true;4682}4683if (!found_el_char) {4684bool found_dot_char = false;4685dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;4686if (!_font_has_char(dot_gl_font_rid, '.')) {4687const Array &fonts = spans[span_size - 1].fonts;4688for (int i = 0; i < fonts.size(); i++) {4689if (_font_has_char(fonts[i], '.')) {4690dot_gl_font_rid = fonts[i];4691found_dot_char = true;4692break;4693}4694}4695if (!found_dot_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {4696RID rid = _find_sys_font_for_text(fonts[0], String(), spans[span_size - 1].language, ".");4697if (rid.is_valid()) {4698dot_gl_font_rid = rid;4699}4700}4701}4702}4703}4704RID whitespace_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;4705if (!_font_has_char(whitespace_gl_font_rid, ' ')) {4706const Array &fonts = spans[span_size - 1].fonts;4707for (int i = 0; i < fonts.size(); i++) {4708if (_font_has_char(fonts[i], ' ')) {4709whitespace_gl_font_rid = fonts[i];4710break;4711}4712}4713}47144715int32_t dot_gl_idx = ((add_ellipsis || enforce_ellipsis || short_string_ellipsis) && dot_gl_font_rid.is_valid()) ? _font_get_glyph_index(dot_gl_font_rid, last_gl_font_size, (found_el_char ? sd->el_char : '.'), 0) : -1;4716Vector2 dot_adv = ((add_ellipsis || enforce_ellipsis || short_string_ellipsis) && dot_gl_font_rid.is_valid()) ? _font_get_glyph_advance(dot_gl_font_rid, last_gl_font_size, dot_gl_idx) : Vector2();4717int32_t whitespace_gl_idx = whitespace_gl_font_rid.is_valid() ? _font_get_glyph_index(whitespace_gl_font_rid, last_gl_font_size, ' ', 0) : -1;4718Vector2 whitespace_adv = whitespace_gl_font_rid.is_valid() ? _font_get_glyph_advance(whitespace_gl_font_rid, last_gl_font_size, whitespace_gl_idx) : Vector2();47194720int ellipsis_width = 0;4721if (add_ellipsis && whitespace_gl_font_rid.is_valid()) {4722ellipsis_width = (found_el_char ? 1 : 3) * dot_adv.x + sd->extra_spacing[SPACING_GLYPH] + _font_get_spacing(dot_gl_font_rid, SPACING_GLYPH) + (cut_per_word ? whitespace_adv.x : 0);4723}47244725int ell_min_characters = 6;4726double width = sd->width;4727double width_without_el = width;47284729int trim_pos = 0;4730int ellipsis_pos = (enforce_ellipsis || short_string_ellipsis) ? 0 : -1;47314732int last_valid_cut = -1;4733int last_valid_cut_witout_el = -1;47344735if ((enforce_ellipsis || short_string_ellipsis) && (width + ellipsis_width <= p_width)) {4736trim_pos = -1;4737ellipsis_pos = sd_size;4738} else {4739for (int i = sd_size - 1; i != -1; i--) {4740width -= sd_glyphs[i].advance * sd_glyphs[i].repeat;47414742if (sd_glyphs[i].count > 0) {4743bool above_min_char_threshold = (i >= ell_min_characters);4744if (!above_min_char_threshold && last_valid_cut_witout_el != -1) {4745trim_pos = last_valid_cut_witout_el;4746ellipsis_pos = -1;4747width = width_without_el;4748break;4749}4750if (!(enforce_ellipsis || short_string_ellipsis) && width <= p_width && last_valid_cut_witout_el == -1) {4751if (cut_per_word && above_min_char_threshold) {4752if ((sd_glyphs[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) {4753last_valid_cut_witout_el = i;4754width_without_el = width;4755}4756} else {4757last_valid_cut_witout_el = i;4758width_without_el = width;4759}4760}4761if (width + (((above_min_char_threshold && add_ellipsis) || enforce_ellipsis || short_string_ellipsis) ? ellipsis_width : 0) <= p_width) {4762if (cut_per_word && above_min_char_threshold) {4763if ((sd_glyphs[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) {4764last_valid_cut = i;4765}4766} else {4767last_valid_cut = i;4768}4769if (last_valid_cut != -1) {4770trim_pos = last_valid_cut;47714772if (add_ellipsis && (above_min_char_threshold || enforce_ellipsis || short_string_ellipsis) && width - ellipsis_width <= p_width) {4773ellipsis_pos = trim_pos;4774}4775break;4776}4777}4778}4779}4780}47814782sd->overrun_trim_data.trim_pos = trim_pos;4783sd->overrun_trim_data.ellipsis_pos = ellipsis_pos;4784if (trim_pos == 0 && (enforce_ellipsis || short_string_ellipsis) && add_ellipsis) {4785sd->overrun_trim_data.ellipsis_pos = 0;4786}47874788if ((trim_pos >= 0 && sd->width > p_width) || enforce_ellipsis || short_string_ellipsis) {4789if (add_ellipsis && (ellipsis_pos > 0 || enforce_ellipsis || short_string_ellipsis)) {4790// Insert an additional space when cutting word bound for aesthetics.4791if (cut_per_word && (ellipsis_pos > 0)) {4792Glyph gl;4793gl.count = 1;4794gl.advance = whitespace_adv.x;4795gl.index = whitespace_gl_idx;4796gl.font_rid = whitespace_gl_font_rid;4797gl.font_size = last_gl_font_size;4798gl.flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT | GRAPHEME_IS_VIRTUAL;47994800sd->overrun_trim_data.ellipsis_glyph_buf.append(gl);4801}4802// Add ellipsis dots.4803if (dot_gl_idx != 0) {4804Glyph gl;4805gl.count = 1;4806gl.repeat = (found_el_char ? 1 : 3);4807gl.advance = dot_adv.x;4808gl.index = dot_gl_idx;4809gl.font_rid = dot_gl_font_rid;4810gl.font_size = last_gl_font_size;4811gl.flags = GRAPHEME_IS_PUNCTUATION | GRAPHEME_IS_VIRTUAL;48124813sd->overrun_trim_data.ellipsis_glyph_buf.append(gl);4814}4815}48164817sd->text_trimmed = true;4818sd->width_trimmed = width + ((ellipsis_pos != -1) ? ellipsis_width : 0);4819}4820}48214822int64_t TextServerFallback::_shaped_text_get_trim_pos(const RID &p_shaped) const {4823ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4824ERR_FAIL_NULL_V_MSG(sd, -1, "ShapedTextDataFallback invalid.");48254826MutexLock lock(sd->mutex);4827return sd->overrun_trim_data.trim_pos;4828}48294830int64_t TextServerFallback::_shaped_text_get_ellipsis_pos(const RID &p_shaped) const {4831ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4832ERR_FAIL_NULL_V_MSG(sd, -1, "ShapedTextDataFallback invalid.");48334834MutexLock lock(sd->mutex);4835return sd->overrun_trim_data.ellipsis_pos;4836}48374838const Glyph *TextServerFallback::_shaped_text_get_ellipsis_glyphs(const RID &p_shaped) const {4839ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4840ERR_FAIL_NULL_V_MSG(sd, nullptr, "ShapedTextDataFallback invalid.");48414842MutexLock lock(sd->mutex);4843return sd->overrun_trim_data.ellipsis_glyph_buf.ptr();4844}48454846int64_t TextServerFallback::_shaped_text_get_ellipsis_glyph_count(const RID &p_shaped) const {4847ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4848ERR_FAIL_NULL_V_MSG(sd, 0, "ShapedTextDataFallback invalid.");48494850MutexLock lock(sd->mutex);4851return sd->overrun_trim_data.ellipsis_glyph_buf.size();4852}48534854bool TextServerFallback::_shaped_text_shape(const RID &p_shaped) {4855ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4856ERR_FAIL_NULL_V(sd, false);48574858MutexLock lock(sd->mutex);4859if (sd->valid.is_set()) {4860return true;4861}48624863if (sd->parent != RID()) {4864full_copy(sd);4865}48664867// Cleanup.4868sd->justification_ops_valid = false;4869sd->line_breaks_valid = false;4870sd->ascent = 0.0;4871sd->descent = 0.0;4872sd->width = 0.0;4873sd->glyphs.clear();4874sd->runs.clear();4875sd->runs_dirty = true;48764877if (sd->text.length() == 0) {4878sd->valid.set();4879return true;4880}48814882// "Shape" string.4883for (int i = 0; i < sd->spans.size(); i++) {4884const ShapedTextDataFallback::Span &span = sd->spans[i];4885if (span.embedded_key != Variant()) {4886// Embedded object.4887if (sd->orientation == ORIENTATION_HORIZONTAL) {4888sd->objects[span.embedded_key].rect.position.x = sd->width;4889sd->width += sd->objects[span.embedded_key].rect.size.x;4890} else {4891sd->objects[span.embedded_key].rect.position.y = sd->width;4892sd->width += sd->objects[span.embedded_key].rect.size.y;4893}4894Glyph gl;4895gl.span_index = i;4896gl.start = span.start;4897gl.end = span.end;4898gl.count = 1;4899gl.index = 0;4900gl.flags = GRAPHEME_IS_VALID | GRAPHEME_IS_EMBEDDED_OBJECT;4901if (sd->orientation == ORIENTATION_HORIZONTAL) {4902gl.advance = sd->objects[span.embedded_key].rect.size.x;4903} else {4904gl.advance = sd->objects[span.embedded_key].rect.size.y;4905}4906sd->glyphs.push_back(gl);4907} else {4908// Text span.4909int last_non_zero_w = sd->end - 1;4910if (i == sd->spans.size() - 1) {4911for (int j = span.end - 1; j >= span.start; j--) {4912last_non_zero_w = j;4913uint32_t idx = (int32_t)sd->text[j - sd->start];4914if (!is_control(idx) && !(idx >= 0x200B && idx <= 0x200D)) {4915break;4916}4917}4918}49194920RID prev_font;4921for (int j = span.start; j < span.end; j++) {4922Glyph gl;4923gl.span_index = i;4924gl.start = j;4925gl.end = j + 1;4926gl.count = 1;4927gl.font_size = span.font_size;4928gl.index = (int32_t)sd->text[j - sd->start]; // Use codepoint.4929bool zw = (gl.index >= 0x200b && gl.index <= 0x200d);4930if (gl.index == 0x0009 || gl.index == 0x000b || zw) {4931gl.index = 0x0020;4932}4933if (!sd->preserve_control && is_control(gl.index)) {4934gl.index = 0x0020;4935}4936// Select first font which has character (font are already sorted by span language).4937for (int k = 0; k < span.fonts.size(); k++) {4938if (_font_has_char(span.fonts[k], gl.index)) {4939gl.font_rid = span.fonts[k];4940break;4941}4942}4943if (!gl.font_rid.is_valid() && prev_font.is_valid()) {4944if (_font_has_char(prev_font, gl.index)) {4945gl.font_rid = prev_font;4946}4947}4948if (!gl.font_rid.is_valid() && OS::get_singleton()->has_feature("system_fonts") && span.fonts.size() > 0) {4949// Try system fallback.4950RID fdef = span.fonts[0];4951if (_font_is_allow_system_fallback(fdef)) {4952String text = sd->text.substr(j, 1);4953gl.font_rid = _find_sys_font_for_text(fdef, String(), span.language, text);4954}4955}4956prev_font = gl.font_rid;49574958if (gl.font_rid.is_valid()) {4959double scale = _font_get_scale(gl.font_rid, gl.font_size);4960bool subpos = (scale != 1.0) || (_font_get_subpixel_positioning(gl.font_rid) == SUBPIXEL_POSITIONING_ONE_HALF) || (_font_get_subpixel_positioning(gl.font_rid) == SUBPIXEL_POSITIONING_ONE_QUARTER) || (_font_get_subpixel_positioning(gl.font_rid) == SUBPIXEL_POSITIONING_AUTO && gl.font_size <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE);4961if (sd->text[j - sd->start] != 0 && !is_linebreak(sd->text[j - sd->start])) {4962if (sd->orientation == ORIENTATION_HORIZONTAL) {4963gl.advance = _font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x;4964gl.x_off = 0;4965gl.y_off = _font_get_baseline_offset(gl.font_rid) * (double)(_font_get_ascent(gl.font_rid, gl.font_size) + _font_get_descent(gl.font_rid, gl.font_size));4966sd->ascent = MAX(sd->ascent, _font_get_ascent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_TOP));4967sd->descent = MAX(sd->descent, _font_get_descent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_BOTTOM));4968} else {4969gl.advance = _font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).y;4970gl.x_off = -Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5) + _font_get_baseline_offset(gl.font_rid) * (double)(_font_get_ascent(gl.font_rid, gl.font_size) + _font_get_descent(gl.font_rid, gl.font_size));4971gl.y_off = _font_get_ascent(gl.font_rid, gl.font_size);4972sd->ascent = MAX(sd->ascent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));4973sd->descent = MAX(sd->descent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));4974}4975}4976if (zw) {4977gl.advance = 0.0;4978}4979if ((j < last_non_zero_w) && !Math::is_zero_approx(gl.advance)) {4980// Do not add extra spacing to the last glyph of the string and zero width glyphs.4981if (is_whitespace(sd->text[j - sd->start])) {4982gl.advance += sd->extra_spacing[SPACING_SPACE] + _font_get_spacing(gl.font_rid, SPACING_SPACE);4983} else {4984gl.advance += sd->extra_spacing[SPACING_GLYPH] + _font_get_spacing(gl.font_rid, SPACING_GLYPH);4985}4986}4987sd->upos = MAX(sd->upos, _font_get_underline_position(gl.font_rid, gl.font_size));4988sd->uthk = MAX(sd->uthk, _font_get_underline_thickness(gl.font_rid, gl.font_size));49894990// Add kerning to previous glyph.4991if (sd->glyphs.size() > 0) {4992Glyph &prev_gl = sd->glyphs.write[sd->glyphs.size() - 1];4993if (prev_gl.font_rid == gl.font_rid && prev_gl.font_size == gl.font_size) {4994if (sd->orientation == ORIENTATION_HORIZONTAL) {4995prev_gl.advance += _font_get_kerning(gl.font_rid, gl.font_size, Vector2i(prev_gl.index, gl.index)).x;4996} else {4997prev_gl.advance += _font_get_kerning(gl.font_rid, gl.font_size, Vector2i(prev_gl.index, gl.index)).y;4998}4999}5000}5001if (sd->orientation == ORIENTATION_HORIZONTAL && !subpos) {5002gl.advance = Math::round(gl.advance);5003}5004} else if (sd->preserve_invalid || (sd->preserve_control && is_control(gl.index))) {5005// Glyph not found, replace with hex code box.5006if (sd->orientation == ORIENTATION_HORIZONTAL) {5007gl.advance = get_hex_code_box_size(gl.font_size, gl.index).x;5008sd->ascent = MAX(sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.85);5009sd->descent = MAX(sd->descent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.15);5010} else {5011gl.advance = get_hex_code_box_size(gl.font_size, gl.index).y;5012sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5));5013sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5));5014}5015}5016sd->width += gl.advance;5017sd->glyphs.push_back(gl);5018}5019}5020}50215022// Align embedded objects to baseline.5023_realign(sd);50245025sd->valid.set();5026return sd->valid.is_set();5027}50285029bool TextServerFallback::_shaped_text_is_ready(const RID &p_shaped) const {5030const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5031ERR_FAIL_NULL_V(sd, false);50325033return sd->valid.is_set();5034}50355036const Glyph *TextServerFallback::_shaped_text_get_glyphs(const RID &p_shaped) const {5037const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5038ERR_FAIL_NULL_V(sd, nullptr);50395040MutexLock lock(sd->mutex);5041if (!sd->valid.is_set()) {5042const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);5043}5044return sd->glyphs.ptr();5045}50465047int64_t TextServerFallback::_shaped_text_get_glyph_count(const RID &p_shaped) const {5048const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5049ERR_FAIL_NULL_V(sd, 0);50505051MutexLock lock(sd->mutex);5052if (!sd->valid.is_set()) {5053const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);5054}5055return sd->glyphs.size();5056}50575058const Glyph *TextServerFallback::_shaped_text_sort_logical(const RID &p_shaped) {5059const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5060ERR_FAIL_NULL_V(sd, nullptr);50615062MutexLock lock(sd->mutex);5063if (!sd->valid.is_set()) {5064_shaped_text_shape(p_shaped);5065}50665067return sd->glyphs.ptr(); // Already in the logical order, return as is.5068}50695070Vector2i TextServerFallback::_shaped_text_get_range(const RID &p_shaped) const {5071const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5072ERR_FAIL_NULL_V(sd, Vector2i());50735074MutexLock lock(sd->mutex);5075return Vector2(sd->start, sd->end);5076}50775078Array TextServerFallback::_shaped_text_get_objects(const RID &p_shaped) const {5079Array ret;5080const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5081ERR_FAIL_NULL_V(sd, ret);50825083MutexLock lock(sd->mutex);5084for (const KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : sd->objects) {5085ret.push_back(E.key);5086}50875088return ret;5089}50905091Rect2 TextServerFallback::_shaped_text_get_object_rect(const RID &p_shaped, const Variant &p_key) const {5092const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5093ERR_FAIL_NULL_V(sd, Rect2());50945095MutexLock lock(sd->mutex);5096ERR_FAIL_COND_V(!sd->objects.has(p_key), Rect2());5097if (!sd->valid.is_set()) {5098const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);5099}5100return sd->objects[p_key].rect;5101}51025103Vector2i TextServerFallback::_shaped_text_get_object_range(const RID &p_shaped, const Variant &p_key) const {5104const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5105ERR_FAIL_NULL_V(sd, Vector2i());51065107MutexLock lock(sd->mutex);5108ERR_FAIL_COND_V(!sd->objects.has(p_key), Vector2i());5109return Vector2i(sd->objects[p_key].start, sd->objects[p_key].end);5110}51115112int64_t TextServerFallback::_shaped_text_get_object_glyph(const RID &p_shaped, const Variant &p_key) const {5113const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5114ERR_FAIL_NULL_V(sd, -1);51155116MutexLock lock(sd->mutex);5117ERR_FAIL_COND_V(!sd->objects.has(p_key), -1);5118const ShapedTextDataFallback::EmbeddedObject &obj = sd->objects[p_key];5119int sd_size = sd->glyphs.size();5120const Glyph *sd_glyphs = sd->glyphs.ptr();5121for (int i = 0; i < sd_size; i++) {5122if (obj.start == sd_glyphs[i].start) {5123return i;5124}5125}5126return -1;5127}51285129Size2 TextServerFallback::_shaped_text_get_size(const RID &p_shaped) const {5130const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5131ERR_FAIL_NULL_V(sd, Size2());51325133MutexLock lock(sd->mutex);5134if (!sd->valid.is_set()) {5135const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);5136}5137if (sd->orientation == TextServer::ORIENTATION_HORIZONTAL) {5138return Size2(sd->width, sd->ascent + sd->descent + sd->extra_spacing[SPACING_TOP] + sd->extra_spacing[SPACING_BOTTOM]).ceil();5139} else {5140return Size2(sd->ascent + sd->descent + sd->extra_spacing[SPACING_TOP] + sd->extra_spacing[SPACING_BOTTOM], sd->width).ceil();5141}5142}51435144double TextServerFallback::_shaped_text_get_ascent(const RID &p_shaped) const {5145const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5146ERR_FAIL_NULL_V(sd, 0.0);51475148MutexLock lock(sd->mutex);5149if (!sd->valid.is_set()) {5150const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);5151}5152return sd->ascent + sd->extra_spacing[SPACING_TOP];5153}51545155double TextServerFallback::_shaped_text_get_descent(const RID &p_shaped) const {5156const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5157ERR_FAIL_NULL_V(sd, 0.0);51585159MutexLock lock(sd->mutex);5160if (!sd->valid.is_set()) {5161const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);5162}5163return sd->descent + sd->extra_spacing[SPACING_BOTTOM];5164}51655166double TextServerFallback::_shaped_text_get_width(const RID &p_shaped) const {5167const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5168ERR_FAIL_NULL_V(sd, 0.0);51695170MutexLock lock(sd->mutex);5171if (!sd->valid.is_set()) {5172const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);5173}5174return Math::ceil(sd->width);5175}51765177double TextServerFallback::_shaped_text_get_underline_position(const RID &p_shaped) const {5178const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5179ERR_FAIL_NULL_V(sd, 0.0);51805181MutexLock lock(sd->mutex);5182if (!sd->valid.is_set()) {5183const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);5184}51855186return sd->upos;5187}51885189double TextServerFallback::_shaped_text_get_underline_thickness(const RID &p_shaped) const {5190const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5191ERR_FAIL_NULL_V(sd, 0.0);51925193MutexLock lock(sd->mutex);5194if (!sd->valid.is_set()) {5195const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);5196}51975198return sd->uthk;5199}52005201PackedInt32Array TextServerFallback::_shaped_text_get_character_breaks(const RID &p_shaped) const {5202const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5203ERR_FAIL_NULL_V(sd, PackedInt32Array());52045205MutexLock lock(sd->mutex);5206if (!sd->valid.is_set()) {5207const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);5208}52095210PackedInt32Array ret;5211int size = sd->end - sd->start;5212if (size > 0) {5213ret.resize(size);5214for (int i = 0; i < size; i++) {5215#ifdef GDEXTENSION5216ret[i] = i + 1 + sd->start;5217#else5218ret.write[i] = i + 1 + sd->start;5219#endif5220}5221}5222return ret;5223}52245225String TextServerFallback::_string_to_upper(const String &p_string, const String &p_language) const {5226return p_string.to_upper();5227}52285229String TextServerFallback::_string_to_lower(const String &p_string, const String &p_language) const {5230return p_string.to_lower();5231}52325233String TextServerFallback::_string_to_title(const String &p_string, const String &p_language) const {5234return p_string.capitalize();5235}52365237PackedInt32Array TextServerFallback::_string_get_word_breaks(const String &p_string, const String &p_language, int64_t p_chars_per_line) const {5238PackedInt32Array ret;52395240if (p_chars_per_line > 0) {5241int line_start = 0;5242int last_break = -1;5243int line_length = 0;52445245for (int i = 0; i < p_string.length(); i++) {5246const char32_t c = p_string[i];52475248bool is_lb = is_linebreak(c);5249bool is_ws = is_whitespace(c);5250bool is_p = (is_punct(c) && c != 0x005F) || is_underscore(c) || c == '\t' || c == 0xfffc;52515252if (is_lb) {5253if (line_length > 0) {5254ret.push_back(line_start);5255ret.push_back(i);5256}5257line_start = i;5258line_length = 0;5259last_break = -1;5260continue;5261} else if (is_ws || is_p) {5262last_break = i;5263}52645265if (line_length == p_chars_per_line) {5266if (last_break != -1) {5267int last_break_w_spaces = last_break;5268while (last_break > line_start && is_whitespace(p_string[last_break - 1])) {5269last_break--;5270}5271if (line_start != last_break) {5272ret.push_back(line_start);5273ret.push_back(last_break);5274}5275while (last_break_w_spaces < p_string.length() && is_whitespace(p_string[last_break_w_spaces])) {5276last_break_w_spaces++;5277}5278line_start = last_break_w_spaces;5279if (last_break_w_spaces < i) {5280line_length = i - last_break_w_spaces;5281} else {5282i = last_break_w_spaces;5283line_length = 0;5284}5285} else {5286ret.push_back(line_start);5287ret.push_back(i);5288line_start = i;5289line_length = 0;5290}5291last_break = -1;5292}5293line_length++;5294}5295if (line_length > 0) {5296ret.push_back(line_start);5297ret.push_back(p_string.length());5298}5299} else {5300int word_start = 0; // -1 if no word encountered. Leading spaces are part of a word.5301int word_length = 0;53025303for (int i = 0; i < p_string.length(); i++) {5304const char32_t c = p_string[i];53055306bool is_lb = is_linebreak(c);5307bool is_ws = is_whitespace(c);5308bool is_p = (is_punct(c) && c != 0x005F) || is_underscore(c) || c == '\t' || c == 0xfffc;53095310if (word_start == -1) {5311if (!is_lb && !is_ws && !is_p) {5312word_start = i;5313}5314continue;5315}53165317if (is_lb) {5318if (word_start != -1 && word_length > 0) {5319ret.push_back(word_start);5320ret.push_back(i);5321}5322word_start = -1;5323word_length = 0;5324} else if (is_ws || is_p) {5325if (word_start != -1 && word_length > 0) {5326ret.push_back(word_start);5327ret.push_back(i);5328}5329word_start = -1;5330word_length = 0;5331}53325333word_length++;5334}5335if (word_start != -1 && word_length > 0) {5336ret.push_back(word_start);5337ret.push_back(p_string.length());5338}5339}5340return ret;5341}53425343void TextServerFallback::_update_settings() {5344lcd_subpixel_layout.set((TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"));5345}53465347TextServerFallback::TextServerFallback() {5348_insert_feature_sets();5349ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &TextServerFallback::_update_settings));5350}53515352void TextServerFallback::_font_clear_system_fallback_cache() {5353for (const KeyValue<SystemFontKey, SystemFontCache> &E : system_fonts) {5354const Vector<SystemFontCacheRec> &sysf_cache = E.value.var;5355for (const SystemFontCacheRec &F : sysf_cache) {5356_free_rid(F.rid);5357}5358}5359system_fonts.clear();5360system_font_data.clear();5361}53625363void TextServerFallback::_cleanup() {5364font_clear_system_fallback_cache();5365}53665367TextServerFallback::~TextServerFallback() {5368#ifdef MODULE_FREETYPE_ENABLED5369if (ft_library != nullptr) {5370FT_Done_FreeType(ft_library);5371}5372#endif5373}537453755376