Merge pull request #64530 from bruvzg/svg_in_ot
This commit is contained in:
commit
471c42ee1f
18 changed files with 1304 additions and 29 deletions
|
@ -41,11 +41,11 @@ static inline bool _is_white_space(char c) {
|
|||
}
|
||||
|
||||
//! sets the state that text was found. Returns true if set should be set
|
||||
bool XMLParser::_set_text(char *start, char *end) {
|
||||
bool XMLParser::_set_text(const char *start, const char *end) {
|
||||
// check if text is more than 2 characters, and if not, check if there is
|
||||
// only white space, so that this text won't be reported
|
||||
if (end - start < 3) {
|
||||
char *p = start;
|
||||
const char *p = start;
|
||||
for (; p != end; ++p) {
|
||||
if (!_is_white_space(*p)) {
|
||||
break;
|
||||
|
@ -92,7 +92,7 @@ void XMLParser::_parse_closing_xml_element() {
|
|||
void XMLParser::_ignore_definition() {
|
||||
node_type = NODE_UNKNOWN;
|
||||
|
||||
char *F = P;
|
||||
const char *F = P;
|
||||
// move until end marked with '>' reached
|
||||
while (*P && *P != '>') {
|
||||
next_char();
|
||||
|
@ -123,8 +123,8 @@ bool XMLParser::_parse_cdata() {
|
|||
return true;
|
||||
}
|
||||
|
||||
char *cDataBegin = P;
|
||||
char *cDataEnd = nullptr;
|
||||
const char *cDataBegin = P;
|
||||
const char *cDataEnd = nullptr;
|
||||
|
||||
// find end of CDATA
|
||||
while (*P && !cDataEnd) {
|
||||
|
@ -152,9 +152,9 @@ void XMLParser::_parse_comment() {
|
|||
node_type = NODE_COMMENT;
|
||||
P += 1;
|
||||
|
||||
char *pEndOfInput = data + length;
|
||||
char *pCommentBegin;
|
||||
char *pCommentEnd;
|
||||
const char *pEndOfInput = data + length;
|
||||
const char *pCommentBegin;
|
||||
const char *pCommentEnd;
|
||||
|
||||
if (P + 1 < pEndOfInput && P[0] == '-' && P[1] == '-') {
|
||||
// Comment, use '-->' as end.
|
||||
|
@ -293,7 +293,7 @@ void XMLParser::_parse_opening_xml_element() {
|
|||
}
|
||||
|
||||
void XMLParser::_parse_current_node() {
|
||||
char *start = P;
|
||||
const char *start = P;
|
||||
node_offset = P - data;
|
||||
|
||||
// more forward until '<' found
|
||||
|
@ -458,15 +458,36 @@ bool XMLParser::is_empty() const {
|
|||
Error XMLParser::open_buffer(const Vector<uint8_t> &p_buffer) {
|
||||
ERR_FAIL_COND_V(p_buffer.size() == 0, ERR_INVALID_DATA);
|
||||
|
||||
if (data) {
|
||||
memdelete_arr(data);
|
||||
if (data_copy) {
|
||||
memdelete_arr(data_copy);
|
||||
data_copy = nullptr;
|
||||
}
|
||||
|
||||
length = p_buffer.size();
|
||||
data = memnew_arr(char, length + 1);
|
||||
memcpy(data, p_buffer.ptr(), length);
|
||||
data[length] = 0;
|
||||
data_copy = memnew_arr(char, length + 1);
|
||||
memcpy(data_copy, p_buffer.ptr(), length);
|
||||
data_copy[length] = 0;
|
||||
data = data_copy;
|
||||
P = data;
|
||||
current_line = 0;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error XMLParser::_open_buffer(const uint8_t *p_buffer, size_t p_size) {
|
||||
ERR_FAIL_COND_V(p_size == 0, ERR_INVALID_DATA);
|
||||
ERR_FAIL_COND_V(!p_buffer, ERR_INVALID_DATA);
|
||||
|
||||
if (data_copy) {
|
||||
memdelete_arr(data_copy);
|
||||
data_copy = nullptr;
|
||||
}
|
||||
|
||||
length = p_size;
|
||||
data = (const char *)p_buffer;
|
||||
P = data;
|
||||
current_line = 0;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
@ -479,13 +500,15 @@ Error XMLParser::open(const String &p_path) {
|
|||
length = file->get_length();
|
||||
ERR_FAIL_COND_V(length < 1, ERR_FILE_CORRUPT);
|
||||
|
||||
if (data) {
|
||||
memdelete_arr(data);
|
||||
if (data_copy) {
|
||||
memdelete_arr(data_copy);
|
||||
data_copy = nullptr;
|
||||
}
|
||||
|
||||
data = memnew_arr(char, length + 1);
|
||||
file->get_buffer((uint8_t *)data, length);
|
||||
data[length] = 0;
|
||||
data_copy = memnew_arr(char, length + 1);
|
||||
file->get_buffer((uint8_t *)data_copy, length);
|
||||
data_copy[length] = 0;
|
||||
data = data_copy;
|
||||
P = data;
|
||||
current_line = 0;
|
||||
|
||||
|
@ -512,8 +535,9 @@ void XMLParser::skip_section() {
|
|||
}
|
||||
|
||||
void XMLParser::close() {
|
||||
if (data) {
|
||||
if (data_copy) {
|
||||
memdelete_arr(data);
|
||||
data_copy = nullptr;
|
||||
}
|
||||
data = nullptr;
|
||||
length = 0;
|
||||
|
@ -528,7 +552,8 @@ int XMLParser::get_current_line() const {
|
|||
}
|
||||
|
||||
XMLParser::~XMLParser() {
|
||||
if (data) {
|
||||
memdelete_arr(data);
|
||||
if (data_copy) {
|
||||
memdelete_arr(data_copy);
|
||||
data_copy = nullptr;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,8 +65,9 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
char *data = nullptr;
|
||||
char *P = nullptr;
|
||||
char *data_copy = nullptr;
|
||||
const char *data = nullptr;
|
||||
const char *P = nullptr;
|
||||
uint64_t length = 0;
|
||||
uint64_t current_line = 0;
|
||||
String node_name;
|
||||
|
@ -81,7 +82,7 @@ private:
|
|||
|
||||
Vector<Attribute> attributes;
|
||||
|
||||
bool _set_text(char *start, char *end);
|
||||
bool _set_text(const char *start, const char *end);
|
||||
void _parse_closing_xml_element();
|
||||
void _ignore_definition();
|
||||
bool _parse_cdata();
|
||||
|
@ -118,6 +119,7 @@ public:
|
|||
|
||||
Error open(const String &p_path);
|
||||
Error open_buffer(const Vector<uint8_t> &p_buffer);
|
||||
Error _open_buffer(const uint8_t *p_buffer, size_t p_size);
|
||||
|
||||
void close();
|
||||
|
||||
|
|
|
@ -39,6 +39,9 @@ thirdparty_obj = []
|
|||
freetype_enabled = "freetype" in env.module_list
|
||||
msdfgen_enabled = "msdfgen" in env.module_list
|
||||
|
||||
if "svg" in env.module_list:
|
||||
env_text_server_adv.Prepend(CPPPATH=["#thirdparty/thorvg/inc", "#thirdparty/thorvg/src/lib"])
|
||||
|
||||
if env["builtin_harfbuzz"]:
|
||||
env_harfbuzz = env_modules.Clone()
|
||||
env_harfbuzz.disable_warnings()
|
||||
|
|
|
@ -23,6 +23,7 @@ opts.Add(BoolVariable("brotli_enabled", "Use Brotli library", True))
|
|||
opts.Add(BoolVariable("freetype_enabled", "Use FreeType library", True))
|
||||
opts.Add(BoolVariable("msdfgen_enabled", "Use MSDFgen library (require FreeType)", True))
|
||||
opts.Add(BoolVariable("graphite_enabled", "Use Graphite library (require FreeType)", True))
|
||||
opts.Add(BoolVariable("thorvg_enabled", "Use ThorVG library (require FreeType)", True))
|
||||
opts.Add(BoolVariable("static_icu_data", "Use built-in ICU data", True))
|
||||
opts.Add(BoolVariable("verbose", "Enable verbose output for the compilation", False))
|
||||
|
||||
|
@ -34,6 +35,79 @@ if not env["verbose"]:
|
|||
if env["platform"] == "windows" and not env["use_mingw"]:
|
||||
env.AppendUnique(CCFLAGS=["/utf-8"]) # Force to use Unicode encoding.
|
||||
|
||||
# ThorVG
|
||||
if env["thorvg_enabled"] and env["freetype_enabled"]:
|
||||
env_tvg = env.Clone()
|
||||
env_tvg.disable_warnings()
|
||||
|
||||
thirdparty_tvg_dir = "../../../thirdparty/thorvg/"
|
||||
thirdparty_tvg_sources = [
|
||||
"src/lib/sw_engine/tvgSwFill.cpp",
|
||||
"src/lib/sw_engine/tvgSwImage.cpp",
|
||||
"src/lib/sw_engine/tvgSwMath.cpp",
|
||||
"src/lib/sw_engine/tvgSwMemPool.cpp",
|
||||
"src/lib/sw_engine/tvgSwRaster.cpp",
|
||||
"src/lib/sw_engine/tvgSwRenderer.cpp",
|
||||
"src/lib/sw_engine/tvgSwRle.cpp",
|
||||
"src/lib/sw_engine/tvgSwShape.cpp",
|
||||
"src/lib/sw_engine/tvgSwStroke.cpp",
|
||||
"src/lib/tvgAccessor.cpp",
|
||||
"src/lib/tvgBezier.cpp",
|
||||
"src/lib/tvgCanvas.cpp",
|
||||
"src/lib/tvgFill.cpp",
|
||||
"src/lib/tvgGlCanvas.cpp",
|
||||
"src/lib/tvgInitializer.cpp",
|
||||
"src/lib/tvgLinearGradient.cpp",
|
||||
"src/lib/tvgLoader.cpp",
|
||||
"src/lib/tvgLzw.cpp",
|
||||
"src/lib/tvgPaint.cpp",
|
||||
"src/lib/tvgPicture.cpp",
|
||||
"src/lib/tvgRadialGradient.cpp",
|
||||
"src/lib/tvgRender.cpp",
|
||||
"src/lib/tvgSaver.cpp",
|
||||
"src/lib/tvgScene.cpp",
|
||||
"src/lib/tvgShape.cpp",
|
||||
"src/lib/tvgSwCanvas.cpp",
|
||||
"src/lib/tvgTaskScheduler.cpp",
|
||||
"src/loaders/external_png/tvgPngLoader.cpp",
|
||||
"src/loaders/jpg/tvgJpgd.cpp",
|
||||
"src/loaders/jpg/tvgJpgLoader.cpp",
|
||||
"src/loaders/raw/tvgRawLoader.cpp",
|
||||
"src/loaders/svg/tvgSvgCssStyle.cpp",
|
||||
"src/loaders/svg/tvgSvgLoader.cpp",
|
||||
"src/loaders/svg/tvgSvgPath.cpp",
|
||||
"src/loaders/svg/tvgSvgSceneBuilder.cpp",
|
||||
"src/loaders/svg/tvgSvgUtil.cpp",
|
||||
"src/loaders/svg/tvgXmlParser.cpp",
|
||||
"src/loaders/tvg/tvgTvgBinInterpreter.cpp",
|
||||
"src/loaders/tvg/tvgTvgLoader.cpp",
|
||||
"src/savers/tvg/tvgTvgSaver.cpp",
|
||||
]
|
||||
thirdparty_tvg_sources = [thirdparty_tvg_dir + file for file in thirdparty_tvg_sources]
|
||||
|
||||
env_tvg.Append(
|
||||
CPPPATH=[
|
||||
"../../../thirdparty/thorvg/inc",
|
||||
"../../../thirdparty/thorvg/src/lib",
|
||||
"../../../thirdparty/thorvg/src/lib/sw_engine",
|
||||
"../../../thirdparty/thorvg/src/loaders/external_png",
|
||||
"../../../thirdparty/thorvg/src/loaders/jpg",
|
||||
"../../../thirdparty/thorvg/src/loaders/raw",
|
||||
"../../../thirdparty/thorvg/src/loaders/svg",
|
||||
"../../../thirdparty/thorvg/src/loaders/tvg",
|
||||
"../../../thirdparty/thorvg/src/savers/tvg",
|
||||
"../../../thirdparty/libpng",
|
||||
]
|
||||
)
|
||||
env.Append(CPPPATH=["../../../thirdparty/thorvg/inc", "../../../thirdparty/thorvg/src/lib"])
|
||||
env.Append(CPPDEFINES=["MODULE_SVG_ENABLED"])
|
||||
|
||||
lib = env_tvg.Library(
|
||||
f'tvg_builtin{env["suffix"]}{env["LIBSUFFIX"]}',
|
||||
thirdparty_tvg_sources,
|
||||
)
|
||||
env.Append(LIBS=[lib])
|
||||
|
||||
# MSDFGEN
|
||||
if env["msdfgen_enabled"] and env["freetype_enabled"]:
|
||||
env_msdfgen = env.Clone()
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
|
||||
using namespace godot;
|
||||
|
||||
#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get(m_var)
|
||||
|
||||
#else
|
||||
// Headers for building as built-in module.
|
||||
|
||||
|
@ -50,7 +52,7 @@ using namespace godot;
|
|||
#include "core/string/print_string.h"
|
||||
#include "core/string/translation.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For freetype, msdfgen.
|
||||
#include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg.
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -69,6 +71,10 @@ using namespace godot;
|
|||
#include "msdfgen.h"
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
#include "thorvg_svg_in_ot.h"
|
||||
#endif
|
||||
|
||||
/*************************************************************************/
|
||||
/* bmp_font_t HarfBuzz Bitmap font interface */
|
||||
/*************************************************************************/
|
||||
|
@ -1346,6 +1352,9 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f
|
|||
memdelete(fd);
|
||||
ERR_FAIL_V_MSG(false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
|
||||
}
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks());
|
||||
#endif
|
||||
}
|
||||
|
||||
memset(&fd->stream, 0, sizeof(FT_StreamRec));
|
||||
|
@ -1888,6 +1897,9 @@ int64_t TextServerAdvanced::_font_get_face_count(const RID &p_font_rid) const {
|
|||
if (!ft_library) {
|
||||
error = FT_Init_FreeType(&ft_library);
|
||||
ERR_FAIL_COND_V_MSG(error != 0, false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks());
|
||||
#endif
|
||||
}
|
||||
|
||||
FT_StreamRec stream;
|
||||
|
|
|
@ -88,7 +88,7 @@ using namespace godot;
|
|||
#include "core/templates/rid_owner.h"
|
||||
#include "scene/resources/texture.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For freetype, msdfgen.
|
||||
#include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg.
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -117,6 +117,7 @@ using namespace godot;
|
|||
#include FT_ADVANCES_H
|
||||
#include FT_MULTIPLE_MASTERS_H
|
||||
#include FT_BBOX_H
|
||||
#include FT_MODULE_H
|
||||
#include FT_CONFIG_OPTIONS_H
|
||||
#if !defined(FT_CONFIG_OPTION_USE_BROTLI) && !defined(_MSC_VER)
|
||||
#warning FreeType is configured without Brotli support, built-in fonts will not be available.
|
||||
|
|
70
modules/text_server_adv/thorvg_bounds_iterator.cpp
Normal file
70
modules/text_server_adv/thorvg_bounds_iterator.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*************************************************************************/
|
||||
/* thorvg_bounds_iterator.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifdef GDEXTENSION
|
||||
// Headers for building as GDExtension plug-in.
|
||||
|
||||
#include <godot_cpp/godot.hpp>
|
||||
|
||||
using namespace godot;
|
||||
|
||||
#else
|
||||
// Headers for building as built-in module.
|
||||
|
||||
#include "core/typedefs.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For svg.
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
|
||||
#include "thorvg_bounds_iterator.h"
|
||||
|
||||
#include <tvgIteratorAccessor.h>
|
||||
#include <tvgPaint.h>
|
||||
|
||||
// This function uses private ThorVG API to get bounding box of top level children elements.
|
||||
|
||||
void tvg_get_bounds(tvg::Picture *p_picture, float &r_min_x, float &r_min_y, float &r_max_x, float &r_max_y) {
|
||||
tvg::IteratorAccessor itrAccessor;
|
||||
if (tvg::Iterator *it = itrAccessor.iterator(p_picture)) {
|
||||
while (const tvg::Paint *child = it->next()) {
|
||||
float x = 0, y = 0, w = 0, h = 0;
|
||||
child->bounds(&x, &y, &w, &h, true);
|
||||
r_min_x = MIN(x, r_min_x);
|
||||
r_min_y = MIN(y, r_min_y);
|
||||
r_max_x = MAX(x + w, r_max_x);
|
||||
r_max_y = MAX(y + h, r_max_y);
|
||||
}
|
||||
delete (it);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MODULE_SVG_ENABLED
|
58
modules/text_server_adv/thorvg_bounds_iterator.h
Normal file
58
modules/text_server_adv/thorvg_bounds_iterator.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*************************************************************************/
|
||||
/* thorvg_bounds_iterator.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 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 THORVG_BOUNDS_ITERATOR_H
|
||||
#define THORVG_BOUNDS_ITERATOR_H
|
||||
|
||||
#ifdef GDEXTENSION
|
||||
// Headers for building as GDExtension plug-in.
|
||||
|
||||
#include <godot_cpp/core/mutex_lock.hpp>
|
||||
#include <godot_cpp/godot.hpp>
|
||||
|
||||
using namespace godot;
|
||||
|
||||
#else
|
||||
// Headers for building as built-in module.
|
||||
|
||||
#include "core/typedefs.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For svg.
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
|
||||
#include <thorvg.h>
|
||||
|
||||
void tvg_get_bounds(tvg::Picture *p_picture, float &r_min_x, float &r_min_y, float &r_max_x, float &r_max_y);
|
||||
|
||||
#endif // MODULE_SVG_ENABLED
|
||||
|
||||
#endif // THORVG_BOUNDS_ITERATOR_H
|
320
modules/text_server_adv/thorvg_svg_in_ot.cpp
Normal file
320
modules/text_server_adv/thorvg_svg_in_ot.cpp
Normal file
|
@ -0,0 +1,320 @@
|
|||
/*************************************************************************/
|
||||
/* thorvg_svg_in_ot.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifdef GDEXTENSION
|
||||
// Headers for building as GDExtension plug-in.
|
||||
|
||||
#include <godot_cpp/classes/xml_parser.hpp>
|
||||
#include <godot_cpp/core/mutex_lock.hpp>
|
||||
#include <godot_cpp/godot.hpp>
|
||||
#include <godot_cpp/templates/vector.hpp>
|
||||
|
||||
using namespace godot;
|
||||
|
||||
#else
|
||||
// Headers for building as built-in module.
|
||||
|
||||
#include "core/error/error_macros.h"
|
||||
#include "core/io/xml_parser.h"
|
||||
#include "core/os/memory.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/string/ustring.h"
|
||||
#include "core/typedefs.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For svg.
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
|
||||
#include "thorvg_bounds_iterator.h"
|
||||
#include "thorvg_svg_in_ot.h"
|
||||
|
||||
#include <freetype/otsvg.h>
|
||||
#include <ft2build.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
FT_Error tvg_svg_in_ot_init(FT_Pointer *p_state) {
|
||||
*p_state = memnew(TVG_State);
|
||||
|
||||
return FT_Err_Ok;
|
||||
}
|
||||
|
||||
void tvg_svg_in_ot_free(FT_Pointer *p_state) {
|
||||
TVG_State *state = *reinterpret_cast<TVG_State **>(p_state);
|
||||
memdelete(state);
|
||||
}
|
||||
|
||||
FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Pointer *p_state) {
|
||||
TVG_State *state = *reinterpret_cast<TVG_State **>(p_state);
|
||||
if (!state) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "SVG in OT state not initialized.");
|
||||
}
|
||||
MutexLock lock(state->mutex);
|
||||
|
||||
FT_SVG_Document document = (FT_SVG_Document)p_slot->other;
|
||||
FT_Size_Metrics metrics = document->metrics;
|
||||
|
||||
GL_State &gl_state = state->glyph_map[p_slot->glyph_index];
|
||||
if (!gl_state.ready) {
|
||||
Ref<XMLParser> parser;
|
||||
parser.instantiate();
|
||||
#ifdef GDEXTENSION
|
||||
PackedByteArray data;
|
||||
data.resize(document->svg_document_length);
|
||||
memcpy(data.ptrw(), document->svg_document, document->svg_document_length);
|
||||
parser->open_buffer(data);
|
||||
#else
|
||||
parser->_open_buffer((const uint8_t *)document->svg_document, document->svg_document_length);
|
||||
#endif
|
||||
|
||||
float aspect = 1.0f;
|
||||
String xml_body;
|
||||
while (parser->read() == OK) {
|
||||
if (parser->has_attribute("id")) {
|
||||
#ifdef GDEXTENSION
|
||||
const String &gl_name = parser->get_named_attribute_value("id");
|
||||
#else
|
||||
const String &gl_name = parser->get_attribute_value("id");
|
||||
#endif
|
||||
if (gl_name.begins_with("glyph")) {
|
||||
int dot_pos = gl_name.find(".");
|
||||
int64_t gl_idx = gl_name.substr(5, (dot_pos > 0) ? dot_pos - 5 : -1).to_int();
|
||||
if (p_slot->glyph_index != gl_idx) {
|
||||
parser->skip_section();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (parser->get_node_type() == XMLParser::NODE_ELEMENT && parser->get_node_name() == "svg") {
|
||||
if (parser->has_attribute("viewBox")) {
|
||||
#ifdef GDEXTENSION
|
||||
PackedStringArray vb = parser->get_named_attribute_value("viewBox").split(" ");
|
||||
#else
|
||||
Vector<String> vb = parser->get_attribute_value("viewBox").split(" ");
|
||||
#endif
|
||||
|
||||
if (vb.size() == 4) {
|
||||
aspect = vb[2].to_float() / vb[3].to_float();
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
#ifdef GDEXTENSION
|
||||
if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
|
||||
xml_body = xml_body + "<" + parser->get_node_name();
|
||||
for (int i = 0; i < parser->get_attribute_count(); i++) {
|
||||
xml_body = xml_body + " " + parser->get_attribute_name(i) + "=\"" + parser->get_attribute_value(i) + "\"";
|
||||
}
|
||||
xml_body = xml_body + ">";
|
||||
} else if (parser->get_node_type() == XMLParser::NODE_TEXT) {
|
||||
xml_body = xml_body + parser->get_node_data();
|
||||
} else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END) {
|
||||
xml_body = xml_body + "</" + parser->get_node_name() + ">";
|
||||
}
|
||||
#else
|
||||
if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
|
||||
xml_body += vformat("<%s", parser->get_node_name());
|
||||
for (int i = 0; i < parser->get_attribute_count(); i++) {
|
||||
xml_body += vformat(" %s=\"%s\"", parser->get_attribute_name(i), parser->get_attribute_value(i));
|
||||
}
|
||||
xml_body += ">";
|
||||
} else if (parser->get_node_type() == XMLParser::NODE_TEXT) {
|
||||
xml_body += parser->get_node_data();
|
||||
} else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END) {
|
||||
xml_body += vformat("</%s>", parser->get_node_name());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
String temp_xml = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 0 0\">" + xml_body;
|
||||
|
||||
std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen();
|
||||
tvg::Result result = picture->load(temp_xml.utf8().get_data(), temp_xml.utf8().length(), "svg+xml", false);
|
||||
if (result != tvg::Result::Success) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (bounds detection).");
|
||||
}
|
||||
|
||||
float min_x = INFINITY, min_y = INFINITY, max_x = -INFINITY, max_y = -INFINITY;
|
||||
tvg_get_bounds(picture.get(), min_x, min_y, max_x, max_y);
|
||||
|
||||
float new_h = (max_y - min_y);
|
||||
float new_w = (max_x - min_x);
|
||||
|
||||
if (new_h * aspect >= new_w) {
|
||||
new_w = (new_h * aspect);
|
||||
} else {
|
||||
new_h = (new_w / aspect);
|
||||
}
|
||||
|
||||
#ifdef GDEXTENSION
|
||||
gl_state.xml_code = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"" + rtos(min_x) + " " + rtos(min_y) + " " + rtos(new_w) + " " + rtos(new_h) + "\">" + xml_body;
|
||||
#else
|
||||
gl_state.xml_code = vformat("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"%f %f %f %f\">", min_x, min_y, new_w, new_h) + xml_body;
|
||||
#endif
|
||||
|
||||
picture = tvg::Picture::gen();
|
||||
result = picture->load(gl_state.xml_code.utf8().get_data(), gl_state.xml_code.utf8().length(), "svg+xml", false);
|
||||
if (result != tvg::Result::Success) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph metrics).");
|
||||
}
|
||||
|
||||
float x_svg_to_out, y_svg_to_out;
|
||||
x_svg_to_out = (float)metrics.x_ppem / new_w;
|
||||
y_svg_to_out = (float)metrics.y_ppem / new_h;
|
||||
|
||||
gl_state.m.e11 = (double)document->transform.xx / (1 << 16) * x_svg_to_out;
|
||||
gl_state.m.e12 = -(double)document->transform.xy / (1 << 16) * x_svg_to_out;
|
||||
gl_state.m.e21 = -(double)document->transform.yx / (1 << 16) * y_svg_to_out;
|
||||
gl_state.m.e22 = (double)document->transform.yy / (1 << 16) * y_svg_to_out;
|
||||
gl_state.m.e13 = (double)document->delta.x / 64 * new_w / metrics.x_ppem;
|
||||
gl_state.m.e23 = -(double)document->delta.y / 64 * new_h / metrics.y_ppem;
|
||||
gl_state.m.e31 = 0;
|
||||
gl_state.m.e32 = 0;
|
||||
gl_state.m.e33 = 1;
|
||||
|
||||
result = picture->transform(gl_state.m);
|
||||
if (result != tvg::Result::Success) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to apply transform to SVG document.");
|
||||
}
|
||||
|
||||
result = picture->bounds(&gl_state.x, &gl_state.y, &gl_state.w, &gl_state.h, true);
|
||||
if (result != tvg::Result::Success) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to get SVG bounds.");
|
||||
}
|
||||
|
||||
gl_state.bmp_y = -min_y * gl_state.h / new_h;
|
||||
gl_state.bmp_x = min_x * gl_state.w / new_w;
|
||||
|
||||
gl_state.ready = true;
|
||||
}
|
||||
|
||||
p_slot->bitmap_left = (FT_Int)gl_state.bmp_x;
|
||||
p_slot->bitmap_top = (FT_Int)gl_state.bmp_y;
|
||||
|
||||
float tmp = ceil(gl_state.h);
|
||||
p_slot->bitmap.rows = (unsigned int)tmp;
|
||||
tmp = ceil(gl_state.w);
|
||||
p_slot->bitmap.width = (unsigned int)tmp;
|
||||
p_slot->bitmap.pitch = (int)p_slot->bitmap.width * 4;
|
||||
p_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
|
||||
|
||||
float metrics_width, metrics_height;
|
||||
float horiBearingX, horiBearingY;
|
||||
float vertBearingX, vertBearingY;
|
||||
|
||||
metrics_width = (float)gl_state.w;
|
||||
metrics_height = (float)gl_state.h;
|
||||
horiBearingX = (float)gl_state.x;
|
||||
horiBearingY = (float)-gl_state.y;
|
||||
vertBearingX = p_slot->metrics.horiBearingX / 64.0f - p_slot->metrics.horiAdvance / 64.0f / 2;
|
||||
vertBearingY = (p_slot->metrics.vertAdvance / 64.0f - p_slot->metrics.height / 64.0f) / 2;
|
||||
|
||||
tmp = roundf(metrics_width * 64);
|
||||
p_slot->metrics.width = (FT_Pos)tmp;
|
||||
tmp = roundf(metrics_height * 64);
|
||||
p_slot->metrics.height = (FT_Pos)tmp;
|
||||
|
||||
p_slot->metrics.horiBearingX = (FT_Pos)(horiBearingX * 64);
|
||||
p_slot->metrics.horiBearingY = (FT_Pos)(horiBearingY * 64);
|
||||
p_slot->metrics.vertBearingX = (FT_Pos)(vertBearingX * 64);
|
||||
p_slot->metrics.vertBearingY = (FT_Pos)(vertBearingY * 64);
|
||||
|
||||
if (p_slot->metrics.vertAdvance == 0) {
|
||||
p_slot->metrics.vertAdvance = (FT_Pos)(metrics_height * 1.2f * 64);
|
||||
}
|
||||
|
||||
return FT_Err_Ok;
|
||||
}
|
||||
|
||||
FT_Error tvg_svg_in_ot_render(FT_GlyphSlot p_slot, FT_Pointer *p_state) {
|
||||
TVG_State *state = *reinterpret_cast<TVG_State **>(p_state);
|
||||
if (!state) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "SVG in OT state not initialized.");
|
||||
}
|
||||
MutexLock lock(state->mutex);
|
||||
|
||||
if (!state->glyph_map.has(p_slot->glyph_index)) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "SVG glyph not loaded.");
|
||||
}
|
||||
|
||||
GL_State &gl_state = state->glyph_map[p_slot->glyph_index];
|
||||
ERR_FAIL_COND_V_MSG(!gl_state.ready, FT_Err_Invalid_SVG_Document, "SVG glyph not ready.");
|
||||
|
||||
std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen();
|
||||
tvg::Result res = picture->load(gl_state.xml_code.utf8().get_data(), gl_state.xml_code.utf8().length(), "svg+xml", false);
|
||||
if (res != tvg::Result::Success) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph rendering).");
|
||||
}
|
||||
res = picture->transform(gl_state.m);
|
||||
if (res != tvg::Result::Success) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to apply transform to SVG document.");
|
||||
}
|
||||
|
||||
std::unique_ptr<tvg::SwCanvas> sw_canvas = tvg::SwCanvas::gen();
|
||||
res = sw_canvas->target((uint32_t *)p_slot->bitmap.buffer, (int)p_slot->bitmap.width, (int)p_slot->bitmap.width, (int)p_slot->bitmap.rows, tvg::SwCanvas::ARGB8888_STRAIGHT);
|
||||
if (res != tvg::Result::Success) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to create SVG canvas.");
|
||||
}
|
||||
res = sw_canvas->push(std::move(picture));
|
||||
if (res != tvg::Result::Success) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to set SVG canvas source.");
|
||||
}
|
||||
res = sw_canvas->draw();
|
||||
if (res != tvg::Result::Success) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to draw to SVG canvas.");
|
||||
}
|
||||
res = sw_canvas->sync();
|
||||
if (res != tvg::Result::Success) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to sync SVG canvas.");
|
||||
}
|
||||
|
||||
state->glyph_map.erase(p_slot->glyph_index);
|
||||
|
||||
p_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
|
||||
p_slot->bitmap.num_grays = 256;
|
||||
p_slot->format = FT_GLYPH_FORMAT_BITMAP;
|
||||
|
||||
return FT_Err_Ok;
|
||||
}
|
||||
|
||||
SVG_RendererHooks tvg_svg_in_ot_hooks = {
|
||||
(SVG_Lib_Init_Func)tvg_svg_in_ot_init,
|
||||
(SVG_Lib_Free_Func)tvg_svg_in_ot_free,
|
||||
(SVG_Lib_Render_Func)tvg_svg_in_ot_render,
|
||||
(SVG_Lib_Preset_Slot_Func)tvg_svg_in_ot_preset_slot,
|
||||
};
|
||||
|
||||
SVG_RendererHooks *get_tvg_svg_in_ot_hooks() {
|
||||
return &tvg_svg_in_ot_hooks;
|
||||
}
|
||||
|
||||
#endif // MODULE_SVG_ENABLED
|
86
modules/text_server_adv/thorvg_svg_in_ot.h
Normal file
86
modules/text_server_adv/thorvg_svg_in_ot.h
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*************************************************************************/
|
||||
/* thorvg_svg_in_ot.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 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 THORVG_SVG_IN_OT_H
|
||||
#define THORVG_SVG_IN_OT_H
|
||||
|
||||
#ifdef GDEXTENSION
|
||||
// Headers for building as GDExtension plug-in.
|
||||
|
||||
#include <godot_cpp/core/mutex_lock.hpp>
|
||||
#include <godot_cpp/godot.hpp>
|
||||
#include <godot_cpp/templates/hash_map.hpp>
|
||||
|
||||
using namespace godot;
|
||||
|
||||
#else
|
||||
// Headers for building as built-in module.
|
||||
|
||||
#include "core/os/mutex.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/typedefs.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For svg.
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
|
||||
#include <freetype/freetype.h>
|
||||
#include <freetype/otsvg.h>
|
||||
#include <ft2build.h>
|
||||
#include <thorvg.h>
|
||||
|
||||
struct GL_State {
|
||||
bool ready = false;
|
||||
float bmp_x = 0;
|
||||
float bmp_y = 0;
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
float w = 0;
|
||||
float h = 0;
|
||||
String xml_code;
|
||||
tvg::Matrix m;
|
||||
};
|
||||
|
||||
struct TVG_State {
|
||||
Mutex mutex;
|
||||
HashMap<uint32_t, GL_State> glyph_map;
|
||||
};
|
||||
|
||||
FT_Error tvg_svg_in_ot_init(FT_Pointer *p_state);
|
||||
void tvg_svg_in_ot_free(FT_Pointer *p_state);
|
||||
FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Pointer *p_state);
|
||||
FT_Error tvg_svg_in_ot_render(FT_GlyphSlot p_slot, FT_Pointer *p_state);
|
||||
|
||||
SVG_RendererHooks *get_tvg_svg_in_ot_hooks();
|
||||
|
||||
#endif // MODULE_SVG_ENABLED
|
||||
|
||||
#endif // THORVG_SVG_IN_OT_H
|
|
@ -8,6 +8,9 @@ msdfgen_enabled = "msdfgen" in env.module_list
|
|||
|
||||
env_text_server_fb = env_modules.Clone()
|
||||
|
||||
if "svg" in env.module_list:
|
||||
env_text_server_fb.Prepend(CPPPATH=["#thirdparty/thorvg/inc", "#thirdparty/thorvg/src/lib"])
|
||||
|
||||
if env["builtin_msdfgen"] and msdfgen_enabled:
|
||||
env_text_server_fb.Prepend(CPPPATH=["#thirdparty/msdfgen"])
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ opts = Variables([], ARGUMENTS)
|
|||
opts.Add(BoolVariable("brotli_enabled", "Use Brotli library", True))
|
||||
opts.Add(BoolVariable("freetype_enabled", "Use FreeType library", True))
|
||||
opts.Add(BoolVariable("msdfgen_enabled", "Use MSDFgen library (require FreeType)", True))
|
||||
opts.Add(BoolVariable("thorvg_enabled", "Use ThorVG library (require FreeType)", True))
|
||||
opts.Add(BoolVariable("verbose", "Enable verbose output for the compilation", False))
|
||||
|
||||
opts.Update(env)
|
||||
|
@ -29,6 +30,79 @@ opts.Update(env)
|
|||
if not env["verbose"]:
|
||||
methods.no_verbose(sys, env)
|
||||
|
||||
# ThorVG
|
||||
if env["thorvg_enabled"] and env["freetype_enabled"]:
|
||||
env_tvg = env.Clone()
|
||||
env_tvg.disable_warnings()
|
||||
|
||||
thirdparty_tvg_dir = "../../../thirdparty/thorvg/"
|
||||
thirdparty_tvg_sources = [
|
||||
"src/lib/sw_engine/tvgSwFill.cpp",
|
||||
"src/lib/sw_engine/tvgSwImage.cpp",
|
||||
"src/lib/sw_engine/tvgSwMath.cpp",
|
||||
"src/lib/sw_engine/tvgSwMemPool.cpp",
|
||||
"src/lib/sw_engine/tvgSwRaster.cpp",
|
||||
"src/lib/sw_engine/tvgSwRenderer.cpp",
|
||||
"src/lib/sw_engine/tvgSwRle.cpp",
|
||||
"src/lib/sw_engine/tvgSwShape.cpp",
|
||||
"src/lib/sw_engine/tvgSwStroke.cpp",
|
||||
"src/lib/tvgAccessor.cpp",
|
||||
"src/lib/tvgBezier.cpp",
|
||||
"src/lib/tvgCanvas.cpp",
|
||||
"src/lib/tvgFill.cpp",
|
||||
"src/lib/tvgGlCanvas.cpp",
|
||||
"src/lib/tvgInitializer.cpp",
|
||||
"src/lib/tvgLinearGradient.cpp",
|
||||
"src/lib/tvgLoader.cpp",
|
||||
"src/lib/tvgLzw.cpp",
|
||||
"src/lib/tvgPaint.cpp",
|
||||
"src/lib/tvgPicture.cpp",
|
||||
"src/lib/tvgRadialGradient.cpp",
|
||||
"src/lib/tvgRender.cpp",
|
||||
"src/lib/tvgSaver.cpp",
|
||||
"src/lib/tvgScene.cpp",
|
||||
"src/lib/tvgShape.cpp",
|
||||
"src/lib/tvgSwCanvas.cpp",
|
||||
"src/lib/tvgTaskScheduler.cpp",
|
||||
"src/loaders/external_png/tvgPngLoader.cpp",
|
||||
"src/loaders/jpg/tvgJpgd.cpp",
|
||||
"src/loaders/jpg/tvgJpgLoader.cpp",
|
||||
"src/loaders/raw/tvgRawLoader.cpp",
|
||||
"src/loaders/svg/tvgSvgCssStyle.cpp",
|
||||
"src/loaders/svg/tvgSvgLoader.cpp",
|
||||
"src/loaders/svg/tvgSvgPath.cpp",
|
||||
"src/loaders/svg/tvgSvgSceneBuilder.cpp",
|
||||
"src/loaders/svg/tvgSvgUtil.cpp",
|
||||
"src/loaders/svg/tvgXmlParser.cpp",
|
||||
"src/loaders/tvg/tvgTvgBinInterpreter.cpp",
|
||||
"src/loaders/tvg/tvgTvgLoader.cpp",
|
||||
"src/savers/tvg/tvgTvgSaver.cpp",
|
||||
]
|
||||
thirdparty_tvg_sources = [thirdparty_tvg_dir + file for file in thirdparty_tvg_sources]
|
||||
|
||||
env_tvg.Append(
|
||||
CPPPATH=[
|
||||
"../../../thirdparty/thorvg/inc",
|
||||
"../../../thirdparty/thorvg/src/lib",
|
||||
"../../../thirdparty/thorvg/src/lib/sw_engine",
|
||||
"../../../thirdparty/thorvg/src/loaders/external_png",
|
||||
"../../../thirdparty/thorvg/src/loaders/jpg",
|
||||
"../../../thirdparty/thorvg/src/loaders/raw",
|
||||
"../../../thirdparty/thorvg/src/loaders/svg",
|
||||
"../../../thirdparty/thorvg/src/loaders/tvg",
|
||||
"../../../thirdparty/thorvg/src/savers/tvg",
|
||||
"../../../thirdparty/libpng",
|
||||
]
|
||||
)
|
||||
env.Append(CPPPATH=["../../../thirdparty/thorvg/inc", "../../../thirdparty/thorvg/src/lib"])
|
||||
env.Append(CPPDEFINES=["MODULE_SVG_ENABLED"])
|
||||
|
||||
lib = env_tvg.Library(
|
||||
f'tvg_builtin{env["suffix"]}{env["LIBSUFFIX"]}',
|
||||
thirdparty_tvg_sources,
|
||||
)
|
||||
env.Append(LIBS=[lib])
|
||||
|
||||
# MSDFGEN
|
||||
if env["msdfgen_enabled"] and env["freetype_enabled"]:
|
||||
env_msdfgen = env.Clone()
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
|
||||
using namespace godot;
|
||||
|
||||
#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get(m_var)
|
||||
|
||||
#else
|
||||
// Headers for building as built-in module.
|
||||
|
||||
|
@ -49,7 +51,7 @@ using namespace godot;
|
|||
#include "core/string/print_string.h"
|
||||
#include "core/string/ucaps.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For freetype, msdfgen.
|
||||
#include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg.
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -62,6 +64,10 @@ using namespace godot;
|
|||
#include "msdfgen.h"
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
#include "thorvg_svg_in_ot.h"
|
||||
#endif
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
#define OT_TAG(c1, c2, c3, c4) ((int32_t)((((uint32_t)(c1)&0xff) << 24) | (((uint32_t)(c2)&0xff) << 16) | (((uint32_t)(c3)&0xff) << 8) | ((uint32_t)(c4)&0xff)))
|
||||
|
@ -771,6 +777,9 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_f
|
|||
memdelete(fd);
|
||||
ERR_FAIL_V_MSG(false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
|
||||
}
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks());
|
||||
#endif
|
||||
}
|
||||
|
||||
memset(&fd->stream, 0, sizeof(FT_StreamRec));
|
||||
|
@ -992,6 +1001,9 @@ int64_t TextServerFallback::_font_get_face_count(const RID &p_font_rid) const {
|
|||
if (!ft_library) {
|
||||
error = FT_Init_FreeType(&ft_library);
|
||||
ERR_FAIL_COND_V_MSG(error != 0, false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks());
|
||||
#endif
|
||||
}
|
||||
|
||||
FT_StreamRec stream;
|
||||
|
|
|
@ -87,7 +87,7 @@ using namespace godot;
|
|||
#include "core/templates/rid_owner.h"
|
||||
#include "scene/resources/texture.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For freetype, msdfgen.
|
||||
#include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg.
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -101,6 +101,7 @@ using namespace godot;
|
|||
#include FT_ADVANCES_H
|
||||
#include FT_MULTIPLE_MASTERS_H
|
||||
#include FT_BBOX_H
|
||||
#include FT_MODULE_H
|
||||
#include FT_CONFIG_OPTIONS_H
|
||||
#if !defined(FT_CONFIG_OPTION_USE_BROTLI) && !defined(_MSC_VER)
|
||||
#warning FreeType is configured without Brotli support, built-in fonts will not be available.
|
||||
|
|
70
modules/text_server_fb/thorvg_bounds_iterator.cpp
Normal file
70
modules/text_server_fb/thorvg_bounds_iterator.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*************************************************************************/
|
||||
/* thorvg_bounds_iterator.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifdef GDEXTENSION
|
||||
// Headers for building as GDExtension plug-in.
|
||||
|
||||
#include <godot_cpp/godot.hpp>
|
||||
|
||||
using namespace godot;
|
||||
|
||||
#else
|
||||
// Headers for building as built-in module.
|
||||
|
||||
#include "core/typedefs.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For svg.
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
|
||||
#include "thorvg_bounds_iterator.h"
|
||||
|
||||
#include <tvgIteratorAccessor.h>
|
||||
#include <tvgPaint.h>
|
||||
|
||||
// This function uses private ThorVG API to get bounding box of top level children elements.
|
||||
|
||||
void tvg_get_bounds(tvg::Picture *p_picture, float &r_min_x, float &r_min_y, float &r_max_x, float &r_max_y) {
|
||||
tvg::IteratorAccessor itrAccessor;
|
||||
if (tvg::Iterator *it = itrAccessor.iterator(p_picture)) {
|
||||
while (const tvg::Paint *child = it->next()) {
|
||||
float x = 0, y = 0, w = 0, h = 0;
|
||||
child->bounds(&x, &y, &w, &h, true);
|
||||
r_min_x = MIN(x, r_min_x);
|
||||
r_min_y = MIN(y, r_min_y);
|
||||
r_max_x = MAX(x + w, r_max_x);
|
||||
r_max_y = MAX(y + h, r_max_y);
|
||||
}
|
||||
delete (it);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MODULE_SVG_ENABLED
|
58
modules/text_server_fb/thorvg_bounds_iterator.h
Normal file
58
modules/text_server_fb/thorvg_bounds_iterator.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*************************************************************************/
|
||||
/* thorvg_bounds_iterator.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 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 THORVG_BOUNDS_ITERATOR_H
|
||||
#define THORVG_BOUNDS_ITERATOR_H
|
||||
|
||||
#ifdef GDEXTENSION
|
||||
// Headers for building as GDExtension plug-in.
|
||||
|
||||
#include <godot_cpp/core/mutex_lock.hpp>
|
||||
#include <godot_cpp/godot.hpp>
|
||||
|
||||
using namespace godot;
|
||||
|
||||
#else
|
||||
// Headers for building as built-in module.
|
||||
|
||||
#include "core/typedefs.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For svg.
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
|
||||
#include <thorvg.h>
|
||||
|
||||
void tvg_get_bounds(tvg::Picture *p_picture, float &r_min_x, float &r_min_y, float &r_max_x, float &r_max_y);
|
||||
|
||||
#endif // MODULE_SVG_ENABLED
|
||||
|
||||
#endif // THORVG_BOUNDS_ITERATOR_H
|
320
modules/text_server_fb/thorvg_svg_in_ot.cpp
Normal file
320
modules/text_server_fb/thorvg_svg_in_ot.cpp
Normal file
|
@ -0,0 +1,320 @@
|
|||
/*************************************************************************/
|
||||
/* thorvg_svg_in_ot.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifdef GDEXTENSION
|
||||
// Headers for building as GDExtension plug-in.
|
||||
|
||||
#include <godot_cpp/classes/xml_parser.hpp>
|
||||
#include <godot_cpp/core/mutex_lock.hpp>
|
||||
#include <godot_cpp/godot.hpp>
|
||||
#include <godot_cpp/templates/vector.hpp>
|
||||
|
||||
using namespace godot;
|
||||
|
||||
#else
|
||||
// Headers for building as built-in module.
|
||||
|
||||
#include "core/error/error_macros.h"
|
||||
#include "core/io/xml_parser.h"
|
||||
#include "core/os/memory.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/string/ustring.h"
|
||||
#include "core/typedefs.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For svg.
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
|
||||
#include "thorvg_bounds_iterator.h"
|
||||
#include "thorvg_svg_in_ot.h"
|
||||
|
||||
#include <freetype/otsvg.h>
|
||||
#include <ft2build.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
FT_Error tvg_svg_in_ot_init(FT_Pointer *p_state) {
|
||||
*p_state = memnew(TVG_State);
|
||||
|
||||
return FT_Err_Ok;
|
||||
}
|
||||
|
||||
void tvg_svg_in_ot_free(FT_Pointer *p_state) {
|
||||
TVG_State *state = *reinterpret_cast<TVG_State **>(p_state);
|
||||
memdelete(state);
|
||||
}
|
||||
|
||||
FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Pointer *p_state) {
|
||||
TVG_State *state = *reinterpret_cast<TVG_State **>(p_state);
|
||||
if (!state) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "SVG in OT state not initialized.");
|
||||
}
|
||||
MutexLock lock(state->mutex);
|
||||
|
||||
FT_SVG_Document document = (FT_SVG_Document)p_slot->other;
|
||||
FT_Size_Metrics metrics = document->metrics;
|
||||
|
||||
GL_State &gl_state = state->glyph_map[p_slot->glyph_index];
|
||||
if (!gl_state.ready) {
|
||||
Ref<XMLParser> parser;
|
||||
parser.instantiate();
|
||||
#ifdef GDEXTENSION
|
||||
PackedByteArray data;
|
||||
data.resize(document->svg_document_length);
|
||||
memcpy(data.ptrw(), document->svg_document, document->svg_document_length);
|
||||
parser->open_buffer(data);
|
||||
#else
|
||||
parser->_open_buffer((const uint8_t *)document->svg_document, document->svg_document_length);
|
||||
#endif
|
||||
|
||||
float aspect = 1.0f;
|
||||
String xml_body;
|
||||
while (parser->read() == OK) {
|
||||
if (parser->has_attribute("id")) {
|
||||
#ifdef GDEXTENSION
|
||||
const String &gl_name = parser->get_named_attribute_value("id");
|
||||
#else
|
||||
const String &gl_name = parser->get_attribute_value("id");
|
||||
#endif
|
||||
if (gl_name.begins_with("glyph")) {
|
||||
int dot_pos = gl_name.find(".");
|
||||
int64_t gl_idx = gl_name.substr(5, (dot_pos > 0) ? dot_pos - 5 : -1).to_int();
|
||||
if (p_slot->glyph_index != gl_idx) {
|
||||
parser->skip_section();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (parser->get_node_type() == XMLParser::NODE_ELEMENT && parser->get_node_name() == "svg") {
|
||||
if (parser->has_attribute("viewBox")) {
|
||||
#ifdef GDEXTENSION
|
||||
PackedStringArray vb = parser->get_named_attribute_value("viewBox").split(" ");
|
||||
#else
|
||||
Vector<String> vb = parser->get_attribute_value("viewBox").split(" ");
|
||||
#endif
|
||||
|
||||
if (vb.size() == 4) {
|
||||
aspect = vb[2].to_float() / vb[3].to_float();
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
#ifdef GDEXTENSION
|
||||
if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
|
||||
xml_body = xml_body + "<" + parser->get_node_name();
|
||||
for (int i = 0; i < parser->get_attribute_count(); i++) {
|
||||
xml_body = xml_body + " " + parser->get_attribute_name(i) + "=\"" + parser->get_attribute_value(i) + "\"";
|
||||
}
|
||||
xml_body = xml_body + ">";
|
||||
} else if (parser->get_node_type() == XMLParser::NODE_TEXT) {
|
||||
xml_body = xml_body + parser->get_node_data();
|
||||
} else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END) {
|
||||
xml_body = xml_body + "</" + parser->get_node_name() + ">";
|
||||
}
|
||||
#else
|
||||
if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
|
||||
xml_body += vformat("<%s", parser->get_node_name());
|
||||
for (int i = 0; i < parser->get_attribute_count(); i++) {
|
||||
xml_body += vformat(" %s=\"%s\"", parser->get_attribute_name(i), parser->get_attribute_value(i));
|
||||
}
|
||||
xml_body += ">";
|
||||
} else if (parser->get_node_type() == XMLParser::NODE_TEXT) {
|
||||
xml_body += parser->get_node_data();
|
||||
} else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END) {
|
||||
xml_body += vformat("</%s>", parser->get_node_name());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
String temp_xml = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 0 0\">" + xml_body;
|
||||
|
||||
std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen();
|
||||
tvg::Result result = picture->load(temp_xml.utf8().get_data(), temp_xml.utf8().length(), "svg+xml", false);
|
||||
if (result != tvg::Result::Success) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (bounds detection).");
|
||||
}
|
||||
|
||||
float min_x = INFINITY, min_y = INFINITY, max_x = -INFINITY, max_y = -INFINITY;
|
||||
tvg_get_bounds(picture.get(), min_x, min_y, max_x, max_y);
|
||||
|
||||
float new_h = (max_y - min_y);
|
||||
float new_w = (max_x - min_x);
|
||||
|
||||
if (new_h * aspect >= new_w) {
|
||||
new_w = (new_h * aspect);
|
||||
} else {
|
||||
new_h = (new_w / aspect);
|
||||
}
|
||||
|
||||
#ifdef GDEXTENSION
|
||||
gl_state.xml_code = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"" + rtos(min_x) + " " + rtos(min_y) + " " + rtos(new_w) + " " + rtos(new_h) + "\">" + xml_body;
|
||||
#else
|
||||
gl_state.xml_code = vformat("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"%f %f %f %f\">", min_x, min_y, new_w, new_h) + xml_body;
|
||||
#endif
|
||||
|
||||
picture = tvg::Picture::gen();
|
||||
result = picture->load(gl_state.xml_code.utf8().get_data(), gl_state.xml_code.utf8().length(), "svg+xml", false);
|
||||
if (result != tvg::Result::Success) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph metrics).");
|
||||
}
|
||||
|
||||
float x_svg_to_out, y_svg_to_out;
|
||||
x_svg_to_out = (float)metrics.x_ppem / new_w;
|
||||
y_svg_to_out = (float)metrics.y_ppem / new_h;
|
||||
|
||||
gl_state.m.e11 = (double)document->transform.xx / (1 << 16) * x_svg_to_out;
|
||||
gl_state.m.e12 = -(double)document->transform.xy / (1 << 16) * x_svg_to_out;
|
||||
gl_state.m.e21 = -(double)document->transform.yx / (1 << 16) * y_svg_to_out;
|
||||
gl_state.m.e22 = (double)document->transform.yy / (1 << 16) * y_svg_to_out;
|
||||
gl_state.m.e13 = (double)document->delta.x / 64 * new_w / metrics.x_ppem;
|
||||
gl_state.m.e23 = -(double)document->delta.y / 64 * new_h / metrics.y_ppem;
|
||||
gl_state.m.e31 = 0;
|
||||
gl_state.m.e32 = 0;
|
||||
gl_state.m.e33 = 1;
|
||||
|
||||
result = picture->transform(gl_state.m);
|
||||
if (result != tvg::Result::Success) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to apply transform to SVG document.");
|
||||
}
|
||||
|
||||
result = picture->bounds(&gl_state.x, &gl_state.y, &gl_state.w, &gl_state.h, true);
|
||||
if (result != tvg::Result::Success) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to get SVG bounds.");
|
||||
}
|
||||
|
||||
gl_state.bmp_y = -min_y * gl_state.h / new_h;
|
||||
gl_state.bmp_x = min_x * gl_state.w / new_w;
|
||||
|
||||
gl_state.ready = true;
|
||||
}
|
||||
|
||||
p_slot->bitmap_left = (FT_Int)gl_state.bmp_x;
|
||||
p_slot->bitmap_top = (FT_Int)gl_state.bmp_y;
|
||||
|
||||
float tmp = ceil(gl_state.h);
|
||||
p_slot->bitmap.rows = (unsigned int)tmp;
|
||||
tmp = ceil(gl_state.w);
|
||||
p_slot->bitmap.width = (unsigned int)tmp;
|
||||
p_slot->bitmap.pitch = (int)p_slot->bitmap.width * 4;
|
||||
p_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
|
||||
|
||||
float metrics_width, metrics_height;
|
||||
float horiBearingX, horiBearingY;
|
||||
float vertBearingX, vertBearingY;
|
||||
|
||||
metrics_width = (float)gl_state.w;
|
||||
metrics_height = (float)gl_state.h;
|
||||
horiBearingX = (float)gl_state.x;
|
||||
horiBearingY = (float)-gl_state.y;
|
||||
vertBearingX = p_slot->metrics.horiBearingX / 64.0f - p_slot->metrics.horiAdvance / 64.0f / 2;
|
||||
vertBearingY = (p_slot->metrics.vertAdvance / 64.0f - p_slot->metrics.height / 64.0f) / 2;
|
||||
|
||||
tmp = roundf(metrics_width * 64);
|
||||
p_slot->metrics.width = (FT_Pos)tmp;
|
||||
tmp = roundf(metrics_height * 64);
|
||||
p_slot->metrics.height = (FT_Pos)tmp;
|
||||
|
||||
p_slot->metrics.horiBearingX = (FT_Pos)(horiBearingX * 64);
|
||||
p_slot->metrics.horiBearingY = (FT_Pos)(horiBearingY * 64);
|
||||
p_slot->metrics.vertBearingX = (FT_Pos)(vertBearingX * 64);
|
||||
p_slot->metrics.vertBearingY = (FT_Pos)(vertBearingY * 64);
|
||||
|
||||
if (p_slot->metrics.vertAdvance == 0) {
|
||||
p_slot->metrics.vertAdvance = (FT_Pos)(metrics_height * 1.2f * 64);
|
||||
}
|
||||
|
||||
return FT_Err_Ok;
|
||||
}
|
||||
|
||||
FT_Error tvg_svg_in_ot_render(FT_GlyphSlot p_slot, FT_Pointer *p_state) {
|
||||
TVG_State *state = *reinterpret_cast<TVG_State **>(p_state);
|
||||
if (!state) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "SVG in OT state not initialized.");
|
||||
}
|
||||
MutexLock lock(state->mutex);
|
||||
|
||||
if (!state->glyph_map.has(p_slot->glyph_index)) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "SVG glyph not loaded.");
|
||||
}
|
||||
|
||||
GL_State &gl_state = state->glyph_map[p_slot->glyph_index];
|
||||
ERR_FAIL_COND_V_MSG(!gl_state.ready, FT_Err_Invalid_SVG_Document, "SVG glyph not ready.");
|
||||
|
||||
std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen();
|
||||
tvg::Result res = picture->load(gl_state.xml_code.utf8().get_data(), gl_state.xml_code.utf8().length(), "svg+xml", false);
|
||||
if (res != tvg::Result::Success) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph rendering).");
|
||||
}
|
||||
res = picture->transform(gl_state.m);
|
||||
if (res != tvg::Result::Success) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to apply transform to SVG document.");
|
||||
}
|
||||
|
||||
std::unique_ptr<tvg::SwCanvas> sw_canvas = tvg::SwCanvas::gen();
|
||||
res = sw_canvas->target((uint32_t *)p_slot->bitmap.buffer, (int)p_slot->bitmap.width, (int)p_slot->bitmap.width, (int)p_slot->bitmap.rows, tvg::SwCanvas::ARGB8888_STRAIGHT);
|
||||
if (res != tvg::Result::Success) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to create SVG canvas.");
|
||||
}
|
||||
res = sw_canvas->push(std::move(picture));
|
||||
if (res != tvg::Result::Success) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to set SVG canvas source.");
|
||||
}
|
||||
res = sw_canvas->draw();
|
||||
if (res != tvg::Result::Success) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to draw to SVG canvas.");
|
||||
}
|
||||
res = sw_canvas->sync();
|
||||
if (res != tvg::Result::Success) {
|
||||
ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to sync SVG canvas.");
|
||||
}
|
||||
|
||||
state->glyph_map.erase(p_slot->glyph_index);
|
||||
|
||||
p_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
|
||||
p_slot->bitmap.num_grays = 256;
|
||||
p_slot->format = FT_GLYPH_FORMAT_BITMAP;
|
||||
|
||||
return FT_Err_Ok;
|
||||
}
|
||||
|
||||
SVG_RendererHooks tvg_svg_in_ot_hooks = {
|
||||
(SVG_Lib_Init_Func)tvg_svg_in_ot_init,
|
||||
(SVG_Lib_Free_Func)tvg_svg_in_ot_free,
|
||||
(SVG_Lib_Render_Func)tvg_svg_in_ot_render,
|
||||
(SVG_Lib_Preset_Slot_Func)tvg_svg_in_ot_preset_slot,
|
||||
};
|
||||
|
||||
SVG_RendererHooks *get_tvg_svg_in_ot_hooks() {
|
||||
return &tvg_svg_in_ot_hooks;
|
||||
}
|
||||
|
||||
#endif // MODULE_SVG_ENABLED
|
86
modules/text_server_fb/thorvg_svg_in_ot.h
Normal file
86
modules/text_server_fb/thorvg_svg_in_ot.h
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*************************************************************************/
|
||||
/* thorvg_svg_in_ot.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 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 THORVG_SVG_IN_OT_H
|
||||
#define THORVG_SVG_IN_OT_H
|
||||
|
||||
#ifdef GDEXTENSION
|
||||
// Headers for building as GDExtension plug-in.
|
||||
|
||||
#include <godot_cpp/core/mutex_lock.hpp>
|
||||
#include <godot_cpp/godot.hpp>
|
||||
#include <godot_cpp/templates/hash_map.hpp>
|
||||
|
||||
using namespace godot;
|
||||
|
||||
#else
|
||||
// Headers for building as built-in module.
|
||||
|
||||
#include "core/os/mutex.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/typedefs.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For svg.
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
|
||||
#include <freetype/freetype.h>
|
||||
#include <freetype/otsvg.h>
|
||||
#include <ft2build.h>
|
||||
#include <thorvg.h>
|
||||
|
||||
struct GL_State {
|
||||
bool ready = false;
|
||||
float bmp_x = 0;
|
||||
float bmp_y = 0;
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
float w = 0;
|
||||
float h = 0;
|
||||
String xml_code;
|
||||
tvg::Matrix m;
|
||||
};
|
||||
|
||||
struct TVG_State {
|
||||
Mutex mutex;
|
||||
HashMap<uint32_t, GL_State> glyph_map;
|
||||
};
|
||||
|
||||
FT_Error tvg_svg_in_ot_init(FT_Pointer *p_state);
|
||||
void tvg_svg_in_ot_free(FT_Pointer *p_state);
|
||||
FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Pointer *p_state);
|
||||
FT_Error tvg_svg_in_ot_render(FT_GlyphSlot p_slot, FT_Pointer *p_state);
|
||||
|
||||
SVG_RendererHooks *get_tvg_svg_in_ot_hooks();
|
||||
|
||||
#endif // MODULE_SVG_ENABLED
|
||||
|
||||
#endif // THORVG_SVG_IN_OT_H
|
Loading…
Reference in a new issue