PathParser.java revision d85f3e0b931d3e127242a375e0d00c4696bba912
134cbdb2e17271b31d1511d2773edd241fafede7dztenghui/*
234cbdb2e17271b31d1511d2773edd241fafede7dztenghui * Copyright (C) 2015 The Android Open Source Project
334cbdb2e17271b31d1511d2773edd241fafede7dztenghui *
434cbdb2e17271b31d1511d2773edd241fafede7dztenghui * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
534cbdb2e17271b31d1511d2773edd241fafede7dztenghui * in compliance with the License. You may obtain a copy of the License at
634cbdb2e17271b31d1511d2773edd241fafede7dztenghui *
734cbdb2e17271b31d1511d2773edd241fafede7dztenghui * http://www.apache.org/licenses/LICENSE-2.0
834cbdb2e17271b31d1511d2773edd241fafede7dztenghui *
934cbdb2e17271b31d1511d2773edd241fafede7dztenghui * Unless required by applicable law or agreed to in writing, software distributed under the License
1034cbdb2e17271b31d1511d2773edd241fafede7dztenghui * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
1134cbdb2e17271b31d1511d2773edd241fafede7dztenghui * or implied. See the License for the specific language governing permissions and limitations under
1234cbdb2e17271b31d1511d2773edd241fafede7dztenghui * the License.
1334cbdb2e17271b31d1511d2773edd241fafede7dztenghui */
1434cbdb2e17271b31d1511d2773edd241fafede7dztenghui
154fcaa70c2362e58a3fb30d140f0a0eeda8e35b44ztenghuipackage android.support.graphics.drawable;
1634cbdb2e17271b31d1511d2773edd241fafede7dztenghui
1734cbdb2e17271b31d1511d2773edd241fafede7dztenghuiimport android.graphics.Path;
1834cbdb2e17271b31d1511d2773edd241fafede7dztenghuiimport android.util.Log;
1934cbdb2e17271b31d1511d2773edd241fafede7dztenghui
2034cbdb2e17271b31d1511d2773edd241fafede7dztenghuiimport java.util.ArrayList;
2134cbdb2e17271b31d1511d2773edd241fafede7dztenghui
2234cbdb2e17271b31d1511d2773edd241fafede7dztenghui// This class is a duplicate from the PathParser.java of frameworks/base, with slight
2334cbdb2e17271b31d1511d2773edd241fafede7dztenghui// update on incompatible API like copyOfRange().
2434cbdb2e17271b31d1511d2773edd241fafede7dztenghuiclass PathParser {
2534cbdb2e17271b31d1511d2773edd241fafede7dztenghui    private static final String LOGTAG = "PathParser";
2634cbdb2e17271b31d1511d2773edd241fafede7dztenghui
2734cbdb2e17271b31d1511d2773edd241fafede7dztenghui    // Copy from Arrays.copyOfRange() which is only available from API level 9.
2834cbdb2e17271b31d1511d2773edd241fafede7dztenghui    /**
2934cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * Copies elements from {@code original} into a new array, from indexes start (inclusive) to
3034cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * end (exclusive). The original order of elements is preserved.
3134cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * If {@code end} is greater than {@code original.length}, the result is padded
3234cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * with the value {@code 0.0f}.
3334cbdb2e17271b31d1511d2773edd241fafede7dztenghui     *
3434cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @param original the original array
3534cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @param start the start index, inclusive
3634cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @param end the end index, exclusive
3734cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @return the new array
3834cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @throws ArrayIndexOutOfBoundsException if {@code start < 0 || start > original.length}
3934cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @throws IllegalArgumentException if {@code start > end}
4034cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @throws NullPointerException if {@code original == null}
4134cbdb2e17271b31d1511d2773edd241fafede7dztenghui     */
4234cbdb2e17271b31d1511d2773edd241fafede7dztenghui    private static float[] copyOfRange(float[] original, int start, int end) {
4334cbdb2e17271b31d1511d2773edd241fafede7dztenghui        if (start > end) {
4434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            throw new IllegalArgumentException();
4534cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
4634cbdb2e17271b31d1511d2773edd241fafede7dztenghui        int originalLength = original.length;
4734cbdb2e17271b31d1511d2773edd241fafede7dztenghui        if (start < 0 || start > originalLength) {
4834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            throw new ArrayIndexOutOfBoundsException();
4934cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
5034cbdb2e17271b31d1511d2773edd241fafede7dztenghui        int resultLength = end - start;
5134cbdb2e17271b31d1511d2773edd241fafede7dztenghui        int copyLength = Math.min(resultLength, originalLength - start);
5234cbdb2e17271b31d1511d2773edd241fafede7dztenghui        float[] result = new float[resultLength];
5334cbdb2e17271b31d1511d2773edd241fafede7dztenghui        System.arraycopy(original, start, result, 0, copyLength);
5434cbdb2e17271b31d1511d2773edd241fafede7dztenghui        return result;
5534cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
5634cbdb2e17271b31d1511d2773edd241fafede7dztenghui
5734cbdb2e17271b31d1511d2773edd241fafede7dztenghui    /**
5834cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @param pathData The string representing a path, the same as "d" string in svg file.
5934cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @return the generated Path object.
6034cbdb2e17271b31d1511d2773edd241fafede7dztenghui     */
6134cbdb2e17271b31d1511d2773edd241fafede7dztenghui    public static Path createPathFromPathData(String pathData) {
6234cbdb2e17271b31d1511d2773edd241fafede7dztenghui        Path path = new Path();
6334cbdb2e17271b31d1511d2773edd241fafede7dztenghui        PathDataNode[] nodes = createNodesFromPathData(pathData);
6434cbdb2e17271b31d1511d2773edd241fafede7dztenghui        if (nodes != null) {
6534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            try {
6634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                PathDataNode.nodesToPath(nodes, path);
6734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            } catch (RuntimeException e) {
6834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                throw new RuntimeException("Error in parsing " + pathData, e);
6934cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
7034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            return path;
7134cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
7234cbdb2e17271b31d1511d2773edd241fafede7dztenghui        return null;
7334cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
7434cbdb2e17271b31d1511d2773edd241fafede7dztenghui
7534cbdb2e17271b31d1511d2773edd241fafede7dztenghui    /**
7634cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @param pathData The string representing a path, the same as "d" string in svg file.
7734cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @return an array of the PathDataNode.
7834cbdb2e17271b31d1511d2773edd241fafede7dztenghui     */
7934cbdb2e17271b31d1511d2773edd241fafede7dztenghui    public static PathDataNode[] createNodesFromPathData(String pathData) {
8034cbdb2e17271b31d1511d2773edd241fafede7dztenghui        if (pathData == null) {
8134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            return null;
8234cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
8334cbdb2e17271b31d1511d2773edd241fafede7dztenghui        int start = 0;
8434cbdb2e17271b31d1511d2773edd241fafede7dztenghui        int end = 1;
8534cbdb2e17271b31d1511d2773edd241fafede7dztenghui
8634cbdb2e17271b31d1511d2773edd241fafede7dztenghui        ArrayList<PathDataNode> list = new ArrayList<PathDataNode>();
8734cbdb2e17271b31d1511d2773edd241fafede7dztenghui        while (end < pathData.length()) {
8834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            end = nextStart(pathData, end);
8934cbdb2e17271b31d1511d2773edd241fafede7dztenghui            String s = pathData.substring(start, end).trim();
9034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            if (s.length() > 0) {
9134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                float[] val = getFloats(s);
9234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                addNode(list, s.charAt(0), val);
9334cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
9434cbdb2e17271b31d1511d2773edd241fafede7dztenghui
9534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            start = end;
9634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            end++;
9734cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
9834cbdb2e17271b31d1511d2773edd241fafede7dztenghui        if ((end - start) == 1 && start < pathData.length()) {
9934cbdb2e17271b31d1511d2773edd241fafede7dztenghui            addNode(list, pathData.charAt(start), new float[0]);
10034cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
10134cbdb2e17271b31d1511d2773edd241fafede7dztenghui        return list.toArray(new PathDataNode[list.size()]);
10234cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
10334cbdb2e17271b31d1511d2773edd241fafede7dztenghui
10434cbdb2e17271b31d1511d2773edd241fafede7dztenghui    /**
10534cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @param source The array of PathDataNode to be duplicated.
10634cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @return a deep copy of the <code>source</code>.
10734cbdb2e17271b31d1511d2773edd241fafede7dztenghui     */
10834cbdb2e17271b31d1511d2773edd241fafede7dztenghui    public static PathDataNode[] deepCopyNodes(PathDataNode[] source) {
10934cbdb2e17271b31d1511d2773edd241fafede7dztenghui        if (source == null) {
11034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            return null;
11134cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
11234cbdb2e17271b31d1511d2773edd241fafede7dztenghui        PathDataNode[] copy = new PathParser.PathDataNode[source.length];
11334cbdb2e17271b31d1511d2773edd241fafede7dztenghui        for (int i = 0; i < source.length; i ++) {
11434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            copy[i] = new PathDataNode(source[i]);
11534cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
11634cbdb2e17271b31d1511d2773edd241fafede7dztenghui        return copy;
11734cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
11834cbdb2e17271b31d1511d2773edd241fafede7dztenghui
11934cbdb2e17271b31d1511d2773edd241fafede7dztenghui    /**
12034cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @param nodesFrom The source path represented in an array of PathDataNode
12134cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @param nodesTo The target path represented in an array of PathDataNode
12234cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @return whether the <code>nodesFrom</code> can morph into <code>nodesTo</code>
12334cbdb2e17271b31d1511d2773edd241fafede7dztenghui     */
12434cbdb2e17271b31d1511d2773edd241fafede7dztenghui    public static boolean canMorph(PathDataNode[] nodesFrom, PathDataNode[] nodesTo) {
12534cbdb2e17271b31d1511d2773edd241fafede7dztenghui        if (nodesFrom == null || nodesTo == null) {
12634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            return false;
12734cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
12834cbdb2e17271b31d1511d2773edd241fafede7dztenghui
12934cbdb2e17271b31d1511d2773edd241fafede7dztenghui        if (nodesFrom.length != nodesTo.length) {
13034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            return false;
13134cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
13234cbdb2e17271b31d1511d2773edd241fafede7dztenghui
13334cbdb2e17271b31d1511d2773edd241fafede7dztenghui        for (int i = 0; i < nodesFrom.length; i ++) {
134d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu            if (nodesFrom[i].type != nodesTo[i].type
135d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu                    || nodesFrom[i].params.length != nodesTo[i].params.length) {
13634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                return false;
13734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
13834cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
13934cbdb2e17271b31d1511d2773edd241fafede7dztenghui        return true;
14034cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
14134cbdb2e17271b31d1511d2773edd241fafede7dztenghui
14234cbdb2e17271b31d1511d2773edd241fafede7dztenghui    /**
14334cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * Update the target's data to match the source.
14434cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * Before calling this, make sure canMorph(target, source) is true.
14534cbdb2e17271b31d1511d2773edd241fafede7dztenghui     *
14634cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @param target The target path represented in an array of PathDataNode
14734cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @param source The source path represented in an array of PathDataNode
14834cbdb2e17271b31d1511d2773edd241fafede7dztenghui     */
14934cbdb2e17271b31d1511d2773edd241fafede7dztenghui    public static void updateNodes(PathDataNode[] target, PathDataNode[] source) {
15034cbdb2e17271b31d1511d2773edd241fafede7dztenghui        for (int i = 0; i < source.length; i ++) {
151d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu            target[i].type = source[i].type;
152d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu            for (int j = 0; j < source[i].params.length; j ++) {
153d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu                target[i].params[j] = source[i].params[j];
15434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
15534cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
15634cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
15734cbdb2e17271b31d1511d2773edd241fafede7dztenghui
15834cbdb2e17271b31d1511d2773edd241fafede7dztenghui    private static int nextStart(String s, int end) {
15934cbdb2e17271b31d1511d2773edd241fafede7dztenghui        char c;
16034cbdb2e17271b31d1511d2773edd241fafede7dztenghui
16134cbdb2e17271b31d1511d2773edd241fafede7dztenghui        while (end < s.length()) {
16234cbdb2e17271b31d1511d2773edd241fafede7dztenghui            c = s.charAt(end);
16334cbdb2e17271b31d1511d2773edd241fafede7dztenghui            // Note that 'e' or 'E' are not valid path commands, but could be
16434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            // used for floating point numbers' scientific notation.
16534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            // Therefore, when searching for next command, we should ignore 'e'
16634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            // and 'E'.
16734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            if ((((c - 'A') * (c - 'Z') <= 0) || ((c - 'a') * (c - 'z') <= 0))
16834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    && c != 'e' && c != 'E') {
16934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                return end;
17034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
17134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            end++;
17234cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
17334cbdb2e17271b31d1511d2773edd241fafede7dztenghui        return end;
17434cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
17534cbdb2e17271b31d1511d2773edd241fafede7dztenghui
17634cbdb2e17271b31d1511d2773edd241fafede7dztenghui    private static void addNode(ArrayList<PathDataNode> list, char cmd, float[] val) {
17734cbdb2e17271b31d1511d2773edd241fafede7dztenghui        list.add(new PathDataNode(cmd, val));
17834cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
17934cbdb2e17271b31d1511d2773edd241fafede7dztenghui
18034cbdb2e17271b31d1511d2773edd241fafede7dztenghui    private static class ExtractFloatResult {
18134cbdb2e17271b31d1511d2773edd241fafede7dztenghui        // We need to return the position of the next separator and whether the
18234cbdb2e17271b31d1511d2773edd241fafede7dztenghui        // next float starts with a '-' or a '.'.
18334cbdb2e17271b31d1511d2773edd241fafede7dztenghui        int mEndPosition;
18434cbdb2e17271b31d1511d2773edd241fafede7dztenghui        boolean mEndWithNegOrDot;
18534cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
18634cbdb2e17271b31d1511d2773edd241fafede7dztenghui
18734cbdb2e17271b31d1511d2773edd241fafede7dztenghui    /**
18834cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * Parse the floats in the string.
18934cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * This is an optimized version of parseFloat(s.split(",|\\s"));
19034cbdb2e17271b31d1511d2773edd241fafede7dztenghui     *
19134cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @param s the string containing a command and list of floats
19234cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @return array of floats
19334cbdb2e17271b31d1511d2773edd241fafede7dztenghui     */
19434cbdb2e17271b31d1511d2773edd241fafede7dztenghui    private static float[] getFloats(String s) {
19534cbdb2e17271b31d1511d2773edd241fafede7dztenghui        if (s.charAt(0) == 'z' | s.charAt(0) == 'Z') {
19634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            return new float[0];
19734cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
19834cbdb2e17271b31d1511d2773edd241fafede7dztenghui        try {
19934cbdb2e17271b31d1511d2773edd241fafede7dztenghui            float[] results = new float[s.length()];
20034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            int count = 0;
20134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            int startPosition = 1;
20234cbdb2e17271b31d1511d2773edd241fafede7dztenghui            int endPosition = 0;
20334cbdb2e17271b31d1511d2773edd241fafede7dztenghui
20434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            ExtractFloatResult result = new ExtractFloatResult();
20534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            int totalLength = s.length();
20634cbdb2e17271b31d1511d2773edd241fafede7dztenghui
20734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            // The startPosition should always be the first character of the
20834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            // current number, and endPosition is the character after the current
20934cbdb2e17271b31d1511d2773edd241fafede7dztenghui            // number.
21034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            while (startPosition < totalLength) {
21134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                extract(s, startPosition, result);
21234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                endPosition = result.mEndPosition;
21334cbdb2e17271b31d1511d2773edd241fafede7dztenghui
21434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                if (startPosition < endPosition) {
21534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    results[count++] = Float.parseFloat(
21634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                            s.substring(startPosition, endPosition));
21734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                }
21834cbdb2e17271b31d1511d2773edd241fafede7dztenghui
21934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                if (result.mEndWithNegOrDot) {
22034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    // Keep the '-' or '.' sign with next number.
22134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    startPosition = endPosition;
22234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                } else {
22334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    startPosition = endPosition + 1;
22434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                }
22534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
22634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            return copyOfRange(results, 0, count);
22734cbdb2e17271b31d1511d2773edd241fafede7dztenghui        } catch (NumberFormatException e) {
22834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            throw new RuntimeException("error in parsing \"" + s + "\"", e);
22934cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
23034cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
23134cbdb2e17271b31d1511d2773edd241fafede7dztenghui
23234cbdb2e17271b31d1511d2773edd241fafede7dztenghui    /**
23334cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * Calculate the position of the next comma or space or negative sign
23434cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @param s the string to search
23534cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @param start the position to start searching
23634cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * @param result the result of the extraction, including the position of the
23734cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * the starting position of next number, whether it is ending with a '-'.
23834cbdb2e17271b31d1511d2773edd241fafede7dztenghui     */
23934cbdb2e17271b31d1511d2773edd241fafede7dztenghui    private static void extract(String s, int start, ExtractFloatResult result) {
24034cbdb2e17271b31d1511d2773edd241fafede7dztenghui        // Now looking for ' ', ',', '.' or '-' from the start.
24134cbdb2e17271b31d1511d2773edd241fafede7dztenghui        int currentIndex = start;
24234cbdb2e17271b31d1511d2773edd241fafede7dztenghui        boolean foundSeparator = false;
24334cbdb2e17271b31d1511d2773edd241fafede7dztenghui        result.mEndWithNegOrDot = false;
24434cbdb2e17271b31d1511d2773edd241fafede7dztenghui        boolean secondDot = false;
24534cbdb2e17271b31d1511d2773edd241fafede7dztenghui        boolean isExponential = false;
24634cbdb2e17271b31d1511d2773edd241fafede7dztenghui        for (; currentIndex < s.length(); currentIndex++) {
24734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            boolean isPrevExponential = isExponential;
24834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            isExponential = false;
24934cbdb2e17271b31d1511d2773edd241fafede7dztenghui            char currentChar = s.charAt(currentIndex);
25034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            switch (currentChar) {
25134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case ' ':
25234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case ',':
25334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    foundSeparator = true;
25434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    break;
25534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case '-':
25634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    // The negative sign following a 'e' or 'E' is not a separator.
25734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    if (currentIndex != start && !isPrevExponential) {
25834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        foundSeparator = true;
25934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        result.mEndWithNegOrDot = true;
26034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    }
26134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    break;
26234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case '.':
26334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    if (!secondDot) {
26434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        secondDot = true;
26534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    } else {
26634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        // This is the second dot, and it is considered as a separator.
26734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        foundSeparator = true;
26834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        result.mEndWithNegOrDot = true;
26934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    }
27034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    break;
27134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'e':
27234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'E':
27334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    isExponential = true;
27434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    break;
27534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
27634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            if (foundSeparator) {
27734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                break;
27834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
27934cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
28034cbdb2e17271b31d1511d2773edd241fafede7dztenghui        // When there is nothing found, then we put the end position to the end
28134cbdb2e17271b31d1511d2773edd241fafede7dztenghui        // of the string.
28234cbdb2e17271b31d1511d2773edd241fafede7dztenghui        result.mEndPosition = currentIndex;
28334cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
28434cbdb2e17271b31d1511d2773edd241fafede7dztenghui
28534cbdb2e17271b31d1511d2773edd241fafede7dztenghui    /**
28634cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * Each PathDataNode represents one command in the "d" attribute of the svg
28734cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * file.
28834cbdb2e17271b31d1511d2773edd241fafede7dztenghui     * An array of PathDataNode can represent the whole "d" attribute.
28934cbdb2e17271b31d1511d2773edd241fafede7dztenghui     */
29034cbdb2e17271b31d1511d2773edd241fafede7dztenghui    public static class PathDataNode {
291d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu        /*package*/
292d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu        char type;
293d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu        float[] params;
29434cbdb2e17271b31d1511d2773edd241fafede7dztenghui
29534cbdb2e17271b31d1511d2773edd241fafede7dztenghui        private PathDataNode(char type, float[] params) {
296d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu            this.type = type;
297d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu            this.params = params;
29834cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
29934cbdb2e17271b31d1511d2773edd241fafede7dztenghui
30034cbdb2e17271b31d1511d2773edd241fafede7dztenghui        private PathDataNode(PathDataNode n) {
301d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu            type = n.type;
302d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu            params = copyOfRange(n.params, 0, n.params.length);
30334cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
30434cbdb2e17271b31d1511d2773edd241fafede7dztenghui
30534cbdb2e17271b31d1511d2773edd241fafede7dztenghui        /**
30634cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * Convert an array of PathDataNode to Path.
30734cbdb2e17271b31d1511d2773edd241fafede7dztenghui         *
30834cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * @param node The source array of PathDataNode.
30934cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * @param path The target Path object.
31034cbdb2e17271b31d1511d2773edd241fafede7dztenghui         */
31134cbdb2e17271b31d1511d2773edd241fafede7dztenghui        public static void nodesToPath(PathDataNode[] node, Path path) {
31234cbdb2e17271b31d1511d2773edd241fafede7dztenghui            float[] current = new float[6];
31334cbdb2e17271b31d1511d2773edd241fafede7dztenghui            char previousCommand = 'm';
31434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            for (int i = 0; i < node.length; i++) {
315d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu                addCommand(path, current, previousCommand, node[i].type, node[i].params);
316d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu                previousCommand = node[i].type;
31734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
31834cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
31934cbdb2e17271b31d1511d2773edd241fafede7dztenghui
32034cbdb2e17271b31d1511d2773edd241fafede7dztenghui        /**
32134cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * The current PathDataNode will be interpolated between the
32234cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * <code>nodeFrom</code> and <code>nodeTo</code> according to the
32334cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * <code>fraction</code>.
32434cbdb2e17271b31d1511d2773edd241fafede7dztenghui         *
32534cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * @param nodeFrom The start value as a PathDataNode.
32634cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * @param nodeTo The end value as a PathDataNode
32734cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * @param fraction The fraction to interpolate.
32834cbdb2e17271b31d1511d2773edd241fafede7dztenghui         */
32934cbdb2e17271b31d1511d2773edd241fafede7dztenghui        public void interpolatePathDataNode(PathDataNode nodeFrom,
33034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                PathDataNode nodeTo, float fraction) {
331d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu            for (int i = 0; i < nodeFrom.params.length; i++) {
332d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu                params[i] = nodeFrom.params[i] * (1 - fraction)
333d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu                        + nodeTo.params[i] * fraction;
33434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
33534cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
33634cbdb2e17271b31d1511d2773edd241fafede7dztenghui
33734cbdb2e17271b31d1511d2773edd241fafede7dztenghui        private static void addCommand(Path path, float[] current,
33834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                char previousCmd, char cmd, float[] val) {
33934cbdb2e17271b31d1511d2773edd241fafede7dztenghui
34034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            int incr = 2;
34134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            float currentX = current[0];
34234cbdb2e17271b31d1511d2773edd241fafede7dztenghui            float currentY = current[1];
34334cbdb2e17271b31d1511d2773edd241fafede7dztenghui            float ctrlPointX = current[2];
34434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            float ctrlPointY = current[3];
34534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            float currentSegmentStartX = current[4];
34634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            float currentSegmentStartY = current[5];
34734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            float reflectiveCtrlPointX;
34834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            float reflectiveCtrlPointY;
34934cbdb2e17271b31d1511d2773edd241fafede7dztenghui
35034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            switch (cmd) {
35134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'z':
35234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'Z':
35334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    path.close();
35434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    // Path is closed here, but we need to move the pen to the
35534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    // closed position. So we cache the segment's starting position,
35634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    // and restore it here.
35734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    currentX = currentSegmentStartX;
35834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    currentY = currentSegmentStartY;
35934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    ctrlPointX = currentSegmentStartX;
36034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    ctrlPointY = currentSegmentStartY;
36134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    path.moveTo(currentX, currentY);
36234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    break;
36334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'm':
36434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'M':
36534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'l':
36634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'L':
36734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 't':
36834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'T':
36934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    incr = 2;
37034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    break;
37134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'h':
37234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'H':
37334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'v':
37434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'V':
37534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    incr = 1;
37634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    break;
37734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'c':
37834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'C':
37934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    incr = 6;
38034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    break;
38134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 's':
38234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'S':
38334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'q':
38434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'Q':
38534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    incr = 4;
38634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    break;
38734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'a':
38834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                case 'A':
38934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    incr = 7;
39034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    break;
39134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
39234cbdb2e17271b31d1511d2773edd241fafede7dztenghui
39334cbdb2e17271b31d1511d2773edd241fafede7dztenghui            for (int k = 0; k < val.length; k += incr) {
39434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                switch (cmd) {
39534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'm': // moveto - Start a new sub-path (relative)
39634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.rMoveTo(val[k + 0], val[k + 1]);
39734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX += val[k + 0];
39834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY += val[k + 1];
39934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentSegmentStartX = currentX;
40034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentSegmentStartY = currentY;
40134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
40234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'M': // moveto - Start a new sub-path
40334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.moveTo(val[k + 0], val[k + 1]);
40434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX = val[k + 0];
40534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY = val[k + 1];
40634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentSegmentStartX = currentX;
40734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentSegmentStartY = currentY;
40834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
40934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'l': // lineto - Draw a line from the current point (relative)
41034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.rLineTo(val[k + 0], val[k + 1]);
41134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX += val[k + 0];
41234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY += val[k + 1];
41334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
41434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'L': // lineto - Draw a line from the current point
41534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.lineTo(val[k + 0], val[k + 1]);
41634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX = val[k + 0];
41734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY = val[k + 1];
41834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
41934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'h': // horizontal lineto - Draws a horizontal line (relative)
42034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.rLineTo(val[k + 0], 0);
42134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX += val[k + 0];
42234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
42334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'H': // horizontal lineto - Draws a horizontal line
42434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.lineTo(val[k + 0], currentY);
42534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX = val[k + 0];
42634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
42734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'v': // vertical lineto - Draws a vertical line from the current point (r)
42834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.rLineTo(0, val[k + 0]);
42934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY += val[k + 0];
43034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
43134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'V': // vertical lineto - Draws a vertical line from the current point
43234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.lineTo(currentX, val[k + 0]);
43334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY = val[k + 0];
43434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
43534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'c': // curveto - Draws a cubic Bézier curve (relative)
43634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.rCubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
43734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 4], val[k + 5]);
43834cbdb2e17271b31d1511d2773edd241fafede7dztenghui
43934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointX = currentX + val[k + 2];
44034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointY = currentY + val[k + 3];
44134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX += val[k + 4];
44234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY += val[k + 5];
44334cbdb2e17271b31d1511d2773edd241fafede7dztenghui
44434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
44534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'C': // curveto - Draws a cubic Bézier curve
44634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.cubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
44734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 4], val[k + 5]);
44834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX = val[k + 4];
44934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY = val[k + 5];
45034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointX = val[k + 2];
45134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointY = val[k + 3];
45234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
45334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 's': // smooth curveto - Draws a cubic Bézier curve (reflective cp)
45434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        reflectiveCtrlPointX = 0;
45534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        reflectiveCtrlPointY = 0;
45634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        if (previousCmd == 'c' || previousCmd == 's'
45734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                || previousCmd == 'C' || previousCmd == 'S') {
45834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                            reflectiveCtrlPointX = currentX - ctrlPointX;
45934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                            reflectiveCtrlPointY = currentY - ctrlPointY;
46034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        }
46134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.rCubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
46234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 0], val[k + 1],
46334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 2], val[k + 3]);
46434cbdb2e17271b31d1511d2773edd241fafede7dztenghui
46534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointX = currentX + val[k + 0];
46634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointY = currentY + val[k + 1];
46734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX += val[k + 2];
46834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY += val[k + 3];
46934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
47034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'S': // shorthand/smooth curveto Draws a cubic Bézier curve(reflective cp)
47134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        reflectiveCtrlPointX = currentX;
47234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        reflectiveCtrlPointY = currentY;
47334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        if (previousCmd == 'c' || previousCmd == 's'
47434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                || previousCmd == 'C' || previousCmd == 'S') {
47534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                            reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
47634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                            reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
47734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        }
47834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.cubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
47934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
48034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointX = val[k + 0];
48134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointY = val[k + 1];
48234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX = val[k + 2];
48334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY = val[k + 3];
48434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
48534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'q': // Draws a quadratic Bézier (relative)
48634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.rQuadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
48734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointX = currentX + val[k + 0];
48834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointY = currentY + val[k + 1];
48934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX += val[k + 2];
49034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY += val[k + 3];
49134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
49234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'Q': // Draws a quadratic Bézier
49334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.quadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
49434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointX = val[k + 0];
49534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointY = val[k + 1];
49634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX = val[k + 2];
49734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY = val[k + 3];
49834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
49934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 't': // Draws a quadratic Bézier curve(reflective control point)(relative)
50034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        reflectiveCtrlPointX = 0;
50134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        reflectiveCtrlPointY = 0;
50234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        if (previousCmd == 'q' || previousCmd == 't'
50334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                || previousCmd == 'Q' || previousCmd == 'T') {
50434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                            reflectiveCtrlPointX = currentX - ctrlPointX;
50534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                            reflectiveCtrlPointY = currentY - ctrlPointY;
50634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        }
50734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
50834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 0], val[k + 1]);
50934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointX = currentX + reflectiveCtrlPointX;
51034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointY = currentY + reflectiveCtrlPointY;
51134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX += val[k + 0];
51234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY += val[k + 1];
51334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
51434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'T': // Draws a quadratic Bézier curve (reflective control point)
51534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        reflectiveCtrlPointX = currentX;
51634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        reflectiveCtrlPointY = currentY;
51734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        if (previousCmd == 'q' || previousCmd == 't'
51834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                || previousCmd == 'Q' || previousCmd == 'T') {
51934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                            reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
52034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                            reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
52134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        }
52234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        path.quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
52334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 0], val[k + 1]);
52434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointX = reflectiveCtrlPointX;
52534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointY = reflectiveCtrlPointY;
52634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX = val[k + 0];
52734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY = val[k + 1];
52834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
52934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'a': // Draws an elliptical arc
53034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        // (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
53134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        drawArc(path,
53234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                currentX,
53334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                currentY,
53434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 5] + currentX,
53534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 6] + currentY,
53634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 0],
53734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 1],
53834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 2],
53934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 3] != 0,
54034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 4] != 0);
54134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX += val[k + 5];
54234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY += val[k + 6];
54334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointX = currentX;
54434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointY = currentY;
54534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
54634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    case 'A': // Draws an elliptical arc
54734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        drawArc(path,
54834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                currentX,
54934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                currentY,
55034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 5],
55134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 6],
55234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 0],
55334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 1],
55434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 2],
55534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 3] != 0,
55634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                                val[k + 4] != 0);
55734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentX = val[k + 5];
55834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        currentY = val[k + 6];
55934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointX = currentX;
56034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        ctrlPointY = currentY;
56134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        break;
56234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                }
56334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                previousCmd = cmd;
56434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
56534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            current[0] = currentX;
56634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            current[1] = currentY;
56734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            current[2] = ctrlPointX;
56834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            current[3] = ctrlPointY;
56934cbdb2e17271b31d1511d2773edd241fafede7dztenghui            current[4] = currentSegmentStartX;
57034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            current[5] = currentSegmentStartY;
57134cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
57234cbdb2e17271b31d1511d2773edd241fafede7dztenghui
57334cbdb2e17271b31d1511d2773edd241fafede7dztenghui        private static void drawArc(Path p,
57434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                float x0,
57534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                float y0,
57634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                float x1,
57734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                float y1,
57834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                float a,
57934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                float b,
58034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                float theta,
58134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                boolean isMoreThanHalf,
58234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                boolean isPositiveArc) {
58334cbdb2e17271b31d1511d2773edd241fafede7dztenghui
58434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            /* Convert rotation angle from degrees to radians */
58534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double thetaD = Math.toRadians(theta);
58634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            /* Pre-compute rotation matrix entries */
58734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double cosTheta = Math.cos(thetaD);
58834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double sinTheta = Math.sin(thetaD);
58934cbdb2e17271b31d1511d2773edd241fafede7dztenghui            /* Transform (x0, y0) and (x1, y1) into unit space */
59034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            /* using (inverse) rotation, followed by (inverse) scale */
59134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double x0p = (x0 * cosTheta + y0 * sinTheta) / a;
59234cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double y0p = (-x0 * sinTheta + y0 * cosTheta) / b;
59334cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double x1p = (x1 * cosTheta + y1 * sinTheta) / a;
59434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double y1p = (-x1 * sinTheta + y1 * cosTheta) / b;
59534cbdb2e17271b31d1511d2773edd241fafede7dztenghui
59634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            /* Compute differences and averages */
59734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double dx = x0p - x1p;
59834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double dy = y0p - y1p;
59934cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double xm = (x0p + x1p) / 2;
60034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double ym = (y0p + y1p) / 2;
60134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            /* Solve for intersecting unit circles */
60234cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double dsq = dx * dx + dy * dy;
60334cbdb2e17271b31d1511d2773edd241fafede7dztenghui            if (dsq == 0.0) {
60434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                Log.w(LOGTAG, " Points are coincident");
60534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                return; /* Points are coincident */
60634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
60734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double disc = 1.0 / dsq - 1.0 / 4.0;
60834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            if (disc < 0.0) {
60934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                Log.w(LOGTAG, "Points are too far apart " + dsq);
61034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                float adjust = (float) (Math.sqrt(dsq) / 1.99999);
61134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                drawArc(p, x0, y0, x1, y1, a * adjust,
61234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        b * adjust, theta, isMoreThanHalf, isPositiveArc);
61334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                return; /* Points are too far apart */
61434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
61534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double s = Math.sqrt(disc);
61634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double sdx = s * dx;
61734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double sdy = s * dy;
61834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double cx;
61934cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double cy;
62034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            if (isMoreThanHalf == isPositiveArc) {
62134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                cx = xm - sdy;
62234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                cy = ym + sdx;
62334cbdb2e17271b31d1511d2773edd241fafede7dztenghui            } else {
62434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                cx = xm + sdy;
62534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                cy = ym - sdx;
62634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
62734cbdb2e17271b31d1511d2773edd241fafede7dztenghui
62834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double eta0 = Math.atan2((y0p - cy), (x0p - cx));
62934cbdb2e17271b31d1511d2773edd241fafede7dztenghui
63034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double eta1 = Math.atan2((y1p - cy), (x1p - cx));
63134cbdb2e17271b31d1511d2773edd241fafede7dztenghui
63234cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double sweep = (eta1 - eta0);
63334cbdb2e17271b31d1511d2773edd241fafede7dztenghui            if (isPositiveArc != (sweep >= 0)) {
63434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                if (sweep > 0) {
63534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    sweep -= 2 * Math.PI;
63634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                } else {
63734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                    sweep += 2 * Math.PI;
63834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                }
63934cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
64034cbdb2e17271b31d1511d2773edd241fafede7dztenghui
64134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            cx *= a;
64234cbdb2e17271b31d1511d2773edd241fafede7dztenghui            cy *= b;
64334cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double tcx = cx;
64434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            cx = cx * cosTheta - cy * sinTheta;
64534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            cy = tcx * sinTheta + cy * cosTheta;
64634cbdb2e17271b31d1511d2773edd241fafede7dztenghui
64734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep);
64834cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
64934cbdb2e17271b31d1511d2773edd241fafede7dztenghui
65034cbdb2e17271b31d1511d2773edd241fafede7dztenghui        /**
65134cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * Converts an arc to cubic Bezier segments and records them in p.
65234cbdb2e17271b31d1511d2773edd241fafede7dztenghui         *
65334cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * @param p The target for the cubic Bezier segments
65434cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * @param cx The x coordinate center of the ellipse
65534cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * @param cy The y coordinate center of the ellipse
65634cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * @param a The radius of the ellipse in the horizontal direction
65734cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * @param b The radius of the ellipse in the vertical direction
65834cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * @param e1x E(eta1) x coordinate of the starting point of the arc
65934cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * @param e1y E(eta2) y coordinate of the starting point of the arc
66034cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane
66134cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * @param start The start angle of the arc on the ellipse
66234cbdb2e17271b31d1511d2773edd241fafede7dztenghui         * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
66334cbdb2e17271b31d1511d2773edd241fafede7dztenghui         */
66434cbdb2e17271b31d1511d2773edd241fafede7dztenghui        private static void arcToBezier(Path p,
66534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double cx,
66634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double cy,
66734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double a,
66834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double b,
66934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double e1x,
67034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double e1y,
67134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double theta,
67234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double start,
67334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double sweep) {
67434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html
67534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            // and http://www.spaceroots.org/documents/ellipse/node22.html
67634cbdb2e17271b31d1511d2773edd241fafede7dztenghui
67734cbdb2e17271b31d1511d2773edd241fafede7dztenghui            // Maximum of 45 degrees per cubic Bezier segment
67834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            int numSegments = Math.abs((int) Math.ceil(sweep * 4 / Math.PI));
67934cbdb2e17271b31d1511d2773edd241fafede7dztenghui
68034cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double eta1 = start;
68134cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double cosTheta = Math.cos(theta);
68234cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double sinTheta = Math.sin(theta);
68334cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double cosEta1 = Math.cos(eta1);
68434cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double sinEta1 = Math.sin(eta1);
68534cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1);
68634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1);
68734cbdb2e17271b31d1511d2773edd241fafede7dztenghui
68834cbdb2e17271b31d1511d2773edd241fafede7dztenghui            double anglePerSegment = sweep / numSegments;
68934cbdb2e17271b31d1511d2773edd241fafede7dztenghui            for (int i = 0; i < numSegments; i++) {
69034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double eta2 = eta1 + anglePerSegment;
69134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double sinEta2 = Math.sin(eta2);
69234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double cosEta2 = Math.cos(eta2);
69334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2);
69434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2);
69534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2;
69634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2;
69734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double tanDiff2 = Math.tan((eta2 - eta1) / 2);
69834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double alpha =
69934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        Math.sin(eta2 - eta1) * (Math.sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3;
70034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double q1x = e1x + alpha * ep1x;
70134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double q1y = e1y + alpha * ep1y;
70234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double q2x = e2x - alpha * ep2x;
70334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                double q2y = e2y - alpha * ep2y;
70434cbdb2e17271b31d1511d2773edd241fafede7dztenghui
70534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                p.cubicTo((float) q1x,
70634cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        (float) q1y,
70734cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        (float) q2x,
70834cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        (float) q2y,
70934cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        (float) e2x,
71034cbdb2e17271b31d1511d2773edd241fafede7dztenghui                        (float) e2y);
71134cbdb2e17271b31d1511d2773edd241fafede7dztenghui                eta1 = eta2;
71234cbdb2e17271b31d1511d2773edd241fafede7dztenghui                e1x = e2x;
71334cbdb2e17271b31d1511d2773edd241fafede7dztenghui                e1y = e2y;
71434cbdb2e17271b31d1511d2773edd241fafede7dztenghui                ep1x = ep2x;
71534cbdb2e17271b31d1511d2773edd241fafede7dztenghui                ep1y = ep2y;
71634cbdb2e17271b31d1511d2773edd241fafede7dztenghui            }
71734cbdb2e17271b31d1511d2773edd241fafede7dztenghui        }
71834cbdb2e17271b31d1511d2773edd241fafede7dztenghui    }
71934cbdb2e17271b31d1511d2773edd241fafede7dztenghui}
720