Update rive-cpp to 2.0 version
[platform/core/uifw/rive-tizen.git] / submodule / skia / src / utils / SkParsePath.cpp
1 /*
2  * Copyright 2011 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7
8 #include "include/core/SkPath.h"
9 #include "include/core/SkPathTypes.h"
10 #include "include/core/SkPoint.h"
11 #include "include/core/SkScalar.h"
12 #include "include/core/SkStream.h"
13 #include "include/core/SkString.h"
14 #include "include/core/SkTypes.h"
15 #include "include/utils/SkParse.h"
16 #include "include/utils/SkParsePath.h"
17 #include "src/core/SkGeometry.h"
18
19 #include <stdio.h>
20
21 static inline bool is_between(int c, int min, int max) {
22     return (unsigned)(c - min) <= (unsigned)(max - min);
23 }
24
25 static inline bool is_ws(int c) {
26     return is_between(c, 1, 32);
27 }
28
29 static inline bool is_digit(int c) {
30     return is_between(c, '0', '9');
31 }
32
33 static inline bool is_sep(int c) {
34     return is_ws(c) || c == ',';
35 }
36
37 static inline bool is_lower(int c) {
38     return is_between(c, 'a', 'z');
39 }
40
41 static inline int to_upper(int c) {
42     return c - 'a' + 'A';
43 }
44
45 static const char* skip_ws(const char str[]) {
46     SkASSERT(str);
47     while (is_ws(*str))
48         str++;
49     return str;
50 }
51
52 static const char* skip_sep(const char str[]) {
53     if (!str) {
54         return nullptr;
55     }
56     while (is_sep(*str))
57         str++;
58     return str;
59 }
60
61 static const char* find_points(const char str[], SkPoint value[], int count,
62                                bool isRelative, SkPoint* relative) {
63     str = SkParse::FindScalars(str, &value[0].fX, count * 2);
64     if (isRelative) {
65         for (int index = 0; index < count; index++) {
66             value[index].fX += relative->fX;
67             value[index].fY += relative->fY;
68         }
69     }
70     return str;
71 }
72
73 static const char* find_scalar(const char str[], SkScalar* value,
74                                bool isRelative, SkScalar relative) {
75     str = SkParse::FindScalar(str, value);
76     if (!str) {
77         return nullptr;
78     }
79     if (isRelative) {
80         *value += relative;
81     }
82     str = skip_sep(str);
83     return str;
84 }
85
86 // https://www.w3.org/TR/SVG11/paths.html#PathDataBNF
87 //
88 // flag:
89 //    "0" | "1"
90 static const char* find_flag(const char str[], bool* value) {
91     if (!str) {
92         return nullptr;
93     }
94     if (str[0] != '1' && str[0] != '0') {
95         return nullptr;
96     }
97     *value = str[0] != '0';
98     str = skip_sep(str + 1);
99     return str;
100 }
101
102 bool SkParsePath::FromSVGString(const char data[], SkPath* result) {
103     SkPath path;
104     SkPoint first = {0, 0};
105     SkPoint c = {0, 0};
106     SkPoint lastc = {0, 0};
107     SkPoint points[3];
108     char op = '\0';
109     char previousOp = '\0';
110     bool relative = false;
111     for (;;) {
112         if (!data) {
113             // Truncated data
114             return false;
115         }
116         data = skip_ws(data);
117         if (data[0] == '\0') {
118             break;
119         }
120         char ch = data[0];
121         if (is_digit(ch) || ch == '-' || ch == '+' || ch == '.') {
122             if (op == '\0' || op == 'Z') {
123                 return false;
124             }
125         } else if (is_sep(ch)) {
126             data = skip_sep(data);
127         } else {
128             op = ch;
129             relative = false;
130             if (is_lower(op)) {
131                 op = (char) to_upper(op);
132                 relative = true;
133             }
134             data++;
135             data = skip_sep(data);
136         }
137         switch (op) {
138             case 'M':
139                 data = find_points(data, points, 1, relative, &c);
140                 path.moveTo(points[0]);
141                 previousOp = '\0';
142                 op = 'L';
143                 c = points[0];
144                 break;
145             case 'L':
146                 data = find_points(data, points, 1, relative, &c);
147                 path.lineTo(points[0]);
148                 c = points[0];
149                 break;
150             case 'H': {
151                 SkScalar x;
152                 data = find_scalar(data, &x, relative, c.fX);
153                 path.lineTo(x, c.fY);
154                 c.fX = x;
155             } break;
156             case 'V': {
157                 SkScalar y;
158                 data = find_scalar(data, &y, relative, c.fY);
159                 path.lineTo(c.fX, y);
160                 c.fY = y;
161             } break;
162             case 'C':
163                 data = find_points(data, points, 3, relative, &c);
164                 goto cubicCommon;
165             case 'S':
166                 data = find_points(data, &points[1], 2, relative, &c);
167                 points[0] = c;
168                 if (previousOp == 'C' || previousOp == 'S') {
169                     points[0].fX -= lastc.fX - c.fX;
170                     points[0].fY -= lastc.fY - c.fY;
171                 }
172             cubicCommon:
173                 path.cubicTo(points[0], points[1], points[2]);
174                 lastc = points[1];
175                 c = points[2];
176                 break;
177             case 'Q':  // Quadratic Bezier Curve
178                 data = find_points(data, points, 2, relative, &c);
179                 goto quadraticCommon;
180             case 'T':
181                 data = find_points(data, &points[1], 1, relative, &c);
182                 points[0] = c;
183                 if (previousOp == 'Q' || previousOp == 'T') {
184                     points[0].fX -= lastc.fX - c.fX;
185                     points[0].fY -= lastc.fY - c.fY;
186                 }
187             quadraticCommon:
188                 path.quadTo(points[0], points[1]);
189                 lastc = points[0];
190                 c = points[1];
191                 break;
192             case 'A': {
193                 SkPoint radii;
194                 SkScalar angle;
195                 bool largeArc, sweep;
196                 if ((data = find_points(data, &radii, 1, false, nullptr))
197                         && (data = skip_sep(data))
198                         && (data = find_scalar(data, &angle, false, 0))
199                         && (data = skip_sep(data))
200                         && (data = find_flag(data, &largeArc))
201                         && (data = skip_sep(data))
202                         && (data = find_flag(data, &sweep))
203                         && (data = skip_sep(data))
204                         && (data = find_points(data, &points[0], 1, relative, &c))) {
205                     path.arcTo(radii, angle, (SkPath::ArcSize) largeArc,
206                             (SkPathDirection) !sweep, points[0]);
207                     path.getLastPt(&c);
208                 }
209                 } break;
210             case 'Z':
211                 path.close();
212                 c = first;
213                 break;
214             case '~': {
215                 SkPoint args[2];
216                 data = find_points(data, args, 2, false, nullptr);
217                 path.moveTo(args[0].fX, args[0].fY);
218                 path.lineTo(args[1].fX, args[1].fY);
219             } break;
220             default:
221                 return false;
222         }
223         if (previousOp == 0) {
224             first = c;
225         }
226         previousOp = op;
227     }
228     // we're good, go ahead and swap in the result
229     result->swap(path);
230     return true;
231 }
232
233 ///////////////////////////////////////////////////////////////////////////////
234
235 static void write_scalar(SkWStream* stream, SkScalar value) {
236     char buffer[64];
237 #ifdef SK_BUILD_FOR_WIN
238     int len = _snprintf(buffer, sizeof(buffer), "%g", value);
239 #else
240     int len = snprintf(buffer, sizeof(buffer), "%g", value);
241 #endif
242     char* stop = buffer + len;
243     stream->write(buffer, stop - buffer);
244 }
245
246 void SkParsePath::ToSVGString(const SkPath& path, SkString* str, PathEncoding encoding) {
247     SkDynamicMemoryWStream  stream;
248
249     SkPoint current_point{0,0};
250     const auto rel_selector = encoding == PathEncoding::Relative;
251
252     const auto append_command = [&](char cmd, const SkPoint pts[], size_t count) {
253         // Use lower case cmds for relative encoding.
254         cmd += 32 * rel_selector;
255         stream.write(&cmd, 1);
256
257         for (size_t i = 0; i < count; ++i) {
258             const auto pt = pts[i] - current_point;
259             if (i > 0) {
260                 stream.write(" ", 1);
261             }
262             write_scalar(&stream, pt.fX);
263             stream.write(" ", 1);
264             write_scalar(&stream, pt.fY);
265         }
266
267         SkASSERT(count > 0);
268         // For relative encoding, track the current point (otherwise == origin).
269         current_point = pts[count - 1] * rel_selector;
270     };
271
272     SkPath::Iter    iter(path, false);
273     SkPoint         pts[4];
274
275     for (;;) {
276         switch (iter.next(pts)) {
277             case SkPath::kConic_Verb: {
278                 const SkScalar tol = SK_Scalar1 / 1024; // how close to a quad
279                 SkAutoConicToQuads quadder;
280                 const SkPoint* quadPts = quadder.computeQuads(pts, iter.conicWeight(), tol);
281                 for (int i = 0; i < quadder.countQuads(); ++i) {
282                     append_command('Q', &quadPts[i*2 + 1], 2);
283                 }
284             } break;
285            case SkPath::kMove_Verb:
286                 append_command('M', &pts[0], 1);
287                 break;
288             case SkPath::kLine_Verb:
289                 append_command('L', &pts[1], 1);
290                 break;
291             case SkPath::kQuad_Verb:
292                 append_command('Q', &pts[1], 2);
293                 break;
294             case SkPath::kCubic_Verb:
295                 append_command('C', &pts[1], 3);
296                 break;
297             case SkPath::kClose_Verb:
298                 stream.write("Z", 1);
299                 break;
300             case SkPath::kDone_Verb:
301                 str->resize(stream.bytesWritten());
302                 stream.copyTo(str->writable_str());
303             return;
304         }
305     }
306 }