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