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. + + + + + + + + + + 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. + + 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. + + + + Removes all font data sourcers for the set. + + @@ -151,6 +157,13 @@ Returns the number of font data sources. + + + + + Returns TextServer RID of the font data resources. + + @@ -180,15 +193,18 @@ - + - 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]). + + + 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 @@ - + - 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). @@ -256,19 +272,19 @@ - + + Default font size. + + Extra spacing at the bottom of the line in pixels. - + Extra spacing at the top of the line in pixels. + + Default font [url=https://docs.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg]variation coordinates[/url]. + - - Spacing at the top of the line. - - - Spacing at the bottom of the line. - 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 @@ - 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. - 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. - + - - - - - - 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. - + - - - + + - 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. - + - - - Adds a texture to the bitmap font. - - - - - + - - - - 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 kerning overrides. - - - - - - - - + + + - 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. + Removes all font sizes from the cache entry + + + + + + + + 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. + + + + + + + Returns existing or creates a new font cache entry for the specified variation coordinates. - + + Returns the font ascent (number of pixels above the baseline). - - + + - 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. + + + + + + + Returns text server font cache entry resource id. + + + + + + Returns contents of the dynamic font source file. - + + - Returns the font descent (number of pixels below the baseline). + Returns font descent (number of pixels below the baseline). - + + - 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. - + + - 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]. - + + + + + + Returns list of rendered glyphs in the cache entry. + + + - - - + + + - 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. - - - + + + + + - Returns the total font height (ascent plus descent) in pixels. + Returns glyph size. + + + + + + + + + Returns index of the cache texture containing the glyph. + + + + + + + + + Returns rectangle in the cache texture containing the glyph. + + + + + + Returns the font hinting mode. Used by dynamic fonts only. + + + + + + + + + Returns kerning for the pair of glyphs. + + + + + + + + Returns list of the kerning overrides. @@ -132,6 +189,32 @@ Returns list of language support overrides. + + + + Returns the width of the range around the shape between the minimum and maximum representable signed distance. + + + + + + Returns source font size used to generate MSDF textures. + + + + + + Returns font oversampling factor, if set to [code]0.0[/code] global oversampling factor is used instead. Used by dynamic fonts only. + + + + + + + + Returns scaling factor of the color bitmap font. + + @@ -145,11 +228,20 @@ Returns list of script support overrides. + + + + + Return list of the font sizes in the cache. Each size is [code]Vector2i[/code] with font size and outline size. + + - + + + - Returns the spacing for the given [code]type[/code] (see [enum SpacingType]). + Returns extra spacing added between glyphs in pixels. @@ -158,34 +250,68 @@ Returns a string containing all the characters available in the font. - - - + + - Returns underline offset (number of pixels below the baseline). + Returns list of OpenType features supported by font. - - - - - Returns underline thickness in pixels. - - - - - - - Returns variation coordinate [code]tag[/code]. - - - + 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 number of textures used by font cache entry. + + + + + + + + + Returns a copy of the font cache texture image. + + + + + + + + + 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. + + + + + + + + Returns pixel offset of the underline below the baseline. + + + + + + + + Returns thickness of the underline in pixels. + + + + + + + Returns variation coordinates for the specified font cache entry. See [method get_supported_variation_list] for more info. + + @@ -193,10 +319,16 @@ Return [code]true[/code] if a Unicode [code]char[/code] is available in the font. - + - Returns [code]true[/code], if font supports drawing glyph outlines. + Returns [code]true[/code] if font 8-bit anitialiased glyph rendering is supported and enabled. + + + + + + Returns [code]true[/code] if auto-hinting is supported and preffered over font built-in hinting. Used by dynamic fonts only. @@ -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). + + + + Returns [code]true[/code] if glyphs of all sizes are rendered using single multichannel signed distance field generated from the dynamic font vector data. + + @@ -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). - + - - - + - 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. - + - - + + + - 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. - + - - - + + + - Creates new, empty bitmap font. + Removes kerning override for the pair of glyphs. @@ -255,6 +390,148 @@ Removes script support override. + + + + + + Removes specified font size from the cache entry. + + + + + + + + + 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]. + + + + + + + + + Renders specified glyph the the font cache texture. + + + + + + + + + + Renders the range of characters to the font cache texture. + + + + + + + If set to [code]true[/code], 8-bit antialiased glyph rendering is used, otherwise 1-bit rendering is used. Used by dynamic fonts only. + + + + + + + + + Sets the font ascent (number of pixels above the baseline). + + + + + + + Sets font source data, e.g contents of the dynamic font source file. + + + + + + + + + Sets the font descent (number of pixels below the baseline). + + + + + + + If set to [code]true[/code] auto-hinting is preffered over font built-in hinting. + + + + + + + + + + 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. + + + + + + + + + + Sets glyph offset from the baseline. + + + + + + + + + + Sets glyph size. + + + + + + + + + + Sets index of the cache texture containing the glyph. + + + + + + + + + + Sets rectangle in the cache texture containing the glyph. + + + + + + + Sets font hinting mode. Used by dynamic fonts only. + + + + + + + + + + Sets kerning for the pair of glyphs. + + @@ -263,6 +540,43 @@ Adds override for [method is_language_supported]. + + + + + Sets the width of the range around the shape between the minimum and maximum representable signed distance. + + + + + + + Sets source font size used to generate MSDF textures. + + + + + + + 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. + + + + + + + Sets font oversampling factor, if set to [code]0.0[/code] global oversampling factor is used instead. Used by dynamic fonts only. + + + + + + + + + Sets scaling factor of the color bitmap font. + + @@ -273,52 +587,61 @@ - - + + + + - Sets the spacing for [code]type[/code] (see [enum SpacingType]) to [code]value[/code] in pixels (not relative to the font size). + Sets extra spacing added between glyphs in pixels. - + - - + + + + - Sets variation coordinate [code]tag[/code]. + Sets font cache texture image. + + + + + + + + + + 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). + + + + + + + + + Sets pixel offset of the underline below the baseline. + + + + + + + + + Sets thickness of the underline in pixels. + + + + + + + + Sets variation coordinates for the specified font cache entry. See [method get_supported_variation_list] for more info. - - - If [code]true[/code], the font is rendered with anti-aliasing. - - - The path to the font data file. If font data was loaded from memory location is set to [code]"(Memory)"[/code]. - - - If [code]true[/code], distance field hint is enabled. - - - Extra spacing for each glyph in pixels. - This can be a negative number to make the distance between glyphs smaller. - - - Extra spacing for the space character in pixels. - This can be a negative number to make the distance between words smaller. - - - If [code]true[/code], default autohinter is used for font hinting. - - - The font hinting mode used by FreeType. See [enum TextServer.Hinting] for options. - - - - Spacing for each glyph. - - - Spacing for the space character. - 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 @@ - 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]. - 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]. 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 @@ - + - - - - Creates new, empty bitmap font. To free the resulting font, use [method free_rid] method. - - - - - - - - - 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. - - - - - - - - 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. - - - - - - - - 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. @@ -68,52 +36,53 @@ Draws box displaying character hexadecimal code. Used for replacing missing characters. - + - - - - - - + + - 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. - + - - - - + + - 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. - + - - + - Adds a texture to the bitmap font. + Removes all font sizes from the cache entry + + + + + + + + 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. - - + + - 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]. - - + + @@ -121,68 +90,46 @@ - 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]. - - - - - Returns [code]true[/code], if font anti-aliasing is supported and enabled. - - - + Returns the font ascent (number of pixels above the baseline). - - - - - Returns the default size of the font. - - - + Returns the font descent (number of pixels below the baseline). - - - + + + - Returns [code]true[/code], if distance field hint is enabled. + Returns bitmap font fixed size. - - - + + - Returns list of OpenType features supported by font. - - - - - - - Returns [code]true[/code], if autohinter is supported and enabled. + Returns the font oversampling factor, shared by all fonts in the TextServer. - - - + + + - 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. @@ -191,7 +138,7 @@ - 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 @@ - - - + + + + Returns the glyph index of a [code]char[/code], optionally modified by the [code]variation_selector[/code]. - - - - - - + + + + - Returns a kerning of the pair of glyphs. + Returns list of rendered glyphs in the cache entry. - - - - + + + + + - Returns the total font height (ascent plus descent) in pixels. + Returns glyph offset from the baseline. + + + + + + + + + Returns size of the glyph. + + + + + + + + + Returns index of the cache texture containing the glyph. + + + + + + + + + Returns rectangle in the cache texture containing the glyph. - + - Returns the font hinting. + Returns the font hinting mode. Used by dynamic fonts only. + + + + + + + + + Returns kerning for the pair of glyphs. + + + + + + + + Returns list of the kerning overrides. - + Returns [code]true[/code] if support override is enabled for the [code]language[/code]. @@ -241,20 +232,43 @@ - + Returns list of language support overrides. + + + + + Return the width of the range around the shape between the minimum and maximum representable signed distance. + + + + + + + Returns source font size used to generate MSDF textures. + + + - 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. + + + + + + + + Returns scaling factor of the color bitmap font. - + Returns [code]true[/code] if support override is enabled for the [code]script[/code]. @@ -262,98 +276,149 @@ - + Returns list of script support overrides. - - - + + + - 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. - + - + + + - Sets extra spacing for each glyph in pixels. + Returns extra spacing added between glyphs in pixels. - + Returns a string containing all the characters available in the font. + + + + + + Returns number of textures used by font cache entry. + + + + + + + + + Returns font cache texture image data. + + + + + + + + + Returns array containing the first free pixel in the each column of texture. Should be the same size as texture width or empty. + + - + - Returns underline offset (number of pixels below the baseline). + Returns pixel offset of the underline below the baseline. - + - Returns underline thickness in pixels. + Returns thickness of the underline in pixels. - - - - - - Returns variation coordinate [code]tag[/code]. - - - + - + - 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. - + - 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. - + - + - Returns [code]true[/code], if font supports glyph outlines. + Returns [code]true[/code] if font 8-bit anitialiased glyph rendering is supported and enabled. + + + + + + + Returns [code]true[/code] if auto-hinting is supported and preffered over font built-in hinting. Used by dynamic fonts only. - + - 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). + + + + + + + Returns [code]true[/code] if glyphs of all sizes are rendered using single multichannel signed distance field generated from the dynamic font vector data. - + Returns [code]true[/code], if font supports given script (ISO 15924 code). + + + + + + + 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. + + + + + + + + + Removes kerning override for the pair of glyphs. + + - + Remove language support override. @@ -361,92 +426,299 @@ - + Removes script support override. - + - - + + - Sets font anti-aliasing. + Removes specified font size from the cache entry. - + - - + + + - Sets font distance field hint. + 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]. + + + + + + + + + Renders specified glyph the the font cache texture. + + + + + + + + + + Renders the range of characters to the font cache texture. + + + + + + + + If set to [code]true[/code], 8-bit antialiased glyph rendering is used, otherwise 1-bit rendering is used. Used by dynamic fonts only. + + + + + + + + + Sets the font ascent (number of pixels above the baseline). + + + + + + + + Sets font source data, e.g contents of the dynamic font source file. + + + + + + + + + Sets the font descent (number of pixels below the baseline). + + + + + + + + Sets bitmap font fixed size. If set to value greater than zero, same cache entry will be used for all font sizes. - - + + - Enables/disables default autohinter. + If set to [code]true[/code] auto-hinting is preffered over font built-in hinting. + + + + + + + Sets oversampling factor, shared by all font in the TextServer. + Note: This value can be automaticaly changed by display server. + + + + + + + + + + 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. + + + + + + + + + + Sets glyph offset from the baseline. + + + + + + + + + + Sets size of the glyph. + + + + + + + + + + Sets index of the cache texture containing the glyph. + + + + + + + + + + Sets rectangle in the cache texture containing the glyph. - - + + - Sets font hinting. + Sets font hinting mode. Used by dynamic fonts only. + + + + + + + + + + Sets kerning for the pair of glyphs. - + Adds override for [method font_is_language_supported]. + + + + + + Sets the width of the range around the shape between the minimum and maximum representable signed distance. + + + + + + + + Sets source font size used to generate MSDF textures. + + + + + + + + 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. + + - + + - 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. + + + + + + + + + Sets scaling factor of the color bitmap font. - + Adds override for [method font_is_script_supported]. - + - - + + + + - Returns extra spacing for the space character in pixels. + Sets extra spacing added between glyphs in pixels. - + - - + + + + - Sets extra spacing for the space character in pixels. + Sets font cache texture image data. - + - - - + + + + + + Sets array containing the first free pixel in the each column of texture. Should be the same size as texture width or empty. + + + + + + + + + Sets pixel offset of the underline below the baseline. + + + + + + + + + Sets thickness of the underline in pixels. + + + + + + + + Sets variation coordinates for the specified font cache entry. See [method font_supported_variation_list] for more info. + + + + + + + + + + + - Sets variation coordinate [code]name[/code]. Unsupported coordinates will be silently ignored. @@ -478,13 +750,6 @@ Returns the name of the server interface. - - - - Returns list of available system fonts. - Note: This method is supported by servers with the [code]FEATURE_FONT_SYSTEM[/code] feature. - - @@ -514,7 +779,7 @@ Note: This function should be called before any other TextServer functions used, otherwise it won't have any effect. - + @@ -890,7 +1155,7 @@ Aligns shaped text to the given tab-stops. - + @@ -1023,5 +1288,17 @@ Contour point isn't on the curve, but serves as a control point for a cubic Bézier arc. + + Spacing for each glyph. + + + Spacing for the space character. + + + Spacing at the top of the line. + + + Spacing at the bottom of the line. + 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 @@ + Returns custom font used to draw text in the column [code]column[/code]. + + + + + + + Returns custom font size used to draw text in the column [code]column[/code]. @@ -464,6 +472,15 @@ + Sets custom font used to draw text in the column [code]column[/code]. + + + + + + + + Sets custom font size used to draw text in the column [code]column[/code]. 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 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 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 variation_tags = m_variations.split(","); \ + for (int i = 0; i < variation_tags.size(); i++) { \ + Vector 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 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 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 variation_tags = m_variations.split(","); \ + for (int i = 0; i < variation_tags.size(); i++) { \ + Vector 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 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 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 variation_tags = m_variations.split(","); \ + for (int i = 0; i < variation_tags.size(); i++) { \ + Vector 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 load_cached_external_font(const String &p_path, TextServer::Hinting p_hinting, bool p_aa, bool p_autohint) { + Ref font; + font.instantiate(); + + Vector 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 load_cached_internal_font(const uint8_t *p_data, size_t p_size, TextServer::Hinting p_hinting, bool p_aa, bool p_autohint) { + Ref 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 p_theme) { DirAccess *dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); @@ -144,11 +211,7 @@ void editor_register_fonts(Ref p_theme) { String custom_font_path = EditorSettings::get_singleton()->get("interface/editor/main_font"); Ref 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 p_theme) { String custom_font_path_bold = EditorSettings::get_singleton()->get("interface/editor/main_font_bold"); Ref 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 p_theme) { String custom_font_path_source = EditorSettings::get_singleton()->get("interface/editor/code_font"); Ref 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 subtag = String(EditorSettings::get_singleton()->get("interface/editor/code_font_custom_variations")).split(","); - for (int i = 0; i < subtag.size(); i++) { - Vector 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 */ + /* Noto Sans */ - Ref 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 DefaultFont = load_cached_internal_font(_font_NotoSans_Regular, _font_NotoSans_Regular_size, font_hinting, font_antialiased, true); + Ref DefaultFontBold = load_cached_internal_font(_font_NotoSans_Bold, _font_NotoSans_Bold_size, font_hinting, font_antialiased, true); + Ref FontArabic = load_cached_internal_font(_font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size, font_hinting, font_antialiased, true); + Ref FontArabicBold = load_cached_internal_font(_font_NotoNaskhArabicUI_Bold, _font_NotoNaskhArabicUI_Bold_size, font_hinting, font_antialiased, true); + Ref FontBengali = load_cached_internal_font(_font_NotoSansBengaliUI_Regular, _font_NotoSansBengaliUI_Regular_size, font_hinting, font_antialiased, true); + Ref FontBengaliBold = load_cached_internal_font(_font_NotoSansBengaliUI_Bold, _font_NotoSansBengaliUI_Bold_size, font_hinting, font_antialiased, true); + Ref FontDevanagari = load_cached_internal_font(_font_NotoSansDevanagariUI_Regular, _font_NotoSansDevanagariUI_Regular_size, font_hinting, font_antialiased, true); + Ref FontDevanagariBold = load_cached_internal_font(_font_NotoSansDevanagariUI_Bold, _font_NotoSansDevanagariUI_Bold_size, font_hinting, font_antialiased, true); + Ref FontGeorgian = load_cached_internal_font(_font_NotoSansGeorgian_Regular, _font_NotoSansGeorgian_Regular_size, font_hinting, font_antialiased, true); + Ref FontGeorgianBold = load_cached_internal_font(_font_NotoSansGeorgian_Bold, _font_NotoSansGeorgian_Bold_size, font_hinting, font_antialiased, true); + Ref FontHebrew = load_cached_internal_font(_font_NotoSansHebrew_Regular, _font_NotoSansHebrew_Regular_size, font_hinting, font_antialiased, true); + Ref FontHebrewBold = load_cached_internal_font(_font_NotoSansHebrew_Bold, _font_NotoSansHebrew_Bold_size, font_hinting, font_antialiased, true); + Ref FontMalayalam = load_cached_internal_font(_font_NotoSansMalayalamUI_Regular, _font_NotoSansMalayalamUI_Regular_size, font_hinting, font_antialiased, true); + Ref FontMalayalamBold = load_cached_internal_font(_font_NotoSansMalayalamUI_Bold, _font_NotoSansMalayalamUI_Bold_size, font_hinting, font_antialiased, true); + Ref FontOriya = load_cached_internal_font(_font_NotoSansOriyaUI_Regular, _font_NotoSansOriyaUI_Regular_size, font_hinting, font_antialiased, true); + Ref FontOriyaBold = load_cached_internal_font(_font_NotoSansOriyaUI_Bold, _font_NotoSansOriyaUI_Bold_size, font_hinting, font_antialiased, true); + Ref FontSinhala = load_cached_internal_font(_font_NotoSansSinhalaUI_Regular, _font_NotoSansSinhalaUI_Regular_size, font_hinting, font_antialiased, true); + Ref FontSinhalaBold = load_cached_internal_font(_font_NotoSansSinhalaUI_Bold, _font_NotoSansSinhalaUI_Bold_size, font_hinting, font_antialiased, true); + Ref FontTamil = load_cached_internal_font(_font_NotoSansTamilUI_Regular, _font_NotoSansTamilUI_Regular_size, font_hinting, font_antialiased, true); + Ref FontTamilBold = load_cached_internal_font(_font_NotoSansTamilUI_Bold, _font_NotoSansTamilUI_Bold_size, font_hinting, font_antialiased, true); + Ref FontTelugu = load_cached_internal_font(_font_NotoSansTeluguUI_Regular, _font_NotoSansTeluguUI_Regular_size, font_hinting, font_antialiased, true); + Ref FontTeluguBold = load_cached_internal_font(_font_NotoSansTeluguUI_Bold, _font_NotoSansTeluguUI_Bold_size, font_hinting, font_antialiased, true); + Ref FontThai = load_cached_internal_font(_font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size, font_hinting, font_antialiased, true); + Ref FontThaiBold = load_cached_internal_font(_font_NotoSansThaiUI_Bold, _font_NotoSansThaiUI_Bold_size, font_hinting, font_antialiased, true); - Ref 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? + /* Droid Sans */ - Ref 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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? + Ref FontFallback = load_cached_internal_font(_font_DroidSansFallback, _font_DroidSansFallback_size, font_hinting, font_antialiased, true); + Ref FontJapanese = load_cached_internal_font(_font_DroidSansJapanese, _font_DroidSansJapanese_size, font_hinting, font_antialiased, true); /* Hack */ - Ref 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 subtag = String(EditorSettings::get_singleton()->get("interface/editor/code_font_custom_variations")).split(","); - Dictionary ftrs; - for (int i = 0; i < subtag.size(); i++) { - Vector subtag_a = subtag[i].split("="); - if (subtag_a.size() == 2) { - dfmono->set_variation(subtag_a[0], subtag_a[1].to_float()); - } - } + Ref 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 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 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 import_font_data_dynamic; + import_font_data_dynamic.instantiate(); + ResourceFormatImporter::get_singleton()->add_importer(import_font_data_dynamic); + + Ref import_font_data_bmfont; + import_font_data_bmfont.instantiate(); + ResourceFormatImporter::get_singleton()->add_importer(import_font_data_bmfont); + + Ref import_font_data_image; + import_font_data_image.instantiate(); + ResourceFormatImporter::get_singleton()->add_importer(import_font_data_image); + Ref 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 settings; + Map defaults; + List 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 *p_list) const { + for (const List::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 import_variation_data; + import_variation_data.instantiate(); + import_variation_data->owner = this; + ERR_FAIL_NULL(import_variation_data); + + for (List::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 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 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 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::Element *E = import_variation_data_a->settings.front(); E; E = E->next()) { + Ref 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 ftr = ftr_edit->get_text().split(","); + for (int i = 0; i < ftr.size(); i++) { + Vector 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 ftr = ftr_edit->get_text().split(","); + for (int i = 0; i < ftr.size(); i++) { + Vector 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 &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 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 variations; + for (TreeItem *vars_item = vars_list_root->get_first_child(); vars_item; vars_item = vars_item->get_next()) { + String variation; + Ref 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::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 langs_enabled; + Vector 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 scripts_enabled; + Vector 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 ranges; + char32_t start = selected_chars.front()->get(); + for (Set::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 ranges; + int32_t start = selected_glyphs.front()->get(); + for (Set::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::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 data = FileAccess::get_file_as_array(p_path); + + // Load font for preview. + Ref 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 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::Element *E = options_general.front(); E; E = E->next()) { + import_settings_data->defaults[E->get().option.name] = E->get().default_value; + } + + Ref config; + config.instantiate(); + ERR_FAIL_NULL(config); + + Error err = config->load(p_path + ".import"); + print_verbose("Loading import settings:"); + if (err == OK) { + List keys; + config->get_section_keys("params", &keys); + for (List::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 ranges = config->get_value("params", key); + for (int i = 0; i < ranges.size(); i++) { + int32_t start, end; + Vector 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 ranges = config->get_value("params", key); + for (int i = 0; i < ranges.size(); i++) { + int32_t start, end; + Vector 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 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 import_variation_data_custom; + import_variation_data_custom.instantiate(); + import_variation_data_custom->owner = this; + ERR_FAIL_NULL(import_variation_data_custom); + + for (List::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 variation_tags = variations[i].split(","); + for (int j = 0; j < variation_tags.size(); j++) { + Vector 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 import_settings_data; + List options_variations; + List 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_preview; + Ref font_main; + + Set selected_chars; + Set 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 *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 &p_options) const { + return true; +} + +void ResourceImporterBMFont::get_import_options(List *r_options, int p_preset) const { + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress"), true)); +} + +void _convert_packed_8bit(Ref &r_font, Ref &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 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 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 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 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 &r_font, Ref &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 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 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 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 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 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 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 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 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 &r_font, Ref &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 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 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 &r_font, Ref &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 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 &r_font, Ref &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 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 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 &p_options, List *r_platform_variants, List *r_gen_files, Variant *r_metadata) { + print_verbose("Importing BMFont font from: " + p_source_file); + + Ref 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 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 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 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 *p_extensions) const override; + virtual String get_save_extension() const override; + virtual String get_resource_type() const override; + + virtual void get_import_options(List *r_options, int p_preset = 0) const override; + virtual bool get_option_visibility(const String &p_option, const Map &p_options) const override; + + virtual Error import(const String &p_source_file, const String &p_save_path, const Map &p_options, List *r_platform_variants, List *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 *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 &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 *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())); + r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "preload/glyph_ranges"), Vector())); + r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "preload/configurations"), Vector())); + + r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "support_overrides/language_enabled"), Vector())); + r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "support_overrides/language_disabled"), Vector())); + + r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "support_overrides/script_enabled"), Vector())); + r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "support_overrides/script_disabled"), Vector())); +} + +bool ResourceImporterDynamicFont::_decode_variation(const String &p_token, Dictionary &r_variations, Vector2i &r_size, String &r_name, Vector2i &r_spacing) { + Vector 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 &p_options, List *r_platform_variants, List *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 data = FileAccess::get_file_as_array(p_source_file); + + // Create font. + Ref 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 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 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 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 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 variations = p_options["preload/configurations"]; + Vector char_ranges = p_options["preload/char_ranges"]; + Vector 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 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 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 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 *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 *r_options, int p_preset = 0) const override; + virtual bool get_option_visibility(const String &p_option, const Map &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 &p_options, List *r_platform_variants, List *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 *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 &p_options) const { + return true; +} + +void ResourceImporterImageFont::get_import_options(List *r_options, int p_preset) const { + r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "character_ranges"), Vector())); + 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 &p_options, List *r_platform_variants, List *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 ranges = p_options["character_ranges"]; + + Ref 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 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 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 *p_extensions) const override; + virtual String get_save_extension() const override; + virtual String get_resource_type() const override; + + virtual void get_import_options(List *r_options, int p_preset = 0) const override; + virtual bool get_option_visibility(const String &p_option, const Map &p_options) const override; + + virtual Error import(const String &p_source_file, const String &p_save_path, const Map &p_options, List *r_platform_variants, List *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 EditorFontPreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size) const { RES res = ResourceLoader::load(p_path); Ref sampled_font; @@ -886,15 +837,15 @@ Ref 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 &p_data) { Ref 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(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(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(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(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(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(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(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); } -/*************************************************************************/ -/* Font interface */ -/*************************************************************************/ - -RID TextServerGDNative::create_font_system(const String &p_name, int p_base_size) { - ERR_FAIL_COND_V(interface == nullptr, RID()); - godot_rid result = interface->create_font_system(data, (const godot_string *)&p_name, p_base_size); - 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; -} - -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; -} - -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_bitmap_add_texture(RID p_font, const Ref &p_texture) { - ERR_FAIL_COND(interface == nullptr); - interface->font_bitmap_add_texture(data, (godot_rid *)&p_font, (const godot_object *)p_texture.ptr()); -} - -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) { - 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); -} - -void TextServerGDNative::font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) { - ERR_FAIL_COND(interface == nullptr); - interface->font_bitmap_add_kerning_pair(data, (godot_rid *)&p_font, p_A, p_B, p_kerning); -} - -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); -} - -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); -} - -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); -} - -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); -} - -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); -} - -int TextServerGDNative::font_get_spacing_space(RID p_font) const { +int32_t TextServerGDNative::name_to_tag(const String &p_name) const { ERR_FAIL_COND_V(interface == nullptr, 0); - return interface->font_get_spacing_space(data, (godot_rid *)&p_font); + return interface->name_to_tag(data, (godot_string *)&p_name); } -void TextServerGDNative::font_set_spacing_space(RID p_font, int p_value) { - ERR_FAIL_COND(interface == nullptr); - interface->font_set_spacing_space(data, (godot_rid *)&p_font, p_value); -} - -int TextServerGDNative::font_get_spacing_glyph(RID p_font) const { - ERR_FAIL_COND_V(interface == nullptr, 0); - return interface->font_get_spacing_glyph(data, (godot_rid *)&p_font); -} - -void TextServerGDNative::font_set_spacing_glyph(RID p_font, int p_value) { - ERR_FAIL_COND(interface == nullptr); - interface->font_set_spacing_glyph(data, (godot_rid *)&p_font, p_value); -} - -void TextServerGDNative::font_set_antialiased(RID p_font, bool p_antialiased) { - ERR_FAIL_COND(interface == nullptr); - interface->font_set_antialiased(data, (godot_rid *)&p_font, p_antialiased); -} - -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); -} - -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); - - return info; -} - -void TextServerGDNative::font_set_variation(RID p_font, const String &p_name, double p_value) { - ERR_FAIL_COND(interface == nullptr); - interface->font_set_variation(data, (godot_rid *)&p_font, (godot_string *)&p_name, p_value); -} - -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); -} - -void TextServerGDNative::font_set_hinting(RID p_font, TextServer::Hinting p_hinting) { - ERR_FAIL_COND(interface == nullptr); - interface->font_set_hinting(data, (godot_rid *)&p_font, (godot_int)p_hinting); -} - -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); -} - -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); - - return info; -} - -void TextServerGDNative::font_set_distance_field_hint(RID p_font, bool p_distance_field) { - ERR_FAIL_COND(interface == nullptr); - interface->font_set_distance_field_hint(data, (godot_rid *)&p_font, p_distance_field); -} - -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); -} - -void TextServerGDNative::font_set_force_autohinter(RID p_font, bool p_enabeld) { - ERR_FAIL_COND(interface == nullptr); - interface->font_set_force_autohinter(data, (godot_rid *)&p_font, p_enabeld); -} - -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); -} - -bool TextServerGDNative::font_has_char(RID p_font, char32_t p_char) const { - ERR_FAIL_COND_V(interface == nullptr, false); - return interface->font_has_char(data, (godot_rid *)&p_font, p_char); -} - -String TextServerGDNative::font_get_supported_chars(RID p_font) const { +String TextServerGDNative::tag_to_name(int32_t p_tag) 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 result = interface->tag_to_name(data, p_tag); + String name = *(String *)&result; godot_string_destroy(&result); - return ret; + return name; } -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); +/*************************************************************************/ +/* Font */ +/*************************************************************************/ + +RID TextServerGDNative::create_font() { + ERR_FAIL_COND_V(interface == nullptr, RID()); + godot_rid result = interface->create_font(data); + RID rid = *(RID *)&result; + return rid; } -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); -} - -bool TextServerGDNative::font_is_language_supported(RID p_font, const String &p_language) const { - ERR_FAIL_COND_V(interface == nullptr, false); - return interface->font_is_language_supported(data, (godot_rid *)&p_font, (godot_string *)&p_language); -} - -void TextServerGDNative::font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) { +void TextServerGDNative::font_set_data(RID p_font_rid, const PackedByteArray &p_data) { 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_set_data(data, (godot_rid *)&p_font_rid, (const godot_packed_byte_array *)&p_data); } -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_remove_language_support_override(RID p_font, const String &p_language) { +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_remove_language_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_language); + interface->font_set_data_ptr(data, (godot_rid *)&p_font_rid, p_data_ptr, p_data_size); } -Vector TextServerGDNative::font_get_language_support_overrides(RID p_font) { - ERR_FAIL_COND_V(interface == nullptr, Vector()); - godot_packed_string_array result = interface->font_get_language_support_overrides(data, (godot_rid *)&p_font); - Vector ret = *(Vector *)&result; - godot_packed_string_array_destroy(&result); - return ret; -} - -bool TextServerGDNative::font_is_script_supported(RID p_font, const String &p_script) const { - ERR_FAIL_COND_V(interface == nullptr, false); - return interface->font_is_script_supported(data, (godot_rid *)&p_font, (godot_string *)&p_script); -} - -void TextServerGDNative::font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) { +void TextServerGDNative::font_set_antialiased(RID p_font_rid, bool p_antialiased) { 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_antialiased(data, (godot_rid *)&p_font_rid, p_antialiased); } -bool TextServerGDNative::font_get_script_support_override(RID p_font, const String &p_script) { +bool TextServerGDNative::font_is_antialiased(RID p_font_rid) const { 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_is_antialiased(data, (godot_rid *)&p_font_rid); } -void TextServerGDNative::font_remove_script_support_override(RID p_font, const String &p_script) { +void TextServerGDNative::font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) { ERR_FAIL_COND(interface == nullptr); - interface->font_remove_script_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_script); + interface->font_set_multichannel_signed_distance_field(data, (godot_rid *)&p_font_rid, p_msdf); } -Vector TextServerGDNative::font_get_script_support_overrides(RID p_font) { - ERR_FAIL_COND_V(interface == nullptr, Vector()); - godot_packed_string_array result = interface->font_get_script_support_overrides(data, (godot_rid *)&p_font); - Vector ret = *(Vector *)&result; - godot_packed_string_array_destroy(&result); - return ret; +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); } -uint32_t TextServerGDNative::font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector) const { +void TextServerGDNative::font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_msdf_pixel_range(data, (godot_rid *)&p_font_rid, p_msdf_pixel_range); +} + +int TextServerGDNative::font_get_msdf_pixel_range(RID p_font_rid) const { ERR_FAIL_COND_V(interface == nullptr, 0); - return interface->font_get_glyph_index(data, (godot_rid *)&p_font, p_char, p_variation_selector); + return interface->font_get_msdf_pixel_range(data, (godot_rid *)&p_font_rid); } -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; -} - -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; -} - -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; -} - -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; -} - -bool TextServerGDNative::font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector &r_points, Vector &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); -} - -float TextServerGDNative::font_get_oversampling() const { - ERR_FAIL_COND_V(interface == nullptr, 1.f); - return interface->font_get_oversampling(data); -} - -void TextServerGDNative::font_set_oversampling(float p_oversampling) { +void TextServerGDNative::font_set_msdf_size(RID p_font_rid, int p_msdf_size) { ERR_FAIL_COND(interface == nullptr); - return interface->font_set_oversampling(data, p_oversampling); + interface->font_set_msdf_size(data, (godot_rid *)&p_font_rid, p_msdf_size); } -Vector TextServerGDNative::get_system_fonts() const { - ERR_FAIL_COND_V(interface == nullptr, Vector()); - godot_packed_string_array result = interface->get_system_fonts(data); - Vector fonts = *(Vector *)&result; +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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +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_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); +} + +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(data, (godot_rid *)&p_font_rid, p_size, (godot_int)p_spacing); +} + +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_clear_textures(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size); +} + +void TextServerGDNative::font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) { + ERR_FAIL_COND(interface == nullptr); + interface->font_remove_texture(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_texture_index); +} + +void TextServerGDNative::font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref &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()); +} + +Ref 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()); + 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 *)result); +} + +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_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) { + ERR_FAIL_COND(interface == nullptr); + interface->font_remove_glyph(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph); +} + +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_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_glyph_advance(data, (godot_rid *)&p_font_rid, p_size, p_glyph, (const godot_vector2 *)&p_advance); +} + +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; +} + +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); +} + +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_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_glyph_size(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph, (const godot_vector2 *)&p_size); +} + +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_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_glyph_uv_rect(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph, (const godot_rect2 *)&p_uv_rect); +} + +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_get_glyph_contours(RID p_font_rid, int p_size, int32_t p_index, Vector &r_points, Vector &r_contours, bool &r_orientation) const { + ERR_FAIL_COND_V(interface == nullptr, false); + 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); +} + +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; +} + +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); +} + +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; +} + +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_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_render_range(RID p_font_rid, const Vector2i &p_size, char32_t p_start, char32_t p_end) { + ERR_FAIL_COND(interface == nullptr); + interface->font_render_range(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_start, p_end); +} + +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_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_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); +} + +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_language_supported(RID p_font_rid, const String &p_language) const { + ERR_FAIL_COND_V(interface == nullptr, false); + return interface->font_is_language_supported(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_language); +} + +void TextServerGDNative::font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_language_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_language, p_supported); +} + +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_language_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_language); +} + +void TextServerGDNative::font_remove_language_support_override(RID p_font_rid, const String &p_language) { + ERR_FAIL_COND(interface == nullptr); + interface->font_remove_language_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_language); +} + +Vector 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 fonts; + return list; +} + +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); +} + +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); +} + +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); +} + +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); +} + +Vector 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; +} + +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; +} + +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; +} + +real_t TextServerGDNative::font_get_global_oversampling() const { + ERR_FAIL_COND_V(interface == nullptr, 0.0f); + return interface->font_get_global_oversampling(data); +} + +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 &p_tab_stops) { +real_t TextServerGDNative::shaped_text_tab_align(RID p_shaped, const Vector &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 TextServerGDNative::shaped_text_sort_logical(RID p_sha return glyphs; } -Vector TextServerGDNative::shaped_text_get_line_breaks_adv(RID p_shaped, const Vector &p_width, int p_start, bool p_once, uint8_t p_break_flags) const { +Vector TextServerGDNative::shaped_text_get_line_breaks_adv(RID p_shaped, const Vector &p_width, int p_start, bool p_once, uint8_t p_break_flags) const { ERR_FAIL_COND_V(interface == nullptr, Vector()); 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 TextServerGDNative::shaped_text_get_line_breaks_adv(RID p_shape } } -Vector TextServerGDNative::shaped_text_get_line_breaks(RID p_shaped, float p_width, int p_start, uint8_t p_break_flags) const { +Vector 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()); 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_bitmap_add_texture(RID p_font, const Ref &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_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 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_antialiased(RID p_font_rid, bool p_antialiased) override; + virtual bool font_is_antialiased(RID p_font_rid) 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_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 int font_get_spacing_space(RID p_font) const override; - virtual void font_set_spacing_space(RID p_font, int p_value) 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 int font_get_spacing_glyph(RID p_font) const override; - virtual void font_set_spacing_glyph(RID p_font, int p_value) 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_antialiased(RID p_font, bool p_antialiased) override; - virtual bool font_get_antialiased(RID p_font) 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 Dictionary font_get_feature_list(RID p_font) const override; - virtual Dictionary font_get_variation_list(RID p_font) 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_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 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_hinting(RID p_font, Hinting p_hinting) override; - virtual Hinting font_get_hinting(RID p_font) 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_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_oversampling(RID p_font_rid, real_t p_oversampling) override; + virtual real_t font_get_oversampling(RID p_font_rid) 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_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 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_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 bool font_has_outline(RID p_font) const override; - virtual float font_get_base_size(RID p_font) 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 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 font_get_language_support_overrides(RID p_font) 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 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 font_get_script_support_overrides(RID p_font) 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 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_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 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 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 bool font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector &r_points, Vector &r_contours, bool &r_orientation) 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 float font_get_oversampling() const override; - virtual void font_set_oversampling(float p_oversampling) override; + virtual void font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref &p_image) override; + virtual Ref font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const override; - virtual Vector get_system_fonts() 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 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 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 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 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 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 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 bool font_get_glyph_contours(RID p_font, int p_size, int32_t p_index, Vector &r_points, Vector &r_contours, bool &r_orientation) 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_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 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_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 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 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 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 font_get_language_support_overrides(RID p_font_rid) 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 font_get_script_support_overrides(RID p_font_rid) 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 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 &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 &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 shaped_text_sort_logical(RID p_shaped) override; - virtual Vector shaped_text_get_line_breaks_adv(RID p_shaped, const Vector &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 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 shaped_text_get_line_breaks_adv(RID p_shaped, const Vector &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 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 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(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(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(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(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(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(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(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(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(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 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 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 &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::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> 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 char_map; - Map 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 &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::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_vars; - Vector 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(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(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 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::Element *E = size_cache.front(); E; E = E->next()) { - memdelete(E->get()); - } - size_cache.clear(); - for (Map::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(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(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(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(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(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(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(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(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(this)->get_data_for_size(base_size); - ERR_FAIL_COND_V(fds == nullptr, false); - - const_cast(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(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(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, Vector2()); - - const_cast(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(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(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, Vector2()); - - const_cast(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(this)->get_data_for_size(p_size, p_outline_size); - ERR_FAIL_COND_V(fds == nullptr, Vector2()); - - const_cast(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 &r_points, Vector &r_contours, bool &r_orientation) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast(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 -#include FT_FREETYPE_H -#include FT_TRUETYPE_TABLES_H - -#include -#include - -struct DynamicFontDataAdvanced : public FontDataAdvanced { - _THREAD_SAFE_CLASS_ - -private: - struct CharTexture { - Vector imgdata; - int texture_size = 0; - Vector offsets; - Ref 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 textures; - HashMap 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 font_mem_cache; - - Map 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 size_cache; - Map 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 &r_points, Vector &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 - -struct FontDataAdvanced { - Map lang_support_overrides; - Map 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 &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 &r_points, Vector &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(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(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(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(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(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(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(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(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(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; -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_FREETYPE_ENABLED - } else if (p_filename.get_extension() == "ttf" || p_filename.get_extension() == "otf" || p_filename.get_extension() == "woff") { - fd = memnew(DynamicFontDataAdvanced); -#endif - } else { - return RID(); - } + int mw = p_width; + int mh = p_height; - Error err = fd->load_from_file(p_filename, p_base_size); - if (err != OK) { - memdelete(fd); - return RID(); - } + for (int i = 0; i < p_data->textures.size(); i++) { + const FontTexture &ct = p_data->textures[i]; - return font_owner.make_rid(fd); -} - -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); -#ifdef MODULE_FREETYPE_ENABLED - } else if (p_type == "ttf" || p_type == "otf" || p_type == "woff") { - fd = memnew(DynamicFontDataAdvanced); -#endif - } else { - return RID(); - } - - Error err = fd->load_from_memory(p_data, p_size, p_base_size); - if (err != OK) { - memdelete(fd); - return RID(); - } - - 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(); - } - - return font_owner.make_rid(fd); -} - -void TextServerAdvanced::font_bitmap_add_texture(RID p_font, const Ref &p_texture) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND(!fd); - fd->bitmap_add_texture(p_texture); -} - -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); - ERR_FAIL_COND(!fd); - fd->bitmap_add_char(p_char, p_texture_idx, p_rect, p_align, p_advance); -} - -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); - ERR_FAIL_COND(!fd); - fd->bitmap_add_kerning_pair(p_A, p_B, p_kerning); -} - -float TextServerAdvanced::font_get_height(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_height(p_size); -} - -float TextServerAdvanced::font_get_ascent(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_ascent(p_size); -} - -float TextServerAdvanced::font_get_descent(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_descent(p_size); -} - -float TextServerAdvanced::font_get_underline_position(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_underline_position(p_size); -} - -float TextServerAdvanced::font_get_underline_thickness(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_underline_thickness(p_size); -} - -int TextServerAdvanced::font_get_spacing_space(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_space(); -} - -void TextServerAdvanced::font_set_spacing_space(RID p_font, int p_value) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND(!fd); - fd->set_spacing_space(p_value); -} - -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_set_spacing_glyph(RID p_font, int p_value) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND(!fd); - fd->set_spacing_glyph(p_value); -} - -void TextServerAdvanced::font_set_antialiased(RID p_font, bool p_antialiased) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND(!fd); - fd->set_antialiased(p_antialiased); -} - -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(); -} - -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(); -} - -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_set_variation(RID p_font, const String &p_name, double p_value) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND(!fd); - fd->set_variation(p_name, p_value); -} - -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); -} - -void TextServerAdvanced::font_set_distance_field_hint(RID p_font, bool p_distance_field) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND(!fd); - fd->set_distance_field_hint(p_distance_field); -} - -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(); -} - -void TextServerAdvanced::font_set_hinting(RID p_font, TextServer::Hinting p_hinting) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND(!fd); - fd->set_hinting(p_hinting); -} - -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(); -} - -void TextServerAdvanced::font_set_force_autohinter(RID p_font, bool p_enabeld) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND(!fd); - fd->set_force_autohinter(p_enabeld); -} - -bool TextServerAdvanced::font_get_force_autohinter(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, false); - return fd->get_force_autohinter(); -} - -bool TextServerAdvanced::font_has_char(RID p_font, char32_t p_char) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, false); - return fd->has_char(p_char); -} - -String TextServerAdvanced::font_get_supported_chars(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, String()); - return fd->get_supported_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(); -} - -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(); -} - -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 tags = p_language.replace("-", "_").split("_"); - if (tags.size() > 0) { - if (fd->lang_support_overrides.has(tags[0])) { - return fd->lang_support_overrides[tags[0]]; + if (RenderingServer::get_singleton() != nullptr) { + if (ct.texture->get_format() != p_image_format) { + continue; } } - return fd->is_lang_supported(p_language); + + 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; +} + +#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 *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(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(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(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(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> 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); } } -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); - ERR_FAIL_COND(!fd); - fd->lang_support_overrides[p_language] = p_supported; +_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 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 = ℑ + 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 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 +_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 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 + +/*************************************************************************/ +/* 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; + } + + if (p_glyph == 0) { // Non graphical or invalid glyph, do not render. + fd->glyph_map[p_glyph] = FontGlyph(); + return true; + } + +#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; } -bool TextServerAdvanced::font_get_language_support_override(RID p_font, const String &p_language) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +_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 + 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_vars; + Vector 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 { + // 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; +} + +_FORCE_INLINE_ void TextServerAdvanced::_font_clear_cache(FontDataAdvanced *p_font_data) { + for (const Map::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); +} + +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; + } +} + +bool TextServerAdvanced::font_is_antialiased(RID p_font_rid) 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); + return fd->antialiased; } -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_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->lang_support_overrides.erase(p_language); + + MutexLock lock(fd->mutex); + if (fd->msdf != p_msdf) { + _font_clear_cache(fd); + fd->msdf = p_msdf; + } } -Vector TextServerAdvanced::font_get_language_support_overrides(RID p_font) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, Vector()); - Vector ret; - for (Map::Element *E = fd->lang_support_overrides.front(); E; E = E->next()) { +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); + + MutexLock lock(fd->mutex); + if (fd->msdf_range != p_msdf_pixel_range) { + _font_clear_cache(fd); + fd->msdf_range = p_msdf_pixel_range; + } +} + +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); + + MutexLock lock(fd->mutex); + if (fd->msdf_source_size != p_msdf_size) { + _font_clear_cache(fd); + fd->msdf_source_size = p_msdf_size; + } +} + +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::Element *E = fd->cache.front(); E; E = E->next()) { ret.push_back(E->key()); } return ret; } -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); +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::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); + + 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; + } +} + +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); + + 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; + } +} + +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); + + 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; + } +} + +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); + + 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; + } +} + +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); + + 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 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); + + 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 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); + 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 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_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref &p_image) { + FontDataAdvanced *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 img = memnew(Image(tex.texture_w, tex.texture_h, 0, tex.format, tex.imgdata)); + tex.texture = Ref(); + tex.texture.instantiate(); + tex.texture->create_from_image(img); +} + +Ref 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()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Ref()); + ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), Ref()); + + const FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + Ref 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); + + 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; +} + +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; +} + +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 &gl = fd->cache[size]->glyph_map; + const int32_t *E = nullptr; + while ((E = gl.next(E))) { + ret.push_back(*E); + } + return ret; +} + +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_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); + + 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); +} + +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 &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_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); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].advance = p_advance; + gl[p_glyph].found = true; +} + +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 &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_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); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].rect.position = p_offset; + gl[p_glyph].found = true; +} + +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 &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_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); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].rect.size = p_gl_size; + gl[p_glyph].found = true; +} + +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 &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 &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 &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 &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 &r_points, Vector &r_contours, bool &r_orientation) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, false); + + 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; +} + +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::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 &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); + + 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_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, String()); + + 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 &gl = at_size->glyph_map; + const int32_t *E = nullptr; + while ((E = gl.next(E))) { + chars += char32_t(*E); + } + } + return chars; +} + +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); + } +} + +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)); +} + +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); + } + } + } + } +} + +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); + + 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_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); + + 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_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); + + MutexLock lock(fd->mutex); + fd->language_support_overrides[p_language] = p_supported; +} + +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 TextServerAdvanced::font_get_language_support_overrides(RID p_font_rid) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Vector()); + + MutexLock lock(fd->mutex); + Vector out; + for (const Map::Element *E = fd->language_support_overrides.front(); E; E = E->next()) { + out.push_back(E->key()); + } + return out; +} + +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 TextServerAdvanced::font_get_script_support_overrides(RID p_font) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +Vector TextServerAdvanced::font_get_script_support_overrides(RID p_font_rid) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, Vector()); - Vector ret; - for (Map::Element *E = fd->script_support_overrides.front(); E; E = E->next()) { - ret.push_back(E->key()); + + MutexLock lock(fd->mutex); + Vector out; + for (const Map::Element *E = fd->script_support_overrides.front(); E; E = E->next()) { + out.push_back(E->key()); } - return ret; + return out; } -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); +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()); + + 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_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_variation_list(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Dictionary()); + + 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; } -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); -} - -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); -} - -bool TextServerAdvanced::font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector &r_points, Vector &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); -} - -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 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 text_bufs; - shaped_owner.get_owned_list(&text_bufs); - for (const RID &E : text_bufs) { - invalidate(shaped_owner.getornull(E)); + if (font_cleared) { + List text_bufs; + shaped_owner.get_owned_list(&text_bufs); + for (const RID &E : text_bufs) { + invalidate(shaped_owner.getornull(E)); + } } } } -Vector TextServerAdvanced::get_system_fonts() const { - return Vector(); -} - /*************************************************************************/ /* 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 &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 &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::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::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(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 &p_tab_stops) { - _THREAD_SAFE_METHOD_ +real_t TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector &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(this)->shaped_text_shape(p_shaped); } @@ -1679,7 +3554,7 @@ float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vectorpara_direction == DIRECTION_LTR) { @@ -1696,7 +3571,7 @@ float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vectorwidth += gl[i].advance - old_adv; off = 0; @@ -1716,10 +3591,11 @@ float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vectormutex); 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 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::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 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()); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast(this)->shaped_text_shape(p_shaped); } @@ -2631,16 +4510,18 @@ Vector 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 TextServerAdvanced::shaped_text_sort_logical(RID p_shaped) { - _THREAD_SAFE_METHOD_ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, Vector()); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast(this)->shaped_text_shape(p_shaped); } @@ -2655,10 +4536,11 @@ Vector 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::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(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(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(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(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(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(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(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 #include +#include "modules/modules_enabled.gen.h" + +#ifdef MODULE_FREETYPE_ENABLED +#include +#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 +#include +#endif + #include #include -#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 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 textures; + HashMap glyph_map; + Map 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 cache; + + bool face_init = false; + Set supported_scripts; + Dictionary supported_features; + Dictionary supported_varaitions; + + // Language/script support override. + Map language_support_overrides; + Map 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::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 font_owner; mutable RID_PtrOwner 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 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_bitmap_add_texture(RID p_font, const Ref &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_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 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_antialiased(RID p_font_rid, bool p_antialiased) override; + virtual bool font_is_antialiased(RID p_font_rid) 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_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 int font_get_spacing_space(RID p_font) const override; - virtual void font_set_spacing_space(RID p_font, int p_value) 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 int font_get_spacing_glyph(RID p_font) const override; - virtual void font_set_spacing_glyph(RID p_font, int p_value) 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_antialiased(RID p_font, bool p_antialiased) override; - virtual bool font_get_antialiased(RID p_font) 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 Dictionary font_get_feature_list(RID p_font) const override; - virtual Dictionary font_get_variation_list(RID p_font) 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_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 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_hinting(RID p_font, Hinting p_hinting) override; - virtual Hinting font_get_hinting(RID p_font) 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_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_oversampling(RID p_font_rid, real_t p_oversampling) override; + virtual real_t font_get_oversampling(RID p_font_rid) 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_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 bool font_has_char(RID p_font, char32_t p_char) const override; - virtual String font_get_supported_chars(RID p_font) const override; + hb_font_t *_font_get_hb_handle(RID p_font, int p_font_size) const; - virtual bool font_has_outline(RID p_font) const override; - virtual float font_get_base_size(RID p_font) const 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 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 font_get_language_support_overrides(RID p_font) 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 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 font_get_script_support_overrides(RID p_font) 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 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_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 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 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 bool font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector &r_points, Vector &r_contours, bool &r_orientation) 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 float font_get_oversampling() const override; - virtual void font_set_oversampling(float p_oversampling) 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 Vector get_system_fonts() const override; + virtual void font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref &p_image) override; + virtual Ref font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) 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 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 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 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 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 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 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 bool font_get_glyph_contours(RID p_font, int p_size, int32_t p_index, Vector &r_points, Vector &r_contours, bool &r_orientation) 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_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 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_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 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 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 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 font_get_language_support_overrides(RID p_font_rid) 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 font_get_script_support_overrides(RID p_font_rid) 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 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 &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 &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 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 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 &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::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> 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 char_map; - Map 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 &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::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 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::Element *E = size_cache.front(); E; E = E->next()) { - memdelete(E->get()); - } - size_cache.clear(); - for (Map::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(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(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(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(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(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(this)->get_data_for_size(base_size); - ERR_FAIL_COND_V(fds == nullptr, false); - - const_cast(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(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(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, Vector2()); - - const_cast(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(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(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, Vector2()); - - const_cast(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(this)->get_data_for_size(p_size, p_outline_size); - ERR_FAIL_COND_V(fds == nullptr, Vector2()); - - const_cast(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 &r_points, Vector &r_contours, bool &r_orientation) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast(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 -#include FT_FREETYPE_H - -struct DynamicFontDataFallback : public FontDataFallback { - _THREAD_SAFE_CLASS_ - -private: - struct CharTexture { - Vector imgdata; - int texture_size = 0; - Vector offsets; - Ref 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 textures; - HashMap 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 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 size_cache; - Map 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 &r_points, Vector &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 lang_support_overrides; - Map 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 &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 &r_points, Vector &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; -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_FREETYPE_ENABLED - } else if (p_filename.get_extension() == "ttf" || p_filename.get_extension() == "otf" || p_filename.get_extension() == "woff") { - fd = memnew(DynamicFontDataFallback); -#endif - } else { - return RID(); - } + int mw = p_width; + int mh = p_height; - Error err = fd->load_from_file(p_filename, p_base_size); - if (err != OK) { - memdelete(fd); - return RID(); - } + for (int i = 0; i < p_data->textures.size(); i++) { + const FontTexture &ct = p_data->textures[i]; - return font_owner.make_rid(fd); -} - -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); -#ifdef MODULE_FREETYPE_ENABLED - } else if (p_type == "ttf" || p_type == "otf" || p_type == "woff") { - fd = memnew(DynamicFontDataFallback); -#endif - } else { - return RID(); - } - - Error err = fd->load_from_memory(p_data, p_size, p_base_size); - if (err != OK) { - memdelete(fd); - return RID(); - } - - 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(); - } - - return font_owner.make_rid(fd); -} - -void TextServerFallback::font_bitmap_add_texture(RID p_font, const Ref &p_texture) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND(!fd); - fd->bitmap_add_texture(p_texture); -} - -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); - ERR_FAIL_COND(!fd); - fd->bitmap_add_char(p_char, p_texture_idx, p_rect, p_align, p_advance); -} - -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); - ERR_FAIL_COND(!fd); - fd->bitmap_add_kerning_pair(p_A, p_B, p_kerning); -} - -float TextServerFallback::font_get_height(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_height(p_size); -} - -float TextServerFallback::font_get_ascent(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_ascent(p_size); -} - -float TextServerFallback::font_get_descent(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_descent(p_size); -} - -float TextServerFallback::font_get_underline_position(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_underline_position(p_size); -} - -float TextServerFallback::font_get_underline_thickness(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_underline_thickness(p_size); -} - -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_spacing_space(RID p_font, int p_value) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND(!fd); - fd->set_spacing_space(p_value); -} - -int TextServerFallback::font_get_spacing_glyph(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_glyph(); -} - -void TextServerFallback::font_set_spacing_glyph(RID p_font, int p_value) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND(!fd); - fd->set_spacing_glyph(p_value); -} - -void TextServerFallback::font_set_antialiased(RID p_font, bool p_antialiased) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND(!fd); - fd->set_antialiased(p_antialiased); -} - -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_distance_field_hint(RID p_font, bool p_distance_field) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND(!fd); - fd->set_distance_field_hint(p_distance_field); -} - -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(); -} - -void TextServerFallback::font_set_hinting(RID p_font, TextServer::Hinting p_hinting) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND(!fd); - fd->set_hinting(p_hinting); -} - -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_set_force_autohinter(RID p_font, bool p_enabeld) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND(!fd); - fd->set_force_autohinter(p_enabeld); -} - -bool TextServerFallback::font_get_force_autohinter(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, false); - return fd->get_force_autohinter(); -} - -bool TextServerFallback::font_has_char(RID p_font, char32_t p_char) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, false); - return fd->has_char(p_char); -} - -String TextServerFallback::font_get_supported_chars(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, String()); - return fd->get_supported_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(); -} - -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(); -} - -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 tags = p_language.replace("-", "_").split("_"); - if (tags.size() > 0) { - if (fd->lang_support_overrides.has(tags[0])) { - return fd->lang_support_overrides[tags[0]]; + if (RenderingServer::get_singleton() != nullptr) { + if (ct.texture->get_format() != p_image_format) { + continue; } } - return false; + + 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; +} + +#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 *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(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(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(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(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> 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); } } -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); - ERR_FAIL_COND(!fd); - fd->lang_support_overrides[p_language] = p_supported; +_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 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 = ℑ + 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 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 +_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 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 + +/*************************************************************************/ +/* 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; + } + + if (p_glyph == 0) { // Non graphical or invalid glyph, do not render. + fd->glyph_map[p_glyph] = FontGlyph(); + return true; + } + +#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; } -bool TextServerFallback::font_get_language_support_override(RID p_font, const String &p_language) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +_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 + 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 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 + } + p_font_data->cache[p_size] = fd; + return true; +} + +_FORCE_INLINE_ void TextServerFallback::_font_clear_cache(FontDataFallback *p_font_data) { + for (const Map::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); +} + +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; + } +} + +bool TextServerFallback::font_is_antialiased(RID p_font_rid) 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); + return fd->antialiased; } -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_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->lang_support_overrides.erase(p_language); + + MutexLock lock(fd->mutex); + if (fd->msdf != p_msdf) { + _font_clear_cache(fd); + fd->msdf = p_msdf; + } } -Vector TextServerFallback::font_get_language_support_overrides(RID p_font) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, Vector()); - Vector ret; - for (Map::Element *E = fd->lang_support_overrides.front(); E; E = E->next()) { +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); + + MutexLock lock(fd->mutex); + if (fd->msdf_range != p_msdf_pixel_range) { + _font_clear_cache(fd); + fd->msdf_range = p_msdf_pixel_range; + } +} + +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); + + MutexLock lock(fd->mutex); + if (fd->msdf_source_size != p_msdf_size) { + _font_clear_cache(fd); + fd->msdf_source_size = p_msdf_size; + } +} + +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); + + MutexLock lock(fd->mutex); + return fd->oversampling; +} + +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::Element *E = fd->cache.front(); E; E = E->next()) { ret.push_back(E->key()); } return ret; } -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); +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::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); + + 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; + } +} + +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); + + 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; + } +} + +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); + + 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; + } +} + +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); + + 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; + } +} + +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; +} + +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); + + 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(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); + + 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_clear_textures(RID p_font_rid, const Vector2i &p_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)); + fd->cache[size]->textures.clear(); +} + +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); + + 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 TextServerFallback::font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref &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 img = memnew(Image(tex.texture_w, tex.texture_h, 0, tex.format, tex.imgdata)); + tex.texture = Ref(); + tex.texture.instantiate(); + tex.texture->create_from_image(img); +} + +Ref 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()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Ref()); + ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), Ref()); + + const FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + Ref 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); + + 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; +} + +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; +} + +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 &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); + + 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 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); +} + +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 &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); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].advance = p_advance; + gl[p_glyph].found = true; +} + +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 &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 &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 &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 &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 &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 &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 &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 &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 &r_points, Vector &r_contours, bool &r_orientation) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, false); + + 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; +} + +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::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 &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); + + 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_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, String()); + + 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 &gl = at_size->glyph_map; + const int32_t *E = nullptr; + while ((E = gl.next(E))) { + chars += char32_t(*E); + } + } + return chars; +} + +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); + } +} + +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)); +} + +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); + } + } + } + } +} + +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); + + 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_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); + + 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_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); + + MutexLock lock(fd->mutex); + fd->language_support_overrides[p_language] = p_supported; +} + +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 TextServerFallback::font_get_language_support_overrides(RID p_font_rid) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Vector()); + + MutexLock lock(fd->mutex); + Vector out; + for (const Map::Element *E = fd->language_support_overrides.front(); E; E = E->next()) { + out.push_back(E->key()); + } + return out; +} + +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,97 +1890,86 @@ 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 TextServerFallback::font_get_script_support_overrides(RID p_font) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +Vector TextServerFallback::font_get_script_support_overrides(RID p_font_rid) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, Vector()); - Vector ret; - for (Map::Element *E = fd->script_support_overrides.front(); E; E = E->next()) { - ret.push_back(E->key()); + + MutexLock lock(fd->mutex); + Vector out; + for (const Map::Element *E = fd->script_support_overrides.front(); E; E = E->next()) { + out.push_back(E->key()); } - return ret; + return out; } -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; +Dictionary TextServerFallback::font_supported_feature_list(RID p_font_rid) const { + return Dictionary(); } -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); +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()); + + 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; } -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); -} - -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); -} - -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); -} - -bool TextServerFallback::font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector &r_points, Vector &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 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; + } + } + + if (font_cleared) { + List text_bufs; + shaped_owner.get_owned_list(&text_bufs); + for (const RID &E : text_bufs) { + invalidate(shaped_owner.getornull(E)); + } } } } -Vector TextServerFallback::get_system_fonts() const { - return Vector(); -} - /*************************************************************************/ /* Shaped text buffer interface */ /*************************************************************************/ @@ -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 &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 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::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::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(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 &p_tab_stops) { - _THREAD_SAFE_METHOD_ +real_t TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector &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(this)->shaped_text_shape(p_shaped); } @@ -1081,7 +2579,7 @@ float TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vectorpara_direction == DIRECTION_LTR) { @@ -1098,7 +2596,7 @@ float TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vectorwidth += gl[i].advance - old_adv; off = 0; @@ -1119,9 +2617,10 @@ float TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vectormutex); 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::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 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()); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast(this)->shaped_text_shape(p_shaped); } @@ -1509,16 +3011,18 @@ Vector 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 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()); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast(this)->shaped_text_shape(p_shaped); } @@ -1527,10 +3031,11 @@ Vector 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::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(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(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(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(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(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(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(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 +#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 font_owner; - mutable RID_PtrOwner 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 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 textures; + HashMap glyph_map; + Map 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 cache; + + bool face_init = false; + Dictionary supported_varaitions; + + // Language/script support override. + Map language_support_overrides; + Map 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::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 font_owner; + mutable RID_PtrOwner 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_bitmap_add_texture(RID p_font, const Ref &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_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 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_antialiased(RID p_font_rid, bool p_antialiased) override; + virtual bool font_is_antialiased(RID p_font_rid) 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_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 int font_get_spacing_space(RID p_font) const override; - virtual void font_set_spacing_space(RID p_font, int p_value) 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 int font_get_spacing_glyph(RID p_font) const override; - virtual void font_set_spacing_glyph(RID p_font, int p_value) 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_antialiased(RID p_font, bool p_antialiased) override; - virtual bool font_get_antialiased(RID p_font) 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_hinting(RID p_font, Hinting p_hinting) override; - virtual Hinting font_get_hinting(RID p_font) 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_force_autohinter(RID p_font, bool p_enabeld) override; - virtual bool font_get_force_autohinter(RID p_font) 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 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_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_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_oversampling(RID p_font_rid, real_t p_oversampling) override; + virtual real_t font_get_oversampling(RID p_font_rid) const override; - virtual bool font_has_outline(RID p_font) const override; - virtual float font_get_base_size(RID p_font) 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 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 font_get_language_support_overrides(RID p_font) 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 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 font_get_script_support_overrides(RID p_font) 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 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_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 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 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 bool font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector &r_points, Vector &r_contours, bool &r_orientation) 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 float font_get_oversampling() const override; - virtual void font_set_oversampling(float p_oversampling) 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 Vector get_system_fonts() 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 &p_image) override; + virtual Ref font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) 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 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 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 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 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 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 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 bool font_get_glyph_contours(RID p_font, int p_size, int32_t p_index, Vector &r_points, Vector &r_contours, bool &r_orientation) 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_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 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_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 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 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 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 font_get_language_support_overrides(RID p_font_rid) 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 font_get_script_support_overrides(RID p_font_rid) 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 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 &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 &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 = 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 = 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 = 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 = 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 &p_font) { ERR_FAIL_INDEX(p_column, cells.size()); cells.write[p_column].custom_font = p_font; } + Ref TreeItem::get_custom_font(int p_column) const { ERR_FAIL_INDEX_V(p_column, cells.size(), Ref()); 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