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