Flutter Engine
The Flutter Engine
SkParsePath.cpp
Go to the documentation of this file.
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
16#include "src/core/SkGeometry.h"
17
18#include <cstdio>
19
20enum class SkPathDirection;
21
22static inline bool is_between(int c, int min, int max) {
23 return (unsigned)(c - min) <= (unsigned)(max - min);
24}
25
26static inline bool is_ws(int c) {
27 return is_between(c, 1, 32);
28}
29
30static inline bool is_digit(int c) {
31 return is_between(c, '0', '9');
32}
33
34static inline bool is_sep(int c) {
35 return is_ws(c) || c == ',';
36}
37
38static inline bool is_lower(int c) {
39 return is_between(c, 'a', 'z');
40}
41
42static inline int to_upper(int c) {
43 return c - 'a' + 'A';
44}
45
46static const char* skip_ws(const char str[]) {
47 SkASSERT(str);
48 while (is_ws(*str))
49 str++;
50 return str;
51}
52
53static const char* skip_sep(const char str[]) {
54 if (!str) {
55 return nullptr;
56 }
57 while (is_sep(*str))
58 str++;
59 return str;
60}
61
62// If unable to read count points from str into value, this will return nullptr
63// to signal the failure. Otherwise, it will return the next offset to read from.
64static const char* find_points(const char str[], SkPoint value[], int count,
65 bool isRelative, SkPoint* relative) {
66 str = SkParse::FindScalars(str, &value[0].fX, count * 2);
67 if (isRelative) {
68 for (int index = 0; index < count; index++) {
69 value[index].fX += relative->fX;
70 value[index].fY += relative->fY;
71 }
72 }
73 return str;
74}
75
76// If unable to read a scalar from str into value, this will return nullptr
77// to signal the failure. Otherwise, it will return the next offset to read from.
78static const char* find_scalar(const char str[], SkScalar* value,
79 bool isRelative, SkScalar relative) {
80 str = SkParse::FindScalar(str, value);
81 if (!str) {
82 return nullptr;
83 }
84 if (isRelative) {
85 *value += relative;
86 }
87 str = skip_sep(str);
88 return str;
89}
90
91// https://www.w3.org/TR/SVG11/paths.html#PathDataBNF
92//
93// flag:
94// "0" | "1"
95static const char* find_flag(const char str[], bool* value) {
96 if (!str) {
97 return nullptr;
98 }
99 if (str[0] != '1' && str[0] != '0') {
100 return nullptr;
101 }
102 *value = str[0] != '0';
103 str = skip_sep(str + 1);
104 return str;
105}
106
108 // We will write all data to this local path and only write it
109 // to result if the whole parsing succeeds.
110 SkPath path;
111 SkPoint first = {0, 0};
112 SkPoint c = {0, 0};
113 SkPoint lastc = {0, 0};
114 // We will use find_points and find_scalar to read into these.
115 // There might not be enough data to fill them, so to avoid
116 // MSAN warnings about using uninitialized bytes, we initialize
117 // them there.
118 SkPoint points[3] = {};
119 SkScalar scratch = 0;
120 char op = '\0';
121 char previousOp = '\0';
122 bool relative = false;
123 for (;;) {
124 if (!data) {
125 // Truncated data
126 return false;
127 }
128 data = skip_ws(data);
129 if (data[0] == '\0') {
130 break;
131 }
132 char ch = data[0];
133 if (is_digit(ch) || ch == '-' || ch == '+' || ch == '.') {
134 if (op == '\0' || op == 'Z') {
135 return false;
136 }
137 } else if (is_sep(ch)) {
138 data = skip_sep(data);
139 } else {
140 op = ch;
141 relative = false;
142 if (is_lower(op)) {
143 op = (char) to_upper(op);
144 relative = true;
145 }
146 data++;
147 data = skip_sep(data);
148 }
149 switch (op) {
150 case 'M': // Move
151 data = find_points(data, points, 1, relative, &c);
152 // find_points might have failed, so this might be the
153 // previous point. However, data will be set to nullptr
154 // if it failed, so we will check this at the top of the loop.
155 path.moveTo(points[0]);
156 previousOp = '\0';
157 op = 'L';
158 c = points[0];
159 break;
160 case 'L': // Line
161 data = find_points(data, points, 1, relative, &c);
162 path.lineTo(points[0]);
163 c = points[0];
164 break;
165 case 'H': // Horizontal Line
166 data = find_scalar(data, &scratch, relative, c.fX);
167 // Similarly, if there wasn't a scalar to read, data will
168 // be set to nullptr and this lineTo is bogus but will
169 // be ultimately ignored when the next time through the loop
170 // detects that and bails out.
171 path.lineTo(scratch, c.fY);
172 c.fX = scratch;
173 break;
174 case 'V': // Vertical Line
175 data = find_scalar(data, &scratch, relative, c.fY);
176 path.lineTo(c.fX, scratch);
177 c.fY = scratch;
178 break;
179 case 'C': // Cubic Bezier Curve
180 data = find_points(data, points, 3, relative, &c);
181 goto cubicCommon;
182 case 'S': // Continued "Smooth" Cubic Bezier Curve
183 data = find_points(data, &points[1], 2, relative, &c);
184 points[0] = c;
185 if (previousOp == 'C' || previousOp == 'S') {
186 points[0].fX -= lastc.fX - c.fX;
187 points[0].fY -= lastc.fY - c.fY;
188 }
189 cubicCommon:
190 path.cubicTo(points[0], points[1], points[2]);
191 lastc = points[1];
192 c = points[2];
193 break;
194 case 'Q': // Quadratic Bezier Curve
195 data = find_points(data, points, 2, relative, &c);
196 goto quadraticCommon;
197 case 'T': // Continued Quadratic Bezier Curve
198 data = find_points(data, &points[1], 1, relative, &c);
199 points[0] = c;
200 if (previousOp == 'Q' || previousOp == 'T') {
201 points[0].fX -= lastc.fX - c.fX;
202 points[0].fY -= lastc.fY - c.fY;
203 }
204 quadraticCommon:
205 path.quadTo(points[0], points[1]);
206 lastc = points[0];
207 c = points[1];
208 break;
209 case 'A': { // Arc (Elliptical)
210 SkPoint radii;
211 SkScalar angle;
212 bool largeArc, sweep;
213 if ((data = find_points(data, &radii, 1, false, nullptr))
214 && (data = skip_sep(data))
215 && (data = find_scalar(data, &angle, false, 0))
216 && (data = skip_sep(data))
217 && (data = find_flag(data, &largeArc))
218 && (data = skip_sep(data))
219 && (data = find_flag(data, &sweep))
220 && (data = skip_sep(data))
221 && (data = find_points(data, &points[0], 1, relative, &c))) {
222 path.arcTo(radii, angle, (SkPath::ArcSize) largeArc,
223 (SkPathDirection) !sweep, points[0]);
224 path.getLastPt(&c);
225 }
226 } break;
227 case 'Z': // Close Path
228 path.close();
229 c = first;
230 break;
231 default:
232 return false;
233 }
234 if (previousOp == 0) {
235 first = c;
236 }
237 previousOp = op;
238 }
239 // we're good, go ahead and swap in the result
240 result->swap(path);
241 return true;
242}
243
244///////////////////////////////////////////////////////////////////////////////
245
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 stream.writeScalarAsText(pt.fX);
263 stream.write(" ", 1);
264 stream.writeScalarAsText(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;
286 append_command('M', &pts[0], 1);
287 break;
289 append_command('L', &pts[1], 1);
290 break;
292 append_command('Q', &pts[1], 2);
293 break;
295 append_command('C', &pts[1], 3);
296 break;
298 stream.write("Z", 1);
299 break;
300 case SkPath::kDone_Verb: {
301 SkString str;
302 str.resize(stream.bytesWritten());
303 stream.copyTo(str.data());
304 return str;
305 }
306 }
307 }
308}
int count
Definition: FontMgrTest.cpp:50
static const int points[]
#define SkASSERT(cond)
Definition: SkAssert.h:116
static bool is_ws(int c)
Definition: SkParsePath.cpp:26
static bool is_sep(int c)
Definition: SkParsePath.cpp:34
static const char * skip_sep(const char str[])
Definition: SkParsePath.cpp:53
static int to_upper(int c)
Definition: SkParsePath.cpp:42
static bool is_digit(int c)
Definition: SkParsePath.cpp:30
static const char * find_flag(const char str[], bool *value)
Definition: SkParsePath.cpp:95
static const char * skip_ws(const char str[])
Definition: SkParsePath.cpp:46
static bool is_between(int c, int min, int max)
Definition: SkParsePath.cpp:22
static const char * find_scalar(const char str[], SkScalar *value, bool isRelative, SkScalar relative)
Definition: SkParsePath.cpp:78
static const char * find_points(const char str[], SkPoint value[], int count, bool isRelative, SkPoint *relative)
Definition: SkParsePath.cpp:64
static bool is_lower(int c)
Definition: SkParsePath.cpp:38
SkPathDirection
Definition: SkPathTypes.h:34
#define SK_Scalar1
Definition: SkScalar.h:18
const SkPoint * computeQuads(const SkConic &conic, SkScalar tol)
Definition: SkGeometry.h:524
int countQuads() const
Definition: SkGeometry.h:539
static SkString ToSVGString(const SkPath &, PathEncoding=PathEncoding::Absolute)
static bool FromSVGString(const char str[], SkPath *)
static const char * FindScalar(const char str[], SkScalar *value)
Definition: SkParse.cpp:216
static const char * FindScalars(const char str[], SkScalar value[], int count)
Definition: SkParse.cpp:231
Verb next(SkPoint pts[4])
Definition: SkPath.cpp:1901
SkScalar conicWeight() const
Definition: SkPath.h:1535
Definition: SkPath.h:59
ArcSize
Definition: SkPath.h:923
@ kClose_Verb
Definition: SkPath.h:1471
@ kMove_Verb
Definition: SkPath.h:1466
@ kConic_Verb
Definition: SkPath.h:1469
@ kDone_Verb
Definition: SkPath.h:1472
@ kCubic_Verb
Definition: SkPath.h:1470
@ kQuad_Verb
Definition: SkPath.h:1468
@ kLine_Verb
Definition: SkPath.h:1467
const char * data() const
Definition: SkString.h:132
void resize(size_t len)
Definition: SkString.cpp:374
float SkScalar
Definition: extension.cpp:12
uint8_t value
GAsyncResult * result
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
float fX
x-axis value
Definition: SkPoint_impl.h:164
float fY
y-axis value
Definition: SkPoint_impl.h:165
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63