diff options
Diffstat (limited to 'modules/text_server_adv')
-rw-r--r-- | modules/text_server_adv/SCsub | 24 | ||||
-rw-r--r-- | modules/text_server_adv/gdextension_build/SConstruct | 121 | ||||
-rw-r--r-- | modules/text_server_adv/gdextension_build/text_server_adv.gdextension | 24 | ||||
-rw-r--r-- | modules/text_server_adv/icu_data/icudata_stub.cpp | 58 | ||||
-rw-r--r-- | modules/text_server_adv/register_types.cpp | 60 | ||||
-rw-r--r-- | modules/text_server_adv/register_types.h | 58 | ||||
-rw-r--r-- | modules/text_server_adv/script_iterator.cpp | 58 | ||||
-rw-r--r-- | modules/text_server_adv/script_iterator.h | 58 | ||||
-rw-r--r-- | modules/text_server_adv/text_server_adv.cpp | 1614 | ||||
-rw-r--r-- | modules/text_server_adv/text_server_adv.h | 644 | ||||
-rw-r--r-- | modules/text_server_adv/thorvg_bounds_iterator.cpp | 70 | ||||
-rw-r--r-- | modules/text_server_adv/thorvg_bounds_iterator.h | 58 | ||||
-rw-r--r-- | modules/text_server_adv/thorvg_svg_in_ot.cpp | 320 | ||||
-rw-r--r-- | modules/text_server_adv/thorvg_svg_in_ot.h | 86 |
14 files changed, 2317 insertions, 936 deletions
diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub index c6678307af..4a9c7b3567 100644 --- a/modules/text_server_adv/SCsub +++ b/modules/text_server_adv/SCsub @@ -39,6 +39,9 @@ thirdparty_obj = [] freetype_enabled = "freetype" in env.module_list msdfgen_enabled = "msdfgen" in env.module_list +if "svg" in env.module_list: + env_text_server_adv.Prepend(CPPPATH=["#thirdparty/thorvg/inc", "#thirdparty/thorvg/src/lib"]) + if env["builtin_harfbuzz"]: env_harfbuzz = env_modules.Clone() env_harfbuzz.disable_warnings() @@ -131,24 +134,23 @@ if env["builtin_harfbuzz"]: env_harfbuzz.Append( CCFLAGS=[ "-DHAVE_FREETYPE", - "-DHAVE_GRAPHITE2", ] ) + if env["graphite"]: + env_harfbuzz.Append( + CCFLAGS=[ + "-DHAVE_GRAPHITE2", + ] + ) if env["builtin_freetype"]: env_harfbuzz.Prepend(CPPPATH=["#thirdparty/freetype/include"]) if env["builtin_graphite"] and env["graphite"]: env_harfbuzz.Prepend(CPPPATH=["#thirdparty/graphite/include"]) env_harfbuzz.Append(CCFLAGS=["-DGRAPHITE2_STATIC"]) - if env["platform"] == "android" or env["platform"] == "linuxbsd": + if env["platform"] in ["android", "linuxbsd", "web"]: env_harfbuzz.Append(CCFLAGS=["-DHAVE_PTHREAD"]) - if env["platform"] == "javascript": - if env["threads_enabled"]: - env_harfbuzz.Append(CCFLAGS=["-DHAVE_PTHREAD"]) - else: - env_harfbuzz.Append(CCFLAGS=["-DHB_NO_MT"]) - env_text_server_adv.Prepend(CPPPATH=["#thirdparty/harfbuzz/src"]) lib = env_harfbuzz.add_library("harfbuzz_builtin", thirdparty_sources) @@ -449,9 +451,9 @@ if env["builtin_icu"]: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - icu_data_name = "icudt71l.dat" + icu_data_name = "icudt72l.dat" - if env_icu["tools"]: + if env.editor_build: env_icu.Depends("#thirdparty/icu4c/icudata.gen.h", "#thirdparty/icu4c/" + icu_data_name) env_icu.Command("#thirdparty/icu4c/icudata.gen.h", "#thirdparty/icu4c/" + icu_data_name, make_icu_data) env_text_server_adv.Prepend(CPPPATH=["#thirdparty/icu4c/"]) @@ -485,7 +487,7 @@ if env["builtin_icu"]: "-DICU_DATA_NAME=" + icu_data_name, ] ) - if env_text_server_adv["tools"]: + if env.editor_build: env_text_server_adv.Append(CXXFLAGS=["-DICU_STATIC_DATA"]) env_text_server_adv.Prepend(CPPPATH=["#thirdparty/icu4c/common/", "#thirdparty/icu4c/i18n/"]) diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct index 0170c007ae..4a363fdd7a 100644 --- a/modules/text_server_adv/gdextension_build/SConstruct +++ b/modules/text_server_adv/gdextension_build/SConstruct @@ -19,9 +19,11 @@ env = SConscript("./godot-cpp/SConstruct") env.__class__.disable_warnings = methods.disable_warnings opts = Variables([], ARGUMENTS) +opts.Add(BoolVariable("brotli_enabled", "Use Brotli library", True)) opts.Add(BoolVariable("freetype_enabled", "Use FreeType library", True)) opts.Add(BoolVariable("msdfgen_enabled", "Use MSDFgen library (require FreeType)", True)) opts.Add(BoolVariable("graphite_enabled", "Use Graphite library (require FreeType)", True)) +opts.Add(BoolVariable("thorvg_enabled", "Use ThorVG library (require FreeType)", True)) opts.Add(BoolVariable("static_icu_data", "Use built-in ICU data", True)) opts.Add(BoolVariable("verbose", "Enable verbose output for the compilation", False)) @@ -33,6 +35,79 @@ if not env["verbose"]: if env["platform"] == "windows" and not env["use_mingw"]: env.AppendUnique(CCFLAGS=["/utf-8"]) # Force to use Unicode encoding. +# ThorVG +if env["thorvg_enabled"] and env["freetype_enabled"]: + env_tvg = env.Clone() + env_tvg.disable_warnings() + + thirdparty_tvg_dir = "../../../thirdparty/thorvg/" + thirdparty_tvg_sources = [ + "src/lib/sw_engine/tvgSwFill.cpp", + "src/lib/sw_engine/tvgSwImage.cpp", + "src/lib/sw_engine/tvgSwMath.cpp", + "src/lib/sw_engine/tvgSwMemPool.cpp", + "src/lib/sw_engine/tvgSwRaster.cpp", + "src/lib/sw_engine/tvgSwRenderer.cpp", + "src/lib/sw_engine/tvgSwRle.cpp", + "src/lib/sw_engine/tvgSwShape.cpp", + "src/lib/sw_engine/tvgSwStroke.cpp", + "src/lib/tvgAccessor.cpp", + "src/lib/tvgBezier.cpp", + "src/lib/tvgCanvas.cpp", + "src/lib/tvgFill.cpp", + "src/lib/tvgGlCanvas.cpp", + "src/lib/tvgInitializer.cpp", + "src/lib/tvgLinearGradient.cpp", + "src/lib/tvgLoader.cpp", + "src/lib/tvgLzw.cpp", + "src/lib/tvgPaint.cpp", + "src/lib/tvgPicture.cpp", + "src/lib/tvgRadialGradient.cpp", + "src/lib/tvgRender.cpp", + "src/lib/tvgSaver.cpp", + "src/lib/tvgScene.cpp", + "src/lib/tvgShape.cpp", + "src/lib/tvgSwCanvas.cpp", + "src/lib/tvgTaskScheduler.cpp", + "src/loaders/external_png/tvgPngLoader.cpp", + "src/loaders/jpg/tvgJpgd.cpp", + "src/loaders/jpg/tvgJpgLoader.cpp", + "src/loaders/raw/tvgRawLoader.cpp", + "src/loaders/svg/tvgSvgCssStyle.cpp", + "src/loaders/svg/tvgSvgLoader.cpp", + "src/loaders/svg/tvgSvgPath.cpp", + "src/loaders/svg/tvgSvgSceneBuilder.cpp", + "src/loaders/svg/tvgSvgUtil.cpp", + "src/loaders/svg/tvgXmlParser.cpp", + "src/loaders/tvg/tvgTvgBinInterpreter.cpp", + "src/loaders/tvg/tvgTvgLoader.cpp", + "src/savers/tvg/tvgTvgSaver.cpp", + ] + thirdparty_tvg_sources = [thirdparty_tvg_dir + file for file in thirdparty_tvg_sources] + + env_tvg.Append( + CPPPATH=[ + "../../../thirdparty/thorvg/inc", + "../../../thirdparty/thorvg/src/lib", + "../../../thirdparty/thorvg/src/lib/sw_engine", + "../../../thirdparty/thorvg/src/loaders/external_png", + "../../../thirdparty/thorvg/src/loaders/jpg", + "../../../thirdparty/thorvg/src/loaders/raw", + "../../../thirdparty/thorvg/src/loaders/svg", + "../../../thirdparty/thorvg/src/loaders/tvg", + "../../../thirdparty/thorvg/src/savers/tvg", + "../../../thirdparty/libpng", + ] + ) + env.Append(CPPPATH=["../../../thirdparty/thorvg/inc", "../../../thirdparty/thorvg/src/lib"]) + env.Append(CPPDEFINES=["MODULE_SVG_ENABLED"]) + + lib = env_tvg.Library( + f'tvg_builtin{env["suffix"]}{env["LIBSUFFIX"]}', + thirdparty_tvg_sources, + ) + env.Append(LIBS=[lib]) + # MSDFGEN if env["msdfgen_enabled"] and env["freetype_enabled"]: env_msdfgen = env.Clone() @@ -67,7 +142,7 @@ if env["msdfgen_enabled"] and env["freetype_enabled"]: env.Append(CPPDEFINES=["MODULE_MSDFGEN_ENABLED"]) lib = env_msdfgen.Library( - f'msdfgen_builtin.{env["platform"]}.{env["target"]}.{env["arch_suffix"]}{env["LIBSUFFIX"]}', + f'msdfgen_builtin{env["suffix"]}{env["LIBSUFFIX"]}', thirdparty_msdfgen_sources, ) env.Append(LIBS=[lib]) @@ -162,6 +237,25 @@ if env["freetype_enabled"]: ] thirdparty_freetype_sources += [thirdparty_zlib_dir + file for file in thirdparty_zlib_sources] + if env["brotli_enabled"]: + thirdparty_brotli_dir = "../../../thirdparty/brotli/" + thirdparty_brotli_sources = [ + "common/constants.c", + "common/context.c", + "common/dictionary.c", + "common/platform.c", + "common/shared_dictionary.c", + "common/transform.c", + "dec/bit_reader.c", + "dec/decode.c", + "dec/huffman.c", + "dec/state.c", + ] + thirdparty_freetype_sources += [thirdparty_brotli_dir + file for file in thirdparty_brotli_sources] + env_freetype.Append(CPPDEFINES=["FT_CONFIG_OPTION_USE_BROTLI"]) + env_freetype.Prepend(CPPPATH=[thirdparty_brotli_dir + "include"]) + env.Append(CPPDEFINES=["FT_CONFIG_OPTION_USE_BROTLI"]) + env_freetype.Append(CPPPATH=[thirdparty_freetype_dir + "/include", thirdparty_zlib_dir, thirdparty_png_dir]) env.Append(CPPPATH=[thirdparty_freetype_dir + "/include"]) @@ -173,13 +267,13 @@ if env["freetype_enabled"]: "FT_CONFIG_OPTION_SYSTEM_ZLIB", ] ) - if env["target"] == "debug": + if env.dev_build: env_freetype.Append(CPPDEFINES=["ZLIB_DEBUG"]) env.Append(CPPDEFINES=["MODULE_FREETYPE_ENABLED"]) lib = env_freetype.Library( - f'freetype_builtin.{env["platform"]}.{env["target"]}.{env["arch_suffix"]}{env["LIBSUFFIX"]}', + f'freetype_builtin{env["suffix"]}{env["LIBSUFFIX"]}', thirdparty_freetype_sources, ) env.Append(LIBS=[lib]) @@ -265,6 +359,7 @@ env_harfbuzz.Append( CPPPATH=[ "../../../thirdparty/harfbuzz/src", "../../../thirdparty/icu4c/common/", + "../../../thirdparty/icu4c/i18n/", ] ) @@ -300,7 +395,7 @@ if env["freetype_enabled"]: env.Append(CPPPATH=["../../../thirdparty/harfbuzz/src"]) lib = env_harfbuzz.Library( - f'harfbuzz_builtin.{env["platform"]}.{env["target"]}.{env["arch_suffix"]}{env["LIBSUFFIX"]}', + f'harfbuzz_builtin{env["suffix"]}{env["LIBSUFFIX"]}', thirdparty_harfbuzz_sources, ) env.Prepend(LIBS=[lib]) @@ -360,7 +455,7 @@ if env["graphite_enabled"] and env["freetype_enabled"]: ) lib = env_graphite.Library( - f'graphite_builtin.{env["platform"]}.{env["target"]}.{env["arch_suffix"]}{env["LIBSUFFIX"]}', + f'graphite_builtin{env["suffix"]}{env["LIBSUFFIX"]}', thirdparty_graphite_sources, ) env.Append(LIBS=[lib]) @@ -569,10 +664,14 @@ thirdparty_icu_sources = [ "common/uvectr32.cpp", "common/uvectr64.cpp", "common/wintz.cpp", + "i18n/scriptset.cpp", + "i18n/ucln_in.cpp", + "i18n/uspoof.cpp", + "i18n/uspoof_impl.cpp", ] thirdparty_icu_sources = [thirdparty_icu_dir + file for file in thirdparty_icu_sources] -icu_data_name = "icudt71l.dat" +icu_data_name = "icudt72l.dat" if env["static_icu_data"]: env_icu.Depends("../../../thirdparty/icu4c/icudata.gen.h", "../../../thirdparty/icu4c/" + icu_data_name) @@ -584,7 +683,7 @@ if env["static_icu_data"]: else: thirdparty_sources += ["../icu_data/icudata_stub.cpp"] -env_icu.Append(CPPPATH=["../../../thirdparty/icu4c/common/"]) +env_icu.Append(CPPPATH=["../../../thirdparty/icu4c/common/", "../../../thirdparty/icu4c/i18n/"]) env_icu.Append( CXXFLAGS=[ "-DU_STATIC_IMPLEMENTATION", @@ -610,14 +709,12 @@ env.Append( "-DICU_DATA_NAME=" + icu_data_name, ] ) -env.Append(CPPPATH=["../../../thirdparty/icu4c/common/"]) +env.Append(CPPPATH=["../../../thirdparty/icu4c/common/", "../../../thirdparty/icu4c/i18n/"]) if env["platform"] == "windows": env.Append(LIBS=["advapi32"]) -lib = env_icu.Library( - f'icu_builtin.{env["platform"]}.{env["target"]}.{env["arch_suffix"]}{env["LIBSUFFIX"]}', thirdparty_icu_sources -) +lib = env_icu.Library(f'icu_builtin{env["suffix"]}{env["LIBSUFFIX"]}', thirdparty_icu_sources) env.Append(LIBS=[lib]) env.Append(CPPDEFINES=["GDEXTENSION"]) @@ -637,7 +734,7 @@ if env["platform"] == "macos": ) else: library = env.SharedLibrary( - f'./bin/libtextserver_advanced.{env["platform"]}.{env["target"]}.{env["arch_suffix"]}{env["SHLIBSUFFIX"]}', + f'./bin/libtextserver_advanced{env["suffix"]}{env["SHLIBSUFFIX"]}', source=sources, ) diff --git a/modules/text_server_adv/gdextension_build/text_server_adv.gdextension b/modules/text_server_adv/gdextension_build/text_server_adv.gdextension index 11ed271ae9..c12fcfdfdf 100644 --- a/modules/text_server_adv/gdextension_build/text_server_adv.gdextension +++ b/modules/text_server_adv/gdextension_build/text_server_adv.gdextension @@ -4,9 +4,21 @@ entry_symbol = "textserver_advanced_init" [libraries] -linux.64.debug = "bin/libtextserver_advanced.linux.debug.64.so" -linux.64.release = "bin/libtextserver_advanced.linux.release.64.so" -windows.64.debug = "bin/libtextserver_advanced.windows.debug.64.dll" -windows.64.release = "bin/libtextserver_advanced.windows.release.64.dll" -macos.debug = "bin/libtextserver_advanced.macos.debug.framework" -macos.release = "bin/libtextserver_advanced.macos.release.framework" +linux.x86_64.debug = "bin/libtextserver_advanced.linux.template_debug.x86_64.so" +linux.x86_64.release = "bin/libtextserver_advanced.linux.template_release.x86_64.so" +linux.x86_32.debug = "bin/libtextserver_advanced.linux.template_debug.x86_32.so" +linux.x86_32.release = "bin/libtextserver_advanced.linux.template_release.x86_32.so" +linux.arm64.debug = "bin/libtextserver_advanced.linux.template_debug.arm64.so" +linux.arm64.release = "bin/libtextserver_advanced.linux.template_release.arm64.so" +linux.rv64.debug = "bin/libtextserver_advanced.linux.template_debug.rv64.so" +linux.rv64.release = "bin/libtextserver_advanced.linux.template_release.rv64.so" + +windows.x86_64.debug = "bin/libtextserver_advanced.windows.template_debug.x86_64.dll" +windows.x86_64.release = "bin/libtextserver_advanced.windows.template_release.x86_64.dll" +windows.x86_32.debug = "bin/libtextserver_advanced.windows.template_debug.x86_32.dll" +windows.x86_32.release = "bin/libtextserver_advanced.windows.template_release.x86_32.dll" +windows.arm64.debug = "bin/libtextserver_advanced.windows.template_debug.arm64.dll" +windows.arm64.release = "bin/libtextserver_advanced.windows.template_release.arm64.dll" + +macos.debug = "bin/libtextserver_advanced.macos.template_debug.framework" +macos.release = "bin/libtextserver_advanced.macos.template_release.framework" diff --git a/modules/text_server_adv/icu_data/icudata_stub.cpp b/modules/text_server_adv/icu_data/icudata_stub.cpp index 47dfa5ce26..a6a9e1cc1d 100644 --- a/modules/text_server_adv/icu_data/icudata_stub.cpp +++ b/modules/text_server_adv/icu_data/icudata_stub.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* icudata_stub.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* icudata_stub.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #include "unicode/udata.h" #include "unicode/utypes.h" diff --git a/modules/text_server_adv/register_types.cpp b/modules/text_server_adv/register_types.cpp index 6a26584506..7820cd5e61 100644 --- a/modules/text_server_adv/register_types.cpp +++ b/modules/text_server_adv/register_types.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* register_types.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* register_types.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #include "register_types.h" @@ -62,7 +62,7 @@ using namespace godot; extern "C" { -GDNativeBool GDN_EXPORT textserver_advanced_init(const GDNativeInterface *p_interface, const GDNativeExtensionClassLibraryPtr p_library, GDNativeInitialization *r_initialization) { +GDExtensionBool GDN_EXPORT textserver_advanced_init(const GDExtensionInterface *p_interface, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) { GDExtensionBinding::InitObject init_obj(p_interface, p_library, r_initialization); init_obj.register_initializer(&initialize_text_server_adv_module); diff --git a/modules/text_server_adv/register_types.h b/modules/text_server_adv/register_types.h index dfe20c860c..c11765048d 100644 --- a/modules/text_server_adv/register_types.h +++ b/modules/text_server_adv/register_types.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* register_types.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* register_types.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef TEXT_SERVER_ADV_REGISTER_TYPES_H #define TEXT_SERVER_ADV_REGISTER_TYPES_H diff --git a/modules/text_server_adv/script_iterator.cpp b/modules/text_server_adv/script_iterator.cpp index 3331254b20..fbe6373538 100644 --- a/modules/text_server_adv/script_iterator.cpp +++ b/modules/text_server_adv/script_iterator.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* script_iterator.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* script_iterator.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #include "script_iterator.h" diff --git a/modules/text_server_adv/script_iterator.h b/modules/text_server_adv/script_iterator.h index 025b62c6fb..164fdec784 100644 --- a/modules/text_server_adv/script_iterator.h +++ b/modules/text_server_adv/script_iterator.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* script_iterator.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* script_iterator.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef SCRIPT_ITERATOR_H #define SCRIPT_ITERATOR_H diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 73dbf2f443..44a2c76727 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -1,57 +1,59 @@ -/*************************************************************************/ -/* text_server_adv.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* text_server_adv.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #include "text_server_adv.h" -#include "core/object/worker_thread_pool.h" #ifdef GDEXTENSION // Headers for building as GDExtension plug-in. -#include <godot_cpp/classes/file.hpp> +#include <godot_cpp/classes/file_access.hpp> +#include <godot_cpp/classes/os.hpp> +#include <godot_cpp/classes/project_settings.hpp> #include <godot_cpp/classes/rendering_server.hpp> #include <godot_cpp/classes/translation_server.hpp> #include <godot_cpp/core/error_macros.hpp> using namespace godot; +#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get(m_var) + #else // Headers for building as built-in module. -#include "core/core_bind.h" +#include "core/config/project_settings.h" #include "core/error/error_macros.h" +#include "core/object/worker_thread_pool.h" #include "core/string/print_string.h" #include "core/string/translation.h" -#include "modules/modules_enabled.gen.h" // For freetype, msdfgen. - -using namespace core_bind; +#include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg. #endif @@ -70,6 +72,10 @@ using namespace core_bind; #include "msdfgen.h" #endif +#ifdef MODULE_SVG_ENABLED +#include "thorvg_svg_in_ot.h" +#endif + /*************************************************************************/ /* bmp_font_t HarfBuzz Bitmap font interface */ /*************************************************************************/ @@ -328,7 +334,7 @@ _FORCE_INLINE_ bool is_connected_to_prev(char32_t p_chr, char32_t p_pchr) { /*************************************************************************/ -bool TextServerAdvanced::has_feature(Feature p_feature) const { +bool TextServerAdvanced::_has_feature(Feature p_feature) const { switch (p_feature) { case FEATURE_SIMPLE_LAYOUT: case FEATURE_BIDI_LAYOUT: @@ -355,7 +361,7 @@ bool TextServerAdvanced::has_feature(Feature p_feature) const { return false; } -String TextServerAdvanced::get_name() const { +String TextServerAdvanced::_get_name() const { #ifdef GDEXTENSION return "ICU / HarfBuzz / Graphite (GDExtension)"; #else @@ -363,7 +369,7 @@ String TextServerAdvanced::get_name() const { #endif } -int64_t TextServerAdvanced::get_features() const { +int64_t TextServerAdvanced::_get_features() const { int64_t interface_features = FEATURE_SIMPLE_LAYOUT | FEATURE_BIDI_LAYOUT | FEATURE_VERTICAL_LAYOUT | FEATURE_SHAPING | FEATURE_KASHIDA_JUSTIFICATION | FEATURE_BREAK_ITERATORS | FEATURE_FONT_BITMAP | FEATURE_FONT_VARIABLE | FEATURE_CONTEXT_SENSITIVE_CASE_CONVERSION | FEATURE_USE_SUPPORT_DATA; #ifdef MODULE_FREETYPE_ENABLED interface_features |= FEATURE_FONT_DYNAMIC; @@ -375,25 +381,31 @@ int64_t TextServerAdvanced::get_features() const { return interface_features; } -void TextServerAdvanced::free_rid(const RID &p_rid) { +void TextServerAdvanced::_free_rid(const RID &p_rid) { _THREAD_SAFE_METHOD_ if (font_owner.owns(p_rid)) { FontAdvanced *fd = font_owner.get_or_null(p_rid); - font_owner.free(p_rid); + { + MutexLock lock(fd->mutex); + font_owner.free(p_rid); + } memdelete(fd); } else if (shaped_owner.owns(p_rid)) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_rid); - shaped_owner.free(p_rid); + { + MutexLock lock(sd->mutex); + shaped_owner.free(p_rid); + } memdelete(sd); } } -bool TextServerAdvanced::has(const RID &p_rid) { +bool TextServerAdvanced::_has(const RID &p_rid) { _THREAD_SAFE_METHOD_ return font_owner.owns(p_rid) || shaped_owner.owns(p_rid); } -bool TextServerAdvanced::load_support_data(const String &p_filename) { +bool TextServerAdvanced::_load_support_data(const String &p_filename) { _THREAD_SAFE_METHOD_ #ifdef ICU_STATIC_DATA @@ -406,13 +418,16 @@ bool TextServerAdvanced::load_support_data(const String &p_filename) { if (!icu_data_loaded) { String filename = (p_filename.is_empty()) ? String("res://") + _MKSTR(ICU_DATA_NAME) : p_filename; - Ref<File> f; - f.instantiate(); - if (f->open(filename, File::READ) != OK) { + Ref<FileAccess> f = FileAccess::open(filename, FileAccess::READ); + if (f.is_null()) { return false; } uint64_t len = f->get_length(); +#ifdef GDEXTENSION PackedByteArray icu_data = f->get_buffer(len); +#else + PackedByteArray icu_data = f->_get_buffer(len); +#endif UErrorCode err = U_ZERO_ERROR; udata_setCommonData(icu_data.ptr(), &err); @@ -431,7 +446,7 @@ bool TextServerAdvanced::load_support_data(const String &p_filename) { return true; } -String TextServerAdvanced::get_support_data_filename() const { +String TextServerAdvanced::_get_support_data_filename() const { #ifdef ICU_STATIC_DATA return _MKSTR(ICU_DATA_NAME); #else @@ -439,7 +454,7 @@ String TextServerAdvanced::get_support_data_filename() const { #endif } -String TextServerAdvanced::get_support_data_info() const { +String TextServerAdvanced::_get_support_data_info() const { #ifdef ICU_STATIC_DATA return String("ICU break iteration data (") + _MKSTR(ICU_DATA_NAME) + String(")."); #else @@ -447,22 +462,25 @@ String TextServerAdvanced::get_support_data_info() const { #endif } -bool TextServerAdvanced::save_support_data(const String &p_filename) const { +bool TextServerAdvanced::_save_support_data(const String &p_filename) const { _THREAD_SAFE_METHOD_ #ifdef ICU_STATIC_DATA // Store data to the res file if it's available. - Ref<File> f; - f.instantiate(); - if (f->open(p_filename, File::WRITE) != OK) { + Ref<FileAccess> f = FileAccess::open(p_filename, FileAccess::WRITE); + if (f.is_null()) { return false; } PackedByteArray icu_data; icu_data.resize(U_ICUDATA_SIZE); memcpy(icu_data.ptrw(), U_ICUDATA_ENTRY_POINT, U_ICUDATA_SIZE); +#ifdef GDEXTENSION f->store_buffer(icu_data); +#else + f->_store_buffer(icu_data); +#endif return true; #else @@ -470,7 +488,7 @@ bool TextServerAdvanced::save_support_data(const String &p_filename) const { #endif } -bool TextServerAdvanced::is_locale_right_to_left(const String &p_locale) const { +bool TextServerAdvanced::_is_locale_right_to_left(const String &p_locale) const { String l = p_locale.get_slicec('_', 0); if ((l == "ar") || (l == "dv") || (l == "he") || (l == "fa") || (l == "ff") || (l == "ku") || (l == "ur")) { return true; @@ -742,7 +760,7 @@ void TextServerAdvanced::_insert_feature_sets() { _insert_feature("weight", HB_TAG('w', 'g', 'h', 't'), Variant::Type::INT, false); } -int64_t TextServerAdvanced::name_to_tag(const String &p_name) const { +int64_t TextServerAdvanced::_name_to_tag(const String &p_name) const { if (feature_sets.has(p_name)) { return feature_sets[p_name]; } @@ -765,7 +783,7 @@ bool TextServerAdvanced::_get_tag_hidden(int64_t p_tag) const { return false; } -String TextServerAdvanced::tag_to_name(int64_t p_tag) const { +String TextServerAdvanced::_tag_to_name(int64_t p_tag) const { if (feature_sets_inv.has(p_tag)) { return feature_sets_inv[p_tag].name; } @@ -783,54 +801,27 @@ String TextServerAdvanced::tag_to_name(int64_t p_tag) const { _FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::find_texture_pos_for_glyph(FontForSizeAdvanced *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height, bool p_msdf) const { FontTexturePosition ret; - ret.index = -1; int mw = p_width; int mh = p_height; - for (int i = 0; i < p_data->textures.size(); i++) { - const FontTexture &ct = p_data->textures[i]; - - if (mw > ct.texture_w || mh > ct.texture_h) { // Too big for this texture. + ShelfPackTexture *ct = p_data->textures.ptrw(); + for (int32_t i = 0; i < p_data->textures.size(); i++) { + if (p_image_format != ct[i].format) { continue; } - - if (ct.offsets.size() < ct.texture_w) { + if (mw > ct[i].texture_w || mh > ct[i].texture_h) { // Too big for this texture. continue; } - ret.y = 0x7fffffff; - ret.x = 0; - - for (int j = 0; j < ct.texture_w - mw; j++) { - int max_y = 0; - - for (int k = j; k < j + mw; k++) { - int y = ct.offsets[k]; - if (y > max_y) { - max_y = y; - } - } - - if (max_y < ret.y) { - ret.y = max_y; - ret.x = j; - } - } - - if (ret.y == 0x7fffffff || ret.y + mh > ct.texture_h) { - continue; // Fail, could not fit it here. + ret = ct[i].pack_rect(i, mh, mw); + if (ret.index != -1) { + break; } - - ret.index = i; - break; } if (ret.index == -1) { // Could not find texture to fit, create one. - ret.x = 0; - ret.y = 0; - int texsize = MAX(p_data->size.x * p_data->oversampling * 8, 256); #ifdef GDEXTENSION @@ -858,12 +849,9 @@ _FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::find_ #endif } - FontTexture tex; - tex.texture_w = texsize; - tex.texture_h = texsize; + ShelfPackTexture tex = ShelfPackTexture(texsize, texsize); tex.format = p_image_format; tex.imgdata.resize(texsize * texsize * p_color_size); - { // Zero texture. uint8_t *w = tex.imgdata.ptrw(); @@ -886,14 +874,10 @@ _FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::find_ ERR_FAIL_V(ret); } } - tex.offsets.resize(texsize); - int32_t *offw = tex.offsets.ptrw(); - for (int i = 0; i < texsize; i++) { // Zero offsets. - offw[i] = 0; - } - p_data->textures.push_back(tex); - ret.index = p_data->textures.size() - 1; + + int32_t idx = p_data->textures.size() - 1; + ret = p_data->textures.write[idx].pack_rect(idx, mh, mw); } return ret; @@ -1027,7 +1011,7 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf( FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, 4, Image::FORMAT_RGBA8, mw, mh, true); ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph()); - FontTexture &tex = p_data->textures.write[tex_pos.index]; + ShelfPackTexture &tex = p_data->textures.write[tex_pos.index]; edgeColoringSimple(shape, 3.0); // Max. angle. msdfgen::Bitmap<float, 4> image(w, h); // Texture size. @@ -1042,8 +1026,14 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf( td.projection = &projection; td.distancePixelConversion = &distancePixelConversion; +#ifdef GDEXTENSION + for (int i = 0; i < h; i++) { + _generateMTSDF_threaded(i, &td); + } +#else WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &TextServerAdvanced::_generateMTSDF_threaded, &td, h, -1, true, SNAME("FontServerRasterizeMSDF")); WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task); +#endif msdfgen::msdfErrorCorrection(image, shape, projection, p_pixel_range, config); @@ -1064,12 +1054,6 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf( tex.dirty = true; - // Update height array. - int32_t *offw = tex.offsets.ptrw(); - for (int k = tex_pos.x; k < tex_pos.x + mw; k++) { - offw[k] = tex_pos.y + mh; - } - chr.texture_idx = tex_pos.index; chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w + p_rect_margin * 2, h + p_rect_margin * 2); @@ -1082,9 +1066,28 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf( #endif #ifdef MODULE_FREETYPE_ENABLED -_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const { +_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance, bool p_bgra) const { int w = bitmap.width; int h = bitmap.rows; + int color_size = 2; + + switch (bitmap.pixel_mode) { + case FT_PIXEL_MODE_MONO: + case FT_PIXEL_MODE_GRAY: { + color_size = 2; + } break; + case FT_PIXEL_MODE_BGRA: { + color_size = 4; + } break; + case FT_PIXEL_MODE_LCD: { + color_size = 4; + w /= 3; + } break; + case FT_PIXEL_MODE_LCD_V: { + color_size = 4; + h /= 3; + } break; + } int mw = w + p_rect_margin * 4; int mh = h + p_rect_margin * 4; @@ -1092,15 +1095,13 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma ERR_FAIL_COND_V(mw > 4096, FontGlyph()); ERR_FAIL_COND_V(mh > 4096, FontGlyph()); - int color_size = bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 2; Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8; FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, color_size, require_format, mw, mh, false); ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph()); // Fit character in char texture. - - FontTexture &tex = p_data->textures.write[tex_pos.index]; + ShelfPackTexture &tex = p_data->textures.write[tex_pos.index]; { uint8_t *wr = tex.imgdata.ptrw(); @@ -1127,6 +1128,34 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma wr[ofs + 0] = bitmap.buffer[ofs_color + 2]; wr[ofs + 3] = bitmap.buffer[ofs_color + 3]; } break; + case FT_PIXEL_MODE_LCD: { + int ofs_color = i * bitmap.pitch + (j * 3); + if (p_bgra) { + wr[ofs + 0] = bitmap.buffer[ofs_color + 0]; + wr[ofs + 1] = bitmap.buffer[ofs_color + 1]; + wr[ofs + 2] = bitmap.buffer[ofs_color + 2]; + wr[ofs + 3] = 255; + } else { + wr[ofs + 0] = bitmap.buffer[ofs_color + 2]; + wr[ofs + 1] = bitmap.buffer[ofs_color + 1]; + wr[ofs + 2] = bitmap.buffer[ofs_color + 0]; + wr[ofs + 3] = 255; + } + } break; + case FT_PIXEL_MODE_LCD_V: { + int ofs_color = i * bitmap.pitch * 3 + j; + if (p_bgra) { + wr[ofs + 0] = bitmap.buffer[ofs_color + bitmap.pitch * 2]; + wr[ofs + 1] = bitmap.buffer[ofs_color + bitmap.pitch]; + wr[ofs + 2] = bitmap.buffer[ofs_color + 0]; + wr[ofs + 3] = 255; + } else { + wr[ofs + 0] = bitmap.buffer[ofs_color + 0]; + wr[ofs + 1] = bitmap.buffer[ofs_color + bitmap.pitch]; + wr[ofs + 2] = bitmap.buffer[ofs_color + bitmap.pitch * 2]; + wr[ofs + 3] = 255; + } + } break; default: ERR_FAIL_V_MSG(FontGlyph(), "Font uses unsupported pixel format: " + String::num_int64(bitmap.pixel_mode) + "."); break; @@ -1137,12 +1166,6 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma tex.dirty = true; - // Update height array. - int32_t *offw = tex.offsets.ptrw(); - for (int k = tex_pos.x; k < tex_pos.x + mw; k++) { - offw[k] = tex_pos.y + mh; - } - FontGlyph chr; chr.advance = advance * p_data->scale / p_data->oversampling; chr.texture_idx = tex_pos.index; @@ -1230,9 +1253,44 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data, FT_Outline_Transform(&fd->face->glyph->outline, &mat); } + FT_Render_Mode aa_mode = FT_RENDER_MODE_NORMAL; + bool bgra = false; + switch (p_font_data->antialiasing) { + case FONT_ANTIALIASING_NONE: { + aa_mode = FT_RENDER_MODE_MONO; + } break; + case FONT_ANTIALIASING_GRAY: { + aa_mode = FT_RENDER_MODE_NORMAL; + } break; + case FONT_ANTIALIASING_LCD: { + int aa_layout = (int)((p_glyph >> 24) & 7); + switch (aa_layout) { + case FONT_LCD_SUBPIXEL_LAYOUT_HRGB: { + aa_mode = FT_RENDER_MODE_LCD; + bgra = false; + } break; + case FONT_LCD_SUBPIXEL_LAYOUT_HBGR: { + aa_mode = FT_RENDER_MODE_LCD; + bgra = true; + } break; + case FONT_LCD_SUBPIXEL_LAYOUT_VRGB: { + aa_mode = FT_RENDER_MODE_LCD_V; + bgra = false; + } break; + case FONT_LCD_SUBPIXEL_LAYOUT_VBGR: { + aa_mode = FT_RENDER_MODE_LCD_V; + bgra = true; + } break; + default: { + aa_mode = FT_RENDER_MODE_NORMAL; + } break; + } + } break; + } + if (!outline) { if (!p_font_data->msdf) { - error = FT_Render_Glyph(fd->face->glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO); + error = FT_Render_Glyph(fd->face->glyph, aa_mode); } FT_GlyphSlot slot = fd->face->glyph; if (!error) { @@ -1244,7 +1302,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data, ERR_FAIL_V_MSG(false, "Compiled without MSDFGEN support!"); #endif } else { - gl = rasterize_bitmap(fd, rect_range, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0); + gl = rasterize_bitmap(fd, rect_range, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, bgra); } } } else { @@ -1264,11 +1322,11 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data, if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) { goto cleanup_glyph; } - if (FT_Glyph_To_Bitmap(&glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1) != 0) { + if (FT_Glyph_To_Bitmap(&glyph, aa_mode, nullptr, 1) != 0) { goto cleanup_glyph; } glyph_bitmap = (FT_BitmapGlyph)glyph; - gl = rasterize_bitmap(fd, rect_range, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2()); + gl = rasterize_bitmap(fd, rect_range, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2(), bgra); cleanup_glyph: FT_Done_Glyph(glyph); @@ -1297,7 +1355,13 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f int error = 0; if (!ft_library) { error = FT_Init_FreeType(&ft_library); - ERR_FAIL_COND_V_MSG(error != 0, false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'."); + if (error != 0) { + memdelete(fd); + ERR_FAIL_V_MSG(false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'."); + } +#ifdef MODULE_SVG_ENABLED + FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks()); +#endif } memset(&fd->stream, 0, sizeof(FT_StreamRec)); @@ -1313,17 +1377,20 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f fargs.stream = &fd->stream; int max_index = 0; - FT_Face tmp_face; + FT_Face tmp_face = nullptr; error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face); - if (error == 0) { + if (tmp_face && error == 0) { max_index = tmp_face->num_faces - 1; } - FT_Done_Face(tmp_face); + if (tmp_face) { + FT_Done_Face(tmp_face); + } error = FT_Open_Face(ft_library, &fargs, CLAMP(p_font_data->face_index, 0, max_index), &fd->face); if (error) { FT_Done_Face(fd->face); fd->face = nullptr; + memdelete(fd); ERR_FAIL_V_MSG(false, "FreeType: Error loading font: '" + String(FT_Error_String(error)) + "'."); } @@ -1331,7 +1398,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f fd->oversampling = 1.0; fd->size.x = p_font_data->msdf_source_size; } else if (p_font_data->oversampling <= 0.0) { - fd->oversampling = font_get_global_oversampling(); + fd->oversampling = _font_get_global_oversampling(); } else { fd->oversampling = p_font_data->oversampling; } @@ -1377,11 +1444,13 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f if (fd->face->style_name != nullptr) { p_font_data->style_name = String::utf8((const char *)fd->face->style_name); } + p_font_data->weight = _font_get_weight_by_name(p_font_data->style_name.to_lower()); + p_font_data->stretch = _font_get_stretch_by_name(p_font_data->style_name.to_lower()); p_font_data->style_flags = 0; - if (fd->face->style_flags & FT_STYLE_FLAG_BOLD) { + if ((fd->face->style_flags & FT_STYLE_FLAG_BOLD) || p_font_data->weight >= 700) { p_font_data->style_flags.set_flag(FONT_BOLD); } - if (fd->face->style_flags & FT_STYLE_FLAG_ITALIC) { + if ((fd->face->style_flags & FT_STYLE_FLAG_ITALIC) || _is_ital_style(p_font_data->style_name.to_lower())) { p_font_data->style_flags.set_flag(FONT_ITALIC); } if (fd->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) { @@ -1724,8 +1793,8 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f coords.write[i] = CLAMP(var.value * 65536.0, amaster->axis[i].minimum, amaster->axis[i].maximum); } - if (p_font_data->variation_coordinates.has(tag_to_name(var.tag))) { - var.value = p_font_data->variation_coordinates[tag_to_name(var.tag)]; + if (p_font_data->variation_coordinates.has(_tag_to_name(var.tag))) { + var.value = p_font_data->variation_coordinates[_tag_to_name(var.tag)]; coords.write[i] = CLAMP(var.value * 65536.0, amaster->axis[i].minimum, amaster->axis[i].maximum); } @@ -1737,6 +1806,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f FT_Done_MM_Var(ft_library, amaster); } #else + memdelete(fd); ERR_FAIL_V_MSG(false, "FreeType: Can't load dynamic font, engine is compiled without FreeType support!"); #endif } else { @@ -1770,7 +1840,7 @@ hb_font_t *TextServerAdvanced::_font_get_hb_handle(const RID &p_font_rid, int64_ return fd->cache[size]->hb_handle; } -RID TextServerAdvanced::create_font() { +RID TextServerAdvanced::_create_font() { _THREAD_SAFE_METHOD_ FontAdvanced *fd = memnew(FontAdvanced); @@ -1778,7 +1848,7 @@ RID TextServerAdvanced::create_font() { return font_owner.make_rid(fd); } -void TextServerAdvanced::font_set_data(const RID &p_font_rid, const PackedByteArray &p_data) { +void TextServerAdvanced::_font_set_data(const RID &p_font_rid, const PackedByteArray &p_data) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -1789,7 +1859,7 @@ void TextServerAdvanced::font_set_data(const RID &p_font_rid, const PackedByteAr fd->data_size = fd->data.size(); } -void TextServerAdvanced::font_set_data_ptr(const RID &p_font_rid, const uint8_t *p_data_ptr, int64_t p_data_size) { +void TextServerAdvanced::_font_set_data_ptr(const RID &p_font_rid, const uint8_t *p_data_ptr, int64_t p_data_size) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -1800,7 +1870,7 @@ void TextServerAdvanced::font_set_data_ptr(const RID &p_font_rid, const uint8_t fd->data_size = p_data_size; } -void TextServerAdvanced::font_set_face_index(const RID &p_font_rid, int64_t p_face_index) { +void TextServerAdvanced::_font_set_face_index(const RID &p_font_rid, int64_t p_face_index) { ERR_FAIL_COND(p_face_index < 0); ERR_FAIL_COND(p_face_index >= 0x7FFF); @@ -1814,7 +1884,7 @@ void TextServerAdvanced::font_set_face_index(const RID &p_font_rid, int64_t p_fa } } -int64_t TextServerAdvanced::font_get_face_index(const RID &p_font_rid) const { +int64_t TextServerAdvanced::_font_get_face_index(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, 0); @@ -1822,7 +1892,7 @@ int64_t TextServerAdvanced::font_get_face_index(const RID &p_font_rid) const { return fd->face_index; } -int64_t TextServerAdvanced::font_get_face_count(const RID &p_font_rid) const { +int64_t TextServerAdvanced::_font_get_face_count(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, 0); @@ -1836,6 +1906,9 @@ int64_t TextServerAdvanced::font_get_face_count(const RID &p_font_rid) const { if (!ft_library) { error = FT_Init_FreeType(&ft_library); ERR_FAIL_COND_V_MSG(error != 0, false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'."); +#ifdef MODULE_SVG_ENABLED + FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks()); +#endif } FT_StreamRec stream; @@ -1851,19 +1924,19 @@ int64_t TextServerAdvanced::font_get_face_count(const RID &p_font_rid) const { fargs.flags = FT_OPEN_MEMORY; fargs.stream = &stream; - FT_Face tmp_face; + FT_Face tmp_face = nullptr; error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face); if (error == 0) { face_count = tmp_face->num_faces; + FT_Done_Face(tmp_face); } - FT_Done_Face(tmp_face); #endif } return face_count; } -void TextServerAdvanced::font_set_style(const RID &p_font_rid, BitField<FontStyle> p_style) { +void TextServerAdvanced::_font_set_style(const RID &p_font_rid, BitField<FontStyle> p_style) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -1873,7 +1946,7 @@ void TextServerAdvanced::font_set_style(const RID &p_font_rid, BitField<FontStyl fd->style_flags = p_style; } -BitField<TextServer::FontStyle> TextServerAdvanced::font_get_style(const RID &p_font_rid) const { +BitField<TextServer::FontStyle> TextServerAdvanced::_font_get_style(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, 0); @@ -1883,7 +1956,7 @@ BitField<TextServer::FontStyle> TextServerAdvanced::font_get_style(const RID &p_ return fd->style_flags; } -void TextServerAdvanced::font_set_style_name(const RID &p_font_rid, const String &p_name) { +void TextServerAdvanced::_font_set_style_name(const RID &p_font_rid, const String &p_name) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -1893,7 +1966,7 @@ void TextServerAdvanced::font_set_style_name(const RID &p_font_rid, const String fd->style_name = p_name; } -String TextServerAdvanced::font_get_style_name(const RID &p_font_rid) const { +String TextServerAdvanced::_font_get_style_name(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, String()); @@ -1903,7 +1976,47 @@ String TextServerAdvanced::font_get_style_name(const RID &p_font_rid) const { return fd->style_name; } -void TextServerAdvanced::font_set_name(const RID &p_font_rid, const String &p_name) { +void TextServerAdvanced::_font_set_weight(const RID &p_font_rid, int64_t p_weight) { + FontAdvanced *fd = font_owner.get_or_null(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, 16); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->weight = CLAMP(p_weight, 100, 999); +} + +int64_t TextServerAdvanced::_font_get_weight(const RID &p_font_rid) const { + FontAdvanced *fd = font_owner.get_or_null(p_font_rid); + ERR_FAIL_COND_V(!fd, 400); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, 16); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 400); + return fd->weight; +} + +void TextServerAdvanced::_font_set_stretch(const RID &p_font_rid, int64_t p_stretch) { + FontAdvanced *fd = font_owner.get_or_null(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, 16); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->stretch = CLAMP(p_stretch, 50, 200); +} + +int64_t TextServerAdvanced::_font_get_stretch(const RID &p_font_rid) const { + FontAdvanced *fd = font_owner.get_or_null(p_font_rid); + ERR_FAIL_COND_V(!fd, 100); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, 16); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 100); + return fd->stretch; +} + +void TextServerAdvanced::_font_set_name(const RID &p_font_rid, const String &p_name) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -1913,7 +2026,7 @@ void TextServerAdvanced::font_set_name(const RID &p_font_rid, const String &p_na fd->font_name = p_name; } -String TextServerAdvanced::font_get_name(const RID &p_font_rid) const { +String TextServerAdvanced::_font_get_name(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, String()); @@ -1923,26 +2036,26 @@ String TextServerAdvanced::font_get_name(const RID &p_font_rid) const { return fd->font_name; } -void TextServerAdvanced::font_set_antialiased(const RID &p_font_rid, bool p_antialiased) { +void TextServerAdvanced::_font_set_antialiasing(const RID &p_font_rid, TextServer::FontAntialiasing p_antialiasing) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); MutexLock lock(fd->mutex); - if (fd->antialiased != p_antialiased) { + if (fd->antialiasing != p_antialiasing) { _font_clear_cache(fd); - fd->antialiased = p_antialiased; + fd->antialiasing = p_antialiasing; } } -bool TextServerAdvanced::font_is_antialiased(const RID &p_font_rid) const { +TextServer::FontAntialiasing TextServerAdvanced::_font_get_antialiasing(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); - ERR_FAIL_COND_V(!fd, false); + ERR_FAIL_COND_V(!fd, TextServer::FONT_ANTIALIASING_NONE); MutexLock lock(fd->mutex); - return fd->antialiased; + return fd->antialiasing; } -void TextServerAdvanced::font_set_generate_mipmaps(const RID &p_font_rid, bool p_generate_mipmaps) { +void TextServerAdvanced::_font_set_generate_mipmaps(const RID &p_font_rid, bool p_generate_mipmaps) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -1951,13 +2064,14 @@ void TextServerAdvanced::font_set_generate_mipmaps(const RID &p_font_rid, bool p for (KeyValue<Vector2i, FontForSizeAdvanced *> &E : fd->cache) { for (int i = 0; i < E.value->textures.size(); i++) { E.value->textures.write[i].dirty = true; + E.value->textures.write[i].texture = Ref<ImageTexture>(); } } fd->mipmaps = p_generate_mipmaps; } } -bool TextServerAdvanced::font_get_generate_mipmaps(const RID &p_font_rid) const { +bool TextServerAdvanced::_font_get_generate_mipmaps(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, false); @@ -1965,7 +2079,7 @@ bool TextServerAdvanced::font_get_generate_mipmaps(const RID &p_font_rid) const return fd->mipmaps; } -void TextServerAdvanced::font_set_multichannel_signed_distance_field(const RID &p_font_rid, bool p_msdf) { +void TextServerAdvanced::_font_set_multichannel_signed_distance_field(const RID &p_font_rid, bool p_msdf) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -1976,7 +2090,7 @@ void TextServerAdvanced::font_set_multichannel_signed_distance_field(const RID & } } -bool TextServerAdvanced::font_is_multichannel_signed_distance_field(const RID &p_font_rid) const { +bool TextServerAdvanced::_font_is_multichannel_signed_distance_field(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, false); @@ -1984,7 +2098,7 @@ bool TextServerAdvanced::font_is_multichannel_signed_distance_field(const RID &p return fd->msdf; } -void TextServerAdvanced::font_set_msdf_pixel_range(const RID &p_font_rid, int64_t p_msdf_pixel_range) { +void TextServerAdvanced::_font_set_msdf_pixel_range(const RID &p_font_rid, int64_t p_msdf_pixel_range) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -1995,7 +2109,7 @@ void TextServerAdvanced::font_set_msdf_pixel_range(const RID &p_font_rid, int64_ } } -int64_t TextServerAdvanced::font_get_msdf_pixel_range(const RID &p_font_rid) const { +int64_t TextServerAdvanced::_font_get_msdf_pixel_range(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, false); @@ -2003,7 +2117,7 @@ int64_t TextServerAdvanced::font_get_msdf_pixel_range(const RID &p_font_rid) con return fd->msdf_range; } -void TextServerAdvanced::font_set_msdf_size(const RID &p_font_rid, int64_t p_msdf_size) { +void TextServerAdvanced::_font_set_msdf_size(const RID &p_font_rid, int64_t p_msdf_size) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2014,7 +2128,7 @@ void TextServerAdvanced::font_set_msdf_size(const RID &p_font_rid, int64_t p_msd } } -int64_t TextServerAdvanced::font_get_msdf_size(const RID &p_font_rid) const { +int64_t TextServerAdvanced::_font_get_msdf_size(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, false); @@ -2022,7 +2136,7 @@ int64_t TextServerAdvanced::font_get_msdf_size(const RID &p_font_rid) const { return fd->msdf_source_size; } -void TextServerAdvanced::font_set_fixed_size(const RID &p_font_rid, int64_t p_fixed_size) { +void TextServerAdvanced::_font_set_fixed_size(const RID &p_font_rid, int64_t p_fixed_size) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2030,7 +2144,7 @@ void TextServerAdvanced::font_set_fixed_size(const RID &p_font_rid, int64_t p_fi fd->fixed_size = p_fixed_size; } -int64_t TextServerAdvanced::font_get_fixed_size(const RID &p_font_rid) const { +int64_t TextServerAdvanced::_font_get_fixed_size(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, false); @@ -2038,7 +2152,23 @@ int64_t TextServerAdvanced::font_get_fixed_size(const RID &p_font_rid) const { return fd->fixed_size; } -void TextServerAdvanced::font_set_force_autohinter(const RID &p_font_rid, bool p_force_autohinter) { +void TextServerAdvanced::_font_set_allow_system_fallback(const RID &p_font_rid, bool p_allow_system_fallback) { + FontAdvanced *fd = font_owner.get_or_null(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + fd->allow_system_fallback = p_allow_system_fallback; +} + +bool TextServerAdvanced::_font_is_allow_system_fallback(const RID &p_font_rid) const { + FontAdvanced *fd = font_owner.get_or_null(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->allow_system_fallback; +} + +void TextServerAdvanced::_font_set_force_autohinter(const RID &p_font_rid, bool p_force_autohinter) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2049,7 +2179,7 @@ void TextServerAdvanced::font_set_force_autohinter(const RID &p_font_rid, bool p } } -bool TextServerAdvanced::font_is_force_autohinter(const RID &p_font_rid) const { +bool TextServerAdvanced::_font_is_force_autohinter(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, false); @@ -2057,7 +2187,7 @@ bool TextServerAdvanced::font_is_force_autohinter(const RID &p_font_rid) const { return fd->force_autohinter; } -void TextServerAdvanced::font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) { +void TextServerAdvanced::_font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2068,7 +2198,7 @@ void TextServerAdvanced::font_set_hinting(const RID &p_font_rid, TextServer::Hin } } -TextServer::Hinting TextServerAdvanced::font_get_hinting(const RID &p_font_rid) const { +TextServer::Hinting TextServerAdvanced::_font_get_hinting(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, HINTING_NONE); @@ -2076,7 +2206,7 @@ TextServer::Hinting TextServerAdvanced::font_get_hinting(const RID &p_font_rid) return fd->hinting; } -void TextServerAdvanced::font_set_subpixel_positioning(const RID &p_font_rid, TextServer::SubpixelPositioning p_subpixel) { +void TextServerAdvanced::_font_set_subpixel_positioning(const RID &p_font_rid, TextServer::SubpixelPositioning p_subpixel) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2084,7 +2214,7 @@ void TextServerAdvanced::font_set_subpixel_positioning(const RID &p_font_rid, Te fd->subpixel_positioning = p_subpixel; } -TextServer::SubpixelPositioning TextServerAdvanced::font_get_subpixel_positioning(const RID &p_font_rid) const { +TextServer::SubpixelPositioning TextServerAdvanced::_font_get_subpixel_positioning(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, SUBPIXEL_POSITIONING_DISABLED); @@ -2092,7 +2222,7 @@ TextServer::SubpixelPositioning TextServerAdvanced::font_get_subpixel_positionin return fd->subpixel_positioning; } -void TextServerAdvanced::font_set_embolden(const RID &p_font_rid, double p_strength) { +void TextServerAdvanced::_font_set_embolden(const RID &p_font_rid, double p_strength) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2103,7 +2233,7 @@ void TextServerAdvanced::font_set_embolden(const RID &p_font_rid, double p_stren } } -double TextServerAdvanced::font_get_embolden(const RID &p_font_rid) const { +double TextServerAdvanced::_font_get_embolden(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, 0.0); @@ -2111,7 +2241,7 @@ double TextServerAdvanced::font_get_embolden(const RID &p_font_rid) const { return fd->embolden; } -void TextServerAdvanced::font_set_transform(const RID &p_font_rid, const Transform2D &p_transform) { +void TextServerAdvanced::_font_set_transform(const RID &p_font_rid, const Transform2D &p_transform) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2122,7 +2252,7 @@ void TextServerAdvanced::font_set_transform(const RID &p_font_rid, const Transfo } } -Transform2D TextServerAdvanced::font_get_transform(const RID &p_font_rid) const { +Transform2D TextServerAdvanced::_font_get_transform(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, Transform2D()); @@ -2130,7 +2260,7 @@ Transform2D TextServerAdvanced::font_get_transform(const RID &p_font_rid) const return fd->transform; } -void TextServerAdvanced::font_set_variation_coordinates(const RID &p_font_rid, const Dictionary &p_variation_coordinates) { +void TextServerAdvanced::_font_set_variation_coordinates(const RID &p_font_rid, const Dictionary &p_variation_coordinates) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2141,7 +2271,7 @@ void TextServerAdvanced::font_set_variation_coordinates(const RID &p_font_rid, c } } -Dictionary TextServerAdvanced::font_get_variation_coordinates(const RID &p_font_rid) const { +Dictionary TextServerAdvanced::_font_get_variation_coordinates(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, Dictionary()); @@ -2149,7 +2279,7 @@ Dictionary TextServerAdvanced::font_get_variation_coordinates(const RID &p_font_ return fd->variation_coordinates; } -void TextServerAdvanced::font_set_oversampling(const RID &p_font_rid, double p_oversampling) { +void TextServerAdvanced::_font_set_oversampling(const RID &p_font_rid, double p_oversampling) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2160,7 +2290,7 @@ void TextServerAdvanced::font_set_oversampling(const RID &p_font_rid, double p_o } } -double TextServerAdvanced::font_get_oversampling(const RID &p_font_rid) const { +double TextServerAdvanced::_font_get_oversampling(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, 0.0); @@ -2168,19 +2298,19 @@ double TextServerAdvanced::font_get_oversampling(const RID &p_font_rid) const { return fd->oversampling; } -Array TextServerAdvanced::font_get_size_cache_list(const RID &p_font_rid) const { +TypedArray<Vector2i> TextServerAdvanced::_font_get_size_cache_list(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); - ERR_FAIL_COND_V(!fd, Array()); + ERR_FAIL_COND_V(!fd, TypedArray<Vector2i>()); MutexLock lock(fd->mutex); - Array ret; + TypedArray<Vector2i> ret; for (const KeyValue<Vector2i, FontForSizeAdvanced *> &E : fd->cache) { ret.push_back(E.key); } return ret; } -void TextServerAdvanced::font_clear_size_cache(const RID &p_font_rid) { +void TextServerAdvanced::_font_clear_size_cache(const RID &p_font_rid) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2191,7 +2321,7 @@ void TextServerAdvanced::font_clear_size_cache(const RID &p_font_rid) { fd->cache.clear(); } -void TextServerAdvanced::font_remove_size_cache(const RID &p_font_rid, const Vector2i &p_size) { +void TextServerAdvanced::_font_remove_size_cache(const RID &p_font_rid, const Vector2i &p_size) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2202,7 +2332,7 @@ void TextServerAdvanced::font_remove_size_cache(const RID &p_font_rid, const Vec } } -void TextServerAdvanced::font_set_ascent(const RID &p_font_rid, int64_t p_size, double p_ascent) { +void TextServerAdvanced::_font_set_ascent(const RID &p_font_rid, int64_t p_size, double p_ascent) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2213,7 +2343,7 @@ void TextServerAdvanced::font_set_ascent(const RID &p_font_rid, int64_t p_size, fd->cache[size]->ascent = p_ascent; } -double TextServerAdvanced::font_get_ascent(const RID &p_font_rid, int64_t p_size) const { +double TextServerAdvanced::_font_get_ascent(const RID &p_font_rid, int64_t p_size) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, 0.0); @@ -2229,7 +2359,7 @@ double TextServerAdvanced::font_get_ascent(const RID &p_font_rid, int64_t p_size } } -void TextServerAdvanced::font_set_descent(const RID &p_font_rid, int64_t p_size, double p_descent) { +void TextServerAdvanced::_font_set_descent(const RID &p_font_rid, int64_t p_size, double p_descent) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2239,7 +2369,7 @@ void TextServerAdvanced::font_set_descent(const RID &p_font_rid, int64_t p_size, fd->cache[size]->descent = p_descent; } -double TextServerAdvanced::font_get_descent(const RID &p_font_rid, int64_t p_size) const { +double TextServerAdvanced::_font_get_descent(const RID &p_font_rid, int64_t p_size) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, 0.0); @@ -2255,7 +2385,7 @@ double TextServerAdvanced::font_get_descent(const RID &p_font_rid, int64_t p_siz } } -void TextServerAdvanced::font_set_underline_position(const RID &p_font_rid, int64_t p_size, double p_underline_position) { +void TextServerAdvanced::_font_set_underline_position(const RID &p_font_rid, int64_t p_size, double p_underline_position) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2266,7 +2396,7 @@ void TextServerAdvanced::font_set_underline_position(const RID &p_font_rid, int6 fd->cache[size]->underline_position = p_underline_position; } -double TextServerAdvanced::font_get_underline_position(const RID &p_font_rid, int64_t p_size) const { +double TextServerAdvanced::_font_get_underline_position(const RID &p_font_rid, int64_t p_size) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, 0.0); @@ -2282,7 +2412,7 @@ double TextServerAdvanced::font_get_underline_position(const RID &p_font_rid, in } } -void TextServerAdvanced::font_set_underline_thickness(const RID &p_font_rid, int64_t p_size, double p_underline_thickness) { +void TextServerAdvanced::_font_set_underline_thickness(const RID &p_font_rid, int64_t p_size, double p_underline_thickness) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2293,7 +2423,7 @@ void TextServerAdvanced::font_set_underline_thickness(const RID &p_font_rid, int fd->cache[size]->underline_thickness = p_underline_thickness; } -double TextServerAdvanced::font_get_underline_thickness(const RID &p_font_rid, int64_t p_size) const { +double TextServerAdvanced::_font_get_underline_thickness(const RID &p_font_rid, int64_t p_size) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, 0.0); @@ -2309,7 +2439,7 @@ double TextServerAdvanced::font_get_underline_thickness(const RID &p_font_rid, i } } -void TextServerAdvanced::font_set_scale(const RID &p_font_rid, int64_t p_size, double p_scale) { +void TextServerAdvanced::_font_set_scale(const RID &p_font_rid, int64_t p_size, double p_scale) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2325,7 +2455,7 @@ void TextServerAdvanced::font_set_scale(const RID &p_font_rid, int64_t p_size, d fd->cache[size]->scale = p_scale; } -double TextServerAdvanced::font_get_scale(const RID &p_font_rid, int64_t p_size) const { +double TextServerAdvanced::_font_get_scale(const RID &p_font_rid, int64_t p_size) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, 0.0); @@ -2341,7 +2471,7 @@ double TextServerAdvanced::font_get_scale(const RID &p_font_rid, int64_t p_size) } } -int64_t TextServerAdvanced::font_get_texture_count(const RID &p_font_rid, const Vector2i &p_size) const { +int64_t TextServerAdvanced::_font_get_texture_count(const RID &p_font_rid, const Vector2i &p_size) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, 0); @@ -2353,7 +2483,7 @@ int64_t TextServerAdvanced::font_get_texture_count(const RID &p_font_rid, const return fd->cache[size]->textures.size(); } -void TextServerAdvanced::font_clear_textures(const RID &p_font_rid, const Vector2i &p_size) { +void TextServerAdvanced::_font_clear_textures(const RID &p_font_rid, const Vector2i &p_size) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); MutexLock lock(fd->mutex); @@ -2363,7 +2493,7 @@ void TextServerAdvanced::font_clear_textures(const RID &p_font_rid, const Vector fd->cache[size]->textures.clear(); } -void TextServerAdvanced::font_remove_texture(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) { +void TextServerAdvanced::_font_remove_texture(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2375,7 +2505,7 @@ void TextServerAdvanced::font_remove_texture(const RID &p_font_rid, const Vector fd->cache[size]->textures.remove_at(p_texture_index); } -void TextServerAdvanced::font_set_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const Ref<Image> &p_image) { +void TextServerAdvanced::_font_set_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const Ref<Image> &p_image) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); ERR_FAIL_COND(p_image.is_null()); @@ -2388,16 +2518,14 @@ void TextServerAdvanced::font_set_texture_image(const RID &p_font_rid, const Vec fd->cache[size]->textures.resize(p_texture_index + 1); } - FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + ShelfPackTexture &tex = fd->cache[size]->textures.write[p_texture_index]; tex.imgdata = p_image->get_data(); tex.texture_w = p_image->get_width(); tex.texture_h = p_image->get_height(); tex.format = p_image->get_format(); - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } @@ -2406,7 +2534,7 @@ void TextServerAdvanced::font_set_texture_image(const RID &p_font_rid, const Vec tex.dirty = false; } -Ref<Image> TextServerAdvanced::font_get_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const { +Ref<Image> TextServerAdvanced::_font_get_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, Ref<Image>()); @@ -2415,15 +2543,12 @@ Ref<Image> TextServerAdvanced::font_get_texture_image(const RID &p_font_rid, con ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Ref<Image>()); ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), Ref<Image>()); - const FontTexture &tex = fd->cache[size]->textures[p_texture_index]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); - - return img; + const ShelfPackTexture &tex = fd->cache[size]->textures[p_texture_index]; + return Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); } -void TextServerAdvanced::font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offset) { +void TextServerAdvanced::_font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offsets) { + ERR_FAIL_COND(p_offsets.size() % 4 != 0); FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2435,11 +2560,14 @@ void TextServerAdvanced::font_set_texture_offsets(const RID &p_font_rid, const V fd->cache[size]->textures.resize(p_texture_index + 1); } - FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; - tex.offsets = p_offset; + ShelfPackTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + tex.shelves.clear(); + for (int32_t i = 0; i < p_offsets.size(); i += 4) { + tex.shelves.push_back(Shelf(p_offsets[i], p_offsets[i + 1], p_offsets[i + 2], p_offsets[i + 3])); + } } -PackedInt32Array TextServerAdvanced::font_get_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const { +PackedInt32Array TextServerAdvanced::_font_get_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, PackedInt32Array()); @@ -2448,19 +2576,31 @@ PackedInt32Array TextServerAdvanced::font_get_texture_offsets(const RID &p_font_ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), PackedInt32Array()); ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), PackedInt32Array()); - const FontTexture &tex = fd->cache[size]->textures[p_texture_index]; - return tex.offsets; + const ShelfPackTexture &tex = fd->cache[size]->textures[p_texture_index]; + PackedInt32Array ret; + ret.resize(tex.shelves.size() * 4); + + int32_t *wr = ret.ptrw(); + int32_t i = 0; + for (const Shelf &E : tex.shelves) { + wr[i * 4] = E.x; + wr[i * 4 + 1] = E.y; + wr[i * 4 + 2] = E.w; + wr[i * 4 + 3] = E.h; + i++; + } + return ret; } -Array TextServerAdvanced::font_get_glyph_list(const RID &p_font_rid, const Vector2i &p_size) const { +PackedInt32Array TextServerAdvanced::_font_get_glyph_list(const RID &p_font_rid, const Vector2i &p_size) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); - ERR_FAIL_COND_V(!fd, Array()); + ERR_FAIL_COND_V(!fd, PackedInt32Array()); MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array()); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), PackedInt32Array()); - Array ret; + PackedInt32Array ret; const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; for (const KeyValue<int32_t, FontGlyph> &E : gl) { ret.push_back(E.key); @@ -2468,7 +2608,7 @@ Array TextServerAdvanced::font_get_glyph_list(const RID &p_font_rid, const Vecto return ret; } -void TextServerAdvanced::font_clear_glyphs(const RID &p_font_rid, const Vector2i &p_size) { +void TextServerAdvanced::_font_clear_glyphs(const RID &p_font_rid, const Vector2i &p_size) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2479,7 +2619,7 @@ void TextServerAdvanced::font_clear_glyphs(const RID &p_font_rid, const Vector2i fd->cache[size]->glyph_map.clear(); } -void TextServerAdvanced::font_remove_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) { +void TextServerAdvanced::_font_remove_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2504,7 +2644,7 @@ double TextServerAdvanced::_get_extra_advance(RID p_font_rid, int p_font_size) c } } -Vector2 TextServerAdvanced::font_get_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph) const { +Vector2 TextServerAdvanced::_font_get_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, Vector2()); @@ -2512,7 +2652,16 @@ Vector2 TextServerAdvanced::font_get_glyph_advance(const RID &p_font_rid, int64_ Vector2i size = _get_size(fd, p_size); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); - if (!_ensure_glyph(fd, size, p_glyph)) { + + int mod = 0; + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + mod = (layout << 24); + } + } + + if (!_ensure_glyph(fd, size, p_glyph | mod)) { return Vector2(); // Invalid or non graphicl glyph, do not display errors. } @@ -2523,16 +2672,17 @@ Vector2 TextServerAdvanced::font_get_glyph_advance(const RID &p_font_rid, int64_ ea.x = fd->embolden * double(size.x) / 64.0; } + double scale = _font_get_scale(p_font_rid, p_size); if (fd->msdf) { - return (gl[p_glyph].advance + ea) * (double)p_size / (double)fd->msdf_source_size; - } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_DISABLED) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x > SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { - return (gl[p_glyph].advance + ea).round(); + return (gl[p_glyph | mod].advance + ea) * (double)p_size / (double)fd->msdf_source_size; + } 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))) { + return (gl[p_glyph | mod].advance + ea).round(); } else { - return gl[p_glyph].advance + ea; + return gl[p_glyph | mod].advance + ea; } } -void TextServerAdvanced::font_set_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph, const Vector2 &p_advance) { +void TextServerAdvanced::_font_set_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph, const Vector2 &p_advance) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2547,7 +2697,7 @@ void TextServerAdvanced::font_set_glyph_advance(const RID &p_font_rid, int64_t p gl[p_glyph].found = true; } -Vector2 TextServerAdvanced::font_get_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const { +Vector2 TextServerAdvanced::_font_get_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, Vector2()); @@ -2555,20 +2705,29 @@ Vector2 TextServerAdvanced::font_get_glyph_offset(const RID &p_font_rid, const V Vector2i size = _get_size_outline(fd, p_size); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); - if (!_ensure_glyph(fd, size, p_glyph)) { + + int mod = 0; + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + mod = (layout << 24); + } + } + + if (!_ensure_glyph(fd, size, p_glyph | mod)) { return Vector2(); // Invalid or non graphicl glyph, do not display errors. } const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; if (fd->msdf) { - return gl[p_glyph].rect.position * (double)p_size.x / (double)fd->msdf_source_size; + return gl[p_glyph | mod].rect.position * (double)p_size.x / (double)fd->msdf_source_size; } else { - return gl[p_glyph].rect.position; + return gl[p_glyph | mod].rect.position; } } -void TextServerAdvanced::font_set_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_offset) { +void TextServerAdvanced::_font_set_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_offset) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2583,7 +2742,7 @@ void TextServerAdvanced::font_set_glyph_offset(const RID &p_font_rid, const Vect gl[p_glyph].found = true; } -Vector2 TextServerAdvanced::font_get_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const { +Vector2 TextServerAdvanced::_font_get_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, Vector2()); @@ -2591,20 +2750,29 @@ Vector2 TextServerAdvanced::font_get_glyph_size(const RID &p_font_rid, const Vec Vector2i size = _get_size_outline(fd, p_size); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); - if (!_ensure_glyph(fd, size, p_glyph)) { + + int mod = 0; + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + mod = (layout << 24); + } + } + + if (!_ensure_glyph(fd, size, p_glyph | mod)) { return Vector2(); // Invalid or non graphicl glyph, do not display errors. } const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; if (fd->msdf) { - return gl[p_glyph].rect.size * (double)p_size.x / (double)fd->msdf_source_size; + return gl[p_glyph | mod].rect.size * (double)p_size.x / (double)fd->msdf_source_size; } else { - return gl[p_glyph].rect.size; + return gl[p_glyph | mod].rect.size; } } -void TextServerAdvanced::font_set_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_gl_size) { +void TextServerAdvanced::_font_set_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_gl_size) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2619,7 +2787,7 @@ void TextServerAdvanced::font_set_glyph_size(const RID &p_font_rid, const Vector gl[p_glyph].found = true; } -Rect2 TextServerAdvanced::font_get_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const { +Rect2 TextServerAdvanced::_font_get_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, Rect2()); @@ -2627,15 +2795,24 @@ Rect2 TextServerAdvanced::font_get_glyph_uv_rect(const RID &p_font_rid, const Ve Vector2i size = _get_size_outline(fd, p_size); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Rect2()); - if (!_ensure_glyph(fd, size, p_glyph)) { + + int mod = 0; + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + mod = (layout << 24); + } + } + + if (!_ensure_glyph(fd, size, p_glyph | mod)) { return Rect2(); // Invalid or non graphicl glyph, do not display errors. } const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - return gl[p_glyph].uv_rect; + return gl[p_glyph | mod].uv_rect; } -void TextServerAdvanced::font_set_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Rect2 &p_uv_rect) { +void TextServerAdvanced::_font_set_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Rect2 &p_uv_rect) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2650,7 +2827,7 @@ void TextServerAdvanced::font_set_glyph_uv_rect(const RID &p_font_rid, const Vec gl[p_glyph].found = true; } -int64_t TextServerAdvanced::font_get_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const { +int64_t TextServerAdvanced::_font_get_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, -1); @@ -2658,15 +2835,24 @@ int64_t TextServerAdvanced::font_get_glyph_texture_idx(const RID &p_font_rid, co Vector2i size = _get_size_outline(fd, p_size); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), -1); - if (!_ensure_glyph(fd, size, p_glyph)) { + + int mod = 0; + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + mod = (layout << 24); + } + } + + if (!_ensure_glyph(fd, size, p_glyph | mod)) { return -1; // Invalid or non graphicl glyph, do not display errors. } const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - return gl[p_glyph].texture_idx; + return gl[p_glyph | mod].texture_idx; } -void TextServerAdvanced::font_set_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, int64_t p_texture_idx) { +void TextServerAdvanced::_font_set_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, int64_t p_texture_idx) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2681,7 +2867,7 @@ void TextServerAdvanced::font_set_glyph_texture_idx(const RID &p_font_rid, const gl[p_glyph].found = true; } -RID TextServerAdvanced::font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const { +RID TextServerAdvanced::_font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, RID()); @@ -2689,20 +2875,27 @@ RID TextServerAdvanced::font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i size = _get_size_outline(fd, p_size); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), RID()); - if (!_ensure_glyph(fd, size, p_glyph)) { + + int mod = 0; + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + mod = (layout << 24); + } + } + + if (!_ensure_glyph(fd, size, p_glyph | mod)) { return RID(); // Invalid or non graphicl glyph, do not display errors. } const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - ERR_FAIL_COND_V(gl[p_glyph].texture_idx < -1 || gl[p_glyph].texture_idx >= fd->cache[size]->textures.size(), RID()); + ERR_FAIL_COND_V(gl[p_glyph | mod].texture_idx < -1 || gl[p_glyph | mod].texture_idx >= fd->cache[size]->textures.size(), RID()); if (RenderingServer::get_singleton() != nullptr) { - if (gl[p_glyph].texture_idx != -1) { - if (fd->cache[size]->textures[gl[p_glyph].texture_idx].dirty) { - FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph].texture_idx]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + if (gl[p_glyph | mod].texture_idx != -1) { + if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { + ShelfPackTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } @@ -2713,14 +2906,14 @@ RID TextServerAdvanced::font_get_glyph_texture_rid(const RID &p_font_rid, const } tex.dirty = false; } - return fd->cache[size]->textures[gl[p_glyph].texture_idx].texture->get_rid(); + return fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].texture->get_rid(); } } return RID(); } -Size2 TextServerAdvanced::font_get_glyph_texture_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const { +Size2 TextServerAdvanced::_font_get_glyph_texture_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, Size2()); @@ -2728,20 +2921,27 @@ Size2 TextServerAdvanced::font_get_glyph_texture_size(const RID &p_font_rid, con Vector2i size = _get_size_outline(fd, p_size); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Size2()); - if (!_ensure_glyph(fd, size, p_glyph)) { + + int mod = 0; + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + mod = (layout << 24); + } + } + + if (!_ensure_glyph(fd, size, p_glyph | mod)) { return Size2(); // Invalid or non graphicl glyph, do not display errors. } const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - ERR_FAIL_COND_V(gl[p_glyph].texture_idx < -1 || gl[p_glyph].texture_idx >= fd->cache[size]->textures.size(), Size2()); + ERR_FAIL_COND_V(gl[p_glyph | mod].texture_idx < -1 || gl[p_glyph | mod].texture_idx >= fd->cache[size]->textures.size(), Size2()); if (RenderingServer::get_singleton() != nullptr) { - if (gl[p_glyph].texture_idx != -1) { - if (fd->cache[size]->textures[gl[p_glyph].texture_idx].dirty) { - FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph].texture_idx]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + if (gl[p_glyph | mod].texture_idx != -1) { + if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { + ShelfPackTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } @@ -2752,14 +2952,14 @@ Size2 TextServerAdvanced::font_get_glyph_texture_size(const RID &p_font_rid, con } tex.dirty = false; } - return fd->cache[size]->textures[gl[p_glyph].texture_idx].texture->get_size(); + return fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].texture->get_size(); } } return Size2(); } -Dictionary TextServerAdvanced::font_get_glyph_contours(const RID &p_font_rid, int64_t p_size, int64_t p_index) const { +Dictionary TextServerAdvanced::_font_get_glyph_contours(const RID &p_font_rid, int64_t p_size, int64_t p_index) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, Dictionary()); @@ -2809,23 +3009,23 @@ Dictionary TextServerAdvanced::font_get_glyph_contours(const RID &p_font_rid, in #endif } -Array TextServerAdvanced::font_get_kerning_list(const RID &p_font_rid, int64_t p_size) const { +TypedArray<Vector2i> TextServerAdvanced::_font_get_kerning_list(const RID &p_font_rid, int64_t p_size) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); - ERR_FAIL_COND_V(!fd, Array()); + ERR_FAIL_COND_V(!fd, TypedArray<Vector2i>()); MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); - ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array()); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), TypedArray<Vector2i>()); - Array ret; + TypedArray<Vector2i> ret; for (const KeyValue<Vector2i, FontForSizeAdvanced *> &E : fd->cache) { ret.push_back(E.key); } return ret; } -void TextServerAdvanced::font_clear_kerning_map(const RID &p_font_rid, int64_t p_size) { +void TextServerAdvanced::_font_clear_kerning_map(const RID &p_font_rid, int64_t p_size) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2836,7 +3036,7 @@ void TextServerAdvanced::font_clear_kerning_map(const RID &p_font_rid, int64_t p fd->cache[size]->kerning_map.clear(); } -void TextServerAdvanced::font_remove_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) { +void TextServerAdvanced::_font_remove_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2847,7 +3047,7 @@ void TextServerAdvanced::font_remove_kerning(const RID &p_font_rid, int64_t p_si fd->cache[size]->kerning_map.erase(p_glyph_pair); } -void TextServerAdvanced::font_set_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) { +void TextServerAdvanced::_font_set_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2858,7 +3058,7 @@ void TextServerAdvanced::font_set_kerning(const RID &p_font_rid, int64_t p_size, fd->cache[size]->kerning_map[p_glyph_pair] = p_kerning; } -Vector2 TextServerAdvanced::font_get_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) const { +Vector2 TextServerAdvanced::_font_get_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, Vector2()); @@ -2891,7 +3091,7 @@ Vector2 TextServerAdvanced::font_get_kerning(const RID &p_font_rid, int64_t p_si return Vector2(); } -int64_t TextServerAdvanced::font_get_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_char, int64_t p_variation_selector) const { +int64_t TextServerAdvanced::_font_get_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_char, int64_t p_variation_selector) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, 0); ERR_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) + "."); @@ -2916,10 +3116,12 @@ int64_t TextServerAdvanced::font_get_glyph_index(const RID &p_font_rid, int64_t #endif } -bool TextServerAdvanced::font_has_char(const RID &p_font_rid, int64_t p_char) const { +bool TextServerAdvanced::_font_has_char(const RID &p_font_rid, int64_t p_char) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); - ERR_FAIL_COND_V(!fd, false); ERR_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) + "."); + if (!fd) { + return false; + } MutexLock lock(fd->mutex); if (fd->cache.is_empty()) { @@ -2935,7 +3137,7 @@ bool TextServerAdvanced::font_has_char(const RID &p_font_rid, int64_t p_char) co return (at_size) ? at_size->glyph_map.has((int32_t)p_char) : false; } -String TextServerAdvanced::font_get_supported_chars(const RID &p_font_rid) const { +String TextServerAdvanced::_font_get_supported_chars(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, String()); @@ -2968,7 +3170,7 @@ String TextServerAdvanced::font_get_supported_chars(const RID &p_font_rid) const return chars; } -void TextServerAdvanced::font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) { +void TextServerAdvanced::_font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); ERR_FAIL_COND_MSG((p_start >= 0xd800 && p_start <= 0xdfff) || (p_start > 0x10ffff), "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_start, 16) + "."); @@ -2984,16 +3186,18 @@ void TextServerAdvanced::font_render_range(const RID &p_font_rid, const Vector2i if (fd->msdf) { _ensure_glyph(fd, size, (int32_t)idx); } else { - if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { - _ensure_glyph(fd, size, (int32_t)idx | (0 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (1 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (2 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (3 << 27)); - } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { - _ensure_glyph(fd, size, (int32_t)idx | (1 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (0 << 27)); - } else { - _ensure_glyph(fd, size, (int32_t)idx); + for (int aa = 0; aa < ((fd->antialiasing == FONT_ANTIALIASING_LCD) ? FONT_LCD_SUBPIXEL_LAYOUT_MAX : 1); aa++) { + if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { + _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (2 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (3 << 27) | (aa << 24)); + } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { + _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24)); + } else { + _ensure_glyph(fd, size, (int32_t)idx | (aa << 24)); + } } } } @@ -3001,7 +3205,7 @@ void TextServerAdvanced::font_render_range(const RID &p_font_rid, const Vector2i } } -void TextServerAdvanced::font_render_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_index) { +void TextServerAdvanced::_font_render_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_index) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -3014,23 +3218,25 @@ void TextServerAdvanced::font_render_glyph(const RID &p_font_rid, const Vector2i if (fd->msdf) { _ensure_glyph(fd, size, (int32_t)idx); } else { - if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { - _ensure_glyph(fd, size, (int32_t)idx | (0 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (1 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (2 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (3 << 27)); - } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { - _ensure_glyph(fd, size, (int32_t)idx | (1 << 27)); - _ensure_glyph(fd, size, (int32_t)idx | (0 << 27)); - } else { - _ensure_glyph(fd, size, (int32_t)idx); + for (int aa = 0; aa < ((fd->antialiasing == FONT_ANTIALIASING_LCD) ? FONT_LCD_SUBPIXEL_LAYOUT_MAX : 1); aa++) { + if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { + _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (2 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (3 << 27) | (aa << 24)); + } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { + _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24)); + _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24)); + } else { + _ensure_glyph(fd, size, (int32_t)idx | (aa << 24)); + } } } } #endif } -void TextServerAdvanced::font_draw_glyph(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const { +void TextServerAdvanced::_font_draw_glyph(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -3039,9 +3245,19 @@ void TextServerAdvanced::font_draw_glyph(const RID &p_font_rid, const RID &p_can ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); int32_t index = p_index & 0xffffff; // Remove subpixel shifts. + bool lcd_aa = false; #ifdef MODULE_FREETYPE_ENABLED if (!fd->msdf && fd->cache[size]->face) { + // LCD layout, bits 24, 25, 26 + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + lcd_aa = true; + index = index | (layout << 24); + } + } + // Subpixel X-shift, bits 27, 28 if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { int xshift = (int)(Math::floor(4 * (p_pos.x + 0.125)) - 4 * Math::floor(p_pos.x + 0.125)); index = index | (xshift << 27); @@ -3063,16 +3279,14 @@ void TextServerAdvanced::font_draw_glyph(const RID &p_font_rid, const RID &p_can if (gl.texture_idx != -1) { Color modulate = p_color; #ifdef MODULE_FREETYPE_ENABLED - if (fd->cache[size]->face && FT_HAS_COLOR(fd->cache[size]->face)) { + if (fd->cache[size]->face && (fd->cache[size]->textures[gl.texture_idx].format == Image::FORMAT_RGBA8) && !lcd_aa && !fd->msdf) { modulate.r = modulate.g = modulate.b = 1.0; } #endif if (RenderingServer::get_singleton() != nullptr) { if (fd->cache[size]->textures[gl.texture_idx].dirty) { - FontTexture &tex = fd->cache[size]->textures.write[gl.texture_idx]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + ShelfPackTexture &tex = fd->cache[size]->textures.write[gl.texture_idx]; + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } @@ -3088,27 +3302,33 @@ void TextServerAdvanced::font_draw_glyph(const RID &p_font_rid, const RID &p_can Point2 cpos = p_pos; cpos += gl.rect.position * (double)p_size / (double)fd->msdf_source_size; Size2 csize = gl.rect.size * (double)p_size / (double)fd->msdf_source_size; - RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, 0, fd->msdf_range); + RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, 0, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size); } else { + double scale = _font_get_scale(p_font_rid, p_size); Point2 cpos = p_pos; - cpos.y = Math::floor(cpos.y); if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { - cpos.x = ((int)Math::floor(cpos.x + 0.125)); + cpos.x = cpos.x + 0.125; } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { - cpos.x = ((int)Math::floor(cpos.x + 0.25)); - } else { + cpos.x = cpos.x + 0.25; + } + if (scale == 1.0) { + cpos.y = Math::floor(cpos.y); cpos.x = Math::floor(cpos.x); } cpos += gl.rect.position; Size2 csize = gl.rect.size; - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + if (lcd_aa) { + RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate); + } else { + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + } } } } } } -void TextServerAdvanced::font_draw_glyph_outline(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const { +void TextServerAdvanced::_font_draw_glyph_outline(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -3117,9 +3337,19 @@ void TextServerAdvanced::font_draw_glyph_outline(const RID &p_font_rid, const RI ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); int32_t index = p_index & 0xffffff; // Remove subpixel shifts. + bool lcd_aa = false; #ifdef MODULE_FREETYPE_ENABLED if (!fd->msdf && fd->cache[size]->face) { + // LCD layout, bits 24, 25, 26 + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + lcd_aa = true; + index = index | (layout << 24); + } + } + // Subpixel X-shift, bits 27, 28 if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { int xshift = (int)(Math::floor(4 * (p_pos.x + 0.125)) - 4 * Math::floor(p_pos.x + 0.125)); index = index | (xshift << 27); @@ -3141,16 +3371,14 @@ void TextServerAdvanced::font_draw_glyph_outline(const RID &p_font_rid, const RI if (gl.texture_idx != -1) { Color modulate = p_color; #ifdef MODULE_FREETYPE_ENABLED - if (fd->cache[size]->face && FT_HAS_COLOR(fd->cache[size]->face)) { + if (fd->cache[size]->face && (fd->cache[size]->textures[gl.texture_idx].format == Image::FORMAT_RGBA8) && !lcd_aa && !fd->msdf) { modulate.r = modulate.g = modulate.b = 1.0; } #endif if (RenderingServer::get_singleton() != nullptr) { if (fd->cache[size]->textures[gl.texture_idx].dirty) { - FontTexture &tex = fd->cache[size]->textures.write[gl.texture_idx]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + ShelfPackTexture &tex = fd->cache[size]->textures.write[gl.texture_idx]; + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } @@ -3166,27 +3394,33 @@ void TextServerAdvanced::font_draw_glyph_outline(const RID &p_font_rid, const RI Point2 cpos = p_pos; cpos += gl.rect.position * (double)p_size / (double)fd->msdf_source_size; Size2 csize = gl.rect.size * (double)p_size / (double)fd->msdf_source_size; - RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, p_outline_size * 2, fd->msdf_range); + RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, p_outline_size, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size); } else { Point2 cpos = p_pos; - cpos.y = Math::floor(cpos.y); + double scale = _font_get_scale(p_font_rid, p_size); if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) { - cpos.x = ((int)Math::floor(cpos.x + 0.125)); + cpos.x = cpos.x + 0.125; } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { - cpos.x = ((int)Math::floor(cpos.x + 0.25)); - } else { + cpos.x = cpos.x + 0.25; + } + if (scale == 1.0) { + cpos.y = Math::floor(cpos.y); cpos.x = Math::floor(cpos.x); } cpos += gl.rect.position; Size2 csize = gl.rect.size; - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + if (lcd_aa) { + RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate); + } else { + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + } } } } } } -bool TextServerAdvanced::font_is_language_supported(const RID &p_font_rid, const String &p_language) const { +bool TextServerAdvanced::_font_is_language_supported(const RID &p_font_rid, const String &p_language) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, false); @@ -3198,7 +3432,7 @@ bool TextServerAdvanced::font_is_language_supported(const RID &p_font_rid, const } } -void TextServerAdvanced::font_set_language_support_override(const RID &p_font_rid, const String &p_language, bool p_supported) { +void TextServerAdvanced::_font_set_language_support_override(const RID &p_font_rid, const String &p_language, bool p_supported) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -3206,7 +3440,7 @@ void TextServerAdvanced::font_set_language_support_override(const RID &p_font_ri fd->language_support_overrides[p_language] = p_supported; } -bool TextServerAdvanced::font_get_language_support_override(const RID &p_font_rid, const String &p_language) { +bool TextServerAdvanced::_font_get_language_support_override(const RID &p_font_rid, const String &p_language) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, false); @@ -3214,7 +3448,7 @@ bool TextServerAdvanced::font_get_language_support_override(const RID &p_font_ri return fd->language_support_overrides[p_language]; } -void TextServerAdvanced::font_remove_language_support_override(const RID &p_font_rid, const String &p_language) { +void TextServerAdvanced::_font_remove_language_support_override(const RID &p_font_rid, const String &p_language) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -3222,7 +3456,7 @@ void TextServerAdvanced::font_remove_language_support_override(const RID &p_font fd->language_support_overrides.erase(p_language); } -PackedStringArray TextServerAdvanced::font_get_language_support_overrides(const RID &p_font_rid) { +PackedStringArray TextServerAdvanced::_font_get_language_support_overrides(const RID &p_font_rid) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, PackedStringArray()); @@ -3234,7 +3468,7 @@ PackedStringArray TextServerAdvanced::font_get_language_support_overrides(const return out; } -bool TextServerAdvanced::font_is_script_supported(const RID &p_font_rid, const String &p_script) const { +bool TextServerAdvanced::_font_is_script_supported(const RID &p_font_rid, const String &p_script) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, false); @@ -3248,7 +3482,7 @@ bool TextServerAdvanced::font_is_script_supported(const RID &p_font_rid, const S } } -void TextServerAdvanced::font_set_script_support_override(const RID &p_font_rid, const String &p_script, bool p_supported) { +void TextServerAdvanced::_font_set_script_support_override(const RID &p_font_rid, const String &p_script, bool p_supported) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -3256,7 +3490,7 @@ void TextServerAdvanced::font_set_script_support_override(const RID &p_font_rid, fd->script_support_overrides[p_script] = p_supported; } -bool TextServerAdvanced::font_get_script_support_override(const RID &p_font_rid, const String &p_script) { +bool TextServerAdvanced::_font_get_script_support_override(const RID &p_font_rid, const String &p_script) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, false); @@ -3264,7 +3498,7 @@ bool TextServerAdvanced::font_get_script_support_override(const RID &p_font_rid, return fd->script_support_overrides[p_script]; } -void TextServerAdvanced::font_remove_script_support_override(const RID &p_font_rid, const String &p_script) { +void TextServerAdvanced::_font_remove_script_support_override(const RID &p_font_rid, const String &p_script) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -3272,7 +3506,7 @@ void TextServerAdvanced::font_remove_script_support_override(const RID &p_font_r fd->script_support_overrides.erase(p_script); } -PackedStringArray TextServerAdvanced::font_get_script_support_overrides(const RID &p_font_rid) { +PackedStringArray TextServerAdvanced::_font_get_script_support_overrides(const RID &p_font_rid) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, PackedStringArray()); @@ -3284,7 +3518,7 @@ PackedStringArray TextServerAdvanced::font_get_script_support_overrides(const RI return out; } -void TextServerAdvanced::font_set_opentype_feature_overrides(const RID &p_font_rid, const Dictionary &p_overrides) { +void TextServerAdvanced::_font_set_opentype_feature_overrides(const RID &p_font_rid, const Dictionary &p_overrides) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -3294,7 +3528,7 @@ void TextServerAdvanced::font_set_opentype_feature_overrides(const RID &p_font_r fd->feature_overrides = p_overrides; } -Dictionary TextServerAdvanced::font_get_opentype_feature_overrides(const RID &p_font_rid) const { +Dictionary TextServerAdvanced::_font_get_opentype_feature_overrides(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, Dictionary()); @@ -3302,7 +3536,7 @@ Dictionary TextServerAdvanced::font_get_opentype_feature_overrides(const RID &p_ return fd->feature_overrides; } -Dictionary TextServerAdvanced::font_supported_feature_list(const RID &p_font_rid) const { +Dictionary TextServerAdvanced::_font_supported_feature_list(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, Dictionary()); @@ -3312,7 +3546,7 @@ Dictionary TextServerAdvanced::font_supported_feature_list(const RID &p_font_rid return fd->supported_features; } -Dictionary TextServerAdvanced::font_supported_variation_list(const RID &p_font_rid) const { +Dictionary TextServerAdvanced::_font_supported_variation_list(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, Dictionary()); @@ -3322,11 +3556,11 @@ Dictionary TextServerAdvanced::font_supported_variation_list(const RID &p_font_r return fd->supported_varaitions; } -double TextServerAdvanced::font_get_global_oversampling() const { +double TextServerAdvanced::_font_get_global_oversampling() const { return oversampling; } -void TextServerAdvanced::font_set_global_oversampling(double p_oversampling) { +void TextServerAdvanced::_font_set_global_oversampling(double p_oversampling) { _THREAD_SAFE_METHOD_ if (oversampling != p_oversampling) { oversampling = p_oversampling; @@ -3334,8 +3568,8 @@ void TextServerAdvanced::font_set_global_oversampling(double p_oversampling) { font_owner.get_owned_list(&fonts); bool font_cleared = false; for (const RID &E : fonts) { - if (!font_is_multichannel_signed_distance_field(E) && font_get_oversampling(E) <= 0) { - font_clear_size_cache(E); + if (!_font_is_multichannel_signed_distance_field(E) && _font_get_oversampling(E) <= 0) { + _font_clear_size_cache(E); font_cleared = true; } } @@ -3444,7 +3678,7 @@ void TextServerAdvanced::full_copy(ShapedTextDataAdvanced *p_shaped) { p_shaped->parent = RID(); } -RID TextServerAdvanced::create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) { +RID TextServerAdvanced::_create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) { _THREAD_SAFE_METHOD_ ShapedTextDataAdvanced *sd = memnew(ShapedTextDataAdvanced); @@ -3454,7 +3688,7 @@ RID TextServerAdvanced::create_shaped_text(TextServer::Direction p_direction, Te return shaped_owner.make_rid(sd); } -void TextServerAdvanced::shaped_text_clear(const RID &p_shaped) { +void TextServerAdvanced::_shaped_text_clear(const RID &p_shaped) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND(!sd); @@ -3469,7 +3703,7 @@ void TextServerAdvanced::shaped_text_clear(const RID &p_shaped) { invalidate(sd, true); } -void TextServerAdvanced::shaped_text_set_direction(const RID &p_shaped, TextServer::Direction p_direction) { +void TextServerAdvanced::_shaped_text_set_direction(const RID &p_shaped, TextServer::Direction p_direction) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND(!sd); @@ -3483,7 +3717,7 @@ void TextServerAdvanced::shaped_text_set_direction(const RID &p_shaped, TextServ } } -TextServer::Direction TextServerAdvanced::shaped_text_get_direction(const RID &p_shaped) const { +TextServer::Direction TextServerAdvanced::_shaped_text_get_direction(const RID &p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, TextServer::DIRECTION_LTR); @@ -3491,7 +3725,7 @@ TextServer::Direction TextServerAdvanced::shaped_text_get_direction(const RID &p return sd->direction; } -TextServer::Direction TextServerAdvanced::shaped_text_get_inferred_direction(const RID &p_shaped) const { +TextServer::Direction TextServerAdvanced::_shaped_text_get_inferred_direction(const RID &p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, TextServer::DIRECTION_LTR); @@ -3499,7 +3733,7 @@ TextServer::Direction TextServerAdvanced::shaped_text_get_inferred_direction(con return sd->para_direction; } -void TextServerAdvanced::shaped_text_set_custom_punctuation(const RID &p_shaped, const String &p_punct) { +void TextServerAdvanced::_shaped_text_set_custom_punctuation(const RID &p_shaped, const String &p_punct) { _THREAD_SAFE_METHOD_ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND(!sd); @@ -3513,14 +3747,14 @@ void TextServerAdvanced::shaped_text_set_custom_punctuation(const RID &p_shaped, } } -String TextServerAdvanced::shaped_text_get_custom_punctuation(const RID &p_shaped) const { +String TextServerAdvanced::_shaped_text_get_custom_punctuation(const RID &p_shaped) const { _THREAD_SAFE_METHOD_ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, String()); return sd->custom_punct; } -void TextServerAdvanced::shaped_text_set_bidi_override(const RID &p_shaped, const Array &p_override) { +void TextServerAdvanced::_shaped_text_set_bidi_override(const RID &p_shaped, const Array &p_override) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND(!sd); @@ -3537,7 +3771,7 @@ void TextServerAdvanced::shaped_text_set_bidi_override(const RID &p_shaped, cons invalidate(sd, false); } -void TextServerAdvanced::shaped_text_set_orientation(const RID &p_shaped, TextServer::Orientation p_orientation) { +void TextServerAdvanced::_shaped_text_set_orientation(const RID &p_shaped, TextServer::Orientation p_orientation) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND(!sd); @@ -3551,7 +3785,7 @@ void TextServerAdvanced::shaped_text_set_orientation(const RID &p_shaped, TextSe } } -void TextServerAdvanced::shaped_text_set_preserve_invalid(const RID &p_shaped, bool p_enabled) { +void TextServerAdvanced::_shaped_text_set_preserve_invalid(const RID &p_shaped, bool p_enabled) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND(!sd); @@ -3563,7 +3797,7 @@ void TextServerAdvanced::shaped_text_set_preserve_invalid(const RID &p_shaped, b } } -bool TextServerAdvanced::shaped_text_get_preserve_invalid(const RID &p_shaped) const { +bool TextServerAdvanced::_shaped_text_get_preserve_invalid(const RID &p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); @@ -3571,7 +3805,7 @@ bool TextServerAdvanced::shaped_text_get_preserve_invalid(const RID &p_shaped) c return sd->preserve_invalid; } -void TextServerAdvanced::shaped_text_set_preserve_control(const RID &p_shaped, bool p_enabled) { +void TextServerAdvanced::_shaped_text_set_preserve_control(const RID &p_shaped, bool p_enabled) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND(!sd); @@ -3585,7 +3819,7 @@ void TextServerAdvanced::shaped_text_set_preserve_control(const RID &p_shaped, b } } -bool TextServerAdvanced::shaped_text_get_preserve_control(const RID &p_shaped) const { +bool TextServerAdvanced::_shaped_text_get_preserve_control(const RID &p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); @@ -3593,7 +3827,7 @@ bool TextServerAdvanced::shaped_text_get_preserve_control(const RID &p_shaped) c return sd->preserve_control; } -void TextServerAdvanced::shaped_text_set_spacing(const RID &p_shaped, SpacingType p_spacing, int64_t p_value) { +void TextServerAdvanced::_shaped_text_set_spacing(const RID &p_shaped, SpacingType p_spacing, int64_t p_value) { ERR_FAIL_INDEX((int)p_spacing, 4); ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND(!sd); @@ -3608,7 +3842,7 @@ void TextServerAdvanced::shaped_text_set_spacing(const RID &p_shaped, SpacingTyp } } -int64_t TextServerAdvanced::shaped_text_get_spacing(const RID &p_shaped, SpacingType p_spacing) const { +int64_t TextServerAdvanced::_shaped_text_get_spacing(const RID &p_shaped, SpacingType p_spacing) const { ERR_FAIL_INDEX_V((int)p_spacing, 4, 0); const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); @@ -3618,7 +3852,7 @@ int64_t TextServerAdvanced::shaped_text_get_spacing(const RID &p_shaped, Spacing return sd->extra_spacing[p_spacing]; } -TextServer::Orientation TextServerAdvanced::shaped_text_get_orientation(const RID &p_shaped) const { +TextServer::Orientation TextServerAdvanced::_shaped_text_get_orientation(const RID &p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, TextServer::ORIENTATION_HORIZONTAL); @@ -3626,41 +3860,33 @@ TextServer::Orientation TextServerAdvanced::shaped_text_get_orientation(const RI return sd->orientation; } -int64_t TextServerAdvanced::shaped_get_span_count(const RID &p_shaped) const { +int64_t TextServerAdvanced::_shaped_get_span_count(const RID &p_shaped) const { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, 0); return sd->spans.size(); } -Variant TextServerAdvanced::shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const { +Variant TextServerAdvanced::_shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, Variant()); ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant()); return sd->spans[p_index].meta; } -void TextServerAdvanced::shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const Array &p_fonts, int64_t p_size, const Dictionary &p_opentype_features) { +void TextServerAdvanced::_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) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND(!sd); ERR_FAIL_INDEX(p_index, sd->spans.size()); ShapedTextDataAdvanced::Span &span = sd->spans.ptrw()[p_index]; - bool changed = (span.font_size != p_size) || (span.features != p_opentype_features) || (p_fonts.size() != span.fonts.size()); - if (!changed) { - for (int i = 0; i < p_fonts.size(); i++) { - changed = changed || (span.fonts[i] != p_fonts[i]); - } - } - if (changed) { - span.fonts = p_fonts; - span.font_size = p_size; - span.features = p_opentype_features; + span.fonts = p_fonts; + span.font_size = p_size; + span.features = p_opentype_features; - invalidate(sd, false); - } + invalidate(sd, false); } -bool TextServerAdvanced::shaped_text_add_string(const RID &p_shaped, const String &p_text, const Array &p_fonts, int64_t p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) { +bool TextServerAdvanced::_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) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); ERR_FAIL_COND_V(p_size <= 0, false); @@ -3695,7 +3921,7 @@ bool TextServerAdvanced::shaped_text_add_string(const RID &p_shaped, const Strin return true; } -bool TextServerAdvanced::shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, int64_t p_length) { +bool TextServerAdvanced::_shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, int64_t p_length, float p_baseline) { _THREAD_SAFE_METHOD_ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); @@ -3715,6 +3941,7 @@ bool TextServerAdvanced::shaped_text_add_object(const RID &p_shaped, const Varia obj.inline_align = p_inline_align; obj.rect.size = p_size; obj.pos = span.start; + obj.baseline = p_baseline; sd->spans.push_back(span); sd->text = sd->text + String::chr(0xfffc).repeat(p_length); @@ -3725,7 +3952,7 @@ bool TextServerAdvanced::shaped_text_add_object(const RID &p_shaped, const Varia return true; } -bool TextServerAdvanced::shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align) { +bool TextServerAdvanced::_shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, float p_baseline) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); @@ -3733,6 +3960,7 @@ bool TextServerAdvanced::shaped_text_resize_object(const RID &p_shaped, const Va ERR_FAIL_COND_V(!sd->objects.has(p_key), false); sd->objects[p_key].rect.size = p_size; sd->objects[p_key].inline_align = p_inline_align; + sd->objects[p_key].baseline = p_baseline; if (sd->valid) { // Recalc string metrics. sd->ascent = 0; @@ -3766,14 +3994,14 @@ bool TextServerAdvanced::shaped_text_resize_object(const RID &p_shaped, const Va } else { if (gl.font_rid.is_valid()) { if (sd->orientation == ORIENTATION_HORIZONTAL) { - sd->ascent = MAX(sd->ascent, MAX(font_get_ascent(gl.font_rid, gl.font_size), -gl.y_off)); - sd->descent = MAX(sd->descent, MAX(font_get_descent(gl.font_rid, gl.font_size), gl.y_off)); + sd->ascent = MAX(sd->ascent, MAX(_font_get_ascent(gl.font_rid, gl.font_size), -gl.y_off)); + sd->descent = MAX(sd->descent, MAX(_font_get_descent(gl.font_rid, gl.font_size), gl.y_off)); } else { - sd->ascent = MAX(sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); - sd->descent = MAX(sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); + sd->ascent = MAX(sd->ascent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); + sd->descent = MAX(sd->descent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); } - sd->upos = MAX(sd->upos, font_get_underline_position(gl.font_rid, gl.font_size)); - sd->uthk = MAX(sd->uthk, font_get_underline_thickness(gl.font_rid, gl.font_size)); + sd->upos = MAX(sd->upos, _font_get_underline_position(gl.font_rid, gl.font_size)); + sd->uthk = MAX(sd->uthk, _font_get_underline_thickness(gl.font_rid, gl.font_size)); } else if (sd->preserve_invalid || (sd->preserve_control && is_control(gl.index))) { // Glyph not found, replace with hex code box. if (sd->orientation == ORIENTATION_HORIZONTAL) { @@ -3819,6 +4047,9 @@ void TextServerAdvanced::_realign(ShapedTextDataAdvanced *p_sd) const { case INLINE_ALIGNMENT_CENTER_TO: { E.value.rect.position.y -= E.value.rect.size.y / 2; } break; + case INLINE_ALIGNMENT_BASELINE_TO: { + E.value.rect.position.y -= E.value.baseline; + } break; case INLINE_ALIGNMENT_TOP_TO: { // NOP } break; @@ -3847,6 +4078,9 @@ void TextServerAdvanced::_realign(ShapedTextDataAdvanced *p_sd) const { case INLINE_ALIGNMENT_CENTER_TO: { E.value.rect.position.x -= E.value.rect.size.x / 2; } break; + case INLINE_ALIGNMENT_BASELINE_TO: { + E.value.rect.position.x -= E.value.baseline; + } break; case INLINE_ALIGNMENT_TOP_TO: { // NOP } break; @@ -3860,7 +4094,7 @@ void TextServerAdvanced::_realign(ShapedTextDataAdvanced *p_sd) const { p_sd->descent = full_descent; } -RID TextServerAdvanced::shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const { +RID TextServerAdvanced::_shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const { _THREAD_SAFE_METHOD_ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); @@ -3868,10 +4102,10 @@ RID TextServerAdvanced::shaped_text_substr(const RID &p_shaped, int64_t p_start, MutexLock lock(sd->mutex); if (sd->parent != RID()) { - return shaped_text_substr(sd->parent, p_start, p_length); + return _shaped_text_substr(sd->parent, p_start, p_length); } if (!sd->valid) { - const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); + const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } ERR_FAIL_COND_V(p_start < 0 || p_length < 0, RID()); ERR_FAIL_COND_V(sd->start > p_start || sd->end < p_start, RID()); @@ -3975,11 +4209,11 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S } else { if (gl.font_rid.is_valid()) { if (p_new_sd->orientation == ORIENTATION_HORIZONTAL) { - p_new_sd->ascent = MAX(p_new_sd->ascent, MAX(font_get_ascent(gl.font_rid, gl.font_size), -gl.y_off)); - p_new_sd->descent = MAX(p_new_sd->descent, MAX(font_get_descent(gl.font_rid, gl.font_size), gl.y_off)); + p_new_sd->ascent = MAX(p_new_sd->ascent, MAX(_font_get_ascent(gl.font_rid, gl.font_size), -gl.y_off)); + p_new_sd->descent = MAX(p_new_sd->descent, MAX(_font_get_descent(gl.font_rid, gl.font_size), gl.y_off)); } else { - p_new_sd->ascent = MAX(p_new_sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); - p_new_sd->descent = MAX(p_new_sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); + p_new_sd->ascent = MAX(p_new_sd->ascent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); + p_new_sd->descent = MAX(p_new_sd->descent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); } } else if (p_new_sd->preserve_invalid || (p_new_sd->preserve_control && is_control(gl.index))) { // Glyph not found, replace with hex code box. @@ -4005,7 +4239,7 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S return true; } -RID TextServerAdvanced::shaped_text_get_parent(const RID &p_shaped) const { +RID TextServerAdvanced::_shaped_text_get_parent(const RID &p_shaped) const { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, RID()); @@ -4013,16 +4247,16 @@ RID TextServerAdvanced::shaped_text_get_parent(const RID &p_shaped) const { return sd->parent; } -double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double p_width, BitField<TextServer::JustificationFlag> p_jst_flags) { +double TextServerAdvanced::_shaped_text_fit_to_width(const RID &p_shaped, double p_width, BitField<TextServer::JustificationFlag> p_jst_flags) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, 0.0); MutexLock lock(sd->mutex); if (!sd->valid) { - const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); + const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } if (!sd->justification_ops_valid) { - const_cast<TextServerAdvanced *>(this)->shaped_text_update_justification_ops(p_shaped); + const_cast<TextServerAdvanced *>(this)->_shaped_text_update_justification_ops(p_shaped); } sd->fit_width_minimum_reached = false; @@ -4170,16 +4404,16 @@ double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double return Math::ceil(justification_width); } -double TextServerAdvanced::shaped_text_tab_align(const RID &p_shaped, const PackedFloat32Array &p_tab_stops) { +double TextServerAdvanced::_shaped_text_tab_align(const RID &p_shaped, const PackedFloat32Array &p_tab_stops) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, 0.0); MutexLock lock(sd->mutex); if (!sd->valid) { - const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); + const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } if (!sd->line_breaks_valid) { - const_cast<TextServerAdvanced *>(this)->shaped_text_update_breaks(p_shaped); + const_cast<TextServerAdvanced *>(this)->_shaped_text_update_breaks(p_shaped); } for (int i = 0; i < p_tab_stops.size(); i++) { @@ -4226,13 +4460,13 @@ double TextServerAdvanced::shaped_text_tab_align(const RID &p_shaped, const Pack return 0.0; } -void TextServerAdvanced::shaped_text_overrun_trim_to_width(const RID &p_shaped_line, double p_width, BitField<TextServer::TextOverrunFlag> p_trim_flags) { +void TextServerAdvanced::_shaped_text_overrun_trim_to_width(const RID &p_shaped_line, double p_width, BitField<TextServer::TextOverrunFlag> p_trim_flags) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped_line); ERR_FAIL_COND_MSG(!sd, "ShapedTextDataAdvanced invalid."); MutexLock lock(sd->mutex); if (!sd->valid) { - shaped_text_shape(p_shaped_line); + _shaped_text_shape(p_shaped_line); } sd->text_trimmed = false; @@ -4271,30 +4505,30 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(const RID &p_shaped_l // Find usable fonts, if fonts from the last glyph do not have required chars. RID dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid; - if (!font_has_char(dot_gl_font_rid, '.')) { + if (!_font_has_char(dot_gl_font_rid, '.')) { const Array &fonts = spans[spans.size() - 1].fonts; for (int i = 0; i < fonts.size(); i++) { - if (font_has_char(fonts[i], '.')) { + if (_font_has_char(fonts[i], '.')) { dot_gl_font_rid = fonts[i]; break; } } } RID whitespace_gl_font_rid = sd_glyphs[sd_size - 1].font_rid; - if (!font_has_char(whitespace_gl_font_rid, '.')) { + if (!_font_has_char(whitespace_gl_font_rid, '.')) { const Array &fonts = spans[spans.size() - 1].fonts; for (int i = 0; i < fonts.size(); i++) { - if (font_has_char(fonts[i], ' ')) { + if (_font_has_char(fonts[i], ' ')) { whitespace_gl_font_rid = fonts[i]; break; } } } - int32_t dot_gl_idx = dot_gl_font_rid.is_valid() ? font_get_glyph_index(dot_gl_font_rid, last_gl_font_size, '.') : -10; - Vector2 dot_adv = dot_gl_font_rid.is_valid() ? font_get_glyph_advance(dot_gl_font_rid, last_gl_font_size, dot_gl_idx) : Vector2(); - int32_t whitespace_gl_idx = whitespace_gl_font_rid.is_valid() ? font_get_glyph_index(whitespace_gl_font_rid, last_gl_font_size, ' ') : -10; - Vector2 whitespace_adv = whitespace_gl_font_rid.is_valid() ? font_get_glyph_advance(whitespace_gl_font_rid, last_gl_font_size, whitespace_gl_idx) : Vector2(); + int32_t dot_gl_idx = dot_gl_font_rid.is_valid() ? _font_get_glyph_index(dot_gl_font_rid, last_gl_font_size, '.', 0) : -10; + Vector2 dot_adv = dot_gl_font_rid.is_valid() ? _font_get_glyph_advance(dot_gl_font_rid, last_gl_font_size, dot_gl_idx) : Vector2(); + int32_t whitespace_gl_idx = whitespace_gl_font_rid.is_valid() ? _font_get_glyph_index(whitespace_gl_font_rid, last_gl_font_size, ' ', 0) : -10; + Vector2 whitespace_adv = whitespace_gl_font_rid.is_valid() ? _font_get_glyph_advance(whitespace_gl_font_rid, last_gl_font_size, whitespace_gl_idx) : Vector2(); int ellipsis_width = 0; if (add_ellipsis && whitespace_gl_font_rid.is_valid()) { @@ -4316,35 +4550,40 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(const RID &p_shaped_l int glyphs_to = (is_rtl) ? sd_size - 1 : -1; int glyphs_delta = (is_rtl) ? +1 : -1; - for (int i = glyphs_from; i != glyphs_to; i += glyphs_delta) { - if (!is_rtl) { - width -= sd_glyphs[i].advance * sd_glyphs[i].repeat; - } - if (sd_glyphs[i].count > 0) { - bool above_min_char_threshold = ((is_rtl) ? sd_size - 1 - i : i) >= ell_min_characters; + if (enforce_ellipsis && (width + ellipsis_width <= p_width)) { + trim_pos = -1; + ellipsis_pos = (is_rtl) ? 0 : sd_size; + } else { + for (int i = glyphs_from; i != glyphs_to; i += glyphs_delta) { + if (!is_rtl) { + width -= sd_glyphs[i].advance * sd_glyphs[i].repeat; + } + if (sd_glyphs[i].count > 0) { + bool above_min_char_threshold = ((is_rtl) ? sd_size - 1 - i : i) >= ell_min_characters; - if (width + (((above_min_char_threshold && add_ellipsis) || enforce_ellipsis) ? ellipsis_width : 0) <= p_width) { - if (cut_per_word && above_min_char_threshold) { - if ((sd_glyphs[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) { + if (width + (((above_min_char_threshold && add_ellipsis) || enforce_ellipsis) ? ellipsis_width : 0) <= p_width) { + if (cut_per_word && above_min_char_threshold) { + if ((sd_glyphs[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) { + last_valid_cut = i; + found = true; + } + } else { last_valid_cut = i; found = true; } - } else { - last_valid_cut = i; - found = true; - } - if (found) { - trim_pos = last_valid_cut; + if (found) { + trim_pos = last_valid_cut; - if (add_ellipsis && (above_min_char_threshold || enforce_ellipsis) && width - ellipsis_width <= p_width) { - ellipsis_pos = trim_pos; + if (add_ellipsis && (above_min_char_threshold || enforce_ellipsis) && width - ellipsis_width <= p_width) { + ellipsis_pos = trim_pos; + } + break; } - break; } } - } - if (is_rtl) { - width -= sd_glyphs[i].advance * sd_glyphs[i].repeat; + if (is_rtl) { + width -= sd_glyphs[i].advance * sd_glyphs[i].repeat; + } } } @@ -4388,7 +4627,7 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(const RID &p_shaped_l } } -int64_t TextServerAdvanced::shaped_text_get_trim_pos(const RID &p_shaped) const { +int64_t TextServerAdvanced::_shaped_text_get_trim_pos(const RID &p_shaped) const { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextDataAdvanced invalid."); @@ -4396,7 +4635,7 @@ int64_t TextServerAdvanced::shaped_text_get_trim_pos(const RID &p_shaped) const return sd->overrun_trim_data.trim_pos; } -int64_t TextServerAdvanced::shaped_text_get_ellipsis_pos(const RID &p_shaped) const { +int64_t TextServerAdvanced::_shaped_text_get_ellipsis_pos(const RID &p_shaped) const { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextDataAdvanced invalid."); @@ -4404,7 +4643,7 @@ int64_t TextServerAdvanced::shaped_text_get_ellipsis_pos(const RID &p_shaped) co return sd->overrun_trim_data.ellipsis_pos; } -const Glyph *TextServerAdvanced::shaped_text_get_ellipsis_glyphs(const RID &p_shaped) const { +const Glyph *TextServerAdvanced::_shaped_text_get_ellipsis_glyphs(const RID &p_shaped) const { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V_MSG(!sd, nullptr, "ShapedTextDataAdvanced invalid."); @@ -4412,7 +4651,7 @@ const Glyph *TextServerAdvanced::shaped_text_get_ellipsis_glyphs(const RID &p_sh return sd->overrun_trim_data.ellipsis_glyph_buf.ptr(); } -int64_t TextServerAdvanced::shaped_text_get_ellipsis_glyph_count(const RID &p_shaped) const { +int64_t TextServerAdvanced::_shaped_text_get_ellipsis_glyph_count(const RID &p_shaped) const { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V_MSG(!sd, 0, "ShapedTextDataAdvanced invalid."); @@ -4420,13 +4659,13 @@ int64_t TextServerAdvanced::shaped_text_get_ellipsis_glyph_count(const RID &p_sh return sd->overrun_trim_data.ellipsis_glyph_buf.size(); } -bool TextServerAdvanced::shaped_text_update_breaks(const RID &p_shaped) { +bool TextServerAdvanced::_shaped_text_update_breaks(const RID &p_shaped) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); MutexLock lock(sd->mutex); if (!sd->valid) { - shaped_text_shape(p_shaped); + _shaped_text_shape(p_shaped); } if (sd->line_breaks_valid) { @@ -4437,6 +4676,7 @@ bool TextServerAdvanced::shaped_text_update_breaks(const RID &p_shaped) { if (!sd->break_ops_valid) { sd->breaks.clear(); + sd->break_inserts = 0; UErrorCode err = U_ZERO_ERROR; int i = 0; while (i < sd->spans.size()) { @@ -4466,6 +4706,11 @@ bool TextServerAdvanced::shaped_text_update_breaks(const RID &p_shaped) { } else if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_SOFT) && (ubrk_getRuleStatus(bi) < UBRK_LINE_SOFT_LIMIT)) { sd->breaks[pos] = false; } + int pos_p = pos - 1 - sd->start; + char32_t c = sd->text[pos_p]; + if (pos - sd->start != sd->end && !is_whitespace(c) && (c != 0xfffc)) { + sd->break_inserts++; + } } } ubrk_close(bi); @@ -4474,60 +4719,83 @@ bool TextServerAdvanced::shaped_text_update_breaks(const RID &p_shaped) { sd->break_ops_valid = true; } + Vector<Glyph> glyphs_new; + + bool rewrite = false; + int sd_shift = 0; + int sd_size = sd->glyphs.size(); + Glyph *sd_glyphs = sd->glyphs.ptrw(); + Glyph *sd_glyphs_new = nullptr; + + if (sd->break_inserts > 0) { + glyphs_new.resize(sd->glyphs.size() + sd->break_inserts); + sd_glyphs_new = glyphs_new.ptrw(); + rewrite = true; + } else { + sd_glyphs_new = sd_glyphs; + } + sd->sort_valid = false; sd->glyphs_logical.clear(); - int sd_size = sd->glyphs.size(); const char32_t *ch = sd->text.ptr(); - Glyph *sd_glyphs = sd->glyphs.ptrw(); int c_punct_size = sd->custom_punct.length(); const char32_t *c_punct = sd->custom_punct.ptr(); for (int i = 0; i < sd_size; i++) { + if (rewrite) { + for (int j = 0; j < sd_glyphs[i].count; j++) { + sd_glyphs_new[sd_shift + i + j] = sd_glyphs[i + j]; + } + } if (sd_glyphs[i].count > 0) { char32_t c = ch[sd_glyphs[i].start - sd->start]; if (c == 0xfffc) { + i += (sd_glyphs[i].count - 1); continue; } if (c == 0x0009 || c == 0x000b) { - sd_glyphs[i].flags |= GRAPHEME_IS_TAB; + sd_glyphs_new[sd_shift + i].flags |= GRAPHEME_IS_TAB; } if (is_whitespace(c)) { - sd_glyphs[i].flags |= GRAPHEME_IS_SPACE; + sd_glyphs_new[sd_shift + i].flags |= GRAPHEME_IS_SPACE; } if (c_punct_size == 0) { if (u_ispunct(c) && c != 0x005f) { - sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION; + sd_glyphs_new[sd_shift + i].flags |= GRAPHEME_IS_PUNCTUATION; } } else { for (int j = 0; j < c_punct_size; j++) { if (c_punct[j] == c) { - sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION; + sd_glyphs_new[sd_shift + i].flags |= GRAPHEME_IS_PUNCTUATION; break; } } } if (is_underscore(c)) { - sd_glyphs[i].flags |= GRAPHEME_IS_UNDERSCORE; + sd_glyphs_new[sd_shift + i].flags |= GRAPHEME_IS_UNDERSCORE; } if (sd->breaks.has(sd_glyphs[i].end)) { if (sd->breaks[sd_glyphs[i].end] && (is_linebreak(c))) { - sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD; + sd_glyphs_new[sd_shift + i].flags |= GRAPHEME_IS_BREAK_HARD; } else if (is_whitespace(c)) { - sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_SOFT; + sd_glyphs_new[sd_shift + i].flags |= GRAPHEME_IS_BREAK_SOFT; } else { int count = sd_glyphs[i].count; // Do not add extra space at the end of the line. if (sd_glyphs[i].end == sd->end) { + i += (sd_glyphs[i].count - 1); continue; } // Do not add extra space after existing space. if (sd_glyphs[i].flags & GRAPHEME_IS_RTL) { if ((i + count < sd_size - 1) && ((sd_glyphs[i + count].flags & (GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT)) == (GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT))) { + i += (sd_glyphs[i].count - 1); continue; } } else { - if ((i > 0) && ((sd_glyphs[i - 1].flags & (GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT)) == (GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT))) { + if ((sd_glyphs[i].flags & (GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT)) == (GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT)) { + i += (sd_glyphs[i].count - 1); continue; } } @@ -4540,22 +4808,28 @@ bool TextServerAdvanced::shaped_text_update_breaks(const RID &p_shaped) { gl.flags = GRAPHEME_IS_BREAK_SOFT | GRAPHEME_IS_VIRTUAL | GRAPHEME_IS_SPACE; if (sd_glyphs[i].flags & GRAPHEME_IS_RTL) { gl.flags |= GRAPHEME_IS_RTL; - sd->glyphs.insert(i, gl); // Insert before. + for (int j = sd_glyphs[i].count - 1; j >= 0; j--) { + sd_glyphs_new[sd_shift + i + j + 1] = sd_glyphs_new[sd_shift + i + j]; + } + sd_glyphs_new[sd_shift + i] = gl; } else { - sd->glyphs.insert(i + count, gl); // Insert after. + sd_glyphs_new[sd_shift + i + count] = gl; } - i += count; - - // Update write pointer and size. - sd_size = sd->glyphs.size(); - sd_glyphs = sd->glyphs.ptrw(); - continue; + sd_shift++; + ERR_FAIL_COND_V_MSG(sd_shift > sd->break_inserts, false, "Invalid break insert count!"); } } - i += (sd_glyphs[i].count - 1); } } + if (sd_shift < sd->break_inserts) { + // Note: should not happen with a normal text, but might be a case with special fonts that substitute a long string (with breaks opportunities in it) with a single glyph (like Font Awesome). + glyphs_new.resize(sd->glyphs.size() + sd_shift); + } + + if (sd->break_inserts > 0) { + sd->glyphs = glyphs_new; + } sd->line_breaks_valid = true; @@ -4637,16 +4911,16 @@ _FORCE_INLINE_ int64_t _generate_kashida_justification_opportunies(const String return kashida_pos; } -bool TextServerAdvanced::shaped_text_update_justification_ops(const RID &p_shaped) { +bool TextServerAdvanced::_shaped_text_update_justification_ops(const RID &p_shaped) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); MutexLock lock(sd->mutex); if (!sd->valid) { - shaped_text_shape(p_shaped); + _shaped_text_shape(p_shaped); } if (!sd->line_breaks_valid) { - shaped_text_update_breaks(p_shaped); + _shaped_text_update_breaks(p_shaped); } if (sd->justification_ops_valid) { @@ -4791,7 +5065,8 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(const RID &p_shape Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced *p_sd, char32_t p_char, hb_script_t p_script, hb_direction_t p_direction, const RID &p_font, int64_t p_font_size) { hb_font_t *hb_font = _font_get_hb_handle(p_font, p_font_size); - bool subpos = (font_get_subpixel_positioning(p_font) == SUBPIXEL_POSITIONING_ONE_HALF) || (font_get_subpixel_positioning(p_font) == SUBPIXEL_POSITIONING_ONE_QUARTER) || (font_get_subpixel_positioning(p_font) == SUBPIXEL_POSITIONING_AUTO && p_font_size <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE); + double scale = _font_get_scale(p_font, p_font_size); + bool subpos = (scale != 1.0) || (_font_get_subpixel_positioning(p_font) == SUBPIXEL_POSITIONING_ONE_HALF) || (_font_get_subpixel_positioning(p_font) == SUBPIXEL_POSITIONING_ONE_QUARTER) || (_font_get_subpixel_positioning(p_font) == SUBPIXEL_POSITIONING_AUTO && p_font_size <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE); ERR_FAIL_COND_V(hb_font == nullptr, Glyph()); hb_buffer_clear_contents(p_sd->hb_buffer); @@ -4817,25 +5092,24 @@ Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced *p_sd, char gl.font_size = p_font_size; if (glyph_count > 0) { - double scale = font_get_scale(p_font, p_font_size); if (p_sd->orientation == ORIENTATION_HORIZONTAL) { if (subpos) { - gl.advance = glyph_pos[0].x_advance / (64.0 / scale) + _get_extra_advance(p_font, p_font_size); + gl.advance = (double)glyph_pos[0].x_advance / (64.0 / scale) + _get_extra_advance(p_font, p_font_size); } else { - gl.advance = Math::round(glyph_pos[0].x_advance / (64.0 / scale) + _get_extra_advance(p_font, p_font_size)); + gl.advance = Math::round((double)glyph_pos[0].x_advance / (64.0 / scale) + _get_extra_advance(p_font, p_font_size)); } } else { - gl.advance = -Math::round(glyph_pos[0].y_advance / (64.0 / scale)); + gl.advance = -Math::round((double)glyph_pos[0].y_advance / (64.0 / scale)); } gl.count = 1; gl.index = glyph_info[0].codepoint; if (subpos) { - gl.x_off = glyph_pos[0].x_offset / (64.0 / scale); + gl.x_off = (double)glyph_pos[0].x_offset / (64.0 / scale); } else { - gl.x_off = Math::round(glyph_pos[0].x_offset / (64.0 / scale)); + gl.x_off = Math::round((double)glyph_pos[0].x_offset / (64.0 / scale)); } - gl.y_off = -Math::round(glyph_pos[0].y_offset / (64.0 / scale)); + gl.y_off = -Math::round((double)glyph_pos[0].y_offset / (64.0 / scale)); if ((glyph_info[0].codepoint != 0) || !u_isgraph(p_char)) { gl.flags |= GRAPHEME_IS_VALID; @@ -4852,7 +5126,7 @@ _FORCE_INLINE_ void TextServerAdvanced::_add_featuers(const Dictionary &p_source if (value >= 0) { hb_feature_t feature; if (keys[i].get_type() == Variant::STRING) { - feature.tag = name_to_tag(keys[i]); + feature.tag = _name_to_tag(keys[i]); } else { feature.tag = keys[i]; } @@ -4864,10 +5138,177 @@ _FORCE_INLINE_ void TextServerAdvanced::_add_featuers(const Dictionary &p_source } } -void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, Array p_fonts, int64_t p_span, int64_t p_fb_index) { +void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, TypedArray<RID> p_fonts, int64_t p_span, int64_t p_fb_index, int64_t p_prev_start, int64_t p_prev_end) { + RID f; int fs = p_sd->spans[p_span].font_size; - if (p_fb_index >= p_fonts.size()) { - // Add fallback glyphs. + + if (p_fb_index >= 0 && p_fb_index < p_fonts.size()) { + // Try font from list. + f = p_fonts[p_fb_index]; + } else if (OS::get_singleton()->has_feature("system_fonts") && p_fonts.size() > 0 && ((p_fb_index == p_fonts.size()) || (p_fb_index > p_fonts.size() && p_start != p_prev_start))) { + // Try system fallback. + RID fdef = p_fonts[0]; + if (_font_is_allow_system_fallback(fdef)) { + String text = p_sd->text.substr(p_start, 1); + String font_name = _font_get_name(fdef); + BitField<FontStyle> font_style = _font_get_style(fdef); + int font_weight = _font_get_weight(fdef); + int font_stretch = _font_get_stretch(fdef); + Dictionary dvar = _font_get_variation_coordinates(fdef); + static int64_t wgth_tag = _name_to_tag("weight"); + static int64_t wdth_tag = _name_to_tag("width"); + static int64_t ital_tag = _name_to_tag("italic"); + if (dvar.has(wgth_tag)) { + font_weight = dvar[wgth_tag].operator int(); + } + if (dvar.has(wdth_tag)) { + font_stretch = dvar[wdth_tag].operator int(); + } + if (dvar.has(ital_tag) && dvar[ital_tag].operator int() == 1) { + font_style.set_flag(TextServer::FONT_ITALIC); + } + + char scr_buffer[5] = { 0, 0, 0, 0, 0 }; + hb_tag_to_string(hb_script_to_iso15924_tag(p_script), scr_buffer); + String script_code = String(scr_buffer); + String locale = (p_sd->spans[p_span].language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_sd->spans[p_span].language; + + PackedStringArray fallback_font_name = OS::get_singleton()->get_system_font_path_for_text(font_name, text, locale, script_code, font_weight, font_stretch, font_style & TextServer::FONT_ITALIC); +#ifdef GDEXTENSION + for (int fb = 0; fb < fallback_font_name.size(); fb++) { + const String &E = fallback_font_name[fb]; +#else + for (const String &E : fallback_font_name) { +#endif + SystemFontKey key = SystemFontKey(E, font_style & TextServer::FONT_ITALIC, font_weight, font_stretch, fdef, this); + if (system_fonts.has(key)) { + const SystemFontCache &sysf_cache = system_fonts[key]; + int best_score = 0; + int best_match = -1; + for (int face_idx = 0; face_idx < sysf_cache.var.size(); face_idx++) { + const SystemFontCacheRec &F = sysf_cache.var[face_idx]; + if (unlikely(!_font_has_char(F.rid, text[0]))) { + continue; + } + BitField<FontStyle> style = _font_get_style(F.rid); + int weight = _font_get_weight(F.rid); + int stretch = _font_get_stretch(F.rid); + int score = (20 - Math::abs(weight - font_weight) / 50); + score += (20 - Math::abs(stretch - font_stretch) / 10); + if (bool(style & TextServer::FONT_ITALIC) == bool(font_style & TextServer::FONT_ITALIC)) { + score += 30; + } + if (score >= best_score) { + best_score = score; + best_match = face_idx; + } + if (best_score == 70) { + break; + } + } + if (best_match != -1) { + f = sysf_cache.var[best_match].rid; + } + } + if (!f.is_valid()) { + if (system_fonts.has(key)) { + const SystemFontCache &sysf_cache = system_fonts[key]; + if (sysf_cache.max_var == sysf_cache.var.size()) { + // All subfonts already tested, skip. + continue; + } + } + + if (!system_font_data.has(E)) { + system_font_data[E] = FileAccess::get_file_as_bytes(E); + } + + const PackedByteArray &font_data = system_font_data[E]; + + SystemFontCacheRec sysf; + sysf.rid = _create_font(); + _font_set_data_ptr(sysf.rid, font_data.ptr(), font_data.size()); + + Dictionary var = dvar; + // Select matching style from collection. + int best_score = 0; + int best_match = -1; + for (int face_idx = 0; face_idx < _font_get_face_count(sysf.rid); face_idx++) { + _font_set_face_index(sysf.rid, face_idx); + if (unlikely(!_font_has_char(sysf.rid, text[0]))) { + continue; + } + BitField<FontStyle> style = _font_get_style(sysf.rid); + int weight = _font_get_weight(sysf.rid); + int stretch = _font_get_stretch(sysf.rid); + int score = (20 - Math::abs(weight - font_weight) / 50); + score += (20 - Math::abs(stretch - font_stretch) / 10); + if (bool(style & TextServer::FONT_ITALIC) == bool(font_style & TextServer::FONT_ITALIC)) { + score += 30; + } + if (score >= best_score) { + best_score = score; + best_match = face_idx; + } + if (best_score == 70) { + break; + } + } + if (best_match == -1) { + _free_rid(sysf.rid); + continue; + } else { + _font_set_face_index(sysf.rid, best_match); + } + sysf.index = best_match; + + // If it's a variable font, apply weight, stretch and italic coordinates to match requested style. + if (best_score != 70) { + Dictionary ftr = _font_supported_variation_list(sysf.rid); + if (ftr.has(wdth_tag)) { + var[wdth_tag] = font_stretch; + _font_set_stretch(sysf.rid, font_stretch); + } + if (ftr.has(wgth_tag)) { + var[wgth_tag] = font_weight; + _font_set_weight(sysf.rid, font_weight); + } + if ((font_style & TextServer::FONT_ITALIC) && ftr.has(ital_tag)) { + var[ital_tag] = 1; + _font_set_style(sysf.rid, _font_get_style(sysf.rid) | TextServer::FONT_ITALIC); + } + } + + _font_set_antialiasing(sysf.rid, key.antialiasing); + _font_set_generate_mipmaps(sysf.rid, key.mipmaps); + _font_set_multichannel_signed_distance_field(sysf.rid, key.msdf); + _font_set_msdf_pixel_range(sysf.rid, key.msdf_range); + _font_set_msdf_size(sysf.rid, key.msdf_source_size); + _font_set_fixed_size(sysf.rid, key.fixed_size); + _font_set_force_autohinter(sysf.rid, key.force_autohinter); + _font_set_hinting(sysf.rid, key.hinting); + _font_set_subpixel_positioning(sysf.rid, key.subpixel_positioning); + _font_set_variation_coordinates(sysf.rid, var); + _font_set_oversampling(sysf.rid, key.oversampling); + _font_set_embolden(sysf.rid, key.embolden); + _font_set_transform(sysf.rid, key.transform); + + if (system_fonts.has(key)) { + system_fonts[key].var.push_back(sysf); + } else { + SystemFontCache &sysf_cache = system_fonts[key]; + sysf_cache.max_var = _font_get_face_count(sysf.rid); + sysf_cache.var.push_back(sysf); + } + f = sysf.rid; + } + break; + } + } + } + + if (!f.is_valid()) { + // No valid font, use fallback hex code boxes. for (int i = p_start; i < p_end; i++) { if (p_sd->preserve_invalid || (p_sd->preserve_control && is_control(p_sd->text[i]))) { Glyph gl; @@ -4885,6 +5326,8 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star p_sd->ascent = MAX(p_sd->ascent, get_hex_code_box_size(fs, gl.index).y); } else { gl.advance = get_hex_code_box_size(fs, gl.index).y; + gl.y_off = get_hex_code_box_size(fs, gl.index).y; + gl.x_off = -Math::round(get_hex_code_box_size(fs, gl.index).x * 0.5); p_sd->ascent = MAX(p_sd->ascent, Math::round(get_hex_code_box_size(fs, gl.index).x * 0.5)); p_sd->descent = MAX(p_sd->descent, Math::round(get_hex_code_box_size(fs, gl.index).x * 0.5)); } @@ -4896,19 +5339,18 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star return; } - RID f = p_fonts[p_fb_index]; FontAdvanced *fd = font_owner.get_or_null(f); ERR_FAIL_COND(!fd); MutexLock lock(fd->mutex); Vector2i fss = _get_size(fd, fs); hb_font_t *hb_font = _font_get_hb_handle(f, fs); - double scale = font_get_scale(f, fs); + double scale = _font_get_scale(f, fs); double sp_sp = p_sd->extra_spacing[SPACING_SPACE]; double sp_gl = p_sd->extra_spacing[SPACING_GLYPH]; bool last_run = (p_sd->end == p_end); double ea = _get_extra_advance(f, fs); - bool subpos = (font_get_subpixel_positioning(f) == SUBPIXEL_POSITIONING_ONE_HALF) || (font_get_subpixel_positioning(f) == SUBPIXEL_POSITIONING_ONE_QUARTER) || (font_get_subpixel_positioning(f) == SUBPIXEL_POSITIONING_AUTO && fs <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE); + bool subpos = (scale != 1.0) || (_font_get_subpixel_positioning(f) == SUBPIXEL_POSITIONING_ONE_HALF) || (_font_get_subpixel_positioning(f) == SUBPIXEL_POSITIONING_ONE_QUARTER) || (_font_get_subpixel_positioning(f) == SUBPIXEL_POSITIONING_AUTO && fs <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE); ERR_FAIL_COND(hb_font == nullptr); hb_buffer_clear_contents(p_sd->hb_buffer); @@ -4936,7 +5378,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star hb_buffer_add_utf32(p_sd->hb_buffer, (const uint32_t *)p_sd->text.ptr(), p_sd->text.length(), p_start, p_end - p_start); Vector<hb_feature_t> ftrs; - _add_featuers(font_get_opentype_feature_overrides(f), ftrs); + _add_featuers(_font_get_opentype_feature_overrides(f), ftrs); _add_featuers(p_sd->spans[p_span].features, ftrs); hb_shape(hb_font, p_sd->hb_buffer, ftrs.is_empty() ? nullptr : &ftrs[0], ftrs.size()); @@ -4945,6 +5387,14 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(p_sd->hb_buffer, &glyph_count); hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(p_sd->hb_buffer, &glyph_count); + int mod = 0; + if (fd->antialiasing == FONT_ANTIALIASING_LCD) { + TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout"); + if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) { + mod = (layout << 24); + } + } + // Process glyphs. if (glyph_count > 0) { Glyph *w = (Glyph *)memalloc(glyph_count * sizeof(Glyph)); @@ -4983,7 +5433,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star gl.end = end; gl.count = 0; - gl.font_rid = p_fonts[p_fb_index]; + gl.font_rid = f; gl.font_size = fs; if (glyph_info[i].mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) { @@ -4998,22 +5448,22 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star gl.index = glyph_info[i].codepoint; if (gl.index != 0) { - _ensure_glyph(fd, fss, gl.index); + _ensure_glyph(fd, fss, gl.index | mod); if (p_sd->orientation == ORIENTATION_HORIZONTAL) { if (subpos) { - gl.advance = glyph_pos[i].x_advance / (64.0 / scale) + ea; + gl.advance = (double)glyph_pos[i].x_advance / (64.0 / scale) + ea; } else { - gl.advance = Math::round(glyph_pos[i].x_advance / (64.0 / scale) + ea); + gl.advance = Math::round((double)glyph_pos[i].x_advance / (64.0 / scale) + ea); } } else { - gl.advance = -Math::round(glyph_pos[i].y_advance / (64.0 / scale)); + gl.advance = -Math::round((double)glyph_pos[i].y_advance / (64.0 / scale)); } if (subpos) { - gl.x_off = glyph_pos[i].x_offset / (64.0 / scale); + gl.x_off = (double)glyph_pos[i].x_offset / (64.0 / scale); } else { - gl.x_off = Math::round(glyph_pos[i].x_offset / (64.0 / scale)); + gl.x_off = Math::round((double)glyph_pos[i].x_offset / (64.0 / scale)); } - gl.y_off = -Math::round(glyph_pos[i].y_offset / (64.0 / scale)); + gl.y_off = -Math::round((double)glyph_pos[i].y_offset / (64.0 / scale)); } if (!last_run || i < glyph_count - 1) { // Do not add extra spacing to the last glyph of the string. @@ -5050,7 +5500,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star for (unsigned int i = 0; i < glyph_count; i++) { if ((w[i].flags & GRAPHEME_IS_VALID) == GRAPHEME_IS_VALID) { if (failed_subrun_start != p_end + 1) { - _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1); + _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1, p_start, p_end); failed_subrun_start = p_end + 1; failed_subrun_end = p_start; } @@ -5059,7 +5509,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star p_sd->ascent = MAX(p_sd->ascent, -w[i + j].y_off); p_sd->descent = MAX(p_sd->descent, w[i + j].y_off); } else { - double gla = Math::round(font_get_glyph_advance(f, fs, w[i + j].index).x * 0.5); + double gla = Math::round(_font_get_glyph_advance(f, fs, w[i + j].index).x * 0.5); p_sd->ascent = MAX(p_sd->ascent, gla); p_sd->descent = MAX(p_sd->descent, gla); } @@ -5080,16 +5530,16 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star } memfree(w); if (failed_subrun_start != p_end + 1) { - _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1); + _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1, p_start, p_end); } - p_sd->ascent = MAX(p_sd->ascent, font_get_ascent(f, fs)); - p_sd->descent = MAX(p_sd->descent, font_get_descent(f, fs)); - p_sd->upos = MAX(p_sd->upos, font_get_underline_position(f, fs)); - p_sd->uthk = MAX(p_sd->uthk, font_get_underline_thickness(f, fs)); + p_sd->ascent = MAX(p_sd->ascent, _font_get_ascent(f, fs)); + p_sd->descent = MAX(p_sd->descent, _font_get_descent(f, fs)); + p_sd->upos = MAX(p_sd->upos, _font_get_underline_position(f, fs)); + p_sd->uthk = MAX(p_sd->uthk, _font_get_underline_thickness(f, fs)); } } -bool TextServerAdvanced::shaped_text_shape(const RID &p_shaped) { +bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); @@ -5100,7 +5550,7 @@ bool TextServerAdvanced::shaped_text_shape(const RID &p_shaped) { invalidate(sd, false); if (sd->parent != RID()) { - shaped_text_shape(sd->parent); + _shaped_text_shape(sd->parent); ShapedTextDataAdvanced *parent_sd = shaped_owner.get_or_null(sd->parent); ERR_FAIL_COND_V(!parent_sd->valid, false); ERR_FAIL_COND_V(!_shape_substr(sd, parent_sd, sd->start, sd->end - sd->start), false); @@ -5200,7 +5650,7 @@ bool TextServerAdvanced::shaped_text_shape(const RID &p_shaped) { int32_t script_run_end = MIN(sd->script_iter->script_ranges[j].end, bidi_run_end); char scr_buffer[5] = { 0, 0, 0, 0, 0 }; hb_tag_to_string(hb_script_to_iso15924_tag(sd->script_iter->script_ranges[j].script), scr_buffer); - String script = String(scr_buffer); + String script_code = String(scr_buffer); int spn_from = (is_rtl) ? 0 : sd->spans.size() - 1; int spn_to = (is_rtl) ? sd->spans.size() : -1; @@ -5236,9 +5686,12 @@ bool TextServerAdvanced::shaped_text_shape(const RID &p_shaped) { Array fonts_scr_only; Array fonts_no_match; int font_count = span.fonts.size(); - for (int l = 0; l < font_count; l++) { - if (font_is_script_supported(span.fonts[l], script)) { - if (font_is_language_supported(span.fonts[l], span.language)) { + if (font_count > 0) { + fonts.push_back(sd->spans[k].fonts[0]); + } + for (int l = 1; l < font_count; l++) { + if (_font_is_script_supported(span.fonts[l], script_code)) { + if (_font_is_language_supported(span.fonts[l], span.language)) { fonts.push_back(sd->spans[k].fonts[l]); } else { fonts_scr_only.push_back(sd->spans[k].fonts[l]); @@ -5249,7 +5702,7 @@ bool TextServerAdvanced::shaped_text_shape(const RID &p_shaped) { } fonts.append_array(fonts_scr_only); fonts.append_array(fonts_no_match); - _shape_run(sd, MAX(sd->spans[k].start - sd->start, script_run_start), MIN(sd->spans[k].end - sd->start, script_run_end), sd->script_iter->script_ranges[j].script, bidi_run_direction, fonts, k, 0); + _shape_run(sd, MAX(sd->spans[k].start - sd->start, script_run_start), MIN(sd->spans[k].end - sd->start, script_run_end), sd->script_iter->script_ranges[j].script, bidi_run_direction, fonts, k, 0, 0, 0); } } } @@ -5262,7 +5715,7 @@ bool TextServerAdvanced::shaped_text_shape(const RID &p_shaped) { return sd->valid; } -bool TextServerAdvanced::shaped_text_is_ready(const RID &p_shaped) const { +bool TextServerAdvanced::_shaped_text_is_ready(const RID &p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); @@ -5270,35 +5723,35 @@ bool TextServerAdvanced::shaped_text_is_ready(const RID &p_shaped) const { return sd->valid; } -const Glyph *TextServerAdvanced::shaped_text_get_glyphs(const RID &p_shaped) const { +const Glyph *TextServerAdvanced::_shaped_text_get_glyphs(const RID &p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, nullptr); MutexLock lock(sd->mutex); if (!sd->valid) { - const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); + const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } return sd->glyphs.ptr(); } -int64_t TextServerAdvanced::shaped_text_get_glyph_count(const RID &p_shaped) const { +int64_t TextServerAdvanced::_shaped_text_get_glyph_count(const RID &p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, 0); MutexLock lock(sd->mutex); if (!sd->valid) { - const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); + const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } return sd->glyphs.size(); } -const Glyph *TextServerAdvanced::shaped_text_sort_logical(const RID &p_shaped) { +const Glyph *TextServerAdvanced::_shaped_text_sort_logical(const RID &p_shaped) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, nullptr); MutexLock lock(sd->mutex); if (!sd->valid) { - const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); + const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } if (!sd->sort_valid) { @@ -5310,7 +5763,7 @@ const Glyph *TextServerAdvanced::shaped_text_sort_logical(const RID &p_shaped) { return sd->glyphs_logical.ptr(); } -Vector2i TextServerAdvanced::shaped_text_get_range(const RID &p_shaped) const { +Vector2i TextServerAdvanced::_shaped_text_get_range(const RID &p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, Vector2i()); @@ -5318,7 +5771,7 @@ Vector2i TextServerAdvanced::shaped_text_get_range(const RID &p_shaped) const { return Vector2(sd->start, sd->end); } -Array TextServerAdvanced::shaped_text_get_objects(const RID &p_shaped) const { +Array TextServerAdvanced::_shaped_text_get_objects(const RID &p_shaped) const { Array ret; const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, ret); @@ -5331,25 +5784,25 @@ Array TextServerAdvanced::shaped_text_get_objects(const RID &p_shaped) const { return ret; } -Rect2 TextServerAdvanced::shaped_text_get_object_rect(const RID &p_shaped, const Variant &p_key) const { +Rect2 TextServerAdvanced::_shaped_text_get_object_rect(const RID &p_shaped, const Variant &p_key) const { const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, Rect2()); MutexLock lock(sd->mutex); ERR_FAIL_COND_V(!sd->objects.has(p_key), Rect2()); if (!sd->valid) { - const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); + const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } return sd->objects[p_key].rect; } -Size2 TextServerAdvanced::shaped_text_get_size(const RID &p_shaped) const { +Size2 TextServerAdvanced::_shaped_text_get_size(const RID &p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, Size2()); MutexLock lock(sd->mutex); if (!sd->valid) { - const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); + const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } if (sd->orientation == TextServer::ORIENTATION_HORIZONTAL) { return Size2((sd->text_trimmed ? sd->width_trimmed : sd->width), sd->ascent + sd->descent + sd->extra_spacing[SPACING_TOP] + sd->extra_spacing[SPACING_BOTTOM]).ceil(); @@ -5358,58 +5811,58 @@ Size2 TextServerAdvanced::shaped_text_get_size(const RID &p_shaped) const { } } -double TextServerAdvanced::shaped_text_get_ascent(const RID &p_shaped) const { +double TextServerAdvanced::_shaped_text_get_ascent(const RID &p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, 0.0); MutexLock lock(sd->mutex); if (!sd->valid) { - const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); + const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } return sd->ascent + sd->extra_spacing[SPACING_TOP]; } -double TextServerAdvanced::shaped_text_get_descent(const RID &p_shaped) const { +double TextServerAdvanced::_shaped_text_get_descent(const RID &p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, 0.0); MutexLock lock(sd->mutex); if (!sd->valid) { - const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); + const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } return sd->descent + sd->extra_spacing[SPACING_BOTTOM]; } -double TextServerAdvanced::shaped_text_get_width(const RID &p_shaped) const { +double TextServerAdvanced::_shaped_text_get_width(const RID &p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, 0.0); MutexLock lock(sd->mutex); if (!sd->valid) { - const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); + const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } return Math::ceil(sd->text_trimmed ? sd->width_trimmed : sd->width); } -double TextServerAdvanced::shaped_text_get_underline_position(const RID &p_shaped) const { +double TextServerAdvanced::_shaped_text_get_underline_position(const RID &p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, 0.0); MutexLock lock(sd->mutex); if (!sd->valid) { - const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); + const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } return sd->upos; } -double TextServerAdvanced::shaped_text_get_underline_thickness(const RID &p_shaped) const { +double TextServerAdvanced::_shaped_text_get_underline_thickness(const RID &p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, 0.0); MutexLock lock(sd->mutex); if (!sd->valid) { - const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); + const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); } return sd->uthk; @@ -5590,7 +6043,7 @@ void TextServerAdvanced::_insert_num_systems_lang() { } } -String TextServerAdvanced::format_number(const String &p_string, const String &p_language) const { +String TextServerAdvanced::_format_number(const String &p_string, const String &p_language) const { const StringName lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language; String res = p_string; @@ -5615,7 +6068,7 @@ String TextServerAdvanced::format_number(const String &p_string, const String &p return res; } -String TextServerAdvanced::parse_number(const String &p_string, const String &p_language) const { +String TextServerAdvanced::_parse_number(const String &p_string, const String &p_language) const { const StringName lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language; String res = p_string; @@ -5643,7 +6096,7 @@ String TextServerAdvanced::parse_number(const String &p_string, const String &p_ return res; } -String TextServerAdvanced::percent_sign(const String &p_language) const { +String TextServerAdvanced::_percent_sign(const String &p_language) const { const StringName lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language; for (int i = 0; i < num_systems.size(); i++) { @@ -5657,9 +6110,9 @@ String TextServerAdvanced::percent_sign(const String &p_language) const { return "%"; } -int TextServerAdvanced::is_confusable(const String &p_string, const PackedStringArray &p_dict) const { +int64_t TextServerAdvanced::_is_confusable(const String &p_string, const PackedStringArray &p_dict) const { UErrorCode status = U_ZERO_ERROR; - int match_index = -1; + int64_t match_index = -1; Char16String utf16 = p_string.utf16(); Vector<UChar *> skeletons; @@ -5697,7 +6150,7 @@ int TextServerAdvanced::is_confusable(const String &p_string, const PackedString return match_index; } -bool TextServerAdvanced::spoof_check(const String &p_string) const { +bool TextServerAdvanced::_spoof_check(const String &p_string) const { UErrorCode status = U_ZERO_ERROR; Char16String utf16 = p_string.utf16(); @@ -5719,7 +6172,7 @@ bool TextServerAdvanced::spoof_check(const String &p_string) const { return (bitmask != 0); } -String TextServerAdvanced::strip_diacritics(const String &p_string) const { +String TextServerAdvanced::_strip_diacritics(const String &p_string) const { UErrorCode err = U_ZERO_ERROR; // Get NFKD normalizer singleton. @@ -5746,13 +6199,20 @@ String TextServerAdvanced::strip_diacritics(const String &p_string) const { String result; for (int i = 0; i < normalized_string.length(); i++) { if (u_getCombiningClass(normalized_string[i]) == 0) { +#ifdef GDEXTENSION + result = result + String::chr(normalized_string[i]); +#else result = result + normalized_string[i]; +#endif } } return result; } -String TextServerAdvanced::string_to_upper(const String &p_string, const String &p_language) const { +String TextServerAdvanced::_string_to_upper(const String &p_string, const String &p_language) const { + if (p_string.is_empty()) { + return p_string; + } const String lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language; // Convert to UTF-16. @@ -5771,7 +6231,10 @@ String TextServerAdvanced::string_to_upper(const String &p_string, const String return String::utf16(upper.ptr(), len); } -String TextServerAdvanced::string_to_lower(const String &p_string, const String &p_language) const { +String TextServerAdvanced::_string_to_lower(const String &p_string, const String &p_language) const { + if (p_string.is_empty()) { + return p_string; + } const String lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language; // Convert to UTF-16. Char16String utf16 = p_string.utf16(); @@ -5789,7 +6252,7 @@ String TextServerAdvanced::string_to_lower(const String &p_string, const String return String::utf16(lower.ptr(), len); } -PackedInt32Array TextServerAdvanced::string_get_word_breaks(const String &p_string, const String &p_language) const { +PackedInt32Array TextServerAdvanced::_string_get_word_breaks(const String &p_string, const String &p_language, int p_chars_per_line) const { const String lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language; // Convert to UTF-16. Char16String utf16 = p_string.utf16(); @@ -5797,15 +6260,7 @@ PackedInt32Array TextServerAdvanced::string_get_word_breaks(const String &p_stri HashSet<int> breaks; UErrorCode err = U_ZERO_ERROR; UBreakIterator *bi = ubrk_open(UBRK_LINE, lang.ascii().get_data(), (const UChar *)utf16.get_data(), utf16.length(), &err); - if (U_FAILURE(err)) { - // No data loaded - use fallback. - for (int i = 0; i < p_string.length(); i++) { - char32_t c = p_string[i]; - if (is_whitespace(c) || is_linebreak(c)) { - breaks.insert(i); - } - } - } else { + if (U_SUCCESS(err)) { while (ubrk_next(bi) != UBRK_DONE) { int pos = _convert_pos(p_string, utf16, ubrk_current(bi)) - 1; if (pos != p_string.length() - 1) { @@ -5816,28 +6271,84 @@ PackedInt32Array TextServerAdvanced::string_get_word_breaks(const String &p_stri ubrk_close(bi); PackedInt32Array ret; + + int line_start = 0; + int line_end = 0; // End of last word on current line. + int word_start = 0; // -1 if no word encountered. Leading spaces are part of a word. + int word_length = 0; + for (int i = 0; i < p_string.length(); i++) { - char32_t c = p_string[i]; - if (c == 0xfffc) { - continue; - } - if (u_ispunct(c) && c != 0x005F) { - ret.push_back(i); - continue; - } - if (is_underscore(c)) { - ret.push_back(i); - continue; - } - if (breaks.has(i)) { + const char32_t c = p_string[i]; + + if (is_linebreak(c)) { + // Force newline. + ret.push_back(line_start); ret.push_back(i); + line_start = i + 1; + line_end = line_start; + word_start = line_start; + word_length = 0; + } else if (c == 0xfffc) { continue; + } else if ((u_ispunct(c) && c != 0x005F) || is_underscore(c) || c == '\t' || is_whitespace(c)) { + // A whitespace ends current word. + if (word_length > 0) { + line_end = i - 1; + word_start = -1; + word_length = 0; + } + } else if (breaks.has(i)) { + // End current word, no space. + if (word_length > 0) { + line_end = i; + word_start = i + 1; + word_length = 0; + } + if (p_chars_per_line <= 0) { + ret.push_back(line_start); + ret.push_back(line_end + 1); + line_start = word_start; + line_end = line_start; + } + } else { + if (word_start == -1) { + word_start = i; + if (p_chars_per_line <= 0) { + ret.push_back(line_start); + ret.push_back(line_end + 1); + line_start = word_start; + line_end = line_start; + } + } + word_length += 1; + + if (p_chars_per_line > 0) { + if (word_length > p_chars_per_line) { + // Word too long: wrap before current character. + ret.push_back(line_start); + ret.push_back(i); + line_start = i; + line_end = i; + word_start = i; + word_length = 1; + } else if (i - line_start + 1 > p_chars_per_line) { + // Line too long: wrap after the last word. + ret.push_back(line_start); + ret.push_back(line_end + 1); + line_start = word_start; + line_end = line_start; + } + } } } + if (line_start < p_string.length()) { + ret.push_back(line_start); + ret.push_back(p_string.length()); + } return ret; } -bool TextServerAdvanced::is_valid_identifier(const String &p_string) const { +bool TextServerAdvanced::_is_valid_identifier(const String &p_string) const { enum UAX31SequenceStatus { SEQ_NOT_STARTED, SEQ_STARTED, @@ -5858,7 +6369,7 @@ bool TextServerAdvanced::is_valid_identifier(const String &p_string) const { if (U_FAILURE(err)) { return false; // Failed to load normalizer. } - bool isnurom = unorm2_isNormalized(norm_c, utf16.ptr(), utf16.length(), &err); + bool isnurom = unorm2_isNormalized(norm_c, utf16.get_data(), utf16.length(), &err); if (U_FAILURE(err) || !isnurom) { return false; // Do not conform to Normalization Form C. } @@ -6028,6 +6539,17 @@ TextServerAdvanced::TextServerAdvanced() { _bmp_create_font_funcs(); } +void TextServerAdvanced::_cleanup() { + for (const KeyValue<SystemFontKey, SystemFontCache> &E : system_fonts) { + const Vector<SystemFontCacheRec> &sysf_cache = E.value.var; + for (const SystemFontCacheRec &F : sysf_cache) { + _free_rid(F.rid); + } + } + system_fonts.clear(); + system_font_data.clear(); +} + TextServerAdvanced::~TextServerAdvanced() { _bmp_free_font_funcs(); #ifdef MODULE_FREETYPE_ENABLED diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index 7ae329d616..e8a3a10ab8 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* text_server_adv.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* text_server_adv.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef TEXT_SERVER_ADV_H #define TEXT_SERVER_ADV_H @@ -42,6 +42,7 @@ #include <godot_cpp/godot.hpp> #include <godot_cpp/core/class_db.hpp> +#include <godot_cpp/core/ext_wrappers.gen.inc> #include <godot_cpp/core/mutex_lock.hpp> #include <godot_cpp/variant/array.hpp> @@ -52,6 +53,7 @@ #include <godot_cpp/variant/rect2.hpp> #include <godot_cpp/variant/rid.hpp> #include <godot_cpp/variant/string.hpp> +#include <godot_cpp/variant/typed_array.hpp> #include <godot_cpp/variant/vector2.hpp> #include <godot_cpp/variant/vector2i.hpp> @@ -78,13 +80,15 @@ using namespace godot; #else // Headers for building as built-in module. +#include "servers/text/text_server_extension.h" + +#include "core/extension/ext_wrappers.gen.inc" #include "core/object/worker_thread_pool.h" #include "core/templates/hash_map.h" #include "core/templates/rid_owner.h" #include "scene/resources/texture.h" -#include "servers/text/text_server_extension.h" -#include "modules/modules_enabled.gen.h" // For freetype, msdfgen. +#include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg. #endif @@ -113,6 +117,7 @@ using namespace godot; #include FT_ADVANCES_H #include FT_MULTIPLE_MASTERS_H #include FT_BBOX_H +#include FT_MODULE_H #include FT_CONFIG_OPTIONS_H #if !defined(FT_CONFIG_OPTION_USE_BROTLI) && !defined(_MSC_VER) #warning FreeType is configured without Brotli support, built-in fonts will not be available. @@ -164,20 +169,86 @@ class TextServerAdvanced : public TextServerExtension { const int rect_range = 1; - struct FontTexture { + struct FontTexturePosition { + int32_t index = -1; + int32_t x = 0; + int32_t y = 0; + + FontTexturePosition() {} + FontTexturePosition(int32_t p_id, int32_t p_x, int32_t p_y) : + index(p_id), x(p_x), y(p_y) {} + }; + + struct Shelf { + int32_t x = 0; + int32_t y = 0; + int32_t w = 0; + int32_t h = 0; + + FontTexturePosition alloc_shelf(int32_t p_id, int32_t p_w, int32_t p_h) { + if (p_w > w || p_h > h) { + return FontTexturePosition(-1, 0, 0); + } + int32_t xx = x; + x += p_w; + w -= p_w; + return FontTexturePosition(p_id, xx, y); + } + + Shelf() {} + Shelf(int32_t p_x, int32_t p_y, int32_t p_w, int32_t p_h) : + x(p_x), y(p_y), w(p_w), h(p_h) {} + }; + + struct ShelfPackTexture { + int32_t texture_w = 1024; + int32_t texture_h = 1024; + Image::Format format; PackedByteArray imgdata; - int texture_w = 0; - int texture_h = 0; - PackedInt32Array offsets; Ref<ImageTexture> texture; bool dirty = true; - }; - struct FontTexturePosition { - int index = 0; - int x = 0; - int y = 0; + List<Shelf> shelves; + + FontTexturePosition pack_rect(int32_t p_id, int32_t p_h, int32_t p_w) { + int32_t y = 0; + int32_t waste = 0; + Shelf *best_shelf = nullptr; + int32_t best_waste = std::numeric_limits<std::int32_t>::max(); + + for (Shelf &E : shelves) { + y += E.h; + if (p_w > E.w) { + continue; + } + if (p_h == E.h) { + return E.alloc_shelf(p_id, p_w, p_h); + } + if (p_h > E.h) { + continue; + } + if (p_h < E.h) { + waste = (E.h - p_h) * p_w; + if (waste < best_waste) { + best_waste = waste; + best_shelf = &E; + } + } + } + if (best_shelf) { + return best_shelf->alloc_shelf(p_id, p_w, p_h); + } + if (p_h <= (texture_h - y) && p_w <= texture_w) { + List<Shelf>::Element *E = shelves.push_back(Shelf(0, y, texture_w, p_h)); + return E->get().alloc_shelf(p_id, p_w, p_h); + } + return FontTexturePosition(-1, 0, 0); + } + + ShelfPackTexture() {} + ShelfPackTexture(int32_t p_w, int32_t p_h) : + texture_w(p_w), texture_h(p_h) {} }; struct FontGlyph { @@ -198,7 +269,7 @@ class TextServerAdvanced : public TextServerExtension { Vector2i size; - Vector<FontTexture> textures; + Vector<ShelfPackTexture> textures; HashMap<int32_t, FontGlyph> glyph_map; HashMap<Vector2i, Vector2> kerning_map; hb_font_t *hb_handle = nullptr; @@ -223,12 +294,13 @@ class TextServerAdvanced : public TextServerExtension { struct FontAdvanced { Mutex mutex; - bool antialiased = true; + TextServer::FontAntialiasing antialiasing = TextServer::FONT_ANTIALIASING_GRAY; bool mipmaps = false; bool msdf = false; int msdf_range = 14; int msdf_source_size = 48; int fixed_size = 0; + bool allow_system_fallback = true; bool force_autohinter = false; TextServer::Hinting hinting = TextServer::HINTING_LIGHT; TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO; @@ -240,6 +312,8 @@ class TextServerAdvanced : public TextServerExtension { BitField<TextServer::FontStyle> style_flags = 0; String font_name; String style_name; + int weight = 400; + int stretch = 100; HashMap<Vector2i, FontForSizeAdvanced *, VariantHasher, VariantComparator> cache; @@ -271,7 +345,7 @@ class TextServerAdvanced : public TextServerExtension { _FORCE_INLINE_ FontGlyph rasterize_msdf(FontAdvanced *p_font_data, FontForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const; #endif #ifdef MODULE_FREETYPE_ENABLED - _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const; + _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance, bool p_bgra) const; #endif _FORCE_INLINE_ bool _ensure_glyph(FontAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const; _FORCE_INLINE_ bool _ensure_cache_for_size(FontAdvanced *p_font_data, const Vector2i &p_size) const; @@ -301,6 +375,57 @@ class TextServerAdvanced : public TextServerExtension { _FORCE_INLINE_ double _get_extra_advance(RID p_font_rid, int p_font_size) const; _FORCE_INLINE_ Variant::Type _get_tag_type(int64_t p_tag) const; _FORCE_INLINE_ bool _get_tag_hidden(int64_t p_tag) const; + _FORCE_INLINE_ int _font_get_weight_by_name(const String &p_sty_name) const { + String sty_name = p_sty_name.replace(" ", "").replace("-", ""); + if (sty_name.find("thin") >= 0 || sty_name.find("hairline") >= 0) { + return 100; + } else if (sty_name.find("extralight") >= 0 || sty_name.find("ultralight") >= 0) { + return 200; + } else if (sty_name.find("light") >= 0) { + return 300; + } else if (sty_name.find("semilight") >= 0) { + return 350; + } else if (sty_name.find("regular") >= 0) { + return 400; + } else if (sty_name.find("medium") >= 0) { + return 500; + } else if (sty_name.find("semibold") >= 0 || sty_name.find("demibold") >= 0) { + return 600; + } else if (sty_name.find("bold") >= 0) { + return 700; + } else if (sty_name.find("extrabold") >= 0 || sty_name.find("ultrabold") >= 0) { + return 800; + } else if (sty_name.find("black") >= 0 || sty_name.find("heavy") >= 0) { + return 900; + } else if (sty_name.find("extrablack") >= 0 || sty_name.find("ultrablack") >= 0) { + return 950; + } + return 400; + } + _FORCE_INLINE_ int _font_get_stretch_by_name(const String &p_sty_name) const { + String sty_name = p_sty_name.replace(" ", "").replace("-", ""); + if (sty_name.find("ultracondensed") >= 0) { + return 50; + } else if (sty_name.find("extracondensed") >= 0) { + return 63; + } else if (sty_name.find("condensed") >= 0) { + return 75; + } else if (sty_name.find("semicondensed") >= 0) { + return 87; + } else if (sty_name.find("semiexpanded") >= 0) { + return 113; + } else if (sty_name.find("expanded") >= 0) { + return 125; + } else if (sty_name.find("extraexpanded") >= 0) { + return 150; + } else if (sty_name.find("ultraexpanded") >= 0) { + return 200; + } + return 100; + } + _FORCE_INLINE_ bool _is_ital_style(const String &p_sty_name) const { + return (p_sty_name.find("italic") >= 0) || (p_sty_name.find("oblique") >= 0); + } // Shaped text cache data. struct TrimData { @@ -342,6 +467,7 @@ class TextServerAdvanced : public TextServerExtension { int pos = 0; InlineAlignment inline_align = INLINE_ALIGNMENT_CENTER; Rect2 rect; + float baseline = 0; }; HashMap<Variant, EmbeddedObject, VariantHasher, VariantComparator> objects; @@ -380,6 +506,7 @@ class TextServerAdvanced : public TextServerExtension { HashMap<int, bool> jstops; HashMap<int, bool> breaks; + int break_inserts = 0; bool break_ops_valid = false; bool js_ops_valid = false; @@ -402,12 +529,87 @@ class TextServerAdvanced : public TextServerExtension { mutable RID_PtrOwner<FontAdvanced> font_owner; mutable RID_PtrOwner<ShapedTextDataAdvanced> shaped_owner; + struct SystemFontKey { + String font_name; + TextServer::FontAntialiasing antialiasing = TextServer::FONT_ANTIALIASING_GRAY; + bool italic = false; + bool mipmaps = false; + bool msdf = false; + bool force_autohinter = false; + int weight = 400; + int stretch = 100; + int msdf_range = 14; + int msdf_source_size = 48; + int fixed_size = 0; + TextServer::Hinting hinting = TextServer::HINTING_LIGHT; + TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO; + Dictionary variation_coordinates; + double oversampling = 0.0; + double embolden = 0.0; + Transform2D transform; + + bool operator==(const SystemFontKey &p_b) const { + return (font_name == p_b.font_name) && (antialiasing == p_b.antialiasing) && (italic == p_b.italic) && (mipmaps == p_b.mipmaps) && (msdf == p_b.msdf) && (force_autohinter == p_b.force_autohinter) && (weight == p_b.weight) && (stretch == p_b.stretch) && (msdf_range == p_b.msdf_range) && (msdf_source_size == p_b.msdf_source_size) && (fixed_size == p_b.fixed_size) && (hinting == p_b.hinting) && (subpixel_positioning == p_b.subpixel_positioning) && (variation_coordinates == p_b.variation_coordinates) && (oversampling == p_b.oversampling) && (embolden == p_b.embolden) && (transform == p_b.transform); + } + + SystemFontKey(const String &p_font_name, bool p_italic, int p_weight, int p_stretch, RID p_font, const TextServerAdvanced *p_fb) { + font_name = p_font_name; + italic = p_italic; + weight = p_weight; + stretch = p_stretch; + antialiasing = p_fb->_font_get_antialiasing(p_font); + mipmaps = p_fb->_font_get_generate_mipmaps(p_font); + msdf = p_fb->_font_is_multichannel_signed_distance_field(p_font); + msdf_range = p_fb->_font_get_msdf_pixel_range(p_font); + msdf_source_size = p_fb->_font_get_msdf_size(p_font); + fixed_size = p_fb->_font_get_fixed_size(p_font); + force_autohinter = p_fb->_font_is_force_autohinter(p_font); + hinting = p_fb->_font_get_hinting(p_font); + subpixel_positioning = p_fb->_font_get_subpixel_positioning(p_font); + variation_coordinates = p_fb->_font_get_variation_coordinates(p_font); + oversampling = p_fb->_font_get_oversampling(p_font); + embolden = p_fb->_font_get_embolden(p_font); + transform = p_fb->_font_get_transform(p_font); + } + }; + + struct SystemFontCacheRec { + RID rid; + int index = 0; + }; + + struct SystemFontCache { + Vector<SystemFontCacheRec> var; + int max_var = 0; + }; + + struct SystemFontKeyHasher { + _FORCE_INLINE_ static uint32_t hash(const SystemFontKey &p_a) { + uint32_t hash = p_a.font_name.hash(); + hash = hash_murmur3_one_32(p_a.variation_coordinates.hash(), hash); + hash = hash_murmur3_one_32(p_a.weight, hash); + hash = hash_murmur3_one_32(p_a.stretch, hash); + hash = hash_murmur3_one_32(p_a.msdf_range, hash); + hash = hash_murmur3_one_32(p_a.msdf_source_size, hash); + hash = hash_murmur3_one_32(p_a.fixed_size, hash); + hash = hash_murmur3_one_double(p_a.oversampling, hash); + hash = hash_murmur3_one_double(p_a.embolden, hash); + hash = hash_murmur3_one_real(p_a.transform[0].x, hash); + hash = hash_murmur3_one_real(p_a.transform[0].y, hash); + hash = hash_murmur3_one_real(p_a.transform[1].x, hash); + hash = hash_murmur3_one_real(p_a.transform[1].y, hash); + return hash_fmix32(hash_murmur3_one_32(((int)p_a.mipmaps) | ((int)p_a.msdf << 1) | ((int)p_a.italic << 2) | ((int)p_a.force_autohinter << 3) | ((int)p_a.hinting << 4) | ((int)p_a.subpixel_positioning << 8) | ((int)p_a.antialiasing << 12), hash)); + } + }; + mutable HashMap<SystemFontKey, SystemFontCache, SystemFontKeyHasher> system_fonts; + mutable HashMap<String, PackedByteArray> system_font_data; + void _realign(ShapedTextDataAdvanced *p_sd) const; int64_t _convert_pos(const String &p_utf32, const Char16String &p_utf16, int64_t p_pos) const; int64_t _convert_pos(const ShapedTextDataAdvanced *p_sd, int64_t p_pos) const; int64_t _convert_pos_inv(const ShapedTextDataAdvanced *p_sd, int64_t p_pos) const; bool _shape_substr(ShapedTextDataAdvanced *p_new_sd, const ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_length) const; - void _shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, Array p_fonts, int64_t p_span, int64_t p_fb_index); + void _shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, TypedArray<RID> p_fonts, int64_t p_span, int64_t p_fb_index, int64_t p_prev_start, int64_t p_prev_end); Glyph _shape_single_glyph(ShapedTextDataAdvanced *p_sd, char32_t p_char, hb_script_t p_script, hb_direction_t p_direction, const RID &p_font, int64_t p_font_size); _FORCE_INLINE_ void _add_featuers(const Dictionary &p_source, Vector<hb_feature_t> &r_ftrs); @@ -461,258 +663,270 @@ protected: void invalidate(ShapedTextDataAdvanced *p_shaped, bool p_text = false); public: - virtual bool has_feature(Feature p_feature) const override; - virtual String get_name() const override; - virtual int64_t get_features() const override; + MODBIND1RC(bool, has_feature, Feature); + MODBIND0RC(String, get_name); + MODBIND0RC(int64_t, get_features); - virtual void free_rid(const RID &p_rid) override; - virtual bool has(const RID &p_rid) override; - virtual bool load_support_data(const String &p_filename) override; + MODBIND1(free_rid, const RID &); + MODBIND1R(bool, has, const RID &); + MODBIND1R(bool, load_support_data, const String &); - virtual String get_support_data_filename() const override; - virtual String get_support_data_info() const override; - virtual bool save_support_data(const String &p_filename) const override; + MODBIND0RC(String, get_support_data_filename); + MODBIND0RC(String, get_support_data_info); + MODBIND1RC(bool, save_support_data, const String &); - virtual bool is_locale_right_to_left(const String &p_locale) const override; + MODBIND1RC(bool, is_locale_right_to_left, const String &); - virtual int64_t name_to_tag(const String &p_name) const override; - virtual String tag_to_name(int64_t p_tag) const override; + MODBIND1RC(int64_t, name_to_tag, const String &); + MODBIND1RC(String, tag_to_name, int64_t); /* Font interface */ - virtual RID create_font() override; - virtual void font_set_data(const RID &p_font_rid, const PackedByteArray &p_data) override; - virtual void font_set_data_ptr(const RID &p_font_rid, const uint8_t *p_data_ptr, int64_t p_data_size) override; + MODBIND0R(RID, create_font); + + MODBIND2(font_set_data, const RID &, const PackedByteArray &); + MODBIND3(font_set_data_ptr, const RID &, const uint8_t *, int64_t); - virtual void font_set_face_index(const RID &p_font_rid, int64_t p_index) override; - virtual int64_t font_get_face_index(const RID &p_font_rid) const override; + MODBIND2(font_set_face_index, const RID &, int64_t); + MODBIND1RC(int64_t, font_get_face_index, const RID &); - virtual int64_t font_get_face_count(const RID &p_font_rid) const override; + MODBIND1RC(int64_t, font_get_face_count, const RID &); - virtual void font_set_style(const RID &p_font_rid, BitField<FontStyle> p_style) override; - virtual BitField<FontStyle> font_get_style(const RID &p_font_rid) const override; + MODBIND2(font_set_style, const RID &, BitField<FontStyle>); + MODBIND1RC(BitField<FontStyle>, font_get_style, const RID &); - virtual void font_set_style_name(const RID &p_font_rid, const String &p_name) override; - virtual String font_get_style_name(const RID &p_font_rid) const override; + MODBIND2(font_set_style_name, const RID &, const String &); + MODBIND1RC(String, font_get_style_name, const RID &); - virtual void font_set_name(const RID &p_font_rid, const String &p_name) override; - virtual String font_get_name(const RID &p_font_rid) const override; + MODBIND2(font_set_weight, const RID &, int64_t); + MODBIND1RC(int64_t, font_get_weight, const RID &); - virtual void font_set_antialiased(const RID &p_font_rid, bool p_antialiased) override; - virtual bool font_is_antialiased(const RID &p_font_rid) const override; + MODBIND2(font_set_stretch, const RID &, int64_t); + MODBIND1RC(int64_t, font_get_stretch, const RID &); - virtual void font_set_generate_mipmaps(const RID &p_font_rid, bool p_generate_mipmaps) override; - virtual bool font_get_generate_mipmaps(const RID &p_font_rid) const override; + MODBIND2(font_set_name, const RID &, const String &); + MODBIND1RC(String, font_get_name, const RID &); - virtual void font_set_multichannel_signed_distance_field(const RID &p_font_rid, bool p_msdf) override; - virtual bool font_is_multichannel_signed_distance_field(const RID &p_font_rid) const override; + MODBIND2(font_set_antialiasing, const RID &, TextServer::FontAntialiasing); + MODBIND1RC(TextServer::FontAntialiasing, font_get_antialiasing, const RID &); - virtual void font_set_msdf_pixel_range(const RID &p_font_rid, int64_t p_msdf_pixel_range) override; - virtual int64_t font_get_msdf_pixel_range(const RID &p_font_rid) const override; + MODBIND2(font_set_generate_mipmaps, const RID &, bool); + MODBIND1RC(bool, font_get_generate_mipmaps, const RID &); - virtual void font_set_msdf_size(const RID &p_font_rid, int64_t p_msdf_size) override; - virtual int64_t font_get_msdf_size(const RID &p_font_rid) const override; + MODBIND2(font_set_multichannel_signed_distance_field, const RID &, bool); + MODBIND1RC(bool, font_is_multichannel_signed_distance_field, const RID &); - virtual void font_set_fixed_size(const RID &p_font_rid, int64_t p_fixed_size) override; - virtual int64_t font_get_fixed_size(const RID &p_font_rid) const override; + MODBIND2(font_set_msdf_pixel_range, const RID &, int64_t); + MODBIND1RC(int64_t, font_get_msdf_pixel_range, const RID &); - virtual void font_set_force_autohinter(const RID &p_font_rid, bool p_force_autohinter) override; - virtual bool font_is_force_autohinter(const RID &p_font_rid) const override; + MODBIND2(font_set_msdf_size, const RID &, int64_t); + MODBIND1RC(int64_t, font_get_msdf_size, const RID &); - virtual void font_set_subpixel_positioning(const RID &p_font_rid, SubpixelPositioning p_subpixel) override; - virtual SubpixelPositioning font_get_subpixel_positioning(const RID &p_font_rid) const override; + MODBIND2(font_set_fixed_size, const RID &, int64_t); + MODBIND1RC(int64_t, font_get_fixed_size, const RID &); - virtual void font_set_embolden(const RID &p_font_rid, double p_strength) override; - virtual double font_get_embolden(const RID &p_font_rid) const override; + MODBIND2(font_set_allow_system_fallback, const RID &, bool); + MODBIND1RC(bool, font_is_allow_system_fallback, const RID &); - virtual void font_set_transform(const RID &p_font_rid, const Transform2D &p_transform) override; - virtual Transform2D font_get_transform(const RID &p_font_rid) const override; + MODBIND2(font_set_force_autohinter, const RID &, bool); + MODBIND1RC(bool, font_is_force_autohinter, const RID &); - virtual void font_set_variation_coordinates(const RID &p_font_rid, const Dictionary &p_variation_coordinates) override; - virtual Dictionary font_get_variation_coordinates(const RID &p_font_rid) const override; + MODBIND2(font_set_subpixel_positioning, const RID &, SubpixelPositioning); + MODBIND1RC(SubpixelPositioning, font_get_subpixel_positioning, const RID &); - virtual void font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) override; - virtual TextServer::Hinting font_get_hinting(const RID &p_font_rid) const override; + MODBIND2(font_set_embolden, const RID &, double); + MODBIND1RC(double, font_get_embolden, const RID &); - virtual void font_set_oversampling(const RID &p_font_rid, double p_oversampling) override; - virtual double font_get_oversampling(const RID &p_font_rid) const override; + MODBIND2(font_set_transform, const RID &, const Transform2D &); + MODBIND1RC(Transform2D, font_get_transform, const RID &); - virtual Array font_get_size_cache_list(const RID &p_font_rid) const override; - virtual void font_clear_size_cache(const RID &p_font_rid) override; - virtual void font_remove_size_cache(const RID &p_font_rid, const Vector2i &p_size) override; + MODBIND2(font_set_variation_coordinates, const RID &, const Dictionary &); + MODBIND1RC(Dictionary, font_get_variation_coordinates, const RID &); - virtual void font_set_ascent(const RID &p_font_rid, int64_t p_size, double p_ascent) override; - virtual double font_get_ascent(const RID &p_font_rid, int64_t p_size) const override; + MODBIND2(font_set_hinting, const RID &, TextServer::Hinting); + MODBIND1RC(TextServer::Hinting, font_get_hinting, const RID &); - virtual void font_set_descent(const RID &p_font_rid, int64_t p_size, double p_descent) override; - virtual double font_get_descent(const RID &p_font_rid, int64_t p_size) const override; + MODBIND2(font_set_oversampling, const RID &, double); + MODBIND1RC(double, font_get_oversampling, const RID &); - virtual void font_set_underline_position(const RID &p_font_rid, int64_t p_size, double p_underline_position) override; - virtual double font_get_underline_position(const RID &p_font_rid, int64_t p_size) const override; + MODBIND1RC(TypedArray<Vector2i>, font_get_size_cache_list, const RID &); + MODBIND1(font_clear_size_cache, const RID &); + MODBIND2(font_remove_size_cache, const RID &, const Vector2i &); - virtual void font_set_underline_thickness(const RID &p_font_rid, int64_t p_size, double p_underline_thickness) override; - virtual double font_get_underline_thickness(const RID &p_font_rid, int64_t p_size) const override; + MODBIND3(font_set_ascent, const RID &, int64_t, double); + MODBIND2RC(double, font_get_ascent, const RID &, int64_t); - virtual void font_set_scale(const RID &p_font_rid, int64_t p_size, double p_scale) override; - virtual double font_get_scale(const RID &p_font_rid, int64_t p_size) const override; + MODBIND3(font_set_descent, const RID &, int64_t, double); + MODBIND2RC(double, font_get_descent, const RID &, int64_t); - virtual int64_t font_get_texture_count(const RID &p_font_rid, const Vector2i &p_size) const override; - virtual void font_clear_textures(const RID &p_font_rid, const Vector2i &p_size) override; - virtual void font_remove_texture(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) override; + MODBIND3(font_set_underline_position, const RID &, int64_t, double); + MODBIND2RC(double, font_get_underline_position, const RID &, int64_t); - virtual void font_set_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const Ref<Image> &p_image) override; - virtual Ref<Image> font_get_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const override; + MODBIND3(font_set_underline_thickness, const RID &, int64_t, double); + MODBIND2RC(double, font_get_underline_thickness, const RID &, int64_t); - virtual void font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offset) override; - virtual PackedInt32Array font_get_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const override; + MODBIND3(font_set_scale, const RID &, int64_t, double); + MODBIND2RC(double, font_get_scale, const RID &, int64_t); - virtual Array font_get_glyph_list(const RID &p_font_rid, const Vector2i &p_size) const override; - virtual void font_clear_glyphs(const RID &p_font_rid, const Vector2i &p_size) override; - virtual void font_remove_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) override; + MODBIND2RC(int64_t, font_get_texture_count, const RID &, const Vector2i &); + MODBIND2(font_clear_textures, const RID &, const Vector2i &); + MODBIND3(font_remove_texture, const RID &, const Vector2i &, int64_t); - virtual Vector2 font_get_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph) const override; - virtual void font_set_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph, const Vector2 &p_advance) override; + MODBIND4(font_set_texture_image, const RID &, const Vector2i &, int64_t, const Ref<Image> &); + MODBIND3RC(Ref<Image>, font_get_texture_image, const RID &, const Vector2i &, int64_t); - virtual Vector2 font_get_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override; - virtual void font_set_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_offset) override; + MODBIND4(font_set_texture_offsets, const RID &, const Vector2i &, int64_t, const PackedInt32Array &); + MODBIND3RC(PackedInt32Array, font_get_texture_offsets, const RID &, const Vector2i &, int64_t); - virtual Vector2 font_get_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override; - virtual void font_set_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_gl_size) override; + MODBIND2RC(PackedInt32Array, font_get_glyph_list, const RID &, const Vector2i &); + MODBIND2(font_clear_glyphs, const RID &, const Vector2i &); + MODBIND3(font_remove_glyph, const RID &, const Vector2i &, int64_t); - virtual Rect2 font_get_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override; - virtual void font_set_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Rect2 &p_uv_rect) override; + MODBIND3RC(Vector2, font_get_glyph_advance, const RID &, int64_t, int64_t); + MODBIND4(font_set_glyph_advance, const RID &, int64_t, int64_t, const Vector2 &); - virtual int64_t font_get_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override; - virtual void font_set_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, int64_t p_texture_idx) override; + MODBIND3RC(Vector2, font_get_glyph_offset, const RID &, const Vector2i &, int64_t); + MODBIND4(font_set_glyph_offset, const RID &, const Vector2i &, int64_t, const Vector2 &); - virtual RID font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override; - virtual Size2 font_get_glyph_texture_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override; + MODBIND3RC(Vector2, font_get_glyph_size, const RID &, const Vector2i &, int64_t); + MODBIND4(font_set_glyph_size, const RID &, const Vector2i &, int64_t, const Vector2 &); - virtual Dictionary font_get_glyph_contours(const RID &p_font, int64_t p_size, int64_t p_index) const override; + MODBIND3RC(Rect2, font_get_glyph_uv_rect, const RID &, const Vector2i &, int64_t); + MODBIND4(font_set_glyph_uv_rect, const RID &, const Vector2i &, int64_t, const Rect2 &); - virtual Array font_get_kerning_list(const RID &p_font_rid, int64_t p_size) const override; - virtual void font_clear_kerning_map(const RID &p_font_rid, int64_t p_size) override; - virtual void font_remove_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) override; + MODBIND3RC(int64_t, font_get_glyph_texture_idx, const RID &, const Vector2i &, int64_t); + MODBIND4(font_set_glyph_texture_idx, const RID &, const Vector2i &, int64_t, int64_t); - virtual void font_set_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) override; - virtual Vector2 font_get_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) const override; + MODBIND3RC(RID, font_get_glyph_texture_rid, const RID &, const Vector2i &, int64_t); + MODBIND3RC(Size2, font_get_glyph_texture_size, const RID &, const Vector2i &, int64_t); - virtual int64_t font_get_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_char, int64_t p_variation_selector = 0) const override; + MODBIND3RC(Dictionary, font_get_glyph_contours, const RID &, int64_t, int64_t); - virtual bool font_has_char(const RID &p_font_rid, int64_t p_char) const override; - virtual String font_get_supported_chars(const RID &p_font_rid) const override; + MODBIND2RC(TypedArray<Vector2i>, font_get_kerning_list, const RID &, int64_t); + MODBIND2(font_clear_kerning_map, const RID &, int64_t); + MODBIND3(font_remove_kerning, const RID &, int64_t, const Vector2i &); - virtual void font_render_range(const RID &p_font, const Vector2i &p_size, int64_t p_start, int64_t p_end) override; - virtual void font_render_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_index) override; + MODBIND4(font_set_kerning, const RID &, int64_t, const Vector2i &, const Vector2 &); + MODBIND3RC(Vector2, font_get_kerning, const RID &, int64_t, const Vector2i &); - virtual void font_draw_glyph(const RID &p_font, const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color = Color(1, 1, 1)) const override; - virtual void font_draw_glyph_outline(const RID &p_font, const RID &p_canvas, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color = Color(1, 1, 1)) const override; + MODBIND4RC(int64_t, font_get_glyph_index, const RID &, int64_t, int64_t, int64_t); - virtual bool font_is_language_supported(const RID &p_font_rid, const String &p_language) const override; - virtual void font_set_language_support_override(const RID &p_font_rid, const String &p_language, bool p_supported) override; - virtual bool font_get_language_support_override(const RID &p_font_rid, const String &p_language) override; - virtual void font_remove_language_support_override(const RID &p_font_rid, const String &p_language) override; - virtual PackedStringArray font_get_language_support_overrides(const RID &p_font_rid) override; + MODBIND2RC(bool, font_has_char, const RID &, int64_t); + MODBIND1RC(String, font_get_supported_chars, const RID &); - virtual bool font_is_script_supported(const RID &p_font_rid, const String &p_script) const override; - virtual void font_set_script_support_override(const RID &p_font_rid, const String &p_script, bool p_supported) override; - virtual bool font_get_script_support_override(const RID &p_font_rid, const String &p_script) override; - virtual void font_remove_script_support_override(const RID &p_font_rid, const String &p_script) override; - virtual PackedStringArray font_get_script_support_overrides(const RID &p_font_rid) override; + MODBIND4(font_render_range, const RID &, const Vector2i &, int64_t, int64_t); + MODBIND3(font_render_glyph, const RID &, const Vector2i &, int64_t); - virtual void font_set_opentype_feature_overrides(const RID &p_font_rid, const Dictionary &p_overrides) override; - virtual Dictionary font_get_opentype_feature_overrides(const RID &p_font_rid) const override; + MODBIND6C(font_draw_glyph, const RID &, const RID &, int64_t, const Vector2 &, int64_t, const Color &); + MODBIND7C(font_draw_glyph_outline, const RID &, const RID &, int64_t, int64_t, const Vector2 &, int64_t, const Color &); - virtual Dictionary font_supported_feature_list(const RID &p_font_rid) const override; - virtual Dictionary font_supported_variation_list(const RID &p_font_rid) const override; + MODBIND2RC(bool, font_is_language_supported, const RID &, const String &); + MODBIND3(font_set_language_support_override, const RID &, const String &, bool); + MODBIND2R(bool, font_get_language_support_override, const RID &, const String &); + MODBIND2(font_remove_language_support_override, const RID &, const String &); + MODBIND1R(PackedStringArray, font_get_language_support_overrides, const RID &); - virtual double font_get_global_oversampling() const override; - virtual void font_set_global_oversampling(double p_oversampling) override; + MODBIND2RC(bool, font_is_script_supported, const RID &, const String &); + MODBIND3(font_set_script_support_override, const RID &, const String &, bool); + MODBIND2R(bool, font_get_script_support_override, const RID &, const String &); + MODBIND2(font_remove_script_support_override, const RID &, const String &); + MODBIND1R(PackedStringArray, font_get_script_support_overrides, const RID &); + + MODBIND2(font_set_opentype_feature_overrides, const RID &, const Dictionary &); + MODBIND1RC(Dictionary, font_get_opentype_feature_overrides, const RID &); + + MODBIND1RC(Dictionary, font_supported_feature_list, const RID &); + MODBIND1RC(Dictionary, font_supported_variation_list, const RID &); + + MODBIND0RC(double, font_get_global_oversampling); + MODBIND1(font_set_global_oversampling, double); /* Shaped text buffer interface */ - virtual RID create_shaped_text(Direction p_direction = DIRECTION_AUTO, Orientation p_orientation = ORIENTATION_HORIZONTAL) override; + MODBIND2R(RID, create_shaped_text, Direction, Orientation); + + MODBIND1(shaped_text_clear, const RID &); - virtual void shaped_text_clear(const RID &p_shaped) override; + MODBIND2(shaped_text_set_direction, const RID &, Direction); + MODBIND1RC(Direction, shaped_text_get_direction, const RID &); + MODBIND1RC(Direction, shaped_text_get_inferred_direction, const RID &); - virtual void shaped_text_set_direction(const RID &p_shaped, Direction p_direction = DIRECTION_AUTO) override; - virtual Direction shaped_text_get_direction(const RID &p_shaped) const override; - virtual Direction shaped_text_get_inferred_direction(const RID &p_shaped) const override; + MODBIND2(shaped_text_set_bidi_override, const RID &, const Array &); - virtual void shaped_text_set_bidi_override(const RID &p_shaped, const Array &p_override) override; + MODBIND2(shaped_text_set_custom_punctuation, const RID &, const String &); + MODBIND1RC(String, shaped_text_get_custom_punctuation, const RID &); - virtual void shaped_text_set_custom_punctuation(const RID &p_shaped, const String &p_punct) override; - virtual String shaped_text_get_custom_punctuation(const RID &p_shaped) const override; + MODBIND2(shaped_text_set_orientation, const RID &, Orientation); + MODBIND1RC(Orientation, shaped_text_get_orientation, const RID &); - virtual void shaped_text_set_orientation(const RID &p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) override; - virtual Orientation shaped_text_get_orientation(const RID &p_shaped) const override; + MODBIND2(shaped_text_set_preserve_invalid, const RID &, bool); + MODBIND1RC(bool, shaped_text_get_preserve_invalid, const RID &); - virtual void shaped_text_set_preserve_invalid(const RID &p_shaped, bool p_enabled) override; - virtual bool shaped_text_get_preserve_invalid(const RID &p_shaped) const override; + MODBIND2(shaped_text_set_preserve_control, const RID &, bool); + MODBIND1RC(bool, shaped_text_get_preserve_control, const RID &); - virtual void shaped_text_set_preserve_control(const RID &p_shaped, bool p_enabled) override; - virtual bool shaped_text_get_preserve_control(const RID &p_shaped) const override; + MODBIND3(shaped_text_set_spacing, const RID &, SpacingType, int64_t); + MODBIND2RC(int64_t, shaped_text_get_spacing, const RID &, SpacingType); - virtual void shaped_text_set_spacing(const RID &p_shaped, SpacingType p_spacing, int64_t p_value) override; - virtual int64_t shaped_text_get_spacing(const RID &p_shaped, SpacingType p_spacing) const override; + MODBIND7R(bool, shaped_text_add_string, const RID &, const String &, const TypedArray<RID> &, int64_t, const Dictionary &, const String &, const Variant &); + MODBIND6R(bool, shaped_text_add_object, const RID &, const Variant &, const Size2 &, InlineAlignment, int64_t, float); + MODBIND5R(bool, shaped_text_resize_object, const RID &, const Variant &, const Size2 &, InlineAlignment, float); - virtual bool shaped_text_add_string(const RID &p_shaped, const String &p_text, const Array &p_fonts, int64_t p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) override; - virtual bool shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int64_t p_length = 1) override; - virtual bool shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER) override; + MODBIND1RC(int64_t, shaped_get_span_count, const RID &); + MODBIND2RC(Variant, shaped_get_span_meta, const RID &, int64_t); + MODBIND5(shaped_set_span_update_font, const RID &, int64_t, const TypedArray<RID> &, int64_t, const Dictionary &); - virtual int64_t shaped_get_span_count(const RID &p_shaped) const override; - virtual Variant shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const override; - virtual void shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const Array &p_fonts, int64_t p_size, const Dictionary &p_opentype_features = Dictionary()) override; + MODBIND3RC(RID, shaped_text_substr, const RID &, int64_t, int64_t); + MODBIND1RC(RID, shaped_text_get_parent, const RID &); - virtual RID shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const override; - virtual RID shaped_text_get_parent(const RID &p_shaped) const override; + MODBIND3R(double, shaped_text_fit_to_width, const RID &, double, BitField<TextServer::JustificationFlag>); + MODBIND2R(double, shaped_text_tab_align, const RID &, const PackedFloat32Array &); - virtual double shaped_text_fit_to_width(const RID &p_shaped, double p_width, BitField<TextServer::JustificationFlag> p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override; - virtual double shaped_text_tab_align(const RID &p_shaped, const PackedFloat32Array &p_tab_stops) override; + MODBIND1R(bool, shaped_text_shape, const RID &); + MODBIND1R(bool, shaped_text_update_breaks, const RID &); + MODBIND1R(bool, shaped_text_update_justification_ops, const RID &); - virtual bool shaped_text_shape(const RID &p_shaped) override; - virtual bool shaped_text_update_breaks(const RID &p_shaped) override; - virtual bool shaped_text_update_justification_ops(const RID &p_shaped) override; + MODBIND1RC(int64_t, shaped_text_get_trim_pos, const RID &); + MODBIND1RC(int64_t, shaped_text_get_ellipsis_pos, const RID &); + MODBIND1RC(const Glyph *, shaped_text_get_ellipsis_glyphs, const RID &); + MODBIND1RC(int64_t, shaped_text_get_ellipsis_glyph_count, const RID &); - virtual int64_t shaped_text_get_trim_pos(const RID &p_shaped) const override; - virtual int64_t shaped_text_get_ellipsis_pos(const RID &p_shaped) const override; - virtual const Glyph *shaped_text_get_ellipsis_glyphs(const RID &p_shaped) const override; - virtual int64_t shaped_text_get_ellipsis_glyph_count(const RID &p_shaped) const override; + MODBIND3(shaped_text_overrun_trim_to_width, const RID &, double, BitField<TextServer::TextOverrunFlag>); - virtual void shaped_text_overrun_trim_to_width(const RID &p_shaped, double p_width, BitField<TextServer::TextOverrunFlag> p_trim_flags) override; + MODBIND1RC(bool, shaped_text_is_ready, const RID &); - virtual bool shaped_text_is_ready(const RID &p_shaped) const override; + MODBIND1RC(const Glyph *, shaped_text_get_glyphs, const RID &); + MODBIND1R(const Glyph *, shaped_text_sort_logical, const RID &); + MODBIND1RC(int64_t, shaped_text_get_glyph_count, const RID &); - virtual const Glyph *shaped_text_get_glyphs(const RID &p_shaped) const override; - virtual const Glyph *shaped_text_sort_logical(const RID &p_shaped) override; - virtual int64_t shaped_text_get_glyph_count(const RID &p_shaped) const override; + MODBIND1RC(Vector2i, shaped_text_get_range, const RID &); - virtual Vector2i shaped_text_get_range(const RID &p_shaped) const override; + MODBIND1RC(Array, shaped_text_get_objects, const RID &); + MODBIND2RC(Rect2, shaped_text_get_object_rect, const RID &, const Variant &); - virtual Array shaped_text_get_objects(const RID &p_shaped) const override; - virtual Rect2 shaped_text_get_object_rect(const RID &p_shaped, const Variant &p_key) const override; + MODBIND1RC(Size2, shaped_text_get_size, const RID &); + MODBIND1RC(double, shaped_text_get_ascent, const RID &); + MODBIND1RC(double, shaped_text_get_descent, const RID &); + MODBIND1RC(double, shaped_text_get_width, const RID &); + MODBIND1RC(double, shaped_text_get_underline_position, const RID &); + MODBIND1RC(double, shaped_text_get_underline_thickness, const RID &); - virtual Size2 shaped_text_get_size(const RID &p_shaped) const override; - virtual double shaped_text_get_ascent(const RID &p_shaped) const override; - virtual double shaped_text_get_descent(const RID &p_shaped) const override; - virtual double shaped_text_get_width(const RID &p_shaped) const override; - virtual double shaped_text_get_underline_position(const RID &p_shaped) const override; - virtual double shaped_text_get_underline_thickness(const RID &p_shaped) const override; + MODBIND2RC(String, format_number, const String &, const String &); + MODBIND2RC(String, parse_number, const String &, const String &); + MODBIND1RC(String, percent_sign, const String &); - virtual String format_number(const String &p_string, const String &p_language = "") const override; - virtual String parse_number(const String &p_string, const String &p_language = "") const override; - virtual String percent_sign(const String &p_language = "") const override; + MODBIND3RC(PackedInt32Array, string_get_word_breaks, const String &, const String &, int); - virtual PackedInt32Array string_get_word_breaks(const String &p_string, const String &p_language = "") const override; + MODBIND2RC(int64_t, is_confusable, const String &, const PackedStringArray &); + MODBIND1RC(bool, spoof_check, const String &); - virtual int is_confusable(const String &p_string, const PackedStringArray &p_dict) const override; - virtual bool spoof_check(const String &p_string) const override; + MODBIND1RC(String, strip_diacritics, const String &); + MODBIND1RC(bool, is_valid_identifier, const String &); - virtual String strip_diacritics(const String &p_string) const override; - virtual bool is_valid_identifier(const String &p_string) const override; + MODBIND2RC(String, string_to_upper, const String &, const String &); + MODBIND2RC(String, string_to_lower, const String &, const String &); - virtual String string_to_upper(const String &p_string, const String &p_language = "") const override; - virtual String string_to_lower(const String &p_string, const String &p_language = "") const override; + MODBIND0(cleanup); TextServerAdvanced(); ~TextServerAdvanced(); diff --git a/modules/text_server_adv/thorvg_bounds_iterator.cpp b/modules/text_server_adv/thorvg_bounds_iterator.cpp new file mode 100644 index 0000000000..807f356b83 --- /dev/null +++ b/modules/text_server_adv/thorvg_bounds_iterator.cpp @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* thorvg_bounds_iterator.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifdef GDEXTENSION +// Headers for building as GDExtension plug-in. + +#include <godot_cpp/godot.hpp> + +using namespace godot; + +#else +// Headers for building as built-in module. + +#include "core/typedefs.h" + +#include "modules/modules_enabled.gen.h" // For svg. +#endif + +#ifdef MODULE_SVG_ENABLED + +#include "thorvg_bounds_iterator.h" + +#include <tvgIteratorAccessor.h> +#include <tvgPaint.h> + +// This function uses private ThorVG API to get bounding box of top level children elements. + +void tvg_get_bounds(tvg::Picture *p_picture, float &r_min_x, float &r_min_y, float &r_max_x, float &r_max_y) { + tvg::IteratorAccessor itrAccessor; + if (tvg::Iterator *it = itrAccessor.iterator(p_picture)) { + while (const tvg::Paint *child = it->next()) { + float x = 0, y = 0, w = 0, h = 0; + child->bounds(&x, &y, &w, &h, true); + r_min_x = MIN(x, r_min_x); + r_min_y = MIN(y, r_min_y); + r_max_x = MAX(x + w, r_max_x); + r_max_y = MAX(y + h, r_max_y); + } + delete (it); + } +} + +#endif // MODULE_SVG_ENABLED diff --git a/modules/text_server_adv/thorvg_bounds_iterator.h b/modules/text_server_adv/thorvg_bounds_iterator.h new file mode 100644 index 0000000000..a44cbb99a7 --- /dev/null +++ b/modules/text_server_adv/thorvg_bounds_iterator.h @@ -0,0 +1,58 @@ +/**************************************************************************/ +/* thorvg_bounds_iterator.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef THORVG_BOUNDS_ITERATOR_H +#define THORVG_BOUNDS_ITERATOR_H + +#ifdef GDEXTENSION +// Headers for building as GDExtension plug-in. + +#include <godot_cpp/core/mutex_lock.hpp> +#include <godot_cpp/godot.hpp> + +using namespace godot; + +#else +// Headers for building as built-in module. + +#include "core/typedefs.h" + +#include "modules/modules_enabled.gen.h" // For svg. +#endif + +#ifdef MODULE_SVG_ENABLED + +#include <thorvg.h> + +void tvg_get_bounds(tvg::Picture *p_picture, float &r_min_x, float &r_min_y, float &r_max_x, float &r_max_y); + +#endif // MODULE_SVG_ENABLED + +#endif // THORVG_BOUNDS_ITERATOR_H diff --git a/modules/text_server_adv/thorvg_svg_in_ot.cpp b/modules/text_server_adv/thorvg_svg_in_ot.cpp new file mode 100644 index 0000000000..9354d3f9b3 --- /dev/null +++ b/modules/text_server_adv/thorvg_svg_in_ot.cpp @@ -0,0 +1,320 @@ +/**************************************************************************/ +/* thorvg_svg_in_ot.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifdef GDEXTENSION +// Headers for building as GDExtension plug-in. + +#include <godot_cpp/classes/xml_parser.hpp> +#include <godot_cpp/core/mutex_lock.hpp> +#include <godot_cpp/godot.hpp> +#include <godot_cpp/templates/vector.hpp> + +using namespace godot; + +#else +// Headers for building as built-in module. + +#include "core/error/error_macros.h" +#include "core/io/xml_parser.h" +#include "core/os/memory.h" +#include "core/os/os.h" +#include "core/string/ustring.h" +#include "core/typedefs.h" +#include "core/variant/variant.h" + +#include "modules/modules_enabled.gen.h" // For svg. +#endif + +#ifdef MODULE_SVG_ENABLED + +#include "thorvg_bounds_iterator.h" +#include "thorvg_svg_in_ot.h" + +#include <freetype/otsvg.h> +#include <ft2build.h> + +#include <math.h> +#include <stdlib.h> + +FT_Error tvg_svg_in_ot_init(FT_Pointer *p_state) { + *p_state = memnew(TVG_State); + + return FT_Err_Ok; +} + +void tvg_svg_in_ot_free(FT_Pointer *p_state) { + TVG_State *state = *reinterpret_cast<TVG_State **>(p_state); + memdelete(state); +} + +FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Pointer *p_state) { + TVG_State *state = *reinterpret_cast<TVG_State **>(p_state); + if (!state) { + ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "SVG in OT state not initialized."); + } + MutexLock lock(state->mutex); + + FT_SVG_Document document = (FT_SVG_Document)p_slot->other; + FT_Size_Metrics metrics = document->metrics; + + GL_State &gl_state = state->glyph_map[p_slot->glyph_index]; + if (!gl_state.ready) { + Ref<XMLParser> parser; + parser.instantiate(); +#ifdef GDEXTENSION + PackedByteArray data; + data.resize(document->svg_document_length); + memcpy(data.ptrw(), document->svg_document, document->svg_document_length); + parser->open_buffer(data); +#else + parser->_open_buffer((const uint8_t *)document->svg_document, document->svg_document_length); +#endif + + float aspect = 1.0f; + String xml_body; + while (parser->read() == OK) { + if (parser->has_attribute("id")) { +#ifdef GDEXTENSION + const String &gl_name = parser->get_named_attribute_value("id"); +#else + const String &gl_name = parser->get_attribute_value("id"); +#endif + if (gl_name.begins_with("glyph")) { + int dot_pos = gl_name.find("."); + int64_t gl_idx = gl_name.substr(5, (dot_pos > 0) ? dot_pos - 5 : -1).to_int(); + if (p_slot->glyph_index != gl_idx) { + parser->skip_section(); + continue; + } + } + } + if (parser->get_node_type() == XMLParser::NODE_ELEMENT && parser->get_node_name() == "svg") { + if (parser->has_attribute("viewBox")) { +#ifdef GDEXTENSION + PackedStringArray vb = parser->get_named_attribute_value("viewBox").split(" "); +#else + Vector<String> vb = parser->get_attribute_value("viewBox").split(" "); +#endif + + if (vb.size() == 4) { + aspect = vb[2].to_float() / vb[3].to_float(); + } + } + continue; + } +#ifdef GDEXTENSION + if (parser->get_node_type() == XMLParser::NODE_ELEMENT) { + xml_body = xml_body + "<" + parser->get_node_name(); + for (int i = 0; i < parser->get_attribute_count(); i++) { + xml_body = xml_body + " " + parser->get_attribute_name(i) + "=\"" + parser->get_attribute_value(i) + "\""; + } + xml_body = xml_body + ">"; + } else if (parser->get_node_type() == XMLParser::NODE_TEXT) { + xml_body = xml_body + parser->get_node_data(); + } else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END) { + xml_body = xml_body + "</" + parser->get_node_name() + ">"; + } +#else + if (parser->get_node_type() == XMLParser::NODE_ELEMENT) { + xml_body += vformat("<%s", parser->get_node_name()); + for (int i = 0; i < parser->get_attribute_count(); i++) { + xml_body += vformat(" %s=\"%s\"", parser->get_attribute_name(i), parser->get_attribute_value(i)); + } + xml_body += ">"; + } else if (parser->get_node_type() == XMLParser::NODE_TEXT) { + xml_body += parser->get_node_data(); + } else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END) { + xml_body += vformat("</%s>", parser->get_node_name()); + } +#endif + } + String temp_xml = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 0 0\">" + xml_body; + + std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen(); + tvg::Result result = picture->load(temp_xml.utf8().get_data(), temp_xml.utf8().length(), "svg+xml", false); + if (result != tvg::Result::Success) { + ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (bounds detection)."); + } + + float min_x = INFINITY, min_y = INFINITY, max_x = -INFINITY, max_y = -INFINITY; + tvg_get_bounds(picture.get(), min_x, min_y, max_x, max_y); + + float new_h = (max_y - min_y); + float new_w = (max_x - min_x); + + if (new_h * aspect >= new_w) { + new_w = (new_h * aspect); + } else { + new_h = (new_w / aspect); + } + +#ifdef GDEXTENSION + gl_state.xml_code = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"" + rtos(min_x) + " " + rtos(min_y) + " " + rtos(new_w) + " " + rtos(new_h) + "\">" + xml_body; +#else + gl_state.xml_code = vformat("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"%f %f %f %f\">", min_x, min_y, new_w, new_h) + xml_body; +#endif + + picture = tvg::Picture::gen(); + result = picture->load(gl_state.xml_code.utf8().get_data(), gl_state.xml_code.utf8().length(), "svg+xml", false); + if (result != tvg::Result::Success) { + ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph metrics)."); + } + + float x_svg_to_out, y_svg_to_out; + x_svg_to_out = (float)metrics.x_ppem / new_w; + y_svg_to_out = (float)metrics.y_ppem / new_h; + + gl_state.m.e11 = (double)document->transform.xx / (1 << 16) * x_svg_to_out; + gl_state.m.e12 = -(double)document->transform.xy / (1 << 16) * x_svg_to_out; + gl_state.m.e21 = -(double)document->transform.yx / (1 << 16) * y_svg_to_out; + gl_state.m.e22 = (double)document->transform.yy / (1 << 16) * y_svg_to_out; + gl_state.m.e13 = (double)document->delta.x / 64 * new_w / metrics.x_ppem; + gl_state.m.e23 = -(double)document->delta.y / 64 * new_h / metrics.y_ppem; + gl_state.m.e31 = 0; + gl_state.m.e32 = 0; + gl_state.m.e33 = 1; + + result = picture->transform(gl_state.m); + if (result != tvg::Result::Success) { + ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to apply transform to SVG document."); + } + + result = picture->bounds(&gl_state.x, &gl_state.y, &gl_state.w, &gl_state.h, true); + if (result != tvg::Result::Success) { + ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to get SVG bounds."); + } + + gl_state.bmp_y = -min_y * gl_state.h / new_h; + gl_state.bmp_x = min_x * gl_state.w / new_w; + + gl_state.ready = true; + } + + p_slot->bitmap_left = (FT_Int)gl_state.bmp_x; + p_slot->bitmap_top = (FT_Int)gl_state.bmp_y; + + float tmp = ceil(gl_state.h); + p_slot->bitmap.rows = (unsigned int)tmp; + tmp = ceil(gl_state.w); + p_slot->bitmap.width = (unsigned int)tmp; + p_slot->bitmap.pitch = (int)p_slot->bitmap.width * 4; + p_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA; + + float metrics_width, metrics_height; + float horiBearingX, horiBearingY; + float vertBearingX, vertBearingY; + + metrics_width = (float)gl_state.w; + metrics_height = (float)gl_state.h; + horiBearingX = (float)gl_state.x; + horiBearingY = (float)-gl_state.y; + vertBearingX = p_slot->metrics.horiBearingX / 64.0f - p_slot->metrics.horiAdvance / 64.0f / 2; + vertBearingY = (p_slot->metrics.vertAdvance / 64.0f - p_slot->metrics.height / 64.0f) / 2; + + tmp = roundf(metrics_width * 64); + p_slot->metrics.width = (FT_Pos)tmp; + tmp = roundf(metrics_height * 64); + p_slot->metrics.height = (FT_Pos)tmp; + + p_slot->metrics.horiBearingX = (FT_Pos)(horiBearingX * 64); + p_slot->metrics.horiBearingY = (FT_Pos)(horiBearingY * 64); + p_slot->metrics.vertBearingX = (FT_Pos)(vertBearingX * 64); + p_slot->metrics.vertBearingY = (FT_Pos)(vertBearingY * 64); + + if (p_slot->metrics.vertAdvance == 0) { + p_slot->metrics.vertAdvance = (FT_Pos)(metrics_height * 1.2f * 64); + } + + return FT_Err_Ok; +} + +FT_Error tvg_svg_in_ot_render(FT_GlyphSlot p_slot, FT_Pointer *p_state) { + TVG_State *state = *reinterpret_cast<TVG_State **>(p_state); + if (!state) { + ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "SVG in OT state not initialized."); + } + MutexLock lock(state->mutex); + + if (!state->glyph_map.has(p_slot->glyph_index)) { + ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "SVG glyph not loaded."); + } + + GL_State &gl_state = state->glyph_map[p_slot->glyph_index]; + ERR_FAIL_COND_V_MSG(!gl_state.ready, FT_Err_Invalid_SVG_Document, "SVG glyph not ready."); + + std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen(); + tvg::Result res = picture->load(gl_state.xml_code.utf8().get_data(), gl_state.xml_code.utf8().length(), "svg+xml", false); + if (res != tvg::Result::Success) { + ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph rendering)."); + } + res = picture->transform(gl_state.m); + if (res != tvg::Result::Success) { + ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to apply transform to SVG document."); + } + + std::unique_ptr<tvg::SwCanvas> sw_canvas = tvg::SwCanvas::gen(); + res = sw_canvas->target((uint32_t *)p_slot->bitmap.buffer, (int)p_slot->bitmap.width, (int)p_slot->bitmap.width, (int)p_slot->bitmap.rows, tvg::SwCanvas::ARGB8888_STRAIGHT); + if (res != tvg::Result::Success) { + ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to create SVG canvas."); + } + res = sw_canvas->push(std::move(picture)); + if (res != tvg::Result::Success) { + ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to set SVG canvas source."); + } + res = sw_canvas->draw(); + if (res != tvg::Result::Success) { + ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to draw to SVG canvas."); + } + res = sw_canvas->sync(); + if (res != tvg::Result::Success) { + ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to sync SVG canvas."); + } + + state->glyph_map.erase(p_slot->glyph_index); + + p_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA; + p_slot->bitmap.num_grays = 256; + p_slot->format = FT_GLYPH_FORMAT_BITMAP; + + return FT_Err_Ok; +} + +SVG_RendererHooks tvg_svg_in_ot_hooks = { + (SVG_Lib_Init_Func)tvg_svg_in_ot_init, + (SVG_Lib_Free_Func)tvg_svg_in_ot_free, + (SVG_Lib_Render_Func)tvg_svg_in_ot_render, + (SVG_Lib_Preset_Slot_Func)tvg_svg_in_ot_preset_slot, +}; + +SVG_RendererHooks *get_tvg_svg_in_ot_hooks() { + return &tvg_svg_in_ot_hooks; +} + +#endif // MODULE_SVG_ENABLED diff --git a/modules/text_server_adv/thorvg_svg_in_ot.h b/modules/text_server_adv/thorvg_svg_in_ot.h new file mode 100644 index 0000000000..4035a984b6 --- /dev/null +++ b/modules/text_server_adv/thorvg_svg_in_ot.h @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* thorvg_svg_in_ot.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef THORVG_SVG_IN_OT_H +#define THORVG_SVG_IN_OT_H + +#ifdef GDEXTENSION +// Headers for building as GDExtension plug-in. + +#include <godot_cpp/core/mutex_lock.hpp> +#include <godot_cpp/godot.hpp> +#include <godot_cpp/templates/hash_map.hpp> + +using namespace godot; + +#else +// Headers for building as built-in module. + +#include "core/os/mutex.h" +#include "core/templates/hash_map.h" +#include "core/typedefs.h" + +#include "modules/modules_enabled.gen.h" // For svg. +#endif + +#ifdef MODULE_SVG_ENABLED + +#include <freetype/freetype.h> +#include <freetype/otsvg.h> +#include <ft2build.h> +#include <thorvg.h> + +struct GL_State { + bool ready = false; + float bmp_x = 0; + float bmp_y = 0; + float x = 0; + float y = 0; + float w = 0; + float h = 0; + String xml_code; + tvg::Matrix m; +}; + +struct TVG_State { + Mutex mutex; + HashMap<uint32_t, GL_State> glyph_map; +}; + +FT_Error tvg_svg_in_ot_init(FT_Pointer *p_state); +void tvg_svg_in_ot_free(FT_Pointer *p_state); +FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Pointer *p_state); +FT_Error tvg_svg_in_ot_render(FT_GlyphSlot p_slot, FT_Pointer *p_state); + +SVG_RendererHooks *get_tvg_svg_in_ot_hooks(); + +#endif // MODULE_SVG_ENABLED + +#endif // THORVG_SVG_IN_OT_H |