mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-13 19:44:28 +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',
|
||||
'tvgSvgLoader.h',
|
||||
'tvgSvgLoaderCommon.h',
|
||||
'tvgSvgPath.h',
|
||||
'tvgSvgSceneBuilder.h',
|
||||
'tvgSimpleXmlParser.cpp',
|
||||
'tvgSvgLoader.cpp',
|
||||
'tvgSvgPath.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();
|
||||
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: {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#define _TVG_SVG_SCENE_BUILDER_H_
|
||||
|
||||
#include "tvgSvgLoaderCommon.h"
|
||||
#include "tvgSvgPath.h"
|
||||
|
||||
class SvgSceneBuilder
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue