Path: blob/master/modules/text_server_fb/text_server_fb.cpp
10277 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/string/print_string.h"54#include "core/string/translation_server.h"5556#include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg.5758#endif5960// Thirdparty headers.6162#ifdef MODULE_MSDFGEN_ENABLED63#include <core/EdgeHolder.h>64#include <core/ShapeDistanceFinder.h>65#include <core/contour-combiners.h>66#include <core/edge-selectors.h>67#include <msdfgen.h>68#endif6970#ifdef MODULE_FREETYPE_ENABLED71#include FT_SFNT_NAMES_H72#include FT_TRUETYPE_IDS_H73#ifdef MODULE_SVG_ENABLED74#include "thorvg_svg_in_ot.h"75#endif76#endif7778/*************************************************************************/7980bool TextServerFallback::_has_feature(Feature p_feature) const {81switch (p_feature) {82case FEATURE_SIMPLE_LAYOUT:83case FEATURE_FONT_BITMAP:84#ifdef MODULE_FREETYPE_ENABLED85case FEATURE_FONT_DYNAMIC:86#endif87#ifdef MODULE_MSDFGEN_ENABLED88case FEATURE_FONT_MSDF:89#endif90return true;91default: {92}93}94return false;95}9697String TextServerFallback::_get_name() const {98#ifdef GDEXTENSION99return "Fallback (GDExtension)";100#elif defined(GODOT_MODULE)101return "Fallback (Built-in)";102#endif103}104105int64_t TextServerFallback::_get_features() const {106int64_t interface_features = FEATURE_SIMPLE_LAYOUT | FEATURE_FONT_BITMAP;107#ifdef MODULE_FREETYPE_ENABLED108interface_features |= FEATURE_FONT_DYNAMIC;109#endif110#ifdef MODULE_MSDFGEN_ENABLED111interface_features |= FEATURE_FONT_MSDF;112#endif113114return interface_features;115}116117void TextServerFallback::_free_rid(const RID &p_rid) {118_THREAD_SAFE_METHOD_119if (font_owner.owns(p_rid)) {120MutexLock ftlock(ft_mutex);121122FontFallback *fd = font_owner.get_or_null(p_rid);123for (const KeyValue<Vector2i, FontForSizeFallback *> &ffsd : fd->cache) {124OversamplingLevel *ol = oversampling_levels.getptr(ffsd.value->viewport_oversampling);125if (ol != nullptr) {126ol->fonts.erase(ffsd.value);127}128}129{130MutexLock lock(fd->mutex);131font_owner.free(p_rid);132}133memdelete(fd);134} else if (font_var_owner.owns(p_rid)) {135MutexLock ftlock(ft_mutex);136137FontFallbackLinkedVariation *fdv = font_var_owner.get_or_null(p_rid);138{139font_var_owner.free(p_rid);140}141memdelete(fdv);142} else if (shaped_owner.owns(p_rid)) {143ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_rid);144{145MutexLock lock(sd->mutex);146shaped_owner.free(p_rid);147}148memdelete(sd);149}150}151152bool TextServerFallback::_has(const RID &p_rid) {153_THREAD_SAFE_METHOD_154return font_owner.owns(p_rid) || shaped_owner.owns(p_rid);155}156157String TextServerFallback::_get_support_data_filename() const {158return "";159}160161String TextServerFallback::_get_support_data_info() const {162return "Not supported";163}164165bool TextServerFallback::_load_support_data(const String &p_filename) {166return false; // No extra data used.167}168169bool TextServerFallback::_save_support_data(const String &p_filename) const {170return false; // No extra data used.171}172173PackedByteArray TextServerFallback::_get_support_data() const {174return PackedByteArray(); // No extra data used.175}176177bool TextServerFallback::_is_locale_right_to_left(const String &p_locale) const {178return false; // No RTL support.179}180181_FORCE_INLINE_ void TextServerFallback::_insert_feature(const StringName &p_name, int32_t p_tag) {182feature_sets.insert(p_name, p_tag);183feature_sets_inv.insert(p_tag, p_name);184}185186void TextServerFallback::_insert_feature_sets() {187// Registered OpenType variation tag.188_insert_feature("italic", OT_TAG('i', 't', 'a', 'l'));189_insert_feature("optical_size", OT_TAG('o', 'p', 's', 'z'));190_insert_feature("slant", OT_TAG('s', 'l', 'n', 't'));191_insert_feature("width", OT_TAG('w', 'd', 't', 'h'));192_insert_feature("weight", OT_TAG('w', 'g', 'h', 't'));193}194195_FORCE_INLINE_ int32_t ot_tag_from_string(const char *p_str, int p_len) {196char tag[4];197uint32_t i;198199if (!p_str || !p_len || !*p_str) {200return OT_TAG(0, 0, 0, 0);201}202203if (p_len < 0 || p_len > 4) {204p_len = 4;205}206for (i = 0; i < (uint32_t)p_len && p_str[i]; i++) {207tag[i] = p_str[i];208}209210for (; i < 4; i++) {211tag[i] = ' ';212}213214return OT_TAG(tag[0], tag[1], tag[2], tag[3]);215}216217int64_t TextServerFallback::_name_to_tag(const String &p_name) const {218if (feature_sets.has(p_name)) {219return feature_sets[p_name];220}221222// No readable name, use tag string.223return ot_tag_from_string(p_name.replace("custom_", "").ascii().get_data(), -1);224}225226_FORCE_INLINE_ void ot_tag_to_string(int32_t p_tag, char *p_buf) {227p_buf[0] = (char)(uint8_t)(p_tag >> 24);228p_buf[1] = (char)(uint8_t)(p_tag >> 16);229p_buf[2] = (char)(uint8_t)(p_tag >> 8);230p_buf[3] = (char)(uint8_t)(p_tag >> 0);231}232233String TextServerFallback::_tag_to_name(int64_t p_tag) const {234if (feature_sets_inv.has(p_tag)) {235return feature_sets_inv[p_tag];236}237238// No readable name, use tag string.239char name[5];240memset(name, 0, 5);241ot_tag_to_string(p_tag, name);242return String("custom_") + String(name);243}244245/*************************************************************************/246/* Font Glyph Rendering */247/*************************************************************************/248249_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 {250FontTexturePosition ret;251252int mw = p_width;253int mh = p_height;254255ShelfPackTexture *ct = p_data->textures.ptrw();256for (int32_t i = 0; i < p_data->textures.size(); i++) {257if (ct[i].image.is_null()) {258continue;259}260if (p_image_format != ct[i].image->get_format()) {261continue;262}263if (mw > ct[i].texture_w || mh > ct[i].texture_h) { // Too big for this texture.264continue;265}266267ret = ct[i].pack_rect(i, mh, mw);268if (ret.index != -1) {269break;270}271}272273if (ret.index == -1) {274// Could not find texture to fit, create one.275int texsize = MAX(p_data->size.x * 0.125, 256);276277texsize = next_power_of_2((uint32_t)texsize);278279if (p_msdf) {280texsize = MIN(texsize, 2048);281} else {282texsize = MIN(texsize, 1024);283}284if (mw > texsize) { // Special case, adapt to it?285texsize = next_power_of_2((uint32_t)mw);286}287if (mh > texsize) { // Special case, adapt to it?288texsize = next_power_of_2((uint32_t)mh);289}290291ShelfPackTexture tex = ShelfPackTexture(texsize, texsize);292tex.image = Image::create_empty(texsize, texsize, false, p_image_format);293{294// Zero texture.295uint8_t *w = tex.image->ptrw();296ERR_FAIL_COND_V(texsize * texsize * p_color_size > tex.image->get_data_size(), ret);297// Initialize the texture to all-white pixels to prevent artifacts when the298// font is displayed at a non-default scale with filtering enabled.299if (p_color_size == 2) {300for (int i = 0; i < texsize * texsize * p_color_size; i += 2) { // FORMAT_LA8, BW font.301w[i + 0] = 255;302w[i + 1] = 0;303}304} else if (p_color_size == 4) {305for (int i = 0; i < texsize * texsize * p_color_size; i += 4) { // FORMAT_RGBA8, Color font, Multichannel(+True) SDF.306if (p_msdf) {307w[i + 0] = 0;308w[i + 1] = 0;309w[i + 2] = 0;310} else {311w[i + 0] = 255;312w[i + 1] = 255;313w[i + 2] = 255;314}315w[i + 3] = 0;316}317} else {318ERR_FAIL_V(ret);319}320}321p_data->textures.push_back(tex);322323int32_t idx = p_data->textures.size() - 1;324ret = p_data->textures.write[idx].pack_rect(idx, mh, mw);325}326327return ret;328}329330#ifdef MODULE_MSDFGEN_ENABLED331332struct MSContext {333msdfgen::Point2 position;334msdfgen::Shape *shape = nullptr;335msdfgen::Contour *contour = nullptr;336};337338class DistancePixelConversion {339double invRange;340341public:342_FORCE_INLINE_ explicit DistancePixelConversion(double range) :343invRange(1 / range) {}344_FORCE_INLINE_ void operator()(float *pixels, const msdfgen::MultiAndTrueDistance &distance) const {345pixels[0] = float(invRange * distance.r + .5);346pixels[1] = float(invRange * distance.g + .5);347pixels[2] = float(invRange * distance.b + .5);348pixels[3] = float(invRange * distance.a + .5);349}350};351352struct MSDFThreadData {353msdfgen::Bitmap<float, 4> *output;354msdfgen::Shape *shape;355msdfgen::Projection *projection;356DistancePixelConversion *distancePixelConversion;357};358359static msdfgen::Point2 ft_point2(const FT_Vector &vector) {360return msdfgen::Point2(vector.x / 60.0f, vector.y / 60.0f);361}362363static int ft_move_to(const FT_Vector *to, void *user) {364MSContext *context = static_cast<MSContext *>(user);365if (!(context->contour && context->contour->edges.empty())) {366context->contour = &context->shape->addContour();367}368context->position = ft_point2(*to);369return 0;370}371372static int ft_line_to(const FT_Vector *to, void *user) {373MSContext *context = static_cast<MSContext *>(user);374msdfgen::Point2 endpoint = ft_point2(*to);375if (endpoint != context->position) {376context->contour->addEdge(new msdfgen::LinearSegment(context->position, endpoint));377context->position = endpoint;378}379return 0;380}381382static int ft_conic_to(const FT_Vector *control, const FT_Vector *to, void *user) {383MSContext *context = static_cast<MSContext *>(user);384context->contour->addEdge(new msdfgen::QuadraticSegment(context->position, ft_point2(*control), ft_point2(*to)));385context->position = ft_point2(*to);386return 0;387}388389static int ft_cubic_to(const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to, void *user) {390MSContext *context = static_cast<MSContext *>(user);391context->contour->addEdge(new msdfgen::CubicSegment(context->position, ft_point2(*control1), ft_point2(*control2), ft_point2(*to)));392context->position = ft_point2(*to);393return 0;394}395396void TextServerFallback::_generateMTSDF_threaded(void *p_td, uint32_t p_y) {397MSDFThreadData *td = static_cast<MSDFThreadData *>(p_td);398399msdfgen::ShapeDistanceFinder<msdfgen::OverlappingContourCombiner<msdfgen::MultiAndTrueDistanceSelector>> distanceFinder(*td->shape);400int row = td->shape->inverseYAxis ? td->output->height() - p_y - 1 : p_y;401for (int col = 0; col < td->output->width(); ++col) {402int x = (p_y % 2) ? td->output->width() - col - 1 : col;403msdfgen::Point2 p = td->projection->unproject(msdfgen::Point2(x + .5, p_y + .5));404msdfgen::MultiAndTrueDistance distance = distanceFinder.distance(p);405td->distancePixelConversion->operator()(td->output->operator()(x, row), distance);406}407}408409_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 {410msdfgen::Shape shape;411412shape.contours.clear();413shape.inverseYAxis = false;414415MSContext context = {};416context.shape = &shape;417FT_Outline_Funcs ft_functions;418ft_functions.move_to = &ft_move_to;419ft_functions.line_to = &ft_line_to;420ft_functions.conic_to = &ft_conic_to;421ft_functions.cubic_to = &ft_cubic_to;422ft_functions.shift = 0;423ft_functions.delta = 0;424425int error = FT_Outline_Decompose(p_outline, &ft_functions, &context);426ERR_FAIL_COND_V_MSG(error, FontGlyph(), "FreeType: Outline decomposition error: '" + String(FT_Error_String(error)) + "'.");427if (!shape.contours.empty() && shape.contours.back().edges.empty()) {428shape.contours.pop_back();429}430431if (FT_Outline_Get_Orientation(p_outline) == 1) {432for (int i = 0; i < (int)shape.contours.size(); ++i) {433shape.contours[i].reverse();434}435}436437shape.inverseYAxis = true;438shape.normalize();439440msdfgen::Shape::Bounds bounds = shape.getBounds(p_pixel_range);441442FontGlyph chr;443chr.found = true;444chr.advance = p_advance;445446if (shape.validate() && shape.contours.size() > 0) {447int w = (bounds.r - bounds.l);448int h = (bounds.t - bounds.b);449450if (w == 0 || h == 0) {451chr.texture_idx = -1;452chr.uv_rect = Rect2();453chr.rect = Rect2();454return chr;455}456int mw = w + p_rect_margin * 4;457int mh = h + p_rect_margin * 4;458459ERR_FAIL_COND_V(mw > 4096, FontGlyph());460ERR_FAIL_COND_V(mh > 4096, FontGlyph());461462FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, 4, Image::FORMAT_RGBA8, mw, mh, true);463ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph());464ShelfPackTexture &tex = p_data->textures.write[tex_pos.index];465466edgeColoringSimple(shape, 3.0); // Max. angle.467msdfgen::Bitmap<float, 4> image(w, h); // Texture size.468469DistancePixelConversion distancePixelConversion(p_pixel_range);470msdfgen::Projection projection(msdfgen::Vector2(1.0, 1.0), msdfgen::Vector2(-bounds.l, -bounds.b));471msdfgen::MSDFGeneratorConfig config(true, msdfgen::ErrorCorrectionConfig());472473MSDFThreadData td;474td.output = ℑ475td.shape = &shape;476td.projection = &projection;477td.distancePixelConversion = &distancePixelConversion;478479WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_native_group_task(&TextServerFallback::_generateMTSDF_threaded, &td, h, -1, true, String("TextServerFBRenderMSDF"));480WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);481482msdfgen::msdfErrorCorrection(image, shape, projection, p_pixel_range, config);483484{485uint8_t *wr = tex.image->ptrw();486487for (int i = 0; i < h; i++) {488for (int j = 0; j < w; j++) {489int ofs = ((i + tex_pos.y + p_rect_margin * 2) * tex.texture_w + j + tex_pos.x + p_rect_margin * 2) * 4;490ERR_FAIL_COND_V(ofs >= tex.image->get_data_size(), FontGlyph());491wr[ofs + 0] = (uint8_t)(CLAMP(image(j, i)[0] * 256.f, 0.f, 255.f));492wr[ofs + 1] = (uint8_t)(CLAMP(image(j, i)[1] * 256.f, 0.f, 255.f));493wr[ofs + 2] = (uint8_t)(CLAMP(image(j, i)[2] * 256.f, 0.f, 255.f));494wr[ofs + 3] = (uint8_t)(CLAMP(image(j, i)[3] * 256.f, 0.f, 255.f));495}496}497}498499tex.dirty = true;500501chr.texture_idx = tex_pos.index;502503chr.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);504chr.rect.position = Vector2(bounds.l - p_rect_margin, -bounds.t - p_rect_margin);505chr.rect.size = chr.uv_rect.size;506}507return chr;508}509#endif510511#ifdef MODULE_FREETYPE_ENABLED512_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 {513FontGlyph chr;514chr.advance = p_advance * p_data->scale;515chr.found = true;516517int w = p_bitmap.width;518int h = p_bitmap.rows;519520if (w == 0 || h == 0) {521chr.texture_idx = -1;522chr.uv_rect = Rect2();523chr.rect = Rect2();524return chr;525}526527int color_size = 2;528529switch (p_bitmap.pixel_mode) {530case FT_PIXEL_MODE_MONO:531case FT_PIXEL_MODE_GRAY: {532color_size = 2;533} break;534case FT_PIXEL_MODE_BGRA: {535color_size = 4;536} break;537case FT_PIXEL_MODE_LCD: {538color_size = 4;539w /= 3;540} break;541case FT_PIXEL_MODE_LCD_V: {542color_size = 4;543h /= 3;544} break;545}546547int mw = w + p_rect_margin * 4;548int mh = h + p_rect_margin * 4;549550ERR_FAIL_COND_V(mw > 4096, FontGlyph());551ERR_FAIL_COND_V(mh > 4096, FontGlyph());552553Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8;554555FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, color_size, require_format, mw, mh, false);556ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph());557558// Fit character in char texture.559ShelfPackTexture &tex = p_data->textures.write[tex_pos.index];560561{562uint8_t *wr = tex.image->ptrw();563564for (int i = 0; i < h; i++) {565for (int j = 0; j < w; j++) {566int ofs = ((i + tex_pos.y + p_rect_margin * 2) * tex.texture_w + j + tex_pos.x + p_rect_margin * 2) * color_size;567ERR_FAIL_COND_V(ofs >= tex.image->get_data_size(), FontGlyph());568switch (p_bitmap.pixel_mode) {569case FT_PIXEL_MODE_MONO: {570int byte = i * p_bitmap.pitch + (j >> 3);571int bit = 1 << (7 - (j % 8));572wr[ofs + 0] = 255; // grayscale as 1573wr[ofs + 1] = (p_bitmap.buffer[byte] & bit) ? 255 : 0;574} break;575case FT_PIXEL_MODE_GRAY:576wr[ofs + 0] = 255; // grayscale as 1577wr[ofs + 1] = p_bitmap.buffer[i * p_bitmap.pitch + j];578break;579case FT_PIXEL_MODE_BGRA: {580int ofs_color = i * p_bitmap.pitch + (j << 2);581wr[ofs + 2] = p_bitmap.buffer[ofs_color + 0];582wr[ofs + 1] = p_bitmap.buffer[ofs_color + 1];583wr[ofs + 0] = p_bitmap.buffer[ofs_color + 2];584wr[ofs + 3] = p_bitmap.buffer[ofs_color + 3];585} break;586case FT_PIXEL_MODE_LCD: {587int ofs_color = i * p_bitmap.pitch + (j * 3);588if (p_bgra) {589wr[ofs + 0] = p_bitmap.buffer[ofs_color + 2];590wr[ofs + 1] = p_bitmap.buffer[ofs_color + 1];591wr[ofs + 2] = p_bitmap.buffer[ofs_color + 0];592wr[ofs + 3] = 255;593} else {594wr[ofs + 0] = p_bitmap.buffer[ofs_color + 0];595wr[ofs + 1] = p_bitmap.buffer[ofs_color + 1];596wr[ofs + 2] = p_bitmap.buffer[ofs_color + 2];597wr[ofs + 3] = 255;598}599} break;600case FT_PIXEL_MODE_LCD_V: {601int ofs_color = i * p_bitmap.pitch * 3 + j;602if (p_bgra) {603wr[ofs + 0] = p_bitmap.buffer[ofs_color + p_bitmap.pitch * 2];604wr[ofs + 1] = p_bitmap.buffer[ofs_color + p_bitmap.pitch];605wr[ofs + 2] = p_bitmap.buffer[ofs_color + 0];606wr[ofs + 3] = 255;607} else {608wr[ofs + 0] = p_bitmap.buffer[ofs_color + 0];609wr[ofs + 1] = p_bitmap.buffer[ofs_color + p_bitmap.pitch];610wr[ofs + 2] = p_bitmap.buffer[ofs_color + p_bitmap.pitch * 2];611wr[ofs + 3] = 255;612}613} break;614default:615ERR_FAIL_V_MSG(FontGlyph(), "Font uses unsupported pixel format: " + String::num_int64(p_bitmap.pixel_mode) + ".");616break;617}618}619}620}621622tex.dirty = true;623624chr.texture_idx = tex_pos.index;625626chr.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);627chr.rect.position = Vector2(p_xofs - p_rect_margin, -p_yofs - p_rect_margin) * p_data->scale;628chr.rect.size = chr.uv_rect.size * p_data->scale;629return chr;630}631#endif632633/*************************************************************************/634/* Font Cache */635/*************************************************************************/636637bool TextServerFallback::_ensure_glyph(FontFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph, FontGlyph &r_glyph, uint32_t p_oversampling) const {638FontForSizeFallback *fd = nullptr;639ERR_FAIL_COND_V(!_ensure_cache_for_size(p_font_data, p_size, fd, false, p_oversampling), false);640641int32_t glyph_index = p_glyph & 0xffffff; // Remove subpixel shifts.642643HashMap<int32_t, FontGlyph>::Iterator E = fd->glyph_map.find(p_glyph);644if (E) {645r_glyph = E->value;646return E->value.found;647}648649if (glyph_index == 0) { // Non graphical or invalid glyph, do not render.650E = fd->glyph_map.insert(p_glyph, FontGlyph());651r_glyph = E->value;652return true;653}654655#ifdef MODULE_FREETYPE_ENABLED656FontGlyph gl;657if (fd->face) {658FT_Int32 flags = FT_LOAD_DEFAULT;659660bool outline = p_size.y > 0;661switch (p_font_data->hinting) {662case TextServer::HINTING_NONE:663flags |= FT_LOAD_NO_HINTING;664break;665case TextServer::HINTING_LIGHT:666flags |= FT_LOAD_TARGET_LIGHT;667break;668default:669flags |= FT_LOAD_TARGET_NORMAL;670break;671}672if (p_font_data->force_autohinter) {673flags |= FT_LOAD_FORCE_AUTOHINT;674}675if (outline || (p_font_data->disable_embedded_bitmaps && !FT_HAS_COLOR(fd->face))) {676flags |= FT_LOAD_NO_BITMAP;677} else if (FT_HAS_COLOR(fd->face)) {678flags |= FT_LOAD_COLOR;679}680681glyph_index = FT_Get_Char_Index(fd->face, glyph_index);682683FT_Fixed v, h;684FT_Get_Advance(fd->face, glyph_index, flags, &h);685FT_Get_Advance(fd->face, glyph_index, flags | FT_LOAD_VERTICAL_LAYOUT, &v);686687int error = FT_Load_Glyph(fd->face, glyph_index, flags);688if (error) {689E = fd->glyph_map.insert(p_glyph, FontGlyph());690r_glyph = E->value;691return false;692}693694if (!p_font_data->msdf) {695if ((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)) {696FT_Pos xshift = (int)((p_glyph >> 27) & 3) << 4;697FT_Outline_Translate(&fd->face->glyph->outline, xshift, 0);698} 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)) {699FT_Pos xshift = (int)((p_glyph >> 27) & 3) << 5;700FT_Outline_Translate(&fd->face->glyph->outline, xshift, 0);701}702}703704if (p_font_data->embolden != 0.f) {705FT_Pos strength = p_font_data->embolden * p_size.x / 16; // 26.6 fractional units (1 / 64).706FT_Outline_Embolden(&fd->face->glyph->outline, strength);707}708709if (p_font_data->transform != Transform2D()) {710FT_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).711FT_Outline_Transform(&fd->face->glyph->outline, &mat);712}713714FT_Render_Mode aa_mode = FT_RENDER_MODE_NORMAL;715bool bgra = false;716switch (p_font_data->antialiasing) {717case FONT_ANTIALIASING_NONE: {718aa_mode = FT_RENDER_MODE_MONO;719} break;720case FONT_ANTIALIASING_GRAY: {721aa_mode = FT_RENDER_MODE_NORMAL;722} break;723case FONT_ANTIALIASING_LCD: {724int aa_layout = (int)((p_glyph >> 24) & 7);725switch (aa_layout) {726case FONT_LCD_SUBPIXEL_LAYOUT_HRGB: {727aa_mode = FT_RENDER_MODE_LCD;728bgra = false;729} break;730case FONT_LCD_SUBPIXEL_LAYOUT_HBGR: {731aa_mode = FT_RENDER_MODE_LCD;732bgra = true;733} break;734case FONT_LCD_SUBPIXEL_LAYOUT_VRGB: {735aa_mode = FT_RENDER_MODE_LCD_V;736bgra = false;737} break;738case FONT_LCD_SUBPIXEL_LAYOUT_VBGR: {739aa_mode = FT_RENDER_MODE_LCD_V;740bgra = true;741} break;742default: {743aa_mode = FT_RENDER_MODE_NORMAL;744} break;745}746} break;747}748749FT_GlyphSlot slot = fd->face->glyph;750bool from_svg = (slot->format == FT_GLYPH_FORMAT_SVG); // Need to check before FT_Render_Glyph as it will change format to bitmap.751if (!outline) {752if (!p_font_data->msdf) {753error = FT_Render_Glyph(slot, aa_mode);754}755if (!error) {756if (p_font_data->msdf) {757#ifdef MODULE_MSDFGEN_ENABLED758gl = 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);759#else760fd->glyph_map[p_glyph] = FontGlyph();761ERR_FAIL_V_MSG(false, "Compiled without MSDFGEN support!");762#endif763} else {764gl = 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);765}766}767} else {768FT_Stroker stroker;769if (FT_Stroker_New(ft_library, &stroker) != 0) {770fd->glyph_map[p_glyph] = FontGlyph();771ERR_FAIL_V_MSG(false, "FreeType: Failed to load glyph stroker.");772}773774FT_Stroker_Set(stroker, (int)(fd->size.y * 16.0), FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0);775FT_Glyph glyph;776FT_BitmapGlyph glyph_bitmap;777778if (FT_Get_Glyph(fd->face->glyph, &glyph) != 0) {779goto cleanup_stroker;780}781if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) {782goto cleanup_glyph;783}784if (FT_Glyph_To_Bitmap(&glyph, aa_mode, nullptr, 1) != 0) {785goto cleanup_glyph;786}787glyph_bitmap = (FT_BitmapGlyph)glyph;788gl = rasterize_bitmap(fd, rect_range, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2(), bgra);789790cleanup_glyph:791FT_Done_Glyph(glyph);792cleanup_stroker:793FT_Stroker_Done(stroker);794}795gl.from_svg = from_svg;796E = fd->glyph_map.insert(p_glyph, gl);797r_glyph = E->value;798return gl.found;799}800#endif801E = fd->glyph_map.insert(p_glyph, FontGlyph());802r_glyph = E->value;803return false;804}805806bool 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 {807ERR_FAIL_COND_V(p_size.x <= 0, false);808809HashMap<Vector2i, FontForSizeFallback *>::Iterator E = p_font_data->cache.find(p_size);810if (E) {811r_cache_for_size = E->value;812// Size used directly, remove from oversampling list.813if (p_oversampling == 0 && E->value->viewport_oversampling != 0) {814OversamplingLevel *ol = oversampling_levels.getptr(E->value->viewport_oversampling);815if (ol) {816ol->fonts.erase(E->value);817}818}819return true;820}821822r_cache_for_size = nullptr;823FontForSizeFallback *fd = memnew(FontForSizeFallback);824fd->size = p_size;825if (p_font_data->data_ptr && (p_font_data->data_size > 0)) {826// Init dynamic font.827#ifdef MODULE_FREETYPE_ENABLED828int error = 0;829{830MutexLock ftlock(ft_mutex);831if (!ft_library) {832error = FT_Init_FreeType(&ft_library);833if (error != 0) {834memdelete(fd);835if (p_silent) {836return false;837} else {838ERR_FAIL_V_MSG(false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");839}840}841#ifdef MODULE_SVG_ENABLED842FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks());843#endif844}845846memset(&fd->stream, 0, sizeof(FT_StreamRec));847fd->stream.base = (unsigned char *)p_font_data->data_ptr;848fd->stream.size = p_font_data->data_size;849fd->stream.pos = 0;850851FT_Open_Args fargs;852memset(&fargs, 0, sizeof(FT_Open_Args));853fargs.memory_base = (unsigned char *)p_font_data->data_ptr;854fargs.memory_size = p_font_data->data_size;855fargs.flags = FT_OPEN_MEMORY;856fargs.stream = &fd->stream;857858int max_index = 0;859FT_Face tmp_face = nullptr;860error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);861if (tmp_face && error == 0) {862max_index = tmp_face->num_faces - 1;863}864if (tmp_face) {865FT_Done_Face(tmp_face);866}867868error = FT_Open_Face(ft_library, &fargs, CLAMP(p_font_data->face_index, 0, max_index), &fd->face);869if (error) {870FT_Done_Face(fd->face);871fd->face = nullptr;872memdelete(fd);873if (p_silent) {874return false;875} else {876ERR_FAIL_V_MSG(false, "FreeType: Error loading font: '" + String(FT_Error_String(error)) + "'.");877}878}879}880881double sz = double(fd->size.x) / 64.0;882if (p_font_data->msdf) {883sz = p_font_data->msdf_source_size;884}885886if (FT_HAS_COLOR(fd->face) && fd->face->num_fixed_sizes > 0) {887int best_match = 0;888int diff = Math::abs(sz - ((int64_t)fd->face->available_sizes[0].width));889fd->scale = sz / fd->face->available_sizes[0].width;890for (int i = 1; i < fd->face->num_fixed_sizes; i++) {891int ndiff = Math::abs(sz - ((int64_t)fd->face->available_sizes[i].width));892if (ndiff < diff) {893best_match = i;894diff = ndiff;895fd->scale = sz / fd->face->available_sizes[i].width;896}897}898FT_Select_Size(fd->face, best_match);899} else {900FT_Size_RequestRec req;901req.type = FT_SIZE_REQUEST_TYPE_NOMINAL;902req.width = sz * 64.0;903req.height = sz * 64.0;904req.horiResolution = 0;905req.vertResolution = 0;906907FT_Request_Size(fd->face, &req);908if (fd->face->size->metrics.y_ppem != 0) {909fd->scale = sz / (double)fd->face->size->metrics.y_ppem;910}911}912913fd->ascent = (fd->face->size->metrics.ascender / 64.0) * fd->scale;914fd->descent = (-fd->face->size->metrics.descender / 64.0) * fd->scale;915fd->underline_position = (-FT_MulFix(fd->face->underline_position, fd->face->size->metrics.y_scale) / 64.0) * fd->scale;916fd->underline_thickness = (FT_MulFix(fd->face->underline_thickness, fd->face->size->metrics.y_scale) / 64.0) * fd->scale;917918if (!p_font_data->face_init) {919// When a font does not provide a `family_name`, FreeType tries to synthesize one based on other names.920// FreeType automatically converts non-ASCII characters to "?" in the synthesized name.921// To avoid that behavior, use the format-specific name directly if available.922if (FT_IS_SFNT(fd->face)) {923int name_count = FT_Get_Sfnt_Name_Count(fd->face);924for (int i = 0; i < name_count; i++) {925FT_SfntName sfnt_name;926if (FT_Get_Sfnt_Name(fd->face, i, &sfnt_name) != 0) {927continue;928}929if (sfnt_name.name_id != TT_NAME_ID_FONT_FAMILY && sfnt_name.name_id != TT_NAME_ID_TYPOGRAPHIC_FAMILY) {930continue;931}932if (!p_font_data->font_name.is_empty() && sfnt_name.language_id != TT_MS_LANGID_ENGLISH_UNITED_STATES) {933continue;934}935936switch (sfnt_name.platform_id) {937case TT_PLATFORM_APPLE_UNICODE: {938p_font_data->font_name.clear();939p_font_data->font_name.append_utf16((const char16_t *)sfnt_name.string, sfnt_name.string_len / 2, false);940} break;941942case TT_PLATFORM_MICROSOFT: {943if (sfnt_name.encoding_id == TT_MS_ID_UNICODE_CS || sfnt_name.encoding_id == TT_MS_ID_UCS_4) {944p_font_data->font_name.clear();945p_font_data->font_name.append_utf16((const char16_t *)sfnt_name.string, sfnt_name.string_len / 2, false);946}947} break;948}949}950}951if (p_font_data->font_name.is_empty() && fd->face->family_name != nullptr) {952p_font_data->font_name = String::utf8((const char *)fd->face->family_name);953}954if (fd->face->style_name != nullptr) {955p_font_data->style_name = String::utf8((const char *)fd->face->style_name);956}957p_font_data->weight = _font_get_weight_by_name(p_font_data->style_name.to_lower());958p_font_data->stretch = _font_get_stretch_by_name(p_font_data->style_name.to_lower());959p_font_data->style_flags = 0;960if ((fd->face->style_flags & FT_STYLE_FLAG_BOLD) || p_font_data->weight >= 700) {961p_font_data->style_flags.set_flag(FONT_BOLD);962}963if ((fd->face->style_flags & FT_STYLE_FLAG_ITALIC) || _is_ital_style(p_font_data->style_name.to_lower())) {964p_font_data->style_flags.set_flag(FONT_ITALIC);965}966if (fd->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) {967p_font_data->style_flags.set_flag(FONT_FIXED_WIDTH);968}969// Read OpenType variations.970p_font_data->supported_varaitions.clear();971if (fd->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {972FT_MM_Var *amaster;973FT_Get_MM_Var(fd->face, &amaster);974for (FT_UInt i = 0; i < amaster->num_axis; i++) {975p_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);976}977FT_Done_MM_Var(ft_library, amaster);978}979p_font_data->face_init = true;980}981982#if defined(MACOS_ENABLED) || defined(IOS_ENABLED)983if (p_font_data->font_name == ".Apple Color Emoji UI" || p_font_data->font_name == "Apple Color Emoji") {984// The baseline offset is missing from the Apple Color Emoji UI font data, so add it manually.985// This issue doesn't occur with other system emoji fonts.986if (!FT_Load_Glyph(fd->face, FT_Get_Char_Index(fd->face, 0x1F92E), FT_LOAD_DEFAULT | FT_LOAD_COLOR)) {987if (fd->face->glyph->metrics.horiBearingY == fd->face->glyph->metrics.height) {988p_font_data->baseline_offset = 0.15;989}990}991}992#endif993994// Write variations.995if (fd->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {996FT_MM_Var *amaster;997998FT_Get_MM_Var(fd->face, &amaster);9991000Vector<FT_Fixed> coords;1001coords.resize(amaster->num_axis);10021003FT_Get_Var_Design_Coordinates(fd->face, coords.size(), coords.ptrw());10041005for (FT_UInt i = 0; i < amaster->num_axis; i++) {1006// Reset to default.1007int32_t var_tag = amaster->axis[i].tag;1008double var_value = (double)amaster->axis[i].def / 65536.0;1009coords.write[i] = amaster->axis[i].def;10101011if (p_font_data->variation_coordinates.has(var_tag)) {1012var_value = p_font_data->variation_coordinates[var_tag];1013coords.write[i] = CLAMP(var_value * 65536.0, amaster->axis[i].minimum, amaster->axis[i].maximum);1014}10151016if (p_font_data->variation_coordinates.has(tag_to_name(var_tag))) {1017var_value = p_font_data->variation_coordinates[tag_to_name(var_tag)];1018coords.write[i] = CLAMP(var_value * 65536.0, amaster->axis[i].minimum, amaster->axis[i].maximum);1019}1020}10211022FT_Set_Var_Design_Coordinates(fd->face, coords.size(), coords.ptrw());1023FT_Done_MM_Var(ft_library, amaster);1024}1025#else1026memdelete(fd);1027if (p_silent) {1028return false;1029} else {1030ERR_FAIL_V_MSG(false, "FreeType: Can't load dynamic font, engine is compiled without FreeType support!");1031}1032#endif1033}10341035fd->owner = p_font_data;1036p_font_data->cache.insert(p_size, fd);1037r_cache_for_size = fd;1038if (p_oversampling != 0) {1039OversamplingLevel *ol = oversampling_levels.getptr(p_oversampling);1040if (ol) {1041fd->viewport_oversampling = p_oversampling;1042ol->fonts.insert(fd);1043}1044}1045return true;1046}10471048void TextServerFallback::_reference_oversampling_level(double p_oversampling) {1049uint32_t oversampling = CLAMP(p_oversampling, 0.1, 100.0) * 64;1050if (oversampling == 64) {1051return;1052}1053OversamplingLevel *ol = oversampling_levels.getptr(oversampling);1054if (ol) {1055ol->refcount++;1056} else {1057OversamplingLevel new_ol;1058oversampling_levels.insert(oversampling, new_ol);1059}1060}10611062void TextServerFallback::_unreference_oversampling_level(double p_oversampling) {1063uint32_t oversampling = CLAMP(p_oversampling, 0.1, 100.0) * 64;1064if (oversampling == 64) {1065return;1066}1067OversamplingLevel *ol = oversampling_levels.getptr(oversampling);1068if (ol) {1069ol->refcount--;1070if (ol->refcount == 0) {1071for (FontForSizeFallback *fd : ol->fonts) {1072fd->owner->cache.erase(fd->size);1073memdelete(fd);1074}1075ol->fonts.clear();1076oversampling_levels.erase(oversampling);1077}1078}1079}10801081_FORCE_INLINE_ bool TextServerFallback::_font_validate(const RID &p_font_rid) const {1082FontFallback *fd = _get_font_data(p_font_rid);1083ERR_FAIL_NULL_V(fd, false);10841085MutexLock lock(fd->mutex);1086Vector2i size = _get_size(fd, 16);1087FontForSizeFallback *ffsd = nullptr;1088return _ensure_cache_for_size(fd, size, ffsd, true);1089}10901091_FORCE_INLINE_ void TextServerFallback::_font_clear_cache(FontFallback *p_font_data) {1092MutexLock ftlock(ft_mutex);10931094for (const KeyValue<Vector2i, FontForSizeFallback *> &E : p_font_data->cache) {1095if (E.value->viewport_oversampling != 0) {1096OversamplingLevel *ol = oversampling_levels.getptr(E.value->viewport_oversampling);1097if (ol) {1098ol->fonts.erase(E.value);1099}1100}1101memdelete(E.value);1102}11031104p_font_data->cache.clear();1105p_font_data->face_init = false;1106p_font_data->supported_varaitions.clear();1107}11081109RID TextServerFallback::_create_font() {1110_THREAD_SAFE_METHOD_11111112FontFallback *fd = memnew(FontFallback);11131114return font_owner.make_rid(fd);1115}11161117RID TextServerFallback::_create_font_linked_variation(const RID &p_font_rid) {1118_THREAD_SAFE_METHOD_11191120RID rid = p_font_rid;1121FontFallbackLinkedVariation *fdv = font_var_owner.get_or_null(rid);1122if (unlikely(fdv)) {1123rid = fdv->base_font;1124}1125ERR_FAIL_COND_V(!font_owner.owns(rid), RID());11261127FontFallbackLinkedVariation *new_fdv = memnew(FontFallbackLinkedVariation);1128new_fdv->base_font = rid;11291130return font_var_owner.make_rid(new_fdv);1131}11321133void TextServerFallback::_font_set_data(const RID &p_font_rid, const PackedByteArray &p_data) {1134FontFallback *fd = _get_font_data(p_font_rid);1135ERR_FAIL_NULL(fd);11361137MutexLock lock(fd->mutex);1138_font_clear_cache(fd);1139fd->data = p_data;1140fd->data_ptr = fd->data.ptr();1141fd->data_size = fd->data.size();1142}11431144void TextServerFallback::_font_set_data_ptr(const RID &p_font_rid, const uint8_t *p_data_ptr, int64_t p_data_size) {1145FontFallback *fd = _get_font_data(p_font_rid);1146ERR_FAIL_NULL(fd);11471148MutexLock lock(fd->mutex);1149_font_clear_cache(fd);1150fd->data.resize(0);1151fd->data_ptr = p_data_ptr;1152fd->data_size = p_data_size;1153}11541155void TextServerFallback::_font_set_style(const RID &p_font_rid, BitField<FontStyle> p_style) {1156FontFallback *fd = _get_font_data(p_font_rid);1157ERR_FAIL_NULL(fd);11581159MutexLock lock(fd->mutex);1160Vector2i size = _get_size(fd, 16);1161FontForSizeFallback *ffsd = nullptr;1162ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));1163fd->style_flags = p_style;1164}11651166void TextServerFallback::_font_set_face_index(const RID &p_font_rid, int64_t p_face_index) {1167ERR_FAIL_COND(p_face_index < 0);1168ERR_FAIL_COND(p_face_index >= 0x7FFF);11691170FontFallback *fd = _get_font_data(p_font_rid);1171ERR_FAIL_NULL(fd);11721173MutexLock lock(fd->mutex);1174if (fd->face_index != p_face_index) {1175fd->face_index = p_face_index;1176_font_clear_cache(fd);1177}1178}11791180int64_t TextServerFallback::_font_get_face_index(const RID &p_font_rid) const {1181FontFallback *fd = _get_font_data(p_font_rid);1182ERR_FAIL_NULL_V(fd, 0);11831184MutexLock lock(fd->mutex);1185return fd->face_index;1186}11871188int64_t TextServerFallback::_font_get_face_count(const RID &p_font_rid) const {1189FontFallback *fd = _get_font_data(p_font_rid);1190ERR_FAIL_NULL_V(fd, 0);11911192MutexLock lock(fd->mutex);1193int face_count = 0;11941195if (fd->data_ptr && (fd->data_size > 0)) {1196// Init dynamic font.1197#ifdef MODULE_FREETYPE_ENABLED1198int error = 0;1199if (!ft_library) {1200error = FT_Init_FreeType(&ft_library);1201ERR_FAIL_COND_V_MSG(error != 0, false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");1202#ifdef MODULE_SVG_ENABLED1203FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks());1204#endif1205}12061207FT_StreamRec stream;1208memset(&stream, 0, sizeof(FT_StreamRec));1209stream.base = (unsigned char *)fd->data_ptr;1210stream.size = fd->data_size;1211stream.pos = 0;12121213FT_Open_Args fargs;1214memset(&fargs, 0, sizeof(FT_Open_Args));1215fargs.memory_base = (unsigned char *)fd->data_ptr;1216fargs.memory_size = fd->data_size;1217fargs.flags = FT_OPEN_MEMORY;1218fargs.stream = &stream;12191220MutexLock ftlock(ft_mutex);12211222FT_Face tmp_face = nullptr;1223error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);1224if (error == 0) {1225face_count = tmp_face->num_faces;1226FT_Done_Face(tmp_face);1227}1228#endif1229}12301231return face_count;1232}12331234BitField<TextServer::FontStyle> TextServerFallback::_font_get_style(const RID &p_font_rid) const {1235FontFallback *fd = _get_font_data(p_font_rid);1236ERR_FAIL_NULL_V(fd, 0);12371238MutexLock lock(fd->mutex);1239Vector2i size = _get_size(fd, 16);1240FontForSizeFallback *ffsd = nullptr;1241ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0);1242return fd->style_flags;1243}12441245void TextServerFallback::_font_set_style_name(const RID &p_font_rid, const String &p_name) {1246FontFallback *fd = _get_font_data(p_font_rid);1247ERR_FAIL_NULL(fd);12481249MutexLock lock(fd->mutex);1250Vector2i size = _get_size(fd, 16);1251FontForSizeFallback *ffsd = nullptr;1252ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));1253fd->style_name = p_name;1254}12551256String TextServerFallback::_font_get_style_name(const RID &p_font_rid) const {1257FontFallback *fd = _get_font_data(p_font_rid);1258ERR_FAIL_NULL_V(fd, String());12591260MutexLock lock(fd->mutex);1261Vector2i size = _get_size(fd, 16);1262FontForSizeFallback *ffsd = nullptr;1263ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), String());1264return fd->style_name;1265}12661267void TextServerFallback::_font_set_weight(const RID &p_font_rid, int64_t p_weight) {1268FontFallback *fd = _get_font_data(p_font_rid);1269ERR_FAIL_NULL(fd);12701271MutexLock lock(fd->mutex);1272Vector2i size = _get_size(fd, 16);1273FontForSizeFallback *ffsd = nullptr;1274ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));1275fd->weight = CLAMP(p_weight, 100, 999);1276}12771278int64_t TextServerFallback::_font_get_weight(const RID &p_font_rid) const {1279FontFallback *fd = _get_font_data(p_font_rid);1280ERR_FAIL_NULL_V(fd, 400);12811282MutexLock lock(fd->mutex);1283Vector2i size = _get_size(fd, 16);1284FontForSizeFallback *ffsd = nullptr;1285ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 400);1286return fd->weight;1287}12881289void TextServerFallback::_font_set_stretch(const RID &p_font_rid, int64_t p_stretch) {1290FontFallback *fd = _get_font_data(p_font_rid);1291ERR_FAIL_NULL(fd);12921293MutexLock lock(fd->mutex);1294Vector2i size = _get_size(fd, 16);1295FontForSizeFallback *ffsd = nullptr;1296ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));1297fd->stretch = CLAMP(p_stretch, 50, 200);1298}12991300int64_t TextServerFallback::_font_get_stretch(const RID &p_font_rid) const {1301FontFallback *fd = _get_font_data(p_font_rid);1302ERR_FAIL_NULL_V(fd, 100);13031304MutexLock lock(fd->mutex);1305Vector2i size = _get_size(fd, 16);1306FontForSizeFallback *ffsd = nullptr;1307ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 100);1308return fd->stretch;1309}13101311void TextServerFallback::_font_set_name(const RID &p_font_rid, const String &p_name) {1312FontFallback *fd = _get_font_data(p_font_rid);1313ERR_FAIL_NULL(fd);13141315MutexLock lock(fd->mutex);1316Vector2i size = _get_size(fd, 16);1317FontForSizeFallback *ffsd = nullptr;1318ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));1319fd->font_name = p_name;1320}13211322String TextServerFallback::_font_get_name(const RID &p_font_rid) const {1323FontFallback *fd = _get_font_data(p_font_rid);1324ERR_FAIL_NULL_V(fd, String());13251326MutexLock lock(fd->mutex);1327Vector2i size = _get_size(fd, 16);1328FontForSizeFallback *ffsd = nullptr;1329ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), String());1330return fd->font_name;1331}13321333void TextServerFallback::_font_set_antialiasing(const RID &p_font_rid, TextServer::FontAntialiasing p_antialiasing) {1334FontFallback *fd = _get_font_data(p_font_rid);1335ERR_FAIL_NULL(fd);13361337MutexLock lock(fd->mutex);1338if (fd->antialiasing != p_antialiasing) {1339_font_clear_cache(fd);1340fd->antialiasing = p_antialiasing;1341}1342}13431344TextServer::FontAntialiasing TextServerFallback::_font_get_antialiasing(const RID &p_font_rid) const {1345FontFallback *fd = _get_font_data(p_font_rid);1346ERR_FAIL_NULL_V(fd, TextServer::FONT_ANTIALIASING_NONE);13471348MutexLock lock(fd->mutex);1349return fd->antialiasing;1350}13511352void TextServerFallback::_font_set_disable_embedded_bitmaps(const RID &p_font_rid, bool p_disable_embedded_bitmaps) {1353FontFallback *fd = _get_font_data(p_font_rid);1354ERR_FAIL_NULL(fd);13551356MutexLock lock(fd->mutex);1357if (fd->disable_embedded_bitmaps != p_disable_embedded_bitmaps) {1358_font_clear_cache(fd);1359fd->disable_embedded_bitmaps = p_disable_embedded_bitmaps;1360}1361}13621363bool TextServerFallback::_font_get_disable_embedded_bitmaps(const RID &p_font_rid) const {1364FontFallback *fd = _get_font_data(p_font_rid);1365ERR_FAIL_NULL_V(fd, false);13661367MutexLock lock(fd->mutex);1368return fd->disable_embedded_bitmaps;1369}13701371void TextServerFallback::_font_set_generate_mipmaps(const RID &p_font_rid, bool p_generate_mipmaps) {1372FontFallback *fd = _get_font_data(p_font_rid);1373ERR_FAIL_NULL(fd);13741375MutexLock lock(fd->mutex);1376if (fd->mipmaps != p_generate_mipmaps) {1377for (KeyValue<Vector2i, FontForSizeFallback *> &E : fd->cache) {1378for (int i = 0; i < E.value->textures.size(); i++) {1379E.value->textures.write[i].dirty = true;1380E.value->textures.write[i].texture = Ref<ImageTexture>();1381}1382}1383fd->mipmaps = p_generate_mipmaps;1384}1385}13861387bool TextServerFallback::_font_get_generate_mipmaps(const RID &p_font_rid) const {1388FontFallback *fd = _get_font_data(p_font_rid);1389ERR_FAIL_NULL_V(fd, false);13901391MutexLock lock(fd->mutex);1392return fd->mipmaps;1393}13941395void TextServerFallback::_font_set_multichannel_signed_distance_field(const RID &p_font_rid, bool p_msdf) {1396FontFallback *fd = _get_font_data(p_font_rid);1397ERR_FAIL_NULL(fd);13981399MutexLock lock(fd->mutex);1400if (fd->msdf != p_msdf) {1401_font_clear_cache(fd);1402fd->msdf = p_msdf;1403}1404}14051406bool TextServerFallback::_font_is_multichannel_signed_distance_field(const RID &p_font_rid) const {1407FontFallback *fd = _get_font_data(p_font_rid);1408ERR_FAIL_NULL_V(fd, false);14091410MutexLock lock(fd->mutex);1411return fd->msdf;1412}14131414void TextServerFallback::_font_set_msdf_pixel_range(const RID &p_font_rid, int64_t p_msdf_pixel_range) {1415FontFallback *fd = _get_font_data(p_font_rid);1416ERR_FAIL_NULL(fd);14171418MutexLock lock(fd->mutex);1419if (fd->msdf_range != p_msdf_pixel_range) {1420_font_clear_cache(fd);1421fd->msdf_range = p_msdf_pixel_range;1422}1423}14241425int64_t TextServerFallback::_font_get_msdf_pixel_range(const RID &p_font_rid) const {1426FontFallback *fd = _get_font_data(p_font_rid);1427ERR_FAIL_NULL_V(fd, false);14281429MutexLock lock(fd->mutex);1430return fd->msdf_range;1431}14321433void TextServerFallback::_font_set_msdf_size(const RID &p_font_rid, int64_t p_msdf_size) {1434FontFallback *fd = _get_font_data(p_font_rid);1435ERR_FAIL_NULL(fd);14361437MutexLock lock(fd->mutex);1438if (fd->msdf_source_size != p_msdf_size) {1439_font_clear_cache(fd);1440fd->msdf_source_size = p_msdf_size;1441}1442}14431444int64_t TextServerFallback::_font_get_msdf_size(const RID &p_font_rid) const {1445FontFallback *fd = _get_font_data(p_font_rid);1446ERR_FAIL_NULL_V(fd, 0);14471448MutexLock lock(fd->mutex);1449return fd->msdf_source_size;1450}14511452void TextServerFallback::_font_set_fixed_size(const RID &p_font_rid, int64_t p_fixed_size) {1453FontFallback *fd = _get_font_data(p_font_rid);1454ERR_FAIL_NULL(fd);14551456MutexLock lock(fd->mutex);1457fd->fixed_size = p_fixed_size;1458}14591460int64_t TextServerFallback::_font_get_fixed_size(const RID &p_font_rid) const {1461FontFallback *fd = _get_font_data(p_font_rid);1462ERR_FAIL_NULL_V(fd, 0);14631464MutexLock lock(fd->mutex);1465return fd->fixed_size;1466}14671468void TextServerFallback::_font_set_fixed_size_scale_mode(const RID &p_font_rid, TextServer::FixedSizeScaleMode p_fixed_size_scale_mode) {1469FontFallback *fd = _get_font_data(p_font_rid);1470ERR_FAIL_NULL(fd);14711472MutexLock lock(fd->mutex);1473fd->fixed_size_scale_mode = p_fixed_size_scale_mode;1474}14751476TextServer::FixedSizeScaleMode TextServerFallback::_font_get_fixed_size_scale_mode(const RID &p_font_rid) const {1477FontFallback *fd = _get_font_data(p_font_rid);1478ERR_FAIL_NULL_V(fd, FIXED_SIZE_SCALE_DISABLE);14791480MutexLock lock(fd->mutex);1481return fd->fixed_size_scale_mode;1482}14831484void TextServerFallback::_font_set_allow_system_fallback(const RID &p_font_rid, bool p_allow_system_fallback) {1485FontFallback *fd = _get_font_data(p_font_rid);1486ERR_FAIL_NULL(fd);14871488MutexLock lock(fd->mutex);1489fd->allow_system_fallback = p_allow_system_fallback;1490}14911492bool TextServerFallback::_font_is_allow_system_fallback(const RID &p_font_rid) const {1493FontFallback *fd = _get_font_data(p_font_rid);1494ERR_FAIL_NULL_V(fd, false);14951496MutexLock lock(fd->mutex);1497return fd->allow_system_fallback;1498}14991500void TextServerFallback::_font_set_force_autohinter(const RID &p_font_rid, bool p_force_autohinter) {1501FontFallback *fd = _get_font_data(p_font_rid);1502ERR_FAIL_NULL(fd);15031504MutexLock lock(fd->mutex);1505if (fd->force_autohinter != p_force_autohinter) {1506_font_clear_cache(fd);1507fd->force_autohinter = p_force_autohinter;1508}1509}15101511bool TextServerFallback::_font_is_force_autohinter(const RID &p_font_rid) const {1512FontFallback *fd = _get_font_data(p_font_rid);1513ERR_FAIL_NULL_V(fd, false);15141515MutexLock lock(fd->mutex);1516return fd->force_autohinter;1517}15181519void TextServerFallback::_font_set_modulate_color_glyphs(const RID &p_font_rid, bool p_modulate) {1520FontFallback *fd = _get_font_data(p_font_rid);1521ERR_FAIL_NULL(fd);15221523MutexLock lock(fd->mutex);1524if (fd->modulate_color_glyphs != p_modulate) {1525fd->modulate_color_glyphs = p_modulate;1526}1527}15281529bool TextServerFallback::_font_is_modulate_color_glyphs(const RID &p_font_rid) const {1530FontFallback *fd = _get_font_data(p_font_rid);1531ERR_FAIL_NULL_V(fd, false);15321533MutexLock lock(fd->mutex);1534return fd->modulate_color_glyphs;1535}15361537void TextServerFallback::_font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) {1538FontFallback *fd = _get_font_data(p_font_rid);1539ERR_FAIL_NULL(fd);15401541MutexLock lock(fd->mutex);1542if (fd->hinting != p_hinting) {1543_font_clear_cache(fd);1544fd->hinting = p_hinting;1545}1546}15471548TextServer::Hinting TextServerFallback::_font_get_hinting(const RID &p_font_rid) const {1549FontFallback *fd = _get_font_data(p_font_rid);1550ERR_FAIL_NULL_V(fd, HINTING_NONE);15511552MutexLock lock(fd->mutex);1553return fd->hinting;1554}15551556void TextServerFallback::_font_set_subpixel_positioning(const RID &p_font_rid, TextServer::SubpixelPositioning p_subpixel) {1557FontFallback *fd = _get_font_data(p_font_rid);1558ERR_FAIL_NULL(fd);15591560MutexLock lock(fd->mutex);1561fd->subpixel_positioning = p_subpixel;1562}15631564TextServer::SubpixelPositioning TextServerFallback::_font_get_subpixel_positioning(const RID &p_font_rid) const {1565FontFallback *fd = _get_font_data(p_font_rid);1566ERR_FAIL_NULL_V(fd, SUBPIXEL_POSITIONING_DISABLED);15671568MutexLock lock(fd->mutex);1569return fd->subpixel_positioning;1570}15711572void TextServerFallback::_font_set_keep_rounding_remainders(const RID &p_font_rid, bool p_keep_rounding_remainders) {1573FontFallback *fd = _get_font_data(p_font_rid);1574ERR_FAIL_NULL(fd);15751576MutexLock lock(fd->mutex);1577fd->keep_rounding_remainders = p_keep_rounding_remainders;1578}15791580bool TextServerFallback::_font_get_keep_rounding_remainders(const RID &p_font_rid) const {1581FontFallback *fd = _get_font_data(p_font_rid);1582ERR_FAIL_NULL_V(fd, false);15831584MutexLock lock(fd->mutex);1585return fd->keep_rounding_remainders;1586}15871588void TextServerFallback::_font_set_embolden(const RID &p_font_rid, double p_strength) {1589FontFallback *fd = _get_font_data(p_font_rid);1590ERR_FAIL_NULL(fd);15911592MutexLock lock(fd->mutex);1593if (fd->embolden != p_strength) {1594_font_clear_cache(fd);1595fd->embolden = p_strength;1596}1597}15981599double TextServerFallback::_font_get_embolden(const RID &p_font_rid) const {1600FontFallback *fd = _get_font_data(p_font_rid);1601ERR_FAIL_NULL_V(fd, 0.0);16021603MutexLock lock(fd->mutex);1604return fd->embolden;1605}16061607void TextServerFallback::_font_set_spacing(const RID &p_font_rid, SpacingType p_spacing, int64_t p_value) {1608ERR_FAIL_INDEX((int)p_spacing, 4);1609FontFallbackLinkedVariation *fdv = font_var_owner.get_or_null(p_font_rid);1610if (fdv) {1611if (fdv->extra_spacing[p_spacing] != p_value) {1612fdv->extra_spacing[p_spacing] = p_value;1613}1614} else {1615FontFallback *fd = font_owner.get_or_null(p_font_rid);1616ERR_FAIL_NULL(fd);16171618MutexLock lock(fd->mutex);1619if (fd->extra_spacing[p_spacing] != p_value) {1620_font_clear_cache(fd);1621fd->extra_spacing[p_spacing] = p_value;1622}1623}1624}16251626int64_t TextServerFallback::_font_get_spacing(const RID &p_font_rid, SpacingType p_spacing) const {1627ERR_FAIL_INDEX_V((int)p_spacing, 4, 0);1628FontFallbackLinkedVariation *fdv = font_var_owner.get_or_null(p_font_rid);1629if (fdv) {1630return fdv->extra_spacing[p_spacing];1631} else {1632FontFallback *fd = font_owner.get_or_null(p_font_rid);1633ERR_FAIL_NULL_V(fd, 0);16341635MutexLock lock(fd->mutex);1636return fd->extra_spacing[p_spacing];1637}1638}16391640void TextServerFallback::_font_set_baseline_offset(const RID &p_font_rid, double p_baseline_offset) {1641FontFallbackLinkedVariation *fdv = font_var_owner.get_or_null(p_font_rid);1642if (fdv) {1643if (fdv->baseline_offset != p_baseline_offset) {1644fdv->baseline_offset = p_baseline_offset;1645}1646} else {1647FontFallback *fd = font_owner.get_or_null(p_font_rid);1648ERR_FAIL_NULL(fd);16491650MutexLock lock(fd->mutex);1651if (fd->baseline_offset != p_baseline_offset) {1652_font_clear_cache(fd);1653fd->baseline_offset = p_baseline_offset;1654}1655}1656}16571658double TextServerFallback::_font_get_baseline_offset(const RID &p_font_rid) const {1659FontFallbackLinkedVariation *fdv = font_var_owner.get_or_null(p_font_rid);1660if (fdv) {1661return fdv->baseline_offset;1662} else {1663FontFallback *fd = font_owner.get_or_null(p_font_rid);1664ERR_FAIL_NULL_V(fd, 0.0);16651666MutexLock lock(fd->mutex);1667return fd->baseline_offset;1668}1669}16701671void TextServerFallback::_font_set_transform(const RID &p_font_rid, const Transform2D &p_transform) {1672FontFallback *fd = _get_font_data(p_font_rid);1673ERR_FAIL_NULL(fd);16741675MutexLock lock(fd->mutex);1676if (fd->transform != p_transform) {1677_font_clear_cache(fd);1678fd->transform = p_transform;1679}1680}16811682Transform2D TextServerFallback::_font_get_transform(const RID &p_font_rid) const {1683FontFallback *fd = _get_font_data(p_font_rid);1684ERR_FAIL_NULL_V(fd, Transform2D());16851686MutexLock lock(fd->mutex);1687return fd->transform;1688}16891690void TextServerFallback::_font_set_variation_coordinates(const RID &p_font_rid, const Dictionary &p_variation_coordinates) {1691FontFallback *fd = _get_font_data(p_font_rid);1692ERR_FAIL_NULL(fd);16931694MutexLock lock(fd->mutex);1695if (!fd->variation_coordinates.recursive_equal(p_variation_coordinates, 1)) {1696_font_clear_cache(fd);1697fd->variation_coordinates = p_variation_coordinates.duplicate();1698}1699}17001701double TextServerFallback::_font_get_oversampling(const RID &p_font_rid) const {1702FontFallback *fd = _get_font_data(p_font_rid);1703ERR_FAIL_NULL_V(fd, -1.0);17041705MutexLock lock(fd->mutex);1706return fd->oversampling_override;1707}17081709void TextServerFallback::_font_set_oversampling(const RID &p_font_rid, double p_oversampling) {1710FontFallback *fd = _get_font_data(p_font_rid);1711ERR_FAIL_NULL(fd);17121713MutexLock lock(fd->mutex);1714if (fd->oversampling_override != p_oversampling) {1715_font_clear_cache(fd);1716fd->oversampling_override = p_oversampling;1717}1718}17191720Dictionary TextServerFallback::_font_get_variation_coordinates(const RID &p_font_rid) const {1721FontFallback *fd = _get_font_data(p_font_rid);1722ERR_FAIL_NULL_V(fd, Dictionary());17231724MutexLock lock(fd->mutex);1725return fd->variation_coordinates;1726}17271728TypedArray<Vector2i> TextServerFallback::_font_get_size_cache_list(const RID &p_font_rid) const {1729FontFallback *fd = _get_font_data(p_font_rid);1730ERR_FAIL_NULL_V(fd, TypedArray<Vector2i>());17311732MutexLock lock(fd->mutex);1733TypedArray<Vector2i> ret;1734for (const KeyValue<Vector2i, FontForSizeFallback *> &E : fd->cache) {1735if ((E.key.x % 64 == 0) && (E.value->viewport_oversampling == 0)) {1736ret.push_back(Vector2i(E.key.x / 64, E.key.y));1737}1738}1739return ret;1740}17411742TypedArray<Dictionary> TextServerFallback::_font_get_size_cache_info(const RID &p_font_rid) const {1743FontFallback *fd = _get_font_data(p_font_rid);1744ERR_FAIL_NULL_V(fd, TypedArray<Dictionary>());17451746MutexLock lock(fd->mutex);1747TypedArray<Dictionary> ret;1748for (const KeyValue<Vector2i, FontForSizeFallback *> &E : fd->cache) {1749Dictionary size_info;1750size_info["size_px"] = Vector2i(E.key.x / 64, E.key.y);1751if (E.value->viewport_oversampling) {1752size_info["viewport_oversampling"] = double(E.value->viewport_oversampling) / 64.0;1753}1754size_info["glyphs"] = E.value->glyph_map.size();1755size_info["textures"] = E.value->textures.size();1756uint64_t sz = 0;1757for (const ShelfPackTexture &tx : E.value->textures) {1758sz += tx.image->get_data_size() * 2;1759}1760size_info["textures_size"] = sz;1761ret.push_back(size_info);1762}17631764return ret;1765}17661767void TextServerFallback::_font_clear_size_cache(const RID &p_font_rid) {1768FontFallback *fd = _get_font_data(p_font_rid);1769ERR_FAIL_NULL(fd);17701771MutexLock lock(fd->mutex);1772MutexLock ftlock(ft_mutex);1773for (const KeyValue<Vector2i, FontForSizeFallback *> &E : fd->cache) {1774if (E.value->viewport_oversampling != 0) {1775OversamplingLevel *ol = oversampling_levels.getptr(E.value->viewport_oversampling);1776if (ol) {1777ol->fonts.erase(E.value);1778}1779}1780memdelete(E.value);1781}1782fd->cache.clear();1783}17841785void TextServerFallback::_font_remove_size_cache(const RID &p_font_rid, const Vector2i &p_size) {1786FontFallback *fd = _get_font_data(p_font_rid);1787ERR_FAIL_NULL(fd);17881789MutexLock lock(fd->mutex);1790MutexLock ftlock(ft_mutex);1791Vector2i size = Vector2i(p_size.x * 64, p_size.y);1792if (fd->cache.has(size)) {1793if (fd->cache[size]->viewport_oversampling != 0) {1794OversamplingLevel *ol = oversampling_levels.getptr(fd->cache[size]->viewport_oversampling);1795if (ol) {1796ol->fonts.erase(fd->cache[size]);1797}1798}1799memdelete(fd->cache[size]);1800fd->cache.erase(size);1801}1802}18031804void TextServerFallback::_font_set_ascent(const RID &p_font_rid, int64_t p_size, double p_ascent) {1805FontFallback *fd = _get_font_data(p_font_rid);1806ERR_FAIL_NULL(fd);18071808MutexLock lock(fd->mutex);1809Vector2i size = _get_size(fd, p_size);18101811FontForSizeFallback *ffsd = nullptr;1812ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));1813ffsd->ascent = p_ascent;1814}18151816double TextServerFallback::_font_get_ascent(const RID &p_font_rid, int64_t p_size) const {1817FontFallback *fd = _get_font_data(p_font_rid);1818ERR_FAIL_NULL_V(fd, 0.0);18191820MutexLock lock(fd->mutex);1821Vector2i size = _get_size(fd, p_size);18221823FontForSizeFallback *ffsd = nullptr;1824ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0.0);18251826if (fd->msdf) {1827return ffsd->ascent * (double)p_size / (double)fd->msdf_source_size;1828} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size * 64) {1829if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {1830return ffsd->ascent * (double)p_size / (double)fd->fixed_size;1831} else {1832return ffsd->ascent * Math::round((double)p_size / (double)fd->fixed_size);1833}1834} else {1835return ffsd->ascent;1836}1837}18381839void TextServerFallback::_font_set_descent(const RID &p_font_rid, int64_t p_size, double p_descent) {1840FontFallback *fd = _get_font_data(p_font_rid);1841ERR_FAIL_NULL(fd);18421843Vector2i size = _get_size(fd, p_size);18441845FontForSizeFallback *ffsd = nullptr;1846ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));1847ffsd->descent = p_descent;1848}18491850double TextServerFallback::_font_get_descent(const RID &p_font_rid, int64_t p_size) const {1851FontFallback *fd = _get_font_data(p_font_rid);1852ERR_FAIL_NULL_V(fd, 0.0);18531854MutexLock lock(fd->mutex);1855Vector2i size = _get_size(fd, p_size);18561857FontForSizeFallback *ffsd = nullptr;1858ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0.0);18591860if (fd->msdf) {1861return ffsd->descent * (double)p_size / (double)fd->msdf_source_size;1862} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size * 64) {1863if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {1864return ffsd->descent * (double)p_size / (double)fd->fixed_size;1865} else {1866return ffsd->descent * Math::round((double)p_size / (double)fd->fixed_size);1867}1868} else {1869return ffsd->descent;1870}1871}18721873void TextServerFallback::_font_set_underline_position(const RID &p_font_rid, int64_t p_size, double p_underline_position) {1874FontFallback *fd = _get_font_data(p_font_rid);1875ERR_FAIL_NULL(fd);18761877MutexLock lock(fd->mutex);1878Vector2i size = _get_size(fd, p_size);18791880FontForSizeFallback *ffsd = nullptr;1881ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));1882ffsd->underline_position = p_underline_position;1883}18841885double TextServerFallback::_font_get_underline_position(const RID &p_font_rid, int64_t p_size) const {1886FontFallback *fd = _get_font_data(p_font_rid);1887ERR_FAIL_NULL_V(fd, 0.0);18881889MutexLock lock(fd->mutex);1890Vector2i size = _get_size(fd, p_size);18911892FontForSizeFallback *ffsd = nullptr;1893ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0.0);18941895if (fd->msdf) {1896return ffsd->underline_position * (double)p_size / (double)fd->msdf_source_size;1897} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size * 64) {1898if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {1899return ffsd->underline_position * (double)p_size / (double)fd->fixed_size;1900} else {1901return ffsd->underline_position * Math::round((double)p_size / (double)fd->fixed_size);1902}1903} else {1904return ffsd->underline_position;1905}1906}19071908void TextServerFallback::_font_set_underline_thickness(const RID &p_font_rid, int64_t p_size, double p_underline_thickness) {1909FontFallback *fd = _get_font_data(p_font_rid);1910ERR_FAIL_NULL(fd);19111912MutexLock lock(fd->mutex);1913Vector2i size = _get_size(fd, p_size);19141915FontForSizeFallback *ffsd = nullptr;1916ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));1917ffsd->underline_thickness = p_underline_thickness;1918}19191920double TextServerFallback::_font_get_underline_thickness(const RID &p_font_rid, int64_t p_size) const {1921FontFallback *fd = _get_font_data(p_font_rid);1922ERR_FAIL_NULL_V(fd, 0.0);19231924MutexLock lock(fd->mutex);1925Vector2i size = _get_size(fd, p_size);19261927FontForSizeFallback *ffsd = nullptr;1928ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0.0);19291930if (fd->msdf) {1931return ffsd->underline_thickness * (double)p_size / (double)fd->msdf_source_size;1932} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size * 64) {1933if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {1934return ffsd->underline_thickness * (double)p_size / (double)fd->fixed_size;1935} else {1936return ffsd->underline_thickness * Math::round((double)p_size / (double)fd->fixed_size);1937}1938} else {1939return ffsd->underline_thickness;1940}1941}19421943void TextServerFallback::_font_set_scale(const RID &p_font_rid, int64_t p_size, double p_scale) {1944FontFallback *fd = _get_font_data(p_font_rid);1945ERR_FAIL_NULL(fd);19461947MutexLock lock(fd->mutex);1948Vector2i size = _get_size(fd, p_size);19491950FontForSizeFallback *ffsd = nullptr;1951ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));1952#ifdef MODULE_FREETYPE_ENABLED1953if (ffsd->face) {1954return; // Do not override scale for dynamic fonts, it's calculated automatically.1955}1956#endif1957ffsd->scale = p_scale;1958}19591960double TextServerFallback::_font_get_scale(const RID &p_font_rid, int64_t p_size) const {1961FontFallback *fd = _get_font_data(p_font_rid);1962ERR_FAIL_NULL_V(fd, 0.0);19631964MutexLock lock(fd->mutex);1965Vector2i size = _get_size(fd, p_size);19661967FontForSizeFallback *ffsd = nullptr;1968ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0.0);19691970if (fd->msdf) {1971return ffsd->scale * (double)p_size / (double)fd->msdf_source_size;1972} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size * 64) {1973if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {1974return ffsd->scale * (double)p_size / (double)fd->fixed_size;1975} else {1976return ffsd->scale * Math::round((double)p_size / (double)fd->fixed_size);1977}1978} else {1979return ffsd->scale;1980}1981}19821983int64_t TextServerFallback::_font_get_texture_count(const RID &p_font_rid, const Vector2i &p_size) const {1984FontFallback *fd = _get_font_data(p_font_rid);1985ERR_FAIL_NULL_V(fd, 0);19861987MutexLock lock(fd->mutex);1988Vector2i size = _get_size_outline(fd, p_size);19891990FontForSizeFallback *ffsd = nullptr;1991ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), 0);19921993return ffsd->textures.size();1994}19951996void TextServerFallback::_font_clear_textures(const RID &p_font_rid, const Vector2i &p_size) {1997FontFallback *fd = _get_font_data(p_font_rid);1998ERR_FAIL_NULL(fd);1999MutexLock lock(fd->mutex);2000Vector2i size = _get_size_outline(fd, p_size);20012002FontForSizeFallback *ffsd = nullptr;2003ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));2004ffsd->textures.clear();2005}20062007void TextServerFallback::_font_remove_texture(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) {2008FontFallback *fd = _get_font_data(p_font_rid);2009ERR_FAIL_NULL(fd);20102011MutexLock lock(fd->mutex);2012Vector2i size = _get_size_outline(fd, p_size);2013FontForSizeFallback *ffsd = nullptr;2014ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));2015ERR_FAIL_INDEX(p_texture_index, ffsd->textures.size());20162017ffsd->textures.remove_at(p_texture_index);2018}20192020void TextServerFallback::_font_set_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const Ref<Image> &p_image) {2021FontFallback *fd = _get_font_data(p_font_rid);2022ERR_FAIL_NULL(fd);2023ERR_FAIL_COND(p_image.is_null());20242025MutexLock lock(fd->mutex);2026Vector2i size = _get_size_outline(fd, p_size);2027FontForSizeFallback *ffsd = nullptr;2028ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));2029ERR_FAIL_COND(p_texture_index < 0);2030if (p_texture_index >= ffsd->textures.size()) {2031ffsd->textures.resize(p_texture_index + 1);2032}20332034ShelfPackTexture &tex = ffsd->textures.write[p_texture_index];20352036tex.image = p_image;2037tex.texture_w = p_image->get_width();2038tex.texture_h = p_image->get_height();20392040Ref<Image> img = p_image;2041if (fd->mipmaps && !img->has_mipmaps()) {2042img = p_image->duplicate();2043img->generate_mipmaps();2044}2045tex.texture = ImageTexture::create_from_image(img);2046tex.dirty = false;2047}20482049Ref<Image> TextServerFallback::_font_get_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const {2050FontFallback *fd = _get_font_data(p_font_rid);2051ERR_FAIL_NULL_V(fd, Ref<Image>());20522053MutexLock lock(fd->mutex);2054Vector2i size = _get_size_outline(fd, p_size);2055FontForSizeFallback *ffsd = nullptr;2056ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Ref<Image>());2057ERR_FAIL_INDEX_V(p_texture_index, ffsd->textures.size(), Ref<Image>());20582059const ShelfPackTexture &tex = ffsd->textures[p_texture_index];2060return tex.image;2061}20622063void TextServerFallback::_font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offsets) {2064ERR_FAIL_COND(p_offsets.size() % 4 != 0);2065FontFallback *fd = _get_font_data(p_font_rid);2066ERR_FAIL_NULL(fd);20672068MutexLock lock(fd->mutex);2069Vector2i size = _get_size_outline(fd, p_size);2070FontForSizeFallback *ffsd = nullptr;2071ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));2072ERR_FAIL_COND(p_texture_index < 0);2073if (p_texture_index >= ffsd->textures.size()) {2074ffsd->textures.resize(p_texture_index + 1);2075}20762077ShelfPackTexture &tex = ffsd->textures.write[p_texture_index];2078tex.shelves.clear();2079for (int32_t i = 0; i < p_offsets.size(); i += 4) {2080tex.shelves.push_back(Shelf(p_offsets[i], p_offsets[i + 1], p_offsets[i + 2], p_offsets[i + 3]));2081}2082}20832084PackedInt32Array TextServerFallback::_font_get_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const {2085FontFallback *fd = _get_font_data(p_font_rid);2086ERR_FAIL_NULL_V(fd, PackedInt32Array());20872088MutexLock lock(fd->mutex);2089Vector2i size = _get_size_outline(fd, p_size);2090FontForSizeFallback *ffsd = nullptr;2091ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), PackedInt32Array());2092ERR_FAIL_INDEX_V(p_texture_index, ffsd->textures.size(), PackedInt32Array());20932094const ShelfPackTexture &tex = ffsd->textures[p_texture_index];2095PackedInt32Array ret;2096ret.resize(tex.shelves.size() * 4);20972098int32_t *wr = ret.ptrw();2099int32_t i = 0;2100for (const Shelf &E : tex.shelves) {2101wr[i * 4] = E.x;2102wr[i * 4 + 1] = E.y;2103wr[i * 4 + 2] = E.w;2104wr[i * 4 + 3] = E.h;2105i++;2106}2107return ret;2108}21092110PackedInt32Array TextServerFallback::_font_get_glyph_list(const RID &p_font_rid, const Vector2i &p_size) const {2111FontFallback *fd = _get_font_data(p_font_rid);2112ERR_FAIL_NULL_V(fd, PackedInt32Array());21132114MutexLock lock(fd->mutex);2115Vector2i size = _get_size_outline(fd, p_size);2116FontForSizeFallback *ffsd = nullptr;2117ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), PackedInt32Array());21182119PackedInt32Array ret;2120const HashMap<int32_t, FontGlyph> &gl = ffsd->glyph_map;2121for (const KeyValue<int32_t, FontGlyph> &E : gl) {2122ret.push_back(E.key);2123}2124return ret;2125}21262127void TextServerFallback::_font_clear_glyphs(const RID &p_font_rid, const Vector2i &p_size) {2128FontFallback *fd = _get_font_data(p_font_rid);2129ERR_FAIL_NULL(fd);21302131MutexLock lock(fd->mutex);2132Vector2i size = _get_size_outline(fd, p_size);2133FontForSizeFallback *ffsd = nullptr;2134ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));21352136ffsd->glyph_map.clear();2137}21382139void TextServerFallback::_font_remove_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) {2140FontFallback *fd = _get_font_data(p_font_rid);2141ERR_FAIL_NULL(fd);21422143MutexLock lock(fd->mutex);2144Vector2i size = _get_size_outline(fd, p_size);2145FontForSizeFallback *ffsd = nullptr;2146ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));21472148ffsd->glyph_map.erase(p_glyph);2149}21502151Vector2 TextServerFallback::_font_get_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph) const {2152FontFallback *fd = _get_font_data(p_font_rid);2153ERR_FAIL_NULL_V(fd, Vector2());21542155MutexLock lock(fd->mutex);2156Vector2i size = _get_size(fd, p_size);21572158FontForSizeFallback *ffsd = nullptr;2159ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Vector2());21602161int mod = 0;2162if (fd->antialiasing == FONT_ANTIALIASING_LCD) {2163TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get();2164if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {2165mod = (layout << 24);2166}2167}21682169FontGlyph fgl;2170if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) {2171return Vector2(); // Invalid or non graphicl glyph, do not display errors.2172}21732174Vector2 ea;2175if (fd->embolden != 0.0) {2176ea.x = fd->embolden * double(size.x) / 4096.0;2177}21782179double scale = _font_get_scale(p_font_rid, p_size);2180if (fd->msdf) {2181return (fgl.advance + ea) * (double)p_size / (double)fd->msdf_source_size;2182} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size * 64) {2183if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {2184return (fgl.advance + ea) * (double)p_size / (double)fd->fixed_size;2185} else {2186return (fgl.advance + ea) * Math::round((double)p_size / (double)fd->fixed_size);2187}2188} 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))) {2189return (fgl.advance + ea).round();2190} else {2191return fgl.advance + ea;2192}2193}21942195void TextServerFallback::_font_set_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph, const Vector2 &p_advance) {2196FontFallback *fd = _get_font_data(p_font_rid);2197ERR_FAIL_NULL(fd);21982199MutexLock lock(fd->mutex);2200Vector2i size = _get_size(fd, p_size);22012202FontForSizeFallback *ffsd = nullptr;2203ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));22042205FontGlyph &fgl = ffsd->glyph_map[p_glyph];22062207fgl.advance = p_advance;2208fgl.found = true;2209}22102211Vector2 TextServerFallback::_font_get_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {2212FontFallback *fd = _get_font_data(p_font_rid);2213ERR_FAIL_NULL_V(fd, Vector2());22142215MutexLock lock(fd->mutex);2216Vector2i size = _get_size_outline(fd, p_size);22172218FontForSizeFallback *ffsd = nullptr;2219ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Vector2());22202221int mod = 0;2222if (fd->antialiasing == FONT_ANTIALIASING_LCD) {2223TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get();2224if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {2225mod = (layout << 24);2226}2227}22282229FontGlyph fgl;2230if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) {2231return Vector2(); // Invalid or non graphicl glyph, do not display errors.2232}22332234if (fd->msdf) {2235return fgl.rect.position * (double)p_size.x / (double)fd->msdf_source_size;2236} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size.x * 64) {2237if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {2238return fgl.rect.position * (double)p_size.x / (double)fd->fixed_size;2239} else {2240return fgl.rect.position * Math::round((double)p_size.x / (double)fd->fixed_size);2241}2242} else {2243return fgl.rect.position;2244}2245}22462247void TextServerFallback::_font_set_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_offset) {2248FontFallback *fd = _get_font_data(p_font_rid);2249ERR_FAIL_NULL(fd);22502251MutexLock lock(fd->mutex);2252Vector2i size = _get_size_outline(fd, p_size);22532254FontForSizeFallback *ffsd = nullptr;2255ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));22562257FontGlyph &fgl = ffsd->glyph_map[p_glyph];22582259fgl.rect.position = p_offset;2260fgl.found = true;2261}22622263Vector2 TextServerFallback::_font_get_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {2264FontFallback *fd = _get_font_data(p_font_rid);2265ERR_FAIL_NULL_V(fd, Vector2());22662267MutexLock lock(fd->mutex);2268Vector2i size = _get_size_outline(fd, p_size);22692270FontForSizeFallback *ffsd = nullptr;2271ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Vector2());22722273int mod = 0;2274if (fd->antialiasing == FONT_ANTIALIASING_LCD) {2275TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get();2276if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {2277mod = (layout << 24);2278}2279}22802281FontGlyph fgl;2282if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) {2283return Vector2(); // Invalid or non graphicl glyph, do not display errors.2284}22852286if (fd->msdf) {2287return fgl.rect.size * (double)p_size.x / (double)fd->msdf_source_size;2288} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size.x * 64) {2289if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {2290return fgl.rect.size * (double)p_size.x / (double)fd->fixed_size;2291} else {2292return fgl.rect.size * Math::round((double)p_size.x / (double)fd->fixed_size);2293}2294} else {2295return fgl.rect.size;2296}2297}22982299void TextServerFallback::_font_set_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_gl_size) {2300FontFallback *fd = _get_font_data(p_font_rid);2301ERR_FAIL_NULL(fd);23022303MutexLock lock(fd->mutex);2304Vector2i size = _get_size_outline(fd, p_size);23052306FontForSizeFallback *ffsd = nullptr;2307ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));23082309FontGlyph &fgl = ffsd->glyph_map[p_glyph];23102311fgl.rect.size = p_gl_size;2312fgl.found = true;2313}23142315Rect2 TextServerFallback::_font_get_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {2316FontFallback *fd = _get_font_data(p_font_rid);2317ERR_FAIL_NULL_V(fd, Rect2());23182319MutexLock lock(fd->mutex);2320Vector2i size = _get_size_outline(fd, p_size);23212322FontForSizeFallback *ffsd = nullptr;2323ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Rect2());23242325int mod = 0;2326if (fd->antialiasing == FONT_ANTIALIASING_LCD) {2327TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get();2328if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {2329mod = (layout << 24);2330}2331}23322333FontGlyph fgl;2334if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) {2335return Rect2(); // Invalid or non graphicl glyph, do not display errors.2336}23372338return fgl.uv_rect;2339}23402341void TextServerFallback::_font_set_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Rect2 &p_uv_rect) {2342FontFallback *fd = _get_font_data(p_font_rid);2343ERR_FAIL_NULL(fd);23442345MutexLock lock(fd->mutex);2346Vector2i size = _get_size_outline(fd, p_size);23472348FontForSizeFallback *ffsd = nullptr;2349ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));23502351FontGlyph &fgl = ffsd->glyph_map[p_glyph];23522353fgl.uv_rect = p_uv_rect;2354fgl.found = true;2355}23562357int64_t TextServerFallback::_font_get_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {2358FontFallback *fd = _get_font_data(p_font_rid);2359ERR_FAIL_NULL_V(fd, -1);23602361MutexLock lock(fd->mutex);2362Vector2i size = _get_size_outline(fd, p_size);23632364FontForSizeFallback *ffsd = nullptr;2365ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), -1);23662367int mod = 0;2368if (fd->antialiasing == FONT_ANTIALIASING_LCD) {2369TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get();2370if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {2371mod = (layout << 24);2372}2373}23742375FontGlyph fgl;2376if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) {2377return -1; // Invalid or non graphicl glyph, do not display errors.2378}23792380return fgl.texture_idx;2381}23822383void TextServerFallback::_font_set_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, int64_t p_texture_idx) {2384FontFallback *fd = _get_font_data(p_font_rid);2385ERR_FAIL_NULL(fd);23862387MutexLock lock(fd->mutex);2388Vector2i size = _get_size_outline(fd, p_size);23892390FontForSizeFallback *ffsd = nullptr;2391ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));23922393FontGlyph &fgl = ffsd->glyph_map[p_glyph];23942395fgl.texture_idx = p_texture_idx;2396fgl.found = true;2397}23982399RID TextServerFallback::_font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {2400FontFallback *fd = _get_font_data(p_font_rid);2401ERR_FAIL_NULL_V(fd, RID());24022403MutexLock lock(fd->mutex);2404Vector2i size = _get_size_outline(fd, p_size);24052406FontForSizeFallback *ffsd = nullptr;2407ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), RID());24082409int mod = 0;2410if (fd->antialiasing == FONT_ANTIALIASING_LCD) {2411TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get();2412if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {2413mod = (layout << 24);2414}2415}24162417FontGlyph fgl;2418if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) {2419return RID(); // Invalid or non graphicl glyph, do not display errors.2420}24212422ERR_FAIL_COND_V(fgl.texture_idx < -1 || fgl.texture_idx >= ffsd->textures.size(), RID());24232424if (RenderingServer::get_singleton() != nullptr) {2425if (fgl.texture_idx != -1) {2426if (ffsd->textures[fgl.texture_idx].dirty) {2427ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx];2428Ref<Image> img = tex.image;2429if (fgl.from_svg) {2430// Same as the "fix alpha border" process option when importing SVGs2431img->fix_alpha_edges();2432}2433if (fd->mipmaps && !img->has_mipmaps()) {2434img = tex.image->duplicate();2435img->generate_mipmaps();2436}2437if (tex.texture.is_null()) {2438tex.texture = ImageTexture::create_from_image(img);2439} else {2440tex.texture->update(img);2441}2442tex.dirty = false;2443}2444return ffsd->textures[fgl.texture_idx].texture->get_rid();2445}2446}24472448return RID();2449}24502451Size2 TextServerFallback::_font_get_glyph_texture_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {2452FontFallback *fd = _get_font_data(p_font_rid);2453ERR_FAIL_NULL_V(fd, Size2());24542455MutexLock lock(fd->mutex);2456Vector2i size = _get_size_outline(fd, p_size);24572458FontForSizeFallback *ffsd = nullptr;2459ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Size2());24602461int mod = 0;2462if (fd->antialiasing == FONT_ANTIALIASING_LCD) {2463TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get();2464if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {2465mod = (layout << 24);2466}2467}24682469FontGlyph fgl;2470if (!_ensure_glyph(fd, size, p_glyph | mod, fgl)) {2471return Size2(); // Invalid or non graphicl glyph, do not display errors.2472}24732474ERR_FAIL_COND_V(fgl.texture_idx < -1 || fgl.texture_idx >= ffsd->textures.size(), Size2());24752476if (RenderingServer::get_singleton() != nullptr) {2477if (fgl.texture_idx != -1) {2478if (ffsd->textures[fgl.texture_idx].dirty) {2479ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx];2480Ref<Image> img = tex.image;2481if (fgl.from_svg) {2482// Same as the "fix alpha border" process option when importing SVGs2483img->fix_alpha_edges();2484}2485if (fd->mipmaps && !img->has_mipmaps()) {2486img = tex.image->duplicate();2487img->generate_mipmaps();2488}2489if (tex.texture.is_null()) {2490tex.texture = ImageTexture::create_from_image(img);2491} else {2492tex.texture->update(img);2493}2494tex.dirty = false;2495}2496return ffsd->textures[fgl.texture_idx].texture->get_size();2497}2498}24992500return Size2();2501}25022503Dictionary TextServerFallback::_font_get_glyph_contours(const RID &p_font_rid, int64_t p_size, int64_t p_index) const {2504FontFallback *fd = _get_font_data(p_font_rid);2505ERR_FAIL_NULL_V(fd, Dictionary());25062507MutexLock lock(fd->mutex);2508Vector2i size = _get_size(fd, p_size);25092510FontForSizeFallback *ffsd = nullptr;2511ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Dictionary());25122513#ifdef MODULE_FREETYPE_ENABLED2514PackedVector3Array points;2515PackedInt32Array contours;25162517int32_t index = p_index & 0xffffff; // Remove subpixel shifts.25182519int error = FT_Load_Glyph(ffsd->face, FT_Get_Char_Index(ffsd->face, index), FT_LOAD_NO_BITMAP | (fd->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0));2520ERR_FAIL_COND_V(error, Dictionary());25212522if (fd->embolden != 0.f) {2523FT_Pos strength = fd->embolden * size.x / 16; // 26.6 fractional units (1 / 64).2524FT_Outline_Embolden(&ffsd->face->glyph->outline, strength);2525}25262527if (fd->transform != Transform2D()) {2528FT_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).2529FT_Outline_Transform(&ffsd->face->glyph->outline, &mat);2530}25312532double scale = (1.0 / 64.0) * ffsd->scale;2533if (fd->msdf) {2534scale = scale * (double)p_size / (double)fd->msdf_source_size;2535} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size * 64) {2536if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {2537scale = scale * (double)p_size / (double)fd->fixed_size;2538} else {2539scale = scale * Math::round((double)p_size / (double)fd->fixed_size);2540}2541}2542for (short i = 0; i < ffsd->face->glyph->outline.n_points; i++) {2543points.push_back(Vector3(ffsd->face->glyph->outline.points[i].x * scale, -ffsd->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(ffsd->face->glyph->outline.tags[i])));2544}2545for (short i = 0; i < ffsd->face->glyph->outline.n_contours; i++) {2546contours.push_back(ffsd->face->glyph->outline.contours[i]);2547}2548bool orientation = (FT_Outline_Get_Orientation(&ffsd->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT);25492550Dictionary out;2551out["points"] = points;2552out["contours"] = contours;2553out["orientation"] = orientation;2554return out;2555#else2556return Dictionary();2557#endif2558}25592560TypedArray<Vector2i> TextServerFallback::_font_get_kerning_list(const RID &p_font_rid, int64_t p_size) const {2561FontFallback *fd = _get_font_data(p_font_rid);2562ERR_FAIL_NULL_V(fd, TypedArray<Vector2i>());25632564MutexLock lock(fd->mutex);2565Vector2i size = _get_size(fd, p_size);25662567FontForSizeFallback *ffsd = nullptr;2568ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), TypedArray<Vector2i>());25692570TypedArray<Vector2i> ret;2571for (const KeyValue<Vector2i, Vector2> &E : ffsd->kerning_map) {2572ret.push_back(E.key);2573}2574return ret;2575}25762577void TextServerFallback::_font_clear_kerning_map(const RID &p_font_rid, int64_t p_size) {2578FontFallback *fd = _get_font_data(p_font_rid);2579ERR_FAIL_NULL(fd);25802581MutexLock lock(fd->mutex);2582Vector2i size = _get_size(fd, p_size);25832584FontForSizeFallback *ffsd = nullptr;2585ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));2586ffsd->kerning_map.clear();2587}25882589void TextServerFallback::_font_remove_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) {2590FontFallback *fd = _get_font_data(p_font_rid);2591ERR_FAIL_NULL(fd);25922593MutexLock lock(fd->mutex);2594Vector2i size = _get_size(fd, p_size);25952596FontForSizeFallback *ffsd = nullptr;2597ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));2598ffsd->kerning_map.erase(p_glyph_pair);2599}26002601void TextServerFallback::_font_set_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) {2602FontFallback *fd = _get_font_data(p_font_rid);2603ERR_FAIL_NULL(fd);26042605MutexLock lock(fd->mutex);2606Vector2i size = _get_size(fd, p_size);26072608FontForSizeFallback *ffsd = nullptr;2609ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));2610ffsd->kerning_map[p_glyph_pair] = p_kerning;2611}26122613Vector2 TextServerFallback::_font_get_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) const {2614FontFallback *fd = _get_font_data(p_font_rid);2615ERR_FAIL_NULL_V(fd, Vector2());26162617MutexLock lock(fd->mutex);2618Vector2i size = _get_size(fd, p_size);26192620FontForSizeFallback *ffsd = nullptr;2621ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Vector2());26222623const HashMap<Vector2i, Vector2> &kern = ffsd->kerning_map;26242625if (kern.has(p_glyph_pair)) {2626if (fd->msdf) {2627return kern[p_glyph_pair] * (double)p_size / (double)fd->msdf_source_size;2628} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size * 64) {2629if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {2630return kern[p_glyph_pair] * (double)p_size / (double)fd->fixed_size;2631} else {2632return kern[p_glyph_pair] * Math::round((double)p_size / (double)fd->fixed_size);2633}2634} else {2635return kern[p_glyph_pair];2636}2637} else {2638#ifdef MODULE_FREETYPE_ENABLED2639if (ffsd->face) {2640FT_Vector delta;2641int32_t glyph_a = FT_Get_Char_Index(ffsd->face, p_glyph_pair.x);2642int32_t glyph_b = FT_Get_Char_Index(ffsd->face, p_glyph_pair.y);2643FT_Get_Kerning(ffsd->face, glyph_a, glyph_b, FT_KERNING_DEFAULT, &delta);2644if (fd->msdf) {2645return Vector2(delta.x, delta.y) * (double)p_size / (double)fd->msdf_source_size;2646} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size * 64) {2647if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {2648return Vector2(delta.x, delta.y) * (double)p_size / (double)fd->fixed_size;2649} else {2650return Vector2(delta.x, delta.y) * Math::round((double)p_size / (double)fd->fixed_size);2651}2652} else {2653return Vector2(delta.x, delta.y);2654}2655}2656#endif2657}2658return Vector2();2659}26602661int64_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 {2662ERR_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) + ".");2663return (int64_t)p_char;2664}26652666int64_t TextServerFallback::_font_get_char_from_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_glyph_index) const {2667return p_glyph_index;2668}26692670bool TextServerFallback::_font_has_char(const RID &p_font_rid, int64_t p_char) const {2671FontFallback *fd = _get_font_data(p_font_rid);2672ERR_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) + ".");2673if (!fd) {2674return false;2675}26762677MutexLock lock(fd->mutex);2678FontForSizeFallback *ffsd = nullptr;2679if (fd->cache.is_empty()) {2680ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size * 64, 0) : Vector2i(16 * 64, 0), ffsd), false);2681} else {2682ffsd = fd->cache.begin()->value;2683}26842685#ifdef MODULE_FREETYPE_ENABLED2686if (ffsd->face) {2687return FT_Get_Char_Index(ffsd->face, p_char) != 0;2688}2689#endif2690return ffsd->glyph_map.has((int32_t)p_char);2691}26922693String TextServerFallback::_font_get_supported_chars(const RID &p_font_rid) const {2694FontFallback *fd = _get_font_data(p_font_rid);2695ERR_FAIL_NULL_V(fd, String());26962697MutexLock lock(fd->mutex);2698FontForSizeFallback *ffsd = nullptr;2699if (fd->cache.is_empty()) {2700ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size * 64, 0) : Vector2i(16 * 64, 0), ffsd), String());2701} else {2702ffsd = fd->cache.begin()->value;2703}27042705String chars;2706#ifdef MODULE_FREETYPE_ENABLED2707if (ffsd->face) {2708FT_UInt gindex;2709FT_ULong charcode = FT_Get_First_Char(ffsd->face, &gindex);2710while (gindex != 0) {2711if (charcode != 0) {2712chars = chars + String::chr(charcode);2713}2714charcode = FT_Get_Next_Char(ffsd->face, charcode, &gindex);2715}2716return chars;2717}2718#endif2719const HashMap<int32_t, FontGlyph> &gl = ffsd->glyph_map;2720for (const KeyValue<int32_t, FontGlyph> &E : gl) {2721chars = chars + String::chr(E.key);2722}2723return chars;2724}27252726PackedInt32Array TextServerFallback::_font_get_supported_glyphs(const RID &p_font_rid) const {2727FontFallback *fd = _get_font_data(p_font_rid);2728ERR_FAIL_NULL_V(fd, PackedInt32Array());27292730MutexLock lock(fd->mutex);2731FontForSizeFallback *at_size = nullptr;2732if (fd->cache.is_empty()) {2733ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size * 64, 0) : Vector2i(16 * 64, 0), at_size), PackedInt32Array());2734} else {2735at_size = fd->cache.begin()->value;2736}27372738PackedInt32Array glyphs;2739#ifdef MODULE_FREETYPE_ENABLED2740if (at_size && at_size->face) {2741FT_UInt gindex;2742FT_ULong charcode = FT_Get_First_Char(at_size->face, &gindex);2743while (gindex != 0) {2744glyphs.push_back(gindex);2745charcode = FT_Get_Next_Char(at_size->face, charcode, &gindex);2746}2747return glyphs;2748}2749#endif2750if (at_size) {2751const HashMap<int32_t, FontGlyph> &gl = at_size->glyph_map;2752for (const KeyValue<int32_t, FontGlyph> &E : gl) {2753glyphs.push_back(E.key);2754}2755}2756return glyphs;2757}27582759void TextServerFallback::_font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) {2760FontFallback *fd = _get_font_data(p_font_rid);2761ERR_FAIL_NULL(fd);2762ERR_FAIL_COND_MSG((p_start >= 0xd800 && p_start <= 0xdfff) || (p_start > 0x10ffff), "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_start, 16) + ".");2763ERR_FAIL_COND_MSG((p_end >= 0xd800 && p_end <= 0xdfff) || (p_end > 0x10ffff), "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_end, 16) + ".");27642765MutexLock lock(fd->mutex);2766Vector2i size = _get_size_outline(fd, p_size);2767FontForSizeFallback *ffsd = nullptr;2768ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));2769for (int64_t i = p_start; i <= p_end; i++) {2770#ifdef MODULE_FREETYPE_ENABLED2771int32_t idx = i;2772if (ffsd->face) {2773FontGlyph fgl;2774if (fd->msdf) {2775_ensure_glyph(fd, size, (int32_t)idx, fgl);2776} else {2777for (int aa = 0; aa < ((fd->antialiasing == FONT_ANTIALIASING_LCD) ? FONT_LCD_SUBPIXEL_LAYOUT_MAX : 1); aa++) {2778if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE * 64)) {2779_ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24), fgl);2780_ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24), fgl);2781_ensure_glyph(fd, size, (int32_t)idx | (2 << 27) | (aa << 24), fgl);2782_ensure_glyph(fd, size, (int32_t)idx | (3 << 27) | (aa << 24), fgl);2783} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE * 64)) {2784_ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24), fgl);2785_ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24), fgl);2786} else {2787_ensure_glyph(fd, size, (int32_t)idx | (aa << 24), fgl);2788}2789}2790}2791}2792#endif2793}2794}27952796void TextServerFallback::_font_render_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_index) {2797FontFallback *fd = _get_font_data(p_font_rid);2798ERR_FAIL_NULL(fd);27992800MutexLock lock(fd->mutex);2801Vector2i size = _get_size_outline(fd, p_size);2802FontForSizeFallback *ffsd = nullptr;2803ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));2804#ifdef MODULE_FREETYPE_ENABLED2805int32_t idx = p_index & 0xffffff; // Remove subpixel shifts.2806if (ffsd->face) {2807FontGlyph fgl;2808if (fd->msdf) {2809_ensure_glyph(fd, size, (int32_t)idx, fgl);2810} else {2811for (int aa = 0; aa < ((fd->antialiasing == FONT_ANTIALIASING_LCD) ? FONT_LCD_SUBPIXEL_LAYOUT_MAX : 1); aa++) {2812if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE * 64)) {2813_ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24), fgl);2814_ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24), fgl);2815_ensure_glyph(fd, size, (int32_t)idx | (2 << 27) | (aa << 24), fgl);2816_ensure_glyph(fd, size, (int32_t)idx | (3 << 27) | (aa << 24), fgl);2817} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE * 64)) {2818_ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24), fgl);2819_ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24), fgl);2820} else {2821_ensure_glyph(fd, size, (int32_t)idx | (aa << 24), fgl);2822}2823}2824}2825}2826#endif2827}28282829void 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 {2830if (p_index == 0) {2831return; // Non visual character, skip.2832}2833FontFallback *fd = _get_font_data(p_font_rid);2834ERR_FAIL_NULL(fd);28352836MutexLock lock(fd->mutex);28372838// Oversampling.2839bool viewport_oversampling = false;2840float oversampling_factor = p_oversampling;2841if (p_oversampling <= 0.0) {2842if (fd->oversampling_override > 0.0) {2843oversampling_factor = fd->oversampling_override;2844} else if (vp_oversampling > 0.0) {2845oversampling_factor = vp_oversampling;2846viewport_oversampling = true;2847} else {2848oversampling_factor = 1.0;2849}2850}2851bool skip_oversampling = fd->msdf || fd->fixed_size > 0;2852if (skip_oversampling) {2853oversampling_factor = 1.0;2854} else {2855uint64_t oversampling_level = CLAMP(oversampling_factor, 0.1, 100.0) * 64;2856oversampling_factor = double(oversampling_level) / 64.0;2857}28582859Vector2i size;2860if (skip_oversampling) {2861size = _get_size(fd, p_size);2862} else {2863size = Vector2i(p_size * 64 * oversampling_factor, 0);2864}28652866FontForSizeFallback *ffsd = nullptr;2867ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd, false, viewport_oversampling ? 64 * oversampling_factor : 0));28682869int32_t index = p_index & 0xffffff; // Remove subpixel shifts.2870bool lcd_aa = false;28712872#ifdef MODULE_FREETYPE_ENABLED2873if (!fd->msdf && ffsd->face) {2874// LCD layout, bits 24, 25, 262875if (fd->antialiasing == FONT_ANTIALIASING_LCD) {2876TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get();2877if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {2878lcd_aa = true;2879index = index | (layout << 24);2880}2881}2882// Subpixel X-shift, bits 27, 282883if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE * 64)) {2884int xshift = (int)(Math::floor(4 * (p_pos.x + 0.125)) - 4 * Math::floor(p_pos.x + 0.125));2885index = index | (xshift << 27);2886} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE * 64)) {2887int xshift = (int)(Math::floor(2 * (p_pos.x + 0.25)) - 2 * Math::floor(p_pos.x + 0.25));2888index = index | (xshift << 27);2889}2890}2891#endif28922893FontGlyph fgl;2894if (!_ensure_glyph(fd, size, index, fgl, viewport_oversampling ? 64 * oversampling_factor : 0)) {2895return; // Invalid or non-graphical glyph, do not display errors, nothing to draw.2896}28972898if (fgl.found) {2899ERR_FAIL_COND(fgl.texture_idx < -1 || fgl.texture_idx >= ffsd->textures.size());29002901if (fgl.texture_idx != -1) {2902Color modulate = p_color;2903#ifdef MODULE_FREETYPE_ENABLED2904if (!fd->modulate_color_glyphs && ffsd->face && ffsd->textures[fgl.texture_idx].image.is_valid() && (ffsd->textures[fgl.texture_idx].image->get_format() == Image::FORMAT_RGBA8) && !lcd_aa && !fd->msdf) {2905modulate.r = modulate.g = modulate.b = 1.0;2906}2907#endif2908if (RenderingServer::get_singleton() != nullptr) {2909if (ffsd->textures[fgl.texture_idx].dirty) {2910ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx];2911Ref<Image> img = tex.image;2912if (fgl.from_svg) {2913// Same as the "fix alpha border" process option when importing SVGs2914img->fix_alpha_edges();2915}2916if (fd->mipmaps && !img->has_mipmaps()) {2917img = tex.image->duplicate();2918img->generate_mipmaps();2919}2920if (tex.texture.is_null()) {2921tex.texture = ImageTexture::create_from_image(img);2922} else {2923tex.texture->update(img);2924}2925tex.dirty = false;2926}2927RID texture = ffsd->textures[fgl.texture_idx].texture->get_rid();2928if (fd->msdf) {2929Point2 cpos = p_pos;2930cpos += fgl.rect.position * (double)p_size / (double)fd->msdf_source_size;2931Size2 csize = fgl.rect.size * (double)p_size / (double)fd->msdf_source_size;2932RenderingServer::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);2933} else {2934Point2 cpos = p_pos;2935double scale = _font_get_scale(p_font_rid, p_size) / oversampling_factor;2936if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {2937cpos.x = cpos.x + 0.125;2938} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {2939cpos.x = cpos.x + 0.25;2940}2941if (scale == 1.0) {2942cpos.y = Math::floor(cpos.y);2943cpos.x = Math::floor(cpos.x);2944}2945Vector2 gpos = fgl.rect.position;2946Size2 csize = fgl.rect.size;2947if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE) {2948if (size.x != p_size * 64) {2949if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {2950double gl_scale = (double)p_size / (double)fd->fixed_size;2951gpos *= gl_scale;2952csize *= gl_scale;2953} else {2954double gl_scale = Math::round((double)p_size / (double)fd->fixed_size);2955gpos *= gl_scale;2956csize *= gl_scale;2957}2958}2959} else {2960gpos /= oversampling_factor;2961csize /= oversampling_factor;2962}2963cpos += gpos;2964if (lcd_aa) {2965RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate);2966} else {2967RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate, false, false);2968}2969}2970}2971}2972}2973}29742975void 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 {2976if (p_index == 0) {2977return; // Non visual character, skip.2978}2979FontFallback *fd = _get_font_data(p_font_rid);2980ERR_FAIL_NULL(fd);29812982MutexLock lock(fd->mutex);29832984// Oversampling.2985bool viewport_oversampling = false;2986float oversampling_factor = p_oversampling;2987if (p_oversampling <= 0.0) {2988if (fd->oversampling_override > 0.0) {2989oversampling_factor = fd->oversampling_override;2990} else if (vp_oversampling > 0.0) {2991oversampling_factor = vp_oversampling;2992viewport_oversampling = true;2993} else {2994oversampling_factor = 1.0;2995}2996}2997bool skip_oversampling = fd->msdf || fd->fixed_size > 0;2998if (skip_oversampling) {2999oversampling_factor = 1.0;3000} else {3001uint64_t oversampling_level = CLAMP(oversampling_factor, 0.1, 100.0) * 64;3002oversampling_factor = double(oversampling_level) / 64.0;3003}30043005Vector2i size;3006if (skip_oversampling) {3007size = _get_size_outline(fd, Vector2i(p_size, p_outline_size));3008} else {3009size = Vector2i(p_size * 64 * oversampling_factor, p_outline_size * oversampling_factor);3010}30113012FontForSizeFallback *ffsd = nullptr;3013ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd, false, viewport_oversampling ? 64 * oversampling_factor : 0));30143015int32_t index = p_index & 0xffffff; // Remove subpixel shifts.3016bool lcd_aa = false;30173018#ifdef MODULE_FREETYPE_ENABLED3019if (!fd->msdf && ffsd->face) {3020// LCD layout, bits 24, 25, 263021if (fd->antialiasing == FONT_ANTIALIASING_LCD) {3022TextServer::FontLCDSubpixelLayout layout = lcd_subpixel_layout.get();3023if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {3024lcd_aa = true;3025index = index | (layout << 24);3026}3027}3028// Subpixel X-shift, bits 27, 283029if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE * 64)) {3030int xshift = (int)(Math::floor(4 * (p_pos.x + 0.125)) - 4 * Math::floor(p_pos.x + 0.125));3031index = index | (xshift << 27);3032} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE * 64)) {3033int xshift = (int)(Math::floor(2 * (p_pos.x + 0.25)) - 2 * Math::floor(p_pos.x + 0.25));3034index = index | (xshift << 27);3035}3036}3037#endif30383039FontGlyph fgl;3040if (!_ensure_glyph(fd, size, index, fgl, viewport_oversampling ? 64 * oversampling_factor : 0)) {3041return; // Invalid or non-graphical glyph, do not display errors, nothing to draw.3042}30433044if (fgl.found) {3045ERR_FAIL_COND(fgl.texture_idx < -1 || fgl.texture_idx >= ffsd->textures.size());30463047if (fgl.texture_idx != -1) {3048Color modulate = p_color;3049#ifdef MODULE_FREETYPE_ENABLED3050if (ffsd->face && ffsd->textures[fgl.texture_idx].image.is_valid() && (ffsd->textures[fgl.texture_idx].image->get_format() == Image::FORMAT_RGBA8) && !lcd_aa && !fd->msdf) {3051modulate.r = modulate.g = modulate.b = 1.0;3052}3053#endif3054if (RenderingServer::get_singleton() != nullptr) {3055if (ffsd->textures[fgl.texture_idx].dirty) {3056ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx];3057Ref<Image> img = tex.image;3058if (fd->mipmaps && !img->has_mipmaps()) {3059img = tex.image->duplicate();3060img->generate_mipmaps();3061}3062if (tex.texture.is_null()) {3063tex.texture = ImageTexture::create_from_image(img);3064} else {3065tex.texture->update(img);3066}3067tex.dirty = false;3068}3069RID texture = ffsd->textures[fgl.texture_idx].texture->get_rid();3070if (fd->msdf) {3071Point2 cpos = p_pos;3072cpos += fgl.rect.position * (double)p_size / (double)fd->msdf_source_size;3073Size2 csize = fgl.rect.size * (double)p_size / (double)fd->msdf_source_size;3074RenderingServer::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);3075} else {3076Point2 cpos = p_pos;3077double scale = _font_get_scale(p_font_rid, p_size) / oversampling_factor;3078if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {3079cpos.x = cpos.x + 0.125;3080} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {3081cpos.x = cpos.x + 0.25;3082}3083if (scale == 1.0) {3084cpos.y = Math::floor(cpos.y);3085cpos.x = Math::floor(cpos.x);3086}3087Vector2 gpos = fgl.rect.position;3088Size2 csize = fgl.rect.size;3089if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE) {3090if (size.x != p_size * 64) {3091if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {3092double gl_scale = (double)p_size / (double)fd->fixed_size;3093gpos *= gl_scale;3094csize *= gl_scale;3095} else {3096double gl_scale = Math::round((double)p_size / (double)fd->fixed_size);3097gpos *= gl_scale;3098csize *= gl_scale;3099}3100}3101} else {3102gpos /= oversampling_factor;3103csize /= oversampling_factor;3104}3105cpos += gpos;3106if (lcd_aa) {3107RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate);3108} else {3109RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, fgl.uv_rect, modulate, false, false);3110}3111}3112}3113}3114}3115}31163117bool TextServerFallback::_font_is_language_supported(const RID &p_font_rid, const String &p_language) const {3118FontFallback *fd = _get_font_data(p_font_rid);3119ERR_FAIL_NULL_V(fd, false);31203121MutexLock lock(fd->mutex);3122if (fd->language_support_overrides.has(p_language)) {3123return fd->language_support_overrides[p_language];3124} else {3125return true;3126}3127}31283129void TextServerFallback::_font_set_language_support_override(const RID &p_font_rid, const String &p_language, bool p_supported) {3130FontFallback *fd = _get_font_data(p_font_rid);3131ERR_FAIL_NULL(fd);31323133MutexLock lock(fd->mutex);3134fd->language_support_overrides[p_language] = p_supported;3135}31363137bool TextServerFallback::_font_get_language_support_override(const RID &p_font_rid, const String &p_language) {3138FontFallback *fd = _get_font_data(p_font_rid);3139ERR_FAIL_NULL_V(fd, false);31403141MutexLock lock(fd->mutex);3142return fd->language_support_overrides[p_language];3143}31443145void TextServerFallback::_font_remove_language_support_override(const RID &p_font_rid, const String &p_language) {3146FontFallback *fd = _get_font_data(p_font_rid);3147ERR_FAIL_NULL(fd);31483149MutexLock lock(fd->mutex);3150fd->language_support_overrides.erase(p_language);3151}31523153PackedStringArray TextServerFallback::_font_get_language_support_overrides(const RID &p_font_rid) {3154FontFallback *fd = _get_font_data(p_font_rid);3155ERR_FAIL_NULL_V(fd, PackedStringArray());31563157MutexLock lock(fd->mutex);3158PackedStringArray out;3159for (const KeyValue<String, bool> &E : fd->language_support_overrides) {3160out.push_back(E.key);3161}3162return out;3163}31643165bool TextServerFallback::_font_is_script_supported(const RID &p_font_rid, const String &p_script) const {3166FontFallback *fd = _get_font_data(p_font_rid);3167ERR_FAIL_NULL_V(fd, false);31683169MutexLock lock(fd->mutex);3170if (fd->script_support_overrides.has(p_script)) {3171return fd->script_support_overrides[p_script];3172} else {3173return true;3174}3175}31763177void TextServerFallback::_font_set_script_support_override(const RID &p_font_rid, const String &p_script, bool p_supported) {3178FontFallback *fd = _get_font_data(p_font_rid);3179ERR_FAIL_NULL(fd);31803181MutexLock lock(fd->mutex);3182fd->script_support_overrides[p_script] = p_supported;3183}31843185bool TextServerFallback::_font_get_script_support_override(const RID &p_font_rid, const String &p_script) {3186FontFallback *fd = _get_font_data(p_font_rid);3187ERR_FAIL_NULL_V(fd, false);31883189MutexLock lock(fd->mutex);3190return fd->script_support_overrides[p_script];3191}31923193void TextServerFallback::_font_remove_script_support_override(const RID &p_font_rid, const String &p_script) {3194FontFallback *fd = _get_font_data(p_font_rid);3195ERR_FAIL_NULL(fd);31963197MutexLock lock(fd->mutex);3198Vector2i size = _get_size(fd, 16);3199FontForSizeFallback *ffsd = nullptr;3200ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));3201fd->script_support_overrides.erase(p_script);3202}32033204PackedStringArray TextServerFallback::_font_get_script_support_overrides(const RID &p_font_rid) {3205FontFallback *fd = _get_font_data(p_font_rid);3206ERR_FAIL_NULL_V(fd, PackedStringArray());32073208MutexLock lock(fd->mutex);3209PackedStringArray out;3210for (const KeyValue<String, bool> &E : fd->script_support_overrides) {3211out.push_back(E.key);3212}3213return out;3214}32153216void TextServerFallback::_font_set_opentype_feature_overrides(const RID &p_font_rid, const Dictionary &p_overrides) {3217FontFallback *fd = _get_font_data(p_font_rid);3218ERR_FAIL_NULL(fd);32193220MutexLock lock(fd->mutex);3221Vector2i size = _get_size(fd, 16);3222FontForSizeFallback *ffsd = nullptr;3223ERR_FAIL_COND(!_ensure_cache_for_size(fd, size, ffsd));3224fd->feature_overrides = p_overrides;3225}32263227Dictionary TextServerFallback::_font_get_opentype_feature_overrides(const RID &p_font_rid) const {3228FontFallback *fd = _get_font_data(p_font_rid);3229ERR_FAIL_NULL_V(fd, Dictionary());32303231MutexLock lock(fd->mutex);3232return fd->feature_overrides;3233}32343235Dictionary TextServerFallback::_font_supported_feature_list(const RID &p_font_rid) const {3236return Dictionary();3237}32383239Dictionary TextServerFallback::_font_supported_variation_list(const RID &p_font_rid) const {3240FontFallback *fd = _get_font_data(p_font_rid);3241ERR_FAIL_NULL_V(fd, Dictionary());32423243MutexLock lock(fd->mutex);3244Vector2i size = _get_size(fd, 16);3245FontForSizeFallback *ffsd = nullptr;3246ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), Dictionary());3247return fd->supported_varaitions;3248}32493250/*************************************************************************/3251/* Shaped text buffer interface */3252/*************************************************************************/32533254void TextServerFallback::invalidate(ShapedTextDataFallback *p_shaped) {3255p_shaped->valid.clear();3256p_shaped->sort_valid = false;3257p_shaped->line_breaks_valid = false;3258p_shaped->justification_ops_valid = false;3259p_shaped->ascent = 0.0;3260p_shaped->descent = 0.0;3261p_shaped->width = 0.0;3262p_shaped->upos = 0.0;3263p_shaped->uthk = 0.0;3264p_shaped->glyphs.clear();3265p_shaped->glyphs_logical.clear();3266p_shaped->runs.clear();3267p_shaped->runs_dirty = true;3268}32693270void TextServerFallback::full_copy(ShapedTextDataFallback *p_shaped) {3271ShapedTextDataFallback *parent = shaped_owner.get_or_null(p_shaped->parent);32723273for (const KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : parent->objects) {3274if (E.value.start >= p_shaped->start && E.value.start < p_shaped->end) {3275p_shaped->objects[E.key] = E.value;3276}3277}32783279for (int i = p_shaped->first_span; i <= p_shaped->last_span; i++) {3280ShapedTextDataFallback::Span span = parent->spans[i];3281span.start = MAX(p_shaped->start, span.start);3282span.end = MIN(p_shaped->end, span.end);3283p_shaped->spans.push_back(span);3284}3285p_shaped->first_span = 0;3286p_shaped->last_span = 0;32873288p_shaped->parent = RID();3289}32903291RID TextServerFallback::_create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {3292_THREAD_SAFE_METHOD_3293ERR_FAIL_COND_V_MSG(p_direction == DIRECTION_INHERITED, RID(), "Invalid text direction.");32943295ShapedTextDataFallback *sd = memnew(ShapedTextDataFallback);3296sd->direction = p_direction;3297sd->orientation = p_orientation;32983299return shaped_owner.make_rid(sd);3300}33013302void TextServerFallback::_shaped_text_clear(const RID &p_shaped) {3303ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3304ERR_FAIL_NULL(sd);33053306MutexLock lock(sd->mutex);3307sd->parent = RID();3308sd->start = 0;3309sd->end = 0;3310sd->text = String();3311sd->spans.clear();3312sd->first_span = 0;3313sd->last_span = 0;3314sd->objects.clear();3315invalidate(sd);3316}33173318void TextServerFallback::_shaped_text_set_direction(const RID &p_shaped, TextServer::Direction p_direction) {3319ERR_FAIL_COND_MSG(p_direction == DIRECTION_INHERITED, "Invalid text direction.");3320if (p_direction == DIRECTION_RTL) {3321ERR_PRINT_ONCE("Right-to-left layout is not supported by this text server.");3322}3323}33243325TextServer::Direction TextServerFallback::_shaped_text_get_direction(const RID &p_shaped) const {3326return TextServer::DIRECTION_LTR;3327}33283329TextServer::Direction TextServerFallback::_shaped_text_get_inferred_direction(const RID &p_shaped) const {3330return TextServer::DIRECTION_LTR;3331}33323333void TextServerFallback::_shaped_text_set_custom_punctuation(const RID &p_shaped, const String &p_punct) {3334_THREAD_SAFE_METHOD_3335ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3336ERR_FAIL_NULL(sd);33373338if (sd->custom_punct != p_punct) {3339if (sd->parent != RID()) {3340full_copy(sd);3341}3342sd->custom_punct = p_punct;3343invalidate(sd);3344}3345}33463347String TextServerFallback::_shaped_text_get_custom_punctuation(const RID &p_shaped) const {3348_THREAD_SAFE_METHOD_3349const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3350ERR_FAIL_NULL_V(sd, String());3351return sd->custom_punct;3352}33533354void TextServerFallback::_shaped_text_set_custom_ellipsis(const RID &p_shaped, int64_t p_char) {3355_THREAD_SAFE_METHOD_3356ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3357ERR_FAIL_NULL(sd);3358sd->el_char = p_char;3359}33603361int64_t TextServerFallback::_shaped_text_get_custom_ellipsis(const RID &p_shaped) const {3362_THREAD_SAFE_METHOD_3363const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3364ERR_FAIL_NULL_V(sd, 0);3365return sd->el_char;3366}33673368void TextServerFallback::_shaped_text_set_orientation(const RID &p_shaped, TextServer::Orientation p_orientation) {3369ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3370ERR_FAIL_NULL(sd);33713372MutexLock lock(sd->mutex);3373if (sd->orientation != p_orientation) {3374if (sd->parent != RID()) {3375full_copy(sd);3376}3377sd->orientation = p_orientation;3378invalidate(sd);3379}3380}33813382void TextServerFallback::_shaped_text_set_bidi_override(const RID &p_shaped, const Array &p_override) {3383// No BiDi support, ignore.3384}33853386TextServer::Orientation TextServerFallback::_shaped_text_get_orientation(const RID &p_shaped) const {3387const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3388ERR_FAIL_NULL_V(sd, TextServer::ORIENTATION_HORIZONTAL);33893390MutexLock lock(sd->mutex);3391return sd->orientation;3392}33933394void TextServerFallback::_shaped_text_set_preserve_invalid(const RID &p_shaped, bool p_enabled) {3395ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);33963397MutexLock lock(sd->mutex);3398ERR_FAIL_NULL(sd);3399if (sd->preserve_invalid != p_enabled) {3400if (sd->parent != RID()) {3401full_copy(sd);3402}3403sd->preserve_invalid = p_enabled;3404invalidate(sd);3405}3406}34073408bool TextServerFallback::_shaped_text_get_preserve_invalid(const RID &p_shaped) const {3409const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3410ERR_FAIL_NULL_V(sd, false);34113412MutexLock lock(sd->mutex);3413return sd->preserve_invalid;3414}34153416void TextServerFallback::_shaped_text_set_preserve_control(const RID &p_shaped, bool p_enabled) {3417ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3418ERR_FAIL_NULL(sd);34193420MutexLock lock(sd->mutex);3421if (sd->preserve_control != p_enabled) {3422if (sd->parent != RID()) {3423full_copy(sd);3424}3425sd->preserve_control = p_enabled;3426invalidate(sd);3427}3428}34293430bool TextServerFallback::_shaped_text_get_preserve_control(const RID &p_shaped) const {3431const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3432ERR_FAIL_NULL_V(sd, false);34333434MutexLock lock(sd->mutex);3435return sd->preserve_control;3436}34373438void TextServerFallback::_shaped_text_set_spacing(const RID &p_shaped, SpacingType p_spacing, int64_t p_value) {3439ERR_FAIL_INDEX((int)p_spacing, 4);3440ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3441ERR_FAIL_NULL(sd);34423443MutexLock lock(sd->mutex);3444if (sd->extra_spacing[p_spacing] != p_value) {3445if (sd->parent != RID()) {3446full_copy(sd);3447}3448sd->extra_spacing[p_spacing] = p_value;3449invalidate(sd);3450}3451}34523453int64_t TextServerFallback::_shaped_text_get_spacing(const RID &p_shaped, SpacingType p_spacing) const {3454ERR_FAIL_INDEX_V((int)p_spacing, 4, 0);34553456const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3457ERR_FAIL_NULL_V(sd, 0);34583459MutexLock lock(sd->mutex);3460return sd->extra_spacing[p_spacing];3461}34623463int64_t TextServerFallback::_shaped_get_span_count(const RID &p_shaped) const {3464ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3465ERR_FAIL_NULL_V(sd, 0);34663467if (sd->parent != RID()) {3468return sd->last_span - sd->first_span + 1;3469} else {3470return sd->spans.size();3471}3472}34733474Variant TextServerFallback::_shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const {3475ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3476ERR_FAIL_NULL_V(sd, Variant());3477if (sd->parent != RID()) {3478ShapedTextDataFallback *parent_sd = shaped_owner.get_or_null(sd->parent);3479ERR_FAIL_COND_V(!parent_sd->valid.is_set(), Variant());3480ERR_FAIL_INDEX_V(p_index + sd->first_span, parent_sd->spans.size(), Variant());3481return parent_sd->spans[p_index + sd->first_span].meta;3482} else {3483ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant());3484return sd->spans[p_index].meta;3485}3486}34873488Variant TextServerFallback::_shaped_get_span_embedded_object(const RID &p_shaped, int64_t p_index) const {3489ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3490ERR_FAIL_NULL_V(sd, Variant());3491if (sd->parent != RID()) {3492ShapedTextDataFallback *parent_sd = shaped_owner.get_or_null(sd->parent);3493ERR_FAIL_COND_V(!parent_sd->valid.is_set(), Variant());3494ERR_FAIL_INDEX_V(p_index + sd->first_span, parent_sd->spans.size(), Variant());3495return parent_sd->spans[p_index + sd->first_span].embedded_key;3496} else {3497ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant());3498return sd->spans[p_index].embedded_key;3499}3500}35013502String TextServerFallback::_shaped_get_span_text(const RID &p_shaped, int64_t p_index) const {3503ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3504ERR_FAIL_NULL_V(sd, String());3505ShapedTextDataFallback *span_sd = sd;3506if (sd->parent.is_valid()) {3507span_sd = shaped_owner.get_or_null(sd->parent);3508ERR_FAIL_NULL_V(span_sd, String());3509}3510ERR_FAIL_INDEX_V(p_index, span_sd->spans.size(), String());3511return span_sd->text.substr(span_sd->spans[p_index].start, span_sd->spans[p_index].end - span_sd->spans[p_index].start);3512}35133514Variant TextServerFallback::_shaped_get_span_object(const RID &p_shaped, int64_t p_index) const {3515ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3516ERR_FAIL_NULL_V(sd, Variant());3517ShapedTextDataFallback *span_sd = sd;3518if (sd->parent.is_valid()) {3519span_sd = shaped_owner.get_or_null(sd->parent);3520ERR_FAIL_NULL_V(span_sd, Variant());3521}3522ERR_FAIL_INDEX_V(p_index, span_sd->spans.size(), Variant());3523return span_sd->spans[p_index].embedded_key;3524}35253526void TextServerFallback::_generate_runs(ShapedTextDataFallback *p_sd) const {3527ERR_FAIL_NULL(p_sd);3528p_sd->runs.clear();35293530ShapedTextDataFallback *span_sd = p_sd;3531if (p_sd->parent.is_valid()) {3532span_sd = shaped_owner.get_or_null(p_sd->parent);3533ERR_FAIL_NULL(span_sd);3534}35353536int sd_size = p_sd->glyphs.size();3537Glyph *sd_gl = p_sd->glyphs.ptrw();35383539int span_count = span_sd->spans.size();3540int span = -1;3541int span_start = -1;3542int span_end = -1;35433544TextRun run;3545for (int i = 0; i < sd_size; i += sd_gl[i].count) {3546const Glyph &gl = sd_gl[i];3547if (gl.start < 0 || gl.end < 0) {3548continue;3549}3550if (gl.start < span_start || gl.start >= span_end) {3551span = -1;3552span_start = -1;3553span_end = -1;3554for (int j = 0; j < span_count; j++) {3555if (gl.start >= span_sd->spans[j].start && gl.end <= span_sd->spans[j].end) {3556span = j;3557span_start = span_sd->spans[j].start;3558span_end = span_sd->spans[j].end;3559break;3560}3561}3562}3563if (run.font_rid != gl.font_rid || run.font_size != gl.font_size || run.span_index != span) {3564if (run.span_index >= 0) {3565p_sd->runs.push_back(run);3566}3567run.range = Vector2i(gl.start, gl.end);3568run.font_rid = gl.font_rid;3569run.font_size = gl.font_size;3570run.span_index = span;3571}3572run.range.x = MIN(run.range.x, gl.start);3573run.range.y = MAX(run.range.y, gl.end);3574}3575if (run.span_index >= 0) {3576p_sd->runs.push_back(run);3577}3578p_sd->runs_dirty = false;3579}35803581int64_t TextServerFallback::_shaped_get_run_count(const RID &p_shaped) const {3582ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3583ERR_FAIL_NULL_V(sd, 0);3584MutexLock lock(sd->mutex);3585if (!sd->valid.is_set()) {3586const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);3587}3588if (sd->runs_dirty) {3589_generate_runs(sd);3590}3591return sd->runs.size();3592}35933594String TextServerFallback::_shaped_get_run_text(const RID &p_shaped, int64_t p_index) const {3595ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3596ERR_FAIL_NULL_V(sd, String());3597MutexLock lock(sd->mutex);3598if (!sd->valid.is_set()) {3599const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);3600}3601if (sd->runs_dirty) {3602_generate_runs(sd);3603}3604ERR_FAIL_INDEX_V(p_index, sd->runs.size(), String());3605return sd->text.substr(sd->runs[p_index].range.x - sd->start, sd->runs[p_index].range.y - sd->runs[p_index].range.x);3606}36073608Vector2i TextServerFallback::_shaped_get_run_range(const RID &p_shaped, int64_t p_index) const {3609ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3610ERR_FAIL_NULL_V(sd, Vector2i());3611MutexLock lock(sd->mutex);3612if (!sd->valid.is_set()) {3613const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);3614}3615if (sd->runs_dirty) {3616_generate_runs(sd);3617}3618ERR_FAIL_INDEX_V(p_index, sd->runs.size(), Vector2i());3619return sd->runs[p_index].range;3620}36213622RID TextServerFallback::_shaped_get_run_font_rid(const RID &p_shaped, int64_t p_index) const {3623ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3624ERR_FAIL_NULL_V(sd, RID());3625MutexLock lock(sd->mutex);3626if (!sd->valid.is_set()) {3627const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);3628}3629if (sd->runs_dirty) {3630_generate_runs(sd);3631}3632ERR_FAIL_INDEX_V(p_index, sd->runs.size(), RID());3633return sd->runs[p_index].font_rid;3634}36353636int TextServerFallback::_shaped_get_run_font_size(const RID &p_shaped, int64_t p_index) const {3637ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3638ERR_FAIL_NULL_V(sd, 0);3639MutexLock lock(sd->mutex);3640if (!sd->valid.is_set()) {3641const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);3642}3643if (sd->runs_dirty) {3644_generate_runs(sd);3645}3646ERR_FAIL_INDEX_V(p_index, sd->runs.size(), 0);3647return sd->runs[p_index].font_size;3648}36493650String TextServerFallback::_shaped_get_run_language(const RID &p_shaped, int64_t p_index) const {3651ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3652ERR_FAIL_NULL_V(sd, String());3653MutexLock lock(sd->mutex);3654if (!sd->valid.is_set()) {3655const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);3656}3657if (sd->runs_dirty) {3658_generate_runs(sd);3659}3660ERR_FAIL_INDEX_V(p_index, sd->runs.size(), String());36613662int span_idx = sd->runs[p_index].span_index;3663ShapedTextDataFallback *span_sd = sd;3664if (sd->parent.is_valid()) {3665span_sd = shaped_owner.get_or_null(sd->parent);3666ERR_FAIL_NULL_V(span_sd, String());3667}3668ERR_FAIL_INDEX_V(span_idx, span_sd->spans.size(), String());3669return span_sd->spans[span_idx].language;3670}36713672TextServer::Direction TextServerFallback::_shaped_get_run_direction(const RID &p_shaped, int64_t p_index) const {3673ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3674ERR_FAIL_NULL_V(sd, TextServer::DIRECTION_LTR);3675MutexLock lock(sd->mutex);3676if (!sd->valid.is_set()) {3677const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);3678}3679if (sd->runs_dirty) {3680_generate_runs(sd);3681}3682ERR_FAIL_INDEX_V(p_index, sd->runs.size(), TextServer::DIRECTION_LTR);3683return TextServer::DIRECTION_LTR;3684}36853686Variant TextServerFallback::_shaped_get_run_object(const RID &p_shaped, int64_t p_index) const {3687ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3688ERR_FAIL_NULL_V(sd, Variant());3689MutexLock lock(sd->mutex);3690if (!sd->valid.is_set()) {3691const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);3692}3693if (sd->runs_dirty) {3694_generate_runs(sd);3695}3696ERR_FAIL_INDEX_V(p_index, sd->runs.size(), Variant());36973698int span_idx = sd->runs[p_index].span_index;3699ShapedTextDataFallback *span_sd = sd;3700if (sd->parent.is_valid()) {3701span_sd = shaped_owner.get_or_null(sd->parent);3702ERR_FAIL_NULL_V(span_sd, Variant());3703}3704ERR_FAIL_INDEX_V(span_idx, span_sd->spans.size(), Variant());3705return span_sd->spans[span_idx].embedded_key;3706}37073708void 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) {3709ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3710ERR_FAIL_NULL(sd);3711if (sd->parent != RID()) {3712full_copy(sd);3713}3714ERR_FAIL_INDEX(p_index, sd->spans.size());37153716ShapedTextDataFallback::Span &span = sd->spans.ptrw()[p_index];3717span.fonts.clear();3718// Pre-sort fonts, push fonts with the language support first.3719Array fonts_no_match;3720int font_count = p_fonts.size();3721for (int i = 0; i < font_count; i++) {3722if (_font_is_language_supported(p_fonts[i], span.language)) {3723span.fonts.push_back(p_fonts[i]);3724} else {3725fonts_no_match.push_back(p_fonts[i]);3726}3727}3728span.fonts.append_array(fonts_no_match);3729span.font_size = p_size;3730span.features = p_opentype_features;37313732sd->valid.clear();3733}37343735bool 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) {3736ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3737ERR_FAIL_NULL_V(sd, false);37383739MutexLock lock(sd->mutex);3740ERR_FAIL_COND_V(p_size <= 0, false);37413742for (int i = 0; i < p_fonts.size(); i++) {3743ERR_FAIL_NULL_V(_get_font_data(p_fonts[i]), false);3744}37453746if (p_text.is_empty()) {3747return true;3748}37493750if (sd->parent != RID()) {3751full_copy(sd);3752}37533754ShapedTextDataFallback::Span span;3755span.start = sd->text.length();3756span.end = span.start + p_text.length();37573758// Pre-sort fonts, push fonts with the language support first.3759Array fonts_no_match;3760int font_count = p_fonts.size();3761if (font_count > 0) {3762span.fonts.push_back(p_fonts[0]);3763}3764for (int i = 1; i < font_count; i++) {3765if (_font_is_language_supported(p_fonts[i], p_language)) {3766span.fonts.push_back(p_fonts[i]);3767} else {3768fonts_no_match.push_back(p_fonts[i]);3769}3770}3771span.fonts.append_array(fonts_no_match);37723773ERR_FAIL_COND_V(span.fonts.is_empty(), false);3774span.font_size = p_size;3775span.language = p_language;3776span.meta = p_meta;37773778sd->spans.push_back(span);3779sd->text = sd->text + p_text;3780sd->end += p_text.length();3781invalidate(sd);37823783return true;3784}37853786bool 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) {3787ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3788ERR_FAIL_NULL_V(sd, false);37893790MutexLock lock(sd->mutex);3791ERR_FAIL_COND_V(p_key == Variant(), false);3792ERR_FAIL_COND_V(sd->objects.has(p_key), false);37933794if (sd->parent != RID()) {3795full_copy(sd);3796}37973798ShapedTextDataFallback::Span span;3799span.start = sd->start + sd->text.length();3800span.end = span.start + p_length;3801span.embedded_key = p_key;38023803ShapedTextDataFallback::EmbeddedObject obj;3804obj.inline_align = p_inline_align;3805obj.rect.size = p_size;3806obj.start = span.start;3807obj.end = span.end;3808obj.baseline = p_baseline;38093810sd->spans.push_back(span);3811sd->text = sd->text + String::chr(0xfffc).repeat(p_length);3812sd->end += p_length;3813sd->objects[p_key] = obj;3814invalidate(sd);38153816return true;3817}38183819String TextServerFallback::_shaped_get_text(const RID &p_shaped) const {3820const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3821ERR_FAIL_NULL_V(sd, String());38223823return sd->text;3824}38253826bool TextServerFallback::_shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, double p_baseline) {3827ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3828ERR_FAIL_NULL_V(sd, false);38293830MutexLock lock(sd->mutex);3831ERR_FAIL_COND_V(!sd->objects.has(p_key), false);3832sd->objects[p_key].rect.size = p_size;3833sd->objects[p_key].inline_align = p_inline_align;3834sd->objects[p_key].baseline = p_baseline;3835if (sd->valid.is_set()) {3836// Recalc string metrics.3837sd->ascent = 0;3838sd->descent = 0;3839sd->width = 0;3840sd->upos = 0;3841sd->uthk = 0;38423843Vector<ShapedTextDataFallback::Span> &spans = sd->spans;3844if (sd->parent != RID()) {3845ShapedTextDataFallback *parent_sd = shaped_owner.get_or_null(sd->parent);3846ERR_FAIL_COND_V(!parent_sd->valid.is_set(), false);3847spans = parent_sd->spans;3848}38493850int sd_size = sd->glyphs.size();3851int span_size = spans.size();38523853for (int i = 0; i < sd_size; i++) {3854Glyph gl = sd->glyphs[i];3855Variant key;3856if ((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) {3857key = spans[gl.span_index + sd->first_span].embedded_key;3858}3859if (key != Variant()) {3860if (sd->orientation == ORIENTATION_HORIZONTAL) {3861sd->objects[key].rect.position.x = sd->width;3862sd->width += sd->objects[key].rect.size.x;3863sd->glyphs.write[i].advance = sd->objects[key].rect.size.x;3864} else {3865sd->objects[key].rect.position.y = sd->width;3866sd->width += sd->objects[key].rect.size.y;3867sd->glyphs.write[i].advance = sd->objects[key].rect.size.y;3868}3869} else {3870if (gl.font_rid.is_valid()) {3871if (sd->orientation == ORIENTATION_HORIZONTAL) {3872sd->ascent = MAX(sd->ascent, _font_get_ascent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_TOP));3873sd->descent = MAX(sd->descent, _font_get_descent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_BOTTOM));3874} else {3875sd->ascent = MAX(sd->ascent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));3876sd->descent = MAX(sd->descent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));3877}3878sd->upos = MAX(sd->upos, _font_get_underline_position(gl.font_rid, gl.font_size));3879sd->uthk = MAX(sd->uthk, _font_get_underline_thickness(gl.font_rid, gl.font_size));3880} else if (sd->preserve_invalid || (sd->preserve_control && is_control(gl.index))) {3881// Glyph not found, replace with hex code box.3882if (sd->orientation == ORIENTATION_HORIZONTAL) {3883sd->ascent = MAX(sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.85);3884sd->descent = MAX(sd->descent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.15);3885} else {3886sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5));3887sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5));3888}3889}3890sd->width += gl.advance * gl.repeat;3891}3892}3893_realign(sd);3894}3895return true;3896}38973898void TextServerFallback::_realign(ShapedTextDataFallback *p_sd) const {3899// Align embedded objects to baseline.3900double full_ascent = p_sd->ascent;3901double full_descent = p_sd->descent;3902for (KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : p_sd->objects) {3903if ((E.value.start >= p_sd->start) && (E.value.start < p_sd->end)) {3904if (p_sd->orientation == ORIENTATION_HORIZONTAL) {3905switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {3906case INLINE_ALIGNMENT_TO_TOP: {3907E.value.rect.position.y = -p_sd->ascent;3908} break;3909case INLINE_ALIGNMENT_TO_CENTER: {3910E.value.rect.position.y = (-p_sd->ascent + p_sd->descent) / 2;3911} break;3912case INLINE_ALIGNMENT_TO_BASELINE: {3913E.value.rect.position.y = 0;3914} break;3915case INLINE_ALIGNMENT_TO_BOTTOM: {3916E.value.rect.position.y = p_sd->descent;3917} break;3918}3919switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {3920case INLINE_ALIGNMENT_BOTTOM_TO: {3921E.value.rect.position.y -= E.value.rect.size.y;3922} break;3923case INLINE_ALIGNMENT_CENTER_TO: {3924E.value.rect.position.y -= E.value.rect.size.y / 2;3925} break;3926case INLINE_ALIGNMENT_BASELINE_TO: {3927E.value.rect.position.y -= E.value.baseline;3928} break;3929case INLINE_ALIGNMENT_TOP_TO: {3930// NOP3931} break;3932}3933full_ascent = MAX(full_ascent, -E.value.rect.position.y);3934full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);3935} else {3936switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {3937case INLINE_ALIGNMENT_TO_TOP: {3938E.value.rect.position.x = -p_sd->ascent;3939} break;3940case INLINE_ALIGNMENT_TO_CENTER: {3941E.value.rect.position.x = (-p_sd->ascent + p_sd->descent) / 2;3942} break;3943case INLINE_ALIGNMENT_TO_BASELINE: {3944E.value.rect.position.x = 0;3945} break;3946case INLINE_ALIGNMENT_TO_BOTTOM: {3947E.value.rect.position.x = p_sd->descent;3948} break;3949}3950switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {3951case INLINE_ALIGNMENT_BOTTOM_TO: {3952E.value.rect.position.x -= E.value.rect.size.x;3953} break;3954case INLINE_ALIGNMENT_CENTER_TO: {3955E.value.rect.position.x -= E.value.rect.size.x / 2;3956} break;3957case INLINE_ALIGNMENT_BASELINE_TO: {3958E.value.rect.position.x -= E.value.baseline;3959} break;3960case INLINE_ALIGNMENT_TOP_TO: {3961// NOP3962} break;3963}3964full_ascent = MAX(full_ascent, -E.value.rect.position.x);3965full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);3966}3967}3968}3969p_sd->ascent = full_ascent;3970p_sd->descent = full_descent;3971}39723973RID TextServerFallback::_shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const {3974_THREAD_SAFE_METHOD_39753976const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);3977ERR_FAIL_NULL_V(sd, RID());39783979MutexLock lock(sd->mutex);3980if (sd->parent != RID()) {3981return _shaped_text_substr(sd->parent, p_start, p_length);3982}3983if (!sd->valid.is_set()) {3984const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);3985}3986ERR_FAIL_COND_V(p_start < 0 || p_length < 0, RID());3987ERR_FAIL_COND_V(sd->start > p_start || sd->end < p_start, RID());3988ERR_FAIL_COND_V(sd->end < p_start + p_length, RID());39893990ShapedTextDataFallback *new_sd = memnew(ShapedTextDataFallback);3991new_sd->parent = p_shaped;3992new_sd->start = p_start;3993new_sd->end = p_start + p_length;39943995new_sd->orientation = sd->orientation;3996new_sd->direction = sd->direction;3997new_sd->custom_punct = sd->custom_punct;3998new_sd->para_direction = sd->para_direction;3999new_sd->line_breaks_valid = sd->line_breaks_valid;4000new_sd->justification_ops_valid = sd->justification_ops_valid;4001new_sd->sort_valid = false;4002new_sd->upos = sd->upos;4003new_sd->uthk = sd->uthk;4004for (int i = 0; i < TextServer::SPACING_MAX; i++) {4005new_sd->extra_spacing[i] = sd->extra_spacing[i];4006}40074008if (p_length > 0) {4009new_sd->text = sd->text.substr(p_start - sd->start, p_length);40104011int span_size = sd->spans.size();40124013new_sd->first_span = 0;4014new_sd->last_span = span_size - 1;4015for (int i = 0; i < span_size; i++) {4016const ShapedTextDataFallback::Span &span = sd->spans[i];4017if (span.end <= p_start) {4018new_sd->first_span = i + 1;4019} else if (span.start >= p_start + p_length) {4020new_sd->last_span = i - 1;4021break;4022}4023}40244025int sd_size = sd->glyphs.size();4026const Glyph *sd_glyphs = sd->glyphs.ptr();40274028for (int i = 0; i < sd_size; i++) {4029if ((sd_glyphs[i].start >= new_sd->start) && (sd_glyphs[i].end <= new_sd->end)) {4030Glyph gl = sd_glyphs[i];4031if (gl.span_index >= 0) {4032gl.span_index -= new_sd->first_span;4033}4034if (gl.end == p_start + p_length && ((gl.flags & GRAPHEME_IS_SOFT_HYPHEN) == GRAPHEME_IS_SOFT_HYPHEN)) {4035gl.index = 0x00ad;4036gl.advance = font_get_glyph_advance(gl.font_rid, gl.font_size, 0x00ad).x;4037}4038if ((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) {4039Variant key = sd->spans[gl.span_index + new_sd->first_span].embedded_key;4040if (key != Variant()) {4041ShapedTextDataFallback::EmbeddedObject obj = sd->objects[key];4042if (new_sd->orientation == ORIENTATION_HORIZONTAL) {4043obj.rect.position.x = new_sd->width;4044new_sd->width += obj.rect.size.x;4045} else {4046obj.rect.position.y = new_sd->width;4047new_sd->width += obj.rect.size.y;4048}4049new_sd->objects[key] = obj;4050}4051} else {4052if (gl.font_rid.is_valid()) {4053if (new_sd->orientation == ORIENTATION_HORIZONTAL) {4054new_sd->ascent = MAX(new_sd->ascent, _font_get_ascent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_TOP));4055new_sd->descent = MAX(new_sd->descent, _font_get_descent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_BOTTOM));4056} else {4057new_sd->ascent = MAX(new_sd->ascent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));4058new_sd->descent = MAX(new_sd->descent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));4059}4060} else if (new_sd->preserve_invalid || (new_sd->preserve_control && is_control(gl.index))) {4061// Glyph not found, replace with hex code box.4062if (new_sd->orientation == ORIENTATION_HORIZONTAL) {4063new_sd->ascent = MAX(new_sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.85);4064new_sd->descent = MAX(new_sd->descent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.15);4065} else {4066new_sd->ascent = MAX(new_sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5));4067new_sd->descent = MAX(new_sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5));4068}4069}4070new_sd->width += gl.advance * gl.repeat;4071}4072new_sd->glyphs.push_back(gl);4073}4074}40754076_realign(new_sd);4077}4078new_sd->valid.set();40794080return shaped_owner.make_rid(new_sd);4081}40824083RID TextServerFallback::_shaped_text_get_parent(const RID &p_shaped) const {4084ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4085ERR_FAIL_NULL_V(sd, RID());40864087MutexLock lock(sd->mutex);4088return sd->parent;4089}40904091double TextServerFallback::_shaped_text_fit_to_width(const RID &p_shaped, double p_width, BitField<JustificationFlag> p_jst_flags) {4092ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4093ERR_FAIL_NULL_V(sd, 0.0);40944095MutexLock lock(sd->mutex);4096if (!sd->valid.is_set()) {4097_shaped_text_shape(p_shaped);4098}4099if (!sd->justification_ops_valid) {4100_shaped_text_update_justification_ops(p_shaped);4101}41024103int start_pos = 0;4104int end_pos = sd->glyphs.size() - 1;41054106if (p_jst_flags.has_flag(JUSTIFICATION_AFTER_LAST_TAB)) {4107int start, end, delta;4108if (sd->para_direction == DIRECTION_LTR) {4109start = sd->glyphs.size() - 1;4110end = -1;4111delta = -1;4112} else {4113start = 0;4114end = sd->glyphs.size();4115delta = +1;4116}41174118for (int i = start; i != end; i += delta) {4119if ((sd->glyphs[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) {4120if (sd->para_direction == DIRECTION_LTR) {4121start_pos = i;4122break;4123} else {4124end_pos = i;4125break;4126}4127}4128}4129}41304131double justification_width;4132if (p_jst_flags.has_flag(JUSTIFICATION_CONSTRAIN_ELLIPSIS)) {4133if (sd->overrun_trim_data.trim_pos >= 0) {4134end_pos = sd->overrun_trim_data.trim_pos;4135justification_width = sd->width_trimmed;4136} else {4137return Math::ceil(sd->width);4138}4139} else {4140justification_width = sd->width;4141}41424143if (p_jst_flags.has_flag(JUSTIFICATION_TRIM_EDGE_SPACES)) {4144// Trim spaces.4145while ((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)) {4146justification_width -= sd->glyphs[start_pos].advance * sd->glyphs[start_pos].repeat;4147sd->glyphs.write[start_pos].advance = 0;4148start_pos += sd->glyphs[start_pos].count;4149}4150while ((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)) {4151justification_width -= sd->glyphs[end_pos].advance * sd->glyphs[end_pos].repeat;4152sd->glyphs.write[end_pos].advance = 0;4153end_pos -= sd->glyphs[end_pos].count;4154}4155} else {4156// Skip breaks, but do not reset size.4157while ((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)) {4158start_pos += sd->glyphs[start_pos].count;4159}4160while ((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)) {4161end_pos -= sd->glyphs[end_pos].count;4162}4163}41644165int space_count = 0;4166for (int i = start_pos; i <= end_pos; i++) {4167const Glyph &gl = sd->glyphs[i];4168if (gl.count > 0) {4169if ((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) {4170space_count++;4171}4172}4173}41744175if ((space_count > 0) && p_jst_flags.has_flag(JUSTIFICATION_WORD_BOUND)) {4176double delta_width_per_space = (p_width - justification_width) / space_count;4177for (int i = start_pos; i <= end_pos; i++) {4178Glyph &gl = sd->glyphs.write[i];4179if (gl.count > 0) {4180if ((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) {4181double old_adv = gl.advance;4182gl.advance = MAX(gl.advance + delta_width_per_space, Math::round(0.1 * gl.font_size));4183justification_width += (gl.advance - old_adv);4184}4185}4186}4187}41884189if (Math::floor(p_width) < Math::floor(justification_width)) {4190sd->fit_width_minimum_reached = true;4191}41924193if (!p_jst_flags.has_flag(JUSTIFICATION_CONSTRAIN_ELLIPSIS)) {4194sd->width = justification_width;4195}41964197return Math::ceil(justification_width);4198}41994200double TextServerFallback::_shaped_text_tab_align(const RID &p_shaped, const PackedFloat32Array &p_tab_stops) {4201ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4202ERR_FAIL_NULL_V(sd, 0.0);42034204MutexLock lock(sd->mutex);4205if (!sd->valid.is_set()) {4206_shaped_text_shape(p_shaped);4207}4208if (!sd->line_breaks_valid) {4209_shaped_text_update_breaks(p_shaped);4210}42114212for (int i = 0; i < p_tab_stops.size(); i++) {4213if (p_tab_stops[i] <= 0) {4214return 0.0;4215}4216}42174218int tab_index = 0;4219double off = 0.0;42204221int start, end, delta;4222if (sd->para_direction == DIRECTION_LTR) {4223start = 0;4224end = sd->glyphs.size();4225delta = +1;4226} else {4227start = sd->glyphs.size() - 1;4228end = -1;4229delta = -1;4230}42314232Glyph *gl = sd->glyphs.ptrw();42334234for (int i = start; i != end; i += delta) {4235if ((gl[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) {4236double tab_off = 0.0;4237while (tab_off <= off) {4238tab_off += p_tab_stops[tab_index];4239tab_index++;4240if (tab_index >= p_tab_stops.size()) {4241tab_index = 0;4242}4243}4244double old_adv = gl[i].advance;4245gl[i].advance = tab_off - off;4246sd->width += gl[i].advance - old_adv;4247off = 0;4248continue;4249}4250off += gl[i].advance * gl[i].repeat;4251}42524253return 0.0;4254}42554256bool TextServerFallback::_shaped_text_update_breaks(const RID &p_shaped) {4257ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4258ERR_FAIL_NULL_V(sd, false);42594260MutexLock lock(sd->mutex);4261if (!sd->valid.is_set()) {4262_shaped_text_shape(p_shaped);4263}42644265if (sd->line_breaks_valid) {4266return true; // Nothing to do.4267}42684269int sd_size = sd->glyphs.size();4270Glyph *sd_glyphs = sd->glyphs.ptrw();42714272int c_punct_size = sd->custom_punct.length();4273const char32_t *c_punct = sd->custom_punct.ptr();42744275for (int i = 0; i < sd_size; i++) {4276if (sd_glyphs[i].count > 0) {4277char32_t c = sd->text[sd_glyphs[i].start - sd->start];4278char32_t c_next = i < sd_size ? sd->text[sd_glyphs[i].start - sd->start + 1] : 0x0000;4279if (c_punct_size == 0) {4280if (is_punct(c) && c != 0x005F && c != ' ') {4281sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION;4282}4283} else {4284for (int j = 0; j < c_punct_size; j++) {4285if (c_punct[j] == c) {4286sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION;4287break;4288}4289}4290}4291if (is_underscore(c)) {4292sd->glyphs.write[i].flags |= GRAPHEME_IS_UNDERSCORE;4293}4294if (is_whitespace(c) && !is_linebreak(c)) {4295sd_glyphs[i].flags |= GRAPHEME_IS_SPACE;4296if (c != 0x00A0 && c != 0x202F && c != 0x2060 && c != 0x2007) { // Skip for non-breaking space variants.4297sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_SOFT;4298}4299}4300if (is_linebreak(c)) {4301sd_glyphs[i].flags |= GRAPHEME_IS_SPACE;4302if (c != 0x000D || c_next != 0x000A) { // Skip first hard break in CR-LF pair.4303sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD;4304}4305}4306if (c == 0x0009 || c == 0x000b) {4307sd_glyphs[i].flags |= GRAPHEME_IS_TAB;4308}4309if (c == 0x00ad) {4310sd_glyphs[i].flags |= GRAPHEME_IS_SOFT_HYPHEN;4311}43124313i += (sd_glyphs[i].count - 1);4314}4315}4316sd->line_breaks_valid = true;4317return sd->line_breaks_valid;4318}43194320bool TextServerFallback::_shaped_text_update_justification_ops(const RID &p_shaped) {4321ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4322ERR_FAIL_NULL_V(sd, false);43234324MutexLock lock(sd->mutex);4325if (!sd->valid.is_set()) {4326_shaped_text_shape(p_shaped);4327}4328if (!sd->line_breaks_valid) {4329_shaped_text_update_breaks(p_shaped);4330}43314332sd->justification_ops_valid = true; // Not supported by fallback server.4333return true;4334}43354336RID TextServerFallback::_find_sys_font_for_text(const RID &p_fdef, const String &p_script_code, const String &p_language, const String &p_text) {4337RID f;4338// Try system fallback.4339if (_font_is_allow_system_fallback(p_fdef)) {4340String font_name = _font_get_name(p_fdef);4341BitField<FontStyle> font_style = _font_get_style(p_fdef);4342int font_weight = _font_get_weight(p_fdef);4343int font_stretch = _font_get_stretch(p_fdef);4344Dictionary dvar = _font_get_variation_coordinates(p_fdef);4345static int64_t wgth_tag = name_to_tag("weight");4346static int64_t wdth_tag = name_to_tag("width");4347static int64_t ital_tag = name_to_tag("italic");4348if (dvar.has(wgth_tag)) {4349font_weight = dvar[wgth_tag].operator int();4350}4351if (dvar.has(wdth_tag)) {4352font_stretch = dvar[wdth_tag].operator int();4353}4354if (dvar.has(ital_tag) && dvar[ital_tag].operator int() == 1) {4355font_style.set_flag(TextServer::FONT_ITALIC);4356}43574358String locale = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language;4359PackedStringArray 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);4360#ifdef GDEXTENSION4361for (int fb = 0; fb < fallback_font_name.size(); fb++) {4362const String &E = fallback_font_name[fb];4363#elif defined(GODOT_MODULE)4364for (const String &E : fallback_font_name) {4365#endif4366SystemFontKey key = SystemFontKey(E, font_style & TextServer::FONT_ITALIC, font_weight, font_stretch, p_fdef, this);4367if (system_fonts.has(key)) {4368const SystemFontCache &sysf_cache = system_fonts[key];4369int best_score = 0;4370int best_match = -1;4371for (int face_idx = 0; face_idx < sysf_cache.var.size(); face_idx++) {4372const SystemFontCacheRec &F = sysf_cache.var[face_idx];4373if (unlikely(!_font_has_char(F.rid, p_text[0]))) {4374continue;4375}4376BitField<FontStyle> style = _font_get_style(F.rid);4377int weight = _font_get_weight(F.rid);4378int stretch = _font_get_stretch(F.rid);4379int score = (20 - Math::abs(weight - font_weight) / 50);4380score += (20 - Math::abs(stretch - font_stretch) / 10);4381if (bool(style & TextServer::FONT_ITALIC) == bool(font_style & TextServer::FONT_ITALIC)) {4382score += 30;4383}4384if (score >= best_score) {4385best_score = score;4386best_match = face_idx;4387}4388if (best_score == 70) {4389break;4390}4391}4392if (best_match != -1) {4393f = sysf_cache.var[best_match].rid;4394}4395}4396if (!f.is_valid()) {4397if (system_fonts.has(key)) {4398const SystemFontCache &sysf_cache = system_fonts[key];4399if (sysf_cache.max_var == sysf_cache.var.size()) {4400// All subfonts already tested, skip.4401continue;4402}4403}44044405if (!system_font_data.has(E)) {4406system_font_data[E] = FileAccess::get_file_as_bytes(E);4407}44084409const PackedByteArray &font_data = system_font_data[E];44104411SystemFontCacheRec sysf;4412sysf.rid = _create_font();4413_font_set_data_ptr(sysf.rid, font_data.ptr(), font_data.size());4414if (!_font_validate(sysf.rid)) {4415_free_rid(sysf.rid);4416continue;4417}44184419Dictionary var = dvar;4420// Select matching style from collection.4421int best_score = 0;4422int best_match = -1;4423for (int face_idx = 0; face_idx < _font_get_face_count(sysf.rid); face_idx++) {4424_font_set_face_index(sysf.rid, face_idx);4425if (unlikely(!_font_has_char(sysf.rid, p_text[0]))) {4426continue;4427}4428BitField<FontStyle> style = _font_get_style(sysf.rid);4429int weight = _font_get_weight(sysf.rid);4430int stretch = _font_get_stretch(sysf.rid);4431int score = (20 - Math::abs(weight - font_weight) / 50);4432score += (20 - Math::abs(stretch - font_stretch) / 10);4433if (bool(style & TextServer::FONT_ITALIC) == bool(font_style & TextServer::FONT_ITALIC)) {4434score += 30;4435}4436if (score >= best_score) {4437best_score = score;4438best_match = face_idx;4439}4440if (best_score == 70) {4441break;4442}4443}4444if (best_match == -1) {4445_free_rid(sysf.rid);4446continue;4447} else {4448_font_set_face_index(sysf.rid, best_match);4449}4450sysf.index = best_match;44514452// If it's a variable font, apply weight, stretch and italic coordinates to match requested style.4453if (best_score != 70) {4454Dictionary ftr = _font_supported_variation_list(sysf.rid);4455if (ftr.has(wdth_tag)) {4456var[wdth_tag] = font_stretch;4457_font_set_stretch(sysf.rid, font_stretch);4458}4459if (ftr.has(wgth_tag)) {4460var[wgth_tag] = font_weight;4461_font_set_weight(sysf.rid, font_weight);4462}4463if ((font_style & TextServer::FONT_ITALIC) && ftr.has(ital_tag)) {4464var[ital_tag] = 1;4465_font_set_style(sysf.rid, _font_get_style(sysf.rid) | TextServer::FONT_ITALIC);4466}4467}44684469bool fb_use_msdf = key.msdf;4470if (fb_use_msdf) {4471FontFallback *fd = _get_font_data(sysf.rid);4472if (fd) {4473MutexLock lock(fd->mutex);4474Vector2i size = _get_size(fd, 16);4475FontForSizeFallback *ffsd = nullptr;4476if (_ensure_cache_for_size(fd, size, ffsd)) {4477if (ffsd && (FT_HAS_COLOR(ffsd->face) || !FT_IS_SCALABLE(ffsd->face))) {4478fb_use_msdf = false;4479}4480}4481}4482}44834484_font_set_antialiasing(sysf.rid, key.antialiasing);4485_font_set_disable_embedded_bitmaps(sysf.rid, key.disable_embedded_bitmaps);4486_font_set_generate_mipmaps(sysf.rid, key.mipmaps);4487_font_set_multichannel_signed_distance_field(sysf.rid, fb_use_msdf);4488_font_set_msdf_pixel_range(sysf.rid, key.msdf_range);4489_font_set_msdf_size(sysf.rid, key.msdf_source_size);4490_font_set_fixed_size(sysf.rid, key.fixed_size);4491_font_set_force_autohinter(sysf.rid, key.force_autohinter);4492_font_set_hinting(sysf.rid, key.hinting);4493_font_set_subpixel_positioning(sysf.rid, key.subpixel_positioning);4494_font_set_keep_rounding_remainders(sysf.rid, key.keep_rounding_remainders);4495_font_set_variation_coordinates(sysf.rid, var);4496_font_set_embolden(sysf.rid, key.embolden);4497_font_set_transform(sysf.rid, key.transform);4498_font_set_spacing(sysf.rid, SPACING_TOP, key.extra_spacing[SPACING_TOP]);4499_font_set_spacing(sysf.rid, SPACING_BOTTOM, key.extra_spacing[SPACING_BOTTOM]);4500_font_set_spacing(sysf.rid, SPACING_SPACE, key.extra_spacing[SPACING_SPACE]);4501_font_set_spacing(sysf.rid, SPACING_GLYPH, key.extra_spacing[SPACING_GLYPH]);45024503if (system_fonts.has(key)) {4504system_fonts[key].var.push_back(sysf);4505} else {4506SystemFontCache &sysf_cache = system_fonts[key];4507sysf_cache.max_var = _font_get_face_count(sysf.rid);4508sysf_cache.var.push_back(sysf);4509}4510f = sysf.rid;4511}4512break;4513}4514}4515return f;4516}45174518void TextServerFallback::_shaped_text_overrun_trim_to_width(const RID &p_shaped_line, double p_width, BitField<TextServer::TextOverrunFlag> p_trim_flags) {4519ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped_line);4520ERR_FAIL_NULL_MSG(sd, "ShapedTextDataFallback invalid.");45214522MutexLock lock(sd->mutex);4523if (!sd->valid.is_set()) {4524_shaped_text_shape(p_shaped_line);4525}45264527sd->text_trimmed = false;4528sd->overrun_trim_data.ellipsis_glyph_buf.clear();45294530bool add_ellipsis = p_trim_flags.has_flag(OVERRUN_ADD_ELLIPSIS);4531bool cut_per_word = p_trim_flags.has_flag(OVERRUN_TRIM_WORD_ONLY);4532bool enforce_ellipsis = p_trim_flags.has_flag(OVERRUN_ENFORCE_ELLIPSIS);4533bool justification_aware = p_trim_flags.has_flag(OVERRUN_JUSTIFICATION_AWARE);45344535Glyph *sd_glyphs = sd->glyphs.ptrw();45364537if ((p_trim_flags & OVERRUN_TRIM) == OVERRUN_NO_TRIM || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) {4538sd->overrun_trim_data.trim_pos = -1;4539sd->overrun_trim_data.ellipsis_pos = -1;4540return;4541}45424543if (justification_aware && !sd->fit_width_minimum_reached) {4544return;4545}45464547Vector<ShapedTextDataFallback::Span> &spans = sd->spans;4548if (sd->parent != RID()) {4549ShapedTextDataFallback *parent_sd = shaped_owner.get_or_null(sd->parent);4550ERR_FAIL_COND(!parent_sd->valid.is_set());4551spans = parent_sd->spans;4552}45534554int span_size = spans.size();4555if (span_size == 0) {4556return;4557}45584559int sd_size = sd->glyphs.size();4560int last_gl_font_size = sd_glyphs[sd_size - 1].font_size;4561bool found_el_char = false;45624563// Find usable fonts, if fonts from the last glyph do not have required chars.4564RID dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;4565if (add_ellipsis || enforce_ellipsis) {4566if (!_font_has_char(dot_gl_font_rid, sd->el_char)) {4567const Array &fonts = spans[span_size - 1].fonts;4568for (int i = 0; i < fonts.size(); i++) {4569if (_font_has_char(fonts[i], sd->el_char)) {4570dot_gl_font_rid = fonts[i];4571found_el_char = true;4572break;4573}4574}4575if (!found_el_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {4576const char32_t u32str[] = { sd->el_char, 0 };4577RID rid = _find_sys_font_for_text(fonts[0], String(), spans[span_size - 1].language, u32str);4578if (rid.is_valid()) {4579dot_gl_font_rid = rid;4580found_el_char = true;4581}4582}4583} else {4584found_el_char = true;4585}4586if (!found_el_char) {4587bool found_dot_char = false;4588dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;4589if (!_font_has_char(dot_gl_font_rid, '.')) {4590const Array &fonts = spans[span_size - 1].fonts;4591for (int i = 0; i < fonts.size(); i++) {4592if (_font_has_char(fonts[i], '.')) {4593dot_gl_font_rid = fonts[i];4594found_dot_char = true;4595break;4596}4597}4598if (!found_dot_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {4599RID rid = _find_sys_font_for_text(fonts[0], String(), spans[span_size - 1].language, ".");4600if (rid.is_valid()) {4601dot_gl_font_rid = rid;4602}4603}4604}4605}4606}4607RID whitespace_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;4608if (!_font_has_char(whitespace_gl_font_rid, ' ')) {4609const Array &fonts = spans[span_size - 1].fonts;4610for (int i = 0; i < fonts.size(); i++) {4611if (_font_has_char(fonts[i], ' ')) {4612whitespace_gl_font_rid = fonts[i];4613break;4614}4615}4616}46174618int32_t dot_gl_idx = ((add_ellipsis || enforce_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;4619Vector2 dot_adv = ((add_ellipsis || enforce_ellipsis) && dot_gl_font_rid.is_valid()) ? _font_get_glyph_advance(dot_gl_font_rid, last_gl_font_size, dot_gl_idx) : Vector2();4620int32_t whitespace_gl_idx = whitespace_gl_font_rid.is_valid() ? _font_get_glyph_index(whitespace_gl_font_rid, last_gl_font_size, ' ', 0) : -1;4621Vector2 whitespace_adv = whitespace_gl_font_rid.is_valid() ? _font_get_glyph_advance(whitespace_gl_font_rid, last_gl_font_size, whitespace_gl_idx) : Vector2();46224623int ellipsis_width = 0;4624if (add_ellipsis && whitespace_gl_font_rid.is_valid()) {4625ellipsis_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);4626}46274628int ell_min_characters = 6;4629double width = sd->width;4630double width_without_el = width;46314632int trim_pos = 0;4633int ellipsis_pos = (enforce_ellipsis) ? 0 : -1;46344635int last_valid_cut = -1;4636int last_valid_cut_witout_el = -1;46374638if (enforce_ellipsis && (width + ellipsis_width <= p_width)) {4639trim_pos = -1;4640ellipsis_pos = sd_size;4641} else {4642for (int i = sd_size - 1; i != -1; i--) {4643width -= sd_glyphs[i].advance * sd_glyphs[i].repeat;46444645if (sd_glyphs[i].count > 0) {4646bool above_min_char_threshold = (i >= ell_min_characters);4647if (!above_min_char_threshold && last_valid_cut_witout_el != -1) {4648trim_pos = last_valid_cut_witout_el;4649ellipsis_pos = -1;4650width = width_without_el;4651break;4652}4653if (!enforce_ellipsis && width <= p_width && last_valid_cut_witout_el == -1) {4654if (cut_per_word && above_min_char_threshold) {4655if ((sd_glyphs[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) {4656last_valid_cut_witout_el = i;4657width_without_el = width;4658}4659} else {4660last_valid_cut_witout_el = i;4661width_without_el = width;4662}4663}4664if (width + (((above_min_char_threshold && add_ellipsis) || enforce_ellipsis) ? ellipsis_width : 0) <= p_width) {4665if (cut_per_word && above_min_char_threshold) {4666if ((sd_glyphs[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) {4667last_valid_cut = i;4668}4669} else {4670last_valid_cut = i;4671}4672if (last_valid_cut != -1) {4673trim_pos = last_valid_cut;46744675if (add_ellipsis && (above_min_char_threshold || enforce_ellipsis) && width - ellipsis_width <= p_width) {4676ellipsis_pos = trim_pos;4677}4678break;4679}4680}4681}4682}4683}46844685sd->overrun_trim_data.trim_pos = trim_pos;4686sd->overrun_trim_data.ellipsis_pos = ellipsis_pos;4687if (trim_pos == 0 && enforce_ellipsis && add_ellipsis) {4688sd->overrun_trim_data.ellipsis_pos = 0;4689}46904691if ((trim_pos >= 0 && sd->width > p_width) || enforce_ellipsis) {4692if (add_ellipsis && (ellipsis_pos > 0 || enforce_ellipsis)) {4693// Insert an additional space when cutting word bound for aesthetics.4694if (cut_per_word && (ellipsis_pos > 0)) {4695Glyph gl;4696gl.count = 1;4697gl.advance = whitespace_adv.x;4698gl.index = whitespace_gl_idx;4699gl.font_rid = whitespace_gl_font_rid;4700gl.font_size = last_gl_font_size;4701gl.flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT | GRAPHEME_IS_VIRTUAL;47024703sd->overrun_trim_data.ellipsis_glyph_buf.append(gl);4704}4705// Add ellipsis dots.4706if (dot_gl_idx != 0) {4707Glyph gl;4708gl.count = 1;4709gl.repeat = (found_el_char ? 1 : 3);4710gl.advance = dot_adv.x;4711gl.index = dot_gl_idx;4712gl.font_rid = dot_gl_font_rid;4713gl.font_size = last_gl_font_size;4714gl.flags = GRAPHEME_IS_PUNCTUATION | GRAPHEME_IS_VIRTUAL;47154716sd->overrun_trim_data.ellipsis_glyph_buf.append(gl);4717}4718}47194720sd->text_trimmed = true;4721sd->width_trimmed = width + ((ellipsis_pos != -1) ? ellipsis_width : 0);4722}4723}47244725int64_t TextServerFallback::_shaped_text_get_trim_pos(const RID &p_shaped) const {4726ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4727ERR_FAIL_NULL_V_MSG(sd, -1, "ShapedTextDataFallback invalid.");47284729MutexLock lock(sd->mutex);4730return sd->overrun_trim_data.trim_pos;4731}47324733int64_t TextServerFallback::_shaped_text_get_ellipsis_pos(const RID &p_shaped) const {4734ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4735ERR_FAIL_NULL_V_MSG(sd, -1, "ShapedTextDataFallback invalid.");47364737MutexLock lock(sd->mutex);4738return sd->overrun_trim_data.ellipsis_pos;4739}47404741const Glyph *TextServerFallback::_shaped_text_get_ellipsis_glyphs(const RID &p_shaped) const {4742ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4743ERR_FAIL_NULL_V_MSG(sd, nullptr, "ShapedTextDataFallback invalid.");47444745MutexLock lock(sd->mutex);4746return sd->overrun_trim_data.ellipsis_glyph_buf.ptr();4747}47484749int64_t TextServerFallback::_shaped_text_get_ellipsis_glyph_count(const RID &p_shaped) const {4750ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4751ERR_FAIL_NULL_V_MSG(sd, 0, "ShapedTextDataFallback invalid.");47524753MutexLock lock(sd->mutex);4754return sd->overrun_trim_data.ellipsis_glyph_buf.size();4755}47564757bool TextServerFallback::_shaped_text_shape(const RID &p_shaped) {4758ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4759ERR_FAIL_NULL_V(sd, false);47604761MutexLock lock(sd->mutex);4762if (sd->valid.is_set()) {4763return true;4764}47654766if (sd->parent != RID()) {4767full_copy(sd);4768}47694770// Cleanup.4771sd->justification_ops_valid = false;4772sd->line_breaks_valid = false;4773sd->ascent = 0.0;4774sd->descent = 0.0;4775sd->width = 0.0;4776sd->glyphs.clear();4777sd->runs.clear();4778sd->runs_dirty = true;47794780if (sd->text.length() == 0) {4781sd->valid.set();4782return true;4783}47844785// "Shape" string.4786for (int i = 0; i < sd->spans.size(); i++) {4787const ShapedTextDataFallback::Span &span = sd->spans[i];4788if (span.embedded_key != Variant()) {4789// Embedded object.4790if (sd->orientation == ORIENTATION_HORIZONTAL) {4791sd->objects[span.embedded_key].rect.position.x = sd->width;4792sd->width += sd->objects[span.embedded_key].rect.size.x;4793} else {4794sd->objects[span.embedded_key].rect.position.y = sd->width;4795sd->width += sd->objects[span.embedded_key].rect.size.y;4796}4797Glyph gl;4798gl.span_index = i;4799gl.start = span.start;4800gl.end = span.end;4801gl.count = 1;4802gl.index = 0;4803gl.flags = GRAPHEME_IS_VALID | GRAPHEME_IS_EMBEDDED_OBJECT;4804if (sd->orientation == ORIENTATION_HORIZONTAL) {4805gl.advance = sd->objects[span.embedded_key].rect.size.x;4806} else {4807gl.advance = sd->objects[span.embedded_key].rect.size.y;4808}4809sd->glyphs.push_back(gl);4810} else {4811// Text span.4812RID prev_font;4813for (int j = span.start; j < span.end; j++) {4814Glyph gl;4815gl.span_index = i;4816gl.start = j;4817gl.end = j + 1;4818gl.count = 1;4819gl.font_size = span.font_size;4820gl.index = (int32_t)sd->text[j - sd->start]; // Use codepoint.4821if (gl.index == 0x0009 || gl.index == 0x000b) {4822gl.index = 0x0020;4823}4824if (!sd->preserve_control && is_control(gl.index)) {4825gl.index = 0x0020;4826}4827// Select first font which has character (font are already sorted by span language).4828for (int k = 0; k < span.fonts.size(); k++) {4829if (_font_has_char(span.fonts[k], gl.index)) {4830gl.font_rid = span.fonts[k];4831break;4832}4833}4834if (!gl.font_rid.is_valid() && prev_font.is_valid()) {4835if (_font_has_char(prev_font, gl.index)) {4836gl.font_rid = prev_font;4837}4838}4839if (!gl.font_rid.is_valid() && OS::get_singleton()->has_feature("system_fonts") && span.fonts.size() > 0) {4840// Try system fallback.4841RID fdef = span.fonts[0];4842if (_font_is_allow_system_fallback(fdef)) {4843String text = sd->text.substr(j, 1);4844gl.font_rid = _find_sys_font_for_text(fdef, String(), span.language, text);4845}4846}4847prev_font = gl.font_rid;48484849if (gl.font_rid.is_valid()) {4850double scale = _font_get_scale(gl.font_rid, gl.font_size);4851bool 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);4852if (sd->text[j - sd->start] != 0 && !is_linebreak(sd->text[j - sd->start])) {4853if (sd->orientation == ORIENTATION_HORIZONTAL) {4854gl.advance = _font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x;4855gl.x_off = 0;4856gl.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));4857sd->ascent = MAX(sd->ascent, _font_get_ascent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_TOP));4858sd->descent = MAX(sd->descent, _font_get_descent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_BOTTOM));4859} else {4860gl.advance = _font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).y;4861gl.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));4862gl.y_off = _font_get_ascent(gl.font_rid, gl.font_size);4863sd->ascent = MAX(sd->ascent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));4864sd->descent = MAX(sd->descent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));4865}4866}4867if (j < sd->end - 1) {4868// Do not add extra spacing to the last glyph of the string.4869if (is_whitespace(sd->text[j - sd->start])) {4870gl.advance += sd->extra_spacing[SPACING_SPACE] + _font_get_spacing(gl.font_rid, SPACING_SPACE);4871} else {4872gl.advance += sd->extra_spacing[SPACING_GLYPH] + _font_get_spacing(gl.font_rid, SPACING_GLYPH);4873}4874}4875sd->upos = MAX(sd->upos, _font_get_underline_position(gl.font_rid, gl.font_size));4876sd->uthk = MAX(sd->uthk, _font_get_underline_thickness(gl.font_rid, gl.font_size));48774878// Add kerning to previous glyph.4879if (sd->glyphs.size() > 0) {4880Glyph &prev_gl = sd->glyphs.write[sd->glyphs.size() - 1];4881if (prev_gl.font_rid == gl.font_rid && prev_gl.font_size == gl.font_size) {4882if (sd->orientation == ORIENTATION_HORIZONTAL) {4883prev_gl.advance += _font_get_kerning(gl.font_rid, gl.font_size, Vector2i(prev_gl.index, gl.index)).x;4884} else {4885prev_gl.advance += _font_get_kerning(gl.font_rid, gl.font_size, Vector2i(prev_gl.index, gl.index)).y;4886}4887}4888}4889if (sd->orientation == ORIENTATION_HORIZONTAL && !subpos) {4890gl.advance = Math::round(gl.advance);4891}4892} else if (sd->preserve_invalid || (sd->preserve_control && is_control(gl.index))) {4893// Glyph not found, replace with hex code box.4894if (sd->orientation == ORIENTATION_HORIZONTAL) {4895gl.advance = get_hex_code_box_size(gl.font_size, gl.index).x;4896sd->ascent = MAX(sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.85);4897sd->descent = MAX(sd->descent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.15);4898} else {4899gl.advance = get_hex_code_box_size(gl.font_size, gl.index).y;4900sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5));4901sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5));4902}4903}4904sd->width += gl.advance;4905sd->glyphs.push_back(gl);4906}4907}4908}49094910// Align embedded objects to baseline.4911_realign(sd);49124913sd->valid.set();4914return sd->valid.is_set();4915}49164917bool TextServerFallback::_shaped_text_is_ready(const RID &p_shaped) const {4918const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4919ERR_FAIL_NULL_V(sd, false);49204921return sd->valid.is_set();4922}49234924const Glyph *TextServerFallback::_shaped_text_get_glyphs(const RID &p_shaped) const {4925const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4926ERR_FAIL_NULL_V(sd, nullptr);49274928MutexLock lock(sd->mutex);4929if (!sd->valid.is_set()) {4930const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);4931}4932return sd->glyphs.ptr();4933}49344935int64_t TextServerFallback::_shaped_text_get_glyph_count(const RID &p_shaped) const {4936const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4937ERR_FAIL_NULL_V(sd, 0);49384939MutexLock lock(sd->mutex);4940if (!sd->valid.is_set()) {4941const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);4942}4943return sd->glyphs.size();4944}49454946const Glyph *TextServerFallback::_shaped_text_sort_logical(const RID &p_shaped) {4947const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4948ERR_FAIL_NULL_V(sd, nullptr);49494950MutexLock lock(sd->mutex);4951if (!sd->valid.is_set()) {4952_shaped_text_shape(p_shaped);4953}49544955return sd->glyphs.ptr(); // Already in the logical order, return as is.4956}49574958Vector2i TextServerFallback::_shaped_text_get_range(const RID &p_shaped) const {4959const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4960ERR_FAIL_NULL_V(sd, Vector2i());49614962MutexLock lock(sd->mutex);4963return Vector2(sd->start, sd->end);4964}49654966Array TextServerFallback::_shaped_text_get_objects(const RID &p_shaped) const {4967Array ret;4968const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4969ERR_FAIL_NULL_V(sd, ret);49704971MutexLock lock(sd->mutex);4972for (const KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : sd->objects) {4973ret.push_back(E.key);4974}49754976return ret;4977}49784979Rect2 TextServerFallback::_shaped_text_get_object_rect(const RID &p_shaped, const Variant &p_key) const {4980const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4981ERR_FAIL_NULL_V(sd, Rect2());49824983MutexLock lock(sd->mutex);4984ERR_FAIL_COND_V(!sd->objects.has(p_key), Rect2());4985if (!sd->valid.is_set()) {4986const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);4987}4988return sd->objects[p_key].rect;4989}49904991Vector2i TextServerFallback::_shaped_text_get_object_range(const RID &p_shaped, const Variant &p_key) const {4992const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);4993ERR_FAIL_NULL_V(sd, Vector2i());49944995MutexLock lock(sd->mutex);4996ERR_FAIL_COND_V(!sd->objects.has(p_key), Vector2i());4997return Vector2i(sd->objects[p_key].start, sd->objects[p_key].end);4998}49995000int64_t TextServerFallback::_shaped_text_get_object_glyph(const RID &p_shaped, const Variant &p_key) const {5001const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5002ERR_FAIL_NULL_V(sd, -1);50035004MutexLock lock(sd->mutex);5005ERR_FAIL_COND_V(!sd->objects.has(p_key), -1);5006const ShapedTextDataFallback::EmbeddedObject &obj = sd->objects[p_key];5007int sd_size = sd->glyphs.size();5008const Glyph *sd_glyphs = sd->glyphs.ptr();5009for (int i = 0; i < sd_size; i++) {5010if (obj.start == sd_glyphs[i].start) {5011return i;5012}5013}5014return -1;5015}50165017Size2 TextServerFallback::_shaped_text_get_size(const RID &p_shaped) const {5018const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5019ERR_FAIL_NULL_V(sd, Size2());50205021MutexLock lock(sd->mutex);5022if (!sd->valid.is_set()) {5023const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);5024}5025if (sd->orientation == TextServer::ORIENTATION_HORIZONTAL) {5026return Size2(sd->width, sd->ascent + sd->descent + sd->extra_spacing[SPACING_TOP] + sd->extra_spacing[SPACING_BOTTOM]).ceil();5027} else {5028return Size2(sd->ascent + sd->descent + sd->extra_spacing[SPACING_TOP] + sd->extra_spacing[SPACING_BOTTOM], sd->width).ceil();5029}5030}50315032double TextServerFallback::_shaped_text_get_ascent(const RID &p_shaped) const {5033const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5034ERR_FAIL_NULL_V(sd, 0.0);50355036MutexLock lock(sd->mutex);5037if (!sd->valid.is_set()) {5038const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);5039}5040return sd->ascent + sd->extra_spacing[SPACING_TOP];5041}50425043double TextServerFallback::_shaped_text_get_descent(const RID &p_shaped) const {5044const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5045ERR_FAIL_NULL_V(sd, 0.0);50465047MutexLock lock(sd->mutex);5048if (!sd->valid.is_set()) {5049const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);5050}5051return sd->descent + sd->extra_spacing[SPACING_BOTTOM];5052}50535054double TextServerFallback::_shaped_text_get_width(const RID &p_shaped) const {5055const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5056ERR_FAIL_NULL_V(sd, 0.0);50575058MutexLock lock(sd->mutex);5059if (!sd->valid.is_set()) {5060const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);5061}5062return Math::ceil(sd->width);5063}50645065double TextServerFallback::_shaped_text_get_underline_position(const RID &p_shaped) const {5066const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5067ERR_FAIL_NULL_V(sd, 0.0);50685069MutexLock lock(sd->mutex);5070if (!sd->valid.is_set()) {5071const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);5072}50735074return sd->upos;5075}50765077double TextServerFallback::_shaped_text_get_underline_thickness(const RID &p_shaped) const {5078const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5079ERR_FAIL_NULL_V(sd, 0.0);50805081MutexLock lock(sd->mutex);5082if (!sd->valid.is_set()) {5083const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);5084}50855086return sd->uthk;5087}50885089PackedInt32Array TextServerFallback::_shaped_text_get_character_breaks(const RID &p_shaped) const {5090const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);5091ERR_FAIL_NULL_V(sd, PackedInt32Array());50925093MutexLock lock(sd->mutex);5094if (!sd->valid.is_set()) {5095const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);5096}50975098PackedInt32Array ret;5099int size = sd->end - sd->start;5100if (size > 0) {5101ret.resize(size);5102for (int i = 0; i < size; i++) {5103#ifdef GDEXTENSION5104ret[i] = i + 1 + sd->start;5105#else5106ret.write[i] = i + 1 + sd->start;5107#endif5108}5109}5110return ret;5111}51125113String TextServerFallback::_string_to_upper(const String &p_string, const String &p_language) const {5114return p_string.to_upper();5115}51165117String TextServerFallback::_string_to_lower(const String &p_string, const String &p_language) const {5118return p_string.to_lower();5119}51205121String TextServerFallback::_string_to_title(const String &p_string, const String &p_language) const {5122return p_string.capitalize();5123}51245125PackedInt32Array TextServerFallback::_string_get_word_breaks(const String &p_string, const String &p_language, int64_t p_chars_per_line) const {5126PackedInt32Array ret;51275128if (p_chars_per_line > 0) {5129int line_start = 0;5130int last_break = -1;5131int line_length = 0;51325133for (int i = 0; i < p_string.length(); i++) {5134const char32_t c = p_string[i];51355136bool is_lb = is_linebreak(c);5137bool is_ws = is_whitespace(c);5138bool is_p = (is_punct(c) && c != 0x005F) || is_underscore(c) || c == '\t' || c == 0xfffc;51395140if (is_lb) {5141if (line_length > 0) {5142ret.push_back(line_start);5143ret.push_back(i);5144}5145line_start = i;5146line_length = 0;5147last_break = -1;5148continue;5149} else if (is_ws || is_p) {5150last_break = i;5151}51525153if (line_length == p_chars_per_line) {5154if (last_break != -1) {5155int last_break_w_spaces = last_break;5156while (last_break > line_start && is_whitespace(p_string[last_break - 1])) {5157last_break--;5158}5159if (line_start != last_break) {5160ret.push_back(line_start);5161ret.push_back(last_break);5162}5163while (last_break_w_spaces < p_string.length() && is_whitespace(p_string[last_break_w_spaces])) {5164last_break_w_spaces++;5165}5166line_start = last_break_w_spaces;5167if (last_break_w_spaces < i) {5168line_length = i - last_break_w_spaces;5169} else {5170i = last_break_w_spaces;5171line_length = 0;5172}5173} else {5174ret.push_back(line_start);5175ret.push_back(i);5176line_start = i;5177line_length = 0;5178}5179last_break = -1;5180}5181line_length++;5182}5183if (line_length > 0) {5184ret.push_back(line_start);5185ret.push_back(p_string.length());5186}5187} else {5188int word_start = 0; // -1 if no word encountered. Leading spaces are part of a word.5189int word_length = 0;51905191for (int i = 0; i < p_string.length(); i++) {5192const char32_t c = p_string[i];51935194bool is_lb = is_linebreak(c);5195bool is_ws = is_whitespace(c);5196bool is_p = (is_punct(c) && c != 0x005F) || is_underscore(c) || c == '\t' || c == 0xfffc;51975198if (word_start == -1) {5199if (!is_lb && !is_ws && !is_p) {5200word_start = i;5201}5202continue;5203}52045205if (is_lb) {5206if (word_start != -1 && word_length > 0) {5207ret.push_back(word_start);5208ret.push_back(i);5209}5210word_start = -1;5211word_length = 0;5212} else if (is_ws || is_p) {5213if (word_start != -1 && word_length > 0) {5214ret.push_back(word_start);5215ret.push_back(i);5216}5217word_start = -1;5218word_length = 0;5219}52205221word_length++;5222}5223if (word_start != -1 && word_length > 0) {5224ret.push_back(word_start);5225ret.push_back(p_string.length());5226}5227}5228return ret;5229}52305231void TextServerFallback::_update_settings() {5232lcd_subpixel_layout.set((TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"));5233}52345235TextServerFallback::TextServerFallback() {5236_insert_feature_sets();5237ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &TextServerFallback::_update_settings));5238}52395240void TextServerFallback::_font_clear_system_fallback_cache() {5241for (const KeyValue<SystemFontKey, SystemFontCache> &E : system_fonts) {5242const Vector<SystemFontCacheRec> &sysf_cache = E.value.var;5243for (const SystemFontCacheRec &F : sysf_cache) {5244_free_rid(F.rid);5245}5246}5247system_fonts.clear();5248system_font_data.clear();5249}52505251void TextServerFallback::_cleanup() {5252font_clear_system_fallback_cache();5253}52545255TextServerFallback::~TextServerFallback() {5256#ifdef MODULE_FREETYPE_ENABLED5257if (ft_library != nullptr) {5258FT_Done_FreeType(ft_library);5259}5260#endif5261}526252635264