1804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu/*
2804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * Copyright (C) 2015 The Android Open Source Project
3804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu *
4804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * Licensed under the Apache License, Version 2.0 (the "License");
5804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * you may not use this file except in compliance with the License.
6804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * You may obtain a copy of the License at
7804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu *
8804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu *      http://www.apache.org/licenses/LICENSE-2.0
9804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu *
10804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * Unless required by applicable law or agreed to in writing, software
11804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * distributed under the License is distributed on an "AS IS" BASIS,
12804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * See the License for the specific language governing permissions and
14804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * limitations under the License.
15804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu */
16804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
17804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu#include "VectorDrawableUtils.h"
18804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
19804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu#include "PathParser.h"
20804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
21804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu#include <math.h>
22804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu#include <utils/Log.h>
23804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
24804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liunamespace android {
25804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liunamespace uirenderer {
26804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
27804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liuclass PathResolver {
28804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liupublic:
29804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    float currentX = 0;
30804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    float currentY = 0;
31804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    float ctrlPointX = 0;
32804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    float ctrlPointY = 0;
33804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    float currentSegmentStartX = 0;
34804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    float currentSegmentStartY = 0;
35804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    void addCommand(SkPath* outPath, char previousCmd,
36804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            char cmd, const std::vector<float>* points, size_t start, size_t end);
37804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu};
38804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
39804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liubool VectorDrawableUtils::canMorph(const PathData& morphFrom, const PathData& morphTo) {
40804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    if (morphFrom.verbs.size() != morphTo.verbs.size()) {
41804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        return false;
42804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    }
43804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
44804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    for (unsigned int i = 0; i < morphFrom.verbs.size(); i++) {
45804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        if (morphFrom.verbs[i] != morphTo.verbs[i]
46804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                ||  morphFrom.verbSizes[i] != morphTo.verbSizes[i]) {
47804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            return false;
48804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        }
49804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    }
50804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    return true;
51804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu}
52804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
53804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liubool VectorDrawableUtils::interpolatePathData(PathData* outData, const PathData& morphFrom,
54804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        const PathData& morphTo, float fraction) {
55804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    if (!canMorph(morphFrom, morphTo)) {
56804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        return false;
57804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    }
58804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    interpolatePaths(outData, morphFrom, morphTo, fraction);
59804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    return true;
60804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu}
61804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
62804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu /**
63804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * Convert an array of PathVerb to Path.
64804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu */
65804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liuvoid VectorDrawableUtils::verbsToPath(SkPath* outPath, const PathData& data) {
66804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    PathResolver resolver;
67804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    char previousCommand = 'm';
68804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    size_t start = 0;
69804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    outPath->reset();
70804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    for (unsigned int i = 0; i < data.verbs.size(); i++) {
71804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        size_t verbSize = data.verbSizes[i];
72804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        resolver.addCommand(outPath, previousCommand, data.verbs[i], &data.points, start,
73804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                start + verbSize);
74804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        previousCommand = data.verbs[i];
75804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        start += verbSize;
76804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    }
77804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu}
78804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
79804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu/**
80804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * The current PathVerb will be interpolated between the
81804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * <code>nodeFrom</code> and <code>nodeTo</code> according to the
82804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * <code>fraction</code>.
83804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu *
84804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * @param nodeFrom The start value as a PathVerb.
85804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * @param nodeTo The end value as a PathVerb
86804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * @param fraction The fraction to interpolate.
87804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu */
88804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liuvoid VectorDrawableUtils::interpolatePaths(PathData* outData,
89804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        const PathData& from, const PathData& to, float fraction) {
90804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    outData->points.resize(from.points.size());
91804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    outData->verbSizes = from.verbSizes;
92804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    outData->verbs = from.verbs;
93804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
94804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    for (size_t i = 0; i < from.points.size(); i++) {
95804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        outData->points[i] = from.points[i] * (1 - fraction) + to.points[i] * fraction;
96804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    }
97804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu}
98804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
99804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu/**
100804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * Converts an arc to cubic Bezier segments and records them in p.
101804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu *
102804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * @param p The target for the cubic Bezier segments
103804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * @param cx The x coordinate center of the ellipse
104804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * @param cy The y coordinate center of the ellipse
105804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * @param a The radius of the ellipse in the horizontal direction
106804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * @param b The radius of the ellipse in the vertical direction
107804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * @param e1x E(eta1) x coordinate of the starting point of the arc
108804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * @param e1y E(eta2) y coordinate of the starting point of the arc
109804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane
110804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * @param start The start angle of the arc on the ellipse
111804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
112804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu */
113804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liustatic void arcToBezier(SkPath* p,
114804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        double cx,
115804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        double cy,
116804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        double a,
117804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        double b,
118804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        double e1x,
119804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        double e1y,
120804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        double theta,
121804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        double start,
122804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        double sweep) {
123804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html
124804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    // and http://www.spaceroots.org/documents/ellipse/node22.html
125804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
126804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    // Maximum of 45 degrees per cubic Bezier segment
127804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    int numSegments = ceil(fabs(sweep * 4 / M_PI));
128804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
129804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double eta1 = start;
130804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double cosTheta = cos(theta);
131804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double sinTheta = sin(theta);
132804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double cosEta1 = cos(eta1);
133804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double sinEta1 = sin(eta1);
134804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1);
135804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1);
136804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
137804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double anglePerSegment = sweep / numSegments;
138804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    for (int i = 0; i < numSegments; i++) {
139804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        double eta2 = eta1 + anglePerSegment;
140804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        double sinEta2 = sin(eta2);
141804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        double cosEta2 = cos(eta2);
142804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2);
143804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2);
144804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2;
145804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2;
146804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        double tanDiff2 = tan((eta2 - eta1) / 2);
147804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        double alpha =
148804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                sin(eta2 - eta1) * (sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3;
149804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        double q1x = e1x + alpha * ep1x;
150804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        double q1y = e1y + alpha * ep1y;
151804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        double q2x = e2x - alpha * ep2x;
152804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        double q2y = e2y - alpha * ep2y;
153804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
154804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        p->cubicTo((float) q1x,
155804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                (float) q1y,
156804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                (float) q2x,
157804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                (float) q2y,
158804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                (float) e2x,
159804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                (float) e2y);
160804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        eta1 = eta2;
161804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        e1x = e2x;
162804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        e1y = e2y;
163804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        ep1x = ep2x;
164804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        ep1y = ep2y;
165804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    }
166804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu}
167804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
168804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liuinline double toRadians(float theta) { return theta * M_PI / 180;}
169804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
170804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liustatic void drawArc(SkPath* p,
171804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        float x0,
172804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        float y0,
173804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        float x1,
174804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        float y1,
175804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        float a,
176804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        float b,
177804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        float theta,
178804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        bool isMoreThanHalf,
179804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        bool isPositiveArc) {
180804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
181804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    /* Convert rotation angle from degrees to radians */
182804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double thetaD = toRadians(theta);
183804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    /* Pre-compute rotation matrix entries */
184804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double cosTheta = cos(thetaD);
185804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double sinTheta = sin(thetaD);
186804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    /* Transform (x0, y0) and (x1, y1) into unit space */
187804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    /* using (inverse) rotation, followed by (inverse) scale */
188804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double x0p = (x0 * cosTheta + y0 * sinTheta) / a;
189804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double y0p = (-x0 * sinTheta + y0 * cosTheta) / b;
190804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double x1p = (x1 * cosTheta + y1 * sinTheta) / a;
191804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double y1p = (-x1 * sinTheta + y1 * cosTheta) / b;
192804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
193804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    /* Compute differences and averages */
194804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double dx = x0p - x1p;
195804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double dy = y0p - y1p;
196804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double xm = (x0p + x1p) / 2;
197804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double ym = (y0p + y1p) / 2;
198804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    /* Solve for intersecting unit circles */
199804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double dsq = dx * dx + dy * dy;
200804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    if (dsq == 0.0) {
20185d99528b23b5575d97f614fe25f839d19740abcTeng-Hui Zhu        VECTOR_DRAWABLE_LOGD("Points are coincident");
202804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        return; /* Points are coincident */
203804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    }
204804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double disc = 1.0 / dsq - 1.0 / 4.0;
205804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    if (disc < 0.0) {
20685d99528b23b5575d97f614fe25f839d19740abcTeng-Hui Zhu        VECTOR_DRAWABLE_LOGD("Points are too far apart %f", dsq);
207804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        float adjust = (float) (sqrt(dsq) / 1.99999);
208804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        drawArc(p, x0, y0, x1, y1, a * adjust,
209804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                b * adjust, theta, isMoreThanHalf, isPositiveArc);
210804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        return; /* Points are too far apart */
211804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    }
212804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double s = sqrt(disc);
213804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double sdx = s * dx;
214804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double sdy = s * dy;
215804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double cx;
216804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double cy;
217804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    if (isMoreThanHalf == isPositiveArc) {
218804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        cx = xm - sdy;
219804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        cy = ym + sdx;
220804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    } else {
221804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        cx = xm + sdy;
222804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        cy = ym - sdx;
223804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    }
224804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
225804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double eta0 = atan2((y0p - cy), (x0p - cx));
226804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
227804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double eta1 = atan2((y1p - cy), (x1p - cx));
228804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
229804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double sweep = (eta1 - eta0);
230804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    if (isPositiveArc != (sweep >= 0)) {
231804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        if (sweep > 0) {
232804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            sweep -= 2 * M_PI;
233804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        } else {
234804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            sweep += 2 * M_PI;
235804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        }
236804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    }
237804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
238804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    cx *= a;
239804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    cy *= b;
240804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    double tcx = cx;
241804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    cx = cx * cosTheta - cy * sinTheta;
242804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    cy = tcx * sinTheta + cy * cosTheta;
243804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
244804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep);
245804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu}
246804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
247804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
248804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
249804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu// Use the given verb, and points in the range [start, end) to insert a command into the SkPath.
250804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liuvoid PathResolver::addCommand(SkPath* outPath, char previousCmd,
251804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        char cmd, const std::vector<float>* points, size_t start, size_t end) {
252804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
253804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    int incr = 2;
254804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    float reflectiveCtrlPointX;
255804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    float reflectiveCtrlPointY;
256804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
257804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    switch (cmd) {
258804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    case 'z':
259804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    case 'Z':
260804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        outPath->close();
261804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        // Path is closed here, but we need to move the pen to the
262804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        // closed position. So we cache the segment's starting position,
263804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        // and restore it here.
264804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        currentX = currentSegmentStartX;
265804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        currentY = currentSegmentStartY;
266804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        ctrlPointX = currentSegmentStartX;
267804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        ctrlPointY = currentSegmentStartY;
268804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        outPath->moveTo(currentX, currentY);
269804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        break;
270804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    case 'm':
271804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    case 'M':
272804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    case 'l':
273804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    case 'L':
274804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    case 't':
275804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    case 'T':
276804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        incr = 2;
277804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        break;
278804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    case 'h':
279804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    case 'H':
280804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    case 'v':
281804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    case 'V':
282804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        incr = 1;
283804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        break;
284804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    case 'c':
285804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    case 'C':
286804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        incr = 6;
287804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        break;
288804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    case 's':
289804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    case 'S':
290804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    case 'q':
291804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    case 'Q':
292804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        incr = 4;
293804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        break;
294804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    case 'a':
295804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    case 'A':
296804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        incr = 7;
297804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        break;
298804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    }
299804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
300804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    for (unsigned int k = start; k < end; k += incr) {
301804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        switch (cmd) {
302804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        case 'm': // moveto - Start a new sub-path (relative)
303804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentX += points->at(k + 0);
304804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentY += points->at(k + 1);
305804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            if (k > start) {
306804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                // According to the spec, if a moveto is followed by multiple
307804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                // pairs of coordinates, the subsequent pairs are treated as
308804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                // implicit lineto commands.
309804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                outPath->rLineTo(points->at(k + 0), points->at(k + 1));
310804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            } else {
311804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                outPath->rMoveTo(points->at(k + 0), points->at(k + 1));
312804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                currentSegmentStartX = currentX;
313804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                currentSegmentStartY = currentY;
314804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            }
315804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            break;
316804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        case 'M': // moveto - Start a new sub-path
317804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentX = points->at(k + 0);
318804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentY = points->at(k + 1);
319804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            if (k > start) {
320804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                // According to the spec, if a moveto is followed by multiple
321804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                // pairs of coordinates, the subsequent pairs are treated as
322804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                // implicit lineto commands.
323804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                outPath->lineTo(points->at(k + 0), points->at(k + 1));
324804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            } else {
325804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                outPath->moveTo(points->at(k + 0), points->at(k + 1));
326804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                currentSegmentStartX = currentX;
327804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                currentSegmentStartY = currentY;
328804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            }
329804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            break;
330804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        case 'l': // lineto - Draw a line from the current point (relative)
331804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            outPath->rLineTo(points->at(k + 0), points->at(k + 1));
332804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentX += points->at(k + 0);
333804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentY += points->at(k + 1);
334804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            break;
335804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        case 'L': // lineto - Draw a line from the current point
336804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            outPath->lineTo(points->at(k + 0), points->at(k + 1));
337804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentX = points->at(k + 0);
338804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentY = points->at(k + 1);
339804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            break;
340804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        case 'h': // horizontal lineto - Draws a horizontal line (relative)
341804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            outPath->rLineTo(points->at(k + 0), 0);
342804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentX += points->at(k + 0);
343804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            break;
344804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        case 'H': // horizontal lineto - Draws a horizontal line
345804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            outPath->lineTo(points->at(k + 0), currentY);
346804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentX = points->at(k + 0);
347804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            break;
348804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        case 'v': // vertical lineto - Draws a vertical line from the current point (r)
349804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            outPath->rLineTo(0, points->at(k + 0));
350804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentY += points->at(k + 0);
351804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            break;
352804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        case 'V': // vertical lineto - Draws a vertical line from the current point
353804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            outPath->lineTo(currentX, points->at(k + 0));
354804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentY = points->at(k + 0);
355804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            break;
356804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        case 'c': // curveto - Draws a cubic Bézier curve (relative)
357804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            outPath->rCubicTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3),
358804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    points->at(k + 4), points->at(k + 5));
359804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
360804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            ctrlPointX = currentX + points->at(k + 2);
361804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            ctrlPointY = currentY + points->at(k + 3);
362804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentX += points->at(k + 4);
363804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentY += points->at(k + 5);
364804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
365804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            break;
366804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        case 'C': // curveto - Draws a cubic Bézier curve
367804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            outPath->cubicTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3),
368804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    points->at(k + 4), points->at(k + 5));
369804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentX = points->at(k + 4);
370804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentY = points->at(k + 5);
371804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            ctrlPointX = points->at(k + 2);
372804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            ctrlPointY = points->at(k + 3);
373804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            break;
374804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        case 's': // smooth curveto - Draws a cubic Bézier curve (reflective cp)
375804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            reflectiveCtrlPointX = 0;
376804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            reflectiveCtrlPointY = 0;
377804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            if (previousCmd == 'c' || previousCmd == 's'
378804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    || previousCmd == 'C' || previousCmd == 'S') {
379804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                reflectiveCtrlPointX = currentX - ctrlPointX;
380804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                reflectiveCtrlPointY = currentY - ctrlPointY;
381804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            }
382804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            outPath->rCubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
383804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    points->at(k + 0), points->at(k + 1),
384804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    points->at(k + 2), points->at(k + 3));
385804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            ctrlPointX = currentX + points->at(k + 0);
386804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            ctrlPointY = currentY + points->at(k + 1);
387804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentX += points->at(k + 2);
388804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentY += points->at(k + 3);
389804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            break;
390804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        case 'S': // shorthand/smooth curveto Draws a cubic Bézier curve(reflective cp)
391804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            reflectiveCtrlPointX = currentX;
392804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            reflectiveCtrlPointY = currentY;
393804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            if (previousCmd == 'c' || previousCmd == 's'
394804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    || previousCmd == 'C' || previousCmd == 'S') {
395804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
396804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
397804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            }
398804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            outPath->cubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
399804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3));
400804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            ctrlPointX = points->at(k + 0);
401804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            ctrlPointY = points->at(k + 1);
402804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentX = points->at(k + 2);
403804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentY = points->at(k + 3);
404804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            break;
405804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        case 'q': // Draws a quadratic Bézier (relative)
406804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            outPath->rQuadTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3));
407804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            ctrlPointX = currentX + points->at(k + 0);
408804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            ctrlPointY = currentY + points->at(k + 1);
409804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentX += points->at(k + 2);
410804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentY += points->at(k + 3);
411804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            break;
412804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        case 'Q': // Draws a quadratic Bézier
413804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            outPath->quadTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3));
414804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            ctrlPointX = points->at(k + 0);
415804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            ctrlPointY = points->at(k + 1);
416804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentX = points->at(k + 2);
417804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentY = points->at(k + 3);
418804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            break;
419804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        case 't': // Draws a quadratic Bézier curve(reflective control point)(relative)
420804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            reflectiveCtrlPointX = 0;
421804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            reflectiveCtrlPointY = 0;
422804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            if (previousCmd == 'q' || previousCmd == 't'
423804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    || previousCmd == 'Q' || previousCmd == 'T') {
424804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                reflectiveCtrlPointX = currentX - ctrlPointX;
425804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                reflectiveCtrlPointY = currentY - ctrlPointY;
426804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            }
427804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            outPath->rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
428804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    points->at(k + 0), points->at(k + 1));
429804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            ctrlPointX = currentX + reflectiveCtrlPointX;
430804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            ctrlPointY = currentY + reflectiveCtrlPointY;
431804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentX += points->at(k + 0);
432804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentY += points->at(k + 1);
433804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            break;
434804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        case 'T': // Draws a quadratic Bézier curve (reflective control point)
435804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            reflectiveCtrlPointX = currentX;
436804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            reflectiveCtrlPointY = currentY;
437804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            if (previousCmd == 'q' || previousCmd == 't'
438804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    || previousCmd == 'Q' || previousCmd == 'T') {
439804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
440804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
441804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            }
442804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            outPath->quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
443804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    points->at(k + 0), points->at(k + 1));
444804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            ctrlPointX = reflectiveCtrlPointX;
445804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            ctrlPointY = reflectiveCtrlPointY;
446804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentX = points->at(k + 0);
447804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentY = points->at(k + 1);
448804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            break;
449804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        case 'a': // Draws an elliptical arc
450804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            // (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
451804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            drawArc(outPath,
452804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    currentX,
453804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    currentY,
454804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    points->at(k + 5) + currentX,
455804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    points->at(k + 6) + currentY,
456804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    points->at(k + 0),
457804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    points->at(k + 1),
458804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    points->at(k + 2),
459804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    points->at(k + 3) != 0,
460804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    points->at(k + 4) != 0);
461804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentX += points->at(k + 5);
462804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentY += points->at(k + 6);
463804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            ctrlPointX = currentX;
464804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            ctrlPointY = currentY;
465804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            break;
466804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        case 'A': // Draws an elliptical arc
467804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            drawArc(outPath,
468804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    currentX,
469804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    currentY,
470804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    points->at(k + 5),
471804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    points->at(k + 6),
472804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    points->at(k + 0),
473804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    points->at(k + 1),
474804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    points->at(k + 2),
475804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    points->at(k + 3) != 0,
476804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    points->at(k + 4) != 0);
477804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentX = points->at(k + 5);
478804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            currentY = points->at(k + 6);
479804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            ctrlPointX = currentX;
480804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            ctrlPointY = currentY;
481804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            break;
482804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        default:
483804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            LOG_ALWAYS_FATAL("Unsupported command: %c", cmd);
484804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            break;
485804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        }
486804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        previousCmd = cmd;
487804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    }
488804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu}
489804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
490804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu} // namespace uirenderer
491804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu} // namespace android
492