SvgLoader: Implement svgpath draw 64/237264/4
authorJunsuChoi <jsuya.choi@samsung.com>
Fri, 26 Jun 2020 09:51:36 +0000 (18:51 +0900)
committerHermet Park <chuneon.park@samsung.com>
Mon, 29 Jun 2020 07:17:22 +0000 (07:17 +0000)
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
src/loaders/svg_loader/tvgSvgPath.cpp [new file with mode: 0644]
src/loaders/svg_loader/tvgSvgPath.h [new file with mode: 0644]
src/loaders/svg_loader/tvgSvgSceneBuilder.cpp
src/loaders/svg_loader/tvgSvgSceneBuilder.h

index 21967af..de7adf7 100644 (file)
@@ -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 (file)
index 0000000..70058e0
--- /dev/null
@@ -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_
diff --git a/src/loaders/svg_loader/tvgSvgPath.h b/src/loaders/svg_loader/tvgSvgPath.h
new file mode 100644 (file)
index 0000000..47a35d8
--- /dev/null
@@ -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_
index 601ed0c..b3fe81d 100644 (file)
@@ -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: {
index 20a41a7..1bd779b 100644 (file)
@@ -18,6 +18,7 @@
 #define _TVG_SVG_SCENE_BUILDER_H_
 
 #include "tvgSvgLoaderCommon.h"
+#include "tvgSvgPath.h"
 
 class SvgSceneBuilder
 {