[Complex Text Layouts] Implement TextServer interface. Implement Fallback TextServer.

This commit is contained in:
bruvzg 2020-08-05 09:25:28 +03:00
parent a8c2cc9028
commit 493da99269
No known key found for this signature in database
GPG key ID: FCED35F1CECE0D3A
28 changed files with 5442 additions and 16 deletions

View file

@ -157,6 +157,11 @@ Copyright: 2018, Source Foundry Authors
2003, Bitstream Inc. 2003, Bitstream Inc.
License: Expat and Bitstream Vera Fonts Copyright License: Expat and Bitstream Vera Fonts Copyright
Files: ./thirdparty/fonts/Tamsyn*.png
Comment: Tamsyn font
Copyright: 2015, Scott Fial
License: Tamsyn
Files: ./thirdparty/freetype/ Files: ./thirdparty/freetype/
Comment: The FreeType Project Comment: The FreeType Project
Copyright: 1996-2020, David Turner, Robert Wilhelm, and Werner Lemberg. Copyright: 1996-2020, David Turner, Robert Wilhelm, and Werner Lemberg.
@ -1633,6 +1638,18 @@ License: OFL-1.1
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE. FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE.
License: Tamsyn
Tamsyn font is free. You are hereby granted permission to use, copy, modify,
and distribute it as you see fit.
.
Tamsyn font is provided "as is" without any express or implied warranty.
.
The author makes no representations about the suitability of this font for
a particular purpose.
.
In no event will the author be held liable for damages arising from the use
of this font.
License: Zlib License: Zlib
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View file

@ -47,6 +47,7 @@ void MainLoop::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_APPLICATION_PAUSED); BIND_CONSTANT(NOTIFICATION_APPLICATION_PAUSED);
BIND_CONSTANT(NOTIFICATION_APPLICATION_FOCUS_IN); BIND_CONSTANT(NOTIFICATION_APPLICATION_FOCUS_IN);
BIND_CONSTANT(NOTIFICATION_APPLICATION_FOCUS_OUT); BIND_CONSTANT(NOTIFICATION_APPLICATION_FOCUS_OUT);
BIND_CONSTANT(NOTIFICATION_TEXT_SERVER_CHANGED);
ADD_SIGNAL(MethodInfo("on_request_permissions_result", PropertyInfo(Variant::STRING, "permission"), PropertyInfo(Variant::BOOL, "granted"))); ADD_SIGNAL(MethodInfo("on_request_permissions_result", PropertyInfo(Variant::STRING, "permission"), PropertyInfo(Variant::BOOL, "granted")));
}; };

View file

@ -56,6 +56,7 @@ public:
NOTIFICATION_APPLICATION_PAUSED = 2015, NOTIFICATION_APPLICATION_PAUSED = 2015,
NOTIFICATION_APPLICATION_FOCUS_IN = 2016, NOTIFICATION_APPLICATION_FOCUS_IN = 2016,
NOTIFICATION_APPLICATION_FOCUS_OUT = 2017, NOTIFICATION_APPLICATION_FOCUS_OUT = 2017,
NOTIFICATION_TEXT_SERVER_CHANGED = 2018,
}; };
virtual void init(); virtual void init();

View file

@ -209,7 +209,6 @@ void CharString::copy_from(const char *p_cstr) {
/* String */ /* String */
/*************************************************************************/ /*************************************************************************/
//TODO: move to TextServer
//kind of poor should be rewritten properly //kind of poor should be rewritten properly
String String::word_wrap(int p_chars_per_line) const { String String::word_wrap(int p_chars_per_line) const {
int from = 0; int from = 0;
@ -4796,7 +4795,7 @@ Vector<uint8_t> String::to_utf16_buffer() const {
Char16String charstr = s->utf16(); Char16String charstr = s->utf16();
Vector<uint8_t> retval; Vector<uint8_t> retval;
size_t len = charstr.length() * 2; size_t len = charstr.length() * sizeof(char16_t);
retval.resize(len); retval.resize(len);
uint8_t *w = retval.ptrw(); uint8_t *w = retval.ptrw();
copymem(w, (const void *)charstr.ptr(), len); copymem(w, (const void *)charstr.ptr(), len);
@ -4811,7 +4810,7 @@ Vector<uint8_t> String::to_utf32_buffer() const {
} }
Vector<uint8_t> retval; Vector<uint8_t> retval;
size_t len = s->length() * 4; size_t len = s->length() * sizeof(char32_t);
retval.resize(len); retval.resize(len);
uint8_t *w = retval.ptrw(); uint8_t *w = retval.ptrw();
copymem(w, (const void *)s->ptr(), len); copymem(w, (const void *)s->ptr(), len);

View file

@ -28,8 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/ /*************************************************************************/
#ifndef USTRING_H #ifndef USTRING_GODOT_H
#define USTRING_H #define USTRING_GODOT_H
// Note: Renamed to avoid conflict with ICU header with the same name.
#include "core/templates/cowdata.h" #include "core/templates/cowdata.h"
#include "core/templates/vector.h" #include "core/templates/vector.h"
@ -555,4 +556,4 @@ _FORCE_INLINE_ Vector<String> sarray(P... p_args) {
return arr; return arr;
} }
#endif // USTRING_H #endif // USTRING_GODOT_H

126
core/templates/lru.h Normal file
View file

@ -0,0 +1,126 @@
/*************************************************************************/
/* lru.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef LRU_H
#define LRU_H
#include "core/math/math_funcs.h"
#include "hash_map.h"
#include "list.h"
template <class TKey, class TData>
class LRUCache {
private:
struct Pair {
TKey key;
TData data;
Pair() {}
Pair(const TKey &p_key, const TData &p_data) :
key(p_key),
data(p_data) {
}
};
typedef typename List<Pair>::Element *Element;
List<Pair> _list;
HashMap<TKey, Element> _map;
size_t capacity;
public:
const TData *insert(const TKey &p_key, const TData &p_value) {
Element *e = _map.getptr(p_key);
Element n = _list.push_front(Pair(p_key, p_value));
if (e) {
_list.erase(*e);
_map.erase(p_key);
}
_map[p_key] = _list.front();
while (_map.size() > capacity) {
Element d = _list.back();
_map.erase(d->get().key);
_list.pop_back();
}
return &n->get().data;
}
void clear() {
_map.clear();
_list.clear();
}
bool has(const TKey &p_key) const {
return _map.getptr(p_key);
}
const TData &get(const TKey &p_key) {
Element *e = _map.getptr(p_key);
CRASH_COND(!e);
_list.move_to_front(*e);
return (*e)->get().data;
};
const TData *getptr(const TKey &p_key) {
Element *e = _map.getptr(p_key);
if (!e) {
return nullptr;
} else {
_list.move_to_front(*e);
return &(*e)->get().data;
}
}
_FORCE_INLINE_ size_t get_capacity() const { return capacity; }
void set_capacity(size_t p_capacity) {
if (capacity > 0) {
capacity = p_capacity;
while (_map.size() > capacity) {
Element d = _list.back();
_map.erase(d->get().key);
_list.pop_back();
}
}
}
LRUCache() {
capacity = 64;
}
LRUCache(int p_capacity) {
capacity = p_capacity;
}
};
#endif

View file

@ -418,7 +418,7 @@ struct _VariantCall {
String s; String s;
if (p_instance->size() > 0) { if (p_instance->size() > 0) {
const uint8_t *r = p_instance->ptr(); const uint8_t *r = p_instance->ptr();
s.parse_utf16((const char16_t *)r, p_instance->size() / 2); s.parse_utf16((const char16_t *)r, floor((double)p_instance->size() / (double)sizeof(char16_t)));
} }
return s; return s;
} }
@ -427,7 +427,7 @@ struct _VariantCall {
String s; String s;
if (p_instance->size() > 0) { if (p_instance->size() > 0) {
const uint8_t *r = p_instance->ptr(); const uint8_t *r = p_instance->ptr();
s = String((const char32_t *)r, p_instance->size() / 4); s = String((const char32_t *)r, floor((double)p_instance->size() / (double)sizeof(char32_t)));
} }
return s; return s;
} }

View file

@ -72,6 +72,7 @@
#include "servers/register_server_types.h" #include "servers/register_server_types.h"
#include "servers/rendering/rendering_server_raster.h" #include "servers/rendering/rendering_server_raster.h"
#include "servers/rendering/rendering_server_wrap_mt.h" #include "servers/rendering/rendering_server_wrap_mt.h"
#include "servers/text_server.h"
#include "servers/xr_server.h" #include "servers/xr_server.h"
#ifdef TESTS_ENABLED #ifdef TESTS_ENABLED
@ -113,6 +114,7 @@ static DisplayServer *display_server = nullptr;
static RenderingServer *rendering_server = nullptr; static RenderingServer *rendering_server = nullptr;
static CameraServer *camera_server = nullptr; static CameraServer *camera_server = nullptr;
static XRServer *xr_server = nullptr; static XRServer *xr_server = nullptr;
static TextServerManager *tsman = nullptr;
static PhysicsServer3D *physics_server = nullptr; static PhysicsServer3D *physics_server = nullptr;
static PhysicsServer2D *physics_2d_server = nullptr; static PhysicsServer2D *physics_2d_server = nullptr;
static NavigationServer3D *navigation_server = nullptr; static NavigationServer3D *navigation_server = nullptr;
@ -122,6 +124,7 @@ static bool _start_success = false;
// Drivers // Drivers
static int text_driver_idx = -1;
static int display_driver_idx = -1; static int display_driver_idx = -1;
static int audio_driver_idx = -1; static int audio_driver_idx = -1;
@ -304,7 +307,18 @@ void Main::print_help(const char *p_binary) {
OS::get_singleton()->print(")"); OS::get_singleton()->print(")");
} }
OS::get_singleton()->print("].\n"); OS::get_singleton()->print("].\n");
OS::get_singleton()->print(" --rendering-driver <driver> Rendering driver (depends on display driver).\n"); OS::get_singleton()->print(" --rendering-driver <driver> Rendering driver (depends on display driver).\n");
OS::get_singleton()->print(" --text-driver <driver> Text driver (Fonts, BiDi, shaping) [");
for (int i = 0; i < TextServerManager::get_interface_count(); i++) {
if (i > 0) {
OS::get_singleton()->print(", ");
}
OS::get_singleton()->print("'%s'", TextServerManager::get_interface_name(i).utf8().get_data());
}
OS::get_singleton()->print("].\n");
OS::get_singleton()->print("\n"); OS::get_singleton()->print("\n");
#ifndef SERVER_ENABLED #ifndef SERVER_ENABLED
@ -544,6 +558,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
I = args.front(); I = args.front();
String text_driver = "";
String display_driver = ""; String display_driver = "";
String audio_driver = ""; String audio_driver = "";
String tablet_driver = ""; String tablet_driver = "";
@ -649,6 +664,40 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
OS::get_singleton()->print("Missing audio driver argument, aborting.\n"); OS::get_singleton()->print("Missing audio driver argument, aborting.\n");
goto error; goto error;
} }
} else if (I->get() == "--text-driver") {
if (I->next()) {
text_driver = I->next()->get();
bool found = false;
for (int i = 0; i < TextServerManager::get_interface_count(); i++) {
if (text_driver == TextServerManager::get_interface_name(i)) {
found = true;
}
}
if (!found) {
OS::get_singleton()->print("Unknown text driver '%s', aborting.\nValid options are ",
text_driver.utf8().get_data());
for (int i = 0; i < TextServerManager::get_interface_count(); i++) {
if (i == TextServerManager::get_interface_count() - 1) {
OS::get_singleton()->print(" and ");
} else if (i != 0) {
OS::get_singleton()->print(", ");
}
OS::get_singleton()->print("'%s'", TextServerManager::get_interface_name(i).utf8().get_data());
}
OS::get_singleton()->print(".\n");
goto error;
}
N = I->next()->next();
} else {
OS::get_singleton()->print("Missing text driver argument, aborting.\n");
goto error;
}
} else if (I->get() == "--display-driver") { // force video driver } else if (I->get() == "--display-driver") { // force video driver
@ -1159,6 +1208,11 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
OS::get_singleton()->set_cmdline(execpath, main_args); OS::get_singleton()->set_cmdline(execpath, main_args);
GLOBAL_DEF("display/window/text_name", "");
if (text_driver == "") {
text_driver = GLOBAL_GET("display/window/text_name");
}
GLOBAL_DEF("rendering/quality/driver/driver_name", "Vulkan"); GLOBAL_DEF("rendering/quality/driver/driver_name", "Vulkan");
ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/driver/driver_name", ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/driver/driver_name",
PropertyInfo(Variant::STRING, PropertyInfo(Variant::STRING,
@ -1289,6 +1343,35 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
OS::get_singleton()->_render_thread_mode = OS::RenderThreadMode(rtm); OS::get_singleton()->_render_thread_mode = OS::RenderThreadMode(rtm);
} }
/* Determine text driver */
if (text_driver != "") {
/* Load user selected text server. */
for (int i = 0; i < TextServerManager::get_interface_count(); i++) {
if (text_driver == TextServerManager::get_interface_name(i)) {
text_driver_idx = i;
break;
}
}
}
if (text_driver_idx < 0) {
/* If not selected, use one with the most features available. */
int max_features = 0;
for (int i = 0; i < TextServerManager::get_interface_count(); i++) {
uint32_t ftrs = TextServerManager::get_interface_features(i);
int features = 0;
while (ftrs) {
features += ftrs & 1;
ftrs >>= 1;
}
if (features >= max_features) {
max_features = features;
text_driver_idx = i;
}
}
}
/* Determine audio and video drivers */ /* Determine audio and video drivers */
for (int i = 0; i < DisplayServer::get_create_function_count(); i++) { for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
@ -1388,6 +1471,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
error: error:
text_driver = "";
display_driver = ""; display_driver = "";
audio_driver = ""; audio_driver = "";
tablet_driver = ""; tablet_driver = "";
@ -1449,6 +1533,30 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
Thread::_main_thread_id = p_main_tid_override; Thread::_main_thread_id = p_main_tid_override;
} }
/* Initialize Text Server */
{
tsman = memnew(TextServerManager);
Error err;
TextServer *text_server = TextServerManager::initialize(text_driver_idx, err);
if (err != OK || text_server == nullptr) {
for (int i = 0; i < TextServerManager::get_interface_count(); i++) {
if (i == text_driver_idx) {
continue; //don't try the same twice
}
text_server = TextServerManager::initialize(i, err);
if (err == OK && text_server != nullptr) {
break;
}
}
}
if (err != OK || text_server == nullptr) {
ERR_PRINT("Unable to create TextServer, all text drivers failed.");
return err;
}
}
/* Initialize Input */ /* Initialize Input */
input = memnew(Input); input = memnew(Input);
@ -1459,23 +1567,21 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
String rendering_driver; // temp broken String rendering_driver; // temp broken
Error err; Error err;
display_server = DisplayServer::create(display_driver_idx, rendering_driver, window_mode, window_flags, display_server = DisplayServer::create(display_driver_idx, rendering_driver, window_mode, window_flags, window_size, err);
window_size, err); if (err != OK || display_server == nullptr) {
if (err != OK) {
//ok i guess we can't use this display server, try other ones //ok i guess we can't use this display server, try other ones
for (int i = 0; i < DisplayServer::get_create_function_count(); i++) { for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
if (i == display_driver_idx) { if (i == display_driver_idx) {
continue; //don't try the same twice continue; //don't try the same twice
} }
display_server = DisplayServer::create(display_driver_idx, rendering_driver, window_mode, window_flags, display_server = DisplayServer::create(i, rendering_driver, window_mode, window_flags, window_size, err);
window_size, err); if (err == OK && display_server != nullptr) {
if (err == OK) {
break; break;
} }
} }
} }
if (!display_server || err != OK) { if (err != OK || display_server == nullptr) {
ERR_PRINT("Unable to create DisplayServer, all display drivers failed."); ERR_PRINT("Unable to create DisplayServer, all display drivers failed.");
return err; return err;
} }
@ -2572,6 +2678,10 @@ void Main::cleanup() {
finalize_navigation_server(); finalize_navigation_server();
finalize_display(); finalize_display();
if (tsman) {
memdelete(tsman);
}
if (input) { if (input) {
memdelete(input); memdelete(input);
} }

View file

@ -0,0 +1,12 @@
#!/usr/bin/env python
Import("env")
Import("env_modules")
env_text_server_fb = env_modules.Clone()
env_text_server_fb.Append(
CPPPATH=[
"#thirdparty/freetype/include",
]
)
env_text_server_fb.add_source_files(env.modules_sources, "*.cpp")

View file

@ -0,0 +1,357 @@
/*************************************************************************/
/* bitmap_font_fb.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "bitmap_font_fb.h"
Error BitmapFontDataFallback::load_from_file(const String &p_filename, int p_base_size) {
_THREAD_SAFE_METHOD_
//fnt format used by angelcode bmfont
//http://www.angelcode.com/products/bmfont/
FileAccess *f = FileAccess::open(p_filename, FileAccess::READ);
ERR_FAIL_COND_V_MSG(!f, ERR_FILE_NOT_FOUND, "Can't open font: " + p_filename + ".");
while (true) {
String line = f->get_line();
int delimiter = line.find(" ");
String type = line.substr(0, delimiter);
int pos = delimiter + 1;
Map<String, String> keys;
while (pos < line.size() && line[pos] == ' ') {
pos++;
}
while (pos < line.size()) {
int eq = line.find("=", pos);
if (eq == -1) {
break;
}
String key = line.substr(pos, eq - pos);
int end = -1;
String value;
if (line[eq + 1] == '"') {
end = line.find("\"", eq + 2);
if (end == -1) {
break;
}
value = line.substr(eq + 2, end - 1 - eq - 1);
pos = end + 1;
} else {
end = line.find(" ", eq + 1);
if (end == -1) {
end = line.size();
}
value = line.substr(eq + 1, end - eq);
pos = end;
}
while (pos < line.size() && line[pos] == ' ') {
pos++;
}
keys[key] = value;
}
if (type == "info") {
if (keys.has("size")) {
base_size = keys["size"].to_int();
}
} else if (type == "common") {
if (keys.has("lineHeight")) {
height = keys["lineHeight"].to_int();
}
if (keys.has("base")) {
ascent = keys["base"].to_int();
}
} else if (type == "page") {
if (keys.has("file")) {
String base_dir = p_filename.get_base_dir();
String file = base_dir.plus_file(keys["file"]);
if (RenderingServer::get_singleton() != nullptr) {
Ref<Texture2D> tex = ResourceLoader::load(file);
if (tex.is_null()) {
ERR_PRINT("Can't load font texture!");
} else {
ERR_FAIL_COND_V_MSG(tex.is_null(), ERR_FILE_CANT_READ, "It's not a reference to a valid Texture object.");
textures.push_back(tex);
}
}
}
} else if (type == "char") {
Character c;
char32_t idx = 0;
if (keys.has("id")) {
idx = keys["id"].to_int();
}
if (keys.has("x")) {
c.rect.position.x = keys["x"].to_int();
}
if (keys.has("y")) {
c.rect.position.y = keys["y"].to_int();
}
if (keys.has("width")) {
c.rect.size.width = keys["width"].to_int();
}
if (keys.has("height")) {
c.rect.size.height = keys["height"].to_int();
}
if (keys.has("xoffset")) {
c.align.x = keys["xoffset"].to_int();
}
if (keys.has("yoffset")) {
c.align.y = keys["yoffset"].to_int();
}
if (keys.has("page")) {
c.texture_idx = keys["page"].to_int();
}
if (keys.has("xadvance")) {
c.advance.x = keys["xadvance"].to_int();
}
if (keys.has("yadvance")) {
c.advance.y = keys["yadvance"].to_int();
}
if (c.advance.x < 0) {
c.advance.x = c.rect.size.width + 1;
}
if (c.advance.y < 0) {
c.advance.y = c.rect.size.height + 1;
}
char_map[idx] = c;
} else if (type == "kerning") {
KerningPairKey kpk;
float k = 0;
if (keys.has("first")) {
kpk.A = keys["first"].to_int();
}
if (keys.has("second")) {
kpk.B = keys["second"].to_int();
}
if (keys.has("amount")) {
k = keys["amount"].to_int();
}
kerning_map[kpk] = k;
}
if (f->eof_reached()) {
break;
}
}
if (base_size == 0) {
base_size = height;
}
valid = true;
memdelete(f);
return OK;
}
Error BitmapFontDataFallback::load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(p_data == nullptr, ERR_CANT_CREATE);
ERR_FAIL_COND_V(p_size != sizeof(TextServer::BitmapFontData), ERR_CANT_CREATE);
const TextServer::BitmapFontData *data = (const TextServer::BitmapFontData *)p_data;
if (RenderingServer::get_singleton() != nullptr) {
Ref<Image> image = memnew(Image(data->img));
Ref<ImageTexture> tex = memnew(ImageTexture);
tex->create_from_image(image);
textures.push_back(tex);
}
for (int i = 0; i < data->charcount; i++) {
const int *c = &data->char_rects[i * 8];
Character chr;
chr.rect.position.x = c[1];
chr.rect.position.y = c[2];
chr.rect.size.x = c[3];
chr.rect.size.y = c[4];
chr.texture_idx = 0;
if (c[7] < 0) {
chr.advance.x = c[3];
} else {
chr.advance.x = c[7];
}
chr.align = Vector2(c[6], c[5]);
char_map[c[0]] = chr;
}
for (int i = 0; i < data->kerning_count; i++) {
KerningPairKey kpk;
kpk.A = data->kernings[i * 3 + 0];
kpk.B = data->kernings[i * 3 + 1];
if (data->kernings[i * 3 + 2] == 0 && kerning_map.has(kpk)) {
kerning_map.erase(kpk);
} else {
kerning_map[kpk] = data->kernings[i * 3 + 2];
}
}
height = data->height;
ascent = data->ascent;
base_size = p_base_size;
if (base_size == 0) {
base_size = height;
}
valid = true;
return OK;
}
float BitmapFontDataFallback::get_height(int p_size) const {
ERR_FAIL_COND_V(!valid, 0.f);
return height * (float(p_size) / float(base_size));
}
float BitmapFontDataFallback::get_ascent(int p_size) const {
ERR_FAIL_COND_V(!valid, 0.f);
return ascent * (float(p_size) / float(base_size));
}
float BitmapFontDataFallback::get_descent(int p_size) const {
ERR_FAIL_COND_V(!valid, 0.f);
return (height - ascent) * (float(p_size) / float(base_size));
}
float BitmapFontDataFallback::get_underline_position(int p_size) const {
ERR_FAIL_COND_V(!valid, 0.f);
return 2 * (float(p_size) / float(base_size));
}
float BitmapFontDataFallback::get_underline_thickness(int p_size) const {
ERR_FAIL_COND_V(!valid, 0.f);
return 1 * (float(p_size) / float(base_size));
}
void BitmapFontDataFallback::set_distance_field_hint(bool p_distance_field) {
distance_field_hint = p_distance_field;
}
bool BitmapFontDataFallback::get_distance_field_hint() const {
return distance_field_hint;
}
float BitmapFontDataFallback::get_base_size() const {
return base_size;
}
bool BitmapFontDataFallback::has_char(char32_t p_char) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(!valid, false);
return char_map.has(p_char);
}
String BitmapFontDataFallback::get_supported_chars() const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(!valid, String());
String chars;
const char32_t *k = nullptr;
while ((k = char_map.next(k))) {
chars += char32_t(*k);
}
return chars;
}
Vector2 BitmapFontDataFallback::get_advance(char32_t p_char, int p_size) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(!valid, Vector2());
const Character *c = char_map.getptr(p_char);
ERR_FAIL_COND_V(c == nullptr, Vector2());
return c->advance * (float(p_size) / float(base_size));
}
Vector2 BitmapFontDataFallback::get_kerning(char32_t p_char, char32_t p_next, int p_size) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(!valid, Vector2());
KerningPairKey kpk;
kpk.A = p_char;
kpk.B = p_next;
const Map<KerningPairKey, int>::Element *E = kerning_map.find(kpk);
if (E) {
return Vector2(-E->get() * (float(p_size) / float(base_size)), 0);
} else {
return Vector2();
}
}
Vector2 BitmapFontDataFallback::draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
_THREAD_SAFE_METHOD_
if (p_index == 0) {
return Vector2();
}
ERR_FAIL_COND_V(!valid, Vector2());
const Character *c = char_map.getptr(p_index);
ERR_FAIL_COND_V(c == nullptr, Vector2());
ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), Vector2());
if (c->texture_idx != -1) {
Point2 cpos = p_pos;
cpos += c->align * (float(p_size) / float(base_size));
cpos.y -= ascent * (float(p_size) / float(base_size));
if (RenderingServer::get_singleton() != nullptr) {
//if (distance_field_hint) { // Not implemented.
// RenderingServer::get_singleton()->canvas_item_set_distance_field_mode(p_canvas, true);
//}
RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, c->rect.size * (float(p_size) / float(base_size))), textures[c->texture_idx]->get_rid(), c->rect, p_color, false, false);
//if (distance_field_hint) {
// RenderingServer::get_singleton()->canvas_item_set_distance_field_mode(p_canvas, false);
//}
}
}
return c->advance * (float(p_size) / float(base_size));
}
Vector2 BitmapFontDataFallback::draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
_THREAD_SAFE_METHOD_
if (p_index == 0) {
return Vector2();
}
ERR_FAIL_COND_V(!valid, Vector2());
const Character *c = char_map.getptr(p_index);
ERR_FAIL_COND_V(c == nullptr, Vector2());
ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), Vector2());
// Not supported, return advance for compatibility.
return c->advance * (float(p_size) / float(base_size));
}

View file

@ -0,0 +1,107 @@
/*************************************************************************/
/* bitmap_font_fb.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef BITMAP_FONT_FALLBACK_H
#define BITMAP_FONT_FALLBACK_H
#include "font_fb.h"
struct BitmapFontDataFallback : public FontDataFallback {
_THREAD_SAFE_CLASS_
private:
Vector<Ref<Texture2D>> textures;
struct Character {
int texture_idx = 0;
Rect2 rect;
Vector2 align;
Vector2 advance = Vector2(-1, -1);
};
struct KerningPairKey {
union {
struct {
uint32_t A, B;
};
uint64_t pair = 0;
};
_FORCE_INLINE_ bool operator<(const KerningPairKey &p_r) const { return pair < p_r.pair; }
};
HashMap<char32_t, Character> char_map;
Map<KerningPairKey, int> kerning_map;
float height = 0.f;
float ascent = 0.f;
int base_size = 0;
bool distance_field_hint = false;
public:
virtual void clear_cache() override{};
virtual Error load_from_file(const String &p_filename, int p_base_size) override;
virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) override;
virtual float get_height(int p_size) const override;
virtual float get_ascent(int p_size) const override;
virtual float get_descent(int p_size) const override;
virtual float get_underline_position(int p_size) const override;
virtual float get_underline_thickness(int p_size) const override;
virtual void set_antialiased(bool p_antialiased) override{};
virtual bool get_antialiased() const override { return false; };
virtual void set_hinting(TextServer::Hinting p_hinting) override{};
virtual TextServer::Hinting get_hinting() const override { return TextServer::HINTING_NONE; };
virtual void set_distance_field_hint(bool p_distance_field) override;
virtual bool get_distance_field_hint() const override;
virtual void set_force_autohinter(bool p_enabeld) override{};
virtual bool get_force_autohinter() const override { return false; };
virtual bool has_outline() const override { return false; };
virtual float get_base_size() const override;
virtual bool has_char(char32_t p_char) const override;
virtual String get_supported_chars() const override;
virtual Vector2 get_advance(char32_t p_char, int p_size) const override;
virtual Vector2 get_kerning(char32_t p_char, char32_t p_next, int p_size) const override;
virtual Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override;
virtual Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override;
};
#endif // BITMAP_FONT_FALLBACK_H

View file

@ -0,0 +1,11 @@
def can_build(env, platform):
return env.module_check_dependencies("text_server_fb", ["freetype"])
def configure(env):
pass
def is_enabled():
# The module is disabled by default. Use module_text_server_fb_enabled=yes to enable it.
return False

View file

@ -0,0 +1,731 @@
/*************************************************************************/
/* dynamic_font_fb.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "dynamic_font_fb.h"
#include FT_STROKER_H
#include FT_ADVANCES_H
HashMap<String, Vector<uint8_t>> DynamicFontDataFallback::font_mem_cache;
DynamicFontDataFallback::DataAtSize *DynamicFontDataFallback::get_data_for_size(int p_size, int p_outline_size) {
ERR_FAIL_COND_V(!valid, nullptr);
ERR_FAIL_COND_V(p_size < 0 || p_size > UINT16_MAX, nullptr);
ERR_FAIL_COND_V(p_outline_size < 0 || p_outline_size > UINT16_MAX, nullptr);
CacheID id;
id.size = p_size;
id.outline_size = p_outline_size;
DataAtSize *fds = nullptr;
Map<CacheID, DataAtSize *>::Element *E = nullptr;
if (p_outline_size != 0) {
E = size_cache_outline.find(id);
} else {
E = size_cache.find(id);
}
if (E != nullptr) {
fds = E->get();
} else {
// FT_OPEN_STREAM is extremely slow only on Android.
if (OS::get_singleton()->get_name() == "Android" && font_mem == nullptr && font_path != String()) {
if (font_mem_cache.has(font_path)) {
font_mem = font_mem_cache[font_path].ptr();
font_mem_size = font_mem_cache[font_path].size();
} else {
FileAccess *f = FileAccess::open(font_path, FileAccess::READ);
if (!f) {
ERR_FAIL_V_MSG(nullptr, "Cannot open font file '" + font_path + "'.");
}
size_t len = f->get_len();
font_mem_cache[font_path] = Vector<uint8_t>();
Vector<uint8_t> &fontdata = font_mem_cache[font_path];
fontdata.resize(len);
f->get_buffer(fontdata.ptrw(), len);
font_mem = fontdata.ptr();
font_mem_size = len;
f->close();
}
}
int error = 0;
fds = memnew(DataAtSize);
if (font_mem == nullptr && font_path != String()) {
FileAccess *f = FileAccess::open(font_path, FileAccess::READ);
if (!f) {
memdelete(fds);
ERR_FAIL_V_MSG(nullptr, "Cannot open font file '" + font_path + "'.");
}
memset(&fds->stream, 0, sizeof(FT_StreamRec));
fds->stream.base = nullptr;
fds->stream.size = f->get_len();
fds->stream.pos = 0;
fds->stream.descriptor.pointer = f;
fds->stream.read = _ft_stream_io;
fds->stream.close = _ft_stream_close;
FT_Open_Args fargs;
memset(&fargs, 0, sizeof(FT_Open_Args));
fargs.flags = FT_OPEN_STREAM;
fargs.stream = &fds->stream;
error = FT_Open_Face(library, &fargs, 0, &fds->face);
} else if (font_mem) {
memset(&fds->stream, 0, sizeof(FT_StreamRec));
fds->stream.base = (unsigned char *)font_mem;
fds->stream.size = font_mem_size;
fds->stream.pos = 0;
FT_Open_Args fargs;
memset(&fargs, 0, sizeof(FT_Open_Args));
fargs.memory_base = (unsigned char *)font_mem;
fargs.memory_size = font_mem_size;
fargs.flags = FT_OPEN_MEMORY;
fargs.stream = &fds->stream;
error = FT_Open_Face(library, &fargs, 0, &fds->face);
} else {
memdelete(fds);
ERR_FAIL_V_MSG(nullptr, "DynamicFont uninitialized.");
}
if (error == FT_Err_Unknown_File_Format) {
memdelete(fds);
ERR_FAIL_V_MSG(nullptr, "Unknown font format.");
} else if (error) {
memdelete(fds);
ERR_FAIL_V_MSG(nullptr, "Error loading font.");
}
oversampling = TS->font_get_oversampling();
if (FT_HAS_COLOR(fds->face) && fds->face->num_fixed_sizes > 0) {
int best_match = 0;
int diff = ABS(p_size - ((int64_t)fds->face->available_sizes[0].width));
fds->scale_color_font = float(p_size * oversampling) / fds->face->available_sizes[0].width;
for (int i = 1; i < fds->face->num_fixed_sizes; i++) {
int ndiff = ABS(p_size - ((int64_t)fds->face->available_sizes[i].width));
if (ndiff < diff) {
best_match = i;
diff = ndiff;
fds->scale_color_font = float(p_size * oversampling) / fds->face->available_sizes[i].width;
}
}
FT_Select_Size(fds->face, best_match);
} else {
FT_Set_Pixel_Sizes(fds->face, 0, p_size * oversampling);
}
fds->size = p_size;
fds->ascent = (fds->face->size->metrics.ascender / 64.0) / oversampling * fds->scale_color_font;
fds->descent = (-fds->face->size->metrics.descender / 64.0) / oversampling * fds->scale_color_font;
fds->underline_position = -fds->face->underline_position / 64.0 / oversampling * fds->scale_color_font;
fds->underline_thickness = fds->face->underline_thickness / 64.0 / oversampling * fds->scale_color_font;
if (p_outline_size != 0) {
size_cache_outline[id] = fds;
} else {
size_cache[id] = fds;
}
}
return fds;
}
unsigned long DynamicFontDataFallback::_ft_stream_io(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count) {
FileAccess *f = (FileAccess *)stream->descriptor.pointer;
if (f->get_position() != offset) {
f->seek(offset);
}
if (count == 0) {
return 0;
}
return f->get_buffer(buffer, count);
}
void DynamicFontDataFallback::_ft_stream_close(FT_Stream stream) {
FileAccess *f = (FileAccess *)stream->descriptor.pointer;
f->close();
memdelete(f);
}
DynamicFontDataFallback::TexturePosition DynamicFontDataFallback::find_texture_pos_for_glyph(DynamicFontDataFallback::DataAtSize *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) {
TexturePosition ret;
ret.index = -1;
ret.x = 0;
ret.y = 0;
int mw = p_width;
int mh = p_height;
for (int i = 0; i < p_data->textures.size(); i++) {
const CharTexture &ct = p_data->textures[i];
if (RenderingServer::get_singleton() != nullptr) {
if (ct.texture->get_format() != p_image_format) {
continue;
}
}
if (mw > ct.texture_size || mh > ct.texture_size) { //too big for this texture
continue;
}
ret.y = 0x7FFFFFFF;
ret.x = 0;
for (int j = 0; j < ct.texture_size - mw; j++) {
int max_y = 0;
for (int k = j; k < j + mw; k++) {
int y = ct.offsets[k];
if (y > max_y) {
max_y = y;
}
}
if (max_y < ret.y) {
ret.y = max_y;
ret.x = j;
}
}
if (ret.y == 0x7FFFFFFF || ret.y + mh > ct.texture_size) {
continue; //fail, could not fit it here
}
ret.index = i;
break;
}
if (ret.index == -1) {
//could not find texture to fit, create one
ret.x = 0;
ret.y = 0;
int texsize = MAX(p_data->size * oversampling * 8, 256);
if (mw > texsize) {
texsize = mw; //special case, adapt to it?
}
if (mh > texsize) {
texsize = mh; //special case, adapt to it?
}
texsize = next_power_of_2(texsize);
texsize = MIN(texsize, 4096);
CharTexture tex;
tex.texture_size = texsize;
tex.imgdata.resize(texsize * texsize * p_color_size); //grayscale alpha
{
//zero texture
uint8_t *w = tex.imgdata.ptrw();
ERR_FAIL_COND_V(texsize * texsize * p_color_size > tex.imgdata.size(), ret);
// Initialize the texture to all-white pixels to prevent artifacts when the
// font is displayed at a non-default scale with filtering enabled.
if (p_color_size == 2) {
for (int i = 0; i < texsize * texsize * p_color_size; i += 2) { // FORMAT_LA8
w[i + 0] = 255;
w[i + 1] = 0;
}
} else if (p_color_size == 4) {
for (int i = 0; i < texsize * texsize * p_color_size; i += 4) { // FORMAT_RGBA8
w[i + 0] = 255;
w[i + 1] = 255;
w[i + 2] = 255;
w[i + 3] = 0;
}
} else {
ERR_FAIL_V(ret);
}
}
tex.offsets.resize(texsize);
for (int i = 0; i < texsize; i++) { //zero offsets
tex.offsets.write[i] = 0;
}
p_data->textures.push_back(tex);
ret.index = p_data->textures.size() - 1;
}
return ret;
}
DynamicFontDataFallback::Character DynamicFontDataFallback::Character::not_found() {
Character ch;
return ch;
}
DynamicFontDataFallback::Character DynamicFontDataFallback::bitmap_to_character(DynamicFontDataFallback::DataAtSize *p_data, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) {
int w = bitmap.width;
int h = bitmap.rows;
int mw = w + rect_margin * 2;
int mh = h + rect_margin * 2;
ERR_FAIL_COND_V(mw > 4096, Character::not_found());
ERR_FAIL_COND_V(mh > 4096, Character::not_found());
int color_size = bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 2;
Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8;
TexturePosition tex_pos = find_texture_pos_for_glyph(p_data, color_size, require_format, mw, mh);
ERR_FAIL_COND_V(tex_pos.index < 0, Character::not_found());
//fit character in char texture
CharTexture &tex = p_data->textures.write[tex_pos.index];
{
uint8_t *wr = tex.imgdata.ptrw();
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
int ofs = ((i + tex_pos.y + rect_margin) * tex.texture_size + j + tex_pos.x + rect_margin) * color_size;
ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), Character::not_found());
switch (bitmap.pixel_mode) {
case FT_PIXEL_MODE_MONO: {
int byte = i * bitmap.pitch + (j >> 3);
int bit = 1 << (7 - (j % 8));
wr[ofs + 0] = 255; //grayscale as 1
wr[ofs + 1] = (bitmap.buffer[byte] & bit) ? 255 : 0;
} break;
case FT_PIXEL_MODE_GRAY:
wr[ofs + 0] = 255; //grayscale as 1
wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j];
break;
case FT_PIXEL_MODE_BGRA: {
int ofs_color = i * bitmap.pitch + (j << 2);
wr[ofs + 2] = bitmap.buffer[ofs_color + 0];
wr[ofs + 1] = bitmap.buffer[ofs_color + 1];
wr[ofs + 0] = bitmap.buffer[ofs_color + 2];
wr[ofs + 3] = bitmap.buffer[ofs_color + 3];
} break;
// TODO: FT_PIXEL_MODE_LCD
default:
ERR_FAIL_V_MSG(Character::not_found(), "Font uses unsupported pixel format: " + itos(bitmap.pixel_mode) + ".");
break;
}
}
}
}
//blit to image and texture
{
if (RenderingServer::get_singleton() != nullptr) {
Ref<Image> img = memnew(Image(tex.texture_size, tex.texture_size, 0, require_format, tex.imgdata));
if (tex.texture.is_null()) {
tex.texture.instance();
tex.texture->create_from_image(img);
} else {
tex.texture->update(img); //update
}
}
}
// update height array
for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
tex.offsets.write[k] = tex_pos.y + mh;
}
Character chr;
chr.align = Vector2(xofs, -yofs) * p_data->scale_color_font / oversampling;
chr.advance = advance * p_data->scale_color_font / oversampling;
chr.texture_idx = tex_pos.index;
chr.found = true;
chr.rect_uv = Rect2(tex_pos.x + rect_margin, tex_pos.y + rect_margin, w, h);
chr.rect = chr.rect_uv;
chr.rect.position /= oversampling;
chr.rect.size *= (p_data->scale_color_font / oversampling);
return chr;
}
void DynamicFontDataFallback::update_char(int p_size, char32_t p_char) {
DataAtSize *fds = get_data_for_size(p_size, false);
ERR_FAIL_COND(fds == nullptr);
if (fds->char_map.has(p_char)) {
return;
}
Character character = Character::not_found();
FT_GlyphSlot slot = fds->face->glyph;
FT_UInt gl_index = FT_Get_Char_Index(fds->face, p_char);
if (gl_index == 0) {
fds->char_map[p_char] = character;
return;
}
int ft_hinting;
switch (hinting) {
case TextServer::HINTING_NONE:
ft_hinting = FT_LOAD_NO_HINTING;
break;
case TextServer::HINTING_LIGHT:
ft_hinting = FT_LOAD_TARGET_LIGHT;
break;
default:
ft_hinting = FT_LOAD_TARGET_NORMAL;
break;
}
FT_Fixed v, h;
FT_Get_Advance(fds->face, gl_index, FT_HAS_COLOR(fds->face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting, &h);
FT_Get_Advance(fds->face, gl_index, FT_HAS_COLOR(fds->face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting | FT_LOAD_VERTICAL_LAYOUT, &v);
int error = FT_Load_Glyph(fds->face, gl_index, FT_HAS_COLOR(fds->face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting);
if (error) {
fds->char_map[p_char] = character;
return;
}
error = FT_Render_Glyph(fds->face->glyph, antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
if (!error) {
character = bitmap_to_character(fds, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0);
}
fds->char_map[p_char] = character;
}
void DynamicFontDataFallback::update_char_outline(int p_size, int p_outline_size, char32_t p_char) {
DataAtSize *fds = get_data_for_size(p_size, p_outline_size);
ERR_FAIL_COND(fds == nullptr);
if (fds->char_map.has(p_char)) {
return;
}
Character character = Character::not_found();
FT_UInt gl_index = FT_Get_Char_Index(fds->face, p_char);
if (gl_index == 0) {
fds->char_map[p_char] = character;
return;
}
int error = FT_Load_Glyph(fds->face, gl_index, FT_LOAD_NO_BITMAP | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0));
if (error) {
fds->char_map[p_char] = character;
return;
}
FT_Stroker stroker;
if (FT_Stroker_New(library, &stroker) != 0) {
fds->char_map[p_char] = character;
return;
}
FT_Stroker_Set(stroker, (int)(p_outline_size * oversampling * 64.0), FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0);
FT_Glyph glyph;
FT_BitmapGlyph glyph_bitmap;
if (FT_Get_Glyph(fds->face->glyph, &glyph) != 0) {
goto cleanup_stroker;
}
if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) {
goto cleanup_glyph;
}
if (FT_Glyph_To_Bitmap(&glyph, antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1) != 0) {
goto cleanup_glyph;
}
glyph_bitmap = (FT_BitmapGlyph)glyph;
character = bitmap_to_character(fds, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2());
cleanup_glyph:
FT_Done_Glyph(glyph);
cleanup_stroker:
FT_Stroker_Done(stroker);
fds->char_map[p_char] = character;
}
void DynamicFontDataFallback::clear_cache() {
_THREAD_SAFE_METHOD_
for (Map<CacheID, DataAtSize *>::Element *E = size_cache.front(); E; E = E->next()) {
memdelete(E->get());
}
size_cache.clear();
for (Map<CacheID, DataAtSize *>::Element *E = size_cache_outline.front(); E; E = E->next()) {
memdelete(E->get());
}
size_cache_outline.clear();
}
Error DynamicFontDataFallback::load_from_file(const String &p_filename, int p_base_size) {
_THREAD_SAFE_METHOD_
if (library == nullptr) {
int error = FT_Init_FreeType(&library);
ERR_FAIL_COND_V_MSG(error != 0, ERR_CANT_CREATE, "Error initializing FreeType.");
}
clear_cache();
font_path = p_filename;
base_size = p_base_size;
valid = true;
DataAtSize *fds = get_data_for_size(base_size); // load base size.
if (fds == nullptr) {
valid = false;
ERR_FAIL_V(ERR_CANT_CREATE);
}
return OK;
}
Error DynamicFontDataFallback::load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) {
_THREAD_SAFE_METHOD_
if (library == nullptr) {
int error = FT_Init_FreeType(&library);
ERR_FAIL_COND_V_MSG(error != 0, ERR_CANT_CREATE, "Error initializing FreeType.");
}
clear_cache();
font_mem = p_data;
font_mem_size = p_size;
base_size = p_base_size;
valid = true;
DataAtSize *fds = get_data_for_size(base_size); // load base size.
if (fds == nullptr) {
valid = false;
ERR_FAIL_V(ERR_CANT_CREATE);
}
return OK;
}
float DynamicFontDataFallback::get_height(int p_size) const {
_THREAD_SAFE_METHOD_
DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
ERR_FAIL_COND_V(fds == nullptr, 0.f);
return fds->ascent + fds->descent;
}
float DynamicFontDataFallback::get_ascent(int p_size) const {
_THREAD_SAFE_METHOD_
DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
ERR_FAIL_COND_V(fds == nullptr, 0.f);
return fds->ascent;
}
float DynamicFontDataFallback::get_descent(int p_size) const {
_THREAD_SAFE_METHOD_
DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
ERR_FAIL_COND_V(fds == nullptr, 0.f);
return fds->descent;
}
float DynamicFontDataFallback::get_underline_position(int p_size) const {
_THREAD_SAFE_METHOD_
DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
ERR_FAIL_COND_V(fds == nullptr, 0.f);
return fds->underline_position;
}
float DynamicFontDataFallback::get_underline_thickness(int p_size) const {
_THREAD_SAFE_METHOD_
DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
ERR_FAIL_COND_V(fds == nullptr, 0.f);
return fds->underline_thickness;
}
void DynamicFontDataFallback::set_antialiased(bool p_antialiased) {
if (antialiased != p_antialiased) {
clear_cache();
antialiased = p_antialiased;
}
}
bool DynamicFontDataFallback::get_antialiased() const {
return antialiased;
}
void DynamicFontDataFallback::set_force_autohinter(bool p_enabled) {
if (force_autohinter != p_enabled) {
clear_cache();
force_autohinter = p_enabled;
}
}
bool DynamicFontDataFallback::get_force_autohinter() const {
return force_autohinter;
}
void DynamicFontDataFallback::set_hinting(TextServer::Hinting p_hinting) {
if (hinting != p_hinting) {
clear_cache();
hinting = p_hinting;
}
}
TextServer::Hinting DynamicFontDataFallback::get_hinting() const {
return hinting;
}
bool DynamicFontDataFallback::has_outline() const {
return true;
}
float DynamicFontDataFallback::get_base_size() const {
return base_size;
}
bool DynamicFontDataFallback::has_char(char32_t p_char) const {
_THREAD_SAFE_METHOD_
DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(base_size);
ERR_FAIL_COND_V(fds == nullptr, false);
const_cast<DynamicFontDataFallback *>(this)->update_char(base_size, p_char);
Character ch = fds->char_map[p_char];
return (ch.found);
}
String DynamicFontDataFallback::get_supported_chars() const {
_THREAD_SAFE_METHOD_
DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(base_size);
ERR_FAIL_COND_V(fds == nullptr, String());
String chars;
FT_UInt gindex;
FT_ULong charcode = FT_Get_First_Char(fds->face, &gindex);
while (gindex != 0) {
if (charcode != 0) {
chars += char32_t(charcode);
}
charcode = FT_Get_Next_Char(fds->face, charcode, &gindex);
}
return chars;
}
Vector2 DynamicFontDataFallback::get_advance(char32_t p_char, int p_size) const {
_THREAD_SAFE_METHOD_
DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
ERR_FAIL_COND_V(fds == nullptr, Vector2());
const_cast<DynamicFontDataFallback *>(this)->update_char(p_size, p_char);
Character ch = fds->char_map[p_char];
return ch.advance;
}
Vector2 DynamicFontDataFallback::get_kerning(char32_t p_char, char32_t p_next, int p_size) const {
_THREAD_SAFE_METHOD_
DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
ERR_FAIL_COND_V(fds == nullptr, Vector2());
FT_Vector delta;
FT_Get_Kerning(fds->face, FT_Get_Char_Index(fds->face, p_char), FT_Get_Char_Index(fds->face, p_next), FT_KERNING_DEFAULT, &delta);
return Vector2(delta.x, delta.y);
}
Vector2 DynamicFontDataFallback::draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
_THREAD_SAFE_METHOD_
DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
ERR_FAIL_COND_V(fds == nullptr, Vector2());
const_cast<DynamicFontDataFallback *>(this)->update_char(p_size, p_index);
Character ch = fds->char_map[p_index];
Vector2 advance;
if (ch.found) {
ERR_FAIL_COND_V(ch.texture_idx < -1 || ch.texture_idx >= fds->textures.size(), Vector2());
if (ch.texture_idx != -1) {
Point2 cpos = p_pos;
cpos += ch.align;
Color modulate = p_color;
if (FT_HAS_COLOR(fds->face)) {
modulate.r = modulate.g = modulate.b = 1.0;
}
if (RenderingServer::get_singleton() != nullptr) {
RID texture = fds->textures[ch.texture_idx].texture->get_rid();
RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, ch.rect.size), texture, ch.rect_uv, modulate, false, false);
}
}
advance = ch.advance;
}
return advance;
}
Vector2 DynamicFontDataFallback::draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
_THREAD_SAFE_METHOD_
DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size, p_outline_size);
ERR_FAIL_COND_V(fds == nullptr, Vector2());
const_cast<DynamicFontDataFallback *>(this)->update_char_outline(p_size, p_outline_size, p_index);
Character ch = fds->char_map[p_index];
Vector2 advance;
if (ch.found) {
ERR_FAIL_COND_V(ch.texture_idx < -1 || ch.texture_idx >= fds->textures.size(), Vector2());
if (ch.texture_idx != -1) {
Point2 cpos = p_pos;
cpos += ch.align;
Color modulate = p_color;
if (FT_HAS_COLOR(fds->face)) {
modulate.r = modulate.g = modulate.b = 1.0;
}
if (RenderingServer::get_singleton() != nullptr) {
RID texture = fds->textures[ch.texture_idx].texture->get_rid();
RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, ch.rect.size), texture, ch.rect_uv, modulate, false, false);
}
}
advance = ch.advance;
}
return advance;
}
DynamicFontDataFallback::~DynamicFontDataFallback() {
clear_cache();
if (library != nullptr) {
FT_Done_FreeType(library);
}
}

View file

@ -0,0 +1,172 @@
/*************************************************************************/
/* dynamic_font_fb.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef DYNAMIC_FONT_FALLBACK_H
#define DYNAMIC_FONT_FALLBACK_H
#include "font_fb.h"
#include <ft2build.h>
#include FT_FREETYPE_H
struct DynamicFontDataFallback : public FontDataFallback {
_THREAD_SAFE_CLASS_
private:
struct CharTexture {
Vector<uint8_t> imgdata;
int texture_size = 0;
Vector<int> offsets;
Ref<ImageTexture> texture;
};
struct Character {
bool found = false;
int texture_idx = 0;
Rect2 rect;
Rect2 rect_uv;
Vector2 align;
Vector2 advance = Vector2(-1, -1);
static Character not_found();
};
struct TexturePosition {
int index = 0;
int x = 0;
int y = 0;
};
struct CacheID {
union {
struct {
uint32_t size : 16;
uint32_t outline_size : 16;
};
uint32_t key;
};
bool operator<(CacheID right) const {
return key < right.key;
}
CacheID() {
key = 0;
}
};
struct DataAtSize {
FT_Face face = nullptr;
FT_StreamRec stream;
int size = 0;
float scale_color_font = 1.f;
float ascent = 0;
float descent = 0;
float underline_position = 0;
float underline_thickness = 0;
Vector<CharTexture> textures;
HashMap<char32_t, Character> char_map;
~DataAtSize() {
if (face != nullptr) {
FT_Done_Face(face);
}
}
};
FT_Library library = nullptr;
// Source data.
const uint8_t *font_mem = nullptr;
int font_mem_size = 0;
String font_path;
static HashMap<String, Vector<uint8_t>> font_mem_cache;
float rect_margin = 1.f;
int base_size = 16;
float oversampling = 1.f;
bool antialiased = true;
bool force_autohinter = false;
TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
Map<CacheID, DataAtSize *> size_cache;
Map<CacheID, DataAtSize *> size_cache_outline;
static unsigned long _ft_stream_io(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count);
static void _ft_stream_close(FT_Stream stream);
DataAtSize *get_data_for_size(int p_size, int p_outline_size = 0);
TexturePosition find_texture_pos_for_glyph(DataAtSize *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height);
Character bitmap_to_character(DataAtSize *p_data, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance);
_FORCE_INLINE_ void update_char(int p_size, char32_t p_char);
_FORCE_INLINE_ void update_char_outline(int p_size, int p_outline_size, char32_t p_char);
public:
virtual void clear_cache() override;
virtual Error load_from_file(const String &p_filename, int p_base_size) override;
virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) override;
virtual float get_height(int p_size) const override;
virtual float get_ascent(int p_size) const override;
virtual float get_descent(int p_size) const override;
virtual float get_underline_position(int p_size) const override;
virtual float get_underline_thickness(int p_size) const override;
virtual void set_antialiased(bool p_antialiased) override;
virtual bool get_antialiased() const override;
virtual void set_hinting(TextServer::Hinting p_hinting) override;
virtual TextServer::Hinting get_hinting() const override;
virtual void set_force_autohinter(bool p_enabeld) override;
virtual bool get_force_autohinter() const override;
virtual void set_distance_field_hint(bool p_distance_field) override{};
virtual bool get_distance_field_hint() const override { return false; };
virtual bool has_outline() const override;
virtual float get_base_size() const override;
virtual bool has_char(char32_t p_char) const override;
virtual String get_supported_chars() const override;
virtual Vector2 get_advance(char32_t p_char, int p_size) const override;
virtual Vector2 get_kerning(char32_t p_char, char32_t p_next, int p_size) const override;
virtual Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override;
virtual Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override;
virtual ~DynamicFontDataFallback() override;
};
#endif // DYNAMIC_FONT_FALLBACK_H

View file

@ -0,0 +1,80 @@
/*************************************************************************/
/* font_fb.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef FONT_FALLBACK_H
#define FONT_FALLBACK_H
#include "servers/text_server.h"
struct FontDataFallback {
Map<String, bool> lang_support_overrides;
Map<String, bool> script_support_overrides;
bool valid = false;
virtual void clear_cache() = 0;
virtual Error load_from_file(const String &p_filename, int p_base_size) = 0;
virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) = 0;
virtual float get_height(int p_size) const = 0;
virtual float get_ascent(int p_size) const = 0;
virtual float get_descent(int p_size) const = 0;
virtual float get_underline_position(int p_size) const = 0;
virtual float get_underline_thickness(int p_size) const = 0;
virtual void set_antialiased(bool p_antialiased) = 0;
virtual bool get_antialiased() const = 0;
virtual void set_hinting(TextServer::Hinting p_hinting) = 0;
virtual TextServer::Hinting get_hinting() const = 0;
virtual void set_distance_field_hint(bool p_distance_field) = 0;
virtual bool get_distance_field_hint() const = 0;
virtual void set_force_autohinter(bool p_enabeld) = 0;
virtual bool get_force_autohinter() const = 0;
virtual bool has_outline() const = 0;
virtual float get_base_size() const = 0;
virtual bool has_char(char32_t p_char) const = 0;
virtual String get_supported_chars() const = 0;
virtual Vector2 get_advance(char32_t p_char, int p_size) const = 0;
virtual Vector2 get_kerning(char32_t p_char, char32_t p_next, int p_size) const = 0;
virtual Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const = 0;
virtual Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const = 0;
virtual ~FontDataFallback(){};
};
#endif // FONT_FALLBACK_H

View file

@ -0,0 +1,43 @@
/*************************************************************************/
/* register_types.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "register_types.h"
#include "text_server_fb.h"
void preregister_text_server_fb_types() {
TextServerFallback::register_server();
}
void register_text_server_fb_types() {
}
void unregister_text_server_fb_types() {
}

View file

@ -0,0 +1,40 @@
/*************************************************************************/
/* register_types.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef TEXT_SERVER_FB_REGISTER_TYPES_H
#define TEXT_SERVER_FB_REGISTER_TYPES_H
#define MODULE_TEXT_SERVER_FB_HAS_PREREGISTER
void preregister_text_server_fb_types();
void register_text_server_fb_types();
void unregister_text_server_fb_types();
#endif // TEXT_SERVER_FB_REGISTER_TYPES_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,193 @@
/*************************************************************************/
/* text_server_fb.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef TEXT_SERVER_FALLBACK_H
#define TEXT_SERVER_FALLBACK_H
/*************************************************************************/
/* Fallback Text Server provides simplified TS functionality, without */
/* BiDi, shaping and advanced font features support. */
/*************************************************************************/
#include "servers/text_server.h"
#include "core/templates/rid_owner.h"
#include "scene/resources/texture.h"
#include "font_fb.h"
class TextServerFallback : public TextServer {
GDCLASS(TextServerFallback, TextServer);
_THREAD_SAFE_CLASS_
float oversampling = 1.f;
mutable RID_PtrOwner<FontDataFallback> font_owner;
mutable RID_PtrOwner<ShapedTextData> shaped_owner;
static String interface_name;
static uint32_t interface_features;
protected:
static void _bind_methods(){};
void full_copy(ShapedTextData *p_shaped);
void invalidate(ShapedTextData *p_shaped);
public:
virtual bool has_feature(Feature p_feature) override;
virtual String get_name() const override;
virtual void free(RID p_rid) override;
virtual bool has(RID p_rid) override;
virtual bool load_support_data(const String &p_filename) override;
#ifdef TOOLS_ENABLED
virtual String get_support_data_filename() override { return ""; };
virtual String get_support_data_info() override { return "Not supported"; };
virtual bool save_support_data(const String &p_filename) override;
#endif
virtual bool is_locale_right_to_left(const String &p_locale) override;
/* Font interface */
virtual RID create_font_system(const String &p_name, int p_base_size = 16) override;
virtual RID create_font_resource(const String &p_filename, int p_base_size = 16) override;
virtual RID create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16) override;
virtual float font_get_height(RID p_font, int p_size) const override;
virtual float font_get_ascent(RID p_font, int p_size) const override;
virtual float font_get_descent(RID p_font, int p_size) const override;
virtual float font_get_underline_position(RID p_font, int p_size) const override;
virtual float font_get_underline_thickness(RID p_font, int p_size) const override;
virtual void font_set_antialiased(RID p_font, bool p_antialiased) override;
virtual bool font_get_antialiased(RID p_font) const override;
virtual void font_set_hinting(RID p_font, Hinting p_hinting) override;
virtual Hinting font_get_hinting(RID p_font) const override;
virtual void font_set_force_autohinter(RID p_font, bool p_enabeld) override;
virtual bool font_get_force_autohinter(RID p_font) const override;
virtual bool font_has_char(RID p_font, char32_t p_char) const override;
virtual String font_get_supported_chars(RID p_font) const override;
virtual void font_set_distance_field_hint(RID p_font, bool p_distance_field) override;
virtual bool font_get_distance_field_hint(RID p_font) const override;
virtual bool font_has_outline(RID p_font) const override;
virtual float font_get_base_size(RID p_font) const override;
virtual bool font_is_language_supported(RID p_font, const String &p_language) const override;
virtual void font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) override;
virtual bool font_get_language_support_override(RID p_font, const String &p_language) override;
virtual void font_remove_language_support_override(RID p_font, const String &p_language) override;
Vector<String> font_get_language_support_overrides(RID p_font) override;
virtual bool font_is_script_supported(RID p_font, const String &p_script) const override;
virtual void font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) override;
virtual bool font_get_script_support_override(RID p_font, const String &p_script) override;
virtual void font_remove_script_support_override(RID p_font, const String &p_script) override;
Vector<String> font_get_script_support_overrides(RID p_font) override;
virtual uint32_t font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector = 0x0000) const override;
virtual Vector2 font_get_glyph_advance(RID p_font, uint32_t p_index, int p_size) const override;
virtual Vector2 font_get_glyph_kerning(RID p_font, uint32_t p_index_a, uint32_t p_index_b, int p_size) const override;
virtual Vector2 font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
virtual Vector2 font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
virtual float font_get_oversampling() const override;
virtual void font_set_oversampling(float p_oversampling) override;
virtual Vector<String> get_system_fonts() const override;
/* Shaped text buffer interface */
virtual RID create_shaped_text(Direction p_direction = DIRECTION_AUTO, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
virtual void shaped_text_clear(RID p_shaped) override;
virtual void shaped_text_set_direction(RID p_shaped, Direction p_direction = DIRECTION_AUTO) override;
virtual Direction shaped_text_get_direction(RID p_shaped) const override;
virtual void shaped_text_set_bidi_override(RID p_shaped, const Vector<Vector2i> &p_override) override;
virtual void shaped_text_set_orientation(RID p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
virtual Orientation shaped_text_get_orientation(RID p_shaped) const override;
virtual void shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) override;
virtual bool shaped_text_get_preserve_invalid(RID p_shaped) const override;
virtual void shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) override;
virtual bool shaped_text_get_preserve_control(RID p_shaped) const override;
virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") override;
virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER, int p_length = 1) override;
virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER) override;
virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override;
virtual RID shaped_text_get_parent(RID p_shaped) const override;
virtual float shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
virtual float shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) override;
virtual bool shaped_text_shape(RID p_shaped) override;
virtual bool shaped_text_update_breaks(RID p_shaped) override;
virtual bool shaped_text_update_justification_ops(RID p_shaped) override;
virtual bool shaped_text_is_ready(RID p_shaped) const override;
virtual Vector<Glyph> shaped_text_get_glyphs(RID p_shaped) const override;
virtual Vector2i shaped_text_get_range(RID p_shaped) const override;
virtual Vector<Glyph> shaped_text_sort_logical(RID p_shaped) override;
virtual Array shaped_text_get_objects(RID p_shaped) const override;
virtual Rect2 shaped_text_get_object_rect(RID p_shaped, Variant p_key) const override;
virtual Size2 shaped_text_get_size(RID p_shaped) const override;
virtual float shaped_text_get_ascent(RID p_shaped) const override;
virtual float shaped_text_get_descent(RID p_shaped) const override;
virtual float shaped_text_get_width(RID p_shaped) const override;
virtual float shaped_text_get_underline_position(RID p_shaped) const override;
virtual float shaped_text_get_underline_thickness(RID p_shaped) const override;
static TextServer *create_func(Error &r_error, void *p_user_data);
static void register_server();
TextServerFallback(){};
~TextServerFallback(){};
};
#endif // TEXT_SERVER_FALLBACK_H

View file

@ -65,9 +65,9 @@
#include "rendering/rasterizer.h" #include "rendering/rasterizer.h"
#include "rendering/rendering_device.h" #include "rendering/rendering_device.h"
#include "rendering/rendering_device_binds.h" #include "rendering/rendering_device_binds.h"
#include "rendering_server.h" #include "rendering_server.h"
#include "servers/rendering/shader_types.h" #include "servers/rendering/shader_types.h"
#include "text_server.h"
#include "xr/xr_interface.h" #include "xr/xr_interface.h"
#include "xr/xr_positional_tracker.h" #include "xr/xr_positional_tracker.h"
#include "xr_server.h" #include "xr_server.h"
@ -102,6 +102,11 @@ void register_server_types() {
ClassDB::register_virtual_class<DisplayServer>(); ClassDB::register_virtual_class<DisplayServer>();
ClassDB::register_virtual_class<RenderingServer>(); ClassDB::register_virtual_class<RenderingServer>();
ClassDB::register_class<AudioServer>(); ClassDB::register_class<AudioServer>();
ClassDB::register_class<TextServerManager>();
ClassDB::register_virtual_class<TextServer>();
TextServer::initialize_hex_code_box_fonts();
ClassDB::register_virtual_class<PhysicsServer2D>(); ClassDB::register_virtual_class<PhysicsServer2D>();
ClassDB::register_virtual_class<PhysicsServer3D>(); ClassDB::register_virtual_class<PhysicsServer3D>();
ClassDB::register_virtual_class<NavigationServer2D>(); ClassDB::register_virtual_class<NavigationServer2D>();
@ -209,6 +214,7 @@ void register_server_types() {
void unregister_server_types() { void unregister_server_types() {
memdelete(shader_types); memdelete(shader_types);
TextServer::finish_hex_code_box_fonts();
} }
void register_server_singletons() { void register_server_singletons() {
@ -219,6 +225,7 @@ void register_server_singletons() {
Engine::get_singleton()->add_singleton(Engine::Singleton("PhysicsServer3D", PhysicsServer3D::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("PhysicsServer3D", PhysicsServer3D::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer2D", NavigationServer2D::get_singleton_mut())); Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer2D", NavigationServer2D::get_singleton_mut()));
Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer3D", NavigationServer3D::get_singleton_mut())); Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer3D", NavigationServer3D::get_singleton_mut()));
Engine::get_singleton()->add_singleton(Engine::Singleton("TextServerManager", TextServerManager::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("XRServer", XRServer::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("XRServer", XRServer::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("CameraServer", CameraServer::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("CameraServer", CameraServer::get_singleton()));
} }

1246
servers/text_server.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,453 @@
/*************************************************************************/
/* text_server.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef TEXT_SERVER_H
#define TEXT_SERVER_H
#include "core/object/reference.h"
#include "core/os/os.h"
#include "core/templates/rid.h"
#include "core/variant/variant.h"
#include "scene/resources/texture.h"
class CanvasTexture;
class TextServer : public Object {
GDCLASS(TextServer, Object);
public:
enum Direction {
DIRECTION_AUTO,
DIRECTION_LTR,
DIRECTION_RTL
};
enum Orientation {
ORIENTATION_HORIZONTAL,
ORIENTATION_VERTICAL
};
enum JustificationFlag {
JUSTIFICATION_NONE = 0,
JUSTIFICATION_KASHIDA = 1 << 0,
JUSTIFICATION_WORD_BOUND = 1 << 1,
JUSTIFICATION_TRIM_EDGE_SPACES = 1 << 2,
JUSTIFICATION_AFTER_LAST_TAB = 1 << 3
};
enum LineBreakFlag {
BREAK_NONE = 0,
BREAK_MANDATORY = 1 << 4,
BREAK_WORD_BOUND = 1 << 5,
BREAK_GRAPHEME_BOUND = 1 << 6
//RESERVED = 1 << 7
};
enum GraphemeFlag {
GRAPHEME_IS_VALID = 1 << 0, // Glyph is valid.
GRAPHEME_IS_RTL = 1 << 1, // Glyph is right-to-left.
GRAPHEME_IS_VIRTUAL = 1 << 2, // Glyph is not part of source string (added by fit_to_width function, do not affect caret movement).
GRAPHEME_IS_SPACE = 1 << 3, // Is whitespace (for justification).
GRAPHEME_IS_BREAK_HARD = 1 << 4, // Is line break (mandatory break, e.g "\n")
GRAPHEME_IS_BREAK_SOFT = 1 << 5, // Is line break (optional break, e.g space)
GRAPHEME_IS_TAB = 1 << 6, // Is tab or vertical tab
GRAPHEME_IS_ELONGATION = 1 << 7 // Elongation (e.g kashida), glyph can be duplicated or truncated to fit line to width.
};
enum Hinting {
HINTING_NONE,
HINTING_LIGHT,
HINTING_NORMAL
};
enum Feature {
FEATURE_BIDI_LAYOUT = 1 << 0,
FEATURE_VERTICAL_LAYOUT = 1 << 1,
FEATURE_SHAPING = 1 << 2,
FEATURE_KASHIDA_JUSTIFICATION = 1 << 3,
FEATURE_BREAK_ITERATORS = 1 << 4,
FEATURE_FONT_SYSTEM = 1 << 5,
FEATURE_USE_SUPPORT_DATA = 1 << 6
};
struct Glyph {
int start = -1; // Start offset in the source string.
int end = -1; // End offset in the source string.
uint8_t count = 0; // Number of glyphs in the grapheme, set in the first glyph only.
uint8_t repeat = 1; // Draw multiple times in the row.
uint8_t flags = 0; // Grapheme flags (valid, rtl, virtual), set in the first glyph only.
float x_off = 0.f; // Offset from the origin of the glyph on baseline.
float y_off = 0.f;
float advance = 0.f; // Advance to the next glyph along baseline(x for horizontal layout, y for vertical).
RID font_rid; // Font resource.
int font_size = 0; // Font size;
uint32_t index = 0; // Glyph index (font specific) or UTF-32 codepoint (for the invalid glyphs).
bool operator==(const Glyph &p_a) const;
bool operator!=(const Glyph &p_a) const;
bool operator<(const Glyph &p_a) const;
bool operator>(const Glyph &p_a) const;
};
struct GlyphCompare { // For line breaking reordering.
_FORCE_INLINE_ bool operator()(const Glyph &l, const Glyph &r) const {
if (l.start == r.start) {
if (l.count == r.count) {
if ((l.flags & GRAPHEME_IS_VIRTUAL) == GRAPHEME_IS_VIRTUAL) {
return false;
} else {
return true;
}
}
return l.count > r.count; // Sort first glyoh with count & flags, order of the rest are irrelevant.
} else {
return l.start < r.start;
}
}
};
struct ShapedTextData {
/* Source data */
RID parent; // Substring parent ShapedTextData.
int start = 0; // Substring start offset in the parent string.
int end = 0; // Substring end offset in the parent string.
String text;
TextServer::Direction direction = DIRECTION_LTR; // Desired text direction.
TextServer::Orientation orientation = ORIENTATION_HORIZONTAL;
struct Span {
int start = -1;
int end = -1;
Vector<RID> fonts;
int font_size = 0;
Variant embedded_key;
String language;
Dictionary features;
};
Vector<Span> spans;
struct EmbeddedObject {
int pos = 0;
VAlign inline_align = VALIGN_TOP;
Rect2 rect;
};
Map<Variant, EmbeddedObject> objects;
/* Shaped data */
TextServer::Direction para_direction = DIRECTION_LTR; // Detected text direction.
bool valid = false; // String is shaped.
bool line_breaks_valid = false; // Line and word break flags are populated (and virtual zero width spaces inserted).
bool justification_ops_valid = false; // Virtual elongation glyphs are added to the string.
bool sort_valid = false;
bool preserve_invalid = true; // Draw hex code box instead of missing characters.
bool preserve_control = false; // Draw control characters.
float ascent = 0.f; // Ascent for horizontal layout, 1/2 of width for vertical.
float descent = 0.f; // Descent for horizontal layout, 1/2 of width for vertical.
float width = 0.f; // Width for horizontal layout, height for vertical.
float upos = 0.f;
float uthk = 0.f;
Vector<TextServer::Glyph> glyphs;
Vector<TextServer::Glyph> glyphs_logical;
};
struct BitmapFontData {
int height = 0;
int ascent = 0;
int charcount = 0;
const int *char_rects = nullptr;
int kerning_count = 0;
const int *kernings = nullptr;
int w = 0;
int h = 0;
const unsigned char *img = nullptr;
};
protected:
static void _bind_methods();
static Vector3 hex_code_box_font_size[2];
static Ref<CanvasTexture> hex_code_box_font_tex[2];
public:
static void initialize_hex_code_box_fonts();
static void finish_hex_code_box_fonts();
virtual bool has_feature(Feature p_feature) = 0;
virtual String get_name() const = 0;
virtual void free(RID p_rid) = 0;
virtual bool has(RID p_rid) = 0;
virtual bool load_support_data(const String &p_filename) = 0;
#ifdef TOOLS_ENABLED
virtual String get_support_data_filename() = 0;
virtual String get_support_data_info() = 0;
virtual bool save_support_data(const String &p_filename) = 0;
#endif
virtual bool is_locale_right_to_left(const String &p_locale) = 0;
virtual int32_t name_to_tag(const String &p_name) { return 0; };
virtual String tag_to_name(int32_t p_tag) { return ""; };
/* Font interface */
virtual RID create_font_system(const String &p_name, int p_base_size = 16) = 0;
virtual RID create_font_resource(const String &p_filename, int p_base_size = 16) = 0;
virtual RID create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16) = 0;
virtual float font_get_height(RID p_font, int p_size) const = 0;
virtual float font_get_ascent(RID p_font, int p_size) const = 0;
virtual float font_get_descent(RID p_font, int p_size) const = 0;
virtual float font_get_underline_position(RID p_font, int p_size) const = 0;
virtual float font_get_underline_thickness(RID p_font, int p_size) const = 0;
virtual void font_set_antialiased(RID p_font, bool p_antialiased) = 0;
virtual bool font_get_antialiased(RID p_font) const = 0;
virtual Dictionary font_get_feature_list(RID p_font) const { return Dictionary(); };
virtual void font_set_distance_field_hint(RID p_font, bool p_distance_field) = 0;
virtual bool font_get_distance_field_hint(RID p_font) const = 0;
virtual void font_set_hinting(RID p_font, Hinting p_hinting) = 0;
virtual Hinting font_get_hinting(RID p_font) const = 0;
virtual void font_set_force_autohinter(RID p_font, bool p_enabeld) = 0;
virtual bool font_get_force_autohinter(RID p_font) const = 0;
virtual bool font_has_char(RID p_font, char32_t p_char) const = 0;
virtual String font_get_supported_chars(RID p_font) const = 0;
virtual bool font_has_outline(RID p_font) const = 0;
virtual float font_get_base_size(RID p_font) const = 0;
virtual bool font_is_language_supported(RID p_font, const String &p_language) const = 0;
virtual void font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) = 0;
virtual bool font_get_language_support_override(RID p_font, const String &p_language) = 0;
virtual void font_remove_language_support_override(RID p_font, const String &p_language) = 0;
virtual Vector<String> font_get_language_support_overrides(RID p_font) = 0;
virtual bool font_is_script_supported(RID p_font, const String &p_script) const = 0;
virtual void font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) = 0;
virtual bool font_get_script_support_override(RID p_font, const String &p_script) = 0;
virtual void font_remove_script_support_override(RID p_font, const String &p_script) = 0;
virtual Vector<String> font_get_script_support_overrides(RID p_font) = 0;
virtual uint32_t font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector = 0x0000) const = 0;
virtual Vector2 font_get_glyph_advance(RID p_font, uint32_t p_index, int p_size) const = 0;
virtual Vector2 font_get_glyph_kerning(RID p_font, uint32_t p_index_a, uint32_t p_index_b, int p_size) const = 0;
virtual Vector2 font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const = 0;
virtual Vector2 font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const = 0;
virtual float font_get_oversampling() const = 0;
virtual void font_set_oversampling(float p_oversampling) = 0;
Vector2 get_hex_code_box_size(int p_size, char32_t p_index) const;
void draw_hex_code_box(RID p_canvas, int p_size, const Vector2 &p_pos, char32_t p_index, const Color &p_color) const;
virtual Vector<String> get_system_fonts() const = 0;
/* Shaped text buffer interface */
virtual RID create_shaped_text(Direction p_direction = DIRECTION_AUTO, Orientation p_orientation = ORIENTATION_HORIZONTAL) = 0;
virtual void shaped_text_clear(RID p_shaped) = 0;
virtual void shaped_text_set_direction(RID p_shaped, Direction p_direction = DIRECTION_AUTO) = 0;
virtual Direction shaped_text_get_direction(RID p_shaped) const = 0;
virtual void shaped_text_set_bidi_override(RID p_shaped, const Vector<Vector2i> &p_override) = 0;
virtual void shaped_text_set_orientation(RID p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) = 0;
virtual Orientation shaped_text_get_orientation(RID p_shaped) const = 0;
virtual void shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) = 0;
virtual bool shaped_text_get_preserve_invalid(RID p_shaped) const = 0;
virtual void shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) = 0;
virtual bool shaped_text_get_preserve_control(RID p_shaped) const = 0;
virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") = 0;
virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER, int p_length = 1) = 0;
virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER) = 0;
virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const = 0; // Copy shaped substring (e.g. line break) without reshaping, but correctly reordered, preservers range.
virtual RID shaped_text_get_parent(RID p_shaped) const = 0;
virtual float shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) = 0;
virtual float shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) = 0;
virtual bool shaped_text_shape(RID p_shaped) = 0;
virtual bool shaped_text_update_breaks(RID p_shaped) = 0;
virtual bool shaped_text_update_justification_ops(RID p_shaped) = 0;
virtual bool shaped_text_is_ready(RID p_shaped) const = 0;
virtual Vector<Glyph> shaped_text_get_glyphs(RID p_shaped) const = 0;
virtual Vector2i shaped_text_get_range(RID p_shaped) const = 0;
virtual Vector<Glyph> shaped_text_sort_logical(RID p_shaped) = 0;
virtual Vector<Vector2i> shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<float> &p_width, int p_start = 0, bool p_once = true, uint8_t /*TextBreakFlag*/ p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const;
virtual Vector<Vector2i> shaped_text_get_line_breaks(RID p_shaped, float p_width, int p_start = 0, uint8_t /*TextBreakFlag*/ p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const;
virtual Vector<Vector2i> shaped_text_get_word_breaks(RID p_shaped) const;
virtual Array shaped_text_get_objects(RID p_shaped) const = 0;
virtual Rect2 shaped_text_get_object_rect(RID p_shaped, Variant p_key) const = 0;
virtual Size2 shaped_text_get_size(RID p_shaped) const = 0;
virtual float shaped_text_get_ascent(RID p_shaped) const = 0;
virtual float shaped_text_get_descent(RID p_shaped) const = 0;
virtual float shaped_text_get_width(RID p_shaped) const = 0;
virtual float shaped_text_get_underline_position(RID p_shaped) const = 0;
virtual float shaped_text_get_underline_thickness(RID p_shaped) const = 0;
virtual Direction shaped_text_get_dominant_direciton_in_range(RID p_shaped, int p_start, int p_end) const;
virtual void shaped_text_get_carets(RID p_shaped, int p_position, Rect2 &p_leading_caret, Direction &p_leading_dir, Rect2 &p_trailing_caret, Direction &p_trailing_dir) const;
virtual Vector<Vector2> shaped_text_get_selection(RID p_shaped, int p_start, int p_end) const;
virtual int shaped_text_hit_test_grapheme(RID p_shaped, float p_coords) const; // Return grapheme index.
virtual int shaped_text_hit_test_position(RID p_shaped, float p_coords) const; // Return caret/selection position.
virtual int shaped_text_next_grapheme_pos(RID p_shaped, int p_pos);
virtual int shaped_text_prev_grapheme_pos(RID p_shaped, int p_pos);
// The pen position is always placed on the baseline and moveing left to right.
virtual void shaped_text_draw(RID p_shaped, RID p_canvas, const Vector2 &p_pos, float p_clip_l = -1.f, float p_clip_r = -1.f, const Color &p_color = Color(1, 1, 1)) const;
virtual void shaped_text_draw_outline(RID p_shaped, RID p_canvas, const Vector2 &p_pos, float p_clip_l = -1.f, float p_clip_r = -1.f, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1)) const;
// Number conversion.
virtual String format_number(const String &p_string, const String &p_language = "") const { return p_string; };
virtual String parse_number(const String &p_string, const String &p_language = "") const { return p_string; };
virtual String percent_sign(const String &p_language = "") const { return "%"; };
/* GDScript wrappers */
RID _create_font_memory(const PackedByteArray &p_data, const String &p_type, int p_base_size = 16);
Array _shaped_text_get_glyphs(RID p_shaped) const;
Dictionary _shaped_text_get_carets(RID p_shaped, int p_position) const;
void _shaped_text_set_bidi_override(RID p_shaped, const Array &p_override);
Array _shaped_text_get_line_breaks_adv(RID p_shaped, const PackedFloat32Array &p_width, int p_start, bool p_once, uint8_t p_break_flags) const;
Array _shaped_text_get_line_breaks(RID p_shaped, float p_width, int p_start, uint8_t p_break_flags) const;
Array _shaped_text_get_word_breaks(RID p_shaped) const;
Array _shaped_text_get_selection(RID p_shaped, int p_start, int p_end) const;
TextServer();
~TextServer();
};
/*************************************************************************/
class TextServerManager : public Object {
GDCLASS(TextServerManager, Object);
public:
typedef TextServer *(*CreateFunction)(Error &r_error, void *p_user_data);
protected:
static void _bind_methods();
private:
static TextServerManager *singleton;
static TextServer *server;
enum {
MAX_SERVERS = 64
};
struct TextServerCreate {
String name;
CreateFunction create_function = nullptr;
uint32_t features = 0;
TextServer *instance = nullptr;
void *user_data = nullptr;
};
static TextServerCreate server_create_functions[MAX_SERVERS];
static int server_create_count;
public:
_FORCE_INLINE_ static TextServerManager *get_singleton() {
return singleton;
}
static void register_create_function(const String &p_name, uint32_t p_features, CreateFunction p_function, void *p_user_data);
static int get_interface_count();
static String get_interface_name(int p_index);
static uint32_t get_interface_features(int p_index);
static TextServer *initialize(int p_index, Error &r_error);
static TextServer *get_primary_interface();
/* GDScript wrappers */
int _get_interface_count() const;
String _get_interface_name(int p_index) const;
uint32_t _get_interface_features(int p_index) const;
TextServer *_get_interface(int p_index) const;
Array _get_interfaces() const;
TextServer *_find_interface(const String &p_name) const;
bool _set_primary_interface(int p_index);
TextServer *_get_primary_interface() const;
TextServerManager();
~TextServerManager();
};
/*************************************************************************/
#define TS TextServerManager::get_primary_interface()
VARIANT_ENUM_CAST(TextServer::Direction);
VARIANT_ENUM_CAST(TextServer::Orientation);
VARIANT_ENUM_CAST(TextServer::JustificationFlag);
VARIANT_ENUM_CAST(TextServer::LineBreakFlag);
VARIANT_ENUM_CAST(TextServer::GraphemeFlag);
VARIANT_ENUM_CAST(TextServer::Hinting);
VARIANT_ENUM_CAST(TextServer::Feature);
#endif // TEXT_SERVER_H

100
tests/test_lru.h Normal file
View file

@ -0,0 +1,100 @@
/*************************************************************************/
/* test_lru.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef TEST_LRU_H
#define TEST_LRU_H
#include "core/templates/lru.h"
#include "core/templates/vector.h"
#include "tests/test_macros.h"
namespace TestLRU {
TEST_CASE("[LRU] Store and read") {
LRUCache<int, int> lru;
lru.set_capacity(3);
lru.insert(1, 1);
lru.insert(50, 2);
lru.insert(100, 5);
CHECK(lru.has(1));
CHECK(lru.has(50));
CHECK(lru.has(100));
CHECK(!lru.has(200));
CHECK(lru.get(1) == 1);
CHECK(lru.get(50) == 2);
CHECK(lru.get(100) == 5);
CHECK(lru.getptr(1) != nullptr);
CHECK(lru.getptr(1000) == nullptr);
lru.insert(600, 600); // Erase <50>
CHECK(lru.has(600));
CHECK(!lru.has(50));
}
TEST_CASE("[LRU] Resize and clear") {
LRUCache<int, int> lru;
lru.set_capacity(3);
lru.insert(1, 1);
lru.insert(2, 2);
lru.insert(3, 3);
CHECK(lru.get_capacity() == 3);
lru.set_capacity(5);
CHECK(lru.get_capacity() == 5);
CHECK(lru.has(1));
CHECK(lru.has(2));
CHECK(lru.has(3));
CHECK(!lru.has(4));
lru.set_capacity(2);
CHECK(lru.get_capacity() == 2);
CHECK(!lru.has(1));
CHECK(lru.has(2));
CHECK(lru.has(3));
CHECK(!lru.has(4));
lru.clear();
CHECK(!lru.has(1));
CHECK(!lru.has(2));
CHECK(!lru.has(3));
CHECK(!lru.has(4));
}
} // namespace TestLRU
#endif // TEST_LRU_H

View file

@ -45,6 +45,7 @@
#include "test_gui.h" #include "test_gui.h"
#include "test_json.h" #include "test_json.h"
#include "test_list.h" #include "test_list.h"
#include "test_lru.h"
#include "test_math.h" #include "test_math.h"
#include "test_method_bind.h" #include "test_method_bind.h"
#include "test_node_path.h" #include "test_node_path.h"
@ -58,6 +59,7 @@
#include "test_render.h" #include "test_render.h"
#include "test_shader_lang.h" #include "test_shader_lang.h"
#include "test_string.h" #include "test_string.h"
#include "test_text_server.h"
#include "test_validate_testing.h" #include "test_validate_testing.h"
#include "test_variant.h" #include "test_variant.h"

249
tests/test_text_server.h Normal file
View file

@ -0,0 +1,249 @@
/*************************************************************************/
/* test_text_server.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef TEST_TEXT_SERVER_H
#define TEST_TEXT_SERVER_H
#include "editor/builtin_fonts.gen.h"
#include "servers/text_server.h"
#include "tests/test_macros.h"
namespace TestTextServer {
TEST_SUITE("[[TextServer]") {
TEST_CASE("[TextServer] Init, font loading and shaping") {
TextServerManager *tsman = memnew(TextServerManager);
Error err = OK;
SUBCASE("[TextServer] Init") {
for (int i = 0; i < TextServerManager::get_interface_count(); i++) {
TextServer *ts = TextServerManager::initialize(i, err);
TEST_FAIL_COND((err != OK || ts == nullptr), "Text server " + TextServerManager::get_interface_name(i) + " init failed.");
}
}
SUBCASE("[TextServer] Loading fonts") {
for (int i = 0; i < TextServerManager::get_interface_count(); i++) {
TextServer *ts = TextServerManager::initialize(i, err);
RID font = ts->create_font_memory(_font_NotoSansUI_Regular, _font_NotoSansUI_Regular_size, "ttf");
TEST_FAIL_COND(font == RID(), "Loading font failed.");
ts->free(font);
}
}
SUBCASE("[TextServer] Text layout: Font fallback") {
for (int i = 0; i < TextServerManager::get_interface_count(); i++) {
TextServer *ts = TextServerManager::initialize(i, err);
Vector<RID> font;
font.push_back(ts->create_font_memory(_font_NotoSansUI_Regular, _font_NotoSansUI_Regular_size, "ttf"));
font.push_back(ts->create_font_memory(_font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size, "ttf"));
String test = U"คนอ้วน khon uan ראה";
// 6^ 17^
RID ctx = ts->create_shaped_text();
TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
bool ok = ts->shaped_text_add_string(ctx, test, font, 16);
TEST_FAIL_COND(!ok, "Adding text to the buffer failed.");
Vector<TextServer::Glyph> glyphs = ts->shaped_text_get_glyphs(ctx);
TEST_FAIL_COND(glyphs.size() == 0, "Shaping failed");
for (int j = 0; j < glyphs.size(); j++) {
if (glyphs[j].start < 6) {
TEST_FAIL_COND(glyphs[j].font_rid != font[1], "Incorrect font selected.");
}
if ((glyphs[j].start > 6) && (glyphs[j].start < 16)) {
TEST_FAIL_COND(glyphs[j].font_rid != font[0], "Incorrect font selected.");
}
if (glyphs[j].start > 16) {
TEST_FAIL_COND(glyphs[j].font_rid != RID(), "Incorrect font selected.");
TEST_FAIL_COND(glyphs[j].index != test[glyphs[j].start], "Incorrect glyph index.");
}
TEST_FAIL_COND((glyphs[j].start < 0 || glyphs[j].end > test.length()), "Incorrect glyph range.");
TEST_FAIL_COND(glyphs[j].font_size != 16, "Incorrect glyph font size.");
}
ts->free(ctx);
for (int j = 0; j < font.size(); j++) {
ts->free(font[j]);
}
font.clear();
}
}
SUBCASE("[TextServer] Text layout: BiDi") {
for (int i = 0; i < TextServerManager::get_interface_count(); i++) {
TextServer *ts = TextServerManager::initialize(i, err);
if (!ts->has_feature(TextServer::FEATURE_BIDI_LAYOUT)) {
continue;
}
Vector<RID> font;
font.push_back(ts->create_font_memory(_font_NotoSansUI_Regular, _font_NotoSansUI_Regular_size, "ttf"));
font.push_back(ts->create_font_memory(_font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size, "ttf"));
String test = U"Arabic (اَلْعَرَبِيَّةُ, al-ʿarabiyyah)";
// 7^ 26^
RID ctx = ts->create_shaped_text();
TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
bool ok = ts->shaped_text_add_string(ctx, test, font, 16);
TEST_FAIL_COND(!ok, "Adding text to the buffer failed.");
Vector<TextServer::Glyph> glyphs = ts->shaped_text_get_glyphs(ctx);
TEST_FAIL_COND(glyphs.size() == 0, "Shaping failed");
for (int j = 0; j < glyphs.size(); j++) {
if (glyphs[j].count > 0) {
if (glyphs[j].start < 7) {
TEST_FAIL_COND(((glyphs[j].flags & TextServer::GRAPHEME_IS_RTL) == TextServer::GRAPHEME_IS_RTL), "Incorrect direction.");
}
if ((glyphs[j].start > 8) && (glyphs[j].start < 23)) {
TEST_FAIL_COND(((glyphs[j].flags & TextServer::GRAPHEME_IS_RTL) != TextServer::GRAPHEME_IS_RTL), "Incorrect direction.");
}
if (glyphs[j].start > 26) {
TEST_FAIL_COND(((glyphs[j].flags & TextServer::GRAPHEME_IS_RTL) == TextServer::GRAPHEME_IS_RTL), "Incorrect direction.");
}
}
}
ts->free(ctx);
for (int j = 0; j < font.size(); j++) {
ts->free(font[j]);
}
font.clear();
}
}
SUBCASE("[TextServer] Text layout: Line breaking") {
for (int i = 0; i < TextServerManager::get_interface_count(); i++) {
TextServer *ts = TextServerManager::initialize(i, err);
String test_1 = U"test test test";
// 5^ 10^
Vector<RID> font;
font.push_back(ts->create_font_memory(_font_NotoSansUI_Regular, _font_NotoSansUI_Regular_size, "ttf"));
font.push_back(ts->create_font_memory(_font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size, "ttf"));
RID ctx = ts->create_shaped_text();
TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
bool ok = ts->shaped_text_add_string(ctx, test_1, font, 16);
TEST_FAIL_COND(!ok, "Adding text to the buffer failed.");
Vector<Vector2i> brks = ts->shaped_text_get_line_breaks(ctx, 1);
TEST_FAIL_COND(brks.size() != 3, "Invalid line breaks number.");
if (brks.size() == 3) {
TEST_FAIL_COND(brks[0] != Vector2i(0, 5), "Invalid line break position.");
TEST_FAIL_COND(brks[1] != Vector2i(5, 10), "Invalid line break position.");
TEST_FAIL_COND(brks[2] != Vector2i(10, 14), "Invalid line break position.");
}
ts->free(ctx);
for (int j = 0; j < font.size(); j++) {
ts->free(font[j]);
}
font.clear();
}
}
SUBCASE("[TextServer] Text layout: Justification") {
for (int i = 0; i < TextServerManager::get_interface_count(); i++) {
TextServer *ts = TextServerManager::initialize(i, err);
Vector<RID> font;
font.push_back(ts->create_font_memory(_font_NotoSansUI_Regular, _font_NotoSansUI_Regular_size, "ttf"));
font.push_back(ts->create_font_memory(_font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size, "ttf"));
String test_1 = U"الحمد";
String test_2 = U"الحمد test";
String test_3 = U"test test";
// 7^ 26^
RID ctx;
bool ok;
float width_old, width;
if (ts->has_feature(TextServer::FEATURE_KASHIDA_JUSTIFICATION)) {
ctx = ts->create_shaped_text();
TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
ok = ts->shaped_text_add_string(ctx, test_1, font, 16);
TEST_FAIL_COND(!ok, "Adding text to the buffer failed.");
width_old = ts->shaped_text_get_width(ctx);
width = ts->shaped_text_fit_to_width(ctx, 100, TextServer::JUSTIFICATION_WORD_BOUND);
TEST_FAIL_COND((width != width_old), "Invalid fill width.");
width = ts->shaped_text_fit_to_width(ctx, 100, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA);
TEST_FAIL_COND((width <= width_old || width > 100), "Invalid fill width.");
ts->free(ctx);
ctx = ts->create_shaped_text();
TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
ok = ts->shaped_text_add_string(ctx, test_2, font, 16);
TEST_FAIL_COND(!ok, "Adding text to the buffer failed.");
width_old = ts->shaped_text_get_width(ctx);
width = ts->shaped_text_fit_to_width(ctx, 100, TextServer::JUSTIFICATION_WORD_BOUND);
TEST_FAIL_COND((width <= width_old || width > 100), "Invalid fill width.");
width = ts->shaped_text_fit_to_width(ctx, 100, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA);
TEST_FAIL_COND((width <= width_old || width > 100), "Invalid fill width.");
ts->free(ctx);
}
ctx = ts->create_shaped_text();
TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
ok = ts->shaped_text_add_string(ctx, test_3, font, 16);
TEST_FAIL_COND(!ok, "Adding text to the buffer failed.");
width_old = ts->shaped_text_get_width(ctx);
width = ts->shaped_text_fit_to_width(ctx, 100, TextServer::JUSTIFICATION_WORD_BOUND);
TEST_FAIL_COND((width <= width_old || width > 100), "Invalid fill width.");
ts->free(ctx);
for (int j = 0; j < font.size(); j++) {
ts->free(font[j]);
}
font.clear();
}
}
memdelete(tsman);
}
}
}; // namespace TestTextServer
#endif // TEST_TEXT_SERVER_H

View file

@ -143,6 +143,12 @@ Use UI font variant if available, because it has tight vertical metrics and good
- Version: ? (pre-2014 commit when DroidSansJapanese.ttf was obsoleted) - Version: ? (pre-2014 commit when DroidSansJapanese.ttf was obsoleted)
- License: Apache 2.0 - License: Apache 2.0
### Tamsyn
- Upstream: http://www.fial.com/~scott/tamsyn-font/
- Version: 1.11
- License: Tamsyn
Extracted "0..9,A..F" characters for hex code printing.
## freetype ## freetype

BIN
thirdparty/fonts/Tamsyn10x20.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

BIN
thirdparty/fonts/Tamsyn5x9.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 B