diff options
Diffstat (limited to 'scene/resources/font.cpp')
-rw-r--r-- | scene/resources/font.cpp | 2315 |
1 files changed, 1278 insertions, 1037 deletions
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index efbe9c93f7..f61ac7fcaa 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -30,431 +30,566 @@ #include "font.h" +#include "core/core_string_names.h" #include "core/io/image_loader.h" #include "core/io/resource_loader.h" #include "core/string/translation.h" +#include "core/templates/hash_map.h" #include "core/templates/hashfuncs.h" #include "scene/resources/text_line.h" #include "scene/resources/text_paragraph.h" +#include "scene/resources/theme.h" -_FORCE_INLINE_ void FontData::_clear_cache() { - for (int i = 0; i < cache.size(); i++) { - if (cache[i].is_valid()) { - TS->free_rid(cache[i]); - cache.write[i] = RID(); - } - } +/*************************************************************************/ +/* Font */ +/*************************************************************************/ + +void Font::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_fallbacks", "fallbacks"), &Font::set_fallbacks); + ClassDB::bind_method(D_METHOD("get_fallbacks"), &Font::get_fallbacks); + + // Output. + ClassDB::bind_method(D_METHOD("find_variation", "variation_coordinates", "face_index", "strength", "transform"), &Font::find_variation, DEFVAL(0), DEFVAL(0.0), DEFVAL(Transform2D())); + ClassDB::bind_method(D_METHOD("get_rids"), &Font::get_rids); + + // Font metrics. + ClassDB::bind_method(D_METHOD("get_height", "font_size"), &Font::get_height, DEFVAL(DEFAULT_FONT_SIZE)); + ClassDB::bind_method(D_METHOD("get_ascent", "font_size"), &Font::get_ascent, DEFVAL(DEFAULT_FONT_SIZE)); + ClassDB::bind_method(D_METHOD("get_descent", "font_size"), &Font::get_descent, DEFVAL(DEFAULT_FONT_SIZE)); + ClassDB::bind_method(D_METHOD("get_underline_position", "font_size"), &Font::get_underline_position, DEFVAL(DEFAULT_FONT_SIZE)); + ClassDB::bind_method(D_METHOD("get_underline_thickness", "font_size"), &Font::get_underline_thickness, DEFVAL(DEFAULT_FONT_SIZE)); + + ClassDB::bind_method(D_METHOD("get_font_name"), &Font::get_font_name); + ClassDB::bind_method(D_METHOD("get_font_style_name"), &Font::get_font_style_name); + ClassDB::bind_method(D_METHOD("get_font_style"), &Font::get_font_style); + + ClassDB::bind_method(D_METHOD("get_spacing", "spacing"), &Font::get_spacing); + ClassDB::bind_method(D_METHOD("get_opentype_features"), &Font::get_opentype_features); + + // Drawing string. + ClassDB::bind_method(D_METHOD("set_cache_capacity", "single_line", "multi_line"), &Font::set_cache_capacity); + + ClassDB::bind_method(D_METHOD("get_string_size", "text", "alignment", "width", "font_size", "jst_flags", "direction", "orientation"), &Font::get_string_size, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("get_multiline_string_size", "text", "alignment", "width", "font_size", "max_lines", "brk_flags", "jst_flags", "direction", "orientation"), &Font::get_multiline_string_size, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + + ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "alignment", "width", "font_size", "modulate", "jst_flags", "direction", "orientation"), &Font::draw_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "alignment", "width", "font_size", "max_lines", "modulate", "brk_flags", "jst_flags", "direction", "orientation"), &Font::draw_multiline_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + + ClassDB::bind_method(D_METHOD("draw_string_outline", "canvas_item", "pos", "text", "alignment", "width", "font_size", "size", "modulate", "jst_flags", "direction", "orientation"), &Font::draw_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_multiline_string_outline", "canvas_item", "pos", "text", "alignment", "width", "font_size", "max_lines", "size", "modulate", "brk_flags", "jst_flags", "direction", "orientation"), &Font::draw_multiline_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + + // Drawing char. + ClassDB::bind_method(D_METHOD("get_char_size", "char"), &Font::get_char_size); + ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "modulate"), &Font::draw_char, DEFVAL(Color(1.0, 1.0, 1.0))); + ClassDB::bind_method(D_METHOD("draw_char_outline", "canvas_item", "pos", "char", "size", "modulate"), &Font::draw_char_outline, DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0))); + + // Helper functions. + ClassDB::bind_method(D_METHOD("has_char", "char"), &Font::has_char); + ClassDB::bind_method(D_METHOD("get_supported_chars"), &Font::get_supported_chars); + + ClassDB::bind_method(D_METHOD("is_language_supported", "language"), &Font::is_language_supported); + ClassDB::bind_method(D_METHOD("is_script_supported", "script"), &Font::is_script_supported); + + ClassDB::bind_method(D_METHOD("get_supported_feature_list"), &Font::get_supported_feature_list); + ClassDB::bind_method(D_METHOD("get_supported_variation_list"), &Font::get_supported_variation_list); + ClassDB::bind_method(D_METHOD("get_face_count"), &Font::get_face_count); } -_FORCE_INLINE_ void FontData::_ensure_rid(int p_cache_index) const { - if (unlikely(p_cache_index >= cache.size())) { - cache.resize(p_cache_index + 1); - } - if (unlikely(!cache[p_cache_index].is_valid())) { - cache.write[p_cache_index] = TS->create_font(); - TS->font_set_data_ptr(cache[p_cache_index], data_ptr, data_size); - TS->font_set_antialiased(cache[p_cache_index], antialiased); - TS->font_set_multichannel_signed_distance_field(cache[p_cache_index], msdf); - TS->font_set_msdf_pixel_range(cache[p_cache_index], msdf_pixel_range); - TS->font_set_msdf_size(cache[p_cache_index], msdf_size); - TS->font_set_fixed_size(cache[p_cache_index], fixed_size); - TS->font_set_force_autohinter(cache[p_cache_index], force_autohinter); - TS->font_set_hinting(cache[p_cache_index], hinting); - TS->font_set_subpixel_positioning(cache[p_cache_index], subpixel_positioning); - TS->font_set_embolden(cache[p_cache_index], embolden); - TS->font_set_transform(cache[p_cache_index], transform); - TS->font_set_oversampling(cache[p_cache_index], oversampling); +void Font::_update_rids_fb(const Ref<Font> &p_f, int p_depth) const { + ERR_FAIL_COND(p_depth > MAX_FALLBACK_DEPTH); + if (p_f.is_valid()) { + RID rid = p_f->_get_rid(); + if (rid.is_valid()) { + rids.push_back(rid); + } + const TypedArray<Font> &_fallbacks = p_f->get_fallbacks(); + for (int i = 0; i < _fallbacks.size(); i++) { + _update_rids_fb(_fallbacks[i], p_depth + 1); + } } } -void FontData::_bind_methods() { - ClassDB::bind_method(D_METHOD("load_bitmap_font", "path"), &FontData::load_bitmap_font); - ClassDB::bind_method(D_METHOD("load_dynamic_font", "path"), &FontData::load_dynamic_font); +void Font::_update_rids() const { + rids.clear(); + _update_rids_fb(const_cast<Font *>(this), 0); + dirty_rids = false; +} - ClassDB::bind_method(D_METHOD("set_data", "data"), &FontData::set_data); - ClassDB::bind_method(D_METHOD("get_data"), &FontData::get_data); +void Font::_invalidate_rids() { + rids.clear(); + dirty_rids = true; - ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontData::set_antialiased); - ClassDB::bind_method(D_METHOD("is_antialiased"), &FontData::is_antialiased); + cache.clear(); + cache_wrap.clear(); - ClassDB::bind_method(D_METHOD("set_font_name", "name"), &FontData::set_font_name); - ClassDB::bind_method(D_METHOD("get_font_name"), &FontData::get_font_name); + emit_changed(); +} - ClassDB::bind_method(D_METHOD("set_font_style_name", "name"), &FontData::set_font_style_name); - ClassDB::bind_method(D_METHOD("get_font_style_name"), &FontData::get_font_style_name); +bool Font::_is_cyclic(const Ref<Font> &p_f, int p_depth) const { + ERR_FAIL_COND_V(p_depth > MAX_FALLBACK_DEPTH, false); + if (p_f.is_null()) { + return false; + } + for (int i = 0; i < p_f->fallbacks.size(); i++) { + const Ref<Font> &f = p_f->fallbacks[i]; + if (f == this) { + return true; + } + return _is_cyclic(f, p_depth + 1); + } + return false; +} - ClassDB::bind_method(D_METHOD("set_font_style", "style"), &FontData::set_font_style); - ClassDB::bind_method(D_METHOD("get_font_style"), &FontData::get_font_style); +void Font::reset_state() { + _invalidate_rids(); +} - ClassDB::bind_method(D_METHOD("set_multichannel_signed_distance_field", "msdf"), &FontData::set_multichannel_signed_distance_field); - ClassDB::bind_method(D_METHOD("is_multichannel_signed_distance_field"), &FontData::is_multichannel_signed_distance_field); +// Fallbacks. +void Font::set_fallbacks(const TypedArray<Font> &p_fallbacks) { + ERR_FAIL_COND(_is_cyclic(this, 0)); + for (int i = 0; i < fallbacks.size(); i++) { + Ref<Font> f = fallbacks[i]; + if (f.is_valid()) { + f->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Font::_invalidate_rids)); + } + } + fallbacks = p_fallbacks; + for (int i = 0; i < fallbacks.size(); i++) { + Ref<Font> f = fallbacks[i]; + if (f.is_valid()) { + f->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); + } + } + _invalidate_rids(); +} - ClassDB::bind_method(D_METHOD("set_msdf_pixel_range", "msdf_pixel_range"), &FontData::set_msdf_pixel_range); - ClassDB::bind_method(D_METHOD("get_msdf_pixel_range"), &FontData::get_msdf_pixel_range); +TypedArray<Font> Font::get_fallbacks() const { + return fallbacks; +} - ClassDB::bind_method(D_METHOD("set_msdf_size", "msdf_size"), &FontData::set_msdf_size); - ClassDB::bind_method(D_METHOD("get_msdf_size"), &FontData::get_msdf_size); +// Output. +TypedArray<RID> Font::get_rids() const { + if (dirty_rids) { + _update_rids(); + } + return rids; +} - ClassDB::bind_method(D_METHOD("set_fixed_size", "fixed_size"), &FontData::set_fixed_size); - ClassDB::bind_method(D_METHOD("get_fixed_size"), &FontData::get_fixed_size); +// Drawing string. +real_t Font::get_height(int p_font_size) const { + if (dirty_rids) { + _update_rids(); + } + real_t ret = 0.f; + for (int i = 0; i < rids.size(); i++) { + ret = MAX(ret, TS->font_get_ascent(rids[i], p_font_size) + TS->font_get_descent(rids[i], p_font_size)); + } + return ret + get_spacing(TextServer::SPACING_BOTTOM) + get_spacing(TextServer::SPACING_TOP); +} - ClassDB::bind_method(D_METHOD("set_force_autohinter", "force_autohinter"), &FontData::set_force_autohinter); - ClassDB::bind_method(D_METHOD("is_force_autohinter"), &FontData::is_force_autohinter); +real_t Font::get_ascent(int p_font_size) const { + if (dirty_rids) { + _update_rids(); + } + real_t ret = 0.f; + for (int i = 0; i < rids.size(); i++) { + ret = MAX(ret, TS->font_get_ascent(rids[i], p_font_size)); + } + return ret + get_spacing(TextServer::SPACING_TOP); +} - ClassDB::bind_method(D_METHOD("set_hinting", "hinting"), &FontData::set_hinting); - ClassDB::bind_method(D_METHOD("get_hinting"), &FontData::get_hinting); +real_t Font::get_descent(int p_font_size) const { + if (dirty_rids) { + _update_rids(); + } + real_t ret = 0.f; + for (int i = 0; i < rids.size(); i++) { + ret = MAX(ret, TS->font_get_descent(rids[i], p_font_size)); + } + return ret + get_spacing(TextServer::SPACING_BOTTOM); +} - ClassDB::bind_method(D_METHOD("set_subpixel_positioning", "subpixel_positioning"), &FontData::set_subpixel_positioning); - ClassDB::bind_method(D_METHOD("get_subpixel_positioning"), &FontData::get_subpixel_positioning); +real_t Font::get_underline_position(int p_font_size) const { + if (dirty_rids) { + _update_rids(); + } + real_t ret = 0.f; + for (int i = 0; i < rids.size(); i++) { + ret = MAX(ret, TS->font_get_underline_position(rids[i], p_font_size)); + } + return ret + get_spacing(TextServer::SPACING_TOP); +} - ClassDB::bind_method(D_METHOD("set_embolden", "strength"), &FontData::set_embolden); - ClassDB::bind_method(D_METHOD("get_embolden"), &FontData::get_embolden); +real_t Font::get_underline_thickness(int p_font_size) const { + if (dirty_rids) { + _update_rids(); + } + real_t ret = 0.f; + for (int i = 0; i < rids.size(); i++) { + ret = MAX(ret, TS->font_get_underline_thickness(rids[i], p_font_size)); + } + return ret; +} - ClassDB::bind_method(D_METHOD("set_transform", "transform"), &FontData::set_transform); - ClassDB::bind_method(D_METHOD("get_transform"), &FontData::get_transform); +String Font::get_font_name() const { + return TS->font_get_name(_get_rid()); +} - ClassDB::bind_method(D_METHOD("set_oversampling", "oversampling"), &FontData::set_oversampling); - ClassDB::bind_method(D_METHOD("get_oversampling"), &FontData::get_oversampling); +String Font::get_font_style_name() const { + return TS->font_get_style_name(_get_rid()); +} - ClassDB::bind_method(D_METHOD("find_cache", "variation_coordinates"), &FontData::find_cache); +BitField<TextServer::FontStyle> Font::get_font_style() const { + return TS->font_get_style(_get_rid()); +} - ClassDB::bind_method(D_METHOD("get_cache_count"), &FontData::get_cache_count); - ClassDB::bind_method(D_METHOD("clear_cache"), &FontData::clear_cache); - ClassDB::bind_method(D_METHOD("remove_cache", "cache_index"), &FontData::remove_cache); +Dictionary Font::get_opentype_features() const { + return Dictionary(); +} - ClassDB::bind_method(D_METHOD("get_size_cache_list", "cache_index"), &FontData::get_size_cache_list); - ClassDB::bind_method(D_METHOD("clear_size_cache", "cache_index"), &FontData::clear_size_cache); - ClassDB::bind_method(D_METHOD("remove_size_cache", "cache_index", "size"), &FontData::remove_size_cache); +// Drawing string. +void Font::set_cache_capacity(int p_single_line, int p_multi_line) { + cache.set_capacity(p_single_line); + cache_wrap.set_capacity(p_multi_line); +} - ClassDB::bind_method(D_METHOD("set_variation_coordinates", "cache_index", "variation_coordinates"), &FontData::set_variation_coordinates); - ClassDB::bind_method(D_METHOD("get_variation_coordinates", "cache_index"), &FontData::get_variation_coordinates); +Size2 Font::get_string_size(const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + uint64_t hash = p_text.hash64(); + hash = hash_djb2_one_64(p_font_size, hash); + if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { + hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); + hash = hash_djb2_one_64(p_jst_flags.operator uint32_t(), hash); + } + hash = hash_djb2_one_64(p_direction, hash); + hash = hash_djb2_one_64(p_orientation, hash); - ClassDB::bind_method(D_METHOD("set_ascent", "cache_index", "size", "ascent"), &FontData::set_ascent); - ClassDB::bind_method(D_METHOD("get_ascent", "cache_index", "size"), &FontData::get_ascent); + Ref<TextLine> buffer; + if (cache.has(hash)) { + buffer = cache.get(hash); + } else { + buffer.instantiate(); + buffer->set_direction(p_direction); + buffer->set_orientation(p_orientation); + buffer->add_string(p_text, Ref<Font>(this), p_font_size); + if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { + buffer->set_horizontal_alignment(p_alignment); + buffer->set_width(p_width); + buffer->set_flags(p_jst_flags); + } + cache.insert(hash, buffer); + } + return buffer->get_size(); +} - ClassDB::bind_method(D_METHOD("set_descent", "cache_index", "size", "descent"), &FontData::set_descent); - ClassDB::bind_method(D_METHOD("get_descent", "cache_index", "size"), &FontData::get_descent); +Size2 Font::get_multiline_string_size(const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + uint64_t hash = p_text.hash64(); + hash = hash_djb2_one_64(p_font_size, hash); + hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); + hash = hash_djb2_one_64(p_brk_flags.operator uint32_t(), hash); + hash = hash_djb2_one_64(p_jst_flags.operator uint32_t(), hash); + hash = hash_djb2_one_64(p_direction, hash); + hash = hash_djb2_one_64(p_orientation, hash); - ClassDB::bind_method(D_METHOD("set_underline_position", "cache_index", "size", "underline_position"), &FontData::set_underline_position); - ClassDB::bind_method(D_METHOD("get_underline_position", "cache_index", "size"), &FontData::get_underline_position); + Ref<TextParagraph> lines_buffer; + if (cache_wrap.has(hash)) { + lines_buffer = cache_wrap.get(hash); + } else { + lines_buffer.instantiate(); + lines_buffer->set_direction(p_direction); + lines_buffer->set_orientation(p_orientation); + lines_buffer->add_string(p_text, Ref<Font>(this), p_font_size); + lines_buffer->set_width(p_width); + lines_buffer->set_break_flags(p_brk_flags); + lines_buffer->set_justification_flags(p_jst_flags); + cache_wrap.insert(hash, lines_buffer); + } - ClassDB::bind_method(D_METHOD("set_underline_thickness", "cache_index", "size", "underline_thickness"), &FontData::set_underline_thickness); - ClassDB::bind_method(D_METHOD("get_underline_thickness", "cache_index", "size"), &FontData::get_underline_thickness); + lines_buffer->set_alignment(p_alignment); + lines_buffer->set_max_lines_visible(p_max_lines); - ClassDB::bind_method(D_METHOD("set_scale", "cache_index", "size", "scale"), &FontData::set_scale); - ClassDB::bind_method(D_METHOD("get_scale", "cache_index", "size"), &FontData::get_scale); + return lines_buffer->get_size(); +} - ClassDB::bind_method(D_METHOD("set_spacing", "cache_index", "size", "spacing_type", "value"), &FontData::set_spacing); - ClassDB::bind_method(D_METHOD("get_spacing", "cache_index", "size", "spacing_type"), &FontData::get_spacing); +void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, const Color &p_modulate, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + uint64_t hash = p_text.hash64(); + hash = hash_djb2_one_64(p_font_size, hash); + if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { + hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); + hash = hash_djb2_one_64(p_jst_flags.operator uint32_t(), hash); + } + hash = hash_djb2_one_64(p_direction, hash); + hash = hash_djb2_one_64(p_orientation, hash); - ClassDB::bind_method(D_METHOD("get_texture_count", "cache_index", "size"), &FontData::get_texture_count); - ClassDB::bind_method(D_METHOD("clear_textures", "cache_index", "size"), &FontData::clear_textures); - ClassDB::bind_method(D_METHOD("remove_texture", "cache_index", "size", "texture_index"), &FontData::remove_texture); + Ref<TextLine> buffer; + if (cache.has(hash)) { + buffer = cache.get(hash); + } else { + buffer.instantiate(); + buffer->set_direction(p_direction); + buffer->set_orientation(p_orientation); + buffer->add_string(p_text, Ref<Font>(this), p_font_size); + cache.insert(hash, buffer); + } - ClassDB::bind_method(D_METHOD("set_texture_image", "cache_index", "size", "texture_index", "image"), &FontData::set_texture_image); - ClassDB::bind_method(D_METHOD("get_texture_image", "cache_index", "size", "texture_index"), &FontData::get_texture_image); + Vector2 ofs = p_pos; + if (p_orientation == TextServer::ORIENTATION_HORIZONTAL) { + ofs.y -= buffer->get_line_ascent(); + } else { + ofs.x -= buffer->get_line_ascent(); + } - ClassDB::bind_method(D_METHOD("set_texture_offsets", "cache_index", "size", "texture_index", "offset"), &FontData::set_texture_offsets); - ClassDB::bind_method(D_METHOD("get_texture_offsets", "cache_index", "size", "texture_index"), &FontData::get_texture_offsets); + buffer->set_width(p_width); + buffer->set_horizontal_alignment(p_alignment); + if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { + buffer->set_flags(p_jst_flags); + } - ClassDB::bind_method(D_METHOD("get_glyph_list", "cache_index", "size"), &FontData::get_glyph_list); - ClassDB::bind_method(D_METHOD("clear_glyphs", "cache_index", "size"), &FontData::clear_glyphs); - ClassDB::bind_method(D_METHOD("remove_glyph", "cache_index", "size", "glyph"), &FontData::remove_glyph); + buffer->draw(p_canvas_item, ofs, p_modulate); +} - ClassDB::bind_method(D_METHOD("set_glyph_advance", "cache_index", "size", "glyph", "advance"), &FontData::set_glyph_advance); - ClassDB::bind_method(D_METHOD("get_glyph_advance", "cache_index", "size", "glyph"), &FontData::get_glyph_advance); +void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, const Color &p_modulate, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + uint64_t hash = p_text.hash64(); + hash = hash_djb2_one_64(p_font_size, hash); + hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); + hash = hash_djb2_one_64(p_brk_flags.operator uint32_t(), hash); + hash = hash_djb2_one_64(p_jst_flags.operator uint32_t(), hash); + hash = hash_djb2_one_64(p_direction, hash); + hash = hash_djb2_one_64(p_orientation, hash); - ClassDB::bind_method(D_METHOD("set_glyph_offset", "cache_index", "size", "glyph", "offset"), &FontData::set_glyph_offset); - ClassDB::bind_method(D_METHOD("get_glyph_offset", "cache_index", "size", "glyph"), &FontData::get_glyph_offset); + Ref<TextParagraph> lines_buffer; + if (cache_wrap.has(hash)) { + lines_buffer = cache_wrap.get(hash); + } else { + lines_buffer.instantiate(); + lines_buffer->set_direction(p_direction); + lines_buffer->set_orientation(p_orientation); + lines_buffer->add_string(p_text, Ref<Font>(this), p_font_size); + lines_buffer->set_width(p_width); + lines_buffer->set_break_flags(p_brk_flags); + lines_buffer->set_justification_flags(p_jst_flags); + cache_wrap.insert(hash, lines_buffer); + } - ClassDB::bind_method(D_METHOD("set_glyph_size", "cache_index", "size", "glyph", "gl_size"), &FontData::set_glyph_size); - ClassDB::bind_method(D_METHOD("get_glyph_size", "cache_index", "size", "glyph"), &FontData::get_glyph_size); + Vector2 ofs = p_pos; + if (p_orientation == TextServer::ORIENTATION_HORIZONTAL) { + ofs.y -= lines_buffer->get_line_ascent(0); + } else { + ofs.x -= lines_buffer->get_line_ascent(0); + } - ClassDB::bind_method(D_METHOD("set_glyph_uv_rect", "cache_index", "size", "glyph", "uv_rect"), &FontData::set_glyph_uv_rect); - ClassDB::bind_method(D_METHOD("get_glyph_uv_rect", "cache_index", "size", "glyph"), &FontData::get_glyph_uv_rect); + lines_buffer->set_alignment(p_alignment); + lines_buffer->set_max_lines_visible(p_max_lines); - ClassDB::bind_method(D_METHOD("set_glyph_texture_idx", "cache_index", "size", "glyph", "texture_idx"), &FontData::set_glyph_texture_idx); - ClassDB::bind_method(D_METHOD("get_glyph_texture_idx", "cache_index", "size", "glyph"), &FontData::get_glyph_texture_idx); + lines_buffer->draw(p_canvas_item, ofs, p_modulate); +} - ClassDB::bind_method(D_METHOD("get_kerning_list", "cache_index", "size"), &FontData::get_kerning_list); - ClassDB::bind_method(D_METHOD("clear_kerning_map", "cache_index", "size"), &FontData::clear_kerning_map); - ClassDB::bind_method(D_METHOD("remove_kerning", "cache_index", "size", "glyph_pair"), &FontData::remove_kerning); +void Font::draw_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_size, const Color &p_modulate, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + uint64_t hash = p_text.hash64(); + hash = hash_djb2_one_64(p_font_size, hash); + if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { + hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); + hash = hash_djb2_one_64(p_jst_flags.operator uint32_t(), hash); + } + hash = hash_djb2_one_64(p_direction, hash); + hash = hash_djb2_one_64(p_orientation, hash); - ClassDB::bind_method(D_METHOD("set_kerning", "cache_index", "size", "glyph_pair", "kerning"), &FontData::set_kerning); - ClassDB::bind_method(D_METHOD("get_kerning", "cache_index", "size", "glyph_pair"), &FontData::get_kerning); + Ref<TextLine> buffer; + if (cache.has(hash)) { + buffer = cache.get(hash); + } else { + buffer.instantiate(); + buffer->set_direction(p_direction); + buffer->set_orientation(p_orientation); + buffer->add_string(p_text, Ref<Font>(this), p_font_size); + cache.insert(hash, buffer); + } - ClassDB::bind_method(D_METHOD("render_range", "cache_index", "size", "start", "end"), &FontData::render_range); - ClassDB::bind_method(D_METHOD("render_glyph", "cache_index", "size", "index"), &FontData::render_glyph); + Vector2 ofs = p_pos; + if (p_orientation == TextServer::ORIENTATION_HORIZONTAL) { + ofs.y -= buffer->get_line_ascent(); + } else { + ofs.x -= buffer->get_line_ascent(); + } - ClassDB::bind_method(D_METHOD("get_cache_rid", "cache_index"), &FontData::get_cache_rid); + buffer->set_width(p_width); + buffer->set_horizontal_alignment(p_alignment); + if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { + buffer->set_flags(p_jst_flags); + } - ClassDB::bind_method(D_METHOD("is_language_supported", "language"), &FontData::is_language_supported); - ClassDB::bind_method(D_METHOD("set_language_support_override", "language", "supported"), &FontData::set_language_support_override); - ClassDB::bind_method(D_METHOD("get_language_support_override", "language"), &FontData::get_language_support_override); - ClassDB::bind_method(D_METHOD("remove_language_support_override", "language"), &FontData::remove_language_support_override); - ClassDB::bind_method(D_METHOD("get_language_support_overrides"), &FontData::get_language_support_overrides); + buffer->draw_outline(p_canvas_item, ofs, p_size, p_modulate); +} - ClassDB::bind_method(D_METHOD("is_script_supported", "script"), &FontData::is_script_supported); - ClassDB::bind_method(D_METHOD("set_script_support_override", "script", "supported"), &FontData::set_script_support_override); - ClassDB::bind_method(D_METHOD("get_script_support_override", "script"), &FontData::get_script_support_override); - ClassDB::bind_method(D_METHOD("remove_script_support_override", "script"), &FontData::remove_script_support_override); - ClassDB::bind_method(D_METHOD("get_script_support_overrides"), &FontData::get_script_support_overrides); +void Font::draw_multiline_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, int p_size, const Color &p_modulate, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + uint64_t hash = p_text.hash64(); + hash = hash_djb2_one_64(p_font_size, hash); + hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); + hash = hash_djb2_one_64(p_brk_flags.operator uint32_t(), hash); + hash = hash_djb2_one_64(p_jst_flags.operator uint32_t(), hash); + hash = hash_djb2_one_64(p_direction, hash); + hash = hash_djb2_one_64(p_orientation, hash); - ClassDB::bind_method(D_METHOD("set_opentype_feature_overrides", "overrides"), &FontData::set_opentype_feature_overrides); - ClassDB::bind_method(D_METHOD("get_opentype_feature_overrides"), &FontData::get_opentype_feature_overrides); + Ref<TextParagraph> lines_buffer; + if (cache_wrap.has(hash)) { + lines_buffer = cache_wrap.get(hash); + } else { + lines_buffer.instantiate(); + lines_buffer->set_direction(p_direction); + lines_buffer->set_orientation(p_orientation); + lines_buffer->add_string(p_text, Ref<Font>(this), p_font_size); + lines_buffer->set_width(p_width); + lines_buffer->set_break_flags(p_brk_flags); + lines_buffer->set_justification_flags(p_jst_flags); + cache_wrap.insert(hash, lines_buffer); + } - ClassDB::bind_method(D_METHOD("has_char", "char"), &FontData::has_char); - ClassDB::bind_method(D_METHOD("get_supported_chars"), &FontData::get_supported_chars); + Vector2 ofs = p_pos; + if (p_orientation == TextServer::ORIENTATION_HORIZONTAL) { + ofs.y -= lines_buffer->get_line_ascent(0); + } else { + ofs.x -= lines_buffer->get_line_ascent(0); + } - ClassDB::bind_method(D_METHOD("get_glyph_index", "size", "char", "variation_selector"), &FontData::get_glyph_index); + lines_buffer->set_alignment(p_alignment); + lines_buffer->set_max_lines_visible(p_max_lines); - ClassDB::bind_method(D_METHOD("get_supported_feature_list"), &FontData::get_supported_feature_list); - ClassDB::bind_method(D_METHOD("get_supported_variation_list"), &FontData::get_supported_variation_list); + lines_buffer->draw_outline(p_canvas_item, ofs, p_size, p_modulate); +} - ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_data", "get_data"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_antialiased", "is_antialiased"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "font_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_name", "get_font_name"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "style_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_style_name", "get_font_style_name"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_style", "get_font_style"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel", PROPERTY_USAGE_STORAGE), "set_subpixel_positioning", "get_subpixel_positioning"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "embolden", PROPERTY_HINT_RANGE, "-2,2,0.01", PROPERTY_USAGE_STORAGE), "set_embolden", "get_embolden"); - ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_transform", "get_transform"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_multichannel_signed_distance_field", "is_multichannel_signed_distance_field"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_pixel_range", "get_msdf_pixel_range"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_size", "get_msdf_size"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_force_autohinter", "is_force_autohinter"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_STORAGE), "set_hinting", "get_hinting"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_oversampling", "get_oversampling"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_fixed_size", "get_fixed_size"); - ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "opentype_feature_overrides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_opentype_feature_overrides", "get_opentype_feature_overrides"); +// Drawing char. +Size2 Font::get_char_size(char32_t p_char, int p_font_size) const { + if (dirty_rids) { + _update_rids(); + } + for (int i = 0; i < rids.size(); i++) { + if (TS->font_has_char(rids[i], p_char)) { + int32_t glyph = TS->font_get_glyph_index(rids[i], p_font_size, p_char, 0); + return Size2(TS->font_get_glyph_advance(rids[i], p_font_size, glyph).x, get_height(p_font_size)); + } + } + return Size2(); } -bool FontData::_set(const StringName &p_name, const Variant &p_value) { - Vector<String> tokens = p_name.operator String().split("/"); - if (tokens.size() == 2 && tokens[0] == "language_support_override") { - String lang = tokens[1]; - set_language_support_override(lang, p_value); - return true; - } else if (tokens.size() == 2 && tokens[0] == "script_support_override") { - String script = tokens[1]; - set_script_support_override(script, p_value); - return true; - } else if (tokens.size() >= 3 && tokens[0] == "cache") { - int cache_index = tokens[1].to_int(); - if (tokens.size() == 3 && tokens[2] == "variation_coordinates") { - set_variation_coordinates(cache_index, p_value); - return true; +real_t Font::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, int p_font_size, const Color &p_modulate) const { + if (dirty_rids) { + _update_rids(); + } + for (int i = 0; i < rids.size(); i++) { + if (TS->font_has_char(rids[i], p_char)) { + int32_t glyph = TS->font_get_glyph_index(rids[i], p_font_size, p_char, 0); + TS->font_draw_glyph(rids[i], p_canvas_item, p_font_size, p_pos, glyph, p_modulate); + return TS->font_get_glyph_advance(rids[i], p_font_size, glyph).x; } - if (tokens.size() >= 5) { - Vector2i sz = Vector2i(tokens[2].to_int(), tokens[3].to_int()); - if (tokens[4] == "ascent") { - set_ascent(cache_index, sz.x, p_value); - return true; - } else if (tokens[4] == "descent") { - set_descent(cache_index, sz.x, p_value); - return true; - } else if (tokens[4] == "underline_position") { - set_underline_position(cache_index, sz.x, p_value); - return true; - } else if (tokens[4] == "underline_thickness") { - set_underline_thickness(cache_index, sz.x, p_value); - return true; - } else if (tokens[4] == "scale") { - set_scale(cache_index, sz.x, p_value); - return true; - } else if (tokens[4] == "spacing_glyph") { - set_spacing(cache_index, sz.x, TextServer::SPACING_GLYPH, p_value); - return true; - } else if (tokens[4] == "spacing_space") { - set_spacing(cache_index, sz.x, TextServer::SPACING_SPACE, p_value); - return true; - } else if (tokens.size() == 7 && tokens[4] == "textures") { - int texture_index = tokens[5].to_int(); - if (tokens[6] == "image") { - set_texture_image(cache_index, sz, texture_index, p_value); - return true; - } else if (tokens[6] == "offsets") { - set_texture_offsets(cache_index, sz, texture_index, p_value); - return true; - } - } else if (tokens.size() == 7 && tokens[4] == "glyphs") { - int32_t glyph_index = tokens[5].to_int(); - if (tokens[6] == "advance") { - set_glyph_advance(cache_index, sz.x, glyph_index, p_value); - return true; - } else if (tokens[6] == "offset") { - set_glyph_offset(cache_index, sz, glyph_index, p_value); - return true; - } else if (tokens[6] == "size") { - set_glyph_size(cache_index, sz, glyph_index, p_value); - return true; - } else if (tokens[6] == "uv_rect") { - set_glyph_uv_rect(cache_index, sz, glyph_index, p_value); - return true; - } else if (tokens[6] == "texture_idx") { - set_glyph_texture_idx(cache_index, sz, glyph_index, p_value); - return true; - } - } else if (tokens.size() == 7 && tokens[4] == "kerning_overrides") { - Vector2i gp = Vector2i(tokens[5].to_int(), tokens[6].to_int()); - set_kerning(cache_index, sz.x, gp, p_value); - return true; - } + } + return 0.f; +} + +real_t Font::draw_char_outline(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, int p_font_size, int p_size, const Color &p_modulate) const { + if (dirty_rids) { + _update_rids(); + } + for (int i = 0; i < rids.size(); i++) { + if (TS->font_has_char(rids[i], p_char)) { + int32_t glyph = TS->font_get_glyph_index(rids[i], p_font_size, p_char, 0); + TS->font_draw_glyph_outline(rids[i], p_canvas_item, p_font_size, p_size, p_pos, glyph, p_modulate); + return TS->font_get_glyph_advance(rids[i], p_font_size, glyph).x; } } - return false; + return 0.f; } -bool FontData::_get(const StringName &p_name, Variant &r_ret) const { - Vector<String> tokens = p_name.operator String().split("/"); - if (tokens.size() == 2 && tokens[0] == "language_support_override") { - String lang = tokens[1]; - r_ret = get_language_support_override(lang); - return true; - } else if (tokens.size() == 2 && tokens[0] == "script_support_override") { - String script = tokens[1]; - r_ret = get_script_support_override(script); - return true; - } else if (tokens.size() >= 3 && tokens[0] == "cache") { - int cache_index = tokens[1].to_int(); - if (tokens.size() == 3 && tokens[2] == "variation_coordinates") { - r_ret = get_variation_coordinates(cache_index); +// Helper functions. +bool Font::has_char(char32_t p_char) const { + if (dirty_rids) { + _update_rids(); + } + for (int i = 0; i < rids.size(); i++) { + if (TS->font_has_char(rids[i], p_char)) { return true; } - if (tokens.size() >= 5) { - Vector2i sz = Vector2i(tokens[2].to_int(), tokens[3].to_int()); - if (tokens[4] == "ascent") { - r_ret = get_ascent(cache_index, sz.x); - return true; - } else if (tokens[4] == "descent") { - r_ret = get_descent(cache_index, sz.x); - return true; - } else if (tokens[4] == "underline_position") { - r_ret = get_underline_position(cache_index, sz.x); - return true; - } else if (tokens[4] == "underline_thickness") { - r_ret = get_underline_thickness(cache_index, sz.x); - return true; - } else if (tokens[4] == "scale") { - r_ret = get_scale(cache_index, sz.x); - return true; - } else if (tokens[4] == "spacing_glyph") { - r_ret = get_spacing(cache_index, sz.x, TextServer::SPACING_GLYPH); - return true; - } else if (tokens[4] == "spacing_space") { - r_ret = get_spacing(cache_index, sz.x, TextServer::SPACING_SPACE); - return true; - } else if (tokens.size() == 7 && tokens[4] == "textures") { - int texture_index = tokens[5].to_int(); - if (tokens[6] == "image") { - r_ret = get_texture_image(cache_index, sz, texture_index); - return true; - } else if (tokens[6] == "offsets") { - r_ret = get_texture_offsets(cache_index, sz, texture_index); - return true; - } - } else if (tokens.size() == 7 && tokens[4] == "glyphs") { - int32_t glyph_index = tokens[5].to_int(); - if (tokens[6] == "advance") { - r_ret = get_glyph_advance(cache_index, sz.x, glyph_index); - return true; - } else if (tokens[6] == "offset") { - r_ret = get_glyph_offset(cache_index, sz, glyph_index); - return true; - } else if (tokens[6] == "size") { - r_ret = get_glyph_size(cache_index, sz, glyph_index); - return true; - } else if (tokens[6] == "uv_rect") { - r_ret = get_glyph_uv_rect(cache_index, sz, glyph_index); - return true; - } else if (tokens[6] == "texture_idx") { - r_ret = get_glyph_texture_idx(cache_index, sz, glyph_index); - return true; - } - } else if (tokens.size() == 7 && tokens[4] == "kerning_overrides") { - Vector2i gp = Vector2i(tokens[5].to_int(), tokens[6].to_int()); - r_ret = get_kerning(cache_index, sz.x, gp); - return true; - } - } } return false; } -void FontData::_get_property_list(List<PropertyInfo> *p_list) const { - Vector<String> lang_over = get_language_support_overrides(); - for (int i = 0; i < lang_over.size(); i++) { - p_list->push_back(PropertyInfo(Variant::BOOL, "language_support_override/" + lang_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - } - Vector<String> scr_over = get_script_support_overrides(); - for (int i = 0; i < scr_over.size(); i++) { - p_list->push_back(PropertyInfo(Variant::BOOL, "script_support_override/" + scr_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); +String Font::get_supported_chars() const { + if (dirty_rids) { + _update_rids(); } - for (int i = 0; i < cache.size(); i++) { - String prefix = "cache/" + itos(i) + "/"; - Array sizes = get_size_cache_list(i); - p_list->push_back(PropertyInfo(Variant::DICTIONARY, prefix + "variation_coordinates", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - for (int j = 0; j < sizes.size(); j++) { - Vector2i sz = sizes[j]; - String prefix_sz = prefix + itos(sz.x) + "/" + itos(sz.y) + "/"; - if (sz.y == 0) { - p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "ascent", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "descent", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "underline_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "underline_thickness", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::BOOL, prefix_sz + "spacing_glyph", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::BOOL, prefix_sz + "spacing_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - } - - int tx_cnt = get_texture_count(i, sz); - for (int k = 0; k < tx_cnt; k++) { - p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, prefix_sz + "textures/" + itos(k) + "/offsets", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::OBJECT, prefix_sz + "textures/" + itos(k) + "/image", PROPERTY_HINT_RESOURCE_TYPE, "Image", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT)); - } - Array glyphs = get_glyph_list(i, sz); - for (int k = 0; k < glyphs.size(); k++) { - const int32_t &gl = glyphs[k]; - if (sz.y == 0) { - p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/advance", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - } - p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::RECT2, prefix_sz + "glyphs/" + itos(gl) + "/uv_rect", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::INT, prefix_sz + "glyphs/" + itos(gl) + "/texture_idx", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - } - if (sz.y == 0) { - Array kerning_map = get_kerning_list(i, sz.x); - for (int k = 0; k < kerning_map.size(); k++) { - const Vector2i &gl_pair = kerning_map[k]; - p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "kerning_overrides/" + itos(gl_pair.x) + "/" + itos(gl_pair.y), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - } + String chars; + for (int i = 0; i < rids.size(); i++) { + String data_chars = TS->font_get_supported_chars(rids[i]); + for (int j = 0; j < data_chars.length(); j++) { + if (chars.find_char(data_chars[j]) == -1) { + chars += data_chars[j]; } } } + return chars; } -void FontData::reset_state() { - _clear_cache(); - data.clear(); - data_ptr = nullptr; - data_size = 0; - cache.clear(); +bool Font::is_language_supported(const String &p_language) const { + return TS->font_is_language_supported(_get_rid(), p_language); +} - antialiased = true; - msdf = false; - force_autohinter = false; - hinting = TextServer::HINTING_LIGHT; - subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED; - msdf_pixel_range = 14; - msdf_size = 128; - fixed_size = 0; - embolden = 0.f; - transform = Transform2D(); - oversampling = 0.f; +bool Font::is_script_supported(const String &p_script) const { + return TS->font_is_script_supported(_get_rid(), p_script); +} + +Dictionary Font::get_supported_feature_list() const { + return TS->font_supported_feature_list(_get_rid()); +} + +Dictionary Font::get_supported_variation_list() const { + return TS->font_supported_variation_list(_get_rid()); +} + +int64_t Font::get_face_count() const { + return TS->font_get_face_count(_get_rid()); +} + +Font::Font() { + cache.set_capacity(64); + cache_wrap.set_capacity(16); } -void FontData::_convert_packed_8bit(Ref<Image> &p_source, int p_page, int p_sz) { +Font::~Font() { + reset_state(); +} + +/*************************************************************************/ +/* FontFile */ +/*************************************************************************/ + +_FORCE_INLINE_ void FontFile::_clear_cache() { + for (int i = 0; i < cache.size(); i++) { + if (cache[i].is_valid()) { + TS->free_rid(cache[i]); + cache.write[i] = RID(); + } + } +} + +_FORCE_INLINE_ void FontFile::_ensure_rid(int p_cache_index) const { + if (unlikely(p_cache_index >= cache.size())) { + cache.resize(p_cache_index + 1); + } + if (unlikely(!cache[p_cache_index].is_valid())) { + cache.write[p_cache_index] = TS->create_font(); + TS->font_set_data_ptr(cache[p_cache_index], data_ptr, data_size); + TS->font_set_antialiased(cache[p_cache_index], antialiased); + TS->font_set_generate_mipmaps(cache[p_cache_index], mipmaps); + TS->font_set_multichannel_signed_distance_field(cache[p_cache_index], msdf); + TS->font_set_msdf_pixel_range(cache[p_cache_index], msdf_pixel_range); + TS->font_set_msdf_size(cache[p_cache_index], msdf_size); + TS->font_set_fixed_size(cache[p_cache_index], fixed_size); + TS->font_set_force_autohinter(cache[p_cache_index], force_autohinter); + TS->font_set_hinting(cache[p_cache_index], hinting); + TS->font_set_subpixel_positioning(cache[p_cache_index], subpixel_positioning); + TS->font_set_oversampling(cache[p_cache_index], oversampling); + } +} + +void FontFile::_convert_packed_8bit(Ref<Image> &p_source, int p_page, int p_sz) { int w = p_source->get_width(); int h = p_source->get_height(); @@ -501,7 +636,7 @@ void FontData::_convert_packed_8bit(Ref<Image> &p_source, int p_page, int p_sz) set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 3, img_a); } -void FontData::_convert_packed_4bit(Ref<Image> &p_source, int p_page, int p_sz) { +void FontFile::_convert_packed_4bit(Ref<Image> &p_source, int p_page, int p_sz) { int w = p_source->get_width(); int h = p_source->get_height(); @@ -601,7 +736,7 @@ void FontData::_convert_packed_4bit(Ref<Image> &p_source, int p_page, int p_sz) set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 3, img_ao); } -void FontData::_convert_rgba_4bit(Ref<Image> &p_source, int p_page, int p_sz) { +void FontFile::_convert_rgba_4bit(Ref<Image> &p_source, int p_page, int p_sz) { int w = p_source->get_width(); int h = p_source->get_height(); @@ -657,7 +792,7 @@ void FontData::_convert_rgba_4bit(Ref<Image> &p_source, int p_page, int p_sz) { set_texture_image(0, Vector2i(p_sz, 1), p_page, img_o); } -void FontData::_convert_mono_8bit(Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol) { +void FontFile::_convert_mono_8bit(Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol) { int w = p_source->get_width(); int h = p_source->get_height(); @@ -686,7 +821,7 @@ void FontData::_convert_mono_8bit(Ref<Image> &p_source, int p_page, int p_ch, in set_texture_image(0, Vector2i(p_sz, p_ol), p_page, img_g); } -void FontData::_convert_mono_4bit(Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol) { +void FontFile::_convert_mono_4bit(Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol) { int w = p_source->get_width(); int h = p_source->get_height(); @@ -729,12 +864,481 @@ void FontData::_convert_mono_4bit(Ref<Image> &p_source, int p_page, int p_ch, in set_texture_image(0, Vector2i(p_sz, p_ol), p_page, img_o); } +void FontFile::_bind_methods() { + ClassDB::bind_method(D_METHOD("load_bitmap_font", "path"), &FontFile::load_bitmap_font); + ClassDB::bind_method(D_METHOD("load_dynamic_font", "path"), &FontFile::load_dynamic_font); + + ClassDB::bind_method(D_METHOD("set_data", "data"), &FontFile::set_data); + ClassDB::bind_method(D_METHOD("get_data"), &FontFile::get_data); + + ClassDB::bind_method(D_METHOD("set_font_name", "name"), &FontFile::set_font_name); + ClassDB::bind_method(D_METHOD("set_font_style_name", "name"), &FontFile::set_font_style_name); + ClassDB::bind_method(D_METHOD("set_font_style", "style"), &FontFile::set_font_style); + + ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontFile::set_antialiased); + ClassDB::bind_method(D_METHOD("is_antialiased"), &FontFile::is_antialiased); + + ClassDB::bind_method(D_METHOD("set_generate_mipmaps", "generate_mipmaps"), &FontFile::set_generate_mipmaps); + ClassDB::bind_method(D_METHOD("get_generate_mipmaps"), &FontFile::get_generate_mipmaps); + + ClassDB::bind_method(D_METHOD("set_multichannel_signed_distance_field", "msdf"), &FontFile::set_multichannel_signed_distance_field); + ClassDB::bind_method(D_METHOD("is_multichannel_signed_distance_field"), &FontFile::is_multichannel_signed_distance_field); + + ClassDB::bind_method(D_METHOD("set_msdf_pixel_range", "msdf_pixel_range"), &FontFile::set_msdf_pixel_range); + ClassDB::bind_method(D_METHOD("get_msdf_pixel_range"), &FontFile::get_msdf_pixel_range); + + ClassDB::bind_method(D_METHOD("set_msdf_size", "msdf_size"), &FontFile::set_msdf_size); + ClassDB::bind_method(D_METHOD("get_msdf_size"), &FontFile::get_msdf_size); + + ClassDB::bind_method(D_METHOD("set_fixed_size", "fixed_size"), &FontFile::set_fixed_size); + ClassDB::bind_method(D_METHOD("get_fixed_size"), &FontFile::get_fixed_size); + + ClassDB::bind_method(D_METHOD("set_force_autohinter", "force_autohinter"), &FontFile::set_force_autohinter); + ClassDB::bind_method(D_METHOD("is_force_autohinter"), &FontFile::is_force_autohinter); + + ClassDB::bind_method(D_METHOD("set_hinting", "hinting"), &FontFile::set_hinting); + ClassDB::bind_method(D_METHOD("get_hinting"), &FontFile::get_hinting); + + ClassDB::bind_method(D_METHOD("set_subpixel_positioning", "subpixel_positioning"), &FontFile::set_subpixel_positioning); + ClassDB::bind_method(D_METHOD("get_subpixel_positioning"), &FontFile::get_subpixel_positioning); + + ClassDB::bind_method(D_METHOD("set_oversampling", "oversampling"), &FontFile::set_oversampling); + ClassDB::bind_method(D_METHOD("get_oversampling"), &FontFile::get_oversampling); + + ClassDB::bind_method(D_METHOD("get_cache_count"), &FontFile::get_cache_count); + ClassDB::bind_method(D_METHOD("clear_cache"), &FontFile::clear_cache); + ClassDB::bind_method(D_METHOD("remove_cache", "cache_index"), &FontFile::remove_cache); + + ClassDB::bind_method(D_METHOD("get_size_cache_list", "cache_index"), &FontFile::get_size_cache_list); + ClassDB::bind_method(D_METHOD("clear_size_cache", "cache_index"), &FontFile::clear_size_cache); + ClassDB::bind_method(D_METHOD("remove_size_cache", "cache_index", "size"), &FontFile::remove_size_cache); + + ClassDB::bind_method(D_METHOD("set_variation_coordinates", "cache_index", "variation_coordinates"), &FontFile::set_variation_coordinates); + ClassDB::bind_method(D_METHOD("get_variation_coordinates", "cache_index"), &FontFile::get_variation_coordinates); + + ClassDB::bind_method(D_METHOD("set_embolden", "cache_index", "strength"), &FontFile::set_embolden); + ClassDB::bind_method(D_METHOD("get_embolden", "cache_index"), &FontFile::get_embolden); + + ClassDB::bind_method(D_METHOD("set_transform", "cache_index", "transform"), &FontFile::set_transform); + ClassDB::bind_method(D_METHOD("get_transform", "cache_index"), &FontFile::get_transform); + + ClassDB::bind_method(D_METHOD("set_face_index", "cache_index", "face_index"), &FontFile::set_face_index); + ClassDB::bind_method(D_METHOD("get_face_index", "cache_index"), &FontFile::get_face_index); + + ClassDB::bind_method(D_METHOD("set_cache_ascent", "cache_index", "size", "ascent"), &FontFile::set_cache_ascent); + ClassDB::bind_method(D_METHOD("get_cache_ascent", "cache_index", "size"), &FontFile::get_cache_ascent); + + ClassDB::bind_method(D_METHOD("set_cache_descent", "cache_index", "size", "descent"), &FontFile::set_cache_descent); + ClassDB::bind_method(D_METHOD("get_cache_descent", "cache_index", "size"), &FontFile::get_cache_descent); + + ClassDB::bind_method(D_METHOD("set_cache_underline_position", "cache_index", "size", "underline_position"), &FontFile::set_cache_underline_position); + ClassDB::bind_method(D_METHOD("get_cache_underline_position", "cache_index", "size"), &FontFile::get_cache_underline_position); + + ClassDB::bind_method(D_METHOD("set_cache_underline_thickness", "cache_index", "size", "underline_thickness"), &FontFile::set_cache_underline_thickness); + ClassDB::bind_method(D_METHOD("get_cache_underline_thickness", "cache_index", "size"), &FontFile::get_cache_underline_thickness); + + ClassDB::bind_method(D_METHOD("set_cache_scale", "cache_index", "size", "scale"), &FontFile::set_cache_scale); + ClassDB::bind_method(D_METHOD("get_cache_scale", "cache_index", "size"), &FontFile::get_cache_scale); + + ClassDB::bind_method(D_METHOD("get_texture_count", "cache_index", "size"), &FontFile::get_texture_count); + ClassDB::bind_method(D_METHOD("clear_textures", "cache_index", "size"), &FontFile::clear_textures); + ClassDB::bind_method(D_METHOD("remove_texture", "cache_index", "size", "texture_index"), &FontFile::remove_texture); + + ClassDB::bind_method(D_METHOD("set_texture_image", "cache_index", "size", "texture_index", "image"), &FontFile::set_texture_image); + ClassDB::bind_method(D_METHOD("get_texture_image", "cache_index", "size", "texture_index"), &FontFile::get_texture_image); + + ClassDB::bind_method(D_METHOD("set_texture_offsets", "cache_index", "size", "texture_index", "offset"), &FontFile::set_texture_offsets); + ClassDB::bind_method(D_METHOD("get_texture_offsets", "cache_index", "size", "texture_index"), &FontFile::get_texture_offsets); + + ClassDB::bind_method(D_METHOD("get_glyph_list", "cache_index", "size"), &FontFile::get_glyph_list); + ClassDB::bind_method(D_METHOD("clear_glyphs", "cache_index", "size"), &FontFile::clear_glyphs); + ClassDB::bind_method(D_METHOD("remove_glyph", "cache_index", "size", "glyph"), &FontFile::remove_glyph); + + ClassDB::bind_method(D_METHOD("set_glyph_advance", "cache_index", "size", "glyph", "advance"), &FontFile::set_glyph_advance); + ClassDB::bind_method(D_METHOD("get_glyph_advance", "cache_index", "size", "glyph"), &FontFile::get_glyph_advance); + + ClassDB::bind_method(D_METHOD("set_glyph_offset", "cache_index", "size", "glyph", "offset"), &FontFile::set_glyph_offset); + ClassDB::bind_method(D_METHOD("get_glyph_offset", "cache_index", "size", "glyph"), &FontFile::get_glyph_offset); + + ClassDB::bind_method(D_METHOD("set_glyph_size", "cache_index", "size", "glyph", "gl_size"), &FontFile::set_glyph_size); + ClassDB::bind_method(D_METHOD("get_glyph_size", "cache_index", "size", "glyph"), &FontFile::get_glyph_size); + + ClassDB::bind_method(D_METHOD("set_glyph_uv_rect", "cache_index", "size", "glyph", "uv_rect"), &FontFile::set_glyph_uv_rect); + ClassDB::bind_method(D_METHOD("get_glyph_uv_rect", "cache_index", "size", "glyph"), &FontFile::get_glyph_uv_rect); + + ClassDB::bind_method(D_METHOD("set_glyph_texture_idx", "cache_index", "size", "glyph", "texture_idx"), &FontFile::set_glyph_texture_idx); + ClassDB::bind_method(D_METHOD("get_glyph_texture_idx", "cache_index", "size", "glyph"), &FontFile::get_glyph_texture_idx); + + ClassDB::bind_method(D_METHOD("get_kerning_list", "cache_index", "size"), &FontFile::get_kerning_list); + ClassDB::bind_method(D_METHOD("clear_kerning_map", "cache_index", "size"), &FontFile::clear_kerning_map); + ClassDB::bind_method(D_METHOD("remove_kerning", "cache_index", "size", "glyph_pair"), &FontFile::remove_kerning); + + ClassDB::bind_method(D_METHOD("set_kerning", "cache_index", "size", "glyph_pair", "kerning"), &FontFile::set_kerning); + ClassDB::bind_method(D_METHOD("get_kerning", "cache_index", "size", "glyph_pair"), &FontFile::get_kerning); + + ClassDB::bind_method(D_METHOD("render_range", "cache_index", "size", "start", "end"), &FontFile::render_range); + ClassDB::bind_method(D_METHOD("render_glyph", "cache_index", "size", "index"), &FontFile::render_glyph); + + ClassDB::bind_method(D_METHOD("set_language_support_override", "language", "supported"), &FontFile::set_language_support_override); + ClassDB::bind_method(D_METHOD("get_language_support_override", "language"), &FontFile::get_language_support_override); + ClassDB::bind_method(D_METHOD("remove_language_support_override", "language"), &FontFile::remove_language_support_override); + ClassDB::bind_method(D_METHOD("get_language_support_overrides"), &FontFile::get_language_support_overrides); + + ClassDB::bind_method(D_METHOD("set_script_support_override", "script", "supported"), &FontFile::set_script_support_override); + ClassDB::bind_method(D_METHOD("get_script_support_override", "script"), &FontFile::get_script_support_override); + ClassDB::bind_method(D_METHOD("remove_script_support_override", "script"), &FontFile::remove_script_support_override); + ClassDB::bind_method(D_METHOD("get_script_support_overrides"), &FontFile::get_script_support_overrides); + + ClassDB::bind_method(D_METHOD("set_opentype_feature_overrides", "overrides"), &FontFile::set_opentype_feature_overrides); + ClassDB::bind_method(D_METHOD("get_opentype_feature_overrides"), &FontFile::get_opentype_feature_overrides); + + ClassDB::bind_method(D_METHOD("get_glyph_index", "size", "char", "variation_selector"), &FontFile::get_glyph_index); + + ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_data", "get_data"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_mipmaps", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_generate_mipmaps", "get_generate_mipmaps"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_antialiased", "is_antialiased"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "font_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_name", "get_font_name"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "style_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_style_name", "get_font_style_name"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_FLAGS, "Bold,Italic,Fixed Size", PROPERTY_USAGE_STORAGE), "set_font_style", "get_font_style"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel", PROPERTY_USAGE_STORAGE), "set_subpixel_positioning", "get_subpixel_positioning"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_multichannel_signed_distance_field", "is_multichannel_signed_distance_field"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_pixel_range", "get_msdf_pixel_range"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_size", "get_msdf_size"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_force_autohinter", "is_force_autohinter"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_STORAGE), "set_hinting", "get_hinting"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_oversampling", "get_oversampling"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_fixed_size", "get_fixed_size"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "opentype_feature_overrides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_opentype_feature_overrides", "get_opentype_feature_overrides"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Font"), PROPERTY_USAGE_STORAGE), "set_fallbacks", "get_fallbacks"); +} + +bool FontFile::_set(const StringName &p_name, const Variant &p_value) { + Vector<String> tokens = p_name.operator String().split("/"); + +#ifndef DISABLE_DEPRECATED + if (tokens.size() == 1 && tokens[0] == "font_path") { + // Compatibility, DynamicFontData. + load_dynamic_font(p_value); + } else if (tokens.size() == 1 && tokens[0] == "override_oversampling") { + set_oversampling(p_value); + } + if (tokens.size() == 1 && tokens[0] == "font_data") { + // Compatibility, DynamicFont. + Ref<Font> f = p_value; + if (f.is_valid()) { + fallbacks.push_back(f); + return true; + } + return false; + } else if (tokens.size() == 2 && tokens[0] == "fallback") { + // Compatibility, DynamicFont. + Ref<FontFile> f = p_value; + if (f.is_valid()) { + fallbacks.push_back(f); + return true; + } + return false; + } else if (tokens.size() == 1 && tokens[0] == "textures") { + // Compatibility, BitmapFont. + set_fixed_size(16); + Array textures = p_value; + for (int i = 0; i < textures.size(); i++) { + Ref<ImageTexture> tex = textures[i]; + ERR_CONTINUE(!tex.is_valid()); + set_texture_image(0, Vector2i(16, 0), i, tex->get_image()); + } + } else if (tokens.size() == 1 && tokens[0] == "chars") { + // Compatibility, BitmapFont. + set_fixed_size(16); + PackedInt32Array arr = p_value; + int len = arr.size(); + ERR_FAIL_COND_V(len % 9, false); + if (!len) { + return false; + } + int chars = len / 9; + for (int i = 0; i < chars; i++) { + const int32_t *data = &arr[i * 9]; + char32_t c = data[0]; + set_glyph_texture_idx(0, Vector2i(16, 0), c, data[1]); + set_glyph_uv_rect(0, Vector2i(16, 0), c, Rect2(data[2], data[3], data[4], data[5])); + set_glyph_offset(0, Vector2i(16, 0), c, Size2(data[6], data[7])); + set_glyph_advance(0, 16, c, Vector2(data[8], 0)); + } + } else if (tokens.size() == 1 && tokens[0] == "kernings") { + // Compatibility, BitmapFont. + set_fixed_size(16); + PackedInt32Array arr = p_value; + int len = arr.size(); + ERR_FAIL_COND_V(len % 3, false); + if (!len) { + return false; + } + for (int i = 0; i < len / 3; i++) { + const int32_t *data = &arr[i * 3]; + set_kerning(0, 16, Vector2i(data[0], data[1]), Vector2(data[2], 0)); + } + } else if (tokens.size() == 1 && tokens[0] == "height") { + // Compatibility, BitmapFont. + bmp_height = p_value; + set_fixed_size(16); + set_cache_descent(0, 16, bmp_height - bmp_ascent); + } else if (tokens.size() == 1 && tokens[0] == "ascent") { + // Compatibility, BitmapFont. + bmp_ascent = p_value; + set_fixed_size(16); + set_cache_ascent(0, 16, bmp_ascent); + set_cache_descent(0, 16, bmp_height - bmp_ascent); + } else if (tokens.size() == 1 && tokens[0] == "fallback") { + // Compatibility, BitmapFont. + Ref<Font> f = p_value; + if (f.is_valid()) { + fallbacks.push_back(f); + return true; + } + return false; + } +#endif // DISABLE_DEPRECATED + + if (tokens.size() == 2 && tokens[0] == "language_support_override") { + String lang = tokens[1]; + set_language_support_override(lang, p_value); + return true; + } else if (tokens.size() == 2 && tokens[0] == "script_support_override") { + String script = tokens[1]; + set_script_support_override(script, p_value); + return true; + } else if (tokens.size() >= 3 && tokens[0] == "cache") { + int cache_index = tokens[1].to_int(); + if (tokens.size() == 3 && tokens[2] == "variation_coordinates") { + set_variation_coordinates(cache_index, p_value); + return true; + } else if (tokens.size() == 3 && tokens[2] == "embolden") { + set_embolden(cache_index, p_value); + return true; + } else if (tokens.size() == 3 && tokens[2] == "face_index") { + set_face_index(cache_index, p_value); + return true; + } else if (tokens.size() == 3 && tokens[2] == "transform") { + set_transform(cache_index, p_value); + return true; + } + if (tokens.size() >= 5) { + Vector2i sz = Vector2i(tokens[2].to_int(), tokens[3].to_int()); + if (tokens[4] == "ascent") { + set_cache_ascent(cache_index, sz.x, p_value); + return true; + } else if (tokens[4] == "descent") { + set_cache_descent(cache_index, sz.x, p_value); + return true; + } else if (tokens[4] == "underline_position") { + set_cache_underline_position(cache_index, sz.x, p_value); + return true; + } else if (tokens[4] == "underline_thickness") { + set_cache_underline_thickness(cache_index, sz.x, p_value); + return true; + } else if (tokens[4] == "scale") { + set_cache_scale(cache_index, sz.x, p_value); + return true; + } else if (tokens.size() == 7 && tokens[4] == "textures") { + int texture_index = tokens[5].to_int(); + if (tokens[6] == "image") { + set_texture_image(cache_index, sz, texture_index, p_value); + return true; + } else if (tokens[6] == "offsets") { + set_texture_offsets(cache_index, sz, texture_index, p_value); + return true; + } + } else if (tokens.size() == 7 && tokens[4] == "glyphs") { + int32_t glyph_index = tokens[5].to_int(); + if (tokens[6] == "advance") { + set_glyph_advance(cache_index, sz.x, glyph_index, p_value); + return true; + } else if (tokens[6] == "offset") { + set_glyph_offset(cache_index, sz, glyph_index, p_value); + return true; + } else if (tokens[6] == "size") { + set_glyph_size(cache_index, sz, glyph_index, p_value); + return true; + } else if (tokens[6] == "uv_rect") { + set_glyph_uv_rect(cache_index, sz, glyph_index, p_value); + return true; + } else if (tokens[6] == "texture_idx") { + set_glyph_texture_idx(cache_index, sz, glyph_index, p_value); + return true; + } + } else if (tokens.size() == 7 && tokens[4] == "kerning_overrides") { + Vector2i gp = Vector2i(tokens[5].to_int(), tokens[6].to_int()); + set_kerning(cache_index, sz.x, gp, p_value); + return true; + } + } + } + return false; +} + +bool FontFile::_get(const StringName &p_name, Variant &r_ret) const { + Vector<String> tokens = p_name.operator String().split("/"); + if (tokens.size() == 2 && tokens[0] == "language_support_override") { + String lang = tokens[1]; + r_ret = get_language_support_override(lang); + return true; + } else if (tokens.size() == 2 && tokens[0] == "script_support_override") { + String script = tokens[1]; + r_ret = get_script_support_override(script); + return true; + } else if (tokens.size() >= 3 && tokens[0] == "cache") { + int cache_index = tokens[1].to_int(); + if (tokens.size() == 3 && tokens[2] == "variation_coordinates") { + r_ret = get_variation_coordinates(cache_index); + return true; + } else if (tokens.size() == 3 && tokens[2] == "embolden") { + r_ret = get_embolden(cache_index); + return true; + } else if (tokens.size() == 3 && tokens[2] == "face_index") { + r_ret = get_face_index(cache_index); + return true; + } else if (tokens.size() == 3 && tokens[2] == "transform") { + r_ret = get_transform(cache_index); + return true; + } + if (tokens.size() >= 5) { + Vector2i sz = Vector2i(tokens[2].to_int(), tokens[3].to_int()); + if (tokens[4] == "ascent") { + r_ret = get_cache_ascent(cache_index, sz.x); + return true; + } else if (tokens[4] == "descent") { + r_ret = get_cache_descent(cache_index, sz.x); + return true; + } else if (tokens[4] == "underline_position") { + r_ret = get_cache_underline_position(cache_index, sz.x); + return true; + } else if (tokens[4] == "underline_thickness") { + r_ret = get_cache_underline_thickness(cache_index, sz.x); + return true; + } else if (tokens[4] == "scale") { + r_ret = get_cache_scale(cache_index, sz.x); + return true; + } else if (tokens.size() == 7 && tokens[4] == "textures") { + int texture_index = tokens[5].to_int(); + if (tokens[6] == "image") { + r_ret = get_texture_image(cache_index, sz, texture_index); + return true; + } else if (tokens[6] == "offsets") { + r_ret = get_texture_offsets(cache_index, sz, texture_index); + return true; + } + } else if (tokens.size() == 7 && tokens[4] == "glyphs") { + int32_t glyph_index = tokens[5].to_int(); + if (tokens[6] == "advance") { + r_ret = get_glyph_advance(cache_index, sz.x, glyph_index); + return true; + } else if (tokens[6] == "offset") { + r_ret = get_glyph_offset(cache_index, sz, glyph_index); + return true; + } else if (tokens[6] == "size") { + r_ret = get_glyph_size(cache_index, sz, glyph_index); + return true; + } else if (tokens[6] == "uv_rect") { + r_ret = get_glyph_uv_rect(cache_index, sz, glyph_index); + return true; + } else if (tokens[6] == "texture_idx") { + r_ret = get_glyph_texture_idx(cache_index, sz, glyph_index); + return true; + } + } else if (tokens.size() == 7 && tokens[4] == "kerning_overrides") { + Vector2i gp = Vector2i(tokens[5].to_int(), tokens[6].to_int()); + r_ret = get_kerning(cache_index, sz.x, gp); + return true; + } + } + } + return false; +} + +void FontFile::_get_property_list(List<PropertyInfo> *p_list) const { + Vector<String> lang_over = get_language_support_overrides(); + for (int i = 0; i < lang_over.size(); i++) { + p_list->push_back(PropertyInfo(Variant::BOOL, "language_support_override/" + lang_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + } + Vector<String> scr_over = get_script_support_overrides(); + for (int i = 0; i < scr_over.size(); i++) { + p_list->push_back(PropertyInfo(Variant::BOOL, "script_support_override/" + scr_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + } + for (int i = 0; i < cache.size(); i++) { + String prefix = "cache/" + itos(i) + "/"; + Array sizes = get_size_cache_list(i); + p_list->push_back(PropertyInfo(Variant::DICTIONARY, prefix + "variation_coordinates", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::INT, "face_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::FLOAT, "embolden", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::TRANSFORM2D, "transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + + for (int j = 0; j < sizes.size(); j++) { + Vector2i sz = sizes[j]; + String prefix_sz = prefix + itos(sz.x) + "/" + itos(sz.y) + "/"; + if (sz.y == 0) { + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "ascent", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "descent", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "underline_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "underline_thickness", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + } + + int tx_cnt = get_texture_count(i, sz); + for (int k = 0; k < tx_cnt; k++) { + p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, prefix_sz + "textures/" + itos(k) + "/offsets", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::OBJECT, prefix_sz + "textures/" + itos(k) + "/image", PROPERTY_HINT_RESOURCE_TYPE, "Image", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT)); + } + Array glyphs = get_glyph_list(i, sz); + for (int k = 0; k < glyphs.size(); k++) { + const int32_t &gl = glyphs[k]; + if (sz.y == 0) { + p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/advance", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + } + p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::RECT2, prefix_sz + "glyphs/" + itos(gl) + "/uv_rect", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::INT, prefix_sz + "glyphs/" + itos(gl) + "/texture_idx", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + } + if (sz.y == 0) { + Array kerning_map = get_kerning_list(i, sz.x); + for (int k = 0; k < kerning_map.size(); k++) { + const Vector2i &gl_pair = kerning_map[k]; + p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "kerning_overrides/" + itos(gl_pair.x) + "/" + itos(gl_pair.y), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + } + } + } + } +} + +void FontFile::reset_state() { + _clear_cache(); + data.clear(); + data_ptr = nullptr; + data_size = 0; + cache.clear(); + + antialiased = true; + mipmaps = false; + msdf = false; + force_autohinter = false; + hinting = TextServer::HINTING_LIGHT; + subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED; + msdf_pixel_range = 14; + msdf_size = 128; + fixed_size = 0; + oversampling = 0.f; + + Font::reset_state(); +} + /*************************************************************************/ -Error FontData::load_bitmap_font(const String &p_path) { +Error FontFile::load_bitmap_font(const String &p_path) { reset_state(); antialiased = false; + mipmaps = false; msdf = false; force_autohinter = false; hinting = TextServer::HINTING_NONE; @@ -747,7 +1351,7 @@ Error FontData::load_bitmap_font(const String &p_path) { int height = 0; int ascent = 0; int outline = 0; - uint32_t st_flags = 0; + BitField<TextServer::FontStyle> st_flags = 0; String font_name; bool packed = false; @@ -773,10 +1377,10 @@ Error FontData::load_bitmap_font(const String &p_path) { uint8_t flags = f->get_8(); ERR_FAIL_COND_V_MSG(flags & 0x02, ERR_CANT_CREATE, RTR("Non-unicode version of BMFont is not supported.")); if (flags & (1 << 3)) { - st_flags |= TextServer::FONT_BOLD; + st_flags.set_flag(TextServer::FONT_BOLD); } if (flags & (1 << 2)) { - st_flags |= TextServer::FONT_ITALIC; + st_flags.set_flag(TextServer::FONT_ITALIC); } f->get_8(); // non-unicode charset, skip f->get_16(); // stretch_h, skip @@ -956,7 +1560,7 @@ Error FontData::load_bitmap_font(const String &p_path) { int delimiter = line.find(" "); String type = line.substr(0, delimiter); int pos = delimiter + 1; - Map<String, String> keys; + HashMap<String, String> keys; while (pos < line.size() && line[pos] == ' ') { pos++; @@ -1003,12 +1607,12 @@ Error FontData::load_bitmap_font(const String &p_path) { } if (keys.has("bold")) { if (keys["bold"].to_int()) { - st_flags |= TextServer::FONT_BOLD; + st_flags.set_flag(TextServer::FONT_BOLD); } } if (keys.has("italic")) { if (keys["italic"].to_int()) { - st_flags |= TextServer::FONT_ITALIC; + st_flags.set_flag(TextServer::FONT_ITALIC); } } if (keys.has("face")) { @@ -1193,13 +1797,13 @@ Error FontData::load_bitmap_font(const String &p_path) { set_font_name(font_name); set_font_style(st_flags); - set_ascent(0, base_size, ascent); - set_descent(0, base_size, height - ascent); + set_cache_ascent(0, base_size, ascent); + set_cache_descent(0, base_size, height - ascent); return OK; } -Error FontData::load_dynamic_font(const String &p_path) { +Error FontFile::load_dynamic_font(const String &p_path) { reset_state(); Vector<uint8_t> data = FileAccess::get_file_as_array(p_path); @@ -1208,7 +1812,7 @@ Error FontData::load_dynamic_font(const String &p_path) { return OK; } -void FontData::set_data_ptr(const uint8_t *p_data, size_t p_size) { +void FontFile::set_data_ptr(const uint8_t *p_data, size_t p_size) { data.clear(); data_ptr = p_data; data_size = p_size; @@ -1222,7 +1826,7 @@ void FontData::set_data_ptr(const uint8_t *p_data, size_t p_size) { } } -void FontData::set_data(const PackedByteArray &p_data) { +void FontFile::set_data(const PackedByteArray &p_data) { data = p_data; data_ptr = data.ptr(); data_size = data.size(); @@ -1236,7 +1840,7 @@ void FontData::set_data(const PackedByteArray &p_data) { } } -PackedByteArray FontData::get_data() const { +PackedByteArray FontFile::get_data() const { if (unlikely((size_t)data.size() != data_size)) { PackedByteArray *data_w = const_cast<PackedByteArray *>(&data); data_w->resize(data_size); @@ -1245,37 +1849,22 @@ PackedByteArray FontData::get_data() const { return data; } -void FontData::set_font_name(const String &p_name) { +void FontFile::set_font_name(const String &p_name) { _ensure_rid(0); TS->font_set_name(cache[0], p_name); } -String FontData::get_font_name() const { - _ensure_rid(0); - return TS->font_get_name(cache[0]); -} - -void FontData::set_font_style_name(const String &p_name) { +void FontFile::set_font_style_name(const String &p_name) { _ensure_rid(0); TS->font_set_style_name(cache[0], p_name); } -String FontData::get_font_style_name() const { - _ensure_rid(0); - return TS->font_get_style_name(cache[0]); -} - -void FontData::set_font_style(uint32_t p_style) { +void FontFile::set_font_style(BitField<TextServer::FontStyle> p_style) { _ensure_rid(0); TS->font_set_style(cache[0], p_style); } -uint32_t FontData::get_font_style() const { - _ensure_rid(0); - return TS->font_get_style(cache[0]); -} - -void FontData::set_antialiased(bool p_antialiased) { +void FontFile::set_antialiased(bool p_antialiased) { if (antialiased != p_antialiased) { antialiased = p_antialiased; for (int i = 0; i < cache.size(); i++) { @@ -1286,11 +1875,26 @@ void FontData::set_antialiased(bool p_antialiased) { } } -bool FontData::is_antialiased() const { +bool FontFile::is_antialiased() const { return antialiased; } -void FontData::set_multichannel_signed_distance_field(bool p_msdf) { +void FontFile::set_generate_mipmaps(bool p_generate_mipmaps) { + if (mipmaps != p_generate_mipmaps) { + mipmaps = p_generate_mipmaps; + for (int i = 0; i < cache.size(); i++) { + _ensure_rid(i); + TS->font_set_generate_mipmaps(cache[i], mipmaps); + } + emit_changed(); + } +} + +bool FontFile::get_generate_mipmaps() const { + return mipmaps; +} + +void FontFile::set_multichannel_signed_distance_field(bool p_msdf) { if (msdf != p_msdf) { msdf = p_msdf; for (int i = 0; i < cache.size(); i++) { @@ -1301,11 +1905,11 @@ void FontData::set_multichannel_signed_distance_field(bool p_msdf) { } } -bool FontData::is_multichannel_signed_distance_field() const { +bool FontFile::is_multichannel_signed_distance_field() const { return msdf; } -void FontData::set_msdf_pixel_range(int p_msdf_pixel_range) { +void FontFile::set_msdf_pixel_range(int p_msdf_pixel_range) { if (msdf_pixel_range != p_msdf_pixel_range) { msdf_pixel_range = p_msdf_pixel_range; for (int i = 0; i < cache.size(); i++) { @@ -1316,11 +1920,11 @@ void FontData::set_msdf_pixel_range(int p_msdf_pixel_range) { } } -int FontData::get_msdf_pixel_range() const { +int FontFile::get_msdf_pixel_range() const { return msdf_pixel_range; } -void FontData::set_msdf_size(int p_msdf_size) { +void FontFile::set_msdf_size(int p_msdf_size) { if (msdf_size != p_msdf_size) { msdf_size = p_msdf_size; for (int i = 0; i < cache.size(); i++) { @@ -1331,11 +1935,11 @@ void FontData::set_msdf_size(int p_msdf_size) { } } -int FontData::get_msdf_size() const { +int FontFile::get_msdf_size() const { return msdf_size; } -void FontData::set_fixed_size(int p_fixed_size) { +void FontFile::set_fixed_size(int p_fixed_size) { if (fixed_size != p_fixed_size) { fixed_size = p_fixed_size; for (int i = 0; i < cache.size(); i++) { @@ -1346,11 +1950,11 @@ void FontData::set_fixed_size(int p_fixed_size) { } } -int FontData::get_fixed_size() const { +int FontFile::get_fixed_size() const { return fixed_size; } -void FontData::set_force_autohinter(bool p_force_autohinter) { +void FontFile::set_force_autohinter(bool p_force_autohinter) { if (force_autohinter != p_force_autohinter) { force_autohinter = p_force_autohinter; for (int i = 0; i < cache.size(); i++) { @@ -1361,11 +1965,11 @@ void FontData::set_force_autohinter(bool p_force_autohinter) { } } -bool FontData::is_force_autohinter() const { +bool FontFile::is_force_autohinter() const { return force_autohinter; } -void FontData::set_hinting(TextServer::Hinting p_hinting) { +void FontFile::set_hinting(TextServer::Hinting p_hinting) { if (hinting != p_hinting) { hinting = p_hinting; for (int i = 0; i < cache.size(); i++) { @@ -1376,11 +1980,11 @@ void FontData::set_hinting(TextServer::Hinting p_hinting) { } } -TextServer::Hinting FontData::get_hinting() const { +TextServer::Hinting FontFile::get_hinting() const { return hinting; } -void FontData::set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel) { +void FontFile::set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel) { if (subpixel_positioning != p_subpixel) { subpixel_positioning = p_subpixel; for (int i = 0; i < cache.size(); i++) { @@ -1391,41 +1995,11 @@ void FontData::set_subpixel_positioning(TextServer::SubpixelPositioning p_subpix } } -TextServer::SubpixelPositioning FontData::get_subpixel_positioning() const { +TextServer::SubpixelPositioning FontFile::get_subpixel_positioning() const { return subpixel_positioning; } -void FontData::set_embolden(float p_strength) { - if (embolden != p_strength) { - embolden = p_strength; - for (int i = 0; i < cache.size(); i++) { - _ensure_rid(i); - TS->font_set_embolden(cache[i], embolden); - } - emit_changed(); - } -} - -float FontData::get_embolden() const { - return embolden; -} - -void FontData::set_transform(Transform2D p_transform) { - if (transform != p_transform) { - transform = p_transform; - for (int i = 0; i < cache.size(); i++) { - _ensure_rid(i); - TS->font_set_transform(cache[i], transform); - } - emit_changed(); - } -} - -Transform2D FontData::get_transform() const { - return transform; -} - -void FontData::set_oversampling(real_t p_oversampling) { +void FontFile::set_oversampling(real_t p_oversampling) { if (oversampling != p_oversampling) { oversampling = p_oversampling; for (int i = 0; i < cache.size(); i++) { @@ -1436,17 +2010,20 @@ void FontData::set_oversampling(real_t p_oversampling) { } } -real_t FontData::get_oversampling() const { +real_t FontFile::get_oversampling() const { return oversampling; } -RID FontData::find_cache(const Dictionary &p_variation_coordinates) const { +RID FontFile::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform) const { // Find existing variation cache. const Dictionary &supported_coords = get_supported_variation_list(); for (int i = 0; i < cache.size(); i++) { if (cache[i].is_valid()) { const Dictionary &cache_var = TS->font_get_variation_coordinates(cache[i]); bool match = true; + match = match && (TS->font_get_face_index(cache[i]) == p_face_index); + match = match && (TS->font_get_embolden(cache[i]) == p_strength); + match = match && (TS->font_get_transform(cache[i]) == p_transform); for (const Variant *V = supported_coords.next(nullptr); V && match; V = supported_coords.next(V)) { const Vector3 &def = supported_coords[*V]; @@ -1482,19 +2059,28 @@ RID FontData::find_cache(const Dictionary &p_variation_coordinates) const { int idx = cache.size(); _ensure_rid(idx); TS->font_set_variation_coordinates(cache[idx], p_variation_coordinates); + TS->font_set_face_index(cache[idx], p_face_index); + TS->font_set_embolden(cache[idx], p_strength); + TS->font_set_transform(cache[idx], p_transform); return cache[idx]; } -int FontData::get_cache_count() const { +RID FontFile::_get_rid() const { + _ensure_rid(0); + return cache[0]; +} + +int FontFile::get_cache_count() const { return cache.size(); } -void FontData::clear_cache() { +void FontFile::clear_cache() { _clear_cache(); cache.clear(); + emit_changed(); } -void FontData::remove_cache(int p_cache_index) { +void FontFile::remove_cache(int p_cache_index) { ERR_FAIL_INDEX(p_cache_index, cache.size()); if (cache[p_cache_index].is_valid()) { TS->free_rid(cache.write[p_cache_index]); @@ -1503,952 +2089,607 @@ void FontData::remove_cache(int p_cache_index) { emit_changed(); } -Array FontData::get_size_cache_list(int p_cache_index) const { +Array FontFile::get_size_cache_list(int p_cache_index) const { ERR_FAIL_COND_V(p_cache_index < 0, Array()); _ensure_rid(p_cache_index); return TS->font_get_size_cache_list(cache[p_cache_index]); } -void FontData::clear_size_cache(int p_cache_index) { +void FontFile::clear_size_cache(int p_cache_index) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_clear_size_cache(cache[p_cache_index]); } -void FontData::remove_size_cache(int p_cache_index, const Vector2i &p_size) { +void FontFile::remove_size_cache(int p_cache_index, const Vector2i &p_size) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_remove_size_cache(cache[p_cache_index], p_size); } -void FontData::set_variation_coordinates(int p_cache_index, const Dictionary &p_variation_coordinates) { +void FontFile::set_variation_coordinates(int p_cache_index, const Dictionary &p_variation_coordinates) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_variation_coordinates(cache[p_cache_index], p_variation_coordinates); - emit_changed(); } -Dictionary FontData::get_variation_coordinates(int p_cache_index) const { +Dictionary FontFile::get_variation_coordinates(int p_cache_index) const { ERR_FAIL_COND_V(p_cache_index < 0, Dictionary()); _ensure_rid(p_cache_index); return TS->font_get_variation_coordinates(cache[p_cache_index]); } -void FontData::set_ascent(int p_cache_index, int p_size, real_t p_ascent) { +void FontFile::set_embolden(int p_cache_index, float p_strength) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_set_embolden(cache[p_cache_index], p_strength); +} + +float FontFile::get_embolden(int p_cache_index) const { + ERR_FAIL_COND_V(p_cache_index < 0, 0.f); + _ensure_rid(p_cache_index); + return TS->font_get_embolden(cache[p_cache_index]); +} + +void FontFile::set_transform(int p_cache_index, Transform2D p_transform) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_set_transform(cache[p_cache_index], p_transform); +} + +Transform2D FontFile::get_transform(int p_cache_index) const { + ERR_FAIL_COND_V(p_cache_index < 0, Transform2D()); + _ensure_rid(p_cache_index); + return TS->font_get_transform(cache[p_cache_index]); +} + +void FontFile::set_face_index(int p_cache_index, int64_t p_index) { + ERR_FAIL_COND(p_cache_index < 0); + ERR_FAIL_COND(p_index < 0); + ERR_FAIL_COND(p_index >= 0x7FFF); + + _ensure_rid(p_cache_index); + TS->font_set_face_index(cache[p_cache_index], p_index); +} + +int64_t FontFile::get_face_index(int p_cache_index) const { + ERR_FAIL_COND_V(p_cache_index < 0, 0); + _ensure_rid(p_cache_index); + return TS->font_get_face_index(cache[p_cache_index]); +} + +void FontFile::set_cache_ascent(int p_cache_index, int p_size, real_t p_ascent) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_ascent(cache[p_cache_index], p_size, p_ascent); } -real_t FontData::get_ascent(int p_cache_index, int p_size) const { +real_t FontFile::get_cache_ascent(int p_cache_index, int p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, 0.f); _ensure_rid(p_cache_index); return TS->font_get_ascent(cache[p_cache_index], p_size); } -void FontData::set_descent(int p_cache_index, int p_size, real_t p_descent) { +void FontFile::set_cache_descent(int p_cache_index, int p_size, real_t p_descent) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_descent(cache[p_cache_index], p_size, p_descent); } -real_t FontData::get_descent(int p_cache_index, int p_size) const { +real_t FontFile::get_cache_descent(int p_cache_index, int p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, 0.f); _ensure_rid(p_cache_index); return TS->font_get_descent(cache[p_cache_index], p_size); } -void FontData::set_underline_position(int p_cache_index, int p_size, real_t p_underline_position) { +void FontFile::set_cache_underline_position(int p_cache_index, int p_size, real_t p_underline_position) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_underline_position(cache[p_cache_index], p_size, p_underline_position); } -real_t FontData::get_underline_position(int p_cache_index, int p_size) const { +real_t FontFile::get_cache_underline_position(int p_cache_index, int p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, 0.f); _ensure_rid(p_cache_index); return TS->font_get_underline_position(cache[p_cache_index], p_size); } -void FontData::set_underline_thickness(int p_cache_index, int p_size, real_t p_underline_thickness) { +void FontFile::set_cache_underline_thickness(int p_cache_index, int p_size, real_t p_underline_thickness) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_underline_thickness(cache[p_cache_index], p_size, p_underline_thickness); } -real_t FontData::get_underline_thickness(int p_cache_index, int p_size) const { +real_t FontFile::get_cache_underline_thickness(int p_cache_index, int p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, 0.f); _ensure_rid(p_cache_index); return TS->font_get_underline_thickness(cache[p_cache_index], p_size); } -void FontData::set_scale(int p_cache_index, int p_size, real_t p_scale) { +void FontFile::set_cache_scale(int p_cache_index, int p_size, real_t p_scale) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_scale(cache[p_cache_index], p_size, p_scale); } -real_t FontData::get_scale(int p_cache_index, int p_size) const { +real_t FontFile::get_cache_scale(int p_cache_index, int p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, 0.f); _ensure_rid(p_cache_index); return TS->font_get_scale(cache[p_cache_index], p_size); } -void FontData::set_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing, int p_value) { - ERR_FAIL_COND(p_cache_index < 0); - _ensure_rid(p_cache_index); - TS->font_set_spacing(cache[p_cache_index], p_size, p_spacing, p_value); -} - -int FontData::get_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing) const { - ERR_FAIL_COND_V(p_cache_index < 0, 0); - _ensure_rid(p_cache_index); - return TS->font_get_spacing(cache[p_cache_index], p_size, p_spacing); -} - -int FontData::get_texture_count(int p_cache_index, const Vector2i &p_size) const { +int FontFile::get_texture_count(int p_cache_index, const Vector2i &p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, 0); _ensure_rid(p_cache_index); return TS->font_get_texture_count(cache[p_cache_index], p_size); } -void FontData::clear_textures(int p_cache_index, const Vector2i &p_size) { +void FontFile::clear_textures(int p_cache_index, const Vector2i &p_size) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_clear_textures(cache[p_cache_index], p_size); } -void FontData::remove_texture(int p_cache_index, const Vector2i &p_size, int p_texture_index) { +void FontFile::remove_texture(int p_cache_index, const Vector2i &p_size, int p_texture_index) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_remove_texture(cache[p_cache_index], p_size, p_texture_index); } -void FontData::set_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) { +void FontFile::set_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_texture_image(cache[p_cache_index], p_size, p_texture_index, p_image); } -Ref<Image> FontData::get_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index) const { +Ref<Image> FontFile::get_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index) const { ERR_FAIL_COND_V(p_cache_index < 0, Ref<Image>()); _ensure_rid(p_cache_index); return TS->font_get_texture_image(cache[p_cache_index], p_size, p_texture_index); } -void FontData::set_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) { +void FontFile::set_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_texture_offsets(cache[p_cache_index], p_size, p_texture_index, p_offset); } -PackedInt32Array FontData::get_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index) const { +PackedInt32Array FontFile::get_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index) const { ERR_FAIL_COND_V(p_cache_index < 0, PackedInt32Array()); _ensure_rid(p_cache_index); return TS->font_get_texture_offsets(cache[p_cache_index], p_size, p_texture_index); } -Array FontData::get_glyph_list(int p_cache_index, const Vector2i &p_size) const { +Array FontFile::get_glyph_list(int p_cache_index, const Vector2i &p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, Array()); _ensure_rid(p_cache_index); return TS->font_get_glyph_list(cache[p_cache_index], p_size); } -void FontData::clear_glyphs(int p_cache_index, const Vector2i &p_size) { +void FontFile::clear_glyphs(int p_cache_index, const Vector2i &p_size) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_clear_glyphs(cache[p_cache_index], p_size); } -void FontData::remove_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) { +void FontFile::remove_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_remove_glyph(cache[p_cache_index], p_size, p_glyph); } -void FontData::set_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph, const Vector2 &p_advance) { +void FontFile::set_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph, const Vector2 &p_advance) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_glyph_advance(cache[p_cache_index], p_size, p_glyph, p_advance); } -Vector2 FontData::get_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph) const { +Vector2 FontFile::get_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph) const { ERR_FAIL_COND_V(p_cache_index < 0, Vector2()); _ensure_rid(p_cache_index); return TS->font_get_glyph_advance(cache[p_cache_index], p_size, p_glyph); } -void FontData::set_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) { +void FontFile::set_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_glyph_offset(cache[p_cache_index], p_size, p_glyph, p_offset); } -Vector2 FontData::get_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { +Vector2 FontFile::get_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { ERR_FAIL_COND_V(p_cache_index < 0, Vector2()); _ensure_rid(p_cache_index); return TS->font_get_glyph_offset(cache[p_cache_index], p_size, p_glyph); } -void FontData::set_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) { +void FontFile::set_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_glyph_size(cache[p_cache_index], p_size, p_glyph, p_gl_size); } -Vector2 FontData::get_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { +Vector2 FontFile::get_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { ERR_FAIL_COND_V(p_cache_index < 0, Vector2()); _ensure_rid(p_cache_index); return TS->font_get_glyph_size(cache[p_cache_index], p_size, p_glyph); } -void FontData::set_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) { +void FontFile::set_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_glyph_uv_rect(cache[p_cache_index], p_size, p_glyph, p_uv_rect); } -Rect2 FontData::get_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { +Rect2 FontFile::get_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { ERR_FAIL_COND_V(p_cache_index < 0, Rect2()); _ensure_rid(p_cache_index); return TS->font_get_glyph_uv_rect(cache[p_cache_index], p_size, p_glyph); } -void FontData::set_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) { +void FontFile::set_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_glyph_texture_idx(cache[p_cache_index], p_size, p_glyph, p_texture_idx); } -int FontData::get_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { +int FontFile::get_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { ERR_FAIL_COND_V(p_cache_index < 0, 0); _ensure_rid(p_cache_index); return TS->font_get_glyph_texture_idx(cache[p_cache_index], p_size, p_glyph); } -Array FontData::get_kerning_list(int p_cache_index, int p_size) const { +Array FontFile::get_kerning_list(int p_cache_index, int p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, Array()); _ensure_rid(p_cache_index); return TS->font_get_kerning_list(cache[p_cache_index], p_size); } -void FontData::clear_kerning_map(int p_cache_index, int p_size) { +void FontFile::clear_kerning_map(int p_cache_index, int p_size) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_clear_kerning_map(cache[p_cache_index], p_size); } -void FontData::remove_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) { +void FontFile::remove_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_remove_kerning(cache[p_cache_index], p_size, p_glyph_pair); } -void FontData::set_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) { +void FontFile::set_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_kerning(cache[p_cache_index], p_size, p_glyph_pair, p_kerning); } -Vector2 FontData::get_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) const { +Vector2 FontFile::get_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) const { ERR_FAIL_COND_V(p_cache_index < 0, Vector2()); _ensure_rid(p_cache_index); return TS->font_get_kerning(cache[p_cache_index], p_size, p_glyph_pair); } -void FontData::render_range(int p_cache_index, const Vector2i &p_size, char32_t p_start, char32_t p_end) { +void FontFile::render_range(int p_cache_index, const Vector2i &p_size, char32_t p_start, char32_t p_end) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_render_range(cache[p_cache_index], p_size, p_start, p_end); } -void FontData::render_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_index) { +void FontFile::render_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_index) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_render_glyph(cache[p_cache_index], p_size, p_index); } -RID FontData::get_cache_rid(int p_cache_index) const { - ERR_FAIL_COND_V(p_cache_index < 0, RID()); - _ensure_rid(p_cache_index); - return cache[p_cache_index]; -} - -bool FontData::is_language_supported(const String &p_language) const { - _ensure_rid(0); - return TS->font_is_language_supported(cache[0], p_language); -} - -void FontData::set_language_support_override(const String &p_language, bool p_supported) { +void FontFile::set_language_support_override(const String &p_language, bool p_supported) { _ensure_rid(0); TS->font_set_language_support_override(cache[0], p_language, p_supported); } -bool FontData::get_language_support_override(const String &p_language) const { +bool FontFile::get_language_support_override(const String &p_language) const { _ensure_rid(0); return TS->font_get_language_support_override(cache[0], p_language); } -void FontData::remove_language_support_override(const String &p_language) { +void FontFile::remove_language_support_override(const String &p_language) { _ensure_rid(0); TS->font_remove_language_support_override(cache[0], p_language); } -Vector<String> FontData::get_language_support_overrides() const { +Vector<String> FontFile::get_language_support_overrides() const { _ensure_rid(0); return TS->font_get_language_support_overrides(cache[0]); } -bool FontData::is_script_supported(const String &p_script) const { - _ensure_rid(0); - return TS->font_is_script_supported(cache[0], p_script); -} - -void FontData::set_script_support_override(const String &p_script, bool p_supported) { +void FontFile::set_script_support_override(const String &p_script, bool p_supported) { _ensure_rid(0); TS->font_set_script_support_override(cache[0], p_script, p_supported); } -bool FontData::get_script_support_override(const String &p_script) const { +bool FontFile::get_script_support_override(const String &p_script) const { _ensure_rid(0); return TS->font_get_script_support_override(cache[0], p_script); } -void FontData::remove_script_support_override(const String &p_script) { +void FontFile::remove_script_support_override(const String &p_script) { _ensure_rid(0); TS->font_remove_script_support_override(cache[0], p_script); } -Vector<String> FontData::get_script_support_overrides() const { +Vector<String> FontFile::get_script_support_overrides() const { _ensure_rid(0); return TS->font_get_script_support_overrides(cache[0]); } -void FontData::set_opentype_feature_overrides(const Dictionary &p_overrides) { +void FontFile::set_opentype_feature_overrides(const Dictionary &p_overrides) { _ensure_rid(0); TS->font_set_opentype_feature_overrides(cache[0], p_overrides); } -Dictionary FontData::get_opentype_feature_overrides() const { +Dictionary FontFile::get_opentype_feature_overrides() const { _ensure_rid(0); return TS->font_get_opentype_feature_overrides(cache[0]); } -bool FontData::has_char(char32_t p_char) const { - _ensure_rid(0); - return TS->font_has_char(cache[0], p_char); -} - -String FontData::get_supported_chars() const { - _ensure_rid(0); - return TS->font_get_supported_chars(cache[0]); -} - -int32_t FontData::get_glyph_index(int p_size, char32_t p_char, char32_t p_variation_selector) const { +int32_t FontFile::get_glyph_index(int p_size, char32_t p_char, char32_t p_variation_selector) const { _ensure_rid(0); return TS->font_get_glyph_index(cache[0], p_size, p_char, p_variation_selector); } -Dictionary FontData::get_supported_feature_list() const { - _ensure_rid(0); - return TS->font_supported_feature_list(cache[0]); -} - -Dictionary FontData::get_supported_variation_list() const { - _ensure_rid(0); - return TS->font_supported_variation_list(cache[0]); -} - -FontData::FontData() { +FontFile::FontFile() { /* NOP */ } -FontData::~FontData() { - _clear_cache(); +FontFile::~FontFile() { + reset_state(); } /*************************************************************************/ +/* FontVariation */ +/*************************************************************************/ -void Font::_data_changed() { - for (int i = 0; i < rids.size(); i++) { - rids.write[i] = RID(); - } - emit_changed(); -} - -void Font::_ensure_rid(int p_index) const { - // Find or create cache record. - if (!rids[p_index].is_valid() && data[p_index].is_valid()) { - rids.write[p_index] = data[p_index]->find_cache(variation_coordinates); - } -} +void FontVariation::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_base_font", "font"), &FontVariation::set_base_font); + ClassDB::bind_method(D_METHOD("get_base_font"), &FontVariation::get_base_font); -void Font::_bind_methods() { - ClassDB::bind_method(D_METHOD("add_data", "data"), &Font::add_data); - ClassDB::bind_method(D_METHOD("set_data", "idx", "data"), &Font::set_data); - ClassDB::bind_method(D_METHOD("get_data_count"), &Font::get_data_count); - ClassDB::bind_method(D_METHOD("get_data", "idx"), &Font::get_data); - ClassDB::bind_method(D_METHOD("get_data_rid", "idx"), &Font::get_data_rid); - ClassDB::bind_method(D_METHOD("clear_data"), &Font::clear_data); - ClassDB::bind_method(D_METHOD("remove_data", "idx"), &Font::remove_data); - - ClassDB::bind_method(D_METHOD("set_variation_coordinates", "variation_coordinates"), &Font::set_variation_coordinates); - ClassDB::bind_method(D_METHOD("get_variation_coordinates"), &Font::get_variation_coordinates); - ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "variation_coordinates"), "set_variation_coordinates", "get_variation_coordinates"); - - ClassDB::bind_method(D_METHOD("set_spacing", "spacing", "value"), &Font::set_spacing); - ClassDB::bind_method(D_METHOD("get_spacing", "spacing"), &Font::get_spacing); + ClassDB::bind_method(D_METHOD("set_variation_opentype", "coords"), &FontVariation::set_variation_opentype); + ClassDB::bind_method(D_METHOD("get_variation_opentype"), &FontVariation::get_variation_opentype); - ADD_GROUP("Extra Spacing", "spacing"); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_top"), "set_spacing", "get_spacing", TextServer::SPACING_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_bottom"), "set_spacing", "get_spacing", TextServer::SPACING_BOTTOM); + ClassDB::bind_method(D_METHOD("set_variation_embolden", "strength"), &FontVariation::set_variation_embolden); + ClassDB::bind_method(D_METHOD("get_variation_embolden"), &FontVariation::get_variation_embolden); - ClassDB::bind_method(D_METHOD("get_height", "size"), &Font::get_height, DEFVAL(DEFAULT_FONT_SIZE)); - ClassDB::bind_method(D_METHOD("get_ascent", "size"), &Font::get_ascent, DEFVAL(DEFAULT_FONT_SIZE)); - ClassDB::bind_method(D_METHOD("get_descent", "size"), &Font::get_descent, DEFVAL(DEFAULT_FONT_SIZE)); - ClassDB::bind_method(D_METHOD("get_underline_position", "size"), &Font::get_underline_position, DEFVAL(DEFAULT_FONT_SIZE)); - ClassDB::bind_method(D_METHOD("get_underline_thickness", "size"), &Font::get_underline_thickness, DEFVAL(DEFAULT_FONT_SIZE)); + ClassDB::bind_method(D_METHOD("set_variation_face_index", "face_index"), &FontVariation::set_variation_face_index); + ClassDB::bind_method(D_METHOD("get_variation_face_index"), &FontVariation::get_variation_face_index); - ClassDB::bind_method(D_METHOD("get_string_size", "text", "size", "alignment", "width", "flags"), &Font::get_string_size, DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); - ClassDB::bind_method(D_METHOD("get_multiline_string_size", "text", "width", "size", "flags"), &Font::get_multiline_string_size, DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND)); + ClassDB::bind_method(D_METHOD("set_variation_transform", "transform"), &FontVariation::set_variation_transform); + ClassDB::bind_method(D_METHOD("get_variation_transform"), &FontVariation::get_variation_transform); - ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "alignment", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); - ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "alignment", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_multiline_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); + ClassDB::bind_method(D_METHOD("set_opentype_features", "features"), &FontVariation::set_opentype_features); - ClassDB::bind_method(D_METHOD("get_char_size", "char", "next", "size"), &Font::get_char_size, DEFVAL(0), DEFVAL(DEFAULT_FONT_SIZE)); - ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "next", "size", "modulate", "outline_size", "outline_modulate"), &Font::draw_char, DEFVAL(0), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0))); + ClassDB::bind_method(D_METHOD("set_spacing", "spacing", "value"), &FontVariation::set_spacing); - ClassDB::bind_method(D_METHOD("has_char", "char"), &Font::has_char); - ClassDB::bind_method(D_METHOD("get_supported_chars"), &Font::get_supported_chars); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "base_font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_base_font", "get_base_font"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Font")), "set_fallbacks", "get_fallbacks"); - ClassDB::bind_method(D_METHOD("update_changes"), &Font::update_changes); + ADD_GROUP("Variation", "variation"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "variation_opentype"), "set_variation_opentype", "get_variation_opentype"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "variation_face_index"), "set_variation_face_index", "get_variation_face_index"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "variation_embolden", PROPERTY_HINT_RANGE, "-2,2,0.01"), "set_variation_embolden", "get_variation_embolden"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "variation_transform", PROPERTY_HINT_NONE, "suffix:px"), "set_variation_transform", "get_variation_transform"); - ClassDB::bind_method(D_METHOD("get_rids"), &Font::get_rids); -} + ADD_GROUP("OpenType Features", "opentype"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "opentype_features"), "set_opentype_features", "get_opentype_features"); -bool Font::_set(const StringName &p_name, const Variant &p_value) { - Vector<String> tokens = p_name.operator String().split("/"); -#ifndef DISABLE_DEPRECATED - if (tokens.size() == 1 && tokens[0] == "font_data") { - // Compatibility, DynamicFont main data. - Ref<FontData> fd = p_value; - if (fd.is_valid()) { - add_data(fd); - return true; - } - return false; - } else if (tokens.size() == 2 && tokens[0] == "fallback") { - // Compatibility, DynamicFont fallback data. - Ref<FontData> fd = p_value; - if (fd.is_valid()) { - add_data(fd); - return true; - } - return false; - } else if (tokens.size() == 1 && tokens[0] == "fallback") { - // Compatibility, BitmapFont fallback data. - Ref<Font> f = p_value; - if (f.is_valid()) { - for (int i = 0; i < f->get_data_count(); i++) { - add_data(f->get_data(i)); - } - return true; - } - return false; - } -#endif /* DISABLE_DEPRECATED */ - if (tokens.size() == 2 && tokens[0] == "data") { - int idx = tokens[1].to_int(); - Ref<FontData> fd = p_value; - if (fd.is_valid()) { - if (idx == data.size()) { - add_data(fd); - return true; - } else if (idx >= 0 && idx < data.size()) { - set_data(idx, fd); - return true; - } else { - return false; - } - } else if (idx >= 0 && idx < data.size()) { - remove_data(idx); - return true; - } - } - return false; + ADD_GROUP("Extra Spacing", "spacing"); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_glyph", PROPERTY_HINT_NONE, "suffix:px"), "set_spacing", "get_spacing", TextServer::SPACING_GLYPH); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_space", PROPERTY_HINT_NONE, "suffix:px"), "set_spacing", "get_spacing", TextServer::SPACING_SPACE); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_top", PROPERTY_HINT_NONE, "suffix:px"), "set_spacing", "get_spacing", TextServer::SPACING_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_bottom", PROPERTY_HINT_NONE, "suffix:px"), "set_spacing", "get_spacing", TextServer::SPACING_BOTTOM); } -bool Font::_get(const StringName &p_name, Variant &r_ret) const { - Vector<String> tokens = p_name.operator String().split("/"); - if (tokens.size() == 2 && tokens[0] == "data") { - int idx = tokens[1].to_int(); +void FontVariation::_update_rids() const { + Ref<Font> f = _get_base_font_or_default(); - if (idx == data.size()) { - r_ret = Ref<FontData>(); - return true; - } else if (idx >= 0 && idx < data.size()) { - r_ret = get_data(idx); - return true; + rids.clear(); + if (fallbacks.is_empty() && f.is_valid()) { + RID rid = _get_rid(); + if (rid.is_valid()) { + rids.push_back(rid); } - } - - return false; -} -void Font::_get_property_list(List<PropertyInfo> *p_list) const { - for (int i = 0; i < data.size(); i++) { - p_list->push_back(PropertyInfo(Variant::OBJECT, "data/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "FontData")); + const TypedArray<Font> &base_fallbacks = f->get_fallbacks(); + for (int i = 0; i < base_fallbacks.size(); i++) { + _update_rids_fb(base_fallbacks[i], 0); + } + } else { + _update_rids_fb(const_cast<FontVariation *>(this), 0); } - p_list->push_back(PropertyInfo(Variant::OBJECT, "data/" + itos(data.size()), PROPERTY_HINT_RESOURCE_TYPE, "FontData")); + dirty_rids = false; } -void Font::reset_state() { - for (int i = 0; i < data.size(); i++) { - if (data[i].is_valid()) { - data.write[i]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED); - } +void FontVariation::reset_state() { + if (base_font.is_valid()) { + base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); + base_font.unref(); } - cache.clear(); - cache_wrap.clear(); - data.clear(); - rids.clear(); - variation_coordinates.clear(); - spacing_bottom = 0; - spacing_top = 0; -} - -Dictionary Font::get_feature_list() const { - Dictionary out; - for (int i = 0; i < data.size(); i++) { - Dictionary data_ftrs = data[i]->get_supported_feature_list(); - for (const Variant *ftr = data_ftrs.next(nullptr); ftr != nullptr; ftr = data_ftrs.next(ftr)) { - out[*ftr] = data_ftrs[*ftr]; - } + if (theme_font.is_valid()) { + theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); + theme_font.unref(); } - return out; -} -void Font::add_data(const Ref<FontData> &p_data) { - ERR_FAIL_COND(p_data.is_null()); - data.push_back(p_data); - rids.push_back(RID()); + variation = Variation(); + opentype_features = Dictionary(); - if (data[data.size() - 1].is_valid()) { - data.write[data.size() - 1]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED); - Dictionary data_var_list = p_data->get_supported_variation_list(); - for (int j = 0; j < data_var_list.size(); j++) { - int32_t tag = data_var_list.get_key_at_index(j); - Vector3i value = data_var_list.get_value_at_index(j); - if (!variation_coordinates.has(tag) && !variation_coordinates.has(TS->tag_to_name(tag))) { - variation_coordinates[TS->tag_to_name(tag)] = value.z; - } - } + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + extra_spacing[i] = 0; } - cache.clear(); - cache_wrap.clear(); - - emit_changed(); - notify_property_list_changed(); + Font::reset_state(); } -void Font::set_data(int p_idx, const Ref<FontData> &p_data) { - ERR_FAIL_COND(p_data.is_null()); - ERR_FAIL_INDEX(p_idx, data.size()); - - if (data[p_idx].is_valid()) { - data.write[p_idx]->disconnect(SNAME("changed"), callable_mp(this, &Font::_data_changed)); - } - - data.write[p_idx] = p_data; - rids.write[p_idx] = RID(); - Dictionary data_var_list = p_data->get_supported_variation_list(); - for (int j = 0; j < data_var_list.size(); j++) { - int32_t tag = data_var_list.get_key_at_index(j); - Vector3i value = data_var_list.get_value_at_index(j); - if (!variation_coordinates.has(tag) && !variation_coordinates.has(TS->tag_to_name(tag))) { - variation_coordinates[TS->tag_to_name(tag)] = value.z; +void FontVariation::set_base_font(const Ref<Font> &p_font) { + if (base_font != p_font) { + if (base_font.is_valid()) { + base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); } + base_font = p_font; + if (base_font.is_valid()) { + base_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); + } + _invalidate_rids(); + notify_property_list_changed(); } - - if (data[p_idx].is_valid()) { - data.write[p_idx]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED); - } - - cache.clear(); - cache_wrap.clear(); - - emit_changed(); - notify_property_list_changed(); -} - -int Font::get_data_count() const { - return data.size(); -} - -Ref<FontData> Font::get_data(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, data.size(), Ref<FontData>()); - return data[p_idx]; } -RID Font::get_data_rid(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, data.size(), RID()); - _ensure_rid(p_idx); - return rids[p_idx]; +Ref<Font> FontVariation::get_base_font() const { + return base_font; } -void Font::clear_data() { - for (int i = 0; i < data.size(); i++) { - if (data[i].is_valid()) { - data.write[i]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED); - } +Ref<Font> FontVariation::_get_base_font_or_default() const { + if (theme_font.is_valid()) { + theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids)); + theme_font.unref(); } - data.clear(); - rids.clear(); -} - -void Font::remove_data(int p_idx) { - ERR_FAIL_INDEX(p_idx, data.size()); - if (data[p_idx].is_valid()) { - data.write[p_idx]->disconnect(SNAME("changed"), callable_mp(this, &Font::_data_changed)); + if (base_font.is_valid()) { + return base_font; } - data.remove_at(p_idx); - rids.remove_at(p_idx); - - cache.clear(); - cache_wrap.clear(); + // Check the project-defined Theme resource. + if (Theme::get_project_default().is_valid()) { + List<StringName> theme_types; + Theme::get_project_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); - emit_changed(); - notify_property_list_changed(); -} + for (const StringName &E : theme_types) { + if (Theme::get_project_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + Ref<Font> f = Theme::get_project_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (f.is_valid()) { + theme_font = f; + theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); + } + return f; + } + } + } -void Font::set_variation_coordinates(const Dictionary &p_variation_coordinates) { - _data_changed(); - variation_coordinates = p_variation_coordinates; -} + // Lastly, fall back on the items defined in the default Theme, if they exist. + if (Theme::get_default().is_valid()) { + List<StringName> theme_types; + Theme::get_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); -Dictionary Font::get_variation_coordinates() const { - return variation_coordinates; -} + for (const StringName &E : theme_types) { + if (Theme::get_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (f.is_valid()) { + theme_font = f; + theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); + } + return f; + } + } -void Font::set_spacing(TextServer::SpacingType p_spacing, int p_value) { - _data_changed(); - switch (p_spacing) { - case TextServer::SPACING_TOP: { - spacing_top = p_value; - } break; - case TextServer::SPACING_BOTTOM: { - spacing_bottom = p_value; - } break; - default: { - ERR_FAIL_MSG("Invalid spacing type: " + itos(p_spacing)); - } break; + // If they don't exist, use any type to return the default/empty value. + Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName()); + if (f.is_valid()) { + theme_font = f; + theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); + } + return f; } -} -int Font::get_spacing(TextServer::SpacingType p_spacing) const { - switch (p_spacing) { - case TextServer::SPACING_TOP: { - return spacing_top; - } break; - case TextServer::SPACING_BOTTOM: { - return spacing_bottom; - } break; - default: { - ERR_FAIL_V_MSG(0, "Invalid spacing type: " + itos(p_spacing)); - } break; - } + return Ref<Font>(); } -real_t Font::get_height(int p_size) const { - real_t ret = 0.f; - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - ret = MAX(ret, TS->font_get_ascent(rids[i], p_size) + TS->font_get_descent(rids[i], p_size)); +void FontVariation::set_variation_opentype(const Dictionary &p_coords) { + if (variation.opentype != p_coords) { + variation.opentype = p_coords; + _invalidate_rids(); } - return ret + spacing_bottom + spacing_top; } -real_t Font::get_ascent(int p_size) const { - real_t ret = 0.f; - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - ret = MAX(ret, TS->font_get_ascent(rids[i], p_size)); - } - return ret + spacing_top; +Dictionary FontVariation::get_variation_opentype() const { + return variation.opentype; } -real_t Font::get_descent(int p_size) const { - real_t ret = 0.f; - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - ret = MAX(ret, TS->font_get_descent(rids[i], p_size)); +void FontVariation::set_variation_embolden(float p_strength) { + if (variation.embolden != p_strength) { + variation.embolden = p_strength; + _invalidate_rids(); } - return ret + spacing_bottom; } -real_t Font::get_underline_position(int p_size) const { - real_t ret = 0.f; - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - ret = MAX(ret, TS->font_get_underline_position(rids[i], p_size)); - } - return ret + spacing_top; +float FontVariation::get_variation_embolden() const { + return variation.embolden; } -real_t Font::get_underline_thickness(int p_size) const { - real_t ret = 0.f; - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - ret = MAX(ret, TS->font_get_underline_thickness(rids[i], p_size)); +void FontVariation::set_variation_transform(Transform2D p_transform) { + if (variation.transform != p_transform) { + variation.transform = p_transform; + _invalidate_rids(); } - return ret; } -Size2 Font::get_string_size(const String &p_text, int p_size, HorizontalAlignment p_alignment, float p_width, uint16_t p_flags) const { - ERR_FAIL_COND_V(data.is_empty(), Size2()); - - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - } - - uint64_t hash = p_text.hash64(); - if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { - hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash); - hash = hash_djb2_one_64(p_flags, hash); - } - hash = hash_djb2_one_64(p_size, hash); - - Ref<TextLine> buffer; - if (cache.has(hash)) { - buffer = cache.get(hash); - } else { - buffer.instantiate(); - buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); - cache.insert(hash, buffer); - } - return buffer->get_size(); +Transform2D FontVariation::get_variation_transform() const { + return variation.transform; } -Size2 Font::get_multiline_string_size(const String &p_text, float p_width, int p_size, uint16_t p_flags) const { - ERR_FAIL_COND_V(data.is_empty(), Size2()); - - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - } - - uint64_t hash = p_text.hash64(); - uint64_t wrp_hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash); - wrp_hash = hash_djb2_one_64(p_flags, wrp_hash); - wrp_hash = hash_djb2_one_64(p_size, wrp_hash); - - Ref<TextParagraph> lines_buffer; - if (cache_wrap.has(wrp_hash)) { - lines_buffer = cache_wrap.get(wrp_hash); - } else { - lines_buffer.instantiate(); - lines_buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); - lines_buffer->set_width(p_width); - lines_buffer->set_flags(p_flags); - cache_wrap.insert(wrp_hash, lines_buffer); - } - - Size2 ret; - for (int i = 0; i < lines_buffer->get_line_count(); i++) { - Size2 line_size = lines_buffer->get_line_size(i); - if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) { - ret.x = MAX(ret.x, line_size.x); - ret.y += line_size.y; - } else { - ret.y = MAX(ret.y, line_size.y); - ret.x += line_size.x; - } +void FontVariation::set_variation_face_index(int p_face_index) { + if (variation.face_index != p_face_index) { + variation.face_index = p_face_index; + _invalidate_rids(); } - return ret; } -void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const { - ERR_FAIL_COND(data.is_empty()); - - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - } - - uint64_t hash = p_text.hash64(); - if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { - hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash); - hash = hash_djb2_one_64(p_flags, hash); - } - hash = hash_djb2_one_64(p_size, hash); - - Ref<TextLine> buffer; - if (cache.has(hash)) { - buffer = cache.get(hash); - } else { - buffer.instantiate(); - buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); - cache.insert(hash, buffer); - } - - Vector2 ofs = p_pos; - if (buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) { - ofs.y -= buffer->get_line_ascent(); - } else { - ofs.x -= buffer->get_line_ascent(); - } - - buffer->set_width(p_width); - buffer->set_horizontal_alignment(p_alignment); - buffer->set_flags(p_flags); - - if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) { - buffer->draw_outline(p_canvas_item, ofs, p_outline_size, p_outline_modulate); - } - buffer->draw(p_canvas_item, ofs, p_modulate); +int FontVariation::get_variation_face_index() const { + return variation.face_index; } -void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const { - ERR_FAIL_COND(data.is_empty()); - - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - } - - uint64_t hash = p_text.hash64(); - uint64_t wrp_hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash); - wrp_hash = hash_djb2_one_64(p_flags, wrp_hash); - wrp_hash = hash_djb2_one_64(p_size, wrp_hash); - - Ref<TextParagraph> lines_buffer; - if (cache_wrap.has(wrp_hash)) { - lines_buffer = cache_wrap.get(wrp_hash); - } else { - lines_buffer.instantiate(); - lines_buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); - lines_buffer->set_width(p_width); - lines_buffer->set_flags(p_flags); - cache_wrap.insert(wrp_hash, lines_buffer); - } - - lines_buffer->set_alignment(p_alignment); - - Vector2 lofs = p_pos; - for (int i = 0; i < lines_buffer->get_line_count(); i++) { - if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) { - if (i == 0) { - lofs.y -= lines_buffer->get_line_ascent(0); - } - } else { - if (i == 0) { - lofs.x -= lines_buffer->get_line_ascent(0); - } - } - if (p_width > 0) { - lines_buffer->set_alignment(p_alignment); - } - - if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) { - lines_buffer->draw_line_outline(p_canvas_item, lofs, i, p_outline_size, p_outline_modulate); - } - lines_buffer->draw_line(p_canvas_item, lofs, i, p_modulate); - - Size2 line_size = lines_buffer->get_line_size(i); - if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) { - lofs.y += line_size.y; - } else { - lofs.x += line_size.x; - } - - if ((p_max_lines > 0) && (i >= p_max_lines)) { - return; - } +void FontVariation::set_opentype_features(const Dictionary &p_features) { + if (opentype_features != p_features) { + opentype_features = p_features; + _invalidate_rids(); } } -Size2 Font::get_char_size(char32_t p_char, char32_t p_next, int p_size) const { - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - if (data[i]->has_char(p_char)) { - int32_t glyph_a = TS->font_get_glyph_index(rids[i], p_size, p_char, 0); - Size2 ret = Size2(TS->font_get_glyph_advance(rids[i], p_size, glyph_a).x, TS->font_get_ascent(rids[i], p_size) + TS->font_get_descent(rids[i], p_size)); - if ((p_next != 0) && data[i]->has_char(p_next)) { - int32_t glyph_b = TS->font_get_glyph_index(rids[i], p_size, p_next, 0); - ret.x -= TS->font_get_kerning(rids[i], p_size, Vector2i(glyph_a, glyph_b)).x; - } - return ret; - } - } - return Size2(); +Dictionary FontVariation::get_opentype_features() const { + return opentype_features; } -real_t Font::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate) const { - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - if (data[i]->has_char(p_char)) { - int32_t glyph_a = TS->font_get_glyph_index(rids[i], p_size, p_char, 0); - real_t ret = TS->font_get_glyph_advance(rids[i], p_size, glyph_a).x; - if ((p_next != 0) && data[i]->has_char(p_next)) { - int32_t glyph_b = TS->font_get_glyph_index(rids[i], p_size, p_next, 0); - ret -= TS->font_get_kerning(rids[i], p_size, Vector2i(glyph_a, glyph_b)).x; - } - - if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) { - TS->font_draw_glyph_outline(rids[i], p_canvas_item, p_size, p_outline_size, p_pos, glyph_a, p_outline_modulate); - } - TS->font_draw_glyph(rids[i], p_canvas_item, p_size, p_pos, glyph_a, p_modulate); - return ret; - } +void FontVariation::set_spacing(TextServer::SpacingType p_spacing, int p_value) { + ERR_FAIL_INDEX((int)p_spacing, TextServer::SPACING_MAX); + if (extra_spacing[p_spacing] != p_value) { + extra_spacing[p_spacing] = p_value; + _invalidate_rids(); } - return 0; } -bool Font::has_char(char32_t p_char) const { - for (int i = 0; i < data.size(); i++) { - if (data[i]->has_char(p_char)) { - return true; - } - } - return false; +int FontVariation::get_spacing(TextServer::SpacingType p_spacing) const { + ERR_FAIL_INDEX_V((int)p_spacing, TextServer::SPACING_MAX, 0); + return extra_spacing[p_spacing]; } -String Font::get_supported_chars() const { - String chars; - for (int i = 0; i < data.size(); i++) { - String data_chars = data[i]->get_supported_chars(); - for (int j = 0; j < data_chars.length(); j++) { - if (chars.find_char(data_chars[j]) == -1) { - chars += data_chars[j]; - } - } +RID FontVariation::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform) const { + Ref<Font> f = _get_base_font_or_default(); + if (f.is_valid()) { + return f->find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform); } - return chars; + return RID(); } -Array Font::get_rids() const { - Array _rids; - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - if (rids[i].is_valid()) { - _rids.push_back(rids[i]); - } +RID FontVariation::_get_rid() const { + Ref<Font> f = _get_base_font_or_default(); + if (f.is_valid()) { + return f->find_variation(variation.opentype, variation.face_index, variation.embolden, variation.transform); } - return _rids; -} - -void Font::update_changes() { - emit_changed(); + return RID(); } -Font::Font() { - cache.set_capacity(128); - cache_wrap.set_capacity(32); +FontVariation::FontVariation() { + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + extra_spacing[i] = 0; + } } -Font::~Font() { - clear_data(); - cache.clear(); - cache_wrap.clear(); +FontVariation::~FontVariation() { + reset_state(); } |