Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve emoji SVG parsing by caching #100300

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 152 additions & 38 deletions modules/text_server_adv/thorvg_svg_in_ot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,59 +91,173 @@
parser->_open_buffer((const uint8_t *)document->svg_document, document->svg_document_length);

String xml_body;
String xml_node;

double embox_x = document->units_per_EM;
double embox_y = document->units_per_EM;
while (parser->read() == OK) {
if (parser->has_attribute("id")) {
const String &gl_name = parser->get_named_attribute_value("id");
if (gl_name.begins_with("glyph")) {

uint64_t offset = 0;
if (!state->document_map.has(document->svg_document)) {
state->document_map[document->svg_document] = TVG_DocumentCache();
}
TVG_DocumentCache &cache = state->document_map[document->svg_document];

if (!cache.xml_body.is_empty()) {
// If we have a cached document, that means we have already parsed it.
// All node cache should be available.

xml_body = cache.xml_body;
embox_x = cache.embox_x;
embox_y = cache.embox_y;

DEV_ASSERT(cache.node_caches.has(p_slot->glyph_index));
TVG_NodeCache &nc = cache.node_caches[p_slot->glyph_index];

// Seek will call read() internally.
if (parser->seek(nc.document_offset) == OK) {
int64_t tag_count = -1;

#ifdef DEV_ENABLED
{
DEV_ASSERT(parser->has_attribute("id"));
const String &gl_name = parser->get_named_attribute_value("id");
DEV_ASSERT(gl_name.begins_with("glyph"));

int dot_pos = gl_name.find_char('.');
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;
}
DEV_ASSERT(p_slot->glyph_index == gl_idx);
}
#endif
offset = nc.body_offset;
tag_count = 0;

// We only parse the glyph node.
do {
if (parser->get_node_type() == XMLParser::NODE_ELEMENT && parser->get_node_name() == "svg") {
if (parser->has_attribute("viewBox")) {
PackedStringArray vb = parser->get_named_attribute_value("viewBox").split(" ");

if (vb.size() == 4) {
embox_x = vb[2].to_float();
embox_y = vb[3].to_float();
}
}
if (parser->has_attribute("width")) {
embox_x = parser->get_named_attribute_value("width").to_float();
}
if (parser->has_attribute("height")) {
embox_y = parser->get_named_attribute_value("height").to_float();
}
}
if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
xml_body += vformat("<%s", parser->get_node_name());

bool is_svg_tag = parser->get_node_name() == "svg";
for (int i = 0; i < parser->get_attribute_count(); i++) {
String aname = parser->get_attribute_name(i);
if (is_svg_tag && (aname == "viewBox" || aname == "width" || aname == "height")) {
continue;
}
xml_body += vformat(" %s=\"%s\"", aname, parser->get_attribute_value(i));
}

if (parser->is_empty()) {
xml_body += "/>";
} else {
xml_body += ">";
tag_count++;
}
} 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());
tag_count--;
}
} while (tag_count != 0 && parser->read() == OK);
}
if (parser->get_node_type() == XMLParser::NODE_ELEMENT && parser->get_node_name() == "svg") {
if (parser->has_attribute("viewBox")) {
PackedStringArray vb = parser->get_named_attribute_value("viewBox").split(" ");

if (vb.size() == 4) {
embox_x = vb[2].to_float();
embox_y = vb[3].to_float();
} else {
String *xml = &xml_body;
int64_t tag_count = -1;

while (parser->read() == OK) {
if (parser->has_attribute("id")) {
const String &gl_name = parser->get_named_attribute_value("id");
if (gl_name.begins_with("glyph")) {
int dot_pos = gl_name.find_char('.');
int64_t gl_idx = gl_name.substr(5, (dot_pos > 0) ? dot_pos - 5 : -1).to_int();
cache.node_caches[gl_idx] = TVG_NodeCache{
.document_offset = parser->get_node_offset(),

Check failure on line 189 in modules/text_server_adv/thorvg_svg_in_ot.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=editor, tests=yes)

use of designated initializers requires at least '/std:c++20'

Check failure on line 189 in modules/text_server_adv/thorvg_svg_in_ot.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template (target=template_release, tests=yes)

use of designated initializers requires at least '/std:c++20'
.body_offset = (uint64_t)xml_body.length()
};
if (p_slot->glyph_index != gl_idx) {
parser->skip_section();
continue;
}
offset = xml_body.length();
tag_count = 0;
xml = &xml_node;
}
}
if (parser->has_attribute("width")) {
embox_x = parser->get_named_attribute_value("width").to_float();
}
if (parser->has_attribute("height")) {
embox_y = parser->get_named_attribute_value("height").to_float();
}
}
if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
xml_body += vformat("<%s", parser->get_node_name());
bool is_svg_tag = parser->get_node_name() == "svg";
for (int i = 0; i < parser->get_attribute_count(); i++) {
String aname = parser->get_attribute_name(i);
if (is_svg_tag && (aname == "viewBox" || aname == "width" || aname == "height")) {
continue;
if (parser->get_node_type() == XMLParser::NODE_ELEMENT && parser->get_node_name() == "svg") {
if (parser->has_attribute("viewBox")) {
PackedStringArray vb = parser->get_named_attribute_value("viewBox").split(" ");

if (vb.size() == 4) {
embox_x = vb[2].to_float();
embox_y = vb[3].to_float();
}
}
if (parser->has_attribute("width")) {
embox_x = parser->get_named_attribute_value("width").to_float();
}
if (parser->has_attribute("height")) {
embox_y = parser->get_named_attribute_value("height").to_float();
}
xml_body += vformat(" %s=\"%s\"", aname, parser->get_attribute_value(i));
}
if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
*xml += vformat("<%s", parser->get_node_name());

bool is_svg_tag = parser->get_node_name() == "svg";
for (int i = 0; i < parser->get_attribute_count(); i++) {
String aname = parser->get_attribute_name(i);
if (is_svg_tag && (aname == "viewBox" || aname == "width" || aname == "height")) {
continue;
}
*xml += vformat(" %s=\"%s\"", aname, parser->get_attribute_value(i));
}

if (parser->is_empty()) {
xml_body += "/>";
} else {
xml_body += ">";
if (parser->is_empty()) {
*xml += "/>";
if (tag_count == 0) {
xml = &xml_body;
tag_count = -1;
}
} else {
*xml += ">";
if (tag_count > 0) {
tag_count++;
}
}
} else if (parser->get_node_type() == XMLParser::NODE_TEXT) {
*xml += parser->get_node_data();
} else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END) {
*xml += vformat("</%s>", parser->get_node_name());
if (tag_count > 0) {
tag_count--;
if (tag_count == 0) {
xml = &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());
}

cache.xml_body = xml_body;
cache.embox_x = embox_x;
cache.embox_y = embox_y;
}

xml_body = xml_body.insert(offset, xml_node);

std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen();
gl_state.xml_code = xml_body.utf8();

Expand Down
13 changes: 13 additions & 0 deletions modules/text_server_adv/thorvg_svg_in_ot.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,22 @@ struct GL_State {
tvg::Matrix m;
};

struct TVG_NodeCache {
uint64_t document_offset;
uint64_t body_offset;
};

struct TVG_DocumentCache {
String xml_body;
double embox_x;
double embox_y;
HashMap<int64_t, TVG_NodeCache> node_caches;
};

struct TVG_State {
Mutex mutex;
HashMap<uint32_t, GL_State> glyph_map;
HashMap<FT_Byte *, TVG_DocumentCache> document_map;
};

FT_Error tvg_svg_in_ot_init(FT_Pointer *p_state);
Expand Down
Loading