summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbruvzg <7645683+bruvzg@users.noreply.github.com>2020-12-27 15:30:33 +0200
committerbruvzg <7645683+bruvzg@users.noreply.github.com>2021-08-27 15:43:18 +0300
commit4c3f7d1290311456519ca2416590c7e62465b7f2 (patch)
treed8b2373cb7bcb4b7aff47d81e35f36a0b9bf993c
parent00268e37a0e40630ce76b5144cb272c4cc73a672 (diff)
Makes FontData importable resource.
Adds multi-channel SDF font texture generation and rendering support. Adds per-font oversampling support. Adds FontData import plugins (for dynamic fonts, BMFonts and monospaced image fonts), font texture cache pre-generation and loading. Adds BMFont binary format and outline support.
-rw-r--r--COPYRIGHT.txt5
-rw-r--r--SConstruct1
-rw-r--r--doc/classes/CanvasItem.xml14
-rw-r--r--doc/classes/Font.xml40
-rw-r--r--doc/classes/FontData.xml587
-rw-r--r--doc/classes/TextParagraph.xml4
-rw-r--r--doc/classes/TextServer.xml651
-rw-r--r--doc/classes/TreeItem.xml17
-rw-r--r--editor/editor_fonts.cpp400
-rw-r--r--editor/editor_node.cpp19
-rw-r--r--editor/editor_node.h2
-rw-r--r--editor/import/dynamicfont_import_settings.cpp1889
-rw-r--r--editor/import/dynamicfont_import_settings.h167
-rw-r--r--editor/import/resource_importer_bmfont.cpp797
-rw-r--r--editor/import/resource_importer_bmfont.h56
-rw-r--r--editor/import/resource_importer_dynamicfont.cpp304
-rw-r--r--editor/import/resource_importer_dynamicfont.h71
-rw-r--r--editor/import/resource_importer_imagefont.cpp162
-rw-r--r--editor/import/resource_importer_imagefont.h58
-rw-r--r--editor/plugins/editor_preview_plugins.cpp63
-rw-r--r--editor/plugins/font_editor_plugin.cpp247
-rw-r--r--editor/plugins/font_editor_plugin.h33
-rw-r--r--methods.py11
-rw-r--r--modules/gdnative/gdnative_api.json4
-rw-r--r--modules/gdnative/include/text/godot_text.h158
-rw-r--r--modules/gdnative/text/text_server_gdnative.cpp534
-rw-r--r--modules/gdnative/text/text_server_gdnative.h174
-rw-r--r--modules/msdfgen/SCsub66
-rw-r--r--modules/msdfgen/config.py6
-rw-r--r--modules/msdfgen/register_types.cpp35
-rw-r--r--modules/msdfgen/register_types.h37
-rw-r--r--modules/text_server_adv/SCsub10
-rw-r--r--modules/text_server_adv/bitmap_font_adv.cpp585
-rw-r--r--modules/text_server_adv/bitmap_font_adv.h123
-rw-r--r--modules/text_server_adv/dynamic_font_adv.cpp1030
-rw-r--r--modules/text_server_adv/dynamic_font_adv.h195
-rw-r--r--modules/text_server_adv/font_adv.h115
-rw-r--r--modules/text_server_adv/text_server_adv.cpp2631
-rw-r--r--modules/text_server_adv/text_server_adv.h360
-rw-r--r--modules/text_server_fb/SCsub22
-rw-r--r--modules/text_server_fb/bitmap_font_fb.cpp352
-rw-r--r--modules/text_server_fb/bitmap_font_fb.h111
-rw-r--r--modules/text_server_fb/dynamic_font_fb.cpp713
-rw-r--r--modules/text_server_fb/dynamic_font_fb.h173
-rw-r--r--modules/text_server_fb/font_fb.h101
-rw-r--r--modules/text_server_fb/text_server_fb.cpp2180
-rw-r--r--modules/text_server_fb/text_server_fb.h327
-rw-r--r--scene/gui/label.cpp23
-rw-r--r--scene/gui/line_edit.cpp5
-rw-r--r--scene/gui/rich_text_effect.h6
-rw-r--r--scene/gui/rich_text_label.cpp8
-rw-r--r--scene/gui/text_edit.cpp2
-rw-r--r--scene/gui/tree.cpp23
-rw-r--r--scene/gui/tree.h4
-rw-r--r--scene/main/canvas_item.cpp7
-rw-r--r--scene/main/canvas_item.h1
-rw-r--r--scene/main/window.cpp4
-rw-r--r--scene/register_scene_types.cpp22
-rw-r--r--scene/resources/default_theme/default_theme.cpp3
-rw-r--r--scene/resources/font.cpp1580
-rw-r--r--scene/resources/font.h286
-rw-r--r--scene/resources/text_line.cpp8
-rw-r--r--scene/resources/text_paragraph.cpp8
-rw-r--r--servers/rendering/renderer_canvas_cull.cpp34
-rw-r--r--servers/rendering/renderer_canvas_cull.h1
-rw-r--r--servers/rendering/renderer_canvas_render.h5
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp8
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.h8
-rw-r--r--servers/rendering/renderer_rd/shaders/canvas.glsl37
-rw-r--r--servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl2
-rw-r--r--servers/rendering/rendering_server_default.h1
-rw-r--r--servers/rendering_server.h1
-rw-r--r--servers/text_server.cpp223
-rw-r--r--servers/text_server.h221
-rw-r--r--tests/test_text_server.h40
-rw-r--r--thirdparty/README.md13
-rw-r--r--thirdparty/msdfgen/CHANGELOG.md82
-rw-r--r--thirdparty/msdfgen/LICENSE.txt21
-rw-r--r--thirdparty/msdfgen/core/Bitmap.h50
-rw-r--r--thirdparty/msdfgen/core/Bitmap.hpp117
-rw-r--r--thirdparty/msdfgen/core/BitmapRef.hpp43
-rw-r--r--thirdparty/msdfgen/core/Contour.cpp90
-rw-r--r--thirdparty/msdfgen/core/Contour.h34
-rw-r--r--thirdparty/msdfgen/core/EdgeColor.h18
-rw-r--r--thirdparty/msdfgen/core/EdgeHolder.cpp77
-rw-r--r--thirdparty/msdfgen/core/EdgeHolder.h41
-rw-r--r--thirdparty/msdfgen/core/MSDFErrorCorrection.cpp495
-rw-r--r--thirdparty/msdfgen/core/MSDFErrorCorrection.h56
-rw-r--r--thirdparty/msdfgen/core/Projection.cpp42
-rw-r--r--thirdparty/msdfgen/core/Projection.h37
-rw-r--r--thirdparty/msdfgen/core/Scanline.cpp125
-rw-r--r--thirdparty/msdfgen/core/Scanline.h55
-rw-r--r--thirdparty/msdfgen/core/Shape.cpp183
-rw-r--r--thirdparty/msdfgen/core/Shape.h55
-rw-r--r--thirdparty/msdfgen/core/ShapeDistanceFinder.h37
-rw-r--r--thirdparty/msdfgen/core/ShapeDistanceFinder.hpp56
-rw-r--r--thirdparty/msdfgen/core/SignedDistance.cpp30
-rw-r--r--thirdparty/msdfgen/core/SignedDistance.h25
-rw-r--r--thirdparty/msdfgen/core/Vector2.cpp146
-rw-r--r--thirdparty/msdfgen/core/Vector2.h66
-rw-r--r--thirdparty/msdfgen/core/arithmetics.hpp63
-rw-r--r--thirdparty/msdfgen/core/bitmap-interpolation.hpp25
-rw-r--r--thirdparty/msdfgen/core/contour-combiners.cpp133
-rw-r--r--thirdparty/msdfgen/core/contour-combiners.h47
-rw-r--r--thirdparty/msdfgen/core/edge-coloring.cpp499
-rw-r--r--thirdparty/msdfgen/core/edge-coloring.h29
-rw-r--r--thirdparty/msdfgen/core/edge-segments.cpp504
-rw-r--r--thirdparty/msdfgen/core/edge-segments.h122
-rw-r--r--thirdparty/msdfgen/core/edge-selectors.cpp261
-rw-r--r--thirdparty/msdfgen/core/edge-selectors.h117
-rw-r--r--thirdparty/msdfgen/core/equation-solver.cpp77
-rw-r--r--thirdparty/msdfgen/core/equation-solver.h12
-rw-r--r--thirdparty/msdfgen/core/generator-config.h63
-rw-r--r--thirdparty/msdfgen/core/msdf-error-correction.cpp154
-rw-r--r--thirdparty/msdfgen/core/msdf-error-correction.h28
-rw-r--r--thirdparty/msdfgen/core/msdfgen.cpp288
-rw-r--r--thirdparty/msdfgen/core/pixel-conversion.hpp18
-rw-r--r--thirdparty/msdfgen/core/rasterization.cpp115
-rw-r--r--thirdparty/msdfgen/core/rasterization.h25
-rw-r--r--thirdparty/msdfgen/core/render-sdf.cpp108
-rw-r--r--thirdparty/msdfgen/core/render-sdf.h22
-rw-r--r--thirdparty/msdfgen/core/save-bmp.cpp169
-rw-r--r--thirdparty/msdfgen/core/save-bmp.h16
-rw-r--r--thirdparty/msdfgen/core/save-tiff.cpp190
-rw-r--r--thirdparty/msdfgen/core/save-tiff.h13
-rw-r--r--thirdparty/msdfgen/core/sdf-error-estimation.cpp192
-rw-r--r--thirdparty/msdfgen/core/sdf-error-estimation.h30
-rw-r--r--thirdparty/msdfgen/core/shape-description.cpp284
-rw-r--r--thirdparty/msdfgen/core/shape-description.h16
-rw-r--r--thirdparty/msdfgen/msdfgen.h65
130 files changed, 17422 insertions, 6468 deletions
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index 035e680fe1..4a38d0457c 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -377,6 +377,11 @@ Comment: YUV2RGB
Copyright: 2008-2011, Robin Watts
License: BSD-2-clause
+Files: ./thirdparty/msdfgen/
+Comment: Multi-channel signed distance field generator
+Copyright: 2016, Viktor Chlumsky
+License: MIT
+
Files: ./thirdparty/nanosvg/
Comment: NanoSVG
Copyright: 2013-2014, Mikko Mononen
diff --git a/SConstruct b/SConstruct
index 0ae7442e35..8feb9e61bb 100644
--- a/SConstruct
+++ b/SConstruct
@@ -156,6 +156,7 @@ opts.Add(BoolVariable("builtin_certs", "Use the built-in SSL certificates bundle
opts.Add(BoolVariable("builtin_embree", "Use the built-in Embree library", True))
opts.Add(BoolVariable("builtin_enet", "Use the built-in ENet library", True))
opts.Add(BoolVariable("builtin_freetype", "Use the built-in FreeType library", True))
+opts.Add(BoolVariable("builtin_msdfgen", "Use the built-in MSDFgen library", True))
opts.Add(BoolVariable("builtin_glslang", "Use the built-in glslang library", True))
opts.Add(BoolVariable("builtin_graphite", "Use the built-in Graphite library", True))
opts.Add(BoolVariable("builtin_harfbuzz", "Use the built-in HarfBuzz library", True))
diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml
index 2c92ce0185..4641bc52a4 100644
--- a/doc/classes/CanvasItem.xml
+++ b/doc/classes/CanvasItem.xml
@@ -106,6 +106,20 @@
Draws a [Mesh] in 2D, using the provided texture. See [MeshInstance2D] for related documentation.
</description>
</method>
+ <method name="draw_msdf_texture_rect_region">
+ <return type="void" />
+ <argument index="0" name="texture" type="Texture2D" />
+ <argument index="1" name="rect" type="Rect2" />
+ <argument index="2" name="src_rect" type="Rect2" />
+ <argument index="3" name="modulate" type="Color" default="Color(1, 1, 1, 1)" />
+ <argument index="4" name="outline" type="float" default="0.0" />
+ <argument index="5" name="pixel_range" type="float" default="4.0" />
+ <description>
+ Draws a textured rectangle region of the multi-channel signed distance field texture at a given position, optionally modulated by a color.
+ If [code]outline[/code] is positive, each alpha channel value of pixel in region is set to maximum value of true distance in the [code]outline[/code] radius.
+ Value of the [code]pixel_range[/code] should the same that was used during distance field texture generation.
+ </description>
+ </method>
<method name="draw_multiline">
<return type="void" />
<argument index="0" name="points" type="PackedVector2Array" />
diff --git a/doc/classes/Font.xml b/doc/classes/Font.xml
index 06dcaca846..aa70856e32 100644
--- a/doc/classes/Font.xml
+++ b/doc/classes/Font.xml
@@ -70,6 +70,12 @@
Add font data source to the set.
</description>
</method>
+ <method name="clear_data">
+ <return type="void" />
+ <description>
+ Removes all font data sourcers for the set.
+ </description>
+ </method>
<method name="draw_char" qualifiers="const">
<return type="float" />
<argument index="0" name="canvas_item" type="RID" />
@@ -151,6 +157,13 @@
Returns the number of font data sources.
</description>
</method>
+ <method name="get_data_rid" qualifiers="const">
+ <return type="RID" />
+ <argument index="0" name="idx" type="int" />
+ <description>
+ Returns TextServer RID of the font data resources.
+ </description>
+ </method>
<method name="get_descent" qualifiers="const">
<return type="float" />
<argument index="0" name="size" type="int" default="-1" />
@@ -180,15 +193,18 @@
</method>
<method name="get_spacing" qualifiers="const">
<return type="int" />
- <argument index="0" name="type" type="int" />
+ <argument index="0" name="spacing" type="int" enum="TextServer.SpacingType" />
<description>
- Returns the spacing for the given [code]type[/code] (see [enum SpacingType]).
+ Returns the spacing for the given [code]type[/code] (see [enum TextServer.SpacingType]).
</description>
</method>
<method name="get_string_size" qualifiers="const">
<return type="Vector2" />
<argument index="0" name="text" type="String" />
<argument index="1" name="size" type="int" default="-1" />
+ <argument index="2" name="align" type="int" enum="HAlign" default="0" />
+ <argument index="3" name="width" type="float" default="-1" />
+ <argument index="4" name="flags" type="int" default="3" />
<description>
Returns the size of a bounding box of a string, taking kerning and advance into account.
[b]Note:[/b] Real height of the string is context-dependent and can be significantly different from the value returned by [method get_height].
@@ -242,10 +258,10 @@
</method>
<method name="set_spacing">
<return type="void" />
- <argument index="0" name="type" type="int" />
+ <argument index="0" name="spacing" type="int" enum="TextServer.SpacingType" />
<argument index="1" name="value" type="int" />
<description>
- Sets the spacing for [code]type[/code] (see [enum SpacingType]) to [code]value[/code] in pixels (not relative to the font size).
+ Sets the spacing for [code]type[/code] (see [enum TextServer.SpacingType]) to [code]value[/code] in pixels (not relative to the font size).
</description>
</method>
<method name="update_changes">
@@ -256,19 +272,19 @@
</method>
</methods>
<members>
- <member name="extra_spacing_bottom" type="int" setter="set_spacing" getter="get_spacing" default="0">
+ <member name="base_size" type="int" setter="set_base_size" getter="get_base_size" default="16">
+ Default font size.
+ </member>
+ <member name="spacing_bottom" type="int" setter="set_spacing" getter="get_spacing" default="0">
Extra spacing at the bottom of the line in pixels.
</member>
- <member name="extra_spacing_top" type="int" setter="set_spacing" getter="get_spacing" default="0">
+ <member name="spacing_top" type="int" setter="set_spacing" getter="get_spacing" default="0">
Extra spacing at the top of the line in pixels.
</member>
+ <member name="variation_coordinates" type="Dictionary" setter="set_variation_coordinates" getter="get_variation_coordinates" default="{}">
+ Default font [url=https://docs.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg]variation coordinates[/url].
+ </member>
</members>
<constants>
- <constant name="SPACING_TOP" value="0" enum="SpacingType">
- Spacing at the top of the line.
- </constant>
- <constant name="SPACING_BOTTOM" value="1" enum="SpacingType">
- Spacing at the bottom of the line.
- </constant>
</constants>
</class>
diff --git a/doc/classes/FontData.xml b/doc/classes/FontData.xml
index 7a845a698f..72af7ca485 100644
--- a/doc/classes/FontData.xml
+++ b/doc/classes/FontData.xml
@@ -1,122 +1,179 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="FontData" inherits="Resource" version="4.0">
<brief_description>
- Font data source, file or memory buffer.
+ Font source data and prerendered glyph cache, imported from dynamic or bitmap font.
+ Supported font formats:
+ - Dynamic font importer: TrueType (.ttf), OpenType (.otf), WOFF (.woff), Type 1 (.pfb, .pfm).
+ - Bitmap font importer: AngelCode BMFont (.fnt, .font), text and binary (version 3) format variants.
+ - Monospace image font importer: All supported image formats.
</brief_description>
<description>
- Built-in text servers support font data sources of the following formats:
- - Bitmap fonts in the [url=https://www.angelcode.com/products/bmfont/]BMFont[/url] format. Handles [code].fnt, *.font[/code] fonts containing texture atlases. Non-scalable. Supports distance fields. Complex text shaping support is limited.
- - Dynamic fonts using the [url=https://www.freetype.org/]FreeType[/url] and [url=https://github.com/silnrsi/graphite/]Graphite[/url] library for rasterization. Handles [code]*.ttf, *.otf[/code] fonts. Scalable. Doesn't support distance fields. Supports complex text shaping and OpenType features.
</description>
<tutorials>
</tutorials>
<methods>
- <method name="bitmap_add_char">
+ <method name="clear_cache">
<return type="void" />
- <argument index="0" name="char" type="int" />
- <argument index="1" name="texture_idx" type="int" />
- <argument index="2" name="rect" type="Rect2" />
- <argument index="3" name="align" type="Vector2" />
- <argument index="4" name="advance" type="float" />
<description>
- Adds a character to the font, where [code]character[/code] is the Unicode value, [code]texture[/code] is the texture index, [code]rect[/code] is the region in the texture (in pixels!), [code]align[/code] is the (optional) alignment for the character and [code]advance[/code] is the (optional) advance.
+ Removes all font cache entries.
</description>
</method>
- <method name="bitmap_add_kerning_pair">
+ <method name="clear_glyphs">
<return type="void" />
- <argument index="0" name="A" type="int" />
- <argument index="1" name="B" type="int" />
- <argument index="2" name="kerning" type="int" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="Vector2i" />
<description>
- Adds a kerning pair to the bitmap font as a difference. Kerning pairs are special cases where a typeface advance is determined by the next character.
+ Removes all rendered glyphs information from the cache entry. Note: This function will not remove textures associated with the glyphs, use [method remove_texture] to remove them manually.
</description>
</method>
- <method name="bitmap_add_texture">
+ <method name="clear_kerning_map">
<return type="void" />
- <argument index="0" name="texture" type="Texture" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="int" />
<description>
- Adds a texture to the bitmap font.
+ Removes all kerning overrides.
</description>
</method>
- <method name="draw_glyph" qualifiers="const">
- <return type="Vector2" />
- <argument index="0" name="canvas" type="RID" />
- <argument index="1" name="size" type="int" />
- <argument index="2" name="pos" type="Vector2" />
- <argument index="3" name="index" type="int" />
- <argument index="4" name="color" type="Color" default="Color(1, 1, 1, 1)" />
+ <method name="clear_size_cache">
+ <return type="void" />
+ <argument index="0" name="cache_index" type="int" />
<description>
- Draws single glyph into a canvas item at the position, using [code]font[/code] at the size [code]size[/code].
- Returns advance of the glyph for horizontal and vertical layouts.
- Note: Glyph index is bound to the font data, use only glyphs indices returned by [method TextServer.shaped_text_get_glyphs] or [method get_glyph_index] for this font data.
+ Removes all font sizes from the cache entry
</description>
</method>
- <method name="draw_glyph_outline" qualifiers="const">
- <return type="Vector2" />
- <argument index="0" name="canvas" type="RID" />
- <argument index="1" name="size" type="int" />
- <argument index="2" name="outline_size" type="int" />
- <argument index="3" name="pos" type="Vector2" />
- <argument index="4" name="index" type="int" />
- <argument index="5" name="color" type="Color" default="Color(1, 1, 1, 1)" />
+ <method name="clear_textures">
+ <return type="void" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="Vector2i" />
+ <description>
+ Removes all textures from font cache entry. Note: This function will not remove glyphs associated with the texture, use [method remove_glyph] to remove them manually.
+ </description>
+ </method>
+ <method name="find_cache" qualifiers="const">
+ <return type="RID" />
+ <argument index="0" name="variation_coordinates" type="Dictionary" />
<description>
- Draws single glyph outline of size [code]outline_size[/code] into a canvas item at the position, using [code]font[/code] at the size [code]size[/code]. If outline drawing is not supported, nothing is drawn.
- Returns advance of the glyph for horizontal and vertical layouts (regardless of outline drawing support).
- Note: Glyph index is bound to the font data, use only glyphs indices returned by [method TextServer.shaped_text_get_glyphs] or [method get_glyph_index] for this font data.
+ Returns existing or creates a new font cache entry for the specified variation coordinates.
</description>
</method>
<method name="get_ascent" qualifiers="const">
<return type="float" />
- <argument index="0" name="size" type="int" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="int" />
<description>
Returns the font ascent (number of pixels above the baseline).
</description>
</method>
- <method name="get_base_size" qualifiers="const">
- <return type="float" />
+ <method name="get_cache_count" qualifiers="const">
+ <return type="int" />
<description>
- Returns the base size of the font (the only size supported for non-scalable fonts, meaningless for scalable fonts).
+ Returns number of the font cache entries.
+ </description>
+ </method>
+ <method name="get_cache_rid" qualifiers="const">
+ <return type="RID" />
+ <argument index="0" name="cache_index" type="int" />
+ <description>
+ Returns text server font cache entry resource id.
+ </description>
+ </method>
+ <method name="get_data" qualifiers="const">
+ <return type="PackedByteArray" />
+ <description>
+ Returns contents of the dynamic font source file.
</description>
</method>
<method name="get_descent" qualifiers="const">
<return type="float" />
- <argument index="0" name="size" type="int" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="int" />
<description>
- Returns the font descent (number of pixels below the baseline).
+ Returns font descent (number of pixels below the baseline).
</description>
</method>
<method name="get_glyph_advance" qualifiers="const">
<return type="Vector2" />
- <argument index="0" name="index" type="int" />
+ <argument index="0" name="cache_index" type="int" />
<argument index="1" name="size" type="int" />
+ <argument index="2" name="glyph" type="int" />
<description>
- Returns advance of the glyph for horizontal and vertical layouts.
- Note: Glyph index is bound to the font data, use only glyphs indices returned by [method TextServer.shaped_text_get_glyphs] or [method get_glyph_index] for this font data.
+ Returns glyph advance (offset of the next glyph). Note: advance for glyphs outlines is the same as the base glyph advance and is not saved.
</description>
</method>
<method name="get_glyph_index" qualifiers="const">
<return type="int" />
<argument index="0" name="char" type="int" />
- <argument index="1" name="variation_selector" type="int" default="0" />
+ <argument index="1" name="variation_selector" type="int" />
+ <argument index="2" name="arg2" type="int" />
<description>
- Return the glyph index of a [code]char[/code], optionally modified by the [code]variation_selector[/code].
+ Returns the glyph index of a [code]char[/code], optionally modified by the [code]variation_selector[/code].
</description>
</method>
- <method name="get_glyph_kerning" qualifiers="const">
+ <method name="get_glyph_list" qualifiers="const">
+ <return type="Array" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="Vector2i" />
+ <description>
+ Returns list of rendered glyphs in the cache entry.
+ </description>
+ </method>
+ <method name="get_glyph_offset" qualifiers="const">
<return type="Vector2" />
- <argument index="0" name="index_a" type="int" />
- <argument index="1" name="index_b" type="int" />
- <argument index="2" name="size" type="int" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="glyph" type="int" />
<description>
- Returns a kerning of the pair of glyphs for horizontal and vertical layouts.
- Note: Glyph index is bound to the font data, use only glyphs indices returned by [method TextServer.shaped_text_get_glyphs] or [method get_glyph_index] for this font data.
+ Returns glyph offset from the baseline.
</description>
</method>
- <method name="get_height" qualifiers="const">
- <return type="float" />
- <argument index="0" name="size" type="int" />
+ <method name="get_glyph_size" qualifiers="const">
+ <return type="Vector2" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="glyph" type="int" />
<description>
- Returns the total font height (ascent plus descent) in pixels.
+ Returns glyph size.
+ </description>
+ </method>
+ <method name="get_glyph_texture_idx" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="glyph" type="int" />
+ <description>
+ Returns index of the cache texture containing the glyph.
+ </description>
+ </method>
+ <method name="get_glyph_uv_rect" qualifiers="const">
+ <return type="Rect2" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="glyph" type="int" />
+ <description>
+ Returns rectangle in the cache texture containing the glyph.
+ </description>
+ </method>
+ <method name="get_hinting" qualifiers="const">
+ <return type="int" enum="TextServer.Hinting" />
+ <description>
+ Returns the font hinting mode. Used by dynamic fonts only.
+ </description>
+ </method>
+ <method name="get_kerning" qualifiers="const">
+ <return type="Vector2" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="glyph_pair" type="Vector2i" />
+ <description>
+ Returns kerning for the pair of glyphs.
+ </description>
+ </method>
+ <method name="get_kerning_list" qualifiers="const">
+ <return type="Array" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="int" />
+ <description>
+ Returns list of the kerning overrides.
</description>
</method>
<method name="get_language_support_override" qualifiers="const">
@@ -132,6 +189,32 @@
Returns list of language support overrides.
</description>
</method>
+ <method name="get_msdf_pixel_range" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns the width of the range around the shape between the minimum and maximum representable signed distance.
+ </description>
+ </method>
+ <method name="get_msdf_size" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns source font size used to generate MSDF textures.
+ </description>
+ </method>
+ <method name="get_oversampling" qualifiers="const">
+ <return type="float" />
+ <description>
+ Returns font oversampling factor, if set to [code]0.0[/code] global oversampling factor is used instead. Used by dynamic fonts only.
+ </description>
+ </method>
+ <method name="get_scale" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="int" />
+ <description>
+ Returns scaling factor of the color bitmap font.
+ </description>
+ </method>
<method name="get_script_support_override" qualifiers="const">
<return type="bool" />
<argument index="0" name="script" type="String" />
@@ -145,11 +228,20 @@
Returns list of script support overrides.
</description>
</method>
+ <method name="get_size_cache_list" qualifiers="const">
+ <return type="Array" />
+ <argument index="0" name="cache_index" type="int" />
+ <description>
+ Return list of the font sizes in the cache. Each size is [code]Vector2i[/code] with font size and outline size.
+ </description>
+ </method>
<method name="get_spacing" qualifiers="const">
<return type="int" />
- <argument index="0" name="type" type="int" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="arg2" type="int" enum="TextServer.SpacingType" />
<description>
- Returns the spacing for the given [code]type[/code] (see [enum SpacingType]).
+ Returns extra spacing added between glyphs in pixels.
</description>
</method>
<method name="get_supported_chars" qualifiers="const">
@@ -158,32 +250,66 @@
Returns a string containing all the characters available in the font.
</description>
</method>
- <method name="get_underline_position" qualifiers="const">
- <return type="float" />
- <argument index="0" name="size" type="int" />
+ <method name="get_supported_feature_list" qualifiers="const">
+ <return type="Dictionary" />
<description>
- Returns underline offset (number of pixels below the baseline).
+ Returns list of OpenType features supported by font.
</description>
</method>
- <method name="get_underline_thickness" qualifiers="const">
+ <method name="get_supported_variation_list" qualifiers="const">
+ <return type="Dictionary" />
+ <description>
+ Returns list of supported [url=https://docs.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg]variation coordinates[/url], each coordinate is returned as [code]tag: Vector3i(min_value,max_value,default_value)[/code].
+ Font variations allow for continuous change of glyph characteristics along some given design axis, such as weight, width or slant.
+ </description>
+ </method>
+ <method name="get_texture_count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="Vector2i" />
+ <description>
+ Returns number of textures used by font cache entry.
+ </description>
+ </method>
+ <method name="get_texture_image" qualifiers="const">
+ <return type="Image" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="texture_index" type="int" />
+ <description>
+ Returns a copy of the font cache texture image.
+ </description>
+ </method>
+ <method name="get_texture_offsets" qualifiers="const">
+ <return type="PackedInt32Array" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="texture_index" type="int" />
+ <description>
+ Returns a copy of the array containing the first free pixel in the each column of texture. Should be the same size as texture width or empty.
+ </description>
+ </method>
+ <method name="get_underline_position" qualifiers="const">
<return type="float" />
- <argument index="0" name="size" type="int" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="int" />
<description>
- Returns underline thickness in pixels.
+ Returns pixel offset of the underline below the baseline.
</description>
</method>
- <method name="get_variation" qualifiers="const">
+ <method name="get_underline_thickness" qualifiers="const">
<return type="float" />
- <argument index="0" name="tag" type="String" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="int" />
<description>
- Returns variation coordinate [code]tag[/code].
+ Returns thickness of the underline in pixels.
</description>
</method>
- <method name="get_variation_list" qualifiers="const">
+ <method name="get_variation_coordinates" qualifiers="const">
<return type="Dictionary" />
+ <argument index="0" name="cache_index" type="int" />
<description>
- Returns list of supported [url=https://docs.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg]variation coordinates[/url], each coordinate is returned as [code]tag: Vector3i(min_value,max_value,default_value)[/code].
- Font variations allow for continuous change of glyph characteristics along some given design axis, such as weight, width or slant.
+ Returns variation coordinates for the specified font cache entry. See [method get_supported_variation_list] for more info.
</description>
</method>
<method name="has_char" qualifiers="const">
@@ -193,10 +319,16 @@
Return [code]true[/code] if a Unicode [code]char[/code] is available in the font.
</description>
</method>
- <method name="has_outline" qualifiers="const">
+ <method name="is_antialiased" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if font 8-bit anitialiased glyph rendering is supported and enabled.
+ </description>
+ </method>
+ <method name="is_force_autohinter" qualifiers="const">
<return type="bool" />
<description>
- Returns [code]true[/code], if font supports drawing glyph outlines.
+ Returns [code]true[/code] if auto-hinting is supported and preffered over font built-in hinting. Used by dynamic fonts only.
</description>
</method>
<method name="is_language_supported" qualifiers="const">
@@ -206,6 +338,12 @@
Returns [code]true[/code], if font supports given language ([url=https://en.wikipedia.org/wiki/ISO_639-1]ISO 639[/url] code).
</description>
</method>
+ <method name="is_multichannel_signed_distance_field" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if glyphs of all sizes are rendered using single multichannel signed distance field generated from the dynamic font vector data.
+ </description>
+ </method>
<method name="is_script_supported" qualifiers="const">
<return type="bool" />
<argument index="0" name="script" type="String" />
@@ -213,32 +351,29 @@
Returns [code]true[/code], if font supports given script ([url=https://en.wikipedia.org/wiki/ISO_15924]ISO 15924[/url] code).
</description>
</method>
- <method name="load_memory">
+ <method name="remove_cache">
<return type="void" />
- <argument index="0" name="data" type="PackedByteArray" />
- <argument index="1" name="type" type="String" />
- <argument index="2" name="base_size" type="int" default="16" />
+ <argument index="0" name="cache_index" type="int" />
<description>
- Creates new font from the data in memory.
- Note: For non-scalable fonts [code]base_size[/code] is ignored, use [method get_base_size] to check actual font size.
+ Removes specified font cache entry.
</description>
</method>
- <method name="load_resource">
+ <method name="remove_glyph">
<return type="void" />
- <argument index="0" name="filename" type="String" />
- <argument index="1" name="base_size" type="int" default="16" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="glyph" type="int" />
<description>
- Creates new font from the file.
- Note: For non-scalable fonts [code]base_size[/code] is ignored, use [method get_base_size] to check actual font size.
+ Removes specified rendered glyph information from the cache entry. Note: This function will not remove textures associated with the glyphs, use [method remove_texture] to remove them manually.
</description>
</method>
- <method name="new_bitmap">
+ <method name="remove_kerning">
<return type="void" />
- <argument index="0" name="height" type="float" />
- <argument index="1" name="ascent" type="float" />
- <argument index="2" name="base_size" type="int" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="glyph_pair" type="Vector2i" />
<description>
- Creates new, empty bitmap font.
+ Removes kerning override for the pair of glyphs.
</description>
</method>
<method name="remove_language_support_override">
@@ -255,6 +390,148 @@
Removes script support override.
</description>
</method>
+ <method name="remove_size_cache">
+ <return type="void" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="Vector2i" />
+ <description>
+ Removes specified font size from the cache entry.
+ </description>
+ </method>
+ <method name="remove_texture">
+ <return type="void" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="texture_index" type="int" />
+ <description>
+ Removes specified texture from font cache entry. Note: This function will not remove glyphs associated with the texture, remove them manually, using [method remove_glyph].
+ </description>
+ </method>
+ <method name="render_glyph">
+ <return type="void" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="index" type="int" />
+ <description>
+ Renders specified glyph the the font cache texture.
+ </description>
+ </method>
+ <method name="render_range">
+ <return type="void" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="start" type="int" />
+ <argument index="3" name="end" type="int" />
+ <description>
+ Renders the range of characters to the font cache texture.
+ </description>
+ </method>
+ <method name="set_antialiased">
+ <return type="void" />
+ <argument index="0" name="antialiased" type="bool" />
+ <description>
+ If set to [code]true[/code], 8-bit antialiased glyph rendering is used, otherwise 1-bit rendering is used. Used by dynamic fonts only.
+ </description>
+ </method>
+ <method name="set_ascent">
+ <return type="void" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="ascent" type="float" />
+ <description>
+ Sets the font ascent (number of pixels above the baseline).
+ </description>
+ </method>
+ <method name="set_data">
+ <return type="void" />
+ <argument index="0" name="data" type="PackedByteArray" />
+ <description>
+ Sets font source data, e.g contents of the dynamic font source file.
+ </description>
+ </method>
+ <method name="set_descent">
+ <return type="void" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="descent" type="float" />
+ <description>
+ Sets the font descent (number of pixels below the baseline).
+ </description>
+ </method>
+ <method name="set_force_autohinter">
+ <return type="void" />
+ <argument index="0" name="force_autohinter" type="bool" />
+ <description>
+ If set to [code]true[/code] auto-hinting is preffered over font built-in hinting.
+ </description>
+ </method>
+ <method name="set_glyph_advance">
+ <return type="void" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="glyph" type="int" />
+ <argument index="3" name="advance" type="Vector2" />
+ <description>
+ Sets glyph advance (offset of the next glyph). Note: advance for glyphs outlines is the same as the base glyph advance and is not saved.
+ </description>
+ </method>
+ <method name="set_glyph_offset">
+ <return type="void" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="glyph" type="int" />
+ <argument index="3" name="offset" type="Vector2" />
+ <description>
+ Sets glyph offset from the baseline.
+ </description>
+ </method>
+ <method name="set_glyph_size">
+ <return type="void" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="glyph" type="int" />
+ <argument index="3" name="gl_size" type="Vector2" />
+ <description>
+ Sets glyph size.
+ </description>
+ </method>
+ <method name="set_glyph_texture_idx">
+ <return type="void" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="glyph" type="int" />
+ <argument index="3" name="texture_idx" type="int" />
+ <description>
+ Sets index of the cache texture containing the glyph.
+ </description>
+ </method>
+ <method name="set_glyph_uv_rect">
+ <return type="void" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="glyph" type="int" />
+ <argument index="3" name="uv_rect" type="Rect2" />
+ <description>
+ Sets rectangle in the cache texture containing the glyph.
+ </description>
+ </method>
+ <method name="set_hinting">
+ <return type="void" />
+ <argument index="0" name="hinting" type="int" enum="TextServer.Hinting" />
+ <description>
+ Sets font hinting mode. Used by dynamic fonts only.
+ </description>
+ </method>
+ <method name="set_kerning">
+ <return type="void" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="glyph_pair" type="Vector2i" />
+ <argument index="3" name="kerning" type="Vector2" />
+ <description>
+ Sets kerning for the pair of glyphs.
+ </description>
+ </method>
<method name="set_language_support_override">
<return type="void" />
<argument index="0" name="language" type="String" />
@@ -263,6 +540,43 @@
Adds override for [method is_language_supported].
</description>
</method>
+ <method name="set_msdf_pixel_range">
+ <return type="void" />
+ <argument index="0" name="msdf_pixel_range" type="int" />
+ <description>
+ Sets the width of the range around the shape between the minimum and maximum representable signed distance.
+ </description>
+ </method>
+ <method name="set_msdf_size">
+ <return type="void" />
+ <argument index="0" name="msdf_size" type="int" />
+ <description>
+ Sets source font size used to generate MSDF textures.
+ </description>
+ </method>
+ <method name="set_multichannel_signed_distance_field">
+ <return type="void" />
+ <argument index="0" name="msdf" type="bool" />
+ <description>
+ If set to [code]true[/code], glyphs of all sizes are rendered using single multichannel signed distance field generated from the dynamic font vector data.
+ </description>
+ </method>
+ <method name="set_oversampling">
+ <return type="void" />
+ <argument index="0" name="oversampling" type="float" />
+ <description>
+ Sets font oversampling factor, if set to [code]0.0[/code] global oversampling factor is used instead. Used by dynamic fonts only.
+ </description>
+ </method>
+ <method name="set_scale">
+ <return type="void" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="scale" type="float" />
+ <description>
+ Sets scaling factor of the color bitmap font.
+ </description>
+ </method>
<method name="set_script_support_override">
<return type="void" />
<argument index="0" name="script" type="String" />
@@ -273,52 +587,61 @@
</method>
<method name="set_spacing">
<return type="void" />
- <argument index="0" name="type" type="int" />
- <argument index="1" name="value" type="int" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="spacing" type="int" enum="TextServer.SpacingType" />
+ <argument index="3" name="arg3" type="int" />
+ <description>
+ Sets extra spacing added between glyphs in pixels.
+ </description>
+ </method>
+ <method name="set_texture_image">
+ <return type="void" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="texture_index" type="int" />
+ <argument index="3" name="image" type="Image" />
+ <description>
+ Sets font cache texture image.
+ </description>
+ </method>
+ <method name="set_texture_offsets">
+ <return type="void" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="texture_index" type="int" />
+ <argument index="3" name="offset" type="PackedInt32Array" />
+ <description>
+ Sets array containing the first free pixel in the each column of texture. Should be the same size as texture width or empty (for the fonts without dynamic glyph generation support).
+ </description>
+ </method>
+ <method name="set_underline_position">
+ <return type="void" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="underline_position" type="float" />
+ <description>
+ Sets pixel offset of the underline below the baseline.
+ </description>
+ </method>
+ <method name="set_underline_thickness">
+ <return type="void" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="underline_thickness" type="float" />
<description>
- Sets the spacing for [code]type[/code] (see [enum SpacingType]) to [code]value[/code] in pixels (not relative to the font size).
+ Sets thickness of the underline in pixels.
</description>
</method>
- <method name="set_variation">
+ <method name="set_variation_coordinates">
<return type="void" />
- <argument index="0" name="tag" type="String" />
- <argument index="1" name="value" type="float" />
+ <argument index="0" name="cache_index" type="int" />
+ <argument index="1" name="variation_coordinates" type="Dictionary" />
<description>
- Sets variation coordinate [code]tag[/code].
+ Sets variation coordinates for the specified font cache entry. See [method get_supported_variation_list] for more info.
</description>
</method>
</methods>
- <members>
- <member name="antialiased" type="bool" setter="set_antialiased" getter="get_antialiased" default="false">
- If [code]true[/code], the font is rendered with anti-aliasing.
- </member>
- <member name="data_path" type="String" setter="set_data_path" getter="get_data_path" default="&quot;&quot;">
- The path to the font data file. If font data was loaded from memory location is set to [code]"(Memory)"[/code].
- </member>
- <member name="distance_field_hint" type="bool" setter="set_distance_field_hint" getter="get_distance_field_hint" default="false">
- If [code]true[/code], distance field hint is enabled.
- </member>
- <member name="extra_spacing_glyph" type="int" setter="set_spacing" getter="get_spacing" default="0">
- Extra spacing for each glyph in pixels.
- This can be a negative number to make the distance between glyphs smaller.
- </member>
- <member name="extra_spacing_space" type="int" setter="set_spacing" getter="get_spacing" default="0">
- Extra spacing for the space character in pixels.
- This can be a negative number to make the distance between words smaller.
- </member>
- <member name="force_autohinter" type="bool" setter="set_force_autohinter" getter="get_force_autohinter" default="false">
- If [code]true[/code], default autohinter is used for font hinting.
- </member>
- <member name="hinting" type="int" setter="set_hinting" getter="get_hinting" enum="TextServer.Hinting" default="0">
- The font hinting mode used by FreeType. See [enum TextServer.Hinting] for options.
- </member>
- </members>
<constants>
- <constant name="SPACING_GLYPH" value="0" enum="SpacingType">
- Spacing for each glyph.
- </constant>
- <constant name="SPACING_SPACE" value="1" enum="SpacingType">
- Spacing for the space character.
- </constant>
</constants>
</class>
diff --git a/doc/classes/TextParagraph.xml b/doc/classes/TextParagraph.xml
index fb94e14c8d..aa35acdbd2 100644
--- a/doc/classes/TextParagraph.xml
+++ b/doc/classes/TextParagraph.xml
@@ -220,13 +220,13 @@
<method name="get_spacing_bottom" qualifiers="const">
<return type="int" />
<description>
- Returns extra spacing at the bottom of the line. See [member Font.extra_spacing_bottom].
+ Returns extra spacing at the bottom of the line. See [member Font.spacing_bottom].
</description>
</method>
<method name="get_spacing_top" qualifiers="const">
<return type="int" />
<description>
- Returns extra spacing at the top of the line. See [member Font.extra_spacing_top].
+ Returns extra spacing at the top of the line. See [member Font.spacing_top].
</description>
</method>
<method name="hit_test" qualifiers="const">
diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml
index ac56be4392..b32168ebc5 100644
--- a/doc/classes/TextServer.xml
+++ b/doc/classes/TextServer.xml
@@ -9,42 +9,10 @@
<tutorials>
</tutorials>
<methods>
- <method name="create_font_bitmap">
+ <method name="create_font">
<return type="RID" />
- <argument index="0" name="height" type="float" />
- <argument index="1" name="ascent" type="float" />
- <argument index="2" name="base_size" type="int" />
<description>
- Creates new, empty bitmap font. To free the resulting font, use [method free_rid] method.
- </description>
- </method>
- <method name="create_font_memory">
- <return type="RID" />
- <argument index="0" name="data" type="PackedByteArray" />
- <argument index="1" name="type" type="String" />
- <argument index="2" name="base_size" type="int" default="16" />
- <description>
- Creates new font from the data in memory. To free the resulting font, use [method free_rid] method.
- Note: For non-scalable fonts [code]base_size[/code] is ignored, use [method font_get_base_size] to check actual font size.
- </description>
- </method>
- <method name="create_font_resource">
- <return type="RID" />
- <argument index="0" name="filename" type="String" />
- <argument index="1" name="base_size" type="int" default="16" />
- <description>
- Creates new font from the file. To free the resulting font, use [method free_rid] method.
- Note: For non-scalable fonts [code]base_size[/code] is ignored, use [method font_get_base_size] to check actual font size.
- </description>
- </method>
- <method name="create_font_system">
- <return type="RID" />
- <argument index="0" name="name" type="String" />
- <argument index="1" name="base_size" type="int" default="16" />
- <description>
- Creates new font from the system font. To free the resulting font, use [method free_rid] method.
- Note: This method is supported by servers with the [code]FEATURE_FONT_SYSTEM[/code] feature.
- Note: For non-scalable fonts [code]base_size[/code] is ignored, use [method font_get_base_size] to check actual font size.
+ Creates new, empty font cache entry resource. To free the resulting resourec, use [method free_rid] method.
</description>
</method>
<method name="create_shaped_text">
@@ -68,52 +36,53 @@
Draws box displaying character hexadecimal code. Used for replacing missing characters.
</description>
</method>
- <method name="font_bitmap_add_char">
+ <method name="font_clear_glyphs">
<return type="void" />
- <argument index="0" name="font" type="RID" />
- <argument index="1" name="char" type="int" />
- <argument index="2" name="texture_idx" type="int" />
- <argument index="3" name="rect" type="Rect2" />
- <argument index="4" name="align" type="Vector2" />
- <argument index="5" name="advance" type="float" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="Vector2i" />
<description>
- Adds a character to the font, where [code]character[/code] is the Unicode value, [code]texture[/code] is the texture index, [code]rect[/code] is the region in the texture (in pixels!), [code]align[/code] is the (optional) alignment for the character and [code]advance[/code] is the (optional) advance.
+ Removes all rendered glyphs information from the cache entry. Note: This function will not remove textures associated with the glyphs, use [method font_remove_texture] to remove them manually.
</description>
</method>
- <method name="font_bitmap_add_kerning_pair">
+ <method name="font_clear_kerning_map">
<return type="void" />
- <argument index="0" name="font" type="RID" />
- <argument index="1" name="A" type="int" />
- <argument index="2" name="B" type="int" />
- <argument index="3" name="kerning" type="int" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="int" />
<description>
- Adds a kerning pair to the bitmap font as a difference. Kerning pairs are special cases where a typeface advance is determined by the next character.
+ Removes all kerning overrides.
</description>
</method>
- <method name="font_bitmap_add_texture">
+ <method name="font_clear_size_cache">
<return type="void" />
- <argument index="0" name="font" type="RID" />
- <argument index="1" name="texture" type="Texture" />
+ <argument index="0" name="font_rid" type="RID" />
+ <description>
+ Removes all font sizes from the cache entry
+ </description>
+ </method>
+ <method name="font_clear_textures">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="Vector2i" />
<description>
- Adds a texture to the bitmap font.
+ Removes all textures from font cache entry. Note: This function will not remove glyphs associated with the texture, use [method font_remove_glyph] to remove them manually.
</description>
</method>
<method name="font_draw_glyph" qualifiers="const">
- <return type="Vector2" />
- <argument index="0" name="font" type="RID" />
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
<argument index="1" name="canvas" type="RID" />
<argument index="2" name="size" type="int" />
<argument index="3" name="pos" type="Vector2" />
<argument index="4" name="index" type="int" />
<argument index="5" name="color" type="Color" default="Color(1, 1, 1, 1)" />
<description>
- Draws single glyph into a canvas item at the position, using [code]font[/code] at the size [code]size[/code].
+ Draws single glyph into a canvas item at the position, using [code]font_rid[/code] at the size [code]size[/code].
Note: Glyph index is specific to the font, use glyphs indices returned by [method shaped_text_get_glyphs] or [method font_get_glyph_index].
</description>
</method>
<method name="font_draw_glyph_outline" qualifiers="const">
- <return type="Vector2" />
- <argument index="0" name="font" type="RID" />
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
<argument index="1" name="canvas" type="RID" />
<argument index="2" name="size" type="int" />
<argument index="3" name="outline_size" type="int" />
@@ -121,68 +90,46 @@
<argument index="5" name="index" type="int" />
<argument index="6" name="color" type="Color" default="Color(1, 1, 1, 1)" />
<description>
- Draws single glyph outline of size [code]outline_size[/code] into a canvas item at the position, using [code]font[/code] at the size [code]size[/code].
+ Draws single glyph outline of size [code]outline_size[/code] into a canvas item at the position, using [code]font_rid[/code] at the size [code]size[/code].
Note: Glyph index is specific to the font, use glyphs indices returned by [method shaped_text_get_glyphs] or [method font_get_glyph_index].
</description>
</method>
- <method name="font_get_antialiased" qualifiers="const">
- <return type="bool" />
- <argument index="0" name="font" type="RID" />
- <description>
- Returns [code]true[/code], if font anti-aliasing is supported and enabled.
- </description>
- </method>
<method name="font_get_ascent" qualifiers="const">
<return type="float" />
- <argument index="0" name="font" type="RID" />
+ <argument index="0" name="font_rid" type="RID" />
<argument index="1" name="size" type="int" />
<description>
Returns the font ascent (number of pixels above the baseline).
</description>
</method>
- <method name="font_get_base_size" qualifiers="const">
- <return type="float" />
- <argument index="0" name="font" type="RID" />
- <description>
- Returns the default size of the font.
- </description>
- </method>
<method name="font_get_descent" qualifiers="const">
<return type="float" />
- <argument index="0" name="font" type="RID" />
+ <argument index="0" name="font_rid" type="RID" />
<argument index="1" name="size" type="int" />
<description>
Returns the font descent (number of pixels below the baseline).
</description>
</method>
- <method name="font_get_distance_field_hint" qualifiers="const">
- <return type="bool" />
- <argument index="0" name="font" type="RID" />
- <description>
- Returns [code]true[/code], if distance field hint is enabled.
- </description>
- </method>
- <method name="font_get_feature_list" qualifiers="const">
- <return type="Dictionary" />
- <argument index="0" name="font" type="RID" />
+ <method name="font_get_fixed_size" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="font_rid" type="RID" />
<description>
- Returns list of OpenType features supported by font.
+ Returns bitmap font fixed size.
</description>
</method>
- <method name="font_get_force_autohinter" qualifiers="const">
- <return type="bool" />
- <argument index="0" name="font" type="RID" />
+ <method name="font_get_global_oversampling" qualifiers="const">
+ <return type="float" />
<description>
- Returns [code]true[/code], if autohinter is supported and enabled.
+ Returns the font oversampling factor, shared by all fonts in the TextServer.
</description>
</method>
<method name="font_get_glyph_advance" qualifiers="const">
<return type="Vector2" />
- <argument index="0" name="font" type="RID" />
- <argument index="1" name="index" type="int" />
- <argument index="2" name="size" type="int" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="glyph" type="int" />
<description>
- Returns advance of the glyph.
+ Returns glyph advance (offset of the next glyph). Note: advance for glyphs outlines is the same as the base glyph advance and is not saved.
</description>
</method>
<method name="font_get_glyph_contours" qualifiers="const">
@@ -191,7 +138,7 @@
<argument index="1" name="size" type="int" />
<argument index="2" name="index" type="int" />
<description>
- Returns outline contours of the glyph in a Dictionary.
+ Returns outline contours of the glyph as a [code]Dictionary[/code] with the following contents:
[code]points[/code] - [PackedVector3Array], containing outline points. [code]x[/code] and [code]y[/code] are point coordinates. [code]z[/code] is the type of the point, using the [enum ContourPointTag] values.
[code]contours[/code] - [PackedInt32Array], containing indices the end points of each contour.
[code]orientation[/code] - [bool], contour orientation. If [code]true[/code], clockwise contours must be filled.
@@ -199,41 +146,85 @@
</method>
<method name="font_get_glyph_index" qualifiers="const">
<return type="int" />
- <argument index="0" name="font" type="RID" />
- <argument index="1" name="char" type="int" />
- <argument index="2" name="variation_selector" type="int" default="0" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="char" type="int" />
+ <argument index="3" name="variation_selector" type="int" />
<description>
Returns the glyph index of a [code]char[/code], optionally modified by the [code]variation_selector[/code].
</description>
</method>
- <method name="font_get_glyph_kerning" qualifiers="const">
+ <method name="font_get_glyph_list" qualifiers="const">
+ <return type="Array" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="Vector2i" />
+ <description>
+ Returns list of rendered glyphs in the cache entry.
+ </description>
+ </method>
+ <method name="font_get_glyph_offset" qualifiers="const">
<return type="Vector2" />
- <argument index="0" name="font" type="RID" />
- <argument index="1" name="index_a" type="int" />
- <argument index="2" name="index_b" type="int" />
- <argument index="3" name="size" type="int" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="glyph" type="int" />
<description>
- Returns a kerning of the pair of glyphs.
+ Returns glyph offset from the baseline.
</description>
</method>
- <method name="font_get_height" qualifiers="const">
- <return type="float" />
- <argument index="0" name="font" type="RID" />
- <argument index="1" name="size" type="int" />
+ <method name="font_get_glyph_size" qualifiers="const">
+ <return type="Vector2" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="glyph" type="int" />
<description>
- Returns the total font height (ascent plus descent) in pixels.
+ Returns size of the glyph.
+ </description>
+ </method>
+ <method name="font_get_glyph_texture_idx" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="glyph" type="int" />
+ <description>
+ Returns index of the cache texture containing the glyph.
+ </description>
+ </method>
+ <method name="font_get_glyph_uv_rect" qualifiers="const">
+ <return type="Rect2" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="glyph" type="int" />
+ <description>
+ Returns rectangle in the cache texture containing the glyph.
</description>
</method>
<method name="font_get_hinting" qualifiers="const">
<return type="int" enum="TextServer.Hinting" />
- <argument index="0" name="font" type="RID" />
+ <argument index="0" name="font_rid" type="RID" />
<description>
- Returns the font hinting.
+ Returns the font hinting mode. Used by dynamic fonts only.
+ </description>
+ </method>
+ <method name="font_get_kerning" qualifiers="const">
+ <return type="Vector2" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="glyph_pair" type="Vector2i" />
+ <description>
+ Returns kerning for the pair of glyphs.
+ </description>
+ </method>
+ <method name="font_get_kerning_list" qualifiers="const">
+ <return type="Array" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="int" />
+ <description>
+ Returns list of the kerning overrides.
</description>
</method>
<method name="font_get_language_support_override">
<return type="bool" />
- <argument index="0" name="font" type="RID" />
+ <argument index="0" name="font_rid" type="RID" />
<argument index="1" name="language" type="String" />
<description>
Returns [code]true[/code] if support override is enabled for the [code]language[/code].
@@ -241,20 +232,43 @@
</method>
<method name="font_get_language_support_overrides">
<return type="PackedStringArray" />
- <argument index="0" name="font" type="RID" />
+ <argument index="0" name="font_rid" type="RID" />
<description>
Returns list of language support overrides.
</description>
</method>
+ <method name="font_get_msdf_pixel_range" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="font_rid" type="RID" />
+ <description>
+ Return the width of the range around the shape between the minimum and maximum representable signed distance.
+ </description>
+ </method>
+ <method name="font_get_msdf_size" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="font_rid" type="RID" />
+ <description>
+ Returns source font size used to generate MSDF textures.
+ </description>
+ </method>
<method name="font_get_oversampling" qualifiers="const">
<return type="float" />
+ <argument index="0" name="font_rid" type="RID" />
<description>
- Returns the font oversampling factor, shared by all fonts in the TextServer.
+ Returns font oversampling factor, if set to [code]0.0[/code] global oversampling factor is used instead. Used by dynamic fonts only.
+ </description>
+ </method>
+ <method name="font_get_scale" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="int" />
+ <description>
+ Returns scaling factor of the color bitmap font.
</description>
</method>
<method name="font_get_script_support_override">
<return type="bool" />
- <argument index="0" name="font" type="RID" />
+ <argument index="0" name="font_rid" type="RID" />
<argument index="1" name="script" type="String" />
<description>
Returns [code]true[/code] if support override is enabled for the [code]script[/code].
@@ -262,98 +276,149 @@
</method>
<method name="font_get_script_support_overrides">
<return type="PackedStringArray" />
- <argument index="0" name="font" type="RID" />
+ <argument index="0" name="font_rid" type="RID" />
<description>
Returns list of script support overrides.
</description>
</method>
- <method name="font_get_spacing_glyph" qualifiers="const">
- <return type="int" />
- <argument index="0" name="font" type="RID" />
+ <method name="font_get_size_cache_list" qualifiers="const">
+ <return type="Array" />
+ <argument index="0" name="font_rid" type="RID" />
<description>
- Returns extra spacing for each glyph in pixels.
+ Return list of the font sizes in the cache. Each size is [code]Vector2i[/code] with font size and outline size.
</description>
</method>
- <method name="font_get_spacing_space" qualifiers="const">
+ <method name="font_get_spacing" qualifiers="const">
<return type="int" />
- <argument index="0" name="font" type="RID" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="spacing" type="int" enum="TextServer.SpacingType" />
<description>
- Sets extra spacing for each glyph in pixels.
+ Returns extra spacing added between glyphs in pixels.
</description>
</method>
<method name="font_get_supported_chars" qualifiers="const">
<return type="String" />
- <argument index="0" name="font" type="RID" />
+ <argument index="0" name="font_rid" type="RID" />
<description>
Returns a string containing all the characters available in the font.
</description>
</method>
- <method name="font_get_underline_position" qualifiers="const">
- <return type="float" />
- <argument index="0" name="font" type="RID" />
- <argument index="1" name="size" type="int" />
+ <method name="font_get_texture_count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="Vector2i" />
<description>
- Returns underline offset (number of pixels below the baseline).
+ Returns number of textures used by font cache entry.
</description>
</method>
- <method name="font_get_underline_thickness" qualifiers="const">
+ <method name="font_get_texture_image" qualifiers="const">
+ <return type="Image" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="texture_index" type="int" />
+ <description>
+ Returns font cache texture image data.
+ </description>
+ </method>
+ <method name="font_get_texture_offsets" qualifiers="const">
+ <return type="PackedInt32Array" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="texture_index" type="int" />
+ <description>
+ Returns array containing the first free pixel in the each column of texture. Should be the same size as texture width or empty.
+ </description>
+ </method>
+ <method name="font_get_underline_position" qualifiers="const">
<return type="float" />
- <argument index="0" name="font" type="RID" />
+ <argument index="0" name="font_rid" type="RID" />
<argument index="1" name="size" type="int" />
<description>
- Returns underline thickness in pixels.
+ Returns pixel offset of the underline below the baseline.
</description>
</method>
- <method name="font_get_variation" qualifiers="const">
+ <method name="font_get_underline_thickness" qualifiers="const">
<return type="float" />
- <argument index="0" name="font" type="RID" />
- <argument index="1" name="tag" type="String" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="int" />
<description>
- Returns variation coordinate [code]tag[/code].
+ Returns thickness of the underline in pixels.
</description>
</method>
- <method name="font_get_variation_list" qualifiers="const">
+ <method name="font_get_variation_coordinates" qualifiers="const">
<return type="Dictionary" />
- <argument index="0" name="font" type="RID" />
+ <argument index="0" name="font_rid" type="RID" />
<description>
- Returns list of supported [url=https://docs.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg]variation coordinates[/url], each coordinate is returned as [code]tag: Vector3i(min_value,max_value,default_value)[/code].
- Font variations allow for continuous change of glyph characteristics along some given design axis, such as weight, width or slant.
+ Returns variation coordinates for the specified font cache entry. See [method font_supported_variation_list] for more info.
</description>
</method>
<method name="font_has_char" qualifiers="const">
<return type="bool" />
- <argument index="0" name="font" type="RID" />
+ <argument index="0" name="font_rid" type="RID" />
<argument index="1" name="char" type="int" />
<description>
- Returns [code]true[/code] if [code]char[/code] is available in the font.
+ Return [code]true[/code] if a Unicode [code]char[/code] is available in the font.
</description>
</method>
- <method name="font_has_outline" qualifiers="const">
+ <method name="font_is_antialiased" qualifiers="const">
<return type="bool" />
- <argument index="0" name="font" type="RID" />
+ <argument index="0" name="font_rid" type="RID" />
+ <description>
+ Returns [code]true[/code] if font 8-bit anitialiased glyph rendering is supported and enabled.
+ </description>
+ </method>
+ <method name="font_is_force_autohinter" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="font_rid" type="RID" />
<description>
- Returns [code]true[/code], if font supports glyph outlines.
+ Returns [code]true[/code] if auto-hinting is supported and preffered over font built-in hinting. Used by dynamic fonts only.
</description>
</method>
<method name="font_is_language_supported" qualifiers="const">
<return type="bool" />
- <argument index="0" name="font" type="RID" />
+ <argument index="0" name="font_rid" type="RID" />
<argument index="1" name="language" type="String" />
<description>
- Returns [code]true[/code], if font supports given language (ISO 639 code).
+ Returns [code]true[/code], if font supports given language ([url=https://en.wikipedia.org/wiki/ISO_639-1]ISO 639[/url] code).
+ </description>
+ </method>
+ <method name="font_is_multichannel_signed_distance_field" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="font_rid" type="RID" />
+ <description>
+ Returns [code]true[/code] if glyphs of all sizes are rendered using single multichannel signed distance field generated from the dynamic font vector data.
</description>
</method>
<method name="font_is_script_supported" qualifiers="const">
<return type="bool" />
- <argument index="0" name="font" type="RID" />
+ <argument index="0" name="font_rid" type="RID" />
<argument index="1" name="script" type="String" />
<description>
Returns [code]true[/code], if font supports given script (ISO 15924 code).
</description>
</method>
+ <method name="font_remove_glyph">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="glyph" type="int" />
+ <description>
+ Removes specified rendered glyph information from the cache entry. Note: This function will not remove textures associated with the glyphs, use [method font_remove_texture] to remove them manually.
+ </description>
+ </method>
+ <method name="font_remove_kerning">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="glyph_pair" type="Vector2i" />
+ <description>
+ Removes kerning override for the pair of glyphs.
+ </description>
+ </method>
<method name="font_remove_language_support_override">
<return type="void" />
- <argument index="0" name="font" type="RID" />
+ <argument index="0" name="font_rid" type="RID" />
<argument index="1" name="language" type="String" />
<description>
Remove language support override.
@@ -361,92 +426,299 @@
</method>
<method name="font_remove_script_support_override">
<return type="void" />
- <argument index="0" name="font" type="RID" />
+ <argument index="0" name="font_rid" type="RID" />
<argument index="1" name="script" type="String" />
<description>
Removes script support override.
</description>
</method>
+ <method name="font_remove_size_cache">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="Vector2i" />
+ <description>
+ Removes specified font size from the cache entry.
+ </description>
+ </method>
+ <method name="font_remove_texture">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="texture_index" type="int" />
+ <description>
+ Removes specified texture from font cache entry. Note: This function will not remove glyphs associated with the texture, remove them manually, using [method font_remove_glyph].
+ </description>
+ </method>
+ <method name="font_render_glyph">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="index" type="int" />
+ <description>
+ Renders specified glyph the the font cache texture.
+ </description>
+ </method>
+ <method name="font_render_range">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="start" type="int" />
+ <argument index="3" name="end" type="int" />
+ <description>
+ Renders the range of characters to the font cache texture.
+ </description>
+ </method>
<method name="font_set_antialiased">
<return type="void" />
- <argument index="0" name="font" type="RID" />
+ <argument index="0" name="font_rid" type="RID" />
<argument index="1" name="antialiased" type="bool" />
<description>
- Sets font anti-aliasing.
+ If set to [code]true[/code], 8-bit antialiased glyph rendering is used, otherwise 1-bit rendering is used. Used by dynamic fonts only.
</description>
</method>
- <method name="font_set_distance_field_hint">
+ <method name="font_set_ascent">
<return type="void" />
- <argument index="0" name="font" type="RID" />
- <argument index="1" name="distance_field" type="bool" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="ascent" type="float" />
+ <description>
+ Sets the font ascent (number of pixels above the baseline).
+ </description>
+ </method>
+ <method name="font_set_data">
+ <return type="void" />
+ <argument index="0" name="data" type="RID" />
+ <argument index="1" name="arg1" type="PackedByteArray" />
<description>
- Sets font distance field hint.
+ Sets font source data, e.g contents of the dynamic font source file.
+ </description>
+ </method>
+ <method name="font_set_descent">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="descent" type="float" />
+ <description>
+ Sets the font descent (number of pixels below the baseline).
+ </description>
+ </method>
+ <method name="font_set_fixed_size">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="fixed_size" type="int" />
+ <description>
+ Sets bitmap font fixed size. If set to value greater than zero, same cache entry will be used for all font sizes.
</description>
</method>
<method name="font_set_force_autohinter">
<return type="void" />
- <argument index="0" name="font" type="RID" />
- <argument index="1" name="enabeld" type="bool" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="force_autohinter" type="bool" />
+ <description>
+ If set to [code]true[/code] auto-hinting is preffered over font built-in hinting.
+ </description>
+ </method>
+ <method name="font_set_global_oversampling">
+ <return type="void" />
+ <argument index="0" name="oversampling" type="float" />
+ <description>
+ Sets oversampling factor, shared by all font in the TextServer.
+ Note: This value can be automaticaly changed by display server.
+ </description>
+ </method>
+ <method name="font_set_glyph_advance">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="glyph" type="int" />
+ <argument index="3" name="advance" type="Vector2" />
+ <description>
+ Sets glyph advance (offset of the next glyph). Note: advance for glyphs outlines is the same as the base glyph advance and is not saved.
+ </description>
+ </method>
+ <method name="font_set_glyph_offset">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="glyph" type="int" />
+ <argument index="3" name="offset" type="Vector2" />
+ <description>
+ Sets glyph offset from the baseline.
+ </description>
+ </method>
+ <method name="font_set_glyph_size">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="glyph" type="int" />
+ <argument index="3" name="size" type="Vector2" />
<description>
- Enables/disables default autohinter.
+ Sets size of the glyph.
+ </description>
+ </method>
+ <method name="font_set_glyph_texture_idx">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="glyph" type="int" />
+ <argument index="3" name="texture_idx" type="int" />
+ <description>
+ Sets index of the cache texture containing the glyph.
+ </description>
+ </method>
+ <method name="font_set_glyph_uv_rect">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="glyph" type="int" />
+ <argument index="3" name="uv_rect" type="Rect2" />
+ <description>
+ Sets rectangle in the cache texture containing the glyph.
</description>
</method>
<method name="font_set_hinting">
<return type="void" />
- <argument index="0" name="font" type="RID" />
- <argument index="1" name="hinting" type="int" enum="TextServer.Hinting" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="_hinting" type="int" enum="TextServer.Hinting" />
<description>
- Sets font hinting.
+ Sets font hinting mode. Used by dynamic fonts only.
+ </description>
+ </method>
+ <method name="font_set_kerning">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="glyph_pair" type="Vector2i" />
+ <argument index="3" name="kerning" type="Vector2" />
+ <description>
+ Sets kerning for the pair of glyphs.
</description>
</method>
<method name="font_set_language_support_override">
<return type="void" />
- <argument index="0" name="font" type="RID" />
+ <argument index="0" name="font_rid" type="RID" />
<argument index="1" name="language" type="String" />
<argument index="2" name="supported" type="bool" />
<description>
Adds override for [method font_is_language_supported].
</description>
</method>
+ <method name="font_set_msdf_pixel_range">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="msdf_pixel_range" type="int" />
+ <description>
+ Sets the width of the range around the shape between the minimum and maximum representable signed distance.
+ </description>
+ </method>
+ <method name="font_set_msdf_size">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="msdf_size" type="int" />
+ <description>
+ Sets source font size used to generate MSDF textures.
+ </description>
+ </method>
+ <method name="font_set_multichannel_signed_distance_field">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="msdf" type="bool" />
+ <description>
+ If set to [code]true[/code], glyphs of all sizes are rendered using single multichannel signed distance field generated from the dynamic font vector data.
+ </description>
+ </method>
<method name="font_set_oversampling">
<return type="void" />
- <argument index="0" name="oversampling" type="float" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="oversampling" type="float" />
<description>
- Sets oversampling factor, shared by all font in the TextServer.
+ Sets font oversampling factor, if set to [code]0.0[/code] global oversampling factor is used instead. Used by dynamic fonts only.
+ </description>
+ </method>
+ <method name="font_set_scale">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="scale" type="float" />
+ <description>
+ Sets scaling factor of the color bitmap font.
</description>
</method>
<method name="font_set_script_support_override">
<return type="void" />
- <argument index="0" name="font" type="RID" />
+ <argument index="0" name="font_rid" type="RID" />
<argument index="1" name="script" type="String" />
<argument index="2" name="supported" type="bool" />
<description>
Adds override for [method font_is_script_supported].
</description>
</method>
- <method name="font_set_spacing_glyph">
+ <method name="font_set_spacing">
<return type="void" />
- <argument index="0" name="font" type="RID" />
- <argument index="1" name="value" type="int" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="spacing" type="int" enum="TextServer.SpacingType" />
+ <argument index="3" name="value" type="int" />
<description>
- Returns extra spacing for the space character in pixels.
+ Sets extra spacing added between glyphs in pixels.
</description>
</method>
- <method name="font_set_spacing_space">
+ <method name="font_set_texture_image">
<return type="void" />
- <argument index="0" name="font" type="RID" />
- <argument index="1" name="value" type="int" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="texture_index" type="int" />
+ <argument index="3" name="image" type="Image" />
<description>
- Sets extra spacing for the space character in pixels.
+ Sets font cache texture image data.
</description>
</method>
- <method name="font_set_variation">
+ <method name="font_set_texture_offsets">
<return type="void" />
- <argument index="0" name="font" type="RID" />
- <argument index="1" name="tag" type="String" />
- <argument index="2" name="value" type="float" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="Vector2i" />
+ <argument index="2" name="texture_index" type="int" />
+ <argument index="3" name="offset" type="PackedInt32Array" />
+ <description>
+ Sets array containing the first free pixel in the each column of texture. Should be the same size as texture width or empty.
+ </description>
+ </method>
+ <method name="font_set_underline_position">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="underline_position" type="float" />
+ <description>
+ Sets pixel offset of the underline below the baseline.
+ </description>
+ </method>
+ <method name="font_set_underline_thickness">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="size" type="int" />
+ <argument index="2" name="underline_thickness" type="float" />
+ <description>
+ Sets thickness of the underline in pixels.
+ </description>
+ </method>
+ <method name="font_set_variation_coordinates">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="variation_coordinates" type="Dictionary" />
+ <description>
+ Sets variation coordinates for the specified font cache entry. See [method font_supported_variation_list] for more info.
+ </description>
+ </method>
+ <method name="font_supported_feature_list" qualifiers="const">
+ <return type="Dictionary" />
+ <argument index="0" name="font_rid" type="RID" />
+ <description>
+ </description>
+ </method>
+ <method name="font_supported_variation_list" qualifiers="const">
+ <return type="Dictionary" />
+ <argument index="0" name="font_rid" type="RID" />
<description>
- Sets variation coordinate [code]name[/code]. Unsupported coordinates will be silently ignored.
</description>
</method>
<method name="format_number" qualifiers="const">
@@ -478,13 +750,6 @@
Returns the name of the server interface.
</description>
</method>
- <method name="get_system_fonts" qualifiers="const">
- <return type="PackedStringArray" />
- <description>
- Returns list of available system fonts.
- Note: This method is supported by servers with the [code]FEATURE_FONT_SYSTEM[/code] feature.
- </description>
- </method>
<method name="has">
<return type="bool" />
<argument index="0" name="rid" type="RID" />
@@ -514,7 +779,7 @@
Note: This function should be called before any other TextServer functions used, otherwise it won't have any effect.
</description>
</method>
- <method name="name_to_tag">
+ <method name="name_to_tag" qualifiers="const">
<return type="int" />
<argument index="0" name="name" type="String" />
<description>
@@ -890,7 +1155,7 @@
Aligns shaped text to the given tab-stops.
</description>
</method>
- <method name="tag_to_name">
+ <method name="tag_to_name" qualifiers="const">
<return type="String" />
<argument index="0" name="tag" type="int" />
<description>
@@ -1023,5 +1288,17 @@
<constant name="CONTOUR_CURVE_TAG_OFF_CUBIC" value="2" enum="ContourPointTag">
Contour point isn't on the curve, but serves as a control point for a cubic Bézier arc.
</constant>
+ <constant name="SPACING_GLYPH" value="0" enum="SpacingType">
+ Spacing for each glyph.
+ </constant>
+ <constant name="SPACING_SPACE" value="1" enum="SpacingType">
+ Spacing for the space character.
+ </constant>
+ <constant name="SPACING_TOP" value="2" enum="SpacingType">
+ Spacing at the top of the line.
+ </constant>
+ <constant name="SPACING_BOTTOM" value="3" enum="SpacingType">
+ Spacing at the bottom of the line.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml
index 406c074758..b997d87ac0 100644
--- a/doc/classes/TreeItem.xml
+++ b/doc/classes/TreeItem.xml
@@ -140,6 +140,14 @@
<return type="Font" />
<argument index="0" name="column" type="int" />
<description>
+ Returns custom font used to draw text in the column [code]column[/code].
+ </description>
+ </method>
+ <method name="get_custom_font_size" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="column" type="int" />
+ <description>
+ Returns custom font size used to draw text in the column [code]column[/code].
</description>
</method>
<method name="get_expand_right" qualifiers="const">
@@ -464,6 +472,15 @@
<argument index="0" name="column" type="int" />
<argument index="1" name="font" type="Font" />
<description>
+ Sets custom font used to draw text in the column [code]column[/code].
+ </description>
+ </method>
+ <method name="set_custom_font_size">
+ <return type="void" />
+ <argument index="0" name="column" type="int" />
+ <argument index="1" name="font_size" type="int" />
+ <description>
+ Sets custom font size used to draw text in the column [code]column[/code].
</description>
</method>
<method name="set_editable">
diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp
index 1e3db1a7b0..e5d6315ef7 100644
--- a/editor/editor_fonts.cpp
+++ b/editor/editor_fonts.cpp
@@ -67,46 +67,113 @@
m_name->add_data(FontJapanese); \
m_name->add_data(FontFallback);
-// the custom spacings might only work with Noto Sans
-#define MAKE_DEFAULT_FONT(m_name) \
- Ref<Font> m_name; \
- m_name.instantiate(); \
- if (CustomFont.is_valid()) { \
- m_name->add_data(CustomFont); \
- m_name->add_data(DefaultFont); \
- } else { \
- m_name->add_data(DefaultFont); \
- } \
- m_name->set_spacing(Font::SPACING_TOP, -EDSCALE); \
- m_name->set_spacing(Font::SPACING_BOTTOM, -EDSCALE); \
+#define MAKE_DEFAULT_FONT(m_name, m_variations, m_base_size) \
+ Ref<Font> m_name; \
+ m_name.instantiate(); \
+ if (CustomFont.is_valid()) { \
+ m_name->add_data(CustomFont); \
+ m_name->add_data(DefaultFont); \
+ } else { \
+ m_name->add_data(DefaultFont); \
+ } \
+ { \
+ Dictionary variations; \
+ if (m_variations != String()) { \
+ Vector<String> variation_tags = m_variations.split(","); \
+ for (int i = 0; i < variation_tags.size(); i++) { \
+ Vector<String> tokens = variation_tags[i].split("="); \
+ if (tokens.size() == 2) { \
+ variations[tokens[0]] = tokens[1].to_float(); \
+ } \
+ } \
+ } \
+ m_name->set_variation_coordinates(variations); \
+ } \
+ m_name->set_base_size(m_base_size); \
+ m_name->set_spacing(TextServer::SPACING_TOP, -EDSCALE); \
+ m_name->set_spacing(TextServer::SPACING_BOTTOM, -EDSCALE); \
MAKE_FALLBACKS(m_name);
-#define MAKE_BOLD_FONT(m_name) \
- Ref<Font> m_name; \
- m_name.instantiate(); \
- if (CustomFontBold.is_valid()) { \
- m_name->add_data(CustomFontBold); \
- m_name->add_data(DefaultFontBold); \
- } else { \
- m_name->add_data(DefaultFontBold); \
- } \
- m_name->set_spacing(Font::SPACING_TOP, -EDSCALE); \
- m_name->set_spacing(Font::SPACING_BOTTOM, -EDSCALE); \
+#define MAKE_BOLD_FONT(m_name, m_variations, m_base_size) \
+ Ref<Font> m_name; \
+ m_name.instantiate(); \
+ if (CustomFontBold.is_valid()) { \
+ m_name->add_data(CustomFontBold); \
+ m_name->add_data(DefaultFontBold); \
+ } else { \
+ m_name->add_data(DefaultFontBold); \
+ } \
+ { \
+ Dictionary variations; \
+ if (m_variations != String()) { \
+ Vector<String> variation_tags = m_variations.split(","); \
+ for (int i = 0; i < variation_tags.size(); i++) { \
+ Vector<String> tokens = variation_tags[i].split("="); \
+ if (tokens.size() == 2) { \
+ variations[tokens[0]] = tokens[1].to_float(); \
+ } \
+ } \
+ } \
+ m_name->set_variation_coordinates(variations); \
+ } \
+ m_name->set_base_size(m_base_size); \
+ m_name->set_spacing(TextServer::SPACING_TOP, -EDSCALE); \
+ m_name->set_spacing(TextServer::SPACING_BOTTOM, -EDSCALE); \
MAKE_FALLBACKS_BOLD(m_name);
-#define MAKE_SOURCE_FONT(m_name) \
- Ref<Font> m_name; \
- m_name.instantiate(); \
- if (CustomFontSource.is_valid()) { \
- m_name->add_data(CustomFontSource); \
- m_name->add_data(dfmono); \
- } else { \
- m_name->add_data(dfmono); \
- } \
- m_name->set_spacing(Font::SPACING_TOP, -EDSCALE); \
- m_name->set_spacing(Font::SPACING_BOTTOM, -EDSCALE); \
+#define MAKE_SOURCE_FONT(m_name, m_variations, m_base_size) \
+ Ref<Font> m_name; \
+ m_name.instantiate(); \
+ if (CustomFontSource.is_valid()) { \
+ m_name->add_data(CustomFontSource); \
+ m_name->add_data(dfmono); \
+ } else { \
+ m_name->add_data(dfmono); \
+ } \
+ { \
+ Dictionary variations; \
+ if (m_variations != String()) { \
+ Vector<String> variation_tags = m_variations.split(","); \
+ for (int i = 0; i < variation_tags.size(); i++) { \
+ Vector<String> tokens = variation_tags[i].split("="); \
+ if (tokens.size() == 2) { \
+ variations[tokens[0]] = tokens[1].to_float(); \
+ } \
+ } \
+ } \
+ m_name->set_variation_coordinates(variations); \
+ } \
+ m_name->set_base_size(m_base_size); \
+ m_name->set_spacing(TextServer::SPACING_TOP, -EDSCALE); \
+ m_name->set_spacing(TextServer::SPACING_BOTTOM, -EDSCALE); \
MAKE_FALLBACKS(m_name);
+Ref<FontData> load_cached_external_font(const String &p_path, TextServer::Hinting p_hinting, bool p_aa, bool p_autohint) {
+ Ref<FontData> font;
+ font.instantiate();
+
+ Vector<uint8_t> data = FileAccess::get_file_as_array(p_path);
+
+ font->set_data(data);
+ font->set_antialiased(p_aa);
+ font->set_hinting(p_hinting);
+ font->set_force_autohinter(p_autohint);
+
+ return font;
+}
+
+Ref<FontData> load_cached_internal_font(const uint8_t *p_data, size_t p_size, TextServer::Hinting p_hinting, bool p_aa, bool p_autohint) {
+ Ref<FontData> font;
+ font.instantiate();
+
+ font->set_data_ptr(p_data, p_size);
+ font->set_antialiased(p_aa);
+ font->set_hinting(p_hinting);
+ font->set_force_autohinter(p_autohint);
+
+ return font;
+}
+
void editor_register_fonts(Ref<Theme> p_theme) {
DirAccess *dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
@@ -144,11 +211,7 @@ void editor_register_fonts(Ref<Theme> p_theme) {
String custom_font_path = EditorSettings::get_singleton()->get("interface/editor/main_font");
Ref<FontData> CustomFont;
if (custom_font_path.length() > 0 && dir->file_exists(custom_font_path)) {
- CustomFont.instantiate();
- CustomFont->load_resource(custom_font_path, default_font_size);
- CustomFont->set_antialiased(font_antialiased);
- CustomFont->set_hinting(font_hinting);
- CustomFont->set_force_autohinter(true); //just looks better..i think?
+ CustomFont = load_cached_external_font(custom_font_path, font_hinting, font_antialiased, true);
} else {
EditorSettings::get_singleton()->set_manually("interface/editor/main_font", "");
}
@@ -158,11 +221,7 @@ void editor_register_fonts(Ref<Theme> p_theme) {
String custom_font_path_bold = EditorSettings::get_singleton()->get("interface/editor/main_font_bold");
Ref<FontData> CustomFontBold;
if (custom_font_path_bold.length() > 0 && dir->file_exists(custom_font_path_bold)) {
- CustomFontBold.instantiate();
- CustomFontBold->load_resource(custom_font_path_bold, default_font_size);
- CustomFontBold->set_antialiased(font_antialiased);
- CustomFontBold->set_hinting(font_hinting);
- CustomFontBold->set_force_autohinter(true); //just looks better..i think?
+ CustomFontBold = load_cached_external_font(custom_font_path_bold, font_hinting, font_antialiased, true);
} else {
EditorSettings::get_singleton()->set_manually("interface/editor/main_font_bold", "");
}
@@ -172,231 +231,51 @@ void editor_register_fonts(Ref<Theme> p_theme) {
String custom_font_path_source = EditorSettings::get_singleton()->get("interface/editor/code_font");
Ref<FontData> CustomFontSource;
if (custom_font_path_source.length() > 0 && dir->file_exists(custom_font_path_source)) {
- CustomFontSource.instantiate();
- CustomFontSource->load_resource(custom_font_path_source, default_font_size);
- CustomFontSource->set_antialiased(font_antialiased);
- CustomFontSource->set_hinting(font_hinting);
-
- Vector<String> subtag = String(EditorSettings::get_singleton()->get("interface/editor/code_font_custom_variations")).split(",");
- for (int i = 0; i < subtag.size(); i++) {
- Vector<String> subtag_a = subtag[i].split("=");
- if (subtag_a.size() == 2) {
- CustomFontSource->set_variation(subtag_a[0], subtag_a[1].to_float());
- }
- }
+ CustomFontSource = load_cached_external_font(custom_font_path_source, font_hinting, font_antialiased, true);
} else {
EditorSettings::get_singleton()->set_manually("interface/editor/code_font", "");
}
memdelete(dir);
- /* Noto Sans UI */
-
- Ref<FontData> DefaultFont;
- DefaultFont.instantiate();
- DefaultFont->load_memory(_font_NotoSans_Regular, _font_NotoSans_Regular_size, "ttf", default_font_size);
- DefaultFont->set_antialiased(font_antialiased);
- DefaultFont->set_hinting(font_hinting);
- DefaultFont->set_force_autohinter(true); //just looks better..i think?
-
- Ref<FontData> DefaultFontBold;
- DefaultFontBold.instantiate();
- DefaultFontBold->load_memory(_font_NotoSans_Bold, _font_NotoSans_Bold_size, "ttf", default_font_size);
- DefaultFontBold->set_antialiased(font_antialiased);
- DefaultFontBold->set_hinting(font_hinting);
- DefaultFontBold->set_force_autohinter(true); // just looks better..i think?
-
- Ref<FontData> FontArabic;
- FontArabic.instantiate();
- FontArabic->load_memory(_font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size, "ttf", default_font_size);
- FontArabic->set_antialiased(font_antialiased);
- FontArabic->set_hinting(font_hinting);
- FontArabic->set_force_autohinter(true); //just looks better..i think?
-
- Ref<FontData> FontArabicBold;
- FontArabicBold.instantiate();
- FontArabicBold->load_memory(_font_NotoNaskhArabicUI_Bold, _font_NotoNaskhArabicUI_Bold_size, "ttf", default_font_size);
- FontArabicBold->set_antialiased(font_antialiased);
- FontArabicBold->set_hinting(font_hinting);
- FontArabicBold->set_force_autohinter(true); //just looks better..i think?
-
- Ref<FontData> FontBengali;
- FontBengali.instantiate();
- FontBengali->load_memory(_font_NotoSansBengaliUI_Regular, _font_NotoSansBengaliUI_Regular_size, "ttf", default_font_size);
- FontBengali->set_antialiased(font_antialiased);
- FontBengali->set_hinting(font_hinting);
- FontBengali->set_force_autohinter(true); //just looks better..i think?
-
- Ref<FontData> FontBengaliBold;
- FontBengaliBold.instantiate();
- FontBengaliBold->load_memory(_font_NotoSansBengaliUI_Bold, _font_NotoSansBengaliUI_Bold_size, "ttf", default_font_size);
- FontBengaliBold->set_antialiased(font_antialiased);
- FontBengaliBold->set_hinting(font_hinting);
- FontBengaliBold->set_force_autohinter(true); //just looks better..i think?
-
- Ref<FontData> FontDevanagari;
- FontDevanagari.instantiate();
- FontDevanagari->load_memory(_font_NotoSansDevanagariUI_Regular, _font_NotoSansDevanagariUI_Regular_size, "ttf", default_font_size);
- FontDevanagari->set_antialiased(font_antialiased);
- FontDevanagari->set_hinting(font_hinting);
- FontDevanagari->set_force_autohinter(true); //just looks better..i think?
-
- Ref<FontData> FontDevanagariBold;
- FontDevanagariBold.instantiate();
- FontDevanagariBold->load_memory(_font_NotoSansDevanagariUI_Bold, _font_NotoSansDevanagariUI_Bold_size, "ttf", default_font_size);
- FontDevanagariBold->set_antialiased(font_antialiased);
- FontDevanagariBold->set_hinting(font_hinting);
- FontDevanagariBold->set_force_autohinter(true); //just looks better..i think?
-
- Ref<FontData> FontGeorgian;
- FontGeorgian.instantiate();
- FontGeorgian->load_memory(_font_NotoSansGeorgian_Regular, _font_NotoSansGeorgian_Regular_size, "ttf", default_font_size);
- FontGeorgian->set_antialiased(font_antialiased);
- FontGeorgian->set_hinting(font_hinting);
- FontGeorgian->set_force_autohinter(true); //just looks better..i think?
-
- Ref<FontData> FontGeorgianBold;
- FontGeorgianBold.instantiate();
- FontGeorgianBold->load_memory(_font_NotoSansGeorgian_Bold, _font_NotoSansGeorgian_Bold_size, "ttf", default_font_size);
- FontGeorgianBold->set_antialiased(font_antialiased);
- FontGeorgianBold->set_hinting(font_hinting);
- FontGeorgianBold->set_force_autohinter(true); //just looks better..i think?
-
- Ref<FontData> FontHebrew;
- FontHebrew.instantiate();
- FontHebrew->load_memory(_font_NotoSansHebrew_Regular, _font_NotoSansHebrew_Regular_size, "ttf", default_font_size);
- FontHebrew->set_antialiased(font_antialiased);
- FontHebrew->set_hinting(font_hinting);
- FontHebrew->set_force_autohinter(true); //just looks better..i think?
-
- Ref<FontData> FontHebrewBold;
- FontHebrewBold.instantiate();
- FontHebrewBold->load_memory(_font_NotoSansHebrew_Bold, _font_NotoSansHebrew_Bold_size, "ttf", default_font_size);
- FontHebrewBold->set_antialiased(font_antialiased);
- FontHebrewBold->set_hinting(font_hinting);
- FontHebrewBold->set_force_autohinter(true); //just looks better..i think?
-
- Ref<FontData> FontMalayalam;
- FontMalayalam.instantiate();
- FontMalayalam->load_memory(_font_NotoSansMalayalamUI_Regular, _font_NotoSansMalayalamUI_Regular_size, "ttf", default_font_size);
- FontMalayalam->set_antialiased(font_antialiased);
- FontMalayalam->set_hinting(font_hinting);
- FontMalayalam->set_force_autohinter(true); //just looks better..i think?
-
- Ref<FontData> FontMalayalamBold;
- FontMalayalamBold.instantiate();
- FontMalayalamBold->load_memory(_font_NotoSansMalayalamUI_Bold, _font_NotoSansMalayalamUI_Bold_size, "ttf", default_font_size);
- FontMalayalamBold->set_antialiased(font_antialiased);
- FontMalayalamBold->set_hinting(font_hinting);
- FontMalayalamBold->set_force_autohinter(true); //just looks better..i think?
-
- Ref<FontData> FontOriya;
- FontOriya.instantiate();
- FontOriya->load_memory(_font_NotoSansOriyaUI_Regular, _font_NotoSansOriyaUI_Regular_size, "ttf", default_font_size);
- FontOriya->set_antialiased(font_antialiased);
- FontOriya->set_hinting(font_hinting);
- FontOriya->set_force_autohinter(true); //just looks better..i think?
-
- Ref<FontData> FontOriyaBold;
- FontOriyaBold.instantiate();
- FontOriyaBold->load_memory(_font_NotoSansOriyaUI_Bold, _font_NotoSansOriyaUI_Bold_size, "ttf", default_font_size);
- FontOriyaBold->set_antialiased(font_antialiased);
- FontOriyaBold->set_hinting(font_hinting);
- FontOriyaBold->set_force_autohinter(true); //just looks better..i think?
-
- Ref<FontData> FontSinhala;
- FontSinhala.instantiate();
- FontSinhala->load_memory(_font_NotoSansSinhalaUI_Regular, _font_NotoSansSinhalaUI_Regular_size, "ttf", default_font_size);
- FontSinhala->set_antialiased(font_antialiased);
- FontSinhala->set_hinting(font_hinting);
- FontSinhala->set_force_autohinter(true); //just looks better..i think?
-
- Ref<FontData> FontSinhalaBold;
- FontSinhalaBold.instantiate();
- FontSinhalaBold->load_memory(_font_NotoSansSinhalaUI_Bold, _font_NotoSansSinhalaUI_Bold_size, "ttf", default_font_size);
- FontSinhalaBold->set_antialiased(font_antialiased);
- FontSinhalaBold->set_hinting(font_hinting);
- FontSinhalaBold->set_force_autohinter(true); //just looks better..i think?
-
- Ref<FontData> FontTamil;
- FontTamil.instantiate();
- FontTamil->load_memory(_font_NotoSansTamilUI_Regular, _font_NotoSansTamilUI_Regular_size, "ttf", default_font_size);
- FontTamil->set_antialiased(font_antialiased);
- FontTamil->set_hinting(font_hinting);
- FontTamil->set_force_autohinter(true); //just looks better..i think?
-
- Ref<FontData> FontTamilBold;
- FontTamilBold.instantiate();
- FontTamilBold->load_memory(_font_NotoSansTamilUI_Bold, _font_NotoSansTamilUI_Bold_size, "ttf", default_font_size);
- FontTamilBold->set_antialiased(font_antialiased);
- FontTamilBold->set_hinting(font_hinting);
- FontTamilBold->set_force_autohinter(true); //just looks better..i think?
-
- Ref<FontData> FontTelugu;
- FontTelugu.instantiate();
- FontTelugu->load_memory(_font_NotoSansTeluguUI_Regular, _font_NotoSansTeluguUI_Regular_size, "ttf", default_font_size);
- FontTelugu->set_antialiased(font_antialiased);
- FontTelugu->set_hinting(font_hinting);
- FontTelugu->set_force_autohinter(true); //just looks better..i think?
-
- Ref<FontData> FontTeluguBold;
- FontTeluguBold.instantiate();
- FontTeluguBold->load_memory(_font_NotoSansTeluguUI_Bold, _font_NotoSansTeluguUI_Bold_size, "ttf", default_font_size);
- FontTeluguBold->set_antialiased(font_antialiased);
- FontTeluguBold->set_hinting(font_hinting);
- FontTeluguBold->set_force_autohinter(true); //just looks better..i think?
-
- Ref<FontData> FontThai;
- FontThai.instantiate();
- FontThai->load_memory(_font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size, "ttf", default_font_size);
- FontThai->set_antialiased(font_antialiased);
- FontThai->set_hinting(font_hinting);
- FontThai->set_force_autohinter(true); //just looks better..i think?
-
- Ref<FontData> FontThaiBold;
- FontThaiBold.instantiate();
- FontThaiBold->load_memory(_font_NotoSansThaiUI_Bold, _font_NotoSansThaiUI_Bold_size, "ttf", default_font_size);
- FontThaiBold->set_antialiased(font_antialiased);
- FontThaiBold->set_hinting(font_hinting);
- FontThaiBold->set_force_autohinter(true); //just looks better..i think?
-
- /* Droid Sans Fallback */
-
- Ref<FontData> FontFallback;
- FontFallback.instantiate();
- FontFallback->load_memory(_font_DroidSansFallback, _font_DroidSansFallback_size, "ttf", default_font_size);
- FontFallback->set_antialiased(font_antialiased);
- FontFallback->set_hinting(font_hinting);
- FontFallback->set_force_autohinter(true); //just looks better..i think?
-
- /* Droid Sans Japanese */
-
- Ref<FontData> FontJapanese;
- FontJapanese.instantiate();
- FontJapanese->load_memory(_font_DroidSansJapanese, _font_DroidSansJapanese_size, "ttf", default_font_size);
- FontJapanese->set_antialiased(font_antialiased);
- FontJapanese->set_hinting(font_hinting);
- FontJapanese->set_force_autohinter(true); //just looks better..i think?
+ /* Noto Sans */
+
+ Ref<FontData> DefaultFont = load_cached_internal_font(_font_NotoSans_Regular, _font_NotoSans_Regular_size, font_hinting, font_antialiased, true);
+ Ref<FontData> DefaultFontBold = load_cached_internal_font(_font_NotoSans_Bold, _font_NotoSans_Bold_size, font_hinting, font_antialiased, true);
+ Ref<FontData> FontArabic = load_cached_internal_font(_font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size, font_hinting, font_antialiased, true);
+ Ref<FontData> FontArabicBold = load_cached_internal_font(_font_NotoNaskhArabicUI_Bold, _font_NotoNaskhArabicUI_Bold_size, font_hinting, font_antialiased, true);
+ Ref<FontData> FontBengali = load_cached_internal_font(_font_NotoSansBengaliUI_Regular, _font_NotoSansBengaliUI_Regular_size, font_hinting, font_antialiased, true);
+ Ref<FontData> FontBengaliBold = load_cached_internal_font(_font_NotoSansBengaliUI_Bold, _font_NotoSansBengaliUI_Bold_size, font_hinting, font_antialiased, true);
+ Ref<FontData> FontDevanagari = load_cached_internal_font(_font_NotoSansDevanagariUI_Regular, _font_NotoSansDevanagariUI_Regular_size, font_hinting, font_antialiased, true);
+ Ref<FontData> FontDevanagariBold = load_cached_internal_font(_font_NotoSansDevanagariUI_Bold, _font_NotoSansDevanagariUI_Bold_size, font_hinting, font_antialiased, true);
+ Ref<FontData> FontGeorgian = load_cached_internal_font(_font_NotoSansGeorgian_Regular, _font_NotoSansGeorgian_Regular_size, font_hinting, font_antialiased, true);
+ Ref<FontData> FontGeorgianBold = load_cached_internal_font(_font_NotoSansGeorgian_Bold, _font_NotoSansGeorgian_Bold_size, font_hinting, font_antialiased, true);
+ Ref<FontData> FontHebrew = load_cached_internal_font(_font_NotoSansHebrew_Regular, _font_NotoSansHebrew_Regular_size, font_hinting, font_antialiased, true);
+ Ref<FontData> FontHebrewBold = load_cached_internal_font(_font_NotoSansHebrew_Bold, _font_NotoSansHebrew_Bold_size, font_hinting, font_antialiased, true);
+ Ref<FontData> FontMalayalam = load_cached_internal_font(_font_NotoSansMalayalamUI_Regular, _font_NotoSansMalayalamUI_Regular_size, font_hinting, font_antialiased, true);
+ Ref<FontData> FontMalayalamBold = load_cached_internal_font(_font_NotoSansMalayalamUI_Bold, _font_NotoSansMalayalamUI_Bold_size, font_hinting, font_antialiased, true);
+ Ref<FontData> FontOriya = load_cached_internal_font(_font_NotoSansOriyaUI_Regular, _font_NotoSansOriyaUI_Regular_size, font_hinting, font_antialiased, true);
+ Ref<FontData> FontOriyaBold = load_cached_internal_font(_font_NotoSansOriyaUI_Bold, _font_NotoSansOriyaUI_Bold_size, font_hinting, font_antialiased, true);
+ Ref<FontData> FontSinhala = load_cached_internal_font(_font_NotoSansSinhalaUI_Regular, _font_NotoSansSinhalaUI_Regular_size, font_hinting, font_antialiased, true);
+ Ref<FontData> FontSinhalaBold = load_cached_internal_font(_font_NotoSansSinhalaUI_Bold, _font_NotoSansSinhalaUI_Bold_size, font_hinting, font_antialiased, true);
+ Ref<FontData> FontTamil = load_cached_internal_font(_font_NotoSansTamilUI_Regular, _font_NotoSansTamilUI_Regular_size, font_hinting, font_antialiased, true);
+ Ref<FontData> FontTamilBold = load_cached_internal_font(_font_NotoSansTamilUI_Bold, _font_NotoSansTamilUI_Bold_size, font_hinting, font_antialiased, true);
+ Ref<FontData> FontTelugu = load_cached_internal_font(_font_NotoSansTeluguUI_Regular, _font_NotoSansTeluguUI_Regular_size, font_hinting, font_antialiased, true);
+ Ref<FontData> FontTeluguBold = load_cached_internal_font(_font_NotoSansTeluguUI_Bold, _font_NotoSansTeluguUI_Bold_size, font_hinting, font_antialiased, true);
+ Ref<FontData> FontThai = load_cached_internal_font(_font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size, font_hinting, font_antialiased, true);
+ Ref<FontData> FontThaiBold = load_cached_internal_font(_font_NotoSansThaiUI_Bold, _font_NotoSansThaiUI_Bold_size, font_hinting, font_antialiased, true);
+
+ /* Droid Sans */
+
+ Ref<FontData> FontFallback = load_cached_internal_font(_font_DroidSansFallback, _font_DroidSansFallback_size, font_hinting, font_antialiased, true);
+ Ref<FontData> FontJapanese = load_cached_internal_font(_font_DroidSansJapanese, _font_DroidSansJapanese_size, font_hinting, font_antialiased, true);
/* Hack */
- Ref<FontData> dfmono;
- dfmono.instantiate();
- dfmono->load_memory(_font_Hack_Regular, _font_Hack_Regular_size, "ttf", default_font_size);
- dfmono->set_antialiased(font_antialiased);
- dfmono->set_hinting(font_hinting);
-
- Vector<String> subtag = String(EditorSettings::get_singleton()->get("interface/editor/code_font_custom_variations")).split(",");
- Dictionary ftrs;
- for (int i = 0; i < subtag.size(); i++) {
- Vector<String> subtag_a = subtag[i].split("=");
- if (subtag_a.size() == 2) {
- dfmono->set_variation(subtag_a[0], subtag_a[1].to_float());
- }
- }
+ Ref<FontData> dfmono = load_cached_internal_font(_font_Hack_Regular, _font_Hack_Regular_size, font_hinting, font_antialiased, true);
// Default font
- MAKE_DEFAULT_FONT(df);
+ MAKE_DEFAULT_FONT(df, String(), default_font_size);
p_theme->set_default_theme_font(df); // Default theme font
p_theme->set_default_theme_font_size(default_font_size);
@@ -404,7 +283,7 @@ void editor_register_fonts(Ref<Theme> p_theme) {
p_theme->set_font("main", "EditorFonts", df);
// Bold font
- MAKE_BOLD_FONT(df_bold);
+ MAKE_BOLD_FONT(df_bold, String(), default_font_size);
p_theme->set_font_size("bold_size", "EditorFonts", default_font_size);
p_theme->set_font("bold", "EditorFonts", df_bold);
@@ -430,7 +309,8 @@ void editor_register_fonts(Ref<Theme> p_theme) {
p_theme->set_font_size("font_size", "HeaderLarge", default_font_size + 3 * EDSCALE);
// Documentation fonts
- MAKE_SOURCE_FONT(df_code);
+ String code_font_custom_variations = EditorSettings::get_singleton()->get("interface/editor/code_font_custom_variations");
+ MAKE_SOURCE_FONT(df_code, code_font_custom_variations, default_font_size);
p_theme->set_font_size("doc_size", "EditorFonts", int(EDITOR_GET("text_editor/help/help_font_size")) * EDSCALE);
p_theme->set_font("doc", "EditorFonts", df);
p_theme->set_font_size("doc_bold_size", "EditorFonts", int(EDITOR_GET("text_editor/help/help_font_size")) * EDSCALE);
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 2263f30931..b1c546a8c0 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -97,10 +97,14 @@
#include "editor/editor_translation_parser.h"
#include "editor/export_template_manager.h"
#include "editor/filesystem_dock.h"
+#include "editor/import/dynamicfont_import_settings.h"
#include "editor/import/editor_import_collada.h"
#include "editor/import/resource_importer_bitmask.h"
+#include "editor/import/resource_importer_bmfont.h"
#include "editor/import/resource_importer_csv_translation.h"
+#include "editor/import/resource_importer_dynamicfont.h"
#include "editor/import/resource_importer_image.h"
+#include "editor/import/resource_importer_imagefont.h"
#include "editor/import/resource_importer_layered_texture.h"
#include "editor/import/resource_importer_obj.h"
#include "editor/import/resource_importer_scene.h"
@@ -5853,6 +5857,18 @@ EditorNode::EditorNode() {
import_texture_atlas.instantiate();
ResourceFormatImporter::get_singleton()->add_importer(import_texture_atlas);
+ Ref<ResourceImporterDynamicFont> import_font_data_dynamic;
+ import_font_data_dynamic.instantiate();
+ ResourceFormatImporter::get_singleton()->add_importer(import_font_data_dynamic);
+
+ Ref<ResourceImporterBMFont> import_font_data_bmfont;
+ import_font_data_bmfont.instantiate();
+ ResourceFormatImporter::get_singleton()->add_importer(import_font_data_bmfont);
+
+ Ref<ResourceImporterImageFont> import_font_data_image;
+ import_font_data_image.instantiate();
+ ResourceFormatImporter::get_singleton()->add_importer(import_font_data_image);
+
Ref<ResourceImporterCSVTranslation> import_csv_translation;
import_csv_translation.instantiate();
ResourceFormatImporter::get_singleton()->add_importer(import_csv_translation);
@@ -6258,6 +6274,9 @@ EditorNode::EditorNode() {
scene_import_settings = memnew(SceneImportSettings);
gui_base->add_child(scene_import_settings);
+ fontdata_import_settings = memnew(DynamicFontImportSettings);
+ gui_base->add_child(fontdata_import_settings);
+
export_template_manager = memnew(ExportTemplateManager);
gui_base->add_child(export_template_manager);
diff --git a/editor/editor_node.h b/editor/editor_node.h
index bf26a6879b..51d01d07ef 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -93,6 +93,7 @@ class Window;
class SubViewport;
class SceneImportSettings;
class EditorExtensionManager;
+class DynamicFontImportSettings;
class EditorNode : public Node {
GDCLASS(EditorNode, Node);
@@ -422,6 +423,7 @@ private:
EditorResourcePreview *resource_preview;
EditorFolding editor_folding;
+ DynamicFontImportSettings *fontdata_import_settings;
SceneImportSettings *scene_import_settings;
struct BottomPanelItem {
String name;
diff --git a/editor/import/dynamicfont_import_settings.cpp b/editor/import/dynamicfont_import_settings.cpp
new file mode 100644
index 0000000000..37ca40287f
--- /dev/null
+++ b/editor/import/dynamicfont_import_settings.cpp
@@ -0,0 +1,1889 @@
+/*************************************************************************/
+/* dynamicfont_import_settings.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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. */
+/*************************************************************************/
+
+#include "dynamicfont_import_settings.h"
+
+#include "editor/editor_node.h"
+#include "editor/editor_scale.h"
+
+/*************************************************************************/
+/* Settings data */
+/*************************************************************************/
+
+class DynamicFontImportSettingsData : public RefCounted {
+ GDCLASS(DynamicFontImportSettingsData, RefCounted)
+ friend class DynamicFontImportSettings;
+
+ Map<StringName, Variant> settings;
+ Map<StringName, Variant> defaults;
+ List<ResourceImporter::ImportOption> options;
+ DynamicFontImportSettings *owner = nullptr;
+
+ bool _set(const StringName &p_name, const Variant &p_value) {
+ if (defaults.has(p_name) && defaults[p_name] == p_value) {
+ settings.erase(p_name);
+ } else {
+ settings[p_name] = p_value;
+ }
+ return true;
+ }
+
+ bool _get(const StringName &p_name, Variant &r_ret) const {
+ if (settings.has(p_name)) {
+ r_ret = settings[p_name];
+ return true;
+ }
+ if (defaults.has(p_name)) {
+ r_ret = defaults[p_name];
+ return true;
+ }
+ return false;
+ }
+
+ void _get_property_list(List<PropertyInfo> *p_list) const {
+ for (const List<ResourceImporter::ImportOption>::Element *E = options.front(); E; E = E->next()) {
+ if (owner && owner->import_settings_data.is_valid()) {
+ if (owner->import_settings_data->get("multichannel_signed_distance_field") && (E->get().option.name == "size" || E->get().option.name == "outline_size" || E->get().option.name == "oversampling")) {
+ continue;
+ }
+ if (!owner->import_settings_data->get("multichannel_signed_distance_field") && (E->get().option.name == "msdf_pixel_range" || E->get().option.name == "msdf_size")) {
+ continue;
+ }
+ }
+ p_list->push_back(E->get().option);
+ }
+ }
+};
+
+/*************************************************************************/
+/* Glyph ranges */
+/*************************************************************************/
+
+struct UniRange {
+ int32_t start;
+ int32_t end;
+ String name;
+};
+
+static UniRange unicode_ranges[] = {
+ { 0x0000, 0x007F, U"Basic Latin" },
+ { 0x0080, 0x00FF, U"Latin-1 Supplement" },
+ { 0x0100, 0x017F, U"Latin Extended-A" },
+ { 0x0180, 0x024F, U"Latin Extended-B" },
+ { 0x0250, 0x02AF, U"IPA Extensions" },
+ { 0x02B0, 0x02FF, U"Spacing Modifier Letters" },
+ { 0x0300, 0x036F, U"Combining Diacritical Marks" },
+ { 0x0370, 0x03FF, U"Greek and Coptic" },
+ { 0x0400, 0x04FF, U"Cyrillic" },
+ { 0x0500, 0x052F, U"Cyrillic Supplement" },
+ { 0x0530, 0x058F, U"Armenian" },
+ { 0x0590, 0x05FF, U"Hebrew" },
+ { 0x0600, 0x06FF, U"Arabic" },
+ { 0x0700, 0x074F, U"Syriac" },
+ { 0x0750, 0x077F, U"Arabic Supplement" },
+ { 0x0780, 0x07BF, U"Thaana" },
+ { 0x07C0, 0x07FF, U"N'Ko" },
+ { 0x0800, 0x083F, U"Samaritan" },
+ { 0x0840, 0x085F, U"Mandaic" },
+ { 0x0860, 0x086F, U"Syriac Supplement" },
+ { 0x08A0, 0x08FF, U"Arabic Extended-A" },
+ { 0x0900, 0x097F, U"Devanagari" },
+ { 0x0980, 0x09FF, U"Bengali" },
+ { 0x0A00, 0x0A7F, U"Gurmukhi" },
+ { 0x0A80, 0x0AFF, U"Gujarati" },
+ { 0x0B00, 0x0B7F, U"Oriya" },
+ { 0x0B80, 0x0BFF, U"Tamil" },
+ { 0x0C00, 0x0C7F, U"Telugu" },
+ { 0x0C80, 0x0CFF, U"Kannada" },
+ { 0x0D00, 0x0D7F, U"Malayalam" },
+ { 0x0D80, 0x0DFF, U"Sinhala" },
+ { 0x0E00, 0x0E7F, U"Thai" },
+ { 0x0E80, 0x0EFF, U"Lao" },
+ { 0x0F00, 0x0FFF, U"Tibetan" },
+ { 0x1000, 0x109F, U"Myanmar" },
+ { 0x10A0, 0x10FF, U"Georgian" },
+ { 0x1100, 0x11FF, U"Hangul Jamo" },
+ { 0x1200, 0x137F, U"Ethiopic" },
+ { 0x1380, 0x139F, U"Ethiopic Supplement" },
+ { 0x13A0, 0x13FF, U"Cherokee" },
+ { 0x1400, 0x167F, U"Unified Canadian Aboriginal Syllabics" },
+ { 0x1680, 0x169F, U"Ogham" },
+ { 0x16A0, 0x16FF, U"Runic" },
+ { 0x1700, 0x171F, U"Tagalog" },
+ { 0x1720, 0x173F, U"Hanunoo" },
+ { 0x1740, 0x175F, U"Buhid" },
+ { 0x1760, 0x177F, U"Tagbanwa" },
+ { 0x1780, 0x17FF, U"Khmer" },
+ { 0x1800, 0x18AF, U"Mongolian" },
+ { 0x18B0, 0x18FF, U"Unified Canadian Aboriginal Syllabics Extended" },
+ { 0x1900, 0x194F, U"Limbu" },
+ { 0x1950, 0x197F, U"Tai Le" },
+ { 0x1980, 0x19DF, U"New Tai Lue" },
+ { 0x19E0, 0x19FF, U"Khmer Symbols" },
+ { 0x1A00, 0x1A1F, U"Buginese" },
+ { 0x1A20, 0x1AAF, U"Tai Tham" },
+ { 0x1AB0, 0x1AFF, U"Combining Diacritical Marks Extended" },
+ { 0x1B00, 0x1B7F, U"Balinese" },
+ { 0x1B80, 0x1BBF, U"Sundanese" },
+ { 0x1BC0, 0x1BFF, U"Batak" },
+ { 0x1C00, 0x1C4F, U"Lepcha" },
+ { 0x1C50, 0x1C7F, U"Ol Chiki" },
+ { 0x1C80, 0x1C8F, U"Cyrillic Extended-C" },
+ { 0x1C90, 0x1CBF, U"Georgian Extended" },
+ { 0x1CC0, 0x1CCF, U"Sundanese Supplement" },
+ { 0x1CD0, 0x1CFF, U"Vedic Extensions" },
+ { 0x1D00, 0x1D7F, U"Phonetic Extensions" },
+ { 0x1D80, 0x1DBF, U"Phonetic Extensions Supplement" },
+ { 0x1DC0, 0x1DFF, U"Combining Diacritical Marks Supplement" },
+ { 0x1E00, 0x1EFF, U"Latin Extended Additional" },
+ { 0x1F00, 0x1FFF, U"Greek Extended" },
+ { 0x2000, 0x206F, U"General Punctuation" },
+ { 0x2070, 0x209F, U"Superscripts and Subscripts" },
+ { 0x20A0, 0x20CF, U"Currency Symbols" },
+ { 0x20D0, 0x20FF, U"Combining Diacritical Marks for Symbols" },
+ { 0x2100, 0x214F, U"Letterlike Symbols" },
+ { 0x2150, 0x218F, U"Number Forms" },
+ { 0x2190, 0x21FF, U"Arrows" },
+ { 0x2200, 0x22FF, U"Mathematical Operators" },
+ { 0x2300, 0x23FF, U"Miscellaneous Technical" },
+ { 0x2400, 0x243F, U"Control Pictures" },
+ { 0x2440, 0x245F, U"Optical Character Recognition" },
+ { 0x2460, 0x24FF, U"Enclosed Alphanumerics" },
+ { 0x2500, 0x257F, U"Box Drawing" },
+ { 0x2580, 0x259F, U"Block Elements" },
+ { 0x25A0, 0x25FF, U"Geometric Shapes" },
+ { 0x2600, 0x26FF, U"Miscellaneous Symbols" },
+ { 0x2700, 0x27BF, U"Dingbats" },
+ { 0x27C0, 0x27EF, U"Miscellaneous Mathematical Symbols-A" },
+ { 0x27F0, 0x27FF, U"Supplemental Arrows-A" },
+ { 0x2800, 0x28FF, U"Braille Patterns" },
+ { 0x2900, 0x297F, U"Supplemental Arrows-B" },
+ { 0x2980, 0x29FF, U"Miscellaneous Mathematical Symbols-B" },
+ { 0x2A00, 0x2AFF, U"Supplemental Mathematical Operators" },
+ { 0x2B00, 0x2BFF, U"Miscellaneous Symbols and Arrows" },
+ { 0x2C00, 0x2C5F, U"Glagolitic" },
+ { 0x2C60, 0x2C7F, U"Latin Extended-C" },
+ { 0x2C80, 0x2CFF, U"Coptic" },
+ { 0x2D00, 0x2D2F, U"Georgian Supplement" },
+ { 0x2D30, 0x2D7F, U"Tifinagh" },
+ { 0x2D80, 0x2DDF, U"Ethiopic Extended" },
+ { 0x2DE0, 0x2DFF, U"Cyrillic Extended-A" },
+ { 0x2E00, 0x2E7F, U"Supplemental Punctuation" },
+ { 0x2E80, 0x2EFF, U"CJK Radicals Supplement" },
+ { 0x2F00, 0x2FDF, U"Kangxi Radicals" },
+ { 0x2FF0, 0x2FFF, U"Ideographic Description Characters" },
+ { 0x3000, 0x303F, U"CJK Symbols and Punctuation" },
+ { 0x3040, 0x309F, U"Hiragana" },
+ { 0x30A0, 0x30FF, U"Katakana" },
+ { 0x3100, 0x312F, U"Bopomofo" },
+ { 0x3130, 0x318F, U"Hangul Compatibility Jamo" },
+ { 0x3190, 0x319F, U"Kanbun" },
+ { 0x31A0, 0x31BF, U"Bopomofo Extended" },
+ { 0x31C0, 0x31EF, U"CJK Strokes" },
+ { 0x31F0, 0x31FF, U"Katakana Phonetic Extensions" },
+ { 0x3200, 0x32FF, U"Enclosed CJK Letters and Months" },
+ { 0x3300, 0x33FF, U"CJK Compatibility" },
+ { 0x3400, 0x4DBF, U"CJK Unified Ideographs Extension A" },
+ { 0x4DC0, 0x4DFF, U"Yijing Hexagram Symbols" },
+ { 0x4E00, 0x9FFF, U"CJK Unified Ideographs" },
+ { 0xA000, 0xA48F, U"Yi Syllables" },
+ { 0xA490, 0xA4CF, U"Yi Radicals" },
+ { 0xA4D0, 0xA4FF, U"Lisu" },
+ { 0xA500, 0xA63F, U"Vai" },
+ { 0xA640, 0xA69F, U"Cyrillic Extended-B" },
+ { 0xA6A0, 0xA6FF, U"Bamum" },
+ { 0xA700, 0xA71F, U"Modifier Tone Letters" },
+ { 0xA720, 0xA7FF, U"Latin Extended-D" },
+ { 0xA800, 0xA82F, U"Syloti Nagri" },
+ { 0xA830, 0xA83F, U"Common Indic Number Forms" },
+ { 0xA840, 0xA87F, U"Phags-pa" },
+ { 0xA880, 0xA8DF, U"Saurashtra" },
+ { 0xA8E0, 0xA8FF, U"Devanagari Extended" },
+ { 0xA900, 0xA92F, U"Kayah Li" },
+ { 0xA930, 0xA95F, U"Rejang" },
+ { 0xA960, 0xA97F, U"Hangul Jamo Extended-A" },
+ { 0xA980, 0xA9DF, U"Javanese" },
+ { 0xA9E0, 0xA9FF, U"Myanmar Extended-B" },
+ { 0xAA00, 0xAA5F, U"Cham" },
+ { 0xAA60, 0xAA7F, U"Myanmar Extended-A" },
+ { 0xAA80, 0xAADF, U"Tai Viet" },
+ { 0xAAE0, 0xAAFF, U"Meetei Mayek Extensions" },
+ { 0xAB00, 0xAB2F, U"Ethiopic Extended-A" },
+ { 0xAB30, 0xAB6F, U"Latin Extended-E" },
+ { 0xAB70, 0xABBF, U"Cherokee Supplement" },
+ { 0xABC0, 0xABFF, U"Meetei Mayek" },
+ { 0xD7B0, 0xD7FF, U"Hangul Jamo Extended-B" },
+ //{ 0xF800, 0xDFFF, U"Surrogates" },
+ { 0xE000, 0xE2FE, U"Private Use Area" },
+ { 0xF900, 0xFAFF, U"CJK Compatibility Ideographs" },
+ { 0xFB00, 0xFB4F, U"Alphabetic Presentation Forms" },
+ { 0xFB50, 0xFDFF, U"Arabic Presentation Forms-A" },
+ //{ 0xFE00, 0xFE0F, U"Variation Selectors" },
+ { 0xFE10, 0xFE1F, U"Vertical Forms" },
+ { 0xFE20, 0xFE2F, U"Combining Half Marks" },
+ { 0xFE30, 0xFE4F, U"CJK Compatibility Forms" },
+ { 0xFE50, 0xFE6F, U"Small Form Variants" },
+ { 0xFE70, 0xFEFF, U"Arabic Presentation Forms-B" },
+ { 0xFF00, 0xFFEF, U"Halfwidth and Fullwidth Forms" },
+ //{ 0xFFF0, 0xFFFF, U"Specials" },
+ { 0x10000, 0x1007F, U"Linear B Syllabary" },
+ { 0x10080, 0x100FF, U"Linear B Ideograms" },
+ { 0x10100, 0x1013F, U"Aegean Numbers" },
+ { 0x10140, 0x1018F, U"Ancient Greek Numbers" },
+ { 0x10190, 0x101CF, U"Ancient Symbols" },
+ { 0x101D0, 0x101FF, U"Phaistos Disc" },
+ { 0x10280, 0x1029F, U"Lycian" },
+ { 0x102A0, 0x102DF, U"Carian" },
+ { 0x102E0, 0x102FF, U"Coptic Epact Numbers" },
+ { 0x10300, 0x1032F, U"Old Italic" },
+ { 0x10330, 0x1034F, U"Gothic" },
+ { 0x10350, 0x1037F, U"Old Permic" },
+ { 0x10380, 0x1039F, U"Ugaritic" },
+ { 0x103A0, 0x103DF, U"Old Persian" },
+ { 0x10400, 0x1044F, U"Deseret" },
+ { 0x10450, 0x1047F, U"Shavian" },
+ { 0x10480, 0x104AF, U"Osmanya" },
+ { 0x104B0, 0x104FF, U"Osage" },
+ { 0x10500, 0x1052F, U"Elbasan" },
+ { 0x10530, 0x1056F, U"Caucasian Albanian" },
+ { 0x10600, 0x1077F, U"Linear A" },
+ { 0x10800, 0x1083F, U"Cypriot Syllabary" },
+ { 0x10840, 0x1085F, U"Imperial Aramaic" },
+ { 0x10860, 0x1087F, U"Palmyrene" },
+ { 0x10880, 0x108AF, U"Nabataean" },
+ { 0x108E0, 0x108FF, U"Hatran" },
+ { 0x10900, 0x1091F, U"Phoenician" },
+ { 0x10920, 0x1093F, U"Lydian" },
+ { 0x10980, 0x1099F, U"Meroitic Hieroglyphs" },
+ { 0x109A0, 0x109FF, U"Meroitic Cursive" },
+ { 0x10A00, 0x10A5F, U"Kharoshthi" },
+ { 0x10A60, 0x10A7F, U"Old South Arabian" },
+ { 0x10A80, 0x10A9F, U"Old North Arabian" },
+ { 0x10AC0, 0x10AFF, U"Manichaean" },
+ { 0x10B00, 0x10B3F, U"Avestan" },
+ { 0x10B40, 0x10B5F, U"Inscriptional Parthian" },
+ { 0x10B60, 0x10B7F, U"Inscriptional Pahlavi" },
+ { 0x10B80, 0x10BAF, U"Psalter Pahlavi" },
+ { 0x10C00, 0x10C4F, U"Old Turkic" },
+ { 0x10C80, 0x10CFF, U"Old Hungarian" },
+ { 0x10D00, 0x10D3F, U"Hanifi Rohingya" },
+ { 0x10E60, 0x10E7F, U"Rumi Numeral Symbols" },
+ { 0x10E80, 0x10EBF, U"Yezidi" },
+ { 0x10F00, 0x10F2F, U"Old Sogdian" },
+ { 0x10F30, 0x10F6F, U"Sogdian" },
+ { 0x10FB0, 0x10FDF, U"Chorasmian" },
+ { 0x10FE0, 0x10FFF, U"Elymaic" },
+ { 0x11000, 0x1107F, U"Brahmi" },
+ { 0x11080, 0x110CF, U"Kaithi" },
+ { 0x110D0, 0x110FF, U"Sora Sompeng" },
+ { 0x11100, 0x1114F, U"Chakma" },
+ { 0x11150, 0x1117F, U"Mahajani" },
+ { 0x11180, 0x111DF, U"Sharada" },
+ { 0x111E0, 0x111FF, U"Sinhala Archaic Numbers" },
+ { 0x11200, 0x1124F, U"Khojki" },
+ { 0x11280, 0x112AF, U"Multani" },
+ { 0x112B0, 0x112FF, U"Khudawadi" },
+ { 0x11300, 0x1137F, U"Grantha" },
+ { 0x11400, 0x1147F, U"Newa" },
+ { 0x11480, 0x114DF, U"Tirhuta" },
+ { 0x11580, 0x115FF, U"Siddham" },
+ { 0x11600, 0x1165F, U"Modi" },
+ { 0x11660, 0x1167F, U"Mongolian Supplement" },
+ { 0x11680, 0x116CF, U"Takri" },
+ { 0x11700, 0x1173F, U"Ahom" },
+ { 0x11800, 0x1184F, U"Dogra" },
+ { 0x118A0, 0x118FF, U"Warang Citi" },
+ { 0x11900, 0x1195F, U"Dives Akuru" },
+ { 0x119A0, 0x119FF, U"Nandinagari" },
+ { 0x11A00, 0x11A4F, U"Zanabazar Square" },
+ { 0x11A50, 0x11AAF, U"Soyombo" },
+ { 0x11AC0, 0x11AFF, U"Pau Cin Hau" },
+ { 0x11C00, 0x11C6F, U"Bhaiksuki" },
+ { 0x11C70, 0x11CBF, U"Marchen" },
+ { 0x11D00, 0x11D5F, U"Masaram Gondi" },
+ { 0x11D60, 0x11DAF, U"Gunjala Gondi" },
+ { 0x11EE0, 0x11EFF, U"Makasar" },
+ { 0x11FB0, 0x11FBF, U"Lisu Supplement" },
+ { 0x11FC0, 0x11FFF, U"Tamil Supplement" },
+ { 0x12000, 0x123FF, U"Cuneiform" },
+ { 0x12400, 0x1247F, U"Cuneiform Numbers and Punctuation" },
+ { 0x12480, 0x1254F, U"Early Dynastic Cuneiform" },
+ { 0x13000, 0x1342F, U"Egyptian Hieroglyphs" },
+ { 0x13430, 0x1343F, U"Egyptian Hieroglyph Format Controls" },
+ { 0x14400, 0x1467F, U"Anatolian Hieroglyphs" },
+ { 0x16800, 0x16A3F, U"Bamum Supplement" },
+ { 0x16A40, 0x16A6F, U"Mro" },
+ { 0x16AD0, 0x16AFF, U"Bassa Vah" },
+ { 0x16B00, 0x16B8F, U"Pahawh Hmong" },
+ { 0x16E40, 0x16E9F, U"Medefaidrin" },
+ { 0x16F00, 0x16F9F, U"Miao" },
+ { 0x16FE0, 0x16FFF, U"Ideographic Symbols and Punctuation" },
+ { 0x17000, 0x187FF, U"Tangut" },
+ { 0x18800, 0x18AFF, U"Tangut Components" },
+ { 0x18B00, 0x18CFF, U"Khitan Small Script" },
+ { 0x18D00, 0x18D8F, U"Tangut Supplement" },
+ { 0x1B000, 0x1B0FF, U"Kana Supplement" },
+ { 0x1B100, 0x1B12F, U"Kana Extended-A" },
+ { 0x1B130, 0x1B16F, U"Small Kana Extension" },
+ { 0x1B170, 0x1B2FF, U"Nushu" },
+ { 0x1BC00, 0x1BC9F, U"Duployan" },
+ { 0x1BCA0, 0x1BCAF, U"Shorthand Format Controls" },
+ { 0x1D000, 0x1D0FF, U"Byzantine Musical Symbols" },
+ { 0x1D100, 0x1D1FF, U"Musical Symbols" },
+ { 0x1D200, 0x1D24F, U"Ancient Greek Musical Notation" },
+ { 0x1D2E0, 0x1D2FF, U"Mayan Numerals" },
+ { 0x1D300, 0x1D35F, U"Tai Xuan Jing Symbols" },
+ { 0x1D360, 0x1D37F, U"Counting Rod Numerals" },
+ { 0x1D400, 0x1D7FF, U"Mathematical Alphanumeric Symbols" },
+ { 0x1D800, 0x1DAAF, U"Sutton SignWriting" },
+ { 0x1E000, 0x1E02F, U"Glagolitic Supplement" },
+ { 0x1E100, 0x1E14F, U"Nyiakeng Puachue Hmong" },
+ { 0x1E2C0, 0x1E2FF, U"Wancho" },
+ { 0x1E800, 0x1E8DF, U"Mende Kikakui" },
+ { 0x1E900, 0x1E95F, U"Adlam" },
+ { 0x1EC70, 0x1ECBF, U"Indic Siyaq Numbers" },
+ { 0x1ED00, 0x1ED4F, U"Ottoman Siyaq Numbers" },
+ { 0x1EE00, 0x1EEFF, U"Arabic Mathematical Alphabetic Symbols" },
+ { 0x1F000, 0x1F02F, U"Mahjong Tiles" },
+ { 0x1F030, 0x1F09F, U"Domino Tiles" },
+ { 0x1F0A0, 0x1F0FF, U"Playing Cards" },
+ { 0x1F100, 0x1F1FF, U"Enclosed Alphanumeric Supplement" },
+ { 0x1F200, 0x1F2FF, U"Enclosed Ideographic Supplement" },
+ { 0x1F300, 0x1F5FF, U"Miscellaneous Symbols and Pictographs" },
+ { 0x1F600, 0x1F64F, U"Emoticons" },
+ { 0x1F650, 0x1F67F, U"Ornamental Dingbats" },
+ { 0x1F680, 0x1F6FF, U"Transport and Map Symbols" },
+ { 0x1F700, 0x1F77F, U"Alchemical Symbols" },
+ { 0x1F780, 0x1F7FF, U"Geometric Shapes Extended" },
+ { 0x1F800, 0x1F8FF, U"Supplemental Arrows-C" },
+ { 0x1F900, 0x1F9FF, U"Supplemental Symbols and Pictographs" },
+ { 0x1FA00, 0x1FA6F, U"Chess Symbols" },
+ { 0x1FA70, 0x1FAFF, U"Symbols and Pictographs Extended-A" },
+ { 0x1FB00, 0x1FBFF, U"Symbols for Legacy Computing" },
+ { 0x20000, 0x2A6DF, U"CJK Unified Ideographs Extension B" },
+ { 0x2A700, 0x2B73F, U"CJK Unified Ideographs Extension C" },
+ { 0x2B740, 0x2B81F, U"CJK Unified Ideographs Extension D" },
+ { 0x2B820, 0x2CEAF, U"CJK Unified Ideographs Extension E" },
+ { 0x2CEB0, 0x2EBEF, U"CJK Unified Ideographs Extension F" },
+ { 0x2F800, 0x2FA1F, U"CJK Compatibility Ideographs Supplement" },
+ { 0x30000, 0x3134F, U"CJK Unified Ideographs Extension G" },
+ //{ 0xE0000, 0xE007F, U"Tags" },
+ //{ 0xE0100, 0xE01EF, U"Variation Selectors Supplement" },
+ { 0xF0000, 0xFFFFD, U"Supplementary Private Use Area-A" },
+ { 0x100000, 0x10FFFD, U"Supplementary Private Use Area-B" },
+ { 0x10FFFF, 0x10FFFF, String() }
+};
+
+void DynamicFontImportSettings::_add_glyph_range_item(int32_t p_start, int32_t p_end, const String &p_name) {
+ const int page_size = 512;
+ int pages = (p_end - p_start) / page_size;
+ int remain = (p_end - p_start) % page_size;
+
+ int32_t start = p_start;
+ for (int i = 0; i < pages; i++) {
+ TreeItem *item = glyph_tree->create_item(glyph_root);
+ ERR_FAIL_NULL(item);
+ item->set_text(0, _pad_zeros(String::num_int64(start, 16)) + " - " + _pad_zeros(String::num_int64(start + page_size, 16)));
+ item->set_text(1, p_name);
+ item->set_metadata(0, Vector2i(start, start + page_size));
+ start += page_size;
+ }
+ if (remain > 0) {
+ TreeItem *item = glyph_tree->create_item(glyph_root);
+ ERR_FAIL_NULL(item);
+ item->set_text(0, _pad_zeros(String::num_int64(start, 16)) + " - " + _pad_zeros(String::num_int64(p_end, 16)));
+ item->set_text(1, p_name);
+ item->set_metadata(0, Vector2i(start, p_end));
+ }
+}
+
+/*************************************************************************/
+/* Languages and scripts */
+/*************************************************************************/
+
+struct CodeInfo {
+ String name;
+ String code;
+};
+
+static CodeInfo langs[] = {
+ { U"Custom", U"xx" },
+ { U"-", U"-" },
+ { U"Abkhazian", U"ab" },
+ { U"Afar", U"aa" },
+ { U"Afrikaans", U"af" },
+ { U"Akan", U"ak" },
+ { U"Albanian", U"sq" },
+ { U"Amharic", U"am" },
+ { U"Arabic", U"ar" },
+ { U"Aragonese", U"an" },
+ { U"Armenian", U"hy" },
+ { U"Assamese", U"as" },
+ { U"Avaric", U"av" },
+ { U"Avestan", U"ae" },
+ { U"Aymara", U"ay" },
+ { U"Azerbaijani", U"az" },
+ { U"Bambara", U"bm" },
+ { U"Bashkir", U"ba" },
+ { U"Basque", U"eu" },
+ { U"Belarusian", U"be" },
+ { U"Bengali", U"bn" },
+ { U"Bihari", U"bh" },
+ { U"Bislama", U"bi" },
+ { U"Bosnian", U"bs" },
+ { U"Breton", U"br" },
+ { U"Bulgarian", U"bg" },
+ { U"Burmese", U"my" },
+ { U"Catalan", U"ca" },
+ { U"Chamorro", U"ch" },
+ { U"Chechen", U"ce" },
+ { U"Chichewa", U"ny" },
+ { U"Chinese", U"zh" },
+ { U"Chuvash", U"cv" },
+ { U"Cornish", U"kw" },
+ { U"Corsican", U"co" },
+ { U"Cree", U"cr" },
+ { U"Croatian", U"hr" },
+ { U"Czech", U"cs" },
+ { U"Danish", U"da" },
+ { U"Divehi", U"dv" },
+ { U"Dutch", U"nl" },
+ { U"Dzongkha", U"dz" },
+ { U"English", U"en" },
+ { U"Esperanto", U"eo" },
+ { U"Estonian", U"et" },
+ { U"Ewe", U"ee" },
+ { U"Faroese", U"fo" },
+ { U"Fijian", U"fj" },
+ { U"Finnish", U"fi" },
+ { U"French", U"fr" },
+ { U"Fulah", U"ff" },
+ { U"Galician", U"gl" },
+ { U"Georgian", U"ka" },
+ { U"German", U"de" },
+ { U"Greek", U"el" },
+ { U"Guarani", U"gn" },
+ { U"Gujarati", U"gu" },
+ { U"Haitian", U"ht" },
+ { U"Hausa", U"ha" },
+ { U"Hebrew", U"he" },
+ { U"Herero", U"hz" },
+ { U"Hindi", U"hi" },
+ { U"Hiri Motu", U"ho" },
+ { U"Hungarian", U"hu" },
+ { U"Interlingua", U"ia" },
+ { U"Indonesian", U"id" },
+ { U"Interlingue", U"ie" },
+ { U"Irish", U"ga" },
+ { U"Igbo", U"ig" },
+ { U"Inupiaq", U"ik" },
+ { U"Ido", U"io" },
+ { U"Icelandic", U"is" },
+ { U"Italian", U"it" },
+ { U"Inuktitut", U"iu" },
+ { U"Japanese", U"ja" },
+ { U"Javanese", U"jv" },
+ { U"Kalaallisut", U"kl" },
+ { U"Kannada", U"kn" },
+ { U"Kanuri", U"kr" },
+ { U"Kashmiri", U"ks" },
+ { U"Kazakh", U"kk" },
+ { U"Central Khmer", U"km" },
+ { U"Kikuyu", U"ki" },
+ { U"Kinyarwanda", U"rw" },
+ { U"Kirghiz", U"ky" },
+ { U"Komi", U"kv" },
+ { U"Kongo", U"kg" },
+ { U"Korean", U"ko" },
+ { U"Kurdish", U"ku" },
+ { U"Kuanyama", U"kj" },
+ { U"Latin", U"la" },
+ { U"Luxembourgish", U"lb" },
+ { U"Ganda", U"lg" },
+ { U"Limburgan", U"li" },
+ { U"Lingala", U"ln" },
+ { U"Lao", U"lo" },
+ { U"Lithuanian", U"lt" },
+ { U"Luba-Katanga", U"lu" },
+ { U"Latvian", U"lv" },
+ { U"Man", U"gv" },
+ { U"Macedonian", U"mk" },
+ { U"Malagasy", U"mg" },
+ { U"Malay", U"ms" },
+ { U"Malayalam", U"ml" },
+ { U"Maltese", U"mt" },
+ { U"Maori", U"mi" },
+ { U"Marathi", U"mr" },
+ { U"Marshallese", U"mh" },
+ { U"Mongolian", U"mn" },
+ { U"Nauru", U"na" },
+ { U"Navajo", U"nv" },
+ { U"North Ndebele", U"nd" },
+ { U"Nepali", U"ne" },
+ { U"Ndonga", U"ng" },
+ { U"Norwegian Bokmål", U"nb" },
+ { U"Norwegian Nynorsk", U"nn" },
+ { U"Norwegian", U"no" },
+ { U"Sichuan Yi, Nuosu", U"ii" },
+ { U"South Ndebele", U"nr" },
+ { U"Occitan", U"oc" },
+ { U"Ojibwa", U"oj" },
+ { U"Church Slavic", U"cu" },
+ { U"Oromo", U"om" },
+ { U"Oriya", U"or" },
+ { U"Ossetian", U"os" },
+ { U"Punjabi", U"pa" },
+ { U"Pali", U"pi" },
+ { U"Persian", U"fa" },
+ { U"Polish", U"pl" },
+ { U"Pashto", U"ps" },
+ { U"Portuguese", U"pt" },
+ { U"Quechua", U"qu" },
+ { U"Romansh", U"rm" },
+ { U"Rundi", U"rn" },
+ { U"Romanian", U"ro" },
+ { U"Russian", U"ru" },
+ { U"Sanskrit", U"sa" },
+ { U"Sardinian", U"sc" },
+ { U"Sindhi", U"sd" },
+ { U"Northern Sami", U"se" },
+ { U"Samoan", U"sm" },
+ { U"Sango", U"sg" },
+ { U"Serbian", U"sr" },
+ { U"Gaelic", U"gd" },
+ { U"Shona", U"sn" },
+ { U"Sinhala", U"si" },
+ { U"Slovak", U"sk" },
+ { U"Slovenian", U"sl" },
+ { U"Somali", U"so" },
+ { U"Southern Sotho", U"st" },
+ { U"Spanish", U"es" },
+ { U"Sundanese", U"su" },
+ { U"Swahili", U"sw" },
+ { U"Swati", U"ss" },
+ { U"Swedish", U"sv" },
+ { U"Tamil", U"ta" },
+ { U"Telugu", U"te" },
+ { U"Tajik", U"tg" },
+ { U"Thai", U"th" },
+ { U"Tigrinya", U"ti" },
+ { U"Tibetan", U"bo" },
+ { U"Turkmen", U"tk" },
+ { U"Tagalog", U"tl" },
+ { U"Tswana", U"tn" },
+ { U"Tonga", U"to" },
+ { U"Turkish", U"tr" },
+ { U"Tsonga", U"ts" },
+ { U"Tatar", U"tt" },
+ { U"Twi", U"tw" },
+ { U"Tahitian", U"ty" },
+ { U"Uighur", U"ug" },
+ { U"Ukrainian", U"uk" },
+ { U"Urdu", U"ur" },
+ { U"Uzbek", U"uz" },
+ { U"Venda", U"ve" },
+ { U"Vietnamese", U"vi" },
+ { U"Volapük", U"vo" },
+ { U"Walloon", U"wa" },
+ { U"Welsh", U"cy" },
+ { U"Wolof", U"wo" },
+ { U"Western Frisian", U"fy" },
+ { U"Xhosa", U"xh" },
+ { U"Yiddish", U"yi" },
+ { U"Yoruba", U"yo" },
+ { U"Zhuang", U"za" },
+ { U"Zulu", U"zu" },
+ { String(), String() }
+};
+
+static CodeInfo scripts[] = {
+ { U"Custom", U"Qaaa" },
+ { U"-", U"-" },
+ { U"Adlam", U"Adlm" },
+ { U"Afaka", U"Afak" },
+ { U"Caucasian Albanian", U"Aghb" },
+ { U"Ahom", U"Ahom" },
+ { U"Arabic", U"Arab" },
+ { U"Imperial Aramaic", U"Armi" },
+ { U"Armenian", U"Armn" },
+ { U"Avestan", U"Avst" },
+ { U"Balinese", U"Bali" },
+ { U"Bamum", U"Bamu" },
+ { U"Bassa Vah", U"Bass" },
+ { U"Batak", U"Batk" },
+ { U"Bengali", U"Beng" },
+ { U"Bhaiksuki", U"Bhks" },
+ { U"Blissymbols", U"Blis" },
+ { U"Bopomofo", U"Bopo" },
+ { U"Brahmi", U"Brah" },
+ { U"Braille", U"Brai" },
+ { U"Buginese", U"Bugi" },
+ { U"Buhid", U"Buhd" },
+ { U"Chakma", U"Cakm" },
+ { U"Unified Canadian Aboriginal", U"Cans" },
+ { U"Carian", U"Cari" },
+ { U"Cham", U"Cham" },
+ { U"Cherokee", U"Cher" },
+ { U"Chorasmian", U"Chrs" },
+ { U"Cirth", U"Cirt" },
+ { U"Coptic", U"Copt" },
+ { U"Cypro-Minoan", U"Cpmn" },
+ { U"Cypriot", U"Cprt" },
+ { U"Cyrillic", U"Cyrl" },
+ { U"Devanagari", U"Deva" },
+ { U"Dives Akuru", U"Diak" },
+ { U"Dogra", U"Dogr" },
+ { U"Deseret", U"Dsrt" },
+ { U"Duployan", U"Dupl" },
+ { U"Egyptian demotic", U"Egyd" },
+ { U"Egyptian hieratic", U"Egyh" },
+ { U"Egyptian hieroglyphs", U"Egyp" },
+ { U"Elbasan", U"Elba" },
+ { U"Elymaic", U"Elym" },
+ { U"Ethiopic", U"Ethi" },
+ { U"Khutsuri", U"Geok" },
+ { U"Georgian", U"Geor" },
+ { U"Glagolitic", U"Glag" },
+ { U"Gunjala Gondi", U"Gong" },
+ { U"Masaram Gondi", U"Gonm" },
+ { U"Gothic", U"Goth" },
+ { U"Grantha", U"Gran" },
+ { U"Greek", U"Grek" },
+ { U"Gujarati", U"Gujr" },
+ { U"Gurmukhi", U"Guru" },
+ { U"Hangul", U"Hang" },
+ { U"Han", U"Hani" },
+ { U"Hanunoo", U"Hano" },
+ { U"Hatran", U"Hatr" },
+ { U"Hebrew", U"Hebr" },
+ { U"Hiragana", U"Hira" },
+ { U"Anatolian Hieroglyphs", U"Hluw" },
+ { U"Pahawh Hmong", U"Hmng" },
+ { U"Nyiakeng Puachue Hmong", U"Hmnp" },
+ { U"Old Hungarian", U"Hung" },
+ { U"Indus", U"Inds" },
+ { U"Old Italic", U"Ital" },
+ { U"Javanese", U"Java" },
+ { U"Jurchen", U"Jurc" },
+ { U"Kayah Li", U"Kali" },
+ { U"Katakana", U"Kana" },
+ { U"Kharoshthi", U"Khar" },
+ { U"Khmer", U"Khmr" },
+ { U"Khojki", U"Khoj" },
+ { U"Khitan large script", U"Kitl" },
+ { U"Khitan small script", U"Kits" },
+ { U"Kannada", U"Knda" },
+ { U"Kpelle", U"Kpel" },
+ { U"Kaithi", U"Kthi" },
+ { U"Tai Tham", U"Lana" },
+ { U"Lao", U"Laoo" },
+ { U"Latin", U"Latn" },
+ { U"Leke", U"Leke" },
+ { U"Lepcha", U"Lepc" },
+ { U"Limbu", U"Limb" },
+ { U"Linear A", U"Lina" },
+ { U"Linear B", U"Linb" },
+ { U"Lisu", U"Lisu" },
+ { U"Loma", U"Loma" },
+ { U"Lycian", U"Lyci" },
+ { U"Lydian", U"Lydi" },
+ { U"Mahajani", U"Mahj" },
+ { U"Makasar", U"Maka" },
+ { U"Mandaic", U"Mand" },
+ { U"Manichaean", U"Mani" },
+ { U"Marchen", U"Marc" },
+ { U"Mayan Hieroglyphs", U"Maya" },
+ { U"Medefaidrin", U"Medf" },
+ { U"Mende Kikakui", U"Mend" },
+ { U"Meroitic Cursive", U"Merc" },
+ { U"Meroitic Hieroglyphs", U"Mero" },
+ { U"Malayalam", U"Mlym" },
+ { U"Modi", U"Modi" },
+ { U"Mongolian", U"Mong" },
+ { U"Moon", U"Moon" },
+ { U"Mro", U"Mroo" },
+ { U"Meitei Mayek", U"Mtei" },
+ { U"Multani", U"Mult" },
+ { U"Myanmar (Burmese)", U"Mymr" },
+ { U"Nandinagari", U"Nand" },
+ { U"Old North Arabian", U"Narb" },
+ { U"Nabataean", U"Nbat" },
+ { U"Newa", U"Newa" },
+ { U"Naxi Dongba", U"Nkdb" },
+ { U"Nakhi Geba", U"Nkgb" },
+ { U"N’Ko", U"Nkoo" },
+ { U"Nüshu", U"Nshu" },
+ { U"Ogham", U"Ogam" },
+ { U"Ol Chiki", U"Olck" },
+ { U"Old Turkic", U"Orkh" },
+ { U"Oriya", U"Orya" },
+ { U"Osage", U"Osge" },
+ { U"Osmanya", U"Osma" },
+ { U"Old Uyghur", U"Ougr" },
+ { U"Palmyrene", U"Palm" },
+ { U"Pau Cin Hau", U"Pauc" },
+ { U"Proto-Cuneiform", U"Pcun" },
+ { U"Proto-Elamite", U"Pelm" },
+ { U"Old Permic", U"Perm" },
+ { U"Phags-pa", U"Phag" },
+ { U"Inscriptional Pahlavi", U"Phli" },
+ { U"Psalter Pahlavi", U"Phlp" },
+ { U"Book Pahlavi", U"Phlv" },
+ { U"Phoenician", U"Phnx" },
+ { U"Klingon", U"Piqd" },
+ { U"Miao", U"Plrd" },
+ { U"Inscriptional Parthian", U"Prti" },
+ { U"Proto-Sinaitic", U"Psin" },
+ { U"Ranjana", U"Ranj" },
+ { U"Rejang", U"Rjng" },
+ { U"Hanifi Rohingya", U"Rohg" },
+ { U"Rongorongo", U"Roro" },
+ { U"Runic", U"Runr" },
+ { U"Samaritan", U"Samr" },
+ { U"Sarati", U"Sara" },
+ { U"Old South Arabian", U"Sarb" },
+ { U"Saurashtra", U"Saur" },
+ { U"SignWriting", U"Sgnw" },
+ { U"Shavian", U"Shaw" },
+ { U"Sharada", U"Shrd" },
+ { U"Shuishu", U"Shui" },
+ { U"Siddham", U"Sidd" },
+ { U"Khudawadi", U"Sind" },
+ { U"Sinhala", U"Sinh" },
+ { U"Sogdian", U"Sogd" },
+ { U"Old Sogdian", U"Sogo" },
+ { U"Sora Sompeng", U"Sora" },
+ { U"Soyombo", U"Soyo" },
+ { U"Sundanese", U"Sund" },
+ { U"Syloti Nagri", U"Sylo" },
+ { U"Syriac", U"Syrc" },
+ { U"Tagbanwa", U"Tagb" },
+ { U"Takri", U"Takr" },
+ { U"Tai Le", U"Tale" },
+ { U"New Tai Lue", U"Talu" },
+ { U"Tamil", U"Taml" },
+ { U"Tangut", U"Tang" },
+ { U"Tai Viet", U"Tavt" },
+ { U"Telugu", U"Telu" },
+ { U"Tengwar", U"Teng" },
+ { U"Tifinagh", U"Tfng" },
+ { U"Tagalog", U"Tglg" },
+ { U"Thaana", U"Thaa" },
+ { U"Thai", U"Thai" },
+ { U"Tibetan", U"Tibt" },
+ { U"Tirhuta", U"Tirh" },
+ { U"Tangsa", U"Tnsa" },
+ { U"Toto", U"Toto" },
+ { U"Ugaritic", U"Ugar" },
+ { U"Vai", U"Vaii" },
+ { U"Visible Speech", U"Visp" },
+ { U"Vithkuqi", U"Vith" },
+ { U"Warang Citi", U"Wara" },
+ { U"Wancho", U"Wcho" },
+ { U"Woleai", U"Wole" },
+ { U"Old Persian", U"Xpeo" },
+ { U"Cuneiform", U"Xsux" },
+ { U"Yezidi", U"Yezi" },
+ { U"Yi", U"Yiii" },
+ { U"Zanabazar Square", U"Zanb" },
+ { String(), String() }
+};
+
+/*************************************************************************/
+/* Page 1 callbacks: Rendering Options */
+/*************************************************************************/
+
+void DynamicFontImportSettings::_main_prop_changed(const String &p_edited_property) {
+ // Update font preview.
+
+ if (p_edited_property == "antialiased") {
+ if (font_preview->get_data_count() > 0) {
+ font_preview->get_data(0)->set_antialiased(import_settings_data->get("antialiased"));
+ }
+ } else if (p_edited_property == "multichannel_signed_distance_field") {
+ if (font_preview->get_data_count() > 0) {
+ font_preview->get_data(0)->set_multichannel_signed_distance_field(import_settings_data->get("multichannel_signed_distance_field"));
+ }
+ _variation_selected();
+ _variations_validate();
+ } else if (p_edited_property == "msdf_pixel_range") {
+ if (font_preview->get_data_count() > 0) {
+ font_preview->get_data(0)->set_msdf_pixel_range(import_settings_data->get("msdf_pixel_range"));
+ }
+ } else if (p_edited_property == "msdf_size") {
+ if (font_preview->get_data_count() > 0) {
+ font_preview->get_data(0)->set_msdf_size(import_settings_data->get("msdf_size"));
+ }
+ } else if (p_edited_property == "force_autohinter") {
+ if (font_preview->get_data_count() > 0) {
+ font_preview->get_data(0)->set_force_autohinter(import_settings_data->get("force_autohinter"));
+ }
+ } else if (p_edited_property == "hinting") {
+ if (font_preview->get_data_count() > 0) {
+ font_preview->get_data(0)->set_hinting((TextServer::Hinting)import_settings_data->get("hinting").operator int());
+ }
+ } else if (p_edited_property == "oversampling") {
+ if (font_preview->get_data_count() > 0) {
+ font_preview->get_data(0)->set_oversampling(import_settings_data->get("oversampling"));
+ }
+ }
+ font_preview_label->add_theme_font_override("font", font_preview);
+ font_preview_label->update();
+}
+
+/*************************************************************************/
+/* Page 2 callbacks: Configurations */
+/*************************************************************************/
+
+void DynamicFontImportSettings::_variation_add() {
+ TreeItem *vars_item = vars_list->create_item(vars_list_root);
+ ERR_FAIL_NULL(vars_item);
+
+ vars_item->set_text(0, TTR("New configuration"));
+ vars_item->set_editable(0, true);
+ vars_item->add_button(1, vars_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove Variation"));
+ vars_item->set_button_color(1, 0, Color(1, 1, 1, 0.75));
+
+ Ref<DynamicFontImportSettingsData> import_variation_data;
+ import_variation_data.instantiate();
+ import_variation_data->owner = this;
+ ERR_FAIL_NULL(import_variation_data);
+
+ for (List<ResourceImporter::ImportOption>::Element *E = options_variations.front(); E; E = E->next()) {
+ import_variation_data->defaults[E->get().option.name] = E->get().default_value;
+ }
+
+ import_variation_data->options = options_variations;
+ inspector_vars->edit(import_variation_data.ptr());
+ import_variation_data->notify_property_list_changed();
+
+ vars_item->set_metadata(0, import_variation_data);
+
+ _variations_validate();
+}
+
+void DynamicFontImportSettings::_variation_selected() {
+ TreeItem *vars_item = vars_list->get_selected();
+ if (vars_item) {
+ Ref<DynamicFontImportSettingsData> import_variation_data = vars_item->get_metadata(0);
+ ERR_FAIL_NULL(import_variation_data);
+
+ inspector_vars->edit(import_variation_data.ptr());
+ import_variation_data->notify_property_list_changed();
+ }
+}
+
+void DynamicFontImportSettings::_variation_remove(Object *p_item, int p_column, int p_id) {
+ TreeItem *vars_item = (TreeItem *)p_item;
+ ERR_FAIL_NULL(vars_item);
+
+ inspector_vars->edit(nullptr);
+
+ vars_list_root->remove_child(vars_item);
+ memdelete(vars_item);
+
+ if (vars_list_root->get_first_child()) {
+ Ref<DynamicFontImportSettingsData> import_variation_data = vars_list_root->get_first_child()->get_metadata(0);
+ inspector_vars->edit(import_variation_data.ptr());
+ import_variation_data->notify_property_list_changed();
+ }
+
+ _variations_validate();
+}
+
+void DynamicFontImportSettings::_variation_changed(const String &p_edited_property) {
+ _variations_validate();
+}
+
+void DynamicFontImportSettings::_variations_validate() {
+ String warn;
+ if (!vars_list_root->get_first_child()) {
+ warn = TTR("Warinig: There are no configurations specified, no glyphs will be pre-rendered.");
+ }
+ for (TreeItem *vars_item_a = vars_list_root->get_first_child(); vars_item_a; vars_item_a = vars_item_a->get_next()) {
+ Ref<DynamicFontImportSettingsData> import_variation_data_a = vars_item_a->get_metadata(0);
+ ERR_FAIL_NULL(import_variation_data_a);
+
+ for (TreeItem *vars_item_b = vars_list_root->get_first_child(); vars_item_b; vars_item_b = vars_item_b->get_next()) {
+ if (vars_item_b != vars_item_a) {
+ bool match = true;
+ for (Map<StringName, Variant>::Element *E = import_variation_data_a->settings.front(); E; E = E->next()) {
+ Ref<DynamicFontImportSettingsData> import_variation_data_b = vars_item_b->get_metadata(0);
+ ERR_FAIL_NULL(import_variation_data_b);
+ match = match && (import_variation_data_b->settings[E->key()] == E->get());
+ }
+ if (match) {
+ warn = TTR("Warinig: Multiple configurations have identical settings. Duplicates will be ignored.");
+ break;
+ }
+ }
+ }
+ }
+ if (warn.is_empty()) {
+ label_warn->set_text("");
+ label_warn->hide();
+ } else {
+ label_warn->set_text(warn);
+ label_warn->show();
+ }
+}
+
+/*************************************************************************/
+/* Page 3 callbacks: Text to select glyphs */
+/*************************************************************************/
+
+void DynamicFontImportSettings::_change_text_opts() {
+ Vector<String> ftr = ftr_edit->get_text().split(",");
+ for (int i = 0; i < ftr.size(); i++) {
+ Vector<String> tokens = ftr[i].split("=");
+ if (tokens.size() == 2) {
+ text_edit->set_opentype_feature(tokens[0], tokens[1].to_int());
+ } else if (tokens.size() == 1) {
+ text_edit->set_opentype_feature(tokens[0], 1);
+ }
+ }
+ text_edit->set_language(lang_edit->get_text());
+}
+
+void DynamicFontImportSettings::_glyph_clear() {
+ selected_glyphs.clear();
+ label_glyphs->set_text(TTR("Preloaded glyphs: ") + itos(selected_glyphs.size()));
+ _range_selected();
+}
+
+void DynamicFontImportSettings::_glyph_text_selected() {
+ Dictionary ftrs;
+ Vector<String> ftr = ftr_edit->get_text().split(",");
+ for (int i = 0; i < ftr.size(); i++) {
+ Vector<String> tokens = ftr[i].split("=");
+ if (tokens.size() == 2) {
+ ftrs[tokens[0]] = tokens[1].to_int();
+ } else if (tokens.size() == 1) {
+ ftrs[tokens[0]] = 1;
+ }
+ }
+
+ RID text_rid = TS->create_shaped_text();
+ if (text_rid.is_valid()) {
+ TS->shaped_text_add_string(text_rid, text_edit->get_text(), font_main->get_rids(), 16, ftrs, text_edit->get_language());
+ TS->shaped_text_shape(text_rid);
+ const Vector<TextServer::Glyph> &gl = TS->shaped_text_get_glyphs(text_rid);
+
+ for (int i = 0; i < gl.size(); i++) {
+ if (gl[i].font_rid.is_valid() && gl[i].index != 0) {
+ selected_glyphs.insert(gl[i].index);
+ }
+ }
+ TS->free(text_rid);
+ label_glyphs->set_text(TTR("Preloaded glyphs: ") + itos(selected_glyphs.size()));
+ }
+ _range_selected();
+}
+
+/*************************************************************************/
+/* Page 4 callbacks: Character map */
+/*************************************************************************/
+
+void DynamicFontImportSettings::_glyph_selected() {
+ TreeItem *item = glyph_table->get_selected();
+ ERR_FAIL_NULL(item);
+
+ Color scol = glyph_table->get_theme_color("box_selection_fill_color", "Editor");
+ Color fcol = glyph_table->get_theme_color("font_selected_color", "Editor");
+ scol.a = 1.f;
+
+ int32_t c = item->get_metadata(glyph_table->get_selected_column());
+ if (font_main->has_char(c)) {
+ if (_char_update(c)) {
+ item->set_custom_color(glyph_table->get_selected_column(), fcol);
+ item->set_custom_bg_color(glyph_table->get_selected_column(), scol);
+ } else {
+ item->clear_custom_color(glyph_table->get_selected_column());
+ item->clear_custom_bg_color(glyph_table->get_selected_column());
+ }
+ }
+ label_glyphs->set_text(TTR("Preloaded glyphs: ") + itos(selected_glyphs.size()));
+}
+
+void DynamicFontImportSettings::_range_edited() {
+ TreeItem *item = glyph_tree->get_selected();
+ ERR_FAIL_NULL(item);
+ Vector2i range = item->get_metadata(0);
+ _range_update(range.x, range.y);
+}
+
+void DynamicFontImportSettings::_range_selected() {
+ TreeItem *item = glyph_tree->get_selected();
+ if (item) {
+ Vector2i range = item->get_metadata(0);
+ _edit_range(range.x, range.y);
+ }
+}
+
+void DynamicFontImportSettings::_edit_range(int32_t p_start, int32_t p_end) {
+ glyph_table->clear();
+
+ TreeItem *root = glyph_table->create_item();
+ ERR_FAIL_NULL(root);
+
+ Color scol = glyph_table->get_theme_color("box_selection_fill_color", "Editor");
+ Color fcol = glyph_table->get_theme_color("font_selected_color", "Editor");
+ scol.a = 1.f;
+
+ TreeItem *item = nullptr;
+ int col = 0;
+
+ for (int32_t c = p_start; c <= p_end; c++) {
+ if (col == 0) {
+ item = glyph_table->create_item(root);
+ ERR_FAIL_NULL(item);
+ item->set_text(0, _pad_zeros(String::num_int64(c, 16)));
+ item->set_text_align(0, TreeItem::ALIGN_LEFT);
+ item->set_selectable(0, false);
+ item->set_custom_bg_color(0, glyph_table->get_theme_color("dark_color_3", "Editor"));
+ }
+ if (font_main->has_char(c)) {
+ item->set_text(col + 1, String::chr(c));
+ item->set_custom_color(col + 1, Color(1, 1, 1));
+ if (selected_chars.has(c) || (font_main->get_data(0).is_valid() && selected_glyphs.has(font_main->get_data(0)->get_glyph_index(get_theme_font_size("font_size") * 2, c)))) {
+ item->set_custom_color(col + 1, fcol);
+ item->set_custom_bg_color(col + 1, scol);
+ } else {
+ item->clear_custom_color(col + 1);
+ item->clear_custom_bg_color(col + 1);
+ }
+ } else {
+ item->set_custom_bg_color(col + 1, glyph_table->get_theme_color("dark_color_2", "Editor"));
+ }
+ item->set_metadata(col + 1, c);
+ item->set_text_align(col + 1, TreeItem::ALIGN_CENTER);
+ item->set_selectable(col + 1, true);
+ item->set_custom_font(col + 1, font_main);
+ item->set_custom_font_size(col + 1, get_theme_font_size("font_size") * 2);
+
+ col++;
+ if (col == 16) {
+ col = 0;
+ }
+ }
+ label_glyphs->set_text(TTR("Preloaded glyphs: ") + itos(selected_glyphs.size()));
+}
+
+bool DynamicFontImportSettings::_char_update(int32_t p_char) {
+ if (selected_chars.has(p_char)) {
+ selected_chars.erase(p_char);
+ return false;
+ } else if (font_main->get_data(0).is_valid() && selected_glyphs.has(font_main->get_data(0)->get_glyph_index(get_theme_font_size("font_size") * 2, p_char))) {
+ selected_glyphs.erase(font_main->get_data(0)->get_glyph_index(get_theme_font_size("font_size") * 2, p_char));
+ return false;
+ } else {
+ selected_chars.insert(p_char);
+ return true;
+ }
+ label_glyphs->set_text(TTR("Preloaded glyphs: ") + itos(selected_glyphs.size()));
+}
+
+void DynamicFontImportSettings::_range_update(int32_t p_start, int32_t p_end) {
+ bool all_selected = true;
+ for (int32_t i = p_start; i <= p_end; i++) {
+ if (font_main->has_char(i)) {
+ if (font_main->get_data(0).is_valid()) {
+ all_selected = all_selected && (selected_chars.has(i) || (font_main->get_data(0).is_valid() && selected_glyphs.has(font_main->get_data(0)->get_glyph_index(get_theme_font_size("font_size") * 2, i))));
+ } else {
+ all_selected = all_selected && selected_chars.has(i);
+ }
+ }
+ }
+ for (int32_t i = p_start; i <= p_end; i++) {
+ if (font_main->has_char(i)) {
+ if (!all_selected) {
+ selected_chars.insert(i);
+ } else {
+ selected_chars.erase(i);
+ if (font_main->get_data(0).is_valid()) {
+ selected_glyphs.erase(font_main->get_data(0)->get_glyph_index(get_theme_font_size("font_size") * 2, i));
+ }
+ }
+ }
+ }
+ _edit_range(p_start, p_end);
+}
+
+/*************************************************************************/
+/* Page 5 callbacks: CMetadata override */
+/*************************************************************************/
+
+void DynamicFontImportSettings::_lang_add() {
+ menu_langs->set_position(lang_list->get_screen_transform().xform(lang_list->get_local_mouse_position()));
+ menu_langs->set_size(Vector2(1, 1));
+ menu_langs->popup();
+}
+
+void DynamicFontImportSettings::_lang_add_item(int p_option) {
+ TreeItem *lang_item = lang_list->create_item(lang_list_root);
+ ERR_FAIL_NULL(lang_item);
+
+ lang_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ lang_item->set_editable(0, true);
+ lang_item->set_checked(0, false);
+ lang_item->set_text(1, langs[p_option].code);
+ lang_item->set_editable(1, true);
+ lang_item->add_button(2, lang_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
+ lang_item->set_button_color(2, 0, Color(1, 1, 1, 0.75));
+}
+
+void DynamicFontImportSettings::_lang_remove(Object *p_item, int p_column, int p_id) {
+ TreeItem *lang_item = (TreeItem *)p_item;
+ ERR_FAIL_NULL(lang_item);
+
+ lang_list_root->remove_child(lang_item);
+ memdelete(lang_item);
+}
+
+void DynamicFontImportSettings::_script_add() {
+ menu_scripts->set_position(script_list->get_screen_transform().xform(script_list->get_local_mouse_position()));
+ menu_scripts->set_size(Vector2(1, 1));
+ menu_scripts->popup();
+}
+
+void DynamicFontImportSettings::_script_add_item(int p_option) {
+ TreeItem *script_item = script_list->create_item(script_list_root);
+ ERR_FAIL_NULL(script_item);
+
+ script_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ script_item->set_editable(0, true);
+ script_item->set_checked(0, false);
+ script_item->set_text(1, scripts[p_option].code);
+ script_item->set_editable(1, true);
+ script_item->add_button(2, lang_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
+ script_item->set_button_color(2, 0, Color(1, 1, 1, 0.75));
+}
+
+void DynamicFontImportSettings::_script_remove(Object *p_item, int p_column, int p_id) {
+ TreeItem *script_item = (TreeItem *)p_item;
+ ERR_FAIL_NULL(script_item);
+
+ script_list_root->remove_child(script_item);
+ memdelete(script_item);
+}
+
+/*************************************************************************/
+/* Common */
+/*************************************************************************/
+
+DynamicFontImportSettings *DynamicFontImportSettings::singleton = nullptr;
+
+String DynamicFontImportSettings::_pad_zeros(const String &p_hex) const {
+ int len = CLAMP(5 - p_hex.length(), 0, 5);
+ return String("0").repeat(len) + p_hex;
+}
+
+void DynamicFontImportSettings::_notification(int p_what) {
+ if (p_what == NOTIFICATION_READY) {
+ connect("confirmed", callable_mp(this, &DynamicFontImportSettings::_re_import));
+ } else if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ add_lang->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
+ add_script->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
+ add_var->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
+ }
+}
+
+void DynamicFontImportSettings::_re_import() {
+ Map<StringName, Variant> main_settings;
+
+ main_settings["antialiased"] = import_settings_data->get("antialiased");
+ main_settings["multichannel_signed_distance_field"] = import_settings_data->get("multichannel_signed_distance_field");
+ main_settings["msdf_pixel_range"] = import_settings_data->get("msdf_pixel_range");
+ main_settings["msdf_size"] = import_settings_data->get("msdf_size");
+ main_settings["force_autohinter"] = import_settings_data->get("force_autohinter");
+ main_settings["hinting"] = import_settings_data->get("hinting");
+ main_settings["oversampling"] = import_settings_data->get("oversampling");
+ main_settings["compress"] = import_settings_data->get("compress");
+
+ Vector<String> variations;
+ for (TreeItem *vars_item = vars_list_root->get_first_child(); vars_item; vars_item = vars_item->get_next()) {
+ String variation;
+ Ref<DynamicFontImportSettingsData> import_variation_data = vars_item->get_metadata(0);
+ ERR_FAIL_NULL(import_variation_data);
+
+ String name = vars_item->get_text(0);
+ variation += ("name=" + name);
+ for (Map<StringName, Variant>::Element *E = import_variation_data->settings.front(); E; E = E->next()) {
+ if (!variation.is_empty()) {
+ variation += ",";
+ }
+ variation += (String(E->key()) + "=" + String(E->get()));
+ }
+ variations.push_back(variation);
+ }
+ main_settings["preload/configurations"] = variations;
+
+ Vector<String> langs_enabled;
+ Vector<String> langs_disabled;
+ for (TreeItem *lang_item = lang_list_root->get_first_child(); lang_item; lang_item = lang_item->get_next()) {
+ bool selected = lang_item->is_checked(0);
+ String name = lang_item->get_text(1);
+ if (selected) {
+ langs_enabled.push_back(name);
+ } else {
+ langs_disabled.push_back(name);
+ }
+ }
+ main_settings["support_overrides/language_enabled"] = langs_enabled;
+ main_settings["support_overrides/language_disabled"] = langs_disabled;
+
+ Vector<String> scripts_enabled;
+ Vector<String> scripts_disabled;
+ for (TreeItem *script_item = script_list_root->get_first_child(); script_item; script_item = script_item->get_next()) {
+ bool selected = script_item->is_checked(0);
+ String name = script_item->get_text(1);
+ if (selected) {
+ scripts_enabled.push_back(name);
+ } else {
+ scripts_disabled.push_back(name);
+ }
+ }
+ main_settings["support_overrides/script_enabled"] = scripts_enabled;
+ main_settings["support_overrides/script_disabled"] = scripts_disabled;
+
+ if (!selected_chars.is_empty()) {
+ Vector<String> ranges;
+ char32_t start = selected_chars.front()->get();
+ for (Set<char32_t>::Element *E = selected_chars.front()->next(); E; E = E->next()) {
+ if (E->prev() && ((E->prev()->get() + 1) != E->get())) {
+ ranges.push_back(String("0x") + String::num_int64(start, 16) + String("-0x") + String::num_int64(E->prev()->get(), 16));
+ start = E->get();
+ }
+ }
+ ranges.push_back(String("0x") + String::num_int64(start, 16) + String("-0x") + String::num_int64(selected_chars.back()->get(), 16));
+ main_settings["preload/char_ranges"] = ranges;
+ }
+
+ if (!selected_glyphs.is_empty()) {
+ Vector<String> ranges;
+ int32_t start = selected_glyphs.front()->get();
+ for (Set<int32_t>::Element *E = selected_glyphs.front()->next(); E; E = E->next()) {
+ if (E->prev() && ((E->prev()->get() + 1) != E->get())) {
+ ranges.push_back(String("0x") + String::num_int64(start, 16) + String("-0x") + String::num_int64(E->prev()->get(), 16));
+ start = E->get();
+ }
+ }
+ ranges.push_back(String("0x") + String::num_int64(start, 16) + String("-0x") + String::num_int64(selected_glyphs.back()->get(), 16));
+ main_settings["preload/glyph_ranges"] = ranges;
+ }
+
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ print_line("Import settings:");
+ for (Map<StringName, Variant>::Element *E = main_settings.front(); E; E = E->next()) {
+ print_line(String(" ") + String(E->key()).utf8().get_data() + " == " + String(E->get()).utf8().get_data());
+ }
+ }
+
+ EditorFileSystem::get_singleton()->reimport_file_with_custom_parameters(base_path, "font_data_dynamic", main_settings);
+}
+
+void DynamicFontImportSettings::open_settings(const String &p_path) {
+ // Load base font data.
+ Vector<uint8_t> data = FileAccess::get_file_as_array(p_path);
+
+ // Load font for preview.
+ Ref<FontData> dfont_prev;
+ dfont_prev.instantiate();
+ dfont_prev->set_data(data);
+
+ font_preview.instantiate();
+ font_preview->add_data(dfont_prev);
+
+ String sample;
+ static const String sample_base = U"12漢字ԱբΑαАбΑαאבابܐܒހށआআਆઆଆஆఆಆആආกิກິༀကႠა한글ሀᎣᐁᚁᚠᜀᜠᝀᝠកᠠᤁᥐAb😀";
+ for (int i = 0; i < sample_base.length(); i++) {
+ if (dfont_prev->has_char(sample_base[i])) {
+ sample += sample_base[i];
+ }
+ }
+ if (sample.is_empty()) {
+ sample = dfont_prev->get_supported_chars().substr(0, 6);
+ }
+ font_preview_label->set_text(sample);
+
+ // Load second copy of font with MSDF disabled for the glyph table and metadata extraction.
+ Ref<FontData> dfont_main;
+ dfont_main.instantiate();
+ dfont_main->set_data(data);
+ dfont_main->set_multichannel_signed_distance_field(false);
+
+ font_main.instantiate();
+ font_main->add_data(dfont_main);
+ text_edit->add_theme_font_override("font", font_main);
+
+ base_path = p_path;
+
+ inspector_vars->edit(nullptr);
+ inspector_general->edit(nullptr);
+
+ int gww = get_theme_font("font")->get_string_size("00000", get_theme_font_size("font_size")).x + 50;
+ glyph_table->set_column_custom_minimum_width(0, gww);
+
+ glyph_table->clear();
+ vars_list->clear();
+ lang_list->clear();
+ script_list->clear();
+
+ selected_chars.clear();
+ selected_glyphs.clear();
+ text_edit->set_text(String());
+
+ vars_list_root = vars_list->create_item();
+ lang_list_root = lang_list->create_item();
+ script_list_root = script_list->create_item();
+
+ options_variations.clear();
+ Dictionary var_list = dfont_main->get_supported_variation_list();
+ for (int i = 0; i < var_list.size(); i++) {
+ int32_t tag = var_list.get_key_at_index(i);
+ Vector3i value = var_list.get_value_at_index(i);
+ options_variations.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::FLOAT, TS->tag_to_name(tag), PROPERTY_HINT_RANGE, itos(value.x) + "," + itos(value.y) + ",1"), value.z));
+ }
+ options_variations.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "size", PROPERTY_HINT_RANGE, "0,127,1"), 16));
+ options_variations.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "outline_size", PROPERTY_HINT_RANGE, "0,127,1"), 0));
+ options_variations.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "extra_spacing_glyph"), 0));
+ options_variations.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "extra_spacing_space"), 0));
+
+ import_settings_data->defaults.clear();
+ for (List<ResourceImporter::ImportOption>::Element *E = options_general.front(); E; E = E->next()) {
+ import_settings_data->defaults[E->get().option.name] = E->get().default_value;
+ }
+
+ Ref<ConfigFile> config;
+ config.instantiate();
+ ERR_FAIL_NULL(config);
+
+ Error err = config->load(p_path + ".import");
+ print_verbose("Loading import settings:");
+ if (err == OK) {
+ List<String> keys;
+ config->get_section_keys("params", &keys);
+ for (List<String>::Element *E = keys.front(); E; E = E->next()) {
+ String key = E->get();
+ print_verbose(String(" ") + key + " == " + String(config->get_value("params", key)));
+ if (key == "preload/char_ranges") {
+ Vector<String> ranges = config->get_value("params", key);
+ for (int i = 0; i < ranges.size(); i++) {
+ int32_t start, end;
+ Vector<String> tokens = ranges[i].split("-");
+ if (tokens.size() == 2) {
+ if (!ResourceImporterDynamicFont::_decode_range(tokens[0], start) || !ResourceImporterDynamicFont::_decode_range(tokens[1], end)) {
+ WARN_PRINT("Invalid range: \"" + ranges[i] + "\"");
+ continue;
+ }
+ } else if (tokens.size() == 1) {
+ if (!ResourceImporterDynamicFont::_decode_range(tokens[0], start)) {
+ WARN_PRINT("Invalid range: \"" + ranges[i] + "\"");
+ continue;
+ }
+ end = start;
+ } else {
+ WARN_PRINT("Invalid range: \"" + ranges[i] + "\"");
+ continue;
+ }
+ for (int32_t j = start; j <= end; j++) {
+ selected_chars.insert(j);
+ }
+ }
+ } else if (key == "preload/glyph_ranges") {
+ Vector<String> ranges = config->get_value("params", key);
+ for (int i = 0; i < ranges.size(); i++) {
+ int32_t start, end;
+ Vector<String> tokens = ranges[i].split("-");
+ if (tokens.size() == 2) {
+ if (!ResourceImporterDynamicFont::_decode_range(tokens[0], start) || !ResourceImporterDynamicFont::_decode_range(tokens[1], end)) {
+ WARN_PRINT("Invalid range: \"" + ranges[i] + "\"");
+ continue;
+ }
+ } else if (tokens.size() == 1) {
+ if (!ResourceImporterDynamicFont::_decode_range(tokens[0], start)) {
+ WARN_PRINT("Invalid range: \"" + ranges[i] + "\"");
+ continue;
+ }
+ end = start;
+ } else {
+ WARN_PRINT("Invalid range: \"" + ranges[i] + "\"");
+ continue;
+ }
+ for (int32_t j = start; j <= end; j++) {
+ selected_glyphs.insert(j);
+ }
+ }
+ } else if (key == "preload/configurations") {
+ Vector<String> variations = config->get_value("params", key);
+ for (int i = 0; i < variations.size(); i++) {
+ TreeItem *vars_item = vars_list->create_item(vars_list_root);
+ ERR_FAIL_NULL(vars_item);
+
+ vars_item->set_text(0, TTR("Configuration") + " " + itos(i));
+ vars_item->set_editable(0, true);
+ vars_item->add_button(1, vars_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove Variation"));
+ vars_item->set_button_color(1, 0, Color(1, 1, 1, 0.75));
+
+ Ref<DynamicFontImportSettingsData> import_variation_data_custom;
+ import_variation_data_custom.instantiate();
+ import_variation_data_custom->owner = this;
+ ERR_FAIL_NULL(import_variation_data_custom);
+
+ for (List<ResourceImporter::ImportOption>::Element *F = options_variations.front(); F; F = F->next()) {
+ import_variation_data_custom->defaults[F->get().option.name] = F->get().default_value;
+ }
+
+ import_variation_data_custom->options = options_variations;
+
+ vars_item->set_metadata(0, import_variation_data_custom);
+ Vector<String> variation_tags = variations[i].split(",");
+ for (int j = 0; j < variation_tags.size(); j++) {
+ Vector<String> tokens = variation_tags[j].split("=");
+ if (tokens[0] == "name") {
+ vars_item->set_text(0, tokens[1]);
+ } else if (tokens[0] == "size" || tokens[0] == "outline_size" || tokens[0] == "extra_spacing_space" || tokens[0] == "extra_spacing_glyph") {
+ import_variation_data_custom->set(tokens[0], tokens[1].to_int());
+ } else {
+ import_variation_data_custom->set(tokens[0], tokens[1].to_float());
+ }
+ }
+ }
+ } else if (key == "support_overrides/language_enabled") {
+ PackedStringArray _langs = config->get_value("params", key);
+ for (int i = 0; i < _langs.size(); i++) {
+ TreeItem *lang_item = lang_list->create_item(lang_list_root);
+ ERR_FAIL_NULL(lang_item);
+
+ lang_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ lang_item->set_editable(0, true);
+ lang_item->set_checked(0, true);
+ lang_item->set_text(1, _langs[i]);
+ lang_item->set_editable(1, true);
+ lang_item->add_button(2, lang_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
+ }
+ } else if (key == "support_overrides/language_disabled") {
+ PackedStringArray _langs = config->get_value("params", key);
+ for (int i = 0; i < _langs.size(); i++) {
+ TreeItem *lang_item = lang_list->create_item(lang_list_root);
+ ERR_FAIL_NULL(lang_item);
+
+ lang_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ lang_item->set_editable(0, true);
+ lang_item->set_checked(0, false);
+ lang_item->set_text(1, _langs[i]);
+ lang_item->set_editable(1, true);
+ lang_item->add_button(2, lang_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
+ }
+ } else if (key == "support_overrides/script_enabled") {
+ PackedStringArray _scripts = config->get_value("params", key);
+ for (int i = 0; i < _scripts.size(); i++) {
+ TreeItem *script_item = script_list->create_item(script_list_root);
+ ERR_FAIL_NULL(script_item);
+
+ script_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ script_item->set_editable(0, true);
+ script_item->set_checked(0, true);
+ script_item->set_text(1, _scripts[i]);
+ script_item->set_editable(1, true);
+ script_item->add_button(2, lang_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
+ }
+ } else if (key == "support_overrides/script_disabled") {
+ PackedStringArray _scripts = config->get_value("params", key);
+ for (int i = 0; i < _scripts.size(); i++) {
+ TreeItem *script_item = script_list->create_item(script_list_root);
+ ERR_FAIL_NULL(script_item);
+
+ script_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ script_item->set_editable(0, true);
+ script_item->set_checked(0, false);
+ script_item->set_text(1, _scripts[i]);
+ script_item->set_editable(1, true);
+ script_item->add_button(2, lang_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
+ }
+ } else {
+ Variant value = config->get_value("params", key);
+ import_settings_data->defaults[key] = value;
+ }
+ }
+ }
+ label_glyphs->set_text(TTR("Preloaded glyphs: ") + itos(selected_glyphs.size()));
+
+ import_settings_data->options = options_general;
+ inspector_general->edit(import_settings_data.ptr());
+ import_settings_data->notify_property_list_changed();
+
+ if (font_preview->get_data_count() > 0) {
+ font_preview->get_data(0)->set_antialiased(import_settings_data->get("antialiased"));
+ font_preview->get_data(0)->set_multichannel_signed_distance_field(import_settings_data->get("multichannel_signed_distance_field"));
+ font_preview->get_data(0)->set_msdf_pixel_range(import_settings_data->get("msdf_pixel_range"));
+ font_preview->get_data(0)->set_msdf_size(import_settings_data->get("msdf_size"));
+ font_preview->get_data(0)->set_force_autohinter(import_settings_data->get("force_autohinter"));
+ font_preview->get_data(0)->set_hinting((TextServer::Hinting)import_settings_data->get("hinting").operator int());
+ font_preview->get_data(0)->set_oversampling(import_settings_data->get("oversampling"));
+ }
+ font_preview_label->add_theme_font_override("font", font_preview);
+ font_preview_label->update();
+
+ _variations_validate();
+
+ popup_centered_ratio();
+
+ set_title(vformat(TTR("Advanced Import Settings for '%s'"), base_path.get_file()));
+}
+
+DynamicFontImportSettings *DynamicFontImportSettings::get_singleton() {
+ return singleton;
+}
+
+DynamicFontImportSettings::DynamicFontImportSettings() {
+ singleton = this;
+
+ options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "antialiased"), true));
+ options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));
+ options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_RANGE, "1,100,1"), 8));
+ options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_RANGE, "1,250,1"), 48));
+ options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "force_autohinter"), false));
+ options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), 1));
+ options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), 0.0));
+ options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "compress", PROPERTY_HINT_NONE, ""), false));
+
+ // Popup menus
+
+ menu_langs = memnew(PopupMenu);
+ menu_langs->set_name("Language");
+ for (int i = 0; langs[i].name != String(); i++) {
+ if (langs[i].name == "-") {
+ menu_langs->add_separator();
+ } else {
+ menu_langs->add_item(langs[i].name + " (" + langs[i].code + ")", i);
+ }
+ }
+ add_child(menu_langs);
+ menu_langs->connect("id_pressed", callable_mp(this, &DynamicFontImportSettings::_lang_add_item));
+
+ menu_scripts = memnew(PopupMenu);
+ menu_scripts->set_name("Script");
+ for (int i = 0; scripts[i].name != String(); i++) {
+ if (scripts[i].name == "-") {
+ menu_scripts->add_separator();
+ } else {
+ menu_scripts->add_item(scripts[i].name + " (" + scripts[i].code + ")", i);
+ }
+ }
+ add_child(menu_scripts);
+ menu_scripts->connect("id_pressed", callable_mp(this, &DynamicFontImportSettings::_script_add_item));
+
+ Color warn_color = (EditorNode::get_singleton()) ? EditorNode::get_singleton()->get_gui_base()->get_theme_color("warning_color", "Editor") : Color(1, 1, 0);
+
+ // Root layout
+
+ VBoxContainer *root_vb = memnew(VBoxContainer);
+ add_child(root_vb);
+
+ main_pages = memnew(TabContainer);
+ main_pages->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ main_pages->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ root_vb->add_child(main_pages);
+
+ label_warn = memnew(Label);
+ label_warn->set_align(Label::ALIGN_CENTER);
+ label_warn->set_text("");
+ root_vb->add_child(label_warn);
+ label_warn->add_theme_color_override("font_color", warn_color);
+ label_warn->hide();
+
+ // Page 1 layout: Rendering Options
+
+ VBoxContainer *page1_vb = memnew(VBoxContainer);
+ page1_vb->set_meta("_tab_name", TTR("Rendering options"));
+ main_pages->add_child(page1_vb);
+
+ page1_description = memnew(Label);
+ page1_description->set_text(TTR("Select font rendering options:"));
+ page1_description->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ page1_vb->add_child(page1_description);
+
+ HSplitContainer *page1_hb = memnew(HSplitContainer);
+ page1_hb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ page1_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ page1_vb->add_child(page1_hb);
+
+ font_preview_label = memnew(Label);
+ font_preview_label->add_theme_font_size_override("font_size", 200 * EDSCALE);
+ font_preview_label->set_align(Label::ALIGN_CENTER);
+ font_preview_label->set_valign(Label::VALIGN_CENTER);
+ font_preview_label->set_autowrap_mode(Label::AUTOWRAP_ARBITRARY);
+ font_preview_label->set_clip_text(true);
+ font_preview_label->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ font_preview_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ page1_hb->add_child(font_preview_label);
+
+ inspector_general = memnew(EditorInspector);
+ inspector_general->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ inspector_general->set_custom_minimum_size(Size2(300 * EDSCALE, 250 * EDSCALE));
+ inspector_general->connect("property_edited", callable_mp(this, &DynamicFontImportSettings::_main_prop_changed));
+ page1_hb->add_child(inspector_general);
+
+ // Page 2 layout: Configurations
+ VBoxContainer *page2_vb = memnew(VBoxContainer);
+ page2_vb->set_meta("_tab_name", TTR("Sizes and variations"));
+ main_pages->add_child(page2_vb);
+
+ page2_description = memnew(Label);
+ page2_description->set_text(TTR("Add font size, variation coordinates, and extra spacing combinations to pre-render:"));
+ page2_description->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ page2_description->set_autowrap_mode(Label::AUTOWRAP_WORD_SMART);
+ page2_vb->add_child(page2_description);
+
+ HSplitContainer *page2_hb = memnew(HSplitContainer);
+ page2_hb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ page2_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ page2_vb->add_child(page2_hb);
+
+ VBoxContainer *page2_side_vb = memnew(VBoxContainer);
+ page2_hb->add_child(page2_side_vb);
+
+ HBoxContainer *page2_hb_vars = memnew(HBoxContainer);
+ page2_side_vb->add_child(page2_hb_vars);
+
+ label_vars = memnew(Label);
+ page2_hb_vars->add_child(label_vars);
+ label_vars->set_align(Label::ALIGN_CENTER);
+ label_vars->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ label_vars->set_text(TTR("Configuration:"));
+
+ add_var = memnew(Button);
+ page2_hb_vars->add_child(add_var);
+ add_var->set_tooltip(TTR("Add configuration"));
+ add_var->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
+ add_var->connect("pressed", callable_mp(this, &DynamicFontImportSettings::_variation_add));
+
+ vars_list = memnew(Tree);
+ page2_side_vb->add_child(vars_list);
+ vars_list->set_custom_minimum_size(Size2(300 * EDSCALE, 0));
+ vars_list->set_hide_root(true);
+ vars_list->set_columns(2);
+ vars_list->set_column_expand(0, true);
+ vars_list->set_column_custom_minimum_width(0, 80 * EDSCALE);
+ vars_list->set_column_expand(1, false);
+ vars_list->set_column_custom_minimum_width(1, 50 * EDSCALE);
+ vars_list->connect("item_selected", callable_mp(this, &DynamicFontImportSettings::_variation_selected));
+ vars_list->connect("button_pressed", callable_mp(this, &DynamicFontImportSettings::_variation_remove));
+ vars_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+
+ inspector_vars = memnew(EditorInspector);
+ inspector_vars->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ inspector_vars->connect("property_edited", callable_mp(this, &DynamicFontImportSettings::_variation_changed));
+ page2_hb->add_child(inspector_vars);
+
+ // Page 3 layout: Text to select glyphs
+ VBoxContainer *page3_vb = memnew(VBoxContainer);
+ page3_vb->set_meta("_tab_name", TTR("Glyphs from the text"));
+ main_pages->add_child(page3_vb);
+
+ page3_description = memnew(Label);
+ page3_description->set_text(TTR("Enter a text to shape and add all required glyphs to pre-render list:"));
+ page3_description->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ page3_description->set_autowrap_mode(Label::AUTOWRAP_WORD_SMART);
+ page3_vb->add_child(page3_description);
+
+ HBoxContainer *ot_hb = memnew(HBoxContainer);
+ page3_vb->add_child(ot_hb);
+ ot_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+
+ Label *label_ed_ftr = memnew(Label);
+ ot_hb->add_child(label_ed_ftr);
+ label_ed_ftr->set_text(TTR("OpenType features:"));
+
+ ftr_edit = memnew(LineEdit);
+ ot_hb->add_child(ftr_edit);
+ ftr_edit->connect("text_changed", callable_mp(this, &DynamicFontImportSettings::_change_text_opts));
+ ftr_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+
+ Label *label_ed_lang = memnew(Label);
+ ot_hb->add_child(label_ed_lang);
+ label_ed_lang->set_text(TTR("Text language:"));
+
+ lang_edit = memnew(LineEdit);
+ ot_hb->add_child(lang_edit);
+ lang_edit->connect("text_changed", callable_mp(this, &DynamicFontImportSettings::_change_text_opts));
+ lang_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+
+ text_edit = memnew(TextEdit);
+ page3_vb->add_child(text_edit);
+ text_edit->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ text_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+
+ HBoxContainer *text_hb = memnew(HBoxContainer);
+ page3_vb->add_child(text_hb);
+ text_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+
+ label_glyphs = memnew(Label);
+ text_hb->add_child(label_glyphs);
+ label_glyphs->set_text(TTR("Preloaded glyphs: ") + itos(0));
+ label_glyphs->set_custom_minimum_size(Size2(50 * EDSCALE, 0));
+
+ Button *btn_fill = memnew(Button);
+ text_hb->add_child(btn_fill);
+ btn_fill->set_text(TTR("Shape text and add glyphs"));
+ btn_fill->connect("pressed", callable_mp(this, &DynamicFontImportSettings::_glyph_text_selected));
+
+ Button *btn_clear = memnew(Button);
+ text_hb->add_child(btn_clear);
+ btn_clear->set_text(TTR("Clear glyph list"));
+ btn_clear->connect("pressed", callable_mp(this, &DynamicFontImportSettings::_glyph_clear));
+
+ // Page 4 layout: Character map
+ VBoxContainer *page4_vb = memnew(VBoxContainer);
+ page4_vb->set_meta("_tab_name", TTR("Glyphs from the character map"));
+ main_pages->add_child(page4_vb);
+
+ page4_description = memnew(Label);
+ page4_description->set_text(TTR("Add or remove additional glyphs from the character map to pre-render list:\nNote: Some stylistic alternatives and glyph variants do not have one-to-one correspondence to character, and not shown in this map, use \"Glyphs from the text\" to add these."));
+ page4_description->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ page4_description->set_autowrap_mode(Label::AUTOWRAP_WORD_SMART);
+ page4_vb->add_child(page4_description);
+
+ HSplitContainer *glyphs_split = memnew(HSplitContainer);
+ glyphs_split->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ glyphs_split->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ page4_vb->add_child(glyphs_split);
+
+ glyph_table = memnew(Tree);
+ glyphs_split->add_child(glyph_table);
+ glyph_table->set_custom_minimum_size(Size2((30 * 16 + 100) * EDSCALE, 0));
+ glyph_table->set_columns(17);
+ glyph_table->set_column_expand(0, false);
+ glyph_table->set_hide_root(true);
+ glyph_table->set_allow_reselect(true);
+ glyph_table->set_select_mode(Tree::SELECT_SINGLE);
+ glyph_table->connect("item_activated", callable_mp(this, &DynamicFontImportSettings::_glyph_selected));
+ glyph_table->set_column_titles_visible(true);
+ for (int i = 0; i < 16; i++) {
+ glyph_table->set_column_title(i + 1, String::num_int64(i, 16));
+ }
+ glyph_table->add_theme_style_override("selected", glyph_table->get_theme_stylebox("bg"));
+ glyph_table->add_theme_style_override("selected_focus", glyph_table->get_theme_stylebox("bg"));
+ glyph_table->add_theme_constant_override("hseparation", 0);
+ glyph_table->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ glyph_table->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+
+ glyph_tree = memnew(Tree);
+ glyphs_split->add_child(glyph_tree);
+ glyph_tree->set_custom_minimum_size(Size2(300 * EDSCALE, 0));
+ glyph_tree->set_columns(3);
+ glyph_tree->set_hide_root(true);
+ glyph_tree->set_column_expand(0, false);
+ glyph_tree->set_column_expand(1, true);
+ glyph_tree->set_column_custom_minimum_width(0, 120 * EDSCALE);
+ glyph_tree->connect("item_activated", callable_mp(this, &DynamicFontImportSettings::_range_edited));
+ glyph_tree->connect("item_selected", callable_mp(this, &DynamicFontImportSettings::_range_selected));
+ glyph_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ glyph_root = glyph_tree->create_item();
+ for (int i = 0; unicode_ranges[i].name != String(); i++) {
+ _add_glyph_range_item(unicode_ranges[i].start, unicode_ranges[i].end, unicode_ranges[i].name);
+ }
+
+ // Page 4 layout: Metadata override
+ VBoxContainer *page5_vb = memnew(VBoxContainer);
+ page5_vb->set_meta("_tab_name", TTR("Metadata override"));
+ main_pages->add_child(page5_vb);
+
+ page5_description = memnew(Label);
+ page5_description->set_text(TTR("Add or remove language and script support overrides, to control fallback font selection order:"));
+ page5_description->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ page5_description->set_autowrap_mode(Label::AUTOWRAP_WORD_SMART);
+ page5_vb->add_child(page5_description);
+
+ HBoxContainer *hb_lang = memnew(HBoxContainer);
+ page5_vb->add_child(hb_lang);
+
+ label_langs = memnew(Label);
+ label_langs->set_align(Label::ALIGN_CENTER);
+ label_langs->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ label_langs->set_text(TTR("Language support overrides"));
+ hb_lang->add_child(label_langs);
+
+ add_lang = memnew(Button);
+ hb_lang->add_child(add_lang);
+ add_lang->set_tooltip(TTR("Add language override"));
+ add_lang->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
+ add_lang->connect("pressed", callable_mp(this, &DynamicFontImportSettings::_lang_add));
+
+ lang_list = memnew(Tree);
+ page5_vb->add_child(lang_list);
+ lang_list->set_hide_root(true);
+ lang_list->set_columns(3);
+ lang_list->set_column_expand(0, false); // Check
+ lang_list->set_column_custom_minimum_width(0, 50 * EDSCALE);
+ lang_list->set_column_expand(1, true);
+ lang_list->set_column_custom_minimum_width(1, 80 * EDSCALE);
+ lang_list->set_column_expand(2, false);
+ lang_list->set_column_custom_minimum_width(2, 50 * EDSCALE);
+ lang_list->connect("button_pressed", callable_mp(this, &DynamicFontImportSettings::_lang_remove));
+ lang_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+
+ HBoxContainer *hb_script = memnew(HBoxContainer);
+ page5_vb->add_child(hb_script);
+
+ label_script = memnew(Label);
+ label_script->set_align(Label::ALIGN_CENTER);
+ label_script->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ label_script->set_text(TTR("Script support overrides"));
+ hb_script->add_child(label_script);
+
+ add_script = memnew(Button);
+ hb_script->add_child(add_script);
+ add_script->set_tooltip(TTR("Add script override"));
+ add_script->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
+ add_script->connect("pressed", callable_mp(this, &DynamicFontImportSettings::_script_add));
+
+ script_list = memnew(Tree);
+ page5_vb->add_child(script_list);
+ script_list->set_hide_root(true);
+ script_list->set_columns(3);
+ script_list->set_column_expand(0, false);
+ script_list->set_column_custom_minimum_width(0, 50 * EDSCALE);
+ script_list->set_column_expand(1, true);
+ script_list->set_column_custom_minimum_width(1, 80 * EDSCALE);
+ script_list->set_column_expand(2, false);
+ script_list->set_column_custom_minimum_width(2, 50 * EDSCALE);
+ script_list->connect("button_pressed", callable_mp(this, &DynamicFontImportSettings::_script_remove));
+ script_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+
+ // Common
+
+ import_settings_data.instantiate();
+ import_settings_data->owner = this;
+
+ get_ok_button()->set_text(TTR("Reimport"));
+ get_cancel_button()->set_text(TTR("Close"));
+}
diff --git a/editor/import/dynamicfont_import_settings.h b/editor/import/dynamicfont_import_settings.h
new file mode 100644
index 0000000000..05f5e8e00b
--- /dev/null
+++ b/editor/import/dynamicfont_import_settings.h
@@ -0,0 +1,167 @@
+/*************************************************************************/
+/* dynamicfont_import_settings.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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. */
+/*************************************************************************/
+
+#ifndef FONTDATA_IMPORT_SETTINGS_H
+#define FONTDATA_IMPORT_SETTINGS_H
+
+#include "editor/editor_file_dialog.h"
+#include "editor/editor_inspector.h"
+
+#include "editor/import/resource_importer_dynamicfont.h"
+
+#include "scene/gui/dialogs.h"
+#include "scene/gui/item_list.h"
+#include "scene/gui/option_button.h"
+#include "scene/gui/split_container.h"
+#include "scene/gui/subviewport_container.h"
+#include "scene/gui/tab_container.h"
+#include "scene/gui/text_edit.h"
+#include "scene/gui/tree.h"
+
+#include "scene/resources/font.h"
+#include "servers/text_server.h"
+
+class DynamicFontImportSettingsData;
+
+class DynamicFontImportSettings : public ConfirmationDialog {
+ GDCLASS(DynamicFontImportSettings, ConfirmationDialog)
+ friend class DynamicFontImportSettingsData;
+
+ enum ItemButton {
+ BUTTON_ADD_VAR,
+ BUTTON_REMOVE_VAR,
+ };
+
+ static DynamicFontImportSettings *singleton;
+
+ String base_path;
+
+ Ref<DynamicFontImportSettingsData> import_settings_data;
+ List<ResourceImporter::ImportOption> options_variations;
+ List<ResourceImporter::ImportOption> options_general;
+
+ // Root layout
+ Label *label_warn = nullptr;
+ TabContainer *main_pages = nullptr;
+
+ // Page 1 layout: Rendering Options
+ Label *page1_description = nullptr;
+ Label *font_preview_label = nullptr;
+ EditorInspector *inspector_general = nullptr;
+
+ void _main_prop_changed(const String &p_edited_property);
+
+ // Page 2 layout: Configurations
+ Label *page2_description = nullptr;
+ Label *label_vars = nullptr;
+ Button *add_var = nullptr;
+ Tree *vars_list = nullptr;
+ TreeItem *vars_list_root = nullptr;
+ EditorInspector *inspector_vars = nullptr;
+
+ void _variation_add();
+ void _variation_selected();
+ void _variation_remove(Object *p_item, int p_column, int p_id);
+ void _variation_changed(const String &p_edited_property);
+ void _variations_validate();
+
+ // Page 3 layout: Text to select glyphs
+ Label *page3_description = nullptr;
+ Label *label_glyphs = nullptr;
+ TextEdit *text_edit = nullptr;
+ LineEdit *ftr_edit = nullptr;
+ LineEdit *lang_edit = nullptr;
+
+ void _change_text_opts();
+ void _glyph_text_selected();
+ void _glyph_clear();
+
+ // Page 4 layout: Character map
+ Label *page4_description = nullptr;
+ Tree *glyph_table = nullptr;
+ Tree *glyph_tree = nullptr;
+ TreeItem *glyph_root = nullptr;
+
+ void _glyph_selected();
+ void _range_edited();
+ void _range_selected();
+ void _edit_range(int32_t p_start, int32_t p_end);
+ bool _char_update(int32_t p_char);
+ void _range_update(int32_t p_start, int32_t p_end);
+
+ // Page 5 layout: Metadata override
+ Label *page5_description = nullptr;
+ Button *add_lang = nullptr;
+ Button *add_script = nullptr;
+
+ PopupMenu *menu_langs = nullptr;
+ PopupMenu *menu_scripts = nullptr;
+
+ Tree *lang_list = nullptr;
+ TreeItem *lang_list_root = nullptr;
+
+ Tree *script_list = nullptr;
+ TreeItem *script_list_root = nullptr;
+ Label *label_langs = nullptr;
+ Label *label_script = nullptr;
+
+ void _lang_add();
+ void _lang_add_item(int p_option);
+ void _lang_remove(Object *p_item, int p_column, int p_id);
+
+ void _script_add();
+ void _script_add_item(int p_option);
+ void _script_remove(Object *p_item, int p_column, int p_id);
+
+ // Common
+
+ void _add_glyph_range_item(int32_t p_start, int32_t p_end, const String &p_name);
+
+ Ref<Font> font_preview;
+ Ref<Font> font_main;
+
+ Set<char32_t> selected_chars;
+ Set<int32_t> selected_glyphs;
+
+ void _re_import();
+
+ String _pad_zeros(const String &p_hex) const;
+
+protected:
+ void _notification(int p_what);
+
+public:
+ void open_settings(const String &p_path);
+ static DynamicFontImportSettings *get_singleton();
+
+ DynamicFontImportSettings();
+};
+
+#endif // FONTDATA_IMPORT_SETTINGS_H
diff --git a/editor/import/resource_importer_bmfont.cpp b/editor/import/resource_importer_bmfont.cpp
new file mode 100644
index 0000000000..2e7ef1402b
--- /dev/null
+++ b/editor/import/resource_importer_bmfont.cpp
@@ -0,0 +1,797 @@
+/*************************************************************************/
+/* resource_importer_bmfont.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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. */
+/*************************************************************************/
+
+#include "resource_importer_bmfont.h"
+
+#include "core/io/image_loader.h"
+#include "core/io/resource_saver.h"
+
+String ResourceImporterBMFont::get_importer_name() const {
+ return "font_data_bmfont";
+}
+
+String ResourceImporterBMFont::get_visible_name() const {
+ return "Font Data (AngelCode BMFont)";
+}
+
+void ResourceImporterBMFont::get_recognized_extensions(List<String> *p_extensions) const {
+ if (p_extensions) {
+ p_extensions->push_back("font");
+ p_extensions->push_back("fnt");
+ }
+}
+
+String ResourceImporterBMFont::get_save_extension() const {
+ return "fontdata";
+}
+
+String ResourceImporterBMFont::get_resource_type() const {
+ return "FontData";
+}
+
+bool ResourceImporterBMFont::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
+ return true;
+}
+
+void ResourceImporterBMFont::get_import_options(List<ImportOption> *r_options, int p_preset) const {
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress"), true));
+}
+
+void _convert_packed_8bit(Ref<FontData> &r_font, Ref<Image> &p_source, int p_page, int p_sz) {
+ int w = p_source->get_width();
+ int h = p_source->get_height();
+
+ PackedByteArray imgdata = p_source->get_data();
+ const uint8_t *r = imgdata.ptr();
+
+ PackedByteArray imgdata_r;
+ imgdata_r.resize(w * h * 2);
+ uint8_t *wr = imgdata_r.ptrw();
+
+ PackedByteArray imgdata_g;
+ imgdata_g.resize(w * h * 2);
+ uint8_t *wg = imgdata_g.ptrw();
+
+ PackedByteArray imgdata_b;
+ imgdata_b.resize(w * h * 2);
+ uint8_t *wb = imgdata_b.ptrw();
+
+ PackedByteArray imgdata_a;
+ imgdata_a.resize(w * h * 2);
+ uint8_t *wa = imgdata_a.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs_src = (i * w + j) * 4;
+ int ofs_dst = (i * w + j) * 2;
+ wr[ofs_dst + 0] = 255;
+ wr[ofs_dst + 1] = r[ofs_src + 0];
+ wg[ofs_dst + 0] = 255;
+ wg[ofs_dst + 1] = r[ofs_src + 1];
+ wb[ofs_dst + 0] = 255;
+ wb[ofs_dst + 1] = r[ofs_src + 2];
+ wa[ofs_dst + 0] = 255;
+ wa[ofs_dst + 1] = r[ofs_src + 3];
+ }
+ }
+ Ref<Image> img_r = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_r));
+ r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 0, img_r);
+ Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
+ r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 1, img_g);
+ Ref<Image> img_b = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_b));
+ r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 2, img_b);
+ Ref<Image> img_a = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_a));
+ r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 3, img_a);
+}
+
+void _convert_packed_4bit(Ref<FontData> &r_font, Ref<Image> &p_source, int p_page, int p_sz) {
+ int w = p_source->get_width();
+ int h = p_source->get_height();
+
+ PackedByteArray imgdata = p_source->get_data();
+ const uint8_t *r = imgdata.ptr();
+
+ PackedByteArray imgdata_r;
+ imgdata_r.resize(w * h * 2);
+ uint8_t *wr = imgdata_r.ptrw();
+
+ PackedByteArray imgdata_g;
+ imgdata_g.resize(w * h * 2);
+ uint8_t *wg = imgdata_g.ptrw();
+
+ PackedByteArray imgdata_b;
+ imgdata_b.resize(w * h * 2);
+ uint8_t *wb = imgdata_b.ptrw();
+
+ PackedByteArray imgdata_a;
+ imgdata_a.resize(w * h * 2);
+ uint8_t *wa = imgdata_a.ptrw();
+
+ PackedByteArray imgdata_ro;
+ imgdata_ro.resize(w * h * 2);
+ uint8_t *wro = imgdata_ro.ptrw();
+
+ PackedByteArray imgdata_go;
+ imgdata_go.resize(w * h * 2);
+ uint8_t *wgo = imgdata_go.ptrw();
+
+ PackedByteArray imgdata_bo;
+ imgdata_bo.resize(w * h * 2);
+ uint8_t *wbo = imgdata_bo.ptrw();
+
+ PackedByteArray imgdata_ao;
+ imgdata_ao.resize(w * h * 2);
+ uint8_t *wao = imgdata_ao.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs_src = (i * w + j) * 4;
+ int ofs_dst = (i * w + j) * 2;
+ wr[ofs_dst + 0] = 255;
+ wro[ofs_dst + 0] = 255;
+ if (r[ofs_src + 0] > 0x0F) {
+ wr[ofs_dst + 1] = (r[ofs_src + 0] - 0x0F) * 2;
+ wro[ofs_dst + 1] = 0;
+ } else {
+ wr[ofs_dst + 1] = 0;
+ wro[ofs_dst + 1] = r[ofs_src + 0] * 2;
+ }
+ wg[ofs_dst + 0] = 255;
+ wgo[ofs_dst + 0] = 255;
+ if (r[ofs_src + 1] > 0x0F) {
+ wg[ofs_dst + 1] = (r[ofs_src + 1] - 0x0F) * 2;
+ wgo[ofs_dst + 1] = 0;
+ } else {
+ wg[ofs_dst + 1] = 0;
+ wgo[ofs_dst + 1] = r[ofs_src + 1] * 2;
+ }
+ wb[ofs_dst + 0] = 255;
+ wbo[ofs_dst + 0] = 255;
+ if (r[ofs_src + 2] > 0x0F) {
+ wb[ofs_dst + 1] = (r[ofs_src + 2] - 0x0F) * 2;
+ wbo[ofs_dst + 1] = 0;
+ } else {
+ wb[ofs_dst + 1] = 0;
+ wbo[ofs_dst + 1] = r[ofs_src + 2] * 2;
+ }
+ wa[ofs_dst + 0] = 255;
+ wao[ofs_dst + 0] = 255;
+ if (r[ofs_src + 3] > 0x0F) {
+ wa[ofs_dst + 1] = (r[ofs_src + 3] - 0x0F) * 2;
+ wao[ofs_dst + 1] = 0;
+ } else {
+ wa[ofs_dst + 1] = 0;
+ wao[ofs_dst + 1] = r[ofs_src + 3] * 2;
+ }
+ }
+ }
+ Ref<Image> img_r = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_r));
+ r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 0, img_r);
+ Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
+ r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 1, img_g);
+ Ref<Image> img_b = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_b));
+ r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 2, img_b);
+ Ref<Image> img_a = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_a));
+ r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 3, img_a);
+
+ Ref<Image> img_ro = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_ro));
+ r_font->set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 0, img_ro);
+ Ref<Image> img_go = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_go));
+ r_font->set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 1, img_go);
+ Ref<Image> img_bo = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_bo));
+ r_font->set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 2, img_bo);
+ Ref<Image> img_ao = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_ao));
+ r_font->set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 3, img_ao);
+}
+
+void _convert_rgba_4bit(Ref<FontData> &r_font, Ref<Image> &p_source, int p_page, int p_sz) {
+ int w = p_source->get_width();
+ int h = p_source->get_height();
+
+ PackedByteArray imgdata = p_source->get_data();
+ const uint8_t *r = imgdata.ptr();
+
+ PackedByteArray imgdata_g;
+ imgdata_g.resize(w * h * 4);
+ uint8_t *wg = imgdata_g.ptrw();
+
+ PackedByteArray imgdata_o;
+ imgdata_o.resize(w * h * 4);
+ uint8_t *wo = imgdata_o.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs = (i * w + j) * 4;
+
+ if (r[ofs + 0] > 0x7F) {
+ wg[ofs + 0] = r[ofs + 0];
+ wo[ofs + 0] = 0;
+ } else {
+ wg[ofs + 0] = 0;
+ wo[ofs + 0] = r[ofs + 0] * 2;
+ }
+ if (r[ofs + 1] > 0x7F) {
+ wg[ofs + 1] = r[ofs + 1];
+ wo[ofs + 1] = 0;
+ } else {
+ wg[ofs + 1] = 0;
+ wo[ofs + 1] = r[ofs + 1] * 2;
+ }
+ if (r[ofs + 2] > 0x7F) {
+ wg[ofs + 2] = r[ofs + 2];
+ wo[ofs + 2] = 0;
+ } else {
+ wg[ofs + 2] = 0;
+ wo[ofs + 2] = r[ofs + 2] * 2;
+ }
+ if (r[ofs + 3] > 0x7F) {
+ wg[ofs + 3] = r[ofs + 3];
+ wo[ofs + 3] = 0;
+ } else {
+ wg[ofs + 3] = 0;
+ wo[ofs + 3] = r[ofs + 3] * 2;
+ }
+ }
+ }
+ Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_RGBA8, imgdata_g));
+ r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page, img_g);
+
+ Ref<Image> img_o = memnew(Image(w, h, 0, Image::FORMAT_RGBA8, imgdata_o));
+ r_font->set_texture_image(0, Vector2i(p_sz, 1), p_page, img_o);
+}
+
+void _convert_mono_8bit(Ref<FontData> &r_font, Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol) {
+ int w = p_source->get_width();
+ int h = p_source->get_height();
+
+ PackedByteArray imgdata = p_source->get_data();
+ const uint8_t *r = imgdata.ptr();
+
+ int size = 4;
+ if (p_source->get_format() == Image::FORMAT_L8) {
+ size = 1;
+ p_ch = 0;
+ }
+
+ PackedByteArray imgdata_g;
+ imgdata_g.resize(w * h * 2);
+ uint8_t *wg = imgdata_g.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs_src = (i * w + j) * size;
+ int ofs_dst = (i * w + j) * 2;
+ wg[ofs_dst + 0] = 255;
+ wg[ofs_dst + 1] = r[ofs_src + p_ch];
+ }
+ }
+ Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
+ r_font->set_texture_image(0, Vector2i(p_sz, p_ol), p_page, img_g);
+}
+
+void _convert_mono_4bit(Ref<FontData> &r_font, Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol) {
+ int w = p_source->get_width();
+ int h = p_source->get_height();
+
+ PackedByteArray imgdata = p_source->get_data();
+ const uint8_t *r = imgdata.ptr();
+
+ int size = 4;
+ if (p_source->get_format() == Image::FORMAT_L8) {
+ size = 1;
+ p_ch = 0;
+ }
+
+ PackedByteArray imgdata_g;
+ imgdata_g.resize(w * h * 2);
+ uint8_t *wg = imgdata_g.ptrw();
+
+ PackedByteArray imgdata_o;
+ imgdata_o.resize(w * h * 2);
+ uint8_t *wo = imgdata_o.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs_src = (i * w + j) * size;
+ int ofs_dst = (i * w + j) * 2;
+ wg[ofs_dst + 0] = 255;
+ wo[ofs_dst + 0] = 255;
+ if (r[ofs_src + p_ch] > 0x7F) {
+ wg[ofs_dst + 1] = r[ofs_src + p_ch];
+ wo[ofs_dst + 1] = 0;
+ } else {
+ wg[ofs_dst + 1] = 0;
+ wo[ofs_dst + 1] = r[ofs_src + p_ch] * 2;
+ }
+ }
+ }
+ Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
+ r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page, img_g);
+
+ Ref<Image> img_o = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_o));
+ r_font->set_texture_image(0, Vector2i(p_sz, p_ol), p_page, img_o);
+}
+
+Error ResourceImporterBMFont::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
+ print_verbose("Importing BMFont font from: " + p_source_file);
+
+ Ref<FontData> font;
+ font.instantiate();
+ font->set_antialiased(false);
+ font->set_multichannel_signed_distance_field(false);
+ font->set_force_autohinter(false);
+ font->set_hinting(TextServer::HINTING_NONE);
+ font->set_oversampling(1.0f);
+
+ FileAccessRef f = FileAccess::open(p_source_file, FileAccess::READ);
+ if (f == nullptr) {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Cannot open font from file ") + "\"" + p_source_file + "\".");
+ }
+
+ int base_size = 16;
+ int height = 0;
+ int ascent = 0;
+ int outline = 0;
+
+ bool packed = false;
+ uint8_t ch[4] = { 0, 0, 0, 0 }; // RGBA
+ int first_gl_ch = -1;
+ int first_ol_ch = -1;
+ int first_cm_ch = -1;
+
+ unsigned char magic[4];
+ f->get_buffer((unsigned char *)&magic, 4);
+ if (magic[0] == 'B' && magic[1] == 'M' && magic[2] == 'F') {
+ // Binary BMFont file.
+ ERR_FAIL_COND_V_MSG(magic[3] != 3, ERR_CANT_CREATE, vformat(TTR("Version %d of BMFont is not supported."), (int)magic[3]));
+
+ uint8_t block_type = f->get_8();
+ uint32_t block_size = f->get_32();
+ while (!f->eof_reached()) {
+ uint64_t off = f->get_position();
+ switch (block_type) {
+ case 1: /* info */ {
+ ERR_FAIL_COND_V_MSG(block_size < 15, ERR_CANT_CREATE, TTR("Invalid BMFont info block size."));
+ base_size = f->get_16();
+ uint8_t flags = f->get_8();
+ ERR_FAIL_COND_V_MSG(flags & 0x02, ERR_CANT_CREATE, TTR("Non-unicode version of BMFont is not supported."));
+ f->get_8(); // non-unicode charset, skip
+ f->get_16(); // stretch_h, skip
+ f->get_8(); // aa, skip
+ f->get_32(); // padding, skip
+ f->get_16(); // spacing, skip
+ outline = f->get_8();
+ // font name, skip
+ font->set_fixed_size(base_size);
+ } break;
+ case 2: /* common */ {
+ ERR_FAIL_COND_V_MSG(block_size != 15, ERR_CANT_CREATE, TTR("Invalid BMFont common block size."));
+ height = f->get_16();
+ ascent = f->get_16();
+ f->get_32(); // scale, skip
+ f->get_16(); // pages, skip
+ uint8_t flags = f->get_8();
+ packed = (flags & 0x01);
+ ch[3] = f->get_8();
+ ch[0] = f->get_8();
+ ch[1] = f->get_8();
+ ch[2] = f->get_8();
+ for (int i = 0; i < 4; i++) {
+ if (ch[i] == 0 && first_gl_ch == -1) {
+ first_gl_ch = i;
+ }
+ if (ch[i] == 1 && first_ol_ch == -1) {
+ first_ol_ch = i;
+ }
+ if (ch[i] == 2 && first_cm_ch == -1) {
+ first_cm_ch = i;
+ }
+ }
+ } break;
+ case 3: /* pages */ {
+ int page = 0;
+ CharString cs;
+ char32_t c = f->get_8();
+ while (!f->eof_reached() && f->get_position() <= off + block_size) {
+ if (c == '\0') {
+ String base_dir = p_source_file.get_base_dir();
+ String file = base_dir.plus_file(String::utf8(cs.ptr(), cs.length()));
+ if (RenderingServer::get_singleton() != nullptr) {
+ Ref<Image> img;
+ img.instantiate();
+ Error err = ImageLoader::load_image(file, img);
+ ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_READ, TTR("Can't load font texture: ") + "\"" + file + "\".");
+
+ if (packed) {
+ if (ch[3] == 0) { // 4 x 8 bit monochrome, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_packed_8bit(font, img, page, base_size);
+ } else if ((ch[3] == 2) && (outline > 0)) { // 4 x 4 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_packed_4bit(font, img, page, base_size);
+ } else {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format."));
+ }
+ } else {
+ if ((ch[0] == 0) && (ch[1] == 0) && (ch[2] == 0) && (ch[3] == 0)) { // RGBA8 color, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ font->set_texture_image(0, Vector2i(base_size, 0), page, img);
+ } else if ((ch[0] == 2) && (ch[1] == 2) && (ch[2] == 2) && (ch[3] == 2) && (outline > 0)) { // RGBA4 color, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_rgba_4bit(font, img, page, base_size);
+ } else if ((first_gl_ch >= 0) && (first_ol_ch >= 0) && (outline > 0)) { // 1 x 8 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_mono_8bit(font, img, page, first_gl_ch, base_size, 0);
+ _convert_mono_8bit(font, img, page, first_ol_ch, base_size, 1);
+ } else if ((first_cm_ch >= 0) && (outline > 0)) { // 1 x 4 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_mono_4bit(font, img, page, first_cm_ch, base_size, 1);
+ } else if (first_gl_ch >= 0) { // 1 x 8 bit monochrome, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_mono_8bit(font, img, page, first_gl_ch, base_size, 0);
+ } else {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format."));
+ }
+ }
+ }
+ page++;
+ cs = "";
+ } else {
+ cs += c;
+ }
+ c = f->get_8();
+ }
+ } break;
+ case 4: /* chars */ {
+ int char_count = block_size / 20;
+ for (int i = 0; i < char_count; i++) {
+ Vector2 advance;
+ Vector2 size;
+ Vector2 offset;
+ Rect2 uv_rect;
+
+ char32_t idx = f->get_32();
+ uv_rect.position.x = (int16_t)f->get_16();
+ uv_rect.position.y = (int16_t)f->get_16();
+ uv_rect.size.width = (int16_t)f->get_16();
+ size.width = uv_rect.size.width;
+ uv_rect.size.height = (int16_t)f->get_16();
+ size.height = uv_rect.size.height;
+ offset.x = (int16_t)f->get_16();
+ offset.y = (int16_t)f->get_16() - ascent;
+ advance.x = (int16_t)f->get_16();
+ if (advance.x < 0) {
+ advance.x = size.width + 1;
+ }
+
+ int texture_idx = f->get_8();
+ uint8_t channel = f->get_8();
+
+ ERR_FAIL_COND_V_MSG(!packed && channel != 15, ERR_CANT_CREATE, TTR("Invalid glyph channel."));
+ int ch_off = 0;
+ switch (channel) {
+ case 1:
+ ch_off = 2;
+ break; // B
+ case 2:
+ ch_off = 1;
+ break; // G
+ case 4:
+ ch_off = 0;
+ break; // R
+ case 8:
+ ch_off = 3;
+ break; // A
+ default:
+ ch_off = 0;
+ break;
+ }
+ font->set_glyph_advance(0, base_size, idx, advance);
+ font->set_glyph_offset(0, Vector2i(base_size, 0), idx, offset);
+ font->set_glyph_size(0, Vector2i(base_size, 0), idx, size);
+ font->set_glyph_uv_rect(0, Vector2i(base_size, 0), idx, uv_rect);
+ font->set_glyph_texture_idx(0, Vector2i(base_size, 0), idx, texture_idx * (packed ? 4 : 1) + ch_off);
+ if (outline > 0) {
+ font->set_glyph_offset(0, Vector2i(base_size, 1), idx, offset);
+ font->set_glyph_size(0, Vector2i(base_size, 1), idx, size);
+ font->set_glyph_uv_rect(0, Vector2i(base_size, 1), idx, uv_rect);
+ font->set_glyph_texture_idx(0, Vector2i(base_size, 1), idx, texture_idx * (packed ? 4 : 1) + ch_off);
+ }
+ }
+ } break;
+ case 5: /* kerning */ {
+ int pair_count = block_size / 10;
+ for (int i = 0; i < pair_count; i++) {
+ Vector2i kpk;
+ kpk.x = f->get_32();
+ kpk.y = f->get_32();
+ font->set_kerning(0, base_size, kpk, Vector2((int16_t)f->get_16(), 0));
+ }
+ } break;
+ default: {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Invalid BMFont block type."));
+ } break;
+ }
+ f->seek(off + block_size);
+ block_type = f->get_8();
+ block_size = f->get_32();
+ }
+
+ } else {
+ // Text BMFont file.
+ f->seek(0);
+ while (true) {
+ String line = f->get_line();
+
+ int delimiter = line.find(" ");
+ String type = line.substr(0, delimiter);
+ int pos = delimiter + 1;
+ Map<String, String> keys;
+
+ while (pos < line.size() && line[pos] == ' ') {
+ pos++;
+ }
+
+ while (pos < line.size()) {
+ int eq = line.find("=", pos);
+ if (eq == -1) {
+ break;
+ }
+ String key = line.substr(pos, eq - pos);
+ int end = -1;
+ String value;
+ if (line[eq + 1] == '"') {
+ end = line.find("\"", eq + 2);
+ if (end == -1) {
+ break;
+ }
+ value = line.substr(eq + 2, end - 1 - eq - 1);
+ pos = end + 1;
+ } else {
+ end = line.find(" ", eq + 1);
+ if (end == -1) {
+ end = line.size();
+ }
+ value = line.substr(eq + 1, end - eq);
+ pos = end;
+ }
+
+ while (pos < line.size() && line[pos] == ' ') {
+ pos++;
+ }
+
+ keys[key] = value;
+ }
+
+ if (type == "info") {
+ if (keys.has("size")) {
+ base_size = keys["size"].to_int();
+ font->set_fixed_size(base_size);
+ }
+ if (keys.has("outline")) {
+ outline = keys["outline"].to_int();
+ }
+ ERR_FAIL_COND_V_MSG((!keys.has("unicode") || keys["unicode"].to_int() != 1), ERR_CANT_CREATE, TTR("Non-unicode version of BMFont is not supported."));
+ } else if (type == "common") {
+ if (keys.has("lineHeight")) {
+ height = keys["lineHeight"].to_int();
+ }
+ if (keys.has("base")) {
+ ascent = keys["base"].to_int();
+ }
+ if (keys.has("packed")) {
+ packed = (keys["packed"].to_int() == 1);
+ }
+ if (keys.has("alphaChnl")) {
+ ch[3] = keys["alphaChnl"].to_int();
+ }
+ if (keys.has("redChnl")) {
+ ch[0] = keys["redChnl"].to_int();
+ }
+ if (keys.has("greenChnl")) {
+ ch[1] = keys["greenChnl"].to_int();
+ }
+ if (keys.has("blueChnl")) {
+ ch[2] = keys["blueChnl"].to_int();
+ }
+ for (int i = 0; i < 4; i++) {
+ if (ch[i] == 0 && first_gl_ch == -1) {
+ first_gl_ch = i;
+ }
+ if (ch[i] == 1 && first_ol_ch == -1) {
+ first_ol_ch = i;
+ }
+ if (ch[i] == 2 && first_cm_ch == -1) {
+ first_cm_ch = i;
+ }
+ }
+ } else if (type == "page") {
+ int page = 0;
+ if (keys.has("id")) {
+ page = keys["id"].to_int();
+ }
+ if (keys.has("file")) {
+ String base_dir = p_source_file.get_base_dir();
+ String file = base_dir.plus_file(keys["file"]);
+ if (RenderingServer::get_singleton() != nullptr) {
+ Ref<Image> img;
+ img.instantiate();
+ Error err = ImageLoader::load_image(file, img);
+ ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_READ, TTR("Can't load font texture: ") + "\"" + file + "\".");
+ if (packed) {
+ if (ch[3] == 0) { // 4 x 8 bit monochrome, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_packed_8bit(font, img, page, base_size);
+ } else if ((ch[3] == 2) && (outline > 0)) { // 4 x 4 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_packed_4bit(font, img, page, base_size);
+ } else {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format."));
+ }
+ } else {
+ if ((ch[0] == 0) && (ch[1] == 0) && (ch[2] == 0) && (ch[3] == 0)) { // RGBA8 color, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ font->set_texture_image(0, Vector2i(base_size, 0), page, img);
+ } else if ((ch[0] == 2) && (ch[1] == 2) && (ch[2] == 2) && (ch[3] == 2) && (outline > 0)) { // RGBA4 color, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_rgba_4bit(font, img, page, base_size);
+ } else if ((first_gl_ch >= 0) && (first_ol_ch >= 0) && (outline > 0)) { // 1 x 8 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_mono_8bit(font, img, page, first_gl_ch, base_size, 0);
+ _convert_mono_8bit(font, img, page, first_ol_ch, base_size, 1);
+ } else if ((first_cm_ch >= 0) && (outline > 0)) { // 1 x 4 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_mono_4bit(font, img, page, first_cm_ch, base_size, 1);
+ } else if (first_gl_ch >= 0) { // 1 x 8 bit monochrome, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_mono_8bit(font, img, page, first_gl_ch, base_size, 0);
+ } else {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format."));
+ }
+ }
+ }
+ }
+ } else if (type == "char") {
+ char32_t idx = 0;
+ Vector2 advance;
+ Vector2 size;
+ Vector2 offset;
+ Rect2 uv_rect;
+ int texture_idx = -1;
+ uint8_t channel = 15;
+
+ if (keys.has("id")) {
+ idx = keys["id"].to_int();
+ }
+ if (keys.has("x")) {
+ uv_rect.position.x = keys["x"].to_int();
+ }
+ if (keys.has("y")) {
+ uv_rect.position.y = keys["y"].to_int();
+ }
+ if (keys.has("width")) {
+ uv_rect.size.width = keys["width"].to_int();
+ size.width = keys["width"].to_int();
+ }
+ if (keys.has("height")) {
+ uv_rect.size.height = keys["height"].to_int();
+ size.height = keys["height"].to_int();
+ }
+ if (keys.has("xoffset")) {
+ offset.x = keys["xoffset"].to_int();
+ }
+ if (keys.has("yoffset")) {
+ offset.y = keys["yoffset"].to_int() - ascent;
+ }
+ if (keys.has("page")) {
+ texture_idx = keys["page"].to_int();
+ }
+ if (keys.has("xadvance")) {
+ advance.x = keys["xadvance"].to_int();
+ }
+ if (advance.x < 0) {
+ advance.x = size.width + 1;
+ }
+ if (keys.has("chnl")) {
+ channel = keys["chnl"].to_int();
+ }
+
+ ERR_FAIL_COND_V_MSG(!packed && channel != 15, ERR_CANT_CREATE, TTR("Invalid glyph channel."));
+ int ch_off = 0;
+ switch (channel) {
+ case 1:
+ ch_off = 2;
+ break; // B
+ case 2:
+ ch_off = 1;
+ break; // G
+ case 4:
+ ch_off = 0;
+ break; // R
+ case 8:
+ ch_off = 3;
+ break; // A
+ default:
+ ch_off = 0;
+ break;
+ }
+ font->set_glyph_advance(0, base_size, idx, advance);
+ font->set_glyph_offset(0, Vector2i(base_size, 0), idx, offset);
+ font->set_glyph_size(0, Vector2i(base_size, 0), idx, size);
+ font->set_glyph_uv_rect(0, Vector2i(base_size, 0), idx, uv_rect);
+ font->set_glyph_texture_idx(0, Vector2i(base_size, 0), idx, texture_idx * (packed ? 4 : 1) + ch_off);
+ if (outline > 0) {
+ font->set_glyph_offset(0, Vector2i(base_size, 1), idx, offset);
+ font->set_glyph_size(0, Vector2i(base_size, 1), idx, size);
+ font->set_glyph_uv_rect(0, Vector2i(base_size, 1), idx, uv_rect);
+ font->set_glyph_texture_idx(0, Vector2i(base_size, 1), idx, texture_idx * (packed ? 4 : 1) + ch_off);
+ }
+ } else if (type == "kerning") {
+ Vector2i kpk;
+ if (keys.has("first")) {
+ kpk.x = keys["first"].to_int();
+ }
+ if (keys.has("second")) {
+ kpk.y = keys["second"].to_int();
+ }
+ if (keys.has("amount")) {
+ font->set_kerning(0, base_size, kpk, Vector2(keys["amount"].to_int(), 0));
+ }
+ }
+
+ if (f->eof_reached()) {
+ break;
+ }
+ }
+ }
+
+ font->set_ascent(0, base_size, ascent);
+ font->set_descent(0, base_size, height - ascent);
+
+ int flg = ResourceSaver::SaverFlags::FLAG_BUNDLE_RESOURCES | ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS;
+ if ((bool)p_options["compress"]) {
+ flg |= ResourceSaver::SaverFlags::FLAG_COMPRESS;
+ }
+
+ print_verbose("Saving to: " + p_save_path + ".fontdata");
+ Error err = ResourceSaver::save(p_save_path + ".fontdata", font, flg);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save font to file \"" + p_save_path + ".res\".");
+ print_verbose("Done saving to: " + p_save_path + ".fontdata");
+ return OK;
+}
+
+ResourceImporterBMFont::ResourceImporterBMFont() {
+}
diff --git a/editor/import/resource_importer_bmfont.h b/editor/import/resource_importer_bmfont.h
new file mode 100644
index 0000000000..065703132a
--- /dev/null
+++ b/editor/import/resource_importer_bmfont.h
@@ -0,0 +1,56 @@
+/*************************************************************************/
+/* resource_importer_bmfont.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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. */
+/*************************************************************************/
+
+#ifndef RESOURCE_IMPORTER_BMFONT_H
+#define RESOURCE_IMPORTER_BMFONT_H
+
+#include "core/io/resource_importer.h"
+#include "scene/resources/font.h"
+#include "servers/text_server.h"
+
+class ResourceImporterBMFont : public ResourceImporter {
+ GDCLASS(ResourceImporterBMFont, ResourceImporter);
+
+public:
+ virtual String get_importer_name() const override;
+ virtual String get_visible_name() const override;
+ virtual void get_recognized_extensions(List<String> *p_extensions) const override;
+ virtual String get_save_extension() const override;
+ virtual String get_resource_type() const override;
+
+ virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const override;
+ virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const override;
+
+ virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+
+ ResourceImporterBMFont();
+};
+
+#endif // RESOURCE_IMPORTER_BMFONT_H
diff --git a/editor/import/resource_importer_dynamicfont.cpp b/editor/import/resource_importer_dynamicfont.cpp
new file mode 100644
index 0000000000..8e01adbd56
--- /dev/null
+++ b/editor/import/resource_importer_dynamicfont.cpp
@@ -0,0 +1,304 @@
+/*************************************************************************/
+/* resource_importer_dynamicfont.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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. */
+/*************************************************************************/
+
+#include "resource_importer_dynamicfont.h"
+
+#include "dynamicfont_import_settings.h"
+
+#include "core/io/file_access.h"
+#include "core/io/resource_saver.h"
+#include "editor/editor_node.h"
+#include "modules/modules_enabled.gen.h"
+
+String ResourceImporterDynamicFont::get_importer_name() const {
+ return "font_data_dynamic";
+}
+
+String ResourceImporterDynamicFont::get_visible_name() const {
+ return "Font Data (Dynamic Font)";
+}
+
+void ResourceImporterDynamicFont::get_recognized_extensions(List<String> *p_extensions) const {
+ if (p_extensions) {
+#ifdef MODULE_FREETYPE_ENABLED
+ p_extensions->push_back("ttf");
+ p_extensions->push_back("otf");
+ p_extensions->push_back("woff");
+ //p_extensions->push_back("woff2");
+ p_extensions->push_back("pfb");
+ p_extensions->push_back("pfm");
+#endif
+ }
+}
+
+String ResourceImporterDynamicFont::get_save_extension() const {
+ return "fontdata";
+}
+
+String ResourceImporterDynamicFont::get_resource_type() const {
+ return "FontData";
+}
+
+bool ResourceImporterDynamicFont::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
+ if (p_option == "msdf_pixel_range" && !bool(p_options["multichannel_signed_distance_field"])) {
+ return false;
+ }
+ if (p_option == "msdf_size" && !bool(p_options["multichannel_signed_distance_field"])) {
+ return false;
+ }
+ if (p_option == "oversampling" && bool(p_options["multichannel_signed_distance_field"])) {
+ return false;
+ }
+ return true;
+}
+
+int ResourceImporterDynamicFont::get_preset_count() const {
+ return PRESET_MAX;
+}
+
+String ResourceImporterDynamicFont::get_preset_name(int p_idx) const {
+ switch (p_idx) {
+ case PRESET_DYNAMIC:
+ return TTR("Dynamically rendered TrueType/OpenType font");
+ case PRESET_MSDF:
+ return TTR("Prerendered multichannel(+true) signed distance field");
+ default:
+ return String();
+ }
+}
+
+void ResourceImporterDynamicFont::get_import_options(List<ImportOption> *r_options, int p_preset) const {
+ bool msdf = p_preset == PRESET_MSDF;
+
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "antialiased"), true));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), (msdf) ? true : false));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_RANGE, "1,100,1"), 8));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_RANGE, "1,250,1"), 48));
+
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force_autohinter"), false));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), 1));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), 0.0));
+
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress"), true));
+
+ r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "preload/char_ranges"), Vector<String>()));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "preload/glyph_ranges"), Vector<String>()));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "preload/configurations"), Vector<String>()));
+
+ r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "support_overrides/language_enabled"), Vector<String>()));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "support_overrides/language_disabled"), Vector<String>()));
+
+ r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "support_overrides/script_enabled"), Vector<String>()));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "support_overrides/script_disabled"), Vector<String>()));
+}
+
+bool ResourceImporterDynamicFont::_decode_variation(const String &p_token, Dictionary &r_variations, Vector2i &r_size, String &r_name, Vector2i &r_spacing) {
+ Vector<String> tokens = p_token.split("=");
+ if (tokens.size() == 2) {
+ if (tokens[0] == "name") {
+ r_name = tokens[1];
+ } else if (tokens[0] == "size") {
+ r_size.x = tokens[1].to_int();
+ } else if (tokens[0] == "outline_size") {
+ r_size.y = tokens[1].to_int();
+ } else if (tokens[0] == "spacing_space") {
+ r_spacing.x = tokens[1].to_int();
+ } else if (tokens[0] == "spacing_glyph") {
+ r_spacing.y = tokens[1].to_int();
+ } else {
+ r_variations[tokens[0]] = tokens[1].to_float();
+ }
+ return true;
+ } else {
+ WARN_PRINT("Invalid variation: '" + p_token + "'.");
+ return false;
+ }
+}
+
+bool ResourceImporterDynamicFont::_decode_range(const String &p_token, int32_t &r_pos) {
+ if (p_token.begins_with("U+") || p_token.begins_with("u+") || p_token.begins_with("0x")) {
+ // Unicode character hex index.
+ r_pos = p_token.substr(2).hex_to_int();
+ return true;
+ } else if (p_token.length() == 3 && p_token[0] == '\'' && p_token[2] == '\'') {
+ // Unicode character.
+ r_pos = p_token.unicode_at(1);
+ return true;
+ } else if (p_token.is_numeric()) {
+ // Unicode character decimal index.
+ r_pos = p_token.to_int();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool ResourceImporterDynamicFont::has_advanced_options() const {
+ return true;
+}
+void ResourceImporterDynamicFont::show_advanced_options(const String &p_path) {
+ DynamicFontImportSettings::get_singleton()->open_settings(p_path);
+}
+
+Error ResourceImporterDynamicFont::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
+ print_verbose("Importing dynamic font from: " + p_source_file);
+
+ bool antialiased = p_options["antialiased"];
+ bool msdf = p_options["multichannel_signed_distance_field"];
+ int px_range = p_options["msdf_pixel_range"];
+ int px_size = p_options["msdf_size"];
+
+ bool autohinter = p_options["force_autohinter"];
+ int hinting = p_options["hinting"];
+ real_t oversampling = p_options["oversampling"];
+
+ // Load base font data.
+ Vector<uint8_t> data = FileAccess::get_file_as_array(p_source_file);
+
+ // Create font.
+ Ref<FontData> font;
+ font.instantiate();
+ font->set_data(data);
+ font->set_antialiased(antialiased);
+ font->set_multichannel_signed_distance_field(msdf);
+ font->set_msdf_pixel_range(px_range);
+ font->set_msdf_size(px_size);
+ font->set_fixed_size(0);
+ font->set_force_autohinter(autohinter);
+ font->set_hinting((TextServer::Hinting)hinting);
+ font->set_oversampling(oversampling);
+
+ Vector<String> lang_en = p_options["support_overrides/language_enabled"];
+ for (int i = 0; i < lang_en.size(); i++) {
+ font->set_language_support_override(lang_en[i], true);
+ }
+
+ Vector<String> lang_dis = p_options["support_overrides/language_disabled"];
+ for (int i = 0; i < lang_dis.size(); i++) {
+ font->set_language_support_override(lang_dis[i], false);
+ }
+
+ Vector<String> scr_en = p_options["support_overrides/script_enabled"];
+ for (int i = 0; i < scr_en.size(); i++) {
+ font->set_script_support_override(scr_en[i], true);
+ }
+
+ Vector<String> scr_dis = p_options["support_overrides/script_disabled"];
+ for (int i = 0; i < scr_dis.size(); i++) {
+ font->set_script_support_override(scr_dis[i], false);
+ }
+
+ Vector<String> variations = p_options["preload/configurations"];
+ Vector<String> char_ranges = p_options["preload/char_ranges"];
+ Vector<String> gl_ranges = p_options["preload/glyph_ranges"];
+
+ for (int i = 0; i < variations.size(); i++) {
+ String name;
+ Dictionary var;
+ Vector2i size = Vector2(16, 0);
+ Vector2i spacing;
+
+ Vector<String> variation_tags = variations[i].split(",");
+ for (int j = 0; j < variation_tags.size(); j++) {
+ if (!_decode_variation(variation_tags[j], var, size, name, spacing)) {
+ WARN_PRINT(vformat(TTR("Invalid variation: \"%s\""), variations[i]));
+ continue;
+ }
+ }
+ RID conf = font->find_cache(var);
+
+ for (int j = 0; j < char_ranges.size(); j++) {
+ int32_t start, end;
+ Vector<String> tokens = char_ranges[j].split("-");
+ if (tokens.size() == 2) {
+ if (!_decode_range(tokens[0], start) || !_decode_range(tokens[1], end)) {
+ WARN_PRINT(vformat(TTR("Invalid range: \"%s\""), char_ranges[j]));
+ continue;
+ }
+ } else if (tokens.size() == 1) {
+ if (!_decode_range(tokens[0], start)) {
+ WARN_PRINT(vformat(TTR("Invalid range: \"%s\""), char_ranges[j]));
+ continue;
+ }
+ end = start;
+ } else {
+ WARN_PRINT(vformat(TTR("Invalid range: \"%s\""), char_ranges[j]));
+ continue;
+ }
+
+ // Preload character ranges for each variations / sizes.
+ print_verbose(vformat(TTR("Pre-rendering range U+%s...%s from configuration \"%s\" (%d / %d)..."), String::num_int64(start, 16), String::num_int64(end, 16), name, i + 1, variations.size()));
+ TS->font_render_range(conf, size, start, end);
+ }
+
+ for (int j = 0; j < gl_ranges.size(); j++) {
+ int32_t start, end;
+ Vector<String> tokens = gl_ranges[j].split("-");
+ if (tokens.size() == 2) {
+ if (!_decode_range(tokens[0], start) || !_decode_range(tokens[1], end)) {
+ WARN_PRINT(vformat(TTR("Invalid range: \"%s\""), gl_ranges[j]));
+ continue;
+ }
+ } else if (tokens.size() == 1) {
+ if (!_decode_range(tokens[0], start)) {
+ WARN_PRINT(vformat(TTR("Invalid range: \"%s\""), gl_ranges[j]));
+ continue;
+ }
+ end = start;
+ } else {
+ WARN_PRINT(vformat(TTR("Invalid range: \"%s\""), gl_ranges[j]));
+ continue;
+ }
+
+ // Preload glyph range for each variations / sizes.
+ print_verbose(vformat(TTR("Pre-rendering glyph range 0x%s...%s from configuration \"%s\" (%d / %d)..."), String::num_int64(start, 16), String::num_int64(end, 16), name, i + 1, variations.size()));
+ for (int32_t k = start; k <= end; k++) {
+ TS->font_render_glyph(conf, size, k);
+ }
+ }
+
+ TS->font_set_spacing(conf, size.x, TextServer::SPACING_SPACE, spacing.x);
+ TS->font_set_spacing(conf, size.x, TextServer::SPACING_GLYPH, spacing.y);
+ }
+
+ int flg = ResourceSaver::SaverFlags::FLAG_BUNDLE_RESOURCES | ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS;
+ if ((bool)p_options["compress"]) {
+ flg |= ResourceSaver::SaverFlags::FLAG_COMPRESS;
+ }
+
+ print_verbose("Saving to: " + p_save_path + ".fontdata");
+ Error err = ResourceSaver::save(p_save_path + ".fontdata", font, flg);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save font to file \"" + p_save_path + ".res\".");
+ print_verbose("Done saving to: " + p_save_path + ".fontdata");
+ return OK;
+}
+
+ResourceImporterDynamicFont::ResourceImporterDynamicFont() {
+}
diff --git a/editor/import/resource_importer_dynamicfont.h b/editor/import/resource_importer_dynamicfont.h
new file mode 100644
index 0000000000..52f256ab96
--- /dev/null
+++ b/editor/import/resource_importer_dynamicfont.h
@@ -0,0 +1,71 @@
+/*************************************************************************/
+/* resource_importer_dynamicfont.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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. */
+/*************************************************************************/
+
+#ifndef RESOURCE_IMPORTER_FONT_DATA_H
+#define RESOURCE_IMPORTER_FONT_DATA_H
+
+#include "core/io/resource_importer.h"
+#include "scene/resources/font.h"
+#include "servers/text_server.h"
+
+class ResourceImporterDynamicFont : public ResourceImporter {
+ GDCLASS(ResourceImporterDynamicFont, ResourceImporter);
+
+ enum Presets {
+ PRESET_DYNAMIC,
+ PRESET_MSDF,
+ PRESET_MAX
+ };
+
+public:
+ static bool _decode_range(const String &p_token, int32_t &r_pos);
+ static bool _decode_variation(const String &p_token, Dictionary &r_variations, Vector2i &r_size, String &r_name, Vector2i &r_spacing);
+
+ virtual String get_importer_name() const override;
+ virtual String get_visible_name() const override;
+ virtual void get_recognized_extensions(List<String> *p_extensions) const override;
+ virtual String get_save_extension() const override;
+ virtual String get_resource_type() const override;
+
+ virtual int get_preset_count() const override;
+ virtual String get_preset_name(int p_idx) const override;
+
+ virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const override;
+ virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const override;
+
+ bool has_advanced_options() const override;
+ void show_advanced_options(const String &p_path) override;
+
+ virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+
+ ResourceImporterDynamicFont();
+};
+
+#endif // RESOURCE_IMPORTER_FONTDATA_H
diff --git a/editor/import/resource_importer_imagefont.cpp b/editor/import/resource_importer_imagefont.cpp
new file mode 100644
index 0000000000..997280d1dd
--- /dev/null
+++ b/editor/import/resource_importer_imagefont.cpp
@@ -0,0 +1,162 @@
+/*************************************************************************/
+/* resource_importer_imagefont.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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. */
+/*************************************************************************/
+
+#include "resource_importer_imagefont.h"
+
+#include "core/io/image_loader.h"
+#include "core/io/resource_saver.h"
+
+String ResourceImporterImageFont::get_importer_name() const {
+ return "font_data_image";
+}
+
+String ResourceImporterImageFont::get_visible_name() const {
+ return "Font Data (Monospace Image Font)";
+}
+
+void ResourceImporterImageFont::get_recognized_extensions(List<String> *p_extensions) const {
+ if (p_extensions) {
+ ImageLoader::get_recognized_extensions(p_extensions);
+ }
+}
+
+String ResourceImporterImageFont::get_save_extension() const {
+ return "fontdata";
+}
+
+String ResourceImporterImageFont::get_resource_type() const {
+ return "FontData";
+}
+
+bool ResourceImporterImageFont::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
+ return true;
+}
+
+void ResourceImporterImageFont::get_import_options(List<ImportOption> *r_options, int p_preset) const {
+ r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "character_ranges"), Vector<String>()));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "columns"), 1));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "rows"), 1));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "font_size"), 14));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress"), true));
+}
+
+bool ResourceImporterImageFont::_decode_range(const String &p_token, int32_t &r_pos) {
+ if (p_token.begins_with("U+") || p_token.begins_with("u+") || p_token.begins_with("0x")) {
+ // Unicode character hex index.
+ r_pos = p_token.substr(2).hex_to_int();
+ return true;
+ } else if (p_token.length() == 3 && p_token[0] == '\'' && p_token[2] == '\'') {
+ // Unicode character.
+ r_pos = p_token.unicode_at(1);
+ return true;
+ } else if (p_token.is_numeric()) {
+ // Unicode character decimal index.
+ r_pos = p_token.to_int();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+Error ResourceImporterImageFont::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
+ print_verbose("Importing image font from: " + p_source_file);
+
+ int columns = p_options["columns"];
+ int rows = p_options["rows"];
+ int base_size = p_options["font_size"];
+ Vector<String> ranges = p_options["character_ranges"];
+
+ Ref<FontData> font;
+ font.instantiate();
+ font->set_antialiased(false);
+ font->set_multichannel_signed_distance_field(false);
+ font->set_fixed_size(base_size);
+ font->set_force_autohinter(false);
+ font->set_hinting(TextServer::HINTING_NONE);
+ font->set_oversampling(1.0f);
+
+ Ref<Image> img;
+ img.instantiate();
+ Error err = ImageLoader::load_image(p_source_file, img);
+ ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_READ, TTR("Can't load font texture: ") + "\"" + p_source_file + "\".");
+ font->set_texture_image(0, Vector2i(base_size, 0), 0, img);
+
+ int count = columns * rows;
+ int chr_width = img->get_width() / columns;
+ int chr_height = img->get_height() / rows;
+ int pos = 0;
+
+ for (int i = 0; i < ranges.size(); i++) {
+ int32_t start, end;
+ Vector<String> tokens = ranges[i].split("-");
+ if (tokens.size() == 2) {
+ if (!_decode_range(tokens[0], start) || !_decode_range(tokens[1], end)) {
+ WARN_PRINT("Invalid range: \"" + ranges[i] + "\"");
+ continue;
+ }
+ } else if (tokens.size() == 1) {
+ if (!_decode_range(tokens[0], start)) {
+ WARN_PRINT("Invalid range: \"" + ranges[i] + "\"");
+ continue;
+ }
+ end = start;
+ } else {
+ WARN_PRINT("Invalid range: \"" + ranges[i] + "\"");
+ continue;
+ }
+ for (int32_t idx = start; idx <= end; idx++) {
+ int x = pos % columns;
+ int y = pos / columns;
+ font->set_glyph_advance(0, base_size, idx, Vector2(chr_width, 0));
+ font->set_glyph_offset(0, Vector2i(base_size, 0), idx, Vector2(0, -0.5 * chr_height));
+ font->set_glyph_size(0, Vector2i(base_size, 0), idx, Vector2(chr_width, chr_height));
+ font->set_glyph_uv_rect(0, Vector2i(base_size, 0), idx, Rect2(chr_width * x, chr_height * y, chr_width, chr_height));
+ font->set_glyph_texture_idx(0, Vector2i(base_size, 0), idx, 0);
+ pos++;
+ ERR_FAIL_COND_V_MSG(pos >= count, ERR_CANT_CREATE, "Too many characters in range.");
+ }
+ }
+ font->set_ascent(0, base_size, 0.5 * chr_height);
+ font->set_descent(0, base_size, 0.5 * chr_height);
+
+ int flg = ResourceSaver::SaverFlags::FLAG_BUNDLE_RESOURCES | ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS;
+ if ((bool)p_options["compress"]) {
+ flg |= ResourceSaver::SaverFlags::FLAG_COMPRESS;
+ }
+
+ print_verbose("Saving to: " + p_save_path + ".fontdata");
+ err = ResourceSaver::save(p_save_path + ".fontdata", font, flg);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save font to file \"" + p_save_path + ".res\".");
+ print_verbose("Done saving to: " + p_save_path + ".fontdata");
+ return OK;
+}
+
+ResourceImporterImageFont::ResourceImporterImageFont() {
+}
diff --git a/editor/import/resource_importer_imagefont.h b/editor/import/resource_importer_imagefont.h
new file mode 100644
index 0000000000..9b2b38596f
--- /dev/null
+++ b/editor/import/resource_importer_imagefont.h
@@ -0,0 +1,58 @@
+/*************************************************************************/
+/* resource_importer_imagefont.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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. */
+/*************************************************************************/
+
+#ifndef RESOURCE_IMPORTER_IMAGE_FONT_H
+#define RESOURCE_IMPORTER_IMAGE_FONT_H
+
+#include "core/io/resource_importer.h"
+#include "scene/resources/font.h"
+#include "servers/text_server.h"
+
+class ResourceImporterImageFont : public ResourceImporter {
+ GDCLASS(ResourceImporterImageFont, ResourceImporter);
+
+public:
+ static bool _decode_range(const String &p_token, int32_t &r_pos);
+
+ virtual String get_importer_name() const override;
+ virtual String get_visible_name() const override;
+ virtual void get_recognized_extensions(List<String> *p_extensions) const override;
+ virtual String get_save_extension() const override;
+ virtual String get_resource_type() const override;
+
+ virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const override;
+ virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const override;
+
+ virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+
+ ResourceImporterImageFont();
+};
+
+#endif // RESOURCE_IMPORTER_IMAGE_FONT_H
diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp
index 95f68d5f7f..415832ab3b 100644
--- a/editor/plugins/editor_preview_plugins.cpp
+++ b/editor/plugins/editor_preview_plugins.cpp
@@ -826,55 +826,6 @@ bool EditorFontPreviewPlugin::handles(const String &p_type) const {
return ClassDB::is_parent_class(p_type, "FontData") || ClassDB::is_parent_class(p_type, "Font");
}
-struct FSample {
- String script;
- String sample;
-};
-
-static FSample _samples[] = {
- { "hani", U"漢字" },
- { "armn", U"Աբ" },
- { "copt", U"Αα" },
- { "cyrl", U"Аб" },
- { "grek", U"Αα" },
- { "hebr", U"אב" },
- { "arab", U"اب" },
- { "syrc", U"ܐܒ" },
- { "thaa", U"ހށ" },
- { "deva", U"आ" },
- { "beng", U"আ" },
- { "guru", U"ਆ" },
- { "gujr", U"આ" },
- { "orya", U"ଆ" },
- { "taml", U"ஆ" },
- { "telu", U"ఆ" },
- { "knda", U"ಆ" },
- { "mylm", U"ആ" },
- { "sinh", U"ආ" },
- { "thai", U"กิ" },
- { "laoo", U"ກິ" },
- { "tibt", U"ༀ" },
- { "mymr", U"က" },
- { "geor", U"Ⴀა" },
- { "hang", U"한글" },
- { "ethi", U"ሀ" },
- { "cher", U"Ꭳ" },
- { "cans", U"ᐁ" },
- { "ogam", U"ᚁ" },
- { "runr", U"ᚠ" },
- { "tglg", U"ᜀ" },
- { "hano", U"ᜠ" },
- { "buhd", U"ᝀ" },
- { "tagb", U"ᝠ" },
- { "khmr", U"ក" },
- { "mong", U"ᠠ" },
- { "limb", U"ᤁ" },
- { "tale", U"ᥐ" },
- { "latn", U"Ab" },
- { "zyyy", U"😀" },
- { "", U"" }
-};
-
Ref<Texture2D> EditorFontPreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size) const {
RES res = ResourceLoader::load(p_path);
Ref<Font> sampled_font;
@@ -886,15 +837,15 @@ Ref<Texture2D> EditorFontPreviewPlugin::generate_from_path(const String &p_path,
}
String sample;
- for (int j = 0; j < sampled_font->get_data_count(); j++) {
- for (int i = 0; _samples[i].script != String(); i++) {
- if (sampled_font->get_data(j)->is_script_supported(_samples[i].script)) {
- if (sampled_font->get_data(j)->has_char(_samples[i].sample[0])) {
- sample += _samples[i].sample;
- }
- }
+ static const String sample_base = U"12漢字ԱբΑαАбΑαאבابܐܒހށआআਆઆଆஆఆಆആආกิກິༀကႠა한글ሀᎣᐁᚁᚠᜀᜠᝀᝠកᠠᤁᥐAb😀";
+ for (int i = 0; i < sample_base.length(); i++) {
+ if (sampled_font->has_char(sample_base[i])) {
+ sample += sample_base[i];
}
}
+ if (sample.is_empty()) {
+ sample = sampled_font->get_supported_chars().substr(0, 6);
+ }
Vector2 size = sampled_font->get_string_size(sample, 50);
Vector2 pos;
diff --git a/editor/plugins/font_editor_plugin.cpp b/editor/plugins/font_editor_plugin.cpp
index 22c9cc9ab1..52fb5b69ea 100644
--- a/editor/plugins/font_editor_plugin.cpp
+++ b/editor/plugins/font_editor_plugin.cpp
@@ -50,70 +50,24 @@ Size2 FontDataPreview::get_minimum_size() const {
return Vector2(64, 64) * EDSCALE;
}
-struct FSample {
- String script;
- String sample;
-};
-
-static FSample _samples[] = {
- { "hani", U"漢字" },
- { "armn", U"Աբ" },
- { "copt", U"Αα" },
- { "cyrl", U"Аб" },
- { "grek", U"Αα" },
- { "hebr", U"אב" },
- { "arab", U"اب" },
- { "syrc", U"ܐܒ" },
- { "thaa", U"ހށ" },
- { "deva", U"आ" },
- { "beng", U"আ" },
- { "guru", U"ਆ" },
- { "gujr", U"આ" },
- { "orya", U"ଆ" },
- { "taml", U"ஆ" },
- { "telu", U"ఆ" },
- { "knda", U"ಆ" },
- { "mylm", U"ആ" },
- { "sinh", U"ආ" },
- { "thai", U"กิ" },
- { "laoo", U"ກິ" },
- { "tibt", U"ༀ" },
- { "mymr", U"က" },
- { "geor", U"Ⴀა" },
- { "hang", U"한글" },
- { "ethi", U"ሀ" },
- { "cher", U"Ꭳ" },
- { "cans", U"ᐁ" },
- { "ogam", U"ᚁ" },
- { "runr", U"ᚠ" },
- { "tglg", U"ᜀ" },
- { "hano", U"ᜠ" },
- { "buhd", U"ᝀ" },
- { "tagb", U"ᝠ" },
- { "khmr", U"ក" },
- { "mong", U"ᠠ" },
- { "limb", U"ᤁ" },
- { "tale", U"ᥐ" },
- { "latn", U"Ab" },
- { "zyyy", U"😀" },
- { "", U"" }
-};
-
void FontDataPreview::set_data(const Ref<FontData> &p_data) {
Ref<Font> f = memnew(Font);
f->add_data(p_data);
line->clear();
-
- String sample;
- for (int i = 0; _samples[i].script != String(); i++) {
- if (p_data->is_script_supported(_samples[i].script)) {
- if (p_data->has_char(_samples[i].sample[0])) {
- sample += _samples[i].sample;
+ if (p_data.is_valid()) {
+ String sample;
+ static const String sample_base = U"12漢字ԱբΑαАбΑαאבابܐܒހށआআਆઆଆஆఆಆആආกิກິༀကႠა한글ሀᎣᐁᚁᚠᜀᜠᝀᝠកᠠᤁᥐAb😀";
+ for (int i = 0; i < sample_base.length(); i++) {
+ if (p_data->has_char(sample_base[i])) {
+ sample += sample_base[i];
}
}
+ if (sample.is_empty()) {
+ sample = p_data->get_supported_chars().substr(0, 6);
+ }
+ line->add_string(sample, f, 72);
}
- line->add_string(sample, f, 72);
update();
}
@@ -124,159 +78,6 @@ FontDataPreview::FontDataPreview() {
/*************************************************************************/
-void FontDataEditor::_notification(int p_what) {
- if (p_what == NOTIFICATION_SORT_CHILDREN) {
- int split_width = get_name_split_ratio() * get_size().width;
- button->set_size(Size2(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))->get_width(), get_size().height));
- if (is_layout_rtl()) {
- if (le != nullptr) {
- fit_child_in_rect(le, Rect2(Vector2(split_width, 0), Size2(split_width, get_size().height)));
- }
- fit_child_in_rect(chk, Rect2(Vector2(split_width - chk->get_size().x, 0), Size2(chk->get_size().x, get_size().height)));
- fit_child_in_rect(button, Rect2(Vector2(0, 0), Size2(button->get_size().width, get_size().height)));
- } else {
- if (le != nullptr) {
- fit_child_in_rect(le, Rect2(Vector2(0, 0), Size2(split_width, get_size().height)));
- }
- fit_child_in_rect(chk, Rect2(Vector2(split_width, 0), Size2(chk->get_size().x, get_size().height)));
- fit_child_in_rect(button, Rect2(Vector2(get_size().width - button->get_size().width, 0), Size2(button->get_size().width, get_size().height)));
- }
- update();
- }
- if (p_what == NOTIFICATION_DRAW) {
- int split_width = get_name_split_ratio() * get_size().width;
- Color dark_color = get_theme_color(SNAME("dark_color_2"), SNAME("Editor"));
- if (is_layout_rtl()) {
- draw_rect(Rect2(Vector2(0, 0), Size2(split_width, get_size().height)), dark_color);
- } else {
- draw_rect(Rect2(Vector2(split_width, 0), Size2(split_width, get_size().height)), dark_color);
- }
- }
- if (p_what == NOTIFICATION_THEME_CHANGED) {
- if (le != nullptr) {
- button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
- } else {
- button->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
- }
- queue_sort();
- }
- if (p_what == NOTIFICATION_RESIZED) {
- queue_sort();
- }
-}
-
-void FontDataEditor::update_property() {
- if (le == nullptr) {
- bool c = get_edited_object()->get(get_edited_property());
- chk->set_pressed(c);
- chk->set_disabled(is_read_only());
- }
-}
-
-Size2 FontDataEditor::get_minimum_size() const {
- return Size2(0, 60);
-}
-
-void FontDataEditor::_bind_methods() {
-}
-
-void FontDataEditor::init_lang_add() {
- le = memnew(LineEdit);
- le->set_placeholder("Language code");
- le->set_custom_minimum_size(Size2(get_size().width / 2, 0));
- le->set_editable(true);
- add_child(le);
-
- button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
- button->connect("pressed", callable_mp(this, &FontDataEditor::add_lang));
-}
-
-void FontDataEditor::init_lang_edit() {
- button->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
- button->connect("pressed", callable_mp(this, &FontDataEditor::remove_lang));
- chk->connect("toggled", callable_mp(this, &FontDataEditor::toggle_lang));
-}
-
-void FontDataEditor::init_script_add() {
- le = memnew(LineEdit);
- le->set_placeholder("Script code");
- le->set_custom_minimum_size(Size2(get_size().width / 2, 0));
- le->set_editable(true);
- add_child(le);
-
- button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
- button->connect("pressed", callable_mp(this, &FontDataEditor::add_script));
-}
-
-void FontDataEditor::init_script_edit() {
- button->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
- button->connect("pressed", callable_mp(this, &FontDataEditor::remove_script));
- chk->connect("toggled", callable_mp(this, &FontDataEditor::toggle_script));
-}
-
-void FontDataEditor::add_lang() {
- FontData *fd = Object::cast_to<FontData>(get_edited_object());
- if (fd != nullptr && !le->get_text().is_empty()) {
- fd->set_language_support_override(le->get_text(), chk->is_pressed());
- le->set_text("");
- chk->set_pressed(false);
- }
-}
-
-void FontDataEditor::add_script() {
- FontData *fd = Object::cast_to<FontData>(get_edited_object());
- if (fd != nullptr && le->get_text().length() == 4) {
- fd->set_script_support_override(le->get_text(), chk->is_pressed());
- le->set_text("");
- chk->set_pressed(false);
- }
-}
-
-void FontDataEditor::toggle_lang(bool p_pressed) {
- FontData *fd = Object::cast_to<FontData>(get_edited_object());
- if (fd != nullptr) {
- String lang = String(get_edited_property()).replace("language_support_override/", "");
- fd->set_language_support_override(lang, p_pressed);
- }
-}
-
-void FontDataEditor::toggle_script(bool p_pressed) {
- FontData *fd = Object::cast_to<FontData>(get_edited_object());
- if (fd != nullptr) {
- String script = String(get_edited_property()).replace("script_support_override/", "");
- fd->set_script_support_override(script, p_pressed);
- }
-}
-
-void FontDataEditor::remove_lang() {
- FontData *fd = Object::cast_to<FontData>(get_edited_object());
- if (fd != nullptr) {
- String lang = String(get_edited_property()).replace("language_support_override/", "");
- fd->remove_language_support_override(lang);
- }
-}
-
-void FontDataEditor::remove_script() {
- FontData *fd = Object::cast_to<FontData>(get_edited_object());
- if (fd != nullptr) {
- String script = String(get_edited_property()).replace("script_support_override/", "");
- fd->remove_script_support_override(script);
- }
-}
-
-FontDataEditor::FontDataEditor() {
- chk = memnew(CheckBox);
- chk->set_text(TTR("On"));
- chk->set_flat(true);
- add_child(chk);
-
- button = memnew(Button);
- button->set_flat(true);
- add_child(button);
-}
-
-/*************************************************************************/
-
bool EditorInspectorPluginFont::can_handle(Object *p_object) {
return Object::cast_to<FontData>(p_object) != nullptr;
}
@@ -291,34 +92,6 @@ void EditorInspectorPluginFont::parse_begin(Object *p_object) {
}
bool EditorInspectorPluginFont::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, const bool p_wide) {
- if (p_path.begins_with("language_support_override/") && p_object->is_class("FontData")) {
- String lang = p_path.replace("language_support_override/", "");
-
- FontDataEditor *editor = memnew(FontDataEditor);
- if (lang != "_new") {
- editor->init_lang_edit();
- } else {
- editor->init_lang_add();
- }
- add_property_editor(p_path, editor);
-
- return true;
- }
-
- if (p_path.begins_with("script_support_override/") && p_object->is_class("FontData")) {
- String script = p_path.replace("script_support_override/", "");
-
- FontDataEditor *editor = memnew(FontDataEditor);
- if (script != "_new") {
- editor->init_script_edit();
- } else {
- editor->init_script_add();
- }
- add_property_editor(p_path, editor);
-
- return true;
- }
-
return false;
}
diff --git a/editor/plugins/font_editor_plugin.h b/editor/plugins/font_editor_plugin.h
index 71464003a0..3530815872 100644
--- a/editor/plugins/font_editor_plugin.h
+++ b/editor/plugins/font_editor_plugin.h
@@ -55,39 +55,6 @@ public:
/*************************************************************************/
-class FontDataEditor : public EditorProperty {
- GDCLASS(FontDataEditor, EditorProperty);
-
- LineEdit *le = nullptr;
- CheckBox *chk = nullptr;
- Button *button = nullptr;
-
- void toggle_lang(bool p_pressed);
- void toggle_script(bool p_pressed);
- void add_lang();
- void add_script();
- void remove_lang();
- void remove_script();
-
-protected:
- void _notification(int p_what);
-
- static void _bind_methods();
-
-public:
- virtual Size2 get_minimum_size() const override;
- virtual void update_property() override;
-
- void init_lang_add();
- void init_lang_edit();
- void init_script_add();
- void init_script_edit();
-
- FontDataEditor();
-};
-
-/*************************************************************************/
-
class EditorInspectorPluginFont : public EditorInspectorPlugin {
GDCLASS(EditorInspectorPluginFont, EditorInspectorPlugin);
diff --git a/methods.py b/methods.py
index 86a802bf5f..50b413a0e6 100644
--- a/methods.py
+++ b/methods.py
@@ -319,7 +319,7 @@ def disable_module(self):
self.disabled_modules.append(self.current_module)
-def module_check_dependencies(self, module, dependencies):
+def module_check_dependencies(self, module, dependencies, silent=False):
"""
Checks if module dependencies are enabled for a given module,
and prints a warning if they aren't.
@@ -333,11 +333,12 @@ def module_check_dependencies(self, module, dependencies):
missing_deps.append(dep)
if missing_deps != []:
- print(
- "Disabling '{}' module as the following dependencies are not satisfied: {}".format(
- module, ", ".join(missing_deps)
+ if not silent:
+ print(
+ "Disabling '{}' module as the following dependencies are not satisfied: {}".format(
+ module, ", ".join(missing_deps)
+ )
)
- )
return False
else:
return True
diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json
index e02e275359..66d2dc267d 100644
--- a/modules/gdnative/gdnative_api.json
+++ b/modules/gdnative/gdnative_api.json
@@ -5354,7 +5354,7 @@
},
{
"name": "godot_glyph_get_advance",
- "return_type": "godot_float",
+ "return_type": "godot_real_t",
"arguments": [
[
"const godot_glyph *",
@@ -5371,7 +5371,7 @@
"p_self"
],
[
- "godot_float",
+ "godot_real_t",
"p_advance"
]
]
diff --git a/modules/gdnative/include/text/godot_text.h b/modules/gdnative/include/text/godot_text.h
index 6428f2f149..940cfd11f8 100644
--- a/modules/gdnative/include/text/godot_text.h
+++ b/modules/gdnative/include/text/godot_text.h
@@ -60,68 +60,109 @@ typedef struct {
typedef struct {
godot_gdnative_api_version version;
+
void *(*constructor)(godot_object *);
void (*destructor)(void *);
+
godot_string (*get_name)(const void *);
godot_bool (*has_feature)(const void *, godot_int);
+
+ void (*free)(void *, godot_rid *);
+ bool (*has)(void *, godot_rid *);
+
bool (*load_support_data)(void *, const godot_string *);
godot_string (*get_support_data_filename)(const void *);
godot_string (*get_support_data_info)(const void *);
bool (*save_support_data)(void *, const godot_string *);
+
bool (*is_locale_right_to_left)(void *, const godot_string *);
- void (*free)(void *, godot_rid *);
- bool (*has)(void *, godot_rid *);
- godot_rid (*create_font_system)(void *, const godot_string *, int);
- godot_rid (*create_font_resource)(void *, const godot_string *, int);
- godot_rid (*create_font_memory)(void *, const uint8_t *, size_t, godot_string *, int);
- godot_rid (*create_font_bitmap)(void *, float, float, int);
- void (*font_bitmap_add_texture)(void *, godot_rid *, const godot_object *);
- void (*font_bitmap_add_char)(void *, godot_rid *, char32_t, int, const godot_rect2 *, const godot_vector2 *, float);
- void (*font_bitmap_add_kerning_pair)(void *, godot_rid *, char32_t, char32_t, int);
- float (*font_get_height)(void *, godot_rid *, int);
- float (*font_get_ascent)(void *, godot_rid *, int);
- float (*font_get_descent)(void *, godot_rid *, int);
- float (*font_get_underline_position)(void *, godot_rid *, int);
- float (*font_get_underline_thickness)(void *, godot_rid *, int);
- int (*font_get_spacing_space)(void *, godot_rid *);
- void (*font_set_spacing_space)(void *, godot_rid *, int);
- int (*font_get_spacing_glyph)(void *, godot_rid *);
- void (*font_set_spacing_glyph)(void *, godot_rid *, int);
+ int32_t (*name_to_tag)(const void *, const godot_string *);
+ godot_string (*tag_to_name)(const void *, int32_t);
+
+ godot_rid (*create_font)(void *);
+ void (*font_set_data)(void *, godot_rid *, const godot_packed_byte_array *);
+ void (*font_set_data_ptr)(void *, godot_rid *, const uint8_t *, size_t);
void (*font_set_antialiased)(void *, godot_rid *, bool);
- bool (*font_get_antialiased)(void *, godot_rid *);
- godot_dictionary (*font_get_feature_list)(void *, godot_rid *);
- godot_dictionary (*font_get_variation_list)(void *, godot_rid *);
- void (*font_set_variation)(void *, godot_rid *, const godot_string *, double);
- double (*font_get_variation)(void *, godot_rid *, const godot_string *);
- void (*font_set_distance_field_hint)(void *, godot_rid *, bool);
- bool (*font_get_distance_field_hint)(void *, godot_rid *);
- void (*font_set_hinting)(void *, godot_rid *, godot_int);
- godot_int (*font_get_hinting)(void *, godot_rid *);
+ bool (*font_is_antialiased)(const void *, godot_rid *);
+ void (*font_set_multichannel_signed_distance_field)(void *, godot_rid *, bool);
+ bool (*font_is_multichannel_signed_distance_field)(const void *, godot_rid *);
+ void (*font_set_msdf_pixel_range)(void *, godot_rid *, godot_int);
+ godot_int (*font_get_msdf_pixel_range)(const void *, godot_rid *);
+ void (*font_set_msdf_size)(void *, godot_rid *, godot_int);
+ godot_int (*font_get_msdf_size)(const void *, godot_rid *);
+ void (*font_set_fixed_size)(void *, godot_rid *, godot_int);
+ godot_int (*font_get_fixed_size)(const void *, godot_rid *);
void (*font_set_force_autohinter)(void *, godot_rid *, bool);
- bool (*font_get_force_autohinter)(void *, godot_rid *);
- bool (*font_has_char)(void *, godot_rid *, char32_t);
- godot_string (*font_get_supported_chars)(void *, godot_rid *);
- bool (*font_has_outline)(void *, godot_rid *);
- int (*font_get_base_size)(void *, godot_rid *);
- bool (*font_is_language_supported)(void *, godot_rid *, const godot_string *);
+ bool (*font_is_force_autohinter)(const void *, godot_rid *);
+ void (*font_set_hinting)(void *, godot_rid *, godot_int);
+ godot_int (*font_get_hinting)(const void *, godot_rid *);
+ void (*font_set_variation_coordinates)(void *, godot_rid *, const godot_dictionary *);
+ godot_dictionary (*font_get_variation_coordinates)(const void *, godot_rid *);
+ void (*font_set_oversampling)(void *, godot_rid *, godot_real_t);
+ godot_real_t (*font_get_oversampling)(const void *, godot_rid *);
+ godot_array (*font_get_size_cache_list)(const void *, godot_rid *);
+ void (*font_clear_size_cache)(void *, godot_rid *);
+ void (*font_remove_size_cache)(void *, godot_rid *, const godot_vector2i *);
+ void (*font_set_ascent)(void *, godot_rid *, godot_int, godot_real_t);
+ godot_real_t (*font_get_ascent)(const void *, godot_rid *, godot_int);
+ void (*font_set_descent)(void *, godot_rid *, godot_int, godot_real_t);
+ godot_real_t (*font_get_descent)(const void *, godot_rid *, godot_int);
+ void (*font_set_underline_position)(void *, godot_rid *, godot_int, godot_real_t);
+ godot_real_t (*font_get_underline_position)(const void *, godot_rid *, godot_int);
+ void (*font_set_underline_thickness)(void *, godot_rid *, godot_int, godot_real_t);
+ godot_real_t (*font_get_underline_thickness)(const void *, godot_rid *, godot_int);
+ void (*font_set_scale)(void *, godot_rid *, godot_int, godot_real_t);
+ godot_real_t (*font_get_scale)(const void *, godot_rid *, godot_int);
+ void (*font_set_spacing)(void *, godot_rid *, godot_int, godot_int, godot_int);
+ godot_int (*font_get_spacing)(const void *, godot_rid *, godot_int, godot_int);
+ godot_int (*font_get_texture_count)(const void *, godot_rid *, const godot_vector2i *);
+ void (*font_clear_textures)(void *, godot_rid *, const godot_vector2i *);
+ void (*font_remove_texture)(void *, godot_rid *, const godot_vector2i *, godot_int);
+ void (*font_set_texture_image)(void *, godot_rid *, const godot_vector2i *, godot_int, const godot_object *);
+ godot_object *(*font_get_texture_image)(const void *, godot_rid *, const godot_vector2i *, godot_int);
+ void (*font_set_texture_offsets)(void *, godot_rid *, const godot_vector2i *, godot_int, const godot_packed_int32_array *);
+ godot_packed_int32_array (*font_get_texture_offsets)(const void *, godot_rid *, const godot_vector2i *, godot_int);
+ godot_array (*font_get_glyph_list)(const void *, godot_rid *, const godot_vector2i *);
+ void (*font_clear_glyphs)(void *, godot_rid *, const godot_vector2i *);
+ void (*font_remove_glyph)(void *, godot_rid *, const godot_vector2i *, int32_t);
+ godot_vector2 (*font_get_glyph_advance)(const void *, godot_rid *, godot_int, int32_t);
+ void (*font_set_glyph_advance)(void *, godot_rid *, godot_int, int32_t, const godot_vector2 *);
+ godot_vector2 (*font_get_glyph_offset)(const void *, godot_rid *, const godot_vector2i *, int32_t);
+ void (*font_set_glyph_offset)(void *, godot_rid *, const godot_vector2i *, int32_t, const godot_vector2 *);
+ godot_vector2 (*font_get_glyph_size)(const void *, godot_rid *, const godot_vector2i *, int32_t);
+ void (*font_set_glyph_size)(void *, godot_rid *, const godot_vector2i *, int32_t, const godot_vector2 *);
+ godot_rect2 (*font_get_glyph_uv_rect)(const void *, godot_rid *, const godot_vector2i *, int32_t);
+ void (*font_set_glyph_uv_rect)(void *, godot_rid *, const godot_vector2i *, int32_t, const godot_rect2 *);
+ godot_int (*font_get_glyph_texture_idx)(const void *, godot_rid *, const godot_vector2i *, int32_t);
+ void (*font_set_glyph_texture_idx)(void *, godot_rid *, const godot_vector2i *, int32_t, godot_int);
+ bool (*font_get_glyph_contours)(const void *, godot_rid *, godot_int, int32_t, godot_packed_vector3_array *, godot_packed_int32_array *, bool *);
+ godot_array (*font_get_kerning_list)(const void *, godot_rid *, godot_int);
+ void (*font_clear_kerning_map)(void *, godot_rid *, godot_int);
+ void (*font_remove_kerning)(void *, godot_rid *, godot_int, const godot_vector2i *);
+ void (*font_set_kerning)(void *, godot_rid *, godot_int, const godot_vector2i *, const godot_vector2 *);
+ godot_vector2 (*font_get_kerning)(const void *, godot_rid *, godot_int, const godot_vector2i *);
+ int32_t (*font_get_glyph_index)(const void *, godot_rid *, godot_int, char32_t, char32_t);
+ bool (*font_has_char)(const void *, godot_rid *, char32_t);
+ godot_string (*font_get_supported_chars)(const void *, godot_rid *);
+ void (*font_render_range)(void *, godot_rid *, const godot_vector2i *, char32_t, char32_t);
+ void (*font_render_glyph)(void *, godot_rid *, const godot_vector2i *, int32_t);
+ void (*font_draw_glyph)(const void *, godot_rid *, godot_rid *, godot_int, const godot_vector2 *, int32_t, const godot_color *);
+ void (*font_draw_glyph_outline)(const void *, godot_rid *, godot_rid *, godot_int, godot_int, const godot_vector2 *, int32_t, const godot_color *);
+ bool (*font_is_language_supported)(const void *, godot_rid *, const godot_string *);
void (*font_set_language_support_override)(void *, godot_rid *, const godot_string *, bool);
- bool (*font_get_language_support_override)(void *, godot_rid *, const godot_string *);
+ bool (*font_get_language_support_override)(const void *, godot_rid *, const godot_string *);
void (*font_remove_language_support_override)(void *, godot_rid *, const godot_string *);
- godot_packed_string_array (*font_get_language_support_overrides)(void *, godot_rid *);
- bool (*font_is_script_supported)(void *, godot_rid *, const godot_string *);
+ godot_packed_string_array (*font_get_language_support_overrides)(const void *, godot_rid *);
+ bool (*font_is_script_supported)(const void *, godot_rid *, const godot_string *);
void (*font_set_script_support_override)(void *, godot_rid *, const godot_string *, bool);
- bool (*font_get_script_support_override)(void *, godot_rid *, const godot_string *);
+ bool (*font_get_script_support_override)(const void *, godot_rid *, const godot_string *);
void (*font_remove_script_support_override)(void *, godot_rid *, const godot_string *);
- godot_packed_string_array (*font_get_script_support_overrides)(void *, godot_rid *);
- uint32_t (*font_get_glyph_index)(void *, godot_rid *, char32_t, char32_t);
- godot_vector2 (*font_get_glyph_advance)(void *, godot_rid *, uint32_t, int);
- godot_vector2 (*font_get_glyph_kerning)(void *, godot_rid *, uint32_t, uint32_t, int);
- godot_vector2 (*font_draw_glyph)(void *, godot_rid *, godot_rid *, int, const godot_vector2 *, uint32_t, const godot_color *);
- godot_vector2 (*font_draw_glyph_outline)(void *, godot_rid *, godot_rid *, int, int, const godot_vector2 *, uint32_t, const godot_color *);
- bool (*font_get_glyph_contours)(void *, godot_rid *, int, uint32_t, godot_packed_vector3_array *, godot_packed_int32_array *, bool *);
- float (*font_get_oversampling)(void *);
- void (*font_set_oversampling)(void *, float);
- godot_packed_string_array (*get_system_fonts)(void *);
+ godot_packed_string_array (*font_get_script_support_overrides)(const void *, godot_rid *);
+ godot_dictionary (*font_supported_feature_list)(const void *, godot_rid *);
+ godot_dictionary (*font_supported_variation_list)(const void *, godot_rid *);
+ godot_real_t (*font_get_global_oversampling)(const void *);
+ void (*font_set_global_oversampling)(void *, godot_real_t);
+
godot_rid (*create_shaped_text)(void *, godot_int, godot_int);
void (*shaped_text_clear)(void *, godot_rid *);
void (*shaped_text_set_direction)(void *, godot_rid *, godot_int);
@@ -138,27 +179,28 @@ typedef struct {
bool (*shaped_text_resize_object)(void *, godot_rid *, const godot_variant *, const godot_vector2 *, godot_int);
godot_rid (*shaped_text_substr)(void *, godot_rid *, godot_int, godot_int);
godot_rid (*shaped_text_get_parent)(void *, godot_rid *);
- float (*shaped_text_fit_to_width)(void *, godot_rid *, float, uint8_t);
- float (*shaped_text_tab_align)(void *, godot_rid *, godot_packed_float32_array *);
+ godot_real_t (*shaped_text_fit_to_width)(void *, godot_rid *, godot_real_t, uint8_t);
+ godot_real_t (*shaped_text_tab_align)(void *, godot_rid *, godot_packed_float32_array *);
bool (*shaped_text_shape)(void *, godot_rid *);
bool (*shaped_text_update_breaks)(void *, godot_rid *);
bool (*shaped_text_update_justification_ops)(void *, godot_rid *);
- void (*shaped_text_overrun_trim_to_width)(void *, godot_rid *, float, uint8_t);
+ void (*shaped_text_overrun_trim_to_width)(void *, godot_rid *, godot_real_t, uint8_t);
bool (*shaped_text_is_ready)(void *, godot_rid *);
godot_packed_glyph_array (*shaped_text_get_glyphs)(void *, godot_rid *);
godot_vector2i (*shaped_text_get_range)(void *, godot_rid *);
godot_packed_glyph_array (*shaped_text_sort_logical)(void *, godot_rid *);
godot_packed_vector2i_array (*shaped_text_get_line_breaks_adv)(void *, godot_rid *, godot_packed_float32_array *, int, bool, uint8_t);
- godot_packed_vector2i_array (*shaped_text_get_line_breaks)(void *, godot_rid *, float, int, uint8_t);
+ godot_packed_vector2i_array (*shaped_text_get_line_breaks)(void *, godot_rid *, godot_real_t, int, uint8_t);
godot_packed_vector2i_array (*shaped_text_get_word_breaks)(void *, godot_rid *, int);
godot_array (*shaped_text_get_objects)(void *, godot_rid *);
godot_rect2 (*shaped_text_get_object_rect)(void *, godot_rid *, const godot_variant *);
godot_vector2 (*shaped_text_get_size)(void *, godot_rid *);
- float (*shaped_text_get_ascent)(void *, godot_rid *);
- float (*shaped_text_get_descent)(void *, godot_rid *);
- float (*shaped_text_get_width)(void *, godot_rid *);
- float (*shaped_text_get_underline_position)(void *, godot_rid *);
- float (*shaped_text_get_underline_thickness)(void *, godot_rid *);
+ godot_real_t (*shaped_text_get_ascent)(void *, godot_rid *);
+ godot_real_t (*shaped_text_get_descent)(void *, godot_rid *);
+ godot_real_t (*shaped_text_get_width)(void *, godot_rid *);
+ godot_real_t (*shaped_text_get_underline_position)(void *, godot_rid *);
+ godot_real_t (*shaped_text_get_underline_thickness)(void *, godot_rid *);
+
godot_string (*format_number)(void *, const godot_string *, const godot_string *);
godot_string (*parse_number)(void *, const godot_string *, const godot_string *);
godot_string (*percent_sign)(void *, const godot_string *);
@@ -185,8 +227,8 @@ void GDAPI godot_glyph_set_flags(godot_glyph *p_self, godot_int p_flags);
godot_vector2 GDAPI godot_glyph_get_offset(const godot_glyph *p_self);
void GDAPI godot_glyph_set_offset(godot_glyph *p_self, const godot_vector2 *p_offset);
-godot_float GDAPI godot_glyph_get_advance(const godot_glyph *p_self);
-void GDAPI godot_glyph_set_advance(godot_glyph *p_self, godot_float p_advance);
+godot_real_t GDAPI godot_glyph_get_advance(const godot_glyph *p_self);
+void GDAPI godot_glyph_set_advance(godot_glyph *p_self, godot_real_t p_advance);
godot_rid GDAPI godot_glyph_get_font(const godot_glyph *p_self);
void GDAPI godot_glyph_set_font(godot_glyph *p_self, godot_rid *p_font);
diff --git a/modules/gdnative/text/text_server_gdnative.cpp b/modules/gdnative/text/text_server_gdnative.cpp
index 3ed3f5449e..195c32c3f8 100644
--- a/modules/gdnative/text/text_server_gdnative.cpp
+++ b/modules/gdnative/text/text_server_gdnative.cpp
@@ -88,299 +88,479 @@ bool TextServerGDNative::is_locale_right_to_left(const String &p_locale) {
return interface->is_locale_right_to_left(data, (godot_string *)&p_locale);
}
+int32_t TextServerGDNative::name_to_tag(const String &p_name) const {
+ ERR_FAIL_COND_V(interface == nullptr, 0);
+ return interface->name_to_tag(data, (godot_string *)&p_name);
+}
+
+String TextServerGDNative::tag_to_name(int32_t p_tag) const {
+ ERR_FAIL_COND_V(interface == nullptr, String());
+ godot_string result = interface->tag_to_name(data, p_tag);
+ String name = *(String *)&result;
+ godot_string_destroy(&result);
+ return name;
+}
+
/*************************************************************************/
-/* Font interface */
+/* Font */
/*************************************************************************/
-RID TextServerGDNative::create_font_system(const String &p_name, int p_base_size) {
+RID TextServerGDNative::create_font() {
ERR_FAIL_COND_V(interface == nullptr, RID());
- godot_rid result = interface->create_font_system(data, (const godot_string *)&p_name, p_base_size);
+ godot_rid result = interface->create_font(data);
RID rid = *(RID *)&result;
return rid;
}
-RID TextServerGDNative::create_font_resource(const String &p_filename, int p_base_size) {
- ERR_FAIL_COND_V(interface == nullptr, RID());
- godot_rid result = interface->create_font_resource(data, (const godot_string *)&p_filename, p_base_size);
- RID rid = *(RID *)&result;
- return rid;
+void TextServerGDNative::font_set_data(RID p_font_rid, const PackedByteArray &p_data) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_data(data, (godot_rid *)&p_font_rid, (const godot_packed_byte_array *)&p_data);
}
-RID TextServerGDNative::create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size) {
- ERR_FAIL_COND_V(interface == nullptr, RID());
- godot_rid result = interface->create_font_memory(data, p_data, p_size, (godot_string *)&p_type, p_base_size);
- RID rid = *(RID *)&result;
- return rid;
+void TextServerGDNative::font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_data_ptr(data, (godot_rid *)&p_font_rid, p_data_ptr, p_data_size);
}
-RID TextServerGDNative::create_font_bitmap(float p_height, float p_ascent, int p_base_size) {
- ERR_FAIL_COND_V(interface == nullptr, RID());
- godot_rid result = interface->create_font_bitmap(data, p_height, p_ascent, p_base_size);
- RID rid = *(RID *)&result;
- return rid;
+void TextServerGDNative::font_set_antialiased(RID p_font_rid, bool p_antialiased) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_antialiased(data, (godot_rid *)&p_font_rid, p_antialiased);
}
-void TextServerGDNative::font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) {
+bool TextServerGDNative::font_is_antialiased(RID p_font_rid) const {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->font_is_antialiased(data, (godot_rid *)&p_font_rid);
+}
+
+void TextServerGDNative::font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) {
ERR_FAIL_COND(interface == nullptr);
- interface->font_bitmap_add_texture(data, (godot_rid *)&p_font, (const godot_object *)p_texture.ptr());
+ interface->font_set_multichannel_signed_distance_field(data, (godot_rid *)&p_font_rid, p_msdf);
+}
+
+bool TextServerGDNative::font_is_multichannel_signed_distance_field(RID p_font_rid) const {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->font_is_multichannel_signed_distance_field(data, (godot_rid *)&p_font_rid);
}
-void TextServerGDNative::font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) {
+void TextServerGDNative::font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) {
ERR_FAIL_COND(interface == nullptr);
- interface->font_bitmap_add_char(data, (godot_rid *)&p_font, p_char, p_texture_idx, (const godot_rect2 *)&p_rect, (const godot_vector2 *)&p_align, p_advance);
+ interface->font_set_msdf_pixel_range(data, (godot_rid *)&p_font_rid, p_msdf_pixel_range);
}
-void TextServerGDNative::font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) {
+int TextServerGDNative::font_get_msdf_pixel_range(RID p_font_rid) const {
+ ERR_FAIL_COND_V(interface == nullptr, 0);
+ return interface->font_get_msdf_pixel_range(data, (godot_rid *)&p_font_rid);
+}
+
+void TextServerGDNative::font_set_msdf_size(RID p_font_rid, int p_msdf_size) {
ERR_FAIL_COND(interface == nullptr);
- interface->font_bitmap_add_kerning_pair(data, (godot_rid *)&p_font, p_A, p_B, p_kerning);
+ interface->font_set_msdf_size(data, (godot_rid *)&p_font_rid, p_msdf_size);
}
-float TextServerGDNative::font_get_height(RID p_font, int p_size) const {
- ERR_FAIL_COND_V(interface == nullptr, 0.f);
- return interface->font_get_height(data, (godot_rid *)&p_font, p_size);
+int TextServerGDNative::font_get_msdf_size(RID p_font_rid) const {
+ ERR_FAIL_COND_V(interface == nullptr, 0);
+ return interface->font_get_msdf_size(data, (godot_rid *)&p_font_rid);
}
-float TextServerGDNative::font_get_ascent(RID p_font, int p_size) const {
- ERR_FAIL_COND_V(interface == nullptr, 0.f);
- return interface->font_get_ascent(data, (godot_rid *)&p_font, p_size);
+void TextServerGDNative::font_set_fixed_size(RID p_font_rid, int p_fixed_size) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_fixed_size(data, (godot_rid *)&p_font_rid, p_fixed_size);
}
-float TextServerGDNative::font_get_descent(RID p_font, int p_size) const {
- ERR_FAIL_COND_V(interface == nullptr, 0.f);
- return interface->font_get_descent(data, (godot_rid *)&p_font, p_size);
+int TextServerGDNative::font_get_fixed_size(RID p_font_rid) const {
+ ERR_FAIL_COND_V(interface == nullptr, 0);
+ return interface->font_get_fixed_size(data, (godot_rid *)&p_font_rid);
}
-float TextServerGDNative::font_get_underline_position(RID p_font, int p_size) const {
- ERR_FAIL_COND_V(interface == nullptr, 0.f);
- return interface->font_get_underline_position(data, (godot_rid *)&p_font, p_size);
+void TextServerGDNative::font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_force_autohinter(data, (godot_rid *)&p_font_rid, p_force_autohinter);
}
-float TextServerGDNative::font_get_underline_thickness(RID p_font, int p_size) const {
- ERR_FAIL_COND_V(interface == nullptr, 0.f);
- return interface->font_get_underline_thickness(data, (godot_rid *)&p_font, p_size);
+bool TextServerGDNative::font_is_force_autohinter(RID p_font_rid) const {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->font_is_force_autohinter(data, (godot_rid *)&p_font_rid);
}
-int TextServerGDNative::font_get_spacing_space(RID p_font) const {
- ERR_FAIL_COND_V(interface == nullptr, 0);
- return interface->font_get_spacing_space(data, (godot_rid *)&p_font);
+void TextServerGDNative::font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_hinting(data, (godot_rid *)&p_font_rid, (godot_int)p_hinting);
}
-void TextServerGDNative::font_set_spacing_space(RID p_font, int p_value) {
+TextServer::Hinting TextServerGDNative::font_get_hinting(RID p_font_rid) const {
+ ERR_FAIL_COND_V(interface == nullptr, TextServer::HINTING_NONE);
+ return (TextServer::Hinting)interface->font_get_hinting(data, (godot_rid *)&p_font_rid);
+}
+
+void TextServerGDNative::font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_variation_coordinates(data, (godot_rid *)&p_font_rid, (const godot_dictionary *)&p_variation_coordinates);
+}
+
+Dictionary TextServerGDNative::font_get_variation_coordinates(RID p_font_rid) const {
+ ERR_FAIL_COND_V(interface == nullptr, Dictionary());
+ godot_dictionary result = interface->font_get_variation_coordinates(data, (godot_rid *)&p_font_rid);
+ Dictionary dict = *(Dictionary *)&result;
+ godot_dictionary_destroy(&result);
+ return dict;
+}
+
+void TextServerGDNative::font_set_oversampling(RID p_font_rid, real_t p_oversampling) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_oversampling(data, (godot_rid *)&p_font_rid, p_oversampling);
+}
+
+real_t TextServerGDNative::font_get_oversampling(RID p_font_rid) const {
+ ERR_FAIL_COND_V(interface == nullptr, 0.0f);
+ return interface->font_get_oversampling(data, (godot_rid *)&p_font_rid);
+}
+
+Array TextServerGDNative::font_get_size_cache_list(RID p_font_rid) const {
+ ERR_FAIL_COND_V(interface == nullptr, Array());
+ godot_array result = interface->font_get_size_cache_list(data, (godot_rid *)&p_font_rid);
+ Array list = *(Array *)&result;
+ godot_array_destroy(&result);
+ return list;
+}
+
+void TextServerGDNative::font_clear_size_cache(RID p_font_rid) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_clear_size_cache(data, (godot_rid *)&p_font_rid);
+}
+
+void TextServerGDNative::font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_remove_size_cache(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size);
+}
+
+void TextServerGDNative::font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) {
ERR_FAIL_COND(interface == nullptr);
- interface->font_set_spacing_space(data, (godot_rid *)&p_font, p_value);
+ interface->font_set_ascent(data, (godot_rid *)&p_font_rid, p_size, p_ascent);
+}
+
+real_t TextServerGDNative::font_get_ascent(RID p_font_rid, int p_size) const {
+ ERR_FAIL_COND_V(interface == nullptr, 0.0f);
+ return interface->font_get_ascent(data, (godot_rid *)&p_font_rid, p_size);
}
-int TextServerGDNative::font_get_spacing_glyph(RID p_font) const {
+void TextServerGDNative::font_set_descent(RID p_font_rid, int p_size, real_t p_descent) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_descent(data, (godot_rid *)&p_font_rid, p_size, p_descent);
+}
+
+real_t TextServerGDNative::font_get_descent(RID p_font_rid, int p_size) const {
+ ERR_FAIL_COND_V(interface == nullptr, 0.0f);
+ return interface->font_get_descent(data, (godot_rid *)&p_font_rid, p_size);
+}
+
+void TextServerGDNative::font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_underline_position(data, (godot_rid *)&p_font_rid, p_size, p_underline_position);
+}
+
+real_t TextServerGDNative::font_get_underline_position(RID p_font_rid, int p_size) const {
+ ERR_FAIL_COND_V(interface == nullptr, 0.0f);
+ return interface->font_get_underline_position(data, (godot_rid *)&p_font_rid, p_size);
+}
+
+void TextServerGDNative::font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_underline_thickness(data, (godot_rid *)&p_font_rid, p_size, p_underline_thickness);
+}
+
+real_t TextServerGDNative::font_get_underline_thickness(RID p_font_rid, int p_size) const {
+ ERR_FAIL_COND_V(interface == nullptr, 0.0f);
+ return interface->font_get_underline_thickness(data, (godot_rid *)&p_font_rid, p_size);
+}
+
+void TextServerGDNative::font_set_scale(RID p_font_rid, int p_size, real_t p_scale) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_scale(data, (godot_rid *)&p_font_rid, p_size, p_scale);
+}
+
+real_t TextServerGDNative::font_get_scale(RID p_font_rid, int p_size) const {
+ ERR_FAIL_COND_V(interface == nullptr, 0.0f);
+ return interface->font_get_scale(data, (godot_rid *)&p_font_rid, p_size);
+}
+
+void TextServerGDNative::font_set_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing, int p_value) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_spacing(data, (godot_rid *)&p_font_rid, p_size, (godot_int)p_spacing, p_value);
+}
+
+int TextServerGDNative::font_get_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing) const {
ERR_FAIL_COND_V(interface == nullptr, 0);
- return interface->font_get_spacing_glyph(data, (godot_rid *)&p_font);
+ return interface->font_get_spacing(data, (godot_rid *)&p_font_rid, p_size, (godot_int)p_spacing);
}
-void TextServerGDNative::font_set_spacing_glyph(RID p_font, int p_value) {
+int TextServerGDNative::font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const {
+ ERR_FAIL_COND_V(interface == nullptr, -1);
+ return interface->font_get_texture_count(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size);
+}
+
+void TextServerGDNative::font_clear_textures(RID p_font_rid, const Vector2i &p_size) {
ERR_FAIL_COND(interface == nullptr);
- interface->font_set_spacing_glyph(data, (godot_rid *)&p_font, p_value);
+ interface->font_clear_textures(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size);
}
-void TextServerGDNative::font_set_antialiased(RID p_font, bool p_antialiased) {
+void TextServerGDNative::font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) {
ERR_FAIL_COND(interface == nullptr);
- interface->font_set_antialiased(data, (godot_rid *)&p_font, p_antialiased);
+ interface->font_remove_texture(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_texture_index);
}
-bool TextServerGDNative::font_get_antialiased(RID p_font) const {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->font_get_antialiased(data, (godot_rid *)&p_font);
+void TextServerGDNative::font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_texture_image(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_texture_index, (const godot_object *)p_image.ptr());
}
-Dictionary TextServerGDNative::font_get_variation_list(RID p_font) const {
- ERR_FAIL_COND_V(interface == nullptr, Dictionary());
- godot_dictionary result = interface->font_get_variation_list(data, (godot_rid *)&p_font);
- Dictionary info = *(Dictionary *)&result;
- godot_dictionary_destroy(&result);
+Ref<Image> TextServerGDNative::font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const {
+ ERR_FAIL_COND_V(interface == nullptr, Ref<Image>());
+ godot_object *result = interface->font_get_texture_image(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_texture_index);
+ return Ref<Image>((Image *)result);
+}
- return info;
+void TextServerGDNative::font_set_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_texture_offsets(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_texture_index, (const godot_packed_int32_array *)&p_offset);
+}
+
+PackedInt32Array TextServerGDNative::font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const {
+ ERR_FAIL_COND_V(interface == nullptr, PackedInt32Array());
+ godot_packed_int32_array result = interface->font_get_texture_offsets(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_texture_index);
+ PackedInt32Array offset = *(PackedInt32Array *)&result;
+ godot_packed_int32_array_destroy(&result);
+ return offset;
+}
+
+Array TextServerGDNative::font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const {
+ ERR_FAIL_COND_V(interface == nullptr, Array());
+ godot_array result = interface->font_get_glyph_list(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size);
+ Array list = *(Array *)&result;
+ godot_array_destroy(&result);
+ return list;
+}
+
+void TextServerGDNative::font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_clear_glyphs(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size);
}
-void TextServerGDNative::font_set_variation(RID p_font, const String &p_name, double p_value) {
+void TextServerGDNative::font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) {
ERR_FAIL_COND(interface == nullptr);
- interface->font_set_variation(data, (godot_rid *)&p_font, (godot_string *)&p_name, p_value);
+ interface->font_remove_glyph(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph);
}
-double TextServerGDNative::font_get_variation(RID p_font, const String &p_name) const {
- return interface->font_get_variation(data, (godot_rid *)&p_font, (godot_string *)&p_name);
+Vector2 TextServerGDNative::font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const {
+ ERR_FAIL_COND_V(interface == nullptr, Vector2());
+ godot_vector2 result = interface->font_get_glyph_advance(data, (godot_rid *)&p_font_rid, p_size, p_glyph);
+ Vector2 adv = *(Vector2 *)&result;
+ return adv;
}
-void TextServerGDNative::font_set_hinting(RID p_font, TextServer::Hinting p_hinting) {
+void TextServerGDNative::font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) {
ERR_FAIL_COND(interface == nullptr);
- interface->font_set_hinting(data, (godot_rid *)&p_font, (godot_int)p_hinting);
+ interface->font_set_glyph_advance(data, (godot_rid *)&p_font_rid, p_size, p_glyph, (const godot_vector2 *)&p_advance);
}
-TextServer::Hinting TextServerGDNative::font_get_hinting(RID p_font) const {
- ERR_FAIL_COND_V(interface == nullptr, TextServer::HINTING_NONE);
- return (TextServer::Hinting)interface->font_get_hinting(data, (godot_rid *)&p_font);
+Vector2 TextServerGDNative::font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
+ ERR_FAIL_COND_V(interface == nullptr, Vector2());
+ godot_vector2 result = interface->font_get_glyph_offset(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph);
+ Vector2 off = *(Vector2 *)&result;
+ return off;
}
-Dictionary TextServerGDNative::font_get_feature_list(RID p_font) const {
- ERR_FAIL_COND_V(interface == nullptr, Dictionary());
- godot_dictionary result = interface->font_get_feature_list(data, (godot_rid *)&p_font);
- Dictionary info = *(Dictionary *)&result;
- godot_dictionary_destroy(&result);
+void TextServerGDNative::font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_glyph_offset(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph, (const godot_vector2 *)&p_offset);
+}
- return info;
+Vector2 TextServerGDNative::font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
+ ERR_FAIL_COND_V(interface == nullptr, Vector2());
+ godot_vector2 result = interface->font_get_glyph_size(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph);
+ Vector2 sz = *(Vector2 *)&result;
+ return sz;
}
-void TextServerGDNative::font_set_distance_field_hint(RID p_font, bool p_distance_field) {
+void TextServerGDNative::font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) {
ERR_FAIL_COND(interface == nullptr);
- interface->font_set_distance_field_hint(data, (godot_rid *)&p_font, p_distance_field);
+ interface->font_set_glyph_size(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph, (const godot_vector2 *)&p_size);
}
-bool TextServerGDNative::font_get_distance_field_hint(RID p_font) const {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->font_get_distance_field_hint(data, (godot_rid *)&p_font);
+Rect2 TextServerGDNative::font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
+ ERR_FAIL_COND_V(interface == nullptr, Rect2());
+ godot_rect2 result = interface->font_get_glyph_uv_rect(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph);
+ Rect2 uv = *(Rect2 *)&result;
+ return uv;
}
-void TextServerGDNative::font_set_force_autohinter(RID p_font, bool p_enabeld) {
+void TextServerGDNative::font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) {
ERR_FAIL_COND(interface == nullptr);
- interface->font_set_force_autohinter(data, (godot_rid *)&p_font, p_enabeld);
+ interface->font_set_glyph_uv_rect(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph, (const godot_rect2 *)&p_uv_rect);
}
-bool TextServerGDNative::font_get_force_autohinter(RID p_font) const {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->font_get_force_autohinter(data, (godot_rid *)&p_font);
+int TextServerGDNative::font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
+ ERR_FAIL_COND_V(interface == nullptr, -1);
+ return interface->font_get_glyph_texture_idx(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph);
+}
+
+void TextServerGDNative::font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_glyph_texture_idx(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph, p_texture_idx);
}
-bool TextServerGDNative::font_has_char(RID p_font, char32_t p_char) const {
+bool TextServerGDNative::font_get_glyph_contours(RID p_font_rid, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const {
ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->font_has_char(data, (godot_rid *)&p_font, p_char);
+ return interface->font_get_glyph_contours(data, (godot_rid *)&p_font_rid, p_size, p_index, (godot_packed_vector3_array *)&r_points, (godot_packed_int32_array *)&r_contours, &r_orientation);
}
-String TextServerGDNative::font_get_supported_chars(RID p_font) const {
- ERR_FAIL_COND_V(interface == nullptr, String());
- godot_string result = interface->font_get_supported_chars(data, (godot_rid *)&p_font);
- String ret = *(String *)&result;
- godot_string_destroy(&result);
- return ret;
+Array TextServerGDNative::font_get_kerning_list(RID p_font_rid, int p_size) const {
+ ERR_FAIL_COND_V(interface == nullptr, Array());
+ godot_array result = interface->font_get_kerning_list(data, (godot_rid *)&p_font_rid, p_size);
+ Array list = *(Array *)&result;
+ godot_array_destroy(&result);
+ return list;
}
-bool TextServerGDNative::font_has_outline(RID p_font) const {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->font_has_outline(data, (godot_rid *)&p_font);
+void TextServerGDNative::font_clear_kerning_map(RID p_font_rid, int p_size) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_clear_kerning_map(data, (godot_rid *)&p_font_rid, p_size);
}
-float TextServerGDNative::font_get_base_size(RID p_font) const {
- ERR_FAIL_COND_V(interface == nullptr, 0.f);
- return interface->font_get_base_size(data, (godot_rid *)&p_font);
+void TextServerGDNative::font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_remove_kerning(data, (godot_rid *)&p_font_rid, p_size, (const godot_vector2i *)&p_glyph_pair);
+}
+
+void TextServerGDNative::font_set_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_kerning(data, (godot_rid *)&p_font_rid, p_size, (const godot_vector2i *)&p_glyph_pair, (const godot_vector2 *)&p_kerning);
+}
+
+Vector2 TextServerGDNative::font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const {
+ ERR_FAIL_COND_V(interface == nullptr, Vector2());
+ godot_vector2 result = interface->font_get_kerning(data, (godot_rid *)&p_font_rid, p_size, (const godot_vector2i *)&p_glyph_pair);
+ Vector2 kern = *(Vector2 *)&result;
+ return kern;
}
-bool TextServerGDNative::font_is_language_supported(RID p_font, const String &p_language) const {
+int32_t TextServerGDNative::font_get_glyph_index(RID p_font_rid, int p_size, char32_t p_char, char32_t p_variation_selector) const {
+ ERR_FAIL_COND_V(interface == nullptr, 0);
+ return interface->font_get_glyph_index(data, (godot_rid *)&p_font_rid, p_size, p_char, p_variation_selector);
+}
+
+bool TextServerGDNative::font_has_char(RID p_font_rid, char32_t p_char) const {
ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->font_is_language_supported(data, (godot_rid *)&p_font, (godot_string *)&p_language);
+ return interface->font_has_char(data, (godot_rid *)&p_font_rid, p_char);
+}
+
+String TextServerGDNative::font_get_supported_chars(RID p_font_rid) const {
+ ERR_FAIL_COND_V(interface == nullptr, String());
+ godot_string result = interface->font_get_supported_chars(data, (godot_rid *)&p_font_rid);
+ String chars = *(String *)&result;
+ godot_string_destroy(&result);
+ return chars;
}
-void TextServerGDNative::font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) {
+void TextServerGDNative::font_render_range(RID p_font_rid, const Vector2i &p_size, char32_t p_start, char32_t p_end) {
ERR_FAIL_COND(interface == nullptr);
- return interface->font_set_language_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_language, p_supported);
+ interface->font_render_range(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_start, p_end);
}
-bool TextServerGDNative::font_get_language_support_override(RID p_font, const String &p_language) {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->font_get_language_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_language);
+void TextServerGDNative::font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_render_glyph(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_index);
}
-void TextServerGDNative::font_remove_language_support_override(RID p_font, const String &p_language) {
+void TextServerGDNative::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const {
ERR_FAIL_COND(interface == nullptr);
- interface->font_remove_language_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_language);
+ interface->font_draw_glyph(data, (godot_rid *)&p_font_rid, (godot_rid *)&p_canvas, p_size, (const godot_vector2 *)&p_pos, p_index, (const godot_color *)&p_color);
}
-Vector<String> TextServerGDNative::font_get_language_support_overrides(RID p_font) {
- ERR_FAIL_COND_V(interface == nullptr, Vector<String>());
- godot_packed_string_array result = interface->font_get_language_support_overrides(data, (godot_rid *)&p_font);
- Vector<String> ret = *(Vector<String> *)&result;
- godot_packed_string_array_destroy(&result);
- return ret;
+void TextServerGDNative::font_draw_glyph_outline(RID p_font_rid, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_draw_glyph_outline(data, (godot_rid *)&p_font_rid, (godot_rid *)&p_canvas, p_size, p_outline_size, (const godot_vector2 *)&p_pos, p_index, (const godot_color *)&p_color);
}
-bool TextServerGDNative::font_is_script_supported(RID p_font, const String &p_script) const {
+bool TextServerGDNative::font_is_language_supported(RID p_font_rid, const String &p_language) const {
ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->font_is_script_supported(data, (godot_rid *)&p_font, (godot_string *)&p_script);
+ return interface->font_is_language_supported(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_language);
}
-void TextServerGDNative::font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) {
+void TextServerGDNative::font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) {
ERR_FAIL_COND(interface == nullptr);
- return interface->font_set_script_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_script, p_supported);
+ interface->font_set_language_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_language, p_supported);
}
-bool TextServerGDNative::font_get_script_support_override(RID p_font, const String &p_script) {
+bool TextServerGDNative::font_get_language_support_override(RID p_font_rid, const String &p_language) {
ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->font_get_script_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_script);
+ return interface->font_get_language_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_language);
}
-void TextServerGDNative::font_remove_script_support_override(RID p_font, const String &p_script) {
+void TextServerGDNative::font_remove_language_support_override(RID p_font_rid, const String &p_language) {
ERR_FAIL_COND(interface == nullptr);
- interface->font_remove_script_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_script);
+ interface->font_remove_language_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_language);
}
-Vector<String> TextServerGDNative::font_get_script_support_overrides(RID p_font) {
- ERR_FAIL_COND_V(interface == nullptr, Vector<String>());
- godot_packed_string_array result = interface->font_get_script_support_overrides(data, (godot_rid *)&p_font);
- Vector<String> ret = *(Vector<String> *)&result;
+Vector<String> TextServerGDNative::font_get_language_support_overrides(RID p_font_rid) {
+ ERR_FAIL_COND_V(interface == nullptr, PackedStringArray());
+ godot_packed_string_array result = interface->font_get_language_support_overrides(data, (godot_rid *)&p_font_rid);
+ PackedStringArray list = *(PackedStringArray *)&result;
godot_packed_string_array_destroy(&result);
- return ret;
+ return list;
}
-uint32_t TextServerGDNative::font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector) const {
- ERR_FAIL_COND_V(interface == nullptr, 0);
- return interface->font_get_glyph_index(data, (godot_rid *)&p_font, p_char, p_variation_selector);
+bool TextServerGDNative::font_is_script_supported(RID p_font_rid, const String &p_script) const {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->font_is_script_supported(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_script);
}
-Vector2 TextServerGDNative::font_get_glyph_advance(RID p_font, uint32_t p_index, int p_size) const {
- ERR_FAIL_COND_V(interface == nullptr, Vector2());
- godot_vector2 result = interface->font_get_glyph_advance(data, (godot_rid *)&p_font, p_index, p_size);
- Vector2 advance = *(Vector2 *)&result;
- return advance;
+void TextServerGDNative::font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_script_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_script, p_supported);
}
-Vector2 TextServerGDNative::font_get_glyph_kerning(RID p_font, uint32_t p_index_a, uint32_t p_index_b, int p_size) const {
- ERR_FAIL_COND_V(interface == nullptr, Vector2());
- godot_vector2 result = interface->font_get_glyph_kerning(data, (godot_rid *)&p_font, p_index_a, p_index_b, p_size);
- Vector2 kerning = *(Vector2 *)&result;
- return kerning;
+bool TextServerGDNative::font_get_script_support_override(RID p_font_rid, const String &p_script) {
+ ERR_FAIL_COND_V(interface == nullptr, false);
+ return interface->font_get_script_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_script);
}
-Vector2 TextServerGDNative::font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
- ERR_FAIL_COND_V(interface == nullptr, Vector2());
- godot_vector2 result = interface->font_draw_glyph(data, (godot_rid *)&p_font, (godot_rid *)&p_canvas, p_size, (const godot_vector2 *)&p_pos, p_index, (const godot_color *)&p_color);
- Vector2 advance = *(Vector2 *)&result;
- return advance;
+void TextServerGDNative::font_remove_script_support_override(RID p_font_rid, const String &p_script) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_remove_script_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_script);
}
-Vector2 TextServerGDNative::font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
- ERR_FAIL_COND_V(interface == nullptr, Vector2());
- godot_vector2 result = interface->font_draw_glyph_outline(data, (godot_rid *)&p_font, (godot_rid *)&p_canvas, p_size, p_outline_size, (const godot_vector2 *)&p_pos, p_index, (const godot_color *)&p_color);
- Vector2 advance = *(Vector2 *)&result;
- return advance;
+Vector<String> TextServerGDNative::font_get_script_support_overrides(RID p_font_rid) {
+ ERR_FAIL_COND_V(interface == nullptr, PackedStringArray());
+ godot_packed_string_array result = interface->font_get_script_support_overrides(data, (godot_rid *)&p_font_rid);
+ PackedStringArray list = *(PackedStringArray *)&result;
+ godot_packed_string_array_destroy(&result);
+ return list;
}
-bool TextServerGDNative::font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const {
- ERR_FAIL_COND_V(interface == nullptr, false);
- ERR_FAIL_COND_V(interface->font_get_glyph_contours == nullptr, false);
- return interface->font_get_glyph_contours(data, (godot_rid *)&p_font, p_size, p_index, (godot_packed_vector3_array *)&r_points, (godot_packed_int32_array *)&r_contours, (bool *)&r_orientation);
+Dictionary TextServerGDNative::font_supported_feature_list(RID p_font_rid) const {
+ ERR_FAIL_COND_V(interface == nullptr, Dictionary());
+ godot_dictionary result = interface->font_supported_feature_list(data, (godot_rid *)&p_font_rid);
+ Dictionary dict = *(Dictionary *)&result;
+ godot_dictionary_destroy(&result);
+ return dict;
}
-float TextServerGDNative::font_get_oversampling() const {
- ERR_FAIL_COND_V(interface == nullptr, 1.f);
- return interface->font_get_oversampling(data);
+Dictionary TextServerGDNative::font_supported_variation_list(RID p_font_rid) const {
+ ERR_FAIL_COND_V(interface == nullptr, Dictionary());
+ godot_dictionary result = interface->font_supported_variation_list(data, (godot_rid *)&p_font_rid);
+ Dictionary dict = *(Dictionary *)&result;
+ godot_dictionary_destroy(&result);
+ return dict;
}
-void TextServerGDNative::font_set_oversampling(float p_oversampling) {
- ERR_FAIL_COND(interface == nullptr);
- return interface->font_set_oversampling(data, p_oversampling);
+real_t TextServerGDNative::font_get_global_oversampling() const {
+ ERR_FAIL_COND_V(interface == nullptr, 0.0f);
+ return interface->font_get_global_oversampling(data);
}
-Vector<String> TextServerGDNative::get_system_fonts() const {
- ERR_FAIL_COND_V(interface == nullptr, Vector<String>());
- godot_packed_string_array result = interface->get_system_fonts(data);
- Vector<String> fonts = *(Vector<String> *)&result;
- godot_packed_string_array_destroy(&result);
- return fonts;
+void TextServerGDNative::font_set_global_oversampling(real_t p_oversampling) {
+ ERR_FAIL_COND(interface == nullptr);
+ interface->font_set_global_oversampling(data, p_oversampling);
}
/*************************************************************************/
@@ -473,12 +653,12 @@ RID TextServerGDNative::shaped_text_get_parent(RID p_shaped) const {
return rid;
}
-float TextServerGDNative::shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t p_jst_flags) {
+real_t TextServerGDNative::shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t p_jst_flags) {
ERR_FAIL_COND_V(interface == nullptr, 0.f);
return interface->shaped_text_fit_to_width(data, (godot_rid *)&p_shaped, p_width, p_jst_flags);
}
-float TextServerGDNative::shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) {
+real_t TextServerGDNative::shaped_text_tab_align(RID p_shaped, const Vector<real_t> &p_tab_stops) {
ERR_FAIL_COND_V(interface == nullptr, 0.f);
return interface->shaped_text_tab_align(data, (godot_rid *)&p_shaped, (godot_packed_float32_array *)&p_tab_stops);
}
@@ -498,7 +678,7 @@ bool TextServerGDNative::shaped_text_update_justification_ops(RID p_shaped) {
return interface->shaped_text_update_justification_ops(data, (godot_rid *)&p_shaped);
}
-void TextServerGDNative::shaped_text_overrun_trim_to_width(RID p_shaped_line, float p_width, uint8_t p_trim_flags) {
+void TextServerGDNative::shaped_text_overrun_trim_to_width(RID p_shaped_line, real_t p_width, uint8_t p_trim_flags) {
ERR_FAIL_COND(interface == nullptr);
interface->shaped_text_overrun_trim_to_width(data, (godot_rid *)&p_shaped_line, p_width, p_trim_flags);
};
@@ -531,7 +711,7 @@ Vector<TextServer::Glyph> TextServerGDNative::shaped_text_sort_logical(RID p_sha
return glyphs;
}
-Vector<Vector2i> TextServerGDNative::shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<float> &p_width, int p_start, bool p_once, uint8_t p_break_flags) const {
+Vector<Vector2i> TextServerGDNative::shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<real_t> &p_width, int p_start, bool p_once, uint8_t p_break_flags) const {
ERR_FAIL_COND_V(interface == nullptr, Vector<Vector2i>());
if (interface->shaped_text_get_line_breaks_adv != nullptr) {
godot_packed_vector2i_array result = interface->shaped_text_get_line_breaks_adv(data, (godot_rid *)&p_shaped, (godot_packed_float32_array *)&p_width, p_start, p_once, p_break_flags);
@@ -543,7 +723,7 @@ Vector<Vector2i> TextServerGDNative::shaped_text_get_line_breaks_adv(RID p_shape
}
}
-Vector<Vector2i> TextServerGDNative::shaped_text_get_line_breaks(RID p_shaped, float p_width, int p_start, uint8_t p_break_flags) const {
+Vector<Vector2i> TextServerGDNative::shaped_text_get_line_breaks(RID p_shaped, real_t p_width, int p_start, uint8_t p_break_flags) const {
ERR_FAIL_COND_V(interface == nullptr, Vector<Vector2i>());
if (interface->shaped_text_get_line_breaks != nullptr) {
godot_packed_vector2i_array result = interface->shaped_text_get_line_breaks(data, (godot_rid *)&p_shaped, p_width, p_start, p_break_flags);
@@ -588,27 +768,27 @@ Size2 TextServerGDNative::shaped_text_get_size(RID p_shaped) const {
return size;
}
-float TextServerGDNative::shaped_text_get_ascent(RID p_shaped) const {
+real_t TextServerGDNative::shaped_text_get_ascent(RID p_shaped) const {
ERR_FAIL_COND_V(interface == nullptr, 0.f);
return interface->shaped_text_get_ascent(data, (godot_rid *)&p_shaped);
}
-float TextServerGDNative::shaped_text_get_descent(RID p_shaped) const {
+real_t TextServerGDNative::shaped_text_get_descent(RID p_shaped) const {
ERR_FAIL_COND_V(interface == nullptr, 0.f);
return interface->shaped_text_get_descent(data, (godot_rid *)&p_shaped);
}
-float TextServerGDNative::shaped_text_get_width(RID p_shaped) const {
+real_t TextServerGDNative::shaped_text_get_width(RID p_shaped) const {
ERR_FAIL_COND_V(interface == nullptr, 0.f);
return interface->shaped_text_get_width(data, (godot_rid *)&p_shaped);
}
-float TextServerGDNative::shaped_text_get_underline_position(RID p_shaped) const {
+real_t TextServerGDNative::shaped_text_get_underline_position(RID p_shaped) const {
ERR_FAIL_COND_V(interface == nullptr, 0.f);
return interface->shaped_text_get_underline_position(data, (godot_rid *)&p_shaped);
}
-float TextServerGDNative::shaped_text_get_underline_thickness(RID p_shaped) const {
+real_t TextServerGDNative::shaped_text_get_underline_thickness(RID p_shaped) const {
ERR_FAIL_COND_V(interface == nullptr, 0.f);
return interface->shaped_text_get_underline_thickness(data, (godot_rid *)&p_shaped);
}
@@ -756,12 +936,12 @@ void GDAPI godot_glyph_set_offset(godot_glyph *p_self, const godot_vector2 *p_of
self->y_off = offset->y;
}
-godot_float GDAPI godot_glyph_get_advance(const godot_glyph *p_self) {
+godot_real_t GDAPI godot_glyph_get_advance(const godot_glyph *p_self) {
const TextServer::Glyph *self = (const TextServer::Glyph *)p_self;
return self->advance;
}
-void GDAPI godot_glyph_set_advance(godot_glyph *p_self, godot_float p_advance) {
+void GDAPI godot_glyph_set_advance(godot_glyph *p_self, godot_real_t p_advance) {
TextServer::Glyph *self = (TextServer::Glyph *)p_self;
self->advance = p_advance;
}
diff --git a/modules/gdnative/text/text_server_gdnative.h b/modules/gdnative/text/text_server_gdnative.h
index e1b36f4264..f081637e23 100644
--- a/modules/gdnative/text/text_server_gdnative.h
+++ b/modules/gdnative/text/text_server_gdnative.h
@@ -60,78 +60,130 @@ public:
virtual bool is_locale_right_to_left(const String &p_locale) override;
+ virtual int32_t name_to_tag(const String &p_name) const override;
+ virtual String tag_to_name(int32_t p_tag) const override;
+
/* Font interface */
- virtual RID create_font_system(const String &p_name, int p_base_size = 16) override;
- virtual RID create_font_resource(const String &p_filename, int p_base_size = 16) override;
- virtual RID create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16) override;
- virtual RID create_font_bitmap(float p_height, float p_ascent, int p_base_size = 16) override;
+ virtual RID create_font() override;
+
+ virtual void font_set_data(RID p_font_rid, const PackedByteArray &p_data) override;
+ virtual void font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) override;
+
+ virtual void font_set_antialiased(RID p_font_rid, bool p_antialiased) override;
+ virtual bool font_is_antialiased(RID p_font_rid) const override;
+
+ virtual void font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) override;
+ virtual bool font_is_multichannel_signed_distance_field(RID p_font_rid) const override;
+
+ virtual void font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) override;
+ virtual int font_get_msdf_pixel_range(RID p_font_rid) const override;
+
+ virtual void font_set_msdf_size(RID p_font_rid, int p_msdf_size) override;
+ virtual int font_get_msdf_size(RID p_font_rid) const override;
+
+ virtual void font_set_fixed_size(RID p_font_rid, int p_fixed_size) override;
+ virtual int font_get_fixed_size(RID p_font_rid) const override;
+
+ virtual void font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) override;
+ virtual bool font_is_force_autohinter(RID p_font_rid) const override;
+
+ virtual void font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) override;
+ virtual TextServer::Hinting font_get_hinting(RID p_font_rid) const override;
+
+ virtual void font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) override;
+ virtual Dictionary font_get_variation_coordinates(RID p_font_rid) const override;
+
+ virtual void font_set_oversampling(RID p_font_rid, real_t p_oversampling) override;
+ virtual real_t font_get_oversampling(RID p_font_rid) const override;
+
+ virtual Array font_get_size_cache_list(RID p_font_rid) const override;
+ virtual void font_clear_size_cache(RID p_font_rid) override;
+ virtual void font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) override;
+
+ virtual void font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) override;
+ virtual real_t font_get_ascent(RID p_font_rid, int p_size) const override;
+
+ virtual void font_set_descent(RID p_font_rid, int p_size, real_t p_descent) override;
+ virtual real_t font_get_descent(RID p_font_rid, int p_size) const override;
+
+ virtual void font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) override;
+ virtual real_t font_get_underline_position(RID p_font_rid, int p_size) const override;
+
+ virtual void font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) override;
+ virtual real_t font_get_underline_thickness(RID p_font_rid, int p_size) const override;
+
+ virtual void font_set_scale(RID p_font_rid, int p_size, real_t p_scale) override;
+ virtual real_t font_get_scale(RID p_font_rid, int p_size) const override;
+
+ virtual void font_set_spacing(RID p_font_rid, int p_size, SpacingType p_spacing, int p_value) override;
+ virtual int font_get_spacing(RID p_font_rid, int p_size, SpacingType p_spacing) const override;
- virtual void font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) override;
- virtual void font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override;
- virtual void font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) override;
+ virtual int font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const override;
+ virtual void font_clear_textures(RID p_font_rid, const Vector2i &p_size) override;
+ virtual void font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) override;
- virtual float font_get_height(RID p_font, int p_size) const override;
- virtual float font_get_ascent(RID p_font, int p_size) const override;
- virtual float font_get_descent(RID p_font, int p_size) const override;
+ virtual void font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) override;
+ virtual Ref<Image> font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const override;
- virtual float font_get_underline_position(RID p_font, int p_size) const override;
- virtual float font_get_underline_thickness(RID p_font, int p_size) const override;
+ virtual void font_set_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) override;
+ virtual PackedInt32Array font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const override;
- virtual int font_get_spacing_space(RID p_font) const override;
- virtual void font_set_spacing_space(RID p_font, int p_value) override;
+ virtual Array font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const override;
+ virtual void font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) override;
+ virtual void font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) override;
- virtual int font_get_spacing_glyph(RID p_font) const override;
- virtual void font_set_spacing_glyph(RID p_font, int p_value) override;
+ virtual Vector2 font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const override;
+ virtual void font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) override;
- virtual void font_set_antialiased(RID p_font, bool p_antialiased) override;
- virtual bool font_get_antialiased(RID p_font) const override;
+ virtual Vector2 font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override;
+ virtual void font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) override;
- virtual Dictionary font_get_feature_list(RID p_font) const override;
- virtual Dictionary font_get_variation_list(RID p_font) const override;
+ virtual Vector2 font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override;
+ virtual void font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) override;
- virtual void font_set_variation(RID p_font, const String &p_name, double p_value) override;
- virtual double font_get_variation(RID p_font, const String &p_name) const override;
+ virtual Rect2 font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override;
+ virtual void font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) override;
- virtual void font_set_hinting(RID p_font, Hinting p_hinting) override;
- virtual Hinting font_get_hinting(RID p_font) const override;
+ virtual int font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override;
+ virtual void font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) override;
- virtual void font_set_distance_field_hint(RID p_font, bool p_distance_field) override;
- virtual bool font_get_distance_field_hint(RID p_font) const override;
+ virtual bool font_get_glyph_contours(RID p_font, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override;
- virtual void font_set_force_autohinter(RID p_font, bool p_enabeld) override;
- virtual bool font_get_force_autohinter(RID p_font) const override;
+ virtual Array font_get_kerning_list(RID p_font_rid, int p_size) const override;
+ virtual void font_clear_kerning_map(RID p_font_rid, int p_size) override;
+ virtual void font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) override;
- virtual bool font_has_char(RID p_font, char32_t p_char) const override;
- virtual String font_get_supported_chars(RID p_font) const override;
+ virtual void font_set_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) override;
+ virtual Vector2 font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const override;
- virtual bool font_has_outline(RID p_font) const override;
- virtual float font_get_base_size(RID p_font) const override;
+ virtual int32_t font_get_glyph_index(RID p_font_rid, int p_size, char32_t p_char, char32_t p_variation_selector = 0) const override;
- virtual bool font_is_language_supported(RID p_font, const String &p_language) const override;
- virtual void font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) override;
- virtual bool font_get_language_support_override(RID p_font, const String &p_language) override;
- virtual void font_remove_language_support_override(RID p_font, const String &p_language) override;
- Vector<String> font_get_language_support_overrides(RID p_font) override;
+ virtual bool font_has_char(RID p_font_rid, char32_t p_char) const override;
+ virtual String font_get_supported_chars(RID p_font_rid) const override;
- virtual bool font_is_script_supported(RID p_font, const String &p_script) const override;
- virtual void font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) override;
- virtual bool font_get_script_support_override(RID p_font, const String &p_script) override;
- virtual void font_remove_script_support_override(RID p_font, const String &p_script) override;
- Vector<String> font_get_script_support_overrides(RID p_font) override;
+ virtual void font_render_range(RID p_font, const Vector2i &p_size, char32_t p_start, char32_t p_end) override;
+ virtual void font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) override;
- virtual uint32_t font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector = 0x0000) const override;
- virtual Vector2 font_get_glyph_advance(RID p_font, uint32_t p_index, int p_size) const override;
- virtual Vector2 font_get_glyph_kerning(RID p_font, uint32_t p_index_a, uint32_t p_index_b, int p_size) const override;
+ virtual void font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
+ virtual void font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
- virtual Vector2 font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
- virtual Vector2 font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
+ virtual bool font_is_language_supported(RID p_font_rid, const String &p_language) const override;
+ virtual void font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) override;
+ virtual bool font_get_language_support_override(RID p_font_rid, const String &p_language) override;
+ virtual void font_remove_language_support_override(RID p_font_rid, const String &p_language) override;
+ virtual Vector<String> font_get_language_support_overrides(RID p_font_rid) override;
- virtual bool font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override;
+ virtual bool font_is_script_supported(RID p_font_rid, const String &p_script) const override;
+ virtual void font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) override;
+ virtual bool font_get_script_support_override(RID p_font_rid, const String &p_script) override;
+ virtual void font_remove_script_support_override(RID p_font_rid, const String &p_script) override;
+ virtual Vector<String> font_get_script_support_overrides(RID p_font_rid) override;
- virtual float font_get_oversampling() const override;
- virtual void font_set_oversampling(float p_oversampling) override;
+ virtual Dictionary font_supported_feature_list(RID p_font_rid) const override;
+ virtual Dictionary font_supported_variation_list(RID p_font_rid) const override;
- virtual Vector<String> get_system_fonts() const override;
+ virtual real_t font_get_global_oversampling() const override;
+ virtual void font_set_global_oversampling(real_t p_oversampling) override;
/* Shaped text buffer interface */
@@ -160,14 +212,14 @@ public:
virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override;
virtual RID shaped_text_get_parent(RID p_shaped) const override;
- virtual float shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
- virtual float shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) override;
+ virtual real_t shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
+ virtual real_t shaped_text_tab_align(RID p_shaped, const Vector<real_t> &p_tab_stops) override;
virtual bool shaped_text_shape(RID p_shaped) override;
virtual bool shaped_text_update_breaks(RID p_shaped) override;
virtual bool shaped_text_update_justification_ops(RID p_shaped) override;
- virtual void shaped_text_overrun_trim_to_width(RID p_shaped, float p_width, uint8_t p_trim_flags) override;
+ virtual void shaped_text_overrun_trim_to_width(RID p_shaped, real_t p_width, uint8_t p_trim_flags) override;
virtual bool shaped_text_is_ready(RID p_shaped) const override;
@@ -176,18 +228,18 @@ public:
virtual Vector2i shaped_text_get_range(RID p_shaped) const override;
virtual Vector<Glyph> shaped_text_sort_logical(RID p_shaped) override;
- virtual Vector<Vector2i> shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<float> &p_width, int p_start = 0, bool p_once = true, uint8_t /*TextBreakFlag*/ p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const override;
- virtual Vector<Vector2i> shaped_text_get_line_breaks(RID p_shaped, float p_width, int p_start = 0, uint8_t p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const override;
+ virtual Vector<Vector2i> shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<real_t> &p_width, int p_start = 0, bool p_once = true, uint8_t /*TextBreakFlag*/ p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const override;
+ virtual Vector<Vector2i> shaped_text_get_line_breaks(RID p_shaped, real_t p_width, int p_start = 0, uint8_t p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const override;
virtual Vector<Vector2i> shaped_text_get_word_breaks(RID p_shaped, int p_grapheme_flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_PUNCTUATION) const override;
virtual Array shaped_text_get_objects(RID p_shaped) const override;
virtual Rect2 shaped_text_get_object_rect(RID p_shaped, Variant p_key) const override;
virtual Size2 shaped_text_get_size(RID p_shaped) const override;
- virtual float shaped_text_get_ascent(RID p_shaped) const override;
- virtual float shaped_text_get_descent(RID p_shaped) const override;
- virtual float shaped_text_get_width(RID p_shaped) const override;
- virtual float shaped_text_get_underline_position(RID p_shaped) const override;
- virtual float shaped_text_get_underline_thickness(RID p_shaped) const override;
+ virtual real_t shaped_text_get_ascent(RID p_shaped) const override;
+ virtual real_t shaped_text_get_descent(RID p_shaped) const override;
+ virtual real_t shaped_text_get_width(RID p_shaped) const override;
+ virtual real_t shaped_text_get_underline_position(RID p_shaped) const override;
+ virtual real_t shaped_text_get_underline_thickness(RID p_shaped) const override;
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;
diff --git a/modules/msdfgen/SCsub b/modules/msdfgen/SCsub
new file mode 100644
index 0000000000..227369f4bd
--- /dev/null
+++ b/modules/msdfgen/SCsub
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_msdfgen = env_modules.Clone()
+
+# Thirdparty source files
+
+thirdparty_obj = []
+
+if env["builtin_msdfgen"]:
+ env_msdfgen.disable_warnings()
+
+ thirdparty_dir = "#thirdparty/msdfgen/"
+ thirdparty_sources = [
+ "core/Contour.cpp",
+ "core/EdgeHolder.cpp",
+ "core/MSDFErrorCorrection.cpp",
+ "core/Projection.cpp",
+ "core/Scanline.cpp",
+ "core/Shape.cpp",
+ "core/SignedDistance.cpp",
+ "core/Vector2.cpp",
+ "core/contour-combiners.cpp",
+ "core/edge-coloring.cpp",
+ "core/edge-segments.cpp",
+ "core/edge-selectors.cpp",
+ "core/equation-solver.cpp",
+ "core/msdf-error-correction.cpp",
+ "core/msdfgen.cpp",
+ "core/rasterization.cpp",
+ "core/render-sdf.cpp",
+ "core/sdf-error-estimation.cpp",
+ "core/shape-description.cpp",
+ ]
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ env_msdfgen.Append(CPPPATH=["#thirdparty/freetype/include", "#thirdparty/msdfgen", "#thirdparty/nanosvg"])
+
+ lib = env_msdfgen.add_library("msdfgen_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 = []
+
+env_msdfgen.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)
diff --git a/modules/msdfgen/config.py b/modules/msdfgen/config.py
new file mode 100644
index 0000000000..653e466a74
--- /dev/null
+++ b/modules/msdfgen/config.py
@@ -0,0 +1,6 @@
+def can_build(env, platform):
+ return env.module_check_dependencies("msdfgen", ["freetype"])
+
+
+def configure(env):
+ pass
diff --git a/modules/msdfgen/register_types.cpp b/modules/msdfgen/register_types.cpp
new file mode 100644
index 0000000000..ad60a66d2d
--- /dev/null
+++ b/modules/msdfgen/register_types.cpp
@@ -0,0 +1,35 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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. */
+/*************************************************************************/
+
+#include "register_types.h"
+
+void register_msdfgen_types() {}
+
+void unregister_msdfgen_types() {}
diff --git a/modules/msdfgen/register_types.h b/modules/msdfgen/register_types.h
new file mode 100644
index 0000000000..fb776c14ed
--- /dev/null
+++ b/modules/msdfgen/register_types.h
@@ -0,0 +1,37 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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. */
+/*************************************************************************/
+
+#ifndef MSDFGEN_REGISTER_TYPES_H
+#define MSDFGEN_REGISTER_TYPES_H
+
+void register_msdfgen_types();
+void unregister_msdfgen_types();
+
+#endif // MSDFGEN_REGISTER_TYPES_H
diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub
index 1fcbb7aaca..6691f86e60 100644
--- a/modules/text_server_adv/SCsub
+++ b/modules/text_server_adv/SCsub
@@ -38,7 +38,8 @@ def make_icu_data(target, source, env):
# Thirdparty source files
thirdparty_obj = []
-freetype_enabled = env.module_check_dependencies("text_server_adv", ["freetype"])
+freetype_enabled = env.module_check_dependencies("text_server_adv", ["freetype"], True)
+msdngen_enabled = env.module_check_dependencies("text_server_adv", ["msdfgen"], True)
if env["builtin_harfbuzz"]:
env_harfbuzz = env_modules.Clone()
@@ -509,6 +510,13 @@ env_text_server_adv.Append(
]
)
+if msdngen_enabled:
+ env_text_server_adv.Append(
+ CPPPATH=[
+ "#thirdparty/msdfgen",
+ ]
+ )
+
if freetype_enabled:
env_text_server_adv.Append(
CPPPATH=[
diff --git a/modules/text_server_adv/bitmap_font_adv.cpp b/modules/text_server_adv/bitmap_font_adv.cpp
deleted file mode 100644
index 1731fb6223..0000000000
--- a/modules/text_server_adv/bitmap_font_adv.cpp
+++ /dev/null
@@ -1,585 +0,0 @@
-/*************************************************************************/
-/* bitmap_font_adv.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 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. */
-/*************************************************************************/
-
-#include "bitmap_font_adv.h"
-
-/*************************************************************************/
-/* hb_bmp_font_t HarfBuzz Bitmap font interface */
-/*************************************************************************/
-
-struct hb_bmp_font_t {
- BitmapFontDataAdvanced *face = nullptr;
- float font_size = 0.0;
- bool unref = false; /* Whether to destroy bm_face when done. */
-};
-
-static hb_bmp_font_t *_hb_bmp_font_create(BitmapFontDataAdvanced *p_face, float p_font_size, bool p_unref) {
- hb_bmp_font_t *bm_font = reinterpret_cast<hb_bmp_font_t *>(calloc(1, sizeof(hb_bmp_font_t)));
-
- if (!bm_font) {
- return nullptr;
- }
-
- bm_font->face = p_face;
- bm_font->font_size = p_font_size;
- bm_font->unref = p_unref;
-
- return bm_font;
-}
-
-static void _hb_bmp_font_destroy(void *data) {
- hb_bmp_font_t *bm_font = reinterpret_cast<hb_bmp_font_t *>(data);
- free(bm_font);
-}
-
-static hb_bool_t hb_bmp_get_nominal_glyph(hb_font_t *font, void *font_data, hb_codepoint_t unicode, hb_codepoint_t *glyph, void *user_data) {
- const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(font_data);
-
- if (!bm_font->face) {
- return false;
- }
-
- if (!bm_font->face->has_char(unicode)) {
- if (bm_font->face->has_char(0xF000u + unicode)) {
- *glyph = 0xF000u + unicode;
- return true;
- } else {
- return false;
- }
- }
-
- *glyph = unicode;
- return true;
-}
-
-static hb_position_t hb_bmp_get_glyph_h_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph, void *user_data) {
- const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(font_data);
-
- if (!bm_font->face) {
- return 0;
- }
-
- if (!bm_font->face->has_char(glyph)) {
- return 0;
- }
-
- return bm_font->face->get_advance(glyph, bm_font->font_size).x * 64;
-}
-
-static hb_position_t hb_bmp_get_glyph_v_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph, void *user_data) {
- const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(font_data);
-
- if (!bm_font->face) {
- return 0;
- }
-
- if (!bm_font->face->has_char(glyph)) {
- return 0;
- }
-
- return -bm_font->face->get_advance(glyph, bm_font->font_size).y * 64;
-}
-
-static hb_position_t hb_bmp_get_glyph_h_kerning(hb_font_t *font, void *font_data, hb_codepoint_t left_glyph, hb_codepoint_t right_glyph, void *user_data) {
- const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(font_data);
-
- if (!bm_font->face) {
- return 0;
- }
-
- if (!bm_font->face->has_char(left_glyph)) {
- return 0;
- }
-
- if (!bm_font->face->has_char(right_glyph)) {
- return 0;
- }
-
- return bm_font->face->get_kerning(left_glyph, right_glyph, bm_font->font_size).x * 64;
-}
-
-static hb_bool_t hb_bmp_get_glyph_v_origin(hb_font_t *font, void *font_data, hb_codepoint_t glyph, hb_position_t *x, hb_position_t *y, void *user_data) {
- const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(font_data);
-
- if (!bm_font->face) {
- return false;
- }
-
- if (!bm_font->face->has_char(glyph)) {
- return false;
- }
-
- *x = bm_font->face->get_advance(glyph, bm_font->font_size).x * 32;
- *y = bm_font->face->get_ascent(bm_font->font_size) * 64;
-
- return true;
-}
-
-static hb_bool_t hb_bmp_get_glyph_extents(hb_font_t *font, void *font_data, hb_codepoint_t glyph, hb_glyph_extents_t *extents, void *user_data) {
- const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(font_data);
-
- if (!bm_font->face) {
- return false;
- }
-
- if (!bm_font->face->has_char(glyph)) {
- return false;
- }
-
- extents->x_bearing = 0;
- extents->y_bearing = 0;
- extents->width = bm_font->face->get_size(glyph, bm_font->font_size).x * 64;
- extents->height = bm_font->face->get_size(glyph, bm_font->font_size).y * 64;
-
- return true;
-}
-
-static hb_bool_t hb_bmp_get_font_h_extents(hb_font_t *font, void *font_data, hb_font_extents_t *metrics, void *user_data) {
- const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(font_data);
-
- if (!bm_font->face) {
- return false;
- }
-
- metrics->ascender = bm_font->face->get_ascent(bm_font->font_size);
- metrics->descender = bm_font->face->get_descent(bm_font->font_size);
- metrics->line_gap = 0;
-
- return true;
-}
-
-static hb_font_funcs_t *funcs = nullptr;
-void hb_bmp_create_font_funcs() {
- funcs = hb_font_funcs_create();
-
- hb_font_funcs_set_font_h_extents_func(funcs, hb_bmp_get_font_h_extents, nullptr, nullptr);
- //hb_font_funcs_set_font_v_extents_func (funcs, hb_bmp_get_font_v_extents, nullptr, nullptr);
- hb_font_funcs_set_nominal_glyph_func(funcs, hb_bmp_get_nominal_glyph, nullptr, nullptr);
- //hb_font_funcs_set_variation_glyph_func (funcs, hb_bmp_get_variation_glyph, nullptr, nullptr);
- hb_font_funcs_set_glyph_h_advance_func(funcs, hb_bmp_get_glyph_h_advance, nullptr, nullptr);
- hb_font_funcs_set_glyph_v_advance_func(funcs, hb_bmp_get_glyph_v_advance, nullptr, nullptr);
- //hb_font_funcs_set_glyph_h_origin_func(funcs, hb_bmp_get_glyph_h_origin, nullptr, nullptr);
- hb_font_funcs_set_glyph_v_origin_func(funcs, hb_bmp_get_glyph_v_origin, nullptr, nullptr);
- hb_font_funcs_set_glyph_h_kerning_func(funcs, hb_bmp_get_glyph_h_kerning, nullptr, nullptr);
- //hb_font_funcs_set_glyph_v_kerning_func (funcs, hb_bmp_get_glyph_v_kerning, nullptr, nullptr);
- hb_font_funcs_set_glyph_extents_func(funcs, hb_bmp_get_glyph_extents, nullptr, nullptr);
- //hb_font_funcs_set_glyph_contour_point_func (funcs, hb_bmp_get_glyph_contour_point, nullptr, nullptr);
- //hb_font_funcs_set_glyph_name_func (funcs, hb_bmp_get_glyph_name, nullptr, nullptr);
- //hb_font_funcs_set_glyph_from_name_func (funcs, hb_bmp_get_glyph_from_name, nullptr, nullptr);
-
- hb_font_funcs_make_immutable(funcs);
-}
-
-void hb_bmp_free_font_funcs() {
- if (funcs != nullptr) {
- hb_font_funcs_destroy(funcs);
- funcs = nullptr;
- }
-}
-
-static void _hb_bmp_font_set_funcs(hb_font_t *p_font, BitmapFontDataAdvanced *p_face, int p_size, bool p_unref) {
- hb_font_set_funcs(p_font, funcs, _hb_bmp_font_create(p_face, p_size, p_unref), _hb_bmp_font_destroy);
-}
-
-hb_font_t *hb_bmp_font_create(BitmapFontDataAdvanced *p_face, int p_size, hb_destroy_func_t p_destroy) {
- hb_font_t *font;
- hb_face_t *face = hb_face_create(nullptr, 0);
-
- font = hb_font_create(face);
- hb_face_destroy(face);
- _hb_bmp_font_set_funcs(font, p_face, p_size, false);
- return font;
-}
-
-/*************************************************************************/
-/* BitmapFontDataAdvanced */
-/*************************************************************************/
-
-Error BitmapFontDataAdvanced::load_from_file(const String &p_filename, int p_base_size) {
- _THREAD_SAFE_METHOD_
- //fnt format used by angelcode bmfont
- //https://www.angelcode.com/products/bmfont/
-
- FileAccess *f = FileAccess::open(p_filename, FileAccess::READ);
- ERR_FAIL_COND_V_MSG(!f, ERR_FILE_NOT_FOUND, "Can't open font: " + p_filename + ".");
-
- while (true) {
- String line = f->get_line();
-
- int delimiter = line.find(" ");
- String type = line.substr(0, delimiter);
- int pos = delimiter + 1;
- Map<String, String> keys;
-
- while (pos < line.size() && line[pos] == ' ') {
- pos++;
- }
-
- while (pos < line.size()) {
- int eq = line.find("=", pos);
- if (eq == -1) {
- break;
- }
- String key = line.substr(pos, eq - pos);
- int end = -1;
- String value;
- if (line[eq + 1] == '"') {
- end = line.find("\"", eq + 2);
- if (end == -1) {
- break;
- }
- value = line.substr(eq + 2, end - 1 - eq - 1);
- pos = end + 1;
- } else {
- end = line.find(" ", eq + 1);
- if (end == -1) {
- end = line.size();
- }
- value = line.substr(eq + 1, end - eq);
- pos = end;
- }
-
- while (pos < line.size() && line[pos] == ' ') {
- pos++;
- }
-
- keys[key] = value;
- }
-
- if (type == "info") {
- if (keys.has("size")) {
- base_size = keys["size"].to_int();
- }
- } else if (type == "common") {
- if (keys.has("lineHeight")) {
- height = keys["lineHeight"].to_int();
- }
- if (keys.has("base")) {
- ascent = keys["base"].to_int();
- }
- } else if (type == "page") {
- if (keys.has("file")) {
- String base_dir = p_filename.get_base_dir();
- String file = base_dir.plus_file(keys["file"]);
- if (RenderingServer::get_singleton() != nullptr) {
- Ref<Texture2D> tex = ResourceLoader::load(file);
- if (tex.is_null()) {
- ERR_PRINT("Can't load font texture!");
- } else {
- ERR_FAIL_COND_V_MSG(tex.is_null(), ERR_FILE_CANT_READ, "It's not a reference to a valid Texture object.");
- textures.push_back(tex);
- }
- }
- }
- } else if (type == "char") {
- Character c;
- char32_t idx = 0;
- if (keys.has("id")) {
- idx = keys["id"].to_int();
- }
- if (keys.has("x")) {
- c.rect.position.x = keys["x"].to_int();
- }
- if (keys.has("y")) {
- c.rect.position.y = keys["y"].to_int();
- }
- if (keys.has("width")) {
- c.rect.size.width = keys["width"].to_int();
- }
- if (keys.has("height")) {
- c.rect.size.height = keys["height"].to_int();
- }
- if (keys.has("xoffset")) {
- c.align.x = keys["xoffset"].to_int();
- }
- if (keys.has("yoffset")) {
- c.align.y = keys["yoffset"].to_int();
- }
- if (keys.has("page")) {
- c.texture_idx = keys["page"].to_int();
- }
- if (keys.has("xadvance")) {
- c.advance.x = keys["xadvance"].to_int();
- }
- if (keys.has("yadvance")) {
- c.advance.x = keys["yadvance"].to_int();
- }
- if (c.advance.x < 0) {
- c.advance.x = c.rect.size.width + 1;
- }
- if (c.advance.y < 0) {
- c.advance.y = c.rect.size.height + 1;
- }
- char_map[idx] = c;
- } else if (type == "kerning") {
- KerningPairKey kpk;
- float k = 0.0;
- if (keys.has("first")) {
- kpk.A = keys["first"].to_int();
- }
- if (keys.has("second")) {
- kpk.B = keys["second"].to_int();
- }
- if (keys.has("amount")) {
- k = keys["amount"].to_int();
- }
- kerning_map[kpk] = k;
- }
-
- if (f->eof_reached()) {
- break;
- }
- }
- if (base_size == 0) {
- base_size = height;
- }
-
- if (hb_handle) {
- hb_font_destroy(hb_handle);
- }
- hb_handle = hb_bmp_font_create(this, base_size, nullptr);
- valid = true;
-
- memdelete(f);
- return OK;
-}
-
-Error BitmapFontDataAdvanced::bitmap_new(float p_height, float p_ascent, int p_base_size) {
- height = p_height;
- ascent = p_ascent;
-
- base_size = p_base_size;
- if (base_size == 0) {
- base_size = height;
- }
-
- char_map.clear();
- textures.clear();
- kerning_map.clear();
- if (hb_handle) {
- hb_font_destroy(hb_handle);
- }
- hb_handle = hb_bmp_font_create(this, base_size, nullptr);
- valid = true;
-
- return OK;
-}
-
-void BitmapFontDataAdvanced::bitmap_add_texture(const Ref<Texture> &p_texture) {
- ERR_FAIL_COND(!valid);
- ERR_FAIL_COND_MSG(p_texture.is_null(), "It's not a reference to a valid Texture object.");
-
- textures.push_back(p_texture);
-}
-
-void BitmapFontDataAdvanced::bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) {
- ERR_FAIL_COND(!valid);
-
- Character chr;
- chr.rect = p_rect;
- chr.texture_idx = p_texture_idx;
- if (p_advance < 0) {
- chr.advance.x = chr.rect.size.x;
- } else {
- chr.advance.x = p_advance;
- }
- chr.align = p_align;
- char_map[p_char] = chr;
-}
-
-void BitmapFontDataAdvanced::bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) {
- ERR_FAIL_COND(!valid);
-
- KerningPairKey kpk;
- kpk.A = p_A;
- kpk.B = p_B;
-
- if (p_kerning == 0 && kerning_map.has(kpk)) {
- kerning_map.erase(kpk);
- } else {
- kerning_map[kpk] = p_kerning;
- }
-}
-
-float BitmapFontDataAdvanced::get_height(int p_size) const {
- ERR_FAIL_COND_V(!valid, 0.f);
- return height * (float(p_size) / float(base_size));
-}
-
-float BitmapFontDataAdvanced::get_ascent(int p_size) const {
- ERR_FAIL_COND_V(!valid, 0.f);
- return ascent * (float(p_size) / float(base_size));
-}
-
-float BitmapFontDataAdvanced::get_descent(int p_size) const {
- ERR_FAIL_COND_V(!valid, 0.f);
- return (height - ascent) * (float(p_size) / float(base_size));
-}
-
-float BitmapFontDataAdvanced::get_underline_position(int p_size) const {
- ERR_FAIL_COND_V(!valid, 0.f);
- return 2 * (float(p_size) / float(base_size));
-}
-
-float BitmapFontDataAdvanced::get_underline_thickness(int p_size) const {
- ERR_FAIL_COND_V(!valid, 0.f);
- return 1 * (float(p_size) / float(base_size));
-}
-
-void BitmapFontDataAdvanced::set_distance_field_hint(bool p_distance_field) {
- distance_field_hint = p_distance_field;
-}
-
-bool BitmapFontDataAdvanced::get_distance_field_hint() const {
- return distance_field_hint;
-}
-
-float BitmapFontDataAdvanced::get_base_size() const {
- return base_size;
-}
-
-hb_font_t *BitmapFontDataAdvanced::get_hb_handle(int p_size) {
- _THREAD_SAFE_METHOD_
- ERR_FAIL_COND_V(!valid, nullptr);
- return hb_handle;
-}
-
-bool BitmapFontDataAdvanced::has_char(char32_t p_char) const {
- _THREAD_SAFE_METHOD_
- ERR_FAIL_COND_V(!valid, false);
- return char_map.has(p_char);
-}
-
-String BitmapFontDataAdvanced::get_supported_chars() const {
- _THREAD_SAFE_METHOD_
- ERR_FAIL_COND_V(!valid, String());
- String chars;
- const uint32_t *k = nullptr;
- while ((k = char_map.next(k))) {
- chars += char32_t(*k);
- }
- return chars;
-}
-
-Vector2 BitmapFontDataAdvanced::get_advance(uint32_t p_char, int p_size) const {
- _THREAD_SAFE_METHOD_
- ERR_FAIL_COND_V(!valid, Vector2());
- const Character *c = char_map.getptr(p_char);
- ERR_FAIL_COND_V(c == nullptr, Vector2());
-
- return c->advance * (float(p_size) / float(base_size));
-}
-
-Vector2 BitmapFontDataAdvanced::get_align(uint32_t p_char, int p_size) const {
- _THREAD_SAFE_METHOD_
- ERR_FAIL_COND_V(!valid, Vector2());
- const Character *c = char_map.getptr(p_char);
- ERR_FAIL_COND_V(c == nullptr, Vector2());
-
- return c->align * (float(p_size) / float(base_size));
-}
-
-Vector2 BitmapFontDataAdvanced::get_size(uint32_t p_char, int p_size) const {
- _THREAD_SAFE_METHOD_
- ERR_FAIL_COND_V(!valid, Vector2());
- const Character *c = char_map.getptr(p_char);
- ERR_FAIL_COND_V(c == nullptr, Vector2());
-
- return c->rect.size * (float(p_size) / float(base_size));
-}
-
-float BitmapFontDataAdvanced::get_font_scale(int p_size) const {
- return float(p_size) / float(base_size);
-}
-
-Vector2 BitmapFontDataAdvanced::get_kerning(uint32_t p_char, uint32_t p_next, int p_size) const {
- _THREAD_SAFE_METHOD_
- ERR_FAIL_COND_V(!valid, Vector2());
- KerningPairKey kpk;
- kpk.A = p_char;
- kpk.B = p_next;
-
- const Map<KerningPairKey, int>::Element *E = kerning_map.find(kpk);
- if (E) {
- return Vector2(-E->get() * (float(p_size) / float(base_size)), 0.f);
- } else {
- return Vector2();
- }
-}
-
-Vector2 BitmapFontDataAdvanced::draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
- _THREAD_SAFE_METHOD_
- if (p_index == 0) {
- return Vector2();
- }
- ERR_FAIL_COND_V(!valid, Vector2());
- const Character *c = char_map.getptr(p_index);
-
- ERR_FAIL_COND_V(c == nullptr, Vector2());
- ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), Vector2());
- if (c->texture_idx != -1) {
- Point2i cpos = p_pos;
- cpos += (c->align + Vector2(0, -ascent)) * (float(p_size) / float(base_size));
- Size2i csize = c->rect.size * (float(p_size) / float(base_size));
- if (RenderingServer::get_singleton() != nullptr) {
- //if (distance_field_hint) { // Not implemented.
- // RenderingServer::get_singleton()->canvas_item_set_distance_field_mode(p_canvas, true);
- //}
- RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), textures[c->texture_idx]->get_rid(), c->rect, p_color, false, false);
- //if (distance_field_hint) {
- // RenderingServer::get_singleton()->canvas_item_set_distance_field_mode(p_canvas, false);
- //}
- }
- }
-
- return c->advance * (float(p_size) / float(base_size));
-}
-
-Vector2 BitmapFontDataAdvanced::draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
- _THREAD_SAFE_METHOD_
- if (p_index == 0) {
- return Vector2();
- }
- ERR_FAIL_COND_V(!valid, Vector2());
- const Character *c = char_map.getptr(p_index);
-
- ERR_FAIL_COND_V(c == nullptr, Vector2());
- ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), Vector2());
-
- // Not supported, return advance for compatibility.
-
- return c->advance * (float(p_size) / float(base_size));
-}
-
-BitmapFontDataAdvanced::~BitmapFontDataAdvanced() {
- if (hb_handle) {
- hb_font_destroy(hb_handle);
- }
-}
diff --git a/modules/text_server_adv/bitmap_font_adv.h b/modules/text_server_adv/bitmap_font_adv.h
deleted file mode 100644
index 7b620021e1..0000000000
--- a/modules/text_server_adv/bitmap_font_adv.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/*************************************************************************/
-/* bitmap_font_adv.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 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. */
-/*************************************************************************/
-
-#ifndef BITMAP_FONT_ADV_H
-#define BITMAP_FONT_ADV_H
-
-#include "font_adv.h"
-
-void hb_bmp_create_font_funcs();
-void hb_bmp_free_font_funcs();
-
-struct BitmapFontDataAdvanced : public FontDataAdvanced {
- _THREAD_SAFE_CLASS_
-
-private:
- Vector<Ref<Texture2D>> textures;
-
- struct Character {
- int texture_idx = 0;
- Rect2 rect;
- Vector2 align;
- Vector2 advance = Vector2(-1, -1);
- };
-
- struct KerningPairKey {
- union {
- struct {
- uint32_t A, B;
- };
-
- uint64_t pair = 0;
- };
-
- _FORCE_INLINE_ bool operator<(const KerningPairKey &p_r) const { return pair < p_r.pair; }
- };
-
- HashMap<uint32_t, Character> char_map;
- Map<KerningPairKey, int> kerning_map;
- hb_font_t *hb_handle = nullptr;
-
- float height = 0.f;
- float ascent = 0.f;
- int base_size = 0;
- bool distance_field_hint = false;
-
-public:
- virtual void clear_cache() override{};
-
- virtual Error load_from_file(const String &p_filename, int p_base_size) override;
- virtual Error bitmap_new(float p_height, float p_ascent, int p_base_size) override;
-
- virtual void bitmap_add_texture(const Ref<Texture> &p_texture) override;
- virtual void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override;
- virtual void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) override;
-
- virtual float get_height(int p_size) const override;
- virtual float get_ascent(int p_size) const override;
- virtual float get_descent(int p_size) const override;
-
- virtual float get_underline_position(int p_size) const override;
- virtual float get_underline_thickness(int p_size) const override;
-
- virtual void set_antialiased(bool p_antialiased) override{};
- virtual bool get_antialiased() const override { return false; };
-
- virtual void set_hinting(TextServer::Hinting p_hinting) override{};
- virtual TextServer::Hinting get_hinting() const override { return TextServer::HINTING_NONE; };
-
- virtual void set_distance_field_hint(bool p_distance_field) override;
- virtual bool get_distance_field_hint() const override;
-
- virtual void set_force_autohinter(bool p_enabeld) override{};
- virtual bool get_force_autohinter() const override { return false; };
-
- virtual bool has_outline() const override { return false; };
- virtual float get_base_size() const override;
- virtual float get_font_scale(int p_size) const override;
-
- virtual hb_font_t *get_hb_handle(int p_size) override;
-
- virtual bool has_char(char32_t p_char) const override;
- virtual String get_supported_chars() const override;
-
- virtual Vector2 get_advance(uint32_t p_char, int p_size) const override;
- Vector2 get_align(uint32_t p_char, int p_size) const;
- Vector2 get_size(uint32_t p_char, int p_size) const;
- virtual Vector2 get_kerning(uint32_t p_char, uint32_t p_next, int p_size) const override;
- virtual uint32_t get_glyph_index(char32_t p_char, char32_t p_variation_selector) const override { return (uint32_t)p_char; };
-
- virtual Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override;
- virtual Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override;
-
- virtual ~BitmapFontDataAdvanced();
-};
-
-#endif // BITMAP_FONT_ADV_H
diff --git a/modules/text_server_adv/dynamic_font_adv.cpp b/modules/text_server_adv/dynamic_font_adv.cpp
deleted file mode 100644
index 62eedebb59..0000000000
--- a/modules/text_server_adv/dynamic_font_adv.cpp
+++ /dev/null
@@ -1,1030 +0,0 @@
-/*************************************************************************/
-/* dynamic_font_adv.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 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. */
-/*************************************************************************/
-
-#include "dynamic_font_adv.h"
-
-#ifdef MODULE_FREETYPE_ENABLED
-
-#include FT_STROKER_H
-#include FT_ADVANCES_H
-#include FT_MULTIPLE_MASTERS_H
-
-DynamicFontDataAdvanced::DataAtSize *DynamicFontDataAdvanced::get_data_for_size(int p_size, int p_outline_size) {
- ERR_FAIL_COND_V(!valid, nullptr);
- ERR_FAIL_COND_V(p_size < 0 || p_size > UINT16_MAX, nullptr);
- ERR_FAIL_COND_V(p_outline_size < 0 || p_outline_size > UINT16_MAX, nullptr);
-
- CacheID id;
- id.size = p_size;
- id.outline_size = p_outline_size;
-
- DataAtSize *fds = nullptr;
- Map<CacheID, DataAtSize *>::Element *E = nullptr;
- if (p_outline_size != 0) {
- E = size_cache_outline.find(id);
- } else {
- E = size_cache.find(id);
- }
-
- if (E != nullptr) {
- fds = E->get();
- } else {
- if (font_mem == nullptr && font_path != String()) {
- if (!font_mem_cache.is_empty()) {
- font_mem = font_mem_cache.ptr();
- font_mem_size = font_mem_cache.size();
- } else {
- FileAccess *f = FileAccess::open(font_path, FileAccess::READ);
- if (!f) {
- ERR_FAIL_V_MSG(nullptr, "Cannot open font file '" + font_path + "'.");
- }
-
- uint64_t len = f->get_length();
- font_mem_cache.resize(len);
- f->get_buffer(font_mem_cache.ptrw(), len);
- font_mem = font_mem_cache.ptr();
- font_mem_size = len;
- f->close();
- }
- }
-
- int error = 0;
- fds = memnew(DataAtSize);
- if (font_mem) {
- memset(&fds->stream, 0, sizeof(FT_StreamRec));
- fds->stream.base = (unsigned char *)font_mem;
- fds->stream.size = font_mem_size;
- fds->stream.pos = 0;
-
- FT_Open_Args fargs;
- memset(&fargs, 0, sizeof(FT_Open_Args));
- fargs.memory_base = (unsigned char *)font_mem;
- fargs.memory_size = font_mem_size;
- fargs.flags = FT_OPEN_MEMORY;
- fargs.stream = &fds->stream;
- error = FT_Open_Face(library, &fargs, 0, &fds->face);
-
- } else {
- memdelete(fds);
- ERR_FAIL_V_MSG(nullptr, "DynamicFont uninitialized.");
- }
-
- if (error == FT_Err_Unknown_File_Format) {
- memdelete(fds);
- ERR_FAIL_V_MSG(nullptr, "Unknown font format.");
- } else if (error) {
- memdelete(fds);
- ERR_FAIL_V_MSG(nullptr, "Error loading font.");
- }
-
- oversampling = TS->font_get_oversampling();
-
- if (FT_HAS_COLOR(fds->face) && fds->face->num_fixed_sizes > 0) {
- int best_match = 0;
- int diff = ABS(p_size - ((int64_t)fds->face->available_sizes[0].width));
- fds->scale_color_font = float(p_size * oversampling) / fds->face->available_sizes[0].width;
- for (int i = 1; i < fds->face->num_fixed_sizes; i++) {
- int ndiff = ABS(p_size - ((int64_t)fds->face->available_sizes[i].width));
- if (ndiff < diff) {
- best_match = i;
- diff = ndiff;
- fds->scale_color_font = float(p_size * oversampling) / fds->face->available_sizes[i].width;
- }
- }
- FT_Select_Size(fds->face, best_match);
- } else {
- FT_Set_Pixel_Sizes(fds->face, 0, p_size * oversampling);
- }
-
- fds->size = p_size;
- fds->ascent = (fds->face->size->metrics.ascender / 64.0) / oversampling * fds->scale_color_font;
- fds->descent = (-fds->face->size->metrics.descender / 64.0) / oversampling * fds->scale_color_font;
- fds->underline_position = (-FT_MulFix(fds->face->underline_position, fds->face->size->metrics.y_scale) / 64.0) / oversampling * fds->scale_color_font;
- fds->underline_thickness = (FT_MulFix(fds->face->underline_thickness, fds->face->size->metrics.y_scale) / 64.0) / oversampling * fds->scale_color_font;
-
- //Load os2 TTF table
- fds->os2 = (TT_OS2 *)FT_Get_Sfnt_Table(fds->face, FT_SFNT_OS2);
-
- fds->hb_handle = hb_ft_font_create(fds->face, nullptr);
- if (fds->hb_handle == nullptr) {
- memdelete(fds);
- ERR_FAIL_V_MSG(nullptr, "Error loading HB font.");
- }
-
- if (p_outline_size != 0) {
- size_cache_outline[id] = fds;
- } else {
- size_cache[id] = fds;
- }
-
- // Write variations.
- if (fds->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
- FT_MM_Var *amaster;
-
- FT_Get_MM_Var(fds->face, &amaster);
-
- Vector<hb_variation_t> hb_vars;
- Vector<FT_Fixed> coords;
- coords.resize(amaster->num_axis);
-
- FT_Get_Var_Design_Coordinates(fds->face, coords.size(), coords.ptrw());
-
- for (FT_UInt i = 0; i < amaster->num_axis; i++) {
- hb_variation_t var;
-
- // Reset to default.
- var.tag = amaster->axis[i].tag;
- var.value = (double)amaster->axis[i].def / 65536.f;
- coords.write[i] = amaster->axis[i].def;
-
- if (variations.has(var.tag)) {
- var.value = variations[var.tag];
- coords.write[i] = CLAMP(variations[var.tag] * 65536.f, amaster->axis[i].minimum, amaster->axis[i].maximum);
- }
-
- hb_vars.push_back(var);
- }
-
- FT_Set_Var_Design_Coordinates(fds->face, coords.size(), coords.ptrw());
- hb_font_set_variations(fds->hb_handle, hb_vars.is_empty() ? nullptr : &hb_vars[0], hb_vars.size());
-
- FT_Done_MM_Var(library, amaster);
- }
- }
- return fds;
-}
-
-Dictionary DynamicFontDataAdvanced::get_variation_list() const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(base_size);
- if (fds == nullptr) {
- return Dictionary();
- }
-
- Dictionary ret;
- // Read variations.
- if (fds->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
- FT_MM_Var *amaster;
-
- FT_Get_MM_Var(fds->face, &amaster);
-
- for (FT_UInt i = 0; i < amaster->num_axis; i++) {
- ret[(int32_t)amaster->axis[i].tag] = Vector3i(amaster->axis[i].minimum / 65536, amaster->axis[i].maximum / 65536, amaster->axis[i].def / 65536);
- }
-
- FT_Done_MM_Var(library, amaster);
- }
- return ret;
-}
-
-void DynamicFontDataAdvanced::set_variation(const String &p_name, double p_value) {
- _THREAD_SAFE_METHOD_
- int32_t tag = TS->name_to_tag(p_name);
- if (!variations.has(tag) || (variations[tag] != p_value)) {
- variations[tag] = p_value;
- clear_cache();
- }
-}
-
-double DynamicFontDataAdvanced::get_variation(const String &p_name) const {
- _THREAD_SAFE_METHOD_
- int32_t tag = TS->name_to_tag(p_name);
- if (!variations.has(tag)) {
- return 0.f;
- }
- return variations[tag];
-}
-
-Dictionary DynamicFontDataAdvanced::get_feature_list() const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(base_size);
- if (fds == nullptr) {
- return Dictionary();
- }
-
- Dictionary out;
- // Read feature flags.
- unsigned int count = hb_ot_layout_table_get_feature_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GSUB, 0, nullptr, nullptr);
- if (count != 0) {
- hb_tag_t *feature_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
- hb_ot_layout_table_get_feature_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GSUB, 0, &count, feature_tags);
- for (unsigned int i = 0; i < count; i++) {
- out[feature_tags[i]] = 1;
- }
- memfree(feature_tags);
- }
- count = hb_ot_layout_table_get_feature_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GPOS, 0, nullptr, nullptr);
- if (count != 0) {
- hb_tag_t *feature_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
- hb_ot_layout_table_get_feature_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GPOS, 0, &count, feature_tags);
- for (unsigned int i = 0; i < count; i++) {
- out[feature_tags[i]] = 1;
- }
- memfree(feature_tags);
- }
-
- return out;
-}
-
-DynamicFontDataAdvanced::TexturePosition DynamicFontDataAdvanced::find_texture_pos_for_glyph(DynamicFontDataAdvanced::DataAtSize *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) {
- TexturePosition ret;
- ret.index = -1;
-
- int mw = p_width;
- int mh = p_height;
-
- for (int i = 0; i < p_data->textures.size(); i++) {
- const CharTexture &ct = p_data->textures[i];
-
- if (RenderingServer::get_singleton() != nullptr) {
- if (ct.texture->get_format() != p_image_format) {
- continue;
- }
- }
-
- if (mw > ct.texture_size || mh > ct.texture_size) { //too big for this texture
- continue;
- }
-
- ret.y = 0x7FFFFFFF;
- ret.x = 0;
-
- for (int j = 0; j < ct.texture_size - 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_size) {
- continue; //fail, could not fit it here
- }
-
- 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 * oversampling * 8, 256);
- if (mw > texsize) {
- texsize = mw; //special case, adapt to it?
- }
- if (mh > texsize) {
- texsize = mh; //special case, adapt to it?
- }
-
- texsize = next_power_of_2(texsize);
-
- texsize = MIN(texsize, 4096);
-
- CharTexture tex;
- tex.texture_size = texsize;
- tex.imgdata.resize(texsize * texsize * p_color_size); //grayscale alpha
-
- {
- //zero texture
- uint8_t *w = tex.imgdata.ptrw();
- ERR_FAIL_COND_V(texsize * texsize * p_color_size > tex.imgdata.size(), ret);
- // Initialize the texture to all-white pixels to prevent artifacts when the
- // font is displayed at a non-default scale with filtering enabled.
- if (p_color_size == 2) {
- for (int i = 0; i < texsize * texsize * p_color_size; i += 2) { // FORMAT_LA8
- w[i + 0] = 255;
- w[i + 1] = 0;
- }
- } else if (p_color_size == 4) {
- for (int i = 0; i < texsize * texsize * p_color_size; i += 4) { // FORMAT_RGBA8
- w[i + 0] = 255;
- w[i + 1] = 255;
- w[i + 2] = 255;
- w[i + 3] = 0;
- }
- } else {
- ERR_FAIL_V(ret);
- }
- }
- tex.offsets.resize(texsize);
- for (int i = 0; i < texsize; i++) { //zero offsets
- tex.offsets.write[i] = 0;
- }
-
- p_data->textures.push_back(tex);
- ret.index = p_data->textures.size() - 1;
- }
-
- return ret;
-}
-
-DynamicFontDataAdvanced::Character DynamicFontDataAdvanced::Character::not_found() {
- Character ch;
- return ch;
-}
-
-DynamicFontDataAdvanced::Character DynamicFontDataAdvanced::bitmap_to_character(DynamicFontDataAdvanced::DataAtSize *p_data, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) {
- int w = bitmap.width;
- int h = bitmap.rows;
-
- int mw = w + rect_margin * 2;
- int mh = h + rect_margin * 2;
-
- ERR_FAIL_COND_V(mw > 4096, Character::not_found());
- ERR_FAIL_COND_V(mh > 4096, Character::not_found());
-
- 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;
-
- TexturePosition tex_pos = find_texture_pos_for_glyph(p_data, color_size, require_format, mw, mh);
- ERR_FAIL_COND_V(tex_pos.index < 0, Character::not_found());
-
- //fit character in char texture
-
- CharTexture &tex = p_data->textures.write[tex_pos.index];
-
- {
- uint8_t *wr = tex.imgdata.ptrw();
-
- for (int i = 0; i < h; i++) {
- for (int j = 0; j < w; j++) {
- int ofs = ((i + tex_pos.y + rect_margin) * tex.texture_size + j + tex_pos.x + rect_margin) * color_size;
- ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), Character::not_found());
- switch (bitmap.pixel_mode) {
- case FT_PIXEL_MODE_MONO: {
- int byte = i * bitmap.pitch + (j >> 3);
- int bit = 1 << (7 - (j % 8));
- wr[ofs + 0] = 255; //grayscale as 1
- wr[ofs + 1] = (bitmap.buffer[byte] & bit) ? 255 : 0;
- } break;
- case FT_PIXEL_MODE_GRAY:
- wr[ofs + 0] = 255; //grayscale as 1
- wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j];
- break;
- case FT_PIXEL_MODE_BGRA: {
- int ofs_color = i * bitmap.pitch + (j << 2);
- wr[ofs + 2] = bitmap.buffer[ofs_color + 0];
- wr[ofs + 1] = bitmap.buffer[ofs_color + 1];
- wr[ofs + 0] = bitmap.buffer[ofs_color + 2];
- wr[ofs + 3] = bitmap.buffer[ofs_color + 3];
- } break;
- // TODO: FT_PIXEL_MODE_LCD
- default:
- ERR_FAIL_V_MSG(Character::not_found(), "Font uses unsupported pixel format: " + itos(bitmap.pixel_mode) + ".");
- break;
- }
- }
- }
- }
-
- //blit to image and texture
- {
- if (RenderingServer::get_singleton() != nullptr) {
- Ref<Image> img = memnew(Image(tex.texture_size, tex.texture_size, 0, require_format, tex.imgdata));
-
- if (tex.texture.is_null()) {
- tex.texture.instantiate();
- tex.texture->create_from_image(img);
- } else {
- tex.texture->update(img); //update
- }
- }
- }
-
- // update height array
- for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
- tex.offsets.write[k] = tex_pos.y + mh;
- }
-
- Character chr;
- chr.align = (Vector2(xofs, -yofs) * p_data->scale_color_font / oversampling).round();
- chr.advance = (advance * p_data->scale_color_font / oversampling).round();
- chr.texture_idx = tex_pos.index;
- chr.found = true;
-
- chr.rect_uv = Rect2(tex_pos.x + rect_margin, tex_pos.y + rect_margin, w, h);
- chr.rect = chr.rect_uv;
- chr.rect.position /= oversampling;
- chr.rect.size *= (p_data->scale_color_font / oversampling);
- return chr;
-}
-
-void DynamicFontDataAdvanced::update_glyph(int p_size, uint32_t p_index) {
- DataAtSize *fds = get_data_for_size(p_size, false);
- ERR_FAIL_COND(fds == nullptr);
-
- if (fds->glyph_map.has(p_index)) {
- return;
- }
-
- Character character = Character::not_found();
- FT_GlyphSlot slot = fds->face->glyph;
-
- if (p_index == 0) {
- fds->glyph_map[p_index] = character;
- return;
- }
-
- int ft_hinting;
- switch (hinting) {
- case TextServer::HINTING_NONE:
- ft_hinting = FT_LOAD_NO_HINTING;
- break;
- case TextServer::HINTING_LIGHT:
- ft_hinting = FT_LOAD_TARGET_LIGHT;
- break;
- default:
- ft_hinting = FT_LOAD_TARGET_NORMAL;
- break;
- }
-
- FT_Fixed v, h;
- FT_Get_Advance(fds->face, p_index, FT_HAS_COLOR(fds->face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting, &h);
- FT_Get_Advance(fds->face, p_index, FT_HAS_COLOR(fds->face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting | FT_LOAD_VERTICAL_LAYOUT, &v);
-
- int error = FT_Load_Glyph(fds->face, p_index, FT_HAS_COLOR(fds->face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting);
- if (error) {
- fds->glyph_map[p_index] = character;
- return;
- }
-
- error = FT_Render_Glyph(fds->face->glyph, antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
- if (!error) {
- character = bitmap_to_character(fds, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0);
- }
-
- fds->glyph_map[p_index] = character;
-}
-
-void DynamicFontDataAdvanced::update_glyph_outline(int p_size, int p_outline_size, uint32_t p_index) {
- DataAtSize *fds = get_data_for_size(p_size, p_outline_size);
- ERR_FAIL_COND(fds == nullptr);
-
- if (fds->glyph_map.has(p_index)) {
- return;
- }
-
- Character character = Character::not_found();
- if (p_index == 0) {
- fds->glyph_map[p_index] = character;
- return;
- }
-
- int error = FT_Load_Glyph(fds->face, p_index, FT_LOAD_NO_BITMAP | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0));
- if (error) {
- fds->glyph_map[p_index] = character;
- return;
- }
-
- FT_Stroker stroker;
- if (FT_Stroker_New(library, &stroker) != 0) {
- fds->glyph_map[p_index] = character;
- return;
- }
-
- FT_Stroker_Set(stroker, (int)(p_outline_size * oversampling * 64.0), FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0);
- FT_Glyph glyph;
- FT_BitmapGlyph glyph_bitmap;
-
- if (FT_Get_Glyph(fds->face->glyph, &glyph) != 0) {
- goto cleanup_stroker;
- }
- if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) {
- goto cleanup_glyph;
- }
- if (FT_Glyph_To_Bitmap(&glyph, antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1) != 0) {
- goto cleanup_glyph;
- }
-
- glyph_bitmap = (FT_BitmapGlyph)glyph;
- character = bitmap_to_character(fds, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2());
-
-cleanup_glyph:
- FT_Done_Glyph(glyph);
-cleanup_stroker:
- FT_Stroker_Done(stroker);
-
- fds->glyph_map[p_index] = character;
-}
-
-void DynamicFontDataAdvanced::clear_cache() {
- _THREAD_SAFE_METHOD_
- for (Map<CacheID, DataAtSize *>::Element *E = size_cache.front(); E; E = E->next()) {
- memdelete(E->get());
- }
- size_cache.clear();
- for (Map<CacheID, DataAtSize *>::Element *E = size_cache_outline.front(); E; E = E->next()) {
- memdelete(E->get());
- }
- size_cache_outline.clear();
-}
-
-Error DynamicFontDataAdvanced::load_from_file(const String &p_filename, int p_base_size) {
- _THREAD_SAFE_METHOD_
- if (library == nullptr) {
- int error = FT_Init_FreeType(&library);
- ERR_FAIL_COND_V_MSG(error != 0, ERR_CANT_CREATE, "Error initializing FreeType.");
- }
- clear_cache();
-
- font_path = p_filename;
- base_size = p_base_size;
-
- valid = true;
- DataAtSize *fds = get_data_for_size(base_size); // load base size.
- if (fds == nullptr) {
- valid = false;
- ERR_FAIL_V(ERR_CANT_CREATE);
- }
-
- return OK;
-}
-
-Error DynamicFontDataAdvanced::load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) {
- _THREAD_SAFE_METHOD_
- if (library == nullptr) {
- int error = FT_Init_FreeType(&library);
- ERR_FAIL_COND_V_MSG(error != 0, ERR_CANT_CREATE, "Error initializing FreeType.");
- }
- clear_cache();
-
- font_mem = p_data;
- font_mem_size = p_size;
- base_size = p_base_size;
-
- valid = true;
- DataAtSize *fds = get_data_for_size(base_size); // load base size.
- if (fds == nullptr) {
- valid = false;
- ERR_FAIL_V(ERR_CANT_CREATE);
- }
-
- return OK;
-}
-
-float DynamicFontDataAdvanced::get_height(int p_size) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size);
- ERR_FAIL_COND_V(fds == nullptr, 0.f);
- return fds->ascent + fds->descent;
-}
-
-float DynamicFontDataAdvanced::get_ascent(int p_size) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size);
- ERR_FAIL_COND_V(fds == nullptr, 0.f);
- return fds->ascent;
-}
-
-float DynamicFontDataAdvanced::get_descent(int p_size) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size);
- ERR_FAIL_COND_V(fds == nullptr, 0.f);
- return fds->descent;
-}
-
-float DynamicFontDataAdvanced::get_underline_position(int p_size) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size);
- ERR_FAIL_COND_V(fds == nullptr, 0.f);
- return fds->underline_position;
-}
-
-float DynamicFontDataAdvanced::get_underline_thickness(int p_size) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size);
- ERR_FAIL_COND_V(fds == nullptr, 0.f);
- return fds->underline_thickness;
-}
-
-bool DynamicFontDataAdvanced::is_script_supported(uint32_t p_script) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(base_size);
- ERR_FAIL_COND_V(fds == nullptr, false);
-
- unsigned int count = hb_ot_layout_table_get_script_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GSUB, 0, nullptr, nullptr);
- if (count != 0) {
- hb_tag_t *script_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
- hb_ot_layout_table_get_script_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GSUB, 0, &count, script_tags);
- for (unsigned int i = 0; i < count; i++) {
- if (p_script == script_tags[i]) {
- memfree(script_tags);
- return true;
- }
- }
- memfree(script_tags);
- }
- count = hb_ot_layout_table_get_script_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GPOS, 0, nullptr, nullptr);
- if (count != 0) {
- hb_tag_t *script_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
- hb_ot_layout_table_get_script_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GPOS, 0, &count, script_tags);
- for (unsigned int i = 0; i < count; i++) {
- if (p_script == script_tags[i]) {
- memfree(script_tags);
- return true;
- }
- }
- memfree(script_tags);
- }
-
- if (!fds->os2) {
- return false;
- }
-
- switch (p_script) {
- case HB_SCRIPT_COMMON:
- return (fds->os2->ulUnicodeRange1 & 1L << 4) || (fds->os2->ulUnicodeRange1 & 1L << 5) || (fds->os2->ulUnicodeRange1 & 1L << 6) || (fds->os2->ulUnicodeRange1 & 1L << 31) || (fds->os2->ulUnicodeRange2 & 1L << 0) || (fds->os2->ulUnicodeRange2 & 1L << 1) || (fds->os2->ulUnicodeRange2 & 1L << 2) || (fds->os2->ulUnicodeRange2 & 1L << 3) || (fds->os2->ulUnicodeRange2 & 1L << 4) || (fds->os2->ulUnicodeRange2 & 1L << 5) || (fds->os2->ulUnicodeRange2 & 1L << 6) || (fds->os2->ulUnicodeRange2 & 1L << 7) || (fds->os2->ulUnicodeRange2 & 1L << 8) || (fds->os2->ulUnicodeRange2 & 1L << 9) || (fds->os2->ulUnicodeRange2 & 1L << 10) || (fds->os2->ulUnicodeRange2 & 1L << 11) || (fds->os2->ulUnicodeRange2 & 1L << 12) || (fds->os2->ulUnicodeRange2 & 1L << 13) || (fds->os2->ulUnicodeRange2 & 1L << 14) || (fds->os2->ulUnicodeRange2 & 1L << 15) || (fds->os2->ulUnicodeRange2 & 1L << 30) || (fds->os2->ulUnicodeRange3 & 1L << 0) || (fds->os2->ulUnicodeRange3 & 1L << 1) || (fds->os2->ulUnicodeRange3 & 1L << 2) || (fds->os2->ulUnicodeRange3 & 1L << 4) || (fds->os2->ulUnicodeRange3 & 1L << 5) || (fds->os2->ulUnicodeRange3 & 1L << 18) || (fds->os2->ulUnicodeRange3 & 1L << 24) || (fds->os2->ulUnicodeRange3 & 1L << 25) || (fds->os2->ulUnicodeRange3 & 1L << 26) || (fds->os2->ulUnicodeRange3 & 1L << 27) || (fds->os2->ulUnicodeRange3 & 1L << 28) || (fds->os2->ulUnicodeRange4 & 1L << 3) || (fds->os2->ulUnicodeRange4 & 1L << 6) || (fds->os2->ulUnicodeRange4 & 1L << 15) || (fds->os2->ulUnicodeRange4 & 1L << 23) || (fds->os2->ulUnicodeRange4 & 1L << 24) || (fds->os2->ulUnicodeRange4 & 1L << 26);
- case HB_SCRIPT_LATIN:
- return (fds->os2->ulUnicodeRange1 & 1L << 0) || (fds->os2->ulUnicodeRange1 & 1L << 1) || (fds->os2->ulUnicodeRange1 & 1L << 2) || (fds->os2->ulUnicodeRange1 & 1L << 3) || (fds->os2->ulUnicodeRange1 & 1L << 29);
- case HB_SCRIPT_GREEK:
- return (fds->os2->ulUnicodeRange1 & 1L << 7) || (fds->os2->ulUnicodeRange1 & 1L << 30);
- case HB_SCRIPT_COPTIC:
- return (fds->os2->ulUnicodeRange1 & 1L << 8);
- case HB_SCRIPT_CYRILLIC:
- return (fds->os2->ulUnicodeRange1 & 1L << 9);
- case HB_SCRIPT_ARMENIAN:
- return (fds->os2->ulUnicodeRange1 & 1L << 10);
- case HB_SCRIPT_HEBREW:
- return (fds->os2->ulUnicodeRange1 & 1L << 11);
- case HB_SCRIPT_VAI:
- return (fds->os2->ulUnicodeRange1 & 1L << 12);
- case HB_SCRIPT_ARABIC:
- return (fds->os2->ulUnicodeRange1 & 1L << 13) || (fds->os2->ulUnicodeRange2 & 1L << 31) || (fds->os2->ulUnicodeRange3 & 1L << 3);
- case HB_SCRIPT_NKO:
- return (fds->os2->ulUnicodeRange1 & 1L << 14);
- case HB_SCRIPT_DEVANAGARI:
- return (fds->os2->ulUnicodeRange1 & 1L << 15);
- case HB_SCRIPT_BENGALI:
- return (fds->os2->ulUnicodeRange1 & 1L << 16);
- case HB_SCRIPT_GURMUKHI:
- return (fds->os2->ulUnicodeRange1 & 1L << 17);
- case HB_SCRIPT_GUJARATI:
- return (fds->os2->ulUnicodeRange1 & 1L << 18);
- case HB_SCRIPT_ORIYA:
- return (fds->os2->ulUnicodeRange1 & 1L << 19);
- case HB_SCRIPT_TAMIL:
- return (fds->os2->ulUnicodeRange1 & 1L << 20);
- case HB_SCRIPT_TELUGU:
- return (fds->os2->ulUnicodeRange1 & 1L << 21);
- case HB_SCRIPT_KANNADA:
- return (fds->os2->ulUnicodeRange1 & 1L << 22);
- case HB_SCRIPT_MALAYALAM:
- return (fds->os2->ulUnicodeRange1 & 1L << 23);
- case HB_SCRIPT_THAI:
- return (fds->os2->ulUnicodeRange1 & 1L << 24);
- case HB_SCRIPT_LAO:
- return (fds->os2->ulUnicodeRange1 & 1L << 25);
- case HB_SCRIPT_GEORGIAN:
- return (fds->os2->ulUnicodeRange1 & 1L << 26);
- case HB_SCRIPT_BALINESE:
- return (fds->os2->ulUnicodeRange1 & 1L << 27);
- case HB_SCRIPT_HANGUL:
- return (fds->os2->ulUnicodeRange1 & 1L << 28) || (fds->os2->ulUnicodeRange2 & 1L << 20) || (fds->os2->ulUnicodeRange2 & 1L << 24);
- case HB_SCRIPT_HAN:
- return (fds->os2->ulUnicodeRange2 & 1L << 21) || (fds->os2->ulUnicodeRange2 & 1L << 22) || (fds->os2->ulUnicodeRange2 & 1L << 23) || (fds->os2->ulUnicodeRange2 & 1L << 26) || (fds->os2->ulUnicodeRange2 & 1L << 27) || (fds->os2->ulUnicodeRange2 & 1L << 29);
- case HB_SCRIPT_HIRAGANA:
- return (fds->os2->ulUnicodeRange2 & 1L << 17);
- case HB_SCRIPT_KATAKANA:
- return (fds->os2->ulUnicodeRange2 & 1L << 18);
- case HB_SCRIPT_BOPOMOFO:
- return (fds->os2->ulUnicodeRange2 & 1L << 19);
- case HB_SCRIPT_TIBETAN:
- return (fds->os2->ulUnicodeRange3 & 1L << 6);
- case HB_SCRIPT_SYRIAC:
- return (fds->os2->ulUnicodeRange3 & 1L << 7);
- case HB_SCRIPT_THAANA:
- return (fds->os2->ulUnicodeRange3 & 1L << 8);
- case HB_SCRIPT_SINHALA:
- return (fds->os2->ulUnicodeRange3 & 1L << 9);
- case HB_SCRIPT_MYANMAR:
- return (fds->os2->ulUnicodeRange3 & 1L << 10);
- case HB_SCRIPT_ETHIOPIC:
- return (fds->os2->ulUnicodeRange3 & 1L << 11);
- case HB_SCRIPT_CHEROKEE:
- return (fds->os2->ulUnicodeRange3 & 1L << 12);
- case HB_SCRIPT_CANADIAN_SYLLABICS:
- return (fds->os2->ulUnicodeRange3 & 1L << 13);
- case HB_SCRIPT_OGHAM:
- return (fds->os2->ulUnicodeRange3 & 1L << 14);
- case HB_SCRIPT_RUNIC:
- return (fds->os2->ulUnicodeRange3 & 1L << 15);
- case HB_SCRIPT_KHMER:
- return (fds->os2->ulUnicodeRange3 & 1L << 16);
- case HB_SCRIPT_MONGOLIAN:
- return (fds->os2->ulUnicodeRange3 & 1L << 17);
- case HB_SCRIPT_YI:
- return (fds->os2->ulUnicodeRange3 & 1L << 19);
- case HB_SCRIPT_HANUNOO:
- case HB_SCRIPT_TAGBANWA:
- case HB_SCRIPT_BUHID:
- case HB_SCRIPT_TAGALOG:
- return (fds->os2->ulUnicodeRange3 & 1L << 20);
- case HB_SCRIPT_OLD_ITALIC:
- return (fds->os2->ulUnicodeRange3 & 1L << 21);
- case HB_SCRIPT_GOTHIC:
- return (fds->os2->ulUnicodeRange3 & 1L << 22);
- case HB_SCRIPT_DESERET:
- return (fds->os2->ulUnicodeRange3 & 1L << 23);
- case HB_SCRIPT_LIMBU:
- return (fds->os2->ulUnicodeRange3 & 1L << 29);
- case HB_SCRIPT_TAI_LE:
- return (fds->os2->ulUnicodeRange3 & 1L << 30);
- case HB_SCRIPT_NEW_TAI_LUE:
- return (fds->os2->ulUnicodeRange3 & 1L << 31);
- case HB_SCRIPT_BUGINESE:
- return (fds->os2->ulUnicodeRange4 & 1L << 0);
- case HB_SCRIPT_GLAGOLITIC:
- return (fds->os2->ulUnicodeRange4 & 1L << 1);
- case HB_SCRIPT_TIFINAGH:
- return (fds->os2->ulUnicodeRange4 & 1L << 2);
- case HB_SCRIPT_SYLOTI_NAGRI:
- return (fds->os2->ulUnicodeRange4 & 1L << 4);
- case HB_SCRIPT_LINEAR_B:
- return (fds->os2->ulUnicodeRange4 & 1L << 5);
- case HB_SCRIPT_UGARITIC:
- return (fds->os2->ulUnicodeRange4 & 1L << 7);
- case HB_SCRIPT_OLD_PERSIAN:
- return (fds->os2->ulUnicodeRange4 & 1L << 8);
- case HB_SCRIPT_SHAVIAN:
- return (fds->os2->ulUnicodeRange4 & 1L << 9);
- case HB_SCRIPT_OSMANYA:
- return (fds->os2->ulUnicodeRange4 & 1L << 10);
- case HB_SCRIPT_CYPRIOT:
- return (fds->os2->ulUnicodeRange4 & 1L << 11);
- case HB_SCRIPT_KHAROSHTHI:
- return (fds->os2->ulUnicodeRange4 & 1L << 12);
- case HB_SCRIPT_TAI_VIET:
- return (fds->os2->ulUnicodeRange4 & 1L << 13);
- case HB_SCRIPT_CUNEIFORM:
- return (fds->os2->ulUnicodeRange4 & 1L << 14);
- case HB_SCRIPT_SUNDANESE:
- return (fds->os2->ulUnicodeRange4 & 1L << 16);
- case HB_SCRIPT_LEPCHA:
- return (fds->os2->ulUnicodeRange4 & 1L << 17);
- case HB_SCRIPT_OL_CHIKI:
- return (fds->os2->ulUnicodeRange4 & 1L << 18);
- case HB_SCRIPT_SAURASHTRA:
- return (fds->os2->ulUnicodeRange4 & 1L << 19);
- case HB_SCRIPT_KAYAH_LI:
- return (fds->os2->ulUnicodeRange4 & 1L << 20);
- case HB_SCRIPT_REJANG:
- return (fds->os2->ulUnicodeRange4 & 1L << 21);
- case HB_SCRIPT_CHAM:
- return (fds->os2->ulUnicodeRange4 & 1L << 22);
- case HB_SCRIPT_ANATOLIAN_HIEROGLYPHS:
- return (fds->os2->ulUnicodeRange4 & 1L << 25);
- default:
- return false;
- };
-}
-
-void DynamicFontDataAdvanced::set_antialiased(bool p_antialiased) {
- if (antialiased != p_antialiased) {
- clear_cache();
- antialiased = p_antialiased;
- }
-}
-
-bool DynamicFontDataAdvanced::get_antialiased() const {
- return antialiased;
-}
-
-void DynamicFontDataAdvanced::set_force_autohinter(bool p_enabled) {
- if (force_autohinter != p_enabled) {
- clear_cache();
- force_autohinter = p_enabled;
- }
-}
-
-bool DynamicFontDataAdvanced::get_force_autohinter() const {
- return force_autohinter;
-}
-
-void DynamicFontDataAdvanced::set_hinting(TextServer::Hinting p_hinting) {
- if (hinting != p_hinting) {
- clear_cache();
- hinting = p_hinting;
- }
-}
-
-TextServer::Hinting DynamicFontDataAdvanced::get_hinting() const {
- return hinting;
-}
-
-bool DynamicFontDataAdvanced::has_outline() const {
- return true;
-}
-
-float DynamicFontDataAdvanced::get_base_size() const {
- return base_size;
-}
-
-String DynamicFontDataAdvanced::get_supported_chars() const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(base_size);
- ERR_FAIL_COND_V(fds == nullptr, String());
-
- String chars;
-
- FT_UInt gindex;
- FT_ULong charcode = FT_Get_First_Char(fds->face, &gindex);
- while (gindex != 0) {
- if (charcode != 0) {
- chars += char32_t(charcode);
- }
- charcode = FT_Get_Next_Char(fds->face, charcode, &gindex);
- }
-
- return chars;
-}
-
-float DynamicFontDataAdvanced::get_font_scale(int p_size) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size);
- ERR_FAIL_COND_V(fds == nullptr, 1.0f);
-
- return fds->scale_color_font / oversampling;
-}
-
-bool DynamicFontDataAdvanced::has_char(char32_t p_char) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(base_size);
- ERR_FAIL_COND_V(fds == nullptr, false);
-
- const_cast<DynamicFontDataAdvanced *>(this)->update_glyph(base_size, FT_Get_Char_Index(fds->face, p_char));
- Character ch = fds->glyph_map[FT_Get_Char_Index(fds->face, p_char)];
-
- return (ch.found);
-}
-
-hb_font_t *DynamicFontDataAdvanced::get_hb_handle(int p_size) {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = get_data_for_size(p_size);
- ERR_FAIL_COND_V(fds == nullptr, nullptr);
-
- return fds->hb_handle;
-}
-
-uint32_t DynamicFontDataAdvanced::get_glyph_index(char32_t p_char, char32_t p_variation_selector) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(base_size);
- ERR_FAIL_COND_V(fds == nullptr, 0);
-
- if (p_variation_selector == 0x0000) {
- return FT_Get_Char_Index(fds->face, p_char);
- } else {
- return FT_Face_GetCharVariantIndex(fds->face, p_char, p_variation_selector);
- }
-}
-
-Vector2 DynamicFontDataAdvanced::get_advance(uint32_t p_index, int p_size) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size);
- ERR_FAIL_COND_V(fds == nullptr, Vector2());
-
- const_cast<DynamicFontDataAdvanced *>(this)->update_glyph(p_size, p_index);
- Character ch = fds->glyph_map[p_index];
-
- return ch.advance;
-}
-
-Vector2 DynamicFontDataAdvanced::get_kerning(uint32_t p_char, uint32_t p_next, int p_size) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size);
- ERR_FAIL_COND_V(fds == nullptr, Vector2());
-
- FT_Vector delta;
- FT_Get_Kerning(fds->face, p_char, p_next, FT_KERNING_DEFAULT, &delta);
- return Vector2(delta.x, delta.y);
-}
-
-Vector2 DynamicFontDataAdvanced::draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size);
- ERR_FAIL_COND_V(fds == nullptr, Vector2());
-
- const_cast<DynamicFontDataAdvanced *>(this)->update_glyph(p_size, p_index);
- Character ch = fds->glyph_map[p_index];
-
- Vector2 advance;
- if (ch.found) {
- ERR_FAIL_COND_V(ch.texture_idx < -1 || ch.texture_idx >= fds->textures.size(), Vector2());
-
- if (ch.texture_idx != -1) {
- Point2i cpos = p_pos;
- cpos += ch.align;
- Color modulate = p_color;
- if (FT_HAS_COLOR(fds->face)) {
- modulate.r = modulate.g = modulate.b = 1.0;
- }
- if (RenderingServer::get_singleton() != nullptr) {
- RID texture = fds->textures[ch.texture_idx].texture->get_rid();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, ch.rect.size), texture, ch.rect_uv, modulate, false, false);
- }
- }
-
- advance = ch.advance;
- }
-
- return advance;
-}
-
-Vector2 DynamicFontDataAdvanced::draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size, p_outline_size);
- ERR_FAIL_COND_V(fds == nullptr, Vector2());
-
- const_cast<DynamicFontDataAdvanced *>(this)->update_glyph_outline(p_size, p_outline_size, p_index);
- Character ch = fds->glyph_map[p_index];
-
- Vector2 advance;
- if (ch.found) {
- ERR_FAIL_COND_V(ch.texture_idx < -1 || ch.texture_idx >= fds->textures.size(), Vector2());
-
- if (ch.texture_idx != -1) {
- Point2i cpos = p_pos;
- cpos += ch.align;
- Color modulate = p_color;
- if (FT_HAS_COLOR(fds->face)) {
- modulate.r = modulate.g = modulate.b = 1.0;
- }
- if (RenderingServer::get_singleton() != nullptr) {
- RID texture = fds->textures[ch.texture_idx].texture->get_rid();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, ch.rect.size), texture, ch.rect_uv, modulate, false, false);
- }
- }
-
- advance = ch.advance;
- }
-
- return advance;
-}
-
-bool DynamicFontDataAdvanced::get_glyph_contours(int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size);
- ERR_FAIL_COND_V(fds == nullptr, false);
-
- int error = FT_Load_Glyph(fds->face, p_index, FT_LOAD_NO_BITMAP | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0));
- ERR_FAIL_COND_V(error, false);
-
- r_points.clear();
- r_contours.clear();
-
- float h = fds->ascent;
- float scale = (1.0 / 64.0) / oversampling * fds->scale_color_font;
- for (short i = 0; i < fds->face->glyph->outline.n_points; i++) {
- r_points.push_back(Vector3(fds->face->glyph->outline.points[i].x * scale, h - fds->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fds->face->glyph->outline.tags[i])));
- }
- for (short i = 0; i < fds->face->glyph->outline.n_contours; i++) {
- r_contours.push_back(fds->face->glyph->outline.contours[i]);
- }
- r_orientation = (FT_Outline_Get_Orientation(&fds->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT);
- return true;
-}
-
-DynamicFontDataAdvanced::~DynamicFontDataAdvanced() {
- clear_cache();
- if (library != nullptr) {
- FT_Done_FreeType(library);
- }
-}
-
-#endif // MODULE_FREETYPE_ENABLED
diff --git a/modules/text_server_adv/dynamic_font_adv.h b/modules/text_server_adv/dynamic_font_adv.h
deleted file mode 100644
index b3f97bb029..0000000000
--- a/modules/text_server_adv/dynamic_font_adv.h
+++ /dev/null
@@ -1,195 +0,0 @@
-/*************************************************************************/
-/* dynamic_font_adv.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 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. */
-/*************************************************************************/
-
-#ifndef DYNAMIC_FONT_ADV_H
-#define DYNAMIC_FONT_ADV_H
-
-#include "font_adv.h"
-
-#include "modules/modules_enabled.gen.h"
-#ifdef MODULE_FREETYPE_ENABLED
-
-#include <ft2build.h>
-#include FT_FREETYPE_H
-#include FT_TRUETYPE_TABLES_H
-
-#include <hb-ft.h>
-#include <hb-ot.h>
-
-struct DynamicFontDataAdvanced : public FontDataAdvanced {
- _THREAD_SAFE_CLASS_
-
-private:
- struct CharTexture {
- Vector<uint8_t> imgdata;
- int texture_size = 0;
- Vector<int> offsets;
- Ref<ImageTexture> texture;
- };
-
- struct Character {
- bool found = false;
- int texture_idx = 0;
- Rect2 rect;
- Rect2 rect_uv;
- Vector2 align;
- Vector2 advance = Vector2(-1, -1);
-
- static Character not_found();
- };
-
- struct TexturePosition {
- int index = 0;
- int x = 0;
- int y = 0;
- };
-
- struct CacheID {
- union {
- struct {
- uint32_t size : 16;
- uint32_t outline_size : 16;
- };
- uint32_t key = 0;
- };
- bool operator<(CacheID right) const {
- return key < right.key;
- }
- };
-
- struct DataAtSize {
- FT_Face face = nullptr;
- TT_OS2 *os2 = nullptr;
- FT_StreamRec stream;
-
- int size = 0;
- float scale_color_font = 1.f;
- float ascent = 0.0;
- float descent = 0.0;
- float underline_position = 0.0;
- float underline_thickness = 0.0;
-
- Vector<CharTexture> textures;
- HashMap<uint32_t, Character> glyph_map;
-
- hb_font_t *hb_handle = nullptr;
- ~DataAtSize() {
- if (hb_handle != nullptr) {
- hb_font_destroy(hb_handle);
- }
- if (face != nullptr) {
- FT_Done_Face(face);
- }
- }
- };
-
- FT_Library library = nullptr;
-
- // Source data.
- const uint8_t *font_mem = nullptr;
- int font_mem_size = 0;
- String font_path;
- Vector<uint8_t> font_mem_cache;
-
- Map<int32_t, double> variations;
-
- float rect_margin = 1.f;
- int base_size = 16;
- float oversampling = 1.f;
- bool antialiased = true;
- bool force_autohinter = false;
- TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
-
- Map<CacheID, DataAtSize *> size_cache;
- Map<CacheID, DataAtSize *> size_cache_outline;
-
- DataAtSize *get_data_for_size(int p_size, int p_outline_size = 0);
-
- TexturePosition find_texture_pos_for_glyph(DataAtSize *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height);
- Character bitmap_to_character(DataAtSize *p_data, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance);
- _FORCE_INLINE_ void update_glyph(int p_size, uint32_t p_index);
- _FORCE_INLINE_ void update_glyph_outline(int p_size, int p_outline_size, uint32_t p_index);
-
-public:
- virtual void clear_cache() override;
-
- virtual Error load_from_file(const String &p_filename, int p_base_size) override;
- virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) override;
-
- virtual float get_height(int p_size) const override;
- virtual float get_ascent(int p_size) const override;
- virtual float get_descent(int p_size) const override;
-
- virtual Dictionary get_feature_list() const override;
- virtual Dictionary get_variation_list() const override;
-
- virtual void set_variation(const String &p_name, double p_value) override;
- virtual double get_variation(const String &p_name) const override;
-
- virtual float get_underline_position(int p_size) const override;
- virtual float get_underline_thickness(int p_size) const override;
-
- virtual void set_antialiased(bool p_antialiased) override;
- virtual bool get_antialiased() const override;
-
- virtual void set_hinting(TextServer::Hinting p_hinting) override;
- virtual TextServer::Hinting get_hinting() const override;
-
- virtual void set_force_autohinter(bool p_enabled) override;
- virtual bool get_force_autohinter() const override;
-
- virtual void set_distance_field_hint(bool p_distance_field) override{};
- virtual bool get_distance_field_hint() const override { return false; };
-
- virtual bool has_outline() const override;
- virtual float get_base_size() const override;
-
- virtual bool is_script_supported(uint32_t p_script) const override;
-
- virtual bool has_char(char32_t p_char) const override;
- virtual String get_supported_chars() const override;
- virtual float get_font_scale(int p_size) const override;
-
- virtual hb_font_t *get_hb_handle(int p_size) override;
- virtual uint32_t get_glyph_index(char32_t p_char, char32_t p_variation_selector) const override;
- virtual Vector2 get_advance(uint32_t p_index, int p_size) const override;
- virtual Vector2 get_kerning(uint32_t p_char, uint32_t p_next, int p_size) const override;
-
- virtual Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override;
- virtual Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override;
-
- virtual bool get_glyph_contours(int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override;
-
- virtual ~DynamicFontDataAdvanced() override;
-};
-
-#endif // MODULE_FREETYPE_ENABLED
-
-#endif // DYNAMIC_FONT_ADV_H
diff --git a/modules/text_server_adv/font_adv.h b/modules/text_server_adv/font_adv.h
deleted file mode 100644
index 4fadefc569..0000000000
--- a/modules/text_server_adv/font_adv.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/*************************************************************************/
-/* font_adv.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 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. */
-/*************************************************************************/
-
-#ifndef FONT_ADV_H
-#define FONT_ADV_H
-
-#include "servers/text_server.h"
-
-#include <hb.h>
-
-struct FontDataAdvanced {
- Map<String, bool> lang_support_overrides;
- Map<String, bool> script_support_overrides;
- bool valid = false;
- int spacing_space = 0;
- int spacing_glyph = 0;
-
- virtual void clear_cache() = 0;
-
- virtual Error load_from_file(const String &p_filename, int p_base_size) { return ERR_CANT_CREATE; };
- virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) { return ERR_CANT_CREATE; };
- virtual Error bitmap_new(float p_height, float p_ascent, int p_base_size) { return ERR_CANT_CREATE; };
-
- virtual void bitmap_add_texture(const Ref<Texture> &p_texture) { ERR_FAIL_MSG("Not supported."); };
- virtual void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { ERR_FAIL_MSG("Not supported."); };
- virtual void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) { ERR_FAIL_MSG("Not supported."); };
-
- virtual float get_height(int p_size) const = 0;
- virtual float get_ascent(int p_size) const = 0;
- virtual float get_descent(int p_size) const = 0;
-
- virtual Dictionary get_feature_list() const { return Dictionary(); };
- virtual Dictionary get_variation_list() const { return Dictionary(); };
-
- virtual void set_variation(const String &p_name, double p_value){};
- virtual double get_variation(const String &p_name) const { return 0; };
-
- virtual float get_underline_position(int p_size) const = 0;
- virtual float get_underline_thickness(int p_size) const = 0;
-
- virtual int get_spacing_space() const { return spacing_space; };
- virtual void set_spacing_space(int p_value) {
- spacing_space = p_value;
- clear_cache();
- };
-
- virtual int get_spacing_glyph() const { return spacing_glyph; };
- virtual void set_spacing_glyph(int p_value) {
- spacing_glyph = p_value;
- clear_cache();
- };
-
- virtual void set_antialiased(bool p_antialiased) = 0;
- virtual bool get_antialiased() const = 0;
-
- virtual void set_hinting(TextServer::Hinting p_hinting) = 0;
- virtual TextServer::Hinting get_hinting() const = 0;
-
- virtual void set_distance_field_hint(bool p_distance_field) = 0;
- virtual bool get_distance_field_hint() const = 0;
-
- virtual void set_force_autohinter(bool p_enabeld) = 0;
- virtual bool get_force_autohinter() const = 0;
-
- virtual bool has_outline() const = 0;
- virtual float get_base_size() const = 0;
-
- virtual bool is_lang_supported(const String &p_lang) const { return true; };
- virtual bool is_script_supported(uint32_t p_script) const { return true; };
-
- virtual bool has_char(char32_t p_char) const = 0;
- virtual String get_supported_chars() const = 0;
- virtual float get_font_scale(int p_size) const { return 1.0f; };
-
- virtual hb_font_t *get_hb_handle(int p_size) = 0;
- virtual uint32_t get_glyph_index(char32_t p_char, char32_t p_variation_selector) const = 0;
- virtual Vector2 get_advance(uint32_t p_char, int p_size) const = 0;
- virtual Vector2 get_kerning(uint32_t p_char, uint32_t p_next, int p_size) const = 0;
-
- virtual Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const = 0;
- virtual Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const = 0;
-
- virtual bool get_glyph_contours(int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const { return false; };
-
- virtual ~FontDataAdvanced(){};
-};
-
-#endif // FONT_ADV_H
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 9ecb0de5b8..78a87be971 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -29,15 +29,213 @@
/*************************************************************************/
#include "text_server_adv.h"
-#include "bitmap_font_adv.h"
-#include "dynamic_font_adv.h"
+#include "core/string/print_string.h"
#include "core/string/translation.h"
#ifdef ICU_STATIC_DATA
#include "thirdparty/icu4c/icudata.gen.h"
#endif
+#ifdef MODULE_MSDFGEN_ENABLED
+#include "core/ShapeDistanceFinder.h"
+#include "core/contour-combiners.h"
+#include "core/edge-selectors.h"
+#include "msdfgen.h"
+#endif
+
+/*************************************************************************/
+/* hb_bmp_font_t HarfBuzz Bitmap font interface */
+/*************************************************************************/
+
+hb_font_funcs_t *TextServerAdvanced::funcs = nullptr;
+
+TextServerAdvanced::hb_bmp_font_t *TextServerAdvanced::_hb_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref) {
+ hb_bmp_font_t *bm_font = memnew(hb_bmp_font_t);
+
+ if (!bm_font) {
+ return nullptr;
+ }
+
+ bm_font->face = p_face;
+ bm_font->unref = p_unref;
+
+ return bm_font;
+}
+
+void TextServerAdvanced::_hb_bmp_font_destroy(void *p_data) {
+ hb_bmp_font_t *bm_font = reinterpret_cast<hb_bmp_font_t *>(p_data);
+ memdelete(bm_font);
+}
+
+hb_bool_t TextServerAdvanced::hb_bmp_get_nominal_glyph(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_unicode, hb_codepoint_t *r_glyph, void *p_user_data) {
+ const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data);
+
+ if (!bm_font->face) {
+ return false;
+ }
+
+ if (!bm_font->face->glyph_map.has(p_unicode)) {
+ if (bm_font->face->glyph_map.has(0xF000u + p_unicode)) {
+ *r_glyph = 0xF000u + p_unicode;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ *r_glyph = p_unicode;
+ return true;
+}
+
+hb_position_t TextServerAdvanced::hb_bmp_get_glyph_h_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data) {
+ const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data);
+
+ if (!bm_font->face) {
+ return 0;
+ }
+
+ if (!bm_font->face->glyph_map.has(p_glyph)) {
+ return 0;
+ }
+
+ return bm_font->face->glyph_map[p_glyph].advance.x * 64;
+}
+
+hb_position_t TextServerAdvanced::hb_bmp_get_glyph_v_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data) {
+ const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data);
+
+ if (!bm_font->face) {
+ return 0;
+ }
+
+ if (!bm_font->face->glyph_map.has(p_glyph)) {
+ return 0;
+ }
+
+ return -bm_font->face->glyph_map[p_glyph].advance.y * 64;
+}
+
+hb_position_t TextServerAdvanced::hb_bmp_get_glyph_h_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data) {
+ const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data);
+
+ if (!bm_font->face) {
+ return 0;
+ }
+
+ if (!bm_font->face->kerning_map.has(Vector2i(p_left_glyph, p_right_glyph))) {
+ return 0;
+ }
+
+ return bm_font->face->kerning_map[Vector2i(p_left_glyph, p_right_glyph)].x * 64;
+}
+
+hb_position_t TextServerAdvanced::hb_bmp_get_glyph_v_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data) {
+ const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data);
+
+ if (!bm_font->face) {
+ return 0;
+ }
+
+ if (!bm_font->face->kerning_map.has(Vector2i(p_left_glyph, p_right_glyph))) {
+ return 0;
+ }
+
+ return bm_font->face->kerning_map[Vector2i(p_left_glyph, p_right_glyph)].y * 64;
+}
+
+hb_bool_t TextServerAdvanced::hb_bmp_get_glyph_v_origin(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_position_t *r_x, hb_position_t *r_y, void *p_user_data) {
+ const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data);
+
+ if (!bm_font->face) {
+ return false;
+ }
+
+ if (!bm_font->face->glyph_map.has(p_glyph)) {
+ return false;
+ }
+
+ *r_x = bm_font->face->glyph_map[p_glyph].advance.x * 32;
+ *r_y = -bm_font->face->ascent * 64;
+
+ return true;
+}
+
+hb_bool_t TextServerAdvanced::hb_bmp_get_glyph_extents(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_glyph_extents_t *r_extents, void *p_user_data) {
+ const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data);
+
+ if (!bm_font->face) {
+ return false;
+ }
+
+ if (!bm_font->face->glyph_map.has(p_glyph)) {
+ return false;
+ }
+
+ r_extents->x_bearing = 0;
+ r_extents->y_bearing = 0;
+ r_extents->width = bm_font->face->glyph_map[p_glyph].rect.size.x * 64;
+ r_extents->height = bm_font->face->glyph_map[p_glyph].rect.size.y * 64;
+
+ return true;
+}
+
+hb_bool_t TextServerAdvanced::hb_bmp_get_font_h_extents(hb_font_t *p_font, void *p_font_data, hb_font_extents_t *r_metrics, void *p_user_data) {
+ const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data);
+
+ if (!bm_font->face) {
+ return false;
+ }
+
+ r_metrics->ascender = bm_font->face->ascent;
+ r_metrics->descender = bm_font->face->descent;
+ r_metrics->line_gap = 0;
+
+ return true;
+}
+
+void TextServerAdvanced::hb_bmp_create_font_funcs() {
+ if (funcs == nullptr) {
+ funcs = hb_font_funcs_create();
+
+ hb_font_funcs_set_font_h_extents_func(funcs, hb_bmp_get_font_h_extents, nullptr, nullptr);
+ hb_font_funcs_set_nominal_glyph_func(funcs, hb_bmp_get_nominal_glyph, nullptr, nullptr);
+ hb_font_funcs_set_glyph_h_advance_func(funcs, hb_bmp_get_glyph_h_advance, nullptr, nullptr);
+ hb_font_funcs_set_glyph_v_advance_func(funcs, hb_bmp_get_glyph_v_advance, nullptr, nullptr);
+ hb_font_funcs_set_glyph_v_origin_func(funcs, hb_bmp_get_glyph_v_origin, nullptr, nullptr);
+ hb_font_funcs_set_glyph_h_kerning_func(funcs, hb_bmp_get_glyph_h_kerning, nullptr, nullptr);
+ hb_font_funcs_set_glyph_v_kerning_func(funcs, hb_bmp_get_glyph_v_kerning, nullptr, nullptr);
+ hb_font_funcs_set_glyph_extents_func(funcs, hb_bmp_get_glyph_extents, nullptr, nullptr);
+
+ hb_font_funcs_make_immutable(funcs);
+ }
+}
+
+void TextServerAdvanced::hb_bmp_free_font_funcs() {
+ if (funcs != nullptr) {
+ hb_font_funcs_destroy(funcs);
+ funcs = nullptr;
+ }
+}
+
+void TextServerAdvanced::_hb_bmp_font_set_funcs(hb_font_t *p_font, TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref) {
+ hb_font_set_funcs(p_font, funcs, _hb_bmp_font_create(p_face, p_unref), _hb_bmp_font_destroy);
+}
+
+hb_font_t *TextServerAdvanced::hb_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, hb_destroy_func_t p_destroy) {
+ hb_font_t *font;
+ hb_face_t *face = hb_face_create(nullptr, 0);
+
+ font = hb_font_create(face);
+ hb_face_destroy(face);
+ _hb_bmp_font_set_funcs(font, p_face, false);
+ return font;
+}
+
+/*************************************************************************/
+/* Character properties. */
+/*************************************************************************/
+
_FORCE_INLINE_ bool is_ain(char32_t p_chr) {
return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_AIN;
}
@@ -497,7 +695,7 @@ static FeatureInfo feature_set[] = {
{ 0, String() },
};
-int32_t TextServerAdvanced::name_to_tag(const String &p_name) {
+int32_t TextServerAdvanced::name_to_tag(const String &p_name) const {
for (int i = 0; feature_set[i].tag != 0; i++) {
if (feature_set[i].name == p_name) {
return feature_set[i].tag;
@@ -508,7 +706,7 @@ int32_t TextServerAdvanced::name_to_tag(const String &p_name) {
return hb_tag_from_string(p_name.replace("custom_", "").ascii().get_data(), -1);
}
-String TextServerAdvanced::tag_to_name(int32_t p_tag) {
+String TextServerAdvanced::tag_to_name(int32_t p_tag) const {
for (int i = 0; feature_set[i].tag != 0; i++) {
if (feature_set[i].tag == p_tag) {
return feature_set[i].name;
@@ -523,426 +721,2094 @@ String TextServerAdvanced::tag_to_name(int32_t p_tag) {
}
/*************************************************************************/
-/* Font interface */
+/* Font Glyph Rendering */
/*************************************************************************/
-RID TextServerAdvanced::create_font_system(const String &p_name, int p_base_size) {
- ERR_FAIL_V_MSG(RID(), "System fonts are not supported by this text server.");
+_FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::find_texture_pos_for_glyph(FontDataForSizeAdvanced *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) 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 (RenderingServer::get_singleton() != nullptr) {
+ if (ct.texture->get_format() != p_image_format) {
+ continue;
+ }
+ }
+
+ if (mw > ct.texture_w || mh > ct.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.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);
+ if (mw > texsize) {
+ texsize = mw; // Special case, adapt to it?
+ }
+ if (mh > texsize) {
+ texsize = mh; // Special case, adapt to it?
+ }
+
+ texsize = next_power_of_2(texsize);
+
+ texsize = MIN(texsize, 4096);
+
+ FontTexture tex;
+ tex.texture_w = texsize;
+ tex.texture_h = texsize;
+ tex.format = p_image_format;
+ tex.imgdata.resize(texsize * texsize * p_color_size);
+
+ {
+ // Zero texture.
+ uint8_t *w = tex.imgdata.ptrw();
+ ERR_FAIL_COND_V(texsize * texsize * p_color_size > tex.imgdata.size(), ret);
+ // Initialize the texture to all-white pixels to prevent artifacts when the
+ // font is displayed at a non-default scale with filtering enabled.
+ if (p_color_size == 2) {
+ for (int i = 0; i < texsize * texsize * p_color_size; i += 2) { // FORMAT_LA8, BW font.
+ w[i + 0] = 255;
+ w[i + 1] = 0;
+ }
+ } else if (p_color_size == 4) {
+ for (int i = 0; i < texsize * texsize * p_color_size; i += 4) { // FORMAT_RGBA8, Color font, Multichannel(+True) SDF.
+ w[i + 0] = 255;
+ w[i + 1] = 255;
+ w[i + 2] = 255;
+ w[i + 3] = 0;
+ }
+ } else {
+ ERR_FAIL_V(ret);
+ }
+ }
+ tex.offsets.resize(texsize);
+ for (int i = 0; i < texsize; i++) { // Zero offsets.
+ tex.offsets.write[i] = 0;
+ }
+
+ p_data->textures.push_back(tex);
+ ret.index = p_data->textures.size() - 1;
+ }
+
+ return ret;
}
-RID TextServerAdvanced::create_font_resource(const String &p_filename, int p_base_size) {
- _THREAD_SAFE_METHOD_
- FontDataAdvanced *fd = nullptr;
- if (p_filename.get_extension() == "fnt" || p_filename.get_extension() == "font") {
- fd = memnew(BitmapFontDataAdvanced);
+#ifdef MODULE_MSDFGEN_ENABLED
+
+struct MSContext {
+ msdfgen::Point2 position;
+ msdfgen::Shape *shape;
+ msdfgen::Contour *contour;
+};
+
+class DistancePixelConversion {
+ double invRange;
+
+public:
+ _FORCE_INLINE_ explicit DistancePixelConversion(double range) :
+ invRange(1 / range) {}
+ _FORCE_INLINE_ void operator()(float *pixels, const msdfgen::MultiAndTrueDistance &distance) const {
+ pixels[0] = float(invRange * distance.r + .5);
+ pixels[1] = float(invRange * distance.g + .5);
+ pixels[2] = float(invRange * distance.b + .5);
+ pixels[3] = float(invRange * distance.a + .5);
+ }
+};
+
+struct MSDFThreadData {
+ msdfgen::Bitmap<float, 4> *output;
+ msdfgen::Shape *shape;
+ msdfgen::Projection *projection;
+ DistancePixelConversion *distancePixelConversion;
+};
+
+static msdfgen::Point2 ft_point2(const FT_Vector &vector) {
+ return msdfgen::Point2(vector.x / 60.0f, vector.y / 60.0f);
+}
+
+static int ft_move_to(const FT_Vector *to, void *user) {
+ MSContext *context = reinterpret_cast<MSContext *>(user);
+ if (!(context->contour && context->contour->edges.empty())) {
+ context->contour = &context->shape->addContour();
+ }
+ context->position = ft_point2(*to);
+ return 0;
+}
+
+static int ft_line_to(const FT_Vector *to, void *user) {
+ MSContext *context = reinterpret_cast<MSContext *>(user);
+ msdfgen::Point2 endpoint = ft_point2(*to);
+ if (endpoint != context->position) {
+ context->contour->addEdge(new msdfgen::LinearSegment(context->position, endpoint));
+ context->position = endpoint;
+ }
+ return 0;
+}
+
+static int ft_conic_to(const FT_Vector *control, const FT_Vector *to, void *user) {
+ MSContext *context = reinterpret_cast<MSContext *>(user);
+ context->contour->addEdge(new msdfgen::QuadraticSegment(context->position, ft_point2(*control), ft_point2(*to)));
+ context->position = ft_point2(*to);
+ return 0;
+}
+
+static int ft_cubic_to(const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to, void *user) {
+ MSContext *context = reinterpret_cast<MSContext *>(user);
+ context->contour->addEdge(new msdfgen::CubicSegment(context->position, ft_point2(*control1), ft_point2(*control2), ft_point2(*to)));
+ context->position = ft_point2(*to);
+ return 0;
+}
+
+void TextServerAdvanced::_generateMTSDF_threaded(uint32_t y, void *p_td) const {
+ MSDFThreadData *td = (MSDFThreadData *)p_td;
+
+ msdfgen::ShapeDistanceFinder<msdfgen::OverlappingContourCombiner<msdfgen::MultiAndTrueDistanceSelector>> distanceFinder(*td->shape);
+ int row = td->shape->inverseYAxis ? td->output->height() - y - 1 : y;
+ for (int col = 0; col < td->output->width(); ++col) {
+ int x = (y % 2) ? td->output->width() - col - 1 : col;
+ msdfgen::Point2 p = td->projection->unproject(msdfgen::Point2(x + .5, y + .5));
+ msdfgen::MultiAndTrueDistance distance = distanceFinder.distance(p);
+ td->distancePixelConversion->operator()(td->output->operator()(x, row), distance);
+ }
+}
+
+_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(FontDataAdvanced *p_font_data, FontDataForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const {
+ msdfgen::Shape shape;
+
+ shape.contours.clear();
+ shape.inverseYAxis = false;
+
+ MSContext context = {};
+ context.shape = &shape;
+ FT_Outline_Funcs ft_functions;
+ ft_functions.move_to = &ft_move_to;
+ ft_functions.line_to = &ft_line_to;
+ ft_functions.conic_to = &ft_conic_to;
+ ft_functions.cubic_to = &ft_cubic_to;
+ ft_functions.shift = 0;
+ ft_functions.delta = 0;
+
+ int error = FT_Outline_Decompose(outline, &ft_functions, &context);
+ ERR_FAIL_COND_V_MSG(error, FontGlyph(), "FreeType: Outline decomposition error: '" + String(FT_Error_String(error)) + "'.");
+ if (!shape.contours.empty() && shape.contours.back().edges.empty()) {
+ shape.contours.pop_back();
+ }
+
+ if (FT_Outline_Get_Orientation(outline) == 1) {
+ for (int i = 0; i < (int)shape.contours.size(); ++i) {
+ shape.contours[i].reverse();
+ }
+ }
+
+ shape.inverseYAxis = true;
+ shape.normalize();
+
+ msdfgen::Shape::Bounds bounds = shape.getBounds(p_pixel_range);
+
+ FontGlyph chr;
+ chr.found = true;
+ chr.advance = advance.round();
+
+ if (shape.validate() && shape.contours.size() > 0) {
+ int w = (bounds.r - bounds.l);
+ int h = (bounds.t - bounds.b);
+
+ int mw = w + p_rect_margin * 2;
+ int mh = h + p_rect_margin * 2;
+
+ ERR_FAIL_COND_V(mw > 4096, FontGlyph());
+ ERR_FAIL_COND_V(mh > 4096, FontGlyph());
+
+ FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, 4, Image::FORMAT_RGBA8, mw, mh);
+ ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph());
+ FontTexture &tex = p_data->textures.write[tex_pos.index];
+
+ edgeColoringSimple(shape, 3.0); // Max. angle.
+ msdfgen::Bitmap<float, 4> image(w, h); // Texture size.
+ //msdfgen::generateMTSDF(image, shape, p_pixel_range, 1.0, msdfgen::Vector2(-bounds.l, -bounds.b)); // Range, scale, translation.
+
+ DistancePixelConversion distancePixelConversion(p_pixel_range);
+ msdfgen::Projection projection(msdfgen::Vector2(1.0, 1.0), msdfgen::Vector2(-bounds.l, -bounds.b));
+ msdfgen::MSDFGeneratorConfig config(true, msdfgen::ErrorCorrectionConfig());
+
+ MSDFThreadData td;
+ td.output = &image;
+ td.shape = &shape;
+ td.projection = &projection;
+ td.distancePixelConversion = &distancePixelConversion;
+
+ if (p_font_data->work_pool.get_thread_count() == 0) {
+ p_font_data->work_pool.init();
+ }
+ p_font_data->work_pool.do_work(h, this, &TextServerAdvanced::_generateMTSDF_threaded, &td);
+
+ msdfgen::msdfErrorCorrection(image, shape, projection, p_pixel_range, config);
+
+ {
+ uint8_t *wr = tex.imgdata.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs = ((i + tex_pos.y + p_rect_margin) * tex.texture_w + j + tex_pos.x + p_rect_margin) * 4;
+ ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), FontGlyph());
+ wr[ofs + 0] = (uint8_t)(CLAMP(image(j, i)[0] * 256.f, 0.f, 255.f));
+ wr[ofs + 1] = (uint8_t)(CLAMP(image(j, i)[1] * 256.f, 0.f, 255.f));
+ wr[ofs + 2] = (uint8_t)(CLAMP(image(j, i)[2] * 256.f, 0.f, 255.f));
+ wr[ofs + 3] = (uint8_t)(CLAMP(image(j, i)[3] * 256.f, 0.f, 255.f));
+ //wr[ofs + 0] = 100;
+ //wr[ofs + 1] = 100;
+ //wr[ofs + 2] = 100;
+ //wr[ofs + 3] = 100;
+ }
+ }
+ }
+
+ // Blit to image and texture.
+ {
+ if (RenderingServer::get_singleton() != nullptr) {
+ Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, Image::FORMAT_RGBA8, tex.imgdata));
+ if (tex.texture.is_null()) {
+ tex.texture.instantiate();
+ tex.texture->create_from_image(img);
+ } else {
+ tex.texture->update(img);
+ }
+ }
+ }
+
+ // Update height array.
+ for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
+ tex.offsets.write[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, h);
+ chr.rect.position = Vector2(bounds.l, -bounds.t);
+ chr.rect.size = chr.uv_rect.size;
+ }
+ return chr;
+}
+#endif
+
#ifdef MODULE_FREETYPE_ENABLED
- } else if (p_filename.get_extension() == "ttf" || p_filename.get_extension() == "otf" || p_filename.get_extension() == "woff") {
- fd = memnew(DynamicFontDataAdvanced);
+_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitmap(FontDataForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const {
+ int w = bitmap.width;
+ int h = bitmap.rows;
+
+ int mw = w + p_rect_margin * 2;
+ int mh = h + p_rect_margin * 2;
+
+ 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);
+ ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph());
+
+ // Fit character in char texture.
+
+ FontTexture &tex = p_data->textures.write[tex_pos.index];
+
+ {
+ uint8_t *wr = tex.imgdata.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs = ((i + tex_pos.y + p_rect_margin) * tex.texture_w + j + tex_pos.x + p_rect_margin) * color_size;
+ ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), FontGlyph());
+ switch (bitmap.pixel_mode) {
+ case FT_PIXEL_MODE_MONO: {
+ int byte = i * bitmap.pitch + (j >> 3);
+ int bit = 1 << (7 - (j % 8));
+ wr[ofs + 0] = 255; //grayscale as 1
+ wr[ofs + 1] = (bitmap.buffer[byte] & bit) ? 255 : 0;
+ } break;
+ case FT_PIXEL_MODE_GRAY:
+ wr[ofs + 0] = 255; //grayscale as 1
+ wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j];
+ //wr[ofs + 1] = 100;
+ break;
+ case FT_PIXEL_MODE_BGRA: {
+ int ofs_color = i * bitmap.pitch + (j << 2);
+ wr[ofs + 2] = bitmap.buffer[ofs_color + 0];
+ wr[ofs + 1] = bitmap.buffer[ofs_color + 1];
+ wr[ofs + 0] = bitmap.buffer[ofs_color + 2];
+ wr[ofs + 3] = bitmap.buffer[ofs_color + 3];
+ } break;
+ default:
+ ERR_FAIL_V_MSG(FontGlyph(), "Font uses unsupported pixel format: " + itos(bitmap.pixel_mode) + ".");
+ break;
+ }
+ }
+ }
+ }
+
+ // Blit to image and texture.
+ {
+ if (RenderingServer::get_singleton() != nullptr) {
+ Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, require_format, tex.imgdata));
+
+ if (tex.texture.is_null()) {
+ tex.texture.instantiate();
+ tex.texture->create_from_image(img);
+ } else {
+ tex.texture->update(img);
+ }
+ }
+ }
+
+ // Update height array.
+ for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
+ tex.offsets.write[k] = tex_pos.y + mh;
+ }
+
+ FontGlyph chr;
+ chr.advance = (advance * p_data->scale / p_data->oversampling).round();
+ chr.texture_idx = tex_pos.index;
+ chr.found = true;
+
+ chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w, h);
+ chr.rect.position = (Vector2(xofs, -yofs) * p_data->scale / p_data->oversampling).round();
+ chr.rect.size = chr.uv_rect.size * p_data->scale / p_data->oversampling;
+ return chr;
+}
#endif
- } else {
- return RID();
+
+/*************************************************************************/
+/* Font Cache */
+/*************************************************************************/
+
+_FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontDataAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const {
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(p_font_data, p_size), false);
+
+ FontDataForSizeAdvanced *fd = p_font_data->cache[p_size];
+ if (fd->glyph_map.has(p_glyph)) {
+ return fd->glyph_map[p_glyph].found;
}
- Error err = fd->load_from_file(p_filename, p_base_size);
- if (err != OK) {
- memdelete(fd);
- return RID();
+ if (p_glyph == 0) { // Non graphical or invalid glyph, do not render.
+ fd->glyph_map[p_glyph] = FontGlyph();
+ return true;
}
- return font_owner.make_rid(fd);
+#ifdef MODULE_FREETYPE_ENABLED
+ FontGlyph gl;
+ if (fd->face) {
+ FT_Int32 flags = FT_LOAD_DEFAULT;
+
+ bool outline = p_size.y > 0;
+ switch (p_font_data->hinting) {
+ case TextServer::HINTING_NONE:
+ flags |= FT_LOAD_NO_HINTING;
+ break;
+ case TextServer::HINTING_LIGHT:
+ flags |= FT_LOAD_TARGET_LIGHT;
+ break;
+ default:
+ flags |= FT_LOAD_TARGET_NORMAL;
+ break;
+ }
+ if (p_font_data->force_autohinter) {
+ flags |= FT_LOAD_FORCE_AUTOHINT;
+ }
+ if (outline) {
+ flags |= FT_LOAD_NO_BITMAP;
+ } else if (FT_HAS_COLOR(fd->face)) {
+ flags |= FT_LOAD_COLOR;
+ }
+
+ FT_Fixed v, h;
+ FT_Get_Advance(fd->face, p_glyph, flags, &h);
+ FT_Get_Advance(fd->face, p_glyph, flags | FT_LOAD_VERTICAL_LAYOUT, &v);
+
+ int error = FT_Load_Glyph(fd->face, p_glyph, flags);
+ if (error) {
+ fd->glyph_map[p_glyph] = FontGlyph();
+ ERR_FAIL_V_MSG(false, "FreeType: Failed to load glyph.");
+ }
+
+ 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);
+ }
+ FT_GlyphSlot slot = fd->face->glyph;
+ if (!error) {
+ if (p_font_data->msdf) {
+#ifdef MODULE_MSDFGEN_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();
+ 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);
+ }
+ }
+ } else {
+ FT_Stroker stroker;
+ if (FT_Stroker_New(library, &stroker) != 0) {
+ fd->glyph_map[p_glyph] = FontGlyph();
+ ERR_FAIL_V_MSG(false, "FreeType: Failed to load glyph stroker.");
+ }
+
+ FT_Stroker_Set(stroker, (int)(fd->size.y * fd->oversampling * 16.0), FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0);
+ FT_Glyph glyph;
+ FT_BitmapGlyph glyph_bitmap;
+
+ if (FT_Get_Glyph(fd->face->glyph, &glyph) != 0) {
+ goto cleanup_stroker;
+ }
+ 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) {
+ goto cleanup_glyph;
+ }
+ glyph_bitmap = (FT_BitmapGlyph)glyph;
+ gl = rasterize_bitmap(fd, rect_range, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2());
+
+ cleanup_glyph:
+ FT_Done_Glyph(glyph);
+ cleanup_stroker:
+ FT_Stroker_Done(stroker);
+ }
+ fd->glyph_map[p_glyph] = gl;
+ return gl.found;
+ }
+#endif
+ fd->glyph_map[p_glyph] = FontGlyph();
+ return false;
}
-RID TextServerAdvanced::create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size) {
- _THREAD_SAFE_METHOD_
- FontDataAdvanced *fd = nullptr;
- if (p_type == "fnt" || p_type == "font") {
- fd = memnew(BitmapFontDataAdvanced);
+_FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced *p_font_data, const Vector2i &p_size) const {
+ if (p_font_data->cache.has(p_size)) {
+ return true;
+ }
+
+ FontDataForSizeAdvanced *fd = memnew(FontDataForSizeAdvanced);
+ fd->size = p_size;
+ if (p_font_data->data_ptr) {
+ // Init dynamic font.
#ifdef MODULE_FREETYPE_ENABLED
- } else if (p_type == "ttf" || p_type == "otf" || p_type == "woff") {
- fd = memnew(DynamicFontDataAdvanced);
+ int error = 0;
+ if (!library) {
+ error = FT_Init_FreeType(&library);
+ ERR_FAIL_COND_V_MSG(error != 0, false, TTR("FreeType: Error initializing library:") + " '" + String(FT_Error_String(error)) + "'.");
+ }
+
+ memset(&fd->stream, 0, sizeof(FT_StreamRec));
+ fd->stream.base = (unsigned char *)p_font_data->data_ptr;
+ fd->stream.size = p_font_data->data_size;
+ fd->stream.pos = 0;
+
+ FT_Open_Args fargs;
+ memset(&fargs, 0, sizeof(FT_Open_Args));
+ fargs.memory_base = (unsigned char *)p_font_data->data_ptr;
+ fargs.memory_size = p_font_data->data_size;
+ fargs.flags = FT_OPEN_MEMORY;
+ fargs.stream = &fd->stream;
+ error = FT_Open_Face(library, &fargs, 0, &fd->face);
+ if (error) {
+ FT_Done_Face(fd->face);
+ fd->face = nullptr;
+ ERR_FAIL_V_MSG(false, TTR("FreeType: Error loading font:") + " '" + String(FT_Error_String(error)) + "'.");
+ }
+ fd->hb_handle = hb_ft_font_create(fd->face, nullptr);
+ if (fd->hb_handle == nullptr) {
+ FT_Done_Face(fd->face);
+ fd->face = nullptr;
+ ERR_FAIL_V_MSG(false, TTR("HarfBuzz: Error creating FreeType font object."));
+ }
+
+ if (p_font_data->msdf) {
+ fd->oversampling = 1.0f;
+ fd->size.x = p_font_data->msdf_source_size;
+ } else if (p_font_data->oversampling <= 0.0f) {
+ fd->oversampling = TS->font_get_global_oversampling();
+ } else {
+ fd->oversampling = p_font_data->oversampling;
+ }
+
+ if (FT_HAS_COLOR(fd->face) && fd->face->num_fixed_sizes > 0) {
+ int best_match = 0;
+ int diff = ABS(fd->size.x - ((int64_t)fd->face->available_sizes[0].width));
+ fd->scale = real_t(fd->size.x * fd->oversampling) / fd->face->available_sizes[0].width;
+ for (int i = 1; i < fd->face->num_fixed_sizes; i++) {
+ int ndiff = ABS(fd->size.x - ((int64_t)fd->face->available_sizes[i].width));
+ if (ndiff < diff) {
+ best_match = i;
+ diff = ndiff;
+ fd->scale = real_t(fd->size.x * fd->oversampling) / fd->face->available_sizes[i].width;
+ }
+ }
+ FT_Select_Size(fd->face, best_match);
+ } else {
+ FT_Set_Pixel_Sizes(fd->face, 0, fd->size.x * fd->oversampling);
+ }
+
+ fd->ascent = (fd->face->size->metrics.ascender / 64.0) / fd->oversampling * fd->scale;
+ fd->descent = (-fd->face->size->metrics.descender / 64.0) / fd->oversampling * fd->scale;
+ fd->underline_position = (-FT_MulFix(fd->face->underline_position, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale;
+ fd->underline_thickness = (FT_MulFix(fd->face->underline_thickness, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale;
+
+ if (!p_font_data->face_init) {
+ // Get supported scripts from OpenType font data.
+ p_font_data->supported_scripts.clear();
+ unsigned int count = hb_ot_layout_table_get_script_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GSUB, 0, nullptr, nullptr);
+ if (count != 0) {
+ hb_tag_t *script_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
+ hb_ot_layout_table_get_script_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GSUB, 0, &count, script_tags);
+ for (unsigned int i = 0; i < count; i++) {
+ p_font_data->supported_scripts.insert(script_tags[i]);
+ }
+ memfree(script_tags);
+ }
+ count = hb_ot_layout_table_get_script_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GPOS, 0, nullptr, nullptr);
+ if (count != 0) {
+ hb_tag_t *script_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
+ hb_ot_layout_table_get_script_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GPOS, 0, &count, script_tags);
+ for (unsigned int i = 0; i < count; i++) {
+ p_font_data->supported_scripts.insert(script_tags[i]);
+ }
+ memfree(script_tags);
+ }
+
+ // Get supported scripts from OS2 table.
+ TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(fd->face, FT_SFNT_OS2);
+ if (os2) {
+ if ((os2->ulUnicodeRange1 & 1L << 4) || (os2->ulUnicodeRange1 & 1L << 5) || (os2->ulUnicodeRange1 & 1L << 6) || (os2->ulUnicodeRange1 & 1L << 31) || (os2->ulUnicodeRange2 & 1L << 0) || (os2->ulUnicodeRange2 & 1L << 1) || (os2->ulUnicodeRange2 & 1L << 2) || (os2->ulUnicodeRange2 & 1L << 3) || (os2->ulUnicodeRange2 & 1L << 4) || (os2->ulUnicodeRange2 & 1L << 5) || (os2->ulUnicodeRange2 & 1L << 6) || (os2->ulUnicodeRange2 & 1L << 7) || (os2->ulUnicodeRange2 & 1L << 8) || (os2->ulUnicodeRange2 & 1L << 9) || (os2->ulUnicodeRange2 & 1L << 10) || (os2->ulUnicodeRange2 & 1L << 11) || (os2->ulUnicodeRange2 & 1L << 12) || (os2->ulUnicodeRange2 & 1L << 13) || (os2->ulUnicodeRange2 & 1L << 14) || (os2->ulUnicodeRange2 & 1L << 15) || (os2->ulUnicodeRange2 & 1L << 30) || (os2->ulUnicodeRange3 & 1L << 0) || (os2->ulUnicodeRange3 & 1L << 1) || (os2->ulUnicodeRange3 & 1L << 2) || (os2->ulUnicodeRange3 & 1L << 4) || (os2->ulUnicodeRange3 & 1L << 5) || (os2->ulUnicodeRange3 & 1L << 18) || (os2->ulUnicodeRange3 & 1L << 24) || (os2->ulUnicodeRange3 & 1L << 25) || (os2->ulUnicodeRange3 & 1L << 26) || (os2->ulUnicodeRange3 & 1L << 27) || (os2->ulUnicodeRange3 & 1L << 28) || (os2->ulUnicodeRange4 & 1L << 3) || (os2->ulUnicodeRange4 & 1L << 6) || (os2->ulUnicodeRange4 & 1L << 15) || (os2->ulUnicodeRange4 & 1L << 23) || (os2->ulUnicodeRange4 & 1L << 24) || (os2->ulUnicodeRange4 & 1L << 26)) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_COMMON);
+ }
+ if ((os2->ulUnicodeRange1 & 1L << 0) || (os2->ulUnicodeRange1 & 1L << 1) || (os2->ulUnicodeRange1 & 1L << 2) || (os2->ulUnicodeRange1 & 1L << 3) || (os2->ulUnicodeRange1 & 1L << 29)) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_LATIN);
+ }
+ if ((os2->ulUnicodeRange1 & 1L << 7) || (os2->ulUnicodeRange1 & 1L << 30)) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_GREEK);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 8) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_COPTIC);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 9) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_CYRILLIC);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 10) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_ARMENIAN);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 11) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_HEBREW);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 12) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_VAI);
+ }
+ if ((os2->ulUnicodeRange1 & 1L << 13) || (os2->ulUnicodeRange2 & 1L << 31) || (os2->ulUnicodeRange3 & 1L << 3)) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_ARABIC);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 14) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_NKO);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 15) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_DEVANAGARI);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 16) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_BENGALI);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 17) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_GURMUKHI);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 18) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_GUJARATI);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 19) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_ORIYA);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 20) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_TAMIL);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 21) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_TELUGU);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 22) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_KANNADA);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 23) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_MALAYALAM);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 24) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_THAI);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 25) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_LAO);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 26) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_GEORGIAN);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 27) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_BALINESE);
+ }
+ if ((os2->ulUnicodeRange1 & 1L << 28) || (os2->ulUnicodeRange2 & 1L << 20) || (os2->ulUnicodeRange2 & 1L << 24)) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_HANGUL);
+ }
+ if ((os2->ulUnicodeRange2 & 1L << 21) || (os2->ulUnicodeRange2 & 1L << 22) || (os2->ulUnicodeRange2 & 1L << 23) || (os2->ulUnicodeRange2 & 1L << 26) || (os2->ulUnicodeRange2 & 1L << 27) || (os2->ulUnicodeRange2 & 1L << 29)) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_HAN);
+ }
+ if (os2->ulUnicodeRange2 & 1L << 17) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_HIRAGANA);
+ }
+ if (os2->ulUnicodeRange2 & 1L << 18) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_KATAKANA);
+ }
+ if (os2->ulUnicodeRange2 & 1L << 19) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_BOPOMOFO);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 6) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_TIBETAN);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 7) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_SYRIAC);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 8) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_THAANA);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 9) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_SINHALA);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 10) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_MYANMAR);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 11) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_ETHIOPIC);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 12) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_CHEROKEE);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 13) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_CANADIAN_SYLLABICS);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 14) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_OGHAM);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 15) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_RUNIC);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 16) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_KHMER);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 17) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_MONGOLIAN);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 19) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_YI);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 20) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_HANUNOO);
+ p_font_data->supported_scripts.insert(HB_SCRIPT_TAGBANWA);
+ p_font_data->supported_scripts.insert(HB_SCRIPT_BUHID);
+ p_font_data->supported_scripts.insert(HB_SCRIPT_TAGALOG);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 21) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_OLD_ITALIC);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 22) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_GOTHIC);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 23) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_DESERET);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 29) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_LIMBU);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 30) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_TAI_LE);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 31) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_NEW_TAI_LUE);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 0) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_BUGINESE);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 1) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_GLAGOLITIC);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 2) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_TIFINAGH);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 4) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_SYLOTI_NAGRI);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 5) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_LINEAR_B);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 7) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_UGARITIC);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 8) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_OLD_PERSIAN);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 9) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_SHAVIAN);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 10) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_OSMANYA);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 11) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_CYPRIOT);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 12) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_KHAROSHTHI);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 13) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_TAI_VIET);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 14) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_CUNEIFORM);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 16) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_SUNDANESE);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 17) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_LEPCHA);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 18) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_OL_CHIKI);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 19) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_SAURASHTRA);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 20) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_KAYAH_LI);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 21) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_REJANG);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 22) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_CHAM);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 25) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_ANATOLIAN_HIEROGLYPHS);
+ }
+ }
+
+ // Read OpenType feature tags.
+ p_font_data->supported_features.clear();
+ count = hb_ot_layout_table_get_feature_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GSUB, 0, nullptr, nullptr);
+ if (count != 0) {
+ hb_tag_t *feature_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
+ hb_ot_layout_table_get_feature_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GSUB, 0, &count, feature_tags);
+ for (unsigned int i = 0; i < count; i++) {
+ p_font_data->supported_features[feature_tags[i]] = 1;
+ }
+ memfree(feature_tags);
+ }
+ count = hb_ot_layout_table_get_feature_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GPOS, 0, nullptr, nullptr);
+ if (count != 0) {
+ hb_tag_t *feature_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
+ hb_ot_layout_table_get_feature_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GPOS, 0, &count, feature_tags);
+ for (unsigned int i = 0; i < count; i++) {
+ p_font_data->supported_features[feature_tags[i]] = 1;
+ }
+ memfree(feature_tags);
+ }
+
+ // Read OpenType variations.
+ p_font_data->supported_varaitions.clear();
+ if (fd->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
+ FT_MM_Var *amaster;
+ FT_Get_MM_Var(fd->face, &amaster);
+ for (FT_UInt i = 0; i < amaster->num_axis; i++) {
+ p_font_data->supported_varaitions[(int32_t)amaster->axis[i].tag] = Vector3i(amaster->axis[i].minimum / 65536, amaster->axis[i].maximum / 65536, amaster->axis[i].def / 65536);
+ }
+ FT_Done_MM_Var(library, amaster);
+ }
+ p_font_data->face_init = true;
+ }
+
+ // Write variations.
+ if (fd->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
+ FT_MM_Var *amaster;
+
+ FT_Get_MM_Var(fd->face, &amaster);
+
+ Vector<hb_variation_t> hb_vars;
+ Vector<FT_Fixed> coords;
+ coords.resize(amaster->num_axis);
+
+ FT_Get_Var_Design_Coordinates(fd->face, coords.size(), coords.ptrw());
+
+ for (FT_UInt i = 0; i < amaster->num_axis; i++) {
+ hb_variation_t var;
+
+ // Reset to default.
+ var.tag = amaster->axis[i].tag;
+ var.value = (double)amaster->axis[i].def / 65536.f;
+ coords.write[i] = amaster->axis[i].def;
+
+ if (p_font_data->variation_coordinates.has(var.tag)) {
+ var.value = p_font_data->variation_coordinates[var.tag];
+ coords.write[i] = CLAMP(var.value * 65536.f, 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)];
+ coords.write[i] = CLAMP(var.value * 65536.f, amaster->axis[i].minimum, amaster->axis[i].maximum);
+ }
+
+ hb_vars.push_back(var);
+ }
+
+ FT_Set_Var_Design_Coordinates(fd->face, coords.size(), coords.ptrw());
+ hb_font_set_variations(fd->hb_handle, hb_vars.is_empty() ? nullptr : &hb_vars[0], hb_vars.size());
+ FT_Done_MM_Var(library, amaster);
+ }
+#else
+ ERR_FAIL_V_MSG(false, TTR("FreeType: Can't load dynamic font, engine is compiled without FreeType support!");
#endif
} else {
- return RID();
+ // Init bitmap font.
+ fd->hb_handle = hb_bmp_font_create(fd, nullptr);
+ if (!fd->hb_handle) {
+ ERR_FAIL_V_MSG(false, TTR("HarfBuzz: Error creating bitmap font object."));
+ }
}
+ p_font_data->cache[p_size] = fd;
+ return true;
+}
- Error err = fd->load_from_memory(p_data, p_size, p_base_size);
- if (err != OK) {
- memdelete(fd);
- return RID();
+_FORCE_INLINE_ void TextServerAdvanced::_font_clear_cache(FontDataAdvanced *p_font_data) {
+ for (const Map<Vector2i, FontDataForSizeAdvanced *>::Element *E = p_font_data->cache.front(); E; E = E->next()) {
+ memdelete(E->get());
}
+ p_font_data->cache.clear();
+ p_font_data->face_init = false;
+ p_font_data->supported_features.clear();
+ p_font_data->supported_varaitions.clear();
+ p_font_data->supported_scripts.clear();
+}
+
+hb_font_t *TextServerAdvanced::_font_get_hb_handle(RID p_font_rid, int p_size) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, nullptr);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), nullptr);
+
+ return fd->cache[size]->hb_handle;
+}
+
+RID TextServerAdvanced::create_font() {
+ FontDataAdvanced *fd = memnew(FontDataAdvanced);
return font_owner.make_rid(fd);
}
-RID TextServerAdvanced::create_font_bitmap(float p_height, float p_ascent, int p_base_size) {
- _THREAD_SAFE_METHOD_
- FontDataAdvanced *fd = memnew(BitmapFontDataAdvanced);
- Error err = fd->bitmap_new(p_height, p_ascent, p_base_size);
- if (err != OK) {
- memdelete(fd);
- return RID();
+void TextServerAdvanced::font_set_data(RID p_font_rid, const PackedByteArray &p_data) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ _font_clear_cache(fd);
+ fd->data = p_data;
+ fd->data_ptr = fd->data.ptr();
+ fd->data_size = fd->data.size();
+}
+
+void TextServerAdvanced::font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ _font_clear_cache(fd);
+ fd->data.clear();
+ fd->data_ptr = p_data_ptr;
+ fd->data_size = p_data_size;
+}
+
+void TextServerAdvanced::font_set_antialiased(RID p_font_rid, bool p_antialiased) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->antialiased != p_antialiased) {
+ _font_clear_cache(fd);
+ fd->antialiased = p_antialiased;
}
+}
- return font_owner.make_rid(fd);
+bool TextServerAdvanced::font_is_antialiased(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->antialiased;
}
-void TextServerAdvanced::font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) {
- _THREAD_SAFE_METHOD_
- FontDataAdvanced *fd = font_owner.getornull(p_font);
+void TextServerAdvanced::font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
- fd->bitmap_add_texture(p_texture);
+
+ MutexLock lock(fd->mutex);
+ if (fd->msdf != p_msdf) {
+ _font_clear_cache(fd);
+ fd->msdf = p_msdf;
+ }
}
-void TextServerAdvanced::font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) {
- _THREAD_SAFE_METHOD_
- FontDataAdvanced *fd = font_owner.getornull(p_font);
+bool TextServerAdvanced::font_is_multichannel_signed_distance_field(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->msdf;
+}
+
+void TextServerAdvanced::font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
- fd->bitmap_add_char(p_char, p_texture_idx, p_rect, p_align, p_advance);
+
+ MutexLock lock(fd->mutex);
+ if (fd->msdf_range != p_msdf_pixel_range) {
+ _font_clear_cache(fd);
+ fd->msdf_range = p_msdf_pixel_range;
+ }
}
-void TextServerAdvanced::font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) {
- _THREAD_SAFE_METHOD_
- FontDataAdvanced *fd = font_owner.getornull(p_font);
+int TextServerAdvanced::font_get_msdf_pixel_range(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->msdf_range;
+}
+
+void TextServerAdvanced::font_set_msdf_size(RID p_font_rid, int p_msdf_size) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
- fd->bitmap_add_kerning_pair(p_A, p_B, p_kerning);
+
+ MutexLock lock(fd->mutex);
+ if (fd->msdf_source_size != p_msdf_size) {
+ _font_clear_cache(fd);
+ fd->msdf_source_size = p_msdf_size;
+ }
}
-float TextServerAdvanced::font_get_height(RID p_font, int p_size) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
+int TextServerAdvanced::font_get_msdf_size(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->msdf_source_size;
+}
+
+void TextServerAdvanced::font_set_fixed_size(RID p_font_rid, int p_fixed_size) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->fixed_size != p_fixed_size) {
+ fd->fixed_size = p_fixed_size;
+ }
+}
+
+int TextServerAdvanced::font_get_fixed_size(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->fixed_size;
+}
+
+void TextServerAdvanced::font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->force_autohinter != p_force_autohinter) {
+ _font_clear_cache(fd);
+ fd->force_autohinter = p_force_autohinter;
+ }
+}
+
+bool TextServerAdvanced::font_is_force_autohinter(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->force_autohinter;
+}
+
+void TextServerAdvanced::font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->hinting != p_hinting) {
+ _font_clear_cache(fd);
+ fd->hinting = p_hinting;
+ }
+}
+
+TextServer::Hinting TextServerAdvanced::font_get_hinting(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, HINTING_NONE);
+
+ MutexLock lock(fd->mutex);
+ return fd->hinting;
+}
+
+void TextServerAdvanced::font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->variation_coordinates != p_variation_coordinates) {
+ _font_clear_cache(fd);
+ fd->variation_coordinates = p_variation_coordinates;
+ }
+}
+
+Dictionary TextServerAdvanced::font_get_variation_coordinates(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Dictionary());
+
+ MutexLock lock(fd->mutex);
+ return fd->variation_coordinates;
+}
+
+void TextServerAdvanced::font_set_oversampling(RID p_font_rid, real_t p_oversampling) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->oversampling != p_oversampling) {
+ _font_clear_cache(fd);
+ fd->oversampling = p_oversampling;
+ }
+}
+
+real_t TextServerAdvanced::font_get_oversampling(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0.f);
+
+ MutexLock lock(fd->mutex);
+ return fd->oversampling;
+}
+
+Array TextServerAdvanced::font_get_size_cache_list(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Array());
+
+ MutexLock lock(fd->mutex);
+ Array ret;
+ for (const Map<Vector2i, FontDataForSizeAdvanced *>::Element *E = fd->cache.front(); E; E = E->next()) {
+ ret.push_back(E->key());
+ }
+ return ret;
+}
+
+void TextServerAdvanced::font_clear_size_cache(RID p_font_rid) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ for (const Map<Vector2i, FontDataForSizeAdvanced *>::Element *E = fd->cache.front(); E; E = E->next()) {
+ memdelete(E->get());
+ }
+ fd->cache.clear();
+}
+
+void TextServerAdvanced::font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->cache.has(p_size)) {
+ memdelete(fd->cache[p_size]);
+ fd->cache.erase(p_size);
+ }
+}
+
+void TextServerAdvanced::font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->cache[size]->ascent = p_ascent;
+}
+
+real_t TextServerAdvanced::font_get_ascent(RID p_font_rid, int p_size) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.f);
- return fd->get_height(p_size);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
+
+ if (fd->msdf) {
+ return fd->cache[size]->ascent * (real_t)p_size / (real_t)fd->msdf_source_size;
+ } else {
+ return fd->cache[size]->ascent;
+ }
}
-float TextServerAdvanced::font_get_ascent(RID p_font, int p_size) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
+void TextServerAdvanced::font_set_descent(RID p_font_rid, int p_size, real_t p_descent) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->cache[size]->descent = p_descent;
+}
+
+real_t TextServerAdvanced::font_get_descent(RID p_font_rid, int p_size) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.f);
- return fd->get_ascent(p_size);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
+
+ if (fd->msdf) {
+ return fd->cache[size]->descent * (real_t)p_size / (real_t)fd->msdf_source_size;
+ } else {
+ return fd->cache[size]->descent;
+ }
}
-float TextServerAdvanced::font_get_descent(RID p_font, int p_size) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
+void TextServerAdvanced::font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->cache[size]->underline_position = p_underline_position;
+}
+
+real_t TextServerAdvanced::font_get_underline_position(RID p_font_rid, int p_size) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.f);
- return fd->get_descent(p_size);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
+
+ if (fd->msdf) {
+ return fd->cache[size]->underline_position * (real_t)p_size / (real_t)fd->msdf_source_size;
+ } else {
+ return fd->cache[size]->underline_position;
+ }
}
-float TextServerAdvanced::font_get_underline_position(RID p_font, int p_size) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
+void TextServerAdvanced::font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->cache[size]->underline_thickness = p_underline_thickness;
+}
+
+real_t TextServerAdvanced::font_get_underline_thickness(RID p_font_rid, int p_size) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.f);
- return fd->get_underline_position(p_size);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
+
+ if (fd->msdf) {
+ return fd->cache[size]->underline_thickness * (real_t)p_size / (real_t)fd->msdf_source_size;
+ } else {
+ return fd->cache[size]->underline_thickness;
+ }
}
-float TextServerAdvanced::font_get_underline_thickness(RID p_font, int p_size) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
+void TextServerAdvanced::font_set_scale(RID p_font_rid, int p_size, real_t p_scale) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->cache[size]->scale = p_scale;
+}
+
+real_t TextServerAdvanced::font_get_scale(RID p_font_rid, int p_size) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.f);
- return fd->get_underline_thickness(p_size);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
+
+ if (fd->msdf) {
+ return fd->cache[size]->scale * (real_t)p_size / (real_t)fd->msdf_source_size;
+ } else {
+ return fd->cache[size]->scale / fd->cache[size]->oversampling;
+ }
}
-int TextServerAdvanced::font_get_spacing_space(RID p_font) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
+void TextServerAdvanced::font_set_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing, int p_value) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ switch (p_spacing) {
+ case TextServer::SPACING_GLYPH: {
+ fd->cache[size]->spacing_glyph = p_value;
+ } break;
+ case TextServer::SPACING_SPACE: {
+ fd->cache[size]->spacing_space = p_value;
+ } break;
+ default: {
+ ERR_FAIL_MSG("Invalid spacing type: " + itos(p_spacing));
+ } break;
+ }
+}
+
+int TextServerAdvanced::font_get_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, 0);
- return fd->get_spacing_space();
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0);
+
+ switch (p_spacing) {
+ case TextServer::SPACING_GLYPH: {
+ if (fd->msdf) {
+ return fd->cache[size]->spacing_glyph * (real_t)p_size / (real_t)fd->msdf_source_size;
+ } else {
+ return fd->cache[size]->spacing_glyph;
+ }
+ } break;
+ case TextServer::SPACING_SPACE: {
+ if (fd->msdf) {
+ return fd->cache[size]->spacing_space * (real_t)p_size / (real_t)fd->msdf_source_size;
+ } else {
+ return fd->cache[size]->spacing_space;
+ }
+ } break;
+ default: {
+ ERR_FAIL_V_MSG(0, "Invalid spacing type: " + itos(p_spacing));
+ } break;
+ }
+ return 0;
}
-void TextServerAdvanced::font_set_spacing_space(RID p_font, int p_value) {
- _THREAD_SAFE_METHOD_
- FontDataAdvanced *fd = font_owner.getornull(p_font);
+int TextServerAdvanced::font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0);
+
+ return fd->cache[size]->textures.size();
+}
+
+void TextServerAdvanced::font_clear_textures(RID p_font_rid, const Vector2i &p_size) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
- fd->set_spacing_space(p_value);
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->cache[size]->textures.clear();
}
-int TextServerAdvanced::font_get_spacing_glyph(RID p_font) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, 0);
- return fd->get_spacing_glyph();
+void TextServerAdvanced::font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ ERR_FAIL_INDEX(p_texture_index, fd->cache[size]->textures.size());
+
+ fd->cache[size]->textures.remove(p_texture_index);
}
-void TextServerAdvanced::font_set_spacing_glyph(RID p_font, int p_value) {
- _THREAD_SAFE_METHOD_
- FontDataAdvanced *fd = font_owner.getornull(p_font);
+void TextServerAdvanced::font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
- fd->set_spacing_glyph(p_value);
+ ERR_FAIL_COND(p_image.is_null());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ ERR_FAIL_COND(p_texture_index < 0);
+ if (p_texture_index >= fd->cache[size]->textures.size()) {
+ fd->cache[size]->textures.resize(p_texture_index + 1);
+ }
+
+ FontTexture &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 = memnew(Image(tex.texture_w, tex.texture_h, 0, tex.format, tex.imgdata));
+ tex.texture = Ref<ImageTexture>();
+ tex.texture.instantiate();
+ tex.texture->create_from_image(img);
}
-void TextServerAdvanced::font_set_antialiased(RID p_font, bool p_antialiased) {
- _THREAD_SAFE_METHOD_
- FontDataAdvanced *fd = font_owner.getornull(p_font);
+Ref<Image> TextServerAdvanced::font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Ref<Image>());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ 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.write[p_texture_index];
+ Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, tex.format, tex.imgdata));
+
+ return img;
+}
+
+void TextServerAdvanced::font_set_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
- fd->set_antialiased(p_antialiased);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ if (p_texture_index >= fd->cache[size]->textures.size()) {
+ fd->cache[size]->textures.resize(p_texture_index + 1);
+ }
+
+ FontTexture &tex = fd->cache[size]->textures.write[p_texture_index];
+ tex.offsets = p_offset;
}
-Dictionary TextServerAdvanced::font_get_feature_list(RID p_font) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, Dictionary());
- return fd->get_feature_list();
+PackedInt32Array TextServerAdvanced::font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ 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), PackedInt32Array());
+ ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), PackedInt32Array());
+
+ const FontTexture &tex = fd->cache[size]->textures.write[p_texture_index];
+ return tex.offsets;
}
-bool TextServerAdvanced::font_get_antialiased(RID p_font) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, false);
- return fd->get_antialiased();
+Array TextServerAdvanced::font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Array());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array());
+
+ Array ret;
+ const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+ const int32_t *E = nullptr;
+ while ((E = gl.next(E))) {
+ ret.push_back(*E);
+ }
+ return ret;
}
-Dictionary TextServerAdvanced::font_get_variation_list(RID p_font) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, Dictionary());
- return fd->get_variation_list();
+void TextServerAdvanced::font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+
+ fd->cache[size]->glyph_map.clear();
}
-void TextServerAdvanced::font_set_variation(RID p_font, const String &p_name, double p_value) {
- _THREAD_SAFE_METHOD_
- FontDataAdvanced *fd = font_owner.getornull(p_font);
+void TextServerAdvanced::font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
- fd->set_variation(p_name, p_value);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+
+ fd->cache[size]->glyph_map.erase(p_glyph);
}
-double TextServerAdvanced::font_get_variation(RID p_font, const String &p_name) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, 0);
- return fd->get_variation(p_name);
+Vector2 TextServerAdvanced::font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Vector2());
+
+ MutexLock lock(fd->mutex);
+ 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)) {
+ 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].advance * (real_t)p_size / (real_t)fd->msdf_source_size;
+ } else {
+ return gl[p_glyph].advance;
+ }
}
-void TextServerAdvanced::font_set_distance_field_hint(RID p_font, bool p_distance_field) {
- _THREAD_SAFE_METHOD_
- FontDataAdvanced *fd = font_owner.getornull(p_font);
+void TextServerAdvanced::font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
- fd->set_distance_field_hint(p_distance_field);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+
+ HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ gl[p_glyph].advance = p_advance;
+ gl[p_glyph].found = true;
}
-bool TextServerAdvanced::font_get_distance_field_hint(RID p_font) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, false);
- return fd->get_distance_field_hint();
+Vector2 TextServerAdvanced::font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Vector2());
+
+ MutexLock lock(fd->mutex);
+ 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)) {
+ 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 * (real_t)p_size.x / (real_t)fd->msdf_source_size;
+ } else {
+ return gl[p_glyph].rect.position;
+ }
}
-void TextServerAdvanced::font_set_hinting(RID p_font, TextServer::Hinting p_hinting) {
- _THREAD_SAFE_METHOD_
- FontDataAdvanced *fd = font_owner.getornull(p_font);
+void TextServerAdvanced::font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
- fd->set_hinting(p_hinting);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+
+ HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ gl[p_glyph].rect.position = p_offset;
+ gl[p_glyph].found = true;
}
-TextServer::Hinting TextServerAdvanced::font_get_hinting(RID p_font) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, TextServer::HINTING_NONE);
- return fd->get_hinting();
+Vector2 TextServerAdvanced::font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Vector2());
+
+ MutexLock lock(fd->mutex);
+ 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)) {
+ 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 * (real_t)p_size.x / (real_t)fd->msdf_source_size;
+ } else {
+ return gl[p_glyph].rect.size;
+ }
}
-void TextServerAdvanced::font_set_force_autohinter(RID p_font, bool p_enabeld) {
- _THREAD_SAFE_METHOD_
- FontDataAdvanced *fd = font_owner.getornull(p_font);
+void TextServerAdvanced::font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
- fd->set_force_autohinter(p_enabeld);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+
+ HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ gl[p_glyph].rect.size = p_gl_size;
+ gl[p_glyph].found = true;
}
-bool TextServerAdvanced::font_get_force_autohinter(RID p_font) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
+Rect2 TextServerAdvanced::font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Rect2());
+
+ MutexLock lock(fd->mutex);
+ 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)) {
+ 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;
+}
+
+void TextServerAdvanced::font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+
+ HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ gl[p_glyph].uv_rect = p_uv_rect;
+ gl[p_glyph].found = true;
+}
+
+int TextServerAdvanced::font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, -1);
+
+ MutexLock lock(fd->mutex);
+ 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)) {
+ 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;
+}
+
+void TextServerAdvanced::font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+
+ HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ gl[p_glyph].texture_idx = p_texture_idx;
+ gl[p_glyph].found = true;
+}
+
+bool TextServerAdvanced::font_get_glyph_contours(RID p_font_rid, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
- return fd->get_force_autohinter();
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), false);
+
+#ifdef MODULE_FREETYPE_ENABLED
+ int error = FT_Load_Glyph(fd->cache[size]->face, p_index, FT_LOAD_NO_BITMAP | (fd->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0));
+ ERR_FAIL_COND_V(error, false);
+
+ r_points.clear();
+ r_contours.clear();
+
+ real_t h = fd->cache[size]->ascent;
+ real_t scale = (1.0 / 64.0) / fd->cache[size]->oversampling * fd->cache[size]->scale;
+ if (fd->msdf) {
+ scale = scale * (real_t)p_size / (real_t)fd->msdf_source_size;
+ }
+ for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_points; i++) {
+ r_points.push_back(Vector3(fd->cache[size]->face->glyph->outline.points[i].x * scale, h - fd->cache[size]->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fd->cache[size]->face->glyph->outline.tags[i])));
+ }
+ for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_contours; i++) {
+ r_contours.push_back(fd->cache[size]->face->glyph->outline.contours[i]);
+ }
+ r_orientation = (FT_Outline_Get_Orientation(&fd->cache[size]->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT);
+#else
+ return false;
+#endif
+ return true;
}
-bool TextServerAdvanced::font_has_char(RID p_font, char32_t p_char) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
+Array TextServerAdvanced::font_get_kerning_list(RID p_font_rid, int p_size) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Array());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array());
+
+ Array ret;
+ for (const Map<Vector2i, Vector2>::Element *E = fd->cache[size]->kerning_map.front(); E; E = E->next()) {
+ ret.push_back(E->key());
+ }
+ return ret;
+}
+
+void TextServerAdvanced::font_clear_kerning_map(RID p_font_rid, int p_size) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->cache[size]->kerning_map.clear();
+}
+
+void TextServerAdvanced::font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->cache[size]->kerning_map.erase(p_glyph_pair);
+}
+
+void TextServerAdvanced::font_set_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->cache[size]->kerning_map[p_glyph_pair] = p_kerning;
+}
+
+Vector2 TextServerAdvanced::font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Vector2());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
+
+ const Map<Vector2i, Vector2> &kern = fd->cache[size]->kerning_map;
+
+ if (kern.has(p_glyph_pair)) {
+ if (fd->msdf) {
+ return kern[p_glyph_pair] * (real_t)p_size / (real_t)fd->msdf_source_size;
+ } else {
+ return kern[p_glyph_pair];
+ }
+ } else {
+#ifdef MODULE_FREETYPE_ENABLED
+ if (fd->cache[size]->face) {
+ FT_Vector delta;
+ FT_Get_Kerning(fd->cache[size]->face, p_glyph_pair.x, p_glyph_pair.y, FT_KERNING_DEFAULT, &delta);
+ if (fd->msdf) {
+ return Vector2(delta.x, delta.y) * (real_t)p_size / (real_t)fd->msdf_source_size;
+ } else {
+ return Vector2(delta.x, delta.y);
+ }
+ }
+#endif
+ }
+ return Vector2();
+}
+
+int32_t TextServerAdvanced::font_get_glyph_index(RID p_font_rid, int p_size, char32_t p_char, char32_t p_variation_selector) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0);
+
+#ifdef MODULE_FREETYPE_ENABLED
+ if (fd->cache[size]->face) {
+ if (p_variation_selector) {
+ return FT_Face_GetCharVariantIndex(fd->cache[size]->face, p_char, p_variation_selector);
+ } else {
+ return FT_Get_Char_Index(fd->cache[size]->face, p_char);
+ }
+ } else {
+ return 0;
+ }
+#else
+ return (int32_t)p_char;
+#endif
+}
+
+bool TextServerAdvanced::font_has_char(RID p_font_rid, char32_t p_char) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
- return fd->has_char(p_char);
+
+ MutexLock lock(fd->mutex);
+ if (fd->cache.is_empty()) {
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), false);
+ }
+ FontDataForSizeAdvanced *at_size = fd->cache.front()->get();
+
+#ifdef MODULE_FREETYPE_ENABLED
+ if (at_size && at_size->face) {
+ return FT_Get_Char_Index(at_size->face, p_char) != 0;
+ }
+#endif
+ return (at_size) ? at_size->glyph_map.has((int32_t)p_char) : false;
}
-String TextServerAdvanced::font_get_supported_chars(RID p_font) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
+String TextServerAdvanced::font_get_supported_chars(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, String());
- return fd->get_supported_chars();
+
+ MutexLock lock(fd->mutex);
+ if (fd->cache.is_empty()) {
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), String());
+ }
+ FontDataForSizeAdvanced *at_size = fd->cache.front()->get();
+
+ String chars;
+#ifdef MODULE_FREETYPE_ENABLED
+ if (at_size && at_size->face) {
+ FT_UInt gindex;
+ FT_ULong charcode = FT_Get_First_Char(at_size->face, &gindex);
+ while (gindex != 0) {
+ if (charcode != 0) {
+ chars += char32_t(charcode);
+ }
+ charcode = FT_Get_Next_Char(at_size->face, charcode, &gindex);
+ }
+ return chars;
+ }
+#endif
+ if (at_size) {
+ const HashMap<int32_t, FontGlyph> &gl = at_size->glyph_map;
+ const int32_t *E = nullptr;
+ while ((E = gl.next(E))) {
+ chars += char32_t(*E);
+ }
+ }
+ return chars;
}
-bool TextServerAdvanced::font_has_outline(RID p_font) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, false);
- return fd->has_outline();
+void TextServerAdvanced::font_render_range(RID p_font_rid, const Vector2i &p_size, char32_t p_start, char32_t p_end) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ for (char32_t i = p_start; i <= p_end; i++) {
+#ifdef MODULE_FREETYPE_ENABLED
+ if (fd->cache[size]->face) {
+ _ensure_glyph(fd, size, FT_Get_Char_Index(fd->cache[size]->face, i));
+ continue;
+ }
+#endif
+ _ensure_glyph(fd, size, (int32_t)i);
+ }
}
-float TextServerAdvanced::font_get_base_size(RID p_font) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, 0.f);
- return fd->get_base_size();
+void TextServerAdvanced::font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ ERR_FAIL_COND(!_ensure_glyph(fd, size, p_index));
}
-bool TextServerAdvanced::font_is_language_supported(RID p_font, const String &p_language) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, false);
- if (fd->lang_support_overrides.has(p_language)) {
- return fd->lang_support_overrides[p_language];
- } else {
- Vector<String> tags = p_language.replace("-", "_").split("_");
- if (tags.size() > 0) {
- if (fd->lang_support_overrides.has(tags[0])) {
- return fd->lang_support_overrides[tags[0]];
+void TextServerAdvanced::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ if (!_ensure_glyph(fd, size, p_index)) {
+ return; // // Invalid or non graphicl glyph, do not display errors, nothing to draw.
+ }
+
+ const FontGlyph &gl = fd->cache[size]->glyph_map[p_index];
+ if (gl.found) {
+ ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size());
+
+ 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)) {
+ modulate.r = modulate.g = modulate.b = 1.0;
+ }
+#endif
+ if (RenderingServer::get_singleton() != nullptr) {
+ RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid();
+ if (fd->msdf) {
+ Point2 cpos = p_pos;
+ cpos += gl.rect.position * (real_t)p_size / (real_t)fd->msdf_source_size;
+ Size2 csize = gl.rect.size * (real_t)p_size / (real_t)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);
+ } else {
+ Point2i cpos = p_pos;
+ cpos += gl.rect.position;
+ Size2i 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);
+ }
}
}
- return fd->is_lang_supported(p_language);
}
}
-void TextServerAdvanced::font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) {
- _THREAD_SAFE_METHOD_
- FontDataAdvanced *fd = font_owner.getornull(p_font);
+void TextServerAdvanced::font_draw_glyph_outline(RID p_font_rid, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
- fd->lang_support_overrides[p_language] = p_supported;
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, Vector2i(p_size, p_outline_size));
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ if (!_ensure_glyph(fd, size, p_index)) {
+ return; // // Invalid or non graphicl glyph, do not display errors, nothing to draw.
+ }
+
+ const FontGlyph &gl = fd->cache[size]->glyph_map[p_index];
+ if (gl.found) {
+ ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size());
+
+ 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)) {
+ modulate.r = modulate.g = modulate.b = 1.0;
+ }
+#endif
+ if (RenderingServer::get_singleton() != nullptr) {
+ RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid();
+ if (fd->msdf) {
+ Point2 cpos = p_pos;
+ cpos += gl.rect.position * (real_t)p_size / (real_t)fd->msdf_source_size;
+ Size2 csize = gl.rect.size * (real_t)p_size / (real_t)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);
+ } else {
+ Point2i cpos = p_pos;
+ cpos += gl.rect.position;
+ Size2i 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);
+ }
+ }
+ }
+ }
}
-bool TextServerAdvanced::font_get_language_support_override(RID p_font, const String &p_language) {
- _THREAD_SAFE_METHOD_
- FontDataAdvanced *fd = font_owner.getornull(p_font);
+bool TextServerAdvanced::font_is_language_supported(RID p_font_rid, const String &p_language) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
- return fd->lang_support_overrides[p_language];
+
+ MutexLock lock(fd->mutex);
+ if (fd->language_support_overrides.has(p_language)) {
+ return fd->language_support_overrides[p_language];
+ } else {
+ return true;
+ }
}
-void TextServerAdvanced::font_remove_language_support_override(RID p_font, const String &p_language) {
- _THREAD_SAFE_METHOD_
- FontDataAdvanced *fd = font_owner.getornull(p_font);
+void TextServerAdvanced::font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
- fd->lang_support_overrides.erase(p_language);
+
+ MutexLock lock(fd->mutex);
+ fd->language_support_overrides[p_language] = p_supported;
}
-Vector<String> TextServerAdvanced::font_get_language_support_overrides(RID p_font) {
- _THREAD_SAFE_METHOD_
- FontDataAdvanced *fd = font_owner.getornull(p_font);
+bool TextServerAdvanced::font_get_language_support_override(RID p_font_rid, const String &p_language) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->language_support_overrides[p_language];
+}
+
+void TextServerAdvanced::font_remove_language_support_override(RID p_font_rid, const String &p_language) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ fd->language_support_overrides.erase(p_language);
+}
+
+Vector<String> TextServerAdvanced::font_get_language_support_overrides(RID p_font_rid) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector<String>());
- Vector<String> ret;
- for (Map<String, bool>::Element *E = fd->lang_support_overrides.front(); E; E = E->next()) {
- ret.push_back(E->key());
+
+ MutexLock lock(fd->mutex);
+ Vector<String> out;
+ for (const Map<String, bool>::Element *E = fd->language_support_overrides.front(); E; E = E->next()) {
+ out.push_back(E->key());
}
- return ret;
+ return out;
}
-bool TextServerAdvanced::font_is_script_supported(RID p_font, const String &p_script) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
+bool TextServerAdvanced::font_is_script_supported(RID p_font_rid, const String &p_script) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
if (fd->script_support_overrides.has(p_script)) {
return fd->script_support_overrides[p_script];
} else {
- hb_script_t scr = hb_script_from_string(p_script.ascii().get_data(), -1);
- return fd->is_script_supported(scr);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), false);
+ return fd->supported_scripts.has(TS->name_to_tag(p_script));
}
}
-void TextServerAdvanced::font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) {
- _THREAD_SAFE_METHOD_
- FontDataAdvanced *fd = font_owner.getornull(p_font);
+void TextServerAdvanced::font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
fd->script_support_overrides[p_script] = p_supported;
}
-bool TextServerAdvanced::font_get_script_support_override(RID p_font, const String &p_script) {
- _THREAD_SAFE_METHOD_
- FontDataAdvanced *fd = font_owner.getornull(p_font);
+bool TextServerAdvanced::font_get_script_support_override(RID p_font_rid, const String &p_script) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
return fd->script_support_overrides[p_script];
}
-void TextServerAdvanced::font_remove_script_support_override(RID p_font, const String &p_script) {
- _THREAD_SAFE_METHOD_
- FontDataAdvanced *fd = font_owner.getornull(p_font);
+void TextServerAdvanced::font_remove_script_support_override(RID p_font_rid, const String &p_script) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
fd->script_support_overrides.erase(p_script);
}
-Vector<String> TextServerAdvanced::font_get_script_support_overrides(RID p_font) {
- _THREAD_SAFE_METHOD_
- FontDataAdvanced *fd = font_owner.getornull(p_font);
+Vector<String> TextServerAdvanced::font_get_script_support_overrides(RID p_font_rid) {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector<String>());
- Vector<String> ret;
- for (Map<String, bool>::Element *E = fd->script_support_overrides.front(); E; E = E->next()) {
- ret.push_back(E->key());
- }
- return ret;
-}
-uint32_t TextServerAdvanced::font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, 0);
- return fd->get_glyph_index(p_char, p_variation_selector);
+ MutexLock lock(fd->mutex);
+ Vector<String> out;
+ for (const Map<String, bool>::Element *E = fd->script_support_overrides.front(); E; E = E->next()) {
+ out.push_back(E->key());
+ }
+ return out;
}
-Vector2 TextServerAdvanced::font_get_glyph_advance(RID p_font, uint32_t p_index, int p_size) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, Vector2());
- return fd->get_advance(p_index, p_size);
-}
+Dictionary TextServerAdvanced::font_supported_feature_list(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Dictionary());
-Vector2 TextServerAdvanced::font_get_glyph_kerning(RID p_font, uint32_t p_index_a, uint32_t p_index_b, int p_size) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, Vector2());
- return fd->get_kerning(p_index_a, p_index_b, p_size);
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Dictionary());
+ return fd->supported_features;
}
-Vector2 TextServerAdvanced::font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, Vector2());
- return fd->draw_glyph(p_canvas, p_size, p_pos, p_index, p_color);
-}
-
-Vector2 TextServerAdvanced::font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, Vector2());
- return fd->draw_glyph_outline(p_canvas, p_size, p_outline_size, p_pos, p_index, p_color);
-}
+Dictionary TextServerAdvanced::font_supported_variation_list(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Dictionary());
-bool TextServerAdvanced::font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const {
- _THREAD_SAFE_METHOD_
- const FontDataAdvanced *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, false);
- return fd->get_glyph_contours(p_size, p_index, r_points, r_contours, r_orientation);
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Dictionary());
+ return fd->supported_varaitions;
}
-float TextServerAdvanced::font_get_oversampling() const {
+real_t TextServerAdvanced::font_get_global_oversampling() const {
return oversampling;
}
-void TextServerAdvanced::font_set_oversampling(float p_oversampling) {
+void TextServerAdvanced::font_set_global_oversampling(real_t p_oversampling) {
_THREAD_SAFE_METHOD_
if (oversampling != p_oversampling) {
oversampling = p_oversampling;
List<RID> fonts;
font_owner.get_owned_list(&fonts);
+ bool font_cleared = false;
for (const RID &E : fonts) {
- font_owner.getornull(E)->clear_cache();
+ if (!font_is_multichannel_signed_distance_field(E) && font_get_oversampling(E) <= 0) {
+ font_clear_size_cache(E);
+ font_cleared = true;
+ }
}
- List<RID> text_bufs;
- shaped_owner.get_owned_list(&text_bufs);
- for (const RID &E : text_bufs) {
- invalidate(shaped_owner.getornull(E));
+ if (font_cleared) {
+ List<RID> text_bufs;
+ shaped_owner.get_owned_list(&text_bufs);
+ for (const RID &E : text_bufs) {
+ invalidate(shaped_owner.getornull(E));
+ }
}
}
}
-Vector<String> TextServerAdvanced::get_system_fonts() const {
- return Vector<String>();
-}
-
/*************************************************************************/
/* Shaped text buffer interface */
/*************************************************************************/
@@ -1029,10 +2895,10 @@ RID TextServerAdvanced::create_shaped_text(TextServer::Direction p_direction, Te
}
void TextServerAdvanced::shaped_text_clear(RID p_shaped) {
- _THREAD_SAFE_METHOD_
ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND(!sd);
+ MutexLock lock(sd->mutex);
sd->parent = RID();
sd->start = 0;
sd->end = 0;
@@ -1044,10 +2910,10 @@ void TextServerAdvanced::shaped_text_clear(RID p_shaped) {
}
void TextServerAdvanced::shaped_text_set_direction(RID p_shaped, TextServer::Direction p_direction) {
- _THREAD_SAFE_METHOD_
ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND(!sd);
+ MutexLock lock(sd->mutex);
if (sd->direction != p_direction) {
if (sd->parent != RID()) {
full_copy(sd);
@@ -1058,16 +2924,18 @@ void TextServerAdvanced::shaped_text_set_direction(RID p_shaped, TextServer::Dir
}
TextServer::Direction TextServerAdvanced::shaped_text_get_direction(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, TextServer::DIRECTION_LTR);
+
+ MutexLock lock(sd->mutex);
return sd->direction;
}
void TextServerAdvanced::shaped_text_set_bidi_override(RID p_shaped, const Vector<Vector2i> &p_override) {
- _THREAD_SAFE_METHOD_
ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND(!sd);
+
+ MutexLock lock(sd->mutex);
if (sd->parent != RID()) {
full_copy(sd);
}
@@ -1076,9 +2944,10 @@ void TextServerAdvanced::shaped_text_set_bidi_override(RID p_shaped, const Vecto
}
void TextServerAdvanced::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) {
- _THREAD_SAFE_METHOD_
ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND(!sd);
+
+ MutexLock lock(sd->mutex);
if (sd->orientation != p_orientation) {
if (sd->parent != RID()) {
full_copy(sd);
@@ -1089,9 +2958,10 @@ void TextServerAdvanced::shaped_text_set_orientation(RID p_shaped, TextServer::O
}
void TextServerAdvanced::shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) {
- _THREAD_SAFE_METHOD_
ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND(!sd);
+
+ MutexLock lock(sd->mutex);
ERR_FAIL_COND(sd->parent != RID());
if (sd->preserve_invalid != p_enabled) {
sd->preserve_invalid = p_enabled;
@@ -1100,16 +2970,18 @@ void TextServerAdvanced::shaped_text_set_preserve_invalid(RID p_shaped, bool p_e
}
bool TextServerAdvanced::shaped_text_get_preserve_invalid(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
return sd->preserve_invalid;
}
void TextServerAdvanced::shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) {
- _THREAD_SAFE_METHOD_
ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND(!sd);
+
+ MutexLock lock(sd->mutex);
if (sd->preserve_control != p_enabled) {
if (sd->parent != RID()) {
full_copy(sd);
@@ -1120,25 +2992,31 @@ void TextServerAdvanced::shaped_text_set_preserve_control(RID p_shaped, bool p_e
}
bool TextServerAdvanced::shaped_text_get_preserve_control(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
return sd->preserve_control;
}
TextServer::Orientation TextServerAdvanced::shaped_text_get_orientation(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, TextServer::ORIENTATION_HORIZONTAL);
+
+ MutexLock lock(sd->mutex);
return sd->orientation;
}
bool TextServerAdvanced::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
- _THREAD_SAFE_METHOD_
ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, false);
ERR_FAIL_COND_V(p_size <= 0, false);
+ MutexLock lock(sd->mutex);
+ for (int i = 0; i < p_fonts.size(); i++) {
+ ERR_FAIL_COND_V(!font_owner.getornull(p_fonts[i]), false);
+ }
+
if (p_text.is_empty()) {
return true;
}
@@ -1194,9 +3072,10 @@ bool TextServerAdvanced::shaped_text_add_object(RID p_shaped, Variant p_key, con
}
bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align) {
- _THREAD_SAFE_METHOD_
ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
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;
@@ -1231,14 +3110,13 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key,
sd->glyphs.write[i].advance = sd->objects[key].rect.size.y;
}
} else {
- const FontDataAdvanced *fd = font_owner.getornull(gl.font_rid);
- if (fd != nullptr) {
+ if (gl.font_rid.is_valid()) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
- sd->ascent = MAX(sd->ascent, MAX(fd->get_ascent(gl.font_size), -gl.y_off));
- sd->descent = MAX(sd->descent, MAX(fd->get_descent(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(fd->get_advance(gl.index, gl.font_size).x * 0.5));
- sd->descent = MAX(sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).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));
@@ -1257,8 +3135,8 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key,
}
// Align embedded objects to baseline.
- float full_ascent = sd->ascent;
- float full_descent = sd->descent;
+ real_t full_ascent = sd->ascent;
+ real_t full_descent = sd->descent;
for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
if ((E->get().pos >= sd->start) && (E->get().pos < sd->end)) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
@@ -1327,9 +3205,10 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key,
}
RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_length) const {
- _THREAD_SAFE_METHOD_
const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, RID());
+
+ MutexLock lock(sd->mutex);
if (sd->parent != RID()) {
return shaped_text_substr(sd->parent, p_start, p_length);
}
@@ -1363,9 +3242,6 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng
int sd_size = sd->glyphs.size();
const Glyph *sd_glyphs = sd->glyphs.ptr();
- const FontDataAdvanced *fd = nullptr;
- RID prev_rid = RID();
-
for (int ov = 0; ov < sd->bidi_override.size(); ov++) {
UErrorCode err = U_ZERO_ERROR;
@@ -1423,17 +3299,13 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng
new_sd->width += new_sd->objects[key].rect.size.y;
}
} else {
- if (prev_rid != gl.font_rid) {
- fd = font_owner.getornull(gl.font_rid);
- prev_rid = gl.font_rid;
- }
- if (fd != nullptr) {
+ if (gl.font_rid.is_valid()) {
if (new_sd->orientation == ORIENTATION_HORIZONTAL) {
- new_sd->ascent = MAX(new_sd->ascent, MAX(fd->get_ascent(gl.font_size), -gl.y_off));
- new_sd->descent = MAX(new_sd->descent, MAX(fd->get_descent(gl.font_size), gl.y_off));
+ new_sd->ascent = MAX(new_sd->ascent, MAX(font_get_ascent(gl.font_rid, gl.font_size), -gl.y_off));
+ new_sd->descent = MAX(new_sd->descent, MAX(font_get_descent(gl.font_rid, gl.font_size), gl.y_off));
} else {
- new_sd->ascent = MAX(new_sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
- new_sd->descent = MAX(new_sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
+ new_sd->ascent = MAX(new_sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
+ new_sd->descent = MAX(new_sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
}
} else if (new_sd->preserve_invalid || (new_sd->preserve_control && is_control(gl.index))) {
// Glyph not found, replace with hex code box.
@@ -1454,8 +3326,8 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng
}
// Align embedded objects to baseline.
- float full_ascent = new_sd->ascent;
- float full_descent = new_sd->descent;
+ real_t full_ascent = new_sd->ascent;
+ real_t full_descent = new_sd->descent;
for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = new_sd->objects.front(); E; E = E->next()) {
if ((E->get().pos >= new_sd->start) && (E->get().pos < new_sd->end)) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
@@ -1526,16 +3398,18 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng
}
RID TextServerAdvanced::shaped_text_get_parent(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, RID());
+
+ MutexLock lock(sd->mutex);
return sd->parent;
}
-float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t /*JustificationFlag*/ p_jst_flags) {
- _THREAD_SAFE_METHOD_
+real_t TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t /*JustificationFlag*/ p_jst_flags) {
ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
}
@@ -1572,7 +3446,7 @@ float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width,
}
}
- float justification_width;
+ real_t justification_width;
if ((p_jst_flags & JUSTIFICATION_CONSTRAIN_ELLIPSIS) == JUSTIFICATION_CONSTRAIN_ELLIPSIS) {
if (sd->overrun_trim_data.trim_pos >= 0) {
start_pos = sd->overrun_trim_data.trim_pos;
@@ -1612,7 +3486,7 @@ float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width,
}
if ((elongation_count > 0) && ((p_jst_flags & JUSTIFICATION_KASHIDA) == JUSTIFICATION_KASHIDA)) {
- float delta_width_per_kashida = (p_width - justification_width) / elongation_count;
+ real_t delta_width_per_kashida = (p_width - justification_width) / elongation_count;
for (int i = start_pos; i <= end_pos; i++) {
Glyph &gl = sd->glyphs.write[i];
if (gl.count > 0) {
@@ -1627,19 +3501,19 @@ float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width,
}
}
}
- float adv_remain = 0;
+ real_t adv_remain = 0;
if ((space_count > 0) && ((p_jst_flags & JUSTIFICATION_WORD_BOUND) == JUSTIFICATION_WORD_BOUND)) {
- float delta_width_per_space = (p_width - justification_width) / space_count;
+ real_t delta_width_per_space = (p_width - justification_width) / space_count;
for (int i = start_pos; i <= end_pos; i++) {
Glyph &gl = sd->glyphs.write[i];
if (gl.count > 0) {
if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) {
- float old_adv = gl.advance;
- float new_advance;
+ real_t old_adv = gl.advance;
+ real_t new_advance;
if ((gl.flags & GRAPHEME_IS_VIRTUAL) == GRAPHEME_IS_VIRTUAL) {
new_advance = MAX(gl.advance + delta_width_per_space, 0.f);
} else {
- new_advance = MAX(gl.advance + delta_width_per_space, 0.05 * gl.font_size);
+ new_advance = MAX(gl.advance + delta_width_per_space, 0.1 * gl.font_size);
}
gl.advance = new_advance;
adv_remain += (new_advance - gl.advance);
@@ -1667,10 +3541,11 @@ float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width,
return sd->width;
}
-float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) {
- _THREAD_SAFE_METHOD_
+real_t TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<real_t> &p_tab_stops) {
ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
}
@@ -1679,7 +3554,7 @@ float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<float
}
int tab_index = 0;
- float off = 0.f;
+ real_t off = 0.f;
int start, end, delta;
if (sd->para_direction == DIRECTION_LTR) {
@@ -1696,7 +3571,7 @@ float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<float
for (int i = start; i != end; i += delta) {
if ((gl[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) {
- float tab_off = 0.f;
+ real_t tab_off = 0.f;
while (tab_off <= off) {
tab_off += p_tab_stops[tab_index];
tab_index++;
@@ -1704,7 +3579,7 @@ float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<float
tab_index = 0;
}
}
- float old_adv = gl[i].advance;
+ real_t old_adv = gl[i].advance;
gl[i].advance = tab_off - off;
sd->width += gl[i].advance - old_adv;
off = 0;
@@ -1716,10 +3591,11 @@ float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<float
return 0.f;
}
-void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, float p_width, uint8_t p_trim_flags) {
- _THREAD_SAFE_METHOD_
+void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, real_t p_width, uint8_t p_trim_flags) {
ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped_line);
ERR_FAIL_COND_MSG(!sd, "ShapedTextDataAdvanced invalid.");
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
shaped_text_shape(p_shaped_line);
}
@@ -1746,18 +3622,18 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl
int sd_size = sd->glyphs.size();
RID last_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
int last_gl_font_size = sd_glyphs[sd_size - 1].font_size;
- uint32_t dot_gl_idx = font_get_glyph_index(last_gl_font_rid, '.');
- Vector2 dot_adv = font_get_glyph_advance(last_gl_font_rid, dot_gl_idx, last_gl_font_size);
- uint32_t whitespace_gl_idx = font_get_glyph_index(last_gl_font_rid, ' ');
- Vector2 whitespace_adv = font_get_glyph_advance(last_gl_font_rid, whitespace_gl_idx, last_gl_font_size);
+ int32_t dot_gl_idx = font_get_glyph_index(last_gl_font_rid, last_gl_font_size, '.');
+ Vector2 dot_adv = font_get_glyph_advance(last_gl_font_rid, last_gl_font_size, dot_gl_idx);
+ int32_t whitespace_gl_idx = font_get_glyph_index(last_gl_font_rid, last_gl_font_size, ' ');
+ Vector2 whitespace_adv = font_get_glyph_advance(last_gl_font_rid, last_gl_font_size, whitespace_gl_idx);
int ellipsis_width = 0;
if (add_ellipsis) {
- ellipsis_width = 3 * dot_adv.x + font_get_spacing_glyph(last_gl_font_rid) + (cut_per_word ? whitespace_adv.x : 0);
+ ellipsis_width = 3 * dot_adv.x + font_get_spacing(last_gl_font_rid, last_gl_font_size, SPACING_GLYPH) + (cut_per_word ? whitespace_adv.x : 0);
}
int ell_min_characters = 6;
- float width = sd->width;
+ real_t width = sd->width;
bool is_rtl = sd->direction == DIRECTION_RTL || (sd->direction == DIRECTION_AUTO && sd->para_direction == DIRECTION_RTL);
@@ -1842,16 +3718,18 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl
}
TextServer::TrimData TextServerAdvanced::shaped_text_get_trim_data(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V_MSG(!sd, TrimData(), "ShapedTextDataAdvanced invalid.");
+
+ MutexLock lock(sd->mutex);
return sd->overrun_trim_data;
}
bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
- _THREAD_SAFE_METHOD_
ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
shaped_text_shape(p_shaped);
}
@@ -2035,9 +3913,10 @@ _FORCE_INLINE_ int _generate_kashida_justification_opportunies(const String &p_d
}
bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) {
- _THREAD_SAFE_METHOD_
ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
shaped_text_shape(p_shaped);
}
@@ -2145,8 +4024,8 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) {
}
TextServer::Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced *p_sd, char32_t p_char, hb_script_t p_script, hb_direction_t p_direction, RID p_font, int p_font_size) {
- FontDataAdvanced *fd = font_owner.getornull(p_font);
- hb_font_t *hb_font = fd->get_hb_handle(p_font_size);
+ hb_font_t *hb_font = _font_get_hb_handle(p_font, p_font_size);
+ ERR_FAIL_COND_V(hb_font == nullptr, TextServer::Glyph());
hb_buffer_clear_contents(p_sd->hb_buffer);
hb_buffer_set_direction(p_sd->hb_buffer, p_direction);
@@ -2171,16 +4050,17 @@ TextServer::Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced
gl.font_size = p_font_size;
if (glyph_count > 0) {
+ real_t scale = font_get_scale(p_font, p_font_size);
if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
- gl.advance = Math::round(glyph_pos[0].x_advance / (64.0 / fd->get_font_scale(p_font_size)));
+ gl.advance = Math::round(glyph_pos[0].x_advance / (64.0 / scale));
} else {
- gl.advance = -Math::round(glyph_pos[0].y_advance / (64.0 / fd->get_font_scale(p_font_size)));
+ gl.advance = -Math::round(glyph_pos[0].y_advance / (64.0 / scale));
}
gl.count = 1;
gl.index = glyph_info[0].codepoint;
- gl.x_off = Math::round(glyph_pos[0].x_offset / (64.0 / fd->get_font_scale(p_font_size)));
- gl.y_off = -Math::round(glyph_pos[0].y_offset / (64.0 / fd->get_font_scale(p_font_size)));
+ gl.x_off = Math::round(glyph_pos[0].x_offset / (64.0 / scale));
+ gl.y_off = -Math::round(glyph_pos[0].y_offset / (64.0 / scale));
if ((glyph_info[0].codepoint != 0) || !u_isgraph(p_char)) {
gl.flags |= GRAPHEME_IS_VALID;
@@ -2190,13 +4070,8 @@ TextServer::Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced
}
void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_start, int32_t p_end, hb_script_t p_script, hb_direction_t p_direction, Vector<RID> p_fonts, int p_span, int p_fb_index) {
- FontDataAdvanced *fd = nullptr;
- if (p_fb_index < p_fonts.size()) {
- fd = font_owner.getornull(p_fonts[p_fb_index]);
- }
-
int fs = p_sd->spans[p_span].font_size;
- if (fd == nullptr) {
+ if (p_fb_index >= p_fonts.size()) {
// Add fallback glyphs.
for (int i = p_start; i < p_end; i++) {
if (p_sd->preserve_invalid || (p_sd->preserve_control && is_control(p_sd->text[i]))) {
@@ -2227,7 +4102,8 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
return;
}
- hb_font_t *hb_font = fd->get_hb_handle(fs);
+ RID f = p_fonts[p_fb_index];
+ hb_font_t *hb_font = _font_get_hb_handle(f, fs);
ERR_FAIL_COND(hb_font == nullptr);
hb_buffer_clear_contents(p_sd->hb_buffer);
@@ -2307,18 +4183,19 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
gl.index = glyph_info[i].codepoint;
if (gl.index != 0) {
+ real_t scale = font_get_scale(f, fs);
if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
- gl.advance = Math::round(glyph_pos[i].x_advance / (64.0 / fd->get_font_scale(fs)));
+ gl.advance = Math::round(glyph_pos[i].x_advance / (64.0 / scale));
} else {
- gl.advance = -Math::round(glyph_pos[i].y_advance / (64.0 / fd->get_font_scale(fs)));
+ gl.advance = -Math::round(glyph_pos[i].y_advance / (64.0 / scale));
}
- gl.x_off = Math::round(glyph_pos[i].x_offset / (64.0 / fd->get_font_scale(fs)));
- gl.y_off = -Math::round(glyph_pos[i].y_offset / (64.0 / fd->get_font_scale(fs)));
+ gl.x_off = Math::round(glyph_pos[i].x_offset / (64.0 / scale));
+ gl.y_off = -Math::round(glyph_pos[i].y_offset / (64.0 / scale));
}
- if (fd->get_spacing_space() && is_whitespace(p_sd->text[glyph_info[i].cluster])) {
- gl.advance += fd->get_spacing_space();
+ if (font_get_spacing(f, fs, SPACING_SPACE) && is_whitespace(p_sd->text[glyph_info[i].cluster])) {
+ gl.advance += font_get_spacing(f, fs, SPACING_SPACE);
} else {
- gl.advance += fd->get_spacing_glyph();
+ gl.advance += font_get_spacing(f, fs, SPACING_GLYPH);
}
if (p_sd->preserve_control) {
@@ -2356,8 +4233,8 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_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 {
- p_sd->ascent = MAX(p_sd->ascent, Math::round(fd->get_advance(w[i + j].index, fs).x * 0.5));
- p_sd->descent = MAX(p_sd->descent, Math::round(fd->get_advance(w[i + j].index, fs).x * 0.5));
+ p_sd->ascent = MAX(p_sd->ascent, Math::round(font_get_glyph_advance(f, fs, w[i + j].index).x * 0.5));
+ p_sd->descent = MAX(p_sd->descent, Math::round(font_get_glyph_advance(f, fs, w[i + j].index).x * 0.5));
}
p_sd->width += w[i + j].advance;
p_sd->glyphs.push_back(w[i + j]);
@@ -2376,18 +4253,18 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
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);
}
- p_sd->ascent = MAX(p_sd->ascent, fd->get_ascent(fs));
- p_sd->descent = MAX(p_sd->descent, fd->get_descent(fs));
- p_sd->upos = MAX(p_sd->upos, fd->get_underline_position(fs));
- p_sd->uthk = MAX(p_sd->uthk, fd->get_underline_thickness(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(RID p_shaped) {
- _THREAD_SAFE_METHOD_
ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, false);
+ MutexLock lock(sd->mutex);
if (sd->valid) {
return true;
}
@@ -2546,8 +4423,8 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) {
}
// Align embedded objects to baseline.
- float full_ascent = sd->ascent;
- float full_descent = sd->descent;
+ real_t full_ascent = sd->ascent;
+ real_t full_descent = sd->descent;
for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) {
@@ -2614,16 +4491,18 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) {
}
bool TextServerAdvanced::shaped_text_is_ready(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
return sd->valid;
}
Vector<TextServer::Glyph> TextServerAdvanced::shaped_text_get_glyphs(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, Vector<TextServer::Glyph>());
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
}
@@ -2631,16 +4510,18 @@ Vector<TextServer::Glyph> TextServerAdvanced::shaped_text_get_glyphs(RID p_shape
}
Vector2i TextServerAdvanced::shaped_text_get_range(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, Vector2i());
+
+ MutexLock lock(sd->mutex);
return Vector2(sd->start, sd->end);
}
Vector<TextServer::Glyph> TextServerAdvanced::shaped_text_sort_logical(RID p_shaped) {
- _THREAD_SAFE_METHOD_
ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, Vector<TextServer::Glyph>());
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
}
@@ -2655,10 +4536,11 @@ Vector<TextServer::Glyph> TextServerAdvanced::shaped_text_sort_logical(RID p_sha
}
Array TextServerAdvanced::shaped_text_get_objects(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
Array ret;
const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, ret);
+
+ MutexLock lock(sd->mutex);
for (const Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
ret.push_back(E->key());
}
@@ -2667,9 +4549,10 @@ Array TextServerAdvanced::shaped_text_get_objects(RID p_shaped) const {
}
Rect2 TextServerAdvanced::shaped_text_get_object_rect(RID p_shaped, Variant p_key) const {
- _THREAD_SAFE_METHOD_
const ShapedTextDataAdvanced *sd = shaped_owner.getornull(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);
@@ -2678,9 +4561,10 @@ Rect2 TextServerAdvanced::shaped_text_get_object_rect(RID p_shaped, Variant p_ke
}
Size2 TextServerAdvanced::shaped_text_get_size(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, Size2());
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
}
@@ -2691,40 +4575,44 @@ Size2 TextServerAdvanced::shaped_text_get_size(RID p_shaped) const {
}
}
-float TextServerAdvanced::shaped_text_get_ascent(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
+real_t TextServerAdvanced::shaped_text_get_ascent(RID p_shaped) const {
const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
}
return sd->ascent;
}
-float TextServerAdvanced::shaped_text_get_descent(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
+real_t TextServerAdvanced::shaped_text_get_descent(RID p_shaped) const {
const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
}
return sd->descent;
}
-float TextServerAdvanced::shaped_text_get_width(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
+real_t TextServerAdvanced::shaped_text_get_width(RID p_shaped) const {
const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
}
return (sd->text_trimmed ? sd->width_trimmed : sd->width);
}
-float TextServerAdvanced::shaped_text_get_underline_position(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
+real_t TextServerAdvanced::shaped_text_get_underline_position(RID p_shaped) const {
const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
}
@@ -2732,10 +4620,11 @@ float TextServerAdvanced::shaped_text_get_underline_position(RID p_shaped) const
return sd->upos;
}
-float TextServerAdvanced::shaped_text_get_underline_thickness(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
+real_t TextServerAdvanced::shaped_text_get_underline_thickness(RID p_shaped) const {
const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
}
@@ -2762,7 +4651,6 @@ static num_system_data num_systems[]{
};
String TextServerAdvanced::format_number(const String &p_string, const String &p_language) const {
- _THREAD_SAFE_METHOD_
String lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
String res = p_string;
@@ -2789,7 +4677,6 @@ String TextServerAdvanced::format_number(const String &p_string, const String &p
}
String TextServerAdvanced::parse_number(const String &p_string, const String &p_language) const {
- _THREAD_SAFE_METHOD_
String lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
String res = p_string;
@@ -2819,7 +4706,6 @@ String TextServerAdvanced::parse_number(const String &p_string, const String &p_
}
String TextServerAdvanced::percent_sign(const String &p_language) const {
- _THREAD_SAFE_METHOD_
String lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
for (int i = 0; num_systems[i].lang != String(); i++) {
@@ -2849,6 +4735,9 @@ TextServerAdvanced::TextServerAdvanced() {
TextServerAdvanced::~TextServerAdvanced() {
hb_bmp_free_font_funcs();
+ if (library != nullptr) {
+ FT_Done_FreeType(library);
+ }
u_cleanup();
#ifndef ICU_STATIC_DATA
if (icu_data != nullptr) {
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index d19ba41a1a..5989035800 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -39,6 +39,7 @@
#include "servers/text_server.h"
#include "core/templates/rid_owner.h"
+#include "core/templates/thread_work_pool.h"
#include "scene/resources/texture.h"
#include "script_iterator.h"
@@ -53,11 +54,24 @@
#include <unicode/ustring.h>
#include <unicode/utypes.h>
+#include "modules/modules_enabled.gen.h"
+
+#ifdef MODULE_FREETYPE_ENABLED
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_TRUETYPE_TABLES_H
+#include FT_STROKER_H
+#include FT_ADVANCES_H
+#include FT_MULTIPLE_MASTERS_H
+#include FT_BBOX_H
+
+#include <hb-ft.h>
+#include <hb-ot.h>
+#endif
+
#include <hb-icu.h>
#include <hb.h>
-#include "font_adv.h"
-
class TextServerAdvanced : public TextServer {
GDCLASS(TextServerAdvanced, TextServer);
_THREAD_SAFE_CLASS_
@@ -65,8 +79,149 @@ class TextServerAdvanced : public TextServer {
static String interface_name;
static uint32_t interface_features;
+ // ICU support data.
+
uint8_t *icu_data = nullptr;
+ // Font cache data.
+
+#ifdef MODULE_FREETYPE_ENABLED
+ mutable FT_Library library = nullptr;
+#endif
+
+ const int rect_range = 2;
+
+ struct FontTexture {
+ Image::Format format;
+ PackedByteArray imgdata;
+ int texture_w = 0;
+ int texture_h = 0;
+ PackedInt32Array offsets;
+ Ref<ImageTexture> texture;
+ };
+
+ struct FontTexturePosition {
+ int index = 0;
+ int x = 0;
+ int y = 0;
+ };
+
+ struct FontGlyph {
+ bool found = false;
+ int texture_idx = -1;
+ Rect2 rect;
+ Rect2 uv_rect;
+ Vector2 advance;
+ };
+
+ struct FontDataForSizeAdvanced {
+ real_t ascent = 0.f;
+ real_t descent = 0.f;
+ real_t underline_position = 0.f;
+ real_t underline_thickness = 0.f;
+ real_t scale = 1.f;
+ real_t oversampling = 1.f;
+
+ int spacing_glyph = 0;
+ int spacing_space = 0;
+
+ Vector2i size;
+
+ Vector<FontTexture> textures;
+ HashMap<int32_t, FontGlyph> glyph_map;
+ Map<Vector2i, Vector2> kerning_map;
+
+ hb_font_t *hb_handle = nullptr;
+
+#ifdef MODULE_FREETYPE_ENABLED
+ FT_Face face = nullptr;
+ FT_StreamRec stream;
+#endif
+
+ ~FontDataForSizeAdvanced() {
+ if (hb_handle != nullptr) {
+ hb_font_destroy(hb_handle);
+ }
+#ifdef MODULE_FREETYPE_ENABLED
+ if (face != nullptr) {
+ FT_Done_Face(face);
+ }
+#endif
+ }
+ };
+
+ struct FontDataAdvanced {
+ Mutex mutex;
+
+ bool antialiased = true;
+ bool msdf = false;
+ int msdf_range = 14;
+ int msdf_source_size = 48;
+ int fixed_size = 0;
+ bool force_autohinter = false;
+ TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
+ Dictionary variation_coordinates;
+ real_t oversampling = 0.f;
+
+ Map<Vector2i, FontDataForSizeAdvanced *> cache;
+
+ bool face_init = false;
+ Set<uint32_t> supported_scripts;
+ Dictionary supported_features;
+ Dictionary supported_varaitions;
+
+ // Language/script support override.
+ Map<String, bool> language_support_overrides;
+ Map<String, bool> script_support_overrides;
+
+ PackedByteArray data;
+ const uint8_t *data_ptr;
+ size_t data_size;
+ mutable ThreadWorkPool work_pool;
+
+ ~FontDataAdvanced() {
+ work_pool.finish();
+ for (const Map<Vector2i, FontDataForSizeAdvanced *>::Element *E = cache.front(); E; E = E->next()) {
+ memdelete(E->get());
+ }
+ cache.clear();
+ }
+ };
+
+ _FORCE_INLINE_ FontTexturePosition find_texture_pos_for_glyph(FontDataForSizeAdvanced *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) const;
+#ifdef MODULE_MSDFGEN_ENABLED
+ _FORCE_INLINE_ FontGlyph rasterize_msdf(FontDataAdvanced *p_font_data, FontDataForSizeAdvanced *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(FontDataForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const;
+#endif
+ _FORCE_INLINE_ bool _ensure_glyph(FontDataAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const;
+ _FORCE_INLINE_ bool _ensure_cache_for_size(FontDataAdvanced *p_font_data, const Vector2i &p_size) const;
+ _FORCE_INLINE_ void _font_clear_cache(FontDataAdvanced *p_font_data);
+ void _generateMTSDF_threaded(uint32_t y, void *p_td) const;
+
+ _FORCE_INLINE_ Vector2i _get_size(const FontDataAdvanced *p_font_data, int p_size) const {
+ if (p_font_data->msdf) {
+ return Vector2i(p_font_data->msdf_source_size, 0);
+ } else if (p_font_data->fixed_size > 0) {
+ return Vector2i(p_font_data->fixed_size, 0);
+ } else {
+ return Vector2i(p_size, 0);
+ }
+ }
+
+ _FORCE_INLINE_ Vector2i _get_size_outline(const FontDataAdvanced *p_font_data, const Vector2i &p_size) const {
+ if (p_font_data->msdf) {
+ return Vector2i(p_font_data->msdf_source_size, 0);
+ } else if (p_font_data->fixed_size > 0) {
+ return Vector2i(p_font_data->fixed_size, MIN(p_size.y, 1));
+ } else {
+ return p_size;
+ }
+ }
+
+ // Shaped text cache data.
+
struct ShapedTextDataAdvanced : public ShapedTextData {
/* Intermediate data */
Char16String utf16;
@@ -88,7 +243,9 @@ class TextServerAdvanced : public TextServer {
}
};
- float oversampling = 1.f;
+ // Common data.
+
+ real_t oversampling = 1.f;
mutable RID_PtrOwner<FontDataAdvanced> font_owner;
mutable RID_PtrOwner<ShapedTextDataAdvanced> shaped_owner;
@@ -97,6 +254,30 @@ class TextServerAdvanced : public TextServer {
void _shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_start, int32_t p_end, hb_script_t p_script, hb_direction_t p_direction, Vector<RID> p_fonts, int p_span, int p_fb_index);
TextServer::Glyph _shape_single_glyph(ShapedTextDataAdvanced *p_sd, char32_t p_char, hb_script_t p_script, hb_direction_t p_direction, RID p_font, int p_font_size);
+ // HarfBuzz bitmap font interface.
+
+ static hb_font_funcs_t *funcs;
+
+ struct hb_bmp_font_t {
+ TextServerAdvanced::FontDataForSizeAdvanced *face = nullptr;
+ bool unref = false; /* Whether to destroy bm_face when done. */
+ };
+
+ static hb_bmp_font_t *_hb_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref);
+ static void _hb_bmp_font_destroy(void *p_data);
+ static hb_bool_t hb_bmp_get_nominal_glyph(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_unicode, hb_codepoint_t *r_glyph, void *p_user_data);
+ static hb_position_t hb_bmp_get_glyph_h_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data);
+ static hb_position_t hb_bmp_get_glyph_v_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data);
+ static hb_position_t hb_bmp_get_glyph_h_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data);
+ static hb_position_t hb_bmp_get_glyph_v_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data);
+ static hb_bool_t hb_bmp_get_glyph_v_origin(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_position_t *r_x, hb_position_t *r_y, void *p_user_data);
+ static hb_bool_t hb_bmp_get_glyph_extents(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_glyph_extents_t *r_extents, void *p_user_data);
+ static hb_bool_t hb_bmp_get_font_h_extents(hb_font_t *p_font, void *p_font_data, hb_font_extents_t *r_metrics, void *p_user_data);
+ static void hb_bmp_create_font_funcs();
+ static void hb_bmp_free_font_funcs();
+ static void _hb_bmp_font_set_funcs(hb_font_t *p_font, TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref);
+ static hb_font_t *hb_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, hb_destroy_func_t p_destroy);
+
protected:
static void _bind_methods(){};
@@ -119,81 +300,132 @@ public:
virtual bool is_locale_right_to_left(const String &p_locale) override;
- virtual int32_t name_to_tag(const String &p_name) override;
- virtual String tag_to_name(int32_t p_tag) override;
+ virtual int32_t name_to_tag(const String &p_name) const override;
+ virtual String tag_to_name(int32_t p_tag) const override;
/* Font interface */
- virtual RID create_font_system(const String &p_name, int p_base_size = 16) override;
- virtual RID create_font_resource(const String &p_filename, int p_base_size = 16) override;
- virtual RID create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16) override;
- virtual RID create_font_bitmap(float p_height, float p_ascent, int p_base_size = 16) override;
+ virtual RID create_font() override;
+
+ virtual void font_set_data(RID p_font_rid, const PackedByteArray &p_data) override;
+ virtual void font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) override;
+
+ virtual void font_set_antialiased(RID p_font_rid, bool p_antialiased) override;
+ virtual bool font_is_antialiased(RID p_font_rid) const override;
+
+ virtual void font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) override;
+ virtual bool font_is_multichannel_signed_distance_field(RID p_font_rid) const override;
+
+ virtual void font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) override;
+ virtual int font_get_msdf_pixel_range(RID p_font_rid) const override;
+
+ virtual void font_set_msdf_size(RID p_font_rid, int p_msdf_size) override;
+ virtual int font_get_msdf_size(RID p_font_rid) const override;
+
+ virtual void font_set_fixed_size(RID p_font_rid, int p_fixed_size) override;
+ virtual int font_get_fixed_size(RID p_font_rid) const override;
+
+ virtual void font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) override;
+ virtual bool font_is_force_autohinter(RID p_font_rid) const override;
+
+ virtual void font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) override;
+ virtual TextServer::Hinting font_get_hinting(RID p_font_rid) const override;
+
+ virtual void font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) override;
+ virtual Dictionary font_get_variation_coordinates(RID p_font_rid) const override;
+
+ virtual void font_set_oversampling(RID p_font_rid, real_t p_oversampling) override;
+ virtual real_t font_get_oversampling(RID p_font_rid) const override;
+
+ virtual Array font_get_size_cache_list(RID p_font_rid) const override;
+ virtual void font_clear_size_cache(RID p_font_rid) override;
+ virtual void font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) override;
+
+ hb_font_t *_font_get_hb_handle(RID p_font, int p_font_size) const;
+
+ virtual void font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) override;
+ virtual real_t font_get_ascent(RID p_font_rid, int p_size) const override;
+
+ virtual void font_set_descent(RID p_font_rid, int p_size, real_t p_descent) override;
+ virtual real_t font_get_descent(RID p_font_rid, int p_size) const override;
+
+ virtual void font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) override;
+ virtual real_t font_get_underline_position(RID p_font_rid, int p_size) const override;
+
+ virtual void font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) override;
+ virtual real_t font_get_underline_thickness(RID p_font_rid, int p_size) const override;
+
+ virtual void font_set_scale(RID p_font_rid, int p_size, real_t p_scale) override;
+ virtual real_t font_get_scale(RID p_font_rid, int p_size) const override;
+
+ virtual void font_set_spacing(RID p_font_rid, int p_size, SpacingType p_spacing, int p_value) override;
+ virtual int font_get_spacing(RID p_font_rid, int p_size, SpacingType p_spacing) const override;
- virtual void font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) override;
- virtual void font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override;
- virtual void font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) override;
+ virtual int font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const override;
+ virtual void font_clear_textures(RID p_font_rid, const Vector2i &p_size) override;
+ virtual void font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) override;
- virtual float font_get_height(RID p_font, int p_size) const override;
- virtual float font_get_ascent(RID p_font, int p_size) const override;
- virtual float font_get_descent(RID p_font, int p_size) const override;
+ virtual void font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) override;
+ virtual Ref<Image> font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const override;
- virtual float font_get_underline_position(RID p_font, int p_size) const override;
- virtual float font_get_underline_thickness(RID p_font, int p_size) const override;
+ virtual void font_set_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) override;
+ virtual PackedInt32Array font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const override;
- virtual int font_get_spacing_space(RID p_font) const override;
- virtual void font_set_spacing_space(RID p_font, int p_value) override;
+ virtual Array font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const override;
+ virtual void font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) override;
+ virtual void font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) override;
- virtual int font_get_spacing_glyph(RID p_font) const override;
- virtual void font_set_spacing_glyph(RID p_font, int p_value) override;
+ virtual Vector2 font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const override;
+ virtual void font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) override;
- virtual void font_set_antialiased(RID p_font, bool p_antialiased) override;
- virtual bool font_get_antialiased(RID p_font) const override;
+ virtual Vector2 font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override;
+ virtual void font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) override;
- virtual Dictionary font_get_feature_list(RID p_font) const override;
- virtual Dictionary font_get_variation_list(RID p_font) const override;
+ virtual Vector2 font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override;
+ virtual void font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) override;
- virtual void font_set_variation(RID p_font, const String &p_name, double p_value) override;
- virtual double font_get_variation(RID p_font, const String &p_name) const override;
+ virtual Rect2 font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override;
+ virtual void font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) override;
- virtual void font_set_hinting(RID p_font, Hinting p_hinting) override;
- virtual Hinting font_get_hinting(RID p_font) const override;
+ virtual int font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override;
+ virtual void font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) override;
- virtual void font_set_distance_field_hint(RID p_font, bool p_distance_field) override;
- virtual bool font_get_distance_field_hint(RID p_font) const override;
+ virtual bool font_get_glyph_contours(RID p_font, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override;
- virtual void font_set_force_autohinter(RID p_font, bool p_enabeld) override;
- virtual bool font_get_force_autohinter(RID p_font) const override;
+ virtual Array font_get_kerning_list(RID p_font_rid, int p_size) const override;
+ virtual void font_clear_kerning_map(RID p_font_rid, int p_size) override;
+ virtual void font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) override;
- virtual bool font_has_char(RID p_font, char32_t p_char) const override;
- virtual String font_get_supported_chars(RID p_font) const override;
+ virtual void font_set_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) override;
+ virtual Vector2 font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const override;
- virtual bool font_has_outline(RID p_font) const override;
- virtual float font_get_base_size(RID p_font) const override;
+ virtual int32_t font_get_glyph_index(RID p_font_rid, int p_size, char32_t p_char, char32_t p_variation_selector = 0) const override;
- virtual bool font_is_language_supported(RID p_font, const String &p_language) const override;
- virtual void font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) override;
- virtual bool font_get_language_support_override(RID p_font, const String &p_language) override;
- virtual void font_remove_language_support_override(RID p_font, const String &p_language) override;
- Vector<String> font_get_language_support_overrides(RID p_font) override;
+ virtual bool font_has_char(RID p_font_rid, char32_t p_char) const override;
+ virtual String font_get_supported_chars(RID p_font_rid) const override;
- virtual bool font_is_script_supported(RID p_font, const String &p_script) const override;
- virtual void font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) override;
- virtual bool font_get_script_support_override(RID p_font, const String &p_script) override;
- virtual void font_remove_script_support_override(RID p_font, const String &p_script) override;
- Vector<String> font_get_script_support_overrides(RID p_font) override;
+ virtual void font_render_range(RID p_font, const Vector2i &p_size, char32_t p_start, char32_t p_end) override;
+ virtual void font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) override;
- virtual uint32_t font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector = 0x0000) const override;
- virtual Vector2 font_get_glyph_advance(RID p_font, uint32_t p_index, int p_size) const override;
- virtual Vector2 font_get_glyph_kerning(RID p_font, uint32_t p_index_a, uint32_t p_index_b, int p_size) const override;
+ virtual void font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
+ virtual void font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
- virtual Vector2 font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
- virtual Vector2 font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
+ virtual bool font_is_language_supported(RID p_font_rid, const String &p_language) const override;
+ virtual void font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) override;
+ virtual bool font_get_language_support_override(RID p_font_rid, const String &p_language) override;
+ virtual void font_remove_language_support_override(RID p_font_rid, const String &p_language) override;
+ virtual Vector<String> font_get_language_support_overrides(RID p_font_rid) override;
- virtual bool font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override;
+ virtual bool font_is_script_supported(RID p_font_rid, const String &p_script) const override;
+ virtual void font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) override;
+ virtual bool font_get_script_support_override(RID p_font_rid, const String &p_script) override;
+ virtual void font_remove_script_support_override(RID p_font_rid, const String &p_script) override;
+ virtual Vector<String> font_get_script_support_overrides(RID p_font_rid) override;
- virtual float font_get_oversampling() const override;
- virtual void font_set_oversampling(float p_oversampling) override;
+ virtual Dictionary font_supported_feature_list(RID p_font_rid) const override;
+ virtual Dictionary font_supported_variation_list(RID p_font_rid) const override;
- virtual Vector<String> get_system_fonts() const override;
+ virtual real_t font_get_global_oversampling() const override;
+ virtual void font_set_global_oversampling(real_t p_oversampling) override;
/* Shaped text buffer interface */
@@ -222,14 +454,14 @@ public:
virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override;
virtual RID shaped_text_get_parent(RID p_shaped) const override;
- virtual float shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
- virtual float shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) override;
+ virtual real_t shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
+ virtual real_t shaped_text_tab_align(RID p_shaped, const Vector<real_t> &p_tab_stops) override;
virtual bool shaped_text_shape(RID p_shaped) override;
virtual bool shaped_text_update_breaks(RID p_shaped) override;
virtual bool shaped_text_update_justification_ops(RID p_shaped) override;
- virtual void shaped_text_overrun_trim_to_width(RID p_shaped, float p_width, uint8_t p_trim_flags) override;
+ virtual void shaped_text_overrun_trim_to_width(RID p_shaped, real_t p_width, uint8_t p_trim_flags) override;
virtual TrimData shaped_text_get_trim_data(RID p_shaped) const override;
virtual bool shaped_text_is_ready(RID p_shaped) const override;
@@ -244,11 +476,11 @@ public:
virtual Rect2 shaped_text_get_object_rect(RID p_shaped, Variant p_key) const override;
virtual Size2 shaped_text_get_size(RID p_shaped) const override;
- virtual float shaped_text_get_ascent(RID p_shaped) const override;
- virtual float shaped_text_get_descent(RID p_shaped) const override;
- virtual float shaped_text_get_width(RID p_shaped) const override;
- virtual float shaped_text_get_underline_position(RID p_shaped) const override;
- virtual float shaped_text_get_underline_thickness(RID p_shaped) const override;
+ virtual real_t shaped_text_get_ascent(RID p_shaped) const override;
+ virtual real_t shaped_text_get_descent(RID p_shaped) const override;
+ virtual real_t shaped_text_get_width(RID p_shaped) const override;
+ virtual real_t shaped_text_get_underline_position(RID p_shaped) const override;
+ virtual real_t shaped_text_get_underline_thickness(RID p_shaped) const override;
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;
diff --git a/modules/text_server_fb/SCsub b/modules/text_server_fb/SCsub
index 03eccbe7bd..31d1db6167 100644
--- a/modules/text_server_fb/SCsub
+++ b/modules/text_server_fb/SCsub
@@ -3,11 +3,23 @@
Import("env")
Import("env_modules")
+freetype_enabled = env.module_check_dependencies("text_server_fb", ["freetype"], True)
+msdngen_enabled = env.module_check_dependencies("text_server_fb", ["msdfgen"], True)
+
env_text_server_fb = env_modules.Clone()
-env_text_server_fb.Append(
- CPPPATH=[
- "#thirdparty/freetype/include",
- ]
-)
+
+if msdngen_enabled:
+ env_text_server_fb.Append(
+ CPPPATH=[
+ "#thirdparty/msdfgen",
+ ]
+ )
+
+if freetype_enabled:
+ env_text_server_fb.Append(
+ CPPPATH=[
+ "#thirdparty/freetype/include",
+ ]
+ )
env_text_server_fb.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/text_server_fb/bitmap_font_fb.cpp b/modules/text_server_fb/bitmap_font_fb.cpp
deleted file mode 100644
index 313f170f04..0000000000
--- a/modules/text_server_fb/bitmap_font_fb.cpp
+++ /dev/null
@@ -1,352 +0,0 @@
-/*************************************************************************/
-/* bitmap_font_fb.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 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. */
-/*************************************************************************/
-
-#include "bitmap_font_fb.h"
-
-Error BitmapFontDataFallback::load_from_file(const String &p_filename, int p_base_size) {
- _THREAD_SAFE_METHOD_
- //fnt format used by angelcode bmfont
- //http://www.angelcode.com/products/bmfont/
-
- FileAccess *f = FileAccess::open(p_filename, FileAccess::READ);
- ERR_FAIL_COND_V_MSG(!f, ERR_FILE_NOT_FOUND, "Can't open font: " + p_filename + ".");
-
- while (true) {
- String line = f->get_line();
-
- int delimiter = line.find(" ");
- String type = line.substr(0, delimiter);
- int pos = delimiter + 1;
- Map<String, String> keys;
-
- while (pos < line.size() && line[pos] == ' ') {
- pos++;
- }
-
- while (pos < line.size()) {
- int eq = line.find("=", pos);
- if (eq == -1) {
- break;
- }
- String key = line.substr(pos, eq - pos);
- int end = -1;
- String value;
- if (line[eq + 1] == '"') {
- end = line.find("\"", eq + 2);
- if (end == -1) {
- break;
- }
- value = line.substr(eq + 2, end - 1 - eq - 1);
- pos = end + 1;
- } else {
- end = line.find(" ", eq + 1);
- if (end == -1) {
- end = line.size();
- }
- value = line.substr(eq + 1, end - eq);
- pos = end;
- }
-
- while (pos < line.size() && line[pos] == ' ') {
- pos++;
- }
-
- keys[key] = value;
- }
-
- if (type == "info") {
- if (keys.has("size")) {
- base_size = keys["size"].to_int();
- }
- } else if (type == "common") {
- if (keys.has("lineHeight")) {
- height = keys["lineHeight"].to_int();
- }
- if (keys.has("base")) {
- ascent = keys["base"].to_int();
- }
- } else if (type == "page") {
- if (keys.has("file")) {
- String base_dir = p_filename.get_base_dir();
- String file = base_dir.plus_file(keys["file"]);
- if (RenderingServer::get_singleton() != nullptr) {
- Ref<Texture2D> tex = ResourceLoader::load(file);
- if (tex.is_null()) {
- ERR_PRINT("Can't load font texture!");
- } else {
- ERR_FAIL_COND_V_MSG(tex.is_null(), ERR_FILE_CANT_READ, "It's not a reference to a valid Texture object.");
- textures.push_back(tex);
- }
- }
- }
- } else if (type == "char") {
- Character c;
- char32_t idx = 0;
- if (keys.has("id")) {
- idx = keys["id"].to_int();
- }
- if (keys.has("x")) {
- c.rect.position.x = keys["x"].to_int();
- }
- if (keys.has("y")) {
- c.rect.position.y = keys["y"].to_int();
- }
- if (keys.has("width")) {
- c.rect.size.width = keys["width"].to_int();
- }
- if (keys.has("height")) {
- c.rect.size.height = keys["height"].to_int();
- }
- if (keys.has("xoffset")) {
- c.align.x = keys["xoffset"].to_int();
- }
- if (keys.has("yoffset")) {
- c.align.y = keys["yoffset"].to_int();
- }
- if (keys.has("page")) {
- c.texture_idx = keys["page"].to_int();
- }
- if (keys.has("xadvance")) {
- c.advance.x = keys["xadvance"].to_int();
- }
- if (keys.has("yadvance")) {
- c.advance.y = keys["yadvance"].to_int();
- }
- if (c.advance.x < 0) {
- c.advance.x = c.rect.size.width + 1;
- }
- if (c.advance.y < 0) {
- c.advance.y = c.rect.size.height + 1;
- }
- char_map[idx] = c;
- } else if (type == "kerning") {
- KerningPairKey kpk;
- float k = 0.0;
- if (keys.has("first")) {
- kpk.A = keys["first"].to_int();
- }
- if (keys.has("second")) {
- kpk.B = keys["second"].to_int();
- }
- if (keys.has("amount")) {
- k = keys["amount"].to_int();
- }
- kerning_map[kpk] = k;
- }
-
- if (f->eof_reached()) {
- break;
- }
- }
- if (base_size == 0) {
- base_size = height;
- }
-
- valid = true;
-
- memdelete(f);
- return OK;
-}
-
-Error BitmapFontDataFallback::bitmap_new(float p_height, float p_ascent, int p_base_size) {
- height = p_height;
- ascent = p_ascent;
-
- base_size = p_base_size;
- if (base_size == 0) {
- base_size = height;
- }
-
- char_map.clear();
- textures.clear();
- kerning_map.clear();
-
- valid = true;
-
- return OK;
-}
-
-void BitmapFontDataFallback::bitmap_add_texture(const Ref<Texture> &p_texture) {
- ERR_FAIL_COND(!valid);
- ERR_FAIL_COND_MSG(p_texture.is_null(), "It's not a reference to a valid Texture object.");
-
- textures.push_back(p_texture);
-}
-
-void BitmapFontDataFallback::bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) {
- ERR_FAIL_COND(!valid);
-
- Character chr;
- chr.rect = p_rect;
- chr.texture_idx = p_texture_idx;
- if (p_advance < 0) {
- chr.advance.x = chr.rect.size.x;
- } else {
- chr.advance.x = p_advance;
- }
- chr.align = p_align;
- char_map[p_char] = chr;
-}
-
-void BitmapFontDataFallback::bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) {
- ERR_FAIL_COND(!valid);
-
- KerningPairKey kpk;
- kpk.A = p_A;
- kpk.B = p_B;
-
- if (p_kerning == 0 && kerning_map.has(kpk)) {
- kerning_map.erase(kpk);
- } else {
- kerning_map[kpk] = p_kerning;
- }
-}
-
-float BitmapFontDataFallback::get_height(int p_size) const {
- ERR_FAIL_COND_V(!valid, 0.f);
- return height * (float(p_size) / float(base_size));
-}
-
-float BitmapFontDataFallback::get_ascent(int p_size) const {
- ERR_FAIL_COND_V(!valid, 0.f);
- return ascent * (float(p_size) / float(base_size));
-}
-
-float BitmapFontDataFallback::get_descent(int p_size) const {
- ERR_FAIL_COND_V(!valid, 0.f);
- return (height - ascent) * (float(p_size) / float(base_size));
-}
-
-float BitmapFontDataFallback::get_underline_position(int p_size) const {
- ERR_FAIL_COND_V(!valid, 0.f);
- return 2 * (float(p_size) / float(base_size));
-}
-
-float BitmapFontDataFallback::get_underline_thickness(int p_size) const {
- ERR_FAIL_COND_V(!valid, 0.f);
- return 1 * (float(p_size) / float(base_size));
-}
-
-void BitmapFontDataFallback::set_distance_field_hint(bool p_distance_field) {
- distance_field_hint = p_distance_field;
-}
-
-bool BitmapFontDataFallback::get_distance_field_hint() const {
- return distance_field_hint;
-}
-
-float BitmapFontDataFallback::get_base_size() const {
- return base_size;
-}
-
-bool BitmapFontDataFallback::has_char(char32_t p_char) const {
- _THREAD_SAFE_METHOD_
- ERR_FAIL_COND_V(!valid, false);
- return char_map.has(p_char);
-}
-
-String BitmapFontDataFallback::get_supported_chars() const {
- _THREAD_SAFE_METHOD_
- ERR_FAIL_COND_V(!valid, String());
- String chars;
- const char32_t *k = nullptr;
- while ((k = char_map.next(k))) {
- chars += char32_t(*k);
- }
- return chars;
-}
-
-Vector2 BitmapFontDataFallback::get_advance(char32_t p_char, int p_size) const {
- _THREAD_SAFE_METHOD_
- ERR_FAIL_COND_V(!valid, Vector2());
- const Character *c = char_map.getptr(p_char);
- ERR_FAIL_COND_V(c == nullptr, Vector2());
-
- return c->advance * (float(p_size) / float(base_size));
-}
-
-Vector2 BitmapFontDataFallback::get_kerning(char32_t p_char, char32_t p_next, int p_size) const {
- _THREAD_SAFE_METHOD_
- ERR_FAIL_COND_V(!valid, Vector2());
- KerningPairKey kpk;
- kpk.A = p_char;
- kpk.B = p_next;
-
- const Map<KerningPairKey, int>::Element *E = kerning_map.find(kpk);
- if (E) {
- return Vector2(-E->get() * (float(p_size) / float(base_size)), 0);
- } else {
- return Vector2();
- }
-}
-
-Vector2 BitmapFontDataFallback::draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
- _THREAD_SAFE_METHOD_
- if (p_index == 0) {
- return Vector2();
- }
- ERR_FAIL_COND_V(!valid, Vector2());
- const Character *c = char_map.getptr(p_index);
-
- ERR_FAIL_COND_V(c == nullptr, Vector2());
- ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), Vector2());
- if (c->texture_idx != -1) {
- Point2i cpos = p_pos;
- cpos += (c->align + Vector2(0, -ascent)) * (float(p_size) / float(base_size));
- Size2i csize = c->rect.size * (float(p_size) / float(base_size));
- if (RenderingServer::get_singleton() != nullptr) {
- //if (distance_field_hint) { // Not implemented.
- // RenderingServer::get_singleton()->canvas_item_set_distance_field_mode(p_canvas, true);
- //}
- RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), textures[c->texture_idx]->get_rid(), c->rect, p_color, false, false);
- //if (distance_field_hint) {
- // RenderingServer::get_singleton()->canvas_item_set_distance_field_mode(p_canvas, false);
- //}
- }
- }
-
- return c->advance * (float(p_size) / float(base_size));
-}
-
-Vector2 BitmapFontDataFallback::draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
- _THREAD_SAFE_METHOD_
- if (p_index == 0) {
- return Vector2();
- }
- ERR_FAIL_COND_V(!valid, Vector2());
- const Character *c = char_map.getptr(p_index);
-
- ERR_FAIL_COND_V(c == nullptr, Vector2());
- ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), Vector2());
-
- // Not supported, return advance for compatibility.
-
- return c->advance * (float(p_size) / float(base_size));
-}
diff --git a/modules/text_server_fb/bitmap_font_fb.h b/modules/text_server_fb/bitmap_font_fb.h
deleted file mode 100644
index 7cd7507ebc..0000000000
--- a/modules/text_server_fb/bitmap_font_fb.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/*************************************************************************/
-/* bitmap_font_fb.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 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. */
-/*************************************************************************/
-
-#ifndef BITMAP_FONT_FALLBACK_H
-#define BITMAP_FONT_FALLBACK_H
-
-#include "font_fb.h"
-
-struct BitmapFontDataFallback : public FontDataFallback {
- _THREAD_SAFE_CLASS_
-
-private:
- Vector<Ref<Texture2D>> textures;
-
- struct Character {
- int texture_idx = 0;
- Rect2 rect;
- Vector2 align;
- Vector2 advance = Vector2(-1, -1);
- };
-
- struct KerningPairKey {
- union {
- struct {
- uint32_t A, B;
- };
-
- uint64_t pair = 0;
- };
-
- _FORCE_INLINE_ bool operator<(const KerningPairKey &p_r) const { return pair < p_r.pair; }
- };
-
- HashMap<char32_t, Character> char_map;
- Map<KerningPairKey, int> kerning_map;
-
- float height = 0.f;
- float ascent = 0.f;
- int base_size = 0;
- bool distance_field_hint = false;
-
-public:
- virtual void clear_cache() override{};
-
- virtual Error load_from_file(const String &p_filename, int p_base_size) override;
- virtual Error bitmap_new(float p_height, float p_ascent, int p_base_size) override;
-
- virtual void bitmap_add_texture(const Ref<Texture> &p_texture) override;
- virtual void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override;
- virtual void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) override;
-
- virtual float get_height(int p_size) const override;
- virtual float get_ascent(int p_size) const override;
- virtual float get_descent(int p_size) const override;
-
- virtual float get_underline_position(int p_size) const override;
- virtual float get_underline_thickness(int p_size) const override;
-
- virtual void set_antialiased(bool p_antialiased) override{};
- virtual bool get_antialiased() const override { return false; };
-
- virtual void set_hinting(TextServer::Hinting p_hinting) override{};
- virtual TextServer::Hinting get_hinting() const override { return TextServer::HINTING_NONE; };
-
- virtual void set_distance_field_hint(bool p_distance_field) override;
- virtual bool get_distance_field_hint() const override;
-
- virtual void set_force_autohinter(bool p_enabeld) override{};
- virtual bool get_force_autohinter() const override { return false; };
-
- virtual bool has_outline() const override { return false; };
- virtual float get_base_size() const override;
-
- virtual bool has_char(char32_t p_char) const override;
- virtual String get_supported_chars() const override;
-
- virtual Vector2 get_advance(char32_t p_char, int p_size) const override;
- virtual Vector2 get_kerning(char32_t p_char, char32_t p_next, int p_size) const override;
-
- virtual Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override;
- virtual Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override;
-};
-
-#endif // BITMAP_FONT_FALLBACK_H
diff --git a/modules/text_server_fb/dynamic_font_fb.cpp b/modules/text_server_fb/dynamic_font_fb.cpp
deleted file mode 100644
index 7e77987074..0000000000
--- a/modules/text_server_fb/dynamic_font_fb.cpp
+++ /dev/null
@@ -1,713 +0,0 @@
-/*************************************************************************/
-/* dynamic_font_fb.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 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. */
-/*************************************************************************/
-
-#include "dynamic_font_fb.h"
-
-#ifdef MODULE_FREETYPE_ENABLED
-
-#include FT_STROKER_H
-#include FT_ADVANCES_H
-
-DynamicFontDataFallback::DataAtSize *DynamicFontDataFallback::get_data_for_size(int p_size, int p_outline_size) {
- ERR_FAIL_COND_V(!valid, nullptr);
- ERR_FAIL_COND_V(p_size < 0 || p_size > UINT16_MAX, nullptr);
- ERR_FAIL_COND_V(p_outline_size < 0 || p_outline_size > UINT16_MAX, nullptr);
-
- CacheID id;
- id.size = p_size;
- id.outline_size = p_outline_size;
-
- DataAtSize *fds = nullptr;
- Map<CacheID, DataAtSize *>::Element *E = nullptr;
- if (p_outline_size != 0) {
- E = size_cache_outline.find(id);
- } else {
- E = size_cache.find(id);
- }
-
- if (E != nullptr) {
- fds = E->get();
- } else {
- if (font_mem == nullptr && font_path != String()) {
- if (!font_mem_cache.is_empty()) {
- font_mem = font_mem_cache.ptr();
- font_mem_size = font_mem_cache.size();
- } else {
- FileAccess *f = FileAccess::open(font_path, FileAccess::READ);
- if (!f) {
- ERR_FAIL_V_MSG(nullptr, "Cannot open font file '" + font_path + "'.");
- }
-
- uint64_t len = f->get_length();
- font_mem_cache.resize(len);
- f->get_buffer(font_mem_cache.ptrw(), len);
- font_mem = font_mem_cache.ptr();
- font_mem_size = len;
- f->close();
- }
- }
-
- int error = 0;
- fds = memnew(DataAtSize);
- if (font_mem) {
- memset(&fds->stream, 0, sizeof(FT_StreamRec));
- fds->stream.base = (unsigned char *)font_mem;
- fds->stream.size = font_mem_size;
- fds->stream.pos = 0;
-
- FT_Open_Args fargs;
- memset(&fargs, 0, sizeof(FT_Open_Args));
- fargs.memory_base = (unsigned char *)font_mem;
- fargs.memory_size = font_mem_size;
- fargs.flags = FT_OPEN_MEMORY;
- fargs.stream = &fds->stream;
- error = FT_Open_Face(library, &fargs, 0, &fds->face);
-
- } else {
- memdelete(fds);
- ERR_FAIL_V_MSG(nullptr, "DynamicFont uninitialized.");
- }
-
- if (error == FT_Err_Unknown_File_Format) {
- memdelete(fds);
- ERR_FAIL_V_MSG(nullptr, "Unknown font format.");
-
- } else if (error) {
- memdelete(fds);
- ERR_FAIL_V_MSG(nullptr, "Error loading font.");
- }
-
- oversampling = TS->font_get_oversampling();
-
- if (FT_HAS_COLOR(fds->face) && fds->face->num_fixed_sizes > 0) {
- int best_match = 0;
- int diff = ABS(p_size - ((int64_t)fds->face->available_sizes[0].width));
- fds->scale_color_font = float(p_size * oversampling) / fds->face->available_sizes[0].width;
- for (int i = 1; i < fds->face->num_fixed_sizes; i++) {
- int ndiff = ABS(p_size - ((int64_t)fds->face->available_sizes[i].width));
- if (ndiff < diff) {
- best_match = i;
- diff = ndiff;
- fds->scale_color_font = float(p_size * oversampling) / fds->face->available_sizes[i].width;
- }
- }
- FT_Select_Size(fds->face, best_match);
- } else {
- FT_Set_Pixel_Sizes(fds->face, 0, p_size * oversampling);
- }
-
- fds->size = p_size;
- fds->ascent = (fds->face->size->metrics.ascender / 64.0) / oversampling * fds->scale_color_font;
- fds->descent = (-fds->face->size->metrics.descender / 64.0) / oversampling * fds->scale_color_font;
- fds->underline_position = (-FT_MulFix(fds->face->underline_position, fds->face->size->metrics.y_scale) / 64.0) / oversampling * fds->scale_color_font;
- fds->underline_thickness = (FT_MulFix(fds->face->underline_thickness, fds->face->size->metrics.y_scale) / 64.0) / oversampling * fds->scale_color_font;
-
- if (p_outline_size != 0) {
- size_cache_outline[id] = fds;
- } else {
- size_cache[id] = fds;
- }
- }
-
- return fds;
-}
-
-DynamicFontDataFallback::TexturePosition DynamicFontDataFallback::find_texture_pos_for_glyph(DynamicFontDataFallback::DataAtSize *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) {
- TexturePosition ret;
- ret.index = -1;
-
- int mw = p_width;
- int mh = p_height;
-
- for (int i = 0; i < p_data->textures.size(); i++) {
- const CharTexture &ct = p_data->textures[i];
-
- if (RenderingServer::get_singleton() != nullptr) {
- if (ct.texture->get_format() != p_image_format) {
- continue;
- }
- }
-
- if (mw > ct.texture_size || mh > ct.texture_size) { //too big for this texture
- continue;
- }
-
- ret.y = 0x7FFFFFFF;
- ret.x = 0;
-
- for (int j = 0; j < ct.texture_size - 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_size) {
- continue; //fail, could not fit it here
- }
-
- 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 * oversampling * 8, 256);
- if (mw > texsize) {
- texsize = mw; //special case, adapt to it?
- }
- if (mh > texsize) {
- texsize = mh; //special case, adapt to it?
- }
-
- texsize = next_power_of_2(texsize);
-
- texsize = MIN(texsize, 4096);
-
- CharTexture tex;
- tex.texture_size = texsize;
- tex.imgdata.resize(texsize * texsize * p_color_size); //grayscale alpha
-
- {
- //zero texture
- uint8_t *w = tex.imgdata.ptrw();
- ERR_FAIL_COND_V(texsize * texsize * p_color_size > tex.imgdata.size(), ret);
- // Initialize the texture to all-white pixels to prevent artifacts when the
- // font is displayed at a non-default scale with filtering enabled.
- if (p_color_size == 2) {
- for (int i = 0; i < texsize * texsize * p_color_size; i += 2) { // FORMAT_LA8
- w[i + 0] = 255;
- w[i + 1] = 0;
- }
- } else if (p_color_size == 4) {
- for (int i = 0; i < texsize * texsize * p_color_size; i += 4) { // FORMAT_RGBA8
- w[i + 0] = 255;
- w[i + 1] = 255;
- w[i + 2] = 255;
- w[i + 3] = 0;
- }
- } else {
- ERR_FAIL_V(ret);
- }
- }
- tex.offsets.resize(texsize);
- for (int i = 0; i < texsize; i++) { //zero offsets
- tex.offsets.write[i] = 0;
- }
-
- p_data->textures.push_back(tex);
- ret.index = p_data->textures.size() - 1;
- }
-
- return ret;
-}
-
-DynamicFontDataFallback::Character DynamicFontDataFallback::Character::not_found() {
- Character ch;
- return ch;
-}
-
-DynamicFontDataFallback::Character DynamicFontDataFallback::bitmap_to_character(DynamicFontDataFallback::DataAtSize *p_data, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) {
- int w = bitmap.width;
- int h = bitmap.rows;
-
- int mw = w + rect_margin * 2;
- int mh = h + rect_margin * 2;
-
- ERR_FAIL_COND_V(mw > 4096, Character::not_found());
- ERR_FAIL_COND_V(mh > 4096, Character::not_found());
-
- 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;
-
- TexturePosition tex_pos = find_texture_pos_for_glyph(p_data, color_size, require_format, mw, mh);
- ERR_FAIL_COND_V(tex_pos.index < 0, Character::not_found());
-
- //fit character in char texture
-
- CharTexture &tex = p_data->textures.write[tex_pos.index];
-
- {
- uint8_t *wr = tex.imgdata.ptrw();
-
- for (int i = 0; i < h; i++) {
- for (int j = 0; j < w; j++) {
- int ofs = ((i + tex_pos.y + rect_margin) * tex.texture_size + j + tex_pos.x + rect_margin) * color_size;
- ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), Character::not_found());
- switch (bitmap.pixel_mode) {
- case FT_PIXEL_MODE_MONO: {
- int byte = i * bitmap.pitch + (j >> 3);
- int bit = 1 << (7 - (j % 8));
- wr[ofs + 0] = 255; //grayscale as 1
- wr[ofs + 1] = (bitmap.buffer[byte] & bit) ? 255 : 0;
- } break;
- case FT_PIXEL_MODE_GRAY:
- wr[ofs + 0] = 255; //grayscale as 1
- wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j];
- break;
- case FT_PIXEL_MODE_BGRA: {
- int ofs_color = i * bitmap.pitch + (j << 2);
- wr[ofs + 2] = bitmap.buffer[ofs_color + 0];
- wr[ofs + 1] = bitmap.buffer[ofs_color + 1];
- wr[ofs + 0] = bitmap.buffer[ofs_color + 2];
- wr[ofs + 3] = bitmap.buffer[ofs_color + 3];
- } break;
- // TODO: FT_PIXEL_MODE_LCD
- default:
- ERR_FAIL_V_MSG(Character::not_found(), "Font uses unsupported pixel format: " + itos(bitmap.pixel_mode) + ".");
- break;
- }
- }
- }
- }
-
- //blit to image and texture
- {
- if (RenderingServer::get_singleton() != nullptr) {
- Ref<Image> img = memnew(Image(tex.texture_size, tex.texture_size, 0, require_format, tex.imgdata));
-
- if (tex.texture.is_null()) {
- tex.texture.instantiate();
- tex.texture->create_from_image(img);
- } else {
- tex.texture->update(img); //update
- }
- }
- }
-
- // update height array
- for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
- tex.offsets.write[k] = tex_pos.y + mh;
- }
-
- Character chr;
- chr.align = (Vector2(xofs, -yofs) * p_data->scale_color_font / oversampling).round();
- chr.advance = (advance * p_data->scale_color_font / oversampling).round();
- chr.texture_idx = tex_pos.index;
- chr.found = true;
-
- chr.rect_uv = Rect2(tex_pos.x + rect_margin, tex_pos.y + rect_margin, w, h);
- chr.rect = chr.rect_uv;
- chr.rect.position /= oversampling;
- chr.rect.size *= (p_data->scale_color_font / oversampling);
- return chr;
-}
-
-void DynamicFontDataFallback::update_char(int p_size, char32_t p_char) {
- DataAtSize *fds = get_data_for_size(p_size, false);
- ERR_FAIL_COND(fds == nullptr);
-
- if (fds->char_map.has(p_char)) {
- return;
- }
-
- Character character = Character::not_found();
-
- FT_GlyphSlot slot = fds->face->glyph;
- FT_UInt gl_index = FT_Get_Char_Index(fds->face, p_char);
-
- if (gl_index == 0) {
- fds->char_map[p_char] = character;
- return;
- }
-
- int ft_hinting;
- switch (hinting) {
- case TextServer::HINTING_NONE:
- ft_hinting = FT_LOAD_NO_HINTING;
- break;
- case TextServer::HINTING_LIGHT:
- ft_hinting = FT_LOAD_TARGET_LIGHT;
- break;
- default:
- ft_hinting = FT_LOAD_TARGET_NORMAL;
- break;
- }
-
- FT_Fixed v, h;
- FT_Get_Advance(fds->face, gl_index, FT_HAS_COLOR(fds->face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting, &h);
- FT_Get_Advance(fds->face, gl_index, FT_HAS_COLOR(fds->face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting | FT_LOAD_VERTICAL_LAYOUT, &v);
-
- int error = FT_Load_Glyph(fds->face, gl_index, FT_HAS_COLOR(fds->face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting);
- if (error) {
- fds->char_map[p_char] = character;
- return;
- }
-
- error = FT_Render_Glyph(fds->face->glyph, antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
- if (!error) {
- character = bitmap_to_character(fds, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0);
- }
-
- fds->char_map[p_char] = character;
-}
-
-void DynamicFontDataFallback::update_char_outline(int p_size, int p_outline_size, char32_t p_char) {
- DataAtSize *fds = get_data_for_size(p_size, p_outline_size);
- ERR_FAIL_COND(fds == nullptr);
-
- if (fds->char_map.has(p_char)) {
- return;
- }
-
- Character character = Character::not_found();
- FT_UInt gl_index = FT_Get_Char_Index(fds->face, p_char);
-
- if (gl_index == 0) {
- fds->char_map[p_char] = character;
- return;
- }
-
- int error = FT_Load_Glyph(fds->face, gl_index, FT_LOAD_NO_BITMAP | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0));
- if (error) {
- fds->char_map[p_char] = character;
- return;
- }
-
- FT_Stroker stroker;
- if (FT_Stroker_New(library, &stroker) != 0) {
- fds->char_map[p_char] = character;
- return;
- }
-
- FT_Stroker_Set(stroker, (int)(p_outline_size * oversampling * 64.0), FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0);
- FT_Glyph glyph;
- FT_BitmapGlyph glyph_bitmap;
-
- if (FT_Get_Glyph(fds->face->glyph, &glyph) != 0) {
- goto cleanup_stroker;
- }
- if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) {
- goto cleanup_glyph;
- }
- if (FT_Glyph_To_Bitmap(&glyph, antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1) != 0) {
- goto cleanup_glyph;
- }
-
- glyph_bitmap = (FT_BitmapGlyph)glyph;
- character = bitmap_to_character(fds, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2());
-
-cleanup_glyph:
- FT_Done_Glyph(glyph);
-cleanup_stroker:
- FT_Stroker_Done(stroker);
-
- fds->char_map[p_char] = character;
-}
-
-void DynamicFontDataFallback::clear_cache() {
- _THREAD_SAFE_METHOD_
- for (Map<CacheID, DataAtSize *>::Element *E = size_cache.front(); E; E = E->next()) {
- memdelete(E->get());
- }
- size_cache.clear();
- for (Map<CacheID, DataAtSize *>::Element *E = size_cache_outline.front(); E; E = E->next()) {
- memdelete(E->get());
- }
- size_cache_outline.clear();
-}
-
-Error DynamicFontDataFallback::load_from_file(const String &p_filename, int p_base_size) {
- _THREAD_SAFE_METHOD_
- if (library == nullptr) {
- int error = FT_Init_FreeType(&library);
- ERR_FAIL_COND_V_MSG(error != 0, ERR_CANT_CREATE, "Error initializing FreeType.");
- }
- clear_cache();
-
- font_path = p_filename;
- base_size = p_base_size;
-
- valid = true;
- DataAtSize *fds = get_data_for_size(base_size); // load base size.
- if (fds == nullptr) {
- valid = false;
- ERR_FAIL_V(ERR_CANT_CREATE);
- }
-
- return OK;
-}
-
-Error DynamicFontDataFallback::load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) {
- _THREAD_SAFE_METHOD_
- if (library == nullptr) {
- int error = FT_Init_FreeType(&library);
- ERR_FAIL_COND_V_MSG(error != 0, ERR_CANT_CREATE, "Error initializing FreeType.");
- }
- clear_cache();
-
- font_mem = p_data;
- font_mem_size = p_size;
- base_size = p_base_size;
-
- valid = true;
- DataAtSize *fds = get_data_for_size(base_size); // load base size.
- if (fds == nullptr) {
- valid = false;
- ERR_FAIL_V(ERR_CANT_CREATE);
- }
-
- return OK;
-}
-
-float DynamicFontDataFallback::get_height(int p_size) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
- ERR_FAIL_COND_V(fds == nullptr, 0.f);
- return fds->ascent + fds->descent;
-}
-
-float DynamicFontDataFallback::get_ascent(int p_size) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
- ERR_FAIL_COND_V(fds == nullptr, 0.f);
- return fds->ascent;
-}
-
-float DynamicFontDataFallback::get_descent(int p_size) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
- ERR_FAIL_COND_V(fds == nullptr, 0.f);
- return fds->descent;
-}
-
-float DynamicFontDataFallback::get_underline_position(int p_size) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
- ERR_FAIL_COND_V(fds == nullptr, 0.f);
- return fds->underline_position;
-}
-
-float DynamicFontDataFallback::get_underline_thickness(int p_size) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
- ERR_FAIL_COND_V(fds == nullptr, 0.f);
- return fds->underline_thickness;
-}
-
-void DynamicFontDataFallback::set_antialiased(bool p_antialiased) {
- if (antialiased != p_antialiased) {
- clear_cache();
- antialiased = p_antialiased;
- }
-}
-
-bool DynamicFontDataFallback::get_antialiased() const {
- return antialiased;
-}
-
-void DynamicFontDataFallback::set_force_autohinter(bool p_enabled) {
- if (force_autohinter != p_enabled) {
- clear_cache();
- force_autohinter = p_enabled;
- }
-}
-
-bool DynamicFontDataFallback::get_force_autohinter() const {
- return force_autohinter;
-}
-
-void DynamicFontDataFallback::set_hinting(TextServer::Hinting p_hinting) {
- if (hinting != p_hinting) {
- clear_cache();
- hinting = p_hinting;
- }
-}
-
-TextServer::Hinting DynamicFontDataFallback::get_hinting() const {
- return hinting;
-}
-
-bool DynamicFontDataFallback::has_outline() const {
- return true;
-}
-
-float DynamicFontDataFallback::get_base_size() const {
- return base_size;
-}
-
-bool DynamicFontDataFallback::has_char(char32_t p_char) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(base_size);
- ERR_FAIL_COND_V(fds == nullptr, false);
-
- const_cast<DynamicFontDataFallback *>(this)->update_char(base_size, p_char);
- Character ch = fds->char_map[p_char];
-
- return (ch.found);
-}
-
-String DynamicFontDataFallback::get_supported_chars() const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(base_size);
- ERR_FAIL_COND_V(fds == nullptr, String());
-
- String chars;
-
- FT_UInt gindex;
- FT_ULong charcode = FT_Get_First_Char(fds->face, &gindex);
- while (gindex != 0) {
- if (charcode != 0) {
- chars += char32_t(charcode);
- }
- charcode = FT_Get_Next_Char(fds->face, charcode, &gindex);
- }
-
- return chars;
-}
-
-Vector2 DynamicFontDataFallback::get_advance(char32_t p_char, int p_size) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
- ERR_FAIL_COND_V(fds == nullptr, Vector2());
-
- const_cast<DynamicFontDataFallback *>(this)->update_char(p_size, p_char);
- Character ch = fds->char_map[p_char];
-
- return ch.advance;
-}
-
-Vector2 DynamicFontDataFallback::get_kerning(char32_t p_char, char32_t p_next, int p_size) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
- ERR_FAIL_COND_V(fds == nullptr, Vector2());
-
- FT_Vector delta;
- FT_Get_Kerning(fds->face, FT_Get_Char_Index(fds->face, p_char), FT_Get_Char_Index(fds->face, p_next), FT_KERNING_DEFAULT, &delta);
- return Vector2(delta.x, delta.y);
-}
-
-Vector2 DynamicFontDataFallback::draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
- ERR_FAIL_COND_V(fds == nullptr, Vector2());
-
- const_cast<DynamicFontDataFallback *>(this)->update_char(p_size, p_index);
- Character ch = fds->char_map[p_index];
-
- Vector2 advance;
- if (ch.found) {
- ERR_FAIL_COND_V(ch.texture_idx < -1 || ch.texture_idx >= fds->textures.size(), Vector2());
-
- if (ch.texture_idx != -1) {
- Point2i cpos = p_pos;
- cpos += ch.align;
-
- Color modulate = p_color;
- if (FT_HAS_COLOR(fds->face)) {
- modulate.r = modulate.g = modulate.b = 1.0;
- }
- if (RenderingServer::get_singleton() != nullptr) {
- RID texture = fds->textures[ch.texture_idx].texture->get_rid();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, ch.rect.size), texture, ch.rect_uv, modulate, false, false);
- }
- }
-
- advance = ch.advance;
- }
-
- return advance;
-}
-
-Vector2 DynamicFontDataFallback::draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size, p_outline_size);
- ERR_FAIL_COND_V(fds == nullptr, Vector2());
-
- const_cast<DynamicFontDataFallback *>(this)->update_char_outline(p_size, p_outline_size, p_index);
- Character ch = fds->char_map[p_index];
-
- Vector2 advance;
- if (ch.found) {
- ERR_FAIL_COND_V(ch.texture_idx < -1 || ch.texture_idx >= fds->textures.size(), Vector2());
-
- if (ch.texture_idx != -1) {
- Point2i cpos = p_pos;
- cpos += ch.align;
-
- Color modulate = p_color;
- if (FT_HAS_COLOR(fds->face)) {
- modulate.r = modulate.g = modulate.b = 1.0;
- }
- if (RenderingServer::get_singleton() != nullptr) {
- RID texture = fds->textures[ch.texture_idx].texture->get_rid();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, ch.rect.size), texture, ch.rect_uv, modulate, false, false);
- }
- }
-
- advance = ch.advance;
- }
-
- return advance;
-}
-
-bool DynamicFontDataFallback::get_glyph_contours(int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const {
- _THREAD_SAFE_METHOD_
- DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
- ERR_FAIL_COND_V(fds == nullptr, false);
-
- int error = FT_Load_Glyph(fds->face, p_index, FT_LOAD_NO_BITMAP | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0));
- ERR_FAIL_COND_V(error, false);
-
- r_points.clear();
- r_contours.clear();
-
- float h = fds->ascent;
- float scale = (1.0 / 64.0) / oversampling * fds->scale_color_font;
- for (short i = 0; i < fds->face->glyph->outline.n_points; i++) {
- r_points.push_back(Vector3(fds->face->glyph->outline.points[i].x * scale, h - fds->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fds->face->glyph->outline.tags[i])));
- }
- for (short i = 0; i < fds->face->glyph->outline.n_contours; i++) {
- r_contours.push_back(fds->face->glyph->outline.contours[i]);
- }
- r_orientation = (FT_Outline_Get_Orientation(&fds->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT);
- return true;
-}
-
-DynamicFontDataFallback::~DynamicFontDataFallback() {
- clear_cache();
- if (library != nullptr) {
- FT_Done_FreeType(library);
- }
-}
-
-#endif // MODULE_FREETYPE_ENABLED
diff --git a/modules/text_server_fb/dynamic_font_fb.h b/modules/text_server_fb/dynamic_font_fb.h
deleted file mode 100644
index 82e59fa607..0000000000
--- a/modules/text_server_fb/dynamic_font_fb.h
+++ /dev/null
@@ -1,173 +0,0 @@
-/*************************************************************************/
-/* dynamic_font_fb.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 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. */
-/*************************************************************************/
-
-#ifndef DYNAMIC_FONT_FALLBACK_H
-#define DYNAMIC_FONT_FALLBACK_H
-
-#include "font_fb.h"
-
-#include "modules/modules_enabled.gen.h"
-#ifdef MODULE_FREETYPE_ENABLED
-
-#include <ft2build.h>
-#include FT_FREETYPE_H
-
-struct DynamicFontDataFallback : public FontDataFallback {
- _THREAD_SAFE_CLASS_
-
-private:
- struct CharTexture {
- Vector<uint8_t> imgdata;
- int texture_size = 0;
- Vector<int> offsets;
- Ref<ImageTexture> texture;
- };
-
- struct Character {
- bool found = false;
- int texture_idx = 0;
- Rect2 rect;
- Rect2 rect_uv;
- Vector2 align;
- Vector2 advance = Vector2(-1, -1);
-
- static Character not_found();
- };
-
- struct TexturePosition {
- int index = 0;
- int x = 0;
- int y = 0;
- };
-
- struct CacheID {
- union {
- struct {
- uint32_t size : 16;
- uint32_t outline_size : 16;
- };
- uint32_t key = 0;
- };
- bool operator<(CacheID right) const {
- return key < right.key;
- }
- };
-
- struct DataAtSize {
- FT_Face face = nullptr;
- FT_StreamRec stream;
-
- int size = 0;
- float scale_color_font = 1.f;
- float ascent = 0.0;
- float descent = 0.0;
- float underline_position = 0.0;
- float underline_thickness = 0.0;
-
- Vector<CharTexture> textures;
- HashMap<char32_t, Character> char_map;
-
- ~DataAtSize() {
- if (face != nullptr) {
- FT_Done_Face(face);
- }
- }
- };
-
- FT_Library library = nullptr;
-
- // Source data.
- const uint8_t *font_mem = nullptr;
- int font_mem_size = 0;
- String font_path;
- Vector<uint8_t> font_mem_cache;
-
- float rect_margin = 1.f;
- int base_size = 16;
- float oversampling = 1.f;
- bool antialiased = true;
- bool force_autohinter = false;
- TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
-
- Map<CacheID, DataAtSize *> size_cache;
- Map<CacheID, DataAtSize *> size_cache_outline;
-
- DataAtSize *get_data_for_size(int p_size, int p_outline_size = 0);
-
- TexturePosition find_texture_pos_for_glyph(DataAtSize *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height);
- Character bitmap_to_character(DataAtSize *p_data, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance);
- _FORCE_INLINE_ void update_char(int p_size, char32_t p_char);
- _FORCE_INLINE_ void update_char_outline(int p_size, int p_outline_size, char32_t p_char);
-
-public:
- virtual void clear_cache() override;
-
- virtual Error load_from_file(const String &p_filename, int p_base_size) override;
- virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) override;
-
- virtual float get_height(int p_size) const override;
- virtual float get_ascent(int p_size) const override;
- virtual float get_descent(int p_size) const override;
-
- virtual float get_underline_position(int p_size) const override;
- virtual float get_underline_thickness(int p_size) const override;
-
- virtual void set_antialiased(bool p_antialiased) override;
- virtual bool get_antialiased() const override;
-
- virtual void set_hinting(TextServer::Hinting p_hinting) override;
- virtual TextServer::Hinting get_hinting() const override;
-
- virtual void set_force_autohinter(bool p_enabeld) override;
- virtual bool get_force_autohinter() const override;
-
- virtual void set_distance_field_hint(bool p_distance_field) override{};
- virtual bool get_distance_field_hint() const override { return false; };
-
- virtual bool has_outline() const override;
- virtual float get_base_size() const override;
-
- virtual bool has_char(char32_t p_char) const override;
- virtual String get_supported_chars() const override;
-
- virtual Vector2 get_advance(char32_t p_char, int p_size) const override;
- virtual Vector2 get_kerning(char32_t p_char, char32_t p_next, int p_size) const override;
-
- virtual Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override;
- virtual Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override;
-
- virtual bool get_glyph_contours(int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override;
-
- virtual ~DynamicFontDataFallback() override;
-};
-
-#endif // MODULE_FREETYPE_ENABLED
-
-#endif // DYNAMIC_FONT_FALLBACK_H
diff --git a/modules/text_server_fb/font_fb.h b/modules/text_server_fb/font_fb.h
deleted file mode 100644
index fe9888b7f4..0000000000
--- a/modules/text_server_fb/font_fb.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*************************************************************************/
-/* font_fb.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 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. */
-/*************************************************************************/
-
-#ifndef FONT_FALLBACK_H
-#define FONT_FALLBACK_H
-
-#include "servers/text_server.h"
-
-struct FontDataFallback {
- Map<String, bool> lang_support_overrides;
- Map<String, bool> script_support_overrides;
- bool valid = false;
- int spacing_space = 0;
- int spacing_glyph = 0;
-
- virtual void clear_cache() = 0;
-
- virtual Error load_from_file(const String &p_filename, int p_base_size) { return ERR_CANT_CREATE; };
- virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) { return ERR_CANT_CREATE; };
- virtual Error bitmap_new(float p_height, float p_ascent, int p_base_size) { return ERR_CANT_CREATE; };
-
- virtual void bitmap_add_texture(const Ref<Texture> &p_texture) { ERR_FAIL_MSG("Not supported."); };
- virtual void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { ERR_FAIL_MSG("Not supported."); };
- virtual void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) { ERR_FAIL_MSG("Not supported."); };
-
- virtual float get_height(int p_size) const = 0;
- virtual float get_ascent(int p_size) const = 0;
- virtual float get_descent(int p_size) const = 0;
-
- virtual float get_underline_position(int p_size) const = 0;
- virtual float get_underline_thickness(int p_size) const = 0;
-
- virtual int get_spacing_space() const { return spacing_space; };
- virtual void set_spacing_space(int p_value) {
- spacing_space = p_value;
- clear_cache();
- };
-
- virtual int get_spacing_glyph() const { return spacing_glyph; };
- virtual void set_spacing_glyph(int p_value) {
- spacing_glyph = p_value;
- clear_cache();
- };
-
- virtual void set_antialiased(bool p_antialiased) = 0;
- virtual bool get_antialiased() const = 0;
-
- virtual void set_hinting(TextServer::Hinting p_hinting) = 0;
- virtual TextServer::Hinting get_hinting() const = 0;
-
- virtual void set_distance_field_hint(bool p_distance_field) = 0;
- virtual bool get_distance_field_hint() const = 0;
-
- virtual void set_force_autohinter(bool p_enabeld) = 0;
- virtual bool get_force_autohinter() const = 0;
-
- virtual bool has_outline() const = 0;
- virtual float get_base_size() const = 0;
-
- virtual bool has_char(char32_t p_char) const = 0;
- virtual String get_supported_chars() const = 0;
-
- virtual Vector2 get_advance(char32_t p_char, int p_size) const = 0;
- virtual Vector2 get_kerning(char32_t p_char, char32_t p_next, int p_size) const = 0;
-
- virtual Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const = 0;
- virtual Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const = 0;
-
- virtual bool get_glyph_contours(int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const { return false; };
-
- virtual ~FontDataFallback(){};
-};
-
-#endif // FONT_FALLBACK_H
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index 110194c373..e4e6797f92 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -30,8 +30,18 @@
#include "text_server_fb.h"
-#include "bitmap_font_fb.h"
-#include "dynamic_font_fb.h"
+#include "core/string/print_string.h"
+
+#ifdef MODULE_MSDFGEN_ENABLED
+#include "core/ShapeDistanceFinder.h"
+#include "core/contour-combiners.h"
+#include "core/edge-selectors.h"
+#include "msdfgen.h"
+#endif
+
+/*************************************************************************/
+/* Character properties. */
+/*************************************************************************/
_FORCE_INLINE_ bool is_control(char32_t p_char) {
return (p_char <= 0x001f) || (p_char >= 0x007f && p_char <= 0x009F);
@@ -100,291 +110,1779 @@ bool TextServerFallback::is_locale_right_to_left(const String &p_locale) {
return false; // No RTL support.
}
+#define OT_TAG(c1, c2, c3, c4) ((int32_t)((((uint32_t)(c1)&0xFF) << 24) | (((uint32_t)(c2)&0xFF) << 16) | (((uint32_t)(c3)&0xFF) << 8) | ((uint32_t)(c4)&0xFF)))
+
+struct FeatureInfo {
+ int32_t tag;
+ String name;
+};
+
+static FeatureInfo feature_set[] = {
+ // Registered OpenType variation tags.
+ { OT_TAG('i', 't', 'a', 'l'), "italic" },
+ { OT_TAG('o', 'p', 's', 'z'), "optical_size" },
+ { OT_TAG('s', 'l', 'n', 't'), "slant" },
+ { OT_TAG('w', 'd', 't', 'h'), "width" },
+ { OT_TAG('w', 'g', 'h', 't'), "weight" },
+ { 0, String() },
+};
+
+_FORCE_INLINE_ int32_t ot_tag_from_string(const char *p_str, int p_len) {
+ char tag[4];
+ uint32_t i;
+
+ if (!p_str || !p_len || !*p_str)
+ return OT_TAG(0, 0, 0, 0);
+
+ if (p_len < 0 || p_len > 4) {
+ p_len = 4;
+ }
+ for (i = 0; i < (uint32_t)p_len && p_str[i]; i++) {
+ tag[i] = p_str[i];
+ }
+
+ for (; i < 4; i++) {
+ tag[i] = ' ';
+ }
+
+ return OT_TAG(tag[0], tag[1], tag[2], tag[3]);
+}
+
+int32_t TextServerFallback::name_to_tag(const String &p_name) const {
+ for (int i = 0; feature_set[i].tag != 0; i++) {
+ if (feature_set[i].name == p_name) {
+ return feature_set[i].tag;
+ }
+ }
+
+ // No readable name, use tag string.
+ return ot_tag_from_string(p_name.replace("custom_", "").ascii().get_data(), -1);
+}
+
+_FORCE_INLINE_ void ot_tag_to_string(int32_t p_tag, char *p_buf) {
+ p_buf[0] = (char)(uint8_t)(p_tag >> 24);
+ p_buf[1] = (char)(uint8_t)(p_tag >> 16);
+ p_buf[2] = (char)(uint8_t)(p_tag >> 8);
+ p_buf[3] = (char)(uint8_t)(p_tag >> 0);
+}
+
+String TextServerFallback::tag_to_name(int32_t p_tag) const {
+ for (int i = 0; feature_set[i].tag != 0; i++) {
+ if (feature_set[i].tag == p_tag) {
+ return feature_set[i].name;
+ }
+ }
+
+ // No readable name, use tag string.
+ char name[5];
+ memset(name, 0, 5);
+ ot_tag_to_string(p_tag, name);
+ return String("custom_") + String(name);
+}
+
/*************************************************************************/
-/* Font interface */
+/* Font Glyph Rendering */
/*************************************************************************/
-RID TextServerFallback::create_font_system(const String &p_name, int p_base_size) {
- ERR_FAIL_V_MSG(RID(), "System fonts are not supported by this text server.");
+_FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_texture_pos_for_glyph(FontDataForSizeFallback *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) 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 (RenderingServer::get_singleton() != nullptr) {
+ if (ct.texture->get_format() != p_image_format) {
+ continue;
+ }
+ }
+
+ if (mw > ct.texture_w || mh > ct.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.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);
+ if (mw > texsize) {
+ texsize = mw; // Special case, adapt to it?
+ }
+ if (mh > texsize) {
+ texsize = mh; // Special case, adapt to it?
+ }
+
+ texsize = next_power_of_2(texsize);
+
+ texsize = MIN(texsize, 4096);
+
+ FontTexture tex;
+ tex.texture_w = texsize;
+ tex.texture_h = texsize;
+ tex.format = p_image_format;
+ tex.imgdata.resize(texsize * texsize * p_color_size);
+
+ {
+ // Zero texture.
+ uint8_t *w = tex.imgdata.ptrw();
+ ERR_FAIL_COND_V(texsize * texsize * p_color_size > tex.imgdata.size(), ret);
+ // Initialize the texture to all-white pixels to prevent artifacts when the
+ // font is displayed at a non-default scale with filtering enabled.
+ if (p_color_size == 2) {
+ for (int i = 0; i < texsize * texsize * p_color_size; i += 2) { // FORMAT_LA8, BW font.
+ w[i + 0] = 255;
+ w[i + 1] = 0;
+ }
+ } else if (p_color_size == 4) {
+ for (int i = 0; i < texsize * texsize * p_color_size; i += 4) { // FORMAT_RGBA8, Color font, Multichannel(+True) SDF.
+ w[i + 0] = 255;
+ w[i + 1] = 255;
+ w[i + 2] = 255;
+ w[i + 3] = 0;
+ }
+ } else {
+ ERR_FAIL_V(ret);
+ }
+ }
+ tex.offsets.resize(texsize);
+ for (int i = 0; i < texsize; i++) { // Zero offsets.
+ tex.offsets.write[i] = 0;
+ }
+
+ p_data->textures.push_back(tex);
+ ret.index = p_data->textures.size() - 1;
+ }
+
+ return ret;
}
-RID TextServerFallback::create_font_resource(const String &p_filename, int p_base_size) {
- _THREAD_SAFE_METHOD_
- FontDataFallback *fd = nullptr;
- if (p_filename.get_extension() == "fnt" || p_filename.get_extension() == "font") {
- fd = memnew(BitmapFontDataFallback);
+#ifdef MODULE_MSDFGEN_ENABLED
+
+struct MSContext {
+ msdfgen::Point2 position;
+ msdfgen::Shape *shape;
+ msdfgen::Contour *contour;
+};
+
+class DistancePixelConversion {
+ double invRange;
+
+public:
+ _FORCE_INLINE_ explicit DistancePixelConversion(double range) :
+ invRange(1 / range) {}
+ _FORCE_INLINE_ void operator()(float *pixels, const msdfgen::MultiAndTrueDistance &distance) const {
+ pixels[0] = float(invRange * distance.r + .5);
+ pixels[1] = float(invRange * distance.g + .5);
+ pixels[2] = float(invRange * distance.b + .5);
+ pixels[3] = float(invRange * distance.a + .5);
+ }
+};
+
+struct MSDFThreadData {
+ msdfgen::Bitmap<float, 4> *output;
+ msdfgen::Shape *shape;
+ msdfgen::Projection *projection;
+ DistancePixelConversion *distancePixelConversion;
+};
+
+static msdfgen::Point2 ft_point2(const FT_Vector &vector) {
+ return msdfgen::Point2(vector.x / 60.0f, vector.y / 60.0f);
+}
+
+static int ft_move_to(const FT_Vector *to, void *user) {
+ MSContext *context = reinterpret_cast<MSContext *>(user);
+ if (!(context->contour && context->contour->edges.empty())) {
+ context->contour = &context->shape->addContour();
+ }
+ context->position = ft_point2(*to);
+ return 0;
+}
+
+static int ft_line_to(const FT_Vector *to, void *user) {
+ MSContext *context = reinterpret_cast<MSContext *>(user);
+ msdfgen::Point2 endpoint = ft_point2(*to);
+ if (endpoint != context->position) {
+ context->contour->addEdge(new msdfgen::LinearSegment(context->position, endpoint));
+ context->position = endpoint;
+ }
+ return 0;
+}
+
+static int ft_conic_to(const FT_Vector *control, const FT_Vector *to, void *user) {
+ MSContext *context = reinterpret_cast<MSContext *>(user);
+ context->contour->addEdge(new msdfgen::QuadraticSegment(context->position, ft_point2(*control), ft_point2(*to)));
+ context->position = ft_point2(*to);
+ return 0;
+}
+
+static int ft_cubic_to(const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to, void *user) {
+ MSContext *context = reinterpret_cast<MSContext *>(user);
+ context->contour->addEdge(new msdfgen::CubicSegment(context->position, ft_point2(*control1), ft_point2(*control2), ft_point2(*to)));
+ context->position = ft_point2(*to);
+ return 0;
+}
+
+void TextServerFallback::_generateMTSDF_threaded(uint32_t y, void *p_td) const {
+ MSDFThreadData *td = (MSDFThreadData *)p_td;
+
+ msdfgen::ShapeDistanceFinder<msdfgen::OverlappingContourCombiner<msdfgen::MultiAndTrueDistanceSelector>> distanceFinder(*td->shape);
+ int row = td->shape->inverseYAxis ? td->output->height() - y - 1 : y;
+ for (int col = 0; col < td->output->width(); ++col) {
+ int x = (y % 2) ? td->output->width() - col - 1 : col;
+ msdfgen::Point2 p = td->projection->unproject(msdfgen::Point2(x + .5, y + .5));
+ msdfgen::MultiAndTrueDistance distance = distanceFinder.distance(p);
+ td->distancePixelConversion->operator()(td->output->operator()(x, row), distance);
+ }
+}
+
+_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(FontDataFallback *p_font_data, FontDataForSizeFallback *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const {
+ msdfgen::Shape shape;
+
+ shape.contours.clear();
+ shape.inverseYAxis = false;
+
+ MSContext context = {};
+ context.shape = &shape;
+ FT_Outline_Funcs ft_functions;
+ ft_functions.move_to = &ft_move_to;
+ ft_functions.line_to = &ft_line_to;
+ ft_functions.conic_to = &ft_conic_to;
+ ft_functions.cubic_to = &ft_cubic_to;
+ ft_functions.shift = 0;
+ ft_functions.delta = 0;
+
+ int error = FT_Outline_Decompose(outline, &ft_functions, &context);
+ ERR_FAIL_COND_V_MSG(error, FontGlyph(), "FreeType: Outline decomposition error: '" + String(FT_Error_String(error)) + "'.");
+ if (!shape.contours.empty() && shape.contours.back().edges.empty()) {
+ shape.contours.pop_back();
+ }
+
+ if (FT_Outline_Get_Orientation(outline) == 1) {
+ for (int i = 0; i < (int)shape.contours.size(); ++i) {
+ shape.contours[i].reverse();
+ }
+ }
+
+ shape.inverseYAxis = true;
+ shape.normalize();
+
+ msdfgen::Shape::Bounds bounds = shape.getBounds(p_pixel_range);
+
+ FontGlyph chr;
+ chr.found = true;
+ chr.advance = advance.round();
+
+ if (shape.validate() && shape.contours.size() > 0) {
+ int w = (bounds.r - bounds.l);
+ int h = (bounds.t - bounds.b);
+
+ int mw = w + p_rect_margin * 2;
+ int mh = h + p_rect_margin * 2;
+
+ ERR_FAIL_COND_V(mw > 4096, FontGlyph());
+ ERR_FAIL_COND_V(mh > 4096, FontGlyph());
+
+ FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, 4, Image::FORMAT_RGBA8, mw, mh);
+ ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph());
+ FontTexture &tex = p_data->textures.write[tex_pos.index];
+
+ edgeColoringSimple(shape, 3.0); // Max. angle.
+ msdfgen::Bitmap<real_t, 4> image(w, h); // Texture size.
+ //msdfgen::generateMTSDF(image, shape, p_pixel_range, 1.0, msdfgen::Vector2(-bounds.l, -bounds.b)); // Range, scale, translation.
+
+ DistancePixelConversion distancePixelConversion(p_pixel_range);
+ msdfgen::Projection projection(msdfgen::Vector2(1.0, 1.0), msdfgen::Vector2(-bounds.l, -bounds.b));
+ msdfgen::MSDFGeneratorConfig config(true, msdfgen::ErrorCorrectionConfig());
+
+ MSDFThreadData td;
+ td.output = &image;
+ td.shape = &shape;
+ td.projection = &projection;
+ td.distancePixelConversion = &distancePixelConversion;
+
+ if (p_font_data->work_pool.get_thread_count() == 0) {
+ p_font_data->work_pool.init();
+ }
+ p_font_data->work_pool.do_work(h, this, &TextServerFallback::_generateMTSDF_threaded, &td);
+
+ msdfgen::msdfErrorCorrection(image, shape, projection, p_pixel_range, config);
+
+ {
+ uint8_t *wr = tex.imgdata.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs = ((i + tex_pos.y + p_rect_margin) * tex.texture_w + j + tex_pos.x + p_rect_margin) * 4;
+ ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), FontGlyph());
+ wr[ofs + 0] = (uint8_t)(CLAMP(image(j, i)[0] * 256.f, 0.f, 255.f));
+ wr[ofs + 1] = (uint8_t)(CLAMP(image(j, i)[1] * 256.f, 0.f, 255.f));
+ wr[ofs + 2] = (uint8_t)(CLAMP(image(j, i)[2] * 256.f, 0.f, 255.f));
+ wr[ofs + 3] = (uint8_t)(CLAMP(image(j, i)[3] * 256.f, 0.f, 255.f));
+ }
+ }
+ }
+
+ // Blit to image and texture.
+ {
+ if (RenderingServer::get_singleton() != nullptr) {
+ Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, Image::FORMAT_RGBA8, tex.imgdata));
+ if (tex.texture.is_null()) {
+ tex.texture.instantiate();
+ tex.texture->create_from_image(img);
+ } else {
+ tex.texture->update(img);
+ }
+ }
+ }
+
+ // Update height array.
+ for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
+ tex.offsets.write[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, h);
+ chr.rect.position = Vector2(bounds.l, -bounds.t);
+ chr.rect.size = chr.uv_rect.size;
+ }
+ return chr;
+}
+#endif
+
#ifdef MODULE_FREETYPE_ENABLED
- } else if (p_filename.get_extension() == "ttf" || p_filename.get_extension() == "otf" || p_filename.get_extension() == "woff") {
- fd = memnew(DynamicFontDataFallback);
+_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitmap(FontDataForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const {
+ int w = bitmap.width;
+ int h = bitmap.rows;
+
+ int mw = w + p_rect_margin * 2;
+ int mh = h + p_rect_margin * 2;
+
+ 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);
+ ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph());
+
+ // Fit character in char texture.
+
+ FontTexture &tex = p_data->textures.write[tex_pos.index];
+
+ {
+ uint8_t *wr = tex.imgdata.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs = ((i + tex_pos.y + p_rect_margin) * tex.texture_w + j + tex_pos.x + p_rect_margin) * color_size;
+ ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), FontGlyph());
+ switch (bitmap.pixel_mode) {
+ case FT_PIXEL_MODE_MONO: {
+ int byte = i * bitmap.pitch + (j >> 3);
+ int bit = 1 << (7 - (j % 8));
+ wr[ofs + 0] = 255; //grayscale as 1
+ wr[ofs + 1] = (bitmap.buffer[byte] & bit) ? 255 : 0;
+ } break;
+ case FT_PIXEL_MODE_GRAY:
+ wr[ofs + 0] = 255; //grayscale as 1
+ wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j];
+ break;
+ case FT_PIXEL_MODE_BGRA: {
+ int ofs_color = i * bitmap.pitch + (j << 2);
+ wr[ofs + 2] = bitmap.buffer[ofs_color + 0];
+ wr[ofs + 1] = bitmap.buffer[ofs_color + 1];
+ wr[ofs + 0] = bitmap.buffer[ofs_color + 2];
+ wr[ofs + 3] = bitmap.buffer[ofs_color + 3];
+ } break;
+ default:
+ ERR_FAIL_V_MSG(FontGlyph(), "Font uses unsupported pixel format: " + itos(bitmap.pixel_mode) + ".");
+ break;
+ }
+ }
+ }
+ }
+
+ // Blit to image and texture.
+ {
+ if (RenderingServer::get_singleton() != nullptr) {
+ Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, require_format, tex.imgdata));
+
+ if (tex.texture.is_null()) {
+ tex.texture.instantiate();
+ tex.texture->create_from_image(img);
+ } else {
+ tex.texture->update(img);
+ }
+ }
+ }
+
+ // Update height array.
+ for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
+ tex.offsets.write[k] = tex_pos.y + mh;
+ }
+
+ FontGlyph chr;
+ chr.advance = (advance * p_data->scale / p_data->oversampling).round();
+ chr.texture_idx = tex_pos.index;
+ chr.found = true;
+
+ chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w, h);
+ chr.rect.position = (Vector2(xofs, -yofs) * p_data->scale / p_data->oversampling).round();
+ chr.rect.size = chr.uv_rect.size * p_data->scale / p_data->oversampling;
+ return chr;
+}
#endif
- } else {
- return RID();
+
+/*************************************************************************/
+/* Font Cache */
+/*************************************************************************/
+
+_FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontDataFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const {
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(p_font_data, p_size), false);
+
+ FontDataForSizeFallback *fd = p_font_data->cache[p_size];
+ if (fd->glyph_map.has(p_glyph)) {
+ return fd->glyph_map[p_glyph].found;
}
- Error err = fd->load_from_file(p_filename, p_base_size);
- if (err != OK) {
- memdelete(fd);
- return RID();
+ if (p_glyph == 0) { // Non graphical or invalid glyph, do not render.
+ fd->glyph_map[p_glyph] = FontGlyph();
+ return true;
}
- return font_owner.make_rid(fd);
+#ifdef MODULE_FREETYPE_ENABLED
+ FontGlyph gl;
+ if (fd->face) {
+ FT_Int32 flags = FT_LOAD_DEFAULT;
+
+ bool outline = p_size.y > 0;
+ switch (p_font_data->hinting) {
+ case TextServer::HINTING_NONE:
+ flags |= FT_LOAD_NO_HINTING;
+ break;
+ case TextServer::HINTING_LIGHT:
+ flags |= FT_LOAD_TARGET_LIGHT;
+ break;
+ default:
+ flags |= FT_LOAD_TARGET_NORMAL;
+ break;
+ }
+ if (p_font_data->force_autohinter) {
+ flags |= FT_LOAD_FORCE_AUTOHINT;
+ }
+ if (outline) {
+ flags |= FT_LOAD_NO_BITMAP;
+ } else if (FT_HAS_COLOR(fd->face)) {
+ flags |= FT_LOAD_COLOR;
+ }
+
+ FT_Fixed v, h;
+ FT_Get_Advance(fd->face, p_glyph, flags, &h);
+ FT_Get_Advance(fd->face, p_glyph, flags | FT_LOAD_VERTICAL_LAYOUT, &v);
+
+ int error = FT_Load_Glyph(fd->face, p_glyph, flags);
+ if (error) {
+ fd->glyph_map[p_glyph] = FontGlyph();
+ ERR_FAIL_V_MSG(false, "FreeType: Failed to load glyph.");
+ }
+
+ 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);
+ }
+ FT_GlyphSlot slot = fd->face->glyph;
+ if (!error) {
+ if (p_font_data->msdf) {
+#ifdef MODULE_MSDFGEN_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();
+ 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);
+ }
+ }
+ } else {
+ FT_Stroker stroker;
+ if (FT_Stroker_New(library, &stroker) != 0) {
+ fd->glyph_map[p_glyph] = FontGlyph();
+ ERR_FAIL_V_MSG(false, "FreeType: Failed to load glyph stroker.");
+ }
+
+ FT_Stroker_Set(stroker, (int)(fd->size.y * fd->oversampling * 16.0), FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0);
+ FT_Glyph glyph;
+ FT_BitmapGlyph glyph_bitmap;
+
+ if (FT_Get_Glyph(fd->face->glyph, &glyph) != 0) {
+ goto cleanup_stroker;
+ }
+ 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) {
+ goto cleanup_glyph;
+ }
+ glyph_bitmap = (FT_BitmapGlyph)glyph;
+ gl = rasterize_bitmap(fd, rect_range, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2());
+
+ cleanup_glyph:
+ FT_Done_Glyph(glyph);
+ cleanup_stroker:
+ FT_Stroker_Done(stroker);
+ }
+ fd->glyph_map[p_glyph] = gl;
+ return gl.found;
+ }
+#endif
+ fd->glyph_map[p_glyph] = FontGlyph();
+ return false;
}
-RID TextServerFallback::create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size) {
- _THREAD_SAFE_METHOD_
- FontDataFallback *fd = nullptr;
- if (p_type == "fnt" || p_type == "font") {
- fd = memnew(BitmapFontDataFallback);
+_FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback *p_font_data, const Vector2i &p_size) const {
+ if (p_font_data->cache.has(p_size)) {
+ return true;
+ }
+
+ FontDataForSizeFallback *fd = memnew(FontDataForSizeFallback);
+ fd->size = p_size;
+ if (p_font_data->data_ptr) {
+ // Init dynamic font.
#ifdef MODULE_FREETYPE_ENABLED
- } else if (p_type == "ttf" || p_type == "otf" || p_type == "woff") {
- fd = memnew(DynamicFontDataFallback);
+ int error = 0;
+ if (!library) {
+ error = FT_Init_FreeType(&library);
+ ERR_FAIL_COND_V_MSG(error != 0, false, TTR("FreeType: Error initializing library:") + " '" + String(FT_Error_String(error)) + "'.");
+ }
+
+ memset(&fd->stream, 0, sizeof(FT_StreamRec));
+ fd->stream.base = (unsigned char *)p_font_data->data_ptr;
+ fd->stream.size = p_font_data->data_size;
+ fd->stream.pos = 0;
+
+ FT_Open_Args fargs;
+ memset(&fargs, 0, sizeof(FT_Open_Args));
+ fargs.memory_base = (unsigned char *)p_font_data->data_ptr;
+ fargs.memory_size = p_font_data->data_size;
+ fargs.flags = FT_OPEN_MEMORY;
+ fargs.stream = &fd->stream;
+ error = FT_Open_Face(library, &fargs, 0, &fd->face);
+ if (error) {
+ FT_Done_Face(fd->face);
+ fd->face = nullptr;
+ ERR_FAIL_V_MSG(false, TTR("FreeType: Error loading font:") + " '" + String(FT_Error_String(error)) + "'.");
+ }
+
+ if (p_font_data->msdf) {
+ fd->oversampling = 1.0f;
+ fd->size.x = p_font_data->msdf_source_size;
+ } else if (p_font_data->oversampling <= 0.0f) {
+ fd->oversampling = TS->font_get_global_oversampling();
+ } else {
+ fd->oversampling = p_font_data->oversampling;
+ }
+
+ if (FT_HAS_COLOR(fd->face) && fd->face->num_fixed_sizes > 0) {
+ int best_match = 0;
+ int diff = ABS(fd->size.x - ((int64_t)fd->face->available_sizes[0].width));
+ fd->scale = real_t(fd->size.x * fd->oversampling) / fd->face->available_sizes[0].width;
+ for (int i = 1; i < fd->face->num_fixed_sizes; i++) {
+ int ndiff = ABS(fd->size.x - ((int64_t)fd->face->available_sizes[i].width));
+ if (ndiff < diff) {
+ best_match = i;
+ diff = ndiff;
+ fd->scale = real_t(fd->size.x * fd->oversampling) / fd->face->available_sizes[i].width;
+ }
+ }
+ FT_Select_Size(fd->face, best_match);
+ } else {
+ FT_Set_Pixel_Sizes(fd->face, 0, fd->size.x * fd->oversampling);
+ }
+
+ fd->ascent = (fd->face->size->metrics.ascender / 64.0) / fd->oversampling * fd->scale;
+ fd->descent = (-fd->face->size->metrics.descender / 64.0) / fd->oversampling * fd->scale;
+ fd->underline_position = (-FT_MulFix(fd->face->underline_position, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale;
+ fd->underline_thickness = (FT_MulFix(fd->face->underline_thickness, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale;
+
+ if (!p_font_data->face_init) {
+ // Read OpenType variations.
+ p_font_data->supported_varaitions.clear();
+ if (fd->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
+ FT_MM_Var *amaster;
+ FT_Get_MM_Var(fd->face, &amaster);
+ for (FT_UInt i = 0; i < amaster->num_axis; i++) {
+ p_font_data->supported_varaitions[(int32_t)amaster->axis[i].tag] = Vector3i(amaster->axis[i].minimum / 65536, amaster->axis[i].maximum / 65536, amaster->axis[i].def / 65536);
+ }
+ FT_Done_MM_Var(library, amaster);
+ }
+ p_font_data->face_init = true;
+ }
+
+ // Write variations.
+ if (fd->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
+ FT_MM_Var *amaster;
+
+ FT_Get_MM_Var(fd->face, &amaster);
+
+ Vector<FT_Fixed> coords;
+ coords.resize(amaster->num_axis);
+
+ FT_Get_Var_Design_Coordinates(fd->face, coords.size(), coords.ptrw());
+
+ for (FT_UInt i = 0; i < amaster->num_axis; i++) {
+ // Reset to default.
+ int32_t var_tag = amaster->axis[i].tag;
+ real_t var_value = (double)amaster->axis[i].def / 65536.f;
+ coords.write[i] = amaster->axis[i].def;
+
+ if (p_font_data->variation_coordinates.has(var_tag)) {
+ var_value = p_font_data->variation_coordinates[var_tag];
+ coords.write[i] = CLAMP(var_value * 65536.f, 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)];
+ coords.write[i] = CLAMP(var_value * 65536.f, amaster->axis[i].minimum, amaster->axis[i].maximum);
+ }
+ }
+
+ FT_Set_Var_Design_Coordinates(fd->face, coords.size(), coords.ptrw());
+ FT_Done_MM_Var(library, amaster);
+ }
+#else
+ ERR_FAIL_V_MSG(false, TTR("FreeType: Can't load dynamic font, engine is compiled without FreeType support!");
#endif
- } else {
- return RID();
}
+ p_font_data->cache[p_size] = fd;
+ return true;
+}
- Error err = fd->load_from_memory(p_data, p_size, p_base_size);
- if (err != OK) {
- memdelete(fd);
- return RID();
+_FORCE_INLINE_ void TextServerFallback::_font_clear_cache(FontDataFallback *p_font_data) {
+ for (const Map<Vector2i, FontDataForSizeFallback *>::Element *E = p_font_data->cache.front(); E; E = E->next()) {
+ memdelete(E->get());
}
+ p_font_data->cache.clear();
+ p_font_data->face_init = false;
+ p_font_data->supported_varaitions.clear();
+}
+
+RID TextServerFallback::create_font() {
+ FontDataFallback *fd = memnew(FontDataFallback);
+
return font_owner.make_rid(fd);
}
-RID TextServerFallback::create_font_bitmap(float p_height, float p_ascent, int p_base_size) {
- _THREAD_SAFE_METHOD_
- FontDataFallback *fd = memnew(BitmapFontDataFallback);
- Error err = fd->bitmap_new(p_height, p_ascent, p_base_size);
- if (err != OK) {
- memdelete(fd);
- return RID();
+void TextServerFallback::font_set_data(RID p_font_rid, const PackedByteArray &p_data) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ _font_clear_cache(fd);
+ fd->data = p_data;
+ fd->data_ptr = fd->data.ptr();
+ fd->data_size = fd->data.size();
+}
+
+void TextServerFallback::font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ _font_clear_cache(fd);
+ fd->data.clear();
+ fd->data_ptr = p_data_ptr;
+ fd->data_size = p_data_size;
+}
+
+void TextServerFallback::font_set_antialiased(RID p_font_rid, bool p_antialiased) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->antialiased != p_antialiased) {
+ _font_clear_cache(fd);
+ fd->antialiased = p_antialiased;
}
+}
- return font_owner.make_rid(fd);
+bool TextServerFallback::font_is_antialiased(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->antialiased;
}
-void TextServerFallback::font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) {
- _THREAD_SAFE_METHOD_
- FontDataFallback *fd = font_owner.getornull(p_font);
+void TextServerFallback::font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
- fd->bitmap_add_texture(p_texture);
+
+ MutexLock lock(fd->mutex);
+ if (fd->msdf != p_msdf) {
+ _font_clear_cache(fd);
+ fd->msdf = p_msdf;
+ }
}
-void TextServerFallback::font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) {
- _THREAD_SAFE_METHOD_
- FontDataFallback *fd = font_owner.getornull(p_font);
+bool TextServerFallback::font_is_multichannel_signed_distance_field(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->msdf;
+}
+
+void TextServerFallback::font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
- fd->bitmap_add_char(p_char, p_texture_idx, p_rect, p_align, p_advance);
+
+ MutexLock lock(fd->mutex);
+ if (fd->msdf_range != p_msdf_pixel_range) {
+ _font_clear_cache(fd);
+ fd->msdf_range = p_msdf_pixel_range;
+ }
}
-void TextServerFallback::font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) {
- _THREAD_SAFE_METHOD_
- FontDataFallback *fd = font_owner.getornull(p_font);
+int TextServerFallback::font_get_msdf_pixel_range(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->msdf_range;
+}
+
+void TextServerFallback::font_set_msdf_size(RID p_font_rid, int p_msdf_size) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
- fd->bitmap_add_kerning_pair(p_A, p_B, p_kerning);
+
+ MutexLock lock(fd->mutex);
+ if (fd->msdf_source_size != p_msdf_size) {
+ _font_clear_cache(fd);
+ fd->msdf_source_size = p_msdf_size;
+ }
}
-float TextServerFallback::font_get_height(RID p_font, int p_size) const {
- _THREAD_SAFE_METHOD_
- const FontDataFallback *fd = font_owner.getornull(p_font);
+int TextServerFallback::font_get_msdf_size(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->msdf_source_size;
+}
+
+void TextServerFallback::font_set_fixed_size(RID p_font_rid, int p_fixed_size) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->fixed_size != p_fixed_size) {
+ fd->fixed_size = p_fixed_size;
+ }
+}
+
+int TextServerFallback::font_get_fixed_size(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->fixed_size;
+}
+
+void TextServerFallback::font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->force_autohinter != p_force_autohinter) {
+ _font_clear_cache(fd);
+ fd->force_autohinter = p_force_autohinter;
+ }
+}
+
+bool TextServerFallback::font_is_force_autohinter(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->force_autohinter;
+}
+
+void TextServerFallback::font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->hinting != p_hinting) {
+ _font_clear_cache(fd);
+ fd->hinting = p_hinting;
+ }
+}
+
+TextServer::Hinting TextServerFallback::font_get_hinting(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, HINTING_NONE);
+
+ MutexLock lock(fd->mutex);
+ return fd->hinting;
+}
+
+void TextServerFallback::font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->variation_coordinates != p_variation_coordinates) {
+ _font_clear_cache(fd);
+ fd->variation_coordinates = p_variation_coordinates;
+ }
+}
+
+Dictionary TextServerFallback::font_get_variation_coordinates(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Dictionary());
+
+ MutexLock lock(fd->mutex);
+ return fd->variation_coordinates;
+}
+
+void TextServerFallback::font_set_oversampling(RID p_font_rid, real_t p_oversampling) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->oversampling != p_oversampling) {
+ _font_clear_cache(fd);
+ fd->oversampling = p_oversampling;
+ }
+}
+
+real_t TextServerFallback::font_get_oversampling(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.f);
- return fd->get_height(p_size);
+
+ MutexLock lock(fd->mutex);
+ return fd->oversampling;
}
-float TextServerFallback::font_get_ascent(RID p_font, int p_size) const {
- _THREAD_SAFE_METHOD_
- const FontDataFallback *fd = font_owner.getornull(p_font);
+Array TextServerFallback::font_get_size_cache_list(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Array());
+
+ MutexLock lock(fd->mutex);
+ Array ret;
+ for (const Map<Vector2i, FontDataForSizeFallback *>::Element *E = fd->cache.front(); E; E = E->next()) {
+ ret.push_back(E->key());
+ }
+ return ret;
+}
+
+void TextServerFallback::font_clear_size_cache(RID p_font_rid) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ for (const Map<Vector2i, FontDataForSizeFallback *>::Element *E = fd->cache.front(); E; E = E->next()) {
+ memdelete(E->get());
+ }
+ fd->cache.clear();
+}
+
+void TextServerFallback::font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->cache.has(p_size)) {
+ memdelete(fd->cache[p_size]);
+ fd->cache.erase(p_size);
+ }
+}
+
+void TextServerFallback::font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->cache[size]->ascent = p_ascent;
+}
+
+real_t TextServerFallback::font_get_ascent(RID p_font_rid, int p_size) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.f);
- return fd->get_ascent(p_size);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
+
+ if (fd->msdf) {
+ return fd->cache[size]->ascent * (real_t)p_size / (real_t)fd->msdf_source_size;
+ } else {
+ return fd->cache[size]->ascent;
+ }
}
-float TextServerFallback::font_get_descent(RID p_font, int p_size) const {
- _THREAD_SAFE_METHOD_
- const FontDataFallback *fd = font_owner.getornull(p_font);
+void TextServerFallback::font_set_descent(RID p_font_rid, int p_size, real_t p_descent) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->cache[size]->descent = p_descent;
+}
+
+real_t TextServerFallback::font_get_descent(RID p_font_rid, int p_size) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.f);
- return fd->get_descent(p_size);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
+
+ if (fd->msdf) {
+ return fd->cache[size]->descent * (real_t)p_size / (real_t)fd->msdf_source_size;
+ } else {
+ return fd->cache[size]->descent;
+ }
}
-float TextServerFallback::font_get_underline_position(RID p_font, int p_size) const {
- _THREAD_SAFE_METHOD_
- const FontDataFallback *fd = font_owner.getornull(p_font);
+void TextServerFallback::font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->cache[size]->underline_position = p_underline_position;
+}
+
+real_t TextServerFallback::font_get_underline_position(RID p_font_rid, int p_size) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.f);
- return fd->get_underline_position(p_size);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
+
+ if (fd->msdf) {
+ return fd->cache[size]->underline_position * (real_t)p_size / (real_t)fd->msdf_source_size;
+ } else {
+ return fd->cache[size]->underline_position;
+ }
}
-float TextServerFallback::font_get_underline_thickness(RID p_font, int p_size) const {
- _THREAD_SAFE_METHOD_
- const FontDataFallback *fd = font_owner.getornull(p_font);
+void TextServerFallback::font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->cache[size]->underline_thickness = p_underline_thickness;
+}
+
+real_t TextServerFallback::font_get_underline_thickness(RID p_font_rid, int p_size) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.f);
- return fd->get_underline_thickness(p_size);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
+
+ if (fd->msdf) {
+ return fd->cache[size]->underline_thickness * (real_t)p_size / (real_t)fd->msdf_source_size;
+ } else {
+ return fd->cache[size]->underline_thickness;
+ }
}
-int TextServerFallback::font_get_spacing_space(RID p_font) const {
- _THREAD_SAFE_METHOD_
- const FontDataFallback *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, 0);
- return fd->get_spacing_space();
+void TextServerFallback::font_set_scale(RID p_font_rid, int p_size, real_t p_scale) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->cache[size]->scale = p_scale;
}
-void TextServerFallback::font_set_spacing_space(RID p_font, int p_value) {
- _THREAD_SAFE_METHOD_
- FontDataFallback *fd = font_owner.getornull(p_font);
+real_t TextServerFallback::font_get_scale(RID p_font_rid, int p_size) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0.f);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
+
+ if (fd->msdf) {
+ return fd->cache[size]->scale * (real_t)p_size / (real_t)fd->msdf_source_size;
+ } else {
+ return fd->cache[size]->scale / fd->cache[size]->oversampling;
+ }
+}
+
+void TextServerFallback::font_set_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing, int p_value) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
- fd->set_spacing_space(p_value);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ switch (p_spacing) {
+ case TextServer::SPACING_GLYPH: {
+ fd->cache[size]->spacing_glyph = p_value;
+ } break;
+ case TextServer::SPACING_SPACE: {
+ fd->cache[size]->spacing_space = p_value;
+ } break;
+ default: {
+ ERR_FAIL_MSG("Invalid spacing type: " + itos(p_spacing));
+ } break;
+ }
}
-int TextServerFallback::font_get_spacing_glyph(RID p_font) const {
- _THREAD_SAFE_METHOD_
- const FontDataFallback *fd = font_owner.getornull(p_font);
+int TextServerFallback::font_get_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0);
+
+ switch (p_spacing) {
+ case TextServer::SPACING_GLYPH: {
+ if (fd->msdf) {
+ return fd->cache[size]->spacing_glyph * (real_t)p_size / (real_t)fd->msdf_source_size;
+ } else {
+ return fd->cache[size]->spacing_glyph;
+ }
+ } break;
+ case TextServer::SPACING_SPACE: {
+ if (fd->msdf) {
+ return fd->cache[size]->spacing_space * (real_t)p_size / (real_t)fd->msdf_source_size;
+ } else {
+ return fd->cache[size]->spacing_space;
+ }
+ } break;
+ default: {
+ ERR_FAIL_V_MSG(0, "Invalid spacing type: " + itos(p_spacing));
+ } break;
+ }
+ return 0;
+}
+
+int TextServerFallback::font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, 0);
- return fd->get_spacing_glyph();
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0);
+
+ return fd->cache[size]->textures.size();
}
-void TextServerFallback::font_set_spacing_glyph(RID p_font, int p_value) {
- _THREAD_SAFE_METHOD_
- FontDataFallback *fd = font_owner.getornull(p_font);
+void TextServerFallback::font_clear_textures(RID p_font_rid, const Vector2i &p_size) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
- fd->set_spacing_glyph(p_value);
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->cache[size]->textures.clear();
}
-void TextServerFallback::font_set_antialiased(RID p_font, bool p_antialiased) {
- _THREAD_SAFE_METHOD_
- FontDataFallback *fd = font_owner.getornull(p_font);
+void TextServerFallback::font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
- fd->set_antialiased(p_antialiased);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ ERR_FAIL_INDEX(p_texture_index, fd->cache[size]->textures.size());
+
+ fd->cache[size]->textures.remove(p_texture_index);
}
-bool TextServerFallback::font_get_antialiased(RID p_font) const {
- _THREAD_SAFE_METHOD_
- const FontDataFallback *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, false);
- return fd->get_antialiased();
+void TextServerFallback::font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+ ERR_FAIL_COND(p_image.is_null());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ ERR_FAIL_COND(p_texture_index < 0);
+ if (p_texture_index >= fd->cache[size]->textures.size()) {
+ fd->cache[size]->textures.resize(p_texture_index + 1);
+ }
+
+ FontTexture &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 = memnew(Image(tex.texture_w, tex.texture_h, 0, tex.format, tex.imgdata));
+ tex.texture = Ref<ImageTexture>();
+ tex.texture.instantiate();
+ tex.texture->create_from_image(img);
}
-void TextServerFallback::font_set_distance_field_hint(RID p_font, bool p_distance_field) {
- _THREAD_SAFE_METHOD_
- FontDataFallback *fd = font_owner.getornull(p_font);
+Ref<Image> TextServerFallback::font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Ref<Image>());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ 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.write[p_texture_index];
+ Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, tex.format, tex.imgdata));
+
+ return img;
+}
+
+void TextServerFallback::font_set_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
- fd->set_distance_field_hint(p_distance_field);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ if (p_texture_index >= fd->cache[size]->textures.size()) {
+ fd->cache[size]->textures.resize(p_texture_index + 1);
+ }
+
+ FontTexture &tex = fd->cache[size]->textures.write[p_texture_index];
+ tex.offsets = p_offset;
}
-bool TextServerFallback::font_get_distance_field_hint(RID p_font) const {
- _THREAD_SAFE_METHOD_
- const FontDataFallback *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, false);
- return fd->get_distance_field_hint();
+PackedInt32Array TextServerFallback::font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ 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), PackedInt32Array());
+ ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), PackedInt32Array());
+
+ const FontTexture &tex = fd->cache[size]->textures.write[p_texture_index];
+ return tex.offsets;
}
-void TextServerFallback::font_set_hinting(RID p_font, TextServer::Hinting p_hinting) {
- _THREAD_SAFE_METHOD_
- FontDataFallback *fd = font_owner.getornull(p_font);
+Array TextServerFallback::font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Array());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array());
+
+ Array ret;
+ const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+ const int32_t *E = nullptr;
+ while ((E = gl.next(E))) {
+ ret.push_back(*E);
+ }
+ return ret;
+}
+
+void TextServerFallback::font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
- fd->set_hinting(p_hinting);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+
+ fd->cache[size]->glyph_map.clear();
}
-TextServer::Hinting TextServerFallback::font_get_hinting(RID p_font) const {
- _THREAD_SAFE_METHOD_
- const FontDataFallback *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, TextServer::HINTING_NONE);
- return fd->get_hinting();
+void TextServerFallback::font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+
+ fd->cache[size]->glyph_map.erase(p_glyph);
}
-void TextServerFallback::font_set_force_autohinter(RID p_font, bool p_enabeld) {
- _THREAD_SAFE_METHOD_
- FontDataFallback *fd = font_owner.getornull(p_font);
+Vector2 TextServerFallback::font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Vector2());
+
+ MutexLock lock(fd->mutex);
+ 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)) {
+ 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].advance * (real_t)p_size / (real_t)fd->msdf_source_size;
+ } else {
+ return gl[p_glyph].advance;
+ }
+}
+
+void TextServerFallback::font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
- fd->set_force_autohinter(p_enabeld);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+
+ HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ gl[p_glyph].advance = p_advance;
+ gl[p_glyph].found = true;
}
-bool TextServerFallback::font_get_force_autohinter(RID p_font) const {
- _THREAD_SAFE_METHOD_
- const FontDataFallback *fd = font_owner.getornull(p_font);
+Vector2 TextServerFallback::font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Vector2());
+
+ MutexLock lock(fd->mutex);
+ 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)) {
+ 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 * (real_t)p_size.x / (real_t)fd->msdf_source_size;
+ } else {
+ return gl[p_glyph].rect.position;
+ }
+}
+
+void TextServerFallback::font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+
+ HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ gl[p_glyph].rect.position = p_offset;
+ gl[p_glyph].found = true;
+}
+
+Vector2 TextServerFallback::font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Vector2());
+
+ MutexLock lock(fd->mutex);
+ 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)) {
+ 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 * (real_t)p_size.x / (real_t)fd->msdf_source_size;
+ } else {
+ return gl[p_glyph].rect.size;
+ }
+}
+
+void TextServerFallback::font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+
+ HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ gl[p_glyph].rect.size = p_gl_size;
+ gl[p_glyph].found = true;
+}
+
+Rect2 TextServerFallback::font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Rect2());
+
+ MutexLock lock(fd->mutex);
+ 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)) {
+ 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;
+}
+
+void TextServerFallback::font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+
+ HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ gl[p_glyph].uv_rect = p_uv_rect;
+ gl[p_glyph].found = true;
+}
+
+int TextServerFallback::font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, -1);
+
+ MutexLock lock(fd->mutex);
+ 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)) {
+ 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;
+}
+
+void TextServerFallback::font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+
+ HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ gl[p_glyph].texture_idx = p_texture_idx;
+ gl[p_glyph].found = true;
+}
+
+bool TextServerFallback::font_get_glyph_contours(RID p_font_rid, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
- return fd->get_force_autohinter();
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), false);
+
+#ifdef MODULE_FREETYPE_ENABLED
+ int error = FT_Load_Glyph(fd->cache[size]->face, p_index, FT_LOAD_NO_BITMAP | (fd->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0));
+ ERR_FAIL_COND_V(error, false);
+
+ r_points.clear();
+ r_contours.clear();
+
+ real_t h = fd->cache[size]->ascent;
+ real_t scale = (1.0 / 64.0) / fd->cache[size]->oversampling * fd->cache[size]->scale;
+ if (fd->msdf) {
+ scale = scale * (real_t)p_size / (real_t)fd->msdf_source_size;
+ }
+ for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_points; i++) {
+ r_points.push_back(Vector3(fd->cache[size]->face->glyph->outline.points[i].x * scale, h - fd->cache[size]->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fd->cache[size]->face->glyph->outline.tags[i])));
+ }
+ for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_contours; i++) {
+ r_contours.push_back(fd->cache[size]->face->glyph->outline.contours[i]);
+ }
+ r_orientation = (FT_Outline_Get_Orientation(&fd->cache[size]->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT);
+#else
+ return false;
+#endif
+ return true;
}
-bool TextServerFallback::font_has_char(RID p_font, char32_t p_char) const {
- _THREAD_SAFE_METHOD_
- const FontDataFallback *fd = font_owner.getornull(p_font);
+Array TextServerFallback::font_get_kerning_list(RID p_font_rid, int p_size) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Array());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array());
+
+ Array ret;
+ for (const Map<Vector2i, Vector2>::Element *E = fd->cache[size]->kerning_map.front(); E; E = E->next()) {
+ ret.push_back(E->key());
+ }
+ return ret;
+}
+
+void TextServerFallback::font_clear_kerning_map(RID p_font_rid, int p_size) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->cache[size]->kerning_map.clear();
+}
+
+void TextServerFallback::font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->cache[size]->kerning_map.erase(p_glyph_pair);
+}
+
+void TextServerFallback::font_set_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->cache[size]->kerning_map[p_glyph_pair] = p_kerning;
+}
+
+Vector2 TextServerFallback::font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Vector2());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
+
+ const Map<Vector2i, Vector2> &kern = fd->cache[size]->kerning_map;
+
+ if (kern.has(p_glyph_pair)) {
+ if (fd->msdf) {
+ return kern[p_glyph_pair] * (real_t)p_size / (real_t)fd->msdf_source_size;
+ } else {
+ return kern[p_glyph_pair];
+ }
+ } else {
+#ifdef MODULE_FREETYPE_ENABLED
+ if (fd->cache[size]->face) {
+ FT_Vector delta;
+ FT_Get_Kerning(fd->cache[size]->face, p_glyph_pair.x, p_glyph_pair.y, FT_KERNING_DEFAULT, &delta);
+ if (fd->msdf) {
+ return Vector2(delta.x, delta.y) * (real_t)p_size / (real_t)fd->msdf_source_size;
+ } else {
+ return Vector2(delta.x, delta.y);
+ }
+ }
+#endif
+ }
+ return Vector2();
+}
+
+int32_t TextServerFallback::font_get_glyph_index(RID p_font_rid, int p_size, char32_t p_char, char32_t p_variation_selector) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0);
+
+#ifdef MODULE_FREETYPE_ENABLED
+ if (fd->cache[size]->face) {
+ if (p_variation_selector) {
+ return FT_Face_GetCharVariantIndex(fd->cache[size]->face, p_char, p_variation_selector);
+ } else {
+ return FT_Get_Char_Index(fd->cache[size]->face, p_char);
+ }
+ } else {
+ return 0;
+ }
+#else
+ return (int32_t)p_char;
+#endif
+}
+
+bool TextServerFallback::font_has_char(RID p_font_rid, char32_t p_char) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
- return fd->has_char(p_char);
+
+ MutexLock lock(fd->mutex);
+ if (fd->cache.is_empty()) {
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), false);
+ }
+ FontDataForSizeFallback *at_size = fd->cache.front()->get();
+
+#ifdef MODULE_FREETYPE_ENABLED
+ if (at_size && at_size->face) {
+ return FT_Get_Char_Index(at_size->face, p_char) != 0;
+ }
+#endif
+ return (at_size) ? at_size->glyph_map.has((int32_t)p_char) : false;
}
-String TextServerFallback::font_get_supported_chars(RID p_font) const {
- _THREAD_SAFE_METHOD_
- const FontDataFallback *fd = font_owner.getornull(p_font);
+String TextServerFallback::font_get_supported_chars(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, String());
- return fd->get_supported_chars();
+
+ MutexLock lock(fd->mutex);
+ if (fd->cache.is_empty()) {
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), String());
+ }
+ FontDataForSizeFallback *at_size = fd->cache.front()->get();
+
+ String chars;
+#ifdef MODULE_FREETYPE_ENABLED
+ if (at_size && at_size->face) {
+ FT_UInt gindex;
+ FT_ULong charcode = FT_Get_First_Char(at_size->face, &gindex);
+ while (gindex != 0) {
+ if (charcode != 0) {
+ chars += char32_t(charcode);
+ }
+ charcode = FT_Get_Next_Char(at_size->face, charcode, &gindex);
+ }
+ return chars;
+ }
+#endif
+ if (at_size) {
+ const HashMap<int32_t, FontGlyph> &gl = at_size->glyph_map;
+ const int32_t *E = nullptr;
+ while ((E = gl.next(E))) {
+ chars += char32_t(*E);
+ }
+ }
+ return chars;
}
-bool TextServerFallback::font_has_outline(RID p_font) const {
- _THREAD_SAFE_METHOD_
- const FontDataFallback *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, false);
- return fd->has_outline();
+void TextServerFallback::font_render_range(RID p_font_rid, const Vector2i &p_size, char32_t p_start, char32_t p_end) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ for (char32_t i = p_start; i <= p_end; i++) {
+#ifdef MODULE_FREETYPE_ENABLED
+ if (fd->cache[size]->face) {
+ _ensure_glyph(fd, size, FT_Get_Char_Index(fd->cache[size]->face, i));
+ continue;
+ }
+#endif
+ _ensure_glyph(fd, size, (int32_t)i);
+ }
}
-float TextServerFallback::font_get_base_size(RID p_font) const {
- _THREAD_SAFE_METHOD_
- const FontDataFallback *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, 0.f);
- return fd->get_base_size();
+void TextServerFallback::font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ ERR_FAIL_COND(!_ensure_glyph(fd, size, p_index));
}
-bool TextServerFallback::font_is_language_supported(RID p_font, const String &p_language) const {
- _THREAD_SAFE_METHOD_
- const FontDataFallback *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, false);
- if (fd->lang_support_overrides.has(p_language)) {
- return fd->lang_support_overrides[p_language];
- } else {
- Vector<String> tags = p_language.replace("-", "_").split("_");
- if (tags.size() > 0) {
- if (fd->lang_support_overrides.has(tags[0])) {
- return fd->lang_support_overrides[tags[0]];
+void TextServerFallback::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ if (!_ensure_glyph(fd, size, p_index)) {
+ return; // // Invalid or non graphicl glyph, do not display errors, nothing to draw.
+ }
+
+ const FontGlyph &gl = fd->cache[size]->glyph_map[p_index];
+ if (gl.found) {
+ ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size());
+
+ 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)) {
+ modulate.r = modulate.g = modulate.b = 1.0;
+ }
+#endif
+ if (RenderingServer::get_singleton() != nullptr) {
+ RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid();
+ if (fd->msdf) {
+ Point2 cpos = p_pos;
+ cpos += gl.rect.position * (real_t)p_size / (real_t)fd->msdf_source_size;
+ Size2 csize = gl.rect.size * (real_t)p_size / (real_t)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);
+ } else {
+ Point2i cpos = p_pos;
+ cpos += gl.rect.position;
+ Size2i 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);
+ }
}
}
- return false;
}
}
-void TextServerFallback::font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) {
- _THREAD_SAFE_METHOD_
- FontDataFallback *fd = font_owner.getornull(p_font);
+void TextServerFallback::font_draw_glyph_outline(RID p_font_rid, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
- fd->lang_support_overrides[p_language] = p_supported;
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, Vector2i(p_size, p_outline_size));
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ if (!_ensure_glyph(fd, size, p_index)) {
+ return; // // Invalid or non graphicl glyph, do not display errors, nothing to draw.
+ }
+
+ const FontGlyph &gl = fd->cache[size]->glyph_map[p_index];
+ if (gl.found) {
+ ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size());
+
+ 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)) {
+ modulate.r = modulate.g = modulate.b = 1.0;
+ }
+#endif
+ if (RenderingServer::get_singleton() != nullptr) {
+ RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid();
+ if (fd->msdf) {
+ Point2 cpos = p_pos;
+ cpos += gl.rect.position * (real_t)p_size / (real_t)fd->msdf_source_size;
+ Size2 csize = gl.rect.size * (real_t)p_size / (real_t)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);
+ } else {
+ Point2i cpos = p_pos;
+ cpos += gl.rect.position;
+ Size2i 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);
+ }
+ }
+ }
+ }
}
-bool TextServerFallback::font_get_language_support_override(RID p_font, const String &p_language) {
- _THREAD_SAFE_METHOD_
- FontDataFallback *fd = font_owner.getornull(p_font);
+bool TextServerFallback::font_is_language_supported(RID p_font_rid, const String &p_language) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
- return fd->lang_support_overrides[p_language];
+
+ MutexLock lock(fd->mutex);
+ if (fd->language_support_overrides.has(p_language)) {
+ return fd->language_support_overrides[p_language];
+ } else {
+ return true;
+ }
}
-void TextServerFallback::font_remove_language_support_override(RID p_font, const String &p_language) {
- _THREAD_SAFE_METHOD_
- FontDataFallback *fd = font_owner.getornull(p_font);
+void TextServerFallback::font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
- fd->lang_support_overrides.erase(p_language);
+
+ MutexLock lock(fd->mutex);
+ fd->language_support_overrides[p_language] = p_supported;
}
-Vector<String> TextServerFallback::font_get_language_support_overrides(RID p_font) {
- _THREAD_SAFE_METHOD_
- FontDataFallback *fd = font_owner.getornull(p_font);
+bool TextServerFallback::font_get_language_support_override(RID p_font_rid, const String &p_language) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->language_support_overrides[p_language];
+}
+
+void TextServerFallback::font_remove_language_support_override(RID p_font_rid, const String &p_language) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ fd->language_support_overrides.erase(p_language);
+}
+
+Vector<String> TextServerFallback::font_get_language_support_overrides(RID p_font_rid) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector<String>());
- Vector<String> ret;
- for (Map<String, bool>::Element *E = fd->lang_support_overrides.front(); E; E = E->next()) {
- ret.push_back(E->key());
+
+ MutexLock lock(fd->mutex);
+ Vector<String> out;
+ for (const Map<String, bool>::Element *E = fd->language_support_overrides.front(); E; E = E->next()) {
+ out.push_back(E->key());
}
- return ret;
+ return out;
}
-bool TextServerFallback::font_is_script_supported(RID p_font, const String &p_script) const {
- _THREAD_SAFE_METHOD_
- const FontDataFallback *fd = font_owner.getornull(p_font);
+bool TextServerFallback::font_is_script_supported(RID p_font_rid, const String &p_script) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
if (fd->script_support_overrides.has(p_script)) {
return fd->script_support_overrides[p_script];
} else {
@@ -392,95 +1890,84 @@ bool TextServerFallback::font_is_script_supported(RID p_font, const String &p_sc
}
}
-void TextServerFallback::font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) {
- _THREAD_SAFE_METHOD_
- FontDataFallback *fd = font_owner.getornull(p_font);
+void TextServerFallback::font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
fd->script_support_overrides[p_script] = p_supported;
}
-bool TextServerFallback::font_get_script_support_override(RID p_font, const String &p_script) {
- _THREAD_SAFE_METHOD_
- FontDataFallback *fd = font_owner.getornull(p_font);
+bool TextServerFallback::font_get_script_support_override(RID p_font_rid, const String &p_script) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
return fd->script_support_overrides[p_script];
}
-void TextServerFallback::font_remove_script_support_override(RID p_font, const String &p_script) {
- _THREAD_SAFE_METHOD_
- FontDataFallback *fd = font_owner.getornull(p_font);
+void TextServerFallback::font_remove_script_support_override(RID p_font_rid, const String &p_script) {
+ FontDataFallback *fd = font_owner.getornull(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->script_support_overrides.erase(p_script);
}
-Vector<String> TextServerFallback::font_get_script_support_overrides(RID p_font) {
- _THREAD_SAFE_METHOD_
- FontDataFallback *fd = font_owner.getornull(p_font);
+Vector<String> TextServerFallback::font_get_script_support_overrides(RID p_font_rid) {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector<String>());
- Vector<String> ret;
- for (Map<String, bool>::Element *E = fd->script_support_overrides.front(); E; E = E->next()) {
- ret.push_back(E->key());
- }
- return ret;
-}
-uint32_t TextServerFallback::font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector) const {
- return (uint32_t)p_char;
-}
-
-Vector2 TextServerFallback::font_get_glyph_advance(RID p_font, uint32_t p_index, int p_size) const {
- _THREAD_SAFE_METHOD_
- const FontDataFallback *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, Vector2());
- return fd->get_advance(p_index, p_size);
+ MutexLock lock(fd->mutex);
+ Vector<String> out;
+ for (const Map<String, bool>::Element *E = fd->script_support_overrides.front(); E; E = E->next()) {
+ out.push_back(E->key());
+ }
+ return out;
}
-Vector2 TextServerFallback::font_get_glyph_kerning(RID p_font, uint32_t p_index_a, uint32_t p_index_b, int p_size) const {
- _THREAD_SAFE_METHOD_
- const FontDataFallback *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, Vector2());
- return fd->get_kerning(p_index_a, p_index_b, p_size);
+Dictionary TextServerFallback::font_supported_feature_list(RID p_font_rid) const {
+ return Dictionary();
}
-Vector2 TextServerFallback::font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
- _THREAD_SAFE_METHOD_
- const FontDataFallback *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, Vector2());
- return fd->draw_glyph(p_canvas, p_size, p_pos, p_index, p_color);
-}
+Dictionary TextServerFallback::font_supported_variation_list(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Dictionary());
-Vector2 TextServerFallback::font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
- _THREAD_SAFE_METHOD_
- const FontDataFallback *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, Vector2());
- return fd->draw_glyph_outline(p_canvas, p_size, p_outline_size, p_pos, p_index, p_color);
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Dictionary());
+ return fd->supported_varaitions;
}
-bool TextServerFallback::font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const {
- _THREAD_SAFE_METHOD_
- const FontDataFallback *fd = font_owner.getornull(p_font);
- ERR_FAIL_COND_V(!fd, false);
- return fd->get_glyph_contours(p_size, p_index, r_points, r_contours, r_orientation);
-}
-
-float TextServerFallback::font_get_oversampling() const {
+real_t TextServerFallback::font_get_global_oversampling() const {
return oversampling;
}
-void TextServerFallback::font_set_oversampling(float p_oversampling) {
+void TextServerFallback::font_set_global_oversampling(real_t p_oversampling) {
_THREAD_SAFE_METHOD_
if (oversampling != p_oversampling) {
oversampling = p_oversampling;
List<RID> fonts;
font_owner.get_owned_list(&fonts);
+ bool font_cleared = false;
for (const RID &E : fonts) {
- font_owner.getornull(E)->clear_cache();
+ if (!font_is_multichannel_signed_distance_field(E) && font_get_oversampling(E) <= 0) {
+ font_clear_size_cache(E);
+ font_cleared = true;
+ }
}
- }
-}
-Vector<String> TextServerFallback::get_system_fonts() const {
- return Vector<String>();
+ if (font_cleared) {
+ List<RID> text_bufs;
+ shaped_owner.get_owned_list(&text_bufs);
+ for (const RID &E : text_bufs) {
+ invalidate(shaped_owner.getornull(E));
+ }
+ }
+ }
}
/*************************************************************************/
@@ -533,10 +2020,10 @@ RID TextServerFallback::create_shaped_text(TextServer::Direction p_direction, Te
}
void TextServerFallback::shaped_text_clear(RID p_shaped) {
- _THREAD_SAFE_METHOD_
ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND(!sd);
+ MutexLock lock(sd->mutex);
sd->parent = RID();
sd->start = 0;
sd->end = 0;
@@ -557,9 +2044,10 @@ TextServer::Direction TextServerFallback::shaped_text_get_direction(RID p_shaped
}
void TextServerFallback::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) {
- _THREAD_SAFE_METHOD_
ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND(!sd);
+
+ MutexLock lock(sd->mutex);
if (sd->orientation != p_orientation) {
if (sd->parent != RID()) {
full_copy(sd);
@@ -574,15 +2062,17 @@ void TextServerFallback::shaped_text_set_bidi_override(RID p_shaped, const Vecto
}
TextServer::Orientation TextServerFallback::shaped_text_get_orientation(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, TextServer::ORIENTATION_HORIZONTAL);
+
+ MutexLock lock(sd->mutex);
return sd->orientation;
}
void TextServerFallback::shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) {
- _THREAD_SAFE_METHOD_
ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+
+ MutexLock lock(sd->mutex);
ERR_FAIL_COND(!sd);
if (sd->preserve_invalid != p_enabled) {
if (sd->parent != RID()) {
@@ -594,16 +2084,18 @@ void TextServerFallback::shaped_text_set_preserve_invalid(RID p_shaped, bool p_e
}
bool TextServerFallback::shaped_text_get_preserve_invalid(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
return sd->preserve_invalid;
}
void TextServerFallback::shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) {
- _THREAD_SAFE_METHOD_
ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND(!sd);
+
+ MutexLock lock(sd->mutex);
if (sd->preserve_control != p_enabled) {
if (sd->parent != RID()) {
full_copy(sd);
@@ -614,18 +2106,24 @@ void TextServerFallback::shaped_text_set_preserve_control(RID p_shaped, bool p_e
}
bool TextServerFallback::shaped_text_get_preserve_control(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
return sd->preserve_control;
}
bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
- _THREAD_SAFE_METHOD_
ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
ERR_FAIL_COND_V(p_size <= 0, false);
+ for (int i = 0; i < p_fonts.size(); i++) {
+ ERR_FAIL_COND_V(!font_owner.getornull(p_fonts[i]), false);
+ }
+
if (p_text.is_empty()) {
return true;
}
@@ -637,6 +2135,7 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te
ShapedTextData::Span span;
span.start = sd->text.length();
span.end = span.start + p_text.length();
+
// Pre-sort fonts, push fonts with the language support first.
Vector<RID> fonts_no_match;
int font_count = p_fonts.size();
@@ -662,9 +2161,10 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te
}
bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align, int p_length) {
- _THREAD_SAFE_METHOD_
ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
ERR_FAIL_COND_V(p_key == Variant(), false);
ERR_FAIL_COND_V(sd->objects.has(p_key), false);
@@ -692,9 +2192,10 @@ bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, con
}
bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align) {
- _THREAD_SAFE_METHOD_
ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
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;
@@ -706,8 +2207,6 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key,
sd->upos = 0;
sd->uthk = 0;
int sd_size = sd->glyphs.size();
- const FontDataFallback *fd = nullptr;
- RID prev_rid = RID();
for (int i = 0; i < sd_size; i++) {
Glyph gl = sd->glyphs[i];
@@ -731,17 +2230,13 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key,
sd->glyphs.write[i].advance = sd->objects[key].rect.size.y;
}
} else {
- if (prev_rid != gl.font_rid) {
- fd = font_owner.getornull(gl.font_rid);
- prev_rid = gl.font_rid;
- }
- if (fd != nullptr) {
+ if (gl.font_rid.is_valid()) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
- sd->ascent = MAX(sd->ascent, fd->get_ascent(gl.font_size));
- sd->descent = MAX(sd->descent, fd->get_descent(gl.font_size));
+ sd->ascent = MAX(sd->ascent, font_get_ascent(gl.font_rid, gl.font_size));
+ sd->descent = MAX(sd->descent, font_get_descent(gl.font_rid, gl.font_size));
} else {
- sd->ascent = MAX(sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
- sd->descent = MAX(sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).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));
@@ -760,8 +2255,8 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key,
}
// Align embedded objects to baseline.
- float full_ascent = sd->ascent;
- float full_descent = sd->descent;
+ real_t full_ascent = sd->ascent;
+ real_t full_descent = sd->descent;
for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
if ((E->get().pos >= sd->start) && (E->get().pos < sd->end)) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
@@ -830,9 +2325,10 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key,
}
RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_length) const {
- _THREAD_SAFE_METHOD_
const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, RID());
+
+ MutexLock lock(sd->mutex);
if (sd->parent != RID()) {
return shaped_text_substr(sd->parent, p_start, p_length);
}
@@ -886,14 +2382,13 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
new_sd->width += new_sd->objects[key].rect.size.y;
}
} else {
- const FontDataFallback *fd = font_owner.getornull(gl.font_rid);
- if (fd != nullptr) {
+ if (gl.font_rid.is_valid()) {
if (new_sd->orientation == ORIENTATION_HORIZONTAL) {
- new_sd->ascent = MAX(new_sd->ascent, fd->get_ascent(gl.font_size));
- new_sd->descent = MAX(new_sd->descent, fd->get_descent(gl.font_size));
+ new_sd->ascent = MAX(new_sd->ascent, font_get_ascent(gl.font_rid, gl.font_size));
+ new_sd->descent = MAX(new_sd->descent, font_get_descent(gl.font_rid, gl.font_size));
} else {
- new_sd->ascent = MAX(new_sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
- new_sd->descent = MAX(new_sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
+ new_sd->ascent = MAX(new_sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
+ new_sd->descent = MAX(new_sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
}
} else if (new_sd->preserve_invalid || (new_sd->preserve_control && is_control(gl.index))) {
// Glyph not found, replace with hex code box.
@@ -912,8 +2407,8 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
}
// Align embedded objects to baseline.
- float full_ascent = new_sd->ascent;
- float full_descent = new_sd->descent;
+ real_t full_ascent = new_sd->ascent;
+ real_t full_descent = new_sd->descent;
for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = new_sd->objects.front(); E; E = E->next()) {
if ((E->get().pos >= new_sd->start) && (E->get().pos < new_sd->end)) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
@@ -984,16 +2479,18 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
}
RID TextServerFallback::shaped_text_get_parent(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, RID());
+
+ MutexLock lock(sd->mutex);
return sd->parent;
}
-float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t /*JustificationFlag*/ p_jst_flags) {
- _THREAD_SAFE_METHOD_
+real_t TextServerFallback::shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t /*JustificationFlag*/ p_jst_flags) {
ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
}
@@ -1053,13 +2550,13 @@ float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width,
}
if ((space_count > 0) && ((p_jst_flags & JUSTIFICATION_WORD_BOUND) == JUSTIFICATION_WORD_BOUND)) {
- float delta_width_per_space = (p_width - sd->width) / space_count;
+ real_t delta_width_per_space = (p_width - sd->width) / space_count;
for (int i = start_pos; i <= end_pos; i++) {
Glyph &gl = sd->glyphs.write[i];
if (gl.count > 0) {
if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) {
- float old_adv = gl.advance;
- gl.advance = Math::round(MAX(gl.advance + delta_width_per_space, 0.05 * gl.font_size));
+ real_t old_adv = gl.advance;
+ gl.advance = MAX(gl.advance + delta_width_per_space, Math::round(0.1 * gl.font_size));
sd->width += (gl.advance - old_adv);
}
}
@@ -1069,10 +2566,11 @@ float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width,
return sd->width;
}
-float TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) {
- _THREAD_SAFE_METHOD_
+real_t TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<real_t> &p_tab_stops) {
ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
}
@@ -1081,7 +2579,7 @@ float TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<float
}
int tab_index = 0;
- float off = 0.f;
+ real_t off = 0.f;
int start, end, delta;
if (sd->para_direction == DIRECTION_LTR) {
@@ -1098,7 +2596,7 @@ float TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<float
for (int i = start; i != end; i += delta) {
if ((gl[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) {
- float tab_off = 0.f;
+ real_t tab_off = 0.f;
while (tab_off <= off) {
tab_off += p_tab_stops[tab_index];
tab_index++;
@@ -1106,7 +2604,7 @@ float TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<float
tab_index = 0;
}
}
- float old_adv = gl[i].advance;
+ real_t old_adv = gl[i].advance;
gl[i].advance = tab_off - off;
sd->width += gl[i].advance - old_adv;
off = 0;
@@ -1119,9 +2617,10 @@ float TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<float
}
bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) {
- _THREAD_SAFE_METHOD_
ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
shaped_text_shape(p_shaped);
}
@@ -1159,9 +2658,10 @@ bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) {
}
bool TextServerFallback::shaped_text_update_justification_ops(RID p_shaped) {
- _THREAD_SAFE_METHOD_
ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
shaped_text_shape(p_shaped);
}
@@ -1173,10 +2673,11 @@ bool TextServerFallback::shaped_text_update_justification_ops(RID p_shaped) {
return true;
}
-void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, float p_width, uint8_t p_trim_flags) {
- _THREAD_SAFE_METHOD_
+void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, real_t p_width, uint8_t p_trim_flags) {
ShapedTextData *sd = shaped_owner.getornull(p_shaped_line);
- ERR_FAIL_COND_MSG(!sd, "ShapedTextDataAdvanced invalid.");
+ ERR_FAIL_COND_MSG(!sd, "ShapedTextDataFallback invalid.");
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
shaped_text_shape(p_shaped_line);
}
@@ -1203,18 +2704,18 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl
int sd_size = sd->glyphs.size();
RID last_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
int last_gl_font_size = sd_glyphs[sd_size - 1].font_size;
- uint32_t dot_gl_idx = font_get_glyph_index(last_gl_font_rid, '.');
- Vector2 dot_adv = font_get_glyph_advance(last_gl_font_rid, dot_gl_idx, last_gl_font_size);
- uint32_t whitespace_gl_idx = font_get_glyph_index(last_gl_font_rid, ' ');
- Vector2 whitespace_adv = font_get_glyph_advance(last_gl_font_rid, whitespace_gl_idx, last_gl_font_size);
+ int32_t dot_gl_idx = font_get_glyph_index(last_gl_font_rid, '.', 0);
+ Vector2 dot_adv = font_get_glyph_advance(last_gl_font_rid, last_gl_font_size, dot_gl_idx);
+ int32_t whitespace_gl_idx = font_get_glyph_index(last_gl_font_rid, ' ', 0);
+ Vector2 whitespace_adv = font_get_glyph_advance(last_gl_font_rid, last_gl_font_size, whitespace_gl_idx);
int ellipsis_width = 0;
if (add_ellipsis) {
- ellipsis_width = 3 * dot_adv.x + font_get_spacing_glyph(last_gl_font_rid) + (cut_per_word ? whitespace_adv.x : 0);
+ ellipsis_width = 3 * dot_adv.x + font_get_spacing(last_gl_font_rid, last_gl_font_size, TextServer::SPACING_GLYPH) + (cut_per_word ? whitespace_adv.x : 0);
}
int ell_min_characters = 6;
- float width = sd->width;
+ real_t width = sd->width;
int trim_pos = 0;
int ellipsis_pos = (enforce_ellipsis) ? 0 : -1;
@@ -1289,16 +2790,18 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl
}
TextServer::TrimData TextServerFallback::shaped_text_get_trim_data(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
ShapedTextData *sd = shaped_owner.getornull(p_shaped);
- ERR_FAIL_COND_V_MSG(!sd, TrimData(), "ShapedTextDataAdvanced invalid.");
+ ERR_FAIL_COND_V_MSG(!sd, TrimData(), "ShapedTextDataFallback invalid.");
+
+ MutexLock lock(sd->mutex);
return sd->overrun_trim_data;
}
bool TextServerFallback::shaped_text_shape(RID p_shaped) {
- _THREAD_SAFE_METHOD_
ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
if (sd->valid) {
return true;
}
@@ -1347,14 +2850,12 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
} else {
// Text span.
for (int j = span.start; j < span.end; j++) {
- const FontDataFallback *fd = nullptr;
-
Glyph gl;
gl.start = j;
gl.end = j + 1;
gl.count = 1;
gl.font_size = span.font_size;
- gl.index = (uint32_t)sd->text[j]; // Use codepoint.
+ gl.index = (int32_t)sd->text[j]; // Use codepoint.
if (gl.index == 0x0009 || gl.index == 0x000b) {
gl.index = 0x0020;
}
@@ -1363,45 +2864,44 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
}
// Select first font which has character (font are already sorted by span language).
for (int k = 0; k < span.fonts.size(); k++) {
- fd = font_owner.getornull(span.fonts[k]);
- if (fd != nullptr && fd->has_char(gl.index)) {
+ if (font_has_char(span.fonts[k], gl.index)) {
gl.font_rid = span.fonts[k];
break;
}
}
- if (gl.font_rid != RID()) {
+ if (gl.font_rid.is_valid()) {
if (sd->text[j] != 0 && !is_linebreak(sd->text[j])) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
- gl.advance = fd->get_advance(gl.index, gl.font_size).x;
+ gl.advance = font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x;
gl.x_off = 0;
gl.y_off = 0;
- sd->ascent = MAX(sd->ascent, fd->get_ascent(gl.font_size));
- sd->descent = MAX(sd->descent, fd->get_descent(gl.font_size));
+ sd->ascent = MAX(sd->ascent, font_get_ascent(gl.font_rid, gl.font_size));
+ sd->descent = MAX(sd->descent, font_get_descent(gl.font_rid, gl.font_size));
} else {
- gl.advance = fd->get_advance(gl.index, gl.font_size).y;
- gl.x_off = -Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5);
- gl.y_off = fd->get_ascent(gl.font_size);
- sd->ascent = MAX(sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
- sd->descent = MAX(sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
+ gl.advance = font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).y;
+ gl.x_off = -Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5);
+ gl.y_off = font_get_ascent(gl.font_rid, gl.font_size);
+ 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));
}
}
- if (fd->get_spacing_space() && is_whitespace(sd->text[j])) {
- gl.advance += fd->get_spacing_space();
+ if (font_get_spacing(gl.font_rid, gl.font_size, TextServer::SPACING_SPACE) && is_whitespace(sd->text[j])) {
+ gl.advance += font_get_spacing(gl.font_rid, gl.font_size, TextServer::SPACING_SPACE);
} else {
- gl.advance += fd->get_spacing_glyph();
+ gl.advance += font_get_spacing(gl.font_rid, gl.font_size, TextServer::SPACING_GLYPH);
}
- sd->upos = MAX(sd->upos, fd->get_underline_position(gl.font_size));
- sd->uthk = MAX(sd->uthk, fd->get_underline_thickness(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));
// Add kerning to previous glyph.
if (sd->glyphs.size() > 0) {
Glyph &prev_gl = sd->glyphs.write[sd->glyphs.size() - 1];
if (prev_gl.font_rid == gl.font_rid && prev_gl.font_size == gl.font_size) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
- prev_gl.advance += fd->get_kerning(prev_gl.index, gl.index, gl.font_size).x;
+ prev_gl.advance += font_get_kerning(gl.font_rid, gl.font_size, Vector2i(prev_gl.index, gl.index)).x;
} else {
- prev_gl.advance += fd->get_kerning(prev_gl.index, gl.index, gl.font_size).y;
+ prev_gl.advance += font_get_kerning(gl.font_rid, gl.font_size, Vector2i(prev_gl.index, gl.index)).y;
}
}
}
@@ -1424,8 +2924,8 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
}
// Align embedded objects to baseline.
- float full_ascent = sd->ascent;
- float full_descent = sd->descent;
+ real_t full_ascent = sd->ascent;
+ real_t full_descent = sd->descent;
for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) {
@@ -1492,16 +2992,18 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
}
bool TextServerFallback::shaped_text_is_ready(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
return sd->valid;
}
Vector<TextServer::Glyph> TextServerFallback::shaped_text_get_glyphs(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, Vector<TextServer::Glyph>());
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
}
@@ -1509,16 +3011,18 @@ Vector<TextServer::Glyph> TextServerFallback::shaped_text_get_glyphs(RID p_shape
}
Vector2i TextServerFallback::shaped_text_get_range(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, Vector2i());
+
+ MutexLock lock(sd->mutex);
return Vector2(sd->start, sd->end);
}
Vector<TextServer::Glyph> TextServerFallback::shaped_text_sort_logical(RID p_shaped) {
- _THREAD_SAFE_METHOD_
const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, Vector<TextServer::Glyph>());
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
}
@@ -1527,10 +3031,11 @@ Vector<TextServer::Glyph> TextServerFallback::shaped_text_sort_logical(RID p_sha
}
Array TextServerFallback::shaped_text_get_objects(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
Array ret;
const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, ret);
+
+ MutexLock lock(sd->mutex);
for (const Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
ret.push_back(E->key());
}
@@ -1539,9 +3044,10 @@ Array TextServerFallback::shaped_text_get_objects(RID p_shaped) const {
}
Rect2 TextServerFallback::shaped_text_get_object_rect(RID p_shaped, Variant p_key) const {
- _THREAD_SAFE_METHOD_
const ShapedTextData *sd = shaped_owner.getornull(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<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
@@ -1550,9 +3056,10 @@ Rect2 TextServerFallback::shaped_text_get_object_rect(RID p_shaped, Variant p_ke
}
Size2 TextServerFallback::shaped_text_get_size(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, Size2());
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
}
@@ -1563,40 +3070,44 @@ Size2 TextServerFallback::shaped_text_get_size(RID p_shaped) const {
}
}
-float TextServerFallback::shaped_text_get_ascent(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
+real_t TextServerFallback::shaped_text_get_ascent(RID p_shaped) const {
const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
}
return sd->ascent;
}
-float TextServerFallback::shaped_text_get_descent(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
+real_t TextServerFallback::shaped_text_get_descent(RID p_shaped) const {
const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
}
return sd->descent;
}
-float TextServerFallback::shaped_text_get_width(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
+real_t TextServerFallback::shaped_text_get_width(RID p_shaped) const {
const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
}
return sd->width;
}
-float TextServerFallback::shaped_text_get_underline_position(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
+real_t TextServerFallback::shaped_text_get_underline_position(RID p_shaped) const {
const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
}
@@ -1604,10 +3115,11 @@ float TextServerFallback::shaped_text_get_underline_position(RID p_shaped) const
return sd->upos;
}
-float TextServerFallback::shaped_text_get_underline_thickness(RID p_shaped) const {
- _THREAD_SAFE_METHOD_
+real_t TextServerFallback::shaped_text_get_underline_thickness(RID p_shaped) const {
const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
+
+ MutexLock lock(sd->mutex);
if (!sd->valid) {
const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
}
@@ -1623,3 +3135,11 @@ TextServer *TextServerFallback::create_func(Error &r_error, void *p_user_data) {
void TextServerFallback::register_server() {
TextServerManager::register_create_function(interface_name, interface_features, create_func, nullptr);
}
+
+TextServerFallback::TextServerFallback(){};
+
+TextServerFallback::~TextServerFallback() {
+ if (library != nullptr) {
+ FT_Done_FreeType(library);
+ }
+};
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index d4cab2409a..fde75e7135 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/modules/text_server_fb/text_server_fb.h
@@ -39,22 +39,165 @@
#include "servers/text_server.h"
#include "core/templates/rid_owner.h"
-
+#include "core/templates/thread_work_pool.h"
#include "scene/resources/texture.h"
-#include "font_fb.h"
+#include "modules/modules_enabled.gen.h"
+
+#ifdef MODULE_FREETYPE_ENABLED
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_TRUETYPE_TABLES_H
+#include FT_STROKER_H
+#include FT_ADVANCES_H
+#include FT_MULTIPLE_MASTERS_H
+#include FT_BBOX_H
+#endif
class TextServerFallback : public TextServer {
GDCLASS(TextServerFallback, TextServer);
_THREAD_SAFE_CLASS_
- float oversampling = 1.f;
- mutable RID_PtrOwner<FontDataFallback> font_owner;
- mutable RID_PtrOwner<ShapedTextData> shaped_owner;
-
static String interface_name;
static uint32_t interface_features;
+ // Font cache data.
+
+#ifdef MODULE_FREETYPE_ENABLED
+ mutable FT_Library library = nullptr;
+#endif
+
+ const int rect_range = 2;
+
+ struct FontTexture {
+ Image::Format format;
+ PackedByteArray imgdata;
+ int texture_w = 0;
+ int texture_h = 0;
+ PackedInt32Array offsets;
+ Ref<ImageTexture> texture;
+ };
+
+ struct FontTexturePosition {
+ int index = 0;
+ int x = 0;
+ int y = 0;
+ };
+
+ struct FontGlyph {
+ bool found = false;
+ int texture_idx = -1;
+ Rect2 rect;
+ Rect2 uv_rect;
+ Vector2 advance;
+ };
+
+ struct FontDataForSizeFallback {
+ real_t ascent = 0.f;
+ real_t descent = 0.f;
+ real_t underline_position = 0.f;
+ real_t underline_thickness = 0.f;
+ real_t scale = 1.f;
+ real_t oversampling = 1.f;
+
+ int spacing_glyph = 0;
+ int spacing_space = 0;
+
+ Vector2i size;
+
+ Vector<FontTexture> textures;
+ HashMap<int32_t, FontGlyph> glyph_map;
+ Map<Vector2i, Vector2> kerning_map;
+
+#ifdef MODULE_FREETYPE_ENABLED
+ FT_Face face = nullptr;
+ FT_StreamRec stream;
+#endif
+
+ ~FontDataForSizeFallback() {
+#ifdef MODULE_FREETYPE_ENABLED
+ if (face != nullptr) {
+ FT_Done_Face(face);
+ }
+#endif
+ }
+ };
+
+ struct FontDataFallback {
+ Mutex mutex;
+
+ bool antialiased = true;
+ bool msdf = false;
+ int msdf_range = 14;
+ int msdf_source_size = 48;
+ int fixed_size = 0;
+ bool force_autohinter = false;
+ TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
+ Dictionary variation_coordinates;
+ real_t oversampling = 0.f;
+
+ Map<Vector2i, FontDataForSizeFallback *> cache;
+
+ bool face_init = false;
+ Dictionary supported_varaitions;
+
+ // Language/script support override.
+ Map<String, bool> language_support_overrides;
+ Map<String, bool> script_support_overrides;
+
+ PackedByteArray data;
+ const uint8_t *data_ptr;
+ size_t data_size;
+
+ mutable ThreadWorkPool work_pool;
+
+ ~FontDataFallback() {
+ work_pool.finish();
+ for (const Map<Vector2i, FontDataForSizeFallback *>::Element *E = cache.front(); E; E = E->next()) {
+ memdelete(E->get());
+ }
+ cache.clear();
+ }
+ };
+
+ _FORCE_INLINE_ FontTexturePosition find_texture_pos_for_glyph(FontDataForSizeFallback *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) const;
+#ifdef MODULE_MSDFGEN_ENABLED
+ _FORCE_INLINE_ FontGlyph rasterize_msdf(FontDataFallback *p_font_data, FontDataForSizeFallback *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(FontDataForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const;
+#endif
+ _FORCE_INLINE_ bool _ensure_glyph(FontDataFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const;
+ _FORCE_INLINE_ bool _ensure_cache_for_size(FontDataFallback *p_font_data, const Vector2i &p_size) const;
+ _FORCE_INLINE_ void _font_clear_cache(FontDataFallback *p_font_data);
+ void _generateMTSDF_threaded(uint32_t y, void *p_td) const;
+
+ _FORCE_INLINE_ Vector2i _get_size(const FontDataFallback *p_font_data, int p_size) const {
+ if (p_font_data->msdf) {
+ return Vector2i(p_font_data->msdf_source_size, 0);
+ } else if (p_font_data->fixed_size > 0) {
+ return Vector2i(p_font_data->fixed_size, 0);
+ } else {
+ return Vector2i(p_size, 0);
+ }
+ }
+
+ _FORCE_INLINE_ Vector2i _get_size_outline(const FontDataFallback *p_font_data, const Vector2i &p_size) const {
+ if (p_font_data->msdf) {
+ return Vector2i(p_font_data->msdf_source_size, 0);
+ } else if (p_font_data->fixed_size > 0) {
+ return Vector2i(p_font_data->fixed_size, MIN(p_size.y, 1));
+ } else {
+ return p_size;
+ }
+ }
+
+ // Common data.
+
+ real_t oversampling = 1.f;
+ mutable RID_PtrOwner<FontDataFallback> font_owner;
+ mutable RID_PtrOwner<ShapedTextData> shaped_owner;
+
protected:
static void _bind_methods(){};
@@ -77,72 +220,130 @@ public:
virtual bool is_locale_right_to_left(const String &p_locale) override;
+ virtual int32_t name_to_tag(const String &p_name) const override;
+ virtual String tag_to_name(int32_t p_tag) const override;
+
/* Font interface */
- virtual RID create_font_system(const String &p_name, int p_base_size = 16) override;
- virtual RID create_font_resource(const String &p_filename, int p_base_size = 16) override;
- virtual RID create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16) override;
- virtual RID create_font_bitmap(float p_height, float p_ascent, int p_base_size = 16) override;
+ virtual RID create_font() override;
+
+ virtual void font_set_data(RID p_font_rid, const PackedByteArray &p_data) override;
+ virtual void font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) override;
+
+ virtual void font_set_antialiased(RID p_font_rid, bool p_antialiased) override;
+ virtual bool font_is_antialiased(RID p_font_rid) const override;
+
+ virtual void font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) override;
+ virtual bool font_is_multichannel_signed_distance_field(RID p_font_rid) const override;
+
+ virtual void font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) override;
+ virtual int font_get_msdf_pixel_range(RID p_font_rid) const override;
+
+ virtual void font_set_msdf_size(RID p_font_rid, int p_msdf_size) override;
+ virtual int font_get_msdf_size(RID p_font_rid) const override;
+
+ virtual void font_set_fixed_size(RID p_font_rid, int p_fixed_size) override;
+ virtual int font_get_fixed_size(RID p_font_rid) const override;
+
+ virtual void font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) override;
+ virtual bool font_is_force_autohinter(RID p_font_rid) const override;
+
+ virtual void font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) override;
+ virtual TextServer::Hinting font_get_hinting(RID p_font_rid) const override;
+
+ virtual void font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) override;
+ virtual Dictionary font_get_variation_coordinates(RID p_font_rid) const override;
+
+ virtual void font_set_oversampling(RID p_font_rid, real_t p_oversampling) override;
+ virtual real_t font_get_oversampling(RID p_font_rid) const override;
+
+ virtual Array font_get_size_cache_list(RID p_font_rid) const override;
+ virtual void font_clear_size_cache(RID p_font_rid) override;
+ virtual void font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) override;
+
+ virtual void font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) override;
+ virtual real_t font_get_ascent(RID p_font_rid, int p_size) const override;
+
+ virtual void font_set_descent(RID p_font_rid, int p_size, real_t p_descent) override;
+ virtual real_t font_get_descent(RID p_font_rid, int p_size) const override;
+
+ virtual void font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) override;
+ virtual real_t font_get_underline_position(RID p_font_rid, int p_size) const override;
+
+ virtual void font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) override;
+ virtual real_t font_get_underline_thickness(RID p_font_rid, int p_size) const override;
+
+ virtual void font_set_scale(RID p_font_rid, int p_size, real_t p_scale) override;
+ virtual real_t font_get_scale(RID p_font_rid, int p_size) const override;
+
+ virtual void font_set_spacing(RID p_font_rid, int p_size, SpacingType p_spacing, int p_value) override;
+ virtual int font_get_spacing(RID p_font_rid, int p_size, SpacingType p_spacing) const override;
+
+ virtual int font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const override;
+ virtual void font_clear_textures(RID p_font_rid, const Vector2i &p_size) override;
+ virtual void font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) override;
+
+ virtual void font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) override;
+ virtual Ref<Image> font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const override;
- virtual void font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) override;
- virtual void font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override;
- virtual void font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) override;
+ virtual void font_set_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) override;
+ virtual PackedInt32Array font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const override;
- virtual float font_get_height(RID p_font, int p_size) const override;
- virtual float font_get_ascent(RID p_font, int p_size) const override;
- virtual float font_get_descent(RID p_font, int p_size) const override;
+ virtual Array font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const override;
+ virtual void font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) override;
+ virtual void font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) override;
- virtual float font_get_underline_position(RID p_font, int p_size) const override;
- virtual float font_get_underline_thickness(RID p_font, int p_size) const override;
+ virtual Vector2 font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const override;
+ virtual void font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) override;
- virtual int font_get_spacing_space(RID p_font) const override;
- virtual void font_set_spacing_space(RID p_font, int p_value) override;
+ virtual Vector2 font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override;
+ virtual void font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) override;
- virtual int font_get_spacing_glyph(RID p_font) const override;
- virtual void font_set_spacing_glyph(RID p_font, int p_value) override;
+ virtual Vector2 font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override;
+ virtual void font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) override;
- virtual void font_set_antialiased(RID p_font, bool p_antialiased) override;
- virtual bool font_get_antialiased(RID p_font) const override;
+ virtual Rect2 font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override;
+ virtual void font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) override;
- virtual void font_set_hinting(RID p_font, Hinting p_hinting) override;
- virtual Hinting font_get_hinting(RID p_font) const override;
+ virtual int font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override;
+ virtual void font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) override;
- virtual void font_set_force_autohinter(RID p_font, bool p_enabeld) override;
- virtual bool font_get_force_autohinter(RID p_font) const override;
+ virtual bool font_get_glyph_contours(RID p_font, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override;
- virtual bool font_has_char(RID p_font, char32_t p_char) const override;
- virtual String font_get_supported_chars(RID p_font) const override;
+ virtual Array font_get_kerning_list(RID p_font_rid, int p_size) const override;
+ virtual void font_clear_kerning_map(RID p_font_rid, int p_size) override;
+ virtual void font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) override;
- virtual void font_set_distance_field_hint(RID p_font, bool p_distance_field) override;
- virtual bool font_get_distance_field_hint(RID p_font) const override;
+ virtual void font_set_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) override;
+ virtual Vector2 font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const override;
- virtual bool font_has_outline(RID p_font) const override;
- virtual float font_get_base_size(RID p_font) const override;
+ virtual int32_t font_get_glyph_index(RID p_font_rid, int p_size, char32_t p_char, char32_t p_variation_selector = 0) const override;
- virtual bool font_is_language_supported(RID p_font, const String &p_language) const override;
- virtual void font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) override;
- virtual bool font_get_language_support_override(RID p_font, const String &p_language) override;
- virtual void font_remove_language_support_override(RID p_font, const String &p_language) override;
- Vector<String> font_get_language_support_overrides(RID p_font) override;
+ virtual bool font_has_char(RID p_font_rid, char32_t p_char) const override;
+ virtual String font_get_supported_chars(RID p_font_rid) const override;
- virtual bool font_is_script_supported(RID p_font, const String &p_script) const override;
- virtual void font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) override;
- virtual bool font_get_script_support_override(RID p_font, const String &p_script) override;
- virtual void font_remove_script_support_override(RID p_font, const String &p_script) override;
- Vector<String> font_get_script_support_overrides(RID p_font) override;
+ virtual void font_render_range(RID p_font, const Vector2i &p_size, char32_t p_start, char32_t p_end) override;
+ virtual void font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) override;
- virtual uint32_t font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector = 0x0000) const override;
- virtual Vector2 font_get_glyph_advance(RID p_font, uint32_t p_index, int p_size) const override;
- virtual Vector2 font_get_glyph_kerning(RID p_font, uint32_t p_index_a, uint32_t p_index_b, int p_size) const override;
+ virtual void font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
+ virtual void font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
- virtual Vector2 font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
- virtual Vector2 font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
+ virtual bool font_is_language_supported(RID p_font_rid, const String &p_language) const override;
+ virtual void font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) override;
+ virtual bool font_get_language_support_override(RID p_font_rid, const String &p_language) override;
+ virtual void font_remove_language_support_override(RID p_font_rid, const String &p_language) override;
+ virtual Vector<String> font_get_language_support_overrides(RID p_font_rid) override;
- virtual bool font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override;
+ virtual bool font_is_script_supported(RID p_font_rid, const String &p_script) const override;
+ virtual void font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) override;
+ virtual bool font_get_script_support_override(RID p_font_rid, const String &p_script) override;
+ virtual void font_remove_script_support_override(RID p_font_rid, const String &p_script) override;
+ virtual Vector<String> font_get_script_support_overrides(RID p_font_rid) override;
- virtual float font_get_oversampling() const override;
- virtual void font_set_oversampling(float p_oversampling) override;
+ virtual Dictionary font_supported_feature_list(RID p_font_rid) const override;
+ virtual Dictionary font_supported_variation_list(RID p_font_rid) const override;
- virtual Vector<String> get_system_fonts() const override;
+ virtual real_t font_get_global_oversampling() const override;
+ virtual void font_set_global_oversampling(real_t p_oversampling) override;
/* Shaped text buffer interface */
@@ -171,14 +372,14 @@ public:
virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override;
virtual RID shaped_text_get_parent(RID p_shaped) const override;
- virtual float shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
- virtual float shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) override;
+ virtual real_t shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
+ virtual real_t shaped_text_tab_align(RID p_shaped, const Vector<real_t> &p_tab_stops) override;
virtual bool shaped_text_shape(RID p_shaped) override;
virtual bool shaped_text_update_breaks(RID p_shaped) override;
virtual bool shaped_text_update_justification_ops(RID p_shaped) override;
- virtual void shaped_text_overrun_trim_to_width(RID p_shaped, float p_width, uint8_t p_trim_flags) override;
+ virtual void shaped_text_overrun_trim_to_width(RID p_shaped, real_t p_width, uint8_t p_trim_flags) override;
virtual TrimData shaped_text_get_trim_data(RID p_shaped) const override;
virtual bool shaped_text_is_ready(RID p_shaped) const override;
@@ -193,17 +394,17 @@ public:
virtual Rect2 shaped_text_get_object_rect(RID p_shaped, Variant p_key) const override;
virtual Size2 shaped_text_get_size(RID p_shaped) const override;
- virtual float shaped_text_get_ascent(RID p_shaped) const override;
- virtual float shaped_text_get_descent(RID p_shaped) const override;
- virtual float shaped_text_get_width(RID p_shaped) const override;
- virtual float shaped_text_get_underline_position(RID p_shaped) const override;
- virtual float shaped_text_get_underline_thickness(RID p_shaped) const override;
+ virtual real_t shaped_text_get_ascent(RID p_shaped) const override;
+ virtual real_t shaped_text_get_descent(RID p_shaped) const override;
+ virtual real_t shaped_text_get_width(RID p_shaped) const override;
+ virtual real_t shaped_text_get_underline_position(RID p_shaped) const override;
+ virtual real_t shaped_text_get_underline_thickness(RID p_shaped) const override;
static TextServer *create_func(Error &r_error, void *p_user_data);
static void register_server();
- TextServerFallback(){};
- ~TextServerFallback(){};
+ TextServerFallback();
+ ~TextServerFallback();
};
#endif // TEXT_SERVER_FALLBACK_H
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index 50db1fc3ce..3f87003423 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -66,11 +66,11 @@ bool Label::is_uppercase() const {
int Label::get_line_height(int p_line) const {
Ref<Font> font = get_theme_font(SNAME("font"));
if (p_line >= 0 && p_line < lines_rid.size()) {
- return TS->shaped_text_get_size(lines_rid[p_line]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM);
+ return TS->shaped_text_get_size(lines_rid[p_line]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM);
} else if (lines_rid.size() > 0) {
int h = 0;
for (int i = 0; i < lines_rid.size(); i++) {
- h = MAX(h, TS->shaped_text_get_size(lines_rid[i]).y) + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM);
+ h = MAX(h, TS->shaped_text_get_size(lines_rid[i]).y) + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM);
}
return h;
} else {
@@ -89,7 +89,10 @@ void Label::_shape() {
} else {
TS->shaped_text_set_direction(text_rid, (TextServer::Direction)text_direction);
}
- TS->shaped_text_add_string(text_rid, (uppercase) ? xl_text.to_upper() : xl_text, get_theme_font(SNAME("font"))->get_rids(), get_theme_font_size(SNAME("font_size")), opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
+ const Ref<Font> &font = get_theme_font(SNAME("font"));
+ int font_size = get_theme_font_size(SNAME("font_size"));
+ ERR_FAIL_COND(font.is_null());
+ TS->shaped_text_add_string(text_rid, (uppercase) ? xl_text.to_upper() : xl_text, font->get_rids(), font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, xl_text));
dirty = false;
lines_dirty = true;
@@ -217,7 +220,7 @@ void Label::_update_visible() {
minsize.height = 0;
int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped);
for (int64_t i = lines_skipped; i < last_line; i++) {
- minsize.height += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM) + line_spacing;
+ minsize.height += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing;
if (minsize.height > (get_size().height - style->get_minimum_size().height + line_spacing)) {
break;
}
@@ -286,7 +289,7 @@ void Label::_notification(int p_what) {
// Get number of lines to fit to the height.
for (int64_t i = lines_skipped; i < lines_rid.size(); i++) {
- total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM) + line_spacing;
+ total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing;
if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) {
break;
}
@@ -302,7 +305,7 @@ void Label::_notification(int p_what) {
// Get real total height.
total_h = 0;
for (int64_t i = lines_skipped; i < last_line; i++) {
- total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM) + line_spacing;
+ total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing;
}
total_h += style->get_margin(SIDE_TOP) + style->get_margin(SIDE_BOTTOM);
@@ -357,7 +360,7 @@ void Label::_notification(int p_what) {
for (int i = lines_skipped; i < last_line; i++) {
Size2 line_size = TS->shaped_text_get_size(lines_rid[i]);
ofs.x = 0;
- ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + font->get_spacing(Font::SPACING_TOP);
+ ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + font->get_spacing(TextServer::SPACING_TOP);
switch (align) {
case ALIGN_FILL:
if (rtl && autowrap_mode != AUTOWRAP_OFF) {
@@ -433,7 +436,7 @@ void Label::_notification(int p_what) {
}
}
}
- ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing + font->get_spacing(Font::SPACING_BOTTOM);
+ ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing + font->get_spacing(TextServer::SPACING_BOTTOM);
}
}
@@ -455,7 +458,7 @@ Size2 Label::get_minimum_size() const {
Size2 min_size = minsize;
Ref<Font> font = get_theme_font(SNAME("font"));
- min_size.height = MAX(min_size.height, font->get_height(get_theme_font_size(SNAME("font_size"))) + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM));
+ min_size.height = MAX(min_size.height, font->get_height(get_theme_font_size(SNAME("font_size"))) + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM));
Size2 min_style = get_theme_stylebox(SNAME("normal"))->get_minimum_size();
if (autowrap_mode != AUTOWRAP_OFF) {
@@ -486,7 +489,7 @@ int Label::get_visible_line_count() const {
int lines_visible = 0;
float total_h = 0.0;
for (int64_t i = lines_skipped; i < lines_rid.size(); i++) {
- total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM) + line_spacing;
+ total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing;
if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) {
break;
}
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 5e9c2d891f..d524e78caa 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -641,7 +641,7 @@ void LineEdit::_notification(int p_what) {
int x_ofs = 0;
bool using_placeholder = text.is_empty() && ime_text.is_empty();
float text_width = TS->shaped_text_get_size(text_rid).x;
- float text_height = TS->shaped_text_get_size(text_rid).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM);
+ float text_height = TS->shaped_text_get_size(text_rid).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM);
switch (align) {
case ALIGN_FILL:
@@ -1531,7 +1531,7 @@ Size2 LineEdit::get_minimum_size() const {
min_size.width = MAX(min_size.width, full_width + em_space_size);
}
- min_size.height = MAX(TS->shaped_text_get_size(text_rid).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM), font->get_height(font_size));
+ min_size.height = MAX(TS->shaped_text_get_size(text_rid).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM), font->get_height(font_size));
// Take icons into account.
bool using_placeholder = text.is_empty() && ime_text.is_empty();
@@ -1941,6 +1941,7 @@ void LineEdit::_shape() {
const Ref<Font> &font = get_theme_font(SNAME("font"));
int font_size = get_theme_font_size(SNAME("font_size"));
+ ERR_FAIL_COND(font.is_null());
TS->shaped_text_add_string(text_rid, t, font->get_rids(), font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, t));
diff --git a/scene/gui/rich_text_effect.h b/scene/gui/rich_text_effect.h
index e674b2f62f..f5506542bb 100644
--- a/scene/gui/rich_text_effect.h
+++ b/scene/gui/rich_text_effect.h
@@ -49,7 +49,7 @@ public:
Color color;
double elapsed_time = 0.0f;
Dictionary environment;
- uint32_t glpyh_index = 0;
+ uint32_t glyph_index = 0;
RID font;
CharFXTransform();
@@ -68,8 +68,8 @@ public:
Color get_color() { return color; }
void set_color(Color p_color) { color = p_color; }
- uint32_t get_glyph_index() const { return glpyh_index; };
- void set_glyph_index(uint32_t p_glpyh_index) { glpyh_index = p_glpyh_index; };
+ uint32_t get_glyph_index() const { return glyph_index; };
+ void set_glyph_index(uint32_t p_glyph_index) { glyph_index = p_glyph_index; };
RID get_font() const { return font; };
void set_font(RID p_font) { font = p_font; };
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 21a87d4e76..b7519af4aa 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -874,7 +874,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
charfx->visibility = visible;
charfx->outline = true;
charfx->font = frid;
- charfx->glpyh_index = gl;
+ charfx->glyph_index = gl;
charfx->offset = fx_offset;
charfx->color = font_color;
@@ -884,7 +884,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
fx_offset += charfx->offset;
font_color = charfx->color;
frid = charfx->font;
- gl = charfx->glpyh_index;
+ gl = charfx->glyph_index;
visible &= charfx->visibility;
}
} else if (item_fx->type == ITEM_SHAKE) {
@@ -1026,7 +1026,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
charfx->visibility = visible;
charfx->outline = false;
charfx->font = frid;
- charfx->glpyh_index = gl;
+ charfx->glyph_index = gl;
charfx->offset = fx_offset;
charfx->color = font_color;
@@ -1036,7 +1036,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
fx_offset += charfx->offset;
font_color = charfx->color;
frid = charfx->font;
- gl = charfx->glpyh_index;
+ gl = charfx->glyph_index;
visible &= charfx->visibility;
}
} else if (item_fx->type == ITEM_SHAKE) {
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 61dfec6abe..a731f80741 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -888,7 +888,7 @@ void TextEdit::_notification(int p_what) {
// Draw line.
RID rid = ldata->get_line_rid(line_wrap_index);
- float text_height = TS->shaped_text_get_size(rid).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM);
+ float text_height = TS->shaped_text_get_size(rid).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM);
if (rtl) {
char_margin = size.width - char_margin - TS->shaped_text_get_size(rid).x;
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 5f07f5216a..bf6709c4dc 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -889,11 +889,22 @@ void TreeItem::set_custom_font(int p_column, const Ref<Font> &p_font) {
ERR_FAIL_INDEX(p_column, cells.size());
cells.write[p_column].custom_font = p_font;
}
+
Ref<Font> TreeItem::get_custom_font(int p_column) const {
ERR_FAIL_INDEX_V(p_column, cells.size(), Ref<Font>());
return cells[p_column].custom_font;
}
+void TreeItem::set_custom_font_size(int p_column, int p_font_size) {
+ ERR_FAIL_INDEX(p_column, cells.size());
+ cells.write[p_column].custom_font_size = p_font_size;
+}
+
+int TreeItem::get_custom_font_size(int p_column) const {
+ ERR_FAIL_INDEX_V(p_column, cells.size(), -1);
+ return cells[p_column].custom_font_size;
+}
+
void TreeItem::set_tooltip(int p_column, const String &p_tooltip) {
ERR_FAIL_INDEX(p_column, cells.size());
cells.write[p_column].tooltip = p_tooltip;
@@ -1132,6 +1143,9 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_custom_font", "column", "font"), &TreeItem::set_custom_font);
ClassDB::bind_method(D_METHOD("get_custom_font", "column"), &TreeItem::get_custom_font);
+ ClassDB::bind_method(D_METHOD("set_custom_font_size", "column", "font_size"), &TreeItem::set_custom_font_size);
+ ClassDB::bind_method(D_METHOD("get_custom_font_size", "column"), &TreeItem::get_custom_font_size);
+
ClassDB::bind_method(D_METHOD("set_custom_bg_color", "column", "color", "just_outline"), &TreeItem::set_custom_bg_color, DEFVAL(false));
ClassDB::bind_method(D_METHOD("clear_custom_bg_color", "column"), &TreeItem::clear_custom_bg_color);
ClassDB::bind_method(D_METHOD("get_custom_bg_color", "column"), &TreeItem::get_custom_bg_color);
@@ -1507,7 +1521,14 @@ void Tree::update_item_cell(TreeItem *p_item, int p_col) {
} else {
font = cache.font;
}
- p_item->cells.write[p_col].text_buf->add_string(valtext, font, cache.font_size, p_item->cells[p_col].opentype_features, (p_item->cells[p_col].language != "") ? p_item->cells[p_col].language : TranslationServer::get_singleton()->get_tool_locale());
+
+ int font_size;
+ if (p_item->cells[p_col].custom_font_size > 0) {
+ font_size = p_item->cells[p_col].custom_font_size;
+ } else {
+ font_size = cache.font_size;
+ }
+ p_item->cells.write[p_col].text_buf->add_string(valtext, font, font_size, p_item->cells[p_col].opentype_features, (p_item->cells[p_col].language != "") ? p_item->cells[p_col].language : TranslationServer::get_singleton()->get_tool_locale());
TS->shaped_text_set_bidi_override(p_item->cells[p_col].text_buf->get_rid(), structured_text_parser(p_item->cells[p_col].st_parser, p_item->cells[p_col].st_args, valtext));
p_item->cells.write[p_col].dirty = false;
}
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index cce8b527db..8b7ddc3faf 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -114,6 +114,7 @@ private:
Vector<Button> buttons;
Ref<Font> custom_font;
+ int custom_font_size = -1;
Cell() {
text_buf.instantiate();
@@ -299,6 +300,9 @@ public:
void set_custom_font(int p_column, const Ref<Font> &p_font);
Ref<Font> get_custom_font(int p_column) const;
+ void set_custom_font_size(int p_column, int p_font_size);
+ int get_custom_font_size(int p_column) const;
+
void set_custom_bg_color(int p_column, const Color &p_color, bool p_bg_outline = false);
void clear_custom_bg_color(int p_column);
Color get_custom_bg_color(int p_column) const;
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index f329332725..64b169b1fb 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -571,6 +571,12 @@ void CanvasItem::draw_texture_rect_region(const Ref<Texture2D> &p_texture, const
p_texture->draw_rect_region(canvas_item, p_rect, p_src_rect, p_modulate, p_transpose, p_clip_uv);
}
+void CanvasItem::draw_msdf_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, double p_outline, double p_pixel_range) {
+ ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
+ ERR_FAIL_COND(p_texture.is_null());
+ RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(canvas_item, p_rect, p_texture->get_rid(), p_src_rect, p_modulate, p_outline, p_pixel_range);
+}
+
void CanvasItem::draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p_rect) {
ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
@@ -881,6 +887,7 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("draw_texture", "texture", "position", "modulate"), &CanvasItem::draw_texture, DEFVAL(Color(1, 1, 1, 1)));
ClassDB::bind_method(D_METHOD("draw_texture_rect", "texture", "rect", "tile", "modulate", "transpose"), &CanvasItem::draw_texture_rect, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(false));
ClassDB::bind_method(D_METHOD("draw_texture_rect_region", "texture", "rect", "src_rect", "modulate", "transpose", "clip_uv"), &CanvasItem::draw_texture_rect_region, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(false), DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("draw_msdf_texture_rect_region", "texture", "rect", "src_rect", "modulate", "outline", "pixel_range"), &CanvasItem::draw_msdf_texture_rect_region, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(0.0), DEFVAL(4.0));
ClassDB::bind_method(D_METHOD("draw_style_box", "style_box", "rect"), &CanvasItem::draw_style_box);
ClassDB::bind_method(D_METHOD("draw_primitive", "points", "colors", "uvs", "texture", "width"), &CanvasItem::draw_primitive, DEFVAL(Ref<Texture2D>()), DEFVAL(1.0));
ClassDB::bind_method(D_METHOD("draw_polygon", "points", "colors", "uvs", "texture"), &CanvasItem::draw_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref<Texture2D>()));
diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h
index a591cab485..01ed47d4dc 100644
--- a/scene/main/canvas_item.h
+++ b/scene/main/canvas_item.h
@@ -226,6 +226,7 @@ public:
void draw_texture(const Ref<Texture2D> &p_texture, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1, 1));
void draw_texture_rect(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false);
void draw_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = false);
+ void draw_msdf_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), double p_outline = 0.0, double p_pixel_range = 4.0);
void draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p_rect);
void draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture = Ref<Texture2D>(), real_t p_width = 1);
void draw_polygon(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture2D> p_texture = Ref<Texture2D>());
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index b9c0ae5a4a..1fcfce2ea9 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -659,8 +659,8 @@ void Window::_update_viewport_size() {
if (!use_font_oversampling) {
font_oversampling = 1.0;
}
- if (TS->font_get_oversampling() != font_oversampling) {
- TS->font_set_oversampling(font_oversampling);
+ if (TS->font_get_global_oversampling() != font_oversampling) {
+ TS->font_set_global_oversampling(font_oversampling);
}
}
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 2cea3c2337..9efb65cc4b 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -247,12 +247,6 @@
static Ref<ResourceFormatSaverText> resource_saver_text;
static Ref<ResourceFormatLoaderText> resource_loader_text;
-static Ref<ResourceFormatLoaderFont> resource_loader_font;
-
-#ifndef DISABLE_DEPRECATED
-static Ref<ResourceFormatLoaderCompatFont> resource_loader_compat_font;
-#endif /* DISABLE_DEPRECATED */
-
static Ref<ResourceFormatLoaderStreamTexture2D> resource_loader_stream_texture;
static Ref<ResourceFormatLoaderStreamTextureLayered> resource_loader_texture_layered;
static Ref<ResourceFormatLoaderStreamTexture3D> resource_loader_texture_3d;
@@ -267,14 +261,6 @@ void register_scene_types() {
Node::init_node_hrcr();
- resource_loader_font.instantiate();
- ResourceLoader::add_resource_format_loader(resource_loader_font);
-
-#ifndef DISABLE_DEPRECATED
- resource_loader_compat_font.instantiate();
- ResourceLoader::add_resource_format_loader(resource_loader_compat_font);
-#endif /* DISABLE_DEPRECATED */
-
resource_loader_stream_texture.instantiate();
ResourceLoader::add_resource_format_loader(resource_loader_stream_texture);
@@ -1065,14 +1051,6 @@ void unregister_scene_types() {
SceneDebugger::deinitialize();
clear_default_theme();
- ResourceLoader::remove_resource_format_loader(resource_loader_font);
- resource_loader_font.unref();
-
-#ifndef DISABLE_DEPRECATED
- ResourceLoader::remove_resource_format_loader(resource_loader_compat_font);
- resource_loader_compat_font.unref();
-#endif /* DISABLE_DEPRECATED */
-
ResourceLoader::remove_resource_format_loader(resource_loader_texture_layered);
resource_loader_texture_layered.unref();
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index dd7b348498..4845c556c6 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -1020,8 +1020,9 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) {
Ref<FontData> dynamic_font_data;
dynamic_font_data.instantiate();
- dynamic_font_data->load_memory(_font_OpenSans_SemiBold, _font_OpenSans_SemiBold_size, "ttf", default_font_size);
+ dynamic_font_data->set_data_ptr(_font_OpenSans_SemiBold, _font_OpenSans_SemiBold_size);
dynamic_font->add_data(dynamic_font_data);
+ dynamic_font->set_base_size(default_font_size);
default_font = dynamic_font;
}
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index 032171847d..7af8e900fd 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -36,54 +36,129 @@
#include "scene/resources/text_line.h"
#include "scene/resources/text_paragraph.h"
-void FontData::_bind_methods() {
- ClassDB::bind_method(D_METHOD("load_resource", "filename", "base_size"), &FontData::load_resource, DEFVAL(16));
- ClassDB::bind_method(D_METHOD("load_memory", "data", "type", "base_size"), &FontData::_load_memory, DEFVAL(16));
- ClassDB::bind_method(D_METHOD("new_bitmap", "height", "ascent", "base_size"), &FontData::new_bitmap);
-
- ClassDB::bind_method(D_METHOD("bitmap_add_texture", "texture"), &FontData::bitmap_add_texture);
- ClassDB::bind_method(D_METHOD("bitmap_add_char", "char", "texture_idx", "rect", "align", "advance"), &FontData::bitmap_add_char);
- ClassDB::bind_method(D_METHOD("bitmap_add_kerning_pair", "A", "B", "kerning"), &FontData::bitmap_add_kerning_pair);
+_FORCE_INLINE_ void FontData::_clear_cache() {
+ for (int i = 0; i < cache.size(); i++) {
+ if (cache[i].is_valid()) {
+ TS->free(cache[i]);
+ cache.write[i] = RID();
+ }
+ }
+}
- ClassDB::bind_method(D_METHOD("set_data_path", "path"), &FontData::set_data_path);
- ClassDB::bind_method(D_METHOD("get_data_path"), &FontData::get_data_path);
+_FORCE_INLINE_ void FontData::_ensure_rid(int p_cache_index) const {
+ if (unlikely(p_cache_index >= cache.size())) {
+ cache.resize(p_cache_index + 1);
+ }
+ if (unlikely(!cache[p_cache_index].is_valid())) {
+ cache.write[p_cache_index] = TS->create_font();
+ TS->font_set_data_ptr(cache[p_cache_index], data_ptr, data_size);
+ TS->font_set_antialiased(cache[p_cache_index], antialiased);
+ TS->font_set_multichannel_signed_distance_field(cache[p_cache_index], msdf);
+ TS->font_set_msdf_pixel_range(cache[p_cache_index], msdf_pixel_range);
+ TS->font_set_msdf_size(cache[p_cache_index], msdf_size);
+ TS->font_set_fixed_size(cache[p_cache_index], fixed_size);
+ TS->font_set_force_autohinter(cache[p_cache_index], force_autohinter);
+ TS->font_set_hinting(cache[p_cache_index], hinting);
+ TS->font_set_oversampling(cache[p_cache_index], oversampling);
+ }
+}
- ClassDB::bind_method(D_METHOD("get_height", "size"), &FontData::get_height);
- ClassDB::bind_method(D_METHOD("get_ascent", "size"), &FontData::get_ascent);
- ClassDB::bind_method(D_METHOD("get_descent", "size"), &FontData::get_descent);
+void FontData::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_data", "data"), &FontData::set_data);
+ ClassDB::bind_method(D_METHOD("get_data"), &FontData::get_data);
- ClassDB::bind_method(D_METHOD("get_underline_position", "size"), &FontData::get_underline_position);
- ClassDB::bind_method(D_METHOD("get_underline_thickness", "size"), &FontData::get_underline_thickness);
+ ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontData::set_antialiased);
+ ClassDB::bind_method(D_METHOD("is_antialiased"), &FontData::is_antialiased);
- ClassDB::bind_method(D_METHOD("get_spacing", "type"), &FontData::get_spacing);
- ClassDB::bind_method(D_METHOD("set_spacing", "type", "value"), &FontData::set_spacing);
+ ClassDB::bind_method(D_METHOD("set_multichannel_signed_distance_field", "msdf"), &FontData::set_multichannel_signed_distance_field);
+ ClassDB::bind_method(D_METHOD("is_multichannel_signed_distance_field"), &FontData::is_multichannel_signed_distance_field);
- ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontData::set_antialiased);
- ClassDB::bind_method(D_METHOD("get_antialiased"), &FontData::get_antialiased);
+ ClassDB::bind_method(D_METHOD("set_msdf_pixel_range", "msdf_pixel_range"), &FontData::set_msdf_pixel_range);
+ ClassDB::bind_method(D_METHOD("get_msdf_pixel_range"), &FontData::get_msdf_pixel_range);
- ClassDB::bind_method(D_METHOD("get_variation_list"), &FontData::get_variation_list);
+ ClassDB::bind_method(D_METHOD("set_msdf_size", "msdf_size"), &FontData::set_msdf_size);
+ ClassDB::bind_method(D_METHOD("get_msdf_size"), &FontData::get_msdf_size);
- ClassDB::bind_method(D_METHOD("set_variation", "tag", "value"), &FontData::set_variation);
- ClassDB::bind_method(D_METHOD("get_variation", "tag"), &FontData::get_variation);
+ ClassDB::bind_method(D_METHOD("set_force_autohinter", "force_autohinter"), &FontData::set_force_autohinter);
+ ClassDB::bind_method(D_METHOD("is_force_autohinter"), &FontData::is_force_autohinter);
ClassDB::bind_method(D_METHOD("set_hinting", "hinting"), &FontData::set_hinting);
ClassDB::bind_method(D_METHOD("get_hinting"), &FontData::get_hinting);
- ClassDB::bind_method(D_METHOD("set_force_autohinter", "enabled"), &FontData::set_force_autohinter);
- ClassDB::bind_method(D_METHOD("get_force_autohinter"), &FontData::get_force_autohinter);
+ ClassDB::bind_method(D_METHOD("set_oversampling", "oversampling"), &FontData::set_oversampling);
+ ClassDB::bind_method(D_METHOD("get_oversampling"), &FontData::get_oversampling);
- ClassDB::bind_method(D_METHOD("set_distance_field_hint", "distance_field"), &FontData::set_distance_field_hint);
- ClassDB::bind_method(D_METHOD("get_distance_field_hint"), &FontData::get_distance_field_hint);
+ ClassDB::bind_method(D_METHOD("find_cache", "variation_coordinates"), &FontData::find_cache);
- ClassDB::bind_method(D_METHOD("has_char", "char"), &FontData::has_char);
- ClassDB::bind_method(D_METHOD("get_supported_chars"), &FontData::get_supported_chars);
+ ClassDB::bind_method(D_METHOD("get_cache_count"), &FontData::get_cache_count);
+ ClassDB::bind_method(D_METHOD("clear_cache"), &FontData::clear_cache);
+ ClassDB::bind_method(D_METHOD("remove_cache", "cache_index"), &FontData::remove_cache);
+
+ ClassDB::bind_method(D_METHOD("get_size_cache_list", "cache_index"), &FontData::get_size_cache_list);
+ ClassDB::bind_method(D_METHOD("clear_size_cache", "cache_index"), &FontData::clear_size_cache);
+ ClassDB::bind_method(D_METHOD("remove_size_cache", "cache_index", "size"), &FontData::remove_size_cache);
+
+ ClassDB::bind_method(D_METHOD("set_variation_coordinates", "cache_index", "variation_coordinates"), &FontData::set_variation_coordinates);
+ ClassDB::bind_method(D_METHOD("get_variation_coordinates", "cache_index"), &FontData::get_variation_coordinates);
+
+ ClassDB::bind_method(D_METHOD("set_ascent", "cache_index", "size", "ascent"), &FontData::set_ascent);
+ ClassDB::bind_method(D_METHOD("get_ascent", "cache_index", "size"), &FontData::get_ascent);
+
+ ClassDB::bind_method(D_METHOD("set_descent", "cache_index", "size", "descent"), &FontData::set_descent);
+ ClassDB::bind_method(D_METHOD("get_descent", "cache_index", "size"), &FontData::get_descent);
+
+ ClassDB::bind_method(D_METHOD("set_underline_position", "cache_index", "size", "underline_position"), &FontData::set_underline_position);
+ ClassDB::bind_method(D_METHOD("get_underline_position", "cache_index", "size"), &FontData::get_underline_position);
+
+ ClassDB::bind_method(D_METHOD("set_underline_thickness", "cache_index", "size", "underline_thickness"), &FontData::set_underline_thickness);
+ ClassDB::bind_method(D_METHOD("get_underline_thickness", "cache_index", "size"), &FontData::get_underline_thickness);
+
+ ClassDB::bind_method(D_METHOD("set_scale", "cache_index", "size", "scale"), &FontData::set_scale);
+ ClassDB::bind_method(D_METHOD("get_scale", "cache_index", "size"), &FontData::get_scale);
+
+ ClassDB::bind_method(D_METHOD("set_spacing", "cache_index", "size", "spacing"), &FontData::set_spacing);
+ ClassDB::bind_method(D_METHOD("get_spacing", "cache_index", "size"), &FontData::get_spacing);
+
+ ClassDB::bind_method(D_METHOD("get_texture_count", "cache_index", "size"), &FontData::get_texture_count);
+ ClassDB::bind_method(D_METHOD("clear_textures", "cache_index", "size"), &FontData::clear_textures);
+ ClassDB::bind_method(D_METHOD("remove_texture", "cache_index", "size", "texture_index"), &FontData::remove_texture);
+
+ ClassDB::bind_method(D_METHOD("set_texture_image", "cache_index", "size", "texture_index", "image"), &FontData::set_texture_image);
+ ClassDB::bind_method(D_METHOD("get_texture_image", "cache_index", "size", "texture_index"), &FontData::get_texture_image);
+
+ ClassDB::bind_method(D_METHOD("set_texture_offsets", "cache_index", "size", "texture_index", "offset"), &FontData::set_texture_offsets);
+ ClassDB::bind_method(D_METHOD("get_texture_offsets", "cache_index", "size", "texture_index"), &FontData::get_texture_offsets);
+
+ ClassDB::bind_method(D_METHOD("get_glyph_list", "cache_index", "size"), &FontData::get_glyph_list);
+ ClassDB::bind_method(D_METHOD("clear_glyphs", "cache_index", "size"), &FontData::clear_glyphs);
+ ClassDB::bind_method(D_METHOD("remove_glyph", "cache_index", "size", "glyph"), &FontData::remove_glyph);
+
+ ClassDB::bind_method(D_METHOD("set_glyph_advance", "cache_index", "size", "glyph", "advance"), &FontData::set_glyph_advance);
+ ClassDB::bind_method(D_METHOD("get_glyph_advance", "cache_index", "size", "glyph"), &FontData::get_glyph_advance);
+
+ ClassDB::bind_method(D_METHOD("set_glyph_offset", "cache_index", "size", "glyph", "offset"), &FontData::set_glyph_offset);
+ ClassDB::bind_method(D_METHOD("get_glyph_offset", "cache_index", "size", "glyph"), &FontData::get_glyph_offset);
+
+ ClassDB::bind_method(D_METHOD("set_glyph_size", "cache_index", "size", "glyph", "gl_size"), &FontData::set_glyph_size);
+ ClassDB::bind_method(D_METHOD("get_glyph_size", "cache_index", "size", "glyph"), &FontData::get_glyph_size);
- ClassDB::bind_method(D_METHOD("get_glyph_advance", "index", "size"), &FontData::get_glyph_advance);
- ClassDB::bind_method(D_METHOD("get_glyph_kerning", "index_a", "index_b", "size"), &FontData::get_glyph_kerning);
+ ClassDB::bind_method(D_METHOD("set_glyph_uv_rect", "cache_index", "size", "glyph", "uv_rect"), &FontData::set_glyph_uv_rect);
+ ClassDB::bind_method(D_METHOD("get_glyph_uv_rect", "cache_index", "size", "glyph"), &FontData::get_glyph_uv_rect);
- ClassDB::bind_method(D_METHOD("get_base_size"), &FontData::get_base_size);
+ ClassDB::bind_method(D_METHOD("set_glyph_texture_idx", "cache_index", "size", "glyph", "texture_idx"), &FontData::set_glyph_texture_idx);
+ ClassDB::bind_method(D_METHOD("get_glyph_texture_idx", "cache_index", "size", "glyph"), &FontData::get_glyph_texture_idx);
- ClassDB::bind_method(D_METHOD("has_outline"), &FontData::has_outline);
+ ClassDB::bind_method(D_METHOD("get_kerning_list", "cache_index", "size"), &FontData::get_kerning_list);
+ ClassDB::bind_method(D_METHOD("clear_kerning_map", "cache_index", "size"), &FontData::clear_kerning_map);
+ ClassDB::bind_method(D_METHOD("remove_kerning", "cache_index", "size", "glyph_pair"), &FontData::remove_kerning);
+
+ ClassDB::bind_method(D_METHOD("set_kerning", "cache_index", "size", "glyph_pair", "kerning"), &FontData::set_kerning);
+ ClassDB::bind_method(D_METHOD("get_kerning", "cache_index", "size", "glyph_pair"), &FontData::get_kerning);
+
+ ClassDB::bind_method(D_METHOD("render_range", "cache_index", "size", "start", "end"), &FontData::render_range);
+ ClassDB::bind_method(D_METHOD("render_glyph", "cache_index", "size", "index"), &FontData::render_glyph);
+
+ ClassDB::bind_method(D_METHOD("get_cache_rid", "cache_index"), &FontData::get_cache_rid);
ClassDB::bind_method(D_METHOD("is_language_supported", "language"), &FontData::is_language_supported);
ClassDB::bind_method(D_METHOD("set_language_support_override", "language", "supported"), &FontData::set_language_support_override);
@@ -97,511 +172,915 @@ void FontData::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_script_support_override", "script"), &FontData::remove_script_support_override);
ClassDB::bind_method(D_METHOD("get_script_support_overrides"), &FontData::get_script_support_overrides);
- ClassDB::bind_method(D_METHOD("get_glyph_index", "char", "variation_selector"), &FontData::get_glyph_index, DEFVAL(0x0000));
- ClassDB::bind_method(D_METHOD("draw_glyph", "canvas", "size", "pos", "index", "color"), &FontData::draw_glyph, DEFVAL(Color(1, 1, 1)));
- ClassDB::bind_method(D_METHOD("draw_glyph_outline", "canvas", "size", "outline_size", "pos", "index", "color"), &FontData::draw_glyph_outline, DEFVAL(Color(1, 1, 1)));
-
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "data_path", PROPERTY_HINT_FILE, "*.ttf,*.otf,*.woff,*.fnt,*.font"), "set_data_path", "get_data_path");
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased"), "set_antialiased", "get_antialiased");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter"), "set_force_autohinter", "get_force_autohinter");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distance_field_hint"), "set_distance_field_hint", "get_distance_field_hint");
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), "set_hinting", "get_hinting");
+ ClassDB::bind_method(D_METHOD("has_char", "char"), &FontData::has_char);
+ ClassDB::bind_method(D_METHOD("get_supported_chars"), &FontData::get_supported_chars);
- ADD_GROUP("Extra Spacing", "extra_spacing");
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_glyph"), "set_spacing", "get_spacing", SPACING_GLYPH);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_space"), "set_spacing", "get_spacing", SPACING_SPACE);
+ ClassDB::bind_method(D_METHOD("get_glyph_index", "char", "variation_selector"), &FontData::get_glyph_index);
- BIND_ENUM_CONSTANT(SPACING_GLYPH);
- BIND_ENUM_CONSTANT(SPACING_SPACE);
+ ClassDB::bind_method(D_METHOD("get_supported_feature_list"), &FontData::get_supported_feature_list);
+ ClassDB::bind_method(D_METHOD("get_supported_variation_list"), &FontData::get_supported_variation_list);
}
bool FontData::_set(const StringName &p_name, const Variant &p_value) {
- String str = p_name;
- if (str.begins_with("language_support_override/")) {
- String lang = str.get_slicec('/', 1);
- if (lang == "_new") {
- return false;
+ Vector<String> tokens = p_name.operator String().split("/");
+ if (tokens.size() == 1) {
+ if (tokens[0] == "data") {
+ set_data(p_value);
+ return true;
+ } else if (tokens[0] == "antialiased") {
+ set_antialiased(p_value);
+ return true;
+ } else if (tokens[0] == "multichannel_signed_distance_field") {
+ set_multichannel_signed_distance_field(p_value);
+ return true;
+ } else if (tokens[0] == "msdf_pixel_range") {
+ set_msdf_pixel_range(p_value);
+ return true;
+ } else if (tokens[0] == "msdf_size") {
+ set_msdf_size(p_value);
+ return true;
+ } else if (tokens[0] == "fixed_size") {
+ set_fixed_size(p_value);
+ return true;
+ } else if (tokens[0] == "hinting") {
+ set_hinting((TextServer::Hinting)p_value.operator int());
+ return true;
+ } else if (tokens[0] == "force_autohinter") {
+ set_force_autohinter(p_value);
+ return true;
+ } else if (tokens[0] == "oversampling") {
+ set_oversampling(p_value);
+ return true;
}
+ } else if (tokens.size() == 2 && tokens[0] == "language_support_override") {
+ String lang = tokens[1];
set_language_support_override(lang, p_value);
return true;
- }
- if (str.begins_with("script_support_override/")) {
- String scr = str.get_slicec('/', 1);
- if (scr == "_new") {
- return false;
- }
- set_script_support_override(scr, p_value);
- return true;
- }
- if (str.begins_with("variation/")) {
- String name = str.get_slicec('/', 1);
- set_variation(name, p_value);
+ } else if (tokens.size() == 2 && tokens[0] == "script_support_override") {
+ String script = tokens[1];
+ set_script_support_override(script, p_value);
return true;
+ } else if (tokens.size() >= 3 && tokens[0] == "cache") {
+ int cache_index = tokens[1].to_int();
+ if (tokens.size() == 3 && tokens[2] == "variation_coordinates") {
+ set_variation_coordinates(cache_index, p_value);
+ return true;
+ }
+ if (tokens.size() >= 5) {
+ Vector2i sz = Vector2i(tokens[2].to_int(), tokens[3].to_int());
+ if (tokens[4] == "ascent") {
+ set_ascent(cache_index, sz.x, p_value);
+ return true;
+ } else if (tokens[4] == "descent") {
+ set_descent(cache_index, sz.x, p_value);
+ return true;
+ } else if (tokens[4] == "underline_position") {
+ set_underline_position(cache_index, sz.x, p_value);
+ return true;
+ } else if (tokens[4] == "underline_thickness") {
+ set_underline_thickness(cache_index, sz.x, p_value);
+ return true;
+ } else if (tokens[4] == "scale") {
+ set_scale(cache_index, sz.x, p_value);
+ return true;
+ } else if (tokens[4] == "spacing_glyph") {
+ set_spacing(cache_index, sz.x, TextServer::SPACING_GLYPH, p_value);
+ return true;
+ } else if (tokens[4] == "spacing_space") {
+ set_spacing(cache_index, sz.x, TextServer::SPACING_SPACE, p_value);
+ return true;
+ } else if (tokens.size() == 7 && tokens[4] == "textures") {
+ int texture_index = tokens[5].to_int();
+ if (tokens[6] == "image") {
+ set_texture_image(cache_index, sz, texture_index, p_value);
+ return true;
+ } else if (tokens[6] == "offsets") {
+ set_texture_offsets(cache_index, sz, texture_index, p_value);
+ return true;
+ }
+ } else if (tokens.size() == 7 && tokens[4] == "glyphs") {
+ int32_t glyph_index = tokens[5].to_int();
+ if (tokens[6] == "advance") {
+ set_glyph_advance(cache_index, sz.x, glyph_index, p_value);
+ return true;
+ } else if (tokens[6] == "offset") {
+ set_glyph_offset(cache_index, sz, glyph_index, p_value);
+ return true;
+ } else if (tokens[6] == "size") {
+ set_glyph_size(cache_index, sz, glyph_index, p_value);
+ return true;
+ } else if (tokens[6] == "uv_rect") {
+ set_glyph_uv_rect(cache_index, sz, glyph_index, p_value);
+ return true;
+ } else if (tokens[6] == "texture_idx") {
+ set_glyph_texture_idx(cache_index, sz, glyph_index, p_value);
+ return true;
+ }
+ } else if (tokens.size() == 7 && tokens[4] == "kerning_overrides") {
+ Vector2i gp = Vector2i(tokens[5].to_int(), tokens[6].to_int());
+ set_kerning(cache_index, sz.x, gp, p_value);
+ return true;
+ }
+ }
}
-
return false;
}
bool FontData::_get(const StringName &p_name, Variant &r_ret) const {
- String str = p_name;
- if (str.begins_with("language_support_override/")) {
- String lang = str.get_slicec('/', 1);
- if (lang == "_new") {
+ Vector<String> tokens = p_name.operator String().split("/");
+ if (tokens.size() == 1) {
+ if (tokens[0] == "data") {
+ r_ret = get_data();
+ return true;
+ } else if (tokens[0] == "antialiased") {
+ r_ret = is_antialiased();
+ return true;
+ } else if (tokens[0] == "multichannel_signed_distance_field") {
+ r_ret = is_multichannel_signed_distance_field();
+ return true;
+ } else if (tokens[0] == "msdf_pixel_range") {
+ r_ret = get_msdf_pixel_range();
+ return true;
+ } else if (tokens[0] == "msdf_size") {
+ r_ret = get_msdf_size();
+ return true;
+ } else if (tokens[0] == "fixed_size") {
+ r_ret = get_fixed_size();
+ return true;
+ } else if (tokens[0] == "hinting") {
+ r_ret = get_hinting();
+ return true;
+ } else if (tokens[0] == "force_autohinter") {
+ r_ret = is_force_autohinter();
+ return true;
+ } else if (tokens[0] == "oversampling") {
+ r_ret = get_oversampling();
return true;
}
+ } else if (tokens.size() == 2 && tokens[0] == "language_support_override") {
+ String lang = tokens[1];
r_ret = get_language_support_override(lang);
return true;
- }
- if (str.begins_with("script_support_override/")) {
- String scr = str.get_slicec('/', 1);
- if (scr == "_new") {
+ } else if (tokens.size() == 2 && tokens[0] == "script_support_override") {
+ String script = tokens[1];
+ r_ret = get_script_support_override(script);
+ return true;
+ } else if (tokens.size() >= 3 && tokens[0] == "cache") {
+ int cache_index = tokens[1].to_int();
+ if (tokens.size() == 3 && tokens[2] == "variation_coordinates") {
+ r_ret = get_variation_coordinates(cache_index);
return true;
}
- r_ret = get_script_support_override(scr);
- return true;
- }
- if (str.begins_with("variation/")) {
- String name = str.get_slicec('/', 1);
-
- r_ret = get_variation(name);
- return true;
+ if (tokens.size() >= 5) {
+ Vector2i sz = Vector2i(tokens[2].to_int(), tokens[3].to_int());
+ if (tokens[4] == "ascent") {
+ r_ret = get_ascent(cache_index, sz.x);
+ return true;
+ } else if (tokens[4] == "descent") {
+ r_ret = get_descent(cache_index, sz.x);
+ return true;
+ } else if (tokens[4] == "underline_position") {
+ r_ret = get_underline_position(cache_index, sz.x);
+ return true;
+ } else if (tokens[4] == "underline_thickness") {
+ r_ret = get_underline_thickness(cache_index, sz.x);
+ return true;
+ } else if (tokens[4] == "scale") {
+ r_ret = get_scale(cache_index, sz.x);
+ return true;
+ } else if (tokens[4] == "spacing_glyph") {
+ r_ret = get_spacing(cache_index, sz.x, TextServer::SPACING_GLYPH);
+ return true;
+ } else if (tokens[4] == "spacing_space") {
+ r_ret = get_spacing(cache_index, sz.x, TextServer::SPACING_SPACE);
+ return true;
+ } else if (tokens.size() == 7 && tokens[4] == "textures") {
+ int texture_index = tokens[5].to_int();
+ if (tokens[6] == "image") {
+ r_ret = get_texture_image(cache_index, sz, texture_index);
+ return true;
+ } else if (tokens[6] == "offsets") {
+ r_ret = get_texture_offsets(cache_index, sz, texture_index);
+ return true;
+ }
+ } else if (tokens.size() == 7 && tokens[4] == "glyphs") {
+ int32_t glyph_index = tokens[5].to_int();
+ if (tokens[6] == "advance") {
+ r_ret = get_glyph_advance(cache_index, sz.x, glyph_index);
+ return true;
+ } else if (tokens[6] == "offset") {
+ r_ret = get_glyph_offset(cache_index, sz, glyph_index);
+ return true;
+ } else if (tokens[6] == "size") {
+ r_ret = get_glyph_size(cache_index, sz, glyph_index);
+ return true;
+ } else if (tokens[6] == "uv_rect") {
+ r_ret = get_glyph_uv_rect(cache_index, sz, glyph_index);
+ return true;
+ } else if (tokens[6] == "texture_idx") {
+ r_ret = get_glyph_texture_idx(cache_index, sz, glyph_index);
+ return true;
+ }
+ } else if (tokens.size() == 7 && tokens[4] == "kerning_overrides") {
+ Vector2i gp = Vector2i(tokens[5].to_int(), tokens[6].to_int());
+ r_ret = get_kerning(cache_index, sz.x, gp);
+ return true;
+ }
+ }
}
-
return false;
}
void FontData::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+
+ p_list->push_back(PropertyInfo(Variant::BOOL, "antialiased", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::INT, "fixed_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "force_autohinter", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+
Vector<String> lang_over = get_language_support_overrides();
for (int i = 0; i < lang_over.size(); i++) {
- p_list->push_back(PropertyInfo(Variant::BOOL, "language_support_override/" + lang_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "language_support_override/" + lang_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
}
- p_list->push_back(PropertyInfo(Variant::NIL, "language_support_override/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
-
Vector<String> scr_over = get_script_support_overrides();
for (int i = 0; i < scr_over.size(); i++) {
- p_list->push_back(PropertyInfo(Variant::BOOL, "script_support_override/" + scr_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE));
- }
- p_list->push_back(PropertyInfo(Variant::NIL, "script_support_override/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "script_support_override/" + scr_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ }
+ for (int i = 0; i < cache.size(); i++) {
+ String prefix = "cache/" + itos(i) + "/";
+ Array sizes = get_size_cache_list(i);
+ p_list->push_back(PropertyInfo(Variant::DICTIONARY, prefix + "variation_coordinates", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ for (int j = 0; j < sizes.size(); j++) {
+ Vector2i sz = sizes[j];
+ String prefix_sz = prefix + itos(sz.x) + "/" + itos(sz.y) + "/";
+ if (sz.y == 0) {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "ascent", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "descent", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "underline_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "underline_thickness", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::BOOL, prefix_sz + "spacing_glyph", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::BOOL, prefix_sz + "spacing_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ }
- Dictionary variations = get_variation_list();
- for (const Variant *ftr = variations.next(nullptr); ftr != nullptr; ftr = variations.next(ftr)) {
- Vector3i v = variations[*ftr];
- p_list->push_back(PropertyInfo(Variant::FLOAT, "variation/" + TS->tag_to_name(*ftr), PROPERTY_HINT_RANGE, itos(v.x) + "," + itos(v.y), PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE));
+ int tx_cnt = get_texture_count(i, sz);
+ for (int k = 0; k < tx_cnt; k++) {
+ p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, prefix_sz + "textures/" + itos(k) + "/offsets", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::OBJECT, prefix_sz + "textures/" + itos(k) + "/image", PROPERTY_HINT_RESOURCE_TYPE, "Image", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT));
+ }
+ Array glyphs = get_glyph_list(i, sz);
+ for (int k = 0; k < glyphs.size(); k++) {
+ const int32_t &gl = glyphs[k];
+ if (sz.y == 0) {
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/advance", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ }
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::RECT2, prefix_sz + "glyphs/" + itos(gl) + "/uv_rect", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::INT, prefix_sz + "glyphs/" + itos(gl) + "/texture_idx", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ }
+ if (sz.y == 0) {
+ Array kerning_map = get_kerning_list(i, sz.x);
+ for (int k = 0; k < kerning_map.size(); k++) {
+ const Vector2i &gl_pair = kerning_map[k];
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "kerning_overrides/" + itos(gl_pair.x) + "/" + itos(gl_pair.y), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ }
+ }
+ }
}
}
void FontData::reset_state() {
- if (rid != RID()) {
- TS->free(rid);
- }
- base_size = 16;
- path = String();
-}
+ _clear_cache();
+ data.clear();
+ data_ptr = nullptr;
+ data_size = 0;
+ cache.clear();
-RID FontData::get_rid() const {
- return rid;
+ antialiased = true;
+ msdf = false;
+ force_autohinter = false;
+ hinting = TextServer::HINTING_LIGHT;
+ msdf_pixel_range = 14;
+ msdf_size = 128;
+ oversampling = 0.f;
}
-void FontData::load_resource(const String &p_filename, int p_base_size) {
- if (rid != RID()) {
- TS->free(rid);
+/*************************************************************************/
+
+void FontData::set_data_ptr(const uint8_t *p_data, size_t p_size) {
+ data.clear();
+ data_ptr = p_data;
+ data_size = p_size;
+
+ if (data_ptr != nullptr) {
+ for (int i = 0; i < cache.size(); i++) {
+ if (cache[i].is_valid()) {
+ TS->font_set_data_ptr(cache[i], data_ptr, data_size);
+ }
+ }
}
- rid = TS->create_font_resource(p_filename, p_base_size);
- path = p_filename;
- base_size = TS->font_get_base_size(rid);
- emit_changed();
}
-void FontData::_load_memory(const PackedByteArray &p_data, const String &p_type, int p_base_size) {
- if (rid != RID()) {
- TS->free(rid);
+void FontData::set_data(const PackedByteArray &p_data) {
+ data = p_data;
+ data_ptr = data.ptr();
+ data_size = data.size();
+
+ if (data_ptr != nullptr) {
+ for (int i = 0; i < cache.size(); i++) {
+ if (cache[i].is_valid()) {
+ TS->font_set_data_ptr(cache[i], data_ptr, data_size);
+ }
+ }
}
- rid = TS->create_font_memory(p_data.ptr(), p_data.size(), p_type, p_base_size);
- path = TTR("(Memory: " + p_type.to_upper() + " @ 0x" + String::num_int64((uint64_t)p_data.ptr(), 16, true) + ")");
- base_size = TS->font_get_base_size(rid);
- emit_changed();
}
-void FontData::load_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size) {
- if (rid != RID()) {
- TS->free(rid);
- }
- rid = TS->create_font_memory(p_data, p_size, p_type, p_base_size);
- path = TTR("(Memory: " + p_type.to_upper() + " @ 0x" + String::num_int64((uint64_t)p_data, 16, true) + ")");
- base_size = TS->font_get_base_size(rid);
- emit_changed();
+PackedByteArray FontData::get_data() const {
+ return data;
}
-void FontData::new_bitmap(float p_height, float p_ascent, int p_base_size) {
- if (rid != RID()) {
- TS->free(rid);
+void FontData::set_antialiased(bool p_antialiased) {
+ if (antialiased != p_antialiased) {
+ antialiased = p_antialiased;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_antialiased(cache[i], antialiased);
+ }
+ emit_changed();
}
- rid = TS->create_font_bitmap(p_height, p_ascent, p_base_size);
- path = TTR("(Bitmap: " + String::num_int64(rid.get_id(), 16, true) + ")");
- base_size = TS->font_get_base_size(rid);
- emit_changed();
}
-void FontData::bitmap_add_texture(const Ref<Texture> &p_texture) {
- if (rid != RID()) {
- TS->font_bitmap_add_texture(rid, p_texture);
- }
+bool FontData::is_antialiased() const {
+ return antialiased;
}
-void FontData::bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) {
- if (rid != RID()) {
- TS->font_bitmap_add_char(rid, p_char, p_texture_idx, p_rect, p_align, p_advance);
+void FontData::set_multichannel_signed_distance_field(bool p_msdf) {
+ if (msdf != p_msdf) {
+ msdf = p_msdf;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_multichannel_signed_distance_field(cache[i], msdf);
+ }
+ emit_changed();
}
}
-void FontData::bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) {
- if (rid != RID()) {
- TS->font_bitmap_add_kerning_pair(rid, p_A, p_B, p_kerning);
- }
+bool FontData::is_multichannel_signed_distance_field() const {
+ return msdf;
}
-void FontData::set_data_path(const String &p_path) {
- load_resource(p_path, base_size);
+void FontData::set_msdf_pixel_range(int p_msdf_pixel_range) {
+ if (msdf_pixel_range != p_msdf_pixel_range) {
+ msdf_pixel_range = p_msdf_pixel_range;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_msdf_pixel_range(cache[i], msdf_pixel_range);
+ }
+ emit_changed();
+ }
}
-String FontData::get_data_path() const {
- return path;
+int FontData::get_msdf_pixel_range() const {
+ return msdf_pixel_range;
}
-float FontData::get_height(int p_size) const {
- if (rid == RID()) {
- return 0.f; // Do not raise errors in getters, to prevent editor from spamming errors on incomplete (without data_path set) fonts.
+void FontData::set_msdf_size(int p_msdf_size) {
+ if (msdf_size != p_msdf_size) {
+ msdf_size = p_msdf_size;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_msdf_size(cache[i], msdf_size);
+ }
+ emit_changed();
}
- return TS->font_get_height(rid, (p_size < 0) ? base_size : p_size);
}
-float FontData::get_ascent(int p_size) const {
- if (rid == RID()) {
- return 0.f;
- }
- return TS->font_get_ascent(rid, (p_size < 0) ? base_size : p_size);
+int FontData::get_msdf_size() const {
+ return msdf_size;
}
-float FontData::get_descent(int p_size) const {
- if (rid == RID()) {
- return 0.f;
+void FontData::set_fixed_size(int p_fixed_size) {
+ if (fixed_size != p_fixed_size) {
+ fixed_size = p_fixed_size;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_fixed_size(cache[i], fixed_size);
+ }
+ emit_changed();
}
- return TS->font_get_descent(rid, (p_size < 0) ? base_size : p_size);
}
-float FontData::get_underline_position(int p_size) const {
- if (rid == RID()) {
- return 0.f;
- }
- return TS->font_get_underline_position(rid, (p_size < 0) ? base_size : p_size);
+int FontData::get_fixed_size() const {
+ return fixed_size;
}
-Dictionary FontData::get_feature_list() const {
- if (rid == RID()) {
- return Dictionary();
+void FontData::set_force_autohinter(bool p_force_autohinter) {
+ if (force_autohinter != p_force_autohinter) {
+ force_autohinter = p_force_autohinter;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_force_autohinter(cache[i], force_autohinter);
+ }
+ emit_changed();
}
- return TS->font_get_feature_list(rid);
}
-float FontData::get_underline_thickness(int p_size) const {
- if (rid == RID()) {
- return 0.f;
- }
- return TS->font_get_underline_thickness(rid, (p_size < 0) ? base_size : p_size);
+bool FontData::is_force_autohinter() const {
+ return force_autohinter;
}
-Dictionary FontData::get_variation_list() const {
- if (rid == RID()) {
- return Dictionary();
+void FontData::set_hinting(TextServer::Hinting p_hinting) {
+ if (hinting != p_hinting) {
+ hinting = p_hinting;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_hinting(cache[i], hinting);
+ }
+ emit_changed();
}
- return TS->font_get_variation_list(rid);
}
-void FontData::set_variation(const String &p_name, double p_value) {
- ERR_FAIL_COND(rid == RID());
- TS->font_set_variation(rid, p_name, p_value);
- emit_changed();
+TextServer::Hinting FontData::get_hinting() const {
+ return hinting;
}
-double FontData::get_variation(const String &p_name) const {
- if (rid == RID()) {
- return 0;
+void FontData::set_oversampling(real_t p_oversampling) {
+ if (oversampling != p_oversampling) {
+ oversampling = p_oversampling;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_oversampling(cache[i], oversampling);
+ }
+ emit_changed();
+ }
+}
+
+real_t FontData::get_oversampling() const {
+ return oversampling;
+}
+
+RID FontData::find_cache(const Dictionary &p_variation_coordinates) const {
+ // Find existing variation cache.
+ const Dictionary &supported_coords = get_supported_variation_list();
+ for (int i = 0; i < cache.size(); i++) {
+ if (cache[i].is_valid()) {
+ const Dictionary &cache_var = TS->font_get_variation_coordinates(cache[i]);
+ bool match = true;
+ for (const Variant *V = supported_coords.next(nullptr); V && match; V = supported_coords.next(V)) {
+ const Vector3 &def = supported_coords[*V];
+
+ real_t c_v = def.z;
+ if (cache_var.has(*V)) {
+ real_t val = cache_var[*V];
+ c_v = CLAMP(val, def.x, def.y);
+ }
+ if (cache_var.has(TS->tag_to_name(*V))) {
+ real_t val = cache_var[TS->tag_to_name(*V)];
+ c_v = CLAMP(val, def.x, def.y);
+ }
+
+ real_t s_v = def.z;
+ if (p_variation_coordinates.has(*V)) {
+ real_t val = p_variation_coordinates[*V];
+ s_v = CLAMP(val, def.x, def.y);
+ }
+ if (p_variation_coordinates.has(TS->tag_to_name(*V))) {
+ real_t val = p_variation_coordinates[TS->tag_to_name(*V)];
+ s_v = CLAMP(val, def.x, def.y);
+ }
+
+ match = match && (c_v == s_v);
+ }
+ if (match) {
+ return cache[i];
+ }
+ }
}
- return TS->font_get_variation(rid, p_name);
+
+ // Create new variation cache.
+ int idx = cache.size();
+ _ensure_rid(idx);
+ TS->font_set_variation_coordinates(cache[idx], p_variation_coordinates);
+ return cache[idx];
}
-int FontData::get_spacing(int p_type) const {
- if (rid == RID()) {
- return 0;
- }
- if (p_type == SPACING_GLYPH) {
- return TS->font_get_spacing_glyph(rid);
- } else {
- return TS->font_get_spacing_space(rid);
- }
+int FontData::get_cache_count() const {
+ return cache.size();
}
-void FontData::set_spacing(int p_type, int p_value) {
- ERR_FAIL_COND(rid == RID());
- if (p_type == SPACING_GLYPH) {
- TS->font_set_spacing_glyph(rid, p_value);
- } else {
- TS->font_set_spacing_space(rid, p_value);
+void FontData::clear_cache() {
+ _clear_cache();
+ cache.clear();
+}
+
+void FontData::remove_cache(int p_cache_index) {
+ ERR_FAIL_INDEX(p_cache_index, cache.size());
+ if (cache[p_cache_index].is_valid()) {
+ TS->free(cache.write[p_cache_index]);
}
+ cache.remove(p_cache_index);
emit_changed();
}
-void FontData::set_antialiased(bool p_antialiased) {
- ERR_FAIL_COND(rid == RID());
- TS->font_set_antialiased(rid, p_antialiased);
- emit_changed();
+Array FontData::get_size_cache_list(int p_cache_index) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_size_cache_list(cache[p_cache_index]);
}
-bool FontData::get_antialiased() const {
- if (rid == RID()) {
- return false;
- }
- return TS->font_get_antialiased(rid);
+void FontData::clear_size_cache(int p_cache_index) {
+ _ensure_rid(p_cache_index);
+ TS->font_clear_size_cache(cache[p_cache_index]);
+}
+
+void FontData::remove_size_cache(int p_cache_index, const Vector2i &p_size) {
+ _ensure_rid(p_cache_index);
+ TS->font_remove_size_cache(cache[p_cache_index], p_size);
}
-void FontData::set_distance_field_hint(bool p_distance_field) {
- ERR_FAIL_COND(rid == RID());
- TS->font_set_distance_field_hint(rid, p_distance_field);
+void FontData::set_variation_coordinates(int p_cache_index, const Dictionary &p_variation_coordinates) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_variation_coordinates(cache[p_cache_index], p_variation_coordinates);
emit_changed();
}
-bool FontData::get_distance_field_hint() const {
- if (rid == RID()) {
- return false;
- }
- return TS->font_get_distance_field_hint(rid);
+Dictionary FontData::get_variation_coordinates(int p_cache_index) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_variation_coordinates(cache[p_cache_index]);
}
-void FontData::set_hinting(TextServer::Hinting p_hinting) {
- ERR_FAIL_COND(rid == RID());
- TS->font_set_hinting(rid, p_hinting);
- emit_changed();
+void FontData::set_ascent(int p_cache_index, int p_size, real_t p_ascent) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_ascent(cache[p_cache_index], p_size, p_ascent);
}
-TextServer::Hinting FontData::get_hinting() const {
- if (rid == RID()) {
- return TextServer::HINTING_NONE;
- }
- return TS->font_get_hinting(rid);
+real_t FontData::get_ascent(int p_cache_index, int p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_ascent(cache[p_cache_index], p_size);
}
-void FontData::set_force_autohinter(bool p_enabeld) {
- ERR_FAIL_COND(rid == RID());
- TS->font_set_force_autohinter(rid, p_enabeld);
- emit_changed();
+void FontData::set_descent(int p_cache_index, int p_size, real_t p_descent) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_descent(cache[p_cache_index], p_size, p_descent);
}
-bool FontData::get_force_autohinter() const {
- if (rid == RID()) {
- return false;
- }
- return TS->font_get_force_autohinter(rid);
+real_t FontData::get_descent(int p_cache_index, int p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_descent(cache[p_cache_index], p_size);
}
-bool FontData::has_char(char32_t p_char) const {
- if (rid == RID()) {
- return false;
- }
- return TS->font_has_char(rid, p_char);
+void FontData::set_underline_position(int p_cache_index, int p_size, real_t p_underline_position) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_underline_position(cache[p_cache_index], p_size, p_underline_position);
}
-String FontData::get_supported_chars() const {
- ERR_FAIL_COND_V(rid == RID(), String());
- return TS->font_get_supported_chars(rid);
+real_t FontData::get_underline_position(int p_cache_index, int p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_underline_position(cache[p_cache_index], p_size);
}
-Vector2 FontData::get_glyph_advance(uint32_t p_index, int p_size) const {
- ERR_FAIL_COND_V(rid == RID(), Vector2());
- return TS->font_get_glyph_advance(rid, p_index, (p_size < 0) ? base_size : p_size);
+void FontData::set_underline_thickness(int p_cache_index, int p_size, real_t p_underline_thickness) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_underline_thickness(cache[p_cache_index], p_size, p_underline_thickness);
}
-Vector2 FontData::get_glyph_kerning(uint32_t p_index_a, uint32_t p_index_b, int p_size) const {
- ERR_FAIL_COND_V(rid == RID(), Vector2());
- return TS->font_get_glyph_kerning(rid, p_index_a, p_index_b, (p_size < 0) ? base_size : p_size);
+real_t FontData::get_underline_thickness(int p_cache_index, int p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_underline_thickness(cache[p_cache_index], p_size);
}
-bool FontData::has_outline() const {
- if (rid == RID()) {
- return false;
- }
- return TS->font_has_outline(rid);
+void FontData::set_scale(int p_cache_index, int p_size, real_t p_scale) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_scale(cache[p_cache_index], p_size, p_scale);
}
-float FontData::get_base_size() const {
- return base_size;
+real_t FontData::get_scale(int p_cache_index, int p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_scale(cache[p_cache_index], p_size);
+}
+
+void FontData::set_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing, int p_value) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_spacing(cache[p_cache_index], p_size, p_spacing, p_value);
+}
+
+int FontData::get_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_spacing(cache[p_cache_index], p_size, p_spacing);
+}
+
+int FontData::get_texture_count(int p_cache_index, const Vector2i &p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_texture_count(cache[p_cache_index], p_size);
+}
+
+void FontData::clear_textures(int p_cache_index, const Vector2i &p_size) {
+ _ensure_rid(p_cache_index);
+ TS->font_clear_textures(cache[p_cache_index], p_size);
+}
+
+void FontData::remove_texture(int p_cache_index, const Vector2i &p_size, int p_texture_index) {
+ _ensure_rid(p_cache_index);
+ TS->font_remove_texture(cache[p_cache_index], p_size, p_texture_index);
+}
+
+void FontData::set_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_texture_image(cache[p_cache_index], p_size, p_texture_index, p_image);
+}
+
+Ref<Image> FontData::get_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_texture_image(cache[p_cache_index], p_size, p_texture_index);
+}
+
+void FontData::set_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_texture_offsets(cache[p_cache_index], p_size, p_texture_index, p_offset);
+}
+
+PackedInt32Array FontData::get_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_texture_offsets(cache[p_cache_index], p_size, p_texture_index);
+}
+
+Array FontData::get_glyph_list(int p_cache_index, const Vector2i &p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_glyph_list(cache[p_cache_index], p_size);
+}
+
+void FontData::clear_glyphs(int p_cache_index, const Vector2i &p_size) {
+ _ensure_rid(p_cache_index);
+ TS->font_clear_glyphs(cache[p_cache_index], p_size);
+}
+
+void FontData::remove_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) {
+ _ensure_rid(p_cache_index);
+ TS->font_remove_glyph(cache[p_cache_index], p_size, p_glyph);
+}
+
+void FontData::set_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph, const Vector2 &p_advance) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_glyph_advance(cache[p_cache_index], p_size, p_glyph, p_advance);
+}
+
+Vector2 FontData::get_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_glyph_advance(cache[p_cache_index], p_size, p_glyph);
+}
+
+void FontData::set_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_glyph_offset(cache[p_cache_index], p_size, p_glyph, p_offset);
+}
+
+Vector2 FontData::get_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_glyph_offset(cache[p_cache_index], p_size, p_glyph);
+}
+
+void FontData::set_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_glyph_size(cache[p_cache_index], p_size, p_glyph, p_gl_size);
+}
+
+Vector2 FontData::get_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_glyph_size(cache[p_cache_index], p_size, p_glyph);
+}
+
+void FontData::set_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_glyph_uv_rect(cache[p_cache_index], p_size, p_glyph, p_uv_rect);
+}
+
+Rect2 FontData::get_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_glyph_uv_rect(cache[p_cache_index], p_size, p_glyph);
+}
+
+void FontData::set_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_glyph_texture_idx(cache[p_cache_index], p_size, p_glyph, p_texture_idx);
+}
+
+int FontData::get_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_glyph_texture_idx(cache[p_cache_index], p_size, p_glyph);
+}
+
+Array FontData::get_kerning_list(int p_cache_index, int p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_kerning_list(cache[p_cache_index], p_size);
+}
+
+void FontData::clear_kerning_map(int p_cache_index, int p_size) {
+ _ensure_rid(p_cache_index);
+ TS->font_clear_kerning_map(cache[p_cache_index], p_size);
+}
+
+void FontData::remove_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) {
+ _ensure_rid(p_cache_index);
+ TS->font_remove_kerning(cache[p_cache_index], p_size, p_glyph_pair);
+}
+
+void FontData::set_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_kerning(cache[p_cache_index], p_size, p_glyph_pair, p_kerning);
+}
+
+Vector2 FontData::get_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_kerning(cache[p_cache_index], p_size, p_glyph_pair);
+}
+
+void FontData::render_range(int p_cache_index, const Vector2i &p_size, char32_t p_start, char32_t p_end) {
+ _ensure_rid(p_cache_index);
+ TS->font_render_range(cache[p_cache_index], p_size, p_start, p_end);
+}
+
+void FontData::render_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_index) {
+ _ensure_rid(p_cache_index);
+ TS->font_render_glyph(cache[p_cache_index], p_size, p_index);
+}
+
+RID FontData::get_cache_rid(int p_cache_index) const {
+ _ensure_rid(p_cache_index);
+ return cache[p_cache_index];
}
bool FontData::is_language_supported(const String &p_language) const {
- if (rid == RID()) {
- return false;
- }
- return TS->font_is_language_supported(rid, p_language);
+ _ensure_rid(0);
+ return TS->font_is_language_supported(cache[0], p_language);
}
void FontData::set_language_support_override(const String &p_language, bool p_supported) {
- ERR_FAIL_COND(rid == RID());
- TS->font_set_language_support_override(rid, p_language, p_supported);
- emit_changed();
+ _ensure_rid(0);
+ TS->font_set_language_support_override(cache[0], p_language, p_supported);
}
bool FontData::get_language_support_override(const String &p_language) const {
- if (rid == RID()) {
- return false;
- }
- return TS->font_get_language_support_override(rid, p_language);
+ _ensure_rid(0);
+ return TS->font_get_language_support_override(cache[0], p_language);
}
void FontData::remove_language_support_override(const String &p_language) {
- ERR_FAIL_COND(rid == RID());
- TS->font_remove_language_support_override(rid, p_language);
- emit_changed();
+ _ensure_rid(0);
+ TS->font_remove_language_support_override(cache[0], p_language);
}
Vector<String> FontData::get_language_support_overrides() const {
- if (rid == RID()) {
- return Vector<String>();
- }
- return TS->font_get_language_support_overrides(rid);
+ _ensure_rid(0);
+ return TS->font_get_language_support_overrides(cache[0]);
}
bool FontData::is_script_supported(const String &p_script) const {
- if (rid == RID()) {
- return false;
- }
- return TS->font_is_script_supported(rid, p_script);
+ _ensure_rid(0);
+ return TS->font_is_script_supported(cache[0], p_script);
}
void FontData::set_script_support_override(const String &p_script, bool p_supported) {
- ERR_FAIL_COND(rid == RID());
- TS->font_set_script_support_override(rid, p_script, p_supported);
- emit_changed();
+ _ensure_rid(0);
+ TS->font_set_script_support_override(cache[0], p_script, p_supported);
}
bool FontData::get_script_support_override(const String &p_script) const {
- if (rid == RID()) {
- return false;
- }
- return TS->font_get_script_support_override(rid, p_script);
+ _ensure_rid(0);
+ return TS->font_get_script_support_override(cache[0], p_script);
}
void FontData::remove_script_support_override(const String &p_script) {
- ERR_FAIL_COND(rid == RID());
- TS->font_remove_script_support_override(rid, p_script);
- emit_changed();
+ _ensure_rid(0);
+ TS->font_remove_script_support_override(cache[0], p_script);
}
Vector<String> FontData::get_script_support_overrides() const {
- if (rid == RID()) {
- return Vector<String>();
- }
- return TS->font_get_script_support_overrides(rid);
+ _ensure_rid(0);
+ return TS->font_get_script_support_overrides(cache[0]);
}
-uint32_t FontData::get_glyph_index(char32_t p_char, char32_t p_variation_selector) const {
- ERR_FAIL_COND_V(rid == RID(), 0);
- return TS->font_get_glyph_index(rid, p_char, p_variation_selector);
+bool FontData::has_char(char32_t p_char) const {
+ _ensure_rid(0);
+ return TS->font_has_char(cache[0], p_char);
}
-Vector2 FontData::draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
- ERR_FAIL_COND_V(rid == RID(), Vector2());
- return TS->font_draw_glyph(rid, p_canvas, (p_size <= 0) ? base_size : p_size, p_pos, p_index, p_color);
+String FontData::get_supported_chars() const {
+ _ensure_rid(0);
+ return TS->font_get_supported_chars(cache[0]);
}
-Vector2 FontData::draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
- ERR_FAIL_COND_V(rid == RID(), Vector2());
- return TS->font_draw_glyph_outline(rid, p_canvas, (p_size <= 0) ? base_size : p_size, p_outline_size, p_pos, p_index, p_color);
+int32_t FontData::get_glyph_index(int p_size, char32_t p_char, char32_t p_variation_selector) const {
+ _ensure_rid(0);
+ return TS->font_get_glyph_index(cache[0], p_size, p_char, p_variation_selector);
}
-FontData::FontData() {}
+Dictionary FontData::get_supported_feature_list() const {
+ _ensure_rid(0);
+ return TS->font_supported_feature_list(cache[0]);
+}
-FontData::FontData(const String &p_filename, int p_base_size) {
- load_resource(p_filename, p_base_size);
+Dictionary FontData::get_supported_variation_list() const {
+ _ensure_rid(0);
+ return TS->font_supported_variation_list(cache[0]);
}
-FontData::FontData(const PackedByteArray &p_data, const String &p_type, int p_base_size) {
- _load_memory(p_data, p_type, p_base_size);
+FontData::FontData() {
+ /* NOP */
}
FontData::~FontData() {
- if (rid != RID()) {
- TS->free(rid);
- }
+ _clear_cache();
}
/*************************************************************************/
+void Font::_data_changed() {
+ for (int i = 0; i < rids.size(); i++) {
+ rids.write[i] = RID();
+ }
+ emit_changed();
+}
+
+void Font::_ensure_rid(int p_index) const {
+ // Find or create cache record.
+ for (int i = 0; i < rids.size(); i++) {
+ if (!rids[i].is_valid() && data[i].is_valid()) {
+ rids.write[i] = data[i]->find_cache(variation_coordinates);
+ }
+ }
+}
+
void Font::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_data", "data"), &Font::add_data);
ClassDB::bind_method(D_METHOD("set_data", "idx", "data"), &Font::set_data);
ClassDB::bind_method(D_METHOD("get_data_count"), &Font::get_data_count);
ClassDB::bind_method(D_METHOD("get_data", "idx"), &Font::get_data);
+ ClassDB::bind_method(D_METHOD("get_data_rid", "idx"), &Font::get_data_rid);
+ ClassDB::bind_method(D_METHOD("clear_data"), &Font::clear_data);
ClassDB::bind_method(D_METHOD("remove_data", "idx"), &Font::remove_data);
+ ClassDB::bind_method(D_METHOD("set_base_size", "size"), &Font::set_base_size);
+ ClassDB::bind_method(D_METHOD("get_base_size"), &Font::get_base_size);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "base_size"), "set_base_size", "get_base_size");
+
+ ClassDB::bind_method(D_METHOD("set_variation_coordinates", "variation_coordinates"), &Font::set_variation_coordinates);
+ ClassDB::bind_method(D_METHOD("get_variation_coordinates"), &Font::get_variation_coordinates);
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "variation_coordinates"), "set_variation_coordinates", "get_variation_coordinates");
+
+ ClassDB::bind_method(D_METHOD("set_spacing", "spacing", "value"), &Font::set_spacing);
+ ClassDB::bind_method(D_METHOD("get_spacing", "spacing"), &Font::get_spacing);
+
+ ADD_GROUP("Extra Spacing", "spacing");
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_top"), "set_spacing", "get_spacing", TextServer::SPACING_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_bottom"), "set_spacing", "get_spacing", TextServer::SPACING_BOTTOM);
+
ClassDB::bind_method(D_METHOD("get_height", "size"), &Font::get_height, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("get_ascent", "size"), &Font::get_ascent, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("get_descent", "size"), &Font::get_descent, DEFVAL(-1));
-
ClassDB::bind_method(D_METHOD("get_underline_position", "size"), &Font::get_underline_position, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("get_underline_thickness", "size"), &Font::get_underline_thickness, DEFVAL(-1));
- ClassDB::bind_method(D_METHOD("get_spacing", "type"), &Font::get_spacing);
- ClassDB::bind_method(D_METHOD("set_spacing", "type", "value"), &Font::set_spacing);
-
- ClassDB::bind_method(D_METHOD("get_string_size", "text", "size"), &Font::get_string_size, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("get_string_size", "text", "size", "align", "width", "flags"), &Font::get_string_size, DEFVAL(-1), DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
ClassDB::bind_method(D_METHOD("get_multiline_string_size", "text", "width", "size", "flags"), &Font::get_multiline_string_size, DEFVAL(-1), DEFVAL(-1), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND));
ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "align", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "align", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_multiline_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
- ClassDB::bind_method(D_METHOD("has_char", "char"), &Font::has_char);
- ClassDB::bind_method(D_METHOD("get_supported_chars"), &Font::get_supported_chars);
-
ClassDB::bind_method(D_METHOD("get_char_size", "char", "next", "size"), &Font::get_char_size, DEFVAL(0), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "next", "size", "modulate", "outline_size", "outline_modulate"), &Font::draw_char, DEFVAL(0), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)));
- ClassDB::bind_method(D_METHOD("update_changes"), &Font::update_changes);
-
- ADD_GROUP("Extra Spacing", "extra_spacing");
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_top"), "set_spacing", "get_spacing", SPACING_TOP);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_bottom"), "set_spacing", "get_spacing", SPACING_BOTTOM);
-
- BIND_ENUM_CONSTANT(SPACING_TOP);
- BIND_ENUM_CONSTANT(SPACING_BOTTOM);
-}
-
-void Font::_data_changed() {
- cache.clear();
- cache_wrap.clear();
+ ClassDB::bind_method(D_METHOD("has_char", "char"), &Font::has_char);
+ ClassDB::bind_method(D_METHOD("get_supported_chars"), &Font::get_supported_chars);
- emit_changed();
- notify_property_list_changed();
+ ClassDB::bind_method(D_METHOD("update_changes"), &Font::update_changes);
}
bool Font::_set(const StringName &p_name, const Variant &p_value) {
- String str = p_name;
+ Vector<String> tokens = p_name.operator String().split("/");
#ifndef DISABLE_DEPRECATED
- if (str == "font_data") { // Compatibility, DynamicFont main data
+ if (tokens.size() == 1 && tokens[0] == "font_data") {
+ // Compatibility, DynamicFont main data.
Ref<FontData> fd = p_value;
if (fd.is_valid()) {
add_data(fd);
return true;
}
return false;
- } else if (str.begins_with("fallback/")) { // Compatibility, DynamicFont fallback data
+ } else if (tokens.size() == 2 && tokens[0] == "fallback") {
+ // Compatibility, DynamicFont fallback data.
Ref<FontData> fd = p_value;
if (fd.is_valid()) {
add_data(fd);
return true;
}
return false;
- } else if (str == "fallback") { // Compatibility, BitmapFont fallback
+ } else if (tokens.size() == 1 && tokens[0] == "fallback") {
+ // Compatibility, BitmapFont fallback data.
Ref<Font> f = p_value;
if (f.is_valid()) {
for (int i = 0; i < f->get_data_count(); i++) {
@@ -612,10 +1091,9 @@ bool Font::_set(const StringName &p_name, const Variant &p_value) {
return false;
}
#endif /* DISABLE_DEPRECATED */
- if (str.begins_with("data/")) {
- int idx = str.get_slicec('/', 1).to_int();
+ if (tokens.size() == 2 && tokens[0] == "data") {
+ int idx = tokens[1].to_int();
Ref<FontData> fd = p_value;
-
if (fd.is_valid()) {
if (idx == data.size()) {
add_data(fd);
@@ -631,14 +1109,13 @@ bool Font::_set(const StringName &p_name, const Variant &p_value) {
return true;
}
}
-
return false;
}
bool Font::_get(const StringName &p_name, Variant &r_ret) const {
- String str = p_name;
- if (str.begins_with("data/")) {
- int idx = str.get_slicec('/', 1).to_int();
+ Vector<String> tokens = p_name.operator String().split("/");
+ if (tokens.size() == 2 && tokens[0] == "data") {
+ int idx = tokens[1].to_int();
if (idx == data.size()) {
r_ret = Ref<FontData>();
@@ -656,24 +1133,44 @@ void Font::_get_property_list(List<PropertyInfo> *p_list) const {
for (int i = 0; i < data.size(); i++) {
p_list->push_back(PropertyInfo(Variant::OBJECT, "data/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "FontData"));
}
-
p_list->push_back(PropertyInfo(Variant::OBJECT, "data/" + itos(data.size()), PROPERTY_HINT_RESOURCE_TYPE, "FontData"));
}
void Font::reset_state() {
- spacing_top = 0;
- spacing_bottom = 0;
+ for (int i = 0; i < data.size(); i++) {
+ if (data[i].is_valid()) {
+ data.write[i]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ }
+ }
cache.clear();
cache_wrap.clear();
data.clear();
+ rids.clear();
+
+ base_size = 16;
+ variation_coordinates.clear();
+ spacing_bottom = 0;
+ spacing_top = 0;
+}
+
+Dictionary Font::get_feature_list() const {
+ Dictionary out;
+ for (int i = 0; i < data.size(); i++) {
+ Dictionary data_ftrs = data[i]->get_supported_feature_list();
+ for (const Variant *ftr = data_ftrs.next(nullptr); ftr != nullptr; ftr = data_ftrs.next(ftr)) {
+ out[*ftr] = data_ftrs[*ftr];
+ }
+ }
+ return out;
}
void Font::add_data(const Ref<FontData> &p_data) {
ERR_FAIL_COND(p_data.is_null());
data.push_back(p_data);
+ rids.push_back(RID());
if (data[data.size() - 1].is_valid()) {
- data.write[data.size() - 1]->connect("changed", callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ data.write[data.size() - 1]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED);
}
cache.clear();
@@ -688,13 +1185,14 @@ void Font::set_data(int p_idx, const Ref<FontData> &p_data) {
ERR_FAIL_INDEX(p_idx, data.size());
if (data[p_idx].is_valid()) {
- data.write[p_idx]->disconnect("changed", callable_mp(this, &Font::_data_changed));
+ data.write[p_idx]->disconnect(SNAME("changed"), callable_mp(this, &Font::_data_changed));
}
data.write[p_idx] = p_data;
+ rids.write[p_idx] = RID();
if (data[p_idx].is_valid()) {
- data.write[p_idx]->connect("changed", callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ data.write[p_idx]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED);
}
cache.clear();
@@ -713,14 +1211,31 @@ Ref<FontData> Font::get_data(int p_idx) const {
return data[p_idx];
}
+RID Font::get_data_rid(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, data.size(), RID());
+ _ensure_rid(p_idx);
+ return rids[p_idx];
+}
+
+void Font::clear_data() {
+ for (int i = 0; i < data.size(); i++) {
+ if (data[i].is_valid()) {
+ data.write[i]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ }
+ }
+ data.clear();
+ rids.clear();
+}
+
void Font::remove_data(int p_idx) {
ERR_FAIL_INDEX(p_idx, data.size());
if (data[p_idx].is_valid()) {
- data.write[p_idx]->disconnect("changed", callable_mp(this, &Font::_data_changed));
+ data.write[p_idx]->disconnect(SNAME("changed"), callable_mp(this, &Font::_data_changed));
}
data.remove(p_idx);
+ rids.remove(p_idx);
cache.clear();
cache_wrap.clear();
@@ -729,117 +1244,148 @@ void Font::remove_data(int p_idx) {
notify_property_list_changed();
}
-Dictionary Font::get_feature_list() const {
- Dictionary out;
- for (int i = 0; i < data.size(); i++) {
- Dictionary data_ftrs = data[i]->get_feature_list();
- for (const Variant *ftr = data_ftrs.next(nullptr); ftr != nullptr; ftr = data_ftrs.next(ftr)) {
- out[*ftr] = data_ftrs[*ftr];
- }
+void Font::set_base_size(int p_size) {
+ base_size = p_size;
+}
+
+int Font::get_base_size() const {
+ return base_size;
+}
+
+void Font::set_variation_coordinates(const Dictionary &p_variation_coordinates) {
+ _data_changed();
+ variation_coordinates = p_variation_coordinates;
+}
+
+Dictionary Font::get_variation_coordinates() const {
+ return variation_coordinates;
+}
+
+void Font::set_spacing(TextServer::SpacingType p_spacing, int p_value) {
+ _data_changed();
+ switch (p_spacing) {
+ case TextServer::SPACING_TOP: {
+ spacing_top = p_value;
+ } break;
+ case TextServer::SPACING_BOTTOM: {
+ spacing_bottom = p_value;
+ } break;
+ default: {
+ ERR_FAIL_MSG("Invalid spacing type: " + itos(p_spacing));
+ } break;
+ }
+}
+
+int Font::get_spacing(TextServer::SpacingType p_spacing) const {
+ switch (p_spacing) {
+ case TextServer::SPACING_TOP: {
+ return spacing_top;
+ } break;
+ case TextServer::SPACING_BOTTOM: {
+ return spacing_bottom;
+ } break;
+ default: {
+ ERR_FAIL_V_MSG(0, "Invalid spacing type: " + itos(p_spacing));
+ } break;
}
- return out;
}
-float Font::get_height(int p_size) const {
- float ret = 0.f;
+real_t Font::get_height(int p_size) const {
+ int size = (p_size <= 0) ? base_size : p_size;
+ real_t ret = 0.f;
for (int i = 0; i < data.size(); i++) {
- ret = MAX(ret, data[i]->get_height(p_size));
+ _ensure_rid(i);
+ ret = MAX(ret, TS->font_get_ascent(rids[i], size) + TS->font_get_descent(rids[i], size));
}
- return ret + spacing_top + spacing_bottom;
+ return ret + spacing_bottom + spacing_top;
}
-float Font::get_ascent(int p_size) const {
- float ret = 0.f;
+real_t Font::get_ascent(int p_size) const {
+ int size = (p_size <= 0) ? base_size : p_size;
+ real_t ret = 0.f;
for (int i = 0; i < data.size(); i++) {
- ret = MAX(ret, data[i]->get_ascent(p_size));
+ _ensure_rid(i);
+ ret = MAX(ret, TS->font_get_ascent(rids[i], size));
}
return ret + spacing_top;
}
-float Font::get_descent(int p_size) const {
- float ret = 0.f;
+real_t Font::get_descent(int p_size) const {
+ int size = (p_size <= 0) ? base_size : p_size;
+ real_t ret = 0.f;
for (int i = 0; i < data.size(); i++) {
- ret = MAX(ret, data[i]->get_descent(p_size));
+ _ensure_rid(i);
+ ret = MAX(ret, TS->font_get_descent(rids[i], size));
}
return ret + spacing_bottom;
}
-float Font::get_underline_position(int p_size) const {
- float ret = 0.f;
+real_t Font::get_underline_position(int p_size) const {
+ int size = (p_size <= 0) ? base_size : p_size;
+ real_t ret = 0.f;
for (int i = 0; i < data.size(); i++) {
- ret = MAX(ret, data[i]->get_underline_position(p_size));
+ _ensure_rid(i);
+ ret = MAX(ret, TS->font_get_underline_position(rids[i], size));
}
- return ret;
+ return ret + spacing_top;
}
-float Font::get_underline_thickness(int p_size) const {
- float ret = 0.f;
+real_t Font::get_underline_thickness(int p_size) const {
+ int size = (p_size <= 0) ? base_size : p_size;
+ real_t ret = 0.f;
for (int i = 0; i < data.size(); i++) {
- ret = MAX(ret, data[i]->get_underline_thickness(p_size));
+ _ensure_rid(i);
+ ret = MAX(ret, TS->font_get_underline_thickness(rids[i], size));
}
return ret;
}
-int Font::get_spacing(int p_type) const {
- if (p_type == SPACING_TOP) {
- return spacing_top;
- } else if (p_type == SPACING_BOTTOM) {
- return spacing_bottom;
- }
+Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, real_t p_width, uint8_t p_flags) const {
+ ERR_FAIL_COND_V(data.is_empty(), Size2());
- return 0;
-}
+ int size = (p_size <= 0) ? base_size : p_size;
-void Font::set_spacing(int p_type, int p_value) {
- if (p_type == SPACING_TOP) {
- spacing_top = p_value;
- } else if (p_type == SPACING_BOTTOM) {
- spacing_bottom = p_value;
+ for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
}
- emit_changed();
- notify_property_list_changed();
-}
-
-// Drawing string and string sizes, cached.
-
-Size2 Font::get_string_size(const String &p_text, int p_size) const {
- ERR_FAIL_COND_V(data.is_empty(), Size2());
-
uint64_t hash = p_text.hash64();
- hash = hash_djb2_one_64(p_size, hash);
+ if (p_align == HALIGN_FILL) {
+ hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
+ hash = hash_djb2_one_64(p_flags, hash);
+ }
+ hash = hash_djb2_one_64(size, hash);
Ref<TextLine> buffer;
if (cache.has(hash)) {
buffer = cache.get(hash);
} else {
buffer.instantiate();
- int size = p_size <= 0 ? data[0]->get_base_size() : p_size;
buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
cache.insert(hash, buffer);
}
- if (buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) {
- return buffer->get_size() + Vector2(0, spacing_top + spacing_bottom);
- } else {
- return buffer->get_size() + Vector2(spacing_top + spacing_bottom, 0);
- }
+ return buffer->get_size();
}
-Size2 Font::get_multiline_string_size(const String &p_text, float p_width, int p_size, uint8_t p_flags) const {
+Size2 Font::get_multiline_string_size(const String &p_text, real_t p_width, int p_size, uint8_t p_flags) const {
ERR_FAIL_COND_V(data.is_empty(), Size2());
- uint64_t hash = p_text.hash64();
- hash = hash_djb2_one_64(p_size, hash);
+ int size = (p_size <= 0) ? base_size : p_size;
+ for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
+ }
+
+ uint64_t hash = p_text.hash64();
uint64_t wrp_hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
wrp_hash = hash_djb2_one_64(p_flags, wrp_hash);
+ wrp_hash = hash_djb2_one_64(size, wrp_hash);
Ref<TextParagraph> lines_buffer;
if (cache_wrap.has(wrp_hash)) {
lines_buffer = cache_wrap.get(wrp_hash);
} else {
lines_buffer.instantiate();
- int size = p_size <= 0 ? data[0]->get_base_size() : p_size;
lines_buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
lines_buffer->set_width(p_width);
lines_buffer->set_flags(p_flags);
@@ -851,40 +1397,50 @@ Size2 Font::get_multiline_string_size(const String &p_text, float p_width, int p
Size2 line_size = lines_buffer->get_line_size(i);
if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) {
ret.x = MAX(ret.x, line_size.x);
- ret.y += line_size.y + spacing_top + spacing_bottom;
+ ret.y += line_size.y;
} else {
ret.y = MAX(ret.y, line_size.y);
- ret.x += line_size.x + spacing_top + spacing_bottom;
+ ret.x += line_size.x;
}
}
return ret;
}
-void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const {
+void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const {
ERR_FAIL_COND(data.is_empty());
+ int size = (p_size <= 0) ? base_size : p_size;
+
+ for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
+ }
+
uint64_t hash = p_text.hash64();
- hash = hash_djb2_one_64(p_size, hash);
+ if (p_align == HALIGN_FILL) {
+ hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
+ hash = hash_djb2_one_64(p_flags, hash);
+ }
+ hash = hash_djb2_one_64(size, hash);
Ref<TextLine> buffer;
if (cache.has(hash)) {
buffer = cache.get(hash);
} else {
buffer.instantiate();
- int size = p_size <= 0 ? data[0]->get_base_size() : p_size;
buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
cache.insert(hash, buffer);
}
Vector2 ofs = p_pos;
if (buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) {
- ofs.y += spacing_top - buffer->get_line_ascent();
+ ofs.y -= buffer->get_line_ascent();
} else {
- ofs.x += spacing_top - buffer->get_line_ascent();
+ ofs.x -= buffer->get_line_ascent();
}
buffer->set_width(p_width);
buffer->set_align(p_align);
+ buffer->set_flags(p_flags);
if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) {
buffer->draw_outline(p_canvas_item, ofs, p_outline_size, p_outline_modulate);
@@ -895,18 +1451,22 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t
void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const {
ERR_FAIL_COND(data.is_empty());
- uint64_t hash = p_text.hash64();
- hash = hash_djb2_one_64(p_size, hash);
+ int size = (p_size <= 0) ? base_size : p_size;
+ for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
+ }
+
+ uint64_t hash = p_text.hash64();
uint64_t wrp_hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
wrp_hash = hash_djb2_one_64(p_flags, wrp_hash);
+ wrp_hash = hash_djb2_one_64(size, wrp_hash);
Ref<TextParagraph> lines_buffer;
if (cache_wrap.has(wrp_hash)) {
lines_buffer = cache_wrap.get(wrp_hash);
} else {
lines_buffer.instantiate();
- int size = p_size <= 0 ? data[0]->get_base_size() : p_size;
lines_buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
lines_buffer->set_width(p_width);
lines_buffer->set_flags(p_flags);
@@ -918,12 +1478,10 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S
Vector2 lofs = p_pos;
for (int i = 0; i < lines_buffer->get_line_count(); i++) {
if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) {
- lofs.y += spacing_top;
if (i == 0) {
lofs.y -= lines_buffer->get_line_ascent(0);
}
} else {
- lofs.x += spacing_top;
if (i == 0) {
lofs.x -= lines_buffer->get_line_ascent(0);
}
@@ -939,9 +1497,9 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S
Size2 line_size = lines_buffer->get_line_size(i);
if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) {
- lofs.y += line_size.y + spacing_bottom;
+ lofs.y += line_size.y;
} else {
- lofs.x += line_size.x + spacing_bottom;
+ lofs.x += line_size.x;
}
if ((p_max_lines > 0) && (i >= p_max_lines)) {
@@ -950,37 +1508,17 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S
}
}
-bool Font::has_char(char32_t p_char) const {
- for (int i = 0; i < data.size(); i++) {
- if (data[i]->has_char(p_char)) {
- return true;
- }
- }
- return false;
-}
-
-String Font::get_supported_chars() const {
- String chars;
- for (int i = 0; i < data.size(); i++) {
- String data_chars = data[i]->get_supported_chars();
- for (int j = 0; j < data_chars.length(); j++) {
- if (chars.find_char(data_chars[j]) == -1) {
- chars += data_chars[j];
- }
- }
- }
- return chars;
-}
-
Size2 Font::get_char_size(char32_t p_char, char32_t p_next, int p_size) const {
+ int size = (p_size <= 0) ? base_size : p_size;
+
for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
if (data[i]->has_char(p_char)) {
- int size = p_size <= 0 ? data[i]->get_base_size() : p_size;
- uint32_t glyph_a = data[i]->get_glyph_index(p_char);
- Size2 ret = Size2(data[i]->get_glyph_advance(glyph_a, size).x, data[i]->get_height(size));
+ int32_t glyph_a = TS->font_get_glyph_index(rids[i], size, p_char, 0);
+ Size2 ret = Size2(TS->font_get_glyph_advance(rids[i], size, glyph_a).x, TS->font_get_ascent(rids[i], size) + TS->font_get_descent(rids[i], size));
if ((p_next != 0) && data[i]->has_char(p_next)) {
- uint32_t glyph_b = data[i]->get_glyph_index(p_next);
- ret.x -= data[i]->get_glyph_kerning(glyph_a, glyph_b, size).x;
+ int32_t glyph_b = TS->font_get_glyph_index(rids[i], size, p_next, 0);
+ ret.x -= TS->font_get_kerning(rids[i], size, Vector2i(glyph_a, glyph_b)).x;
}
return ret;
}
@@ -988,35 +1526,55 @@ Size2 Font::get_char_size(char32_t p_char, char32_t p_next, int p_size) const {
return Size2();
}
-float Font::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate) const {
+real_t Font::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate) const {
+ int size = (p_size <= 0) ? base_size : p_size;
+
for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
if (data[i]->has_char(p_char)) {
- int size = p_size <= 0 ? data[i]->get_base_size() : p_size;
- uint32_t glyph_a = data[i]->get_glyph_index(p_char);
- float ret = data[i]->get_glyph_advance(glyph_a, size).x;
+ int32_t glyph_a = TS->font_get_glyph_index(rids[i], size, p_char, 0);
+ real_t ret = TS->font_get_glyph_advance(rids[i], size, glyph_a).x;
if ((p_next != 0) && data[i]->has_char(p_next)) {
- uint32_t glyph_b = data[i]->get_glyph_index(p_next);
- ret -= data[i]->get_glyph_kerning(glyph_a, glyph_b, size).x;
+ int32_t glyph_b = TS->font_get_glyph_index(rids[i], size, p_next, 0);
+ ret -= TS->font_get_kerning(rids[i], size, Vector2i(glyph_a, glyph_b)).x;
}
+
if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) {
- data[i]->draw_glyph_outline(p_canvas_item, size, p_outline_size, p_pos, glyph_a, p_outline_modulate);
+ TS->font_draw_glyph_outline(rids[i], p_canvas_item, size, p_outline_size, p_pos, glyph_a, p_outline_modulate);
}
- data[i]->draw_glyph(p_canvas_item, size, p_pos, glyph_a, p_modulate);
+ TS->font_draw_glyph(rids[i], p_canvas_item, size, p_pos, glyph_a, p_modulate);
return ret;
}
}
return 0;
}
-Vector<RID> Font::get_rids() const {
- Vector<RID> ret;
+bool Font::has_char(char32_t p_char) const {
+ for (int i = 0; i < data.size(); i++) {
+ if (data[i]->has_char(p_char))
+ return true;
+ }
+ return false;
+}
+
+String Font::get_supported_chars() const {
+ String chars;
for (int i = 0; i < data.size(); i++) {
- RID rid = data[i]->get_rid();
- if (rid != RID()) {
- ret.push_back(rid);
+ String data_chars = data[i]->get_supported_chars();
+ for (int j = 0; j < data_chars.length(); j++) {
+ if (chars.find_char(data_chars[j]) == -1) {
+ chars += data_chars[j];
+ }
}
}
- return ret;
+ return chars;
+}
+
+Vector<RID> Font::get_rids() const {
+ for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
+ }
+ return rids;
}
void Font::update_changes() {
@@ -1029,103 +1587,7 @@ Font::Font() {
}
Font::~Font() {
+ clear_data();
cache.clear();
cache_wrap.clear();
}
-
-/*************************************************************************/
-
-RES ResourceFormatLoaderFont::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
- if (r_error) {
- *r_error = ERR_FILE_CANT_OPEN;
- }
-
- Ref<FontData> dfont;
- dfont.instantiate();
- dfont->load_resource(p_path);
-
- if (r_error) {
- *r_error = OK;
- }
-
- return dfont;
-}
-
-void ResourceFormatLoaderFont::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const {
-#ifndef DISABLE_DEPRECATED
- if (p_type == "DynamicFontData") {
- p_extensions->push_back("ttf");
- p_extensions->push_back("otf");
- p_extensions->push_back("woff");
- return;
- }
- if (p_type == "BitmapFont") { // BitmapFont (*.font, *fnt) is handled by ResourceFormatLoaderCompatFont
- return;
- }
-#endif /* DISABLE_DEPRECATED */
- if (p_type == "" || handles_type(p_type)) {
- get_recognized_extensions(p_extensions);
- }
-}
-
-void ResourceFormatLoaderFont::get_recognized_extensions(List<String> *p_extensions) const {
- p_extensions->push_back("ttf");
- p_extensions->push_back("otf");
- p_extensions->push_back("woff");
- p_extensions->push_back("font");
- p_extensions->push_back("fnt");
-}
-
-bool ResourceFormatLoaderFont::handles_type(const String &p_type) const {
- return (p_type == "FontData");
-}
-
-String ResourceFormatLoaderFont::get_resource_type(const String &p_path) const {
- String el = p_path.get_extension().to_lower();
- if (el == "ttf" || el == "otf" || el == "woff" || el == "font" || el == "fnt") {
- return "FontData";
- }
- return "";
-}
-
-#ifndef DISABLE_DEPRECATED
-
-RES ResourceFormatLoaderCompatFont::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
- if (r_error) {
- *r_error = ERR_FILE_CANT_OPEN;
- }
-
- Ref<FontData> dfont;
- dfont.instantiate();
- dfont->load_resource(p_path);
-
- Ref<Font> font;
- font.instantiate();
- font->add_data(dfont);
-
- if (r_error) {
- *r_error = OK;
- }
-
- return font;
-}
-
-void ResourceFormatLoaderCompatFont::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const {
- if (p_type == "BitmapFont") {
- p_extensions->push_back("font");
- p_extensions->push_back("fnt");
- }
-}
-
-void ResourceFormatLoaderCompatFont::get_recognized_extensions(List<String> *p_extensions) const {
-}
-
-bool ResourceFormatLoaderCompatFont::handles_type(const String &p_type) const {
- return (p_type == "Font");
-}
-
-String ResourceFormatLoaderCompatFont::get_resource_type(const String &p_path) const {
- return "";
-}
-
-#endif /* DISABLE_DEPRECATED */
diff --git a/scene/resources/font.h b/scene/resources/font.h
index 200373aa8c..9a34edce64 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -41,17 +41,27 @@
class FontData : public Resource {
GDCLASS(FontData, Resource);
+ RES_BASE_EXTENSION("fontdata");
-public:
- enum SpacingType {
- SPACING_GLYPH,
- SPACING_SPACE,
- };
+ // Font source data.
+ const uint8_t *data_ptr = nullptr;
+ size_t data_size = 0;
+ PackedByteArray data;
-private:
- RID rid;
- int base_size = 16;
- String path;
+ bool antialiased = true;
+ bool msdf = false;
+ int msdf_pixel_range = 16;
+ int msdf_size = 48;
+ int fixed_size = 0;
+ bool force_autohinter = false;
+ TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
+ real_t oversampling = 0.f;
+
+ // Cache.
+ mutable Vector<RID> cache;
+
+ _FORCE_INLINE_ void _clear_cache();
+ _FORCE_INLINE_ void _ensure_rid(int p_cache_index) const;
protected:
static void _bind_methods();
@@ -63,79 +73,132 @@ protected:
virtual void reset_state() override;
public:
- virtual RID get_rid() const override;
+ // Font source data.
+ virtual void set_data_ptr(const uint8_t *p_data, size_t p_size);
+ virtual void set_data(const PackedByteArray &p_data);
+ virtual PackedByteArray get_data() const;
- void load_resource(const String &p_filename, int p_base_size = 16);
- void load_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16);
- void _load_memory(const PackedByteArray &p_data, const String &p_type, int p_base_size = 16);
+ // Common properties.
+ virtual void set_antialiased(bool p_antialiased);
+ virtual bool is_antialiased() const;
- void new_bitmap(float p_height, float p_ascent, int p_base_size = 16);
+ virtual void set_multichannel_signed_distance_field(bool p_msdf);
+ virtual bool is_multichannel_signed_distance_field() const;
- void bitmap_add_texture(const Ref<Texture> &p_texture);
- void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance);
- void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning);
+ virtual void set_msdf_pixel_range(int p_msdf_pixel_range);
+ virtual int get_msdf_pixel_range() const;
- void set_data_path(const String &p_path);
- String get_data_path() const;
+ virtual void set_msdf_size(int p_msdf_size);
+ virtual int get_msdf_size() const;
- float get_height(int p_size) const;
- float get_ascent(int p_size) const;
- float get_descent(int p_size) const;
+ virtual void set_fixed_size(int p_fixed_size);
+ virtual int get_fixed_size() const;
- Dictionary get_feature_list() const;
- Dictionary get_variation_list() const;
+ virtual void set_force_autohinter(bool p_force_autohinter);
+ virtual bool is_force_autohinter() const;
- void set_variation(const String &p_name, double p_value);
- double get_variation(const String &p_name) const;
+ virtual void set_hinting(TextServer::Hinting p_hinting);
+ virtual TextServer::Hinting get_hinting() const;
- float get_underline_position(int p_size) const;
- float get_underline_thickness(int p_size) const;
+ virtual void set_oversampling(real_t p_oversampling);
+ virtual real_t get_oversampling() const;
- int get_spacing(int p_type) const;
- void set_spacing(int p_type, int p_value);
+ // Cache.
+ virtual RID find_cache(const Dictionary &p_variation_coordinates) const;
- void set_antialiased(bool p_antialiased);
- bool get_antialiased() const;
+ virtual int get_cache_count() const;
+ virtual void clear_cache();
+ virtual void remove_cache(int p_cache_index);
- void set_distance_field_hint(bool p_distance_field);
- bool get_distance_field_hint() const;
+ virtual Array get_size_cache_list(int p_cache_index) const;
+ virtual void clear_size_cache(int p_cache_index);
+ virtual void remove_size_cache(int p_cache_index, const Vector2i &p_size);
- void set_force_autohinter(bool p_enabeld);
- bool get_force_autohinter() const;
+ virtual void set_variation_coordinates(int p_cache_index, const Dictionary &p_variation_coordinates);
+ virtual Dictionary get_variation_coordinates(int p_cache_index) const;
- void set_hinting(TextServer::Hinting p_hinting);
- TextServer::Hinting get_hinting() const;
+ virtual void set_ascent(int p_cache_index, int p_size, real_t p_ascent);
+ virtual real_t get_ascent(int p_cache_index, int p_size) const;
- bool has_char(char32_t p_char) const;
- String get_supported_chars() const;
+ virtual void set_descent(int p_cache_index, int p_size, real_t p_descent);
+ virtual real_t get_descent(int p_cache_index, int p_size) const;
- Vector2 get_glyph_advance(uint32_t p_index, int p_size) const;
- Vector2 get_glyph_kerning(uint32_t p_index_a, uint32_t p_index_b, int p_size) const;
+ virtual void set_underline_position(int p_cache_index, int p_size, real_t p_underline_position);
+ virtual real_t get_underline_position(int p_cache_index, int p_size) const;
- bool has_outline() const;
- float get_base_size() const;
+ virtual void set_underline_thickness(int p_cache_index, int p_size, real_t p_underline_thickness);
+ virtual real_t get_underline_thickness(int p_cache_index, int p_size) const;
- bool is_language_supported(const String &p_language) const;
- void set_language_support_override(const String &p_language, bool p_supported);
- bool get_language_support_override(const String &p_language) const;
- void remove_language_support_override(const String &p_language);
- Vector<String> get_language_support_overrides() const;
+ virtual void set_scale(int p_cache_index, int p_size, real_t p_scale); // Rendering scale for bitmap fonts (e.g. emoji fonts).
+ virtual real_t get_scale(int p_cache_index, int p_size) const;
- bool is_script_supported(const String &p_script) const;
- void set_script_support_override(const String &p_script, bool p_supported);
- bool get_script_support_override(const String &p_script) const;
- void remove_script_support_override(const String &p_script);
- Vector<String> get_script_support_overrides() const;
+ virtual void set_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing, int p_value);
+ virtual int get_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing) const;
- uint32_t get_glyph_index(char32_t p_char, char32_t p_variation_selector = 0x0000) const;
+ virtual int get_texture_count(int p_cache_index, const Vector2i &p_size) const;
+ virtual void clear_textures(int p_cache_index, const Vector2i &p_size);
+ virtual void remove_texture(int p_cache_index, const Vector2i &p_size, int p_texture_index);
- Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const;
- Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const;
+ virtual void set_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image);
+ virtual Ref<Image> get_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index) const;
- FontData();
- FontData(const String &p_filename, int p_base_size);
- FontData(const PackedByteArray &p_data, const String &p_type, int p_base_size);
+ virtual void set_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset);
+ virtual PackedInt32Array get_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index) const;
+
+ virtual Array get_glyph_list(int p_cache_index, const Vector2i &p_size) const;
+ virtual void clear_glyphs(int p_cache_index, const Vector2i &p_size);
+ virtual void remove_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_glyph);
+
+ virtual void set_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph, const Vector2 &p_advance);
+ virtual Vector2 get_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph) const;
+
+ virtual void set_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset);
+ virtual Vector2 get_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const;
+
+ virtual void set_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size);
+ virtual Vector2 get_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const;
+
+ virtual void set_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect);
+ virtual Rect2 get_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const;
+
+ virtual void set_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx);
+ virtual int get_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const;
+ virtual Array get_kerning_list(int p_cache_index, int p_size) const;
+ virtual void clear_kerning_map(int p_cache_index, int p_size);
+ virtual void remove_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair);
+
+ virtual void set_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning);
+ virtual Vector2 get_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) const;
+
+ virtual void render_range(int p_cache_index, const Vector2i &p_size, char32_t p_start, char32_t p_end);
+ virtual void render_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_index);
+
+ virtual RID get_cache_rid(int p_cache_index) const;
+
+ // Language/script support override.
+ virtual bool is_language_supported(const String &p_language) const;
+ virtual void set_language_support_override(const String &p_language, bool p_supported);
+ virtual bool get_language_support_override(const String &p_language) const;
+ virtual void remove_language_support_override(const String &p_language);
+ virtual Vector<String> get_language_support_overrides() const;
+
+ virtual bool is_script_supported(const String &p_script) const;
+ virtual void set_script_support_override(const String &p_script, bool p_supported);
+ virtual bool get_script_support_override(const String &p_script) const;
+ virtual void remove_script_support_override(const String &p_script);
+ virtual Vector<String> get_script_support_overrides() const;
+
+ // Base font properties.
+ virtual bool has_char(char32_t p_char) const;
+ virtual String get_supported_chars() const;
+
+ virtual int32_t get_glyph_index(int p_size, char32_t p_char, char32_t p_variation_selector = 0x0000) const;
+
+ virtual Dictionary get_supported_feature_list() const;
+ virtual Dictionary get_supported_variation_list() const;
+
+ FontData();
~FontData();
};
@@ -147,20 +210,22 @@ class TextParagraph;
class Font : public Resource {
GDCLASS(Font, Resource);
-public:
- enum SpacingType {
- SPACING_TOP,
- SPACING_BOTTOM,
- };
-
-private:
- int spacing_top = 0;
- int spacing_bottom = 0;
-
+ // Shaped string cache.
mutable LRUCache<uint64_t, Ref<TextLine>> cache;
mutable LRUCache<uint64_t, Ref<TextParagraph>> cache_wrap;
+ // Font data cache.
Vector<Ref<FontData>> data;
+ mutable Vector<RID> rids;
+
+ // Font config.
+ int base_size = 16;
+ Dictionary variation_coordinates;
+ int spacing_bottom = 0;
+ int spacing_top = 0;
+
+ _FORCE_INLINE_ void _data_changed();
+ _FORCE_INLINE_ void _ensure_rid(int p_index) const; // Find or create cache record.
protected:
static void _bind_methods();
@@ -171,41 +236,49 @@ protected:
virtual void reset_state() override;
- void _data_changed();
-
public:
Dictionary get_feature_list() const;
- // Font data control.
- void add_data(const Ref<FontData> &p_data);
- void set_data(int p_idx, const Ref<FontData> &p_data);
- int get_data_count() const;
- Ref<FontData> get_data(int p_idx) const;
- void remove_data(int p_idx);
+ // Font data.
+ virtual void add_data(const Ref<FontData> &p_data);
+ virtual void set_data(int p_idx, const Ref<FontData> &p_data);
+ virtual int get_data_count() const;
+ virtual Ref<FontData> get_data(int p_idx) const;
+ virtual RID get_data_rid(int p_idx) const;
+ virtual void clear_data();
+ virtual void remove_data(int p_idx);
+
+ // Font configuration.
+ virtual void set_base_size(int p_size);
+ virtual int get_base_size() const;
- float get_height(int p_size = -1) const;
- float get_ascent(int p_size = -1) const;
- float get_descent(int p_size = -1) const;
+ virtual void set_variation_coordinates(const Dictionary &p_variation_coordinates);
+ virtual Dictionary get_variation_coordinates() const;
- float get_underline_position(int p_size = -1) const;
- float get_underline_thickness(int p_size = -1) const;
+ virtual void set_spacing(TextServer::SpacingType p_spacing, int p_value);
+ virtual int get_spacing(TextServer::SpacingType p_spacing) const;
- int get_spacing(int p_type) const;
- void set_spacing(int p_type, int p_value);
+ // Font metrics.
+ virtual real_t get_height(int p_size = -1) const;
+ virtual real_t get_ascent(int p_size = -1) const;
+ virtual real_t get_descent(int p_size = -1) const;
+ virtual real_t get_underline_position(int p_size = -1) const;
+ virtual real_t get_underline_thickness(int p_size = -1) const;
// Drawing string.
- Size2 get_string_size(const String &p_text, int p_size = -1) const;
- Size2 get_multiline_string_size(const String &p_text, float p_width = -1, int p_size = -1, uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const;
+ virtual Size2 get_string_size(const String &p_text, int p_size = -1, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, uint8_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ virtual Size2 get_multiline_string_size(const String &p_text, real_t p_width = -1, int p_size = -1, uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const;
- void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, float p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
- void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, float p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ virtual void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ virtual void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
// Helper functions.
- bool has_char(char32_t p_char) const;
- String get_supported_chars() const;
+ virtual bool has_char(char32_t p_char) const;
+ virtual String get_supported_chars() const;
- Size2 get_char_size(char32_t p_char, char32_t p_next = 0, int p_size = -1) const;
- float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const;
+ // Drawing char.
+ virtual Size2 get_char_size(char32_t p_char, char32_t p_next = 0, int p_size = -1) const;
+ virtual real_t draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const;
Vector<RID> get_rids() const;
@@ -215,31 +288,4 @@ public:
~Font();
};
-VARIANT_ENUM_CAST(FontData::SpacingType);
-VARIANT_ENUM_CAST(Font::SpacingType);
-
-/*************************************************************************/
-
-class ResourceFormatLoaderFont : public ResourceFormatLoader {
-public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
- virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const;
- virtual void get_recognized_extensions(List<String> *p_extensions) const;
- virtual bool handles_type(const String &p_type) const;
- virtual String get_resource_type(const String &p_path) const;
-};
-
-#ifndef DISABLE_DEPRECATED
-
-class ResourceFormatLoaderCompatFont : public ResourceFormatLoader {
-public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
- virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const;
- virtual void get_recognized_extensions(List<String> *p_extensions) const;
- virtual bool handles_type(const String &p_type) const;
- virtual String get_resource_type(const String &p_path) const;
-};
-
-#endif /* DISABLE_DEPRECATED */
-
#endif /* FONT_H */
diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp
index 0807a062f2..d2f38ba836 100644
--- a/scene/resources/text_line.cpp
+++ b/scene/resources/text_line.cpp
@@ -211,8 +211,8 @@ void TextLine::set_bidi_override(const Vector<Vector2i> &p_override) {
bool TextLine::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
ERR_FAIL_COND_V(p_fonts.is_null(), false);
bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
- spacing_top = p_fonts->get_spacing(Font::SPACING_TOP);
- spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM);
+ spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP);
+ spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM);
dirty = true;
return res;
}
@@ -409,8 +409,8 @@ int TextLine::hit_test(float p_coords) const {
TextLine::TextLine(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
rid = TS->create_shaped_text(p_direction, p_orientation);
- spacing_top = p_fonts->get_spacing(Font::SPACING_TOP);
- spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM);
+ spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP);
+ spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM);
TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
}
diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp
index 763033354a..62949b9b98 100644
--- a/scene/resources/text_paragraph.cpp
+++ b/scene/resources/text_paragraph.cpp
@@ -333,8 +333,8 @@ void TextParagraph::clear_dropcap() {
bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
ERR_FAIL_COND_V(p_fonts.is_null(), false);
bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
- spacing_top = p_fonts->get_spacing(Font::SPACING_TOP);
- spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM);
+ spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP);
+ spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM);
lines_dirty = true;
return res;
}
@@ -829,8 +829,8 @@ void TextParagraph::draw_line_outline(RID p_canvas, const Vector2 &p_pos, int p_
TextParagraph::TextParagraph(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, float p_width, TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
rid = TS->create_shaped_text(p_direction, p_orientation);
TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
- spacing_top = p_fonts->get_spacing(Font::SPACING_TOP);
- spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM);
+ spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP);
+ spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM);
width = p_width;
}
diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp
index fd7d5b91fa..efa3a457d3 100644
--- a/servers/rendering/renderer_canvas_cull.cpp
+++ b/servers/rendering/renderer_canvas_cull.cpp
@@ -806,6 +806,40 @@ void RendererCanvasCull::canvas_item_add_texture_rect(RID p_item, const Rect2 &p
rect->texture = p_texture;
}
+void RendererCanvasCull::canvas_item_add_msdf_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate, int p_outline_size, float p_px_range) {
+ Item *canvas_item = canvas_item_owner.getornull(p_item);
+ ERR_FAIL_COND(!canvas_item);
+
+ Item::CommandRect *rect = canvas_item->alloc_command<Item::CommandRect>();
+ ERR_FAIL_COND(!rect);
+ rect->modulate = p_modulate;
+ rect->rect = p_rect;
+
+ rect->texture = p_texture;
+
+ rect->source = p_src_rect;
+ rect->flags = RendererCanvasRender::CANVAS_RECT_REGION | RendererCanvasRender::CANVAS_RECT_MSDF;
+
+ if (p_rect.size.x < 0) {
+ rect->flags |= RendererCanvasRender::CANVAS_RECT_FLIP_H;
+ rect->rect.size.x = -rect->rect.size.x;
+ }
+ if (p_src_rect.size.x < 0) {
+ rect->flags ^= RendererCanvasRender::CANVAS_RECT_FLIP_H;
+ rect->source.size.x = -rect->source.size.x;
+ }
+ if (p_rect.size.y < 0) {
+ rect->flags |= RendererCanvasRender::CANVAS_RECT_FLIP_V;
+ rect->rect.size.y = -rect->rect.size.y;
+ }
+ if (p_src_rect.size.y < 0) {
+ rect->flags ^= RendererCanvasRender::CANVAS_RECT_FLIP_V;
+ rect->source.size.y = -rect->source.size.y;
+ }
+ rect->outline = p_outline_size;
+ rect->px_range = p_px_range;
+}
+
void RendererCanvasCull::canvas_item_add_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) {
Item *canvas_item = canvas_item_owner.getornull(p_item);
ERR_FAIL_COND(!canvas_item);
diff --git a/servers/rendering/renderer_canvas_cull.h b/servers/rendering/renderer_canvas_cull.h
index 79b5450d14..5e343dcf02 100644
--- a/servers/rendering/renderer_canvas_cull.h
+++ b/servers/rendering/renderer_canvas_cull.h
@@ -222,6 +222,7 @@ public:
void canvas_item_add_circle(RID p_item, const Point2 &p_pos, float p_radius, const Color &p_color);
void canvas_item_add_texture_rect(RID p_item, const Rect2 &p_rect, RID p_texture, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false);
void canvas_item_add_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = false);
+ void canvas_item_add_msdf_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, float p_px_range = 1.0);
void canvas_item_add_nine_patch(RID p_item, const Rect2 &p_rect, const Rect2 &p_source, RID p_texture, const Vector2 &p_topleft, const Vector2 &p_bottomright, RS::NinePatchAxisMode p_x_axis_mode = RS::NINE_PATCH_STRETCH, RS::NinePatchAxisMode p_y_axis_mode = RS::NINE_PATCH_STRETCH, bool p_draw_center = true, const Color &p_modulate = Color(1, 1, 1));
void canvas_item_add_primitive(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture, float p_width = 1.0);
void canvas_item_add_polygon(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID());
diff --git a/servers/rendering/renderer_canvas_render.h b/servers/rendering/renderer_canvas_render.h
index 8afe9ef410..04ddae4089 100644
--- a/servers/rendering/renderer_canvas_render.h
+++ b/servers/rendering/renderer_canvas_render.h
@@ -45,6 +45,7 @@ public:
CANVAS_RECT_TRANSPOSE = 16,
CANVAS_RECT_CLIP_UV = 32,
CANVAS_RECT_IS_GROUP = 64,
+ CANVAS_RECT_MSDF = 128,
};
struct Light {
@@ -193,11 +194,15 @@ public:
Color modulate;
Rect2 source;
uint8_t flags;
+ float outline;
+ float px_range;
RID texture;
CommandRect() {
flags = 0;
+ outline = 0;
+ px_range = 1;
type = TYPE_RECT;
}
};
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index 3af5047854..6267f684e3 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -541,6 +541,14 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
src_rect = Rect2(0, 0, 1, 1);
}
+ if (rect->flags & CANVAS_RECT_MSDF) {
+ push_constant.flags |= FLAGS_USE_MSDF;
+ push_constant.msdf[0] = rect->px_range; // Pixel range.
+ push_constant.msdf[1] = rect->outline; // Outline size.
+ push_constant.msdf[2] = 0.f; // Reserved.
+ push_constant.msdf[3] = 0.f; // Reserved.
+ }
+
push_constant.modulation[0] = rect->modulate.r * base_color.r;
push_constant.modulation[1] = rect->modulate.g * base_color.g;
push_constant.modulation[2] = rect->modulate.b * base_color.b;
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
index 7c4f62832c..ec7d7e2854 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
@@ -84,8 +84,9 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
FLAGS_LIGHT_COUNT_SHIFT = 20,
FLAGS_DEFAULT_NORMAL_MAP_USED = (1 << 26),
- FLAGS_DEFAULT_SPECULAR_MAP_USED = (1 << 27)
+ FLAGS_DEFAULT_SPECULAR_MAP_USED = (1 << 27),
+ FLAGS_USE_MSDF = (1 << 28),
};
enum {
@@ -388,7 +389,10 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
//rect
struct {
float modulation[4];
- float ninepatch_margins[4];
+ union {
+ float msdf[4];
+ float ninepatch_margins[4];
+ };
float dst_rect[4];
float src_rect[4];
float pad[2];
diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl
index a443bcdcb8..2911e8b731 100644
--- a/servers/rendering/renderer_rd/shaders/canvas.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas.glsl
@@ -458,6 +458,14 @@ void light_blend_compute(uint light_base, vec4 light_color, inout vec3 color) {
#endif
+float msdf_median(float r, float g, float b, float a) {
+ return min(max(min(r, g), min(max(r, g), b)), a);
+}
+
+vec2 msdf_map(vec2 value, vec2 in_min, vec2 in_max, vec2 out_min, vec2 out_max) {
+ return out_min + (out_max - out_min) * (value - in_min) / (in_max - in_min);
+}
+
void main() {
vec4 color = color_interp;
vec2 uv = uv_interp;
@@ -485,7 +493,34 @@ void main() {
#endif
- color *= texture(sampler2D(color_texture, texture_sampler), uv);
+#ifndef USE_PRIMITIVE
+ if (bool(draw_data.flags & FLAGS_USE_MSDF)) {
+ float px_range = draw_data.ninepatch_margins.x;
+ float outline_thickness = draw_data.ninepatch_margins.y;
+ //float reserved1 = draw_data.ninepatch_margins.z;
+ //float reserved2 = draw_data.ninepatch_margins.w;
+
+ vec4 msdf_sample = texture(sampler2D(color_texture, texture_sampler), uv);
+ vec2 msdf_size = vec2(textureSize(sampler2D(color_texture, texture_sampler), 0));
+ vec2 dest_size = vec2(1.0) / fwidth(uv);
+ float px_size = max(0.5 * dot((vec2(px_range) / msdf_size), dest_size), 1.0);
+ float d = msdf_median(msdf_sample.r, msdf_sample.g, msdf_sample.b, msdf_sample.a) - 0.5;
+
+ if (outline_thickness > 0) {
+ float cr = clamp(outline_thickness, 0.0, px_range / 2) / px_range;
+ float a = clamp((d + cr) * px_size, 0.0, 1.0);
+ color.a = a * color.a;
+ } else {
+ float a = clamp(d * px_size + 0.5, 0.0, 1.0);
+ color.a = a * color.a;
+ }
+
+ } else {
+#else
+ {
+#endif
+ color *= texture(sampler2D(color_texture, texture_sampler), uv);
+ }
uint light_count = (draw_data.flags >> FLAGS_LIGHT_COUNT_SHIFT) & 0xF; //max 16 lights
bool using_light = light_count > 0 || canvas_data.directional_light_count > 0;
diff --git a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
index 451f9b0089..0cff505cae 100644
--- a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
@@ -24,6 +24,8 @@
#define FLAGS_DEFAULT_NORMAL_MAP_USED (1 << 26)
#define FLAGS_DEFAULT_SPECULAR_MAP_USED (1 << 27)
+#define FLAGS_USE_MSDF (1 << 28)
+
#define SAMPLER_NEAREST_CLAMP 0
#define SAMPLER_LINEAR_CLAMP 1
#define SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP 2
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index 3386b99f53..56e79b62f2 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -763,6 +763,7 @@ public:
FUNC4(canvas_item_add_circle, RID, const Point2 &, float, const Color &)
FUNC6(canvas_item_add_texture_rect, RID, const Rect2 &, RID, bool, const Color &, bool)
FUNC7(canvas_item_add_texture_rect_region, RID, const Rect2 &, RID, const Rect2 &, const Color &, bool, bool)
+ FUNC7(canvas_item_add_msdf_texture_rect_region, RID, const Rect2 &, RID, const Rect2 &, const Color &, int, float)
FUNC10(canvas_item_add_nine_patch, RID, const Rect2 &, const Rect2 &, RID, const Vector2 &, const Vector2 &, NinePatchAxisMode, NinePatchAxisMode, bool, const Color &)
FUNC6(canvas_item_add_primitive, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, float)
FUNC5(canvas_item_add_polygon, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID)
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 6f80b52faa..8fbf231d9b 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -1258,6 +1258,7 @@ public:
virtual void canvas_item_add_circle(RID p_item, const Point2 &p_pos, float p_radius, const Color &p_color) = 0;
virtual void canvas_item_add_texture_rect(RID p_item, const Rect2 &p_rect, RID p_texture, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) = 0;
virtual void canvas_item_add_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = false) = 0;
+ virtual void canvas_item_add_msdf_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, float p_px_range = 1.0) = 0;
virtual void canvas_item_add_nine_patch(RID p_item, const Rect2 &p_rect, const Rect2 &p_source, RID p_texture, const Vector2 &p_topleft, const Vector2 &p_bottomright, NinePatchAxisMode p_x_axis_mode = NINE_PATCH_STRETCH, NinePatchAxisMode p_y_axis_mode = NINE_PATCH_STRETCH, bool p_draw_center = true, const Color &p_modulate = Color(1, 1, 1)) = 0;
virtual void canvas_item_add_primitive(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture, float p_width = 1.0) = 0;
virtual void canvas_item_add_polygon(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID()) = 0;
diff --git a/servers/text_server.cpp b/servers/text_server.cpp
index e51e229418..c05ff78718 100644
--- a/servers/text_server.cpp
+++ b/servers/text_server.cpp
@@ -216,83 +216,130 @@ void TextServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("free_rid", "rid"), &TextServer::free); // shouldn't conflict with Object::free()
/* Font Interface */
- ClassDB::bind_method(D_METHOD("create_font_system", "name", "base_size"), &TextServer::create_font_system, DEFVAL(16));
- ClassDB::bind_method(D_METHOD("create_font_resource", "filename", "base_size"), &TextServer::create_font_resource, DEFVAL(16));
- ClassDB::bind_method(D_METHOD("create_font_memory", "data", "type", "base_size"), &TextServer::_create_font_memory, DEFVAL(16));
- ClassDB::bind_method(D_METHOD("create_font_bitmap", "height", "ascent", "base_size"), &TextServer::create_font_bitmap);
- ClassDB::bind_method(D_METHOD("font_bitmap_add_texture", "font", "texture"), &TextServer::font_bitmap_add_texture);
- ClassDB::bind_method(D_METHOD("font_bitmap_add_char", "font", "char", "texture_idx", "rect", "align", "advance"), &TextServer::font_bitmap_add_char);
- ClassDB::bind_method(D_METHOD("font_bitmap_add_kerning_pair", "font", "A", "B", "kerning"), &TextServer::font_bitmap_add_kerning_pair);
+ ClassDB::bind_method(D_METHOD("create_font"), &TextServer::create_font);
- ClassDB::bind_method(D_METHOD("font_get_height", "font", "size"), &TextServer::font_get_height);
- ClassDB::bind_method(D_METHOD("font_get_ascent", "font", "size"), &TextServer::font_get_ascent);
- ClassDB::bind_method(D_METHOD("font_get_descent", "font", "size"), &TextServer::font_get_descent);
+ ClassDB::bind_method(D_METHOD("font_set_data", "data"), &TextServer::font_set_data);
- ClassDB::bind_method(D_METHOD("font_get_underline_position", "font", "size"), &TextServer::font_get_underline_position);
- ClassDB::bind_method(D_METHOD("font_get_underline_thickness", "font", "size"), &TextServer::font_get_underline_thickness);
+ ClassDB::bind_method(D_METHOD("font_set_antialiased", "font_rid", "antialiased"), &TextServer::font_set_antialiased);
+ ClassDB::bind_method(D_METHOD("font_is_antialiased", "font_rid"), &TextServer::font_is_antialiased);
- ClassDB::bind_method(D_METHOD("font_get_spacing_space", "font"), &TextServer::font_get_spacing_space);
- ClassDB::bind_method(D_METHOD("font_set_spacing_space", "font", "value"), &TextServer::font_set_spacing_space);
+ ClassDB::bind_method(D_METHOD("font_set_multichannel_signed_distance_field", "font_rid", "msdf"), &TextServer::font_set_multichannel_signed_distance_field);
+ ClassDB::bind_method(D_METHOD("font_is_multichannel_signed_distance_field", "font_rid"), &TextServer::font_is_multichannel_signed_distance_field);
- ClassDB::bind_method(D_METHOD("font_get_spacing_glyph", "font"), &TextServer::font_get_spacing_glyph);
- ClassDB::bind_method(D_METHOD("font_set_spacing_glyph", "font", "value"), &TextServer::font_set_spacing_glyph);
+ ClassDB::bind_method(D_METHOD("font_set_msdf_pixel_range", "font_rid", "msdf_pixel_range"), &TextServer::font_set_msdf_pixel_range);
+ ClassDB::bind_method(D_METHOD("font_get_msdf_pixel_range", "font_rid"), &TextServer::font_get_msdf_pixel_range);
- ClassDB::bind_method(D_METHOD("font_set_antialiased", "font", "antialiased"), &TextServer::font_set_antialiased);
- ClassDB::bind_method(D_METHOD("font_get_antialiased", "font"), &TextServer::font_get_antialiased);
+ ClassDB::bind_method(D_METHOD("font_set_msdf_size", "font_rid", "msdf_size"), &TextServer::font_set_msdf_size);
+ ClassDB::bind_method(D_METHOD("font_get_msdf_size", "font_rid"), &TextServer::font_get_msdf_size);
- ClassDB::bind_method(D_METHOD("font_get_feature_list", "font"), &TextServer::font_get_feature_list);
- ClassDB::bind_method(D_METHOD("font_get_variation_list", "font"), &TextServer::font_get_variation_list);
+ ClassDB::bind_method(D_METHOD("font_set_fixed_size", "font_rid", "fixed_size"), &TextServer::font_set_fixed_size);
+ ClassDB::bind_method(D_METHOD("font_get_fixed_size", "font_rid"), &TextServer::font_get_fixed_size);
- ClassDB::bind_method(D_METHOD("font_set_variation", "font", "tag", "value"), &TextServer::font_set_variation);
- ClassDB::bind_method(D_METHOD("font_get_variation", "font", "tag"), &TextServer::font_get_variation);
+ ClassDB::bind_method(D_METHOD("font_set_force_autohinter", "font_rid", "force_autohinter"), &TextServer::font_set_force_autohinter);
+ ClassDB::bind_method(D_METHOD("font_is_force_autohinter", "font_rid"), &TextServer::font_is_force_autohinter);
- ClassDB::bind_method(D_METHOD("font_set_hinting", "font", "hinting"), &TextServer::font_set_hinting);
- ClassDB::bind_method(D_METHOD("font_get_hinting", "font"), &TextServer::font_get_hinting);
+ ClassDB::bind_method(D_METHOD("font_set_hinting", "font_rid", "_hinting"), &TextServer::font_set_hinting);
+ ClassDB::bind_method(D_METHOD("font_get_hinting", "font_rid"), &TextServer::font_get_hinting);
- ClassDB::bind_method(D_METHOD("font_set_distance_field_hint", "font", "distance_field"), &TextServer::font_set_distance_field_hint);
- ClassDB::bind_method(D_METHOD("font_get_distance_field_hint", "font"), &TextServer::font_get_distance_field_hint);
+ ClassDB::bind_method(D_METHOD("font_set_variation_coordinates", "font_rid", "variation_coordinates"), &TextServer::font_set_variation_coordinates);
+ ClassDB::bind_method(D_METHOD("font_get_variation_coordinates", "font_rid"), &TextServer::font_get_variation_coordinates);
- ClassDB::bind_method(D_METHOD("font_set_force_autohinter", "font", "enabeld"), &TextServer::font_set_force_autohinter);
- ClassDB::bind_method(D_METHOD("font_get_force_autohinter", "font"), &TextServer::font_get_force_autohinter);
+ ClassDB::bind_method(D_METHOD("font_set_oversampling", "font_rid", "oversampling"), &TextServer::font_set_oversampling);
+ ClassDB::bind_method(D_METHOD("font_get_oversampling", "font_rid"), &TextServer::font_get_oversampling);
- ClassDB::bind_method(D_METHOD("font_has_char", "font", "char"), &TextServer::font_has_char);
- ClassDB::bind_method(D_METHOD("font_get_supported_chars", "font"), &TextServer::font_get_supported_chars);
+ ClassDB::bind_method(D_METHOD("font_get_size_cache_list", "font_rid"), &TextServer::font_get_size_cache_list);
+ ClassDB::bind_method(D_METHOD("font_clear_size_cache", "font_rid"), &TextServer::font_clear_size_cache);
+ ClassDB::bind_method(D_METHOD("font_remove_size_cache", "font_rid", "size"), &TextServer::font_remove_size_cache);
- ClassDB::bind_method(D_METHOD("font_has_outline", "font"), &TextServer::font_has_outline);
- ClassDB::bind_method(D_METHOD("font_get_base_size", "font"), &TextServer::font_get_base_size);
+ ClassDB::bind_method(D_METHOD("font_set_ascent", "font_rid", "size", "ascent"), &TextServer::font_set_ascent);
+ ClassDB::bind_method(D_METHOD("font_get_ascent", "font_rid", "size"), &TextServer::font_get_ascent);
- ClassDB::bind_method(D_METHOD("font_is_language_supported", "font", "language"), &TextServer::font_is_language_supported);
- ClassDB::bind_method(D_METHOD("font_set_language_support_override", "font", "language", "supported"), &TextServer::font_set_language_support_override);
+ ClassDB::bind_method(D_METHOD("font_set_descent", "font_rid", "size", "descent"), &TextServer::font_set_descent);
+ ClassDB::bind_method(D_METHOD("font_get_descent", "font_rid", "size"), &TextServer::font_get_descent);
- ClassDB::bind_method(D_METHOD("font_get_language_support_override", "font", "language"), &TextServer::font_get_language_support_override);
- ClassDB::bind_method(D_METHOD("font_remove_language_support_override", "font", "language"), &TextServer::font_remove_language_support_override);
- ClassDB::bind_method(D_METHOD("font_get_language_support_overrides", "font"), &TextServer::font_get_language_support_overrides);
+ ClassDB::bind_method(D_METHOD("font_set_underline_position", "font_rid", "size", "underline_position"), &TextServer::font_set_underline_position);
+ ClassDB::bind_method(D_METHOD("font_get_underline_position", "font_rid", "size"), &TextServer::font_get_underline_position);
- ClassDB::bind_method(D_METHOD("font_is_script_supported", "font", "script"), &TextServer::font_is_script_supported);
- ClassDB::bind_method(D_METHOD("font_set_script_support_override", "font", "script", "supported"), &TextServer::font_set_script_support_override);
+ ClassDB::bind_method(D_METHOD("font_set_underline_thickness", "font_rid", "size", "underline_thickness"), &TextServer::font_set_underline_thickness);
+ ClassDB::bind_method(D_METHOD("font_get_underline_thickness", "font_rid", "size"), &TextServer::font_get_underline_thickness);
- ClassDB::bind_method(D_METHOD("font_get_script_support_override", "font", "script"), &TextServer::font_get_script_support_override);
- ClassDB::bind_method(D_METHOD("font_remove_script_support_override", "font", "script"), &TextServer::font_remove_script_support_override);
- ClassDB::bind_method(D_METHOD("font_get_script_support_overrides", "font"), &TextServer::font_get_script_support_overrides);
+ ClassDB::bind_method(D_METHOD("font_set_scale", "font_rid", "size", "scale"), &TextServer::font_set_scale);
+ ClassDB::bind_method(D_METHOD("font_get_scale", "font_rid", "size"), &TextServer::font_get_scale);
- ClassDB::bind_method(D_METHOD("font_get_glyph_index", "font", "char", "variation_selector"), &TextServer::font_get_glyph_index, DEFVAL(0x0000));
- ClassDB::bind_method(D_METHOD("font_get_glyph_advance", "font", "index", "size"), &TextServer::font_get_glyph_advance);
- ClassDB::bind_method(D_METHOD("font_get_glyph_kerning", "font", "index_a", "index_b", "size"), &TextServer::font_get_glyph_kerning);
+ ClassDB::bind_method(D_METHOD("font_set_spacing", "font_rid", "size", "spacing", "value"), &TextServer::font_set_spacing);
+ ClassDB::bind_method(D_METHOD("font_get_spacing", "font_rid", "size", "spacing"), &TextServer::font_get_spacing);
- ClassDB::bind_method(D_METHOD("font_draw_glyph", "font", "canvas", "size", "pos", "index", "color"), &TextServer::font_draw_glyph, DEFVAL(Color(1, 1, 1)));
- ClassDB::bind_method(D_METHOD("font_draw_glyph_outline", "font", "canvas", "size", "outline_size", "pos", "index", "color"), &TextServer::font_draw_glyph_outline, DEFVAL(Color(1, 1, 1)));
+ ClassDB::bind_method(D_METHOD("font_get_texture_count", "font_rid", "size"), &TextServer::font_get_texture_count);
+ ClassDB::bind_method(D_METHOD("font_clear_textures", "font_rid", "size"), &TextServer::font_clear_textures);
+ ClassDB::bind_method(D_METHOD("font_remove_texture", "font_rid", "size", "texture_index"), &TextServer::font_remove_texture);
- ClassDB::bind_method(D_METHOD("font_get_oversampling"), &TextServer::font_get_oversampling);
- ClassDB::bind_method(D_METHOD("font_set_oversampling", "oversampling"), &TextServer::font_set_oversampling);
+ ClassDB::bind_method(D_METHOD("font_set_texture_image", "font_rid", "size", "texture_index", "image"), &TextServer::font_set_texture_image);
+ ClassDB::bind_method(D_METHOD("font_get_texture_image", "font_rid", "size", "texture_index"), &TextServer::font_get_texture_image);
- ClassDB::bind_method(D_METHOD("get_system_fonts"), &TextServer::get_system_fonts);
+ ClassDB::bind_method(D_METHOD("font_set_texture_offsets", "font_rid", "size", "texture_index", "offset"), &TextServer::font_set_texture_offsets);
+ ClassDB::bind_method(D_METHOD("font_get_texture_offsets", "font_rid", "size", "texture_index"), &TextServer::font_get_texture_offsets);
- ClassDB::bind_method(D_METHOD("get_hex_code_box_size", "size", "index"), &TextServer::get_hex_code_box_size);
- ClassDB::bind_method(D_METHOD("draw_hex_code_box", "canvas", "size", "pos", "index", "color"), &TextServer::draw_hex_code_box);
+ ClassDB::bind_method(D_METHOD("font_get_glyph_list", "font_rid", "size"), &TextServer::font_get_glyph_list);
+ ClassDB::bind_method(D_METHOD("font_clear_glyphs", "font_rid", "size"), &TextServer::font_clear_glyphs);
+ ClassDB::bind_method(D_METHOD("font_remove_glyph", "font_rid", "size", "glyph"), &TextServer::font_remove_glyph);
+
+ ClassDB::bind_method(D_METHOD("font_get_glyph_advance", "font_rid", "size", "glyph"), &TextServer::font_get_glyph_advance);
+ ClassDB::bind_method(D_METHOD("font_set_glyph_advance", "font_rid", "size", "glyph", "advance"), &TextServer::font_set_glyph_advance);
+
+ ClassDB::bind_method(D_METHOD("font_get_glyph_offset", "font_rid", "size", "glyph"), &TextServer::font_get_glyph_offset);
+ ClassDB::bind_method(D_METHOD("font_set_glyph_offset", "font_rid", "size", "glyph", "offset"), &TextServer::font_set_glyph_offset);
+
+ ClassDB::bind_method(D_METHOD("font_get_glyph_size", "font_rid", "size", "glyph"), &TextServer::font_get_glyph_size);
+ ClassDB::bind_method(D_METHOD("font_set_glyph_size", "font_rid", "size", "glyph", "size"), &TextServer::font_set_glyph_size);
+
+ ClassDB::bind_method(D_METHOD("font_get_glyph_uv_rect", "font_rid", "size", "glyph"), &TextServer::font_get_glyph_uv_rect);
+ ClassDB::bind_method(D_METHOD("font_set_glyph_uv_rect", "font_rid", "size", "glyph", "uv_rect"), &TextServer::font_set_glyph_uv_rect);
+
+ ClassDB::bind_method(D_METHOD("font_get_glyph_texture_idx", "font_rid", "size", "glyph"), &TextServer::font_get_glyph_texture_idx);
+ ClassDB::bind_method(D_METHOD("font_set_glyph_texture_idx", "font_rid", "size", "glyph", "texture_idx"), &TextServer::font_set_glyph_texture_idx);
ClassDB::bind_method(D_METHOD("font_get_glyph_contours", "font", "size", "index"), &TextServer::_font_get_glyph_contours);
+ ClassDB::bind_method(D_METHOD("font_get_kerning_list", "font_rid", "size"), &TextServer::font_get_kerning_list);
+ ClassDB::bind_method(D_METHOD("font_clear_kerning_map", "font_rid", "size"), &TextServer::font_clear_kerning_map);
+ ClassDB::bind_method(D_METHOD("font_remove_kerning", "font_rid", "size", "glyph_pair"), &TextServer::font_remove_kerning);
+
+ ClassDB::bind_method(D_METHOD("font_set_kerning", "font_rid", "size", "glyph_pair", "kerning"), &TextServer::font_set_kerning);
+ ClassDB::bind_method(D_METHOD("font_get_kerning", "font_rid", "size", "glyph_pair"), &TextServer::font_get_kerning);
+
+ ClassDB::bind_method(D_METHOD("font_get_glyph_index", "font_rid", "size", "char", "variation_selector"), &TextServer::font_get_glyph_index);
+
+ ClassDB::bind_method(D_METHOD("font_has_char", "font_rid", "char"), &TextServer::font_has_char);
+ ClassDB::bind_method(D_METHOD("font_get_supported_chars", "font_rid"), &TextServer::font_get_supported_chars);
+
+ ClassDB::bind_method(D_METHOD("font_render_range", "font_rid", "size", "start", "end"), &TextServer::font_render_range);
+ ClassDB::bind_method(D_METHOD("font_render_glyph", "font_rid", "size", "index"), &TextServer::font_render_glyph);
+
+ ClassDB::bind_method(D_METHOD("font_draw_glyph", "font_rid", "canvas", "size", "pos", "index", "color"), &TextServer::font_draw_glyph, DEFVAL(Color(1, 1, 1)));
+ ClassDB::bind_method(D_METHOD("font_draw_glyph_outline", "font_rid", "canvas", "size", "outline_size", "pos", "index", "color"), &TextServer::font_draw_glyph_outline, DEFVAL(Color(1, 1, 1)));
+
+ ClassDB::bind_method(D_METHOD("font_is_language_supported", "font_rid", "language"), &TextServer::font_is_language_supported);
+ ClassDB::bind_method(D_METHOD("font_set_language_support_override", "font_rid", "language", "supported"), &TextServer::font_set_language_support_override);
+ ClassDB::bind_method(D_METHOD("font_get_language_support_override", "font_rid", "language"), &TextServer::font_get_language_support_override);
+ ClassDB::bind_method(D_METHOD("font_remove_language_support_override", "font_rid", "language"), &TextServer::font_remove_language_support_override);
+ ClassDB::bind_method(D_METHOD("font_get_language_support_overrides", "font_rid"), &TextServer::font_get_language_support_overrides);
+
+ ClassDB::bind_method(D_METHOD("font_is_script_supported", "font_rid", "script"), &TextServer::font_is_script_supported);
+ ClassDB::bind_method(D_METHOD("font_set_script_support_override", "font_rid", "script", "supported"), &TextServer::font_set_script_support_override);
+ ClassDB::bind_method(D_METHOD("font_get_script_support_override", "font_rid", "script"), &TextServer::font_get_script_support_override);
+ ClassDB::bind_method(D_METHOD("font_remove_script_support_override", "font_rid", "script"), &TextServer::font_remove_script_support_override);
+ ClassDB::bind_method(D_METHOD("font_get_script_support_overrides", "font_rid"), &TextServer::font_get_script_support_overrides);
+
+ ClassDB::bind_method(D_METHOD("font_supported_feature_list", "font_rid"), &TextServer::font_supported_feature_list);
+ ClassDB::bind_method(D_METHOD("font_supported_variation_list", "font_rid"), &TextServer::font_supported_variation_list);
+
+ ClassDB::bind_method(D_METHOD("font_get_global_oversampling"), &TextServer::font_get_global_oversampling);
+ ClassDB::bind_method(D_METHOD("font_set_global_oversampling", "oversampling"), &TextServer::font_set_global_oversampling);
+
+ ClassDB::bind_method(D_METHOD("get_hex_code_box_size", "size", "index"), &TextServer::get_hex_code_box_size);
+ ClassDB::bind_method(D_METHOD("draw_hex_code_box", "canvas", "size", "pos", "index", "color"), &TextServer::draw_hex_code_box);
+
/* Shaped text buffer interface */
ClassDB::bind_method(D_METHOD("create_shaped_text", "direction", "orientation"), &TextServer::create_shaped_text, DEFVAL(DIRECTION_AUTO), DEFVAL(ORIENTATION_HORIZONTAL));
@@ -420,6 +467,12 @@ void TextServer::_bind_methods() {
BIND_ENUM_CONSTANT(CONTOUR_CURVE_TAG_ON);
BIND_ENUM_CONSTANT(CONTOUR_CURVE_TAG_OFF_CONIC);
BIND_ENUM_CONSTANT(CONTOUR_CURVE_TAG_OFF_CUBIC);
+
+ /* Font Spacing*/
+ BIND_ENUM_CONSTANT(SPACING_GLYPH);
+ BIND_ENUM_CONSTANT(SPACING_SPACE);
+ BIND_ENUM_CONSTANT(SPACING_TOP);
+ BIND_ENUM_CONSTANT(SPACING_BOTTOM);
}
Vector3 TextServer::hex_code_box_font_size[2] = { Vector3(5, 5, 1), Vector3(10, 10, 2) };
@@ -514,8 +567,8 @@ void TextServer::finish_hex_code_box_fonts() {
Vector2 TextServer::get_hex_code_box_size(int p_size, char32_t p_index) const {
int fnt = (p_size < 20) ? 0 : 1;
- float w = ((p_index <= 0xFF) ? 1 : ((p_index <= 0xFFFF) ? 2 : 3)) * hex_code_box_font_size[fnt].x;
- float h = 2 * hex_code_box_font_size[fnt].y;
+ real_t w = ((p_index <= 0xFF) ? 1 : ((p_index <= 0xFFFF) ? 2 : 3)) * hex_code_box_font_size[fnt].x;
+ real_t h = 2 * hex_code_box_font_size[fnt].y;
return Vector2(w + 4, h + 3 + 2 * hex_code_box_font_size[fnt].z);
}
@@ -534,8 +587,8 @@ void TextServer::draw_hex_code_box(RID p_canvas, int p_size, const Vector2 &p_po
Vector2 pos = p_pos;
Rect2 dest = Rect2(Vector2(), Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y));
- float w = ((p_index <= 0xFF) ? 1 : ((p_index <= 0xFFFF) ? 2 : 3)) * hex_code_box_font_size[fnt].x;
- float h = 2 * hex_code_box_font_size[fnt].y;
+ real_t w = ((p_index <= 0xFF) ? 1 : ((p_index <= 0xFFFF) ? 2 : 3)) * hex_code_box_font_size[fnt].x;
+ real_t h = 2 * hex_code_box_font_size[fnt].y;
pos.y -= Math::floor((h + 3 + hex_code_box_font_size[fnt].z) * 0.75);
@@ -575,7 +628,7 @@ void TextServer::draw_hex_code_box(RID p_canvas, int p_size, const Vector2 &p_po
}
}
-Vector<Vector2i> TextServer::shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<float> &p_width, int p_start, bool p_once, uint8_t /*TextBreakFlag*/ p_break_flags) const {
+Vector<Vector2i> TextServer::shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<real_t> &p_width, int p_start, bool p_once, uint8_t /*TextBreakFlag*/ p_break_flags) const {
Vector<Vector2i> lines;
ERR_FAIL_COND_V(p_width.is_empty(), lines);
@@ -584,7 +637,7 @@ Vector<Vector2i> TextServer::shaped_text_get_line_breaks_adv(RID p_shaped, const
const Vector<Glyph> &logical = const_cast<TextServer *>(this)->shaped_text_sort_logical(p_shaped);
const Vector2i &range = shaped_text_get_range(p_shaped);
- float width = 0.f;
+ real_t width = 0.f;
int line_start = MAX(p_start, range.x);
int last_safe_break = -1;
int chunk = 0;
@@ -646,14 +699,14 @@ Vector<Vector2i> TextServer::shaped_text_get_line_breaks_adv(RID p_shaped, const
return lines;
}
-Vector<Vector2i> TextServer::shaped_text_get_line_breaks(RID p_shaped, float p_width, int p_start, uint8_t /*TextBreakFlag*/ p_break_flags) const {
+Vector<Vector2i> TextServer::shaped_text_get_line_breaks(RID p_shaped, real_t p_width, int p_start, uint8_t /*TextBreakFlag*/ p_break_flags) const {
Vector<Vector2i> lines;
const_cast<TextServer *>(this)->shaped_text_update_breaks(p_shaped);
const Vector<Glyph> &logical = const_cast<TextServer *>(this)->shaped_text_sort_logical(p_shaped);
const Vector2i &range = shaped_text_get_range(p_shaped);
- float width = 0.f;
+ real_t width = 0.f;
int line_start = MAX(p_start, range.x);
int last_safe_break = -1;
int word_count = 0;
@@ -747,11 +800,11 @@ void TextServer::shaped_text_get_carets(RID p_shaped, int p_position, Rect2 &p_l
const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped);
TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped);
const Vector2 &range = shaped_text_get_range(p_shaped);
- float ascent = shaped_text_get_ascent(p_shaped);
- float descent = shaped_text_get_descent(p_shaped);
- float height = (ascent + descent) / 2;
+ real_t ascent = shaped_text_get_ascent(p_shaped);
+ real_t descent = shaped_text_get_descent(p_shaped);
+ real_t height = (ascent + descent) / 2;
- float off = 0.0f;
+ real_t off = 0.0f;
p_leading_dir = DIRECTION_AUTO;
p_trailing_dir = DIRECTION_AUTO;
@@ -857,11 +910,11 @@ void TextServer::shaped_text_get_carets(RID p_shaped, int p_position, Rect2 &p_l
}
// Caret inside grapheme (middle).
if (p_position > glyphs[i].start && p_position < glyphs[i].end && (glyphs[i].flags & GRAPHEME_IS_VIRTUAL) != GRAPHEME_IS_VIRTUAL) {
- float advance = 0.f;
+ real_t advance = 0.f;
for (int j = 0; j < glyphs[i].count; j++) {
advance += glyphs[i + j].advance * glyphs[i + j].repeat;
}
- float char_adv = advance / (float)(glyphs[i].end - glyphs[i].start);
+ real_t char_adv = advance / (real_t)(glyphs[i].end - glyphs[i].start);
Rect2 cr;
if (orientation == ORIENTATION_HORIZONTAL) {
cr.size.x = 1.0f;
@@ -940,14 +993,14 @@ Vector<Vector2> TextServer::shaped_text_get_selection(RID p_shaped, int p_start,
int v_size = visual.size();
const Glyph *glyphs = visual.ptr();
- float off = 0.0f;
+ real_t off = 0.0f;
for (int i = 0; i < v_size; i++) {
for (int k = 0; k < glyphs[i].repeat; k++) {
if ((glyphs[i].count > 0) && ((glyphs[i].index != 0) || ((glyphs[i].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE))) {
if (glyphs[i].start < end && glyphs[i].end > start) {
// Grapheme fully in selection range.
if (glyphs[i].start >= start && glyphs[i].end <= end) {
- float advance = 0.f;
+ real_t advance = 0.f;
for (int j = 0; j < glyphs[i].count; j++) {
advance += glyphs[i + j].advance;
}
@@ -955,11 +1008,11 @@ Vector<Vector2> TextServer::shaped_text_get_selection(RID p_shaped, int p_start,
}
// Only start of grapheme is in selection range.
if (glyphs[i].start >= start && glyphs[i].end > end) {
- float advance = 0.f;
+ real_t advance = 0.f;
for (int j = 0; j < glyphs[i].count; j++) {
advance += glyphs[i + j].advance;
}
- float char_adv = advance / (float)(glyphs[i].end - glyphs[i].start);
+ real_t char_adv = advance / (real_t)(glyphs[i].end - glyphs[i].start);
if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
ranges.push_back(Vector2(off + char_adv * (glyphs[i].end - end), off + advance));
} else {
@@ -968,11 +1021,11 @@ Vector<Vector2> TextServer::shaped_text_get_selection(RID p_shaped, int p_start,
}
// Only end of grapheme is in selection range.
if (glyphs[i].start < start && glyphs[i].end <= end) {
- float advance = 0.f;
+ real_t advance = 0.f;
for (int j = 0; j < glyphs[i].count; j++) {
advance += glyphs[i + j].advance;
}
- float char_adv = advance / (float)(glyphs[i].end - glyphs[i].start);
+ real_t char_adv = advance / (real_t)(glyphs[i].end - glyphs[i].start);
if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
ranges.push_back(Vector2(off, off + char_adv * (start - glyphs[i].start)));
} else {
@@ -981,11 +1034,11 @@ Vector<Vector2> TextServer::shaped_text_get_selection(RID p_shaped, int p_start,
}
// Selection range is within grapheme.
if (glyphs[i].start < start && glyphs[i].end > end) {
- float advance = 0.f;
+ real_t advance = 0.f;
for (int j = 0; j < glyphs[i].count; j++) {
advance += glyphs[i + j].advance;
}
- float char_adv = advance / (float)(glyphs[i].end - glyphs[i].start);
+ real_t char_adv = advance / (real_t)(glyphs[i].end - glyphs[i].start);
if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
ranges.push_back(Vector2(off + char_adv * (glyphs[i].end - end), off + char_adv * (glyphs[i].end - start)));
} else {
@@ -1020,11 +1073,11 @@ Vector<Vector2> TextServer::shaped_text_get_selection(RID p_shaped, int p_start,
return ranges;
}
-int TextServer::shaped_text_hit_test_grapheme(RID p_shaped, float p_coords) const {
+int TextServer::shaped_text_hit_test_grapheme(RID p_shaped, real_t p_coords) const {
const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped);
// Exact grapheme hit test, return -1 if missed.
- float off = 0.0f;
+ real_t off = 0.0f;
int v_size = visual.size();
const Glyph *glyphs = visual.ptr();
@@ -1040,7 +1093,7 @@ int TextServer::shaped_text_hit_test_grapheme(RID p_shaped, float p_coords) cons
return -1;
}
-int TextServer::shaped_text_hit_test_position(RID p_shaped, float p_coords) const {
+int TextServer::shaped_text_hit_test_position(RID p_shaped, real_t p_coords) const {
const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped);
int v_size = visual.size();
@@ -1074,10 +1127,10 @@ int TextServer::shaped_text_hit_test_position(RID p_shaped, float p_coords) cons
}
}
- float off = 0.0f;
+ real_t off = 0.0f;
for (int i = 0; i < v_size; i++) {
if (glyphs[i].count > 0) {
- float advance = 0.f;
+ real_t advance = 0.f;
for (int j = 0; j < glyphs[i].count; j++) {
advance += glyphs[i + j].advance * glyphs[i + j].repeat;
}
@@ -1135,7 +1188,7 @@ int TextServer::shaped_text_prev_grapheme_pos(RID p_shaped, int p_pos) {
return p_pos;
}
-void TextServer::shaped_text_draw(RID p_shaped, RID p_canvas, const Vector2 &p_pos, float p_clip_l, float p_clip_r, const Color &p_color) const {
+void TextServer::shaped_text_draw(RID p_shaped, RID p_canvas, const Vector2 &p_pos, real_t p_clip_l, real_t p_clip_r, const Color &p_color) const {
const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped);
TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped);
bool hex_codes = shaped_text_get_preserve_control(p_shaped) || shaped_text_get_preserve_invalid(p_shaped);
@@ -1228,7 +1281,7 @@ void TextServer::shaped_text_draw(RID p_shaped, RID p_canvas, const Vector2 &p_p
}
}
-void TextServer::shaped_text_draw_outline(RID p_shaped, RID p_canvas, const Vector2 &p_pos, float p_clip_l, float p_clip_r, int p_outline_size, const Color &p_color) const {
+void TextServer::shaped_text_draw_outline(RID p_shaped, RID p_canvas, const Vector2 &p_pos, real_t p_clip_l, real_t p_clip_r, int p_outline_size, const Color &p_color) const {
const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped);
TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped);
@@ -1316,11 +1369,7 @@ void TextServer::shaped_text_draw_outline(RID p_shaped, RID p_canvas, const Vect
}
}
-RID TextServer::_create_font_memory(const PackedByteArray &p_data, const String &p_type, int p_base_size) {
- return create_font_memory(p_data.ptr(), p_data.size(), p_type, p_base_size);
-}
-
-Dictionary TextServer::_font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index) const {
+Dictionary TextServer::_font_get_glyph_contours(RID p_font, int p_size, int32_t p_index) const {
Vector<Vector3> points;
Vector<int32_t> contours;
bool orientation;
@@ -1378,7 +1427,7 @@ Array TextServer::_shaped_text_get_line_breaks_adv(RID p_shaped, const PackedFlo
return ret;
}
-Array TextServer::_shaped_text_get_line_breaks(RID p_shaped, float p_width, int p_start, uint8_t p_break_flags) const {
+Array TextServer::_shaped_text_get_line_breaks(RID p_shaped, real_t p_width, int p_start, uint8_t p_break_flags) const {
Array ret;
Vector<Vector2i> lines = shaped_text_get_line_breaks(p_shaped, p_width, p_start, p_break_flags);
diff --git a/servers/text_server.h b/servers/text_server.h
index 26993838a3..62e02e3c97 100644
--- a/servers/text_server.h
+++ b/servers/text_server.h
@@ -116,6 +116,13 @@ public:
CONTOUR_CURVE_TAG_OFF_CUBIC = 0x02
};
+ enum SpacingType {
+ SPACING_GLYPH,
+ SPACING_SPACE,
+ SPACING_TOP,
+ SPACING_BOTTOM,
+ };
+
struct Glyph {
int start = -1; // Start offset in the source string.
int end = -1; // End offset in the source string.
@@ -124,13 +131,13 @@ public:
uint8_t repeat = 1; // Draw multiple times in the row.
uint16_t flags = 0; // Grapheme flags (valid, rtl, virtual), set in the first glyph only.
- float x_off = 0.f; // Offset from the origin of the glyph on baseline.
- float y_off = 0.f;
- float advance = 0.f; // Advance to the next glyph along baseline(x for horizontal layout, y for vertical).
+ real_t x_off = 0.f; // Offset from the origin of the glyph on baseline.
+ real_t y_off = 0.f;
+ real_t advance = 0.f; // Advance to the next glyph along baseline(x for horizontal layout, y for vertical).
RID font_rid; // Font resource.
int font_size = 0; // Font size;
- uint32_t index = 0; // Glyph index (font specific) or UTF-32 codepoint (for the invalid glyphs).
+ int32_t index = 0; // Glyph index (font specific) or UTF-32 codepoint (for the invalid glyphs).
bool operator==(const Glyph &p_a) const;
bool operator!=(const Glyph &p_a) const;
@@ -163,6 +170,8 @@ public:
};
struct ShapedTextData {
+ Mutex mutex;
+
/* Source data */
RID parent; // Substring parent ShapedTextData.
@@ -205,13 +214,13 @@ public:
bool preserve_invalid = true; // Draw hex code box instead of missing characters.
bool preserve_control = false; // Draw control characters.
- float ascent = 0.f; // Ascent for horizontal layout, 1/2 of width for vertical.
- float descent = 0.f; // Descent for horizontal layout, 1/2 of width for vertical.
- float width = 0.f; // Width for horizontal layout, height for vertical.
- float width_trimmed = 0.f;
+ real_t ascent = 0.f; // Ascent for horizontal layout, 1/2 of width for vertical.
+ real_t descent = 0.f; // Descent for horizontal layout, 1/2 of width for vertical.
+ real_t width = 0.f; // Width for horizontal layout, height for vertical.
+ real_t width_trimmed = 0.f;
- float upos = 0.f;
- float uthk = 0.f;
+ real_t upos = 0.f;
+ real_t uthk = 0.f;
TrimData overrun_trim_data;
bool fit_width_minimum_reached = false;
@@ -245,85 +254,134 @@ public:
virtual bool is_locale_right_to_left(const String &p_locale) = 0;
- virtual int32_t name_to_tag(const String &p_name) { return 0; };
- virtual String tag_to_name(int32_t p_tag) { return ""; };
+ virtual int32_t name_to_tag(const String &p_name) const { return 0; };
+ virtual String tag_to_name(int32_t p_tag) const { return ""; };
/* Font interface */
- virtual RID create_font_system(const String &p_name, int p_base_size = 16) = 0;
- virtual RID create_font_resource(const String &p_filename, int p_base_size = 16) = 0;
- virtual RID create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16) = 0;
- virtual RID create_font_bitmap(float p_height, float p_ascent, int p_base_size = 16) = 0;
+ virtual RID create_font() = 0;
+
+ virtual void font_set_data(RID p_font_rid, const PackedByteArray &p_data) = 0;
+ virtual void font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) = 0;
+
+ virtual void font_set_antialiased(RID p_font_rid, bool p_antialiased) = 0;
+ virtual bool font_is_antialiased(RID p_font_rid) const = 0;
+
+ virtual void font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) = 0;
+ virtual bool font_is_multichannel_signed_distance_field(RID p_font_rid) const = 0;
+
+ virtual void font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) = 0;
+ virtual int font_get_msdf_pixel_range(RID p_font_rid) const = 0;
+
+ virtual void font_set_msdf_size(RID p_font_rid, int p_msdf_size) = 0;
+ virtual int font_get_msdf_size(RID p_font_rid) const = 0;
+
+ virtual void font_set_fixed_size(RID p_font_rid, int p_fixed_size) = 0;
+ virtual int font_get_fixed_size(RID p_font_rid) const = 0;
+
+ virtual void font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) = 0;
+ virtual bool font_is_force_autohinter(RID p_font_rid) const = 0;
+
+ virtual void font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) = 0;
+ virtual TextServer::Hinting font_get_hinting(RID p_font_rid) const = 0;
- virtual void font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) = 0;
- virtual void font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) = 0;
- virtual void font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) = 0;
+ virtual void font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) = 0;
+ virtual Dictionary font_get_variation_coordinates(RID p_font_rid) const = 0;
- virtual float font_get_height(RID p_font, int p_size) const = 0;
- virtual float font_get_ascent(RID p_font, int p_size) const = 0;
- virtual float font_get_descent(RID p_font, int p_size) const = 0;
+ virtual void font_set_oversampling(RID p_font_rid, real_t p_oversampling) = 0;
+ virtual real_t font_get_oversampling(RID p_font_rid) const = 0;
- virtual int font_get_spacing_space(RID p_font) const = 0;
- virtual void font_set_spacing_space(RID p_font, int p_value) = 0;
+ virtual Array font_get_size_cache_list(RID p_font_rid) const = 0;
+ virtual void font_clear_size_cache(RID p_font_rid) = 0;
+ virtual void font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) = 0;
- virtual int font_get_spacing_glyph(RID p_font) const = 0;
- virtual void font_set_spacing_glyph(RID p_font, int p_value) = 0;
+ virtual void font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) = 0;
+ virtual real_t font_get_ascent(RID p_font_rid, int p_size) const = 0;
- virtual float font_get_underline_position(RID p_font, int p_size) const = 0;
- virtual float font_get_underline_thickness(RID p_font, int p_size) const = 0;
+ virtual void font_set_descent(RID p_font_rid, int p_size, real_t p_descent) = 0;
+ virtual real_t font_get_descent(RID p_font_rid, int p_size) const = 0;
- virtual void font_set_antialiased(RID p_font, bool p_antialiased) = 0;
- virtual bool font_get_antialiased(RID p_font) const = 0;
+ virtual void font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) = 0;
+ virtual real_t font_get_underline_position(RID p_font_rid, int p_size) const = 0;
- virtual Dictionary font_get_feature_list(RID p_font) const { return Dictionary(); };
- virtual Dictionary font_get_variation_list(RID p_font) const { return Dictionary(); };
+ virtual void font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) = 0;
+ virtual real_t font_get_underline_thickness(RID p_font_rid, int p_size) const = 0;
- virtual void font_set_variation(RID p_font, const String &p_name, double p_value){};
- virtual double font_get_variation(RID p_font, const String &p_name) const { return 0; };
+ virtual void font_set_scale(RID p_font_rid, int p_size, real_t p_scale) = 0;
+ virtual real_t font_get_scale(RID p_font_rid, int p_size) const = 0;
- virtual void font_set_distance_field_hint(RID p_font, bool p_distance_field) = 0;
- virtual bool font_get_distance_field_hint(RID p_font) const = 0;
+ virtual void font_set_spacing(RID p_font_rid, int p_size, SpacingType p_spacing, int p_value) = 0;
+ virtual int font_get_spacing(RID p_font_rid, int p_size, SpacingType p_spacing) const = 0;
- virtual void font_set_hinting(RID p_font, Hinting p_hinting) = 0;
- virtual Hinting font_get_hinting(RID p_font) const = 0;
+ virtual int font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const = 0;
+ virtual void font_clear_textures(RID p_font_rid, const Vector2i &p_size) = 0;
+ virtual void font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) = 0;
- virtual void font_set_force_autohinter(RID p_font, bool p_enabeld) = 0;
- virtual bool font_get_force_autohinter(RID p_font) const = 0;
+ virtual void font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) = 0;
+ virtual Ref<Image> font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const = 0;
- virtual bool font_has_char(RID p_font, char32_t p_char) const = 0;
- virtual String font_get_supported_chars(RID p_font) const = 0;
+ virtual void font_set_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) = 0;
+ virtual PackedInt32Array font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const = 0;
- virtual bool font_has_outline(RID p_font) const = 0;
- virtual float font_get_base_size(RID p_font) const = 0;
+ virtual Array font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const = 0;
+ virtual void font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) = 0;
+ virtual void font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) = 0;
- virtual bool font_is_language_supported(RID p_font, const String &p_language) const = 0;
- virtual void font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) = 0;
- virtual bool font_get_language_support_override(RID p_font, const String &p_language) = 0;
- virtual void font_remove_language_support_override(RID p_font, const String &p_language) = 0;
- virtual Vector<String> font_get_language_support_overrides(RID p_font) = 0;
+ virtual Vector2 font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const = 0;
+ virtual void font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) = 0;
- virtual bool font_is_script_supported(RID p_font, const String &p_script) const = 0;
- virtual void font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) = 0;
- virtual bool font_get_script_support_override(RID p_font, const String &p_script) = 0;
- virtual void font_remove_script_support_override(RID p_font, const String &p_script) = 0;
- virtual Vector<String> font_get_script_support_overrides(RID p_font) = 0;
+ virtual Vector2 font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const = 0;
+ virtual void font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) = 0;
- virtual uint32_t font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector = 0x0000) const = 0;
- virtual Vector2 font_get_glyph_advance(RID p_font, uint32_t p_index, int p_size) const = 0;
- virtual Vector2 font_get_glyph_kerning(RID p_font, uint32_t p_index_a, uint32_t p_index_b, int p_size) const = 0;
+ virtual Vector2 font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const = 0;
+ virtual void font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) = 0;
- virtual Vector2 font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const = 0;
- virtual Vector2 font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const = 0;
+ virtual Rect2 font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const = 0;
+ virtual void font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) = 0;
- virtual bool font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const = 0;
+ virtual int font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const = 0;
+ virtual void font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) = 0;
- virtual float font_get_oversampling() const = 0;
- virtual void font_set_oversampling(float p_oversampling) = 0;
+ virtual bool font_get_glyph_contours(RID p_font, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const = 0;
+
+ virtual Array font_get_kerning_list(RID p_font_rid, int p_size) const = 0;
+ virtual void font_clear_kerning_map(RID p_font_rid, int p_size) = 0;
+ virtual void font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) = 0;
+
+ virtual void font_set_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) = 0;
+ virtual Vector2 font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const = 0;
+
+ virtual int32_t font_get_glyph_index(RID p_font_rid, int p_size, char32_t p_char, char32_t p_variation_selector) const = 0;
+
+ virtual bool font_has_char(RID p_font_rid, char32_t p_char) const = 0;
+ virtual String font_get_supported_chars(RID p_font_rid) const = 0;
+
+ virtual void font_render_range(RID p_font, const Vector2i &p_size, char32_t p_start, char32_t p_end) = 0;
+ virtual void font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) = 0;
+
+ virtual void font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) const = 0;
+ virtual void font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) const = 0;
+
+ virtual bool font_is_language_supported(RID p_font_rid, const String &p_language) const = 0;
+ virtual void font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) = 0;
+ virtual bool font_get_language_support_override(RID p_font_rid, const String &p_language) = 0;
+ virtual void font_remove_language_support_override(RID p_font_rid, const String &p_language) = 0;
+ virtual Vector<String> font_get_language_support_overrides(RID p_font_rid) = 0;
+
+ virtual bool font_is_script_supported(RID p_font_rid, const String &p_script) const = 0;
+ virtual void font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) = 0;
+ virtual bool font_get_script_support_override(RID p_font_rid, const String &p_script) = 0;
+ virtual void font_remove_script_support_override(RID p_font_rid, const String &p_script) = 0;
+ virtual Vector<String> font_get_script_support_overrides(RID p_font_rid) = 0;
+
+ virtual Dictionary font_supported_feature_list(RID p_font_rid) const = 0;
+ virtual Dictionary font_supported_variation_list(RID p_font_rid) const = 0;
+
+ virtual real_t font_get_global_oversampling() const = 0;
+ virtual void font_set_global_oversampling(real_t p_oversampling) = 0;
Vector2 get_hex_code_box_size(int p_size, char32_t p_index) const;
void draw_hex_code_box(RID p_canvas, int p_size, const Vector2 &p_pos, char32_t p_index, const Color &p_color) const;
- virtual Vector<String> get_system_fonts() const = 0;
-
/* Shaped text buffer interface */
virtual RID create_shaped_text(Direction p_direction = DIRECTION_AUTO, Orientation p_orientation = ORIENTATION_HORIZONTAL) = 0;
@@ -351,8 +409,8 @@ public:
virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const = 0; // Copy shaped substring (e.g. line break) without reshaping, but correctly reordered, preservers range.
virtual RID shaped_text_get_parent(RID p_shaped) const = 0;
- virtual float shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) = 0;
- virtual float shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) = 0;
+ virtual real_t shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) = 0;
+ virtual real_t shaped_text_tab_align(RID p_shaped, const Vector<real_t> &p_tab_stops) = 0;
virtual bool shaped_text_shape(RID p_shaped) = 0;
virtual bool shaped_text_update_breaks(RID p_shaped) = 0;
@@ -366,36 +424,36 @@ public:
virtual Vector<Glyph> shaped_text_sort_logical(RID p_shaped) = 0;
- virtual Vector<Vector2i> shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<float> &p_width, int p_start = 0, bool p_once = true, uint8_t /*TextBreakFlag*/ p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const;
- virtual Vector<Vector2i> shaped_text_get_line_breaks(RID p_shaped, float p_width, int p_start = 0, uint8_t /*TextBreakFlag*/ p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const;
+ virtual Vector<Vector2i> shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<real_t> &p_width, int p_start = 0, bool p_once = true, uint8_t /*TextBreakFlag*/ p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const;
+ virtual Vector<Vector2i> shaped_text_get_line_breaks(RID p_shaped, real_t p_width, int p_start = 0, uint8_t /*TextBreakFlag*/ p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const;
virtual Vector<Vector2i> shaped_text_get_word_breaks(RID p_shaped, int p_grapheme_flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_PUNCTUATION) const;
virtual TrimData shaped_text_get_trim_data(RID p_shaped) const;
- virtual void shaped_text_overrun_trim_to_width(RID p_shaped, float p_width, uint8_t p_trim_flags) = 0;
+ virtual void shaped_text_overrun_trim_to_width(RID p_shaped, real_t p_width, uint8_t p_trim_flags) = 0;
virtual Array shaped_text_get_objects(RID p_shaped) const = 0;
virtual Rect2 shaped_text_get_object_rect(RID p_shaped, Variant p_key) const = 0;
virtual Size2 shaped_text_get_size(RID p_shaped) const = 0;
- virtual float shaped_text_get_ascent(RID p_shaped) const = 0;
- virtual float shaped_text_get_descent(RID p_shaped) const = 0;
- virtual float shaped_text_get_width(RID p_shaped) const = 0;
- virtual float shaped_text_get_underline_position(RID p_shaped) const = 0;
- virtual float shaped_text_get_underline_thickness(RID p_shaped) const = 0;
+ virtual real_t shaped_text_get_ascent(RID p_shaped) const = 0;
+ virtual real_t shaped_text_get_descent(RID p_shaped) const = 0;
+ virtual real_t shaped_text_get_width(RID p_shaped) const = 0;
+ virtual real_t shaped_text_get_underline_position(RID p_shaped) const = 0;
+ virtual real_t shaped_text_get_underline_thickness(RID p_shaped) const = 0;
virtual Direction shaped_text_get_dominant_direciton_in_range(RID p_shaped, int p_start, int p_end) const;
virtual void shaped_text_get_carets(RID p_shaped, int p_position, Rect2 &p_leading_caret, Direction &p_leading_dir, Rect2 &p_trailing_caret, Direction &p_trailing_dir) const;
virtual Vector<Vector2> shaped_text_get_selection(RID p_shaped, int p_start, int p_end) const;
- virtual int shaped_text_hit_test_grapheme(RID p_shaped, float p_coords) const; // Return grapheme index.
- virtual int shaped_text_hit_test_position(RID p_shaped, float p_coords) const; // Return caret/selection position.
+ virtual int shaped_text_hit_test_grapheme(RID p_shaped, real_t p_coords) const; // Return grapheme index.
+ virtual int shaped_text_hit_test_position(RID p_shaped, real_t p_coords) const; // Return caret/selection position.
virtual int shaped_text_next_grapheme_pos(RID p_shaped, int p_pos);
virtual int shaped_text_prev_grapheme_pos(RID p_shaped, int p_pos);
// The pen position is always placed on the baseline and moveing left to right.
- virtual void shaped_text_draw(RID p_shaped, RID p_canvas, const Vector2 &p_pos, float p_clip_l = -1.f, float p_clip_r = -1.f, const Color &p_color = Color(1, 1, 1)) const;
- virtual void shaped_text_draw_outline(RID p_shaped, RID p_canvas, const Vector2 &p_pos, float p_clip_l = -1.f, float p_clip_r = -1.f, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1)) const;
+ virtual void shaped_text_draw(RID p_shaped, RID p_canvas, const Vector2 &p_pos, real_t p_clip_l = -1.f, real_t p_clip_r = -1.f, const Color &p_color = Color(1, 1, 1)) const;
+ virtual void shaped_text_draw_outline(RID p_shaped, RID p_canvas, const Vector2 &p_pos, real_t p_clip_l = -1.f, real_t p_clip_r = -1.f, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1)) const;
// Number conversion.
virtual String format_number(const String &p_string, const String &p_language = "") const { return p_string; };
@@ -403,9 +461,9 @@ public:
virtual String percent_sign(const String &p_language = "") const { return "%"; };
/* GDScript wrappers */
- RID _create_font_memory(const PackedByteArray &p_data, const String &p_type, int p_base_size = 16);
+ RID _create_font_memory(const PackedByteArray &p_data, int p_base_size = 16);
- Dictionary _font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index) const;
+ Dictionary _font_get_glyph_contours(RID p_font, int p_size, int32_t p_index) const;
Array _shaped_text_get_glyphs(RID p_shaped) const;
Dictionary _shaped_text_get_carets(RID p_shaped, int p_position) const;
@@ -413,7 +471,7 @@ public:
void _shaped_text_set_bidi_override(RID p_shaped, const Array &p_override);
Array _shaped_text_get_line_breaks_adv(RID p_shaped, const PackedFloat32Array &p_width, int p_start, bool p_once, uint8_t p_break_flags) const;
- Array _shaped_text_get_line_breaks(RID p_shaped, float p_width, int p_start, uint8_t p_break_flags) const;
+ Array _shaped_text_get_line_breaks(RID p_shaped, real_t p_width, int p_start, uint8_t p_break_flags) const;
Array _shaped_text_get_word_breaks(RID p_shaped) const;
Array _shaped_text_get_selection(RID p_shaped, int p_start, int p_end) const;
@@ -491,5 +549,6 @@ VARIANT_ENUM_CAST(TextServer::GraphemeFlag);
VARIANT_ENUM_CAST(TextServer::Hinting);
VARIANT_ENUM_CAST(TextServer::Feature);
VARIANT_ENUM_CAST(TextServer::ContourPointTag);
+VARIANT_ENUM_CAST(TextServer::SpacingType);
#endif // TEXT_SERVER_H
diff --git a/tests/test_text_server.h b/tests/test_text_server.h
index cac022e33f..b8ed4f89d0 100644
--- a/tests/test_text_server.h
+++ b/tests/test_text_server.h
@@ -55,7 +55,8 @@ TEST_SUITE("[[TextServer]") {
for (int i = 0; i < TextServerManager::get_interface_count(); i++) {
TextServer *ts = TextServerManager::initialize(i, err);
- RID font = ts->create_font_memory(_font_NotoSans_Regular, _font_NotoSans_Regular_size, "ttf");
+ RID font = ts->create_font();
+ ts->font_set_data_ptr(font, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
TEST_FAIL_COND(font == RID(), "Loading font failed.");
ts->free(font);
}
@@ -65,9 +66,14 @@ TEST_SUITE("[[TextServer]") {
for (int i = 0; i < TextServerManager::get_interface_count(); i++) {
TextServer *ts = TextServerManager::initialize(i, err);
+ RID font1 = ts->create_font();
+ ts->font_set_data_ptr(font1, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
+ RID font2 = ts->create_font();
+ ts->font_set_data_ptr(font2, _font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size);
+
Vector<RID> font;
- font.push_back(ts->create_font_memory(_font_NotoSans_Regular, _font_NotoSans_Regular_size, "ttf"));
- font.push_back(ts->create_font_memory(_font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size, "ttf"));
+ font.push_back(font1);
+ font.push_back(font2);
String test = U"คนอ้วน khon uan ראה";
// 6^ 17^
@@ -111,9 +117,14 @@ TEST_SUITE("[[TextServer]") {
continue;
}
+ RID font1 = ts->create_font();
+ ts->font_set_data_ptr(font1, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
+ RID font2 = ts->create_font();
+ ts->font_set_data_ptr(font2, _font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size);
+
Vector<RID> font;
- font.push_back(ts->create_font_memory(_font_NotoSans_Regular, _font_NotoSans_Regular_size, "ttf"));
- font.push_back(ts->create_font_memory(_font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size, "ttf"));
+ font.push_back(font1);
+ font.push_back(font2);
String test = U"Arabic (اَلْعَرَبِيَّةُ, al-ʿarabiyyah)";
// 7^ 26^
@@ -155,9 +166,14 @@ TEST_SUITE("[[TextServer]") {
String test_1 = U"test test test";
// 5^ 10^
+ RID font1 = ts->create_font();
+ ts->font_set_data_ptr(font1, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
+ RID font2 = ts->create_font();
+ ts->font_set_data_ptr(font2, _font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size);
+
Vector<RID> font;
- font.push_back(ts->create_font_memory(_font_NotoSans_Regular, _font_NotoSans_Regular_size, "ttf"));
- font.push_back(ts->create_font_memory(_font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size, "ttf"));
+ font.push_back(font1);
+ font.push_back(font2);
RID ctx = ts->create_shaped_text();
TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
@@ -185,9 +201,14 @@ TEST_SUITE("[[TextServer]") {
for (int i = 0; i < TextServerManager::get_interface_count(); i++) {
TextServer *ts = TextServerManager::initialize(i, err);
+ RID font1 = ts->create_font();
+ ts->font_set_data_ptr(font1, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
+ RID font2 = ts->create_font();
+ ts->font_set_data_ptr(font2, _font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size);
+
Vector<RID> font;
- font.push_back(ts->create_font_memory(_font_NotoSans_Regular, _font_NotoSans_Regular_size, "ttf"));
- font.push_back(ts->create_font_memory(_font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size, "ttf"));
+ font.push_back(font1);
+ font.push_back(font2);
String test_1 = U"الحمد";
String test_2 = U"الحمد test";
@@ -242,7 +263,6 @@ TEST_SUITE("[[TextServer]") {
font.clear();
}
}
-
memdelete(tsman);
}
}
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 1c310eae05..dd2f509516 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -482,6 +482,19 @@ Collection of single-file libraries used in Godot components.
* License: BSD
+## msdfgen
+
+- Upstream: https://github.com/Chlumsky/msdfgen
+- Version: 1.9.1 (1b3b6b985094e6f12751177490add3ad11dd91a9, 2010)
+- License: MIT
+
+Files extracted from the upstream source:
+
+- `msdfgen.h`
+- Files in `core/` folder.
+- `LICENSE.txt` and `CHANGELOG.md`
+
+
## nanosvg
- Upstream: https://github.com/memononen/nanosvg
diff --git a/thirdparty/msdfgen/CHANGELOG.md b/thirdparty/msdfgen/CHANGELOG.md
new file mode 100644
index 0000000000..4b9a752650
--- /dev/null
+++ b/thirdparty/msdfgen/CHANGELOG.md
@@ -0,0 +1,82 @@
+
+## Version 1.9 (2021-05-28)
+
+- Error correction of multi-channel distance fields has been completely reworked
+- Added new edge coloring strategy that optimizes colors based on distances between edges
+- Added some minor functions for the library API
+- Minor code refactor and optimizations
+
+## Version 1.8 (2020-10-17)
+
+- Integrated the Skia library into the project, which is used to preprocess the shape geometry and eliminate any self-intersections and other irregularities previously unsupported by the software
+ - The scanline pass and overlapping contour mode is made obsolete by this step and has been disabled by default. The preprocess step can be disabled by the new `-nopreprocess` switch and the former enabled by `-scanline` and `-overlap` respectively.
+ - The project can be built without the Skia library, forgoing the geometry preprocessing feature. This is controlled by the macro definition `MSDFGEN_USE_SKIA`
+- Significantly improved performance of the core algorithm by reusing results from previously computed pixels
+- Introduced an additional error correction routine which eliminates MSDF artifacts by analytically predicting results of bilinear interpolation
+- Added the possibility to load font glyphs by their index rather than a Unicode value (use the prefix `g` before the character code in `-font` argument)
+- Added `-distanceshift` argument that can be used to adjust the center of the distance range in the output distance field
+- Fixed several errors in the evaluation of curve distances
+- Fixed an issue with paths containing convergent corners (those whose inner angle is zero)
+- The algorithm for pseudo-distance computation slightly changed, fixing certain rare edge cases and improving consistency
+- Added the ability to supply own `FT_Face` handle to the msdfgen library
+- Minor refactor of the core algorithm
+
+### Version 1.7.1 (2020-03-09)
+
+- Fixed an edge case bug in scanline rasterization
+
+## Version 1.7 (2020-03-07)
+
+- Added `mtsdf` mode - a combination of `msdf` with `sdf` in the alpha channel
+- Distance fields can now be stored as uncompressed TIFF image files with floating point precision
+- Bitmap class refactor - template argument split into data type and number of channels, bitmap reference classes introduced
+- Added a secondary "ink trap" edge coloring heuristic, can be selected using `-coloringstrategy inktrap`
+- Added computation of estimated rendering error for a given SDF
+- Added computation of bounding box that includes sharp mitered corners
+- The API for bounds computation of the `Shape` class changed for clarity
+- Fixed several edge case bugs
+
+## Version 1.6 (2019-04-08)
+
+- Core algorithm rewritten to split up advanced edge selection logic into modular template arguments.
+- Pseudo-distance evaluation reworked to eliminate discontinuities at the midpoint between edges.
+- MSDF error correction reworked to also fix distances away from edges and consider diagonal pairs. Code simplified.
+- Added scanline rasterization support for `Shape`.
+- Added a scanline pass in the standalone version, which corrects the signs in the distance field according to the selected fill rule (`-fillrule`). Can be disabled using `-noscanline`.
+- Fixed autoframe scaling, which previously caused the output to have unnecessary empty border.
+- `-guessorder` switch no longer enabled by default, as the functionality is now provided by the scanline pass.
+- Updated FreeType and other libraries, changed to static linkage
+- Added 64-bit and static library builds to the Visual Studio solution
+
+## Version 1.5 (2017-07-23)
+
+- Fixed rounding error in cubic curve splitting.
+- SVG parser fixes and support for additional path commands.
+- Added CMake build script.
+
+## Version 1.4 (2017-02-09)
+
+- Reworked contour combining logic to support overlapping contours. Original algorithm preserved in functions with `_legacy` suffix, which are invoked by the new `-legacy` switch.
+- Fixed a severe bug in cubic curve distance computation, where a control point lies at the endpoint.
+- Standalone version now automatically detects if the input has the wrong orientation and adjusts the distance field accordingly. Can be disabled by `-keeporder` or `-reverseorder` switch.
+- SVG parser fixes and improvements.
+
+## Version 1.3 (2016-12-07)
+
+- Fixed `-reverseorder` switch.
+- Fixed glyph loading to use the proper method of acquiring outlines from FreeType.
+
+## Version 1.2 (2016-07-20)
+
+- Added option to specify that shape vertices are listed in reverse order (`-reverseorder`).
+- Added option to set a seed for the edge coloring heuristic (-seed \<n\>), which can be used to adjust the output.
+- Fixed parsing of glyph contours that start with a curve control point.
+
+## Version 1.1 (2016-05-08)
+
+- Switched to MIT license due to popular demand.
+- Fixed SDF rendering anti-aliasing when the output is smaller than the distance field.
+
+## Version 1.0 (2016-04-28)
+
+- Project published.
diff --git a/thirdparty/msdfgen/LICENSE.txt b/thirdparty/msdfgen/LICENSE.txt
new file mode 100644
index 0000000000..5fb05446bc
--- /dev/null
+++ b/thirdparty/msdfgen/LICENSE.txt
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016 Viktor Chlumsky
+
+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.
diff --git a/thirdparty/msdfgen/core/Bitmap.h b/thirdparty/msdfgen/core/Bitmap.h
new file mode 100644
index 0000000000..14407d6c34
--- /dev/null
+++ b/thirdparty/msdfgen/core/Bitmap.h
@@ -0,0 +1,50 @@
+
+#pragma once
+
+#include "BitmapRef.hpp"
+
+namespace msdfgen {
+
+/// A 2D image bitmap with N channels of type T. Pixel memory is managed by the class.
+template <typename T, int N = 1>
+class Bitmap {
+
+public:
+ Bitmap();
+ Bitmap(int width, int height);
+ Bitmap(const BitmapConstRef<T, N> &orig);
+ Bitmap(const Bitmap<T, N> &orig);
+#ifdef MSDFGEN_USE_CPP11
+ Bitmap(Bitmap<T, N> &&orig);
+#endif
+ ~Bitmap();
+ Bitmap<T, N> & operator=(const BitmapConstRef<T, N> &orig);
+ Bitmap<T, N> & operator=(const Bitmap<T, N> &orig);
+#ifdef MSDFGEN_USE_CPP11
+ Bitmap<T, N> & operator=(Bitmap<T, N> &&orig);
+#endif
+ /// Bitmap width in pixels.
+ int width() const;
+ /// Bitmap height in pixels.
+ int height() const;
+ T * operator()(int x, int y);
+ const T * operator()(int x, int y) const;
+#ifdef MSDFGEN_USE_CPP11
+ explicit operator T *();
+ explicit operator const T *() const;
+#else
+ operator T *();
+ operator const T *() const;
+#endif
+ operator BitmapRef<T, N>();
+ operator BitmapConstRef<T, N>() const;
+
+private:
+ T *pixels;
+ int w, h;
+
+};
+
+}
+
+#include "Bitmap.hpp"
diff --git a/thirdparty/msdfgen/core/Bitmap.hpp b/thirdparty/msdfgen/core/Bitmap.hpp
new file mode 100644
index 0000000000..cb16cac8d4
--- /dev/null
+++ b/thirdparty/msdfgen/core/Bitmap.hpp
@@ -0,0 +1,117 @@
+
+#include "Bitmap.h"
+
+#include <cstdlib>
+#include <cstring>
+
+namespace msdfgen {
+
+template <typename T, int N>
+Bitmap<T, N>::Bitmap() : pixels(NULL), w(0), h(0) { }
+
+template <typename T, int N>
+Bitmap<T, N>::Bitmap(int width, int height) : w(width), h(height) {
+ pixels = new T[N*w*h];
+}
+
+template <typename T, int N>
+Bitmap<T, N>::Bitmap(const BitmapConstRef<T, N> &orig) : w(orig.width), h(orig.height) {
+ pixels = new T[N*w*h];
+ memcpy(pixels, orig.pixels, sizeof(T)*N*w*h);
+}
+
+template <typename T, int N>
+Bitmap<T, N>::Bitmap(const Bitmap<T, N> &orig) : w(orig.w), h(orig.h) {
+ pixels = new T[N*w*h];
+ memcpy(pixels, orig.pixels, sizeof(T)*N*w*h);
+}
+
+#ifdef MSDFGEN_USE_CPP11
+template <typename T, int N>
+Bitmap<T, N>::Bitmap(Bitmap<T, N> &&orig) : pixels(orig.pixels), w(orig.w), h(orig.h) {
+ orig.pixels = NULL;
+ orig.w = 0, orig.h = 0;
+}
+#endif
+
+template <typename T, int N>
+Bitmap<T, N>::~Bitmap() {
+ delete [] pixels;
+}
+
+template <typename T, int N>
+Bitmap<T, N> & Bitmap<T, N>::operator=(const BitmapConstRef<T, N> &orig) {
+ if (pixels != orig.pixels) {
+ delete [] pixels;
+ w = orig.width, h = orig.height;
+ pixels = new T[N*w*h];
+ memcpy(pixels, orig.pixels, sizeof(T)*N*w*h);
+ }
+ return *this;
+}
+
+template <typename T, int N>
+Bitmap<T, N> & Bitmap<T, N>::operator=(const Bitmap<T, N> &orig) {
+ if (this != &orig) {
+ delete [] pixels;
+ w = orig.w, h = orig.h;
+ pixels = new T[N*w*h];
+ memcpy(pixels, orig.pixels, sizeof(T)*N*w*h);
+ }
+ return *this;
+}
+
+#ifdef MSDFGEN_USE_CPP11
+template <typename T, int N>
+Bitmap<T, N> & Bitmap<T, N>::operator=(Bitmap<T, N> &&orig) {
+ if (this != &orig) {
+ delete [] pixels;
+ pixels = orig.pixels;
+ w = orig.w, h = orig.h;
+ orig.pixels = NULL;
+ }
+ return *this;
+}
+#endif
+
+template <typename T, int N>
+int Bitmap<T, N>::width() const {
+ return w;
+}
+
+template <typename T, int N>
+int Bitmap<T, N>::height() const {
+ return h;
+}
+
+template <typename T, int N>
+T * Bitmap<T, N>::operator()(int x, int y) {
+ return pixels+N*(w*y+x);
+}
+
+template <typename T, int N>
+const T * Bitmap<T, N>::operator()(int x, int y) const {
+ return pixels+N*(w*y+x);
+}
+
+template <typename T, int N>
+Bitmap<T, N>::operator T *() {
+ return pixels;
+}
+
+template <typename T, int N>
+Bitmap<T, N>::operator const T *() const {
+ return pixels;
+}
+
+template <typename T, int N>
+Bitmap<T, N>::operator BitmapRef<T, N>() {
+ return BitmapRef<T, N>(pixels, w, h);
+}
+
+template <typename T, int N>
+Bitmap<T, N>::operator BitmapConstRef<T, N>() const {
+ return BitmapConstRef<T, N>(pixels, w, h);
+}
+
+}
diff --git a/thirdparty/msdfgen/core/BitmapRef.hpp b/thirdparty/msdfgen/core/BitmapRef.hpp
new file mode 100644
index 0000000000..6f9620dcdf
--- /dev/null
+++ b/thirdparty/msdfgen/core/BitmapRef.hpp
@@ -0,0 +1,43 @@
+
+#pragma once
+
+#include <cstdlib>
+
+namespace msdfgen {
+
+typedef unsigned char byte;
+
+/// Reference to a 2D image bitmap or a buffer acting as one. Pixel storage not owned or managed by the object.
+template <typename T, int N = 1>
+struct BitmapRef {
+
+ T *pixels;
+ int width, height;
+
+ inline BitmapRef() : pixels(NULL), width(0), height(0) { }
+ inline BitmapRef(T *pixels, int width, int height) : pixels(pixels), width(width), height(height) { }
+
+ inline T * operator()(int x, int y) const {
+ return pixels+N*(width*y+x);
+ }
+
+};
+
+/// Constant reference to a 2D image bitmap or a buffer acting as one. Pixel storage not owned or managed by the object.
+template <typename T, int N = 1>
+struct BitmapConstRef {
+
+ const T *pixels;
+ int width, height;
+
+ inline BitmapConstRef() : pixels(NULL), width(0), height(0) { }
+ inline BitmapConstRef(const T *pixels, int width, int height) : pixels(pixels), width(width), height(height) { }
+ inline BitmapConstRef(const BitmapRef<T, N> &orig) : pixels(orig.pixels), width(orig.width), height(orig.height) { }
+
+ inline const T * operator()(int x, int y) const {
+ return pixels+N*(width*y+x);
+ }
+
+};
+
+}
diff --git a/thirdparty/msdfgen/core/Contour.cpp b/thirdparty/msdfgen/core/Contour.cpp
new file mode 100644
index 0000000000..ca80d3c55a
--- /dev/null
+++ b/thirdparty/msdfgen/core/Contour.cpp
@@ -0,0 +1,90 @@
+
+#include "Contour.h"
+
+#include "arithmetics.hpp"
+
+namespace msdfgen {
+
+static double shoelace(const Point2 &a, const Point2 &b) {
+ return (b.x-a.x)*(a.y+b.y);
+}
+
+void Contour::addEdge(const EdgeHolder &edge) {
+ edges.push_back(edge);
+}
+
+#ifdef MSDFGEN_USE_CPP11
+void Contour::addEdge(EdgeHolder &&edge) {
+ edges.push_back((EdgeHolder &&) edge);
+}
+#endif
+
+EdgeHolder & Contour::addEdge() {
+ edges.resize(edges.size()+1);
+ return edges.back();
+}
+
+static void boundPoint(double &l, double &b, double &r, double &t, Point2 p) {
+ if (p.x < l) l = p.x;
+ if (p.y < b) b = p.y;
+ if (p.x > r) r = p.x;
+ if (p.y > t) t = p.y;
+}
+
+void Contour::bound(double &l, double &b, double &r, double &t) const {
+ for (std::vector<EdgeHolder>::const_iterator edge = edges.begin(); edge != edges.end(); ++edge)
+ (*edge)->bound(l, b, r, t);
+}
+
+void Contour::boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const {
+ if (edges.empty())
+ return;
+ Vector2 prevDir = edges.back()->direction(1).normalize(true);
+ for (std::vector<EdgeHolder>::const_iterator edge = edges.begin(); edge != edges.end(); ++edge) {
+ Vector2 dir = -(*edge)->direction(0).normalize(true);
+ if (polarity*crossProduct(prevDir, dir) >= 0) {
+ double miterLength = miterLimit;
+ double q = .5*(1-dotProduct(prevDir, dir));
+ if (q > 0)
+ miterLength = min(1/sqrt(q), miterLimit);
+ Point2 miter = (*edge)->point(0)+border*miterLength*(prevDir+dir).normalize(true);
+ boundPoint(l, b, r, t, miter);
+ }
+ prevDir = (*edge)->direction(1).normalize(true);
+ }
+}
+
+int Contour::winding() const {
+ if (edges.empty())
+ return 0;
+ double total = 0;
+ if (edges.size() == 1) {
+ Point2 a = edges[0]->point(0), b = edges[0]->point(1/3.), c = edges[0]->point(2/3.);
+ total += shoelace(a, b);
+ total += shoelace(b, c);
+ total += shoelace(c, a);
+ } else if (edges.size() == 2) {
+ Point2 a = edges[0]->point(0), b = edges[0]->point(.5), c = edges[1]->point(0), d = edges[1]->point(.5);
+ total += shoelace(a, b);
+ total += shoelace(b, c);
+ total += shoelace(c, d);
+ total += shoelace(d, a);
+ } else {
+ Point2 prev = edges.back()->point(0);
+ for (std::vector<EdgeHolder>::const_iterator edge = edges.begin(); edge != edges.end(); ++edge) {
+ Point2 cur = (*edge)->point(0);
+ total += shoelace(prev, cur);
+ prev = cur;
+ }
+ }
+ return sign(total);
+}
+
+void Contour::reverse() {
+ for (int i = (int) edges.size()/2; i > 0; --i)
+ EdgeHolder::swap(edges[i-1], edges[edges.size()-i]);
+ for (std::vector<EdgeHolder>::iterator edge = edges.begin(); edge != edges.end(); ++edge)
+ (*edge)->reverse();
+}
+
+}
diff --git a/thirdparty/msdfgen/core/Contour.h b/thirdparty/msdfgen/core/Contour.h
new file mode 100644
index 0000000000..f79b269582
--- /dev/null
+++ b/thirdparty/msdfgen/core/Contour.h
@@ -0,0 +1,34 @@
+
+#pragma once
+
+#include <vector>
+#include "EdgeHolder.h"
+
+namespace msdfgen {
+
+/// A single closed contour of a shape.
+class Contour {
+
+public:
+ /// The sequence of edges that make up the contour.
+ std::vector<EdgeHolder> edges;
+
+ /// Adds an edge to the contour.
+ void addEdge(const EdgeHolder &edge);
+#ifdef MSDFGEN_USE_CPP11
+ void addEdge(EdgeHolder &&edge);
+#endif
+ /// Creates a new edge in the contour and returns its reference.
+ EdgeHolder & addEdge();
+ /// Adjusts the bounding box to fit the contour.
+ void bound(double &l, double &b, double &r, double &t) const;
+ /// Adjusts the bounding box to fit the contour border's mitered corners.
+ void boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const;
+ /// Computes the winding of the contour. Returns 1 if positive, -1 if negative.
+ int winding() const;
+ /// Reverses the sequence of edges on the contour.
+ void reverse();
+
+};
+
+}
diff --git a/thirdparty/msdfgen/core/EdgeColor.h b/thirdparty/msdfgen/core/EdgeColor.h
new file mode 100644
index 0000000000..9d49a5a89e
--- /dev/null
+++ b/thirdparty/msdfgen/core/EdgeColor.h
@@ -0,0 +1,18 @@
+
+#pragma once
+
+namespace msdfgen {
+
+/// Edge color specifies which color channels an edge belongs to.
+enum EdgeColor {
+ BLACK = 0,
+ RED = 1,
+ GREEN = 2,
+ YELLOW = 3,
+ BLUE = 4,
+ MAGENTA = 5,
+ CYAN = 6,
+ WHITE = 7
+};
+
+}
diff --git a/thirdparty/msdfgen/core/EdgeHolder.cpp b/thirdparty/msdfgen/core/EdgeHolder.cpp
new file mode 100644
index 0000000000..1a8c5f66e9
--- /dev/null
+++ b/thirdparty/msdfgen/core/EdgeHolder.cpp
@@ -0,0 +1,77 @@
+
+#include "EdgeHolder.h"
+
+namespace msdfgen {
+
+void EdgeHolder::swap(EdgeHolder &a, EdgeHolder &b) {
+ EdgeSegment *tmp = a.edgeSegment;
+ a.edgeSegment = b.edgeSegment;
+ b.edgeSegment = tmp;
+}
+
+EdgeHolder::EdgeHolder() : edgeSegment(NULL) { }
+
+EdgeHolder::EdgeHolder(EdgeSegment *segment) : edgeSegment(segment) { }
+
+EdgeHolder::EdgeHolder(Point2 p0, Point2 p1, EdgeColor edgeColor) : edgeSegment(new LinearSegment(p0, p1, edgeColor)) { }
+
+EdgeHolder::EdgeHolder(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor) : edgeSegment(new QuadraticSegment(p0, p1, p2, edgeColor)) { }
+
+EdgeHolder::EdgeHolder(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor) : edgeSegment(new CubicSegment(p0, p1, p2, p3, edgeColor)) { }
+
+EdgeHolder::EdgeHolder(const EdgeHolder &orig) : edgeSegment(orig.edgeSegment ? orig.edgeSegment->clone() : NULL) { }
+
+#ifdef MSDFGEN_USE_CPP11
+EdgeHolder::EdgeHolder(EdgeHolder &&orig) : edgeSegment(orig.edgeSegment) {
+ orig.edgeSegment = NULL;
+}
+#endif
+
+EdgeHolder::~EdgeHolder() {
+ delete edgeSegment;
+}
+
+EdgeHolder & EdgeHolder::operator=(const EdgeHolder &orig) {
+ if (this != &orig) {
+ delete edgeSegment;
+ edgeSegment = orig.edgeSegment ? orig.edgeSegment->clone() : NULL;
+ }
+ return *this;
+}
+
+#ifdef MSDFGEN_USE_CPP11
+EdgeHolder & EdgeHolder::operator=(EdgeHolder &&orig) {
+ if (this != &orig) {
+ delete edgeSegment;
+ edgeSegment = orig.edgeSegment;
+ orig.edgeSegment = NULL;
+ }
+ return *this;
+}
+#endif
+
+EdgeSegment & EdgeHolder::operator*() {
+ return *edgeSegment;
+}
+
+const EdgeSegment & EdgeHolder::operator*() const {
+ return *edgeSegment;
+}
+
+EdgeSegment * EdgeHolder::operator->() {
+ return edgeSegment;
+}
+
+const EdgeSegment * EdgeHolder::operator->() const {
+ return edgeSegment;
+}
+
+EdgeHolder::operator EdgeSegment *() {
+ return edgeSegment;
+}
+
+EdgeHolder::operator const EdgeSegment *() const {
+ return edgeSegment;
+}
+
+}
diff --git a/thirdparty/msdfgen/core/EdgeHolder.h b/thirdparty/msdfgen/core/EdgeHolder.h
new file mode 100644
index 0000000000..c4c5be7616
--- /dev/null
+++ b/thirdparty/msdfgen/core/EdgeHolder.h
@@ -0,0 +1,41 @@
+
+#pragma once
+
+#include "edge-segments.h"
+
+namespace msdfgen {
+
+/// Container for a single edge of dynamic type.
+class EdgeHolder {
+
+public:
+ /// Swaps the edges held by a and b.
+ static void swap(EdgeHolder &a, EdgeHolder &b);
+
+ EdgeHolder();
+ EdgeHolder(EdgeSegment *segment);
+ EdgeHolder(Point2 p0, Point2 p1, EdgeColor edgeColor = WHITE);
+ EdgeHolder(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor = WHITE);
+ EdgeHolder(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor = WHITE);
+ EdgeHolder(const EdgeHolder &orig);
+#ifdef MSDFGEN_USE_CPP11
+ EdgeHolder(EdgeHolder &&orig);
+#endif
+ ~EdgeHolder();
+ EdgeHolder & operator=(const EdgeHolder &orig);
+#ifdef MSDFGEN_USE_CPP11
+ EdgeHolder & operator=(EdgeHolder &&orig);
+#endif
+ EdgeSegment & operator*();
+ const EdgeSegment & operator*() const;
+ EdgeSegment * operator->();
+ const EdgeSegment * operator->() const;
+ operator EdgeSegment *();
+ operator const EdgeSegment *() const;
+
+private:
+ EdgeSegment *edgeSegment;
+
+};
+
+}
diff --git a/thirdparty/msdfgen/core/MSDFErrorCorrection.cpp b/thirdparty/msdfgen/core/MSDFErrorCorrection.cpp
new file mode 100644
index 0000000000..7918597fd2
--- /dev/null
+++ b/thirdparty/msdfgen/core/MSDFErrorCorrection.cpp
@@ -0,0 +1,495 @@
+
+#include "MSDFErrorCorrection.h"
+
+#include <cstring>
+#include "arithmetics.hpp"
+#include "equation-solver.h"
+#include "EdgeColor.h"
+#include "bitmap-interpolation.hpp"
+#include "edge-selectors.h"
+#include "contour-combiners.h"
+#include "ShapeDistanceFinder.h"
+#include "generator-config.h"
+
+namespace msdfgen {
+
+#define ARTIFACT_T_EPSILON .01
+#define PROTECTION_RADIUS_TOLERANCE 1.001
+
+#define CLASSIFIER_FLAG_CANDIDATE 0x01
+#define CLASSIFIER_FLAG_ARTIFACT 0x02
+
+const double ErrorCorrectionConfig::defaultMinDeviationRatio = 1.11111111111111111;
+const double ErrorCorrectionConfig::defaultMinImproveRatio = 1.11111111111111111;
+
+/// The base artifact classifier recognizes artifacts based on the contents of the SDF alone.
+class BaseArtifactClassifier {
+public:
+ inline BaseArtifactClassifier(double span, bool protectedFlag) : span(span), protectedFlag(protectedFlag) { }
+ /// Evaluates if the median value xm interpolated at xt in the range between am at at and bm at bt indicates an artifact.
+ inline int rangeTest(double at, double bt, double xt, float am, float bm, float xm) const {
+ // For protected texels, only consider inversion artifacts (interpolated median has different sign than boundaries). For the rest, it is sufficient that the interpolated median is outside its boundaries.
+ if ((am > .5f && bm > .5f && xm <= .5f) || (am < .5f && bm < .5f && xm >= .5f) || (!protectedFlag && median(am, bm, xm) != xm)) {
+ double axSpan = (xt-at)*span, bxSpan = (bt-xt)*span;
+ // Check if the interpolated median's value is in the expected range based on its distance (span) from boundaries a, b.
+ if (!(xm >= am-axSpan && xm <= am+axSpan && xm >= bm-bxSpan && xm <= bm+bxSpan))
+ return CLASSIFIER_FLAG_CANDIDATE|CLASSIFIER_FLAG_ARTIFACT;
+ return CLASSIFIER_FLAG_CANDIDATE;
+ }
+ return 0;
+ }
+ /// Returns true if the combined results of the tests performed on the median value m interpolated at t indicate an artifact.
+ inline bool evaluate(double t, float m, int flags) const {
+ return (flags&2) != 0;
+ }
+private:
+ double span;
+ bool protectedFlag;
+};
+
+/// The shape distance checker evaluates the exact shape distance to find additional artifacts at a significant performance cost.
+template <template <typename> class ContourCombiner, int N>
+class ShapeDistanceChecker {
+public:
+ class ArtifactClassifier : public BaseArtifactClassifier {
+ public:
+ inline ArtifactClassifier(ShapeDistanceChecker *parent, const Vector2 &direction, double span) : BaseArtifactClassifier(span, parent->protectedFlag), parent(parent), direction(direction) { }
+ /// Returns true if the combined results of the tests performed on the median value m interpolated at t indicate an artifact.
+ inline bool evaluate(double t, float m, int flags) const {
+ if (flags&CLASSIFIER_FLAG_CANDIDATE) {
+ // Skip expensive distance evaluation if the point has already been classified as an artifact by the base classifier.
+ if (flags&CLASSIFIER_FLAG_ARTIFACT)
+ return true;
+ Vector2 tVector = t*direction;
+ float oldMSD[N], newMSD[3];
+ // Compute the color that would be currently interpolated at the artifact candidate's position.
+ Point2 sdfCoord = parent->sdfCoord+tVector;
+ interpolate(oldMSD, parent->sdf, sdfCoord);
+ // Compute the color that would be interpolated at the artifact candidate's position if error correction was applied on the current texel.
+ double aWeight = (1-fabs(tVector.x))*(1-fabs(tVector.y));
+ float aPSD = median(parent->msd[0], parent->msd[1], parent->msd[2]);
+ newMSD[0] = float(oldMSD[0]+aWeight*(aPSD-parent->msd[0]));
+ newMSD[1] = float(oldMSD[1]+aWeight*(aPSD-parent->msd[1]));
+ newMSD[2] = float(oldMSD[2]+aWeight*(aPSD-parent->msd[2]));
+ // Compute the evaluated distance (interpolated median) before and after error correction, as well as the exact shape distance.
+ float oldPSD = median(oldMSD[0], oldMSD[1], oldMSD[2]);
+ float newPSD = median(newMSD[0], newMSD[1], newMSD[2]);
+ float refPSD = float(parent->invRange*parent->distanceFinder.distance(parent->shapeCoord+tVector*parent->texelSize)+.5);
+ // Compare the differences of the exact distance and the before and after distances.
+ return parent->minImproveRatio*fabsf(newPSD-refPSD) < double(fabsf(oldPSD-refPSD));
+ }
+ return false;
+ }
+ private:
+ ShapeDistanceChecker *parent;
+ Vector2 direction;
+ };
+ Point2 shapeCoord, sdfCoord;
+ const float *msd;
+ bool protectedFlag;
+ inline ShapeDistanceChecker(const BitmapConstRef<float, N> &sdf, const Shape &shape, const Projection &projection, double invRange, double minImproveRatio) : distanceFinder(shape), sdf(sdf), invRange(invRange), minImproveRatio(minImproveRatio) {
+ texelSize = projection.unprojectVector(Vector2(1));
+ }
+ inline ArtifactClassifier classifier(const Vector2 &direction, double span) {
+ return ArtifactClassifier(this, direction, span);
+ }
+private:
+ ShapeDistanceFinder<ContourCombiner<PseudoDistanceSelector> > distanceFinder;
+ BitmapConstRef<float, N> sdf;
+ double invRange;
+ Vector2 texelSize;
+ double minImproveRatio;
+};
+
+MSDFErrorCorrection::MSDFErrorCorrection() { }
+
+MSDFErrorCorrection::MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const Projection &projection, double range) : stencil(stencil), projection(projection) {
+ invRange = 1/range;
+ minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio;
+ minImproveRatio = ErrorCorrectionConfig::defaultMinImproveRatio;
+ memset(stencil.pixels, 0, sizeof(byte)*stencil.width*stencil.height);
+}
+
+void MSDFErrorCorrection::setMinDeviationRatio(double minDeviationRatio) {
+ this->minDeviationRatio = minDeviationRatio;
+}
+
+void MSDFErrorCorrection::setMinImproveRatio(double minImproveRatio) {
+ this->minImproveRatio = minImproveRatio;
+}
+
+void MSDFErrorCorrection::protectCorners(const Shape &shape) {
+ for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
+ if (!contour->edges.empty()) {
+ const EdgeSegment *prevEdge = contour->edges.back();
+ for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
+ int commonColor = prevEdge->color&(*edge)->color;
+ // If the color changes from prevEdge to edge, this is a corner.
+ if (!(commonColor&(commonColor-1))) {
+ // Find the four texels that envelop the corner and mark them as protected.
+ Point2 p = projection.project((*edge)->point(0));
+ if (shape.inverseYAxis)
+ p.y = stencil.height-p.y;
+ int l = (int) floor(p.x-.5);
+ int b = (int) floor(p.y-.5);
+ int r = l+1;
+ int t = b+1;
+ // Check that the positions are within bounds.
+ if (l < stencil.width && b < stencil.height && r >= 0 && t >= 0) {
+ if (l >= 0 && b >= 0)
+ *stencil(l, b) |= (byte) PROTECTED;
+ if (r < stencil.width && b >= 0)
+ *stencil(r, b) |= (byte) PROTECTED;
+ if (l >= 0 && t < stencil.height)
+ *stencil(l, t) |= (byte) PROTECTED;
+ if (r < stencil.width && t < stencil.height)
+ *stencil(r, t) |= (byte) PROTECTED;
+ }
+ }
+ prevEdge = *edge;
+ }
+ }
+}
+
+/// Determines if the channel contributes to an edge between the two texels a, b.
+static bool edgeBetweenTexelsChannel(const float *a, const float *b, int channel) {
+ // Find interpolation ratio t (0 < t < 1) where an edge is expected (mix(a[channel], b[channel], t) == 0.5).
+ double t = (a[channel]-.5)/(a[channel]-b[channel]);
+ if (t > 0 && t < 1) {
+ // Interpolate channel values at t.
+ float c[3] = {
+ mix(a[0], b[0], t),
+ mix(a[1], b[1], t),
+ mix(a[2], b[2], t)
+ };
+ // This is only an edge if the zero-distance channel is the median.
+ return median(c[0], c[1], c[2]) == c[channel];
+ }
+ return false;
+}
+
+/// Returns a bit mask of which channels contribute to an edge between the two texels a, b.
+static int edgeBetweenTexels(const float *a, const float *b) {
+ return (
+ RED*edgeBetweenTexelsChannel(a, b, 0)+
+ GREEN*edgeBetweenTexelsChannel(a, b, 1)+
+ BLUE*edgeBetweenTexelsChannel(a, b, 2)
+ );
+}
+
+/// Marks texel as protected if one of its non-median channels is present in the channel mask.
+static void protectExtremeChannels(byte *stencil, const float *msd, float m, int mask) {
+ if (
+ (mask&RED && msd[0] != m) ||
+ (mask&GREEN && msd[1] != m) ||
+ (mask&BLUE && msd[2] != m)
+ )
+ *stencil |= (byte) MSDFErrorCorrection::PROTECTED;
+}
+
+template <int N>
+void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, N> &sdf) {
+ float radius;
+ // Horizontal texel pairs
+ radius = float(PROTECTION_RADIUS_TOLERANCE*projection.unprojectVector(Vector2(invRange, 0)).length());
+ for (int y = 0; y < sdf.height; ++y) {
+ const float *left = sdf(0, y);
+ const float *right = sdf(1, y);
+ for (int x = 0; x < sdf.width-1; ++x) {
+ float lm = median(left[0], left[1], left[2]);
+ float rm = median(right[0], right[1], right[2]);
+ if (fabsf(lm-.5f)+fabsf(rm-.5f) < radius) {
+ int mask = edgeBetweenTexels(left, right);
+ protectExtremeChannels(stencil(x, y), left, lm, mask);
+ protectExtremeChannels(stencil(x+1, y), right, rm, mask);
+ }
+ left += N, right += N;
+ }
+ }
+ // Vertical texel pairs
+ radius = float(PROTECTION_RADIUS_TOLERANCE*projection.unprojectVector(Vector2(0, invRange)).length());
+ for (int y = 0; y < sdf.height-1; ++y) {
+ const float *bottom = sdf(0, y);
+ const float *top = sdf(0, y+1);
+ for (int x = 0; x < sdf.width; ++x) {
+ float bm = median(bottom[0], bottom[1], bottom[2]);
+ float tm = median(top[0], top[1], top[2]);
+ if (fabsf(bm-.5f)+fabsf(tm-.5f) < radius) {
+ int mask = edgeBetweenTexels(bottom, top);
+ protectExtremeChannels(stencil(x, y), bottom, bm, mask);
+ protectExtremeChannels(stencil(x, y+1), top, tm, mask);
+ }
+ bottom += N, top += N;
+ }
+ }
+ // Diagonal texel pairs
+ radius = float(PROTECTION_RADIUS_TOLERANCE*projection.unprojectVector(Vector2(invRange)).length());
+ for (int y = 0; y < sdf.height-1; ++y) {
+ const float *lb = sdf(0, y);
+ const float *rb = sdf(1, y);
+ const float *lt = sdf(0, y+1);
+ const float *rt = sdf(1, y+1);
+ for (int x = 0; x < sdf.width-1; ++x) {
+ float mlb = median(lb[0], lb[1], lb[2]);
+ float mrb = median(rb[0], rb[1], rb[2]);
+ float mlt = median(lt[0], lt[1], lt[2]);
+ float mrt = median(rt[0], rt[1], rt[2]);
+ if (fabsf(mlb-.5f)+fabsf(mrt-.5f) < radius) {
+ int mask = edgeBetweenTexels(lb, rt);
+ protectExtremeChannels(stencil(x, y), lb, mlb, mask);
+ protectExtremeChannels(stencil(x+1, y+1), rt, mrt, mask);
+ }
+ if (fabsf(mrb-.5f)+fabsf(mlt-.5f) < radius) {
+ int mask = edgeBetweenTexels(rb, lt);
+ protectExtremeChannels(stencil(x+1, y), rb, mrb, mask);
+ protectExtremeChannels(stencil(x, y+1), lt, mlt, mask);
+ }
+ lb += N, rb += N, lt += N, rt += N;
+ }
+ }
+}
+
+void MSDFErrorCorrection::protectAll() {
+ byte *end = stencil.pixels+stencil.width*stencil.height;
+ for (byte *mask = stencil.pixels; mask < end; ++mask)
+ *mask |= (byte) PROTECTED;
+}
+
+/// Returns the median of the linear interpolation of texels a, b at t.
+static float interpolatedMedian(const float *a, const float *b, double t) {
+ return median(
+ mix(a[0], b[0], t),
+ mix(a[1], b[1], t),
+ mix(a[2], b[2], t)
+ );
+}
+/// Returns the median of the bilinear interpolation with the given constant, linear, and quadratic terms at t.
+static float interpolatedMedian(const float *a, const float *l, const float *q, double t) {
+ return float(median(
+ t*(t*q[0]+l[0])+a[0],
+ t*(t*q[1]+l[1])+a[1],
+ t*(t*q[2]+l[2])+a[2]
+ ));
+}
+
+/// Determines if the interpolated median xm is an artifact.
+static bool isArtifact(bool isProtected, double axSpan, double bxSpan, float am, float bm, float xm) {
+ return (
+ // For protected texels, only report an artifact if it would cause fill inversion (change between positive and negative distance).
+ (!isProtected || (am > .5f && bm > .5f && xm <= .5f) || (am < .5f && bm < .5f && xm >= .5f)) &&
+ // This is an artifact if the interpolated median is outside the range of possible values based on its distance from a, b.
+ !(xm >= am-axSpan && xm <= am+axSpan && xm >= bm-bxSpan && xm <= bm+bxSpan)
+ );
+}
+
+/// Checks if a linear interpolation artifact will occur at a point where two specific color channels are equal - such points have extreme median values.
+template <class ArtifactClassifier>
+static bool hasLinearArtifactInner(const ArtifactClassifier &artifactClassifier, float am, float bm, const float *a, const float *b, float dA, float dB) {
+ // Find interpolation ratio t (0 < t < 1) where two color channels are equal (mix(dA, dB, t) == 0).
+ double t = (double) dA/(dA-dB);
+ if (t > ARTIFACT_T_EPSILON && t < 1-ARTIFACT_T_EPSILON) {
+ // Interpolate median at t and let the classifier decide if its value indicates an artifact.
+ float xm = interpolatedMedian(a, b, t);
+ return artifactClassifier.evaluate(t, xm, artifactClassifier.rangeTest(0, 1, t, am, bm, xm));
+ }
+ return false;
+}
+
+/// Checks if a bilinear interpolation artifact will occur at a point where two specific color channels are equal - such points have extreme median values.
+template <class ArtifactClassifier>
+static bool hasDiagonalArtifactInner(const ArtifactClassifier &artifactClassifier, float am, float dm, const float *a, const float *l, const float *q, float dA, float dBC, float dD, double tEx0, double tEx1) {
+ // Find interpolation ratios t (0 < t[i] < 1) where two color channels are equal.
+ double t[2];
+ int solutions = solveQuadratic(t, dD-dBC+dA, dBC-dA-dA, dA);
+ for (int i = 0; i < solutions; ++i) {
+ // Solutions t[i] == 0 and t[i] == 1 are singularities and occur very often because two channels are usually equal at texels.
+ if (t[i] > ARTIFACT_T_EPSILON && t[i] < 1-ARTIFACT_T_EPSILON) {
+ // Interpolate median xm at t.
+ float xm = interpolatedMedian(a, l, q, t[i]);
+ // Determine if xm deviates too much from medians of a, d.
+ int rangeFlags = artifactClassifier.rangeTest(0, 1, t[i], am, dm, xm);
+ // Additionally, check xm against the interpolated medians at the local extremes tEx0, tEx1.
+ double tEnd[2];
+ float em[2];
+ // tEx0
+ if (tEx0 > 0 && tEx0 < 1) {
+ tEnd[0] = 0, tEnd[1] = 1;
+ em[0] = am, em[1] = dm;
+ tEnd[tEx0 > t[i]] = tEx0;
+ em[tEx0 > t[i]] = interpolatedMedian(a, l, q, tEx0);
+ rangeFlags |= artifactClassifier.rangeTest(tEnd[0], tEnd[1], t[i], am, dm, xm);
+ }
+ // tEx1
+ if (tEx1 > 0 && tEx1 < 1) {
+ tEnd[0] = 0, tEnd[1] = 1;
+ em[0] = am, em[1] = dm;
+ tEnd[tEx1 > t[i]] = tEx1;
+ em[tEx1 > t[i]] = interpolatedMedian(a, l, q, tEx1);
+ rangeFlags |= artifactClassifier.rangeTest(tEnd[0], tEnd[1], t[i], am, dm, xm);
+ }
+ if (artifactClassifier.evaluate(t[i], xm, rangeFlags))
+ return true;
+ }
+ }
+ return false;
+}
+
+/// Checks if a linear interpolation artifact will occur inbetween two horizontally or vertically adjacent texels a, b.
+template <class ArtifactClassifier>
+static bool hasLinearArtifact(const ArtifactClassifier &artifactClassifier, float am, const float *a, const float *b) {
+ float bm = median(b[0], b[1], b[2]);
+ return (
+ // Out of the pair, only report artifacts for the texel further from the edge to minimize side effects.
+ fabsf(am-.5f) >= fabsf(bm-.5f) && (
+ // Check points where each pair of color channels meets.
+ hasLinearArtifactInner(artifactClassifier, am, bm, a, b, a[1]-a[0], b[1]-b[0]) ||
+ hasLinearArtifactInner(artifactClassifier, am, bm, a, b, a[2]-a[1], b[2]-b[1]) ||
+ hasLinearArtifactInner(artifactClassifier, am, bm, a, b, a[0]-a[2], b[0]-b[2])
+ )
+ );
+}
+
+/// Checks if a bilinear interpolation artifact will occur inbetween two diagonally adjacent texels a, d (with b, c forming the other diagonal).
+template <class ArtifactClassifier>
+static bool hasDiagonalArtifact(const ArtifactClassifier &artifactClassifier, float am, const float *a, const float *b, const float *c, const float *d) {
+ float dm = median(d[0], d[1], d[2]);
+ // Out of the pair, only report artifacts for the texel further from the edge to minimize side effects.
+ if (fabsf(am-.5f) >= fabsf(dm-.5f)) {
+ float abc[3] = {
+ a[0]-b[0]-c[0],
+ a[1]-b[1]-c[1],
+ a[2]-b[2]-c[2]
+ };
+ // Compute the linear terms for bilinear interpolation.
+ float l[3] = {
+ -a[0]-abc[0],
+ -a[1]-abc[1],
+ -a[2]-abc[2]
+ };
+ // Compute the quadratic terms for bilinear interpolation.
+ float q[3] = {
+ d[0]+abc[0],
+ d[1]+abc[1],
+ d[2]+abc[2]
+ };
+ // Compute interpolation ratios tEx (0 < tEx[i] < 1) for the local extremes of each color channel (the derivative 2*q[i]*tEx[i]+l[i] == 0).
+ double tEx[3] = {
+ -.5*l[0]/q[0],
+ -.5*l[1]/q[1],
+ -.5*l[2]/q[2]
+ };
+ // Check points where each pair of color channels meets.
+ return (
+ hasDiagonalArtifactInner(artifactClassifier, am, dm, a, l, q, a[1]-a[0], b[1]-b[0]+c[1]-c[0], d[1]-d[0], tEx[0], tEx[1]) ||
+ hasDiagonalArtifactInner(artifactClassifier, am, dm, a, l, q, a[2]-a[1], b[2]-b[1]+c[2]-c[1], d[2]-d[1], tEx[1], tEx[2]) ||
+ hasDiagonalArtifactInner(artifactClassifier, am, dm, a, l, q, a[0]-a[2], b[0]-b[2]+c[0]-c[2], d[0]-d[2], tEx[2], tEx[0])
+ );
+ }
+ return false;
+}
+
+template <int N>
+void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf) {
+ // Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels.
+ double hSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange, 0)).length();
+ double vSpan = minDeviationRatio*projection.unprojectVector(Vector2(0, invRange)).length();
+ double dSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange)).length();
+ // Inspect all texels.
+ for (int y = 0; y < sdf.height; ++y) {
+ for (int x = 0; x < sdf.width; ++x) {
+ const float *c = sdf(x, y);
+ float cm = median(c[0], c[1], c[2]);
+ bool protectedFlag = (*stencil(x, y)&PROTECTED) != 0;
+ const float *l = NULL, *b = NULL, *r = NULL, *t = NULL;
+ // Mark current texel c with the error flag if an artifact occurs when it's interpolated with any of its 8 neighbors.
+ *stencil(x, y) |= (byte) (ERROR*(
+ (x > 0 && ((l = sdf(x-1, y)), hasLinearArtifact(BaseArtifactClassifier(hSpan, protectedFlag), cm, c, l))) ||
+ (y > 0 && ((b = sdf(x, y-1)), hasLinearArtifact(BaseArtifactClassifier(vSpan, protectedFlag), cm, c, b))) ||
+ (x < sdf.width-1 && ((r = sdf(x+1, y)), hasLinearArtifact(BaseArtifactClassifier(hSpan, protectedFlag), cm, c, r))) ||
+ (y < sdf.height-1 && ((t = sdf(x, y+1)), hasLinearArtifact(BaseArtifactClassifier(vSpan, protectedFlag), cm, c, t))) ||
+ (x > 0 && y > 0 && hasDiagonalArtifact(BaseArtifactClassifier(dSpan, protectedFlag), cm, c, l, b, sdf(x-1, y-1))) ||
+ (x < sdf.width-1 && y > 0 && hasDiagonalArtifact(BaseArtifactClassifier(dSpan, protectedFlag), cm, c, r, b, sdf(x+1, y-1))) ||
+ (x > 0 && y < sdf.height-1 && hasDiagonalArtifact(BaseArtifactClassifier(dSpan, protectedFlag), cm, c, l, t, sdf(x-1, y+1))) ||
+ (x < sdf.width-1 && y < sdf.height-1 && hasDiagonalArtifact(BaseArtifactClassifier(dSpan, protectedFlag), cm, c, r, t, sdf(x+1, y+1)))
+ ));
+ }
+ }
+}
+
+template <template <typename> class ContourCombiner, int N>
+void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf, const Shape &shape) {
+ // Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels.
+ double hSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange, 0)).length();
+ double vSpan = minDeviationRatio*projection.unprojectVector(Vector2(0, invRange)).length();
+ double dSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange)).length();
+#ifdef MSDFGEN_USE_OPENMP
+ #pragma omp parallel
+#endif
+ {
+ ShapeDistanceChecker<ContourCombiner, N> shapeDistanceChecker(sdf, shape, projection, invRange, minImproveRatio);
+ bool rightToLeft = false;
+ // Inspect all texels.
+#ifdef MSDFGEN_USE_OPENMP
+ #pragma omp for
+#endif
+ for (int y = 0; y < sdf.height; ++y) {
+ int row = shape.inverseYAxis ? sdf.height-y-1 : y;
+ for (int col = 0; col < sdf.width; ++col) {
+ int x = rightToLeft ? sdf.width-col-1 : col;
+ if ((*stencil(x, row)&ERROR))
+ continue;
+ const float *c = sdf(x, row);
+ shapeDistanceChecker.shapeCoord = projection.unproject(Point2(x+.5, y+.5));
+ shapeDistanceChecker.sdfCoord = Point2(x+.5, row+.5);
+ shapeDistanceChecker.msd = c;
+ shapeDistanceChecker.protectedFlag = (*stencil(x, row)&PROTECTED) != 0;
+ float cm = median(c[0], c[1], c[2]);
+ const float *l = NULL, *b = NULL, *r = NULL, *t = NULL;
+ // Mark current texel c with the error flag if an artifact occurs when it's interpolated with any of its 8 neighbors.
+ *stencil(x, row) |= (byte) (ERROR*(
+ (x > 0 && ((l = sdf(x-1, row)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(-1, 0), hSpan), cm, c, l))) ||
+ (row > 0 && ((b = sdf(x, row-1)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(0, -1), vSpan), cm, c, b))) ||
+ (x < sdf.width-1 && ((r = sdf(x+1, row)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(+1, 0), hSpan), cm, c, r))) ||
+ (row < sdf.height-1 && ((t = sdf(x, row+1)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(0, +1), vSpan), cm, c, t))) ||
+ (x > 0 && row > 0 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(-1, -1), dSpan), cm, c, l, b, sdf(x-1, row-1))) ||
+ (x < sdf.width-1 && row > 0 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(+1, -1), dSpan), cm, c, r, b, sdf(x+1, row-1))) ||
+ (x > 0 && row < sdf.height-1 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(-1, +1), dSpan), cm, c, l, t, sdf(x-1, row+1))) ||
+ (x < sdf.width-1 && row < sdf.height-1 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(+1, +1), dSpan), cm, c, r, t, sdf(x+1, row+1)))
+ ));
+ }
+ }
+ }
+}
+
+template <int N>
+void MSDFErrorCorrection::apply(const BitmapRef<float, N> &sdf) const {
+ int texelCount = sdf.width*sdf.height;
+ const byte *mask = stencil.pixels;
+ float *texel = sdf.pixels;
+ for (int i = 0; i < texelCount; ++i) {
+ if (*mask&ERROR) {
+ // Set all color channels to the median.
+ float m = median(texel[0], texel[1], texel[2]);
+ texel[0] = m, texel[1] = m, texel[2] = m;
+ }
+ ++mask;
+ texel += N;
+ }
+}
+
+BitmapConstRef<byte, 1> MSDFErrorCorrection::getStencil() const {
+ return stencil;
+}
+
+template void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, 3> &sdf);
+template void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, 4> &sdf);
+template void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, 3> &sdf);
+template void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, 4> &sdf);
+template void MSDFErrorCorrection::findErrors<SimpleContourCombiner>(const BitmapConstRef<float, 3> &sdf, const Shape &shape);
+template void MSDFErrorCorrection::findErrors<SimpleContourCombiner>(const BitmapConstRef<float, 4> &sdf, const Shape &shape);
+template void MSDFErrorCorrection::findErrors<OverlappingContourCombiner>(const BitmapConstRef<float, 3> &sdf, const Shape &shape);
+template void MSDFErrorCorrection::findErrors<OverlappingContourCombiner>(const BitmapConstRef<float, 4> &sdf, const Shape &shape);
+template void MSDFErrorCorrection::apply(const BitmapRef<float, 3> &sdf) const;
+template void MSDFErrorCorrection::apply(const BitmapRef<float, 4> &sdf) const;
+
+}
diff --git a/thirdparty/msdfgen/core/MSDFErrorCorrection.h b/thirdparty/msdfgen/core/MSDFErrorCorrection.h
new file mode 100644
index 0000000000..c2e92fbce7
--- /dev/null
+++ b/thirdparty/msdfgen/core/MSDFErrorCorrection.h
@@ -0,0 +1,56 @@
+
+#pragma once
+
+#include "Projection.h"
+#include "Shape.h"
+#include "BitmapRef.hpp"
+
+namespace msdfgen {
+
+/// Performs error correction on a computed MSDF to eliminate interpolation artifacts. This is a low-level class, you may want to use the API in msdf-error-correction.h instead.
+class MSDFErrorCorrection {
+
+public:
+ /// Stencil flags.
+ enum Flags {
+ /// Texel marked as potentially causing interpolation errors.
+ ERROR = 1,
+ /// Texel marked as protected. Protected texels are only given the error flag if they cause inversion artifacts.
+ PROTECTED = 2
+ };
+
+ MSDFErrorCorrection();
+ explicit MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const Projection &projection, double range);
+ /// Sets the minimum ratio between the actual and maximum expected distance delta to be considered an error.
+ void setMinDeviationRatio(double minDeviationRatio);
+ /// Sets the minimum ratio between the pre-correction distance error and the post-correction distance error.
+ void setMinImproveRatio(double minImproveRatio);
+ /// Flags all texels that are interpolated at corners as protected.
+ void protectCorners(const Shape &shape);
+ /// Flags all texels that contribute to edges as protected.
+ template <int N>
+ void protectEdges(const BitmapConstRef<float, N> &sdf);
+ /// Flags all texels as protected.
+ void protectAll();
+ /// Flags texels that are expected to cause interpolation artifacts based on analysis of the SDF only.
+ template <int N>
+ void findErrors(const BitmapConstRef<float, N> &sdf);
+ /// Flags texels that are expected to cause interpolation artifacts based on analysis of the SDF and comparison with the exact shape distance.
+ template <template <typename> class ContourCombiner, int N>
+ void findErrors(const BitmapConstRef<float, N> &sdf, const Shape &shape);
+ /// Modifies the MSDF so that all texels with the error flag are converted to single-channel.
+ template <int N>
+ void apply(const BitmapRef<float, N> &sdf) const;
+ /// Returns the stencil in its current state (see Flags).
+ BitmapConstRef<byte, 1> getStencil() const;
+
+private:
+ BitmapRef<byte, 1> stencil;
+ Projection projection;
+ double invRange;
+ double minDeviationRatio;
+ double minImproveRatio;
+
+};
+
+}
diff --git a/thirdparty/msdfgen/core/Projection.cpp b/thirdparty/msdfgen/core/Projection.cpp
new file mode 100644
index 0000000000..fa2f5e2592
--- /dev/null
+++ b/thirdparty/msdfgen/core/Projection.cpp
@@ -0,0 +1,42 @@
+
+#include "Projection.h"
+
+namespace msdfgen {
+
+Projection::Projection() : scale(1), translate(0) { }
+
+Projection::Projection(const Vector2 &scale, const Vector2 &translate) : scale(scale), translate(translate) { }
+
+Point2 Projection::project(const Point2 &coord) const {
+ return scale*(coord+translate);
+}
+
+Point2 Projection::unproject(const Point2 &coord) const {
+ return coord/scale-translate;
+}
+
+Vector2 Projection::projectVector(const Vector2 &vector) const {
+ return scale*vector;
+}
+
+Vector2 Projection::unprojectVector(const Vector2 &vector) const {
+ return vector/scale;
+}
+
+double Projection::projectX(double x) const {
+ return scale.x*(x+translate.x);
+}
+
+double Projection::projectY(double y) const {
+ return scale.y*(y+translate.y);
+}
+
+double Projection::unprojectX(double x) const {
+ return x/scale.x-translate.x;
+}
+
+double Projection::unprojectY(double y) const {
+ return y/scale.y-translate.y;
+}
+
+}
diff --git a/thirdparty/msdfgen/core/Projection.h b/thirdparty/msdfgen/core/Projection.h
new file mode 100644
index 0000000000..7cdb1c307a
--- /dev/null
+++ b/thirdparty/msdfgen/core/Projection.h
@@ -0,0 +1,37 @@
+
+#pragma once
+
+#include "Vector2.h"
+
+namespace msdfgen {
+
+/// A transformation from shape coordinates to pixel coordinates.
+class Projection {
+
+public:
+ Projection();
+ Projection(const Vector2 &scale, const Vector2 &translate);
+ /// Converts the shape coordinate to pixel coordinate.
+ Point2 project(const Point2 &coord) const;
+ /// Converts the pixel coordinate to shape coordinate.
+ Point2 unproject(const Point2 &coord) const;
+ /// Converts the vector to pixel coordinate space.
+ Vector2 projectVector(const Vector2 &vector) const;
+ /// Converts the vector from pixel coordinate space.
+ Vector2 unprojectVector(const Vector2 &vector) const;
+ /// Converts the X-coordinate from shape to pixel coordinate space.
+ double projectX(double x) const;
+ /// Converts the Y-coordinate from shape to pixel coordinate space.
+ double projectY(double y) const;
+ /// Converts the X-coordinate from pixel to shape coordinate space.
+ double unprojectX(double x) const;
+ /// Converts the Y-coordinate from pixel to shape coordinate space.
+ double unprojectY(double y) const;
+
+private:
+ Vector2 scale;
+ Vector2 translate;
+
+};
+
+}
diff --git a/thirdparty/msdfgen/core/Scanline.cpp b/thirdparty/msdfgen/core/Scanline.cpp
new file mode 100644
index 0000000000..8e5352dbf6
--- /dev/null
+++ b/thirdparty/msdfgen/core/Scanline.cpp
@@ -0,0 +1,125 @@
+
+#include "Scanline.h"
+
+#include <algorithm>
+#include "arithmetics.hpp"
+
+namespace msdfgen {
+
+static int compareIntersections(const void *a, const void *b) {
+ return sign(reinterpret_cast<const Scanline::Intersection *>(a)->x-reinterpret_cast<const Scanline::Intersection *>(b)->x);
+}
+
+bool interpretFillRule(int intersections, FillRule fillRule) {
+ switch (fillRule) {
+ case FILL_NONZERO:
+ return intersections != 0;
+ case FILL_ODD:
+ return intersections&1;
+ case FILL_POSITIVE:
+ return intersections > 0;
+ case FILL_NEGATIVE:
+ return intersections < 0;
+ }
+ return false;
+}
+
+double Scanline::overlap(const Scanline &a, const Scanline &b, double xFrom, double xTo, FillRule fillRule) {
+ double total = 0;
+ bool aInside = false, bInside = false;
+ int ai = 0, bi = 0;
+ double ax = !a.intersections.empty() ? a.intersections[ai].x : xTo;
+ double bx = !b.intersections.empty() ? b.intersections[bi].x : xTo;
+ while (ax < xFrom || bx < xFrom) {
+ double xNext = min(ax, bx);
+ if (ax == xNext && ai < (int) a.intersections.size()) {
+ aInside = interpretFillRule(a.intersections[ai].direction, fillRule);
+ ax = ++ai < (int) a.intersections.size() ? a.intersections[ai].x : xTo;
+ }
+ if (bx == xNext && bi < (int) b.intersections.size()) {
+ bInside = interpretFillRule(b.intersections[bi].direction, fillRule);
+ bx = ++bi < (int) b.intersections.size() ? b.intersections[bi].x : xTo;
+ }
+ }
+ double x = xFrom;
+ while (ax < xTo || bx < xTo) {
+ double xNext = min(ax, bx);
+ if (aInside == bInside)
+ total += xNext-x;
+ if (ax == xNext && ai < (int) a.intersections.size()) {
+ aInside = interpretFillRule(a.intersections[ai].direction, fillRule);
+ ax = ++ai < (int) a.intersections.size() ? a.intersections[ai].x : xTo;
+ }
+ if (bx == xNext && bi < (int) b.intersections.size()) {
+ bInside = interpretFillRule(b.intersections[bi].direction, fillRule);
+ bx = ++bi < (int) b.intersections.size() ? b.intersections[bi].x : xTo;
+ }
+ x = xNext;
+ }
+ if (aInside == bInside)
+ total += xTo-x;
+ return total;
+}
+
+Scanline::Scanline() : lastIndex(0) { }
+
+void Scanline::preprocess() {
+ lastIndex = 0;
+ if (!intersections.empty()) {
+ qsort(&intersections[0], intersections.size(), sizeof(Intersection), compareIntersections);
+ int totalDirection = 0;
+ for (std::vector<Intersection>::iterator intersection = intersections.begin(); intersection != intersections.end(); ++intersection) {
+ totalDirection += intersection->direction;
+ intersection->direction = totalDirection;
+ }
+ }
+}
+
+void Scanline::setIntersections(const std::vector<Intersection> &intersections) {
+ this->intersections = intersections;
+ preprocess();
+}
+
+#ifdef MSDFGEN_USE_CPP11
+void Scanline::setIntersections(std::vector<Intersection> &&intersections) {
+ this->intersections = (std::vector<Intersection> &&) intersections;
+ preprocess();
+}
+#endif
+
+int Scanline::moveTo(double x) const {
+ if (intersections.empty())
+ return -1;
+ int index = lastIndex;
+ if (x < intersections[index].x) {
+ do {
+ if (index == 0) {
+ lastIndex = 0;
+ return -1;
+ }
+ --index;
+ } while (x < intersections[index].x);
+ } else {
+ while (index < (int) intersections.size()-1 && x >= intersections[index+1].x)
+ ++index;
+ }
+ lastIndex = index;
+ return index;
+}
+
+int Scanline::countIntersections(double x) const {
+ return moveTo(x)+1;
+}
+
+int Scanline::sumIntersections(double x) const {
+ int index = moveTo(x);
+ if (index >= 0)
+ return intersections[index].direction;
+ return 0;
+}
+
+bool Scanline::filled(double x, FillRule fillRule) const {
+ return interpretFillRule(sumIntersections(x), fillRule);
+}
+
+}
diff --git a/thirdparty/msdfgen/core/Scanline.h b/thirdparty/msdfgen/core/Scanline.h
new file mode 100644
index 0000000000..9c8f34044b
--- /dev/null
+++ b/thirdparty/msdfgen/core/Scanline.h
@@ -0,0 +1,55 @@
+
+#pragma once
+
+#include <vector>
+
+namespace msdfgen {
+
+/// Fill rule dictates how intersection total is interpreted during rasterization.
+enum FillRule {
+ FILL_NONZERO,
+ FILL_ODD, // "even-odd"
+ FILL_POSITIVE,
+ FILL_NEGATIVE
+};
+
+/// Resolves the number of intersection into a binary fill value based on fill rule.
+bool interpretFillRule(int intersections, FillRule fillRule);
+
+/// Represents a horizontal scanline intersecting a shape.
+class Scanline {
+
+public:
+ /// An intersection with the scanline.
+ struct Intersection {
+ /// X coordinate.
+ double x;
+ /// Normalized Y direction of the oriented edge at the point of intersection.
+ int direction;
+ };
+
+ static double overlap(const Scanline &a, const Scanline &b, double xFrom, double xTo, FillRule fillRule);
+
+ Scanline();
+ /// Populates the intersection list.
+ void setIntersections(const std::vector<Intersection> &intersections);
+#ifdef MSDFGEN_USE_CPP11
+ void setIntersections(std::vector<Intersection> &&intersections);
+#endif
+ /// Returns the number of intersections left of x.
+ int countIntersections(double x) const;
+ /// Returns the total sign of intersections left of x.
+ int sumIntersections(double x) const;
+ /// Decides whether the scanline is filled at x based on fill rule.
+ bool filled(double x, FillRule fillRule) const;
+
+private:
+ std::vector<Intersection> intersections;
+ mutable int lastIndex;
+
+ void preprocess();
+ int moveTo(double x) const;
+
+};
+
+}
diff --git a/thirdparty/msdfgen/core/Shape.cpp b/thirdparty/msdfgen/core/Shape.cpp
new file mode 100644
index 0000000000..8d6f47c807
--- /dev/null
+++ b/thirdparty/msdfgen/core/Shape.cpp
@@ -0,0 +1,183 @@
+
+#include "Shape.h"
+
+#include <algorithm>
+#include "arithmetics.hpp"
+
+namespace msdfgen {
+
+Shape::Shape() : inverseYAxis(false) { }
+
+void Shape::addContour(const Contour &contour) {
+ contours.push_back(contour);
+}
+
+#ifdef MSDFGEN_USE_CPP11
+void Shape::addContour(Contour &&contour) {
+ contours.push_back((Contour &&) contour);
+}
+#endif
+
+Contour & Shape::addContour() {
+ contours.resize(contours.size()+1);
+ return contours.back();
+}
+
+bool Shape::validate() const {
+ for (std::vector<Contour>::const_iterator contour = contours.begin(); contour != contours.end(); ++contour) {
+ if (!contour->edges.empty()) {
+ Point2 corner = contour->edges.back()->point(1);
+ for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
+ if (!*edge)
+ return false;
+ if ((*edge)->point(0) != corner)
+ return false;
+ corner = (*edge)->point(1);
+ }
+ }
+ }
+ return true;
+}
+
+static void deconvergeEdge(EdgeHolder &edgeHolder, int param) {
+ {
+ const QuadraticSegment *quadraticSegment = dynamic_cast<const QuadraticSegment *>(&*edgeHolder);
+ if (quadraticSegment)
+ edgeHolder = quadraticSegment->convertToCubic();
+ }
+ {
+ CubicSegment *cubicSegment = dynamic_cast<CubicSegment *>(&*edgeHolder);
+ if (cubicSegment)
+ cubicSegment->deconverge(param, MSDFGEN_DECONVERGENCE_FACTOR);
+ }
+}
+
+void Shape::normalize() {
+ for (std::vector<Contour>::iterator contour = contours.begin(); contour != contours.end(); ++contour) {
+ if (contour->edges.size() == 1) {
+ EdgeSegment *parts[3] = { };
+ contour->edges[0]->splitInThirds(parts[0], parts[1], parts[2]);
+ contour->edges.clear();
+ contour->edges.push_back(EdgeHolder(parts[0]));
+ contour->edges.push_back(EdgeHolder(parts[1]));
+ contour->edges.push_back(EdgeHolder(parts[2]));
+ } else {
+ EdgeHolder *prevEdge = &contour->edges.back();
+ for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
+ Vector2 prevDir = (*prevEdge)->direction(1).normalize();
+ Vector2 curDir = (*edge)->direction(0).normalize();
+ if (dotProduct(prevDir, curDir) < MSDFGEN_CORNER_DOT_EPSILON-1) {
+ deconvergeEdge(*prevEdge, 1);
+ deconvergeEdge(*edge, 0);
+ }
+ prevEdge = &*edge;
+ }
+ }
+ }
+}
+
+void Shape::bound(double &l, double &b, double &r, double &t) const {
+ for (std::vector<Contour>::const_iterator contour = contours.begin(); contour != contours.end(); ++contour)
+ contour->bound(l, b, r, t);
+}
+
+void Shape::boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const {
+ for (std::vector<Contour>::const_iterator contour = contours.begin(); contour != contours.end(); ++contour)
+ contour->boundMiters(l, b, r, t, border, miterLimit, polarity);
+}
+
+Shape::Bounds Shape::getBounds(double border, double miterLimit, int polarity) const {
+ static const double LARGE_VALUE = 1e240;
+ Shape::Bounds bounds = { +LARGE_VALUE, +LARGE_VALUE, -LARGE_VALUE, -LARGE_VALUE };
+ bound(bounds.l, bounds.b, bounds.r, bounds.t);
+ if (border > 0) {
+ bounds.l -= border, bounds.b -= border;
+ bounds.r += border, bounds.t += border;
+ if (miterLimit > 0)
+ boundMiters(bounds.l, bounds.b, bounds.r, bounds.t, border, miterLimit, polarity);
+ }
+ return bounds;
+}
+
+void Shape::scanline(Scanline &line, double y) const {
+ std::vector<Scanline::Intersection> intersections;
+ double x[3];
+ int dy[3];
+ for (std::vector<Contour>::const_iterator contour = contours.begin(); contour != contours.end(); ++contour) {
+ for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
+ int n = (*edge)->scanlineIntersections(x, dy, y);
+ for (int i = 0; i < n; ++i) {
+ Scanline::Intersection intersection = { x[i], dy[i] };
+ intersections.push_back(intersection);
+ }
+ }
+ }
+#ifdef MSDFGEN_USE_CPP11
+ line.setIntersections((std::vector<Scanline::Intersection> &&) intersections);
+#else
+ line.setIntersections(intersections);
+#endif
+}
+
+int Shape::edgeCount() const {
+ int total = 0;
+ for (std::vector<Contour>::const_iterator contour = contours.begin(); contour != contours.end(); ++contour)
+ total += (int) contour->edges.size();
+ return total;
+}
+
+void Shape::orientContours() {
+ struct Intersection {
+ double x;
+ int direction;
+ int contourIndex;
+
+ static int compare(const void *a, const void *b) {
+ return sign(reinterpret_cast<const Intersection *>(a)->x-reinterpret_cast<const Intersection *>(b)->x);
+ }
+ };
+
+ const double ratio = .5*(sqrt(5)-1); // an irrational number to minimize chance of intersecting a corner or other point of interest
+ std::vector<int> orientations(contours.size());
+ std::vector<Intersection> intersections;
+ for (int i = 0; i < (int) contours.size(); ++i) {
+ if (!orientations[i] && !contours[i].edges.empty()) {
+ // Find an Y that crosses the contour
+ double y0 = contours[i].edges.front()->point(0).y;
+ double y1 = y0;
+ for (std::vector<EdgeHolder>::const_iterator edge = contours[i].edges.begin(); edge != contours[i].edges.end() && y0 == y1; ++edge)
+ y1 = (*edge)->point(1).y;
+ for (std::vector<EdgeHolder>::const_iterator edge = contours[i].edges.begin(); edge != contours[i].edges.end() && y0 == y1; ++edge)
+ y1 = (*edge)->point(ratio).y; // in case all endpoints are in a horizontal line
+ double y = mix(y0, y1, ratio);
+ // Scanline through whole shape at Y
+ double x[3];
+ int dy[3];
+ for (int j = 0; j < (int) contours.size(); ++j) {
+ for (std::vector<EdgeHolder>::const_iterator edge = contours[j].edges.begin(); edge != contours[j].edges.end(); ++edge) {
+ int n = (*edge)->scanlineIntersections(x, dy, y);
+ for (int k = 0; k < n; ++k) {
+ Intersection intersection = { x[k], dy[k], j };
+ intersections.push_back(intersection);
+ }
+ }
+ }
+ qsort(&intersections[0], intersections.size(), sizeof(Intersection), &Intersection::compare);
+ // Disqualify multiple intersections
+ for (int j = 1; j < (int) intersections.size(); ++j)
+ if (intersections[j].x == intersections[j-1].x)
+ intersections[j].direction = intersections[j-1].direction = 0;
+ // Inspect scanline and deduce orientations of intersected contours
+ for (int j = 0; j < (int) intersections.size(); ++j)
+ if (intersections[j].direction)
+ orientations[intersections[j].contourIndex] += 2*((j&1)^(intersections[j].direction > 0))-1;
+ intersections.clear();
+ }
+ }
+ // Reverse contours that have the opposite orientation
+ for (int i = 0; i < (int) contours.size(); ++i)
+ if (orientations[i] < 0)
+ contours[i].reverse();
+}
+
+}
diff --git a/thirdparty/msdfgen/core/Shape.h b/thirdparty/msdfgen/core/Shape.h
new file mode 100644
index 0000000000..7539921ce7
--- /dev/null
+++ b/thirdparty/msdfgen/core/Shape.h
@@ -0,0 +1,55 @@
+
+#pragma once
+
+#include <vector>
+#include "Contour.h"
+#include "Scanline.h"
+
+namespace msdfgen {
+
+// Threshold of the dot product of adjacent edge directions to be considered convergent.
+#define MSDFGEN_CORNER_DOT_EPSILON .000001
+// The proportional amount by which a curve's control point will be adjusted to eliminate convergent corners.
+#define MSDFGEN_DECONVERGENCE_FACTOR .000001
+
+/// Vector shape representation.
+class Shape {
+
+public:
+ struct Bounds {
+ double l, b, r, t;
+ };
+
+ /// The list of contours the shape consists of.
+ std::vector<Contour> contours;
+ /// Specifies whether the shape uses bottom-to-top (false) or top-to-bottom (true) Y coordinates.
+ bool inverseYAxis;
+
+ Shape();
+ /// Adds a contour.
+ void addContour(const Contour &contour);
+#ifdef MSDFGEN_USE_CPP11
+ void addContour(Contour &&contour);
+#endif
+ /// Adds a blank contour and returns its reference.
+ Contour & addContour();
+ /// Normalizes the shape geometry for distance field generation.
+ void normalize();
+ /// Performs basic checks to determine if the object represents a valid shape.
+ bool validate() const;
+ /// Adjusts the bounding box to fit the shape.
+ void bound(double &l, double &b, double &r, double &t) const;
+ /// Adjusts the bounding box to fit the shape border's mitered corners.
+ void boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const;
+ /// Computes the minimum bounding box that fits the shape, optionally with a (mitered) border.
+ Bounds getBounds(double border = 0, double miterLimit = 0, int polarity = 0) const;
+ /// Outputs the scanline that intersects the shape at y.
+ void scanline(Scanline &line, double y) const;
+ /// Returns the total number of edge segments
+ int edgeCount() const;
+ /// Assumes its contours are unoriented (even-odd fill rule). Attempts to orient them to conform to the non-zero winding rule.
+ void orientContours();
+
+};
+
+}
diff --git a/thirdparty/msdfgen/core/ShapeDistanceFinder.h b/thirdparty/msdfgen/core/ShapeDistanceFinder.h
new file mode 100644
index 0000000000..57df8d8e72
--- /dev/null
+++ b/thirdparty/msdfgen/core/ShapeDistanceFinder.h
@@ -0,0 +1,37 @@
+
+#pragma once
+
+#include <vector>
+#include "Vector2.h"
+#include "edge-selectors.h"
+#include "contour-combiners.h"
+
+namespace msdfgen {
+
+/// Finds the distance between a point and a Shape. ContourCombiner dictates the distance metric and its data type.
+template <class ContourCombiner>
+class ShapeDistanceFinder {
+
+public:
+ typedef typename ContourCombiner::DistanceType DistanceType;
+
+ // Passed shape object must persist until the distance finder is destroyed!
+ explicit ShapeDistanceFinder(const Shape &shape);
+ /// Finds the distance from origin. Not thread-safe! Is fastest when subsequent queries are close together.
+ DistanceType distance(const Point2 &origin);
+
+ /// Finds the distance between shape and origin. Does not allocate result cache used to optimize performance of multiple queries.
+ static DistanceType oneShotDistance(const Shape &shape, const Point2 &origin);
+
+private:
+ const Shape &shape;
+ ContourCombiner contourCombiner;
+ std::vector<typename ContourCombiner::EdgeSelectorType::EdgeCache> shapeEdgeCache;
+
+};
+
+typedef ShapeDistanceFinder<SimpleContourCombiner<TrueDistanceSelector> > SimpleTrueShapeDistanceFinder;
+
+}
+
+#include "ShapeDistanceFinder.hpp"
diff --git a/thirdparty/msdfgen/core/ShapeDistanceFinder.hpp b/thirdparty/msdfgen/core/ShapeDistanceFinder.hpp
new file mode 100644
index 0000000000..028738e5c3
--- /dev/null
+++ b/thirdparty/msdfgen/core/ShapeDistanceFinder.hpp
@@ -0,0 +1,56 @@
+
+#include "ShapeDistanceFinder.h"
+
+namespace msdfgen {
+
+template <class ContourCombiner>
+ShapeDistanceFinder<ContourCombiner>::ShapeDistanceFinder(const Shape &shape) : shape(shape), contourCombiner(shape), shapeEdgeCache(shape.edgeCount()) { }
+
+template <class ContourCombiner>
+typename ShapeDistanceFinder<ContourCombiner>::DistanceType ShapeDistanceFinder<ContourCombiner>::distance(const Point2 &origin) {
+ contourCombiner.reset(origin);
+ typename ContourCombiner::EdgeSelectorType::EdgeCache *edgeCache = &shapeEdgeCache[0];
+
+ for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
+ if (!contour->edges.empty()) {
+ typename ContourCombiner::EdgeSelectorType &edgeSelector = contourCombiner.edgeSelector(int(contour-shape.contours.begin()));
+
+ const EdgeSegment *prevEdge = contour->edges.size() >= 2 ? *(contour->edges.end()-2) : *contour->edges.begin();
+ const EdgeSegment *curEdge = contour->edges.back();
+ for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
+ const EdgeSegment *nextEdge = *edge;
+ edgeSelector.addEdge(*edgeCache++, prevEdge, curEdge, nextEdge);
+ prevEdge = curEdge;
+ curEdge = nextEdge;
+ }
+ }
+ }
+
+ return contourCombiner.distance();
+}
+
+template <class ContourCombiner>
+typename ShapeDistanceFinder<ContourCombiner>::DistanceType ShapeDistanceFinder<ContourCombiner>::oneShotDistance(const Shape &shape, const Point2 &origin) {
+ ContourCombiner contourCombiner(shape);
+ contourCombiner.reset(origin);
+
+ for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
+ if (!contour->edges.empty()) {
+ typename ContourCombiner::EdgeSelectorType &edgeSelector = contourCombiner.edgeSelector(int(contour-shape.contours.begin()));
+
+ const EdgeSegment *prevEdge = contour->edges.size() >= 2 ? *(contour->edges.end()-2) : *contour->edges.begin();
+ const EdgeSegment *curEdge = contour->edges.back();
+ for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
+ const EdgeSegment *nextEdge = *edge;
+ typename ContourCombiner::EdgeSelectorType::EdgeCache dummy;
+ edgeSelector.addEdge(dummy, prevEdge, curEdge, nextEdge);
+ prevEdge = curEdge;
+ curEdge = nextEdge;
+ }
+ }
+ }
+
+ return contourCombiner.distance();
+}
+
+}
diff --git a/thirdparty/msdfgen/core/SignedDistance.cpp b/thirdparty/msdfgen/core/SignedDistance.cpp
new file mode 100644
index 0000000000..18c9d2c424
--- /dev/null
+++ b/thirdparty/msdfgen/core/SignedDistance.cpp
@@ -0,0 +1,30 @@
+
+#include "SignedDistance.h"
+
+#include <cmath>
+
+namespace msdfgen {
+
+const SignedDistance SignedDistance::INFINITE(-1e240, 1);
+
+SignedDistance::SignedDistance() : distance(-1e240), dot(1) { }
+
+SignedDistance::SignedDistance(double dist, double d) : distance(dist), dot(d) { }
+
+bool operator<(SignedDistance a, SignedDistance b) {
+ return fabs(a.distance) < fabs(b.distance) || (fabs(a.distance) == fabs(b.distance) && a.dot < b.dot);
+}
+
+bool operator>(SignedDistance a, SignedDistance b) {
+ return fabs(a.distance) > fabs(b.distance) || (fabs(a.distance) == fabs(b.distance) && a.dot > b.dot);
+}
+
+bool operator<=(SignedDistance a, SignedDistance b) {
+ return fabs(a.distance) < fabs(b.distance) || (fabs(a.distance) == fabs(b.distance) && a.dot <= b.dot);
+}
+
+bool operator>=(SignedDistance a, SignedDistance b) {
+ return fabs(a.distance) > fabs(b.distance) || (fabs(a.distance) == fabs(b.distance) && a.dot >= b.dot);
+}
+
+}
diff --git a/thirdparty/msdfgen/core/SignedDistance.h b/thirdparty/msdfgen/core/SignedDistance.h
new file mode 100644
index 0000000000..034210f751
--- /dev/null
+++ b/thirdparty/msdfgen/core/SignedDistance.h
@@ -0,0 +1,25 @@
+
+#pragma once
+
+namespace msdfgen {
+
+/// Represents a signed distance and alignment, which together can be compared to uniquely determine the closest edge segment.
+class SignedDistance {
+
+public:
+ static const SignedDistance INFINITE;
+
+ double distance;
+ double dot;
+
+ SignedDistance();
+ SignedDistance(double dist, double d);
+
+ friend bool operator<(SignedDistance a, SignedDistance b);
+ friend bool operator>(SignedDistance a, SignedDistance b);
+ friend bool operator<=(SignedDistance a, SignedDistance b);
+ friend bool operator>=(SignedDistance a, SignedDistance b);
+
+};
+
+}
diff --git a/thirdparty/msdfgen/core/Vector2.cpp b/thirdparty/msdfgen/core/Vector2.cpp
new file mode 100644
index 0000000000..896963ff2c
--- /dev/null
+++ b/thirdparty/msdfgen/core/Vector2.cpp
@@ -0,0 +1,146 @@
+
+#include "Vector2.h"
+
+namespace msdfgen {
+
+Vector2::Vector2(double val) : x(val), y(val) { }
+
+Vector2::Vector2(double x, double y) : x(x), y(y) { }
+
+void Vector2::reset() {
+ x = 0, y = 0;
+}
+
+void Vector2::set(double x, double y) {
+ Vector2::x = x, Vector2::y = y;
+}
+
+double Vector2::length() const {
+ return sqrt(x*x+y*y);
+}
+
+double Vector2::direction() const {
+ return atan2(y, x);
+}
+
+Vector2 Vector2::normalize(bool allowZero) const {
+ double len = length();
+ if (len == 0)
+ return Vector2(0, !allowZero);
+ return Vector2(x/len, y/len);
+}
+
+Vector2 Vector2::getOrthogonal(bool polarity) const {
+ return polarity ? Vector2(-y, x) : Vector2(y, -x);
+}
+
+Vector2 Vector2::getOrthonormal(bool polarity, bool allowZero) const {
+ double len = length();
+ if (len == 0)
+ return polarity ? Vector2(0, !allowZero) : Vector2(0, -!allowZero);
+ return polarity ? Vector2(-y/len, x/len) : Vector2(y/len, -x/len);
+}
+
+Vector2 Vector2::project(const Vector2 &vector, bool positive) const {
+ Vector2 n = normalize(true);
+ double t = dotProduct(vector, n);
+ if (positive && t <= 0)
+ return Vector2();
+ return t*n;
+}
+
+Vector2::operator const void*() const {
+ return x || y ? this : NULL;
+}
+
+bool Vector2::operator!() const {
+ return !x && !y;
+}
+
+bool Vector2::operator==(const Vector2 &other) const {
+ return x == other.x && y == other.y;
+}
+
+bool Vector2::operator!=(const Vector2 &other) const {
+ return x != other.x || y != other.y;
+}
+
+Vector2 Vector2::operator+() const {
+ return *this;
+}
+
+Vector2 Vector2::operator-() const {
+ return Vector2(-x, -y);
+}
+
+Vector2 Vector2::operator+(const Vector2 &other) const {
+ return Vector2(x+other.x, y+other.y);
+}
+
+Vector2 Vector2::operator-(const Vector2 &other) const {
+ return Vector2(x-other.x, y-other.y);
+}
+
+Vector2 Vector2::operator*(const Vector2 &other) const {
+ return Vector2(x*other.x, y*other.y);
+}
+
+Vector2 Vector2::operator/(const Vector2 &other) const {
+ return Vector2(x/other.x, y/other.y);
+}
+
+Vector2 Vector2::operator*(double value) const {
+ return Vector2(x*value, y*value);
+}
+
+Vector2 Vector2::operator/(double value) const {
+ return Vector2(x/value, y/value);
+}
+
+Vector2 & Vector2::operator+=(const Vector2 &other) {
+ x += other.x, y += other.y;
+ return *this;
+}
+
+Vector2 & Vector2::operator-=(const Vector2 &other) {
+ x -= other.x, y -= other.y;
+ return *this;
+}
+
+Vector2 & Vector2::operator*=(const Vector2 &other) {
+ x *= other.x, y *= other.y;
+ return *this;
+}
+
+Vector2 & Vector2::operator/=(const Vector2 &other) {
+ x /= other.x, y /= other.y;
+ return *this;
+}
+
+Vector2 & Vector2::operator*=(double value) {
+ x *= value, y *= value;
+ return *this;
+}
+
+Vector2 & Vector2::operator/=(double value) {
+ x /= value, y /= value;
+ return *this;
+}
+
+double dotProduct(const Vector2 &a, const Vector2 &b) {
+ return a.x*b.x+a.y*b.y;
+}
+
+double crossProduct(const Vector2 &a, const Vector2 &b) {
+ return a.x*b.y-a.y*b.x;
+}
+
+Vector2 operator*(double value, const Vector2 &vector) {
+ return Vector2(value*vector.x, value*vector.y);
+}
+
+Vector2 operator/(double value, const Vector2 &vector) {
+ return Vector2(value/vector.x, value/vector.y);
+}
+
+}
diff --git a/thirdparty/msdfgen/core/Vector2.h b/thirdparty/msdfgen/core/Vector2.h
new file mode 100644
index 0000000000..47ca637c3d
--- /dev/null
+++ b/thirdparty/msdfgen/core/Vector2.h
@@ -0,0 +1,66 @@
+
+#pragma once
+
+#include <cstdlib>
+#include <cmath>
+
+namespace msdfgen {
+
+/**
+* A 2-dimensional euclidean vector with double precision.
+* Implementation based on the Vector2 template from Artery Engine.
+* @author Viktor Chlumsky
+*/
+struct Vector2 {
+
+ double x, y;
+
+ Vector2(double val = 0);
+ Vector2(double x, double y);
+ /// Sets the vector to zero.
+ void reset();
+ /// Sets individual elements of the vector.
+ void set(double x, double y);
+ /// Returns the vector's length.
+ double length() const;
+ /// Returns the angle of the vector in radians (atan2).
+ double direction() const;
+ /// Returns the normalized vector - one that has the same direction but unit length.
+ Vector2 normalize(bool allowZero = false) const;
+ /// Returns a vector with the same length that is orthogonal to this one.
+ Vector2 getOrthogonal(bool polarity = true) const;
+ /// Returns a vector with unit length that is orthogonal to this one.
+ Vector2 getOrthonormal(bool polarity = true, bool allowZero = false) const;
+ /// Returns a vector projected along this one.
+ Vector2 project(const Vector2 &vector, bool positive = false) const;
+ operator const void *() const;
+ bool operator!() const;
+ bool operator==(const Vector2 &other) const;
+ bool operator!=(const Vector2 &other) const;
+ Vector2 operator+() const;
+ Vector2 operator-() const;
+ Vector2 operator+(const Vector2 &other) const;
+ Vector2 operator-(const Vector2 &other) const;
+ Vector2 operator*(const Vector2 &other) const;
+ Vector2 operator/(const Vector2 &other) const;
+ Vector2 operator*(double value) const;
+ Vector2 operator/(double value) const;
+ Vector2 & operator+=(const Vector2 &other);
+ Vector2 & operator-=(const Vector2 &other);
+ Vector2 & operator*=(const Vector2 &other);
+ Vector2 & operator/=(const Vector2 &other);
+ Vector2 & operator*=(double value);
+ Vector2 & operator/=(double value);
+ /// Dot product of two vectors.
+ friend double dotProduct(const Vector2 &a, const Vector2 &b);
+ /// A special version of the cross product for 2D vectors (returns scalar value).
+ friend double crossProduct(const Vector2 &a, const Vector2 &b);
+ friend Vector2 operator*(double value, const Vector2 &vector);
+ friend Vector2 operator/(double value, const Vector2 &vector);
+
+};
+
+/// A vector may also represent a point, which shall be differentiated semantically using the alias Point2.
+typedef Vector2 Point2;
+
+}
diff --git a/thirdparty/msdfgen/core/arithmetics.hpp b/thirdparty/msdfgen/core/arithmetics.hpp
new file mode 100644
index 0000000000..78c21d658e
--- /dev/null
+++ b/thirdparty/msdfgen/core/arithmetics.hpp
@@ -0,0 +1,63 @@
+
+#pragma once
+
+#include <cstdlib>
+#include <cmath>
+
+namespace msdfgen {
+
+/// Returns the smaller of the arguments.
+template <typename T>
+inline T min(T a, T b) {
+ return b < a ? b : a;
+}
+
+/// Returns the larger of the arguments.
+template <typename T>
+inline T max(T a, T b) {
+ return a < b ? b : a;
+}
+
+/// Returns the middle out of three values
+template <typename T>
+inline T median(T a, T b, T c) {
+ return max(min(a, b), min(max(a, b), c));
+}
+
+/// Returns the weighted average of a and b.
+template <typename T, typename S>
+inline T mix(T a, T b, S weight) {
+ return T((S(1)-weight)*a+weight*b);
+}
+
+/// Clamps the number to the interval from 0 to 1.
+template <typename T>
+inline T clamp(T n) {
+ return n >= T(0) && n <= T(1) ? n : T(n > T(0));
+}
+
+/// Clamps the number to the interval from 0 to b.
+template <typename T>
+inline T clamp(T n, T b) {
+ return n >= T(0) && n <= b ? n : T(n > T(0))*b;
+}
+
+/// Clamps the number to the interval from a to b.
+template <typename T>
+inline T clamp(T n, T a, T b) {
+ return n >= a && n <= b ? n : n < a ? a : b;
+}
+
+/// Returns 1 for positive values, -1 for negative values, and 0 for zero.
+template <typename T>
+inline int sign(T n) {
+ return (T(0) < n)-(n < T(0));
+}
+
+/// Returns 1 for non-negative values and -1 for negative values.
+template <typename T>
+inline int nonZeroSign(T n) {
+ return 2*(n > T(0))-1;
+}
+
+}
diff --git a/thirdparty/msdfgen/core/bitmap-interpolation.hpp b/thirdparty/msdfgen/core/bitmap-interpolation.hpp
new file mode 100644
index 0000000000..a14b0fb534
--- /dev/null
+++ b/thirdparty/msdfgen/core/bitmap-interpolation.hpp
@@ -0,0 +1,25 @@
+
+#pragma once
+
+#include "arithmetics.hpp"
+#include "Vector2.h"
+#include "BitmapRef.hpp"
+
+namespace msdfgen {
+
+template <typename T, int N>
+static void interpolate(T *output, const BitmapConstRef<T, N> &bitmap, Point2 pos) {
+ pos -= .5;
+ int l = (int) floor(pos.x);
+ int b = (int) floor(pos.y);
+ int r = l+1;
+ int t = b+1;
+ double lr = pos.x-l;
+ double bt = pos.y-b;
+ l = clamp(l, bitmap.width-1), r = clamp(r, bitmap.width-1);
+ b = clamp(b, bitmap.height-1), t = clamp(t, bitmap.height-1);
+ for (int i = 0; i < N; ++i)
+ output[i] = mix(mix(bitmap(l, b)[i], bitmap(r, b)[i], lr), mix(bitmap(l, t)[i], bitmap(r, t)[i], lr), bt);
+}
+
+}
diff --git a/thirdparty/msdfgen/core/contour-combiners.cpp b/thirdparty/msdfgen/core/contour-combiners.cpp
new file mode 100644
index 0000000000..d0c5b46d74
--- /dev/null
+++ b/thirdparty/msdfgen/core/contour-combiners.cpp
@@ -0,0 +1,133 @@
+
+#include "contour-combiners.h"
+
+#include "arithmetics.hpp"
+
+namespace msdfgen {
+
+static void initDistance(double &distance) {
+ distance = SignedDistance::INFINITE.distance;
+}
+
+static void initDistance(MultiDistance &distance) {
+ distance.r = SignedDistance::INFINITE.distance;
+ distance.g = SignedDistance::INFINITE.distance;
+ distance.b = SignedDistance::INFINITE.distance;
+}
+
+static double resolveDistance(double distance) {
+ return distance;
+}
+
+static double resolveDistance(const MultiDistance &distance) {
+ return median(distance.r, distance.g, distance.b);
+}
+
+template <class EdgeSelector>
+SimpleContourCombiner<EdgeSelector>::SimpleContourCombiner(const Shape &shape) { }
+
+template <class EdgeSelector>
+void SimpleContourCombiner<EdgeSelector>::reset(const Point2 &p) {
+ shapeEdgeSelector.reset(p);
+}
+
+template <class EdgeSelector>
+EdgeSelector & SimpleContourCombiner<EdgeSelector>::edgeSelector(int) {
+ return shapeEdgeSelector;
+}
+
+template <class EdgeSelector>
+typename SimpleContourCombiner<EdgeSelector>::DistanceType SimpleContourCombiner<EdgeSelector>::distance() const {
+ return shapeEdgeSelector.distance();
+}
+
+template class SimpleContourCombiner<TrueDistanceSelector>;
+template class SimpleContourCombiner<PseudoDistanceSelector>;
+template class SimpleContourCombiner<MultiDistanceSelector>;
+template class SimpleContourCombiner<MultiAndTrueDistanceSelector>;
+
+template <class EdgeSelector>
+OverlappingContourCombiner<EdgeSelector>::OverlappingContourCombiner(const Shape &shape) {
+ windings.reserve(shape.contours.size());
+ for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
+ windings.push_back(contour->winding());
+ edgeSelectors.resize(shape.contours.size());
+}
+
+template <class EdgeSelector>
+void OverlappingContourCombiner<EdgeSelector>::reset(const Point2 &p) {
+ this->p = p;
+ for (typename std::vector<EdgeSelector>::iterator contourEdgeSelector = edgeSelectors.begin(); contourEdgeSelector != edgeSelectors.end(); ++contourEdgeSelector)
+ contourEdgeSelector->reset(p);
+}
+
+template <class EdgeSelector>
+EdgeSelector & OverlappingContourCombiner<EdgeSelector>::edgeSelector(int i) {
+ return edgeSelectors[i];
+}
+
+template <class EdgeSelector>
+typename OverlappingContourCombiner<EdgeSelector>::DistanceType OverlappingContourCombiner<EdgeSelector>::distance() const {
+ int contourCount = (int) edgeSelectors.size();
+ EdgeSelector shapeEdgeSelector;
+ EdgeSelector innerEdgeSelector;
+ EdgeSelector outerEdgeSelector;
+ shapeEdgeSelector.reset(p);
+ innerEdgeSelector.reset(p);
+ outerEdgeSelector.reset(p);
+ for (int i = 0; i < contourCount; ++i) {
+ DistanceType edgeDistance = edgeSelectors[i].distance();
+ shapeEdgeSelector.merge(edgeSelectors[i]);
+ if (windings[i] > 0 && resolveDistance(edgeDistance) >= 0)
+ innerEdgeSelector.merge(edgeSelectors[i]);
+ if (windings[i] < 0 && resolveDistance(edgeDistance) <= 0)
+ outerEdgeSelector.merge(edgeSelectors[i]);
+ }
+
+ DistanceType shapeDistance = shapeEdgeSelector.distance();
+ DistanceType innerDistance = innerEdgeSelector.distance();
+ DistanceType outerDistance = outerEdgeSelector.distance();
+ double innerScalarDistance = resolveDistance(innerDistance);
+ double outerScalarDistance = resolveDistance(outerDistance);
+ DistanceType distance;
+ initDistance(distance);
+
+ int winding = 0;
+ if (innerScalarDistance >= 0 && fabs(innerScalarDistance) <= fabs(outerScalarDistance)) {
+ distance = innerDistance;
+ winding = 1;
+ for (int i = 0; i < contourCount; ++i)
+ if (windings[i] > 0) {
+ DistanceType contourDistance = edgeSelectors[i].distance();
+ if (fabs(resolveDistance(contourDistance)) < fabs(outerScalarDistance) && resolveDistance(contourDistance) > resolveDistance(distance))
+ distance = contourDistance;
+ }
+ } else if (outerScalarDistance <= 0 && fabs(outerScalarDistance) < fabs(innerScalarDistance)) {
+ distance = outerDistance;
+ winding = -1;
+ for (int i = 0; i < contourCount; ++i)
+ if (windings[i] < 0) {
+ DistanceType contourDistance = edgeSelectors[i].distance();
+ if (fabs(resolveDistance(contourDistance)) < fabs(innerScalarDistance) && resolveDistance(contourDistance) < resolveDistance(distance))
+ distance = contourDistance;
+ }
+ } else
+ return shapeDistance;
+
+ for (int i = 0; i < contourCount; ++i)
+ if (windings[i] != winding) {
+ DistanceType contourDistance = edgeSelectors[i].distance();
+ if (resolveDistance(contourDistance)*resolveDistance(distance) >= 0 && fabs(resolveDistance(contourDistance)) < fabs(resolveDistance(distance)))
+ distance = contourDistance;
+ }
+ if (resolveDistance(distance) == resolveDistance(shapeDistance))
+ distance = shapeDistance;
+ return distance;
+}
+
+template class OverlappingContourCombiner<TrueDistanceSelector>;
+template class OverlappingContourCombiner<PseudoDistanceSelector>;
+template class OverlappingContourCombiner<MultiDistanceSelector>;
+template class OverlappingContourCombiner<MultiAndTrueDistanceSelector>;
+
+}
diff --git a/thirdparty/msdfgen/core/contour-combiners.h b/thirdparty/msdfgen/core/contour-combiners.h
new file mode 100644
index 0000000000..944b119aba
--- /dev/null
+++ b/thirdparty/msdfgen/core/contour-combiners.h
@@ -0,0 +1,47 @@
+
+#pragma once
+
+#include "Shape.h"
+#include "edge-selectors.h"
+
+namespace msdfgen {
+
+/// Simply selects the nearest contour.
+template <class EdgeSelector>
+class SimpleContourCombiner {
+
+public:
+ typedef EdgeSelector EdgeSelectorType;
+ typedef typename EdgeSelector::DistanceType DistanceType;
+
+ explicit SimpleContourCombiner(const Shape &shape);
+ void reset(const Point2 &p);
+ EdgeSelector & edgeSelector(int i);
+ DistanceType distance() const;
+
+private:
+ EdgeSelector shapeEdgeSelector;
+
+};
+
+/// Selects the nearest contour that actually forms a border between filled and unfilled area.
+template <class EdgeSelector>
+class OverlappingContourCombiner {
+
+public:
+ typedef EdgeSelector EdgeSelectorType;
+ typedef typename EdgeSelector::DistanceType DistanceType;
+
+ explicit OverlappingContourCombiner(const Shape &shape);
+ void reset(const Point2 &p);
+ EdgeSelector & edgeSelector(int i);
+ DistanceType distance() const;
+
+private:
+ Point2 p;
+ std::vector<int> windings;
+ std::vector<EdgeSelector> edgeSelectors;
+
+};
+
+}
diff --git a/thirdparty/msdfgen/core/edge-coloring.cpp b/thirdparty/msdfgen/core/edge-coloring.cpp
new file mode 100644
index 0000000000..370f9aa38d
--- /dev/null
+++ b/thirdparty/msdfgen/core/edge-coloring.cpp
@@ -0,0 +1,499 @@
+
+#include "edge-coloring.h"
+
+#include <cstdlib>
+#include <cmath>
+#include <cstring>
+#include <queue>
+#include "arithmetics.hpp"
+
+namespace msdfgen {
+
+static bool isCorner(const Vector2 &aDir, const Vector2 &bDir, double crossThreshold) {
+ return dotProduct(aDir, bDir) <= 0 || fabs(crossProduct(aDir, bDir)) > crossThreshold;
+}
+
+static double estimateEdgeLength(const EdgeSegment *edge) {
+ double len = 0;
+ Point2 prev = edge->point(0);
+ for (int i = 1; i <= MSDFGEN_EDGE_LENGTH_PRECISION; ++i) {
+ Point2 cur = edge->point(1./MSDFGEN_EDGE_LENGTH_PRECISION*i);
+ len += (cur-prev).length();
+ prev = cur;
+ }
+ return len;
+}
+
+static void switchColor(EdgeColor &color, unsigned long long &seed, EdgeColor banned = BLACK) {
+ EdgeColor combined = EdgeColor(color&banned);
+ if (combined == RED || combined == GREEN || combined == BLUE) {
+ color = EdgeColor(combined^WHITE);
+ return;
+ }
+ if (color == BLACK || color == WHITE) {
+ static const EdgeColor start[3] = { CYAN, MAGENTA, YELLOW };
+ color = start[seed%3];
+ seed /= 3;
+ return;
+ }
+ int shifted = color<<(1+(seed&1));
+ color = EdgeColor((shifted|shifted>>3)&WHITE);
+ seed >>= 1;
+}
+
+void edgeColoringSimple(Shape &shape, double angleThreshold, unsigned long long seed) {
+ double crossThreshold = sin(angleThreshold);
+ std::vector<int> corners;
+ for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
+ // Identify corners
+ corners.clear();
+ if (!contour->edges.empty()) {
+ Vector2 prevDirection = contour->edges.back()->direction(1);
+ int index = 0;
+ for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge, ++index) {
+ if (isCorner(prevDirection.normalize(), (*edge)->direction(0).normalize(), crossThreshold))
+ corners.push_back(index);
+ prevDirection = (*edge)->direction(1);
+ }
+ }
+
+ // Smooth contour
+ if (corners.empty())
+ for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge)
+ (*edge)->color = WHITE;
+ // "Teardrop" case
+ else if (corners.size() == 1) {
+ EdgeColor colors[3] = { WHITE, WHITE };
+ switchColor(colors[0], seed);
+ switchColor(colors[2] = colors[0], seed);
+ int corner = corners[0];
+ if (contour->edges.size() >= 3) {
+ int m = (int) contour->edges.size();
+ for (int i = 0; i < m; ++i)
+ contour->edges[(corner+i)%m]->color = (colors+1)[int(3+2.875*i/(m-1)-1.4375+.5)-3];
+ } else if (contour->edges.size() >= 1) {
+ // Less than three edge segments for three colors => edges must be split
+ EdgeSegment *parts[7] = { };
+ contour->edges[0]->splitInThirds(parts[0+3*corner], parts[1+3*corner], parts[2+3*corner]);
+ if (contour->edges.size() >= 2) {
+ contour->edges[1]->splitInThirds(parts[3-3*corner], parts[4-3*corner], parts[5-3*corner]);
+ parts[0]->color = parts[1]->color = colors[0];
+ parts[2]->color = parts[3]->color = colors[1];
+ parts[4]->color = parts[5]->color = colors[2];
+ } else {
+ parts[0]->color = colors[0];
+ parts[1]->color = colors[1];
+ parts[2]->color = colors[2];
+ }
+ contour->edges.clear();
+ for (int i = 0; parts[i]; ++i)
+ contour->edges.push_back(EdgeHolder(parts[i]));
+ }
+ }
+ // Multiple corners
+ else {
+ int cornerCount = (int) corners.size();
+ int spline = 0;
+ int start = corners[0];
+ int m = (int) contour->edges.size();
+ EdgeColor color = WHITE;
+ switchColor(color, seed);
+ EdgeColor initialColor = color;
+ for (int i = 0; i < m; ++i) {
+ int index = (start+i)%m;
+ if (spline+1 < cornerCount && corners[spline+1] == index) {
+ ++spline;
+ switchColor(color, seed, EdgeColor((spline == cornerCount-1)*initialColor));
+ }
+ contour->edges[index]->color = color;
+ }
+ }
+ }
+}
+
+struct EdgeColoringInkTrapCorner {
+ int index;
+ double prevEdgeLengthEstimate;
+ bool minor;
+ EdgeColor color;
+};
+
+void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long seed) {
+ typedef EdgeColoringInkTrapCorner Corner;
+ double crossThreshold = sin(angleThreshold);
+ std::vector<Corner> corners;
+ for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
+ // Identify corners
+ double splineLength = 0;
+ corners.clear();
+ if (!contour->edges.empty()) {
+ Vector2 prevDirection = contour->edges.back()->direction(1);
+ int index = 0;
+ for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge, ++index) {
+ if (isCorner(prevDirection.normalize(), (*edge)->direction(0).normalize(), crossThreshold)) {
+ Corner corner = { index, splineLength };
+ corners.push_back(corner);
+ splineLength = 0;
+ }
+ splineLength += estimateEdgeLength(*edge);
+ prevDirection = (*edge)->direction(1);
+ }
+ }
+
+ // Smooth contour
+ if (corners.empty())
+ for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge)
+ (*edge)->color = WHITE;
+ // "Teardrop" case
+ else if (corners.size() == 1) {
+ EdgeColor colors[3] = { WHITE, WHITE };
+ switchColor(colors[0], seed);
+ switchColor(colors[2] = colors[0], seed);
+ int corner = corners[0].index;
+ if (contour->edges.size() >= 3) {
+ int m = (int) contour->edges.size();
+ for (int i = 0; i < m; ++i)
+ contour->edges[(corner+i)%m]->color = (colors+1)[int(3+2.875*i/(m-1)-1.4375+.5)-3];
+ } else if (contour->edges.size() >= 1) {
+ // Less than three edge segments for three colors => edges must be split
+ EdgeSegment *parts[7] = { };
+ contour->edges[0]->splitInThirds(parts[0+3*corner], parts[1+3*corner], parts[2+3*corner]);
+ if (contour->edges.size() >= 2) {
+ contour->edges[1]->splitInThirds(parts[3-3*corner], parts[4-3*corner], parts[5-3*corner]);
+ parts[0]->color = parts[1]->color = colors[0];
+ parts[2]->color = parts[3]->color = colors[1];
+ parts[4]->color = parts[5]->color = colors[2];
+ } else {
+ parts[0]->color = colors[0];
+ parts[1]->color = colors[1];
+ parts[2]->color = colors[2];
+ }
+ contour->edges.clear();
+ for (int i = 0; parts[i]; ++i)
+ contour->edges.push_back(EdgeHolder(parts[i]));
+ }
+ }
+ // Multiple corners
+ else {
+ int cornerCount = (int) corners.size();
+ int majorCornerCount = cornerCount;
+ if (cornerCount > 3) {
+ corners.begin()->prevEdgeLengthEstimate += splineLength;
+ for (int i = 0; i < cornerCount; ++i) {
+ if (
+ corners[i].prevEdgeLengthEstimate > corners[(i+1)%cornerCount].prevEdgeLengthEstimate &&
+ corners[(i+1)%cornerCount].prevEdgeLengthEstimate < corners[(i+2)%cornerCount].prevEdgeLengthEstimate
+ ) {
+ corners[i].minor = true;
+ --majorCornerCount;
+ }
+ }
+ }
+ EdgeColor color = WHITE;
+ EdgeColor initialColor = BLACK;
+ for (int i = 0; i < cornerCount; ++i) {
+ if (!corners[i].minor) {
+ --majorCornerCount;
+ switchColor(color, seed, EdgeColor(!majorCornerCount*initialColor));
+ corners[i].color = color;
+ if (!initialColor)
+ initialColor = color;
+ }
+ }
+ for (int i = 0; i < cornerCount; ++i) {
+ if (corners[i].minor) {
+ EdgeColor nextColor = corners[(i+1)%cornerCount].color;
+ corners[i].color = EdgeColor((color&nextColor)^WHITE);
+ } else
+ color = corners[i].color;
+ }
+ int spline = 0;
+ int start = corners[0].index;
+ color = corners[0].color;
+ int m = (int) contour->edges.size();
+ for (int i = 0; i < m; ++i) {
+ int index = (start+i)%m;
+ if (spline+1 < cornerCount && corners[spline+1].index == index)
+ color = corners[++spline].color;
+ contour->edges[index]->color = color;
+ }
+ }
+ }
+}
+
+// EDGE COLORING BY DISTANCE - EXPERIMENTAL IMPLEMENTATION - WORK IN PROGRESS
+#define MAX_RECOLOR_STEPS 16
+#define EDGE_DISTANCE_PRECISION 16
+
+static double edgeToEdgeDistance(const EdgeSegment &a, const EdgeSegment &b, int precision) {
+ if (a.point(0) == b.point(0) || a.point(0) == b.point(1) || a.point(1) == b.point(0) || a.point(1) == b.point(1))
+ return 0;
+ double iFac = 1./precision;
+ double minDistance = (b.point(0)-a.point(0)).length();
+ for (int i = 0; i <= precision; ++i) {
+ double t = iFac*i;
+ double d = fabs(a.signedDistance(b.point(t), t).distance);
+ minDistance = min(minDistance, d);
+ }
+ for (int i = 0; i <= precision; ++i) {
+ double t = iFac*i;
+ double d = fabs(b.signedDistance(a.point(t), t).distance);
+ minDistance = min(minDistance, d);
+ }
+ return minDistance;
+}
+
+static double splineToSplineDistance(EdgeSegment * const *edgeSegments, int aStart, int aEnd, int bStart, int bEnd, int precision) {
+ double minDistance = fabs(SignedDistance::INFINITE.distance);
+ for (int ai = aStart; ai < aEnd; ++ai)
+ for (int bi = bStart; bi < bEnd && minDistance; ++bi) {
+ double d = edgeToEdgeDistance(*edgeSegments[ai], *edgeSegments[bi], precision);
+ minDistance = min(minDistance, d);
+ }
+ return minDistance;
+}
+
+static void colorSecondDegreeGraph(int *coloring, const int * const *edgeMatrix, int vertexCount, unsigned long long seed) {
+ for (int i = 0; i < vertexCount; ++i) {
+ int possibleColors = 7;
+ for (int j = 0; j < i; ++j) {
+ if (edgeMatrix[i][j])
+ possibleColors &= ~(1<<coloring[j]);
+ }
+ int color = 0;
+ switch (possibleColors) {
+ case 1:
+ color = 0;
+ break;
+ case 2:
+ color = 1;
+ break;
+ case 3:
+ color = (int) seed&1;
+ seed >>= 1;
+ break;
+ case 4:
+ color = 2;
+ break;
+ case 5:
+ color = ((int) seed+1&1)<<1;
+ seed >>= 1;
+ break;
+ case 6:
+ color = ((int) seed&1)+1;
+ seed >>= 1;
+ break;
+ case 7:
+ color = int((seed+i)%3);
+ seed /= 3;
+ break;
+ }
+ coloring[i] = color;
+ }
+}
+
+static int vertexPossibleColors(const int *coloring, const int *edgeVector, int vertexCount) {
+ int usedColors = 0;
+ for (int i = 0; i < vertexCount; ++i)
+ if (edgeVector[i])
+ usedColors |= 1<<coloring[i];
+ return 7&~usedColors;
+}
+
+static void uncolorSameNeighbors(std::queue<int> &uncolored, int *coloring, const int * const *edgeMatrix, int vertex, int vertexCount) {
+ for (int i = vertex+1; i < vertexCount; ++i) {
+ if (edgeMatrix[vertex][i] && coloring[i] == coloring[vertex]) {
+ coloring[i] = -1;
+ uncolored.push(i);
+ }
+ }
+ for (int i = 0; i < vertex; ++i) {
+ if (edgeMatrix[vertex][i] && coloring[i] == coloring[vertex]) {
+ coloring[i] = -1;
+ uncolored.push(i);
+ }
+ }
+}
+
+static bool tryAddEdge(int *coloring, int * const *edgeMatrix, int vertexCount, int vertexA, int vertexB, int *coloringBuffer) {
+ static const int FIRST_POSSIBLE_COLOR[8] = { -1, 0, 1, 0, 2, 2, 1, 0 };
+ edgeMatrix[vertexA][vertexB] = 1;
+ edgeMatrix[vertexB][vertexA] = 1;
+ if (coloring[vertexA] != coloring[vertexB])
+ return true;
+ int bPossibleColors = vertexPossibleColors(coloring, edgeMatrix[vertexB], vertexCount);
+ if (bPossibleColors) {
+ coloring[vertexB] = FIRST_POSSIBLE_COLOR[bPossibleColors];
+ return true;
+ }
+ memcpy(coloringBuffer, coloring, sizeof(int)*vertexCount);
+ std::queue<int> uncolored;
+ {
+ int *coloring = coloringBuffer;
+ coloring[vertexB] = FIRST_POSSIBLE_COLOR[7&~(1<<coloring[vertexA])];
+ uncolorSameNeighbors(uncolored, coloring, edgeMatrix, vertexB, vertexCount);
+ int step = 0;
+ while (!uncolored.empty() && step < MAX_RECOLOR_STEPS) {
+ int i = uncolored.front();
+ uncolored.pop();
+ int possibleColors = vertexPossibleColors(coloring, edgeMatrix[i], vertexCount);
+ if (possibleColors) {
+ coloring[i] = FIRST_POSSIBLE_COLOR[possibleColors];
+ continue;
+ }
+ do {
+ coloring[i] = step++%3;
+ } while (edgeMatrix[i][vertexA] && coloring[i] == coloring[vertexA]);
+ uncolorSameNeighbors(uncolored, coloring, edgeMatrix, i, vertexCount);
+ }
+ }
+ if (!uncolored.empty()) {
+ edgeMatrix[vertexA][vertexB] = 0;
+ edgeMatrix[vertexB][vertexA] = 0;
+ return false;
+ }
+ memcpy(coloring, coloringBuffer, sizeof(int)*vertexCount);
+ return true;
+}
+
+static int cmpDoublePtr(const void *a, const void *b) {
+ return sign(**reinterpret_cast<const double * const *>(a)-**reinterpret_cast<const double * const *>(b));
+}
+
+void edgeColoringByDistance(Shape &shape, double angleThreshold, unsigned long long seed) {
+
+ std::vector<EdgeSegment *> edgeSegments;
+ std::vector<int> splineStarts;
+
+ double crossThreshold = sin(angleThreshold);
+ std::vector<int> corners;
+ for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
+ if (!contour->edges.empty()) {
+ // Identify corners
+ corners.clear();
+ Vector2 prevDirection = contour->edges.back()->direction(1);
+ int index = 0;
+ for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge, ++index) {
+ if (isCorner(prevDirection.normalize(), (*edge)->direction(0).normalize(), crossThreshold))
+ corners.push_back(index);
+ prevDirection = (*edge)->direction(1);
+ }
+
+ splineStarts.push_back((int) edgeSegments.size());
+ // Smooth contour
+ if (corners.empty())
+ for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge)
+ edgeSegments.push_back(&**edge);
+ // "Teardrop" case
+ else if (corners.size() == 1) {
+ int corner = corners[0];
+ if (contour->edges.size() >= 3) {
+ int m = (int) contour->edges.size();
+ for (int i = 0; i < m; ++i) {
+ if (i == m/2)
+ splineStarts.push_back((int) edgeSegments.size());
+ if (int(3+2.875*i/(m-1)-1.4375+.5)-3)
+ edgeSegments.push_back(&*contour->edges[(corner+i)%m]);
+ else
+ contour->edges[(corner+i)%m]->color = WHITE;
+ }
+ } else if (contour->edges.size() >= 1) {
+ // Less than three edge segments for three colors => edges must be split
+ EdgeSegment *parts[7] = { };
+ contour->edges[0]->splitInThirds(parts[0+3*corner], parts[1+3*corner], parts[2+3*corner]);
+ if (contour->edges.size() >= 2) {
+ contour->edges[1]->splitInThirds(parts[3-3*corner], parts[4-3*corner], parts[5-3*corner]);
+ edgeSegments.push_back(parts[0]);
+ edgeSegments.push_back(parts[1]);
+ parts[2]->color = parts[3]->color = WHITE;
+ splineStarts.push_back((int) edgeSegments.size());
+ edgeSegments.push_back(parts[4]);
+ edgeSegments.push_back(parts[5]);
+ } else {
+ edgeSegments.push_back(parts[0]);
+ parts[1]->color = WHITE;
+ splineStarts.push_back((int) edgeSegments.size());
+ edgeSegments.push_back(parts[2]);
+ }
+ contour->edges.clear();
+ for (int i = 0; parts[i]; ++i)
+ contour->edges.push_back(EdgeHolder(parts[i]));
+ }
+ }
+ // Multiple corners
+ else {
+ int cornerCount = (int) corners.size();
+ int spline = 0;
+ int start = corners[0];
+ int m = (int) contour->edges.size();
+ for (int i = 0; i < m; ++i) {
+ int index = (start+i)%m;
+ if (spline+1 < cornerCount && corners[spline+1] == index) {
+ splineStarts.push_back((int) edgeSegments.size());
+ ++spline;
+ }
+ edgeSegments.push_back(&*contour->edges[index]);
+ }
+ }
+ }
+ splineStarts.push_back((int) edgeSegments.size());
+
+ int segmentCount = (int) edgeSegments.size();
+ int splineCount = (int) splineStarts.size()-1;
+ if (!splineCount)
+ return;
+
+ std::vector<double> distanceMatrixStorage(splineCount*splineCount);
+ std::vector<double *> distanceMatrix(splineCount);
+ for (int i = 0; i < splineCount; ++i)
+ distanceMatrix[i] = &distanceMatrixStorage[i*splineCount];
+ const double *distanceMatrixBase = &distanceMatrixStorage[0];
+
+ for (int i = 0; i < splineCount; ++i) {
+ distanceMatrix[i][i] = -1;
+ for (int j = i+1; j < splineCount; ++j) {
+ double dist = splineToSplineDistance(&edgeSegments[0], splineStarts[i], splineStarts[i+1], splineStarts[j], splineStarts[j+1], EDGE_DISTANCE_PRECISION);
+ distanceMatrix[i][j] = dist;
+ distanceMatrix[j][i] = dist;
+ }
+ }
+
+ std::vector<const double *> graphEdgeDistances;
+ graphEdgeDistances.reserve(splineCount*(splineCount-1)/2);
+ for (int i = 0; i < splineCount; ++i)
+ for (int j = i+1; j < splineCount; ++j)
+ graphEdgeDistances.push_back(&distanceMatrix[i][j]);
+ int graphEdgeCount = (int) graphEdgeDistances.size();
+ if (!graphEdgeDistances.empty())
+ qsort(&graphEdgeDistances[0], graphEdgeDistances.size(), sizeof(const double *), &cmpDoublePtr);
+
+ std::vector<int> edgeMatrixStorage(splineCount*splineCount);
+ std::vector<int *> edgeMatrix(splineCount);
+ for (int i = 0; i < splineCount; ++i)
+ edgeMatrix[i] = &edgeMatrixStorage[i*splineCount];
+ int nextEdge = 0;
+ for (; nextEdge < graphEdgeCount && !*graphEdgeDistances[nextEdge]; ++nextEdge) {
+ int elem = graphEdgeDistances[nextEdge]-distanceMatrixBase;
+ int row = elem/splineCount;
+ int col = elem%splineCount;
+ edgeMatrix[row][col] = 1;
+ edgeMatrix[col][row] = 1;
+ }
+
+ std::vector<int> coloring(2*splineCount);
+ colorSecondDegreeGraph(&coloring[0], &edgeMatrix[0], splineCount, seed);
+ for (; nextEdge < graphEdgeCount; ++nextEdge) {
+ int elem = graphEdgeDistances[nextEdge]-distanceMatrixBase;
+ tryAddEdge(&coloring[0], &edgeMatrix[0], splineCount, elem/splineCount, elem%splineCount, &coloring[splineCount]);
+ }
+
+ const EdgeColor colors[3] = { YELLOW, CYAN, MAGENTA };
+ int spline = -1;
+ for (int i = 0; i < segmentCount; ++i) {
+ if (splineStarts[spline+1] == i)
+ ++spline;
+ edgeSegments[i]->color = colors[coloring[spline]];
+ }
+}
+
+}
diff --git a/thirdparty/msdfgen/core/edge-coloring.h b/thirdparty/msdfgen/core/edge-coloring.h
new file mode 100644
index 0000000000..ffd5e6dce8
--- /dev/null
+++ b/thirdparty/msdfgen/core/edge-coloring.h
@@ -0,0 +1,29 @@
+
+#pragma once
+
+#include "Shape.h"
+
+#define MSDFGEN_EDGE_LENGTH_PRECISION 4
+
+namespace msdfgen {
+
+/** Assigns colors to edges of the shape in accordance to the multi-channel distance field technique.
+ * May split some edges if necessary.
+ * angleThreshold specifies the maximum angle (in radians) to be considered a corner, for example 3 (~172 degrees).
+ * Values below 1/2 PI will be treated as the external angle.
+ */
+void edgeColoringSimple(Shape &shape, double angleThreshold, unsigned long long seed = 0);
+
+/** The alternative "ink trap" coloring strategy is designed for better results with typefaces
+ * that use ink traps as a design feature. It guarantees that even if all edges that are shorter than
+ * both their neighboring edges are removed, the coloring remains consistent with the established rules.
+ */
+void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long seed = 0);
+
+/** The alternative coloring by distance tries to use different colors for edges that are close together.
+ * This should theoretically be the best strategy on average. However, since it needs to compute the distance
+ * between all pairs of edges, and perform a graph optimization task, it is much slower than the rest.
+ */
+void edgeColoringByDistance(Shape &shape, double angleThreshold, unsigned long long seed = 0);
+
+}
diff --git a/thirdparty/msdfgen/core/edge-segments.cpp b/thirdparty/msdfgen/core/edge-segments.cpp
new file mode 100644
index 0000000000..5274a9a5a1
--- /dev/null
+++ b/thirdparty/msdfgen/core/edge-segments.cpp
@@ -0,0 +1,504 @@
+
+#include "edge-segments.h"
+
+#include "arithmetics.hpp"
+#include "equation-solver.h"
+
+namespace msdfgen {
+
+void EdgeSegment::distanceToPseudoDistance(SignedDistance &distance, Point2 origin, double param) const {
+ if (param < 0) {
+ Vector2 dir = direction(0).normalize();
+ Vector2 aq = origin-point(0);
+ double ts = dotProduct(aq, dir);
+ if (ts < 0) {
+ double pseudoDistance = crossProduct(aq, dir);
+ if (fabs(pseudoDistance) <= fabs(distance.distance)) {
+ distance.distance = pseudoDistance;
+ distance.dot = 0;
+ }
+ }
+ } else if (param > 1) {
+ Vector2 dir = direction(1).normalize();
+ Vector2 bq = origin-point(1);
+ double ts = dotProduct(bq, dir);
+ if (ts > 0) {
+ double pseudoDistance = crossProduct(bq, dir);
+ if (fabs(pseudoDistance) <= fabs(distance.distance)) {
+ distance.distance = pseudoDistance;
+ distance.dot = 0;
+ }
+ }
+ }
+}
+
+LinearSegment::LinearSegment(Point2 p0, Point2 p1, EdgeColor edgeColor) : EdgeSegment(edgeColor) {
+ p[0] = p0;
+ p[1] = p1;
+}
+
+QuadraticSegment::QuadraticSegment(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor) : EdgeSegment(edgeColor) {
+ if (p1 == p0 || p1 == p2)
+ p1 = 0.5*(p0+p2);
+ p[0] = p0;
+ p[1] = p1;
+ p[2] = p2;
+}
+
+CubicSegment::CubicSegment(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor) : EdgeSegment(edgeColor) {
+ if ((p1 == p0 || p1 == p3) && (p2 == p0 || p2 == p3)) {
+ p1 = mix(p0, p3, 1/3.);
+ p2 = mix(p0, p3, 2/3.);
+ }
+ p[0] = p0;
+ p[1] = p1;
+ p[2] = p2;
+ p[3] = p3;
+}
+
+LinearSegment * LinearSegment::clone() const {
+ return new LinearSegment(p[0], p[1], color);
+}
+
+QuadraticSegment * QuadraticSegment::clone() const {
+ return new QuadraticSegment(p[0], p[1], p[2], color);
+}
+
+CubicSegment * CubicSegment::clone() const {
+ return new CubicSegment(p[0], p[1], p[2], p[3], color);
+}
+
+Point2 LinearSegment::point(double param) const {
+ return mix(p[0], p[1], param);
+}
+
+Point2 QuadraticSegment::point(double param) const {
+ return mix(mix(p[0], p[1], param), mix(p[1], p[2], param), param);
+}
+
+Point2 CubicSegment::point(double param) const {
+ Vector2 p12 = mix(p[1], p[2], param);
+ return mix(mix(mix(p[0], p[1], param), p12, param), mix(p12, mix(p[2], p[3], param), param), param);
+}
+
+Vector2 LinearSegment::direction(double param) const {
+ return p[1]-p[0];
+}
+
+Vector2 QuadraticSegment::direction(double param) const {
+ Vector2 tangent = mix(p[1]-p[0], p[2]-p[1], param);
+ if (!tangent)
+ return p[2]-p[0];
+ return tangent;
+}
+
+Vector2 CubicSegment::direction(double param) const {
+ Vector2 tangent = mix(mix(p[1]-p[0], p[2]-p[1], param), mix(p[2]-p[1], p[3]-p[2], param), param);
+ if (!tangent) {
+ if (param == 0) return p[2]-p[0];
+ if (param == 1) return p[3]-p[1];
+ }
+ return tangent;
+}
+
+Vector2 LinearSegment::directionChange(double param) const {
+ return Vector2();
+}
+
+Vector2 QuadraticSegment::directionChange(double param) const {
+ return (p[2]-p[1])-(p[1]-p[0]);
+}
+
+Vector2 CubicSegment::directionChange(double param) const {
+ return mix((p[2]-p[1])-(p[1]-p[0]), (p[3]-p[2])-(p[2]-p[1]), param);
+}
+
+double LinearSegment::length() const {
+ return (p[1]-p[0]).length();
+}
+
+double QuadraticSegment::length() const {
+ Vector2 ab = p[1]-p[0];
+ Vector2 br = p[2]-p[1]-ab;
+ double abab = dotProduct(ab, ab);
+ double abbr = dotProduct(ab, br);
+ double brbr = dotProduct(br, br);
+ double abLen = sqrt(abab);
+ double brLen = sqrt(brbr);
+ double crs = crossProduct(ab, br);
+ double h = sqrt(abab+abbr+abbr+brbr);
+ return (
+ brLen*((abbr+brbr)*h-abbr*abLen)+
+ crs*crs*log((brLen*h+abbr+brbr)/(brLen*abLen+abbr))
+ )/(brbr*brLen);
+}
+
+SignedDistance LinearSegment::signedDistance(Point2 origin, double &param) const {
+ Vector2 aq = origin-p[0];
+ Vector2 ab = p[1]-p[0];
+ param = dotProduct(aq, ab)/dotProduct(ab, ab);
+ Vector2 eq = p[param > .5]-origin;
+ double endpointDistance = eq.length();
+ if (param > 0 && param < 1) {
+ double orthoDistance = dotProduct(ab.getOrthonormal(false), aq);
+ if (fabs(orthoDistance) < endpointDistance)
+ return SignedDistance(orthoDistance, 0);
+ }
+ return SignedDistance(nonZeroSign(crossProduct(aq, ab))*endpointDistance, fabs(dotProduct(ab.normalize(), eq.normalize())));
+}
+
+SignedDistance QuadraticSegment::signedDistance(Point2 origin, double &param) const {
+ Vector2 qa = p[0]-origin;
+ Vector2 ab = p[1]-p[0];
+ Vector2 br = p[2]-p[1]-ab;
+ double a = dotProduct(br, br);
+ double b = 3*dotProduct(ab, br);
+ double c = 2*dotProduct(ab, ab)+dotProduct(qa, br);
+ double d = dotProduct(qa, ab);
+ double t[3];
+ int solutions = solveCubic(t, a, b, c, d);
+
+ Vector2 epDir = direction(0);
+ double minDistance = nonZeroSign(crossProduct(epDir, qa))*qa.length(); // distance from A
+ param = -dotProduct(qa, epDir)/dotProduct(epDir, epDir);
+ {
+ epDir = direction(1);
+ double distance = (p[2]-origin).length(); // distance from B
+ if (distance < fabs(minDistance)) {
+ minDistance = nonZeroSign(crossProduct(epDir, p[2]-origin))*distance;
+ param = dotProduct(origin-p[1], epDir)/dotProduct(epDir, epDir);
+ }
+ }
+ for (int i = 0; i < solutions; ++i) {
+ if (t[i] > 0 && t[i] < 1) {
+ Point2 qe = qa+2*t[i]*ab+t[i]*t[i]*br;
+ double distance = qe.length();
+ if (distance <= fabs(minDistance)) {
+ minDistance = nonZeroSign(crossProduct(ab+t[i]*br, qe))*distance;
+ param = t[i];
+ }
+ }
+ }
+
+ if (param >= 0 && param <= 1)
+ return SignedDistance(minDistance, 0);
+ if (param < .5)
+ return SignedDistance(minDistance, fabs(dotProduct(direction(0).normalize(), qa.normalize())));
+ else
+ return SignedDistance(minDistance, fabs(dotProduct(direction(1).normalize(), (p[2]-origin).normalize())));
+}
+
+SignedDistance CubicSegment::signedDistance(Point2 origin, double &param) const {
+ Vector2 qa = p[0]-origin;
+ Vector2 ab = p[1]-p[0];
+ Vector2 br = p[2]-p[1]-ab;
+ Vector2 as = (p[3]-p[2])-(p[2]-p[1])-br;
+
+ Vector2 epDir = direction(0);
+ double minDistance = nonZeroSign(crossProduct(epDir, qa))*qa.length(); // distance from A
+ param = -dotProduct(qa, epDir)/dotProduct(epDir, epDir);
+ {
+ epDir = direction(1);
+ double distance = (p[3]-origin).length(); // distance from B
+ if (distance < fabs(minDistance)) {
+ minDistance = nonZeroSign(crossProduct(epDir, p[3]-origin))*distance;
+ param = dotProduct(epDir-(p[3]-origin), epDir)/dotProduct(epDir, epDir);
+ }
+ }
+ // Iterative minimum distance search
+ for (int i = 0; i <= MSDFGEN_CUBIC_SEARCH_STARTS; ++i) {
+ double t = (double) i/MSDFGEN_CUBIC_SEARCH_STARTS;
+ Vector2 qe = qa+3*t*ab+3*t*t*br+t*t*t*as;
+ for (int step = 0; step < MSDFGEN_CUBIC_SEARCH_STEPS; ++step) {
+ // Improve t
+ Vector2 d1 = 3*ab+6*t*br+3*t*t*as;
+ Vector2 d2 = 6*br+6*t*as;
+ t -= dotProduct(qe, d1)/(dotProduct(d1, d1)+dotProduct(qe, d2));
+ if (t <= 0 || t >= 1)
+ break;
+ qe = qa+3*t*ab+3*t*t*br+t*t*t*as;
+ double distance = qe.length();
+ if (distance < fabs(minDistance)) {
+ minDistance = nonZeroSign(crossProduct(d1, qe))*distance;
+ param = t;
+ }
+ }
+ }
+
+ if (param >= 0 && param <= 1)
+ return SignedDistance(minDistance, 0);
+ if (param < .5)
+ return SignedDistance(minDistance, fabs(dotProduct(direction(0).normalize(), qa.normalize())));
+ else
+ return SignedDistance(minDistance, fabs(dotProduct(direction(1).normalize(), (p[3]-origin).normalize())));
+}
+
+int LinearSegment::scanlineIntersections(double x[3], int dy[3], double y) const {
+ if ((y >= p[0].y && y < p[1].y) || (y >= p[1].y && y < p[0].y)) {
+ double param = (y-p[0].y)/(p[1].y-p[0].y);
+ x[0] = mix(p[0].x, p[1].x, param);
+ dy[0] = sign(p[1].y-p[0].y);
+ return 1;
+ }
+ return 0;
+}
+
+int QuadraticSegment::scanlineIntersections(double x[3], int dy[3], double y) const {
+ int total = 0;
+ int nextDY = y > p[0].y ? 1 : -1;
+ x[total] = p[0].x;
+ if (p[0].y == y) {
+ if (p[0].y < p[1].y || (p[0].y == p[1].y && p[0].y < p[2].y))
+ dy[total++] = 1;
+ else
+ nextDY = 1;
+ }
+ {
+ Vector2 ab = p[1]-p[0];
+ Vector2 br = p[2]-p[1]-ab;
+ double t[2];
+ int solutions = solveQuadratic(t, br.y, 2*ab.y, p[0].y-y);
+ // Sort solutions
+ double tmp;
+ if (solutions >= 2 && t[0] > t[1])
+ tmp = t[0], t[0] = t[1], t[1] = tmp;
+ for (int i = 0; i < solutions && total < 2; ++i) {
+ if (t[i] >= 0 && t[i] <= 1) {
+ x[total] = p[0].x+2*t[i]*ab.x+t[i]*t[i]*br.x;
+ if (nextDY*(ab.y+t[i]*br.y) >= 0) {
+ dy[total++] = nextDY;
+ nextDY = -nextDY;
+ }
+ }
+ }
+ }
+ if (p[2].y == y) {
+ if (nextDY > 0 && total > 0) {
+ --total;
+ nextDY = -1;
+ }
+ if ((p[2].y < p[1].y || (p[2].y == p[1].y && p[2].y < p[0].y)) && total < 2) {
+ x[total] = p[2].x;
+ if (nextDY < 0) {
+ dy[total++] = -1;
+ nextDY = 1;
+ }
+ }
+ }
+ if (nextDY != (y >= p[2].y ? 1 : -1)) {
+ if (total > 0)
+ --total;
+ else {
+ if (fabs(p[2].y-y) < fabs(p[0].y-y))
+ x[total] = p[2].x;
+ dy[total++] = nextDY;
+ }
+ }
+ return total;
+}
+
+int CubicSegment::scanlineIntersections(double x[3], int dy[3], double y) const {
+ int total = 0;
+ int nextDY = y > p[0].y ? 1 : -1;
+ x[total] = p[0].x;
+ if (p[0].y == y) {
+ if (p[0].y < p[1].y || (p[0].y == p[1].y && (p[0].y < p[2].y || (p[0].y == p[2].y && p[0].y < p[3].y))))
+ dy[total++] = 1;
+ else
+ nextDY = 1;
+ }
+ {
+ Vector2 ab = p[1]-p[0];
+ Vector2 br = p[2]-p[1]-ab;
+ Vector2 as = (p[3]-p[2])-(p[2]-p[1])-br;
+ double t[3];
+ int solutions = solveCubic(t, as.y, 3*br.y, 3*ab.y, p[0].y-y);
+ // Sort solutions
+ double tmp;
+ if (solutions >= 2) {
+ if (t[0] > t[1])
+ tmp = t[0], t[0] = t[1], t[1] = tmp;
+ if (solutions >= 3 && t[1] > t[2]) {
+ tmp = t[1], t[1] = t[2], t[2] = tmp;
+ if (t[0] > t[1])
+ tmp = t[0], t[0] = t[1], t[1] = tmp;
+ }
+ }
+ for (int i = 0; i < solutions && total < 3; ++i) {
+ if (t[i] >= 0 && t[i] <= 1) {
+ x[total] = p[0].x+3*t[i]*ab.x+3*t[i]*t[i]*br.x+t[i]*t[i]*t[i]*as.x;
+ if (nextDY*(ab.y+2*t[i]*br.y+t[i]*t[i]*as.y) >= 0) {
+ dy[total++] = nextDY;
+ nextDY = -nextDY;
+ }
+ }
+ }
+ }
+ if (p[3].y == y) {
+ if (nextDY > 0 && total > 0) {
+ --total;
+ nextDY = -1;
+ }
+ if ((p[3].y < p[2].y || (p[3].y == p[2].y && (p[3].y < p[1].y || (p[3].y == p[1].y && p[3].y < p[0].y)))) && total < 3) {
+ x[total] = p[3].x;
+ if (nextDY < 0) {
+ dy[total++] = -1;
+ nextDY = 1;
+ }
+ }
+ }
+ if (nextDY != (y >= p[3].y ? 1 : -1)) {
+ if (total > 0)
+ --total;
+ else {
+ if (fabs(p[3].y-y) < fabs(p[0].y-y))
+ x[total] = p[3].x;
+ dy[total++] = nextDY;
+ }
+ }
+ return total;
+}
+
+static void pointBounds(Point2 p, double &l, double &b, double &r, double &t) {
+ if (p.x < l) l = p.x;
+ if (p.y < b) b = p.y;
+ if (p.x > r) r = p.x;
+ if (p.y > t) t = p.y;
+}
+
+void LinearSegment::bound(double &l, double &b, double &r, double &t) const {
+ pointBounds(p[0], l, b, r, t);
+ pointBounds(p[1], l, b, r, t);
+}
+
+void QuadraticSegment::bound(double &l, double &b, double &r, double &t) const {
+ pointBounds(p[0], l, b, r, t);
+ pointBounds(p[2], l, b, r, t);
+ Vector2 bot = (p[1]-p[0])-(p[2]-p[1]);
+ if (bot.x) {
+ double param = (p[1].x-p[0].x)/bot.x;
+ if (param > 0 && param < 1)
+ pointBounds(point(param), l, b, r, t);
+ }
+ if (bot.y) {
+ double param = (p[1].y-p[0].y)/bot.y;
+ if (param > 0 && param < 1)
+ pointBounds(point(param), l, b, r, t);
+ }
+}
+
+void CubicSegment::bound(double &l, double &b, double &r, double &t) const {
+ pointBounds(p[0], l, b, r, t);
+ pointBounds(p[3], l, b, r, t);
+ Vector2 a0 = p[1]-p[0];
+ Vector2 a1 = 2*(p[2]-p[1]-a0);
+ Vector2 a2 = p[3]-3*p[2]+3*p[1]-p[0];
+ double params[2];
+ int solutions;
+ solutions = solveQuadratic(params, a2.x, a1.x, a0.x);
+ for (int i = 0; i < solutions; ++i)
+ if (params[i] > 0 && params[i] < 1)
+ pointBounds(point(params[i]), l, b, r, t);
+ solutions = solveQuadratic(params, a2.y, a1.y, a0.y);
+ for (int i = 0; i < solutions; ++i)
+ if (params[i] > 0 && params[i] < 1)
+ pointBounds(point(params[i]), l, b, r, t);
+}
+
+void LinearSegment::reverse() {
+ Point2 tmp = p[0];
+ p[0] = p[1];
+ p[1] = tmp;
+}
+
+void QuadraticSegment::reverse() {
+ Point2 tmp = p[0];
+ p[0] = p[2];
+ p[2] = tmp;
+}
+
+void CubicSegment::reverse() {
+ Point2 tmp = p[0];
+ p[0] = p[3];
+ p[3] = tmp;
+ tmp = p[1];
+ p[1] = p[2];
+ p[2] = tmp;
+}
+
+void LinearSegment::moveStartPoint(Point2 to) {
+ p[0] = to;
+}
+
+void QuadraticSegment::moveStartPoint(Point2 to) {
+ Vector2 origSDir = p[0]-p[1];
+ Point2 origP1 = p[1];
+ p[1] += crossProduct(p[0]-p[1], to-p[0])/crossProduct(p[0]-p[1], p[2]-p[1])*(p[2]-p[1]);
+ p[0] = to;
+ if (dotProduct(origSDir, p[0]-p[1]) < 0)
+ p[1] = origP1;
+}
+
+void CubicSegment::moveStartPoint(Point2 to) {
+ p[1] += to-p[0];
+ p[0] = to;
+}
+
+void LinearSegment::moveEndPoint(Point2 to) {
+ p[1] = to;
+}
+
+void QuadraticSegment::moveEndPoint(Point2 to) {
+ Vector2 origEDir = p[2]-p[1];
+ Point2 origP1 = p[1];
+ p[1] += crossProduct(p[2]-p[1], to-p[2])/crossProduct(p[2]-p[1], p[0]-p[1])*(p[0]-p[1]);
+ p[2] = to;
+ if (dotProduct(origEDir, p[2]-p[1]) < 0)
+ p[1] = origP1;
+}
+
+void CubicSegment::moveEndPoint(Point2 to) {
+ p[2] += to-p[3];
+ p[3] = to;
+}
+
+void LinearSegment::splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const {
+ part1 = new LinearSegment(p[0], point(1/3.), color);
+ part2 = new LinearSegment(point(1/3.), point(2/3.), color);
+ part3 = new LinearSegment(point(2/3.), p[1], color);
+}
+
+void QuadraticSegment::splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const {
+ part1 = new QuadraticSegment(p[0], mix(p[0], p[1], 1/3.), point(1/3.), color);
+ part2 = new QuadraticSegment(point(1/3.), mix(mix(p[0], p[1], 5/9.), mix(p[1], p[2], 4/9.), .5), point(2/3.), color);
+ part3 = new QuadraticSegment(point(2/3.), mix(p[1], p[2], 2/3.), p[2], color);
+}
+
+void CubicSegment::splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const {
+ part1 = new CubicSegment(p[0], p[0] == p[1] ? p[0] : mix(p[0], p[1], 1/3.), mix(mix(p[0], p[1], 1/3.), mix(p[1], p[2], 1/3.), 1/3.), point(1/3.), color);
+ part2 = new CubicSegment(point(1/3.),
+ mix(mix(mix(p[0], p[1], 1/3.), mix(p[1], p[2], 1/3.), 1/3.), mix(mix(p[1], p[2], 1/3.), mix(p[2], p[3], 1/3.), 1/3.), 2/3.),
+ mix(mix(mix(p[0], p[1], 2/3.), mix(p[1], p[2], 2/3.), 2/3.), mix(mix(p[1], p[2], 2/3.), mix(p[2], p[3], 2/3.), 2/3.), 1/3.),
+ point(2/3.), color);
+ part3 = new CubicSegment(point(2/3.), mix(mix(p[1], p[2], 2/3.), mix(p[2], p[3], 2/3.), 2/3.), p[2] == p[3] ? p[3] : mix(p[2], p[3], 2/3.), p[3], color);
+}
+
+EdgeSegment * QuadraticSegment::convertToCubic() const {
+ return new CubicSegment(p[0], mix(p[0], p[1], 2/3.), mix(p[1], p[2], 1/3.), p[2], color);
+}
+
+void CubicSegment::deconverge(int param, double amount) {
+ Vector2 dir = direction(param);
+ Vector2 normal = dir.getOrthonormal();
+ double h = dotProduct(directionChange(param)-dir, normal);
+ switch (param) {
+ case 0:
+ p[1] += amount*(dir+sign(h)*sqrt(fabs(h))*normal);
+ break;
+ case 1:
+ p[2] -= amount*(dir-sign(h)*sqrt(fabs(h))*normal);
+ break;
+ }
+}
+
+}
diff --git a/thirdparty/msdfgen/core/edge-segments.h b/thirdparty/msdfgen/core/edge-segments.h
new file mode 100644
index 0000000000..1c8fb599ff
--- /dev/null
+++ b/thirdparty/msdfgen/core/edge-segments.h
@@ -0,0 +1,122 @@
+
+#pragma once
+
+#include "Vector2.h"
+#include "SignedDistance.h"
+#include "EdgeColor.h"
+
+namespace msdfgen {
+
+// Parameters for iterative search of closest point on a cubic Bezier curve. Increase for higher precision.
+#define MSDFGEN_CUBIC_SEARCH_STARTS 4
+#define MSDFGEN_CUBIC_SEARCH_STEPS 4
+
+/// An abstract edge segment.
+class EdgeSegment {
+
+public:
+ EdgeColor color;
+
+ EdgeSegment(EdgeColor edgeColor = WHITE) : color(edgeColor) { }
+ virtual ~EdgeSegment() { }
+ /// Creates a copy of the edge segment.
+ virtual EdgeSegment * clone() const = 0;
+ /// Returns the point on the edge specified by the parameter (between 0 and 1).
+ virtual Point2 point(double param) const = 0;
+ /// Returns the direction the edge has at the point specified by the parameter.
+ virtual Vector2 direction(double param) const = 0;
+ /// Returns the change of direction (second derivative) at the point specified by the parameter.
+ virtual Vector2 directionChange(double param) const = 0;
+ /// Returns the minimum signed distance between origin and the edge.
+ virtual SignedDistance signedDistance(Point2 origin, double &param) const = 0;
+ /// Converts a previously retrieved signed distance from origin to pseudo-distance.
+ virtual void distanceToPseudoDistance(SignedDistance &distance, Point2 origin, double param) const;
+ /// Outputs a list of (at most three) intersections (their X coordinates) with an infinite horizontal scanline at y and returns how many there are.
+ virtual int scanlineIntersections(double x[3], int dy[3], double y) const = 0;
+ /// Adjusts the bounding box to fit the edge segment.
+ virtual void bound(double &l, double &b, double &r, double &t) const = 0;
+
+ /// Reverses the edge (swaps its start point and end point).
+ virtual void reverse() = 0;
+ /// Moves the start point of the edge segment.
+ virtual void moveStartPoint(Point2 to) = 0;
+ /// Moves the end point of the edge segment.
+ virtual void moveEndPoint(Point2 to) = 0;
+ /// Splits the edge segments into thirds which together represent the original edge.
+ virtual void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const = 0;
+
+};
+
+/// A line segment.
+class LinearSegment : public EdgeSegment {
+
+public:
+ Point2 p[2];
+
+ LinearSegment(Point2 p0, Point2 p1, EdgeColor edgeColor = WHITE);
+ LinearSegment * clone() const;
+ Point2 point(double param) const;
+ Vector2 direction(double param) const;
+ Vector2 directionChange(double param) const;
+ double length() const;
+ SignedDistance signedDistance(Point2 origin, double &param) const;
+ int scanlineIntersections(double x[3], int dy[3], double y) const;
+ void bound(double &l, double &b, double &r, double &t) const;
+
+ void reverse();
+ void moveStartPoint(Point2 to);
+ void moveEndPoint(Point2 to);
+ void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const;
+
+};
+
+/// A quadratic Bezier curve.
+class QuadraticSegment : public EdgeSegment {
+
+public:
+ Point2 p[3];
+
+ QuadraticSegment(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor = WHITE);
+ QuadraticSegment * clone() const;
+ Point2 point(double param) const;
+ Vector2 direction(double param) const;
+ Vector2 directionChange(double param) const;
+ double length() const;
+ SignedDistance signedDistance(Point2 origin, double &param) const;
+ int scanlineIntersections(double x[3], int dy[3], double y) const;
+ void bound(double &l, double &b, double &r, double &t) const;
+
+ void reverse();
+ void moveStartPoint(Point2 to);
+ void moveEndPoint(Point2 to);
+ void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const;
+
+ EdgeSegment * convertToCubic() const;
+
+};
+
+/// A cubic Bezier curve.
+class CubicSegment : public EdgeSegment {
+
+public:
+ Point2 p[4];
+
+ CubicSegment(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor = WHITE);
+ CubicSegment * clone() const;
+ Point2 point(double param) const;
+ Vector2 direction(double param) const;
+ Vector2 directionChange(double param) const;
+ SignedDistance signedDistance(Point2 origin, double &param) const;
+ int scanlineIntersections(double x[3], int dy[3], double y) const;
+ void bound(double &l, double &b, double &r, double &t) const;
+
+ void reverse();
+ void moveStartPoint(Point2 to);
+ void moveEndPoint(Point2 to);
+ void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const;
+
+ void deconverge(int param, double amount);
+
+};
+
+}
diff --git a/thirdparty/msdfgen/core/edge-selectors.cpp b/thirdparty/msdfgen/core/edge-selectors.cpp
new file mode 100644
index 0000000000..aee78847fb
--- /dev/null
+++ b/thirdparty/msdfgen/core/edge-selectors.cpp
@@ -0,0 +1,261 @@
+
+#include "edge-selectors.h"
+
+#include "arithmetics.hpp"
+
+namespace msdfgen {
+
+#define DISTANCE_DELTA_FACTOR 1.001
+
+TrueDistanceSelector::EdgeCache::EdgeCache() : absDistance(0) { }
+
+void TrueDistanceSelector::reset(const Point2 &p) {
+ double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
+ minDistance.distance += nonZeroSign(minDistance.distance)*delta;
+ this->p = p;
+}
+
+void TrueDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
+ double delta = DISTANCE_DELTA_FACTOR*(p-cache.point).length();
+ if (cache.absDistance-delta <= fabs(minDistance.distance)) {
+ double dummy;
+ SignedDistance distance = edge->signedDistance(p, dummy);
+ if (distance < minDistance)
+ minDistance = distance;
+ cache.point = p;
+ cache.absDistance = fabs(distance.distance);
+ }
+}
+
+void TrueDistanceSelector::merge(const TrueDistanceSelector &other) {
+ if (other.minDistance < minDistance)
+ minDistance = other.minDistance;
+}
+
+TrueDistanceSelector::DistanceType TrueDistanceSelector::distance() const {
+ return minDistance.distance;
+}
+
+PseudoDistanceSelectorBase::EdgeCache::EdgeCache() : absDistance(0), aDomainDistance(0), bDomainDistance(0), aPseudoDistance(0), bPseudoDistance(0) { }
+
+bool PseudoDistanceSelectorBase::getPseudoDistance(double &distance, const Vector2 &ep, const Vector2 &edgeDir) {
+ double ts = dotProduct(ep, edgeDir);
+ if (ts > 0) {
+ double pseudoDistance = crossProduct(ep, edgeDir);
+ if (fabs(pseudoDistance) < fabs(distance)) {
+ distance = pseudoDistance;
+ return true;
+ }
+ }
+ return false;
+}
+
+PseudoDistanceSelectorBase::PseudoDistanceSelectorBase() : minNegativePseudoDistance(-fabs(minTrueDistance.distance)), minPositivePseudoDistance(fabs(minTrueDistance.distance)), nearEdge(NULL), nearEdgeParam(0) { }
+
+void PseudoDistanceSelectorBase::reset(double delta) {
+ minTrueDistance.distance += nonZeroSign(minTrueDistance.distance)*delta;
+ minNegativePseudoDistance = -fabs(minTrueDistance.distance);
+ minPositivePseudoDistance = fabs(minTrueDistance.distance);
+ nearEdge = NULL;
+ nearEdgeParam = 0;
+}
+
+bool PseudoDistanceSelectorBase::isEdgeRelevant(const EdgeCache &cache, const EdgeSegment *edge, const Point2 &p) const {
+ double delta = DISTANCE_DELTA_FACTOR*(p-cache.point).length();
+ return (
+ cache.absDistance-delta <= fabs(minTrueDistance.distance) ||
+ fabs(cache.aDomainDistance) < delta ||
+ fabs(cache.bDomainDistance) < delta ||
+ (cache.aDomainDistance > 0 && (cache.aPseudoDistance < 0 ?
+ cache.aPseudoDistance+delta >= minNegativePseudoDistance :
+ cache.aPseudoDistance-delta <= minPositivePseudoDistance
+ )) ||
+ (cache.bDomainDistance > 0 && (cache.bPseudoDistance < 0 ?
+ cache.bPseudoDistance+delta >= minNegativePseudoDistance :
+ cache.bPseudoDistance-delta <= minPositivePseudoDistance
+ ))
+ );
+}
+
+void PseudoDistanceSelectorBase::addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, double param) {
+ if (distance < minTrueDistance) {
+ minTrueDistance = distance;
+ nearEdge = edge;
+ nearEdgeParam = param;
+ }
+}
+
+void PseudoDistanceSelectorBase::addEdgePseudoDistance(double distance) {
+ if (distance <= 0 && distance > minNegativePseudoDistance)
+ minNegativePseudoDistance = distance;
+ if (distance >= 0 && distance < minPositivePseudoDistance)
+ minPositivePseudoDistance = distance;
+}
+
+void PseudoDistanceSelectorBase::merge(const PseudoDistanceSelectorBase &other) {
+ if (other.minTrueDistance < minTrueDistance) {
+ minTrueDistance = other.minTrueDistance;
+ nearEdge = other.nearEdge;
+ nearEdgeParam = other.nearEdgeParam;
+ }
+ if (other.minNegativePseudoDistance > minNegativePseudoDistance)
+ minNegativePseudoDistance = other.minNegativePseudoDistance;
+ if (other.minPositivePseudoDistance < minPositivePseudoDistance)
+ minPositivePseudoDistance = other.minPositivePseudoDistance;
+}
+
+double PseudoDistanceSelectorBase::computeDistance(const Point2 &p) const {
+ double minDistance = minTrueDistance.distance < 0 ? minNegativePseudoDistance : minPositivePseudoDistance;
+ if (nearEdge) {
+ SignedDistance distance = minTrueDistance;
+ nearEdge->distanceToPseudoDistance(distance, p, nearEdgeParam);
+ if (fabs(distance.distance) < fabs(minDistance))
+ minDistance = distance.distance;
+ }
+ return minDistance;
+}
+
+SignedDistance PseudoDistanceSelectorBase::trueDistance() const {
+ return minTrueDistance;
+}
+
+void PseudoDistanceSelector::reset(const Point2 &p) {
+ double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
+ PseudoDistanceSelectorBase::reset(delta);
+ this->p = p;
+}
+
+void PseudoDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
+ if (isEdgeRelevant(cache, edge, p)) {
+ double param;
+ SignedDistance distance = edge->signedDistance(p, param);
+ addEdgeTrueDistance(edge, distance, param);
+ cache.point = p;
+ cache.absDistance = fabs(distance.distance);
+
+ Vector2 ap = p-edge->point(0);
+ Vector2 bp = p-edge->point(1);
+ Vector2 aDir = edge->direction(0).normalize(true);
+ Vector2 bDir = edge->direction(1).normalize(true);
+ Vector2 prevDir = prevEdge->direction(1).normalize(true);
+ Vector2 nextDir = nextEdge->direction(0).normalize(true);
+ double add = dotProduct(ap, (prevDir+aDir).normalize(true));
+ double bdd = -dotProduct(bp, (bDir+nextDir).normalize(true));
+ if (add > 0) {
+ double pd = distance.distance;
+ if (getPseudoDistance(pd, ap, -aDir))
+ addEdgePseudoDistance(pd = -pd);
+ cache.aPseudoDistance = pd;
+ }
+ if (bdd > 0) {
+ double pd = distance.distance;
+ if (getPseudoDistance(pd, bp, bDir))
+ addEdgePseudoDistance(pd);
+ cache.bPseudoDistance = pd;
+ }
+ cache.aDomainDistance = add;
+ cache.bDomainDistance = bdd;
+ }
+}
+
+PseudoDistanceSelector::DistanceType PseudoDistanceSelector::distance() const {
+ return computeDistance(p);
+}
+
+void MultiDistanceSelector::reset(const Point2 &p) {
+ double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
+ r.reset(delta);
+ g.reset(delta);
+ b.reset(delta);
+ this->p = p;
+}
+
+void MultiDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
+ if (
+ (edge->color&RED && r.isEdgeRelevant(cache, edge, p)) ||
+ (edge->color&GREEN && g.isEdgeRelevant(cache, edge, p)) ||
+ (edge->color&BLUE && b.isEdgeRelevant(cache, edge, p))
+ ) {
+ double param;
+ SignedDistance distance = edge->signedDistance(p, param);
+ if (edge->color&RED)
+ r.addEdgeTrueDistance(edge, distance, param);
+ if (edge->color&GREEN)
+ g.addEdgeTrueDistance(edge, distance, param);
+ if (edge->color&BLUE)
+ b.addEdgeTrueDistance(edge, distance, param);
+ cache.point = p;
+ cache.absDistance = fabs(distance.distance);
+
+ Vector2 ap = p-edge->point(0);
+ Vector2 bp = p-edge->point(1);
+ Vector2 aDir = edge->direction(0).normalize(true);
+ Vector2 bDir = edge->direction(1).normalize(true);
+ Vector2 prevDir = prevEdge->direction(1).normalize(true);
+ Vector2 nextDir = nextEdge->direction(0).normalize(true);
+ double add = dotProduct(ap, (prevDir+aDir).normalize(true));
+ double bdd = -dotProduct(bp, (bDir+nextDir).normalize(true));
+ if (add > 0) {
+ double pd = distance.distance;
+ if (PseudoDistanceSelectorBase::getPseudoDistance(pd, ap, -aDir)) {
+ pd = -pd;
+ if (edge->color&RED)
+ r.addEdgePseudoDistance(pd);
+ if (edge->color&GREEN)
+ g.addEdgePseudoDistance(pd);
+ if (edge->color&BLUE)
+ b.addEdgePseudoDistance(pd);
+ }
+ cache.aPseudoDistance = pd;
+ }
+ if (bdd > 0) {
+ double pd = distance.distance;
+ if (PseudoDistanceSelectorBase::getPseudoDistance(pd, bp, bDir)) {
+ if (edge->color&RED)
+ r.addEdgePseudoDistance(pd);
+ if (edge->color&GREEN)
+ g.addEdgePseudoDistance(pd);
+ if (edge->color&BLUE)
+ b.addEdgePseudoDistance(pd);
+ }
+ cache.bPseudoDistance = pd;
+ }
+ cache.aDomainDistance = add;
+ cache.bDomainDistance = bdd;
+ }
+}
+
+void MultiDistanceSelector::merge(const MultiDistanceSelector &other) {
+ r.merge(other.r);
+ g.merge(other.g);
+ b.merge(other.b);
+}
+
+MultiDistanceSelector::DistanceType MultiDistanceSelector::distance() const {
+ MultiDistance multiDistance;
+ multiDistance.r = r.computeDistance(p);
+ multiDistance.g = g.computeDistance(p);
+ multiDistance.b = b.computeDistance(p);
+ return multiDistance;
+}
+
+SignedDistance MultiDistanceSelector::trueDistance() const {
+ SignedDistance distance = r.trueDistance();
+ if (g.trueDistance() < distance)
+ distance = g.trueDistance();
+ if (b.trueDistance() < distance)
+ distance = b.trueDistance();
+ return distance;
+}
+
+MultiAndTrueDistanceSelector::DistanceType MultiAndTrueDistanceSelector::distance() const {
+ MultiDistance multiDistance = MultiDistanceSelector::distance();
+ MultiAndTrueDistance mtd;
+ mtd.r = multiDistance.r;
+ mtd.g = multiDistance.g;
+ mtd.b = multiDistance.b;
+ mtd.a = trueDistance().distance;
+ return mtd;
+}
+
+}
diff --git a/thirdparty/msdfgen/core/edge-selectors.h b/thirdparty/msdfgen/core/edge-selectors.h
new file mode 100644
index 0000000000..3620999f82
--- /dev/null
+++ b/thirdparty/msdfgen/core/edge-selectors.h
@@ -0,0 +1,117 @@
+
+#pragma once
+
+#include "Vector2.h"
+#include "SignedDistance.h"
+#include "edge-segments.h"
+
+namespace msdfgen {
+
+struct MultiDistance {
+ double r, g, b;
+};
+struct MultiAndTrueDistance : MultiDistance {
+ double a;
+};
+
+/// Selects the nearest edge by its true distance.
+class TrueDistanceSelector {
+
+public:
+ typedef double DistanceType;
+
+ struct EdgeCache {
+ Point2 point;
+ double absDistance;
+
+ EdgeCache();
+ };
+
+ void reset(const Point2 &p);
+ void addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
+ void merge(const TrueDistanceSelector &other);
+ DistanceType distance() const;
+
+private:
+ Point2 p;
+ SignedDistance minDistance;
+
+};
+
+class PseudoDistanceSelectorBase {
+
+public:
+ struct EdgeCache {
+ Point2 point;
+ double absDistance;
+ double aDomainDistance, bDomainDistance;
+ double aPseudoDistance, bPseudoDistance;
+
+ EdgeCache();
+ };
+
+ static bool getPseudoDistance(double &distance, const Vector2 &ep, const Vector2 &edgeDir);
+
+ PseudoDistanceSelectorBase();
+ void reset(double delta);
+ bool isEdgeRelevant(const EdgeCache &cache, const EdgeSegment *edge, const Point2 &p) const;
+ void addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, double param);
+ void addEdgePseudoDistance(double distance);
+ void merge(const PseudoDistanceSelectorBase &other);
+ double computeDistance(const Point2 &p) const;
+ SignedDistance trueDistance() const;
+
+private:
+ SignedDistance minTrueDistance;
+ double minNegativePseudoDistance;
+ double minPositivePseudoDistance;
+ const EdgeSegment *nearEdge;
+ double nearEdgeParam;
+
+};
+
+/// Selects the nearest edge by its pseudo-distance.
+class PseudoDistanceSelector : public PseudoDistanceSelectorBase {
+
+public:
+ typedef double DistanceType;
+
+ void reset(const Point2 &p);
+ void addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
+ DistanceType distance() const;
+
+private:
+ Point2 p;
+
+};
+
+/// Selects the nearest edge for each of the three channels by its pseudo-distance.
+class MultiDistanceSelector {
+
+public:
+ typedef MultiDistance DistanceType;
+ typedef PseudoDistanceSelectorBase::EdgeCache EdgeCache;
+
+ void reset(const Point2 &p);
+ void addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
+ void merge(const MultiDistanceSelector &other);
+ DistanceType distance() const;
+ SignedDistance trueDistance() const;
+
+private:
+ Point2 p;
+ PseudoDistanceSelectorBase r, g, b;
+
+};
+
+/// Selects the nearest edge for each of the three color channels by its pseudo-distance and by true distance for the alpha channel.
+class MultiAndTrueDistanceSelector : public MultiDistanceSelector {
+
+public:
+ typedef MultiAndTrueDistance DistanceType;
+
+ DistanceType distance() const;
+
+};
+
+}
diff --git a/thirdparty/msdfgen/core/equation-solver.cpp b/thirdparty/msdfgen/core/equation-solver.cpp
new file mode 100644
index 0000000000..fbe906428b
--- /dev/null
+++ b/thirdparty/msdfgen/core/equation-solver.cpp
@@ -0,0 +1,77 @@
+
+#include "equation-solver.h"
+
+#define _USE_MATH_DEFINES
+#include <cmath>
+
+#define TOO_LARGE_RATIO 1e12
+
+namespace msdfgen {
+
+int solveQuadratic(double x[2], double a, double b, double c) {
+ // a = 0 -> linear equation
+ if (a == 0 || fabs(b)+fabs(c) > TOO_LARGE_RATIO*fabs(a)) {
+ // a, b = 0 -> no solution
+ if (b == 0 || fabs(c) > TOO_LARGE_RATIO*fabs(b)) {
+ if (c == 0)
+ return -1; // 0 = 0
+ return 0;
+ }
+ x[0] = -c/b;
+ return 1;
+ }
+ double dscr = b*b-4*a*c;
+ if (dscr > 0) {
+ dscr = sqrt(dscr);
+ x[0] = (-b+dscr)/(2*a);
+ x[1] = (-b-dscr)/(2*a);
+ return 2;
+ } else if (dscr == 0) {
+ x[0] = -b/(2*a);
+ return 1;
+ } else
+ return 0;
+}
+
+static int solveCubicNormed(double x[3], double a, double b, double c) {
+ double a2 = a*a;
+ double q = (a2 - 3*b)/9;
+ double r = (a*(2*a2-9*b) + 27*c)/54;
+ double r2 = r*r;
+ double q3 = q*q*q;
+ double A, B;
+ if (r2 < q3) {
+ double t = r/sqrt(q3);
+ if (t < -1) t = -1;
+ if (t > 1) t = 1;
+ t = acos(t);
+ a /= 3; q = -2*sqrt(q);
+ x[0] = q*cos(t/3)-a;
+ x[1] = q*cos((t+2*M_PI)/3)-a;
+ x[2] = q*cos((t-2*M_PI)/3)-a;
+ return 3;
+ } else {
+ A = -pow(fabs(r)+sqrt(r2-q3), 1/3.);
+ if (r < 0) A = -A;
+ B = A == 0 ? 0 : q/A;
+ a /= 3;
+ x[0] = (A+B)-a;
+ x[1] = -0.5*(A+B)-a;
+ x[2] = 0.5*sqrt(3.)*(A-B);
+ if (fabs(x[2]) < 1e-14)
+ return 2;
+ return 1;
+ }
+}
+
+int solveCubic(double x[3], double a, double b, double c, double d) {
+ if (a != 0) {
+ double bn = b/a, cn = c/a, dn = d/a;
+ // Check that a isn't "almost zero"
+ if (fabs(bn) < TOO_LARGE_RATIO && fabs(cn) < TOO_LARGE_RATIO && fabs(dn) < TOO_LARGE_RATIO)
+ return solveCubicNormed(x, bn, cn, dn);
+ }
+ return solveQuadratic(x, b, c, d);
+}
+
+}
diff --git a/thirdparty/msdfgen/core/equation-solver.h b/thirdparty/msdfgen/core/equation-solver.h
new file mode 100644
index 0000000000..bae097b2b9
--- /dev/null
+++ b/thirdparty/msdfgen/core/equation-solver.h
@@ -0,0 +1,12 @@
+
+#pragma once
+
+namespace msdfgen {
+
+// ax^2 + bx + c = 0
+int solveQuadratic(double x[2], double a, double b, double c);
+
+// ax^3 + bx^2 + cx + d = 0
+int solveCubic(double x[3], double a, double b, double c, double d);
+
+}
diff --git a/thirdparty/msdfgen/core/generator-config.h b/thirdparty/msdfgen/core/generator-config.h
new file mode 100644
index 0000000000..ddcad961f2
--- /dev/null
+++ b/thirdparty/msdfgen/core/generator-config.h
@@ -0,0 +1,63 @@
+
+#pragma once
+
+#include <cstdlib>
+#include "BitmapRef.hpp"
+
+namespace msdfgen {
+
+/// The configuration of the MSDF error correction pass.
+struct ErrorCorrectionConfig {
+ /// The default value of minDeviationRatio.
+ static const double defaultMinDeviationRatio;
+ /// The default value of minImproveRatio.
+ static const double defaultMinImproveRatio;
+
+ /// Mode of operation.
+ enum Mode {
+ /// Skips error correction pass.
+ DISABLED,
+ /// Corrects all discontinuities of the distance field regardless if edges are adversely affected.
+ INDISCRIMINATE,
+ /// Corrects artifacts at edges and other discontinuous distances only if it does not affect edges or corners.
+ EDGE_PRIORITY,
+ /// Only corrects artifacts at edges.
+ EDGE_ONLY
+ } mode;
+ /// Configuration of whether to use an algorithm that computes the exact shape distance at the positions of suspected artifacts. This algorithm can be much slower.
+ enum DistanceCheckMode {
+ /// Never computes exact shape distance.
+ DO_NOT_CHECK_DISTANCE,
+ /// Only computes exact shape distance at edges. Provides a good balance between speed and precision.
+ CHECK_DISTANCE_AT_EDGE,
+ /// Computes and compares the exact shape distance for each suspected artifact.
+ ALWAYS_CHECK_DISTANCE
+ } distanceCheckMode;
+ /// The minimum ratio between the actual and maximum expected distance delta to be considered an error.
+ double minDeviationRatio;
+ /// The minimum ratio between the pre-correction distance error and the post-correction distance error. Has no effect for DO_NOT_CHECK_DISTANCE.
+ double minImproveRatio;
+ /// An optional buffer to avoid dynamic allocation. Must have at least as many bytes as the MSDF has pixels.
+ byte *buffer;
+
+ inline explicit ErrorCorrectionConfig(Mode mode = EDGE_PRIORITY, DistanceCheckMode distanceCheckMode = CHECK_DISTANCE_AT_EDGE, double minDeviationRatio = defaultMinDeviationRatio, double minImproveRatio = defaultMinImproveRatio, byte *buffer = NULL) : mode(mode), distanceCheckMode(distanceCheckMode), minDeviationRatio(minDeviationRatio), minImproveRatio(minImproveRatio), buffer(buffer) { }
+};
+
+/// The configuration of the distance field generator algorithm.
+struct GeneratorConfig {
+ /// Specifies whether to use the version of the algorithm that supports overlapping contours with the same winding. May be set to false to improve performance when no such contours are present.
+ bool overlapSupport;
+
+ inline explicit GeneratorConfig(bool overlapSupport = true) : overlapSupport(overlapSupport) { }
+};
+
+/// The configuration of the multi-channel distance field generator algorithm.
+struct MSDFGeneratorConfig : GeneratorConfig {
+ /// Configuration of the error correction pass.
+ ErrorCorrectionConfig errorCorrection;
+
+ inline MSDFGeneratorConfig() { }
+ inline explicit MSDFGeneratorConfig(bool overlapSupport, const ErrorCorrectionConfig &errorCorrection = ErrorCorrectionConfig()) : GeneratorConfig(overlapSupport), errorCorrection(errorCorrection) { }
+};
+
+}
diff --git a/thirdparty/msdfgen/core/msdf-error-correction.cpp b/thirdparty/msdfgen/core/msdf-error-correction.cpp
new file mode 100644
index 0000000000..21ddff8c85
--- /dev/null
+++ b/thirdparty/msdfgen/core/msdf-error-correction.cpp
@@ -0,0 +1,154 @@
+
+#include "msdf-error-correction.h"
+
+#include <vector>
+#include "arithmetics.hpp"
+#include "Bitmap.h"
+#include "contour-combiners.h"
+#include "MSDFErrorCorrection.h"
+
+namespace msdfgen {
+
+template <int N>
+static void msdfErrorCorrectionInner(const BitmapRef<float, N> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) {
+ if (config.errorCorrection.mode == ErrorCorrectionConfig::DISABLED)
+ return;
+ Bitmap<byte, 1> stencilBuffer;
+ if (!config.errorCorrection.buffer)
+ stencilBuffer = Bitmap<byte, 1>(sdf.width, sdf.height);
+ BitmapRef<byte, 1> stencil;
+ stencil.pixels = config.errorCorrection.buffer ? config.errorCorrection.buffer : (byte *) stencilBuffer;
+ stencil.width = sdf.width, stencil.height = sdf.height;
+ MSDFErrorCorrection ec(stencil, projection, range);
+ ec.setMinDeviationRatio(config.errorCorrection.minDeviationRatio);
+ ec.setMinImproveRatio(config.errorCorrection.minImproveRatio);
+ switch (config.errorCorrection.mode) {
+ case ErrorCorrectionConfig::DISABLED:
+ case ErrorCorrectionConfig::INDISCRIMINATE:
+ break;
+ case ErrorCorrectionConfig::EDGE_PRIORITY:
+ ec.protectCorners(shape);
+ ec.protectEdges<N>(sdf);
+ break;
+ case ErrorCorrectionConfig::EDGE_ONLY:
+ ec.protectAll();
+ break;
+ }
+ if (config.errorCorrection.distanceCheckMode == ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE || (config.errorCorrection.distanceCheckMode == ErrorCorrectionConfig::CHECK_DISTANCE_AT_EDGE && config.errorCorrection.mode != ErrorCorrectionConfig::EDGE_ONLY)) {
+ ec.findErrors<N>(sdf);
+ if (config.errorCorrection.distanceCheckMode == ErrorCorrectionConfig::CHECK_DISTANCE_AT_EDGE)
+ ec.protectAll();
+ }
+ if (config.errorCorrection.distanceCheckMode == ErrorCorrectionConfig::ALWAYS_CHECK_DISTANCE || config.errorCorrection.distanceCheckMode == ErrorCorrectionConfig::CHECK_DISTANCE_AT_EDGE) {
+ if (config.overlapSupport)
+ ec.findErrors<OverlappingContourCombiner, N>(sdf, shape);
+ else
+ ec.findErrors<SimpleContourCombiner, N>(sdf, shape);
+ }
+ ec.apply(sdf);
+}
+
+template <int N>
+static void msdfErrorCorrectionShapeless(const BitmapRef<float, N> &sdf, const Projection &projection, double range, double minDeviationRatio, bool protectAll) {
+ Bitmap<byte, 1> stencilBuffer(sdf.width, sdf.height);
+ MSDFErrorCorrection ec(stencilBuffer, projection, range);
+ ec.setMinDeviationRatio(minDeviationRatio);
+ if (protectAll)
+ ec.protectAll();
+ ec.findErrors<N>(sdf);
+ ec.apply(sdf);
+}
+
+void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) {
+ msdfErrorCorrectionInner(sdf, shape, projection, range, config);
+}
+void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) {
+ msdfErrorCorrectionInner(sdf, shape, projection, range, config);
+}
+
+void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, double range, double minDeviationRatio) {
+ msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, false);
+}
+void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double minDeviationRatio) {
+ msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, false);
+}
+
+void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, double range, double minDeviationRatio) {
+ msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, true);
+}
+void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double minDeviationRatio) {
+ msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, true);
+}
+
+
+// Legacy version
+
+inline static bool detectClash(const float *a, const float *b, double threshold) {
+ // Sort channels so that pairs (a0, b0), (a1, b1), (a2, b2) go from biggest to smallest absolute difference
+ float a0 = a[0], a1 = a[1], a2 = a[2];
+ float b0 = b[0], b1 = b[1], b2 = b[2];
+ float tmp;
+ if (fabsf(b0-a0) < fabsf(b1-a1)) {
+ tmp = a0, a0 = a1, a1 = tmp;
+ tmp = b0, b0 = b1, b1 = tmp;
+ }
+ if (fabsf(b1-a1) < fabsf(b2-a2)) {
+ tmp = a1, a1 = a2, a2 = tmp;
+ tmp = b1, b1 = b2, b2 = tmp;
+ if (fabsf(b0-a0) < fabsf(b1-a1)) {
+ tmp = a0, a0 = a1, a1 = tmp;
+ tmp = b0, b0 = b1, b1 = tmp;
+ }
+ }
+ return (fabsf(b1-a1) >= threshold) &&
+ !(b0 == b1 && b0 == b2) && // Ignore if other pixel has been equalized
+ fabsf(a2-.5f) >= fabsf(b2-.5f); // Out of the pair, only flag the pixel farther from a shape edge
+}
+
+template <int N>
+static void msdfErrorCorrectionInner_legacy(const BitmapRef<float, N> &output, const Vector2 &threshold) {
+ std::vector<std::pair<int, int> > clashes;
+ int w = output.width, h = output.height;
+ for (int y = 0; y < h; ++y)
+ for (int x = 0; x < w; ++x) {
+ if (
+ (x > 0 && detectClash(output(x, y), output(x-1, y), threshold.x)) ||
+ (x < w-1 && detectClash(output(x, y), output(x+1, y), threshold.x)) ||
+ (y > 0 && detectClash(output(x, y), output(x, y-1), threshold.y)) ||
+ (y < h-1 && detectClash(output(x, y), output(x, y+1), threshold.y))
+ )
+ clashes.push_back(std::make_pair(x, y));
+ }
+ for (std::vector<std::pair<int, int> >::const_iterator clash = clashes.begin(); clash != clashes.end(); ++clash) {
+ float *pixel = output(clash->first, clash->second);
+ float med = median(pixel[0], pixel[1], pixel[2]);
+ pixel[0] = med, pixel[1] = med, pixel[2] = med;
+ }
+#ifndef MSDFGEN_NO_DIAGONAL_CLASH_DETECTION
+ clashes.clear();
+ for (int y = 0; y < h; ++y)
+ for (int x = 0; x < w; ++x) {
+ if (
+ (x > 0 && y > 0 && detectClash(output(x, y), output(x-1, y-1), threshold.x+threshold.y)) ||
+ (x < w-1 && y > 0 && detectClash(output(x, y), output(x+1, y-1), threshold.x+threshold.y)) ||
+ (x > 0 && y < h-1 && detectClash(output(x, y), output(x-1, y+1), threshold.x+threshold.y)) ||
+ (x < w-1 && y < h-1 && detectClash(output(x, y), output(x+1, y+1), threshold.x+threshold.y))
+ )
+ clashes.push_back(std::make_pair(x, y));
+ }
+ for (std::vector<std::pair<int, int> >::const_iterator clash = clashes.begin(); clash != clashes.end(); ++clash) {
+ float *pixel = output(clash->first, clash->second);
+ float med = median(pixel[0], pixel[1], pixel[2]);
+ pixel[0] = med, pixel[1] = med, pixel[2] = med;
+ }
+#endif
+}
+
+void msdfErrorCorrection_legacy(const BitmapRef<float, 3> &output, const Vector2 &threshold) {
+ msdfErrorCorrectionInner_legacy(output, threshold);
+}
+void msdfErrorCorrection_legacy(const BitmapRef<float, 4> &output, const Vector2 &threshold) {
+ msdfErrorCorrectionInner_legacy(output, threshold);
+}
+
+}
diff --git a/thirdparty/msdfgen/core/msdf-error-correction.h b/thirdparty/msdfgen/core/msdf-error-correction.h
new file mode 100644
index 0000000000..d5384c9329
--- /dev/null
+++ b/thirdparty/msdfgen/core/msdf-error-correction.h
@@ -0,0 +1,28 @@
+
+#pragma once
+
+#include "Vector2.h"
+#include "Projection.h"
+#include "Shape.h"
+#include "BitmapRef.hpp"
+#include "generator-config.h"
+
+namespace msdfgen {
+
+/// Predicts potential artifacts caused by the interpolation of the MSDF and corrects them by converting nearby texels to single-channel.
+void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
+void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
+
+/// Applies the simplified error correction to all discontiunous distances (INDISCRIMINATE mode). Does not need shape or translation.
+void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, double range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
+void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
+
+/// Applies the simplified error correction to edges only (EDGE_ONLY mode). Does not need shape or translation.
+void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, double range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
+void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
+
+/// The original version of the error correction algorithm.
+void msdfErrorCorrection_legacy(const BitmapRef<float, 3> &output, const Vector2 &threshold);
+void msdfErrorCorrection_legacy(const BitmapRef<float, 4> &output, const Vector2 &threshold);
+
+}
diff --git a/thirdparty/msdfgen/core/msdfgen.cpp b/thirdparty/msdfgen/core/msdfgen.cpp
new file mode 100644
index 0000000000..0289295f14
--- /dev/null
+++ b/thirdparty/msdfgen/core/msdfgen.cpp
@@ -0,0 +1,288 @@
+
+#include "../msdfgen.h"
+
+#include <vector>
+#include "edge-selectors.h"
+#include "contour-combiners.h"
+#include "ShapeDistanceFinder.h"
+
+namespace msdfgen {
+
+template <typename DistanceType>
+class DistancePixelConversion;
+
+template <>
+class DistancePixelConversion<double> {
+ double invRange;
+public:
+ typedef BitmapRef<float, 1> BitmapRefType;
+ inline explicit DistancePixelConversion(double range) : invRange(1/range) { }
+ inline void operator()(float *pixels, double distance) const {
+ *pixels = float(invRange*distance+.5);
+ }
+};
+
+template <>
+class DistancePixelConversion<MultiDistance> {
+ double invRange;
+public:
+ typedef BitmapRef<float, 3> BitmapRefType;
+ inline explicit DistancePixelConversion(double range) : invRange(1/range) { }
+ inline void operator()(float *pixels, const MultiDistance &distance) const {
+ pixels[0] = float(invRange*distance.r+.5);
+ pixels[1] = float(invRange*distance.g+.5);
+ pixels[2] = float(invRange*distance.b+.5);
+ }
+};
+
+template <>
+class DistancePixelConversion<MultiAndTrueDistance> {
+ double invRange;
+public:
+ typedef BitmapRef<float, 4> BitmapRefType;
+ inline explicit DistancePixelConversion(double range) : invRange(1/range) { }
+ inline void operator()(float *pixels, const MultiAndTrueDistance &distance) const {
+ pixels[0] = float(invRange*distance.r+.5);
+ pixels[1] = float(invRange*distance.g+.5);
+ pixels[2] = float(invRange*distance.b+.5);
+ pixels[3] = float(invRange*distance.a+.5);
+ }
+};
+
+template <class ContourCombiner>
+void generateDistanceField(const typename DistancePixelConversion<typename ContourCombiner::DistanceType>::BitmapRefType &output, const Shape &shape, const Projection &projection, double range) {
+ DistancePixelConversion<typename ContourCombiner::DistanceType> distancePixelConversion(range);
+#ifdef MSDFGEN_USE_OPENMP
+ #pragma omp parallel
+#endif
+ {
+ ShapeDistanceFinder<ContourCombiner> distanceFinder(shape);
+ bool rightToLeft = false;
+#ifdef MSDFGEN_USE_OPENMP
+ #pragma omp for
+#endif
+ for (int y = 0; y < output.height; ++y) {
+ int row = shape.inverseYAxis ? output.height-y-1 : y;
+ for (int col = 0; col < output.width; ++col) {
+ int x = rightToLeft ? output.width-col-1 : col;
+ Point2 p = projection.unproject(Point2(x+.5, y+.5));
+ typename ContourCombiner::DistanceType distance = distanceFinder.distance(p);
+ distancePixelConversion(output(x, row), distance);
+ }
+ rightToLeft = !rightToLeft;
+ }
+ }
+}
+
+void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config) {
+ if (config.overlapSupport)
+ generateDistanceField<OverlappingContourCombiner<TrueDistanceSelector> >(output, shape, projection, range);
+ else
+ generateDistanceField<SimpleContourCombiner<TrueDistanceSelector> >(output, shape, projection, range);
+}
+
+void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config) {
+ if (config.overlapSupport)
+ generateDistanceField<OverlappingContourCombiner<PseudoDistanceSelector> >(output, shape, projection, range);
+ else
+ generateDistanceField<SimpleContourCombiner<PseudoDistanceSelector> >(output, shape, projection, range);
+}
+
+void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) {
+ if (config.overlapSupport)
+ generateDistanceField<OverlappingContourCombiner<MultiDistanceSelector> >(output, shape, projection, range);
+ else
+ generateDistanceField<SimpleContourCombiner<MultiDistanceSelector> >(output, shape, projection, range);
+ msdfErrorCorrection(output, shape, projection, range, config);
+}
+
+void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) {
+ if (config.overlapSupport)
+ generateDistanceField<OverlappingContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, projection, range);
+ else
+ generateDistanceField<SimpleContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, projection, range);
+ msdfErrorCorrection(output, shape, projection, range, config);
+}
+
+// Legacy API
+
+void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
+ generateSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport));
+}
+
+void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
+ generatePseudoSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport));
+}
+
+void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig, bool overlapSupport) {
+ generateMSDF(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(overlapSupport, errorCorrectionConfig));
+}
+
+void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig, bool overlapSupport) {
+ generateMTSDF(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(overlapSupport, errorCorrectionConfig));
+}
+
+// Legacy version
+
+void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
+#ifdef MSDFGEN_USE_OPENMP
+ #pragma omp parallel for
+#endif
+ for (int y = 0; y < output.height; ++y) {
+ int row = shape.inverseYAxis ? output.height-y-1 : y;
+ for (int x = 0; x < output.width; ++x) {
+ double dummy;
+ Point2 p = Vector2(x+.5, y+.5)/scale-translate;
+ SignedDistance minDistance;
+ for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
+ for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
+ SignedDistance distance = (*edge)->signedDistance(p, dummy);
+ if (distance < minDistance)
+ minDistance = distance;
+ }
+ *output(x, row) = float(minDistance.distance/range+.5);
+ }
+ }
+}
+
+void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
+#ifdef MSDFGEN_USE_OPENMP
+ #pragma omp parallel for
+#endif
+ for (int y = 0; y < output.height; ++y) {
+ int row = shape.inverseYAxis ? output.height-y-1 : y;
+ for (int x = 0; x < output.width; ++x) {
+ Point2 p = Vector2(x+.5, y+.5)/scale-translate;
+ SignedDistance minDistance;
+ const EdgeHolder *nearEdge = NULL;
+ double nearParam = 0;
+ for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
+ for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
+ double param;
+ SignedDistance distance = (*edge)->signedDistance(p, param);
+ if (distance < minDistance) {
+ minDistance = distance;
+ nearEdge = &*edge;
+ nearParam = param;
+ }
+ }
+ if (nearEdge)
+ (*nearEdge)->distanceToPseudoDistance(minDistance, p, nearParam);
+ *output(x, row) = float(minDistance.distance/range+.5);
+ }
+ }
+}
+
+void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig) {
+#ifdef MSDFGEN_USE_OPENMP
+ #pragma omp parallel for
+#endif
+ for (int y = 0; y < output.height; ++y) {
+ int row = shape.inverseYAxis ? output.height-y-1 : y;
+ for (int x = 0; x < output.width; ++x) {
+ Point2 p = Vector2(x+.5, y+.5)/scale-translate;
+
+ struct {
+ SignedDistance minDistance;
+ const EdgeHolder *nearEdge;
+ double nearParam;
+ } r, g, b;
+ r.nearEdge = g.nearEdge = b.nearEdge = NULL;
+ r.nearParam = g.nearParam = b.nearParam = 0;
+
+ for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
+ for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
+ double param;
+ SignedDistance distance = (*edge)->signedDistance(p, param);
+ if ((*edge)->color&RED && distance < r.minDistance) {
+ r.minDistance = distance;
+ r.nearEdge = &*edge;
+ r.nearParam = param;
+ }
+ if ((*edge)->color&GREEN && distance < g.minDistance) {
+ g.minDistance = distance;
+ g.nearEdge = &*edge;
+ g.nearParam = param;
+ }
+ if ((*edge)->color&BLUE && distance < b.minDistance) {
+ b.minDistance = distance;
+ b.nearEdge = &*edge;
+ b.nearParam = param;
+ }
+ }
+
+ if (r.nearEdge)
+ (*r.nearEdge)->distanceToPseudoDistance(r.minDistance, p, r.nearParam);
+ if (g.nearEdge)
+ (*g.nearEdge)->distanceToPseudoDistance(g.minDistance, p, g.nearParam);
+ if (b.nearEdge)
+ (*b.nearEdge)->distanceToPseudoDistance(b.minDistance, p, b.nearParam);
+ output(x, row)[0] = float(r.minDistance.distance/range+.5);
+ output(x, row)[1] = float(g.minDistance.distance/range+.5);
+ output(x, row)[2] = float(b.minDistance.distance/range+.5);
+ }
+ }
+
+ errorCorrectionConfig.distanceCheckMode = ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE;
+ msdfErrorCorrection(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(false, errorCorrectionConfig));
+}
+
+void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig) {
+#ifdef MSDFGEN_USE_OPENMP
+ #pragma omp parallel for
+#endif
+ for (int y = 0; y < output.height; ++y) {
+ int row = shape.inverseYAxis ? output.height-y-1 : y;
+ for (int x = 0; x < output.width; ++x) {
+ Point2 p = Vector2(x+.5, y+.5)/scale-translate;
+
+ SignedDistance minDistance;
+ struct {
+ SignedDistance minDistance;
+ const EdgeHolder *nearEdge;
+ double nearParam;
+ } r, g, b;
+ r.nearEdge = g.nearEdge = b.nearEdge = NULL;
+ r.nearParam = g.nearParam = b.nearParam = 0;
+
+ for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
+ for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
+ double param;
+ SignedDistance distance = (*edge)->signedDistance(p, param);
+ if (distance < minDistance)
+ minDistance = distance;
+ if ((*edge)->color&RED && distance < r.minDistance) {
+ r.minDistance = distance;
+ r.nearEdge = &*edge;
+ r.nearParam = param;
+ }
+ if ((*edge)->color&GREEN && distance < g.minDistance) {
+ g.minDistance = distance;
+ g.nearEdge = &*edge;
+ g.nearParam = param;
+ }
+ if ((*edge)->color&BLUE && distance < b.minDistance) {
+ b.minDistance = distance;
+ b.nearEdge = &*edge;
+ b.nearParam = param;
+ }
+ }
+
+ if (r.nearEdge)
+ (*r.nearEdge)->distanceToPseudoDistance(r.minDistance, p, r.nearParam);
+ if (g.nearEdge)
+ (*g.nearEdge)->distanceToPseudoDistance(g.minDistance, p, g.nearParam);
+ if (b.nearEdge)
+ (*b.nearEdge)->distanceToPseudoDistance(b.minDistance, p, b.nearParam);
+ output(x, row)[0] = float(r.minDistance.distance/range+.5);
+ output(x, row)[1] = float(g.minDistance.distance/range+.5);
+ output(x, row)[2] = float(b.minDistance.distance/range+.5);
+ output(x, row)[3] = float(minDistance.distance/range+.5);
+ }
+ }
+
+ errorCorrectionConfig.distanceCheckMode = ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE;
+ msdfErrorCorrection(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(false, errorCorrectionConfig));
+}
+
+}
diff --git a/thirdparty/msdfgen/core/pixel-conversion.hpp b/thirdparty/msdfgen/core/pixel-conversion.hpp
new file mode 100644
index 0000000000..7e9b6d08f0
--- /dev/null
+++ b/thirdparty/msdfgen/core/pixel-conversion.hpp
@@ -0,0 +1,18 @@
+
+#pragma once
+
+#include "arithmetics.hpp"
+
+namespace msdfgen {
+
+typedef unsigned char byte;
+
+inline byte pixelFloatToByte(float x) {
+ return byte(clamp(256.f*x, 255.f));
+}
+
+inline float pixelByteToFloat(byte x) {
+ return 1.f/255.f*float(x);
+}
+
+}
diff --git a/thirdparty/msdfgen/core/rasterization.cpp b/thirdparty/msdfgen/core/rasterization.cpp
new file mode 100644
index 0000000000..9aa695a8c1
--- /dev/null
+++ b/thirdparty/msdfgen/core/rasterization.cpp
@@ -0,0 +1,115 @@
+
+#include "rasterization.h"
+
+#include <vector>
+#include "arithmetics.hpp"
+
+namespace msdfgen {
+
+void rasterize(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, FillRule fillRule) {
+ Scanline scanline;
+ for (int y = 0; y < output.height; ++y) {
+ int row = shape.inverseYAxis ? output.height-y-1 : y;
+ shape.scanline(scanline, projection.unprojectY(y+.5));
+ for (int x = 0; x < output.width; ++x)
+ *output(x, row) = (float) scanline.filled(projection.unprojectX(x+.5), fillRule);
+ }
+}
+
+void distanceSignCorrection(const BitmapRef<float, 1> &sdf, const Shape &shape, const Projection &projection, FillRule fillRule) {
+ Scanline scanline;
+ for (int y = 0; y < sdf.height; ++y) {
+ int row = shape.inverseYAxis ? sdf.height-y-1 : y;
+ shape.scanline(scanline, projection.unprojectY(y+.5));
+ for (int x = 0; x < sdf.width; ++x) {
+ bool fill = scanline.filled(projection.unprojectX(x+.5), fillRule);
+ float &sd = *sdf(x, row);
+ if ((sd > .5f) != fill)
+ sd = 1.f-sd;
+ }
+ }
+}
+
+template <int N>
+static void multiDistanceSignCorrection(const BitmapRef<float, N> &sdf, const Shape &shape, const Projection &projection, FillRule fillRule) {
+ int w = sdf.width, h = sdf.height;
+ if (!(w*h))
+ return;
+ Scanline scanline;
+ bool ambiguous = false;
+ std::vector<char> matchMap;
+ matchMap.resize(w*h);
+ char *match = &matchMap[0];
+ for (int y = 0; y < h; ++y) {
+ int row = shape.inverseYAxis ? h-y-1 : y;
+ shape.scanline(scanline, projection.unprojectY(y+.5));
+ for (int x = 0; x < w; ++x) {
+ bool fill = scanline.filled(projection.unprojectX(x+.5), fillRule);
+ float *msd = sdf(x, row);
+ float sd = median(msd[0], msd[1], msd[2]);
+ if (sd == .5f)
+ ambiguous = true;
+ else if ((sd > .5f) != fill) {
+ msd[0] = 1.f-msd[0];
+ msd[1] = 1.f-msd[1];
+ msd[2] = 1.f-msd[2];
+ *match = -1;
+ } else
+ *match = 1;
+ if (N >= 4 && (msd[3] > .5f) != fill)
+ msd[3] = 1.f-msd[3];
+ ++match;
+ }
+ }
+ // This step is necessary to avoid artifacts when whole shape is inverted
+ if (ambiguous) {
+ match = &matchMap[0];
+ for (int y = 0; y < h; ++y) {
+ int row = shape.inverseYAxis ? h-y-1 : y;
+ for (int x = 0; x < w; ++x) {
+ if (!*match) {
+ int neighborMatch = 0;
+ if (x > 0) neighborMatch += *(match-1);
+ if (x < w-1) neighborMatch += *(match+1);
+ if (y > 0) neighborMatch += *(match-w);
+ if (y < h-1) neighborMatch += *(match+w);
+ if (neighborMatch < 0) {
+ float *msd = sdf(x, row);
+ msd[0] = 1.f-msd[0];
+ msd[1] = 1.f-msd[1];
+ msd[2] = 1.f-msd[2];
+ }
+ }
+ ++match;
+ }
+ }
+ }
+}
+
+void distanceSignCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, FillRule fillRule) {
+ multiDistanceSignCorrection(sdf, shape, projection, fillRule);
+}
+
+void distanceSignCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, FillRule fillRule) {
+ multiDistanceSignCorrection(sdf, shape, projection, fillRule);
+}
+
+// Legacy API
+
+void rasterize(const BitmapRef<float, 1> &output, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
+ rasterize(output, shape, Projection(scale, translate), fillRule);
+}
+
+void distanceSignCorrection(const BitmapRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
+ distanceSignCorrection(sdf, shape, Projection(scale, translate), fillRule);
+}
+
+void distanceSignCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
+ distanceSignCorrection(sdf, shape, Projection(scale, translate), fillRule);
+}
+
+void distanceSignCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
+ distanceSignCorrection(sdf, shape, Projection(scale, translate), fillRule);
+}
+
+}
diff --git a/thirdparty/msdfgen/core/rasterization.h b/thirdparty/msdfgen/core/rasterization.h
new file mode 100644
index 0000000000..82d0c73d95
--- /dev/null
+++ b/thirdparty/msdfgen/core/rasterization.h
@@ -0,0 +1,25 @@
+
+#pragma once
+
+#include "Vector2.h"
+#include "Shape.h"
+#include "Projection.h"
+#include "Scanline.h"
+#include "BitmapRef.hpp"
+
+namespace msdfgen {
+
+/// Rasterizes the shape into a monochrome bitmap.
+void rasterize(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, FillRule fillRule = FILL_NONZERO);
+/// Fixes the sign of the input signed distance field, so that it matches the shape's rasterized fill.
+void distanceSignCorrection(const BitmapRef<float, 1> &sdf, const Shape &shape, const Projection &projection, FillRule fillRule = FILL_NONZERO);
+void distanceSignCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, FillRule fillRule = FILL_NONZERO);
+void distanceSignCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, FillRule fillRule = FILL_NONZERO);
+
+// Old version of the function API's kept for backwards compatibility
+void rasterize(const BitmapRef<float, 1> &output, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
+void distanceSignCorrection(const BitmapRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
+void distanceSignCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
+void distanceSignCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
+
+}
diff --git a/thirdparty/msdfgen/core/render-sdf.cpp b/thirdparty/msdfgen/core/render-sdf.cpp
new file mode 100644
index 0000000000..e282285e59
--- /dev/null
+++ b/thirdparty/msdfgen/core/render-sdf.cpp
@@ -0,0 +1,108 @@
+
+#include "render-sdf.h"
+
+#include "arithmetics.hpp"
+#include "pixel-conversion.hpp"
+#include "bitmap-interpolation.hpp"
+
+namespace msdfgen {
+
+static float distVal(float dist, double pxRange, float midValue) {
+ if (!pxRange)
+ return (float) (dist > midValue);
+ return (float) clamp((dist-midValue)*pxRange+.5);
+}
+
+void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 1> &sdf, double pxRange, float midValue) {
+ Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
+ pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
+ for (int y = 0; y < output.height; ++y)
+ for (int x = 0; x < output.width; ++x) {
+ float sd;
+ interpolate(&sd, sdf, scale*Point2(x+.5, y+.5));
+ *output(x, y) = distVal(sd, pxRange, midValue);
+ }
+}
+
+void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1> &sdf, double pxRange, float midValue) {
+ Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
+ pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
+ for (int y = 0; y < output.height; ++y)
+ for (int x = 0; x < output.width; ++x) {
+ float sd;
+ interpolate(&sd, sdf, scale*Point2(x+.5, y+.5));
+ float v = distVal(sd, pxRange, midValue);
+ output(x, y)[0] = v;
+ output(x, y)[1] = v;
+ output(x, y)[2] = v;
+ }
+}
+
+void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3> &sdf, double pxRange, float midValue) {
+ Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
+ pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
+ for (int y = 0; y < output.height; ++y)
+ for (int x = 0; x < output.width; ++x) {
+ float sd[3];
+ interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
+ *output(x, y) = distVal(median(sd[0], sd[1], sd[2]), pxRange, midValue);
+ }
+}
+
+void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3> &sdf, double pxRange, float midValue) {
+ Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
+ pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
+ for (int y = 0; y < output.height; ++y)
+ for (int x = 0; x < output.width; ++x) {
+ float sd[3];
+ interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
+ output(x, y)[0] = distVal(sd[0], pxRange, midValue);
+ output(x, y)[1] = distVal(sd[1], pxRange, midValue);
+ output(x, y)[2] = distVal(sd[2], pxRange, midValue);
+ }
+}
+
+void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 4> &sdf, double pxRange, float midValue) {
+ Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
+ pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
+ for (int y = 0; y < output.height; ++y)
+ for (int x = 0; x < output.width; ++x) {
+ float sd[4];
+ interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
+ *output(x, y) = distVal(median(sd[0], sd[1], sd[2]), pxRange, midValue);
+ }
+}
+
+void renderSDF(const BitmapRef<float, 4> &output, const BitmapConstRef<float, 4> &sdf, double pxRange, float midValue) {
+ Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
+ pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
+ for (int y = 0; y < output.height; ++y)
+ for (int x = 0; x < output.width; ++x) {
+ float sd[4];
+ interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
+ output(x, y)[0] = distVal(sd[0], pxRange, midValue);
+ output(x, y)[1] = distVal(sd[1], pxRange, midValue);
+ output(x, y)[2] = distVal(sd[2], pxRange, midValue);
+ output(x, y)[3] = distVal(sd[3], pxRange, midValue);
+ }
+}
+
+void simulate8bit(const BitmapRef<float, 1> &bitmap) {
+ const float *end = bitmap.pixels+1*bitmap.width*bitmap.height;
+ for (float *p = bitmap.pixels; p < end; ++p)
+ *p = pixelByteToFloat(pixelFloatToByte(*p));
+}
+
+void simulate8bit(const BitmapRef<float, 3> &bitmap) {
+ const float *end = bitmap.pixels+3*bitmap.width*bitmap.height;
+ for (float *p = bitmap.pixels; p < end; ++p)
+ *p = pixelByteToFloat(pixelFloatToByte(*p));
+}
+
+void simulate8bit(const BitmapRef<float, 4> &bitmap) {
+ const float *end = bitmap.pixels+4*bitmap.width*bitmap.height;
+ for (float *p = bitmap.pixels; p < end; ++p)
+ *p = pixelByteToFloat(pixelFloatToByte(*p));
+}
+
+}
diff --git a/thirdparty/msdfgen/core/render-sdf.h b/thirdparty/msdfgen/core/render-sdf.h
new file mode 100644
index 0000000000..7f2d270b67
--- /dev/null
+++ b/thirdparty/msdfgen/core/render-sdf.h
@@ -0,0 +1,22 @@
+
+#pragma once
+
+#include "Vector2.h"
+#include "BitmapRef.hpp"
+
+namespace msdfgen {
+
+/// Reconstructs the shape's appearance into output from the distance field sdf.
+void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 1> &sdf, double pxRange = 0, float midValue = .5f);
+void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1> &sdf, double pxRange = 0, float midValue = .5f);
+void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3> &sdf, double pxRange = 0, float midValue = .5f);
+void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3> &sdf, double pxRange = 0, float midValue = .5f);
+void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 4> &sdf, double pxRange = 0, float midValue = .5f);
+void renderSDF(const BitmapRef<float, 4> &output, const BitmapConstRef<float, 4> &sdf, double pxRange = 0, float midValue = .5f);
+
+/// Snaps the values of the floating-point bitmaps into one of the 256 values representable in a standard 8-bit bitmap.
+void simulate8bit(const BitmapRef<float, 1> &bitmap);
+void simulate8bit(const BitmapRef<float, 3> &bitmap);
+void simulate8bit(const BitmapRef<float, 4> &bitmap);
+
+}
diff --git a/thirdparty/msdfgen/core/save-bmp.cpp b/thirdparty/msdfgen/core/save-bmp.cpp
new file mode 100644
index 0000000000..f761ee4f88
--- /dev/null
+++ b/thirdparty/msdfgen/core/save-bmp.cpp
@@ -0,0 +1,169 @@
+
+#define _CRT_SECURE_NO_WARNINGS
+
+#include "save-bmp.h"
+
+#include <cstdio>
+
+#ifdef MSDFGEN_USE_CPP11
+ #include <cstdint>
+#else
+ typedef int int32_t;
+ typedef unsigned uint32_t;
+ typedef unsigned short uint16_t;
+ typedef unsigned char uint8_t;
+#endif
+
+#include "pixel-conversion.hpp"
+
+namespace msdfgen {
+
+template <typename T>
+static bool writeValue(FILE *file, T value) {
+ #ifdef __BIG_ENDIAN__
+ T reverse = 0;
+ for (int i = 0; i < sizeof(T); ++i) {
+ reverse <<= 8;
+ reverse |= value&T(0xff);
+ value >>= 8;
+ }
+ return fwrite(&reverse, sizeof(T), 1, file) == 1;
+ #else
+ return fwrite(&value, sizeof(T), 1, file) == 1;
+ #endif
+}
+
+static bool writeBmpHeader(FILE *file, int width, int height, int &paddedWidth) {
+ paddedWidth = (3*width+3)&~3;
+ const uint32_t bitmapStart = 54;
+ const uint32_t bitmapSize = paddedWidth*height;
+ const uint32_t fileSize = bitmapStart+bitmapSize;
+
+ writeValue<uint16_t>(file, 0x4d42u);
+ writeValue<uint32_t>(file, fileSize);
+ writeValue<uint16_t>(file, 0);
+ writeValue<uint16_t>(file, 0);
+ writeValue<uint32_t>(file, bitmapStart);
+
+ writeValue<uint32_t>(file, 40);
+ writeValue<int32_t>(file, width);
+ writeValue<int32_t>(file, height);
+ writeValue<uint16_t>(file, 1);
+ writeValue<uint16_t>(file, 24);
+ writeValue<uint32_t>(file, 0);
+ writeValue<uint32_t>(file, bitmapSize);
+ writeValue<uint32_t>(file, 2835);
+ writeValue<uint32_t>(file, 2835);
+ writeValue<uint32_t>(file, 0);
+ writeValue<uint32_t>(file, 0);
+
+ return true;
+}
+
+bool saveBmp(const BitmapConstRef<byte, 1> &bitmap, const char *filename) {
+ FILE *file = fopen(filename, "wb");
+ if (!file)
+ return false;
+
+ int paddedWidth;
+ writeBmpHeader(file, bitmap.width, bitmap.height, paddedWidth);
+
+ const uint8_t padding[4] = { };
+ int padLength = paddedWidth-3*bitmap.width;
+ for (int y = 0; y < bitmap.height; ++y) {
+ for (int x = 0; x < bitmap.width; ++x) {
+ uint8_t px = (uint8_t) *bitmap(x, y);
+ fwrite(&px, sizeof(uint8_t), 1, file);
+ fwrite(&px, sizeof(uint8_t), 1, file);
+ fwrite(&px, sizeof(uint8_t), 1, file);
+ }
+ fwrite(padding, 1, padLength, file);
+ }
+
+ return !fclose(file);
+}
+
+bool saveBmp(const BitmapConstRef<byte, 3> &bitmap, const char *filename) {
+ FILE *file = fopen(filename, "wb");
+ if (!file)
+ return false;
+
+ int paddedWidth;
+ writeBmpHeader(file, bitmap.width, bitmap.height, paddedWidth);
+
+ const uint8_t padding[4] = { };
+ int padLength = paddedWidth-3*bitmap.width;
+ for (int y = 0; y < bitmap.height; ++y) {
+ for (int x = 0; x < bitmap.width; ++x) {
+ uint8_t bgr[3] = {
+ (uint8_t) bitmap(x, y)[2],
+ (uint8_t) bitmap(x, y)[1],
+ (uint8_t) bitmap(x, y)[0]
+ };
+ fwrite(bgr, sizeof(uint8_t), 3, file);
+ }
+ fwrite(padding, 1, padLength, file);
+ }
+
+ return !fclose(file);
+}
+
+bool saveBmp(const BitmapConstRef<byte, 4> &bitmap, const char *filename) {
+ // RGBA not supported by the BMP format
+ return false;
+}
+
+bool saveBmp(const BitmapConstRef<float, 1> &bitmap, const char *filename) {
+ FILE *file = fopen(filename, "wb");
+ if (!file)
+ return false;
+
+ int paddedWidth;
+ writeBmpHeader(file, bitmap.width, bitmap.height, paddedWidth);
+
+ const uint8_t padding[4] = { };
+ int padLength = paddedWidth-3*bitmap.width;
+ for (int y = 0; y < bitmap.height; ++y) {
+ for (int x = 0; x < bitmap.width; ++x) {
+ uint8_t px = (uint8_t) pixelFloatToByte(*bitmap(x, y));
+ fwrite(&px, sizeof(uint8_t), 1, file);
+ fwrite(&px, sizeof(uint8_t), 1, file);
+ fwrite(&px, sizeof(uint8_t), 1, file);
+ }
+ fwrite(padding, 1, padLength, file);
+ }
+
+ return !fclose(file);
+}
+
+bool saveBmp(const BitmapConstRef<float, 3> &bitmap, const char *filename) {
+ FILE *file = fopen(filename, "wb");
+ if (!file)
+ return false;
+
+ int paddedWidth;
+ writeBmpHeader(file, bitmap.width, bitmap.height, paddedWidth);
+
+ const uint8_t padding[4] = { };
+ int padLength = paddedWidth-3*bitmap.width;
+ for (int y = 0; y < bitmap.height; ++y) {
+ for (int x = 0; x < bitmap.width; ++x) {
+ uint8_t bgr[3] = {
+ (uint8_t) pixelFloatToByte(bitmap(x, y)[2]),
+ (uint8_t) pixelFloatToByte(bitmap(x, y)[1]),
+ (uint8_t) pixelFloatToByte(bitmap(x, y)[0])
+ };
+ fwrite(bgr, sizeof(uint8_t), 3, file);
+ }
+ fwrite(padding, 1, padLength, file);
+ }
+
+ return !fclose(file);
+}
+
+bool saveBmp(const BitmapConstRef<float, 4> &bitmap, const char *filename) {
+ // RGBA not supported by the BMP format
+ return false;
+}
+
+}
diff --git a/thirdparty/msdfgen/core/save-bmp.h b/thirdparty/msdfgen/core/save-bmp.h
new file mode 100644
index 0000000000..98f852921f
--- /dev/null
+++ b/thirdparty/msdfgen/core/save-bmp.h
@@ -0,0 +1,16 @@
+
+#pragma once
+
+#include "BitmapRef.hpp"
+
+namespace msdfgen {
+
+/// Saves the bitmap as a BMP file.
+bool saveBmp(const BitmapConstRef<byte, 1> &bitmap, const char *filename);
+bool saveBmp(const BitmapConstRef<byte, 3> &bitmap, const char *filename);
+bool saveBmp(const BitmapConstRef<byte, 4> &bitmap, const char *filename);
+bool saveBmp(const BitmapConstRef<float, 1> &bitmap, const char *filename);
+bool saveBmp(const BitmapConstRef<float, 3> &bitmap, const char *filename);
+bool saveBmp(const BitmapConstRef<float, 4> &bitmap, const char *filename);
+
+}
diff --git a/thirdparty/msdfgen/core/save-tiff.cpp b/thirdparty/msdfgen/core/save-tiff.cpp
new file mode 100644
index 0000000000..71405e00e5
--- /dev/null
+++ b/thirdparty/msdfgen/core/save-tiff.cpp
@@ -0,0 +1,190 @@
+
+#define _CRT_SECURE_NO_WARNINGS
+
+#include "save-tiff.h"
+
+#include <cstdio>
+
+#ifdef MSDFGEN_USE_CPP11
+ #include <cstdint>
+#else
+ typedef int int32_t;
+ typedef unsigned uint32_t;
+ typedef unsigned short uint16_t;
+ typedef unsigned char uint8_t;
+#endif
+
+namespace msdfgen {
+
+template <typename T>
+static bool writeValue(FILE *file, T value) {
+ return fwrite(&value, sizeof(T), 1, file) == 1;
+}
+template <typename T>
+static void writeValueRepeated(FILE *file, T value, int times) {
+ for (int i = 0; i < times; ++i)
+ writeValue(file, value);
+}
+
+static bool writeTiffHeader(FILE *file, int width, int height, int channels) {
+ #ifdef __BIG_ENDIAN__
+ writeValue<uint16_t>(file, 0x4d4du);
+ #else
+ writeValue<uint16_t>(file, 0x4949u);
+ #endif
+ writeValue<uint16_t>(file, 42);
+ writeValue<uint32_t>(file, 0x0008u); // Offset of first IFD
+ // Offset = 0x0008
+
+ writeValue<uint16_t>(file, 15); // Number of IFD entries
+
+ // ImageWidth
+ writeValue<uint16_t>(file, 0x0100u);
+ writeValue<uint16_t>(file, 0x0004u);
+ writeValue<uint32_t>(file, 1);
+ writeValue<int32_t>(file, width);
+ // ImageLength
+ writeValue<uint16_t>(file, 0x0101u);
+ writeValue<uint16_t>(file, 0x0004u);
+ writeValue<uint32_t>(file, 1);
+ writeValue<int32_t>(file, height);
+ // BitsPerSample
+ writeValue<uint16_t>(file, 0x0102u);
+ writeValue<uint16_t>(file, 0x0003u);
+ writeValue<uint32_t>(file, channels);
+ if (channels > 1)
+ writeValue<uint32_t>(file, 0x00c2u); // Offset of 32, 32, ...
+ else {
+ writeValue<uint16_t>(file, 32);
+ writeValue<uint16_t>(file, 0);
+ }
+ // Compression
+ writeValue<uint16_t>(file, 0x0103u);
+ writeValue<uint16_t>(file, 0x0003u);
+ writeValue<uint32_t>(file, 1);
+ writeValue<uint16_t>(file, 1);
+ writeValue<uint16_t>(file, 0);
+ // PhotometricInterpretation
+ writeValue<uint16_t>(file, 0x0106u);
+ writeValue<uint16_t>(file, 0x0003u);
+ writeValue<uint32_t>(file, 1);
+ writeValue<uint16_t>(file, channels >= 3 ? 2 : 1);
+ writeValue<uint16_t>(file, 0);
+ // StripOffsets
+ writeValue<uint16_t>(file, 0x0111u);
+ writeValue<uint16_t>(file, 0x0004u);
+ writeValue<uint32_t>(file, 1);
+ writeValue<uint32_t>(file, 0x00d2u+(channels > 1)*channels*12); // Offset of pixel data
+ // SamplesPerPixel
+ writeValue<uint16_t>(file, 0x0115u);
+ writeValue<uint16_t>(file, 0x0003u);
+ writeValue<uint32_t>(file, 1);
+ writeValue<uint16_t>(file, channels);
+ writeValue<uint16_t>(file, 0);
+ // RowsPerStrip
+ writeValue<uint16_t>(file, 0x0116u);
+ writeValue<uint16_t>(file, 0x0004u);
+ writeValue<uint32_t>(file, 1);
+ writeValue<int32_t>(file, height);
+ // StripByteCounts
+ writeValue<uint16_t>(file, 0x0117u);
+ writeValue<uint16_t>(file, 0x0004u);
+ writeValue<uint32_t>(file, 1);
+ writeValue<int32_t>(file, sizeof(float)*channels*width*height);
+ // XResolution
+ writeValue<uint16_t>(file, 0x011au);
+ writeValue<uint16_t>(file, 0x0005u);
+ writeValue<uint32_t>(file, 1);
+ writeValue<uint32_t>(file, 0x00c2u+(channels > 1)*channels*2); // Offset of 300, 1
+ // YResolution
+ writeValue<uint16_t>(file, 0x011bu);
+ writeValue<uint16_t>(file, 0x0005u);
+ writeValue<uint32_t>(file, 1);
+ writeValue<uint32_t>(file, 0x00cau+(channels > 1)*channels*2); // Offset of 300, 1
+ // ResolutionUnit
+ writeValue<uint16_t>(file, 0x0128u);
+ writeValue<uint16_t>(file, 0x0003u);
+ writeValue<uint32_t>(file, 1);
+ writeValue<uint16_t>(file, 2);
+ writeValue<uint16_t>(file, 0);
+ // SampleFormat
+ writeValue<uint16_t>(file, 0x0153u);
+ writeValue<uint16_t>(file, 0x0003u);
+ writeValue<uint32_t>(file, channels);
+ if (channels > 1)
+ writeValue<uint32_t>(file, 0x00d2u+channels*2); // Offset of 3, 3, ...
+ else {
+ writeValue<uint16_t>(file, 3);
+ writeValue<uint16_t>(file, 0);
+ }
+ // SMinSampleValue
+ writeValue<uint16_t>(file, 0x0154u);
+ writeValue<uint16_t>(file, 0x000bu);
+ writeValue<uint32_t>(file, channels);
+ if (channels > 1)
+ writeValue<uint32_t>(file, 0x00d2u+channels*4); // Offset of 0.f, 0.f, ...
+ else
+ writeValue<float>(file, 0.f);
+ // SMaxSampleValue
+ writeValue<uint16_t>(file, 0x0155u);
+ writeValue<uint16_t>(file, 0x000bu);
+ writeValue<uint32_t>(file, channels);
+ if (channels > 1)
+ writeValue<uint32_t>(file, 0x00d2u+channels*8); // Offset of 1.f, 1.f, ...
+ else
+ writeValue<float>(file, 1.f);
+ // Offset = 0x00be
+
+ writeValue<uint32_t>(file, 0);
+
+ if (channels > 1) {
+ // 0x00c2 BitsPerSample data
+ writeValueRepeated<uint16_t>(file, 32, channels);
+ // 0x00c2 + 2*N XResolution data
+ writeValue<uint32_t>(file, 300);
+ writeValue<uint32_t>(file, 1);
+ // 0x00ca + 2*N YResolution data
+ writeValue<uint32_t>(file, 300);
+ writeValue<uint32_t>(file, 1);
+ // 0x00d2 + 2*N SampleFormat data
+ writeValueRepeated<uint16_t>(file, 3, channels);
+ // 0x00d2 + 4*N SMinSampleValue data
+ writeValueRepeated<float>(file, 0.f, channels);
+ // 0x00d2 + 8*N SMaxSampleValue data
+ writeValueRepeated<float>(file, 1.f, channels);
+ // Offset = 0x00d2 + 12*N
+ } else {
+ // 0x00c2 XResolution data
+ writeValue<uint32_t>(file, 300);
+ writeValue<uint32_t>(file, 1);
+ // 0x00ca YResolution data
+ writeValue<uint32_t>(file, 300);
+ writeValue<uint32_t>(file, 1);
+ // Offset = 0x00d2
+ }
+
+ return true;
+}
+
+template <int N>
+bool saveTiffFloat(const BitmapConstRef<float, N> &bitmap, const char *filename) {
+ FILE *file = fopen(filename, "wb");
+ if (!file)
+ return false;
+ writeTiffHeader(file, bitmap.width, bitmap.height, N);
+ for (int y = bitmap.height-1; y >= 0; --y)
+ fwrite(bitmap(0, y), sizeof(float), N*bitmap.width, file);
+ return !fclose(file);
+}
+
+bool saveTiff(const BitmapConstRef<float, 1> &bitmap, const char *filename) {
+ return saveTiffFloat(bitmap, filename);
+}
+bool saveTiff(const BitmapConstRef<float, 3> &bitmap, const char *filename) {
+ return saveTiffFloat(bitmap, filename);
+}
+bool saveTiff(const BitmapConstRef<float, 4> &bitmap, const char *filename) {
+ return saveTiffFloat(bitmap, filename);
+}
+
+}
diff --git a/thirdparty/msdfgen/core/save-tiff.h b/thirdparty/msdfgen/core/save-tiff.h
new file mode 100644
index 0000000000..072cd71d50
--- /dev/null
+++ b/thirdparty/msdfgen/core/save-tiff.h
@@ -0,0 +1,13 @@
+
+#pragma once
+
+#include "BitmapRef.hpp"
+
+namespace msdfgen {
+
+/// Saves the bitmap as an uncompressed floating-point TIFF file.
+bool saveTiff(const BitmapConstRef<float, 1> &bitmap, const char *filename);
+bool saveTiff(const BitmapConstRef<float, 3> &bitmap, const char *filename);
+bool saveTiff(const BitmapConstRef<float, 4> &bitmap, const char *filename);
+
+}
diff --git a/thirdparty/msdfgen/core/sdf-error-estimation.cpp b/thirdparty/msdfgen/core/sdf-error-estimation.cpp
new file mode 100644
index 0000000000..7c00c449a9
--- /dev/null
+++ b/thirdparty/msdfgen/core/sdf-error-estimation.cpp
@@ -0,0 +1,192 @@
+
+#include "sdf-error-estimation.h"
+
+#include <cmath>
+#include "arithmetics.hpp"
+
+namespace msdfgen {
+
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Projection &projection, double y, bool inverseYAxis) {
+ if (!(sdf.width > 0 && sdf.height > 0))
+ return line.setIntersections(std::vector<Scanline::Intersection>());
+ double pixelY = clamp(projection.projectY(y)-.5, double(sdf.height-1));
+ if (inverseYAxis)
+ pixelY = sdf.height-1-pixelY;
+ int b = (int) floor(pixelY);
+ int t = b+1;
+ double bt = pixelY-b;
+ if (t >= sdf.height) {
+ b = sdf.height-1;
+ t = sdf.height-1;
+ bt = 1;
+ }
+ bool inside = false;
+ std::vector<Scanline::Intersection> intersections;
+ float lv, rv = mix(*sdf(0, b), *sdf(0, t), bt);
+ if ((inside = rv > .5f)) {
+ Scanline::Intersection intersection = { -1e240, 1 };
+ intersections.push_back(intersection);
+ }
+ for (int l = 0, r = 1; r < sdf.width; ++l, ++r) {
+ lv = rv;
+ rv = mix(*sdf(r, b), *sdf(r, t), bt);
+ if (lv != rv) {
+ double lr = double(.5f-lv)/double(rv-lv);
+ if (lr >= 0 && lr <= 1) {
+ Scanline::Intersection intersection = { projection.unprojectX(l+lr+.5), sign(rv-lv) };
+ intersections.push_back(intersection);
+ }
+ }
+ }
+#ifdef MSDFGEN_USE_CPP11
+ line.setIntersections((std::vector<Scanline::Intersection> &&) intersections);
+#else
+ line.setIntersections(intersections);
+#endif
+}
+
+template <int N>
+void scanlineMSDF(Scanline &line, const BitmapConstRef<float, N> &sdf, const Projection &projection, double y, bool inverseYAxis) {
+ if (!(sdf.width > 0 && sdf.height > 0))
+ return line.setIntersections(std::vector<Scanline::Intersection>());
+ double pixelY = clamp(projection.projectY(y)-.5, double(sdf.height-1));
+ if (inverseYAxis)
+ pixelY = sdf.height-1-pixelY;
+ int b = (int) floor(pixelY);
+ int t = b+1;
+ double bt = pixelY-b;
+ if (t >= sdf.height) {
+ b = sdf.height-1;
+ t = sdf.height-1;
+ bt = 1;
+ }
+ bool inside = false;
+ std::vector<Scanline::Intersection> intersections;
+ float lv[3], rv[3];
+ rv[0] = mix(sdf(0, b)[0], sdf(0, t)[0], bt);
+ rv[1] = mix(sdf(0, b)[1], sdf(0, t)[1], bt);
+ rv[2] = mix(sdf(0, b)[2], sdf(0, t)[2], bt);
+ if ((inside = median(rv[0], rv[1], rv[2]) > .5f)) {
+ Scanline::Intersection intersection = { -1e240, 1 };
+ intersections.push_back(intersection);
+ }
+ for (int l = 0, r = 1; r < sdf.width; ++l, ++r) {
+ lv[0] = rv[0], lv[1] = rv[1], lv[2] = rv[2];
+ rv[0] = mix(sdf(r, b)[0], sdf(r, t)[0], bt);
+ rv[1] = mix(sdf(r, b)[1], sdf(r, t)[1], bt);
+ rv[2] = mix(sdf(r, b)[2], sdf(r, t)[2], bt);
+ Scanline::Intersection newIntersections[4];
+ int newIntersectionCount = 0;
+ for (int i = 0; i < 3; ++i) {
+ if (lv[i] != rv[i]) {
+ double lr = double(.5f-lv[i])/double(rv[i]-lv[i]);
+ if (lr >= 0 && lr <= 1) {
+ float v[3] = {
+ mix(lv[0], rv[0], lr),
+ mix(lv[1], rv[1], lr),
+ mix(lv[2], rv[2], lr)
+ };
+ if (median(v[0], v[1], v[2]) == v[i]) {
+ newIntersections[newIntersectionCount].x = projection.unprojectX(l+lr+.5);
+ newIntersections[newIntersectionCount].direction = sign(rv[i]-lv[i]);
+ ++newIntersectionCount;
+ }
+ }
+ }
+ }
+ // Sort new intersections
+ if (newIntersectionCount >= 2) {
+ if (newIntersections[0].x > newIntersections[1].x)
+ newIntersections[3] = newIntersections[0], newIntersections[0] = newIntersections[1], newIntersections[1] = newIntersections[3];
+ if (newIntersectionCount >= 3 && newIntersections[1].x > newIntersections[2].x) {
+ newIntersections[3] = newIntersections[1], newIntersections[1] = newIntersections[2], newIntersections[2] = newIntersections[3];
+ if (newIntersections[0].x > newIntersections[1].x)
+ newIntersections[3] = newIntersections[0], newIntersections[0] = newIntersections[1], newIntersections[1] = newIntersections[3];
+ }
+ }
+ for (int i = 0; i < newIntersectionCount; ++i) {
+ if ((newIntersections[i].direction > 0) == !inside) {
+ intersections.push_back(newIntersections[i]);
+ inside = !inside;
+ }
+ }
+ // Consistency check
+ float rvScalar = median(rv[0], rv[1], rv[2]);
+ if ((rvScalar > .5f) != inside && rvScalar != .5f && !intersections.empty()) {
+ intersections.pop_back();
+ inside = !inside;
+ }
+ }
+#ifdef MSDFGEN_USE_CPP11
+ line.setIntersections((std::vector<Scanline::Intersection> &&) intersections);
+#else
+ line.setIntersections(intersections);
+#endif
+}
+
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Projection &projection, double y, bool inverseYAxis) {
+ scanlineMSDF(line, sdf, projection, y, inverseYAxis);
+}
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Projection &projection, double y, bool inverseYAxis) {
+ scanlineMSDF(line, sdf, projection, y, inverseYAxis);
+}
+
+template <int N>
+double estimateSDFErrorInner(const BitmapConstRef<float, N> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
+ if (sdf.width <= 1 || sdf.height <= 1 || scanlinesPerRow < 1)
+ return 0;
+ double subRowSize = 1./scanlinesPerRow;
+ double xFrom = projection.unprojectX(.5);
+ double xTo = projection.unprojectX(sdf.width-.5);
+ double overlapFactor = 1/(xTo-xFrom);
+ double error = 0;
+ Scanline refScanline, sdfScanline;
+ for (int row = 0; row < sdf.height-1; ++row) {
+ for (int subRow = 0; subRow < scanlinesPerRow; ++subRow) {
+ double bt = (subRow+.5)*subRowSize;
+ double y = projection.unprojectY(row+bt+.5);
+ shape.scanline(refScanline, y);
+ scanlineSDF(sdfScanline, sdf, projection, y, shape.inverseYAxis);
+ error += 1-overlapFactor*Scanline::overlap(refScanline, sdfScanline, xFrom, xTo, fillRule);
+ }
+ }
+ return error/((sdf.height-1)*scanlinesPerRow);
+}
+
+double estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
+ return estimateSDFErrorInner(sdf, shape, projection, scanlinesPerRow, fillRule);
+}
+double estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
+ return estimateSDFErrorInner(sdf, shape, projection, scanlinesPerRow, fillRule);
+}
+double estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
+ return estimateSDFErrorInner(sdf, shape, projection, scanlinesPerRow, fillRule);
+}
+
+// Legacy API
+
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y) {
+ scanlineSDF(line, sdf, Projection(scale, translate), y, inverseYAxis);
+}
+
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y) {
+ scanlineSDF(line, sdf, Projection(scale, translate), y, inverseYAxis);
+}
+
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y) {
+ scanlineSDF(line, sdf, Projection(scale, translate), y, inverseYAxis);
+}
+
+double estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
+ return estimateSDFError(sdf, shape, Projection(scale, translate), scanlinesPerRow, fillRule);
+}
+
+double estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
+ return estimateSDFError(sdf, shape, Projection(scale, translate), scanlinesPerRow, fillRule);
+}
+
+double estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
+ return estimateSDFError(sdf, shape, Projection(scale, translate), scanlinesPerRow, fillRule);
+}
+
+}
diff --git a/thirdparty/msdfgen/core/sdf-error-estimation.h b/thirdparty/msdfgen/core/sdf-error-estimation.h
new file mode 100644
index 0000000000..d2fd40d2b8
--- /dev/null
+++ b/thirdparty/msdfgen/core/sdf-error-estimation.h
@@ -0,0 +1,30 @@
+
+#pragma once
+
+#include "Vector2.h"
+#include "Shape.h"
+#include "Projection.h"
+#include "Scanline.h"
+#include "BitmapRef.hpp"
+
+namespace msdfgen {
+
+/// Analytically constructs a scanline at y evaluating fill by linear interpolation of the SDF.
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Projection &projection, double y, bool inverseYAxis = false);
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Projection &projection, double y, bool inverseYAxis = false);
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Projection &projection, double y, bool inverseYAxis = false);
+
+/// Estimates the portion of the area that will be filled incorrectly when rendering using the SDF.
+double estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
+double estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
+double estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
+
+// Old version of the function API's kept for backwards compatibility
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y);
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y);
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y);
+double estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
+double estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
+double estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
+
+}
diff --git a/thirdparty/msdfgen/core/shape-description.cpp b/thirdparty/msdfgen/core/shape-description.cpp
new file mode 100644
index 0000000000..a096fa2541
--- /dev/null
+++ b/thirdparty/msdfgen/core/shape-description.cpp
@@ -0,0 +1,284 @@
+
+#define _CRT_SECURE_NO_WARNINGS
+#include "shape-description.h"
+
+namespace msdfgen {
+
+int readCharF(FILE *input) {
+ int c = '\0';
+ do {
+ c = fgetc(input);
+ } while (c == ' ' || c == '\t' || c == '\r' || c == '\n');
+ return c;
+}
+
+int readCharS(const char **input) {
+ int c = '\0';
+ do {
+ c = *(*input)++;
+ } while (c == ' ' || c == '\t' || c == '\r' || c == '\n');
+ if (!c) {
+ --c;
+ return EOF;
+ }
+ return c;
+}
+
+int readCoordF(FILE *input, Point2 &coord) {
+ return fscanf(input, "%lf,%lf", &coord.x, &coord.y);
+}
+
+int readCoordS(const char **input, Point2 &coord) {
+ int read = 0;
+ int result = sscanf(*input, "%lf,%lf%n", &coord.x, &coord.y, &read);
+ *input += read;
+ return result;
+}
+
+static bool writeCoord(FILE *output, Point2 coord) {
+ fprintf(output, "%.12g, %.12g", coord.x, coord.y);
+ return true;
+}
+
+template <typename T, int (*readChar)(T *), int (*readCoord)(T *, Point2 &)>
+static int readControlPoints(T *input, Point2 *output) {
+ int result = readCoord(input, output[0]);
+ if (result == 2) {
+ switch (readChar(input)) {
+ case ')':
+ return 1;
+ case ';':
+ break;
+ default:
+ return -1;
+ }
+ result = readCoord(input, output[1]);
+ if (result == 2 && readChar(input) == ')')
+ return 2;
+ } else if (result != 1 && readChar(input) == ')')
+ return 0;
+ return -1;
+}
+
+template <typename T, int (*readChar)(T *), int (*readCoord)(T *, Point2 &)>
+static bool readContour(T *input, Contour &output, const Point2 *first, int terminator, bool &colorsSpecified) {
+ Point2 p[4], start;
+ if (first)
+ p[0] = *first;
+ else {
+ int result = readCoord(input, p[0]);
+ if (result != 2)
+ return result != 1 && readChar(input) == terminator;
+ }
+ start = p[0];
+ int c = '\0';
+ while ((c = readChar(input)) != terminator) {
+ if (c != ';')
+ return false;
+ EdgeColor color = WHITE;
+ int result = readCoord(input, p[1]);
+ if (result == 2) {
+ output.addEdge(EdgeHolder(p[0], p[1], color));
+ p[0] = p[1];
+ continue;
+ } else if (result == 1)
+ return false;
+ else {
+ int controlPoints = 0;
+ switch ((c = readChar(input))) {
+ case '#':
+ output.addEdge(EdgeHolder(p[0], start, color));
+ p[0] = start;
+ continue;
+ case ';':
+ goto FINISH_EDGE;
+ case '(':
+ goto READ_CONTROL_POINTS;
+ case 'C': case 'c':
+ color = CYAN;
+ colorsSpecified = true;
+ break;
+ case 'M': case 'm':
+ color = MAGENTA;
+ colorsSpecified = true;
+ break;
+ case 'Y': case 'y':
+ color = YELLOW;
+ colorsSpecified = true;
+ break;
+ case 'W': case 'w':
+ color = WHITE;
+ colorsSpecified = true;
+ break;
+ default:
+ return c == terminator;
+ }
+ switch (readChar(input)) {
+ case ';':
+ goto FINISH_EDGE;
+ case '(':
+ READ_CONTROL_POINTS:
+ if ((controlPoints = readControlPoints<T, readChar, readCoord>(input, p+1)) < 0)
+ return false;
+ break;
+ default:
+ return false;
+ }
+ if (readChar(input) != ';')
+ return false;
+ FINISH_EDGE:
+ result = readCoord(input, p[1+controlPoints]);
+ if (result != 2) {
+ if (result == 1)
+ return false;
+ else {
+ if (readChar(input) == '#')
+ p[1+controlPoints] = start;
+ else
+ return false;
+ }
+ }
+ switch (controlPoints) {
+ case 0:
+ output.addEdge(EdgeHolder(p[0], p[1], color));
+ p[0] = p[1];
+ continue;
+ case 1:
+ output.addEdge(EdgeHolder(p[0], p[1], p[2], color));
+ p[0] = p[2];
+ continue;
+ case 2:
+ output.addEdge(EdgeHolder(p[0], p[1], p[2], p[3], color));
+ p[0] = p[3];
+ continue;
+ }
+ }
+ }
+ return true;
+}
+
+bool readShapeDescription(FILE *input, Shape &output, bool *colorsSpecified) {
+ bool locColorsSpec = false;
+ output.contours.clear();
+ output.inverseYAxis = false;
+ Point2 p;
+ int result = readCoordF(input, p);
+ if (result == 2) {
+ return readContour<FILE, readCharF, readCoordF>(input, output.addContour(), &p, EOF, locColorsSpec);
+ } else if (result == 1)
+ return false;
+ else {
+ int c = readCharF(input);
+ if (c == '@') {
+ char after = '\0';
+ if (fscanf(input, "invert-y%c", &after) != 1)
+ return feof(input) != 0;
+ output.inverseYAxis = true;
+ c = after;
+ if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
+ c = readCharF(input);
+ }
+ for (; c == '{'; c = readCharF(input))
+ if (!readContour<FILE, readCharF, readCoordF>(input, output.addContour(), NULL, '}', locColorsSpec))
+ return false;
+ if (colorsSpecified)
+ *colorsSpecified = locColorsSpec;
+ return c == EOF && feof(input);
+ }
+}
+
+bool readShapeDescription(const char *input, Shape &output, bool *colorsSpecified) {
+ bool locColorsSpec = false;
+ output.contours.clear();
+ output.inverseYAxis = false;
+ Point2 p;
+ int result = readCoordS(&input, p);
+ if (result == 2) {
+ return readContour<const char *, readCharS, readCoordS>(&input, output.addContour(), &p, EOF, locColorsSpec);
+ } else if (result == 1)
+ return false;
+ else {
+ int c = readCharS(&input);
+ if (c == '@') {
+ for (int i = 0; i < (int) sizeof("invert-y")-1; ++i)
+ if (input[i] != "invert-y"[i])
+ return false;
+ output.inverseYAxis = true;
+ input += sizeof("invert-y")-1;
+ c = readCharS(&input);
+ }
+ for (; c == '{'; c = readCharS(&input))
+ if (!readContour<const char *, readCharS, readCoordS>(&input, output.addContour(), NULL, '}', locColorsSpec))
+ return false;
+ if (colorsSpecified)
+ *colorsSpecified = locColorsSpec;
+ return c == EOF;
+ }
+}
+
+static bool isColored(const Shape &shape) {
+ for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
+ for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge)
+ if ((*edge)->color != WHITE)
+ return true;
+ return false;
+}
+
+bool writeShapeDescription(FILE *output, const Shape &shape) {
+ if (!shape.validate())
+ return false;
+ bool writeColors = isColored(shape);
+ if (shape.inverseYAxis)
+ fprintf(output, "@invert-y\n");
+ for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
+ fprintf(output, "{\n");
+ if (!contour->edges.empty()) {
+ for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
+ char colorCode = '\0';
+ if (writeColors) {
+ switch ((*edge)->color) {
+ case YELLOW: colorCode = 'y'; break;
+ case MAGENTA: colorCode = 'm'; break;
+ case CYAN: colorCode = 'c'; break;
+ case WHITE: colorCode = 'w'; break;
+ default:;
+ }
+ }
+ if (const LinearSegment *e = dynamic_cast<const LinearSegment *>(&**edge)) {
+ fprintf(output, "\t");
+ writeCoord(output, e->p[0]);
+ fprintf(output, ";\n");
+ if (colorCode)
+ fprintf(output, "\t\t%c;\n", colorCode);
+ }
+ if (const QuadraticSegment *e = dynamic_cast<const QuadraticSegment *>(&**edge)) {
+ fprintf(output, "\t");
+ writeCoord(output, e->p[0]);
+ fprintf(output, ";\n\t\t");
+ if (colorCode)
+ fprintf(output, "%c", colorCode);
+ fprintf(output, "(");
+ writeCoord(output, e->p[1]);
+ fprintf(output, ");\n");
+ }
+ if (const CubicSegment *e = dynamic_cast<const CubicSegment *>(&**edge)) {
+ fprintf(output, "\t");
+ writeCoord(output, e->p[0]);
+ fprintf(output, ";\n\t\t");
+ if (colorCode)
+ fprintf(output, "%c", colorCode);
+ fprintf(output, "(");
+ writeCoord(output, e->p[1]);
+ fprintf(output, "; ");
+ writeCoord(output, e->p[2]);
+ fprintf(output, ");\n");
+ }
+ }
+ fprintf(output, "\t#\n");
+ }
+ fprintf(output, "}\n");
+ }
+ return true;
+}
+
+}
diff --git a/thirdparty/msdfgen/core/shape-description.h b/thirdparty/msdfgen/core/shape-description.h
new file mode 100644
index 0000000000..5df7c50a03
--- /dev/null
+++ b/thirdparty/msdfgen/core/shape-description.h
@@ -0,0 +1,16 @@
+
+#pragma once
+
+#include <cstdlib>
+#include <cstdio>
+#include "Shape.h"
+
+namespace msdfgen {
+
+/// Deserializes a text description of a vector shape into output.
+bool readShapeDescription(FILE *input, Shape &output, bool *colorsSpecified = NULL);
+bool readShapeDescription(const char *input, Shape &output, bool *colorsSpecified = NULL);
+/// Serializes a shape object into a text description.
+bool writeShapeDescription(FILE *output, const Shape &shape);
+
+}
diff --git a/thirdparty/msdfgen/msdfgen.h b/thirdparty/msdfgen/msdfgen.h
new file mode 100644
index 0000000000..fb36bd7e1d
--- /dev/null
+++ b/thirdparty/msdfgen/msdfgen.h
@@ -0,0 +1,65 @@
+
+#pragma once
+
+/*
+ * MULTI-CHANNEL SIGNED DISTANCE FIELD GENERATOR v1.9 (2021-05-28)
+ * ---------------------------------------------------------------
+ * A utility by Viktor Chlumsky, (c) 2014 - 2021
+ *
+ * The technique used to generate multi-channel distance fields in this code
+ * has been developed by Viktor Chlumsky in 2014 for his master's thesis,
+ * "Shape Decomposition for Multi-Channel Distance Fields". It provides improved
+ * quality of sharp corners in glyphs and other 2D shapes compared to monochrome
+ * distance fields. To reconstruct an image of the shape, apply the median of three
+ * operation on the triplet of sampled signed distance values.
+ *
+ */
+
+#include "core/arithmetics.hpp"
+#include "core/Vector2.h"
+#include "core/Projection.h"
+#include "core/Scanline.h"
+#include "core/Shape.h"
+#include "core/BitmapRef.hpp"
+#include "core/Bitmap.h"
+#include "core/bitmap-interpolation.hpp"
+#include "core/pixel-conversion.hpp"
+#include "core/edge-coloring.h"
+#include "core/generator-config.h"
+#include "core/msdf-error-correction.h"
+#include "core/render-sdf.h"
+#include "core/rasterization.h"
+#include "core/sdf-error-estimation.h"
+#include "core/save-bmp.h"
+#include "core/save-tiff.h"
+#include "core/shape-description.h"
+
+#define MSDFGEN_VERSION "1.9"
+
+namespace msdfgen {
+
+/// Generates a conventional single-channel signed distance field.
+void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config = GeneratorConfig());
+
+/// Generates a single-channel signed pseudo-distance field.
+void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config = GeneratorConfig());
+
+/// Generates a multi-channel signed distance field. Edge colors must be assigned first! (See edgeColoringSimple)
+void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
+
+/// Generates a multi-channel signed distance field with true distance in the alpha channel. Edge colors must be assigned first.
+void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
+
+// Old version of the function API's kept for backwards compatibility
+void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
+void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
+void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig = ErrorCorrectionConfig(), bool overlapSupport = true);
+void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig = ErrorCorrectionConfig(), bool overlapSupport = true);
+
+// Original simpler versions of the previous functions, which work well under normal circumstances, but cannot deal with overlapping contours.
+void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate);
+void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate);
+void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig());
+void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig());
+
+}