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