Skip to content

Instantly share code, notes, and snippets.

@migueldeicaza
Last active August 16, 2025 16:39
Show Gist options
  • Select an option

  • Save migueldeicaza/2e701f9ce2e53a422f66cfe5c0fa0fff to your computer and use it in GitHub Desktop.

Select an option

Save migueldeicaza/2e701f9ce2e53a422f66cfe5c0fa0fff to your computer and use it in GitHub Desktop.
Patch to use CoreText in Godot instead of OpenType.
diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub
index 1167fbc187..fb90e8689d 100644
--- a/modules/text_server_adv/SCsub
+++ b/modules/text_server_adv/SCsub
@@ -12,8 +12,13 @@ env_text_server_adv = env_modules.Clone()
thirdparty_obj = []
freetype_enabled = "freetype" in env.module_list
+coretext_enabled = env.get("coretext", False) and env["platform"] == "macos"
msdfgen_enabled = "msdfgen" in env.module_list
+if coretext_enabled:
+ env_text_server_adv.Append(CPPDEFINES=["CORETEXT_ENABLED"])
+ env_text_server_adv.Append(FRAMEWORKS=["CoreText", "CoreGraphics", "CoreFoundation"])
+
if "svg" in env.module_list:
env_text_server_adv.Prepend(
CPPEXTPATH=[
@@ -118,6 +123,13 @@ if env["builtin_harfbuzz"]:
thirdparty_sources += [
"src/hb-graphite2.cc",
]
+
+ if coretext_enabled:
+ thirdparty_sources += [
+ "src/hb-coretext.cc",
+ "src/hb-coretext-font.cc",
+ "src/hb-coretext-shape.cc",
+ ]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
env_harfbuzz.Prepend(CPPEXTPATH=["#thirdparty/harfbuzz/src"])
@@ -151,6 +163,14 @@ if env["builtin_harfbuzz"]:
if env["builtin_graphite"] and env["graphite"]:
env_harfbuzz.Prepend(CPPEXTPATH=["#thirdparty/graphite/include"])
env_harfbuzz.Append(CCFLAGS=["-DGRAPHITE2_STATIC"])
+
+ if coretext_enabled:
+ env_harfbuzz.Append(
+ CCFLAGS=[
+ "-DHAVE_CORETEXT",
+ ]
+ )
+ env_harfbuzz.Append(FRAMEWORKS=["CoreText", "CoreGraphics", "CoreFoundation"])
if env["platform"] in ["android", "linuxbsd", "web"]:
env_harfbuzz.Append(CCFLAGS=["-DHAVE_PTHREAD"])
@@ -533,7 +553,13 @@ if env["builtin_freetype"] and freetype_enabled:
if env["builtin_graphite"] and freetype_enabled and env["graphite"]:
env_text_server_adv.Prepend(CPPEXTPATH=["#thirdparty/graphite/include"])
-env_text_server_adv.add_source_files(module_obj, "*.cpp")
+if coretext_enabled and not freetype_enabled:
+ # Exclude SVG-in-OT file when using CoreText without FreeType since it depends on FreeType
+ source_files = Glob("*.cpp")
+ filtered_sources = [f for f in source_files if not str(f).endswith("thorvg_svg_in_ot.cpp")]
+ env_text_server_adv.add_source_files(module_obj, filtered_sources)
+else:
+ 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.
diff --git a/modules/text_server_adv/config.py b/modules/text_server_adv/config.py
index 08051398d1..f0d9990e06 100644
--- a/modules/text_server_adv/config.py
+++ b/modules/text_server_adv/config.py
@@ -1,4 +1,5 @@
def can_build(env, platform):
+ # Always include FreeType dependency, CoreText is optional on macOS
env.module_add_dependencies("text_server_adv", ["freetype", "msdfgen", "svg"], True)
return True
@@ -6,9 +7,14 @@ def can_build(env, platform):
def get_opts(platform):
from SCons.Variables import BoolVariable
- return [
+ opts = [
BoolVariable("graphite", "Enable SIL Graphite smart fonts support", True),
]
+
+ if platform == "macos":
+ opts.append(BoolVariable("coretext", "Use Apple's CoreText instead of FreeType for font rendering", False))
+
+ return opts
def configure(env):
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index b8a9e44aaa..a066dedfc5 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -65,7 +65,7 @@ using namespace godot;
// Thirdparty headers.
-#ifdef MODULE_MSDFGEN_ENABLED
+#if defined(MODULE_MSDFGEN_ENABLED) && defined(MODULE_FREETYPE_ENABLED)
#include <core/EdgeHolder.h>
#include <core/ShapeDistanceFinder.h>
#include <core/contour-combiners.h>
@@ -353,10 +353,10 @@ bool TextServerAdvanced::_has_feature(Feature p_feature) const {
case FEATURE_KASHIDA_JUSTIFICATION:
case FEATURE_BREAK_ITERATORS:
case FEATURE_FONT_BITMAP:
-#ifdef MODULE_FREETYPE_ENABLED
+#if defined(MODULE_FREETYPE_ENABLED) || defined(CORETEXT_ENABLED)
case FEATURE_FONT_DYNAMIC:
#endif
-#ifdef MODULE_MSDFGEN_ENABLED
+#if defined(MODULE_MSDFGEN_ENABLED) && defined(MODULE_FREETYPE_ENABLED)
case FEATURE_FONT_MSDF:
#endif
case FEATURE_FONT_VARIABLE:
@@ -381,10 +381,10 @@ String TextServerAdvanced::_get_name() 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
+#if defined(MODULE_FREETYPE_ENABLED) || defined(CORETEXT_ENABLED)
interface_features |= FEATURE_FONT_DYNAMIC;
#endif
-#ifdef MODULE_MSDFGEN_ENABLED
+#if defined(MODULE_MSDFGEN_ENABLED) && defined(MODULE_FREETYPE_ENABLED)
interface_features |= FEATURE_FONT_MSDF;
#endif
@@ -905,7 +905,7 @@ _FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::find_
return ret;
}
-#ifdef MODULE_MSDFGEN_ENABLED
+#if defined(MODULE_MSDFGEN_ENABLED) && defined(MODULE_FREETYPE_ENABLED)
struct MSContext {
msdfgen::Point2 position;
@@ -1210,6 +1210,90 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma
}
#endif
+#ifdef CORETEXT_ENABLED
+_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_coretext_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, CGImageRef p_image, const Vector2 &p_advance, const CGRect &p_bbox) const {
+ FontGlyph chr;
+ chr.advance = p_advance;
+ chr.found = true;
+
+ size_t w = CGImageGetWidth(p_image);
+ size_t h = CGImageGetHeight(p_image);
+
+ if (w == 0 || h == 0) {
+ chr.texture_idx = -1;
+ chr.uv_rect = Rect2();
+ chr.rect = Rect2();
+ return chr;
+ }
+
+ int mw = w + p_rect_margin * 4;
+ int mh = h + p_rect_margin * 4;
+
+ FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, 2, Image::FORMAT_LA8, mw, mh, false);
+ ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph());
+
+ // Ensure we have enough textures
+ if (tex_pos.index >= p_data->textures.size()) {
+ p_data->textures.resize(tex_pos.index + 1);
+ }
+
+ ShelfPackTexture &tex = p_data->textures.write[tex_pos.index];
+
+ if (!tex.image.is_valid()) {
+ tex.image.instantiate();
+ tex.image->initialize_data(tex.texture_w, tex.texture_h, false, Image::FORMAT_LA8);
+ }
+
+ // Get the image data from CoreGraphics
+ CFDataRef imageData = CGDataProviderCopyData(CGImageGetDataProvider(p_image));
+ if (imageData) {
+ const uint8_t *source_data = CFDataGetBytePtr(imageData);
+ size_t bytes_per_row = CGImageGetBytesPerRow(p_image);
+ size_t bits_per_pixel = CGImageGetBitsPerPixel(p_image);
+ size_t bits_per_component = CGImageGetBitsPerComponent(p_image);
+
+ // Debug: Check the actual image format
+ // Expected: 8 bits per pixel (grayscale), 8 bits per component
+ if (bits_per_pixel == 8 && bits_per_component == 8) {
+ // Copy the CoreText rendered glyph to our texture (grayscale data used as alpha)
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ int src_index = y * bytes_per_row + x;
+ uint8_t grayscale = source_data[src_index];
+
+ // Use grayscale value as alpha (white text on transparent background)
+ tex.image->set_pixel(tex_pos.x + x + p_rect_margin, tex_pos.y + y + p_rect_margin, Color(1.0, 1.0, 1.0, grayscale / 255.0));
+ }
+ }
+ } else {
+ // Fallback for unexpected format - assume it's grayscale
+ ERR_PRINT(vformat("Unexpected CoreText image format: %d bits per pixel, %d bits per component", (int)bits_per_pixel, (int)bits_per_component));
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ int src_index = y * bytes_per_row + x;
+ uint8_t grayscale = source_data[src_index];
+
+ // Use grayscale value as alpha (white text on transparent background)
+ tex.image->set_pixel(tex_pos.x + x + p_rect_margin, tex_pos.y + y + p_rect_margin, Color(1.0, 1.0, 1.0, grayscale / 255.0));
+ }
+ }
+ }
+
+ CFRelease(imageData);
+ }
+
+ tex.dirty = true;
+
+ 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);
+ // Use CoreText bbox directly as bearing values
+ // bbox.origin gives us the bottom-left corner of the glyph relative to its origin
+ chr.rect.position = Vector2(p_bbox.origin.x - p_rect_margin, -(p_bbox.origin.y + p_bbox.size.height) - p_rect_margin) * p_data->scale;
+ chr.rect.size = chr.uv_rect.size * p_data->scale;
+ return chr;
+}
+#endif
+
/*************************************************************************/
/* Font Cache */
/*************************************************************************/
@@ -1232,8 +1316,68 @@ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data, const Vector2i
return true;
}
-#ifdef MODULE_FREETYPE_ENABLED
FontGlyph gl;
+
+ if (_should_use_coretext()) {
+#ifdef CORETEXT_ENABLED
+ // CoreText backend implementation
+ if (fd->ct_font) {
+ CGGlyph cg_glyph = (CGGlyph)glyph_index;
+ CGSize advance;
+ CTFontGetAdvancesForGlyphs(fd->ct_font, kCTFontOrientationDefault, &cg_glyph, &advance, 1);
+
+ // Get glyph bounding box
+ CGRect bbox;
+ CTFontGetBoundingRectsForGlyphs(fd->ct_font, kCTFontOrientationDefault, &cg_glyph, &bbox, 1);
+
+ if (bbox.size.width > 0 && bbox.size.height > 0) {
+ // Create bitmap context to render glyph
+ int width = (int)ceil(bbox.size.width) + 4; // Add padding
+ int height = (int)ceil(bbox.size.height) + 4;
+
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
+ CGContextRef context = CGBitmapContextCreate(nullptr, width, height, 8, width, colorSpace, kCGImageAlphaNone);
+ CGColorSpaceRelease(colorSpace);
+
+ if (context) {
+ // Set up context for rendering
+ CGContextSetTextMatrix(context, CGAffineTransformIdentity);
+ CGContextSetTextDrawingMode(context, kCGTextFill);
+ CGContextSetGrayFillColor(context, 1.0, 1.0); // White fill with full alpha
+
+ // Position and render the glyph
+ CGPoint position = CGPointMake(-bbox.origin.x + 2, -bbox.origin.y + 2);
+ CTFontDrawGlyphs(fd->ct_font, &cg_glyph, &position, 1, context);
+
+ // Create bitmap from context
+ CGImageRef image = CGBitmapContextCreateImage(context);
+ if (image) {
+ gl = rasterize_coretext_bitmap(fd, 1, image, Vector2(advance.width, advance.height), bbox);
+ CGImageRelease(image);
+ }
+
+ CGContextRelease(context);
+ }
+
+ if (!gl.found) {
+ // Fallback for failed rasterization
+ gl.advance = Vector2(advance.width, advance.height);
+ gl.found = true;
+ }
+ } else {
+ // Handle zero-size glyphs (like spaces)
+ gl.advance = Vector2(advance.width, advance.height) * fd->scale;
+ gl.found = true;
+ }
+ }
+
+ E = fd->glyph_map.insert(p_glyph, gl);
+ r_glyph = E->value;
+ return gl.found;
+#endif
+ } else {
+#ifdef MODULE_FREETYPE_ENABLED
+ // FreeType backend implementation
if (fd->face) {
FT_Int32 flags = FT_LOAD_DEFAULT;
@@ -1332,7 +1476,7 @@ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data, const Vector2i
}
if (!error) {
if (p_font_data->msdf) {
-#ifdef MODULE_MSDFGEN_ENABLED
+#if defined(MODULE_MSDFGEN_ENABLED) && defined(MODULE_FREETYPE_ENABLED)
gl = rasterize_msdf(p_font_data, fd, p_font_data->msdf_range, rect_range, &slot->outline, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0);
#else
fd->glyph_map[p_glyph] = FontGlyph();
@@ -1376,6 +1520,9 @@ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data, const Vector2i
return gl.found;
}
#endif
+ } // End runtime backend selection
+
+ // Fallback if no backend handled the glyph
E = fd->glyph_map.insert(p_glyph, FontGlyph());
r_glyph = E->value;
return false;
@@ -1401,6 +1548,104 @@ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_font_data, const
fd->size = p_size;
if (p_font_data->data_ptr && (p_font_data->data_size > 0)) {
// Init dynamic font.
+ if (_should_use_coretext()) {
+#ifdef CORETEXT_ENABLED
+ // Use CoreText backend
+ // Create CoreText font from data
+ CGDataProviderRef data_provider = CGDataProviderCreateWithData(nullptr, p_font_data->data_ptr, p_font_data->data_size, nullptr);
+ printf("Created a data provider with size: %zu bytes\n", p_font_data->data_size);
+ if (!data_provider) {
+ memdelete(fd);
+ if (p_silent) {
+ return false;
+ } else {
+ ERR_FAIL_V_MSG(false, "CoreText: Failed to create data provider!");
+ }
+ }
+
+ fd->cg_font = CGFontCreateWithDataProvider(data_provider);
+ CGDataProviderRelease(data_provider);
+
+ if (!fd->cg_font) {
+ memdelete(fd);
+ if (p_silent) {
+ return false;
+ } else {
+ ERR_FAIL_V_MSG(false, "CoreText: Failed to create CGFont!");
+ }
+ }
+
+ double sz = double(fd->size.x) / 64.0;
+ if (p_font_data->msdf) {
+ sz = p_font_data->msdf_source_size;
+ }
+
+ fd->ct_font = CTFontCreateWithGraphicsFont(fd->cg_font, sz, nullptr, nullptr);
+ if (!fd->ct_font) {
+ CFRelease(fd->cg_font);
+ fd->cg_font = nullptr;
+ memdelete(fd);
+ if (p_silent) {
+ return false;
+ } else {
+ ERR_FAIL_V_MSG(false, "CoreText: Failed to create CTFont!");
+ }
+ }
+
+ fd->scale = 1.0; // CoreText handles scaling internally
+
+ // Create HarfBuzz font
+ fd->hb_handle = hb_coretext_font_create(fd->ct_font);
+ // Set HarfBuzz font scale - CoreText units need to be converted to HarfBuzz units (26.6 fractional pixels)
+ hb_font_set_scale(fd->hb_handle, sz * 64, sz * 64);
+
+ // Get font metrics
+ fd->ascent = CTFontGetAscent(fd->ct_font);
+ fd->descent = CTFontGetDescent(fd->ct_font);
+ fd->underline_position = -CTFontGetUnderlinePosition(fd->ct_font);
+ fd->underline_thickness = CTFontGetUnderlineThickness(fd->ct_font);
+
+ if (!p_font_data->face_init) {
+ // Get font name
+ CFStringRef font_name = CTFontCopyFamilyName(fd->ct_font);
+ if (font_name) {
+ char buffer[256];
+ if (CFStringGetCString(font_name, buffer, sizeof(buffer), kCFStringEncodingUTF8)) {
+ p_font_data->font_name = String::utf8(buffer);
+ }
+ CFRelease(font_name);
+ }
+
+ // Get style name
+ CFStringRef style_name = CTFontCopyName(fd->ct_font, kCTFontStyleNameKey);
+ if (style_name) {
+ char buffer[256];
+ if (CFStringGetCString(style_name, buffer, sizeof(buffer), kCFStringEncodingUTF8)) {
+ p_font_data->style_name = String::utf8(buffer);
+ }
+ CFRelease(style_name);
+ }
+
+ // Get font weight and style flags
+ 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;
+
+ CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(fd->ct_font);
+ if (traits & kCTFontTraitBold) {
+ p_font_data->style_flags.set_flag(FONT_BOLD);
+ }
+ if (traits & kCTFontTraitItalic) {
+ p_font_data->style_flags.set_flag(FONT_ITALIC);
+ }
+ if (traits & kCTFontTraitMonoSpace) {
+ p_font_data->style_flags.set_flag(FONT_FIXED_WIDTH);
+ }
+
+ p_font_data->face_init = true;
+ }
+#endif
+ } else {
#ifdef MODULE_FREETYPE_ENABLED
int error = 0;
{
@@ -1415,7 +1660,7 @@ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_font_data, const
ERR_FAIL_V_MSG(false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
}
}
-#ifdef MODULE_SVG_ENABLED
+#if defined(MODULE_SVG_ENABLED) && defined(MODULE_FREETYPE_ENABLED)
FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks());
#endif
}
@@ -1918,12 +2163,14 @@ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_font_data, const
hb_font_set_variations(fd->hb_handle, hb_vars.is_empty() ? nullptr : &hb_vars[0], hb_vars.size());
FT_Done_MM_Var(ft_library, amaster);
}
-#else
+#endif
+ } // End FreeType backend
+#if !defined(MODULE_FREETYPE_ENABLED) && !defined(CORETEXT_ENABLED)
memdelete(fd);
if (p_silent) {
return false;
} else {
- ERR_FAIL_V_MSG(false, "FreeType: Can't load dynamic font, engine is compiled without FreeType support!");
+ ERR_FAIL_V_MSG(false, "No font backend available (FreeType or CoreText)!");
}
#endif
} else {
@@ -2101,7 +2348,7 @@ 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
+#if defined(MODULE_SVG_ENABLED) && defined(MODULE_FREETYPE_ENABLED)
FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks());
#endif
}
@@ -2128,6 +2375,24 @@ int64_t TextServerAdvanced::_font_get_face_count(const RID &p_font_rid) const {
FT_Done_Face(tmp_face);
}
#endif
+
+#ifdef CORETEXT_ENABLED
+ // CoreText typically doesn't have multiple faces in a single font file
+ // but we can check by attempting to create the font
+ CFDataRef font_data = CFDataCreate(nullptr, (const UInt8 *)fd->data_ptr, fd->data_size);
+ if (font_data) {
+ CGDataProviderRef data_provider = CGDataProviderCreateWithCFData(font_data);
+ if (data_provider) {
+ CGFontRef cg_font = CGFontCreateWithDataProvider(data_provider);
+ if (cg_font) {
+ face_count = 1; // CoreText fonts typically have 1 face
+ CFRelease(cg_font);
+ }
+ CFRelease(data_provider);
+ }
+ CFRelease(font_data);
+ }
+#endif
}
return face_count;
@@ -2980,6 +3245,11 @@ void TextServerAdvanced::_font_set_scale(const RID &p_font_rid, int64_t p_size,
if (ffsd->face) {
return; // Do not override scale for dynamic fonts, it's calculated automatically.
}
+#endif
+#ifdef CORETEXT_ENABLED
+ if (ffsd->ct_font) {
+ return; // Do not override scale for dynamic fonts, it's calculated automatically.
+ }
#endif
ffsd->scale = p_scale;
}
@@ -8182,6 +8452,21 @@ TextServerAdvanced::TextServerAdvanced() {
_insert_feature_sets();
_bmp_create_font_funcs();
_update_settings();
+
+ // Check environment variable for backend selection
+#ifdef CORETEXT_ENABLED
+ const char* use_coretext_env = getenv("USE_CORETEXT");
+ if (use_coretext_env != nullptr) {
+ String env_value = String(use_coretext_env).to_lower();
+ use_coretext_backend = (env_value == "true" || env_value == "1" || env_value == "yes");
+ } else {
+ // Default to FreeType if environment variable is not set
+ use_coretext_backend = false;
+ }
+#else
+ use_coretext_backend = false;
+#endif
+
ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &TextServerAdvanced::_update_settings));
}
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index 254430037f..0de40799f3 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -124,8 +124,16 @@ using namespace godot;
#include <hb-ot.h>
#endif
+#ifdef CORETEXT_ENABLED
+#include <CoreText/CoreText.h>
+#include <CoreGraphics/CoreGraphics.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <hb-coretext.h>
+#endif
+
#include <hb-icu.h>
#include <hb.h>
+#include <hb-ot.h>
/*************************************************************************/
@@ -185,6 +193,20 @@ class TextServerAdvanced : public TextServerExtension {
mutable FT_Library ft_library = nullptr;
#endif
+#ifdef CORETEXT_ENABLED
+ // CoreText doesn't need a global library instance like FreeType
+#endif
+
+ // Runtime backend selection
+ mutable bool use_coretext_backend = false;
+ _FORCE_INLINE_ bool _should_use_coretext() const {
+#ifdef CORETEXT_ENABLED
+ return use_coretext_backend;
+#else
+ return false;
+#endif
+ }
+
const int rect_range = 1;
struct FontTexturePosition {
@@ -301,6 +323,11 @@ class TextServerAdvanced : public TextServerExtension {
FT_StreamRec stream;
#endif
+#ifdef CORETEXT_ENABLED
+ CTFontRef ct_font = nullptr;
+ CGFontRef cg_font = nullptr;
+#endif
+
~FontForSizeAdvanced() {
if (hb_handle != nullptr) {
hb_font_destroy(hb_handle);
@@ -309,6 +336,14 @@ class TextServerAdvanced : public TextServerExtension {
if (face != nullptr) {
FT_Done_Face(face);
}
+#endif
+#ifdef CORETEXT_ENABLED
+ if (ct_font != nullptr) {
+ CFRelease(ct_font);
+ }
+ if (cg_font != nullptr) {
+ CFRelease(cg_font);
+ }
#endif
}
};
@@ -382,11 +417,14 @@ class TextServerAdvanced : public TextServerExtension {
};
_FORCE_INLINE_ FontTexturePosition 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;
-#ifdef MODULE_MSDFGEN_ENABLED
+#if defined(MODULE_MSDFGEN_ENABLED) && defined(MODULE_FREETYPE_ENABLED)
_FORCE_INLINE_ FontGlyph rasterize_msdf(FontAdvanced *p_font_data, FontForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *p_outline, const Vector2 &p_advance) const;
#endif
#ifdef MODULE_FREETYPE_ENABLED
_FORCE_INLINE_ FontGlyph rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap p_bitmap, int p_yofs, int p_xofs, const Vector2 &p_advance, bool p_bgra) const;
+#endif
+#ifdef CORETEXT_ENABLED
+ _FORCE_INLINE_ FontGlyph rasterize_coretext_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, CGImageRef p_image, const Vector2 &p_advance, const CGRect &p_bbox) const;
#endif
bool _ensure_glyph(FontAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph, FontGlyph &r_glyph, uint32_t p_oversampling = 0) const;
bool _ensure_cache_for_size(FontAdvanced *p_font_data, const Vector2i &p_size, FontForSizeAdvanced *&r_cache_for_size, bool p_silent = false, uint32_t p_oversampling = 0) const;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment