From 3fe2eedd897faed9e7ce259e44418c2e404a8225 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Fri, 26 Jun 2020 18:51:36 +0900 Subject: [PATCH] SvgLoader: Implement svgpath draw Convert Svg path in string to tvg::PathCommand. Draw converted data by adding it as path to shape. Following tags are supported. Move, Line, Cubic, SCubic, Vertical, Horizontal and Close. Change-Id: I3cb31e05bcb233b4c187e0c9e7eef8cdadf84695 --- src/loaders/svg_loader/meson.build | 2 + src/loaders/svg_loader/tvgSvgPath.cpp | 297 ++++++++++++++++++ src/loaders/svg_loader/tvgSvgPath.h | 8 + src/loaders/svg_loader/tvgSvgSceneBuilder.cpp | 5 +- src/loaders/svg_loader/tvgSvgSceneBuilder.h | 1 + 5 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 src/loaders/svg_loader/tvgSvgPath.cpp create mode 100644 src/loaders/svg_loader/tvgSvgPath.h diff --git a/src/loaders/svg_loader/meson.build b/src/loaders/svg_loader/meson.build index 21967afe..de7adf76 100644 --- a/src/loaders/svg_loader/meson.build +++ b/src/loaders/svg_loader/meson.build @@ -2,9 +2,11 @@ source_file = [ 'tvgSimpleXmlParser.h', 'tvgSvgLoader.h', 'tvgSvgLoaderCommon.h', + 'tvgSvgPath.h', 'tvgSvgSceneBuilder.h', 'tvgSimpleXmlParser.cpp', 'tvgSvgLoader.cpp', + 'tvgSvgPath.cpp', 'tvgSvgSceneBuilder.cpp', ] diff --git a/src/loaders/svg_loader/tvgSvgPath.cpp b/src/loaders/svg_loader/tvgSvgPath.cpp new file mode 100644 index 00000000..70058e00 --- /dev/null +++ b/src/loaders/svg_loader/tvgSvgPath.cpp @@ -0,0 +1,297 @@ +#ifndef __TVG_SVG_PATH_CPP_ +#define __TVG_SVG_PATH_CPP_ + +#include "tvgSvgPath.h" + + +static char* _skipComma(const char* content) +{ + while (*content && isspace(*content)) { + content++; + } + if (*content == ',') return (char*)content + 1; + return (char*)content; +} + + +static inline bool _parseNumber(char** content, float* number) +{ + char* end = NULL; + *number = strtof(*content, &end); + //If the start of string is not number + if ((*content) == end) return false; + //Skip comma if any + *content = _skipComma(end); + return true; +} + + +static inline bool _parseLong(char** content, int* number) +{ + char* end = NULL; + *number = strtol(*content, &end, 10) ? 1 : 0; + //If the start of string is not number + if ((*content) == end) return false; + *content = _skipComma(end); + return true; +} + + +static int _numberCount(char cmd) +{ + int count = 0; + switch (cmd) { + case 'M': + case 'm': + case 'L': + case 'l': { + count = 2; + break; + } + case 'C': + case 'c': + case 'E': + case 'e': { + count = 6; + break; + } + case 'H': + case 'h': + case 'V': + case 'v': { + count = 1; + break; + } + case 'S': + case 's': + case 'Q': + case 'q': + case 'T': + case 't': { + count = 4; + break; + } + case 'A': + case 'a': { + count = 7; + break; + } + default: + break; + } + return count; +} + + +static void _processCommand(vector* cmds, vector* pts, char cmd, float* arr, int count, Point* cur, Point* curCtl) +{ + int i; + switch (cmd) { + case 'm': + case 'l': + case 'c': + case 's': + case 'q': + case 't': { + for (i = 0; i < count - 1; i += 2) { + arr[i] = arr[i] + cur->x; + arr[i + 1] = arr[i + 1] + cur->y; + } + break; + } + case 'h': { + arr[0] = arr[0] + cur->x; + break; + } + case 'v': { + arr[0] = arr[0] + cur->y; + break; + } + case 'a': { + arr[5] = arr[5] + cur->x; + arr[6] = arr[6] + cur->y; + break; + } + default: { + break; + } + } + + switch (cmd) { + case 'm': + case 'M': { + Point p = {arr[0], arr[1]}; + cmds->push_back(PathCommand::MoveTo); + pts->push_back(p); + *cur = {arr[0] ,arr[1]}; + break; + } + case 'l': + case 'L': { + Point p = {arr[0], arr[1]}; + cmds->push_back(PathCommand::LineTo); + pts->push_back(p); + *cur = {arr[0] ,arr[1]}; + break; + } + case 'c': + case 'C': { + Point p[3]; + cmds->push_back(PathCommand::CubicTo); + p[0] = {arr[0], arr[1]}; + p[1] = {arr[2], arr[3]}; + p[2] = {arr[4], arr[5]}; + pts->push_back(p[0]); + pts->push_back(p[1]); + pts->push_back(p[2]); + *curCtl = p[1]; + *cur = p[2]; + break; + } + case 's': + case 'S': { + Point p[3], ctrl; + if ((cmds->size() > 1) && (cmds->at(cmds->size() - 2) == PathCommand::CubicTo)) { + ctrl.x = 2 * cur->x - curCtl->x; + ctrl.y = 2 * cur->x - curCtl->y; + } else { + ctrl = *cur; + } + cmds->push_back(PathCommand::CubicTo); + p[0] = ctrl; + p[1] = {arr[0], arr[1]}; + p[2] = {arr[2], arr[3]}; + pts->push_back(p[0]); + pts->push_back(p[1]); + pts->push_back(p[2]); + *curCtl = p[1]; + *cur = p[2]; + break; + } + case 'q': + case 'Q': { + //TODO: Implement quadratic_to + break; + } + case 't': + case 'T': { + Point p = {arr[0], arr[1]}; + cmds->push_back(PathCommand::MoveTo); + pts->push_back(p); + *cur = {arr[0] ,arr[1]}; + break; + } + case 'h': + case 'H': { + Point p = {arr[0], cur->y}; + cmds->push_back(PathCommand::LineTo); + pts->push_back(p); + cur->x = arr[0]; + break; + } + case 'v': + case 'V': { + Point p = {cur->x, arr[0]}; + cmds->push_back(PathCommand::LineTo); + pts->push_back(p); + cur->y = arr[0]; + break; + } + case 'z': + case 'Z': { + cmds->push_back(PathCommand::Close); + break; + } + case 'a': + case 'A': { + //TODO: Implement arc_to + break; + } + case 'E': + case 'e': { + //TODO: Implement arc + break; + } + default: { + break; + } + } +} + + +static char* _nextCommand(char* path, char* cmd, float* arr, int* count) +{ + int i = 0, large, sweep; + + path = _skipComma(path); + if (isalpha(*path)) { + *cmd = *path; + path++; + *count = _numberCount(*cmd); + } else { + if (*cmd == 'm') *cmd = 'l'; + else if (*cmd == 'M') *cmd = 'L'; + } + if (*count == 7) { + //Special case for arc command + if (_parseNumber(&path, &arr[0])) { + if (_parseNumber(&path, &arr[1])) { + if (_parseNumber(&path, &arr[2])) { + if (_parseLong(&path, &large)) { + if (_parseLong(&path, &sweep)) { + if (_parseNumber(&path, &arr[5])) { + if (_parseNumber(&path, &arr[6])) { + arr[3] = large; + arr[4] = sweep; + return path; + } + } + } + } + } + } + } + *count = 0; + return NULL; + } + for (i = 0; i < *count; i++) { + if (!_parseNumber(&path, &arr[i])) { + *count = 0; + return NULL; + } + path = _skipComma(path); + } + return path; +} + + +tuple, vector> svgPathToTvgPath(const char* svgPath) +{ + vector cmds; + vector pts; + + float numberArray[7]; + int numberCount = 0; + Point cur = { 0, 0 }; + Point curCtl = { 0, 0 }; + char cmd = 0; + char* path = (char*)svgPath; + char* curLocale; + + curLocale = setlocale(LC_NUMERIC, NULL); + if (curLocale) curLocale = strdup(curLocale); + setlocale(LC_NUMERIC, "POSIX"); + + while ((path[0] != '\0')) { + path = _nextCommand(path, &cmd, numberArray, &numberCount); + if (!path) break; + _processCommand(&cmds, &pts, cmd, numberArray, numberCount, &cur, &curCtl); + } + + setlocale(LC_NUMERIC, curLocale); + if (curLocale) free(curLocale); + + return make_tuple(cmds, pts); +} + +#endif //__TVG_SVG_PATH_CPP_ diff --git a/src/loaders/svg_loader/tvgSvgPath.h b/src/loaders/svg_loader/tvgSvgPath.h new file mode 100644 index 00000000..47a35d87 --- /dev/null +++ b/src/loaders/svg_loader/tvgSvgPath.h @@ -0,0 +1,8 @@ +#ifndef _TVG_SVG_PATH_H_ +#define _TVG_SVG_PATH_H_ + +#include "tvgCommon.h" + +tuple, vector> svgPathToTvgPath(const char* svg_path_data); + +#endif //_TVG_SVG_PATH_H_ diff --git a/src/loaders/svg_loader/tvgSvgSceneBuilder.cpp b/src/loaders/svg_loader/tvgSvgSceneBuilder.cpp index 601ed0c1..b3fe81d9 100644 --- a/src/loaders/svg_loader/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg_loader/tvgSvgSceneBuilder.cpp @@ -93,7 +93,10 @@ unique_ptr _shapeBuildHelper(SvgNode* node) auto shape = tvg::Shape::gen(); switch (node->type) { case SvgNodeType::Path: { - //TODO: Support path + if (node->node.path.path) { + auto pathResult = svgPathToTvgPath(node->node.path.path->c_str()); + shape->appendPath(get<0>(pathResult).data(), get<0>(pathResult).size(), get<1>(pathResult).data(), get<1>(pathResult).size()); + } break; } case SvgNodeType::Ellipse: { diff --git a/src/loaders/svg_loader/tvgSvgSceneBuilder.h b/src/loaders/svg_loader/tvgSvgSceneBuilder.h index 20a41a74..1bd779b1 100644 --- a/src/loaders/svg_loader/tvgSvgSceneBuilder.h +++ b/src/loaders/svg_loader/tvgSvgSceneBuilder.h @@ -18,6 +18,7 @@ #define _TVG_SVG_SCENE_BUILDER_H_ #include "tvgSvgLoaderCommon.h" +#include "tvgSvgPath.h" class SvgSceneBuilder {