mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-14 12:04:29 +00:00
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
This commit is contained in:
parent
1513412e45
commit
3fe2eedd89
5 changed files with 312 additions and 1 deletions
|
@ -2,9 +2,11 @@ source_file = [
|
||||||
'tvgSimpleXmlParser.h',
|
'tvgSimpleXmlParser.h',
|
||||||
'tvgSvgLoader.h',
|
'tvgSvgLoader.h',
|
||||||
'tvgSvgLoaderCommon.h',
|
'tvgSvgLoaderCommon.h',
|
||||||
|
'tvgSvgPath.h',
|
||||||
'tvgSvgSceneBuilder.h',
|
'tvgSvgSceneBuilder.h',
|
||||||
'tvgSimpleXmlParser.cpp',
|
'tvgSimpleXmlParser.cpp',
|
||||||
'tvgSvgLoader.cpp',
|
'tvgSvgLoader.cpp',
|
||||||
|
'tvgSvgPath.cpp',
|
||||||
'tvgSvgSceneBuilder.cpp',
|
'tvgSvgSceneBuilder.cpp',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
297
src/loaders/svg_loader/tvgSvgPath.cpp
Normal file
297
src/loaders/svg_loader/tvgSvgPath.cpp
Normal file
|
@ -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<PathCommand>* cmds, vector<Point>* 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<PathCommand>, vector<Point>> svgPathToTvgPath(const char* svgPath)
|
||||||
|
{
|
||||||
|
vector<PathCommand> cmds;
|
||||||
|
vector<Point> 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_
|
8
src/loaders/svg_loader/tvgSvgPath.h
Normal file
8
src/loaders/svg_loader/tvgSvgPath.h
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef _TVG_SVG_PATH_H_
|
||||||
|
#define _TVG_SVG_PATH_H_
|
||||||
|
|
||||||
|
#include "tvgCommon.h"
|
||||||
|
|
||||||
|
tuple<vector<tvg::PathCommand>, vector<tvg::Point>> svgPathToTvgPath(const char* svg_path_data);
|
||||||
|
|
||||||
|
#endif //_TVG_SVG_PATH_H_
|
|
@ -93,7 +93,10 @@ unique_ptr<tvg::Shape> _shapeBuildHelper(SvgNode* node)
|
||||||
auto shape = tvg::Shape::gen();
|
auto shape = tvg::Shape::gen();
|
||||||
switch (node->type) {
|
switch (node->type) {
|
||||||
case SvgNodeType::Path: {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case SvgNodeType::Ellipse: {
|
case SvgNodeType::Ellipse: {
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#define _TVG_SVG_SCENE_BUILDER_H_
|
#define _TVG_SVG_SCENE_BUILDER_H_
|
||||||
|
|
||||||
#include "tvgSvgLoaderCommon.h"
|
#include "tvgSvgLoaderCommon.h"
|
||||||
|
#include "tvgSvgPath.h"
|
||||||
|
|
||||||
class SvgSceneBuilder
|
class SvgSceneBuilder
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Reference in a new issue