134cbdb2e17271b31d1511d2773edd241fafede7dztenghui/*
2142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki * Copyright (C) 2017 The Android Open Source Project
334cbdb2e17271b31d1511d2773edd241fafede7dztenghui *
4b6086751979cb14740815502597e9fcfddb7054aztenghui * Licensed under the Apache License, Version 2.0 (the "License");
5b6086751979cb14740815502597e9fcfddb7054aztenghui * you may not use this file except in compliance with the License.
6b6086751979cb14740815502597e9fcfddb7054aztenghui * You may obtain a copy of the License at
734cbdb2e17271b31d1511d2773edd241fafede7dztenghui *
8b6086751979cb14740815502597e9fcfddb7054aztenghui *      http://www.apache.org/licenses/LICENSE-2.0
934cbdb2e17271b31d1511d2773edd241fafede7dztenghui *
10b6086751979cb14740815502597e9fcfddb7054aztenghui * Unless required by applicable law or agreed to in writing, software
11b6086751979cb14740815502597e9fcfddb7054aztenghui * distributed under the License is distributed on an "AS IS" BASIS,
12b6086751979cb14740815502597e9fcfddb7054aztenghui * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b6086751979cb14740815502597e9fcfddb7054aztenghui * See the License for the specific language governing permissions and
14b6086751979cb14740815502597e9fcfddb7054aztenghui * limitations under the License.
1534cbdb2e17271b31d1511d2773edd241fafede7dztenghui */
1634cbdb2e17271b31d1511d2773edd241fafede7dztenghui
17142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Arakipackage android.support.v4.graphics;
18142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki
19142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Arakiimport static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
2034cbdb2e17271b31d1511d2773edd241fafede7dztenghui
2134cbdb2e17271b31d1511d2773edd241fafede7dztenghuiimport android.graphics.Path;
22142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Arakiimport android.support.annotation.RestrictTo;
2334cbdb2e17271b31d1511d2773edd241fafede7dztenghuiimport android.util.Log;
2434cbdb2e17271b31d1511d2773edd241fafede7dztenghui
2534cbdb2e17271b31d1511d2773edd241fafede7dztenghuiimport java.util.ArrayList;
2634cbdb2e17271b31d1511d2773edd241fafede7dztenghui
27142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki/**
28142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki * This class is a duplicate from the PathParser.java of frameworks/base, with slight
29142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki * update on incompatible API like copyOfRange().
30142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki *
31142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki * @hide
32142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki */
33142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki@RestrictTo(LIBRARY_GROUP)
34142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Arakipublic class PathParser {
3534cbdb2e17271b31d1511d2773edd241fafede7dztenghui    private static final String LOGTAG = "PathParser";
3634cbdb2e17271b31d1511d2773edd241fafede7dztenghui
3734cbdb2e17271b31d1511d2773edd241fafede7dztenghui    // Copy from Arrays.copyOfRange() which is only available from API level 9.
382c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu
3934cbdb2e17271b31d1511d2773edd241fafede7dztenghui    /**
4034cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * Copies elements from {@code original} into a new array, from indexes start (inclusive) to
4134cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * end (exclusive). The original order of elements is preserved.
4234cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * If {@code end} is greater than {@code original.length}, the result is padded
4334cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * with the value {@code 0.0f}.
4434cbdb2e17271b31d1511d2773edd241fafede7dztenghui     *
4534cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @param original the original array
462c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu     * @param start    the start index, inclusive
472c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu     * @param end      the end index, exclusive
4834cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @return the new array
4934cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @throws ArrayIndexOutOfBoundsException if {@code start < 0 || start > original.length}
502c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu     * @throws IllegalArgumentException       if {@code start > end}
512c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu     * @throws NullPointerException           if {@code original == null}
5234cbdb2e17271b31d1511d2773edd241fafede7dztenghui     */
538fbfb9b792b2978ba33bead0120a83b7af47a5f7Aurimas Liutikas    static float[] copyOfRange(float[] original, int start, int end) {
5434cbdb2e17271b31d1511d2773edd241fafede7dztenghui        if (start > end) {
5534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            throw new IllegalArgumentException();
5634cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
5734cbdb2e17271b31d1511d2773edd241fafede7dztenghui        int originalLength = original.length;
5834cbdb2e17271b31d1511d2773edd241fafede7dztenghui        if (start < 0 || start > originalLength) {
5934cbdb2e17271b31d1511d2773edd241fafede7dztenghui            throw new ArrayIndexOutOfBoundsException();
6034cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
6134cbdb2e17271b31d1511d2773edd241fafede7dztenghui        int resultLength = end - start;
6234cbdb2e17271b31d1511d2773edd241fafede7dztenghui        int copyLength = Math.min(resultLength, originalLength - start);
6334cbdb2e17271b31d1511d2773edd241fafede7dztenghui        float[] result = new float[resultLength];
6434cbdb2e17271b31d1511d2773edd241fafede7dztenghui        System.arraycopy(original, start, result, 0, copyLength);
6534cbdb2e17271b31d1511d2773edd241fafede7dztenghui        return result;
6634cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
6734cbdb2e17271b31d1511d2773edd241fafede7dztenghui
6834cbdb2e17271b31d1511d2773edd241fafede7dztenghui    /**
6934cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @param pathData The string representing a path, the same as "d" string in svg file.
7034cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @return the generated Path object.
7134cbdb2e17271b31d1511d2773edd241fafede7dztenghui     */
7234cbdb2e17271b31d1511d2773edd241fafede7dztenghui    public static Path createPathFromPathData(String pathData) {
7334cbdb2e17271b31d1511d2773edd241fafede7dztenghui        Path path = new Path();
7434cbdb2e17271b31d1511d2773edd241fafede7dztenghui        PathDataNode[] nodes = createNodesFromPathData(pathData);
7534cbdb2e17271b31d1511d2773edd241fafede7dztenghui        if (nodes != null) {
7634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            try {
7734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                PathDataNode.nodesToPath(nodes, path);
7834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            } catch (RuntimeException e) {
7934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                throw new RuntimeException("Error in parsing " + pathData, e);
8034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
8134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            return path;
8234cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
8334cbdb2e17271b31d1511d2773edd241fafede7dztenghui        return null;
8434cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
8534cbdb2e17271b31d1511d2773edd241fafede7dztenghui
8634cbdb2e17271b31d1511d2773edd241fafede7dztenghui    /**
8734cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @param pathData The string representing a path, the same as "d" string in svg file.
8834cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @return an array of the PathDataNode.
8934cbdb2e17271b31d1511d2773edd241fafede7dztenghui     */
9034cbdb2e17271b31d1511d2773edd241fafede7dztenghui    public static PathDataNode[] createNodesFromPathData(String pathData) {
9134cbdb2e17271b31d1511d2773edd241fafede7dztenghui        if (pathData == null) {
9234cbdb2e17271b31d1511d2773edd241fafede7dztenghui            return null;
9334cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
9434cbdb2e17271b31d1511d2773edd241fafede7dztenghui        int start = 0;
9534cbdb2e17271b31d1511d2773edd241fafede7dztenghui        int end = 1;
9634cbdb2e17271b31d1511d2773edd241fafede7dztenghui
9734cbdb2e17271b31d1511d2773edd241fafede7dztenghui        ArrayList<PathDataNode> list = new ArrayList<PathDataNode>();
9834cbdb2e17271b31d1511d2773edd241fafede7dztenghui        while (end < pathData.length()) {
9934cbdb2e17271b31d1511d2773edd241fafede7dztenghui            end = nextStart(pathData, end);
10034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            String s = pathData.substring(start, end).trim();
10134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            if (s.length() > 0) {
10234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                float[] val = getFloats(s);
10334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                addNode(list, s.charAt(0), val);
10434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
10534cbdb2e17271b31d1511d2773edd241fafede7dztenghui
10634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            start = end;
10734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            end++;
10834cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
10934cbdb2e17271b31d1511d2773edd241fafede7dztenghui        if ((end - start) == 1 && start < pathData.length()) {
11034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            addNode(list, pathData.charAt(start), new float[0]);
11134cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
11234cbdb2e17271b31d1511d2773edd241fafede7dztenghui        return list.toArray(new PathDataNode[list.size()]);
11334cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
11434cbdb2e17271b31d1511d2773edd241fafede7dztenghui
11534cbdb2e17271b31d1511d2773edd241fafede7dztenghui    /**
11634cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @param source The array of PathDataNode to be duplicated.
11734cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @return a deep copy of the <code>source</code>.
11834cbdb2e17271b31d1511d2773edd241fafede7dztenghui     */
11934cbdb2e17271b31d1511d2773edd241fafede7dztenghui    public static PathDataNode[] deepCopyNodes(PathDataNode[] source) {
12034cbdb2e17271b31d1511d2773edd241fafede7dztenghui        if (source == null) {
12134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            return null;
12234cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
12334cbdb2e17271b31d1511d2773edd241fafede7dztenghui        PathDataNode[] copy = new PathParser.PathDataNode[source.length];
1242c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu        for (int i = 0; i < source.length; i++) {
12534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            copy[i] = new PathDataNode(source[i]);
12634cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
12734cbdb2e17271b31d1511d2773edd241fafede7dztenghui        return copy;
12834cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
12934cbdb2e17271b31d1511d2773edd241fafede7dztenghui
13034cbdb2e17271b31d1511d2773edd241fafede7dztenghui    /**
13134cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @param nodesFrom The source path represented in an array of PathDataNode
1322c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu     * @param nodesTo   The target path represented in an array of PathDataNode
13334cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @return whether the <code>nodesFrom</code> can morph into <code>nodesTo</code>
13434cbdb2e17271b31d1511d2773edd241fafede7dztenghui     */
13534cbdb2e17271b31d1511d2773edd241fafede7dztenghui    public static boolean canMorph(PathDataNode[] nodesFrom, PathDataNode[] nodesTo) {
13634cbdb2e17271b31d1511d2773edd241fafede7dztenghui        if (nodesFrom == null || nodesTo == null) {
13734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            return false;
13834cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
13934cbdb2e17271b31d1511d2773edd241fafede7dztenghui
14034cbdb2e17271b31d1511d2773edd241fafede7dztenghui        if (nodesFrom.length != nodesTo.length) {
14134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            return false;
14234cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
14334cbdb2e17271b31d1511d2773edd241fafede7dztenghui
1442c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu        for (int i = 0; i < nodesFrom.length; i++) {
145b6086751979cb14740815502597e9fcfddb7054aztenghui            if (nodesFrom[i].mType != nodesTo[i].mType
146b6086751979cb14740815502597e9fcfddb7054aztenghui                    || nodesFrom[i].mParams.length != nodesTo[i].mParams.length) {
14734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                return false;
14834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
14934cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
15034cbdb2e17271b31d1511d2773edd241fafede7dztenghui        return true;
15134cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
15234cbdb2e17271b31d1511d2773edd241fafede7dztenghui
15334cbdb2e17271b31d1511d2773edd241fafede7dztenghui    /**
15434cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * Update the target's data to match the source.
15534cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * Before calling this, make sure canMorph(target, source) is true.
15634cbdb2e17271b31d1511d2773edd241fafede7dztenghui     *
15734cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @param target The target path represented in an array of PathDataNode
15834cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @param source The source path represented in an array of PathDataNode
15934cbdb2e17271b31d1511d2773edd241fafede7dztenghui     */
16034cbdb2e17271b31d1511d2773edd241fafede7dztenghui    public static void updateNodes(PathDataNode[] target, PathDataNode[] source) {
1612c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu        for (int i = 0; i < source.length; i++) {
162b6086751979cb14740815502597e9fcfddb7054aztenghui            target[i].mType = source[i].mType;
163b6086751979cb14740815502597e9fcfddb7054aztenghui            for (int j = 0; j < source[i].mParams.length; j++) {
164b6086751979cb14740815502597e9fcfddb7054aztenghui                target[i].mParams[j] = source[i].mParams[j];
16534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
16634cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
16734cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
16834cbdb2e17271b31d1511d2773edd241fafede7dztenghui
16934cbdb2e17271b31d1511d2773edd241fafede7dztenghui    private static int nextStart(String s, int end) {
17034cbdb2e17271b31d1511d2773edd241fafede7dztenghui        char c;
17134cbdb2e17271b31d1511d2773edd241fafede7dztenghui
17234cbdb2e17271b31d1511d2773edd241fafede7dztenghui        while (end < s.length()) {
17334cbdb2e17271b31d1511d2773edd241fafede7dztenghui            c = s.charAt(end);
17434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            // Note that 'e' or 'E' are not valid path commands, but could be
17534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            // used for floating point numbers' scientific notation.
17634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            // Therefore, when searching for next command, we should ignore 'e'
17734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            // and 'E'.
17834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            if ((((c - 'A') * (c - 'Z') <= 0) || ((c - 'a') * (c - 'z') <= 0))
17934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    && c != 'e' && c != 'E') {
18034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                return end;
18134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
18234cbdb2e17271b31d1511d2773edd241fafede7dztenghui            end++;
18334cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
18434cbdb2e17271b31d1511d2773edd241fafede7dztenghui        return end;
18534cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
18634cbdb2e17271b31d1511d2773edd241fafede7dztenghui
18734cbdb2e17271b31d1511d2773edd241fafede7dztenghui    private static void addNode(ArrayList<PathDataNode> list, char cmd, float[] val) {
18834cbdb2e17271b31d1511d2773edd241fafede7dztenghui        list.add(new PathDataNode(cmd, val));
18934cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
19034cbdb2e17271b31d1511d2773edd241fafede7dztenghui
19134cbdb2e17271b31d1511d2773edd241fafede7dztenghui    private static class ExtractFloatResult {
19234cbdb2e17271b31d1511d2773edd241fafede7dztenghui        // We need to return the position of the next separator and whether the
19334cbdb2e17271b31d1511d2773edd241fafede7dztenghui        // next float starts with a '-' or a '.'.
19434cbdb2e17271b31d1511d2773edd241fafede7dztenghui        int mEndPosition;
19534cbdb2e17271b31d1511d2773edd241fafede7dztenghui        boolean mEndWithNegOrDot;
1968fbfb9b792b2978ba33bead0120a83b7af47a5f7Aurimas Liutikas
1978fbfb9b792b2978ba33bead0120a83b7af47a5f7Aurimas Liutikas        ExtractFloatResult() {
1988fbfb9b792b2978ba33bead0120a83b7af47a5f7Aurimas Liutikas        }
19934cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
20034cbdb2e17271b31d1511d2773edd241fafede7dztenghui
20134cbdb2e17271b31d1511d2773edd241fafede7dztenghui    /**
20234cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * Parse the floats in the string.
20334cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * This is an optimized version of parseFloat(s.split(",|\\s"));
20434cbdb2e17271b31d1511d2773edd241fafede7dztenghui     *
20534cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @param s the string containing a command and list of floats
20634cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @return array of floats
20734cbdb2e17271b31d1511d2773edd241fafede7dztenghui     */
20834cbdb2e17271b31d1511d2773edd241fafede7dztenghui    private static float[] getFloats(String s) {
20998ec0d5b378c8417156037af6389e90f0074a26eAurimas Liutikas        if (s.charAt(0) == 'z' || s.charAt(0) == 'Z') {
21034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            return new float[0];
21134cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
21234cbdb2e17271b31d1511d2773edd241fafede7dztenghui        try {
21334cbdb2e17271b31d1511d2773edd241fafede7dztenghui            float[] results = new float[s.length()];
21434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            int count = 0;
21534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            int startPosition = 1;
21634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            int endPosition = 0;
21734cbdb2e17271b31d1511d2773edd241fafede7dztenghui
21834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            ExtractFloatResult result = new ExtractFloatResult();
21934cbdb2e17271b31d1511d2773edd241fafede7dztenghui            int totalLength = s.length();
22034cbdb2e17271b31d1511d2773edd241fafede7dztenghui
22134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            // The startPosition should always be the first character of the
22234cbdb2e17271b31d1511d2773edd241fafede7dztenghui            // current number, and endPosition is the character after the current
22334cbdb2e17271b31d1511d2773edd241fafede7dztenghui            // number.
22434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            while (startPosition < totalLength) {
22534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                extract(s, startPosition, result);
22634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                endPosition = result.mEndPosition;
22734cbdb2e17271b31d1511d2773edd241fafede7dztenghui
22834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                if (startPosition < endPosition) {
22934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    results[count++] = Float.parseFloat(
23034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                            s.substring(startPosition, endPosition));
23134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                }
23234cbdb2e17271b31d1511d2773edd241fafede7dztenghui
23334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                if (result.mEndWithNegOrDot) {
23434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    // Keep the '-' or '.' sign with next number.
23534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    startPosition = endPosition;
23634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                } else {
23734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    startPosition = endPosition + 1;
23834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                }
23934cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
24034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            return copyOfRange(results, 0, count);
24134cbdb2e17271b31d1511d2773edd241fafede7dztenghui        } catch (NumberFormatException e) {
24234cbdb2e17271b31d1511d2773edd241fafede7dztenghui            throw new RuntimeException("error in parsing \"" + s + "\"", e);
24334cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
24434cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
24534cbdb2e17271b31d1511d2773edd241fafede7dztenghui
24634cbdb2e17271b31d1511d2773edd241fafede7dztenghui    /**
24734cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * Calculate the position of the next comma or space or negative sign
2482c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu     *
2492c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu     * @param s      the string to search
2502c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu     * @param start  the position to start searching
25134cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @param result the result of the extraction, including the position of the
2522c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu     *               the starting position of next number, whether it is ending with a '-'.
25334cbdb2e17271b31d1511d2773edd241fafede7dztenghui     */
25434cbdb2e17271b31d1511d2773edd241fafede7dztenghui    private static void extract(String s, int start, ExtractFloatResult result) {
25534cbdb2e17271b31d1511d2773edd241fafede7dztenghui        // Now looking for ' ', ',', '.' or '-' from the start.
25634cbdb2e17271b31d1511d2773edd241fafede7dztenghui        int currentIndex = start;
25734cbdb2e17271b31d1511d2773edd241fafede7dztenghui        boolean foundSeparator = false;
25834cbdb2e17271b31d1511d2773edd241fafede7dztenghui        result.mEndWithNegOrDot = false;
25934cbdb2e17271b31d1511d2773edd241fafede7dztenghui        boolean secondDot = false;
26034cbdb2e17271b31d1511d2773edd241fafede7dztenghui        boolean isExponential = false;
26134cbdb2e17271b31d1511d2773edd241fafede7dztenghui        for (; currentIndex < s.length(); currentIndex++) {
26234cbdb2e17271b31d1511d2773edd241fafede7dztenghui            boolean isPrevExponential = isExponential;
26334cbdb2e17271b31d1511d2773edd241fafede7dztenghui            isExponential = false;
26434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            char currentChar = s.charAt(currentIndex);
26534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            switch (currentChar) {
26634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case ' ':
26734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case ',':
26834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    foundSeparator = true;
26934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    break;
27034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case '-':
27134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    // The negative sign following a 'e' or 'E' is not a separator.
27234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    if (currentIndex != start && !isPrevExponential) {
27334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        foundSeparator = true;
27434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        result.mEndWithNegOrDot = true;
27534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    }
27634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    break;
27734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case '.':
27834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    if (!secondDot) {
27934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        secondDot = true;
28034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    } else {
28134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        // This is the second dot, and it is considered as a separator.
28234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        foundSeparator = true;
28334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        result.mEndWithNegOrDot = true;
28434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    }
28534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    break;
28634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'e':
28734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'E':
28834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    isExponential = true;
28934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    break;
29034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
29134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            if (foundSeparator) {
29234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                break;
29334cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
29434cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
29534cbdb2e17271b31d1511d2773edd241fafede7dztenghui        // When there is nothing found, then we put the end position to the end
29634cbdb2e17271b31d1511d2773edd241fafede7dztenghui        // of the string.
29734cbdb2e17271b31d1511d2773edd241fafede7dztenghui        result.mEndPosition = currentIndex;
29834cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
29934cbdb2e17271b31d1511d2773edd241fafede7dztenghui
30034cbdb2e17271b31d1511d2773edd241fafede7dztenghui    /**
30134cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * Each PathDataNode represents one command in the "d" attribute of the svg
30234cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * file.
30334cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * An array of PathDataNode can represent the whole "d" attribute.
30434cbdb2e17271b31d1511d2773edd241fafede7dztenghui     */
30534cbdb2e17271b31d1511d2773edd241fafede7dztenghui    public static class PathDataNode {
306142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki
307142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki        /**
308142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki         * @hide
309142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki         */
310142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki        @RestrictTo(LIBRARY_GROUP)
311142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki        public char mType;
312142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki
313142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki        /**
314142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki         * @hide
315142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki         */
316142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki        @RestrictTo(LIBRARY_GROUP)
317142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki        public float[] mParams;
31834cbdb2e17271b31d1511d2773edd241fafede7dztenghui
3198fbfb9b792b2978ba33bead0120a83b7af47a5f7Aurimas Liutikas        PathDataNode(char type, float[] params) {
320b6086751979cb14740815502597e9fcfddb7054aztenghui            this.mType = type;
321b6086751979cb14740815502597e9fcfddb7054aztenghui            this.mParams = params;
32234cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
32334cbdb2e17271b31d1511d2773edd241fafede7dztenghui
3248fbfb9b792b2978ba33bead0120a83b7af47a5f7Aurimas Liutikas        PathDataNode(PathDataNode n) {
325b6086751979cb14740815502597e9fcfddb7054aztenghui            mType = n.mType;
326b6086751979cb14740815502597e9fcfddb7054aztenghui            mParams = copyOfRange(n.mParams, 0, n.mParams.length);
32734cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
32834cbdb2e17271b31d1511d2773edd241fafede7dztenghui
32934cbdb2e17271b31d1511d2773edd241fafede7dztenghui        /**
33034cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * Convert an array of PathDataNode to Path.
33134cbdb2e17271b31d1511d2773edd241fafede7dztenghui         *
33234cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * @param node The source array of PathDataNode.
33334cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * @param path The target Path object.
33434cbdb2e17271b31d1511d2773edd241fafede7dztenghui         */
33534cbdb2e17271b31d1511d2773edd241fafede7dztenghui        public static void nodesToPath(PathDataNode[] node, Path path) {
33634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            float[] current = new float[6];
33734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            char previousCommand = 'm';
33834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            for (int i = 0; i < node.length; i++) {
339b6086751979cb14740815502597e9fcfddb7054aztenghui                addCommand(path, current, previousCommand, node[i].mType, node[i].mParams);
340b6086751979cb14740815502597e9fcfddb7054aztenghui                previousCommand = node[i].mType;
34134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
34234cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
34334cbdb2e17271b31d1511d2773edd241fafede7dztenghui
34434cbdb2e17271b31d1511d2773edd241fafede7dztenghui        /**
34534cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * The current PathDataNode will be interpolated between the
34634cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * <code>nodeFrom</code> and <code>nodeTo</code> according to the
34734cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * <code>fraction</code>.
34834cbdb2e17271b31d1511d2773edd241fafede7dztenghui         *
34934cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * @param nodeFrom The start value as a PathDataNode.
3502c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu         * @param nodeTo   The end value as a PathDataNode
35134cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * @param fraction The fraction to interpolate.
35234cbdb2e17271b31d1511d2773edd241fafede7dztenghui         */
35334cbdb2e17271b31d1511d2773edd241fafede7dztenghui        public void interpolatePathDataNode(PathDataNode nodeFrom,
354b6086751979cb14740815502597e9fcfddb7054aztenghui                PathDataNode nodeTo, float fraction) {
355b6086751979cb14740815502597e9fcfddb7054aztenghui            for (int i = 0; i < nodeFrom.mParams.length; i++) {
356b6086751979cb14740815502597e9fcfddb7054aztenghui                mParams[i] = nodeFrom.mParams[i] * (1 - fraction)
357b6086751979cb14740815502597e9fcfddb7054aztenghui                        + nodeTo.mParams[i] * fraction;
35834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
35934cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
36034cbdb2e17271b31d1511d2773edd241fafede7dztenghui
36134cbdb2e17271b31d1511d2773edd241fafede7dztenghui        private static void addCommand(Path path, float[] current,
362b6086751979cb14740815502597e9fcfddb7054aztenghui                char previousCmd, char cmd, float[] val) {
36334cbdb2e17271b31d1511d2773edd241fafede7dztenghui
36434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            int incr = 2;
36534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            float currentX = current[0];
36634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            float currentY = current[1];
36734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            float ctrlPointX = current[2];
36834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            float ctrlPointY = current[3];
36934cbdb2e17271b31d1511d2773edd241fafede7dztenghui            float currentSegmentStartX = current[4];
37034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            float currentSegmentStartY = current[5];
37134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            float reflectiveCtrlPointX;
37234cbdb2e17271b31d1511d2773edd241fafede7dztenghui            float reflectiveCtrlPointY;
37334cbdb2e17271b31d1511d2773edd241fafede7dztenghui
37434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            switch (cmd) {
37534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'z':
37634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'Z':
37734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    path.close();
37834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    // Path is closed here, but we need to move the pen to the
37934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    // closed position. So we cache the segment's starting position,
38034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    // and restore it here.
38134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    currentX = currentSegmentStartX;
38234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    currentY = currentSegmentStartY;
38334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    ctrlPointX = currentSegmentStartX;
38434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    ctrlPointY = currentSegmentStartY;
38534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    path.moveTo(currentX, currentY);
38634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    break;
38734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'm':
38834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'M':
38934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'l':
39034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'L':
39134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 't':
39234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'T':
39334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    incr = 2;
39434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    break;
39534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'h':
39634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'H':
39734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'v':
39834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'V':
39934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    incr = 1;
40034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    break;
40134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'c':
40234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'C':
40334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    incr = 6;
40434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    break;
40534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 's':
40634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'S':
40734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'q':
40834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'Q':
40934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    incr = 4;
41034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    break;
41134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'a':
41234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'A':
41334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    incr = 7;
41434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    break;
41534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
41634cbdb2e17271b31d1511d2773edd241fafede7dztenghui
41734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            for (int k = 0; k < val.length; k += incr) {
41834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                switch (cmd) {
41934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'm': // moveto - Start a new sub-path (relative)
42034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX += val[k + 0];
42134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY += val[k + 1];
4222c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu                        if (k > 0) {
4232c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu                            // According to the spec, if a moveto is followed by multiple
4242c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu                            // pairs of coordinates, the subsequent pairs are treated as
4252c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu                            // implicit lineto commands.
4262c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu                            path.rLineTo(val[k + 0], val[k + 1]);
4272c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu                        } else {
4282c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu                            path.rMoveTo(val[k + 0], val[k + 1]);
4292c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu                            currentSegmentStartX = currentX;
4302c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu                            currentSegmentStartY = currentY;
4312c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu                        }
43234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
43334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'M': // moveto - Start a new sub-path
43434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX = val[k + 0];
43534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY = val[k + 1];
4362c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu                        if (k > 0) {
4372c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu                            // According to the spec, if a moveto is followed by multiple
4382c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu                            // pairs of coordinates, the subsequent pairs are treated as
4392c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu                            // implicit lineto commands.
4402c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu                            path.lineTo(val[k + 0], val[k + 1]);
4412c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu                        } else {
4422c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu                            path.moveTo(val[k + 0], val[k + 1]);
4432c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu                            currentSegmentStartX = currentX;
4442c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu                            currentSegmentStartY = currentY;
4452c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu                        }
44634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
44734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'l': // lineto - Draw a line from the current point (relative)
44834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.rLineTo(val[k + 0], val[k + 1]);
44934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX += val[k + 0];
45034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY += val[k + 1];
45134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
45234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'L': // lineto - Draw a line from the current point
45334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.lineTo(val[k + 0], val[k + 1]);
45434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX = val[k + 0];
45534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY = val[k + 1];
45634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
45734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'h': // horizontal lineto - Draws a horizontal line (relative)
45834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.rLineTo(val[k + 0], 0);
45934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX += val[k + 0];
46034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
46134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'H': // horizontal lineto - Draws a horizontal line
46234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.lineTo(val[k + 0], currentY);
46334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX = val[k + 0];
46434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
46534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'v': // vertical lineto - Draws a vertical line from the current point (r)
46634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.rLineTo(0, val[k + 0]);
46734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY += val[k + 0];
46834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
46934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'V': // vertical lineto - Draws a vertical line from the current point
47034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.lineTo(currentX, val[k + 0]);
47134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY = val[k + 0];
47234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
47334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'c': // curveto - Draws a cubic Bézier curve (relative)
47434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.rCubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
47534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 4], val[k + 5]);
47634cbdb2e17271b31d1511d2773edd241fafede7dztenghui
47734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointX = currentX + val[k + 2];
47834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointY = currentY + val[k + 3];
47934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX += val[k + 4];
48034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY += val[k + 5];
48134cbdb2e17271b31d1511d2773edd241fafede7dztenghui
48234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
48334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'C': // curveto - Draws a cubic Bézier curve
48434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.cubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
48534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 4], val[k + 5]);
48634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX = val[k + 4];
48734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY = val[k + 5];
48834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointX = val[k + 2];
48934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointY = val[k + 3];
49034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
49134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 's': // smooth curveto - Draws a cubic Bézier curve (reflective cp)
49234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        reflectiveCtrlPointX = 0;
49334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        reflectiveCtrlPointY = 0;
49434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        if (previousCmd == 'c' || previousCmd == 's'
49534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                || previousCmd == 'C' || previousCmd == 'S') {
49634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                            reflectiveCtrlPointX = currentX - ctrlPointX;
49734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                            reflectiveCtrlPointY = currentY - ctrlPointY;
49834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        }
49934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.rCubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
50034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 0], val[k + 1],
50134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 2], val[k + 3]);
50234cbdb2e17271b31d1511d2773edd241fafede7dztenghui
50334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointX = currentX + val[k + 0];
50434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointY = currentY + val[k + 1];
50534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX += val[k + 2];
50634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY += val[k + 3];
50734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
50834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'S': // shorthand/smooth curveto Draws a cubic Bézier curve(reflective cp)
50934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        reflectiveCtrlPointX = currentX;
51034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        reflectiveCtrlPointY = currentY;
51134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        if (previousCmd == 'c' || previousCmd == 's'
51234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                || previousCmd == 'C' || previousCmd == 'S') {
51334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                            reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
51434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                            reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
51534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        }
51634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.cubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
51734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
51834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointX = val[k + 0];
51934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointY = val[k + 1];
52034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX = val[k + 2];
52134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY = val[k + 3];
52234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
52334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'q': // Draws a quadratic Bézier (relative)
52434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.rQuadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
52534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointX = currentX + val[k + 0];
52634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointY = currentY + val[k + 1];
52734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX += val[k + 2];
52834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY += val[k + 3];
52934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
53034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'Q': // Draws a quadratic Bézier
53134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.quadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
53234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointX = val[k + 0];
53334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointY = val[k + 1];
53434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX = val[k + 2];
53534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY = val[k + 3];
53634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
53734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 't': // Draws a quadratic Bézier curve(reflective control point)(relative)
53834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        reflectiveCtrlPointX = 0;
53934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        reflectiveCtrlPointY = 0;
54034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        if (previousCmd == 'q' || previousCmd == 't'
54134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                || previousCmd == 'Q' || previousCmd == 'T') {
54234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                            reflectiveCtrlPointX = currentX - ctrlPointX;
54334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                            reflectiveCtrlPointY = currentY - ctrlPointY;
54434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        }
54534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
54634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 0], val[k + 1]);
54734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointX = currentX + reflectiveCtrlPointX;
54834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointY = currentY + reflectiveCtrlPointY;
54934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX += val[k + 0];
55034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY += val[k + 1];
55134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
55234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'T': // Draws a quadratic Bézier curve (reflective control point)
55334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        reflectiveCtrlPointX = currentX;
55434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        reflectiveCtrlPointY = currentY;
55534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        if (previousCmd == 'q' || previousCmd == 't'
55634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                || previousCmd == 'Q' || previousCmd == 'T') {
55734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                            reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
55834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                            reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
55934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        }
56034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
56134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 0], val[k + 1]);
56234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointX = reflectiveCtrlPointX;
56334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointY = reflectiveCtrlPointY;
56434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX = val[k + 0];
56534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY = val[k + 1];
56634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
56734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'a': // Draws an elliptical arc
56834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        // (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
56934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        drawArc(path,
57034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                currentX,
57134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                currentY,
57234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 5] + currentX,
57334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 6] + currentY,
57434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 0],
57534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 1],
57634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 2],
57734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 3] != 0,
57834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 4] != 0);
57934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX += val[k + 5];
58034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY += val[k + 6];
58134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointX = currentX;
58234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointY = currentY;
58334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
58434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'A': // Draws an elliptical arc
58534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        drawArc(path,
58634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                currentX,
58734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                currentY,
58834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 5],
58934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 6],
59034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 0],
59134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 1],
59234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 2],
59334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 3] != 0,
59434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 4] != 0);
59534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX = val[k + 5];
59634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY = val[k + 6];
59734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointX = currentX;
59834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointY = currentY;
59934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
60034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                }
60134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                previousCmd = cmd;
60234cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
60334cbdb2e17271b31d1511d2773edd241fafede7dztenghui            current[0] = currentX;
60434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            current[1] = currentY;
60534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            current[2] = ctrlPointX;
60634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            current[3] = ctrlPointY;
60734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            current[4] = currentSegmentStartX;
60834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            current[5] = currentSegmentStartY;
60934cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
61034cbdb2e17271b31d1511d2773edd241fafede7dztenghui
61134cbdb2e17271b31d1511d2773edd241fafede7dztenghui        private static void drawArc(Path p,
612b6086751979cb14740815502597e9fcfddb7054aztenghui                float x0,
613b6086751979cb14740815502597e9fcfddb7054aztenghui                float y0,
614b6086751979cb14740815502597e9fcfddb7054aztenghui                float x1,
615b6086751979cb14740815502597e9fcfddb7054aztenghui                float y1,
616b6086751979cb14740815502597e9fcfddb7054aztenghui                float a,
617b6086751979cb14740815502597e9fcfddb7054aztenghui                float b,
618b6086751979cb14740815502597e9fcfddb7054aztenghui                float theta,
619b6086751979cb14740815502597e9fcfddb7054aztenghui                boolean isMoreThanHalf,
620b6086751979cb14740815502597e9fcfddb7054aztenghui                boolean isPositiveArc) {
62134cbdb2e17271b31d1511d2773edd241fafede7dztenghui
62234cbdb2e17271b31d1511d2773edd241fafede7dztenghui            /* Convert rotation angle from degrees to radians */
62334cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double thetaD = Math.toRadians(theta);
62434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            /* Pre-compute rotation matrix entries */
62534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double cosTheta = Math.cos(thetaD);
62634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double sinTheta = Math.sin(thetaD);
62734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            /* Transform (x0, y0) and (x1, y1) into unit space */
62834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            /* using (inverse) rotation, followed by (inverse) scale */
62934cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double x0p = (x0 * cosTheta + y0 * sinTheta) / a;
63034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double y0p = (-x0 * sinTheta + y0 * cosTheta) / b;
63134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double x1p = (x1 * cosTheta + y1 * sinTheta) / a;
63234cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double y1p = (-x1 * sinTheta + y1 * cosTheta) / b;
63334cbdb2e17271b31d1511d2773edd241fafede7dztenghui
63434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            /* Compute differences and averages */
63534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double dx = x0p - x1p;
63634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double dy = y0p - y1p;
63734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double xm = (x0p + x1p) / 2;
63834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double ym = (y0p + y1p) / 2;
63934cbdb2e17271b31d1511d2773edd241fafede7dztenghui            /* Solve for intersecting unit circles */
64034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double dsq = dx * dx + dy * dy;
64134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            if (dsq == 0.0) {
64234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                Log.w(LOGTAG, " Points are coincident");
64334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                return; /* Points are coincident */
64434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
64534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double disc = 1.0 / dsq - 1.0 / 4.0;
64634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            if (disc < 0.0) {
64734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                Log.w(LOGTAG, "Points are too far apart " + dsq);
64834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                float adjust = (float) (Math.sqrt(dsq) / 1.99999);
64934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                drawArc(p, x0, y0, x1, y1, a * adjust,
65034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        b * adjust, theta, isMoreThanHalf, isPositiveArc);
65134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                return; /* Points are too far apart */
65234cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
65334cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double s = Math.sqrt(disc);
65434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double sdx = s * dx;
65534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double sdy = s * dy;
65634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double cx;
65734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double cy;
65834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            if (isMoreThanHalf == isPositiveArc) {
65934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                cx = xm - sdy;
66034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                cy = ym + sdx;
66134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            } else {
66234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                cx = xm + sdy;
66334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                cy = ym - sdx;
66434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
66534cbdb2e17271b31d1511d2773edd241fafede7dztenghui
66634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double eta0 = Math.atan2((y0p - cy), (x0p - cx));
66734cbdb2e17271b31d1511d2773edd241fafede7dztenghui
66834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double eta1 = Math.atan2((y1p - cy), (x1p - cx));
66934cbdb2e17271b31d1511d2773edd241fafede7dztenghui
67034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double sweep = (eta1 - eta0);
67134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            if (isPositiveArc != (sweep >= 0)) {
67234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                if (sweep > 0) {
67334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    sweep -= 2 * Math.PI;
67434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                } else {
67534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    sweep += 2 * Math.PI;
67634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                }
67734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
67834cbdb2e17271b31d1511d2773edd241fafede7dztenghui
67934cbdb2e17271b31d1511d2773edd241fafede7dztenghui            cx *= a;
68034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            cy *= b;
68134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double tcx = cx;
68234cbdb2e17271b31d1511d2773edd241fafede7dztenghui            cx = cx * cosTheta - cy * sinTheta;
68334cbdb2e17271b31d1511d2773edd241fafede7dztenghui            cy = tcx * sinTheta + cy * cosTheta;
68434cbdb2e17271b31d1511d2773edd241fafede7dztenghui
68534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep);
68634cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
68734cbdb2e17271b31d1511d2773edd241fafede7dztenghui
68834cbdb2e17271b31d1511d2773edd241fafede7dztenghui        /**
68934cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * Converts an arc to cubic Bezier segments and records them in p.
69034cbdb2e17271b31d1511d2773edd241fafede7dztenghui         *
6912c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu         * @param p     The target for the cubic Bezier segments
6922c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu         * @param cx    The x coordinate center of the ellipse
6932c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu         * @param cy    The y coordinate center of the ellipse
6942c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu         * @param a     The radius of the ellipse in the horizontal direction
6952c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu         * @param b     The radius of the ellipse in the vertical direction
6962c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu         * @param e1x   E(eta1) x coordinate of the starting point of the arc
6972c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu         * @param e1y   E(eta2) y coordinate of the starting point of the arc
69834cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane
69934cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * @param start The start angle of the arc on the ellipse
70034cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
70134cbdb2e17271b31d1511d2773edd241fafede7dztenghui         */
70234cbdb2e17271b31d1511d2773edd241fafede7dztenghui        private static void arcToBezier(Path p,
703b6086751979cb14740815502597e9fcfddb7054aztenghui                double cx,
704b6086751979cb14740815502597e9fcfddb7054aztenghui                double cy,
705b6086751979cb14740815502597e9fcfddb7054aztenghui                double a,
706b6086751979cb14740815502597e9fcfddb7054aztenghui                double b,
707b6086751979cb14740815502597e9fcfddb7054aztenghui                double e1x,
708b6086751979cb14740815502597e9fcfddb7054aztenghui                double e1y,
709b6086751979cb14740815502597e9fcfddb7054aztenghui                double theta,
710b6086751979cb14740815502597e9fcfddb7054aztenghui                double start,
711b6086751979cb14740815502597e9fcfddb7054aztenghui                double sweep) {
71234cbdb2e17271b31d1511d2773edd241fafede7dztenghui            // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html
71334cbdb2e17271b31d1511d2773edd241fafede7dztenghui            // and http://www.spaceroots.org/documents/ellipse/node22.html
71434cbdb2e17271b31d1511d2773edd241fafede7dztenghui
71534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            // Maximum of 45 degrees per cubic Bezier segment
7162c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu            int numSegments = (int) Math.ceil(Math.abs(sweep * 4 / Math.PI));
71734cbdb2e17271b31d1511d2773edd241fafede7dztenghui
71834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double eta1 = start;
71934cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double cosTheta = Math.cos(theta);
72034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double sinTheta = Math.sin(theta);
72134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double cosEta1 = Math.cos(eta1);
72234cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double sinEta1 = Math.sin(eta1);
72334cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1);
72434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1);
72534cbdb2e17271b31d1511d2773edd241fafede7dztenghui
72634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double anglePerSegment = sweep / numSegments;
72734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            for (int i = 0; i < numSegments; i++) {
72834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double eta2 = eta1 + anglePerSegment;
72934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double sinEta2 = Math.sin(eta2);
73034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double cosEta2 = Math.cos(eta2);
73134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2);
73234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2);
73334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2;
73434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2;
73534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double tanDiff2 = Math.tan((eta2 - eta1) / 2);
73634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double alpha =
73734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        Math.sin(eta2 - eta1) * (Math.sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3;
73834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double q1x = e1x + alpha * ep1x;
73934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double q1y = e1y + alpha * ep1y;
74034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double q2x = e2x - alpha * ep2x;
74134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double q2y = e2y - alpha * ep2y;
74234cbdb2e17271b31d1511d2773edd241fafede7dztenghui
743134f43b8234fc9a7b075abdb46e898e63f6c8b81ztenghui                // Adding this no-op call to workaround a proguard related issue.
744134f43b8234fc9a7b075abdb46e898e63f6c8b81ztenghui                p.rLineTo(0, 0);
745134f43b8234fc9a7b075abdb46e898e63f6c8b81ztenghui
746134f43b8234fc9a7b075abdb46e898e63f6c8b81ztenghui                p.cubicTo((float) q1x,
747134f43b8234fc9a7b075abdb46e898e63f6c8b81ztenghui                        (float) q1y,
748134f43b8234fc9a7b075abdb46e898e63f6c8b81ztenghui                        (float) q2x,
749134f43b8234fc9a7b075abdb46e898e63f6c8b81ztenghui                        (float) q2y,
750134f43b8234fc9a7b075abdb46e898e63f6c8b81ztenghui                        (float) e2x,
751134f43b8234fc9a7b075abdb46e898e63f6c8b81ztenghui                        (float) e2y);
75234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                eta1 = eta2;
75334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                e1x = e2x;
75434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                e1y = e2y;
75534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                ep1x = ep2x;
75634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                ep1y = ep2y;
75734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
75834cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
75934cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
76034cbdb2e17271b31d1511d2773edd241fafede7dztenghui}
761