Merge commit '1843c454bd' as 'engine'

This commit is contained in:
Sara Gerretsen 2026-04-28 14:58:19 +02:00
commit e7cc4cd72f
13965 changed files with 7502032 additions and 0 deletions

View file

@ -0,0 +1,2 @@
# Godot-cpp headers
gdextension_build/godot-cpp

View file

@ -0,0 +1,548 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
import text_server_adv_builders
Import("env")
Import("env_modules")
env_text_server_adv = env_modules.Clone()
# Thirdparty source files
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/common",
"#thirdparty/thorvg/src/renderer",
]
)
# Enable ThorVG static object linking.
env_text_server_adv.Append(CPPDEFINES=["TVG_STATIC"])
if env["builtin_icu4c"]:
env_text_server_adv.Append(CPPDEFINES=["HAVE_ICU_BUILTIN"])
if env["builtin_harfbuzz"]:
env_harfbuzz = env_modules.Clone()
env_harfbuzz.disable_warnings()
thirdparty_dir = "#thirdparty/harfbuzz/"
thirdparty_sources = [
"src/hb-aat-layout.cc",
"src/hb-aat-map.cc",
"src/hb-blob.cc",
"src/hb-buffer-serialize.cc",
"src/hb-buffer-verify.cc",
"src/hb-buffer.cc",
# "src/hb-cairo-utils.cc",
# "src/hb-cairo.cc",
"src/hb-common.cc",
# "src/hb-coretext-font.cc",
# "src/hb-coretext-shape.cc",
# "src/hb-directwrite.cc",
"src/hb-draw.cc",
"src/hb-face-builder.cc",
"src/hb-face.cc",
"src/hb-fallback-shape.cc",
"src/hb-font.cc",
# "src/hb-gdi.cc",
# "src/hb-glib.cc",
# "src/hb-gobject-structs.cc",
"src/hb-icu.cc",
# "src/hb-kbts.cc",
"src/hb-map.cc",
"src/hb-number.cc",
"src/hb-ot-cff1-table.cc",
"src/hb-ot-cff2-table.cc",
"src/hb-ot-color.cc",
"src/hb-ot-face.cc",
"src/hb-ot-font.cc",
"src/hb-ot-layout.cc",
"src/hb-ot-map.cc",
"src/hb-ot-math.cc",
"src/hb-ot-meta.cc",
"src/hb-ot-metrics.cc",
"src/hb-ot-name.cc",
"src/hb-ot-shaper-arabic.cc",
"src/hb-ot-shaper-default.cc",
"src/hb-ot-shaper-hangul.cc",
"src/hb-ot-shaper-hebrew.cc",
"src/hb-ot-shaper-indic-table.cc",
"src/hb-ot-shaper-indic.cc",
"src/hb-ot-shaper-khmer.cc",
"src/hb-ot-shaper-myanmar.cc",
"src/hb-ot-shaper-syllabic.cc",
"src/hb-ot-shaper-thai.cc",
"src/hb-ot-shaper-use.cc",
"src/hb-ot-shaper-vowel-constraints.cc",
"src/hb-ot-shape-fallback.cc",
"src/hb-ot-shape-normalize.cc",
"src/hb-ot-shape.cc",
"src/hb-ot-tag.cc",
"src/hb-ot-var.cc",
"src/hb-outline.cc",
"src/hb-paint-bounded.cc",
"src/hb-paint-extents.cc",
"src/hb-paint.cc",
"src/hb-set.cc",
"src/hb-shape-plan.cc",
"src/hb-shape.cc",
"src/hb-shaper.cc",
"src/hb-static.cc",
"src/hb-style.cc",
# "src/hb-subset-cff-common.cc",
# "src/hb-subset-cff1.cc",
# "src/hb-subset-cff2.cc",
# "src/hb-subset-cff2-to-cff1.cc",
# "src/hb-subset-input.cc",
# "src/hb-subset-instancer-iup.cc",
# "src/hb-subset-instancer-solver.cc",
# "src/hb-subset-plan.cc",
# "src/hb-subset-serialize.cc",
# "src/hb-subset-table-cff.cc",
# "src/hb-subset-table-color.cc",
# "src/hb-subset-table-layout.cc",
# "src/hb-subset-table-other.cc",
# "src/hb-subset-table-var.cc",
"src/hb-raster.cc",
"src/hb-raster-draw.cc",
"src/hb-raster-image.cc",
"src/hb-raster-paint.cc",
"src/hb-vector-draw.cc",
# "src/hb-vector-paint-pdf.cc",
"src/hb-vector-paint-svg.cc",
"src/hb-vector-paint.cc",
"src/hb-vector-path.cc",
"src/hb-vector.cc",
"src/hb-subset.cc",
"src/hb-ucd.cc",
"src/hb-unicode.cc",
"src/hb-zlib.cc",
# "src/hb-uniscribe.cc",
"src/OT/Var/VARC/VARC.cc",
]
if freetype_enabled:
thirdparty_sources += [
"src/hb-ft.cc",
]
if env["graphite"]:
thirdparty_sources += [
"src/hb-graphite2.cc",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
env_harfbuzz.Prepend(CPPPATH=["#thirdparty/harfbuzz/src"])
env_harfbuzz.Append(CPPDEFINES=["HAVE_ICU", "HAVE_ZLIB", "HAVE_PNG"])
if env["builtin_icu4c"]:
env_harfbuzz.Prepend(CPPPATH=["#thirdparty/icu4c/common/", "#thirdparty/icu4c/i18n/"])
env_harfbuzz.Append(
CPPDEFINES=[
"U_STATIC_IMPLEMENTATION",
("U_HAVE_LIB_SUFFIX", 1),
("U_LIB_SUFFIX_C_NAME", "_godot"),
"HAVE_ICU_BUILTIN",
]
)
if freetype_enabled:
env_harfbuzz.Append(CPPDEFINES=["HAVE_FREETYPE"])
if env["graphite"]:
env_harfbuzz.Append(CPPDEFINES=["HAVE_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(CPPDEFINES=["GRAPHITE2_STATIC"])
if env["platform"] in ["android", "linuxbsd", "web"]:
env_harfbuzz.Append(CPPDEFINES=["HAVE_PTHREAD"])
env_text_server_adv.Prepend(CPPPATH=["#thirdparty/harfbuzz/src"])
lib = env_harfbuzz.add_library("harfbuzz_builtin", thirdparty_sources)
thirdparty_obj += lib
# Needs to be appended to arrive after libscene in the linker call,
# but we don't want it to arrive *after* system libs, so manual hack
# LIBS contains first SCons Library objects ("SCons.Node.FS.File object")
# and then plain strings for system library. We insert between the two.
inserted = False
for idx, linklib in enumerate(env["LIBS"]):
if isinstance(linklib, (str, bytes)): # first system lib such as "X11", otherwise SCons lib object
env["LIBS"].insert(idx, lib)
inserted = True
break
if not inserted:
env.Append(LIBS=[lib])
if env["builtin_graphite"] and freetype_enabled and env["graphite"]:
env_graphite = env_modules.Clone()
env_graphite.disable_warnings()
thirdparty_dir = "#thirdparty/graphite/"
thirdparty_sources = [
"src/gr_char_info.cpp",
"src/gr_face.cpp",
"src/gr_features.cpp",
"src/gr_font.cpp",
"src/gr_logging.cpp",
"src/gr_segment.cpp",
"src/gr_slot.cpp",
"src/CmapCache.cpp",
"src/Code.cpp",
"src/Collider.cpp",
"src/Decompressor.cpp",
"src/Face.cpp",
#'src/FileFace.cpp',
"src/FeatureMap.cpp",
"src/Font.cpp",
"src/GlyphCache.cpp",
"src/GlyphFace.cpp",
"src/Intervals.cpp",
"src/Justifier.cpp",
"src/NameTable.cpp",
"src/Pass.cpp",
"src/Position.cpp",
"src/Segment.cpp",
"src/Silf.cpp",
"src/Slot.cpp",
"src/Sparse.cpp",
"src/TtfUtil.cpp",
"src/UtfCodec.cpp",
"src/FileFace.cpp",
"src/json.cpp",
]
if not env_graphite.msvc:
thirdparty_sources += ["src/direct_machine.cpp"]
else:
thirdparty_sources += ["src/call_machine.cpp"]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
env_graphite.Prepend(CPPPATH=["#thirdparty/graphite/src", "#thirdparty/graphite/include"])
env_graphite.Append(
CPPDEFINES=[
"GRAPHITE2_STATIC",
"GRAPHITE2_NTRACING",
"GRAPHITE2_NFILEFACE",
]
)
lib = env_graphite.add_library("graphite_builtin", thirdparty_sources)
thirdparty_obj += lib
# Needs to be appended to arrive after libscene in the linker call,
# but we don't want it to arrive *after* system libs, so manual hack
# LIBS contains first SCons Library objects ("SCons.Node.FS.File object")
# and then plain strings for system library. We insert between the two.
inserted = False
for idx, linklib in enumerate(env["LIBS"]):
if isinstance(linklib, (str, bytes)): # first system lib such as "X11", otherwise SCons lib object
env["LIBS"].insert(idx, lib)
inserted = True
break
if not inserted:
env.Append(LIBS=[lib])
if env["builtin_icu4c"]:
env_icu = env_modules.Clone()
env_icu.disable_warnings()
thirdparty_dir = "#thirdparty/icu4c/"
thirdparty_sources = [
"common/appendable.cpp",
"common/bmpset.cpp",
"common/brkeng.cpp",
"common/brkiter.cpp",
"common/bytesinkutil.cpp",
"common/bytestream.cpp",
"common/bytestrie.cpp",
"common/bytestriebuilder.cpp",
"common/bytestrieiterator.cpp",
"common/caniter.cpp",
"common/characterproperties.cpp",
"common/chariter.cpp",
"common/charstr.cpp",
"common/cmemory.cpp",
"common/cstr.cpp",
"common/cstring.cpp",
"common/cwchar.cpp",
"common/dictbe.cpp",
"common/dictionarydata.cpp",
"common/dtintrv.cpp",
"common/edits.cpp",
"common/emojiprops.cpp",
"common/errorcode.cpp",
"common/filteredbrk.cpp",
"common/filterednormalizer2.cpp",
"common/fixedstring.cpp",
"common/icudataver.cpp",
"common/icuplug.cpp",
"common/loadednormalizer2impl.cpp",
"common/localebuilder.cpp",
"common/localematcher.cpp",
"common/localeprioritylist.cpp",
"common/locavailable.cpp",
"common/locbased.cpp",
"common/locdispnames.cpp",
"common/locdistance.cpp",
"common/locdspnm.cpp",
"common/locid.cpp",
"common/loclikely.cpp",
"common/loclikelysubtags.cpp",
"common/locmap.cpp",
"common/locresdata.cpp",
"common/locutil.cpp",
"common/lsr.cpp",
"common/lstmbe.cpp",
"common/messagepattern.cpp",
"common/mlbe.cpp",
"common/normalizer2.cpp",
"common/normalizer2impl.cpp",
"common/normlzr.cpp",
"common/parsepos.cpp",
"common/patternprops.cpp",
"common/pluralmap.cpp",
"common/propname.cpp",
"common/propsvec.cpp",
"common/punycode.cpp",
"common/putil.cpp",
"common/rbbi.cpp",
"common/rbbi_cache.cpp",
"common/rbbidata.cpp",
"common/rbbinode.cpp",
"common/rbbirb.cpp",
"common/rbbiscan.cpp",
"common/rbbisetb.cpp",
"common/rbbistbl.cpp",
"common/rbbitblb.cpp",
"common/resbund.cpp",
"common/resbund_cnv.cpp",
"common/resource.cpp",
"common/restrace.cpp",
"common/ruleiter.cpp",
"common/schriter.cpp",
"common/serv.cpp",
"common/servlk.cpp",
"common/servlkf.cpp",
"common/servls.cpp",
"common/servnotf.cpp",
"common/servrbf.cpp",
"common/servslkf.cpp",
"common/sharedobject.cpp",
"common/simpleformatter.cpp",
"common/static_unicode_sets.cpp",
"common/stringpiece.cpp",
"common/stringtriebuilder.cpp",
"common/uarrsort.cpp",
"common/ubidi.cpp",
"common/ubidi_props.cpp",
"common/ubidiln.cpp",
"common/ubiditransform.cpp",
"common/ubidiwrt.cpp",
"common/ubrk.cpp",
"common/ucase.cpp",
"common/ucasemap.cpp",
"common/ucasemap_titlecase_brkiter.cpp",
"common/ucat.cpp",
"common/uchar.cpp",
"common/ucharstrie.cpp",
"common/ucharstriebuilder.cpp",
"common/ucharstrieiterator.cpp",
"common/uchriter.cpp",
"common/ucln_cmn.cpp",
"common/ucmndata.cpp",
"common/ucnv.cpp",
"common/ucnv2022.cpp",
"common/ucnv_bld.cpp",
"common/ucnv_cb.cpp",
"common/ucnv_cnv.cpp",
"common/ucnv_ct.cpp",
"common/ucnv_err.cpp",
"common/ucnv_ext.cpp",
"common/ucnv_io.cpp",
"common/ucnv_lmb.cpp",
"common/ucnv_set.cpp",
"common/ucnv_u16.cpp",
"common/ucnv_u32.cpp",
"common/ucnv_u7.cpp",
"common/ucnv_u8.cpp",
"common/ucnvbocu.cpp",
"common/ucnvdisp.cpp",
"common/ucnvhz.cpp",
"common/ucnvisci.cpp",
"common/ucnvlat1.cpp",
"common/ucnvmbcs.cpp",
"common/ucnvscsu.cpp",
"common/ucnvsel.cpp",
"common/ucol_swp.cpp",
"common/ucptrie.cpp",
"common/ucurr.cpp",
"common/udata.cpp",
"common/udatamem.cpp",
"common/udataswp.cpp",
"common/uenum.cpp",
"common/uhash.cpp",
"common/uhash_us.cpp",
"common/uidna.cpp",
"common/uinit.cpp",
"common/uinvchar.cpp",
"common/uiter.cpp",
"common/ulist.cpp",
"common/uloc.cpp",
"common/uloc_keytype.cpp",
"common/uloc_tag.cpp",
"common/ulocale.cpp",
"common/ulocbuilder.cpp",
"common/umapfile.cpp",
"common/umath.cpp",
"common/umutablecptrie.cpp",
"common/umutex.cpp",
"common/unames.cpp",
"common/unifiedcache.cpp",
"common/unifilt.cpp",
"common/unifunct.cpp",
"common/uniset.cpp",
"common/uniset_closure.cpp",
"common/uniset_props.cpp",
"common/unisetspan.cpp",
"common/unistr.cpp",
"common/unistr_case.cpp",
"common/unistr_case_locale.cpp",
"common/unistr_cnv.cpp",
"common/unistr_props.cpp",
"common/unistr_titlecase_brkiter.cpp",
"common/unorm.cpp",
"common/unormcmp.cpp",
"common/uobject.cpp",
"common/uprops.cpp",
"common/ures_cnv.cpp",
"common/uresbund.cpp",
"common/uresdata.cpp",
"common/usc_impl.cpp",
"common/uscript.cpp",
"common/uscript_props.cpp",
"common/uset.cpp",
"common/uset_props.cpp",
"common/usetiter.cpp",
# "common/ushape.cpp",
"common/usprep.cpp",
"common/ustack.cpp",
"common/ustr_cnv.cpp",
"common/ustr_titlecase_brkiter.cpp",
"common/ustr_wcs.cpp",
"common/ustrcase.cpp",
"common/ustrcase_locale.cpp",
"common/ustrenum.cpp",
"common/ustrfmt.cpp",
"common/ustring.cpp",
"common/ustrtrns.cpp",
"common/utext.cpp",
"common/utf_impl.cpp",
"common/util.cpp",
"common/util_props.cpp",
"common/utrace.cpp",
"common/utrie.cpp",
"common/utrie2.cpp",
"common/utrie2_builder.cpp",
"common/utrie_swap.cpp",
"common/uts46.cpp",
"common/utypes.cpp",
"common/uvector.cpp",
"common/uvectr32.cpp",
"common/uvectr64.cpp",
"common/wintz.cpp",
"i18n/scriptset.cpp",
"i18n/ucln_in.cpp",
"i18n/uspoof.cpp",
"i18n/uspoof_impl.cpp",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
if env.editor_build:
icudata = env_icu.CommandNoCache(
"#thirdparty/icu4c/icudata.gen.h",
"#thirdparty/icu4c/icudt_godot.dat",
env.Run(text_server_adv_builders.make_icu_data),
)
env_text_server_adv.Prepend(CPPPATH=["#thirdparty/icu4c/"])
else:
thirdparty_sources += ["icu_data/icudata_stub.cpp"]
env_icu.Prepend(CPPPATH=["#thirdparty/icu4c/common/", "#thirdparty/icu4c/i18n/"])
env_icu.Append(
CPPDEFINES=[
"U_STATIC_IMPLEMENTATION",
"U_COMMON_IMPLEMENTATION",
"UCONFIG_NO_COLLATION",
"UCONFIG_NO_CONVERSION",
"UCONFIG_NO_FORMATTING",
"UCONFIG_NO_SERVICE",
"UCONFIG_NO_IDNA",
"UCONFIG_NO_FILE_IO",
"UCONFIG_NO_TRANSLITERATION",
"UCONFIG_NO_REGULAR_EXPRESSIONS",
("PKGDATA_MODE", "static"),
("U_ENABLE_DYLOAD", 0),
("U_HAVE_LIB_SUFFIX", 1),
("U_LIB_SUFFIX_C_NAME", "_godot"),
]
)
env_text_server_adv.Append(
CPPDEFINES=[
"U_STATIC_IMPLEMENTATION",
("U_HAVE_LIB_SUFFIX", 1),
("U_LIB_SUFFIX_C_NAME", "_godot"),
]
)
if env.editor_build:
env_text_server_adv.Append(CPPDEFINES=["ICU_STATIC_DATA"])
env_text_server_adv.Prepend(CPPPATH=["#thirdparty/icu4c/common/", "#thirdparty/icu4c/i18n/"])
lib = env_icu.add_library("icu_builtin", thirdparty_sources)
thirdparty_obj += lib
# Needs to be appended to arrive after libscene in the linker call,
# but we don't want it to arrive *after* system libs, so manual hack
# LIBS contains first SCons Library objects ("SCons.Node.FS.File object")
# and then plain strings for system library. We insert between the two.
inserted = False
for idx, linklib in enumerate(env["LIBS"]):
if isinstance(linklib, (str, bytes)): # first system lib such as "X11", otherwise SCons lib object
env["LIBS"].insert(idx, lib)
inserted = True
break
if not inserted:
env.Append(LIBS=[lib])
# Godot source files
module_obj = []
if env["builtin_msdfgen"] and msdfgen_enabled:
env_text_server_adv.Append(CPPDEFINES=[("MSDFGEN_PUBLIC", "")])
env_text_server_adv.Prepend(CPPPATH=["#thirdparty/msdfgen"])
if env["builtin_freetype"] and freetype_enabled:
env_text_server_adv.Append(CPPDEFINES=["FT_CONFIG_OPTION_USE_BROTLI"])
env_text_server_adv.Prepend(CPPPATH=["#thirdparty/freetype/include"])
if env["builtin_graphite"] and freetype_enabled and env["graphite"]:
env_text_server_adv.Prepend(CPPPATH=["#thirdparty/graphite/include"])
env_text_server_adv.add_source_files(module_obj, "*.cpp")
env.modules_sources += module_obj
# Needed to force rebuilding the module files when the thirdparty library is updated.
env.Depends(module_obj, thirdparty_obj)

View file

@ -0,0 +1,25 @@
def can_build(env, platform):
env.module_add_dependencies("text_server_adv", ["freetype", "msdfgen", "svg"], True)
return True
def get_opts(platform):
from SCons.Variables import BoolVariable
return [
BoolVariable("graphite", "Enable SIL Graphite smart fonts support", True),
]
def configure(env):
pass
def get_doc_classes():
return [
"TextServerAdvanced",
]
def get_doc_path():
return "doc_classes"

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="TextServerAdvanced" inherits="TextServerExtension" api_type="core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
An advanced text server with support for BiDi, complex text layout, and contextual OpenType features. Used in Godot by default.
</brief_description>
<description>
An implementation of [TextServer] that uses HarfBuzz, ICU and SIL Graphite to support BiDi, complex text layouts and contextual OpenType features. This is Godot's default primary [TextServer] interface.
</description>
<tutorials>
</tutorials>
</class>

View file

@ -0,0 +1,63 @@
/**************************************************************************/
/* 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>
#include <unicode/uversion.h>
typedef struct {
uint16_t header_size;
uint8_t magic_1, magic_2;
UDataInfo info;
char padding[8];
uint32_t count, reserved;
int fake_name_and_data[4];
} ICU_data_header;
extern "C" U_EXPORT const ICU_data_header U_ICUDATA_ENTRY_POINT = {
32,
0xDA, 0x27,
{ sizeof(UDataInfo),
0,
#if U_IS_BIG_ENDIAN
1,
#else
0,
#endif
U_CHARSET_FAMILY,
sizeof(UChar),
0,
{ 0x54, 0x6F, 0x43, 0x50 },
{ 1, 0, 0, 0 },
{ 0, 0, 0, 0 } },
{ 0, 0, 0, 0, 0, 0, 0, 0 },
0, 0,
{ 0, 0, 0, 0 }
};

View file

@ -0,0 +1,55 @@
/**************************************************************************/
/* 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"
#include "text_server_adv.h"
#include "core/object/class_db.h"
void initialize_text_server_adv_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SERVERS) {
return;
}
GDREGISTER_CLASS(TextServerAdvanced);
TextServerManager *tsman = TextServerManager::get_singleton();
if (tsman) {
Ref<TextServerAdvanced> ts;
ts.instantiate();
tsman->add_interface(ts);
}
}
void uninitialize_text_server_adv_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SERVERS) {
return;
}
}

View file

@ -0,0 +1,36 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "modules/register_module_types.h"
void initialize_text_server_adv_module(ModuleInitializationLevel p_level);
void uninitialize_text_server_adv_module(ModuleInitializationLevel p_level);

View file

@ -0,0 +1,209 @@
/**************************************************************************/
/* 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"
// This implementation is derived from ICU: icu4c/source/extra/scrptrun/scrptrun.cpp
inline constexpr UChar32 ZERO_WIDTH_JOINER = 0x200d;
inline constexpr UChar32 VARIATION_SELECTOR_15 = 0xfe0e;
inline constexpr UChar32 VARIATION_SELECTOR_16 = 0xfe0f;
inline constexpr UChar32 COMBINING_ENCLOSING_KEYCAP = 0x20e3;
inline bool ScriptIterator::same_script(int32_t p_script_one, int32_t p_script_two) {
return p_script_one <= USCRIPT_INHERITED || p_script_two <= USCRIPT_INHERITED || p_script_one == p_script_two;
}
inline bool ScriptIterator::is_emoji(UChar32 p_c, UChar32 p_next) {
if (p_next == VARIATION_SELECTOR_15 && (u_hasBinaryProperty(p_c, UCHAR_EMOJI) || u_hasBinaryProperty(p_c, UCHAR_EXTENDED_PICTOGRAPHIC))) {
return false;
} else if (p_next == VARIATION_SELECTOR_16 && (u_hasBinaryProperty(p_c, UCHAR_EMOJI) || u_hasBinaryProperty(p_c, UCHAR_EXTENDED_PICTOGRAPHIC))) {
return true;
} else {
return u_hasBinaryProperty(p_c, UCHAR_EMOJI_PRESENTATION) || u_hasBinaryProperty(p_c, UCHAR_EMOJI_MODIFIER) || u_hasBinaryProperty(p_c, UCHAR_REGIONAL_INDICATOR);
}
}
ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length) {
struct ParenStackEntry {
int pair_index;
UScriptCode script_code;
};
struct EmojiSubrunEntry {
int start;
int end;
};
if (p_start >= p_length) {
p_start = p_length - 1;
}
if (p_start < 0) {
p_start = 0;
}
int paren_size = PAREN_STACK_DEPTH;
ParenStackEntry starter_paren_stack[PAREN_STACK_DEPTH];
ParenStackEntry *paren_stack = starter_paren_stack;
int emoji_size = EMOJI_STACK_DEPTH;
EmojiSubrunEntry starter_emoji_stack[EMOJI_STACK_DEPTH];
EmojiSubrunEntry *emoji_stack = starter_emoji_stack;
int script_start;
int script_end = p_start;
UScriptCode script_code;
int paren_sp = -1;
int start_sp = paren_sp;
UErrorCode err = U_ZERO_ERROR;
const char32_t *str = p_string.ptr();
do {
script_code = USCRIPT_COMMON;
int emoji_sp = -1;
bool emoji_run = false;
for (script_start = script_end; script_end < p_length; script_end++) {
UChar32 ch = str[script_end];
UChar32 n = (script_end + 1 < p_length) ? str[script_end + 1] : 0;
if (is_emoji(ch, n)) {
if (!emoji_run) {
emoji_run = true;
emoji_sp++;
if (unlikely(emoji_sp >= emoji_size)) {
emoji_size += EMOJI_STACK_DEPTH;
if (emoji_stack == starter_emoji_stack) {
emoji_stack = static_cast<EmojiSubrunEntry *>(memalloc(emoji_size * sizeof(EmojiSubrunEntry)));
} else {
emoji_stack = static_cast<EmojiSubrunEntry *>(memrealloc(emoji_stack, emoji_size * sizeof(EmojiSubrunEntry)));
}
}
emoji_stack[emoji_sp].start = script_end;
emoji_stack[emoji_sp].end = script_end;
}
} else if (emoji_run && ch != ZERO_WIDTH_JOINER && ch != VARIATION_SELECTOR_16 && ch != COMBINING_ENCLOSING_KEYCAP && !(u_hasBinaryProperty(ch, UCHAR_EXTENDED_PICTOGRAPHIC) && n != VARIATION_SELECTOR_15)) {
emoji_run = false;
emoji_stack[emoji_sp].end = script_end;
}
UScriptCode sc = uscript_getScript(ch, &err);
if (U_FAILURE(err)) {
if (paren_stack != starter_paren_stack) {
memfree(paren_stack);
}
ERR_FAIL_MSG(u_errorName(err));
}
if (u_getIntPropertyValue(ch, UCHAR_BIDI_PAIRED_BRACKET_TYPE) != U_BPT_NONE) {
if (u_getIntPropertyValue(ch, UCHAR_BIDI_PAIRED_BRACKET_TYPE) == U_BPT_OPEN) {
// If it's an open character, push it onto the stack.
paren_sp++;
if (unlikely(paren_sp >= paren_size)) {
// If the stack is full, allocate more space to handle deeply nested parentheses. This is unlikely to happen with any real text.
paren_size += PAREN_STACK_DEPTH;
if (paren_stack == starter_paren_stack) {
paren_stack = static_cast<ParenStackEntry *>(memalloc(paren_size * sizeof(ParenStackEntry)));
} else {
paren_stack = static_cast<ParenStackEntry *>(memrealloc(paren_stack, paren_size * sizeof(ParenStackEntry)));
}
}
paren_stack[paren_sp].pair_index = ch;
paren_stack[paren_sp].script_code = script_code;
} else if (paren_sp >= 0) {
// If it's a close character, find the matching open on the stack, and use that script code. Any non-matching open characters above it on the stack will be popped.
UChar32 paired_ch = u_getBidiPairedBracket(ch);
while (paren_sp >= 0 && paren_stack[paren_sp].pair_index != paired_ch) {
paren_sp -= 1;
}
if (paren_sp < start_sp) {
start_sp = paren_sp;
}
if (paren_sp >= 0) {
sc = paren_stack[paren_sp].script_code;
}
}
}
if (same_script(script_code, sc)) {
if (script_code <= USCRIPT_INHERITED && sc > USCRIPT_INHERITED) {
script_code = sc;
// Now that we have a final script code, fix any open characters we pushed before we knew the script code.
while (start_sp < paren_sp) {
paren_stack[++start_sp].script_code = script_code;
}
}
if ((u_getIntPropertyValue(ch, UCHAR_BIDI_PAIRED_BRACKET_TYPE) == U_BPT_CLOSE) && paren_sp >= 0) {
// If this character is a close paired character pop the matching open character from the stack.
paren_sp -= 1;
if (start_sp >= 0) {
start_sp -= 1;
}
}
} else {
break;
}
}
if (emoji_run) {
emoji_stack[emoji_sp].end = script_end;
}
for (int sub = 0; sub <= emoji_sp; sub++) {
if (emoji_stack[sub].start > script_start) {
ScriptRange rng;
rng.script = hb_icu_script_to_script(script_code);
rng.start = script_start;
rng.end = emoji_stack[sub].start;
script_ranges.push_back(rng);
}
ScriptRange rng;
rng.script = (hb_script_t)HB_TAG('Z', 's', 'y', 'e');
rng.start = emoji_stack[sub].start;
rng.end = emoji_stack[sub].end;
script_ranges.push_back(rng);
script_start = emoji_stack[sub].end;
}
if (script_start != script_end) {
ScriptRange rng;
rng.script = hb_icu_script_to_script(script_code);
rng.start = script_start;
rng.end = script_end;
script_ranges.push_back(rng);
}
if (emoji_stack != starter_emoji_stack) {
memfree(emoji_stack);
}
} while (script_end < p_length);
if (paren_stack != starter_paren_stack) {
memfree(paren_stack);
}
}

View file

@ -0,0 +1,62 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "core/string/ustring.h"
#include "core/templates/vector.h"
#include <hb-icu.h>
#include <hb.h>
#include <unicode/uchar.h>
#include <unicode/uloc.h>
#include <unicode/uscript.h>
#include <unicode/ustring.h>
#include <unicode/utypes.h>
class ScriptIterator {
static const int PAREN_STACK_DEPTH = 128;
static const int EMOJI_STACK_DEPTH = 32;
public:
struct ScriptRange {
int start = 0;
int end = 0;
hb_script_t script = HB_SCRIPT_COMMON;
};
Vector<ScriptRange> script_ranges;
private:
inline static bool same_script(int32_t p_script_one, int32_t p_script_two);
inline static bool is_emoji(UChar32 p_c, UChar32 p_next);
public:
ScriptIterator(const String &p_string, int p_start, int p_length);
};

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,21 @@
"""Functions used to generate source files during build time"""
import methods
def make_icu_data(target, source, env):
buffer = methods.get_buffer(str(source[0]))
with methods.generated_wrapper(str(target[0])) as file:
file.write(f"""\
/* (C) 2016 and later: Unicode, Inc. and others. */
/* License & terms of use: https://www.unicode.org/copyright.html */
#include <unicode/utypes.h>
#include <unicode/udata.h>
#include <unicode/uversion.h>
extern "C" U_EXPORT const size_t U_ICUDATA_SIZE = {len(buffer)};
extern "C" U_EXPORT const unsigned char U_ICUDATA_ENTRY_POINT[] = {{
{methods.format_buffer(buffer, 1)}
}};
""")

View file

@ -0,0 +1,370 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#include "thorvg_svg_in_ot.h"
#include "core/error/error_macros.h"
#include "core/io/xml_parser.h"
#include "core/os/memory.h"
#include "core/string/ustring.h"
#include "core/typedefs.h"
#include "core/variant/variant.h"
#include "modules/modules_enabled.gen.h" // For svg, freetype.
#ifdef MODULE_SVG_ENABLED
#ifdef MODULE_FREETYPE_ENABLED
#include <freetype/otsvg.h>
#include <ft2build.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);
}
static void construct_xml(Ref<XMLParser> &parser, double &r_embox_x, double &r_embox_y, String *p_xml, int64_t &r_tag_count) {
if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
*p_xml += vformat("<%s", parser->get_node_name());
bool is_svg_tag = parser->get_node_name() == "svg";
for (int i = 0; i < parser->get_attribute_count(); i++) {
String aname = parser->get_attribute_name(i);
String value = parser->get_attribute_value(i);
if (is_svg_tag && aname == "viewBox") {
PackedStringArray vb = value.split(" ");
if (vb.size() == 4) {
r_embox_x = vb[2].to_float();
r_embox_y = vb[3].to_float();
}
} else if (is_svg_tag && aname == "width") {
r_embox_x = value.to_float();
} else if (is_svg_tag && aname == "height") {
r_embox_y = value.to_float();
} else {
*p_xml += vformat(" %s=\"%s\"", aname, value);
}
}
if (parser->is_empty()) {
*p_xml += "/>";
} else {
*p_xml += ">";
if (r_tag_count >= 0) {
r_tag_count++;
}
}
} else if (parser->get_node_type() == XMLParser::NODE_TEXT) {
*p_xml += parser->get_node_data();
} else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END) {
*p_xml += vformat("</%s>", parser->get_node_name());
if (r_tag_count > 0) {
r_tag_count--;
}
}
}
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();
parser->_open_buffer((const uint8_t *)document->svg_document, document->svg_document_length);
String xml_body;
double embox_x = document->units_per_EM;
double embox_y = document->units_per_EM;
TVG_DocumentCache &cache = state->document_map[document->svg_document];
if (!cache.xml_body.is_empty()) {
// If we have a cached document, that means we have already parsed it.
// All node cache should be available.
xml_body = cache.xml_body;
embox_x = cache.embox_x;
embox_y = cache.embox_y;
ERR_FAIL_COND_V(!cache.node_caches.has(p_slot->glyph_index), FT_Err_Invalid_SVG_Document);
Vector<TVG_NodeCache> &ncs = cache.node_caches[p_slot->glyph_index];
uint64_t offset = 0;
for (TVG_NodeCache &nc : ncs) {
// Seek will call read() internally.
if (parser->seek(nc.document_offset) == OK) {
int64_t tag_count = 0;
String xml_node;
// We only parse the glyph node.
do {
construct_xml(parser, embox_x, embox_y, &xml_node, tag_count);
} while (tag_count != 0 && parser->read() == OK);
xml_body = xml_body.insert(nc.body_offset + offset, xml_node);
offset += xml_node.length();
}
}
} else {
String xml_node;
String xml_body_temp;
String *p_xml = &xml_body_temp;
int64_t tag_count = -1;
bool is_in_defs = false;
while (parser->read() == OK) {
if (parser->get_node_type() == XMLParser::NODE_ELEMENT && parser->get_node_name().to_lower() == "defs") {
is_in_defs = true;
} else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END && parser->get_node_name().to_lower() == "defs") {
is_in_defs = false;
}
if (!is_in_defs && parser->has_attribute("id")) {
const String &gl_name = parser->get_named_attribute_value("id");
if (gl_name.begins_with("glyph")) {
int dot_pos = gl_name.find_char('.');
int64_t gl_idx = gl_name.substr(5, (dot_pos > 0) ? dot_pos - 5 : -1).to_int();
TVG_NodeCache node_cache = TVG_NodeCache();
node_cache.document_offset = parser->get_node_offset(),
node_cache.body_offset = (uint64_t)cache.xml_body.length();
cache.node_caches[gl_idx].push_back(node_cache);
if (p_slot->glyph_index != gl_idx) {
parser->skip_section();
continue;
}
tag_count = 0;
xml_node = "";
p_xml = &xml_node;
}
}
xml_body_temp = "";
construct_xml(parser, embox_x, embox_y, p_xml, tag_count);
if (xml_body_temp.length() > 0) {
xml_body += xml_body_temp;
cache.xml_body += xml_body_temp;
continue;
}
if (tag_count == 0) {
p_xml = &xml_body_temp;
tag_count = -1;
xml_body += xml_node;
}
}
cache.embox_x = embox_x;
cache.embox_y = embox_y;
}
tvg::Picture *picture = tvg::Picture::gen();
gl_state.xml_code = xml_body.utf8();
tvg::Result result = picture->load(gl_state.xml_code.get_data(), gl_state.xml_code.length(), "svg+xml", nullptr, false);
if (result != tvg::Result::Success) {
tvg::Paint::rel(picture);
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph metrics).");
}
float svg_width, svg_height;
picture->size(&svg_width, &svg_height);
double aspect_x = (svg_width > svg_height) ? svg_width / svg_height : 1.0;
double aspect_y = (svg_width < svg_height) ? svg_height / svg_width : 1.0;
result = picture->size(embox_x * aspect_x, embox_y * aspect_y);
if (result != tvg::Result::Success) {
tvg::Paint::rel(picture);
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to resize SVG document.");
}
double scale = double(embox_x * aspect_x) / double(svg_width);
double yoff = double(document->metrics.ascender + document->metrics.descender) / double(document->metrics.ascender);
double x_svg_to_out = (double)metrics.x_ppem / embox_x / scale;
double y_svg_to_out = (double)metrics.y_ppem / embox_y / scale;
gl_state.m.e11 = (double)document->transform.xx / (1 << 16);
gl_state.m.e12 = -(double)document->transform.xy / (1 << 16);
gl_state.m.e21 = -(double)document->transform.yx / (1 << 16);
gl_state.m.e22 = (double)document->transform.yy / (1 << 16);
gl_state.m.e13 = (double)document->delta.x / 64 * embox_x / metrics.x_ppem * scale;
gl_state.m.e23 = -(double)document->delta.y / 64 * embox_y / metrics.y_ppem * scale;
gl_state.m.e31 = 0;
gl_state.m.e32 = 0;
gl_state.m.e33 = 1;
result = picture->size(embox_x * aspect_x * x_svg_to_out, embox_y * aspect_y * y_svg_to_out);
if (result != tvg::Result::Success) {
tvg::Paint::rel(picture);
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to resize SVG document.");
}
result = picture->transform(gl_state.m);
if (result != tvg::Result::Success) {
tvg::Paint::rel(picture);
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to apply transform to SVG document.");
}
picture->size(&gl_state.w, &gl_state.h);
gl_state.x = (double(p_slot->metrics.horiAdvance) / 64.0 - gl_state.w) / 2.0;
gl_state.y = -Math::ceil(gl_state.h * yoff);
tvg::Paint::rel(picture);
gl_state.ready = true;
}
p_slot->bitmap_left = (FT_Int)gl_state.x;
p_slot->bitmap_top = (FT_Int)-gl_state.y;
double tmpd = Math::ceil(gl_state.h);
p_slot->bitmap.rows = (unsigned int)tmpd;
tmpd = Math::ceil(gl_state.w);
p_slot->bitmap.width = (unsigned int)tmpd;
p_slot->bitmap.pitch = (int)p_slot->bitmap.width * 4;
p_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
float metrics_width = (float)gl_state.w;
float metrics_height = (float)gl_state.h;
float horiBearingX = (float)gl_state.x;
float horiBearingY = (float)-gl_state.y;
float vertBearingX = p_slot->metrics.horiBearingX / 64.0f - p_slot->metrics.horiAdvance / 64.0f / 2;
float vertBearingY = (p_slot->metrics.vertAdvance / 64.0f - p_slot->metrics.height / 64.0f) / 2;
float tmpf = Math::round(metrics_width * 64);
p_slot->metrics.width = (FT_Pos)tmpf;
tmpf = Math::round(metrics_height * 64);
p_slot->metrics.height = (FT_Pos)tmpf;
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.");
tvg::Picture *picture = tvg::Picture::gen();
tvg::Result res = picture->load(gl_state.xml_code.get_data(), gl_state.xml_code.length(), "svg+xml", nullptr, false);
if (res != tvg::Result::Success) {
tvg::Paint::rel(picture);
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph rendering).");
}
res = picture->size(gl_state.w, gl_state.h);
if (res != tvg::Result::Success) {
tvg::Paint::rel(picture);
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to resize SVG document.");
}
res = picture->transform(gl_state.m);
if (res != tvg::Result::Success) {
tvg::Paint::rel(picture);
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::ColorSpace::ARGB8888S);
if (res != tvg::Result::Success) {
tvg::Paint::rel(picture);
ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to create SVG canvas.");
}
res = sw_canvas->add(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_FREETYPE_ENABLED
#endif // MODULE_SVG_ENABLED

View file

@ -0,0 +1,84 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "core/os/mutex.h"
#include "core/string/ustring.h"
#include "core/templates/hash_map.h"
#include "core/typedefs.h"
#include "modules/modules_enabled.gen.h" // For svg, freetype.
#ifdef MODULE_SVG_ENABLED
#ifdef MODULE_FREETYPE_ENABLED
#include <freetype/freetype.h>
#include <freetype/otsvg.h>
#include <ft2build.h>
#include <thorvg.h>
struct GL_State {
bool ready = false;
float x = 0;
float y = 0;
float w = 0;
float h = 0;
CharString xml_code;
tvg::Matrix m;
};
struct TVG_NodeCache {
uint64_t document_offset;
uint64_t body_offset;
};
struct TVG_DocumentCache {
String xml_body;
double embox_x;
double embox_y;
HashMap<int64_t, Vector<TVG_NodeCache>> node_caches;
};
struct TVG_State {
Mutex mutex;
HashMap<uint32_t, GL_State> glyph_map;
HashMap<FT_Byte *, TVG_DocumentCache> document_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_FREETYPE_ENABLED
#endif // MODULE_SVG_ENABLED